support unicode characters that are longer than one byte

This commit is contained in:
Crispy 2023-02-26 17:53:20 +01:00
parent c6090b3cc1
commit 3f2d3491ed

View file

@ -14,6 +14,7 @@ use termion::{
const TAB_SIZE: usize = 4; const TAB_SIZE: usize = 4;
#[derive(Debug)]
pub struct Editor { pub struct Editor {
text: String, text: String,
lines: Vec<Line>, lines: Vec<Line>,
@ -21,9 +22,11 @@ pub struct Editor {
quit: bool, quit: bool,
} }
#[derive(Debug)]
struct Cursor { struct Cursor {
line: usize, line: usize,
column: usize, column: usize,
// target_column: usize,
} }
type Line = Range<usize>; type Line = Range<usize>;
@ -75,6 +78,9 @@ impl Editor {
Key::Right => self.move_right(), Key::Right => self.move_right(),
Key::Up => self.move_up(), Key::Up => self.move_up(),
Key::Down => self.move_down(), Key::Down => self.move_down(),
Key::F(1) => {
dbg!(&self);
}
_ => (), _ => (),
} }
} }
@ -83,7 +89,7 @@ impl Editor {
fn move_left(&mut self) { fn move_left(&mut self) {
if self.cursor.column > 0 { if self.cursor.column > 0 {
self.cursor.column -= 1; self.cursor.column = self.prev_char_index() - self.current_line().start;
} else if self.cursor.line > 0 { } else if self.cursor.line > 0 {
self.cursor.line -= 1; self.cursor.line -= 1;
self.cursor.column = self.current_line().len(); self.cursor.column = self.current_line().len();
@ -92,7 +98,7 @@ impl Editor {
fn move_right(&mut self) { fn move_right(&mut self) {
if self.cursor.column < self.current_line().len() { if self.cursor.column < self.current_line().len() {
self.cursor.column += 1; self.cursor.column = self.next_char_index() - self.current_line().start;
} else if self.cursor.line < self.lines.len() - 1 { } else if self.cursor.line < self.lines.len() - 1 {
self.cursor.line += 1; self.cursor.line += 1;
self.cursor.column = 0; self.cursor.column = 0;
@ -101,33 +107,53 @@ impl Editor {
fn move_up(&mut self) { fn move_up(&mut self) {
if self.cursor.line > 0 { if self.cursor.line > 0 {
let physical_column = self.text
[self.current_line().start..(self.current_line().start + self.cursor.column)]
.chars()
.count();
self.cursor.line -= 1; self.cursor.line -= 1;
self.cursor.column = self.cursor.column.min(self.current_line().len()); self.cursor.column = physical_column.min(self.current_line().len());
self.ensure_char_boundary();
} }
} }
fn move_down(&mut self) { fn move_down(&mut self) {
if self.cursor.line < self.lines.len() - 1 { if self.cursor.line < self.lines.len() - 1 {
let physical_column = self.text
[self.current_line().start..(self.current_line().start + self.cursor.column)]
.chars()
.count();
self.cursor.line += 1; self.cursor.line += 1;
self.cursor.column = self.cursor.column.min(self.current_line().len()); self.cursor.column = physical_column.min(self.current_line().len());
self.ensure_char_boundary();
} }
} }
fn current_line(&self) -> Line { /// Moves cursor left until it is on a character (in case it was in the middle of a multi-byte character)
self.lines.get(self.cursor.line).unwrap_or(&(0..0)).clone() fn ensure_char_boundary(&mut self) {
while !self
.text
.is_char_boundary(self.current_line().start + self.cursor.column)
{
self.cursor.column -= 1;
}
}
fn current_line(&self) -> &Line {
self.lines.get(self.cursor.line).unwrap()
} }
fn find_lines(&mut self) { fn find_lines(&mut self) {
self.lines.clear(); self.lines.clear();
let mut this_line = 0..0; let mut this_line = 0..0;
for (index, char) in self.text.chars().enumerate() { for (index, char) in self.text.char_indices() {
if char == '\n' { if char == '\n' {
this_line.end = index; this_line.end = index;
self.lines.push(this_line.clone()); self.lines.push(this_line.clone());
this_line.start = index + 1; this_line.start = index + 1;
} }
} }
this_line.end = self.text.chars().count(); this_line.end = self.text.len();
self.lines.push(this_line); self.lines.push(this_line);
} }
@ -143,7 +169,7 @@ impl Editor {
); );
} }
print!( print!(
"{}{}, {}", "{}({}, {})",
cursor::Goto(1, terminal_size().unwrap().1), cursor::Goto(1, terminal_size().unwrap().1),
self.cursor.line, self.cursor.line,
self.cursor.column self.cursor.column
@ -161,34 +187,55 @@ impl Editor {
fn insert_char(&mut self, ch: char) { fn insert_char(&mut self, ch: char) {
// eprintln!("inserting {ch} at {}", self.index()); // eprintln!("inserting {ch} at {}", self.index());
self.text.insert(self.index(), ch); self.text.insert(self.char_index(), ch);
self.find_lines(); self.find_lines();
self.move_right(); self.move_right();
} }
fn backspace(&mut self) { fn backspace(&mut self) {
if self.index() > 0 { if self.char_index() > 0 {
self.text.remove(self.index() - 1);
self.move_left(); self.move_left();
self.text.remove(self.char_index());
self.find_lines(); self.find_lines();
} }
} }
fn delete(&mut self) { fn delete(&mut self) {
if self.index() < self.text.len() { if self.char_index() < self.text.len() {
self.text.remove(self.index()); self.text.remove(self.char_index());
self.find_lines(); self.find_lines();
} }
} }
fn index(&self) -> usize { /// Byte position of current character. May be text.len if cursor is at the end of the file
fn char_index(&self) -> usize {
self.current_line().start + self.cursor.column self.current_line().start + self.cursor.column
} }
/// Byte position of next character.
/// Returns text.len if cursor is on the last character
fn next_char_index(&self) -> usize {
self.text[self.char_index()..]
.char_indices()
.nth(1)
.map_or(self.text.len(), |(byte, _char)| byte + self.char_index())
}
/// Byte position of preceding character.
/// Panics if cursor is at index 0
fn prev_char_index(&self) -> usize {
self.text[..self.char_index()]
.char_indices()
.last()
.map(|(byte, _char)| byte)
.unwrap()
}
fn physical_column(&self) -> usize { fn physical_column(&self) -> usize {
let start = self.current_line().start; let start = self.current_line().start;
let end = self.current_line().start + self.cursor.column; let end = self.char_index();
let preceding_chars = self.text[start..end].chars().count();
let preceding_tabs = self.text[start..end].chars().filter(|&c| c == '\t').count(); let preceding_tabs = self.text[start..end].chars().filter(|&c| c == '\t').count();
self.cursor.column + preceding_tabs * (TAB_SIZE - 1) preceding_chars + preceding_tabs * (TAB_SIZE - 1)
} }
} }