aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/event.rs1
-rw-r--r--src/grid/mod.rs57
-rw-r--r--src/grid/tests.rs12
-rw-r--r--src/index.rs73
-rw-r--r--src/input.rs59
-rw-r--r--src/term/mod.rs116
-rw-r--r--src/url.rs42
8 files changed, 146 insertions, 215 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 499f612a..a94b5305 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Parsing issues with URLs starting in the first or ending in the last column
- URLs stopping at double-width characters
- Fix `start_maximized` option on X11
+- Error when parsing URLs ending with Unicode outside of the ascii range
## Version 0.2.9
diff --git a/src/event.rs b/src/event.rs
index 121c0c42..f7d9f225 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -432,6 +432,7 @@ impl<N: Notify> Processor<N> {
processor.ctx.terminal.dirty = true;
processor.ctx.terminal.next_is_urgent = Some(false);
} else {
+ processor.ctx.terminal.reset_url_highlight();
processor.ctx.terminal.dirty = true;
*hide_mouse = false;
}
diff --git a/src/grid/mod.rs b/src/grid/mod.rs
index 272ea340..a1903535 100644
--- a/src/grid/mod.rs
+++ b/src/grid/mod.rs
@@ -15,9 +15,9 @@
//! A specialized 2d grid implementation optimized for use in a terminal.
use std::cmp::{min, max, Ordering};
-use std::ops::{Deref, Range, Index, IndexMut, RangeTo, RangeFrom, RangeFull};
+use std::ops::{Deref, Range, Index, IndexMut, RangeTo, RangeFrom, RangeFull, RangeInclusive};
-use crate::index::{self, Point, Line, Column, IndexRange, PointIterator};
+use crate::index::{self, Point, Line, Column, IndexRange};
use crate::selection::Selection;
mod row;
@@ -60,7 +60,8 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
self.lines.eq(&other.lines) &&
self.display_offset.eq(&other.display_offset) &&
self.scroll_limit.eq(&other.scroll_limit) &&
- self.selection.eq(&other.selection)
+ self.selection.eq(&other.selection) &&
+ self.url_highlight.eq(&other.url_highlight)
}
}
@@ -103,6 +104,10 @@ pub struct Grid<T> {
#[serde(default)]
max_scroll_limit: usize,
+
+ /// Range for URL hover highlights
+ #[serde(default)]
+ pub url_highlight: Option<RangeInclusive<index::Linear>>,
}
#[derive(Copy, Clone)]
@@ -132,6 +137,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
scroll_limit: 0,
selection: None,
max_scroll_limit: scrollback,
+ url_highlight: None,
}
}
@@ -387,6 +393,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
let prev = self.lines;
self.selection = None;
+ self.url_highlight = None;
self.raw.rotate(*prev as isize - *target as isize);
self.raw.shrink_visible_lines(target);
self.lines = target;
@@ -422,6 +429,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
if let Some(ref mut selection) = self.selection {
selection.rotate(-(*positions as isize));
}
+ self.url_highlight = None;
self.decrease_scroll_limit(*positions);
@@ -473,6 +481,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
if let Some(ref mut selection) = self.selection {
selection.rotate(*positions as isize);
}
+ self.url_highlight = None;
// // This next loop swaps "fixed" lines outside of a scroll region
// // back into place after the rotation. The work is done in buffer-
@@ -517,6 +526,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
self.display_offset = 0;
self.selection = None;
+ self.url_highlight = None;
}
}
@@ -573,7 +583,7 @@ impl<T> Grid<T> {
pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> {
GridIterator {
grid: self,
- point_iter: point.iter(self.num_cols() - 1, self.len() - 1),
+ cur: point,
}
}
@@ -589,27 +599,50 @@ impl<T> Grid<T> {
}
pub struct GridIterator<'a, T> {
- point_iter: PointIterator<usize>,
+ /// Immutable grid reference
grid: &'a Grid<T>,
-}
-impl<'a, T> GridIterator<'a, T> {
- pub fn cur(&self) -> Point<usize> {
- self.point_iter.cur
- }
+ /// Current position of the iterator within the grid.
+ pub cur: Point<usize>,
}
impl<'a, T> Iterator for GridIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
- self.point_iter.next().map(|p| &self.grid[p.line][p.col])
+ let last_col = self.grid.num_cols() - Column(1);
+ match self.cur {
+ Point { line, col } if line == 0 && col == last_col => None,
+ Point { col, .. } if
+ (col == last_col) => {
+ self.cur.line -= 1;
+ self.cur.col = Column(0);
+ Some(&self.grid[self.cur.line][self.cur.col])
+ },
+ _ => {
+ self.cur.col += Column(1);
+ Some(&self.grid[self.cur.line][self.cur.col])
+ }
+ }
}
}
impl<'a, T> BidirectionalIterator for GridIterator<'a, T> {
fn prev(&mut self) -> Option<Self::Item> {
- self.point_iter.prev().map(|p| &self.grid[p.line][p.col])
+ let num_cols = self.grid.num_cols();
+
+ match self.cur {
+ Point { line, col: Column(0) } if line == self.grid.len() - 1 => None,
+ Point { col: Column(0), .. } => {
+ self.cur.line += 1;
+ self.cur.col = num_cols - Column(1);
+ Some(&self.grid[self.cur.line][self.cur.col])
+ },
+ _ => {
+ self.cur.col -= Column(1);
+ Some(&self.grid[self.cur.line][self.cur.col])
+ }
+ }
}
}
diff --git a/src/grid/tests.rs b/src/grid/tests.rs
index 33d772a2..82edda69 100644
--- a/src/grid/tests.rs
+++ b/src/grid/tests.rs
@@ -112,8 +112,8 @@ fn test_iter() {
assert_eq!(None, iter.prev());
assert_eq!(Some(&1), iter.next());
- assert_eq!(Column(1), iter.cur().col);
- assert_eq!(4, iter.cur().line);
+ assert_eq!(Column(1), iter.cur.col);
+ assert_eq!(4, iter.cur.line);
assert_eq!(Some(&2), iter.next());
assert_eq!(Some(&3), iter.next());
@@ -121,12 +121,12 @@ fn test_iter() {
// test linewrapping
assert_eq!(Some(&5), iter.next());
- assert_eq!(Column(0), iter.cur().col);
- assert_eq!(3, iter.cur().line);
+ assert_eq!(Column(0), iter.cur.col);
+ assert_eq!(3, iter.cur.line);
assert_eq!(Some(&4), iter.prev());
- assert_eq!(Column(4), iter.cur().col);
- assert_eq!(4, iter.cur().line);
+ assert_eq!(Column(4), iter.cur.col);
+ assert_eq!(4, iter.cur.line);
// test that iter ends at end of grid
diff --git a/src/index.rs b/src/index.rs
index 6dbfbc40..0004454e 100644
--- a/src/index.rs
+++ b/src/index.rs
@@ -19,8 +19,6 @@ use std::cmp::{Ord, Ordering};
use std::fmt;
use std::ops::{self, Deref, Range, RangeInclusive, Add, Sub, AddAssign, SubAssign};
-use crate::grid::BidirectionalIterator;
-
/// The side of a cell
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Side {
@@ -72,67 +70,6 @@ impl From<Point> for Point<usize> {
}
}
-impl<T> Point<T>
-where
- T: Copy + Default + SubAssign<usize> + PartialEq,
-{
- pub fn iter(&self, last_col: Column, last_line: T) -> PointIterator<T> {
- PointIterator {
- cur: *self,
- last_col,
- last_line,
- }
- }
-}
-
-pub struct PointIterator<T> {
- pub cur: Point<T>,
- last_col: Column,
- last_line: T,
-}
-
-impl<T> Iterator for PointIterator<T>
-where
- T: Copy + Default + SubAssign<usize> + PartialEq,
-{
- type Item = Point<T>;
-
- fn next(&mut self) -> Option<Self::Item> {
- match self.cur {
- Point { line, col } if line == Default::default() && col == self.last_col => None,
- Point { col, .. } if col == self.last_col => {
- self.cur.line -= 1;
- self.cur.col = Column(0);
- Some(self.cur)
- },
- _ => {
- self.cur.col += Column(1);
- Some(self.cur)
- }
- }
- }
-}
-
-impl<T> BidirectionalIterator for PointIterator<T>
-where
- T: Copy + Default + AddAssign<usize> + SubAssign<usize> + PartialEq,
-{
- fn prev(&mut self) -> Option<Self::Item> {
- match self.cur {
- Point { line, col: Column(0) } if line == self.last_line => None,
- Point { col: Column(0), .. } => {
- self.cur.line += 1;
- self.cur.col = self.last_col;
- Some(self.cur)
- },
- _ => {
- self.cur.col -= Column(1);
- Some(self.cur)
- }
- }
- }
-}
-
/// A line
///
/// Newtype to avoid passing values incorrectly
@@ -163,6 +100,16 @@ impl fmt::Display for Column {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Linear(pub usize);
+impl Linear {
+ pub fn new(columns: Column, column: Column, line: Line) -> Self {
+ Linear(line.0 * columns.0 + column.0)
+ }
+
+ pub fn from_point(columns: Column, point: Point<usize>) -> Self {
+ Linear(point.line * columns.0 + point.col.0)
+ }
+}
+
impl fmt::Display for Linear {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Linear({})", self.0)
diff --git a/src/input.rs b/src/input.rs
index 9fe8e6bc..51e4e712 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -21,9 +21,10 @@
use std::borrow::Cow;
use std::mem;
use std::time::Instant;
-use std::iter::once;
+use std::ops::RangeInclusive;
use copypasta::{Clipboard, Load, Buffer as ClipboardBuffer};
+use unicode_width::UnicodeWidthStr;
use glutin::{
ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta,
TouchPhase,
@@ -32,10 +33,9 @@ use glutin::{
use crate::config::{self, Key};
use crate::grid::Scroll;
use crate::event::{ClickState, Mouse};
-use crate::index::{Line, Column, Side, Point};
-use crate::term::{Term, SizeInfo, Search, UrlHoverSaveState};
+use crate::index::{Line, Column, Side, Point, Linear};
+use crate::term::{Term, SizeInfo, Search};
use crate::term::mode::TermMode;
-use crate::term::cell::Flags;
use crate::util::fmt::Red;
use crate::util::start_daemon;
use crate::message_bar::{self, Message};
@@ -448,45 +448,30 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
None
};
- if let Some(Url { origin, len, .. }) = url {
- let mouse_cursor = if self.ctx.terminal().mode().intersects(mouse_mode) {
- MouseCursor::Default
- } else {
- MouseCursor::Text
- };
-
+ if let Some(Url { origin, text }) = url {
let cols = self.ctx.size_info().cols().0;
- let last_line = self.ctx.size_info().lines().0 - 1;
// Calculate the URL's start position
- let col = (cols + point.col.0 - origin % cols) % cols;
- let line = last_line - point.line.0 + (origin + cols - point.col.0 - 1) / cols;
- let start = Point::new(line, Column(col));
-
- // Update URLs only on change, so they don't all get marked as underlined
- if self.ctx.terminal().url_highlight_start() == Some(start) {
- return;
- }
-
- // Since the URL changed without reset, we need to clear the previous underline
- self.ctx.terminal_mut().reset_url_highlight();
+ let lines_before = (origin + cols - point.col.0 - 1) / cols;
+ let (start_col, start_line) = if lines_before > point.line.0 {
+ (0, 0)
+ } else {
+ let start_col = (cols + point.col.0 - origin % cols) % cols;
+ let start_line = point.line.0 - lines_before;
+ (start_col, start_line)
+ };
+ let start = Point::new(start_line, Column(start_col));
- // Underline all cells and store their current underline state
- let mut underlined = Vec::with_capacity(len);
- let iter = once(start).chain(start.iter(Column(cols - 1), last_line));
- for point in iter.take(len) {
- let cell = &mut self.ctx.terminal_mut().grid_mut()[point.line][point.col];
- underlined.push(cell.flags.contains(Flags::UNDERLINE));
- cell.flags.insert(Flags::UNDERLINE);
- }
+ // Calculate the URL's end position
+ let len = text.width();
+ let end_col = (point.col.0 + len - origin) % cols - 1;
+ let end_line = point.line.0 + (point.col.0 + len - origin) / cols;
+ let end = Point::new(end_line, Column(end_col));
- // Save the higlight state for restoring it again
- self.ctx.terminal_mut().set_url_highlight(UrlHoverSaveState {
- mouse_cursor,
- underlined,
- start,
- });
+ let start = Linear::from_point(Column(cols), start);
+ let end = Linear::from_point(Column(cols), end);
+ self.ctx.terminal_mut().set_url_highlight(RangeInclusive::new(start, end));
self.ctx.terminal_mut().set_mouse_cursor(MouseCursor::Hand);
self.ctx.terminal_mut().dirty = true;
} else {
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 => {
diff --git a/src/url.rs b/src/url.rs
index 59207499..33665197 100644
--- a/src/url.rs
+++ b/src/url.rs
@@ -13,6 +13,7 @@
// limitations under the License.
use url;
+use unicode_width::UnicodeWidthChar;
use crate::term::cell::{Cell, Flags};
@@ -28,14 +29,12 @@ const URL_SCHEMES: [&str; 8] = [
pub struct Url {
pub text: String,
pub origin: usize,
- pub len: usize,
}
/// Parser for streaming inside-out detection of URLs.
pub struct UrlParser {
state: String,
origin: usize,
- len: usize,
}
impl UrlParser {
@@ -43,7 +42,6 @@ impl UrlParser {
UrlParser {
state: String::new(),
origin: 0,
- len: 0,
}
}
@@ -51,7 +49,6 @@ impl UrlParser {
pub fn advance_left(&mut self, cell: &Cell) -> bool {
if cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
self.origin += 1;
- self.len += 1;
return false;
}
@@ -59,7 +56,6 @@ impl UrlParser {
true
} else {
self.origin += 1;
- self.len += 1;
false
}
}
@@ -67,16 +63,10 @@ impl UrlParser {
/// Advance the parser one character to the right.
pub fn advance_right(&mut self, cell: &Cell) -> bool {
if cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
- self.len += 1;
return false;
}
- if self.advance(cell.c, self.state.len()) {
- true
- } else {
- self.len += 1;
- false
- }
+ self.advance(cell.c, self.state.len())
}
/// Returns the URL if the parser has found any.
@@ -93,7 +83,7 @@ impl UrlParser {
match c {
'a'...'z' | 'A'...'Z' => (),
_ => {
- self.origin = self.origin.saturating_sub(byte_index + 1);
+ self.origin = self.origin.saturating_sub(byte_index + c.width().unwrap_or(1));
self.state = self.state.split_off(byte_index + c.len_utf8());
break;
}
@@ -104,7 +94,7 @@ impl UrlParser {
// Remove non-matching parenthesis and brackets
let mut open_parens_count: isize = 0;
let mut open_bracks_count: isize = 0;
- for (i, c) in self.state.chars().enumerate() {
+ for (i, c) in self.state.char_indices() {
match c {
'(' => open_parens_count += 1,
')' if open_parens_count > 0 => open_parens_count -= 1,
@@ -140,7 +130,6 @@ impl UrlParser {
Some(Url {
origin: self.origin - 1,
text: self.state,
- len: self.len,
})
} else {
None
@@ -247,25 +236,14 @@ mod tests {
let term = url_create_term("https://全.org");
let url = term.url_search(Point::new(0, Column(9)));
assert_eq!(url.map(|u| u.origin), Some(9));
- }
-
- #[test]
- fn url_len() {
- let term = url_create_term(" test https://example.org ");
- let url = term.url_search(Point::new(0, Column(10)));
- assert_eq!(url.map(|u| u.len), Some(19));
- let term = url_create_term("https://全.org");
- let url = term.url_search(Point::new(0, Column(0)));
- assert_eq!(url.map(|u| u.len), Some(14));
-
- let term = url_create_term("https://全.org");
- let url = term.url_search(Point::new(0, Column(10)));
- assert_eq!(url.map(|u| u.len), Some(14));
+ let term = url_create_term("test@https://example.org");
+ let url = term.url_search(Point::new(0, Column(9)));
+ assert_eq!(url.map(|u| u.origin), Some(4));
- let term = url_create_term("https://全.org");
+ let term = url_create_term("test全https://example.org");
let url = term.url_search(Point::new(0, Column(9)));
- assert_eq!(url.map(|u| u.len), Some(14));
+ assert_eq!(url.map(|u| u.origin), Some(3));
}
#[test]
@@ -288,6 +266,8 @@ mod tests {
url_test("'https://example.org'", "https://example.org");
url_test("'https://example.org", "https://example.org");
url_test("https://example.org'", "https://example.org");
+
+ url_test("(https://example.org/test全)", "https://example.org/test全");
}
#[test]