diff options
| author | Jonas Maier <> | 2026-05-03 11:29:42 +0200 |
|---|---|---|
| committer | Jonas Maier <> | 2026-05-03 11:29:42 +0200 |
| commit | 110b179e3651570f6ede3090e2e8f304dac7024d (patch) | |
| tree | f8af54954d0845132d5ee3affed167b06fbc853e | |
| parent | d5953e52f0df8ca6727e71fa07f147467a7369c1 (diff) | |
| download | pish-110b179e3651570f6ede3090e2e8f304dac7024d.tar.gz | |
keybinds seem to work somewhat
| -rw-r--r-- | src/ansi/mod.rs | 4 | ||||
| -rw-r--r-- | src/main.rs | 73 | ||||
| -rw-r--r-- | src/run/builtin.rs | 114 | ||||
| -rw-r--r-- | src/run/mod.rs | 21 |
4 files changed, 135 insertions, 77 deletions
diff --git a/src/ansi/mod.rs b/src/ansi/mod.rs index 0b43ede..cbdcbe6 100644 --- a/src/ansi/mod.rs +++ b/src/ansi/mod.rs @@ -190,7 +190,7 @@ enum EscapeTrie { More(BTreeMap<u8, EscapeTrie>), } -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum KbInput<'a> { Key([u8; 1]), Escape(Escape<'a>), @@ -207,7 +207,7 @@ impl<'a> KbInput<'a> { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Escape<'a> { pub keys: &'a [&'a str], pub value: Vec<u8>, diff --git a/src/main.rs b/src/main.rs index f2b7544..a21b313 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,8 @@ -#![feature(unix_socket_ancillary_data, peer_credentials_unix_socket)] +#![feature( + unix_socket_ancillary_data, + peer_credentials_unix_socket, + associated_type_defaults +)] #![allow(clippy::needless_range_loop)] use std::collections::HashMap; @@ -81,7 +85,7 @@ pub struct Session { line: LineBuf, history: Vec<HistoryEntry>, prev_path: BString, - builtins: HashMap<BString, &'static dyn run::Builtin>, + builtins: HashMap<BString, &'static dyn run::BuiltinClone>, vars: HashMap<BString, BString>, funs: HashMap<BString, Block>, aliases: run::Aliases, @@ -214,6 +218,16 @@ impl Session { self.line.display_post(b" "); } + fn del_left_or_previous(&mut self) { + if self.line.is_empty() && !self.line.is_dirty() && !self.history.is_empty() { + // take previous command for editing + let cmd = self.history[self.history.len() - 1].cmd.clone(); + self.type_bytes(&cmd); + } else { + self.del_left(); + } + } + fn move_to_begin(&mut self) { cursor::move_cursor(Direction::Left, self.line.all_left()); io::stdout().flush().unwrap(); @@ -316,6 +330,11 @@ impl Session { se.reprint_prompt(); } } + + fn screen_clear(&mut self) { + clear_screen(); + self.reprint_prompt(); + } } fn exec_rc_file(se: Arc<Mutex<Session>>) { @@ -375,7 +394,7 @@ fn event_loop() { aliases: run::Aliases::new(), path_cache: Default::default(), ctrlc: Default::default(), - debug_keystrokes: true, + debug_keystrokes: false, loud: false, ti_keybinds: HashMap::new(), ascii_keybinds: HashMap::new(), @@ -488,47 +507,6 @@ fn event_loop() { /* match key { - Kb::CtrlA => se.move_to_begin(), - Kb::CtrlE => se.move_to_end(), - Kb::Eof | Kb::CtrlD => break, - Kb::CtrlL => { - clear_screen(); - print!("{}", se.prompt()); - io::stdout().write_all(&se.line.into_bytes()).unwrap(); - cursor::move_cursor(Direction::Left, se.line.distance_from_right_end()); - io::stdout().lock().flush().unwrap(); - } - Kb::CtrlR => { - println!(" search is not yet implemented"); - se.reprint_prompt(); - } - Kb::Key(b'\r' | b'\n') => { - let line = se.line.into_bytes(); - - if !line.is_empty() { - let parsed = match parse::do_parse(&line) { - Ok(p) => p, - Err((crate::parse::ParseError::Eof, _)) => { - se.line.add(b'\n'); - print!("\r\n> "); - continue; - } - Err(e) => { - println!("{e:?}\n{}", se.prompt()); - continue; - } - }; - print!("\r\n"); - let entry = HistoryEntry::new(line.clone()); - history::persist(&entry); - se.history.push(entry); - se.history_visit = 0; - se.line.dump(); - drop(se); - run::run(session.clone(), parsed); - } - } - Kb::Key(b'\t') => {} Kb::Arrow(dir) => match dir { Direction::Up => se.history_up(), Direction::Down => se.history_down(), @@ -554,13 +532,6 @@ fn event_loop() { } }, Kb::DeleteLeft => { - if se.line.is_empty() && !se.line.is_dirty() && !se.history.is_empty() { - // take previous command for editing - let cmd = se.history[se.history.len() - 1].cmd.clone(); - se.type_bytes(&cmd); - } else { - se.del_left(); - } } Kb::DeleteRight => se.del_right(), Kb::CtrlDeleteRight => se.del_right_word(), diff --git a/src/run/builtin.rs b/src/run/builtin.rs index 507759f..2886d71 100644 --- a/src/run/builtin.rs +++ b/src/run/builtin.rs @@ -48,6 +48,7 @@ fn read_args<T: ArgParse>(args: &[BString], w: &mut dyn Write) -> std::result::R Err(Error::Exit(-2)) } +#[derive(Copy, Clone)] pub struct cd; impl Builtin for cd { fn name(&self) -> &str { @@ -91,6 +92,7 @@ impl Builtin for cd { } } +#[derive(Copy, Clone)] pub struct clear; impl Builtin for clear { fn name(&self) -> &str { @@ -110,13 +112,14 @@ impl Builtin for clear { } /// restart shell +#[derive(Copy, Clone)] pub struct re; impl Builtin for re { fn name(&self) -> &str { "re" } - fn special(&self, session: Arc<Mutex<Session>>, _args: &[BString]) { + fn special(&mut self, session: Arc<Mutex<Session>>, _args: &[BString]) { session.lock().unwrap().raw.disable(); crate::reload::begin_reload(); session.lock().unwrap().raw.enable(); // something went wrong, let's restore raw mode @@ -133,6 +136,7 @@ impl Builtin for re { } } +#[derive(Copy, Clone)] pub struct Sink { name: &'static str, append: bool, @@ -168,6 +172,7 @@ pub const fn sink(name: &'static str, append: bool) -> Sink { Sink { name, append } } +#[derive(Copy, Clone)] pub struct from; impl Builtin for from { fn name(&self) -> &str { @@ -191,6 +196,7 @@ impl Builtin for from { } } +#[derive(Copy, Clone)] pub struct _type; impl Builtin for _type { fn name(&self) -> &str { @@ -227,6 +233,7 @@ impl Builtin for _type { } } +#[derive(Copy, Clone)] pub struct builtins; impl Builtin for builtins { fn name(&self) -> &str { @@ -280,6 +287,7 @@ fn display_history<T: Iterator<Item = HistoryEntry>>(it: T, stdout: &mut dyn Wri Ok(()) } +#[derive(Copy, Clone)] pub struct history; impl Builtin for history { fn name(&self) -> &str { @@ -332,6 +340,7 @@ impl Builtin for history { } } +#[derive(Copy, Clone)] pub struct escape; impl Builtin for escape { fn name(&self) -> &str { @@ -354,6 +363,7 @@ impl Builtin for escape { } } +#[derive(Copy, Clone)] pub struct parse; impl Builtin for parse { fn name(&self) -> &str { @@ -383,6 +393,7 @@ impl Builtin for parse { } } +#[derive(Copy, Clone)] pub struct completion; impl Builtin for completion { fn name(&self) -> &str { @@ -410,6 +421,7 @@ impl Builtin for completion { } } +#[derive(Copy, Clone)] pub struct null; impl Builtin for null { fn name(&self) -> &str { @@ -428,6 +440,7 @@ impl Builtin for null { } } +#[derive(Copy, Clone)] pub struct var; impl Builtin for var { fn name(&self) -> &str { @@ -456,6 +469,7 @@ impl Builtin for var { } } +#[derive(Copy, Clone)] pub struct alias; impl Builtin for alias { fn name(&self) -> &str { @@ -517,6 +531,7 @@ impl Builtin for alias { } } +#[derive(Copy, Clone)] pub struct unalias; impl Builtin for unalias { fn name(&self) -> &str { @@ -541,6 +556,7 @@ impl Builtin for unalias { } } +#[derive(Copy, Clone)] pub struct debug; impl Builtin for debug { fn name(&self) -> &str { @@ -565,6 +581,7 @@ impl Builtin for debug { } } +#[derive(Copy, Clone)] pub struct terminfo; impl Builtin for terminfo { fn name(&self) -> &str { @@ -602,12 +619,35 @@ impl Builtin for terminfo { } } -pub struct bind; +#[derive(Clone)] +pub struct bind { + key: Option<crate::ansi::KbInput<'static>>, +} + +impl bind { + pub const fn new() -> Self { + Self { key: None } + } + + fn is_interactive(args: &[BString]) -> bool { + matches!(args.get(0).map(|x| &x[..]), Some(b"i" | b"interactive")) + } +} + impl Builtin for bind { fn name(&self) -> &str { "bind" } + fn special(&mut self, _session: Arc<Mutex<Session>>, args: &[BString]) { + if Self::is_interactive(args) { + let raw = crate::raw::ScopedRawMode::on_fd(0); + raw.enable(); + self.key = crate::ansi::read(false); + raw.disable(); + } + } + fn io( &self, session: Arc<Mutex<Session>>, @@ -618,47 +658,78 @@ impl Builtin for bind { let mut usage = || { writeln!( stdout, - "usage: bind ti NAMED_KEYBIND COMMAND | bind key KEY COMMAND" + "usage: bind ti NAMED_KEYBIND COMMAND | bind key KEY COMMAND | bind [i|interactive]" )?; Err(Error::Exit(1)) }; - if args.len() < 3 { + if args.len() < 2 { return usage(); } let kind = &args[0]; - let key = &args[1]; - let cmd = &args[2]; - let args = &args[3..]; let mut se = session.lock().unwrap(); - let map = match &kind[..] { - b"ti" => &mut se.ti_keybinds, - b"key" => &mut se.ascii_keybinds, - _ => return usage(), + let bind0 = |map: &mut HashMap<BString, _>, key: &[u8], cmd: &[BString]| { + map.insert( + key.to_vec(), + crate::parse::Command { + cmd: cmd[0].clone(), + args: cmd[1..].to_vec(), + }, + ); + }; + + let bind1 = |map: &mut HashMap<_, _>| { + bind0(map, &args[1], &args[2..]); }; - map.insert( - key.clone(), - crate::parse::Command { - cmd: cmd.clone(), - args: args.to_vec(), - }, - ); + if Self::is_interactive(args) { + let Some(key) = self.key.as_ref() else { + writeln!(stdout, "no input available.")?; + return Err(Error::Exit(-1)); + }; + + let x = match key { + ansi::KbInput::Escape(e) => { + bind0(&mut se.ascii_keybinds, e.keys[0].as_bytes(), &args[1..]); + format!("ti {}", e.keys[0]) + } + _ => { + bind0(&mut se.ti_keybinds, key.as_bytes(), &args[1..]); + format!("key {}", key.as_bytes().escape_ascii()) + } + }; + + // print what gets bound + write!(stdout, "bind {x} ")?; + for arg in args.iter().skip(1) { + stdout.write(&arg[..])?; + write!(stdout, " ")?; + } + writeln!(stdout)?; + return Ok(()); + } + + match &kind[..] { + b"ti" => bind1(&mut se.ti_keybinds), + b"key" => bind1(&mut se.ascii_keybinds), + _ => return usage(), + } Ok(()) } } +#[derive(Clone)] pub struct exit; impl Builtin for exit { fn name(&self) -> &str { "exit" } - fn special(&self, _session: Arc<Mutex<Session>>, args: &[BString]) { + fn special(&mut self, _session: Arc<Mutex<Session>>, args: &[BString]) { let exit_code: i32 = loop { let Some(arg) = args.get(0) else { break 0; @@ -687,6 +758,7 @@ impl Builtin for exit { } /// control terminal +#[derive(Copy, Clone)] pub struct ct; impl Builtin for ct { fn name(&self) -> &str { @@ -714,12 +786,16 @@ impl Builtin for ct { b"cursor_right_word" => se.cursor_right_word(), b"cursor_left_word" => se.cursor_left_word(), b"prompt_clear" => se.prompt_clear(), + b"screen_clear" => se.screen_clear(), b"complete" => { drop(se); Session::complete(session) } b"history_previous" => se.history_up(), b"history_next" => se.history_down(), + b"prompt_del_left" => se.del_left(), + b"prompt_del_right" => se.del_right(), + b"prompt_del_left_or_previous" => se.del_left_or_previous(), _ => return Err(Error::Exit(-2)), } diff --git a/src/run/mod.rs b/src/run/mod.rs index a31ff1a..f727b01 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -202,6 +202,7 @@ impl Executor { ) -> SpawnedCmd { match cmd { CommandKind::Builtin(builtin) => { + let mut builtin = builtin.clone_box(); builtin.special(self.se.clone(), &args[1..]); let cloned_session = self.se.clone(); let handle = wait::spawn(move || { @@ -532,7 +533,7 @@ type BuiltinResult = Result<(), BuiltinError>; pub trait Builtin: Send + Sync { fn name(&self) -> &str; - fn special(&self, session: Arc<Mutex<Session>>, args: &[BString]) {} + fn special(&mut self, session: Arc<Mutex<Session>>, args: &[BString]) {} fn io( &self, @@ -543,7 +544,17 @@ pub trait Builtin: Send + Sync { ) -> BuiltinResult; } -const BUILTINS: &[&'static dyn Builtin] = &[ +pub trait BuiltinClone : Builtin { + fn clone_box(&self) -> Box<dyn Builtin>; +} + +impl<T: 'static + Builtin + Clone> BuiltinClone for T { + fn clone_box(&self) -> Box<dyn Builtin> { + Box::new(self.clone()) + } +} + +const BUILTINS: &[&'static dyn BuiltinClone] = &[ &builtin::cd, &builtin::clear, #[cfg(debug_assertions)] @@ -565,12 +576,12 @@ const BUILTINS: &[&'static dyn Builtin] = &[ #[cfg(debug_assertions)] &builtin::debug, &builtin::terminfo, - &builtin::bind, + &builtin::bind::new(), &builtin::exit, &builtin::ct, ]; -pub fn builtin_map() -> HashMap<BString, &'static dyn Builtin> { +pub fn builtin_map() -> HashMap<BString, &'static dyn BuiltinClone> { let mut map = HashMap::new(); for &b in BUILTINS { map.insert(b.name().as_bytes().to_vec(), b); @@ -580,7 +591,7 @@ pub fn builtin_map() -> HashMap<BString, &'static dyn Builtin> { #[derive(Clone)] pub enum CommandKind { - Builtin(&'static dyn Builtin), + Builtin(&'static dyn BuiltinClone), Fun(Block), Path(PathBuf), } |
