Skip to content

Commit 5f29d2f

Browse files
andrea-berlingextrawurst
authored andcommitted
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
1 parent f27b581 commit 5f29d2f

File tree

7 files changed

+194
-12
lines changed

7 files changed

+194
-12
lines changed

src/app.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ use crate::{
1010
options::{Options, SharedOptions},
1111
popup_stack::PopupStack,
1212
popups::{
13-
AppOption, BlameFilePopup, BranchListPopup, CommitPopup,
14-
CompareCommitsPopup, ConfirmPopup, CreateBranchPopup,
15-
ExternalEditorPopup, FetchPopup, FileRevlogPopup,
16-
FuzzyFindPopup, HelpPopup, InspectCommitPopup,
17-
LogSearchPopupPopup, MsgPopup, OptionsPopup, PullPopup,
18-
PushPopup, PushTagsPopup, RenameBranchPopup, ResetPopup,
19-
RevisionFilesPopup, StashMsgPopup, SubmodulesListPopup,
20-
TagCommitPopup, TagListPopup,
13+
AppOption, BlameFileOpen, BlameFilePopup, BranchListPopup,
14+
CommitPopup, CompareCommitsPopup, ConfirmPopup,
15+
CreateBranchPopup, ExternalEditorPopup, FetchPopup,
16+
FileRevlogPopup, FuzzyFindPopup, GotoLinePopup, HelpPopup,
17+
InspectCommitPopup, LogSearchPopupPopup, MsgPopup,
18+
OptionsPopup, PullPopup, PushPopup, PushTagsPopup,
19+
RenameBranchPopup, ResetPopup, RevisionFilesPopup,
20+
StashMsgPopup, SubmodulesListPopup, TagCommitPopup,
21+
TagListPopup,
2122
},
2223
queue::{
2324
Action, AppTabs, InternalEvent, NeedsUpdate, Queue,
@@ -106,6 +107,7 @@ pub struct App {
106107
popup_stack: PopupStack,
107108
options: SharedOptions,
108109
repo_path_text: String,
110+
goto_line_popup: GotoLinePopup,
109111

110112
// "Flags"
111113
requires_redraw: Cell<bool>,
@@ -208,6 +210,7 @@ impl App {
208210
stashing_tab: Stashing::new(&env),
209211
stashlist_tab: StashList::new(&env),
210212
files_tab: FilesTab::new(&env),
213+
goto_line_popup: GotoLinePopup::new(&env),
211214
tab: 0,
212215
queue: env.queue,
213216
theme: env.theme,
@@ -495,7 +498,8 @@ impl App {
495498
status_tab,
496499
files_tab,
497500
stashing_tab,
498-
stashlist_tab
501+
stashlist_tab,
502+
goto_line_popup
499503
]
500504
);
501505

@@ -526,7 +530,8 @@ impl App {
526530
fetch_popup,
527531
options_popup,
528532
confirm_popup,
529-
msg_popup
533+
msg_popup,
534+
goto_line_popup
530535
]
531536
);
532537

@@ -670,6 +675,9 @@ impl App {
670675
StackablePopupOpen::CompareCommits(param) => {
671676
self.compare_commits_popup.open(param)?;
672677
}
678+
StackablePopupOpen::GotoLine => {
679+
self.goto_line_popup.open()?;
680+
}
673681
}
674682

675683
Ok(())
@@ -872,6 +880,25 @@ impl App {
872880
InternalEvent::CommitSearch(options) => {
873881
self.revlog.search(options);
874882
}
883+
InternalEvent::GotoLine(line) => {
884+
if let Some(popup) = self.popup_stack.pop() {
885+
if let StackablePopupOpen::BlameFile(params) =
886+
popup
887+
{
888+
self.popup_stack.push(
889+
StackablePopupOpen::BlameFile(
890+
BlameFileOpen {
891+
selection: Some(line),
892+
..params
893+
},
894+
),
895+
)
896+
}
897+
flags.insert(
898+
NeedsUpdate::ALL | NeedsUpdate::COMMANDS,
899+
);
900+
}
901+
}
875902
};
876903

877904
Ok(flags)

src/keys/key_list.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ pub struct KeysList {
123123
pub commit_history_next: GituiKeyEvent,
124124
pub commit: GituiKeyEvent,
125125
pub newline: GituiKeyEvent,
126+
pub goto_line: GituiKeyEvent,
126127
}
127128

128129
#[rustfmt::skip]
@@ -215,6 +216,7 @@ impl Default for KeysList {
215216
commit_history_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL),
216217
commit: GituiKeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL),
217218
newline: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
219+
goto_line: GituiKeyEvent::new(KeyCode::Char('L'), KeyModifiers::SHIFT),
218220
}
219221
}
220222
}

src/popups/blame_file.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub struct BlameFilePopup {
9797
app_sender: Sender<AsyncAppNotification>,
9898
git_sender: Sender<AsyncGitNotification>,
9999
repo: RepoPathRef,
100+
goto_line_popup_is_open: bool,
100101
}
101102

102103
impl DrawableComponent for BlameFilePopup {
@@ -234,6 +235,16 @@ impl Component for BlameFilePopup {
234235
)
235236
.order(1),
236237
);
238+
out.push(
239+
CommandInfo::new(
240+
strings::commands::open_line_number_popup(
241+
&self.key_config,
242+
),
243+
true,
244+
has_result,
245+
)
246+
.order(1),
247+
);
237248
}
238249

239250
visibility_blocking(self)
@@ -243,7 +254,7 @@ impl Component for BlameFilePopup {
243254
&mut self,
244255
event: &crossterm::event::Event,
245256
) -> Result<EventState> {
246-
if self.is_visible() {
257+
if self.is_visible() && !self.goto_line_popup_is_open {
247258
if let Event::Key(key) = event {
248259
if key_match(key, self.key_config.keys.exit_popup) {
249260
self.hide_stacked(false);
@@ -307,6 +318,16 @@ impl Component for BlameFilePopup {
307318
),
308319
));
309320
}
321+
} else if key_match(
322+
key,
323+
self.key_config.keys.goto_line,
324+
) {
325+
self.goto_line_popup_is_open = true;
326+
self.hide_stacked(true);
327+
self.visible = true;
328+
self.queue.push(InternalEvent::OpenPopup(
329+
StackablePopupOpen::GotoLine,
330+
));
310331
}
311332

312333
return Ok(EventState::Consumed);
@@ -344,6 +365,7 @@ impl BlameFilePopup {
344365
git_sender: env.sender_git.clone(),
345366
blame: None,
346367
repo: env.repo.clone(),
368+
goto_line_popup_is_open: false,
347369
}
348370
}
349371

@@ -378,6 +400,7 @@ impl BlameFilePopup {
378400
)));
379401
self.table_state.get_mut().select(Some(0));
380402
self.visible = true;
403+
self.goto_line_popup_is_open = false;
381404
self.update()?;
382405

383406
Ok(())
@@ -721,7 +744,8 @@ impl BlameFilePopup {
721744
self.open_request.as_ref().and_then(|req| req.selection)
722745
{
723746
let mut table_state = self.table_state.take();
724-
table_state.select(Some(selection));
747+
let max_line_number = self.get_max_line_number();
748+
table_state.select(Some(selection.min(max_line_number)));
725749
self.table_state.set(table_state);
726750
}
727751
}

src/popups/goto_line.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use crate::{
2+
app::Environment,
3+
components::{
4+
visibility_blocking, CommandBlocking, CommandInfo, Component,
5+
DrawableComponent, EventState,
6+
},
7+
keys::{key_match, SharedKeyConfig},
8+
queue::{InternalEvent, Queue},
9+
ui::{self, style::SharedTheme},
10+
};
11+
12+
use ratatui::{
13+
layout::Rect,
14+
widgets::{Block, Clear, Paragraph},
15+
Frame,
16+
};
17+
18+
use anyhow::Result;
19+
20+
use crossterm::event::{Event, KeyCode};
21+
22+
pub struct GotoLinePopup {
23+
visible: bool,
24+
line: String,
25+
key_config: SharedKeyConfig,
26+
queue: Queue,
27+
theme: SharedTheme,
28+
}
29+
30+
impl GotoLinePopup {
31+
pub fn new(env: &Environment) -> Self {
32+
Self {
33+
visible: false,
34+
line: String::new(),
35+
key_config: env.key_config.clone(),
36+
queue: env.queue.clone(),
37+
theme: env.theme.clone(),
38+
}
39+
}
40+
41+
pub fn open(&mut self) -> Result<()> {
42+
self.visible = true;
43+
Ok(())
44+
}
45+
}
46+
47+
impl Component for GotoLinePopup {
48+
///
49+
fn commands(
50+
&self,
51+
_out: &mut Vec<CommandInfo>,
52+
_force_all: bool,
53+
) -> CommandBlocking {
54+
visibility_blocking(self)
55+
}
56+
57+
fn is_visible(&self) -> bool {
58+
self.visible
59+
}
60+
61+
///
62+
fn event(&mut self, event: &Event) -> Result<EventState> {
63+
if self.is_visible() {
64+
if let Event::Key(key) = event {
65+
if key_match(key, self.key_config.keys.exit_popup) {
66+
self.visible = false;
67+
self.line.clear();
68+
self.queue.push(InternalEvent::PopupStackPop)
69+
} else if let KeyCode::Char(c) = key.code {
70+
if c.is_digit(10) {
71+
// I'd assume it's unusual for people to blame
72+
// files with milions of lines
73+
if self.line.len() < 6 {
74+
self.line.push(c)
75+
}
76+
}
77+
} else if let KeyCode::Backspace = key.code {
78+
self.line.pop();
79+
} else if key_match(key, self.key_config.keys.enter) {
80+
self.visible = false;
81+
if self.line.len() > 0 {
82+
self.queue.push(InternalEvent::GotoLine(
83+
self.line.parse::<usize>().unwrap(),
84+
));
85+
}
86+
self.queue.push(InternalEvent::PopupStackPop);
87+
self.line.clear();
88+
}
89+
return Ok(EventState::Consumed);
90+
}
91+
}
92+
93+
Ok(EventState::NotConsumed)
94+
}
95+
}
96+
97+
impl DrawableComponent for GotoLinePopup {
98+
fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
99+
if self.is_visible() {
100+
let input = Paragraph::new(self.line.as_str())
101+
.style(self.theme.text(true, false))
102+
.block(Block::bordered().title("Go to Line"));
103+
104+
let input_area = ui::centered_rect_absolute(15, 3, area);
105+
f.render_widget(Clear, input_area);
106+
f.render_widget(input, input_area);
107+
}
108+
109+
Ok(())
110+
}
111+
}

src/popups/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod externaleditor;
88
mod fetch;
99
mod file_revlog;
1010
mod fuzzy_find;
11+
mod goto_line;
1112
mod help;
1213
mod inspect_commit;
1314
mod log_search;
@@ -34,6 +35,7 @@ pub use externaleditor::ExternalEditorPopup;
3435
pub use fetch::FetchPopup;
3536
pub use file_revlog::{FileRevOpen, FileRevlogPopup};
3637
pub use fuzzy_find::FuzzyFindPopup;
38+
pub use goto_line::GotoLinePopup;
3739
pub use help::HelpPopup;
3840
pub use inspect_commit::{InspectCommitOpen, InspectCommitPopup};
3941
pub use log_search::LogSearchPopupPopup;

src/queue.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pub enum StackablePopupOpen {
6868
InspectCommit(InspectCommitOpen),
6969
///
7070
CompareCommits(InspectCommitOpen),
71+
///
72+
GotoLine,
7173
}
7274

7375
pub enum AppTabs {
@@ -146,6 +148,8 @@ pub enum InternalEvent {
146148
RewordCommit(CommitId),
147149
///
148150
CommitSearch(LogFilterSearchOptions),
151+
///
152+
GotoLine(usize),
149153
}
150154

151155
/// single threaded simple queue for components to communicate with each other

src/strings.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,18 @@ pub mod commands {
13001300
CMD_GROUP_LOG,
13011301
)
13021302
}
1303+
pub fn open_line_number_popup(
1304+
key_config: &SharedKeyConfig,
1305+
) -> CommandText {
1306+
CommandText::new(
1307+
format!(
1308+
"Go to Line [{}]",
1309+
key_config.get_hint(key_config.keys.goto_line),
1310+
),
1311+
"go to a given line number in the blame view",
1312+
CMD_GROUP_GENERAL,
1313+
)
1314+
}
13031315
pub fn log_tag_commit(
13041316
key_config: &SharedKeyConfig,
13051317
) -> CommandText {

0 commit comments

Comments
 (0)