summaryrefslogtreecommitdiffstats
path: root/zellij-server/src/ui
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-10-11 16:45:46 +0200
committerGitHub <noreply@github.com>2022-10-11 16:45:46 +0200
commitc64bf5207a0d1b79b305f11a3d73c9da77092b6e (patch)
tree2f5c0efc7c41bc10d96b398c9861d5c7903d2ba8 /zellij-server/src/ui
parentcb926119bc5e180c78f2f754bc61532645c3b5b4 (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.rs213
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
+ }
}