Handle itemDefaults in CompletionList according to spec (#41187)

Karl-Erik Enkelmann created

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

Change summary

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(-)

Detailed changes

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",

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"

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();

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()
                     }],