aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs258
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();