From a1e2d6a5573d967aaceeadaefa17b6a00a2e4ca4 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 1 Jan 2021 05:19:03 +0000 Subject: [PATCH] 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. --- CHANGELOG.md | 1 + alacritty.yml | 30 +++++++---- alacritty/src/display.rs | 69 +++++++++++++++++++------ alacritty/src/renderer/mod.rs | 18 +++---- alacritty_terminal/src/config/colors.rs | 7 +++ 5 files changed, 89 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a94c3a4d..ca1e5f37 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/alacritty.yml b/alacritty.yml index cb16bf7c..4a23f5c9 100644 --- a/alacritty.yml +++ b/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' diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs index 4084d639..b5c2bfa9 100644 --- a/alacritty/src/display.rs +++ b/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, + 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))))] diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index 5edcbb4a..39e53b82 100644 --- a/alacritty/src/renderer/mod.rs +++ b/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, + 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::>(); diff --git a/alacritty_terminal/src/config/colors.rs b/alacritty_terminal/src/config/colors.rs index f295da1c..88ca5057 100644 --- a/alacritty_terminal/src/config/colors.rs +++ b/alacritty_terminal/src/config/colors.rs @@ -16,6 +16,7 @@ pub struct Colors { pub dim: Option, pub indexed_colors: Vec, 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, + pub background: Option, +} + #[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct IndexedColor { pub color: Rgb,