diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 258 |
1 files changed, 184 insertions, 74 deletions
diff --git a/src/main.rs b/src/main.rs index d388ce1..f2b7544 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,7 @@ use crate::completion::{PathCache, completion}; use crate::ctrlc::CtrlC; use crate::cursor::{Direction, move_cursor}; use crate::history::HistoryEntry; -use crate::parse::{Block, ExpString, Parse}; +use crate::parse::{Block, ExpString, Parse, PostExpansion}; macro_rules! print { ($($x:tt)*) => {{ @@ -89,6 +89,11 @@ pub struct Session { path_cache: PathCache, ctrlc: CtrlC, + /// terminfo identifier to command invocation + ti_keybinds: HashMap<BString, parse::Command<PostExpansion>>, + /// byte literals to command invocation + ascii_keybinds: HashMap<BString, parse::Command<PostExpansion>>, + debug_keystrokes: bool, loud: bool, @@ -146,6 +151,11 @@ impl Session { self.line.clear(); } + fn prompt_clear(&mut self) { + self.clear_prompt(); + self.history_visit = 0; + } + fn reprint_prompt(&self) { print!("{}", self.prompt()); self.line.display_pre(); @@ -180,49 +190,6 @@ impl Session { } } - // move to next - fn move_left_word(&mut self) { - let mut i = 0; - - // find word - while let Some(b' ') = self.line.get_left() { - self.line.left(); - i += 1; - } - - // skip it - while let Some(x) = self.line.get_left() - && !x.is_ascii_whitespace() - { - self.line.left(); - i += 1; - } - - cursor::move_cursor(Direction::Left, i); - io::stdout().flush().unwrap(); - } - - fn move_right_word(&mut self) { - let mut i = 0; - - // find word - while let Some(b' ') = self.line.get_right() { - self.line.right(); - i += 1; - } - - // skip it - while let Some(x) = self.line.get_right() - && !x.is_ascii_whitespace() - { - self.line.right(); - i += 1; - } - - cursor::move_cursor(Direction::Right, i); - io::stdout().flush().unwrap(); - } - fn type_byte(&mut self, b: u8) { self.line.add(b); io::stdout().lock().write_all(&[b]).unwrap(); @@ -273,6 +240,82 @@ impl Session { } self.line.display_post(&vec![b' '; del]); } + + fn cursor_left(&mut self) { + if self.line.left() { + move_cursor(Direction::Left, 1); + io::stdout().lock().flush().unwrap(); + } + } + + fn cursor_right(&mut self) { + if self.line.right() { + move_cursor(Direction::Right, 1); + io::stdout().lock().flush().unwrap(); + } + } + + // move to next + fn cursor_left_word(&mut self) { + let mut i = 0; + + // find word + while let Some(b' ') = self.line.get_left() { + self.line.left(); + i += 1; + } + + // skip it + while let Some(x) = self.line.get_left() + && !x.is_ascii_whitespace() + { + self.line.left(); + i += 1; + } + + cursor::move_cursor(Direction::Left, i); + io::stdout().flush().unwrap(); + } + + fn cursor_right_word(&mut self) { + let mut i = 0; + + // find word + while let Some(b' ') = self.line.get_right() { + self.line.right(); + i += 1; + } + + // skip it + while let Some(x) = self.line.get_right() + && !x.is_ascii_whitespace() + { + self.line.right(); + i += 1; + } + + cursor::move_cursor(Direction::Right, i); + io::stdout().flush().unwrap(); + } + + fn complete(session: Arc<Mutex<Session>>) { + let cmd = session.lock().unwrap().line.pre().to_vec(); + + let comp = completion(session.clone(), &cmd); + + let mut se = session.lock().unwrap(); + + se.type_bytes(&comp.shared_prefix); + + if comp.suggestions.len() > 1 { + print!("\r\n"); + for s in comp.suggestions { + io::stdout().lock().write_all(&s.display).unwrap(); + println!(); + } + se.reprint_prompt(); + } + } } fn exec_rc_file(se: Arc<Mutex<Session>>) { @@ -332,8 +375,10 @@ fn event_loop() { aliases: run::Aliases::new(), path_cache: Default::default(), ctrlc: Default::default(), - debug_keystrokes: false, + debug_keystrokes: true, loud: false, + ti_keybinds: HashMap::new(), + ascii_keybinds: HashMap::new(), }; let session = Arc::new(Mutex::new(se)); @@ -348,20 +393,102 @@ fn event_loop() { let _sock_dropper = export_fun::listen(session.clone()); let _ctrlc = ctrlc::setup(session.clone()); - loop { + 'repl: loop { let mut se = session.lock().unwrap(); - use ansi::KeyboardInput as Kb; - match ansi::read(se.debug_keystrokes) { - Kb::CtrlA => se.move_to_begin(), - Kb::CtrlB => { - println!(" Ctrl+B is not yet implemented"); - se.reprint_prompt(); + let Some(key) = ansi::read(se.debug_keystrokes) else { + break; + }; + + if se.debug_keystrokes { + println!("{key:?}"); + } + + if let Some(cmd) = se.ascii_keybinds.get(key.as_bytes()) { + let cmd = cmd.clone(); + drop(se); + // not sure if/how to report this error - would be strange to print something to console every time a keybind command returns nonzero exit code. + let _ = run::run_quiet(session.clone(), cmd); + continue 'repl; + } + + match key { + // TODO: make simple characters also keybinds and do not specially handle tab or newline here. + ansi::KbInput::Key([b'\t']) => { + drop(se); + Session::complete(session.clone()); } - Kb::CtrlC => { - se.clear_prompt(); - se.history_visit = 0; + ansi::KbInput::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); + } } + ansi::KbInput::Key([x]) => se.type_byte(x), + ansi::KbInput::Escape(escape) => { + for terminfo_key in escape.keys.iter() { + if let Some(cmd) = se.ti_keybinds.get(terminfo_key.as_bytes()) { + let cmd = cmd.clone(); + drop(se); + // not sure if/how to report this error - would be strange to print something to console every time a keybind command returns nonzero exit code. + let _ = run::run_quiet(session.clone(), cmd); + continue 'repl; + } + } + + if matches!(&escape.value[..], 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); + } + } + } + ansi::KbInput::InvalidEscape(_) => continue, + } + + /* + match key { + Kb::CtrlA => se.move_to_begin(), Kb::CtrlE => se.move_to_end(), Kb::Eof | Kb::CtrlD => break, Kb::CtrlL => { @@ -401,25 +528,7 @@ fn event_loop() { run::run(session.clone(), parsed); } } - Kb::Key(b'\t') => { - let cmd = se.line.pre().to_vec(); - drop(se); - - let comp = completion(session.clone(), &cmd); - - let mut se = session.lock().unwrap(); - - se.type_bytes(&comp.shared_prefix); - - if comp.suggestions.len() > 1 { - print!("\r\n"); - for s in comp.suggestions { - io::stdout().lock().write_all(&s.display).unwrap(); - println!(); - } - se.reprint_prompt(); - } - } + Kb::Key(b'\t') => {} Kb::Arrow(dir) => match dir { Direction::Up => se.history_up(), Direction::Down => se.history_down(), @@ -459,6 +568,7 @@ fn event_loop() { Kb::End => se.move_to_end(), Kb::Key(x) => se.type_byte(x), } + */ } session.lock().unwrap().raw.disable(); |
