language: Change signature of initialization_options_schema (#45937)

This makes this take the LSP adapter delegate instead of the binary
itself.

Despite us passing `LanguageServerBinaryOptions` with `allow_download:
false`, extensions would still try to download the binary because it was
never implemented for these to respect that. This would cause us to try
to download all langauge servers provided by extensions when opening a
settings file and/or requesting the JSON schema for that.

This PR fixes this by passing the LSP adapter delegate instead, so the
few language servers which actually want to have the binary for
resolving the initialization options can decide on this by themselves.
With that, we no longer download all language servers for the schema
request

Release Notes:

- N/A
This commit is contained in:
Finn Evers
2026-01-01 19:20:53 +01:00
committed by GitHub
parent 83966115a8
commit 872b2b3510
6 changed files with 77 additions and 60 deletions

1
Cargo.lock generated
View File

@@ -8653,7 +8653,6 @@ dependencies = [
"extension", "extension",
"gpui", "gpui",
"language", "language",
"lsp",
"paths", "paths",
"project", "project",
"schemars", "schemars",

View File

@@ -20,7 +20,6 @@ dap.workspace = true
extension.workspace = true extension.workspace = true
gpui.workspace = true gpui.workspace = true
language.workspace = true language.workspace = true
lsp.workspace = true
paths.workspace = true paths.workspace = true
project.workspace = true project.workspace = true
schemars.workspace = true schemars.workspace = true
@@ -31,11 +30,3 @@ snippet_provider.workspace = true
task.workspace = true task.workspace = true
theme.workspace = true theme.workspace = true
util.workspace = true util.workspace = true
# Uncomment other workspace dependencies as needed
# assistant.workspace = true
# client.workspace = true
# project.workspace = true
# settings.workspace = true

View File

@@ -3,8 +3,7 @@ use std::{str::FromStr, sync::Arc};
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use gpui::{App, AsyncApp, BorrowAppContext as _, Entity, Task, WeakEntity}; use gpui::{App, AsyncApp, BorrowAppContext as _, Entity, Task, WeakEntity};
use language::{LanguageRegistry, language_settings::all_language_settings}; use language::{LanguageRegistry, LspAdapterDelegate, language_settings::all_language_settings};
use lsp::LanguageServerBinaryOptions;
use project::{LspStore, lsp_store::LocalLspAdapterDelegate}; use project::{LspStore, lsp_store::LocalLspAdapterDelegate};
use settings::LSP_SETTINGS_SCHEMA_URL_PREFIX; use settings::LSP_SETTINGS_SCHEMA_URL_PREFIX;
use util::schemars::{AllowTrailingCommas, DefaultDenyUnknownFields}; use util::schemars::{AllowTrailingCommas, DefaultDenyUnknownFields};
@@ -123,18 +122,18 @@ pub async fn resolve_schema_request_inner(
.find(|adapter| adapter.name().as_ref() as &str == lsp_name) .find(|adapter| adapter.name().as_ref() as &str == lsp_name)
.with_context(|| format!("LSP adapter not found: {}", lsp_name))?; .with_context(|| format!("LSP adapter not found: {}", lsp_name))?;
let delegate = cx let delegate: Arc<dyn LspAdapterDelegate> = cx
.update(|inner_cx| { .update(|inner_cx| {
lsp_store.update(inner_cx, |lsp_store, inner_cx| { lsp_store.update(inner_cx, |lsp_store, cx| {
let Some(local) = lsp_store.as_local() else { let Some(local) = lsp_store.as_local() else {
return None; return None;
}; };
let Some(worktree) = local.worktree_store.read(inner_cx).worktrees().next() let Some(worktree) = local.worktree_store.read(cx).worktrees().next()
else { else {
return None; return None;
}; };
Some(LocalLspAdapterDelegate::from_local_lsp( Some(LocalLspAdapterDelegate::from_local_lsp(
local, &worktree, inner_cx, local, &worktree, cx,
)) ))
}) })
})? })?
@@ -143,36 +142,8 @@ pub async fn resolve_schema_request_inner(
"either LSP store is not in local mode or no worktree is available" "either LSP store is not in local mode or no worktree is available"
))?; ))?;
let adapter_for_schema = adapter.clone(); adapter
.initialization_options_schema(&delegate, cx)
let binary = adapter
.get_language_server_command(
delegate,
None,
LanguageServerBinaryOptions {
allow_path_lookup: true,
allow_binary_download: false,
pre_release: false,
},
cx,
)
.await
.await
.0
.with_context(|| {
format!(
concat!(
"Failed to find language server {} ",
"to generate initialization params schema"
),
lsp_name
)
})?;
adapter_for_schema
.adapter
.clone()
.initialization_options_schema(&binary)
.await .await
.unwrap_or_else(|| { .unwrap_or_else(|| {
serde_json::json!({ serde_json::json!({

View File

@@ -331,6 +331,21 @@ impl CachedLspAdapter {
.unwrap_or_else(|| language_name.lsp_id()) .unwrap_or_else(|| language_name.lsp_id())
} }
pub async fn initialization_options_schema(
&self,
delegate: &Arc<dyn LspAdapterDelegate>,
cx: &mut AsyncApp,
) -> Option<serde_json::Value> {
self.adapter
.clone()
.initialization_options_schema(
delegate,
self.cached_binary.clone().lock_owned().await,
cx,
)
.await
}
pub fn process_prompt_response(&self, context: &PromptResponseContext, cx: &mut AsyncApp) { pub fn process_prompt_response(&self, context: &PromptResponseContext, cx: &mut AsyncApp) {
self.adapter.process_prompt_response(context, cx) self.adapter.process_prompt_response(context, cx)
} }
@@ -464,7 +479,9 @@ pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller {
/// Returns the JSON schema of the initialization_options for the language server. /// Returns the JSON schema of the initialization_options for the language server.
async fn initialization_options_schema( async fn initialization_options_schema(
self: Arc<Self>, self: Arc<Self>,
_language_server_binary: &LanguageServerBinary, _delegate: &Arc<dyn LspAdapterDelegate>,
_cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
_cx: &mut AsyncApp,
) -> Option<serde_json::Value> { ) -> Option<serde_json::Value> {
None None
} }
@@ -591,6 +608,7 @@ pub trait DynLspInstaller {
pre_release: bool, pre_release: bool,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<LanguageServerBinary>; ) -> Result<LanguageServerBinary>;
fn get_language_server_command( fn get_language_server_command(
self: Arc<Self>, self: Arc<Self>,
delegate: Arc<dyn LspAdapterDelegate>, delegate: Arc<dyn LspAdapterDelegate>,
@@ -686,6 +704,12 @@ where
return (Ok(binary), None); return (Ok(binary), None);
} }
if let Some((pre_release, cached_binary)) = cached_binary_deref
&& *pre_release == binary_options.pre_release
{
return (Ok(cached_binary.clone()), None);
}
if !binary_options.allow_binary_download { if !binary_options.allow_binary_download {
return ( return (
Err(anyhow::anyhow!("downloading language servers disabled")), Err(anyhow::anyhow!("downloading language servers disabled")),
@@ -693,12 +717,6 @@ where
); );
} }
if let Some((pre_release, cached_binary)) = cached_binary_deref
&& *pre_release == binary_options.pre_release
{
return (Ok(cached_binary.clone()), None);
}
let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await
else { else {
return ( return (

View File

@@ -2,16 +2,17 @@ use anyhow::{Context as _, ensure};
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use async_trait::async_trait; use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use futures::lock::OwnedMutexGuard;
use futures::{AsyncBufReadExt, StreamExt as _}; use futures::{AsyncBufReadExt, StreamExt as _};
use gpui::{App, AsyncApp, SharedString, Task}; use gpui::{App, AsyncApp, SharedString, Task};
use http_client::github::{AssetKind, GitHubLspBinaryVersion, latest_github_release}; use http_client::github::{AssetKind, GitHubLspBinaryVersion, latest_github_release};
use language::language_settings::language_settings; use language::language_settings::language_settings;
use language::{ContextLocation, LanguageToolchainStore, LspInstaller}; use language::{ContextLocation, DynLspInstaller, LanguageToolchainStore, LspInstaller};
use language::{ContextProvider, LspAdapter, LspAdapterDelegate}; use language::{ContextProvider, LspAdapter, LspAdapterDelegate};
use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery}; use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery};
use language::{Toolchain, ToolchainList, ToolchainLister, ToolchainMetadata}; use language::{Toolchain, ToolchainList, ToolchainLister, ToolchainMetadata};
use lsp::LanguageServerName;
use lsp::{LanguageServerBinary, Uri}; use lsp::{LanguageServerBinary, Uri};
use lsp::{LanguageServerBinaryOptions, LanguageServerName};
use node_runtime::{NodeRuntime, VersionStrategy}; use node_runtime::{NodeRuntime, VersionStrategy};
use pet_core::Configuration; use pet_core::Configuration;
use pet_core::os_environment::Environment; use pet_core::os_environment::Environment;
@@ -2342,9 +2343,27 @@ impl LspAdapter for RuffLspAdapter {
async fn initialization_options_schema( async fn initialization_options_schema(
self: Arc<Self>, self: Arc<Self>,
language_server_binary: &LanguageServerBinary, delegate: &Arc<dyn LspAdapterDelegate>,
cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
cx: &mut AsyncApp,
) -> Option<serde_json::Value> { ) -> Option<serde_json::Value> {
let mut command = util::command::new_smol_command(&language_server_binary.path); let binary = self
.get_language_server_command(
delegate.clone(),
None,
LanguageServerBinaryOptions {
allow_path_lookup: true,
allow_binary_download: false,
pre_release: false,
},
cached_binary,
cx.clone(),
)
.await
.0
.ok()?;
let mut command = util::command::new_smol_command(&binary.path);
command command
.args(&["config", "--output-format", "json"]) .args(&["config", "--output-format", "json"])
.stdout(Stdio::piped()) .stdout(Stdio::piped())

View File

@@ -2,12 +2,13 @@ use anyhow::{Context as _, Result};
use async_trait::async_trait; use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use futures::StreamExt; use futures::StreamExt;
use futures::lock::OwnedMutexGuard;
use gpui::{App, AppContext, AsyncApp, SharedString, Task}; use gpui::{App, AppContext, AsyncApp, SharedString, Task};
use http_client::github::AssetKind; use http_client::github::AssetKind;
use http_client::github::{GitHubLspBinaryVersion, latest_github_release}; use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
use http_client::github_download::{GithubBinaryMetadata, download_server_binary}; use http_client::github_download::{GithubBinaryMetadata, download_server_binary};
pub use language::*; pub use language::*;
use lsp::{InitializeParams, LanguageServerBinary}; use lsp::{InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions};
use project::lsp_store::rust_analyzer_ext::CARGO_DIAGNOSTICS_SOURCE_NAME; use project::lsp_store::rust_analyzer_ext::CARGO_DIAGNOSTICS_SOURCE_NAME;
use project::project_settings::ProjectSettings; use project::project_settings::ProjectSettings;
use regex::Regex; use regex::Regex;
@@ -513,9 +514,27 @@ impl LspAdapter for RustLspAdapter {
async fn initialization_options_schema( async fn initialization_options_schema(
self: Arc<Self>, self: Arc<Self>,
language_server_binary: &LanguageServerBinary, delegate: &Arc<dyn LspAdapterDelegate>,
cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
cx: &mut AsyncApp,
) -> Option<serde_json::Value> { ) -> Option<serde_json::Value> {
let mut command = util::command::new_smol_command(&language_server_binary.path); let binary = self
.get_language_server_command(
delegate.clone(),
None,
LanguageServerBinaryOptions {
allow_path_lookup: true,
allow_binary_download: false,
pre_release: false,
},
cached_binary,
cx.clone(),
)
.await
.0
.ok()?;
let mut command = util::command::new_smol_command(&binary.path);
command command
.arg("--print-config-schema") .arg("--print-config-schema")
.stdout(Stdio::piped()) .stdout(Stdio::piped())