diff options
author | Christian Duerr <chrisduerr@users.noreply.github.com> | 2019-03-30 09:23:48 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-30 09:23:48 +0000 |
commit | 91aa683bcd060b2ac2f621a388a6448f564d0537 (patch) | |
tree | 924c9a0759a84be19e397eae8cc28cbd3500d553 /src/term/mod.rs | |
parent | 28636923e02a6cfac0fc950b963623cbcad5fda9 (diff) | |
download | alacritty-91aa683bcd060b2ac2f621a388a6448f564d0537.tar.gz alacritty-91aa683bcd060b2ac2f621a388a6448f564d0537.zip |
Rework URL highlighting
This completely reworks URL highlighting to fix two issues which were
caused by the original approach.
The primary issues that were not straight-forward to resolve with the
previous implementation were about handling the URL highlighted content
moving while the highlight is active.
This lead to issues with highlighting with scrolling and when the
display offset was not 0.
The new approach sticks closely to prior art done for the selection,
where the selection is tracked on the grid and updated whenever the
buffer is rotated.
The truncation of URLs was incorrectly assuming input to be just a
single codepoint wide to truncate the end of URLs with unmatching
closing parenthesis. This is now handled properly using Rust's built-in
Unicode support.
This fixes #2231.
This fixes #2225.
Diffstat (limited to 'src/term/mod.rs')
-rw-r--r-- | src/term/mod.rs | 116 |
1 files changed, 50 insertions, 66 deletions
diff --git a/src/term/mod.rs b/src/term/mod.rs index fc2b1727..89105041 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -17,7 +17,6 @@ use std::ops::{Range, Index, IndexMut, RangeInclusive}; use std::{ptr, io, mem}; use std::cmp::{min, max}; use std::time::{Duration, Instant}; -use std::iter::once; use arraydeque::ArrayDeque; use unicode_width::UnicodeWidthChar; @@ -27,7 +26,7 @@ use font::{self, Size}; use crate::ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle}; use crate::grid::{ BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll, - ViewportPosition, + ViewportPosition }; use crate::index::{self, Point, Column, Line, IndexRange, Contains, Linear}; use crate::selection::{self, Selection, Locations}; @@ -71,11 +70,11 @@ impl Search for Term { break; } - if iter.cur().col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) { + if iter.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) { break; // cut off if on new line or hit escape char } - point = iter.cur(); + point = iter.cur; } point @@ -93,7 +92,7 @@ impl Search for Term { break; } - point = iter.cur(); + point = iter.cur; if point.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) { break; // cut off if on new line or hit escape char @@ -120,7 +119,7 @@ impl Search for Term { // Find URLs let mut url_parser = UrlParser::new(); while let Some(cell) = iterb.prev() { - if (iterb.cur().col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE)) + if (iterb.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE)) || url_parser.advance_left(cell) { break; @@ -129,7 +128,7 @@ impl Search for Term { while let Some(cell) = iterf.next() { if url_parser.advance_right(cell) - || (iterf.cur().col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE)) + || (iterf.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE)) { break; } @@ -164,6 +163,7 @@ pub struct RenderableCellsIter<'a> { config: &'a Config, colors: &'a color::List, selection: Option<RangeInclusive<index::Linear>>, + url_highlight: &'a Option<RangeInclusive<index::Linear>>, cursor_cells: ArrayDeque<[Indexed<Cell>; 3]>, } @@ -173,15 +173,14 @@ impl<'a> RenderableCellsIter<'a> { /// The cursor and terminal mode are required for properly displaying the /// cursor. fn new<'b>( - grid: &'b Grid<Cell>, - cursor: &'b Point, - colors: &'b color::List, - mode: TermMode, + term: &'b Term, config: &'b Config, selection: Option<Locations>, cursor_style: CursorStyle, ) -> RenderableCellsIter<'b> { - let cursor_offset = grid.line_to_offset(cursor.line); + let grid = &term.grid; + + let cursor_offset = grid.line_to_offset(term.cursor.point.line); let inner = grid.display_iter(); let mut selection_range = None; @@ -224,8 +223,8 @@ impl<'a> RenderableCellsIter<'a> { } let cols = grid.num_cols(); - let start = Linear(start.line.0 * cols.0 + start.col.0); - let end = Linear(end.line.0 * cols.0 + end.col.0); + let start = Linear::from_point(cols, start.into()); + let end = Linear::from_point(cols, end.into()); // Update the selection selection_range = Some(RangeInclusive::new(start, end)); @@ -233,14 +232,15 @@ impl<'a> RenderableCellsIter<'a> { } RenderableCellsIter { - cursor, + cursor: &term.cursor.point, cursor_offset, grid, inner, - mode, + mode: term.mode, selection: selection_range, + url_highlight: &grid.url_highlight, config, - colors, + colors: &term.colors, cursor_cells: ArrayDeque::new(), }.initialize(cursor_style) } @@ -435,7 +435,7 @@ impl<'a> Iterator for RenderableCellsIter<'a> { fn next(&mut self) -> Option<Self::Item> { loop { // Handle cursor - let (cell, selected) = if self.cursor_offset == self.inner.offset() && + let (cell, selected, highlighted) = if self.cursor_offset == self.inner.offset() && self.inner.column() == self.cursor.col { // Cursor cell @@ -448,11 +448,11 @@ impl<'a> Iterator for RenderableCellsIter<'a> { if self.cursor_cells.is_empty() { self.inner.next(); } - (cell, false) + (cell, false, false) } else { let cell = self.inner.next()?; - let index = Linear(cell.line.0 * self.grid.num_cols().0 + cell.column.0); + let index = Linear::new(self.grid.num_cols(), cell.column, cell.line); let selected = self.selection.as_ref() .map(|range| range.contains_(index)) @@ -463,7 +463,12 @@ impl<'a> Iterator for RenderableCellsIter<'a> { continue; } - (cell, selected) + // Underline URL highlights + let highlighted = self.url_highlight.as_ref() + .map(|range| range.contains_(index)) + .unwrap_or(false); + + (cell, selected, highlighted) }; // Lookup RGB values @@ -488,14 +493,19 @@ impl<'a> Iterator for RenderableCellsIter<'a> { fg_rgb = col; } + let mut flags = cell.flags; + if highlighted { + flags.insert(Flags::UNDERLINE); + } + return Some(RenderableCell { line: cell.line, column: cell.column, - flags: cell.flags, chars: cell.chars(), fg: fg_rgb, bg: bg_rgb, bg_alpha, + flags, }) } } @@ -824,16 +834,6 @@ pub struct Term { /// Hint that Alacritty should be closed should_exit: bool, - - /// URL highlight save state - url_hover_save: Option<UrlHoverSaveState>, -} - -/// Temporary save state for restoring mouse cursor and underline after unhovering a URL. -pub struct UrlHoverSaveState { - pub mouse_cursor: MouseCursor, - pub underlined: Vec<bool>, - pub start: Point<usize>, } /// Terminal size info @@ -910,8 +910,10 @@ impl Term { self.next_title.take() } + #[inline] pub fn scroll_display(&mut self, scroll: Scroll) { self.grid.scroll_display(scroll); + self.reset_url_highlight(); self.dirty = true; } @@ -966,7 +968,6 @@ impl Term { auto_scroll: config.scrolling().auto_scroll, message_buffer, should_exit: false, - url_hover_save: None, } } @@ -1162,7 +1163,8 @@ impl Term { &self.grid } - /// Mutable access to the raw grid data structure + /// Mutable access for swapping out the grid during tests + #[cfg(test)] pub fn grid_mut(&mut self) -> &mut Grid<Cell> { &mut self.grid } @@ -1191,10 +1193,7 @@ impl Term { }; RenderableCellsIter::new( - &self.grid, - &self.cursor.point, - &self.colors, - self.mode, + &self, config, selection, cursor, @@ -1212,8 +1211,6 @@ impl Term { return; } - self.reset_url_highlight(); - let old_cols = self.grid.num_cols(); let old_lines = self.grid.num_lines(); let mut num_cols = size.cols(); @@ -1232,6 +1229,7 @@ impl Term { self.grid.selection = None; self.alt_grid.selection = None; + self.grid.url_highlight = None; // Should not allow less than 1 col, causes all sorts of checks to be required. if num_cols <= Column(1) { @@ -1381,37 +1379,22 @@ impl Term { } #[inline] - pub fn set_url_highlight(&mut self, hover_save: UrlHoverSaveState) { - self.url_hover_save = Some(hover_save); + pub fn set_url_highlight(&mut self, hl: RangeInclusive<index::Linear>) { + self.grid.url_highlight = Some(hl); } #[inline] - pub fn url_highlight_start(&self) -> Option<Point<usize>> { - self.url_hover_save.as_ref().map(|hs| hs.start) - } - - /// Remove the URL highlight and restore the previous mouse cursor and underline state. pub fn reset_url_highlight(&mut self) { - let hover_save = match self.url_hover_save.take() { - Some(hover_save) => hover_save, - _ => return, + let mouse_mode = + TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG | TermMode::MOUSE_REPORT_CLICK; + let mouse_cursor = if self.mode().intersects(mouse_mode) { + MouseCursor::Default + } else { + MouseCursor::Text }; + self.set_mouse_cursor(mouse_cursor); - // Reset the mouse cursor - self.set_mouse_cursor(hover_save.mouse_cursor); - - let last_line = self.size_info.lines().0 - 1; - let last_col = self.size_info.cols() - 1; - - // Reset the underline state after unhovering a URL - let mut iter = once(hover_save.start).chain(hover_save.start.iter(last_col, last_line)); - for underlined in &hover_save.underlined { - if let (Some(point), false) = (iter.next(), underlined) { - let cell = &mut self.grid[point.line][point.col]; - cell.flags.remove(Flags::UNDERLINE); - } - } - + self.grid.url_highlight = None; self.dirty = true; } } @@ -1961,8 +1944,9 @@ impl ansi::Handler for Term { let mut template = self.cursor.template; template.flags ^= template.flags; - // Remove active selections + // Remove active selections and URL highlights self.grid.selection = None; + self.grid.url_highlight = None; match mode { ansi::ClearMode::Below => { |