Merge branch 'main' into mac-config

This commit is contained in:
a-kenji 2021-05-11 11:10:34 +02:00 committed by GitHub
commit 4b39699964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 179 additions and 13 deletions

View File

@ -10,7 +10,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* Move the sync command to tab mode (https://github.com/zellij-org/zellij/pull/412) * Move the sync command to tab mode (https://github.com/zellij-org/zellij/pull/412)
* Fix exit code of `dump-default-config` (https://github.com/zellij-org/zellij/pull/480) * Fix exit code of `dump-default-config` (https://github.com/zellij-org/zellij/pull/480)
* Feature: Switch tabs using `Alt + h/l` in normal mode if there are no panes in the direction (https://github.com/zellij-org/zellij/pull/471) * Feature: Switch tabs using `Alt + h/l` in normal mode if there are no panes in the direction (https://github.com/zellij-org/zellij/pull/471)
* Fix handling of $HOME `config` direcotry, especially relevant for darwin systems (https://github.com/zellij-org/zellij/pull/487) * Terminal Compatibility: various behaviour fixes (https://github.com/zellij-org/zellij/pull/486)
* Fix handling of `$HOME` `config` directory, especially relevant for darwin systems (https://github.com/zellij-org/zellij/pull/487)
## [0.8.0] - 2021-05-07 ## [0.8.0] - 2021-05-07
* Terminal compatibility: pass vttest 8 (https://github.com/zellij-org/zellij/pull/461) * Terminal compatibility: pass vttest 8 (https://github.com/zellij-org/zellij/pull/461)

View File

@ -12,7 +12,7 @@ const SCROLL_BACK: usize = 10_000;
use crate::utils::logging::debug_log_to_file; use crate::utils::logging::debug_log_to_file;
use crate::panes::terminal_character::{ use crate::panes::terminal_character::{
CharacterStyles, CharsetIndex, Cursor, StandardCharset, TerminalCharacter, CharacterStyles, CharsetIndex, Cursor, CursorShape, StandardCharset, TerminalCharacter,
EMPTY_TERMINAL_CHARACTER, EMPTY_TERMINAL_CHARACTER,
}; };
@ -177,6 +177,7 @@ pub struct Grid {
saved_cursor_position: Option<Cursor>, saved_cursor_position: Option<Cursor>,
scroll_region: Option<(usize, usize)>, scroll_region: Option<(usize, usize)>,
active_charset: CharsetIndex, active_charset: CharsetIndex,
preceding_char: Option<TerminalCharacter>,
pub should_render: bool, 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 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 erasure_mode: bool, // ERM
@ -210,6 +211,7 @@ impl Grid {
cursor: Cursor::new(0, 0), cursor: Cursor::new(0, 0),
saved_cursor_position: None, saved_cursor_position: None,
scroll_region: None, scroll_region: None,
preceding_char: None,
width: columns, width: columns,
height: rows, height: rows,
should_render: true, should_render: true,
@ -245,6 +247,26 @@ impl Grid {
empty_character.styles = styles; empty_character.styles = styles;
self.pad_current_line_until(self.cursor.x); 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) { fn set_horizontal_tabstop(&mut self) {
self.horizontal_tabstops.insert(self.cursor.x); self.horizontal_tabstops.insert(self.cursor.x);
} }
@ -925,6 +947,10 @@ impl Grid {
self.active_charset = Default::default(); self.active_charset = Default::default();
self.erasure_mode = false; self.erasure_mode = false;
self.disable_linewrap = false; self.disable_linewrap = false;
self.cursor.change_shape(CursorShape::Block);
}
fn set_preceding_character(&mut self, terminal_character: TerminalCharacter) {
self.preceding_char = Some(terminal_character);
} }
} }
@ -937,6 +963,7 @@ impl Perform for Grid {
character: c, character: c,
styles: self.cursor.pending_styles, styles: self.cursor.pending_styles,
}; };
self.set_preceding_character(terminal_character);
self.add_character(terminal_character); self.add_character(terminal_character);
} }
@ -950,9 +977,10 @@ impl Perform for Grid {
// tab // tab
self.advance_to_next_tabstop(self.cursor.pending_styles); self.advance_to_next_tabstop(self.cursor.pending_styles);
} }
10 | 11 => { 10 | 11 | 12 => {
// 0a, newline // 0a, newline
// 0b, vertical tabulation // 0b, vertical tabulation
// 0c, form feed
self.add_newline(); self.add_newline();
} }
13 => { 13 => {
@ -985,7 +1013,7 @@ impl Perform for Grid {
// TBD // TBD
} }
fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) { fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], _ignore: bool, c: char) {
let mut params_iter = params.iter(); let mut params_iter = params.iter();
let mut next_param_or = |default: u16| { let mut next_param_or = |default: u16| {
params_iter params_iter
@ -998,7 +1026,7 @@ impl Perform for Grid {
self.cursor self.cursor
.pending_styles .pending_styles
.add_style_from_ansi_params(&mut params_iter); .add_style_from_ansi_params(&mut params_iter);
} else if c == 'C' { } else if c == 'C' || c == 'a' {
// move cursor forward // move cursor forward
let move_by = next_param_or(1); let move_by = next_param_or(1);
self.move_cursor_forward_until_edge(move_by); self.move_cursor_forward_until_edge(move_by);
@ -1031,7 +1059,6 @@ impl Perform for Grid {
self.clear_all(char_to_replace); self.clear_all(char_to_replace);
} }
}; };
// TODO: implement 1
} else if c == 'H' || c == 'f' { } else if c == 'H' || c == 'f' {
// goto row/col // goto row/col
// we subtract 1 from the row/column because these are 1 indexed // we subtract 1 from the row/column because these are 1 indexed
@ -1043,7 +1070,7 @@ impl Perform for Grid {
// move cursor up until edge of screen // move cursor up until edge of screen
let move_up_count = next_param_or(1); let move_up_count = next_param_or(1);
self.move_cursor_up(move_up_count as usize); self.move_cursor_up(move_up_count as usize);
} else if c == 'B' { } else if c == 'B' || c == 'e' {
// move cursor down until edge of screen // move cursor down until edge of screen
let move_down_count = next_param_or(1); let move_down_count = next_param_or(1);
let pad_character = EMPTY_TERMINAL_CHARACTER; let pad_character = EMPTY_TERMINAL_CHARACTER;
@ -1052,7 +1079,7 @@ impl Perform for Grid {
let move_back_count = next_param_or(1); let move_back_count = next_param_or(1);
self.move_cursor_back(move_back_count); self.move_cursor_back(move_back_count);
} else if c == 'l' { } else if c == 'l' {
let first_intermediate_is_questionmark = match _intermediates.get(0) { let first_intermediate_is_questionmark = match intermediates.get(0) {
Some(b'?') => true, Some(b'?') => true,
None => false, None => false,
_ => false, _ => false,
@ -1101,7 +1128,7 @@ impl Perform for Grid {
self.insert_mode = false; self.insert_mode = false;
} }
} else if c == 'h' { } else if c == 'h' {
let first_intermediate_is_questionmark = match _intermediates.get(0) { let first_intermediate_is_questionmark = match intermediates.get(0) {
Some(b'?') => true, Some(b'?') => true,
None => false, None => false,
_ => false, _ => false,
@ -1171,7 +1198,7 @@ impl Perform for Grid {
let line_count_to_add = next_param_or(1); let line_count_to_add = next_param_or(1);
let pad_character = EMPTY_TERMINAL_CHARACTER; let pad_character = EMPTY_TERMINAL_CHARACTER;
self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character); self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character);
} else if c == 'G' { } else if c == 'G' || c == '`' {
let column = next_param_or(1).saturating_sub(1); let column = next_param_or(1).saturating_sub(1);
self.move_cursor_to_column(column); self.move_cursor_to_column(column);
} else if c == 'g' { } else if c == 'g' {
@ -1216,6 +1243,46 @@ impl Perform for Grid {
// TODO: should this be styled? // TODO: should this be styled?
self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER); self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER);
} }
} else if c == 'b' {
if let Some(c) = self.preceding_char {
for _ in 0..next_param_or(1) {
self.add_character(c);
}
}
} else if c == 'E' {
let count = next_param_or(1);
let pad_character = EMPTY_TERMINAL_CHARACTER;
self.move_cursor_down(count, pad_character);
} else if c == 'F' {
let count = next_param_or(1);
self.move_cursor_up(count);
self.move_cursor_to_beginning_of_line();
} else if c == 'I' {
for _ in 0..next_param_or(1) {
self.advance_to_next_tabstop(self.cursor.pending_styles);
}
} else if c == 'q' {
let first_intermediate_is_space = matches!(intermediates.get(0), Some(b' '));
if first_intermediate_is_space {
// DECSCUSR (CSI Ps SP q) -- Set Cursor Style.
let cursor_style_id = next_param_or(0);
let shape = match cursor_style_id {
0 | 2 => Some(CursorShape::Block),
1 => Some(CursorShape::BlinkingBlock),
3 => Some(CursorShape::BlinkingUnderline),
4 => Some(CursorShape::Underline),
5 => Some(CursorShape::BlinkingBeam),
6 => Some(CursorShape::Beam),
_ => None,
};
if let Some(cursor_shape) = shape {
self.cursor.change_shape(cursor_shape);
}
}
} else if c == 'Z' {
for _ in 0..next_param_or(1) {
self.move_to_previous_tabstop();
}
} else { } else {
let result = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params)); let result = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
#[cfg(not(test))] #[cfg(not(test))]
@ -1262,6 +1329,7 @@ impl Perform for Grid {
self.move_cursor_to_beginning_of_line(); self.move_cursor_to_beginning_of_line();
} }
(b'M', None) => { (b'M', None) => {
// TODO: if cursor is at the top, it should go down one
self.move_cursor_up_with_scrolling(1); self.move_cursor_up_with_scrolling(1);
} }
(b'c', None) => { (b'c', None) => {

View File

@ -701,6 +701,16 @@ impl IndexMut<CharsetIndex> for Charsets {
} }
} }
#[derive(Clone, Copy, Debug)]
pub enum CursorShape {
Block,
BlinkingBlock,
Underline,
BlinkingUnderline,
Beam,
BlinkingBeam,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Cursor { pub struct Cursor {
pub x: usize, pub x: usize,
@ -708,6 +718,7 @@ pub struct Cursor {
pub is_hidden: bool, pub is_hidden: bool,
pub pending_styles: CharacterStyles, pub pending_styles: CharacterStyles,
pub charsets: Charsets, pub charsets: Charsets,
shape: CursorShape,
} }
impl Cursor { impl Cursor {
@ -718,8 +729,15 @@ impl Cursor {
is_hidden: false, is_hidden: false,
pending_styles: CharacterStyles::new(), pending_styles: CharacterStyles::new(),
charsets: Default::default(), charsets: Default::default(),
shape: CursorShape::Block,
} }
} }
pub fn change_shape(&mut self, shape: CursorShape) {
self.shape = shape;
}
pub fn get_shape(&self) -> CursorShape {
self.shape
}
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]

View File

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use crate::panes::grid::Grid; use crate::panes::grid::Grid;
use crate::panes::terminal_character::{ use crate::panes::terminal_character::{
CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER, CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
}; };
use crate::pty::VteBytes; use crate::pty::VteBytes;
use crate::tab::Pane; use crate::tab::Pane;
@ -291,6 +291,16 @@ impl Pane for TerminalPane {
fn set_active_at(&mut self, time: Instant) { fn set_active_at(&mut self, time: Instant) {
self.active_at = time; self.active_at = time;
} }
fn cursor_shape_csi(&self) -> String {
match self.grid.cursor_shape() {
CursorShape::Block => "\u{1b}[0 q".to_string(),
CursorShape::BlinkingBlock => "\u{1b}[1 q".to_string(),
CursorShape::Underline => "\u{1b}[4 q".to_string(),
CursorShape::BlinkingUnderline => "\u{1b}[3 q".to_string(),
CursorShape::Beam => "\u{1b}[6 q".to_string(),
CursorShape::BlinkingBeam => "\u{1b}[5 q".to_string(),
}
}
} }
impl TerminalPane { impl TerminalPane {

View File

@ -347,3 +347,39 @@ fn vttest8_5() {
} }
assert_snapshot!(format!("{:?}", grid)); assert_snapshot!(format!("{:?}", grid));
} }
#[test]
fn csi_b() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let fixture_name = "csi-b";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}
#[test]
fn csi_capital_i() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let fixture_name = "csi-capital-i";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}
#[test]
fn csi_capital_z() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(51, 97);
let fixture_name = "csi-capital-z";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}

View File

@ -0,0 +1,8 @@
---
source: src/client/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): ffffff
01 (C):

View File

@ -0,0 +1,8 @@
---
source: src/client/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): foo
01 (C):

View File

@ -0,0 +1,8 @@
---
source: src/client/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): 12345678foo234567890
01 (C):

View File

@ -132,6 +132,9 @@ pub trait Pane {
fn clear_scroll(&mut self); fn clear_scroll(&mut self);
fn active_at(&self) -> Instant; fn active_at(&self) -> Instant;
fn set_active_at(&mut self, instant: Instant); fn set_active_at(&mut self, instant: Instant);
fn cursor_shape_csi(&self) -> String {
"\u{1b}[0 q".to_string() // default to non blinking block
}
fn right_boundary_x_coords(&self) -> usize { fn right_boundary_x_coords(&self) -> usize {
self.x() + self.columns() self.x() + self.columns()
@ -762,10 +765,12 @@ impl Tab {
match self.get_active_terminal_cursor_position() { match self.get_active_terminal_cursor_position() {
Some((cursor_position_x, cursor_position_y)) => { Some((cursor_position_x, cursor_position_y)) => {
let show_cursor = "\u{1b}[?25h"; let show_cursor = "\u{1b}[?25h";
let change_cursor_shape = self.get_active_pane().unwrap().cursor_shape_csi();
let goto_cursor_position = &format!( let goto_cursor_position = &format!(
"\u{1b}[{};{}H\u{1b}[m", "\u{1b}[{};{}H\u{1b}[m{}",
cursor_position_y + 1, cursor_position_y + 1,
cursor_position_x + 1 cursor_position_x + 1,
change_cursor_shape
); // goto row/col ); // goto row/col
output.push_str(show_cursor); output.push_str(show_cursor);
output.push_str(goto_cursor_position); output.push_str(goto_cursor_position);

1
src/tests/fixtures/csi-b vendored Normal file
View File

@ -0,0 +1 @@
f

1
src/tests/fixtures/csi-capital-i vendored Normal file
View File

@ -0,0 +1 @@
foo

1
src/tests/fixtures/csi-capital-z vendored Normal file
View File

@ -0,0 +1 @@
12345678901234567890foo