diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index a10a5f074c..3352d21ef8 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -37,7 +37,9 @@ pub use block_map::{ use block_map::{BlockRow, BlockSnapshot}; use collections::{HashMap, HashSet}; pub use crease_map::*; -pub use fold_map::{ChunkRenderer, ChunkRendererContext, Fold, FoldId, FoldPlaceholder, FoldPoint}; +pub use fold_map::{ + ChunkRenderer, ChunkRendererContext, ChunkRendererId, Fold, FoldId, FoldPlaceholder, FoldPoint, +}; use fold_map::{FoldMap, FoldSnapshot}; use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle}; pub use inlay_map::Inlay; @@ -538,7 +540,7 @@ impl DisplayMap { pub fn update_fold_widths( &mut self, - widths: impl IntoIterator, + widths: impl IntoIterator, cx: &mut Context, ) -> bool { let snapshot = self.buffer.read(cx).snapshot(cx); diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 197b85497b..f37e7063e7 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -1,4 +1,4 @@ -use crate::display_map::inlay_map::InlayChunk; +use crate::{InlayId, display_map::inlay_map::InlayChunk}; use super::{ Highlights, @@ -277,13 +277,16 @@ impl FoldMapWriter<'_> { pub(crate) fn update_fold_widths( &mut self, - new_widths: impl IntoIterator, + new_widths: impl IntoIterator, ) -> (FoldSnapshot, Vec) { let mut edits = Vec::new(); let inlay_snapshot = self.0.snapshot.inlay_snapshot.clone(); let buffer = &inlay_snapshot.buffer; for (id, new_width) in new_widths { + let ChunkRendererId::Fold(id) = id else { + continue; + }; if let Some(metadata) = self.0.snapshot.fold_metadata_by_id.get(&id).cloned() { if Some(new_width) != metadata.width { let buffer_start = metadata.range.start.to_offset(buffer); @@ -529,7 +532,7 @@ impl FoldMap { placeholder: Some(TransformPlaceholder { text: ELLIPSIS, renderer: ChunkRenderer { - id: fold.id, + id: ChunkRendererId::Fold(fold.id), render: Arc::new(move |cx| { (fold.placeholder.render)( fold_id, @@ -1267,11 +1270,17 @@ pub struct Chunk<'a> { pub renderer: Option, } +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum ChunkRendererId { + Fold(FoldId), + Inlay(InlayId), +} + /// A recipe for how the chunk should be presented. #[derive(Clone)] pub struct ChunkRenderer { - /// The id of the fold associated with this chunk. - pub id: FoldId, + /// The id of the renderer associated with this chunk. + pub id: ChunkRendererId, /// Creates a custom element to represent this chunk. pub render: Arc AnyElement>, /// If true, the element is constrained to the shaped width of the text. diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 4f7c4962f2..488e31c90a 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1,4 +1,4 @@ -use crate::{ChunkRenderer, HighlightStyles, InlayId, display_map::FoldId}; +use crate::{ChunkRenderer, HighlightStyles, InlayId}; use collections::BTreeSet; use gpui::{Hsla, Rgba}; use language::{Chunk, Edit, Point, TextSummary}; @@ -14,7 +14,7 @@ use sum_tree::{Bias, Cursor, SumTree}; use text::{Patch, Rope}; use ui::{ActiveTheme, IntoElement as _, ParentElement as _, Styled as _, div}; -use super::{Highlights, custom_highlights::CustomHighlightsChunks}; +use super::{Highlights, custom_highlights::CustomHighlightsChunks, fold_map::ChunkRendererId}; /// Decides where the [`Inlay`]s should be displayed. /// @@ -338,10 +338,10 @@ impl<'a> Iterator for InlayChunks<'a> { } InlayId::Hint(_) => self.highlight_styles.inlay_hint, InlayId::DebuggerValue(_) => self.highlight_styles.inlay_hint, - InlayId::Color(id) => { + InlayId::Color(_) => { if let Some(color) = inlay.color { renderer = Some(ChunkRenderer { - id: FoldId(id), + id: ChunkRendererId::Inlay(inlay.id), render: Arc::new(move |cx| { div() .w_4() diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 419e3c4ae9..66f5f2d5fa 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -17333,9 +17333,9 @@ impl Editor { self.active_indent_guides_state.dirty = true; } - pub fn update_fold_widths( + pub fn update_renderer_widths( &mut self, - widths: impl IntoIterator, + widths: impl IntoIterator, cx: &mut Context, ) -> bool { self.display_map diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 4260537076..1c55ff2d09 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -12,8 +12,8 @@ use crate::{ ToggleFold, code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP}, display_map::{ - Block, BlockContext, BlockStyle, DisplaySnapshot, EditorMargins, FoldId, HighlightKey, - HighlightedChunk, ToDisplayPoint, + Block, BlockContext, BlockStyle, ChunkRendererId, DisplaySnapshot, EditorMargins, + HighlightKey, HighlightedChunk, ToDisplayPoint, }, editor_settings::{ CurrentLineHighlight, DocumentColorsRenderMode, DoubleClickInMultibuffer, Minimap, @@ -7119,7 +7119,7 @@ pub(crate) struct LineWithInvisibles { enum LineFragment { Text(ShapedLine), Element { - id: FoldId, + id: ChunkRendererId, element: Option, size: Size, len: usize, @@ -8297,7 +8297,7 @@ impl Element for EditorElement { window, cx, ); - let new_fold_widths = line_layouts + let new_renrerer_widths = line_layouts .iter() .flat_map(|layout| &layout.fragments) .filter_map(|fragment| { @@ -8308,7 +8308,7 @@ impl Element for EditorElement { } }); if self.editor.update(cx, |editor, cx| { - editor.update_fold_widths(new_fold_widths, cx) + editor.update_renderer_widths(new_renrerer_widths, cx) }) { // If the fold widths have changed, we need to prepaint // the element again to account for any changes in diff --git a/crates/editor/src/lsp_colors.rs b/crates/editor/src/lsp_colors.rs index 7f771b9266..ce07dd43fe 100644 --- a/crates/editor/src/lsp_colors.rs +++ b/crates/editor/src/lsp_colors.rs @@ -19,18 +19,21 @@ use crate::{ #[derive(Debug)] pub(super) struct LspColorData { - cache_version_used: usize, + buffer_colors: HashMap, + render_mode: DocumentColorsRenderMode, +} + +#[derive(Debug, Default)] +struct BufferColors { colors: Vec<(Range, DocumentColor, InlayId)>, inlay_colors: HashMap, - render_mode: DocumentColorsRenderMode, + cache_version_used: usize, } impl LspColorData { pub fn new(cx: &App) -> Self { Self { - cache_version_used: 0, - colors: Vec::new(), - inlay_colors: HashMap::default(), + buffer_colors: HashMap::default(), render_mode: EditorSettings::get_global(cx).lsp_document_colors, } } @@ -47,8 +50,9 @@ impl LspColorData { DocumentColorsRenderMode::Inlay => Some(InlaySplice { to_remove: Vec::new(), to_insert: self - .colors + .buffer_colors .iter() + .flat_map(|(_, buffer_colors)| buffer_colors.colors.iter()) .map(|(range, color, id)| { Inlay::color( id.id(), @@ -63,33 +67,49 @@ impl LspColorData { }) .collect(), }), - DocumentColorsRenderMode::None => { - self.colors.clear(); - Some(InlaySplice { - to_remove: self.inlay_colors.drain().map(|(id, _)| id).collect(), - to_insert: Vec::new(), - }) - } + DocumentColorsRenderMode::None => Some(InlaySplice { + to_remove: self + .buffer_colors + .drain() + .flat_map(|(_, buffer_colors)| buffer_colors.inlay_colors) + .map(|(id, _)| id) + .collect(), + to_insert: Vec::new(), + }), DocumentColorsRenderMode::Border | DocumentColorsRenderMode::Background => { Some(InlaySplice { - to_remove: self.inlay_colors.drain().map(|(id, _)| id).collect(), + to_remove: self + .buffer_colors + .iter_mut() + .flat_map(|(_, buffer_colors)| buffer_colors.inlay_colors.drain()) + .map(|(id, _)| id) + .collect(), to_insert: Vec::new(), }) } } } - fn set_colors(&mut self, colors: Vec<(Range, DocumentColor, InlayId)>) -> bool { - if self.colors == colors { + fn set_colors( + &mut self, + buffer_id: BufferId, + colors: Vec<(Range, DocumentColor, InlayId)>, + cache_version: Option, + ) -> bool { + let buffer_colors = self.buffer_colors.entry(buffer_id).or_default(); + if let Some(cache_version) = cache_version { + buffer_colors.cache_version_used = cache_version; + } + if buffer_colors.colors == colors { return false; } - self.inlay_colors = colors + buffer_colors.inlay_colors = colors .iter() .enumerate() .map(|(i, (_, _, id))| (*id, i)) .collect(); - self.colors = colors; + buffer_colors.colors = colors; true } @@ -103,8 +123,9 @@ impl LspColorData { { Vec::new() } else { - self.colors + self.buffer_colors .iter() + .flat_map(|(_, buffer_colors)| &buffer_colors.colors) .map(|(range, color, _)| { let display_range = range.clone().to_display_points(snapshot); let color = Hsla::from(Rgba { @@ -162,10 +183,9 @@ impl Editor { ColorFetchStrategy::IgnoreCache } else { ColorFetchStrategy::UseCache { - known_cache_version: self - .colors - .as_ref() - .map(|colors| colors.cache_version_used), + known_cache_version: self.colors.as_ref().and_then(|colors| { + Some(colors.buffer_colors.get(&buffer_id)?.cache_version_used) + }), } }; let colors_task = lsp_store.document_colors(fetch_strategy, buffer, cx)?; @@ -201,15 +221,13 @@ impl Editor { return; }; - let mut cache_version = None; - let mut new_editor_colors = Vec::<(Range, DocumentColor)>::new(); + let mut new_editor_colors = HashMap::default(); for (buffer_id, colors) in all_colors { let Some(excerpts) = editor_excerpts.get(&buffer_id) else { continue; }; match colors { Ok(colors) => { - cache_version = colors.cache_version; for color in colors.colors { let color_start = point_from_lsp(color.lsp_range.start); let color_end = point_from_lsp(color.lsp_range.end); @@ -243,8 +261,15 @@ impl Editor { continue; }; + let new_entry = + new_editor_colors.entry(buffer_id).or_insert_with(|| { + (Vec::<(Range, DocumentColor)>::new(), None) + }); + new_entry.1 = colors.cache_version; + let new_buffer_colors = &mut new_entry.0; + let (Ok(i) | Err(i)) = - new_editor_colors.binary_search_by(|(probe, _)| { + new_buffer_colors.binary_search_by(|(probe, _)| { probe .start .cmp(&color_start_anchor, &multi_buffer_snapshot) @@ -254,7 +279,7 @@ impl Editor { .cmp(&color_end_anchor, &multi_buffer_snapshot) }) }); - new_editor_colors + new_buffer_colors .insert(i, (color_start_anchor..color_end_anchor, color)); break; } @@ -267,45 +292,70 @@ impl Editor { editor .update(cx, |editor, cx| { let mut colors_splice = InlaySplice::default(); - let mut new_color_inlays = Vec::with_capacity(new_editor_colors.len()); let Some(colors) = &mut editor.colors else { return; }; - let mut existing_colors = colors.colors.iter().peekable(); - for (new_range, new_color) in new_editor_colors { - let rgba_color = Rgba { - r: new_color.color.red, - g: new_color.color.green, - b: new_color.color.blue, - a: new_color.color.alpha, - }; + let mut updated = false; + for (buffer_id, (new_buffer_colors, new_cache_version)) in new_editor_colors { + let mut new_buffer_color_inlays = + Vec::with_capacity(new_buffer_colors.len()); + let mut existing_buffer_colors = colors + .buffer_colors + .entry(buffer_id) + .or_default() + .colors + .iter() + .peekable(); + for (new_range, new_color) in new_buffer_colors { + let rgba_color = Rgba { + r: new_color.color.red, + g: new_color.color.green, + b: new_color.color.blue, + a: new_color.color.alpha, + }; - loop { - match existing_colors.peek() { - Some((existing_range, existing_color, existing_inlay_id)) => { - match existing_range - .start - .cmp(&new_range.start, &multi_buffer_snapshot) - .then_with(|| { - existing_range - .end - .cmp(&new_range.end, &multi_buffer_snapshot) - }) { - cmp::Ordering::Less => { - colors_splice.to_remove.push(*existing_inlay_id); - existing_colors.next(); - continue; - } - cmp::Ordering::Equal => { - if existing_color == &new_color { - new_color_inlays.push(( - new_range, - new_color, - *existing_inlay_id, - )); - } else { + loop { + match existing_buffer_colors.peek() { + Some((existing_range, existing_color, existing_inlay_id)) => { + match existing_range + .start + .cmp(&new_range.start, &multi_buffer_snapshot) + .then_with(|| { + existing_range + .end + .cmp(&new_range.end, &multi_buffer_snapshot) + }) { + cmp::Ordering::Less => { colors_splice.to_remove.push(*existing_inlay_id); + existing_buffer_colors.next(); + continue; + } + cmp::Ordering::Equal => { + if existing_color == &new_color { + new_buffer_color_inlays.push(( + new_range, + new_color, + *existing_inlay_id, + )); + } else { + colors_splice + .to_remove + .push(*existing_inlay_id); + let inlay = Inlay::color( + post_inc(&mut editor.next_color_inlay_id), + new_range.start, + rgba_color, + ); + let inlay_id = inlay.id; + colors_splice.to_insert.push(inlay); + new_buffer_color_inlays + .push((new_range, new_color, inlay_id)); + } + existing_buffer_colors.next(); + break; + } + cmp::Ordering::Greater => { let inlay = Inlay::color( post_inc(&mut editor.next_color_inlay_id), new_range.start, @@ -313,49 +363,40 @@ impl Editor { ); let inlay_id = inlay.id; colors_splice.to_insert.push(inlay); - new_color_inlays + new_buffer_color_inlays .push((new_range, new_color, inlay_id)); + break; } - existing_colors.next(); - break; - } - cmp::Ordering::Greater => { - let inlay = Inlay::color( - post_inc(&mut editor.next_color_inlay_id), - new_range.start, - rgba_color, - ); - let inlay_id = inlay.id; - colors_splice.to_insert.push(inlay); - new_color_inlays.push((new_range, new_color, inlay_id)); - break; } } - } - None => { - let inlay = Inlay::color( - post_inc(&mut editor.next_color_inlay_id), - new_range.start, - rgba_color, - ); - let inlay_id = inlay.id; - colors_splice.to_insert.push(inlay); - new_color_inlays.push((new_range, new_color, inlay_id)); - break; + None => { + let inlay = Inlay::color( + post_inc(&mut editor.next_color_inlay_id), + new_range.start, + rgba_color, + ); + let inlay_id = inlay.id; + colors_splice.to_insert.push(inlay); + new_buffer_color_inlays + .push((new_range, new_color, inlay_id)); + break; + } } } } - } - if existing_colors.peek().is_some() { - colors_splice - .to_remove - .extend(existing_colors.map(|(_, _, id)| *id)); + + if existing_buffer_colors.peek().is_some() { + colors_splice + .to_remove + .extend(existing_buffer_colors.map(|(_, _, id)| *id)); + } + updated |= colors.set_colors( + buffer_id, + new_buffer_color_inlays, + new_cache_version, + ); } - let mut updated = colors.set_colors(new_color_inlays); - if let Some(cache_version) = cache_version { - colors.cache_version_used = cache_version; - } if colors.render_mode == DocumentColorsRenderMode::Inlay && (!colors_splice.to_insert.is_empty() || !colors_splice.to_remove.is_empty()) diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index bf269ba1d7..dc402be2b6 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -170,6 +170,7 @@ pub struct LocalLspStore { _subscription: gpui::Subscription, lsp_tree: Entity, registered_buffers: HashMap, + buffers_opened_in_servers: HashMap>, buffer_pull_diagnostics_result_ids: HashMap>>, } @@ -2546,6 +2547,10 @@ impl LocalLspStore { vec![snapshot] }); + self.buffers_opened_in_servers + .entry(buffer_id) + .or_default() + .insert(server.server_id()); cx.emit(LspStoreEvent::LanguageServerUpdate { language_server_id: server.server_id(), name: None, @@ -3208,6 +3213,9 @@ impl LocalLspStore { self.language_servers.remove(server_id_to_remove); self.buffer_pull_diagnostics_result_ids .remove(server_id_to_remove); + for buffer_servers in self.buffers_opened_in_servers.values_mut() { + buffer_servers.remove(server_id_to_remove); + } cx.emit(LspStoreEvent::LanguageServerRemoved(*server_id_to_remove)); } servers_to_remove.into_keys().collect() @@ -3787,6 +3795,7 @@ impl LspStore { }), lsp_tree: LanguageServerTree::new(manifest_tree, languages.clone(), cx), registered_buffers: HashMap::default(), + buffers_opened_in_servers: HashMap::default(), buffer_pull_diagnostics_result_ids: HashMap::default(), }), last_formatting_failure: None, @@ -4159,6 +4168,7 @@ impl LspStore { lsp_store.lsp_data.remove(&buffer_id); let local = lsp_store.as_local_mut().unwrap(); local.registered_buffers.remove(&buffer_id); + local.buffers_opened_in_servers.remove(&buffer_id); if let Some(file) = File::from_dyn(buffer.read(cx).file()).cloned() { local.unregister_old_buffer_from_language_servers(&buffer, &file, cx); } @@ -6235,21 +6245,31 @@ impl LspStore { } => { if let Some(cached_data) = self.lsp_data.get(&buffer_id) { if !version_queried_for.changed_since(&cached_data.colors_for_version) { - if Some(cached_data.cache_version) == known_cache_version { - return None; - } else { - return Some( - Task::ready(Ok(DocumentColors { - colors: cached_data - .colors - .values() - .flatten() - .cloned() - .collect(), - cache_version: Some(cached_data.cache_version), - })) - .shared(), - ); + let has_different_servers = self.as_local().is_some_and(|local| { + local + .buffers_opened_in_servers + .get(&buffer_id) + .cloned() + .unwrap_or_default() + != cached_data.colors.keys().copied().collect() + }); + if !has_different_servers { + if Some(cached_data.cache_version) == known_cache_version { + return None; + } else { + return Some( + Task::ready(Ok(DocumentColors { + colors: cached_data + .colors + .values() + .flatten() + .cloned() + .collect(), + cache_version: Some(cached_data.cache_version), + })) + .shared(), + ); + } } } } @@ -7522,6 +7542,14 @@ impl LspStore { .unwrap_or(true) }) .map(|(_, server)| server.server_id()) + .filter(|server_id| { + self.as_local().is_none_or(|local| { + local + .buffers_opened_in_servers + .get(&snapshot.remote_id()) + .is_some_and(|servers| servers.contains(server_id)) + }) + }) .collect::>() }); @@ -10084,6 +10112,7 @@ impl LspStore { } // Tell the language server about every open buffer in the worktree that matches the language. + let mut buffer_paths_registered = Vec::new(); self.buffer_store.clone().update(cx, |buffer_store, cx| { for buffer_handle in buffer_store.buffers() { let buffer = buffer_handle.read(cx); @@ -10142,6 +10171,12 @@ impl LspStore { version, initial_snapshot.text(), ); + buffer_paths_registered.push(file.abs_path(cx)); + local + .buffers_opened_in_servers + .entry(buffer.remote_id()) + .or_default() + .insert(server_id); } buffer_handle.update(cx, |buffer, cx| { buffer.set_completion_triggers( @@ -10163,6 +10198,18 @@ impl LspStore { } }); + for abs_path in buffer_paths_registered { + cx.emit(LspStoreEvent::LanguageServerUpdate { + language_server_id: server_id, + name: Some(adapter.name()), + message: proto::update_language_server::Variant::RegisteredForBuffer( + proto::RegisteredForBuffer { + buffer_abs_path: abs_path.to_string_lossy().to_string(), + }, + ), + }); + } + cx.notify(); } @@ -10612,6 +10659,9 @@ impl LspStore { } if let Some(local) = self.as_local_mut() { local.buffer_pull_diagnostics_result_ids.remove(&for_server); + for buffer_servers in local.buffers_opened_in_servers.values_mut() { + buffer_servers.remove(&for_server); + } } }