Detailed changes
@@ -76,6 +76,20 @@ pub trait Extension: Send + Sync + 'static {
worktree: Arc<dyn WorktreeDelegate>,
) -> Result<Option<String>>;
+ async fn language_server_additional_initialization_options(
+ &self,
+ language_server_id: LanguageServerName,
+ target_language_server_id: LanguageServerName,
+ worktree: Arc<dyn WorktreeDelegate>,
+ ) -> Result<Option<String>>;
+
+ async fn language_server_additional_workspace_configuration(
+ &self,
+ language_server_id: LanguageServerName,
+ target_language_server_id: LanguageServerName,
+ worktree: Arc<dyn WorktreeDelegate>,
+ ) -> Result<Option<String>>;
+
async fn labels_for_completions(
&self,
language_server_id: LanguageServerName,
@@ -93,6 +93,26 @@ pub trait Extension: Send + Sync {
Ok(None)
}
+ /// Returns the initialization options to pass to the other language server.
+ fn language_server_additional_initialization_options(
+ &mut self,
+ _language_server_id: &LanguageServerId,
+ _target_language_server_id: &LanguageServerId,
+ _worktree: &Worktree,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(None)
+ }
+
+ /// Returns the workspace configuration options to pass to the other language server.
+ fn language_server_additional_workspace_configuration(
+ &mut self,
+ _language_server_id: &LanguageServerId,
+ _target_language_server_id: &LanguageServerId,
+ _worktree: &Worktree,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(None)
+ }
+
/// Returns the label for the given completion.
fn label_for_completion(
&self,
@@ -235,6 +255,38 @@ impl wit::Guest for Component {
.and_then(|value| serde_json::to_string(&value).ok()))
}
+ fn language_server_additional_initialization_options(
+ language_server_id: String,
+ target_language_server_id: String,
+ worktree: &Worktree,
+ ) -> Result<Option<String>, String> {
+ let language_server_id = LanguageServerId(language_server_id);
+ let target_language_server_id = LanguageServerId(target_language_server_id);
+ Ok(extension()
+ .language_server_additional_initialization_options(
+ &language_server_id,
+ &target_language_server_id,
+ worktree,
+ )?
+ .and_then(|value| serde_json::to_string(&value).ok()))
+ }
+
+ fn language_server_additional_workspace_configuration(
+ language_server_id: String,
+ target_language_server_id: String,
+ worktree: &Worktree,
+ ) -> Result<Option<String>, String> {
+ let language_server_id = LanguageServerId(language_server_id);
+ let target_language_server_id = LanguageServerId(target_language_server_id);
+ Ok(extension()
+ .language_server_additional_workspace_configuration(
+ &language_server_id,
+ &target_language_server_id,
+ worktree,
+ )?
+ .and_then(|value| serde_json::to_string(&value).ok()))
+ }
+
fn labels_for_completions(
language_server_id: String,
completions: Vec<Completion>,
@@ -95,6 +95,12 @@ world extension {
/// Returns the workspace configuration options to pass to the language server.
export language-server-workspace-configuration: func(language-server-id: string, worktree: borrow<worktree>) -> result<option<string>, string>;
+ /// Returns the initialization options to pass to the other language server.
+ export language-server-additional-initialization-options: func(language-server-id: string, target-language-server-id: string, worktree: borrow<worktree>) -> result<option<string>, string>;
+
+ /// Returns the workspace configuration options to pass to the other language server.
+ export language-server-additional-workspace-configuration: func(language-server-id: string, target-language-server-id: string, worktree: borrow<worktree>) -> result<option<string>, string>;
+
/// A label containing some code.
record code-label {
/// The source code to parse with Tree-sitter.
@@ -140,6 +140,56 @@ impl extension::Extension for WasmExtension {
.await
}
+ async fn language_server_additional_initialization_options(
+ &self,
+ language_server_id: LanguageServerName,
+ target_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_additional_initialization_options(
+ store,
+ &language_server_id,
+ &target_language_server_id,
+ resource,
+ )
+ .await?
+ .map_err(|err| anyhow!("{err}"))?;
+ anyhow::Ok(options)
+ }
+ .boxed()
+ })
+ .await
+ }
+
+ async fn language_server_additional_workspace_configuration(
+ &self,
+ language_server_id: LanguageServerName,
+ target_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_additional_workspace_configuration(
+ store,
+ &language_server_id,
+ &target_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,
@@ -365,6 +365,58 @@ impl Extension {
}
}
+ pub async fn call_language_server_additional_initialization_options(
+ &self,
+ store: &mut Store<WasmState>,
+ language_server_id: &LanguageServerName,
+ target_language_server_id: &LanguageServerName,
+ resource: Resource<Arc<dyn WorktreeDelegate>>,
+ ) -> Result<Result<Option<String>, String>> {
+ match self {
+ Extension::V040(ext) => {
+ ext.call_language_server_additional_initialization_options(
+ store,
+ &language_server_id.0,
+ &target_language_server_id.0,
+ resource,
+ )
+ .await
+ }
+ Extension::V030(_)
+ | Extension::V020(_)
+ | Extension::V010(_)
+ | Extension::V006(_)
+ | Extension::V004(_)
+ | Extension::V001(_) => Ok(Ok(None)),
+ }
+ }
+
+ pub async fn call_language_server_additional_workspace_configuration(
+ &self,
+ store: &mut Store<WasmState>,
+ language_server_id: &LanguageServerName,
+ target_language_server_id: &LanguageServerName,
+ resource: Resource<Arc<dyn WorktreeDelegate>>,
+ ) -> Result<Result<Option<String>, String>> {
+ match self {
+ Extension::V040(ext) => {
+ ext.call_language_server_additional_workspace_configuration(
+ store,
+ &language_server_id.0,
+ &target_language_server_id.0,
+ resource,
+ )
+ .await
+ }
+ Extension::V030(_)
+ | Extension::V020(_)
+ | Extension::V010(_)
+ | Extension::V006(_)
+ | Extension::V004(_)
+ | Extension::V001(_) => Ok(Ok(None)),
+ }
+ }
+
pub async fn call_labels_for_completions(
&self,
store: &mut Store<WasmState>,
@@ -305,6 +305,7 @@ pub trait LspAdapterDelegate: Send + Sync {
fn worktree_root_path(&self) -> &Path;
fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool;
fn update_status(&self, language: LanguageServerName, status: BinaryStatus);
+ fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>>;
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>;
async fn npm_package_installed_version(
@@ -515,6 +516,26 @@ pub trait LspAdapter: 'static + Send + Sync {
Ok(serde_json::json!({}))
}
+ async fn additional_initialization_options(
+ self: Arc<Self>,
+ _target_language_server_id: LanguageServerName,
+ _: &dyn Fs,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<Value>> {
+ Ok(None)
+ }
+
+ async fn additional_workspace_configuration(
+ self: Arc<Self>,
+ _target_language_server_id: LanguageServerName,
+ _: &dyn Fs,
+ _: &Arc<dyn LspAdapterDelegate>,
+ _: Arc<dyn LanguageToolchainStore>,
+ _cx: &mut AsyncApp,
+ ) -> Result<Option<Value>> {
+ Ok(None)
+ }
+
/// Returns a list of code actions supported by a given LspAdapter
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
Some(vec![
@@ -912,6 +912,15 @@ impl LanguageRegistry {
.unwrap_or_default()
}
+ pub fn all_lsp_adapters(&self) -> Vec<Arc<CachedLspAdapter>> {
+ self.state
+ .read()
+ .all_lsp_adapters
+ .values()
+ .cloned()
+ .collect()
+ }
+
pub fn adapter_for_name(&self, name: &LanguageServerName) -> Option<Arc<CachedLspAdapter>> {
self.state.read().all_lsp_adapters.get(name).cloned()
}
@@ -267,6 +267,58 @@ impl LspAdapter for ExtensionLspAdapter {
})
}
+ async fn additional_initialization_options(
+ self: Arc<Self>,
+ target_language_server_id: LanguageServerName,
+ _: &dyn Fs,
+ delegate: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _;
+ let json_options: Option<String> = self
+ .extension
+ .language_server_additional_initialization_options(
+ self.language_server_id.clone(),
+ target_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 additional_initialization_options from extension: {json_options}"
+ )
+ })?
+ } else {
+ None
+ })
+ }
+
+ async fn additional_workspace_configuration(
+ self: Arc<Self>,
+ target_language_server_id: LanguageServerName,
+ _: &dyn Fs,
+ delegate: &Arc<dyn LspAdapterDelegate>,
+ _: Arc<dyn LanguageToolchainStore>,
+ _cx: &mut AsyncApp,
+ ) -> Result<Option<serde_json::Value>> {
+ let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _;
+ let json_options: Option<String> = self
+ .extension
+ .language_server_additional_workspace_configuration(
+ self.language_server_id.clone(),
+ target_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 additional_workspace_configuration from extension: {json_options}")
+ })?
+ } else {
+ None
+ })
+ }
+
async fn labels_for_completions(
self: Arc<Self>,
completions: &[lsp::CompletionItem],
@@ -251,17 +251,21 @@ impl LocalLspStore {
let toolchains = this.update(cx, |this, cx| this.toolchain_store(cx))?;
let language_server = pending_server.await?;
- let workspace_config = adapter
- .adapter
- .clone()
- .workspace_configuration(fs.as_ref(), &delegate, toolchains.clone(), cx)
- .await?;
+ let workspace_config = Self::workspace_configuration_for_adapter(
+ adapter.adapter.clone(),
+ fs.as_ref(),
+ &delegate,
+ toolchains.clone(),
+ cx,
+ )
+ .await?;
- let mut initialization_options = adapter
- .adapter
- .clone()
- .initialization_options(fs.as_ref(), &(delegate))
- .await?;
+ let mut initialization_options = Self::initialization_options_for_adapter(
+ adapter.adapter.clone(),
+ fs.as_ref(),
+ &delegate,
+ )
+ .await?;
match (&mut initialization_options, override_options) {
(Some(initialization_options), Some(override_options)) => {
@@ -478,9 +482,16 @@ impl LocalLspStore {
async move {
let toolchains =
this.update(&mut cx, |this, cx| this.toolchain_store(cx))?;
- let workspace_config = adapter
- .workspace_configuration(fs.as_ref(), &delegate, toolchains, &mut cx)
- .await?;
+
+ let workspace_config = Self::workspace_configuration_for_adapter(
+ adapter.clone(),
+ fs.as_ref(),
+ &delegate,
+ toolchains.clone(),
+ &mut cx,
+ )
+ .await?;
+
Ok(params
.items
.into_iter()
@@ -3225,6 +3236,67 @@ impl LocalLspStore {
self.rebuild_watched_paths(language_server_id, cx);
}
+
+ async fn initialization_options_for_adapter(
+ adapter: Arc<dyn LspAdapter>,
+ fs: &dyn Fs,
+ delegate: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ let Some(mut initialization_config) =
+ adapter.clone().initialization_options(fs, delegate).await?
+ else {
+ return Ok(None);
+ };
+
+ for other_adapter in delegate.registered_lsp_adapters() {
+ if other_adapter.name() == adapter.name() {
+ continue;
+ }
+ if let Ok(Some(target_config)) = other_adapter
+ .clone()
+ .additional_initialization_options(adapter.name(), fs, delegate)
+ .await
+ {
+ merge_json_value_into(target_config.clone(), &mut initialization_config);
+ }
+ }
+
+ Ok(Some(initialization_config))
+ }
+
+ async fn workspace_configuration_for_adapter(
+ adapter: Arc<dyn LspAdapter>,
+ fs: &dyn Fs,
+ delegate: &Arc<dyn LspAdapterDelegate>,
+ toolchains: Arc<dyn LanguageToolchainStore>,
+ cx: &mut AsyncApp,
+ ) -> Result<serde_json::Value> {
+ let mut workspace_config = adapter
+ .clone()
+ .workspace_configuration(fs, delegate, toolchains.clone(), cx)
+ .await?;
+
+ for other_adapter in delegate.registered_lsp_adapters() {
+ if other_adapter.name() == adapter.name() {
+ continue;
+ }
+ if let Ok(Some(target_config)) = other_adapter
+ .clone()
+ .additional_workspace_configuration(
+ adapter.name(),
+ fs,
+ delegate,
+ toolchains.clone(),
+ cx,
+ )
+ .await
+ {
+ merge_json_value_into(target_config.clone(), &mut workspace_config);
+ }
+ }
+
+ Ok(workspace_config)
+ }
}
#[derive(Debug)]
@@ -3764,8 +3836,8 @@ impl LspStore {
for (adapter, server, delegate) in servers {
adapter.clear_zed_json_schema_cache().await;
- let Some(json_workspace_config) = adapter
- .workspace_configuration(
+ let Some(json_workspace_config) = LocalLspStore::workspace_configuration_for_adapter(
+ adapter,
fs.as_ref(),
&delegate,
toolchain_store.clone(),
@@ -6065,10 +6137,15 @@ impl LspStore {
let toolchain_store = this.update(cx, |this, cx| this.toolchain_store(cx)).ok()?;
for (adapter, server, delegate) in servers {
- let settings = adapter
- .workspace_configuration(fs.as_ref(), &delegate, toolchain_store.clone(), cx)
- .await
- .ok()?;
+ let settings = LocalLspStore::workspace_configuration_for_adapter(
+ adapter,
+ fs.as_ref(),
+ &delegate,
+ toolchain_store.clone(),
+ cx,
+ )
+ .await
+ .ok()?;
server
.notify::<lsp::notification::DidChangeConfiguration>(
@@ -9811,6 +9888,14 @@ impl LspAdapterDelegate for LocalLspAdapterDelegate {
.update_lsp_status(server_name, status);
}
+ fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>> {
+ self.language_registry
+ .all_lsp_adapters()
+ .into_iter()
+ .map(|adapter| adapter.adapter.clone() as Arc<dyn LspAdapter>)
+ .collect()
+ }
+
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>> {
let dir = self.language_registry.language_server_download_dir(name)?;