From 60dde3a40928e010e518b225b948c413f995a383 Mon Sep 17 00:00:00 2001 From: Stephen Broadley Date: Fri, 2 Aug 2024 18:53:23 +0100 Subject: [PATCH 1/6] added 'nullspace' option --- helix-term/src/ui/editor.rs | 56 ++++++++++++++++++++++++++++++++++--- helix-view/src/editor.rs | 19 +++++++++++++ helix-view/src/view.rs | 20 ++++++++++++- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index f7541fe25..61a8f392f 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -93,7 +93,51 @@ impl EditorView { let theme = &editor.theme; let config = editor.config(); - let view_offset = doc.view_offset(view.id); + if config.nullspace.enable { + let gutter_width = view.gutter_offset(doc); + let text_width = config.text_width as u16; + + let view_width = text_width + gutter_width; + + if view_width < view.area.width { + let null_width = (area.width - view_width) / 2; + + let null_l = area.with_width(null_width).clip_bottom(1); + let null_r = Rect::new( + area.x + view_width + null_width, + area.y, + area.width - view_width - null_width, + area.height, + ) + .clip_bottom(1); + + let null_style = theme + .try_get("ui.null") + .or_else(|| Some(theme.get("ui.linenr"))) + .unwrap(); + + fn fill_area(s: &mut Surface, r: Rect, c: char) { + for y in r.top()..r.bottom() { + for x in r.left()..r.right() { + let cell = s.get_mut(x, y).unwrap(); + cell.symbol.clear(); + cell.symbol.push(c); + } + } + } + + // We currently on use the first char in the 'pattern' + // but in future I would like to use the whole string + // to render nicer patterns. + let c = config.nullspace.pattern.chars().nth(0).unwrap(); + + fill_area(surface, null_l, c); + fill_area(surface, null_r, c); + + surface.set_style(null_l, null_style); + surface.set_style(null_r, null_style); + } + } let text_annotations = view.text_annotations(doc, Some(theme)); let mut decorations = DecorationManager::default(); @@ -165,13 +209,16 @@ impl EditorView { } } - let gutter_overflow = view.gutter_offset(doc) == 0; - if !gutter_overflow { + let gutter_width = view.gutter_offset(doc); + if gutter_width > 0 { + let mut gutter_area = inner.with_width(gutter_width); + gutter_area.x -= gutter_width; + Self::render_gutter( editor, doc, view, - view.area, + gutter_area, theme, is_focused & self.terminal_focused, &mut decorations, @@ -201,6 +248,7 @@ impl EditorView { inline_diagnostic_config, config.end_of_line_diagnostics, )); + render_document( surface, inner, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 1708b3b4e..5054e79d5 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -315,6 +315,8 @@ pub struct Config { /// Column numbers at which to draw the rulers. Defaults to `[]`, meaning no rulers. pub rulers: Vec, #[serde(default)] + pub nullspace: NullspaceConfig, + #[serde(default)] pub whitespace: WhitespaceConfig, /// Persistently display open buffers along the top pub bufferline: BufferLine, @@ -698,6 +700,22 @@ impl std::str::FromStr for GutterType { } } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default)] +pub struct NullspaceConfig { + pub enable: bool, + pub pattern: String, +} + +impl Default for NullspaceConfig { + fn default() -> Self { + Self { + enable: true, + pattern: String::from("╲"), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] pub struct WhitespaceConfig { @@ -960,6 +978,7 @@ impl Default for Config { lsp: LspConfig::default(), terminal: get_terminal_provider(), rulers: Vec::new(), + nullspace: NullspaceConfig::default(), whitespace: WhitespaceConfig::default(), bufferline: BufferLine::default(), indent_guides: IndentGuidesConfig::default(), diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index a229f01ea..2c3130c22 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -191,7 +191,25 @@ impl View { } pub fn inner_area(&self, doc: &Document) -> Rect { - self.area.clip_left(self.gutter_offset(doc)).clip_bottom(1) // -1 for statusline + let config = doc.config.load(); + let gutter_width = self.gutter_offset(doc); + + if config.nullspace.enable { + let text_width = config.text_width as u16; + let view_width = gutter_width + text_width; + + if self.area.width > view_width { + let null_width = (self.area.width - view_width) / 2; + + return self + .area + .clip_left(gutter_width + null_width) + .clip_bottom(1) + .with_width(text_width.min(self.area.width)); + } + } + + self.area.clip_left(gutter_width).clip_bottom(1) // -1 for statusline } pub fn inner_height(&self) -> usize { From 35657560ddadd927c0b9702815030278bbba41e7 Mon Sep 17 00:00:00 2001 From: Stephen Broadley Date: Fri, 2 Aug 2024 19:15:05 +0100 Subject: [PATCH 2/6] corrected merge --- helix-term/src/ui/editor.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 61a8f392f..4710f833b 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -164,6 +164,8 @@ impl EditorView { decorations.add_decoration(line_decoration); } + let view_offset = doc.view_offset(view.id); + let syntax_highlights = Self::doc_syntax_highlights(doc, view_offset.anchor, inner.height, theme); From 4e6cd3fd06d59872d09a8c0fbc46cc1bc8c2998f Mon Sep 17 00:00:00 2001 From: Stephen Broadley Date: Fri, 2 Aug 2024 20:13:49 +0100 Subject: [PATCH 3/6] added 'shift_left' and 'shift_right' to Rect --- helix-term/src/ui/editor.rs | 3 +-- helix-view/src/editor.rs | 2 +- helix-view/src/graphics.rs | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 4710f833b..2d48c1fb6 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -213,8 +213,7 @@ impl EditorView { let gutter_width = view.gutter_offset(doc); if gutter_width > 0 { - let mut gutter_area = inner.with_width(gutter_width); - gutter_area.x -= gutter_width; + let gutter_area = inner.with_width(gutter_width).shift_left(gutter_width); Self::render_gutter( editor, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 5054e79d5..a8ede13d6 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -710,7 +710,7 @@ pub struct NullspaceConfig { impl Default for NullspaceConfig { fn default() -> Self { Self { - enable: true, + enable: false, pattern: String::from("╲"), } } diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index a26823b97..a04394a0a 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -153,6 +153,22 @@ impl Rect { } } + // Returns a new Rect shifted left. + pub fn shift_left(self, shift: u16) -> Rect { + Rect { + x: self.x - shift, + ..self + } + } + + // Returns a new Rect shifted left. + pub fn shift_right(self, shift: u16) -> Rect { + Rect { + x: self.x + shift, + ..self + } + } + // Returns a new Rect with height reduced from the bottom. // This does _not_ change the `y` coordinate. pub fn clip_bottom(self, height: u16) -> Rect { From faa03c4e850469a03f8eeb1d6131bb292affbf63 Mon Sep 17 00:00:00 2001 From: Stephen Broadley Date: Fri, 2 Aug 2024 20:38:24 +0100 Subject: [PATCH 4/6] updated style name for 'nullspace' to 'ui.nullspace' --- helix-term/src/ui/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 2d48c1fb6..f57d9f2eb 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -112,7 +112,7 @@ impl EditorView { .clip_bottom(1); let null_style = theme - .try_get("ui.null") + .try_get("ui.nullspace") .or_else(|| Some(theme.get("ui.linenr"))) .unwrap(); From eb614fc4a4351df65d65400b0431e8e37f270bce Mon Sep 17 00:00:00 2001 From: Stephen Broadley Date: Sun, 4 Aug 2024 20:46:21 +0100 Subject: [PATCH 5/6] add 'split_***' utility API to Rect --- helix-view/src/graphics.rs | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index a04394a0a..c5b6f9baf 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -254,6 +254,74 @@ impl Rect { && self.y < other.y + other.height && self.y + self.height > other.y } + + pub fn split_left(self, width: u16) -> (Rect, Rect) { + ( + Rect { width, ..self }, + Rect { + x: self.x + width, + width: self.width - width, + ..self + }, + ) + } + + pub fn split_right(self, width: u16) -> (Rect, Rect) { + ( + Rect { + width: self.width - width, + ..self + }, + Rect { + x: self.right() - width, + width, + ..self + }, + ) + } + + pub fn split_top(self, height: u16) -> (Rect, Rect) { + ( + Rect { height, ..self }, + Rect { + y: self.y + height, + height: self.height - height, + ..self + }, + ) + } + + pub fn split_bottom(self, height: u16) -> (Rect, Rect) { + ( + Rect { + height: self.height - height, + ..self + }, + Rect { + y: self.bottom() - height, + height, + ..self + }, + ) + } + + pub fn split_centre_vertical(self, width: u16) -> (Rect, Rect, Rect) { + let l = (self.width - width) / 2; + let r = self.width - width - l; + ( + Rect { width: l, ..self }, + Rect { + width, + x: self.x + l, + ..self + }, + Rect { + width: r, + x: self.right() - r, + ..self + }, + ) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] From cf531cb8545a4938c1e4f98ab530ee1337321172 Mon Sep 17 00:00:00 2001 From: Stephen Broadley Date: Sun, 4 Aug 2024 20:47:00 +0100 Subject: [PATCH 6/6] simplified code for 'nullspace' calculation in EditorView render --- helix-term/src/ui/editor.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index f57d9f2eb..4eb25935c 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -94,22 +94,8 @@ impl EditorView { let config = editor.config(); if config.nullspace.enable { - let gutter_width = view.gutter_offset(doc); - let text_width = config.text_width as u16; - - let view_width = text_width + gutter_width; - - if view_width < view.area.width { - let null_width = (area.width - view_width) / 2; - - let null_l = area.with_width(null_width).clip_bottom(1); - let null_r = Rect::new( - area.x + view_width + null_width, - area.y, - area.width - view_width - null_width, - area.height, - ) - .clip_bottom(1); + if inner.width < view.area.width { + let (null_l, _, null_r) = area.clip_bottom(1).split_centre_vertical(inner.width); let null_style = theme .try_get("ui.nullspace")