Wrap up the plugin system refactor, running everything through update()

This commit is contained in:
Brooks J Rady 2021-03-25 17:22:10 +00:00
parent 84a5cf95d1
commit b6f945da35
16 changed files with 121 additions and 307 deletions

View File

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

View File

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

View File

@ -12,23 +12,10 @@ pub struct LinePart {
len: usize,
}
#[derive(PartialEq)]
enum BarMode {
Normal,
Rename,
}
impl Default for BarMode {
fn default() -> Self {
BarMode::Normal
}
}
#[derive(Default)]
struct State {
tabs: Vec<TabInfo>,
mode: BarMode,
new_name: String,
mode: InputMode,
}
static ARROW_SEPARATOR: &str = "";
@ -51,12 +38,14 @@ impl ZellijTile for State {
set_selectable(false);
set_invisible_borders(true);
set_max_height(1);
subscribe(&[EventType::TabUpdate]);
subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]);
}
fn update(&mut self, event: Event) {
if let Event::TabUpdate(tabs) = event {
self.tabs = tabs;
match event {
Event::ModeUpdate(mode_info) => self.mode = mode_info.mode,
Event::TabUpdate(tabs) => self.tabs = tabs,
_ => unimplemented!(), // FIXME: This should be unreachable, but this could be cleaner
}
}
@ -68,11 +57,9 @@ impl ZellijTile for State {
let mut active_tab_index = 0;
for t in self.tabs.iter_mut() {
let mut tabname = t.name.clone();
if t.active && self.mode == BarMode::Rename {
if self.new_name.is_empty() {
if t.active && self.mode == InputMode::RenameTab {
if tabname.is_empty() {
tabname = String::from("Enter name...");
} else {
tabname = self.new_name.clone();
}
active_tab_index = t.position;
} else if t.active {
@ -88,19 +75,4 @@ impl ZellijTile for State {
}
println!("{}\u{1b}[48;5;238m\u{1b}[0K", s);
}
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,10 +1,8 @@
use crate::utils::consts::ZELLIJ_ROOT_LAYOUT_DIR;
use directories_next::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::{fs::File, io::prelude::*};
use crate::common::wasm_vm::NaughtyEventType;
use crate::panes::PositionAndSize;
fn split_space_to_parts_vertically(
@ -181,19 +179,15 @@ pub struct Layout {
pub plugin: Option<PathBuf>,
#[serde(default)]
pub expansion_boundary: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events: Vec<NaughtyEventType>,
}
impl Layout {
pub fn new(layout_path: PathBuf) -> Self {
let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
let layout_dir = project_dirs.data_dir().join("layouts/");
let root_layout_dir = Path::new(ZELLIJ_ROOT_LAYOUT_DIR);
let mut layout_file = File::open(&layout_path)
.or_else(|_| File::open(&layout_path.with_extension("yaml")))
.or_else(|_| File::open(&layout_dir.join(&layout_path).with_extension("yaml")))
.or_else(|_| File::open(root_layout_dir.join(&layout_path).with_extension("yaml")))
.unwrap_or_else(|_| panic!("cannot find layout {}", &layout_path.display()));
let mut layout = String::new();

View File

@ -258,11 +258,7 @@ impl Tab {
if let Some(plugin) = &layout.plugin {
let (pid_tx, pid_rx) = channel();
self.send_plugin_instructions
.send(PluginInstruction::Load(
pid_tx,
plugin.clone(),
layout.events.clone(),
))
.send(PluginInstruction::Load(pid_tx, plugin.clone()))
.unwrap();
let pid = pid_rx.recv().unwrap();
let new_plugin = PluginPane::new(
@ -301,7 +297,6 @@ impl Tab {
self.toggle_active_pane_fullscreen();
}
if !self.has_panes() {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
self.os_api.set_terminal_size_using_fd(
@ -352,7 +347,6 @@ impl Tab {
if terminal_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.columns()
&& terminal_to_split.rows() > terminal_to_split.min_height() * 2
{
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid {
let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
@ -373,7 +367,6 @@ impl Tab {
self.active_terminal = Some(pid);
}
} else if terminal_to_split.columns() > terminal_to_split.min_width() * 2 {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid {
let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
let new_terminal = TerminalPane::new(term_pid, right_winsize);
@ -403,7 +396,6 @@ impl Tab {
self.toggle_active_pane_fullscreen();
}
if !self.has_panes() {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
self.os_api.set_terminal_size_using_fd(
@ -414,9 +406,7 @@ impl Tab {
self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid);
}
} else {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid {
} else if let PaneId::Terminal(term_pid) = pid {
// TODO: check minimum size of active terminal
let active_pane_id = &self.get_active_pane_id().unwrap();
let active_pane = self.panes.get_mut(active_pane_id).unwrap();
@ -456,14 +446,12 @@ impl Tab {
self.render();
}
}
}
pub fn vertical_split(&mut self, pid: PaneId) {
self.close_down_to_max_terminals();
if self.fullscreen_is_active {
self.toggle_active_pane_fullscreen();
}
if !self.has_panes() {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid {
let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
self.os_api.set_terminal_size_using_fd(
@ -474,9 +462,7 @@ impl Tab {
self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid);
}
} else {
// FIXME: This could use a second look
if let PaneId::Terminal(term_pid) = pid {
} else if let PaneId::Terminal(term_pid) = pid {
// TODO: check minimum size of active terminal
let active_pane_id = &self.get_active_pane_id().unwrap();
let active_pane = self.panes.get_mut(active_pane_id).unwrap();
@ -516,7 +502,6 @@ impl Tab {
self.render();
}
}
}
pub fn get_active_pane(&self) -> Option<&dyn Pane> {
// FIXME: Could use Option::map() here
match self.get_active_pane_id() {
@ -1680,6 +1665,7 @@ impl Tab {
self.reduce_pane_and_surroundings_right(&active_pane_id, count);
}
}
self.render();
}
pub fn resize_left(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much
@ -1691,6 +1677,7 @@ impl Tab {
self.reduce_pane_and_surroundings_left(&active_pane_id, count);
}
}
self.render();
}
pub fn resize_down(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much
@ -1702,6 +1689,7 @@ impl Tab {
self.reduce_pane_and_surroundings_down(&active_pane_id, count);
}
}
self.render();
}
pub fn resize_up(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much
@ -1713,6 +1701,7 @@ impl Tab {
self.reduce_pane_and_surroundings_up(&active_pane_id, count);
}
}
self.render();
}
pub fn move_focus(&mut self) {
if !self.has_selectable_panes() {

View File

@ -282,7 +282,6 @@ pub enum PluginContext {
Load,
Update,
Render,
Input,
Unload,
Quit,
}
@ -293,7 +292,6 @@ impl From<&PluginInstruction> for PluginContext {
PluginInstruction::Load(..) => PluginContext::Load,
PluginInstruction::Update(..) => PluginContext::Update,
PluginInstruction::Render(..) => PluginContext::Render,
PluginInstruction::Input(..) => PluginContext::Input,
PluginInstruction::Unload(_) => PluginContext::Unload,
PluginInstruction::Quit => PluginContext::Quit,
}

View File

@ -49,5 +49,4 @@ pub enum Action {
CloseTab,
GoToTab(u32),
TabNameInput(Vec<u8>),
SaveTabName,
}

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::{NaughtyEventType, PluginInputType, PluginInstruction};
use crate::wasm_vm::PluginInstruction;
use crate::CommandIsExecuting;
use termion::input::{TermRead, TermReadEventsAndRaw};
@ -66,11 +66,6 @@ impl InputHandler {
Ok((event, raw_bytes)) => match event {
termion::event::Event::Key(key) => {
let key = cast_termion_key(key);
// FIXME: This is a bit of a hack to get resizing to work!
drop(
self.send_screen_instructions
.send(ScreenInstruction::Render),
);
// FIXME this explicit break is needed because the current test
// framework relies on it to not create dead threads that loop
// and eat up CPUs. Do not remove until the test framework has
@ -237,27 +232,10 @@ impl InputHandler {
.unwrap();
}
Action::TabNameInput(c) => {
self.send_plugin_instructions
.send(PluginInstruction::Input(
PluginInputType::Event(NaughtyEventType::Tab),
c.clone(),
))
.unwrap();
self.send_screen_instructions
.send(ScreenInstruction::UpdateTabName(c))
.unwrap();
}
Action::SaveTabName => {
self.send_plugin_instructions
.send(PluginInstruction::Input(
PluginInputType::Event(NaughtyEventType::Tab),
vec![b'\n'],
))
.unwrap();
self.send_screen_instructions
.send(ScreenInstruction::UpdateTabName(vec![b'\n']))
.unwrap();
}
Action::NoOp => {}
}

View File

@ -226,10 +226,7 @@ fn get_defaults_for_mode(mode: &InputMode) -> ModeKeybinds {
defaults.insert(Key::Up, vec![Action::ScrollUp]);
}
InputMode::RenameTab => {
defaults.insert(
Key::Char('\n'),
vec![Action::SaveTabName, Action::SwitchToMode(InputMode::Tab)],
);
defaults.insert(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]);
defaults.insert(
Key::Ctrl('g'),
vec![Action::SwitchToMode(InputMode::Normal)],

View File

@ -10,7 +10,7 @@ pub mod utils;
pub mod wasm_vm;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::mpsc;
use std::thread;
use std::{collections::HashMap, fs};
@ -32,16 +32,12 @@ use os_input_output::OsApi;
use pty_bus::{PtyBus, PtyInstruction};
use screen::{Screen, ScreenInstruction};
use serde::{Deserialize, Serialize};
use termion::input::TermRead;
use utils::consts::{ZELLIJ_IPC_PIPE, ZELLIJ_ROOT_PLUGIN_DIR};
use utils::consts::ZELLIJ_IPC_PIPE;
use wasm_vm::PluginEnv;
use wasm_vm::{
wasi_stdout, wasi_write_string, zellij_imports, NaughtyEventType, PluginInputType,
PluginInstruction,
};
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, Key};
use zellij_tile::data::{EventType, InputMode};
#[derive(Serialize, Deserialize, Debug)]
pub enum ApiCommand {
@ -352,8 +348,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.get_active_tab_mut()
.unwrap()
.set_pane_selectable(id, selectable);
// FIXME: Is this needed?
screen.render();
}
ScreenInstruction::SetMaxHeight(id, max_height) => {
screen
@ -417,42 +411,7 @@ 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<NaughtyEventType, String> = [(
NaughtyEventType::Tab,
"handle_tab_rename_keypress".to_string(),
)]
.iter()
.cloned()
.collect();
move || loop {
// FIXME: This 100% *must* be destroyed before this makes in into main!!!!!!!!!!!
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!")
}
}
}
let (event, mut err_ctx) = receive_plugin_instructions
.recv()
.expect("failed to receive event on channel");
@ -461,18 +420,13 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
send_pty_instructions.update(err_ctx);
send_app_instructions.update(err_ctx);
match event {
PluginInstruction::Load(pid_tx, path, events) => {
PluginInstruction::Load(pid_tx, path) => {
let project_dirs =
ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
let plugin_dir = project_dirs.data_dir().join("plugins/");
// FIXME: This really shouldn't need to exist anymore, let's get rid of it!
let root_plugin_dir = Path::new(ZELLIJ_ROOT_PLUGIN_DIR);
let wasm_bytes = fs::read(&path)
.or_else(|_| fs::read(&path.with_extension("wasm")))
.or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm")))
.or_else(|_| {
fs::read(&root_plugin_dir.join(&path).with_extension("wasm"))
})
.unwrap_or_else(|_| panic!("cannot find plugin {}", &path.display()));
// FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
@ -504,7 +458,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
send_app_instructions: send_app_instructions.clone(),
wasi_env,
subscriptions: Arc::new(Mutex::new(HashSet::new())),
events,
};
let zellij = zellij_imports(&store, &plugin_env);
@ -546,29 +499,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
}
// FIXME: Deduplicate this with the callback below!
PluginInstruction::Input(input_type, input_bytes) => {
let PluginInputType::Event(event) = input_type;
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().flatten() {
let key = cast_termion_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::Unload(pid) => drop(plugin_map.remove(&pid)),
PluginInstruction::Quit => break,
}

View File

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

View File

@ -74,7 +74,6 @@ pub struct Screen {
active_tab_index: Option<usize>,
/// The [`OsApi`] this [`Screen`] uses.
os_api: Box<dyn OsApi>,
tabname_buf: String,
input_mode: InputMode,
}
@ -100,7 +99,6 @@ impl Screen {
active_tab_index: None,
tabs: BTreeMap::new(),
os_api,
tabname_buf: String::new(),
input_mode,
}
}
@ -288,25 +286,20 @@ impl Screen {
pub fn update_active_tab_name(&mut self, buf: Vec<u8>) {
let s = str::from_utf8(&buf).unwrap();
let active_tab = self.get_active_tab_mut().unwrap();
match s {
"\0" => {
self.tabname_buf = String::new();
}
"\n" => {
let new_name = self.tabname_buf.clone();
let active_tab = self.get_active_tab_mut().unwrap();
active_tab.name = new_name;
self.update_tabs();
self.render();
active_tab.name = String::new();
}
"\u{007F}" | "\u{0008}" => {
//delete and backspace keys
self.tabname_buf.pop();
active_tab.name.pop();
}
c => {
self.tabname_buf.push_str(c);
active_tab.name.push_str(c);
}
}
self.update_tabs();
}
pub fn change_input_mode(&mut self, input_mode: InputMode) {
self.input_mode = input_mode;

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_FILE: &str = "/tmp/zellij/zellij-log/log.txt";
pub const ZELLIJ_IPC_PIPE: &str = "/tmp/zellij/ipc";
pub const ZELLIJ_ROOT_PLUGIN_DIR: &str = "/usr/share/zellij/plugins";
pub const ZELLIJ_ROOT_LAYOUT_DIR: &str = "/usr/share/zellij/layouts";

View File

@ -1,4 +1,3 @@
use serde::{Deserialize, Serialize};
use std::{
collections::HashSet,
path::PathBuf,
@ -12,22 +11,11 @@ use super::{
pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, PaneId, SenderWithContext,
};
#[derive(Clone, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)]
pub enum NaughtyEventType {
Tab,
}
#[derive(Clone, Debug)]
pub enum PluginInputType {
Event(NaughtyEventType),
}
#[derive(Clone, Debug)]
pub enum PluginInstruction {
Load(Sender<u32>, PathBuf, Vec<NaughtyEventType>),
Load(Sender<u32>, PathBuf),
Update(Option<u32>, Event), // Focused plugin / broadcast, event data
Render(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
Input(PluginInputType, Vec<u8>), // plugin id, input bytes
Unload(u32),
Quit,
}
@ -40,7 +28,6 @@ pub struct PluginEnv {
pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels
pub wasi_env: WasiEnv,
pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
pub events: Vec<NaughtyEventType>, // FIXME: Murder this very soon (should not survive into main)
}
// Plugin API ---------------------------------------------------------------------------------------------------------

View File

@ -9,8 +9,6 @@ pub trait ZellijTile {
fn load(&mut self) {}
fn update(&mut self, event: Event) {}
fn render(&mut self, rows: usize, cols: usize) {}
// FIXME: Everything below this line should be purged
fn handle_tab_rename_keypress(&mut self, key: Key) {}
}
#[macro_export]
@ -29,9 +27,7 @@ macro_rules! register_tile {
#[no_mangle]
pub fn update() {
STATE.with(|state| {
state
.borrow_mut()
.update($crate::shim::deserialize_from_stdin().unwrap());
state.borrow_mut().update($crate::shim::object_from_stdin());
});
}
@ -41,14 +37,5 @@ macro_rules! register_tile {
state.borrow_mut().render(rows as usize, cols as usize);
});
}
#[no_mangle]
pub fn handle_tab_rename_keypress() {
STATE.with(|state| {
state
.borrow_mut()
.handle_tab_rename_keypress($crate::shim::get_key());
})
}
};
}

View File

@ -3,9 +3,7 @@ use std::{io, path::Path};
use crate::data::*;
pub fn get_key() -> Key {
deserialize_from_stdin().unwrap()
}
// Subscription Handling
pub fn subscribe(event_types: &[EventType]) {
println!("{}", serde_json::to_string(event_types).unwrap());
@ -17,31 +15,34 @@ pub fn unsubscribe(event_types: &[EventType]) {
unsafe { host_unsubscribe() };
}
pub fn open_file(path: &Path) {
println!("{}", path.to_string_lossy());
unsafe { host_open_file() };
}
// Plugin Settings
pub fn set_max_height(max_height: i32) {
unsafe { host_set_max_height(max_height) };
}
pub fn set_invisible_borders(invisible_borders: bool) {
let invisible_borders = if invisible_borders { 1 } else { 0 };
unsafe { host_set_invisible_borders(invisible_borders) };
unsafe { host_set_invisible_borders(if invisible_borders { 1 } else { 0 }) };
}
pub fn set_selectable(selectable: bool) {
let selectable = if selectable { 1 } else { 0 };
unsafe { host_set_selectable(selectable) };
unsafe { host_set_selectable(if selectable { 1 } else { 0 }) };
}
// Host Functions
pub fn open_file(path: &Path) {
println!("{}", path.to_string_lossy());
unsafe { host_open_file() };
}
// Internal Functions
#[doc(hidden)]
// FIXME: Make this just return T and do a .unwrap() at the end; also naming?
pub fn deserialize_from_stdin<T: DeserializeOwned>() -> Option<T> {
pub fn object_from_stdin<T: DeserializeOwned>() -> T {
let mut json = String::new();
io::stdin().read_line(&mut json).unwrap();
serde_json::from_str(&json).ok()
serde_json::from_str(&json).unwrap()
}
#[link(wasm_import_module = "zellij")]