settings: Make "auto" and "language_server" valid format steps (#40113)
Follow up for: #39983 and https://github.com/zed-industries/zed/pull/40040#issuecomment-3393902691 Previously it was possible to have formatting done using prettier or language server using `"formatter": "auto"` and specify code actions to apply on format using the `"code_actions_on_format"` setting. However, post #39983 this is no longer possible due to the removal of the `"code_actions_on_format"` setting. To rectify this regression, this PR makes it so that the `"auto"` and `"language_server"` strings that were previously only allowed as top level values on the `"formatter"` key, are now allowed as format steps like so: ```json { "formatter": ["auto", "language_server"] } ``` Therefore to replicate the previous behavior using `"auto"` and `"code_actions_on_format"` you can use the following configuration: ```json { "formatter": [{"code_action": ...}, "auto"] } ``` Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
@@ -1107,22 +1107,28 @@
|
||||
// Whether or not to perform a buffer format before saving: [on, off]
|
||||
// Keep in mind, if the autosave with delay is enabled, format_on_save will be ignored
|
||||
"format_on_save": "on",
|
||||
// How to perform a buffer format. This setting can take 4 values:
|
||||
// How to perform a buffer format. This setting can take multiple values:
|
||||
//
|
||||
// 1. Format code using the current language server:
|
||||
// 1. Default. Format files using Zed's Prettier integration (if applicable),
|
||||
// or falling back to formatting via language server:
|
||||
// "formatter": "auto"
|
||||
// 2. Format code using the current language server:
|
||||
// "formatter": "language_server"
|
||||
// 2. Format code using an external command:
|
||||
// 3. Format code using a specific language server:
|
||||
// "formatter": {"language_server": {"name": "ruff"}}
|
||||
// 4. Format code using an external command:
|
||||
// "formatter": {
|
||||
// "external": {
|
||||
// "command": "prettier",
|
||||
// "arguments": ["--stdin-filepath", "{buffer_path}"]
|
||||
// }
|
||||
// }
|
||||
// 3. Format code using Zed's Prettier integration:
|
||||
// 5. Format code using Zed's Prettier integration:
|
||||
// "formatter": "prettier"
|
||||
// 4. Default. Format files using Zed's Prettier integration (if applicable),
|
||||
// or falling back to formatting via language server:
|
||||
// "formatter": "auto"
|
||||
// 6. Format code using a code action
|
||||
// "formatter": {"code_action": "source.fixAll.eslint"}
|
||||
// 7. An array of any format step specified above to apply in order
|
||||
// "formatter": [{"code_action": "source.fixAll.eslint"}, "prettier"]
|
||||
"formatter": "auto",
|
||||
// How to soft-wrap long lines of text.
|
||||
// Possible values:
|
||||
@@ -1690,7 +1696,7 @@
|
||||
"preferred_line_length": 72
|
||||
},
|
||||
"Go": {
|
||||
"formatter": [{ "code_action": "source.organizeImports" }, { "language_server": {} }],
|
||||
"formatter": [{ "code_action": "source.organizeImports" }, "language_server"],
|
||||
"debuggers": ["Delve"]
|
||||
},
|
||||
"GraphQL": {
|
||||
|
||||
@@ -790,7 +790,7 @@ mod tests {
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.all_languages.defaults.format_on_save = Some(FormatOnSave::On);
|
||||
settings.project.all_languages.defaults.formatter =
|
||||
Some(language::language_settings::SelectedFormatter::Auto);
|
||||
Some(language::language_settings::FormatterList::default());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1538,7 +1538,7 @@ mod tests {
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.all_languages.defaults.format_on_save = Some(FormatOnSave::On);
|
||||
settings.project.all_languages.defaults.formatter =
|
||||
Some(language::language_settings::SelectedFormatter::Auto);
|
||||
Some(language::language_settings::FormatterList::default());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ use gpui::{
|
||||
use language::{
|
||||
Diagnostic, DiagnosticEntry, DiagnosticSourceKind, FakeLspAdapter, Language, LanguageConfig,
|
||||
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
|
||||
language_settings::{Formatter, FormatterList, SelectedFormatter},
|
||||
language_settings::{Formatter, FormatterList},
|
||||
tree_sitter_rust, tree_sitter_typescript,
|
||||
};
|
||||
use lsp::{LanguageServerId, OneOf};
|
||||
@@ -39,7 +39,7 @@ use project::{
|
||||
use prompt_store::PromptBuilder;
|
||||
use rand::prelude::*;
|
||||
use serde_json::json;
|
||||
use settings::{PrettierSettingsContent, SettingsStore};
|
||||
use settings::{LanguageServerFormatterSpecifier, PrettierSettingsContent, SettingsStore};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
env, future, mem,
|
||||
@@ -4610,14 +4610,13 @@ async fn test_formatting_buffer(
|
||||
cx_a.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings(cx, |file| {
|
||||
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
|
||||
FormatterList::Single(Formatter::External {
|
||||
file.project.all_languages.defaults.formatter =
|
||||
Some(FormatterList::Single(Formatter::External {
|
||||
command: "awk".into(),
|
||||
arguments: Some(
|
||||
vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
|
||||
),
|
||||
}),
|
||||
));
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4708,7 +4707,7 @@ async fn test_prettier_formatting_buffer(
|
||||
cx_a.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings(cx, |file| {
|
||||
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
|
||||
file.project.all_languages.defaults.formatter = Some(FormatterList::default());
|
||||
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
|
||||
allowed: Some(true),
|
||||
..Default::default()
|
||||
@@ -4719,8 +4718,8 @@ async fn test_prettier_formatting_buffer(
|
||||
cx_b.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings(cx, |file| {
|
||||
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
|
||||
FormatterList::Single(Formatter::LanguageServer { name: None }),
|
||||
file.project.all_languages.defaults.formatter = Some(FormatterList::Single(
|
||||
Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
|
||||
));
|
||||
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
|
||||
allowed: Some(true),
|
||||
|
||||
@@ -14,7 +14,7 @@ use gpui::{
|
||||
use http_client::BlockedHttpClient;
|
||||
use language::{
|
||||
FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageRegistry,
|
||||
language_settings::{Formatter, FormatterList, SelectedFormatter, language_settings},
|
||||
language_settings::{Formatter, FormatterList, language_settings},
|
||||
tree_sitter_typescript,
|
||||
};
|
||||
use node_runtime::NodeRuntime;
|
||||
@@ -27,7 +27,7 @@ use remote::RemoteClient;
|
||||
use remote_server::{HeadlessAppState, HeadlessProject};
|
||||
use rpc::proto;
|
||||
use serde_json::json;
|
||||
use settings::{PrettierSettingsContent, SettingsStore};
|
||||
use settings::{LanguageServerFormatterSpecifier, PrettierSettingsContent, SettingsStore};
|
||||
use std::{
|
||||
path::Path,
|
||||
sync::{Arc, atomic::AtomicUsize},
|
||||
@@ -491,7 +491,7 @@ async fn test_ssh_collaboration_formatting_with_prettier(
|
||||
cx_a.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings(cx, |file| {
|
||||
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
|
||||
file.project.all_languages.defaults.formatter = Some(FormatterList::default());
|
||||
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
|
||||
allowed: Some(true),
|
||||
..Default::default()
|
||||
@@ -502,8 +502,8 @@ async fn test_ssh_collaboration_formatting_with_prettier(
|
||||
cx_b.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings(cx, |file| {
|
||||
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::List(
|
||||
FormatterList::Single(Formatter::LanguageServer { name: None }),
|
||||
file.project.all_languages.defaults.formatter = Some(FormatterList::Single(
|
||||
Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
|
||||
));
|
||||
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
|
||||
allowed: Some(true),
|
||||
@@ -550,7 +550,7 @@ async fn test_ssh_collaboration_formatting_with_prettier(
|
||||
cx_a.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings(cx, |file| {
|
||||
file.project.all_languages.defaults.formatter = Some(SelectedFormatter::Auto);
|
||||
file.project.all_languages.defaults.formatter = Some(FormatterList::default());
|
||||
file.project.all_languages.defaults.prettier = Some(PrettierSettingsContent {
|
||||
allowed: Some(true),
|
||||
..Default::default()
|
||||
|
||||
@@ -27,7 +27,6 @@ use language::{
|
||||
LanguageConfigOverride, LanguageMatcher, LanguageName, Override, Point,
|
||||
language_settings::{
|
||||
CompletionSettingsContent, FormatterList, LanguageSettingsContent, LspInsertMode,
|
||||
SelectedFormatter,
|
||||
},
|
||||
tree_sitter_python,
|
||||
};
|
||||
@@ -11908,8 +11907,8 @@ async fn test_range_format_respects_language_tab_size_override(cx: &mut TestAppC
|
||||
#[gpui::test]
|
||||
async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
|
||||
Formatter::LanguageServer { name: None },
|
||||
settings.defaults.formatter = Some(FormatterList::Single(Formatter::LanguageServer(
|
||||
settings::LanguageServerFormatterSpecifier::Current,
|
||||
)))
|
||||
});
|
||||
|
||||
@@ -12034,11 +12033,11 @@ async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
|
||||
async fn test_multiple_formatters(cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.remove_trailing_whitespace_on_save = Some(true);
|
||||
settings.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Vec(vec![
|
||||
Formatter::LanguageServer { name: None },
|
||||
settings.defaults.formatter = Some(FormatterList::Vec(vec![
|
||||
Formatter::LanguageServer(settings::LanguageServerFormatterSpecifier::Current),
|
||||
Formatter::CodeAction("code-action-1".into()),
|
||||
Formatter::CodeAction("code-action-2".into()),
|
||||
])))
|
||||
]))
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
@@ -12293,9 +12292,9 @@ async fn test_multiple_formatters(cx: &mut TestAppContext) {
|
||||
#[gpui::test]
|
||||
async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Vec(vec![
|
||||
Formatter::LanguageServer { name: None },
|
||||
])))
|
||||
settings.defaults.formatter = Some(FormatterList::Vec(vec![Formatter::LanguageServer(
|
||||
settings::LanguageServerFormatterSpecifier::Current,
|
||||
)]))
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
@@ -12498,7 +12497,7 @@ async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
|
||||
#[gpui::test]
|
||||
async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.formatter = Some(SelectedFormatter::Auto)
|
||||
settings.defaults.formatter = Some(FormatterList::default())
|
||||
});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
@@ -18187,9 +18186,7 @@ fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
|
||||
#[gpui::test]
|
||||
async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.formatter = Some(SelectedFormatter::List(FormatterList::Single(
|
||||
Formatter::Prettier,
|
||||
)))
|
||||
settings.defaults.formatter = Some(FormatterList::Single(Formatter::Prettier))
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
@@ -18256,7 +18253,7 @@ async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
|
||||
);
|
||||
|
||||
update_test_language_settings(cx, |settings| {
|
||||
settings.defaults.formatter = Some(SelectedFormatter::Auto)
|
||||
settings.defaults.formatter = Some(FormatterList::default())
|
||||
});
|
||||
let format = editor.update_in(cx, |editor, window, cx| {
|
||||
editor.perform_format(
|
||||
|
||||
@@ -13,7 +13,7 @@ use itertools::{Either, Itertools};
|
||||
pub use settings::{
|
||||
CompletionSettingsContent, EditPredictionProvider, EditPredictionsMode, FormatOnSave,
|
||||
Formatter, FormatterList, InlayHintKind, LanguageSettingsContent, LspInsertMode,
|
||||
RewrapBehavior, SelectedFormatter, ShowWhitespaceSetting, SoftWrap, WordsCompletionMode,
|
||||
RewrapBehavior, ShowWhitespaceSetting, SoftWrap, WordsCompletionMode,
|
||||
};
|
||||
use settings::{ExtendingVec, Settings, SettingsContent, SettingsLocation, SettingsStore};
|
||||
use shellexpand;
|
||||
@@ -96,7 +96,7 @@ pub struct LanguageSettings {
|
||||
/// when saving it.
|
||||
pub ensure_final_newline_on_save: bool,
|
||||
/// How to perform a buffer format.
|
||||
pub formatter: settings::SelectedFormatter,
|
||||
pub formatter: settings::FormatterList,
|
||||
/// Zed's Prettier integration settings.
|
||||
pub prettier: PrettierSettings,
|
||||
/// Whether to automatically close JSX tags.
|
||||
|
||||
@@ -61,9 +61,7 @@ use language::{
|
||||
LanguageRegistry, LocalFile, LspAdapter, LspAdapterDelegate, LspInstaller, ManifestDelegate,
|
||||
ManifestName, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Toolchain,
|
||||
Transaction, Unclipped,
|
||||
language_settings::{
|
||||
FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, language_settings,
|
||||
},
|
||||
language_settings::{FormatOnSave, Formatter, LanguageSettings, language_settings},
|
||||
point_to_lsp,
|
||||
proto::{
|
||||
deserialize_anchor, deserialize_lsp_edit, deserialize_version, serialize_anchor,
|
||||
@@ -1338,23 +1336,24 @@ impl LocalLspStore {
|
||||
let formatters = match (trigger, &settings.format_on_save) {
|
||||
(FormatTrigger::Save, FormatOnSave::Off) => &[],
|
||||
(FormatTrigger::Manual, _) | (FormatTrigger::Save, FormatOnSave::On) => {
|
||||
match &settings.formatter {
|
||||
SelectedFormatter::Auto => {
|
||||
if settings.prettier.allowed {
|
||||
zlog::trace!(logger => "Formatter set to auto: defaulting to prettier");
|
||||
std::slice::from_ref(&Formatter::Prettier)
|
||||
} else {
|
||||
zlog::trace!(logger => "Formatter set to auto: defaulting to primary language server");
|
||||
std::slice::from_ref(&Formatter::LanguageServer { name: None })
|
||||
}
|
||||
}
|
||||
SelectedFormatter::List(formatter_list) => formatter_list.as_ref(),
|
||||
}
|
||||
settings.formatter.as_ref()
|
||||
}
|
||||
};
|
||||
|
||||
for formatter in formatters {
|
||||
let formatter = if formatter == &Formatter::Auto {
|
||||
if settings.prettier.allowed {
|
||||
zlog::trace!(logger => "Formatter set to auto: defaulting to prettier");
|
||||
&Formatter::Prettier
|
||||
} else {
|
||||
zlog::trace!(logger => "Formatter set to auto: defaulting to primary language server");
|
||||
&Formatter::LanguageServer(settings::LanguageServerFormatterSpecifier::Current)
|
||||
}
|
||||
} else {
|
||||
formatter
|
||||
};
|
||||
match formatter {
|
||||
Formatter::Auto => unreachable!("Auto resolved above"),
|
||||
Formatter::Prettier => {
|
||||
let logger = zlog::scoped!(logger => "prettier");
|
||||
zlog::trace!(logger => "formatting");
|
||||
@@ -1409,7 +1408,7 @@ impl LocalLspStore {
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Formatter::LanguageServer { name } => {
|
||||
Formatter::LanguageServer(specifier) => {
|
||||
let logger = zlog::scoped!(logger => "language-server");
|
||||
zlog::trace!(logger => "formatting");
|
||||
let _timer = zlog::time!(logger => "Formatting buffer using language server");
|
||||
@@ -1419,16 +1418,19 @@ impl LocalLspStore {
|
||||
continue;
|
||||
};
|
||||
|
||||
let language_server = if let Some(name) = name.as_deref() {
|
||||
adapters_and_servers.iter().find_map(|(adapter, server)| {
|
||||
if adapter.name.0.as_ref() == name {
|
||||
Some(server.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
adapters_and_servers.first().map(|e| e.1.clone())
|
||||
let language_server = match specifier {
|
||||
settings::LanguageServerFormatterSpecifier::Specific { name } => {
|
||||
adapters_and_servers.iter().find_map(|(adapter, server)| {
|
||||
if adapter.name.0.as_ref() == name {
|
||||
Some(server.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
settings::LanguageServerFormatterSpecifier::Current => {
|
||||
adapters_and_servers.first().map(|e| e.1.clone())
|
||||
}
|
||||
};
|
||||
|
||||
let Some(language_server) = language_server else {
|
||||
|
||||
@@ -16,7 +16,7 @@ use futures::{
|
||||
use gpui::{AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};
|
||||
use language::{
|
||||
Buffer, LanguageRegistry, LocalFile,
|
||||
language_settings::{Formatter, LanguageSettings, SelectedFormatter},
|
||||
language_settings::{Formatter, LanguageSettings},
|
||||
};
|
||||
use lsp::{LanguageServer, LanguageServerId, LanguageServerName};
|
||||
use node_runtime::NodeRuntime;
|
||||
@@ -700,14 +700,11 @@ impl PrettierStore {
|
||||
pub fn prettier_plugins_for_language(
|
||||
language_settings: &LanguageSettings,
|
||||
) -> Option<&HashSet<String>> {
|
||||
match &language_settings.formatter {
|
||||
SelectedFormatter::Auto => Some(&language_settings.prettier.plugins),
|
||||
|
||||
SelectedFormatter::List(list) => list
|
||||
.as_ref()
|
||||
.contains(&Formatter::Prettier)
|
||||
.then_some(&language_settings.prettier.plugins),
|
||||
let formatters = language_settings.formatter.as_ref();
|
||||
if formatters.contains(&Formatter::Prettier) || formatters.contains(&Formatter::Auto) {
|
||||
return Some(&language_settings.prettier.plugins);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) async fn format_with_prettier(
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use std::{borrow::Cow, num::NonZeroU32};
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use collections::{HashMap, HashSet};
|
||||
use gpui::{Modifiers, SharedString};
|
||||
use schemars::{JsonSchema, json_schema};
|
||||
use serde::{
|
||||
Deserialize, Deserializer, Serialize,
|
||||
de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor},
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
use std::sync::Arc;
|
||||
@@ -246,7 +243,7 @@ pub struct LanguageSettingsContent {
|
||||
/// How to perform a buffer format.
|
||||
///
|
||||
/// Default: auto
|
||||
pub formatter: Option<SelectedFormatter>,
|
||||
pub formatter: Option<FormatterList>,
|
||||
/// Zed's Prettier integration settings.
|
||||
/// Allows to enable/disable formatting with Prettier
|
||||
/// and configure default Prettier, used when no project-level Prettier installation is found.
|
||||
@@ -639,102 +636,6 @@ pub enum FormatOnSave {
|
||||
Off,
|
||||
}
|
||||
|
||||
/// Controls which formatter should be used when formatting code.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, MergeFrom)]
|
||||
pub enum SelectedFormatter {
|
||||
/// Format files using Zed's Prettier integration (if applicable),
|
||||
/// or falling back to formatting via language server.
|
||||
#[default]
|
||||
Auto,
|
||||
List(FormatterList),
|
||||
}
|
||||
|
||||
impl JsonSchema for SelectedFormatter {
|
||||
fn schema_name() -> Cow<'static, str> {
|
||||
"Formatter".into()
|
||||
}
|
||||
|
||||
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
|
||||
let formatter_schema = Formatter::json_schema(generator);
|
||||
|
||||
json_schema!({
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": formatter_schema
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["auto", "language_server"]
|
||||
},
|
||||
formatter_schema
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SelectedFormatter {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
SelectedFormatter::Auto => serializer.serialize_str("auto"),
|
||||
SelectedFormatter::List(list) => list.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SelectedFormatter {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct FormatDeserializer;
|
||||
|
||||
impl<'d> Visitor<'d> for FormatDeserializer {
|
||||
type Value = SelectedFormatter;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a valid formatter kind")
|
||||
}
|
||||
fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v == "auto" {
|
||||
Ok(Self::Value::Auto)
|
||||
} else if v == "language_server" {
|
||||
Ok(Self::Value::List(FormatterList::Single(
|
||||
Formatter::LanguageServer { name: None },
|
||||
)))
|
||||
} else {
|
||||
let ret: Result<FormatterList, _> =
|
||||
Deserialize::deserialize(v.into_deserializer());
|
||||
ret.map(SelectedFormatter::List)
|
||||
}
|
||||
}
|
||||
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'d>,
|
||||
{
|
||||
let ret: Result<FormatterList, _> =
|
||||
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
|
||||
ret.map(SelectedFormatter::List)
|
||||
}
|
||||
fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'d>,
|
||||
{
|
||||
let ret: Result<FormatterList, _> =
|
||||
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
|
||||
ret.map(SelectedFormatter::List)
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_any(FormatDeserializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls which formatters should be used when formatting code.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(untagged)]
|
||||
@@ -762,10 +663,11 @@ impl AsRef<[Formatter]> for FormatterList {
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Formatter {
|
||||
/// Format code using the current language server.
|
||||
LanguageServer { name: Option<String> },
|
||||
/// Format code using Zed's Prettier integration.
|
||||
/// Format files using Zed's Prettier integration (if applicable),
|
||||
/// or falling back to formatting via language server.
|
||||
#[default]
|
||||
Auto,
|
||||
/// Format code using Zed's Prettier integration.
|
||||
Prettier,
|
||||
/// Format code using an external command.
|
||||
External {
|
||||
@@ -776,6 +678,73 @@ pub enum Formatter {
|
||||
},
|
||||
/// Files should be formatted using a code action executed by language servers.
|
||||
CodeAction(String),
|
||||
/// Format code using a language server.
|
||||
#[serde(untagged)]
|
||||
LanguageServer(LanguageServerFormatterSpecifier),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(
|
||||
rename_all = "snake_case",
|
||||
// allow specifying language servers as "language_server" or {"language_server": {"name": ...}}
|
||||
from = "LanguageServerVariantContent",
|
||||
into = "LanguageServerVariantContent"
|
||||
)]
|
||||
pub enum LanguageServerFormatterSpecifier {
|
||||
Specific {
|
||||
name: String,
|
||||
},
|
||||
#[default]
|
||||
Current,
|
||||
}
|
||||
|
||||
impl From<LanguageServerVariantContent> for LanguageServerFormatterSpecifier {
|
||||
fn from(value: LanguageServerVariantContent) -> Self {
|
||||
match value {
|
||||
LanguageServerVariantContent::Specific {
|
||||
language_server: LanguageServerSpecifierContent { name: Some(name) },
|
||||
} => Self::Specific { name },
|
||||
_ => Self::Current,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LanguageServerFormatterSpecifier> for LanguageServerVariantContent {
|
||||
fn from(value: LanguageServerFormatterSpecifier) -> Self {
|
||||
match value {
|
||||
LanguageServerFormatterSpecifier::Specific { name } => Self::Specific {
|
||||
language_server: LanguageServerSpecifierContent { name: Some(name) },
|
||||
},
|
||||
LanguageServerFormatterSpecifier::Current => {
|
||||
Self::Current(CurrentLanguageServerContent::LanguageServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case", untagged)]
|
||||
enum LanguageServerVariantContent {
|
||||
/// Format code using a specific language server.
|
||||
Specific {
|
||||
language_server: LanguageServerSpecifierContent,
|
||||
},
|
||||
/// Format code using the current language server.
|
||||
Current(CurrentLanguageServerContent),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum CurrentLanguageServerContent {
|
||||
#[default]
|
||||
LanguageServer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
struct LanguageServerSpecifierContent {
|
||||
/// The name of the language server to format with
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
/// The settings for indent guides.
|
||||
@@ -884,31 +853,53 @@ mod test {
|
||||
fn test_formatter_deserialization() {
|
||||
let raw_auto = "{\"formatter\": \"auto\"}";
|
||||
let settings: LanguageSettingsContent = serde_json::from_str(raw_auto).unwrap();
|
||||
assert_eq!(settings.formatter, Some(SelectedFormatter::Auto));
|
||||
assert_eq!(
|
||||
settings.formatter,
|
||||
Some(FormatterList::Single(Formatter::Auto))
|
||||
);
|
||||
let raw = "{\"formatter\": \"language_server\"}";
|
||||
let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
|
||||
assert_eq!(
|
||||
settings.formatter,
|
||||
Some(SelectedFormatter::List(FormatterList::Single(
|
||||
Formatter::LanguageServer { name: None }
|
||||
Some(FormatterList::Single(Formatter::LanguageServer(
|
||||
LanguageServerFormatterSpecifier::Current
|
||||
)))
|
||||
);
|
||||
|
||||
let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}]}";
|
||||
let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
|
||||
assert_eq!(
|
||||
settings.formatter,
|
||||
Some(SelectedFormatter::List(FormatterList::Vec(vec![
|
||||
Formatter::LanguageServer { name: None }
|
||||
])))
|
||||
Some(FormatterList::Vec(vec![Formatter::LanguageServer(
|
||||
LanguageServerFormatterSpecifier::Current
|
||||
)]))
|
||||
);
|
||||
let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"prettier\"]}";
|
||||
let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"language_server\", \"prettier\"]}";
|
||||
let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
|
||||
assert_eq!(
|
||||
settings.formatter,
|
||||
Some(SelectedFormatter::List(FormatterList::Vec(vec![
|
||||
Formatter::LanguageServer { name: None },
|
||||
Some(FormatterList::Vec(vec![
|
||||
Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
|
||||
Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
|
||||
Formatter::Prettier
|
||||
])))
|
||||
]))
|
||||
);
|
||||
|
||||
let raw = "{\"formatter\": [{\"language_server\": {\"name\": \"ruff\"}}, \"prettier\"]}";
|
||||
let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
|
||||
assert_eq!(
|
||||
settings.formatter,
|
||||
Some(FormatterList::Vec(vec![
|
||||
Formatter::LanguageServer(LanguageServerFormatterSpecifier::Specific {
|
||||
name: "ruff".to_string()
|
||||
}),
|
||||
Formatter::Prettier
|
||||
]))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_string(&LanguageServerFormatterSpecifier::Current).unwrap(),
|
||||
"\"language_server\"",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user