Added a get_help function to the plugin API
This commit is contained in:
parent
efcd36a52c
commit
01df6ed568
5 changed files with 113 additions and 11 deletions
|
|
@ -266,6 +266,8 @@ impl From<&PluginInstruction> for PluginContext {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum AppContext {
|
pub enum AppContext {
|
||||||
|
GetState,
|
||||||
|
SetState,
|
||||||
Exit,
|
Exit,
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
@ -273,6 +275,8 @@ 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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
46
src/input.rs
46
src/input.rs
|
|
@ -1,9 +1,8 @@
|
||||||
/// Module for handling input
|
|
||||||
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::CommandIsExecuting;
|
use crate::CommandIsExecuting;
|
||||||
use crate::{errors::ContextType, wasm_vm::PluginInstruction};
|
use crate::{errors::ContextType, wasm_vm::PluginInstruction};
|
||||||
|
use crate::{os_input_output::OsApi, update_state, AppState};
|
||||||
use crate::{AppInstruction, SenderWithContext, OPENCALLS};
|
use crate::{AppInstruction, SenderWithContext, OPENCALLS};
|
||||||
|
|
||||||
struct InputHandler {
|
struct InputHandler {
|
||||||
|
|
@ -45,6 +44,9 @@ impl InputHandler {
|
||||||
self.send_app_instructions.update(err_ctx);
|
self.send_app_instructions.update(err_ctx);
|
||||||
self.send_screen_instructions.update(err_ctx);
|
self.send_screen_instructions.update(err_ctx);
|
||||||
loop {
|
loop {
|
||||||
|
update_state(&self.send_app_instructions, |_| AppState {
|
||||||
|
input_mode: self.mode,
|
||||||
|
});
|
||||||
match self.mode {
|
match self.mode {
|
||||||
InputMode::Normal => self.read_normal_mode(),
|
InputMode::Normal => self.read_normal_mode(),
|
||||||
InputMode::Command => self.read_command_mode(false),
|
InputMode::Command => self.read_command_mode(false),
|
||||||
|
|
@ -112,12 +114,10 @@ impl InputHandler {
|
||||||
// multiple commands. If we're already in persistent mode, it'll return us to normal mode.
|
// multiple commands. If we're already in persistent mode, it'll return us to normal mode.
|
||||||
match self.mode {
|
match self.mode {
|
||||||
InputMode::Command => self.mode = InputMode::CommandPersistent,
|
InputMode::Command => self.mode = InputMode::CommandPersistent,
|
||||||
InputMode::CommandPersistent => {
|
InputMode::CommandPersistent => self.mode = InputMode::Normal,
|
||||||
self.mode = InputMode::Normal;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
[27] => {
|
[27] => {
|
||||||
// Esc
|
// Esc
|
||||||
|
|
@ -262,7 +262,7 @@ impl InputHandler {
|
||||||
self.command_is_executing.wait_until_pane_is_closed();
|
self.command_is_executing.wait_until_pane_is_closed();
|
||||||
}
|
}
|
||||||
//@@@khs26 Write this to the powerbar?
|
//@@@khs26 Write this to the powerbar?
|
||||||
_ => {}
|
_ => continue,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.mode == InputMode::Command {
|
if self.mode == InputMode::Command {
|
||||||
|
|
@ -299,7 +299,7 @@ impl InputHandler {
|
||||||
/// normal mode
|
/// normal mode
|
||||||
/// - Exiting means that we should start the shutdown process for mosaic or the given
|
/// - Exiting means that we should start the shutdown process for mosaic or the given
|
||||||
/// input handler
|
/// input handler
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum InputMode {
|
pub enum InputMode {
|
||||||
Normal,
|
Normal,
|
||||||
Command,
|
Command,
|
||||||
|
|
@ -307,6 +307,36 @@ pub enum InputMode {
|
||||||
Exiting,
|
Exiting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This should be auto-generated from the soon-to-be-added `get_default_keybinds`
|
||||||
|
pub fn get_help(mode: &InputMode) -> Vec<String> {
|
||||||
|
let command_help = vec![
|
||||||
|
"<n/b/z> Split".into(),
|
||||||
|
"<j/k/h/l> Resize".into(),
|
||||||
|
"<p> Focus Next".into(),
|
||||||
|
"<x> Close Pane".into(),
|
||||||
|
"<q> Quit".into(),
|
||||||
|
"<PgUp/PgDown> Scroll".into(),
|
||||||
|
"<1> New Tab".into(),
|
||||||
|
"<2> Next Tab".into(),
|
||||||
|
"<3> Last Tab".into(),
|
||||||
|
];
|
||||||
|
match mode {
|
||||||
|
InputMode::Normal => vec!["<Ctrl-g> Command Mode".into()],
|
||||||
|
InputMode::Command => [
|
||||||
|
vec![
|
||||||
|
"<Ctrl-g> Persistent Mode".into(),
|
||||||
|
"<ESC> Normal Mode".into(),
|
||||||
|
],
|
||||||
|
command_help,
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
|
InputMode::CommandPersistent => {
|
||||||
|
[vec!["<Ctrl-g/ESC> Normal Mode".into()], command_help].concat()
|
||||||
|
}
|
||||||
|
InputMode::Exiting => vec!["Bye from Mosaic!".into()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Entry point to the module that instantiates a new InputHandler and calls its
|
/// Entry point to the module that instantiates a new InputHandler and calls its
|
||||||
/// reading loop
|
/// reading loop
|
||||||
pub fn input_loop(
|
pub fn input_loop(
|
||||||
|
|
|
||||||
47
src/main.rs
47
src/main.rs
|
|
@ -16,14 +16,15 @@ mod utils;
|
||||||
|
|
||||||
mod wasm_vm;
|
mod wasm_vm;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc::{channel, sync_channel, Receiver, SendError, Sender, SyncSender};
|
use std::sync::mpsc::{channel, sync_channel, Receiver, SendError, Sender, SyncSender};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::{cell::RefCell, sync::mpsc::TrySendError};
|
||||||
|
|
||||||
|
use input::InputMode;
|
||||||
use panes::PaneId;
|
use panes::PaneId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
@ -85,6 +86,14 @@ impl<T: Clone> SenderWithContext<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, new_ctx: ErrorContext) {
|
pub fn update(&mut self, new_ctx: ErrorContext) {
|
||||||
self.err_ctx = new_ctx;
|
self.err_ctx = new_ctx;
|
||||||
}
|
}
|
||||||
|
|
@ -125,13 +134,44 @@ pub fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: It would be good to add some more things to this over time
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub input_mode: InputMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
input_mode: InputMode::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) = channel();
|
||||||
|
|
||||||
|
drop(app_tx.send(AppInstruction::GetState(state_tx)));
|
||||||
|
let state = state_rx.recv().unwrap();
|
||||||
|
|
||||||
|
drop(app_tx.send(AppInstruction::SetState(update_fn(state))))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum AppInstruction {
|
pub enum AppInstruction {
|
||||||
|
GetState(Sender<AppState>),
|
||||||
|
SetState(AppState),
|
||||||
Exit,
|
Exit,
|
||||||
Error(String),
|
Error(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
|
let mut app_state = AppState::default();
|
||||||
let mut active_threads = vec![];
|
let mut active_threads = vec![];
|
||||||
|
|
||||||
let command_is_executing = CommandIsExecuting::new();
|
let command_is_executing = CommandIsExecuting::new();
|
||||||
|
|
@ -399,6 +439,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
.spawn({
|
.spawn({
|
||||||
let mut send_pty_instructions = send_pty_instructions.clone();
|
let mut send_pty_instructions = send_pty_instructions.clone();
|
||||||
let mut send_screen_instructions = send_screen_instructions.clone();
|
let mut send_screen_instructions = send_screen_instructions.clone();
|
||||||
|
let mut send_app_instructions = send_app_instructions.clone();
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
|
|
@ -413,6 +454,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
||||||
send_screen_instructions.update(err_ctx);
|
send_screen_instructions.update(err_ctx);
|
||||||
send_pty_instructions.update(err_ctx);
|
send_pty_instructions.update(err_ctx);
|
||||||
|
send_app_instructions.update(err_ctx);
|
||||||
match event {
|
match event {
|
||||||
PluginInstruction::Load(pid_tx, path) => {
|
PluginInstruction::Load(pid_tx, path) => {
|
||||||
// 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
|
||||||
|
|
@ -441,6 +483,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
plugin_id,
|
plugin_id,
|
||||||
send_pty_instructions: send_pty_instructions.clone(),
|
send_pty_instructions: send_pty_instructions.clone(),
|
||||||
send_screen_instructions: send_screen_instructions.clone(),
|
send_screen_instructions: send_screen_instructions.clone(),
|
||||||
|
send_app_instructions: send_app_instructions.clone(),
|
||||||
wasi_env,
|
wasi_env,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -602,6 +645,8 @@ 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 => {
|
||||||
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
||||||
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
use std::{path::PathBuf, sync::mpsc::Sender};
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
sync::mpsc::{channel, Sender},
|
||||||
|
};
|
||||||
use wasmer::{imports, Function, ImportObject, Store, WasmerEnv};
|
use wasmer::{imports, Function, ImportObject, Store, WasmerEnv};
|
||||||
use wasmer_wasi::WasiEnv;
|
use wasmer_wasi::WasiEnv;
|
||||||
|
|
||||||
use crate::{panes::PaneId, pty_bus::PtyInstruction, screen::ScreenInstruction, SenderWithContext};
|
use crate::{
|
||||||
|
input::get_help, panes::PaneId, pty_bus::PtyInstruction, screen::ScreenInstruction,
|
||||||
|
AppInstruction, SenderWithContext,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PluginInstruction {
|
pub enum PluginInstruction {
|
||||||
|
|
@ -18,6 +24,7 @@ pub enum PluginInstruction {
|
||||||
pub struct PluginEnv {
|
pub struct PluginEnv {
|
||||||
pub plugin_id: u32,
|
pub plugin_id: u32,
|
||||||
pub send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
pub send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +36,7 @@ pub fn mosaic_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
|
||||||
"mosaic" => {
|
"mosaic" => {
|
||||||
"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_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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +62,21 @@ fn host_set_selectable(plugin_env: &PluginEnv, selectable: 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
|
||||||
|
|
|
||||||
BIN
status-bar.wasm
Executable file → Normal file
BIN
status-bar.wasm
Executable file → Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue