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.
This commit is contained in:
Piotr Osiewicz 2024-08-25 18:02:54 +02:00 committed by GitHub
parent 14f8d3a33a
commit 5e55d5507f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 30 deletions

View file

@ -452,7 +452,7 @@ pub struct BufferChunks<'a> {
buffer_snapshot: Option<&'a BufferSnapshot>, buffer_snapshot: Option<&'a BufferSnapshot>,
range: Range<usize>, range: Range<usize>,
chunks: text::Chunks<'a>, chunks: text::Chunks<'a>,
diagnostic_endpoints: Peekable<vec::IntoIter<DiagnosticEndpoint>>, diagnostic_endpoints: Option<Peekable<vec::IntoIter<DiagnosticEndpoint>>>,
error_depth: usize, error_depth: usize,
warning_depth: usize, warning_depth: usize,
information_depth: usize, information_depth: usize,
@ -2578,8 +2578,9 @@ impl BufferSnapshot {
if language_aware { if language_aware {
syntax = Some(self.get_highlights(range.clone())); syntax = Some(self.get_highlights(range.clone()));
} }
// We want to look at diagnostic spans only when iterating over language-annotated chunks.
BufferChunks::new(self.text.as_rope(), range, syntax, Some(self)) 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. /// 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, text: &'a Rope,
range: Range<usize>, range: Range<usize>,
syntax: Option<(SyntaxMapCaptures<'a>, Vec<HighlightMap>)>, syntax: Option<(SyntaxMapCaptures<'a>, Vec<HighlightMap>)>,
diagnostics: bool,
buffer_snapshot: Option<&'a BufferSnapshot>, buffer_snapshot: Option<&'a BufferSnapshot>,
) -> Self { ) -> Self {
let mut highlights = None; 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 chunks = text.chunks_in_range(range.clone());
let mut this = BufferChunks { let mut this = BufferChunks {
@ -3871,25 +3873,27 @@ impl<'a> BufferChunks<'a> {
} }
fn initialize_diagnostic_endpoints(&mut self) { fn initialize_diagnostic_endpoints(&mut self) {
if let Some(buffer) = self.buffer_snapshot { if let Some(diagnostics) = self.diagnostic_endpoints.as_mut() {
let mut diagnostic_endpoints = Vec::new(); if let Some(buffer) = self.buffer_snapshot {
for entry in buffer.diagnostics_in_range::<_, usize>(self.range.clone(), false) { let mut diagnostic_endpoints = Vec::new();
diagnostic_endpoints.push(DiagnosticEndpoint { for entry in buffer.diagnostics_in_range::<_, usize>(self.range.clone(), false) {
offset: entry.range.start, diagnostic_endpoints.push(DiagnosticEndpoint {
is_start: true, offset: entry.range.start,
severity: entry.diagnostic.severity, is_start: true,
is_unnecessary: entry.diagnostic.is_unnecessary, severity: entry.diagnostic.severity,
}); is_unnecessary: entry.diagnostic.is_unnecessary,
diagnostic_endpoints.push(DiagnosticEndpoint { });
offset: entry.range.end, diagnostic_endpoints.push(DiagnosticEndpoint {
is_start: false, offset: entry.range.end,
severity: entry.diagnostic.severity, is_start: false,
is_unnecessary: entry.diagnostic.is_unnecessary, 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() { let mut diagnostic_endpoints = std::mem::take(&mut self.diagnostic_endpoints);
if endpoint.offset <= self.range.start { if let Some(diagnostic_endpoints) = diagnostic_endpoints.as_mut() {
self.update_diagnostic_depths(endpoint); while let Some(endpoint) = diagnostic_endpoints.peek().copied() {
self.diagnostic_endpoints.next(); if endpoint.offset <= self.range.start {
} else { self.update_diagnostic_depths(endpoint);
next_diagnostic_endpoint = endpoint.offset; diagnostic_endpoints.next();
break; } else {
next_diagnostic_endpoint = endpoint.offset;
break;
}
} }
} }
self.diagnostic_endpoints = diagnostic_endpoints;
if let Some(chunk) = self.chunks.peek() { if let Some(chunk) = self.chunks.peek() {
let chunk_start = self.range.start; let chunk_start = self.range.start;

View file

@ -1358,7 +1358,9 @@ impl Language {
}); });
let highlight_maps = vec![grammar.highlight_map()]; let highlight_maps = vec![grammar.highlight_map()];
let mut offset = 0; 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(); let end_offset = offset + chunk.text.len();
if let Some(highlight_id) = chunk.syntax_highlight_id { if let Some(highlight_id) = chunk.syntax_highlight_id {
if !highlight_id.is_default() { if !highlight_id.is_default() {