summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty_terminal/src')
-rw-r--r--alacritty_terminal/src/ansi.rs10
-rw-r--r--alacritty_terminal/src/grid/mod.rs12
-rw-r--r--alacritty_terminal/src/index.rs48
-rw-r--r--alacritty_terminal/src/lib.rs1
-rw-r--r--alacritty_terminal/src/renderer/mod.rs4
-rw-r--r--alacritty_terminal/src/renderer/rects.rs56
-rw-r--r--alacritty_terminal/src/selection.rs108
-rw-r--r--alacritty_terminal/src/term/mod.rs362
-rw-r--r--alacritty_terminal/src/url.rs44
9 files changed, 238 insertions, 407 deletions
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index d989b78e..a05904df 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use vte;
-use crate::index::{Column, Contains, Line};
+use crate::index::{Column, Line};
use crate::term::color::Rgb;
// Parse colors in XParseColor format
@@ -629,8 +629,8 @@ pub enum Attr {
Dim,
/// Italic text
Italic,
- /// Underscore text
- Underscore,
+ /// Underline text
+ Underline,
/// Blink cursor slowly
BlinkSlow,
/// Blink cursor fast
@@ -1163,7 +1163,7 @@ fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> {
1 => Some(Attr::Bold),
2 => Some(Attr::Dim),
3 => Some(Attr::Italic),
- 4 => Some(Attr::Underscore),
+ 4 => Some(Attr::Underline),
5 => Some(Attr::BlinkSlow),
6 => Some(Attr::BlinkFast),
7 => Some(Attr::Reverse),
@@ -1260,7 +1260,7 @@ fn parse_sgr_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
*i += 4;
let range = 0..256;
- if !range.contains_(r) || !range.contains_(g) || !range.contains_(b) {
+ if !range.contains(&r) || !range.contains(&g) || !range.contains(&b) {
debug!("Invalid RGB color spec: ({}, {}, {})", r, g, b);
return None;
}
diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs
index 5f5b84fe..3141208f 100644
--- a/alacritty_terminal/src/grid/mod.rs
+++ b/alacritty_terminal/src/grid/mod.rs
@@ -15,7 +15,7 @@
//! A specialized 2d grid implementation optimized for use in a terminal.
use std::cmp::{max, min, Ordering};
-use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};
+use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo};
use serde::{Deserialize, Serialize};
@@ -63,7 +63,6 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
&& self.display_offset.eq(&other.display_offset)
&& self.scroll_limit.eq(&other.scroll_limit)
&& self.selection.eq(&other.selection)
- && self.url_highlight.eq(&other.url_highlight)
}
}
@@ -106,10 +105,6 @@ 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,7 +127,6 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
scroll_limit: 0,
selection: None,
max_scroll_limit: scrollback,
- url_highlight: None,
}
}
@@ -398,7 +392,6 @@ 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;
@@ -434,7 +427,6 @@ 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);
@@ -479,7 +471,6 @@ 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-
@@ -524,7 +515,6 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
self.display_offset = 0;
self.selection = None;
- self.url_highlight = None;
}
}
diff --git a/alacritty_terminal/src/index.rs b/alacritty_terminal/src/index.rs
index 0a100e18..d40245f3 100644
--- a/alacritty_terminal/src/index.rs
+++ b/alacritty_terminal/src/index.rs
@@ -17,7 +17,7 @@
/// Indexing types and implementations for Grid and Line
use std::cmp::{Ord, Ordering};
use std::fmt;
-use std::ops::{self, Add, AddAssign, Deref, Range, RangeInclusive, Sub, SubAssign};
+use std::ops::{self, Add, AddAssign, Deref, Range, Sub, SubAssign};
use serde::{Deserialize, Serialize};
@@ -41,6 +41,30 @@ impl<L> Point<L> {
pub fn new(line: L, col: Column) -> Point<L> {
Point { line, col }
}
+
+ #[inline]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ pub fn sub(mut self, num_cols: usize, length: usize) -> Point<L>
+ where
+ L: Copy + Sub<usize, Output = L>,
+ {
+ let line_changes = f32::ceil(length.saturating_sub(self.col.0) as f32 / num_cols as f32);
+ self.line = self.line - line_changes as usize;
+ self.col = Column((num_cols + self.col.0 - length % num_cols) % num_cols);
+ self
+ }
+
+ #[inline]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ pub fn add(mut self, num_cols: usize, length: usize) -> Point<L>
+ where
+ L: Copy + Add<usize, Output = L>,
+ {
+ let line_changes = length.saturating_sub(self.col.0) / num_cols;
+ self.line = self.line + line_changes;
+ self.col = Column((self.col.0 + length) % num_cols);
+ self
+ }
}
impl Ord for Point {
@@ -253,28 +277,6 @@ impl<T> From<Range<T>> for IndexRange<T> {
}
}
-// can be removed if range_contains is stabilized
-pub trait Contains {
- type Content;
- fn contains_(&self, item: Self::Content) -> bool;
-}
-
-impl<T: PartialOrd<T>> Contains for Range<T> {
- type Content = T;
-
- fn contains_(&self, item: Self::Content) -> bool {
- (self.start <= item) && (item < self.end)
- }
-}
-
-impl<T: PartialOrd<T>> Contains for RangeInclusive<T> {
- type Content = T;
-
- fn contains_(&self, item: Self::Content) -> bool {
- (self.start() <= &item) && (&item <= self.end())
- }
-}
-
macro_rules! ops {
($ty:ty, $construct:expr) => {
add!($ty, $construct);
diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs
index dc26046d..82c83856 100644
--- a/alacritty_terminal/src/lib.rs
+++ b/alacritty_terminal/src/lib.rs
@@ -38,7 +38,6 @@ pub mod selection;
pub mod sync;
pub mod term;
pub mod tty;
-pub mod url;
pub mod util;
pub use crate::grid::Grid;
diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs
index 76daac91..b200743a 100644
--- a/alacritty_terminal/src/renderer/mod.rs
+++ b/alacritty_terminal/src/renderer/mod.rs
@@ -382,8 +382,8 @@ impl GlyphCache {
let grid_width = cell_width as u32 * dimensions.columns_u32();
let grid_height = cell_height as u32 * dimensions.lines_u32();
- let width = (f64::from(grid_width) + 2. * padding_x).floor();
- let height = (f64::from(grid_height) + 2. * padding_y).floor();
+ let width = padding_x.mul_add(2., f64::from(grid_width)).floor();
+ let height = padding_y.mul_add(2., f64::from(grid_height)).floor();
Some((width, height))
}
diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs
index c105c2e7..d5645828 100644
--- a/alacritty_terminal/src/renderer/rects.rs
+++ b/alacritty_terminal/src/renderer/rects.rs
@@ -15,7 +15,7 @@ use std::collections::HashMap;
use font::Metrics;
-use crate::index::Point;
+use crate::index::{Column, Point};
use crate::term::cell::Flags;
use crate::term::color::Rgb;
use crate::term::{RenderableCell, SizeInfo};
@@ -36,16 +36,42 @@ impl RenderRect {
}
}
-struct RenderLine {
- start: Point,
- end: Point,
- color: Rgb,
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct RenderLine {
+ pub start: Point,
+ pub end: Point,
+ pub color: Rgb,
}
impl RenderLine {
- fn into_rect(self, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> RenderRect {
- let start_x = self.start.col.0 as f32 * size.cell_width;
- let end_x = (self.end.col.0 + 1) as f32 * size.cell_width;
+ pub fn rects(&self, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
+ let mut rects = Vec::new();
+
+ let mut start = self.start;
+ while start.line < self.end.line {
+ let mut end = start;
+ end.col = size.cols() - 1;
+ rects.push(Self::create_rect(metrics, size, flag, start, end, self.color));
+
+ start.col = Column(0);
+ start.line += 1;
+ }
+
+ rects.push(Self::create_rect(metrics, size, flag, start, self.end, self.color));
+
+ rects
+ }
+
+ fn create_rect(
+ metrics: &Metrics,
+ size: &SizeInfo,
+ flag: Flags,
+ start: Point,
+ end: Point,
+ color: Rgb,
+ ) -> RenderRect {
+ let start_x = start.col.0 as f32 * size.cell_width;
+ let end_x = (end.col.0 + 1) as f32 * size.cell_width;
let width = end_x - start_x;
let (position, mut height) = match flag {
@@ -57,7 +83,7 @@ impl RenderLine {
// Make sure lines are always visible
height = height.max(1.);
- let line_bottom = (self.start.line.0 as f32 + 1.) * size.cell_height;
+ let line_bottom = (start.line.0 as f32 + 1.) * size.cell_height;
let baseline = line_bottom + metrics.descent;
let mut y = baseline - position - height / 2.;
@@ -66,7 +92,7 @@ impl RenderLine {
y = max_y;
}
- RenderRect::new(start_x + size.padding_x, y + size.padding_y, width, height, self.color, 1.)
+ RenderRect::new(start_x + size.padding_x, y + size.padding_y, width, height, color, 1.)
}
}
@@ -81,11 +107,11 @@ impl RenderLines {
Self::default()
}
- pub fn into_rects(self, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
+ pub fn rects(&self, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
self.inner
- .into_iter()
+ .iter()
.map(|(flag, lines)| -> Vec<RenderRect> {
- lines.into_iter().map(|line| line.into_rect(flag, &metrics, &size)).collect()
+ lines.iter().map(|line| line.rects(*flag, metrics, size)).flatten().collect()
})
.flatten()
.collect()
@@ -100,9 +126,9 @@ impl RenderLines {
// Check if there's an active line
if let Some(line) = self.inner.get_mut(flag).and_then(|lines| lines.last_mut()) {
- if cell.line == line.start.line
- && cell.fg == line.color
+ if cell.fg == line.color
&& cell.column == line.end.col + 1
+ && cell.line == line.end.line
{
// Update the length of the line
line.end = cell.into();
diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs
index 29934e5a..5f94634f 100644
--- a/alacritty_terminal/src/selection.rs
+++ b/alacritty_terminal/src/selection.rs
@@ -144,22 +144,22 @@ impl Selection {
// Simple selection is empty when the points are identical
// or two adjacent cells have the sides right -> left
start == end
- || (start.side == Side::Left
- && end.side == Side::Right
+ || (start.side == Side::Right
+ && end.side == Side::Left
&& (start.point.line == end.point.line)
- && start.point.col == end.point.col + 1)
+ && start.point.col + 1 == end.point.col)
},
Selection::Block { region: Range { ref start, ref end } } => {
// Block selection is empty when the points' columns and sides are identical
// or two cells with adjacent columns have the sides right -> left,
// regardless of their lines
(start.point.col == end.point.col && start.side == end.side)
- || (start.point.col == end.point.col + 1
+ || (start.point.col + 1 == end.point.col
+ && start.side == Side::Right
+ && end.side == Side::Left)
+ || (end.point.col + 1 == start.point.col
&& start.side == Side::Left
&& end.side == Side::Right)
- || (end.point.col == start.point.col + 1
- && end.side == Side::Left
- && start.side == Side::Right)
},
Selection::Semantic { .. } | Selection::Lines { .. } => false,
}
@@ -214,16 +214,16 @@ impl Selection {
span.map(|mut span| {
let grid = term.grid();
- if span.end.col < cols
- && grid[span.end.line][span.end.col].flags.contains(Flags::WIDE_CHAR_SPACER)
+ if span.start.col < cols
+ && grid[span.start.line][span.start.col].flags.contains(Flags::WIDE_CHAR_SPACER)
{
- span.end.col = Column(span.end.col.saturating_sub(1));
+ span.start.col = Column(span.start.col.saturating_sub(1));
}
- if span.start.col.0 < cols.saturating_sub(1)
- && grid[span.start.line][span.start.col].flags.contains(Flags::WIDE_CHAR)
+ if span.end.col.0 < cols.saturating_sub(1)
+ && grid[span.end.line][span.end.col].flags.contains(Flags::WIDE_CHAR)
{
- span.start.col += 1;
+ span.end.col += 1;
}
span
@@ -232,7 +232,7 @@ impl Selection {
// Bring start and end points in the correct order
fn points_need_swap(start: Point<isize>, end: Point<isize>) -> bool {
- start.line > end.line || start.line == end.line && start.col < end.col
+ start.line < end.line || start.line == end.line && start.col > end.col
}
// Clamp selection inside the grid to prevent out of bounds errors
@@ -242,26 +242,26 @@ impl Selection {
lines: isize,
cols: Column,
) -> Option<(Point<isize>, Point<isize>)> {
- if end.line >= lines {
+ if start.line >= lines {
// Don't show selection above visible region
- if start.line >= lines {
+ if end.line >= lines {
return None;
}
// Clamp selection above viewport to visible region
- end.line = lines - 1;
- end.col = Column(0);
+ start.line = lines - 1;
+ start.col = Column(0);
}
- if start.line < 0 {
+ if end.line < 0 {
// Don't show selection below visible region
- if end.line < 0 {
+ if start.line < 0 {
return None;
}
// Clamp selection below viewport to visible region
- start.line = 0;
- start.col = cols - 1;
+ end.line = 0;
+ end.col = cols - 1;
}
Some((start, end))
@@ -275,10 +275,10 @@ impl Selection {
if let Some(end) = term.bracket_search(start.into()) {
(start.into(), end)
} else {
- (term.semantic_search_right(start.into()), term.semantic_search_left(end.into()))
+ (term.semantic_search_left(start.into()), term.semantic_search_right(end.into()))
}
} else {
- (term.semantic_search_right(start.into()), term.semantic_search_left(end.into()))
+ (term.semantic_search_left(start.into()), term.semantic_search_right(end.into()))
};
Some(Span { start, end, is_block: false })
@@ -288,8 +288,8 @@ impl Selection {
where
T: Dimensions,
{
- start.col = term.dimensions().col - 1;
- end.col = Column(0);
+ end.col = term.dimensions().col - 1;
+ start.col = Column(0);
Some(Span { start: start.into(), end: end.into(), is_block: false })
}
@@ -310,19 +310,19 @@ impl Selection {
}
// Remove last cell if selection ends to the left of a cell
- if start_side == Side::Left && start != end {
- // Special case when selection starts to left of first cell
- if start.col == Column(0) {
- start.col = term.dimensions().col - 1;
- start.line += 1;
+ if end_side == Side::Left && start != end {
+ // Special case when selection ends to left of first cell
+ if end.col == Column(0) {
+ end.col = term.dimensions().col - 1;
+ end.line += 1;
} else {
- start.col -= 1;
+ end.col -= 1;
}
}
// Remove first cell if selection starts at the right of a cell
- if end_side == Side::Right && start != end {
- end.col += 1;
+ if start_side == Side::Right && start != end {
+ start.col += 1;
}
// Return the selection with all cells inclusive
@@ -340,20 +340,20 @@ impl Selection {
return None;
}
- // Always go bottom-right -> top-left
- if start.col < end.col {
+ // Always go top-left -> bottom-right
+ if start.col > end.col {
std::mem::swap(&mut start_side, &mut end_side);
std::mem::swap(&mut start.col, &mut end.col);
}
// Remove last cell if selection ends to the left of a cell
- if start_side == Side::Left && start != end && start.col.0 > 0 {
- start.col -= 1;
+ if end_side == Side::Left && start != end && end.col.0 > 0 {
+ end.col -= 1;
}
// Remove first cell if selection starts at the right of a cell
- if end_side == Side::Right && start != end {
- end.col += 1;
+ if start_side == Side::Right && start != end {
+ start.col += 1;
}
// Return the selection with all cells inclusive
@@ -508,8 +508,8 @@ mod test {
selection.update(Point::new(0, Column(1)), Side::Right);
assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span {
- start: Point::new(0, Column(1)),
- end: Point::new(1, Column(2)),
+ start: Point::new(1, Column(2)),
+ end: Point::new(0, Column(1)),
is_block: false,
});
}
@@ -532,8 +532,8 @@ mod test {
selection.update(Point::new(1, Column(0)), Side::Right);
assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span {
- start: Point::new(0, Column(1)),
- end: Point::new(1, Column(1)),
+ start: Point::new(1, Column(1)),
+ end: Point::new(0, Column(1)),
is_block: false,
});
}
@@ -545,8 +545,8 @@ mod test {
selection.rotate(-3);
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
- start: Point::new(0, Column(4)),
- end: Point::new(2, Column(0)),
+ start: Point::new(2, Column(0)),
+ end: Point::new(0, Column(4)),
is_block: false,
});
}
@@ -558,8 +558,8 @@ mod test {
selection.rotate(-3);
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
- start: Point::new(0, Column(4)),
- end: Point::new(2, Column(3)),
+ start: Point::new(2, Column(3)),
+ end: Point::new(0, Column(4)),
is_block: false,
});
}
@@ -571,8 +571,8 @@ mod test {
selection.rotate(-3);
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
- start: Point::new(0, Column(4)),
- end: Point::new(2, Column(4)),
+ start: Point::new(2, Column(4)),
+ end: Point::new(0, Column(4)),
is_block: false,
});
}
@@ -584,8 +584,8 @@ mod test {
selection.rotate(-3);
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
- start: Point::new(0, Column(4)),
- end: Point::new(2, Column(4)),
+ start: Point::new(2, Column(4)),
+ end: Point::new(0, Column(4)),
is_block: true,
});
}
@@ -604,8 +604,8 @@ mod test {
selection.update(Point::new(0, Column(8)), Side::Right);
assert_eq!(selection.to_span(&term).unwrap(), Span {
- start: Point::new(0, Column(9)),
- end: Point::new(0, Column(0)),
+ start: Point::new(0, Column(0)),
+ end: Point::new(0, Column(9)),
is_block: false,
});
}
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index bc7235cb..63a5e040 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -14,12 +14,11 @@
//
//! Exports the `Term` type which is a high-level API for the Grid
use std::cmp::{max, min};
-use std::ops::{Index, IndexMut, Range, RangeInclusive};
+use std::ops::{Index, IndexMut, Range};
use std::time::{Duration, Instant};
use std::{io, mem, ptr};
use log::{debug, trace};
-use rfind_url::{Parser, ParserState};
use serde::{Deserialize, Serialize};
use unicode_width::UnicodeWidthChar;
@@ -33,13 +32,12 @@ use crate::event::{Event, EventListener};
use crate::grid::{
BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
};
-use crate::index::{self, Column, Contains, IndexRange, Line, Linear, Point};
+use crate::index::{self, Column, IndexRange, Line, Point};
use crate::selection::{self, Selection, SelectionRange, Span};
use crate::term::cell::{Cell, Flags, LineLength};
use crate::term::color::Rgb;
#[cfg(windows)]
use crate::tty;
-use crate::url::Url;
pub mod cell;
pub mod color;
@@ -76,7 +74,7 @@ impl<T> Search for Term<T> {
break;
}
- if iter.point().col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
+ if iter.point().col == last_col && !cell.flags.contains(Flags::WRAPLINE) {
break; // cut off if on new line or hit escape char
}
@@ -100,7 +98,7 @@ impl<T> Search for Term<T> {
point = iter.point();
- if point.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
+ if point.col == last_col && !cell.flags.contains(Flags::WRAPLINE) {
break; // cut off if on new line or hit escape char
}
}
@@ -181,7 +179,6 @@ pub struct RenderableCellsIter<'a, C> {
config: &'a Config<C>,
colors: &'a color::List,
selection: Option<SelectionRange>,
- url_highlight: &'a Option<RangeInclusive<index::Linear>>,
}
impl<'a, C> RenderableCellsIter<'a, C> {
@@ -202,7 +199,7 @@ impl<'a, C> RenderableCellsIter<'a, C> {
let selection_range = selection.map(|span| {
let (limit_start, limit_end) = if span.is_block {
- (span.end.col, span.start.col)
+ (span.start.col, span.end.col)
} else {
(Column(0), term.cols() - 1)
};
@@ -211,11 +208,6 @@ impl<'a, C> RenderableCellsIter<'a, C> {
let mut start = term.buffer_to_visible(span.start);
let mut end = term.buffer_to_visible(span.end);
- // Start and end lines are swapped as we switch from buffer to line coordinates
- if start > end {
- mem::swap(&mut start, &mut end);
- }
-
// Trim start/end with partially visible block selection
start.col = max(limit_start, start.col);
end.col = min(limit_end, end.col);
@@ -227,8 +219,8 @@ impl<'a, C> RenderableCellsIter<'a, C> {
let cursor = &term.cursor.point;
let cursor_visible = term.mode.contains(TermMode::SHOW_CURSOR) && grid.contains(cursor);
let cursor_key = if cursor_visible {
- let is_wide = grid[cursor].flags.contains(cell::Flags::WIDE_CHAR)
- && (cursor.col + 1) < grid.num_cols();
+ let is_wide =
+ grid[cursor].flags.contains(Flags::WIDE_CHAR) && (cursor.col + 1) < grid.num_cols();
Some(CursorKey { style: cursor_style, is_wide })
} else {
// Use hidden cursor so text will not get inverted
@@ -242,7 +234,6 @@ impl<'a, C> RenderableCellsIter<'a, C> {
grid,
inner,
selection: selection_range,
- url_highlight: &grid.url_highlight,
config,
colors: &term.colors,
cursor_key,
@@ -266,7 +257,7 @@ pub struct RenderableCell {
pub fg: Rgb,
pub bg: Rgb,
pub bg_alpha: f32,
- pub flags: cell::Flags,
+ pub flags: Flags,
}
impl RenderableCell {
@@ -315,27 +306,22 @@ impl RenderableCell {
}
}
- fn compute_fg_rgb<C>(
- config: &Config<C>,
- colors: &color::List,
- fg: Color,
- flags: cell::Flags,
- ) -> Rgb {
+ fn compute_fg_rgb<C>(config: &Config<C>, colors: &color::List, fg: Color, flags: Flags) -> Rgb {
match fg {
Color::Spec(rgb) => rgb,
Color::Named(ansi) => {
match (config.draw_bold_text_with_bright_colors(), flags & Flags::DIM_BOLD) {
// If no bright foreground is set, treat it like the BOLD flag doesn't exist
- (_, cell::Flags::DIM_BOLD)
+ (_, Flags::DIM_BOLD)
if ansi == NamedColor::Foreground
&& config.colors.primary.bright_foreground.is_none() =>
{
colors[NamedColor::DimForeground]
},
// Draw bold text in bright colors *and* contains bold flag.
- (true, cell::Flags::BOLD) => colors[ansi.to_bright()],
+ (true, Flags::BOLD) => colors[ansi.to_bright()],
// Cell is marked as dim and not bold
- (_, cell::Flags::DIM) | (false, cell::Flags::DIM_BOLD) => colors[ansi.to_dim()],
+ (_, Flags::DIM) | (false, Flags::DIM_BOLD) => colors[ansi.to_dim()],
// None of the above, keep original color.
_ => colors[ansi],
}
@@ -346,9 +332,9 @@ impl RenderableCell {
flags & Flags::DIM_BOLD,
idx,
) {
- (true, cell::Flags::BOLD, 0..=7) => idx as usize + 8,
- (false, cell::Flags::DIM, 8..=15) => idx as usize - 8,
- (false, cell::Flags::DIM, 0..=7) => idx as usize + 260,
+ (true, Flags::BOLD, 0..=7) => idx as usize + 8,
+ (false, Flags::DIM, 8..=15) => idx as usize - 8,
+ (false, Flags::DIM, 0..=7) => idx as usize + 260,
_ => idx as usize,
};
@@ -429,7 +415,7 @@ impl<'a, C> Iterator for RenderableCellsIter<'a, C> {
return Some(cell);
}
} else {
- let mut cell = self.inner.next()?;
+ let cell = self.inner.next()?;
let selected = self
.selection
@@ -437,13 +423,6 @@ impl<'a, C> Iterator for RenderableCellsIter<'a, C> {
.map(|range| range.contains(cell.column, cell.line))
.unwrap_or(false);
- // Underline URL highlights
- let index = Linear::new(self.grid.num_cols(), cell.column, cell.line);
- if self.url_highlight.as_ref().map(|range| range.contains_(index)).unwrap_or(false)
- {
- cell.inner.flags.insert(Flags::UNDERLINE);
- }
-
if !cell.is_empty() || selected {
return Some(RenderableCell::new(self.config, self.colors, cell, selected));
}
@@ -471,6 +450,7 @@ pub mod mode {
const FOCUS_IN_OUT = 0b000_1000_0000_0000;
const ALT_SCREEN = 0b001_0000_0000_0000;
const MOUSE_DRAG = 0b010_0000_0000_0000;
+ const MOUSE_MODE = 0b010_0000_0100_1000;
const ALTERNATE_SCROLL = 0b100_0000_0000_0000;
const ANY = 0b111_1111_1111_1111;
const NONE = 0;
@@ -688,6 +668,9 @@ impl VisualBell {
}
pub struct Term<T> {
+ /// Terminal focus
+ pub is_focused: bool,
+
/// The grid
grid: Grid<Cell>,
@@ -762,9 +745,6 @@ pub struct Term<T> {
/// Proxy for sending events to the event loop
event_proxy: T,
- /// Terminal focus
- pub is_focused: bool,
-
/// Current title of the window
title: String,
@@ -810,15 +790,14 @@ impl SizeInfo {
Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize)
}
- pub fn contains_point(&self, x: usize, y: usize, include_padding: bool) -> bool {
- if include_padding {
- x < self.width as usize && y < self.height as usize
- } else {
- x < (self.width - self.padding_x) as usize
- && x >= self.padding_x as usize
- && y < (self.height - self.padding_y) as usize
- && y >= self.padding_y as usize
- }
+ /// Check if coordinates are inside the terminal grid.
+ ///
+ /// The padding is not counted as part of the grid.
+ pub fn contains_point(&self, x: usize, y: usize) -> bool {
+ x < (self.width - self.padding_x) as usize
+ && x >= self.padding_x as usize
+ && y < (self.height - self.padding_y) as usize
+ && y >= self.padding_y as usize
}
pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point {
@@ -848,7 +827,6 @@ impl<T> Term<T> {
{
self.event_proxy.send_event(Event::MouseCursorDirty);
self.grid.scroll_display(scroll);
- self.reset_url_highlight();
self.dirty = true;
}
@@ -923,118 +901,84 @@ impl<T> Term<T> {
self.grid.update_history(config.scrolling.history() as usize, &self.cursor.template);
}
+ /// Convert the active selection to a String.
pub fn selection_to_string(&self) -> Option<String> {
- trait Append {
- fn append(
- &mut self,
- append_newline: bool,
- grid: &Grid<Cell>,
- tabs: &TabStops,
- line: usize,
- cols: Range<Column>,
- );
- }
-
- impl Append for String {
- fn append(
- &mut self,
- append_newline: bool,
- grid: &Grid<Cell>,
- tabs: &TabStops,
- mut line: usize,
- cols: Range<Column>,
- ) {
- // Select until last line still within the buffer
- line = min(line, grid.len() - 1);
-
- let grid_line = &grid[line];
- let line_length = grid_line.line_length();
- let line_end = min(line_length, cols.end + 1);
-
- if cols.start < line_end {
- let mut tab_mode = false;
-
- for col in IndexRange::from(cols.start..line_end) {
- let cell = grid_line[col];
-
- if tab_mode {
- // Skip over whitespace until next tab-stop once a tab was found
- if tabs[col] {
- tab_mode = false;
- } else if cell.c == ' ' {
- continue;
- }
- }
-
- if !cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
- self.push(cell.c);
- for c in (&cell.chars()[1..]).iter().filter(|c| **c != ' ') {
- self.push(*c);
- }
- }
+ let selection = self.grid.selection.clone()?;
+ let Span { start, end, is_block } = selection.to_span(self)?;
- if cell.c == '\t' {
- tab_mode = true;
- }
- }
- }
+ let mut res = String::new();
- if append_newline
- || (cols.end >= grid.num_cols() - 1
- && (line_end == Column(0)
- || !grid[line][line_end - 1].flags.contains(cell::Flags::WRAPLINE)))
- {
- self.push('\n');
- }
+ if is_block {
+ for line in (end.line + 1..=start.line).rev() {
+ res += &(self.line_to_string(line, start.col..end.col) + "\n");
}
+ res += &self.line_to_string(end.line, start.col..end.col);
+ } else {
+ res = self.bounds_to_string(start, end);
}
- let selection = self.grid.selection.clone()?;
- let Span { mut start, mut end, is_block } = selection.to_span(self)?;
+ Some(res)
+ }
+ /// Convert range between two points to a String.
+ pub fn bounds_to_string(&self, start: Point<usize>, end: Point<usize>) -> String {
let mut res = String::new();
- if start > end {
- ::std::mem::swap(&mut start, &mut end);
+ for line in (end.line..=start.line).rev() {
+ let start_col = if line == start.line { start.col } else { Column(0) };
+ let end_col = if line == end.line { end.col } else { self.cols() - 1 };
+
+ res += &self.line_to_string(line, start_col..end_col);
}
- let line_count = end.line - start.line;
+ res
+ }
- // Setup block selection start/end point limits
- let (limit_start, limit_end) =
- if is_block { (end.col, start.col) } else { (Column(0), self.grid.num_cols()) };
+ /// Convert a single line in the grid to a String.
+ fn line_to_string(&self, line: usize, cols: Range<Column>) -> String {
+ let mut text = String::new();
- match line_count {
- // Selection within single line
- 0 => {
- res.append(false, &self.grid, &self.tabs, start.line, start.col..end.col);
- },
+ let grid_line = &self.grid[line];
+ let line_length = grid_line.line_length();
+ let line_end = min(line_length, cols.end + 1);
- // Selection ends on line following start
- 1 => {
- // Ending line
- res.append(is_block, &self.grid, &self.tabs, end.line, end.col..limit_end);
+ let mut tab_mode = false;
- // Starting line
- res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col);
- },
+ for col in IndexRange::from(cols.start..line_end) {
+ let cell = grid_line[col];
- // Multi line selection
- _ => {
- // Ending line
- res.append(is_block, &self.grid, &self.tabs, end.line, end.col..limit_end);
+ // Skip over cells until next tab-stop once a tab was found
+ if tab_mode {
+ if self.tabs[col] {
+ tab_mode = false;
+ } else {
+ continue;
+ }
+ }
+
+ if cell.c == '\t' {
+ tab_mode = true;
+ }
- let middle_range = (start.line + 1)..(end.line);
- for line in middle_range.rev() {
- res.append(is_block, &self.grid, &self.tabs, line, limit_start..limit_end);
+ if !cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
+ // Push cells primary character
+ text.push(cell.c);
+
+ // Push zero-width characters
+ for c in (&cell.chars()[1..]).iter().take_while(|c| **c != ' ') {
+ text.push(*c);
}
+ }
+ }
- // Starting line
- res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col);
- },
+ if cols.end >= self.cols() - 1
+ && (line_end == Column(0)
+ || !self.grid[line][line_end - 1].flags.contains(cell::Flags::WRAPLINE))
+ {
+ text.push('\n');
}
- Some(res)
+ text
}
pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
@@ -1097,7 +1041,6 @@ impl<T> Term<T> {
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) {
@@ -1233,92 +1176,9 @@ impl<T> Term<T> {
self.event_proxy.send_event(Event::Exit);
}
- #[inline]
- pub fn set_url_highlight(&mut self, hl: RangeInclusive<index::Linear>) {
- self.grid.url_highlight = Some(hl);
- self.dirty = true;
- }
-
- #[inline]
- pub fn reset_url_highlight(&mut self) {
- self.grid.url_highlight = None;
- self.dirty = true;
- }
-
pub fn clipboard(&mut self) -> &mut Clipboard {
&mut self.clipboard
}
-
- pub fn urls(&self) -> Vec<Url> {
- let display_offset = self.grid.display_offset();
- let num_cols = self.grid.num_cols().0;
- let last_col = Column(num_cols - 1);
- let last_line = self.grid.num_lines() - 1;
-
- let grid_end_point = Point::new(0, last_col);
- let mut iter = self.grid.iter_from(grid_end_point);
-
- let mut parser = Parser::new();
- let mut extra_url_len = 0;
- let mut urls = Vec::new();
-
- let mut c = Some(iter.cell());
- while let Some(cell) = c {
- let point = iter.point();
- c = iter.prev();
-
- // Skip double-width cell but extend URL length
- if cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
- extra_url_len += 1;
- continue;
- }
-
- // Interrupt URLs on line break
- if point.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
- extra_url_len = 0;
- parser.reset();
- }
-
- // Advance parser
- match parser.advance(cell.c) {
- ParserState::Url(length) => {
- urls.push(Url::new(point, length + extra_url_len, num_cols))
- },
- ParserState::NoUrl => {
- extra_url_len = 0;
-
- // Stop searching for URLs once the viewport has been left without active URL
- if point.line > last_line.0 + display_offset {
- break;
- }
- },
- _ => (),
- }
- }
-
- urls
- }
-
- pub fn url_to_string(&self, url: Url) -> String {
- let mut url_text = String::new();
-
- let mut iter = self.grid.iter_from(url.start);
-
- let mut c = Some(iter.cell());
- while let Some(cell) = c {
- if !cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
- url_text.push(cell.c);
- }
-
- if iter.point() == url.end {
- break;
- }
-
- c = iter.next();
- }
-
- url_text
- }
}
impl<T> TermInfo for Term<T> {
@@ -1387,7 +1247,7 @@ impl<T: EventListener> ansi::Handler for Term<T> {
let location = Point { line: self.cursor.point.line, col: self.cursor.point.col };
let cell = &mut self.grid[&location];
- cell.flags.insert(cell::Flags::WRAPLINE);
+ cell.flags.insert(Flags::WRAPLINE);
}
if (self.cursor.point.line + 1) >= self.scroll_region.end {
@@ -1422,7 +1282,7 @@ impl<T: EventListener> ansi::Handler for Term<T> {
if width == 0 {
let mut col = self.cursor.point.col.0.saturating_sub(1);
let line = self.cursor.point.line;
- if self.grid[line][Column(col)].flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
+ if self.grid[line][Column(col)].flags.contains(Flags::WIDE_CHAR_SPACER) {
col = col.saturating_sub(1);
}
self.grid[line][Column(col)].push_extra(c);
@@ -1435,13 +1295,13 @@ impl<T: EventListener> ansi::Handler for Term<T> {
// Handle wide chars
if width == 2 {
- cell.flags.insert(cell::Flags::WIDE_CHAR);
+ cell.flags.insert(Flags::WIDE_CHAR);
if self.cursor.point.col + 1 < num_cols {
self.cursor.point.col += 1;
let spacer = &mut self.grid[&self.cursor.point];
*spacer = self.cursor.template;
- spacer.flags.insert(cell::Flags::WIDE_CHAR_SPACER);
+ spacer.flags.insert(Flags::WIDE_CHAR_SPACER);
}
}
}
@@ -1702,7 +1562,7 @@ impl<T: EventListener> ansi::Handler for Term<T> {
#[inline]
fn insert_blank_lines(&mut self, lines: Line) {
trace!("Inserting blank {} lines", lines);
- if self.scroll_region.contains_(self.cursor.point.line) {
+ if self.scroll_region.contains(&self.cursor.point.line) {
let origin = self.cursor.point.line;
self.scroll_down_relative(origin, lines);
}
@@ -1711,7 +1571,7 @@ impl<T: EventListener> ansi::Handler for Term<T> {
#[inline]
fn delete_lines(&mut self, lines: Line) {
trace!("Deleting {} lines", lines);
- if self.scroll_region.contains_(self.cursor.point.line) {
+ if self.scroll_region.contains(&self.cursor.point.line) {
let origin = self.cursor.point.line;
self.scroll_up_relative(origin, lines);
}
@@ -1868,9 +1728,8 @@ impl<T: EventListener> ansi::Handler for Term<T> {
let mut template = self.cursor.template;
template.flags ^= template.flags;
- // Remove active selections and URL highlights
+ // Remove active selections
self.grid.selection = None;
- self.grid.url_highlight = None;
match mode {
ansi::ClearMode::Below => {
@@ -1959,24 +1818,22 @@ impl<T: EventListener> ansi::Handler for Term<T> {
Attr::Reset => {
self.cursor.template.fg = Color::Named(NamedColor::Foreground);
self.cursor.template.bg = Color::Named(NamedColor::Background);
- self.cursor.template.flags = cell::Flags::empty();
- },
- Attr::Reverse => self.cursor.template.flags.insert(cell::Flags::INVERSE),
- Attr::CancelReverse => self.cursor.template.flags.remove(cell::Flags::INVERSE),
- Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD),
- Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD),
- Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM),
- Attr::CancelBoldDim => {
- self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM)
+ self.cursor.template.flags = Flags::empty();
},
- Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC),
- Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC),
- Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE),
- Attr::CancelUnderline => self.cursor.template.flags.remove(cell::Flags::UNDERLINE),
- Attr::Hidden => self.cursor.template.flags.insert(cell::Flags::HIDDEN),
- Attr::CancelHidden => self.cursor.template.flags.remove(cell::Flags::HIDDEN),
- Attr::Strike => self.cursor.template.flags.insert(cell::Flags::STRIKEOUT),
- Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT),
+ Attr::Reverse => self.cursor.template.flags.insert(Flags::INVERSE),
+ Attr::CancelReverse => self.cursor.template.flags.remove(Flags::INVERSE),
+ Attr::Bold => self.cursor.template.flags.insert(Flags::BOLD),
+ Attr::CancelBold => self.cursor.template.flags.remove(Flags::BOLD),
+ Attr::Dim => self.cursor.template.flags.insert(Flags::DIM),
+ Attr::CancelBoldDim => self.cursor.template.flags.remove(Flags::BOLD | Flags::DIM),
+ Attr::Italic => self.cursor.template.flags.insert(Flags::ITALIC),
+ Attr::CancelItalic => self.cursor.template.flags.remove(Flags::ITALIC),
+ Attr::Underline => self.cursor.template.flags.insert(Flags::UNDERLINE),
+ Attr::CancelUnderline => self.cursor.template.flags.remove(Flags::UNDERLINE),
+ Attr::Hidden => self.cursor.template.flags.insert(Flags::HIDDEN),
+ Attr::CancelHidden => self.cursor.template.flags.remove(Flags::HIDDEN),
+ Attr::Strike => self.cursor.template.flags.insert(Flags::STRIKEOUT),
+ Attr::CancelStrike => self.cursor.template.flags.remove(Flags::STRIKEOUT),
_ => {
debug!("Term got unhandled attr: {:?}", attr);
},
@@ -2189,7 +2046,8 @@ mod tests {
use crate::grid::{Grid, Scroll};
use crate::index::{Column, Line, Point, Side};
use crate::selection::Selection;
- use crate::term::{cell, Cell, SizeInfo, Term};
+ use crate::term::cell::{Cell, Flags};
+ use crate::term::{SizeInfo, Term};
struct Mock;
impl EventListener for Mock {
@@ -2217,7 +2075,7 @@ mod tests {
grid[Line(0)][Column(0)].c = '"';
grid[Line(0)][Column(3)].c = '"';
grid[Line(1)][Column(2)].c = '"';
- grid[Line(0)][Column(4)].flags.insert(cell::Flags::WRAPLINE);
+ grid[Line(0)][Column(4)].flags.insert(Flags::WRAPLINE);
let mut escape_chars = String::from("\"");
diff --git a/alacritty_terminal/src/url.rs b/alacritty_terminal/src/url.rs
deleted file mode 100644
index 9e8ecd4b..00000000
--- a/alacritty_terminal/src/url.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use crate::ansi::TermInfo;
-use crate::index::{Column, Linear, Point};
-use crate::term::Term;
-use std::ops::RangeInclusive;
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
-pub struct Url {
- pub start: Point<usize>,
- pub end: Point<usize>,
-}
-
-impl Url {
- pub fn new(start: Point<usize>, length: usize, num_cols: usize) -> Self {
- let unwrapped_end_col = start.col.0 + length - 1;
- let end_col = unwrapped_end_col % num_cols;
- let end_line = start.line - unwrapped_end_col / num_cols;
-
- Url { end: Point::new(end_line, Column(end_col)), start }
- }
-
- /// Check if point is within this URL
- pub fn contains(&self, point: impl Into<Point<usize>>) -> bool {
- let point = point.into();
-
- point.line <= self.start.line
- && point.line >= self.end.line
- && (point.line != self.start.line || point.col >= self.start.col)
- && (point.line != self.end.line || point.col <= self.end.col)
- }
-
- /// Convert URLs bounding points to linear indices
- pub fn linear_bounds<T>(&self, terminal: &Term<T>) -> RangeInclusive<Linear> {
- let mut start = self.start;
- let mut end = self.end;
-
- start = terminal.buffer_to_visible(start);
- end = terminal.buffer_to_visible(end);
-
- let start = Linear::from_point(terminal.cols(), start);
- let end = Linear::from_point(terminal.cols(), end);
-
- RangeInclusive::new(start, end)
- }
-}