From 5e55d5507f2b78bc0cc65199a45e8283b8cb5213 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sun, 25 Aug 2024 18:02:54 +0200 Subject: [PATCH] language: Do not fetch diagnostics when iterating over text without language awareness (#16824) This PR fixes a regression from https://github.com/zed-industries/zed/pull/15646 where we've started fetching diagnostic spans unconditionally (whereas previously that wasn't done when iterating over raw text). Closes #16764 Release Notes: - Fixed performance regression in handling buffers with large quantities of diagnostics. --- crates/language/src/buffer.rs | 66 ++++++++++++++++++--------------- crates/language/src/language.rs | 4 +- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index bf300d6808be91ee4441b06476bace1357f00826..ae8b2a957fe8b1eb03cae3cc0bbd171591c3be2a 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -452,7 +452,7 @@ pub struct BufferChunks<'a> { buffer_snapshot: Option<&'a BufferSnapshot>, range: Range, chunks: text::Chunks<'a>, - diagnostic_endpoints: Peekable>, + diagnostic_endpoints: Option>>, error_depth: usize, warning_depth: usize, information_depth: usize, @@ -2578,8 +2578,9 @@ impl BufferSnapshot { if language_aware { syntax = Some(self.get_highlights(range.clone())); } - - BufferChunks::new(self.text.as_rope(), range, syntax, Some(self)) + // We want to look at diagnostic spans only when iterating over language-annotated chunks. + let diagnostics = language_aware; + BufferChunks::new(self.text.as_rope(), range, syntax, diagnostics, Some(self)) } /// Invokes the given callback for each line of text in the given range of the buffer. @@ -3798,6 +3799,7 @@ impl<'a> BufferChunks<'a> { text: &'a Rope, range: Range, syntax: Option<(SyntaxMapCaptures<'a>, Vec)>, + diagnostics: bool, buffer_snapshot: Option<&'a BufferSnapshot>, ) -> Self { let mut highlights = None; @@ -3810,7 +3812,7 @@ impl<'a> BufferChunks<'a> { }) } - let diagnostic_endpoints = Vec::new().into_iter().peekable(); + let diagnostic_endpoints = diagnostics.then(|| Vec::new().into_iter().peekable()); let chunks = text.chunks_in_range(range.clone()); let mut this = BufferChunks { @@ -3871,25 +3873,27 @@ impl<'a> BufferChunks<'a> { } fn initialize_diagnostic_endpoints(&mut self) { - if let Some(buffer) = self.buffer_snapshot { - let mut diagnostic_endpoints = Vec::new(); - for entry in buffer.diagnostics_in_range::<_, usize>(self.range.clone(), false) { - diagnostic_endpoints.push(DiagnosticEndpoint { - offset: entry.range.start, - is_start: true, - severity: entry.diagnostic.severity, - is_unnecessary: entry.diagnostic.is_unnecessary, - }); - diagnostic_endpoints.push(DiagnosticEndpoint { - offset: entry.range.end, - is_start: false, - severity: entry.diagnostic.severity, - is_unnecessary: entry.diagnostic.is_unnecessary, - }); + if let Some(diagnostics) = self.diagnostic_endpoints.as_mut() { + if let Some(buffer) = self.buffer_snapshot { + let mut diagnostic_endpoints = Vec::new(); + for entry in buffer.diagnostics_in_range::<_, usize>(self.range.clone(), false) { + diagnostic_endpoints.push(DiagnosticEndpoint { + offset: entry.range.start, + is_start: true, + severity: entry.diagnostic.severity, + is_unnecessary: entry.diagnostic.is_unnecessary, + }); + diagnostic_endpoints.push(DiagnosticEndpoint { + offset: entry.range.end, + is_start: false, + severity: entry.diagnostic.severity, + is_unnecessary: entry.diagnostic.is_unnecessary, + }); + } + diagnostic_endpoints + .sort_unstable_by_key(|endpoint| (endpoint.offset, !endpoint.is_start)); + *diagnostics = diagnostic_endpoints.into_iter().peekable(); } - diagnostic_endpoints - .sort_unstable_by_key(|endpoint| (endpoint.offset, !endpoint.is_start)); - self.diagnostic_endpoints = diagnostic_endpoints.into_iter().peekable(); } } @@ -3975,15 +3979,19 @@ impl<'a> Iterator for BufferChunks<'a> { } } - while let Some(endpoint) = self.diagnostic_endpoints.peek().copied() { - if endpoint.offset <= self.range.start { - self.update_diagnostic_depths(endpoint); - self.diagnostic_endpoints.next(); - } else { - next_diagnostic_endpoint = endpoint.offset; - break; + let mut diagnostic_endpoints = std::mem::take(&mut self.diagnostic_endpoints); + if let Some(diagnostic_endpoints) = diagnostic_endpoints.as_mut() { + while let Some(endpoint) = diagnostic_endpoints.peek().copied() { + if endpoint.offset <= self.range.start { + self.update_diagnostic_depths(endpoint); + diagnostic_endpoints.next(); + } else { + next_diagnostic_endpoint = endpoint.offset; + break; + } } } + self.diagnostic_endpoints = diagnostic_endpoints; if let Some(chunk) = self.chunks.peek() { let chunk_start = self.range.start; diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index ee1c468d17289b2413624f47bdb42a1efac7c392..e8c283e9c581dfc6bd9e97b78a2c39da760cb23a 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1358,7 +1358,9 @@ impl Language { }); let highlight_maps = vec![grammar.highlight_map()]; let mut offset = 0; - for chunk in BufferChunks::new(text, range, Some((captures, highlight_maps)), None) { + for chunk in + BufferChunks::new(text, range, Some((captures, highlight_maps)), false, None) + { let end_offset = offset + chunk.text.len(); if let Some(highlight_id) = chunk.syntax_highlight_id { if !highlight_id.is_default() {