summaryrefslogtreecommitdiffstats
path: root/zellij-server/src/panes
diff options
context:
space:
mode:
authorKunal Mohan <kunalmohan99@gmail.com>2021-05-16 16:42:50 +0530
committerKunal Mohan <kunalmohan99@gmail.com>2021-05-16 21:41:56 +0530
commit2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch)
tree5a332470e32b380816098594a89d3c3db1f6eb53 /zellij-server/src/panes
parent28212f54309d09263c6daa1801d49e8b4311be12 (diff)
Big refactor: separate crates for client, server and utilities
Diffstat (limited to 'zellij-server/src/panes')
-rw-r--r--zellij-server/src/panes/grid.rs1556
-rw-r--r--zellij-server/src/panes/mod.rs9
-rw-r--r--zellij-server/src/panes/plugin_pane.rs227
-rw-r--r--zellij-server/src/panes/terminal_character.rs775
-rw-r--r--zellij-server/src/panes/terminal_pane.rs340
-rw-r--r--zellij-server/src/panes/unit/grid_tests.rs397
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__csi_b.snap8
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__csi_capital_i.snap8
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__csi_capital_z.snap8
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__terminal_reports.snap6
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_0.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_1.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_2.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_3.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_4.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest1_5.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_0.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_1.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_10.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_11.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_12.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_13.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_14.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_2.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_3.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_4.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_5.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_6.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_7.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_8.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest2_9.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest3_0.snap47
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_0.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_1.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_2.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_3.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_4.snap57
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__vttest8_5.snap57
38 files changed, 4710 insertions, 0 deletions
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
new file mode 100644
index 000000000..b2ec365a9
--- /dev/null
+++ b/zellij-server/src/panes/grid.rs
@@ -0,0 +1,1556 @@
+use std::{
+ cmp::Ordering,
+ collections::{BTreeSet, VecDeque},
+ fmt::{self, Debug, Formatter},
+};
+
+use vte::{Params, Perform};
+
+const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
+const SCROLL_BACK: usize = 10_000;
+
+use zellij_utils::{consts::VERSION, logging::debug_log_to_file, shared::version_number};
+
+use crate::panes::terminal_character::{
+ CharacterStyles, CharsetIndex, Cursor, CursorShape, StandardCharset, TerminalCharacter,
+ EMPTY_TERMINAL_CHARACTER,
+};
+
+fn get_top_non_canonical_rows(rows: &mut Vec<Row>) -> Vec<Row> {
+ let mut index_of_last_non_canonical_row = None;
+ for (i, row) in rows.iter().enumerate() {
+ if row.is_canonical {
+ break;
+ } else {
+ index_of_last_non_canonical_row = Some(i);
+ }
+ }
+ match index_of_last_non_canonical_row {
+ Some(index_of_last_non_canonical_row) => {
+ rows.drain(..=index_of_last_non_canonical_row).collect()
+ }
+ None => vec![],
+ }
+}
+
+fn get_bottom_canonical_row_and_wraps(rows: &mut VecDeque<Row>) -> Vec<Row> {
+ let mut index_of_last_non_canonical_row = None;
+ for (i, row) in rows.iter().enumerate().rev() {
+ index_of_last_non_canonical_row = Some(i);
+ if row.is_canonical {
+ break;
+ }
+ }
+ match index_of_last_non_canonical_row {
+ Some(index_of_last_non_canonical_row) => {
+ rows.drain(index_of_last_non_canonical_row..).collect()
+ }
+ None => vec![],
+ }
+}
+
+fn transfer_rows_down(
+ source: &mut VecDeque<Row>,
+ destination: &mut Vec<Row>,
+ count: usize,
+ max_src_width: Option<usize>,
+ max_dst_width: Option<usize>,
+) {
+ let mut next_lines: Vec<Row> = vec![];
+ let mut lines_added_to_destination: isize = 0;
+ loop {
+ if lines_added_to_destination as usize == count {
+ break;
+ }
+ if next_lines.is_empty() {
+ match source.pop_back() {
+ Some(next_line) => {
+ let mut top_non_canonical_rows_in_dst = get_top_non_canonical_rows(destination);
+ lines_added_to_destination -= top_non_canonical_rows_in_dst.len() as isize;
+ next_lines.push(next_line);
+ next_lines.append(&mut top_non_canonical_rows_in_dst);
+ next_lines = match max_dst_width {
+ Some(max_row_width) => {
+ Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
+ }
+ None => vec![Row::from_rows(next_lines)],
+ };
+ if next_lines.is_empty() {
+ // no more lines at source, the line we popped was probably empty
+ break;
+ }
+ }
+ None => break, // no more rows
+ }
+ }
+ destination.insert(0, next_lines.pop().unwrap());
+ lines_added_to_destination += 1;
+ }
+ if !next_lines.is_empty() {
+ match max_src_width {
+ Some(max_row_width) => {
+ let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
+ source.extend(excess_rows);
+ }
+ None => {
+ let excess_row = Row::from_rows(next_lines);
+ bounded_push(source, excess_row);
+ }
+ }
+ }
+}
+
+fn transfer_rows_up(
+ source: &mut Vec<Row>,
+ destination: &mut VecDeque<Row>,
+ count: usize,
+ max_src_width: Option<usize>,
+ max_dst_width: Option<usize>,
+) {
+ let mut next_lines: Vec<Row> = vec![];
+ for _ in 0..count {
+ if next_lines.is_empty() {
+ if !source.is_empty() {
+ let next_line = source.remove(0);
+ if !next_line.is_canonical {
+ let mut bottom_canonical_row_and_wraps_in_dst =
+ get_bottom_canonical_row_and_wraps(destination);
+ next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
+ }
+ next_lines.push(next_line);
+ next_lines = match max_dst_width {
+ Some(max_row_width) => {
+ Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
+ }
+ None => vec![Row::from_rows(next_lines)],
+ };
+ } else {
+ break; // no more rows
+ }
+ }
+ bounded_push(destination, next_lines.remove(0));
+ }
+ if !next_lines.is_empty() {
+ match max_src_width {
+ Some(max_row_width) => {
+ let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
+ for row in excess_rows {
+ source.insert(0, row);
+ }
+ }
+ None => {
+ let excess_row = Row::from_rows(next_lines);
+ source.insert(0, excess_row);
+ }
+ }
+ }
+}
+
+fn bounded_push(vec: &mut VecDeque<Row>, value: Row) {
+ if vec.len() >= SCROLL_BACK {
+ vec.pop_front();
+ }
+ vec.push_back(value)
+}
+
+pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet<usize> {
+ let mut i = TABSTOP_WIDTH;
+ let mut horizontal_tabstops = BTreeSet::new();
+ loop {
+ if i > columns {
+ break;
+ }
+ horizontal_tabstops.insert(i);
+ i += TABSTOP_WIDTH;
+ }
+ horizontal_tabstops
+}
+
+#[derive(Clone)]
+pub struct Grid {
+ lines_above: VecDeque<Row>,
+ viewport: Vec<Row>,
+ lines_below: Vec<Row>,
+ horizontal_tabstops: BTreeSet<usize>,
+ alternative_lines_above_viewport_and_cursor: Option<(VecDeque<Row>, Vec<Row>, Cursor)>,
+ cursor: Cursor,
+ saved_cursor_position: Option<Cursor>,
+ scroll_region: Option<(usize, usize)>,
+ active_charset: CharsetIndex,
+ preceding_char: Option<TerminalCharacter>,
+ pub should_render: bool,
+ pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
+ pub erasure_mode: bool, // ERM
+ pub insert_mode: bool,
+ pub disable_linewrap: bool,
+ pub clear_viewport_before_rendering: bool,
+ pub width: usize,
+ pub height: usize,
+ pub pending_messages_to_pty: Vec<Vec<u8>>,
+}
+
+impl Debug for Grid {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ for (i, row) in self.viewport.iter().enumerate() {
+ if row.is_canonical {
+ writeln!(f, "{:02?} (C): {:?}", i, row)?;
+ } else {
+ writeln!(f, "{:02?} (W): {:?}", i, row)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl Grid {
+ pub fn new(rows: usize, columns: usize) -> Self {
+ Grid {
+ lines_above: VecDeque::with_capacity(SCROLL_BACK),
+ viewport: vec![Row::new().canonical()],
+ lines_below: vec![],
+ horizontal_tabstops: create_horizontal_tabstops(columns),
+ cursor: Cursor::new(0, 0),
+ saved_cursor_position: None,
+ scroll_region: None,
+ preceding_char: None,
+ width: columns,
+ height: rows,
+ should_render: true,
+ cursor_key_mode: false,
+ erasure_mode: false,
+ insert_mode: false,
+ disable_linewrap: false,
+ alternative_lines_above_viewport_and_cursor: None,
+ clear_viewport_before_rendering: false,
+ active_charset: Default::default(),
+ pending_messages_to_pty: vec![],
+ }
+ }
+ pub fn contains_widechar(&self) -> bool {
+ self.viewport.iter().any(|c| c.contains_widechar())
+ }
+ pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
+ let mut next_tabstop = None;
+ for tabstop in self.horizontal_tabstops.iter() {
+ if *tabstop > self.cursor.x {
+ next_tabstop = Some(tabstop);
+ break;
+ }
+ }
+ match next_tabstop {
+ Some(tabstop) => {
+ self.cursor.x = *tabstop;
+ }
+ None => {
+ self.cursor.x = self.width.saturating_sub(1);
+ }
+ }
+ let mut empty_character = EMPTY_TERMINAL_CHARACTER;
+ empty_character.styles = styles;
+ self.pad_current_line_until(self.cursor.x);
+ }
+ pub fn move_to_previous_tabstop(&mut self) {
+ let mut previous_tabstop = None;
+ for tabstop in self.horizontal_tabstops.iter() {
+ if *tabstop >= self.cursor.x {
+ break;
+ }
+ previous_tabstop = Some(tabstop);
+ }
+ match previous_tabstop {
+ Some(tabstop) => {
+ self.cursor.x = *tabstop;
+ }
+ None => {
+ self.cursor.x = 0;
+ }
+ }
+ }
+ pub fn cursor_shape(&self) -> CursorShape {
+ self.cursor.get_shape()
+ }
+ fn set_horizontal_tabstop(&mut self) {
+ self.horizontal_tabstops.insert(self.cursor.x);
+ }
+ fn clear_tabstop(&mut self, position: usize) {
+ self.horizontal_tabstops.remove(&position);
+ }
+ fn clear_all_tabstops(&mut self) {
+ self.horizontal_tabstops.clear();
+ }
+ fn save_cursor_position(&mut self) {
+ self.saved_cursor_position = Some(self.cursor.clone());
+ }
+ fn restore_cursor_position(&mut self) {
+ if let Some(saved_cursor_position) = self.saved_cursor_position.as_ref() {
+ self.cursor = saved_cursor_position.clone();
+ }
+ }
+ fn configure_charset(&mut self, charset: StandardCharset, index: CharsetIndex) {
+ self.cursor.charsets[index] = charset;
+ }
+ fn set_active_charset(&mut self, index: CharsetIndex) {
+ self.active_charset = index;
+ }
+ fn cursor_canonical_line_index(&self) -> usize {
+ let mut cursor_canonical_line_index = 0;
+ let mut canonical_lines_traversed = 0;
+ for (i, line) in self.viewport.iter().enumerate() {
+ if line.is_canonical {
+ cursor_canonical_line_index = canonical_lines_traversed;
+ canonical_lines_traversed += 1;
+ }
+ if i == self.cursor.y {
+ break;
+ }
+ }
+ cursor_canonical_line_index
+ }
+ // TODO: merge these two funtions
+ fn cursor_index_in_canonical_line(&self) -> usize {
+ let mut cursor_canonical_line_index = 0;
+ let mut cursor_index_in_canonical_line = 0;
+ for (i, line) in self.viewport.iter().enumerate() {
+ if line.is_canonical {
+ cursor_canonical_line_index = i;
+ }
+ if i == self.cursor.y {
+ let line_wrap_position_in_line = self.cursor.y - cursor_canonical_line_index;
+ cursor_index_in_canonical_line = line_wrap_position_in_line + self.cursor.x;
+ break;
+ }
+ }
+ cursor_index_in_canonical_line
+ }
+ fn canonical_line_y_coordinates(&self, canonical_line_index: usize) -> usize {
+ let mut canonical_lines_traversed = 0;
+ let mut y_coordinates = 0;
+ for (i, line) in self.viewport.iter().enumerate() {
+ if line.is_canonical {
+ canonical_lines_traversed += 1;
+ if canonical_lines_traversed == canonical_line_index + 1 {
+ y_coordinates = i;
+ break;
+ }
+ }
+ }
+ y_coordinates
+ }
+ pub fn scroll_up_one_line(&mut self) {
+ if !self.lines_above.is_empty() && self.viewport.len() == self.height {
+ let line_to_push_down = self.viewport.pop().unwrap();
+ self.lines_below.insert(0, line_to_push_down);
+ let line_to_insert_at_viewport_top = self.lines_above.pop_back().unwrap();
+ self.viewport.insert(0, line_to_insert_at_viewport_top);
+ }
+ }
+ pub fn scroll_down_one_line(&mut self) {
+ if !self.lines_below.is_empty() && self.viewport.len() == self.height {
+ let mut line_to_push_up = self.viewport.remove(0);
+ if line_to_push_up.is_canonical {
+ bounded_push(&mut self.lines_above, line_to_push_up);
+ } else {
+ let mut last_line_above = self.lines_above.pop_back().unwrap();
+ last_line_above.append(&mut line_to_push_up.columns);
+ bounded_push(&mut self.lines_above, last_line_above);
+ }
+ let line_to_insert_at_viewport_bottom = self.lines_below.remove(0);
+ self.viewport.push(line_to_insert_at_viewport_bottom);
+ }
+ }
+ pub fn change_size(&mut self, new_rows: usize, new_columns: usize) {
+ if new_columns != self.width {
+ let mut cursor_canonical_line_index = self.cursor_canonical_line_index();
+ let cursor_index_in_canonical_line = self.cursor_index_in_canonical_line();
+ let mut viewport_canonical_lines = vec![];
+ for mut row in self.viewport.drain(..) {
+ if !row.is_canonical
+ && viewport_canonical_lines.is_empty()
+ && !self.lines_above.is_empty()
+ {
+ let mut first_line_above = self.lines_above.pop_back().unwrap();
+ first_line_above.append(&mut row.columns);
+ viewport_canonical_lines.push(first_line_above);
+ cursor_canonical_line_index += 1;
+ } else if row.is_canonical {
+ viewport_canonical_lines.push(row);
+ } else {
+ match viewport_canonical_lines.last_mut() {