Detailed changes
@@ -483,6 +483,7 @@
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }], // editor.action.moveSelectionToNextFindMatch / find_under_expand_skip
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }], // editor.action.moveSelectionToPreviousFindMatch
"ctrl-k ctrl-i": "editor::Hover",
+ "ctrl-k ctrl-b": "editor::BlameHover",
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": false }],
"f8": ["editor::GoToDiagnostic", { "severity": { "min": "hint", "max": "error" } }],
"shift-f8": ["editor::GoToPreviousDiagnostic", { "severity": { "min": "hint", "max": "error" } }],
@@ -537,6 +537,7 @@
"ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": false }], // editor.action.addSelectionToPreviousFindMatch
"cmd-k ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": true }], // editor.action.moveSelectionToPreviousFindMatch
"cmd-k cmd-i": "editor::Hover",
+ "cmd-k cmd-b": "editor::BlameHover",
"cmd-/": ["editor::ToggleComments", { "advance_downwards": false }],
"f8": ["editor::GoToDiagnostic", { "severity": { "min": "hint", "max": "error" } }],
"shift-f8": ["editor::GoToPreviousDiagnostic", { "severity": { "min": "hint", "max": "error" } }],
@@ -124,6 +124,7 @@
"g r a": "editor::ToggleCodeActions",
"g g": "vim::StartOfDocument",
"g h": "editor::Hover",
+ "g B": "editor::BlameHover",
"g t": "pane::ActivateNextItem",
"g shift-t": "pane::ActivatePreviousItem",
"g d": "editor::GoToDefinition",
@@ -322,6 +322,8 @@ actions!(
ApplyDiffHunk,
/// Deletes the character before the cursor.
Backspace,
+ /// Shows git blame information for the current line.
+ BlameHover,
/// Cancels the current operation.
Cancel,
/// Cancels the running flycheck operation.
@@ -950,6 +950,7 @@ struct InlineBlamePopover {
hide_task: Option<Task<()>>,
popover_bounds: Option<Bounds<Pixels>>,
popover_state: InlineBlamePopoverState,
+ keyboard_grace: bool,
}
enum SelectionDragState {
@@ -6517,21 +6518,55 @@ impl Editor {
}
}
+ pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
+ let snapshot = self.snapshot(window, cx);
+ let cursor = self.selections.newest::<Point>(cx).head();
+ let Some((buffer, point, _)) = snapshot.buffer_snapshot.point_to_buffer_point(cursor)
+ else {
+ return;
+ };
+
+ let Some(blame) = self.blame.as_ref() else {
+ return;
+ };
+
+ let row_info = RowInfo {
+ buffer_id: Some(buffer.remote_id()),
+ buffer_row: Some(point.row),
+ ..Default::default()
+ };
+ let Some(blame_entry) = blame
+ .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
+ .flatten()
+ else {
+ return;
+ };
+
+ let anchor = self.selections.newest_anchor().head();
+ let position = self.to_pixel_point(anchor, &snapshot, window);
+ if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
+ self.show_blame_popover(&blame_entry, position + last_bounds.origin, true, cx);
+ };
+ }
+
fn show_blame_popover(
&mut self,
blame_entry: &BlameEntry,
position: gpui::Point<Pixels>,
+ ignore_timeout: bool,
cx: &mut Context<Self>,
) {
if let Some(state) = &mut self.inline_blame_popover {
state.hide_task.take();
} else {
- let delay = EditorSettings::get_global(cx).hover_popover_delay;
+ let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
let blame_entry = blame_entry.clone();
let show_task = cx.spawn(async move |editor, cx| {
- cx.background_executor()
- .timer(std::time::Duration::from_millis(delay))
- .await;
+ if !ignore_timeout {
+ cx.background_executor()
+ .timer(std::time::Duration::from_millis(blame_popover_delay))
+ .await;
+ }
editor
.update(cx, |editor, cx| {
editor.inline_blame_popover_show_task.take();
@@ -6560,6 +6595,7 @@ impl Editor {
commit_message: details,
markdown,
},
+ keyboard_grace: ignore_timeout,
});
cx.notify();
})
@@ -216,6 +216,7 @@ impl EditorElement {
register_action(editor, window, Editor::newline_above);
register_action(editor, window, Editor::newline_below);
register_action(editor, window, Editor::backspace);
+ register_action(editor, window, Editor::blame_hover);
register_action(editor, window, Editor::delete);
register_action(editor, window, Editor::tab);
register_action(editor, window, Editor::backtab);
@@ -1143,10 +1144,14 @@ impl EditorElement {
.as_ref()
.and_then(|state| state.popover_bounds)
.map_or(false, |bounds| bounds.contains(&event.position));
+ let keyboard_grace = editor
+ .inline_blame_popover
+ .as_ref()
+ .map_or(false, |state| state.keyboard_grace);
if mouse_over_inline_blame || mouse_over_popover {
- editor.show_blame_popover(&blame_entry, event.position, cx);
- } else {
+ editor.show_blame_popover(&blame_entry, event.position, false, cx);
+ } else if !keyboard_grace {
editor.hide_blame_popover(cx);
}
} else {