diff options
author | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 16:42:50 +0530 |
---|---|---|
committer | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 21:41:56 +0530 |
commit | 2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch) | |
tree | 5a332470e32b380816098594a89d3c3db1f6eb53 /zellij-server/src/panes | |
parent | 28212f54309d09263c6daa1801d49e8b4311be12 (diff) |
Big refactor: separate crates for client, server and utilities
Diffstat (limited to 'zellij-server/src/panes')
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. "[D") + 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() { |