From e9ab81850e61b9b5e9c1f25d79cafd8378bda600 Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 9 Mar 2021 21:51:17 +0000 Subject: [PATCH] Add event subscription tracking --- build-all.sh | 18 +++++++--------- default-tiles/tab-bar/src/main.rs | 2 +- src/client/layout.rs | 4 ++-- src/common/errors.rs | 2 ++ src/common/input/handler.rs | 6 +++--- src/common/mod.rs | 24 ++++++++++++++------- src/common/screen.rs | 4 ++-- src/common/wasm_vm.rs | 35 +++++++++++++++++++++++-------- zellij-tile/src/data.rs | 16 +++++++++++--- zellij-tile/src/shim.rs | 14 ++++++++++++- 10 files changed, 85 insertions(+), 40 deletions(-) diff --git a/build-all.sh b/build-all.sh index df8e6978..f267b110 100755 --- a/build-all.sh +++ b/build-all.sh @@ -1,29 +1,25 @@ #!/bin/sh -total=6 +total=5 # This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open -echo "Building zellij-tile (1/$total)..." -cd zellij-tile +echo "Building status-bar (1/$total)..." +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 - -echo "Building strider (3/$total)..." +echo "Building strider (2/$total)..." cd ../strider cargo build --release --target-dir ../../target -echo "Building tab-bar (4/$total)..." +echo "Building tab-bar (3/$total)..." cd ../tab-bar cargo build --release --target-dir ../../target -echo "Optimising WASM executables (5/$total)..." +echo "Optimising WASM executables (4/$total)..." cd ../.. wasm-opt -O target/wasm32-wasi/release/status-bar.wasm -o target/status-bar.wasm || cp target/wasm32-wasi/release/status-bar.wasm target/status-bar.wasm wasm-opt -O target/wasm32-wasi/release/strider.wasm -o target/strider.wasm || cp target/wasm32-wasi/release/strider.wasm target/strider.wasm wasm-opt -O target/wasm32-wasi/release/tab-bar.wasm -o target/tab-bar.wasm || cp target/wasm32-wasi/release/tab-bar.wasm target/tab-bar.wasm -echo "Building zellij (6/$total)..." +echo "Building zellij (5/$total)..." cargo build --target-dir target $@ diff --git a/default-tiles/tab-bar/src/main.rs b/default-tiles/tab-bar/src/main.rs index 5220509b..6c00cb79 100644 --- a/default-tiles/tab-bar/src/main.rs +++ b/default-tiles/tab-bar/src/main.rs @@ -28,7 +28,7 @@ impl Default for BarMode { struct State { active_tab_index: usize, num_tabs: usize, - tabs: Vec, + tabs: Vec, mode: BarMode, new_name: String, } diff --git a/src/client/layout.rs b/src/client/layout.rs index 1b383540..33df2a2f 100644 --- a/src/client/layout.rs +++ b/src/client/layout.rs @@ -4,7 +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::common::wasm_vm::NaughtyEventType; use crate::panes::PositionAndSize; fn split_space_to_parts_vertically( @@ -182,7 +182,7 @@ pub struct Layout { #[serde(default)] pub expansion_boundary: bool, #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec, + pub events: Vec, } impl Layout { diff --git a/src/common/errors.rs b/src/common/errors.rs index df741629..48a92b76 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -164,6 +164,7 @@ impl Display for ContextType { } } +// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!! /// Stack call representations corresponding to the different types of [`ScreenInstruction`]s. #[derive(Debug, Clone, Copy, PartialEq)] pub enum ScreenContext { @@ -201,6 +202,7 @@ pub enum ScreenContext { UpdateTabName, } +// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!! impl From<&ScreenInstruction> for ScreenContext { fn from(screen_instruction: &ScreenInstruction) -> Self { match *screen_instruction { diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index e498fbb6..40d53d5d 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::{EventType, PluginInputType, PluginInstruction}; +use crate::wasm_vm::{NaughtyEventType, PluginInputType, PluginInstruction}; use crate::CommandIsExecuting; use termion::input::TermReadEventsAndRaw; @@ -234,7 +234,7 @@ impl InputHandler { Action::TabNameInput(c) => { self.send_plugin_instructions .send(PluginInstruction::Input( - PluginInputType::Event(EventType::Tab), + PluginInputType::Event(NaughtyEventType::Tab), c.clone(), )) .unwrap(); @@ -245,7 +245,7 @@ impl InputHandler { Action::SaveTabName => { self.send_plugin_instructions .send(PluginInstruction::Input( - PluginInputType::Event(EventType::Tab), + PluginInputType::Event(NaughtyEventType::Tab), vec![b'\n'], )) .unwrap(); diff --git a/src/common/mod.rs b/src/common/mod.rs index 9c41e131..411269b6 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -9,12 +9,16 @@ pub mod screen; pub mod utils; pub mod wasm_vm; -use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::mpsc; use std::thread; use std::{cell::RefCell, sync::mpsc::TrySendError}; use std::{collections::HashMap, fs}; +use std::{ + collections::HashSet, + io::Write, + sync::{Arc, Mutex}, +}; use crate::cli::CliArgs; use crate::layout::Layout; @@ -31,7 +35,8 @@ use termion::input::TermRead; use utils::consts::{ZELLIJ_IPC_PIPE, ZELLIJ_ROOT_PLUGIN_DIR}; use wasm_vm::PluginEnv; use wasm_vm::{ - wasi_stdout, wasi_write_string, zellij_imports, EventType, PluginInputType, PluginInstruction, + wasi_stdout, wasi_write_string, zellij_imports, NaughtyEventType, PluginInputType, + PluginInstruction, }; use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer_wasi::{Pipe, WasiState}; @@ -442,11 +447,13 @@ 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(); + let handler_map: HashMap = [( + NaughtyEventType::Tab, + "handle_tab_rename_keypress".to_string(), + )] + .iter() + .cloned() + .collect(); move || loop { let (event, mut err_ctx) = receive_plugin_instructions @@ -499,6 +506,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, + subscriptions: Arc::new(Mutex::new(HashSet::new())), events, }; @@ -526,7 +534,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { } PluginInstruction::UpdateTabs(mut tabs) => { for (instance, plugin_env) in plugin_map.values() { - if !plugin_env.events.contains(&EventType::Tab) { + if !plugin_env.events.contains(&NaughtyEventType::Tab) { continue; } let handler = instance.exports.get_function("update_tabs").unwrap(); diff --git a/src/common/screen.rs b/src/common/screen.rs index b848cc49..c4c4bdd7 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -13,7 +13,7 @@ use crate::tab::Tab; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; use crate::{layout::Layout, panes::PaneId}; -use zellij_tile::data::TabData; +use zellij_tile::data::TabInfo; /// Instructions that can be sent to the [`Screen`]. #[derive(Debug, Clone)] @@ -272,7 +272,7 @@ impl Screen { let mut tab_data = vec![]; let active_tab_index = self.active_tab_index.unwrap(); for tab in self.tabs.values() { - tab_data.push(TabData { + tab_data.push(TabInfo { position: tab.position, name: tab.name.clone(), active: active_tab_index == tab.index, diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs index 24e8d2a6..9c03ab8a 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -1,11 +1,15 @@ use serde::{Deserialize, Serialize}; use std::{ + collections::HashSet, path::PathBuf, - sync::mpsc::{channel, Sender}, + sync::{ + mpsc::{channel, Sender}, + Arc, Mutex, + }, }; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer_wasi::WasiEnv; -use zellij_tile::data::TabData; +use zellij_tile::data::{EventType, TabInfo}; use super::{ input::handler::get_help, pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, @@ -13,24 +17,24 @@ use super::{ }; #[derive(Clone, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)] -pub enum EventType { +pub enum NaughtyEventType { Tab, } #[derive(Clone, Debug)] pub enum PluginInputType { Normal(u32), - Event(EventType), + Event(NaughtyEventType), } #[derive(Clone, Debug)] pub enum PluginInstruction { - Load(Sender, PathBuf, Vec), + Load(Sender, PathBuf, Vec), Draw(Sender, u32, usize, usize), // String buffer, plugin id, rows, cols Input(PluginInputType, Vec), // plugin id, input bytes GlobalInput(Vec), // input bytes Unload(u32), - UpdateTabs(Vec), // num tabs, active tab + UpdateTabs(Vec), // num tabs, active tab Quit, } @@ -41,7 +45,8 @@ pub struct PluginEnv { pub send_app_instructions: SenderWithContext, pub send_pty_instructions: SenderWithContext, // FIXME: This should be a big bundle of all of the channels pub wasi_env: WasiEnv, - pub events: Vec, + pub subscriptions: Arc>>, + pub events: Vec, // FIXME: Murder this very soon (should not survive into main) } // Plugin API --------------------------------------------------------------------------------------------------------- @@ -49,6 +54,8 @@ pub struct PluginEnv { pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { imports! { "zellij" => { + "host_subscribe" => Function::new_native_with_env(store, plugin_env.clone(), host_subscribe), + "host_unsubscribe" => Function::new_native_with_env(store, plugin_env.clone(), host_unsubscribe), "host_open_file" => Function::new_native_with_env(store, plugin_env.clone(), host_open_file), "host_set_invisible_borders" => Function::new_native_with_env(store, plugin_env.clone(), host_set_invisible_borders), "host_set_max_height" => Function::new_native_with_env(store, plugin_env.clone(), host_set_max_height), @@ -58,7 +65,18 @@ pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { } } -// FIXME: Bundle up all of the channels! Pair that with WasiEnv? +fn host_subscribe(plugin_env: &PluginEnv) { + let mut subscriptions = plugin_env.subscriptions.lock().unwrap(); + let new: HashSet = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap(); + subscriptions.extend(new); +} + +fn host_unsubscribe(plugin_env: &PluginEnv) { + let mut subscriptions = plugin_env.subscriptions.lock().unwrap(); + let old: HashSet = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap(); + subscriptions.retain(|k| !old.contains(k)); +} + fn host_open_file(plugin_env: &PluginEnv) { let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); plugin_env @@ -67,7 +85,6 @@ fn host_open_file(plugin_env: &PluginEnv) { .unwrap(); } -// FIXME: Think about these naming conventions – should everything be prefixed by 'host'? fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) { let selectable = selectable != 0; plugin_env diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index b64f1e4e..2979bece 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use strum_macros::EnumIter; +use strum_macros::{EnumDiscriminants, EnumIter, ToString}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Key { @@ -23,6 +23,15 @@ pub enum Key { Esc, } +#[derive(Debug, EnumDiscriminants, ToString, Serialize, Deserialize)] +#[strum_discriminants(derive(Hash, Serialize, Deserialize))] +#[strum_discriminants(name(EventType))] +pub enum Event { + ModeUpdate(Help), // FIXME: Rename the `Help` struct + TabUpdate(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 { @@ -56,11 +65,12 @@ impl Default for InputMode { #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct Help { pub mode: InputMode, + // FIXME: This should probably return Keys and Actions, then sort out strings plugin-side pub keybinds: Vec<(String, String)>, // => } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct TabData { +#[derive(Debug, Default, Clone, Deserialize, Serialize)] +pub struct TabInfo { /* subset of fields to publish to plugins */ pub position: usize, pub name: String, diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index bb038e82..3681e9ba 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -7,6 +7,16 @@ pub fn get_key() -> Key { deserialize_from_stdin().unwrap() } +pub fn subscribe(event_types: &[EventType]) { + println!("{}", serde_json::to_string(event_types).unwrap()); + unsafe { host_subscribe() }; +} + +pub fn unsubscribe(event_types: &[EventType]) { + println!("{}", serde_json::to_string(event_types).unwrap()); + unsafe { host_unsubscribe() }; +} + pub fn open_file(path: &Path) { println!("{}", path.to_string_lossy()); unsafe { host_open_file() }; @@ -31,7 +41,7 @@ pub fn get_help() -> Help { deserialize_from_stdin().unwrap_or_default() } -pub fn get_tabs() -> Vec { +pub fn get_tabs() -> Vec { deserialize_from_stdin().unwrap_or_default() } @@ -43,6 +53,8 @@ fn deserialize_from_stdin() -> Option { #[link(wasm_import_module = "zellij")] extern "C" { + fn host_subscribe(); + fn host_unsubscribe(); fn host_open_file(); fn host_set_max_height(max_height: i32); fn host_set_selectable(selectable: i32);