store buffer and display_map model handles on selections collection

Keith Simmons created

Change summary

crates/collab/src/rpc.rs                   |  10 
crates/diagnostics/src/diagnostics.rs      |   8 
crates/diagnostics/src/items.rs            |   2 
crates/editor/src/editor.rs                | 320 ++++++++---------------
crates/editor/src/element.rs               |   4 
crates/editor/src/items.rs                 |   4 
crates/editor/src/selections_collection.rs | 117 +++++--
crates/editor/src/test.rs                  |   2 
crates/go_to_line/src/go_to_line.rs        |   2 
crates/outline/src/outline.rs              |   2 
crates/search/src/buffer_search.rs         |  53 ++-
crates/search/src/project_search.rs        |  32 +-
crates/vim/src/vim_test_context.rs         |   2 
crates/zed/src/zed.rs                      |   2 
14 files changed, 266 insertions(+), 294 deletions(-)

Detailed changes

crates/collab/src/rpc.rs 🔗

@@ -5499,11 +5499,11 @@ mod tests {
             Some((worktree_id, "2.txt").into())
         );
         assert_eq!(
-            editor_b2.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)),
+            editor_b2.read_with(cx_b, |editor, cx| editor.selections.selected_ranges(cx)),
             vec![2..3]
         );
         assert_eq!(
-            editor_b1.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)),
+            editor_b1.read_with(cx_b, |editor, cx| editor.selections.selected_ranges(cx)),
             vec![0..1]
         );
 
@@ -5546,7 +5546,7 @@ mod tests {
         });
         editor_b1
             .condition(cx_b, |editor, cx| {
-                editor.selected_ranges(cx) == vec![1..1, 2..2]
+                editor.selections.selected_ranges(cx) == vec![1..1, 2..2]
             })
             .await;
 
@@ -5560,7 +5560,9 @@ mod tests {
             editor.set_scroll_position(vec2f(0., 100.), cx);
         });
         editor_b1
-            .condition(cx_b, |editor, cx| editor.selected_ranges(cx) == vec![3..3])
+            .condition(cx_b, |editor, cx| {
+                editor.selections.selected_ranges(cx) == vec![3..3]
+            })
             .await;
 
         // After unfollowing, client B stops receiving updates from client A.

crates/diagnostics/src/diagnostics.rs 🔗

@@ -419,9 +419,7 @@ impl ProjectDiagnosticsEditor {
                 groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
                 new_excerpt_ids_by_selection_id =
                     editor.change_selections(Some(Autoscroll::Fit), cx, |s| s.refresh());
-                selections = editor
-                    .selections
-                    .interleaved::<usize>(&editor.buffer().read(cx).read(cx));
+                selections = editor.selections.interleaved::<usize>(cx);
             }
 
             // If any selection has lost its position, move it to start of the next primary diagnostic.
@@ -899,7 +897,7 @@ mod tests {
             // Cursor is at the first diagnostic
             view.editor.update(cx, |editor, cx| {
                 assert_eq!(
-                    editor.selected_display_ranges(cx),
+                    editor.selections.selected_display_ranges(cx),
                     [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
                 );
             });
@@ -1000,7 +998,7 @@ mod tests {
             // Cursor keeps its position.
             view.editor.update(cx, |editor, cx| {
                 assert_eq!(
-                    editor.selected_display_ranges(cx),
+                    editor.selections.selected_display_ranges(cx),
                     [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
                 );
             });

crates/diagnostics/src/items.rs 🔗

@@ -58,7 +58,7 @@ impl DiagnosticIndicator {
     fn update(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
         let editor = editor.read(cx);
         let buffer = editor.buffer().read(cx);
-        let cursor_position = editor.selections.newest::<usize>(&buffer.read(cx)).head();
+        let cursor_position = editor.selections.newest::<usize>(cx).head();
         let new_diagnostic = buffer
             .read(cx)
             .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false)

crates/editor/src/editor.rs 🔗

@@ -51,12 +51,11 @@ use std::{
     borrow::Cow,
     cmp::{self, Ordering, Reverse},
     iter, mem,
-    ops::{Deref, DerefMut, Range, RangeInclusive, Sub},
+    ops::{Deref, DerefMut, Range, RangeInclusive},
     sync::Arc,
     time::{Duration, Instant},
 };
 pub use sum_tree::Bias;
-use text::rope::TextDimension;
 use theme::{DiagnosticStyle, Theme};
 use util::{post_inc, ResultExt, TryFutureExt};
 use workspace::{ItemNavHistory, Workspace};
@@ -938,11 +937,13 @@ impl Editor {
         cx.observe(&display_map, Self::on_display_map_changed)
             .detach();
 
+        let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
+
         let mut this = Self {
             handle: cx.weak_handle(),
             buffer,
             display_map,
-            selections: SelectionsCollection::new(),
+            selections,
             columnar_selection_tail: None,
             add_selections_state: None,
             select_next_state: None,
@@ -1186,15 +1187,11 @@ impl Editor {
             first_cursor_top = highlighted_rows.start as f32;
             last_cursor_bottom = first_cursor_top + 1.;
         } else if autoscroll == Autoscroll::Newest {
-            let newest_selection = self
-                .selections
-                .newest::<Point>(&display_map.buffer_snapshot);
+            let newest_selection = self.selections.newest::<Point>(cx);
             first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
             last_cursor_bottom = first_cursor_top + 1.;
         } else {
-            let selections = self
-                .selections
-                .interleaved::<Point>(&display_map.buffer_snapshot);
+            let selections = self.selections.interleaved::<Point>(cx);
             first_cursor_top = selections
                 .first()
                 .unwrap()
@@ -1254,9 +1251,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) -> bool {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let selections = self
-            .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot);
+        let selections = self.selections.interleaved::<Point>(cx);
 
         let mut target_left;
         let mut target_right;
@@ -1387,9 +1382,7 @@ impl Editor {
         let old_cursor_position = self.selections.newest_anchor().head();
         self.push_to_selection_history();
 
-        let result =
-            self.selections
-                .change_with(self.display_map.clone(), self.buffer.clone(), cx, change);
+        let result = self.selections.change_with(cx, change);
 
         if let Some(autoscroll) = autoscroll {
             self.request_autoscroll(autoscroll, cx);
@@ -1406,7 +1399,7 @@ impl Editor {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let selections = self
             .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot)
+            .interleaved::<Point>(cx)
             .into_iter()
             .map(|selection| selection.map(|point| point.to_display_point(&display_map)))
             .collect();
@@ -1465,10 +1458,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let tail = self
-            .selections
-            .newest::<usize>(&display_map.buffer_snapshot)
-            .tail();
+        let tail = self.selections.newest::<usize>(cx).tail();
         self.begin_selection(position, false, click_count, cx);
 
         let position = position.to_offset(&display_map, Bias::Left);
@@ -1573,10 +1563,7 @@ impl Editor {
         }
 
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let tail = self
-            .selections
-            .newest::<Point>(&display_map.buffer_snapshot)
-            .tail();
+        let tail = self.selections.newest::<Point>(cx).tail();
         self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
 
         self.select_columns(
@@ -1687,9 +1674,7 @@ impl Editor {
     fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
         self.columnar_selection_tail.take();
         if self.selections.pending_anchor().is_some() {
-            let selections = self
-                .selections
-                .interleaved::<usize>(&self.buffer.read(cx).snapshot(cx));
+            let selections = self.selections.interleaved::<usize>(cx);
             self.change_selections(None, cx, |s| {
                 s.select(selections);
                 s.clear_pending();
@@ -1769,43 +1754,6 @@ impl Editor {
         cx.propagate_action();
     }
 
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn selected_ranges<D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug>(
-        &self,
-        cx: &AppContext,
-    ) -> Vec<Range<D>> {
-        self.selections
-            .interleaved::<D>(&self.buffer.read(cx).read(cx))
-            .iter()
-            .map(|s| {
-                if s.reversed {
-                    s.end.clone()..s.start.clone()
-                } else {
-                    s.start.clone()..s.end.clone()
-                }
-            })
-            .collect()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec<Range<DisplayPoint>> {
-        let display_map = self
-            .display_map
-            .update(cx, |display_map, cx| display_map.snapshot(cx));
-        self.selections
-            .disjoint_anchors()
-            .iter()
-            .chain(self.selections.pending_anchor().as_ref())
-            .map(|s| {
-                if s.reversed {
-                    s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
-                } else {
-                    s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
-                }
-            })
-            .collect()
-    }
-
     pub fn handle_input(&mut self, action: &Input, cx: &mut ViewContext<Self>) {
         if !self.input_enabled {
             cx.propagate_action();
@@ -1827,8 +1775,9 @@ impl Editor {
     pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
         self.transact(cx, |this, cx| {
             let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
+                let selections = this.selections.interleaved::<usize>(cx);
+
                 let buffer = this.buffer.read(cx).snapshot(cx);
-                let selections = this.selections.interleaved::<usize>(&buffer);
                 selections
                     .iter()
                     .map(|selection| {
@@ -1910,9 +1859,7 @@ impl Editor {
     pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
         let text: Arc<str> = text.into();
         self.transact(cx, |this, cx| {
-            let old_selections = this
-                .selections
-                .interleaved::<usize>(&this.buffer.read(cx).snapshot(cx));
+            let old_selections = this.selections.interleaved::<usize>(cx);
             let selection_anchors = this.buffer.update(cx, |buffer, cx| {
                 let anchors = {
                     let snapshot = buffer.read(cx);
@@ -1975,7 +1922,7 @@ impl Editor {
         {
             if self
                 .selections
-                .interleaved::<usize>(&snapshot)
+                .interleaved::<usize>(cx)
                 .iter()
                 .any(|selection| selection.is_empty())
             {
@@ -2018,8 +1965,7 @@ impl Editor {
     }
 
     fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext<Self>) {
-        let snapshot = self.buffer.read(cx).snapshot(cx);
-        let selections = self.selections.interleaved::<usize>(&snapshot);
+        let selections = self.selections.interleaved::<usize>(cx);
         let mut bracket_pair_state = None;
         let mut new_selections = None;
         self.buffer.update(cx, |buffer, cx| {
@@ -2119,7 +2065,7 @@ impl Editor {
 
     fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
         let buffer = self.buffer.read(cx).snapshot(cx);
-        let old_selections = self.selections.interleaved::<usize>(&buffer);
+        let old_selections = self.selections.interleaved::<usize>(cx);
         let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
             autoclose_pair
         } else {
@@ -2288,13 +2234,11 @@ impl Editor {
             snippet = None;
             text = completion.new_text.clone();
         };
+        let selections = self.selections.interleaved::<usize>(cx);
         let buffer = buffer_handle.read(cx);
         let old_range = completion.old_range.to_offset(&buffer);
         let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
 
-        let selections = self
-            .selections
-            .interleaved::<usize>(&self.buffer.read(cx).snapshot(cx));
         let newest_selection = self.selections.newest_anchor();
         if newest_selection.start.buffer_id != Some(buffer_handle.id()) {
             return None;
@@ -2816,9 +2760,7 @@ impl Editor {
 
     pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self
-            .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot);
+        let mut selections = self.selections.interleaved::<Point>(cx);
         for selection in &mut selections {
             if selection.is_empty() {
                 let old_head = selection.head();
@@ -2877,9 +2819,7 @@ impl Editor {
             return;
         }
 
-        let mut selections = self
-            .selections
-            .interleaved::<Point>(&self.buffer.read(cx).read(cx));
+        let mut selections = self.selections.interleaved::<Point>(cx);
         if selections.iter().all(|s| s.is_empty()) {
             self.transact(cx, |this, cx| {
                 this.buffer.update(cx, |buffer, cx| {
@@ -2914,9 +2854,7 @@ impl Editor {
     }
 
     pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
-        let mut selections = self
-            .selections
-            .interleaved::<Point>(&self.buffer.read(cx).read(cx));
+        let mut selections = self.selections.interleaved::<Point>(cx);
         self.transact(cx, |this, cx| {
             let mut last_indent = None;
             this.buffer.update(cx, |buffer, cx| {
@@ -2979,9 +2917,7 @@ impl Editor {
 
     pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let selections = self
-            .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot);
+        let selections = self.selections.interleaved::<Point>(cx);
         let mut deletion_ranges = Vec::new();
         let mut last_outdent = None;
         {
@@ -3024,18 +2960,17 @@ impl Editor {
                     cx,
                 );
             });
-            let snapshot = this.buffer.read(cx).snapshot(cx);
             this.change_selections(Some(Autoscroll::Fit), cx, |s| {
-                s.select(s.interleaved::<usize>(&snapshot))
+                // TODO: Make sure this is a reasonable change
+                // This used to call select(local_selections)
+                s.refresh()
             });
         });
     }
 
     pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let selections = self
-            .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot);
+        let selections = self.selections.interleaved::<Point>(cx);
 
         let mut new_cursors = Vec::new();
         let mut edit_ranges = Vec::new();
@@ -3117,7 +3052,7 @@ impl Editor {
     pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = &display_map.buffer_snapshot;
-        let selections = self.selections.interleaved::<Point>(buffer);
+        let selections = self.selections.interleaved::<Point>(cx);
 
         let mut edits = Vec::new();
         let mut selections_iter = selections.iter().peekable();
@@ -3162,7 +3097,7 @@ impl Editor {
         let mut unfold_ranges = Vec::new();
         let mut refold_ranges = Vec::new();
 
-        let selections = self.selections.interleaved::<Point>(&buffer);
+        let selections = self.selections.interleaved::<Point>(cx);
         let mut selections = selections.iter().peekable();
         let mut contiguous_row_selections = Vec::new();
         let mut new_selections = Vec::new();
@@ -3274,7 +3209,7 @@ impl Editor {
         let mut unfold_ranges = Vec::new();
         let mut refold_ranges = Vec::new();
 
-        let selections = self.selections.interleaved::<Point>(&buffer);
+        let selections = self.selections.interleaved::<Point>(cx);
         let mut selections = selections.iter().peekable();
         let mut contiguous_row_selections = Vec::new();
         let mut new_selections = Vec::new();
@@ -3412,9 +3347,10 @@ impl Editor {
                 edits
             });
             this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
-            let buffer = this.buffer.read(cx).snapshot(cx);
             this.change_selections(Some(Autoscroll::Fit), cx, |s| {
-                s.select(s.interleaved::<usize>(&buffer));
+                // TODO: Make sure this swap is reasonable.
+                // This was select(interleaved)
+                s.refresh();
             });
         });
     }
@@ -3422,7 +3358,7 @@ impl Editor {
     pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
         let mut text = String::new();
         let buffer = self.buffer.read(cx).snapshot(cx);
-        let mut selections = self.selections.interleaved::<Point>(&buffer);
+        let mut selections = self.selections.interleaved::<Point>(cx);
         let mut clipboard_selections = Vec::with_capacity(selections.len());
         {
             let max_point = buffer.max_point();
@@ -3455,8 +3391,8 @@ impl Editor {
     }
 
     pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
+        let selections = self.selections.interleaved::<Point>(cx);
         let buffer = self.buffer.read(cx).read(cx);
-        let selections = self.selections.interleaved::<Point>(&buffer);
         let mut text = String::new();
         let mut clipboard_selections = Vec::with_capacity(selections.len());
         {
@@ -3489,9 +3425,7 @@ impl Editor {
             if let Some(item) = cx.as_mut().read_from_clipboard() {
                 let mut clipboard_text = Cow::Borrowed(item.text());
                 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
-                    let old_selections = this
-                        .selections
-                        .interleaved::<usize>(&this.buffer.read(cx).read(cx));
+                    let old_selections = this.selections.interleaved::<usize>(cx);
                     let all_selections_were_entire_line =
                         clipboard_selections.iter().all(|s| s.is_entire_line);
                     if clipboard_selections.len() != old_selections.len() {
@@ -3544,9 +3478,7 @@ impl Editor {
                         buffer.edit_with_autoindent(edits, cx);
                     });
 
-                    let selections = this
-                        .selections
-                        .interleaved::<usize>(&this.buffer.read(cx).read(cx));
+                    let selections = this.selections.interleaved::<usize>(cx);
                     this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
                 } else {
                     this.insert(&clipboard_text, cx);
@@ -3970,9 +3902,7 @@ impl Editor {
     }
 
     pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
-        let mut selection = self
-            .selections
-            .last::<Point>(&self.buffer.read(cx).read(cx));
+        let mut selection = self.selections.last::<Point>(cx);
         selection.set_head(Point::zero(), SelectionGoal::None);
 
         self.change_selections(Some(Autoscroll::Fit), cx, |s| {
@@ -4031,7 +3961,7 @@ impl Editor {
 
     pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
         let buffer = self.buffer.read(cx).snapshot(cx);
-        let mut selection = self.selections.first::<usize>(&buffer);
+        let mut selection = self.selections.first::<usize>(cx);
         selection.set_head(buffer.len(), SelectionGoal::None);
         self.change_selections(Some(Autoscroll::Fit), cx, |s| {
             s.select(vec![selection]);
@@ -4047,9 +3977,7 @@ impl Editor {
 
     pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self
-            .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot);
+        let mut selections = self.selections.interleaved::<Point>(cx);
         let max_point = display_map.buffer_snapshot.max_point();
         for selection in &mut selections {
             let rows = selection.spanned_rows(true, &display_map);
@@ -4070,8 +3998,8 @@ impl Editor {
         let mut to_unfold = Vec::new();
         let mut new_selection_ranges = Vec::new();
         {
+            let selections = self.selections.interleaved::<Point>(cx);
             let buffer = self.buffer.read(cx).read(cx);
-            let selections = self.selections.interleaved::<Point>(&buffer);
             for selection in selections {
                 for row in selection.start.row..selection.end.row {
                     let cursor = Point::new(row, buffer.line_len(row));
@@ -4098,9 +4026,7 @@ impl Editor {
     fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
         self.push_to_selection_history();
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self
-            .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot);
+        let mut selections = self.selections.interleaved::<Point>(cx);
         let mut state = self.add_selections_state.take().unwrap_or_else(|| {
             let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
             let range = oldest_selection.display_range(&display_map).sorted();
@@ -4197,7 +4123,7 @@ impl Editor {
         self.push_to_selection_history();
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = &display_map.buffer_snapshot;
-        let mut selections = self.selections.interleaved::<usize>(&buffer);
+        let mut selections = self.selections.interleaved::<usize>(cx);
         if let Some(mut select_next_state) = self.select_next_state.take() {
             let query = &select_next_state.query;
             if !select_next_state.done {
@@ -4287,9 +4213,7 @@ impl Editor {
 
     pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
         self.transact(cx, |this, cx| {
-            let mut selections = this
-                .selections
-                .interleaved::<Point>(&this.buffer.read(cx).snapshot(cx));
+            let mut selections = this.selections.interleaved::<Point>(cx);
             let mut all_selection_lines_are_comments = true;
             let mut edit_ranges = Vec::new();
             let mut last_toggled_row = None;
@@ -4390,9 +4314,7 @@ impl Editor {
                 }
             });
 
-            let selections = this
-                .selections
-                .interleaved::<usize>(&this.buffer.read(cx).read(cx));
+            let selections = this.selections.interleaved::<usize>(cx);
             this.change_selections(Some(Autoscroll::Fit), cx, |s| {
                 s.select(selections);
             });
@@ -4406,10 +4328,7 @@ impl Editor {
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = self.buffer.read(cx).snapshot(cx);
-        let old_selections = self
-            .selections
-            .interleaved::<usize>(&buffer)
-            .into_boxed_slice();
+        let old_selections = self.selections.interleaved::<usize>(cx).into_boxed_slice();
 
         let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
         let mut selected_larger_node = false;
@@ -4469,7 +4388,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let buffer = self.buffer.read(cx).snapshot(cx);
-        let mut selections = self.selections.interleaved::<usize>(&buffer);
+        let mut selections = self.selections.interleaved::<usize>(cx);
         for selection in &mut selections {
             if let Some((open_range, close_range)) =
                 buffer.enclosing_bracket_ranges(selection.start..selection.end)
@@ -4534,7 +4453,7 @@ impl Editor {
 
     pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
         let buffer = self.buffer.read(cx).snapshot(cx);
-        let selection = self.selections.newest::<usize>(&buffer);
+        let selection = self.selections.newest::<usize>(cx);
         let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
             active_diagnostics
                 .primary_range
@@ -4619,7 +4538,7 @@ impl Editor {
 
         let editor = editor_handle.read(cx);
         let buffer = editor.buffer.read(cx);
-        let head = editor.selections.newest::<usize>(&buffer.read(cx)).head();
+        let head = editor.selections.newest::<usize>(cx).head();
         let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
             text_anchor
         } else {
@@ -4666,7 +4585,7 @@ impl Editor {
 
         let editor = editor_handle.read(cx);
         let buffer = editor.buffer.read(cx);
-        let head = editor.selections.newest::<usize>(&buffer.read(cx)).head();
+        let head = editor.selections.newest::<usize>(cx).head();
         let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
         let replica_id = editor.replica_id(cx);
 
@@ -4925,11 +4844,7 @@ impl Editor {
 
         if moving_cursor {
             let rename_editor = rename.editor.read(cx);
-            let rename_buffer = rename_editor.buffer.read(cx);
-            let cursor_in_rename_editor = rename_editor
-                .selections
-                .newest::<usize>(&rename_buffer.read(cx))
-                .head();
+            let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
 
             // Update the selection to match the position of the selection inside
             // the rename editor.
@@ -5092,10 +5007,9 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let old_cursor_position = self.selections.newest_anchor().head();
-        self.selections
-            .change_with(self.display_map.clone(), self.buffer.clone(), cx, |s| {
-                s.select_anchors(selections);
-            });
+        self.selections.change_with(cx, |s| {
+            s.select_anchors(selections);
+        });
         self.selections_did_change(false, &old_cursor_position, cx);
     }
 
@@ -5165,9 +5079,7 @@ impl Editor {
         let mut fold_ranges = Vec::new();
 
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let selections = self
-            .selections
-            .interleaved::<Point>(&display_map.buffer_snapshot);
+        let selections = self.selections.interleaved::<Point>(cx);
         for selection in selections {
             let range = selection.display_range(&display_map).sorted();
             let buffer_start_row = range.start.to_point(&display_map).row;
@@ -5191,7 +5103,7 @@ impl Editor {
     pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = &display_map.buffer_snapshot;
-        let selections = self.selections.interleaved::<Point>(buffer);
+        let selections = self.selections.interleaved::<Point>(cx);
         let ranges = selections
             .iter()
             .map(|s| {
@@ -5249,9 +5161,7 @@ impl Editor {
     }
 
     pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
-        let selections = self
-            .selections
-            .interleaved::<Point>(&self.buffer.read(cx).read(cx));
+        let selections = self.selections.interleaved::<Point>(cx);
         let ranges = selections.into_iter().map(|s| s.start..s.end);
         self.fold_ranges(ranges, cx);
     }
@@ -5628,7 +5538,7 @@ impl Editor {
         }
 
         let mut new_selections_by_buffer = HashMap::default();
-        for selection in editor.selections.interleaved::<usize>(&buffer.read(cx)) {
+        for selection in editor.selections.interleaved::<usize>(cx) {
             for (buffer, mut range) in
                 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
             {
@@ -6322,14 +6232,14 @@ mod tests {
             editor.insert("cd", cx);
             editor.end_transaction_at(now, cx);
             assert_eq!(editor.text(cx), "12cd56");
-            assert_eq!(editor.selected_ranges(cx), vec![4..4]);
+            assert_eq!(editor.selections.selected_ranges(cx), vec![4..4]);
 
             editor.start_transaction_at(now, cx);
             editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
             editor.insert("e", cx);
             editor.end_transaction_at(now, cx);
             assert_eq!(editor.text(cx), "12cde6");
-            assert_eq!(editor.selected_ranges(cx), vec![5..5]);
+            assert_eq!(editor.selections.selected_ranges(cx), vec![5..5]);
 
             now += group_interval + Duration::from_millis(1);
             editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
@@ -6343,30 +6253,30 @@ mod tests {
             });
 
             assert_eq!(editor.text(cx), "ab2cde6");
-            assert_eq!(editor.selected_ranges(cx), vec![3..3]);
+            assert_eq!(editor.selections.selected_ranges(cx), vec![3..3]);
 
             // Last transaction happened past the group interval in a different editor.
             // Undo it individually and don't restore selections.
             editor.undo(&Undo, cx);
             assert_eq!(editor.text(cx), "12cde6");
-            assert_eq!(editor.selected_ranges(cx), vec![2..2]);
+            assert_eq!(editor.selections.selected_ranges(cx), vec![2..2]);
 
             // First two transactions happened within the group interval in this editor.
             // Undo them together and restore selections.
             editor.undo(&Undo, cx);
             editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
             assert_eq!(editor.text(cx), "123456");
-            assert_eq!(editor.selected_ranges(cx), vec![0..0]);
+            assert_eq!(editor.selections.selected_ranges(cx), vec![0..0]);
 
             // Redo the first two transactions together.
             editor.redo(&Redo, cx);
             assert_eq!(editor.text(cx), "12cde6");
-            assert_eq!(editor.selected_ranges(cx), vec![5..5]);
+            assert_eq!(editor.selections.selected_ranges(cx), vec![5..5]);
 
             // Redo the last transaction on its own.
             editor.redo(&Redo, cx);
             assert_eq!(editor.text(cx), "ab2cde6");
-            assert_eq!(editor.selected_ranges(cx), vec![6..6]);
+            assert_eq!(editor.selections.selected_ranges(cx), vec![6..6]);
 
             // Test empty transactions.
             editor.start_transaction_at(now, cx);
@@ -6386,7 +6296,7 @@ mod tests {
             view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
         });
         assert_eq!(
-            editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
+            editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
         );
 
@@ -6395,7 +6305,7 @@ mod tests {
         });
 
         assert_eq!(
-            editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
+            editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
         );
 
@@ -6404,7 +6314,7 @@ mod tests {
         });
 
         assert_eq!(
-            editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
+            editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
         );
 
@@ -6414,7 +6324,7 @@ mod tests {
         });
 
         assert_eq!(
-            editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
+            editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)),
             [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
         );
 
@@ -6424,7 +6334,7 @@ mod tests {
         });
 
         assert_eq!(
-            editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
+            editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)),
             [
                 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
                 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
@@ -6436,7 +6346,7 @@ mod tests {
         });
 
         assert_eq!(
-            editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
+            editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)),
             [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
         );
     }
@@ -6450,7 +6360,7 @@ mod tests {
         view.update(cx, |view, cx| {
             view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
             );
         });
@@ -6458,7 +6368,7 @@ mod tests {
         view.update(cx, |view, cx| {
             view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
             );
         });
@@ -6467,7 +6377,7 @@ mod tests {
             view.cancel(&Cancel, cx);
             view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
             );
         });
@@ -6503,7 +6413,7 @@ mod tests {
             editor.navigate(nav_entry.data.unwrap(), cx);
             assert_eq!(nav_entry.item.id(), cx.view_id());
             assert_eq!(
-                editor.selected_display_ranges(cx),
+                editor.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
             );
             assert!(nav_history.borrow_mut().pop_backward().is_none());
@@ -6513,7 +6423,7 @@ mod tests {
             editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
             editor.end_selection(cx);
             assert_eq!(
-                editor.selected_display_ranges(cx),
+                editor.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
             );
             assert!(nav_history.borrow_mut().pop_backward().is_none());
@@ -6523,14 +6433,14 @@ mod tests {
             editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
             editor.end_selection(cx);
             assert_eq!(
-                editor.selected_display_ranges(cx),
+                editor.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
             );
             let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
             editor.navigate(nav_entry.data.unwrap(), cx);
             assert_eq!(nav_entry.item.id(), cx.view_id());
             assert_eq!(
-                editor.selected_display_ranges(cx),
+                editor.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
             );
             assert!(nav_history.borrow_mut().pop_backward().is_none());
@@ -6566,7 +6476,7 @@ mod tests {
                 cx,
             );
             assert_eq!(
-                editor.selected_display_ranges(cx),
+                editor.selections.selected_display_ranges(cx),
                 &[editor.max_point(cx)..editor.max_point(cx)]
             );
             assert_eq!(
@@ -6593,7 +6503,7 @@ mod tests {
             view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
             view.end_selection(cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 [
                     DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
                     DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
@@ -6604,7 +6514,7 @@ mod tests {
         view.update(cx, |view, cx| {
             view.cancel(&Cancel, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
             );
         });
@@ -6612,7 +6522,7 @@ mod tests {
         view.update(cx, |view, cx| {
             view.cancel(&Cancel, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
             );
         });
@@ -6723,43 +6633,43 @@ mod tests {
 
         view.update(cx, |view, cx| {
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
             );
 
             view.move_down(&MoveDown, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
             );
 
             view.move_right(&MoveRight, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
             );
 
             view.move_left(&MoveLeft, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
             );
 
             view.move_up(&MoveUp, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
             );
 
             view.move_to_end(&MoveToEnd, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
             );
 
             view.move_to_beginning(&MoveToBeginning, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
             );
 
@@ -6768,13 +6678,13 @@ mod tests {
             });
             view.select_to_beginning(&SelectToBeginning, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
             );
 
             view.select_to_end(&SelectToEnd, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
             );
         });
@@ -6802,80 +6712,80 @@ mod tests {
 
             view.move_right(&MoveRight, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(0, "ⓐ".len())]
             );
             view.move_right(&MoveRight, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(0, "ⓐⓑ".len())]
             );
             view.move_right(&MoveRight, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(0, "ⓐⓑ…".len())]
             );
 
             view.move_down(&MoveDown, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(1, "ab…".len())]
             );
             view.move_left(&MoveLeft, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(1, "ab".len())]
             );
             view.move_left(&MoveLeft, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(1, "a".len())]
             );
 
             view.move_down(&MoveDown, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(2, "α".len())]
             );
             view.move_right(&MoveRight, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(2, "αβ".len())]
             );
             view.move_right(&MoveRight, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(2, "αβ…".len())]
             );
             view.move_right(&MoveRight, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(2, "αβ…ε".len())]
             );
 
             view.move_up(&MoveUp, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(1, "ab…e".len())]
             );
             view.move_up(&MoveUp, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
             );
             view.move_left(&MoveLeft, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(0, "ⓐⓑ…".len())]
             );
             view.move_left(&MoveLeft, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(0, "ⓐⓑ".len())]
             );
             view.move_left(&MoveLeft, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(0, "ⓐ".len())]
             );
         });
@@ -6892,37 +6802,37 @@ mod tests {
             });
             view.move_down(&MoveDown, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(1, "abcd".len())]
             );
 
             view.move_down(&MoveDown, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(2, "αβγ".len())]
             );
 
             view.move_down(&MoveDown, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(3, "abcd".len())]
             );
 
             view.move_down(&MoveDown, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
             );
 
             view.move_up(&MoveUp, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(3, "abcd".len())]
             );
 
             view.move_up(&MoveUp, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[empty_range(2, "αβγ".len())]
             );
         });
@@ -6945,7 +6855,7 @@ mod tests {
         view.update(cx, |view, cx| {
             view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[
                     DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
                     DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
@@ -6956,7 +6866,7 @@ mod tests {
         view.update(cx, |view, cx| {
             view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[
                     DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
                     DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
@@ -6967,7 +6877,7 @@ mod tests {
         view.update(cx, |view, cx| {
             view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
             assert_eq!(
-                view.selected_display_ranges(cx),
+                view.selections.selected_display_ranges(cx),
                 &[
                     DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
                     DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),

crates/editor/src/element.rs 🔗

@@ -959,7 +959,7 @@ impl Element for EditorElement {
             if view.show_local_selections {
                 let local_selections = view
                     .selections
-                    .interleaved_in_range(start_anchor..end_anchor, &display_map.buffer_snapshot);
+                    .interleaved_in_range(start_anchor..end_anchor, cx);
                 for selection in &local_selections {
                     let is_empty = selection.start == selection.end;
                     let selection_start = snapshot.prev_line_boundary(selection.start).1;
@@ -1043,7 +1043,7 @@ impl Element for EditorElement {
 
             let newest_selection_head = view
                 .selections
-                .newest::<usize>(&snapshot.buffer_snapshot)
+                .newest::<usize>(cx)
                 .head()
                 .to_display_point(&snapshot);
 

crates/editor/src/items.rs 🔗

@@ -252,13 +252,13 @@ fn deserialize_selection(
 impl Item for Editor {
     fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
         if let Ok(data) = data.downcast::<NavigationData>() {
+            let newest_selection = self.selections.newest::<Point>(cx);
             let buffer = self.buffer.read(cx).read(cx);
             let offset = if buffer.can_resolve(&data.cursor_anchor) {
                 data.cursor_anchor.to_point(&buffer)
             } else {
                 buffer.clip_point(data.cursor_position, Bias::Left)
             };
-            let newest_selection = self.selections.newest::<Point>(&buffer);
 
             let scroll_top_anchor = if buffer.can_resolve(&data.scroll_top_anchor) {
                 data.scroll_top_anchor
@@ -465,7 +465,7 @@ impl CursorPosition {
 
         self.selected_count = 0;
         let mut last_selection: Option<Selection<usize>> = None;
-        for selection in editor.selections.interleaved::<usize>(&buffer) {
+        for selection in editor.selections.interleaved::<usize>(cx) {
             self.selected_count += selection.end - selection.start;
             if last_selection
                 .as_ref()

crates/editor/src/selections_collection.rs 🔗

@@ -5,7 +5,7 @@ use std::{
 };
 
 use collections::HashMap;
-use gpui::{ModelHandle, MutableAppContext};
+use gpui::{AppContext, ModelHandle, MutableAppContext};
 use itertools::Itertools;
 use language::{rope::TextDimension, Bias, Point, Selection, SelectionGoal, ToPoint};
 use util::post_inc;
@@ -22,14 +22,18 @@ pub struct PendingSelection {
 }
 
 pub struct SelectionsCollection {
+    display_map: ModelHandle<DisplayMap>,
+    buffer: ModelHandle<MultiBuffer>,
     pub next_selection_id: usize,
     disjoint: Arc<[Selection<Anchor>]>,
     pending: Option<PendingSelection>,
 }
 
 impl SelectionsCollection {
-    pub fn new() -> Self {
+    pub fn new(display_map: ModelHandle<DisplayMap>, buffer: ModelHandle<MultiBuffer>) -> Self {
         Self {
+            display_map,
+            buffer,
             next_selection_id: 1,
             disjoint: Arc::from([]),
             pending: Some(PendingSelection {
@@ -45,6 +49,14 @@ impl SelectionsCollection {
         }
     }
 
+    fn display_map(&self, cx: &mut MutableAppContext) -> DisplaySnapshot {
+        self.display_map.update(cx, |map, cx| map.snapshot(cx))
+    }
+
+    fn buffer(&self, cx: &AppContext) -> MultiBufferSnapshot {
+        self.buffer.read(cx).snapshot(cx)
+    }
+
     pub fn count<'a>(&self) -> usize {
         let mut count = self.disjoint.len();
         if self.pending.is_some() {
@@ -65,25 +77,26 @@ impl SelectionsCollection {
 
     pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        snapshot: &MultiBufferSnapshot,
+        cx: &AppContext,
     ) -> Option<Selection<D>> {
         self.pending_anchor()
             .as_ref()
-            .map(|pending| pending.map(|p| p.summary::<D>(&snapshot)))
+            .map(|pending| pending.map(|p| p.summary::<D>(&self.buffer(cx))))
     }
 
     pub fn pending_mode(&self) -> Option<SelectMode> {
         self.pending.as_ref().map(|pending| pending.mode.clone())
     }
 
-    pub fn interleaved<'a, D>(&self, buffer: &MultiBufferSnapshot) -> Vec<Selection<D>>
+    pub fn interleaved<'a, D>(&self, cx: &AppContext) -> Vec<Selection<D>>
     where
         D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
     {
         let disjoint_anchors = &self.disjoint;
-        let mut disjoint = resolve_multiple::<D, _>(disjoint_anchors.iter(), &buffer).peekable();
+        let mut disjoint =
+            resolve_multiple::<D, _>(disjoint_anchors.iter(), &self.buffer(cx)).peekable();
 
-        let mut pending_opt = self.pending::<D>(&buffer);
+        let mut pending_opt = self.pending::<D>(cx);
 
         iter::from_fn(move || {
             if let Some(pending) = pending_opt.as_mut() {
@@ -114,8 +127,9 @@ impl SelectionsCollection {
     pub fn interleaved_in_range<'a>(
         &self,
         range: Range<Anchor>,
-        buffer: &MultiBufferSnapshot,
+        cx: &AppContext,
     ) -> Vec<Selection<Point>> {
+        let buffer = self.buffer(cx);
         let start_ix = match self
             .disjoint
             .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer))
@@ -162,9 +176,9 @@ impl SelectionsCollection {
 
     pub fn newest<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        snapshot: &MultiBufferSnapshot,
+        cx: &AppContext,
     ) -> Selection<D> {
-        resolve(self.newest_anchor(), snapshot)
+        resolve(self.newest_anchor(), &self.buffer(cx))
     }
 
     pub fn oldest_anchor(&self) -> &Selection<Anchor> {
@@ -177,36 +191,65 @@ impl SelectionsCollection {
 
     pub fn oldest<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        snapshot: &MultiBufferSnapshot,
+        cx: &AppContext,
     ) -> Selection<D> {
-        resolve(self.oldest_anchor(), snapshot)
+        resolve(self.oldest_anchor(), &self.buffer(cx))
     }
 
     pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        snapshot: &MultiBufferSnapshot,
+        cx: &AppContext,
     ) -> Selection<D> {
-        self.interleaved(&snapshot).first().unwrap().clone()
+        self.interleaved(cx).first().unwrap().clone()
     }
 
     pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(
         &self,
-        snapshot: &MultiBufferSnapshot,
+        cx: &AppContext,
     ) -> Selection<D> {
-        self.interleaved(&snapshot).last().unwrap().clone()
+        self.interleaved(cx).last().unwrap().clone()
+    }
+
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn selected_ranges<D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug>(
+        &self,
+        cx: &AppContext,
+    ) -> Vec<Range<D>> {
+        self.interleaved::<D>(cx)
+            .iter()
+            .map(|s| {
+                if s.reversed {
+                    s.end.clone()..s.start.clone()
+                } else {
+                    s.start.clone()..s.end.clone()
+                }
+            })
+            .collect()
+    }
+
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec<Range<DisplayPoint>> {
+        let display_map = self.display_map(cx);
+        self.disjoint_anchors()
+            .iter()
+            .chain(self.pending_anchor().as_ref())
+            .map(|s| {
+                if s.reversed {
+                    s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
+                } else {
+                    s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
+                }
+            })
+            .collect()
     }
 
     pub(crate) fn change_with<R>(
         &mut self,
-        display_map: ModelHandle<DisplayMap>,
-        buffer: ModelHandle<MultiBuffer>,
         cx: &mut MutableAppContext,
         change: impl FnOnce(&mut MutableSelectionsCollection) -> R,
     ) -> R {
         let mut mutable_collection = MutableSelectionsCollection {
             collection: self,
-            display_map,
-            buffer,
             cx,
         };
 
@@ -221,12 +264,18 @@ impl SelectionsCollection {
 
 pub struct MutableSelectionsCollection<'a> {
     collection: &'a mut SelectionsCollection,
-    buffer: ModelHandle<MultiBuffer>,
-    display_map: ModelHandle<DisplayMap>,
     cx: &'a mut MutableAppContext,
 }
 
 impl<'a> MutableSelectionsCollection<'a> {
+    fn display_map(&mut self) -> DisplaySnapshot {
+        self.collection.display_map(self.cx)
+    }
+
+    fn buffer(&mut self) -> MultiBufferSnapshot {
+        self.collection.buffer(self.cx)
+    }
+
     pub fn clear_disjoint(&mut self) {
         self.collection.disjoint = Arc::from([]);
     }
@@ -303,19 +352,11 @@ impl<'a> MutableSelectionsCollection<'a> {
 
     pub fn insert_range<T>(&mut self, range: Range<T>)
     where
-        T: 'a
-            + ToOffset
-            + ToPoint
-            + TextDimension
-            + Ord
-            + Sub<T, Output = T>
-            + std::marker::Copy
-            + std::fmt::Debug,
+        T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy,
     {
-        let buffer = self.buffer.read(self.cx).snapshot(self.cx);
-        let mut selections = self.interleaved(&buffer);
-        let mut start = range.start.to_offset(&buffer);
-        let mut end = range.end.to_offset(&buffer);
+        let mut selections = self.interleaved(self.cx);
+        let mut start = range.start.to_offset(&self.buffer());
+        let mut end = range.end.to_offset(&self.buffer());
         let reversed = if start > end {
             mem::swap(&mut start, &mut end);
             true
@@ -440,7 +481,7 @@ impl<'a> MutableSelectionsCollection<'a> {
     where
         T: IntoIterator<Item = Range<DisplayPoint>>,
     {
-        let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx));
+        let display_map = self.display_map();
         let selections = ranges
             .into_iter()
             .map(|range| {
@@ -468,9 +509,9 @@ impl<'a> MutableSelectionsCollection<'a> {
         &mut self,
         mut move_selection: impl FnMut(&DisplaySnapshot, &mut Selection<DisplayPoint>),
     ) {
-        let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx));
+        let display_map = self.display_map();
         let selections = self
-            .interleaved::<Point>(&display_map.buffer_snapshot)
+            .interleaved::<Point>(self.cx)
             .into_iter()
             .map(|selection| {
                 let mut selection = selection.map(|point| point.to_display_point(&display_map));
@@ -514,7 +555,7 @@ impl<'a> MutableSelectionsCollection<'a> {
         &mut self,
         mut find_replacement_cursors: impl FnMut(&DisplaySnapshot) -> Vec<DisplayPoint>,
     ) {
-        let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx));
+        let display_map = self.display_map();
         let new_selections = find_replacement_cursors(&display_map)
             .into_iter()
             .map(|cursor| {

crates/editor/src/test.rs 🔗

@@ -54,5 +54,5 @@ pub fn assert_text_with_selections(
     let (unmarked_text, text_ranges) = marked_text_ranges(marked_text);
 
     assert_eq!(editor.text(cx), unmarked_text);
-    assert_eq!(editor.selected_ranges(cx), text_ranges);
+    assert_eq!(editor.selections.selected_ranges(cx), text_ranges);
 }

crates/go_to_line/src/go_to_line.rs 🔗

@@ -43,7 +43,7 @@ impl GoToLine {
             let buffer = editor.buffer().read(cx).read(cx);
             (
                 Some(scroll_position),
-                editor.selections.newest(&buffer).head(),
+                editor.selections.newest(cx).head(),
                 buffer.max_point(),
             )
         });

crates/outline/src/outline.rs 🔗

@@ -172,7 +172,7 @@ impl PickerDelegate for OutlineView {
 
             let editor = self.active_editor.read(cx);
             let buffer = editor.buffer().read(cx).read(cx);
-            let cursor_offset = editor.selections.newest::<usize>(&buffer).head();
+            let cursor_offset = editor.selections.newest::<usize>(cx).head();
             selected_index = self
                 .outline
                 .items

crates/search/src/buffer_search.rs 🔗

@@ -225,10 +225,7 @@ impl BufferSearchBar {
         let display_map = editor
             .update(cx, |editor, cx| editor.snapshot(cx))
             .display_snapshot;
-        let selection = editor
-            .read(cx)
-            .selections
-            .newest::<usize>(&display_map.buffer_snapshot);
+        let selection = editor.read(cx).selections.newest::<usize>(cx);
 
         let mut text: String;
         if selection.start == selection.end {
@@ -732,7 +729,9 @@ mod tests {
             assert_eq!(search_bar.active_match_index, Some(0));
             search_bar.select_next_match(&SelectNextMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
             );
         });
@@ -743,7 +742,9 @@ mod tests {
         search_bar.update(cx, |search_bar, cx| {
             search_bar.select_next_match(&SelectNextMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
             );
         });
@@ -754,7 +755,9 @@ mod tests {
         search_bar.update(cx, |search_bar, cx| {
             search_bar.select_next_match(&SelectNextMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
             );
         });
@@ -765,7 +768,9 @@ mod tests {
         search_bar.update(cx, |search_bar, cx| {
             search_bar.select_next_match(&SelectNextMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
             );
         });
@@ -776,7 +781,9 @@ mod tests {
         search_bar.update(cx, |search_bar, cx| {
             search_bar.select_prev_match(&SelectPrevMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
             );
         });
@@ -787,7 +794,9 @@ mod tests {
         search_bar.update(cx, |search_bar, cx| {
             search_bar.select_prev_match(&SelectPrevMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
             );
         });
@@ -798,7 +807,9 @@ mod tests {
         search_bar.update(cx, |search_bar, cx| {
             search_bar.select_prev_match(&SelectPrevMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
             );
         });
@@ -817,7 +828,9 @@ mod tests {
             assert_eq!(search_bar.active_match_index, Some(1));
             search_bar.select_prev_match(&SelectPrevMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
             );
         });
@@ -836,7 +849,9 @@ mod tests {
             assert_eq!(search_bar.active_match_index, Some(1));
             search_bar.select_next_match(&SelectNextMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
             );
         });
@@ -855,7 +870,9 @@ mod tests {
             assert_eq!(search_bar.active_match_index, Some(2));
             search_bar.select_prev_match(&SelectPrevMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
             );
         });
@@ -874,7 +891,9 @@ mod tests {
             assert_eq!(search_bar.active_match_index, Some(2));
             search_bar.select_next_match(&SelectNextMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
             );
         });
@@ -893,7 +912,9 @@ mod tests {
             assert_eq!(search_bar.active_match_index, Some(0));
             search_bar.select_prev_match(&SelectPrevMatch, cx);
             assert_eq!(
-                editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
             );
         });

crates/search/src/project_search.rs 🔗

@@ -891,7 +891,7 @@ mod tests {
             assert_eq!(
                 search_view
                     .results_editor
-                    .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                    .update(cx, |editor, cx| editor.selections.selected_display_ranges(cx)),
                 [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)]
             );
 
@@ -901,9 +901,9 @@ mod tests {
         search_view.update(cx, |search_view, cx| {
             assert_eq!(search_view.active_match_index, Some(1));
             assert_eq!(
-                search_view
-                    .results_editor
-                    .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                search_view.results_editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)]
             );
             search_view.select_match(Direction::Next, cx);
@@ -912,9 +912,9 @@ mod tests {
         search_view.update(cx, |search_view, cx| {
             assert_eq!(search_view.active_match_index, Some(2));
             assert_eq!(
-                search_view
-                    .results_editor
-                    .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                search_view.results_editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)]
             );
             search_view.select_match(Direction::Next, cx);
@@ -923,9 +923,9 @@ mod tests {
         search_view.update(cx, |search_view, cx| {
             assert_eq!(search_view.active_match_index, Some(0));
             assert_eq!(
-                search_view
-                    .results_editor
-                    .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                search_view.results_editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)]
             );
             search_view.select_match(Direction::Prev, cx);
@@ -934,9 +934,9 @@ mod tests {
         search_view.update(cx, |search_view, cx| {
             assert_eq!(search_view.active_match_index, Some(2));
             assert_eq!(
-                search_view
-                    .results_editor
-                    .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                search_view.results_editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)]
             );
             search_view.select_match(Direction::Prev, cx);
@@ -945,9 +945,9 @@ mod tests {
         search_view.update(cx, |search_view, cx| {
             assert_eq!(search_view.active_match_index, Some(1));
             assert_eq!(
-                search_view
-                    .results_editor
-                    .update(cx, |editor, cx| editor.selected_display_ranges(cx)),
+                search_view.results_editor.update(cx, |editor, cx| editor
+                    .selections
+                    .selected_display_ranges(cx)),
                 [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)]
             );
         });

crates/vim/src/vim_test_context.rs 🔗

@@ -200,7 +200,7 @@ impl<'a> VimTestContext<'a> {
             self.editor.read_with(self.cx, |editor, cx| {
                 let (empty_selections, non_empty_selections): (Vec<_>, Vec<_>) = editor
                     .selections
-                    .interleaved::<usize>(&editor.buffer().read(cx).read(cx))
+                    .interleaved::<usize>(cx)
                     .into_iter()
                     .partition_map(|selection| {
                         if selection.is_empty() {

crates/zed/src/zed.rs 🔗

@@ -1180,7 +1180,7 @@ mod tests {
                 let editor = item.downcast::<Editor>().unwrap();
                 let (selections, scroll_position) = editor.update(cx, |editor, cx| {
                     (
-                        editor.selected_display_ranges(cx),
+                        editor.selections.selected_display_ranges(cx),
                         editor.scroll_position(cx),
                     )
                 });