Handle itemDefaults in CompletionList according to spec (#41187)

Closes https://github.com/zed-extensions/java/issues/101

Previously Zed did not handle resolving CompletionList with itemDefaults
correctly according to the
[LSP-Spec](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#:~:text=/**%0A%09%20*%20The%20edit%20text,%3F%3A%20string%3B).
When a `CompletionList` is provided by the server, that includes ranges
as `itemDefaults`, the field to use for the snippet to insert as
`newText` is in the `textEditText` field, with a fallback to `label` if
that does not exist (`insertText` is ignored).

Release Notes:

- Fixed Java language severs' completion defaults handling on Zed's side
This commit is contained in:
Karl-Erik Enkelmann 2025-10-25 21:15:32 +02:00 committed by GitHub
parent 79ef10bfc3
commit 8e09256c8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 9 additions and 8 deletions

2
Cargo.lock generated
View file

@ -9452,7 +9452,7 @@ dependencies = [
[[package]]
name = "lsp-types"
version = "0.95.1"
source = "git+https://github.com/zed-industries/lsp-types?rev=0874f8742fe55b4dc94308c1e3c0069710d8eeaf#0874f8742fe55b4dc94308c1e3c0069710d8eeaf"
source = "git+https://github.com/zed-industries/lsp-types?rev=b71ab4eeb27d9758be8092020a46fe33fbca4e33#b71ab4eeb27d9758be8092020a46fe33fbca4e33"
dependencies = [
"bitflags 1.3.2",
"serde",

View file

@ -534,7 +534,7 @@ libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "0874f8742fe55b4dc94308c1e3c0069710d8eeaf" }
lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "b71ab4eeb27d9758be8092020a46fe33fbca4e33" }
mach2 = "0.5"
markup5ever_rcdom = "0.3.0"
metal = "0.29"

View file

@ -2240,7 +2240,7 @@ impl LspCommand for GetCompletions {
let lsp_edit = lsp_completion.text_edit.clone().or_else(|| {
let default_text_edit = lsp_defaults.as_deref()?.edit_range.as_ref()?;
let new_text = lsp_completion
.insert_text
.text_edit_text
.as_ref()
.unwrap_or(&lsp_completion.label)
.clone();

View file

@ -3408,7 +3408,7 @@ async fn test_completions_with_edit_ranges(cx: &mut gpui::TestAppContext) {
let fake_server = fake_language_servers.next().await.unwrap();
let text = "let a = obj.fqn";
// Test 1: When text_edit is None but insert_text exists with default edit_range
// Test 1: When text_edit is None but text_edit_text exists with default edit_range
{
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
let completions = project.update(cx, |project, cx| {
@ -3430,7 +3430,7 @@ async fn test_completions_with_edit_ranges(cx: &mut gpui::TestAppContext) {
}),
items: vec![lsp::CompletionItem {
label: "labelText".into(),
insert_text: Some("insertText".into()),
text_edit_text: Some("textEditText".into()),
text_edit: None,
..Default::default()
}],
@ -3448,14 +3448,14 @@ async fn test_completions_with_edit_ranges(cx: &mut gpui::TestAppContext) {
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
assert_eq!(completions.len(), 1);
assert_eq!(completions[0].new_text, "insertText");
assert_eq!(completions[0].new_text, "textEditText");
assert_eq!(
completions[0].replace_range.to_offset(&snapshot),
text.len() - 3..text.len()
);
}
// Test 2: When both text_edit and insert_text are None with default edit_range
// Test 2: When both text_edit and text_edit_text are None with default edit_range
{
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
let completions = project.update(cx, |project, cx| {
@ -3477,7 +3477,8 @@ async fn test_completions_with_edit_ranges(cx: &mut gpui::TestAppContext) {
}),
items: vec![lsp::CompletionItem {
label: "labelText".into(),
insert_text: None,
text_edit_text: None,
insert_text: Some("irrelevant".into()),
text_edit: None,
..Default::default()
}],