Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/gutter.rs')
| -rw-r--r-- | helix-view/src/gutter.rs | 112 |
1 files changed, 99 insertions, 13 deletions
diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs index 7506e515..8c9ab818 100644 --- a/helix-view/src/gutter.rs +++ b/helix-view/src/gutter.rs @@ -1,13 +1,96 @@ use std::fmt::Write; -use helix_core::syntax::config::LanguageServerFeature; +use helix_config::{config_serde_adapter, options, List}; +use helix_core::syntax::LanguageServerFeature; +use serde::{Deserialize, Serialize}; use crate::{ - editor::GutterType, graphics::{Style, UnderlineStyle}, Document, Editor, Theme, View, }; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum LineNumber { + /// Show absolute line number + #[serde(alias = "abs")] + Absolute, + /// If focused and in normal/select mode, show relative line number to the primary cursor. + /// If unfocused or in insert mode, show absolute line number. + #[serde(alias = "rel")] + Relative, +} + +config_serde_adapter!(LineNumber); + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GutterType { + /// Show diagnostics and other features like breakpoints + Diagnostics, + /// Show line numbers + LineNumbers, + /// Show one blank space + Spacer, + /// Highlight local changes + Diff, +} + +impl std::str::FromStr for GutterType { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_str() { + "diagnostics" => Ok(Self::Diagnostics), + "spacer" => Ok(Self::Spacer), + "line-numbers" => Ok(Self::LineNumbers), + "diff" => Ok(Self::Diff), + _ => anyhow::bail!( + "expected one of `diagnostics`, `spacer`, `line-numbers` or `diff` (found {s:?})" + ), + } + } +} + +impl helix_config::Ty for GutterType { + fn from_value(val: helix_config::Value) -> anyhow::Result<Self> { + let val: String = val.typed()?; + val.parse() + } + + fn to_value(&self) -> helix_config::Value { + match self { + GutterType::Diagnostics => "diagnostics".into(), + GutterType::LineNumbers => "lineNumbers".into(), + GutterType::Spacer => "spacer".into(), + GutterType::Diff => "diff".into(), + } + } +} + +options! { + struct GutterConfig { + /// A list of gutters to display + #[name = "gutters.layout"] + layout: List<GutterType> = &[ + GutterType::Diagnostics, + GutterType::Spacer, + GutterType::LineNumbers, + GutterType::Spacer, + GutterType::Diff, + ], + /// The minimum number of characters the line number gutter should take up. + #[name = "gutters.line-numbers.min-width"] + line_number_min_width: usize = 3, + /// Line number display: `absolute` simply shows each line's number, + /// while `relative` shows the distance from the current line. When + /// unfocused or in insert mode, `relative` will still show absolute + /// line numbers + #[name = "line-number"] + line_number_mode: LineNumber = LineNumber::Absolute, + } +} + fn count_digits(n: usize) -> usize { (usize::checked_ilog10(n).unwrap_or(0) + 1) as usize } @@ -69,10 +152,9 @@ pub fn diagnostic<'doc>( .iter() .take_while(|d| { d.line == line - && d.provider.language_server_id().is_none_or(|id| { - doc.language_servers_with_feature(LanguageServerFeature::Diagnostics) - .any(|ls| ls.id() == id) - }) + && doc + .language_servers_with_feature(LanguageServerFeature::Diagnostics) + .any(|ls| ls.id() == d.language_server_id) }); diagnostics_on_line.max_by_key(|d| d.severity).map(|d| { write!(out, "●").ok(); @@ -179,7 +261,7 @@ pub fn line_numbers<'doc>( && current_line != line; let display_num = if relative { - current_line.abs_diff(line) + abs_diff(current_line, line) } else { line + 1 }; @@ -227,6 +309,15 @@ pub fn padding<'doc>( Box::new(|_line: usize, _selected: bool, _first_visual_line: bool, _out: &mut String| None) } +#[inline(always)] +const fn abs_diff(a: usize, b: usize) -> usize { + if a > b { + a - b + } else { + b - a + } +} + pub fn breakpoints<'doc>( editor: &'doc Editor, doc: &'doc Document, @@ -334,7 +425,7 @@ mod tests { use crate::graphics::Rect; use crate::DocumentId; use arc_swap::ArcSwap; - use helix_core::{syntax, Rope}; + use helix_core::Rope; #[test] fn test_default_gutter_widths() { @@ -346,7 +437,6 @@ mod tests { rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), - Arc::new(ArcSwap::from_pointee(syntax::Loader::default())), ); assert_eq!(view.gutters.layout.len(), 5); @@ -372,7 +462,6 @@ mod tests { rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), - Arc::new(ArcSwap::from_pointee(syntax::Loader::default())), ); assert_eq!(view.gutters.layout.len(), 1); @@ -391,7 +480,6 @@ mod tests { rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), - Arc::new(ArcSwap::from_pointee(syntax::Loader::default())), ); assert_eq!(view.gutters.layout.len(), 2); @@ -414,7 +502,6 @@ mod tests { rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), - Arc::new(ArcSwap::from_pointee(syntax::Loader::default())), ); let rope = Rope::from_str("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np"); @@ -422,7 +509,6 @@ mod tests { rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), - Arc::new(ArcSwap::from_pointee(syntax::Loader::default())), ); assert_eq!(view.gutters.layout.len(), 2); |