Merge pull request #676 from nick1udwig/handle-osc-8-links

handle osc 8 links
This commit is contained in:
Jeremy Soller 2026-01-06 06:13:13 -07:00 committed by GitHub
commit dae3e28ff4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 82 additions and 6 deletions

View file

@ -246,6 +246,7 @@ pub struct Terminal {
pub url_regex_search: RegexSearch,
pub regex_matches: Vec<alacritty_terminal::term::search::Match>,
pub active_regex_match: Option<alacritty_terminal::term::search::Match>,
pub active_hyperlink_id: Option<String>,
bold_font_weight: Weight,
buffer: Arc<Buffer>,
is_focused: bool,
@ -335,6 +336,7 @@ impl Terminal {
Ok(Self {
active_regex_match: None,
active_hyperlink_id: None,
url_regex_search: url_regex_search(),
regex_matches: Vec::new(),
bold_font_weight: Weight(bold_font_weight),
@ -887,6 +889,28 @@ impl Terminal {
flags |= Flags::UNDERLINE;
}
}
if let Some(active_id) = &self.active_hyperlink_id {
let mut matches_active = indexed
.cell
.hyperlink()
.is_some_and(|link| link.id() == active_id);
if !matches_active
&& indexed.cell.flags.intersects(
Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER,
)
&& indexed.point.column.0 > 0
{
matches_active = grid[Point::new(
indexed.point.line,
Column(indexed.point.column.0 - 1),
)]
.hyperlink()
.is_some_and(|link| link.id() == active_id);
}
if matches_active {
flags |= Flags::UNDERLINE;
}
}
let metadata = Metadata::new(bg, fg)
.with_flags(flags)

View file

@ -336,11 +336,7 @@ where
let location = terminal
.viewport_to_point(TermPoint::new(row as usize, TermColumn(col as usize)));
if terminal
.regex_matches
.iter()
.any(|bounds| bounds.contains(&location))
{
if get_hyperlink(&terminal, location).is_some() {
return mouse::Interaction::Pointer;
}
}
@ -1002,7 +998,10 @@ where
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
state.modifiers = modifiers;
if modifiers.contains(Modifiers::CTRL) || terminal.active_regex_match.is_some() {
if modifiers.contains(Modifiers::CTRL)
|| terminal.active_regex_match.is_some()
|| terminal.active_hyperlink_id.is_some()
{
//Might need to update the url regex highlight,
//so we need to calculate the mouse position
let location = if let Some(p) = cursor_position.position() {
@ -1451,6 +1450,9 @@ fn get_hyperlink(
terminal: &std::sync::MutexGuard<'_, Terminal>,
location: TermPoint,
) -> Option<String> {
if let Some(link) = osc8_hyperlink_at(terminal, location) {
return Some(link.uri().to_string());
}
if let Some(match_) = terminal
.regex_matches
.iter()
@ -1463,6 +1465,32 @@ fn get_hyperlink(
}
}
fn get_hyperlink_id(
terminal: &std::sync::MutexGuard<'_, Terminal>,
location: TermPoint,
) -> Option<String> {
osc8_hyperlink_at(terminal, location).map(|link| link.id().to_string())
}
fn osc8_hyperlink_at(
terminal: &std::sync::MutexGuard<'_, Terminal>,
location: TermPoint,
) -> Option<alacritty_terminal::term::cell::Hyperlink> {
let term = terminal.term.lock();
let grid = term.grid();
let cell = &grid[location];
if let Some(link) = cell.hyperlink() {
return Some(link);
}
if cell.flags.intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER)
&& location.column.0 > 0
{
let left = TermPoint::new(location.line, TermColumn(location.column.0 - 1));
return grid[left].hyperlink();
}
None
}
fn update_active_regex_match(
terminal: &mut std::sync::MutexGuard<'_, Terminal>,
location: Option<TermPoint>,
@ -1475,6 +1503,9 @@ fn update_active_regex_match(
return;
}
let allow_hyperlink = modifiers
.map(|mods| mods.contains(Modifiers::CTRL))
.unwrap_or(false);
//Require CTRL for keyboard and mouse interaction
if let Some(modifiers) = modifiers {
if !modifiers.contains(Modifiers::CTRL) {
@ -1482,16 +1513,37 @@ fn update_active_regex_match(
terminal.active_regex_match = None;
terminal.needs_update = true;
}
if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
return;
}
} else if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
let Some(location) = location else {
if terminal.active_regex_match.is_some() {
terminal.active_regex_match = None;
terminal.needs_update = true;
}
if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
return;
};
if allow_hyperlink {
let next_hyperlink_id = get_hyperlink_id(terminal, location);
if terminal.active_hyperlink_id != next_hyperlink_id {
terminal.active_hyperlink_id = next_hyperlink_id;
terminal.needs_update = true;
}
} else if terminal.active_hyperlink_id.is_some() {
terminal.active_hyperlink_id = None;
terminal.needs_update = true;
}
if let Some(match_) = terminal
.regex_matches
.iter()