aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/url.rs
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2019-11-03 21:59:28 +0100
committerGitHub <noreply@github.com>2019-11-03 21:59:28 +0100
commitb47a88b142a8987f1d0d48db8c0db1e5f3048a76 (patch)
tree0863fb40b8e081ccc40f1437e74c49a68f4e6c59 /alacritty/src/url.rs
parentfa6ceacfa4158c568c55dff85621788ff1df4099 (diff)
downloadr-alacritty-b47a88b142a8987f1d0d48db8c0db1e5f3048a76.tar.gz
r-alacritty-b47a88b142a8987f1d0d48db8c0db1e5f3048a76.tar.bz2
r-alacritty-b47a88b142a8987f1d0d48db8c0db1e5f3048a76.zip
Fix URL highlighting
Fixes #2898. Fixes #2479.
Diffstat (limited to 'alacritty/src/url.rs')
-rw-r--r--alacritty/src/url.rs179
1 files changed, 179 insertions, 0 deletions
diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs
new file mode 100644
index 00000000..849e7a4e
--- /dev/null
+++ b/alacritty/src/url.rs
@@ -0,0 +1,179 @@
+use std::cmp::min;
+use std::mem;
+
+use glutin::event::{ElementState, ModifiersState};
+use urlocator::{UrlLocation, UrlLocator};
+
+use font::Metrics;
+
+use alacritty_terminal::index::Point;
+use alacritty_terminal::renderer::rects::{RenderLine, RenderRect};
+use alacritty_terminal::term::cell::Flags;
+use alacritty_terminal::term::{RenderableCell, RenderableCellContent, SizeInfo};
+
+use crate::config::{Config, RelaxedEq};
+use crate::event::Mouse;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Url {
+ lines: Vec<RenderLine>,
+ end_offset: u16,
+ num_cols: usize,
+}
+
+impl Url {
+ pub fn rects(&self, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
+ let end = self.end();
+ self.lines
+ .iter()
+ .filter(|line| line.start <= end)
+ .map(|line| {
+ let mut rect_line = *line;
+ rect_line.end = min(line.end, end);
+ rect_line.rects(Flags::UNDERLINE, metrics, size)
+ })
+ .flatten()
+ .collect()
+ }
+
+ pub fn start(&self) -> Point {
+ self.lines[0].start
+ }
+
+ pub fn end(&self) -> Point {
+ self.lines[self.lines.len() - 1].end.sub(self.num_cols, self.end_offset as usize)
+ }
+}
+
+pub struct Urls {
+ locator: UrlLocator,
+ urls: Vec<Url>,
+ last_point: Option<Point>,
+ state: UrlLocation,
+}
+
+impl Default for Urls {
+ fn default() -> Self {
+ Self {
+ locator: UrlLocator::new(),
+ urls: Vec::new(),
+ state: UrlLocation::Reset,
+ last_point: None,
+ }
+ }
+}
+
+impl Urls {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ // Update tracked URLs
+ pub fn update(&mut self, num_cols: usize, cell: RenderableCell) {
+ // Ignore double-width spacers to prevent reset
+ if cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
+ return;
+ }
+
+ // Convert cell to character
+ let c = match cell.inner {
+ RenderableCellContent::Chars(chars) => chars[0],
+ RenderableCellContent::Cursor(_) => return,
+ };
+
+ let point: Point = cell.into();
+
+ // Reset URL when empty cells have been skipped
+ if point != Point::default() && Some(point.sub(num_cols, 1)) != self.last_point {
+ self.reset();
+ }
+ self.last_point = Some(point);
+
+ // Advance parser
+ let last_state = mem::replace(&mut self.state, self.locator.advance(c));
+ match (self.state, last_state) {
+ (UrlLocation::Url(_length, end_offset), _) => {
+ let mut end = point;
+
+ // Extend by one cell for double-width characters
+ if cell.flags.contains(Flags::WIDE_CHAR) {
+ end.col += 1;
+
+ self.last_point = Some(end);
+ }
+
+ if let Some(url) = self.urls.last_mut() {
+ let last_index = url.lines.len() - 1;
+ let last_line = &mut url.lines[last_index];
+
+ if last_line.color == cell.fg {
+ // Update existing line
+ last_line.end = end;
+ } else {
+ // Create new line with different color
+ url.lines.push(RenderLine { start: point, end, color: cell.fg });
+ }
+
+ // Update offset
+ url.end_offset = end_offset;
+ }
+ },
+ (UrlLocation::Reset, UrlLocation::Scheme) => {
+ self.urls.pop();
+ },
+ (UrlLocation::Scheme, UrlLocation::Reset) => {
+ self.urls.push(Url {
+ lines: vec![RenderLine { start: point, end: point, color: cell.fg }],
+ end_offset: 0,
+ num_cols,
+ });
+ },
+ (UrlLocation::Scheme, _) => {
+ if let Some(url) = self.urls.last_mut() {
+ if url.lines[url.lines.len() - 1].color != cell.fg {
+ url.lines.push(RenderLine { start: point, end: point, color: cell.fg });
+ }
+ }
+ },
+ _ => (),
+ }
+
+ // Reset at un-wrapped linebreak
+ if cell.column.0 + 1 == num_cols && !cell.flags.contains(Flags::WRAPLINE) {
+ self.reset();
+ }
+ }
+
+ pub fn highlighted(
+ &self,
+ config: &Config,
+ mouse: &Mouse,
+ mods: ModifiersState,
+ mouse_mode: bool,
+ selection: bool,
+ ) -> Option<Url> {
+ // Make sure all prerequisites for highlighting are met
+ if selection
+ || (mouse_mode && !mods.shift)
+ || !mouse.inside_grid
+ || config.ui_config.mouse.url.launcher.is_none()
+ || !config.ui_config.mouse.url.mods().relaxed_eq(mods)
+ || mouse.left_button_state == ElementState::Pressed
+ {
+ return None;
+ }
+
+ for url in &self.urls {
+ if (url.start()..=url.end()).contains(&Point::new(mouse.line, mouse.column)) {
+ return Some(url.clone());
+ }
+ }
+
+ None
+ }
+
+ fn reset(&mut self) {
+ self.locator = UrlLocator::new();
+ self.state = UrlLocation::Reset;
+ }
+}