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.rs105
1 files changed, 95 insertions, 10 deletions
diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs
index 377518fb..c1b5e2b1 100644
--- a/helix-view/src/gutter.rs
+++ b/helix-view/src/gutter.rs
@@ -35,10 +35,10 @@ impl GutterType {
}
}
- pub fn width(self, _view: &View, doc: &Document) -> usize {
+ pub fn width(self, view: &View, doc: &Document) -> usize {
match self {
GutterType::Diagnostics => 1,
- GutterType::LineNumbers => line_numbers_width(_view, doc),
+ GutterType::LineNumbers => line_numbers_width(view, doc),
GutterType::Spacer => 1,
GutterType::Diff => 1,
}
@@ -140,12 +140,13 @@ pub fn line_numbers<'doc>(
is_focused: bool,
) -> GutterFn<'doc> {
let text = doc.text().slice(..);
- let last_line = view.last_line(doc);
- let width = GutterType::LineNumbers.width(view, doc);
+ let width = line_numbers_width(view, doc);
+
+ let last_line_in_view = view.last_line(doc);
// Whether to draw the line number for the last line of the
// document or not. We only draw it if it's not an empty line.
- let draw_last = text.line_to_byte(last_line) < text.len_bytes();
+ let draw_last = text.line_to_byte(last_line_in_view) < text.len_bytes();
let linenr = theme.get("ui.linenr");
let linenr_select = theme.get("ui.linenr.selected");
@@ -158,7 +159,7 @@ pub fn line_numbers<'doc>(
let mode = editor.mode;
Box::new(move |line: usize, selected: bool, out: &mut String| {
- if line == last_line && !draw_last {
+ if line == last_line_in_view && !draw_last {
write!(out, "{:>1$}", '~', width).unwrap();
Some(linenr)
} else {
@@ -187,14 +188,19 @@ pub fn line_numbers<'doc>(
})
}
-pub fn line_numbers_width(_view: &View, doc: &Document) -> usize {
+/// The width of a "line-numbers" gutter
+///
+/// The width of the gutter depends on the number of lines in the document,
+/// whether there is content on the last line (the `~` line), and the
+/// `editor.gutters.line-numbers.min-width` settings.
+fn line_numbers_width(view: &View, doc: &Document) -> usize {
let text = doc.text();
let last_line = text.len_lines().saturating_sub(1);
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
let last_drawn = if draw_last { last_line + 1 } else { last_line };
-
- // set a lower bound to 2-chars to minimize ambiguous relative line numbers
- std::cmp::max(count_digits(last_drawn), 2)
+ let digits = count_digits(last_drawn);
+ let n_min = view.gutters.line_numbers.min_width;
+ digits.max(n_min)
}
pub fn padding<'doc>(
@@ -282,3 +288,82 @@ pub fn diagnostics_or_breakpoints<'doc>(
breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out))
})
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::document::Document;
+ use crate::editor::{GutterConfig, GutterLineNumbersConfig};
+ use crate::graphics::Rect;
+ use crate::DocumentId;
+ use helix_core::Rope;
+
+ #[test]
+ fn test_default_gutter_widths() {
+ let mut view = View::new(DocumentId::default(), GutterConfig::default());
+ view.area = Rect::new(40, 40, 40, 40);
+
+ let rope = Rope::from_str("abc\n\tdef");
+ let doc = Document::from(rope, None);
+
+ assert_eq!(view.gutters.layout.len(), 5);
+ assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
+ assert_eq!(view.gutters.layout[1].width(&view, &doc), 1);
+ assert_eq!(view.gutters.layout[2].width(&view, &doc), 3);
+ assert_eq!(view.gutters.layout[3].width(&view, &doc), 1);
+ assert_eq!(view.gutters.layout[4].width(&view, &doc), 1);
+ }
+
+ #[test]
+ fn test_configured_gutter_widths() {
+ let gutters = GutterConfig {
+ layout: vec![GutterType::Diagnostics],
+ ..Default::default()
+ };
+
+ let mut view = View::new(DocumentId::default(), gutters);
+ view.area = Rect::new(40, 40, 40, 40);
+
+ let rope = Rope::from_str("abc\n\tdef");
+ let doc = Document::from(rope, None);
+
+ assert_eq!(view.gutters.layout.len(), 1);
+ assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
+
+ let gutters = GutterConfig {
+ layout: vec![GutterType::Diagnostics, GutterType::LineNumbers],
+ line_numbers: GutterLineNumbersConfig { min_width: 10 },
+ };
+
+ let mut view = View::new(DocumentId::default(), gutters);
+ view.area = Rect::new(40, 40, 40, 40);
+
+ let rope = Rope::from_str("abc\n\tdef");
+ let doc = Document::from(rope, None);
+
+ assert_eq!(view.gutters.layout.len(), 2);
+ assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
+ assert_eq!(view.gutters.layout[1].width(&view, &doc), 10);
+ }
+
+ #[test]
+ fn test_line_numbers_gutter_width_resizes() {
+ let gutters = GutterConfig {
+ layout: vec![GutterType::Diagnostics, GutterType::LineNumbers],
+ line_numbers: GutterLineNumbersConfig { min_width: 1 },
+ };
+
+ let mut view = View::new(DocumentId::default(), gutters);
+ view.area = Rect::new(40, 40, 40, 40);
+
+ let rope = Rope::from_str("a\nb");
+ let doc_short = Document::from(rope, None);
+
+ let rope = Rope::from_str("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np");
+ let doc_long = Document::from(rope, None);
+
+ assert_eq!(view.gutters.layout.len(), 2);
+ assert_eq!(view.gutters.layout[1].width(&view, &doc_short), 1);
+ assert_eq!(view.gutters.layout[1].width(&view, &doc_long), 2);
+ }
+}