mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-25 14:05:41 -05:00
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.
This commit is contained in:
parent
8ed72cc065
commit
a1e2d6a557
5 changed files with 89 additions and 36 deletions
|
@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Customizable keybindings for search
|
- Customizable keybindings for search
|
||||||
- History for search mode, bound to ^P/^N/Up/Down by default
|
- History for search mode, bound to ^P/^N/Up/Down by default
|
||||||
- Default binding to cancel search on Ctrl+C
|
- Default binding to cancel search on Ctrl+C
|
||||||
|
- History position indicator for search and vi mode
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -211,16 +211,6 @@
|
||||||
# text: CellBackground
|
# text: CellBackground
|
||||||
# cursor: CellForeground
|
# 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
|
# Search colors
|
||||||
#
|
#
|
||||||
# Colors used for the search bar and match highlighting.
|
# Colors used for the search bar and match highlighting.
|
||||||
|
@ -238,6 +228,26 @@
|
||||||
# background: '#c5c8c6'
|
# background: '#c5c8c6'
|
||||||
# foreground: '#1d1f21'
|
# 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 colors
|
||||||
#normal:
|
#normal:
|
||||||
# black: '#1d1f21'
|
# black: '#1d1f21'
|
||||||
|
|
|
@ -23,10 +23,10 @@ use wayland_client::{Display as WaylandDisplay, EventQueue};
|
||||||
use crossfont::{self, Rasterize, Rasterizer};
|
use crossfont::{self, Rasterize, Rasterizer};
|
||||||
|
|
||||||
use alacritty_terminal::event::{EventListener, OnResize};
|
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::selection::Selection;
|
||||||
use alacritty_terminal::term::{SizeInfo, Term, TermMode};
|
use alacritty_terminal::term::{SizeInfo, Term, TermMode, MIN_COLS, MIN_SCREEN_LINES};
|
||||||
use alacritty_terminal::term::{MIN_COLS, MIN_SCREEN_LINES};
|
|
||||||
|
|
||||||
use crate::config::font::Font;
|
use crate::config::font::Font;
|
||||||
use crate::config::window::Dimensions;
|
use crate::config::window::Dimensions;
|
||||||
|
@ -460,21 +460,19 @@ impl Display {
|
||||||
let cursor = content.cursor();
|
let cursor = content.cursor();
|
||||||
|
|
||||||
let visual_bell_intensity = terminal.visual_bell.intensity();
|
let visual_bell_intensity = terminal.visual_bell.intensity();
|
||||||
|
let display_offset = terminal.grid().display_offset();
|
||||||
let background_color = terminal.background_color();
|
let background_color = terminal.background_color();
|
||||||
let cursor_point = terminal.grid().cursor.point;
|
let cursor_point = terminal.grid().cursor.point;
|
||||||
|
let total_lines = terminal.grid().total_lines();
|
||||||
let metrics = self.glyph_cache.font_metrics();
|
let metrics = self.glyph_cache.font_metrics();
|
||||||
let glyph_cache = &mut self.glyph_cache;
|
|
||||||
let size_info = self.size_info;
|
let size_info = self.size_info;
|
||||||
|
|
||||||
let selection = !terminal.selection.as_ref().map(Selection::is_empty).unwrap_or(true);
|
let selection = !terminal.selection.as_ref().map(Selection::is_empty).unwrap_or(true);
|
||||||
let mouse_mode = terminal.mode().intersects(TermMode::MOUSE_MODE)
|
let mouse_mode = terminal.mode().intersects(TermMode::MOUSE_MODE)
|
||||||
&& !terminal.mode().contains(TermMode::VI);
|
&& !terminal.mode().contains(TermMode::VI);
|
||||||
|
|
||||||
let vi_mode_cursor = if terminal.mode().contains(TermMode::VI) {
|
let vi_mode = terminal.mode().contains(TermMode::VI);
|
||||||
Some(terminal.vi_mode_cursor)
|
let vi_mode_cursor = if vi_mode { Some(terminal.vi_mode_cursor) } else { None };
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Drop terminal as early as possible to free lock.
|
// Drop terminal as early as possible to free lock.
|
||||||
drop(terminal);
|
drop(terminal);
|
||||||
|
@ -490,6 +488,7 @@ impl Display {
|
||||||
{
|
{
|
||||||
let _sampler = self.meter.sampler();
|
let _sampler = self.meter.sampler();
|
||||||
|
|
||||||
|
let glyph_cache = &mut self.glyph_cache;
|
||||||
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
||||||
// Iterate over all non-empty cells in the grid.
|
// Iterate over all non-empty cells in the grid.
|
||||||
for mut cell in grid_cells {
|
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(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));
|
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.
|
// Push the cursor rects for rendering.
|
||||||
|
@ -574,13 +581,13 @@ impl Display {
|
||||||
let start_line = size_info.screen_lines() + search_offset;
|
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 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::Error => config.colors.normal.red,
|
||||||
MessageType::Warning => config.colors.normal.yellow,
|
MessageType::Warning => config.colors.normal.yellow,
|
||||||
};
|
};
|
||||||
|
|
||||||
let message_bar_rect =
|
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.
|
// Push message_bar in the end, so it'll be above all other content.
|
||||||
rects.push(message_bar_rect);
|
rects.push(message_bar_rect);
|
||||||
|
@ -589,10 +596,12 @@ impl Display {
|
||||||
self.renderer.draw_rects(&size_info, rects);
|
self.renderer.draw_rects(&size_info, rects);
|
||||||
|
|
||||||
// Relay messages to the user.
|
// Relay messages to the user.
|
||||||
|
let glyph_cache = &mut self.glyph_cache;
|
||||||
let fg = config.colors.primary.background;
|
let fg = config.colors.primary.background;
|
||||||
for (i, message_text) in text.iter().enumerate() {
|
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| {
|
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 {
|
} else {
|
||||||
|
@ -681,10 +690,12 @@ impl Display {
|
||||||
// Assure text length is at least num_cols.
|
// Assure text length is at least num_cols.
|
||||||
let text = format!("{:<1$}", text, 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 fg = config.colors.search_bar_foreground();
|
||||||
let bg = config.colors.search_bar_background();
|
let bg = config.colors.search_bar_background();
|
||||||
|
|
||||||
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
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 {
|
if !config.ui_config.debug.render_timer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let glyph_cache = &mut self.glyph_cache;
|
let glyph_cache = &mut self.glyph_cache;
|
||||||
|
|
||||||
let timing = format!("{:.3} usec", self.meter.average());
|
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 fg = config.colors.primary.background;
|
||||||
let bg = config.colors.normal.red;
|
let bg = config.colors.normal.red;
|
||||||
|
|
||||||
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
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.
|
/// Requst a new frame for a window on Wayland.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||||
|
|
|
@ -14,7 +14,7 @@ use fnv::FnvHasher;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use unicode_width::UnicodeWidthChar;
|
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::cell::Flags;
|
||||||
use alacritty_terminal::term::color::Rgb;
|
use alacritty_terminal::term::color::Rgb;
|
||||||
use alacritty_terminal::term::render::RenderableCell;
|
use alacritty_terminal::term::render::RenderableCell;
|
||||||
|
@ -820,25 +820,23 @@ impl<'a> RenderApi<'a> {
|
||||||
pub fn render_string(
|
pub fn render_string(
|
||||||
&mut self,
|
&mut self,
|
||||||
glyph_cache: &mut GlyphCache,
|
glyph_cache: &mut GlyphCache,
|
||||||
line: Line,
|
point: Point,
|
||||||
string: &str,
|
|
||||||
fg: Rgb,
|
fg: Rgb,
|
||||||
bg: Option<Rgb>,
|
bg: Rgb,
|
||||||
|
string: &str,
|
||||||
) {
|
) {
|
||||||
let bg_alpha = bg.map(|_| 1.0).unwrap_or(0.0);
|
|
||||||
|
|
||||||
let cells = string
|
let cells = string
|
||||||
.chars()
|
.chars()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, character)| RenderableCell {
|
.map(|(i, character)| RenderableCell {
|
||||||
line,
|
line: point.line,
|
||||||
column: Column(i),
|
column: point.col + i,
|
||||||
character,
|
character,
|
||||||
zerowidth: None,
|
zerowidth: None,
|
||||||
flags: Flags::empty(),
|
flags: Flags::empty(),
|
||||||
bg_alpha,
|
bg_alpha: 1.0,
|
||||||
fg,
|
fg,
|
||||||
bg: bg.unwrap_or(Rgb { r: 0, g: 0, b: 0 }),
|
bg,
|
||||||
is_match: false,
|
is_match: false,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub struct Colors {
|
||||||
pub dim: Option<DimColors>,
|
pub dim: Option<DimColors>,
|
||||||
pub indexed_colors: Vec<IndexedColor>,
|
pub indexed_colors: Vec<IndexedColor>,
|
||||||
pub search: SearchColors,
|
pub search: SearchColors,
|
||||||
|
pub line_indicator: LineIndicatorColors,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Colors {
|
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)]
|
#[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||||
pub struct IndexedColor {
|
pub struct IndexedColor {
|
||||||
pub color: Rgb,
|
pub color: Rgb,
|
||||||
|
|
Loading…
Reference in a new issue