From 91b0ca089508671569b6537b79c4654be408a133 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 23 Jan 2025 13:17:32 +0200 Subject: [PATCH] Omit `tsdk_path` from the servers' options if it does not exist (#23525) Part of https://github.com/zed-industries/zed/issues/22606 Before, `tsdk_path` for vtsls and typescript-language-server unconditionally set a `tsdk`/`tsserver` property for the corresponding language server, even if there were no such directory at all. Instead, make the corresponding code to omit such property if it was not found on the FS. Release Notes: - Fixed "The path /.../tsserver.js doesn't point to a valid tsserver install. Falling back to bundled TypeScript version." pop-up appearing --- Cargo.lock | 1 + crates/language/src/language.rs | 4 ++ crates/language_extension/Cargo.toml | 1 + .../src/extension_lsp_adapter.rs | 3 ++ crates/languages/src/css.rs | 2 + crates/languages/src/go.rs | 2 + crates/languages/src/json.rs | 4 +- crates/languages/src/python.rs | 3 ++ crates/languages/src/tailwind.rs | 4 +- crates/languages/src/typescript.rs | 20 +++++++-- crates/languages/src/vtsls.rs | 18 ++++++-- crates/languages/src/yaml.rs | 3 +- crates/project/src/lsp_store.rs | 44 +++++++++++++++---- crates/project/src/project.rs | 2 + 14 files changed, 92 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 489bf6989a..e56e26deb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6788,6 +6788,7 @@ dependencies = [ "async-trait", "collections", "extension", + "fs", "futures 0.3.31", "gpui", "language", diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 2769e0fe0b..d926d26b17 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -25,6 +25,7 @@ use crate::language_settings::SoftWrap; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use collections::{HashMap, HashSet}; +use fs::Fs; use futures::Future; use gpui::{AppContext, AsyncAppContext, Model, SharedString, Task}; pub use highlight_map::HighlightMap; @@ -498,6 +499,7 @@ pub trait LspAdapter: 'static + Send + Sync { /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp::InitializeParams`] async fn initialization_options( self: Arc, + _: &dyn Fs, _: &Arc, ) -> Result> { Ok(None) @@ -505,6 +507,7 @@ pub trait LspAdapter: 'static + Send + Sync { async fn workspace_configuration( self: Arc, + _: &dyn Fs, _: &Arc, _: Arc, _cx: &mut AsyncAppContext, @@ -1901,6 +1904,7 @@ impl LspAdapter for FakeLspAdapter { async fn initialization_options( self: Arc, + _: &dyn Fs, _: &Arc, ) -> Result> { Ok(self.initialization_options.clone()) diff --git a/crates/language_extension/Cargo.toml b/crates/language_extension/Cargo.toml index ef561b8841..6f9439fb5a 100644 --- a/crates/language_extension/Cargo.toml +++ b/crates/language_extension/Cargo.toml @@ -17,6 +17,7 @@ async-trait.workspace = true collections.workspace = true extension.workspace = true futures.workspace = true +fs.workspace = true gpui.workspace = true language.workspace = true lsp.workspace = true diff --git a/crates/language_extension/src/extension_lsp_adapter.rs b/crates/language_extension/src/extension_lsp_adapter.rs index 3286e09e2d..ec9ea1e0e5 100644 --- a/crates/language_extension/src/extension_lsp_adapter.rs +++ b/crates/language_extension/src/extension_lsp_adapter.rs @@ -8,6 +8,7 @@ use anyhow::{Context, Result}; use async_trait::async_trait; use collections::HashMap; use extension::{Extension, ExtensionLanguageServerProxy, WorktreeDelegate}; +use fs::Fs; use futures::{Future, FutureExt}; use gpui::AsyncAppContext; use language::{ @@ -224,6 +225,7 @@ impl LspAdapter for ExtensionLspAdapter { async fn initialization_options( self: Arc, + _: &dyn Fs, delegate: &Arc, ) -> Result> { let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; @@ -246,6 +248,7 @@ impl LspAdapter for ExtensionLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, delegate: &Arc, _: Arc, _cx: &mut AsyncAppContext, diff --git a/crates/languages/src/css.rs b/crates/languages/src/css.rs index 148f6acced..50e34cea40 100644 --- a/crates/languages/src/css.rs +++ b/crates/languages/src/css.rs @@ -4,6 +4,7 @@ use futures::StreamExt; use language::{LspAdapter, LspAdapterDelegate}; use lsp::{LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; +use project::Fs; use serde_json::json; use smol::fs; use std::{ @@ -107,6 +108,7 @@ impl LspAdapter for CssLspAdapter { async fn initialization_options( self: Arc, + _: &dyn Fs, _: &Arc, ) -> Result> { Ok(Some(json!({ diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index d56c720eb0..ca17f13e26 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -6,6 +6,7 @@ use gpui::{AppContext, AsyncAppContext, Task}; use http_client::github::latest_github_release; pub use language::*; use lsp::{LanguageServerBinary, LanguageServerName}; +use project::Fs; use regex::Regex; use serde_json::json; use smol::fs; @@ -197,6 +198,7 @@ impl super::LspAdapter for GoLspAdapter { async fn initialization_options( self: Arc, + _: &dyn Fs, _: &Arc, ) -> Result> { Ok(Some(json!({ diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index a783195db3..95aefd405a 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -9,7 +9,7 @@ use http_client::github::{latest_github_release, GitHubLspBinaryVersion}; use language::{LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterDelegate}; use lsp::{LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; -use project::{lsp_store::language_server_settings, ContextProviderWithTasks}; +use project::{lsp_store::language_server_settings, ContextProviderWithTasks, Fs}; use serde_json::{json, Value}; use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore}; use smol::{ @@ -208,6 +208,7 @@ impl LspAdapter for JsonLspAdapter { async fn initialization_options( self: Arc, + _: &dyn Fs, _: &Arc, ) -> Result> { Ok(Some(json!({ @@ -217,6 +218,7 @@ impl LspAdapter for JsonLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, delegate: &Arc, _: Arc, cx: &mut AsyncAppContext, diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index be9cef8651..a3cf24cf57 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -18,6 +18,7 @@ use pet_core::os_environment::Environment; use pet_core::python_environment::PythonEnvironmentKind; use pet_core::Configuration; use project::lsp_store::language_server_settings; +use project::Fs; use serde_json::{json, Value}; use smol::lock::OnceCell; use std::cmp::Ordering; @@ -250,6 +251,7 @@ impl LspAdapter for PythonLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, adapter: &Arc, toolchains: Arc, cx: &mut AsyncAppContext, @@ -931,6 +933,7 @@ impl LspAdapter for PyLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, adapter: &Arc, toolchains: Arc, cx: &mut AsyncAppContext, diff --git a/crates/languages/src/tailwind.rs b/crates/languages/src/tailwind.rs index 02c3dbefc2..328cf51279 100644 --- a/crates/languages/src/tailwind.rs +++ b/crates/languages/src/tailwind.rs @@ -6,7 +6,7 @@ use gpui::AsyncAppContext; use language::{LanguageToolchainStore, LspAdapter, LspAdapterDelegate}; use lsp::{LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; -use project::lsp_store::language_server_settings; +use project::{lsp_store::language_server_settings, Fs}; use serde_json::{json, Value}; use smol::fs; use std::{ @@ -116,6 +116,7 @@ impl LspAdapter for TailwindLspAdapter { async fn initialization_options( self: Arc, + _: &dyn Fs, _: &Arc, ) -> Result> { Ok(Some(json!({ @@ -131,6 +132,7 @@ impl LspAdapter for TailwindLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, delegate: &Arc, _: Arc, cx: &mut AsyncAppContext, diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index edfdbf5f55..b14f3dc237 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -8,8 +8,8 @@ use http_client::github::{build_asset_url, AssetKind, GitHubLspBinaryVersion}; use language::{LanguageToolchainStore, LspAdapter, LspAdapterDelegate}; use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; -use project::lsp_store::language_server_settings; use project::ContextProviderWithTasks; +use project::{lsp_store::language_server_settings, Fs}; use serde_json::{json, Value}; use smol::{fs, io::BufReader, stream::StreamExt}; use std::{ @@ -77,16 +77,25 @@ impl TypeScriptLspAdapter { pub fn new(node: NodeRuntime) -> Self { TypeScriptLspAdapter { node } } - async fn tsdk_path(adapter: &Arc) -> &'static str { + async fn tsdk_path(fs: &dyn Fs, adapter: &Arc) -> Option<&'static str> { let is_yarn = adapter .read_text_file(PathBuf::from(".yarn/sdks/typescript/lib/typescript.js")) .await .is_ok(); - if is_yarn { + let tsdk_path = if is_yarn { ".yarn/sdks/typescript/lib" } else { "node_modules/typescript/lib" + }; + + if fs + .is_dir(&adapter.worktree_root_path().join(tsdk_path)) + .await + { + Some(tsdk_path) + } else { + None } } } @@ -233,9 +242,10 @@ impl LspAdapter for TypeScriptLspAdapter { async fn initialization_options( self: Arc, + fs: &dyn Fs, adapter: &Arc, ) -> Result> { - let tsdk_path = Self::tsdk_path(adapter).await; + let tsdk_path = Self::tsdk_path(fs, adapter).await; Ok(Some(json!({ "provideFormatter": true, "hostInfo": "zed", @@ -257,6 +267,7 @@ impl LspAdapter for TypeScriptLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, delegate: &Arc, _: Arc, cx: &mut AsyncAppContext, @@ -353,6 +364,7 @@ impl LspAdapter for EsLintLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, delegate: &Arc, _: Arc, cx: &mut AsyncAppContext, diff --git a/crates/languages/src/vtsls.rs b/crates/languages/src/vtsls.rs index 22718e410c..33dea77540 100644 --- a/crates/languages/src/vtsls.rs +++ b/crates/languages/src/vtsls.rs @@ -5,7 +5,7 @@ use gpui::AsyncAppContext; use language::{LanguageToolchainStore, LspAdapter, LspAdapterDelegate}; use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; -use project::lsp_store::language_server_settings; +use project::{lsp_store::language_server_settings, Fs}; use serde_json::Value; use std::{ any::Any, @@ -34,16 +34,25 @@ impl VtslsLspAdapter { VtslsLspAdapter { node } } - async fn tsdk_path(adapter: &Arc) -> &'static str { + async fn tsdk_path(fs: &dyn Fs, adapter: &Arc) -> Option<&'static str> { let is_yarn = adapter .read_text_file(PathBuf::from(".yarn/sdks/typescript/lib/typescript.js")) .await .is_ok(); - if is_yarn { + let tsdk_path = if is_yarn { ".yarn/sdks/typescript/lib" } else { Self::TYPESCRIPT_TSDK_PATH + }; + + if fs + .is_dir(&adapter.worktree_root_path().join(tsdk_path)) + .await + { + Some(tsdk_path) + } else { + None } } } @@ -196,11 +205,12 @@ impl LspAdapter for VtslsLspAdapter { async fn workspace_configuration( self: Arc, + fs: &dyn Fs, delegate: &Arc, _: Arc, cx: &mut AsyncAppContext, ) -> Result { - let tsdk_path = Self::tsdk_path(delegate).await; + let tsdk_path = Self::tsdk_path(fs, delegate).await; let config = serde_json::json!({ "tsdk": tsdk_path, "suggest": { diff --git a/crates/languages/src/yaml.rs b/crates/languages/src/yaml.rs index 12878b8f93..91f6e48aae 100644 --- a/crates/languages/src/yaml.rs +++ b/crates/languages/src/yaml.rs @@ -7,7 +7,7 @@ use language::{ }; use lsp::{LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; -use project::lsp_store::language_server_settings; +use project::{lsp_store::language_server_settings, Fs}; use serde_json::Value; use settings::{Settings, SettingsLocation}; use smol::fs; @@ -128,6 +128,7 @@ impl LspAdapter for YamlLspAdapter { async fn workspace_configuration( self: Arc, + _: &dyn Fs, delegate: &Arc, _: Arc, cx: &mut AsyncAppContext, diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 9e3960c925..d887853ef4 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -239,6 +239,7 @@ impl LocalLspStore { let adapter = adapter.clone(); let this = self.weak.clone(); let pending_workspace_folders = pending_workspace_folders.clone(); + let fs = self.fs.clone(); cx.spawn(move |mut cx| async move { let result = { let delegate = delegate.clone(); @@ -254,13 +255,18 @@ impl LocalLspStore { let workspace_config = adapter .adapter .clone() - .workspace_configuration(&delegate, toolchains.clone(), &mut cx) + .workspace_configuration( + fs.as_ref(), + &delegate, + toolchains.clone(), + &mut cx, + ) .await?; let mut initialization_options = adapter .adapter .clone() - .initialization_options(&(delegate)) + .initialization_options(fs.as_ref(), &(delegate)) .await?; match (&mut initialization_options, override_options) { @@ -277,7 +283,13 @@ impl LocalLspStore { adapter.adapter.prepare_initialize_params(params) })??; - Self::setup_lsp_messages(this.clone(), &language_server, delegate, adapter); + Self::setup_lsp_messages( + this.clone(), + fs, + &language_server, + delegate, + adapter, + ); let did_change_configuration_params = Arc::new(lsp::DidChangeConfigurationParams { @@ -425,6 +437,7 @@ impl LocalLspStore { fn setup_lsp_messages( this: WeakModel, + fs: Arc, language_server: &LanguageServer, delegate: Arc, adapter: Arc, @@ -458,15 +471,17 @@ impl LocalLspStore { let adapter = adapter.adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); + let fs = fs.clone(); move |params, mut cx| { let adapter = adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); + let fs = fs.clone(); async move { let toolchains = this.update(&mut cx, |this, cx| this.toolchain_store(cx))?; let workspace_config = adapter - .workspace_configuration(&delegate, toolchains, &mut cx) + .workspace_configuration(fs.as_ref(), &delegate, toolchains, &mut cx) .await?; Ok(params .items @@ -3029,7 +3044,10 @@ impl LspStore { let _maintain_workspace_config = { let (sender, receiver) = watch::channel(); - (Self::maintain_workspace_config(receiver, cx), sender) + ( + Self::maintain_workspace_config(fs.clone(), receiver, cx), + sender, + ) }; let project_tree = ProjectTree::new(worktree_store.clone(), cx); Self { @@ -3093,6 +3111,7 @@ impl LspStore { }) } + #[allow(clippy::too_many_arguments)] pub(super) fn new_remote( buffer_store: Model, worktree_store: Model, @@ -3100,6 +3119,7 @@ impl LspStore { languages: Arc, upstream_client: AnyProtoClient, project_id: u64, + fs: Arc, cx: &mut ModelContext, ) -> Self { cx.subscribe(&buffer_store, Self::on_buffer_store_event) @@ -3108,7 +3128,7 @@ impl LspStore { .detach(); let _maintain_workspace_config = { let (sender, receiver) = watch::channel(); - (Self::maintain_workspace_config(receiver, cx), sender) + (Self::maintain_workspace_config(fs, receiver, cx), sender) }; Self { mode: LspStoreMode::Remote(RemoteLspStore { @@ -5138,6 +5158,7 @@ impl LspStore { pub(crate) async fn refresh_workspace_configurations( this: &WeakModel, + fs: Arc, mut cx: AsyncAppContext, ) { maybe!(async move { @@ -5190,7 +5211,12 @@ impl LspStore { .ok()?; for (adapter, server, delegate) in servers { let settings = adapter - .workspace_configuration(&delegate, toolchain_store.clone(), &mut cx) + .workspace_configuration( + fs.as_ref(), + &delegate, + toolchain_store.clone(), + &mut cx, + ) .await .ok()?; @@ -5213,6 +5239,7 @@ impl LspStore { } } fn maintain_workspace_config( + fs: Arc, external_refresh_requests: watch::Receiver<()>, cx: &mut ModelContext, ) -> Task> { @@ -5227,7 +5254,7 @@ impl LspStore { futures::stream::select(settings_changed_rx, external_refresh_requests); cx.spawn(move |this, cx| async move { while let Some(()) = joint_future.next().await { - Self::refresh_workspace_configurations(&this, cx.clone()).await; + Self::refresh_workspace_configurations(&this, fs.clone(), cx.clone()).await; } drop(settings_observation); @@ -8454,6 +8481,7 @@ impl LspAdapter for SshLspAdapter { async fn initialization_options( self: Arc, + _: &dyn Fs, _: &Arc, ) -> Result> { let Some(options) = &self.initialization_options else { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index d4f78757d8..fe40280806 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -810,6 +810,7 @@ impl Project { languages.clone(), ssh_proto.clone(), SSH_PROJECT_ID, + fs.clone(), cx, ) }); @@ -983,6 +984,7 @@ impl Project { languages.clone(), client.clone().into(), remote_id, + fs.clone(), cx, ); lsp_store.set_language_server_statuses_from_proto(response.payload.language_servers);