Round better for up/down

Conrad Irwin created

Change summary

crates/editor/src/display_map.rs |  4 ++--
crates/editor/src/movement.rs    |  8 ++------
crates/gpui/src/text_layout.rs   | 24 ++++++++++++++++++++++++
3 files changed, 28 insertions(+), 8 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -618,9 +618,9 @@ impl DisplaySnapshot {
         display_row: u32,
         x_coordinate: f32,
         text_layout_details: &TextLayoutDetails,
-    ) -> Option<u32> {
+    ) -> u32 {
         let layout_line = self.layout_line_for_row(display_row, text_layout_details);
-        layout_line.index_for_x(x_coordinate).map(|c| c as u32)
+        layout_line.closest_index_for_x(x_coordinate) as u32
     }
 
     // column_for_x(row, x)

crates/editor/src/movement.rs 🔗

@@ -115,9 +115,7 @@ pub fn up_by_rows(
         Bias::Left,
     );
     if point.row() < start.row() {
-        *point.column_mut() = map
-            .column_for_x(point.row(), goal_x, text_layout_details)
-            .unwrap_or(point.column());
+        *point.column_mut() = map.column_for_x(point.row(), goal_x, text_layout_details)
     } else if preserve_column_at_start {
         return (start, goal);
     } else {
@@ -149,9 +147,7 @@ pub fn down_by_rows(
     let new_row = start.row() + row_count;
     let mut point = map.clip_point(DisplayPoint::new(new_row, 0), Bias::Right);
     if point.row() > start.row() {
-        *point.column_mut() = map
-            .column_for_x(point.row(), goal_x, text_layout_details)
-            .unwrap_or(map.line_len(point.row()));
+        *point.column_mut() = map.column_for_x(point.row(), goal_x, text_layout_details)
     } else if preserve_column_at_end {
         return (start, goal);
     } else {

crates/gpui/src/text_layout.rs 🔗

@@ -266,6 +266,8 @@ impl Line {
         self.layout.len == 0
     }
 
+    /// index_for_x returns the character containing the given x coordinate.
+    /// (e.g. to handle a mouse-click)
     pub fn index_for_x(&self, x: f32) -> Option<usize> {
         if x >= self.layout.width {
             None
@@ -281,6 +283,28 @@ impl Line {
         }
     }
 
+    /// closest_index_for_x returns the character boundary closest to the given x coordinate
+    /// (e.g. to handle aligning up/down arrow keys)
+    pub fn closest_index_for_x(&self, x: f32) -> usize {
+        let mut prev_index = 0;
+        let mut prev_x = 0.0;
+
+        for run in self.layout.runs.iter() {
+            for glyph in run.glyphs.iter() {
+                if glyph.position.x() >= x {
+                    if glyph.position.x() - x < x - prev_x {
+                        return glyph.index;
+                    } else {
+                        return prev_index;
+                    }
+                }
+                prev_index = glyph.index;
+                prev_x = glyph.position.x();
+            }
+        }
+        prev_index
+    }
+
     pub fn paint(
         &self,
         origin: Vector2F,