diff --git a/assets/layouts/default.yaml b/assets/layouts/default.yaml index f06bab17..463c5e2d 100644 --- a/assets/layouts/default.yaml +++ b/assets/layouts/default.yaml @@ -5,6 +5,8 @@ parts: split_size: Fixed: 1 plugin: tab-bar + events: + - Tab - direction: Vertical expansion_boundary: true - direction: Vertical diff --git a/default-tiles/status-bar/src/main.rs b/default-tiles/status-bar/src/main.rs index e5de067d..1b64a304 100644 --- a/default-tiles/status-bar/src/main.rs +++ b/default-tiles/status-bar/src/main.rs @@ -104,7 +104,7 @@ fn key_path(help: &Help) -> LinePart { len, ) } - InputMode::Tab => { + InputMode::Tab | InputMode::RenameTab => { let mode_shortcut_text = "t "; let superkey = superkey_text.bold().on_magenta(); let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black(); diff --git a/default-tiles/tab-bar/src/line.rs b/default-tiles/tab-bar/src/line.rs index f28fab3f..306c0281 100644 --- a/default-tiles/tab-bar/src/line.rs +++ b/default-tiles/tab-bar/src/line.rs @@ -115,9 +115,7 @@ fn add_next_tabs_msg( title_bar: &mut Vec, cols: usize, ) { - while get_current_title_len(&title_bar) + - // get_tabs_after_len(tabs_after_active.len()) >= cols { - right_more_message(tabs_after_active.len()).len + while get_current_title_len(&title_bar) + right_more_message(tabs_after_active.len()).len >= cols { tabs_after_active.insert(0, title_bar.pop().unwrap()); diff --git a/default-tiles/tab-bar/src/main.rs b/default-tiles/tab-bar/src/main.rs index 8a334334..9f1c10ea 100644 --- a/default-tiles/tab-bar/src/main.rs +++ b/default-tiles/tab-bar/src/main.rs @@ -4,7 +4,7 @@ mod tab; use zellij_tile::*; use crate::line::tab_line; -use crate::tab::nameless_tab; +use crate::tab::tab_style; #[derive(Debug)] pub struct LinePart { @@ -12,10 +12,25 @@ pub struct LinePart { len: usize, } +#[derive(PartialEq)] +enum BarMode { + Normal, + Rename, +} + +impl Default for BarMode { + fn default() -> Self { + BarMode::Normal + } +} + #[derive(Default)] struct State { active_tab_index: usize, num_tabs: usize, + tabs: Vec, + mode: BarMode, + new_name: String, } static ARROW_SEPARATOR: &str = ""; @@ -29,20 +44,32 @@ impl ZellijTile for State { set_max_height(1); self.active_tab_index = 0; self.num_tabs = 0; + self.mode = BarMode::Normal; + self.new_name = String::new(); } fn draw(&mut self, _rows: usize, cols: usize) { - if self.num_tabs == 0 { + if self.tabs.is_empty() { return; } let mut all_tabs: Vec = vec![]; - for i in 0..self.num_tabs { - let tab = nameless_tab(i, i == self.active_tab_index); + let mut active_tab_index = 0; + for t in self.tabs.iter_mut() { + let mut tabname = t.name.clone(); + if t.active && self.mode == BarMode::Rename { + if self.new_name.is_empty() { + tabname = String::from("Enter name..."); + } else { + tabname = self.new_name.clone(); + } + active_tab_index = t.position; + } else if t.active { + active_tab_index = t.position; + } + let tab = tab_style(tabname, t.active, t.position); all_tabs.push(tab); } - - let tab_line = tab_line(all_tabs, self.active_tab_index, cols); - + let tab_line = tab_line(all_tabs, active_tab_index, cols); let mut s = String::new(); for bar_part in tab_line { s = format!("{}{}", s, bar_part.part); @@ -50,8 +77,19 @@ impl ZellijTile for State { println!("{}\u{1b}[40m\u{1b}[0K", s); } - fn update_tabs(&mut self, active_tab_index: usize, num_tabs: usize) { - self.active_tab_index = active_tab_index; - self.num_tabs = num_tabs; + fn update_tabs(&mut self) { + self.tabs = get_tabs(); + } + + fn handle_tab_rename_keypress(&mut self, key: Key) { + self.mode = BarMode::Rename; + match key { + Key::Char('\n') | Key::Esc => { + self.mode = BarMode::Normal; + self.new_name.clear(); + } + Key::Char(c) => self.new_name = format!("{}{}", self.new_name, c), + _ => {} + } } } diff --git a/default-tiles/tab-bar/src/tab.rs b/default-tiles/tab-bar/src/tab.rs index 78a9ba0b..fa345ba0 100644 --- a/default-tiles/tab-bar/src/tab.rs +++ b/default-tiles/tab-bar/src/tab.rs @@ -9,11 +9,11 @@ pub fn active_tab(text: String, is_furthest_to_the_left: bool) -> LinePart { ARROW_SEPARATOR.black().on_magenta() }; let right_separator = ARROW_SEPARATOR.magenta().on_black(); - let tab_styled_text = format!("{}{}{}", left_separator, text, right_separator) + let tab_styled_text = format!("{} {} {}", left_separator, text, right_separator) .black() .bold() .on_magenta(); - let tab_text_len = text.chars().count() + 2; // 2 for left and right separators + let tab_text_len = text.chars().count() + 4; // 2 for left and right separators, 2 for the text padding LinePart { part: format!("{}", tab_styled_text), len: tab_text_len, @@ -27,26 +27,27 @@ pub fn non_active_tab(text: String, is_furthest_to_the_left: bool) -> LinePart { ARROW_SEPARATOR.black().on_green() }; let right_separator = ARROW_SEPARATOR.green().on_black(); - let tab_styled_text = format!("{}{}{}", left_separator, text, right_separator) + let tab_styled_text = format!("{} {} {}", left_separator, text, right_separator) .black() .bold() .on_green(); - let tab_text_len = text.chars().count() + 2; // 2 for the left and right separators + let tab_text_len = text.chars().count() + 4; // 2 for the left and right separators, 2 for the text padding LinePart { part: format!("{}", tab_styled_text), len: tab_text_len, } } -pub fn tab(text: String, is_active_tab: bool, is_furthest_to_the_left: bool) -> LinePart { - if is_active_tab { - active_tab(text, is_furthest_to_the_left) +pub fn tab_style(text: String, is_active_tab: bool, position: usize) -> LinePart { + let tab_text; + if text.is_empty() { + tab_text = format!("Tab #{}", position + 1); } else { - non_active_tab(text, is_furthest_to_the_left) + tab_text = text; + } + if is_active_tab { + active_tab(tab_text, position == 0) + } else { + non_active_tab(tab_text, position == 0) } } - -pub fn nameless_tab(index: usize, is_active_tab: bool) -> LinePart { - let tab_text = format!(" Tab #{} ", index + 1); - tab(tab_text, is_active_tab, index == 0) -} diff --git a/src/client/layout.rs b/src/client/layout.rs index c032be38..1b383540 100644 --- a/src/client/layout.rs +++ b/src/client/layout.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::{fs::File, io::prelude::*}; +use crate::common::wasm_vm::EventType; use crate::panes::PositionAndSize; fn split_space_to_parts_vertically( @@ -180,6 +181,8 @@ pub struct Layout { pub plugin: Option, #[serde(default)] pub expansion_boundary: bool, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec, } impl Layout { diff --git a/src/client/tab.rs b/src/client/tab.rs index bf096367..1ef0b720 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -2,11 +2,13 @@ //! as well as how they should be resized use crate::common::{AppInstruction, SenderWithContext}; +use crate::layout::Layout; use crate::panes::{PaneId, PositionAndSize, TerminalPane}; use crate::pty_bus::{PtyInstruction, VteEvent}; +use crate::wasm_vm::{PluginInputType, PluginInstruction}; use crate::{boundaries::Boundaries, panes::PluginPane}; -use crate::{layout::Layout, wasm_vm::PluginInstruction}; use crate::{os_input_output::OsApi, utils::shared::pad_to_size}; +use serde::{Deserialize, Serialize}; use std::os::unix::io::RawFd; use std::{ cmp::Reverse, @@ -51,6 +53,7 @@ fn split_horizontally_with_gap(rect: &PositionAndSize) -> (PositionAndSize, Posi pub struct Tab { pub index: usize, pub position: usize, + pub name: String, panes: BTreeMap>, panes_to_hide: HashSet, active_terminal: Option, @@ -64,6 +67,14 @@ pub struct Tab { expansion_boundary: Option, } +#[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 pub trait Pane { fn x(&self) -> usize; @@ -170,6 +181,7 @@ impl Tab { pub fn new( index: usize, position: usize, + name: String, full_screen_ws: &PositionAndSize, mut os_api: Box, send_pty_instructions: SenderWithContext, @@ -195,6 +207,7 @@ impl Tab { index, position, panes, + name, max_panes, panes_to_hide: HashSet::new(), active_terminal: pane_id, @@ -249,7 +262,11 @@ impl Tab { if let Some(plugin) = &layout.plugin { let (pid_tx, pid_rx) = channel(); self.send_plugin_instructions - .send(PluginInstruction::Load(pid_tx, plugin.clone())) + .send(PluginInstruction::Load( + pid_tx, + plugin.clone(), + layout.events.clone(), + )) .unwrap(); let pid = pid_rx.recv().unwrap(); let new_plugin = PluginPane::new( @@ -546,7 +563,10 @@ impl Tab { } Some(PaneId::Plugin(pid)) => { self.send_plugin_instructions - .send(PluginInstruction::Input(pid, input_bytes)) + .send(PluginInstruction::Input( + PluginInputType::Normal(pid), + input_bytes, + )) .unwrap(); } _ => {} diff --git a/src/common/errors.rs b/src/common/errors.rs index 03aec714..df741629 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -198,6 +198,7 @@ pub enum ScreenContext { SwitchTabPrev, CloseTab, GoToTab, + UpdateTabName, } impl From<&ScreenInstruction> for ScreenContext { @@ -236,6 +237,7 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev, ScreenInstruction::CloseTab => ScreenContext::CloseTab, ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab, + ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName, } } } diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs index fe5e2dad..88a7036f 100644 --- a/src/common/input/actions.rs +++ b/src/common/input/actions.rs @@ -48,4 +48,6 @@ pub enum Action { /// Close the current tab. CloseTab, GoToTab(u32), + TabNameInput(Vec), + SaveTabName, } diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index 5c4f1971..52db9b4f 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -7,7 +7,7 @@ use crate::errors::ContextType; use crate::os_input_output::OsApi; use crate::pty_bus::PtyInstruction; use crate::screen::ScreenInstruction; -use crate::wasm_vm::PluginInstruction; +use crate::wasm_vm::{EventType, PluginInputType, PluginInstruction}; use crate::CommandIsExecuting; use serde::{Deserialize, Serialize}; @@ -232,6 +232,28 @@ impl InputHandler { .send(ScreenInstruction::GoToTab(i)) .unwrap(); } + Action::TabNameInput(c) => { + self.send_plugin_instructions + .send(PluginInstruction::Input( + PluginInputType::Event(EventType::Tab), + c.clone(), + )) + .unwrap(); + self.send_screen_instructions + .send(ScreenInstruction::UpdateTabName(c)) + .unwrap(); + } + Action::SaveTabName => { + self.send_plugin_instructions + .send(PluginInstruction::Input( + PluginInputType::Event(EventType::Tab), + vec![b'\n'], + )) + .unwrap(); + self.send_screen_instructions + .send(ScreenInstruction::UpdateTabName(vec![b'\n'])) + .unwrap(); + } Action::NoOp => {} } @@ -265,6 +287,7 @@ pub enum InputMode { 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, @@ -310,10 +333,14 @@ pub fn get_help(mode: InputMode) -> Help { keybinds.push(("←↓↑→".to_string(), "Move focus".to_string())); keybinds.push(("n".to_string(), "New".to_string())); keybinds.push(("x".to_string(), "Close".to_string())); + keybinds.push(("r".to_string(), "Rename".to_string())); } InputMode::Scroll => { keybinds.push(("↓↑".to_string(), "Scroll".to_string())); } + InputMode::RenameTab => { + keybinds.push(("Enter".to_string(), "when done".to_string())); + } } keybinds.push(("ESC".to_string(), "BACK".to_string())); keybinds.push(("q".to_string(), "QUIT".to_string())); diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs index e56488b4..46abf4ab 100644 --- a/src/common/input/keybinds.rs +++ b/src/common/input/keybinds.rs @@ -128,6 +128,13 @@ fn get_defaults_for_mode(mode: &InputMode) -> Result { defaults.insert(Key::Char('n'), vec![Action::NewTab]); defaults.insert(Key::Char('x'), vec![Action::CloseTab]); + defaults.insert( + Key::Char('r'), + vec![ + Action::SwitchToMode(InputMode::RenameTab), + Action::TabNameInput(vec![0]), + ], + ); defaults.insert(Key::Char('q'), vec![Action::Quit]); defaults.insert( Key::Ctrl('g'), @@ -155,6 +162,23 @@ fn get_defaults_for_mode(mode: &InputMode) -> Result { ); defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]); } + InputMode::RenameTab => { + defaults.insert( + Key::Char('\n'), + vec![Action::SaveTabName, Action::SwitchToMode(InputMode::Tab)], + ); + defaults.insert( + Key::Ctrl('g'), + vec![Action::SwitchToMode(InputMode::Normal)], + ); + defaults.insert( + Key::Esc, + vec![ + Action::TabNameInput(vec![0x1b]), + Action::SwitchToMode(InputMode::Tab), + ], + ); + } } Ok(defaults) @@ -178,6 +202,7 @@ pub fn key_to_actions( }; match *mode { InputMode::Normal => mode_keybind_or_action(Action::Write(input)), + InputMode::RenameTab => mode_keybind_or_action(Action::TabNameInput(input)), _ => mode_keybind_or_action(Action::NoOp), } } diff --git a/src/common/mod.rs b/src/common/mod.rs index f78303ea..7b19bcef 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -34,7 +34,9 @@ use os_input_output::OsApi; use pty_bus::{PtyBus, PtyInstruction}; use screen::{Screen, ScreenInstruction}; use utils::consts::{ZELLIJ_IPC_PIPE, ZELLIJ_ROOT_PLUGIN_DIR}; -use wasm_vm::{wasi_stdout, wasi_write_string, zellij_imports, PluginInstruction}; +use wasm_vm::{ + wasi_stdout, wasi_write_string, zellij_imports, EventType, PluginInputType, PluginInstruction, +}; #[derive(Serialize, Deserialize, Debug)] pub enum ApiCommand { @@ -419,6 +421,9 @@ pub fn start(mut os_input: Box, opts: CliArgs) { ScreenInstruction::GoToTab(tab_index) => { screen.go_to_tab(tab_index as usize) } + ScreenInstruction::UpdateTabName(c) => { + screen.update_active_tab_name(c); + } ScreenInstruction::Quit => { break; } @@ -438,6 +443,11 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let store = Store::default(); let mut plugin_id = 0; let mut plugin_map = HashMap::new(); + let handler_map: HashMap = + [(EventType::Tab, "handle_tab_rename_keypress".to_string())] + .iter() + .cloned() + .collect(); move || loop { let (event, mut err_ctx) = receive_plugin_instructions @@ -448,7 +458,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { send_pty_instructions.update(err_ctx); send_app_instructions.update(err_ctx); match event { - PluginInstruction::Load(pid_tx, path) => { + PluginInstruction::Load(pid_tx, path, events) => { let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); let plugin_dir = project_dirs.data_dir().join("plugins/"); @@ -490,6 +500,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { send_screen_instructions: send_screen_instructions.clone(), send_app_instructions: send_app_instructions.clone(), wasi_env, + events, }; let zellij = zellij_imports(&store, &plugin_env); @@ -514,32 +525,58 @@ pub fn start(mut os_input: Box, opts: CliArgs) { buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap(); } - PluginInstruction::UpdateTabs(active_tab_index, num_tabs) => { - for (instance, _) in plugin_map.values() { + 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(); - handler - .call(&[ - Value::I32(active_tab_index as i32), - Value::I32(num_tabs as i32), - ]) - .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(pid, input_bytes) => { - 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(); + 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) => { diff --git a/src/common/screen.rs b/src/common/screen.rs index 7747b43b..199a1223 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -2,13 +2,14 @@ use std::collections::BTreeMap; use std::os::unix::io::RawFd; +use std::str; use std::sync::mpsc::Receiver; use super::{AppInstruction, SenderWithContext}; use crate::os_input_output::OsApi; use crate::panes::PositionAndSize; use crate::pty_bus::{PtyInstruction, VteEvent}; -use crate::tab::Tab; +use crate::tab::{Tab, TabData}; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; use crate::{layout::Layout, panes::PaneId}; @@ -46,6 +47,7 @@ pub enum ScreenInstruction { SwitchTabPrev, CloseTab, GoToTab(u32), + UpdateTabName(Vec), } /// A [`Screen`] holds multiple [`Tab`]s, each one holding multiple [`panes`](crate::client::panes). @@ -69,6 +71,7 @@ pub struct Screen { active_tab_index: Option, /// The [`OsApi`] this [`Screen`] uses. os_api: Box, + tabname_buf: String, } impl Screen { @@ -92,6 +95,7 @@ impl Screen { active_tab_index: None, tabs: BTreeMap::new(), os_api, + tabname_buf: String::new(), } } @@ -103,6 +107,7 @@ impl Screen { let tab = Tab::new( tab_index, position, + String::new(), &self.full_screen_ws, self.os_api.clone(), self.send_pty_instructions.clone(), @@ -246,6 +251,7 @@ impl Screen { let mut tab = Tab::new( tab_index, position, + String::new(), &self.full_screen_ws, self.os_api.clone(), self.send_pty_instructions.clone(), @@ -261,13 +267,36 @@ impl Screen { } fn update_tabs(&self) { - if let Some(active_tab) = self.get_active_tab() { - self.send_plugin_instructions - .send(PluginInstruction::UpdateTabs( - active_tab.position, - self.tabs.len(), - )) - .unwrap(); + let mut tab_data = vec![]; + let active_tab_index = self.active_tab_index.unwrap(); + for tab in self.tabs.values() { + tab_data.push(TabData { + position: tab.position, + name: tab.name.clone(), + active: active_tab_index == tab.index, + }); + } + self.send_plugin_instructions + .send(PluginInstruction::UpdateTabs(tab_data)) + .unwrap(); + } + + pub fn update_active_tab_name(&mut self, buf: Vec) { + let s = str::from_utf8(&buf).unwrap(); + match s { + "\0" => { + self.tabname_buf = String::new(); + } + "\n" => { + let new_name = self.tabname_buf.clone(); + let active_tab = self.get_active_tab_mut().unwrap(); + active_tab.name = new_name; + self.update_tabs(); + self.render(); + } + c => { + self.tabname_buf.push_str(c); + } } } } diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs index 5d4b1e30..15b920ad 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -1,24 +1,36 @@ +use crate::tab::TabData; +use serde::{Deserialize, Serialize}; use std::{ path::PathBuf, sync::mpsc::{channel, Sender}, }; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer_wasi::WasiEnv; -// use crate::utils::logging::debug_log_to_file; use super::{ input::handler::get_help, pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, PaneId, SenderWithContext, }; +#[derive(Clone, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)] +pub enum EventType { + Tab, +} + +#[derive(Clone, Debug)] +pub enum PluginInputType { + Normal(u32), + Event(EventType), +} + #[derive(Clone, Debug)] pub enum PluginInstruction { - Load(Sender, PathBuf), + Load(Sender, PathBuf, Vec), Draw(Sender, u32, usize, usize), // String buffer, plugin id, rows, cols - Input(u32, Vec), // plugin id, input bytes + Input(PluginInputType, Vec), // plugin id, input bytes GlobalInput(Vec), // input bytes Unload(u32), - UpdateTabs(usize, usize), // num tabs, active tab + UpdateTabs(Vec), // num tabs, active tab Quit, } @@ -29,6 +41,7 @@ pub struct PluginEnv { pub send_app_instructions: SenderWithContext, pub send_pty_instructions: SenderWithContext, // FIXME: This should be a big bundle of all of the channels pub wasi_env: WasiEnv, + pub events: Vec, } // Plugin API --------------------------------------------------------------------------------------------------------- diff --git a/zellij-tile/src/lib.rs b/zellij-tile/src/lib.rs index b5ba63fe..de87ee9c 100644 --- a/zellij-tile/src/lib.rs +++ b/zellij-tile/src/lib.rs @@ -7,7 +7,8 @@ pub trait ZellijTile { fn draw(&mut self, rows: usize, cols: usize) {} fn handle_key(&mut self, key: Key) {} fn handle_global_key(&mut self, key: Key) {} - fn update_tabs(&mut self, active_tab_index: usize, num_active_tabs: usize) {} + fn update_tabs(&mut self) {} + fn handle_tab_rename_keypress(&mut self, key: Key) {} } #[macro_export] @@ -45,11 +46,18 @@ macro_rules! register_tile { } #[no_mangle] - pub fn update_tabs(active_tab_index: i32, num_active_tabs: i32) { + 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() - .update_tabs(active_tab_index as usize, num_active_tabs as usize); + .handle_tab_rename_keypress($crate::get_key()); }) } }; diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index 52740472..20185c72 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -38,10 +38,19 @@ pub enum InputMode { 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, +} + impl Default for InputMode { fn default() -> InputMode { InputMode::Normal @@ -76,6 +85,10 @@ pub fn get_help() -> Help { deserialize_from_stdin().unwrap_or_default() } +pub fn get_tabs() -> Vec { + deserialize_from_stdin().unwrap_or_default() +} + fn deserialize_from_stdin() -> Option { let mut json = String::new(); io::stdin().read_line(&mut json).unwrap();