Reduce accessibility of multibuffer read to reduce risk of borrowing snapshot and buffer refcells twice

Keith Simmons created

Change summary

crates/breadcrumbs/src/breadcrumbs.rs |  4 +---
crates/diagnostics/src/diagnostics.rs |  4 ++--
crates/diagnostics/src/items.rs       |  2 +-
crates/editor/src/multi_buffer.rs     | 23 ++++++++++++++++++++++-
crates/go_to_line/src/go_to_line.rs   |  4 ++--
crates/journal/src/journal.rs         |  2 +-
crates/outline/src/outline.rs         |  4 ++--
crates/search/src/buffer_search.rs    |  6 +++---
crates/search/src/project_search.rs   |  4 ++--
9 files changed, 36 insertions(+), 17 deletions(-)

Detailed changes

crates/breadcrumbs/src/breadcrumbs.rs 🔗

@@ -39,9 +39,7 @@ impl Breadcrumbs {
         let editor = self.editor.as_ref()?.read(cx);
         let cursor = editor.selections.newest_anchor().head();
         let multibuffer = &editor.buffer().read(cx);
-        let (buffer_id, symbols) = multibuffer
-            .read(cx)
-            .symbols_containing(cursor, Some(theme))?;
+        let (buffer_id, symbols) = multibuffer.symbols_containing(cursor, Some(theme), cx)?;
         let buffer = multibuffer.buffer(buffer_id)?;
         Some((buffer, symbols))
     }

crates/diagnostics/src/diagnostics.rs 🔗

@@ -489,11 +489,11 @@ impl workspace::Item for ProjectDiagnosticsEditor {
     }
 
     fn is_dirty(&self, cx: &AppContext) -> bool {
-        self.excerpts.read(cx).read(cx).is_dirty()
+        self.excerpts.read(cx).is_dirty(cx)
     }
 
     fn has_conflict(&self, cx: &AppContext) -> bool {
-        self.excerpts.read(cx).read(cx).has_conflict()
+        self.excerpts.read(cx).has_conflict(cx)
     }
 
     fn can_save(&self, _: &AppContext) -> bool {

crates/diagnostics/src/items.rs 🔗

@@ -60,7 +60,7 @@ impl DiagnosticIndicator {
         let buffer = editor.buffer().read(cx);
         let cursor_position = editor.selections.newest::<usize>(cx).head();
         let new_diagnostic = buffer
-            .read(cx)
+            .snapshot(cx)
             .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false)
             .filter(|entry| !entry.range.is_empty())
             .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))

crates/editor/src/multi_buffer.rs 🔗

@@ -226,7 +226,7 @@ impl MultiBuffer {
         self.snapshot.borrow().clone()
     }
 
-    pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
+    pub(crate) fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
         self.sync(cx);
         self.snapshot.borrow()
     }
@@ -255,6 +255,27 @@ impl MultiBuffer {
         self.subscriptions.subscribe()
     }
 
+    pub fn is_dirty(&self, cx: &AppContext) -> bool {
+        self.read(cx).is_dirty()
+    }
+
+    pub fn has_conflict(&self, cx: &AppContext) -> bool {
+        self.read(cx).has_conflict()
+    }
+
+    pub fn len(&self, cx: &AppContext) -> usize {
+        self.read(cx).len()
+    }
+
+    pub fn symbols_containing<T: ToOffset>(
+        &self,
+        offset: T,
+        theme: Option<&SyntaxTheme>,
+        cx: &AppContext,
+    ) -> Option<(usize, Vec<OutlineItem<Anchor>>)> {
+        self.read(cx).symbols_containing(offset, theme)
+    }
+
     pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ModelContext<Self>)
     where
         I: IntoIterator<Item = (Range<S>, T)>,

crates/go_to_line/src/go_to_line.rs 🔗

@@ -40,7 +40,7 @@ impl GoToLine {
 
         let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
             let scroll_position = editor.scroll_position(cx);
-            let buffer = editor.buffer().read(cx).read(cx);
+            let buffer = editor.buffer().read(cx).snapshot(cx);
             (
                 Some(scroll_position),
                 editor.selections.newest(cx).head(),
@@ -108,7 +108,7 @@ impl GoToLine {
         match event {
             editor::Event::Blurred => cx.emit(Event::Dismissed),
             editor::Event::BufferEdited { .. } => {
-                let line_editor = self.line_editor.read(cx).buffer().read(cx).read(cx).text();
+                let line_editor = self.line_editor.read(cx).text(cx);
                 let mut components = line_editor.trim().split(&[',', ':'][..]);
                 let row = components.next().and_then(|row| row.parse::<u32>().ok());
                 let column = components.next().and_then(|row| row.parse::<u32>().ok());

crates/journal/src/journal.rs 🔗

@@ -56,7 +56,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
             if let Some(Some(Ok(item))) = opened.first() {
                 if let Some(editor) = item.downcast::<Editor>() {
                     editor.update(&mut cx, |editor, cx| {
-                        let len = editor.buffer().read(cx).read(cx).len();
+                        let len = editor.buffer().read(cx).len(cx);
                         editor.change_selections(Some(Autoscroll::Center), cx, |s| {
                             s.select_ranges([len..len])
                         });

crates/outline/src/outline.rs 🔗

@@ -84,7 +84,7 @@ impl OutlineView {
                 .read(cx)
                 .buffer()
                 .read(cx)
-                .read(cx)
+                .snapshot(cx)
                 .outline(Some(cx.global::<Settings>().theme.editor.syntax.as_ref()));
             if let Some(outline) = buffer {
                 workspace.toggle_modal(cx, |_, cx| {
@@ -171,8 +171,8 @@ impl PickerDelegate for OutlineView {
                 .collect();
 
             let editor = self.active_editor.read(cx);
-            let buffer = editor.buffer().read(cx).read(cx);
             let cursor_offset = editor.selections.newest::<usize>(cx).head();
+            let buffer = editor.buffer().read(cx).snapshot(cx);
             selected_index = self
                 .outline
                 .items

crates/search/src/buffer_search.rs 🔗

@@ -265,7 +265,7 @@ impl BufferSearchBar {
     fn set_query(&mut self, query: &str, cx: &mut ViewContext<Self>) {
         self.query_editor.update(cx, |query_editor, cx| {
             query_editor.buffer().update(cx, |query_buffer, cx| {
-                let len = query_buffer.read(cx).len();
+                let len = query_buffer.len(cx);
                 query_buffer.edit([(0..len, query)], cx);
             });
         });
@@ -388,7 +388,7 @@ impl BufferSearchBar {
                             &editor.selections.newest_anchor().head(),
                             index,
                             direction,
-                            &editor.buffer().read(cx).read(cx),
+                            &editor.buffer().read(cx).snapshot(cx),
                         );
                         let range_to_select = ranges[new_index].clone();
                         editor.unfold_ranges([range_to_select.clone()], false, cx);
@@ -565,7 +565,7 @@ impl BufferSearchBar {
             active_match_index(
                 &ranges,
                 &editor.selections.newest_anchor().head(),
-                &editor.buffer().read(cx).read(cx),
+                &editor.buffer().read(cx).snapshot(cx),
             )
         });
         if new_index != self.active_match_index {

crates/search/src/project_search.rs 🔗

@@ -457,7 +457,7 @@ impl ProjectSearchView {
                 &results_editor.selections.newest_anchor().head(),
                 index,
                 direction,
-                &results_editor.buffer().read(cx).read(cx),
+                &results_editor.buffer().read(cx).snapshot(cx),
             );
             let range_to_select = model.match_ranges[new_index].clone();
             self.results_editor.update(cx, |editor, cx| {
@@ -515,7 +515,7 @@ impl ProjectSearchView {
         let new_index = active_match_index(
             &self.model.read(cx).match_ranges,
             &results_editor.selections.newest_anchor().head(),
-            &results_editor.buffer().read(cx).read(cx),
+            &results_editor.buffer().read(cx).snapshot(cx),
         );
         if self.active_match_index != new_index {
             self.active_match_index = new_index;