wip: latest plugin system merge in

This commit is contained in:
denis 2021-03-27 14:43:16 +02:00
commit b71315b036
33 changed files with 723 additions and 954 deletions

42
Cargo.lock generated
View file

@ -1007,9 +1007,9 @@ dependencies = [
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.6.1" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" checksum = "cc14fc54a812b4472b4113facc3e44d099fbc0ea2ce0551fa5c703f8edfbfd38"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@ -1281,12 +1281,6 @@ dependencies = [
"num_cpus", "num_cpus",
] ]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.5" version = "0.2.5"
@ -1302,7 +1296,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
dependencies = [ dependencies = [
"redox_syscall 0.2.5", "redox_syscall",
] ]
[[package]] [[package]]
@ -1312,7 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"redox_syscall 0.2.5", "redox_syscall",
] ]
[[package]] [[package]]
@ -1593,9 +1587,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.64" version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1632,7 +1626,7 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"rand", "rand",
"redox_syscall 0.2.5", "redox_syscall",
"remove_dir_all", "remove_dir_all",
"winapi", "winapi",
] ]
@ -1648,16 +1642,15 @@ dependencies = [
] ]
[[package]] [[package]]
name = "termion_temporary_zellij_fork" name = "termion"
version = "1.6.0" version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65175afb01727f72d690bc8b2a2ac411c0243ec43988b7bd96d428301197bed0" checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
dependencies = [ dependencies = [
"libc", "libc",
"numtoa", "numtoa",
"redox_syscall 0.1.57", "redox_syscall",
"redox_termios", "redox_termios",
"serde",
] ]
[[package]] [[package]]
@ -2167,9 +2160,9 @@ checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf"
[[package]] [[package]]
name = "wast" name = "wast"
version = "35.0.0" version = "35.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db5ae96da18bb5926341516fd409b5a8ce4e4714da7f0a1063d3b20ac9f9a1e1" checksum = "1a5800e9f86a1eae935e38bea11e60fd253f6d514d153fb39b3e5535a7b37b56"
dependencies = [ dependencies = [
"leb128", "leb128",
] ]
@ -2303,8 +2296,7 @@ dependencies = [
"strip-ansi-escapes", "strip-ansi-escapes",
"structopt", "structopt",
"strum", "strum",
"strum_macros", "termion",
"termion_temporary_zellij_fork",
"termios", "termios",
"toml", "toml",
"unicode-truncate", "unicode-truncate",
@ -2313,13 +2305,17 @@ dependencies = [
"walkdir", "walkdir",
"wasmer", "wasmer",
"wasmer-wasi", "wasmer-wasi",
"xrdb", "zellij-tile",
] ]
[[package]] [[package]]
name = "zellij-tile" name = "zellij-tile"
version = "0.5.0" version = "0.5.0"
dependencies = [ dependencies = [
"colors-transform",
"serde", "serde",
"serde_json", "serde_json",
"strum",
"strum_macros",
"xrdb",
] ]

View file

@ -27,20 +27,18 @@ serde_yaml = "0.8"
signal-hook = "0.1.10" signal-hook = "0.1.10"
strip-ansi-escapes = "0.1.0" strip-ansi-escapes = "0.1.0"
structopt = "0.3" structopt = "0.3"
# termion = { git = "https://gitlab.com/TheLostLambda/termion.git", version = "1.6.0", features = ["serde"] } termion = "1.5.0"
termion = { package = "termion_temporary_zellij_fork" , version = "1.6.0", features = ["serde"]}
termios = "0.3" termios = "0.3"
unicode-truncate = "0.2.0" unicode-truncate = "0.2.0"
unicode-width = "0.1.8" unicode-width = "0.1.8"
vte = "0.8.0" vte = "0.8.0"
strum = "0.20.0" strum = "0.20.0"
strum_macros = "0.20.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
wasmer = "1.0.0" wasmer = "1.0.0"
wasmer-wasi = "1.0.0" wasmer-wasi = "1.0.0"
interprocess = "1.0.1" interprocess = "1.0.1"
xrdb = "0.1.1"
colors-transform = "0.2.5" colors-transform = "0.2.5"
zellij-tile = { path = "zellij-tile/", version = "0.5.0" }
[dependencies.async-std] [dependencies.async-std]
version = "1.3.0" version = "1.3.0"

View file

@ -5,8 +5,6 @@ parts:
split_size: split_size:
Fixed: 1 Fixed: 1
plugin: tab-bar plugin: tab-bar
events:
- Tab
- direction: Vertical - direction: Vertical
expansion_boundary: true expansion_boundary: true
- direction: Vertical - direction: Vertical

View file

@ -5,8 +5,6 @@ parts:
split_size: split_size:
Fixed: 1 Fixed: 1
plugin: tab-bar plugin: tab-bar
events:
- Tab
- direction: Vertical - direction: Vertical
parts: parts:
- direction: Horizontal - direction: Horizontal

View file

@ -1,29 +1,25 @@
#!/bin/sh #!/bin/sh
total=6 total=5
# This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open # This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open
echo "Building zellij-tile (1/$total)..." echo "Building status-bar (1/$total)..."
cd zellij-tile cd default-tiles/status-bar
cargo build --release --target-dir ../target
echo "Building status-bar (2/$total)..."
cd ../default-tiles/status-bar
cargo build --release --target-dir ../../target cargo build --release --target-dir ../../target
echo "Building strider (3/$total)..." echo "Building strider (2/$total)..."
cd ../strider cd ../strider
cargo build --release --target-dir ../../target cargo build --release --target-dir ../../target
echo "Building tab-bar (4/$total)..." echo "Building tab-bar (3/$total)..."
cd ../tab-bar cd ../tab-bar
cargo build --release --target-dir ../../target cargo build --release --target-dir ../../target
echo "Optimising WASM executables (5/$total)..." echo "Optimising WASM executables (4/$total)..."
cd ../.. 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/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/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 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 $@ cargo build --target-dir target $@

View file

@ -1,5 +1,5 @@
use ansi_term::{ANSIStrings, Color::RGB, Style}; 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::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN, RED, WHITE};
use crate::{LinePart, ARROW_SEPARATOR}; 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)) .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2))
.on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2))
.bold() .bold()
.paint(format!(" <")); .paint(" <");
let char_shortcut = Style::new() let char_shortcut = Style::new()
.bold() .bold()
.fg(RGB(palette.red.0, palette.red.1, palette.red.2)) .fg(RGB(palette.red.0, palette.red.1, palette.red.2))
.on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2))
.bold() .bold()
.paint(format!("{}", letter)); .paint(letter.to_string());
let char_right_separator = Style::new() let char_right_separator = Style::new()
.bold() .bold()
.fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2))
.on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2))
.bold() .bold()
.paint(format!(">")); .paint(">");
let styled_text = Style::new() let styled_text = Style::new()
.fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2))
.on(RGB(palette.bg.0, palette.bg.1, palette.bg.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)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2))
.paint(ARROW_SEPARATOR); .paint(ARROW_SEPARATOR);
LinePart { LinePart {
part: format!( part: ANSIStrings(&[
"{}", prefix_separator,
ANSIStrings(&[ char_left_separator,
prefix_separator, char_shortcut,
char_left_separator, char_right_separator,
char_shortcut, styled_text,
char_right_separator, suffix_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 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)) .on(RGB(palette.green.0, palette.green.1, palette.green.2))
.paint(ARROW_SEPARATOR); .paint(ARROW_SEPARATOR);
LinePart { LinePart {
part: format!( part: ANSIStrings(&[
"{}", prefix_separator,
ANSIStrings(&[ char_left_separator,
prefix_separator, char_shortcut,
char_left_separator, char_right_separator,
char_shortcut, styled_text,
char_right_separator, suffix_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 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)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2))
.paint(ARROW_SEPARATOR); .paint(ARROW_SEPARATOR);
LinePart { LinePart {
part: format!( part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(),
"{}",
ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator])
),
len, 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)) .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2))
.paint(ARROW_SEPARATOR); .paint(ARROW_SEPARATOR);
LinePart { LinePart {
part: format!( part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(),
"{}",
ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator])
),
len, len,
} }
} }
@ -256,11 +246,13 @@ fn shortened_ctrl_key(key: &CtrlKeyShortcut, palette: Palette) -> LinePart {
}; };
match key.mode { match key.mode {
CtrlKeyMode::Unselected => { CtrlKeyMode::Unselected => {
unselected_mode_shortcut(letter_shortcut, &format!("{}", shortened_text), palette) unselected_mode_shortcut(letter_shortcut, &shortened_text, palette)
}
CtrlKeyMode::Selected => {
selected_mode_shortcut(letter_shortcut, &format!("{}", 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( CtrlKeyMode::Disabled => disabled_mode_shortcut(
&format!(" <{}>{}", letter_shortcut, shortened_text), &format!(" <{}>{}", letter_shortcut, shortened_text),
palette, palette,
@ -317,16 +309,16 @@ pub fn superkey(palette: Palette) -> LinePart {
.bold() .bold()
.paint(prefix_text); .paint(prefix_text);
LinePart { LinePart {
part: format!("{}", prefix), part: prefix.to_string(),
len: prefix_text.chars().count(), 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 { match &help.mode {
InputMode::Locked => key_indicators( InputMode::Locked => key_indicators(
max_len, max_len,
&vec![ &[
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Lock),
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Pane),
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Tab), CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Tab),
@ -338,7 +330,7 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart {
), ),
InputMode::Resize => key_indicators( InputMode::Resize => key_indicators(
max_len, max_len,
&vec![ &[
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
@ -350,7 +342,7 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart {
), ),
InputMode::Pane => key_indicators( InputMode::Pane => key_indicators(
max_len, max_len,
&vec![ &[
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Pane),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), 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( InputMode::Tab | InputMode::RenameTab => key_indicators(
max_len, max_len,
&vec![ &[
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Tab), CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Tab),
@ -374,7 +366,7 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart {
), ),
InputMode::Scroll => key_indicators( InputMode::Scroll => key_indicators(
max_len, max_len,
&vec![ &[
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
@ -384,9 +376,9 @@ pub fn ctrl_keys(help: &Help, max_len: usize) -> LinePart {
], ],
help.palette, help.palette,
), ),
InputMode::Normal | _ => key_indicators( InputMode::Normal => key_indicators(
max_len, max_len,
&vec![ &[
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),

View file

@ -2,7 +2,7 @@ mod first_line;
mod second_line; mod second_line;
use std::fmt::{Display, Error, Formatter}; use std::fmt::{Display, Error, Formatter};
use zellij_tile::*; use zellij_tile::prelude::*;
use first_line::{ctrl_keys, superkey}; use first_line::{ctrl_keys, superkey};
use second_line::keybinds; use second_line::keybinds;
@ -23,7 +23,9 @@ static ARROW_SEPARATOR: &str = "";
static MORE_MSG: &str = " ... "; static MORE_MSG: &str = " ... ";
#[derive(Default)] #[derive(Default)]
struct State {} struct State {
mode_info: ModeInfo,
}
register_tile!(State); register_tile!(State);
@ -40,23 +42,41 @@ impl Display for LinePart {
} }
impl ZellijTile for State { impl ZellijTile for State {
fn init(&mut self) { fn load(&mut self) {
set_selectable(false); set_selectable(false);
set_invisible_borders(true); set_invisible_borders(true);
set_max_height(2); set_max_height(2);
subscribe(&[EventType::ModeUpdate]);
} }
fn draw(&mut self, _rows: usize, cols: usize) { fn update(&mut self, event: Event) {
let help = get_help(); if let Event::ModeUpdate(mode_info) = event {
let superkey = superkey(help.palette); self.mode_info = mode_info;
let ctrl_keys = ctrl_keys(&help, cols - superkey.len); }
}
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 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;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 // [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!(
println!("{}\u{1b}[{};{};{}m\u{1b}[0K", second_line, help.palette.bg.0, help.palette.bg.1, help.palette.bg.2); "{}\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
);
} }
} }

View file

@ -1,6 +1,6 @@
// use colored::*; // use colored::*;
use ansi_term::{ANSIStrings, Color::RGB, Style}; use ansi_term::{ANSIStrings, Color::RGB, Style};
use zellij_tile::*; use zellij_tile::prelude::*;
use crate::colors::{BLACK, GREEN, ORANGE, WHITE}; use crate::colors::{BLACK, GREEN, ORANGE, WHITE};
use crate::{LinePart, MORE_MSG}; 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 { match help.mode {
InputMode::Normal => LinePart::default(), InputMode::Normal => LinePart::default(),
InputMode::Locked => locked_interface_indication(palette), InputMode::Locked => locked_interface_indication(help.palette),
_ => { _ => {
let mut line_part = LinePart::default(); let mut line_part = LinePart::default();
for (i, (letter, description)) in help.keybinds.iter().enumerate() { 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.len += shortcut.len;
line_part.part = format!("{}{}", line_part.part, shortcut,); 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.len += select_pane_shortcut.len;
line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,);
line_part 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 { match help.mode {
InputMode::Normal => LinePart::default(), InputMode::Normal => LinePart::default(),
InputMode::Locked => locked_interface_indication(palette), InputMode::Locked => locked_interface_indication(help.palette),
_ => { _ => {
let mut line_part = LinePart::default(); let mut line_part = LinePart::default();
for (i, (letter, description)) in help.keybinds.iter().enumerate() { 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.len += shortcut.len;
line_part.part = format!("{}{}", line_part.part, shortcut,); 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.len += select_pane_shortcut.len;
line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,);
line_part 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 { match help.mode {
InputMode::Normal => LinePart::default(), InputMode::Normal => LinePart::default(),
InputMode::Locked => { InputMode::Locked => {
let line_part = locked_interface_indication(palette); let line_part = locked_interface_indication(help.palette);
if line_part.len <= max_len { if line_part.len <= max_len {
line_part line_part
} else { } else {
@ -210,7 +210,7 @@ fn best_effort_shortcut_list(help: &Help, max_len: usize, palette: Palette) -> L
_ => { _ => {
let mut line_part = LinePart::default(); let mut line_part = LinePart::default();
for (i, (letter, description)) in help.keybinds.iter().enumerate() { 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 { if line_part.len + shortcut.len + MORE_MSG.chars().count() > max_len {
// TODO: better // TODO: better
line_part.part = format!("{}{}", line_part.part, MORE_MSG); 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.len += shortcut.len;
line_part.part = format!("{}{}", line_part.part, shortcut,); 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 { if line_part.len + select_pane_shortcut.len <= max_len {
line_part.len += select_pane_shortcut.len; line_part.len += select_pane_shortcut.len;
line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); 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 { pub fn keybinds(help: &ModeInfo, max_width: usize) -> LinePart {
let full_shortcut_list = full_shortcut_list(help, palette); let full_shortcut_list = full_shortcut_list(help);
if full_shortcut_list.len <= max_width { if full_shortcut_list.len <= max_width {
return full_shortcut_list; 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 { if shortened_shortcut_list.len <= max_width {
return shortened_shortcut_list; return shortened_shortcut_list;
} }
return best_effort_shortcut_list(help, max_width, palette); best_effort_shortcut_list(help, max_width)
} }

View file

@ -3,16 +3,51 @@ mod state;
use colored::*; use colored::*;
use state::{FsEntry, State}; use state::{FsEntry, State};
use std::{cmp::min, fs::read_dir}; use std::{cmp::min, fs::read_dir};
use zellij_tile::*; use zellij_tile::prelude::*;
register_tile!(State); register_tile!(State);
impl ZellijTile for State { impl ZellijTile for State {
fn init(&mut self) { fn load(&mut self) {
refresh_directory(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 { for i in 0..rows {
if self.selected() < self.scroll() { if self.selected() < self.scroll() {
*self.scroll_mut() = self.selected(); *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) { fn refresh_directory(state: &mut State) {

View file

@ -58,6 +58,6 @@ impl FsEntry {
} }
pub fn is_hidden_file(&self) -> bool { pub fn is_hidden_file(&self) -> bool {
self.name().chars().nth(0).unwrap() == '.'.into() self.name().starts_with('.')
} }
} }

View file

@ -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 { let more_text = if tab_count_to_the_left < 10000 {
format!(" ← +{} ", tab_count_to_the_left) format!(" ← +{} ", tab_count_to_the_left)
} else { } else {
format!(" ← +many ") " ← +many ".to_string()
}; };
// 238 // 238
let more_text_len = more_text.chars().count() + 2; // 2 for the arrows 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 { let more_text = if tab_count_to_the_right < 10000 {
format!(" +{}", tab_count_to_the_right) format!(" +{}", tab_count_to_the_right)
} else { } else {
format!(" +many → ") " +many → ".to_string()
}; };
let more_text_len = more_text.chars().count() + 1; // 2 for the arrow 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); 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 { 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_text_len = prefix_text.chars().count();
let prefix_styled_text = Style::new().fg(WHITE).on(GRAY).bold().paint(prefix_text); let prefix_styled_text = Style::new().fg(WHITE).on(GRAY).bold().paint(prefix_text);
LinePart { LinePart {

View file

@ -1,7 +1,7 @@
mod line; mod line;
mod tab; mod tab;
use zellij_tile::*; use zellij_tile::prelude::*;
use crate::line::tab_line; use crate::line::tab_line;
use crate::tab::tab_style; use crate::tab::tab_style;
@ -12,25 +12,10 @@ pub struct LinePart {
len: usize, len: usize,
} }
#[derive(PartialEq)]
enum BarMode {
Normal,
Rename,
}
impl Default for BarMode {
fn default() -> Self {
BarMode::Normal
}
}
#[derive(Default)] #[derive(Default)]
struct State { struct State {
active_tab_index: usize, tabs: Vec<TabInfo>,
num_tabs: usize, mode_info: ModeInfo,
tabs: Vec<TabData>,
mode: BarMode,
new_name: String,
} }
static ARROW_SEPARATOR: &str = ""; static ARROW_SEPARATOR: &str = "";
@ -49,18 +34,22 @@ pub mod colors {
register_tile!(State); register_tile!(State);
impl ZellijTile for State { impl ZellijTile for State {
fn init(&mut self) { fn load(&mut self) {
set_selectable(false); set_selectable(false);
set_invisible_borders(true); set_invisible_borders(true);
set_max_height(1); set_max_height(1);
self.active_tab_index = 0; subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]);
self.num_tabs = 0;
self.mode = BarMode::Normal;
self.new_name = String::new();
} }
fn draw(&mut self, _rows: usize, cols: usize) { fn update(&mut self, event: Event) {
let help = get_help(); 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() { if self.tabs.is_empty() {
return; return;
} }
@ -68,17 +57,15 @@ impl ZellijTile for State {
let mut active_tab_index = 0; let mut active_tab_index = 0;
for t in self.tabs.iter_mut() { for t in self.tabs.iter_mut() {
let mut tabname = t.name.clone(); let mut tabname = t.name.clone();
if t.active && self.mode == BarMode::Rename { if t.active && self.mode_info.mode == InputMode::RenameTab {
if self.new_name.is_empty() { if tabname.is_empty() {
tabname = String::from("Enter name..."); tabname = String::from("Enter name...");
} else {
tabname = self.new_name.clone();
} }
active_tab_index = t.position; active_tab_index = t.position;
} else if t.active { } else if t.active {
active_tab_index = t.position; 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); all_tabs.push(tab);
} }
let tab_line = tab_line(all_tabs, active_tab_index, cols); 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 { for bar_part in tab_line {
s = format!("{}{}", s, bar_part.part); 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); println!(
} "{}\u{1b}[{};{};{}m\u{1b}[0K",
s,
fn update_tabs(&mut self) { self.mode_info.palette.bg.0,
self.tabs = get_tabs(); self.mode_info.palette.bg.1,
} self.mode_info.palette.bg.2
);
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();
}
_ => {}
}
} }
} }

View file

@ -1,7 +1,7 @@
use crate::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN}; use crate::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN};
use crate::{LinePart, ARROW_SEPARATOR}; use crate::{LinePart, ARROW_SEPARATOR};
use ansi_term::{ANSIStrings, Color::RGB, Style}; 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 { pub fn active_tab(text: String, palette: Palette) -> LinePart {
let left_separator = Style::new() let left_separator = Style::new()

View file

@ -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 ansi_term::Colour::RGB;
use std::collections::HashMap; use std::collections::HashMap;
use zellij_tile::data::{colors, InputMode, Palette};
use std::fmt::{Display, Error, Formatter}; use std::fmt::{Display, Error, Formatter};
pub mod boundary_type { pub mod boundary_type {
pub const TOP_RIGHT: &str = ""; pub const TOP_RIGHT: &str = "";
pub const VERTICAL: &str = ""; pub const VERTICAL: &str = "";
@ -50,7 +50,11 @@ impl Display for BoundarySymbol {
match self.invisible { match self.invisible {
true => write!(f, " "), true => write!(f, " "),
false => match self.color { 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), None => write!(f, "{}", self.boundary_type),
}, },
} }
@ -75,7 +79,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::VERTICAL) => { (boundary_type::TOP_RIGHT, boundary_type::VERTICAL) => {
@ -84,7 +88,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL) => { (boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL) => {
@ -93,7 +97,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::TOP_LEFT) => { (boundary_type::TOP_RIGHT, boundary_type::TOP_LEFT) => {
@ -102,7 +106,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::BOTTOM_RIGHT) => { (boundary_type::TOP_RIGHT, boundary_type::BOTTOM_RIGHT) => {
@ -111,7 +115,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::BOTTOM_LEFT) => { (boundary_type::TOP_RIGHT, boundary_type::BOTTOM_LEFT) => {
@ -120,7 +124,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::VERTICAL_LEFT) => { (boundary_type::TOP_RIGHT, boundary_type::VERTICAL_LEFT) => {
@ -129,7 +133,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::VERTICAL_RIGHT) => { (boundary_type::TOP_RIGHT, boundary_type::VERTICAL_RIGHT) => {
@ -138,7 +142,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL_DOWN) => {
@ -147,7 +151,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL_UP) => { (boundary_type::TOP_RIGHT, boundary_type::HORIZONTAL_UP) => {
@ -156,7 +160,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_RIGHT, boundary_type::CROSS) => { (boundary_type::TOP_RIGHT, boundary_type::CROSS) => {
@ -165,7 +169,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::HORIZONTAL) => { (boundary_type::HORIZONTAL, boundary_type::HORIZONTAL) => {
@ -174,7 +178,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::VERTICAL) => { (boundary_type::HORIZONTAL, boundary_type::VERTICAL) => {
@ -183,7 +187,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::TOP_LEFT) => { (boundary_type::HORIZONTAL, boundary_type::TOP_LEFT) => {
@ -192,7 +196,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::BOTTOM_RIGHT) => { (boundary_type::HORIZONTAL, boundary_type::BOTTOM_RIGHT) => {
@ -201,7 +205,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::BOTTOM_LEFT) => { (boundary_type::HORIZONTAL, boundary_type::BOTTOM_LEFT) => {
@ -210,7 +214,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::VERTICAL_LEFT) => { (boundary_type::HORIZONTAL, boundary_type::VERTICAL_LEFT) => {
@ -219,7 +223,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::VERTICAL_RIGHT) => { (boundary_type::HORIZONTAL, boundary_type::VERTICAL_RIGHT) => {
@ -228,7 +232,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::HORIZONTAL, boundary_type::HORIZONTAL_DOWN) => {
@ -237,7 +241,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::HORIZONTAL_UP) => { (boundary_type::HORIZONTAL, boundary_type::HORIZONTAL_UP) => {
@ -246,7 +250,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL, boundary_type::CROSS) => { (boundary_type::HORIZONTAL, boundary_type::CROSS) => {
@ -255,7 +259,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::VERTICAL) => { (boundary_type::VERTICAL, boundary_type::VERTICAL) => {
@ -264,7 +268,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::TOP_LEFT) => { (boundary_type::VERTICAL, boundary_type::TOP_LEFT) => {
@ -273,7 +277,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::BOTTOM_RIGHT) => { (boundary_type::VERTICAL, boundary_type::BOTTOM_RIGHT) => {
@ -282,7 +286,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::BOTTOM_LEFT) => { (boundary_type::VERTICAL, boundary_type::BOTTOM_LEFT) => {
@ -291,7 +295,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::VERTICAL_LEFT) => { (boundary_type::VERTICAL, boundary_type::VERTICAL_LEFT) => {
@ -300,7 +304,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::VERTICAL_RIGHT) => { (boundary_type::VERTICAL, boundary_type::VERTICAL_RIGHT) => {
@ -309,7 +313,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::VERTICAL, boundary_type::HORIZONTAL_DOWN) => {
@ -318,7 +322,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::HORIZONTAL_UP) => { (boundary_type::VERTICAL, boundary_type::HORIZONTAL_UP) => {
@ -327,7 +331,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL, boundary_type::CROSS) => { (boundary_type::VERTICAL, boundary_type::CROSS) => {
@ -336,7 +340,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::TOP_LEFT) => { (boundary_type::TOP_LEFT, boundary_type::TOP_LEFT) => {
@ -345,7 +349,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::BOTTOM_RIGHT) => { (boundary_type::TOP_LEFT, boundary_type::BOTTOM_RIGHT) => {
@ -354,7 +358,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::BOTTOM_LEFT) => { (boundary_type::TOP_LEFT, boundary_type::BOTTOM_LEFT) => {
@ -363,7 +367,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::VERTICAL_LEFT) => { (boundary_type::TOP_LEFT, boundary_type::VERTICAL_LEFT) => {
@ -372,7 +376,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::VERTICAL_RIGHT) => { (boundary_type::TOP_LEFT, boundary_type::VERTICAL_RIGHT) => {
@ -381,7 +385,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::TOP_LEFT, boundary_type::HORIZONTAL_DOWN) => {
@ -390,7 +394,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::HORIZONTAL_UP) => { (boundary_type::TOP_LEFT, boundary_type::HORIZONTAL_UP) => {
@ -399,7 +403,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::TOP_LEFT, boundary_type::CROSS) => { (boundary_type::TOP_LEFT, boundary_type::CROSS) => {
@ -408,7 +412,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_RIGHT, boundary_type::BOTTOM_RIGHT) => { (boundary_type::BOTTOM_RIGHT, boundary_type::BOTTOM_RIGHT) => {
@ -417,7 +421,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_RIGHT, boundary_type::BOTTOM_LEFT) => { (boundary_type::BOTTOM_RIGHT, boundary_type::BOTTOM_LEFT) => {
@ -426,7 +430,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_RIGHT, boundary_type::VERTICAL_LEFT) => { (boundary_type::BOTTOM_RIGHT, boundary_type::VERTICAL_LEFT) => {
@ -435,7 +439,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_RIGHT, boundary_type::VERTICAL_RIGHT) => { (boundary_type::BOTTOM_RIGHT, boundary_type::VERTICAL_RIGHT) => {
@ -444,7 +448,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_RIGHT, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::BOTTOM_RIGHT, boundary_type::HORIZONTAL_DOWN) => {
@ -453,7 +457,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_RIGHT, boundary_type::HORIZONTAL_UP) => { (boundary_type::BOTTOM_RIGHT, boundary_type::HORIZONTAL_UP) => {
@ -462,7 +466,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_RIGHT, boundary_type::CROSS) => { (boundary_type::BOTTOM_RIGHT, boundary_type::CROSS) => {
@ -471,7 +475,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_LEFT, boundary_type::BOTTOM_LEFT) => { (boundary_type::BOTTOM_LEFT, boundary_type::BOTTOM_LEFT) => {
@ -480,7 +484,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_LEFT, boundary_type::VERTICAL_LEFT) => { (boundary_type::BOTTOM_LEFT, boundary_type::VERTICAL_LEFT) => {
@ -489,7 +493,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_LEFT, boundary_type::VERTICAL_RIGHT) => { (boundary_type::BOTTOM_LEFT, boundary_type::VERTICAL_RIGHT) => {
@ -498,7 +502,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_LEFT, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::BOTTOM_LEFT, boundary_type::HORIZONTAL_DOWN) => {
@ -507,7 +511,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_LEFT, boundary_type::HORIZONTAL_UP) => { (boundary_type::BOTTOM_LEFT, boundary_type::HORIZONTAL_UP) => {
@ -516,7 +520,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::BOTTOM_LEFT, boundary_type::CROSS) => { (boundary_type::BOTTOM_LEFT, boundary_type::CROSS) => {
@ -525,7 +529,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_LEFT, boundary_type::VERTICAL_LEFT) => { (boundary_type::VERTICAL_LEFT, boundary_type::VERTICAL_LEFT) => {
@ -534,7 +538,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_LEFT, boundary_type::VERTICAL_RIGHT) => { (boundary_type::VERTICAL_LEFT, boundary_type::VERTICAL_RIGHT) => {
@ -543,7 +547,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_LEFT, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::VERTICAL_LEFT, boundary_type::HORIZONTAL_DOWN) => {
@ -552,7 +556,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_LEFT, boundary_type::HORIZONTAL_UP) => { (boundary_type::VERTICAL_LEFT, boundary_type::HORIZONTAL_UP) => {
@ -561,7 +565,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_LEFT, boundary_type::CROSS) => { (boundary_type::VERTICAL_LEFT, boundary_type::CROSS) => {
@ -570,7 +574,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_RIGHT, boundary_type::VERTICAL_RIGHT) => { (boundary_type::VERTICAL_RIGHT, boundary_type::VERTICAL_RIGHT) => {
@ -579,7 +583,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_RIGHT, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::VERTICAL_RIGHT, boundary_type::HORIZONTAL_DOWN) => {
@ -588,7 +592,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_RIGHT, boundary_type::HORIZONTAL_UP) => { (boundary_type::VERTICAL_RIGHT, boundary_type::HORIZONTAL_UP) => {
@ -597,7 +601,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::VERTICAL_RIGHT, boundary_type::CROSS) => { (boundary_type::VERTICAL_RIGHT, boundary_type::CROSS) => {
@ -606,7 +610,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL_DOWN, boundary_type::HORIZONTAL_DOWN) => { (boundary_type::HORIZONTAL_DOWN, boundary_type::HORIZONTAL_DOWN) => {
@ -615,7 +619,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL_DOWN, boundary_type::HORIZONTAL_UP) => { (boundary_type::HORIZONTAL_DOWN, boundary_type::HORIZONTAL_UP) => {
@ -624,7 +628,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL_DOWN, boundary_type::CROSS) => { (boundary_type::HORIZONTAL_DOWN, boundary_type::CROSS) => {
@ -633,7 +637,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL_UP, boundary_type::HORIZONTAL_UP) => { (boundary_type::HORIZONTAL_UP, boundary_type::HORIZONTAL_UP) => {
@ -642,7 +646,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::HORIZONTAL_UP, boundary_type::CROSS) => { (boundary_type::HORIZONTAL_UP, boundary_type::CROSS) => {
@ -651,7 +655,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(boundary_type::CROSS, boundary_type::CROSS) => { (boundary_type::CROSS, boundary_type::CROSS) => {
@ -660,7 +664,7 @@ fn combine_symbols(
Some(BoundarySymbol { Some(BoundarySymbol {
boundary_type, boundary_type,
invisible, invisible,
color: color, color,
}) })
} }
(_, _) => None, (_, _) => None,

View file

@ -1,10 +1,8 @@
use crate::utils::consts::ZELLIJ_ROOT_LAYOUT_DIR;
use directories_next::ProjectDirs; use directories_next::ProjectDirs;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use std::{fs::File, io::prelude::*}; use std::{fs::File, io::prelude::*};
use crate::common::wasm_vm::EventType;
use crate::panes::PositionAndSize; use crate::panes::PositionAndSize;
fn split_space_to_parts_vertically( fn split_space_to_parts_vertically(
@ -181,19 +179,15 @@ pub struct Layout {
pub plugin: Option<PathBuf>, pub plugin: Option<PathBuf>,
#[serde(default)] #[serde(default)]
pub expansion_boundary: bool, pub expansion_boundary: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events: Vec<EventType>,
} }
impl Layout { impl Layout {
pub fn new(layout_path: PathBuf) -> Self { pub fn new(layout_path: PathBuf) -> Self {
let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
let layout_dir = project_dirs.data_dir().join("layouts/"); 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) let mut layout_file = File::open(&layout_path)
.or_else(|_| File::open(&layout_path.with_extension("yaml"))) .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(&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())); .unwrap_or_else(|_| panic!("cannot find layout {}", &layout_path.display()));
let mut layout = String::new(); let mut layout = String::new();

View file

@ -120,7 +120,7 @@ impl Pane for PluginPane {
let (buf_tx, buf_rx) = channel(); let (buf_tx, buf_rx) = channel();
self.send_plugin_instructions self.send_plugin_instructions
.send(PluginInstruction::Draw( .send(PluginInstruction::Render(
buf_tx, buf_tx,
self.pid, self.pid,
self.rows(), self.rows(),

View file

@ -23,7 +23,7 @@ pub enum AnsiCode {
On, On,
Reset, Reset,
NamedColor(NamedColor), NamedColor(NamedColor),
RGBCode((u8, u8, u8)), RgbCode((u8, u8, u8)),
ColorIndex(u8), ColorIndex(u8),
} }
@ -336,7 +336,7 @@ impl CharacterStyles {
[36, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))), [36, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
[37, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))), [37, ..] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))),
[38, 2, ..] => { [38, 2, ..] => {
let ansi_code = AnsiCode::RGBCode(( let ansi_code = AnsiCode::RgbCode((
*ansi_params.get(2).unwrap() as u8, *ansi_params.get(2).unwrap() as u8,
*ansi_params.get(3).unwrap() as u8, *ansi_params.get(3).unwrap() as u8,
*ansi_params.get(4).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))), [46, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
[47, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))), [47, ..] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))),
[48, 2, ..] => { [48, 2, ..] => {
let ansi_code = AnsiCode::RGBCode(( let ansi_code = AnsiCode::RgbCode((
*ansi_params.get(2).unwrap() as u8, *ansi_params.get(2).unwrap() as u8,
*ansi_params.get(3).unwrap() as u8, *ansi_params.get(3).unwrap() as u8,
*ansi_params.get(4).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 { if let Some(ansi_code) = self.foreground {
match ansi_code { match ansi_code {
AnsiCode::RGBCode((r, g, b)) => { AnsiCode::RgbCode((r, g, b)) => {
write!(f, "\u{1b}[38;2;{};{};{}m", r, g, b)?; write!(f, "\u{1b}[38;2;{};{};{}m", r, g, b)?;
} }
AnsiCode::ColorIndex(color_index) => { AnsiCode::ColorIndex(color_index) => {
@ -433,7 +433,7 @@ impl Display for CharacterStyles {
}; };
if let Some(ansi_code) = self.background { if let Some(ansi_code) = self.background {
match ansi_code { match ansi_code {
AnsiCode::RGBCode((r, g, b)) => { AnsiCode::RgbCode((r, g, b)) => {
write!(f, "\u{1b}[48;2;{};{};{}m", r, g, b)?; write!(f, "\u{1b}[48;2;{};{};{}m", r, g, b)?;
} }
AnsiCode::ColorIndex(color_index) => { AnsiCode::ColorIndex(color_index) => {

View file

@ -1,23 +1,20 @@
//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size, //! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
//! as well as how they should be resized //! 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::layout::Layout;
use crate::panes::{PaneId, PositionAndSize, TerminalPane}; use crate::panes::{PaneId, PositionAndSize, TerminalPane};
use crate::pty_bus::{PtyInstruction, VteEvent}; use crate::pty_bus::{PtyInstruction, VteEvent};
use crate::wasm_vm::{PluginInputType, PluginInstruction}; use crate::wasm_vm::PluginInstruction;
use crate::{ use crate::{boundaries::Boundaries, panes::PluginPane};
boundaries::Boundaries,
panes::PluginPane,
};
use crate::{os_input_output::OsApi, utils::shared::pad_to_size}; use crate::{os_input_output::OsApi, utils::shared::pad_to_size};
use serde::{Deserialize, Serialize};
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::{ use std::{
cmp::Reverse, cmp::Reverse,
collections::{BTreeMap, HashSet}, collections::{BTreeMap, HashSet},
}; };
use std::{io::Write, sync::mpsc::channel}; 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 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; const MIN_TERMINAL_HEIGHT: usize = 2;
@ -69,15 +66,7 @@ pub struct Tab {
pub send_app_instructions: SenderWithContext<AppInstruction>, pub send_app_instructions: SenderWithContext<AppInstruction>,
expansion_boundary: Option<PositionAndSize>, expansion_boundary: Option<PositionAndSize>,
pub input_mode: InputMode, pub input_mode: InputMode,
pub colors: Palette 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,
} }
// FIXME: Use a struct that has a pane_type enum, to reduce all of the duplication // 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<usize>, max_panes: Option<usize>,
pane_id: Option<PaneId>, pane_id: Option<PaneId>,
input_mode: InputMode, input_mode: InputMode,
colors: Palette colors: Palette,
) -> Self { ) -> Self {
let panes = if let Some(PaneId::Terminal(pid)) = pane_id { let panes = if let Some(PaneId::Terminal(pid)) = pane_id {
let new_terminal = TerminalPane::new(pid, *full_screen_ws); let new_terminal = TerminalPane::new(pid, *full_screen_ws);
@ -226,7 +215,7 @@ impl Tab {
send_plugin_instructions, send_plugin_instructions,
expansion_boundary: None, expansion_boundary: None,
input_mode, input_mode,
colors colors,
} }
} }
@ -271,11 +260,7 @@ impl Tab {
if let Some(plugin) = &layout.plugin { if let Some(plugin) = &layout.plugin {
let (pid_tx, pid_rx) = channel(); let (pid_tx, pid_rx) = channel();
self.send_plugin_instructions self.send_plugin_instructions
.send(PluginInstruction::Load( .send(PluginInstruction::Load(pid_tx, plugin.clone()))
pid_tx,
plugin.clone(),
layout.events.clone(),
))
.unwrap(); .unwrap();
let pid = pid_rx.recv().unwrap(); let pid = pid_rx.recv().unwrap();
let new_plugin = PluginPane::new( let new_plugin = PluginPane::new(
@ -314,7 +299,6 @@ impl Tab {
self.toggle_active_pane_fullscreen(); self.toggle_active_pane_fullscreen();
} }
if !self.has_panes() { if !self.has_panes() {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid { if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws); let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
self.os_api.set_terminal_size_using_fd( 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() if terminal_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.columns()
&& terminal_to_split.rows() > terminal_to_split.min_height() * 2 && terminal_to_split.rows() > terminal_to_split.min_height() * 2
{ {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid { if let PaneId::Terminal(term_pid) = pid {
let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws); let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize); let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
@ -386,7 +369,6 @@ impl Tab {
self.active_terminal = Some(pid); self.active_terminal = Some(pid);
} }
} else if terminal_to_split.columns() > terminal_to_split.min_width() * 2 { } 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 { if let PaneId::Terminal(term_pid) = pid {
let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws); let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, right_winsize); let new_terminal = TerminalPane::new(term_pid, right_winsize);
@ -416,7 +398,6 @@ impl Tab {
self.toggle_active_pane_fullscreen(); self.toggle_active_pane_fullscreen();
} }
if !self.has_panes() { if !self.has_panes() {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid { if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws); let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
self.os_api.set_terminal_size_using_fd( self.os_api.set_terminal_size_using_fd(
@ -427,47 +408,44 @@ impl Tab {
self.panes.insert(pid, Box::new(new_terminal)); self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid); self.active_terminal = Some(pid);
} }
} else { } else if let PaneId::Terminal(term_pid) = pid {
// FIXME: This could use a second look // TODO: check minimum size of active terminal
if let PaneId::Terminal(term_pid) = pid { let active_pane_id = &self.get_active_pane_id().unwrap();
// TODO: check minimum size of active terminal let active_pane = self.panes.get_mut(active_pane_id).unwrap();
let active_pane_id = &self.get_active_pane_id().unwrap(); if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 + 1 {
let active_pane = self.panes.get_mut(active_pane_id).unwrap(); self.send_pty_instructions
if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 + 1 { .send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
self.send_pty_instructions .unwrap();
.send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty return;
.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();
} }
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) { pub fn vertical_split(&mut self, pid: PaneId) {
@ -476,7 +454,6 @@ impl Tab {
self.toggle_active_pane_fullscreen(); self.toggle_active_pane_fullscreen();
} }
if !self.has_panes() { if !self.has_panes() {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid { if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws); let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
self.os_api.set_terminal_size_using_fd( self.os_api.set_terminal_size_using_fd(
@ -487,47 +464,44 @@ impl Tab {
self.panes.insert(pid, Box::new(new_terminal)); self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid); self.active_terminal = Some(pid);
} }
} else { } else if let PaneId::Terminal(term_pid) = pid {
// FIXME: This could use a second look // TODO: check minimum size of active terminal
if let PaneId::Terminal(term_pid) = pid { let active_pane_id = &self.get_active_pane_id().unwrap();
// TODO: check minimum size of active terminal let active_pane = self.panes.get_mut(active_pane_id).unwrap();
let active_pane_id = &self.get_active_pane_id().unwrap(); if active_pane.columns() < MIN_TERMINAL_WIDTH * 2 + 1 {
let active_pane = self.panes.get_mut(active_pane_id).unwrap(); self.send_pty_instructions
if active_pane.columns() < MIN_TERMINAL_WIDTH * 2 + 1 { .send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
self.send_pty_instructions .unwrap();
.send(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty return;
.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();
} }
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> { pub fn get_active_pane(&self) -> Option<&dyn Pane> {
@ -571,12 +545,11 @@ impl Tab {
.expect("failed to drain terminal"); .expect("failed to drain terminal");
} }
Some(PaneId::Plugin(pid)) => { Some(PaneId::Plugin(pid)) => {
self.send_plugin_instructions for key in parse_keys(&input_bytes) {
.send(PluginInstruction::Input( self.send_plugin_instructions
PluginInputType::Normal(pid), .send(PluginInstruction::Update(Some(pid), Event::KeyPress(key)))
input_bytes, .unwrap()
)) }
.unwrap();
} }
_ => {} _ => {}
} }
@ -1694,6 +1667,7 @@ impl Tab {
self.reduce_pane_and_surroundings_right(&active_pane_id, count); self.reduce_pane_and_surroundings_right(&active_pane_id, count);
} }
} }
self.render();
} }
pub fn resize_left(&mut self) { pub fn resize_left(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much // 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.reduce_pane_and_surroundings_left(&active_pane_id, count);
} }
} }
self.render();
} }
pub fn resize_down(&mut self) { pub fn resize_down(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much // 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.reduce_pane_and_surroundings_down(&active_pane_id, count);
} }
} }
self.render();
} }
pub fn resize_up(&mut self) { pub fn resize_up(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much // 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.reduce_pane_and_surroundings_up(&active_pane_id, count);
} }
} }
self.render();
} }
pub fn move_focus(&mut self) { pub fn move_focus(&mut self) {
if !self.has_selectable_panes() { if !self.has_selectable_panes() {

View file

@ -133,7 +133,7 @@ pub enum ContextType {
Plugin(PluginContext), Plugin(PluginContext),
/// An app-related call. /// An app-related call.
App(AppContext), App(AppContext),
IPCServer, IpcServer,
StdinHandler, StdinHandler,
AsyncTask, AsyncTask,
/// An empty, placeholder call. This should be thought of as representing no call at all. /// 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::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c),
ContextType::App(c) => write!(f, "{}main_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 => { ContextType::StdinHandler => {
write!(f, "{}stdin_handler_thread: {}AcceptInput", purple, green) 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. /// Stack call representations corresponding to the different types of [`ScreenInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScreenContext { pub enum ScreenContext {
@ -202,6 +203,7 @@ pub enum ScreenContext {
ChangeInputMode, ChangeInputMode,
} }
// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!!
impl From<&ScreenInstruction> for ScreenContext { impl From<&ScreenInstruction> for ScreenContext {
fn from(screen_instruction: &ScreenInstruction) -> Self { fn from(screen_instruction: &ScreenInstruction) -> Self {
match *screen_instruction { match *screen_instruction {
@ -278,24 +280,20 @@ use crate::wasm_vm::PluginInstruction;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum PluginContext { pub enum PluginContext {
Load, Load,
Draw, Update,
Input, Render,
GlobalInput,
Unload, Unload,
Quit, Quit,
Tabs,
} }
impl From<&PluginInstruction> for PluginContext { impl From<&PluginInstruction> for PluginContext {
fn from(plugin_instruction: &PluginInstruction) -> Self { fn from(plugin_instruction: &PluginInstruction) -> Self {
match *plugin_instruction { match *plugin_instruction {
PluginInstruction::Load(..) => PluginContext::Load, PluginInstruction::Load(..) => PluginContext::Load,
PluginInstruction::Draw(..) => PluginContext::Draw, PluginInstruction::Update(..) => PluginContext::Update,
PluginInstruction::Input(..) => PluginContext::Input, PluginInstruction::Render(..) => PluginContext::Render,
PluginInstruction::GlobalInput(_) => PluginContext::GlobalInput,
PluginInstruction::Unload(_) => PluginContext::Unload, PluginInstruction::Unload(_) => PluginContext::Unload,
PluginInstruction::Quit => PluginContext::Quit, 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. /// Stack call representations corresponding to the different types of [`AppInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum AppContext { pub enum AppContext {
GetState,
SetState,
Exit, Exit,
Error, Error,
} }
@ -312,8 +308,6 @@ pub enum AppContext {
impl From<&AppInstruction> for AppContext { impl From<&AppInstruction> for AppContext {
fn from(app_instruction: &AppInstruction) -> Self { fn from(app_instruction: &AppInstruction) -> Self {
match *app_instruction { match *app_instruction {
AppInstruction::GetState(_) => AppContext::GetState,
AppInstruction::SetState(_) => AppContext::SetState,
AppInstruction::Exit => AppContext::Exit, AppInstruction::Exit => AppContext::Exit,
AppInstruction::Error(_) => AppContext::Error, AppInstruction::Error(_) => AppContext::Error,
} }

View file

@ -1,6 +1,6 @@
//! Definition of the actions that can be bound to keys. //! 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). /// The four directions (left, right, up, down).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -19,7 +19,7 @@ pub enum Action {
/// Write to the terminal. /// Write to the terminal.
Write(Vec<u8>), Write(Vec<u8>),
/// Switch to the specified input mode. /// Switch to the specified input mode.
SwitchToMode(handler::InputMode), SwitchToMode(InputMode),
/// Resize focus pane in specified direction. /// Resize focus pane in specified direction.
Resize(Direction), Resize(Direction),
/// Switch focus to next pane in specified direction. /// Switch focus to next pane in specified direction.
@ -49,5 +49,4 @@ pub enum Action {
CloseTab, CloseTab,
GoToTab(u32), GoToTab(u32),
TabNameInput(Vec<u8>), TabNameInput(Vec<u8>),
SaveTabName,
} }

View file

@ -2,20 +2,16 @@
use super::actions::Action; use super::actions::Action;
use super::keybinds::get_default_keybinds; 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::errors::ContextType;
use crate::os_input_output::OsApi; use crate::os_input_output::OsApi;
use crate::pty_bus::PtyInstruction; use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction; use crate::screen::ScreenInstruction;
use crate::wasm_vm::{EventType, PluginInputType, PluginInstruction}; use crate::wasm_vm::PluginInstruction;
use crate::CommandIsExecuting; 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 termion::input::{TermRead, TermReadEventsAndRaw};
use strum_macros::EnumIter; use zellij_tile::data::{Event, InputMode, Key, ModeInfo, Palette};
use termion::input::TermReadEventsAndRaw;
use super::keybinds::key_to_actions; use super::keybinds::key_to_actions;
@ -65,27 +61,21 @@ impl InputHandler {
'input_loop: loop { 'input_loop: loop {
//@@@ I think this should actually just iterate over stdin directly //@@@ I think this should actually just iterate over stdin directly
let stdin_buffer = self.os_input.read_from_stdin(); 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() { for key_result in stdin_buffer.events_and_raw() {
match key_result { match key_result {
Ok((event, raw_bytes)) => match event { Ok((event, raw_bytes)) => match event {
termion::event::Event::Key(key) => { termion::event::Event::Key(key) => {
let key = cast_termion_key(key);
// FIXME this explicit break is needed because the current test // FIXME this explicit break is needed because the current test
// framework relies on it to not create dead threads that loop // framework relies on it to not create dead threads that loop
// and eat up CPUs. Do not remove until the test framework has // and eat up CPUs. Do not remove until the test framework has
// been revised. Sorry about this (@categorille) // been revised. Sorry about this (@categorille)
if { let mut should_break = false;
let mut should_break = false; for action in key_to_actions(&key, raw_bytes, &self.mode, &keybinds)
for action in {
key_to_actions(&key, raw_bytes, &self.mode, &keybinds) should_break |= self.dispatch_action(action);
{ }
should_break |= self.dispatch_action(action); if should_break {
}
should_break
} {
break 'input_loop; break 'input_loop;
} }
} }
@ -133,11 +123,14 @@ impl InputHandler {
} }
Action::SwitchToMode(mode) => { Action::SwitchToMode(mode) => {
self.mode = mode; self.mode = mode;
update_state(&self.send_app_instructions, |_| AppState { self.send_plugin_instructions
input_mode: self.mode, .send(PluginInstruction::Update(
}); None,
Event::ModeUpdate(get_mode_info(mode)),
))
.unwrap();
self.send_screen_instructions self.send_screen_instructions
.send(ScreenInstruction::ChangeInputMode(self.mode)) .send(ScreenInstruction::ChangeInputMode(mode))
.unwrap(); .unwrap();
self.send_screen_instructions self.send_screen_instructions
.send(ScreenInstruction::Render) .send(ScreenInstruction::Render)
@ -239,27 +232,10 @@ impl InputHandler {
.unwrap(); .unwrap();
} }
Action::TabNameInput(c) => { Action::TabNameInput(c) => {
self.send_plugin_instructions
.send(PluginInstruction::Input(
PluginInputType::Event(EventType::Tab),
c.clone(),
))
.unwrap();
self.send_screen_instructions self.send_screen_instructions
.send(ScreenInstruction::UpdateTabName(c)) .send(ScreenInstruction::UpdateTabName(c))
.unwrap(); .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 => {} 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)>, // <shortcut> => <shortcut description>
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 /// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds
/// (as pairs of [`String`]s). /// (as pairs of [`String`]s).
// TODO this should probably be automatically generated in some way // 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 keybinds: Vec<(String, String)> = vec![];
let mut palette = Palette::new(); let mut palette = Palette::new();
match mode { match mode {
@ -421,7 +284,11 @@ pub fn get_help(mode: InputMode) -> Help {
keybinds.push(("Enter".to_string(), "when done".to_string())); 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 /// Entry point to the module. Instantiates an [`InputHandler`] and starts
@ -444,3 +311,35 @@ pub fn input_loop(
) )
.handle_input(); .handle_input();
} }
pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> {
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!")
}
}
}

View file

@ -1,12 +1,11 @@
//! Mapping of inputs to sequences of actions. //! Mapping of inputs to sequences of actions.
use super::actions::{Action, Direction}; use super::actions::{Action, Direction};
use super::handler::InputMode;
use std::collections::HashMap; use std::collections::HashMap;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use termion::event::Key; use zellij_tile::data::*;
type Keybinds = HashMap<InputMode, ModeKeybinds>; type Keybinds = HashMap<InputMode, ModeKeybinds>;
type ModeKeybinds = HashMap<Key, Vec<Action>>; type ModeKeybinds = HashMap<Key, Vec<Action>>;
@ -17,14 +16,14 @@ pub fn get_default_keybinds() -> Result<Keybinds, String> {
let mut defaults = Keybinds::new(); let mut defaults = Keybinds::new();
for mode in InputMode::iter() { for mode in InputMode::iter() {
defaults.insert(mode, get_defaults_for_mode(&mode)?); defaults.insert(mode, get_defaults_for_mode(&mode));
} }
Ok(defaults) Ok(defaults)
} }
/// Returns the default keybinds for a givent [`InputMode`]. /// Returns the default keybinds for a givent [`InputMode`].
fn get_defaults_for_mode(mode: &InputMode) -> Result<ModeKeybinds, String> { fn get_defaults_for_mode(mode: &InputMode) -> ModeKeybinds {
let mut defaults = ModeKeybinds::new(); let mut defaults = ModeKeybinds::new();
match *mode { match *mode {
@ -227,10 +226,7 @@ fn get_defaults_for_mode(mode: &InputMode) -> Result<ModeKeybinds, String> {
defaults.insert(Key::Up, vec![Action::ScrollUp]); defaults.insert(Key::Up, vec![Action::ScrollUp]);
} }
InputMode::RenameTab => { InputMode::RenameTab => {
defaults.insert( defaults.insert(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]);
Key::Char('\n'),
vec![Action::SaveTabName, Action::SwitchToMode(InputMode::Tab)],
);
defaults.insert( defaults.insert(
Key::Ctrl('g'), Key::Ctrl('g'),
vec![Action::SwitchToMode(InputMode::Normal)], vec![Action::SwitchToMode(InputMode::Normal)],
@ -245,7 +241,7 @@ fn get_defaults_for_mode(mode: &InputMode) -> Result<ModeKeybinds, String> {
} }
} }
Ok(defaults) defaults
} }
/// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current

View file

@ -3,12 +3,12 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::collections::HashSet;
type SessionID = u64; type SessionId = u64;
#[derive(PartialEq, Eq, Serialize, Deserialize, Hash)] #[derive(PartialEq, Eq, Serialize, Deserialize, Hash)]
pub struct Session { pub struct Session {
// Unique ID for this session // Unique ID for this session
id: SessionID, id: SessionId,
// Identifier for the underlying IPC primitive (socket, pipe) // Identifier for the underlying IPC primitive (socket, pipe)
conn_name: String, conn_name: String,
// User configured alias for the session // User configured alias for the session
@ -30,9 +30,9 @@ pub enum ClientToServerMsg {
// Create a new session // Create a new session
CreateSession, CreateSession,
// Attach to a running session // Attach to a running session
AttachToSession(SessionID, ClientType), AttachToSession(SessionId, ClientType),
// Force detach // Force detach
DetachSession(SessionID), DetachSession(SessionId),
// Disconnect from the session we're connected to // Disconnect from the session we're connected to
DisconnectFromSession, DisconnectFromSession,
} }

View file

@ -9,38 +9,35 @@ pub mod screen;
pub mod utils; pub mod utils;
pub mod wasm_vm; pub mod wasm_vm;
use std::io::Write; use std::cell::RefCell;
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::{cell::RefCell, sync::mpsc::TrySendError};
use std::{collections::HashMap, fs}; use std::{collections::HashMap, fs};
use xrdb::Colors; use std::{
use colors_transform::{Rgb, Color}; collections::HashSet,
use ansi_term::Colour::RGB; io::Write,
str::FromStr,
use crate::panes::PaneId; sync::{Arc, Mutex},
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 crate::cli::CliArgs; use crate::cli::CliArgs;
use crate::layout::Layout; use crate::layout::Layout;
use crate::common::input::handler::Palette; use crate::panes::PaneId;
use command_is_executing::CommandIsExecuting; use command_is_executing::CommandIsExecuting;
use directories_next::ProjectDirs;
use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext}; use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext};
use input::handler::input_loop; use input::handler::input_loop;
use os_input_output::OsApi; use os_input_output::OsApi;
use pty_bus::{PtyBus, PtyInstruction}; use pty_bus::{PtyBus, PtyInstruction};
use screen::{Screen, ScreenInstruction}; use screen::{Screen, ScreenInstruction};
use utils::{consts::{ZELLIJ_IPC_PIPE, ZELLIJ_ROOT_PLUGIN_DIR}, logging::debug_log_to_file}; use serde::{Deserialize, Serialize};
use wasm_vm::{ use utils::consts::ZELLIJ_IPC_PIPE;
wasi_stdout, wasi_write_string, zellij_imports, EventType, PluginInputType, PluginInstruction, 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)] #[derive(Serialize, Deserialize, Debug)]
pub enum ApiCommand { pub enum ApiCommand {
@ -49,25 +46,6 @@ pub enum ApiCommand {
SplitVertically, SplitVertically,
MoveFocus, 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<AppInstruction>,
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. /// An [MPSC](mpsc) asynchronous channel with added error context.
pub type ChannelWithContext<T> = ( pub type ChannelWithContext<T> = (
@ -111,19 +89,6 @@ impl<T: Clone> SenderWithContext<T> {
} }
} }
/// 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 /// Updates this [`SenderWithContext`]'s [`ErrorContext`]. This is the way one adds
/// a call to the error context. /// a call to the error context.
/// ///
@ -146,19 +111,10 @@ thread_local!(
/// Instructions related to the entire application. /// Instructions related to the entire application.
#[derive(Clone)] #[derive(Clone)]
pub enum AppInstruction { pub enum AppInstruction {
GetState(mpsc::Sender<AppState>),
SetState(AppState),
Exit, Exit,
Error(String), 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. /// Start Zellij with the specified [`OsApi`] and command-line arguments.
// FIXME this should definitely be modularized and split into different functions. // FIXME this should definitely be modularized and split into different functions.
pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) { pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
@ -168,7 +124,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.get_stdout_writer() .get_stdout_writer()
.write(take_snapshot.as_bytes()) .write(take_snapshot.as_bytes())
.unwrap(); .unwrap();
let mut app_state = AppState::default();
let command_is_executing = CommandIsExecuting::new(); let command_is_executing = CommandIsExecuting::new();
@ -303,7 +258,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
os_input, os_input,
max_panes, max_panes,
InputMode::Normal, InputMode::Normal,
colors colors,
); );
loop { loop {
let (event, mut err_ctx) = screen let (event, mut err_ctx) = screen
@ -395,8 +350,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.set_pane_selectable(id, selectable); .set_pane_selectable(id, selectable);
// FIXME: Is this needed?
screen.render();
} }
ScreenInstruction::SetMaxHeight(id, max_height) => { ScreenInstruction::SetMaxHeight(id, max_height) => {
screen screen
@ -460,12 +413,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let store = Store::default(); let store = Store::default();
let mut plugin_id = 0; let mut plugin_id = 0;
let mut plugin_map = HashMap::new(); let mut plugin_map = HashMap::new();
let handler_map: HashMap<EventType, String> =
[(EventType::Tab, "handle_tab_rename_keypress".to_string())]
.iter()
.cloned()
.collect();
move || loop { move || loop {
let (event, mut err_ctx) = receive_plugin_instructions let (event, mut err_ctx) = receive_plugin_instructions
.recv() .recv()
@ -475,18 +422,13 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
send_pty_instructions.update(err_ctx); send_pty_instructions.update(err_ctx);
send_app_instructions.update(err_ctx); send_app_instructions.update(err_ctx);
match event { match event {
PluginInstruction::Load(pid_tx, path, events) => { PluginInstruction::Load(pid_tx, path) => {
let project_dirs = let project_dirs =
ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
let plugin_dir = project_dirs.data_dir().join("plugins/"); 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) let wasm_bytes = fs::read(&path)
.or_else(|_| fs::read(&path.with_extension("wasm"))) .or_else(|_| fs::read(&path.with_extension("wasm")))
.or_else(|_| fs::read(&plugin_dir.join(&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())); .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 // 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<dyn OsApi>, opts: CliArgs) {
send_screen_instructions: send_screen_instructions.clone(), send_screen_instructions: send_screen_instructions.clone(),
send_app_instructions: send_app_instructions.clone(), send_app_instructions: send_app_instructions.clone(),
wasi_env, wasi_env,
events, subscriptions: Arc::new(Mutex::new(HashSet::new())),
}; };
let zellij = zellij_imports(&store, &plugin_env); let zellij = zellij_imports(&store, &plugin_env);
@ -532,88 +474,33 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
pid_tx.send(plugin_id).unwrap(); pid_tx.send(plugin_id).unwrap();
plugin_id += 1; 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 (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(); .unwrap();
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).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::Unload(pid) => drop(plugin_map.remove(&pid)),
PluginInstruction::Quit => break, PluginInstruction::Quit => break,
} }
@ -636,7 +523,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE) let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE)
.expect("could not listen on ipc socket"); .expect("could not listen on ipc socket");
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); 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_pty_instructions.update(err_ctx);
send_screen_instructions.update(err_ctx); send_screen_instructions.update(err_ctx);
@ -711,8 +598,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
send_screen_instructions.update(err_ctx); send_screen_instructions.update(err_ctx);
send_pty_instructions.update(err_ctx); send_pty_instructions.update(err_ctx);
match app_instruction { match app_instruction {
AppInstruction::GetState(state_tx) => drop(state_tx.send(app_state.clone())),
AppInstruction::SetState(state) => app_state = state,
AppInstruction::Exit => { AppInstruction::Exit => {
break; break;
} }

View file

@ -82,15 +82,10 @@ fn handle_command_exit(mut child: Child) {
} }
for signal in signals.pending() { for signal in signals.pending() {
// FIXME: We need to handle more signals here! if signal == signal_hook::SIGINT {
#[allow(clippy::single_match)] child.kill().unwrap();
match signal { child.wait().unwrap();
signal_hook::SIGINT => { break 'handle_exit;
child.kill().unwrap();
child.wait().unwrap();
break 'handle_exit;
}
_ => {}
} }
} }
} }

View file

@ -5,14 +5,16 @@ use std::os::unix::io::RawFd;
use std::str; use std::str;
use std::sync::mpsc::Receiver; 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::os_input_output::OsApi;
use crate::panes::PositionAndSize; use crate::panes::PositionAndSize;
use crate::pty_bus::{PtyInstruction, VteEvent}; use crate::pty_bus::{PtyInstruction, VteEvent};
use crate::tab::{Tab, TabData}; use crate::tab::Tab;
use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction};
use crate::{layout::Layout, panes::PaneId}; use crate::{layout::Layout, panes::PaneId};
use zellij_tile::data::{Event, InputMode, Palette, TabInfo};
/// Instructions that can be sent to the [`Screen`]. /// Instructions that can be sent to the [`Screen`].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ScreenInstruction { pub enum ScreenInstruction {
@ -72,9 +74,8 @@ pub struct Screen {
active_tab_index: Option<usize>, active_tab_index: Option<usize>,
/// The [`OsApi`] this [`Screen`] uses. /// The [`OsApi`] this [`Screen`] uses.
os_api: Box<dyn OsApi>, os_api: Box<dyn OsApi>,
tabname_buf: String,
input_mode: InputMode, input_mode: InputMode,
colors: Palette colors: Palette,
} }
impl Screen { impl Screen {
@ -88,7 +89,7 @@ impl Screen {
os_api: Box<dyn OsApi>, os_api: Box<dyn OsApi>,
max_panes: Option<usize>, max_panes: Option<usize>,
input_mode: InputMode, input_mode: InputMode,
colors: Palette colors: Palette,
) -> Self { ) -> Self {
Screen { Screen {
receiver: receive_screen_instructions, receiver: receive_screen_instructions,
@ -100,9 +101,8 @@ impl Screen {
active_tab_index: None, active_tab_index: None,
tabs: BTreeMap::new(), tabs: BTreeMap::new(),
os_api, os_api,
tabname_buf: String::new(),
input_mode, input_mode,
colors colors,
} }
} }
@ -123,7 +123,7 @@ impl Screen {
self.max_panes, self.max_panes,
Some(PaneId::Terminal(pane_id)), Some(PaneId::Terminal(pane_id)),
self.input_mode, self.input_mode,
self.colors self.colors,
); );
self.active_tab_index = Some(tab_index); self.active_tab_index = Some(tab_index);
self.tabs.insert(tab_index, tab); self.tabs.insert(tab_index, tab);
@ -178,15 +178,12 @@ impl Screen {
pub fn go_to_tab(&mut self, mut tab_index: usize) { pub fn go_to_tab(&mut self, mut tab_index: usize) {
tab_index -= 1; tab_index -= 1;
let active_tab = self.get_active_tab().unwrap(); let active_tab = self.get_active_tab().unwrap();
match self.tabs.values().find(|t| t.position == tab_index) { if let Some(t) = self.tabs.values().find(|t| t.position == tab_index) {
Some(t) => { if t.index != active_tab.index {
if t.index != active_tab.index { self.active_tab_index = Some(t.index);
self.active_tab_index = Some(t.index); self.update_tabs();
self.update_tabs(); self.render();
self.render();
}
} }
None => {}
} }
} }
@ -269,7 +266,7 @@ impl Screen {
self.max_panes, self.max_panes,
None, None,
self.input_mode, self.input_mode,
self.colors self.colors,
); );
tab.apply_layout(layout, new_pids); tab.apply_layout(layout, new_pids);
self.active_tab_index = Some(tab_index); self.active_tab_index = Some(tab_index);
@ -281,38 +278,33 @@ impl Screen {
let mut tab_data = vec![]; let mut tab_data = vec![];
let active_tab_index = self.active_tab_index.unwrap(); let active_tab_index = self.active_tab_index.unwrap();
for tab in self.tabs.values() { for tab in self.tabs.values() {
tab_data.push(TabData { tab_data.push(TabInfo {
position: tab.position, position: tab.position,
name: tab.name.clone(), name: tab.name.clone(),
active: active_tab_index == tab.index, active: active_tab_index == tab.index,
}); });
} }
self.send_plugin_instructions self.send_plugin_instructions
.send(PluginInstruction::UpdateTabs(tab_data)) .send(PluginInstruction::Update(None, Event::TabUpdate(tab_data)))
.unwrap(); .unwrap();
} }
pub fn update_active_tab_name(&mut self, buf: Vec<u8>) { pub fn update_active_tab_name(&mut self, buf: Vec<u8>) {
let s = str::from_utf8(&buf).unwrap(); let s = str::from_utf8(&buf).unwrap();
let active_tab = self.get_active_tab_mut().unwrap();
match s { match s {
"\0" => { "\0" => {
self.tabname_buf = String::new(); active_tab.name = 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();
} }
"\u{007F}" | "\u{0008}" => { "\u{007F}" | "\u{0008}" => {
//delete and backspace keys //delete and backspace keys
self.tabname_buf.pop(); active_tab.name.pop();
} }
c => { 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) { pub fn change_input_mode(&mut self, input_mode: InputMode) {
self.input_mode = input_mode; self.input_mode = input_mode;

View file

@ -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_DIR: &str = "/tmp/zellij/zellij-log";
pub const ZELLIJ_TMP_LOG_FILE: &str = "/tmp/zellij/zellij-log/log.txt"; 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_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";

View file

@ -1,36 +1,22 @@
use crate::tab::TabData;
use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashSet,
path::PathBuf, path::PathBuf,
sync::mpsc::{channel, Sender}, sync::{mpsc::Sender, Arc, Mutex},
}; };
use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv};
use wasmer_wasi::WasiEnv; use wasmer_wasi::WasiEnv;
use zellij_tile::data::{Event, EventType};
use super::{ use super::{
input::handler::get_help, pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, PaneId, SenderWithContext,
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)] #[derive(Clone, Debug)]
pub enum PluginInstruction { pub enum PluginInstruction {
Load(Sender<u32>, PathBuf, Vec<EventType>), Load(Sender<u32>, PathBuf),
Draw(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols Update(Option<u32>, Event), // Focused plugin / broadcast, event data
Input(PluginInputType, Vec<u8>), // plugin id, input bytes Render(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
GlobalInput(Vec<u8>), // input bytes
Unload(u32), Unload(u32),
UpdateTabs(Vec<TabData>), // num tabs, active tab
Quit, Quit,
} }
@ -41,7 +27,7 @@ pub struct PluginEnv {
pub send_app_instructions: SenderWithContext<AppInstruction>, pub send_app_instructions: SenderWithContext<AppInstruction>,
pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels
pub wasi_env: WasiEnv, pub wasi_env: WasiEnv,
pub events: Vec<EventType>, pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
} }
// Plugin API --------------------------------------------------------------------------------------------------------- // Plugin API ---------------------------------------------------------------------------------------------------------
@ -49,16 +35,28 @@ pub struct PluginEnv {
pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
imports! { imports! {
"zellij" => { "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_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_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_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_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<EventType> = 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<EventType> = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap();
subscriptions.retain(|k| !old.contains(k));
}
fn host_open_file(plugin_env: &PluginEnv) { fn host_open_file(plugin_env: &PluginEnv) {
let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap());
plugin_env plugin_env
@ -67,7 +65,6 @@ fn host_open_file(plugin_env: &PluginEnv) {
.unwrap(); .unwrap();
} }
// FIXME: Think about these naming conventions should everything be prefixed by 'host'?
fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) { fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) {
let selectable = selectable != 0; let selectable = selectable != 0;
plugin_env plugin_env
@ -101,21 +98,6 @@ fn host_set_invisible_borders(plugin_env: &PluginEnv, invisible_borders: i32) {
.unwrap() .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 --------------------------------------------------------------------------------------------------- // Helper Functions ---------------------------------------------------------------------------------------------------
// FIXME: Unwrap city // FIXME: Unwrap city

View file

@ -8,4 +8,8 @@ license = "MIT"
[dependencies] [dependencies]
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
strum = "0.20.0"
strum_macros = "0.20.0"
xrdb = "0.1.1"
colors-transform = "0.2.5"

162
zellij-tile/src/data.rs Normal file
View file

@ -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<TabInfo>),
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)>, // <shortcut> => <shortcut description>
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,
}

View file

@ -1,14 +1,14 @@
mod shim; pub mod data;
pub mod prelude;
pub mod shim;
use data::*;
pub use shim::*;
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait ZellijTile { pub trait ZellijTile {
fn init(&mut self) {} fn load(&mut self) {}
fn draw(&mut self, rows: usize, cols: usize) {} fn update(&mut self, event: Event) {}
fn handle_key(&mut self, key: Key) {} fn render(&mut self, rows: usize, cols: usize) {}
fn handle_global_key(&mut self, key: Key) {}
fn update_tabs(&mut self) {}
fn handle_tab_rename_keypress(&mut self, key: Key) {}
} }
#[macro_export] #[macro_export]
@ -20,45 +20,22 @@ macro_rules! register_tile {
fn main() { fn main() {
STATE.with(|state| { STATE.with(|state| {
state.borrow_mut().init(); state.borrow_mut().load();
}); });
} }
#[no_mangle] #[no_mangle]
pub fn draw(rows: i32, cols: i32) { pub fn update() {
STATE.with(|state| { STATE.with(|state| {
state.borrow_mut().draw(rows as usize, cols as usize); state.borrow_mut().update($crate::shim::object_from_stdin());
}); });
} }
#[no_mangle] #[no_mangle]
pub fn handle_key() { pub fn render(rows: i32, cols: i32) {
STATE.with(|state| { 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());
})
}
}; };
} }

View file

@ -0,0 +1,3 @@
pub use crate::data::*;
pub use crate::shim::*;
pub use crate::*;

View file

@ -1,123 +1,56 @@
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::de::DeserializeOwned;
use std::{io, path::Path}; use std::{io, path::Path};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] use crate::data::*;
pub enum Key {
Backspace, // Subscription Handling
Left,
Right, pub fn subscribe(event_types: &[EventType]) {
Up, println!("{}", serde_json::to_string(event_types).unwrap());
Down, unsafe { host_subscribe() };
Home,
End,
PageUp,
PageDown,
BackTab,
Delete,
Insert,
F(u8),
Char(char),
Alt(char),
Ctrl(char),
Null,
Esc,
} }
// TODO: use same struct from main crate? pub fn unsubscribe(event_types: &[EventType]) {
#[derive(Default, Debug, Clone, Serialize, Deserialize)] println!("{}", serde_json::to_string(event_types).unwrap());
pub struct Help { unsafe { host_unsubscribe() };
pub mode: InputMode,
pub keybinds: Vec<(String, String)>,
pub palette: Palette
} }
// TODO: use same struct from main crate? // Plugin Settings
#[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() };
}
pub fn set_max_height(max_height: i32) { pub fn set_max_height(max_height: i32) {
unsafe { host_set_max_height(max_height) }; unsafe { host_set_max_height(max_height) };
} }
pub fn set_invisible_borders(invisible_borders: bool) { pub fn set_invisible_borders(invisible_borders: bool) {
let invisible_borders = if invisible_borders { 1 } else { 0 }; unsafe { host_set_invisible_borders(if invisible_borders { 1 } else { 0 }) };
unsafe { host_set_invisible_borders(invisible_borders) };
} }
pub fn set_selectable(selectable: bool) { pub fn set_selectable(selectable: bool) {
let selectable = if selectable { 1 } else { 0 }; unsafe { host_set_selectable(if selectable { 1 } else { 0 }) };
unsafe { host_set_selectable(selectable) };
} }
pub fn get_help() -> Help { // Host Functions
unsafe { host_get_help() };
deserialize_from_stdin().unwrap_or_default() pub fn open_file(path: &Path) {
println!("{}", path.to_string_lossy());
unsafe { host_open_file() };
} }
pub fn get_tabs() -> Vec<TabData> { // Internal Functions
deserialize_from_stdin().unwrap_or_default()
}
fn deserialize_from_stdin<T: DeserializeOwned>() -> Option<T> { #[doc(hidden)]
pub fn object_from_stdin<T: DeserializeOwned>() -> T {
let mut json = String::new(); let mut json = String::new();
io::stdin().read_line(&mut json).unwrap(); io::stdin().read_line(&mut json).unwrap();
serde_json::from_str(&json).ok() serde_json::from_str(&json).unwrap()
} }
#[link(wasm_import_module = "zellij")] #[link(wasm_import_module = "zellij")]
extern "C" { extern "C" {
fn host_subscribe();
fn host_unsubscribe();
fn host_open_file(); fn host_open_file();
fn host_set_max_height(max_height: i32); fn host_set_max_height(max_height: i32);
fn host_set_selectable(selectable: i32); fn host_set_selectable(selectable: i32);
fn host_set_invisible_borders(invisible_borders: i32); fn host_set_invisible_borders(invisible_borders: i32);
fn host_get_help();
} }