summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2022-07-10 20:11:28 +0300
committerGitHub <noreply@github.com>2022-07-10 20:11:28 +0300
commit694a52bcffeffdc9e163818c3b2ac5c39e26f1ef (patch)
treea774babc1869b4c700d7df1478dbbfe5b2c3bcda /alacritty_terminal/src
parent8451b75689b44f11ec1707af7e26d915772c3972 (diff)
downloadalacritty-694a52bcffeffdc9e163818c3b2ac5c39e26f1ef.tar.gz
alacritty-694a52bcffeffdc9e163818c3b2ac5c39e26f1ef.zip
Add support for hyperlink escape sequence
This commit adds support for hyperlink escape sequence `OSC 8 ; params ; URI ST`. The configuration option responsible for those is `hints.enabled.hyperlinks`. Fixes #922.
Diffstat (limited to 'alacritty_terminal/src')
-rw-r--r--alacritty_terminal/src/ansi.rs25
-rw-r--r--alacritty_terminal/src/grid/row.rs10
-rw-r--r--alacritty_terminal/src/term/cell.rs84
-rw-r--r--alacritty_terminal/src/term/mod.rs8
4 files changed, 121 insertions, 6 deletions
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index 393f6a8c..e30ed0f5 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -12,6 +12,7 @@ use vte::{Params, ParamsIter};
use alacritty_config_derive::ConfigDeserialize;
use crate::index::{Column, Line};
+use crate::term::cell::Hyperlink;
use crate::term::color::Rgb;
/// Maximum time before a synchronized update is aborted.
@@ -457,6 +458,9 @@ pub trait Handler {
/// Report text area size in characters.
fn text_area_size_chars(&mut self) {}
+
+ /// Set hyperlink.
+ fn set_hyperlink(&mut self, _: Option<Hyperlink>) {}
}
/// Terminal cursor configuration.
@@ -1007,6 +1011,27 @@ where
}
},
+ // Hyperlink.
+ b"8" if params.len() > 2 => {
+ let link_params = params[1];
+ let uri = str::from_utf8(params[2]).unwrap_or_default();
+
+ // The OSC 8 escape sequence must be stopped when getting an empty `uri`.
+ if uri.is_empty() {
+ self.handler.set_hyperlink(None);
+ return;
+ }
+
+ // Link parameters are in format of `key1=value1:key2=value2`. Currently only key
+ // `id` is defined.
+ let id = link_params
+ .split(|&b| b == b':')
+ .find_map(|kv| kv.strip_prefix(b"id="))
+ .and_then(|kv| str::from_utf8(kv).ok());
+
+ self.handler.set_hyperlink(Some(Hyperlink::new(id, uri)));
+ },
+
// Get/set Foreground, Background, Cursor colors.
b"10" | b"11" | b"12" => {
if params.len() >= 2 {
diff --git a/alacritty_terminal/src/grid/row.rs b/alacritty_terminal/src/grid/row.rs
index f6bcb022..900d2a76 100644
--- a/alacritty_terminal/src/grid/row.rs
+++ b/alacritty_terminal/src/grid/row.rs
@@ -171,6 +171,16 @@ impl<T> Row<T> {
}
}
+impl<'a, T> IntoIterator for &'a Row<T> {
+ type IntoIter = slice::Iter<'a, T>;
+ type Item = &'a T;
+
+ #[inline]
+ fn into_iter(self) -> slice::Iter<'a, T> {
+ self.inner.iter()
+ }
+}
+
impl<'a, T> IntoIterator for &'a mut Row<T> {
type IntoIter = slice::IterMut<'a, T>;
type Item = &'a mut T;
diff --git a/alacritty_terminal/src/term/cell.rs b/alacritty_terminal/src/term/cell.rs
index 716bcf9b..d3f45e9a 100644
--- a/alacritty_terminal/src/term/cell.rs
+++ b/alacritty_terminal/src/term/cell.rs
@@ -1,3 +1,4 @@
+use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use bitflags::bitflags;
@@ -33,6 +34,53 @@ bitflags! {
}
}
+/// Counter for hyperlinks without explicit ID.
+static HYPERLINK_ID_SUFFIX: AtomicU32 = AtomicU32::new(0);
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Hyperlink {
+ inner: Arc<HyperlinkInner>,
+}
+
+impl Hyperlink {
+ pub fn new<T: ToString>(id: Option<T>, uri: T) -> Self {
+ let inner = Arc::new(HyperlinkInner::new(id, uri));
+ Self { inner }
+ }
+
+ pub fn id(&self) -> &str {
+ &self.inner.id
+ }
+
+ pub fn uri(&self) -> &str {
+ &self.inner.uri
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
+struct HyperlinkInner {
+ /// Identifier for the given hyperlink.
+ id: String,
+
+ /// Resource identifier of the hyperlink.
+ uri: String,
+}
+
+impl HyperlinkInner {
+ pub fn new<T: ToString>(id: Option<T>, uri: T) -> Self {
+ let id = match id {
+ Some(id) => id.to_string(),
+ None => {
+ let mut id = HYPERLINK_ID_SUFFIX.fetch_add(1, Ordering::Relaxed).to_string();
+ id.push_str("_alacritty");
+ id
+ },
+ };
+
+ Self { id, uri: uri.to_string() }
+ }
+}
+
/// Trait for determining if a reset should be performed.
pub trait ResetDiscriminant<T> {
/// Value based on which equality for the reset will be determined.
@@ -61,6 +109,8 @@ pub struct CellExtra {
zerowidth: Vec<char>,
underline_color: Option<Color>,
+
+ hyperlink: Option<Hyperlink>,
}
/// Content and attributes of a single cell in the terminal grid.
@@ -113,14 +163,17 @@ impl Cell {
/// Set underline color on the cell.
pub fn set_underline_color(&mut self, color: Option<Color>) {
// If we reset color and we don't have zerowidth we should drop extra storage.
- if color.is_none() && self.extra.as_ref().map_or(true, |extra| !extra.zerowidth.is_empty())
+ if color.is_none()
+ && self
+ .extra
+ .as_ref()
+ .map_or(true, |extra| !extra.zerowidth.is_empty() || extra.hyperlink.is_some())
{
self.extra = None;
- return;
+ } else {
+ let extra = self.extra.get_or_insert(Default::default());
+ Arc::make_mut(extra).underline_color = color;
}
-
- let extra = self.extra.get_or_insert(Default::default());
- Arc::make_mut(extra).underline_color = color;
}
/// Underline color stored in this cell.
@@ -128,6 +181,27 @@ impl Cell {
pub fn underline_color(&self) -> Option<Color> {
self.extra.as_ref()?.underline_color
}
+
+ /// Set hyperlink.
+ pub fn set_hyperlink(&mut self, hyperlink: Option<Hyperlink>) {
+ let should_drop = hyperlink.is_none()
+ && self.extra.as_ref().map_or(true, |extra| {
+ !extra.zerowidth.is_empty() || extra.underline_color.is_some()
+ });
+
+ if should_drop {
+ self.extra = None;
+ } else {
+ let extra = self.extra.get_or_insert(Default::default());
+ Arc::make_mut(extra).hyperlink = hyperlink;
+ }
+ }
+
+ /// Hyperlink stored in this cell.
+ #[inline]
+ pub fn hyperlink(&self) -> Option<Hyperlink> {
+ self.extra.as_ref()?.hyperlink.clone()
+ }
}
impl GridCell for Cell {
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index d600cc78..1f3269c2 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -16,7 +16,7 @@ use crate::event::{Event, EventListener};
use crate::grid::{Dimensions, Grid, GridIterator, Scroll};
use crate::index::{self, Boundary, Column, Direction, Line, Point, Side};
use crate::selection::{Selection, SelectionRange, SelectionType};
-use crate::term::cell::{Cell, Flags, LineLength};
+use crate::term::cell::{Cell, Flags, Hyperlink, LineLength};
use crate::term::color::{Colors, Rgb};
use crate::vi_mode::{ViModeCursor, ViMotion};
@@ -1679,6 +1679,12 @@ impl<T: EventListener> Handler for Term<T> {
}
}
+ #[inline]
+ fn set_hyperlink(&mut self, hyperlink: Option<Hyperlink>) {
+ trace!("Setting hyperlink: {:?}", hyperlink);
+ self.grid.cursor.template.set_hyperlink(hyperlink);
+ }
+
/// Set a terminal attribute.
#[inline]
fn terminal_attribute(&mut self, attr: Attr) {