diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2024-01-07 00:18:07 -0800 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2024-01-07 00:18:07 -0800 |
| commit | 79b47a31332309b6fc1e5831e031ee4ca0d54450 (patch) | |
| tree | 8d5b815f21c78906b22ece75ca629e1b563e221c | |
| parent | 01510eeccff4f5062ec061a964176d3a800a7094 (diff) | |
| download | editor-79b47a31332309b6fc1e5831e031ee4ca0d54450.tar.zst | |
Implement redo.
Is this how redo works?
| -rw-r--r-- | src/main.rs | 130 | ||||
| -rw-r--r-- | src/term.rs | 5 |
2 files changed, 86 insertions, 49 deletions
diff --git a/src/main.rs b/src/main.rs index c1131f1..f0a38d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,13 +5,13 @@ use rope::Rope; use std::error::Error; use std::ffi::{OsStr, OsString}; use std::io::{stdin, stdout, Read, Write}; +use std::ops::{Deref, DerefMut}; use std::path::Path; use term::Key; #[derive(Debug, Clone, Copy)] enum Mode { Insert, - Backspace, Normal, } @@ -24,7 +24,6 @@ struct State { cursor_col: usize, line_offset: usize, line_cols: usize, - prev: Option<Box<State>>, } fn parse_status_report(buf: &[u8]) -> Result<(usize, usize), Box<dyn Error>> { @@ -46,28 +45,28 @@ fn parse_status_report(buf: &[u8]) -> Result<(usize, usize), Box<dyn Error>> { return Ok((row - 1, col - 1)); } -impl State { - fn cursor_pos(&mut self) -> Result<(usize, usize), Box<dyn Error>> { - write!(stdout(), "\x1b[6n") - .map_err(|err| format!("cursor position: status report: {}", err))?; - stdout() - .flush() - .map_err(|err| format!("cursor position: status report: {}", err))?; - let mut buf = vec![0; 20]; - let n = stdin() - .read(&mut buf) - .map_err(|err| format!("cursor position: read status report: {}", err))?; - buf.truncate(n); - let pos = parse_status_report(&buf).map_err(|_| { - format!( - "cursor position: invalid response: {}", - String::from_utf8_lossy(&buf) - ) - })?; - return Ok(pos); - } +fn cursor_pos() -> Result<(usize, usize), Box<dyn Error>> { + write!(stdout(), "\x1b[6n") + .map_err(|err| format!("cursor position: status report: {}", err))?; + stdout() + .flush() + .map_err(|err| format!("cursor position: status report: {}", err))?; + let mut buf = vec![0; 20]; + let n = stdin() + .read(&mut buf) + .map_err(|err| format!("cursor position: read status report: {}", err))?; + buf.truncate(n); + let pos = parse_status_report(&buf).map_err(|_| { + format!( + "cursor position: invalid response: {}", + String::from_utf8_lossy(&buf) + ) + })?; + return Ok(pos); +} - fn repaint_all(&mut self) -> Result<(), Box<dyn Error>> { +impl State { + fn repaint_all(&self) -> Result<(), Box<dyn Error>> { let size = term::size()?; let mut stdout = stdout().lock(); @@ -80,7 +79,7 @@ impl State { break; } self.buf.line(i).print(&mut stdout)?; - let (_, col) = self.cursor_pos()?; + let (_, col) = cursor_pos()?; if col == usize::from(size.ws_col - 1) { write!(stdout, "\x1b[{}G\x1b[30m\x1b[47m>\x1b[m ", size.ws_col - 1)?; } @@ -96,7 +95,7 @@ impl State { let line = self.buf.line(self.row_start + self.cursor_row); line.print(&mut stdout)?; - let (_, col) = self.cursor_pos()?; + let (_, col) = cursor_pos()?; self.line_cols = col; let mut truncated = false; if col == usize::from(term_size.ws_col - 1) { @@ -123,7 +122,7 @@ impl State { let x = line.floor_char_boundary(lo + (hi - lo) / 2); write!(stdout, "\x1b[G")?; line.slice(0, x).print(&mut stdout)?; - let (_, col) = self.cursor_pos()?; + let (_, col) = cursor_pos()?; if col <= self.cursor_col { lo = line.ceil_char_boundary(x + 1); } else { @@ -204,43 +203,66 @@ impl State { self.repaint_line_full()?; return Ok(()); } +} + +#[derive(Debug)] +struct StateHistory { + i: usize, + history: Vec<State>, +} +impl StateHistory { fn set_mode(&mut self, mode: Mode) { match (self.mode, mode) { - (Mode::Insert, Mode::Insert) => (), - (Mode::Insert, _) => { - if let Ok((_, col)) = self.cursor_pos() { + (Mode::Insert, Mode::Normal) => { + if let Ok((_, col)) = cursor_pos() { self.cursor_col = col; } let _ = self.repaint_line_full(); } - (Mode::Normal, Mode::Normal) => (), - (Mode::Normal, _) => { - let prev = self.prev.take(); - let mut curr = Box::new(self.clone()); - curr.prev = prev; - self.prev = Some(curr); + (Mode::Normal, Mode::Insert) => { + let curr = self.clone(); + self.i += 1; + self.history.truncate(self.i); + self.history.push(curr); } _ => (), } self.mode = mode; } } + +impl Deref for StateHistory { + type Target = State; + + fn deref(&self) -> &Self::Target { + return &self.history[self.i]; + } +} + +impl DerefMut for StateHistory { + fn deref_mut(&mut self) -> &mut Self::Target { + return &mut self.history[self.i]; + } +} + fn edit(file: &OsStr) -> Result<(), Box<dyn Error>> { let _raw_handle = term::raw().map_err(|err| format!("cannot put terminal in raw mode: {}", err))?; let r = Rope::open(Path::new(file))?; let size = term::size()?; - let mut state = State { - buf: r, - mode: Mode::Normal, - row_start: 0, - cursor_row: 0, - cursor_col: 0, - line_offset: 0, - line_cols: 0, - prev: None, + let mut state = StateHistory { + i: 0, + history: vec![State { + buf: r, + mode: Mode::Normal, + row_start: 0, + cursor_row: 0, + cursor_col: 0, + line_offset: 0, + line_cols: 0, + }], }; let _ = state.repaint_all(); let _ = write!(stdout(), "\x1b[H"); @@ -258,10 +280,20 @@ fn edit(file: &OsStr) -> Result<(), Box<dyn Error>> { } Key::CtrlZ => { state.set_mode(Mode::Normal); - let Some(prev) = state.prev else { + if state.i == 0 { + continue; + } + state.i -= 1; + let _ = state.repaint_all(); + let _ = state.repaint_line_full(); + let _ = stdout().flush(); + } + Key::CtrlY => { + state.set_mode(Mode::Normal); + if state.i == state.history.len() - 1 { continue; - }; - state = *prev; + } + state.i += 1; let _ = state.repaint_all(); let _ = state.repaint_line_full(); let _ = stdout().flush(); @@ -287,7 +319,7 @@ fn edit(file: &OsStr) -> Result<(), Box<dyn Error>> { let _ = stdout().flush(); } Key::Backspace => { - state.set_mode(Mode::Backspace); + state.set_mode(Mode::Insert); if state.row_start == 0 && state.cursor_row == 0 && state.line_offset == 0 { continue; } @@ -299,7 +331,7 @@ fn edit(file: &OsStr) -> Result<(), Box<dyn Error>> { } else { let _ = write!(stdout(), "\x1b[G"); let _ = state.buf.slice(line_start, gap_start).print(&mut stdout()); - let (_, col) = state.cursor_pos()?; + let (_, col) = cursor_pos()?; state.cursor_col = col; } state.buf = state diff --git a/src/term.rs b/src/term.rs index 3a56091..988786d 100644 --- a/src/term.rs +++ b/src/term.rs @@ -69,6 +69,7 @@ pub enum Key { CtrlQ, CtrlS, CtrlZ, + CtrlY, Up, Down, Left, @@ -80,6 +81,7 @@ pub enum Key { pub fn read_key() -> Result<Key, Box<dyn Error>> { const CTRL_Q: u8 = 17; const CTRL_S: u8 = 19; + const CTRL_Y: u8 = 25; const CTRL_Z: u8 = 26; const BACKSPACE: u8 = 127; const ESC: u8 = 27; @@ -93,6 +95,9 @@ pub fn read_key() -> Result<Key, Box<dyn Error>> { if buf[0] == CTRL_S { return Ok(Key::CtrlS); } + if buf[0] == CTRL_Y { + return Ok(Key::CtrlY); + } if buf[0] == CTRL_Z { return Ok(Key::CtrlZ); } |
