WIP

Antonio Scandurra created

Change summary

zed/src/editor/buffer_view.rs          |  75 ++++++++++++--
zed/src/editor/display_map/fold_map.rs | 142 ++++++++++++++++++---------
zed/src/editor/display_map/mod.rs      |   7 +
3 files changed, 162 insertions(+), 62 deletions(-)

Detailed changes

zed/src/editor/buffer_view.rs 🔗

@@ -784,6 +784,9 @@ impl BufferView {
 
         let mut edits = Vec::new();
         let mut new_selection_ranges = Vec::new();
+        let mut old_folds = Vec::new();
+        let mut new_folds = Vec::new();
+
         let mut selections = self.selections(app).iter().peekable();
         let mut contiguous_selections = Vec::new();
         while let Some(selection) = selections.next() {
@@ -830,25 +833,42 @@ impl BufferView {
                 edits.push((prev_row_start..prev_row_start, text));
                 edits.push((selection_row_start - 1..selection_row_end, String::new()));
 
-                // Move selections up.
                 let row_delta = buffer_rows.start
                     - prev_row_display_start
                         .to_buffer_point(map, Bias::Left, app)
                         .unwrap()
                         .row;
+
+                // Move selections up.
                 for range in &mut contiguous_selections {
                     range.start.row -= row_delta;
                     range.end.row -= row_delta;
                 }
+
+                // Move folds up.
+                old_folds.push(selection_row_start..selection_row_end);
+                for fold in map
+                    .folds_in_range(selection_row_start..selection_row_end, app)
+                    .unwrap()
+                {
+                    let mut start = fold.start.to_point(buffer).unwrap();
+                    let mut end = fold.end.to_point(buffer).unwrap();
+                    start.row -= row_delta;
+                    end.row -= row_delta;
+                    new_folds.push(start..end);
+                }
             }
+
             new_selection_ranges.extend(contiguous_selections.drain(..));
         }
 
+        self.unfold_ranges(old_folds, ctx);
         self.buffer.update(ctx, |buffer, ctx| {
             for (range, text) in edits.into_iter().rev() {
                 buffer.edit(Some(range), text, Some(ctx)).unwrap();
             }
         });
+        self.fold_ranges(new_folds, ctx);
         self.select_ranges(new_selection_ranges, true, ctx);
 
         self.end_transaction(ctx);
@@ -863,6 +883,9 @@ impl BufferView {
 
         let mut edits = Vec::new();
         let mut new_selection_ranges = Vec::new();
+        let mut old_folds = Vec::new();
+        let mut new_folds = Vec::new();
+
         let mut selections = self.selections(app).iter().peekable();
         let mut contiguous_selections = Vec::new();
         while let Some(selection) = selections.next() {
@@ -912,26 +935,43 @@ impl BufferView {
                 edits.push((selection_row_start..selection_row_end + 1, String::new()));
                 edits.push((next_row_end..next_row_end, text));
 
-                // Move selections down.
                 let row_delta = next_row_display_end
                     .to_buffer_point(map, Bias::Right, app)
                     .unwrap()
                     .row
                     - buffer_rows.end
                     + 1;
+
+                // Move selections down.
                 for range in &mut contiguous_selections {
                     range.start.row += row_delta;
                     range.end.row += row_delta;
                 }
+
+                // Move folds down.
+                old_folds.push(selection_row_start..selection_row_end);
+                for fold in map
+                    .folds_in_range(selection_row_start..selection_row_end, app)
+                    .unwrap()
+                {
+                    let mut start = fold.start.to_point(buffer).unwrap();
+                    let mut end = fold.end.to_point(buffer).unwrap();
+                    start.row += row_delta;
+                    end.row += row_delta;
+                    new_folds.push(start..end);
+                }
             }
+
             new_selection_ranges.extend(contiguous_selections.drain(..));
         }
 
+        self.unfold_ranges(old_folds, ctx);
         self.buffer.update(ctx, |buffer, ctx| {
             for (range, text) in edits.into_iter().rev() {
                 buffer.edit(Some(range), text, Some(ctx)).unwrap();
             }
         });
+        self.fold_ranges(new_folds, ctx);
         self.select_ranges(new_selection_ranges, true, ctx);
 
         self.end_transaction(ctx);
@@ -1533,12 +1573,7 @@ impl BufferView {
             }
         }
 
-        if !fold_ranges.is_empty() {
-            self.display_map.update(ctx, |map, ctx| {
-                map.fold(fold_ranges, ctx).unwrap();
-            });
-            *self.autoscroll_requested.lock() = true;
-        }
+        self.fold_ranges(fold_ranges, ctx);
     }
 
     pub fn unfold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
@@ -1559,11 +1594,7 @@ impl BufferView {
                 start..end
             })
             .collect::<Vec<_>>();
-
-        self.display_map.update(ctx, |map, ctx| {
-            map.unfold(ranges, ctx).unwrap();
-        });
-        *self.autoscroll_requested.lock() = true;
+        self.unfold_ranges(ranges, ctx);
     }
 
     fn is_line_foldable(&self, display_row: u32, app: &AppContext) -> bool {
@@ -1621,6 +1652,24 @@ impl BufferView {
         });
     }
 
+    fn fold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
+        if !ranges.is_empty() {
+            self.display_map.update(ctx, |map, ctx| {
+                map.fold(ranges, ctx).unwrap();
+            });
+            *self.autoscroll_requested.lock() = true;
+        }
+    }
+
+    fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
+        if !ranges.is_empty() {
+            self.display_map.update(ctx, |map, ctx| {
+                map.unfold(ranges, ctx).unwrap();
+            });
+            *self.autoscroll_requested.lock() = true;
+        }
+    }
+
     pub fn line(&self, display_row: u32, app: &AppContext) -> Result<String> {
         self.display_map.read(app).line(display_row, app)
     }

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

@@ -89,6 +89,33 @@ impl FoldMap {
         DisplayPoint(self.transforms.summary().display.rightmost_point)
     }
 
+    pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
+    where
+        T: ToOffset,
+    {
+        let buffer = self.buffer.read(app);
+        let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?;
+        let mut start_ix = find_insertion_index(&self.folds, |probe| probe.cmp(&range, buffer))?;
+        let mut end_ix = start_ix;
+
+        for fold in self.folds[..start_ix].iter().rev() {
+            if fold.end.cmp(&range.start, buffer)? == Ordering::Greater {
+                start_ix -= 1;
+            } else {
+                break;
+            }
+        }
+        for fold in &self.folds[end_ix..] {
+            if range.end.cmp(&fold.start, buffer)? == Ordering::Greater {
+                end_ix += 1;
+            } else {
+                break;
+            }
+        }
+
+        Ok(&self.folds[start_ix..end_ix])
+    }
+
     pub fn fold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
@@ -422,7 +449,7 @@ impl<'a> Iterator for Chars<'a> {
             return Some(c);
         }
 
-        if self.offset == self.cursor.end().display.chars {
+        while self.offset == self.cursor.end().display.chars && self.cursor.item().is_some() {
             self.cursor.next();
         }
 
@@ -584,6 +611,9 @@ mod tests {
         let iterations = env::var("ITERATIONS")
             .map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
             .unwrap_or(100);
+        let operations = env::var("OPERATIONS")
+            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+            .unwrap_or(10);
         let seed_range = if let Ok(seed) = env::var("SEED") {
             let seed = seed.parse().expect("invalid `SEED` variable");
             seed..seed + 1
@@ -591,6 +621,8 @@ mod tests {
             0..iterations
         };
 
+        let operations = 2;
+        let seed_range = 133..=133;
         for seed in seed_range {
             println!("{:?}", seed);
             let mut rng = StdRng::seed_from_u64(seed);
@@ -603,66 +635,78 @@ mod tests {
                 });
                 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 
-                {
-                    let buffer = buffer.read(app);
-
-                    let fold_count = rng.gen_range(0..10);
-                    let mut fold_ranges: Vec<Range<usize>> = Vec::new();
-                    for _ in 0..fold_count {
-                        let end = rng.gen_range(0..buffer.len() + 1);
-                        let start = rng.gen_range(0..end + 1);
-                        fold_ranges.push(start..end);
+                for op_ix in 0..operations {
+                    dbg!(op_ix);
+
+                    log::info!("Text: {:?}", buffer.read(app).text());
+                    {
+                        let buffer = buffer.read(app);
+
+                        let fold_count = rng.gen_range(0..=2);
+                        let mut fold_ranges: Vec<Range<usize>> = Vec::new();
+                        for _ in 0..fold_count {
+                            let end = rng.gen_range(0..buffer.len() + 1);
+                            let start = rng.gen_range(0..end + 1);
+                            fold_ranges.push(start..end);
+                        }
+                        log::info!("Folding {:?}", fold_ranges);
+                        if op_ix == 1 {
+                            dbg!("stopping");
+                        }
+                        map.fold(fold_ranges.clone(), app.as_ref()).unwrap();
+                        assert_eq!(map.transforms.summary().buffer.chars, buffer.len());
+
+                        let mut expected_text = buffer.text();
+                        for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
+                            expected_text.replace_range(fold_range.start..fold_range.end, "…");
+                        }
+                        assert_eq!(map.text(app.as_ref()), expected_text);
+
+                        for fold_range in map.merged_fold_ranges(app.as_ref()) {
+                            let display_point =
+                                map.to_display_point(fold_range.start.to_point(buffer).unwrap());
+                            assert!(map.is_line_folded(display_point.row()));
+                        }
                     }
 
-                    map.fold(fold_ranges, app.as_ref()).unwrap();
+                    let edits = buffer.update(app, |buffer, ctx| {
+                        let start_version = buffer.version.clone();
+                        let edit_count = rng.gen_range(0..=2);
+                        buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
+                        buffer.edits_since(start_version).collect::<Vec<_>>()
+                    });
+                    log::info!("Editing {:?}", edits);
+                    map.apply_edits(&edits, app.as_ref()).unwrap();
+                    assert_eq!(
+                        map.transforms.summary().buffer.chars,
+                        buffer.read(app).len()
+                    );
 
+                    let buffer = map.buffer.read(app);
                     let mut expected_text = buffer.text();
+                    let mut expected_buffer_rows = Vec::new();
+                    let mut next_row = buffer.max_point().row;
                     for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
+                        let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
+                        let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
+                        expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
+                        next_row = fold_start.row;
+
                         expected_text.replace_range(fold_range.start..fold_range.end, "…");
                     }
+                    expected_buffer_rows.extend((0..=next_row).rev());
+                    expected_buffer_rows.reverse();
 
                     assert_eq!(map.text(app.as_ref()), expected_text);
 
-                    for fold_range in map.merged_fold_ranges(app.as_ref()) {
-                        let display_point =
-                            map.to_display_point(fold_range.start.to_point(buffer).unwrap());
-                        assert!(map.is_line_folded(display_point.row()));
+                    for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
+                        let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
+                        assert_eq!(
+                            map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
+                            expected_buffer_rows[idx..],
+                        );
                     }
                 }
-
-                let edits = buffer.update(app, |buffer, ctx| {
-                    let start_version = buffer.version.clone();
-                    let edit_count = rng.gen_range(1..10);
-                    buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
-                    buffer.edits_since(start_version).collect::<Vec<_>>()
-                });
-
-                map.apply_edits(&edits, app.as_ref()).unwrap();
-
-                let buffer = map.buffer.read(app);
-                let mut expected_text = buffer.text();
-                let mut expected_buffer_rows = Vec::new();
-                let mut next_row = buffer.max_point().row;
-                for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
-                    let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
-                    let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
-                    expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
-                    next_row = fold_start.row;
-
-                    expected_text.replace_range(fold_range.start..fold_range.end, "…");
-                }
-                expected_buffer_rows.extend((0..=next_row).rev());
-                expected_buffer_rows.reverse();
-
-                assert_eq!(map.text(app.as_ref()), expected_text);
-
-                for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
-                    let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
-                    assert_eq!(
-                        map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
-                        expected_buffer_rows[idx..],
-                    );
-                }
             });
         }
     }

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

@@ -34,6 +34,13 @@ impl DisplayMap {
         }
     }
 
+    pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
+    where
+        T: ToOffset,
+    {
+        self.fold_map.folds_in_range(range, app)
+    }
+
     pub fn fold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,