aboutsummaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
authordenis <denismaximov98@gmail.com>2021-05-01 13:45:01 +0300
committerdenis <denismaximov98@gmail.com>2021-05-01 13:45:01 +0300
commiteabecde90b1617ce4c29be9d5af3f52ccb67e45f (patch)
tree0d0dafa594269045f084383a9719f2a4995fe7ea /src/client
parentwip: more cleaning up (diff)
parentdocs(changelog): performance improvements (diff)
downloadsl0ppij-eabecde90b1617ce4c29be9d5af3f52ccb67e45f.tar.gz
sl0ppij-eabecde90b1617ce4c29be9d5af3f52ccb67e45f.tar.xz
sl0ppij-eabecde90b1617ce4c29be9d5af3f52ccb67e45f.zip
chore: painless merge this time
Diffstat (limited to 'src/client')
-rw-r--r--src/client/layout.rs12
-rw-r--r--src/client/panes/grid.rs133
-rw-r--r--src/client/panes/plugin_pane.rs3
-rw-r--r--src/client/panes/terminal_character.rs12
-rw-r--r--src/client/panes/terminal_pane.rs22
-rw-r--r--src/client/panes/unit/grid_tests.rs85
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap47
-rw-r--r--src/client/tab.rs39
13 files changed, 548 insertions, 40 deletions
diff --git a/src/client/layout.rs b/src/client/layout.rs
index e28058ba..ca1adb4d 100644
--- a/src/client/layout.rs
+++ b/src/client/layout.rs
@@ -205,6 +205,18 @@ impl Layout {
layout
}
+ // It wants to use Path here, but that doesn't compile.
+ #[warn(clippy::ptr_arg)]
+ pub fn from_defaults(layout_path: &PathBuf, data_dir: &Path) -> Self {
+ Self::new(
+ &data_dir
+ .join("layouts/")
+ .join(layout_path)
+ .with_extension("yaml"),
+ &data_dir,
+ )
+ }
+
pub fn total_terminal_panes(&self) -> usize {
let mut total_panes = 0;
total_panes += self.parts.len();
diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs
index c6dd201e..162af1e6 100644
--- a/src/client/panes/grid.rs
+++ b/src/client/panes/grid.rs
@@ -161,6 +161,7 @@ pub struct Grid {
pending_styles: CharacterStyles,
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 clear_viewport_before_rendering: bool,
pub width: usize,
pub height: usize,
@@ -170,9 +171,9 @@ 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, "{:?} (C): {:?}", i, row)?;
+ writeln!(f, "{:02?} (C): {:?}", i, row)?;
} else {
- writeln!(f, "{:?} (W): {:?}", i, row)?;
+ writeln!(f, "{:02?} (W): {:?}", i, row)?;
}
}
Ok(())
@@ -192,20 +193,23 @@ impl Grid {
pending_styles: CharacterStyles::new(),
should_render: true,
cursor_key_mode: false,
+ erasure_mode: false,
alternative_lines_above_viewport_and_cursor: None,
clear_viewport_before_rendering: false,
}
}
+ 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 columns_until_next_tabstop = TABSTOP_WIDTH - (self.cursor.x % TABSTOP_WIDTH);
- let columns_until_screen_end = self.width - self.cursor.x;
+ let columns_until_screen_end = (self.width - self.cursor.x).saturating_sub(1);
let columns_to_advance =
std::cmp::min(columns_until_next_tabstop, columns_until_screen_end);
let mut empty_character = EMPTY_TERMINAL_CHARACTER;
empty_character.styles = styles;
- for _ in 0..columns_to_advance {
- self.add_character(empty_character)
- }
+ self.cursor.x += columns_to_advance;
+ self.pad_current_line_until(self.cursor.x);
}
fn cursor_canonical_line_index(&self) -> usize {
let mut cursor_canonical_line_index = 0;
@@ -475,6 +479,13 @@ impl Grid {
}
}
}
+ pub fn fill_viewport(&mut self, character: TerminalCharacter) {
+ self.viewport.clear();
+ for _ in 0..self.height {
+ let columns = vec![character; self.width];
+ self.viewport.push(Row::from_columns(columns).canonical());
+ }
+ }
pub fn add_canonical_line(&mut self) {
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
if self.cursor.y == scroll_region_bottom {
@@ -490,8 +501,12 @@ impl Grid {
}
self.viewport.remove(scroll_region_top);
let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
+ if self.viewport.len() >= scroll_region_bottom {
+ self.viewport
+ .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
+ } else {
+ self.viewport.push(Row::from_columns(columns).canonical());
+ }
return;
}
}
@@ -518,13 +533,6 @@ impl Grid {
pub fn move_cursor_to_beginning_of_line(&mut self) {
self.cursor.x = 0;
}
- pub fn move_cursor_backwards(&mut self, count: usize) {
- if self.cursor.x > count {
- self.cursor.x -= count;
- } else {
- self.cursor.x = 0;
- }
- }
pub fn insert_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) {
match self.viewport.get_mut(self.cursor.y) {
Some(row) => {
@@ -601,17 +609,13 @@ impl Grid {
}
}
pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
- let line_part = vec![replace_with; self.cursor.x];
+ let line_part = vec![replace_with; self.cursor.x + 1];
let row = self.viewport.get_mut(self.cursor.y).unwrap();
row.replace_beginning_with(line_part);
}
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
- if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y).as_mut() {
+ if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y) {
cursor_row.truncate(self.cursor.x);
- let mut replace_with_columns_in_cursor_row =
- vec![replace_with; self.width - self.cursor.x];
- cursor_row.append(&mut replace_with_columns_in_cursor_row);
-
let replace_with_columns = vec![replace_with; self.width];
self.replace_characters_in_line_after_cursor(replace_with);
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
@@ -619,6 +623,15 @@ impl Grid {
}
}
}
+ pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
+ if self.viewport.get(self.cursor.y).is_some() {
+ self.replace_characters_in_line_before_cursor(replace_with);
+ let replace_with_columns = vec![replace_with; self.width];
+ for row in self.viewport.iter_mut().take(self.cursor.y) {
+ row.replace_columns(replace_with_columns.clone());
+ }
+ }
+ }
pub fn clear_cursor_line(&mut self) {
self.viewport.get_mut(self.cursor.y).unwrap().truncate(0);
}
@@ -642,10 +655,25 @@ impl Grid {
}
}
pub fn move_cursor_to(&mut self, x: usize, y: usize, pad_character: TerminalCharacter) {
- self.cursor.x = std::cmp::min(self.width - 1, x);
- self.cursor.y = std::cmp::min(self.height - 1, y);
- self.pad_lines_until(self.cursor.y, pad_character);
- self.pad_current_line_until(self.cursor.x);
+ match self.scroll_region {
+ Some((scroll_region_top, scroll_region_bottom)) => {
+ self.cursor.x = std::cmp::min(self.width - 1, x);
+ let y_offset = if self.erasure_mode {
+ scroll_region_top
+ } else {
+ 0
+ };
+ self.cursor.y = std::cmp::min(scroll_region_bottom, y + y_offset);
+ self.pad_lines_until(self.cursor.y, pad_character);
+ self.pad_current_line_until(self.cursor.x);
+ }
+ None => {
+ self.cursor.x = std::cmp::min(self.width - 1, x);
+ self.cursor.y = std::cmp::min(self.height - 1, y);
+ self.pad_lines_until(self.cursor.y, pad_character);
+ self.pad_current_line_until(self.cursor.x);
+ }
+ }
}
pub fn move_cursor_up(&mut self, count: usize) {
self.cursor.y = if self.cursor.y < count {
@@ -690,6 +718,10 @@ impl Grid {
self.pad_lines_until(self.cursor.y, pad_character);
}
pub fn move_cursor_back(&mut self, count: usize) {
+ if self.cursor.x == self.width {
+ // on the rightmost screen edge, backspace skips one character
+ self.cursor.x -= 1;
+ }
if self.cursor.x < count {
self.cursor.x = 0;
} else {
@@ -827,14 +859,15 @@ impl vte::Perform for Grid {
match byte {
8 => {
// backspace
- self.move_cursor_backwards(1);
+ self.move_cursor_back(1);
}
9 => {
// tab
self.advance_to_next_tabstop(self.pending_styles);
}
- 10 => {
+ 10 | 11 => {
// 0a, newline
+ // 0b, form-feed
self.add_newline();
}
13 => {
@@ -891,11 +924,13 @@ impl vte::Perform for Grid {
char_to_replace.styles = self.pending_styles;
if params[0] == 0 {
self.clear_all_after_cursor(char_to_replace);
+ } else if params[0] == 1 {
+ self.clear_all_before_cursor(char_to_replace);
} else if params[0] == 2 {
self.clear_all(char_to_replace);
}
// TODO: implement 1
- } else if c == 'H' {
+ } else if c == 'H' || c == 'f' {
// goto row/col
// we subtract 1 from the row/column because these are 1 indexed
// (except when they are 0, in which case they should be 1
@@ -963,6 +998,16 @@ impl vte::Perform for Grid {
Some(&1) => {
self.cursor_key_mode = false;
}
+ Some(&3) => {
+ // DECCOLM - only side effects
+ self.scroll_region = None;
+ self.clear_all(EMPTY_TERMINAL_CHARACTER);
+ self.cursor.x = 0;
+ self.cursor.y = 0;
+ }
+ Some(&6) => {
+ self.erasure_mode = false;
+ }
_ => {}
};
}
@@ -993,6 +1038,16 @@ impl vte::Perform for Grid {
Some(&1) => {
self.cursor_key_mode = true;
}
+ Some(&3) => {
+ // DECCOLM - only side effects
+ self.scroll_region = None;
+ self.clear_all(EMPTY_TERMINAL_CHARACTER);
+ self.cursor.x = 0;
+ self.cursor.y = 0;
+ }
+ Some(&6) => {
+ self.erasure_mode = true;
+ }
_ => {}
};
}
@@ -1098,12 +1153,27 @@ impl vte::Perform for Grid {
fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
match (byte, intermediates.get(0)) {
+ (b'D', None) => {
+ self.add_newline();
+ }
+ (b'E', None) => {
+ self.add_newline();
+ self.move_cursor_to_beginning_of_line();
+ }
(b'M', None) => {
self.move_cursor_up_with_scrolling(1);
}
(b'c', None) => {
self.reset_terminal_state();
}
+ (b'H', None) => {
+ self.advance_to_next_tabstop(self.pending_styles);
+ }
+ (b'8', Some(b'#')) => {
+ let mut fill_character = EMPTY_TERMINAL_CHARACTER;
+ fill_character.character = 'E';
+ self.fill_viewport(fill_character);
+ }
_ => {}
}
}
@@ -1156,6 +1226,9 @@ impl Row {
self.is_canonical = true;
self
}
+ pub fn contains_widechar(&self) -> bool {
+ self.columns.iter().any(|c| c.is_widechar())
+ }
pub fn add_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
match self.columns.len().cmp(&x) {
Ordering::Equal => self.columns.push(terminal_character),
@@ -1254,3 +1327,7 @@ impl Cursor {
}
}
}
+
+#[cfg(test)]
+#[path = "./unit/grid_tests.rs"]
+mod grid_tests;
diff --git a/src/client/panes/plugin_pane.rs b/src/client/panes/plugin_pane.rs
index 21480ac2..c3b633ec 100644
--- a/src/client/panes/plugin_pane.rs
+++ b/src/client/panes/plugin_pane.rs
@@ -96,6 +96,9 @@ impl Pane for PluginPane {
fn position_and_size_override(&self) -> Option<PositionAndSize> {
self.position_and_size_override
}
+ fn contains_widechar(&self) -> bool {
+ false
+ }
fn should_render(&self) -> bool {
self.should_render
}
diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs
index 7569e993..94620bba 100644
--- a/src/client/panes/terminal_character.rs
+++ b/src/client/panes/terminal_character.rs
@@ -1,3 +1,5 @@
+use unicode_width::UnicodeWidthChar;
+
use crate::utils::logging::debug_log_to_file;
use ::std::fmt::{self, Debug, Display, Formatter};
@@ -634,3 +636,13 @@ impl ::std::fmt::Debug for TerminalCharacter {
write!(f, "{}", self.character)
}
}
+
+impl TerminalCharacter {
+ pub fn width(&self) -> usize {
+ self.character.width().unwrap_or(0)
+ }
+
+ pub fn is_widechar(&self) -> bool {
+ self.width() > 1
+ }
+}
diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs
index ddb10889..a5a957e6 100644
--- a/src/client/panes/terminal_pane.rs
+++ b/src/client/panes/terminal_pane.rs
@@ -87,6 +87,7 @@ impl Pane for TerminalPane {
for byte in bytes.iter() {
self.vte_parser.advance(&mut self.grid, *byte);
}
+ self.set_should_render(true);
}
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
// (x, y)
@@ -138,6 +139,9 @@ impl Pane for TerminalPane {
fn position_and_size_override(&self) -> Option<PositionAndSize> {
self.position_and_size_override
}
+ fn contains_widechar(&self) -> bool {
+ self.grid.contains_widechar()
+ }
fn should_render(&self) -> bool {
self.grid.should_render
}
@@ -166,14 +170,7 @@ impl Pane for TerminalPane {
self.max_width
}
fn render(&mut self) -> Option<String> {
- // FIXME:
- // the below conditional is commented out because it causes several bugs:
- // 1. When panes are resized or tabs are switched the previous contents of the screen stick
- // around
- // 2. When there are wide characters in a pane, since we don't yet handle them properly,
- // the spill over to the pane to the right
- // if self.should_render || cfg!(test) {
- if true {
+ if self.should_render() {
let mut vte_output = String::new();
let buffer_lines = &self.read_buffer_as_lines();
let display_cols = self.get_columns();
@@ -215,7 +212,7 @@ impl Pane for TerminalPane {
}
character_styles.clear();
}
- self.grid.should_render = false;
+ self.set_should_render(false);
Some(vte_output)
} else {
None
@@ -274,15 +271,15 @@ impl Pane for TerminalPane {
}
fn scroll_up(&mut self, count: usize) {
self.grid.move_viewport_up(count);
- self.grid.should_render = true;
+ self.set_should_render(true);
}
fn scroll_down(&mut self, count: usize) {
self.grid.move_viewport_down(count);
- self.grid.should_render = true;
+ self.set_should_render(true);
}
fn clear_scroll(&mut self) {
self.grid.reset_viewport();
- self.grid.should_render = true;
+ self.set_should_render(true);
}
fn active_at(&self) -> Instant {
@@ -337,6 +334,7 @@ impl TerminalPane {
let rows = self.get_rows();
let columns = self.get_columns();
self.grid.change_size(rows, columns);
+ self.set_should_render(true);
}
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
self.grid.as_character_lines()
diff --git a/src/client/panes/unit/grid_tests.rs b/src/client/panes/unit/grid_tests.rs
new file mode 100644
index 00000000..92f5fbdf
--- /dev/null
+++ b/src/client/panes/unit/grid_tests.rs
@@ -0,0 +1,85 @@
+use super::super::Grid;
+use ::insta::assert_snapshot;
+
+fn read_fixture(fixture_name: &str) -> Vec<u8> {
+ let mut path_to_file = std::path::PathBuf::new();
+ path_to_file.push("src");
+ path_to_file.push("tests");
+ path_to_file.push("fixtures");
+ path_to_file.push(fixture_name);
+ let content = std::fs::read(path_to_file)
+ .unwrap_or_else(|_| panic!("could not read fixture {:?}", &fixture_name));
+ content
+}
+
+#[test]
+fn vttest1_0() {
+ let mut vte_parser = vte::Parser::new();
+ let mut grid = Grid::new(41, 110);
+ let fixture_name = "vttest1-0";
+ let content = read_fixture(fixture_name);
+ for byte in content {
+ vte_parser.advance(&mut grid, byte);
+ }
+ assert_snapshot!(format!("{:?}", grid));
+}
+
+#[test]
+fn vttest1_1() {
+ let mut vte_parser = vte::Parser::new();
+ let mut grid = Grid::new(41, 110);
+ let fixture_name = "vttest1-1";
+ let content = read_fixture(fixture_name);
+ for byte in content {
+ vte_parser.advance(&mut grid, byte);
+ }
+ assert_snapshot!(format!("{:?}", grid));
+}
+
+#[test]
+fn vttest1_2() {
+ let mut vte_parser = vte::Parser::new();
+ let mut grid = Grid::new(41, 110);
+ let fixture_name = "vttest1-2";
+ let content = read_fixture(fixture_name);
+ for byte in content {
+ vte_parser.advance(&mut grid, byte);
+ }
+ assert_snapshot!(format!("{:?}", grid));
+}
+
+#[test]
+fn vttest1_3() {
+ let mut vte_parser = vte::Parser::new();
+ let mut grid = Grid::new(41, 110);
+ let fixture_name = "vttest1-3";
+ let content = read_fixture(fixture_name);
+ for byte in content {
+ vte_parser.advance(&mut grid, byte);
+ }
+ assert_snapshot!(format!("{:?}", grid));
+}
+
+#[test]
+fn vttest1_4() {
+ let mut vte_parser = vte::Parser::new();
+ let mut grid = Grid::new(41, 110);
+ let fixture_name = "vttest1-4";
+ let content = read_fixture(fixture_name);
+ for byte in content {
+ vte_parser.advance(&mut grid, byte);
+ }
+ assert_snapshot!(format!("{:?}", grid));
+}
+
+#[test]
+fn vttest1_5() {
+ let mut vte_parser = vte::Parser::new();
+ let mut grid = Grid::new(41, 110);
+ let fixture_name = "vttest1-5";
+ let content = read_fixture(fixture_name);
+ for byte in content {
+ vte_parser.advance(&mut grid, byte);
+ }
+ assert_snapshot!(format!("{:?}", grid));
+}
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap
new file mode 100644
index 00000000..8641098c
--- /dev/null
+++ b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap
@@ -0,0 +1,47 @@
+---
+source: src/client/panes/grid.rs
+expression: "format!(\"{:?}\", grid)"
+
+---
+00 (C): ********************************************************************************
+01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
+02 (C): *+ +*
+03 (C): *+ +*
+04 (C): *+ +*
+05 (C): *+ +*
+06 (C): *+ +*
+07 (C): *+ +*
+08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
+09 (C): *+ E E +*
+10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
+11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
+12 (C): *+ E middle there should be a frame of E's around this text E +*
+13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
+14 (C): *+ E E +*
+15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
+16 (C): *+ +*
+17 (C): *+ +*
+18 (C): *+ +*
+19 (C): *+ +*
+20 (C): *+ +*
+21 (C): *+ +*
+22 (C): *+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+23 (C): ********************************************************************************
+24 (C):
+25 (C):
+26 (C):
+27 (C):
+28 (C):
+29 (C):
+30 (C):
+31 (C):
+32 (C):
+33 (C):
+34 (C):
+35 (C):
+36 (C):
+37 (C):
+38 (C):
+39 (C):
+40 (C):
+
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap
new file mode 100644
index 00000000..09663484
--- /dev/null
+++ b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap
@@ -0,0 +1,47 @@
+---
+source: src/client/panes/grid.rs
+expression: "format!(\"{:?}\", grid)"
+
+---
+00 (C): **************************************************************************************************************
+01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
+02 (C): *+ +*
+03 (C): *+ +*
+04 (C): *+ +*
+05 (C): *+ +*
+06 (C): *+ +*
+07 (C): *+ +*
+08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
+09 (C): *+ E E +*
+10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
+11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
+12 (C): *+ E middle there should be a frame of E's around this text E +*
+13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
+14 (C): *+ E E +*
+15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
+16 (C): *+ +*
+17 (C): *+ +*
+18 (C): *+ +*
+19 (C): *+ +*
+20 (C): *+ +*
+21 (C): *+ +*
+22 (C): ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ *
+23 (C): **************************************************************************************************************
+24 (C):
+25 (C):
+26 (C):
+27 (C):
+28 (C):
+29 (C):
+30 (C):
+31 (C):
+32 (C):
+33 (C):
+34 (C):
+35 (C):
+36 (C):
+37 (C):
+38 (C):
+39 (C):
+40 (C):
+
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap
new file mode 100644
index 00000000..05d80b1d
--- /dev/null
+++ b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap
@@ -0,0 +1,47 @@
+---
+source: src/client/panes/grid.rs
+expression: "format!(\"{:?}\", grid)"
+
+---
+00 (C): Test of autowrap, mixing control and print characters.
+01 (C): The left/right margins should have letters in order:
+02 (C): I iJ
+03 (C): j
+04 (C): K K k
+05 (C): L l
+06 (C): M mN
+07 (C): n
+08 (C): O O o
+09 (C): P p
+10 (C): Q qR
+11 (C): r
+12 (C): S S s
+13 (C): T t
+14 (C): U uV
+15 (C): v
+16 (C): W W w
+17 (C): X x
+18 (C): Y yZ
+19 (C): z
+20 (C):
+21 (C): Push <RETURN>
+22 (C):
+23 (C):
+24 (C):
+25 (C):
+26 (C):
+27 (C):
+28 (C):
+29 (C):
+30 (C):
+31 (C):
+32 (C):
+33 (C):
+34 (C):
+35 (C):
+36 (C):
+37 (C):
+38 (C):
+39 (C):
+40 (C):
+
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap
new file mode 100644
index 00000000..64c3e7b9
--- /dev/null
+++ b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap
@@ -0,0 +1,47 @@
+---
+source: src/client/panes/grid.rs
+expression: "format!(\"{:?}\", grid)"
+
+---
+00 (C): Test of autowrap, mixing control and print characters.
+01 (C): The left/right margins should have letters in order:
+02 (C): I i
+03 (C): J j
+04 (C): K k
+05 (C): L l
+06 (C): M m
+07 (C): N n
+08 (C): O o
+09 (C): P p
+10 (C): Q q
+11 (C): R r
+12 (C): S s
+13 (C): T t
+14 (C): U u
+15 (C): V v
+16 (C): W w
+17 (C): X x
+18 (C): Y y
+19 (C): Z z
+20 (C):
+21 (C): Push <RETURN>
+22 (C):
+23 (C):
+24 (C):
+25 (C):
+26 (C):
+27 (C):
+28 (C):
+29 (C):
+30 (C):
+31 (C):
+32 (C):
+33 (C):
+34 (C):
+35 (C):
+36 (C):
+37 (C):
+38 (C):
+39 (C):
+40 (C):
+
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap
new file mode 100644
index 00000000..dcb417ae
--- /dev/null
+++ b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap
@@ -0,0 +1,47 @@
+---
+source: src/client/panes/grid.rs
+expression: "format!(\"{:?}\", grid)"
+
+---
+00 (C): Test of cursor-control characters inside ESC sequences.
+01 (C): Below should be four identical lines:
+02 (C):
+03 (C): A B C D E F G H I
+04 (C): A B C D E F G H I
+05 (C): A B C D E F G H I
+06 (C): A B C D E F G H I
+07 (C):
+08 (C): Push <RETURN>
+09 (C):
+10 (C):
+11 (C):
+12 (C):
+13 (C):
+14 (C):
+15 (C):
+16 (C):
+17 (C):
+18 (C):
+19 (C):
+20 (C):
+21 (C):
+22 (C):
+23 (C):
+24 (C):
+25 (C):
+26 (C):
+27 (C):
+28 (C):
+29 (C):
+30 (C):
+31 (C):
+32 (C):
+33 (C):
+34 (C):
+35 (C):
+36 (C):
+37 (C):
+38 (C):
+39 (C):
+40 (C):
+
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap
new file mode 100644
index 00000000..d044691c
--- /dev/null
+++ b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap
@@ -0,0 +1,47 @@
+---
+source: src/client/panes/grid.rs
+expression: "format!(\"{:?}\", grid)"
+
+---
+00 (C): Test of leading zeros in ESC sequences.
+01 (C): Two lines below you should see the sentence "This is a correct sentence".
+02 (C):
+03 (C): This is a correct sentence
+04 (C):
+05 (C):
+06 (C):
+07 (C):
+08 (C):
+09 (C):
+10 (C):
+11 (C):
+12 (C):
+13 (C):
+14 (C):
+15 (C):
+16 (C):
+17 (C):
+18 (C):
+19 (C): Push <RETURN>
+20 (C):
+21 (C):
+22 (C):
+23 (C):
+24 (C):
+25 (C):
+26 (C):
+27 (C):
+28 (C):
+29 (C):
+30 (C):
+31 (C):
+32 (C):
+33 (C):
+34 (C):
+35 (C):
+36 (C):
+37 (C):
+38 (C):
+39 (C):
+40 (C):
+
diff --git a/src/client/tab.rs b/src/client/tab.rs
index 34bc585d..b01e04e5 100644
--- a/src/client/tab.rs
+++ b/src/client/tab.rs
@@ -67,6 +67,7 @@ pub struct Tab {
max_panes: Option<usize>,
full_screen_ws: PositionAndSize,
fullscreen_is_active: bool,
+ synchronize_is_active: bool,
os_api: Box<dyn OsApi>,
pub send_pty_instructions: SenderWithContext<PtyInstruction>,
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
@@ -104,6 +105,9 @@ pub trait Pane {
fn position_and_size_override(&self) -> Option<PositionAndSize>;
fn should_render(&self) -> bool;
fn set_should_render(&mut self, should_render: bool);
+ // FIXME: this method is used to trigger a force render to hide widechar problem
+ // it should be removed when we can handle widechars
+ fn contains_widechar(&self) -> bool;
fn selectable(&self) -> bool;
fn set_selectable(&mut self, selectable: bool);
fn set_invisible_borders(&mut self, invisible_borders: bool);
@@ -254,6 +258,7 @@ impl Tab {
active_terminal: pane_id,
full_screen_ws: *full_screen_ws,
fullscreen_is_active: false,
+ synchronize_is_active: false,
os_api,
send_app_instructions,
send_pty_instructions,
@@ -599,6 +604,21 @@ impl Tab {
terminal_output.handle_pty_bytes(bytes);
}
}
+ pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec<u8>) {
+ let pane_ids = self.get_pane_ids();
+ pane_ids.iter().for_each(|pane_id| match pane_id {
+ PaneId::Terminal(pid) => {
+ self.write_to_pane_id(input_bytes.clone(), *pid);
+ }
+ PaneId::Plugin(_) => {}
+ });
+ }
+ pub fn write_to_pane_id(&mut self, mut input_bytes: Vec<u8>, pid: RawFd) {
+ self.os_api
+ .write_to_tty_stdin(pid, &mut input_bytes)
+ .expect("failed to write to terminal");
+ self.os_api.tcdrain(pid).expect("failed to drain terminal");
+ }
pub fn write_to_active_terminal(&mut self, input_bytes: Vec<u8>) {
match self.get_active_pane_id() {
Some(PaneId::Terminal(active_terminal_id)) => {
@@ -684,12 +704,31 @@ impl Tab {
pub fn toggle_fullscreen_is_active(&mut self) {
self.fullscreen_is_active = !self.fullscreen_is_active;
}
+ pub fn set_force_render(&mut self) {
+ for pane in self.panes.values_mut() {
+ pane.set_should_render(true);
+ }
+ }
+ pub fn is_sync_panes_active(&self) -> bool {
+ self.synchronize_is_active
+ }
+ pub fn toggle_sync_panes_is_active(&mut self) {
+ self.synchronize_is_active = !self.synchronize_is_active;
+ }
+ pub fn panes_contain_widechar(&self) -> bool {
+ self.panes.iter().any(|(_, p)| p.contains_widechar())
+ }
pub fn render(&mut self) {
if self.active_terminal.is_none() {
// we might not have an active terminal if we closed the last pane
// in that case, we should not render as the app is exiting
return;
}
+ // if any pane contain widechar, all pane in the same row will messup. We should render them every time
+ // FIXME: remove this when we can handle widechars correctly
+ if self.panes_contain_widechar() {
+ self.set_force_render()
+ }
let mut stdout = self.os_api.get_stdout_writer();
let mut boundaries = Boundaries::new(
self.full_screen_ws.columns as u16,