From 5f29d2f8a4c44c0376b9fa8c56e01567efe2d77e Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sun, 9 Jun 2024 11:21:12 +0200 Subject: [PATCH 01/16] Add Go to line feature for the blame view Add new stackable popup to get a line number to go to from the user Add a new internal event to change the selection in the most recent blame view as a result of using the go to line popup Add new keybinding for the go to line functionality (Shift-L) Add boolean to BlameFilePopup to check whether the view is currently shadowed by the go to line popup and, if so, let the key events bubble up to the go to line popup Add new command definition for the go to line functionality in BlameFilePopup Add clamping of the selected row in set_open_selection --- src/app.rs | 47 +++++++++++++---- src/keys/key_list.rs | 2 + src/popups/blame_file.rs | 28 +++++++++- src/popups/goto_line.rs | 111 +++++++++++++++++++++++++++++++++++++++ src/popups/mod.rs | 2 + src/queue.rs | 4 ++ src/strings.rs | 12 +++++ 7 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 src/popups/goto_line.rs diff --git a/src/app.rs b/src/app.rs index a7f7996235..5ec7d2abab 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, BlameFileOpen, BlameFilePopup, BranchListPopup, + CommitPopup, CompareCommitsPopup, ConfirmPopup, + CreateBranchPopup, ExternalEditorPopup, FetchPopup, + FileRevlogPopup, FuzzyFindPopup, GotoLinePopup, HelpPopup, + InspectCommitPopup, LogSearchPopupPopup, MsgPopup, + OptionsPopup, PullPopup, PushPopup, PushTagsPopup, + RenameBranchPopup, ResetPopup, RevisionFilesPopup, + StashMsgPopup, SubmodulesListPopup, TagCommitPopup, + TagListPopup, }, queue::{ Action, AppTabs, InternalEvent, NeedsUpdate, Queue, @@ -106,6 +107,7 @@ pub struct App { popup_stack: PopupStack, options: SharedOptions, repo_path_text: String, + goto_line_popup: GotoLinePopup, // "Flags" requires_redraw: Cell, @@ -208,6 +210,7 @@ impl App { stashing_tab: Stashing::new(&env), stashlist_tab: StashList::new(&env), files_tab: FilesTab::new(&env), + goto_line_popup: GotoLinePopup::new(&env), tab: 0, queue: env.queue, theme: env.theme, @@ -495,7 +498,8 @@ impl App { status_tab, files_tab, stashing_tab, - stashlist_tab + stashlist_tab, + goto_line_popup ] ); @@ -526,7 +530,8 @@ impl App { fetch_popup, options_popup, confirm_popup, - msg_popup + msg_popup, + goto_line_popup ] ); @@ -670,6 +675,9 @@ impl App { StackablePopupOpen::CompareCommits(param) => { self.compare_commits_popup.open(param)?; } + StackablePopupOpen::GotoLine => { + self.goto_line_popup.open()?; + } } Ok(()) @@ -872,6 +880,25 @@ impl App { InternalEvent::CommitSearch(options) => { self.revlog.search(options); } + InternalEvent::GotoLine(line) => { + if let Some(popup) = self.popup_stack.pop() { + if let StackablePopupOpen::BlameFile(params) = + popup + { + self.popup_stack.push( + StackablePopupOpen::BlameFile( + BlameFileOpen { + selection: Some(line), + ..params + }, + ), + ) + } + flags.insert( + NeedsUpdate::ALL | NeedsUpdate::COMMANDS, + ); + } + } }; Ok(flags) diff --git a/src/keys/key_list.rs b/src/keys/key_list.rs index a542ef938a..436193ae01 100644 --- a/src/keys/key_list.rs +++ b/src/keys/key_list.rs @@ -123,6 +123,7 @@ pub struct KeysList { pub commit_history_next: GituiKeyEvent, pub commit: GituiKeyEvent, pub newline: GituiKeyEvent, + pub goto_line: GituiKeyEvent, } #[rustfmt::skip] @@ -215,6 +216,7 @@ impl Default for KeysList { commit_history_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL), commit: GituiKeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL), newline: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), + goto_line: GituiKeyEvent::new(KeyCode::Char('L'), KeyModifiers::SHIFT), } } } diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 34143481fc..fbf73c59fd 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -97,6 +97,7 @@ pub struct BlameFilePopup { app_sender: Sender, git_sender: Sender, repo: RepoPathRef, + goto_line_popup_is_open: bool, } impl DrawableComponent for BlameFilePopup { @@ -234,6 +235,16 @@ impl Component for BlameFilePopup { ) .order(1), ); + out.push( + CommandInfo::new( + strings::commands::open_line_number_popup( + &self.key_config, + ), + true, + has_result, + ) + .order(1), + ); } visibility_blocking(self) @@ -243,7 +254,7 @@ impl Component for BlameFilePopup { &mut self, event: &crossterm::event::Event, ) -> Result { - if self.is_visible() { + if self.is_visible() && !self.goto_line_popup_is_open { if let Event::Key(key) = event { if key_match(key, self.key_config.keys.exit_popup) { self.hide_stacked(false); @@ -307,6 +318,16 @@ impl Component for BlameFilePopup { ), )); } + } else if key_match( + key, + self.key_config.keys.goto_line, + ) { + self.goto_line_popup_is_open = true; + self.hide_stacked(true); + self.visible = true; + self.queue.push(InternalEvent::OpenPopup( + StackablePopupOpen::GotoLine, + )); } return Ok(EventState::Consumed); @@ -344,6 +365,7 @@ impl BlameFilePopup { git_sender: env.sender_git.clone(), blame: None, repo: env.repo.clone(), + goto_line_popup_is_open: false, } } @@ -378,6 +400,7 @@ impl BlameFilePopup { ))); self.table_state.get_mut().select(Some(0)); self.visible = true; + self.goto_line_popup_is_open = false; self.update()?; Ok(()) @@ -721,7 +744,8 @@ impl BlameFilePopup { self.open_request.as_ref().and_then(|req| req.selection) { let mut table_state = self.table_state.take(); - table_state.select(Some(selection)); + let max_line_number = self.get_max_line_number(); + table_state.select(Some(selection.min(max_line_number))); self.table_state.set(table_state); } } diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs new file mode 100644 index 0000000000..5f53b16138 --- /dev/null +++ b/src/popups/goto_line.rs @@ -0,0 +1,111 @@ +use crate::{ + app::Environment, + components::{ + visibility_blocking, CommandBlocking, CommandInfo, Component, + DrawableComponent, EventState, + }, + keys::{key_match, SharedKeyConfig}, + queue::{InternalEvent, Queue}, + ui::{self, style::SharedTheme}, +}; + +use ratatui::{ + layout::Rect, + widgets::{Block, Clear, Paragraph}, + Frame, +}; + +use anyhow::Result; + +use crossterm::event::{Event, KeyCode}; + +pub struct GotoLinePopup { + visible: bool, + line: String, + key_config: SharedKeyConfig, + queue: Queue, + theme: SharedTheme, +} + +impl GotoLinePopup { + pub fn new(env: &Environment) -> Self { + Self { + visible: false, + line: String::new(), + key_config: env.key_config.clone(), + queue: env.queue.clone(), + theme: env.theme.clone(), + } + } + + pub fn open(&mut self) -> Result<()> { + self.visible = true; + Ok(()) + } +} + +impl Component for GotoLinePopup { + /// + fn commands( + &self, + _out: &mut Vec, + _force_all: bool, + ) -> CommandBlocking { + visibility_blocking(self) + } + + fn is_visible(&self) -> bool { + self.visible + } + + /// + fn event(&mut self, event: &Event) -> Result { + if self.is_visible() { + if let Event::Key(key) = event { + if key_match(key, self.key_config.keys.exit_popup) { + self.visible = false; + self.line.clear(); + self.queue.push(InternalEvent::PopupStackPop) + } else if let KeyCode::Char(c) = key.code { + if c.is_digit(10) { + // I'd assume it's unusual for people to blame + // files with milions of lines + if self.line.len() < 6 { + self.line.push(c) + } + } + } else if let KeyCode::Backspace = key.code { + self.line.pop(); + } else if key_match(key, self.key_config.keys.enter) { + self.visible = false; + if self.line.len() > 0 { + self.queue.push(InternalEvent::GotoLine( + self.line.parse::().unwrap(), + )); + } + self.queue.push(InternalEvent::PopupStackPop); + self.line.clear(); + } + return Ok(EventState::Consumed); + } + } + + Ok(EventState::NotConsumed) + } +} + +impl DrawableComponent for GotoLinePopup { + fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { + if self.is_visible() { + let input = Paragraph::new(self.line.as_str()) + .style(self.theme.text(true, false)) + .block(Block::bordered().title("Go to Line")); + + let input_area = ui::centered_rect_absolute(15, 3, area); + f.render_widget(Clear, input_area); + f.render_widget(input, input_area); + } + + Ok(()) + } +} diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 2216461ad7..65b8c196d0 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -8,6 +8,7 @@ mod externaleditor; mod fetch; mod file_revlog; mod fuzzy_find; +mod goto_line; mod help; mod inspect_commit; mod log_search; @@ -34,6 +35,7 @@ pub use externaleditor::ExternalEditorPopup; pub use fetch::FetchPopup; pub use file_revlog::{FileRevOpen, FileRevlogPopup}; pub use fuzzy_find::FuzzyFindPopup; +pub use goto_line::GotoLinePopup; pub use help::HelpPopup; pub use inspect_commit::{InspectCommitOpen, InspectCommitPopup}; pub use log_search::LogSearchPopupPopup; diff --git a/src/queue.rs b/src/queue.rs index 9ee7830bdd..e0da43e11a 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -68,6 +68,8 @@ pub enum StackablePopupOpen { InspectCommit(InspectCommitOpen), /// CompareCommits(InspectCommitOpen), + /// + GotoLine, } pub enum AppTabs { @@ -146,6 +148,8 @@ pub enum InternalEvent { RewordCommit(CommitId), /// CommitSearch(LogFilterSearchOptions), + /// + GotoLine(usize), } /// single threaded simple queue for components to communicate with each other diff --git a/src/strings.rs b/src/strings.rs index 70ca9e3e8f..3d8b1f70d3 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -1300,6 +1300,18 @@ pub mod commands { CMD_GROUP_LOG, ) } + pub fn open_line_number_popup( + key_config: &SharedKeyConfig, + ) -> CommandText { + CommandText::new( + format!( + "Go to Line [{}]", + key_config.get_hint(key_config.keys.goto_line), + ), + "go to a given line number in the blame view", + CMD_GROUP_GENERAL, + ) + } pub fn log_tag_commit( key_config: &SharedKeyConfig, ) -> CommandText { From 9c526de1a96f62ce0cc79e8e0ddb8d760ef86902 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sun, 9 Jun 2024 11:45:12 +0200 Subject: [PATCH 02/16] Fix make check errors --- src/app.rs | 4 ++-- src/popups/goto_line.rs | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/app.rs b/src/app.rs index 5ec7d2abab..26812d634b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -676,7 +676,7 @@ impl App { self.compare_commits_popup.open(param)?; } StackablePopupOpen::GotoLine => { - self.goto_line_popup.open()?; + self.goto_line_popup.open(); } } @@ -892,7 +892,7 @@ impl App { ..params }, ), - ) + ); } flags.insert( NeedsUpdate::ALL | NeedsUpdate::COMMANDS, diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index 5f53b16138..c6b2e8a832 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -38,9 +38,8 @@ impl GotoLinePopup { } } - pub fn open(&mut self) -> Result<()> { + pub fn open(&mut self) { self.visible = true; - Ok(()) } } @@ -65,22 +64,22 @@ impl Component for GotoLinePopup { if key_match(key, self.key_config.keys.exit_popup) { self.visible = false; self.line.clear(); - self.queue.push(InternalEvent::PopupStackPop) + self.queue.push(InternalEvent::PopupStackPop); } else if let KeyCode::Char(c) = key.code { - if c.is_digit(10) { + if c.is_ascii_digit() { // I'd assume it's unusual for people to blame // files with milions of lines if self.line.len() < 6 { - self.line.push(c) + self.line.push(c); } } - } else if let KeyCode::Backspace = key.code { + } else if key.code == KeyCode::Backspace { self.line.pop(); } else if key_match(key, self.key_config.keys.enter) { self.visible = false; - if self.line.len() > 0 { + if !self.line.is_empty() { self.queue.push(InternalEvent::GotoLine( - self.line.parse::().unwrap(), + self.line.parse::().expect("This shouldn't happen since the input is constrained to ascii digits only"), )); } self.queue.push(InternalEvent::PopupStackPop); From 55da5d97ea62ed2aefa19f9d47146f209a230048 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sun, 9 Jun 2024 12:07:02 +0200 Subject: [PATCH 03/16] Move goto_line_popup up the components list for proper event handling Remove unnecessary goto_line_popup_is_open boolean --- src/app.rs | 4 ++-- src/popups/blame_file.rs | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/app.rs b/src/app.rs index 26812d634b..bd2de7b642 100644 --- a/src/app.rs +++ b/src/app.rs @@ -474,6 +474,7 @@ impl App { msg_popup, confirm_popup, commit_popup, + goto_line_popup, blame_file_popup, file_revlog_popup, stashmsg_popup, @@ -498,8 +499,7 @@ impl App { status_tab, files_tab, stashing_tab, - stashlist_tab, - goto_line_popup + stashlist_tab ] ); diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index fbf73c59fd..9f4987b6cc 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -97,7 +97,6 @@ pub struct BlameFilePopup { app_sender: Sender, git_sender: Sender, repo: RepoPathRef, - goto_line_popup_is_open: bool, } impl DrawableComponent for BlameFilePopup { @@ -254,7 +253,7 @@ impl Component for BlameFilePopup { &mut self, event: &crossterm::event::Event, ) -> Result { - if self.is_visible() && !self.goto_line_popup_is_open { + if self.is_visible() { if let Event::Key(key) = event { if key_match(key, self.key_config.keys.exit_popup) { self.hide_stacked(false); @@ -322,7 +321,6 @@ impl Component for BlameFilePopup { key, self.key_config.keys.goto_line, ) { - self.goto_line_popup_is_open = true; self.hide_stacked(true); self.visible = true; self.queue.push(InternalEvent::OpenPopup( @@ -365,7 +363,6 @@ impl BlameFilePopup { git_sender: env.sender_git.clone(), blame: None, repo: env.repo.clone(), - goto_line_popup_is_open: false, } } @@ -400,7 +397,6 @@ impl BlameFilePopup { ))); self.table_state.get_mut().select(Some(0)); self.visible = true; - self.goto_line_popup_is_open = false; self.update()?; Ok(()) From 6bdab9ddcb2b3d94929ffbba8700ec8c0e25604b Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sun, 30 Jun 2024 13:30:16 +0200 Subject: [PATCH 04/16] Preserve BlameProcess in BlamePopup when jumping lines Add Context enum to keep the blame status across line jumps Add a context field to the GotoLinePopup Add blame field to BlameFilePopup Use the blame field in BlameFilePopup.open if not None --- asyncgit/src/blame.rs | 6 ++++-- src/app.rs | 14 ++++++++++---- src/components/revision_files.rs | 1 + src/components/status_tree.rs | 1 + src/popups/blame_file.rs | 30 ++++++++++++++++++++---------- src/popups/file_revlog.rs | 1 + src/popups/goto_line.rs | 8 ++++++-- src/popups/mod.rs | 2 +- src/queue.rs | 15 +++++++++++---- src/ui/syntax_text.rs | 5 ++++- 10 files changed, 59 insertions(+), 24 deletions(-) diff --git a/asyncgit/src/blame.rs b/asyncgit/src/blame.rs index dcbb93aff0..0c99478512 100644 --- a/asyncgit/src/blame.rs +++ b/asyncgit/src/blame.rs @@ -14,7 +14,7 @@ use std::{ }; /// -#[derive(Hash, Clone, PartialEq, Eq)] +#[derive(Hash, Clone, PartialEq, Eq, Debug)] pub struct BlameParams { /// path to the file to blame pub file_path: String, @@ -22,15 +22,17 @@ pub struct BlameParams { pub commit_id: Option, } +#[derive(Debug)] struct Request(R, Option); -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] struct LastResult { params: P, result: R, } /// +#[derive(Debug, Clone)] pub struct AsyncBlame { current: Arc>>, last: Arc>>>, diff --git a/src/app.rs b/src/app.rs index bd2de7b642..70cc9698ef 100644 --- a/src/app.rs +++ b/src/app.rs @@ -21,7 +21,7 @@ use crate::{ TagListPopup, }, queue::{ - Action, AppTabs, InternalEvent, NeedsUpdate, Queue, + Action, AppTabs, Context, InternalEvent, NeedsUpdate, Queue, StackablePopupOpen, }, setup_popups, @@ -675,8 +675,8 @@ impl App { StackablePopupOpen::CompareCommits(param) => { self.compare_commits_popup.open(param)?; } - StackablePopupOpen::GotoLine => { - self.goto_line_popup.open(); + StackablePopupOpen::GotoLine(context) => { + self.goto_line_popup.open(Some(context)); } } @@ -880,7 +880,7 @@ impl App { InternalEvent::CommitSearch(options) => { self.revlog.search(options); } - InternalEvent::GotoLine(line) => { + InternalEvent::GotoLine(line, context) => { if let Some(popup) = self.popup_stack.pop() { if let StackablePopupOpen::BlameFile(params) = popup @@ -889,6 +889,12 @@ impl App { StackablePopupOpen::BlameFile( BlameFileOpen { selection: Some(line), + blame: context.and_then(|ctx| { + let Context::Blame( + blame_process, + ) = ctx; + blame_process + }), ..params }, ), diff --git a/src/components/revision_files.rs b/src/components/revision_files.rs index 4c61179e77..99f8bf51cf 100644 --- a/src/components/revision_files.rs +++ b/src/components/revision_files.rs @@ -193,6 +193,7 @@ impl RevisionFilesComponent { file_path: path, commit_id: self.revision.as_ref().map(|c| c.id), selection: None, + blame: None, }), )); diff --git a/src/components/status_tree.rs b/src/components/status_tree.rs index af0cde79ec..d61c8d66c0 100644 --- a/src/components/status_tree.rs +++ b/src/components/status_tree.rs @@ -456,6 +456,7 @@ impl Component for StatusTreeComponent { file_path: status_item.path, commit_id: self.revision, selection: None, + blame: None, }, ), )); diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 9f4987b6cc..fbf48889c9 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -7,7 +7,7 @@ use crate::{ }, keys::{key_match, SharedKeyConfig}, popups::{FileRevOpen, InspectCommitOpen}, - queue::{InternalEvent, Queue, StackablePopupOpen}, + queue::{Context, InternalEvent, Queue, StackablePopupOpen}, string_utils::tabs_to_spaces, strings, ui::{self, style::SharedTheme, AsyncSyntaxJob, SyntaxText}, @@ -35,7 +35,8 @@ static NO_AUTHOR: &str = ""; static MIN_AUTHOR_WIDTH: usize = 3; static MAX_AUTHOR_WIDTH: usize = 20; -struct SyntaxFileBlame { +#[derive(Debug, Clone)] +pub struct SyntaxFileBlame { pub file_blame: FileBlame, pub styled_text: Option, } @@ -54,7 +55,8 @@ impl SyntaxFileBlame { } } -enum BlameProcess { +#[derive(Clone, Debug)] +pub enum BlameProcess { GettingBlame(AsyncBlame), SyntaxHighlighting { unstyled_file_blame: SyntaxFileBlame, @@ -81,6 +83,7 @@ pub struct BlameFileOpen { pub file_path: String, pub commit_id: Option, pub selection: Option, + pub blame: Option, } pub struct BlameFilePopup { @@ -324,7 +327,9 @@ impl Component for BlameFilePopup { self.hide_stacked(true); self.visible = true; self.queue.push(InternalEvent::OpenPopup( - StackablePopupOpen::GotoLine, + StackablePopupOpen::GotoLine(Context::Blame( + self.blame.clone(), + )), )); } @@ -375,6 +380,7 @@ impl BlameFilePopup { file_path: request.file_path, commit_id: request.commit_id, selection: self.get_selection(), + blame: self.blame.clone(), }), )); } @@ -390,11 +396,15 @@ impl BlameFilePopup { file_path: open.file_path, commit_id: open.commit_id, }); - self.blame = - Some(BlameProcess::GettingBlame(AsyncBlame::new( - self.repo.borrow().clone(), - &self.git_sender, - ))); + self.blame = match open.blame { + None => { + Some(BlameProcess::GettingBlame(AsyncBlame::new( + self.repo.borrow().clone(), + &self.git_sender, + ))) + } + blame => blame, + }; self.table_state.get_mut().select(Some(0)); self.visible = true; self.update()?; @@ -457,7 +467,6 @@ impl BlameFilePopup { ), }, ); - self.set_open_selection(); self.highlight_blame_lines(); return Ok(()); @@ -468,6 +477,7 @@ impl BlameFilePopup { } } } + self.set_open_selection(); Ok(()) } diff --git a/src/popups/file_revlog.rs b/src/popups/file_revlog.rs index 1bfc0b266e..c3963e1714 100644 --- a/src/popups/file_revlog.rs +++ b/src/popups/file_revlog.rs @@ -536,6 +536,7 @@ impl Component for FileRevlogPopup { file_path: open_request.file_path, commit_id: self.selected_commit(), selection: None, + blame: None, }, ), )); diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index c6b2e8a832..7e7daf8eb3 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -5,7 +5,7 @@ use crate::{ DrawableComponent, EventState, }, keys::{key_match, SharedKeyConfig}, - queue::{InternalEvent, Queue}, + queue::{Context, InternalEvent, Queue}, ui::{self, style::SharedTheme}, }; @@ -25,6 +25,7 @@ pub struct GotoLinePopup { key_config: SharedKeyConfig, queue: Queue, theme: SharedTheme, + context: Option, } impl GotoLinePopup { @@ -35,11 +36,13 @@ impl GotoLinePopup { key_config: env.key_config.clone(), queue: env.queue.clone(), theme: env.theme.clone(), + context: None, } } - pub fn open(&mut self) { + pub fn open(&mut self, context: Option) { self.visible = true; + self.context = context; } } @@ -80,6 +83,7 @@ impl Component for GotoLinePopup { if !self.line.is_empty() { self.queue.push(InternalEvent::GotoLine( self.line.parse::().expect("This shouldn't happen since the input is constrained to ascii digits only"), + self.context.clone() )); } self.queue.push(InternalEvent::PopupStackPop); diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 65b8c196d0..36de54c4f8 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -25,7 +25,7 @@ mod submodules; mod tag_commit; mod taglist; -pub use blame_file::{BlameFileOpen, BlameFilePopup}; +pub use blame_file::{BlameFileOpen, BlameFilePopup, BlameProcess}; pub use branchlist::BranchListPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; diff --git a/src/queue.rs b/src/queue.rs index e0da43e11a..8dbedc7efe 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,8 +1,8 @@ use crate::{ components::FuzzyFinderTarget, popups::{ - AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen, - InspectCommitOpen, + AppOption, BlameFileOpen, BlameProcess, FileRevOpen, + FileTreeOpen, InspectCommitOpen, }, tabs::StashingOptions, }; @@ -56,6 +56,13 @@ pub enum Action { UndoCommit, } +#[derive(Debug, Clone)] +pub enum Context { + Blame(Option), + //FileView, + //PossibleRange(u32, u32), +} + #[derive(Debug)] pub enum StackablePopupOpen { /// @@ -69,7 +76,7 @@ pub enum StackablePopupOpen { /// CompareCommits(InspectCommitOpen), /// - GotoLine, + GotoLine(Context), } pub enum AppTabs { @@ -149,7 +156,7 @@ pub enum InternalEvent { /// CommitSearch(LogFilterSearchOptions), /// - GotoLine(usize), + GotoLine(usize, Option), } /// single threaded simple queue for components to communicate with each other diff --git a/src/ui/syntax_text.rs b/src/ui/syntax_text.rs index fba036e714..1a160d5817 100644 --- a/src/ui/syntax_text.rs +++ b/src/ui/syntax_text.rs @@ -22,10 +22,12 @@ use syntect::{ use crate::{AsyncAppNotification, SyntaxHighlightProgress}; +#[derive(Debug, Clone)] struct SyntaxLine { items: Vec<(Style, usize, Range)>, } +#[derive(Debug, Clone)] pub struct SyntaxText { text: String, lines: Vec, @@ -211,12 +213,13 @@ fn syntact_style_to_tui(style: &Style) -> ratatui::style::Style { res } +#[derive(Debug)] enum JobState { Request((String, String)), Response(SyntaxText), } -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] pub struct AsyncSyntaxJob { state: Arc>>, } From 20ed1d213dc7d6f1769ad833344c10006d14f135 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sat, 6 Jul 2024 18:23:13 +0200 Subject: [PATCH 05/16] Load blame from the last blame file instead of context Remove context and references to it Addres https://github.com/extrawurst/gitui/pull/2262#discussion_r1660621933 Address https://github.com/extrawurst/gitui/pull/2262#discussion_r1660626111 --- src/app.rs | 17 +++++++---------- src/popups/blame_file.rs | 11 +++++------ src/popups/goto_line.rs | 10 +++------- src/popups/mod.rs | 2 +- src/queue.rs | 15 ++++----------- 5 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/app.rs b/src/app.rs index 70cc9698ef..fbd2277784 100644 --- a/src/app.rs +++ b/src/app.rs @@ -21,7 +21,7 @@ use crate::{ TagListPopup, }, queue::{ - Action, AppTabs, Context, InternalEvent, NeedsUpdate, Queue, + Action, AppTabs, InternalEvent, NeedsUpdate, Queue, StackablePopupOpen, }, setup_popups, @@ -675,8 +675,8 @@ impl App { StackablePopupOpen::CompareCommits(param) => { self.compare_commits_popup.open(param)?; } - StackablePopupOpen::GotoLine(context) => { - self.goto_line_popup.open(Some(context)); + StackablePopupOpen::GotoLine => { + self.goto_line_popup.open(); } } @@ -880,21 +880,18 @@ impl App { InternalEvent::CommitSearch(options) => { self.revlog.search(options); } - InternalEvent::GotoLine(line, context) => { + InternalEvent::GotoLine(line) => { if let Some(popup) = self.popup_stack.pop() { if let StackablePopupOpen::BlameFile(params) = popup { + let blame = + self.blame_file_popup.blame.clone(); self.popup_stack.push( StackablePopupOpen::BlameFile( BlameFileOpen { selection: Some(line), - blame: context.and_then(|ctx| { - let Context::Blame( - blame_process, - ) = ctx; - blame_process - }), + blame, ..params }, ), diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index fbf48889c9..4a15a523aa 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -7,7 +7,7 @@ use crate::{ }, keys::{key_match, SharedKeyConfig}, popups::{FileRevOpen, InspectCommitOpen}, - queue::{Context, InternalEvent, Queue, StackablePopupOpen}, + queue::{InternalEvent, Queue, StackablePopupOpen}, string_utils::tabs_to_spaces, strings, ui::{self, style::SharedTheme, AsyncSyntaxJob, SyntaxText}, @@ -96,7 +96,7 @@ pub struct BlameFilePopup { table_state: std::cell::Cell, key_config: SharedKeyConfig, current_height: std::cell::Cell, - blame: Option, + pub blame: Option, app_sender: Sender, git_sender: Sender, repo: RepoPathRef, @@ -327,9 +327,7 @@ impl Component for BlameFilePopup { self.hide_stacked(true); self.visible = true; self.queue.push(InternalEvent::OpenPopup( - StackablePopupOpen::GotoLine(Context::Blame( - self.blame.clone(), - )), + StackablePopupOpen::GotoLine, )); } @@ -751,7 +749,8 @@ impl BlameFilePopup { { let mut table_state = self.table_state.take(); let max_line_number = self.get_max_line_number(); - table_state.select(Some(selection.min(max_line_number))); + table_state + .select(Some(selection.clamp(0, max_line_number))); self.table_state.set(table_state); } } diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index 7e7daf8eb3..5313ac0140 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -5,7 +5,7 @@ use crate::{ DrawableComponent, EventState, }, keys::{key_match, SharedKeyConfig}, - queue::{Context, InternalEvent, Queue}, + queue::{InternalEvent, Queue}, ui::{self, style::SharedTheme}, }; @@ -25,7 +25,6 @@ pub struct GotoLinePopup { key_config: SharedKeyConfig, queue: Queue, theme: SharedTheme, - context: Option, } impl GotoLinePopup { @@ -36,13 +35,11 @@ impl GotoLinePopup { key_config: env.key_config.clone(), queue: env.queue.clone(), theme: env.theme.clone(), - context: None, } } - pub fn open(&mut self, context: Option) { + pub fn open(&mut self) { self.visible = true; - self.context = context; } } @@ -82,8 +79,7 @@ impl Component for GotoLinePopup { self.visible = false; if !self.line.is_empty() { self.queue.push(InternalEvent::GotoLine( - self.line.parse::().expect("This shouldn't happen since the input is constrained to ascii digits only"), - self.context.clone() + self.line.parse::()?, )); } self.queue.push(InternalEvent::PopupStackPop); diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 36de54c4f8..65b8c196d0 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -25,7 +25,7 @@ mod submodules; mod tag_commit; mod taglist; -pub use blame_file::{BlameFileOpen, BlameFilePopup, BlameProcess}; +pub use blame_file::{BlameFileOpen, BlameFilePopup}; pub use branchlist::BranchListPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; diff --git a/src/queue.rs b/src/queue.rs index 8dbedc7efe..e0da43e11a 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,8 +1,8 @@ use crate::{ components::FuzzyFinderTarget, popups::{ - AppOption, BlameFileOpen, BlameProcess, FileRevOpen, - FileTreeOpen, InspectCommitOpen, + AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen, + InspectCommitOpen, }, tabs::StashingOptions, }; @@ -56,13 +56,6 @@ pub enum Action { UndoCommit, } -#[derive(Debug, Clone)] -pub enum Context { - Blame(Option), - //FileView, - //PossibleRange(u32, u32), -} - #[derive(Debug)] pub enum StackablePopupOpen { /// @@ -76,7 +69,7 @@ pub enum StackablePopupOpen { /// CompareCommits(InspectCommitOpen), /// - GotoLine(Context), + GotoLine, } pub enum AppTabs { @@ -156,7 +149,7 @@ pub enum InternalEvent { /// CommitSearch(LogFilterSearchOptions), /// - GotoLine(usize, Option), + GotoLine(usize), } /// single threaded simple queue for components to communicate with each other From e1291f09728e277064b6981f5d55ba85ff9c7293 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Tue, 16 Jul 2024 22:39:24 +0200 Subject: [PATCH 06/16] Add a BlameRequest enum for re-using blame data in Goto line Modiy uses of BlameFileOpen accordingly Make the blame field of BlameFilePopup private again --- src/app.rs | 14 ++++++-------- src/components/revision_files.rs | 4 ++-- src/components/status_tree.rs | 4 ++-- src/popups/blame_file.rs | 22 +++++++++++++--------- src/popups/file_revlog.rs | 4 ++-- src/popups/mod.rs | 2 +- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/app.rs b/src/app.rs index fbd2277784..9ac58004a5 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,11 +10,11 @@ use crate::{ options::{Options, SharedOptions}, popup_stack::PopupStack, popups::{ - AppOption, BlameFileOpen, BlameFilePopup, BranchListPopup, - CommitPopup, CompareCommitsPopup, ConfirmPopup, - CreateBranchPopup, ExternalEditorPopup, FetchPopup, - FileRevlogPopup, FuzzyFindPopup, GotoLinePopup, HelpPopup, - InspectCommitPopup, LogSearchPopupPopup, MsgPopup, + AppOption, BlameFileOpen, BlameFilePopup, BlameRequest, + BranchListPopup, CommitPopup, CompareCommitsPopup, + ConfirmPopup, CreateBranchPopup, ExternalEditorPopup, + FetchPopup, FileRevlogPopup, FuzzyFindPopup, GotoLinePopup, + HelpPopup, InspectCommitPopup, LogSearchPopupPopup, MsgPopup, OptionsPopup, PullPopup, PushPopup, PushTagsPopup, RenameBranchPopup, ResetPopup, RevisionFilesPopup, StashMsgPopup, SubmodulesListPopup, TagCommitPopup, @@ -885,13 +885,11 @@ impl App { if let StackablePopupOpen::BlameFile(params) = popup { - let blame = - self.blame_file_popup.blame.clone(); self.popup_stack.push( StackablePopupOpen::BlameFile( BlameFileOpen { selection: Some(line), - blame, + blame: BlameRequest::KeepExisting, ..params }, ), diff --git a/src/components/revision_files.rs b/src/components/revision_files.rs index 99f8bf51cf..ea23eca0c3 100644 --- a/src/components/revision_files.rs +++ b/src/components/revision_files.rs @@ -6,7 +6,7 @@ use super::{ use crate::{ app::Environment, keys::{key_match, SharedKeyConfig}, - popups::{BlameFileOpen, FileRevOpen}, + popups::{BlameFileOpen, BlameRequest, FileRevOpen}, queue::{InternalEvent, Queue, StackablePopupOpen}, strings::{self, order, symbol}, try_or_popup, @@ -193,7 +193,7 @@ impl RevisionFilesComponent { file_path: path, commit_id: self.revision.as_ref().map(|c| c.id), selection: None, - blame: None, + blame: BlameRequest::StartNew, }), )); diff --git a/src/components/status_tree.rs b/src/components/status_tree.rs index d61c8d66c0..a6a34efe24 100644 --- a/src/components/status_tree.rs +++ b/src/components/status_tree.rs @@ -9,7 +9,7 @@ use crate::{ app::Environment, components::{CommandInfo, Component, EventState}, keys::{key_match, SharedKeyConfig}, - popups::{BlameFileOpen, FileRevOpen}, + popups::{BlameFileOpen, BlameRequest, FileRevOpen}, queue::{InternalEvent, NeedsUpdate, Queue, StackablePopupOpen}, strings::{self, order}, ui::{self, style::SharedTheme}, @@ -456,7 +456,7 @@ impl Component for StatusTreeComponent { file_path: status_item.path, commit_id: self.revision, selection: None, - blame: None, + blame: BlameRequest::StartNew, }, ), )); diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 4a15a523aa..3ebe3e5dbc 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -78,12 +78,18 @@ impl BlameProcess { } } +#[derive(Clone, Debug)] +pub enum BlameRequest { + StartNew, + KeepExisting, +} + #[derive(Clone, Debug)] pub struct BlameFileOpen { pub file_path: String, pub commit_id: Option, pub selection: Option, - pub blame: Option, + pub blame: BlameRequest, } pub struct BlameFilePopup { @@ -96,7 +102,7 @@ pub struct BlameFilePopup { table_state: std::cell::Cell, key_config: SharedKeyConfig, current_height: std::cell::Cell, - pub blame: Option, + blame: Option, app_sender: Sender, git_sender: Sender, repo: RepoPathRef, @@ -378,7 +384,7 @@ impl BlameFilePopup { file_path: request.file_path, commit_id: request.commit_id, selection: self.get_selection(), - blame: self.blame.clone(), + blame: BlameRequest::KeepExisting, }), )); } @@ -394,15 +400,13 @@ impl BlameFilePopup { file_path: open.file_path, commit_id: open.commit_id, }); - self.blame = match open.blame { - None => { + if matches!(open.blame, BlameRequest::StartNew) { + self.blame = Some(BlameProcess::GettingBlame(AsyncBlame::new( self.repo.borrow().clone(), &self.git_sender, - ))) - } - blame => blame, - }; + ))); + } self.table_state.get_mut().select(Some(0)); self.visible = true; self.update()?; diff --git a/src/popups/file_revlog.rs b/src/popups/file_revlog.rs index c3963e1714..011fed22e7 100644 --- a/src/popups/file_revlog.rs +++ b/src/popups/file_revlog.rs @@ -28,7 +28,7 @@ use ratatui::{ Frame, }; -use super::{BlameFileOpen, InspectCommitOpen}; +use super::{BlameFileOpen, BlameRequest, InspectCommitOpen}; const SLICE_SIZE: usize = 1200; @@ -536,7 +536,7 @@ impl Component for FileRevlogPopup { file_path: open_request.file_path, commit_id: self.selected_commit(), selection: None, - blame: None, + blame: BlameRequest::StartNew, }, ), )); diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 65b8c196d0..7b148f72d2 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -25,7 +25,7 @@ mod submodules; mod tag_commit; mod taglist; -pub use blame_file::{BlameFileOpen, BlameFilePopup}; +pub use blame_file::{BlameFileOpen, BlameFilePopup, BlameRequest}; pub use branchlist::BranchListPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; From 4855ca6b677e24f6c75dbcc0dca9f1ce262b8b51 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sat, 7 Sep 2024 16:57:04 +0200 Subject: [PATCH 07/16] Add input validation to Go to line popup Add a context struct for the go to line popup to keep the max line number allowed Add support for negative values for the go to line popup input (go to the -n-th to last line) Make the go to line input box red when invalid values are provided Add an error message to the Go to line popup when invalid values are used Allow arbitrarily large values in the Go to line input box --- src/app.rs | 4 +- src/popups/blame_file.rs | 8 +++- src/popups/goto_line.rs | 80 +++++++++++++++++++++++++++++++--------- src/popups/mod.rs | 2 +- src/queue.rs | 4 +- 5 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/app.rs b/src/app.rs index 9ac58004a5..aad0de7236 100644 --- a/src/app.rs +++ b/src/app.rs @@ -675,8 +675,8 @@ impl App { StackablePopupOpen::CompareCommits(param) => { self.compare_commits_popup.open(param)?; } - StackablePopupOpen::GotoLine => { - self.goto_line_popup.open(); + StackablePopupOpen::GotoLine(param) => { + self.goto_line_popup.open(param); } } diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 3ebe3e5dbc..433a62321e 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -30,6 +30,8 @@ use ratatui::{ }; use std::path::Path; +use super::{goto_line::GotoLineContext, GotoLineOpen}; + static NO_COMMIT_ID: &str = "0000000"; static NO_AUTHOR: &str = ""; static MIN_AUTHOR_WIDTH: usize = 3; @@ -333,7 +335,11 @@ impl Component for BlameFilePopup { self.hide_stacked(true); self.visible = true; self.queue.push(InternalEvent::OpenPopup( - StackablePopupOpen::GotoLine, + StackablePopupOpen::GotoLine(GotoLineOpen { + context: GotoLineContext { + max_line: self.get_max_line_number(), + }, + }), )); } diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index 5313ac0140..39d6625c87 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -11,6 +11,7 @@ use crate::{ use ratatui::{ layout::Rect, + style::{Color, Style}, widgets::{Block, Clear, Paragraph}, Frame, }; @@ -19,27 +20,44 @@ use anyhow::Result; use crossterm::event::{Event, KeyCode}; +#[derive(Debug)] +pub struct GotoLineContext { + pub max_line: usize, +} + +#[derive(Debug)] +pub struct GotoLineOpen { + pub context: GotoLineContext, +} + pub struct GotoLinePopup { visible: bool, - line: String, + input: String, + line_number: usize, key_config: SharedKeyConfig, queue: Queue, theme: SharedTheme, + invalid_input: bool, + context: GotoLineContext, } impl GotoLinePopup { pub fn new(env: &Environment) -> Self { Self { visible: false, - line: String::new(), + input: String::new(), key_config: env.key_config.clone(), queue: env.queue.clone(), theme: env.theme.clone(), + invalid_input: false, + context: GotoLineContext { max_line: 0 }, + line_number: 0, } } - pub fn open(&mut self) { + pub fn open(&mut self, open: GotoLineOpen) { self.visible = true; + self.context = open.context; } } @@ -63,32 +81,53 @@ impl Component for GotoLinePopup { if let Event::Key(key) = event { if key_match(key, self.key_config.keys.exit_popup) { self.visible = false; - self.line.clear(); + self.input.clear(); self.queue.push(InternalEvent::PopupStackPop); } else if let KeyCode::Char(c) = key.code { - if c.is_ascii_digit() { - // I'd assume it's unusual for people to blame - // files with milions of lines - if self.line.len() < 6 { - self.line.push(c); - } + if c.is_ascii_digit() || c == '-' { + self.input.push(c); } } else if key.code == KeyCode::Backspace { - self.line.pop(); + self.input.pop(); } else if key_match(key, self.key_config.keys.enter) { self.visible = false; - if !self.line.is_empty() { + if self.invalid_input { + self.queue.push(InternalEvent::ShowErrorMsg( + format!("Invalid input: only numbers between -{0} and {0} (included) are allowed",self.context.max_line)) + , + ); + } else if !self.input.is_empty() { self.queue.push(InternalEvent::GotoLine( - self.line.parse::()?, + self.line_number, )); } self.queue.push(InternalEvent::PopupStackPop); - self.line.clear(); + self.input.clear(); + self.invalid_input = false; + } + } + match self.input.parse::() { + Ok(input) => { + if input.unsigned_abs() > self.context.max_line { + self.invalid_input = true; + } else { + self.invalid_input = false; + self.line_number = if input > 0 { + input.unsigned_abs() + } else { + self.context.max_line + - input.unsigned_abs() + } + } + } + Err(_) => { + if !self.input.is_empty() { + self.invalid_input = true; + } } - return Ok(EventState::Consumed); } + return Ok(EventState::Consumed); } - Ok(EventState::NotConsumed) } } @@ -96,8 +135,13 @@ impl Component for GotoLinePopup { impl DrawableComponent for GotoLinePopup { fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> { if self.is_visible() { - let input = Paragraph::new(self.line.as_str()) - .style(self.theme.text(true, false)) + let style = if self.invalid_input { + Style::default().fg(Color::Red) + } else { + self.theme.text(true, false) + }; + let input = Paragraph::new(self.input.as_str()) + .style(style) .block(Block::bordered().title("Go to Line")); let input_area = ui::centered_rect_absolute(15, 3, area); diff --git a/src/popups/mod.rs b/src/popups/mod.rs index 7b148f72d2..340189baea 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -35,7 +35,7 @@ pub use externaleditor::ExternalEditorPopup; pub use fetch::FetchPopup; pub use file_revlog::{FileRevOpen, FileRevlogPopup}; pub use fuzzy_find::FuzzyFindPopup; -pub use goto_line::GotoLinePopup; +pub use goto_line::{GotoLineOpen, GotoLinePopup}; pub use help::HelpPopup; pub use inspect_commit::{InspectCommitOpen, InspectCommitPopup}; pub use log_search::LogSearchPopupPopup; diff --git a/src/queue.rs b/src/queue.rs index e0da43e11a..16847b5fbb 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -2,7 +2,7 @@ use crate::{ components::FuzzyFinderTarget, popups::{ AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen, - InspectCommitOpen, + GotoLineOpen, InspectCommitOpen, }, tabs::StashingOptions, }; @@ -69,7 +69,7 @@ pub enum StackablePopupOpen { /// CompareCommits(InspectCommitOpen), /// - GotoLine, + GotoLine(GotoLineOpen), } pub enum AppTabs { From 56732c20f17be5bb08b6d1b13a82a2cf7935dc83 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Wed, 18 Sep 2024 18:02:09 +0200 Subject: [PATCH 08/16] Fix up negative indexing and add changelog entry --- CHANGELOG.md | 4 ++++ src/popups/goto_line.rs | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2593b46b7c..b7781ec49d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes * respect env vars like `GIT_CONFIG_GLOBAL` ([#2298](https://github.com/extrawurst/gitui/issues/2298)) +### Added + +* Add "go to line" command for the blame view [[@andrea-berling](https://github.com/andrea-berling)] ([#2262](https://github.com/extrawurst/gitui/pull/2262)) + ## [0.26.3] - 2024-06-02 ### Breaking Changes diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index 39d6625c87..78f70fe05e 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -93,7 +93,7 @@ impl Component for GotoLinePopup { self.visible = false; if self.invalid_input { self.queue.push(InternalEvent::ShowErrorMsg( - format!("Invalid input: only numbers between -{0} and {0} (included) are allowed",self.context.max_line)) + format!("Invalid input: only numbers between -{} and {} (included) are allowed (-1 means denotes the last line, -2 denotes the second to last line, and so on)",self.context.max_line + 1, self.context.max_line)) , ); } else if !self.input.is_empty() { @@ -108,15 +108,21 @@ impl Component for GotoLinePopup { } match self.input.parse::() { Ok(input) => { - if input.unsigned_abs() > self.context.max_line { + let mut max_value_allowed_abs = + self.context.max_line; + // negative indices are 1 based + if input < 0 { + max_value_allowed_abs += 1; + } + let input_abs = input.unsigned_abs(); + if input_abs > max_value_allowed_abs { self.invalid_input = true; } else { self.invalid_input = false; - self.line_number = if input > 0 { - input.unsigned_abs() + self.line_number = if input >= 0 { + input_abs } else { - self.context.max_line - - input.unsigned_abs() + max_value_allowed_abs - input_abs } } } From bea05c22cbc05adaae7d349a02dd5a635ad45aa0 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Tue, 1 Oct 2024 20:35:08 +0200 Subject: [PATCH 09/16] Prevent GotoLine popup from opening before blame load and fix typo --- src/popups/blame_file.rs | 30 +++++++++++++++++++++--------- src/popups/goto_line.rs | 2 +- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 433a62321e..b3e04ec8a4 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -332,15 +332,27 @@ impl Component for BlameFilePopup { key, self.key_config.keys.goto_line, ) { - self.hide_stacked(true); - self.visible = true; - self.queue.push(InternalEvent::OpenPopup( - StackablePopupOpen::GotoLine(GotoLineOpen { - context: GotoLineContext { - max_line: self.get_max_line_number(), - }, - }), - )); + let maybe_blame_result = &self + .blame + .as_ref() + .and_then(|blame| blame.result()); + if maybe_blame_result.is_some() { + let max_line = maybe_blame_result + .expect("This can not be None") + .lines() + .len() - 1; + self.hide_stacked(true); + self.visible = true; + self.queue.push(InternalEvent::OpenPopup( + StackablePopupOpen::GotoLine( + GotoLineOpen { + context: GotoLineContext { + max_line, + }, + }, + ), + )); + } } return Ok(EventState::Consumed); diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index 78f70fe05e..f6b860ea62 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -93,7 +93,7 @@ impl Component for GotoLinePopup { self.visible = false; if self.invalid_input { self.queue.push(InternalEvent::ShowErrorMsg( - format!("Invalid input: only numbers between -{} and {} (included) are allowed (-1 means denotes the last line, -2 denotes the second to last line, and so on)",self.context.max_line + 1, self.context.max_line)) + format!("Invalid input: only numbers between -{} and {} (included) are allowed (-1 denotes the last line, -2 denotes the second to last line, and so on)",self.context.max_line + 1, self.context.max_line)) , ); } else if !self.input.is_empty() { From 583eb7cf8de1832791c4e33c1e6a3c61801174a3 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sat, 22 Mar 2025 19:12:15 +0100 Subject: [PATCH 10/16] Add hints for the Go To Line popup --- src/popups/goto_line.rs | 24 ++++++++++++++++++++++-- src/strings.rs | 11 +++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index f6b860ea62..f900b2aa14 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -6,6 +6,7 @@ use crate::{ }, keys::{key_match, SharedKeyConfig}, queue::{InternalEvent, Queue}, + strings, ui::{self, style::SharedTheme}, }; @@ -65,9 +66,28 @@ impl Component for GotoLinePopup { /// fn commands( &self, - _out: &mut Vec, - _force_all: bool, + 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, + ) + .order(1), + ); + out.push( + CommandInfo::new( + strings::commands::goto_line(&self.key_config), + true, + true, + ) + .order(1), + ); + } + visibility_blocking(self) } diff --git a/src/strings.rs b/src/strings.rs index 53787ce618..62a2c6e564 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -1882,4 +1882,15 @@ pub mod commands { CMD_GROUP_LOG, ) } + + pub fn goto_line(key_config: &SharedKeyConfig) -> CommandText { + CommandText::new( + format!( + "Go To Line [{}]", + key_config.get_hint(key_config.keys.enter), + ), + "Go to the given line", + CMD_GROUP_GENERAL, + ) + } } From fb2563db323c780e3d98a15328e68a94aef1adb2 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Thu, 30 Oct 2025 20:40:30 +0100 Subject: [PATCH 11/16] fix(changelog): clean up some merge mixups in the changelog --- CHANGELOG.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 969ab5a9ae..5e8d957591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,10 +48,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * use default shell instead of bash on Unix-like OS [[@yerke](https://github.com/yerke)] ([#2343](https://github.com/gitui-org/gitui/pull/2343)) ### Added +* add popups for viewing, adding, updating and removing remotes [[@robin-thoene](https://github.com/robin-thoene)] ([#2172](https://github.com/gitui-org/gitui/issues/2172)) +* support for `Copy Path` action in WSL [[@johnDeSilencio](https://github.com/johnDeSilencio)] ([#2413](https://github.com/gitui-org/gitui/pull/2413)) +* help popup scrollbar [[@wugeer](https://github.com/wugeer)] ([#2388](https://github.com/gitui-org/gitui/pull/2388)) -* support for "Copy Path" action in WSL [[@johnDeSilencio](https://github.com/johnDeSilencio)] ([#2413](https://github.com/extrawurst/gitui/pull/2413)) -* help popup scrollbar [[@wugeer](https://github.com/wugeer)](https://github.com/extrawurst/gitui/pull/2388)) -* add popups for viewing, adding, updating and removing remotes [[@robin-thoene](https://github.com/robin-thoene)] ([#2172](https://github.com/extrawurst/gitui/issues/2172)) +### Fixes +* respect env vars like `GIT_CONFIG_GLOBAL` ([#2298](https://github.com/gitui-org/gitui/issues/2298)) +* Set `CREATE_NO_WINDOW` flag when executing Git hooks on Windows ([#2371](https://github.com/gitui-org/gitui/pull/2371)) ## [0.26.3] - 2024-06-02 From ec4b1ef407918ff79c9c038ababafa87627b5e38 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Thu, 30 Oct 2025 20:41:10 +0100 Subject: [PATCH 12/16] refactor(go_to_line): rename "Go to line" into "Go to" --- src/popups/goto_line.rs | 2 +- src/strings.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index f900b2aa14..b5bd6b2643 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -168,7 +168,7 @@ impl DrawableComponent for GotoLinePopup { }; let input = Paragraph::new(self.input.as_str()) .style(style) - .block(Block::bordered().title("Go to Line")); + .block(Block::bordered().title("Go to")); let input_area = ui::centered_rect_absolute(15, 3, area); f.render_widget(Clear, input_area); diff --git a/src/strings.rs b/src/strings.rs index 8d517092ee..6792a0ceaa 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -1454,7 +1454,7 @@ pub mod commands { ) -> CommandText { CommandText::new( format!( - "Go to Line [{}]", + "Go to [{}]", key_config.get_hint(key_config.keys.goto_line), ), "go to a given line number in the blame view", @@ -1886,7 +1886,7 @@ pub mod commands { pub fn goto_line(key_config: &SharedKeyConfig) -> CommandText { CommandText::new( format!( - "Go To Line [{}]", + "Go To [{}]", key_config.get_hint(key_config.keys.enter), ), "Go to the given line", From e95c394ca4978690fe07fc7c8e2472f9b1640348 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Fri, 31 Oct 2025 09:44:41 +0100 Subject: [PATCH 13/16] refactor(popup): make goto line a non-stackable popup The "Go to Line" popup was previously managed as a stackable popup, which added unnecessary complexity. This commit changes it to be a non-stackable popup, simplifying the interaction and removing it from the main popup stack management. This makes the code easier to reason about and maintain. This also simplifies the BlameFilePopup API by removing the now-redundant BlameRequest enum. --- src/app.rs | 45 ++++++++++------------------ src/components/revision_files.rs | 3 +- src/components/status_tree.rs | 4 +-- src/popups/blame_file.rs | 51 +++++++++++++------------------- src/popups/file_revlog.rs | 3 +- src/popups/goto_line.rs | 11 ++----- src/popups/mod.rs | 4 +-- src/queue.rs | 6 ++-- 8 files changed, 45 insertions(+), 82 deletions(-) diff --git a/src/app.rs b/src/app.rs index 49d236d95c..f4274a500f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,16 +10,16 @@ use crate::{ options::{Options, SharedOptions}, popup_stack::PopupStack, popups::{ - AppOption, BlameFileOpen, BlameFilePopup, BlameRequest, - BranchListPopup, CommitPopup, CompareCommitsPopup, - ConfirmPopup, CreateBranchPopup, CreateRemotePopup, - ExternalEditorPopup, FetchPopup, FileRevlogPopup, - FuzzyFindPopup, GotoLinePopup, HelpPopup, InspectCommitPopup, - LogSearchPopupPopup, MsgPopup, OptionsPopup, PullPopup, - PushPopup, PushTagsPopup, RemoteListPopup, RenameBranchPopup, - RenameRemotePopup, ResetPopup, RevisionFilesPopup, - StashMsgPopup, SubmodulesListPopup, TagCommitPopup, - TagListPopup, UpdateRemoteUrlPopup, + AppOption, BlameFilePopup, BranchListPopup, CommitPopup, + CompareCommitsPopup, ConfirmPopup, CreateBranchPopup, + CreateRemotePopup, ExternalEditorPopup, FetchPopup, + FileRevlogPopup, FuzzyFindPopup, GotoLinePopup, HelpPopup, + InspectCommitPopup, LogSearchPopupPopup, MsgPopup, + OptionsPopup, PullPopup, PushPopup, PushTagsPopup, + RemoteListPopup, RenameBranchPopup, RenameRemotePopup, + ResetPopup, RevisionFilesPopup, StashMsgPopup, + SubmodulesListPopup, TagCommitPopup, TagListPopup, + UpdateRemoteUrlPopup, }, queue::{ Action, AppTabs, InternalEvent, NeedsUpdate, Queue, @@ -695,9 +695,6 @@ impl App { StackablePopupOpen::CompareCommits(param) => { self.compare_commits_popup.open(param)?; } - StackablePopupOpen::GotoLine(param) => { - self.goto_line_popup.open(param); - } } Ok(()) @@ -912,24 +909,12 @@ impl App { InternalEvent::CommitSearch(options) => { self.revlog.search(options); } + InternalEvent::OpenGotoLinePopup(context) => { + self.goto_line_popup.open(context); + } InternalEvent::GotoLine(line) => { - if let Some(popup) = self.popup_stack.pop() { - if let StackablePopupOpen::BlameFile(params) = - popup - { - self.popup_stack.push( - StackablePopupOpen::BlameFile( - BlameFileOpen { - selection: Some(line), - blame: BlameRequest::KeepExisting, - ..params - }, - ), - ); - } - flags.insert( - NeedsUpdate::ALL | NeedsUpdate::COMMANDS, - ); + if self.blame_file_popup.is_visible() { + self.blame_file_popup.goto_line(line); } } } diff --git a/src/components/revision_files.rs b/src/components/revision_files.rs index ad4a2b6b70..f3fec043d0 100644 --- a/src/components/revision_files.rs +++ b/src/components/revision_files.rs @@ -6,7 +6,7 @@ use super::{ use crate::{ app::Environment, keys::{key_match, SharedKeyConfig}, - popups::{BlameFileOpen, BlameRequest, FileRevOpen}, + popups::{BlameFileOpen, FileRevOpen}, queue::{InternalEvent, Queue, StackablePopupOpen}, strings::{self, order, symbol}, try_or_popup, @@ -193,7 +193,6 @@ impl RevisionFilesComponent { file_path: path, commit_id: self.revision.as_ref().map(|c| c.id), selection: None, - blame: BlameRequest::StartNew, }), )); diff --git a/src/components/status_tree.rs b/src/components/status_tree.rs index fd935b3740..591152c3b4 100644 --- a/src/components/status_tree.rs +++ b/src/components/status_tree.rs @@ -9,7 +9,7 @@ use crate::{ app::Environment, components::{CommandInfo, Component, EventState}, keys::{key_match, SharedKeyConfig}, - popups::{BlameFileOpen, BlameRequest, FileRevOpen}, + popups::{BlameFileOpen, FileRevOpen}, queue::{InternalEvent, NeedsUpdate, Queue, StackablePopupOpen}, strings::{self, order}, ui::{self, style::SharedTheme}, @@ -491,8 +491,6 @@ impl Component for StatusTreeComponent { .path, commit_id: self.revision, selection: None, - blame: - BlameRequest::StartNew, }, ), ), diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 8d96e156e3..9bd29f9a0a 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -30,7 +30,7 @@ use ratatui::{ }; use std::path::Path; -use super::{goto_line::GotoLineContext, GotoLineOpen}; +use super::goto_line::GotoLineContext; static NO_COMMIT_ID: &str = "0000000"; static NO_AUTHOR: &str = ""; @@ -80,18 +80,11 @@ impl BlameProcess { } } -#[derive(Clone, Debug)] -pub enum BlameRequest { - StartNew, - KeepExisting, -} - #[derive(Clone, Debug)] pub struct BlameFileOpen { pub file_path: String, pub commit_id: Option, pub selection: Option, - pub blame: BlameRequest, } pub struct BlameFilePopup { @@ -336,22 +329,13 @@ impl Component for BlameFilePopup { .blame .as_ref() .and_then(|blame| blame.result()); - if maybe_blame_result.is_some() { - let max_line = maybe_blame_result - .expect("This can not be None") - .lines() - .len() - 1; - self.hide_stacked(true); - self.visible = true; - self.queue.push(InternalEvent::OpenPopup( - StackablePopupOpen::GotoLine( - GotoLineOpen { - context: GotoLineContext { - max_line, - }, - }, + if let Some(blame_result) = maybe_blame_result { + let max_line = blame_result.lines().len() - 1; + self.queue.push( + InternalEvent::OpenGotoLinePopup( + GotoLineContext { max_line }, ), - )); + ); } } @@ -402,7 +386,6 @@ impl BlameFilePopup { file_path: request.file_path, commit_id: request.commit_id, selection: self.get_selection(), - blame: BlameRequest::KeepExisting, }), )); } @@ -418,13 +401,11 @@ impl BlameFilePopup { file_path: open.file_path, commit_id: open.commit_id, }); - if matches!(open.blame, BlameRequest::StartNew) { - self.blame = - Some(BlameProcess::GettingBlame(AsyncBlame::new( - self.repo.borrow().clone(), - &self.git_sender, - ))); - } + self.blame = + Some(BlameProcess::GettingBlame(AsyncBlame::new( + self.repo.borrow().clone(), + &self.git_sender, + ))); self.table_state.get_mut().select(Some(0)); self.visible = true; self.update()?; @@ -793,6 +774,14 @@ impl BlameFilePopup { }) } + pub fn goto_line(&mut self, line: usize) { + self.visible = true; + let mut table_state = self.table_state.take(); + table_state + .select(Some(line.clamp(0, self.get_max_line_number()))); + self.table_state.set(table_state); + } + fn selected_commit(&self) -> Option { self.blame .as_ref() diff --git a/src/popups/file_revlog.rs b/src/popups/file_revlog.rs index c5c31c7fa7..771ae857fe 100644 --- a/src/popups/file_revlog.rs +++ b/src/popups/file_revlog.rs @@ -28,7 +28,7 @@ use ratatui::{ Frame, }; -use super::{BlameFileOpen, BlameRequest, InspectCommitOpen}; +use super::{BlameFileOpen, InspectCommitOpen}; const SLICE_SIZE: usize = 1200; @@ -533,7 +533,6 @@ impl Component for FileRevlogPopup { file_path: open_request.file_path, commit_id: self.selected_commit(), selection: None, - blame: BlameRequest::StartNew, }, ), )); diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index b5bd6b2643..45da7c5ce2 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -26,11 +26,6 @@ pub struct GotoLineContext { pub max_line: usize, } -#[derive(Debug)] -pub struct GotoLineOpen { - pub context: GotoLineContext, -} - pub struct GotoLinePopup { visible: bool, input: String, @@ -56,9 +51,9 @@ impl GotoLinePopup { } } - pub fn open(&mut self, open: GotoLineOpen) { + pub fn open(&mut self, context: GotoLineContext) { self.visible = true; - self.context = open.context; + self.context = context; } } @@ -102,7 +97,6 @@ impl Component for GotoLinePopup { if key_match(key, self.key_config.keys.exit_popup) { self.visible = false; self.input.clear(); - self.queue.push(InternalEvent::PopupStackPop); } else if let KeyCode::Char(c) = key.code { if c.is_ascii_digit() || c == '-' { self.input.push(c); @@ -121,7 +115,6 @@ impl Component for GotoLinePopup { self.line_number, )); } - self.queue.push(InternalEvent::PopupStackPop); self.input.clear(); self.invalid_input = false; } diff --git a/src/popups/mod.rs b/src/popups/mod.rs index fe746d9983..d17914f8f4 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -29,7 +29,7 @@ mod tag_commit; mod taglist; mod update_remote_url; -pub use blame_file::{BlameFileOpen, BlameFilePopup, BlameRequest}; +pub use blame_file::{BlameFileOpen, BlameFilePopup}; pub use branchlist::BranchListPopup; pub use commit::CommitPopup; pub use compare_commits::CompareCommitsPopup; @@ -40,7 +40,7 @@ pub use externaleditor::ExternalEditorPopup; pub use fetch::FetchPopup; pub use file_revlog::{FileRevOpen, FileRevlogPopup}; pub use fuzzy_find::FuzzyFindPopup; -pub use goto_line::{GotoLineOpen, GotoLinePopup}; +pub use goto_line::{GotoLineContext, GotoLinePopup}; pub use help::HelpPopup; pub use inspect_commit::{InspectCommitOpen, InspectCommitPopup}; pub use log_search::LogSearchPopupPopup; diff --git a/src/queue.rs b/src/queue.rs index ba70b4853a..ad327d1504 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -2,7 +2,7 @@ use crate::{ components::FuzzyFinderTarget, popups::{ AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen, - GotoLineOpen, InspectCommitOpen, + GotoLineContext, InspectCommitOpen, }, tabs::StashingOptions, }; @@ -71,8 +71,6 @@ pub enum StackablePopupOpen { InspectCommit(InspectCommitOpen), /// CompareCommits(InspectCommitOpen), - /// - GotoLine(GotoLineOpen), } pub enum AppTabs { @@ -160,6 +158,8 @@ pub enum InternalEvent { /// CommitSearch(LogFilterSearchOptions), /// + OpenGotoLinePopup(GotoLineContext), + /// GotoLine(usize), } From f26b25e0518771ac2a69ffc6bb78f5d657ae20e3 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Fri, 31 Oct 2025 09:52:21 +0100 Subject: [PATCH 14/16] refactor: remove unused Debug derives This commit removes a number of unused `#[derive(Debug)]` implementations across the codebase to reduce code size and improve compile times. --- asyncgit/src/blame.rs | 6 ++---- src/popups/blame_file.rs | 8 ++------ src/ui/syntax_text.rs | 5 +---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/asyncgit/src/blame.rs b/asyncgit/src/blame.rs index decf866865..6bf4afe358 100644 --- a/asyncgit/src/blame.rs +++ b/asyncgit/src/blame.rs @@ -14,7 +14,7 @@ use std::{ }; /// -#[derive(Hash, Clone, PartialEq, Eq, Debug)] +#[derive(Hash, Clone, PartialEq, Eq)] pub struct BlameParams { /// path to the file to blame pub file_path: String, @@ -22,17 +22,15 @@ pub struct BlameParams { pub commit_id: Option, } -#[derive(Debug)] struct Request(R, Option); -#[derive(Debug, Default, Clone)] +#[derive(Default, Clone)] struct LastResult { params: P, result: R, } /// -#[derive(Debug, Clone)] pub struct AsyncBlame { current: Arc>>, last: Arc>>>, diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 9bd29f9a0a..510dd4b7e3 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -37,7 +37,6 @@ static NO_AUTHOR: &str = ""; static MIN_AUTHOR_WIDTH: usize = 3; static MAX_AUTHOR_WIDTH: usize = 20; -#[derive(Debug, Clone)] pub struct SyntaxFileBlame { pub file_blame: FileBlame, pub styled_text: Option, @@ -57,7 +56,6 @@ impl SyntaxFileBlame { } } -#[derive(Clone, Debug)] pub enum BlameProcess { GettingBlame(AsyncBlame), SyntaxHighlighting { @@ -468,6 +466,7 @@ impl BlameFilePopup { ), }, ); + self.set_open_selection(); self.highlight_blame_lines(); return Ok(()); @@ -478,7 +477,6 @@ impl BlameFilePopup { } } } - self.set_open_selection(); Ok(()) } @@ -752,9 +750,7 @@ impl BlameFilePopup { self.open_request.as_ref().and_then(|req| req.selection) { let mut table_state = self.table_state.take(); - let max_line_number = self.get_max_line_number(); - table_state - .select(Some(selection.clamp(0, max_line_number))); + table_state.select(Some(selection)); self.table_state.set(table_state); } } diff --git a/src/ui/syntax_text.rs b/src/ui/syntax_text.rs index 8e79279089..5be2192ea8 100644 --- a/src/ui/syntax_text.rs +++ b/src/ui/syntax_text.rs @@ -23,12 +23,10 @@ use crate::{AsyncAppNotification, SyntaxHighlightProgress}; pub const DEFAULT_SYNTAX_THEME: &str = "base16-eighties.dark"; -#[derive(Debug, Clone)] struct SyntaxLine { items: Vec<(Style, usize, Range)>, } -#[derive(Debug, Clone)] pub struct SyntaxText { text: String, lines: Vec, @@ -224,13 +222,12 @@ fn syntact_style_to_tui(style: &Style) -> ratatui::style::Style { res } -#[derive(Debug)] enum JobState { Request((String, String)), Response(SyntaxText), } -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default)] pub struct AsyncSyntaxJob { state: Arc>>, syntax: String, From aa808430ed7954454835291bb2f5dd7c8a44875e Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Fri, 31 Oct 2025 09:54:29 +0100 Subject: [PATCH 15/16] refactor: remove unnecessary pub specifiers --- src/popups/blame_file.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 510dd4b7e3..9254a9df2c 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -37,7 +37,7 @@ static NO_AUTHOR: &str = ""; static MIN_AUTHOR_WIDTH: usize = 3; static MAX_AUTHOR_WIDTH: usize = 20; -pub struct SyntaxFileBlame { +struct SyntaxFileBlame { pub file_blame: FileBlame, pub styled_text: Option, } @@ -56,7 +56,7 @@ impl SyntaxFileBlame { } } -pub enum BlameProcess { +enum BlameProcess { GettingBlame(AsyncBlame), SyntaxHighlighting { unstyled_file_blame: SyntaxFileBlame, From 81e65151a67ddb2bbe1bc309b0e4afbe1de681e0 Mon Sep 17 00:00:00 2001 From: Andrea Berlingieri Date: Sat, 1 Nov 2025 18:12:28 +0100 Subject: [PATCH 16/16] refactor: simplify `GotoLinePopup` The `GotoLineContext` struct was unnecessary, as it only contained a single `max_line` field. This commit refactors the `GotoLinePopup` to accept `max_line` directly, simplifying the code and removing the need for the `GotoLineContext` struct. --- src/app.rs | 4 ++-- src/popups/blame_file.rs | 4 +--- src/popups/goto_line.rs | 18 ++++++------------ src/popups/mod.rs | 2 +- src/queue.rs | 4 ++-- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/app.rs b/src/app.rs index f4274a500f..68a1b439e9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -909,8 +909,8 @@ impl App { InternalEvent::CommitSearch(options) => { self.revlog.search(options); } - InternalEvent::OpenGotoLinePopup(context) => { - self.goto_line_popup.open(context); + InternalEvent::OpenGotoLinePopup(max_line) => { + self.goto_line_popup.open(max_line); } InternalEvent::GotoLine(line) => { if self.blame_file_popup.is_visible() { diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 9254a9df2c..b7d1304b33 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -30,8 +30,6 @@ use ratatui::{ }; use std::path::Path; -use super::goto_line::GotoLineContext; - static NO_COMMIT_ID: &str = "0000000"; static NO_AUTHOR: &str = ""; static MIN_AUTHOR_WIDTH: usize = 3; @@ -331,7 +329,7 @@ impl Component for BlameFilePopup { let max_line = blame_result.lines().len() - 1; self.queue.push( InternalEvent::OpenGotoLinePopup( - GotoLineContext { max_line }, + max_line, ), ); } diff --git a/src/popups/goto_line.rs b/src/popups/goto_line.rs index 45da7c5ce2..492f60b779 100644 --- a/src/popups/goto_line.rs +++ b/src/popups/goto_line.rs @@ -21,11 +21,6 @@ use anyhow::Result; use crossterm::event::{Event, KeyCode}; -#[derive(Debug)] -pub struct GotoLineContext { - pub max_line: usize, -} - pub struct GotoLinePopup { visible: bool, input: String, @@ -34,7 +29,7 @@ pub struct GotoLinePopup { queue: Queue, theme: SharedTheme, invalid_input: bool, - context: GotoLineContext, + max_line: usize, } impl GotoLinePopup { @@ -46,14 +41,14 @@ impl GotoLinePopup { queue: env.queue.clone(), theme: env.theme.clone(), invalid_input: false, - context: GotoLineContext { max_line: 0 }, + max_line: 0, line_number: 0, } } - pub fn open(&mut self, context: GotoLineContext) { + pub fn open(&mut self, max_line: usize) { self.visible = true; - self.context = context; + self.max_line = max_line; } } @@ -107,7 +102,7 @@ impl Component for GotoLinePopup { self.visible = false; if self.invalid_input { self.queue.push(InternalEvent::ShowErrorMsg( - format!("Invalid input: only numbers between -{} and {} (included) are allowed (-1 denotes the last line, -2 denotes the second to last line, and so on)",self.context.max_line + 1, self.context.max_line)) + format!("Invalid input: only numbers between -{} and {} (included) are allowed (-1 denotes the last line, -2 denotes the second to last line, and so on)",self.max_line + 1, self.max_line)) , ); } else if !self.input.is_empty() { @@ -121,8 +116,7 @@ impl Component for GotoLinePopup { } match self.input.parse::() { Ok(input) => { - let mut max_value_allowed_abs = - self.context.max_line; + let mut max_value_allowed_abs = self.max_line; // negative indices are 1 based if input < 0 { max_value_allowed_abs += 1; diff --git a/src/popups/mod.rs b/src/popups/mod.rs index d17914f8f4..86d48ddef9 100644 --- a/src/popups/mod.rs +++ b/src/popups/mod.rs @@ -40,7 +40,7 @@ pub use externaleditor::ExternalEditorPopup; pub use fetch::FetchPopup; pub use file_revlog::{FileRevOpen, FileRevlogPopup}; pub use fuzzy_find::FuzzyFindPopup; -pub use goto_line::{GotoLineContext, GotoLinePopup}; +pub use goto_line::GotoLinePopup; pub use help::HelpPopup; pub use inspect_commit::{InspectCommitOpen, InspectCommitPopup}; pub use log_search::LogSearchPopupPopup; diff --git a/src/queue.rs b/src/queue.rs index ad327d1504..339610032f 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -2,7 +2,7 @@ use crate::{ components::FuzzyFinderTarget, popups::{ AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen, - GotoLineContext, InspectCommitOpen, + InspectCommitOpen, }, tabs::StashingOptions, }; @@ -158,7 +158,7 @@ pub enum InternalEvent { /// CommitSearch(LogFilterSearchOptions), /// - OpenGotoLinePopup(GotoLineContext), + OpenGotoLinePopup(usize), /// GotoLine(usize), }