2020-05-05 22:50:23 +00:00
|
|
|
//! Exports the `Term` type which is a high-level API for the Grid.
|
2020-06-06 18:49:14 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
use std::cmp::{max, min};
|
2020-12-28 09:45:39 +00:00
|
|
|
use std::ops::{Index, IndexMut, Range};
|
2020-06-06 21:33:20 +00:00
|
|
|
use std::sync::Arc;
|
2021-04-17 23:20:13 +00:00
|
|
|
use std::{mem, ptr, str};
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
use bitflags::bitflags;
|
2019-10-05 00:29:26 +00:00
|
|
|
use log::{debug, trace};
|
|
|
|
use serde::{Deserialize, Serialize};
|
2019-03-30 16:48:36 +00:00
|
|
|
use unicode_width::UnicodeWidthChar;
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
use crate::ansi::{
|
2020-11-23 23:11:03 +00:00
|
|
|
self, Attr, CharsetIndex, Color, CursorShape, CursorStyle, Handler, NamedColor, StandardCharset,
|
2019-03-30 16:48:36 +00:00
|
|
|
};
|
2021-01-24 21:45:36 +00:00
|
|
|
use crate::config::Config;
|
2019-10-05 00:29:26 +00:00
|
|
|
use crate::event::{Event, EventListener};
|
2021-05-22 22:48:43 +00:00
|
|
|
use crate::grid::{Dimensions, Grid, GridIterator, Scroll};
|
2021-03-30 23:25:38 +00:00
|
|
|
use crate::index::{self, Boundary, Column, Direction, Line, Point, Side};
|
2022-01-04 21:46:20 +00:00
|
|
|
use crate::selection::{Selection, SelectionRange, SelectionType};
|
2019-03-30 16:48:36 +00:00
|
|
|
use crate::term::cell::{Cell, Flags, LineLength};
|
2021-01-24 21:45:36 +00:00
|
|
|
use crate::term::color::{Colors, Rgb};
|
2020-03-18 02:35:08 +00:00
|
|
|
use crate::vi_mode::{ViModeCursor, ViMotion};
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-11-28 22:30:08 +00:00
|
|
|
pub mod cell;
|
2017-02-11 20:49:40 +00:00
|
|
|
pub mod color;
|
2021-01-24 21:45:36 +00:00
|
|
|
pub mod search;
|
2020-03-01 04:07:36 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
/// Minimum number of columns.
|
|
|
|
///
|
|
|
|
/// A minimum of 2 is necessary to hold fullwidth unicode characters.
|
2021-03-30 23:25:38 +00:00
|
|
|
pub const MIN_COLUMNS: usize = 2;
|
2020-09-27 22:36:08 +00:00
|
|
|
|
|
|
|
/// Minimum number of visible lines.
|
|
|
|
pub const MIN_SCREEN_LINES: usize = 1;
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
/// Max size of the window title stack.
|
|
|
|
const TITLE_STACK_MAX_DEPTH: usize = 4096;
|
|
|
|
|
|
|
|
/// Default tab interval, corresponding to terminfo `it` value.
|
|
|
|
const INITIAL_TABSTOPS: usize = 8;
|
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
bitflags! {
|
|
|
|
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 URGENCY_HINTS = 0b0010_0000_0000_0000_0000;
|
|
|
|
const ANY = std::u32::MAX;
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
impl Default for TermMode {
|
|
|
|
fn default() -> TermMode {
|
|
|
|
TermMode::SHOW_CURSOR
|
|
|
|
| TermMode::LINE_WRAP
|
|
|
|
| TermMode::ALTERNATE_SCROLL
|
|
|
|
| TermMode::URGENCY_HINTS
|
2016-06-08 04:17:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Terminal size info.
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
|
|
|
|
pub struct SizeInfo {
|
|
|
|
/// Terminal window width.
|
2020-09-27 22:36:08 +00:00
|
|
|
width: f32,
|
2020-03-18 02:35:08 +00:00
|
|
|
|
|
|
|
/// Terminal window height.
|
2020-09-27 22:36:08 +00:00
|
|
|
height: f32,
|
2020-03-18 02:35:08 +00:00
|
|
|
|
|
|
|
/// Width of individual cell.
|
2020-09-27 22:36:08 +00:00
|
|
|
cell_width: f32,
|
2020-03-18 02:35:08 +00:00
|
|
|
|
|
|
|
/// Height of individual cell.
|
2020-09-27 22:36:08 +00:00
|
|
|
cell_height: f32,
|
2020-03-18 02:35:08 +00:00
|
|
|
|
|
|
|
/// Horizontal window padding.
|
2020-09-27 22:36:08 +00:00
|
|
|
padding_x: f32,
|
2020-03-18 02:35:08 +00:00
|
|
|
|
|
|
|
/// Horizontal window padding.
|
2020-09-27 22:36:08 +00:00
|
|
|
padding_y: f32,
|
2020-03-18 02:35:08 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
/// Number of lines in the viewport.
|
2021-03-30 23:25:38 +00:00
|
|
|
screen_lines: usize,
|
2020-09-27 22:36:08 +00:00
|
|
|
|
|
|
|
/// Number of columns in the viewport.
|
2021-03-30 23:25:38 +00:00
|
|
|
columns: usize,
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SizeInfo {
|
2020-09-27 22:36:08 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
pub fn new(
|
|
|
|
width: f32,
|
|
|
|
height: f32,
|
|
|
|
cell_width: f32,
|
|
|
|
cell_height: f32,
|
|
|
|
mut padding_x: f32,
|
|
|
|
mut padding_y: f32,
|
|
|
|
dynamic_padding: bool,
|
|
|
|
) -> SizeInfo {
|
|
|
|
if dynamic_padding {
|
|
|
|
padding_x = Self::dynamic_padding(padding_x.floor(), width, cell_width);
|
|
|
|
padding_y = Self::dynamic_padding(padding_y.floor(), height, cell_height);
|
|
|
|
}
|
2020-03-18 02:35:08 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
let lines = (height - 2. * padding_y) / cell_height;
|
2021-03-30 23:25:38 +00:00
|
|
|
let screen_lines = max(lines as usize, MIN_SCREEN_LINES);
|
2020-03-18 02:35:08 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let columns = (width - 2. * padding_x) / cell_width;
|
|
|
|
let columns = max(columns as usize, MIN_COLUMNS);
|
2020-09-27 22:36:08 +00:00
|
|
|
|
|
|
|
SizeInfo {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
cell_width,
|
|
|
|
cell_height,
|
|
|
|
padding_x: padding_x.floor(),
|
|
|
|
padding_y: padding_y.floor(),
|
|
|
|
screen_lines,
|
2021-03-30 23:25:38 +00:00
|
|
|
columns,
|
2020-09-27 22:36:08 +00:00
|
|
|
}
|
2020-06-18 01:02:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-09-27 22:36:08 +00:00
|
|
|
pub fn reserve_lines(&mut self, count: usize) {
|
2021-03-30 23:25:38 +00:00
|
|
|
self.screen_lines = max(self.screen_lines.saturating_sub(count), MIN_SCREEN_LINES);
|
2020-06-18 01:02:56 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Check if coordinates are inside the terminal grid.
|
|
|
|
///
|
2020-09-27 22:36:08 +00:00
|
|
|
/// The padding, message bar or search are not counted as part of the grid.
|
2020-06-18 01:02:56 +00:00
|
|
|
#[inline]
|
2020-03-18 02:35:08 +00:00
|
|
|
pub fn contains_point(&self, x: usize, y: usize) -> bool {
|
2021-03-30 23:25:38 +00:00
|
|
|
x <= (self.padding_x + self.columns as f32 * self.cell_width) as usize
|
2020-09-27 22:36:08 +00:00
|
|
|
&& x > self.padding_x as usize
|
2021-03-30 23:25:38 +00:00
|
|
|
&& y <= (self.padding_y + self.screen_lines as f32 * self.cell_height) as usize
|
2020-09-27 22:36:08 +00:00
|
|
|
&& y > self.padding_y as usize
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn width(&self) -> f32 {
|
|
|
|
self.width
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn height(&self) -> f32 {
|
|
|
|
self.height
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn cell_width(&self) -> f32 {
|
|
|
|
self.cell_width
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn cell_height(&self) -> f32 {
|
|
|
|
self.cell_height
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn padding_x(&self) -> f32 {
|
|
|
|
self.padding_x
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn padding_y(&self) -> f32 {
|
|
|
|
self.padding_y
|
|
|
|
}
|
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
/// Calculate padding to spread it evenly around the terminal content.
|
2020-09-27 22:36:08 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 {
|
|
|
|
padding + ((dimension - 2. * padding) % cell_dimension) / 2.
|
2020-09-27 22:36:08 +00:00
|
|
|
}
|
2021-03-30 23:25:38 +00:00
|
|
|
}
|
2020-09-27 22:36:08 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
impl Dimensions for SizeInfo {
|
2020-09-27 22:36:08 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn columns(&self) -> usize {
|
|
|
|
self.columns
|
2020-09-27 22:36:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn screen_lines(&self) -> usize {
|
|
|
|
self.screen_lines
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn total_lines(&self) -> usize {
|
|
|
|
self.screen_lines()
|
2020-09-27 22:36:08 +00:00
|
|
|
}
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
pub struct Term<T> {
|
2020-05-30 20:45:44 +00:00
|
|
|
/// Terminal focus controlling the cursor shape.
|
2019-11-03 20:59:28 +00:00
|
|
|
pub is_focused: bool,
|
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
/// Cursor for keyboard selection.
|
|
|
|
pub vi_mode_cursor: ViModeCursor,
|
|
|
|
|
|
|
|
pub selection: Option<Selection>,
|
|
|
|
|
2020-06-28 19:12:37 +00:00
|
|
|
/// Currently active grid.
|
|
|
|
///
|
|
|
|
/// Tracks the screen buffer currently in use. While the alternate screen buffer is active,
|
|
|
|
/// this will be the alternate grid. Otherwise it is the primary screen buffer.
|
|
|
|
grid: Grid<Cell>,
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2020-06-28 19:12:37 +00:00
|
|
|
/// Currently inactive grid.
|
|
|
|
///
|
|
|
|
/// Opposite of the active grid. While the alternate screen buffer is active, this will be the
|
|
|
|
/// primary grid. Otherwise it is the alternate screen buffer.
|
|
|
|
inactive_grid: Grid<Cell>,
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Index into `charsets`, pointing to what ASCII is currently being mapped to.
|
2017-01-09 20:07:23 +00:00
|
|
|
active_charset: CharsetIndex,
|
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Tabstops.
|
2018-12-15 21:33:33 +00:00
|
|
|
tabs: TabStops,
|
2016-06-06 23:54:15 +00:00
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Mode flags.
|
2016-06-08 04:17:48 +00:00
|
|
|
mode: TermMode,
|
2016-06-08 17:39:49 +00:00
|
|
|
|
2019-11-18 21:15:25 +00:00
|
|
|
/// Scroll region.
|
|
|
|
///
|
|
|
|
/// Range going from top to bottom of the terminal, indexed from the top of the viewport.
|
2016-07-04 00:00:00 +00:00
|
|
|
scroll_region: Range<Line>,
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2017-01-15 01:53:48 +00:00
|
|
|
semantic_escape_chars: String,
|
2017-02-11 20:49:40 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
/// Modified terminal colors.
|
|
|
|
colors: Colors,
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Current style of the cursor.
|
2017-12-03 21:38:42 +00:00
|
|
|
cursor_style: Option<CursorStyle>,
|
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Default style for resetting the cursor.
|
2017-12-03 21:38:42 +00:00
|
|
|
default_cursor_style: CursorStyle,
|
2017-12-24 18:24:28 +00:00
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Style of the vi mode cursor.
|
|
|
|
vi_mode_cursor_style: Option<CursorStyle>,
|
|
|
|
|
|
|
|
/// Proxy for sending events to the event loop.
|
2019-10-05 00:29:26 +00:00
|
|
|
event_proxy: T,
|
|
|
|
|
2020-03-14 15:09:10 +00:00
|
|
|
/// Current title of the window.
|
|
|
|
title: Option<String>,
|
|
|
|
|
2019-10-14 17:50:58 +00:00
|
|
|
/// Stack of saved window titles. When a title is popped from this stack, the `title` for the
|
2020-07-11 17:03:09 +00:00
|
|
|
/// term is set.
|
2020-03-14 15:09:10 +00:00
|
|
|
title_stack: Vec<Option<String>>,
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
/// Information about cell dimensions.
|
|
|
|
cell_width: usize,
|
|
|
|
cell_height: usize,
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
impl<T> Term<T> {
|
2017-01-11 05:21:19 +00:00
|
|
|
#[inline]
|
2019-10-05 00:29:26 +00:00
|
|
|
pub fn scroll_display(&mut self, scroll: Scroll)
|
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
2018-03-11 12:01:06 +00:00
|
|
|
self.grid.scroll_display(scroll);
|
2020-07-09 21:45:22 +00:00
|
|
|
self.event_proxy.send_event(Event::MouseCursorDirty);
|
2021-03-30 23:25:38 +00:00
|
|
|
|
|
|
|
// Clamp vi mode cursor to the viewport.
|
|
|
|
let viewport_start = -(self.grid.display_offset() as i32);
|
|
|
|
let viewport_end = viewport_start + self.bottommost_line().0;
|
|
|
|
let vi_cursor_line = &mut self.vi_mode_cursor.point.line.0;
|
|
|
|
*vi_cursor_line = min(viewport_end, max(viewport_start, *vi_cursor_line));
|
|
|
|
self.vi_mode_recompute_selection();
|
2018-02-17 01:33:32 +00:00
|
|
|
}
|
|
|
|
|
2021-11-22 18:34:09 +00:00
|
|
|
pub fn new(config: &Config, size: SizeInfo, event_proxy: T) -> Term<T> {
|
2021-03-30 23:25:38 +00:00
|
|
|
let num_cols = size.columns;
|
2020-09-27 22:36:08 +00:00
|
|
|
let num_lines = size.screen_lines;
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2019-05-10 11:36:16 +00:00
|
|
|
let history_size = config.scrolling.history() as usize;
|
2020-11-05 04:45:14 +00:00
|
|
|
let grid = Grid::new(num_lines, num_cols, history_size);
|
|
|
|
let alt = Grid::new(num_lines, num_cols, 0);
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let tabs = TabStops::new(grid.columns());
|
2016-07-30 03:16:13 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let scroll_region = Line(0)..Line(grid.screen_lines() as i32);
|
2016-05-31 03:44:37 +00:00
|
|
|
|
|
|
|
Term {
|
2018-03-04 22:40:15 +00:00
|
|
|
grid,
|
2020-06-28 19:12:37 +00:00
|
|
|
inactive_grid: alt,
|
2017-02-01 17:55:23 +00:00
|
|
|
active_charset: Default::default(),
|
2020-03-18 02:35:08 +00:00
|
|
|
vi_mode_cursor: Default::default(),
|
2018-03-04 22:40:15 +00:00
|
|
|
tabs,
|
2016-06-23 16:42:00 +00:00
|
|
|
mode: Default::default(),
|
2018-03-04 22:40:15 +00:00
|
|
|
scroll_region,
|
2021-01-24 21:45:36 +00:00
|
|
|
colors: color::Colors::default(),
|
2020-12-21 02:44:38 +00:00
|
|
|
semantic_escape_chars: config.selection.semantic_escape_chars.to_owned(),
|
2017-12-03 21:38:42 +00:00
|
|
|
cursor_style: None,
|
2020-11-23 23:11:03 +00:00
|
|
|
default_cursor_style: config.cursor.style(),
|
|
|
|
vi_mode_cursor_style: config.cursor.vi_mode_style(),
|
2019-10-05 00:29:26 +00:00
|
|
|
event_proxy,
|
|
|
|
is_focused: true,
|
2020-03-14 15:09:10 +00:00
|
|
|
title: None,
|
2019-10-14 17:50:58 +00:00
|
|
|
title_stack: Vec::new(),
|
2020-05-30 20:45:44 +00:00
|
|
|
selection: None,
|
2020-09-27 22:36:08 +00:00
|
|
|
cell_width: size.cell_width as usize,
|
|
|
|
cell_height: size.cell_height as usize,
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-22 18:34:09 +00:00
|
|
|
pub fn update_config(&mut self, config: &Config)
|
2020-03-14 15:09:10 +00:00
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
2020-12-21 02:44:38 +00:00
|
|
|
self.semantic_escape_chars = config.selection.semantic_escape_chars.to_owned();
|
2020-11-23 23:11:03 +00:00
|
|
|
self.default_cursor_style = config.cursor.style();
|
|
|
|
self.vi_mode_cursor_style = config.cursor.vi_mode_style();
|
2020-03-14 15:09:10 +00:00
|
|
|
|
2020-07-11 17:03:09 +00:00
|
|
|
let title_event = match &self.title {
|
|
|
|
Some(title) => Event::Title(title.clone()),
|
|
|
|
None => Event::ResetTitle,
|
|
|
|
};
|
2020-02-23 01:55:29 +00:00
|
|
|
|
2020-07-11 17:03:09 +00:00
|
|
|
self.event_proxy.send_event(title_event);
|
2020-03-14 15:09:10 +00:00
|
|
|
|
2020-06-28 19:12:37 +00:00
|
|
|
if self.mode.contains(TermMode::ALT_SCREEN) {
|
|
|
|
self.inactive_grid.update_history(config.scrolling.history() as usize);
|
2020-02-23 01:55:29 +00:00
|
|
|
} else {
|
|
|
|
self.grid.update_history(config.scrolling.history() as usize);
|
|
|
|
}
|
2016-12-30 02:38:22 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
/// Convert the active selection to a String.
|
2018-03-05 17:57:34 +00:00
|
|
|
pub fn selection_to_string(&self) -> Option<String> {
|
2020-05-30 20:45:44 +00:00
|
|
|
let selection_range = self.selection.as_ref().and_then(|s| s.to_range(self))?;
|
2022-01-04 21:46:20 +00:00
|
|
|
let SelectionRange { start, end, .. } = selection_range;
|
2018-12-15 21:33:33 +00:00
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
let mut res = String::new();
|
2016-12-30 01:39:30 +00:00
|
|
|
|
2022-01-04 21:46:20 +00:00
|
|
|
match self.selection.as_ref() {
|
|
|
|
Some(Selection { ty: SelectionType::Block, .. }) => {
|
|
|
|
for line in (start.line.0..end.line.0).map(Line::from) {
|
|
|
|
res += self
|
|
|
|
.line_to_string(line, start.column..end.column, start.column.0 != 0)
|
|
|
|
.trim_end();
|
2022-01-07 09:03:15 +00:00
|
|
|
res += "\n";
|
2020-02-22 02:42:44 +00:00
|
|
|
}
|
2022-01-07 09:03:15 +00:00
|
|
|
|
2022-01-04 21:46:20 +00:00
|
|
|
res += self.line_to_string(end.line, start.column..end.column, true).trim_end();
|
|
|
|
},
|
|
|
|
Some(Selection { ty: SelectionType::Lines, .. }) => {
|
|
|
|
res = self.bounds_to_string(start, end) + "\n";
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
res = self.bounds_to_string(start, end);
|
|
|
|
},
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
Some(res)
|
|
|
|
}
|
2018-03-05 17:57:34 +00:00
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
/// Convert range between two points to a String.
|
2021-03-30 23:25:38 +00:00
|
|
|
pub fn bounds_to_string(&self, start: Point, end: Point) -> String {
|
2016-12-27 03:52:37 +00:00
|
|
|
let mut res = String::new();
|
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
for line in (start.line.0..=end.line.0).map(Line::from) {
|
2021-01-24 21:45:36 +00:00
|
|
|
let start_col = if line == start.line { start.column } else { Column(0) };
|
2021-03-30 23:25:38 +00:00
|
|
|
let end_col = if line == end.line { end.column } else { self.last_column() };
|
2019-11-03 20:59:28 +00:00
|
|
|
|
2020-02-07 06:50:18 +00:00
|
|
|
res += &self.line_to_string(line, start_col..end_col, line == end.line);
|
2018-03-07 04:57:40 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 21:46:20 +00:00
|
|
|
res.strip_suffix('\n').map(str::to_owned).unwrap_or(res)
|
2019-11-03 20:59:28 +00:00
|
|
|
}
|
2019-06-20 15:56:09 +00:00
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
/// Convert a single line in the grid to a String.
|
2020-02-07 06:50:18 +00:00
|
|
|
fn line_to_string(
|
|
|
|
&self,
|
2021-03-30 23:25:38 +00:00
|
|
|
line: Line,
|
2020-02-07 06:50:18 +00:00
|
|
|
mut cols: Range<Column>,
|
|
|
|
include_wrapped_wide: bool,
|
|
|
|
) -> String {
|
2019-11-03 20:59:28 +00:00
|
|
|
let mut text = String::new();
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
let grid_line = &self.grid[line];
|
2020-02-07 06:50:18 +00:00
|
|
|
let line_length = min(grid_line.line_length(), cols.end + 1);
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Include wide char when trailing spacer is selected.
|
2020-02-07 06:50:18 +00:00
|
|
|
if grid_line[cols.start].flags.contains(Flags::WIDE_CHAR_SPACER) {
|
|
|
|
cols.start -= 1;
|
|
|
|
}
|
2018-03-07 04:57:40 +00:00
|
|
|
|
2020-02-07 06:50:18 +00:00
|
|
|
let mut tab_mode = false;
|
2021-03-30 23:25:38 +00:00
|
|
|
for column in (cols.start.0..line_length.0).map(Column::from) {
|
|
|
|
let cell = &grid_line[column];
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Skip over cells until next tab-stop once a tab was found.
|
2019-11-03 20:59:28 +00:00
|
|
|
if tab_mode {
|
2021-07-16 19:08:07 +00:00
|
|
|
if self.tabs[column] || cell.c != ' ' {
|
2019-11-03 20:59:28 +00:00
|
|
|
tab_mode = false;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
if cell.c == '\t' {
|
|
|
|
tab_mode = true;
|
|
|
|
}
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
if !cell.flags.intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER) {
|
2020-05-05 22:50:23 +00:00
|
|
|
// Push cells primary character.
|
2019-11-03 20:59:28 +00:00
|
|
|
text.push(cell.c);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Push zero-width characters.
|
2020-11-05 04:45:14 +00:00
|
|
|
for c in cell.zerowidth().into_iter().flatten() {
|
2019-11-03 20:59:28 +00:00
|
|
|
text.push(*c);
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
2019-11-03 20:59:28 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
if cols.end >= self.columns() - 1
|
2020-02-07 06:50:18 +00:00
|
|
|
&& (line_length.0 == 0
|
|
|
|
|| !self.grid[line][line_length - 1].flags.contains(Flags::WRAPLINE))
|
2019-11-03 20:59:28 +00:00
|
|
|
{
|
|
|
|
text.push('\n');
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// If wide char is not part of the selection, but leading spacer is, include it.
|
2021-03-30 23:25:38 +00:00
|
|
|
if line_length == self.columns()
|
2020-02-07 06:50:18 +00:00
|
|
|
&& line_length.0 >= 2
|
2020-07-09 21:45:22 +00:00
|
|
|
&& grid_line[line_length - 1].flags.contains(Flags::LEADING_WIDE_CHAR_SPACER)
|
2020-02-07 06:50:18 +00:00
|
|
|
&& include_wrapped_wide
|
|
|
|
{
|
2021-03-30 23:25:38 +00:00
|
|
|
text.push(self.grid[line - 1i32][Column(0)].c);
|
2020-02-07 06:50:18 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
text
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
/// Terminal content required for rendering.
|
|
|
|
#[inline]
|
|
|
|
pub fn renderable_content(&self) -> RenderableContent<'_>
|
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
|
|
|
RenderableContent::new(self)
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Access to the raw grid data structure.
|
2016-11-28 22:27:52 +00:00
|
|
|
///
|
|
|
|
/// This is a bit of a hack; when the window is closed, the event processor
|
|
|
|
/// serializes the grid state to a file.
|
2016-11-20 00:16:20 +00:00
|
|
|
pub fn grid(&self) -> &Grid<Cell> {
|
|
|
|
&self.grid
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Mutable access for swapping out the grid during tests.
|
2019-03-30 09:23:48 +00:00
|
|
|
#[cfg(test)]
|
2019-01-04 15:47:20 +00:00
|
|
|
pub fn grid_mut(&mut self) -> &mut Grid<Cell> {
|
|
|
|
&mut self.grid
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Resize terminal to new dimensions.
|
2020-08-28 22:26:03 +00:00
|
|
|
pub fn resize(&mut self, size: SizeInfo) {
|
2020-09-27 22:36:08 +00:00
|
|
|
self.cell_width = size.cell_width as usize;
|
|
|
|
self.cell_height = size.cell_height as usize;
|
2020-08-28 22:26:03 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let old_cols = self.columns();
|
2020-07-09 21:45:22 +00:00
|
|
|
let old_lines = self.screen_lines();
|
2020-09-27 22:36:08 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let num_cols = size.columns;
|
2020-09-27 22:36:08 +00:00
|
|
|
let num_lines = size.screen_lines;
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
if old_cols == num_cols && old_lines == num_lines {
|
2017-05-06 15:45:23 +00:00
|
|
|
debug!("Term::resize dimensions unchanged");
|
2016-06-29 17:21:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-07 00:06:57 +00:00
|
|
|
debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
// Move vi mode cursor with the content.
|
|
|
|
let history_size = self.history_size();
|
|
|
|
let mut delta = num_lines as i32 - old_lines as i32;
|
|
|
|
let min_delta = min(0, num_lines as i32 - self.grid.cursor.point.line.0 - 1);
|
|
|
|
delta = min(max(delta, min_delta), history_size as i32);
|
|
|
|
self.vi_mode_cursor.point.line += delta;
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
// Invalidate selection and tabs only when necessary.
|
|
|
|
if old_cols != num_cols {
|
|
|
|
self.selection = None;
|
|
|
|
|
|
|
|
// Recreate tabs list.
|
|
|
|
self.tabs.resize(num_cols);
|
|
|
|
} else if let Some(selection) = self.selection.take() {
|
2021-03-30 23:25:38 +00:00
|
|
|
let range = Line(0)..Line(num_lines as i32);
|
|
|
|
self.selection = selection.rotate(self, &range, -delta);
|
2020-07-09 21:45:22 +00:00
|
|
|
}
|
|
|
|
|
2019-06-23 23:29:01 +00:00
|
|
|
let is_alt = self.mode.contains(TermMode::ALT_SCREEN);
|
2020-05-30 20:45:44 +00:00
|
|
|
self.grid.resize(!is_alt, num_lines, num_cols);
|
2020-06-28 19:12:37 +00:00
|
|
|
self.inactive_grid.resize(is_alt, num_lines, num_cols);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
// Clamp vi cursor to viewport.
|
2021-03-30 23:25:38 +00:00
|
|
|
let vi_point = self.vi_mode_cursor.point;
|
2021-07-12 01:22:49 +00:00
|
|
|
let viewport_top = Line(-(self.grid.display_offset() as i32));
|
|
|
|
let viewport_bottom = viewport_top + self.bottommost_line();
|
|
|
|
self.vi_mode_cursor.point.line = max(min(vi_point.line, viewport_bottom), viewport_top);
|
2021-07-04 00:30:05 +00:00
|
|
|
self.vi_mode_cursor.point.column = min(vi_point.column, self.last_column());
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
// Reset scrolling region.
|
2021-03-30 23:25:38 +00:00
|
|
|
self.scroll_region = Line(0)..Line(self.screen_lines() as i32);
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
/// Active terminal modes.
|
2016-06-08 04:17:48 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn mode(&self) -> &TermMode {
|
|
|
|
&self.mode
|
|
|
|
}
|
|
|
|
|
2020-06-28 19:12:37 +00:00
|
|
|
/// Swap primary and alternate screen buffer.
|
2016-05-31 03:44:37 +00:00
|
|
|
pub fn swap_alt(&mut self) {
|
2020-07-06 19:10:06 +00:00
|
|
|
if !self.mode.contains(TermMode::ALT_SCREEN) {
|
|
|
|
// Set alt screen cursor to the current primary screen cursor.
|
2020-11-05 04:45:14 +00:00
|
|
|
self.inactive_grid.cursor = self.grid.cursor.clone();
|
2017-02-03 04:51:14 +00:00
|
|
|
|
2020-07-06 19:10:06 +00:00
|
|
|
// Drop information about the primary screens saved cursor.
|
2020-11-05 04:45:14 +00:00
|
|
|
self.grid.saved_cursor = self.grid.cursor.clone();
|
2020-07-06 05:08:36 +00:00
|
|
|
|
2020-07-06 19:10:06 +00:00
|
|
|
// Reset alternate screen contents.
|
2021-01-24 21:45:36 +00:00
|
|
|
self.inactive_grid.reset_region(..);
|
2020-06-28 19:12:37 +00:00
|
|
|
}
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2020-06-28 19:12:37 +00:00
|
|
|
mem::swap(&mut self.grid, &mut self.inactive_grid);
|
|
|
|
self.mode ^= TermMode::ALT_SCREEN;
|
2020-05-30 20:45:44 +00:00
|
|
|
self.selection = None;
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-08-22 15:37:50 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Scroll screen down.
|
2016-08-22 15:37:50 +00:00
|
|
|
///
|
|
|
|
/// Text moves down; clear at bottom
|
2017-01-10 06:05:26 +00:00
|
|
|
/// Expects origin to be in scroll range.
|
2016-08-22 15:37:50 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn scroll_down_relative(&mut self, origin: Line, mut lines: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Scrolling down relative: origin={}, lines={}", origin, lines);
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
lines = min(lines, (self.scroll_region.end - self.scroll_region.start).0 as usize);
|
|
|
|
lines = min(lines, (self.scroll_region.end - origin).0 as usize);
|
2016-08-22 15:37:50 +00:00
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
let region = origin..self.scroll_region.end;
|
|
|
|
|
|
|
|
// Scroll selection.
|
2021-03-30 23:25:38 +00:00
|
|
|
self.selection =
|
|
|
|
self.selection.take().and_then(|s| s.rotate(self, ®ion, -(lines as i32)));
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2021-07-17 23:14:39 +00:00
|
|
|
// Scroll vi mode cursor.
|
|
|
|
let line = &mut self.vi_mode_cursor.point.line;
|
|
|
|
if region.start <= *line && region.end > *line {
|
|
|
|
*line = min(*line + lines, region.end - 1);
|
|
|
|
}
|
|
|
|
|
2016-08-22 15:37:50 +00:00
|
|
|
// Scroll between origin and bottom
|
2020-11-05 04:45:14 +00:00
|
|
|
self.grid.scroll_down(®ion, lines);
|
2016-08-22 15:37:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Scroll screen up
|
|
|
|
///
|
|
|
|
/// Text moves up; clear at top
|
2017-01-10 06:05:26 +00:00
|
|
|
/// Expects origin to be in scroll range.
|
2016-08-22 15:37:50 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn scroll_up_relative(&mut self, origin: Line, mut lines: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Scrolling up relative: origin={}, lines={}", origin, lines);
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
lines = min(lines, (self.scroll_region.end - self.scroll_region.start).0 as usize);
|
2020-05-30 20:45:44 +00:00
|
|
|
|
|
|
|
let region = origin..self.scroll_region.end;
|
|
|
|
|
|
|
|
// Scroll selection.
|
2021-03-30 23:25:38 +00:00
|
|
|
self.selection = self.selection.take().and_then(|s| s.rotate(self, ®ion, lines as i32));
|
2016-08-22 15:37:50 +00:00
|
|
|
|
2021-12-03 19:33:21 +00:00
|
|
|
self.grid.scroll_up(®ion, lines);
|
|
|
|
|
2021-07-17 23:14:39 +00:00
|
|
|
// Scroll vi mode cursor.
|
|
|
|
let viewport_top = Line(-(self.grid.display_offset() as i32));
|
|
|
|
let top = if region.start == 0 { viewport_top } else { region.start };
|
|
|
|
let line = &mut self.vi_mode_cursor.point.line;
|
|
|
|
if (top <= *line) && region.end > *line {
|
|
|
|
*line = max(*line - lines, top);
|
|
|
|
}
|
2016-08-22 15:37:50 +00:00
|
|
|
}
|
2017-04-19 16:03:33 +00:00
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
fn deccolm(&mut self)
|
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
2020-05-05 22:50:23 +00:00
|
|
|
// Setting 132 column font makes no sense, but run the other side effects.
|
|
|
|
// Clear scrolling region.
|
2020-07-09 21:45:22 +00:00
|
|
|
self.set_scrolling_region(1, None);
|
2017-04-19 16:03:33 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Clear grid.
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.reset_region(..);
|
2017-07-28 22:14:18 +00:00
|
|
|
}
|
2019-02-07 22:36:45 +00:00
|
|
|
|
|
|
|
#[inline]
|
2019-10-05 00:29:26 +00:00
|
|
|
pub fn exit(&mut self)
|
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
|
|
|
self.event_proxy.send_event(Event::Exit);
|
2019-02-07 22:36:45 +00:00
|
|
|
}
|
2019-03-24 17:37:30 +00:00
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
/// Toggle the vi mode.
|
|
|
|
#[inline]
|
2020-11-23 23:11:03 +00:00
|
|
|
pub fn toggle_vi_mode(&mut self)
|
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
2020-03-18 02:35:08 +00:00
|
|
|
self.mode ^= TermMode::VI;
|
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
if self.mode.contains(TermMode::VI) {
|
2021-04-14 19:39:35 +00:00
|
|
|
let display_offset = self.grid.display_offset() as i32;
|
|
|
|
if self.grid.cursor.point.line > self.bottommost_line() - display_offset {
|
|
|
|
// Move cursor to top-left if terminal cursor is not visible.
|
|
|
|
let point = Point::new(Line(-display_offset), Column(0));
|
|
|
|
self.vi_mode_cursor = ViModeCursor::new(point);
|
|
|
|
} else {
|
|
|
|
// Reset vi mode cursor position to match primary cursor.
|
|
|
|
self.vi_mode_cursor = ViModeCursor::new(self.grid.cursor.point);
|
|
|
|
}
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
|
|
|
|
2020-11-23 23:11:03 +00:00
|
|
|
// Update UI about cursor blinking state changes.
|
2021-10-23 07:16:47 +00:00
|
|
|
self.event_proxy.send_event(Event::CursorBlinkingChange);
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Move vi mode cursor.
|
|
|
|
#[inline]
|
|
|
|
pub fn vi_motion(&mut self, motion: ViMotion)
|
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
2020-05-05 22:50:23 +00:00
|
|
|
// Require vi mode to be active.
|
2020-03-18 02:35:08 +00:00
|
|
|
if !self.mode.contains(TermMode::VI) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Move cursor.
|
2020-03-18 02:35:08 +00:00
|
|
|
self.vi_mode_cursor = self.vi_mode_cursor.motion(self, motion);
|
2020-07-09 21:45:22 +00:00
|
|
|
self.vi_mode_recompute_selection();
|
|
|
|
}
|
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
/// Move vi cursor to a point in the grid.
|
2020-07-09 21:45:22 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
pub fn vi_goto_point(&mut self, point: Point)
|
2020-07-09 21:45:22 +00:00
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
|
|
|
// Move viewport to make point visible.
|
|
|
|
self.scroll_to_point(point);
|
|
|
|
|
|
|
|
// Move vi cursor to the point.
|
2021-03-30 23:25:38 +00:00
|
|
|
self.vi_mode_cursor.point = point;
|
2020-07-09 21:45:22 +00:00
|
|
|
|
|
|
|
self.vi_mode_recompute_selection();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update the active selection to match the vi mode cursor position.
|
|
|
|
#[inline]
|
|
|
|
fn vi_mode_recompute_selection(&mut self) {
|
|
|
|
// Require vi mode to be active.
|
|
|
|
if !self.mode.contains(TermMode::VI) {
|
|
|
|
return;
|
|
|
|
}
|
2020-03-18 02:35:08 +00:00
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
// Update only if non-empty selection is present.
|
2021-03-30 23:25:38 +00:00
|
|
|
if let Some(selection) = self.selection.as_mut().filter(|s| !s.is_empty()) {
|
|
|
|
selection.update(self.vi_mode_cursor.point, Side::Left);
|
|
|
|
selection.include_all();
|
|
|
|
}
|
2020-07-09 21:45:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Scroll display to point if it is outside of viewport.
|
2021-03-30 23:25:38 +00:00
|
|
|
pub fn scroll_to_point(&mut self, point: Point)
|
2020-07-09 21:45:22 +00:00
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
2021-03-30 23:25:38 +00:00
|
|
|
let display_offset = self.grid.display_offset() as i32;
|
|
|
|
let screen_lines = self.grid.screen_lines() as i32;
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
if point.line < -display_offset {
|
|
|
|
let lines = point.line + display_offset;
|
|
|
|
self.scroll_display(Scroll::Delta(-lines.0));
|
|
|
|
} else if point.line >= (screen_lines - display_offset) {
|
|
|
|
let lines = point.line + display_offset - screen_lines + 1i32;
|
|
|
|
self.scroll_display(Scroll::Delta(-lines.0));
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
2020-07-09 21:45:22 +00:00
|
|
|
}
|
2020-03-18 02:35:08 +00:00
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
/// Jump to the end of a wide cell.
|
2021-03-30 23:25:38 +00:00
|
|
|
pub fn expand_wide(&self, mut point: Point, direction: Direction) -> Point {
|
2021-01-24 21:45:36 +00:00
|
|
|
let flags = self.grid[point.line][point.column].flags;
|
2020-07-09 21:45:22 +00:00
|
|
|
|
|
|
|
match direction {
|
|
|
|
Direction::Right if flags.contains(Flags::LEADING_WIDE_CHAR_SPACER) => {
|
2021-01-24 21:45:36 +00:00
|
|
|
point.column = Column(1);
|
2021-03-30 23:25:38 +00:00
|
|
|
point.line += 1;
|
2020-07-09 21:45:22 +00:00
|
|
|
},
|
2021-05-28 11:00:37 +00:00
|
|
|
Direction::Right if flags.contains(Flags::WIDE_CHAR) => {
|
|
|
|
point.column = min(point.column + 1, self.last_column());
|
|
|
|
},
|
2020-07-09 21:45:22 +00:00
|
|
|
Direction::Left if flags.intersects(Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER) => {
|
|
|
|
if flags.contains(Flags::WIDE_CHAR_SPACER) {
|
2021-01-24 21:45:36 +00:00
|
|
|
point.column -= 1;
|
2020-07-09 21:45:22 +00:00
|
|
|
}
|
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let prev = point.sub(self, Boundary::Grid, 1);
|
2020-07-09 21:45:22 +00:00
|
|
|
if self.grid[prev].flags.contains(Flags::LEADING_WIDE_CHAR_SPACER) {
|
|
|
|
point = prev;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
point
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn semantic_escape_chars(&self) -> &str {
|
|
|
|
&self.semantic_escape_chars
|
|
|
|
}
|
|
|
|
|
2020-11-23 23:11:03 +00:00
|
|
|
/// Active terminal cursor style.
|
|
|
|
///
|
|
|
|
/// While vi mode is active, this will automatically return the vi mode cursor style.
|
|
|
|
#[inline]
|
|
|
|
pub fn cursor_style(&self) -> CursorStyle {
|
|
|
|
let cursor_style = self.cursor_style.unwrap_or(self.default_cursor_style);
|
|
|
|
|
|
|
|
if self.mode.contains(TermMode::VI) {
|
|
|
|
self.vi_mode_cursor_style.unwrap_or(cursor_style)
|
|
|
|
} else {
|
|
|
|
cursor_style
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-09 23:06:41 +00:00
|
|
|
/// Insert a linebreak at the current cursor position.
|
|
|
|
#[inline]
|
|
|
|
fn wrapline(&mut self)
|
|
|
|
where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
|
|
|
if !self.mode.contains(TermMode::LINE_WRAP) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace!("Wrapping input");
|
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
self.grid.cursor_cell().flags.insert(Flags::WRAPLINE);
|
2020-01-09 23:06:41 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
if self.grid.cursor.point.line + 1 >= self.scroll_region.end {
|
2020-01-09 23:06:41 +00:00
|
|
|
self.linefeed();
|
|
|
|
} else {
|
2020-05-30 20:45:44 +00:00
|
|
|
self.grid.cursor.point.line += 1;
|
2020-01-09 23:06:41 +00:00
|
|
|
}
|
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.cursor.point.column = Column(0);
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = false;
|
2020-01-09 23:06:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Write `c` to the cell at the cursor position.
|
2020-11-05 04:45:14 +00:00
|
|
|
#[inline(always)]
|
2021-04-29 17:06:44 +00:00
|
|
|
fn write_at_cursor(&mut self, c: char) {
|
2020-11-05 04:45:14 +00:00
|
|
|
let c = self.grid.cursor.charsets[self.active_charset].map(c);
|
|
|
|
let fg = self.grid.cursor.template.fg;
|
|
|
|
let bg = self.grid.cursor.template.bg;
|
|
|
|
let flags = self.grid.cursor.template.flags;
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2021-04-29 17:06:44 +00:00
|
|
|
let mut cursor_cell = self.grid.cursor_cell();
|
|
|
|
|
|
|
|
// Clear all related cells when overwriting a fullwidth cell.
|
|
|
|
if cursor_cell.flags.intersects(Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER) {
|
|
|
|
// Remove wide char and spacer.
|
|
|
|
let wide = cursor_cell.flags.contains(Flags::WIDE_CHAR);
|
|
|
|
let point = self.grid.cursor.point;
|
2021-08-01 21:14:17 +00:00
|
|
|
if wide && point.column < self.last_column() {
|
2021-04-29 17:06:44 +00:00
|
|
|
self.grid[point.line][point.column + 1].flags.remove(Flags::WIDE_CHAR_SPACER);
|
2021-07-22 22:11:12 +00:00
|
|
|
} else if point.column > 0 {
|
2021-04-29 17:06:44 +00:00
|
|
|
self.grid[point.line][point.column - 1].clear_wide();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove leading spacers.
|
|
|
|
if point.column <= 1 && point.line != self.topmost_line() {
|
|
|
|
let column = self.last_column();
|
|
|
|
self.grid[point.line - 1i32][column].flags.remove(Flags::LEADING_WIDE_CHAR_SPACER);
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor_cell = self.grid.cursor_cell();
|
|
|
|
}
|
2020-11-05 04:45:14 +00:00
|
|
|
|
|
|
|
cursor_cell.drop_extra();
|
|
|
|
|
|
|
|
cursor_cell.c = c;
|
|
|
|
cursor_cell.fg = fg;
|
|
|
|
cursor_cell.bg = bg;
|
|
|
|
cursor_cell.flags = flags;
|
2020-01-09 23:06:41 +00:00
|
|
|
}
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
impl<T> Dimensions for Term<T> {
|
2016-06-08 17:39:49 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn columns(&self) -> usize {
|
|
|
|
self.grid.columns()
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn screen_lines(&self) -> usize {
|
2020-07-09 21:45:22 +00:00
|
|
|
self.grid.screen_lines()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn total_lines(&self) -> usize {
|
|
|
|
self.grid.total_lines()
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 23:06:41 +00:00
|
|
|
impl<T: EventListener> Handler for Term<T> {
|
2020-05-05 22:50:23 +00:00
|
|
|
/// A character to be displayed.
|
2020-11-05 04:45:14 +00:00
|
|
|
#[inline(never)]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn input(&mut self, c: char) {
|
2020-05-05 22:50:23 +00:00
|
|
|
// Number of cells the char will occupy.
|
2020-01-09 23:06:41 +00:00
|
|
|
let width = match c.width() {
|
|
|
|
Some(width) => width,
|
|
|
|
None => return,
|
|
|
|
};
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Handle zero-width characters.
|
2020-01-09 23:06:41 +00:00
|
|
|
if width == 0 {
|
2020-11-15 00:28:03 +00:00
|
|
|
// Get previous column.
|
2021-04-29 17:06:44 +00:00
|
|
|
let mut column = self.grid.cursor.point.column;
|
2020-11-15 00:28:03 +00:00
|
|
|
if !self.grid.cursor.input_needs_wrap {
|
2021-04-29 17:06:44 +00:00
|
|
|
column.0 = column.saturating_sub(1);
|
2020-11-15 00:28:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Put zerowidth characters over first fullwidth character cell.
|
2020-05-30 20:45:44 +00:00
|
|
|
let line = self.grid.cursor.point.line;
|
2021-04-29 17:06:44 +00:00
|
|
|
if self.grid[line][column].flags.contains(Flags::WIDE_CHAR_SPACER) {
|
|
|
|
column.0 = column.saturating_sub(1);
|
2017-01-12 03:00:29 +00:00
|
|
|
}
|
2020-11-15 00:28:03 +00:00
|
|
|
|
2021-04-29 17:06:44 +00:00
|
|
|
self.grid[line][column].push_zerowidth(c);
|
2020-01-09 23:06:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Move cursor to next line.
|
2020-07-06 05:08:36 +00:00
|
|
|
if self.grid.cursor.input_needs_wrap {
|
2020-01-09 23:06:41 +00:00
|
|
|
self.wrapline();
|
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// If in insert mode, first shift cells to the right.
|
2021-04-29 17:06:44 +00:00
|
|
|
let columns = self.columns();
|
|
|
|
if self.mode.contains(TermMode::INSERT) && self.grid.cursor.point.column + width < columns {
|
2020-05-30 20:45:44 +00:00
|
|
|
let line = self.grid.cursor.point.line;
|
2021-01-24 21:45:36 +00:00
|
|
|
let col = self.grid.cursor.point.column;
|
2020-11-13 02:22:28 +00:00
|
|
|
let row = &mut self.grid[line][..];
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2021-04-29 17:06:44 +00:00
|
|
|
for col in (col.0..(columns - width)).rev() {
|
2020-11-13 02:22:28 +00:00
|
|
|
row.swap(col + width, col);
|
2016-08-20 00:55:44 +00:00
|
|
|
}
|
2016-07-30 03:16:13 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 23:06:41 +00:00
|
|
|
if width == 1 {
|
|
|
|
self.write_at_cursor(c);
|
|
|
|
} else {
|
2021-04-29 17:06:44 +00:00
|
|
|
if self.grid.cursor.point.column + 1 >= columns {
|
2020-05-17 15:32:06 +00:00
|
|
|
if self.mode.contains(TermMode::LINE_WRAP) {
|
|
|
|
// Insert placeholder before wide char if glyph does not fit in this row.
|
2021-04-29 17:06:44 +00:00
|
|
|
self.grid.cursor.template.flags.insert(Flags::LEADING_WIDE_CHAR_SPACER);
|
|
|
|
self.write_at_cursor(' ');
|
|
|
|
self.grid.cursor.template.flags.remove(Flags::LEADING_WIDE_CHAR_SPACER);
|
2020-05-17 15:32:06 +00:00
|
|
|
self.wrapline();
|
|
|
|
} else {
|
|
|
|
// Prevent out of bounds crash when linewrapping is disabled.
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = true;
|
2020-05-17 15:32:06 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-12-10 17:53:56 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Write full width glyph to current cursor cell.
|
2021-04-29 17:06:44 +00:00
|
|
|
self.grid.cursor.template.flags.insert(Flags::WIDE_CHAR);
|
|
|
|
self.write_at_cursor(c);
|
|
|
|
self.grid.cursor.template.flags.remove(Flags::WIDE_CHAR);
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Write spacer to cell following the wide glyph.
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.cursor.point.column += 1;
|
2021-04-29 17:06:44 +00:00
|
|
|
self.grid.cursor.template.flags.insert(Flags::WIDE_CHAR_SPACER);
|
|
|
|
self.write_at_cursor(' ');
|
|
|
|
self.grid.cursor.template.flags.remove(Flags::WIDE_CHAR_SPACER);
|
2017-01-12 03:00:29 +00:00
|
|
|
}
|
|
|
|
|
2021-04-29 17:06:44 +00:00
|
|
|
if self.grid.cursor.point.column + 1 < columns {
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.cursor.point.column += 1;
|
2017-01-07 23:13:42 +00:00
|
|
|
} else {
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = true;
|
2016-07-30 03:16:13 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 17:42:29 +00:00
|
|
|
#[inline]
|
2019-11-17 00:04:16 +00:00
|
|
|
fn decaln(&mut self) {
|
|
|
|
trace!("Decalnning");
|
2017-04-18 17:42:29 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
for line in (0..self.screen_lines()).map(Line::from) {
|
|
|
|
for column in 0..self.columns() {
|
2021-01-24 21:45:36 +00:00
|
|
|
let cell = &mut self.grid[line][Column(column)];
|
|
|
|
*cell = Cell::default();
|
|
|
|
cell.c = 'E';
|
|
|
|
}
|
|
|
|
}
|
2017-04-18 17:42:29 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto(&mut self, line: Line, col: Column) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Going to: line={}, col={}", line, col);
|
2019-04-19 20:56:11 +00:00
|
|
|
let (y_offset, max_y) = if self.mode.contains(TermMode::ORIGIN) {
|
2017-04-20 03:08:44 +00:00
|
|
|
(self.scroll_region.start, self.scroll_region.end - 1)
|
2017-04-19 05:59:24 +00:00
|
|
|
} else {
|
2021-03-30 23:25:38 +00:00
|
|
|
(Line(0), self.bottommost_line())
|
2017-04-19 05:59:24 +00:00
|
|
|
};
|
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
self.grid.cursor.point.line = max(min(line + y_offset, max_y), Line(0));
|
|
|
|
self.grid.cursor.point.column = min(col, self.last_column());
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2016-07-04 00:00:00 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto_line(&mut self, line: Line) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Going to line: {}", line);
|
2021-01-24 21:45:36 +00:00
|
|
|
self.goto(line, self.grid.cursor.point.column)
|
2016-06-06 22:13:45 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto_col(&mut self, col: Column) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Going to column: {}", col);
|
2020-05-30 20:45:44 +00:00
|
|
|
self.goto(self.grid.cursor.point.line, col)
|
2016-06-06 22:13:45 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn insert_blank(&mut self, count: usize) {
|
2020-11-05 04:45:14 +00:00
|
|
|
let cursor = &self.grid.cursor;
|
|
|
|
let bg = cursor.template.bg;
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
// Ensure inserting within terminal bounds
|
2021-03-30 23:25:38 +00:00
|
|
|
let count = min(count, self.columns() - cursor.point.column.0);
|
2016-10-10 15:29:51 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
let source = cursor.point.column;
|
2021-03-30 23:25:38 +00:00
|
|
|
let destination = cursor.point.column.0 + count;
|
|
|
|
let num_cells = self.columns() - destination;
|
2016-10-10 15:29:51 +00:00
|
|
|
|
2020-11-05 04:45:14 +00:00
|
|
|
let line = cursor.point.line;
|
2020-11-13 02:22:28 +00:00
|
|
|
let row = &mut self.grid[line][..];
|
2016-10-10 15:29:51 +00:00
|
|
|
|
2020-11-13 02:22:28 +00:00
|
|
|
for offset in (0..num_cells).rev() {
|
2021-03-30 23:25:38 +00:00
|
|
|
row.swap(destination + offset, source.0 + offset);
|
2016-10-10 15:29:51 +00:00
|
|
|
}
|
|
|
|
|
2020-07-15 21:27:32 +00:00
|
|
|
// Cells were just moved out toward the end of the line;
|
|
|
|
// fill in between source and dest with blanks.
|
2021-03-30 23:25:38 +00:00
|
|
|
for cell in &mut row[source.0..destination] {
|
2020-11-05 04:45:14 +00:00
|
|
|
*cell = bg.into();
|
2016-10-10 15:29:51 +00:00
|
|
|
}
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn move_up(&mut self, lines: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Moving up: {}", lines);
|
2021-03-30 23:25:38 +00:00
|
|
|
self.goto(self.grid.cursor.point.line - lines, self.grid.cursor.point.column)
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn move_down(&mut self, lines: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Moving down: {}", lines);
|
2021-03-30 23:25:38 +00:00
|
|
|
self.goto(self.grid.cursor.point.line + lines, self.grid.cursor.point.column)
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_forward(&mut self, cols: Column) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Moving forward: {}", cols);
|
2021-03-30 23:25:38 +00:00
|
|
|
let last_column = self.last_column();
|
|
|
|
self.grid.cursor.point.column = min(self.grid.cursor.point.column + cols, last_column);
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_backward(&mut self, cols: Column) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Moving backward: {}", cols);
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.cursor.point.column =
|
|
|
|
Column(self.grid.cursor.point.column.saturating_sub(cols.0));
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2021-04-17 23:20:13 +00:00
|
|
|
fn identify_terminal(&mut self, intermediate: Option<char>) {
|
2020-07-23 21:55:15 +00:00
|
|
|
match intermediate {
|
|
|
|
None => {
|
|
|
|
trace!("Reporting primary device attributes");
|
2021-04-17 23:20:13 +00:00
|
|
|
let text = String::from("\x1b[?6c");
|
|
|
|
self.event_proxy.send_event(Event::PtyWrite(text));
|
2020-07-23 21:55:15 +00:00
|
|
|
},
|
|
|
|
Some('>') => {
|
|
|
|
trace!("Reporting secondary device attributes");
|
|
|
|
let version = version_number(env!("CARGO_PKG_VERSION"));
|
2021-04-17 23:20:13 +00:00
|
|
|
let text = format!("\x1b[>0;{};1c", version);
|
|
|
|
self.event_proxy.send_event(Event::PtyWrite(text));
|
2020-07-23 21:55:15 +00:00
|
|
|
},
|
|
|
|
_ => debug!("Unsupported device attributes intermediate"),
|
|
|
|
}
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2017-05-07 20:08:23 +00:00
|
|
|
#[inline]
|
2021-04-17 23:20:13 +00:00
|
|
|
fn device_status(&mut self, arg: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Reporting device status: {}", arg);
|
2017-05-08 14:50:34 +00:00
|
|
|
match arg {
|
|
|
|
5 => {
|
2021-04-17 23:20:13 +00:00
|
|
|
let text = String::from("\x1b[0n");
|
|
|
|
self.event_proxy.send_event(Event::PtyWrite(text));
|
2017-05-08 14:50:34 +00:00
|
|
|
},
|
|
|
|
6 => {
|
2020-05-30 20:45:44 +00:00
|
|
|
let pos = self.grid.cursor.point;
|
2021-04-17 23:20:13 +00:00
|
|
|
let text = format!("\x1b[{};{}R", pos.line + 1, pos.column + 1);
|
|
|
|
self.event_proxy.send_event(Event::PtyWrite(text));
|
2017-05-08 14:50:34 +00:00
|
|
|
},
|
|
|
|
_ => debug!("unknown device status query: {}", arg),
|
|
|
|
};
|
2017-05-07 20:08:23 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn move_down_and_cr(&mut self, lines: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Moving down and cr: {}", lines);
|
2021-03-30 23:25:38 +00:00
|
|
|
self.goto(self.grid.cursor.point.line + lines, Column(0))
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn move_up_and_cr(&mut self, lines: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Moving up and cr: {}", lines);
|
2021-03-30 23:25:38 +00:00
|
|
|
self.goto(self.grid.cursor.point.line - lines, Column(0))
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:00:00 +00:00
|
|
|
|
2020-03-01 03:27:23 +00:00
|
|
|
/// Insert tab at cursor position.
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2020-11-26 00:57:44 +00:00
|
|
|
fn put_tab(&mut self, mut count: u16) {
|
2020-05-05 22:50:23 +00:00
|
|
|
// A tab after the last column is the same as a linebreak.
|
2020-07-06 05:08:36 +00:00
|
|
|
if self.grid.cursor.input_needs_wrap {
|
2020-03-01 03:27:23 +00:00
|
|
|
self.wrapline();
|
|
|
|
return;
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
while self.grid.cursor.point.column < self.columns() && count != 0 {
|
2016-05-31 03:44:37 +00:00
|
|
|
count -= 1;
|
2018-12-15 21:33:33 +00:00
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
let c = self.grid.cursor.charsets[self.active_charset].map('\t');
|
|
|
|
let cell = self.grid.cursor_cell();
|
2019-01-13 21:54:36 +00:00
|
|
|
if cell.c == ' ' {
|
2020-05-30 20:45:44 +00:00
|
|
|
cell.c = c;
|
2019-01-13 21:54:36 +00:00
|
|
|
}
|
2018-12-15 21:33:33 +00:00
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
loop {
|
2021-03-30 23:25:38 +00:00
|
|
|
if (self.grid.cursor.point.column + 1) == self.columns() {
|
2016-05-31 03:44:37 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-02-27 08:12:04 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.cursor.point.column += 1;
|
2017-04-04 12:08:10 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
if self.tabs[self.grid.cursor.point.column] {
|
2017-04-04 12:08:10 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 17:06:44 +00:00
|
|
|
/// Backspace.
|
2016-05-31 03:44:37 +00:00
|
|
|
#[inline]
|
2016-06-09 15:37:59 +00:00
|
|
|
fn backspace(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Backspace");
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
if self.grid.cursor.point.column > Column(0) {
|
|
|
|
self.grid.cursor.point.column -= 1;
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = false;
|
2016-12-10 18:14:45 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Carriage return.
|
2016-05-31 03:44:37 +00:00
|
|
|
#[inline]
|
|
|
|
fn carriage_return(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Carriage return");
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.cursor.point.column = Column(0);
|
2020-07-06 05:08:36 +00:00
|
|
|
self.grid.cursor.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Linefeed.
|
2016-05-31 03:44:37 +00:00
|
|
|
#[inline]
|
|
|
|
fn linefeed(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Linefeed");
|
2020-05-30 20:45:44 +00:00
|
|
|
let next = self.grid.cursor.point.line + 1;
|
2017-11-11 16:45:17 +00:00
|
|
|
if next == self.scroll_region.end {
|
2021-03-30 23:25:38 +00:00
|
|
|
self.scroll_up(1);
|
2020-07-09 21:45:22 +00:00
|
|
|
} else if next < self.screen_lines() {
|
2020-05-30 20:45:44 +00:00
|
|
|
self.grid.cursor.point.line += 1;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Set current position as a tabstop.
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn bell(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Bell");
|
2020-07-10 19:32:44 +00:00
|
|
|
self.event_proxy.send_event(Event::Bell);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn substitute(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("[unimplemented] Substitute");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Run LF/NL.
|
2017-04-18 17:41:11 +00:00
|
|
|
///
|
|
|
|
/// LF/NL mode has some interesting history. According to ECMA-48 4th
|
|
|
|
/// edition, in LINE FEED mode,
|
|
|
|
///
|
2017-10-30 15:03:58 +00:00
|
|
|
/// > The execution of the formatter functions LINE FEED (LF), FORM FEED
|
2017-04-18 17:41:11 +00:00
|
|
|
/// (FF), LINE TABULATION (VT) cause only movement of the active position in
|
|
|
|
/// the direction of the line progression.
|
|
|
|
///
|
|
|
|
/// In NEW LINE mode,
|
|
|
|
///
|
2017-10-30 15:03:58 +00:00
|
|
|
/// > The execution of the formatter functions LINE FEED (LF), FORM FEED
|
2017-04-18 17:41:11 +00:00
|
|
|
/// (FF), LINE TABULATION (VT) cause movement to the line home position on
|
|
|
|
/// the following line, the following form, etc. In the case of LF this is
|
|
|
|
/// referred to as the New Line (NL) option.
|
|
|
|
///
|
|
|
|
/// Additionally, ECMA-48 4th edition says that this option is deprecated.
|
|
|
|
/// ECMA-48 5th edition only mentions this option (without explanation)
|
|
|
|
/// saying that it's been removed.
|
|
|
|
///
|
|
|
|
/// As an emulator, we need to support it since applications may still rely
|
|
|
|
/// on it.
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn newline(&mut self) {
|
2017-04-18 17:41:11 +00:00
|
|
|
self.linefeed();
|
|
|
|
|
2019-04-19 20:56:11 +00:00
|
|
|
if self.mode.contains(TermMode::LINE_FEED_NEW_LINE) {
|
2017-04-18 17:41:11 +00:00
|
|
|
self.carriage_return();
|
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn set_horizontal_tabstop(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Setting horizontal tabstop");
|
2021-01-24 21:45:36 +00:00
|
|
|
self.tabs[self.grid.cursor.point.column] = true;
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn scroll_up(&mut self, lines: usize) {
|
2016-08-22 15:37:50 +00:00
|
|
|
let origin = self.scroll_region.start;
|
|
|
|
self.scroll_up_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn scroll_down(&mut self, lines: usize) {
|
2016-08-22 15:37:50 +00:00
|
|
|
let origin = self.scroll_region.start;
|
|
|
|
self.scroll_down_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn insert_blank_lines(&mut self, lines: usize) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Inserting blank {} lines", lines);
|
2020-05-30 20:45:44 +00:00
|
|
|
|
|
|
|
let origin = self.grid.cursor.point.line;
|
|
|
|
if self.scroll_region.contains(&origin) {
|
2016-08-22 15:37:50 +00:00
|
|
|
self.scroll_down_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn delete_lines(&mut self, lines: usize) {
|
2020-05-30 20:45:44 +00:00
|
|
|
let origin = self.grid.cursor.point.line;
|
2021-03-30 23:25:38 +00:00
|
|
|
let lines = min(self.screen_lines() - origin.0 as usize, lines);
|
2019-11-15 19:38:52 +00:00
|
|
|
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Deleting {} lines", lines);
|
2019-11-15 19:38:52 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
if lines > 0 && self.scroll_region.contains(&origin) {
|
2016-08-22 15:37:50 +00:00
|
|
|
self.scroll_up_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn erase_chars(&mut self, count: Column) {
|
2020-11-05 04:45:14 +00:00
|
|
|
let cursor = &self.grid.cursor;
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
trace!("Erasing chars: count={}, col={}", count, cursor.point.column);
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
let start = cursor.point.column;
|
2021-03-30 23:25:38 +00:00
|
|
|
let end = min(start + count, Column(self.columns()));
|
2016-07-02 04:13:09 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Cleared cells have current background color set.
|
2020-11-05 04:45:14 +00:00
|
|
|
let bg = self.grid.cursor.template.bg;
|
|
|
|
let line = cursor.point.line;
|
|
|
|
let row = &mut self.grid[line];
|
|
|
|
for cell in &mut row[start..end] {
|
|
|
|
*cell = bg.into();
|
2016-07-02 04:13:09 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn delete_chars(&mut self, count: usize) {
|
|
|
|
let columns = self.columns();
|
2020-11-05 04:45:14 +00:00
|
|
|
let cursor = &self.grid.cursor;
|
|
|
|
let bg = cursor.template.bg;
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Ensure deleting within terminal bounds.
|
2021-03-30 23:25:38 +00:00
|
|
|
let count = min(count, columns);
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let start = cursor.point.column.0;
|
|
|
|
let end = min(start + count, columns - 1);
|
|
|
|
let num_cells = columns - end;
|
2016-09-15 15:49:55 +00:00
|
|
|
|
2020-11-05 04:45:14 +00:00
|
|
|
let line = cursor.point.line;
|
2020-11-13 02:22:28 +00:00
|
|
|
let row = &mut self.grid[line][..];
|
2016-09-15 15:49:55 +00:00
|
|
|
|
2020-11-13 02:22:28 +00:00
|
|
|
for offset in 0..num_cells {
|
2021-03-30 23:25:38 +00:00
|
|
|
row.swap(start + offset, end + offset);
|
2016-09-15 15:49:55 +00:00
|
|
|
}
|
|
|
|
|
2020-11-05 04:45:14 +00:00
|
|
|
// Clear last `count` cells in the row. If deleting 1 char, need to delete
|
2016-12-17 06:48:04 +00:00
|
|
|
// 1 cell.
|
2021-03-30 23:25:38 +00:00
|
|
|
let end = columns - count;
|
2020-11-05 04:45:14 +00:00
|
|
|
for cell in &mut row[end..] {
|
|
|
|
*cell = bg.into();
|
2016-09-15 15:49:55 +00:00
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2020-11-26 00:57:44 +00:00
|
|
|
fn move_backward_tabs(&mut self, count: u16) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Moving backward {} tabs", count);
|
2017-06-15 19:46:53 +00:00
|
|
|
|
|
|
|
for _ in 0..count {
|
2021-01-24 21:45:36 +00:00
|
|
|
let mut col = self.grid.cursor.point.column;
|
2017-06-15 19:46:53 +00:00
|
|
|
for i in (0..(col.0)).rev() {
|
2018-12-15 21:33:33 +00:00
|
|
|
if self.tabs[index::Column(i)] {
|
2017-06-15 19:46:53 +00:00
|
|
|
col = index::Column(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.cursor.point.column = col;
|
2017-06-15 19:46:53 +00:00
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2020-11-26 00:57:44 +00:00
|
|
|
fn move_forward_tabs(&mut self, count: u16) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("[unimplemented] Moving forward {} tabs", count);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn save_cursor_position(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Saving cursor position");
|
2017-02-01 17:55:23 +00:00
|
|
|
|
2020-11-05 04:45:14 +00:00
|
|
|
self.grid.saved_cursor = self.grid.cursor.clone();
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn restore_cursor_position(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Restoring cursor position");
|
2017-02-01 17:55:23 +00:00
|
|
|
|
2020-11-05 04:45:14 +00:00
|
|
|
self.grid.cursor = self.grid.saved_cursor.clone();
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn clear_line(&mut self, mode: ansi::LineClearMode) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Clearing line: {:?}", mode);
|
2017-04-20 03:43:02 +00:00
|
|
|
|
2020-11-05 04:45:14 +00:00
|
|
|
let cursor = &self.grid.cursor;
|
|
|
|
let bg = cursor.template.bg;
|
|
|
|
|
|
|
|
let point = cursor.point;
|
|
|
|
let row = &mut self.grid[point.line];
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
|
|
|
ansi::LineClearMode::Right => {
|
2021-01-24 21:45:36 +00:00
|
|
|
for cell in &mut row[point.column..] {
|
2020-11-05 04:45:14 +00:00
|
|
|
*cell = bg.into();
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
},
|
2016-08-22 15:11:34 +00:00
|
|
|
ansi::LineClearMode::Left => {
|
2021-01-24 21:45:36 +00:00
|
|
|
for cell in &mut row[..=point.column] {
|
2020-11-05 04:45:14 +00:00
|
|
|
*cell = bg.into();
|
2016-08-22 15:11:34 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
ansi::LineClearMode::All => {
|
|
|
|
for cell in &mut row[..] {
|
2020-11-05 04:45:14 +00:00
|
|
|
*cell = bg.into();
|
2016-08-22 15:11:34 +00:00
|
|
|
}
|
|
|
|
},
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2020-06-26 16:04:55 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let range = self.grid.cursor.point.line..=self.grid.cursor.point.line;
|
|
|
|
self.selection = self.selection.take().filter(|s| !s.intersects_range(range));
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Set the indexed color value.
|
2017-02-06 21:28:33 +00:00
|
|
|
#[inline]
|
|
|
|
fn set_color(&mut self, index: usize, color: Rgb) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Setting color[{}] = {:?}", index, color);
|
2021-01-24 21:45:36 +00:00
|
|
|
self.colors[index] = Some(color);
|
2017-02-06 21:28:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Write a foreground/background color escape sequence with the current color.
|
2019-06-02 13:16:38 +00:00
|
|
|
#[inline]
|
2021-01-24 21:45:36 +00:00
|
|
|
fn dynamic_color_sequence(&mut self, code: u8, index: usize, terminator: &str) {
|
|
|
|
trace!("Requested write of escape sequence for color code {}: color[{}]", code, index);
|
|
|
|
|
|
|
|
let terminator = terminator.to_owned();
|
|
|
|
self.event_proxy.send_event(Event::ColorRequest(
|
|
|
|
index,
|
|
|
|
Arc::new(move |color| {
|
|
|
|
format!(
|
|
|
|
"\x1b]{};rgb:{1:02x}{1:02x}/{2:02x}{2:02x}/{3:02x}{3:02x}{4}",
|
|
|
|
code, color.r, color.g, color.b, terminator
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
));
|
2019-06-02 13:16:38 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Reset the indexed color to original value.
|
2017-07-28 22:14:18 +00:00
|
|
|
#[inline]
|
|
|
|
fn reset_color(&mut self, index: usize) {
|
2019-04-28 21:42:43 +00:00
|
|
|
trace!("Resetting color[{}]", index);
|
2021-01-24 21:45:36 +00:00
|
|
|
self.colors[index] = None;
|
2017-07-28 22:14:18 +00:00
|
|
|
}
|
|
|
|
|
2020-06-06 21:33:20 +00:00
|
|
|
/// Store data into clipboard.
|
2018-01-02 16:32:50 +00:00
|
|
|
#[inline]
|
2020-06-06 21:33:20 +00:00
|
|
|
fn clipboard_store(&mut self, clipboard: u8, base64: &[u8]) {
|
2019-11-11 00:12:14 +00:00
|
|
|
let clipboard_type = match clipboard {
|
|
|
|
b'c' => ClipboardType::Clipboard,
|
|
|
|
b'p' | b's' => ClipboardType::Selection,
|
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Ok(bytes) = base64::decode(base64) {
|
2020-06-06 21:33:20 +00:00
|
|
|
if let Ok(text) = String::from_utf8(bytes) {
|
|
|
|
self.event_proxy.send_event(Event::ClipboardStore(clipboard_type, text));
|
2019-11-11 00:12:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-06 21:33:20 +00:00
|
|
|
/// Load data from clipboard.
|
2019-11-11 00:12:14 +00:00
|
|
|
#[inline]
|
2020-06-06 21:33:20 +00:00
|
|
|
fn clipboard_load(&mut self, clipboard: u8, terminator: &str) {
|
2019-11-11 00:12:14 +00:00
|
|
|
let clipboard_type = match clipboard {
|
|
|
|
b'c' => ClipboardType::Clipboard,
|
|
|
|
b'p' | b's' => ClipboardType::Selection,
|
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
|
2020-06-06 21:33:20 +00:00
|
|
|
let terminator = terminator.to_owned();
|
|
|
|
|
|
|
|
self.event_proxy.send_event(Event::ClipboardLoad(
|
|
|
|
clipboard_type,
|
|
|
|
Arc::new(move |text| {
|
|
|
|
let base64 = base64::encode(&text);
|
|
|
|
format!("\x1b]52;{};{}{}", clipboard as char, base64, terminator)
|
|
|
|
}),
|
|
|
|
));
|
2018-01-02 16:32:50 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn clear_screen(&mut self, mode: ansi::ClearMode) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Clearing screen: {:?}", mode);
|
2020-11-05 04:45:14 +00:00
|
|
|
let bg = self.grid.cursor.template.bg;
|
2017-04-20 03:43:02 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let screen_lines = self.screen_lines();
|
2019-02-08 04:49:44 +00:00
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2017-02-06 21:13:25 +00:00
|
|
|
ansi::ClearMode::Above => {
|
2020-05-30 20:45:44 +00:00
|
|
|
let cursor = self.grid.cursor.point;
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// If clearing more than one line.
|
2021-03-30 23:25:38 +00:00
|
|
|
if cursor.line > 1 {
|
2020-05-05 22:50:23 +00:00
|
|
|
// Fully clear all lines before the current line.
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.reset_region(..cursor.line);
|
2017-02-06 21:13:25 +00:00
|
|
|
}
|
2020-05-30 20:45:44 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Clear up to the current column in the current line.
|
2021-03-30 23:25:38 +00:00
|
|
|
let end = min(cursor.column + 1, Column(self.columns()));
|
2020-05-30 20:45:44 +00:00
|
|
|
for cell in &mut self.grid[cursor.line][..end] {
|
2020-11-05 04:45:14 +00:00
|
|
|
*cell = bg.into();
|
2017-02-06 21:13:25 +00:00
|
|
|
}
|
2020-06-26 16:04:55 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let range = Line(0)..=cursor.line;
|
|
|
|
self.selection = self.selection.take().filter(|s| !s.intersects_range(range));
|
2017-03-04 04:22:54 +00:00
|
|
|
},
|
2019-11-18 21:15:25 +00:00
|
|
|
ansi::ClearMode::Below => {
|
2020-05-30 20:45:44 +00:00
|
|
|
let cursor = self.grid.cursor.point;
|
2021-01-24 21:45:36 +00:00
|
|
|
for cell in &mut self.grid[cursor.line][cursor.column..] {
|
2020-11-05 04:45:14 +00:00
|
|
|
*cell = bg.into();
|
2019-11-18 21:15:25 +00:00
|
|
|
}
|
2020-06-26 16:04:55 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
if (cursor.line.0 as usize) < screen_lines - 1 {
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.reset_region((cursor.line + 1)..);
|
2019-11-18 21:15:25 +00:00
|
|
|
}
|
2020-06-26 16:04:55 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let range = cursor.line..Line(screen_lines as i32);
|
|
|
|
self.selection = self.selection.take().filter(|s| !s.intersects_range(range));
|
2019-11-18 21:15:25 +00:00
|
|
|
},
|
|
|
|
ansi::ClearMode::All => {
|
|
|
|
if self.mode.contains(TermMode::ALT_SCREEN) {
|
2021-01-24 21:45:36 +00:00
|
|
|
self.grid.reset_region(..);
|
2019-11-18 21:15:25 +00:00
|
|
|
} else {
|
2021-12-18 15:36:56 +00:00
|
|
|
let old_offset = self.grid.display_offset();
|
|
|
|
|
2020-11-05 04:45:14 +00:00
|
|
|
self.grid.clear_viewport();
|
2021-12-03 07:45:05 +00:00
|
|
|
|
2021-12-18 15:36:56 +00:00
|
|
|
// Compute number of lines scrolled by clearing the viewport.
|
|
|
|
let lines = self.grid.display_offset().saturating_sub(old_offset);
|
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
self.vi_mode_cursor.point.line =
|
2021-12-18 15:36:56 +00:00
|
|
|
(self.vi_mode_cursor.point.line - lines).grid_clamp(self, Boundary::Grid);
|
2019-11-18 21:15:25 +00:00
|
|
|
}
|
2020-06-26 16:04:55 +00:00
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
self.selection = None;
|
2020-06-26 16:04:55 +00:00
|
|
|
},
|
2020-08-25 18:24:16 +00:00
|
|
|
ansi::ClearMode::Saved if self.history_size() > 0 => {
|
2020-06-26 16:04:55 +00:00
|
|
|
self.grid.clear_history();
|
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
self.vi_mode_cursor.point.line =
|
|
|
|
self.vi_mode_cursor.point.line.grid_clamp(self, Boundary::Cursor);
|
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
self.selection = self.selection.take().filter(|s| !s.intersects_range(..Line(0)));
|
2019-11-18 21:15:25 +00:00
|
|
|
},
|
2020-06-26 16:04:55 +00:00
|
|
|
// We have no history to clear.
|
|
|
|
ansi::ClearMode::Saved => (),
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn clear_tabs(&mut self, mode: ansi::TabulationClearMode) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Clearing tabs: {:?}", mode);
|
2017-05-01 04:11:47 +00:00
|
|
|
match mode {
|
|
|
|
ansi::TabulationClearMode::Current => {
|
2021-01-24 21:45:36 +00:00
|
|
|
self.tabs[self.grid.cursor.point.column] = false;
|
2017-05-01 04:11:47 +00:00
|
|
|
},
|
|
|
|
ansi::TabulationClearMode::All => {
|
2018-12-15 21:33:33 +00:00
|
|
|
self.tabs.clear_all();
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2017-05-01 04:11:47 +00:00
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Reset all important fields in the term struct.
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn reset_state(&mut self) {
|
2020-06-28 19:12:37 +00:00
|
|
|
if self.mode.contains(TermMode::ALT_SCREEN) {
|
|
|
|
mem::swap(&mut self.grid, &mut self.inactive_grid);
|
2019-03-02 21:30:29 +00:00
|
|
|
}
|
2018-01-18 17:27:07 +00:00
|
|
|
self.active_charset = Default::default();
|
|
|
|
self.cursor_style = None;
|
2020-11-05 04:45:14 +00:00
|
|
|
self.grid.reset();
|
|
|
|
self.inactive_grid.reset();
|
2021-03-30 23:25:38 +00:00
|
|
|
self.scroll_region = Line(0)..Line(self.screen_lines() as i32);
|
|
|
|
self.tabs = TabStops::new(self.columns());
|
2020-03-14 15:09:10 +00:00
|
|
|
self.title_stack = Vec::new();
|
|
|
|
self.title = None;
|
2020-05-30 20:45:44 +00:00
|
|
|
self.selection = None;
|
2020-11-19 01:15:34 +00:00
|
|
|
|
|
|
|
// Preserve vi mode across resets.
|
|
|
|
self.mode &= TermMode::VI;
|
|
|
|
self.mode.insert(TermMode::default());
|
2020-11-23 23:11:03 +00:00
|
|
|
|
2021-10-23 07:16:47 +00:00
|
|
|
self.event_proxy.send_event(Event::CursorBlinkingChange);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn reverse_index(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Reversing index");
|
2020-07-09 21:45:22 +00:00
|
|
|
// If cursor is at the top.
|
2020-05-30 20:45:44 +00:00
|
|
|
if self.grid.cursor.point.line == self.scroll_region.start {
|
2021-03-30 23:25:38 +00:00
|
|
|
self.scroll_down(1);
|
2016-05-31 03:44:37 +00:00
|
|
|
} else {
|
2021-03-30 23:25:38 +00:00
|
|
|
self.grid.cursor.point.line = max(self.grid.cursor.point.line - 1, Line(0));
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Set a terminal attribute.
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn terminal_attribute(&mut self, attr: Attr) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Setting attribute: {:?}", attr);
|
2020-05-30 20:45:44 +00:00
|
|
|
let cursor = &mut self.grid.cursor;
|
2016-05-31 03:44:37 +00:00
|
|
|
match attr {
|
2020-05-30 20:45:44 +00:00
|
|
|
Attr::Foreground(color) => cursor.template.fg = color,
|
|
|
|
Attr::Background(color) => cursor.template.bg = color,
|
2016-05-31 03:44:37 +00:00
|
|
|
Attr::Reset => {
|
2020-05-30 20:45:44 +00:00
|
|
|
cursor.template.fg = Color::Named(NamedColor::Foreground);
|
|
|
|
cursor.template.bg = Color::Named(NamedColor::Background);
|
|
|
|
cursor.template.flags = Flags::empty();
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2020-05-30 20:45:44 +00:00
|
|
|
Attr::Reverse => cursor.template.flags.insert(Flags::INVERSE),
|
|
|
|
Attr::CancelReverse => cursor.template.flags.remove(Flags::INVERSE),
|
|
|
|
Attr::Bold => cursor.template.flags.insert(Flags::BOLD),
|
|
|
|
Attr::CancelBold => cursor.template.flags.remove(Flags::BOLD),
|
|
|
|
Attr::Dim => cursor.template.flags.insert(Flags::DIM),
|
|
|
|
Attr::CancelBoldDim => cursor.template.flags.remove(Flags::BOLD | Flags::DIM),
|
|
|
|
Attr::Italic => cursor.template.flags.insert(Flags::ITALIC),
|
|
|
|
Attr::CancelItalic => cursor.template.flags.remove(Flags::ITALIC),
|
2020-08-12 16:05:22 +00:00
|
|
|
Attr::Underline => {
|
|
|
|
cursor.template.flags.remove(Flags::DOUBLE_UNDERLINE);
|
|
|
|
cursor.template.flags.insert(Flags::UNDERLINE);
|
|
|
|
},
|
|
|
|
Attr::DoubleUnderline => {
|
|
|
|
cursor.template.flags.remove(Flags::UNDERLINE);
|
|
|
|
cursor.template.flags.insert(Flags::DOUBLE_UNDERLINE);
|
|
|
|
},
|
|
|
|
Attr::CancelUnderline => {
|
|
|
|
cursor.template.flags.remove(Flags::UNDERLINE | Flags::DOUBLE_UNDERLINE);
|
|
|
|
},
|
2020-05-30 20:45:44 +00:00
|
|
|
Attr::Hidden => cursor.template.flags.insert(Flags::HIDDEN),
|
|
|
|
Attr::CancelHidden => cursor.template.flags.remove(Flags::HIDDEN),
|
|
|
|
Attr::Strike => cursor.template.flags.insert(Flags::STRIKEOUT),
|
|
|
|
Attr::CancelStrike => cursor.template.flags.remove(Flags::STRIKEOUT),
|
2016-05-31 03:44:37 +00:00
|
|
|
_ => {
|
2017-01-13 07:15:06 +00:00
|
|
|
debug!("Term got unhandled attr: {:?}", attr);
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn set_mode(&mut self, mode: ansi::Mode) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Setting mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2020-10-10 21:24:40 +00:00
|
|
|
ansi::Mode::UrgencyHints => self.mode.insert(TermMode::URGENCY_HINTS),
|
2017-02-01 17:55:23 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
2020-06-28 19:12:37 +00:00
|
|
|
if !self.mode.contains(TermMode::ALT_SCREEN) {
|
2017-08-09 18:24:51 +00:00
|
|
|
self.swap_alt();
|
|
|
|
}
|
2017-02-01 17:55:23 +00:00
|
|
|
},
|
2019-04-19 20:56:11 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.insert(TermMode::SHOW_CURSOR),
|
|
|
|
ansi::Mode::CursorKeys => self.mode.insert(TermMode::APP_CURSOR),
|
2020-05-05 22:50:23 +00:00
|
|
|
// Mouse protocols are mutually exclusive.
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => {
|
2019-11-12 16:50:33 +00:00
|
|
|
self.mode.remove(TermMode::MOUSE_MODE);
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.insert(TermMode::MOUSE_REPORT_CLICK);
|
2019-10-05 00:29:26 +00:00
|
|
|
self.event_proxy.send_event(Event::MouseCursorDirty);
|
2017-12-24 20:15:42 +00:00
|
|
|
},
|
2018-01-26 21:28:43 +00:00
|
|
|
ansi::Mode::ReportCellMouseMotion => {
|
2019-11-12 16:50:33 +00:00
|
|
|
self.mode.remove(TermMode::MOUSE_MODE);
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.insert(TermMode::MOUSE_DRAG);
|
2019-10-05 00:29:26 +00:00
|
|
|
self.event_proxy.send_event(Event::MouseCursorDirty);
|
2018-01-26 21:28:43 +00:00
|
|
|
},
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportAllMouseMotion => {
|
2019-11-12 16:50:33 +00:00
|
|
|
self.mode.remove(TermMode::MOUSE_MODE);
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.insert(TermMode::MOUSE_MOTION);
|
2019-10-05 00:29:26 +00:00
|
|
|
self.event_proxy.send_event(Event::MouseCursorDirty);
|
2017-12-24 20:15:42 +00:00
|
|
|
},
|
2019-04-19 20:56:11 +00:00
|
|
|
ansi::Mode::ReportFocusInOut => self.mode.insert(TermMode::FOCUS_IN_OUT),
|
|
|
|
ansi::Mode::BracketedPaste => self.mode.insert(TermMode::BRACKETED_PASTE),
|
2020-05-05 22:50:23 +00:00
|
|
|
// Mouse encodings are mutually exclusive.
|
2019-11-12 16:50:33 +00:00
|
|
|
ansi::Mode::SgrMouse => {
|
|
|
|
self.mode.remove(TermMode::UTF8_MOUSE);
|
|
|
|
self.mode.insert(TermMode::SGR_MOUSE);
|
|
|
|
},
|
|
|
|
ansi::Mode::Utf8Mouse => {
|
|
|
|
self.mode.remove(TermMode::SGR_MOUSE);
|
|
|
|
self.mode.insert(TermMode::UTF8_MOUSE);
|
|
|
|
},
|
2019-10-15 19:13:58 +00:00
|
|
|
ansi::Mode::AlternateScroll => self.mode.insert(TermMode::ALTERNATE_SCROLL),
|
2019-04-19 20:56:11 +00:00
|
|
|
ansi::Mode::LineWrap => self.mode.insert(TermMode::LINE_WRAP),
|
|
|
|
ansi::Mode::LineFeedNewLine => self.mode.insert(TermMode::LINE_FEED_NEW_LINE),
|
|
|
|
ansi::Mode::Origin => self.mode.insert(TermMode::ORIGIN),
|
2021-02-13 18:15:57 +00:00
|
|
|
ansi::Mode::ColumnMode => self.deccolm(),
|
2020-05-05 22:50:23 +00:00
|
|
|
ansi::Mode::Insert => self.mode.insert(TermMode::INSERT),
|
2019-01-07 00:06:57 +00:00
|
|
|
ansi::Mode::BlinkingCursor => {
|
2020-11-23 23:11:03 +00:00
|
|
|
let style = self.cursor_style.get_or_insert(self.default_cursor_style);
|
|
|
|
style.blinking = true;
|
2021-10-23 07:16:47 +00:00
|
|
|
self.event_proxy.send_event(Event::CursorBlinkingChange);
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2019-03-30 16:48:36 +00:00
|
|
|
fn unset_mode(&mut self, mode: ansi::Mode) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Unsetting mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2020-10-10 21:24:40 +00:00
|
|
|
ansi::Mode::UrgencyHints => self.mode.remove(TermMode::URGENCY_HINTS),
|
2017-02-01 17:55:23 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
2020-06-28 19:12:37 +00:00
|
|
|
if self.mode.contains(TermMode::ALT_SCREEN) {
|
2017-08-09 18:24:51 +00:00
|
|
|
self.swap_alt();
|
|
|
|
}
|
2017-02-01 17:55:23 +00:00
|
|
|
},
|
2019-04-19 20:56:11 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.remove(TermMode::SHOW_CURSOR),
|
|
|
|
ansi::Mode::CursorKeys => self.mode.remove(TermMode::APP_CURSOR),
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => {
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.remove(TermMode::MOUSE_REPORT_CLICK);
|
2019-10-05 00:29:26 +00:00
|
|
|
self.event_proxy.send_event(Event::MouseCursorDirty);
|
2017-12-24 20:15:42 +00:00
|
|
|
},
|
2018-01-26 21:28:43 +00:00
|
|
|
ansi::Mode::ReportCellMouseMotion => {
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.remove(TermMode::MOUSE_DRAG);
|
2019-10-05 00:29:26 +00:00
|
|
|
self.event_proxy.send_event(Event::MouseCursorDirty);
|
2018-01-26 21:28:43 +00:00
|
|
|
},
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportAllMouseMotion => {
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.remove(TermMode::MOUSE_MOTION);
|
2019-10-05 00:29:26 +00:00
|
|
|
self.event_proxy.send_event(Event::MouseCursorDirty);
|
2017-12-24 20:15:42 +00:00
|
|
|
},
|
2019-04-19 20:56:11 +00:00
|
|
|
ansi::Mode::ReportFocusInOut => self.mode.remove(TermMode::FOCUS_IN_OUT),
|
|
|
|
ansi::Mode::BracketedPaste => self.mode.remove(TermMode::BRACKETED_PASTE),
|
|
|
|
ansi::Mode::SgrMouse => self.mode.remove(TermMode::SGR_MOUSE),
|
2019-11-04 19:41:13 +00:00
|
|
|
ansi::Mode::Utf8Mouse => self.mode.remove(TermMode::UTF8_MOUSE),
|
2019-10-15 19:13:58 +00:00
|
|
|
ansi::Mode::AlternateScroll => self.mode.remove(TermMode::ALTERNATE_SCROLL),
|
2019-04-19 20:56:11 +00:00
|
|
|
ansi::Mode::LineWrap => self.mode.remove(TermMode::LINE_WRAP),
|
|
|
|
ansi::Mode::LineFeedNewLine => self.mode.remove(TermMode::LINE_FEED_NEW_LINE),
|
|
|
|
ansi::Mode::Origin => self.mode.remove(TermMode::ORIGIN),
|
2021-02-13 18:15:57 +00:00
|
|
|
ansi::Mode::ColumnMode => self.deccolm(),
|
2019-04-19 20:56:11 +00:00
|
|
|
ansi::Mode::Insert => self.mode.remove(TermMode::INSERT),
|
2019-01-07 00:06:57 +00:00
|
|
|
ansi::Mode::BlinkingCursor => {
|
2020-11-23 23:11:03 +00:00
|
|
|
let style = self.cursor_style.get_or_insert(self.default_cursor_style);
|
|
|
|
style.blinking = false;
|
2021-10-23 07:16:47 +00:00
|
|
|
self.event_proxy.send_event(Event::CursorBlinkingChange);
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-08 17:39:49 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2020-07-09 21:45:22 +00:00
|
|
|
fn set_scrolling_region(&mut self, top: usize, bottom: Option<usize>) {
|
|
|
|
// Fallback to the last line as default.
|
2021-03-30 23:25:38 +00:00
|
|
|
let bottom = bottom.unwrap_or_else(|| self.screen_lines());
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2019-09-26 12:44:59 +00:00
|
|
|
if top >= bottom {
|
|
|
|
debug!("Invalid scrolling region: ({};{})", top, bottom);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bottom should be included in the range, but range end is not
|
|
|
|
// usually included. One option would be to use an inclusive
|
|
|
|
// range, but instead we just let the open range end be 1
|
|
|
|
// higher.
|
2021-03-30 23:25:38 +00:00
|
|
|
let start = Line(top as i32 - 1);
|
|
|
|
let end = Line(bottom as i32);
|
2019-09-26 12:44:59 +00:00
|
|
|
|
|
|
|
trace!("Setting scrolling region: ({};{})", start, end);
|
|
|
|
|
2021-03-30 23:25:38 +00:00
|
|
|
let screen_lines = Line(self.screen_lines() as i32);
|
|
|
|
self.scroll_region.start = min(start, screen_lines);
|
|
|
|
self.scroll_region.end = min(end, screen_lines);
|
2016-08-22 15:37:50 +00:00
|
|
|
self.goto(Line(0), Column(0));
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 16:19:48 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_keypad_application_mode(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Setting keypad application mode");
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.insert(TermMode::APP_KEYPAD);
|
2016-07-04 16:19:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn unset_keypad_application_mode(&mut self) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Unsetting keypad application mode");
|
2019-04-19 20:56:11 +00:00
|
|
|
self.mode.remove(TermMode::APP_KEYPAD);
|
2016-07-04 16:19:48 +00:00
|
|
|
}
|
2017-01-09 20:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Configuring charset {:?} as {:?}", index, charset);
|
2020-05-30 20:45:44 +00:00
|
|
|
self.grid.cursor.charsets[index] = charset;
|
2017-01-09 20:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_active_charset(&mut self, index: CharsetIndex) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Setting active charset {:?}", index);
|
2017-01-09 20:07:23 +00:00
|
|
|
self.active_charset = index;
|
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
|
|
|
#[inline]
|
2017-12-03 21:38:42 +00:00
|
|
|
fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
|
2019-01-07 00:06:57 +00:00
|
|
|
trace!("Setting cursor style {:?}", style);
|
2017-05-28 16:38:10 +00:00
|
|
|
self.cursor_style = style;
|
2020-11-23 23:11:03 +00:00
|
|
|
|
|
|
|
// Notify UI about blinking changes.
|
2021-10-23 07:16:47 +00:00
|
|
|
self.event_proxy.send_event(Event::CursorBlinkingChange);
|
2020-11-23 23:11:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_cursor_shape(&mut self, shape: CursorShape) {
|
|
|
|
trace!("Setting cursor shape {:?}", shape);
|
|
|
|
|
|
|
|
let style = self.cursor_style.get_or_insert(self.default_cursor_style);
|
|
|
|
style.shape = shape;
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
2019-10-14 17:50:58 +00:00
|
|
|
|
2020-03-14 15:09:10 +00:00
|
|
|
#[inline]
|
|
|
|
fn set_title(&mut self, title: Option<String>) {
|
|
|
|
trace!("Setting title to '{:?}'", title);
|
|
|
|
|
|
|
|
self.title = title.clone();
|
|
|
|
|
2020-07-11 17:03:09 +00:00
|
|
|
let title_event = match title {
|
|
|
|
Some(title) => Event::Title(title),
|
|
|
|
None => Event::ResetTitle,
|
|
|
|
};
|
|
|
|
|
|
|
|
self.event_proxy.send_event(title_event);
|
2020-03-14 15:09:10 +00:00
|
|
|
}
|
|
|
|
|
2019-10-14 17:50:58 +00:00
|
|
|
#[inline]
|
|
|
|
fn push_title(&mut self) {
|
2020-03-14 15:09:10 +00:00
|
|
|
trace!("Pushing '{:?}' onto title stack", self.title);
|
2019-10-14 17:50:58 +00:00
|
|
|
|
|
|
|
if self.title_stack.len() >= TITLE_STACK_MAX_DEPTH {
|
|
|
|
let removed = self.title_stack.remove(0);
|
|
|
|
trace!(
|
2020-03-14 15:09:10 +00:00
|
|
|
"Removing '{:?}' from bottom of title stack that exceeds its maximum depth",
|
2019-10-14 17:50:58 +00:00
|
|
|
removed
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.title_stack.push(self.title.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn pop_title(&mut self) {
|
|
|
|
trace!("Attempting to pop title from stack...");
|
|
|
|
|
|
|
|
if let Some(popped) = self.title_stack.pop() {
|
2020-03-14 15:09:10 +00:00
|
|
|
trace!("Title '{:?}' popped from stack", popped);
|
|
|
|
self.set_title(popped);
|
2019-10-14 17:50:58 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-28 22:26:03 +00:00
|
|
|
|
|
|
|
#[inline]
|
2021-04-17 23:20:13 +00:00
|
|
|
fn text_area_size_pixels(&mut self) {
|
2021-03-30 23:25:38 +00:00
|
|
|
let width = self.cell_width * self.columns();
|
|
|
|
let height = self.cell_height * self.screen_lines();
|
2021-04-17 23:20:13 +00:00
|
|
|
let text = format!("\x1b[4;{};{}t", height, width);
|
|
|
|
self.event_proxy.send_event(Event::PtyWrite(text));
|
2020-08-28 22:26:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2021-04-17 23:20:13 +00:00
|
|
|
fn text_area_size_chars(&mut self) {
|
|
|
|
let text = format!("\x1b[8;{};{}t", self.screen_lines(), self.columns());
|
|
|
|
self.event_proxy.send_event(Event::PtyWrite(text));
|
2020-08-28 22:26:03 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
|
2020-07-23 21:55:15 +00:00
|
|
|
/// Terminal version for escape sequence reports.
|
|
|
|
///
|
|
|
|
/// This returns the current terminal version as a unique number based on alacritty_terminal's
|
|
|
|
/// semver version. The different versions are padded to ensure that a higher semver version will
|
|
|
|
/// always report a higher version number.
|
|
|
|
fn version_number(mut version: &str) -> usize {
|
|
|
|
if let Some(separator) = version.rfind('-') {
|
|
|
|
version = &version[..separator];
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut version_number = 0;
|
|
|
|
|
|
|
|
let semver_versions = version.split('.');
|
|
|
|
for (i, semver_version) in semver_versions.rev().enumerate() {
|
|
|
|
let semver_number = semver_version.parse::<usize>().unwrap_or(0);
|
|
|
|
version_number += usize::pow(100, i as u32) * semver_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
version_number
|
|
|
|
}
|
|
|
|
|
2020-06-06 21:33:20 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub enum ClipboardType {
|
|
|
|
Clipboard,
|
|
|
|
Selection,
|
|
|
|
}
|
|
|
|
|
2018-12-15 21:33:33 +00:00
|
|
|
struct TabStops {
|
2019-03-30 16:48:36 +00:00
|
|
|
tabs: Vec<bool>,
|
2018-12-15 21:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TabStops {
|
2020-03-01 03:27:23 +00:00
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn new(columns: usize) -> TabStops {
|
|
|
|
TabStops { tabs: (0..columns).map(|i| i % INITIAL_TABSTOPS == 0).collect() }
|
2018-12-15 21:33:33 +00:00
|
|
|
}
|
|
|
|
|
2020-03-01 03:27:23 +00:00
|
|
|
/// Remove all tabstops.
|
|
|
|
#[inline]
|
2018-12-15 21:33:33 +00:00
|
|
|
fn clear_all(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
ptr::write_bytes(self.tabs.as_mut_ptr(), 0, self.tabs.len());
|
|
|
|
}
|
|
|
|
}
|
2020-03-01 03:27:23 +00:00
|
|
|
|
|
|
|
/// Increase tabstop capacity.
|
|
|
|
#[inline]
|
2021-03-30 23:25:38 +00:00
|
|
|
fn resize(&mut self, columns: usize) {
|
2020-03-01 03:27:23 +00:00
|
|
|
let mut index = self.tabs.len();
|
2021-03-30 23:25:38 +00:00
|
|
|
self.tabs.resize_with(columns, || {
|
2020-03-01 04:07:36 +00:00
|
|
|
let is_tabstop = index % INITIAL_TABSTOPS == 0;
|
2020-03-01 03:27:23 +00:00
|
|
|
index += 1;
|
|
|
|
is_tabstop
|
|
|
|
});
|
|
|
|
}
|
2018-12-15 21:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Index<Column> for TabStops {
|
|
|
|
type Output = bool;
|
|
|
|
|
|
|
|
fn index(&self, index: Column) -> &bool {
|
|
|
|
&self.tabs[index.0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IndexMut<Column> for TabStops {
|
|
|
|
fn index_mut(&mut self, index: Column) -> &mut bool {
|
|
|
|
self.tabs.index_mut(index.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
/// Terminal cursor rendering information.
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct RenderableCursor {
|
|
|
|
pub shape: CursorShape,
|
|
|
|
pub point: Point,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderableCursor {
|
|
|
|
fn new<T>(term: &Term<T>) -> Self {
|
|
|
|
// Cursor position.
|
|
|
|
let vi_mode = term.mode().contains(TermMode::VI);
|
2021-04-22 20:08:58 +00:00
|
|
|
let mut point = if vi_mode { term.vi_mode_cursor.point } else { term.grid.cursor.point };
|
|
|
|
if term.grid[point].flags.contains(Flags::WIDE_CHAR_SPACER) {
|
|
|
|
point.column -= 1;
|
|
|
|
}
|
2021-01-24 21:45:36 +00:00
|
|
|
|
|
|
|
// Cursor shape.
|
2021-03-30 23:25:38 +00:00
|
|
|
let shape = if !vi_mode && !term.mode().contains(TermMode::SHOW_CURSOR) {
|
2021-01-24 21:45:36 +00:00
|
|
|
CursorShape::Hidden
|
|
|
|
} else {
|
|
|
|
term.cursor_style().shape
|
|
|
|
};
|
|
|
|
|
|
|
|
Self { shape, point }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Visible terminal content.
|
|
|
|
///
|
|
|
|
/// This contains all content required to render the current terminal view.
|
|
|
|
pub struct RenderableContent<'a> {
|
2021-05-22 22:48:43 +00:00
|
|
|
pub display_iter: GridIterator<'a, Cell>,
|
2021-03-30 23:25:38 +00:00
|
|
|
pub selection: Option<SelectionRange>,
|
2021-01-24 21:45:36 +00:00
|
|
|
pub cursor: RenderableCursor,
|
|
|
|
pub display_offset: usize,
|
|
|
|
pub colors: &'a color::Colors,
|
|
|
|
pub mode: TermMode,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> RenderableContent<'a> {
|
|
|
|
fn new<T>(term: &'a Term<T>) -> Self {
|
|
|
|
Self {
|
|
|
|
display_iter: term.grid().display_iter(),
|
|
|
|
display_offset: term.grid().display_offset(),
|
|
|
|
cursor: RenderableCursor::new(term),
|
2021-03-30 23:25:38 +00:00
|
|
|
selection: term.selection.as_ref().and_then(|s| s.to_range(term)),
|
2021-01-24 21:45:36 +00:00
|
|
|
colors: &term.colors,
|
|
|
|
mode: *term.mode(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
/// Terminal test helpers.
|
|
|
|
pub mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
use unicode_width::UnicodeWidthChar;
|
|
|
|
|
|
|
|
use crate::config::Config;
|
|
|
|
use crate::index::Column;
|
|
|
|
|
|
|
|
/// Construct a terminal from its content as string.
|
|
|
|
///
|
|
|
|
/// A `\n` will break line and `\r\n` will break line without wrapping.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use alacritty_terminal::term::test::mock_term;
|
|
|
|
///
|
|
|
|
/// // Create a terminal with the following cells:
|
|
|
|
/// //
|
|
|
|
/// // [h][e][l][l][o] <- WRAPLINE flag set
|
|
|
|
/// // [:][)][ ][ ][ ]
|
|
|
|
/// // [t][e][s][t][ ]
|
|
|
|
/// mock_term(
|
|
|
|
/// "\
|
|
|
|
/// hello\n:)\r\ntest",
|
|
|
|
/// );
|
|
|
|
/// ```
|
|
|
|
pub fn mock_term(content: &str) -> Term<()> {
|
|
|
|
let lines: Vec<&str> = content.split('\n').collect();
|
|
|
|
let num_cols = lines
|
|
|
|
.iter()
|
|
|
|
.map(|line| line.chars().filter(|c| *c != '\r').map(|c| c.width().unwrap()).sum())
|
|
|
|
.max()
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
// Create terminal with the appropriate dimensions.
|
2020-09-27 22:36:08 +00:00
|
|
|
let size = SizeInfo::new(num_cols as f32, lines.len() as f32, 1., 1., 0., 0., false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2020-07-09 21:45:22 +00:00
|
|
|
|
|
|
|
// Fill terminal with content.
|
2021-03-30 23:25:38 +00:00
|
|
|
for (line, text) in lines.iter().enumerate() {
|
|
|
|
let line = Line(line as i32);
|
|
|
|
if !text.ends_with('\r') && line + 1 != lines.len() {
|
2020-07-09 21:45:22 +00:00
|
|
|
term.grid[line][Column(num_cols - 1)].flags.insert(Flags::WRAPLINE);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut index = 0;
|
|
|
|
for c in text.chars().take_while(|c| *c != '\r') {
|
|
|
|
term.grid[line][Column(index)].c = c;
|
|
|
|
|
|
|
|
// Handle fullwidth characters.
|
|
|
|
let width = c.width().unwrap();
|
|
|
|
if width == 2 {
|
|
|
|
term.grid[line][Column(index)].flags.insert(Flags::WIDE_CHAR);
|
|
|
|
term.grid[line][Column(index + 1)].flags.insert(Flags::WIDE_CHAR_SPACER);
|
|
|
|
}
|
|
|
|
|
|
|
|
index += width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
term
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-20 00:16:20 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-03-24 01:29:07 +00:00
|
|
|
use super::*;
|
|
|
|
|
2019-04-28 20:21:39 +00:00
|
|
|
use std::mem;
|
2016-11-25 20:03:11 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
|
2021-11-22 18:34:09 +00:00
|
|
|
use crate::config::Config;
|
2018-12-10 17:53:56 +00:00
|
|
|
use crate::grid::{Grid, Scroll};
|
2021-03-30 23:25:38 +00:00
|
|
|
use crate::index::{Column, Point, Side};
|
2020-03-18 02:35:08 +00:00
|
|
|
use crate::selection::{Selection, SelectionType};
|
2019-11-03 20:59:28 +00:00
|
|
|
use crate::term::cell::{Cell, Flags};
|
2017-01-15 01:53:48 +00:00
|
|
|
|
2021-07-04 20:01:29 +00:00
|
|
|
#[test]
|
|
|
|
fn scroll_display_page_up() {
|
|
|
|
let size = SizeInfo::new(5., 10., 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2021-07-04 20:01:29 +00:00
|
|
|
|
|
|
|
// Create 11 lines of scrollback.
|
|
|
|
for _ in 0..20 {
|
|
|
|
term.newline();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scrollable amount to top is 11.
|
|
|
|
term.scroll_display(Scroll::PageUp);
|
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-1), Column(0)));
|
|
|
|
assert_eq!(term.grid.display_offset(), 10);
|
|
|
|
|
|
|
|
// Scrollable amount to top is 1.
|
|
|
|
term.scroll_display(Scroll::PageUp);
|
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-2), Column(0)));
|
|
|
|
assert_eq!(term.grid.display_offset(), 11);
|
|
|
|
|
|
|
|
// Scrollable amount to top is 0.
|
|
|
|
term.scroll_display(Scroll::PageUp);
|
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-2), Column(0)));
|
|
|
|
assert_eq!(term.grid.display_offset(), 11);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn scroll_display_page_down() {
|
|
|
|
let size = SizeInfo::new(5., 10., 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2021-07-04 20:01:29 +00:00
|
|
|
|
|
|
|
// Create 11 lines of scrollback.
|
|
|
|
for _ in 0..20 {
|
|
|
|
term.newline();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change display_offset to topmost.
|
|
|
|
term.grid_mut().scroll_display(Scroll::Top);
|
|
|
|
term.vi_mode_cursor = ViModeCursor::new(Point::new(Line(-11), Column(0)));
|
|
|
|
|
|
|
|
// Scrollable amount to bottom is 11.
|
|
|
|
term.scroll_display(Scroll::PageDown);
|
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-1), Column(0)));
|
|
|
|
assert_eq!(term.grid.display_offset(), 1);
|
|
|
|
|
|
|
|
// Scrollable amount to bottom is 1.
|
|
|
|
term.scroll_display(Scroll::PageDown);
|
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(0), Column(0)));
|
|
|
|
assert_eq!(term.grid.display_offset(), 0);
|
|
|
|
|
|
|
|
// Scrollable amount to bottom is 0.
|
|
|
|
term.scroll_display(Scroll::PageDown);
|
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(0), Column(0)));
|
|
|
|
assert_eq!(term.grid.display_offset(), 0);
|
|
|
|
}
|
|
|
|
|
2022-01-07 09:03:15 +00:00
|
|
|
#[test]
|
|
|
|
fn simple_selection_works() {
|
|
|
|
let size = SizeInfo::new(5., 5., 1.0, 1.0, 0.0, 0.0, false);
|
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
|
|
|
let grid = term.grid_mut();
|
|
|
|
for i in 0..4 {
|
|
|
|
if i == 1 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
grid[Line(i)][Column(0)].c = '"';
|
|
|
|
|
|
|
|
for j in 1..4 {
|
|
|
|
grid[Line(i)][Column(j)].c = 'a';
|
|
|
|
}
|
|
|
|
|
|
|
|
grid[Line(i)][Column(4)].c = '"';
|
|
|
|
}
|
|
|
|
grid[Line(2)][Column(0)].c = ' ';
|
|
|
|
grid[Line(2)][Column(4)].c = ' ';
|
|
|
|
grid[Line(2)][Column(4)].flags.insert(Flags::WRAPLINE);
|
|
|
|
grid[Line(3)][Column(0)].c = ' ';
|
|
|
|
|
|
|
|
// Multiple lines contain an empty line.
|
|
|
|
term.selection = Some(Selection::new(
|
|
|
|
SelectionType::Simple,
|
|
|
|
Point { line: Line(0), column: Column(0) },
|
|
|
|
Side::Left,
|
|
|
|
));
|
|
|
|
if let Some(s) = term.selection.as_mut() {
|
|
|
|
s.update(Point { line: Line(2), column: Column(4) }, Side::Right);
|
|
|
|
}
|
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("\"aaa\"\n\n aaa ")));
|
|
|
|
|
|
|
|
// A wrapline.
|
|
|
|
term.selection = Some(Selection::new(
|
|
|
|
SelectionType::Simple,
|
|
|
|
Point { line: Line(2), column: Column(0) },
|
|
|
|
Side::Left,
|
|
|
|
));
|
|
|
|
if let Some(s) = term.selection.as_mut() {
|
|
|
|
s.update(Point { line: Line(3), column: Column(4) }, Side::Right);
|
|
|
|
}
|
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from(" aaa aaa\"")));
|
|
|
|
}
|
|
|
|
|
2017-01-15 01:53:48 +00:00
|
|
|
#[test]
|
|
|
|
fn semantic_selection_works() {
|
2021-03-30 23:25:38 +00:00
|
|
|
let size = SizeInfo::new(5., 3., 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2021-03-30 23:25:38 +00:00
|
|
|
let mut grid: Grid<Cell> = Grid::new(3, 5, 0);
|
2017-01-15 01:53:48 +00:00
|
|
|
for i in 0..5 {
|
|
|
|
for j in 0..2 {
|
|
|
|
grid[Line(j)][Column(i)].c = 'a';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
grid[Line(0)][Column(0)].c = '"';
|
|
|
|
grid[Line(0)][Column(3)].c = '"';
|
|
|
|
grid[Line(1)][Column(2)].c = '"';
|
2019-11-03 20:59:28 +00:00
|
|
|
grid[Line(0)][Column(4)].flags.insert(Flags::WRAPLINE);
|
2017-01-15 01:53:48 +00:00
|
|
|
|
|
|
|
let mut escape_chars = String::from("\"");
|
|
|
|
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
|
|
|
|
|
|
|
|
{
|
2020-05-30 20:45:44 +00:00
|
|
|
term.selection = Some(Selection::new(
|
2020-03-18 02:35:08 +00:00
|
|
|
SelectionType::Semantic,
|
2021-03-30 23:25:38 +00:00
|
|
|
Point { line: Line(0), column: Column(1) },
|
2020-03-18 02:35:08 +00:00
|
|
|
Side::Left,
|
|
|
|
));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2020-05-30 20:45:44 +00:00
|
|
|
term.selection = Some(Selection::new(
|
2020-03-18 02:35:08 +00:00
|
|
|
SelectionType::Semantic,
|
2021-03-30 23:25:38 +00:00
|
|
|
Point { line: Line(0), column: Column(4) },
|
2020-03-18 02:35:08 +00:00
|
|
|
Side::Left,
|
|
|
|
));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2020-05-30 20:45:44 +00:00
|
|
|
term.selection = Some(Selection::new(
|
2020-03-18 02:35:08 +00:00
|
|
|
SelectionType::Semantic,
|
2021-03-30 23:25:38 +00:00
|
|
|
Point { line: Line(1), column: Column(1) },
|
2020-03-18 02:35:08 +00:00
|
|
|
Side::Left,
|
|
|
|
));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn line_selection_works() {
|
2021-03-30 23:25:38 +00:00
|
|
|
let size = SizeInfo::new(5., 1., 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2021-03-30 23:25:38 +00:00
|
|
|
let mut grid: Grid<Cell> = Grid::new(1, 5, 0);
|
2017-01-15 01:53:48 +00:00
|
|
|
for i in 0..5 {
|
|
|
|
grid[Line(0)][Column(i)].c = 'a';
|
|
|
|
}
|
|
|
|
grid[Line(0)][Column(0)].c = '"';
|
|
|
|
grid[Line(0)][Column(3)].c = '"';
|
|
|
|
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
term.selection = Some(Selection::new(
|
2020-03-18 02:35:08 +00:00
|
|
|
SelectionType::Lines,
|
2021-03-30 23:25:38 +00:00
|
|
|
Point { line: Line(0), column: Column(3) },
|
2020-03-18 02:35:08 +00:00
|
|
|
Side::Left,
|
|
|
|
));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
|
2018-09-26 18:42:41 +00:00
|
|
|
#[test]
|
2022-01-07 09:03:15 +00:00
|
|
|
fn block_selection_works() {
|
|
|
|
let size = SizeInfo::new(5., 5., 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2022-01-07 09:03:15 +00:00
|
|
|
let grid = term.grid_mut();
|
|
|
|
for i in 1..4 {
|
|
|
|
grid[Line(i)][Column(0)].c = '"';
|
|
|
|
|
|
|
|
for j in 1..4 {
|
|
|
|
grid[Line(i)][Column(j)].c = 'a';
|
2018-09-26 18:42:41 +00:00
|
|
|
}
|
|
|
|
|
2022-01-07 09:03:15 +00:00
|
|
|
grid[Line(i)][Column(4)].c = '"';
|
|
|
|
}
|
|
|
|
grid[Line(2)][Column(2)].c = ' ';
|
|
|
|
grid[Line(2)][Column(4)].flags.insert(Flags::WRAPLINE);
|
|
|
|
grid[Line(3)][Column(4)].c = ' ';
|
2018-09-26 18:42:41 +00:00
|
|
|
|
2022-01-07 09:03:15 +00:00
|
|
|
term.selection = Some(Selection::new(
|
|
|
|
SelectionType::Block,
|
|
|
|
Point { line: Line(0), column: Column(3) },
|
2021-03-30 23:25:38 +00:00
|
|
|
Side::Left,
|
2022-01-07 09:03:15 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
// The same column.
|
|
|
|
if let Some(s) = term.selection.as_mut() {
|
|
|
|
s.update(Point { line: Line(3), column: Column(3) }, Side::Right);
|
|
|
|
}
|
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("\na\na\na")));
|
|
|
|
|
|
|
|
// The first column.
|
|
|
|
if let Some(s) = term.selection.as_mut() {
|
|
|
|
s.update(Point { line: Line(3), column: Column(0) }, Side::Left);
|
|
|
|
}
|
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("\n\"aa\n\"a\n\"aa")));
|
|
|
|
|
|
|
|
// The last column.
|
|
|
|
if let Some(s) = term.selection.as_mut() {
|
|
|
|
s.update(Point { line: Line(3), column: Column(4) }, Side::Right);
|
|
|
|
}
|
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("\na\"\na\"\na")));
|
2018-09-26 18:42:41 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Check that the grid can be serialized back and forth losslessly.
|
2016-11-20 00:16:20 +00:00
|
|
|
///
|
|
|
|
/// This test is in the term module as opposed to the grid since we want to
|
|
|
|
/// test this property with a T=Cell.
|
|
|
|
#[test]
|
|
|
|
fn grid_serde() {
|
2021-03-30 23:25:38 +00:00
|
|
|
let grid: Grid<Cell> = Grid::new(24, 80, 0);
|
2016-11-20 00:16:20 +00:00
|
|
|
let serialized = serde_json::to_string(&grid).expect("ser");
|
2019-03-30 16:48:36 +00:00
|
|
|
let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized).expect("de");
|
2016-11-20 00:16:20 +00:00
|
|
|
|
|
|
|
assert_eq!(deserialized, grid);
|
|
|
|
}
|
2016-11-25 20:03:11 +00:00
|
|
|
|
2017-01-09 20:07:23 +00:00
|
|
|
#[test]
|
|
|
|
fn input_line_drawing_character() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2017-01-09 20:07:23 +00:00
|
|
|
let cursor = Point::new(Line(0), Column(0));
|
2019-03-30 16:48:36 +00:00
|
|
|
term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
|
2017-01-09 20:07:23 +00:00
|
|
|
term.input('a');
|
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
assert_eq!(term.grid()[cursor].c, '▒');
|
2017-01-09 20:07:23 +00:00
|
|
|
}
|
2018-07-21 17:17:41 +00:00
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
#[test]
|
2021-12-18 15:36:56 +00:00
|
|
|
fn clearing_viewport_keeps_history_position() {
|
2021-12-03 07:45:05 +00:00
|
|
|
let size = SizeInfo::new(10.0, 20.0, 1.0, 1.0, 0.0, 0.0, false);
|
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
|
|
|
|
|
|
|
// Create 10 lines of scrollback.
|
|
|
|
for _ in 0..29 {
|
|
|
|
term.newline();
|
|
|
|
}
|
|
|
|
|
2021-12-18 15:36:56 +00:00
|
|
|
// Change the display area.
|
|
|
|
term.scroll_display(Scroll::Top);
|
|
|
|
|
|
|
|
assert_eq!(term.grid.display_offset(), 10);
|
|
|
|
|
|
|
|
// Clear the viewport.
|
|
|
|
term.clear_screen(ansi::ClearMode::All);
|
|
|
|
|
|
|
|
assert_eq!(term.grid.display_offset(), 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn clearing_viewport_with_vi_mode_keeps_history_position() {
|
|
|
|
let size = SizeInfo::new(10.0, 20.0, 1.0, 1.0, 0.0, 0.0, false);
|
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
|
|
|
|
|
|
|
// Create 10 lines of scrollback.
|
|
|
|
for _ in 0..29 {
|
|
|
|
term.newline();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable vi mode.
|
|
|
|
term.toggle_vi_mode();
|
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
// Change the display area and the vi cursor position.
|
|
|
|
term.scroll_display(Scroll::Top);
|
|
|
|
term.vi_mode_cursor.point = Point::new(Line(-5), Column(3));
|
|
|
|
|
2021-12-18 15:36:56 +00:00
|
|
|
assert_eq!(term.grid.display_offset(), 10);
|
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
// Clear the viewport.
|
|
|
|
term.clear_screen(ansi::ClearMode::All);
|
|
|
|
|
2021-12-18 15:36:56 +00:00
|
|
|
assert_eq!(term.grid.display_offset(), 10);
|
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-5), Column(3)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn clearing_scrollback_resets_display_offset() {
|
|
|
|
let size = SizeInfo::new(10.0, 20.0, 1.0, 1.0, 0.0, 0.0, false);
|
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
|
|
|
|
|
|
|
// Create 10 lines of scrollback.
|
|
|
|
for _ in 0..29 {
|
|
|
|
term.newline();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change the display area.
|
|
|
|
term.scroll_display(Scroll::Top);
|
|
|
|
|
|
|
|
assert_eq!(term.grid.display_offset(), 10);
|
|
|
|
|
|
|
|
// Clear the scrollback buffer.
|
|
|
|
term.clear_screen(ansi::ClearMode::Saved);
|
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
assert_eq!(term.grid.display_offset(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-12-18 15:36:56 +00:00
|
|
|
fn clearing_scrollback_sets_vi_cursor_into_viewport() {
|
2021-12-03 07:45:05 +00:00
|
|
|
let size = SizeInfo::new(10.0, 20.0, 1.0, 1.0, 0.0, 0.0, false);
|
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
|
|
|
|
|
|
|
// Create 10 lines of scrollback.
|
|
|
|
for _ in 0..29 {
|
|
|
|
term.newline();
|
|
|
|
}
|
|
|
|
|
2021-12-18 15:36:56 +00:00
|
|
|
// Enable vi mode.
|
|
|
|
term.toggle_vi_mode();
|
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
// Change the display area and the vi cursor position.
|
|
|
|
term.scroll_display(Scroll::Top);
|
|
|
|
term.vi_mode_cursor.point = Point::new(Line(-5), Column(3));
|
|
|
|
|
2021-12-18 15:36:56 +00:00
|
|
|
assert_eq!(term.grid.display_offset(), 10);
|
|
|
|
|
2021-12-03 07:45:05 +00:00
|
|
|
// Clear the scrollback buffer.
|
|
|
|
term.clear_screen(ansi::ClearMode::Saved);
|
|
|
|
|
|
|
|
assert_eq!(term.grid.display_offset(), 0);
|
2021-12-18 15:36:56 +00:00
|
|
|
assert_eq!(term.vi_mode_cursor.point, Point::new(Line(0), Column(3)));
|
2021-12-03 07:45:05 +00:00
|
|
|
}
|
|
|
|
|
2018-09-02 00:30:03 +00:00
|
|
|
#[test]
|
|
|
|
fn clear_saved_lines() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2018-09-02 00:30:03 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Add one line of scrollback.
|
2021-03-30 23:25:38 +00:00
|
|
|
term.grid.scroll_up(&(Line(0)..Line(1)), 1);
|
2018-09-02 00:30:03 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Clear the history.
|
2018-09-02 00:30:03 +00:00
|
|
|
term.clear_screen(ansi::ClearMode::Saved);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Make sure that scrolling does not change the grid.
|
2018-09-02 00:30:03 +00:00
|
|
|
let mut scrolled_grid = term.grid.clone();
|
|
|
|
scrolled_grid.scroll_display(Scroll::Top);
|
2020-01-26 13:49:58 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Truncate grids for comparison.
|
2020-01-26 13:49:58 +00:00
|
|
|
scrolled_grid.truncate();
|
|
|
|
term.grid.truncate();
|
|
|
|
|
2018-09-02 00:30:03 +00:00
|
|
|
assert_eq!(term.grid, scrolled_grid);
|
|
|
|
}
|
2019-10-14 17:50:58 +00:00
|
|
|
|
2021-12-03 19:33:21 +00:00
|
|
|
#[test]
|
|
|
|
fn vi_cursor_keep_pos_on_scrollback_buffer() {
|
|
|
|
let size = SizeInfo::new(5., 10., 1.0, 1.0, 0.0, 0.0, false);
|
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
|
|
|
|
|
|
|
// Create 11 lines of scrollback.
|
|
|
|
for _ in 0..20 {
|
|
|
|
term.newline();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable vi mode.
|
|
|
|
term.toggle_vi_mode();
|
|
|
|
|
|
|
|
term.scroll_display(Scroll::Top);
|
|
|
|
term.vi_mode_cursor.point.line = Line(-11);
|
|
|
|
|
|
|
|
term.linefeed();
|
|
|
|
assert_eq!(term.vi_mode_cursor.point.line, Line(-12));
|
|
|
|
}
|
|
|
|
|
2020-03-24 01:29:07 +00:00
|
|
|
#[test]
|
|
|
|
fn grow_lines_updates_active_cursor_pos() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Create 10 lines of scrollback.
|
2020-03-24 01:29:07 +00:00
|
|
|
for _ in 0..19 {
|
|
|
|
term.newline();
|
|
|
|
}
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 10);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Increase visible lines.
|
2021-03-30 23:25:38 +00:00
|
|
|
size.screen_lines = 30;
|
2020-08-28 22:26:03 +00:00
|
|
|
term.resize(size);
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 0);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(19), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn grow_lines_updates_inactive_cursor_pos() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Create 10 lines of scrollback.
|
2020-03-24 01:29:07 +00:00
|
|
|
for _ in 0..19 {
|
|
|
|
term.newline();
|
|
|
|
}
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 10);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Enter alt screen.
|
2020-03-24 01:29:07 +00:00
|
|
|
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Increase visible lines.
|
2021-03-30 23:25:38 +00:00
|
|
|
size.screen_lines = 30;
|
2020-08-28 22:26:03 +00:00
|
|
|
term.resize(size);
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Leave alt screen.
|
2020-03-24 01:29:07 +00:00
|
|
|
term.unset_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
|
|
|
|
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 0);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(19), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn shrink_lines_updates_active_cursor_pos() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Create 10 lines of scrollback.
|
2020-03-24 01:29:07 +00:00
|
|
|
for _ in 0..19 {
|
|
|
|
term.newline();
|
|
|
|
}
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 10);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Increase visible lines.
|
2021-03-30 23:25:38 +00:00
|
|
|
size.screen_lines = 5;
|
2020-08-28 22:26:03 +00:00
|
|
|
term.resize(size);
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 15);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(4), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn shrink_lines_updates_inactive_cursor_pos() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Create 10 lines of scrollback.
|
2020-03-24 01:29:07 +00:00
|
|
|
for _ in 0..19 {
|
|
|
|
term.newline();
|
|
|
|
}
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 10);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Enter alt screen.
|
2020-03-24 01:29:07 +00:00
|
|
|
term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Increase visible lines.
|
2021-03-30 23:25:38 +00:00
|
|
|
size.screen_lines = 5;
|
2020-08-28 22:26:03 +00:00
|
|
|
term.resize(size);
|
2020-03-24 01:29:07 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Leave alt screen.
|
2020-03-24 01:29:07 +00:00
|
|
|
term.unset_mode(ansi::Mode::SwapScreenAndSetRestoreCursor);
|
|
|
|
|
2020-08-25 18:24:16 +00:00
|
|
|
assert_eq!(term.history_size(), 15);
|
2020-05-30 20:45:44 +00:00
|
|
|
assert_eq!(term.grid.cursor.point, Point::new(Line(4), Column(0)));
|
2020-03-24 01:29:07 +00:00
|
|
|
}
|
|
|
|
|
2019-10-14 17:50:58 +00:00
|
|
|
#[test]
|
|
|
|
fn window_title() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
|
2021-11-22 18:34:09 +00:00
|
|
|
let mut term = Term::new(&Config::default(), size, ());
|
2019-10-14 17:50:58 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title None by default.
|
2020-03-14 15:09:10 +00:00
|
|
|
assert_eq!(term.title, None);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title can be set.
|
2020-03-14 15:09:10 +00:00
|
|
|
term.set_title(Some("Test".into()));
|
|
|
|
assert_eq!(term.title, Some("Test".into()));
|
2019-10-14 17:50:58 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title can be pushed onto stack.
|
2020-03-14 15:09:10 +00:00
|
|
|
term.push_title();
|
|
|
|
term.set_title(Some("Next".into()));
|
|
|
|
assert_eq!(term.title, Some("Next".into()));
|
|
|
|
assert_eq!(term.title_stack.get(0).unwrap(), &Some("Test".into()));
|
2019-10-14 17:50:58 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title can be popped from stack and set as the window title.
|
2020-03-14 15:09:10 +00:00
|
|
|
term.pop_title();
|
|
|
|
assert_eq!(term.title, Some("Test".into()));
|
|
|
|
assert!(term.title_stack.is_empty());
|
2019-10-14 17:50:58 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title stack doesn't grow infinitely.
|
2020-03-14 15:09:10 +00:00
|
|
|
for _ in 0..4097 {
|
|
|
|
term.push_title();
|
2019-10-14 17:50:58 +00:00
|
|
|
}
|
2020-03-14 15:09:10 +00:00
|
|
|
assert_eq!(term.title_stack.len(), 4096);
|
2019-10-14 17:50:58 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title and title stack reset when terminal state is reset.
|
2020-03-14 15:09:10 +00:00
|
|
|
term.push_title();
|
|
|
|
term.reset_state();
|
|
|
|
assert_eq!(term.title, None);
|
|
|
|
assert!(term.title_stack.is_empty());
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title stack pops back to default.
|
2020-03-14 15:09:10 +00:00
|
|
|
term.title = None;
|
|
|
|
term.push_title();
|
|
|
|
term.set_title(Some("Test".into()));
|
|
|
|
term.pop_title();
|
|
|
|
assert_eq!(term.title, None);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Title can be reset to default.
|
2020-03-14 15:09:10 +00:00
|
|
|
term.title = Some("Test".into());
|
|
|
|
term.set_title(None);
|
|
|
|
assert_eq!(term.title, None);
|
2019-10-14 17:50:58 +00:00
|
|
|
}
|
2020-07-23 21:55:15 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parse_cargo_version() {
|
|
|
|
assert!(version_number(env!("CARGO_PKG_VERSION")) >= 10_01);
|
|
|
|
assert_eq!(version_number("0.0.1-dev"), 1);
|
|
|
|
assert_eq!(version_number("0.1.2-dev"), 1_02);
|
|
|
|
assert_eq!(version_number("1.2.3-dev"), 1_02_03);
|
|
|
|
assert_eq!(version_number("999.99.99"), 9_99_99_99);
|
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
}
|