diff --git a/src/edit.rs b/src/edit.rs index 53e8629..eb19b4f 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -193,6 +193,11 @@ pub struct Editor { /// this thing draws our prompt prompt: Prompt, + + /// Our current position when scrolling through the history. A hist_pos of 0 means that we're + /// not looking at the history. hist_pos of 1 means that we're looking at the first most-recent + /// command entered. + hist_pos: usize, } impl Editor { @@ -202,6 +207,7 @@ impl Editor { buffer: Buffer::new(), display: output::Writer::stdout()?, prompt: Prompt::new(), + hist_pos: 0, }) } @@ -223,7 +229,7 @@ impl Editor { Status::Ongoing } Control(cc) => self.handle_control(cc)?, - Escape(escape) => self.handle_escape(escape)?, + Escape(escape) => self.handle_escape(escape, history)?, Size => { debug!("ignoring size event"); Status::Ongoing @@ -240,7 +246,10 @@ impl Editor { match status { Status::Ongoing => {} Status::Done => return Ok(None), - Status::Submit(e) => return Ok(Some(e)), + Status::Submit(e) => { + self.hist_pos = 0; + return Ok(Some(e)); + } } } } @@ -261,12 +270,13 @@ impl Editor { Ok(Status::Ongoing) } - fn handle_escape(&mut self, esc: Escape) -> Result { + fn handle_escape(&mut self, esc: Escape, history: &History) -> Result { use Escape::*; match esc { Left => self.back(1)?, Right => self.forward(1)?, - Up => self.prev_history()?, + Up => self.prev_history(history)?, + Down => self.next_history(history)?, Home => self.seek_left()?, End => self.seek_right()?, esc => debug!(" Ignored escape: {esc:?}"), @@ -424,7 +434,41 @@ impl Editor { Ok(()) } - fn prev_history(&mut self) -> Result<()> { + fn prev_history(&mut self, history: &History) -> Result<()> { + let text = match history.look_back(self.hist_pos + 1) { + Some(s) => { + self.hist_pos += 1; + s + } + None => return Ok(()), + }; + self.seek_right()?; + self.clear_left()?; + for c in text.chars() { + self.insert(c)?; + } + Ok(()) + } + + fn next_history(&mut self, history: &History) -> Result<()> { + if self.hist_pos <= 1 { + self.seek_right()?; + self.clear_left()?; + self.hist_pos = 0; + return Ok(()); + } + let text = match history.look_back(self.hist_pos - 1) { + Some(s) => { + self.hist_pos -= 1; + s + } + None => return Ok(()), + }; + self.seek_right()?; + self.clear_left()?; + for c in text.chars() { + self.insert(c)?; + } Ok(()) } } diff --git a/src/history.rs b/src/history.rs index 21d28f2..bd5982a 100644 --- a/src/history.rs +++ b/src/history.rs @@ -10,4 +10,11 @@ impl History { pub fn add>(&mut self, event: S) { self.events.push(event.into()); } + + pub fn look_back(&self, idx: usize) -> Option<&str> { + if idx > self.events.len() { + return None; + } + Some(&self.events[self.events.len() - idx]) + } } diff --git a/src/interactive.rs b/src/interactive.rs index c4760bd..217e226 100644 --- a/src/interactive.rs +++ b/src/interactive.rs @@ -49,6 +49,7 @@ impl Session { Some(text) => text, None => break, }; + history.add(text.clone()); let command = match parse(&text) { Ok(ast) => ast, Err(e) => { diff --git a/src/prompt.rs b/src/prompt.rs index b12a3a7..fc76e97 100644 --- a/src/prompt.rs +++ b/src/prompt.rs @@ -15,6 +15,11 @@ impl Prompt { // | | +-------- clear styles // | | | +- show cursor // v v v v + #[cfg(debug_assertions)] + s: String::from("\x1b[?25l\x1b[31m ▷ \x1b[0m\x1b[?25h"), + + #[allow(dead_code)] + #[cfg(not(debug_assertions))] s: String::from("\x1b[?25l\x1b[32m ▷ \x1b[0m\x1b[?25h"), } }