Add a bias parameter when converting buffer points to fold points

Max Brunsfeld created

Change summary

zed/src/editor.rs                      | 62 +++++++++++++++------------
zed/src/editor/buffer/selection.rs     |  8 +-
zed/src/editor/display_map.rs          | 46 ++++++++++---------
zed/src/editor/display_map/fold_map.rs | 30 +++++++++----
4 files changed, 83 insertions(+), 63 deletions(-)

Detailed changes

zed/src/editor.rs 🔗

@@ -528,14 +528,14 @@ impl Editor {
             .first()
             .unwrap()
             .head()
-            .to_display_point(&display_map)
+            .to_display_point(&display_map, Bias::Left)
             .row() as f32;
         let last_cursor_bottom = self
             .selections(cx)
             .last()
             .unwrap()
             .head()
-            .to_display_point(&display_map)
+            .to_display_point(&display_map, Bias::Right)
             .row() as f32
             + 1.0;
 
@@ -575,7 +575,7 @@ impl Editor {
         let mut target_left = std::f32::INFINITY;
         let mut target_right = 0.0_f32;
         for selection in self.selections(cx) {
-            let head = selection.head().to_display_point(&display_map);
+            let head = selection.head().to_display_point(&display_map, Bias::Left);
             let start_column = head.column().saturating_sub(3);
             let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
             target_left = target_left
@@ -806,7 +806,7 @@ impl Editor {
             for selection in &mut selections {
                 let range = selection.point_range(buffer);
                 if range.start == range.end {
-                    let head = selection.head().to_display_point(&display_map);
+                    let head = selection.head().to_display_point(&display_map, Bias::Left);
                     let cursor = display_map
                         .anchor_before(movement::left(&display_map, head).unwrap(), Bias::Left);
                     selection.set_head(&buffer, cursor);
@@ -829,7 +829,7 @@ impl Editor {
             for selection in &mut selections {
                 let range = selection.point_range(buffer);
                 if range.start == range.end {
-                    let head = selection.head().to_display_point(&display_map);
+                    let head = selection.head().to_display_point(&display_map, Bias::Left);
                     let cursor = display_map
                         .anchor_before(movement::right(&display_map, head).unwrap(), Bias::Right);
                     selection.set_head(&buffer, cursor);
@@ -856,7 +856,10 @@ impl Editor {
         let mut selections = self.selections(app).iter().peekable();
         while let Some(selection) = selections.next() {
             let (mut rows, _) = selection.buffer_rows_for_display_rows(false, &display_map);
-            let goal_display_column = selection.head().to_display_point(&display_map).column();
+            let goal_display_column = selection
+                .head()
+                .to_display_point(&display_map, Bias::Left)
+                .column();
 
             // Accumulate contiguous regions of rows that we want to delete.
             while let Some(next_selection) = selections.peek() {
@@ -886,7 +889,8 @@ impl Editor {
                 cursor_buffer_row = rows.start.saturating_sub(1);
             }
 
-            let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
+            let mut cursor =
+                Point::new(cursor_buffer_row, 0).to_display_point(&display_map, Bias::Left);
             *cursor.column_mut() =
                 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
 
@@ -1286,8 +1290,8 @@ impl Editor {
         let mut selections = self.selections(app).to_vec();
         {
             for selection in &mut selections {
-                let start = selection.start.to_display_point(&display_map);
-                let end = selection.end.to_display_point(&display_map);
+                let start = selection.start.to_display_point(&display_map, Bias::Left);
+                let end = selection.end.to_display_point(&display_map, Bias::Left);
 
                 if start != end {
                     selection.end = selection.start.clone();
@@ -1310,7 +1314,7 @@ impl Editor {
         {
             let buffer = self.buffer.read(cx);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let cursor = display_map
                     .anchor_before(movement::left(&display_map, head).unwrap(), Bias::Left);
                 selection.set_head(&buffer, cursor);
@@ -1325,8 +1329,8 @@ impl Editor {
         let mut selections = self.selections(cx.as_ref()).to_vec();
         {
             for selection in &mut selections {
-                let start = selection.start.to_display_point(&display_map);
-                let end = selection.end.to_display_point(&display_map);
+                let start = selection.start.to_display_point(&display_map, Bias::Left);
+                let end = selection.end.to_display_point(&display_map, Bias::Left);
 
                 if start != end {
                     selection.start = selection.end.clone();
@@ -1350,7 +1354,7 @@ impl Editor {
             let app = cx.as_ref();
             let buffer = self.buffer.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let cursor = display_map
                     .anchor_before(movement::right(&display_map, head).unwrap(), Bias::Right);
                 selection.set_head(&buffer, cursor);
@@ -1368,8 +1372,8 @@ impl Editor {
             let mut selections = self.selections(cx.as_ref()).to_vec();
             {
                 for selection in &mut selections {
-                    let start = selection.start.to_display_point(&display_map);
-                    let end = selection.end.to_display_point(&display_map);
+                    let start = selection.start.to_display_point(&display_map, Bias::Left);
+                    let end = selection.end.to_display_point(&display_map, Bias::Left);
                     if start != end {
                         selection.goal = SelectionGoal::None;
                     }
@@ -1393,7 +1397,7 @@ impl Editor {
             let app = cx.as_ref();
             let buffer = self.buffer.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let (head, goal) = movement::up(&display_map, head, selection.goal).unwrap();
                 selection.set_head(&buffer, display_map.anchor_before(head, Bias::Left));
                 selection.goal = goal;
@@ -1410,8 +1414,8 @@ impl Editor {
             let mut selections = self.selections(cx.as_ref()).to_vec();
             {
                 for selection in &mut selections {
-                    let start = selection.start.to_display_point(&display_map);
-                    let end = selection.end.to_display_point(&display_map);
+                    let start = selection.start.to_display_point(&display_map, Bias::Left);
+                    let end = selection.end.to_display_point(&display_map, Bias::Left);
                     if start != end {
                         selection.goal = SelectionGoal::None;
                     }
@@ -1435,7 +1439,7 @@ impl Editor {
             let app = cx.as_ref();
             let buffer = self.buffer.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let (head, goal) = movement::down(&display_map, head, selection.goal).unwrap();
                 selection.set_head(&buffer, display_map.anchor_before(head, Bias::Right));
                 selection.goal = goal;
@@ -1449,7 +1453,7 @@ impl Editor {
         let mut selections = self.selections(cx).to_vec();
         {
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head = movement::prev_word_boundary(&display_map, head).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
                 selection.start = anchor.clone();
@@ -1467,7 +1471,7 @@ impl Editor {
         {
             let buffer = self.buffer.read(cx);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head = movement::prev_word_boundary(&display_map, head).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
                 selection.set_head(buffer, anchor);
@@ -1489,7 +1493,7 @@ impl Editor {
         let mut selections = self.selections(cx).to_vec();
         {
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head = movement::next_word_boundary(&display_map, head).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
                 selection.start = anchor.clone();
@@ -1507,7 +1511,7 @@ impl Editor {
         {
             let buffer = self.buffer.read(cx);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head = movement::next_word_boundary(&display_map, head).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
                 selection.set_head(buffer, anchor);
@@ -1529,7 +1533,7 @@ impl Editor {
         let mut selections = self.selections(cx).to_vec();
         {
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head = movement::line_beginning(&display_map, head, true).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
                 selection.start = anchor.clone();
@@ -1551,7 +1555,7 @@ impl Editor {
         {
             let buffer = self.buffer.read(cx);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head =
                     movement::line_beginning(&display_map, head, *toggle_indent).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
@@ -1574,7 +1578,7 @@ impl Editor {
         let mut selections = self.selections(cx).to_vec();
         {
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head = movement::line_end(&display_map, head).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
                 selection.start = anchor.clone();
@@ -1592,7 +1596,7 @@ impl Editor {
         {
             let buffer = self.buffer.read(cx);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(&display_map);
+                let head = selection.head().to_display_point(&display_map, Bias::Left);
                 let new_head = movement::line_end(&display_map, head).unwrap();
                 let anchor = display_map.anchor_before(new_head, Bias::Left);
                 selection.set_head(buffer, anchor);
@@ -2480,7 +2484,9 @@ fn compute_scroll_position(
     mut scroll_position: Vector2F,
     scroll_top_anchor: &Anchor,
 ) -> Vector2F {
-    let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
+    let scroll_top = scroll_top_anchor
+        .to_display_point(snapshot, Bias::Left)
+        .row() as f32;
     scroll_position.set_y(scroll_top + scroll_position.y());
     scroll_position
 }

zed/src/editor/buffer/selection.rs 🔗

@@ -80,8 +80,8 @@ impl Selection {
     }
 
     pub fn display_range(&self, map: &DisplayMapSnapshot) -> Range<DisplayPoint> {
-        let start = self.start.to_display_point(map);
-        let end = self.end.to_display_point(map);
+        let start = self.start.to_display_point(map, Bias::Left);
+        let end = self.end.to_display_point(map, Bias::Left);
         if self.reversed {
             end..start
         } else {
@@ -94,11 +94,11 @@ impl Selection {
         include_end_if_at_line_start: bool,
         map: &DisplayMapSnapshot,
     ) -> (Range<u32>, Range<u32>) {
-        let display_start = self.start.to_display_point(map);
+        let display_start = self.start.to_display_point(map, Bias::Left);
         let buffer_start =
             DisplayPoint::new(display_start.row(), 0).to_buffer_point(map, Bias::Left);
 
-        let mut display_end = self.end.to_display_point(map);
+        let mut display_end = self.end.to_display_point(map, Bias::Right);
         if !include_end_if_at_line_start
             && display_end.row() != map.max_point().row()
             && display_start.row() != display_end.row()

zed/src/editor/display_map.rs 🔗

@@ -117,24 +117,25 @@ impl DisplayMapSnapshot {
         let mut point = display_point.to_buffer_point(self, Bias::Left);
         while point.column != 0 {
             point.column = 0;
-            display_point = point.to_display_point(self);
+            display_point = point.to_display_point(self, Bias::Left);
             if display_point.column() != 0 {
                 *display_point.column_mut() = 0;
-                point = display_point.to_buffer_point(self, Bias::Left);
             }
+            point = display_point.to_buffer_point(self, Bias::Left);
         }
 
         (display_point, point)
     }
 
     pub fn next_row_boundary(&self, mut display_point: DisplayPoint) -> (DisplayPoint, Point) {
+        let max_point = self.max_point();
         *display_point.row_mut() += 1;
         *display_point.column_mut() = 0;
         let mut point = display_point.to_buffer_point(self, Bias::Right);
-        while point.column != 0 {
+        while point.column != 0 && display_point <= max_point {
             point.column = 0;
             point.row += 1;
-            display_point = point.to_display_point(self);
+            display_point = point.to_display_point(self, Bias::Right);
             if display_point.column() != 0 {
                 *display_point.row_mut() += 1;
                 *display_point.column_mut() = 0;
@@ -142,7 +143,7 @@ impl DisplayMapSnapshot {
             }
         }
 
-        (display_point, point)
+        (display_point.min(max_point), point)
     }
 
     pub fn max_point(&self) -> DisplayPoint {
@@ -307,8 +308,8 @@ impl DisplayPoint {
 }
 
 impl Point {
-    pub fn to_display_point(self, map: &DisplayMapSnapshot) -> DisplayPoint {
-        let fold_point = self.to_fold_point(&map.folds_snapshot);
+    pub fn to_display_point(self, map: &DisplayMapSnapshot, bias: Bias) -> DisplayPoint {
+        let fold_point = self.to_fold_point(&map.folds_snapshot, bias);
         let tab_point = map.tabs_snapshot.to_tab_point(fold_point);
         let wrap_point = map.wraps_snapshot.to_wrap_point(tab_point);
         DisplayPoint(wrap_point)
@@ -316,8 +317,9 @@ impl Point {
 }
 
 impl Anchor {
-    pub fn to_display_point(&self, map: &DisplayMapSnapshot) -> DisplayPoint {
-        self.to_point(&map.buffer_snapshot).to_display_point(map)
+    pub fn to_display_point(&self, map: &DisplayMapSnapshot, bias: Bias) -> DisplayPoint {
+        self.to_point(&map.buffer_snapshot)
+            .to_display_point(map, bias)
     }
 }
 
@@ -435,13 +437,13 @@ mod tests {
                 }
 
                 assert_eq!(
-                    prev_buffer_bound.to_display_point(&snapshot),
+                    prev_buffer_bound.to_display_point(&snapshot, Left),
                     prev_display_bound,
                     "{:?} to display point",
                     prev_buffer_bound
                 );
                 assert_eq!(
-                    next_buffer_bound.to_display_point(&snapshot),
+                    next_buffer_bound.to_display_point(&snapshot, Left),
                     next_display_bound,
                     "{:?} to display point",
                     next_buffer_bound
@@ -787,40 +789,40 @@ mod tests {
 
         let point = Point::new(0, "✅\t\t".len() as u32);
         let display_point = DisplayPoint::new(0, "✅       ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_buffer_point(&map, Bias::Left), point,);
+        assert_eq!(point.to_display_point(&map, Left), display_point);
+        assert_eq!(display_point.to_buffer_point(&map, Left), point,);
 
         let point = Point::new(1, "β\t".len() as u32);
         let display_point = DisplayPoint::new(1, "β   ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_buffer_point(&map, Bias::Left), point,);
+        assert_eq!(point.to_display_point(&map, Left), display_point);
+        assert_eq!(display_point.to_buffer_point(&map, Left), point,);
 
         let point = Point::new(2, "🏀β\t\t".len() as u32);
         let display_point = DisplayPoint::new(2, "🏀β      ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_buffer_point(&map, Bias::Left), point,);
+        assert_eq!(point.to_display_point(&map, Left), display_point);
+        assert_eq!(display_point.to_buffer_point(&map, Left), point,);
 
         // Display points inside of expanded tabs
         assert_eq!(
-            DisplayPoint::new(0, "✅      ".len() as u32).to_buffer_point(&map, Bias::Right),
+            DisplayPoint::new(0, "✅      ".len() as u32).to_buffer_point(&map, Right),
             Point::new(0, "✅\t\t".len() as u32),
         );
         assert_eq!(
-            DisplayPoint::new(0, "✅      ".len() as u32).to_buffer_point(&map, Bias::Left),
+            DisplayPoint::new(0, "✅      ".len() as u32).to_buffer_point(&map, Left),
             Point::new(0, "✅\t".len() as u32),
         );
         assert_eq!(
-            DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Right),
+            DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Right),
             Point::new(0, "✅\t".len() as u32),
         );
         assert_eq!(
-            DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Bias::Left),
+            DisplayPoint::new(0, "✅ ".len() as u32).to_buffer_point(&map, Left),
             Point::new(0, "✅".len() as u32),
         );
 
         // Clipping display points inside of multi-byte characters
         assert_eq!(
-            map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Left),
+            map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Left),
             DisplayPoint::new(0, 0)
         );
         assert_eq!(

zed/src/editor/display_map/fold_map.rs 🔗

@@ -77,14 +77,21 @@ impl FoldPoint {
 }
 
 impl Point {
-    pub fn to_fold_point(&self, snapshot: &Snapshot) -> FoldPoint {
+    pub fn to_fold_point(&self, snapshot: &Snapshot, bias: Bias) -> FoldPoint {
         let mut cursor = snapshot.transforms.cursor::<Point, FoldPoint>();
         cursor.seek(self, Bias::Right, &());
-        let overshoot = *self - cursor.seek_start();
-        FoldPoint(cmp::min(
-            cursor.sum_start().0 + overshoot,
-            cursor.sum_end(&()).0,
-        ))
+        if cursor.item().map_or(false, |t| t.is_fold()) {
+            match bias {
+                Bias::Left => *cursor.sum_start(),
+                Bias::Right => cursor.sum_end(&()),
+            }
+        } else {
+            let overshoot = *self - cursor.seek_start();
+            FoldPoint(cmp::min(
+                cursor.sum_start().0 + overshoot,
+                cursor.sum_end(&()).0,
+            ))
+        }
     }
 }
 
@@ -1365,7 +1372,7 @@ mod tests {
                 let buffer_point = fold_point.to_buffer_point(&snapshot);
                 let buffer_offset = buffer_point.to_offset(&buffer);
                 assert_eq!(
-                    buffer_point.to_fold_point(&snapshot),
+                    buffer_point.to_fold_point(&snapshot, Right),
                     fold_point,
                     "buffer_Point.to_fold_point({:?})",
                     buffer_point,
@@ -1413,7 +1420,9 @@ mod tests {
             }
 
             for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
-                let fold_row = Point::new(*buffer_row, 0).to_fold_point(&snapshot).row();
+                let fold_row = Point::new(*buffer_row, 0)
+                    .to_fold_point(&snapshot, Right)
+                    .row();
                 assert_eq!(
                     snapshot.buffer_rows(fold_row).collect::<Vec<_>>(),
                     expected_buffer_rows[idx..],
@@ -1421,7 +1430,10 @@ mod tests {
             }
 
             for fold_range in map.merged_fold_ranges(cx.as_ref()) {
-                let fold_point = fold_range.start.to_point(&buffer).to_fold_point(&snapshot);
+                let fold_point = fold_range
+                    .start
+                    .to_point(&buffer)
+                    .to_fold_point(&snapshot, Right);
                 assert!(snapshot.is_line_folded(fold_point.row()));
             }