summaryrefslogtreecommitdiffstats
path: root/alacritty_terminal
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2021-01-24 21:45:36 +0000
committerGitHub <noreply@github.com>2021-01-24 21:45:36 +0000
commit530de00049c2afcc562d36ccdb3e6afa2fe396a5 (patch)
tree3dabbcef3fc4a2041f9027d82243aa0d70928153 /alacritty_terminal
parent7291702f6b4fff10f2470f084abe0785b95659a0 (diff)
Move renderable cell transformation to alacritty
This refactors a large chunk of the alacritty_terminal API to expose all data necessary for rendering uniformly through the `renderable_content` call. This also no longer transforms the cells for rendering by a GUI but instead just reports the content from a terminal emulation perspective. The transformation into renderable cells is now done inside the alacritty crate. Since the terminal itself only ever needs to know about modified color RGB values, the configuration for colors was moved to the alacritty UI code.
Diffstat (limited to 'alacritty_terminal')
-rw-r--r--alacritty_terminal/Cargo.toml2
-rw-r--r--alacritty_terminal/src/ansi.rs10
-rw-r--r--alacritty_terminal/src/config/bell.rs70
-rw-r--r--alacritty_terminal/src/config/colors.rs213
-rw-r--r--alacritty_terminal/src/config/mod.rs28
-rw-r--r--alacritty_terminal/src/event.rs38
-rw-r--r--alacritty_terminal/src/grid/mod.rs506
-rw-r--r--alacritty_terminal/src/grid/resize.rs26
-rw-r--r--alacritty_terminal/src/grid/tests.rs28
-rw-r--r--alacritty_terminal/src/index.rs51
-rw-r--r--alacritty_terminal/src/selection.rs125
-rw-r--r--alacritty_terminal/src/term/color.rs203
-rw-r--r--alacritty_terminal/src/term/mod.rs529
-rw-r--r--alacritty_terminal/src/term/render.rs431
-rw-r--r--alacritty_terminal/src/term/search.rs232
-rw-r--r--alacritty_terminal/src/vi_mode.rs42
-rw-r--r--alacritty_terminal/tests/ref.rs2
17 files changed, 681 insertions, 1855 deletions
diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml
index ee228a3f..41b8b1f8 100644
--- a/alacritty_terminal/Cargo.toml
+++ b/alacritty_terminal/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "alacritty_terminal"
-version = "0.12.1-dev"
+version = "0.13.0-dev"
authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"]
license = "Apache-2.0"
description = "Library for writing terminal emulators"
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index 2bd445ea..8f2264af 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -306,7 +306,7 @@ pub trait Handler {
fn set_color(&mut self, _: usize, _: Rgb) {}
/// Write a foreground/background color escape sequence with the current color.
- fn dynamic_color_sequence<W: io::Write>(&mut self, _: &mut W, _: u8, _: usize, _: &str) {}
+ fn dynamic_color_sequence(&mut self, _: u8, _: usize, _: &str) {}
/// Reset an indexed color to original value.
fn reset_color(&mut self, _: usize) {}
@@ -778,10 +778,10 @@ where
}
#[inline]
- fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, _c: char) {
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, action: char) {
debug!(
- "[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
- params, intermediates, ignore
+ "[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}, action: {:?}",
+ params, intermediates, ignore, action
);
}
@@ -798,7 +798,6 @@ where
// TODO replace OSC parsing with parser combinators.
#[inline]
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
- let writer = &mut self.writer;
let terminator = if bell_terminated { "\x07" } else { "\x1b\\" };
fn unhandled(params: &[&[u8]]) {
@@ -868,7 +867,6 @@ where
self.handler.set_color(index, color);
} else if param == b"?" {
self.handler.dynamic_color_sequence(
- writer,
dynamic_code,
index,
terminator,
diff --git a/alacritty_terminal/src/config/bell.rs b/alacritty_terminal/src/config/bell.rs
deleted file mode 100644
index 825a7b1f..00000000
--- a/alacritty_terminal/src/config/bell.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use std::time::Duration;
-
-use alacritty_config_derive::ConfigDeserialize;
-
-use crate::config::Program;
-use crate::term::color::Rgb;
-
-#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
-pub struct BellConfig {
- /// Visual bell animation function.
- pub animation: BellAnimation,
-
- /// Command to run on bell.
- pub command: Option<Program>,
-
- /// Visual bell flash color.
- pub color: Rgb,
-
- /// Visual bell duration in milliseconds.
- duration: u16,
-}
-
-impl Default for BellConfig {
- fn default() -> Self {
- Self {
- color: Rgb { r: 255, g: 255, b: 255 },
- animation: Default::default(),
- command: Default::default(),
- duration: Default::default(),
- }
- }
-}
-
-impl BellConfig {
- pub fn duration(&self) -> Duration {
- Duration::from_millis(self.duration as u64)
- }
-}
-
-/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert
-/// Penner's Easing Functions.
-#[derive(ConfigDeserialize, Clone, Copy, Debug, PartialEq, Eq)]
-pub enum BellAnimation {
- // CSS animation.
- Ease,
- // CSS animation.
- EaseOut,
- // Penner animation.
- EaseOutSine,
- // Penner animation.
- EaseOutQuad,
- // Penner animation.
- EaseOutCubic,
- // Penner animation.
- EaseOutQuart,
- // Penner animation.
- EaseOutQuint,
- // Penner animation.
- EaseOutExpo,
- // Penner animation.
- EaseOutCirc,
- // Penner animation.
- Linear,
-}
-
-impl Default for BellAnimation {
- fn default() -> Self {
- BellAnimation::EaseOutExpo
- }
-}
diff --git a/alacritty_terminal/src/config/colors.rs b/alacritty_terminal/src/config/colors.rs
deleted file mode 100644
index 88ca5057..00000000
--- a/alacritty_terminal/src/config/colors.rs
+++ /dev/null
@@ -1,213 +0,0 @@
-use serde::de::Error as SerdeError;
-use serde::{Deserialize, Deserializer};
-
-use alacritty_config_derive::ConfigDeserialize;
-
-use crate::term::color::{CellRgb, Rgb};
-
-#[derive(ConfigDeserialize, Clone, Debug, Default, PartialEq, Eq)]
-pub struct Colors {
- pub primary: PrimaryColors,
- pub cursor: InvertedCellColors,
- pub vi_mode_cursor: InvertedCellColors,
- pub selection: InvertedCellColors,
- pub normal: NormalColors,
- pub bright: BrightColors,
- pub dim: Option<DimColors>,
- pub indexed_colors: Vec<IndexedColor>,
- pub search: SearchColors,
- pub line_indicator: LineIndicatorColors,
-}
-
-impl Colors {
- pub fn search_bar_foreground(&self) -> Rgb {
- self.search.bar.foreground.unwrap_or(self.primary.background)
- }
-
- pub fn search_bar_background(&self) -> Rgb {
- self.search.bar.background.unwrap_or(self.primary.foreground)
- }
-}
-
-#[derive(ConfigDeserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
-pub struct LineIndicatorColors {
- pub foreground: Option<Rgb>,
- pub background: Option<Rgb>,
-}
-
-#[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
-pub struct IndexedColor {
- pub color: Rgb,
-
- index: ColorIndex,
-}
-
-impl IndexedColor {
- #[inline]
- pub fn index(&self) -> u8 {
- self.index.0
- }
-}
-
-#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
-struct ColorIndex(u8);
-
-impl<'de> Deserialize<'de> for ColorIndex {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- let index = u8::deserialize(deserializer)?;
-
- if index < 16 {
- Err(SerdeError::custom(
- "Config error: indexed_color's index is {}, but a value bigger than 15 was \
- expected; ignoring setting",
- ))
- } else {
- Ok(Self(index))
- }
- }
-}
-
-#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
-pub struct InvertedCellColors {
- #[config(alias = "text")]
- pub foreground: CellRgb,
- #[config(alias = "cursor")]
- pub background: CellRgb,
-}
-
-impl Default for InvertedCellColors {
- fn default() -> Self {
- Self { foreground: CellRgb::CellBackground, background: CellRgb::CellForeground }
- }
-}
-
-#[derive(ConfigDeserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
-pub struct SearchColors {
- pub focused_match: InvertedCellColors,
- pub matches: MatchColors,
- bar: BarColors,
-}
-
-#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
-pub struct MatchColors {
- pub foreground: CellRgb,
- pub background: CellRgb,
-}
-
-impl Default for MatchColors {
- fn default() -> Self {
- Self {
- background: CellRgb::Rgb(Rgb { r: 0xff, g: 0xff, b: 0xff }),
- foreground: CellRgb::Rgb(Rgb { r: 0x00, g: 0x00, b: 0x00 }),
- }
- }
-}
-
-#[derive(ConfigDeserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
-pub struct BarColors {
- foreground: Option<Rgb>,
- background: Option<Rgb>,
-}
-
-#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
-pub struct PrimaryColors {
- pub foreground: Rgb,
- pub background: Rgb,
- pub bright_foreground: Option<Rgb>,
- pub dim_foreground: Option<Rgb>,
-}
-
-impl Default for PrimaryColors {
- fn default() -> Self {
- PrimaryColors {
- background: Rgb { r: 0x1d, g: 0x1f, b: 0x21 },
- foreground: Rgb { r: 0xc5, g: 0xc8, b: 0xc6 },
- bright_foreground: Default::default(),
- dim_foreground: Default::default(),
- }
- }
-}
-
-#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
-pub struct NormalColors {
- pub black: Rgb,
- pub red: Rgb,
- pub green: Rgb,
- pub yellow: Rgb,
- pub blue: Rgb,
- pub magenta: Rgb,
- pub cyan: Rgb,
- pub white: Rgb,
-}
-
-impl Default for NormalColors {
- fn default() -> Self {
- NormalColors {
- black: Rgb { r: 0x1d, g: 0x1f, b: 0x21 },
- red: Rgb { r: 0xcc, g: 0x66, b: 0x66 },
- green: Rgb { r: 0xb5, g: 0xbd, b: 0x68 },
- yellow: Rgb { r: 0xf0, g: 0xc6, b: 0x74 },
- blue: Rgb { r: 0x81, g: 0xa2, b: 0xbe },
- magenta: Rgb { r: 0xb2, g: 0x94, b: 0xbb },
- cyan: Rgb { r: 0x8a, g: 0xbe, b: 0xb7 },
- white: Rgb { r: 0xc5, g: 0xc8, b: 0xc6 },
- }
- }
-}
-
-#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
-pub struct BrightColors {
- pub black: Rgb,
- pub red: Rgb,
- pub green: Rgb,
- pub yellow: Rgb,
- pub blue: Rgb,
- pub magenta: Rgb,
- pub cyan: Rgb,
- pub white: Rgb,
-}
-
-impl Default for BrightColors {
- fn default() -> Self {
- BrightColors {
- black: Rgb { r: 0x66, g: 0x66, b: 0x66 },
- red: Rgb { r: 0xd5, g: 0x4e, b: 0x53 },
- green: Rgb { r: 0xb9, g: 0xca, b: 0x4a },
- yellow: Rgb { r: 0xe7, g: 0xc5, b: 0x47 },
- blue: Rgb { r: 0x7a, g: 0xa6, b: 0xda },
- magenta: Rgb { r: 0xc3, g: 0x97, b: 0xd8 },
- cyan: Rgb { r: 0x70, g: 0xc0, b: 0xb1 },
- white: Rgb { r: 0xea, g: 0xea, b: 0xea },
- }
- }
-}
-
-#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
-pub struct DimColors {
- pub black: Rgb,
- pub red: Rgb,
- pub green: Rgb,
- pub yellow: Rgb,
- pub blue: Rgb,
- pub magenta: Rgb,
- pub cyan: Rgb,
- pub white: Rgb,
-}
-
-impl Default for DimColors {
- fn default() -> Self {
- DimColors {
- black: Rgb { r: 0x13, g: 0x14, b: 0x15 },
- red: Rgb { r: 0x86, g: 0x43, b: 0x43 },
- green: Rgb { r: 0x77, g: 0x7c, b: 0x44 },
- yellow: Rgb { r: 0x9e, g: 0x82, b: 0x4c },
- blue: Rgb { r: 0x55, g: 0x6a, b: 0x7d },
- magenta: Rgb { r: 0x75, g: 0x61, b: 0x7b },
- cyan: Rgb { r: 0x5b, g: 0x7d, b: 0x78 },
- white: Rgb { r: 0x82, g: 0x84, b: 0x82 },
- }
- }
-}
diff --git a/alacritty_terminal/src/config/mod.rs b/alacritty_terminal/src/config/mod.rs
index 9b6f695f..59449faa 100644
--- a/alacritty_terminal/src/config/mod.rs
+++ b/alacritty_terminal/src/config/mod.rs
@@ -6,14 +6,10 @@ use serde::Deserialize;
use alacritty_config_derive::ConfigDeserialize;
-mod bell;
-mod colors;
mod scrolling;
use crate::ansi::{CursorShape, CursorStyle};
-pub use crate::config::bell::{BellAnimation, BellConfig};
-pub use crate::config::colors::Colors;
pub use crate::config::scrolling::Scrolling;
pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
@@ -27,11 +23,6 @@ pub struct Config<T> {
/// TERM env variable.
pub env: HashMap<String, String>,
- /// Should draw bold text with brighter colors instead of bold font.
- pub draw_bold_text_with_bright_colors: bool,
-
- pub colors: Colors,
-
pub selection: Selection,
/// Path to a shell program to run on startup.
@@ -53,19 +44,6 @@ pub struct Config<T> {
/// Remain open after child process exits.
#[config(skip)]
pub hold: bool,
-
- /// Bell configuration.
- bell: BellConfig,
-
- #[config(deprecated = "use `bell` instead")]
- pub visual_bell: Option<BellConfig>,
-}
-
-impl<T> Config<T> {
- #[inline]
- pub fn bell(&self) -> &BellConfig {
- self.visual_bell.as_ref().unwrap_or(&self.bell)
- }
}
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
@@ -190,9 +168,9 @@ impl CursorBlinking {
}
}
-impl Into<bool> for CursorBlinking {
- fn into(self) -> bool {
- self == Self::On || self == Self::Always
+impl From<CursorBlinking> for bool {
+ fn from(blinking: CursorBlinking) -> bool {
+ blinking == CursorBlinking::On || blinking == CursorBlinking::Always
}
}
diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs
index 351b7bc2..a1252570 100644
--- a/alacritty_terminal/src/event.rs
+++ b/alacritty_terminal/src/event.rs
@@ -2,18 +2,49 @@ use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
+use crate::term::color::Rgb;
use crate::term::{ClipboardType, SizeInfo};
+/// Terminal event.
+///
+/// These events instruct the UI over changes that can't be handled by the terminal emulation layer
+/// itself.
#[derive(Clone)]
pub enum Event {
+ /// Grid has changed possibly requiring a mouse cursor shape change.
MouseCursorDirty,
+
+ /// Window title change.
Title(String),
+
+ /// Reset to the default window title.
ResetTitle,
+
+ /// Request to store a text string in the clipboard.
ClipboardStore(ClipboardType, String),
+
+ /// Request to write the contents of the clipboard to the PTY.
+ ///
+ /// The attached function is a formatter which will corectly transform the clipboard content
+ /// into the expected escape sequence format.
ClipboardLoad(ClipboardType, Arc<dyn Fn(&str) -> String + Sync + Send + 'static>),
+
+ /// Request to write the RGB value of a color to the PTY.
+ ///
+ /// The attached function is a formatter which will corectly transform the RGB color into the
+ /// expected escape sequence format.
+ ColorRequest(usize, Arc<dyn Fn(Rgb) -> String + Sync + Send + 'static>),
+
+ /// Cursor blinking state has changed.
CursorBlinkingChange(bool),
+
+ /// New terminal content available.
Wakeup,
+
+ /// Terminal bell ring.
Bell,
+
+ /// Shutdown request.
Exit,
}
@@ -25,6 +56,7 @@ impl Debug for Event {
Event::ResetTitle => write!(f, "ResetTitle"),
Event::ClipboardStore(ty, text) => write!(f, "ClipboardStore({:?}, {})", ty, text),
Event::ClipboardLoad(ty, _) => write!(f, "ClipboardLoad({:?})", ty),
+ Event::ColorRequest(index, _) => write!(f, "ColorRequest({})", index),
Event::Wakeup => write!(f, "Wakeup"),
Event::Bell => write!(f, "Bell"),
Event::Exit => write!(f, "Exit"),
@@ -48,5 +80,9 @@ pub trait OnResize {
/// Event Loop for notifying the renderer about terminal events.
pub trait EventListener {
- fn send_event(&self, event: Event);
+ fn send_event(&self, _event: Event) {}
}
+
+/// Placeholder implementation for tests.
+#[cfg(test)]
+impl EventListener for () {}
diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs
index 4b3c86dc..7949489a 100644
--- a/alacritty_terminal/src/grid/mod.rs
+++ b/alacritty_terminal/src/grid/mod.rs
@@ -1,7 +1,8 @@
//! A specialized 2D grid implementation optimized for use in a terminal.
use std::cmp::{max, min};
-use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};
+use std::iter::{Map, TakeWhile};
+use std::ops::{Bound, Deref, Index, IndexMut, Range, RangeBounds, RangeInclusive};
use serde::{Deserialize, Serialize};
@@ -18,37 +19,6 @@ mod tests;
pub use self::row::Row;
use self::storage::Storage;
-/// Bidirectional iterator.
-pub trait BidirectionalIterator: Iterator {
- fn prev(&mut self) -> Option<Self::Item>;
-}
-
-/// An item in the grid along with its Line and Column.
-pub struct Indexed<T> {
- pub inner: T,
- pub line: Line,
- pub column: Column,
-}
-
-impl<T> Deref for Indexed<T> {
- type Target = T;
-
- #[inline]
- fn deref(&self) -> &T {
- &self.inner
- }
-}
-
-impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
- fn eq(&self, other: &Self) -> bool {
- // Compare struct fields and check result of grid comparison.
- self.raw.eq(&other.raw)
- && self.cols.eq(&other.cols)
- && self.lines.eq(&other.lines)
- && self.display_offset.eq(&other.display_offset)
- }
-}
-
pub trait GridCell: Sized {
/// Check if the cell contains any content.
fn is_empty(&self) -> bool;
@@ -99,6 +69,15 @@ impl IndexMut<CharsetIndex> for Charsets {
}
}
+#[derive(Debug, Copy, Clone)]
+pub enum Scroll {
+ Delta(isize),
+ PageUp,
+ PageDown,
+ Top,
+ Bottom,
+}
+
/// Grid based terminal content storage.
///
/// ```notrust
@@ -157,15 +136,6 @@ pub struct Grid<T> {
max_scroll_limit: usize,
}
-#[derive(Debug, Copy, Clone)]
-pub enum Scroll {
- Delta(isize),
- PageUp,
- PageDown,
- Top,
- Bottom,
-}
-
impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
pub fn new(lines: Line, cols: Column, max_scroll_limit: usize) -> Grid<T> {
Grid {
@@ -341,15 +311,15 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
D: PartialEq,
{
// Determine how many lines to scroll up by.
- let end = Point { line: 0, col: self.cols() };
+ let end = Point { line: 0, column: self.cols() };
let mut iter = self.iter_from(end);
while let Some(cell) = iter.prev() {
- if !cell.is_empty() || iter.cur.line >= *self.lines {
+ if !cell.is_empty() || cell.point.line >= *self.lines {
break;
}
}
- debug_assert!(iter.cur.line <= *self.lines);
- let positions = self.lines - iter.cur.line;
+ debug_assert!(iter.point.line <= *self.lines);
+ let positions = self.lines - iter.point.line;
let region = Line(0)..self.screen_lines();
// Reset display offset.
@@ -383,8 +353,33 @@ impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
}
}
-#[allow(clippy::len_without_is_empty)]
impl<T> Grid<T> {
+ /// Reset a visible region within the grid.
+ pub fn reset_region<D, R: RangeBounds<Line>>(&mut self, bounds: R)
+ where
+ T: ResetDiscriminant<D> + GridCell + Clone + Default,
+ D: PartialEq,
+ {
+ let start = match bounds.start_bound() {
+ Bound::Included(line) => *line,
+ Bound::Excluded(line) => *line + 1,
+ Bound::Unbounded => Line(0),
+ };
+
+ let end = match bounds.end_bound() {
+ Bound::Included(line) => *line + 1,
+ Bound::Excluded(line) => *line,
+ Bound::Unbounded => self.screen_lines(),
+ };
+
+ debug_assert!(start < self.screen_lines());
+ debug_assert!(end <= self.screen_lines());
+
+ for row in start.0..end.0 {
+ self.raw[Line(row)].reset(&self.cursor.template);
+ }
+ }
+
/// Clamp a buffer point to the visible region.
pub fn clamp_buffer_to_visible(&self, point: Point<usize>) -> Point {
if point.line < self.display_offset {
@@ -424,12 +419,7 @@ impl<T> Grid<T> {
/// Convert viewport relative point to global buffer indexing.
#[inline]
pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
- Point { line: self.lines.0 + self.display_offset - point.line.0 - 1, col: point.col }
- }
-
- #[inline]
- pub fn display_iter(&self) -> DisplayIter<'_, T> {
- DisplayIter::new(self)
+ Point { line: self.lines.0 + self.display_offset - point.line.0 - 1, column: point.column }
}
#[inline]
@@ -459,126 +449,51 @@ impl<T> Grid<T> {
#[inline]
pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> {
- GridIterator { grid: self, cur: point }
- }
-
- #[inline]
- pub fn display_offset(&self) -> usize {
- self.display_offset
+ GridIterator { grid: self, point }
}
+ /// Iterator over all visible cells.
#[inline]
- pub fn cursor_cell(&mut self) -> &mut T {
- let point = self.cursor.point;
- &mut self[&point]
- }
-}
-
-/// Grid dimensions.
-pub trait Dimensions {
- /// Total number of lines in the buffer, this includes scrollback and visible lines.
- fn total_lines(&self) -> usize;
+ pub fn display_iter(&self) -> DisplayIter<'_, T> {
+ let start = Point::new(self.display_offset + self.lines.0, self.cols() - 1);
+ let end = Point::new(self.display_offset, self.cols());
- /// Height of the viewport in lines.
- fn screen_lines(&self) -> Line;
+ let iter = GridIterator { grid: self, point: start };
- /// Width of the terminal in columns.
- fn cols(&self) -> Column;
+ let display_offset = self.display_offset;
+ let lines = self.lines.0;
- /// Number of invisible lines part of the scrollback history.
- #[inline]
- fn history_size(&self) -> usize {
- self.total_lines() - self.screen_lines().0
- }
-}
-
-impl<G> Dimensions for Grid<G> {
- #[inline]
- fn total_lines(&self) -> usize {
- self.raw.len()
+ let take_while: DisplayIterTakeFun<'_, T> =
+ Box::new(move |indexed: &Indexed<&T>| indexed.point <= end);
+ let map: DisplayIterMapFun<'_, T> = Box::new(move |indexed: Indexed<&T>| {
+ let line = Line(lines + display_offset - indexed.point.line - 1);
+ Indexed { point: Point::new(line, indexed.point.column), cell: indexed.cell }
+ });
+ iter.take_while(take_while).map(map)
}
#[inline]
- fn screen_lines(&self) -> Line {
- self.lines
+ pub fn display_offset(&self) -> usize {
+ self.display_offset
}
#[inline]
- fn cols(&self) -> Column {
- self.cols
- }
-}
-
-#[cfg(test)]
-impl Dimensions for (Line, Column) {
- fn total_lines(&self) -> usize {
- *self.0
- }
-
- fn screen_lines(&self) -> Line {
- self.0
- }
-
- fn cols(&self) -> Column {
- self.1
- }
-}
-
-pub struct GridIterator<'a, T> {
- /// Immutable grid reference.
- grid: &'a Grid<T>,
-
- /// Current position of the iterator within the grid.
- cur: Point<usize>,
-}
-
-impl<'a, T> GridIterator<'a, T> {
- pub fn point(&self) -> Point<usize> {
- self.cur
- }
-
- pub fn cell(&self) -> &'a T {
- &self.grid[self.cur]
- }
-}
-
-impl<'a, T> Iterator for GridIterator<'a, T> {
- type Item = &'a T;
-
- fn next(&mut self) -> Option<Self::Item> {
- let last_col = self.grid.cols() - 1;
-
- match self.cur {
- Point { line, col } if line == 0 && col == last_col => return None,
- Point { col, .. } if (col == last_col) => {
- self.cur.line -= 1;
- self.cur.col = Column(0);
- },
- _ => self.cur.col += Column(1),
- }
-
- Some(&self.grid[self.cur])
+ pub fn cursor_cell(&mut self) -> &mut T {
+ let point = self.cursor.point;
+ &mut self[point.line][point.column]
}
}
-impl<'a, T> BidirectionalIterator for GridIterator<'a, T> {
- fn prev(&mut self) -> Option<Self::Item> {
- let last_col = self.grid.cols() - 1;
-
- match self.cur {
- Point { line, col: Column(0) } if line == self.grid.total_lines() - 1 => return None,
- Point { col: Column(0), .. } => {
- self.cur.line += 1;
- self.cur.col = last_col;
- },
- _ => self.cur.col -= Column(1),
- }
-
- Some(&self.grid[self.cur])
+impl<T: PartialEq> PartialEq for Grid<T> {
+ fn eq(&self, other: &Self) -> bool {
+ // Compare struct fields and check result of grid comparison.
+ self.raw.eq(&other.raw)
+ && self.cols.eq(&other.cols)
+ && self.lines.eq(&other.lines)
+ && self.display_offset.eq(&other.display_offset)
}
}
-/// Index active region by line.
impl<T> Index<Line> for Grid<T> {
type Output = Row<T>;
@@ -588,16 +503,6 @@ impl<T> Index<Line> for Grid<T> {
}
}
-/// Index with buffer offset.
-impl<T> Index<usize> for Grid<T> {
- type Output = Row<T>;
-
- #[inline]
- fn index(&self, index: usize) -> &Row<T> {
- &self.raw[index]
- }
-}
-
impl<T> IndexMut<Line> for Grid<T> {
#[inline]
fn index_mut(&mut self, index: Line) -> &mut Row<T> {
@@ -605,26 +510,19 @@ impl<T> IndexMut<Line> for Grid<T> {
}
}
-impl<T> IndexMut<usize> for Grid<T> {
- #[inline]
- fn index_mut(&mut self, index: usize) -> &mut Row<T> {
- &mut self.raw[index]
- }
-}
-
-impl<'point, T> Index<&'point Point> for Grid<T> {
- type Output = T;
+impl<T> Index<usize> for Grid<T> {
+ type Output = Row<T>;
#[inline]
- fn index<'a>(&'a self, point: &Point) -> &'a T {
- &self[point.line][point.col]
+ fn index(&self, index: usize) -> &Row<T> {
+ &self.raw[index]
}
}
-impl<'point, T> IndexMut<&'point Point> for Grid<T> {
+impl<T> IndexMut<usize> for Grid<T> {
#[inline]
- fn index_mut<'a, 'b>(&'a mut self, point: &'b Point) -> &'a mut T {
- &mut self[point.line][point.col]
+ fn index_mut(&mut self, index: usize) -> &mut Row<T> {
+ &mut self.raw[index]
}
}
@@ -633,216 +531,162 @@ impl<T> Index<Point<usize>> for Grid<T> {
#[inline]
fn index(&self, point: Point<usize>) -> &T {
- &self[point.line][point.col]
+ &self[point.line][point.column]
}
}
impl<T> IndexMut<Point<usize>> for Grid<T> {
#[inline]
fn index_mut(&mut self, point: Point<usize>) -> &mut T {
- &mut self[point.line][point.col]
+ &mut self[point.line][point.column]
}
}
-/// A subset of lines in the grid.
-///<