Further improve color inlay hints in multi buffers (#33642)

Follow-up of https://github.com/zed-industries/zed/pull/33605

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2025-06-30 12:18:43 +03:00 committed by GitHub
parent ac3328adb6
commit ae6237178c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 230 additions and 128 deletions

View file

@ -37,7 +37,9 @@ pub use block_map::{
use block_map::{BlockRow, BlockSnapshot}; use block_map::{BlockRow, BlockSnapshot};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
pub use crease_map::*; 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 fold_map::{FoldMap, FoldSnapshot};
use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle}; use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle};
pub use inlay_map::Inlay; pub use inlay_map::Inlay;
@ -538,7 +540,7 @@ impl DisplayMap {
pub fn update_fold_widths( pub fn update_fold_widths(
&mut self, &mut self,
widths: impl IntoIterator<Item = (FoldId, Pixels)>, widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> bool { ) -> bool {
let snapshot = self.buffer.read(cx).snapshot(cx); let snapshot = self.buffer.read(cx).snapshot(cx);

View file

@ -1,4 +1,4 @@
use crate::display_map::inlay_map::InlayChunk; use crate::{InlayId, display_map::inlay_map::InlayChunk};
use super::{ use super::{
Highlights, Highlights,
@ -277,13 +277,16 @@ impl FoldMapWriter<'_> {
pub(crate) fn update_fold_widths( pub(crate) fn update_fold_widths(
&mut self, &mut self,
new_widths: impl IntoIterator<Item = (FoldId, Pixels)>, new_widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
) -> (FoldSnapshot, Vec<FoldEdit>) { ) -> (FoldSnapshot, Vec<FoldEdit>) {
let mut edits = Vec::new(); let mut edits = Vec::new();
let inlay_snapshot = self.0.snapshot.inlay_snapshot.clone(); let inlay_snapshot = self.0.snapshot.inlay_snapshot.clone();
let buffer = &inlay_snapshot.buffer; let buffer = &inlay_snapshot.buffer;
for (id, new_width) in new_widths { 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 let Some(metadata) = self.0.snapshot.fold_metadata_by_id.get(&id).cloned() {
if Some(new_width) != metadata.width { if Some(new_width) != metadata.width {
let buffer_start = metadata.range.start.to_offset(buffer); let buffer_start = metadata.range.start.to_offset(buffer);
@ -529,7 +532,7 @@ impl FoldMap {
placeholder: Some(TransformPlaceholder { placeholder: Some(TransformPlaceholder {
text: ELLIPSIS, text: ELLIPSIS,
renderer: ChunkRenderer { renderer: ChunkRenderer {
id: fold.id, id: ChunkRendererId::Fold(fold.id),
render: Arc::new(move |cx| { render: Arc::new(move |cx| {
(fold.placeholder.render)( (fold.placeholder.render)(
fold_id, fold_id,
@ -1267,11 +1270,17 @@ pub struct Chunk<'a> {
pub renderer: Option<ChunkRenderer>, pub renderer: Option<ChunkRenderer>,
} }
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ChunkRendererId {
Fold(FoldId),
Inlay(InlayId),
}
/// A recipe for how the chunk should be presented. /// A recipe for how the chunk should be presented.
#[derive(Clone)] #[derive(Clone)]
pub struct ChunkRenderer { pub struct ChunkRenderer {
/// The id of the fold associated with this chunk. /// The id of the renderer associated with this chunk.
pub id: FoldId, pub id: ChunkRendererId,
/// Creates a custom element to represent this chunk. /// Creates a custom element to represent this chunk.
pub render: Arc<dyn Send + Sync + Fn(&mut ChunkRendererContext) -> AnyElement>, pub render: Arc<dyn Send + Sync + Fn(&mut ChunkRendererContext) -> AnyElement>,
/// If true, the element is constrained to the shaped width of the text. /// If true, the element is constrained to the shaped width of the text.

View file

@ -1,4 +1,4 @@
use crate::{ChunkRenderer, HighlightStyles, InlayId, display_map::FoldId}; use crate::{ChunkRenderer, HighlightStyles, InlayId};
use collections::BTreeSet; use collections::BTreeSet;
use gpui::{Hsla, Rgba}; use gpui::{Hsla, Rgba};
use language::{Chunk, Edit, Point, TextSummary}; use language::{Chunk, Edit, Point, TextSummary};
@ -14,7 +14,7 @@ use sum_tree::{Bias, Cursor, SumTree};
use text::{Patch, Rope}; use text::{Patch, Rope};
use ui::{ActiveTheme, IntoElement as _, ParentElement as _, Styled as _, div}; 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. /// 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::Hint(_) => self.highlight_styles.inlay_hint,
InlayId::DebuggerValue(_) => self.highlight_styles.inlay_hint, InlayId::DebuggerValue(_) => self.highlight_styles.inlay_hint,
InlayId::Color(id) => { InlayId::Color(_) => {
if let Some(color) = inlay.color { if let Some(color) = inlay.color {
renderer = Some(ChunkRenderer { renderer = Some(ChunkRenderer {
id: FoldId(id), id: ChunkRendererId::Inlay(inlay.id),
render: Arc::new(move |cx| { render: Arc::new(move |cx| {
div() div()
.w_4() .w_4()

View file

@ -17333,9 +17333,9 @@ impl Editor {
self.active_indent_guides_state.dirty = true; self.active_indent_guides_state.dirty = true;
} }
pub fn update_fold_widths( pub fn update_renderer_widths(
&mut self, &mut self,
widths: impl IntoIterator<Item = (FoldId, Pixels)>, widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> bool { ) -> bool {
self.display_map self.display_map

View file

@ -12,8 +12,8 @@ use crate::{
ToggleFold, ToggleFold,
code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP}, code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP},
display_map::{ display_map::{
Block, BlockContext, BlockStyle, DisplaySnapshot, EditorMargins, FoldId, HighlightKey, Block, BlockContext, BlockStyle, ChunkRendererId, DisplaySnapshot, EditorMargins,
HighlightedChunk, ToDisplayPoint, HighlightKey, HighlightedChunk, ToDisplayPoint,
}, },
editor_settings::{ editor_settings::{
CurrentLineHighlight, DocumentColorsRenderMode, DoubleClickInMultibuffer, Minimap, CurrentLineHighlight, DocumentColorsRenderMode, DoubleClickInMultibuffer, Minimap,
@ -7119,7 +7119,7 @@ pub(crate) struct LineWithInvisibles {
enum LineFragment { enum LineFragment {
Text(ShapedLine), Text(ShapedLine),
Element { Element {
id: FoldId, id: ChunkRendererId,
element: Option<AnyElement>, element: Option<AnyElement>,
size: Size<Pixels>, size: Size<Pixels>,
len: usize, len: usize,
@ -8297,7 +8297,7 @@ impl Element for EditorElement {
window, window,
cx, cx,
); );
let new_fold_widths = line_layouts let new_renrerer_widths = line_layouts
.iter() .iter()
.flat_map(|layout| &layout.fragments) .flat_map(|layout| &layout.fragments)
.filter_map(|fragment| { .filter_map(|fragment| {
@ -8308,7 +8308,7 @@ impl Element for EditorElement {
} }
}); });
if self.editor.update(cx, |editor, cx| { 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 // If the fold widths have changed, we need to prepaint
// the element again to account for any changes in // the element again to account for any changes in

View file

@ -19,18 +19,21 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
pub(super) struct LspColorData { pub(super) struct LspColorData {
cache_version_used: usize, buffer_colors: HashMap<BufferId, BufferColors>,
render_mode: DocumentColorsRenderMode,
}
#[derive(Debug, Default)]
struct BufferColors {
colors: Vec<(Range<Anchor>, DocumentColor, InlayId)>, colors: Vec<(Range<Anchor>, DocumentColor, InlayId)>,
inlay_colors: HashMap<InlayId, usize>, inlay_colors: HashMap<InlayId, usize>,
render_mode: DocumentColorsRenderMode, cache_version_used: usize,
} }
impl LspColorData { impl LspColorData {
pub fn new(cx: &App) -> Self { pub fn new(cx: &App) -> Self {
Self { Self {
cache_version_used: 0, buffer_colors: HashMap::default(),
colors: Vec::new(),
inlay_colors: HashMap::default(),
render_mode: EditorSettings::get_global(cx).lsp_document_colors, render_mode: EditorSettings::get_global(cx).lsp_document_colors,
} }
} }
@ -47,8 +50,9 @@ impl LspColorData {
DocumentColorsRenderMode::Inlay => Some(InlaySplice { DocumentColorsRenderMode::Inlay => Some(InlaySplice {
to_remove: Vec::new(), to_remove: Vec::new(),
to_insert: self to_insert: self
.colors .buffer_colors
.iter() .iter()
.flat_map(|(_, buffer_colors)| buffer_colors.colors.iter())
.map(|(range, color, id)| { .map(|(range, color, id)| {
Inlay::color( Inlay::color(
id.id(), id.id(),
@ -63,33 +67,49 @@ impl LspColorData {
}) })
.collect(), .collect(),
}), }),
DocumentColorsRenderMode::None => { DocumentColorsRenderMode::None => Some(InlaySplice {
self.colors.clear(); to_remove: self
Some(InlaySplice { .buffer_colors
to_remove: self.inlay_colors.drain().map(|(id, _)| id).collect(), .drain()
to_insert: Vec::new(), .flat_map(|(_, buffer_colors)| buffer_colors.inlay_colors)
}) .map(|(id, _)| id)
} .collect(),
to_insert: Vec::new(),
}),
DocumentColorsRenderMode::Border | DocumentColorsRenderMode::Background => { DocumentColorsRenderMode::Border | DocumentColorsRenderMode::Background => {
Some(InlaySplice { 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(), to_insert: Vec::new(),
}) })
} }
} }
} }
fn set_colors(&mut self, colors: Vec<(Range<Anchor>, DocumentColor, InlayId)>) -> bool { fn set_colors(
if self.colors == colors { &mut self,
buffer_id: BufferId,
colors: Vec<(Range<Anchor>, DocumentColor, InlayId)>,
cache_version: Option<usize>,
) -> 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; return false;
} }
self.inlay_colors = colors buffer_colors.inlay_colors = colors
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, (_, _, id))| (*id, i)) .map(|(i, (_, _, id))| (*id, i))
.collect(); .collect();
self.colors = colors; buffer_colors.colors = colors;
true true
} }
@ -103,8 +123,9 @@ impl LspColorData {
{ {
Vec::new() Vec::new()
} else { } else {
self.colors self.buffer_colors
.iter() .iter()
.flat_map(|(_, buffer_colors)| &buffer_colors.colors)
.map(|(range, color, _)| { .map(|(range, color, _)| {
let display_range = range.clone().to_display_points(snapshot); let display_range = range.clone().to_display_points(snapshot);
let color = Hsla::from(Rgba { let color = Hsla::from(Rgba {
@ -162,10 +183,9 @@ impl Editor {
ColorFetchStrategy::IgnoreCache ColorFetchStrategy::IgnoreCache
} else { } else {
ColorFetchStrategy::UseCache { ColorFetchStrategy::UseCache {
known_cache_version: self known_cache_version: self.colors.as_ref().and_then(|colors| {
.colors Some(colors.buffer_colors.get(&buffer_id)?.cache_version_used)
.as_ref() }),
.map(|colors| colors.cache_version_used),
} }
}; };
let colors_task = lsp_store.document_colors(fetch_strategy, buffer, cx)?; let colors_task = lsp_store.document_colors(fetch_strategy, buffer, cx)?;
@ -201,15 +221,13 @@ impl Editor {
return; return;
}; };
let mut cache_version = None; let mut new_editor_colors = HashMap::default();
let mut new_editor_colors = Vec::<(Range<Anchor>, DocumentColor)>::new();
for (buffer_id, colors) in all_colors { for (buffer_id, colors) in all_colors {
let Some(excerpts) = editor_excerpts.get(&buffer_id) else { let Some(excerpts) = editor_excerpts.get(&buffer_id) else {
continue; continue;
}; };
match colors { match colors {
Ok(colors) => { Ok(colors) => {
cache_version = colors.cache_version;
for color in colors.colors { for color in colors.colors {
let color_start = point_from_lsp(color.lsp_range.start); let color_start = point_from_lsp(color.lsp_range.start);
let color_end = point_from_lsp(color.lsp_range.end); let color_end = point_from_lsp(color.lsp_range.end);
@ -243,8 +261,15 @@ impl Editor {
continue; continue;
}; };
let new_entry =
new_editor_colors.entry(buffer_id).or_insert_with(|| {
(Vec::<(Range<Anchor>, DocumentColor)>::new(), None)
});
new_entry.1 = colors.cache_version;
let new_buffer_colors = &mut new_entry.0;
let (Ok(i) | Err(i)) = let (Ok(i) | Err(i)) =
new_editor_colors.binary_search_by(|(probe, _)| { new_buffer_colors.binary_search_by(|(probe, _)| {
probe probe
.start .start
.cmp(&color_start_anchor, &multi_buffer_snapshot) .cmp(&color_start_anchor, &multi_buffer_snapshot)
@ -254,7 +279,7 @@ impl Editor {
.cmp(&color_end_anchor, &multi_buffer_snapshot) .cmp(&color_end_anchor, &multi_buffer_snapshot)
}) })
}); });
new_editor_colors new_buffer_colors
.insert(i, (color_start_anchor..color_end_anchor, color)); .insert(i, (color_start_anchor..color_end_anchor, color));
break; break;
} }
@ -267,45 +292,70 @@ impl Editor {
editor editor
.update(cx, |editor, cx| { .update(cx, |editor, cx| {
let mut colors_splice = InlaySplice::default(); 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 { let Some(colors) = &mut editor.colors else {
return; return;
}; };
let mut existing_colors = colors.colors.iter().peekable(); let mut updated = false;
for (new_range, new_color) in new_editor_colors { for (buffer_id, (new_buffer_colors, new_cache_version)) in new_editor_colors {
let rgba_color = Rgba { let mut new_buffer_color_inlays =
r: new_color.color.red, Vec::with_capacity(new_buffer_colors.len());
g: new_color.color.green, let mut existing_buffer_colors = colors
b: new_color.color.blue, .buffer_colors
a: new_color.color.alpha, .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 { loop {
match existing_colors.peek() { match existing_buffer_colors.peek() {
Some((existing_range, existing_color, existing_inlay_id)) => { Some((existing_range, existing_color, existing_inlay_id)) => {
match existing_range match existing_range
.start .start
.cmp(&new_range.start, &multi_buffer_snapshot) .cmp(&new_range.start, &multi_buffer_snapshot)
.then_with(|| { .then_with(|| {
existing_range existing_range
.end .end
.cmp(&new_range.end, &multi_buffer_snapshot) .cmp(&new_range.end, &multi_buffer_snapshot)
}) { }) {
cmp::Ordering::Less => { 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 {
colors_splice.to_remove.push(*existing_inlay_id); 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( let inlay = Inlay::color(
post_inc(&mut editor.next_color_inlay_id), post_inc(&mut editor.next_color_inlay_id),
new_range.start, new_range.start,
@ -313,49 +363,40 @@ impl Editor {
); );
let inlay_id = inlay.id; let inlay_id = inlay.id;
colors_splice.to_insert.push(inlay); colors_splice.to_insert.push(inlay);
new_color_inlays new_buffer_color_inlays
.push((new_range, new_color, inlay_id)); .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 => {
None => { let inlay = Inlay::color(
let inlay = Inlay::color( post_inc(&mut editor.next_color_inlay_id),
post_inc(&mut editor.next_color_inlay_id), new_range.start,
new_range.start, rgba_color,
rgba_color, );
); let inlay_id = inlay.id;
let inlay_id = inlay.id; colors_splice.to_insert.push(inlay);
colors_splice.to_insert.push(inlay); new_buffer_color_inlays
new_color_inlays.push((new_range, new_color, inlay_id)); .push((new_range, new_color, inlay_id));
break; break;
}
} }
} }
} }
}
if existing_colors.peek().is_some() { if existing_buffer_colors.peek().is_some() {
colors_splice colors_splice
.to_remove .to_remove
.extend(existing_colors.map(|(_, _, id)| *id)); .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 if colors.render_mode == DocumentColorsRenderMode::Inlay
&& (!colors_splice.to_insert.is_empty() && (!colors_splice.to_insert.is_empty()
|| !colors_splice.to_remove.is_empty()) || !colors_splice.to_remove.is_empty())

View file

@ -170,6 +170,7 @@ pub struct LocalLspStore {
_subscription: gpui::Subscription, _subscription: gpui::Subscription,
lsp_tree: Entity<LanguageServerTree>, lsp_tree: Entity<LanguageServerTree>,
registered_buffers: HashMap<BufferId, usize>, registered_buffers: HashMap<BufferId, usize>,
buffers_opened_in_servers: HashMap<BufferId, HashSet<LanguageServerId>>,
buffer_pull_diagnostics_result_ids: HashMap<LanguageServerId, HashMap<PathBuf, Option<String>>>, buffer_pull_diagnostics_result_ids: HashMap<LanguageServerId, HashMap<PathBuf, Option<String>>>,
} }
@ -2546,6 +2547,10 @@ impl LocalLspStore {
vec![snapshot] vec![snapshot]
}); });
self.buffers_opened_in_servers
.entry(buffer_id)
.or_default()
.insert(server.server_id());
cx.emit(LspStoreEvent::LanguageServerUpdate { cx.emit(LspStoreEvent::LanguageServerUpdate {
language_server_id: server.server_id(), language_server_id: server.server_id(),
name: None, name: None,
@ -3208,6 +3213,9 @@ impl LocalLspStore {
self.language_servers.remove(server_id_to_remove); self.language_servers.remove(server_id_to_remove);
self.buffer_pull_diagnostics_result_ids self.buffer_pull_diagnostics_result_ids
.remove(server_id_to_remove); .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)); cx.emit(LspStoreEvent::LanguageServerRemoved(*server_id_to_remove));
} }
servers_to_remove.into_keys().collect() servers_to_remove.into_keys().collect()
@ -3787,6 +3795,7 @@ impl LspStore {
}), }),
lsp_tree: LanguageServerTree::new(manifest_tree, languages.clone(), cx), lsp_tree: LanguageServerTree::new(manifest_tree, languages.clone(), cx),
registered_buffers: HashMap::default(), registered_buffers: HashMap::default(),
buffers_opened_in_servers: HashMap::default(),
buffer_pull_diagnostics_result_ids: HashMap::default(), buffer_pull_diagnostics_result_ids: HashMap::default(),
}), }),
last_formatting_failure: None, last_formatting_failure: None,
@ -4159,6 +4168,7 @@ impl LspStore {
lsp_store.lsp_data.remove(&buffer_id); lsp_store.lsp_data.remove(&buffer_id);
let local = lsp_store.as_local_mut().unwrap(); let local = lsp_store.as_local_mut().unwrap();
local.registered_buffers.remove(&buffer_id); 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() { if let Some(file) = File::from_dyn(buffer.read(cx).file()).cloned() {
local.unregister_old_buffer_from_language_servers(&buffer, &file, cx); 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 let Some(cached_data) = self.lsp_data.get(&buffer_id) {
if !version_queried_for.changed_since(&cached_data.colors_for_version) { if !version_queried_for.changed_since(&cached_data.colors_for_version) {
if Some(cached_data.cache_version) == known_cache_version { let has_different_servers = self.as_local().is_some_and(|local| {
return None; local
} else { .buffers_opened_in_servers
return Some( .get(&buffer_id)
Task::ready(Ok(DocumentColors { .cloned()
colors: cached_data .unwrap_or_default()
.colors != cached_data.colors.keys().copied().collect()
.values() });
.flatten() if !has_different_servers {
.cloned() if Some(cached_data.cache_version) == known_cache_version {
.collect(), return None;
cache_version: Some(cached_data.cache_version), } else {
})) return Some(
.shared(), 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) .unwrap_or(true)
}) })
.map(|(_, server)| server.server_id()) .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::<Vec<_>>() .collect::<Vec<_>>()
}); });
@ -10084,6 +10112,7 @@ impl LspStore {
} }
// Tell the language server about every open buffer in the worktree that matches the language. // 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| { self.buffer_store.clone().update(cx, |buffer_store, cx| {
for buffer_handle in buffer_store.buffers() { for buffer_handle in buffer_store.buffers() {
let buffer = buffer_handle.read(cx); let buffer = buffer_handle.read(cx);
@ -10142,6 +10171,12 @@ impl LspStore {
version, version,
initial_snapshot.text(), 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_handle.update(cx, |buffer, cx| {
buffer.set_completion_triggers( 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(); cx.notify();
} }
@ -10612,6 +10659,9 @@ impl LspStore {
} }
if let Some(local) = self.as_local_mut() { if let Some(local) = self.as_local_mut() {
local.buffer_pull_diagnostics_result_ids.remove(&for_server); 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);
}
} }
} }