aboutsummaryrefslogtreecommitdiffstats
path: root/src/rope.rs
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-01-06 10:10:52 -0800
committerRose Hogenson <rosehogenson@posteo.net>2024-01-06 10:10:52 -0800
commitdbb1ba202bb5803af1b580b588e7aeef0c6f7e31 (patch)
tree937c755caafc14367b108eae6103b8987ee44aa8 /src/rope.rs
parentf50c6f210a353d8b12552f6a20bf58c2a7bdd3df (diff)
downloadeditor-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.rs86
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();
+ }
}