Make language adapters able to require certain bundled formatters

Kirill Bulatov created

Change summary

assets/settings/default.json             |  2 --
crates/language/src/language.rs          | 24 ++++++++++++++++++++++++
crates/language/src/language_settings.rs |  4 ----
crates/project/src/project.rs            | 21 ++++++++++++++-------
crates/zed/src/languages/css.rs          |  6 +++++-
crates/zed/src/languages/html.rs         |  6 +++++-
crates/zed/src/languages/json.rs         |  8 +++++++-
crates/zed/src/languages/php.rs          |  6 +++++-
crates/zed/src/languages/svelte.rs       |  6 +++++-
crates/zed/src/languages/typescript.rs   | 10 +++++++++-
crates/zed/src/languages/yaml.rs         |  7 ++++++-
11 files changed, 80 insertions(+), 20 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -201,8 +201,6 @@
   //     }
   // TODO kb description
   "formatter": "auto",
-  // TODO kb description + better settings
-  "prettier": true,
   // How to soft-wrap long lines of text. This setting can take
   // three values:
   //

crates/language/src/language.rs 🔗

@@ -227,6 +227,10 @@ impl CachedLspAdapter {
     ) -> Option<CodeLabel> {
         self.adapter.label_for_symbol(name, kind, language).await
     }
+
+    pub fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        self.adapter.enabled_formatters()
+    }
 }
 
 pub trait LspAdapterDelegate: Send + Sync {
@@ -333,6 +337,26 @@ pub trait LspAdapter: 'static + Send + Sync {
     async fn language_ids(&self) -> HashMap<String, String> {
         Default::default()
     }
+
+    // TODO kb enable this for
+    // markdown somehow?
+    // tailwind (needs a css plugin, there are 2 of them)
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        Vec::new()
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum BundledFormatter {
+    Prettier { plugin_names: Vec<String> },
+}
+
+impl BundledFormatter {
+    pub fn prettier() -> Self {
+        Self::Prettier {
+            plugin_names: Vec::new(),
+        }
+    }
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]

crates/language/src/language_settings.rs 🔗

@@ -47,7 +47,6 @@ pub struct LanguageSettings {
     pub show_wrap_guides: bool,
     pub wrap_guides: Vec<usize>,
     pub format_on_save: FormatOnSave,
-    pub prettier: bool,
     pub remove_trailing_whitespace_on_save: bool,
     pub ensure_final_newline_on_save: bool,
     pub formatter: Formatter,
@@ -93,8 +92,6 @@ pub struct LanguageSettingsContent {
     #[serde(default)]
     pub format_on_save: Option<FormatOnSave>,
     #[serde(default)]
-    pub prettier: Option<bool>,
-    #[serde(default)]
     pub remove_trailing_whitespace_on_save: Option<bool>,
     #[serde(default)]
     pub ensure_final_newline_on_save: Option<bool>,
@@ -401,7 +398,6 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
     );
     merge(&mut settings.formatter, src.formatter.clone());
     merge(&mut settings.format_on_save, src.format_on_save.clone());
-    merge(&mut settings.prettier, src.prettier);
     merge(
         &mut settings.remove_trailing_whitespace_on_save,
         src.remove_trailing_whitespace_on_save,

crates/project/src/project.rs 🔗

@@ -37,11 +37,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, 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, 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,
 };
 use log::error;
 use lsp::{
@@ -2651,6 +2651,8 @@ impl Project {
         }
     }
 
+    // TODO kb 2 usages of this method (buffer language select + settings change) should take care of
+    // `LspAdapter::enabled_formatters` collecting and initializing. Remove `Option<WorktreeId>` for prettier instances?
     fn start_language_servers(
         &mut self,
         worktree: &ModelHandle<Worktree>,
@@ -8202,8 +8204,13 @@ impl Project {
     ) -> Option<Task<Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>>> {
         let buffer = buffer.read(cx);
         let buffer_file = buffer.file();
-        let language_settings = language_settings(buffer.language(), buffer_file, cx).clone();
-        if !language_settings.prettier {
+        let buffer_language = buffer.language()?;
+        if !buffer_language
+            .lsp_adapters()
+            .iter()
+            .flat_map(|adapter| adapter.enabled_formatters())
+            .any(|formatter| matches!(formatter, BundledFormatter::Prettier { .. }))
+        {
             return None;
         }
 

crates/zed/src/languages/css.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
 use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
@@ -96,6 +96,10 @@ impl LspAdapter for CssLspAdapter {
             "provideFormatter": true
         }))
     }
+
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        vec![BundledFormatter::prettier()]
+    }
 }
 
 async fn get_cached_server_binary(

crates/zed/src/languages/html.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
 use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
@@ -96,6 +96,10 @@ impl LspAdapter for HtmlLspAdapter {
             "provideFormatter": true
         }))
     }
+
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        vec![BundledFormatter::prettier()]
+    }
 }
 
 async fn get_cached_server_binary(

crates/zed/src/languages/json.rs 🔗

@@ -4,7 +4,9 @@ use collections::HashMap;
 use feature_flags::FeatureFlagAppExt;
 use futures::{future::BoxFuture, FutureExt, StreamExt};
 use gpui::AppContext;
-use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{
+    BundledFormatter, LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate,
+};
 use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
@@ -144,6 +146,10 @@ 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()]
+    }
 }
 
 async fn get_cached_server_binary(

crates/zed/src/languages/php.rs 🔗

@@ -3,7 +3,7 @@ use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use collections::HashMap;
 
-use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
 use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 
@@ -103,6 +103,10 @@ impl LspAdapter for IntelephenseLspAdapter {
     async fn language_ids(&self) -> HashMap<String, String> {
         HashMap::from_iter([("PHP".into(), "php".into())])
     }
+
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        vec![BundledFormatter::prettier()]
+    }
 }
 
 async fn get_cached_server_binary(

crates/zed/src/languages/svelte.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
 use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
@@ -95,6 +95,10 @@ impl LspAdapter for SvelteLspAdapter {
             "provideFormatter": true
         }))
     }
+
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        vec![BundledFormatter::prettier()]
+    }
 }
 
 async fn get_cached_server_binary(

crates/zed/src/languages/typescript.rs 🔗

@@ -4,7 +4,7 @@ use async_tar::Archive;
 use async_trait::async_trait;
 use futures::{future::BoxFuture, FutureExt};
 use gpui::AppContext;
-use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate};
 use lsp::{CodeActionKind, LanguageServerBinary};
 use node_runtime::NodeRuntime;
 use serde_json::{json, Value};
@@ -161,6 +161,10 @@ impl LspAdapter for TypeScriptLspAdapter {
             "provideFormatter": true
         }))
     }
+
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        vec![BundledFormatter::prettier()]
+    }
 }
 
 async fn get_cached_ts_server_binary(
@@ -309,6 +313,10 @@ impl LspAdapter for EsLintLspAdapter {
     async fn initialization_options(&self) -> Option<serde_json::Value> {
         None
     }
+
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        vec![BundledFormatter::prettier()]
+    }
 }
 
 async fn get_cached_eslint_server_binary(

crates/zed/src/languages/yaml.rs 🔗

@@ -3,7 +3,8 @@ use async_trait::async_trait;
 use futures::{future::BoxFuture, FutureExt, StreamExt};
 use gpui::AppContext;
 use language::{
-    language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate,
+    language_settings::all_language_settings, BundledFormatter, LanguageServerName, LspAdapter,
+    LspAdapterDelegate,
 };
 use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
@@ -108,6 +109,10 @@ impl LspAdapter for YamlLspAdapter {
         }))
         .boxed()
     }
+
+    fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+        vec![BundledFormatter::prettier()]
+    }
 }
 
 async fn get_cached_server_binary(