diff --git a/crates/editor/src/bracket_colorization.rs b/crates/editor/src/bracket_colorization.rs index bf71d5c71c9580d04e0c61047215992c5cbd4a26..16fe29a7fa4aa066cf045a63c477fbb569d80334 100644 --- a/crates/editor/src/bracket_colorization.rs +++ b/crates/editor/src/bracket_colorization.rs @@ -5,10 +5,10 @@ use std::ops::Range; use crate::{Editor, HighlightKey}; -use collections::HashMap; -use gpui::{Context, HighlightStyle}; +use collections::{HashMap, HashSet}; +use gpui::{AppContext as _, Context, HighlightStyle}; use itertools::Itertools; -use language::language_settings; +use language::{BufferRow, BufferSnapshot, language_settings}; use multi_buffer::{Anchor, ExcerptId}; use ui::{ActiveTheme, utils::ensure_minimum_contrast}; @@ -19,22 +19,16 @@ impl Editor { } if invalidate { - self.fetched_tree_sitter_chunks.clear(); + self.bracket_fetched_tree_sitter_chunks.clear(); } let accents_count = cx.theme().accents().0.len(); let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx); - let anchors_in_multi_buffer = |current_excerpt: ExcerptId, - text_anchors: [text::Anchor; 4]| - -> Option<[Option<_>; 4]> { - multi_buffer_snapshot - .anchors_in_excerpt(current_excerpt, text_anchors)? - .collect_array() - }; - - let bracket_matches_by_accent = self.visible_excerpts(false, cx).into_iter().fold( - HashMap::default(), - |mut acc, (excerpt_id, (buffer, _, buffer_range))| { + + let visible_excerpts = self.visible_excerpts(false, cx); + let excerpt_data: Vec<(ExcerptId, BufferSnapshot, Range)> = visible_excerpts + .into_iter() + .filter_map(|(excerpt_id, (buffer, _, buffer_range))| { let buffer_snapshot = buffer.read(cx).snapshot(); if language_settings::language_settings( buffer_snapshot.language().map(|language| language.name()), @@ -43,112 +37,173 @@ impl Editor { ) .colorize_brackets { - let fetched_chunks = self - .fetched_tree_sitter_chunks - .entry(excerpt_id) - .or_default(); - - let brackets_by_accent = buffer_snapshot - .fetch_bracket_ranges( - buffer_range.start..buffer_range.end, - Some(fetched_chunks), - ) - .into_iter() - .flat_map(|(chunk_range, pairs)| { - if fetched_chunks.insert(chunk_range) { - pairs - } else { - Vec::new() - } - }) - .filter_map(|pair| { - let color_index = pair.color_index?; - - let buffer_open_range = - buffer_snapshot.anchor_range_around(pair.open_range); - let buffer_close_range = - buffer_snapshot.anchor_range_around(pair.close_range); - let [ - buffer_open_range_start, - buffer_open_range_end, - buffer_close_range_start, - buffer_close_range_end, - ] = anchors_in_multi_buffer( - excerpt_id, - [ - buffer_open_range.start, - buffer_open_range.end, - buffer_close_range.start, - buffer_close_range.end, - ], - )?; - let multi_buffer_open_range = - buffer_open_range_start.zip(buffer_open_range_end); - let multi_buffer_close_range = - buffer_close_range_start.zip(buffer_close_range_end); - - let mut ranges = Vec::with_capacity(2); - if let Some((open_start, open_end)) = multi_buffer_open_range { - ranges.push(open_start..open_end); - } - if let Some((close_start, close_end)) = multi_buffer_close_range { - ranges.push(close_start..close_end); - } - if ranges.is_empty() { - None - } else { - Some((color_index % accents_count, ranges)) - } - }); + Some((excerpt_id, buffer_snapshot, buffer_range)) + } else { + None + } + }) + .collect(); - for (accent_number, new_ranges) in brackets_by_accent { - let ranges = acc - .entry(accent_number) - .or_insert_with(Vec::>::new); + let mut fetched_tree_sitter_chunks = excerpt_data + .iter() + .filter_map(|(excerpt_id, ..)| { + Some(( + *excerpt_id, + self.bracket_fetched_tree_sitter_chunks + .get(excerpt_id) + .cloned()?, + )) + }) + .collect::>>>(); + + let bracket_matches_by_accent = cx.background_spawn(async move { + let anchors_in_multi_buffer = |current_excerpt: ExcerptId, + text_anchors: [text::Anchor; 4]| + -> Option<[Option<_>; 4]> { + multi_buffer_snapshot + .anchors_in_excerpt(current_excerpt, text_anchors)? + .collect_array() + }; - for new_range in new_ranges { - let i = ranges - .binary_search_by(|probe| { - probe.start.cmp(&new_range.start, &multi_buffer_snapshot) - }) - .unwrap_or_else(|i| i); - ranges.insert(i, new_range); + let bracket_matches_by_accent: HashMap>> = + excerpt_data.into_iter().fold( + HashMap::default(), + |mut acc, (excerpt_id, buffer_snapshot, buffer_range)| { + let fetched_chunks = + fetched_tree_sitter_chunks.entry(excerpt_id).or_default(); + + let brackets_by_accent = compute_bracket_ranges( + &buffer_snapshot, + buffer_range, + fetched_chunks, + excerpt_id, + accents_count, + &anchors_in_multi_buffer, + ); + + for (accent_number, new_ranges) in brackets_by_accent { + let ranges = acc + .entry(accent_number) + .or_insert_with(Vec::>::new); + + for new_range in new_ranges { + let i = ranges + .binary_search_by(|probe| { + probe.start.cmp(&new_range.start, &multi_buffer_snapshot) + }) + .unwrap_or_else(|i| i); + ranges.insert(i, new_range); + } } - } - } - acc - }, - ); + acc + }, + ); - if invalidate { - self.clear_highlights_with( - &mut |key| matches!(key, HighlightKey::ColorizeBracket(_)), - cx, - ); - } + (bracket_matches_by_accent, fetched_tree_sitter_chunks) + }); let editor_background = cx.theme().colors().editor_background; let accents = cx.theme().accents().clone(); - for (accent_number, bracket_highlights) in bracket_matches_by_accent { - let bracket_color = accents.color_for_index(accent_number as u32); - let adjusted_color = ensure_minimum_contrast(bracket_color, editor_background, 55.0); - let style = HighlightStyle { - color: Some(adjusted_color), - ..HighlightStyle::default() - }; - self.highlight_text_key( - HighlightKey::ColorizeBracket(accent_number), - bracket_highlights, - style, - true, - cx, - ); - } + self.colorize_brackets_task = cx.spawn(async move |editor, cx| { + if invalidate { + editor + .update(cx, |editor, cx| { + editor.clear_highlights_with( + &mut |key| matches!(key, HighlightKey::ColorizeBracket(_)), + cx, + ); + }) + .ok(); + } + + let (bracket_matches_by_accent, updated_chunks) = bracket_matches_by_accent.await; + + editor + .update(cx, |editor, cx| { + editor + .bracket_fetched_tree_sitter_chunks + .extend(updated_chunks); + for (accent_number, bracket_highlights) in bracket_matches_by_accent { + let bracket_color = accents.color_for_index(accent_number as u32); + let adjusted_color = + ensure_minimum_contrast(bracket_color, editor_background, 55.0); + let style = HighlightStyle { + color: Some(adjusted_color), + ..HighlightStyle::default() + }; + + editor.highlight_text_key( + HighlightKey::ColorizeBracket(accent_number), + bracket_highlights, + style, + true, + cx, + ); + } + }) + .ok(); + }); } } +fn compute_bracket_ranges( + buffer_snapshot: &BufferSnapshot, + buffer_range: Range, + fetched_chunks: &mut HashSet>, + excerpt_id: ExcerptId, + accents_count: usize, + anchors_in_multi_buffer: &impl Fn(ExcerptId, [text::Anchor; 4]) -> Option<[Option; 4]>, +) -> Vec<(usize, Vec>)> { + buffer_snapshot + .fetch_bracket_ranges(buffer_range.start..buffer_range.end, Some(fetched_chunks)) + .into_iter() + .flat_map(|(chunk_range, pairs)| { + if fetched_chunks.insert(chunk_range) { + pairs + } else { + Vec::new() + } + }) + .filter_map(|pair| { + let color_index = pair.color_index?; + + let buffer_open_range = buffer_snapshot.anchor_range_around(pair.open_range); + let buffer_close_range = buffer_snapshot.anchor_range_around(pair.close_range); + let [ + buffer_open_range_start, + buffer_open_range_end, + buffer_close_range_start, + buffer_close_range_end, + ] = anchors_in_multi_buffer( + excerpt_id, + [ + buffer_open_range.start, + buffer_open_range.end, + buffer_close_range.start, + buffer_close_range.end, + ], + )?; + let multi_buffer_open_range = buffer_open_range_start.zip(buffer_open_range_end); + let multi_buffer_close_range = buffer_close_range_start.zip(buffer_close_range_end); + + let mut ranges = Vec::with_capacity(2); + if let Some((open_start, open_end)) = multi_buffer_open_range { + ranges.push(open_start..open_end); + } + if let Some((close_start, close_end)) = multi_buffer_close_range { + ranges.push(close_start..close_end); + } + if ranges.is_empty() { + None + } else { + Some((color_index % accents_count, ranges)) + } + }) + .collect() +} + #[cfg(test)] mod tests { use std::{cmp, sync::Arc, time::Duration}; @@ -164,7 +219,7 @@ mod tests { }; use collections::HashSet; use fs::FakeFs; - use gpui::{AppContext as _, UpdateGlobal as _}; + use gpui::UpdateGlobal as _; use indoc::indoc; use itertools::Itertools; use language::{Capability, markdown_lang}; @@ -749,6 +804,7 @@ mod foo «1{ }); }); }); + cx.executor().run_until_parked(); assert_eq!( &separate_with_comment_lines( indoc! {r#" @@ -776,6 +832,7 @@ mod foo { }); }); }); + cx.executor().run_until_parked(); assert_eq!( &separate_with_comment_lines( indoc! {r#" diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a6d9e593cc4b2d8d593f48a7887e6308ff0e63cb..93d87885babf6265ff4b12c9da2c4c0cc07ec9a9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1347,7 +1347,7 @@ pub struct Editor { suppress_selection_callback: bool, applicable_language_settings: HashMap, LanguageSettings>, accent_data: Option, - fetched_tree_sitter_chunks: HashMap>>, + bracket_fetched_tree_sitter_chunks: HashMap>>, semantic_token_state: SemanticTokenState, pub(crate) refresh_matching_bracket_highlights_task: Task<()>, refresh_document_symbols_task: Shared>, @@ -1356,6 +1356,7 @@ pub struct Editor { outline_symbols_at_cursor: Option<(BufferId, Vec>)>, sticky_headers_task: Task<()>, sticky_headers: Option>>, + pub(crate) colorize_brackets_task: Task<()>, } #[derive(Debug, PartialEq)] @@ -2600,7 +2601,7 @@ impl Editor { applicable_language_settings: HashMap::default(), semantic_token_state: SemanticTokenState::new(cx, full_mode), accent_data: None, - fetched_tree_sitter_chunks: HashMap::default(), + bracket_fetched_tree_sitter_chunks: HashMap::default(), number_deleted_lines: false, refresh_matching_bracket_highlights_task: Task::ready(()), refresh_document_symbols_task: Task::ready(()).shared(), @@ -2609,6 +2610,7 @@ impl Editor { outline_symbols_at_cursor: None, sticky_headers_task: Task::ready(()), sticky_headers: None, + colorize_brackets_task: Task::ready(()), }; if is_minimap { @@ -24165,7 +24167,7 @@ impl Editor { self.refresh_document_highlights(cx); let snapshot = multibuffer.read(cx).snapshot(cx); for id in ids { - self.fetched_tree_sitter_chunks.remove(id); + self.bracket_fetched_tree_sitter_chunks.remove(id); if let Some(buffer) = snapshot.buffer_for_excerpt(*id) { self.semantic_token_state .invalidate_buffer(&buffer.remote_id()); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 9a0033306a1032786535a188a9ea830cb44c3ca3..88be32d6d73d967ab34b287534308164b8623679 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -17284,6 +17284,7 @@ async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) { } }); + cx.executor().run_until_parked(); cx.condition(|editor, _| editor.context_menu_visible()) .await; cx.assert_editor_state("fn main() { let a = 2.ˇ; }"); diff --git a/crates/editor/src/inlays/inlay_hints.rs b/crates/editor/src/inlays/inlay_hints.rs index d7a116065101dcc5070a7280ba7c3424e74685fe..0b3f6bda09c2cf86b994682e2ed89c2614d72737 100644 --- a/crates/editor/src/inlays/inlay_hints.rs +++ b/crates/editor/src/inlays/inlay_hints.rs @@ -4501,9 +4501,9 @@ let c = 3;"# }, ); - let buffer = project + let (buffer, _buffer_handle) = project .update(cx, |project, cx| { - project.open_local_buffer(path!("/a/main.rs"), cx) + project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx) }) .await .unwrap();