Merge branch 'master' into new-file

Max Brunsfeld created

Change summary

zed/src/editor/buffer/mod.rs           |  84 ---
zed/src/editor/buffer_view.rs          | 495 ++++++++++++++++-----------
zed/src/editor/display_map/fold_map.rs | 410 ++++++++++++++--------
zed/src/editor/display_map/mod.rs      | 206 ++++++-----
zed/src/editor/movement.rs             |   4 
5 files changed, 688 insertions(+), 511 deletions(-)

Detailed changes

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

@@ -678,9 +678,8 @@ impl Buffer {
             if let Some(ctx) = ctx {
                 ctx.notify();
 
-                let changes = self.edits_since(since).collect::<Vec<_>>();
-                if !changes.is_empty() {
-                    self.did_edit(changes, was_dirty, ctx);
+                if self.edits_since(since).next().is_some() {
+                    self.did_edit(was_dirty, ctx);
                 }
             }
         }
@@ -741,8 +740,8 @@ impl Buffer {
         Ok(ops)
     }
 
-    fn did_edit(&self, changes: Vec<Edit>, was_dirty: bool, ctx: &mut ModelContext<Self>) {
-        ctx.emit(Event::Edited(changes));
+    fn did_edit(&self, was_dirty: bool, ctx: &mut ModelContext<Self>) {
+        ctx.emit(Event::Edited);
         if !was_dirty {
             ctx.emit(Event::Dirtied);
         }
@@ -891,9 +890,8 @@ impl Buffer {
 
         if let Some(ctx) = ctx {
             ctx.notify();
-            let changes = self.edits_since(old_version).collect::<Vec<_>>();
-            if !changes.is_empty() {
-                self.did_edit(changes, was_dirty, ctx);
+            if self.edits_since(old_version).next().is_some() {
+                self.did_edit(was_dirty, ctx);
             }
         }
 
@@ -1085,9 +1083,8 @@ impl Buffer {
 
         if let Some(ctx) = ctx {
             ctx.notify();
-            let changes = self.edits_since(old_version).collect::<Vec<_>>();
-            if !changes.is_empty() {
-                self.did_edit(changes, was_dirty, ctx);
+            if self.edits_since(old_version).next().is_some() {
+                self.did_edit(was_dirty, ctx);
             }
         }
 
@@ -1112,9 +1109,8 @@ impl Buffer {
 
         if let Some(ctx) = ctx {
             ctx.notify();
-            let changes = self.edits_since(old_version).collect::<Vec<_>>();
-            if !changes.is_empty() {
-                self.did_edit(changes, was_dirty, ctx);
+            if self.edits_since(old_version).next().is_some() {
+                self.did_edit(was_dirty, ctx);
             }
         }
 
@@ -1793,7 +1789,7 @@ impl Snapshot {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Event {
-    Edited(Vec<Edit>),
+    Edited,
     Dirtied,
     Saved,
     FileHandleChanged,
@@ -2402,34 +2398,11 @@ mod tests {
             let buffer_1_events = buffer_1_events.borrow();
             assert_eq!(
                 *buffer_1_events,
-                vec![
-                    Event::Edited(vec![Edit {
-                        old_range: 2..4,
-                        new_range: 2..5
-                    }]),
-                    Event::Dirtied,
-                    Event::Edited(vec![Edit {
-                        old_range: 5..5,
-                        new_range: 5..7
-                    }]),
-                    Event::Edited(vec![Edit {
-                        old_range: 5..7,
-                        new_range: 5..5
-                    }]),
-                ]
+                vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited]
             );
 
             let buffer_2_events = buffer_2_events.borrow();
-            assert_eq!(
-                *buffer_2_events,
-                vec![
-                    Event::Edited(vec![Edit {
-                        old_range: 2..4,
-                        new_range: 2..5
-                    },]),
-                    Event::Dirtied
-                ]
-            );
+            assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]);
         });
     }
 
@@ -2969,16 +2942,7 @@ mod tests {
             model.update(app, |buffer, ctx| {
                 assert!(buffer.text() == "ac");
                 assert!(buffer.is_dirty());
-                assert_eq!(
-                    *events.borrow(),
-                    &[
-                        Event::Edited(vec![Edit {
-                            old_range: 1..2,
-                            new_range: 1..1
-                        }]),
-                        Event::Dirtied
-                    ]
-                );
+                assert_eq!(*events.borrow(), &[Event::Edited, Event::Dirtied]);
                 events.borrow_mut().clear();
 
                 buffer.did_save(buffer.version(), None, ctx);
@@ -3000,17 +2964,7 @@ mod tests {
                 assert!(buffer.is_dirty());
                 assert_eq!(
                     *events.borrow(),
-                    &[
-                        Event::Edited(vec![Edit {
-                            old_range: 1..1,
-                            new_range: 1..2
-                        }]),
-                        Event::Dirtied,
-                        Event::Edited(vec![Edit {
-                            old_range: 2..2,
-                            new_range: 2..3
-                        }]),
-                    ],
+                    &[Event::Edited, Event::Dirtied, Event::Edited],
                 );
                 events.borrow_mut().clear();
 
@@ -3022,13 +2976,7 @@ mod tests {
             });
 
             model.update(app, |_, _| {
-                assert_eq!(
-                    *events.borrow(),
-                    &[Event::Edited(vec![Edit {
-                        old_range: 1..3,
-                        new_range: 1..1
-                    },])]
-                );
+                assert_eq!(*events.borrow(), &[Event::Edited]);
             });
         });
     }

zed/src/editor/buffer_view.rs 🔗

@@ -253,7 +253,7 @@ pub enum SelectAction {
 pub struct BufferView {
     handle: WeakViewHandle<Self>,
     buffer: ModelHandle<Buffer>,
-    display_map: ModelHandle<DisplayMap>,
+    display_map: DisplayMap,
     selection_set_id: SelectionSetId,
     pending_selection: Option<Selection>,
     scroll_position: Mutex<Vector2F>,
@@ -289,14 +289,11 @@ impl BufferView {
 
         ctx.observe_model(&buffer, Self::on_buffer_changed);
         ctx.subscribe_to_model(&buffer, Self::on_buffer_event);
-        let display_map = ctx.add_model(|ctx| {
-            DisplayMap::new(
-                buffer.clone(),
-                smol::block_on(settings.read()).tab_size,
-                ctx,
-            )
-        });
-        ctx.observe_model(&display_map, Self::on_display_map_changed);
+        let display_map = DisplayMap::new(
+            buffer.clone(),
+            smol::block_on(settings.read()).tab_size,
+            ctx.as_ref(),
+        );
 
         let (selection_set_id, _) = buffer.update(ctx, |buffer, ctx| {
             buffer.add_selection_set(
@@ -366,14 +363,13 @@ impl BufferView {
             return false;
         }
 
-        let map = self.display_map.read(app);
         let visible_lines = viewport_height / line_height;
         let first_cursor_top = self
             .selections(app)
             .first()
             .unwrap()
             .head()
-            .to_display_point(map, app)
+            .to_display_point(&self.display_map, app)
             .unwrap()
             .row() as f32;
         let last_cursor_bottom = self
@@ -381,7 +377,7 @@ impl BufferView {
             .last()
             .unwrap()
             .head()
-            .to_display_point(map, app)
+            .to_display_point(&self.display_map, app)
             .unwrap()
             .row() as f32
             + 1.0;
@@ -414,16 +410,20 @@ impl BufferView {
         scroll_width: f32,
         max_glyph_width: f32,
         layouts: &[Arc<text_layout::Line>],
-        app: &AppContext,
+        ctx: &AppContext,
     ) {
-        let map = self.display_map.read(app);
-
         let mut target_left = std::f32::INFINITY;
         let mut target_right = 0.0_f32;
-        for selection in self.selections(app) {
-            let head = selection.head().to_display_point(map, app).unwrap();
+        for selection in self.selections(ctx) {
+            let head = selection
+                .head()
+                .to_display_point(&self.display_map, ctx)
+                .unwrap();
             let start_column = head.column().saturating_sub(3);
-            let end_column = cmp::min(map.line_len(head.row(), app).unwrap(), head.column() + 3);
+            let end_column = cmp::min(
+                self.display_map.line_len(head.row(), ctx).unwrap(),
+                head.column() + 3,
+            );
             target_left = target_left
                 .min(layouts[(head.row() - start_row) as usize].x_for_index(start_column as usize));
             target_right = target_right.max(
@@ -465,8 +465,8 @@ impl BufferView {
             ctx.emit(Event::Activate);
         }
 
-        let display_map = self.display_map.read(ctx);
-        let cursor = display_map
+        let cursor = self
+            .display_map
             .anchor_before(position, Bias::Left, ctx.as_ref())
             .unwrap();
         let selection = Selection {
@@ -491,8 +491,8 @@ impl BufferView {
         ctx: &mut ViewContext<Self>,
     ) {
         let buffer = self.buffer.read(ctx);
-        let map = self.display_map.read(ctx);
-        let cursor = map
+        let cursor = self
+            .display_map
             .anchor_before(position, Bias::Left, ctx.as_ref())
             .unwrap();
         if let Some(selection) = self.pending_selection.as_mut() {
@@ -553,7 +553,6 @@ impl BufferView {
     where
         T: IntoIterator<Item = &'a Range<DisplayPoint>>,
     {
-        let map = self.display_map.read(ctx);
         let mut selections = Vec::new();
         for range in ranges {
             let mut start = range.start;
@@ -566,8 +565,12 @@ impl BufferView {
             };
 
             selections.push(Selection {
-                start: map.anchor_before(start, Bias::Left, ctx.as_ref())?,
-                end: map.anchor_before(end, Bias::Left, ctx.as_ref())?,
+                start: self
+                    .display_map
+                    .anchor_before(start, Bias::Left, ctx.as_ref())?,
+                end: self
+                    .display_map
+                    .anchor_before(end, Bias::Left, ctx.as_ref())?,
                 reversed,
                 goal_column: None,
             });
@@ -633,17 +636,17 @@ impl BufferView {
         let mut selections = self.selections(ctx.as_ref()).to_vec();
         {
             let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(ctx);
             for selection in &mut selections {
                 let range = selection.range(buffer);
                 if range.start == range.end {
                     let head = selection
                         .head()
-                        .to_display_point(map, ctx.as_ref())
+                        .to_display_point(&self.display_map, ctx.as_ref())
                         .unwrap();
-                    let cursor = map
+                    let cursor = self
+                        .display_map
                         .anchor_before(
-                            movement::left(map, head, ctx.as_ref()).unwrap(),
+                            movement::left(&self.display_map, head, ctx.as_ref()).unwrap(),
                             Bias::Left,
                             ctx.as_ref(),
                         )
@@ -664,17 +667,17 @@ impl BufferView {
         let mut selections = self.selections(ctx.as_ref()).to_vec();
         {
             let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(ctx);
             for selection in &mut selections {
                 let range = selection.range(buffer);
                 if range.start == range.end {
                     let head = selection
                         .head()
-                        .to_display_point(map, ctx.as_ref())
+                        .to_display_point(&self.display_map, ctx.as_ref())
                         .unwrap();
-                    let cursor = map
+                    let cursor = self
+                        .display_map
                         .anchor_before(
-                            movement::right(map, head, ctx.as_ref()).unwrap(),
+                            movement::right(&self.display_map, head, ctx.as_ref()).unwrap(),
                             Bias::Right,
                             ctx.as_ref(),
                         )
@@ -694,7 +697,6 @@ impl BufferView {
         self.start_transaction(ctx);
 
         let app = ctx.as_ref();
-        let map = self.display_map.read(app);
         let buffer = self.buffer.read(app);
 
         let mut new_cursors = Vec::new();
@@ -702,16 +704,17 @@ impl BufferView {
 
         let mut selections = self.selections(app).iter().peekable();
         while let Some(selection) = selections.next() {
-            let (mut rows, _) = selection.buffer_rows_for_display_rows(map, app);
+            let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app);
             let goal_display_column = selection
                 .head()
-                .to_display_point(map, app)
+                .to_display_point(&self.display_map, app)
                 .unwrap()
                 .column();
 
             // Accumulate contiguous regions of rows that we want to delete.
             while let Some(next_selection) = selections.peek() {
-                let (next_rows, _) = next_selection.buffer_rows_for_display_rows(map, app);
+                let (next_rows, _) =
+                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
                 if next_rows.start <= rows.end {
                     rows.end = next_rows.end;
                     selections.next().unwrap();
@@ -737,14 +740,18 @@ impl BufferView {
             }
 
             let mut cursor = Point::new(cursor_buffer_row, 0)
-                .to_display_point(map, app)
+                .to_display_point(&self.display_map, app)
                 .unwrap();
             *cursor.column_mut() = cmp::min(
                 goal_display_column,
-                map.line_len(cursor.row(), app).unwrap(),
+                self.display_map.line_len(cursor.row(), app).unwrap(),
             );
 
-            new_cursors.push(cursor.to_buffer_point(map, Bias::Left, app).unwrap());
+            new_cursors.push(
+                cursor
+                    .to_buffer_point(&self.display_map, Bias::Left, app)
+                    .unwrap(),
+            );
             edit_ranges.push(edit_start..edit_end);
         }
 
@@ -783,15 +790,15 @@ impl BufferView {
 
         let app = ctx.as_ref();
         let buffer = self.buffer.read(ctx);
-        let map = self.display_map.read(ctx);
 
         let mut edits = Vec::new();
         let mut selections_iter = selections.iter_mut().peekable();
         while let Some(selection) = selections_iter.next() {
             // Avoid duplicating the same lines twice.
-            let (mut rows, _) = selection.buffer_rows_for_display_rows(map, app);
+            let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app);
             while let Some(next_selection) = selections_iter.peek() {
-                let (next_rows, _) = next_selection.buffer_rows_for_display_rows(map, app);
+                let (next_rows, _) =
+                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
                 if next_rows.start <= rows.end - 1 {
                     rows.end = next_rows.end;
                     selections_iter.next().unwrap();
@@ -833,7 +840,6 @@ impl BufferView {
 
         let app = ctx.as_ref();
         let buffer = self.buffer.read(ctx);
-        let map = self.display_map.read(ctx);
 
         let mut edits = Vec::new();
         let mut new_selection_ranges = Vec::new();
@@ -846,10 +852,10 @@ impl BufferView {
             // Accumulate contiguous regions of rows that we want to move.
             contiguous_selections.push(selection.range(buffer));
             let (mut buffer_rows, mut display_rows) =
-                selection.buffer_rows_for_display_rows(map, app);
+                selection.buffer_rows_for_display_rows(&self.display_map, app);
             while let Some(next_selection) = selections.peek() {
                 let (next_buffer_rows, next_display_rows) =
-                    next_selection.buffer_rows_for_display_rows(map, app);
+                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
                 if next_buffer_rows.start <= buffer_rows.end {
                     buffer_rows.end = next_buffer_rows.end;
                     display_rows.end = next_display_rows.end;
@@ -862,9 +868,8 @@ impl BufferView {
 
             // Cut the text from the selected rows and paste it at the start of the previous line.
             if display_rows.start != 0 {
-                let selection_row_start =
-                    Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap();
-                let selection_row_end = Point::new(
+                let start = Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap();
+                let end = Point::new(
                     buffer_rows.end - 1,
                     buffer.line_len(buffer_rows.end - 1).unwrap(),
                 )
@@ -873,22 +878,18 @@ impl BufferView {
 
                 let prev_row_display_start = DisplayPoint::new(display_rows.start - 1, 0);
                 let prev_row_start = prev_row_display_start
-                    .to_buffer_offset(map, Bias::Left, app)
+                    .to_buffer_offset(&self.display_map, Bias::Left, app)
                     .unwrap();
 
                 let mut text = String::new();
-                text.extend(
-                    buffer
-                        .text_for_range(selection_row_start..selection_row_end)
-                        .unwrap(),
-                );
+                text.extend(buffer.text_for_range(start..end).unwrap());
                 text.push('\n');
                 edits.push((prev_row_start..prev_row_start, text));
-                edits.push((selection_row_start - 1..selection_row_end, String::new()));
+                edits.push((start - 1..end, String::new()));
 
                 let row_delta = buffer_rows.start
                     - prev_row_display_start
-                        .to_buffer_point(map, Bias::Left, app)
+                        .to_buffer_point(&self.display_map, Bias::Left, app)
                         .unwrap()
                         .row;
 
@@ -899,11 +900,8 @@ impl BufferView {
                 }
 
                 // 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()
-                {
+                old_folds.push(start..end);
+                for fold in self.display_map.folds_in_range(start..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;
@@ -932,7 +930,6 @@ impl BufferView {
 
         let app = ctx.as_ref();
         let buffer = self.buffer.read(ctx);
-        let map = self.display_map.read(ctx);
 
         let mut edits = Vec::new();
         let mut new_selection_ranges = Vec::new();
@@ -945,10 +942,10 @@ impl BufferView {
             // Accumulate contiguous regions of rows that we want to move.
             contiguous_selections.push(selection.range(buffer));
             let (mut buffer_rows, mut display_rows) =
-                selection.buffer_rows_for_display_rows(map, app);
+                selection.buffer_rows_for_display_rows(&self.display_map, app);
             while let Some(next_selection) = selections.peek() {
                 let (next_buffer_rows, next_display_rows) =
-                    next_selection.buffer_rows_for_display_rows(map, app);
+                    next_selection.buffer_rows_for_display_rows(&self.display_map, app);
                 if next_buffer_rows.start <= buffer_rows.end {
                     buffer_rows.end = next_buffer_rows.end;
                     display_rows.end = next_display_rows.end;
@@ -960,10 +957,9 @@ impl BufferView {
             }
 
             // Cut the text from the selected rows and paste it at the end of the next line.
-            if display_rows.end <= map.max_point(app).row() {
-                let selection_row_start =
-                    Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap();
-                let selection_row_end = Point::new(
+            if display_rows.end <= self.display_map.max_point(app).row() {
+                let start = Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap();
+                let end = Point::new(
                     buffer_rows.end - 1,
                     buffer.line_len(buffer_rows.end - 1).unwrap(),
                 )
@@ -972,24 +968,20 @@ impl BufferView {
 
                 let next_row_display_end = DisplayPoint::new(
                     display_rows.end,
-                    map.line_len(display_rows.end, app).unwrap(),
+                    self.display_map.line_len(display_rows.end, app).unwrap(),
                 );
                 let next_row_end = next_row_display_end
-                    .to_buffer_offset(map, Bias::Left, app)
+                    .to_buffer_offset(&self.display_map, Bias::Right, app)
                     .unwrap();
 
                 let mut text = String::new();
                 text.push('\n');
-                text.extend(
-                    buffer
-                        .text_for_range(selection_row_start..selection_row_end)
-                        .unwrap(),
-                );
-                edits.push((selection_row_start..selection_row_end + 1, String::new()));
+                text.extend(buffer.text_for_range(start..end).unwrap());
+                edits.push((start..end + 1, String::new()));
                 edits.push((next_row_end..next_row_end, text));
 
                 let row_delta = next_row_display_end
-                    .to_buffer_point(map, Bias::Right, app)
+                    .to_buffer_point(&self.display_map, Bias::Right, app)
                     .unwrap()
                     .row
                     - buffer_rows.end
@@ -1002,11 +994,8 @@ impl BufferView {
                 }
 
                 // 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()
-                {
+                old_folds.push(start..end);
+                for fold in self.display_map.folds_in_range(start..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;
@@ -1170,16 +1159,26 @@ impl BufferView {
         let app = ctx.as_ref();
         let mut selections = self.selections(app).to_vec();
         {
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let start = selection.start.to_display_point(map, app).unwrap();
-                let end = selection.end.to_display_point(map, app).unwrap();
+                let start = selection
+                    .start
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let end = selection
+                    .end
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
 
                 if start != end {
                     selection.end = selection.start.clone();
                 } else {
-                    let cursor = map
-                        .anchor_before(movement::left(map, start, app).unwrap(), Bias::Left, app)
+                    let cursor = self
+                        .display_map
+                        .anchor_before(
+                            movement::left(&self.display_map, start, app).unwrap(),
+                            Bias::Left,
+                            app,
+                        )
                         .unwrap();
                     selection.start = cursor.clone();
                     selection.end = cursor;
@@ -1195,15 +1194,15 @@ impl BufferView {
         let mut selections = self.selections(ctx.as_ref()).to_vec();
         {
             let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(ctx);
             for selection in &mut selections {
                 let head = selection
                     .head()
-                    .to_display_point(map, ctx.as_ref())
+                    .to_display_point(&self.display_map, ctx.as_ref())
                     .unwrap();
-                let cursor = map
+                let cursor = self
+                    .display_map
                     .anchor_before(
-                        movement::left(map, head, ctx.as_ref()).unwrap(),
+                        movement::left(&self.display_map, head, ctx.as_ref()).unwrap(),
                         Bias::Left,
                         ctx.as_ref(),
                     )
@@ -1219,16 +1218,26 @@ impl BufferView {
         let mut selections = self.selections(ctx.as_ref()).to_vec();
         {
             let app = ctx.as_ref();
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let start = selection.start.to_display_point(map, app).unwrap();
-                let end = selection.end.to_display_point(map, app).unwrap();
+                let start = selection
+                    .start
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let end = selection
+                    .end
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
 
                 if start != end {
                     selection.start = selection.end.clone();
                 } else {
-                    let cursor = map
-                        .anchor_before(movement::right(map, end, app).unwrap(), Bias::Right, app)
+                    let cursor = self
+                        .display_map
+                        .anchor_before(
+                            movement::right(&self.display_map, end, app).unwrap(),
+                            Bias::Right,
+                            app,
+                        )
                         .unwrap();
                     selection.start = cursor.clone();
                     selection.end = cursor;
@@ -1245,14 +1254,18 @@ impl BufferView {
         {
             let app = ctx.as_ref();
             let buffer = self.buffer.read(app);
-            let map = self.display_map.read(app);
             for selection in &mut selections {
                 let head = selection
                     .head()
-                    .to_display_point(map, ctx.as_ref())
+                    .to_display_point(&self.display_map, ctx.as_ref())
                     .unwrap();
-                let cursor = map
-                    .anchor_before(movement::right(map, head, app).unwrap(), Bias::Right, app)
+                let cursor = self
+                    .display_map
+                    .anchor_before(
+                        movement::right(&self.display_map, head, app).unwrap(),
+                        Bias::Right,
+                        app,
+                    )
                     .unwrap();
                 selection.set_head(&buffer, cursor);
                 selection.goal_column = None;
@@ -1268,17 +1281,25 @@ impl BufferView {
             let mut selections = self.selections(ctx.as_ref()).to_vec();
             {
                 let app = ctx.as_ref();
-                let map = self.display_map.read(app);
                 for selection in &mut selections {
-                    let start = selection.start.to_display_point(map, app).unwrap();
-                    let end = selection.end.to_display_point(map, app).unwrap();
+                    let start = selection
+                        .start
+                        .to_display_point(&self.display_map, app)
+                        .unwrap();
+                    let end = selection
+                        .end
+                        .to_display_point(&self.display_map, app)
+                        .unwrap();
                     if start != end {
                         selection.goal_column = None;
                     }
 
                     let (start, goal_column) =
-                        movement::up(map, start, selection.goal_column, app).unwrap();
-                    let cursor = map.anchor_before(start, Bias::Left, app).unwrap();
+                        movement::up(&self.display_map, start, selection.goal_column, app).unwrap();
+                    let cursor = self
+                        .display_map
+                        .anchor_before(start, Bias::Left, app)
+                        .unwrap();
                     selection.start = cursor.clone();
                     selection.end = cursor;
                     selection.goal_column = goal_column;
@@ -1294,12 +1315,19 @@ impl BufferView {
         {
             let app = ctx.as_ref();
             let buffer = self.buffer.read(app);
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
                 let (head, goal_column) =
-                    movement::up(map, head, selection.goal_column, app).unwrap();
-                selection.set_head(&buffer, map.anchor_before(head, Bias::Left, app).unwrap());
+                    movement::up(&self.display_map, head, selection.goal_column, app).unwrap();
+                selection.set_head(
+                    &buffer,
+                    self.display_map
+                        .anchor_before(head, Bias::Left, app)
+                        .unwrap(),
+                );
                 selection.goal_column = goal_column;
             }
         }
@@ -1313,17 +1341,25 @@ impl BufferView {
             let mut selections = self.selections(ctx.as_ref()).to_vec();
             {
                 let app = ctx.as_ref();
-                let map = self.display_map.read(app);
                 for selection in &mut selections {
-                    let start = selection.start.to_display_point(map, app).unwrap();
-                    let end = selection.end.to_display_point(map, app).unwrap();
+                    let start = selection
+                        .start
+                        .to_display_point(&self.display_map, app)
+                        .unwrap();
+                    let end = selection
+                        .end
+                        .to_display_point(&self.display_map, app)
+                        .unwrap();
                     if start != end {
                         selection.goal_column = None;
                     }
 
                     let (start, goal_column) =
-                        movement::down(map, end, selection.goal_column, app).unwrap();
-                    let cursor = map.anchor_before(start, Bias::Right, app).unwrap();
+                        movement::down(&self.display_map, end, selection.goal_column, app).unwrap();
+                    let cursor = self
+                        .display_map
+                        .anchor_before(start, Bias::Right, app)
+                        .unwrap();
                     selection.start = cursor.clone();
                     selection.end = cursor;
                     selection.goal_column = goal_column;
@@ -1339,12 +1375,19 @@ impl BufferView {
         {
             let app = ctx.as_ref();
             let buffer = self.buffer.read(app);
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
                 let (head, goal_column) =
-                    movement::down(map, head, selection.goal_column, app).unwrap();
-                selection.set_head(&buffer, map.anchor_before(head, Bias::Right, app).unwrap());
+                    movement::down(&self.display_map, head, selection.goal_column, app).unwrap();
+                selection.set_head(
+                    &buffer,
+                    self.display_map
+                        .anchor_before(head, Bias::Right, app)
+                        .unwrap(),
+                );
                 selection.goal_column = goal_column;
             }
         }
@@ -1355,11 +1398,16 @@ impl BufferView {
         let app = ctx.as_ref();
         let mut selections = self.selections(app).to_vec();
         {
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::prev_word_boundary(map, head, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.start = anchor.clone();
                 selection.end = anchor;
                 selection.reversed = false;
@@ -1374,11 +1422,16 @@ impl BufferView {
         let mut selections = self.selections(app).to_vec();
         {
             let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::prev_word_boundary(map, head, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head = movement::prev_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.set_head(buffer, anchor);
                 selection.goal_column = None;
             }
@@ -1397,11 +1450,16 @@ impl BufferView {
         let app = ctx.as_ref();
         let mut selections = self.selections(app).to_vec();
         {
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::next_word_boundary(map, head, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.start = anchor.clone();
                 selection.end = anchor;
                 selection.reversed = false;
@@ -1416,11 +1474,16 @@ impl BufferView {
         let mut selections = self.selections(app).to_vec();
         {
             let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::next_word_boundary(map, head, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head = movement::next_word_boundary(&self.display_map, head, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.set_head(buffer, anchor);
                 selection.goal_column = None;
             }
@@ -1439,11 +1502,17 @@ impl BufferView {
         let app = ctx.as_ref();
         let mut selections = self.selections(app).to_vec();
         {
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::line_beginning(map, head, true, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head =
+                    movement::line_beginning(&self.display_map, head, true, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.start = anchor.clone();
                 selection.end = anchor;
                 selection.reversed = false;
@@ -1462,11 +1531,17 @@ impl BufferView {
         let mut selections = self.selections(app).to_vec();
         {
             let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::line_beginning(map, head, *toggle_indent, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head =
+                    movement::line_beginning(&self.display_map, head, *toggle_indent, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.set_head(buffer, anchor);
                 selection.goal_column = None;
             }
@@ -1485,11 +1560,16 @@ impl BufferView {
         let app = ctx.as_ref();
         let mut selections = self.selections(app).to_vec();
         {
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::line_end(map, head, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head = movement::line_end(&self.display_map, head, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.start = anchor.clone();
                 selection.end = anchor;
                 selection.reversed = false;
@@ -1504,11 +1584,16 @@ impl BufferView {
         let mut selections = self.selections(app).to_vec();
         {
             let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(app);
             for selection in &mut selections {
-                let head = selection.head().to_display_point(map, app).unwrap();
-                let new_head = movement::line_end(map, head, app).unwrap();
-                let anchor = map.anchor_before(new_head, Bias::Left, app).unwrap();
+                let head = selection
+                    .head()
+                    .to_display_point(&self.display_map, app)
+                    .unwrap();
+                let new_head = movement::line_end(&self.display_map, head, app).unwrap();
+                let anchor = self
+                    .display_map
+                    .anchor_before(new_head, Bias::Left, app)
+                    .unwrap();
                 selection.set_head(buffer, anchor);
                 selection.goal_column = None;
             }
@@ -1574,12 +1659,13 @@ impl BufferView {
         range: Range<DisplayPoint>,
         app: &'a AppContext,
     ) -> impl 'a + Iterator<Item = Range<DisplayPoint>> {
-        let map = self.display_map.read(app);
-
-        let start = map.anchor_before(range.start, Bias::Left, app).unwrap();
+        let start = self
+            .display_map
+            .anchor_before(range.start, Bias::Left, app)
+            .unwrap();
         let start_index = self.selection_insertion_index(&start, app);
         let pending_selection = self.pending_selection.as_ref().and_then(|s| {
-            let selection_range = s.display_range(map, app);
+            let selection_range = s.display_range(&self.display_map, app);
             if selection_range.start <= range.end || selection_range.end <= range.end {
                 Some(selection_range)
             } else {
@@ -1588,7 +1674,7 @@ impl BufferView {
         });
         self.selections(app)[start_index..]
             .iter()
-            .map(move |s| s.display_range(map, app))
+            .map(move |s| s.display_range(&self.display_map, app))
             .take_while(move |r| r.start <= range.end || r.end <= range.end)
             .chain(pending_selection)
     }
@@ -1688,17 +1774,18 @@ impl BufferView {
         let mut fold_ranges = Vec::new();
 
         let app = ctx.as_ref();
-        let map = self.display_map.read(app);
         for selection in self.selections(app) {
-            let range = selection.display_range(map, app).sorted();
+            let range = selection.display_range(&self.display_map, app).sorted();
             let buffer_start_row = range
                 .start
-                .to_buffer_point(map, Bias::Left, app)
+                .to_buffer_point(&self.display_map, Bias::Left, app)
                 .unwrap()
                 .row;
 
             for row in (0..=range.end.row()).rev() {
-                if self.is_line_foldable(row, app) && !map.is_line_folded(row) {
+                if self.is_line_foldable(row, app)
+                    && !self.display_map.is_line_folded(row, ctx.as_ref())
+                {
                     let fold_range = self.foldable_range_for_line(row, app).unwrap();
                     if fold_range.end.row >= buffer_start_row {
                         fold_ranges.push(fold_range);
@@ -1717,15 +1804,20 @@ impl BufferView {
         use super::RangeExt;
 
         let app = ctx.as_ref();
-        let map = self.display_map.read(app);
         let buffer = self.buffer.read(app);
         let ranges = self
             .selections(app)
             .iter()
             .map(|s| {
-                let range = s.display_range(map, app).sorted();
-                let mut start = range.start.to_buffer_point(map, Bias::Left, app).unwrap();
-                let mut end = range.end.to_buffer_point(map, Bias::Left, app).unwrap();
+                let range = s.display_range(&self.display_map, app).sorted();
+                let mut start = range
+                    .start
+                    .to_buffer_point(&self.display_map, Bias::Left, app)
+                    .unwrap();
+                let mut end = range
+                    .end
+                    .to_buffer_point(&self.display_map, Bias::Left, app)
+                    .unwrap();
                 start.column = 0;
                 end.column = buffer.line_len(end.row).unwrap();
                 start..end
@@ -1735,17 +1827,17 @@ impl BufferView {
     }
 
     fn is_line_foldable(&self, display_row: u32, app: &AppContext) -> bool {
-        let map = self.display_map.read(app);
         let max_point = self.max_point(app);
         if display_row >= max_point.row() {
             false
         } else {
-            let (start_indent, is_blank) = map.line_indent(display_row, app).unwrap();
+            let (start_indent, is_blank) = self.display_map.line_indent(display_row, app).unwrap();
             if is_blank {
                 false
             } else {
                 for display_row in display_row + 1..=max_point.row() {
-                    let (indent, is_blank) = map.line_indent(display_row, app).unwrap();
+                    let (indent, is_blank) =
+                        self.display_map.line_indent(display_row, app).unwrap();
                     if !is_blank {
                         return indent > start_indent;
                     }
@@ -1756,14 +1848,13 @@ impl BufferView {
     }
 
     fn foldable_range_for_line(&self, start_row: u32, app: &AppContext) -> Result<Range<Point>> {
-        let map = self.display_map.read(app);
         let max_point = self.max_point(app);
 
-        let (start_indent, _) = map.line_indent(start_row, app)?;
+        let (start_indent, _) = self.display_map.line_indent(start_row, app)?;
         let start = DisplayPoint::new(start_row, self.line_len(start_row, app)?);
         let mut end = None;
         for row in start_row + 1..=max_point.row() {
-            let (indent, is_blank) = map.line_indent(row, app)?;
+            let (indent, is_blank) = self.display_map.line_indent(row, app)?;
             if !is_blank && indent <= start_indent {
                 end = Some(DisplayPoint::new(row - 1, self.line_len(row - 1, app)?));
                 break;
@@ -1771,60 +1862,57 @@ impl BufferView {
         }
 
         let end = end.unwrap_or(max_point);
-        return Ok(start.to_buffer_point(map, Bias::Left, app)?
-            ..end.to_buffer_point(map, Bias::Left, app)?);
+        return Ok(start.to_buffer_point(&self.display_map, Bias::Left, app)?
+            ..end.to_buffer_point(&self.display_map, Bias::Left, app)?);
     }
 
     pub fn fold_selected_ranges(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
         use super::RangeExt;
 
-        self.display_map.update(ctx, |map, ctx| {
-            let buffer = self.buffer.read(ctx);
-            let ranges = self
-                .selections(ctx.as_ref())
-                .iter()
-                .map(|s| s.range(buffer).sorted())
-                .collect::<Vec<_>>();
-            map.fold(ranges, ctx).unwrap();
-        });
+        let buffer = self.buffer.read(ctx);
+        let ranges = self
+            .selections(ctx.as_ref())
+            .iter()
+            .map(|s| s.range(buffer).sorted())
+            .collect::<Vec<_>>();
+        self.display_map.fold(ranges, ctx.as_ref()).unwrap();
+        ctx.notify();
     }
 
     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.display_map.fold(ranges, ctx.as_ref()).unwrap();
             *self.autoscroll_requested.lock() = true;
+            ctx.notify();
         }
     }
 
     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.display_map.unfold(ranges, ctx.as_ref()).unwrap();
             *self.autoscroll_requested.lock() = true;
+            ctx.notify();
         }
     }
 
-    pub fn line(&self, display_row: u32, app: &AppContext) -> Result<String> {
-        self.display_map.read(app).line(display_row, app)
+    pub fn line(&self, display_row: u32, ctx: &AppContext) -> Result<String> {
+        self.display_map.line(display_row, ctx)
     }
 
-    pub fn line_len(&self, display_row: u32, app: &AppContext) -> Result<u32> {
-        self.display_map.read(app).line_len(display_row, app)
+    pub fn line_len(&self, display_row: u32, ctx: &AppContext) -> Result<u32> {
+        self.display_map.line_len(display_row, ctx)
     }
 
-    pub fn rightmost_point(&self, app: &AppContext) -> DisplayPoint {
-        self.display_map.read(app).rightmost_point()
+    pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint {
+        self.display_map.rightmost_point(ctx)
     }
 
-    pub fn max_point(&self, app: &AppContext) -> DisplayPoint {
-        self.display_map.read(app).max_point(app)
+    pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
+        self.display_map.max_point(ctx)
     }
 
-    pub fn text(&self, app: &AppContext) -> String {
-        self.display_map.read(app).text(app)
+    pub fn text(&self, ctx: &AppContext) -> String {
+        self.display_map.text(ctx)
     }
 
     pub fn font_size(&self) -> f32 {
@@ -1887,10 +1975,8 @@ impl BufferView {
         viewport_height: f32,
         font_cache: &FontCache,
         layout_cache: &TextLayoutCache,
-        app: &AppContext,
+        ctx: &AppContext,
     ) -> Result<Vec<Arc<text_layout::Line>>> {
-        let display_map = self.display_map.read(app);
-
         let settings = smol::block_on(self.settings.read());
         let font_size = settings.buffer_font_size;
         let font_id =
@@ -1898,14 +1984,19 @@ impl BufferView {
 
         let start_row = self.scroll_position().y() as usize;
         let end_row = cmp::min(
-            self.max_point(app).row() as usize,
+            self.max_point(ctx).row() as usize,
             start_row + (viewport_height / self.line_height(font_cache)).ceil() as usize,
         );
         let line_count = end_row - start_row + 1;
 
         let mut layouts = Vec::with_capacity(line_count);
         let mut line_number = String::new();
-        for buffer_row in display_map.buffer_rows(start_row as u32)?.take(line_count) {
+        for buffer_row in self
+            .display_map
+            .snapshot(ctx)
+            .buffer_rows(start_row as u32)?
+            .take(line_count)
+        {
             line_number.clear();
             write!(&mut line_number, "{}", buffer_row + 1).unwrap();
             layouts.push(layout_cache.layout_str(

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

@@ -3,10 +3,12 @@ use super::{
 };
 use crate::{
     sum_tree::{self, Cursor, SumTree},
+    time,
     util::find_insertion_index,
 };
 use anyhow::{anyhow, Result};
 use gpui::{AppContext, ModelHandle};
+use parking_lot::{Mutex, MutexGuard};
 use std::{
     cmp::{self, Ordering},
     iter::Take,
@@ -16,49 +18,44 @@ use sum_tree::{Dimension, SeekBias};
 
 pub struct FoldMap {
     buffer: ModelHandle<Buffer>,
-    transforms: SumTree<Transform>,
+    transforms: Mutex<SumTree<Transform>>,
     folds: Vec<Range<Anchor>>,
+    last_sync: Mutex<time::Global>,
 }
 
 impl FoldMap {
-    pub fn new(buffer: ModelHandle<Buffer>, app: &AppContext) -> Self {
-        let text_summary = buffer.read(app).text_summary();
+    pub fn new(buffer_handle: ModelHandle<Buffer>, ctx: &AppContext) -> Self {
+        let buffer = buffer_handle.read(ctx);
+        let text_summary = buffer.text_summary();
         Self {
-            buffer,
+            buffer: buffer_handle,
             folds: Vec::new(),
-            transforms: SumTree::from_item(Transform {
+            transforms: Mutex::new(SumTree::from_item(Transform {
                 summary: TransformSummary {
                     buffer: text_summary.clone(),
                     display: text_summary,
                 },
                 display_text: None,
-            }),
+            })),
+            last_sync: Mutex::new(buffer.version()),
         }
     }
 
-    pub fn buffer_rows(&self, start_row: u32) -> Result<BufferRows> {
-        if start_row > self.transforms.summary().display.lines.row {
-            return Err(anyhow!("invalid display row {}", start_row));
+    pub fn snapshot(&self, ctx: &AppContext) -> FoldMapSnapshot {
+        FoldMapSnapshot {
+            transforms: self.sync(ctx).clone(),
+            buffer: self.buffer.clone(),
         }
-
-        let display_point = Point::new(start_row, 0);
-        let mut cursor = self.transforms.cursor();
-        cursor.seek(&DisplayPoint(display_point), SeekBias::Left);
-
-        Ok(BufferRows {
-            display_point,
-            cursor,
-        })
     }
 
-    pub fn len(&self) -> usize {
-        self.transforms.summary().display.chars
+    pub fn len(&self, ctx: &AppContext) -> usize {
+        self.sync(ctx).summary().display.chars
     }
 
     pub fn line_len(&self, row: u32, ctx: &AppContext) -> Result<u32> {
         let line_start = self.to_display_offset(DisplayPoint::new(row, 0), ctx)?.0;
-        let line_end = if row >= self.max_point().row() {
-            self.len()
+        let line_end = if row >= self.max_point(ctx).row() {
+            self.len(ctx)
         } else {
             self.to_display_offset(DisplayPoint::new(row + 1, 0), ctx)?
                 .0
@@ -68,61 +65,39 @@ impl FoldMap {
         Ok((line_end - line_start) as u32)
     }
 
-    pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result<Chars<'a>> {
-        let offset = self.to_display_offset(point, app)?;
-        let mut cursor = self.transforms.cursor();
-        cursor.seek(&offset, SeekBias::Right);
-        let buffer = self.buffer.read(app);
-        Ok(Chars {
-            cursor,
-            offset: offset.0,
-            buffer,
-            buffer_chars: None,
-        })
+    pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
+        DisplayPoint(self.sync(ctx).summary().display.lines)
     }
 
-    pub fn max_point(&self) -> DisplayPoint {
-        DisplayPoint(self.transforms.summary().display.lines)
+    pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint {
+        DisplayPoint(self.sync(ctx).summary().display.rightmost_point)
     }
 
-    pub fn rightmost_point(&self) -> DisplayPoint {
-        DisplayPoint(self.transforms.summary().display.rightmost_point)
-    }
-
-    pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
+    pub fn folds_in_range<'a, T>(
+        &'a self,
+        range: Range<T>,
+        app: &'a AppContext,
+    ) -> Result<impl Iterator<Item = &'a 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])
+        Ok(self.folds.iter().filter(move |fold| {
+            range.start.cmp(&fold.end, buffer).unwrap() == Ordering::Less
+                && range.end.cmp(&fold.start, buffer).unwrap() == Ordering::Greater
+        }))
     }
 
     pub fn fold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
-        app: &AppContext,
+        ctx: &AppContext,
     ) -> Result<()> {
+        let _ = self.sync(ctx);
+
         let mut edits = Vec::new();
-        let buffer = self.buffer.read(app);
+        let buffer = self.buffer.read(ctx);
         for range in ranges.into_iter() {
             let start = range.start.to_offset(buffer)?;
             let end = range.end.to_offset(buffer)?;
@@ -142,16 +117,18 @@ impl FoldMap {
                 .then_with(|| b.old_range.end.cmp(&a.old_range.end))
         });
 
-        self.apply_edits(&edits, app)?;
+        self.apply_edits(edits, ctx);
         Ok(())
     }
 
     pub fn unfold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
-        app: &AppContext,
+        ctx: &AppContext,
     ) -> Result<()> {
-        let buffer = self.buffer.read(app);
+        let _ = self.sync(ctx);
+
+        let buffer = self.buffer.read(ctx);
 
         let mut edits = Vec::new();
         for range in ranges.into_iter() {
@@ -176,12 +153,13 @@ impl FoldMap {
             });
         }
 
-        self.apply_edits(&edits, app)?;
+        self.apply_edits(edits, ctx);
         Ok(())
     }
 
-    pub fn is_line_folded(&self, display_row: u32) -> bool {
-        let mut cursor = self.transforms.cursor::<DisplayPoint, DisplayPoint>();
+    pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
+        let transforms = self.sync(ctx);
+        let mut cursor = transforms.cursor::<DisplayPoint, DisplayPoint>();
         cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right);
         while let Some(transform) = cursor.item() {
             if transform.display_text.is_some() {
@@ -196,43 +174,33 @@ impl FoldMap {
         false
     }
 
-    pub fn to_buffer_offset(&self, point: DisplayPoint, app: &AppContext) -> Result<usize> {
-        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
+    pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<usize> {
+        let transforms = self.sync(ctx);
+        let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
         cursor.seek(&point, SeekBias::Right);
         let overshoot = point.0 - cursor.start().display.lines;
-        (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(app))
+        (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))
     }
 
     pub fn to_display_offset(
         &self,
         point: DisplayPoint,
-        app: &AppContext,
+        ctx: &AppContext,
     ) -> Result<DisplayOffset> {
-        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
-        cursor.seek(&point, SeekBias::Right);
-        let overshoot = point.0 - cursor.start().display.lines;
-        let mut offset = cursor.start().display.chars;
-        if !overshoot.is_zero() {
-            let transform = cursor
-                .item()
-                .ok_or_else(|| anyhow!("display point {:?} is out of range", point))?;
-            assert!(transform.display_text.is_none());
-            let end_buffer_offset =
-                (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(app))?;
-            offset += end_buffer_offset - cursor.start().buffer.chars;
-        }
-        Ok(DisplayOffset(offset))
+        self.snapshot(ctx).to_display_offset(point, ctx)
     }
 
-    pub fn to_buffer_point(&self, display_point: DisplayPoint) -> Point {
-        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
+    pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
+        let transforms = self.sync(ctx);
+        let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
         cursor.seek(&display_point, SeekBias::Right);
         let overshoot = display_point.0 - cursor.start().display.lines;
         cursor.start().buffer.lines + overshoot
     }
 
-    pub fn to_display_point(&self, point: Point) -> DisplayPoint {
-        let mut cursor = self.transforms.cursor::<Point, TransformSummary>();
+    pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint {
+        let transforms = self.sync(ctx);
+        let mut cursor = transforms.cursor::<Point, TransformSummary>();
         cursor.seek(&point, SeekBias::Right);
         let overshoot = point - cursor.start().buffer.lines;
         DisplayPoint(cmp::min(
@@ -241,12 +209,23 @@ impl FoldMap {
         ))
     }
 
-    pub fn apply_edits(&mut self, edits: &[Edit], app: &AppContext) -> Result<()> {
-        let buffer = self.buffer.read(app);
-        let mut edits = edits.iter().cloned().peekable();
+    fn sync(&self, ctx: &AppContext) -> MutexGuard<SumTree<Transform>> {
+        let buffer = self.buffer.read(ctx);
+        let mut edits = buffer.edits_since(self.last_sync.lock().clone()).peekable();
+        if edits.peek().is_some() {
+            self.apply_edits(edits, ctx);
+        }
+        *self.last_sync.lock() = buffer.version();
+        self.transforms.lock()
+    }
+
+    fn apply_edits(&self, edits: impl IntoIterator<Item = Edit>, ctx: &AppContext) {
+        let buffer = self.buffer.read(ctx);
+        let mut edits = edits.into_iter().peekable();
 
         let mut new_transforms = SumTree::new();
-        let mut cursor = self.transforms.cursor::<usize, usize>();
+        let mut transforms = self.transforms.lock();
+        let mut cursor = transforms.cursor::<usize, usize>();
         cursor.seek(&0, SeekBias::Right);
 
         while let Some(mut edit) = edits.next() {
@@ -282,9 +261,10 @@ impl FoldMap {
             edit.new_range.end =
                 ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize;
 
-            let anchor = buffer.anchor_before(edit.new_range.start)?;
+            let anchor = buffer.anchor_before(edit.new_range.start).unwrap();
             let folds_start =
-                find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer))?;
+                find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer))
+                    .unwrap();
             let mut folds = self.folds[folds_start..]
                 .iter()
                 .map(|fold| {
@@ -366,9 +346,58 @@ impl FoldMap {
         }
 
         drop(cursor);
-        self.transforms = new_transforms;
+        *transforms = new_transforms;
+    }
+}
 
-        Ok(())
+pub struct FoldMapSnapshot {
+    transforms: SumTree<Transform>,
+    buffer: ModelHandle<Buffer>,
+}
+
+impl FoldMapSnapshot {
+    pub fn buffer_rows(&self, start_row: u32) -> Result<BufferRows> {
+        if start_row > self.transforms.summary().display.lines.row {
+            return Err(anyhow!("invalid display row {}", start_row));
+        }
+
+        let display_point = Point::new(start_row, 0);
+        let mut cursor = self.transforms.cursor();
+        cursor.seek(&DisplayPoint(display_point), SeekBias::Left);
+
+        Ok(BufferRows {
+            display_point,
+            cursor,
+        })
+    }
+
+    pub fn chars_at<'a>(&'a self, point: DisplayPoint, ctx: &'a AppContext) -> Result<Chars<'a>> {
+        let offset = self.to_display_offset(point, ctx)?;
+        let mut cursor = self.transforms.cursor();
+        cursor.seek(&offset, SeekBias::Right);
+        Ok(Chars {
+            cursor,
+            offset: offset.0,
+            buffer: self.buffer.read(ctx),
+            buffer_chars: None,
+        })
+    }
+
+    fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<DisplayOffset> {
+        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
+        cursor.seek(&point, SeekBias::Right);
+        let overshoot = point.0 - cursor.start().display.lines;
+        let mut offset = cursor.start().display.chars;
+        if !overshoot.is_zero() {
+            let transform = cursor
+                .item()
+                .ok_or_else(|| anyhow!("display point {:?} is out of range", point))?;
+            assert!(transform.display_text.is_none());
+            let end_buffer_offset =
+                (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))?;
+            offset += end_buffer_offset - cursor.start().buffer.chars;
+        }
+        Ok(DisplayOffset(offset))
     }
 }
 
@@ -500,6 +529,7 @@ impl<'a> Dimension<'a, TransformSummary> for usize {
 mod tests {
     use super::*;
     use crate::test::sample_text;
+    use buffer::ToPoint;
     use gpui::App;
 
     #[test]
@@ -518,8 +548,7 @@ mod tests {
             .unwrap();
             assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee");
 
-            let edits = buffer.update(app, |buffer, ctx| {
-                let start_version = buffer.version.clone();
+            buffer.update(app, |buffer, ctx| {
                 buffer
                     .edit(
                         vec![
@@ -530,21 +559,16 @@ mod tests {
                         Some(ctx),
                     )
                     .unwrap();
-                buffer.edits_since(start_version).collect::<Vec<_>>()
             });
-
-            map.apply_edits(&edits, app.as_ref()).unwrap();
             assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee");
 
-            let edits = buffer.update(app, |buffer, ctx| {
+            buffer.update(app, |buffer, ctx| {
                 let start_version = buffer.version.clone();
                 buffer
                     .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx))
                     .unwrap();
                 buffer.edits_since(start_version).collect::<Vec<_>>()
             });
-
-            map.apply_edits(&edits, app.as_ref()).unwrap();
             assert_eq!(map.text(app.as_ref()), "123a…c123456eee");
 
             map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref())
@@ -585,12 +609,11 @@ mod tests {
                 assert_eq!(map.text(app.as_ref()), "…fghijkl");
 
                 // Edit within one of the folds.
-                let edits = buffer.update(app, |buffer, ctx| {
+                buffer.update(app, |buffer, ctx| {
                     let version = buffer.version();
                     buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap();
                     buffer.edits_since(version).collect::<Vec<_>>()
                 });
-                map.apply_edits(edits.as_slice(), app.as_ref()).unwrap();
                 map.check_invariants(app.as_ref());
                 assert_eq!(map.text(app.as_ref()), "12345…fghijkl");
             }
@@ -632,19 +655,49 @@ mod tests {
             .unwrap();
             assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee");
 
-            let edits = buffer.update(app, |buffer, ctx| {
-                let start_version = buffer.version.clone();
+            buffer.update(app, |buffer, ctx| {
                 buffer
                     .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx))
                     .unwrap();
-                buffer.edits_since(start_version).collect::<Vec<_>>()
             });
-
-            map.apply_edits(&edits, app.as_ref()).unwrap();
             assert_eq!(map.text(app.as_ref()), "aa…eeeee");
         });
     }
 
+    #[test]
+    fn test_folds_in_range() {
+        App::test((), |app| {
+            let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
+            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
+            let buffer = buffer.read(app);
+
+            map.fold(
+                vec![
+                    Point::new(0, 2)..Point::new(2, 2),
+                    Point::new(0, 4)..Point::new(1, 0),
+                    Point::new(1, 2)..Point::new(3, 2),
+                    Point::new(3, 1)..Point::new(4, 1),
+                ],
+                app.as_ref(),
+            )
+            .unwrap();
+            let fold_ranges = map
+                .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref())
+                .unwrap()
+                .map(|fold| {
+                    fold.start.to_point(buffer).unwrap()..fold.end.to_point(buffer).unwrap()
+                })
+                .collect::<Vec<_>>();
+            assert_eq!(
+                fold_ranges,
+                vec![
+                    Point::new(0, 2)..Point::new(2, 2),
+                    Point::new(1, 2)..Point::new(3, 2)
+                ]
+            );
+        });
+    }
+
     #[test]
     fn test_random_folds() {
         use crate::editor::ToPoint;
@@ -679,10 +732,10 @@ mod tests {
 
                 for _ in 0..operations {
                     log::info!("text: {:?}", buffer.read(app).text());
-                    {
+                    if rng.gen() {
                         let buffer = buffer.read(app);
 
-                        let fold_count = rng.gen_range(0..=2);
+                        let fold_count = rng.gen_range(1..=5);
                         let mut fold_ranges: Vec<Range<usize>> = Vec::new();
                         for _ in 0..fold_count {
                             let end = rng.gen_range(0..buffer.len() + 1);
@@ -691,29 +744,15 @@ mod tests {
                         }
                         log::info!("folding {:?}", fold_ranges);
                         map.fold(fold_ranges.clone(), app.as_ref()).unwrap();
-                        map.check_invariants(app.as_ref());
-
-                        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()));
-                        }
+                    } else {
+                        let edits = buffer.update(app, |buffer, ctx| {
+                            let start_version = buffer.version.clone();
+                            let edit_count = rng.gen_range(1..=5);
+                            buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
+                            buffer.edits_since(start_version).collect::<Vec<_>>()
+                        });
+                        log::info!("editing {:?}", edits);
                     }
-
-                    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();
                     map.check_invariants(app.as_ref());
 
                     let buffer = map.buffer.read(app);
@@ -733,13 +772,78 @@ mod tests {
 
                     assert_eq!(map.text(app.as_ref()), expected_text);
 
+                    for (display_row, line) in expected_text.lines().enumerate() {
+                        let line_len = map.line_len(display_row as u32, app.as_ref()).unwrap();
+                        assert_eq!(line_len, line.chars().count() as u32);
+                    }
+
+                    let mut display_point = DisplayPoint::new(0, 0);
+                    let mut display_offset = DisplayOffset(0);
+                    for c in expected_text.chars() {
+                        let buffer_point = map.to_buffer_point(display_point, app.as_ref());
+                        let buffer_offset = buffer_point.to_offset(buffer).unwrap();
+                        assert_eq!(
+                            map.to_display_point(buffer_point, app.as_ref()),
+                            display_point
+                        );
+                        assert_eq!(
+                            map.to_buffer_offset(display_point, app.as_ref()).unwrap(),
+                            buffer_offset
+                        );
+                        assert_eq!(
+                            map.to_display_offset(display_point, app.as_ref()).unwrap(),
+                            display_offset
+                        );
+
+                        if c == '\n' {
+                            *display_point.row_mut() += 1;
+                            *display_point.column_mut() = 0;
+                        } else {
+                            *display_point.column_mut() += 1;
+                        }
+                        display_offset.0 += 1;
+                    }
+
+                    for _ in 0..5 {
+                        let row = rng.gen_range(0..=map.max_point(app.as_ref()).row());
+                        let column = rng.gen_range(0..=map.line_len(row, app.as_ref()).unwrap());
+                        let point = DisplayPoint::new(row, column);
+                        let offset = map.to_display_offset(point, app.as_ref()).unwrap().0;
+                        let len = rng.gen_range(0..=map.len(app.as_ref()) - offset);
+                        assert_eq!(
+                            map.snapshot(app.as_ref())
+                                .chars_at(point, app.as_ref())
+                                .unwrap()
+                                .take(len)
+                                .collect::<String>(),
+                            expected_text
+                                .chars()
+                                .skip(offset)
+                                .take(len)
+                                .collect::<String>()
+                        );
+                    }
+
                     for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
-                        let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
+                        let display_row = map
+                            .to_display_point(Point::new(*buffer_row, 0), app.as_ref())
+                            .row();
                         assert_eq!(
-                            map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
+                            map.snapshot(app.as_ref())
+                                .buffer_rows(display_row)
+                                .unwrap()
+                                .collect::<Vec<_>>(),
                             expected_buffer_rows[idx..],
                         );
                     }
+
+                    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(),
+                            app.as_ref(),
+                        );
+                        assert!(map.is_line_folded(display_point.row(), app.as_ref()));
+                    }
                 }
             });
         }
@@ -764,24 +868,36 @@ mod tests {
 
             assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n");
             assert_eq!(
-                map.buffer_rows(0).unwrap().collect::<Vec<_>>(),
+                map.snapshot(app.as_ref())
+                    .buffer_rows(0)
+                    .unwrap()
+                    .collect::<Vec<_>>(),
                 vec![0, 3, 5, 6]
             );
-            assert_eq!(map.buffer_rows(3).unwrap().collect::<Vec<_>>(), vec![6]);
+            assert_eq!(
+                map.snapshot(app.as_ref())
+                    .buffer_rows(3)
+                    .unwrap()
+                    .collect::<Vec<_>>(),
+                vec![6]
+            );
         });
     }
 
     impl FoldMap {
         fn text(&self, app: &AppContext) -> String {
-            self.chars_at(DisplayPoint(Point::zero()), app)
+            self.snapshot(app)
+                .chars_at(DisplayPoint(Point::zero()), app)
                 .unwrap()
                 .collect()
         }
 
         fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
             let buffer = self.buffer.read(app);
-            let mut fold_ranges = self
-                .folds
+            let mut folds = self.folds.clone();
+            // Ensure sorting doesn't change how folds get merged and displayed.
+            folds.sort_by(|a, b| a.cmp(b, buffer).unwrap());
+            let mut fold_ranges = folds
                 .iter()
                 .map(|fold| {
                     fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap()
@@ -807,10 +923,12 @@ mod tests {
             merged_ranges
         }
 
-        fn check_invariants(&self, app: &AppContext) {
+        fn check_invariants(&self, ctx: &AppContext) {
+            let transforms = self.sync(ctx);
+            let buffer = self.buffer.read(ctx);
             assert_eq!(
-                self.transforms.summary().buffer.chars,
-                self.buffer.read(app).len(),
+                transforms.summary().buffer.chars,
+                buffer.len(),
                 "transform tree does not match buffer's length"
             );
         }

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

@@ -3,8 +3,8 @@ mod fold_map;
 use super::{buffer, Anchor, AnchorRangeExt, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
 use anyhow::Result;
 pub use fold_map::BufferRows;
-use fold_map::FoldMap;
-use gpui::{AppContext, Entity, ModelContext, ModelHandle};
+use fold_map::{FoldMap, FoldMapSnapshot};
+use gpui::{AppContext, ModelHandle};
 use std::ops::Range;
 
 #[derive(Copy, Clone)]
@@ -19,22 +19,27 @@ pub struct DisplayMap {
     tab_size: usize,
 }
 
-impl Entity for DisplayMap {
-    type Event = ();
-}
-
 impl DisplayMap {
-    pub fn new(buffer: ModelHandle<Buffer>, tab_size: usize, ctx: &mut ModelContext<Self>) -> Self {
-        ctx.subscribe(&buffer, Self::handle_buffer_event);
-
+    pub fn new(buffer: ModelHandle<Buffer>, tab_size: usize, ctx: &AppContext) -> Self {
         DisplayMap {
             buffer: buffer.clone(),
-            fold_map: FoldMap::new(buffer, ctx.as_ref()),
+            fold_map: FoldMap::new(buffer, ctx),
             tab_size,
         }
     }
 
-    pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
+    pub fn snapshot(&self, ctx: &AppContext) -> DisplayMapSnapshot {
+        DisplayMapSnapshot {
+            folds_snapshot: self.fold_map.snapshot(ctx),
+            tab_size: self.tab_size,
+        }
+    }
+
+    pub fn folds_in_range<'a, T>(
+        &'a self,
+        range: Range<T>,
+        app: &'a AppContext,
+    ) -> Result<impl Iterator<Item = &'a Range<Anchor>>>
     where
         T: ToOffset,
     {
@@ -44,40 +49,45 @@ impl DisplayMap {
     pub fn fold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
-        ctx: &mut ModelContext<Self>,
+        ctx: &AppContext,
     ) -> Result<()> {
-        self.fold_map.fold(ranges, ctx.as_ref())?;
-        ctx.notify();
-        Ok(())
+        self.fold_map.fold(ranges, ctx)
     }
 
     pub fn unfold<T: ToOffset>(
         &mut self,
         ranges: impl IntoIterator<Item = Range<T>>,
-        ctx: &mut ModelContext<Self>,
+        ctx: &AppContext,
     ) -> Result<()> {
-        self.fold_map.unfold(ranges, ctx.as_ref())?;
-        ctx.notify();
-        Ok(())
+        self.fold_map.unfold(ranges, ctx)
     }
 
-    pub fn is_line_folded(&self, display_row: u32) -> bool {
-        self.fold_map.is_line_folded(display_row)
+    pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
+        self.fold_map.is_line_folded(display_row, ctx)
     }
 
-    pub fn text(&self, app: &AppContext) -> String {
-        self.chars_at(DisplayPoint::zero(), app).unwrap().collect()
+    pub fn text(&self, ctx: &AppContext) -> String {
+        self.snapshot(ctx)
+            .chars_at(DisplayPoint::zero(), ctx)
+            .unwrap()
+            .collect()
     }
 
-    pub fn line(&self, display_row: u32, app: &AppContext) -> Result<String> {
-        let chars = self.chars_at(DisplayPoint::new(display_row, 0), app)?;
-        Ok(chars.take_while(|c| *c != '\n').collect())
+    pub fn line(&self, display_row: u32, ctx: &AppContext) -> Result<String> {
+        Ok(self
+            .snapshot(ctx)
+            .chars_at(DisplayPoint::new(display_row, 0), ctx)?
+            .take_while(|c| *c != '\n')
+            .collect())
     }
 
-    pub fn line_indent(&self, display_row: u32, app: &AppContext) -> Result<(u32, bool)> {
+    pub fn line_indent(&self, display_row: u32, ctx: &AppContext) -> Result<(u32, bool)> {
         let mut indent = 0;
         let mut is_blank = true;
-        for c in self.chars_at(DisplayPoint::new(display_row, 0), app)? {
+        for c in self
+            .snapshot(ctx)
+            .chars_at(DisplayPoint::new(display_row, 0), ctx)?
+        {
             if c == ' ' {
                 indent += 1;
             } else {
@@ -88,38 +98,18 @@ impl DisplayMap {
         Ok((indent, is_blank))
     }
 
-    pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result<Chars<'a>> {
-        let column = point.column() as usize;
-        let (point, to_next_stop) = point.collapse_tabs(self, Bias::Left, app)?;
-        let mut fold_chars = self.fold_map.chars_at(point, app)?;
-        if to_next_stop > 0 {
-            fold_chars.next();
-        }
-
-        Ok(Chars {
-            fold_chars,
-            column,
-            to_next_stop,
-            tab_size: self.tab_size,
-        })
-    }
-
-    pub fn buffer_rows(&self, start_row: u32) -> Result<BufferRows> {
-        self.fold_map.buffer_rows(start_row)
-    }
-
     pub fn line_len(&self, row: u32, ctx: &AppContext) -> Result<u32> {
         DisplayPoint::new(row, self.fold_map.line_len(row, ctx)?)
             .expand_tabs(self, ctx)
             .map(|point| point.column())
     }
 
-    pub fn max_point(&self, app: &AppContext) -> DisplayPoint {
-        self.fold_map.max_point().expand_tabs(self, app).unwrap()
+    pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
+        self.fold_map.max_point(ctx).expand_tabs(self, ctx).unwrap()
     }
 
-    pub fn rightmost_point(&self) -> DisplayPoint {
-        self.fold_map.rightmost_point()
+    pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint {
+        self.fold_map.rightmost_point(ctx)
     }
 
     pub fn anchor_before(
@@ -143,12 +133,57 @@ impl DisplayMap {
             .read(app)
             .anchor_after(point.to_buffer_point(self, bias, app)?)
     }
+}
 
-    fn handle_buffer_event(&mut self, event: &buffer::Event, ctx: &mut ModelContext<Self>) {
-        match event {
-            buffer::Event::Edited(edits) => self.fold_map.apply_edits(edits, ctx.as_ref()).unwrap(),
-            _ => {}
+pub struct DisplayMapSnapshot {
+    folds_snapshot: FoldMapSnapshot,
+    tab_size: usize,
+}
+
+impl DisplayMapSnapshot {
+    pub fn buffer_rows(&self, start_row: u32) -> Result<BufferRows> {
+        self.folds_snapshot.buffer_rows(start_row)
+    }
+
+    pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result<Chars<'a>> {
+        let column = point.column() as usize;
+        let (point, to_next_stop) = self.collapse_tabs(point, Bias::Left, app)?;
+        let mut fold_chars = self.folds_snapshot.chars_at(point, app)?;
+        if to_next_stop > 0 {
+            fold_chars.next();
         }
+
+        Ok(Chars {
+            fold_chars,
+            column,
+            to_next_stop,
+            tab_size: self.tab_size,
+        })
+    }
+
+    fn expand_tabs(&self, mut point: DisplayPoint, ctx: &AppContext) -> Result<DisplayPoint> {
+        let chars = self
+            .folds_snapshot
+            .chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx)?;
+        let expanded = expand_tabs(chars, point.column() as usize, self.tab_size);
+        *point.column_mut() = expanded as u32;
+        Ok(point)
+    }
+
+    fn collapse_tabs(
+        &self,
+        mut point: DisplayPoint,
+        bias: Bias,
+        ctx: &AppContext,
+    ) -> Result<(DisplayPoint, usize)> {
+        let chars = self
+            .folds_snapshot
+            .chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx)?;
+        let expanded = point.column() as usize;
+        let (collapsed, to_next_stop) = collapse_tabs(chars, expanded, bias, self.tab_size);
+        *point.column_mut() = collapsed as u32;
+
+        Ok((point, to_next_stop))
     }
 }
 
@@ -180,50 +215,36 @@ impl DisplayPoint {
         &mut self.0.column
     }
 
-    pub fn to_buffer_point(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result<Point> {
+    pub fn to_buffer_point(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Result<Point> {
         Ok(map
             .fold_map
-            .to_buffer_point(self.collapse_tabs(map, bias, app)?.0))
+            .to_buffer_point(self.collapse_tabs(map, bias, ctx)?.0, ctx))
     }
 
-    pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result<usize> {
+    pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Result<usize> {
         map.fold_map
-            .to_buffer_offset(self.collapse_tabs(map, bias, app)?.0, app)
+            .to_buffer_offset(self.collapse_tabs(&map, bias, ctx)?.0, ctx)
     }
 
-    fn expand_tabs(mut self, map: &DisplayMap, app: &AppContext) -> Result<Self> {
-        let chars = map
-            .fold_map
-            .chars_at(DisplayPoint(Point::new(self.row(), 0)), app)?;
-        let expanded = expand_tabs(chars, self.column() as usize, map.tab_size);
-        *self.column_mut() = expanded as u32;
-
-        Ok(self)
+    fn expand_tabs(self, map: &DisplayMap, ctx: &AppContext) -> Result<Self> {
+        map.snapshot(ctx).expand_tabs(self, ctx)
     }
 
     fn collapse_tabs(
-        mut self,
+        self,
         map: &DisplayMap,
         bias: Bias,
-        app: &AppContext,
+        ctx: &AppContext,
     ) -> Result<(Self, usize)> {
-        let chars = map
-            .fold_map
-            .chars_at(DisplayPoint(Point::new(self.0.row, 0)), app)?;
-        let expanded = self.column() as usize;
-        let (collapsed, to_next_stop) = collapse_tabs(chars, expanded, bias, map.tab_size);
-        *self.column_mut() = collapsed as u32;
-
-        Ok((self, to_next_stop))
+        map.snapshot(ctx).collapse_tabs(self, bias, ctx)
     }
 }
 
 impl Point {
-    pub fn to_display_point(self, map: &DisplayMap, app: &AppContext) -> Result<DisplayPoint> {
-        let mut display_point = map.fold_map.to_display_point(self);
-        let chars = map
-            .fold_map
-            .chars_at(DisplayPoint::new(display_point.row(), 0), app)?;
+    pub fn to_display_point(self, map: &DisplayMap, ctx: &AppContext) -> Result<DisplayPoint> {
+        let mut display_point = map.fold_map.to_display_point(self, ctx);
+        let snapshot = map.fold_map.snapshot(ctx);
+        let chars = snapshot.chars_at(DisplayPoint::new(display_point.row(), 0), ctx)?;
         *display_point.column_mut() =
             expand_tabs(chars, display_point.column() as usize, map.tab_size) as u32;
         Ok(display_point)
@@ -325,7 +346,7 @@ mod tests {
         App::test((), |app| {
             let text = sample_text(6, 6);
             let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
-            let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx));
+            let map = DisplayMap::new(buffer.clone(), 4, app.as_ref());
             buffer
                 .update(app, |buffer, ctx| {
                     buffer.edit(
@@ -340,23 +361,25 @@ mod tests {
                 })
                 .unwrap();
 
-            let map = map.read(app);
             assert_eq!(
-                map.chars_at(DisplayPoint::new(1, 0), app.as_ref())
+                map.snapshot(app.as_ref())
+                    .chars_at(DisplayPoint::new(1, 0), app.as_ref())
                     .unwrap()
                     .take(10)
                     .collect::<String>(),
                 "    b   bb"
             );
             assert_eq!(
-                map.chars_at(DisplayPoint::new(1, 2), app.as_ref())
+                map.snapshot(app.as_ref())
+                    .chars_at(DisplayPoint::new(1, 2), app.as_ref())
                     .unwrap()
                     .take(10)
                     .collect::<String>(),
                 "  b   bbbb"
             );
             assert_eq!(
-                map.chars_at(DisplayPoint::new(1, 6), app.as_ref())
+                map.snapshot(app.as_ref())
+                    .chars_at(DisplayPoint::new(1, 6), app.as_ref())
                     .unwrap()
                     .take(13)
                     .collect::<String>(),
@@ -392,11 +415,8 @@ mod tests {
     fn test_max_point() {
         App::test((), |app| {
             let buffer = app.add_model(|ctx| Buffer::new(0, "aaa\n\t\tbbb", ctx));
-            let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx));
-            assert_eq!(
-                map.read(app).max_point(app.as_ref()),
-                DisplayPoint::new(1, 11)
-            )
+            let map = DisplayMap::new(buffer.clone(), 4, app.as_ref());
+            assert_eq!(map.max_point(app.as_ref()), DisplayPoint::new(1, 11))
         });
     }
 }

zed/src/editor/movement.rs 🔗

@@ -96,7 +96,7 @@ pub fn prev_word_boundary(
         let mut boundary = DisplayPoint::new(point.row(), 0);
         let mut column = 0;
         let mut prev_c = None;
-        for c in map.chars_at(boundary, app)? {
+        for c in map.snapshot(app).chars_at(boundary, app)? {
             if column >= point.column() {
                 break;
             }
@@ -118,7 +118,7 @@ pub fn next_word_boundary(
     app: &AppContext,
 ) -> Result<DisplayPoint> {
     let mut prev_c = None;
-    for c in map.chars_at(point, app)? {
+    for c in map.snapshot(app).chars_at(point, app)? {
         if prev_c.is_some() && (c == '\n' || char_kind(prev_c.unwrap()) != char_kind(c)) {
             break;
         }