diff options
Diffstat (limited to 'helix-view/src/gutter.rs')
-rw-r--r-- | helix-view/src/gutter.rs | 105 |
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); + } +} |