diff options
author | Aram Drevekenin <aram@poor.dev> | 2022-10-11 16:45:46 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-11 16:45:46 +0200 |
commit | c64bf5207a0d1b79b305f11a3d73c9da77092b6e (patch) | |
tree | 2f5c0efc7c41bc10d96b398c9861d5c7903d2ba8 /zellij-server/src/ui | |
parent | cb926119bc5e180c78f2f754bc61532645c3b5b4 (diff) |
feat(ux): rerun command pane (#1787)
* chore(config): default kdl keybindings config
* tests
* work
* refactor(config): move stuff around
* work
* tab merge layout
* work
* work
* layouts working
* work
* layout tests
* work
* work
* feat(parsing): kdl layouts without config
* refactor(kdl): move stuff around
* work
* tests(layout): add cases and fix bugs
* work
* fix(kdl): various bugs
* chore(layouts): move all layouts to kdl
* feat(kdl): shared keybidns
* fix(layout): do not count fixed panes toward percentile
* fix(keybinds): missing keybinds and actions
* fix(config): adjust default tips
* refactor(config): move stuff around
* fix(tests): make e2e tests pass
* fix(kdl): add verbose parsing errors
* fix(kdl): focused tab
* fix(layout): corret default_tab_template behavior
* style(code): fix compile warnings
* feat(cli): send actions through the cli
* fix(cli): exit only when action is done
* fix(cli): open embedded pane from floating pane
* fix(cli): send actions to other sessions
* feat(cli): command alias
* feat(converter): convert old config
* feat(converter): convert old layout and theme files
* feat(kdl): pretty errors
* feat(client): convert old YAML files on startup
* fix: various bugs and styling issues
* fix: e2e tests
* fix(screen): propagate errors after merge
* style(clippy): lower clippy level
* fix(tests): own session_name variable
* style(fmt): rustfmt
* fix(cli): various action fixes
* style(fmt): rustfmt
* fix(themes): loading of theme files
* style(fmt): rustfmt
* fix(tests): theme fixtures
* fix(layouts): better errors on unknown nodes
* fix(kdl): clarify valid node terminator error
* fix(e2e): adjust close tab test
* fix(e2e): adjust close tab test again
* style(code): cleanup some comments
* get command panes not to exit on command exit
* separate terminal pane_ids from raw_fds
* render frame according to exit status
* re-run command on enter and close pane on ctrl-c
* proper error when command is not found
* make ui nicer
* initial pane title for command panes
* fix pane override bug
* reap terminal_ids from os_input_output on pane close
* bool floating flag
* some ui tweaks
* fix tests
* make rustfmt happy
* e2e test for command pane
* fix various concurrency issues
* rename command to run in the cli
* rustfmt
* style(fmt): rustfmt
* fix(e2e): command => run
* fix(e2e): command => run in snapshot too!
Diffstat (limited to 'zellij-server/src/ui')
-rw-r--r-- | zellij-server/src/ui/pane_boundaries_frame.rs | 213 |
1 files changed, 197 insertions, 16 deletions
diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs index 8f25c98fe..186d4d182 100644 --- a/zellij-server/src/ui/pane_boundaries_frame.rs +++ b/zellij-server/src/ui/pane_boundaries_frame.rs @@ -61,6 +61,12 @@ fn background_color(characters: &str, color: Option<PaletteColor>) -> Vec<Termin colored_string } +#[derive(Debug, Clone, Copy, PartialEq)] +enum ExitStatus { + Code(i32), + Exited, +} + pub struct FrameParams { pub focused_client: Option<ClientId>, pub is_main_client: bool, @@ -81,6 +87,7 @@ pub struct PaneFrame { pub is_main_client: bool, pub other_cursors_exist_in_session: bool, pub other_focused_clients: Vec<ClientId>, + exit_status: Option<ExitStatus>, } impl PaneFrame { @@ -100,8 +107,15 @@ impl PaneFrame { is_main_client: frame_params.is_main_client, other_focused_clients: frame_params.other_focused_clients, other_cursors_exist_in_session: frame_params.other_cursors_exist_in_session, + exit_status: None, } } + pub fn add_exit_status(&mut self, exit_status: Option<i32>) { + self.exit_status = match exit_status { + Some(exit_status) => Some(ExitStatus::Code(exit_status)), + None => Some(ExitStatus::Exited), + }; + } fn client_cursor(&self, client_id: ClientId) -> Vec<TerminalCharacter> { let color = client_id_to_colors(client_id, self.style.colors); background_color(" ", color.map(|c| c.0)) @@ -594,6 +608,65 @@ impl PaneFrame { .or_else(|| Some(self.title_line_without_middle())) .unwrap() } + fn render_held_undertitle(&self) -> Vec<TerminalCharacter> { + let max_undertitle_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners + let exit_status = self.exit_status.unwrap(); // unwrap is safe because we only call this if + + let (mut first_part, first_part_len) = self.first_held_title_part_full(exit_status); + let mut left_boundary = + foreground_color(self.get_corner(boundary_type::BOTTOM_LEFT), self.color); + let mut right_boundary = + foreground_color(self.get_corner(boundary_type::BOTTOM_RIGHT), self.color); + if self.is_main_client { + let (mut second_part, second_part_len) = self.second_held_title_part_full(); + let full_text_len = first_part_len + second_part_len; + if full_text_len <= max_undertitle_length { + // render exit status and tips + let mut padding = String::new(); + for _ in full_text_len..max_undertitle_length { + padding.push_str(boundary_type::HORIZONTAL); + } + let mut ret = vec![]; + ret.append(&mut left_boundary); + ret.append(&mut first_part); + ret.append(&mut second_part); + ret.append(&mut foreground_color(&padding, self.color)); + ret.append(&mut right_boundary); + ret + } else if first_part_len <= max_undertitle_length { + // render only exit status + let mut padding = String::new(); + for _ in first_part_len..max_undertitle_length { + padding.push_str(boundary_type::HORIZONTAL); + } + let mut ret = vec![]; + ret.append(&mut left_boundary); + ret.append(&mut first_part); + ret.append(&mut foreground_color(&padding, self.color)); + ret.append(&mut right_boundary); + ret + } else { + self.empty_undertitle(max_undertitle_length) + } + } else { + if first_part_len <= max_undertitle_length { + // render first part + let full_text_len = first_part_len; + let mut padding = String::new(); + for _ in full_text_len..max_undertitle_length { + padding.push_str(boundary_type::HORIZONTAL); + } + let mut ret = vec![]; + ret.append(&mut left_boundary); + ret.append(&mut first_part); + ret.append(&mut foreground_color(&padding, self.color)); + ret.append(&mut right_boundary); + ret + } else { + self.empty_undertitle(max_undertitle_length) + } + } + } pub fn render(&self) -> (Vec<CharacterChunk>, Option<String>) { let mut character_chunks = vec![]; for row in 0..self.geom.rows { @@ -605,24 +678,30 @@ impl PaneFrame { character_chunks.push(CharacterChunk::new(title, x, y)); } else if row == self.geom.rows - 1 { // bottom row - let mut bottom_row = vec![]; - for col in 0..self.geom.cols { - let boundary = if col == 0 { - // bottom left corner - self.get_corner(boundary_type::BOTTOM_LEFT) - } else if col == self.geom.cols - 1 { - // bottom right corner - self.get_corner(boundary_type::BOTTOM_RIGHT) - } else { - boundary_type::HORIZONTAL - }; + if self.exit_status.is_some() { + let x = self.geom.x; + let y = self.geom.y + row; + character_chunks.push(CharacterChunk::new(self.render_held_undertitle(), x, y)); + } else { + let mut bottom_row = vec![]; + for col in 0..self.geom.cols { + let boundary = if col == 0 { + // bottom left corner + self.get_corner(boundary_type::BOTTOM_LEFT) + } else if col == self.geom.cols - 1 { + // bottom right corner + self.get_corner(boundary_type::BOTTOM_RIGHT) + } else { + boundary_type::HORIZONTAL + }; - let mut boundary_character = foreground_color(boundary, self.color); - bottom_row.append(&mut boundary_character); + let mut boundary_character = foreground_color(boundary, self.color); + bottom_row.append(&mut boundary_character); + } + let x = self.geom.x; + let y = self.geom.y + row; + character_chunks.push(CharacterChunk::new(bottom_row, x, y)); } - let x = self.geom.x; - let y = self.geom.y + row; - character_chunks.push(CharacterChunk::new(bottom_row, x, y)); } else { let boundary_character_left = foreground_color(boundary_type::VERTICAL, self.color); let boundary_character_right = @@ -639,4 +718,106 @@ impl PaneFrame { } (character_chunks, None) } + fn first_held_title_part_full( + &self, + exit_status: ExitStatus, + ) -> (Vec<TerminalCharacter>, usize) { + // (title part, length) + match exit_status { + ExitStatus::Code(exit_code) => { + let mut first_part = vec![]; + let left_bracket = " [ "; + let exited_text = "EXIT CODE: "; + let exit_code_text = format!("{}", exit_code); + let exit_code_color = if exit_code == 0 { + self.style.colors.green + } else { + self.style.colors.red + }; + let right_bracket = " ] "; + first_part.append(&mut foreground_color(left_bracket, self.color)); + first_part.append(&mut foreground_color(exited_text, self.color)); + first_part.append(&mut foreground_color( + &exit_code_text, + Some(exit_code_color), + )); + first_part.append(&mut foreground_color(right_bracket, self.color)); + ( + first_part, + left_bracket.len() + + exited_text.len() + + exit_code_text.len() + + right_bracket.len(), + ) + }, + ExitStatus::Exited => { + let mut first_part = vec![]; + let left_bracket = " [ "; + let exited_text = "EXITED"; + let right_bracket = " ] "; + first_part.append(&mut foreground_color(left_bracket, self.color)); + first_part.append(&mut foreground_color( + exited_text, + Some(self.style.colors.red), + )); + first_part.append(&mut foreground_color(right_bracket, self.color)); + ( + first_part, + left_bracket.len() + exited_text.len() + right_bracket.len(), + ) + }, + } + } + fn second_held_title_part_full(&self) -> (Vec<TerminalCharacter>, usize) { + // (title part, length) + let mut second_part = vec![]; + let left_enter_bracket = "<"; + let enter_text = "ENTER"; + let right_enter_bracket = ">"; + let enter_tip = " to re-run, "; + let left_break_bracket = "<"; + let break_text = "Ctrl-c"; + let right_break_bracket = ">"; + let break_tip = " to exit "; + second_part.append(&mut foreground_color(left_enter_bracket, self.color)); + second_part.append(&mut foreground_color( + enter_text, + Some(self.style.colors.orange), + )); + second_part.append(&mut foreground_color(right_enter_bracket, self.color)); + second_part.append(&mut foreground_color(enter_tip, self.color)); + second_part.append(&mut foreground_color(left_break_bracket, self.color)); + second_part.append(&mut foreground_color( + break_text, + Some(self.style.colors.orange), + )); + second_part.append(&mut foreground_color(right_break_bracket, self.color)); + second_part.append(&mut foreground_color(break_tip, self.color)); + ( + second_part, + left_enter_bracket.len() + + enter_text.len() + + right_enter_bracket.len() + + enter_tip.len() + + left_break_bracket.len() + + break_text.len() + + right_break_bracket.len() + + break_tip.len(), + ) + } + fn empty_undertitle(&self, max_undertitle_length: usize) -> Vec<TerminalCharacter> { + let mut left_boundary = + foreground_color(self.get_corner(boundary_type::BOTTOM_LEFT), self.color); + let mut right_boundary = + foreground_color(self.get_corner(boundary_type::BOTTOM_RIGHT), self.color); + let mut ret = vec![]; + let mut padding = String::new(); + for _ in 0..max_undertitle_length { + padding.push_str(boundary_type::HORIZONTAL); + } + ret.append(&mut left_boundary); + ret.append(&mut foreground_color(&padding, self.color)); + ret.append(&mut right_boundary); + ret + } } |