Browse Source

Add vi/search line indicator

This adds a new visual indicator which shows the position in history of
either the display offset during search, or the vi mode cursor.

To make it as unintrusive as possible, the overlay is hidden whenever
the vi mode cursor collides with its position.

Fixes #3984.
Christian Duerr 3 months ago
parent
commit
a1e2d6a557
5 changed files with 89 additions and 36 deletions
  1. 1 0
      CHANGELOG.md
  2. 20 10
      alacritty.yml
  3. 53 16
      alacritty/src/display.rs
  4. 8 10
      alacritty/src/renderer/mod.rs
  5. 7 0
      alacritty_terminal/src/config/colors.rs

+ 1 - 0
CHANGELOG.md

@@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Customizable keybindings for search
 - History for search mode, bound to ^P/^N/Up/Down by default
 - Default binding to cancel search on Ctrl+C
+- History position indicator for search and vi mode
 
 ### Changed
 

+ 20 - 10
alacritty.yml

@@ -211,16 +211,6 @@
   #  text: CellBackground
   #  cursor: CellForeground
 
-  # Selection colors
-  #
-  # Colors which should be used to draw the selection area.
-  #
-  # Allowed values are CellForeground and CellBackground, which reference the
-  # affected cell, or hexadecimal colors like #ff00ff.
-  #selection:
-  #  text: CellBackground
-  #  background: CellForeground
-
   # Search colors
   #
   # Colors used for the search bar and match highlighting.
@@ -238,6 +228,26 @@
     #  background: '#c5c8c6'
     #  foreground: '#1d1f21'
 
+  # Line indicator
+  #
+  # Color used for the indicator displaying the position in history during
+  # search and vi mode.
+  #
+  # By default, these will use the opposing primary color.
+  #line_indicator:
+  #  foreground: None
+  #  background: None
+
+  # Selection colors
+  #
+  # Colors which should be used to draw the selection area.
+  #
+  # Allowed values are CellForeground and CellBackground, which reference the
+  # affected cell, or hexadecimal colors like #ff00ff.
+  #selection:
+  #  text: CellBackground
+  #  background: CellForeground
+
   # Normal colors
   #normal:
   #  black:   '#1d1f21'

+ 53 - 16
alacritty/src/display.rs

@@ -23,10 +23,10 @@ use wayland_client::{Display as WaylandDisplay, EventQueue};
 use crossfont::{self, Rasterize, Rasterizer};
 
 use alacritty_terminal::event::{EventListener, OnResize};
-use alacritty_terminal::index::{Column, Direction, Point};
+use alacritty_terminal::grid::Dimensions as _;
+use alacritty_terminal::index::{Column, Direction, Line, Point};
 use alacritty_terminal::selection::Selection;
-use alacritty_terminal::term::{SizeInfo, Term, TermMode};
-use alacritty_terminal::term::{MIN_COLS, MIN_SCREEN_LINES};
+use alacritty_terminal::term::{SizeInfo, Term, TermMode, MIN_COLS, MIN_SCREEN_LINES};
 
 use crate::config::font::Font;
 use crate::config::window::Dimensions;
@@ -460,21 +460,19 @@ impl Display {
         let cursor = content.cursor();
 
         let visual_bell_intensity = terminal.visual_bell.intensity();
+        let display_offset = terminal.grid().display_offset();
         let background_color = terminal.background_color();
         let cursor_point = terminal.grid().cursor.point;
+        let total_lines = terminal.grid().total_lines();
         let metrics = self.glyph_cache.font_metrics();
-        let glyph_cache = &mut self.glyph_cache;
         let size_info = self.size_info;
 
         let selection = !terminal.selection.as_ref().map(Selection::is_empty).unwrap_or(true);
         let mouse_mode = terminal.mode().intersects(TermMode::MOUSE_MODE)
             && !terminal.mode().contains(TermMode::VI);
 
-        let vi_mode_cursor = if terminal.mode().contains(TermMode::VI) {
-            Some(terminal.vi_mode_cursor)
-        } else {
-            None
-        };
+        let vi_mode = terminal.mode().contains(TermMode::VI);
+        let vi_mode_cursor = if vi_mode { Some(terminal.vi_mode_cursor) } else { None };
 
         // Drop terminal as early as possible to free lock.
         drop(terminal);
@@ -490,6 +488,7 @@ impl Display {
         {
             let _sampler = self.meter.sampler();
 
+            let glyph_cache = &mut self.glyph_cache;
             self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
                 // Iterate over all non-empty cells in the grid.
                 for mut cell in grid_cells {
@@ -539,11 +538,19 @@ impl Display {
             }
         }
 
-        // Highlight URLs at the vi mode cursor position.
         if let Some(vi_mode_cursor) = vi_mode_cursor {
-            if let Some(url) = self.urls.find_at(vi_mode_cursor.point) {
+            // Highlight URLs at the vi mode cursor position.
+            let vi_mode_point = vi_mode_cursor.point;
+            if let Some(url) = self.urls.find_at(vi_mode_point) {
                 rects.append(&mut url.rects(&metrics, &size_info));
             }
+
+            // Indicate vi mode by showing the cursor's position in the top right corner.
+            let line = size_info.screen_lines() + display_offset - vi_mode_point.line - 1;
+            self.draw_line_indicator(config, &size_info, total_lines, Some(vi_mode_point), line.0);
+        } else if search_active {
+            // Show current display offset in vi-less search to indicate match position.
+            self.draw_line_indicator(config, &size_info, total_lines, None, display_offset);
         }
 
         // Push the cursor rects for rendering.
@@ -574,13 +581,13 @@ impl Display {
             let start_line = size_info.screen_lines() + search_offset;
             let y = size_info.cell_height().mul_add(start_line.0 as f32, size_info.padding_y());
 
-            let color = match message.ty() {
+            let bg = match message.ty() {
                 MessageType::Error => config.colors.normal.red,
                 MessageType::Warning => config.colors.normal.yellow,
             };
 
             let message_bar_rect =
-                RenderRect::new(0., y, size_info.width(), size_info.height() - y, color, 1.);
+                RenderRect::new(0., y, size_info.width(), size_info.height() - y, bg, 1.);
 
             // Push message_bar in the end, so it'll be above all other content.
             rects.push(message_bar_rect);
@@ -589,10 +596,12 @@ impl Display {
             self.renderer.draw_rects(&size_info, rects);
 
             // Relay messages to the user.
+            let glyph_cache = &mut self.glyph_cache;
             let fg = config.colors.primary.background;
             for (i, message_text) in text.iter().enumerate() {
+                let point = Point::new(start_line + i, Column(0));
                 self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
-                    api.render_string(glyph_cache, start_line + i, &message_text, fg, None);
+                    api.render_string(glyph_cache, point, fg, bg, &message_text);
                 });
             }
         } else {
@@ -681,10 +690,12 @@ impl Display {
         // Assure text length is at least num_cols.
         let text = format!("{:<1$}", text, num_cols);
 
+        let point = Point::new(size_info.screen_lines(), Column(0));
         let fg = config.colors.search_bar_foreground();
         let bg = config.colors.search_bar_background();
+
         self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
-            api.render_string(glyph_cache, size_info.screen_lines(), &text, fg, Some(bg));
+            api.render_string(glyph_cache, point, fg, bg, &text);
         });
     }
 
@@ -693,17 +704,43 @@ impl Display {
         if !config.ui_config.debug.render_timer {
             return;
         }
+
         let glyph_cache = &mut self.glyph_cache;
 
         let timing = format!("{:.3} usec", self.meter.average());
+        let point = Point::new(size_info.screen_lines() - 2, Column(0));
         let fg = config.colors.primary.background;
         let bg = config.colors.normal.red;
 
         self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
-            api.render_string(glyph_cache, size_info.screen_lines() - 2, &timing[..], fg, Some(bg));
+            api.render_string(glyph_cache, point, fg, bg, &timing);
         });
     }
 
+    /// Draw an indicator for the position of a line in history.
+    fn draw_line_indicator(
+        &mut self,
+        config: &Config,
+        size_info: &SizeInfo,
+        total_lines: usize,
+        vi_mode_point: Option<Point>,
+        line: usize,
+    ) {
+        let text = format!("[{}/{}]", line, total_lines - 1);
+        let column = Column(size_info.cols().0.saturating_sub(text.len()));
+        let colors = &config.colors;
+        let fg = colors.line_indicator.foreground.unwrap_or(colors.primary.background);
+        let bg = colors.line_indicator.background.unwrap_or(colors.primary.foreground);
+
+        // Do not render anything if it would obscure the vi mode cursor.
+        if vi_mode_point.map_or(true, |point| point.line.0 != 0 || point.col < column) {
+            let glyph_cache = &mut self.glyph_cache;
+            self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
+                api.render_string(glyph_cache, Point::new(Line(0), column), fg, bg, &text);
+            });
+        }
+    }
+
     /// Requst a new frame for a window on Wayland.
     #[inline]
     #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]

+ 8 - 10
alacritty/src/renderer/mod.rs

@@ -14,7 +14,7 @@ use fnv::FnvHasher;
 use log::{error, info};
 use unicode_width::UnicodeWidthChar;
 
-use alacritty_terminal::index::{Column, Line};
+use alacritty_terminal::index::Point;
 use alacritty_terminal::term::cell::Flags;
 use alacritty_terminal::term::color::Rgb;
 use alacritty_terminal::term::render::RenderableCell;
@@ -820,25 +820,23 @@ impl<'a> RenderApi<'a> {
     pub fn render_string(
         &mut self,
         glyph_cache: &mut GlyphCache,
-        line: Line,
-        string: &str,
+        point: Point,
         fg: Rgb,
-        bg: Option<Rgb>,
+        bg: Rgb,
+        string: &str,
     ) {
-        let bg_alpha = bg.map(|_| 1.0).unwrap_or(0.0);
-
         let cells = string
             .chars()
             .enumerate()
             .map(|(i, character)| RenderableCell {
-                line,
-                column: Column(i),
+                line: point.line,
+                column: point.col + i,
                 character,
                 zerowidth: None,
                 flags: Flags::empty(),
-                bg_alpha,
+                bg_alpha: 1.0,
                 fg,
-                bg: bg.unwrap_or(Rgb { r: 0, g: 0, b: 0 }),
+                bg,
                 is_match: false,
             })
             .collect::<Vec<_>>();

+ 7 - 0
alacritty_terminal/src/config/colors.rs

@@ -16,6 +16,7 @@ pub struct Colors {
     pub dim: Option<DimColors>,
     pub indexed_colors: Vec<IndexedColor>,
     pub search: SearchColors,
+    pub line_indicator: LineIndicatorColors,
 }
 
 impl Colors {
@@ -28,6 +29,12 @@ impl Colors {
     }
 }
 
+#[derive(ConfigDeserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
+pub struct LineIndicatorColors {
+    pub foreground: Option<Rgb>,
+    pub background: Option<Rgb>,
+}
+
 #[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
 pub struct IndexedColor {
     pub color: Rgb,