From 8e09256c8ccf522aa4107cf9f0ec48fb4f75a8e8 Mon Sep 17 00:00:00 2001 From: Karl-Erik Enkelmann <110300169+playdohface@users.noreply.github.com> Date: Sat, 25 Oct 2025 21:15:32 +0200 Subject: [PATCH] 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 --- Cargo.lock | 2 +- Cargo.toml | 2 +- crates/project/src/lsp_command.rs | 2 +- crates/project/src/project_tests.rs | 11 ++++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2b142c440c973fe7c247b770660e1d937740fc3..fcfe5171c29bed845ce4322a89e57861a6e046c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index e0682924bc377d40a0711630c77ad4dd000515b6..e6080d5a1ca673dec44ce1724c94c5756bbff21c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 55742c284ddcc7dfa6669ea3924fc60a77b2e1ab..a1eef32e8fc545046c6f7978be57ae2cc2131058 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -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(); diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index e3714cddf15d7623ab32403ea9cdd889c27abedc..1d4dbc6c86be9ba80e62c29ef32ce1161a6d1a25 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -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() }],