Underline hint matches during selection

This patch underlines the full regex hint match while the keyboard hint
selection is in process.

While it would be possible to color the entire match, this would only
introduce unnecessary configuration options and be too noisy. The
underline matches the mouse highlighting and has a less drastic visual
impact.

Closes #6178.
This commit is contained in:
Christian Duerr 2023-09-22 19:49:52 +02:00 committed by GitHub
parent e35e5ad14f
commit a58fb39b68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 16 deletions

View File

@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- The double click threshold was raised to `400ms`
- OSC 52 paste ability is now **disabled by default**; use `terminal.osc52` to adjust it
- Apply `colors.transparent_background_colors` for selections, hints, and search matches
- Underline full hint during keyboard selection
### Fixed

View File

@ -4,7 +4,7 @@ use std::{cmp, mem};
use alacritty_terminal::ansi::{Color, CursorShape, NamedColor};
use alacritty_terminal::event::EventListener;
use alacritty_terminal::grid::Indexed;
use alacritty_terminal::grid::{Dimensions, Indexed};
use alacritty_terminal::index::{Column, Line, Point};
use alacritty_terminal::selection::SelectionRange;
use alacritty_terminal::term::cell::{Cell, Flags, Hyperlink};
@ -15,7 +15,7 @@ use alacritty_terminal::term::{self, RenderableContent as TerminalContent, Term,
use crate::config::UiConfig;
use crate::display::color::{List, DIM_FACTOR};
use crate::display::hint::{self, HintState};
use crate::display::Display;
use crate::display::{Display, SizeInfo};
use crate::event::SearchState;
/// Minimum contrast between a fixed cursor color and the cell's background.
@ -34,6 +34,7 @@ pub struct RenderableContent<'a> {
config: &'a UiConfig,
colors: &'a List,
focused_match: Option<&'a Match>,
size: &'a SizeInfo,
}
impl<'a> RenderableContent<'a> {
@ -74,6 +75,7 @@ impl<'a> RenderableContent<'a> {
Self {
colors: &display.colors,
size: &display.size_info,
cursor: RenderableCursor::new_hidden(),
terminal_content,
focused_match,
@ -223,18 +225,27 @@ impl RenderableCell {
let viewport_start = Point::new(Line(-(display_offset as i32)), Column(0));
let colors = &content.config.colors;
let mut character = cell.c;
let mut flags = cell.flags;
if let Some((c, is_first)) =
content.hint.as_mut().and_then(|hint| hint.advance(viewport_start, cell.point))
let num_cols = content.size.columns();
if let Some((c, is_first)) = content
.hint
.as_mut()
.and_then(|hint| hint.advance(viewport_start, num_cols, cell.point))
{
let (config_fg, config_bg) = if is_first {
(colors.hints.start.foreground, colors.hints.start.background)
if is_first {
let (config_fg, config_bg) =
(colors.hints.start.foreground, colors.hints.start.background);
Self::compute_cell_rgb(&mut fg, &mut bg, &mut bg_alpha, config_fg, config_bg);
} else if c.is_some() {
let (config_fg, config_bg) =
(colors.hints.end.foreground, colors.hints.end.background);
Self::compute_cell_rgb(&mut fg, &mut bg, &mut bg_alpha, config_fg, config_bg);
} else {
(colors.hints.end.foreground, colors.hints.end.background)
};
Self::compute_cell_rgb(&mut fg, &mut bg, &mut bg_alpha, config_fg, config_bg);
flags.insert(Flags::UNDERLINE);
}
character = c;
character = c.unwrap_or(character);
} else if is_selected {
let config_fg = colors.selection.foreground;
let config_bg = colors.selection.background;
@ -265,7 +276,6 @@ impl RenderableCell {
let cell_point = cell.point;
let point = term::point_to_viewport(display_offset, cell_point).unwrap();
let flags = cell.flags;
let underline = cell
.underline_color()
.map_or(fg, |underline| Self::compute_fg_rgb(content, underline, flags));
@ -440,7 +450,15 @@ impl<'a> Hint<'a> {
/// this position will be returned.
///
/// The tuple's [`bool`] will be `true` when the character is the first for this hint.
fn advance(&mut self, viewport_start: Point, point: Point) -> Option<(char, bool)> {
///
/// The tuple's [`Option<char>`] will be [`None`] when the point is part of the match, but not
/// part of the hint label.
fn advance(
&mut self,
viewport_start: Point,
num_cols: usize,
point: Point,
) -> Option<(Option<char>, bool)> {
// Check if we're within a match at all.
if !self.matches.advance(point) {
return None;
@ -450,15 +468,22 @@ impl<'a> Hint<'a> {
let start = self
.matches
.get(self.matches.index)
.map(|bounds| cmp::max(*bounds.start(), viewport_start))
.filter(|start| start.line == point.line)?;
.map(|bounds| cmp::max(*bounds.start(), viewport_start))?;
// Position within the hint label.
let label_position = point.column.0 - start.column.0;
let line_delta = point.line.0 - start.line.0;
let col_delta = point.column.0 as i32 - start.column.0 as i32;
let label_position = usize::try_from(line_delta * num_cols as i32 + col_delta).unwrap_or(0);
let is_first = label_position == 0;
// Hint label character.
self.labels[self.matches.index].get(label_position).copied().map(|c| (c, is_first))
let hint_char = self.labels[self.matches.index]
.get(label_position)
.copied()
.map(|c| (Some(c), is_first))
.unwrap_or((None, false));
Some(hint_char)
}
}