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 d8f8939b5dbc1ff7cce3b84c68ddb4d98392b465..6c12bd886d7c880f68bca078815dd17ae4ca392f 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 2225b7c5aa68b43d45dacf404c038248ab2c897f..f973fe2ebfbdbac1020f75297c4a469d42505f48 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 da576065ddd25163796b882615d5fa4f8d80f17d..ca3fb20f0115b7db5f9cc984a0ca1719b59f4b05 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 8493d91ec4fa0b6a5fe810b4064694f2d8feb8d7..68817790973c259ccc6f4252820f02201fc69273 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 40825e30cbe79c8e2c48772a40804dda60948894..23a067a6230dbacfe452d821104b7d8a2bdfa12a 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 d2b890a1dbe3d4137d9819a55a40ee5e19374add..f91679db850bb79001258213df629f51ab7c9d7c 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())