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