diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2024-01-06 10:10:52 -0800 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2024-01-06 10:10:52 -0800 |
| commit | dbb1ba202bb5803af1b580b588e7aeef0c6f7e31 (patch) | |
| tree | 937c755caafc14367b108eae6103b8987ee44aa8 /src/rope.rs | |
| parent | f50c6f210a353d8b12552f6a20bf58c2a7bdd3df (diff) | |
| download | editor-dbb1ba202bb5803af1b580b588e7aeef0c6f7e31.tar.zst | |
Fix unicode handling.
It's hard to say if this counts as "fixed," but I think it's at least
better than GNU nano.
Diffstat (limited to 'src/rope.rs')
| -rw-r--r-- | src/rope.rs | 86 |
1 files changed, 78 insertions, 8 deletions
diff --git a/src/rope.rs b/src/rope.rs index ca057bd..ea37e6c 100644 --- a/src/rope.rs +++ b/src/rope.rs @@ -76,6 +76,9 @@ impl Rope { } pub fn print(&self, out: &mut dyn Write) -> Result<(), std::io::Error> { + if self.len() == 0 { + return Ok(()); + } // TODO: escape unprintable characters match self { Rope(Node::Leaf(l)) => { @@ -209,9 +212,9 @@ impl Rope { return Ok(()); } - fn slice(&self, start: usize, end: usize) -> Rope { - if start >= self.len() { - panic!("Index {} out of range 0..{}", start, self.len()); + pub fn slice(&self, start: usize, end: usize) -> Rope { + if start > self.len() { + panic!("Index {} out of range 0..{}", start, self.len()+1); } if end > self.len() { panic!("Index {} out of range 0..{}", end, self.len()); @@ -223,8 +226,8 @@ impl Rope { Rope(Node::Leaf(l)) => { return Rope(Node::Leaf(Leaf{ buf: l.buf.clone(), - start: u8::try_from(start).expect("buffer too long"), - end: u8::try_from(end).expect("buffer too long"), + start: l.start + u8::try_from(start).expect("buffer too long"), + end: l.start + u8::try_from(end).expect("buffer too long"), })); } Rope(Node::Branch(b)) => { @@ -234,11 +237,11 @@ impl Rope { } let mut right = Rope::leaf(Vec::new()); if end > b.left.len() { - let mut left_start = 0; + let mut right_start = 0; if start > b.left.len() { - left_start = start - b.left.len(); + right_start = start - b.left.len(); } - right = b.right.slice(left_start, end - b.left.len()); + right = b.right.slice(right_start, end - b.left.len()); } return left.concat(&right); } @@ -256,4 +259,71 @@ impl Rope { } return self.slice(start, end-1); } + + pub fn char(&self, n: usize) -> Result<usize, usize> { + match self { + Rope(Node::Leaf(l)) => { + let mut count = 0; + for (i, &b) in l.bytes().iter().enumerate() { + if b & 0xc0 == 0x80 { + continue; + } + if count == n { + return Ok(i); + } + count += 1; + } + return Err(count); + } + Rope(Node::Branch(b)) => { + match b.left.char(n) { + Ok(i) => { + return Ok(i); + } + Err(len) => { + match b.right.char(n - len) { + Ok(i) => { + return Ok(b.left.len() + i); + } + Err(right_len) => { + return Err(len + right_len); + } + } + } + } + } + } + } + + fn is_char_boundary(&self, index: usize) -> bool { + match self { + Rope(Node::Leaf(l)) => { + return l.bytes()[index] & 0xc0 != 0x80; + } + Rope(Node::Branch(b)) => { + if index < b.left.len() { + return b.left.is_char_boundary(index); + } + return b.right.is_char_boundary(index - b.left.len()); + } + } + } + + pub fn floor_char_boundary(&self, index: usize) -> usize { + for i in (0..index+1).rev() { + if self.is_char_boundary(i) { + return i; + } + } + panic!("I'm not valid UTF-8: {:?}", self); + } + + pub fn ceil_char_boundary(&self, index: usize) -> usize { + for i in index..self.len() { + if self.is_char_boundary(i) { + return i; + } + } + return self.len(); + } } |
