Fix hint Select action for hyperlink escape

This fixes an issue where the `Select` action for hyperlink escape text
would select the entire line, instead of selecting only the hyperlink
itself.

It also changes the way hyperlinks with the same ID are highlighted,
removing the restriction of being on consecutive lines and instead
highlighting all visible cells that correspond to the matching
hyperlink.

Closes #7766.
This commit is contained in:
Christian Duerr 2024-03-12 12:15:00 +01:00 committed by GitHub
parent 41d2f1df45
commit 275726f784
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 22 additions and 42 deletions

View file

@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mouse cursor not changing on Wayland when cursor theme uses legacy cursor icon names - Mouse cursor not changing on Wayland when cursor theme uses legacy cursor icon names
- Config keys are available under proper names - Config keys are available under proper names
- Build failure when compiling with x11 feature on NetBSD - Build failure when compiling with x11 feature on NetBSD
- Hint `Select` action selecting the entire line for URL escapes
### Changed ### Changed

View file

@ -5,7 +5,7 @@ use std::rc::Rc;
use alacritty_config::SerdeReplace; use alacritty_config::SerdeReplace;
use clap::{ArgAction, Args, Parser, Subcommand, ValueHint}; use clap::{ArgAction, Args, Parser, Subcommand, ValueHint};
use log::{self, error, LevelFilter}; use log::{error, LevelFilter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use toml::Value; use toml::Value;

View file

@ -10,7 +10,7 @@ use alacritty_terminal::term::Config as TermConfig;
use alacritty_terminal::tty::{Options as PtyOptions, Shell}; use alacritty_terminal::tty::{Options as PtyOptions, Shell};
use log::{error, warn}; use log::{error, warn};
use serde::de::{Error as SerdeError, MapAccess, Visitor}; use serde::de::{Error as SerdeError, MapAccess, Visitor};
use serde::{self, Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use winit::keyboard::{Key, ModifiersState}; use winit::keyboard::{Key, ModifiersState};

View file

@ -204,7 +204,8 @@ pub struct HintMatch {
impl HintMatch { impl HintMatch {
#[inline] #[inline]
pub fn should_highlight(&self, point: Point, pointed_hyperlink: Option<&Hyperlink>) -> bool { pub fn should_highlight(&self, point: Point, pointed_hyperlink: Option<&Hyperlink>) -> bool {
self.bounds.contains(&point) && self.hyperlink.as_ref() == pointed_hyperlink self.hyperlink.as_ref() == pointed_hyperlink
&& (self.hyperlink.is_some() || self.bounds.contains(&point))
} }
#[inline] #[inline]
@ -400,49 +401,30 @@ pub fn highlighted_at<T>(
} }
/// Retrieve the hyperlink with its range, if there is one at the specified point. /// Retrieve the hyperlink with its range, if there is one at the specified point.
///
/// This will only return contiguous cells, even if another hyperlink with the same ID exists.
fn hyperlink_at<T>(term: &Term<T>, point: Point) -> Option<(Hyperlink, Match)> { fn hyperlink_at<T>(term: &Term<T>, point: Point) -> Option<(Hyperlink, Match)> {
let hyperlink = term.grid()[point].hyperlink()?; let hyperlink = term.grid()[point].hyperlink()?;
let viewport_start = Line(-(term.grid().display_offset() as i32));
let viewport_end = viewport_start + term.bottommost_line();
let mut match_start = Point::new(point.line, Column(0));
let mut match_end = Point::new(point.line, Column(term.columns() - 1));
let grid = term.grid(); let grid = term.grid();
// Find adjacent lines that have the same `hyperlink`. The end purpose to highlight hyperlinks let mut match_end = point;
// that span across multiple lines or not directly attached to each other. for cell in grid.iter_from(point) {
if cell.hyperlink().map_or(false, |link| link == hyperlink) {
// Find the closest to the viewport start adjacent line. match_end = cell.point;
while match_start.line > viewport_start { } else {
let next_line = match_start.line - 1i32;
// Iterate over all the cells in the grid's line and check if any of those cells contains
// the hyperlink we've found at original `point`.
let line_contains_hyperlink = grid[next_line]
.into_iter()
.any(|cell| cell.hyperlink().map_or(false, |h| h == hyperlink));
// There's no hyperlink on the next line, break.
if !line_contains_hyperlink {
break; break;
} }
match_start.line = next_line;
} }
// Ditto for the end. let mut match_start = point;
while match_end.line < viewport_end { let mut iter = grid.iter_from(point);
let next_line = match_end.line + 1i32; while let Some(cell) = iter.prev() {
if cell.hyperlink().map_or(false, |link| link == hyperlink) {
let line_contains_hyperlink = grid[next_line] match_start = cell.point;
.into_iter() } else {
.any(|cell| cell.hyperlink().map_or(false, |h| h == hyperlink));
if !line_contains_hyperlink {
break; break;
} }
match_end.line = next_line;
} }
Some((hyperlink, match_start..=match_end)) Some((hyperlink, match_start..=match_end))

View file

@ -20,7 +20,7 @@ use winit::dpi::PhysicalSize;
use winit::keyboard::ModifiersState; use winit::keyboard::ModifiersState;
use winit::window::CursorIcon; use winit::window::CursorIcon;
use crossfont::{self, Rasterize, Rasterizer, Size as FontSize}; use crossfont::{Rasterize, Rasterizer, Size as FontSize};
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use alacritty_terminal::event::{EventListener, OnResize, WindowSize}; use alacritty_terminal::event::{EventListener, OnResize, WindowSize};

View file

@ -12,7 +12,7 @@ use std::sync::{Arc, Mutex, OnceLock};
use std::time::Instant; use std::time::Instant;
use std::{env, process}; use std::{env, process};
use log::{self, Level, LevelFilter}; use log::{Level, LevelFilter};
use winit::event_loop::EventLoopProxy; use winit::event_loop::EventLoopProxy;
use crate::cli::Options; use crate::cli::Options;

View file

@ -5,7 +5,6 @@ use std::collections::VecDeque;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::fs::File; use std::fs::File;
use std::io::{self, ErrorKind, Read, Write}; use std::io::{self, ErrorKind, Read, Write};
use std::marker::Send;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
use std::sync::Arc; use std::sync::Arc;

View file

@ -1,4 +1,4 @@
use std::cmp::{max, PartialEq}; use std::cmp::max;
use std::mem; use std::mem;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};

View file

@ -2389,10 +2389,8 @@ pub mod test {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use unicode_width::UnicodeWidthChar;
use crate::event::VoidListener; use crate::event::VoidListener;
use crate::index::Column;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TermSize { pub struct TermSize {

View file

@ -11,7 +11,7 @@ use std::process::{Child, Command, Stdio};
use std::sync::Arc; use std::sync::Arc;
use std::{env, ptr}; use std::{env, ptr};
use libc::{self, c_int, TIOCSCTTY}; use libc::{c_int, TIOCSCTTY};
use log::error; use log::error;
use polling::{Event, PollMode, Poller}; use polling::{Event, PollMode, Poller};
use rustix_openpty::openpty; use rustix_openpty::openpty;