From 110b179e3651570f6ede3090e2e8f304dac7024d Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Sun, 3 May 2026 11:29:42 +0200 Subject: keybinds seem to work somewhat --- src/run/builtin.rs | 114 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 95 insertions(+), 19 deletions(-) (limited to 'src/run/builtin.rs') 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(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>, _args: &[BString]) { + fn special(&mut self, session: Arc>, _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>(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>, +} + +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>, 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>, @@ -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, 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>, args: &[BString]) { + fn special(&mut self, _session: Arc>, 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)), } -- cgit v1.2.3