feat: add line_blame
static command
_
This commit is contained in:
parent
f830c76a76
commit
c8eb9f2899
9 changed files with 77 additions and 29 deletions
book/src/generated
helix-term/src
helix-vcs/src
helix-view/src
|
@ -173,6 +173,7 @@
|
|||
| `redo` | Redo change | normal: `` U ``, select: `` U `` |
|
||||
| `earlier` | Move backward in history | normal: `` <A-u> ``, select: `` <A-u> `` |
|
||||
| `later` | Move forward in history | normal: `` <A-U> ``, select: `` <A-U> `` |
|
||||
| `line_blame` | Blame for the current line | normal: `` <space>B ``, select: `` <space>B `` |
|
||||
| `commit_undo_checkpoint` | Commit changes to new checkpoint | insert: `` <C-s> `` |
|
||||
| `yank` | Yank selection | normal: `` y ``, select: `` y `` |
|
||||
| `yank_to_clipboard` | Yank selections to clipboard | normal: `` <space>y ``, select: `` <space>y `` |
|
||||
|
|
|
@ -472,6 +472,7 @@ impl MappableCommand {
|
|||
redo, "Redo change",
|
||||
earlier, "Move backward in history",
|
||||
later, "Move forward in history",
|
||||
line_blame, "Blame for the current line",
|
||||
commit_undo_checkpoint, "Commit changes to new checkpoint",
|
||||
yank, "Yank selection",
|
||||
yank_to_clipboard, "Yank selections to clipboard",
|
||||
|
@ -6559,6 +6560,42 @@ fn goto_word(cx: &mut Context) {
|
|||
jump_to_word(cx, Movement::Move)
|
||||
}
|
||||
|
||||
fn line_blame(cx: &mut Context) {
|
||||
let (view, doc) = current!(cx.editor);
|
||||
const BLAME_ERROR: &str = "No blame available for the current file";
|
||||
let Some(diff) = doc.diff_handle() else {
|
||||
cx.editor.set_error(BLAME_ERROR);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(path) = doc.path() else {
|
||||
cx.editor.set_error(BLAME_ERROR);
|
||||
return;
|
||||
};
|
||||
|
||||
let cursor_line = doc.cursor_line(view.id);
|
||||
let (inserted_lines_count, deleted_lines_count) =
|
||||
diff.load().inserted_and_deleted_before_line(cursor_line);
|
||||
|
||||
let Ok(cursor_line) = u32::try_from(doc.cursor_line(view.id)) else {
|
||||
cx.editor.set_error(BLAME_ERROR);
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(output) = cx.editor.diff_providers.blame_line(
|
||||
path,
|
||||
cursor_line,
|
||||
inserted_lines_count,
|
||||
deleted_lines_count,
|
||||
) else {
|
||||
cx.editor.set_error(BLAME_ERROR);
|
||||
return;
|
||||
};
|
||||
|
||||
cx.editor
|
||||
.set_status(output.parse_format(&cx.editor.config().version_control.inline_blame_format));
|
||||
}
|
||||
|
||||
fn extend_to_word(cx: &mut Context) {
|
||||
jump_to_word(cx, Movement::Extend)
|
||||
}
|
||||
|
|
|
@ -32,19 +32,16 @@ impl helix_event::AsyncHook for BlameHandler {
|
|||
file,
|
||||
cursor_line,
|
||||
diff_providers,
|
||||
removed_lines_count,
|
||||
added_lines_count,
|
||||
deleted_lines_count: removed_lines_count,
|
||||
inserted_lines_count: added_lines_count,
|
||||
blame_format,
|
||||
} = event;
|
||||
|
||||
self.cursor_line = cursor_line;
|
||||
|
||||
// convert 0-based line numbers into 1-based line numbers
|
||||
let cursor_line = cursor_line + 1;
|
||||
|
||||
let worker = tokio::spawn(async move {
|
||||
diff_providers
|
||||
.blame(&file, cursor_line, added_lines_count, removed_lines_count)
|
||||
.blame_line(&file, cursor_line, added_lines_count, removed_lines_count)
|
||||
.map(|s| s.parse_format(&blame_format))
|
||||
});
|
||||
self.worker = Some(worker);
|
||||
|
@ -87,38 +84,27 @@ pub(super) fn register_hooks(handlers: &Handlers) {
|
|||
}
|
||||
|
||||
let (view, doc) = current!(event.cx.editor);
|
||||
let text = doc.text();
|
||||
let selection = doc.selection(view.id);
|
||||
let Some(file) = doc.path() else {
|
||||
return Ok(());
|
||||
};
|
||||
let file = file.to_path_buf();
|
||||
|
||||
let Ok(cursor_line) =
|
||||
u32::try_from(text.char_to_line(selection.primary().cursor(doc.text().slice(..))))
|
||||
else {
|
||||
let Ok(cursor_line) = u32::try_from(doc.cursor_line(view.id)) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let hunks = doc.diff_handle().unwrap().load();
|
||||
|
||||
let mut removed_lines_count: u32 = 0;
|
||||
let mut added_lines_count: u32 = 0;
|
||||
for hunk in hunks.hunks_intersecting_line_ranges(std::iter::once((0, cursor_line as usize)))
|
||||
{
|
||||
let lines_inserted = hunk.after.end - hunk.after.start;
|
||||
let lines_removed = hunk.before.end - hunk.before.start;
|
||||
added_lines_count += lines_inserted;
|
||||
removed_lines_count += lines_removed;
|
||||
}
|
||||
let (inserted_lines_count, deleted_lines_count) =
|
||||
hunks.inserted_and_deleted_before_line(cursor_line as usize);
|
||||
|
||||
send_blocking(
|
||||
&tx,
|
||||
BlameEvent::PostCommand {
|
||||
file,
|
||||
cursor_line,
|
||||
removed_lines_count,
|
||||
added_lines_count,
|
||||
deleted_lines_count,
|
||||
inserted_lines_count,
|
||||
// ok to clone because diff_providers is very small
|
||||
diff_providers: event.cx.editor.diff_providers.clone(),
|
||||
// ok to clone because blame_format is likely to be about 30 characters or less
|
||||
|
|
|
@ -232,6 +232,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
|
|||
"D" => workspace_diagnostics_picker,
|
||||
"g" => changed_file_picker,
|
||||
"a" => code_action,
|
||||
"B" => line_blame,
|
||||
"'" => last_picker,
|
||||
"G" => { "Debug (experimental)" sticky=true
|
||||
"l" => dap_launch,
|
||||
|
|
|
@ -177,6 +177,20 @@ impl Diff<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the amount of lines inserted and deleted before a given line
|
||||
pub fn inserted_and_deleted_before_line(&self, cursor_line: usize) -> (u32, u32) {
|
||||
let mut inserted_lines_count: u32 = 0;
|
||||
let mut deleted_lines_count: u32 = 0;
|
||||
for hunk in self.hunks_intersecting_line_ranges(std::iter::once((0, cursor_line))) {
|
||||
let lines_inserted = hunk.after.end - hunk.after.start;
|
||||
let lines_removed = hunk.before.end - hunk.before.start;
|
||||
inserted_lines_count += lines_inserted;
|
||||
deleted_lines_count += lines_removed;
|
||||
}
|
||||
|
||||
(inserted_lines_count, deleted_lines_count)
|
||||
}
|
||||
|
||||
pub fn doc(&self) -> &Rope {
|
||||
if self.inverted {
|
||||
&self.diff.diff_base
|
||||
|
|
|
@ -134,7 +134,9 @@ pub fn blame_line(
|
|||
// So when our cursor is on the 10th added line or earlier, blame_line will be 0. This means
|
||||
// the blame will be incorrect. But that's fine, because when the cursor_line is on some hunk,
|
||||
// we can show to the user nothing at all. This is detected in the editor
|
||||
let blame_line = line.saturating_sub(added_lines_count) + removed_lines_count;
|
||||
//
|
||||
// Add 1 to convert 0-based line numbers into 1-based
|
||||
let blame_line = line.saturating_sub(added_lines_count) + removed_lines_count + 1;
|
||||
|
||||
let repo_dir = get_repo_dir(file)?;
|
||||
let repo = open_repo(repo_dir)
|
||||
|
|
|
@ -50,8 +50,8 @@ impl DiffProviderRegistry {
|
|||
})
|
||||
}
|
||||
|
||||
/// Blame range of lines in a file. Lines are 1-indexed
|
||||
pub fn blame(
|
||||
/// Blame a line in a file
|
||||
pub fn blame_line(
|
||||
&self,
|
||||
file: &Path,
|
||||
line: u32,
|
||||
|
@ -60,7 +60,7 @@ impl DiffProviderRegistry {
|
|||
) -> anyhow::Result<BlameInformation> {
|
||||
self.providers
|
||||
.iter()
|
||||
.map(|provider| provider.blame(file, line, added_lines_count, removed_lines_count))
|
||||
.map(|provider| provider.blame_line(file, line, added_lines_count, removed_lines_count))
|
||||
.next()
|
||||
.context("No provider found")?
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ impl DiffProvider {
|
|||
}
|
||||
}
|
||||
|
||||
fn blame(
|
||||
fn blame_line(
|
||||
&self,
|
||||
file: &Path,
|
||||
line: u32,
|
||||
|
|
|
@ -1262,6 +1262,13 @@ impl Document {
|
|||
Range::new(0, 1).grapheme_aligned(self.text().slice(..))
|
||||
}
|
||||
|
||||
/// Get the line of cursor for the primary selection
|
||||
pub fn cursor_line(&self, view_id: ViewId) -> usize {
|
||||
let text = self.text();
|
||||
let selection = self.selection(view_id);
|
||||
text.char_to_line(selection.primary().cursor(text.slice(..)))
|
||||
}
|
||||
|
||||
/// Reset the view's selection on this document to the
|
||||
/// [origin](Document::origin) cursor.
|
||||
pub fn reset_selection(&mut self, view_id: ViewId) {
|
||||
|
|
|
@ -25,9 +25,9 @@ pub enum BlameEvent {
|
|||
file: PathBuf,
|
||||
cursor_line: u32,
|
||||
/// How many lines were removed before cursor_line
|
||||
removed_lines_count: u32,
|
||||
deleted_lines_count: u32,
|
||||
/// How many lines were added before cursor_line
|
||||
added_lines_count: u32,
|
||||
inserted_lines_count: u32,
|
||||
diff_providers: DiffProviderRegistry,
|
||||
/// Format of the blame
|
||||
blame_format: String,
|
||||
|
|
Loading…
Add table
Reference in a new issue