Hold the brackets lock less

Kirill Bulatov created

Change summary

crates/language/src/buffer.rs           | 43 ++++++++++++++++++--------
crates/language/src/buffer/row_chunk.rs | 12 +++++--
2 files changed, 37 insertions(+), 18 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -32,7 +32,7 @@ use gpui::{
 };
 
 use lsp::{LanguageServerId, NumberOrString};
-use parking_lot::Mutex;
+use parking_lot::{Mutex, RawMutex, lock_api::MutexGuard};
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 use settings::WorktreeId;
@@ -132,7 +132,7 @@ pub struct Buffer {
     tree_sitter_data: Arc<Mutex<TreeSitterData>>,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct TreeSitterData {
     chunks: RowChunks,
     brackets_by_chunks: Vec<Option<Vec<BracketMatch>>>,
@@ -4149,23 +4149,16 @@ impl BufferSnapshot {
     ///
     /// The resulting collection is not ordered.
     fn fetch_bracket_ranges(&self, range: Range<usize>) -> Vec<BracketMatch> {
-        let mut tree_sitter_data = self.tree_sitter_data.lock();
-        if self
-            .version
-            .changed_since(&tree_sitter_data.chunks.version())
-        {
-            *tree_sitter_data = TreeSitterData::new(self.text.clone());
-        }
-
+        let mut tree_sitter_data = self.latest_tree_sitter_data().clone();
+        let mut new_bracket_matches = HashMap::default();
         let mut all_bracket_matches = Vec::new();
         for chunk in tree_sitter_data
             .chunks
             .applicable_chunks(&[self.anchor_before(range.start)..self.anchor_after(range.end)])
-            .collect::<Vec<_>>()
         {
-            let chunk_brackets = &mut tree_sitter_data.brackets_by_chunks[chunk.id];
+            let chunk_brackets = tree_sitter_data.brackets_by_chunks.remove(chunk.id);
             let bracket_matches = match chunk_brackets {
-                Some(cached_brackets) => cached_brackets.clone(),
+                Some(cached_brackets) => cached_brackets,
                 None => {
                     let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
                         grammar.brackets_config.as_ref().map(|c| &c.query)
@@ -4216,16 +4209,38 @@ impl BufferSnapshot {
                         None
                     })
                     .collect::<Vec<_>>();
-                    *chunk_brackets = Some(new_matches.clone());
+
+                    new_bracket_matches.insert(chunk.id, new_matches.clone());
                     new_matches
                 }
             };
             all_bracket_matches.extend(bracket_matches);
         }
 
+        let mut latest_tree_sitter_data = self.latest_tree_sitter_data();
+        if latest_tree_sitter_data.chunks.version() == &self.version {
+            for (chunk_id, new_matches) in new_bracket_matches {
+                let old_chunks = &mut latest_tree_sitter_data.brackets_by_chunks[chunk_id];
+                if old_chunks.is_none() {
+                    *old_chunks = Some(new_matches);
+                }
+            }
+        }
+
         all_bracket_matches
     }
 
+    fn latest_tree_sitter_data(&self) -> MutexGuard<'_, RawMutex, TreeSitterData> {
+        let mut tree_sitter_data = self.tree_sitter_data.lock();
+        if self
+            .version
+            .changed_since(tree_sitter_data.chunks.version())
+        {
+            *tree_sitter_data = TreeSitterData::new(self.text.clone());
+        }
+        tree_sitter_data
+    }
+
     pub fn all_bracket_ranges(&self, range: Range<usize>) -> Vec<BracketMatch> {
         self.fetch_bracket_ranges(range)
     }

crates/language/src/buffer/row_chunk.rs 🔗

@@ -1,7 +1,7 @@
 //! A row chunk is an exclusive range of rows, [`BufferRow`] within a buffer of a certain version, [`Global`].
 //! All but the last chunk are of a constant, given size.
 
-use std::ops::Range;
+use std::{ops::Range, sync::Arc};
 
 use clock::Global;
 use text::OffsetRangeExt as _;
@@ -16,9 +16,10 @@ use crate::BufferRow;
 /// Together, chunks form entire document at a particular version [`Global`].
 /// Each chunk is queried for inlays as `(start_row, 0)..(end_exclusive, 0)` via
 /// <https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHintParams>
+#[derive(Clone)]
 pub struct RowChunks {
     pub snapshot: text::BufferSnapshot,
-    pub chunks: Vec<RowChunk>,
+    pub chunks: Arc<[RowChunk]>,
 }
 
 impl std::fmt::Debug for RowChunks {
@@ -42,8 +43,11 @@ impl RowChunks {
                 start: chunk_start,
                 end_exclusive: (chunk_start + max_rows_per_chunk).min(last_row),
             })
-            .collect();
-        Self { snapshot, chunks }
+            .collect::<Vec<_>>();
+        Self {
+            snapshot,
+            chunks: Arc::from(chunks),
+        }
     }
 
     pub fn version(&self) -> &Global {