editor: Add an option to disable the formatter (#48991)

Xin Zhao created

Related issue #48600, following #48904

Setting `"formatter": null` does not actually disable the formatter, as
it is functionally equivalent to leaving the setting undefined. In this
case, Zed falls back to the default behavior—either "auto" or a specific
language server, depending on the language.

This PR adds a "none" option to the formatter setting, allowing users to
explicitly disable formatting while keeping `code_actions_on_format`
enabled separately.
```json
"formatter": "none"
```

I still have some doubts about the necessity of this setting itself, but
if it is a desired addition, I will update the documentation
accordingly. If not, please feel free to close this PR.

- [x] Tests or screenshots needed?
- [x] Code Reviewed
- [x] Manual QA

Release Notes:

- Added `"formatter": "none"` in settings to explicitly disable the
formatter.

Change summary

crates/project/src/lsp_store.rs         | 4 ++++
crates/settings_content/src/language.rs | 8 ++++++++
docs/src/reference/all-settings.md      | 8 ++++++++
3 files changed, 20 insertions(+)

Detailed changes

crates/project/src/lsp_store.rs 🔗

@@ -1703,6 +1703,10 @@ impl LocalLspStore {
                 formatter
             };
             match formatter {
+                Formatter::None => {
+                    zlog::trace!(logger => "skipping formatter 'none'");
+                    continue;
+                }
                 Formatter::Auto => unreachable!("Auto resolved above"),
                 Formatter::Prettier => {
                     let logger = zlog::scoped!(logger => "prettier");

crates/settings_content/src/language.rs 🔗

@@ -955,6 +955,8 @@ pub enum Formatter {
     /// or falling back to formatting via language server.
     #[default]
     Auto,
+    /// Do not format code.
+    None,
     /// Format code using Zed's Prettier integration.
     Prettier,
     /// Format code using an external command.
@@ -1148,6 +1150,12 @@ mod test {
             settings.formatter,
             Some(FormatterList::Single(Formatter::Auto))
         );
+        let raw_none = "{\"formatter\": \"none\"}";
+        let settings: LanguageSettingsContent = serde_json::from_str(raw_none).unwrap();
+        assert_eq!(
+            settings.formatter,
+            Some(FormatterList::Single(Formatter::None))
+        );
         let raw = "{\"formatter\": \"language_server\"}";
         let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
         assert_eq!(

docs/src/reference/all-settings.md 🔗

@@ -1908,6 +1908,14 @@ WARNING: `{buffer_path}` should not be used to direct your formatter to read fro
 Here `rust-analyzer` will be used first to format the code, followed by a call of sed.
 If any of the formatters fails, the subsequent ones will still be executed.
 
+6. To disable the formatter, use `"none"`. This setting disables the configured formatter, but any actions in `code_actions_on_format` will still be executed:
+
+```json [settings]
+{
+  "formatter": "none"
+}
+```
+
 ## Auto close
 
 - Description: Whether to automatically add matching closing characters when typing opening parenthesis, bracket, brace, single or double quote characters.