diff --git a/crates/extension/src/extension_lsp_adapter.rs b/crates/extension/src/extension_lsp_adapter.rs index 4f6206683d..9ca48739a8 100644 --- a/crates/extension/src/extension_lsp_adapter.rs +++ b/crates/extension/src/extension_lsp_adapter.rs @@ -92,4 +92,37 @@ impl LspAdapter for ExtensionLspAdapter { async fn installation_test_binary(&self, _: PathBuf) -> Option { None } + + async fn initialization_options( + self: Arc, + delegate: &Arc, + ) -> Result> { + let delegate = delegate.clone(); + let json_options = self + .extension + .call({ + let this = self.clone(); + |extension, store| { + async move { + let resource = store.data_mut().table().push(delegate)?; + let options = extension + .call_language_server_initialization_options( + store, + &this.config, + resource, + ) + .await? + .map_err(|e| anyhow!("{}", e))?; + anyhow::Ok(options) + } + .boxed() + } + }) + .await?; + Ok(if let Some(json_options) = json_options { + serde_json::from_str(&json_options)? + } else { + None + }) + } } diff --git a/crates/extension/src/wasm_host.rs b/crates/extension/src/wasm_host.rs index 3d8e0b53e0..9d6f2683cb 100644 --- a/crates/extension/src/wasm_host.rs +++ b/crates/extension/src/wasm_host.rs @@ -307,6 +307,46 @@ impl wit::ExtensionImports for WasmState { .map_err(|err| err.to_string())) } + async fn npm_package_installed_version( + &mut self, + package_name: String, + ) -> wasmtime::Result, String>> { + async fn inner( + this: &mut WasmState, + package_name: String, + ) -> anyhow::Result> { + this.host + .node_runtime + .npm_package_installed_version(&this.host.work_dir, &package_name) + .await + } + + Ok(inner(self, package_name) + .await + .map_err(|err| err.to_string())) + } + + async fn npm_install_package( + &mut self, + package_name: String, + version: String, + ) -> wasmtime::Result> { + async fn inner( + this: &mut WasmState, + package_name: String, + version: String, + ) -> anyhow::Result<()> { + this.host + .node_runtime + .npm_install_packages(&this.host.work_dir, &[(&package_name, &version)]) + .await + } + + Ok(inner(self, package_name, version) + .await + .map_err(|err| err.to_string())) + } + async fn latest_github_release( &mut self, repo: String, diff --git a/crates/extension_api/src/extension_api.rs b/crates/extension_api/src/extension_api.rs index b501d1daa0..14450eb08c 100644 --- a/crates/extension_api/src/extension_api.rs +++ b/crates/extension_api/src/extension_api.rs @@ -13,6 +13,14 @@ pub trait Extension: Send + Sync { config: wit::LanguageServerConfig, worktree: &wit::Worktree, ) -> Result; + + fn language_server_initialization_options( + &mut self, + _config: wit::LanguageServerConfig, + _worktree: &wit::Worktree, + ) -> Result> { + Ok(None) + } } #[macro_export] @@ -60,4 +68,11 @@ impl wit::Guest for Component { ) -> Result { extension().language_server_command(config, worktree) } + + fn language_server_initialization_options( + config: LanguageServerConfig, + worktree: &Worktree, + ) -> Result, String> { + extension().language_server_initialization_options(config, worktree) + } } diff --git a/crates/extension_api/wit/extension.wit b/crates/extension_api/wit/extension.wit index 2afa599bc2..d4e97a86a5 100644 --- a/crates/extension_api/wit/extension.wit +++ b/crates/extension_api/wit/extension.wit @@ -51,6 +51,12 @@ world extension { /// Gets the latest version of the given NPM package. import npm-package-latest-version: func(package-name: string) -> result; + /// Returns the installed version of the given NPM package, if it exists. + import npm-package-installed-version: func(package-name: string) -> result, string>; + + /// Installs the specified NPM package. + import npm-install-package: func(package-name: string, version: string) -> result<_, string>; + /// Gets the latest release for the given GitHub repository. import latest-github-release: func(repo: string, options: github-release-options) -> result; @@ -81,4 +87,5 @@ world extension { } export language-server-command: func(config: language-server-config, worktree: borrow) -> result; + export language-server-initialization-options: func(config: language-server-config, worktree: borrow) -> result, string>; } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index c2d0b485c1..8f2f4bce6e 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -259,10 +259,6 @@ impl CachedLspAdapter { self.adapter.label_for_symbol(name, kind, language).await } - pub fn prettier_plugins(&self) -> &[&'static str] { - self.adapter.prettier_plugins() - } - #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> Option<&FakeLspAdapter> { self.adapter.as_fake() @@ -441,8 +437,11 @@ 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`] - fn initialization_options(&self) -> Option { - None + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(None) } fn workspace_configuration(&self, _workspace_root: &Path, _cx: &mut AppContext) -> Value { @@ -472,10 +471,6 @@ pub trait LspAdapter: 'static + Send + Sync { Default::default() } - fn prettier_plugins(&self) -> &[&'static str] { - &[] - } - #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> Option<&FakeLspAdapter> { None @@ -575,6 +570,9 @@ pub struct LanguageConfig { /// The name of a Prettier parser that should be used for this language. #[serde(default)] pub prettier_parser_name: Option, + /// The names of any Prettier plugins that should be used for this language. + #[serde(default)] + pub prettier_plugins: Vec>, } #[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)] @@ -656,6 +654,7 @@ impl Default for LanguageConfig { overrides: Default::default(), word_characters: Default::default(), prettier_parser_name: None, + prettier_plugins: Default::default(), collapsed_placeholder: Default::default(), } } @@ -1283,6 +1282,10 @@ impl Language { pub fn prettier_parser_name(&self) -> Option<&str> { self.config.prettier_parser_name.as_deref() } + + pub fn prettier_plugins(&self) -> &Vec> { + &self.config.prettier_plugins + } } impl LanguageScope { @@ -1547,12 +1550,11 @@ impl LspAdapter for FakeLspAdapter { self.disk_based_diagnostics_progress_token.clone() } - fn initialization_options(&self) -> Option { - self.initialization_options.clone() - } - - fn prettier_plugins(&self) -> &[&'static str] { - &self.prettier_plugins + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(self.initialization_options.clone()) } fn as_fake(&self) -> Option<&FakeLspAdapter> { diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 4edfb43420..b5a85f34e5 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -63,7 +63,7 @@ pub enum LanguageServerBinaryStatus { pub struct PendingLanguageServer { pub server_id: LanguageServerId, - pub task: Task>, + pub task: Task)>>, pub container_dir: Option>, } @@ -629,6 +629,15 @@ impl LanguageRegistry { .unwrap_or_default() } + pub fn all_prettier_plugins(&self) -> Vec> { + let state = self.state.read(); + state + .languages + .iter() + .flat_map(|language| language.config.prettier_plugins.iter().cloned()) + .collect() + } + pub fn update_lsp_status( &self, server_name: LanguageServerName, @@ -680,6 +689,12 @@ impl LanguageRegistry { ) .await?; + let options = adapter + .adapter + .clone() + .initialization_options(&delegate) + .await?; + if let Some(task) = adapter.will_start_server(&delegate, &mut cx) { task.await?; } @@ -727,18 +742,21 @@ impl LanguageRegistry { }) .detach(); - return Ok(server); + return Ok((server, options)); } drop(this); - lsp::LanguageServer::new( - stderr_capture, - server_id, - binary, - &root_path, - adapter.code_action_kinds(), - cx, - ) + Ok(( + lsp::LanguageServer::new( + stderr_capture, + server_id, + binary, + &root_path, + adapter.code_action_kinds(), + cx, + )?, + options, + )) } }); diff --git a/crates/languages/src/astro.rs b/crates/languages/src/astro.rs index 75db8e9e94..c978e50638 100644 --- a/crates/languages/src/astro.rs +++ b/crates/languages/src/astro.rs @@ -90,17 +90,16 @@ impl LspAdapter for AstroLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "provideFormatter": true, "typescript": { "tsdk": "node_modules/typescript/lib", } - })) - } - - fn prettier_plugins(&self) -> &[&'static str] { - &["prettier-plugin-astro"] + }))) } } diff --git a/crates/languages/src/astro/config.toml b/crates/languages/src/astro/config.toml index e9f4b3d043..4e949ca515 100644 --- a/crates/languages/src/astro/config.toml +++ b/crates/languages/src/astro/config.toml @@ -16,6 +16,7 @@ brackets = [ word_characters = ["#", "$", "-"] scope_opt_in_language_servers = ["tailwindcss-language-server"] prettier_parser_name = "astro" +prettier_plugins = ["prettier-plugin-astro"] [overrides.string] word_characters = ["-"] diff --git a/crates/languages/src/css.rs b/crates/languages/src/css.rs index a91ff8befc..6e2e77a0ce 100644 --- a/crates/languages/src/css.rs +++ b/crates/languages/src/css.rs @@ -91,10 +91,13 @@ impl LspAdapter for CssLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "provideFormatter": true - })) + }))) } } diff --git a/crates/languages/src/deno.rs b/crates/languages/src/deno.rs index 1aed3930fb..6dab167380 100644 --- a/crates/languages/src/deno.rs +++ b/crates/languages/src/deno.rs @@ -188,10 +188,13 @@ impl LspAdapter for DenoLspAdapter { }) } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "provideFormatter": true, - })) + }))) } fn language_ids(&self) -> HashMap { diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index 505ba7bd04..af5aba381b 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -189,8 +189,11 @@ impl super::LspAdapter for GoLspAdapter { }) } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "usePlaceholders": true, "hints": { "assignVariableTypes": true, @@ -201,7 +204,7 @@ impl super::LspAdapter for GoLspAdapter { "parameterNames": true, "rangeVariableTypes": true } - })) + }))) } async fn label_for_completion( diff --git a/crates/languages/src/html.rs b/crates/languages/src/html.rs index a8dbfd47ba..6462280abe 100644 --- a/crates/languages/src/html.rs +++ b/crates/languages/src/html.rs @@ -91,10 +91,13 @@ impl LspAdapter for HtmlLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "provideFormatter": true - })) + }))) } } diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index 2c9cc76ac4..4eb9560fe9 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -143,10 +143,13 @@ impl LspAdapter for JsonLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "provideFormatter": true - })) + }))) } fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value { diff --git a/crates/languages/src/php.rs b/crates/languages/src/php.rs index 1e539826da..0151551d33 100644 --- a/crates/languages/src/php.rs +++ b/crates/languages/src/php.rs @@ -97,24 +97,9 @@ impl LspAdapter for IntelephenseLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - async fn label_for_completion( - &self, - _item: &lsp::CompletionItem, - _language: &Arc, - ) -> Option { - None - } - - fn initialization_options(&self) -> Option { - None - } fn language_ids(&self) -> HashMap { HashMap::from_iter([("PHP".into(), "php".into())]) } - - fn prettier_plugins(&self) -> &[&'static str] { - &["@prettier/plugin-php"] - } } async fn get_cached_server_binary( diff --git a/crates/languages/src/php/config.toml b/crates/languages/src/php/config.toml index e0ee871b87..54b6c2905f 100644 --- a/crates/languages/src/php/config.toml +++ b/crates/languages/src/php/config.toml @@ -15,3 +15,4 @@ collapsed_placeholder = "/* ... */" word_characters = ["$"] scope_opt_in_language_servers = ["tailwindcss-language-server"] prettier_parser_name = "php" +prettier_plugins = ["@prettier/plugin-php"] diff --git a/crates/languages/src/prisma.rs b/crates/languages/src/prisma.rs index 40f65babf0..124f21aefd 100644 --- a/crates/languages/src/prisma.rs +++ b/crates/languages/src/prisma.rs @@ -88,10 +88,6 @@ impl LspAdapter for PrismaLspAdapter { ) -> Option { get_cached_server_binary(container_dir, &*self.node).await } - - fn initialization_options(&self) -> Option { - None - } } async fn get_cached_server_binary( diff --git a/crates/languages/src/purescript.rs b/crates/languages/src/purescript.rs index e5a167f7ae..b050e41733 100644 --- a/crates/languages/src/purescript.rs +++ b/crates/languages/src/purescript.rs @@ -93,12 +93,15 @@ impl LspAdapter for PurescriptLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "purescript": { "addSpagoSources": true } - })) + }))) } fn language_ids(&self) -> HashMap { diff --git a/crates/languages/src/svelte.rs b/crates/languages/src/svelte.rs index 58d1dae2ea..8e890dcf3a 100644 --- a/crates/languages/src/svelte.rs +++ b/crates/languages/src/svelte.rs @@ -90,7 +90,10 @@ impl LspAdapter for SvelteLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - fn initialization_options(&self) -> Option { + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { let config = json!({ "inlayHints": { "parameterNames": { @@ -116,17 +119,13 @@ impl LspAdapter for SvelteLspAdapter { } }); - Some(json!({ + Ok(Some(json!({ "provideFormatter": true, "configuration": { "typescript": config, "javascript": config } - })) - } - - fn prettier_plugins(&self) -> &[&'static str] { - &["prettier-plugin-svelte"] + }))) } } diff --git a/crates/languages/src/svelte/config.toml b/crates/languages/src/svelte/config.toml index a05f4f9a4f..3bab2f2943 100644 --- a/crates/languages/src/svelte/config.toml +++ b/crates/languages/src/svelte/config.toml @@ -15,6 +15,7 @@ brackets = [ ] scope_opt_in_language_servers = ["tailwindcss-language-server"] prettier_parser_name = "svelte" +prettier_plugins = ["prettier-plugin-svelte"] [overrides.string] word_characters = ["-"] diff --git a/crates/languages/src/tailwind.rs b/crates/languages/src/tailwind.rs index 49a60102ad..b8f58616dd 100644 --- a/crates/languages/src/tailwind.rs +++ b/crates/languages/src/tailwind.rs @@ -92,8 +92,11 @@ impl LspAdapter for TailwindLspAdapter { get_cached_server_binary(container_dir, &*self.node).await } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "provideFormatter": true, "userLanguages": { "html": "html", @@ -101,7 +104,7 @@ impl LspAdapter for TailwindLspAdapter { "javascript": "javascript", "typescriptreact": "typescriptreact", }, - })) + }))) } fn workspace_configuration(&self, _workspace_root: &Path, _: &mut AppContext) -> Value { @@ -126,10 +129,6 @@ impl LspAdapter for TailwindLspAdapter { ("PHP".to_string(), "php".to_string()), ]) } - - fn prettier_plugins(&self) -> &[&'static str] { - &["prettier-plugin-tailwindcss"] - } } async fn get_cached_server_binary( diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index de6d5b3f01..4c2797f384 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -164,8 +164,11 @@ impl LspAdapter for TypeScriptLspAdapter { }) } - fn initialization_options(&self) -> Option { - Some(json!({ + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { + Ok(Some(json!({ "provideFormatter": true, "tsserver": { "path": "node_modules/typescript/lib", @@ -180,7 +183,7 @@ impl LspAdapter for TypeScriptLspAdapter { "includeInlayFunctionLikeReturnTypeHints": true, "includeInlayEnumMemberValueHints": true, } - })) + }))) } fn language_ids(&self) -> HashMap { @@ -367,18 +370,6 @@ impl LspAdapter for EsLintLspAdapter { ) -> Option { get_cached_eslint_server_binary(container_dir, &*self.node).await } - - async fn label_for_completion( - &self, - _item: &lsp::CompletionItem, - _language: &Arc, - ) -> Option { - None - } - - fn initialization_options(&self) -> Option { - None - } } async fn get_cached_eslint_server_binary( diff --git a/crates/languages/src/vue.rs b/crates/languages/src/vue.rs index 7f26d93829..f220ffdce4 100644 --- a/crates/languages/src/vue.rs +++ b/crates/languages/src/vue.rs @@ -5,7 +5,6 @@ pub use language::*; use lsp::{CodeActionKind, LanguageServerBinary}; use node_runtime::NodeRuntime; use parking_lot::Mutex; -use serde_json::Value; use smol::fs::{self}; use std::{ any::Any, @@ -56,17 +55,20 @@ impl super::LspAdapter for VueLspAdapter { ts_version: self.node.npm_package_latest_version("typescript").await?, }) as Box<_>) } - fn initialization_options(&self) -> Option { + async fn initialization_options( + self: Arc, + _: &Arc, + ) -> Result> { let typescript_sdk_path = self.typescript_install_path.lock(); let typescript_sdk_path = typescript_sdk_path .as_ref() .expect("initialization_options called without a container_dir for typescript"); - Some(serde_json::json!({ + Ok(Some(serde_json::json!({ "typescript": { "tsdk": typescript_sdk_path } - })) + }))) } fn code_action_kinds(&self) -> Option> { // REFACTOR is explicitly disabled, as vue-lsp does not adhere to LSP protocol for code actions with these - it diff --git a/crates/node_runtime/src/node_runtime.rs b/crates/node_runtime/src/node_runtime.rs index 59f136d7ec..10aab50b70 100644 --- a/crates/node_runtime/src/node_runtime.rs +++ b/crates/node_runtime/src/node_runtime.rs @@ -4,8 +4,8 @@ use async_tar::Archive; use futures::AsyncReadExt; use semver::Version; use serde::Deserialize; -use serde_json::Value; use smol::{fs, io::BufReader, lock::Mutex, process::Command}; +use std::io; use std::process::{Output, Stdio}; use std::{ env::consts, @@ -46,6 +46,12 @@ pub trait NodeRuntime: Send + Sync { async fn npm_install_packages(&self, directory: &Path, packages: &[(&str, &str)]) -> Result<()>; + async fn npm_package_installed_version( + &self, + local_package_directory: &PathBuf, + name: &str, + ) -> Result>; + async fn should_install_npm_package( &self, package_name: &str, @@ -60,36 +66,19 @@ pub trait NodeRuntime: Send + Sync { return true; } - let package_json_path = local_package_directory.join("package.json"); - - let mut contents = String::new(); - - let Some(mut file) = fs::File::open(package_json_path).await.log_err() else { + let Some(installed_version) = self + .npm_package_installed_version(local_package_directory, package_name) + .await + .log_err() + .flatten() + else { return true; }; - file.read_to_string(&mut contents).await.log_err(); - - let Some(package_json): Option = serde_json::from_str(&contents).log_err() else { + let Some(installed_version) = Version::parse(&installed_version).log_err() else { return true; }; - - let installed_version = package_json - .get("dependencies") - .and_then(|deps| deps.get(package_name)) - .and_then(|server_name| server_name.as_str()); - - let Some(installed_version) = installed_version else { - return true; - }; - - let Some(latest_version) = Version::parse(latest_version).log_err() else { - return true; - }; - - let installed_version = installed_version.trim_start_matches(|c: char| !c.is_ascii_digit()); - - let Some(installed_version) = Version::parse(installed_version).log_err() else { + let Some(latest_version) = Version::parse(&latest_version).log_err() else { return true; }; @@ -281,6 +270,36 @@ impl NodeRuntime for RealNodeRuntime { .ok_or_else(|| anyhow!("no version found for npm package {}", name)) } + async fn npm_package_installed_version( + &self, + local_package_directory: &PathBuf, + name: &str, + ) -> Result> { + let mut package_json_path = local_package_directory.clone(); + package_json_path.extend(["node_modules", name, "package.json"]); + + let mut file = match fs::File::open(package_json_path).await { + Ok(file) => file, + Err(err) => { + if err.kind() == io::ErrorKind::NotFound { + return Ok(None); + } + + Err(err)? + } + }; + + #[derive(Deserialize)] + struct PackageJson { + version: String, + } + + let mut contents = String::new(); + file.read_to_string(&mut contents).await?; + let package_json: PackageJson = serde_json::from_str(&contents)?; + Ok(Some(package_json.version)) + } + async fn npm_install_packages( &self, directory: &Path, @@ -335,6 +354,14 @@ impl NodeRuntime for FakeNodeRuntime { unreachable!("Should not query npm package '{name}' for latest version") } + async fn npm_package_installed_version( + &self, + _local_package_directory: &PathBuf, + name: &str, + ) -> Result> { + unreachable!("Should not query npm package '{name}' for installed version") + } + async fn npm_install_packages( &self, _: &Path, diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 676ed6d1ac..d35fc051c7 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -227,13 +227,8 @@ impl Prettier { let buffer_language = buffer.language(); let parser_with_plugins = buffer_language.and_then(|l| { let prettier_parser = l.prettier_parser_name()?; - let mut prettier_plugins = local - .language_registry - .lsp_adapters(l) - .iter() - .flat_map(|adapter| adapter.prettier_plugins()) - .copied() - .collect::>(); + let mut prettier_plugins = + local.language_registry.all_prettier_plugins(); prettier_plugins.dedup(); Some((prettier_parser, prettier_plugins)) }); @@ -243,8 +238,9 @@ impl Prettier { prettier_node_modules.is_dir(), "Prettier node_modules dir does not exist: {prettier_node_modules:?}" ); - let plugin_name_into_path = |plugin_name: &str| { - let prettier_plugin_dir = prettier_node_modules.join(plugin_name); + let plugin_name_into_path = |plugin_name: Arc| { + let prettier_plugin_dir = + prettier_node_modules.join(plugin_name.as_ref()); [ prettier_plugin_dir.join("dist").join("index.mjs"), prettier_plugin_dir.join("dist").join("index.js"), @@ -267,8 +263,10 @@ impl Prettier { let mut plugins = plugins .into_iter() - .filter(|&plugin_name| { - if plugin_name == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME { + .filter(|plugin_name| { + if plugin_name.as_ref() + == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME + { add_tailwind_back = true; false } else { @@ -276,14 +274,14 @@ impl Prettier { } }) .map(|plugin_name| { - (plugin_name, plugin_name_into_path(plugin_name)) + (plugin_name.clone(), plugin_name_into_path(plugin_name)) }) .collect::>(); if add_tailwind_back { plugins.push(( - &TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME, + TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME.into(), plugin_name_into_path( - TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME, + TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME.into(), ), )); } diff --git a/crates/project/src/prettier_support.rs b/crates/project/src/prettier_support.rs index 244ea0e3e1..43679ccea6 100644 --- a/crates/project/src/prettier_support.rs +++ b/crates/project/src/prettier_support.rs @@ -14,7 +14,7 @@ use futures::{ use gpui::{AsyncAppContext, Model, ModelContext, Task, WeakModel}; use language::{ language_settings::{Formatter, LanguageSettings}, - Buffer, Language, LanguageRegistry, LanguageServerName, LocalFile, + Buffer, Language, LanguageServerName, LocalFile, }; use lsp::{LanguageServer, LanguageServerId}; use node_runtime::NodeRuntime; @@ -25,28 +25,19 @@ use crate::{ Event, File, FormatOperation, PathChange, Project, ProjectEntryId, Worktree, WorktreeId, }; -pub fn prettier_plugins_for_language( - language_registry: &Arc, - language: &Arc, +pub fn prettier_plugins_for_language<'a>( + language: &'a Arc, language_settings: &LanguageSettings, -) -> Option> { +) -> Option<&'a Vec>> { match &language_settings.formatter { Formatter::Prettier { .. } | Formatter::Auto => {} Formatter::LanguageServer | Formatter::External { .. } => return None, }; - let mut prettier_plugins = None; if language.prettier_parser_name().is_some() { - prettier_plugins - .get_or_insert_with(|| HashSet::default()) - .extend( - language_registry - .lsp_adapters(language) - .iter() - .flat_map(|adapter| adapter.prettier_plugins()), - ) + Some(language.prettier_plugins()) + } else { + None } - - prettier_plugins } pub(super) async fn format_with_prettier( @@ -114,14 +105,14 @@ pub(super) async fn format_with_prettier( pub struct DefaultPrettier { prettier: PrettierInstallation, - installed_plugins: HashSet<&'static str>, + installed_plugins: HashSet>, } pub enum PrettierInstallation { NotInstalled { attempts: usize, installation_task: Option>>>>, - not_installed_plugins: HashSet<&'static str>, + not_installed_plugins: HashSet>, }, Installed(PrettierInstance), } @@ -376,12 +367,14 @@ fn register_new_prettier( async fn install_prettier_packages( fs: &dyn Fs, - plugins_to_install: HashSet<&'static str>, + plugins_to_install: HashSet>, node: Arc, ) -> anyhow::Result<()> { - let packages_to_versions = - future::try_join_all(plugins_to_install.iter().chain(Some(&"prettier")).map( - |package_name| async { + let packages_to_versions = future::try_join_all( + plugins_to_install + .iter() + .chain(Some(&"prettier".into())) + .map(|package_name| async { let returned_package_name = package_name.to_string(); let latest_version = node .npm_package_latest_version(package_name) @@ -390,10 +383,10 @@ async fn install_prettier_packages( format!("fetching latest npm version for package {returned_package_name}") })?; anyhow::Ok((returned_package_name, latest_version)) - }, - )) - .await - .context("fetching latest npm versions")?; + }), + ) + .await + .context("fetching latest npm versions")?; let default_prettier_dir = DEFAULT_PRETTIER_DIR.as_path(); match fs.metadata(default_prettier_dir).await.with_context(|| { @@ -639,32 +632,22 @@ impl Project { } } - #[cfg(any(test, feature = "test-support"))] - pub fn install_default_prettier( - &mut self, - _worktree: Option, - plugins: HashSet<&'static str>, - _cx: &mut ModelContext, - ) { - // suppress unused code warnings - let _ = should_write_prettier_server_file; - let _ = install_prettier_packages; - let _ = save_prettier_server_file; - - self.default_prettier.installed_plugins.extend(plugins); - self.default_prettier.prettier = PrettierInstallation::Installed(PrettierInstance { - attempt: 0, - prettier: None, - }); - } - - #[cfg(not(any(test, feature = "test-support")))] pub fn install_default_prettier( &mut self, worktree: Option, - mut new_plugins: HashSet<&'static str>, + plugins: impl Iterator>, cx: &mut ModelContext, ) { + if cfg!(any(test, feature = "test-support")) { + self.default_prettier.installed_plugins.extend(plugins); + self.default_prettier.prettier = PrettierInstallation::Installed(PrettierInstance { + attempt: 0, + prettier: None, + }); + return; + } + + let mut new_plugins = plugins.collect::>(); let Some(node) = self.node.as_ref().cloned() else { return; }; @@ -702,7 +685,7 @@ impl Project { ); return; } - new_plugins.extend(not_installed_plugins.iter()); + new_plugins.extend(not_installed_plugins.iter().cloned()); installation_task.clone() } PrettierInstallation::Installed { .. } => { @@ -735,7 +718,7 @@ impl Project { project.update(&mut cx, |project, _| { if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut project.default_prettier.prettier { *attempts += 1; - new_plugins.extend(not_installed_plugins.iter()); + new_plugins.extend(not_installed_plugins.iter().cloned()); installation_attempt = *attempts; needs_install = true; }; @@ -761,7 +744,7 @@ impl Project { not_installed_plugins.retain(|plugin| { !project.default_prettier.installed_plugins.contains(plugin) }); - not_installed_plugins.extend(new_plugins.iter()); + not_installed_plugins.extend(new_plugins.iter().cloned()); } needs_install |= !new_plugins.is_empty(); })?; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 689ef304fa..44db1aa40d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -994,19 +994,17 @@ impl Project { let mut prettier_plugins_by_worktree = HashMap::default(); for (worktree, language, settings) in language_formatters_to_check { - if let Some(plugins) = prettier_support::prettier_plugins_for_language( - &self.languages, - &language, - &settings, - ) { + if let Some(plugins) = + prettier_support::prettier_plugins_for_language(&language, &settings) + { prettier_plugins_by_worktree .entry(worktree) .or_insert_with(|| HashSet::default()) - .extend(plugins); + .extend(plugins.iter().cloned()); } } for (worktree, prettier_plugins) in prettier_plugins_by_worktree { - self.install_default_prettier(worktree, prettier_plugins, cx); + self.install_default_prettier(worktree, prettier_plugins.into_iter(), cx); } // Start all the newly-enabled language servers. @@ -2845,12 +2843,10 @@ impl Project { let settings = language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone(); let buffer_file = File::from_dyn(buffer_file.as_ref()); let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx)); - if let Some(prettier_plugins) = prettier_support::prettier_plugins_for_language( - &self.languages, - &new_language, - &settings, - ) { - self.install_default_prettier(worktree, prettier_plugins, cx); + if let Some(prettier_plugins) = + prettier_support::prettier_plugins_for_language(&new_language, &settings) + { + self.install_default_prettier(worktree, prettier_plugins.iter().cloned(), cx); }; if let Some(file) = buffer_file { let worktree = file.worktree.clone(); @@ -3104,7 +3100,7 @@ impl Project { ) -> Result> { let workspace_config = cx.update(|cx| adapter.workspace_configuration(worktree_path, cx))?; - let language_server = pending_server.task.await?; + let (language_server, mut initialization_options) = pending_server.task.await?; let name = language_server.name(); language_server @@ -3344,7 +3340,6 @@ impl Project { }) .detach(); - let mut initialization_options = adapter.adapter.initialization_options(); match (&mut initialization_options, override_options) { (Some(initialization_options), Some(override_options)) => { merge_json_value_into(override_options, initialization_options);