diff options
Diffstat (limited to 'alacritty_terminal')
-rw-r--r-- | alacritty_terminal/src/config/colors.rs | 2 | ||||
-rw-r--r-- | alacritty_terminal/src/config/mod.rs | 32 | ||||
-rw-r--r-- | alacritty_terminal/src/grid/mod.rs | 12 | ||||
-rw-r--r-- | alacritty_terminal/src/index.rs | 41 | ||||
-rw-r--r-- | alacritty_terminal/src/lib.rs | 1 | ||||
-rw-r--r-- | alacritty_terminal/src/selection.rs | 106 | ||||
-rw-r--r-- | alacritty_terminal/src/term/mod.rs | 456 | ||||
-rw-r--r-- | alacritty_terminal/src/vi_mode.rs | 799 |
8 files changed, 1215 insertions, 234 deletions
diff --git a/alacritty_terminal/src/config/colors.rs b/alacritty_terminal/src/config/colors.rs index 35c03684..5c057619 100644 --- a/alacritty_terminal/src/config/colors.rs +++ b/alacritty_terminal/src/config/colors.rs @@ -12,6 +12,8 @@ pub struct Colors { #[serde(deserialize_with = "failure_default")] pub cursor: CursorColors, #[serde(deserialize_with = "failure_default")] + pub vi_mode_cursor: CursorColors, + #[serde(deserialize_with = "failure_default")] pub selection: SelectionColors, #[serde(deserialize_with = "failure_default")] normal: NormalColors, diff --git a/alacritty_terminal/src/config/mod.rs b/alacritty_terminal/src/config/mod.rs index da95391c..df8d37bd 100644 --- a/alacritty_terminal/src/config/mod.rs +++ b/alacritty_terminal/src/config/mod.rs @@ -28,7 +28,7 @@ mod scrolling; mod visual_bell; mod window; -use crate::ansi::{Color, CursorStyle, NamedColor}; +use crate::ansi::{CursorStyle, NamedColor}; pub use crate::config::colors::Colors; pub use crate::config::debug::Debug; @@ -170,16 +170,28 @@ impl<T> Config<T> { self.dynamic_title.0 } - /// Cursor foreground color + /// Cursor foreground color. #[inline] pub fn cursor_text_color(&self) -> Option<Rgb> { self.colors.cursor.text } - /// Cursor background color + /// Cursor background color. #[inline] - pub fn cursor_cursor_color(&self) -> Option<Color> { - self.colors.cursor.cursor.map(|_| Color::Named(NamedColor::Cursor)) + pub fn cursor_cursor_color(&self) -> Option<NamedColor> { + self.colors.cursor.cursor.map(|_| NamedColor::Cursor) + } + + /// Vi mode cursor foreground color. + #[inline] + pub fn vi_mode_cursor_text_color(&self) -> Option<Rgb> { + self.colors.vi_mode_cursor.text + } + + /// Vi mode cursor background color. + #[inline] + pub fn vi_mode_cursor_cursor_color(&self) -> Option<Rgb> { + self.colors.vi_mode_cursor.cursor } #[inline] @@ -230,20 +242,16 @@ impl Default for EscapeChars { } #[serde(default)] -#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)] +#[derive(Deserialize, Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct Cursor { #[serde(deserialize_with = "failure_default")] pub style: CursorStyle, + #[serde(deserialize_with = "option_explicit_none")] + pub vi_mode_style: Option<CursorStyle>, #[serde(deserialize_with = "failure_default")] unfocused_hollow: DefaultTrueBool, } -impl Default for Cursor { - fn default() -> Self { - Self { style: Default::default(), unfocused_hollow: Default::default() } - } -} - impl Cursor { pub fn unfocused_hollow(self) -> bool { self.unfocused_hollow.0 diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs index 34d989db..37cf0eb6 100644 --- a/alacritty_terminal/src/grid/mod.rs +++ b/alacritty_terminal/src/grid/mod.rs @@ -264,11 +264,9 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> { let mut new_empty_lines = 0; let mut reversed: Vec<Row<T>> = Vec::with_capacity(self.raw.len()); for (i, mut row) in self.raw.drain().enumerate().rev() { - // FIXME: Rust 1.39.0+ allows moving in pattern guard here // Check if reflowing shoud be performed - let mut last_row = reversed.last_mut(); - let last_row = match last_row { - Some(ref mut last_row) if should_reflow(last_row) => last_row, + let last_row = match reversed.last_mut() { + Some(last_row) if should_reflow(last_row) => last_row, _ => { reversed.push(row); continue; @@ -356,11 +354,9 @@ impl<T: GridCell + PartialEq + Copy> Grid<T> { } loop { - // FIXME: Rust 1.39.0+ allows moving in pattern guard here // Check if reflowing shoud be performed - let wrapped = row.shrink(cols); - let mut wrapped = match wrapped { - Some(_) if reflow => wrapped.unwrap(), + let mut wrapped = match row.shrink(cols) { + Some(wrapped) if reflow => wrapped, _ => { new_raw.push(row); break; diff --git a/alacritty_terminal/src/index.rs b/alacritty_terminal/src/index.rs index 56d32003..1334a74e 100644 --- a/alacritty_terminal/src/index.rs +++ b/alacritty_terminal/src/index.rs @@ -30,6 +30,15 @@ pub enum Side { Right, } +impl Side { + pub fn opposite(self) -> Self { + match self { + Side::Right => Side::Left, + Side::Left => Side::Right, + } + } +} + /// Index in the grid using row, column notation #[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, PartialOrd)] pub struct Point<L = Line> { @@ -49,7 +58,7 @@ impl<L> Point<L> { L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>, { let line_changes = - f32::ceil(rhs.saturating_sub(self.col.0) as f32 / num_cols as f32) as usize; + (rhs.saturating_sub(self.col.0) as f32 / num_cols as f32).ceil() as usize; if self.line.into() > Line(line_changes) { self.line = self.line - line_changes; } else { @@ -63,12 +72,40 @@ impl<L> Point<L> { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn add(mut self, num_cols: usize, rhs: usize) -> Point<L> where - L: Add<usize, Output = L> + Sub<usize, Output = L>, + L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>, { self.line = self.line + (rhs + self.col.0) / num_cols; self.col = Column((self.col.0 + rhs) % num_cols); self } + + #[inline] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn sub_absolute(mut self, num_cols: usize, rhs: usize) -> Point<L> + where + L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>, + { + self.line = + self.line + (rhs.saturating_sub(self.col.0) as f32 / num_cols as f32).ceil() as usize; + self.col = Column((num_cols + self.col.0 - rhs % num_cols) % num_cols); + self + } + + #[inline] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn add_absolute(mut self, num_cols: usize, rhs: usize) -> Point<L> + where + L: Copy + Default + Into<Line> + Add<usize, Output = L> + Sub<usize, Output = L>, + { + let line_changes = (rhs + self.col.0) / num_cols; + if self.line.into() > Line(line_changes) { + self.line = self.line - line_changes; + } else { + self.line = Default::default(); + } + self.col = Column((self.col.0 + rhs) % num_cols); + self + } } impl Ord for Point { diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index 039f2b81..6991ffdc 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -37,6 +37,7 @@ pub mod sync; pub mod term; pub mod tty; pub mod util; +pub mod vi_mode; pub use crate::grid::Grid; pub use crate::term::Term; diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs index f663417f..369846cf 100644 --- a/alacritty_terminal/src/selection.rs +++ b/alacritty_terminal/src/selection.rs @@ -27,7 +27,7 @@ use crate::term::{Search, Term}; /// A Point and side within that point. #[derive(Debug, Copy, Clone, PartialEq)] -pub struct Anchor { +struct Anchor { point: Point<usize>, side: Side, } @@ -67,7 +67,7 @@ impl<L> SelectionRange<L> { /// Different kinds of selection. #[derive(Debug, Copy, Clone, PartialEq)] -enum SelectionType { +pub enum SelectionType { Simple, Block, Semantic, @@ -94,48 +94,20 @@ enum SelectionType { /// [`update`]: enum.Selection.html#method.update #[derive(Debug, Clone, PartialEq)] pub struct Selection { + pub ty: SelectionType, region: Range<Anchor>, - ty: SelectionType, } impl Selection { - pub fn simple(location: Point<usize>, side: Side) -> Selection { + pub fn new(ty: SelectionType, location: Point<usize>, side: Side) -> Selection { Self { region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) }, - ty: SelectionType::Simple, + ty, } } - pub fn block(location: Point<usize>, side: Side) -> Selection { - Self { - region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) }, - ty: SelectionType::Block, - } - } - - pub fn semantic(location: Point<usize>) -> Selection { - Self { - region: Range { - start: Anchor::new(location, Side::Left), - end: Anchor::new(location, Side::Right), - }, - ty: SelectionType::Semantic, - } - } - - pub fn lines(location: Point<usize>) -> Selection { - Self { - region: Range { - start: Anchor::new(location, Side::Left), - end: Anchor::new(location, Side::Right), - }, - ty: SelectionType::Lines, - } - } - - pub fn update(&mut self, location: Point<usize>, side: Side) { - self.region.end.point = location; - self.region.end.side = side; + pub fn update(&mut self, point: Point<usize>, side: Side) { + self.region.end = Anchor::new(point, side); } pub fn rotate( @@ -233,6 +205,24 @@ impl Selection { } } + /// Expand selection sides to include all cells. + pub fn include_all(&mut self) { + let (start, end) = (self.region.start.point, self.region.end.point); + let (start_side, end_side) = match self.ty { + SelectionType::Block + if start.col > end.col || (start.col == end.col && start.line < end.line) => + { + (Side::Right, Side::Left) + }, + SelectionType::Block => (Side::Left, Side::Right), + _ if Self::points_need_swap(start, end) => (Side::Right, Side::Left), + _ => (Side::Left, Side::Right), + }; + + self.region.start.side = start_side; + self.region.end.side = end_side; + } + /// Convert selection to grid coordinates. pub fn to_range<T>(&self, term: &Term<T>) -> Option<SelectionRange> { let grid = term.grid(); @@ -392,7 +382,8 @@ impl Selection { /// look like [ B] and [E ]. #[cfg(test)] mod tests { - use super::{Selection, SelectionRange}; + use super::*; + use crate::clipboard::Clipboard; use crate::config::MockConfig; use crate::event::{Event, EventListener}; @@ -425,7 +416,7 @@ mod tests { #[test] fn single_cell_left_to_right() { let location = Point { line: 0, col: Column(0) }; - let mut selection = Selection::simple(location, Side::Left); + let mut selection = Selection::new(SelectionType::Simple, location, Side::Left); selection.update(location, Side::Right); assert_eq!(selection.to_range(&term(1, 1)).unwrap(), SelectionRange { @@ -443,7 +434,7 @@ mod tests { #[test] fn single_cell_right_to_left() { let location = Point { line: 0, col: Column(0) }; - let mut selection = Selection::simple(location, Side::Right); + let mut selection = Selection::new(SelectionType::Simple, location, Side::Right); selection.update(location, Side::Left); assert_eq!(selection.to_range(&term(1, 1)).unwrap(), SelectionRange { @@ -460,7 +451,8 @@ mod tests { /// 3. [ B][E ] #[test] fn between_adjacent_cells_left_to_right() { - let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(0, Column(0)), Side::Right); selection.update(Point::new(0, Column(1)), Side::Left); assert_eq!(selection.to_range(&term(2, 1)), None); @@ -473,7 +465,8 @@ mod tests { /// 3. [ E][B ] #[test] fn between_adjacent_cells_right_to_left() { - let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(0, Column(1)), Side::Left); selection.update(Point::new(0, Column(0)), Side::Right); assert_eq!(selection.to_range(&term(2, 1)), None); @@ -489,7 +482,8 @@ mod tests { /// [XX][XE][ ][ ][ ] #[test] fn across_adjacent_lines_upward_final_cell_exclusive() { - let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(1, Column(1)), Side::Right); selection.update(Point::new(0, Column(1)), Side::Right); assert_eq!(selection.to_range(&term(5, 2)).unwrap(), SelectionRange { @@ -511,7 +505,8 @@ mod tests { /// [XX][XB][ ][ ][ ] #[test] fn selection_bigger_then_smaller() { - let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Right); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(0, Column(1)), Side::Right); selection.update(Point::new(1, Column(1)), Side::Right); selection.update(Point::new(1, Column(0)), Side::Right); @@ -526,7 +521,8 @@ mod tests { fn line_selection() { let num_lines = 10; let num_cols = 5; - let mut selection = Selection::lines(Point::new(0, Column(1))); + let mut selection = + Selection::new(SelectionType::Lines, Point::new(0, Column(1)), Side::Left); selection.update(Point::new(5, Column(1)), Side::Right); selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap(); @@ -541,7 +537,8 @@ mod tests { fn semantic_selection() { let num_lines = 10; let num_cols = 5; - let mut selection = Selection::semantic(Point::new(0, Column(3))); + let mut selection = + Selection::new(SelectionType::Semantic, Point::new(0, Column(3)), Side::Left); selection.update(Point::new(5, Column(1)), Side::Right); selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap(); @@ -556,7 +553,8 @@ mod tests { fn simple_selection() { let num_lines = 10; let num_cols = 5; - let mut selection = Selection::simple(Point::new(0, Column(3)), Side::Right); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(0, Column(3)), Side::Right); selection.update(Point::new(5, Column(1)), Side::Right); selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap(); @@ -571,7 +569,8 @@ mod tests { fn block_selection() { let num_lines = 10; let num_cols = 5; - let mut selection = Selection::block(Point::new(0, Column(3)), Side::Right); + let mut selection = + Selection::new(SelectionType::Block, Point::new(0, Column(3)), Side::Right); selection.update(Point::new(5, Column(1)), Side::Right); selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap(); @@ -584,7 +583,8 @@ mod tests { #[test] fn simple_is_empty() { - let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(0, Column(0)), Side::Right); assert!(selection.is_empty()); selection.update(Point::new(0, Column(1)), Side::Left); assert!(selection.is_empty()); @@ -594,7 +594,8 @@ mod tests { #[test] fn block_is_empty() { - let mut selection = Selection::block(Point::new(0, Column(0)), Side::Right); + let mut selection = + Selection::new(SelectionType::Block, Point::new(0, Column(0)), Side::Right); assert!(selection.is_empty()); selection.update(Point::new(0, Column(1)), Side::Left); assert!(selection.is_empty()); @@ -612,7 +613,8 @@ mod tests { fn rotate_in_region_up() { let num_lines = 10; let num_cols = 5; - let mut selection = Selection::simple(Point::new(2, Column(3)), Side::Right); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(2, Column(3)), Side::Right); selection.update(Point::new(5, Column(1)), Side::Right); selection = selection.rotate(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), 4).unwrap(); @@ -628,7 +630,8 @@ mod tests { fn rotate_in_region_down() { let num_lines = 10; let num_cols = 5; - let mut selection = Selection::simple(Point::new(5, Column(3)), Side::Right); + let mut selection = + Selection::new(SelectionType::Simple, Point::new(5, Column(3)), Side::Right); selection.update(Point::new(8, Column(1)), Side::Left); selection = selection.rotate(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), -5).unwrap(); @@ -644,7 +647,8 @@ mod tests { fn rotate_in_region_up_block() { let num_lines = 10; let num_cols = 5; - let mut selection = Selection::block(Point::new(2, Column(3)), Side::Right); + let mut selection = + Selection::new(SelectionType::Block, Point::new(2, Column(3)), Side::Right); selection.update(Point::new(5, Column(1)), Side::Right); selection = selection.rotate(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), 4).unwrap(); diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index ac5e56b5..89c3723f 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -31,10 +31,11 @@ use crate::event::{Event, EventListener}; use crate::grid::{ BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll, }; -use crate::index::{self, Column, IndexRange, Line, Point}; +use crate::index::{self, Column, IndexRange, Line, Point, Side}; use crate::selection::{Selection, SelectionRange}; use crate::term::cell::{Cell, Flags, LineLength}; use crate::term::color::Rgb; +use crate::vi_mode::{ViModeCursor, ViMotion}; pub mod cell; pub mod color; @@ -180,7 +181,17 @@ impl<T> Search for Term<T> { } } -/// A key for caching cursor glyphs +/// Cursor storing all information relevant for rendering. +#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize)] +struct RenderableCursor { + text_color: Option<Rgb>, + cursor_color: Option<Rgb>, + key: CursorKey, + point: Point, + rendered: bool, +} + +/// A key for caching cursor glyphs. #[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Deserialize)] pub struct CursorKey { pub style: CursorStyle, @@ -198,10 +209,7 @@ pub struct CursorKey { pub struct RenderableCellsIter<'a, C> { inner: DisplayIter<'a, Cell>, grid: &'a Grid<Cell>, - cursor: &'a Point, - cursor_offset: usize, - cursor_key: Option<CursorKey>, - cursor_style: CursorStyle, + cursor: RenderableCursor, config: &'a Config<C>, colors: &'a color::List, selection: Option<SelectionRange<Line>>, @@ -216,12 +224,10 @@ impl<'a, C> RenderableCellsIter<'a, C> { term: &'b Term<T>, config: &'b Config<C>, selection: Option<SelectionRange>, - mut cursor_style: CursorStyle, ) -> RenderableCellsIter<'b, C> { let grid = &term.grid; let num_cols = grid.num_cols(); - let cursor_offset = grid.num_lines().0 - term.cursor.point.line.0 - 1; let inner = grid.display_iter(); let selection_range = selection.and_then(|span| { @@ -242,29 +248,13 @@ impl<'a, C> RenderableCellsIter<'a, C> { Some(SelectionRange::new(start, end, span.is_block)) }); - // Load cursor glyph - 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(Flags::WIDE_CHAR) && (cursor.col + 1) < num_cols; - Some(CursorKey { style: cursor_style, is_wide }) - } else { - // Use hidden cursor so text will not get inverted - cursor_style = CursorStyle::Hidden; - None - }; - RenderableCellsIter { - cursor, - cursor_offset, + cursor: term.renderable_cursor(config), grid, inner, selection: selection_range, config, colors: &term.colors, - cursor_key, - cursor_style, } } @@ -275,6 +265,18 @@ impl<'a, C> RenderableCellsIter<'a, C> { None => return false, }; + // Do not invert block cursor at selection boundaries + if self.cursor.key.style == CursorStyle::Block + && self.cursor.point == point + && (selection.start == point + || selection.end == point + || (selection.is_block + && ((selection.start.line == point.line && selection.end.col == point.col) + || (selection.end.line == point.line && selection.start.col == point.col)))) + { + return false; + } + // Point itself is selected if selection.contains(point.col, point.line) { return true; @@ -442,43 +444,46 @@ impl<'a, C> Iterator for RenderableCellsIter<'a, C> { #[inline] fn next(&mut self) -> Option<Self::Item> { loop { - if self.cursor_offset == self.inner.offset() && self.inner.column() == self.cursor.col { - let selected = self.is_selected(Point::new(self.cursor.line, self.cursor.col)); + if self.cursor.point.line == self.inner.line() + && self.cursor.point.col == self.inner.column() + { + let selected = self.is_selected(self.cursor.point); + + // Handle cell below cursor + if self.cursor.rendered { + let mut cell = + RenderableCell::new(self.config, self.colors, self.inner.next()?, selected); - // Handle cursor - if let Some(cursor_key) = self.cursor_key.take() { + if self.cursor.key.style == CursorStyle::Block { + mem::swap(&mut cell.bg, &mut cell.fg); + + if let Some(color) = self.cursor.text_color { + cell.fg = color; + } + } + + return Some(cell); + } else { + // Handle cursor + self.cursor.rendered = true; + + let buffer_point = self.grid.visible_to_buffer(self.cursor.point); let cell = Indexed { - inner: self.grid[self.cursor], - column: self.cursor.col, - // Using `self.cursor.line` leads to inconsitent cursor position when - // scrolling. See https://github.com/alacritty/alacritty/issues/2570 for more - // info. - line: self.inner.line(), + inner: self.grid[buffer_point.line][buffer_point.col], + column: self.cursor.point.col, + line: self.cursor.point.line, }; let mut renderable_cell = RenderableCell::new(self.config, self.colors, cell, selected); - renderable_cell.inner = RenderableCellContent::Cursor(cursor_key); + renderable_cell.inner = RenderableCellContent::Cursor(self.cursor.key); - if let Some(color) = self.config.cursor_cursor_color() { - renderable_cell.fg = RenderableCell::compute_bg_rgb(self.colors, color); + if let Some(color) = self.cursor.cursor_color { + renderable_cell.fg = color; } return Some(renderable_cell); - } else { - let mut cell = - RenderableCell::new(self.config, self.colors, self.inner.next()?, selected); - - if self.cursor_style == CursorStyle::Block { - std::mem::swap(&mut cell.bg, &mut cell.fg); - - if let Some(color) = self.config.cursor_text_color() { - cell.fg = color; - } - } - - return Some(cell); } } else { let cell = self.inner.next()?; @@ -497,26 +502,27 @@ pub mod mode { use bitflags::bitflags; bitflags! { - pub struct TermMode: u16 { - const SHOW_CURSOR = 0b0000_0000_0000_0001; - const APP_CURSOR = 0b0000_0000_0000_0010; - const APP_KEYPAD = 0b0000_0000_0000_0100; - const MOUSE_REPORT_CLICK = 0b0000_0000_0000_1000; - const BRACKETED_PASTE = 0b0000_0000_0001_0000; - const SGR_MOUSE = 0b0000_0000_0010_0000; - const MOUSE_MOTION = 0b0000_0000_0100_0000; - const LINE_WRAP = 0b0000_0000_1000_0000; - const LINE_FEED_NEW_LINE = 0b0000_0001_0000_0000; - const ORIGIN = 0b0000_0010_0000_0000; - const INSERT = 0b0000_0100_0000_0000; - const FOCUS_IN_OUT = 0b0000_1000_0000_0000; - const ALT_SCREEN = 0b0001_0000_0000_0000; - const MOUSE_DRAG = 0b0010_0000_0000_0000; - const MOUSE_MODE = 0b0010_0000_0100_1000; - const UTF8_MOUSE = 0b0100_0000_0000_0000; - const ALTERNATE_SCROLL = 0b1000_0000_0000_0000; - const ANY = 0b1111_1111_1111_1111; + pub struct TermMode: u32 { const NONE = 0; + const SHOW_CURSOR = 0b0000_0000_0000_0000_0001; + const APP_CURSOR = 0b0000_0000_0000_0000_0010; + const APP_KEYPAD = 0b0000_0000_0000_0000_0100; + const MOUSE_REPORT_CLICK = 0b0000_0000_0000_0000_1000; + const BRACKETED_PASTE = 0b0000_0000_0000_0001_0000; + const SGR_MOUSE = 0b0000_0000_0000_0010_0000; + const MOUSE_MOTION = 0b0000_0000_0000_0100_0000; + const LINE_WRAP = 0b0000_0000_0000_1000_0000; + const LINE_FEED_NEW_LINE = 0b0000_0000_0001_0000_0000; + const ORIGIN = 0b0000_0000_0010_0000_0000; + const INSERT = 0b0000_0000_0100_0000_0000; + const FOCUS_IN_OUT = 0b0000_0000_1000_0000_0000; + const ALT_SCREEN = 0b0000_0001_0000_0000_0000; + const MOUSE_DRAG = 0b0000_0010_0000_0000_0000; + const MOUSE_MODE = 0b0000_0010_0000_0100_1000; + const UTF8_MOUSE = 0b0000_0100_0000_0000_0000; + const ALTERNATE_SCROLL = 0b0000_1000_0000_0000_0000; + const VI = 0b0001_0000_0000_0000_0000; + const ANY = std::u32::MAX; } } @@ -730,11 +736,69 @@ impl VisualBell { } } +/// Terminal size info. +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] +pub struct SizeInfo { + /// Terminal window width. + pub width: f32, + + /// Terminal window height. + pub height: f32, + + /// Width of individual cell. + pub cell_width: f32, + + /// Height of individual cell. + pub cell_height: f32, + + /// Horizontal window padding. + pub padding_x: f32, + + /// Horizontal window padding. + pub padding_y: f32, + + /// DPI factor of the current window. + #[serde(default)] + pub dpr: f64, +} + +impl SizeInfo { + #[inline] + pub fn lines(&self) -> Line { + Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize) + } + + #[inline] + pub fn cols(&self) -> Column { + Column(((self.width - 2. * self.padding_x) / self.cell_width) 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 { + let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize)); + let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize)); + + Point { + line: min(line, Line(self.lines().saturating_sub(1))), + col: min(col, Column(self.cols().saturating_sub(1))), + } + } +} + pub struct Term<T> { - /// Terminal focus + /// Terminal focus. pub is_focused: bool, - /// The grid + /// The grid. grid: Grid<Cell>, /// Tracks if the next call to input will need to first handle wrapping. @@ -744,23 +808,25 @@ pub struct Term<T> { /// arrays. Without it we would have to sanitize cursor.col every time we used it. input_needs_wrap: bool, - /// Alternate grid + /// Alternate grid. alt_grid: Grid<Cell>, - /// Alt is active + /// Alt is active. alt: bool, - /// The cursor + /// The cursor. cursor: Cursor, - /// The graphic character set, out of `charsets`, which ASCII is currently - /// being mapped to + /// Cursor location for vi mode. + pub vi_mode_cursor: ViModeCursor, + + /// Index into `charsets`, pointing to what ASCII is currently being mapped to. active_charset: CharsetIndex, - /// Tabstops + /// Tabstops. tabs: TabStops, - /// Mode flags + /// Mode flags. mode: TermMode, /// Scroll region. @@ -772,33 +838,36 @@ pub struct Term<T> { pub visual_bell: VisualBell, - /// Saved cursor from main grid + /// Saved cursor from main grid. cursor_save: Cursor, - /// Saved cursor from alt grid + /// Saved cursor from alt grid. cursor_save_alt: Cursor, semantic_escape_chars: String, - /// Colors used for rendering + /// Colors used for rendering. colors: color::List, - /// Is color in `colors` modified or not + /// Is color in `colors` modified or not. color_modified: [bool; color::COUNT], - /// Original colors from config + /// Original colors from config. original_colors: color::List, - /// Current style of the cursor + /// Current style of the cursor. cursor_style: Option<CursorStyle>, - /// Default style for resetting the cursor + /// Default style for resetting the cursor. default_cursor_style: CursorStyle, + /// Style of the vi mode cursor. + vi_mode_cursor_style: Option<CursorStyle>, + /// Clipboard access coupled to the active window clipboard: Clipboard, - /// Proxy for sending events to the event loop + /// Proxy for sending events to the event loop. event_proxy: T, /// Current title of the window. @@ -815,64 +884,6 @@ pub struct Term<T> { title_stack: Vec<Option<String>>, } -/// Terminal size info -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] -pub struct SizeInfo { - /// Terminal window width - pub width: f32, - - /// Terminal window height - pub height: f32, - - /// Width of individual cell - pub cell_width: f32, - - /// Height of individual cell - pub cell_height: f32, - - /// Horizontal window padding - pub padding_x: f32, - - /// Horizontal window padding - pub padding_y: f32, - - /// DPI factor of the current window - #[serde(default)] - pub dpr: f64, -} - -impl SizeInfo { - #[inline] - pub fn lines(&self) -> Line { - Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize) - } - - #[inline] |