diff options
Diffstat (limited to 'helix-view/src/gutter.rs')
-rw-r--r-- | helix-view/src/gutter.rs | 55 |
1 files changed, 52 insertions, 3 deletions
diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs index 61a17791..377518fb 100644 --- a/helix-view/src/gutter.rs +++ b/helix-view/src/gutter.rs @@ -12,7 +12,7 @@ fn count_digits(n: usize) -> usize { std::iter::successors(Some(n), |&n| (n >= 10).then(|| n / 10)).count() } -pub type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>; +pub type GutterFn<'doc> = Box<dyn FnMut(usize, bool, &mut String) -> Option<Style> + 'doc>; pub type Gutter = for<'doc> fn(&'doc Editor, &'doc Document, &View, &Theme, bool, usize) -> GutterFn<'doc>; @@ -31,6 +31,7 @@ impl GutterType { } GutterType::LineNumbers => line_numbers(editor, doc, view, theme, is_focused), GutterType::Spacer => padding(editor, doc, view, theme, is_focused), + GutterType::Diff => diff(editor, doc, view, theme, is_focused), } } @@ -39,6 +40,7 @@ impl GutterType { GutterType::Diagnostics => 1, GutterType::LineNumbers => line_numbers_width(_view, doc), GutterType::Spacer => 1, + GutterType::Diff => 1, } } } @@ -83,6 +85,53 @@ pub fn diagnostic<'doc>( }) } +pub fn diff<'doc>( + _editor: &'doc Editor, + doc: &'doc Document, + _view: &View, + theme: &Theme, + _is_focused: bool, +) -> GutterFn<'doc> { + let added = theme.get("diff.plus"); + let deleted = theme.get("diff.minus"); + let modified = theme.get("diff.delta"); + if let Some(diff_handle) = doc.diff_handle() { + let hunks = diff_handle.hunks(); + let mut hunk_i = 0; + let mut hunk = hunks.nth_hunk(hunk_i); + Box::new(move |line: usize, _selected: bool, out: &mut String| { + // truncating the line is fine here because we don't compute diffs + // for files with more lines than i32::MAX anyways + // we need to special case removals here + // these technically do not have a range of lines to highlight (`hunk.after.start == hunk.after.end`). + // However we still want to display these hunks correctly we must not yet skip to the next hunk here + while hunk.after.end < line as u32 + || !hunk.is_pure_removal() && line as u32 == hunk.after.end + { + hunk_i += 1; + hunk = hunks.nth_hunk(hunk_i); + } + + if hunk.after.start > line as u32 { + return None; + } + + let (icon, style) = if hunk.is_pure_insertion() { + ("▍", added) + } else if hunk.is_pure_removal() { + ("▔", deleted) + } else { + ("▍", modified) + }; + + write!(out, "{}", icon).unwrap(); + Some(style) + }) + } else { + Box::new(move |_, _, _| None) + } +} + pub fn line_numbers<'doc>( editor: &'doc Editor, doc: &'doc Document, @@ -226,8 +275,8 @@ pub fn diagnostics_or_breakpoints<'doc>( theme: &Theme, is_focused: bool, ) -> GutterFn<'doc> { - let diagnostics = diagnostic(editor, doc, view, theme, is_focused); - let breakpoints = breakpoints(editor, doc, view, theme, is_focused); + let mut diagnostics = diagnostic(editor, doc, view, theme, is_focused); + let mut breakpoints = breakpoints(editor, doc, view, theme, is_focused); Box::new(move |line, selected, out| { breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out)) |