extension: Add support for `labelDetails` for LSP completions (#20144)

bangbangsheshotmedown and Marshall Bowers created

Closes #14278


https://github.com/zed-industries/lsp-types/blob/be7336e92a6ad23f214df19bcdceab17f39531a9/src/completion.rs#L419-L429


https://github.com/zed-industries/lsp-types/blob/be7336e92a6ad23f214df19bcdceab17f39531a9/src/completion.rs#L555-L572


Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>

Change summary

crates/extension_api/wit/since_v0.2.0/lsp.wit           |   7 
crates/extension_host/src/extension_lsp_adapter.rs      |  10 +
crates/extension_host/src/wasm_host/wit.rs              |  28 ++
crates/extension_host/src/wasm_host/wit/since_v0_0_6.rs |   4 
crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs | 101 ++++++++++
5 files changed, 142 insertions(+), 8 deletions(-)

Detailed changes

crates/extension_api/wit/since_v0.2.0/lsp.wit 🔗

@@ -2,6 +2,7 @@ interface lsp {
     /// An LSP completion.
     record completion {
         label: string,
+        label-details: option<completion-label-details>,
         detail: option<string>,
         kind: option<completion-kind>,
         insert-text-format: option<insert-text-format>,
@@ -37,6 +38,12 @@ interface lsp {
         other(s32),
     }
 
+    /// Label details for an LSP completion.
+    record completion-label-details {
+        detail: option<string>,
+        description: option<string>,
+    }
+
     /// Defines how to interpret the insert text in a completion item.
     variant insert-text-format {
         plain-text,

crates/extension_host/src/extension_lsp_adapter.rs 🔗

@@ -387,6 +387,7 @@ impl From<lsp::CompletionItem> for wit::Completion {
     fn from(value: lsp::CompletionItem) -> Self {
         Self {
             label: value.label,
+            label_details: value.label_details.map(Into::into),
             detail: value.detail,
             kind: value.kind.map(Into::into),
             insert_text_format: value.insert_text_format.map(Into::into),
@@ -394,6 +395,15 @@ impl From<lsp::CompletionItem> for wit::Completion {
     }
 }
 
+impl From<lsp::CompletionItemLabelDetails> for wit::CompletionLabelDetails {
+    fn from(value: lsp::CompletionItemLabelDetails) -> Self {
+        Self {
+            detail: value.detail,
+            description: value.description,
+        }
+    }
+}
+
 impl From<lsp::CompletionItemKind> for wit::CompletionKind {
     fn from(value: lsp::CompletionItemKind) -> Self {
         match value {

crates/extension_host/src/wasm_host/wit.rs 🔗

@@ -20,7 +20,9 @@ use wasmtime::{
 #[cfg(test)]
 pub use latest::CodeLabelSpanLiteral;
 pub use latest::{
-    zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
+    zed::extension::lsp::{
+        Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
+    },
     zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
     CodeLabel, CodeLabelSpan, Command, Range, SlashCommand,
 };
@@ -262,7 +264,11 @@ impl Extension {
                     .await
             }
             Extension::V010(ext) => Ok(ext
-                .call_labels_for_completions(store, &language_server_id.0, &completions)
+                .call_labels_for_completions(
+                    store,
+                    &language_server_id.0,
+                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
+                )
                 .await?
                 .map(|labels| {
                     labels
@@ -271,7 +277,11 @@ impl Extension {
                         .collect()
                 })),
             Extension::V006(ext) => Ok(ext
-                .call_labels_for_completions(store, &language_server_id.0, &completions)
+                .call_labels_for_completions(
+                    store,
+                    &language_server_id.0,
+                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
+                )
                 .await?
                 .map(|labels| {
                     labels
@@ -295,7 +305,11 @@ impl Extension {
                     .await
             }
             Extension::V010(ext) => Ok(ext
-                .call_labels_for_symbols(store, &language_server_id.0, &symbols)
+                .call_labels_for_symbols(
+                    store,
+                    &language_server_id.0,
+                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
+                )
                 .await?
                 .map(|labels| {
                     labels
@@ -304,7 +318,11 @@ impl Extension {
                         .collect()
                 })),
             Extension::V006(ext) => Ok(ext
-                .call_labels_for_symbols(store, &language_server_id.0, &symbols)
+                .call_labels_for_symbols(
+                    store,
+                    &language_server_id.0,
+                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
+                )
                 .await?
                 .map(|labels| {
                     labels

crates/extension_host/src/wasm_host/wit/since_v0_0_6.rs 🔗

@@ -1,4 +1,4 @@
-use super::latest;
+use super::{latest, since_v0_1_0};
 use crate::wasm_host::WasmState;
 use anyhow::Result;
 use async_trait::async_trait;
@@ -16,7 +16,7 @@ wasmtime::component::bindgen!({
     with: {
          "worktree": ExtensionWorktree,
          "zed:extension/github": latest::zed::extension::github,
-         "zed:extension/lsp": latest::zed::extension::lsp,
+         "zed:extension/lsp": since_v0_1_0::zed::extension::lsp,
          "zed:extension/nodejs": latest::zed::extension::nodejs,
          "zed:extension/platform": latest::zed::extension::platform,
     },

crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs 🔗

@@ -35,7 +35,6 @@ wasmtime::component::bindgen!({
          "key-value-store": ExtensionKeyValueStore,
          "zed:extension/http-client/http-response-stream": ExtensionHttpResponseStream,
          "zed:extension/github": latest::zed::extension::github,
-         "zed:extension/lsp": latest::zed::extension::lsp,
          "zed:extension/nodejs": latest::zed::extension::nodejs,
          "zed:extension/platform": latest::zed::extension::platform,
          "zed:extension/slash-command": latest::zed::extension::slash_command,
@@ -135,6 +134,103 @@ impl From<CodeLabel> for latest::CodeLabel {
     }
 }
 
+impl From<latest::Completion> for Completion {
+    fn from(value: latest::Completion) -> Self {
+        Self {
+            label: value.label,
+            detail: value.detail,
+            kind: value.kind.map(Into::into),
+            insert_text_format: value.insert_text_format.map(Into::into),
+        }
+    }
+}
+
+impl From<latest::lsp::CompletionKind> for lsp::CompletionKind {
+    fn from(value: latest::lsp::CompletionKind) -> Self {
+        match value {
+            latest::lsp::CompletionKind::Text => Self::Text,
+            latest::lsp::CompletionKind::Method => Self::Method,
+            latest::lsp::CompletionKind::Function => Self::Function,
+            latest::lsp::CompletionKind::Constructor => Self::Constructor,
+            latest::lsp::CompletionKind::Field => Self::Field,
+            latest::lsp::CompletionKind::Variable => Self::Variable,
+            latest::lsp::CompletionKind::Class => Self::Class,
+            latest::lsp::CompletionKind::Interface => Self::Interface,
+            latest::lsp::CompletionKind::Module => Self::Module,
+            latest::lsp::CompletionKind::Property => Self::Property,
+            latest::lsp::CompletionKind::Unit => Self::Unit,
+            latest::lsp::CompletionKind::Value => Self::Value,
+            latest::lsp::CompletionKind::Enum => Self::Enum,
+            latest::lsp::CompletionKind::Keyword => Self::Keyword,
+            latest::lsp::CompletionKind::Snippet => Self::Snippet,
+            latest::lsp::CompletionKind::Color => Self::Color,
+            latest::lsp::CompletionKind::File => Self::File,
+            latest::lsp::CompletionKind::Reference => Self::Reference,
+            latest::lsp::CompletionKind::Folder => Self::Folder,
+            latest::lsp::CompletionKind::EnumMember => Self::EnumMember,
+            latest::lsp::CompletionKind::Constant => Self::Constant,
+            latest::lsp::CompletionKind::Struct => Self::Struct,
+            latest::lsp::CompletionKind::Event => Self::Event,
+            latest::lsp::CompletionKind::Operator => Self::Operator,
+            latest::lsp::CompletionKind::TypeParameter => Self::TypeParameter,
+            latest::lsp::CompletionKind::Other(kind) => Self::Other(kind),
+        }
+    }
+}
+
+impl From<latest::lsp::InsertTextFormat> for lsp::InsertTextFormat {
+    fn from(value: latest::lsp::InsertTextFormat) -> Self {
+        match value {
+            latest::lsp::InsertTextFormat::PlainText => Self::PlainText,
+            latest::lsp::InsertTextFormat::Snippet => Self::Snippet,
+            latest::lsp::InsertTextFormat::Other(value) => Self::Other(value),
+        }
+    }
+}
+
+impl From<latest::lsp::Symbol> for lsp::Symbol {
+    fn from(value: latest::lsp::Symbol) -> Self {
+        Self {
+            name: value.name,
+            kind: value.kind.into(),
+        }
+    }
+}
+
+impl From<latest::lsp::SymbolKind> for lsp::SymbolKind {
+    fn from(value: latest::lsp::SymbolKind) -> Self {
+        match value {
+            latest::lsp::SymbolKind::File => Self::File,
+            latest::lsp::SymbolKind::Module => Self::Module,
+            latest::lsp::SymbolKind::Namespace => Self::Namespace,
+            latest::lsp::SymbolKind::Package => Self::Package,
+            latest::lsp::SymbolKind::Class => Self::Class,
+            latest::lsp::SymbolKind::Method => Self::Method,
+            latest::lsp::SymbolKind::Property => Self::Property,
+            latest::lsp::SymbolKind::Field => Self::Field,
+            latest::lsp::SymbolKind::Constructor => Self::Constructor,
+            latest::lsp::SymbolKind::Enum => Self::Enum,
+            latest::lsp::SymbolKind::Interface => Self::Interface,
+            latest::lsp::SymbolKind::Function => Self::Function,
+            latest::lsp::SymbolKind::Variable => Self::Variable,
+            latest::lsp::SymbolKind::Constant => Self::Constant,
+            latest::lsp::SymbolKind::String => Self::String,
+            latest::lsp::SymbolKind::Number => Self::Number,
+            latest::lsp::SymbolKind::Boolean => Self::Boolean,
+            latest::lsp::SymbolKind::Array => Self::Array,
+            latest::lsp::SymbolKind::Object => Self::Object,
+            latest::lsp::SymbolKind::Key => Self::Key,
+            latest::lsp::SymbolKind::Null => Self::Null,
+            latest::lsp::SymbolKind::EnumMember => Self::EnumMember,
+            latest::lsp::SymbolKind::Struct => Self::Struct,
+            latest::lsp::SymbolKind::Event => Self::Event,
+            latest::lsp::SymbolKind::Operator => Self::Operator,
+            latest::lsp::SymbolKind::TypeParameter => Self::TypeParameter,
+            latest::lsp::SymbolKind::Other(kind) => Self::Other(kind),
+        }
+    }
+}
+
 #[async_trait]
 impl HostKeyValueStore for WasmState {
     async fn insert(
@@ -336,6 +432,9 @@ async fn convert_response(
     Ok(extension_response)
 }
 
+#[async_trait]
+impl lsp::Host for WasmState {}
+
 #[async_trait]
 impl ExtensionImports for WasmState {
     async fn get_settings(