diff options
| -rw-r--r-- | src/main.rs | 71 | ||||
| -rw-r--r-- | src/rope.rs | 17 | ||||
| -rw-r--r-- | src/term.rs | 51 |
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; } |
