project: Deduplicate identical language server hover responses (#62266)

Closes https://github.com/zed-industries/zed/issues/62262

## Solution

Identical hover responses from multiple language servers should be
displayed only once.

Different hover responses should still all be preserved, since multiple
language servers may provide complementary information.


## Showcase



https://github.com/user-attachments/assets/67246d9f-ed1c-4f5a-98f5-f10548b7fc6d



---

Release Notes:

- Deduplicated identical language server hover responses

---------

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
This commit is contained in:
Xiaobo Liuandzed-zippy[bot] authored and GitHub committed 2026-08-17 22:47:34 +00:00
1 parent 64d14ea81f
commit fd90c0af7f
2 files changed
+44 -4

No files matched your search

+16 -3
View File
@@ -8284,7 +8284,7 @@ impl LspStore {
.into_iter()
.flatten()
.collect();
Some(hovers)
Some(deduplicate_hovers(hovers))
})
} else {
let all_actions_task = self.request_multiple_lsp_locally(
@@ -8294,13 +8294,13 @@ impl LspStore {
cx,
);
cx.background_spawn(async move {
Some(
Some(deduplicate_hovers(
all_actions_task
.await
.into_iter()
.filter_map(|(_, hover)| remove_empty_hover_blocks(hover?))
.collect::<Vec<Hover>>(),
)
))
})
}
}
@@ -13985,6 +13985,19 @@ fn remove_empty_hover_blocks(mut hover: Hover) -> Option<Hover> {
}
}
fn deduplicate_hovers(hovers: Vec<Hover>) -> Vec<Hover> {
let mut unique_hovers = Vec::with_capacity(hovers.len());
for hover in hovers {
if !unique_hovers
.iter()
.any(|unique: &Hover| unique.contents == hover.contents)
{
unique_hovers.push(hover);
}
}
unique_hovers
}
async fn populate_labels_for_completions(
new_completions: Vec<CoreCompletion>,
language: Option<Arc<Language>>,
@@ -9416,6 +9416,7 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) {
"TailwindServer",
"ESLintServer",
"NoHoverCapabilitiesServer",
"DuplicateTypeScriptServer",
];
let mut language_servers = [
language_registry.register_fake_lsp(
@@ -9462,6 +9463,17 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) {
..FakeLspAdapter::default()
},
),
language_registry.register_fake_lsp(
"tsx",
FakeLspAdapter {
name: language_server_names[4],
capabilities: lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
..lsp::ServerCapabilities::default()
},
..FakeLspAdapter::default()
},
),
];
let (buffer, _handle) = project
@@ -9504,6 +9516,21 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) {
),
);
}
"DuplicateTypeScriptServer" => {
servers_with_hover_requests.insert(
new_server_name,
new_server.set_request_handler::<lsp::request::HoverRequest, _, _>(
|_, _| async move {
Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Scalar(lsp::MarkedString::String(
"TypeScriptServer hover".to_string(),
)),
range: None,
}))
},
),
);
}
"ESLintServer" => {
servers_with_hover_requests.insert(
new_server_name,
@@ -9545,7 +9572,7 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) {
.map(|hover| hover.contents.iter().map(|block| &block.text).join("|"))
.sorted()
.collect::<Vec<_>>(),
"Should receive hover responses from all related servers with hover capabilities"
"Should receive unique hover responses from all related servers with hover capabilities"
);
}