From ab26a175a4f904ed40b40266b5270e90ebf979a4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 3 Feb 2022 11:21:30 +0100 Subject: [PATCH] Opt into language-aware features when getting buffer chunks We use chunks a lot to transform points and sync the various display maps, and always querying tree-sitter or the LSP diagnostics in those cases is unnecessarily expensive. --- crates/editor/src/display_map.rs | 12 ++++--- crates/editor/src/display_map/block_map.rs | 10 +++--- crates/editor/src/display_map/fold_map.rs | 12 ++++--- crates/editor/src/display_map/tab_map.rs | 16 +++++---- crates/editor/src/display_map/wrap_map.rs | 16 +++++---- crates/editor/src/element.rs | 2 +- crates/editor/src/multi_buffer.rs | 31 +++++++++++----- crates/find/src/find.rs | 2 +- crates/language/src/buffer.rs | 41 +++++++++++++--------- crates/language/src/tests.rs | 2 +- 10 files changed, 91 insertions(+), 53 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index b1f11f42757269293a8d1a2c27fd7572229749e1..697dc5ea6256f29a0bab8eb88b5d01bfdb7eff81 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -250,12 +250,16 @@ impl DisplaySnapshot { pub fn text_chunks(&self, display_row: u32) -> impl Iterator { self.blocks_snapshot - .chunks(display_row..self.max_point().row() + 1) + .chunks(display_row..self.max_point().row() + 1, false) .map(|h| h.text) } - pub fn chunks<'a>(&'a self, display_rows: Range) -> DisplayChunks<'a> { - self.blocks_snapshot.chunks(display_rows) + pub fn chunks<'a>( + &'a self, + display_rows: Range, + language_aware: bool, + ) -> DisplayChunks<'a> { + self.blocks_snapshot.chunks(display_rows, language_aware) } pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator + 'a { @@ -1117,7 +1121,7 @@ mod tests { ) -> Vec<(String, Option)> { let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); let mut chunks: Vec<(String, Option)> = Vec::new(); - for chunk in snapshot.chunks(rows) { + for chunk in snapshot.chunks(rows, true) { let color = chunk .highlight_id .and_then(|id| id.style(theme).map(|s| s.color)); diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 600bfbfad1159b00739dc528ddf474d927fcabb1..96848016a9f7ff44268e21960ec49d9837fce730 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -460,12 +460,12 @@ impl<'a> BlockMapWriter<'a> { impl BlockSnapshot { #[cfg(test)] pub fn text(&self) -> String { - self.chunks(0..self.transforms.summary().output_rows) + self.chunks(0..self.transforms.summary().output_rows, false) .map(|chunk| chunk.text) .collect() } - pub fn chunks<'a>(&'a self, rows: Range) -> BlockChunks<'a> { + pub fn chunks<'a>(&'a self, rows: Range, language_aware: bool) -> BlockChunks<'a> { let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows); let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(); let input_end = { @@ -493,7 +493,9 @@ impl BlockSnapshot { cursor.start().1 .0 + overshoot }; BlockChunks { - input_chunks: self.wrap_snapshot.chunks(input_start..input_end), + input_chunks: self + .wrap_snapshot + .chunks(input_start..input_end, language_aware), input_chunk: Default::default(), transforms: cursor, output_row: rows.start, @@ -1335,7 +1337,7 @@ mod tests { for start_row in 0..expected_row_count { let expected_text = expected_lines[start_row..].join("\n"); let actual_text = blocks_snapshot - .chunks(start_row as u32..expected_row_count as u32) + .chunks(start_row as u32..expected_row_count as u32, false) .map(|chunk| chunk.text) .collect::(); assert_eq!( diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index ab21977dfa64aa3f090a4d574175316dfc31521b..a23f6ad01022c4775f4bc60686873606885c9abc 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -489,7 +489,7 @@ impl FoldSnapshot { #[cfg(test)] pub fn text(&self) -> String { - self.chunks(FoldOffset(0)..self.len()) + self.chunks(FoldOffset(0)..self.len(), false) .map(|c| c.text) .collect() } @@ -629,11 +629,11 @@ impl FoldSnapshot { pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator { let start = start.to_offset(self); - self.chunks(start..self.len()) + self.chunks(start..self.len(), false) .flat_map(|chunk| chunk.text.chars()) } - pub fn chunks<'a>(&'a self, range: Range) -> FoldChunks<'a> { + pub fn chunks<'a>(&'a self, range: Range, language_aware: bool) -> FoldChunks<'a> { let mut transform_cursor = self.transforms.cursor::<(FoldOffset, usize)>(); transform_cursor.seek(&range.end, Bias::Right, &()); @@ -646,7 +646,9 @@ impl FoldSnapshot { FoldChunks { transform_cursor, - buffer_chunks: self.buffer_snapshot.chunks(buffer_start..buffer_end), + buffer_chunks: self + .buffer_snapshot + .chunks(buffer_start..buffer_end, language_aware), buffer_chunk: None, buffer_offset: buffer_start, output_offset: range.start.0, @@ -1393,7 +1395,7 @@ mod tests { let text = &expected_text[start.0..end.0]; assert_eq!( snapshot - .chunks(start..end) + .chunks(start..end, false) .map(|c| c.text) .collect::(), text, diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index 9d58e87d8d348355ef59ee22a7484d78d9cc13d6..e2239e76715278ba84e0ade5d3b56c7bc9e0f082 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -34,7 +34,7 @@ impl TabMap { let mut delta = 0; for chunk in old_snapshot .fold_snapshot - .chunks(fold_edit.old.end..max_offset) + .chunks(fold_edit.old.end..max_offset, false) { let patterns: &[_] = &['\t', '\n']; if let Some(ix) = chunk.text.find(patterns) { @@ -109,7 +109,7 @@ impl TabSnapshot { self.max_point() }; for c in self - .chunks(range.start..line_end) + .chunks(range.start..line_end, false) .flat_map(|chunk| chunk.text.chars()) { if c == '\n' { @@ -123,7 +123,7 @@ impl TabSnapshot { last_line_chars = first_line_chars; } else { for _ in self - .chunks(TabPoint::new(range.end.row(), 0)..range.end) + .chunks(TabPoint::new(range.end.row(), 0)..range.end, false) .flat_map(|chunk| chunk.text.chars()) { last_line_chars += 1; @@ -143,7 +143,7 @@ impl TabSnapshot { self.fold_snapshot.version } - pub fn chunks<'a>(&'a self, range: Range) -> TabChunks<'a> { + pub fn chunks<'a>(&'a self, range: Range, language_aware: bool) -> TabChunks<'a> { let (input_start, expanded_char_column, to_next_stop) = self.to_fold_point(range.start, Bias::Left); let input_start = input_start.to_offset(&self.fold_snapshot); @@ -158,7 +158,9 @@ impl TabSnapshot { }; TabChunks { - fold_chunks: self.fold_snapshot.chunks(input_start..input_end), + fold_chunks: self + .fold_snapshot + .chunks(input_start..input_end, language_aware), column: expanded_char_column, output_position: range.start.0, max_output_position: range.end.0, @@ -177,7 +179,7 @@ impl TabSnapshot { #[cfg(test)] pub fn text(&self) -> String { - self.chunks(TabPoint::zero()..self.max_point()) + self.chunks(TabPoint::zero()..self.max_point(), false) .map(|chunk| chunk.text) .collect() } @@ -490,7 +492,7 @@ mod tests { assert_eq!( expected_text, tabs_snapshot - .chunks(start..end) + .chunks(start..end, false) .map(|c| c.text) .collect::(), "chunks({:?}..{:?})", diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index df1d17c5006c63c006c208fe35bd2a766d78a955..1d5e64c8a565798948a46c08b4b32dbaa08b96e7 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -433,8 +433,10 @@ impl WrapSnapshot { let mut line = String::new(); let mut remaining = None; - let mut chunks = new_tab_snapshot - .chunks(TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point()); + let mut chunks = new_tab_snapshot.chunks( + TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(), + false, + ); let mut edit_transforms = Vec::::new(); for _ in edit.new_rows.start..edit.new_rows.end { while let Some(chunk) = @@ -559,11 +561,11 @@ impl WrapSnapshot { } pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator { - self.chunks(wrap_row..self.max_point().row() + 1) + self.chunks(wrap_row..self.max_point().row() + 1, false) .map(|h| h.text) } - pub fn chunks<'a>(&'a self, rows: Range) -> WrapChunks<'a> { + pub fn chunks<'a>(&'a self, rows: Range, language_aware: bool) -> WrapChunks<'a> { let output_start = WrapPoint::new(rows.start, 0); let output_end = WrapPoint::new(rows.end, 0); let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(); @@ -576,7 +578,9 @@ impl WrapSnapshot { .to_tab_point(output_end) .min(self.tab_snapshot.max_point()); WrapChunks { - input_chunks: self.tab_snapshot.chunks(input_start..input_end), + input_chunks: self + .tab_snapshot + .chunks(input_start..input_end, language_aware), input_chunk: Default::default(), output_position: output_start, max_output_row: rows.end, @@ -1288,7 +1292,7 @@ mod tests { } let actual_text = self - .chunks(start_row..end_row) + .chunks(start_row..end_row, true) .map(|c| c.text) .collect::(); assert_eq!( diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 31926eb972d3c15028e11d95df2a52376ef06665..a22e6021e06bce11d7993518a59b3d5da08f6aae 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -598,7 +598,7 @@ impl EditorElement { .collect(); } else { let style = &self.settings.style; - let chunks = snapshot.chunks(rows.clone()).map(|chunk| { + let chunks = snapshot.chunks(rows.clone(), true).map(|chunk| { let highlight_style = chunk .highlight_id .and_then(|highlight_id| highlight_id.style(&style.syntax)); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 8e649b919fa1a47676c7c6e674ff139e27b5ff57..2bdf8199569d52630414324f9ef507bc78e53966 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -125,6 +125,7 @@ pub struct MultiBufferChunks<'a> { range: Range, excerpts: Cursor<'a, Excerpt, usize>, excerpt_chunks: Option>, + language_aware: bool, } pub struct MultiBufferBytes<'a> { @@ -1112,7 +1113,9 @@ impl Entity for MultiBuffer { impl MultiBufferSnapshot { pub fn text(&self) -> String { - self.chunks(0..self.len()).map(|chunk| chunk.text).collect() + self.chunks(0..self.len(), false) + .map(|chunk| chunk.text) + .collect() } pub fn reversed_chars_at<'a, T: ToOffset>( @@ -1162,7 +1165,7 @@ impl MultiBufferSnapshot { &'a self, range: Range, ) -> impl Iterator { - self.chunks(range).map(|chunk| chunk.text) + self.chunks(range, false).map(|chunk| chunk.text) } pub fn is_line_blank(&self, row: u32) -> bool { @@ -1320,12 +1323,17 @@ impl MultiBufferSnapshot { result } - pub fn chunks<'a, T: ToOffset>(&'a self, range: Range) -> MultiBufferChunks<'a> { + pub fn chunks<'a, T: ToOffset>( + &'a self, + range: Range, + language_aware: bool, + ) -> MultiBufferChunks<'a> { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut chunks = MultiBufferChunks { range: range.clone(), excerpts: self.excerpts.cursor(), excerpt_chunks: None, + language_aware: language_aware, }; chunks.seek(range.start); chunks @@ -2108,7 +2116,11 @@ impl Excerpt { } } - fn chunks_in_range<'a>(&'a self, range: Range) -> ExcerptChunks<'a> { + fn chunks_in_range<'a>( + &'a self, + range: Range, + language_aware: bool, + ) -> ExcerptChunks<'a> { let content_start = self.range.start.to_offset(&self.buffer); let chunks_start = content_start + range.start; let chunks_end = content_start + cmp::min(range.end, self.text_summary.bytes); @@ -2122,7 +2134,7 @@ impl Excerpt { 0 }; - let content_chunks = self.buffer.chunks(chunks_start..chunks_end); + let content_chunks = self.buffer.chunks(chunks_start..chunks_end, language_aware); ExcerptChunks { content_chunks, @@ -2321,6 +2333,7 @@ impl<'a> MultiBufferChunks<'a> { if let Some(excerpt) = self.excerpts.item() { self.excerpt_chunks = Some(excerpt.chunks_in_range( self.range.start - self.excerpts.start()..self.range.end - self.excerpts.start(), + self.language_aware, )); } else { self.excerpt_chunks = None; @@ -2340,8 +2353,10 @@ impl<'a> Iterator for MultiBufferChunks<'a> { } else { self.excerpts.next(&()); let excerpt = self.excerpts.item()?; - self.excerpt_chunks = - Some(excerpt.chunks_in_range(0..self.range.end - self.excerpts.start())); + self.excerpt_chunks = Some(excerpt.chunks_in_range( + 0..self.range.end - self.excerpts.start(), + self.language_aware, + )); self.next() } } @@ -3096,7 +3111,7 @@ mod tests { let mut buffer_point_utf16 = buffer_start_point_utf16; for ch in buffer .snapshot() - .chunks(buffer_range.clone()) + .chunks(buffer_range.clone(), false) .flat_map(|c| c.text.chars()) { for _ in 0..ch.len_utf8() { diff --git a/crates/find/src/find.rs b/crates/find/src/find.rs index 354016e576e98e2a2cf899fa55d133c00ecfc151..3de2234da1837b1f870ead6e5722e51afd102e3d 100644 --- a/crates/find/src/find.rs +++ b/crates/find/src/find.rs @@ -607,7 +607,7 @@ async fn regex_search( let mut line = String::new(); let mut line_offset = 0; for (chunk_ix, chunk) in buffer - .chunks(0..buffer.len()) + .chunks(0..buffer.len(), false) .map(|c| c.text) .chain(["\n"]) .enumerate() diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 8c026b08bfe8ffc57eb5b8f71554d155fcfb032d..2efcbbf0d6ac2bcbdd05b42339da4ad1cb74f8a1 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2092,28 +2092,37 @@ impl BufferSnapshot { None } - pub fn chunks<'a, T: ToOffset>(&'a self, range: Range) -> BufferChunks<'a> { + pub fn chunks<'a, T: ToOffset>( + &'a self, + range: Range, + language_aware: bool, + ) -> BufferChunks<'a> { let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut diagnostic_endpoints = Vec::::new(); - for entry in self.diagnostics_in_range::<_, usize>(range.clone()) { - diagnostic_endpoints.push(DiagnosticEndpoint { - offset: entry.range.start, - is_start: true, - severity: entry.diagnostic.severity, - }); - diagnostic_endpoints.push(DiagnosticEndpoint { - offset: entry.range.end, - is_start: false, - severity: entry.diagnostic.severity, - }); + let mut tree = None; + let mut diagnostic_endpoints = Vec::new(); + if language_aware { + tree = self.tree.as_ref(); + for entry in self.diagnostics_in_range::<_, usize>(range.clone()) { + diagnostic_endpoints.push(DiagnosticEndpoint { + offset: entry.range.start, + is_start: true, + severity: entry.diagnostic.severity, + }); + diagnostic_endpoints.push(DiagnosticEndpoint { + offset: entry.range.end, + is_start: false, + severity: entry.diagnostic.severity, + }); + } + diagnostic_endpoints + .sort_unstable_by_key(|endpoint| (endpoint.offset, !endpoint.is_start)); } - diagnostic_endpoints.sort_unstable_by_key(|endpoint| (endpoint.offset, !endpoint.is_start)); BufferChunks::new( self.text.as_rope(), range, - self.tree.as_ref(), + tree, self.grammar(), diagnostic_endpoints, ) @@ -2157,7 +2166,7 @@ impl BufferSnapshot { TextProvider(self.as_rope()), ); - let mut chunks = self.chunks(0..self.len()); + let mut chunks = self.chunks(0..self.len(), true); let item_capture_ix = grammar.outline_query.capture_index_for_name("item")?; let name_capture_ix = grammar.outline_query.capture_index_for_name("name")?; diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index bab62c23502ce39e01d506e9108b85160ca4b627..94001c591ad77bd9582f6da81fba9b03bb26f4ae 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -1090,7 +1090,7 @@ fn chunks_with_diagnostics( range: Range, ) -> Vec<(String, Option)> { let mut chunks: Vec<(String, Option)> = Vec::new(); - for chunk in buffer.snapshot().chunks(range) { + for chunk in buffer.snapshot().chunks(range, true) { if chunks .last() .map_or(false, |prev_chunk| prev_chunk.1 == chunk.diagnostic)