Detailed changes
@@ -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<Path>;
+ /// 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<dyn WorktreeDelegate>,
+ ) -> Result<Command>;
+
+ async fn language_server_initialization_options(
+ &self,
+ language_server_id: LanguageServerName,
+ language_name: LanguageName,
+ worktree: Arc<dyn WorktreeDelegate>,
+ ) -> Result<Option<String>>;
+
+ async fn language_server_workspace_configuration(
+ &self,
+ language_server_id: LanguageServerName,
+ worktree: Arc<dyn WorktreeDelegate>,
+ ) -> Result<Option<String>>;
+
+ async fn labels_for_completions(
+ &self,
+ language_server_id: LanguageServerName,
+ completions: Vec<Completion>,
+ ) -> Result<Vec<Option<CodeLabel>>>;
+
+ async fn labels_for_symbols(
+ &self,
+ language_server_id: LanguageServerName,
+ symbols: Vec<Symbol>,
+ ) -> Result<Vec<Option<CodeLabel>>>;
+
async fn complete_slash_command_argument(
&self,
command: SlashCommand,
@@ -44,7 +84,7 @@ pub trait Extension: Send + Sync + 'static {
&self,
command: SlashCommand,
arguments: Vec<String>,
- resource: Option<Arc<dyn WorktreeDelegate>>,
+ worktree: Option<Arc<dyn WorktreeDelegate>>,
) -> Result<SlashCommandOutput>;
async fn suggest_docs_packages(&self, provider: Arc<str>) -> Result<Vec<String>>;
@@ -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<String>,
+ /// 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<CodeLabelSpan>,
+ /// The range of the displayed label to include when filtering.
+ pub filter_range: Range<usize>,
+}
+
+/// A span within a code label.
+#[derive(Debug, Clone)]
+pub enum CodeLabelSpan {
+ /// A range into the parsed code.
+ CodeRange(Range<usize>),
+ /// 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<String>,
+}
@@ -0,0 +1,96 @@
+use std::option::Option;
+
+/// An LSP completion.
+#[derive(Debug, Clone)]
+pub struct Completion {
+ pub label: String,
+ pub label_details: Option<CompletionLabelDetails>,
+ pub detail: Option<String>,
+ pub kind: Option<CompletionKind>,
+ pub insert_text_format: Option<InsertTextFormat>,
+}
+
+/// 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<String>,
+ pub description: Option<String>,
+}
+
+/// 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),
+}
@@ -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(),
},
);
}
@@ -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<dyn LspAdapterDelegate>);
@@ -49,16 +45,15 @@ impl WorktreeDelegate for WorktreeDelegateAdapter {
}
pub struct ExtensionLspAdapter {
- pub(crate) extension: WasmExtension,
+ pub(crate) extension: Arc<dyn Extension>,
pub(crate) language_server_id: LanguageServerName,
- pub(crate) config: LanguageServerConfig,
- pub(crate) host: Arc<WasmHost>,
+ 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<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
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<Vec<CodeActionKind>> {
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<Self>,
delegate: &Arc<dyn LspAdapterDelegate>,
) -> Result<Option<serde_json::Value>> {
- 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<dyn LanguageToolchainStore>,
_cx: &mut AsyncAppContext,
) -> Result<Value> {
- let delegate = delegate.clone();
+ let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _;
let json_options: Option<String> = 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<Vec<Option<CodeLabel>>> {
let completions = completions
.iter()
- .map(|completion| wit::Completion::from(completion.clone()))
+ .cloned()
+ .map(lsp_completion_to_extension)
.collect::<Vec<_>>();
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::<Vec<_>>();
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<Option<wit::CodeLabel>>,
+fn labels_from_extension(
+ labels: Vec<Option<extension::CodeLabel>>,
language: &Arc<Language>,
) -> Vec<Option<CodeLabel>> {
labels
@@ -347,7 +274,7 @@ fn labels_from_wit(
}
fn build_code_label(
- label: &wit::CodeLabel,
+ label: &extension::CodeLabel,
parsed_runs: &[(Range<usize>, HighlightId)],
language: &Arc<Language>,
) -> Option<CodeLabel> {
@@ -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<wit::Range> for Range<usize> {
- fn from(range: wit::Range) -> Self {
- let start = range.start as usize;
- let end = range.end as usize;
- start..end
- }
-}
-
-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),
- }
+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<lsp::CompletionItemLabelDetails> for wit::CompletionLabelDetails {
- fn from(value: lsp::CompletionItemLabelDetails) -> Self {
- Self {
- detail: value.detail,
- description: value.description,
- }
+fn lsp_completion_item_label_details_to_extension(
+ value: lsp::CompletionItemLabelDetails,
+) -> extension::CompletionLabelDetails {
+ extension::CompletionLabelDetails {
+ detail: value.detail,
+ description: value.description,
}
}
-impl From<lsp::CompletionItemKind> 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_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<lsp::InsertTextFormat> 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)),
- }
+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<lsp::SymbolKind> 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::<Vec<_>>();
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,
@@ -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<dyn WorktreeDelegate>,
+ ) -> Result<Command> {
+ 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<dyn WorktreeDelegate>,
+ ) -> Result<Option<String>> {
+ 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<dyn WorktreeDelegate>,
+ ) -> Result<Option<String>> {
+ 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<Completion>,
+ ) -> Result<Vec<Option<CodeLabel>>> {
+ 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<Symbol>,
+ ) -> Result<Vec<Option<CodeLabel>>> {
+ 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<str>, 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<str>, path: &Path) -> Result<PathBuf> {
let extension_work_dir = self.work_dir.join(id.as_ref());
let path = normalize_path(&extension_work_dir.join(path));
@@ -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<WasmState>,
language_server_id: &LanguageServerName,
- config: &LanguageServerConfig,
+ language_name: &LanguageName,
resource: Resource<Arc<dyn WorktreeDelegate>>,
) -> Result<Result<Command, String>> {
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<WasmState>,
language_server_id: &LanguageServerName,
- config: &LanguageServerConfig,
+ language_name: &LanguageName,
resource: Resource<Arc<dyn WorktreeDelegate>>,
) -> Result<Result<Option<String>, 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
@@ -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<WasmState> {
LINKER.get_or_init(|| super::new_linker(Extension::add_to_linker))
}
+impl From<Range> for std::ops::Range<usize> {
+ fn from(range: Range) -> Self {
+ let start = range.start as usize;
+ let end = range.end as usize;
+ start..end
+ }
+}
+
+impl From<Command> for extension::Command {
+ fn from(value: Command) -> Self {
+ Self {
+ command: value.command,
+ args: value.args,
+ env: value.env,
+ }
+ }
+}
+
+impl From<CodeLabel> 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<CodeLabelSpan> 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<CodeLabelSpanLiteral> for extension::CodeLabelSpanLiteral {
+ fn from(value: CodeLabelSpanLiteral) -> Self {
+ Self {
+ text: value.text,
+ highlight_name: value.highlight_name,
+ }
+ }
+}
+
+impl From<extension::Completion> 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<extension::CompletionLabelDetails> for CompletionLabelDetails {
+ fn from(value: extension::CompletionLabelDetails) -> Self {
+ Self {
+ detail: value.detail,
+ description: value.description,
+ }
+ }
+}
+
+impl From<extension::CompletionKind> 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<extension::InsertTextFormat> 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<extension::Symbol> for Symbol {
+ fn from(value: extension::Symbol) -> Self {
+ Self {
+ kind: value.kind.into(),
+ name: value.name,
+ }
+ }
+}
+
+impl From<extension::SymbolKind> 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<extension::SlashCommand> for SlashCommand {
fn from(value: extension::SlashCommand) -> Self {
Self {
@@ -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));