Detailed changes
@@ -92,4 +92,37 @@ impl LspAdapter for ExtensionLspAdapter {
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
None
}
+
+ async fn initialization_options(
+ self: Arc<Self>,
+ delegate: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ 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
+ })
+ }
}
@@ -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<Result<Option<String>, String>> {
+ async fn inner(
+ this: &mut WasmState,
+ package_name: String,
+ ) -> anyhow::Result<Option<String>> {
+ 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<Result<(), String>> {
+ 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,
@@ -13,6 +13,14 @@ pub trait Extension: Send + Sync {
config: wit::LanguageServerConfig,
worktree: &wit::Worktree,
) -> Result<Command>;
+
+ fn language_server_initialization_options(
+ &mut self,
+ _config: wit::LanguageServerConfig,
+ _worktree: &wit::Worktree,
+ ) -> Result<Option<String>> {
+ Ok(None)
+ }
}
#[macro_export]
@@ -60,4 +68,11 @@ impl wit::Guest for Component {
) -> Result<wit::Command> {
extension().language_server_command(config, worktree)
}
+
+ fn language_server_initialization_options(
+ config: LanguageServerConfig,
+ worktree: &Worktree,
+ ) -> Result<Option<String>, String> {
+ extension().language_server_initialization_options(config, worktree)
+ }
}
@@ -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<string, string>;
+ /// Returns the installed version of the given NPM package, if it exists.
+ import npm-package-installed-version: func(package-name: string) -> result<option<string>, 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<github-release, string>;
@@ -81,4 +87,5 @@ world extension {
}
export language-server-command: func(config: language-server-config, worktree: borrow<worktree>) -> result<command, string>;
+ export language-server-initialization-options: func(config: language-server-config, worktree: borrow<worktree>) -> result<option<string>, string>;
}
@@ -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<Value> {
- None
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<Value>> {
+ 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<String>,
+ /// The names of any Prettier plugins that should be used for this language.
+ #[serde(default)]
+ pub prettier_plugins: Vec<Arc<str>>,
}
#[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<Arc<str>> {
+ &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<Value> {
- self.initialization_options.clone()
- }
-
- fn prettier_plugins(&self) -> &[&'static str] {
- &self.prettier_plugins
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<Value>> {
+ Ok(self.initialization_options.clone())
}
fn as_fake(&self) -> Option<&FakeLspAdapter> {
@@ -63,7 +63,7 @@ pub enum LanguageServerBinaryStatus {
pub struct PendingLanguageServer {
pub server_id: LanguageServerId,
- pub task: Task<Result<lsp::LanguageServer>>,
+ pub task: Task<Result<(lsp::LanguageServer, Option<serde_json::Value>)>>,
pub container_dir: Option<Arc<Path>>,
}
@@ -629,6 +629,15 @@ impl LanguageRegistry {
.unwrap_or_default()
}
+ pub fn all_prettier_plugins(&self) -> Vec<Arc<str>> {
+ 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,
+ ))
}
});
@@ -90,17 +90,16 @@ impl LspAdapter for AstroLspAdapter {
get_cached_server_binary(container_dir, &*self.node).await
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(Some(json!({
"provideFormatter": true,
"typescript": {
"tsdk": "node_modules/typescript/lib",
}
- }))
- }
-
- fn prettier_plugins(&self) -> &[&'static str] {
- &["prettier-plugin-astro"]
+ })))
}
}
@@ -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 = ["-"]
@@ -91,10 +91,13 @@ impl LspAdapter for CssLspAdapter {
get_cached_server_binary(container_dir, &*self.node).await
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(Some(json!({
"provideFormatter": true
- }))
+ })))
}
}
@@ -188,10 +188,13 @@ impl LspAdapter for DenoLspAdapter {
})
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(Some(json!({
"provideFormatter": true,
- }))
+ })))
}
fn language_ids(&self) -> HashMap<String, String> {
@@ -189,8 +189,11 @@ impl super::LspAdapter for GoLspAdapter {
})
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ 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(
@@ -91,10 +91,13 @@ impl LspAdapter for HtmlLspAdapter {
get_cached_server_binary(container_dir, &*self.node).await
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(Some(json!({
"provideFormatter": true
- }))
+ })))
}
}
@@ -143,10 +143,13 @@ impl LspAdapter for JsonLspAdapter {
get_cached_server_binary(container_dir, &*self.node).await
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(Some(json!({
"provideFormatter": true
- }))
+ })))
}
fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value {
@@ -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<language::Language>,
- ) -> Option<language::CodeLabel> {
- None
- }
-
- fn initialization_options(&self) -> Option<serde_json::Value> {
- None
- }
fn language_ids(&self) -> HashMap<String, String> {
HashMap::from_iter([("PHP".into(), "php".into())])
}
-
- fn prettier_plugins(&self) -> &[&'static str] {
- &["@prettier/plugin-php"]
- }
}
async fn get_cached_server_binary(
@@ -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"]
@@ -88,10 +88,6 @@ impl LspAdapter for PrismaLspAdapter {
) -> Option<LanguageServerBinary> {
get_cached_server_binary(container_dir, &*self.node).await
}
-
- fn initialization_options(&self) -> Option<serde_json::Value> {
- None
- }
}
async fn get_cached_server_binary(
@@ -93,12 +93,15 @@ impl LspAdapter for PurescriptLspAdapter {
get_cached_server_binary(container_dir, &*self.node).await
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ Ok(Some(json!({
"purescript": {
"addSpagoSources": true
}
- }))
+ })))
}
fn language_ids(&self) -> HashMap<String, String> {
@@ -90,7 +90,10 @@ impl LspAdapter for SvelteLspAdapter {
get_cached_server_binary(container_dir, &*self.node).await
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
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"]
+ })))
}
}
@@ -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 = ["-"]
@@ -92,8 +92,11 @@ impl LspAdapter for TailwindLspAdapter {
get_cached_server_binary(container_dir, &*self.node).await
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ 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(
@@ -164,8 +164,11 @@ impl LspAdapter for TypeScriptLspAdapter {
})
}
- fn initialization_options(&self) -> Option<serde_json::Value> {
- Some(json!({
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
+ 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<String, String> {
@@ -367,18 +370,6 @@ impl LspAdapter for EsLintLspAdapter {
) -> Option<LanguageServerBinary> {
get_cached_eslint_server_binary(container_dir, &*self.node).await
}
-
- async fn label_for_completion(
- &self,
- _item: &lsp::CompletionItem,
- _language: &Arc<language::Language>,
- ) -> Option<language::CodeLabel> {
- None
- }
-
- fn initialization_options(&self) -> Option<serde_json::Value> {
- None
- }
}
async fn get_cached_eslint_server_binary(
@@ -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<Value> {
+ async fn initialization_options(
+ self: Arc<Self>,
+ _: &Arc<dyn LspAdapterDelegate>,
+ ) -> Result<Option<serde_json::Value>> {
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<Vec<CodeActionKind>> {
// REFACTOR is explicitly disabled, as vue-lsp does not adhere to LSP protocol for code actions with these - it
@@ -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<Option<String>>;
+
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 {
- return true;
- };
-
- file.read_to_string(&mut contents).await.log_err();
-
- let Some(package_json): Option<Value> = serde_json::from_str(&contents).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 {
+ let Some(installed_version) = self
+ .npm_package_installed_version(local_package_directory, package_name)
+ .await
+ .log_err()
+ .flatten()
+ else {
return true;
};
- let Some(latest_version) = Version::parse(latest_version).log_err() else {
+ let Some(installed_version) = Version::parse(&installed_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<Option<String>> {
+ 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<Option<String>> {
+ unreachable!("Should not query npm package '{name}' for installed version")
+ }
+
async fn npm_install_packages(
&self,
_: &Path,
@@ -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::<Vec<_>>();
+ 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<str>| {
+ 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::<Vec<_>>();
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(),
),
));
}
@@ -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<LanguageRegistry>,
- language: &Arc<Language>,
+pub fn prettier_plugins_for_language<'a>(
+ language: &'a Arc<Language>,
language_settings: &LanguageSettings,
-) -> Option<HashSet<&'static str>> {
+) -> Option<&'a Vec<Arc<str>>> {
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<Arc<str>>,
}
pub enum PrettierInstallation {
NotInstalled {
attempts: usize,
installation_task: Option<Shared<Task<Result<(), Arc<anyhow::Error>>>>>,
- not_installed_plugins: HashSet<&'static str>,
+ not_installed_plugins: HashSet<Arc<str>>,
},
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<Arc<str>>,
node: Arc<dyn NodeRuntime>,
) -> 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<WorktreeId>,
- plugins: HashSet<&'static str>,
- _cx: &mut ModelContext<Self>,
- ) {
- // 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<WorktreeId>,
- mut new_plugins: HashSet<&'static str>,
+ plugins: impl Iterator<Item = Arc<str>>,
cx: &mut ModelContext<Self>,
) {
+ 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::<HashSet<_>>();
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();
})?;
@@ -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<Arc<LanguageServer>> {
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);