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()
}
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),
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 };
}
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
}
}
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()
}
}