aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.rs71
-rw-r--r--src/rope.rs17
-rw-r--r--src/term.rs51
3 files changed, 117 insertions, 22 deletions
diff --git a/src/main.rs b/src/main.rs
index b696fbe..9c30520 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -373,6 +373,77 @@ impl State {
write!(stdout(), "\x1b[{}H", self.cursor_row + 1)?;
stdout().flush()?;
}
+ Key::Home => {
+ self.line_offset = 0;
+ self.cursor_col = 0;
+ write!(stdout(), "\x1b[{}H", self.cursor_row + 1)?;
+ stdout().flush()?;
+ }
+ Key::End => {
+ let size = term::size()?;
+ self.line_offset = line_offset(stdin, &self.line(), size.ws_col - 2)?;
+ self.update_cursor_col(stdin)?;
+ stdout().flush()?;
+ }
+ Key::PgUp => {
+ if self.row_start == 0 {
+ self.cursor_row = 0;
+ self.cursor_col = 0;
+ self.line_offset = 0;
+ write!(stdout(), "\x1b[H")?;
+ stdout().flush()?;
+ return Ok(true);
+ }
+ let size = term::size()?;
+ let mut start = 0;
+ if usize::from(size.ws_row) < self.row_start + 2 {
+ start = self.row_start + 2 - usize::from(size.ws_row);
+ };
+ self.line_offset = line_offset(
+ stdin,
+ &self.buf.line(start + usize::from(self.cursor_row)),
+ self.cursor_col,
+ )?;
+ self.row_start = start;
+ self.repaint_screen()?;
+ self.update_cursor_col(stdin)?;
+ stdout().flush()?;
+ }
+ Key::PgDn => {
+ let size = term::size()?;
+ if self.buf.lines() - self.row_start < usize::from(size.ws_row) {
+ self.cursor_row = u16::try_from(self.buf.lines() - self.row_start)
+ .expect("self.buf.lines() - self.row_start < size.ws_row");
+ self.line_offset = self.line().len();
+ self.update_cursor_col(stdin)?;
+ stdout().flush()?;
+ return Ok(true);
+ }
+ let mut start = self.row_start + 1;
+ if size.ws_row >= 2 {
+ start = self.row_start + usize::from(size.ws_row) - 2;
+ }
+ if start + usize::from(self.cursor_row) <= self.buf.lines() {
+ self.line_offset = line_offset(
+ stdin,
+ &self.buf.line(start + usize::from(self.cursor_row)),
+ self.cursor_col,
+ )?;
+ self.row_start = start;
+ self.repaint_screen()?;
+ self.update_cursor_col(stdin)?;
+ stdout().flush()?;
+ return Ok(true);
+ }
+ self.line_offset =
+ line_offset(stdin, &self.buf.line(self.buf.lines()), self.cursor_col)?;
+ self.row_start = start;
+ self.cursor_row = u16::try_from(self.buf.lines() - start)
+ .expect("start + self.cursor_row > self.buf.lines()");
+ self.repaint_screen()?;
+ self.update_cursor_col(stdin)?;
+ stdout().flush()?;
+ }
Key::Byte(CTRL_Q) => {
return Ok(false);
}
diff --git a/src/rope.rs b/src/rope.rs
index 737d949..8bda5af 100644
--- a/src/rope.rs
+++ b/src/rope.rs
@@ -84,7 +84,7 @@ impl Rope {
Rope(Node::Leaf(Leaf {
unsafe_buf: Some(Rc::from(buf)),
start: 0,
- end: u16::try_from(buf.len()).expect("I just checked the length, it should be ok"),
+ end: u16::try_from(buf.len()).expect("buf.len() <= MAX_NODE_SIZE"),
}))
}
@@ -291,6 +291,9 @@ impl Rope {
}
pub fn line(&self, n: usize) -> Rope {
+ if n > self.lines() {
+ panic!("Line index {} out of range 0..={}", n, self.lines());
+ }
self.slice(self.line_start(n), self.line_end(n)).clone()
}
@@ -327,13 +330,11 @@ impl Rope {
return self.clone();
}
match self {
- Rope(Node::Leaf(l)) => {
- Rope(Node::Leaf(Leaf{
- unsafe_buf: l.unsafe_buf.clone(),
- start: l.start+u16::try_from(start).expect("index.start should be less than MAX_NODE_SIZE since self is a leaf"),
- end: l.start+u16::try_from(end).expect("index.end should be less than or equal to MAX_NODE_SIZE since self is a leaf"),
- }))
- }
+ Rope(Node::Leaf(l)) => Rope(Node::Leaf(Leaf {
+ unsafe_buf: l.unsafe_buf.clone(),
+ start: l.start + u16::try_from(start).expect("start <= self.len()"),
+ end: l.start + u16::try_from(end).expect("end <= self.len()"),
+ })),
Rope(Node::Branch(b)) => {
let mut s = Rope::new(&[]);
let mut start = start;
diff --git a/src/term.rs b/src/term.rs
index 2e37c65..bf06538 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -53,6 +53,10 @@ pub enum Key {
Down,
Left,
Right,
+ Home,
+ End,
+ PgDn,
+ PgUp,
Byte(u8),
}
@@ -74,31 +78,50 @@ pub fn read_key(stdin: &mut BufReader<Stdin>) -> Result<Key, Box<dyn Error>> {
stdin.consume(1);
continue;
}
- let mut n = buf.len();
- for (i, &c) in buf.iter().enumerate() {
- match c {
- b'A'..=b'Z' | b'a'..=b'z' => {
- n = i + 1;
- break;
- }
- _ => (),
+ // An escape sequence usually starts with [, then has one or two numbers separated by
+ // semicolon, and ends with some terminating character. To try and munch the whole
+ // sequence, skip over any numbers and semicolon here.
+ let mut n = 1;
+ while let b'0'..=b'9' | b';' = buf[n] {
+ n += 1;
+ if n == buf.len() - 1 {
+ break;
}
}
- let c = buf[1];
+
+ // Skip the terminating character.
+ n += 1;
+
+ let seq = buf[1..n].to_vec();
stdin.consume(n);
- match c {
- b'A' => {
+ let Ok(s) = String::from_utf8(seq) else {
+ continue;
+ };
+ match s.as_str() {
+ "A" => {
return Ok(Key::Up);
}
- b'B' => {
+ "B" => {
return Ok(Key::Down);
}
- b'C' => {
+ "C" => {
return Ok(Key::Right);
}
- b'D' => {
+ "D" => {
return Ok(Key::Left);
}
+ "H" | "1~" => {
+ return Ok(Key::Home);
+ }
+ "F" | "8~" => {
+ return Ok(Key::End);
+ }
+ "5~" => {
+ return Ok(Key::PgUp);
+ }
+ "6~" => {
+ return Ok(Key::PgDn);
+ }
_ => {
continue;
}