From 2d0b41fd06cfa4125b9003d8e1b76e8f83554354 Mon Sep 17 00:00:00 2001 From: Sondre Wold Date: Wed, 1 May 2024 14:04:07 +0200 Subject: [PATCH] More navigation --- octo/Cargo.toml | 1 + octo/src/editor.rs | 56 ++++++++++++++++++++++++++++++++++++++++------ octo/src/row.rs | 35 ++++++++++++++++++++++++++--- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/octo/Cargo.toml b/octo/Cargo.toml index bc088c1..160e751 100644 --- a/octo/Cargo.toml +++ b/octo/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] crossterm = "0.27.0" termion = "3.0.0" +unicode-segmentation = "1" diff --git a/octo/src/editor.rs b/octo/src/editor.rs index 17793ed..458b0e2 100644 --- a/octo/src/editor.rs +++ b/octo/src/editor.rs @@ -62,7 +62,10 @@ impl Editor { println!("Goodbye.\r"); } else { self.draw_rows(); - Terminal::cursor_position(&self.current_position); + Terminal::cursor_position(&Position { + x: self.current_position.x.saturating_sub(self.offset.x), + y: self.current_position.y.saturating_sub(self.offset.y), + }); } Terminal::cursor_show(); Terminal::flush() @@ -98,15 +101,20 @@ impl Editor { } if x < offset.x { offset.x = x; - } else if x >= offset.x.saturating_add(width) { + } else if x >= offset.x.saturating_add(width) { offset.x = x.saturating_sub(width).saturating_add(1); } } fn move_cursor(&mut self, key: Key) { + let terminal_height = self.terminal.size().height as usize; let Position { mut x, mut y } = self.current_position; let size = self.terminal.size(); - let width = size.width.saturating_sub(1) as usize; + let mut width = if let Some(row) = self.document.row(y) { + row.len() + } else { + 0 + }; let height = self.document.len(); match key { Key::Up => y = y.saturating_sub(1), @@ -115,18 +123,52 @@ impl Editor { y = y.saturating_add(1); } } - Key::Left => x = x.saturating_sub(1), + Key::Left => { + if x > 0 { + x -= 1; + } else if y > 0 { + y -= 1; + if let Some(row) = self.document.row(y) { + x = row.len(); + } else { + x = 0; + } + } + } Key::Right => { if x < width { - x = x.saturating_add(1); + x += 1; + } else if y < height { + y += 1; + x = 0; + } + } + Key::PageUp => { + y = if y > terminal_height { + y - terminal_height + } else { + 0 + } + } + Key::PageDown => { + y = if y.saturating_add(terminal_height) < height { + y + terminal_height as usize + } else { + height } } - Key::PageUp => y = 0, - Key::PageDown => y = height, Key::Home => x = 0, Key::End => x = width, _ => (), } + width = if let Some(row) = self.document.row(y) { + row.len() + } else { + 0 + }; + if x > width { + x = width; + } self.current_position = Position { x, y }; } diff --git a/octo/src/row.rs b/octo/src/row.rs index 665c109..44ed395 100644 --- a/octo/src/row.rs +++ b/octo/src/row.rs @@ -1,14 +1,19 @@ use std::cmp; +use unicode_segmentation::UnicodeSegmentation; pub struct Row { string: String, + len: usize, } impl From<&str> for Row { fn from(slice: &str) -> Self { - Self { + let mut row = Self { string: String::from(slice), - } + len: 0, + }; + row.update_len(); + row } } @@ -16,6 +21,30 @@ impl Row { pub fn render(&self, start: usize, end: usize) -> String { let end = cmp::min(end, self.string.len()); let start = cmp::min(start, end); - self.string.get(start..end).unwrap_or_default().to_string() + let mut result = String::new(); + for grapheme in self.string[..] + .graphemes(true) + .skip(start) + .take(end - start) + { + if grapheme == "\t" { + result.push_str(" "); + } else { + result.push_str(grapheme); + } + } + result + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + fn update_len(&mut self) { + self.len = self.string[..].graphemes(true).count() } } -- 2.39.5