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.rs112
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);