Add event subscription tracking

This commit is contained in:
Brooks J Rady 2021-03-09 21:51:17 +00:00
parent 06bce9a1fd
commit e9ab81850e
10 changed files with 85 additions and 40 deletions

View file

@ -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 $@

View file

@ -28,7 +28,7 @@ impl Default for BarMode {
struct State {
active_tab_index: usize,
num_tabs: usize,
tabs: Vec<TabData>,
tabs: Vec<TabInfo>,
mode: BarMode,
new_name: String,
}

View file

@ -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<EventType>,
pub events: Vec<NaughtyEventType>,
}
impl Layout {

View file

@ -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 {

View file

@ -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();

View file

@ -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,8 +447,10 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let store = Store::default();
let mut plugin_id = 0;
let mut plugin_map = HashMap::new();
let handler_map: HashMap<EventType, String> =
[(EventType::Tab, "handle_tab_rename_keypress".to_string())]
let handler_map: HashMap<NaughtyEventType, String> = [(
NaughtyEventType::Tab,
"handle_tab_rename_keypress".to_string(),
)]
.iter()
.cloned()
.collect();
@ -499,6 +506,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, 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<dyn OsApi>, 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();

View file

@ -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,

View file

@ -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<u32>, PathBuf, Vec<EventType>),
Load(Sender<u32>, PathBuf, Vec<NaughtyEventType>),
Draw(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
Input(PluginInputType, Vec<u8>), // plugin id, input bytes
GlobalInput(Vec<u8>), // input bytes
Unload(u32),
UpdateTabs(Vec<TabData>), // num tabs, active tab
UpdateTabs(Vec<TabInfo>), // num tabs, active tab
Quit,
}
@ -41,7 +45,8 @@ pub struct PluginEnv {
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 wasi_env: WasiEnv,
pub events: Vec<EventType>,
pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
pub events: Vec<NaughtyEventType>, // 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<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) {
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

View file

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

View file

@ -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<TabData> {
pub fn get_tabs() -> Vec<TabInfo> {
deserialize_from_stdin().unwrap_or_default()
}
@ -43,6 +53,8 @@ fn deserialize_from_stdin<T: DeserializeOwned>() -> Option<T> {
#[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);