diff --git a/Cargo.lock b/Cargo.lock index ea601aaf..78ef1848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1007,9 +1007,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "cc14fc54a812b4472b4113facc3e44d099fbc0ea2ce0551fa5c703f8edfbfd38" dependencies = [ "autocfg", ] @@ -1281,12 +1281,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - [[package]] name = "redox_syscall" version = "0.2.5" @@ -1302,7 +1296,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" dependencies = [ - "redox_syscall 0.2.5", + "redox_syscall", ] [[package]] @@ -1312,7 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", - "redox_syscall 0.2.5", + "redox_syscall", ] [[package]] @@ -1593,9 +1587,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" dependencies = [ "proc-macro2", "quote", @@ -1632,7 +1626,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand", - "redox_syscall 0.2.5", + "redox_syscall", "remove_dir_all", "winapi", ] @@ -1648,16 +1642,15 @@ dependencies = [ ] [[package]] -name = "termion_temporary_zellij_fork" -version = "1.6.0" +name = "termion" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65175afb01727f72d690bc8b2a2ac411c0243ec43988b7bd96d428301197bed0" +checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" dependencies = [ "libc", "numtoa", - "redox_syscall 0.1.57", + "redox_syscall", "redox_termios", - "serde", ] [[package]] @@ -2167,9 +2160,9 @@ checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf" [[package]] name = "wast" -version = "35.0.0" +version = "35.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db5ae96da18bb5926341516fd409b5a8ce4e4714da7f0a1063d3b20ac9f9a1e1" +checksum = "1a5800e9f86a1eae935e38bea11e60fd253f6d514d153fb39b3e5535a7b37b56" dependencies = [ "leb128", ] @@ -2303,8 +2296,7 @@ dependencies = [ "strip-ansi-escapes", "structopt", "strum", - "strum_macros", - "termion_temporary_zellij_fork", + "termion", "termios", "toml", "unicode-truncate", @@ -2313,13 +2305,17 @@ dependencies = [ "walkdir", "wasmer", "wasmer-wasi", - "xrdb", + "zellij-tile", ] [[package]] name = "zellij-tile" version = "0.5.0" dependencies = [ + "colors-transform", "serde", "serde_json", + "strum", + "strum_macros", + "xrdb", ] diff --git a/Cargo.toml b/Cargo.toml index 67cb4ff4..9d5696ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,20 +27,18 @@ serde_yaml = "0.8" signal-hook = "0.1.10" strip-ansi-escapes = "0.1.0" structopt = "0.3" -# termion = { git = "https://gitlab.com/TheLostLambda/termion.git", version = "1.6.0", features = ["serde"] } -termion = { package = "termion_temporary_zellij_fork" , version = "1.6.0", features = ["serde"]} +termion = "1.5.0" termios = "0.3" unicode-truncate = "0.2.0" unicode-width = "0.1.8" vte = "0.8.0" strum = "0.20.0" -strum_macros = "0.20.0" lazy_static = "1.4.0" wasmer = "1.0.0" wasmer-wasi = "1.0.0" interprocess = "1.0.1" -xrdb = "0.1.1" colors-transform = "0.2.5" +zellij-tile = { path = "zellij-tile/", version = "0.5.0" } [dependencies.async-std] version = "1.3.0" diff --git a/assets/layouts/default.yaml b/assets/layouts/default.yaml index f46569b7..408f2f38 100644 --- a/assets/layouts/default.yaml +++ b/assets/layouts/default.yaml @@ -5,8 +5,6 @@ parts: split_size: Fixed: 1 plugin: tab-bar - events: - - Tab - direction: Vertical expansion_boundary: true - direction: Vertical diff --git a/assets/layouts/strider.yaml b/assets/layouts/strider.yaml index cd972d20..9d0f09b0 100644 --- a/assets/layouts/strider.yaml +++ b/assets/layouts/strider.yaml @@ -5,8 +5,6 @@ parts: split_size: Fixed: 1 plugin: tab-bar - events: - - Tab - direction: Vertical parts: - direction: Horizontal diff --git a/build-all.sh b/build-all.sh index cdb1d0cc..f267b110 100755 --- a/build-all.sh +++ b/build-all.sh @@ -1,29 +1,25 @@ #!/bin/sh -total=6 +total=5 # This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open -echo "Building zellij-tile (1/$total)..." -cd zellij-tile -cargo build --release --target-dir ../target - -echo "Building status-bar (2/$total)..." -cd ../default-tiles/status-bar +echo "Building status-bar (1/$total)..." +cd default-tiles/status-bar cargo build --release --target-dir ../../target -echo "Building strider (3/$total)..." +echo "Building strider (2/$total)..." cd ../strider cargo build --release --target-dir ../../target -echo "Building tab-bar (4/$total)..." +echo "Building tab-bar (3/$total)..." cd ../tab-bar cargo build --release --target-dir ../../target -echo "Optimising WASM executables (5/$total)..." +echo "Optimising WASM executables (4/$total)..." cd ../.. wasm-opt -O target/wasm32-wasi/release/status-bar.wasm -o target/status-bar.wasm || cp target/wasm32-wasi/release/status-bar.wasm target/status-bar.wasm wasm-opt -O target/wasm32-wasi/release/strider.wasm -o target/strider.wasm || cp target/wasm32-wasi/release/strider.wasm target/strider.wasm wasm-opt -O target/wasm32-wasi/release/tab-bar.wasm -o target/tab-bar.wasm || cp target/wasm32-wasi/release/tab-bar.wasm target/tab-bar.wasm -echo "Building zellij (6/$total)..." +echo "Building zellij (5/$total)..." cargo build --target-dir target $@ diff --git a/default-tiles/status-bar/src/first_line.rs b/default-tiles/status-bar/src/first_line.rs index fd6ae50a..febb719a 100644 --- a/default-tiles/status-bar/src/first_line.rs +++ b/default-tiles/status-bar/src/first_line.rs @@ -1,5 +1,5 @@ use ansi_term::{ANSIStrings, Color::RGB, Style}; -use zellij_tile::*; +use zellij_tile::prelude::*; use crate::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN, RED, WHITE}; use crate::{LinePart, ARROW_SEPARATOR}; @@ -73,19 +73,19 @@ fn unselected_mode_shortcut(letter: char, text: &str, palette: Palette) -> LineP .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .bold() - .paint(format!(" <")); + .paint(" <"); let char_shortcut = Style::new() .bold() .fg(RGB(palette.red.0, palette.red.1, palette.red.2)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .bold() - .paint(format!("{}", letter)); + .paint(letter.to_string()); let char_right_separator = Style::new() .bold() .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .bold() - .paint(format!(">")); + .paint(">"); let styled_text = Style::new() .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) @@ -96,17 +96,15 @@ fn unselected_mode_shortcut(letter: char, text: &str, palette: Palette) -> LineP .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .paint(ARROW_SEPARATOR); LinePart { - part: format!( - "{}", - ANSIStrings(&[ - prefix_separator, - char_left_separator, - char_shortcut, - char_right_separator, - styled_text, - suffix_separator - ]) - ), + part: ANSIStrings(&[ + prefix_separator, + char_left_separator, + char_shortcut, + char_right_separator, + styled_text, + suffix_separator, + ]) + .to_string(), len: text.chars().count() + 6, // 2 for the arrows, 3 for the char separators, 1 for the character } } @@ -144,17 +142,15 @@ fn selected_mode_shortcut(letter: char, text: &str, palette: Palette) -> LinePar .on(RGB(palette.green.0, palette.green.1, palette.green.2)) .paint(ARROW_SEPARATOR); LinePart { - part: format!( - "{}", - ANSIStrings(&[ - prefix_separator, - char_left_separator, - char_shortcut, - char_right_separator, - styled_text, - suffix_separator - ]) - ), + part: ANSIStrings(&[ + prefix_separator, + char_left_separator, + char_shortcut, + char_right_separator, + styled_text, + suffix_separator, + ]) + .to_string(), len: text.chars().count() + 6, // 2 for the arrows, 3 for the char separators, 1 for the character } } @@ -197,10 +193,7 @@ fn selected_mode_shortcut_single_letter(letter: char, palette: Palette) -> LineP .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .paint(ARROW_SEPARATOR); LinePart { - part: format!( - "{}", - ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]) - ), + part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(), len, } } @@ -223,10 +216,7 @@ fn unselected_mode_shortcut_single_letter(letter: char, palette: Palette) -> Lin .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .paint(ARROW_SEPARATOR); LinePart { - part: format!( - "{}", - ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]) - ), + part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(), len, } } @@ -256,11 +246,13 @@ fn shortened_ctrl_key(key: &CtrlKeyShortcut, palette: Palette) -> LinePart { }; match key.mode { CtrlKeyMode::Unselected => { - unselected_mode_shortcut(letter_shortcut, &format!("{}", shortened_text), palette) - } - CtrlKeyMode::Selected => { - selected_mode_shortcut(letter_shortcut, &format!("{}", shortened_text), palette) + unselected_mode_shortcut(letter_shortcut, &shortened_text, palette) } + CtrlKeyMode::Selected => selected_mode_shortcut(letter_shortcut, &shortened_text, palette), + CtrlKeyMode::Disabled => disabled_mode_shortcut( + &format!(" <{}>{}", letter_shortcut, shortened_text), + palette, + ), CtrlKeyMode::Disabled => disabled_mode_shortcut( &format!(" <{}>{}", letter_shortcut, shortened_text), palette, @@ -317,16 +309,16 @@ pub fn superkey(palette: Palette) -> LinePart { .bold() .paint(prefix_text); LinePart { - part: format!("{}", prefix), + part: prefix.to_string(), len: prefix_text.chars().count(), } } -pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart { +pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { match &help.mode { InputMode::Locked => key_indicators( max_len, - &vec![ + &[ CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Tab), @@ -338,7 +330,7 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart { ), InputMode::Resize => key_indicators( max_len, - &vec![ + &[ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), @@ -350,7 +342,7 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart { ), InputMode::Pane => key_indicators( max_len, - &vec![ + &[ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), @@ -362,7 +354,7 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart { ), InputMode::Tab | InputMode::RenameTab => key_indicators( max_len, - &vec![ + &[ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Tab), @@ -374,7 +366,7 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart { ), InputMode::Scroll => key_indicators( max_len, - &vec![ + &[ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), @@ -384,9 +376,9 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart { ], help.palette, ), - InputMode::Normal | _ => key_indicators( + InputMode::Normal => key_indicators( max_len, - &vec![ + &[ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), diff --git a/default-tiles/status-bar/src/main.rs b/default-tiles/status-bar/src/main.rs index f1b9f47a..337c4a82 100644 --- a/default-tiles/status-bar/src/main.rs +++ b/default-tiles/status-bar/src/main.rs @@ -2,7 +2,7 @@ mod first_line; mod second_line; use std::fmt::{Display, Error, Formatter}; -use zellij_tile::*; +use zellij_tile::prelude::*; use first_line::{ctrl_keys, superkey}; use second_line::keybinds; @@ -23,7 +23,9 @@ static ARROW_SEPARATOR: &str = ""; static MORE_MSG: &str = " ... "; #[derive(Default)] -struct State {} +struct State { + mode_info: ModeInfo, +} register_tile!(State); @@ -40,23 +42,41 @@ impl Display for LinePart { } impl ZellijTile for State { - fn init(&mut self) { + fn load(&mut self) { set_selectable(false); set_invisible_borders(true); set_max_height(2); + subscribe(&[EventType::ModeUpdate]); } - fn draw(&mut self, _rows: usize, cols: usize) { - let help = get_help(); - let superkey = superkey(help.palette); - let ctrl_keys = ctrl_keys(&help, cols - superkey.len); + fn update(&mut self, event: Event) { + if let Event::ModeUpdate(mode_info) = event { + self.mode_info = mode_info; + } + } + + fn render(&mut self, _rows: usize, cols: usize) { + let superkey = superkey(self.mode_info.palette); + let ctrl_keys = ctrl_keys(&self.mode_info, cols - superkey.len); let first_line = format!("{}{}", superkey, ctrl_keys); - let second_line = keybinds(&help, cols, help.palette); + let second_line = keybinds(&self.mode_info, cols); // [48;5;238m is gray background, [0K is so that it fills the rest of the line // [48;5;16m is black background, [0K is so that it fills the rest of the line - println!("{}\u{1b}[{};{};{}m\u{1b}[0K", first_line, help.palette.bg.0, help.palette.bg.1, help.palette.bg.2); - println!("{}\u{1b}[{};{};{}m\u{1b}[0K", second_line, help.palette.bg.0, help.palette.bg.1, help.palette.bg.2); + println!( + "{}\u{1b}[{};{};{}m\u{1b}[0K", + first_line, + self.mode_info.palette.bg.0, + self.mode_info.palette.bg.1, + self.mode_info.palette.bg.2 + ); + println!( + "{}\u{1b}[{};{};{}m\u{1b}[0K", + second_line, + self.mode_info.palette.bg.0, + self.mode_info.palette.bg.1, + self.mode_info.palette.bg.2 + ); } } diff --git a/default-tiles/status-bar/src/second_line.rs b/default-tiles/status-bar/src/second_line.rs index 21d5a994..42d05c8b 100644 --- a/default-tiles/status-bar/src/second_line.rs +++ b/default-tiles/status-bar/src/second_line.rs @@ -1,6 +1,6 @@ // use colored::*; use ansi_term::{ANSIStrings, Color::RGB, Style}; -use zellij_tile::*; +use zellij_tile::prelude::*; use crate::colors::{BLACK, GREEN, ORANGE, WHITE}; use crate::{LinePart, MORE_MSG}; @@ -158,18 +158,18 @@ fn select_pane_shortcut(is_first_shortcut: bool, palette: Palette) -> LinePart { } } -fn full_shortcut_list(help: &Help, palette: Palette) -> LinePart { +fn full_shortcut_list(help: &ModeInfo) -> LinePart { match help.mode { InputMode::Normal => LinePart::default(), - InputMode::Locked => locked_interface_indication(palette), + InputMode::Locked => locked_interface_indication(help.palette), _ => { let mut line_part = LinePart::default(); for (i, (letter, description)) in help.keybinds.iter().enumerate() { - let shortcut = full_length_shortcut(i == 0, &letter, &description, palette); + let shortcut = full_length_shortcut(i == 0, &letter, &description, help.palette); line_part.len += shortcut.len; line_part.part = format!("{}{}", line_part.part, shortcut,); } - let select_pane_shortcut = select_pane_shortcut(help.keybinds.len() == 0, palette); + let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty(), help.palette); line_part.len += select_pane_shortcut.len; line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); line_part @@ -177,18 +177,18 @@ fn full_shortcut_list(help: &Help, palette: Palette) -> LinePart { } } -fn shortened_shortcut_list(help: &Help, palette: Palette) -> LinePart { +fn shortened_shortcut_list(help: &ModeInfo) -> LinePart { match help.mode { InputMode::Normal => LinePart::default(), - InputMode::Locked => locked_interface_indication(palette), + InputMode::Locked => locked_interface_indication(help.palette), _ => { let mut line_part = LinePart::default(); for (i, (letter, description)) in help.keybinds.iter().enumerate() { - let shortcut = first_word_shortcut(i == 0, &letter, &description, palette); + let shortcut = first_word_shortcut(i == 0, &letter, &description, help.palette); line_part.len += shortcut.len; line_part.part = format!("{}{}", line_part.part, shortcut,); } - let select_pane_shortcut = select_pane_shortcut(help.keybinds.len() == 0, palette); + let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty(), help.palette); line_part.len += select_pane_shortcut.len; line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); line_part @@ -196,11 +196,11 @@ fn shortened_shortcut_list(help: &Help, palette: Palette) -> LinePart { } } -fn best_effort_shortcut_list(help: &Help, max_len: usize, palette: Palette) -> LinePart { +fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart { match help.mode { InputMode::Normal => LinePart::default(), InputMode::Locked => { - let line_part = locked_interface_indication(palette); + let line_part = locked_interface_indication(help.palette); if line_part.len <= max_len { line_part } else { @@ -210,7 +210,7 @@ fn best_effort_shortcut_list(help: &Help, max_len: usize, palette: Palette) -> L _ => { let mut line_part = LinePart::default(); for (i, (letter, description)) in help.keybinds.iter().enumerate() { - let shortcut = first_word_shortcut(i == 0, &letter, &description, palette); + let shortcut = first_word_shortcut(i == 0, &letter, &description, help.palette); if line_part.len + shortcut.len + MORE_MSG.chars().count() > max_len { // TODO: better line_part.part = format!("{}{}", line_part.part, MORE_MSG); @@ -220,7 +220,7 @@ fn best_effort_shortcut_list(help: &Help, max_len: usize, palette: Palette) -> L line_part.len += shortcut.len; line_part.part = format!("{}{}", line_part.part, shortcut,); } - let select_pane_shortcut = select_pane_shortcut(help.keybinds.len() == 0, palette); + let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty(), help.palette); if line_part.len + select_pane_shortcut.len <= max_len { line_part.len += select_pane_shortcut.len; line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); @@ -230,14 +230,14 @@ fn best_effort_shortcut_list(help: &Help, max_len: usize, palette: Palette) -> L } } -pub fn keybinds(help: &Help, max_width: usize, palette: Palette) -> LinePart { - let full_shortcut_list = full_shortcut_list(help, palette); +pub fn keybinds(help: &ModeInfo, max_width: usize) -> LinePart { + let full_shortcut_list = full_shortcut_list(help); if full_shortcut_list.len <= max_width { return full_shortcut_list; } - let shortened_shortcut_list = shortened_shortcut_list(help, palette); + let shortened_shortcut_list = shortened_shortcut_list(help); if shortened_shortcut_list.len <= max_width { return shortened_shortcut_list; } - return best_effort_shortcut_list(help, max_width, palette); + best_effort_shortcut_list(help, max_width) } diff --git a/default-tiles/strider/src/main.rs b/default-tiles/strider/src/main.rs index 3213b2af..c2867fe4 100644 --- a/default-tiles/strider/src/main.rs +++ b/default-tiles/strider/src/main.rs @@ -3,16 +3,51 @@ mod state; use colored::*; use state::{FsEntry, State}; use std::{cmp::min, fs::read_dir}; -use zellij_tile::*; +use zellij_tile::prelude::*; register_tile!(State); impl ZellijTile for State { - fn init(&mut self) { + fn load(&mut self) { refresh_directory(self); + subscribe(&[EventType::KeyPress]); } - fn draw(&mut self, rows: usize, cols: usize) { + fn update(&mut self, event: Event) { + if let Event::KeyPress(key) = event { + match key { + Key::Up | Key::Char('k') => { + *self.selected_mut() = self.selected().saturating_sub(1); + } + Key::Down | Key::Char('j') => { + let next = self.selected().saturating_add(1); + *self.selected_mut() = min(self.files.len() - 1, next); + } + Key::Right | Key::Char('\n') | Key::Char('l') => { + match self.files[self.selected()].clone() { + FsEntry::Dir(p, _) => { + self.path = p; + refresh_directory(self); + } + FsEntry::File(p, _) => open_file(&p), + } + } + Key::Left | Key::Char('h') => { + self.path.pop(); + refresh_directory(self); + } + + Key::Char('.') => { + self.toggle_hidden_files(); + refresh_directory(self); + } + + _ => (), + }; + } + } + + fn render(&mut self, rows: usize, cols: usize) { for i in 0..rows { if self.selected() < self.scroll() { *self.scroll_mut() = self.selected(); @@ -38,38 +73,6 @@ impl ZellijTile for State { } } } - - fn handle_key(&mut self, key: Key) { - match key { - Key::Up | Key::Char('k') => { - *self.selected_mut() = self.selected().saturating_sub(1); - } - Key::Down | Key::Char('j') => { - let next = self.selected().saturating_add(1); - *self.selected_mut() = min(self.files.len() - 1, next); - } - Key::Right | Key::Char('\n') | Key::Char('l') => { - match self.files[self.selected()].clone() { - FsEntry::Dir(p, _) => { - self.path = p; - refresh_directory(self); - } - FsEntry::File(p, _) => open_file(&p), - } - } - Key::Left | Key::Char('h') => { - self.path.pop(); - refresh_directory(self); - } - - Key::Char('.') => { - self.toggle_hidden_files(); - refresh_directory(self); - } - - _ => (), - }; - } } fn refresh_directory(state: &mut State) { diff --git a/default-tiles/strider/src/state.rs b/default-tiles/strider/src/state.rs index 2a9230e8..0d96ae0b 100644 --- a/default-tiles/strider/src/state.rs +++ b/default-tiles/strider/src/state.rs @@ -58,6 +58,6 @@ impl FsEntry { } pub fn is_hidden_file(&self) -> bool { - self.name().chars().nth(0).unwrap() == '.'.into() + self.name().starts_with('.') } } diff --git a/default-tiles/tab-bar/src/line.rs b/default-tiles/tab-bar/src/line.rs index beca8b42..1d1c33ec 100644 --- a/default-tiles/tab-bar/src/line.rs +++ b/default-tiles/tab-bar/src/line.rs @@ -57,7 +57,7 @@ fn left_more_message(tab_count_to_the_left: usize) -> LinePart { let more_text = if tab_count_to_the_left < 10000 { format!(" ← +{} ", tab_count_to_the_left) } else { - format!(" ← +many ") + " ← +many ".to_string() }; // 238 let more_text_len = more_text.chars().count() + 2; // 2 for the arrows @@ -84,7 +84,7 @@ fn right_more_message(tab_count_to_the_right: usize) -> LinePart { let more_text = if tab_count_to_the_right < 10000 { format!(" +{} → ", tab_count_to_the_right) } else { - format!(" +many → ") + " +many → ".to_string() }; let more_text_len = more_text.chars().count() + 1; // 2 for the arrow let left_separator = Style::new().fg(GRAY).on(ORANGE).paint(ARROW_SEPARATOR); @@ -130,7 +130,7 @@ fn add_next_tabs_msg( } fn tab_line_prefix() -> LinePart { - let prefix_text = format!(" Zellij "); + let prefix_text = " Zellij ".to_string(); let prefix_text_len = prefix_text.chars().count(); let prefix_styled_text = Style::new().fg(WHITE).on(GRAY).bold().paint(prefix_text); LinePart { diff --git a/default-tiles/tab-bar/src/main.rs b/default-tiles/tab-bar/src/main.rs index 2de936b2..a43c3e59 100644 --- a/default-tiles/tab-bar/src/main.rs +++ b/default-tiles/tab-bar/src/main.rs @@ -1,7 +1,7 @@ mod line; mod tab; -use zellij_tile::*; +use zellij_tile::prelude::*; use crate::line::tab_line; use crate::tab::tab_style; @@ -12,25 +12,10 @@ pub struct LinePart { len: usize, } -#[derive(PartialEq)] -enum BarMode { - Normal, - Rename, -} - -impl Default for BarMode { - fn default() -> Self { - BarMode::Normal - } -} - #[derive(Default)] struct State { - active_tab_index: usize, - num_tabs: usize, - tabs: Vec, - mode: BarMode, - new_name: String, + tabs: Vec, + mode_info: ModeInfo, } static ARROW_SEPARATOR: &str = ""; @@ -49,18 +34,22 @@ pub mod colors { register_tile!(State); impl ZellijTile for State { - fn init(&mut self) { + fn load(&mut self) { set_selectable(false); set_invisible_borders(true); set_max_height(1); - self.active_tab_index = 0; - self.num_tabs = 0; - self.mode = BarMode::Normal; - self.new_name = String::new(); + subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]); } - fn draw(&mut self, _rows: usize, cols: usize) { - let help = get_help(); + fn update(&mut self, event: Event) { + match event { + Event::ModeUpdate(mode_info) => self.mode_info.mode = mode_info.mode, + Event::TabUpdate(tabs) => self.tabs = tabs, + _ => unimplemented!(), // FIXME: This should be unreachable, but this could be cleaner + } + } + + fn render(&mut self, _rows: usize, cols: usize) { if self.tabs.is_empty() { return; } @@ -68,17 +57,15 @@ impl ZellijTile for State { let mut active_tab_index = 0; for t in self.tabs.iter_mut() { let mut tabname = t.name.clone(); - if t.active && self.mode == BarMode::Rename { - if self.new_name.is_empty() { + if t.active && self.mode_info.mode == InputMode::RenameTab { + if tabname.is_empty() { tabname = String::from("Enter name..."); - } else { - tabname = self.new_name.clone(); } active_tab_index = t.position; } else if t.active { active_tab_index = t.position; } - let tab = tab_style(tabname, t.active, t.position, help.palette); + let tab = tab_style(tabname, t.active, t.position, self.mode_info.palette); all_tabs.push(tab); } let tab_line = tab_line(all_tabs, active_tab_index, cols); @@ -86,25 +73,12 @@ impl ZellijTile for State { for bar_part in tab_line { s = format!("{}{}", s, bar_part.part); } - println!("{}\u{1b}[{};{};{}m\u{1b}[0K", s, help.palette.bg.0, help.palette.bg.1, help.palette.bg.2); - } - - fn update_tabs(&mut self) { - self.tabs = get_tabs(); - } - - fn handle_tab_rename_keypress(&mut self, key: Key) { - self.mode = BarMode::Rename; - match key { - Key::Char('\n') | Key::Esc => { - self.mode = BarMode::Normal; - self.new_name.clear(); - } - Key::Char(c) => self.new_name = format!("{}{}", self.new_name, c), - Key::Backspace | Key::Delete => { - self.new_name.pop(); - } - _ => {} - } + println!( + "{}\u{1b}[{};{};{}m\u{1b}[0K", + s, + self.mode_info.palette.bg.0, + self.mode_info.palette.bg.1, + self.mode_info.palette.bg.2 + ); } } diff --git a/default-tiles/tab-bar/src/tab.rs b/default-tiles/tab-bar/src/tab.rs index 96654a16..375d8c46 100644 --- a/default-tiles/tab-bar/src/tab.rs +++ b/default-tiles/tab-bar/src/tab.rs @@ -1,7 +1,7 @@ use crate::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN}; use crate::{LinePart, ARROW_SEPARATOR}; use ansi_term::{ANSIStrings, Color::RGB, Style}; -use zellij_tile::Palette; +use zellij_tile::data::Palette; pub fn active_tab(text: String, palette: Palette) -> LinePart { let left_separator = Style::new() diff --git a/src/client/boundaries.rs b/src/client/boundaries.rs index e7c2cb03..eb1a5a21 100644 --- a/src/client/boundaries.rs +++ b/src/client/boundaries.rs @@ -1,9 +1,9 @@ -use crate::{common::{input::handler::Palette, colors, input::handler::InputMode}, tab::Pane}; +use crate::tab::Pane; use ansi_term::Colour::RGB; use std::collections::HashMap; +use zellij_tile::data::{colors, InputMode, Palette}; use std::fmt::{Display, Error, Formatter}; - pub mod boundary_type { pub const TOP_RIGHT: &str = "┐"; pub const VERTICAL: &str = "│"; @@ -50,7 +50,11 @@ impl Display for BoundarySymbol { match self.invisible { true => write!(f, " "), false => match self.color { - Some(color) => write!(f, "{}", RGB(color.0, color.1, color.2).paint(self.boundary_type)), + Some(color) => write!( + f, + "{}", + RGB(color.0, color.1, color.2).paint(self.boundary_type) + ), None => write!(f, "{}", self.boundary_type), }, } @@ -75,7 +79,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::VERTICAL) => { @@ -84,7 +88,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL) => { @@ -93,7 +97,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::TOP_LEFT) => { @@ -102,7 +106,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::BOTTOM_RIGHT) => { @@ -111,7 +115,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::BOTTOM_LEFT) => { @@ -120,7 +124,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::VERTICAL_LEFT) => { @@ -129,7 +133,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::VERTICAL_RIGHT) => { @@ -138,7 +142,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL_DOWN) => { @@ -147,7 +151,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL_UP) => { @@ -156,7 +160,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_RIGHT, boundary_type::CROSS) => { @@ -165,7 +169,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::HORIZONTAL) => { @@ -174,7 +178,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::VERTICAL) => { @@ -183,7 +187,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::TOP_LEFT) => { @@ -192,7 +196,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::BOTTOM_RIGHT) => { @@ -201,7 +205,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::BOTTOM_LEFT) => { @@ -210,7 +214,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::VERTICAL_LEFT) => { @@ -219,7 +223,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::VERTICAL_RIGHT) => { @@ -228,7 +232,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::HORIZONTAL_DOWN) => { @@ -237,7 +241,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::HORIZONTAL_UP) => { @@ -246,7 +250,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL, boundary_type::CROSS) => { @@ -255,7 +259,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::VERTICAL) => { @@ -264,7 +268,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::TOP_LEFT) => { @@ -273,7 +277,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::BOTTOM_RIGHT) => { @@ -282,7 +286,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::BOTTOM_LEFT) => { @@ -291,7 +295,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::VERTICAL_LEFT) => { @@ -300,7 +304,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::VERTICAL_RIGHT) => { @@ -309,7 +313,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::HORIZONTAL_DOWN) => { @@ -318,7 +322,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::HORIZONTAL_UP) => { @@ -327,7 +331,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL, boundary_type::CROSS) => { @@ -336,7 +340,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::TOP_LEFT) => { @@ -345,7 +349,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::BOTTOM_RIGHT) => { @@ -354,7 +358,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::BOTTOM_LEFT) => { @@ -363,7 +367,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::VERTICAL_LEFT) => { @@ -372,7 +376,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::VERTICAL_RIGHT) => { @@ -381,7 +385,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::HORIZONTAL_DOWN) => { @@ -390,7 +394,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::HORIZONTAL_UP) => { @@ -399,7 +403,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::TOP_LEFT, boundary_type::CROSS) => { @@ -408,7 +412,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_RIGHT, boundary_type::BOTTOM_RIGHT) => { @@ -417,7 +421,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_RIGHT, boundary_type::BOTTOM_LEFT) => { @@ -426,7 +430,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_RIGHT, boundary_type::VERTICAL_LEFT) => { @@ -435,7 +439,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_RIGHT, boundary_type::VERTICAL_RIGHT) => { @@ -444,7 +448,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_RIGHT, boundary_type::HORIZONTAL_DOWN) => { @@ -453,7 +457,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_RIGHT, boundary_type::HORIZONTAL_UP) => { @@ -462,7 +466,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_RIGHT, boundary_type::CROSS) => { @@ -471,7 +475,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_LEFT, boundary_type::BOTTOM_LEFT) => { @@ -480,7 +484,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_LEFT, boundary_type::VERTICAL_LEFT) => { @@ -489,7 +493,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_LEFT, boundary_type::VERTICAL_RIGHT) => { @@ -498,7 +502,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_LEFT, boundary_type::HORIZONTAL_DOWN) => { @@ -507,7 +511,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_LEFT, boundary_type::HORIZONTAL_UP) => { @@ -516,7 +520,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::BOTTOM_LEFT, boundary_type::CROSS) => { @@ -525,7 +529,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_LEFT, boundary_type::VERTICAL_LEFT) => { @@ -534,7 +538,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_LEFT, boundary_type::VERTICAL_RIGHT) => { @@ -543,7 +547,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_LEFT, boundary_type::HORIZONTAL_DOWN) => { @@ -552,7 +556,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_LEFT, boundary_type::HORIZONTAL_UP) => { @@ -561,7 +565,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_LEFT, boundary_type::CROSS) => { @@ -570,7 +574,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_RIGHT, boundary_type::VERTICAL_RIGHT) => { @@ -579,7 +583,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_RIGHT, boundary_type::HORIZONTAL_DOWN) => { @@ -588,7 +592,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_RIGHT, boundary_type::HORIZONTAL_UP) => { @@ -597,7 +601,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::VERTICAL_RIGHT, boundary_type::CROSS) => { @@ -606,7 +610,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL_DOWN, boundary_type::HORIZONTAL_DOWN) => { @@ -615,7 +619,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL_DOWN, boundary_type::HORIZONTAL_UP) => { @@ -624,7 +628,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL_DOWN, boundary_type::CROSS) => { @@ -633,7 +637,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL_UP, boundary_type::HORIZONTAL_UP) => { @@ -642,7 +646,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::HORIZONTAL_UP, boundary_type::CROSS) => { @@ -651,7 +655,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (boundary_type::CROSS, boundary_type::CROSS) => { @@ -660,7 +664,7 @@ fn combine_symbols( Some(BoundarySymbol { boundary_type, invisible, - color: color, + color, }) } (_, _) => None, diff --git a/src/client/layout.rs b/src/client/layout.rs index 1b383540..2d1daaf4 100644 --- a/src/client/layout.rs +++ b/src/client/layout.rs @@ -1,10 +1,8 @@ -use crate::utils::consts::ZELLIJ_ROOT_LAYOUT_DIR; use directories_next::ProjectDirs; use serde::{Deserialize, Serialize}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::{fs::File, io::prelude::*}; -use crate::common::wasm_vm::EventType; use crate::panes::PositionAndSize; fn split_space_to_parts_vertically( @@ -181,19 +179,15 @@ pub struct Layout { pub plugin: Option, #[serde(default)] pub expansion_boundary: bool, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec, } impl Layout { pub fn new(layout_path: PathBuf) -> Self { let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); let layout_dir = project_dirs.data_dir().join("layouts/"); - let root_layout_dir = Path::new(ZELLIJ_ROOT_LAYOUT_DIR); let mut layout_file = File::open(&layout_path) .or_else(|_| File::open(&layout_path.with_extension("yaml"))) .or_else(|_| File::open(&layout_dir.join(&layout_path).with_extension("yaml"))) - .or_else(|_| File::open(root_layout_dir.join(&layout_path).with_extension("yaml"))) .unwrap_or_else(|_| panic!("cannot find layout {}", &layout_path.display())); let mut layout = String::new(); diff --git a/src/client/panes/plugin_pane.rs b/src/client/panes/plugin_pane.rs index 588cffd6..4cffff5f 100644 --- a/src/client/panes/plugin_pane.rs +++ b/src/client/panes/plugin_pane.rs @@ -120,7 +120,7 @@ impl Pane for PluginPane { let (buf_tx, buf_rx) = channel(); self.send_plugin_instructions - .send(PluginInstruction::Draw( + .send(PluginInstruction::Render( buf_tx, self.pid, self.rows(), diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs index 80c3162b..053ebd3f 100644 --- a/src/client/panes/terminal_character.rs +++ b/src/client/panes/terminal_character.rs @@ -23,7 +23,7 @@ pub enum AnsiCode { On, Reset, NamedColor(NamedColor), - RGBCode((u8, u8, u8)), + RgbCode((u8, u8, u8)), ColorIndex(u8), } @@ -336,7 +336,7 @@ impl CharacterStyles { [36, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))), [37, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))), [38, 2, ..] => { - let ansi_code = AnsiCode::RGBCode(( + let ansi_code = AnsiCode::RgbCode(( *ansi_params.get(2).unwrap() as u8, *ansi_params.get(3).unwrap() as u8, *ansi_params.get(4).unwrap() as u8, @@ -364,7 +364,7 @@ impl CharacterStyles { [46, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))), [47, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))), [48, 2, ..] => { - let ansi_code = AnsiCode::RGBCode(( + let ansi_code = AnsiCode::RgbCode(( *ansi_params.get(2).unwrap() as u8, *ansi_params.get(3).unwrap() as u8, *ansi_params.get(4).unwrap() as u8, @@ -416,7 +416,7 @@ impl Display for CharacterStyles { } if let Some(ansi_code) = self.foreground { match ansi_code { - AnsiCode::RGBCode((r, g, b)) => { + AnsiCode::RgbCode((r, g, b)) => { write!(f, "\u{1b}[38;2;{};{};{}m", r, g, b)?; } AnsiCode::ColorIndex(color_index) => { @@ -433,7 +433,7 @@ impl Display for CharacterStyles { }; if let Some(ansi_code) = self.background { match ansi_code { - AnsiCode::RGBCode((r, g, b)) => { + AnsiCode::RgbCode((r, g, b)) => { write!(f, "\u{1b}[48;2;{};{};{}m", r, g, b)?; } AnsiCode::ColorIndex(color_index) => { diff --git a/src/client/tab.rs b/src/client/tab.rs index 56970851..d5a190f4 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -1,23 +1,20 @@ //! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size, //! as well as how they should be resized -use crate::common::{AppInstruction, SenderWithContext, input::handler::{Palette, InputMode}}; +use crate::common::{input::handler::parse_keys, AppInstruction, SenderWithContext}; use crate::layout::Layout; use crate::panes::{PaneId, PositionAndSize, TerminalPane}; use crate::pty_bus::{PtyInstruction, VteEvent}; -use crate::wasm_vm::{PluginInputType, PluginInstruction}; -use crate::{ - boundaries::Boundaries, - panes::PluginPane, -}; +use crate::wasm_vm::PluginInstruction; +use crate::{boundaries::Boundaries, panes::PluginPane}; use crate::{os_input_output::OsApi, utils::shared::pad_to_size}; -use serde::{Deserialize, Serialize}; use std::os::unix::io::RawFd; use std::{ cmp::Reverse, collections::{BTreeMap, HashSet}, }; use std::{io::Write, sync::mpsc::channel}; +use zellij_tile::data::{colors, Event, InputMode, Palette}; const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this const MIN_TERMINAL_HEIGHT: usize = 2; @@ -69,15 +66,7 @@ pub struct Tab { pub send_app_instructions: SenderWithContext, expansion_boundary: Option, pub input_mode: InputMode, - pub colors: Palette -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct TabData { - /* subset of fields to publish to plugins */ - pub position: usize, - pub name: String, - pub active: bool, + pub colors: Palette, } // FIXME: Use a struct that has a pane_type enum, to reduce all of the duplication @@ -195,7 +184,7 @@ impl Tab { max_panes: Option, pane_id: Option, input_mode: InputMode, - colors: Palette + colors: Palette, ) -> Self { let panes = if let Some(PaneId::Terminal(pid)) = pane_id { let new_terminal = TerminalPane::new(pid, *full_screen_ws); @@ -226,7 +215,7 @@ impl Tab { send_plugin_instructions, expansion_boundary: None, input_mode, - colors + colors, } } @@ -271,11 +260,7 @@ impl Tab { if let Some(plugin) = &layout.plugin { let (pid_tx, pid_rx) = channel(); self.send_plugin_instructions - .send(PluginInstruction::Load( - pid_tx, - plugin.clone(), - layout.events.clone(), - )) + .send(PluginInstruction::Load(pid_tx, plugin.clone())) .unwrap(); let pid = pid_rx.recv().unwrap(); let new_plugin = PluginPane::new( @@ -314,7 +299,6 @@ impl Tab { self.toggle_active_pane_fullscreen(); } if !self.has_panes() { - // FIXME: This could use a second look if let PaneId::Terminal(term_pid) = pid { let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws); self.os_api.set_terminal_size_using_fd( @@ -365,7 +349,6 @@ impl Tab { if terminal_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.columns() && terminal_to_split.rows() > terminal_to_split.min_height() * 2 { - // FIXME: This could use a second look if let PaneId::Terminal(term_pid) = pid { let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws); let new_terminal = TerminalPane::new(term_pid, bottom_winsize); @@ -386,7 +369,6 @@ impl Tab { self.active_terminal = Some(pid); } } else if terminal_to_split.columns() > terminal_to_split.min_width() * 2 { - // FIXME: This could use a second look if let PaneId::Terminal(term_pid) = pid { let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws); let new_terminal = TerminalPane::new(term_pid, right_winsize); @@ -416,7 +398,6 @@ impl Tab { self.toggle_active_pane_fullscreen(); } if !self.has_panes() { - // FIXME: This could use a second look if let PaneId::Terminal(term_pid) = pid { let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws); self.os_api.set_terminal_size_using_fd( @@ -427,47 +408,44 @@ impl Tab { self.panes.insert(pid, Box::new(new_terminal)); self.active_terminal = Some(pid); } - } else { - // FIXME: This could use a second look - if let PaneId::Terminal(term_pid) = pid { - // TODO: check minimum size of active terminal - let active_pane_id = &self.get_active_pane_id().unwrap(); - let active_pane = self.panes.get_mut(active_pane_id).unwrap(); - if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 + 1 { - self.send_pty_instructions - .send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty - .unwrap(); - return; - } - let terminal_ws = PositionAndSize { - x: active_pane.x(), - y: active_pane.y(), - rows: active_pane.rows(), - columns: active_pane.columns(), - }; - let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws); - - active_pane.change_pos_and_size(&top_winsize); - - let new_terminal = TerminalPane::new(term_pid, bottom_winsize); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - bottom_winsize.columns as u16, - bottom_winsize.rows as u16, - ); - self.panes.insert(pid, Box::new(new_terminal)); - - if let PaneId::Terminal(active_terminal_pid) = active_pane_id { - self.os_api.set_terminal_size_using_fd( - *active_terminal_pid, - top_winsize.columns as u16, - top_winsize.rows as u16, - ); - } - - self.active_terminal = Some(pid); - self.render(); + } else if let PaneId::Terminal(term_pid) = pid { + // TODO: check minimum size of active terminal + let active_pane_id = &self.get_active_pane_id().unwrap(); + let active_pane = self.panes.get_mut(active_pane_id).unwrap(); + if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 + 1 { + self.send_pty_instructions + .send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty + .unwrap(); + return; } + let terminal_ws = PositionAndSize { + x: active_pane.x(), + y: active_pane.y(), + rows: active_pane.rows(), + columns: active_pane.columns(), + }; + let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws); + + active_pane.change_pos_and_size(&top_winsize); + + let new_terminal = TerminalPane::new(term_pid, bottom_winsize); + self.os_api.set_terminal_size_using_fd( + new_terminal.pid, + bottom_winsize.columns as u16, + bottom_winsize.rows as u16, + ); + self.panes.insert(pid, Box::new(new_terminal)); + + if let PaneId::Terminal(active_terminal_pid) = active_pane_id { + self.os_api.set_terminal_size_using_fd( + *active_terminal_pid, + top_winsize.columns as u16, + top_winsize.rows as u16, + ); + } + + self.active_terminal = Some(pid); + self.render(); } } pub fn vertical_split(&mut self, pid: PaneId) { @@ -476,7 +454,6 @@ impl Tab { self.toggle_active_pane_fullscreen(); } if !self.has_panes() { - // FIXME: This could use a second look if let PaneId::Terminal(term_pid) = pid { let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws); self.os_api.set_terminal_size_using_fd( @@ -487,47 +464,44 @@ impl Tab { self.panes.insert(pid, Box::new(new_terminal)); self.active_terminal = Some(pid); } - } else { - // FIXME: This could use a second look - if let PaneId::Terminal(term_pid) = pid { - // TODO: check minimum size of active terminal - let active_pane_id = &self.get_active_pane_id().unwrap(); - let active_pane = self.panes.get_mut(active_pane_id).unwrap(); - if active_pane.columns() < MIN_TERMINAL_WIDTH * 2 + 1 { - self.send_pty_instructions - .send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty - .unwrap(); - return; - } - let terminal_ws = PositionAndSize { - x: active_pane.x(), - y: active_pane.y(), - rows: active_pane.rows(), - columns: active_pane.columns(), - }; - let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws); - - active_pane.change_pos_and_size(&left_winsize); - - let new_terminal = TerminalPane::new(term_pid, right_winsize); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - right_winsize.columns as u16, - right_winsize.rows as u16, - ); - self.panes.insert(pid, Box::new(new_terminal)); - - if let PaneId::Terminal(active_terminal_pid) = active_pane_id { - self.os_api.set_terminal_size_using_fd( - *active_terminal_pid, - left_winsize.columns as u16, - left_winsize.rows as u16, - ); - } - - self.active_terminal = Some(pid); - self.render(); + } else if let PaneId::Terminal(term_pid) = pid { + // TODO: check minimum size of active terminal + let active_pane_id = &self.get_active_pane_id().unwrap(); + let active_pane = self.panes.get_mut(active_pane_id).unwrap(); + if active_pane.columns() < MIN_TERMINAL_WIDTH * 2 + 1 { + self.send_pty_instructions + .send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty + .unwrap(); + return; } + let terminal_ws = PositionAndSize { + x: active_pane.x(), + y: active_pane.y(), + rows: active_pane.rows(), + columns: active_pane.columns(), + }; + let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws); + + active_pane.change_pos_and_size(&left_winsize); + + let new_terminal = TerminalPane::new(term_pid, right_winsize); + self.os_api.set_terminal_size_using_fd( + new_terminal.pid, + right_winsize.columns as u16, + right_winsize.rows as u16, + ); + self.panes.insert(pid, Box::new(new_terminal)); + + if let PaneId::Terminal(active_terminal_pid) = active_pane_id { + self.os_api.set_terminal_size_using_fd( + *active_terminal_pid, + left_winsize.columns as u16, + left_winsize.rows as u16, + ); + } + + self.active_terminal = Some(pid); + self.render(); } } pub fn get_active_pane(&self) -> Option<&dyn Pane> { @@ -571,12 +545,11 @@ impl Tab { .expect("failed to drain terminal"); } Some(PaneId::Plugin(pid)) => { - self.send_plugin_instructions - .send(PluginInstruction::Input( - PluginInputType::Normal(pid), - input_bytes, - )) - .unwrap(); + for key in parse_keys(&input_bytes) { + self.send_plugin_instructions + .send(PluginInstruction::Update(Some(pid), Event::KeyPress(key))) + .unwrap() + } } _ => {} } @@ -1694,6 +1667,7 @@ impl Tab { self.reduce_pane_and_surroundings_right(&active_pane_id, count); } } + self.render(); } pub fn resize_left(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much @@ -1705,6 +1679,7 @@ impl Tab { self.reduce_pane_and_surroundings_left(&active_pane_id, count); } } + self.render(); } pub fn resize_down(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much @@ -1716,6 +1691,7 @@ impl Tab { self.reduce_pane_and_surroundings_down(&active_pane_id, count); } } + self.render(); } pub fn resize_up(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much @@ -1727,6 +1703,7 @@ impl Tab { self.reduce_pane_and_surroundings_up(&active_pane_id, count); } } + self.render(); } pub fn move_focus(&mut self) { if !self.has_selectable_panes() { diff --git a/src/common/errors.rs b/src/common/errors.rs index eeebf93f..f5aeae85 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -133,7 +133,7 @@ pub enum ContextType { Plugin(PluginContext), /// An app-related call. App(AppContext), - IPCServer, + IpcServer, StdinHandler, AsyncTask, /// An empty, placeholder call. This should be thought of as representing no call at all. @@ -152,7 +152,7 @@ impl Display for ContextType { ContextType::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c), ContextType::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c), - ContextType::IPCServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green), + ContextType::IpcServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green), ContextType::StdinHandler => { write!(f, "{}stdin_handler_thread: {}AcceptInput", purple, green) } @@ -164,6 +164,7 @@ impl Display for ContextType { } } +// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!! /// Stack call representations corresponding to the different types of [`ScreenInstruction`]s. #[derive(Debug, Clone, Copy, PartialEq)] pub enum ScreenContext { @@ -202,6 +203,7 @@ pub enum ScreenContext { ChangeInputMode, } +// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!! impl From<&ScreenInstruction> for ScreenContext { fn from(screen_instruction: &ScreenInstruction) -> Self { match *screen_instruction { @@ -278,24 +280,20 @@ use crate::wasm_vm::PluginInstruction; #[derive(Debug, Clone, Copy, PartialEq)] pub enum PluginContext { Load, - Draw, - Input, - GlobalInput, + Update, + Render, Unload, Quit, - Tabs, } impl From<&PluginInstruction> for PluginContext { fn from(plugin_instruction: &PluginInstruction) -> Self { match *plugin_instruction { PluginInstruction::Load(..) => PluginContext::Load, - PluginInstruction::Draw(..) => PluginContext::Draw, - PluginInstruction::Input(..) => PluginContext::Input, - PluginInstruction::GlobalInput(_) => PluginContext::GlobalInput, + PluginInstruction::Update(..) => PluginContext::Update, + PluginInstruction::Render(..) => PluginContext::Render, PluginInstruction::Unload(_) => PluginContext::Unload, PluginInstruction::Quit => PluginContext::Quit, - PluginInstruction::UpdateTabs(..) => PluginContext::Tabs, } } } @@ -303,8 +301,6 @@ impl From<&PluginInstruction> for PluginContext { /// Stack call representations corresponding to the different types of [`AppInstruction`]s. #[derive(Debug, Clone, Copy, PartialEq)] pub enum AppContext { - GetState, - SetState, Exit, Error, } @@ -312,8 +308,6 @@ pub enum AppContext { impl From<&AppInstruction> for AppContext { fn from(app_instruction: &AppInstruction) -> Self { match *app_instruction { - AppInstruction::GetState(_) => AppContext::GetState, - AppInstruction::SetState(_) => AppContext::SetState, AppInstruction::Exit => AppContext::Exit, AppInstruction::Error(_) => AppContext::Error, } diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs index 88a7036f..f4226864 100644 --- a/src/common/input/actions.rs +++ b/src/common/input/actions.rs @@ -1,6 +1,6 @@ //! Definition of the actions that can be bound to keys. -use super::handler; +use zellij_tile::data::InputMode; /// The four directions (left, right, up, down). #[derive(Clone, Debug)] @@ -19,7 +19,7 @@ pub enum Action { /// Write to the terminal. Write(Vec), /// Switch to the specified input mode. - SwitchToMode(handler::InputMode), + SwitchToMode(InputMode), /// Resize focus pane in specified direction. Resize(Direction), /// Switch focus to next pane in specified direction. @@ -49,5 +49,4 @@ pub enum Action { CloseTab, GoToTab(u32), TabNameInput(Vec), - SaveTabName, } diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index 4f77c0ae..b135ea16 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -2,20 +2,16 @@ use super::actions::Action; use super::keybinds::get_default_keybinds; -use crate::common::{update_state, AppInstruction, AppState, SenderWithContext, OPENCALLS}; +use crate::common::{AppInstruction, SenderWithContext, OPENCALLS}; use crate::errors::ContextType; use crate::os_input_output::OsApi; use crate::pty_bus::PtyInstruction; use crate::screen::ScreenInstruction; -use crate::wasm_vm::{EventType, PluginInputType, PluginInstruction}; +use crate::wasm_vm::PluginInstruction; use crate::CommandIsExecuting; -use xrdb::Colors; -use colors_transform::{Rgb, Color}; -use crate::common::utils::logging::debug_log_to_file; -use serde::{Deserialize, Serialize}; -use strum_macros::EnumIter; -use termion::input::TermReadEventsAndRaw; +use termion::input::{TermRead, TermReadEventsAndRaw}; +use zellij_tile::data::{Event, InputMode, Key, ModeInfo, Palette}; use super::keybinds::key_to_actions; @@ -65,27 +61,21 @@ impl InputHandler { 'input_loop: loop { //@@@ I think this should actually just iterate over stdin directly let stdin_buffer = self.os_input.read_from_stdin(); - drop( - self.send_plugin_instructions - .send(PluginInstruction::GlobalInput(stdin_buffer.clone())), - ); for key_result in stdin_buffer.events_and_raw() { match key_result { Ok((event, raw_bytes)) => match event { termion::event::Event::Key(key) => { + let key = cast_termion_key(key); // FIXME this explicit break is needed because the current test // framework relies on it to not create dead threads that loop // and eat up CPUs. Do not remove until the test framework has // been revised. Sorry about this (@categorille) - if { - let mut should_break = false; - for action in - key_to_actions(&key, raw_bytes, &self.mode, &keybinds) - { - should_break |= self.dispatch_action(action); - } - should_break - } { + let mut should_break = false; + for action in key_to_actions(&key, raw_bytes, &self.mode, &keybinds) + { + should_break |= self.dispatch_action(action); + } + if should_break { break 'input_loop; } } @@ -133,11 +123,14 @@ impl InputHandler { } Action::SwitchToMode(mode) => { self.mode = mode; - update_state(&self.send_app_instructions, |_| AppState { - input_mode: self.mode, - }); + self.send_plugin_instructions + .send(PluginInstruction::Update( + None, + Event::ModeUpdate(get_mode_info(mode)), + )) + .unwrap(); self.send_screen_instructions - .send(ScreenInstruction::ChangeInputMode(self.mode)) + .send(ScreenInstruction::ChangeInputMode(mode)) .unwrap(); self.send_screen_instructions .send(ScreenInstruction::Render) @@ -239,27 +232,10 @@ impl InputHandler { .unwrap(); } Action::TabNameInput(c) => { - self.send_plugin_instructions - .send(PluginInstruction::Input( - PluginInputType::Event(EventType::Tab), - c.clone(), - )) - .unwrap(); self.send_screen_instructions .send(ScreenInstruction::UpdateTabName(c)) .unwrap(); } - Action::SaveTabName => { - self.send_plugin_instructions - .send(PluginInstruction::Input( - PluginInputType::Event(EventType::Tab), - vec![b'\n'], - )) - .unwrap(); - self.send_screen_instructions - .send(ScreenInstruction::UpdateTabName(vec![b'\n'])) - .unwrap(); - } Action::NoOp => {} } @@ -275,123 +251,10 @@ impl InputHandler { } } -/// Describes the different input modes, which change the way that keystrokes will be interpreted. -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, EnumIter, Serialize, Deserialize)] -pub enum InputMode { - /// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading - /// to other modes - Normal, - /// In `Locked` mode, input is always written to the terminal and all shortcuts are disabled - /// except the one leading back to normal mode - Locked, - /// `Resize` mode allows resizing the different existing panes. - Resize, - /// `Pane` mode allows creating and closing panes, as well as moving between them. - Pane, - /// `Tab` mode allows creating and closing tabs, as well as moving between them. - Tab, - /// `Scroll` mode allows scrolling up and down within a pane. - Scroll, - RenameTab, -} - -/// Represents the contents of the help message that is printed in the status bar, -/// which indicates the current [`InputMode`] and what the keybinds for that mode -/// are. Related to the default `status-bar` plugin. -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct Help { - pub mode: InputMode, - pub keybinds: Vec<(String, String)>, // => - pub palette: Palette -} - -impl Default for InputMode { - fn default() -> InputMode { - InputMode::Normal - } -} - -pub mod colors { - pub const WHITE: (u8, u8, u8) = (238, 238, 238); - pub const GREEN: (u8, u8, u8) = (175, 255, 0); - pub const GRAY: (u8, u8, u8) = (68, 68, 68); - pub const BRIGHT_GRAY: (u8, u8, u8) = (138, 138, 138); - pub const RED: (u8, u8, u8) = (135, 0, 0); - pub const BLACK: (u8, u8, u8) = (0, 0, 0); -} - -#[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)] -pub struct Palette { - pub fg: (u8, u8, u8), - pub bg: (u8, u8, u8), - pub black: (u8, u8, u8), - pub red: (u8, u8, u8), - pub green: (u8, u8, u8), - pub yellow: (u8, u8, u8), - pub blue: (u8, u8, u8), - pub magenta: (u8, u8, u8), - pub cyan: (u8, u8, u8), - pub white: (u8, u8, u8), -} - -impl Palette { - pub fn new() -> Self { - let palette = match Colors::new("xresources") { - Some(colors) => { - let fg = colors.fg.unwrap(); - let fg_imm = &fg; - let fg_hex: &str = &fg_imm; - let fg = Rgb::from_hex_str(fg_hex).unwrap().as_tuple(); - let fg = (fg.0 as u8, fg.1 as u8, fg.2 as u8); - let bg = colors.bg.unwrap(); - let bg_imm = &bg; - let bg_hex: &str = &bg_imm; - let bg = Rgb::from_hex_str(bg_hex).unwrap().as_tuple(); - let bg = (bg.0 as u8, bg.1 as u8, bg.2 as u8); - let colors: Vec<(u8, u8, u8)> = colors.colors.iter().map(|c| { - let c = c.clone(); - let imm_str = &c.unwrap(); - let hex_str: &str = &imm_str; - let rgb = Rgb::from_hex_str(hex_str).unwrap().as_tuple(); - (rgb.0 as u8, rgb.1 as u8, rgb.2 as u8) - }).collect(); - Self { - fg, - bg, - black: colors[0], - red: colors[1], - green: colors[2], - yellow: colors[3], - blue: colors[4], - magenta: colors[5], - cyan: colors[6], - white: colors[7] - } - }, - None => { - Self { - fg: colors::BRIGHT_GRAY, - bg: colors::BLACK, - black: colors::BLACK, - red: colors::RED, - green: colors::GREEN, - yellow: colors::GRAY, - blue: colors::GRAY, - magenta: colors::GRAY, - cyan: colors::GRAY, - white: colors::WHITE - } - } - }; - palette - } -} - - /// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds /// (as pairs of [`String`]s). // TODO this should probably be automatically generated in some way -pub fn get_help(mode: InputMode) -> Help { +pub fn get_mode_info(mode: InputMode) -> ModeInfo { let mut keybinds: Vec<(String, String)> = vec![]; let mut palette = Palette::new(); match mode { @@ -421,7 +284,11 @@ pub fn get_help(mode: InputMode) -> Help { keybinds.push(("Enter".to_string(), "when done".to_string())); } } - Help { mode, keybinds, palette } + ModeInfo { + mode, + keybinds, + palette, + } } /// Entry point to the module. Instantiates an [`InputHandler`] and starts @@ -444,3 +311,35 @@ pub fn input_loop( ) .handle_input(); } + +pub fn parse_keys(input_bytes: &[u8]) -> Vec { + input_bytes.keys().flatten().map(cast_termion_key).collect() +} + +// FIXME: This is an absolutely cursed function that should be destroyed as soon +// as an alternative that doesn't touch zellij-tile can be developed... +fn cast_termion_key(event: termion::event::Key) -> Key { + match event { + termion::event::Key::Backspace => Key::Backspace, + termion::event::Key::Left => Key::Left, + termion::event::Key::Right => Key::Right, + termion::event::Key::Up => Key::Up, + termion::event::Key::Down => Key::Down, + termion::event::Key::Home => Key::Home, + termion::event::Key::End => Key::End, + termion::event::Key::PageUp => Key::PageUp, + termion::event::Key::PageDown => Key::PageDown, + termion::event::Key::BackTab => Key::BackTab, + termion::event::Key::Delete => Key::Delete, + termion::event::Key::Insert => Key::Insert, + termion::event::Key::F(n) => Key::F(n), + termion::event::Key::Char(c) => Key::Char(c), + termion::event::Key::Alt(c) => Key::Alt(c), + termion::event::Key::Ctrl(c) => Key::Ctrl(c), + termion::event::Key::Null => Key::Null, + termion::event::Key::Esc => Key::Esc, + _ => { + unimplemented!("Encountered an unknown key!") + } + } +} diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs index 4b676037..7b1e8779 100644 --- a/src/common/input/keybinds.rs +++ b/src/common/input/keybinds.rs @@ -1,12 +1,11 @@ //! Mapping of inputs to sequences of actions. use super::actions::{Action, Direction}; -use super::handler::InputMode; use std::collections::HashMap; use strum::IntoEnumIterator; -use termion::event::Key; +use zellij_tile::data::*; type Keybinds = HashMap; type ModeKeybinds = HashMap>; @@ -17,14 +16,14 @@ pub fn get_default_keybinds() -> Result { let mut defaults = Keybinds::new(); for mode in InputMode::iter() { - defaults.insert(mode, get_defaults_for_mode(&mode)?); + defaults.insert(mode, get_defaults_for_mode(&mode)); } Ok(defaults) } /// Returns the default keybinds for a givent [`InputMode`]. -fn get_defaults_for_mode(mode: &InputMode) -> Result { +fn get_defaults_for_mode(mode: &InputMode) -> ModeKeybinds { let mut defaults = ModeKeybinds::new(); match *mode { @@ -227,10 +226,7 @@ fn get_defaults_for_mode(mode: &InputMode) -> Result { defaults.insert(Key::Up, vec![Action::ScrollUp]); } InputMode::RenameTab => { - defaults.insert( - Key::Char('\n'), - vec![Action::SaveTabName, Action::SwitchToMode(InputMode::Tab)], - ); + defaults.insert(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]); defaults.insert( Key::Ctrl('g'), vec![Action::SwitchToMode(InputMode::Normal)], @@ -245,7 +241,7 @@ fn get_defaults_for_mode(mode: &InputMode) -> Result { } } - Ok(defaults) + defaults } /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current diff --git a/src/common/ipc.rs b/src/common/ipc.rs index 77d57df3..81576b86 100644 --- a/src/common/ipc.rs +++ b/src/common/ipc.rs @@ -3,12 +3,12 @@ use serde::{Deserialize, Serialize}; use std::collections::HashSet; -type SessionID = u64; +type SessionId = u64; #[derive(PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct Session { // Unique ID for this session - id: SessionID, + id: SessionId, // Identifier for the underlying IPC primitive (socket, pipe) conn_name: String, // User configured alias for the session @@ -30,9 +30,9 @@ pub enum ClientToServerMsg { // Create a new session CreateSession, // Attach to a running session - AttachToSession(SessionID, ClientType), + AttachToSession(SessionId, ClientType), // Force detach - DetachSession(SessionID), + DetachSession(SessionId), // Disconnect from the session we're connected to DisconnectFromSession, } diff --git a/src/common/mod.rs b/src/common/mod.rs index 2bbc18bd..6732619e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -9,38 +9,35 @@ pub mod screen; pub mod utils; pub mod wasm_vm; -use std::io::Write; -use std::path::{Path, PathBuf}; +use std::cell::RefCell; +use std::path::PathBuf; use std::sync::mpsc; use std::thread; -use std::{cell::RefCell, sync::mpsc::TrySendError}; use std::{collections::HashMap, fs}; -use xrdb::Colors; -use colors_transform::{Rgb, Color}; -use ansi_term::Colour::RGB; - -use crate::panes::PaneId; -use directories_next::ProjectDirs; -use input::handler::InputMode; -use serde::{Deserialize, Serialize}; -use termion::input::TermRead; -use wasm_vm::PluginEnv; -use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; -use wasmer_wasi::{Pipe, WasiState}; +use std::{ + collections::HashSet, + io::Write, + str::FromStr, + sync::{Arc, Mutex}, +}; use crate::cli::CliArgs; use crate::layout::Layout; -use crate::common::input::handler::Palette; +use crate::panes::PaneId; use command_is_executing::CommandIsExecuting; +use directories_next::ProjectDirs; use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext}; use input::handler::input_loop; use os_input_output::OsApi; use pty_bus::{PtyBus, PtyInstruction}; use screen::{Screen, ScreenInstruction}; -use utils::{consts::{ZELLIJ_IPC_PIPE, ZELLIJ_ROOT_PLUGIN_DIR}, logging::debug_log_to_file}; -use wasm_vm::{ - wasi_stdout, wasi_write_string, zellij_imports, EventType, PluginInputType, PluginInstruction, -}; +use serde::{Deserialize, Serialize}; +use utils::consts::ZELLIJ_IPC_PIPE; +use wasm_vm::PluginEnv; +use wasm_vm::{wasi_stdout, wasi_write_string, zellij_imports, PluginInstruction}; +use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; +use wasmer_wasi::{Pipe, WasiState}; +use zellij_tile::data::{EventType, InputMode, Palette}; #[derive(Serialize, Deserialize, Debug)] pub enum ApiCommand { @@ -49,25 +46,6 @@ pub enum ApiCommand { SplitVertically, MoveFocus, } -// FIXME: It would be good to add some more things to this over time -#[derive(Debug, Clone, Default)] -pub struct AppState { - pub input_mode: InputMode, -} - -// FIXME: Make this a method on the big `Communication` struct, so that app_tx can be extracted -// from self instead of being explicitly passed here -pub fn update_state( - app_tx: &SenderWithContext, - update_fn: impl FnOnce(AppState) -> AppState, -) { - let (state_tx, state_rx) = mpsc::channel(); - - drop(app_tx.send(AppInstruction::GetState(state_tx))); - let state = state_rx.recv().unwrap(); - - drop(app_tx.send(AppInstruction::SetState(update_fn(state)))) -} /// An [MPSC](mpsc) asynchronous channel with added error context. pub type ChannelWithContext = ( @@ -111,19 +89,6 @@ impl SenderWithContext { } } - /// Attempts to send an event on this sender's channel, terminating instead of blocking - /// if the event could not be sent (buffer full or connection closed). - /// - /// This can only be called on [`SyncSender`](SenderType::SyncSender)s, and will - /// panic if called on an asynchronous [`Sender`](SenderType::Sender). - pub fn try_send(&self, event: T) -> Result<(), TrySendError<(T, ErrorContext)>> { - if let SenderType::SyncSender(ref s) = self.sender { - s.try_send((event, self.err_ctx)) - } else { - panic!("try_send can only be called on SyncSenders!") - } - } - /// Updates this [`SenderWithContext`]'s [`ErrorContext`]. This is the way one adds /// a call to the error context. /// @@ -146,19 +111,10 @@ thread_local!( /// Instructions related to the entire application. #[derive(Clone)] pub enum AppInstruction { - GetState(mpsc::Sender), - SetState(AppState), Exit, Error(String), } -pub mod colors { - pub const WHITE: (u8, u8, u8) = (238, 238, 238); - pub const GREEN: (u8, u8, u8) = (175, 255, 0); - pub const GRAY: (u8, u8, u8) = (68, 68, 68); -} - - /// Start Zellij with the specified [`OsApi`] and command-line arguments. // FIXME this should definitely be modularized and split into different functions. pub fn start(mut os_input: Box, opts: CliArgs) { @@ -168,7 +124,6 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .get_stdout_writer() .write(take_snapshot.as_bytes()) .unwrap(); - let mut app_state = AppState::default(); let command_is_executing = CommandIsExecuting::new(); @@ -303,7 +258,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { os_input, max_panes, InputMode::Normal, - colors + colors, ); loop { let (event, mut err_ctx) = screen @@ -395,8 +350,6 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .get_active_tab_mut() .unwrap() .set_pane_selectable(id, selectable); - // FIXME: Is this needed? - screen.render(); } ScreenInstruction::SetMaxHeight(id, max_height) => { screen @@ -460,12 +413,6 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let store = Store::default(); let mut plugin_id = 0; let mut plugin_map = HashMap::new(); - let handler_map: HashMap = - [(EventType::Tab, "handle_tab_rename_keypress".to_string())] - .iter() - .cloned() - .collect(); - move || loop { let (event, mut err_ctx) = receive_plugin_instructions .recv() @@ -475,18 +422,13 @@ pub fn start(mut os_input: Box, opts: CliArgs) { send_pty_instructions.update(err_ctx); send_app_instructions.update(err_ctx); match event { - PluginInstruction::Load(pid_tx, path, events) => { + PluginInstruction::Load(pid_tx, path) => { let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); let plugin_dir = project_dirs.data_dir().join("plugins/"); - // FIXME: This really shouldn't need to exist anymore, let's get rid of it! - let root_plugin_dir = Path::new(ZELLIJ_ROOT_PLUGIN_DIR); let wasm_bytes = fs::read(&path) .or_else(|_| fs::read(&path.with_extension("wasm"))) .or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm"))) - .or_else(|_| { - fs::read(&root_plugin_dir.join(&path).with_extension("wasm")) - }) .unwrap_or_else(|_| panic!("cannot find plugin {}", &path.display())); // FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that @@ -517,7 +459,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { send_screen_instructions: send_screen_instructions.clone(), send_app_instructions: send_app_instructions.clone(), wasi_env, - events, + subscriptions: Arc::new(Mutex::new(HashSet::new())), }; let zellij = zellij_imports(&store, &plugin_env); @@ -532,88 +474,33 @@ pub fn start(mut os_input: Box, opts: CliArgs) { pid_tx.send(plugin_id).unwrap(); plugin_id += 1; } - PluginInstruction::Draw(buf_tx, pid, rows, cols) => { + PluginInstruction::Update(pid, event) => { + for (&i, (instance, plugin_env)) in &plugin_map { + let subs = plugin_env.subscriptions.lock().unwrap(); + // FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType? + let event_type = EventType::from_str(&event.to_string()).unwrap(); + if (pid.is_none() || pid == Some(i)) && subs.contains(&event_type) { + let update = instance.exports.get_function("update").unwrap(); + wasi_write_string( + &plugin_env.wasi_env, + &serde_json::to_string(&event).unwrap(), + ); + update.call(&[]).unwrap(); + } + } + drop(send_screen_instructions.send(ScreenInstruction::Render)); + } + PluginInstruction::Render(buf_tx, pid, rows, cols) => { let (instance, plugin_env) = plugin_map.get(&pid).unwrap(); - let draw = instance.exports.get_function("draw").unwrap(); + let render = instance.exports.get_function("render").unwrap(); - draw.call(&[Value::I32(rows as i32), Value::I32(cols as i32)]) + render + .call(&[Value::I32(rows as i32), Value::I32(cols as i32)]) .unwrap(); buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap(); } - PluginInstruction::UpdateTabs(mut tabs) => { - for (instance, plugin_env) in plugin_map.values() { - if !plugin_env.events.contains(&EventType::Tab) { - continue; - } - let handler = instance.exports.get_function("update_tabs").unwrap(); - tabs.sort_by(|a, b| a.position.cmp(&b.position)); - wasi_write_string( - &plugin_env.wasi_env, - &serde_json::to_string(&tabs).unwrap(), - ); - handler.call(&[]).unwrap(); - } - } - // FIXME: Deduplicate this with the callback below! - PluginInstruction::Input(input_type, input_bytes) => { - match input_type { - PluginInputType::Normal(pid) => { - let (instance, plugin_env) = plugin_map.get(&pid).unwrap(); - let handle_key = - instance.exports.get_function("handle_key").unwrap(); - for key in input_bytes.keys() { - if let Ok(key) = key { - wasi_write_string( - &plugin_env.wasi_env, - &serde_json::to_string(&key).unwrap(), - ); - handle_key.call(&[]).unwrap(); - } - } - } - PluginInputType::Event(event) => { - for (instance, plugin_env) in plugin_map.values() { - if !plugin_env.events.contains(&event) { - continue; - } - let handle_key = instance - .exports - .get_function(handler_map.get(&event).unwrap()) - .unwrap(); - for key in input_bytes.keys() { - if let Ok(key) = key { - wasi_write_string( - &plugin_env.wasi_env, - &serde_json::to_string(&key).unwrap(), - ); - handle_key.call(&[]).unwrap(); - } - } - } - } - } - drop(send_screen_instructions.send(ScreenInstruction::Render)); - } - PluginInstruction::GlobalInput(input_bytes) => { - // FIXME: Set up an event subscription system, and timed callbacks - for (instance, plugin_env) in plugin_map.values() { - let handler = - instance.exports.get_function("handle_global_key").unwrap(); - for key in input_bytes.keys() { - if let Ok(key) = key { - wasi_write_string( - &plugin_env.wasi_env, - &serde_json::to_string(&key).unwrap(), - ); - handler.call(&[]).unwrap(); - } - } - } - - drop(send_screen_instructions.send(ScreenInstruction::Render)); - } PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)), PluginInstruction::Quit => break, } @@ -636,7 +523,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE) .expect("could not listen on ipc socket"); let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); - err_ctx.add_call(ContextType::IPCServer); + err_ctx.add_call(ContextType::IpcServer); send_pty_instructions.update(err_ctx); send_screen_instructions.update(err_ctx); @@ -711,8 +598,6 @@ pub fn start(mut os_input: Box, opts: CliArgs) { send_screen_instructions.update(err_ctx); send_pty_instructions.update(err_ctx); match app_instruction { - AppInstruction::GetState(state_tx) => drop(state_tx.send(app_state.clone())), - AppInstruction::SetState(state) => app_state = state, AppInstruction::Exit => { break; } diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index 26a7d497..043a9aea 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -82,15 +82,10 @@ fn handle_command_exit(mut child: Child) { } for signal in signals.pending() { - // FIXME: We need to handle more signals here! - #[allow(clippy::single_match)] - match signal { - signal_hook::SIGINT => { - child.kill().unwrap(); - child.wait().unwrap(); - break 'handle_exit; - } - _ => {} + if signal == signal_hook::SIGINT { + child.kill().unwrap(); + child.wait().unwrap(); + break 'handle_exit; } } } diff --git a/src/common/screen.rs b/src/common/screen.rs index 92109435..e11b734d 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -5,14 +5,16 @@ use std::os::unix::io::RawFd; use std::str; use std::sync::mpsc::Receiver; -use super::{AppInstruction, Palette, SenderWithContext, input::handler::InputMode}; +use super::{AppInstruction, SenderWithContext}; use crate::os_input_output::OsApi; use crate::panes::PositionAndSize; use crate::pty_bus::{PtyInstruction, VteEvent}; -use crate::tab::{Tab, TabData}; +use crate::tab::Tab; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; use crate::{layout::Layout, panes::PaneId}; +use zellij_tile::data::{Event, InputMode, Palette, TabInfo}; + /// Instructions that can be sent to the [`Screen`]. #[derive(Debug, Clone)] pub enum ScreenInstruction { @@ -72,9 +74,8 @@ pub struct Screen { active_tab_index: Option, /// The [`OsApi`] this [`Screen`] uses. os_api: Box, - tabname_buf: String, input_mode: InputMode, - colors: Palette + colors: Palette, } impl Screen { @@ -88,7 +89,7 @@ impl Screen { os_api: Box, max_panes: Option, input_mode: InputMode, - colors: Palette + colors: Palette, ) -> Self { Screen { receiver: receive_screen_instructions, @@ -100,9 +101,8 @@ impl Screen { active_tab_index: None, tabs: BTreeMap::new(), os_api, - tabname_buf: String::new(), input_mode, - colors + colors, } } @@ -123,7 +123,7 @@ impl Screen { self.max_panes, Some(PaneId::Terminal(pane_id)), self.input_mode, - self.colors + self.colors, ); self.active_tab_index = Some(tab_index); self.tabs.insert(tab_index, tab); @@ -178,15 +178,12 @@ impl Screen { pub fn go_to_tab(&mut self, mut tab_index: usize) { tab_index -= 1; let active_tab = self.get_active_tab().unwrap(); - match self.tabs.values().find(|t| t.position == tab_index) { - Some(t) => { - if t.index != active_tab.index { - self.active_tab_index = Some(t.index); - self.update_tabs(); - self.render(); - } + if let Some(t) = self.tabs.values().find(|t| t.position == tab_index) { + if t.index != active_tab.index { + self.active_tab_index = Some(t.index); + self.update_tabs(); + self.render(); } - None => {} } } @@ -269,7 +266,7 @@ impl Screen { self.max_panes, None, self.input_mode, - self.colors + self.colors, ); tab.apply_layout(layout, new_pids); self.active_tab_index = Some(tab_index); @@ -281,38 +278,33 @@ impl Screen { let mut tab_data = vec![]; let active_tab_index = self.active_tab_index.unwrap(); for tab in self.tabs.values() { - tab_data.push(TabData { + tab_data.push(TabInfo { position: tab.position, name: tab.name.clone(), active: active_tab_index == tab.index, }); } self.send_plugin_instructions - .send(PluginInstruction::UpdateTabs(tab_data)) + .send(PluginInstruction::Update(None, Event::TabUpdate(tab_data))) .unwrap(); } pub fn update_active_tab_name(&mut self, buf: Vec) { let s = str::from_utf8(&buf).unwrap(); + let active_tab = self.get_active_tab_mut().unwrap(); match s { "\0" => { - self.tabname_buf = String::new(); - } - "\n" => { - let new_name = self.tabname_buf.clone(); - let active_tab = self.get_active_tab_mut().unwrap(); - active_tab.name = new_name; - self.update_tabs(); - self.render(); + active_tab.name = String::new(); } "\u{007F}" | "\u{0008}" => { //delete and backspace keys - self.tabname_buf.pop(); + active_tab.name.pop(); } c => { - self.tabname_buf.push_str(c); + active_tab.name.push_str(c); } } + self.update_tabs(); } pub fn change_input_mode(&mut self, input_mode: InputMode) { self.input_mode = input_mode; diff --git a/src/common/utils/consts.rs b/src/common/utils/consts.rs index fa9eee0d..663a545d 100644 --- a/src/common/utils/consts.rs +++ b/src/common/utils/consts.rs @@ -4,5 +4,3 @@ pub const ZELLIJ_TMP_DIR: &str = "/tmp/zellij"; pub const ZELLIJ_TMP_LOG_DIR: &str = "/tmp/zellij/zellij-log"; pub const ZELLIJ_TMP_LOG_FILE: &str = "/tmp/zellij/zellij-log/log.txt"; pub const ZELLIJ_IPC_PIPE: &str = "/tmp/zellij/ipc"; -pub const ZELLIJ_ROOT_PLUGIN_DIR: &str = "/usr/share/zellij/plugins"; -pub const ZELLIJ_ROOT_LAYOUT_DIR: &str = "/usr/share/zellij/layouts"; diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs index 15b920ad..29b110ae 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -1,36 +1,22 @@ -use crate::tab::TabData; -use serde::{Deserialize, Serialize}; use std::{ + collections::HashSet, path::PathBuf, - sync::mpsc::{channel, Sender}, + sync::{mpsc::Sender, Arc, Mutex}, }; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer_wasi::WasiEnv; +use zellij_tile::data::{Event, EventType}; use super::{ - input::handler::get_help, pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, - PaneId, SenderWithContext, + pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, PaneId, SenderWithContext, }; -#[derive(Clone, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)] -pub enum EventType { - Tab, -} - -#[derive(Clone, Debug)] -pub enum PluginInputType { - Normal(u32), - Event(EventType), -} - #[derive(Clone, Debug)] pub enum PluginInstruction { - Load(Sender, PathBuf, Vec), - Draw(Sender, u32, usize, usize), // String buffer, plugin id, rows, cols - Input(PluginInputType, Vec), // plugin id, input bytes - GlobalInput(Vec), // input bytes + Load(Sender, PathBuf), + Update(Option, Event), // Focused plugin / broadcast, event data + Render(Sender, u32, usize, usize), // String buffer, plugin id, rows, cols Unload(u32), - UpdateTabs(Vec), // num tabs, active tab Quit, } @@ -41,7 +27,7 @@ pub struct PluginEnv { pub send_app_instructions: SenderWithContext, pub send_pty_instructions: SenderWithContext, // FIXME: This should be a big bundle of all of the channels pub wasi_env: WasiEnv, - pub events: Vec, + pub subscriptions: Arc>>, } // Plugin API --------------------------------------------------------------------------------------------------------- @@ -49,16 +35,28 @@ pub struct PluginEnv { pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { imports! { "zellij" => { + "host_subscribe" => Function::new_native_with_env(store, plugin_env.clone(), host_subscribe), + "host_unsubscribe" => Function::new_native_with_env(store, plugin_env.clone(), host_unsubscribe), "host_open_file" => Function::new_native_with_env(store, plugin_env.clone(), host_open_file), "host_set_invisible_borders" => Function::new_native_with_env(store, plugin_env.clone(), host_set_invisible_borders), "host_set_max_height" => Function::new_native_with_env(store, plugin_env.clone(), host_set_max_height), "host_set_selectable" => Function::new_native_with_env(store, plugin_env.clone(), host_set_selectable), - "host_get_help" => Function::new_native_with_env(store, plugin_env.clone(), host_get_help), } } } -// FIXME: Bundle up all of the channels! Pair that with WasiEnv? +fn host_subscribe(plugin_env: &PluginEnv) { + let mut subscriptions = plugin_env.subscriptions.lock().unwrap(); + let new: HashSet = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap(); + subscriptions.extend(new); +} + +fn host_unsubscribe(plugin_env: &PluginEnv) { + let mut subscriptions = plugin_env.subscriptions.lock().unwrap(); + let old: HashSet = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap(); + subscriptions.retain(|k| !old.contains(k)); +} + fn host_open_file(plugin_env: &PluginEnv) { let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); plugin_env @@ -67,7 +65,6 @@ fn host_open_file(plugin_env: &PluginEnv) { .unwrap(); } -// FIXME: Think about these naming conventions – should everything be prefixed by 'host'? fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) { let selectable = selectable != 0; plugin_env @@ -101,21 +98,6 @@ fn host_set_invisible_borders(plugin_env: &PluginEnv, invisible_borders: i32) { .unwrap() } -fn host_get_help(plugin_env: &PluginEnv) { - let (state_tx, state_rx) = channel(); - // FIXME: If I changed the application so that threads were sent the termination - // signal and joined one at a time, there would be an order to shutdown, so I - // could get rid of this .is_ok() check and the .try_send() - if plugin_env - .send_app_instructions - .try_send(AppInstruction::GetState(state_tx)) - .is_ok() - { - let help = get_help(state_rx.recv().unwrap().input_mode); - wasi_write_string(&plugin_env.wasi_env, &serde_json::to_string(&help).unwrap()); - } -} - // Helper Functions --------------------------------------------------------------------------------------------------- // FIXME: Unwrap city diff --git a/zellij-tile/Cargo.toml b/zellij-tile/Cargo.toml index bdabb175..aa0fe088 100644 --- a/zellij-tile/Cargo.toml +++ b/zellij-tile/Cargo.toml @@ -8,4 +8,8 @@ license = "MIT" [dependencies] serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" \ No newline at end of file +serde_json = "1.0" +strum = "0.20.0" +strum_macros = "0.20.0" +xrdb = "0.1.1" +colors-transform = "0.2.5" \ No newline at end of file diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs new file mode 100644 index 00000000..315bc091 --- /dev/null +++ b/zellij-tile/src/data.rs @@ -0,0 +1,162 @@ +use colors_transform::{Color, Rgb}; +use serde::{Deserialize, Serialize}; +use strum_macros::{EnumDiscriminants, EnumIter, EnumString, ToString}; +use xrdb::Colors; +pub mod colors { + pub const WHITE: (u8, u8, u8) = (238, 238, 238); + pub const GREEN: (u8, u8, u8) = (175, 255, 0); + pub const GRAY: (u8, u8, u8) = (68, 68, 68); + pub const BRIGHT_GRAY: (u8, u8, u8) = (138, 138, 138); + pub const RED: (u8, u8, u8) = (135, 0, 0); + pub const BLACK: (u8, u8, u8) = (0, 0, 0); +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct Palette { + pub fg: (u8, u8, u8), + pub bg: (u8, u8, u8), + pub black: (u8, u8, u8), + pub red: (u8, u8, u8), + pub green: (u8, u8, u8), + pub yellow: (u8, u8, u8), + pub blue: (u8, u8, u8), + pub magenta: (u8, u8, u8), + pub cyan: (u8, u8, u8), + pub white: (u8, u8, u8), +} + +impl Palette { + pub fn new() -> Self { + let palette = match Colors::new("xresources") { + Some(colors) => { + let fg = colors.fg.unwrap(); + let fg_imm = &fg; + let fg_hex: &str = &fg_imm; + let fg = Rgb::from_hex_str(fg_hex).unwrap().as_tuple(); + let fg = (fg.0 as u8, fg.1 as u8, fg.2 as u8); + let bg = colors.bg.unwrap(); + let bg_imm = &bg; + let bg_hex: &str = &bg_imm; + let bg = Rgb::from_hex_str(bg_hex).unwrap().as_tuple(); + let bg = (bg.0 as u8, bg.1 as u8, bg.2 as u8); + let colors: Vec<(u8, u8, u8)> = colors + .colors + .iter() + .map(|c| { + let c = c.clone(); + let imm_str = &c.unwrap(); + let hex_str: &str = &imm_str; + let rgb = Rgb::from_hex_str(hex_str).unwrap().as_tuple(); + (rgb.0 as u8, rgb.1 as u8, rgb.2 as u8) + }) + .collect(); + Self { + fg, + bg, + black: colors[0], + red: colors[1], + green: colors[2], + yellow: colors[3], + blue: colors[4], + magenta: colors[5], + cyan: colors[6], + white: colors[7], + } + } + None => Self { + fg: colors::BRIGHT_GRAY, + bg: colors::BLACK, + black: colors::BLACK, + red: colors::RED, + green: colors::GREEN, + yellow: colors::GRAY, + blue: colors::GRAY, + magenta: colors::GRAY, + cyan: colors::GRAY, + white: colors::WHITE, + }, + }; + palette + } +} +impl Default for Palette { + fn default() -> Palette { + Palette::new() + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Key { + Backspace, + Left, + Right, + Up, + Down, + Home, + End, + PageUp, + PageDown, + BackTab, + Delete, + Insert, + F(u8), + Char(char), + Alt(char), + Ctrl(char), + Null, + Esc, +} + +#[derive(Debug, Clone, EnumDiscriminants, ToString, Serialize, Deserialize)] +#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))] +#[strum_discriminants(name(EventType))] +pub enum Event { + ModeUpdate(ModeInfo), + TabUpdate(Vec), + KeyPress(Key), +} + +/// Describes the different input modes, which change the way that keystrokes will be interpreted. +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, EnumIter, Serialize, Deserialize)] +pub enum InputMode { + /// In `Normal` mode, input is always written to the terminal, except for the shortcuts leading + /// to other modes + Normal, + /// In `Locked` mode, input is always written to the terminal and all shortcuts are disabled + /// except the one leading back to normal mode + Locked, + /// `Resize` mode allows resizing the different existing panes. + Resize, + /// `Pane` mode allows creating and closing panes, as well as moving between them. + Pane, + /// `Tab` mode allows creating and closing tabs, as well as moving between them. + Tab, + /// `Scroll` mode allows scrolling up and down within a pane. + Scroll, + RenameTab, +} + +impl Default for InputMode { + fn default() -> InputMode { + InputMode::Normal + } +} + +/// Represents the contents of the help message that is printed in the status bar, +/// which indicates the current [`InputMode`] and what the keybinds for that mode +/// are. Related to the default `status-bar` plugin. +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct ModeInfo { + pub mode: InputMode, + // FIXME: This should probably return Keys and Actions, then sort out strings plugin-side + pub keybinds: Vec<(String, String)>, // => + pub palette: Palette, +} + +#[derive(Debug, Default, Clone, Deserialize, Serialize)] +pub struct TabInfo { + /* subset of fields to publish to plugins */ + pub position: usize, + pub name: String, + pub active: bool, +} diff --git a/zellij-tile/src/lib.rs b/zellij-tile/src/lib.rs index de87ee9c..72211764 100644 --- a/zellij-tile/src/lib.rs +++ b/zellij-tile/src/lib.rs @@ -1,14 +1,14 @@ -mod shim; +pub mod data; +pub mod prelude; +pub mod shim; + +use data::*; -pub use shim::*; #[allow(unused_variables)] pub trait ZellijTile { - fn init(&mut self) {} - fn draw(&mut self, rows: usize, cols: usize) {} - fn handle_key(&mut self, key: Key) {} - fn handle_global_key(&mut self, key: Key) {} - fn update_tabs(&mut self) {} - fn handle_tab_rename_keypress(&mut self, key: Key) {} + fn load(&mut self) {} + fn update(&mut self, event: Event) {} + fn render(&mut self, rows: usize, cols: usize) {} } #[macro_export] @@ -20,45 +20,22 @@ macro_rules! register_tile { fn main() { STATE.with(|state| { - state.borrow_mut().init(); + state.borrow_mut().load(); }); } #[no_mangle] - pub fn draw(rows: i32, cols: i32) { + pub fn update() { STATE.with(|state| { - state.borrow_mut().draw(rows as usize, cols as usize); + state.borrow_mut().update($crate::shim::object_from_stdin()); }); } #[no_mangle] - pub fn handle_key() { + pub fn render(rows: i32, cols: i32) { STATE.with(|state| { - state.borrow_mut().handle_key($crate::get_key()); + state.borrow_mut().render(rows as usize, cols as usize); }); } - - #[no_mangle] - pub fn handle_global_key() { - STATE.with(|state| { - state.borrow_mut().handle_global_key($crate::get_key()); - }); - } - - #[no_mangle] - pub fn update_tabs() { - STATE.with(|state| { - state.borrow_mut().update_tabs(); - }) - } - - #[no_mangle] - pub fn handle_tab_rename_keypress() { - STATE.with(|state| { - state - .borrow_mut() - .handle_tab_rename_keypress($crate::get_key()); - }) - } }; } diff --git a/zellij-tile/src/prelude.rs b/zellij-tile/src/prelude.rs new file mode 100644 index 00000000..2dd24a47 --- /dev/null +++ b/zellij-tile/src/prelude.rs @@ -0,0 +1,3 @@ +pub use crate::data::*; +pub use crate::shim::*; +pub use crate::*; diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index bad9ffa8..42a646a1 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -1,123 +1,56 @@ -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::de::DeserializeOwned; use std::{io, path::Path}; -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Key { - Backspace, - Left, - Right, - Up, - Down, - Home, - End, - PageUp, - PageDown, - BackTab, - Delete, - Insert, - F(u8), - Char(char), - Alt(char), - Ctrl(char), - Null, - Esc, +use crate::data::*; + +// Subscription Handling + +pub fn subscribe(event_types: &[EventType]) { + println!("{}", serde_json::to_string(event_types).unwrap()); + unsafe { host_subscribe() }; } -// TODO: use same struct from main crate? -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct Help { - pub mode: InputMode, - pub keybinds: Vec<(String, String)>, - pub palette: Palette +pub fn unsubscribe(event_types: &[EventType]) { + println!("{}", serde_json::to_string(event_types).unwrap()); + unsafe { host_unsubscribe() }; } -// TODO: use same struct from main crate? -#[derive(Debug, Clone, Deserialize, Serialize)] -pub enum InputMode { - Normal, - Locked, - Resize, - Pane, - Tab, - RenameTab, - Scroll, - Exiting, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct TabData { - /* subset of fields to publish to plugins */ - pub position: usize, - pub name: String, - pub active: bool, -} - -#[derive(Default, Debug, Copy, Clone, Deserialize, Serialize)] -pub struct Palette { - pub fg: (u8, u8, u8), - pub bg: (u8, u8, u8), - pub black: (u8, u8, u8), - pub red: (u8, u8, u8), - pub green: (u8, u8, u8), - pub yellow: (u8, u8, u8), - pub blue: (u8, u8, u8), - pub magenta: (u8, u8, u8), - pub cyan: (u8, u8, u8), - pub white: (u8, u8, u8), -} - -impl Default for InputMode { - fn default() -> InputMode { - InputMode::Normal - } -} - -pub fn get_key() -> Key { - deserialize_from_stdin().unwrap() -} -pub fn get_palette() -> Palette { - deserialize_from_stdin().unwrap() -} - -pub fn open_file(path: &Path) { - println!("{}", path.to_string_lossy()); - unsafe { host_open_file() }; -} +// Plugin Settings pub fn set_max_height(max_height: i32) { unsafe { host_set_max_height(max_height) }; } pub fn set_invisible_borders(invisible_borders: bool) { - let invisible_borders = if invisible_borders { 1 } else { 0 }; - unsafe { host_set_invisible_borders(invisible_borders) }; + unsafe { host_set_invisible_borders(if invisible_borders { 1 } else { 0 }) }; } pub fn set_selectable(selectable: bool) { - let selectable = if selectable { 1 } else { 0 }; - unsafe { host_set_selectable(selectable) }; + unsafe { host_set_selectable(if selectable { 1 } else { 0 }) }; } -pub fn get_help() -> Help { - unsafe { host_get_help() }; - deserialize_from_stdin().unwrap_or_default() +// Host Functions + +pub fn open_file(path: &Path) { + println!("{}", path.to_string_lossy()); + unsafe { host_open_file() }; } -pub fn get_tabs() -> Vec { - deserialize_from_stdin().unwrap_or_default() -} +// Internal Functions -fn deserialize_from_stdin() -> Option { +#[doc(hidden)] +pub fn object_from_stdin() -> T { let mut json = String::new(); io::stdin().read_line(&mut json).unwrap(); - serde_json::from_str(&json).ok() + serde_json::from_str(&json).unwrap() } #[link(wasm_import_module = "zellij")] extern "C" { + fn host_subscribe(); + fn host_unsubscribe(); fn host_open_file(); fn host_set_max_height(max_height: i32); fn host_set_selectable(selectable: i32); fn host_set_invisible_borders(invisible_borders: i32); - fn host_get_help(); }