buffer_diff: Do not block on parsing in `set_snapshot_with_secondary_inner` (#50385)

Lukas Wirth created

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

crates/buffer_diff/src/buffer_diff.rs                         |  2 
crates/edit_prediction_context/src/edit_prediction_context.rs | 14 ++--
crates/editor/src/inlays.rs                                   |  6 +
crates/editor/src/inlays/inlay_hints.rs                       |  2 
crates/language/src/buffer.rs                                 | 14 ++++
crates/language_tools/src/highlights_tree_view.rs             |  8 +-
crates/rope/src/rope.rs                                       | 13 +++-
7 files changed, 37 insertions(+), 22 deletions(-)

Detailed changes

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -1753,6 +1753,7 @@ impl BufferDiff {
         let should_compare_hunks = update.base_text_edits.is_some() || !base_text_changed;
         let parsing_idle = if let Some(diff) = update.base_text_edits {
             state.base_text.update(cx, |base_text, cx| {
+                base_text.set_sync_parse_timeout(None);
                 base_text.set_capability(Capability::ReadWrite, cx);
                 base_text.apply_diff(diff, cx);
                 base_text.set_capability(Capability::ReadOnly, cx);
@@ -1760,6 +1761,7 @@ impl BufferDiff {
             })
         } else if update.base_text_changed {
             state.base_text.update(cx, |base_text, cx| {
+                base_text.set_sync_parse_timeout(None);
                 base_text.set_capability(Capability::ReadWrite, cx);
                 base_text.set_text(new_state.base_text.clone(), cx);
                 base_text.set_capability(Capability::ReadOnly, cx);

crates/edit_prediction_context/src/edit_prediction_context.rs 🔗

@@ -644,14 +644,12 @@ fn identifiers_for_position(
     let outer_range =
         ranges.first().map_or(0, |r| r.start)..ranges.last().map_or(buffer.len(), |r| r.end);
 
-    let mut captures = buffer
-        .syntax
-        .captures(outer_range.clone(), &buffer.text, |grammar| {
-            grammar
-                .highlights_config
-                .as_ref()
-                .map(|config| &config.query)
-        });
+    let mut captures = buffer.captures(outer_range.clone(), |grammar| {
+        grammar
+            .highlights_config
+            .as_ref()
+            .map(|config| &config.query)
+    });
 
     for range in ranges {
         captures.set_byte_range(range.start..outer_range.end);

crates/editor/src/inlays.rs 🔗

@@ -58,10 +58,12 @@ pub enum InlayContent {
 impl Inlay {
     pub fn hint(id: InlayId, position: Anchor, hint: &InlayHint) -> Self {
         let mut text = hint.text();
-        if hint.padding_right && text.reversed_chars_at(text.len()).next() != Some(' ') {
+        let needs_right_padding = hint.padding_right && !text.ends_with(" ");
+        let needs_left_padding = hint.padding_left && !text.starts_with(" ");
+        if needs_right_padding {
             text.push(" ");
         }
-        if hint.padding_left && text.chars_at(0).next() != Some(' ') {
+        if needs_left_padding {
             text.push_front(" ");
         }
         Self {

crates/editor/src/inlays/inlay_hints.rs 🔗

@@ -369,7 +369,7 @@ impl Editor {
         if invalidate_cache.should_invalidate() {
             if invalidate_hints_for_buffers.is_empty() {
                 inlay_hints.clear();
-            } else if invalidate_cache.should_invalidate() {
+            } else {
                 inlay_hints.clear_for_buffers(
                     &invalidate_hints_for_buffers,
                     Self::visible_inlay_hints(self.display_map.read(cx)),

crates/language/src/buffer.rs 🔗

@@ -187,7 +187,7 @@ struct BufferBranchState {
 /// state of a buffer.
 pub struct BufferSnapshot {
     pub text: text::BufferSnapshot,
-    pub syntax: SyntaxSnapshot,
+    pub(crate) syntax: SyntaxSnapshot,
     tree_sitter_data: Arc<TreeSitterData>,
     diagnostics: TreeMap<LanguageServerId, DiagnosticSet>,
     remote_selections: TreeMap<ReplicaId, SelectionSet>,
@@ -1776,7 +1776,9 @@ impl Buffer {
         self.syntax_map.lock().contains_unknown_injections()
     }
 
-    #[cfg(any(test, feature = "test-support"))]
+    /// Sets the sync parse timeout for this buffer.
+    ///
+    /// Setting this to `None` disables sync parsing entirely.
     pub fn set_sync_parse_timeout(&mut self, timeout: Option<Duration>) {
         self.sync_parse_timeout = timeout;
     }
@@ -3706,6 +3708,14 @@ impl BufferSnapshot {
         None
     }
 
+    pub fn captures(
+        &self,
+        range: Range<usize>,
+        query: fn(&Grammar) -> Option<&tree_sitter::Query>,
+    ) -> SyntaxMapCaptures<'_> {
+        self.syntax.captures(range, &self.text, query)
+    }
+
     #[ztracing::instrument(skip_all)]
     fn get_highlights(&self, range: Range<usize>) -> (SyntaxMapCaptures<'_>, Vec<HighlightMap>) {
         let captures = self.syntax.captures(range, &self.text, |grammar| {

crates/language_tools/src/highlights_tree_view.rs 🔗

@@ -397,11 +397,9 @@ impl HighlightsTreeView {
             let end_offset = excerpt_range.context.end.to_offset(buffer_snapshot);
             let range = start_offset..end_offset;
 
-            let captures = buffer_snapshot
-                .syntax
-                .captures(range, buffer_snapshot, |grammar| {
-                    grammar.highlights_config.as_ref().map(|c| &c.query)
-                });
+            let captures = buffer_snapshot.captures(range, |grammar| {
+                grammar.highlights_config.as_ref().map(|c| &c.query)
+            });
             let grammars: Vec<_> = captures.grammars().to_vec();
             let highlight_maps: Vec<_> = grammars.iter().map(|g| g.highlight_map()).collect();
 

crates/rope/src/rope.rs 🔗

@@ -553,7 +553,10 @@ impl Rope {
             return false;
         }
         let mut remaining = pattern;
-        for chunk in self.chunks_in_range(0..pattern.len()) {
+        for chunk in self.chunks_in_range(0..self.len()) {
+            let Some(chunk) = chunk.get(..remaining.len().min(chunk.len())) else {
+                return false;
+            };
             if remaining.starts_with(chunk) {
                 remaining = &remaining[chunk.len()..];
                 if remaining.is_empty() {
@@ -567,12 +570,14 @@ impl Rope {
     }
 
     pub fn ends_with(&self, pattern: &str) -> bool {
-        let len = self.len();
-        if pattern.len() > len {
+        if pattern.len() > self.len() {
             return false;
         }
         let mut remaining = pattern;
-        for chunk in self.reversed_chunks_in_range(len - pattern.len()..len) {
+        for chunk in self.reversed_chunks_in_range(0..self.len()) {
+            let Some(chunk) = chunk.get(chunk.len() - remaining.len().min(chunk.len())..) else {
+                return false;
+            };
             if remaining.ends_with(chunk) {
                 remaining = &remaining[..remaining.len() - chunk.len()];
                 if remaining.is_empty() {