From 872b2b35101e21053c90c53befc16e5197b9a511 Mon Sep 17 00:00:00 2001 From: Finn Evers Date: Thu, 1 Jan 2026 19:20:53 +0100 Subject: [PATCH] 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 --- Cargo.lock | 1 - crates/json_schema_store/Cargo.toml | 9 ---- .../src/json_schema_store.rs | 43 +++---------------- crates/language/src/language.rs | 32 +++++++++++--- crates/languages/src/python.rs | 27 ++++++++++-- crates/languages/src/rust.rs | 25 +++++++++-- 6 files changed, 77 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8f8939b5d..6c12bd886d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8653,7 +8653,6 @@ dependencies = [ "extension", "gpui", "language", - "lsp", "paths", "project", "schemars", diff --git a/crates/json_schema_store/Cargo.toml b/crates/json_schema_store/Cargo.toml index 2225b7c5aa..f973fe2ebf 100644 --- a/crates/json_schema_store/Cargo.toml +++ b/crates/json_schema_store/Cargo.toml @@ -20,7 +20,6 @@ dap.workspace = true extension.workspace = true gpui.workspace = true language.workspace = true -lsp.workspace = true paths.workspace = true project.workspace = true schemars.workspace = true @@ -31,11 +30,3 @@ snippet_provider.workspace = true task.workspace = true theme.workspace = true util.workspace = true - - - -# Uncomment other workspace dependencies as needed -# assistant.workspace = true -# client.workspace = true -# project.workspace = true -# settings.workspace = true diff --git a/crates/json_schema_store/src/json_schema_store.rs b/crates/json_schema_store/src/json_schema_store.rs index da576065dd..ca3fb20f01 100644 --- a/crates/json_schema_store/src/json_schema_store.rs +++ b/crates/json_schema_store/src/json_schema_store.rs @@ -3,8 +3,7 @@ use std::{str::FromStr, sync::Arc}; use anyhow::{Context as _, Result}; use gpui::{App, AsyncApp, BorrowAppContext as _, Entity, Task, WeakEntity}; -use language::{LanguageRegistry, language_settings::all_language_settings}; -use lsp::LanguageServerBinaryOptions; +use language::{LanguageRegistry, LspAdapterDelegate, language_settings::all_language_settings}; use project::{LspStore, lsp_store::LocalLspAdapterDelegate}; use settings::LSP_SETTINGS_SCHEMA_URL_PREFIX; 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) .with_context(|| format!("LSP adapter not found: {}", lsp_name))?; - let delegate = cx + let delegate: Arc = 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 { return None; }; - let Some(worktree) = local.worktree_store.read(inner_cx).worktrees().next() + let Some(worktree) = local.worktree_store.read(cx).worktrees().next() else { return None; }; 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" ))?; - let adapter_for_schema = adapter.clone(); - - 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) + adapter + .initialization_options_schema(&delegate, cx) .await .unwrap_or_else(|| { serde_json::json!({ diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 8493d91ec4..6881779097 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -331,6 +331,21 @@ impl CachedLspAdapter { .unwrap_or_else(|| language_name.lsp_id()) } + pub async fn initialization_options_schema( + &self, + delegate: &Arc, + cx: &mut AsyncApp, + ) -> Option { + 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) { 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. async fn initialization_options_schema( self: Arc, - _language_server_binary: &LanguageServerBinary, + _delegate: &Arc, + _cached_binary: OwnedMutexGuard>, + _cx: &mut AsyncApp, ) -> Option { None } @@ -591,6 +608,7 @@ pub trait DynLspInstaller { pre_release: bool, cx: &mut AsyncApp, ) -> Result; + fn get_language_server_command( self: Arc, delegate: Arc, @@ -686,6 +704,12 @@ where 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 { return ( 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 else { return ( diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 40825e30cb..23a067a623 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -2,16 +2,17 @@ use anyhow::{Context as _, ensure}; use anyhow::{Result, anyhow}; use async_trait::async_trait; use collections::HashMap; +use futures::lock::OwnedMutexGuard; use futures::{AsyncBufReadExt, StreamExt as _}; use gpui::{App, AsyncApp, SharedString, Task}; use http_client::github::{AssetKind, GitHubLspBinaryVersion, latest_github_release}; use language::language_settings::language_settings; -use language::{ContextLocation, LanguageToolchainStore, LspInstaller}; +use language::{ContextLocation, DynLspInstaller, LanguageToolchainStore, LspInstaller}; use language::{ContextProvider, LspAdapter, LspAdapterDelegate}; use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery}; use language::{Toolchain, ToolchainList, ToolchainLister, ToolchainMetadata}; -use lsp::LanguageServerName; use lsp::{LanguageServerBinary, Uri}; +use lsp::{LanguageServerBinaryOptions, LanguageServerName}; use node_runtime::{NodeRuntime, VersionStrategy}; use pet_core::Configuration; use pet_core::os_environment::Environment; @@ -2342,9 +2343,27 @@ impl LspAdapter for RuffLspAdapter { async fn initialization_options_schema( self: Arc, - language_server_binary: &LanguageServerBinary, + delegate: &Arc, + cached_binary: OwnedMutexGuard>, + cx: &mut AsyncApp, ) -> Option { - 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 .args(&["config", "--output-format", "json"]) .stdout(Stdio::piped()) diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index d2b890a1db..f91679db85 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -2,12 +2,13 @@ use anyhow::{Context as _, Result}; use async_trait::async_trait; use collections::HashMap; use futures::StreamExt; +use futures::lock::OwnedMutexGuard; use gpui::{App, AppContext, AsyncApp, SharedString, Task}; use http_client::github::AssetKind; use http_client::github::{GitHubLspBinaryVersion, latest_github_release}; use http_client::github_download::{GithubBinaryMetadata, download_server_binary}; 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::project_settings::ProjectSettings; use regex::Regex; @@ -513,9 +514,27 @@ impl LspAdapter for RustLspAdapter { async fn initialization_options_schema( self: Arc, - language_server_binary: &LanguageServerBinary, + delegate: &Arc, + cached_binary: OwnedMutexGuard>, + cx: &mut AsyncApp, ) -> Option { - 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 .arg("--print-config-schema") .stdout(Stdio::piped())