chiark / gitweb /
Fix movement by words in the editor buffer.
authorSimon Tatham <anakin@pobox.com>
Tue, 2 Jan 2024 11:05:17 +0000 (11:05 +0000)
committerSimon Tatham <anakin@pobox.com>
Tue, 2 Jan 2024 17:30:18 +0000 (17:30 +0000)
I apparently only half-finished writing is_word_boundary: it tested
that the _next_ character wasn't a space, but not that the previous
one was.

src/editor.rs

index bef31c66a96091768298b9a1df396af2bdefc4e9..343db2653a3ba4b05f741fa4db2b917e943b1655 100644 (file)
@@ -46,41 +46,62 @@ impl EditorCore {
         }
     }
 
+    fn is_word_sep(c: char) -> bool {
+        c == ' '
+    }
+
     fn is_word_boundary(&self, pos: usize) -> bool {
         if !self.is_char_boundary(pos) {
             false
-        } else {
-            match self.text[pos..].chars().next() {
-                None => true, // end of string
-
-                // not just before a space
-                Some(c) => c != ' '
+        } else if let Some(prevpos) = self.prev_position(pos) {
+            let mut chars = self.text[prevpos..].chars();
+            let prevchar = chars.next().expect("We're not at end of string");
+            if let Some(thischar) = chars.next() {
+                Self::is_word_sep(prevchar) && !Self::is_word_sep(thischar)
+            } else {
+                true // end of string
             }
+        } else {
+            true // start of string
         }
     }
 
-    fn forward(&mut self) -> bool {
+    fn next_position(&self, pos: usize) -> Option<usize> {
         let len = self.text.len();
-        if self.point >= len {
-            false
+        if pos >= len {
+            None
         } else {
-            self.point += 1;
-            while !self.is_char_boundary(self.point) {
-                self.point += 1;
+            let mut pos = pos + 1;
+            while !self.is_char_boundary(pos) {
+                pos += 1;
             }
-            true
+            Some(pos)
         }
     }
 
-    fn backward(&mut self) -> bool {
-        if self.point == 0 {
-            false
+    fn prev_position(&self, pos: usize) -> Option<usize> {
+        if pos == 0 {
+            None
         } else {
-            self.point -= 1;
-            while !self.is_char_boundary(self.point) {
-                self.point -= 1;
+            let mut pos = pos - 1;
+            while !self.is_char_boundary(pos) {
+                pos -= 1;
             }
-            true
+            Some(pos)
+        }
+    }
+
+    fn forward(&mut self) -> bool {
+        match self.next_position(self.point) {
+            Some(pos) => { self.point = pos; true }
+            None => false
+        }
+    }
+
+    fn backward(&mut self) -> bool {
+        match self.prev_position(self.point) {
+            Some(pos) => { self.point = pos; true }
+            None => false
         }
     }
 
@@ -224,6 +245,39 @@ fn test_forward_backward() {
     assert_eq!(ec.backward(), true);
 }
 
+#[test]
+fn test_forward_backward_word() {
+    let mut ec = EditorCore {
+        text: "lorem ipsum dolor sit amet".to_owned(),
+        point: 0,
+        paste_buffer: "".to_owned(),
+    };
+
+    assert_eq!(ec.forward_word(), true);
+    assert_eq!(ec.point, 6);    // ipsum
+    assert_eq!(ec.forward_word(), true);
+    assert_eq!(ec.point, 12);   // dolor
+    assert_eq!(ec.forward_word(), true);
+    assert_eq!(ec.point, 18);   // sit
+    assert_eq!(ec.forward_word(), true);
+    assert_eq!(ec.point, 22);   // amet
+    assert_eq!(ec.forward_word(), true);
+    assert_eq!(ec.point, 26);   // end of string
+    assert_eq!(ec.forward_word(), false);
+
+    assert_eq!(ec.backward_word(), true);
+    assert_eq!(ec.point, 22);   // amet
+    assert_eq!(ec.backward_word(), true);
+    assert_eq!(ec.point, 18);   // sit
+    assert_eq!(ec.backward_word(), true);
+    assert_eq!(ec.point, 12);   // dolor
+    assert_eq!(ec.backward_word(), true);
+    assert_eq!(ec.point, 6);    // ipsum
+    assert_eq!(ec.backward_word(), true);
+    assert_eq!(ec.point, 0);    // lorem
+    assert_eq!(ec.backward_word(), false);
+}
+
 #[test]
 fn test_delete() {
     let mut ec = EditorCore {
@@ -864,7 +918,7 @@ impl Composer {
         // buffer text.
         let start_cell_index = self.layout
             .partition_point(|cell| cell.y < y);
-        if dbg!(start_cell_index) == self.layout.len() {
+        if start_cell_index == self.layout.len() {
             return None;    // y is after the end of the buffer
         }
         let start_pos = self.layout[start_cell_index].pos;