Skip to content

Commit 8998aec

Browse files
author
Kaniel Kirby
committed
Support recursive blame (fixes gitui-org#2194)
- Replaces `BlameFilePopup.blame: Option<BlameProcess>` with `BlameFilePopup.blame_stack: Vec<BlameProcess>`. - Adds keybinding for going back from a blame (`b`). - Makes keybinding for `blame` (`Shift+B`) work in the `popups/blame_file.rs` view, where it will take the commit and open a blame from that.
1 parent 6ec7eea commit 8998aec

File tree

2 files changed

+76
-49
lines changed

2 files changed

+76
-49
lines changed

src/keys/key_list.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub struct KeysList {
6868
pub shift_down: GituiKeyEvent,
6969
pub enter: GituiKeyEvent,
7070
pub blame: GituiKeyEvent,
71+
pub blame_back: GituiKeyEvent,
7172
pub file_history: GituiKeyEvent,
7273
pub edit_file: GituiKeyEvent,
7374
pub status_stage_all: GituiKeyEvent,
@@ -160,6 +161,7 @@ impl Default for KeysList {
160161
shift_down: GituiKeyEvent::new(KeyCode::Down, KeyModifiers::SHIFT),
161162
enter: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
162163
blame: GituiKeyEvent::new(KeyCode::Char('B'), KeyModifiers::SHIFT),
164+
blame_back: GituiKeyEvent::new(KeyCode::Char('b'), KeyModifiers::empty()),
163165
file_history: GituiKeyEvent::new(KeyCode::Char('H'), KeyModifiers::SHIFT),
164166
edit_file: GituiKeyEvent::new(KeyCode::Char('e'), KeyModifiers::empty()),
165167
status_stage_all: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()),

src/popups/blame_file.rs

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ pub struct BlameFilePopup {
9393
table_state: std::cell::Cell<TableState>,
9494
key_config: SharedKeyConfig,
9595
current_height: std::cell::Cell<usize>,
96-
blame: Option<BlameProcess>,
96+
blame_stack: Vec<BlameProcess>,
9797
app_sender: Sender<AsyncAppNotification>,
9898
git_sender: Sender<AsyncGitNotification>,
9999
repo: RepoPathRef,
@@ -123,7 +123,7 @@ impl DrawableComponent for BlameFilePopup {
123123
];
124124

125125
let number_of_rows: usize = rows.len();
126-
let syntax_highlight_progress = match self.blame {
126+
let syntax_highlight_progress = match self.blame_stack.last() {
127127
Some(BlameProcess::SyntaxHighlighting {
128128
ref job,
129129
..
@@ -193,10 +193,7 @@ impl Component for BlameFilePopup {
193193
out: &mut Vec<CommandInfo>,
194194
force_all: bool,
195195
) -> CommandBlocking {
196-
let has_result = self
197-
.blame
198-
.as_ref()
199-
.is_some_and(|blame| blame.result().is_some());
196+
let has_result = self.blame_stack.last().is_some_and(|last| last.result().is_some());
200197
if self.is_visible() || force_all {
201198
out.push(
202199
CommandInfo::new(
@@ -307,8 +304,40 @@ impl Component for BlameFilePopup {
307304
),
308305
));
309306
}
307+
} else if key_match(
308+
key,
309+
self.key_config.keys.blame,
310+
) {
311+
if let Some(file_path) = self
312+
.params
313+
.as_ref()
314+
.map(|p| p.file_path.clone())
315+
{
316+
let _ = self.open(BlameFileOpen {
317+
file_path,
318+
commit_id: self.selected_commit(),
319+
selection: self.get_selection(),
320+
});
321+
}
322+
} else if key_match(
323+
key,
324+
self.key_config.keys.blame_back,
325+
) {
326+
match self.blame_stack.len() {
327+
0 => {
328+
self.hide_stacked(false)
329+
},
330+
1 => {
331+
self.blame_stack.pop();
332+
self.hide_stacked(false)
333+
},
334+
_ => {
335+
self.blame_stack.pop();
336+
},
337+
}
310338
}
311339

340+
312341
return Ok(EventState::Consumed);
313342
}
314343
}
@@ -342,7 +371,7 @@ impl BlameFilePopup {
342371
current_height: std::cell::Cell::new(0),
343372
app_sender: env.sender_app.clone(),
344373
git_sender: env.sender_git.clone(),
345-
blame: None,
374+
blame_stack: vec![],
346375
repo: env.repo.clone(),
347376
}
348377
}
@@ -371,11 +400,12 @@ impl BlameFilePopup {
371400
file_path: open.file_path,
372401
commit_id: open.commit_id,
373402
});
374-
self.blame =
375-
Some(BlameProcess::GettingBlame(AsyncBlame::new(
403+
self.blame_stack.push(
404+
BlameProcess::GettingBlame(AsyncBlame::new(
376405
self.repo.borrow().clone(),
377406
&self.git_sender,
378-
)));
407+
))
408+
);
379409
self.table_state.get_mut().select(Some(0));
380410
self.visible = true;
381411
self.update()?;
@@ -384,9 +414,8 @@ impl BlameFilePopup {
384414
}
385415

386416
///
387-
pub const fn any_work_pending(&self) -> bool {
388-
self.blame.is_some()
389-
&& !matches!(self.blame, Some(BlameProcess::Result(_)))
417+
pub fn any_work_pending(&self) -> bool {
418+
self.blame_stack.last().is_some_and(|last| last.result().is_some())
390419
}
391420

392421
pub fn update_async(
@@ -416,7 +445,7 @@ impl BlameFilePopup {
416445
if self.is_visible() {
417446
if let Some(BlameProcess::GettingBlame(
418447
ref mut async_blame,
419-
)) = self.blame
448+
)) = self.blame_stack.last_mut()
420449
{
421450
if let Some(params) = &self.params {
422451
if let Some((
@@ -425,19 +454,17 @@ impl BlameFilePopup {
425454
)) = async_blame.last()?
426455
{
427456
if previous_blame_params == *params {
428-
self.blame = Some(
429-
BlameProcess::SyntaxHighlighting {
430-
unstyled_file_blame:
431-
SyntaxFileBlame {
432-
file_blame:
433-
last_file_blame,
434-
styled_text: None,
435-
},
436-
job: AsyncSingleJob::new(
437-
self.app_sender.clone(),
438-
),
439-
},
440-
);
457+
*self.blame_stack.last_mut().unwrap() = BlameProcess::SyntaxHighlighting {
458+
unstyled_file_blame:
459+
SyntaxFileBlame {
460+
file_blame:
461+
last_file_blame,
462+
styled_text: None,
463+
},
464+
job: AsyncSingleJob::new(
465+
self.app_sender.clone(),
466+
),
467+
};
441468
self.set_open_selection();
442469
self.highlight_blame_lines();
443470

@@ -457,7 +484,7 @@ impl BlameFilePopup {
457484
let Some(BlameProcess::SyntaxHighlighting {
458485
ref unstyled_file_blame,
459486
ref job,
460-
}) = self.blame
487+
}) = self.blame_stack.last()
461488
else {
462489
return;
463490
};
@@ -474,16 +501,15 @@ impl BlameFilePopup {
474501
== Path::new(
475502
unstyled_file_blame.path(),
476503
) {
477-
self.blame =
478-
Some(BlameProcess::Result(
479-
SyntaxFileBlame {
480-
file_blame:
481-
unstyled_file_blame
482-
.file_blame
483-
.clone(),
484-
styled_text: Some(syntax),
485-
},
486-
));
504+
*self.blame_stack.last_mut().unwrap() = BlameProcess::Result(
505+
SyntaxFileBlame {
506+
file_blame:
507+
unstyled_file_blame
508+
.file_blame
509+
.clone(),
510+
styled_text: Some(syntax),
511+
},
512+
);
487513
}
488514
}
489515
}
@@ -498,7 +524,7 @@ impl BlameFilePopup {
498524
match (
499525
self.any_work_pending(),
500526
self.params.as_ref(),
501-
self.blame.as_ref().and_then(|blame| blame.result()),
527+
self.blame_stack.last().and_then(|blame| blame.result()),
502528
) {
503529
(true, Some(params), _) => {
504530
format!(
@@ -526,8 +552,7 @@ impl BlameFilePopup {
526552

527553
///
528554
fn get_rows(&self, width: usize) -> Vec<Row> {
529-
self.blame
530-
.as_ref()
555+
self.blame_stack.last()
531556
.and_then(|blame| blame.result())
532557
.map(|file_blame| {
533558
let styled_text: Option<Text<'_>> = file_blame
@@ -556,7 +581,7 @@ impl BlameFilePopup {
556581
let Some(BlameProcess::SyntaxHighlighting {
557582
ref unstyled_file_blame,
558583
ref mut job,
559-
}) = self.blame
584+
}) = self.blame_stack.last_mut()
560585
else {
561586
return;
562587
};
@@ -654,7 +679,7 @@ impl BlameFilePopup {
654679
});
655680

656681
let file_blame =
657-
self.blame.as_ref().and_then(|blame| blame.result());
682+
self.blame_stack.last().and_then(|blame| blame.result());
658683
let is_blamed_commit = file_blame
659684
.and_then(|file_blame| {
660685
blame_hunk.map(|hunk| {
@@ -673,8 +698,8 @@ impl BlameFilePopup {
673698
}
674699

675700
fn get_max_line_number(&self) -> usize {
676-
self.blame
677-
.as_ref()
701+
self.blame_stack
702+
.last()
678703
.and_then(|blame| blame.result())
679704
.map_or(0, |file_blame| file_blame.lines().len() - 1)
680705
}
@@ -727,8 +752,8 @@ impl BlameFilePopup {
727752
}
728753

729754
fn get_selection(&self) -> Option<usize> {
730-
self.blame
731-
.as_ref()
755+
self.blame_stack
756+
.last()
732757
.and_then(|blame| blame.result())
733758
.and_then(|_| {
734759
let table_state = self.table_state.take();
@@ -742,8 +767,8 @@ impl BlameFilePopup {
742767
}
743768

744769
fn selected_commit(&self) -> Option<CommitId> {
745-
self.blame
746-
.as_ref()
770+
self.blame_stack
771+
.last()
747772
.and_then(|blame| blame.result())
748773
.and_then(|file_blame| {
749774
let table_state = self.table_state.take();

0 commit comments

Comments
 (0)