From f7634073315399f7a93674d2c044cf57f636e9a0 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:40:57 +0900 Subject: [PATCH 01/27] add BranchSortPopup --- asyncgit/src/sync/branch/mod.rs | 13 +- src/app.rs | 31 +++-- src/components/mod.rs | 10 ++ src/keys/key_list.rs | 14 ++ src/popups/branch_sort.rs | 218 ++++++++++++++++++++++++++++++++ src/popups/branchlist.rs | 108 +++++++++++++++- src/popups/mod.rs | 2 + src/queue.rs | 6 +- src/strings.rs | 68 ++++++++++ 9 files changed, 457 insertions(+), 13 deletions(-) create mode 100644 src/popups/branch_sort.rs diff --git a/asyncgit/src/sync/branch/mod.rs b/asyncgit/src/sync/branch/mod.rs index 4dbe89aa3a..e8fa8b4635 100644 --- a/asyncgit/src/sync/branch/mod.rs +++ b/asyncgit/src/sync/branch/mod.rs @@ -11,7 +11,10 @@ use super::{ }; use crate::{ error::{Error, Result}, - sync::{repository::repo, utils::get_head_repo, CommitId}, + sync::{ + repository::repo, utils::get_head_repo, CommitId, + CommitSignature, + }, }; use git2::{Branch, BranchType, Repository}; use scopetime::scope_time; @@ -92,6 +95,10 @@ pub struct BranchInfo { /// pub top_commit: CommitId, /// + pub top_commit_time: i64, + /// + pub top_commit_author: String, + /// pub details: BranchDetails, } @@ -181,6 +188,8 @@ pub fn get_branches_info( }) }; + let author = CommitSignature::from(&top_commit.author()); + Ok(BranchInfo { name: bytes2string(name_bytes)?, reference, @@ -188,6 +197,8 @@ pub fn get_branches_info( top_commit.summary_bytes().unwrap_or_default(), )?, top_commit: top_commit.id().into(), + top_commit_time: top_commit.time().seconds(), + top_commit_author: author.name, details, }) }) diff --git a/src/app.rs b/src/app.rs index f2b4a4a9bb..9bca8820a9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,14 +10,15 @@ use crate::{ options::{Options, SharedOptions}, popup_stack::PopupStack, popups::{ - AppOption, BlameFilePopup, BranchListPopup, CommitPopup, - CompareCommitsPopup, ConfirmPopup, CreateBranchPopup, - ExternalEditorPopup, FetchPopup, FileRevlogPopup, - FuzzyFindPopup, HelpPopup, InspectCommitPopup, - LogSearchPopupPopup, MsgPopup, OptionsPopup, PullPopup, - PushPopup, PushTagsPopup, RenameBranchPopup, ResetPopup, - RevisionFilesPopup, StashMsgPopup, SubmodulesListPopup, - TagCommitPopup, TagListPopup, + AppOption, BlameFilePopup, BranchListPopup, BranchSortPopup, + CommitPopup, CompareCommitsPopup, ConfirmPopup, + CreateBranchPopup, ExternalEditorPopup, FetchPopup, + FileRevlogPopup, FuzzyFindPopup, HelpPopup, + InspectCommitPopup, LogSearchPopupPopup, MsgPopup, + OptionsPopup, PullPopup, PushPopup, PushTagsPopup, + RenameBranchPopup, ResetPopup, RevisionFilesPopup, + StashMsgPopup, SubmodulesListPopup, TagCommitPopup, + TagListPopup, }, queue::{ Action, AppTabs, InternalEvent, NeedsUpdate, Queue, @@ -88,6 +89,7 @@ pub struct App { create_branch_popup: CreateBranchPopup, rename_branch_popup: RenameBranchPopup, select_branch_popup: BranchListPopup, + sort_branch_popup: BranchSortPopup, options_popup: OptionsPopup, submodule_popup: SubmodulesListPopup, tags_popup: TagListPopup, @@ -196,6 +198,7 @@ impl App { submodule_popup: SubmodulesListPopup::new(&env), log_search_popup: LogSearchPopupPopup::new(&env), fuzzy_find_popup: FuzzyFindPopup::new(&env), + sort_branch_popup: BranchSortPopup::new(&env), do_quit: QuitState::None, cmdbar: RefCell::new(CommandBar::new( env.theme.clone(), @@ -468,6 +471,7 @@ impl App { [ log_search_popup, fuzzy_find_popup, + sort_branch_popup, msg_popup, confirm_popup, commit_popup, @@ -517,6 +521,7 @@ impl App { reset_popup, create_branch_popup, rename_branch_popup, + sort_branch_popup, revision_files_popup, fuzzy_find_popup, log_search_popup, @@ -803,6 +808,16 @@ impl App { flags .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); } + InternalEvent::OpenBranchSortPopup(sort_by) => { + self.sort_branch_popup.open(sort_by)?; + flags + .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); + } + InternalEvent::BranchListSort(sort_by) => { + self.select_branch_popup.sort(sort_by)?; + flags + .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); + } InternalEvent::OptionSwitched(o) => { match o { AppOption::StatusShowUntracked => { diff --git a/src/components/mod.rs b/src/components/mod.rs index 4f1f3f4006..41b15374a8 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -191,6 +191,16 @@ pub enum FuzzyFinderTarget { Files, } +#[derive(Copy, Clone)] +pub enum BranchListSortBy { + LastCommitTimeAsc, + LastCommitTimeDesc, + BranchNameAsc, + BranchNameDesc, + LastCommitAuthorAsc, + LastCommitAuthorDesc, +} + impl EventState { pub fn is_consumed(&self) -> bool { *self == Self::Consumed diff --git a/src/keys/key_list.rs b/src/keys/key_list.rs index 71e6756db4..b4cec7a9ce 100644 --- a/src/keys/key_list.rs +++ b/src/keys/key_list.rs @@ -107,6 +107,13 @@ pub struct KeysList { pub open_file_tree: GituiKeyEvent, pub file_find: GituiKeyEvent, pub branch_find: GituiKeyEvent, + pub branch_sort: GituiKeyEvent, + pub branch_sort_by_name: GituiKeyEvent, + pub branch_sort_by_name_rev: GituiKeyEvent, + pub branch_sort_by_time: GituiKeyEvent, + pub branch_sort_by_time_rev: GituiKeyEvent, + pub branch_sort_by_author: GituiKeyEvent, + pub branch_sort_by_author_rev: GituiKeyEvent, pub force_push: GituiKeyEvent, pub fetch: GituiKeyEvent, pub pull: GituiKeyEvent, @@ -203,6 +210,13 @@ impl Default for KeysList { open_file_tree: GituiKeyEvent::new(KeyCode::Char('F'), KeyModifiers::SHIFT), file_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), branch_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), + branch_sort: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::empty()), + branch_sort_by_name: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), + branch_sort_by_name_rev: GituiKeyEvent::new(KeyCode::Char('N'), KeyModifiers::SHIFT), + branch_sort_by_time: GituiKeyEvent::new(KeyCode::Char('t'), KeyModifiers::empty()), + branch_sort_by_time_rev: GituiKeyEvent::new(KeyCode::Char('T'), KeyModifiers::SHIFT), + branch_sort_by_author: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()), + branch_sort_by_author_rev: GituiKeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT), diff_hunk_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), diff_hunk_prev: GituiKeyEvent::new(KeyCode::Char('p'), KeyModifiers::empty()), stage_unstage_item: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs new file mode 100644 index 0000000000..46ccf0fc4d --- /dev/null +++ b/src/popups/branch_sort.rs @@ -0,0 +1,218 @@ +use anyhow::Result; +use crossterm::event::Event; +use ratatui::{ + layout::{Alignment, Margin, Rect}, + text::{Line, Span}, + widgets::{Block, Borders, Clear, Paragraph}, + Frame, +}; + +use crate::{ + app::Environment, + components::{ + visibility_blocking, BranchListSortBy, CommandBlocking, + CommandInfo, Component, DrawableComponent, EventState, + }, + keys::{key_match, SharedKeyConfig}, + queue::{InternalEvent, Queue}, + strings, + ui::{self, style::SharedTheme}, +}; + +pub struct BranchSortPopup { + queue: Queue, + visible: bool, + key_config: SharedKeyConfig, + theme: SharedTheme, +} + +impl BranchSortPopup { + /// + pub fn new(env: &Environment) -> Self { + Self { + queue: env.queue.clone(), + visible: false, + key_config: env.key_config.clone(), + theme: env.theme.clone(), + } + } + + pub fn open(&mut self, sort_by: BranchListSortBy) -> Result<()> { + self.show()?; + self.update_sort_key(sort_by); + + Ok(()) + } + + fn is_visible(&self) -> bool { + self.visible + } + + fn hide(&mut self) { + self.visible = false; + } + + fn show(&mut self) -> Result<()> { + self.visible = true; + Ok(()) + } + + fn update_sort_key(&mut self, sort_by: BranchListSortBy) { + self.queue.push(InternalEvent::BranchListSort(sort_by)); + } + + fn get_sort_key_lines(&self) -> Vec { + let texts = vec![ + strings::sort_branch_by_name_msg(&self.key_config), + strings::sort_branch_by_name_rev_msg(&self.key_config), + strings::sort_branch_by_time_msg(&self.key_config), + strings::sort_branch_by_time_rev_msg(&self.key_config), + strings::sort_branch_by_author_msg(&self.key_config), + strings::sort_branch_by_author_rev_msg(&self.key_config), + ]; + texts + .iter() + .map(|t| { + Line::from(vec![Span::styled( + t.clone(), + self.theme.text(true, false), + )]) + }) + .collect() + } +} + +impl DrawableComponent for BranchSortPopup { + fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { + if self.is_visible() { + const MAX_SIZE: (u16, u16) = (50, 20); + + let mut area = ui::centered_rect_absolute( + MAX_SIZE.0, MAX_SIZE.1, area, + ); + + f.render_widget(Clear, area); + f.render_widget( + Block::default() + .borders(Borders::all()) + .style(self.theme.title(true)) + .title(Span::styled( + strings::POPUP_TITLE_BRANCH_SORT, + self.theme.title(true), + )), + area, + ); + + area = area.inner(&Margin { + horizontal: 1, + vertical: 1, + }); + f.render_widget( + Paragraph::new(self.get_sort_key_lines()) + .block( + Block::default() + .borders(Borders::NONE) + .border_style(self.theme.block(true)), + ) + .alignment(Alignment::Left), + area, + ); + } + Ok(()) + } +} + +impl Component for BranchSortPopup { + fn commands( + &self, + out: &mut Vec, + force_all: bool, + ) -> CommandBlocking { + if self.is_visible() || force_all { + out.push(CommandInfo::new( + strings::commands::close_popup(&self.key_config), + true, + true, + )); + } + + visibility_blocking(self) + } + + fn event( + &mut self, + event: &crossterm::event::Event, + ) -> Result { + if self.is_visible() { + if let Event::Key(key) = event { + if key_match(key, self.key_config.keys.exit_popup) { + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_name, + ) { + self.update_sort_key( + BranchListSortBy::BranchNameAsc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_name_rev, + ) { + self.update_sort_key( + BranchListSortBy::BranchNameDesc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_time, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitTimeDesc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_time_rev, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitTimeAsc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_author, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitAuthorAsc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_author_rev, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitAuthorDesc, + ); + self.hide(); + } + } + return Ok(EventState::Consumed); + } + + Ok(EventState::NotConsumed) + } + + fn is_visible(&self) -> bool { + self.visible + } + + fn hide(&mut self) { + self.visible = false; + } + + fn show(&mut self) -> Result<()> { + self.visible = true; + Ok(()) + } +} diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index f7c9b94dd5..142789dec8 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -1,6 +1,7 @@ use crate::components::{ - visibility_blocking, CommandBlocking, CommandInfo, Component, - DrawableComponent, EventState, FuzzyFinderTarget, VerticalScroll, + visibility_blocking, BranchListSortBy, CommandBlocking, + CommandInfo, Component, DrawableComponent, EventState, + FuzzyFinderTarget, VerticalScroll, }; use crate::{ app::Environment, @@ -25,6 +26,7 @@ use asyncgit::{ }, AsyncGitNotification, }; +use chrono::{Local, TimeZone}; use crossterm::event::{Event, KeyEvent}; use ratatui::{ layout::{ @@ -44,6 +46,7 @@ use super::InspectCommitOpen; pub struct BranchListPopup { repo: RepoPathRef, branches: Vec, + sort_by: BranchListSortBy, local: bool, has_remotes: bool, visible: bool, @@ -211,6 +214,12 @@ impl Component for BranchListPopup { true, true, )); + + out.push(CommandInfo::new( + strings::commands::sort_branch(&self.key_config), + true, + true, + )) } visibility_blocking(self) } @@ -304,6 +313,10 @@ impl Component for BranchListPopup { branches, FuzzyFinderTarget::Branches, )); + } else if key_match(e, self.key_config.keys.branch_sort) { + self.queue.push(InternalEvent::OpenBranchSortPopup( + self.sort_by.clone(), + )); } } @@ -329,6 +342,7 @@ impl BranchListPopup { pub fn new(env: &Environment) -> Self { Self { branches: Vec::new(), + sort_by: BranchListSortBy::BranchNameAsc, local: true, has_remotes: false, visible: false, @@ -399,12 +413,79 @@ impl BranchListPopup { } } + pub fn sort(&mut self, sort_by: BranchListSortBy) -> Result<()> { + self.sort_by = sort_by; + self.update_branches()?; + + Ok(()) + } + /// fetch list of branches pub fn update_branches(&mut self) -> Result<()> { if self.is_visible() { self.check_remotes(); self.branches = get_branches_info(&self.repo.borrow(), self.local)?; + match &self.sort_by { + BranchListSortBy::LastCommitAuthorAsc => { + self.branches.sort_by(|a, b| { + match b + .top_commit_author + .cmp(&a.top_commit_author) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitAuthorDesc => { + self.branches.sort_by(|a, b| { + match a + .top_commit_author + .cmp(&b.top_commit_author) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitTimeAsc => { + self.branches.sort_by(|a, b| { + match a + .top_commit_time + .cmp(&b.top_commit_time) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitTimeDesc => { + self.branches.sort_by(|a, b| { + match b + .top_commit_time + .cmp(&a.top_commit_time) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::BranchNameAsc => { + self.branches.sort_by(|a, b| a.name.cmp(&b.name)) + } + BranchListSortBy::BranchNameDesc => { + self.branches.sort_by(|a, b| b.name.cmp(&a.name)) + } + } //remove remote branch called `HEAD` if !self.local { self.branches @@ -568,13 +649,16 @@ impl BranchListPopup { const THREE_DOTS: &str = "..."; const THREE_DOTS_LENGTH: usize = THREE_DOTS.len(); // "..." const COMMIT_HASH_LENGTH: usize = 8; + const COMMIT_DATE_LENGTH: usize = 11; const IS_HEAD_STAR_LENGTH: usize = 3; // "* " let branch_name_length: usize = width_available as usize * 40 / 100; // commit message takes up the remaining width - let commit_message_length: usize = (width_available as usize) + let mut commit_message_length: usize = (width_available + as usize) .saturating_sub(COMMIT_HASH_LENGTH) + .saturating_sub(COMMIT_DATE_LENGTH) .saturating_sub(branch_name_length) .saturating_sub(IS_HEAD_STAR_LENGTH) .saturating_sub(THREE_DOTS_LENGTH); @@ -587,6 +671,16 @@ impl BranchListPopup { .take(height) .enumerate() { + let date_local = Local + .timestamp_opt(displaybranch.top_commit_time, 0) + .unwrap(); + let date_text = date_local.date_naive().to_string() + " "; + let author_text = + displaybranch.top_commit_author.clone() + " "; + + // commit_message_length contains author_text length + commit_message_length = commit_message_length + .saturating_sub(author_text.len()); let mut commit_message = displaybranch.top_commit_message.clone(); if commit_message.len() > commit_message_length { @@ -643,6 +737,12 @@ impl BranchListPopup { ), theme.commit_hash(selected), ); + let span_date = + Span::styled(date_text, theme.text(true, selected)); + let span_author = Span::styled( + author_text, + theme.commit_author(selected), + ); let span_msg = Span::styled( commit_message.to_string(), theme.text(true, selected), @@ -656,6 +756,8 @@ impl BranchListPopup { span_prefix, span_name, span_hash, + span_date, + span_author, span_msg, ])); } diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 2216461ad7..95b1094b50 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -1,5 +1,6 @@ mod blame_file; mod branchlist; +mod branch_sort; mod commit; mod compare_commits; mod confirm; @@ -26,6 +27,7 @@ mod taglist; pub use blame_file::{BlameFileOpen, BlameFilePopup}; pub use branchlist::BranchListPopup; +pub use branch_sort::BranchSortPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; pub use confirm::ConfirmPopup; diff --git a/src/queue.rs b/src/queue.rs index 9ee7830bdd..842e528f99 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,5 +1,5 @@ use crate::{ - components::FuzzyFinderTarget, + components::{BranchListSortBy, FuzzyFinderTarget}, popups::{ AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen, InspectCommitOpen, @@ -113,6 +113,10 @@ pub enum InternalEvent { /// SelectBranch, /// + OpenBranchSortPopup(BranchListSortBy), + /// + BranchListSort(BranchListSortBy), + /// OpenExternalEditor(Option), /// Push(String, PushType, bool, bool), diff --git a/src/strings.rs b/src/strings.rs index 67eb79ba7a..de7a868b96 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -32,6 +32,7 @@ pub static PUSH_TAGS_STATES_DONE: &str = "done"; pub static POPUP_TITLE_SUBMODULES: &str = "Submodules"; pub static POPUP_TITLE_FUZZY_FIND: &str = "Fuzzy Finder"; pub static POPUP_TITLE_LOG_SEARCH: &str = "Search"; +pub static POPUP_TITLE_BRANCH_SORT: &str = "Sort by"; pub static POPUP_FAIL_COPY: &str = "Failed to copy text"; pub static POPUP_SUCCESS_COPY: &str = "Copied Text"; @@ -363,6 +364,63 @@ pub fn rename_branch_popup_msg( "new branch name".to_string() } +pub fn sort_branch_by_name_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = + _key_config.get_hint(_key_config.keys.branch_sort_by_name); + format!("{: <5}branch name (a -> z)", format!("[{}]", hint)) + .to_string() +} +pub fn sort_branch_by_name_rev_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = _key_config + .get_hint(_key_config.keys.branch_sort_by_name_rev); + format!("{: <5}branch name (z -> a)", format!("[{}]", hint)) + .to_string() +} +pub fn sort_branch_by_time_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = + _key_config.get_hint(_key_config.keys.branch_sort_by_time); + format!( + "{: <5}last commit time (new -> old)", + format!("[{}]", hint) + ) +} +pub fn sort_branch_by_time_rev_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = _key_config + .get_hint(_key_config.keys.branch_sort_by_time_rev); + format!( + "{: <5}last commit time (old -> new)", + format!("[{}]", hint) + ) +} +pub fn sort_branch_by_author_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = + _key_config.get_hint(_key_config.keys.branch_sort_by_author); + format!( + "{: <5}last commit author (a -> z)", + format!("[{}]", hint) + ) +} +pub fn sort_branch_by_author_rev_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = _key_config + .get_hint(_key_config.keys.branch_sort_by_author_rev); + format!( + "{: <5}last commit author (z -> a)", + format!("[{}]", hint) + ) +} + pub fn copy_success(s: &str) -> String { format!("{POPUP_SUCCESS_COPY} \"{s}\"") } @@ -469,6 +527,16 @@ pub mod commands { CMD_GROUP_GENERAL, ) } + pub fn sort_branch(key_config: &SharedKeyConfig) -> CommandText { + CommandText::new( + format!( + "Sort by [{}]", + key_config.get_hint(key_config.keys.branch_sort) + ), + "sort branches", + CMD_GROUP_GENERAL, + ) + } pub fn toggle_tabs_direct( key_config: &SharedKeyConfig, ) -> CommandText { From 9d3d664074f199ac3936ef9b7e3d524773b334a6 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:53:09 +0900 Subject: [PATCH 02/27] fix height of branch sort popup --- src/popups/branch_sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 46ccf0fc4d..471dad308c 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -85,7 +85,7 @@ impl BranchSortPopup { impl DrawableComponent for BranchSortPopup { fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { if self.is_visible() { - const MAX_SIZE: (u16, u16) = (50, 20); + const MAX_SIZE: (u16, u16) = (50, 8); let mut area = ui::centered_rect_absolute( MAX_SIZE.0, MAX_SIZE.1, area, From 2d0ca2229d422ddf4394eb0dde6c6def8764ea9a Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:53:20 +0900 Subject: [PATCH 03/27] small fix --- src/popups/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 95b1094b50..81f1990935 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -1,6 +1,6 @@ mod blame_file; -mod branchlist; mod branch_sort; +mod branchlist; mod commit; mod compare_commits; mod confirm; @@ -26,8 +26,8 @@ mod tag_commit; mod taglist; pub use blame_file::{BlameFileOpen, BlameFilePopup}; -pub use branchlist::BranchListPopup; pub use branch_sort::BranchSortPopup; +pub use branchlist::BranchListPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; pub use confirm::ConfirmPopup; From c893ff714493ad625478782812612d7e23597575 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:09:59 +0900 Subject: [PATCH 04/27] fix clippy --- src/popups/branch_sort.rs | 14 ------- src/popups/branchlist.rs | 88 +++++++++++++++++++++++++-------------- src/strings.rs | 32 +++++++------- 3 files changed, 71 insertions(+), 63 deletions(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 471dad308c..93977ea952 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -40,20 +40,6 @@ impl BranchSortPopup { pub fn open(&mut self, sort_by: BranchListSortBy) -> Result<()> { self.show()?; self.update_sort_key(sort_by); - - Ok(()) - } - - fn is_visible(&self) -> bool { - self.visible - } - - fn hide(&mut self) { - self.visible = false; - } - - fn show(&mut self) -> Result<()> { - self.visible = true; Ok(()) } diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index 142789dec8..26353f4af3 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -219,7 +219,7 @@ impl Component for BranchListPopup { strings::commands::sort_branch(&self.key_config), true, true, - )) + )); } visibility_blocking(self) } @@ -315,7 +315,7 @@ impl Component for BranchListPopup { )); } else if key_match(e, self.key_config.keys.branch_sort) { self.queue.push(InternalEvent::OpenBranchSortPopup( - self.sort_by.clone(), + self.sort_by, )); } } @@ -480,10 +480,10 @@ impl BranchListPopup { }); } BranchListSortBy::BranchNameAsc => { - self.branches.sort_by(|a, b| a.name.cmp(&b.name)) + self.branches.sort_by(|a, b| a.name.cmp(&b.name)); } BranchListSortBy::BranchNameDesc => { - self.branches.sort_by(|a, b| b.name.cmp(&a.name)) + self.branches.sort_by(|a, b| b.name.cmp(&a.name)); } } //remove remote branch called `HEAD` @@ -635,6 +635,46 @@ impl BranchListPopup { Ok(()) } + const fn calculate_shared_commit_message_length( + width_available: u16, + three_dots_length: usize, + ) -> usize { + const COMMIT_HASH_LENGTH: usize = 8; + const COMMIT_DATE_LENGTH: usize = 11; + const IS_HEAD_STAR_LENGTH: usize = 3; + let branch_name_length: usize = + width_available as usize * 40 / 100; + + (width_available as usize) + .saturating_sub(COMMIT_HASH_LENGTH) + .saturating_sub(COMMIT_DATE_LENGTH) + .saturating_sub(branch_name_length) + .saturating_sub(IS_HEAD_STAR_LENGTH) + .saturating_sub(three_dots_length) + } + + fn get_branch_name_text( + branch_name_length: usize, + displaybranch: &BranchInfo, + three_dots_length: usize, + three_dots: &str, + ) -> String { + let mut branch_name = displaybranch.name.clone(); + if branch_name.len() + > branch_name_length.saturating_sub(three_dots_length) + { + branch_name = branch_name + .unicode_truncate( + branch_name_length + .saturating_sub(three_dots_length), + ) + .0 + .to_string(); + branch_name += three_dots; + } + branch_name + } + /// Get branches to display fn get_text( &self, @@ -648,20 +688,15 @@ impl BranchListPopup { const EMPTY_SYMBOL: char = ' '; const THREE_DOTS: &str = "..."; const THREE_DOTS_LENGTH: usize = THREE_DOTS.len(); // "..." - const COMMIT_HASH_LENGTH: usize = 8; - const COMMIT_DATE_LENGTH: usize = 11; - const IS_HEAD_STAR_LENGTH: usize = 3; // "* " let branch_name_length: usize = width_available as usize * 40 / 100; // commit message takes up the remaining width - let mut commit_message_length: usize = (width_available - as usize) - .saturating_sub(COMMIT_HASH_LENGTH) - .saturating_sub(COMMIT_DATE_LENGTH) - .saturating_sub(branch_name_length) - .saturating_sub(IS_HEAD_STAR_LENGTH) - .saturating_sub(THREE_DOTS_LENGTH); + let shared_commit_message_length: usize = + Self::calculate_shared_commit_message_length( + width_available, + THREE_DOTS_LENGTH, + ); let mut txt = Vec::new(); for (i, displaybranch) in self @@ -678,8 +713,7 @@ impl BranchListPopup { let author_text = displaybranch.top_commit_author.clone() + " "; - // commit_message_length contains author_text length - commit_message_length = commit_message_length + let commit_message_length = shared_commit_message_length .saturating_sub(author_text.len()); let mut commit_message = displaybranch.top_commit_message.clone(); @@ -691,20 +725,12 @@ impl BranchListPopup { commit_message += THREE_DOTS; } - let mut branch_name = displaybranch.name.clone(); - if branch_name.len() - > branch_name_length.saturating_sub(THREE_DOTS_LENGTH) - { - branch_name = branch_name - .unicode_truncate( - branch_name_length - .saturating_sub(THREE_DOTS_LENGTH), - ) - .0 - .to_string(); - branch_name += THREE_DOTS; - } - + let branch_name = Self::get_branch_name_text( + branch_name_length, + displaybranch, + THREE_DOTS_LENGTH, + THREE_DOTS, + ); let selected = (self.selection as usize - self.scroll.get_top()) == i; @@ -751,7 +777,6 @@ impl BranchListPopup { format!("{branch_name:branch_name_length$} "), theme.branch(selected, is_head), ); - txt.push(Line::from(vec![ span_prefix, span_name, @@ -761,7 +786,6 @@ impl BranchListPopup { span_msg, ])); } - Text::from(txt) } diff --git a/src/strings.rs b/src/strings.rs index de7a868b96..b542f37558 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -365,56 +365,54 @@ pub fn rename_branch_popup_msg( } pub fn sort_branch_by_name_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { let hint = - _key_config.get_hint(_key_config.keys.branch_sort_by_name); + key_config.get_hint(key_config.keys.branch_sort_by_name); format!("{: <5}branch name (a -> z)", format!("[{}]", hint)) - .to_string() } pub fn sort_branch_by_name_rev_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { - let hint = _key_config - .get_hint(_key_config.keys.branch_sort_by_name_rev); + let hint = + key_config.get_hint(key_config.keys.branch_sort_by_name_rev); format!("{: <5}branch name (z -> a)", format!("[{}]", hint)) - .to_string() } pub fn sort_branch_by_time_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { let hint = - _key_config.get_hint(_key_config.keys.branch_sort_by_time); + key_config.get_hint(key_config.keys.branch_sort_by_time); format!( "{: <5}last commit time (new -> old)", format!("[{}]", hint) ) } pub fn sort_branch_by_time_rev_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { - let hint = _key_config - .get_hint(_key_config.keys.branch_sort_by_time_rev); + let hint = + key_config.get_hint(key_config.keys.branch_sort_by_time_rev); format!( "{: <5}last commit time (old -> new)", format!("[{}]", hint) ) } pub fn sort_branch_by_author_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { let hint = - _key_config.get_hint(_key_config.keys.branch_sort_by_author); + key_config.get_hint(key_config.keys.branch_sort_by_author); format!( "{: <5}last commit author (a -> z)", format!("[{}]", hint) ) } pub fn sort_branch_by_author_rev_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { - let hint = _key_config - .get_hint(_key_config.keys.branch_sort_by_author_rev); + let hint = key_config + .get_hint(key_config.keys.branch_sort_by_author_rev); format!( "{: <5}last commit author (z -> a)", format!("[{}]", hint) From 7ce8e27735e54f94a66448fd1439c52e24f0f51a Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:22:44 +0900 Subject: [PATCH 05/27] fix unless use of vec! --- src/popups/branch_sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 93977ea952..edcd09ee99 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -48,7 +48,7 @@ impl BranchSortPopup { } fn get_sort_key_lines(&self) -> Vec { - let texts = vec![ + let texts = [ strings::sort_branch_by_name_msg(&self.key_config), strings::sort_branch_by_name_rev_msg(&self.key_config), strings::sort_branch_by_time_msg(&self.key_config), From 5c02b3c2265c990a75d46c9f59070b99acb5a05e Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:23:24 +0900 Subject: [PATCH 06/27] rebase --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d8ba36866..d9291985c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * sign commits using openpgp [[@hendrikmaus](https://github.com/hendrikmaus)] ([#97](https://github.com/extrawurst/gitui/issues/97)) +* add sort_by popup to branchlist [[@UUGTech](https://github.com/UUGTech)]([#2146](https://github.com/extrawurst/gitui/issues/2146)) ### Changed * Make info and error message popups scrollable [[@MichaelAug](https://github.com/MichaelAug)] ([#1138](https://github.com/extrawurst/gitui/issues/1138)) From dec6128a61e5d9e03fa23dd580f9246403c9ff3b Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:14:01 +0900 Subject: [PATCH 07/27] change sort key with navigation keys --- Cargo.lock | 2 + Cargo.toml | 2 + src/components/mod.rs | 8 ++- src/popups/branch_sort.rs | 113 +++++++++++++++++++------------------- src/strings.rs | 75 ++++++++++++------------- 5 files changed, 101 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 030c0ece0b..024fd6771b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -756,6 +756,8 @@ dependencies = [ "shellexpand", "simplelog", "struct-patch", + "strum", + "strum_macros", "syntect", "tempfile", "tui-textarea", diff --git a/Cargo.toml b/Cargo.toml index a759d57c8c..b08074a7c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,8 @@ serde = "1.0" shellexpand = "3.1" simplelog = { version = "0.12", default-features = false } struct-patch = "0.4" +strum = "0.25" +strum_macros = "0.25" syntect = { version = "5.2", default-features = false, features = [ "parsing", "default-syntaxes", diff --git a/src/components/mod.rs b/src/components/mod.rs index 41b15374a8..fc0cdb9205 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -18,6 +18,8 @@ pub use commitlist::CommitList; pub use cred::CredComponent; pub use diff::DiffComponent; pub use revision_files::RevisionFilesComponent; +use strum::EnumIter; +use strum_macros::{EnumCount, EnumIs}; pub use syntax_text::SyntaxTextComponent; pub use textinput::{InputType, TextInputComponent}; pub use utils::{ @@ -191,12 +193,12 @@ pub enum FuzzyFinderTarget { Files, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, EnumCount, EnumIs, EnumIter)] pub enum BranchListSortBy { - LastCommitTimeAsc, - LastCommitTimeDesc, BranchNameAsc, BranchNameDesc, + LastCommitTimeDesc, + LastCommitTimeAsc, LastCommitAuthorAsc, LastCommitAuthorDesc, } diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index edcd09ee99..8c04ecc0ed 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -6,6 +6,7 @@ use ratatui::{ widgets::{Block, Borders, Clear, Paragraph}, Frame, }; +use strum::{EnumCount, IntoEnumIterator}; use crate::{ app::Environment, @@ -22,6 +23,7 @@ use crate::{ pub struct BranchSortPopup { queue: Queue, visible: bool, + selection: BranchListSortBy, key_config: SharedKeyConfig, theme: SharedTheme, } @@ -32,6 +34,7 @@ impl BranchSortPopup { Self { queue: env.queue.clone(), visible: false, + selection: BranchListSortBy::BranchNameAsc, key_config: env.key_config.clone(), theme: env.theme.clone(), } @@ -47,21 +50,46 @@ impl BranchSortPopup { self.queue.push(InternalEvent::BranchListSort(sort_by)); } + fn move_selection(&mut self, up: bool) { + let diff = if up { + BranchListSortBy::COUNT.saturating_sub(1) + } else { + 1 + }; + let new_selection = (self.selection as usize) + .saturating_add(diff) + .rem_euclid(BranchListSortBy::COUNT); + self.selection = BranchListSortBy::iter() + .collect::>()[new_selection]; + } + fn get_sort_key_lines(&self) -> Vec { let texts = [ - strings::sort_branch_by_name_msg(&self.key_config), - strings::sort_branch_by_name_rev_msg(&self.key_config), - strings::sort_branch_by_time_msg(&self.key_config), - strings::sort_branch_by_time_rev_msg(&self.key_config), - strings::sort_branch_by_author_msg(&self.key_config), - strings::sort_branch_by_author_rev_msg(&self.key_config), + strings::sort_branch_by_name_msg( + self.selection.is_branch_name_asc(), + ), + strings::sort_branch_by_name_rev_msg( + self.selection.is_branch_name_desc(), + ), + strings::sort_branch_by_time_msg( + self.selection.is_last_commit_time_desc(), + ), + strings::sort_branch_by_time_rev_msg( + self.selection.is_last_commit_time_asc(), + ), + strings::sort_branch_by_author_msg( + self.selection.is_last_commit_author_asc(), + ), + strings::sort_branch_by_author_rev_msg( + self.selection.is_last_commit_author_desc(), + ), ]; texts .iter() .map(|t| { Line::from(vec![Span::styled( t.clone(), - self.theme.text(true, false), + self.theme.text(true, t.starts_with("[x]")), )]) }) .collect() @@ -71,10 +99,12 @@ impl BranchSortPopup { impl DrawableComponent for BranchSortPopup { fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { if self.is_visible() { - const MAX_SIZE: (u16, u16) = (50, 8); + let height = u16::try_from(BranchListSortBy::COUNT)? + .saturating_add(2); + let max_size: (u16, u16) = (50, height); let mut area = ui::centered_rect_absolute( - MAX_SIZE.0, MAX_SIZE.1, area, + max_size.0, max_size.1, area, ); f.render_widget(Clear, area); @@ -116,7 +146,14 @@ impl Component for BranchSortPopup { ) -> CommandBlocking { if self.is_visible() || force_all { out.push(CommandInfo::new( - strings::commands::close_popup(&self.key_config), + strings::commands::close_branch_sort_popup( + &self.key_config, + ), + true, + true, + )); + out.push(CommandInfo::new( + strings::commands::scroll(&self.key_config), true, true, )); @@ -131,56 +168,20 @@ impl Component for BranchSortPopup { ) -> Result { if self.is_visible() { if let Event::Key(key) = event { - if key_match(key, self.key_config.keys.exit_popup) { - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_name, - ) { - self.update_sort_key( - BranchListSortBy::BranchNameAsc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_name_rev, - ) { - self.update_sort_key( - BranchListSortBy::BranchNameDesc, - ); + if key_match(key, self.key_config.keys.exit_popup) + || key_match(key, self.key_config.keys.enter) + { self.hide(); + } else if key_match(key, self.key_config.keys.move_up) + { + self.move_selection(true); + self.update_sort_key(self.selection); } else if key_match( key, - self.key_config.keys.branch_sort_by_time, + self.key_config.keys.move_down, ) { - self.update_sort_key( - BranchListSortBy::LastCommitTimeDesc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_time_rev, - ) { - self.update_sort_key( - BranchListSortBy::LastCommitTimeAsc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_author, - ) { - self.update_sort_key( - BranchListSortBy::LastCommitAuthorAsc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_author_rev, - ) { - self.update_sort_key( - BranchListSortBy::LastCommitAuthorDesc, - ); - self.hide(); + self.move_selection(false); + self.update_sort_key(self.selection); } } return Ok(EventState::Consumed); diff --git a/src/strings.rs b/src/strings.rs index b542f37558..16a77cf708 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -364,58 +364,40 @@ pub fn rename_branch_popup_msg( "new branch name".to_string() } -pub fn sort_branch_by_name_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_name); - format!("{: <5}branch name (a -> z)", format!("[{}]", hint)) +pub fn sort_branch_by_name_msg(selected: bool) -> String { + format!( + "[{}] branch name (a -> z)", + if selected { "x" } else { " " } + ) } -pub fn sort_branch_by_name_rev_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_name_rev); - format!("{: <5}branch name (z -> a)", format!("[{}]", hint)) +pub fn sort_branch_by_name_rev_msg(selected: bool) -> String { + format!( + "[{}] branch name (z -> a)", + if selected { "x" } else { " " } + ) } -pub fn sort_branch_by_time_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_time); +pub fn sort_branch_by_time_msg(selected: bool) -> String { format!( - "{: <5}last commit time (new -> old)", - format!("[{}]", hint) + "[{}] last commit time (new -> old)", + if selected { "x" } else { " " } ) } -pub fn sort_branch_by_time_rev_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_time_rev); +pub fn sort_branch_by_time_rev_msg(selected: bool) -> String { format!( - "{: <5}last commit time (old -> new)", - format!("[{}]", hint) + "[{}] last commit time (old -> new)", + if selected { "x" } else { " " } ) } -pub fn sort_branch_by_author_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_author); +pub fn sort_branch_by_author_msg(selected: bool) -> String { format!( - "{: <5}last commit author (a -> z)", - format!("[{}]", hint) + "[{}] last commit author (a -> z)", + if selected { "x" } else { " " } ) } -pub fn sort_branch_by_author_rev_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = key_config - .get_hint(key_config.keys.branch_sort_by_author_rev); +pub fn sort_branch_by_author_rev_msg(selected: bool) -> String { format!( - "{: <5}last commit author (z -> a)", - format!("[{}]", hint) + "[{}] last commit author (z -> a)", + if selected { "x" } else { " " } ) } @@ -817,6 +799,19 @@ pub mod commands { CMD_GROUP_GENERAL, ) } + pub fn close_branch_sort_popup( + key_config: &SharedKeyConfig, + ) -> CommandText { + CommandText::new( + format!( + "Close [{}{}]", + key_config.get_hint(key_config.keys.exit_popup), + key_config.get_hint(key_config.keys.enter), + ), + "close branch sort popup", + CMD_GROUP_GENERAL, + ) + } pub fn close_popup(key_config: &SharedKeyConfig) -> CommandText { CommandText::new( format!( From a39bc4cbf9f9fc5c4e8ca11adaa8a7f12476ea62 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:37:30 +0900 Subject: [PATCH 08/27] remove unused keybindings --- src/keys/key_list.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/keys/key_list.rs b/src/keys/key_list.rs index b4cec7a9ce..99f61f92d4 100644 --- a/src/keys/key_list.rs +++ b/src/keys/key_list.rs @@ -108,12 +108,6 @@ pub struct KeysList { pub file_find: GituiKeyEvent, pub branch_find: GituiKeyEvent, pub branch_sort: GituiKeyEvent, - pub branch_sort_by_name: GituiKeyEvent, - pub branch_sort_by_name_rev: GituiKeyEvent, - pub branch_sort_by_time: GituiKeyEvent, - pub branch_sort_by_time_rev: GituiKeyEvent, - pub branch_sort_by_author: GituiKeyEvent, - pub branch_sort_by_author_rev: GituiKeyEvent, pub force_push: GituiKeyEvent, pub fetch: GituiKeyEvent, pub pull: GituiKeyEvent, @@ -211,12 +205,6 @@ impl Default for KeysList { file_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), branch_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), branch_sort: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::empty()), - branch_sort_by_name: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), - branch_sort_by_name_rev: GituiKeyEvent::new(KeyCode::Char('N'), KeyModifiers::SHIFT), - branch_sort_by_time: GituiKeyEvent::new(KeyCode::Char('t'), KeyModifiers::empty()), - branch_sort_by_time_rev: GituiKeyEvent::new(KeyCode::Char('T'), KeyModifiers::SHIFT), - branch_sort_by_author: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()), - branch_sort_by_author_rev: GituiKeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT), diff_hunk_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), diff_hunk_prev: GituiKeyEvent::new(KeyCode::Char('p'), KeyModifiers::empty()), stage_unstage_item: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), From d329b95256200ad4efc6a2901be328cb38d8c97c Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:42:52 +0900 Subject: [PATCH 09/27] fix to use capital X --- src/popups/branch_sort.rs | 2 +- src/strings.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 8c04ecc0ed..dd12d42406 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -89,7 +89,7 @@ impl BranchSortPopup { .map(|t| { Line::from(vec![Span::styled( t.clone(), - self.theme.text(true, t.starts_with("[x]")), + self.theme.text(true, t.starts_with("[X]")), )]) }) .collect() diff --git a/src/strings.rs b/src/strings.rs index 16a77cf708..472f08db74 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -367,37 +367,37 @@ pub fn rename_branch_popup_msg( pub fn sort_branch_by_name_msg(selected: bool) -> String { format!( "[{}] branch name (a -> z)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_name_rev_msg(selected: bool) -> String { format!( "[{}] branch name (z -> a)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_time_msg(selected: bool) -> String { format!( "[{}] last commit time (new -> old)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_time_rev_msg(selected: bool) -> String { format!( "[{}] last commit time (old -> new)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_author_msg(selected: bool) -> String { format!( "[{}] last commit author (a -> z)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_author_rev_msg(selected: bool) -> String { format!( "[{}] last commit author (z -> a)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } From c56227fc1ac60dea08161e04b4f5478b3d5db643 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:40:57 +0900 Subject: [PATCH 10/27] add BranchSortPopup --- asyncgit/src/sync/branch/mod.rs | 13 +- src/app.rs | 31 +++-- src/components/mod.rs | 10 ++ src/keys/key_list.rs | 14 ++ src/popups/branch_sort.rs | 218 ++++++++++++++++++++++++++++++++ src/popups/branchlist.rs | 108 +++++++++++++++- src/popups/mod.rs | 2 + src/queue.rs | 6 +- src/strings.rs | 68 ++++++++++ 9 files changed, 457 insertions(+), 13 deletions(-) create mode 100644 src/popups/branch_sort.rs diff --git a/asyncgit/src/sync/branch/mod.rs b/asyncgit/src/sync/branch/mod.rs index 4dbe89aa3a..e8fa8b4635 100644 --- a/asyncgit/src/sync/branch/mod.rs +++ b/asyncgit/src/sync/branch/mod.rs @@ -11,7 +11,10 @@ use super::{ }; use crate::{ error::{Error, Result}, - sync::{repository::repo, utils::get_head_repo, CommitId}, + sync::{ + repository::repo, utils::get_head_repo, CommitId, + CommitSignature, + }, }; use git2::{Branch, BranchType, Repository}; use scopetime::scope_time; @@ -92,6 +95,10 @@ pub struct BranchInfo { /// pub top_commit: CommitId, /// + pub top_commit_time: i64, + /// + pub top_commit_author: String, + /// pub details: BranchDetails, } @@ -181,6 +188,8 @@ pub fn get_branches_info( }) }; + let author = CommitSignature::from(&top_commit.author()); + Ok(BranchInfo { name: bytes2string(name_bytes)?, reference, @@ -188,6 +197,8 @@ pub fn get_branches_info( top_commit.summary_bytes().unwrap_or_default(), )?, top_commit: top_commit.id().into(), + top_commit_time: top_commit.time().seconds(), + top_commit_author: author.name, details, }) }) diff --git a/src/app.rs b/src/app.rs index f2b4a4a9bb..9bca8820a9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,14 +10,15 @@ use crate::{ options::{Options, SharedOptions}, popup_stack::PopupStack, popups::{ - AppOption, BlameFilePopup, BranchListPopup, CommitPopup, - CompareCommitsPopup, ConfirmPopup, CreateBranchPopup, - ExternalEditorPopup, FetchPopup, FileRevlogPopup, - FuzzyFindPopup, HelpPopup, InspectCommitPopup, - LogSearchPopupPopup, MsgPopup, OptionsPopup, PullPopup, - PushPopup, PushTagsPopup, RenameBranchPopup, ResetPopup, - RevisionFilesPopup, StashMsgPopup, SubmodulesListPopup, - TagCommitPopup, TagListPopup, + AppOption, BlameFilePopup, BranchListPopup, BranchSortPopup, + CommitPopup, CompareCommitsPopup, ConfirmPopup, + CreateBranchPopup, ExternalEditorPopup, FetchPopup, + FileRevlogPopup, FuzzyFindPopup, HelpPopup, + InspectCommitPopup, LogSearchPopupPopup, MsgPopup, + OptionsPopup, PullPopup, PushPopup, PushTagsPopup, + RenameBranchPopup, ResetPopup, RevisionFilesPopup, + StashMsgPopup, SubmodulesListPopup, TagCommitPopup, + TagListPopup, }, queue::{ Action, AppTabs, InternalEvent, NeedsUpdate, Queue, @@ -88,6 +89,7 @@ pub struct App { create_branch_popup: CreateBranchPopup, rename_branch_popup: RenameBranchPopup, select_branch_popup: BranchListPopup, + sort_branch_popup: BranchSortPopup, options_popup: OptionsPopup, submodule_popup: SubmodulesListPopup, tags_popup: TagListPopup, @@ -196,6 +198,7 @@ impl App { submodule_popup: SubmodulesListPopup::new(&env), log_search_popup: LogSearchPopupPopup::new(&env), fuzzy_find_popup: FuzzyFindPopup::new(&env), + sort_branch_popup: BranchSortPopup::new(&env), do_quit: QuitState::None, cmdbar: RefCell::new(CommandBar::new( env.theme.clone(), @@ -468,6 +471,7 @@ impl App { [ log_search_popup, fuzzy_find_popup, + sort_branch_popup, msg_popup, confirm_popup, commit_popup, @@ -517,6 +521,7 @@ impl App { reset_popup, create_branch_popup, rename_branch_popup, + sort_branch_popup, revision_files_popup, fuzzy_find_popup, log_search_popup, @@ -803,6 +808,16 @@ impl App { flags .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); } + InternalEvent::OpenBranchSortPopup(sort_by) => { + self.sort_branch_popup.open(sort_by)?; + flags + .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); + } + InternalEvent::BranchListSort(sort_by) => { + self.select_branch_popup.sort(sort_by)?; + flags + .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); + } InternalEvent::OptionSwitched(o) => { match o { AppOption::StatusShowUntracked => { diff --git a/src/components/mod.rs b/src/components/mod.rs index 4f1f3f4006..41b15374a8 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -191,6 +191,16 @@ pub enum FuzzyFinderTarget { Files, } +#[derive(Copy, Clone)] +pub enum BranchListSortBy { + LastCommitTimeAsc, + LastCommitTimeDesc, + BranchNameAsc, + BranchNameDesc, + LastCommitAuthorAsc, + LastCommitAuthorDesc, +} + impl EventState { pub fn is_consumed(&self) -> bool { *self == Self::Consumed diff --git a/src/keys/key_list.rs b/src/keys/key_list.rs index 71e6756db4..b4cec7a9ce 100644 --- a/src/keys/key_list.rs +++ b/src/keys/key_list.rs @@ -107,6 +107,13 @@ pub struct KeysList { pub open_file_tree: GituiKeyEvent, pub file_find: GituiKeyEvent, pub branch_find: GituiKeyEvent, + pub branch_sort: GituiKeyEvent, + pub branch_sort_by_name: GituiKeyEvent, + pub branch_sort_by_name_rev: GituiKeyEvent, + pub branch_sort_by_time: GituiKeyEvent, + pub branch_sort_by_time_rev: GituiKeyEvent, + pub branch_sort_by_author: GituiKeyEvent, + pub branch_sort_by_author_rev: GituiKeyEvent, pub force_push: GituiKeyEvent, pub fetch: GituiKeyEvent, pub pull: GituiKeyEvent, @@ -203,6 +210,13 @@ impl Default for KeysList { open_file_tree: GituiKeyEvent::new(KeyCode::Char('F'), KeyModifiers::SHIFT), file_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), branch_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), + branch_sort: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::empty()), + branch_sort_by_name: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), + branch_sort_by_name_rev: GituiKeyEvent::new(KeyCode::Char('N'), KeyModifiers::SHIFT), + branch_sort_by_time: GituiKeyEvent::new(KeyCode::Char('t'), KeyModifiers::empty()), + branch_sort_by_time_rev: GituiKeyEvent::new(KeyCode::Char('T'), KeyModifiers::SHIFT), + branch_sort_by_author: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()), + branch_sort_by_author_rev: GituiKeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT), diff_hunk_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), diff_hunk_prev: GituiKeyEvent::new(KeyCode::Char('p'), KeyModifiers::empty()), stage_unstage_item: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs new file mode 100644 index 0000000000..46ccf0fc4d --- /dev/null +++ b/src/popups/branch_sort.rs @@ -0,0 +1,218 @@ +use anyhow::Result; +use crossterm::event::Event; +use ratatui::{ + layout::{Alignment, Margin, Rect}, + text::{Line, Span}, + widgets::{Block, Borders, Clear, Paragraph}, + Frame, +}; + +use crate::{ + app::Environment, + components::{ + visibility_blocking, BranchListSortBy, CommandBlocking, + CommandInfo, Component, DrawableComponent, EventState, + }, + keys::{key_match, SharedKeyConfig}, + queue::{InternalEvent, Queue}, + strings, + ui::{self, style::SharedTheme}, +}; + +pub struct BranchSortPopup { + queue: Queue, + visible: bool, + key_config: SharedKeyConfig, + theme: SharedTheme, +} + +impl BranchSortPopup { + /// + pub fn new(env: &Environment) -> Self { + Self { + queue: env.queue.clone(), + visible: false, + key_config: env.key_config.clone(), + theme: env.theme.clone(), + } + } + + pub fn open(&mut self, sort_by: BranchListSortBy) -> Result<()> { + self.show()?; + self.update_sort_key(sort_by); + + Ok(()) + } + + fn is_visible(&self) -> bool { + self.visible + } + + fn hide(&mut self) { + self.visible = false; + } + + fn show(&mut self) -> Result<()> { + self.visible = true; + Ok(()) + } + + fn update_sort_key(&mut self, sort_by: BranchListSortBy) { + self.queue.push(InternalEvent::BranchListSort(sort_by)); + } + + fn get_sort_key_lines(&self) -> Vec { + let texts = vec![ + strings::sort_branch_by_name_msg(&self.key_config), + strings::sort_branch_by_name_rev_msg(&self.key_config), + strings::sort_branch_by_time_msg(&self.key_config), + strings::sort_branch_by_time_rev_msg(&self.key_config), + strings::sort_branch_by_author_msg(&self.key_config), + strings::sort_branch_by_author_rev_msg(&self.key_config), + ]; + texts + .iter() + .map(|t| { + Line::from(vec![Span::styled( + t.clone(), + self.theme.text(true, false), + )]) + }) + .collect() + } +} + +impl DrawableComponent for BranchSortPopup { + fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { + if self.is_visible() { + const MAX_SIZE: (u16, u16) = (50, 20); + + let mut area = ui::centered_rect_absolute( + MAX_SIZE.0, MAX_SIZE.1, area, + ); + + f.render_widget(Clear, area); + f.render_widget( + Block::default() + .borders(Borders::all()) + .style(self.theme.title(true)) + .title(Span::styled( + strings::POPUP_TITLE_BRANCH_SORT, + self.theme.title(true), + )), + area, + ); + + area = area.inner(&Margin { + horizontal: 1, + vertical: 1, + }); + f.render_widget( + Paragraph::new(self.get_sort_key_lines()) + .block( + Block::default() + .borders(Borders::NONE) + .border_style(self.theme.block(true)), + ) + .alignment(Alignment::Left), + area, + ); + } + Ok(()) + } +} + +impl Component for BranchSortPopup { + fn commands( + &self, + out: &mut Vec, + force_all: bool, + ) -> CommandBlocking { + if self.is_visible() || force_all { + out.push(CommandInfo::new( + strings::commands::close_popup(&self.key_config), + true, + true, + )); + } + + visibility_blocking(self) + } + + fn event( + &mut self, + event: &crossterm::event::Event, + ) -> Result { + if self.is_visible() { + if let Event::Key(key) = event { + if key_match(key, self.key_config.keys.exit_popup) { + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_name, + ) { + self.update_sort_key( + BranchListSortBy::BranchNameAsc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_name_rev, + ) { + self.update_sort_key( + BranchListSortBy::BranchNameDesc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_time, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitTimeDesc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_time_rev, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitTimeAsc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_author, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitAuthorAsc, + ); + self.hide(); + } else if key_match( + key, + self.key_config.keys.branch_sort_by_author_rev, + ) { + self.update_sort_key( + BranchListSortBy::LastCommitAuthorDesc, + ); + self.hide(); + } + } + return Ok(EventState::Consumed); + } + + Ok(EventState::NotConsumed) + } + + fn is_visible(&self) -> bool { + self.visible + } + + fn hide(&mut self) { + self.visible = false; + } + + fn show(&mut self) -> Result<()> { + self.visible = true; + Ok(()) + } +} diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index f7c9b94dd5..142789dec8 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -1,6 +1,7 @@ use crate::components::{ - visibility_blocking, CommandBlocking, CommandInfo, Component, - DrawableComponent, EventState, FuzzyFinderTarget, VerticalScroll, + visibility_blocking, BranchListSortBy, CommandBlocking, + CommandInfo, Component, DrawableComponent, EventState, + FuzzyFinderTarget, VerticalScroll, }; use crate::{ app::Environment, @@ -25,6 +26,7 @@ use asyncgit::{ }, AsyncGitNotification, }; +use chrono::{Local, TimeZone}; use crossterm::event::{Event, KeyEvent}; use ratatui::{ layout::{ @@ -44,6 +46,7 @@ use super::InspectCommitOpen; pub struct BranchListPopup { repo: RepoPathRef, branches: Vec, + sort_by: BranchListSortBy, local: bool, has_remotes: bool, visible: bool, @@ -211,6 +214,12 @@ impl Component for BranchListPopup { true, true, )); + + out.push(CommandInfo::new( + strings::commands::sort_branch(&self.key_config), + true, + true, + )) } visibility_blocking(self) } @@ -304,6 +313,10 @@ impl Component for BranchListPopup { branches, FuzzyFinderTarget::Branches, )); + } else if key_match(e, self.key_config.keys.branch_sort) { + self.queue.push(InternalEvent::OpenBranchSortPopup( + self.sort_by.clone(), + )); } } @@ -329,6 +342,7 @@ impl BranchListPopup { pub fn new(env: &Environment) -> Self { Self { branches: Vec::new(), + sort_by: BranchListSortBy::BranchNameAsc, local: true, has_remotes: false, visible: false, @@ -399,12 +413,79 @@ impl BranchListPopup { } } + pub fn sort(&mut self, sort_by: BranchListSortBy) -> Result<()> { + self.sort_by = sort_by; + self.update_branches()?; + + Ok(()) + } + /// fetch list of branches pub fn update_branches(&mut self) -> Result<()> { if self.is_visible() { self.check_remotes(); self.branches = get_branches_info(&self.repo.borrow(), self.local)?; + match &self.sort_by { + BranchListSortBy::LastCommitAuthorAsc => { + self.branches.sort_by(|a, b| { + match b + .top_commit_author + .cmp(&a.top_commit_author) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitAuthorDesc => { + self.branches.sort_by(|a, b| { + match a + .top_commit_author + .cmp(&b.top_commit_author) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitTimeAsc => { + self.branches.sort_by(|a, b| { + match a + .top_commit_time + .cmp(&b.top_commit_time) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitTimeDesc => { + self.branches.sort_by(|a, b| { + match b + .top_commit_time + .cmp(&a.top_commit_time) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::BranchNameAsc => { + self.branches.sort_by(|a, b| a.name.cmp(&b.name)) + } + BranchListSortBy::BranchNameDesc => { + self.branches.sort_by(|a, b| b.name.cmp(&a.name)) + } + } //remove remote branch called `HEAD` if !self.local { self.branches @@ -568,13 +649,16 @@ impl BranchListPopup { const THREE_DOTS: &str = "..."; const THREE_DOTS_LENGTH: usize = THREE_DOTS.len(); // "..." const COMMIT_HASH_LENGTH: usize = 8; + const COMMIT_DATE_LENGTH: usize = 11; const IS_HEAD_STAR_LENGTH: usize = 3; // "* " let branch_name_length: usize = width_available as usize * 40 / 100; // commit message takes up the remaining width - let commit_message_length: usize = (width_available as usize) + let mut commit_message_length: usize = (width_available + as usize) .saturating_sub(COMMIT_HASH_LENGTH) + .saturating_sub(COMMIT_DATE_LENGTH) .saturating_sub(branch_name_length) .saturating_sub(IS_HEAD_STAR_LENGTH) .saturating_sub(THREE_DOTS_LENGTH); @@ -587,6 +671,16 @@ impl BranchListPopup { .take(height) .enumerate() { + let date_local = Local + .timestamp_opt(displaybranch.top_commit_time, 0) + .unwrap(); + let date_text = date_local.date_naive().to_string() + " "; + let author_text = + displaybranch.top_commit_author.clone() + " "; + + // commit_message_length contains author_text length + commit_message_length = commit_message_length + .saturating_sub(author_text.len()); let mut commit_message = displaybranch.top_commit_message.clone(); if commit_message.len() > commit_message_length { @@ -643,6 +737,12 @@ impl BranchListPopup { ), theme.commit_hash(selected), ); + let span_date = + Span::styled(date_text, theme.text(true, selected)); + let span_author = Span::styled( + author_text, + theme.commit_author(selected), + ); let span_msg = Span::styled( commit_message.to_string(), theme.text(true, selected), @@ -656,6 +756,8 @@ impl BranchListPopup { span_prefix, span_name, span_hash, + span_date, + span_author, span_msg, ])); } diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 2216461ad7..95b1094b50 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -1,5 +1,6 @@ mod blame_file; mod branchlist; +mod branch_sort; mod commit; mod compare_commits; mod confirm; @@ -26,6 +27,7 @@ mod taglist; pub use blame_file::{BlameFileOpen, BlameFilePopup}; pub use branchlist::BranchListPopup; +pub use branch_sort::BranchSortPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; pub use confirm::ConfirmPopup; diff --git a/src/queue.rs b/src/queue.rs index 9ee7830bdd..842e528f99 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,5 +1,5 @@ use crate::{ - components::FuzzyFinderTarget, + components::{BranchListSortBy, FuzzyFinderTarget}, popups::{ AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen, InspectCommitOpen, @@ -113,6 +113,10 @@ pub enum InternalEvent { /// SelectBranch, /// + OpenBranchSortPopup(BranchListSortBy), + /// + BranchListSort(BranchListSortBy), + /// OpenExternalEditor(Option), /// Push(String, PushType, bool, bool), diff --git a/src/strings.rs b/src/strings.rs index 67eb79ba7a..de7a868b96 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -32,6 +32,7 @@ pub static PUSH_TAGS_STATES_DONE: &str = "done"; pub static POPUP_TITLE_SUBMODULES: &str = "Submodules"; pub static POPUP_TITLE_FUZZY_FIND: &str = "Fuzzy Finder"; pub static POPUP_TITLE_LOG_SEARCH: &str = "Search"; +pub static POPUP_TITLE_BRANCH_SORT: &str = "Sort by"; pub static POPUP_FAIL_COPY: &str = "Failed to copy text"; pub static POPUP_SUCCESS_COPY: &str = "Copied Text"; @@ -363,6 +364,63 @@ pub fn rename_branch_popup_msg( "new branch name".to_string() } +pub fn sort_branch_by_name_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = + _key_config.get_hint(_key_config.keys.branch_sort_by_name); + format!("{: <5}branch name (a -> z)", format!("[{}]", hint)) + .to_string() +} +pub fn sort_branch_by_name_rev_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = _key_config + .get_hint(_key_config.keys.branch_sort_by_name_rev); + format!("{: <5}branch name (z -> a)", format!("[{}]", hint)) + .to_string() +} +pub fn sort_branch_by_time_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = + _key_config.get_hint(_key_config.keys.branch_sort_by_time); + format!( + "{: <5}last commit time (new -> old)", + format!("[{}]", hint) + ) +} +pub fn sort_branch_by_time_rev_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = _key_config + .get_hint(_key_config.keys.branch_sort_by_time_rev); + format!( + "{: <5}last commit time (old -> new)", + format!("[{}]", hint) + ) +} +pub fn sort_branch_by_author_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = + _key_config.get_hint(_key_config.keys.branch_sort_by_author); + format!( + "{: <5}last commit author (a -> z)", + format!("[{}]", hint) + ) +} +pub fn sort_branch_by_author_rev_msg( + _key_config: &SharedKeyConfig, +) -> String { + let hint = _key_config + .get_hint(_key_config.keys.branch_sort_by_author_rev); + format!( + "{: <5}last commit author (z -> a)", + format!("[{}]", hint) + ) +} + pub fn copy_success(s: &str) -> String { format!("{POPUP_SUCCESS_COPY} \"{s}\"") } @@ -469,6 +527,16 @@ pub mod commands { CMD_GROUP_GENERAL, ) } + pub fn sort_branch(key_config: &SharedKeyConfig) -> CommandText { + CommandText::new( + format!( + "Sort by [{}]", + key_config.get_hint(key_config.keys.branch_sort) + ), + "sort branches", + CMD_GROUP_GENERAL, + ) + } pub fn toggle_tabs_direct( key_config: &SharedKeyConfig, ) -> CommandText { From d2a4c8fc57f61a32dca876581c8421b0a1be13f3 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:53:09 +0900 Subject: [PATCH 11/27] fix height of branch sort popup --- src/popups/branch_sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 46ccf0fc4d..471dad308c 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -85,7 +85,7 @@ impl BranchSortPopup { impl DrawableComponent for BranchSortPopup { fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { if self.is_visible() { - const MAX_SIZE: (u16, u16) = (50, 20); + const MAX_SIZE: (u16, u16) = (50, 8); let mut area = ui::centered_rect_absolute( MAX_SIZE.0, MAX_SIZE.1, area, From 0271e5becbeaf16a1fbeca8c41331875b06e7da6 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:53:20 +0900 Subject: [PATCH 12/27] small fix --- src/popups/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 95b1094b50..81f1990935 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -1,6 +1,6 @@ mod blame_file; -mod branchlist; mod branch_sort; +mod branchlist; mod commit; mod compare_commits; mod confirm; @@ -26,8 +26,8 @@ mod tag_commit; mod taglist; pub use blame_file::{BlameFileOpen, BlameFilePopup}; -pub use branchlist::BranchListPopup; pub use branch_sort::BranchSortPopup; +pub use branchlist::BranchListPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; pub use confirm::ConfirmPopup; From bed46c88d7220f23a825c7546e75a50ba2f2cf1d Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:09:59 +0900 Subject: [PATCH 13/27] fix clippy --- src/popups/branch_sort.rs | 14 ------- src/popups/branchlist.rs | 88 +++++++++++++++++++++++++-------------- src/strings.rs | 32 +++++++------- 3 files changed, 71 insertions(+), 63 deletions(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 471dad308c..93977ea952 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -40,20 +40,6 @@ impl BranchSortPopup { pub fn open(&mut self, sort_by: BranchListSortBy) -> Result<()> { self.show()?; self.update_sort_key(sort_by); - - Ok(()) - } - - fn is_visible(&self) -> bool { - self.visible - } - - fn hide(&mut self) { - self.visible = false; - } - - fn show(&mut self) -> Result<()> { - self.visible = true; Ok(()) } diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index 142789dec8..26353f4af3 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -219,7 +219,7 @@ impl Component for BranchListPopup { strings::commands::sort_branch(&self.key_config), true, true, - )) + )); } visibility_blocking(self) } @@ -315,7 +315,7 @@ impl Component for BranchListPopup { )); } else if key_match(e, self.key_config.keys.branch_sort) { self.queue.push(InternalEvent::OpenBranchSortPopup( - self.sort_by.clone(), + self.sort_by, )); } } @@ -480,10 +480,10 @@ impl BranchListPopup { }); } BranchListSortBy::BranchNameAsc => { - self.branches.sort_by(|a, b| a.name.cmp(&b.name)) + self.branches.sort_by(|a, b| a.name.cmp(&b.name)); } BranchListSortBy::BranchNameDesc => { - self.branches.sort_by(|a, b| b.name.cmp(&a.name)) + self.branches.sort_by(|a, b| b.name.cmp(&a.name)); } } //remove remote branch called `HEAD` @@ -635,6 +635,46 @@ impl BranchListPopup { Ok(()) } + const fn calculate_shared_commit_message_length( + width_available: u16, + three_dots_length: usize, + ) -> usize { + const COMMIT_HASH_LENGTH: usize = 8; + const COMMIT_DATE_LENGTH: usize = 11; + const IS_HEAD_STAR_LENGTH: usize = 3; + let branch_name_length: usize = + width_available as usize * 40 / 100; + + (width_available as usize) + .saturating_sub(COMMIT_HASH_LENGTH) + .saturating_sub(COMMIT_DATE_LENGTH) + .saturating_sub(branch_name_length) + .saturating_sub(IS_HEAD_STAR_LENGTH) + .saturating_sub(three_dots_length) + } + + fn get_branch_name_text( + branch_name_length: usize, + displaybranch: &BranchInfo, + three_dots_length: usize, + three_dots: &str, + ) -> String { + let mut branch_name = displaybranch.name.clone(); + if branch_name.len() + > branch_name_length.saturating_sub(three_dots_length) + { + branch_name = branch_name + .unicode_truncate( + branch_name_length + .saturating_sub(three_dots_length), + ) + .0 + .to_string(); + branch_name += three_dots; + } + branch_name + } + /// Get branches to display fn get_text( &self, @@ -648,20 +688,15 @@ impl BranchListPopup { const EMPTY_SYMBOL: char = ' '; const THREE_DOTS: &str = "..."; const THREE_DOTS_LENGTH: usize = THREE_DOTS.len(); // "..." - const COMMIT_HASH_LENGTH: usize = 8; - const COMMIT_DATE_LENGTH: usize = 11; - const IS_HEAD_STAR_LENGTH: usize = 3; // "* " let branch_name_length: usize = width_available as usize * 40 / 100; // commit message takes up the remaining width - let mut commit_message_length: usize = (width_available - as usize) - .saturating_sub(COMMIT_HASH_LENGTH) - .saturating_sub(COMMIT_DATE_LENGTH) - .saturating_sub(branch_name_length) - .saturating_sub(IS_HEAD_STAR_LENGTH) - .saturating_sub(THREE_DOTS_LENGTH); + let shared_commit_message_length: usize = + Self::calculate_shared_commit_message_length( + width_available, + THREE_DOTS_LENGTH, + ); let mut txt = Vec::new(); for (i, displaybranch) in self @@ -678,8 +713,7 @@ impl BranchListPopup { let author_text = displaybranch.top_commit_author.clone() + " "; - // commit_message_length contains author_text length - commit_message_length = commit_message_length + let commit_message_length = shared_commit_message_length .saturating_sub(author_text.len()); let mut commit_message = displaybranch.top_commit_message.clone(); @@ -691,20 +725,12 @@ impl BranchListPopup { commit_message += THREE_DOTS; } - let mut branch_name = displaybranch.name.clone(); - if branch_name.len() - > branch_name_length.saturating_sub(THREE_DOTS_LENGTH) - { - branch_name = branch_name - .unicode_truncate( - branch_name_length - .saturating_sub(THREE_DOTS_LENGTH), - ) - .0 - .to_string(); - branch_name += THREE_DOTS; - } - + let branch_name = Self::get_branch_name_text( + branch_name_length, + displaybranch, + THREE_DOTS_LENGTH, + THREE_DOTS, + ); let selected = (self.selection as usize - self.scroll.get_top()) == i; @@ -751,7 +777,6 @@ impl BranchListPopup { format!("{branch_name:branch_name_length$} "), theme.branch(selected, is_head), ); - txt.push(Line::from(vec![ span_prefix, span_name, @@ -761,7 +786,6 @@ impl BranchListPopup { span_msg, ])); } - Text::from(txt) } diff --git a/src/strings.rs b/src/strings.rs index de7a868b96..b542f37558 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -365,56 +365,54 @@ pub fn rename_branch_popup_msg( } pub fn sort_branch_by_name_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { let hint = - _key_config.get_hint(_key_config.keys.branch_sort_by_name); + key_config.get_hint(key_config.keys.branch_sort_by_name); format!("{: <5}branch name (a -> z)", format!("[{}]", hint)) - .to_string() } pub fn sort_branch_by_name_rev_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { - let hint = _key_config - .get_hint(_key_config.keys.branch_sort_by_name_rev); + let hint = + key_config.get_hint(key_config.keys.branch_sort_by_name_rev); format!("{: <5}branch name (z -> a)", format!("[{}]", hint)) - .to_string() } pub fn sort_branch_by_time_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { let hint = - _key_config.get_hint(_key_config.keys.branch_sort_by_time); + key_config.get_hint(key_config.keys.branch_sort_by_time); format!( "{: <5}last commit time (new -> old)", format!("[{}]", hint) ) } pub fn sort_branch_by_time_rev_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { - let hint = _key_config - .get_hint(_key_config.keys.branch_sort_by_time_rev); + let hint = + key_config.get_hint(key_config.keys.branch_sort_by_time_rev); format!( "{: <5}last commit time (old -> new)", format!("[{}]", hint) ) } pub fn sort_branch_by_author_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { let hint = - _key_config.get_hint(_key_config.keys.branch_sort_by_author); + key_config.get_hint(key_config.keys.branch_sort_by_author); format!( "{: <5}last commit author (a -> z)", format!("[{}]", hint) ) } pub fn sort_branch_by_author_rev_msg( - _key_config: &SharedKeyConfig, + key_config: &SharedKeyConfig, ) -> String { - let hint = _key_config - .get_hint(_key_config.keys.branch_sort_by_author_rev); + let hint = key_config + .get_hint(key_config.keys.branch_sort_by_author_rev); format!( "{: <5}last commit author (z -> a)", format!("[{}]", hint) From 4443fafc35a456978652ed7d80c64d946f8e4eb0 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:22:44 +0900 Subject: [PATCH 14/27] fix unless use of vec! --- src/popups/branch_sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 93977ea952..edcd09ee99 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -48,7 +48,7 @@ impl BranchSortPopup { } fn get_sort_key_lines(&self) -> Vec { - let texts = vec![ + let texts = [ strings::sort_branch_by_name_msg(&self.key_config), strings::sort_branch_by_name_rev_msg(&self.key_config), strings::sort_branch_by_time_msg(&self.key_config), From ae314564d570cba94cfe79d603f4800e86ecb8e4 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:23:24 +0900 Subject: [PATCH 15/27] rebase --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45991a09c4..8597c69a3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * sign commits using openpgp [[@hendrikmaus](https://github.com/hendrikmaus)] ([#97](https://github.com/extrawurst/gitui/issues/97)) +* add sort_by popup to branchlist [[@UUGTech](https://github.com/UUGTech)]([#2146](https://github.com/extrawurst/gitui/issues/2146)) ### Changed * Make info and error message popups scrollable [[@MichaelAug](https://github.com/MichaelAug)] ([#1138](https://github.com/extrawurst/gitui/issues/1138)) From 5a6a679cc6b3378edfa2ec83b8be386d748932fc Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:14:01 +0900 Subject: [PATCH 16/27] change sort key with navigation keys --- Cargo.lock | 2 + Cargo.toml | 2 + src/components/mod.rs | 8 ++- src/popups/branch_sort.rs | 113 +++++++++++++++++++------------------- src/strings.rs | 75 ++++++++++++------------- 5 files changed, 101 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 70e7f6c210..6b09342242 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -756,6 +756,8 @@ dependencies = [ "shellexpand", "simplelog", "struct-patch", + "strum", + "strum_macros", "syntect", "tempfile", "tui-textarea", diff --git a/Cargo.toml b/Cargo.toml index 215365446a..9eb183fa40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,8 @@ serde = "1.0" shellexpand = "3.1" simplelog = { version = "0.12", default-features = false } struct-patch = "0.4" +strum = "0.25" +strum_macros = "0.25" syntect = { version = "5.2", default-features = false, features = [ "parsing", "default-syntaxes", diff --git a/src/components/mod.rs b/src/components/mod.rs index 41b15374a8..fc0cdb9205 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -18,6 +18,8 @@ pub use commitlist::CommitList; pub use cred::CredComponent; pub use diff::DiffComponent; pub use revision_files::RevisionFilesComponent; +use strum::EnumIter; +use strum_macros::{EnumCount, EnumIs}; pub use syntax_text::SyntaxTextComponent; pub use textinput::{InputType, TextInputComponent}; pub use utils::{ @@ -191,12 +193,12 @@ pub enum FuzzyFinderTarget { Files, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, EnumCount, EnumIs, EnumIter)] pub enum BranchListSortBy { - LastCommitTimeAsc, - LastCommitTimeDesc, BranchNameAsc, BranchNameDesc, + LastCommitTimeDesc, + LastCommitTimeAsc, LastCommitAuthorAsc, LastCommitAuthorDesc, } diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index edcd09ee99..8c04ecc0ed 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -6,6 +6,7 @@ use ratatui::{ widgets::{Block, Borders, Clear, Paragraph}, Frame, }; +use strum::{EnumCount, IntoEnumIterator}; use crate::{ app::Environment, @@ -22,6 +23,7 @@ use crate::{ pub struct BranchSortPopup { queue: Queue, visible: bool, + selection: BranchListSortBy, key_config: SharedKeyConfig, theme: SharedTheme, } @@ -32,6 +34,7 @@ impl BranchSortPopup { Self { queue: env.queue.clone(), visible: false, + selection: BranchListSortBy::BranchNameAsc, key_config: env.key_config.clone(), theme: env.theme.clone(), } @@ -47,21 +50,46 @@ impl BranchSortPopup { self.queue.push(InternalEvent::BranchListSort(sort_by)); } + fn move_selection(&mut self, up: bool) { + let diff = if up { + BranchListSortBy::COUNT.saturating_sub(1) + } else { + 1 + }; + let new_selection = (self.selection as usize) + .saturating_add(diff) + .rem_euclid(BranchListSortBy::COUNT); + self.selection = BranchListSortBy::iter() + .collect::>()[new_selection]; + } + fn get_sort_key_lines(&self) -> Vec { let texts = [ - strings::sort_branch_by_name_msg(&self.key_config), - strings::sort_branch_by_name_rev_msg(&self.key_config), - strings::sort_branch_by_time_msg(&self.key_config), - strings::sort_branch_by_time_rev_msg(&self.key_config), - strings::sort_branch_by_author_msg(&self.key_config), - strings::sort_branch_by_author_rev_msg(&self.key_config), + strings::sort_branch_by_name_msg( + self.selection.is_branch_name_asc(), + ), + strings::sort_branch_by_name_rev_msg( + self.selection.is_branch_name_desc(), + ), + strings::sort_branch_by_time_msg( + self.selection.is_last_commit_time_desc(), + ), + strings::sort_branch_by_time_rev_msg( + self.selection.is_last_commit_time_asc(), + ), + strings::sort_branch_by_author_msg( + self.selection.is_last_commit_author_asc(), + ), + strings::sort_branch_by_author_rev_msg( + self.selection.is_last_commit_author_desc(), + ), ]; texts .iter() .map(|t| { Line::from(vec![Span::styled( t.clone(), - self.theme.text(true, false), + self.theme.text(true, t.starts_with("[x]")), )]) }) .collect() @@ -71,10 +99,12 @@ impl BranchSortPopup { impl DrawableComponent for BranchSortPopup { fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { if self.is_visible() { - const MAX_SIZE: (u16, u16) = (50, 8); + let height = u16::try_from(BranchListSortBy::COUNT)? + .saturating_add(2); + let max_size: (u16, u16) = (50, height); let mut area = ui::centered_rect_absolute( - MAX_SIZE.0, MAX_SIZE.1, area, + max_size.0, max_size.1, area, ); f.render_widget(Clear, area); @@ -116,7 +146,14 @@ impl Component for BranchSortPopup { ) -> CommandBlocking { if self.is_visible() || force_all { out.push(CommandInfo::new( - strings::commands::close_popup(&self.key_config), + strings::commands::close_branch_sort_popup( + &self.key_config, + ), + true, + true, + )); + out.push(CommandInfo::new( + strings::commands::scroll(&self.key_config), true, true, )); @@ -131,56 +168,20 @@ impl Component for BranchSortPopup { ) -> Result { if self.is_visible() { if let Event::Key(key) = event { - if key_match(key, self.key_config.keys.exit_popup) { - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_name, - ) { - self.update_sort_key( - BranchListSortBy::BranchNameAsc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_name_rev, - ) { - self.update_sort_key( - BranchListSortBy::BranchNameDesc, - ); + if key_match(key, self.key_config.keys.exit_popup) + || key_match(key, self.key_config.keys.enter) + { self.hide(); + } else if key_match(key, self.key_config.keys.move_up) + { + self.move_selection(true); + self.update_sort_key(self.selection); } else if key_match( key, - self.key_config.keys.branch_sort_by_time, + self.key_config.keys.move_down, ) { - self.update_sort_key( - BranchListSortBy::LastCommitTimeDesc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_time_rev, - ) { - self.update_sort_key( - BranchListSortBy::LastCommitTimeAsc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_author, - ) { - self.update_sort_key( - BranchListSortBy::LastCommitAuthorAsc, - ); - self.hide(); - } else if key_match( - key, - self.key_config.keys.branch_sort_by_author_rev, - ) { - self.update_sort_key( - BranchListSortBy::LastCommitAuthorDesc, - ); - self.hide(); + self.move_selection(false); + self.update_sort_key(self.selection); } } return Ok(EventState::Consumed); diff --git a/src/strings.rs b/src/strings.rs index b542f37558..16a77cf708 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -364,58 +364,40 @@ pub fn rename_branch_popup_msg( "new branch name".to_string() } -pub fn sort_branch_by_name_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_name); - format!("{: <5}branch name (a -> z)", format!("[{}]", hint)) +pub fn sort_branch_by_name_msg(selected: bool) -> String { + format!( + "[{}] branch name (a -> z)", + if selected { "x" } else { " " } + ) } -pub fn sort_branch_by_name_rev_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_name_rev); - format!("{: <5}branch name (z -> a)", format!("[{}]", hint)) +pub fn sort_branch_by_name_rev_msg(selected: bool) -> String { + format!( + "[{}] branch name (z -> a)", + if selected { "x" } else { " " } + ) } -pub fn sort_branch_by_time_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_time); +pub fn sort_branch_by_time_msg(selected: bool) -> String { format!( - "{: <5}last commit time (new -> old)", - format!("[{}]", hint) + "[{}] last commit time (new -> old)", + if selected { "x" } else { " " } ) } -pub fn sort_branch_by_time_rev_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_time_rev); +pub fn sort_branch_by_time_rev_msg(selected: bool) -> String { format!( - "{: <5}last commit time (old -> new)", - format!("[{}]", hint) + "[{}] last commit time (old -> new)", + if selected { "x" } else { " " } ) } -pub fn sort_branch_by_author_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = - key_config.get_hint(key_config.keys.branch_sort_by_author); +pub fn sort_branch_by_author_msg(selected: bool) -> String { format!( - "{: <5}last commit author (a -> z)", - format!("[{}]", hint) + "[{}] last commit author (a -> z)", + if selected { "x" } else { " " } ) } -pub fn sort_branch_by_author_rev_msg( - key_config: &SharedKeyConfig, -) -> String { - let hint = key_config - .get_hint(key_config.keys.branch_sort_by_author_rev); +pub fn sort_branch_by_author_rev_msg(selected: bool) -> String { format!( - "{: <5}last commit author (z -> a)", - format!("[{}]", hint) + "[{}] last commit author (z -> a)", + if selected { "x" } else { " " } ) } @@ -817,6 +799,19 @@ pub mod commands { CMD_GROUP_GENERAL, ) } + pub fn close_branch_sort_popup( + key_config: &SharedKeyConfig, + ) -> CommandText { + CommandText::new( + format!( + "Close [{}{}]", + key_config.get_hint(key_config.keys.exit_popup), + key_config.get_hint(key_config.keys.enter), + ), + "close branch sort popup", + CMD_GROUP_GENERAL, + ) + } pub fn close_popup(key_config: &SharedKeyConfig) -> CommandText { CommandText::new( format!( From 280689440b81ec78b43fb04da185f1d363ef9155 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:37:30 +0900 Subject: [PATCH 17/27] remove unused keybindings --- src/keys/key_list.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/keys/key_list.rs b/src/keys/key_list.rs index b4cec7a9ce..99f61f92d4 100644 --- a/src/keys/key_list.rs +++ b/src/keys/key_list.rs @@ -108,12 +108,6 @@ pub struct KeysList { pub file_find: GituiKeyEvent, pub branch_find: GituiKeyEvent, pub branch_sort: GituiKeyEvent, - pub branch_sort_by_name: GituiKeyEvent, - pub branch_sort_by_name_rev: GituiKeyEvent, - pub branch_sort_by_time: GituiKeyEvent, - pub branch_sort_by_time_rev: GituiKeyEvent, - pub branch_sort_by_author: GituiKeyEvent, - pub branch_sort_by_author_rev: GituiKeyEvent, pub force_push: GituiKeyEvent, pub fetch: GituiKeyEvent, pub pull: GituiKeyEvent, @@ -211,12 +205,6 @@ impl Default for KeysList { file_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), branch_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), branch_sort: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::empty()), - branch_sort_by_name: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), - branch_sort_by_name_rev: GituiKeyEvent::new(KeyCode::Char('N'), KeyModifiers::SHIFT), - branch_sort_by_time: GituiKeyEvent::new(KeyCode::Char('t'), KeyModifiers::empty()), - branch_sort_by_time_rev: GituiKeyEvent::new(KeyCode::Char('T'), KeyModifiers::SHIFT), - branch_sort_by_author: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()), - branch_sort_by_author_rev: GituiKeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT), diff_hunk_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), diff_hunk_prev: GituiKeyEvent::new(KeyCode::Char('p'), KeyModifiers::empty()), stage_unstage_item: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), From 0c1c973d18617d5ea489b3b76babb1a83f578e02 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:42:52 +0900 Subject: [PATCH 18/27] fix to use capital X --- src/popups/branch_sort.rs | 2 +- src/strings.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index 8c04ecc0ed..dd12d42406 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -89,7 +89,7 @@ impl BranchSortPopup { .map(|t| { Line::from(vec![Span::styled( t.clone(), - self.theme.text(true, t.starts_with("[x]")), + self.theme.text(true, t.starts_with("[X]")), )]) }) .collect() diff --git a/src/strings.rs b/src/strings.rs index 16a77cf708..472f08db74 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -367,37 +367,37 @@ pub fn rename_branch_popup_msg( pub fn sort_branch_by_name_msg(selected: bool) -> String { format!( "[{}] branch name (a -> z)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_name_rev_msg(selected: bool) -> String { format!( "[{}] branch name (z -> a)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_time_msg(selected: bool) -> String { format!( "[{}] last commit time (new -> old)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_time_rev_msg(selected: bool) -> String { format!( "[{}] last commit time (old -> new)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_author_msg(selected: bool) -> String { format!( "[{}] last commit author (a -> z)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } pub fn sort_branch_by_author_rev_msg(selected: bool) -> String { format!( "[{}] last commit author (z -> a)", - if selected { "x" } else { " " } + if selected { "X" } else { " " } ) } From 084879044d516bc080bb116102233f4c1ab92c68 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Sun, 14 Apr 2024 14:37:26 +0900 Subject: [PATCH 19/27] avoid unwrap() --- src/popups/branchlist.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index 26353f4af3..09d5343368 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -706,10 +706,12 @@ impl BranchListPopup { .take(height) .enumerate() { - let date_local = Local + let date_text = Local .timestamp_opt(displaybranch.top_commit_time, 0) - .unwrap(); - let date_text = date_local.date_naive().to_string() + " "; + .earliest() + .map_or("????-??-?? ".to_string(), |date| { + date.date_naive().to_string() + " " + }); let author_text = displaybranch.top_commit_author.clone() + " "; From c87ee7978583c4ac09be5ad8ce72e7c35b90315e Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Sun, 14 Apr 2024 15:08:14 +0900 Subject: [PATCH 20/27] store top_commit_time_local inside of BranchInfo --- Cargo.lock | 1 + asyncgit/Cargo.toml | 1 + asyncgit/src/sync/branch/mod.rs | 6 ++++++ src/popups/branchlist.rs | 10 ++++------ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 024fd6771b..dfeec1b566 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,7 @@ name = "asyncgit" version = "0.25.1" dependencies = [ "bitflags 2.5.0", + "chrono", "crossbeam-channel", "easy-cast", "env_logger", diff --git a/asyncgit/Cargo.toml b/asyncgit/Cargo.toml index eec1e09ce4..7ef643d1ec 100644 --- a/asyncgit/Cargo.toml +++ b/asyncgit/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["git"] [dependencies] bitflags = "2" +chrono = { version = "0.4", default-features = false, features = ["clock"] } crossbeam-channel = "0.5" easy-cast = "0.5" fuzzy-matcher = "0.3" diff --git a/asyncgit/src/sync/branch/mod.rs b/asyncgit/src/sync/branch/mod.rs index e8fa8b4635..7f39a5999e 100644 --- a/asyncgit/src/sync/branch/mod.rs +++ b/asyncgit/src/sync/branch/mod.rs @@ -16,6 +16,7 @@ use crate::{ CommitSignature, }, }; +use chrono::{DateTime, Local, TimeZone}; use git2::{Branch, BranchType, Repository}; use scopetime::scope_time; use std::collections::HashSet; @@ -97,6 +98,8 @@ pub struct BranchInfo { /// pub top_commit_time: i64, /// + pub top_commit_time_local: Option>, + /// pub top_commit_author: String, /// pub details: BranchDetails, @@ -198,6 +201,9 @@ pub fn get_branches_info( )?, top_commit: top_commit.id().into(), top_commit_time: top_commit.time().seconds(), + top_commit_time_local: Local + .timestamp_opt(top_commit.time().seconds(), 0) + .earliest(), top_commit_author: author.name, details, }) diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index 09d5343368..4bd9b1c485 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -26,7 +26,6 @@ use asyncgit::{ }, AsyncGitNotification, }; -use chrono::{Local, TimeZone}; use crossterm::event::{Event, KeyEvent}; use ratatui::{ layout::{ @@ -706,11 +705,10 @@ impl BranchListPopup { .take(height) .enumerate() { - let date_text = Local - .timestamp_opt(displaybranch.top_commit_time, 0) - .earliest() - .map_or("????-??-?? ".to_string(), |date| { - date.date_naive().to_string() + " " + let date_text = displaybranch + .top_commit_time_local + .map_or("????-??-?? ".to_string(), |time| { + time.date_naive().to_string() + " " }); let author_text = displaybranch.top_commit_author.clone() + " "; From 73d6171775b83c8ce93a8770c1fd20f39b5cad69 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Sun, 14 Apr 2024 15:51:35 +0900 Subject: [PATCH 21/27] fix clippy --- src/popups/branchlist.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index f843e688a6..ca40361bc9 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -104,6 +104,8 @@ impl DrawableComponent for BranchListPopup { } impl Component for BranchListPopup { + // TODO: clean up + #[allow(clippy::too_many_lines)] fn commands( &self, out: &mut Vec, @@ -219,6 +221,7 @@ impl Component for BranchListPopup { true, true, )); + out.push(CommandInfo::new( strings::commands::reset_branch(&self.key_config), self.valid_selection(), From 9e249e7d56bf1dfbbd641469637be35391513c6f Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Sun, 14 Apr 2024 16:51:57 +0900 Subject: [PATCH 22/27] change the key to sort --- src/keys/key_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keys/key_list.rs b/src/keys/key_list.rs index c249b3c882..081a78cdbf 100644 --- a/src/keys/key_list.rs +++ b/src/keys/key_list.rs @@ -206,7 +206,7 @@ impl Default for KeysList { open_file_tree: GituiKeyEvent::new(KeyCode::Char('F'), KeyModifiers::SHIFT), file_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), branch_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()), - branch_sort: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::empty()), + branch_sort: GituiKeyEvent::new(KeyCode::Char('S'), KeyModifiers::SHIFT), diff_hunk_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::empty()), diff_hunk_prev: GituiKeyEvent::new(KeyCode::Char('p'), KeyModifiers::empty()), stage_unstage_item: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), From 75fb64845510e588546d366b6966ae2333d4fd24 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:50:13 +0900 Subject: [PATCH 23/27] fix branch_sort popup style --- src/popups/branch_sort.rs | 9 +++------ src/strings.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index dd12d42406..ee5fa3f6d7 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -1,10 +1,7 @@ use anyhow::Result; use crossterm::event::Event; use ratatui::{ - layout::{Alignment, Margin, Rect}, - text::{Line, Span}, - widgets::{Block, Borders, Clear, Paragraph}, - Frame, + layout::{Alignment, Margin, Rect}, style::Stylize, text::{Line, Span}, widgets::{Block, Borders, Clear, Paragraph}, Frame }; use strum::{EnumCount, IntoEnumIterator}; @@ -89,7 +86,7 @@ impl BranchSortPopup { .map(|t| { Line::from(vec![Span::styled( t.clone(), - self.theme.text(true, t.starts_with("[X]")), + self.theme.text(t.starts_with("[X]"), false), )]) }) .collect() @@ -130,7 +127,7 @@ impl DrawableComponent for BranchSortPopup { .borders(Borders::NONE) .border_style(self.theme.block(true)), ) - .alignment(Alignment::Left), + .alignment(Alignment::Left).not_bold(), area, ); } diff --git a/src/strings.rs b/src/strings.rs index 1c767c51e6..6cdc52e525 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -366,37 +366,37 @@ pub fn rename_branch_popup_msg( pub fn sort_branch_by_name_msg(selected: bool) -> String { format!( - "[{}] branch name (a -> z)", + "[{}] branch name (a → z)", if selected { "X" } else { " " } ) } pub fn sort_branch_by_name_rev_msg(selected: bool) -> String { format!( - "[{}] branch name (z -> a)", + "[{}] branch name (z → a)", if selected { "X" } else { " " } ) } pub fn sort_branch_by_time_msg(selected: bool) -> String { format!( - "[{}] last commit time (new -> old)", + "[{}] last commit time (new → old)", if selected { "X" } else { " " } ) } pub fn sort_branch_by_time_rev_msg(selected: bool) -> String { format!( - "[{}] last commit time (old -> new)", + "[{}] last commit time (old → new)", if selected { "X" } else { " " } ) } pub fn sort_branch_by_author_msg(selected: bool) -> String { format!( - "[{}] last commit author (a -> z)", + "[{}] last commit author (a → z)", if selected { "X" } else { " " } ) } pub fn sort_branch_by_author_rev_msg(selected: bool) -> String { format!( - "[{}] last commit author (z -> a)", + "[{}] last commit author (z → a)", if selected { "X" } else { " " } ) } From 38bfc844fcbacaf85860bddf20ebf93271865c1b Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:51:10 +0900 Subject: [PATCH 24/27] keep selected index in sort --- src/app.rs | 3 +- src/popups/branchlist.rs | 142 ++++++++++++++++++++++----------------- 2 files changed, 82 insertions(+), 63 deletions(-) diff --git a/src/app.rs b/src/app.rs index 486e587a4c..5ba5c60e4e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -814,7 +814,8 @@ impl App { .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); } InternalEvent::BranchListSort(sort_by) => { - self.select_branch_popup.sort(sort_by)?; + self.select_branch_popup.change_sort_by(sort_by); + self.select_branch_popup.sort()?; flags .insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS); } diff --git a/src/popups/branchlist.rs b/src/popups/branchlist.rs index ca40361bc9..67e6b79c08 100644 --- a/src/popups/branchlist.rs +++ b/src/popups/branchlist.rs @@ -401,6 +401,7 @@ impl BranchListPopup { self.local = !self.local; self.check_remotes(); self.update_branches()?; + self.set_selection(0)?; } Ok(EventState::NotConsumed) } @@ -427,9 +428,85 @@ impl BranchListPopup { } } - pub fn sort(&mut self, sort_by: BranchListSortBy) -> Result<()> { + pub fn change_sort_by(&mut self, sort_by: BranchListSortBy) { self.sort_by = sort_by; - self.update_branches()?; + } + + pub fn sort(&mut self) -> Result<()> { + let pre_selected = self + .branches + .get(self.selection as usize) + .map(|b| b.name.clone()); + match &self.sort_by { + BranchListSortBy::LastCommitAuthorAsc => { + self.branches.sort_by(|a, b| { + match b + .top_commit_author + .cmp(&a.top_commit_author) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitAuthorDesc => { + self.branches.sort_by(|a, b| { + match a + .top_commit_author + .cmp(&b.top_commit_author) + { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitTimeAsc => { + self.branches.sort_by(|a, b| { + match a.top_commit_time.cmp(&b.top_commit_time) { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::LastCommitTimeDesc => { + self.branches.sort_by(|a, b| { + match b.top_commit_time.cmp(&a.top_commit_time) { + std::cmp::Ordering::Equal => { + a.name.cmp(&b.name) + } + other => other, + } + }); + } + BranchListSortBy::BranchNameAsc => { + self.branches.sort_by(|a, b| a.name.cmp(&b.name)); + } + BranchListSortBy::BranchNameDesc => { + self.branches.sort_by(|a, b| b.name.cmp(&a.name)); + } + } + + match pre_selected { + Some(pre_selected) => { + let next_selecttion = self + .branches + .iter() + .position(|b| b.name == pre_selected) + .unwrap_or(0); + self.set_selection( + next_selecttion.try_into().unwrap_or_default(), + )?; + } + None => { + self.set_selection(0)?; + } + } Ok(()) } @@ -440,66 +517,7 @@ impl BranchListPopup { self.check_remotes(); self.branches = get_branches_info(&self.repo.borrow(), self.local)?; - match &self.sort_by { - BranchListSortBy::LastCommitAuthorAsc => { - self.branches.sort_by(|a, b| { - match b - .top_commit_author - .cmp(&a.top_commit_author) - { - std::cmp::Ordering::Equal => { - a.name.cmp(&b.name) - } - other => other, - } - }); - } - BranchListSortBy::LastCommitAuthorDesc => { - self.branches.sort_by(|a, b| { - match a - .top_commit_author - .cmp(&b.top_commit_author) - { - std::cmp::Ordering::Equal => { - a.name.cmp(&b.name) - } - other => other, - } - }); - } - BranchListSortBy::LastCommitTimeAsc => { - self.branches.sort_by(|a, b| { - match a - .top_commit_time - .cmp(&b.top_commit_time) - { - std::cmp::Ordering::Equal => { - a.name.cmp(&b.name) - } - other => other, - } - }); - } - BranchListSortBy::LastCommitTimeDesc => { - self.branches.sort_by(|a, b| { - match b - .top_commit_time - .cmp(&a.top_commit_time) - { - std::cmp::Ordering::Equal => { - a.name.cmp(&b.name) - } - other => other, - } - }); - } - BranchListSortBy::BranchNameAsc => { - self.branches.sort_by(|a, b| a.name.cmp(&b.name)); - } - BranchListSortBy::BranchNameDesc => { - self.branches.sort_by(|a, b| b.name.cmp(&a.name)); - } - } + self.sort()?; //remove remote branch called `HEAD` if !self.local { self.branches From cad4c63a8a1c54f5b84024ccebc90ce50430d0af Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:53:44 +0900 Subject: [PATCH 25/27] cargo fmt --- asyncgit/src/sync/branch/mod.rs | 2 +- src/popups/branch_sort.rs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/asyncgit/src/sync/branch/mod.rs b/asyncgit/src/sync/branch/mod.rs index 22feaa6af4..eade780bcb 100644 --- a/asyncgit/src/sync/branch/mod.rs +++ b/asyncgit/src/sync/branch/mod.rs @@ -11,7 +11,7 @@ use crate::{ sync::{ remotes::get_default_remote_for_push_in_repo, repository::repo, utils::get_head_repo, CommitId, - CommitSignature, + CommitSignature, }, }; use chrono::{DateTime, Local, TimeZone}; diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index ee5fa3f6d7..ec424cb7cd 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -1,7 +1,11 @@ use anyhow::Result; use crossterm::event::Event; use ratatui::{ - layout::{Alignment, Margin, Rect}, style::Stylize, text::{Line, Span}, widgets::{Block, Borders, Clear, Paragraph}, Frame + layout::{Alignment, Margin, Rect}, + style::Stylize, + text::{Line, Span}, + widgets::{Block, Borders, Clear, Paragraph}, + Frame, }; use strum::{EnumCount, IntoEnumIterator}; @@ -127,7 +131,8 @@ impl DrawableComponent for BranchSortPopup { .borders(Borders::NONE) .border_style(self.theme.block(true)), ) - .alignment(Alignment::Left).not_bold(), + .alignment(Alignment::Left) + .not_bold(), area, ); } From 630828efaefbe4207813fe5b1319181420042b88 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Wed, 24 Apr 2024 22:10:54 +0900 Subject: [PATCH 26/27] bold style --- src/popups/branch_sort.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index ec424cb7cd..d1b87ac0a6 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -2,7 +2,6 @@ use anyhow::Result; use crossterm::event::Event; use ratatui::{ layout::{Alignment, Margin, Rect}, - style::Stylize, text::{Line, Span}, widgets::{Block, Borders, Clear, Paragraph}, Frame, @@ -131,8 +130,7 @@ impl DrawableComponent for BranchSortPopup { .borders(Borders::NONE) .border_style(self.theme.block(true)), ) - .alignment(Alignment::Left) - .not_bold(), + .alignment(Alignment::Left), area, ); } From 71f626e7fd7534b8432d34af3fecd8eb03c817f2 Mon Sep 17 00:00:00 2001 From: UG <55311933+UUGTech@users.noreply.github.com> Date: Wed, 1 May 2024 12:21:20 +0900 Subject: [PATCH 27/27] update popup selection style --- src/popups/branch_sort.rs | 4 +++- src/popups/fuzzy_find.rs | 3 ++- src/popups/log_search.rs | 18 ++++++++++++------ src/ui/style.rs | 25 ++++++++++++++++++++++++- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/popups/branch_sort.rs b/src/popups/branch_sort.rs index d1b87ac0a6..2550a829fa 100644 --- a/src/popups/branch_sort.rs +++ b/src/popups/branch_sort.rs @@ -87,9 +87,11 @@ impl BranchSortPopup { texts .iter() .map(|t| { + let selected = t.starts_with("[X]"); Line::from(vec![Span::styled( t.clone(), - self.theme.text(t.starts_with("[X]"), false), + self.theme + .popup_selection(selected, selected, false), )]) }) .collect() diff --git a/src/popups/fuzzy_find.rs b/src/popups/fuzzy_find.rs index 507b64e18f..9cd2ce55f3 100644 --- a/src/popups/fuzzy_find.rs +++ b/src/popups/fuzzy_find.rs @@ -203,7 +203,8 @@ impl FuzzyFindPopup { .map(|(c_idx, c)| { Span::styled( Cow::from(c.to_string()), - self.theme.text( + self.theme.popup_selection( + selected, selected, indices.contains( &(c_idx + trim_length), diff --git a/src/popups/log_search.rs b/src/popups/log_search.rs index e0c57b2cbe..831a374202 100644 --- a/src/popups/log_search.rs +++ b/src/popups/log_search.rs @@ -212,21 +212,24 @@ impl LogSearchPopupPopup { vec![ Line::from(vec![Span::styled( format!("[{x_opt_fuzzy}] fuzzy search"), - self.theme.text( + self.theme.popup_selection( + x_opt_fuzzy == "X", matches!(self.selection, Selection::FuzzyOption), false, ), )]), Line::from(vec![Span::styled( format!("[{x_opt_casesensitive}] case sensitive"), - self.theme.text( + self.theme.popup_selection( + x_opt_casesensitive == "X", matches!(self.selection, Selection::CaseOption), false, ), )]), Line::from(vec![Span::styled( format!("[{x_summary}] summary",), - self.theme.text( + self.theme.popup_selection( + x_summary == "X", matches!( self.selection, Selection::SummarySearch @@ -236,7 +239,8 @@ impl LogSearchPopupPopup { )]), Line::from(vec![Span::styled( format!("[{x_body}] message body",), - self.theme.text( + self.theme.popup_selection( + x_body == "X", matches!( self.selection, Selection::MessageBodySearch @@ -246,7 +250,8 @@ impl LogSearchPopupPopup { )]), Line::from(vec![Span::styled( format!("[{x_files}] committed files",), - self.theme.text( + self.theme.popup_selection( + x_files == "X", matches!( self.selection, Selection::FilenameSearch @@ -256,7 +261,8 @@ impl LogSearchPopupPopup { )]), Line::from(vec![Span::styled( format!("[{x_authors}] authors",), - self.theme.text( + self.theme.popup_selection( + x_authors == "X", matches!( self.selection, Selection::AuthorsSearch diff --git a/src/ui/style.rs b/src/ui/style.rs index e687e45ece..20923a8f89 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -1,6 +1,6 @@ use anyhow::Result; use asyncgit::{DiffLineType, StatusItemType}; -use ratatui::style::{Color, Modifier, Style}; +use ratatui::style::{Color, Modifier, Style, Stylize}; use ron::ser::{to_string_pretty, PrettyConfig}; use serde::{Deserialize, Serialize}; use std::{fs::File, io::Write, path::PathBuf, rc::Rc}; @@ -106,6 +106,29 @@ impl Theme { } } + pub fn popup_selection( + &self, + selected: bool, + focused: bool, + colored_bg: bool, + ) -> Style { + let style = match (selected, focused) { + (false, false) => { + Style::default().fg(self.disabled_fg).not_bold() + } + (false, true) => Style::default().not_bold(), + (true, false) => { + Style::default().fg(self.disabled_fg).bold() + } + (true, true) => Style::default().bold(), + }; + if colored_bg { + style.bg(self.selection_bg) + } else { + style + } + } + pub fn item(&self, typ: StatusItemType, selected: bool) -> Style { let style = match typ { StatusItemType::New => {