Finish implementing default layouts
This commit is contained in:
parent
555f9af37a
commit
209cce96a5
6 changed files with 159 additions and 146 deletions
|
|
@ -5,4 +5,4 @@ parts:
|
|||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 1
|
||||
plugin: status-bar.wasm
|
||||
plugin: status-bar
|
||||
|
|
@ -6,9 +6,9 @@ parts:
|
|||
- direction: Horizontal
|
||||
split_size:
|
||||
Percent: 20
|
||||
plugin: strider.wasm
|
||||
plugin: strider
|
||||
- direction: Horizontal
|
||||
- direction: Vertical
|
||||
split_size:
|
||||
Fixed: 1
|
||||
plugin: status-bar.wasm
|
||||
plugin: status-bar
|
||||
Binary file not shown.
|
|
@ -317,8 +317,8 @@ pub fn get_help(mode: &InputMode) -> Vec<String> {
|
|||
"<q> Quit".into(),
|
||||
"<PgUp/PgDown> Scroll".into(),
|
||||
"<1> New Tab".into(),
|
||||
"<2> Next Tab".into(),
|
||||
"<3> Last Tab".into(),
|
||||
"<2/3> Move Tab".into(),
|
||||
"<4> Close Tab".into(),
|
||||
];
|
||||
match mode {
|
||||
InputMode::Normal => vec!["<Ctrl-g> Command Mode".into()],
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use directories_next::ProjectDirs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs::File, io::prelude::*, path::PathBuf};
|
||||
|
||||
|
|
@ -179,7 +180,11 @@ pub struct Layout {
|
|||
|
||||
impl Layout {
|
||||
pub fn new(layout_path: PathBuf) -> Self {
|
||||
let project_dirs = ProjectDirs::from("org", "Mosaic Contributors", "Mosaic").unwrap();
|
||||
let layout_dir = project_dirs.data_dir().join("layouts/");
|
||||
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")))
|
||||
.unwrap_or_else(|_| panic!("cannot find layout {}", &layout_path.display()));
|
||||
|
||||
let mut layout = String::new();
|
||||
|
|
|
|||
290
src/main.rs
290
src/main.rs
|
|
@ -16,14 +16,15 @@ mod utils;
|
|||
|
||||
mod wasm_vm;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::{channel, sync_channel, Receiver, SendError, Sender, SyncSender};
|
||||
use std::thread;
|
||||
use std::{cell::RefCell, sync::mpsc::TrySendError};
|
||||
use std::{collections::HashMap, fs};
|
||||
|
||||
use directories_next::ProjectDirs;
|
||||
use input::InputMode;
|
||||
use panes::PaneId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -208,7 +209,12 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
os_input.clone(),
|
||||
opts.debug,
|
||||
);
|
||||
let maybe_layout = opts.layout.map(Layout::new);
|
||||
// Don't use default layouts in tests, but do everywhere else
|
||||
#[cfg(not(test))]
|
||||
let default_layout = Some(PathBuf::from("default"));
|
||||
#[cfg(test)]
|
||||
let default_layout = None;
|
||||
let maybe_layout = opts.layout.or(default_layout).map(Layout::new);
|
||||
|
||||
#[cfg(not(test))]
|
||||
std::panic::set_hook({
|
||||
|
|
@ -224,64 +230,57 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
.name("pty".to_string())
|
||||
.spawn({
|
||||
let mut command_is_executing = command_is_executing.clone();
|
||||
move || {
|
||||
if let Some(layout) = maybe_layout {
|
||||
pty_bus.spawn_terminals_for_layout(layout);
|
||||
} else {
|
||||
let pid = pty_bus.spawn_terminal(None);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::NewTab(pid))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
loop {
|
||||
let (event, mut err_ctx) = pty_bus
|
||||
.receive_pty_instructions
|
||||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
|
||||
pty_bus.send_screen_instructions.update(err_ctx);
|
||||
match event {
|
||||
PtyInstruction::SpawnTerminal(file_to_open) => {
|
||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::SpawnTerminalVertically(file_to_open) => {
|
||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
|
||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::NewTab => {
|
||||
send_pty_instructions.send(PtyInstruction::NewTab).unwrap();
|
||||
move || loop {
|
||||
let (event, mut err_ctx) = pty_bus
|
||||
.receive_pty_instructions
|
||||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
|
||||
pty_bus.send_screen_instructions.update(err_ctx);
|
||||
match event {
|
||||
PtyInstruction::SpawnTerminal(file_to_open) => {
|
||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::SpawnTerminalVertically(file_to_open) => {
|
||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
|
||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::NewTab => {
|
||||
if let Some(layout) = maybe_layout.clone() {
|
||||
pty_bus.spawn_terminals_for_layout(layout);
|
||||
} else {
|
||||
let pid = pty_bus.spawn_terminal(None);
|
||||
pty_bus
|
||||
.send_screen_instructions
|
||||
.send(ScreenInstruction::NewTab(pid))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::ClosePane(id) => {
|
||||
pty_bus.close_pane(id);
|
||||
command_is_executing.done_closing_pane();
|
||||
}
|
||||
PtyInstruction::CloseTab(ids) => {
|
||||
pty_bus.close_tab(ids);
|
||||
command_is_executing.done_closing_pane();
|
||||
}
|
||||
PtyInstruction::Quit => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
PtyInstruction::ClosePane(id) => {
|
||||
pty_bus.close_pane(id);
|
||||
command_is_executing.done_closing_pane();
|
||||
}
|
||||
PtyInstruction::CloseTab(ids) => {
|
||||
pty_bus.close_tab(ids);
|
||||
command_is_executing.done_closing_pane();
|
||||
}
|
||||
PtyInstruction::Quit => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -421,7 +420,8 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
ScreenInstruction::SwitchTabPrev => screen.switch_tab_prev(),
|
||||
ScreenInstruction::CloseTab => screen.close_tab(),
|
||||
ScreenInstruction::ApplyLayout((layout, new_pane_pids)) => {
|
||||
screen.apply_layout(layout, new_pane_pids)
|
||||
screen.apply_layout(layout, new_pane_pids);
|
||||
command_is_executing.done_opening_new_pane();
|
||||
}
|
||||
ScreenInstruction::Quit => {
|
||||
break;
|
||||
|
|
@ -441,114 +441,122 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
|||
let mut send_screen_instructions = send_screen_instructions.clone();
|
||||
let mut send_app_instructions = send_app_instructions.clone();
|
||||
|
||||
move || {
|
||||
let store = Store::default();
|
||||
let store = Store::default();
|
||||
let mut plugin_id = 0;
|
||||
let mut plugin_map = HashMap::new();
|
||||
|
||||
let mut plugin_id = 0;
|
||||
let mut plugin_map = HashMap::new();
|
||||
move || loop {
|
||||
let (event, mut err_ctx) = receive_plugin_instructions
|
||||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
||||
send_screen_instructions.update(err_ctx);
|
||||
send_pty_instructions.update(err_ctx);
|
||||
send_app_instructions.update(err_ctx);
|
||||
match event {
|
||||
PluginInstruction::Load(pid_tx, path) => {
|
||||
let project_dirs =
|
||||
ProjectDirs::from("org", "Mosaic Contributors", "Mosaic").unwrap();
|
||||
let plugin_dir = project_dirs.data_dir().join("plugins/");
|
||||
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"))
|
||||
})
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("cannot find plugin {}", &path.display())
|
||||
});
|
||||
|
||||
loop {
|
||||
let (event, mut err_ctx) = receive_plugin_instructions
|
||||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
||||
send_screen_instructions.update(err_ctx);
|
||||
send_pty_instructions.update(err_ctx);
|
||||
send_app_instructions.update(err_ctx);
|
||||
match event {
|
||||
PluginInstruction::Load(pid_tx, path) => {
|
||||
// FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
|
||||
let module = Module::from_file(&store, &path).unwrap();
|
||||
// FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
|
||||
let module = Module::new(&store, &wasm_bytes).unwrap();
|
||||
|
||||
let output = Pipe::new();
|
||||
let input = Pipe::new();
|
||||
let mut wasi_env = WasiState::new("mosaic")
|
||||
.env("CLICOLOR_FORCE", "1")
|
||||
.preopen(|p| {
|
||||
p.directory(".") // FIXME: Change this to a more meaningful dir
|
||||
.alias(".")
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
})
|
||||
.unwrap()
|
||||
.stdin(Box::new(input))
|
||||
.stdout(Box::new(output))
|
||||
.finalize()
|
||||
.unwrap();
|
||||
let output = Pipe::new();
|
||||
let input = Pipe::new();
|
||||
let mut wasi_env = WasiState::new("mosaic")
|
||||
.env("CLICOLOR_FORCE", "1")
|
||||
.preopen(|p| {
|
||||
p.directory(".") // FIXME: Change this to a more meaningful dir
|
||||
.alias(".")
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
})
|
||||
.unwrap()
|
||||
.stdin(Box::new(input))
|
||||
.stdout(Box::new(output))
|
||||
.finalize()
|
||||
.unwrap();
|
||||
|
||||
let wasi = wasi_env.import_object(&module).unwrap();
|
||||
let wasi = wasi_env.import_object(&module).unwrap();
|
||||
|
||||
let plugin_env = PluginEnv {
|
||||
plugin_id,
|
||||
send_pty_instructions: send_pty_instructions.clone(),
|
||||
send_screen_instructions: send_screen_instructions.clone(),
|
||||
send_app_instructions: send_app_instructions.clone(),
|
||||
wasi_env,
|
||||
};
|
||||
let plugin_env = PluginEnv {
|
||||
plugin_id,
|
||||
send_pty_instructions: send_pty_instructions.clone(),
|
||||
send_screen_instructions: send_screen_instructions.clone(),
|
||||
send_app_instructions: send_app_instructions.clone(),
|
||||
wasi_env,
|
||||
};
|
||||
|
||||
let mosaic = mosaic_imports(&store, &plugin_env);
|
||||
let instance =
|
||||
Instance::new(&module, &mosaic.chain_back(wasi)).unwrap();
|
||||
let mosaic = mosaic_imports(&store, &plugin_env);
|
||||
let instance =
|
||||
Instance::new(&module, &mosaic.chain_back(wasi)).unwrap();
|
||||
|
||||
let start = instance.exports.get_function("_start").unwrap();
|
||||
let start = instance.exports.get_function("_start").unwrap();
|
||||
|
||||
// This eventually calls the `.init()` method
|
||||
start.call(&[]).unwrap();
|
||||
// This eventually calls the `.init()` method
|
||||
start.call(&[]).unwrap();
|
||||
|
||||
plugin_map.insert(plugin_id, (instance, plugin_env));
|
||||
pid_tx.send(plugin_id).unwrap();
|
||||
plugin_id += 1;
|
||||
plugin_map.insert(plugin_id, (instance, plugin_env));
|
||||
pid_tx.send(plugin_id).unwrap();
|
||||
plugin_id += 1;
|
||||
}
|
||||
PluginInstruction::Draw(buf_tx, pid, rows, cols) => {
|
||||
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
||||
|
||||
let draw = instance.exports.get_function("draw").unwrap();
|
||||
|
||||
draw.call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
|
||||
.unwrap();
|
||||
|
||||
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).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::Draw(buf_tx, pid, rows, cols) => {
|
||||
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
||||
|
||||
let draw = instance.exports.get_function("draw").unwrap();
|
||||
|
||||
draw.call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
|
||||
.unwrap();
|
||||
|
||||
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).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();
|
||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||
}
|
||||
PluginInstruction::GlobalInput(input_bytes) => {
|
||||
// FIXME: Set up an event subscription system, and timed callbacks
|
||||
for (instance, plugin_env) in plugin_map.values() {
|
||||
let handler =
|
||||
instance.exports.get_function("handle_global_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();
|
||||
handler.call(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||
}
|
||||
PluginInstruction::GlobalInput(input_bytes) => {
|
||||
// FIXME: Set up an event subscription system, and timed callbacks
|
||||
for (instance, plugin_env) in plugin_map.values() {
|
||||
let handler =
|
||||
instance.exports.get_function("handle_global_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(),
|
||||
);
|
||||
handler.call(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||
}
|
||||
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
||||
PluginInstruction::Quit => break,
|
||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||
}
|
||||
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
||||
PluginInstruction::Quit => break,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue