diff --git a/Cargo.lock b/Cargo.lock index 27c91846..a8656836 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -890,11 +890,13 @@ dependencies = [ "serde_json", "serde_yaml", "signal-hook", + "strip-ansi-escapes", "structopt", + "termion", "termios", "unicode-truncate", "unicode-width", - "vte", + "vte 0.8.0", "wasmer", "wasmer-wasi", ] @@ -931,6 +933,12 @@ dependencies = [ "libc", ] +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + [[package]] name = "object" version = "0.22.0" @@ -1134,6 +1142,15 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +dependencies = [ + "redox_syscall", +] + [[package]] name = "regalloc" version = "0.0.31" @@ -1349,6 +1366,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strip-ansi-escapes" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d63676e2abafa709460982ddc02a3bb586b6d15a49b75c212e06edd3933acee" +dependencies = [ + "vte 0.3.3", +] + [[package]] name = "strsim" version = "0.8.0" @@ -1426,6 +1452,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "termion" +version = "1.6.0" +source = "git+https://gitlab.com/TheLostLambda/termion.git#70159e07c59c02dc681db3b38dea16c295610ffa" +dependencies = [ + "libc", + "numtoa", + "redox_syscall", + "redox_termios", + "serde", +] + [[package]] name = "termios" version = "0.3.2" @@ -1559,6 +1597,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "utf8parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" + [[package]] name = "utf8parse" version = "0.2.0" @@ -1583,6 +1627,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vte" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf" +dependencies = [ + "utf8parse 0.1.1", +] + [[package]] name = "vte" version = "0.8.0" @@ -1590,7 +1643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96cc8a191608603611e78c6ec11dafef37e3cca0775aeef1931824753e81711d" dependencies = [ "arrayvec", - "utf8parse", + "utf8parse 0.2.0", "vte_generate_state_changes", ] @@ -1690,9 +1743,9 @@ checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092" [[package]] name = "wasmer" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe7fb8734c3e522aea0bed12315115e4c5d684c3d312db5f3ef6a8a312b1b47" +checksum = "94b1ece7c894857344ae93506686ae36ccd867b4ed55819c06d2316d009098d4" dependencies = [ "cfg-if 0.1.10", "indexmap", @@ -1713,9 +1766,9 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97789fdc5968ea3d29528648dc2422e0c795ca195b88a59c30a56f0e52805690" +checksum = "fc85134b257e5fba5870693441e300b601d08f18833ac4fa6934f0b72afc56d2" dependencies = [ "enumset", "raw-cpuid", @@ -1731,9 +1784,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e80c86796019ef6d4519e1a66f2b99ab73b937a4e43e723772956b3e8c8df23" +checksum = "60d68fb05dbe908724901b680070560944d99d04c52c763e98124aa988ac6dd0" dependencies = [ "cranelift-codegen", "cranelift-frontend", @@ -1750,9 +1803,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c74a84dc4ba0d60e9419f335734fa807097caf4938b2b44bc0703688a42b467" +checksum = "ca24205ffdf2d3b1a9c01219f4f3f0a1382658680abe73bc5b146f941adeeb8e" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1762,9 +1815,9 @@ dependencies = [ [[package]] name = "wasmer-engine" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e787fb8e42b5ad32c1c8dcf105e42d2919dfb3ea4b8e286de3e43f306ae1457b" +checksum = "d91ed16436a9813d92f434e1d40fdf91b45ca30f351a799f793015359acca86b" dependencies = [ "backtrace", "bincode", @@ -1783,9 +1836,9 @@ dependencies = [ [[package]] name = "wasmer-engine-jit" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552f4252f8d7984279c55df0970ca1d42b1e4c63d918e7af1cd004e427e5008c" +checksum = "df1e3ca5e34eacd4ab6d9d32edd41b51d2e39cf3d75453611c9c57cee3a64691" dependencies = [ "bincode", "cfg-if 0.1.10", @@ -1801,9 +1854,9 @@ dependencies = [ [[package]] name = "wasmer-engine-native" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5264031a9b398a071fa128fe89fb55bc75f9c0ac5eaa7f1f9ef9efcee08afa1c" +checksum = "6a21d6c5ae0c384ba2f01f598c95b01d4da2eaec3376fb96de2ded38c54143a0" dependencies = [ "bincode", "cfg-if 0.1.10", @@ -1822,9 +1875,9 @@ dependencies = [ [[package]] name = "wasmer-object" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22ccf03052d73b3588bd30de94db9ee949957a543d0c317122f2b87b7d1f309" +checksum = "06e007e73ec7775aecc61045092dabfcff1e9f228129cd129e76a3e6aae26454" dependencies = [ "object", "thiserror", @@ -1834,9 +1887,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3ea5b135db86baf39ce45f6cf98cc97d6e4234d3f75ac56a026f94bd8b68b1" +checksum = "2dbba7a95edb61b40daa43079979fc3212234e1645a15b8c527c36decad59fc6" dependencies = [ "cranelift-entity", "serde", @@ -1845,9 +1898,9 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d766b8db150b7e524c83b244e14a1180bf919b4f8bea6f063bae9a8e8d4156" +checksum = "9cd9acd4d53c004a11fcaff17f2a2528ae8f1748c6d5c4aea7d8bed2d9236f0f" dependencies = [ "backtrace", "cc", @@ -1865,9 +1918,9 @@ dependencies = [ [[package]] name = "wasmer-wasi" -version = "1.0.0-rc1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9e383c0a20fb697080b8e87613a0bb2e901a9f06ca710030b4a521ebcc398" +checksum = "5de224b58d5813a37dce64c483347909c478c5c2dcb15a93d67cfe6a863fd92c" dependencies = [ "bincode", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index 0b66e78e..f1964cb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,29 +16,25 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.8" signal-hook = "0.1.10" +strip-ansi-escapes = "0.1.0" structopt = "0.3" +termion = { git = "https://gitlab.com/TheLostLambda/termion.git", features = ["serde"] } termios = "0.3" unicode-truncate = "0.1.1" unicode-width = "0.1.8" vte = "0.8.0" +wasmer = "1.0.0" +wasmer-wasi = "1.0.0" [dependencies.async-std] version = "1.3.0" features = ["unstable"] -[dependencies.wasmer] -version = "1.0.0-rc" -optional = true - -[dependencies.wasmer-wasi] -version = "1.0.0-rc" -optional = true - -[features] -wasm-wip = ["wasmer", "wasmer-wasi"] - [dev-dependencies] insta = "0.16.1" [build-dependencies] structopt = "0.3" + +[profile.release] +lto = true diff --git a/build.rs b/build.rs index 58430643..f0f92e61 100644 --- a/build.rs +++ b/build.rs @@ -8,9 +8,7 @@ const BIN_NAME: &str = "mosaic"; fn main() { let mut clap_app = CliArgs::clap(); println!("cargo:rerun-if-changed=src/app.rs"); - let mut out_dir = std::env::var_os("CARGO_MANIFEST_DIR") - .unwrap() - .to_os_string(); + let mut out_dir = std::env::var_os("CARGO_MANIFEST_DIR").unwrap(); out_dir.push("/assets/completions"); println!( diff --git a/src/boundaries.rs b/src/boundaries.rs index 6d5eb3bc..498a3731 100644 --- a/src/boundaries.rs +++ b/src/boundaries.rs @@ -372,7 +372,7 @@ impl Boundaries { boundary_characters: HashMap::new(), } } - pub fn add_rect(&mut self, rect: &Box) { + pub fn add_rect(&mut self, rect: &dyn Pane) { if self.rect_right_boundary_is_before_screen_edge(rect) { // let boundary_x_coords = self.rect_right_boundary_x_coords(rect); let boundary_x_coords = rect.right_boundary_x_coords(); @@ -429,20 +429,20 @@ impl Boundaries { } vte_output } - fn rect_right_boundary_is_before_screen_edge(&self, rect: &Box) -> bool { + fn rect_right_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool { rect.x() + rect.columns() < self.columns } - fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &Box) -> bool { + fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool { rect.y() + rect.rows() < self.rows } - fn rect_right_boundary_row_start(&self, rect: &Box) -> usize { + fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize { if rect.y() == 0 { 0 } else { rect.y() - 1 } } - fn rect_right_boundary_row_end(&self, rect: &Box) -> usize { + fn rect_right_boundary_row_end(&self, rect: &dyn Pane) -> usize { let rect_bottom_row = rect.y() + rect.rows(); // we do this because unless we're on the screen edge, we'd like to go one extra row to // connect to whatever boundary is beneath us @@ -452,14 +452,14 @@ impl Boundaries { rect_bottom_row + 1 } } - fn rect_bottom_boundary_col_start(&self, rect: &Box) -> usize { + fn rect_bottom_boundary_col_start(&self, rect: &dyn Pane) -> usize { if rect.x() == 0 { 0 } else { rect.x() - 1 } } - fn rect_bottom_boundary_col_end(&self, rect: &Box) -> usize { + fn rect_bottom_boundary_col_end(&self, rect: &dyn Pane) -> usize { let rect_right_col = rect.x() + rect.columns(); // we do this because unless we're on the screen edge, we'd like to go one extra column to // connect to whatever boundary is right of us diff --git a/src/command_is_executing.rs b/src/command_is_executing.rs index 0cf47c05..93c44eb6 100644 --- a/src/command_is_executing.rs +++ b/src/command_is_executing.rs @@ -1,3 +1,4 @@ +#![allow(clippy::mutex_atomic)] use std::sync::{Arc, Condvar, Mutex}; #[derive(Clone)] diff --git a/src/errors.rs b/src/errors.rs index e4788e20..e53bbc30 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,17 +1,22 @@ use crate::pty_bus::PtyInstruction; use crate::screen::ScreenInstruction; -use crate::{AppInstruction, SenderWithContext, OPENCALLS}; -use backtrace::Backtrace; +use crate::{AppInstruction, OPENCALLS}; + use std::fmt::{Display, Error, Formatter}; -use std::panic::PanicInfo; -use std::{process, thread}; const MAX_THREAD_CALL_STACK: usize = 6; +#[cfg(not(test))] +use crate::SenderWithContext; +#[cfg(not(test))] +use std::panic::PanicInfo; +#[cfg(not(test))] pub fn handle_panic( info: &PanicInfo<'_>, send_app_instructions: &SenderWithContext, ) { + use backtrace::Backtrace; + use std::{process, thread}; let backtrace = Backtrace::new(); let thread = thread::current(); let thread = thread.name().unwrap_or("unnamed"); @@ -84,6 +89,12 @@ impl ErrorContext { } } +impl Default for ErrorContext { + fn default() -> Self { + Self::new() + } +} + impl Display for ErrorContext { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { writeln!(f, "Originating Thread(s):")?; @@ -101,7 +112,7 @@ impl Display for ErrorContext { pub enum ContextType { Screen(ScreenContext), Pty(PtyContext), - #[cfg(feature = "wasm-wip")] + Plugin(PluginContext), App(AppContext), IPCServer, @@ -117,7 +128,7 @@ impl Display for ContextType { match *self { ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c), ContextType::Pty(c) => write!(f, "{}pty_thread: {}{:?}", purple, green, c), - #[cfg(feature = "wasm-wip")] + ContextType::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c), ContextType::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c), ContextType::IPCServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green), @@ -225,21 +236,24 @@ impl From<&PtyInstruction> for PtyContext { } // FIXME: This whole pattern *needs* a macro eventually, it's soul-crushing to write -#[cfg(feature = "wasm-wip")] + use crate::wasm_vm::PluginInstruction; -#[cfg(feature = "wasm-wip")] + #[derive(Debug, Clone, Copy, PartialEq)] pub enum PluginContext { Load, + Draw, + Input, Unload, Quit, } -#[cfg(feature = "wasm-wip")] impl From<&PluginInstruction> for PluginContext { fn from(plugin_instruction: &PluginInstruction) -> Self { match *plugin_instruction { - PluginInstruction::Load(_) => PluginContext::Load, + PluginInstruction::Load(..) => PluginContext::Load, + PluginInstruction::Draw(..) => PluginContext::Draw, + PluginInstruction::Input(..) => PluginContext::Input, PluginInstruction::Unload(_) => PluginContext::Unload, PluginInstruction::Quit => PluginContext::Quit, } diff --git a/src/layout.rs b/src/layout.rs index 1e876532..c82fc404 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use std::{fs::File, io::prelude::*, path::PathBuf}; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; fn split_space_to_parts_vertically( space_to_split: &PositionAndSize, @@ -61,8 +61,11 @@ fn split_space_to_parts_horizontally( split_parts } -fn split_space(space_to_split: &PositionAndSize, layout: &Layout) -> Vec { - let mut pane_positions: Vec = vec![]; +fn split_space( + space_to_split: &PositionAndSize, + layout: &Layout, +) -> Vec<(Layout, PositionAndSize)> { + let mut pane_positions = Vec::new(); let percentages: Vec = layout .parts .iter() @@ -88,7 +91,7 @@ fn split_space(space_to_split: &PositionAndSize, layout: &Layout) -> Vec usize { + + pub fn total_terminal_panes(&self) -> usize { let mut total_panes = 0; total_panes += self.parts.len(); for part in self.parts.iter() { - total_panes += part.total_panes(); + if part.plugin.is_none() { + total_panes += part.total_terminal_panes(); + } } total_panes } - // FIXME: I probably shouldn't exist, much less with PathBuf (use &Path) - #[cfg(feature = "wasm-wip")] - pub fn list_plugins(&self) -> Vec<&PathBuf> { - dbg!(&self); - let mut plugins: Vec<_> = self.parts.iter().flat_map(Layout::list_plugins).collect(); - if let Some(path) = &self.plugin { - plugins.push(path); - } - plugins - } - - pub fn position_panes_in_space(&self, space: &PositionAndSize) -> Vec { + pub fn position_panes_in_space( + &self, + space: &PositionAndSize, + ) -> Vec<(Layout, PositionAndSize)> { split_space(space, &self) } } diff --git a/src/main.rs b/src/main.rs index 980f22b7..84dfac62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,26 +8,35 @@ mod errors; mod input; mod layout; mod os_input_output; +mod panes; mod pty_bus; mod screen; mod tab; -mod terminal_pane; mod utils; -#[cfg(feature = "wasm-wip")] + mod wasm_vm; +use std::cell::RefCell; +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 panes::PaneId; use serde::{Deserialize, Serialize}; use structopt::StructOpt; +use termion::input::TermRead; +use wasm_vm::PluginEnv; +use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; +use wasmer_wasi::{Pipe, WasiState}; use crate::cli::CliArgs; use crate::command_is_executing::CommandIsExecuting; -use crate::errors::{AppContext, ContextType, ErrorContext, PtyContext, ScreenContext}; +use crate::errors::{ + AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext, +}; use crate::input::input_loop; use crate::layout::Layout; use crate::os_input_output::{get_os_input, OsApi}; @@ -37,9 +46,9 @@ use crate::utils::{ consts::{MOSAIC_IPC_PIPE, MOSAIC_TMP_DIR, MOSAIC_TMP_LOG_DIR}, logging::*, }; -use std::cell::RefCell; +use crate::wasm_vm::{mosaic_imports, wasi_stdout, wasi_write_string, PluginInstruction}; -thread_local!(static OPENCALLS: RefCell = RefCell::new(ErrorContext::new())); +thread_local!(static OPENCALLS: RefCell = RefCell::default()); #[derive(Serialize, Deserialize, Debug)] enum ApiCommand { @@ -49,6 +58,9 @@ enum ApiCommand { MoveFocus, } +pub type ChannelWithContext = (Sender<(T, ErrorContext)>, Receiver<(T, ErrorContext)>); +pub type SyncChannelWithContext = (SyncSender<(T, ErrorContext)>, Receiver<(T, ErrorContext)>); + #[derive(Clone)] enum SenderType { Sender(Sender<(T, ErrorContext)>), @@ -125,42 +137,34 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let command_is_executing = CommandIsExecuting::new(); let full_screen_ws = os_input.get_terminal_size_using_fd(0); - os_input.into_raw_mode(0); - let (send_screen_instructions, receive_screen_instructions): ( - Sender<(ScreenInstruction, ErrorContext)>, - Receiver<(ScreenInstruction, ErrorContext)>, - ) = channel(); + os_input.set_raw_mode(0); + let (send_screen_instructions, receive_screen_instructions): ChannelWithContext< + ScreenInstruction, + > = channel(); let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); let mut send_screen_instructions = SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions)); - let (send_pty_instructions, receive_pty_instructions): ( - Sender<(PtyInstruction, ErrorContext)>, - Receiver<(PtyInstruction, ErrorContext)>, - ) = channel(); + + let (send_pty_instructions, receive_pty_instructions): ChannelWithContext = + channel(); let mut send_pty_instructions = SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions)); - #[cfg(feature = "wasm-wip")] - use crate::wasm_vm::PluginInstruction; - #[cfg(feature = "wasm-wip")] - let (send_plugin_instructions, receive_plugin_instructions): ( - Sender<(PluginInstruction, ErrorContext)>, - Receiver<(PluginInstruction, ErrorContext)>, - ) = channel(); - #[cfg(feature = "wasm-wip")] + let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext< + PluginInstruction, + > = channel(); let send_plugin_instructions = SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions)); - let (send_app_instructions, receive_app_instructions): ( - SyncSender<(AppInstruction, ErrorContext)>, - Receiver<(AppInstruction, ErrorContext)>, - ) = sync_channel(0); + let (send_app_instructions, receive_app_instructions): SyncChannelWithContext = + sync_channel(0); let send_app_instructions = SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions)); let mut pty_bus = PtyBus::new( receive_pty_instructions, send_screen_instructions.clone(), + send_plugin_instructions.clone(), os_input.clone(), opts.debug, ); @@ -180,17 +184,8 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .name("pty".to_string()) .spawn({ let mut command_is_executing = command_is_executing.clone(); - #[cfg(feature = "wasm-wip")] - let send_plugin_instructions = send_plugin_instructions.clone(); move || { if let Some(layout) = maybe_layout { - #[cfg(feature = "wasm-wip")] - for plugin_path in layout.list_plugins() { - dbg!(send_plugin_instructions - .send(PluginInstruction::Load(plugin_path.clone()))) - .unwrap(); - } - pty_bus.spawn_terminals_for_layout(layout); } else { let pid = pty_bus.spawn_terminal(None); @@ -212,21 +207,21 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let pid = pty_bus.spawn_terminal(file_to_open); pty_bus .send_screen_instructions - .send(ScreenInstruction::NewPane(pid)) + .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(pid)) + .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(pid)) + .send(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid))) .unwrap(); } PtyInstruction::NewTab => { @@ -261,6 +256,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let mut command_is_executing = command_is_executing.clone(); let os_input = os_input.clone(); let send_pty_instructions = send_pty_instructions.clone(); + let send_plugin_instructions = send_plugin_instructions.clone(); let send_app_instructions = send_app_instructions.clone(); let max_panes = opts.max_panes; @@ -268,6 +264,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let mut screen = Screen::new( receive_screen_instructions, send_pty_instructions, + send_plugin_instructions, send_app_instructions, &full_screen_ws, os_input, @@ -366,7 +363,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { screen .get_active_tab_mut() .unwrap() - .toggle_active_terminal_fullscreen(); + .toggle_active_pane_fullscreen(); } ScreenInstruction::NewTab(pane_id) => { screen.new_tab(pane_id); @@ -388,97 +385,104 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .unwrap(), ); - // Here be dragons! This is very much a work in progress, and isn't quite functional - // yet. It's being left out of the tests because is slows them down massively (by - // recompiling a WASM module for every single test). Stay tuned for more updates! - #[cfg(feature = "wasm-wip")] active_threads.push( thread::Builder::new() .name("wasm".to_string()) - .spawn(move || { - use crate::errors::PluginContext; - use crate::wasm_vm::{mosaic_imports, wasi_stdout}; - use std::io; - use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; - use wasmer_wasi::{Pipe, WasiState}; + .spawn({ + let mut send_pty_instructions = send_pty_instructions.clone(); + let mut send_screen_instructions = send_screen_instructions.clone(); - let store = Store::default(); + move || { + let store = Store::default(); - 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))); - // FIXME: Clueless on how many of these lines I need... - // screen.send_app_instructions.update(err_ctx); - // screen.send_pty_instructions.update(err_ctx); - match event { - PluginInstruction::Load(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(); + let mut plugin_id = 0; + let mut plugin_map = HashMap::new(); - 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(); + 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); + 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(); - let wasi = wasi_env.import_object(&module).unwrap(); - let mosaic = mosaic_imports(&store, &wasi_env); - let instance = Instance::new(&module, &mosaic.chain_back(wasi)).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 start = instance.exports.get_function("_start").unwrap(); - let handle_key = instance.exports.get_function("handle_key").unwrap(); - let draw = instance.exports.get_function("draw").unwrap(); + let wasi = wasi_env.import_object(&module).unwrap(); - // This eventually calls the `.init()` method - start.call(&[]).unwrap(); + let plugin_env = PluginEnv { + send_pty_instructions: send_pty_instructions.clone(), + wasi_env, + }; - #[warn(clippy::never_loop)] - loop { - let (cols, rows) = (80, 24); //terminal::size()?; - draw.call(&[Value::I32(rows), Value::I32(cols)]).unwrap(); + let mosaic = mosaic_imports(&store, &plugin_env); + let instance = + Instance::new(&module, &mosaic.chain_back(wasi)).unwrap(); - // Needed because raw mode doesn't implicitly return to the start of the line - write!( - io::stdout(), - "{}\n\r", - wasi_stdout(&wasi_env) - .lines() - .collect::>() - .join("\n\r") - ).unwrap(); + let start = instance.exports.get_function("_start").unwrap(); - /* match event::read().unwrap() { - Event::Key(KeyEvent { - code: KeyCode::Char('q'), - .. - }) => break, - Event::Key(e) => { - wasi_write_string(&wasi_env, serde_json::to_string(&e).unwrap()); - handle_key.call(&[])?; - } - _ => (), - } */ - break; + // 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; } - debug_log_to_file("WASM module loaded and exited cleanly :)".to_string()).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(); + } + 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(); + } + } + + send_screen_instructions + .send(ScreenInstruction::Render) + .unwrap(); + } + PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)), + PluginInstruction::Quit => break, } - PluginInstruction::Quit => break, - i => panic!("Yo, dawg, nice job calling the wasm thread!\n {:?} is defo not implemented yet...", i), } } - } - ).unwrap(), + }) + .unwrap(), ); // TODO: currently we don't push this into active_threads @@ -572,14 +576,14 @@ pub fn start(mut os_input: Box, opts: CliArgs) { AppInstruction::Exit => { let _ = send_screen_instructions.send(ScreenInstruction::Quit); let _ = send_pty_instructions.send(PtyInstruction::Quit); - #[cfg(feature = "wasm-wip")] + let _ = send_plugin_instructions.send(PluginInstruction::Quit); break; } AppInstruction::Error(backtrace) => { let _ = send_screen_instructions.send(ScreenInstruction::Quit); let _ = send_pty_instructions.send(PtyInstruction::Quit); - #[cfg(feature = "wasm-wip")] + let _ = send_plugin_instructions.send(PluginInstruction::Quit); os_input.unset_raw_mode(0); let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1); diff --git a/src/os_input_output.rs b/src/os_input_output.rs index 94b7b98d..41ee3b3c 100644 --- a/src/os_input_output.rs +++ b/src/os_input_output.rs @@ -1,4 +1,4 @@ -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use nix::fcntl::{fcntl, FcntlArg, OFlag}; use nix::pty::{forkpty, Winsize}; use nix::sys::signal::{kill, Signal}; @@ -75,6 +75,7 @@ fn handle_command_exit(mut child: Child) { } for signal in signals.pending() { + // FIXME: We need to handle more signals here! match signal { signal_hook::SIGINT => { child.kill().unwrap(); @@ -141,7 +142,7 @@ pub struct OsInputOutput { pub trait OsApi: Send + Sync { fn get_terminal_size_using_fd(&self, pid: RawFd) -> PositionAndSize; fn set_terminal_size_using_fd(&mut self, pid: RawFd, cols: u16, rows: u16); - fn into_raw_mode(&mut self, pid: RawFd); + fn set_raw_mode(&mut self, pid: RawFd); fn unset_raw_mode(&mut self, pid: RawFd); fn spawn_terminal(&mut self, file_to_open: Option) -> (RawFd, RawFd); fn read_from_tty_stdout(&mut self, pid: RawFd, buf: &mut [u8]) -> Result; @@ -160,7 +161,7 @@ impl OsApi for OsInputOutput { fn set_terminal_size_using_fd(&mut self, pid: RawFd, cols: u16, rows: u16) { set_terminal_size_using_fd(pid, cols, rows); } - fn into_raw_mode(&mut self, pid: RawFd) { + fn set_raw_mode(&mut self, pid: RawFd) { into_raw_mode(pid); } fn unset_raw_mode(&mut self, pid: RawFd) { diff --git a/src/terminal_pane/mod.rs b/src/panes/mod.rs similarity index 76% rename from src/terminal_pane/mod.rs rename to src/panes/mod.rs index c6c10be2..7d478886 100644 --- a/src/terminal_pane/mod.rs +++ b/src/panes/mod.rs @@ -1,7 +1,9 @@ +mod plugin_pane; mod scroll; mod terminal_character; mod terminal_pane; +pub use plugin_pane::*; pub use scroll::*; pub use terminal_character::*; pub use terminal_pane::*; diff --git a/src/panes/plugin_pane.rs b/src/panes/plugin_pane.rs new file mode 100644 index 00000000..1573d577 --- /dev/null +++ b/src/panes/plugin_pane.rs @@ -0,0 +1,167 @@ +#![allow(clippy::clippy::if_same_then_else)] + +use crate::{pty_bus::VteEvent, tab::Pane, wasm_vm::PluginInstruction, SenderWithContext}; + +use std::{sync::mpsc::channel, unimplemented}; + +use crate::panes::{PaneId, PositionAndSize}; + +pub struct PluginPane { + pub pid: u32, + pub should_render: bool, + pub position_and_size: PositionAndSize, + pub position_and_size_override: Option, + pub send_plugin_instructions: SenderWithContext, +} + +impl PluginPane { + pub fn new( + pid: u32, + position_and_size: PositionAndSize, + send_plugin_instructions: SenderWithContext, + ) -> Self { + Self { + pid, + should_render: true, + position_and_size, + position_and_size_override: None, + send_plugin_instructions, + } + } +} + +impl Pane for PluginPane { + // FIXME: These position and size things should all be moved to default trait implementations, + // with something like a get_pos_and_sz() method underpinning all of them. Alternatively and + // preferably, just use an enum and not a trait object + fn x(&self) -> usize { + self.position_and_size_override + .unwrap_or(self.position_and_size) + .x + } + fn y(&self) -> usize { + self.position_and_size_override + .unwrap_or(self.position_and_size) + .y + } + fn rows(&self) -> usize { + self.position_and_size_override + .unwrap_or(self.position_and_size) + .rows + } + fn columns(&self) -> usize { + self.position_and_size_override + .unwrap_or(self.position_and_size) + .columns + } + fn reset_size_and_position_override(&mut self) { + self.position_and_size_override = None; + self.should_render = true; + } + fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) { + self.position_and_size = *position_and_size; + self.should_render = true; + } + // FIXME: This is obviously a bit outdated and needs the x and y moved into `size` + fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) { + let position_and_size_override = PositionAndSize { + x, + y, + rows: size.rows, + columns: size.columns, + }; + self.position_and_size_override = Some(position_and_size_override); + self.should_render = true; + } + fn handle_event(&mut self, _event: VteEvent) { + unimplemented!() + } + fn cursor_coordinates(&self) -> Option<(usize, usize)> { + None + } + fn adjust_input_to_terminal(&self, _input_bytes: Vec) -> Vec { + unimplemented!() // FIXME: Shouldn't need this implmented? + } + + fn position_and_size_override(&self) -> Option { + self.position_and_size_override + } + fn should_render(&self) -> bool { + self.should_render + } + fn set_should_render(&mut self, should_render: bool) { + self.should_render = should_render; + } + fn render(&mut self) -> Option { + // if self.should_render { + if true { + // while checking should_render rather than rendering each pane every time + // is more performant, it causes some problems when the pane to the left should be + // rendered and has wide characters (eg. Chinese characters or emoji) + // as a (hopefully) temporary hack, we render all panes until we find a better solution + let (buf_tx, buf_rx) = channel(); + + self.send_plugin_instructions + .send(PluginInstruction::Draw( + buf_tx, + self.pid, + self.rows(), + self.columns(), + )) + .unwrap(); + + self.should_render = false; + Some(buf_rx.recv().unwrap()) + } else { + None + } + } + fn pid(&self) -> PaneId { + PaneId::Plugin(self.pid) + } + fn reduce_height_down(&mut self, count: usize) { + self.position_and_size.y += count; + self.position_and_size.rows -= count; + self.should_render = true; + } + fn increase_height_down(&mut self, count: usize) { + self.position_and_size.rows += count; + self.should_render = true; + } + fn increase_height_up(&mut self, count: usize) { + self.position_and_size.y -= count; + self.position_and_size.rows += count; + self.should_render = true; + } + fn reduce_height_up(&mut self, count: usize) { + self.position_and_size.rows -= count; + self.should_render = true; + } + fn reduce_width_right(&mut self, count: usize) { + self.position_and_size.x += count; + self.position_and_size.columns -= count; + self.should_render = true; + } + fn reduce_width_left(&mut self, count: usize) { + self.position_and_size.columns -= count; + self.should_render = true; + } + fn increase_width_left(&mut self, count: usize) { + self.position_and_size.x -= count; + self.position_and_size.columns += count; + self.should_render = true; + } + fn increase_width_right(&mut self, count: usize) { + self.position_and_size.columns += count; + self.should_render = true; + } + fn scroll_up(&mut self, _count: usize) { + unimplemented!() + } + fn scroll_down(&mut self, _count: usize) { + unimplemented!() + } + fn clear_scroll(&mut self) { + unimplemented!() + } +} diff --git a/src/terminal_pane/scroll.rs b/src/panes/scroll.rs similarity index 98% rename from src/terminal_pane/scroll.rs rename to src/panes/scroll.rs index 0bf4c731..98d1b6f4 100644 --- a/src/terminal_pane/scroll.rs +++ b/src/panes/scroll.rs @@ -1,7 +1,10 @@ use std::collections::VecDeque; -use std::fmt::{self, Debug, Formatter}; +use std::{ + cmp::max, + fmt::{self, Debug, Formatter}, +}; -use crate::terminal_pane::terminal_character::{ +use crate::panes::terminal_character::{ CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER, }; @@ -341,10 +344,8 @@ impl Scroll { if lines_to_skip > 0 { lines_to_skip -= 1; } else { - for _ in line.len()..self.total_columns { - // pad line if needed - line.push(EMPTY_TERMINAL_CHARACTER); - } + // pad line if needed + line.resize(self.total_columns, EMPTY_TERMINAL_CHARACTER); lines.push_front(line); } if lines.len() == self.lines_in_view { @@ -477,9 +478,14 @@ impl Scroll { count }; - for _ in current_fragment.characters.len()..current_cursor_column_position + move_count { - current_fragment.characters.push(EMPTY_TERMINAL_CHARACTER); - } + current_fragment.characters.resize( + max( + current_fragment.characters.len(), + current_cursor_column_position + move_count, + ), + EMPTY_TERMINAL_CHARACTER, + ); + self.cursor_position.move_forward(move_count); } pub fn move_cursor_back(&mut self, count: usize) { @@ -645,9 +651,10 @@ impl Scroll { .get_mut(current_line_wrap_position) .expect("cursor out of bounds"); - for _ in current_fragment.characters.len()..col { - current_fragment.characters.push(EMPTY_TERMINAL_CHARACTER); - } + current_fragment.characters.resize( + max(current_fragment.characters.len(), col), + EMPTY_TERMINAL_CHARACTER, + ); self.cursor_position.move_to_column(col); } pub fn move_cursor_to_column(&mut self, col: usize) { @@ -680,9 +687,7 @@ impl Scroll { self.scroll_region = None; } fn scroll_region_absolute_indices(&mut self) -> Option<(usize, usize)> { - if self.scroll_region.is_none() { - return None; - }; + self.scroll_region?; if self.canonical_lines.len() > self.lines_in_view { let absolute_top = self.canonical_lines.len() - 1 - self.lines_in_view; let absolute_bottom = self.canonical_lines.len() - 1; diff --git a/src/terminal_pane/terminal_character.rs b/src/panes/terminal_character.rs similarity index 98% rename from src/terminal_pane/terminal_character.rs rename to src/panes/terminal_character.rs index 28d638f9..80c3162b 100644 --- a/src/terminal_pane/terminal_character.rs +++ b/src/panes/terminal_character.rs @@ -390,7 +390,7 @@ impl CharacterStyles { } } if let Some(next_params) = ansi_params.get(params_used..) { - if next_params.len() > 0 { + if !next_params.is_empty() { self.add_style_from_ansi_params(next_params); } } @@ -536,14 +536,9 @@ impl Display for CharacterStyles { write!(f, "\u{1b}[2m")?; } AnsiCode::Reset => { - if let Some(bold) = self.bold { + if let Some(AnsiCode::Reset) = self.bold { // we only reset dim if both dim and bold should be reset - match bold { - AnsiCode::Reset => { - write!(f, "\u{1b}[22m")?; - } - _ => {} - } + write!(f, "\u{1b}[22m")?; } } _ => {} diff --git a/src/terminal_pane/terminal_pane.rs b/src/panes/terminal_pane.rs similarity index 76% rename from src/terminal_pane/terminal_pane.rs rename to src/panes/terminal_pane.rs index b61f1536..0d04c681 100644 --- a/src/terminal_pane/terminal_pane.rs +++ b/src/panes/terminal_pane.rs @@ -5,12 +5,17 @@ use ::nix::pty::Winsize; use ::std::os::unix::io::RawFd; use ::vte::Perform; -use crate::terminal_pane::terminal_character::{CharacterStyles, NamedColor, TerminalCharacter}; -use crate::terminal_pane::Scroll; +use crate::panes::terminal_character::{CharacterStyles, TerminalCharacter}; +use crate::panes::Scroll; use crate::utils::logging::debug_log_to_file; use crate::VteEvent; -#[derive(Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)] +pub enum PaneId { + Terminal(RawFd), + Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct? +} +#[derive(Clone, Copy, Debug, Default)] pub struct PositionAndSize { pub x: usize, pub y: usize, @@ -18,13 +23,12 @@ pub struct PositionAndSize { pub columns: usize, } -impl PositionAndSize { - pub fn from(winsize: Winsize) -> PositionAndSize { +impl From for PositionAndSize { + fn from(winsize: Winsize) -> PositionAndSize { PositionAndSize { columns: winsize.ws_col as usize, rows: winsize.ws_row as usize, - x: winsize.ws_xpixel as usize, - y: winsize.ws_ypixel as usize, + ..Default::default() } } } @@ -58,30 +62,12 @@ impl Pane for TerminalPane { self.reflow_lines(); self.mark_for_rerender(); } - fn change_size_p(&mut self, position_and_size: &PositionAndSize) { - self.position_and_size = *position_and_size; + fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) { + self.position_and_size.columns = position_and_size.columns; + self.position_and_size.rows = position_and_size.rows; self.reflow_lines(); self.mark_for_rerender(); } - fn get_rows(&self) -> usize { - match &self.position_and_size_override.as_ref() { - Some(position_and_size_override) => position_and_size_override.rows, - None => self.position_and_size.rows as usize, - } - } - fn get_columns(&self) -> usize { - match &self.position_and_size_override.as_ref() { - Some(position_and_size_override) => position_and_size_override.columns, - None => self.position_and_size.columns as usize, - } - } - fn change_size(&mut self, ws: &PositionAndSize) { - self.position_and_size.columns = ws.columns; - self.position_and_size.rows = ws.rows; - self.reflow_lines(); - self.mark_for_rerender(); - } - fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) { let position_and_size_override = PositionAndSize { x, @@ -179,8 +165,7 @@ impl Pane for TerminalPane { fn set_should_render(&mut self, should_render: bool) { self.should_render = should_render; } - fn buffer_as_vte_output(&mut self) -> Option { - // TODO: rename to render + fn render(&mut self) -> Option { // if self.should_render { if true { // while checking should_render rather than rendering each pane every time @@ -219,8 +204,8 @@ impl Pane for TerminalPane { None } } - fn pid(&self) -> RawFd { - self.pid + fn pid(&self) -> PaneId { + PaneId::Terminal(self.pid) } fn reduce_height_down(&mut self, count: usize) { self.position_and_size.y += count; @@ -281,15 +266,10 @@ impl Pane for TerminalPane { } impl TerminalPane { - pub fn new(pid: RawFd, ws: PositionAndSize, x: usize, y: usize) -> TerminalPane { - let scroll = Scroll::new(ws.columns, ws.rows); + pub fn new(pid: RawFd, position_and_size: PositionAndSize) -> TerminalPane { + let scroll = Scroll::new(position_and_size.columns, position_and_size.rows); let pending_styles = CharacterStyles::new(); - let position_and_size = PositionAndSize { - x, - y, - rows: ws.rows, - columns: ws.columns, - }; + TerminalPane { pid, scroll, @@ -303,43 +283,6 @@ impl TerminalPane { pub fn mark_for_rerender(&mut self) { self.should_render = true; } - pub fn handle_event(&mut self, event: VteEvent) { - match event { - VteEvent::Print(c) => { - self.print(c); - self.mark_for_rerender(); - } - VteEvent::Execute(byte) => { - self.execute(byte); - } - VteEvent::Hook(params, intermediates, ignore, c) => { - self.hook(¶ms, &intermediates, ignore, c); - } - VteEvent::Put(byte) => { - self.put(byte); - } - VteEvent::Unhook => { - self.unhook(); - } - VteEvent::OscDispatch(params, bell_terminated) => { - let params: Vec<&[u8]> = params.iter().map(|p| &p[..]).collect(); - self.osc_dispatch(¶ms[..], bell_terminated); - } - VteEvent::CsiDispatch(params, intermediates, ignore, c) => { - self.csi_dispatch(¶ms, &intermediates, ignore, c); - } - VteEvent::EscDispatch(intermediates, ignore, byte) => { - self.esc_dispatch(&intermediates, ignore, byte); - } - } - } - // TODO: merge these two methods - pub fn change_size(&mut self, ws: &PositionAndSize) { - self.position_and_size.columns = ws.columns; - self.position_and_size.rows = ws.rows; - self.reflow_lines(); - self.mark_for_rerender(); - } pub fn get_x(&self) -> usize { match self.position_and_size_override { Some(position_and_size_override) => position_and_size_override.x, @@ -369,61 +312,15 @@ impl TerminalPane { let columns = self.get_columns(); self.scroll.change_size(columns, rows); } - pub fn buffer_as_vte_output(&mut self) -> Option { - // TODO: rename to render - // if self.should_render { - if true { - // while checking should_render rather than rendering each pane every time - // is more performant, it causes some problems when the pane to the left should be - // rendered and has wide characters (eg. Chinese characters or emoji) - // as a (hopefully) temporary hack, we render all panes until we find a better solution - let mut vte_output = String::new(); - let buffer_lines = &self.read_buffer_as_lines(); - let display_cols = self.get_columns(); - let mut character_styles = CharacterStyles::new(); - for (row, line) in buffer_lines.iter().enumerate() { - let x = self.get_x(); - let y = self.get_y(); - vte_output = format!("{}\u{1b}[{};{}H\u{1b}[m", vte_output, y + row + 1, x + 1); // goto row/col and reset styles - for (col, t_character) in line.iter().enumerate() { - if col < display_cols { - // in some cases (eg. while resizing) some characters will spill over - // before they are corrected by the shell (for the prompt) or by reflowing - // lines - if let Some(new_styles) = - character_styles.update_and_return_diff(&t_character.styles) - { - // the terminal keeps the previous styles as long as we're in the same - // line, so we only want to update the new styles here (this also - // includes resetting previous styles as needed) - vte_output = format!("{}{}", vte_output, new_styles); - } - vte_output.push(t_character.character); - } - } - character_styles.clear(); - } - self.mark_for_rerender(); - Some(vte_output) - } else { - None - } - } + pub fn read_buffer_as_lines(&self) -> Vec> { self.scroll.as_character_lines() } + #[cfg(test)] pub fn cursor_coordinates(&self) -> Option<(usize, usize)> { // (x, y) self.scroll.cursor_coordinates_on_screen() } - pub fn scroll_up(&mut self, count: usize) { - self.scroll.move_viewport_up(count); - self.mark_for_rerender(); - } - pub fn scroll_down(&mut self, count: usize) { - self.scroll.move_viewport_down(count); - self.mark_for_rerender(); - } pub fn rotate_scroll_region_up(&mut self, count: usize) { self.scroll.rotate_scroll_region_up(count); self.mark_for_rerender(); @@ -432,22 +329,6 @@ impl TerminalPane { self.scroll.rotate_scroll_region_down(count); self.mark_for_rerender(); } - pub fn clear_scroll(&mut self) { - self.scroll.reset_viewport(); - self.mark_for_rerender(); - } - pub fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) { - let position_and_size_override = PositionAndSize { - x, - y, - rows: size.rows, - columns: size.columns, - }; - self.position_and_size_override = Some(position_and_size_override); - self.reflow_lines(); - self.mark_for_rerender(); - } - fn add_newline(&mut self) { self.scroll.add_canonical_line(); // self.reset_all_ansi_codes(); // TODO: find out if we should be resetting here or not @@ -558,12 +439,10 @@ impl vte::Perform for TerminalPane { } else { (params[0] as usize - 1, params[0] as usize) } + } else if params[0] == 0 { + (0, params[1] as usize - 1) } else { - if params[0] == 0 { - (0, params[1] as usize - 1) - } else { - (params[0] as usize - 1, params[1] as usize - 1) - } + (params[0] as usize - 1, params[1] as usize - 1) }; self.scroll.move_cursor_to(row, col); } else if c == 'A' { @@ -723,11 +602,8 @@ impl vte::Perform for TerminalPane { } fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) { - match (byte, intermediates.get(0)) { - (b'M', None) => { - self.scroll.move_cursor_up_in_scroll_region(1); - } - _ => {} + if let (b'M', None) = (byte, intermediates.get(0)) { + self.scroll.move_cursor_up_in_scroll_region(1); } } } diff --git a/src/pty_bus.rs b/src/pty_bus.rs index 6eed4b25..e635b176 100644 --- a/src/pty_bus.rs +++ b/src/pty_bus.rs @@ -9,10 +9,13 @@ use ::std::time::{Duration, Instant}; use ::vte; use std::path::PathBuf; -use crate::errors::{ContextType, ErrorContext}; -use crate::layout::Layout; use crate::os_input_output::OsApi; use crate::utils::logging::debug_to_file; +use crate::{ + errors::{ContextType, ErrorContext}, + panes::PaneId, +}; +use crate::{layout::Layout, wasm_vm::PluginInstruction}; use crate::{ScreenInstruction, SenderWithContext, OPENCALLS}; pub struct ReadFromPid { @@ -148,13 +151,14 @@ pub enum PtyInstruction { SpawnTerminalVertically(Option), SpawnTerminalHorizontally(Option), NewTab, - ClosePane(RawFd), - CloseTab(Vec), + ClosePane(PaneId), + CloseTab(Vec), Quit, } pub struct PtyBus { pub send_screen_instructions: SenderWithContext, + pub send_plugin_instructions: SenderWithContext, pub receive_pty_instructions: Receiver<(PtyInstruction, ErrorContext)>, pub id_to_child_pid: HashMap, os_input: Box, @@ -231,7 +235,7 @@ fn stream_terminal_bytes( // we read everything, rather than hanging until there is new data // a better solution would be to fix the test fakes, but this will do for now send_screen_instructions - .send(ScreenInstruction::ClosePane(pid)) + .send(ScreenInstruction::ClosePane(PaneId::Terminal(pid))) .unwrap(); } }); @@ -241,11 +245,13 @@ impl PtyBus { pub fn new( receive_pty_instructions: Receiver<(PtyInstruction, ErrorContext)>, send_screen_instructions: SenderWithContext, + send_plugin_instructions: SenderWithContext, os_input: Box, debug_to_file: bool, ) -> Self { PtyBus { send_screen_instructions, + send_plugin_instructions, receive_pty_instructions, os_input, id_to_child_pid: HashMap::new(), @@ -265,7 +271,7 @@ impl PtyBus { pid_primary } pub fn spawn_terminals_for_layout(&mut self, layout: Layout) { - let total_panes = layout.total_panes(); + let total_panes = layout.total_terminal_panes(); let mut new_pane_pids = vec![]; for _ in 0..total_panes { let (pid_primary, pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal(None); @@ -287,11 +293,19 @@ impl PtyBus { ); } } - pub fn close_pane(&mut self, id: RawFd) { - let child_pid = self.id_to_child_pid.get(&id).unwrap(); - self.os_input.kill(*child_pid).unwrap(); + pub fn close_pane(&mut self, id: PaneId) { + match id { + PaneId::Terminal(id) => { + let child_pid = self.id_to_child_pid.get(&id).unwrap(); + self.os_input.kill(*child_pid).unwrap(); + } + PaneId::Plugin(pid) => self + .send_plugin_instructions + .send(PluginInstruction::Unload(pid)) + .unwrap(), + } } - pub fn close_tab(&mut self, ids: Vec) { - ids.iter().for_each(|id| self.close_pane(*id)); + pub fn close_tab(&mut self, ids: Vec) { + ids.iter().for_each(|&id| self.close_pane(id)); } } diff --git a/src/screen.rs b/src/screen.rs index b8249a5b..365d1d7a 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -2,12 +2,12 @@ use std::collections::BTreeMap; use std::os::unix::io::RawFd; use std::sync::mpsc::Receiver; -use crate::errors::ErrorContext; -use crate::layout::Layout; use crate::os_input_output::OsApi; +use crate::panes::PositionAndSize; use crate::pty_bus::{PtyInstruction, VteEvent}; use crate::tab::Tab; -use crate::terminal_pane::PositionAndSize; +use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; +use crate::{layout::Layout, panes::PaneId}; use crate::{AppInstruction, SenderWithContext}; /* @@ -23,9 +23,9 @@ use crate::{AppInstruction, SenderWithContext}; pub enum ScreenInstruction { Pty(RawFd, VteEvent), Render, - NewPane(RawFd), - HorizontalSplit(RawFd), - VerticalSplit(RawFd), + NewPane(PaneId), + HorizontalSplit(PaneId), + VerticalSplit(PaneId), WriteCharacter(Vec), ResizeLeft, ResizeRight, @@ -42,7 +42,7 @@ pub enum ScreenInstruction { ClearScroll, CloseFocusedPane, ToggleActiveTerminalFullscreen, - ClosePane(RawFd), + ClosePane(PaneId), ApplyLayout((Layout, Vec)), NewTab(RawFd), SwitchTabNext, @@ -55,6 +55,7 @@ pub struct Screen { max_panes: Option, tabs: BTreeMap, pub send_pty_instructions: SenderWithContext, + pub send_plugin_instructions: SenderWithContext, pub send_app_instructions: SenderWithContext, full_screen_ws: PositionAndSize, active_tab_index: Option, @@ -65,6 +66,7 @@ impl Screen { pub fn new( receive_screen_instructions: Receiver<(ScreenInstruction, ErrorContext)>, send_pty_instructions: SenderWithContext, + send_plugin_instructions: SenderWithContext, send_app_instructions: SenderWithContext, full_screen_ws: &PositionAndSize, os_api: Box, @@ -74,6 +76,7 @@ impl Screen { receiver: receive_screen_instructions, max_panes, send_pty_instructions, + send_plugin_instructions, send_app_instructions, full_screen_ws: *full_screen_ws, active_tab_index: None, @@ -88,9 +91,10 @@ impl Screen { &self.full_screen_ws, self.os_api.clone(), self.send_pty_instructions.clone(), + self.send_plugin_instructions.clone(), self.send_app_instructions.clone(), self.max_panes, - Some(pane_id), + Some(PaneId::Terminal(pane_id)), ); self.active_tab_index = Some(tab_index); self.tabs.insert(tab_index, tab); @@ -119,7 +123,7 @@ impl Screen { let active_tab_id = self.get_active_tab().unwrap().index; let tab_ids: Vec = self.tabs.keys().copied().collect(); let first_tab = tab_ids.get(0).unwrap(); - let last_tab = tab_ids.get(tab_ids.len() - 1).unwrap(); + let last_tab = tab_ids.last().unwrap(); let active_tab_id_position = tab_ids.iter().position(|id| id == &active_tab_id).unwrap(); if active_tab_id == *first_tab { @@ -135,18 +139,18 @@ impl Screen { self.switch_tab_prev(); } let mut active_tab = self.tabs.remove(&active_tab_index).unwrap(); - let pane_ids = active_tab.get_terminal_pane_ids(); + let pane_ids = active_tab.get_pane_ids(); self.send_pty_instructions .send(PtyInstruction::CloseTab(pane_ids)) .unwrap(); - if self.tabs.len() == 0 { + if self.tabs.is_empty() { self.active_tab_index = None; self.render(); } } pub fn render(&mut self) { if let Some(active_tab) = self.get_active_tab_mut() { - if active_tab.get_active_terminal().is_some() { + if active_tab.get_active_pane().is_some() { active_tab.render(); } else { self.close_tab(); @@ -181,6 +185,7 @@ impl Screen { &self.full_screen_ws, self.os_api.clone(), self.send_pty_instructions.clone(), + self.send_plugin_instructions.clone(), self.send_app_instructions.clone(), self.max_panes, None, diff --git a/src/tab.rs b/src/tab.rs index e647c8d7..7a56508c 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1,13 +1,15 @@ -use std::collections::{BTreeMap, HashSet}; -use std::io::Write; -use std::os::unix::io::RawFd; - -use crate::boundaries::Boundaries; -use crate::layout::Layout; -use crate::os_input_output::OsApi; +use crate::panes::{PaneId, PositionAndSize, TerminalPane}; use crate::pty_bus::{PtyInstruction, VteEvent}; -use crate::terminal_pane::{PositionAndSize, TerminalPane}; +use crate::{boundaries::Boundaries, panes::PluginPane}; +use crate::{layout::Layout, wasm_vm::PluginInstruction}; +use crate::{os_input_output::OsApi, utils::shared::pad_to_size}; use crate::{AppInstruction, SenderWithContext}; +use std::os::unix::io::RawFd; +use std::{ + cmp::Reverse, + collections::{BTreeMap, HashSet}, +}; +use std::{io::Write, sync::mpsc::channel}; /* * Tab @@ -20,7 +22,7 @@ use crate::{AppInstruction, SenderWithContext}; const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this -type BorderAndPaneIds = (usize, Vec); +type BorderAndPaneIds = (usize, Vec); fn split_vertically_with_gap(rect: &PositionAndSize) -> (PositionAndSize, PositionAndSize) { let width_of_each_half = (rect.columns - 1) / 2; @@ -31,6 +33,7 @@ fn split_vertically_with_gap(rect: &PositionAndSize) -> (PositionAndSize, Positi } else { first_rect.columns = width_of_each_half; } + second_rect.x = first_rect.x + first_rect.columns + 1; second_rect.columns = width_of_each_half; (first_rect, second_rect) } @@ -44,25 +47,22 @@ fn split_horizontally_with_gap(rect: &PositionAndSize) -> (PositionAndSize, Posi } else { first_rect.rows = height_of_each_half; } + second_rect.y = first_rect.y + first_rect.rows + 1; second_rect.rows = height_of_each_half; (first_rect, second_rect) } -#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy)] -enum PaneKind { - Terminal(RawFd), - BuiltInPane(u32), -} pub struct Tab { pub index: usize, - panes: BTreeMap>, - panes_to_hide: HashSet, - active_terminal: Option, + panes: BTreeMap>, + panes_to_hide: HashSet, + active_terminal: Option, max_panes: Option, full_screen_ws: PositionAndSize, fullscreen_is_active: bool, os_api: Box, pub send_pty_instructions: SenderWithContext, + pub send_plugin_instructions: SenderWithContext, pub send_app_instructions: SenderWithContext, } @@ -72,10 +72,7 @@ pub trait Pane { fn rows(&self) -> usize; fn columns(&self) -> usize; fn reset_size_and_position_override(&mut self); - fn change_size_p(&mut self, position_and_size: &PositionAndSize); - fn get_rows(&self) -> usize; - fn get_columns(&self) -> usize; - fn change_size(&mut self, ws: &PositionAndSize); + fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize); fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize); fn handle_event(&mut self, event: VteEvent); fn cursor_coordinates(&self) -> Option<(usize, usize)>; @@ -84,8 +81,8 @@ pub trait Pane { fn position_and_size_override(&self) -> Option; fn should_render(&self) -> bool; fn set_should_render(&mut self, should_render: bool); - fn buffer_as_vte_output(&mut self) -> Option; - fn pid(&self) -> RawFd; + fn render(&mut self) -> Option; + fn pid(&self) -> PaneId; fn reduce_height_down(&mut self, count: usize); fn increase_height_down(&mut self, count: usize); fn increase_height_up(&mut self, count: usize); @@ -104,30 +101,30 @@ pub trait Pane { fn bottom_boundary_y_coords(&self) -> usize { self.y() + self.rows() } - fn is_directly_right_of(&self, other: &Box) -> bool { + fn is_directly_right_of(&self, other: &dyn Pane) -> bool { self.x() == other.x() + other.columns() + 1 } - fn is_directly_left_of(&self, other: &Box) -> bool { + fn is_directly_left_of(&self, other: &dyn Pane) -> bool { self.x() + self.columns() + 1 == other.x() } - fn is_directly_below(&self, other: &Box) -> bool { + fn is_directly_below(&self, other: &dyn Pane) -> bool { self.y() == other.y() + other.rows() + 1 } - fn is_directly_above(&self, other: &Box) -> bool { + fn is_directly_above(&self, other: &dyn Pane) -> bool { self.y() + self.rows() + 1 == other.y() } - fn horizontally_overlaps_with(&self, other: &Box) -> bool { + fn horizontally_overlaps_with(&self, other: &dyn Pane) -> bool { (self.y() >= other.y() && self.y() <= (other.y() + other.rows())) || ((self.y() + self.rows()) <= (other.y() + other.rows()) && (self.y() + self.rows()) > other.y()) || (self.y() <= other.y() && (self.y() + self.rows() >= (other.y() + other.rows()))) || (other.y() <= self.y() && (other.y() + other.rows() >= (self.y() + self.rows()))) } - fn get_horizontal_overlap_with(&self, other: &Box) -> usize { + fn get_horizontal_overlap_with(&self, other: &dyn Pane) -> usize { std::cmp::min(self.y() + self.rows(), other.y() + other.rows()) - std::cmp::max(self.y(), other.y()) } - fn vertically_overlaps_with(&self, other: &Box) -> bool { + fn vertically_overlaps_with(&self, other: &dyn Pane) -> bool { (self.x() >= other.x() && self.x() <= (other.x() + other.columns())) || ((self.x() + self.columns()) <= (other.x() + other.columns()) && (self.x() + self.columns()) > other.x()) @@ -136,37 +133,39 @@ pub trait Pane { || (other.x() <= self.x() && (other.x() + other.columns() >= (self.x() + self.columns()))) } - fn get_vertical_overlap_with(&self, other: &Box) -> usize { + fn get_vertical_overlap_with(&self, other: &dyn Pane) -> usize { std::cmp::min(self.x() + self.columns(), other.x() + other.columns()) - std::cmp::max(self.x(), other.x()) } } impl Tab { + // FIXME: Too many arguments here! Maybe bundle all of the senders for the whole program in a struct? pub fn new( index: usize, full_screen_ws: &PositionAndSize, mut os_api: Box, send_pty_instructions: SenderWithContext, + send_plugin_instructions: SenderWithContext, send_app_instructions: SenderWithContext, max_panes: Option, - pane_id: Option, + pane_id: Option, ) -> Self { - let panes = if let Some(pid) = pane_id { - let new_terminal = TerminalPane::new(pid, *full_screen_ws, 0, 0); + let panes = if let Some(PaneId::Terminal(pid)) = pane_id { + let new_terminal = TerminalPane::new(pid, *full_screen_ws); os_api.set_terminal_size_using_fd( new_terminal.pid, - new_terminal.get_columns() as u16, - new_terminal.get_rows() as u16, + new_terminal.columns() as u16, + new_terminal.rows() as u16, ); - let mut panes: BTreeMap> = BTreeMap::new(); - panes.insert(PaneKind::Terminal(pid), Box::new(new_terminal)); + let mut panes: BTreeMap> = BTreeMap::new(); + panes.insert(PaneId::Terminal(pid), Box::new(new_terminal)); panes } else { BTreeMap::new() }; Tab { - index: index, + index, panes, max_panes, panes_to_hide: HashSet::new(), @@ -176,6 +175,7 @@ impl Tab { os_api, send_app_instructions, send_pty_instructions, + send_plugin_instructions, } } @@ -192,11 +192,11 @@ impl Tab { let mut positions_and_size = positions_in_layout.iter(); for (pane_kind, terminal_pane) in self.panes.iter_mut() { // for now the layout only supports terminal panes - if let PaneKind::Terminal(pid) = pane_kind { + if let PaneId::Terminal(pid) = pane_kind { match positions_and_size.next() { - Some(position_and_size) => { + Some((_, position_and_size)) => { terminal_pane.reset_size_and_position_override(); - terminal_pane.change_size_p(&position_and_size); + terminal_pane.change_pos_and_size(&position_and_size); self.os_api.set_terminal_size_using_fd( *pid, position_and_size.columns as u16, @@ -206,295 +206,295 @@ impl Tab { None => { // we filled the entire layout, no room for this pane // TODO: handle active terminal - self.panes_to_hide.insert(*pid); + self.panes_to_hide.insert(PaneId::Terminal(*pid)); } } } } let mut new_pids = new_pids.iter(); - for position_and_size in positions_and_size { - // there are still panes left to fill, use the pids we received in this method - let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout - let mut new_terminal = TerminalPane::new( - *pid, - self.full_screen_ws, - position_and_size.x, - position_and_size.y, - ); - new_terminal.change_size_p(position_and_size); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - new_terminal.get_columns() as u16, - new_terminal.get_rows() as u16, - ); - self.panes - .insert(PaneKind::Terminal(*pid), Box::new(new_terminal)); + for (layout, position_and_size) in positions_and_size { + // Just a regular terminal + if let Some(plugin) = &layout.plugin { + let (pid_tx, pid_rx) = channel(); + self.send_plugin_instructions + .send(PluginInstruction::Load(pid_tx, plugin.clone())) + .unwrap(); + let pid = pid_rx.recv().unwrap(); + let new_plugin = PluginPane::new( + pid, + *position_and_size, + self.send_plugin_instructions.clone(), + ); + self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin)); + } else { + // there are still panes left to fill, use the pids we received in this method + let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout + let new_terminal = TerminalPane::new(*pid, *position_and_size); + self.os_api.set_terminal_size_using_fd( + new_terminal.pid, + new_terminal.columns() as u16, + new_terminal.rows() as u16, + ); + self.panes + .insert(PaneId::Terminal(*pid), Box::new(new_terminal)); + } } for unused_pid in new_pids { // this is a bit of a hack and happens because we don't have any central location that // can query the screen as to how many panes it needs to create a layout // fixing this will require a bit of an architecture change self.send_pty_instructions - .send(PtyInstruction::ClosePane(*unused_pid)) + .send(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid))) .unwrap(); } - self.active_terminal = self - .panes - .iter() - .filter_map(|(pane_kind, _)| match pane_kind { - PaneKind::Terminal(pid) => Some(*pid), - _ => None, - }) - .next(); + self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next(); self.render(); } - pub fn new_pane(&mut self, pid: RawFd) { + pub fn new_pane(&mut self, pid: PaneId) { self.close_down_to_max_terminals(); if self.fullscreen_is_active { - self.toggle_active_terminal_fullscreen(); + self.toggle_active_pane_fullscreen(); } - if !self.has_terminal_panes() { - let x = 0; - let y = 0; - let new_terminal = TerminalPane::new(pid, self.full_screen_ws, x, y); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - new_terminal.get_columns() as u16, - new_terminal.get_rows() as u16, - ); - self.panes - .insert(PaneKind::Terminal(pid), Box::new(new_terminal)); - self.active_terminal = Some(pid); + 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( + new_terminal.pid, + new_terminal.columns() as u16, + new_terminal.rows() as u16, + ); + self.panes.insert(pid, Box::new(new_terminal)); + self.active_terminal = Some(pid); + } } else { // TODO: check minimum size of active terminal - let (_longest_edge, terminal_id_to_split) = self.get_terminals().fold( - (0, 0), + let (_longest_edge, terminal_id_to_split) = self.get_panes().fold( + (0, PaneId::Terminal(0)), // FIXME: This is a bit hacky, try to use a maximum method? |(current_longest_edge, current_terminal_id_to_split), id_and_terminal_to_check| { let (id_of_terminal_to_check, terminal_to_check) = id_and_terminal_to_check; - let terminal_size = (terminal_to_check.get_rows() * CURSOR_HEIGHT_WIDTH_RATIO) - * terminal_to_check.get_columns(); + let terminal_size = (terminal_to_check.rows() * CURSOR_HEIGHT_WIDTH_RATIO) + * terminal_to_check.columns(); if terminal_size > current_longest_edge { - (terminal_size, id_of_terminal_to_check) + (terminal_size, *id_of_terminal_to_check) } else { (current_longest_edge, current_terminal_id_to_split) } }, ); - let terminal_to_split = self - .panes - .get_mut(&PaneKind::Terminal(terminal_id_to_split)) - .unwrap(); + let terminal_to_split = self.panes.get_mut(&terminal_id_to_split).unwrap(); let terminal_ws = PositionAndSize { - rows: terminal_to_split.get_rows(), - columns: terminal_to_split.get_columns(), + rows: terminal_to_split.rows(), + columns: terminal_to_split.columns(), x: terminal_to_split.x(), y: terminal_to_split.y(), }; - if terminal_to_split.get_rows() * CURSOR_HEIGHT_WIDTH_RATIO - > terminal_to_split.get_columns() - { + if terminal_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.columns() { + // 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); + self.os_api.set_terminal_size_using_fd( + new_terminal.pid, + bottom_winsize.columns as u16, + bottom_winsize.rows as u16, + ); + terminal_to_split.change_pos_and_size(&top_winsize); + self.panes.insert(pid, Box::new(new_terminal)); + if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split { + self.os_api.set_terminal_size_using_fd( + terminal_id_to_split, + top_winsize.columns as u16, + top_winsize.rows as u16, + ); + } + self.active_terminal = Some(pid); + } + } else { + // FIXME: This could use a second look + if let PaneId::Terminal(term_pid) = pid { + let (left_winszie, right_winsize) = split_vertically_with_gap(&terminal_ws); + let new_terminal = TerminalPane::new(term_pid, right_winsize); + self.os_api.set_terminal_size_using_fd( + new_terminal.pid, + right_winsize.columns as u16, + right_winsize.rows as u16, + ); + terminal_to_split.change_pos_and_size(&left_winszie); + self.panes.insert(pid, Box::new(new_terminal)); + if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split { + self.os_api.set_terminal_size_using_fd( + terminal_id_to_split, + left_winszie.columns as u16, + left_winszie.rows as u16, + ); + } + } + } + self.active_terminal = Some(pid); + self.render(); + } + } + pub fn horizontal_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( + new_terminal.pid, + new_terminal.columns() as u16, + new_terminal.rows() as u16, + ); + 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 { + // 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(); + let terminal_ws = PositionAndSize { + x: active_pane.x(), + y: active_pane.y(), + rows: active_pane.rows(), + columns: active_pane.columns(), + }; let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws); - let bottom_half_y = terminal_ws.y + top_winsize.rows + 1; - let new_terminal = - TerminalPane::new(pid, bottom_winsize, terminal_ws.x, bottom_half_y); + + active_pane.change_pos_and_size(&top_winsize); + + let new_terminal = TerminalPane::new(term_pid, bottom_winsize); self.os_api.set_terminal_size_using_fd( new_terminal.pid, bottom_winsize.columns as u16, bottom_winsize.rows as u16, ); - terminal_to_split.change_size(&top_winsize); - self.panes - .insert(PaneKind::Terminal(pid), Box::new(new_terminal)); - self.os_api.set_terminal_size_using_fd( - terminal_id_to_split, - top_winsize.columns as u16, - top_winsize.rows as u16, - ); + self.panes.insert(pid, Box::new(new_terminal)); + + if let PaneId::Terminal(active_terminal_pid) = active_pane_id { + self.os_api.set_terminal_size_using_fd( + *active_terminal_pid, + top_winsize.columns as u16, + top_winsize.rows as u16, + ); + } + self.active_terminal = Some(pid); - } else { - let (left_winszie, right_winsize) = split_vertically_with_gap(&terminal_ws); - let right_side_x = (terminal_ws.x + left_winszie.columns + 1) as usize; - let new_terminal = - TerminalPane::new(pid, right_winsize, right_side_x, terminal_ws.y); + 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( + new_terminal.pid, + new_terminal.columns() as u16, + new_terminal.rows() as u16, + ); + 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 { + // 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(); + let terminal_ws = PositionAndSize { + x: active_pane.x(), + y: active_pane.y(), + rows: active_pane.rows(), + columns: active_pane.columns(), + }; + let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws); + + active_pane.change_pos_and_size(&left_winsize); + + let new_terminal = TerminalPane::new(term_pid, right_winsize); self.os_api.set_terminal_size_using_fd( new_terminal.pid, right_winsize.columns as u16, right_winsize.rows as u16, ); - terminal_to_split.change_size(&left_winszie); - self.panes - .insert(PaneKind::Terminal(pid), Box::new(new_terminal)); - self.os_api.set_terminal_size_using_fd( - terminal_id_to_split, - left_winszie.columns as u16, - left_winszie.rows as u16, - ); + self.panes.insert(pid, Box::new(new_terminal)); + + if let PaneId::Terminal(active_terminal_pid) = active_pane_id { + self.os_api.set_terminal_size_using_fd( + *active_terminal_pid, + left_winsize.columns as u16, + left_winsize.rows as u16, + ); + } + + self.active_terminal = Some(pid); + self.render(); } - self.active_terminal = Some(pid); - self.render(); } } - pub fn horizontal_split(&mut self, pid: RawFd) { - self.close_down_to_max_terminals(); - if self.fullscreen_is_active { - self.toggle_active_terminal_fullscreen(); - } - if !self.has_terminal_panes() { - let x = 0; - let y = 0; - let new_terminal = TerminalPane::new(pid, self.full_screen_ws, x, y); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - new_terminal.get_columns() as u16, - new_terminal.get_rows() as u16, - ); - self.panes - .insert(PaneKind::Terminal(pid), Box::new(new_terminal)); - self.active_terminal = Some(pid); - } else { - // TODO: check minimum size of active terminal - let (active_terminal_ws, active_terminal_x, active_terminal_y) = { - let active_terminal = &self.get_active_terminal().unwrap(); - ( - PositionAndSize { - rows: active_terminal.get_rows(), - columns: active_terminal.get_columns(), - x: 0, - y: 0, - }, - active_terminal.x(), - active_terminal.y(), - ) - }; - let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&active_terminal_ws); - let bottom_half_y = active_terminal_y + top_winsize.rows + 1; - let new_terminal = - TerminalPane::new(pid, bottom_winsize, active_terminal_x, bottom_half_y); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - bottom_winsize.columns as u16, - bottom_winsize.rows as u16, - ); - - { - let active_terminal_id = &self.get_active_terminal_id().unwrap(); - let active_terminal = &mut self - .panes - .get_mut(&PaneKind::Terminal(*active_terminal_id)) - .unwrap(); - active_terminal.change_size(&top_winsize); - } - - self.panes - .insert(PaneKind::Terminal(pid), Box::new(new_terminal)); - let active_terminal_pid = self.get_active_terminal_id().unwrap(); - self.os_api.set_terminal_size_using_fd( - active_terminal_pid, - top_winsize.columns as u16, - top_winsize.rows as u16, - ); - self.active_terminal = Some(pid); - self.render(); - } - } - pub fn vertical_split(&mut self, pid: RawFd) { - self.close_down_to_max_terminals(); - if self.fullscreen_is_active { - self.toggle_active_terminal_fullscreen(); - } - if !self.has_terminal_panes() { - let x = 0; - let y = 0; - let new_terminal = TerminalPane::new(pid, self.full_screen_ws, x, y); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - new_terminal.get_columns() as u16, - new_terminal.get_rows() as u16, - ); - self.panes - .insert(PaneKind::Terminal(pid), Box::new(new_terminal)); - self.active_terminal = Some(pid); - } else { - // TODO: check minimum size of active terminal - let (active_terminal_ws, active_terminal_x, active_terminal_y) = { - let active_terminal = &self.get_active_terminal().unwrap(); - ( - PositionAndSize { - rows: active_terminal.get_rows(), - columns: active_terminal.get_columns(), - x: 0, - y: 0, - }, - active_terminal.x(), - active_terminal.y(), - ) - }; - let (left_winszie, right_winsize) = split_vertically_with_gap(&active_terminal_ws); - let right_side_x = active_terminal_x + left_winszie.columns + 1; - let new_terminal = - TerminalPane::new(pid, right_winsize, right_side_x, active_terminal_y); - self.os_api.set_terminal_size_using_fd( - new_terminal.pid, - right_winsize.columns as u16, - right_winsize.rows as u16, - ); - - { - let active_terminal_id = &self.get_active_terminal_id().unwrap(); - let active_terminal = &mut self - .panes - .get_mut(&PaneKind::Terminal(*active_terminal_id)) - .unwrap(); - active_terminal.change_size(&left_winszie); - } - - self.panes - .insert(PaneKind::Terminal(pid), Box::new(new_terminal)); - let active_terminal_pid = self.get_active_terminal_id().unwrap(); - self.os_api.set_terminal_size_using_fd( - active_terminal_pid, - left_winszie.columns as u16, - left_winszie.rows as u16, - ); - self.active_terminal = Some(pid); - self.render(); - } - } - pub fn get_active_terminal(&self) -> Option<&Box> { - match self.active_terminal { - Some(active_terminal) => self.panes.get(&PaneKind::Terminal(active_terminal)), + pub fn get_active_pane(&self) -> Option<&dyn Pane> { + // FIXME: Could use Option::map() here + match self.get_active_pane_id() { + Some(active_pane) => self.panes.get(&active_pane).map(Box::as_ref), None => None, } } - fn get_active_terminal_id(&self) -> Option { + fn get_active_pane_id(&self) -> Option { self.active_terminal } + fn get_active_terminal_id(&self) -> Option { + // FIXME: Is there a better way to do this? + if let Some(PaneId::Terminal(pid)) = self.active_terminal { + Some(pid) + } else { + None + } + } pub fn handle_pty_event(&mut self, pid: RawFd, event: VteEvent) { // if we don't have the terminal in self.terminals it's probably because // of a race condition where the terminal was created in pty_bus but has not // yet been created in Screen. These events are currently not buffered, so // if you're debugging seemingly randomly missing stdout data, this is // the reason - if let Some(terminal_output) = self.panes.get_mut(&PaneKind::Terminal(pid)) { + if let Some(terminal_output) = self.panes.get_mut(&PaneId::Terminal(pid)) { terminal_output.handle_event(event); } } pub fn write_to_active_terminal(&mut self, input_bytes: Vec) { - if let Some(active_terminal_id) = &self.get_active_terminal_id() { - let active_terminal = self.get_active_terminal().unwrap(); - let mut adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes); - self.os_api - .write_to_tty_stdin(*active_terminal_id, &mut adjusted_input) - .expect("failed to write to terminal"); - self.os_api - .tcdrain(*active_terminal_id) - .expect("failed to drain terminal"); + match self.get_active_pane_id() { + Some(PaneId::Terminal(active_terminal_id)) => { + let active_terminal = self.get_active_pane().unwrap(); + let mut adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes); + self.os_api + .write_to_tty_stdin(active_terminal_id, &mut adjusted_input) + .expect("failed to write to terminal"); + self.os_api + .tcdrain(active_terminal_id) + .expect("failed to drain terminal"); + } + Some(PaneId::Plugin(pid)) => { + self.send_plugin_instructions + .send(PluginInstruction::Input(pid, input_bytes)) + .unwrap(); + } + _ => {} } } pub fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> { // (x, y) - let active_terminal = &self.get_active_terminal().unwrap(); + let active_terminal = &self.get_active_pane()?; active_terminal .cursor_coordinates() .map(|(x_in_terminal, y_in_terminal)| { @@ -503,51 +503,47 @@ impl Tab { (x, y) }) } - pub fn toggle_active_terminal_fullscreen(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { + pub fn toggle_active_pane_fullscreen(&mut self) { + if let Some(active_pane_id) = self.get_active_pane_id() { if self - .get_active_terminal() + .get_active_pane() .unwrap() .position_and_size_override() .is_some() { for terminal_id in self.panes_to_hide.iter() { self.panes - .get_mut(&PaneKind::Terminal(*terminal_id)) + .get_mut(terminal_id) .unwrap() .set_should_render(true); } self.panes_to_hide.clear(); - let active_terminal = self - .panes - .get_mut(&PaneKind::Terminal(active_terminal_id)) - .unwrap(); + let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); active_terminal.reset_size_and_position_override(); } else { - let terminals = self.get_terminals(); - let all_ids_except_current = terminals.filter_map(|(id, _)| { - if id != active_terminal_id { - Some(id) - } else { - None - } - }); + let terminals = self.get_panes(); + let all_ids_except_current = + terminals.filter_map( + |(&id, _)| { + if id != active_pane_id { + Some(id) + } else { + None + } + }, + ); self.panes_to_hide = all_ids_except_current.collect(); - let active_terminal = self - .panes - .get_mut(&PaneKind::Terminal(active_terminal_id)) - .unwrap(); + let active_terminal = self.panes.get_mut(&active_pane_id).unwrap(); active_terminal.override_size_and_position(0, 0, &self.full_screen_ws); } - let active_terminal = self - .panes - .get(&PaneKind::Terminal(active_terminal_id)) - .unwrap(); - self.os_api.set_terminal_size_using_fd( - active_terminal_id, - active_terminal.get_columns() as u16, - active_terminal.get_rows() as u16, - ); + let active_terminal = self.panes.get(&active_pane_id).unwrap(); + if let PaneId::Terminal(active_pid) = active_pane_id { + self.os_api.set_terminal_size_using_fd( + active_pid, + active_terminal.columns() as u16, + active_terminal.rows() as u16, + ); + } self.render(); self.toggle_fullscreen_is_active(); } @@ -566,20 +562,24 @@ impl Tab { self.full_screen_ws.columns as u16, self.full_screen_ws.rows as u16, ); - for (pane_kind, terminal) in self.panes.iter_mut() { - match pane_kind { - PaneKind::Terminal(pid) => { - if !self.panes_to_hide.contains(pid) { - boundaries.add_rect(&terminal); - if let Some(vte_output) = terminal.buffer_as_vte_output() { - stdout - .write_all(&vte_output.as_bytes()) - .expect("cannot write to stdout"); - } - } - } - PaneKind::BuiltInPane(builtin_id) => { - // TBD + for (kind, terminal) in self.panes.iter_mut() { + if !self.panes_to_hide.contains(&terminal.pid()) { + boundaries.add_rect(terminal.as_ref()); + if let Some(vte_output) = terminal.render() { + let vte_output = if let PaneId::Terminal(_) = kind { + vte_output + } else { + pad_to_size(&vte_output, terminal.rows(), terminal.columns()) + }; + // FIXME: Use Termion for cursor and style clearing? + write!( + stdout, + "\u{1b}[{};{}H\u{1b}[m{}", + terminal.y() + 1, + terminal.x() + 1, + vte_output + ) + .expect("cannot write to stdout"); } } } @@ -615,26 +615,21 @@ impl Tab { } } } - fn get_terminals(&self) -> impl Iterator)> { - self.panes - .iter() - .filter_map(|(pane_kind, terminal_pane)| match pane_kind { - PaneKind::Terminal(pid) => Some((*pid, terminal_pane)), - _ => None, - }) + fn get_panes(&self) -> impl Iterator)> { + self.panes.iter() } - fn has_terminal_panes(&self) -> bool { - let mut all_terminals = self.get_terminals(); + fn has_panes(&self) -> bool { + let mut all_terminals = self.get_panes(); all_terminals.next().is_some() } - fn terminal_ids_directly_left_of(&self, id: &RawFd) -> Option> { + fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option> { let mut ids = vec![]; - let terminal_to_check = self.panes.get(&PaneKind::Terminal(*id)).unwrap(); + let terminal_to_check = self.panes.get(id).unwrap(); if terminal_to_check.x() == 0 { return None; } - for (pid, terminal) in self.get_terminals() { - if terminal.x() + terminal.get_columns() == terminal_to_check.x() - 1 { + for (&pid, terminal) in self.get_panes() { + if terminal.x() + terminal.columns() == terminal_to_check.x() - 1 { ids.push(pid); } } @@ -644,11 +639,11 @@ impl Tab { Some(ids) } } - fn terminal_ids_directly_right_of(&self, id: &RawFd) -> Option> { + fn pane_ids_directly_right_of(&self, id: &PaneId) -> Option> { let mut ids = vec![]; - let terminal_to_check = self.panes.get(&PaneKind::Terminal(*id)).unwrap(); - for (pid, terminal) in self.get_terminals() { - if terminal.x() == terminal_to_check.x() + terminal_to_check.get_columns() + 1 { + let terminal_to_check = self.panes.get(id).unwrap(); + for (&pid, terminal) in self.get_panes() { + if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 { ids.push(pid); } } @@ -658,11 +653,11 @@ impl Tab { Some(ids) } } - fn terminal_ids_directly_below(&self, id: &RawFd) -> Option> { + fn pane_ids_directly_below(&self, id: &PaneId) -> Option> { let mut ids = vec![]; - let terminal_to_check = self.panes.get(&PaneKind::Terminal(*id)).unwrap(); - for (pid, terminal) in self.get_terminals() { - if terminal.y() == terminal_to_check.y() + terminal_to_check.get_rows() + 1 { + let terminal_to_check = self.panes.get(id).unwrap(); + for (&pid, terminal) in self.get_panes() { + if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 { ids.push(pid); } } @@ -672,11 +667,11 @@ impl Tab { Some(ids) } } - fn terminal_ids_directly_above(&self, id: &RawFd) -> Option> { + fn pane_ids_directly_above(&self, id: &PaneId) -> Option> { let mut ids = vec![]; - let terminal_to_check = self.panes.get(&PaneKind::Terminal(*id)).unwrap(); - for (pid, terminal) in self.get_terminals() { - if terminal.y() + terminal.get_rows() + 1 == terminal_to_check.y() { + let terminal_to_check = self.panes.get(id).unwrap(); + for (&pid, terminal) in self.get_panes() { + if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() { ids.push(pid); } } @@ -686,63 +681,64 @@ impl Tab { Some(ids) } } - fn panes_top_aligned_with_pane(&self, pane: &Box) -> Vec<&Box> { + fn panes_top_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { self.panes .keys() - .map(|t_id| self.panes.get(&t_id).unwrap()) + .map(|t_id| self.panes.get(&t_id).unwrap().as_ref()) .filter(|terminal| terminal.pid() != pane.pid() && terminal.y() == pane.y()) .collect() } - fn panes_bottom_aligned_with_pane(&self, pane: &Box) -> Vec<&Box> { + fn panes_bottom_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { self.panes .keys() - .map(|t_id| self.panes.get(&t_id).unwrap()) + .map(|t_id| self.panes.get(&t_id).unwrap().as_ref()) .filter(|terminal| { terminal.pid() != pane.pid() - && terminal.y() + terminal.get_rows() == pane.y() + pane.get_rows() + && terminal.y() + terminal.rows() == pane.y() + pane.rows() }) .collect() } - fn panes_right_aligned_with_pane(&self, pane: &Box) -> Vec<&Box> { + fn panes_right_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { self.panes .keys() - .map(|t_id| self.panes.get(&t_id).unwrap()) + .map(|t_id| self.panes.get(&t_id).unwrap().as_ref()) .filter(|terminal| { terminal.pid() != pane.pid() - && terminal.x() + terminal.get_columns() == pane.x() + pane.get_columns() + && terminal.x() + terminal.columns() == pane.x() + pane.columns() }) .collect() } - fn panes_left_aligned_with_pane(&self, pane: &&Box) -> Vec<&Box> { + fn panes_left_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { self.panes .keys() - .map(|t_id| self.panes.get(&t_id).unwrap()) + .map(|t_id| self.panes.get(&t_id).unwrap().as_ref()) .filter(|terminal| terminal.pid() != pane.pid() && terminal.x() == pane.x()) .collect() } fn right_aligned_contiguous_panes_above( &self, - id: &RawFd, + id: &PaneId, terminal_borders_to_the_right: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; let terminal_to_check = self .panes - .get(&PaneKind::Terminal(*id)) - .expect("terminal id does not exist"); - let mut right_aligned_terminals = self.panes_right_aligned_with_pane(&terminal_to_check); + .get(id) + .expect("terminal id does not exist") + .as_ref(); + let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check); // terminals that are next to each other up to current - right_aligned_terminals.sort_by(|a, b| b.y().cmp(&a.y())); + right_aligned_terminals.sort_by_key(|a| Reverse(a.y())); for terminal in right_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() + terminal.get_rows() + 1 == terminal_to_check.y() { + if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() { terminals.push(terminal); } } // top-most border aligned with a pane border to the right let mut top_resize_border = 0; for terminal in &terminals { - let bottom_terminal_boundary = terminal.y() + terminal.get_rows(); + let bottom_terminal_boundary = terminal.y() + terminal.rows(); if terminal_borders_to_the_right .get(&(bottom_terminal_boundary + 1)) .is_some() @@ -759,25 +755,26 @@ impl Tab { } else { top_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (top_resize_border, terminal_ids) } fn right_aligned_contiguous_panes_below( &self, - id: &RawFd, + id: &PaneId, terminal_borders_to_the_right: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; let terminal_to_check = self .panes - .get(&PaneKind::Terminal(*id)) - .expect("terminal id does not exist"); - let mut right_aligned_terminals = self.panes_right_aligned_with_pane(&terminal_to_check); + .get(id) + .expect("terminal id does not exist") + .as_ref(); + let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check); // terminals that are next to each other up to current - right_aligned_terminals.sort_by(|a, b| a.y().cmp(&b.y())); + right_aligned_terminals.sort_by_key(|a| a.y()); for terminal in right_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() == terminal_to_check.y() + terminal_to_check.get_rows() + 1 { + if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 { terminals.push(terminal); } } @@ -793,40 +790,41 @@ impl Tab { bottom_resize_border = top_terminal_boundary; } } - terminals.retain(|terminal| terminal.y() + terminal.get_rows() <= bottom_resize_border); + terminals.retain(|terminal| terminal.y() + terminal.rows() <= bottom_resize_border); // if there are no adjacent panes to resize, we use the border of the main pane we're // resizing let bottom_resize_border = if terminals.is_empty() { - terminal_to_check.y() + terminal_to_check.get_rows() + terminal_to_check.y() + terminal_to_check.rows() } else { bottom_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (bottom_resize_border, terminal_ids) } fn left_aligned_contiguous_panes_above( &self, - id: &RawFd, + id: &PaneId, terminal_borders_to_the_left: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; let terminal_to_check = self .panes - .get(&PaneKind::Terminal(*id)) - .expect("terminal id does not exist"); - let mut left_aligned_terminals = self.panes_left_aligned_with_pane(&terminal_to_check); + .get(id) + .expect("terminal id does not exist") + .as_ref(); + let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check); // terminals that are next to each other up to current - left_aligned_terminals.sort_by(|a, b| b.y().cmp(&a.y())); + left_aligned_terminals.sort_by_key(|a| Reverse(a.y())); for terminal in left_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() + terminal.get_rows() + 1 == terminal_to_check.y() { + if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() { terminals.push(terminal); } } // top-most border aligned with a pane border to the right let mut top_resize_border = 0; for terminal in &terminals { - let bottom_terminal_boundary = terminal.y() + terminal.get_rows(); + let bottom_terminal_boundary = terminal.y() + terminal.rows(); if terminal_borders_to_the_left .get(&(bottom_terminal_boundary + 1)) .is_some() @@ -843,25 +841,26 @@ impl Tab { } else { top_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (top_resize_border, terminal_ids) } fn left_aligned_contiguous_panes_below( &self, - id: &RawFd, + id: &PaneId, terminal_borders_to_the_left: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; let terminal_to_check = self .panes - .get(&PaneKind::Terminal(*id)) - .expect("terminal id does not exist"); - let mut left_aligned_terminals = self.panes_left_aligned_with_pane(&terminal_to_check); + .get(id) + .expect("terminal id does not exist") + .as_ref(); + let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check); // terminals that are next to each other up to current - left_aligned_terminals.sort_by(|a, b| a.y().cmp(&b.y())); + left_aligned_terminals.sort_by_key(|a| a.y()); for terminal in left_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() == terminal_to_check.y() + terminal_to_check.get_rows() + 1 { + if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 { terminals.push(terminal); } } @@ -878,42 +877,43 @@ impl Tab { } } terminals.retain(|terminal| { - // terminal.y() + terminal.get_rows() < bottom_resize_border - terminal.y() + terminal.get_rows() <= bottom_resize_border + // terminal.y() + terminal.rows() < bottom_resize_border + terminal.y() + terminal.rows() <= bottom_resize_border }); // if there are no adjacent panes to resize, we use the border of the main pane we're // resizing let bottom_resize_border = if terminals.is_empty() { - terminal_to_check.y() + terminal_to_check.get_rows() + terminal_to_check.y() + terminal_to_check.rows() } else { bottom_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (bottom_resize_border, terminal_ids) } fn top_aligned_contiguous_panes_to_the_left( &self, - id: &RawFd, + id: &PaneId, terminal_borders_above: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; let terminal_to_check = self .panes - .get(&PaneKind::Terminal(*id)) - .expect("terminal id does not exist"); - let mut top_aligned_terminals = self.panes_top_aligned_with_pane(&terminal_to_check); + .get(id) + .expect("terminal id does not exist") + .as_ref(); + let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check); // terminals that are next to each other up to current - top_aligned_terminals.sort_by(|a, b| b.x().cmp(&a.x())); + top_aligned_terminals.sort_by_key(|a| Reverse(a.x())); for terminal in top_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() + terminal.get_columns() + 1 == terminal_to_check.x() { + if terminal.x() + terminal.columns() + 1 == terminal_to_check.x() { terminals.push(terminal); } } // leftmost border aligned with a pane border above let mut left_resize_border = 0; for terminal in &terminals { - let right_terminal_boundary = terminal.x() + terminal.get_columns(); + let right_terminal_boundary = terminal.x() + terminal.columns(); if terminal_borders_above .get(&(right_terminal_boundary + 1)) .is_some() @@ -930,22 +930,22 @@ impl Tab { } else { left_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (left_resize_border, terminal_ids) } fn top_aligned_contiguous_panes_to_the_right( &self, - id: &RawFd, + id: &PaneId, terminal_borders_above: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; - let terminal_to_check = self.panes.get(&PaneKind::Terminal(*id)).unwrap(); - let mut top_aligned_terminals = self.panes_top_aligned_with_pane(&terminal_to_check); + let terminal_to_check = self.panes.get(id).unwrap().as_ref(); + let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check); // terminals that are next to each other up to current - top_aligned_terminals.sort_by(|a, b| a.x().cmp(&b.x())); + top_aligned_terminals.sort_by_key(|a| a.x()); for terminal in top_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() == terminal_to_check.x() + terminal_to_check.get_columns() + 1 { + if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 { terminals.push(terminal); } } @@ -961,37 +961,37 @@ impl Tab { right_resize_border = left_terminal_boundary; } } - terminals.retain(|terminal| terminal.x() + terminal.get_columns() <= right_resize_border); + terminals.retain(|terminal| terminal.x() + terminal.columns() <= right_resize_border); // if there are no adjacent panes to resize, we use the border of the main pane we're // resizing let right_resize_border = if terminals.is_empty() { - terminal_to_check.x() + terminal_to_check.get_columns() + terminal_to_check.x() + terminal_to_check.columns() } else { right_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (right_resize_border, terminal_ids) } fn bottom_aligned_contiguous_panes_to_the_left( &self, - id: &RawFd, + id: &PaneId, terminal_borders_below: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; - let terminal_to_check = self.panes.get(&PaneKind::Terminal(*id)).unwrap(); - let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(&terminal_to_check); - bottom_aligned_terminals.sort_by(|a, b| b.x().cmp(&a.x())); + let terminal_to_check = self.panes.get(id).unwrap().as_ref(); + let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check); + bottom_aligned_terminals.sort_by_key(|a| Reverse(a.x())); // terminals that are next to each other up to current for terminal in bottom_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() + terminal.get_columns() + 1 == terminal_to_check.x() { + if terminal.x() + terminal.columns() + 1 == terminal_to_check.x() { terminals.push(terminal); } } // leftmost border aligned with a pane border above let mut left_resize_border = 0; for terminal in &terminals { - let right_terminal_boundary = terminal.x() + terminal.get_columns(); + let right_terminal_boundary = terminal.x() + terminal.columns(); if terminal_borders_below .get(&(right_terminal_boundary + 1)) .is_some() @@ -1008,22 +1008,22 @@ impl Tab { } else { left_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (left_resize_border, terminal_ids) } fn bottom_aligned_contiguous_panes_to_the_right( &self, - id: &RawFd, + id: &PaneId, terminal_borders_below: &HashSet, ) -> BorderAndPaneIds { let mut terminals = vec![]; - let terminal_to_check = self.panes.get(&PaneKind::Terminal(*id)).unwrap(); - let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(&terminal_to_check); - bottom_aligned_terminals.sort_by(|a, b| a.x().cmp(&b.x())); + let terminal_to_check = self.panes.get(id).unwrap().as_ref(); + let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check); + bottom_aligned_terminals.sort_by_key(|a| a.x()); // terminals that are next to each other up to current for terminal in bottom_aligned_terminals { let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() == terminal_to_check.x() + terminal_to_check.get_columns() + 1 { + if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 { terminals.push(terminal); } } @@ -1039,118 +1039,134 @@ impl Tab { right_resize_border = left_terminal_boundary; } } - terminals.retain(|terminal| terminal.x() + terminal.get_columns() <= right_resize_border); + terminals.retain(|terminal| terminal.x() + terminal.columns() <= right_resize_border); let right_resize_border = if terminals.is_empty() { - terminal_to_check.x() + terminal_to_check.get_columns() + terminal_to_check.x() + terminal_to_check.columns() } else { right_resize_border }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); + let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); (right_resize_border, terminal_ids) } - fn reduce_pane_height_down(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn reduce_pane_height_down(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.reduce_height_down(count); - self.os_api.set_terminal_size_using_fd( - *id, - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = id { + self.os_api.set_terminal_size_using_fd( + *pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } - fn reduce_pane_height_up(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.reduce_height_up(count); - self.os_api.set_terminal_size_using_fd( - *id, - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = id { + self.os_api.set_terminal_size_using_fd( + *pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } - fn increase_pane_height_down(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.increase_height_down(count); - self.os_api.set_terminal_size_using_fd( - terminal.pid(), - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = terminal.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } - fn increase_pane_height_up(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn increase_pane_height_up(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.increase_height_up(count); - self.os_api.set_terminal_size_using_fd( - terminal.pid(), - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = terminal.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } - fn increase_pane_width_right(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.increase_width_right(count); - self.os_api.set_terminal_size_using_fd( - terminal.pid(), - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = terminal.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } - fn increase_pane_width_left(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn increase_pane_width_left(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.increase_width_left(count); - self.os_api.set_terminal_size_using_fd( - terminal.pid(), - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = terminal.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } - fn reduce_pane_width_right(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn reduce_pane_width_right(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.reduce_width_right(count); - self.os_api.set_terminal_size_using_fd( - terminal.pid(), - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = terminal.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } - fn reduce_pane_width_left(&mut self, id: &RawFd, count: usize) { - let terminal = self.panes.get_mut(&PaneKind::Terminal(*id)).unwrap(); + fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) { + let terminal = self.panes.get_mut(id).unwrap(); terminal.reduce_width_left(count); - self.os_api.set_terminal_size_using_fd( - terminal.pid(), - terminal.get_columns() as u16, - terminal.get_rows() as u16, - ); + if let PaneId::Terminal(pid) = terminal.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + terminal.columns() as u16, + terminal.rows() as u16, + ); + } } fn pane_is_between_vertical_borders( &self, - id: &RawFd, + id: &PaneId, left_border_x: usize, right_border_x: usize, ) -> bool { let terminal = self .panes - .get(&PaneKind::Terminal(*id)) + .get(id) .expect("could not find terminal to check between borders"); - terminal.x() >= left_border_x && terminal.x() + terminal.get_columns() <= right_border_x + terminal.x() >= left_border_x && terminal.x() + terminal.columns() <= right_border_x } fn pane_is_between_horizontal_borders( &self, - id: &RawFd, + id: &PaneId, top_border_y: usize, bottom_border_y: usize, ) -> bool { let terminal = self .panes - .get(&PaneKind::Terminal(*id)) + .get(id) .expect("could not find terminal to check between borders"); - terminal.y() >= top_border_y && terminal.y() + terminal.get_rows() <= bottom_border_y + terminal.y() >= top_border_y && terminal.y() + terminal.rows() <= bottom_border_y } - fn reduce_pane_and_surroundings_up(&mut self, id: &RawFd, count: usize) { + fn reduce_pane_and_surroundings_up(&mut self, id: &PaneId, count: usize) { let mut terminals_below = self - .terminal_ids_directly_below(&id) + .pane_ids_directly_below(&id) .expect("can't reduce pane size up if there are no terminals below"); let terminal_borders_below: HashSet = terminals_below .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().x()) + .map(|t| self.panes.get(t).unwrap().x()) .collect(); let (left_resize_border, terminals_to_the_left) = self.bottom_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_below); @@ -1170,13 +1186,13 @@ impl Tab { self.reduce_pane_height_up(&terminal_id, count); } } - fn reduce_pane_and_surroundings_down(&mut self, id: &RawFd, count: usize) { + fn reduce_pane_and_surroundings_down(&mut self, id: &PaneId, count: usize) { let mut terminals_above = self - .terminal_ids_directly_above(&id) + .pane_ids_directly_above(&id) .expect("can't reduce pane size down if there are no terminals above"); let terminal_borders_above: HashSet = terminals_above .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().x()) + .map(|t| self.panes.get(t).unwrap().x()) .collect(); let (left_resize_border, terminals_to_the_left) = self.top_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_above); @@ -1196,13 +1212,13 @@ impl Tab { self.reduce_pane_height_down(&terminal_id, count); } } - fn reduce_pane_and_surroundings_right(&mut self, id: &RawFd, count: usize) { + fn reduce_pane_and_surroundings_right(&mut self, id: &PaneId, count: usize) { let mut terminals_to_the_left = self - .terminal_ids_directly_left_of(&id) + .pane_ids_directly_left_of(&id) .expect("can't reduce pane size right if there are no terminals to the left"); let terminal_borders_to_the_left: HashSet = terminals_to_the_left .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().y()) + .map(|t| self.panes.get(t).unwrap().y()) .collect(); let (top_resize_border, terminals_above) = self.left_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_left); @@ -1219,13 +1235,13 @@ impl Tab { self.reduce_pane_width_right(&terminal_id, count); } } - fn reduce_pane_and_surroundings_left(&mut self, id: &RawFd, count: usize) { + fn reduce_pane_and_surroundings_left(&mut self, id: &PaneId, count: usize) { let mut terminals_to_the_right = self - .terminal_ids_directly_right_of(&id) + .pane_ids_directly_right_of(&id) .expect("can't reduce pane size left if there are no terminals to the right"); let terminal_borders_to_the_right: HashSet = terminals_to_the_right .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().y()) + .map(|t| self.panes.get(t).unwrap().y()) .collect(); let (top_resize_border, terminals_above) = self.right_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_right); @@ -1242,13 +1258,13 @@ impl Tab { self.reduce_pane_width_left(&terminal_id, count); } } - fn increase_pane_and_surroundings_up(&mut self, id: &RawFd, count: usize) { + fn increase_pane_and_surroundings_up(&mut self, id: &PaneId, count: usize) { let mut terminals_above = self - .terminal_ids_directly_above(&id) + .pane_ids_directly_above(&id) .expect("can't increase pane size up if there are no terminals above"); let terminal_borders_above: HashSet = terminals_above .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().x()) + .map(|t| self.panes.get(t).unwrap().x()) .collect(); let (left_resize_border, terminals_to_the_left) = self.top_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_above); @@ -1268,13 +1284,13 @@ impl Tab { self.increase_pane_height_up(&terminal_id, count); } } - fn increase_pane_and_surroundings_down(&mut self, id: &RawFd, count: usize) { + fn increase_pane_and_surroundings_down(&mut self, id: &PaneId, count: usize) { let mut terminals_below = self - .terminal_ids_directly_below(&id) + .pane_ids_directly_below(&id) .expect("can't increase pane size down if there are no terminals below"); let terminal_borders_below: HashSet = terminals_below .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().x()) + .map(|t| self.panes.get(t).unwrap().x()) .collect(); let (left_resize_border, terminals_to_the_left) = self.bottom_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_below); @@ -1294,13 +1310,13 @@ impl Tab { self.increase_pane_height_down(&terminal_id, count); } } - fn increase_pane_and_surroundings_right(&mut self, id: &RawFd, count: usize) { + fn increase_pane_and_surroundings_right(&mut self, id: &PaneId, count: usize) { let mut terminals_to_the_right = self - .terminal_ids_directly_right_of(&id) + .pane_ids_directly_right_of(&id) .expect("can't increase pane size right if there are no terminals to the right"); let terminal_borders_to_the_right: HashSet = terminals_to_the_right .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().y()) + .map(|t| self.panes.get(t).unwrap().y()) .collect(); let (top_resize_border, terminals_above) = self.right_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_right); @@ -1317,13 +1333,13 @@ impl Tab { self.increase_pane_width_right(&terminal_id, count); } } - fn increase_pane_and_surroundings_left(&mut self, id: &RawFd, count: usize) { + fn increase_pane_and_surroundings_left(&mut self, id: &PaneId, count: usize) { let mut terminals_to_the_left = self - .terminal_ids_directly_left_of(&id) + .pane_ids_directly_left_of(&id) .expect("can't increase pane size right if there are no terminals to the right"); let terminal_borders_to_the_left: HashSet = terminals_to_the_left .iter() - .map(|t| self.panes.get(&PaneKind::Terminal(*t)).unwrap().y()) + .map(|t| self.panes.get(t).unwrap().y()) .collect(); let (top_resize_border, terminals_above) = self.left_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_left); @@ -1340,42 +1356,30 @@ impl Tab { self.increase_pane_width_left(&terminal_id, count); } } - fn panes_exist_above(&self, pane_id: &RawFd) -> bool { - let pane = self - .panes - .get(&PaneKind::Terminal(*pane_id)) - .expect("pane does not exist"); + fn panes_exist_above(&self, pane_id: &PaneId) -> bool { + let pane = self.panes.get(pane_id).expect("pane does not exist"); pane.y() > 0 } - fn panes_exist_below(&self, pane_id: &RawFd) -> bool { - let pane = self - .panes - .get(&PaneKind::Terminal(*pane_id)) - .expect("pane does not exist"); - pane.y() + pane.get_rows() < self.full_screen_ws.rows + fn panes_exist_below(&self, pane_id: &PaneId) -> bool { + let pane = self.panes.get(pane_id).expect("pane does not exist"); + pane.y() + pane.rows() < self.full_screen_ws.rows } - fn panes_exist_to_the_right(&self, pane_id: &RawFd) -> bool { - let pane = self - .panes - .get(&PaneKind::Terminal(*pane_id)) - .expect("pane does not exist"); - pane.x() + pane.get_columns() < self.full_screen_ws.columns + fn panes_exist_to_the_right(&self, pane_id: &PaneId) -> bool { + let pane = self.panes.get(pane_id).expect("pane does not exist"); + pane.x() + pane.columns() < self.full_screen_ws.columns } - fn panes_exist_to_the_left(&self, pane_id: &RawFd) -> bool { - let pane = self - .panes - .get(&PaneKind::Terminal(*pane_id)) - .expect("pane does not exist"); + fn panes_exist_to_the_left(&self, pane_id: &PaneId) -> bool { + let pane = self.panes.get(pane_id).expect("pane does not exist"); pane.x() > 0 } pub fn resize_right(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much let count = 10; - if let Some(active_terminal_id) = self.get_active_terminal_id() { - if self.panes_exist_to_the_right(&active_terminal_id) { - self.increase_pane_and_surroundings_right(&active_terminal_id, count); - } else if self.panes_exist_to_the_left(&active_terminal_id) { - self.reduce_pane_and_surroundings_right(&active_terminal_id, count); + if let Some(active_pane_id) = self.get_active_pane_id() { + if self.panes_exist_to_the_right(&active_pane_id) { + self.increase_pane_and_surroundings_right(&active_pane_id, count); + } else if self.panes_exist_to_the_left(&active_pane_id) { + self.reduce_pane_and_surroundings_right(&active_pane_id, count); } self.render(); } @@ -1383,11 +1387,11 @@ impl Tab { pub fn resize_left(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much let count = 10; - if let Some(active_terminal_id) = self.get_active_terminal_id() { - if self.panes_exist_to_the_right(&active_terminal_id) { - self.reduce_pane_and_surroundings_left(&active_terminal_id, count); - } else if self.panes_exist_to_the_left(&active_terminal_id) { - self.increase_pane_and_surroundings_left(&active_terminal_id, count); + if let Some(active_pane_id) = self.get_active_pane_id() { + if self.panes_exist_to_the_right(&active_pane_id) { + self.reduce_pane_and_surroundings_left(&active_pane_id, count); + } else if self.panes_exist_to_the_left(&active_pane_id) { + self.increase_pane_and_surroundings_left(&active_pane_id, count); } self.render(); } @@ -1395,11 +1399,11 @@ impl Tab { pub fn resize_down(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much let count = 2; - if let Some(active_terminal_id) = self.get_active_terminal_id() { - if self.panes_exist_above(&active_terminal_id) { - self.reduce_pane_and_surroundings_down(&active_terminal_id, count); - } else if self.panes_exist_below(&active_terminal_id) { - self.increase_pane_and_surroundings_down(&active_terminal_id, count); + if let Some(active_pane_id) = self.get_active_pane_id() { + if self.panes_exist_above(&active_pane_id) { + self.reduce_pane_and_surroundings_down(&active_pane_id, count); + } else if self.panes_exist_below(&active_pane_id) { + self.increase_pane_and_surroundings_down(&active_pane_id, count); } self.render(); } @@ -1407,27 +1411,24 @@ impl Tab { pub fn resize_up(&mut self) { // TODO: find out by how much we actually reduced and only reduce by that much let count = 2; - if let Some(active_terminal_id) = self.get_active_terminal_id() { - if self.panes_exist_above(&active_terminal_id) { - self.increase_pane_and_surroundings_up(&active_terminal_id, count); - } else if self.panes_exist_below(&active_terminal_id) { - self.reduce_pane_and_surroundings_up(&active_terminal_id, count); + if let Some(active_pane_id) = self.get_active_pane_id() { + if self.panes_exist_above(&active_pane_id) { + self.increase_pane_and_surroundings_up(&active_pane_id, count); + } else if self.panes_exist_below(&active_pane_id) { + self.reduce_pane_and_surroundings_up(&active_pane_id, count); } self.render(); } } pub fn move_focus(&mut self) { - if !self.has_terminal_panes() { + if !self.has_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal_id = self.get_active_terminal_id().unwrap(); - let terminal_ids: Vec = self - .get_terminals() - .filter_map(|(pid, _)| Some(pid)) - .collect(); // TODO: better, no allocations + let active_terminal_id = self.get_active_pane_id().unwrap(); + let terminal_ids: Vec = self.get_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations let first_terminal = terminal_ids.get(0).unwrap(); let active_terminal_id_position = terminal_ids .iter() @@ -1441,24 +1442,24 @@ impl Tab { self.render(); } pub fn move_focus_left(&mut self) { - if !self.has_terminal_panes() { + if !self.has_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal = self.get_active_terminal(); + let active_terminal = self.get_active_pane(); if let Some(active) = active_terminal { - let terminals = self.get_terminals(); + let terminals = self.get_panes(); let next_index = terminals .enumerate() .filter(|(_, (_, c))| { - c.is_directly_left_of(&active) && c.horizontally_overlaps_with(&active) + c.is_directly_left_of(active) && c.horizontally_overlaps_with(active) }) - .max_by_key(|(_, (_, c))| c.get_horizontal_overlap_with(&active)) + .max_by_key(|(_, (_, c))| c.get_horizontal_overlap_with(active)) .map(|(_, (pid, _))| pid); match next_index { - Some(p) => { + Some(&p) => { self.active_terminal = Some(p); } None => { @@ -1471,24 +1472,24 @@ impl Tab { self.render(); } pub fn move_focus_down(&mut self) { - if !self.has_terminal_panes() { + if !self.has_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal = self.get_active_terminal(); + let active_terminal = self.get_active_pane(); if let Some(active) = active_terminal { - let terminals = self.get_terminals(); + let terminals = self.get_panes(); let next_index = terminals .enumerate() .filter(|(_, (_, c))| { - c.is_directly_below(&active) && c.vertically_overlaps_with(&active) + c.is_directly_below(active) && c.vertically_overlaps_with(active) }) - .max_by_key(|(_, (_, c))| c.get_vertical_overlap_with(&active)) + .max_by_key(|(_, (_, c))| c.get_vertical_overlap_with(active)) .map(|(_, (pid, _))| pid); match next_index { - Some(p) => { + Some(&p) => { self.active_terminal = Some(p); } None => { @@ -1501,24 +1502,24 @@ impl Tab { self.render(); } pub fn move_focus_up(&mut self) { - if !self.has_terminal_panes() { + if !self.has_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal = self.get_active_terminal(); + let active_terminal = self.get_active_pane(); if let Some(active) = active_terminal { - let terminals = self.get_terminals(); + let terminals = self.get_panes(); let next_index = terminals .enumerate() .filter(|(_, (_, c))| { - c.is_directly_above(&active) && c.vertically_overlaps_with(&active) + c.is_directly_above(active) && c.vertically_overlaps_with(active) }) - .max_by_key(|(_, (_, c))| c.get_vertical_overlap_with(&active)) + .max_by_key(|(_, (_, c))| c.get_vertical_overlap_with(active)) .map(|(_, (pid, _))| pid); match next_index { - Some(p) => { + Some(&p) => { self.active_terminal = Some(p); } None => { @@ -1531,24 +1532,24 @@ impl Tab { self.render(); } pub fn move_focus_right(&mut self) { - if !self.has_terminal_panes() { + if !self.has_panes() { return; } if self.fullscreen_is_active { return; } - let active_terminal = self.get_active_terminal(); + let active_terminal = self.get_active_pane(); if let Some(active) = active_terminal { - let terminals = self.get_terminals(); + let terminals = self.get_panes(); let next_index = terminals .enumerate() .filter(|(_, (_, c))| { - c.is_directly_right_of(&active) && c.horizontally_overlaps_with(&active) + c.is_directly_right_of(active) && c.horizontally_overlaps_with(active) }) - .max_by_key(|(_, (_, c))| c.get_horizontal_overlap_with(&active)) + .max_by_key(|(_, (_, c))| c.get_horizontal_overlap_with(active)) .map(|(_, (pid, _))| pid); match next_index { - Some(p) => { + Some(&p) => { self.active_terminal = Some(p); } None => { @@ -1560,28 +1561,28 @@ impl Tab { } self.render(); } - fn horizontal_borders(&self, terminals: &[RawFd]) -> HashSet { + fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet { terminals.iter().fold(HashSet::new(), |mut borders, t| { - let terminal = self.panes.get(&PaneKind::Terminal(*t)).unwrap(); + let terminal = self.panes.get(t).unwrap(); borders.insert(terminal.y()); - borders.insert(terminal.y() + terminal.get_rows() + 1); // 1 for the border width + borders.insert(terminal.y() + terminal.rows() + 1); // 1 for the border width borders }) } - fn vertical_borders(&self, terminals: &[RawFd]) -> HashSet { + fn vertical_borders(&self, terminals: &[PaneId]) -> HashSet { terminals.iter().fold(HashSet::new(), |mut borders, t| { - let terminal = self.panes.get(&PaneKind::Terminal(*t)).unwrap(); + let terminal = self.panes.get(t).unwrap(); borders.insert(terminal.x()); - borders.insert(terminal.x() + terminal.get_columns() + 1); // 1 for the border width + borders.insert(terminal.x() + terminal.columns() + 1); // 1 for the border width borders }) } - fn terminals_to_the_left_between_aligning_borders(&self, id: RawFd) -> Option> { - if let Some(terminal) = &self.panes.get(&PaneKind::Terminal(id)) { + fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option> { + if let Some(terminal) = &self.panes.get(&id) { let upper_close_border = terminal.y(); - let lower_close_border = terminal.y() + terminal.get_rows() + 1; + let lower_close_border = terminal.y() + terminal.rows() + 1; - if let Some(mut terminals_to_the_left) = self.terminal_ids_directly_left_of(&id) { + if let Some(mut terminals_to_the_left) = self.pane_ids_directly_left_of(&id) { let terminal_borders_to_the_left = self.horizontal_borders(&terminals_to_the_left); if terminal_borders_to_the_left.contains(&upper_close_border) && terminal_borders_to_the_left.contains(&lower_close_border) @@ -1599,12 +1600,12 @@ impl Tab { } None } - fn terminals_to_the_right_between_aligning_borders(&self, id: RawFd) -> Option> { - if let Some(terminal) = &self.panes.get(&PaneKind::Terminal(id)) { + fn panes_to_the_right_between_aligning_borders(&self, id: PaneId) -> Option> { + if let Some(terminal) = &self.panes.get(&id) { let upper_close_border = terminal.y(); - let lower_close_border = terminal.y() + terminal.get_rows() + 1; + let lower_close_border = terminal.y() + terminal.rows() + 1; - if let Some(mut terminals_to_the_right) = self.terminal_ids_directly_right_of(&id) { + if let Some(mut terminals_to_the_right) = self.pane_ids_directly_right_of(&id) { let terminal_borders_to_the_right = self.horizontal_borders(&terminals_to_the_right); if terminal_borders_to_the_right.contains(&upper_close_border) @@ -1623,12 +1624,12 @@ impl Tab { } None } - fn terminals_above_between_aligning_borders(&self, id: RawFd) -> Option> { - if let Some(terminal) = &self.panes.get(&PaneKind::Terminal(id)) { + fn panes_above_between_aligning_borders(&self, id: PaneId) -> Option> { + if let Some(terminal) = &self.panes.get(&id) { let left_close_border = terminal.x(); - let right_close_border = terminal.x() + terminal.get_columns() + 1; + let right_close_border = terminal.x() + terminal.columns() + 1; - if let Some(mut terminals_above) = self.terminal_ids_directly_above(&id) { + if let Some(mut terminals_above) = self.pane_ids_directly_above(&id) { let terminal_borders_above = self.vertical_borders(&terminals_above); if terminal_borders_above.contains(&left_close_border) && terminal_borders_above.contains(&right_close_border) @@ -1646,12 +1647,12 @@ impl Tab { } None } - fn terminals_below_between_aligning_borders(&self, id: RawFd) -> Option> { - if let Some(terminal) = &self.panes.get(&PaneKind::Terminal(id)) { + fn terminals_below_between_aligning_borders(&self, id: PaneId) -> Option> { + if let Some(terminal) = &self.panes.get(&id) { let left_close_border = terminal.x(); - let right_close_border = terminal.x() + terminal.get_columns() + 1; + let right_close_border = terminal.x() + terminal.columns() + 1; - if let Some(mut terminals_below) = self.terminal_ids_directly_below(&id) { + if let Some(mut terminals_below) = self.pane_ids_directly_below(&id) { let terminal_borders_below = self.vertical_borders(&terminals_below); if terminal_borders_below.contains(&left_close_border) && terminal_borders_below.contains(&right_close_border) @@ -1671,30 +1672,28 @@ impl Tab { } fn close_down_to_max_terminals(&mut self) { if let Some(max_panes) = self.max_panes { - let terminals = self.get_terminal_pane_ids(); - for pid in terminals.iter().skip(max_panes - 1) { + let terminals = self.get_pane_ids(); + for &pid in terminals.iter().skip(max_panes - 1) { self.send_pty_instructions - .send(PtyInstruction::ClosePane(*pid)) + .send(PtyInstruction::ClosePane(pid)) .unwrap(); - self.close_pane_without_rerender(*pid); + self.close_pane_without_rerender(pid); } } } - pub fn get_terminal_pane_ids(&mut self) -> Vec { - self.get_terminals() - .filter_map(|(pid, _)| Some(pid)) - .collect() + pub fn get_pane_ids(&mut self) -> Vec { + self.get_panes().map(|(&pid, _)| pid).collect() } - pub fn close_pane(&mut self, id: RawFd) { - if self.panes.get(&PaneKind::Terminal(id)).is_some() { + pub fn close_pane(&mut self, id: PaneId) { + if self.panes.get(&id).is_some() { self.close_pane_without_rerender(id); } } - pub fn close_pane_without_rerender(&mut self, id: RawFd) { - if let Some(terminal_to_close) = &self.panes.get(&PaneKind::Terminal(id)) { - let terminal_to_close_width = terminal_to_close.get_columns(); - let terminal_to_close_height = terminal_to_close.get_rows(); - if let Some(terminals) = self.terminals_to_the_left_between_aligning_borders(id) { + pub fn close_pane_without_rerender(&mut self, id: PaneId) { + if let Some(terminal_to_close) = self.panes.get(&id) { + let terminal_to_close_width = terminal_to_close.columns(); + let terminal_to_close_height = terminal_to_close.rows(); + if let Some(terminals) = self.panes_to_the_left_between_aligning_borders(id) { for terminal_id in terminals.iter() { self.increase_pane_width_right(&terminal_id, terminal_to_close_width + 1); // 1 for the border @@ -1702,8 +1701,7 @@ impl Tab { if self.active_terminal == Some(id) { self.active_terminal = Some(*terminals.last().unwrap()); } - } else if let Some(terminals) = self.terminals_to_the_right_between_aligning_borders(id) - { + } else if let Some(terminals) = self.panes_to_the_right_between_aligning_borders(id) { for terminal_id in terminals.iter() { self.increase_pane_width_left(&terminal_id, terminal_to_close_width + 1); // 1 for the border @@ -1711,7 +1709,7 @@ impl Tab { if self.active_terminal == Some(id) { self.active_terminal = Some(*terminals.last().unwrap()); } - } else if let Some(terminals) = self.terminals_above_between_aligning_borders(id) { + } else if let Some(terminals) = self.panes_above_between_aligning_borders(id) { for terminal_id in terminals.iter() { self.increase_pane_height_down(&terminal_id, terminal_to_close_height + 1); // 1 for the border @@ -1729,17 +1727,17 @@ impl Tab { } } else { } - self.panes.remove(&PaneKind::Terminal(id)); - if !self.has_terminal_panes() { + self.panes.remove(&id); + if !self.has_panes() { self.active_terminal = None; } } } pub fn close_focused_pane(&mut self) { - if let Some(active_terminal_id) = self.get_active_terminal_id() { - self.close_pane(active_terminal_id); + if let Some(active_pane_id) = self.get_active_pane_id() { + self.close_pane(active_pane_id); self.send_pty_instructions - .send(PtyInstruction::ClosePane(active_terminal_id)) + .send(PtyInstruction::ClosePane(active_pane_id)) .unwrap(); } } @@ -1747,7 +1745,7 @@ impl Tab { if let Some(active_terminal_id) = self.get_active_terminal_id() { let active_terminal = self .panes - .get_mut(&PaneKind::Terminal(active_terminal_id)) + .get_mut(&PaneId::Terminal(active_terminal_id)) .unwrap(); active_terminal.scroll_up(1); self.render(); @@ -1757,7 +1755,7 @@ impl Tab { if let Some(active_terminal_id) = self.get_active_terminal_id() { let active_terminal = self .panes - .get_mut(&PaneKind::Terminal(active_terminal_id)) + .get_mut(&PaneId::Terminal(active_terminal_id)) .unwrap(); active_terminal.scroll_down(1); self.render(); @@ -1767,7 +1765,7 @@ impl Tab { if let Some(active_terminal_id) = self.get_active_terminal_id() { let active_terminal = self .panes - .get_mut(&PaneKind::Terminal(active_terminal_id)) + .get_mut(&PaneId::Terminal(active_terminal_id)) .unwrap(); active_terminal.clear_scroll(); } diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs index 8ae1a972..821183b6 100644 --- a/src/tests/fakes.rs +++ b/src/tests/fakes.rs @@ -1,4 +1,4 @@ -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use std::collections::{HashMap, VecDeque}; use std::io::Write; use std::os::unix::io::RawFd; @@ -130,7 +130,7 @@ impl OsApi for FakeInputOutput { .unwrap() .push(IoEvent::SetTerminalSizeUsingFd(pid, cols, rows)); } - fn into_raw_mode(&mut self, pid: RawFd) { + fn set_raw_mode(&mut self, pid: RawFd) { self.io_events .lock() .unwrap() diff --git a/src/tests/fixtures/layouts/panes-with-plugins.yaml b/src/tests/fixtures/layouts/panes-with-plugins.yaml index 09913bb4..7fb13b2e 100644 --- a/src/tests/fixtures/layouts/panes-with-plugins.yaml +++ b/src/tests/fixtures/layouts/panes-with-plugins.yaml @@ -14,4 +14,4 @@ parts: Percent: 80 - direction: Vertical split_size: - Percent: 20 + Percent: 20 \ No newline at end of file diff --git a/src/tests/integration/basic.rs b/src/tests/integration/basic.rs index 29cabcd5..7c9d3c6a 100644 --- a/src/tests/integration/basic.rs +++ b/src/tests/integration/basic.rs @@ -1,4 +1,4 @@ -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use ::insta::assert_snapshot; use crate::tests::fakes::FakeInputOutput; diff --git a/src/tests/integration/close_pane.rs b/src/tests/integration/close_pane.rs index f57e6a4b..3aed7480 100644 --- a/src/tests/integration/close_pane.rs +++ b/src/tests/integration/close_pane.rs @@ -1,4 +1,4 @@ -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use ::insta::assert_snapshot; use crate::tests::fakes::FakeInputOutput; diff --git a/src/tests/integration/compatibility.rs b/src/tests/integration/compatibility.rs index a82b9d30..8b446cb4 100644 --- a/src/tests/integration/compatibility.rs +++ b/src/tests/integration/compatibility.rs @@ -1,7 +1,7 @@ use ::insta::assert_snapshot; use ::std::collections::HashMap; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::possible_tty_inputs::Bytes; use crate::tests::utils::get_output_frame_snapshots; diff --git a/src/tests/integration/layouts.rs b/src/tests/integration/layouts.rs index fe935d2e..35ff7188 100644 --- a/src/tests/integration/layouts.rs +++ b/src/tests/integration/layouts.rs @@ -1,7 +1,7 @@ use insta::assert_snapshot; use std::path::PathBuf; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::commands::{COMMAND_TOGGLE, QUIT}; use crate::tests::utils::get_output_frame_snapshots; diff --git a/src/tests/integration/move_focus_down.rs b/src/tests/integration/move_focus_down.rs index 72ac27ae..771c31fe 100644 --- a/src/tests/integration/move_focus_down.rs +++ b/src/tests/integration/move_focus_down.rs @@ -1,6 +1,6 @@ use ::insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/move_focus_left.rs b/src/tests/integration/move_focus_left.rs index 2d1a1480..ca676fb6 100644 --- a/src/tests/integration/move_focus_left.rs +++ b/src/tests/integration/move_focus_left.rs @@ -1,6 +1,6 @@ use ::insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/move_focus_right.rs b/src/tests/integration/move_focus_right.rs index 329d2f2b..80e0f2f0 100644 --- a/src/tests/integration/move_focus_right.rs +++ b/src/tests/integration/move_focus_right.rs @@ -1,6 +1,6 @@ use ::insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/move_focus_up.rs b/src/tests/integration/move_focus_up.rs index 273b7dae..5abfb42f 100644 --- a/src/tests/integration/move_focus_up.rs +++ b/src/tests/integration/move_focus_up.rs @@ -1,6 +1,6 @@ use ::insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/resize_down.rs b/src/tests/integration/resize_down.rs index ba56a0c4..3a639910 100644 --- a/src/tests/integration/resize_down.rs +++ b/src/tests/integration/resize_down.rs @@ -1,6 +1,6 @@ use insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/resize_left.rs b/src/tests/integration/resize_left.rs index 95b241e3..685d291c 100644 --- a/src/tests/integration/resize_left.rs +++ b/src/tests/integration/resize_left.rs @@ -1,6 +1,6 @@ use ::insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/resize_right.rs b/src/tests/integration/resize_right.rs index 768636a3..561b962e 100644 --- a/src/tests/integration/resize_right.rs +++ b/src/tests/integration/resize_right.rs @@ -1,6 +1,6 @@ use ::insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/resize_up.rs b/src/tests/integration/resize_up.rs index eb9000df..d94bcd3f 100644 --- a/src/tests/integration/resize_up.rs +++ b/src/tests/integration/resize_up.rs @@ -1,6 +1,6 @@ use ::insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/integration/tabs.rs b/src/tests/integration/tabs.rs index edf239a3..c968e232 100644 --- a/src/tests/integration/tabs.rs +++ b/src/tests/integration/tabs.rs @@ -2,8 +2,8 @@ use insta::assert_snapshot; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; +use crate::{panes::PositionAndSize, tests::utils::commands::CLOSE_FOCUSED_PANE}; use crate::{start, CliArgs}; -use crate::{terminal_pane::PositionAndSize, tests::utils::commands::CLOSE_FOCUSED_PANE}; use crate::tests::utils::commands::{ CLOSE_TAB, COMMAND_TOGGLE, NEW_TAB, QUIT, SPLIT_HORIZONTALLY, SWITCH_NEXT_TAB, SWITCH_PREV_TAB, diff --git a/src/tests/integration/toggle_fullscreen.rs b/src/tests/integration/toggle_fullscreen.rs index db555ae3..f715300d 100644 --- a/src/tests/integration/toggle_fullscreen.rs +++ b/src/tests/integration/toggle_fullscreen.rs @@ -1,6 +1,6 @@ use insta::assert_snapshot; -use crate::terminal_pane::PositionAndSize; +use crate::panes::PositionAndSize; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::get_output_frame_snapshots; use crate::{start, CliArgs}; diff --git a/src/tests/utils.rs b/src/tests/utils.rs index a107dce4..63c953f6 100644 --- a/src/tests/utils.rs +++ b/src/tests/utils.rs @@ -1,5 +1,5 @@ -use crate::terminal_pane::PositionAndSize; -use crate::terminal_pane::TerminalPane; +use crate::panes::PositionAndSize; +use crate::panes::TerminalPane; pub fn get_output_frame_snapshots( output_frames: &[Vec], @@ -7,9 +7,7 @@ pub fn get_output_frame_snapshots( ) -> Vec { let mut vte_parser = vte::Parser::new(); let main_pid = 0; - let x = 0; - let y = 0; - let mut terminal_output = TerminalPane::new(main_pid, *win_size, x, y); + let mut terminal_output = TerminalPane::new(main_pid, *win_size); let mut snapshots = vec![]; for frame in output_frames.iter() { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 140eec18..0ea4c76f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,2 +1,3 @@ pub mod consts; pub mod logging; +pub mod shared; diff --git a/src/utils/shared.rs b/src/utils/shared.rs new file mode 100644 index 00000000..1d8ca5c3 --- /dev/null +++ b/src/utils/shared.rs @@ -0,0 +1,19 @@ +use std::{iter, str::from_utf8}; + +use strip_ansi_escapes::strip; + +pub fn ansi_len(s: &str) -> usize { + from_utf8(&strip(s.as_bytes()).unwrap()) + .unwrap() + .chars() + .count() +} + +pub fn pad_to_size(s: &str, rows: usize, columns: usize) -> String { + s.lines() + .map(|l| [l, &str::repeat(" ", columns - ansi_len(l))].concat()) + .chain(iter::repeat(str::repeat(" ", columns))) + .take(rows) + .collect::>() + .join("\n\r") +} diff --git a/src/wasm_vm.rs b/src/wasm_vm.rs index f07d9e48..51bd9375 100644 --- a/src/wasm_vm.rs +++ b/src/wasm_vm.rs @@ -1,39 +1,44 @@ -use std::{ - path::PathBuf, - process::{Command, Stdio}, -}; -use wasmer::{imports, Function, ImportObject, Store}; +use std::{path::PathBuf, sync::mpsc::Sender}; +use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer_wasi::WasiEnv; +use crate::{pty_bus::PtyInstruction, SenderWithContext}; + #[derive(Clone, Debug)] pub enum PluginInstruction { - Load(PathBuf), + Load(Sender, PathBuf), + Draw(Sender, u32, usize, usize), // String buffer, plugin id, rows, cols + Input(u32, Vec), // plugin id, input bytes Unload(u32), Quit, } -// Plugin API ----------------------------------------------------------------- +#[derive(WasmerEnv, Clone)] +pub struct PluginEnv { + pub send_pty_instructions: SenderWithContext, // FIXME: This should be a big bundle of all of the channels + pub wasi_env: WasiEnv, +} -pub fn mosaic_imports(store: &Store, wasi_env: &WasiEnv) -> ImportObject { +// Plugin API --------------------------------------------------------------------------------------------------------- + +pub fn mosaic_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { imports! { "mosaic" => { - "host_open_file" => Function::new_native_with_env(store, wasi_env.clone(), host_open_file) + "host_open_file" => Function::new_native_with_env(store, plugin_env.clone(), host_open_file) } } } -fn host_open_file(wasi_env: &WasiEnv) { - Command::new("xdg-open") - .arg(format!( - "./{}", - wasi_stdout(wasi_env).lines().next().unwrap() - )) - .stderr(Stdio::null()) - .spawn() +// FIXME: Bundle up all of the channels! Pair that with WasiEnv? +fn host_open_file(plugin_env: &PluginEnv) { + let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); + plugin_env + .send_pty_instructions + .send(PtyInstruction::SpawnTerminal(Some(path))) .unwrap(); } -// Helper Functions ----------------------------------------------------------- +// Helper Functions --------------------------------------------------------------------------------------------------- // FIXME: Unwrap city pub fn wasi_stdout(wasi_env: &WasiEnv) -> String { @@ -44,7 +49,7 @@ pub fn wasi_stdout(wasi_env: &WasiEnv) -> String { buf } -pub fn _wasi_write_string(wasi_env: &WasiEnv, buf: &str) { +pub fn wasi_write_string(wasi_env: &WasiEnv, buf: &str) { let mut state = wasi_env.state(); let wasi_file = state.fs.stdin_mut().unwrap().as_mut().unwrap(); writeln!(wasi_file, "{}\r", buf).unwrap(); diff --git a/strider.wasm b/strider.wasm old mode 100755 new mode 100644 index 69d8617e..e0062773 Binary files a/strider.wasm and b/strider.wasm differ