Detailed changes
@@ -15,8 +15,8 @@ use gpui::{executor::Deterministic, test::EmptyView, AppContext, ModelHandle, Te
use indoc::indoc;
use language::{
language_settings::{AllLanguageSettings, Formatter, InlayHintSettings},
- tree_sitter_rust, Anchor, BundledFormatter, Diagnostic, DiagnosticEntry, FakeLspAdapter,
- Language, LanguageConfig, LineEnding, OffsetRangeExt, Point, Rope,
+ tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
+ LanguageConfig, LineEnding, OffsetRangeExt, Point, Rope,
};
use live_kit_client::MacOSDisplay;
use lsp::LanguageServerId;
@@ -4530,6 +4530,7 @@ async fn test_prettier_formatting_buffer(
LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
+ prettier_parser_name: Some("test_parser".to_string()),
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -4537,10 +4538,7 @@ async fn test_prettier_formatting_buffer(
let test_plugin = "test_plugin";
let mut fake_language_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
- enabled_formatters: vec![BundledFormatter::Prettier {
- parser_name: Some("test_parser"),
- plugin_names: vec![test_plugin],
- }],
+ prettier_plugins: vec![test_plugin],
..Default::default()
}))
.await;
@@ -19,8 +19,8 @@ use gpui::{
use indoc::indoc;
use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
- BracketPairConfig, BundledFormatter, FakeLspAdapter, LanguageConfig, LanguageConfigOverride,
- LanguageRegistry, Override, Point,
+ BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
+ Override, Point,
};
use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings};
@@ -5095,6 +5095,9 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
+ // Enable Prettier formatting for the same buffer, and ensure
+ // LSP is called instead of Prettier.
+ prettier_parser_name: Some("test_parser".to_string()),
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -5105,12 +5108,6 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
document_formatting_provider: Some(lsp::OneOf::Left(true)),
..Default::default()
},
- // Enable Prettier formatting for the same buffer, and ensure
- // LSP is called instead of Prettier.
- enabled_formatters: vec![BundledFormatter::Prettier {
- parser_name: Some("test_parser"),
- plugin_names: Vec::new(),
- }],
..Default::default()
}))
.await;
@@ -7849,6 +7846,7 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
+ prettier_parser_name: Some("test_parser".to_string()),
..Default::default()
},
Some(tree_sitter_rust::language()),
@@ -7857,10 +7855,7 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
let test_plugin = "test_plugin";
let _ = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
- enabled_formatters: vec![BundledFormatter::Prettier {
- parser_name: Some("test_parser"),
- plugin_names: vec![test_plugin],
- }],
+ prettier_plugins: vec![test_plugin],
..Default::default()
}))
.await;
@@ -226,8 +226,8 @@ impl CachedLspAdapter {
self.adapter.label_for_symbol(name, kind, language).await
}
- pub fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- self.adapter.enabled_formatters()
+ pub fn prettier_plugins(&self) -> &[&'static str] {
+ self.adapter.prettier_plugins()
}
}
@@ -336,31 +336,8 @@ pub trait LspAdapter: 'static + Send + Sync {
Default::default()
}
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- Vec::new()
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum BundledFormatter {
- Prettier {
- // See https://prettier.io/docs/en/options.html#parser for a list of valid values.
- // Usually, every language has a single parser (standard or plugin-provided), hence `Some("parser_name")` can be used.
- // There can not be multiple parsers for a single language, in case of a conflict, we would attempt to select the one with most plugins.
- //
- // But exceptions like Tailwind CSS exist, which uses standard parsers for CSS/JS/HTML/etc. but require an extra plugin to be installed.
- // For those cases, `None` will install the plugin but apply other, regular parser defined for the language, and this would not be a conflict.
- parser_name: Option<&'static str>,
- plugin_names: Vec<&'static str>,
- },
-}
-
-impl BundledFormatter {
- pub fn prettier(parser_name: &'static str) -> Self {
- Self::Prettier {
- parser_name: Some(parser_name),
- plugin_names: Vec::new(),
- }
+ fn prettier_plugins(&self) -> &[&'static str] {
+ &[]
}
}
@@ -398,6 +375,8 @@ pub struct LanguageConfig {
pub overrides: HashMap<String, LanguageConfigOverride>,
#[serde(default)]
pub word_characters: HashSet<char>,
+ #[serde(default)]
+ pub prettier_parser_name: Option<String>,
}
#[derive(Debug, Default)]
@@ -471,6 +450,7 @@ impl Default for LanguageConfig {
overrides: Default::default(),
collapsed_placeholder: Default::default(),
word_characters: Default::default(),
+ prettier_parser_name: None,
}
}
}
@@ -496,7 +476,7 @@ pub struct FakeLspAdapter {
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
pub disk_based_diagnostics_progress_token: Option<String>,
pub disk_based_diagnostics_sources: Vec<String>,
- pub enabled_formatters: Vec<BundledFormatter>,
+ pub prettier_plugins: Vec<&'static str>,
}
#[derive(Clone, Debug, Default)]
@@ -1597,6 +1577,10 @@ impl Language {
override_id: None,
}
}
+
+ pub fn prettier_parser_name(&self) -> Option<&str> {
+ self.config.prettier_parser_name.as_deref()
+ }
}
impl LanguageScope {
@@ -1759,7 +1743,7 @@ impl Default for FakeLspAdapter {
disk_based_diagnostics_progress_token: None,
initialization_options: None,
disk_based_diagnostics_sources: Vec::new(),
- enabled_formatters: Vec::new(),
+ prettier_plugins: Vec::new(),
}
}
}
@@ -1817,8 +1801,8 @@ impl LspAdapter for Arc<FakeLspAdapter> {
self.initialization_options.clone()
}
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- self.enabled_formatters.clone()
+ fn prettier_plugins(&self) -> &[&'static str] {
+ &self.prettier_plugins
}
}
@@ -3,11 +3,11 @@ use std::path::{Path, PathBuf};
use std::sync::Arc;
use anyhow::Context;
-use collections::{HashMap, HashSet};
+use collections::HashMap;
use fs::Fs;
use gpui::{AsyncAppContext, ModelHandle};
use language::language_settings::language_settings;
-use language::{Buffer, BundledFormatter, Diff};
+use language::{Buffer, Diff};
use lsp::{LanguageServer, LanguageServerId};
use node_runtime::NodeRuntime;
use serde::{Deserialize, Serialize};
@@ -242,40 +242,16 @@ impl Prettier {
Self::Real(local) => {
let params = buffer.read_with(cx, |buffer, cx| {
let buffer_language = buffer.language();
- let parsers_with_plugins = buffer_language
- .into_iter()
- .flat_map(|language| {
- language
- .lsp_adapters()
- .iter()
- .flat_map(|adapter| adapter.enabled_formatters())
- .filter_map(|formatter| match formatter {
- BundledFormatter::Prettier {
- parser_name,
- plugin_names,
- } => Some((parser_name, plugin_names)),
- })
- })
- .fold(
- HashMap::default(),
- |mut parsers_with_plugins, (parser_name, plugins)| {
- match parser_name {
- Some(parser_name) => parsers_with_plugins
- .entry(parser_name)
- .or_insert_with(HashSet::default)
- .extend(plugins),
- None => parsers_with_plugins.values_mut().for_each(|existing_plugins| {
- existing_plugins.extend(plugins.iter());
- }),
- }
- parsers_with_plugins
- },
- );
-
- let selected_parser_with_plugins = parsers_with_plugins.iter().max_by_key(|(_, plugins)| plugins.len());
- if parsers_with_plugins.len() > 1 {
- log::warn!("Found multiple parsers with plugins {parsers_with_plugins:?}, will select only one: {selected_parser_with_plugins:?}");
- }
+ let parser_with_plugins = buffer_language.and_then(|l| {
+ let prettier_parser = l.prettier_parser_name()?;
+ let mut prettier_plugins = l
+ .lsp_adapters()
+ .iter()
+ .flat_map(|adapter| adapter.prettier_plugins())
+ .collect::<Vec<_>>();
+ prettier_plugins.dedup();
+ Some((prettier_parser, prettier_plugins))
+ });
let prettier_node_modules = self.prettier_dir().join("node_modules");
anyhow::ensure!(prettier_node_modules.is_dir(), "Prettier node_modules dir does not exist: {prettier_node_modules:?}");
@@ -296,7 +272,7 @@ impl Prettier {
}
None
};
- let (parser, located_plugins) = match selected_parser_with_plugins {
+ let (parser, located_plugins) = match parser_with_plugins {
Some((parser, plugins)) => {
// Tailwind plugin requires being added last
// https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins
@@ -39,11 +39,11 @@ use language::{
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
serialize_anchor, serialize_version, split_operations,
},
- range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, BundledFormatter, CachedLspAdapter,
- CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
- Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile,
- LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16,
- TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
+ range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeAction,
+ CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent,
+ File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate,
+ OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot,
+ ToOffset, ToPointUtf16, Transaction, Unclipped,
};
use log::error;
use lsp::{
@@ -8352,12 +8352,7 @@ impl Project {
let Some(buffer_language) = buffer.language() else {
return Task::ready(None);
};
- if !buffer_language
- .lsp_adapters()
- .iter()
- .flat_map(|adapter| adapter.enabled_formatters())
- .any(|formatter| matches!(formatter, BundledFormatter::Prettier { .. }))
- {
+ if buffer_language.prettier_parser_name().is_none() {
return Task::ready(None);
}
@@ -8510,16 +8505,15 @@ impl Project {
};
let mut prettier_plugins = None;
- for formatter in new_language
- .lsp_adapters()
- .into_iter()
- .flat_map(|adapter| adapter.enabled_formatters())
- {
- match formatter {
- BundledFormatter::Prettier { plugin_names, .. } => prettier_plugins
- .get_or_insert_with(|| HashSet::default())
- .extend(plugin_names),
- }
+ if new_language.prettier_parser_name().is_some() {
+ prettier_plugins
+ .get_or_insert_with(|| HashSet::default())
+ .extend(
+ new_language
+ .lsp_adapters()
+ .iter()
+ .flat_map(|adapter| adapter.prettier_plugins()),
+ )
}
let Some(prettier_plugins) = prettier_plugins else {
return Task::ready(Ok(()));
@@ -1,7 +1,7 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
-use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::json;
@@ -96,10 +96,6 @@ impl LspAdapter for CssLspAdapter {
"provideFormatter": true
}))
}
-
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::prettier("css")]
- }
}
async fn get_cached_server_binary(
@@ -10,3 +10,4 @@ brackets = [
]
word_characters = ["-"]
block_comment = ["/* ", " */"]
+prettier_parser_name = "css"
@@ -1,7 +1,7 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
-use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::json;
@@ -96,10 +96,6 @@ impl LspAdapter for HtmlLspAdapter {
"provideFormatter": true
}))
}
-
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::prettier("html")]
- }
}
async fn get_cached_server_binary(
@@ -11,3 +11,4 @@ brackets = [
{ start = "!--", end = " --", close = true, newline = false, not_in = ["comment", "string"] },
]
word_characters = ["-"]
+prettier_parser_name = "html"
@@ -15,6 +15,7 @@ brackets = [
]
word_characters = ["$", "#"]
scope_opt_in_language_servers = ["tailwindcss-language-server"]
+prettier_parser_name = "babel"
[overrides.element]
line_comment = { remove = true }
@@ -4,9 +4,7 @@ use collections::HashMap;
use feature_flags::FeatureFlagAppExt;
use futures::{future::BoxFuture, FutureExt, StreamExt};
use gpui::AppContext;
-use language::{
- BundledFormatter, LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate,
-};
+use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::json;
@@ -146,10 +144,6 @@ impl LspAdapter for JsonLspAdapter {
async fn language_ids(&self) -> HashMap<String, String> {
[("JSON".into(), "jsonc".into())].into_iter().collect()
}
-
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::prettier("json")]
- }
}
async fn get_cached_server_binary(
@@ -7,3 +7,4 @@ brackets = [
{ start = "[", end = "]", close = true, newline = true },
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
]
+prettier_parser_name = "json"
@@ -1,7 +1,7 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::StreamExt;
-use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::json;
@@ -96,11 +96,8 @@ impl LspAdapter for SvelteLspAdapter {
}))
}
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::Prettier {
- parser_name: Some("svelte"),
- plugin_names: vec!["prettier-plugin-svelte"],
- }]
+ fn prettier_plugins(&self) -> &[&'static str] {
+ &["prettier-plugin-svelte"]
}
}
@@ -13,6 +13,7 @@ brackets = [
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
scope_opt_in_language_servers = ["tailwindcss-language-server"]
+prettier_parser_name = "svelte"
[overrides.string]
word_characters = ["-"]
@@ -6,7 +6,7 @@ use futures::{
FutureExt, StreamExt,
};
use gpui::AppContext;
-use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use serde_json::{json, Value};
@@ -130,11 +130,8 @@ impl LspAdapter for TailwindLspAdapter {
])
}
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::Prettier {
- parser_name: None,
- plugin_names: vec!["prettier-plugin-tailwindcss"],
- }]
+ fn prettier_plugins(&self) -> &[&'static str] {
+ &["prettier-plugin-tailwindcss"]
}
}
@@ -14,6 +14,7 @@ brackets = [
]
word_characters = ["#", "$"]
scope_opt_in_language_servers = ["tailwindcss-language-server"]
+prettier_parser_name = "typescript"
[overrides.element]
line_comment = { remove = true }
@@ -4,7 +4,7 @@ use async_tar::Archive;
use async_trait::async_trait;
use futures::{future::BoxFuture, FutureExt};
use gpui::AppContext;
-use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::{CodeActionKind, LanguageServerBinary};
use node_runtime::NodeRuntime;
use serde_json::{json, Value};
@@ -161,10 +161,6 @@ impl LspAdapter for TypeScriptLspAdapter {
"provideFormatter": true
}))
}
-
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::prettier("typescript")]
- }
}
async fn get_cached_ts_server_binary(
@@ -313,10 +309,6 @@ impl LspAdapter for EsLintLspAdapter {
async fn initialization_options(&self) -> Option<serde_json::Value> {
None
}
-
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::prettier("babel")]
- }
}
async fn get_cached_eslint_server_binary(
@@ -13,3 +13,4 @@ brackets = [
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
word_characters = ["#", "$"]
+prettier_parser_name = "typescript"
@@ -3,8 +3,7 @@ use async_trait::async_trait;
use futures::{future::BoxFuture, FutureExt, StreamExt};
use gpui::AppContext;
use language::{
- language_settings::all_language_settings, BundledFormatter, LanguageServerName, LspAdapter,
- LspAdapterDelegate,
+ language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate,
};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
@@ -109,10 +108,6 @@ impl LspAdapter for YamlLspAdapter {
}))
.boxed()
}
-
- fn enabled_formatters(&self) -> Vec<BundledFormatter> {
- vec![BundledFormatter::prettier("yaml")]
- }
}
async fn get_cached_server_binary(
@@ -9,3 +9,4 @@ brackets = [
]
increase_indent_pattern = ":\\s*[|>]?\\s*$"
+prettier_parser_name = "yaml"