diff --git a/assets/settings/default.json b/assets/settings/default.json index e36440bf10..41ddedccce 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -103,14 +103,7 @@ // Hide the values of in variables from visual display in private files "redact_private_values": false, // Globs to match against file paths to determine if a file is private. - "private_files": [ - "**/.env*", - "**/*.pem", - "**/*.key", - "**/*.cert", - "**/*.crt", - "**/secrets.yml" - ], + "private_files": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"], // Whether to use additional LSP queries to format (and amend) the code after // every "trigger" symbol input, defined by LSP server capabilities. "use_on_type_format": true, @@ -311,9 +304,7 @@ // The list of language servers to use (or disable) for all languages. // // This is typically customized on a per-language basis. - "language_servers": [ - "..." - ], + "language_servers": ["..."], // When to automatically save edited buffers. This setting can // take four values. // @@ -448,9 +439,7 @@ "copilot": { // The set of glob patterns for which copilot should be disabled // in any matching file. - "disabled_globs": [ - ".env" - ] + "disabled_globs": [".env"] }, // Settings specific to journaling "journal": { @@ -561,12 +550,7 @@ // Default directories to search for virtual environments, relative // to the current working directory. We recommend overriding this // in your project's settings, rather than globally. - "directories": [ - ".env", - "env", - ".venv", - "venv" - ], + "directories": [".env", "env", ".venv", "venv"], // Can also be 'csh', 'fish', and `nushell` "activate_script": "default" } @@ -609,19 +593,30 @@ }, // Different settings for specific languages. "languages": { - "C++": { - "format_on_save": "off" + "Astro": { + "prettier": { + "allowed": true, + "plugins": ["prettier-plugin-astro"] + } + }, + "Blade": { + "prettier": { + "allowed": true + } }, "C": { "format_on_save": "off" }, + "C++": { + "format_on_save": "off" + }, + "CSS": { + "prettier": { + "allowed": true + } + }, "Elixir": { - "language_servers": [ - "elixir-ls", - "!next-ls", - "!lexical", - "..." - ] + "language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."] }, "Gleam": { "tab_size": 2 @@ -631,32 +626,120 @@ "source.organizeImports": true } }, + "GraphQL": { + "prettier": { + "allowed": true + } + }, "HEEX": { - "language_servers": [ - "elixir-ls", - "!next-ls", - "!lexical", - "..." - ] + "language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."] + }, + "HTML": { + "prettier": { + "allowed": true + } + }, + "Java": { + "prettier": { + "allowed": true, + "plugins": ["prettier-plugin-java"] + } + }, + "JavaScript": { + "prettier": { + "allowed": true + } + }, + "JSON": { + "prettier": { + "allowed": true + } }, "Make": { "hard_tabs": true }, "Markdown": { - "format_on_save": "off" + "format_on_save": "off", + "prettier": { + "allowed": true + } + }, + "PHP": { + "prettier": { + "allowed": true, + "plugins": ["@prettier/plugin-php"] + } }, "Prisma": { "tab_size": 2 }, "Ruby": { "language_servers": ["solargraph", "!ruby-lsp", "..."] + }, + "SCSS": { + "prettier": { + "allowed": true + } + }, + "SQL": { + "prettier": { + "allowed": true, + "plugins": ["prettier-plugin-sql"] + } + }, + "Svelte": { + "prettier": { + "allowed": true, + "plugins": ["prettier-plugin-svelte"] + } + }, + "TSX": { + "prettier": { + "allowed": true + } + }, + "Twig": { + "prettier": { + "allowed": true + } + }, + "TypeScript": { + "prettier": { + "allowed": true + } + }, + "Vue.js": { + "prettier": { + "allowed": true + } + }, + "XML": { + "prettier": { + "allowed": true, + "plugins": ["@prettier/plugin-xml"] + } + }, + "YAML": { + "prettier": { + "allowed": true + } } }, // Zed's Prettier integration settings. - // If Prettier is enabled, Zed will use this for its Prettier instance for any applicable file, if - // project has no other Prettier installed. + // Allows to enable/disable formatting with Prettier + // and configure default Prettier, used when no project-level Prettier installation is found. "prettier": { - // Use regular Prettier json configuration: + // // Whether to consider prettier formatter or not when attempting to format a file. + // "allowed": false, + // + // // Use regular Prettier json configuration. + // // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if + // // the project has no other Prettier installed. + // "plugins": [], + // + // // Use regular Prettier json configuration. + // // If Prettier is allowed, Zed will use this for its Prettier instance for any applicable file, if + // // the project has no other Prettier installed. // "trailingComma": "es5", // "tabWidth": 4, // "semi": false, diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 44889485b0..7dfe192721 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -17,7 +17,7 @@ use gpui::{ MouseDownEvent, TestAppContext, }; use language::{ - language_settings::{AllLanguageSettings, Formatter}, + language_settings::{AllLanguageSettings, Formatter, PrettierSettings}, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope, }; @@ -4445,18 +4445,17 @@ async fn test_prettier_formatting_buffer( client_a.language_registry().add(Arc::new(Language::new( LanguageConfig { - name: "Rust".into(), + name: "TypeScript".into(), matcher: LanguageMatcher { - path_suffixes: vec!["rs".to_string()], + path_suffixes: vec!["ts".to_string()], ..Default::default() }, - prettier_parser_name: Some("test_parser".to_string()), ..Default::default() }, Some(tree_sitter_rust::language()), ))); let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( - "Rust", + "TypeScript", FakeLspAdapter { prettier_plugins: vec![test_plugin], ..Default::default() @@ -4470,11 +4469,11 @@ async fn test_prettier_formatting_buffer( let buffer_text = "let one = \"two\""; client_a .fs() - .insert_tree(&directory, json!({ "a.rs": buffer_text })) + .insert_tree(&directory, json!({ "a.ts": buffer_text })) .await; let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await; let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX; - let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)); + let open_buffer = project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx)); let buffer_a = cx_a.executor().spawn(open_buffer).await.unwrap(); let project_id = active_call_a @@ -4482,13 +4481,17 @@ async fn test_prettier_formatting_buffer( .await .unwrap(); let project_b = client_b.build_dev_server_project(project_id, cx_b).await; - let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)); + let open_buffer = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.ts"), cx)); let buffer_b = cx_b.executor().spawn(open_buffer).await.unwrap(); cx_a.update(|cx| { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |file| { file.defaults.formatter = Some(Formatter::Auto); + file.defaults.prettier = Some(PrettierSettings { + allowed: true, + ..PrettierSettings::default() + }); }); }); }); @@ -4496,6 +4499,10 @@ async fn test_prettier_formatting_buffer( cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |file| { file.defaults.formatter = Some(Formatter::LanguageServer); + file.defaults.prettier = Some(PrettierSettings { + allowed: true, + ..PrettierSettings::default() + }); }); }); }); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index c6b27afbab..d3a9b55249 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -12,7 +12,9 @@ use futures::StreamExt; use gpui::{div, TestAppContext, VisualTestContext, WindowBounds, WindowOptions}; use indoc::indoc; use language::{ - language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent}, + language_settings::{ + AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings, + }, BracketPairConfig, Capability::ReadWrite, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, Override, Point, @@ -6254,13 +6256,18 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { path_suffixes: vec!["rs".to_string()], ..Default::default() }, - // 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() + ..LanguageConfig::default() }, Some(tree_sitter_rust::language()), ))); + update_test_language_settings(cx, |settings| { + // Enable Prettier formatting for the same buffer, and ensure + // LSP is called instead of Prettier. + settings.defaults.prettier = Some(PrettierSettings { + allowed: true, + ..PrettierSettings::default() + }); + }); let mut fake_servers = language_registry.register_fake_lsp_adapter( "Rust", FakeLspAdapter { @@ -8599,27 +8606,32 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) { }); let fs = FakeFs::new(cx.executor()); - fs.insert_file("/file.rs", Default::default()).await; + fs.insert_file("/file.ts", Default::default()).await; - let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs, ["/file.ts".as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(Arc::new(Language::new( LanguageConfig { - name: "Rust".into(), + name: "TypeScript".into(), matcher: LanguageMatcher { - path_suffixes: vec!["rs".to_string()], + path_suffixes: vec!["ts".to_string()], ..Default::default() }, - prettier_parser_name: Some("test_parser".to_string()), ..Default::default() }, Some(tree_sitter_rust::language()), ))); + update_test_language_settings(cx, |settings| { + settings.defaults.prettier = Some(PrettierSettings { + allowed: true, + ..PrettierSettings::default() + }); + }); let test_plugin = "test_plugin"; let _ = language_registry.register_fake_lsp_adapter( - "Rust", + "TypeScript", FakeLspAdapter { prettier_plugins: vec![test_plugin], ..Default::default() @@ -8628,7 +8640,7 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) { let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX; let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx)) .await .unwrap(); diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 5e9f00c451..0fcdc463f5 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -479,6 +479,7 @@ pub struct Chunk<'a> { } /// A set of edits to a given version of a buffer, computed asynchronously. +#[derive(Debug)] pub struct Diff { pub(crate) base_version: clock::Global, line_ending: LineEnding, diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index cd8e053f36..a5826c2555 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -602,13 +602,6 @@ pub struct LanguageConfig { /// or a whole-word search in buffer search. #[serde(default)] pub word_characters: HashSet, - /// 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>, - /// Whether to indent lines using tab characters, as opposed to multiple /// spaces. #[serde(default)] @@ -700,8 +693,6 @@ impl Default for LanguageConfig { scope_opt_in_language_servers: Default::default(), overrides: Default::default(), word_characters: Default::default(), - prettier_parser_name: None, - prettier_plugins: Default::default(), collapsed_placeholder: Default::default(), hard_tabs: Default::default(), tab_size: Default::default(), @@ -1375,14 +1366,6 @@ 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 - } - pub fn lsp_id(&self) -> String { match self.config.name.as_ref() { "Plain Text" => "plaintext".to_string(), diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index cf0b869d61..e0faa10c3a 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -715,15 +715,6 @@ 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, diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 537816b983..303564d390 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -89,9 +89,7 @@ pub struct LanguageSettings { /// How to perform a buffer format. pub formatter: Formatter, /// Zed's Prettier integration settings. - /// If Prettier is enabled, Zed will use this for its Prettier instance for any applicable file, if - /// the project has no other Prettier installed. - pub prettier: HashMap, + pub prettier: PrettierSettings, /// Whether to use language servers to provide code intelligence. pub enable_language_server: bool, /// The list of language servers to use (or disable) for this language. @@ -267,12 +265,12 @@ pub struct LanguageSettingsContent { #[serde(default)] pub formatter: Option, /// Zed's Prettier integration settings. - /// If Prettier is enabled, Zed will use this for its Prettier instance for any applicable file, if - /// the project has no other Prettier installed. + /// Allows to enable/disable formatting with Prettier + /// and configure default Prettier, used when no project-level Prettier installation is found. /// - /// Default: {} + /// Default: off #[serde(default)] - pub prettier: Option>, + pub prettier: Option, /// Whether to use language servers to provide code intelligence. /// /// Default: true @@ -751,6 +749,30 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent merge(&mut settings.inlay_hints, src.inlay_hints); } +/// Allows to enable/disable formatting with Prettier +/// and configure default Prettier, used when no project-level Prettier installation is found. +/// Prettier formatting is disabled by default. +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +pub struct PrettierSettings { + /// Enables or disables formatting with Prettier for a given language. + #[serde(default)] + pub allowed: bool, + + /// Forces Prettier integration to use a specific parser name when formatting files with the language. + #[serde(default)] + pub parser: Option, + + /// Forces Prettier integration to use specific plugins when formatting files with the language. + /// The default Prettier will be installed with these plugins. + #[serde(default)] + pub plugins: HashSet, + + /// Default Prettier options, in the format as in package.json section for Prettier. + /// If project installs Prettier via its package.json, these options will be ignored. + #[serde(flatten)] + pub options: HashMap, +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/languages/src/css/config.toml b/crates/languages/src/css/config.toml index e22abe6d70..f5f06320e7 100644 --- a/crates/languages/src/css/config.toml +++ b/crates/languages/src/css/config.toml @@ -11,4 +11,3 @@ brackets = [ ] word_characters = ["-"] block_comment = ["/* ", " */"] -prettier_parser_name = "css" diff --git a/crates/languages/src/javascript/config.toml b/crates/languages/src/javascript/config.toml index 460ee0e0fd..32e08c1cd9 100644 --- a/crates/languages/src/javascript/config.toml +++ b/crates/languages/src/javascript/config.toml @@ -17,7 +17,6 @@ brackets = [ word_characters = ["$", "#"] tab_size = 2 scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-server"] -prettier_parser_name = "babel" [overrides.element] line_comments = { remove = true } diff --git a/crates/languages/src/json/config.toml b/crates/languages/src/json/config.toml index c4e0ace9bf..e3869f88a6 100644 --- a/crates/languages/src/json/config.toml +++ b/crates/languages/src/json/config.toml @@ -8,5 +8,4 @@ brackets = [ { start = "[", end = "]", close = true, newline = true }, { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, ] -prettier_parser_name = "json" tab_size = 2 diff --git a/crates/languages/src/markdown/config.toml b/crates/languages/src/markdown/config.toml index e334e86d6a..2f8b80266f 100644 --- a/crates/languages/src/markdown/config.toml +++ b/crates/languages/src/markdown/config.toml @@ -11,7 +11,6 @@ brackets = [ { start = "'", end = "'", close = false, newline = false }, { start = "`", end = "`", close = false, newline = false }, ] -prettier_parser_name = "markdown" tab_size = 2 soft_wrap = "preferred_line_length" diff --git a/crates/languages/src/tsx/config.toml b/crates/languages/src/tsx/config.toml index 3cd377ce0b..f7c8b80a90 100644 --- a/crates/languages/src/tsx/config.toml +++ b/crates/languages/src/tsx/config.toml @@ -15,7 +15,6 @@ brackets = [ ] word_characters = ["#", "$"] scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-server"] -prettier_parser_name = "typescript" tab_size = 2 [overrides.element] diff --git a/crates/languages/src/typescript/config.toml b/crates/languages/src/typescript/config.toml index ca115622c6..254f929053 100644 --- a/crates/languages/src/typescript/config.toml +++ b/crates/languages/src/typescript/config.toml @@ -14,5 +14,4 @@ brackets = [ { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] }, ] word_characters = ["#", "$"] -prettier_parser_name = "typescript" tab_size = 2 diff --git a/crates/languages/src/yaml/config.toml b/crates/languages/src/yaml/config.toml index 43b21a1329..4490e7d0a8 100644 --- a/crates/languages/src/yaml/config.toml +++ b/crates/languages/src/yaml/config.toml @@ -10,5 +10,4 @@ brackets = [ ] increase_indent_pattern = ":\\s*[|>]?\\s*$" -prettier_parser_name = "yaml" tab_size = 2 diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index d35fc051c7..d98d06347d 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -2,7 +2,7 @@ use anyhow::Context; use collections::{HashMap, HashSet}; use fs::Fs; use gpui::{AsyncAppContext, Model}; -use language::{language_settings::language_settings, Buffer, Diff, LanguageRegistry}; +use language::{language_settings::language_settings, Buffer, Diff}; use lsp::{LanguageServer, LanguageServerId}; use node_runtime::NodeRuntime; use serde::{Deserialize, Serialize}; @@ -25,7 +25,6 @@ pub struct RealPrettier { default: bool, prettier_dir: PathBuf, server: Arc, - language_registry: Arc, } #[cfg(any(test, feature = "test-support"))] @@ -156,7 +155,6 @@ impl Prettier { _: LanguageServerId, prettier_dir: PathBuf, _: Arc, - _: Arc, _: AsyncAppContext, ) -> anyhow::Result { Ok(Self::Test(TestPrettier { @@ -170,7 +168,6 @@ impl Prettier { server_id: LanguageServerId, prettier_dir: PathBuf, node: Arc, - language_registry: Arc, cx: AsyncAppContext, ) -> anyhow::Result { use lsp::LanguageServerBinary; @@ -209,7 +206,6 @@ impl Prettier { Ok(Self::Real(RealPrettier { server, default: prettier_dir == DEFAULT_PRETTIER_DIR.as_path(), - language_registry, prettier_dir, })) } @@ -225,22 +221,19 @@ impl Prettier { let params = buffer .update(cx, |buffer, cx| { 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.all_prettier_plugins(); - prettier_plugins.dedup(); - Some((prettier_parser, prettier_plugins)) - }); - + let language_settings = language_settings(buffer_language, buffer.file(), cx); + let prettier_settings = &language_settings.prettier; + anyhow::ensure!( + prettier_settings.allowed, + "Cannot format: prettier is not allowed for language {buffer_language:?}" + ); 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:?}" ); - let plugin_name_into_path = |plugin_name: Arc| { - let prettier_plugin_dir = - prettier_node_modules.join(plugin_name.as_ref()); + let plugin_name_into_path = |plugin_name: &str| { + let prettier_plugin_dir = prettier_node_modules.join(plugin_name); [ prettier_plugin_dir.join("dist").join("index.mjs"), prettier_plugin_dir.join("dist").join("index.js"), @@ -255,45 +248,34 @@ impl Prettier { .into_iter() .find(|possible_plugin_path| possible_plugin_path.is_file()) }; - 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 - let mut add_tailwind_back = false; - let mut plugins = plugins - .into_iter() - .filter(|plugin_name| { - if plugin_name.as_ref() - == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME - { - add_tailwind_back = true; - false - } else { - true - } - }) - .map(|plugin_name| { - (plugin_name.clone(), plugin_name_into_path(plugin_name)) - }) - .collect::>(); - if add_tailwind_back { - plugins.push(( - TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME.into(), - plugin_name_into_path( - TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME.into(), - ), - )); + // Tailwind plugin requires being added last + // https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins + let mut add_tailwind_back = false; + + let mut located_plugins = prettier_settings.plugins.iter() + .filter(|plugin_name| { + if plugin_name.as_str() == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME { + add_tailwind_back = true; + false + } else { + true } - (Some(parser.to_string()), plugins) - } - None => (None, Vec::new()), - }; + }) + .map(|plugin_name| { + let plugin_path = plugin_name_into_path(plugin_name); + (plugin_name.clone(), plugin_path) + }) + .collect::>(); + if add_tailwind_back { + located_plugins.push(( + TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME.to_owned(), + plugin_name_into_path(TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME), + )); + } let prettier_options = if self.is_default() { - let language_settings = - language_settings(buffer_language, buffer.file(), cx); - let mut options = language_settings.prettier.clone(); + let mut options = prettier_settings.options.clone(); if !options.contains_key("tabWidth") { options.insert( "tabWidth".to_string(), @@ -321,11 +303,7 @@ impl Prettier { match located_plugin_path { Some(path) => Some(path), None => { - log::error!( - "Have not found plugin path for {:?} inside {:?}", - plugin_name, - prettier_node_modules - ); + log::error!("Have not found plugin path for {plugin_name:?} inside {prettier_node_modules:?}"); None } } @@ -341,7 +319,7 @@ impl Prettier { anyhow::Ok(FormatParams { text: buffer.text(), options: FormatOptions { - parser, + parser: prettier_settings.parser.clone(), plugins, path: buffer_path, prettier_options, @@ -360,9 +338,19 @@ impl Prettier { #[cfg(any(test, feature = "test-support"))] Self::Test(_) => Ok(buffer .update(cx, |buffer, cx| { - let formatted_text = buffer.text() + FORMAT_SUFFIX; - buffer.diff(formatted_text, cx) - })? + match buffer + .language() + .map(|language| language.lsp_id()) + .as_deref() + { + Some("rust") => anyhow::bail!("prettier does not support Rust"), + Some(_other) => { + let formatted_text = buffer.text() + FORMAT_SUFFIX; + Ok(buffer.diff(formatted_text, cx)) + } + None => panic!("Should not format buffer without a language with prettier"), + } + })?? .await), } } diff --git a/crates/project/src/prettier_support.rs b/crates/project/src/prettier_support.rs index c7155e32a0..c81a703a37 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, LanguageServerName, LocalFile, + Buffer, LanguageServerName, LocalFile, }; use lsp::{LanguageServer, LanguageServerId}; use node_runtime::NodeRuntime; @@ -25,20 +25,12 @@ use crate::{ Event, File, FormatOperation, PathChange, Project, ProjectEntryId, Worktree, WorktreeId, }; -pub fn prettier_plugins_for_language<'a>( - language: &'a Arc, +pub fn prettier_plugins_for_language( language_settings: &LanguageSettings, -) -> Option<&'a Vec>> { +) -> Option<&HashSet> { match &language_settings.formatter { - Formatter::Prettier { .. } | Formatter::Auto => {} - Formatter::LanguageServer | Formatter::External { .. } | Formatter::CodeActions(_) => { - return None - } - }; - if language.prettier_parser_name().is_some() { - Some(language.prettier_plugins()) - } else { - None + Formatter::Prettier { .. } | Formatter::Auto => Some(&language_settings.prettier.plugins), + Formatter::LanguageServer | Formatter::External { .. } | Formatter::CodeActions(_) => None, } } @@ -110,6 +102,7 @@ pub struct DefaultPrettier { installed_plugins: HashSet>, } +#[derive(Debug)] pub enum PrettierInstallation { NotInstalled { attempts: usize, @@ -121,7 +114,7 @@ pub enum PrettierInstallation { pub type PrettierTask = Shared, Arc>>>; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct PrettierInstance { attempt: usize, prettier: Option, @@ -295,20 +288,15 @@ fn start_prettier( ) -> PrettierTask { cx.spawn(|project, mut cx| async move { log::info!("Starting prettier at path {prettier_dir:?}"); - let language_registry = project.update(&mut cx, |project, _| project.languages.clone())?; - let new_server_id = language_registry.next_language_server_id(); + let new_server_id = project.update(&mut cx, |project, _| { + project.languages.next_language_server_id() + })?; - let new_prettier = Prettier::start( - new_server_id, - prettier_dir, - node, - language_registry, - cx.clone(), - ) - .await - .context("default prettier spawn") - .map(Arc::new) - .map_err(Arc::new)?; + let new_prettier = Prettier::start(new_server_id, prettier_dir, node, cx.clone()) + .await + .context("default prettier spawn") + .map(Arc::new) + .map_err(Arc::new)?; register_new_prettier(&project, &new_prettier, worktree_id, new_server_id, &mut cx); Ok(new_prettier) }) @@ -526,10 +514,7 @@ impl Project { } let buffer = buffer.read(cx); let buffer_file = buffer.file(); - let Some(buffer_language) = buffer.language() else { - return Task::ready(None); - }; - if buffer_language.prettier_parser_name().is_none() { + if buffer.language().is_none() { return Task::ready(None); } let Some(node) = self.node.clone() else { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 65dae12ff2..ed5ef9d067 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -550,6 +550,7 @@ pub enum FormatTrigger { // Currently, formatting operations are represented differently depending on // whether they come from a language server or an external command. +#[derive(Debug)] enum FormatOperation { Lsp(Vec<(Range, String)>), External(Diff), @@ -1088,11 +1089,8 @@ impl Project { .push((file.worktree.clone(), Arc::clone(language))); } } - language_formatters_to_check.push(( - buffer_file.map(|f| f.worktree_id(cx)), - Arc::clone(language), - settings.clone(), - )); + language_formatters_to_check + .push((buffer_file.map(|f| f.worktree_id(cx)), settings.clone())); } } } @@ -1148,9 +1146,9 @@ impl Project { } let mut prettier_plugins_by_worktree = HashMap::default(); - for (worktree, language, settings) in language_formatters_to_check { + for (worktree, language_settings) in language_formatters_to_check { if let Some(plugins) = - prettier_support::prettier_plugins_for_language(&language, &settings) + prettier_support::prettier_plugins_for_language(&language_settings) { prettier_plugins_by_worktree .entry(worktree) @@ -1159,7 +1157,11 @@ impl Project { } } for (worktree, prettier_plugins) in prettier_plugins_by_worktree { - self.install_default_prettier(worktree, prettier_plugins.into_iter(), cx); + self.install_default_prettier( + worktree, + prettier_plugins.into_iter().map(Arc::from), + cx, + ); } // Start all the newly-enabled language servers. @@ -3070,10 +3072,12 @@ 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(&new_language, &settings) - { - self.install_default_prettier(worktree, prettier_plugins.iter().cloned(), cx); + if let Some(prettier_plugins) = prettier_support::prettier_plugins_for_language(&settings) { + self.install_default_prettier( + worktree, + prettier_plugins.iter().map(|s| Arc::from(s.as_str())), + cx, + ); }; if let Some(file) = buffer_file { let worktree = file.worktree.clone(); @@ -4786,6 +4790,11 @@ impl Project { .zip(buffer_abs_path.as_ref()); let mut format_operation = None; + let prettier_settings = buffer.read_with(&mut cx, |buffer, cx| { + language_settings(buffer.language(), buffer.file(), cx) + .prettier + .clone() + })?; match (&settings.formatter, &settings.format_on_save) { (_, FormatOnSave::Off) if trigger == FormatTrigger::Save => {} @@ -4845,11 +4854,18 @@ impl Project { } } (Formatter::Auto, FormatOnSave::On | FormatOnSave::Off) => { - let prettier = - prettier_support::format_with_prettier(&project, buffer, &mut cx).await; + let prettier = if prettier_settings.allowed { + prettier_support::format_with_prettier(&project, buffer, &mut cx) + .await + .transpose() + .ok() + .flatten() + } else { + None + }; if let Some(operation) = prettier { - format_operation = Some(operation?); + format_operation = Some(operation); } else if let Some((language_server, buffer_abs_path)) = server_and_buffer { format_operation = Some(FormatOperation::Lsp( Self::format_via_lsp( @@ -4866,11 +4882,12 @@ impl Project { } } (Formatter::Prettier, FormatOnSave::On | FormatOnSave::Off) => { - let prettier = - prettier_support::format_with_prettier(&project, buffer, &mut cx).await; - - if let Some(operation) = prettier { - format_operation = Some(operation?); + if prettier_settings.allowed { + if let Some(operation) = + prettier_support::format_with_prettier(&project, buffer, &mut cx).await + { + format_operation = Some(operation?); + } } } };