project: Don't send context.only for generic code action requests (#50979)

Closes #48917

## Summary
- Stop sending `context.only` for generic `textDocument/codeAction`
requests.
 - Keep explicit kind filtered requests unchanged.
- Add regression coverage for generic code action requests so actions
like `source.addTest` remain visible.

## Root Cause
`GetCodeActions::to_lsp` populated `context.only` even when the caller
requested all code actions (`kinds == None`). That turned the normal
code actions menu into a filtered request. With `gopls`, this filtered
out `source.addTest`, so `Add test for ...` never appeared.

## Verification
 - `cargo fmt --all -- --check`
 - `./script/clippy -p project`
 - `cargo nextest run -p project --no-fail-fast --no-tests=warn`
- `cargo test -p editor
editor_tests::test_organize_imports_manual_trigger -- --exact`
- `cargo test -p editor
editor_tests::test_context_menus_hide_hover_popover -- --exact`

## Manual Testing
- Repro'd the protocol level behavior against `gopls`: unfiltered
requests return `source.addTest`, filtered requests excluding it do not.
 - Opened `zed_guild/testing_projects/act/pkg/artifactcache/handler.go`
 - Triggered `Show Code Actions` on `StartHandler`
 - Confirmed `Add test for StartHandler` appears


Release Notes:

- Fixed Go `gopls` code actions so `Add test for ...` appears in the
generic code actions menu.

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
loadingaliasandKirill Bulatov authored and GitHub committed 2026-03-19 15:59:52 +00:00
1 parent b0b720cf8c
commit 3f023836c2
2 files changed
+90 -5

No files matched your search

+4 -5
View File
@@ -2636,11 +2636,10 @@ impl LspCommand for GetCodeActions {
relevant_diagnostics.push(entry.to_lsp_diagnostic_stub()?);
}
let supported =
Self::supported_code_action_kinds(language_server.adapter_server_capabilities());
let only = if let Some(requested) = &self.kinds {
if let Some(supported_kinds) = supported {
if let Some(supported_kinds) =
Self::supported_code_action_kinds(language_server.adapter_server_capabilities())
{
let filtered = requested
.iter()
.filter(|requested_kind| {
@@ -2655,7 +2654,7 @@ impl LspCommand for GetCodeActions {
Some(requested.clone())
}
} else {
supported
None
};
Ok(lsp::CodeActionParams {
@@ -7755,6 +7755,92 @@ async fn test_code_actions_only_kinds(cx: &mut gpui::TestAppContext) {
);
}
#[gpui::test]
async fn test_code_actions_without_requested_kinds_do_not_send_only_filter(
cx: &mut gpui::TestAppContext,
) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
path!("/dir"),
json!({
"a.ts": "a",
}),
)
.await;
let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(typescript_lang());
let mut fake_language_servers = language_registry.register_fake_lsp(
"TypeScript",
FakeLspAdapter {
capabilities: lsp::ServerCapabilities {
code_action_provider: Some(lsp::CodeActionProviderCapability::Options(
lsp::CodeActionOptions {
code_action_kinds: Some(vec![
CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
"source.doc".into(),
]),
..lsp::CodeActionOptions::default()
},
)),
..lsp::ServerCapabilities::default()
},
..FakeLspAdapter::default()
},
);
let (buffer, _handle) = project
.update(cx, |p, cx| {
p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
})
.await
.unwrap();
cx.executor().run_until_parked();
let fake_server = fake_language_servers
.next()
.await
.expect("failed to get the language server");
let mut request_handled = fake_server.set_request_handler::<
lsp::request::CodeActionRequest,
_,
_,
>(move |params, _| async move {
assert_eq!(
params.context.only, None,
"Code action requests without explicit kind filters should not send `context.only`"
);
Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
lsp::CodeAction {
title: "Add test".to_string(),
kind: Some("source.addTest".into()),
..lsp::CodeAction::default()
},
)]))
});
let code_actions_task = project.update(cx, |project, cx| {
project.code_actions(&buffer, 0..buffer.read(cx).len(), None, cx)
});
let () = request_handled
.next()
.await
.expect("The code action request should have been triggered");
let code_actions = code_actions_task.await.unwrap().unwrap();
assert_eq!(code_actions.len(), 1);
assert_eq!(
code_actions[0].lsp_action.action_kind(),
Some("source.addTest".into())
);
}
#[gpui::test]
async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) {
init_test(cx);