diff --git a/crates/extension/src/extension.rs b/crates/extension/src/extension.rs index 0405c0ad2f..a3c275c537 100644 --- a/crates/extension/src/extension.rs +++ b/crates/extension/src/extension.rs @@ -1,17 +1,20 @@ pub mod extension_builder; mod extension_manifest; -mod slash_command; +mod types; use std::path::{Path, PathBuf}; use std::sync::Arc; +use ::lsp::LanguageServerName; use anyhow::{anyhow, bail, Context as _, Result}; use async_trait::async_trait; +use fs::normalize_path; use gpui::Task; +use language::LanguageName; use semantic_version::SemanticVersion; pub use crate::extension_manifest::*; -pub use crate::slash_command::*; +pub use crate::types::*; #[async_trait] pub trait WorktreeDelegate: Send + Sync + 'static { @@ -34,6 +37,43 @@ pub trait Extension: Send + Sync + 'static { /// Returns the path to this extension's working directory. fn work_dir(&self) -> Arc; + /// Returns a path relative to this extension's working directory. + fn path_from_extension(&self, path: &Path) -> PathBuf { + normalize_path(&self.work_dir().join(path)) + } + + async fn language_server_command( + &self, + language_server_id: LanguageServerName, + language_name: LanguageName, + worktree: Arc, + ) -> Result; + + async fn language_server_initialization_options( + &self, + language_server_id: LanguageServerName, + language_name: LanguageName, + worktree: Arc, + ) -> Result>; + + async fn language_server_workspace_configuration( + &self, + language_server_id: LanguageServerName, + worktree: Arc, + ) -> Result>; + + async fn labels_for_completions( + &self, + language_server_id: LanguageServerName, + completions: Vec, + ) -> Result>>; + + async fn labels_for_symbols( + &self, + language_server_id: LanguageServerName, + symbols: Vec, + ) -> Result>>; + async fn complete_slash_command_argument( &self, command: SlashCommand, @@ -44,7 +84,7 @@ pub trait Extension: Send + Sync + 'static { &self, command: SlashCommand, arguments: Vec, - resource: Option>, + worktree: Option>, ) -> Result; async fn suggest_docs_packages(&self, provider: Arc) -> Result>; diff --git a/crates/extension/src/types.rs b/crates/extension/src/types.rs new file mode 100644 index 0000000000..f4c37b5daf --- /dev/null +++ b/crates/extension/src/types.rs @@ -0,0 +1,49 @@ +mod lsp; +mod slash_command; + +use std::ops::Range; + +pub use lsp::*; +pub use slash_command::*; + +/// A list of environment variables. +pub type EnvVars = Vec<(String, String)>; + +/// A command. +pub struct Command { + /// The command to execute. + pub command: String, + /// The arguments to pass to the command. + pub args: Vec, + /// The environment variables to set for the command. + pub env: EnvVars, +} + +/// A label containing some code. +#[derive(Debug, Clone)] +pub struct CodeLabel { + /// The source code to parse with Tree-sitter. + pub code: String, + /// The spans to display in the label. + pub spans: Vec, + /// The range of the displayed label to include when filtering. + pub filter_range: Range, +} + +/// A span within a code label. +#[derive(Debug, Clone)] +pub enum CodeLabelSpan { + /// A range into the parsed code. + CodeRange(Range), + /// A span containing a code literal. + Literal(CodeLabelSpanLiteral), +} + +/// A span containing a code literal. +#[derive(Debug, Clone)] +pub struct CodeLabelSpanLiteral { + /// The literal text. + pub text: String, + /// The name of the highlight to use for this literal. + pub highlight_name: Option, +} diff --git a/crates/extension/src/types/lsp.rs b/crates/extension/src/types/lsp.rs new file mode 100644 index 0000000000..6e858211cd --- /dev/null +++ b/crates/extension/src/types/lsp.rs @@ -0,0 +1,96 @@ +use std::option::Option; + +/// An LSP completion. +#[derive(Debug, Clone)] +pub struct Completion { + pub label: String, + pub label_details: Option, + pub detail: Option, + pub kind: Option, + pub insert_text_format: Option, +} + +/// The kind of an LSP completion. +#[derive(Debug, Clone, Copy)] +pub enum CompletionKind { + Text, + Method, + Function, + Constructor, + Field, + Variable, + Class, + Interface, + Module, + Property, + Unit, + Value, + Enum, + Keyword, + Snippet, + Color, + File, + Reference, + Folder, + EnumMember, + Constant, + Struct, + Event, + Operator, + TypeParameter, + Other(i32), +} + +/// Label details for an LSP completion. +#[derive(Debug, Clone)] +pub struct CompletionLabelDetails { + pub detail: Option, + pub description: Option, +} + +/// Defines how to interpret the insert text in a completion item. +#[derive(Debug, Clone, Copy)] +pub enum InsertTextFormat { + PlainText, + Snippet, + Other(i32), +} + +/// An LSP symbol. +#[derive(Debug, Clone)] +pub struct Symbol { + pub kind: SymbolKind, + pub name: String, +} + +/// The kind of an LSP symbol. +#[derive(Debug, Clone, Copy)] +pub enum SymbolKind { + File, + Module, + Namespace, + Package, + Class, + Method, + Property, + Field, + Constructor, + Enum, + Interface, + Function, + Variable, + Constant, + String, + Number, + Boolean, + Array, + Object, + Key, + Null, + EnumMember, + Struct, + Event, + Operator, + TypeParameter, + Other(i32), +} diff --git a/crates/extension/src/slash_command.rs b/crates/extension/src/types/slash_command.rs similarity index 100% rename from crates/extension/src/slash_command.rs rename to crates/extension/src/types/slash_command.rs diff --git a/crates/extension_host/src/extension_host.rs b/crates/extension_host/src/extension_host.rs index db292ac6c7..1adea4e0fb 100644 --- a/crates/extension_host/src/extension_host.rs +++ b/crates/extension_host/src/extension_host.rs @@ -5,7 +5,7 @@ pub mod wasm_host; #[cfg(test)] mod extension_store_test; -use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit}; +use crate::extension_lsp_adapter::ExtensionLspAdapter; use anyhow::{anyhow, bail, Context as _, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; @@ -1238,13 +1238,9 @@ impl ExtensionStore { this.registration_hooks.register_lsp_adapter( language.clone(), ExtensionLspAdapter { - extension: wasm_extension.clone(), - host: this.wasm_host.clone(), + extension: extension.clone(), language_server_id: language_server_id.clone(), - config: wit::LanguageServerConfig { - name: language_server_id.0.to_string(), - language_name: language.to_string(), - }, + language_name: language.clone(), }, ); } diff --git a/crates/extension_host/src/extension_lsp_adapter.rs b/crates/extension_host/src/extension_lsp_adapter.rs index 8f0d0f82e3..8f83c68e31 100644 --- a/crates/extension_host/src/extension_lsp_adapter.rs +++ b/crates/extension_host/src/extension_lsp_adapter.rs @@ -1,15 +1,12 @@ -use crate::wasm_host::{ - wit::{self, LanguageServerConfig}, - WasmExtension, WasmHost, -}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use async_trait::async_trait; use collections::HashMap; -use extension::WorktreeDelegate; +use extension::{Extension, WorktreeDelegate}; use futures::{Future, FutureExt}; use gpui::AsyncAppContext; use language::{ - CodeLabel, HighlightId, Language, LanguageToolchainStore, LspAdapter, LspAdapterDelegate, + CodeLabel, HighlightId, Language, LanguageName, LanguageToolchainStore, LspAdapter, + LspAdapterDelegate, }; use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName}; use serde::Serialize; @@ -17,7 +14,6 @@ use serde_json::Value; use std::ops::Range; use std::{any::Any, path::PathBuf, pin::Pin, sync::Arc}; use util::{maybe, ResultExt}; -use wasmtime_wasi::WasiView as _; /// An adapter that allows an [`LspAdapterDelegate`] to be used as a [`WorktreeDelegate`]. pub struct WorktreeDelegateAdapter(pub Arc); @@ -49,16 +45,15 @@ impl WorktreeDelegate for WorktreeDelegateAdapter { } pub struct ExtensionLspAdapter { - pub(crate) extension: WasmExtension, + pub(crate) extension: Arc, pub(crate) language_server_id: LanguageServerName, - pub(crate) config: LanguageServerConfig, - pub(crate) host: Arc, + pub(crate) language_name: LanguageName, } #[async_trait(?Send)] impl LspAdapter for ExtensionLspAdapter { fn name(&self) -> LanguageServerName { - LanguageServerName(self.config.name.clone().into()) + self.language_server_id.clone() } fn get_language_server_command<'a>( @@ -69,33 +64,17 @@ impl LspAdapter for ExtensionLspAdapter { _: &'a mut AsyncAppContext, ) -> Pin>>> { async move { + let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let command = self .extension - .call({ - let this = self.clone(); - |extension, store| { - async move { - let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; - let resource = store.data_mut().table().push(delegate)?; - let command = extension - .call_language_server_command( - store, - &this.language_server_id, - &this.config, - resource, - ) - .await? - .map_err(|e| anyhow!("{}", e))?; - anyhow::Ok(command) - } - .boxed() - } - }) + .language_server_command( + self.language_server_id.clone(), + self.language_name.clone(), + delegate, + ) .await?; - let path = self - .host - .path_from_extension(&self.extension.manifest.id, command.command.as_ref()); + let path = self.extension.path_from_extension(command.command.as_ref()); // TODO: This should now be done via the `zed::make_file_executable` function in // Zed extension API, but we're leaving these existing usages in place temporarily @@ -104,8 +83,8 @@ impl LspAdapter for ExtensionLspAdapter { // We can remove once the following extension versions no longer see any use: // - toml@0.0.2 // - zig@0.0.1 - if ["toml", "zig"].contains(&self.extension.manifest.id.as_ref()) - && path.starts_with(&self.host.work_dir) + if ["toml", "zig"].contains(&self.extension.manifest().id.as_ref()) + && path.starts_with(&self.extension.work_dir()) { #[cfg(not(windows))] { @@ -153,7 +132,7 @@ impl LspAdapter for ExtensionLspAdapter { fn code_action_kinds(&self) -> Option> { let code_action_kinds = self .extension - .manifest + .manifest() .language_servers .get(&self.language_server_id) .and_then(|server| server.code_action_kinds.clone()); @@ -174,14 +153,14 @@ impl LspAdapter for ExtensionLspAdapter { // // We can remove once the following extension versions no longer see any use: // - php@0.0.1 - if self.extension.manifest.id.as_ref() == "php" { + if self.extension.manifest().id.as_ref() == "php" { return HashMap::from_iter([("PHP".into(), "php".into())]); } self.extension - .manifest + .manifest() .language_servers - .get(&LanguageServerName(self.config.name.clone().into())) + .get(&self.language_server_id) .map(|server| server.language_ids.clone()) .unwrap_or_default() } @@ -190,29 +169,14 @@ impl LspAdapter for ExtensionLspAdapter { self: Arc, delegate: &Arc, ) -> Result> { - let delegate = delegate.clone(); + let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let json_options = self .extension - .call({ - let this = self.clone(); - |extension, store| { - async move { - let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; - let resource = store.data_mut().table().push(delegate)?; - let options = extension - .call_language_server_initialization_options( - store, - &this.language_server_id, - &this.config, - resource, - ) - .await? - .map_err(|e| anyhow!("{}", e))?; - anyhow::Ok(options) - } - .boxed() - } - }) + .language_server_initialization_options( + self.language_server_id.clone(), + self.language_name.clone(), + delegate, + ) .await?; Ok(if let Some(json_options) = json_options { serde_json::from_str(&json_options).with_context(|| { @@ -229,32 +193,14 @@ impl LspAdapter for ExtensionLspAdapter { _: Arc, _cx: &mut AsyncAppContext, ) -> Result { - let delegate = delegate.clone(); + let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let json_options: Option = self .extension - .call({ - let this = self.clone(); - |extension, store| { - async move { - let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; - let resource = store.data_mut().table().push(delegate)?; - let options = extension - .call_language_server_workspace_configuration( - store, - &this.language_server_id, - resource, - ) - .await? - .map_err(|e| anyhow!("{}", e))?; - anyhow::Ok(options) - } - .boxed() - } - }) + .language_server_workspace_configuration(self.language_server_id.clone(), delegate) .await?; Ok(if let Some(json_options) = json_options { serde_json::from_str(&json_options).with_context(|| { - format!("failed to parse initialization_options from extension: {json_options}") + format!("failed to parse workspace_configuration from extension: {json_options}") })? } else { serde_json::json!({}) @@ -268,30 +214,16 @@ impl LspAdapter for ExtensionLspAdapter { ) -> Result>> { let completions = completions .iter() - .map(|completion| wit::Completion::from(completion.clone())) + .cloned() + .map(lsp_completion_to_extension) .collect::>(); let labels = self .extension - .call({ - let this = self.clone(); - |extension, store| { - async move { - extension - .call_labels_for_completions( - store, - &this.language_server_id, - completions, - ) - .await? - .map_err(|e| anyhow!("{}", e)) - } - .boxed() - } - }) + .labels_for_completions(self.language_server_id.clone(), completions) .await?; - Ok(labels_from_wit(labels, language)) + Ok(labels_from_extension(labels, language)) } async fn labels_for_symbols( @@ -302,34 +234,29 @@ impl LspAdapter for ExtensionLspAdapter { let symbols = symbols .iter() .cloned() - .map(|(name, kind)| wit::Symbol { + .map(|(name, kind)| extension::Symbol { name, - kind: kind.into(), + kind: lsp_symbol_kind_to_extension(kind), }) .collect::>(); let labels = self .extension - .call({ - let this = self.clone(); - |extension, store| { - async move { - extension - .call_labels_for_symbols(store, &this.language_server_id, symbols) - .await? - .map_err(|e| anyhow!("{}", e)) - } - .boxed() - } - }) + .labels_for_symbols(self.language_server_id.clone(), symbols) .await?; - Ok(labels_from_wit(labels, language)) + Ok(labels_from_extension( + labels + .into_iter() + .map(|label| label.map(Into::into)) + .collect(), + language, + )) } } -fn labels_from_wit( - labels: Vec>, +fn labels_from_extension( + labels: Vec>, language: &Arc, ) -> Vec> { labels @@ -347,7 +274,7 @@ fn labels_from_wit( } fn build_code_label( - label: &wit::CodeLabel, + label: &extension::CodeLabel, parsed_runs: &[(Range, HighlightId)], language: &Arc, ) -> Option { @@ -356,8 +283,7 @@ fn build_code_label( for span in &label.spans { match span { - wit::CodeLabelSpan::CodeRange(range) => { - let range = Range::from(*range); + extension::CodeLabelSpan::CodeRange(range) => { let code_span = &label.code.get(range.clone())?; let mut input_ix = range.start; let mut output_ix = text.len(); @@ -383,7 +309,7 @@ fn build_code_label( text.push_str(code_span); } - wit::CodeLabelSpan::Literal(span) => { + extension::CodeLabelSpan::Literal(span) => { let highlight_id = language .grammar() .zip(span.highlight_name.as_ref()) @@ -398,7 +324,7 @@ fn build_code_label( } } - let filter_range = Range::from(label.filter_range); + let filter_range = label.filter_range.clone(); text.get(filter_range.clone())?; Some(CodeLabel { text, @@ -407,109 +333,101 @@ fn build_code_label( }) } -impl From for Range { - fn from(range: wit::Range) -> Self { - let start = range.start as usize; - let end = range.end as usize; - start..end +fn lsp_completion_to_extension(value: lsp::CompletionItem) -> extension::Completion { + extension::Completion { + label: value.label, + label_details: value + .label_details + .map(lsp_completion_item_label_details_to_extension), + detail: value.detail, + kind: value.kind.map(lsp_completion_item_kind_to_extension), + insert_text_format: value + .insert_text_format + .map(lsp_insert_text_format_to_extension), } } -impl From 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), - } +fn lsp_completion_item_label_details_to_extension( + value: lsp::CompletionItemLabelDetails, +) -> extension::CompletionLabelDetails { + extension::CompletionLabelDetails { + detail: value.detail, + description: value.description, } } -impl From for wit::CompletionLabelDetails { - fn from(value: lsp::CompletionItemLabelDetails) -> Self { - Self { - detail: value.detail, - description: value.description, - } +fn lsp_completion_item_kind_to_extension( + value: lsp::CompletionItemKind, +) -> extension::CompletionKind { + match value { + lsp::CompletionItemKind::TEXT => extension::CompletionKind::Text, + lsp::CompletionItemKind::METHOD => extension::CompletionKind::Method, + lsp::CompletionItemKind::FUNCTION => extension::CompletionKind::Function, + lsp::CompletionItemKind::CONSTRUCTOR => extension::CompletionKind::Constructor, + lsp::CompletionItemKind::FIELD => extension::CompletionKind::Field, + lsp::CompletionItemKind::VARIABLE => extension::CompletionKind::Variable, + lsp::CompletionItemKind::CLASS => extension::CompletionKind::Class, + lsp::CompletionItemKind::INTERFACE => extension::CompletionKind::Interface, + lsp::CompletionItemKind::MODULE => extension::CompletionKind::Module, + lsp::CompletionItemKind::PROPERTY => extension::CompletionKind::Property, + lsp::CompletionItemKind::UNIT => extension::CompletionKind::Unit, + lsp::CompletionItemKind::VALUE => extension::CompletionKind::Value, + lsp::CompletionItemKind::ENUM => extension::CompletionKind::Enum, + lsp::CompletionItemKind::KEYWORD => extension::CompletionKind::Keyword, + lsp::CompletionItemKind::SNIPPET => extension::CompletionKind::Snippet, + lsp::CompletionItemKind::COLOR => extension::CompletionKind::Color, + lsp::CompletionItemKind::FILE => extension::CompletionKind::File, + lsp::CompletionItemKind::REFERENCE => extension::CompletionKind::Reference, + lsp::CompletionItemKind::FOLDER => extension::CompletionKind::Folder, + lsp::CompletionItemKind::ENUM_MEMBER => extension::CompletionKind::EnumMember, + lsp::CompletionItemKind::CONSTANT => extension::CompletionKind::Constant, + lsp::CompletionItemKind::STRUCT => extension::CompletionKind::Struct, + lsp::CompletionItemKind::EVENT => extension::CompletionKind::Event, + lsp::CompletionItemKind::OPERATOR => extension::CompletionKind::Operator, + lsp::CompletionItemKind::TYPE_PARAMETER => extension::CompletionKind::TypeParameter, + _ => extension::CompletionKind::Other(extract_int(value)), } } -impl From for wit::CompletionKind { - fn from(value: lsp::CompletionItemKind) -> Self { - match value { - lsp::CompletionItemKind::TEXT => Self::Text, - lsp::CompletionItemKind::METHOD => Self::Method, - lsp::CompletionItemKind::FUNCTION => Self::Function, - lsp::CompletionItemKind::CONSTRUCTOR => Self::Constructor, - lsp::CompletionItemKind::FIELD => Self::Field, - lsp::CompletionItemKind::VARIABLE => Self::Variable, - lsp::CompletionItemKind::CLASS => Self::Class, - lsp::CompletionItemKind::INTERFACE => Self::Interface, - lsp::CompletionItemKind::MODULE => Self::Module, - lsp::CompletionItemKind::PROPERTY => Self::Property, - lsp::CompletionItemKind::UNIT => Self::Unit, - lsp::CompletionItemKind::VALUE => Self::Value, - lsp::CompletionItemKind::ENUM => Self::Enum, - lsp::CompletionItemKind::KEYWORD => Self::Keyword, - lsp::CompletionItemKind::SNIPPET => Self::Snippet, - lsp::CompletionItemKind::COLOR => Self::Color, - lsp::CompletionItemKind::FILE => Self::File, - lsp::CompletionItemKind::REFERENCE => Self::Reference, - lsp::CompletionItemKind::FOLDER => Self::Folder, - lsp::CompletionItemKind::ENUM_MEMBER => Self::EnumMember, - lsp::CompletionItemKind::CONSTANT => Self::Constant, - lsp::CompletionItemKind::STRUCT => Self::Struct, - lsp::CompletionItemKind::EVENT => Self::Event, - lsp::CompletionItemKind::OPERATOR => Self::Operator, - lsp::CompletionItemKind::TYPE_PARAMETER => Self::TypeParameter, - _ => Self::Other(extract_int(value)), - } +fn lsp_insert_text_format_to_extension( + value: lsp::InsertTextFormat, +) -> extension::InsertTextFormat { + match value { + lsp::InsertTextFormat::PLAIN_TEXT => extension::InsertTextFormat::PlainText, + lsp::InsertTextFormat::SNIPPET => extension::InsertTextFormat::Snippet, + _ => extension::InsertTextFormat::Other(extract_int(value)), } } -impl From for wit::InsertTextFormat { - fn from(value: lsp::InsertTextFormat) -> Self { - match value { - lsp::InsertTextFormat::PLAIN_TEXT => Self::PlainText, - lsp::InsertTextFormat::SNIPPET => Self::Snippet, - _ => Self::Other(extract_int(value)), - } - } -} - -impl From for wit::SymbolKind { - fn from(value: lsp::SymbolKind) -> Self { - match value { - lsp::SymbolKind::FILE => Self::File, - lsp::SymbolKind::MODULE => Self::Module, - lsp::SymbolKind::NAMESPACE => Self::Namespace, - lsp::SymbolKind::PACKAGE => Self::Package, - lsp::SymbolKind::CLASS => Self::Class, - lsp::SymbolKind::METHOD => Self::Method, - lsp::SymbolKind::PROPERTY => Self::Property, - lsp::SymbolKind::FIELD => Self::Field, - lsp::SymbolKind::CONSTRUCTOR => Self::Constructor, - lsp::SymbolKind::ENUM => Self::Enum, - lsp::SymbolKind::INTERFACE => Self::Interface, - lsp::SymbolKind::FUNCTION => Self::Function, - lsp::SymbolKind::VARIABLE => Self::Variable, - lsp::SymbolKind::CONSTANT => Self::Constant, - lsp::SymbolKind::STRING => Self::String, - lsp::SymbolKind::NUMBER => Self::Number, - lsp::SymbolKind::BOOLEAN => Self::Boolean, - lsp::SymbolKind::ARRAY => Self::Array, - lsp::SymbolKind::OBJECT => Self::Object, - lsp::SymbolKind::KEY => Self::Key, - lsp::SymbolKind::NULL => Self::Null, - lsp::SymbolKind::ENUM_MEMBER => Self::EnumMember, - lsp::SymbolKind::STRUCT => Self::Struct, - lsp::SymbolKind::EVENT => Self::Event, - lsp::SymbolKind::OPERATOR => Self::Operator, - lsp::SymbolKind::TYPE_PARAMETER => Self::TypeParameter, - _ => Self::Other(extract_int(value)), - } +fn lsp_symbol_kind_to_extension(value: lsp::SymbolKind) -> extension::SymbolKind { + match value { + lsp::SymbolKind::FILE => extension::SymbolKind::File, + lsp::SymbolKind::MODULE => extension::SymbolKind::Module, + lsp::SymbolKind::NAMESPACE => extension::SymbolKind::Namespace, + lsp::SymbolKind::PACKAGE => extension::SymbolKind::Package, + lsp::SymbolKind::CLASS => extension::SymbolKind::Class, + lsp::SymbolKind::METHOD => extension::SymbolKind::Method, + lsp::SymbolKind::PROPERTY => extension::SymbolKind::Property, + lsp::SymbolKind::FIELD => extension::SymbolKind::Field, + lsp::SymbolKind::CONSTRUCTOR => extension::SymbolKind::Constructor, + lsp::SymbolKind::ENUM => extension::SymbolKind::Enum, + lsp::SymbolKind::INTERFACE => extension::SymbolKind::Interface, + lsp::SymbolKind::FUNCTION => extension::SymbolKind::Function, + lsp::SymbolKind::VARIABLE => extension::SymbolKind::Variable, + lsp::SymbolKind::CONSTANT => extension::SymbolKind::Constant, + lsp::SymbolKind::STRING => extension::SymbolKind::String, + lsp::SymbolKind::NUMBER => extension::SymbolKind::Number, + lsp::SymbolKind::BOOLEAN => extension::SymbolKind::Boolean, + lsp::SymbolKind::ARRAY => extension::SymbolKind::Array, + lsp::SymbolKind::OBJECT => extension::SymbolKind::Object, + lsp::SymbolKind::KEY => extension::SymbolKind::Key, + lsp::SymbolKind::NULL => extension::SymbolKind::Null, + lsp::SymbolKind::ENUM_MEMBER => extension::SymbolKind::EnumMember, + lsp::SymbolKind::STRUCT => extension::SymbolKind::Struct, + lsp::SymbolKind::EVENT => extension::SymbolKind::Event, + lsp::SymbolKind::OPERATOR => extension::SymbolKind::Operator, + lsp::SymbolKind::TYPE_PARAMETER => extension::SymbolKind::TypeParameter, + _ => extension::SymbolKind::Other(extract_int(value)), } } @@ -536,21 +454,14 @@ fn test_build_code_label() { .collect::>(); let label = build_code_label( - &wit::CodeLabel { + &extension::CodeLabel { spans: vec![ - wit::CodeLabelSpan::CodeRange(wit::Range { - start: code.find("pqrs").unwrap() as u32, - end: code.len() as u32, - }), - wit::CodeLabelSpan::CodeRange(wit::Range { - start: code.find(": fn").unwrap() as u32, - end: code.find(" = ").unwrap() as u32, - }), + extension::CodeLabelSpan::CodeRange(code.find("pqrs").unwrap()..code.len()), + extension::CodeLabelSpan::CodeRange( + code.find(": fn").unwrap()..code.find(" = ").unwrap(), + ), ], - filter_range: wit::Range { - start: 0, - end: "pqrs.tuv".len() as u32, - }, + filter_range: 0.."pqrs.tuv".len(), code, }, &code_runs, @@ -588,21 +499,14 @@ fn test_build_code_label_with_invalid_ranges() { // A span uses a code range that is invalid because it starts inside of // a multi-byte character. let label = build_code_label( - &wit::CodeLabel { + &extension::CodeLabel { spans: vec![ - wit::CodeLabelSpan::CodeRange(wit::Range { - start: code.find('B').unwrap() as u32, - end: code.find(" = ").unwrap() as u32, - }), - wit::CodeLabelSpan::CodeRange(wit::Range { - start: code.find('🏀').unwrap() as u32 + 1, - end: code.len() as u32, - }), + extension::CodeLabelSpan::CodeRange( + code.find('B').unwrap()..code.find(" = ").unwrap(), + ), + extension::CodeLabelSpan::CodeRange((code.find('🏀').unwrap() + 1)..code.len()), ], - filter_range: wit::Range { - start: 0, - end: "B".len() as u32, - }, + filter_range: 0.."B".len(), code, }, &code_runs, @@ -612,12 +516,14 @@ fn test_build_code_label_with_invalid_ranges() { // Filter range extends beyond actual text let label = build_code_label( - &wit::CodeLabel { - spans: vec![wit::CodeLabelSpan::Literal(wit::CodeLabelSpanLiteral { - text: "abc".into(), - highlight_name: Some("type".into()), - })], - filter_range: wit::Range { start: 0, end: 5 }, + &extension::CodeLabel { + spans: vec![extension::CodeLabelSpan::Literal( + extension::CodeLabelSpanLiteral { + text: "abc".into(), + highlight_name: Some("type".into()), + }, + )], + filter_range: 0..5, code: String::new(), }, &code_runs, diff --git a/crates/extension_host/src/wasm_host.rs b/crates/extension_host/src/wasm_host.rs index bd66a31a27..54699ac0a1 100644 --- a/crates/extension_host/src/wasm_host.rs +++ b/crates/extension_host/src/wasm_host.rs @@ -4,8 +4,8 @@ use crate::{ExtensionManifest, ExtensionRegistrationHooks}; use anyhow::{anyhow, bail, Context as _, Result}; use async_trait::async_trait; use extension::{ - KeyValueStoreDelegate, SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, - WorktreeDelegate, + CodeLabel, Command, Completion, KeyValueStoreDelegate, SlashCommand, + SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate, }; use fs::{normalize_path, Fs}; use futures::future::LocalBoxFuture; @@ -19,6 +19,8 @@ use futures::{ }; use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task}; use http_client::HttpClient; +use language::LanguageName; +use lsp::LanguageServerName; use node_runtime::NodeRuntime; use release_channel::ReleaseChannel; use semantic_version::SemanticVersion; @@ -65,6 +67,132 @@ impl extension::Extension for WasmExtension { self.work_dir.clone() } + async fn language_server_command( + &self, + language_server_id: LanguageServerName, + language_name: LanguageName, + worktree: Arc, + ) -> Result { + self.call(|extension, store| { + async move { + let resource = store.data_mut().table().push(worktree)?; + let command = extension + .call_language_server_command( + store, + &language_server_id, + &language_name, + resource, + ) + .await? + .map_err(|err| anyhow!("{err}"))?; + + Ok(command.into()) + } + .boxed() + }) + .await + } + + async fn language_server_initialization_options( + &self, + language_server_id: LanguageServerName, + language_name: LanguageName, + worktree: Arc, + ) -> Result> { + self.call(|extension, store| { + async move { + let resource = store.data_mut().table().push(worktree)?; + let options = extension + .call_language_server_initialization_options( + store, + &language_server_id, + &language_name, + resource, + ) + .await? + .map_err(|err| anyhow!("{err}"))?; + anyhow::Ok(options) + } + .boxed() + }) + .await + } + + async fn language_server_workspace_configuration( + &self, + language_server_id: LanguageServerName, + worktree: Arc, + ) -> Result> { + self.call(|extension, store| { + async move { + let resource = store.data_mut().table().push(worktree)?; + let options = extension + .call_language_server_workspace_configuration( + store, + &language_server_id, + resource, + ) + .await? + .map_err(|err| anyhow!("{err}"))?; + anyhow::Ok(options) + } + .boxed() + }) + .await + } + + async fn labels_for_completions( + &self, + language_server_id: LanguageServerName, + completions: Vec, + ) -> Result>> { + self.call(|extension, store| { + async move { + let labels = extension + .call_labels_for_completions( + store, + &language_server_id, + completions.into_iter().map(Into::into).collect(), + ) + .await? + .map_err(|err| anyhow!("{err}"))?; + + Ok(labels + .into_iter() + .map(|label| label.map(Into::into)) + .collect()) + } + .boxed() + }) + .await + } + + async fn labels_for_symbols( + &self, + language_server_id: LanguageServerName, + symbols: Vec, + ) -> Result>> { + self.call(|extension, store| { + async move { + let labels = extension + .call_labels_for_symbols( + store, + &language_server_id, + symbols.into_iter().map(Into::into).collect(), + ) + .await? + .map_err(|err| anyhow!("{err}"))?; + + Ok(labels + .into_iter() + .map(|label| label.map(Into::into)) + .collect()) + } + .boxed() + }) + .await + } + async fn complete_slash_command_argument( &self, command: SlashCommand, @@ -255,7 +383,7 @@ impl WasmHost { Ok(WasmExtension { manifest: manifest.clone(), - work_dir: this.work_dir.clone().into(), + work_dir: this.work_dir.join(manifest.id.as_ref()).into(), tx, zed_api_version, }) @@ -286,11 +414,6 @@ impl WasmHost { .build()) } - pub fn path_from_extension(&self, id: &Arc, path: &Path) -> PathBuf { - let extension_work_dir = self.work_dir.join(id.as_ref()); - normalize_path(&extension_work_dir.join(path)) - } - pub fn writeable_path_from_extension(&self, id: &Arc, path: &Path) -> Result { let extension_work_dir = self.work_dir.join(id.as_ref()); let path = normalize_path(&extension_work_dir.join(path)); diff --git a/crates/extension_host/src/wasm_host/wit.rs b/crates/extension_host/src/wasm_host/wit.rs index 3296c54def..a371b90892 100644 --- a/crates/extension_host/src/wasm_host/wit.rs +++ b/crates/extension_host/src/wasm_host/wit.rs @@ -4,6 +4,7 @@ mod since_v0_0_6; mod since_v0_1_0; mod since_v0_2_0; use extension::{KeyValueStoreDelegate, WorktreeDelegate}; +use language::LanguageName; use lsp::LanguageServerName; use release_channel::ReleaseChannel; use since_v0_2_0 as latest; @@ -163,7 +164,7 @@ impl Extension { &self, store: &mut Store, language_server_id: &LanguageServerName, - config: &LanguageServerConfig, + language_name: &LanguageName, resource: Resource>, ) -> Result> { match self { @@ -180,11 +181,26 @@ impl Extension { .await? .map(|command| command.into())), Extension::V004(ext) => Ok(ext - .call_language_server_command(store, config, resource) + .call_language_server_command( + store, + &LanguageServerConfig { + name: language_server_id.0.to_string(), + language_name: language_name.to_string(), + }, + resource, + ) .await? .map(|command| command.into())), Extension::V001(ext) => Ok(ext - .call_language_server_command(store, &config.clone().into(), resource) + .call_language_server_command( + store, + &LanguageServerConfig { + name: language_server_id.0.to_string(), + language_name: language_name.to_string(), + } + .into(), + resource, + ) .await? .map(|command| command.into())), } @@ -194,7 +210,7 @@ impl Extension { &self, store: &mut Store, language_server_id: &LanguageServerName, - config: &LanguageServerConfig, + language_name: &LanguageName, resource: Resource>, ) -> Result, String>> { match self { @@ -223,13 +239,24 @@ impl Extension { .await } Extension::V004(ext) => { - ext.call_language_server_initialization_options(store, config, resource) - .await + ext.call_language_server_initialization_options( + store, + &LanguageServerConfig { + name: language_server_id.0.to_string(), + language_name: language_name.to_string(), + }, + resource, + ) + .await } Extension::V001(ext) => { ext.call_language_server_initialization_options( store, - &config.clone().into(), + &LanguageServerConfig { + name: language_server_id.0.to_string(), + language_name: language_name.to_string(), + } + .into(), resource, ) .await diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs b/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs index 7e88197ab6..02577abd0e 100644 --- a/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs +++ b/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs @@ -1,4 +1,5 @@ use crate::wasm_host::wit::since_v0_2_0::slash_command::SlashCommandOutputSection; +use crate::wasm_host::wit::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind}; use crate::wasm_host::{wit::ToWasmtimeResult, WasmState}; use ::http_client::{AsyncBody, HttpRequestExt}; use ::settings::{Settings, WorktreeId}; @@ -55,6 +56,159 @@ pub fn linker() -> &'static Linker { LINKER.get_or_init(|| super::new_linker(Extension::add_to_linker)) } +impl From for std::ops::Range { + fn from(range: Range) -> Self { + let start = range.start as usize; + let end = range.end as usize; + start..end + } +} + +impl From for extension::Command { + fn from(value: Command) -> Self { + Self { + command: value.command, + args: value.args, + env: value.env, + } + } +} + +impl From for extension::CodeLabel { + fn from(value: CodeLabel) -> Self { + Self { + code: value.code, + spans: value.spans.into_iter().map(Into::into).collect(), + filter_range: value.filter_range.into(), + } + } +} + +impl From for extension::CodeLabelSpan { + fn from(value: CodeLabelSpan) -> Self { + match value { + CodeLabelSpan::CodeRange(range) => Self::CodeRange(range.into()), + CodeLabelSpan::Literal(literal) => Self::Literal(literal.into()), + } + } +} + +impl From for extension::CodeLabelSpanLiteral { + fn from(value: CodeLabelSpanLiteral) -> Self { + Self { + text: value.text, + highlight_name: value.highlight_name, + } + } +} + +impl From for Completion { + fn from(value: extension::Completion) -> 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), + } + } +} + +impl From for CompletionLabelDetails { + fn from(value: extension::CompletionLabelDetails) -> Self { + Self { + detail: value.detail, + description: value.description, + } + } +} + +impl From for CompletionKind { + fn from(value: extension::CompletionKind) -> Self { + match value { + extension::CompletionKind::Text => Self::Text, + extension::CompletionKind::Method => Self::Method, + extension::CompletionKind::Function => Self::Function, + extension::CompletionKind::Constructor => Self::Constructor, + extension::CompletionKind::Field => Self::Field, + extension::CompletionKind::Variable => Self::Variable, + extension::CompletionKind::Class => Self::Class, + extension::CompletionKind::Interface => Self::Interface, + extension::CompletionKind::Module => Self::Module, + extension::CompletionKind::Property => Self::Property, + extension::CompletionKind::Unit => Self::Unit, + extension::CompletionKind::Value => Self::Value, + extension::CompletionKind::Enum => Self::Enum, + extension::CompletionKind::Keyword => Self::Keyword, + extension::CompletionKind::Snippet => Self::Snippet, + extension::CompletionKind::Color => Self::Color, + extension::CompletionKind::File => Self::File, + extension::CompletionKind::Reference => Self::Reference, + extension::CompletionKind::Folder => Self::Folder, + extension::CompletionKind::EnumMember => Self::EnumMember, + extension::CompletionKind::Constant => Self::Constant, + extension::CompletionKind::Struct => Self::Struct, + extension::CompletionKind::Event => Self::Event, + extension::CompletionKind::Operator => Self::Operator, + extension::CompletionKind::TypeParameter => Self::TypeParameter, + extension::CompletionKind::Other(value) => Self::Other(value), + } + } +} + +impl From for InsertTextFormat { + fn from(value: extension::InsertTextFormat) -> Self { + match value { + extension::InsertTextFormat::PlainText => Self::PlainText, + extension::InsertTextFormat::Snippet => Self::Snippet, + extension::InsertTextFormat::Other(value) => Self::Other(value), + } + } +} + +impl From for Symbol { + fn from(value: extension::Symbol) -> Self { + Self { + kind: value.kind.into(), + name: value.name, + } + } +} + +impl From for SymbolKind { + fn from(value: extension::SymbolKind) -> Self { + match value { + extension::SymbolKind::File => Self::File, + extension::SymbolKind::Module => Self::Module, + extension::SymbolKind::Namespace => Self::Namespace, + extension::SymbolKind::Package => Self::Package, + extension::SymbolKind::Class => Self::Class, + extension::SymbolKind::Method => Self::Method, + extension::SymbolKind::Property => Self::Property, + extension::SymbolKind::Field => Self::Field, + extension::SymbolKind::Constructor => Self::Constructor, + extension::SymbolKind::Enum => Self::Enum, + extension::SymbolKind::Interface => Self::Interface, + extension::SymbolKind::Function => Self::Function, + extension::SymbolKind::Variable => Self::Variable, + extension::SymbolKind::Constant => Self::Constant, + extension::SymbolKind::String => Self::String, + extension::SymbolKind::Number => Self::Number, + extension::SymbolKind::Boolean => Self::Boolean, + extension::SymbolKind::Array => Self::Array, + extension::SymbolKind::Object => Self::Object, + extension::SymbolKind::Key => Self::Key, + extension::SymbolKind::Null => Self::Null, + extension::SymbolKind::EnumMember => Self::EnumMember, + extension::SymbolKind::Struct => Self::Struct, + extension::SymbolKind::Event => Self::Event, + extension::SymbolKind::Operator => Self::Operator, + extension::SymbolKind::TypeParameter => Self::TypeParameter, + extension::SymbolKind::Other(value) => Self::Other(value), + } + } +} + impl From for SlashCommand { fn from(value: extension::SlashCommand) -> Self { Self { diff --git a/crates/indexed_docs/src/extension_indexed_docs_provider.rs b/crates/indexed_docs/src/extension_indexed_docs_provider.rs index 067abeef13..ed006546fe 100644 --- a/crates/indexed_docs/src/extension_indexed_docs_provider.rs +++ b/crates/indexed_docs/src/extension_indexed_docs_provider.rs @@ -26,7 +26,6 @@ impl IndexedDocsProvider for ExtensionIndexedDocsProvider { fn database_path(&self) -> PathBuf { let mut database_path = PathBuf::from(self.extension.work_dir().as_ref()); - database_path.push(self.extension.manifest().id.as_ref()); database_path.push("docs"); database_path.push(format!("{}.0.mdb", self.id));