From f698c5739dfdbe6e892a17c20598750cb2a440fd Mon Sep 17 00:00:00 2001
From: A-Walrus <58790821+A-Walrus@users.noreply.github.com>
Date: Sat, 8 Oct 2022 21:28:42 +0300
Subject: [PATCH] Add cursorcolumn (#4084)
* Implement cursorcolumn
* Add documentation
* Separate column style from line with fallback
* Fallback to cursorcolumn first
* Switch to non-fallback try_get_exact
Add new function `try_get_exact`, which doesn't perform fallback,
and use that instead because the fallback behaviour is being handled
manually.
---
book/src/configuration.md | 1 +
book/src/themes.md | 92 +++++++++++++++++++------------------
helix-term/src/ui/editor.rs | 52 ++++++++++++++++++++-
helix-view/src/editor.rs | 3 ++
helix-view/src/theme.rs | 7 +++
5 files changed, 109 insertions(+), 46 deletions(-)
diff --git a/book/src/configuration.md b/book/src/configuration.md
index ccfa61c00bfc..1a3975277590 100644
--- a/book/src/configuration.md
+++ b/book/src/configuration.md
@@ -45,6 +45,7 @@ on unix operating systems.
| `shell` | Shell to use when running external commands. | Unix: `["sh", "-c"]`
Windows: `["cmd", "/C"]` |
| `line-number` | 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. | `absolute` |
| `cursorline` | Highlight all lines with a cursor. | `false` |
+| `cursorcolumn` | Highlight all columns with a cursor. | `false` |
| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "line-numbers"]` |
| `auto-completion` | Enable automatic pop up of auto-completion. | `true` |
| `auto-format` | Enable automatic formatting on save. | `true` |
diff --git a/book/src/themes.md b/book/src/themes.md
index 4cded68fe4ed..2563bb8533ce 100644
--- a/book/src/themes.md
+++ b/book/src/themes.md
@@ -225,51 +225,53 @@ These scopes are used for theming the editor interface.
- `hover` - for hover popup ui
-| Key | Notes |
-| --- | --- |
-| `ui.background` | |
-| `ui.background.separator` | Picker separator below input line |
-| `ui.cursor` | |
-| `ui.cursor.insert` | |
-| `ui.cursor.select` | |
-| `ui.cursor.match` | Matching bracket etc. |
-| `ui.cursor.primary` | Cursor with primary selection |
-| `ui.gutter` | Gutter |
-| `ui.gutter.selected` | Gutter for the line the cursor is on |
-| `ui.linenr` | Line numbers |
-| `ui.linenr.selected` | Line number for the line the cursor is on |
-| `ui.statusline` | Statusline |
-| `ui.statusline.inactive` | Statusline (unfocused document) |
-| `ui.statusline.normal` | Statusline mode during normal mode ([only if `editor.color-modes` is enabled][editor-section]) |
-| `ui.statusline.insert` | Statusline mode during insert mode ([only if `editor.color-modes` is enabled][editor-section]) |
-| `ui.statusline.select` | Statusline mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) |
-| `ui.statusline.separator` | Separator character in statusline |
-| `ui.popup` | Documentation popups (e.g space-k) |
-| `ui.popup.info` | Prompt for multiple key options |
-| `ui.window` | Border lines separating splits |
-| `ui.help` | Description box for commands |
-| `ui.text` | Command prompts, popup text, etc. |
-| `ui.text.focus` | |
-| `ui.text.info` | The key: command text in `ui.popup.info` boxes |
-| `ui.virtual.ruler` | Ruler columns (see the [`editor.rulers` config][editor-section])|
-| `ui.virtual.whitespace` | Visible white-space characters |
-| `ui.virtual.indent-guide` | Vertical indent width guides |
-| `ui.menu` | Code and command completion menus |
-| `ui.menu.selected` | Selected autocomplete item |
-| `ui.menu.scroll` | `fg` sets thumb color, `bg` sets track color of scrollbar |
-| `ui.selection` | For selections in the editing area |
-| `ui.selection.primary` | |
-| `ui.cursorline.primary` | The line of the primary cursor |
-| `ui.cursorline.secondary` | The lines of any other cursors |
-| `warning` | Diagnostics warning (gutter) |
-| `error` | Diagnostics error (gutter) |
-| `info` | Diagnostics info (gutter) |
-| `hint` | Diagnostics hint (gutter) |
-| `diagnostic` | Diagnostics fallback style (editing area) |
-| `diagnostic.hint` | Diagnostics hint (editing area) |
-| `diagnostic.info` | Diagnostics info (editing area) |
-| `diagnostic.warning` | Diagnostics warning (editing area) |
-| `diagnostic.error` | Diagnostics error (editing area) |
+| Key | Notes |
+| --- | --- |
+| `ui.background` | |
+| `ui.background.separator` | Picker separator below input line |
+| `ui.cursor` | |
+| `ui.cursor.insert` | |
+| `ui.cursor.select` | |
+| `ui.cursor.match` | Matching bracket etc. |
+| `ui.cursor.primary` | Cursor with primary selection |
+| `ui.gutter` | Gutter |
+| `ui.gutter.selected` | Gutter for the line the cursor is on |
+| `ui.linenr` | Line numbers |
+| `ui.linenr.selected` | Line number for the line the cursor is on |
+| `ui.statusline` | Statusline |
+| `ui.statusline.inactive` | Statusline (unfocused document) |
+| `ui.statusline.normal` | Statusline mode during normal mode ([only if `editor.color-modes` is enabled][editor-section]) |
+| `ui.statusline.insert` | Statusline mode during insert mode ([only if `editor.color-modes` is enabled][editor-section]) |
+| `ui.statusline.select` | Statusline mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) |
+| `ui.statusline.separator` | Separator character in statusline |
+| `ui.popup` | Documentation popups (e.g space-k) |
+| `ui.popup.info` | Prompt for multiple key options |
+| `ui.window` | Border lines separating splits |
+| `ui.help` | Description box for commands |
+| `ui.text` | Command prompts, popup text, etc. |
+| `ui.text.focus` | |
+| `ui.text.info` | The key: command text in `ui.popup.info` boxes |
+| `ui.virtual.ruler` | Ruler columns (see the [`editor.rulers` config][editor-section]) |
+| `ui.virtual.whitespace` | Visible white-space characters |
+| `ui.virtual.indent-guide` | Vertical indent width guides |
+| `ui.menu` | Code and command completion menus |
+| `ui.menu.selected` | Selected autocomplete item |
+| `ui.menu.scroll` | `fg` sets thumb color, `bg` sets track color of scrollbar |
+| `ui.selection` | For selections in the editing area |
+| `ui.selection.primary` | |
+| `ui.cursorline.primary` | The line of the primary cursor ([if cursorline is enabled][editor-section]) |
+| `ui.cursorline.secondary` | The lines of any other cursors ([if cursorline is enabled][editor-section]) |
+| `ui.cursorcolumn.primary` | The column of the primary cursor ([if cursorcolumn is enabled][editor-section]) |
+| `ui.cursorcolumn.secondary` | The columns of any other cursors ([if cursorcolumn is enabled][editor-section]) |
+| `warning` | Diagnostics warning (gutter) |
+| `error` | Diagnostics error (gutter) |
+| `info` | Diagnostics info (gutter) |
+| `hint` | Diagnostics hint (gutter) |
+| `diagnostic` | Diagnostics fallback style (editing area) |
+| `diagnostic.hint` | Diagnostics hint (editing area) |
+| `diagnostic.info` | Diagnostics info (editing area) |
+| `diagnostic.warning` | Diagnostics warning (editing area) |
+| `diagnostic.error` | Diagnostics error (editing area) |
You can check compliance to spec with
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 3c713fa140bf..bc3067edc373 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -13,7 +13,7 @@ use helix_core::{
movement::Direction,
syntax::{self, HighlightEvent},
unicode::width::UnicodeWidthStr,
- LineEnding, Position, Range, Selection, Transaction,
+ visual_coords_at_pos, LineEnding, Position, Range, Selection, Transaction,
};
use helix_view::{
document::{Mode, SCRATCH_BUFFER_NAME},
@@ -120,6 +120,9 @@ impl EditorView {
if is_focused && editor.config().cursorline {
Self::highlight_cursorline(doc, view, surface, theme);
}
+ if is_focused && editor.config().cursorcolumn {
+ Self::highlight_cursorcolumn(doc, view, surface, theme);
+ }
let highlights = Self::doc_syntax_highlights(doc, view.offset, inner.height, theme);
let highlights = syntax::merge(highlights, Self::doc_diagnostics_highlights(doc, theme));
@@ -832,6 +835,53 @@ impl EditorView {
}
}
+ /// Apply the highlighting on the columns where a cursor is active
+ pub fn highlight_cursorcolumn(
+ doc: &Document,
+ view: &View,
+ surface: &mut Surface,
+ theme: &Theme,
+ ) {
+ let text = doc.text().slice(..);
+
+ // Manual fallback behaviour:
+ // ui.cursorcolumn.{p/s} -> ui.cursorcolumn -> ui.cursorline.{p/s}
+ let primary_style = theme
+ .try_get_exact("ui.cursorcolumn.primary")
+ .or_else(|| theme.try_get_exact("ui.cursorcolumn"))
+ .unwrap_or_else(|| theme.get("ui.cursorline.primary"));
+ let secondary_style = theme
+ .try_get_exact("ui.cursorcolumn.secondary")
+ .or_else(|| theme.try_get_exact("ui.cursorcolumn"))
+ .unwrap_or_else(|| theme.get("ui.cursorline.secondary"));
+
+ let inner_area = view.inner_area();
+ let offset = view.offset.col;
+
+ let selection = doc.selection(view.id);
+ let primary = selection.primary();
+ for range in selection.iter() {
+ let is_primary = primary == *range;
+
+ let Position { row: _, col } =
+ visual_coords_at_pos(text, range.cursor(text), doc.tab_width());
+ // if the cursor is horizontally in the view
+ if col >= offset && inner_area.width > (col - offset) as u16 {
+ let area = Rect::new(
+ inner_area.x + (col - offset) as u16,
+ view.area.y,
+ 1,
+ view.area.height,
+ );
+ if is_primary {
+ surface.set_style(area, primary_style)
+ } else {
+ surface.set_style(area, secondary_style)
+ }
+ }
+ }
+ }
+
/// Handle events by looking them up in `self.keymaps`. Returns None
/// if event was handled (a command was executed or a subkeymap was
/// activated). Only KeymapResult::{NotFound, Cancelled} is returned
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index de7c924b10c5..14d1164c98cc 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -187,6 +187,8 @@ pub struct Config {
pub line_number: LineNumber,
/// Highlight the lines cursors are currently on. Defaults to false.
pub cursorline: bool,
+ /// Highlight the columns cursors are currently on. Defaults to false.
+ pub cursorcolumn: bool,
/// Gutters. Default ["diagnostics", "line-numbers"]
pub gutters: Vec,
/// Middle click paste support. Defaults to true.
@@ -647,6 +649,7 @@ impl Default for Config {
},
line_number: LineNumber::Absolute,
cursorline: false,
+ cursorcolumn: false,
gutters: vec![GutterType::Diagnostics, GutterType::LineNumbers],
middle_click_paste: true,
auto_pairs: AutoPairConfig::default(),
diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs
index 85f5cc131974..8a1f8b7eceb0 100644
--- a/helix-view/src/theme.rs
+++ b/helix-view/src/theme.rs
@@ -277,6 +277,13 @@ impl Theme {
.find_map(|s| self.styles.get(s).copied())
}
+ /// Get the style of a scope, without falling back to dot separated broader
+ /// scopes. For example if `ui.text.focus` is not defined in the theme, it
+ /// will return `None`, even if `ui.text` is.
+ pub fn try_get_exact(&self, scope: &str) -> Option