aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-01-07 00:18:07 -0800
committerRose Hogenson <rosehogenson@posteo.net>2024-01-07 00:18:07 -0800
commit79b47a31332309b6fc1e5831e031ee4ca0d54450 (patch)
tree8d5b815f21c78906b22ece75ca629e1b563e221c /src/main.rs
parent01510eeccff4f5062ec061a964176d3a800a7094 (diff)
downloadeditor-79b47a31332309b6fc1e5831e031ee4ca0d54450.tar.zst
Implement redo.
Is this how redo works?
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs130
1 files changed, 81 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