summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/term/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty_terminal/src/term/mod.rs')
-rw-r--r--alacritty_terminal/src/term/mod.rs362
1 files changed, 110 insertions, 252 deletions
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("\"");