aboutsummaryrefslogtreecommitdiff
path: root/helix-view/src/gutter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-view/src/gutter.rs')
-rw-r--r--helix-view/src/gutter.rs55
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))