From 0900a27dfc6c7b04ef370cab2a5bd9f1892867e8 Mon Sep 17 00:00:00 2001 From: Krzysztof Juszczyk Date: Fri, 23 Feb 2024 16:20:16 +0100 Subject: [PATCH] [#55213] Include `vte` crate, implement `Perform` trait for `Shell` struct --- Cargo.lock | 28 +++++++++++ Cargo.toml | 1 + src/shell_base.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index cdc32fd..277965f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "autocfg" version = "1.1.0" @@ -670,6 +676,27 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vte" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40eb22ae96f050e0c0d6f7ce43feeae26c348fc4dea56928ca81537cfaa6188b" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "wash" version = "0.1.0" @@ -683,6 +710,7 @@ dependencies = [ "nix 0.26.4", "os_pipe", "regex", + "vte", "wasi", "wasi_ext_lib", ] diff --git a/Cargo.toml b/Cargo.toml index c4ff7a2..0a9de79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ color-eyre = "0.5" lazy_static = "1" regex = "1" glob = "0.3" +vte = "0.13.0" [target.'cfg(target_os = "wasi")'.dependencies] wasi_ext_lib = { git = "https://github.com/antmicro/wasi_ext_lib.git", branch = "main", features = ["hterm"] } diff --git a/src/shell_base.rs b/src/shell_base.rs index 26b84d4..ac84466 100644 --- a/src/shell_base.rs +++ b/src/shell_base.rs @@ -26,6 +26,8 @@ use std::path::{Path, PathBuf}; #[cfg(target_os = "wasi")] use wasi; +use vte::{Params, Parser, Perform}; + #[cfg(target_os = "wasi")] use wasi_ext_lib::termios; @@ -504,6 +506,7 @@ pub struct Shell { pub history: Vec, history_path: PathBuf, + input: String, should_echo: bool, cursor_position: usize, insert_mode: bool, @@ -512,6 +515,128 @@ pub struct Shell { reader: InternalReader, } +impl Perform for Shell { + fn print(&mut self, c: char) { + // regular characters + if self.cursor_position == self.input.len() { + self.input.push(c); + self.echo(&c.to_string()); + } else if self.insert_mode { + // in insert mode, when cursor is in the middle, new character expand CLI + // instead of replacing charcter under cursor + self.input.insert(self.cursor_position, c); + + // for wasi target, we assume that hterm has enabled insert mode + #[cfg(target_os = "wasi")] + self.echo(&c.to_string()); + + #[cfg(not(target_os = "wasi"))] + self.echo(&format!("\x1b[@{}", c)); + } else { + self.input.replace_range( + self.cursor_position..self.cursor_position + 1, + &c.to_string(), + ); + + self.echo(&c.to_string()); + } + + self.cursor_position += 1; + } + + fn execute(&mut self, byte: u8) { + // C0 and C1 control functions + match byte { + // enter + 0x10 | 0x13 => { + self.echo("\n"); + self.cursor_position = 0; + self.input = self.input.trim().to_string(); + } + // backspace + 127 => { + if !self.input.is_empty() && self.cursor_position > 0 { + self.echo("\x1b[D\x1b[P"); + self.input.remove(self.cursor_position - 1); + self.cursor_position -= 1; + } + } + _ => {/* ignore for now */} + } + } + + fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _c: char) { + /* ignore for now */ + } + + fn put(&mut self, _byte: u8) { + /* ignore for now */ + } + + fn unhook(&mut self) { + /* ignore for now */ + } + + fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) { + /* ignore for now */ + } + + fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) { + if params.len() == 1 { + let param = params.iter().next().unwrap(); + match (param[0], c) { + // UpArrow + (_, 'A') => { + + } + // DownArrow + (_, 'B') => { + + } + // RightArrow + (_, 'C') => { + + } + // LeftArrow + (_, 'D') => { + + } + // End + (_, 'F') => { + + } + // Home + (_, 'H') => { + + } + // PageUp + (5, '~') => { + + } + // PageDown + (6, '~') => { + + } + // Insert + (7, '~') => { + + } + // Del + (8, '~') => { + + } + (_, _) => { + + } + } + } + } + + fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) { + /* ignore for now */ + } +} + impl Shell { pub fn new(should_echo: bool, pwd: &str, args: VecDeque) -> Self { Shell { @@ -532,6 +657,7 @@ impl Shell { env!("CARGO_PKG_NAME") ) }), + input: String::new(), vars: HashMap::new(), last_exit_status: EXIT_SUCCESS, last_job_pid: None,