diff options
author | Doug Kelkhoff | 2022-11-08 12:19:59 +0000 |
---|---|---|
committer | GitHub | 2022-11-08 12:19:59 +0000 |
commit | 7ed9e9cf2567ee5e23cd8694ffccb4b38602c02a (patch) | |
tree | d815469c54dc42dc66e57fa85e389325e8b7a3a6 /helix-view/src/gutter.rs | |
parent | c94feed83d746e71fb030639d740af85162b0763 (diff) |
Dynamically resize line number gutter width (#3469)
* dynamically resize line number gutter width
* removing digits lower-bound, permitting spacer
* removing max line num char limit; adding notes; qualified successors; notes
* updating tests to use new line number width when testing views
* linenr width based on document line count
* using min width of 2 so line numbers relative is useful
* lint rolling; removing unnecessary type parameter lifetime
* merge change resolution
* reformat code
* rename row_styler to style; add int_log resource
* adding spacer to gutters default; updating book config entry
* adding view.inner_height(), swap for loop for iterator
* reverting change of current! to view! now that doc is not needed
Diffstat (limited to 'helix-view/src/gutter.rs')
-rw-r--r-- | helix-view/src/gutter.rs | 57 |
1 files changed, 50 insertions, 7 deletions
diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs index 2c207d27..61a17791 100644 --- a/helix-view/src/gutter.rs +++ b/helix-view/src/gutter.rs @@ -1,21 +1,54 @@ use std::fmt::Write; use crate::{ + editor::GutterType, graphics::{Color, Style, UnderlineStyle}, Document, Editor, Theme, View, }; +fn count_digits(n: usize) -> usize { + // NOTE: if int_log gets standardized in stdlib, can use checked_log10 + // (https://github.com/rust-lang/rust/issues/70887#issue) + 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 Gutter = for<'doc> fn(&'doc Editor, &'doc Document, &View, &Theme, bool, usize) -> GutterFn<'doc>; +impl GutterType { + pub fn style<'doc>( + self, + editor: &'doc Editor, + doc: &'doc Document, + view: &View, + theme: &Theme, + is_focused: bool, + ) -> GutterFn<'doc> { + match self { + GutterType::Diagnostics => { + diagnostics_or_breakpoints(editor, doc, view, theme, is_focused) + } + GutterType::LineNumbers => line_numbers(editor, doc, view, theme, is_focused), + GutterType::Spacer => padding(editor, doc, view, theme, is_focused), + } + } + + pub fn width(self, _view: &View, doc: &Document) -> usize { + match self { + GutterType::Diagnostics => 1, + GutterType::LineNumbers => line_numbers_width(_view, doc), + GutterType::Spacer => 1, + } + } +} + pub fn diagnostic<'doc>( _editor: &'doc Editor, doc: &'doc Document, _view: &View, theme: &Theme, _is_focused: bool, - _width: usize, ) -> GutterFn<'doc> { let warning = theme.get("warning"); let error = theme.get("error"); @@ -56,10 +89,11 @@ pub fn line_numbers<'doc>( view: &View, theme: &Theme, is_focused: bool, - width: usize, ) -> GutterFn<'doc> { let text = doc.text().slice(..); let last_line = view.last_line(doc); + let width = GutterType::LineNumbers.width(view, 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(); @@ -91,24 +125,35 @@ pub fn line_numbers<'doc>( } else { line + 1 }; + let style = if selected && is_focused { linenr_select } else { linenr }; + write!(out, "{:>1$}", display_num, width).unwrap(); Some(style) } }) } +pub 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) +} + pub fn padding<'doc>( _editor: &'doc Editor, _doc: &'doc Document, _view: &View, _theme: &Theme, _is_focused: bool, - _width: usize, ) -> GutterFn<'doc> { Box::new(|_line: usize, _selected: bool, _out: &mut String| None) } @@ -128,7 +173,6 @@ pub fn breakpoints<'doc>( _view: &View, theme: &Theme, _is_focused: bool, - _width: usize, ) -> GutterFn<'doc> { let warning = theme.get("warning"); let error = theme.get("error"); @@ -181,10 +225,9 @@ pub fn diagnostics_or_breakpoints<'doc>( view: &View, theme: &Theme, is_focused: bool, - width: usize, ) -> GutterFn<'doc> { - let diagnostics = diagnostic(editor, doc, view, theme, is_focused, width); - let breakpoints = breakpoints(editor, doc, view, theme, is_focused, width); + let diagnostics = diagnostic(editor, doc, view, theme, is_focused); + let breakpoints = breakpoints(editor, doc, view, theme, is_focused); Box::new(move |line, selected, out| { breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out)) |