summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock52
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/args/flags.rs25
-rw-r--r--cli/args/mod.rs7
-rw-r--r--cli/lsp/language_server.rs3
-rw-r--r--cli/tools/fmt.rs96
-rw-r--r--tests/integration/fmt_tests.rs18
-rw-r--r--tests/specs/fmt/unstable_css/__test__.jsonc25
-rw-r--r--tests/specs/fmt/unstable_css/badly_formatted.css1
-rw-r--r--tests/testdata/fmt/badly_formatted.css2
-rw-r--r--tests/testdata/fmt/badly_formatted.md6
-rw-r--r--tests/testdata/fmt/badly_formatted_fixed.css3
-rw-r--r--tests/testdata/fmt/badly_formatted_fixed.md6
13 files changed, 235 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock
index aed11d084..6a758ce09 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -456,7 +456,7 @@ dependencies = [
"bitflags 2.5.0",
"cexpr",
"clang-sys",
- "itertools",
+ "itertools 0.10.5",
"lazy_static",
"lazycell",
"log",
@@ -1186,6 +1186,7 @@ dependencies = [
"libz-sys",
"log",
"lsp-types",
+ "malva",
"memmem",
"monch",
"napi_sym",
@@ -1612,7 +1613,7 @@ dependencies = [
"hyper 0.14.28",
"hyper 1.4.1",
"hyper-util",
- "itertools",
+ "itertools 0.10.5",
"memmem",
"mime",
"once_cell",
@@ -3853,6 +3854,15 @@ dependencies = [
]
[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4236,6 +4246,19 @@ dependencies = [
]
[[package]]
+name = "malva"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf982faadf37679302c3fcd8d843d29bf4bb4d323323cffb2f5217ad39578470"
+dependencies = [
+ "aho-corasick",
+ "itertools 0.13.0",
+ "memchr",
+ "raffia",
+ "tiny_pretty",
+]
+
+[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5290,7 +5313,7 @@ checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
dependencies = [
"bytes",
"heck 0.4.1",
- "itertools",
+ "itertools 0.10.5",
"lazy_static",
"log",
"multimap",
@@ -5311,7 +5334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
dependencies = [
"anyhow",
- "itertools",
+ "itertools 0.10.5",
"proc-macro2",
"quote",
"syn 1.0.109",
@@ -5474,6 +5497,27 @@ dependencies = [
]
[[package]]
+name = "raffia"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a26ea01d105c934a90ddd1fca6a343bec14e1a24b72126ee6f364f5a5dd5ec2"
+dependencies = [
+ "raffia_macro",
+ "smallvec",
+]
+
+[[package]]
+name = "raffia_macro"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fdb50eb5bf734fa5a770680a61876a6ec77b99c1e0e52d1f18ad6ebfa85759f"
+dependencies = [
+ "heck 0.5.0",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 0ea7d95ee..df7323e71 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -123,6 +123,7 @@ libc.workspace = true
libz-sys.workspace = true
log = { workspace = true, features = ["serde"] }
lsp-types.workspace = true
+malva = "=0.8.0"
memmem.workspace = true
monch.workspace = true
notify.workspace = true
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index e28ce549b..acdcaf8a7 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -200,6 +200,7 @@ pub struct FmtFlags {
pub prose_wrap: Option<String>,
pub no_semicolons: Option<bool>,
pub watch: Option<WatchFlags>,
+ pub unstable_css: bool,
pub unstable_yaml: bool,
}
@@ -2018,8 +2019,8 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
// prefer using ts for formatting instead of js because ts works in more scenarios
.default_value("ts")
.value_parser([
- "ts", "tsx", "js", "jsx", "md", "json", "jsonc", "yml", "yaml",
- "ipynb",
+ "ts", "tsx", "js", "jsx", "md", "json", "jsonc", "css", "scss",
+ "sass", "less", "yml", "yaml", "ipynb",
]),
)
.arg(
@@ -2097,6 +2098,13 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
),
)
.arg(
+ Arg::new("unstable-css")
+ .long("unstable-css")
+ .help("Enable formatting CSS, SCSS, Sass and Less files.")
+ .value_parser(FalseyValueParser::new())
+ .action(ArgAction::SetTrue),
+ )
+ .arg(
Arg::new("unstable-yaml")
.long("unstable-yaml")
.help("Enable formatting YAML files.")
@@ -4163,6 +4171,7 @@ fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) {
let single_quote = matches.remove_one::<bool>("single-quote");
let prose_wrap = matches.remove_one::<String>("prose-wrap");
let no_semicolons = matches.remove_one::<bool>("no-semicolons");
+ let unstable_css = matches.get_flag("unstable-css");
let unstable_yaml = matches.get_flag("unstable-yaml");
flags.subcommand = DenoSubcommand::Fmt(FmtFlags {
@@ -4175,6 +4184,7 @@ fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) {
prose_wrap,
no_semicolons,
watch: watch_arg_parse(matches),
+ unstable_css,
unstable_yaml,
});
}
@@ -5881,6 +5891,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: false,
unstable_yaml: false,
watch: Default::default(),
}),
@@ -5905,6 +5916,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: false,
unstable_yaml: false,
watch: Default::default(),
}),
@@ -5929,6 +5941,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: false,
unstable_yaml: false,
watch: Default::default(),
}),
@@ -5953,6 +5966,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: false,
unstable_yaml: false,
watch: Some(Default::default()),
}),
@@ -5966,6 +5980,7 @@ mod tests {
"fmt",
"--watch",
"--no-clear-screen",
+ "--unstable-css",
"--unstable-yaml"
]);
assert_eq!(
@@ -5983,6 +5998,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: true,
unstable_yaml: true,
watch: Some(WatchFlags {
hmr: false,
@@ -6018,6 +6034,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: false,
unstable_yaml: false,
watch: Some(Default::default()),
}),
@@ -6042,6 +6059,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: false,
unstable_yaml: false,
watch: Default::default(),
}),
@@ -6074,6 +6092,7 @@ mod tests {
single_quote: None,
prose_wrap: None,
no_semicolons: None,
+ unstable_css: false,
unstable_yaml: false,
watch: Some(Default::default()),
}),
@@ -6111,6 +6130,7 @@ mod tests {
single_quote: Some(true),
prose_wrap: Some("never".to_string()),
no_semicolons: Some(true),
+ unstable_css: false,
unstable_yaml: false,
watch: Default::default(),
}),
@@ -6142,6 +6162,7 @@ mod tests {
single_quote: Some(false),
prose_wrap: None,
no_semicolons: Some(false),
+ unstable_css: false,
unstable_yaml: false,
watch: Default::default(),
}),
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index bd49c0c15..afad0528c 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -281,6 +281,7 @@ impl BenchOptions {
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct UnstableFmtOptions {
+ pub css: bool,
pub yaml: bool,
}
@@ -314,6 +315,7 @@ impl FmtOptions {
Self {
options: resolve_fmt_options(fmt_flags, fmt_config.options),
unstable: UnstableFmtOptions {
+ css: unstable.css || fmt_flags.unstable_css,
yaml: unstable.yaml || fmt_flags.unstable_yaml,
},
files: fmt_config.files,
@@ -1330,8 +1332,10 @@ impl CliOptions {
}
pub fn resolve_config_unstable_fmt_options(&self) -> UnstableFmtOptions {
+ let workspace = self.workspace();
UnstableFmtOptions {
- yaml: self.workspace().has_unstable("fmt-yaml"),
+ css: workspace.has_unstable("fmt-css"),
+ yaml: workspace.has_unstable("fmt-yaml"),
}
}
@@ -1664,6 +1668,7 @@ impl CliOptions {
"sloppy-imports",
"byonm",
"bare-node-builtins",
+ "fmt-css",
"fmt-yaml",
]);
// add more unstable flags to the same vector holding granular flags
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 314c9ec14..186c94198 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -1368,6 +1368,9 @@ impl Inner {
.data_for_specifier(&specifier)
.map(|d| &d.member_dir.workspace);
let unstable_options = UnstableFmtOptions {
+ css: maybe_workspace
+ .map(|w| w.has_unstable("fmt-css"))
+ .unwrap_or(false),
yaml: maybe_workspace
.map(|w| w.has_unstable("fmt-yaml"))
.unwrap_or(false),
diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs
index c4eebddf7..348e2d862 100644
--- a/cli/tools/fmt.rs
+++ b/cli/tools/fmt.rs
@@ -244,6 +244,10 @@ fn format_markdown(
| "typescript"
| "json"
| "jsonc"
+ | "css"
+ | "scss"
+ | "sass"
+ | "less"
| "yml"
| "yaml"
) {
@@ -263,6 +267,13 @@ fn format_markdown(
json_config.line_width = line_width;
dprint_plugin_json::format_text(&fake_filename, text, &json_config)
}
+ "css" | "scss" | "sass" | "less" => {
+ if unstable_options.css {
+ format_css(&fake_filename, text, fmt_options)
+ } else {
+ Ok(None)
+ }
+ }
"yml" | "yaml" => {
if unstable_options.yaml {
pretty_yaml::format_text(
@@ -305,6 +316,20 @@ pub fn format_json(
dprint_plugin_json::format_text(file_path, file_text, &config)
}
+pub fn format_css(
+ file_path: &Path,
+ file_text: &str,
+ fmt_options: &FmtOptionsConfig,
+) -> Result<Option<String>, AnyError> {
+ malva::format_text(
+ file_text,
+ malva::detect_syntax(file_path).unwrap_or(malva::Syntax::Css),
+ &get_resolved_malva_config(fmt_options),
+ )
+ .map(Some)
+ .map_err(AnyError::from)
+}
+
/// Formats a single TS, TSX, JS, JSX, JSONC, JSON, MD, or IPYNB file.
pub fn format_file(
file_path: &Path,
@@ -319,6 +344,13 @@ pub fn format_file(
format_markdown(file_text, fmt_options, unstable_options)
}
"json" | "jsonc" => format_json(file_path, file_text, fmt_options),
+ "css" | "scss" | "sass" | "less" => {
+ if unstable_options.css {
+ format_css(file_path, file_text, fmt_options)
+ } else {
+ Ok(None)
+ }
+ }
"yml" | "yaml" => {
if unstable_options.yaml {
pretty_yaml::format_text(
@@ -737,6 +769,58 @@ fn get_resolved_json_config(
builder.build()
}
+fn get_resolved_malva_config(
+ options: &FmtOptionsConfig,
+) -> malva::config::FormatOptions {
+ use malva::config::*;
+
+ let layout_options = LayoutOptions {
+ print_width: options.line_width.unwrap_or(80) as usize,
+ use_tabs: options.use_tabs.unwrap_or_default(),
+ indent_width: options.indent_width.unwrap_or(2) as usize,
+ line_break: LineBreak::Lf,
+ };
+
+ let language_options = LanguageOptions {
+ hex_case: HexCase::Lower,
+ hex_color_length: None,
+ quotes: if let Some(true) = options.single_quote {
+ Quotes::PreferSingle
+ } else {
+ Quotes::PreferDouble
+ },
+ operator_linebreak: OperatorLineBreak::Before,
+ block_selector_linebreak: BlockSelectorLineBreak::Consistent,
+ omit_number_leading_zero: false,
+ trailing_comma: true,
+ format_comments: false,
+ linebreak_in_pseudo_parens: true,
+ declaration_order: None,
+ single_line_block_threshold: None,
+ keyframe_selector_notation: None,
+ attr_value_quotes: AttrValueQuotes::Always,
+ prefer_single_line: false,
+ selectors_prefer_single_line: None,
+ function_args_prefer_single_line: None,
+ sass_content_at_rule_prefer_single_line: None,
+ sass_include_at_rule_prefer_single_line: None,
+ sass_map_prefer_single_line: None,
+ sass_module_config_prefer_single_line: None,
+ sass_params_prefer_single_line: None,
+ less_import_options_prefer_single_line: None,
+ less_mixin_args_prefer_single_line: None,
+ less_mixin_params_prefer_single_line: None,
+ top_level_declarations_prefer_single_line: None,
+ selector_override_comment_directive: "deno-fmt-selector-override".into(),
+ ignore_comment_directive: "deno-fmt-ignore".into(),
+ };
+
+ FormatOptions {
+ layout: layout_options,
+ language: language_options,
+ }
+}
+
fn get_resolved_yaml_config(
options: &FmtOptionsConfig,
) -> pretty_yaml::config::FormatOptions {
@@ -864,6 +948,10 @@ fn is_supported_ext_fmt(path: &Path) -> bool {
| "mts"
| "json"
| "jsonc"
+ | "css"
+ | "scss"
+ | "sass"
+ | "less"
| "md"
| "mkd"
| "mkdn"
@@ -906,6 +994,14 @@ mod test {
assert!(is_supported_ext_fmt(Path::new("foo.JSONC")));
assert!(is_supported_ext_fmt(Path::new("foo.json")));
assert!(is_supported_ext_fmt(Path::new("foo.JsON")));
+ assert!(is_supported_ext_fmt(Path::new("foo.css")));
+ assert!(is_supported_ext_fmt(Path::new("foo.Css")));
+ assert!(is_supported_ext_fmt(Path::new("foo.scss")));
+ assert!(is_supported_ext_fmt(Path::new("foo.SCSS")));
+ assert!(is_supported_ext_fmt(Path::new("foo.sass")));
+ assert!(is_supported_ext_fmt(Path::new("foo.Sass")));
+ assert!(is_supported_ext_fmt(Path::new("foo.less")));
+ assert!(is_supported_ext_fmt(Path::new("foo.LeSS")));
assert!(is_supported_ext_fmt(Path::new("foo.yml")));
assert!(is_supported_ext_fmt(Path::new("foo.Yml")));
assert!(is_supported_ext_fmt(Path::new("foo.yaml")));
diff --git a/tests/integration/fmt_tests.rs b/tests/integration/fmt_tests.rs
index dab2b2ce4..7a37ec2fc 100644
--- a/tests/integration/fmt_tests.rs
+++ b/tests/integration/fmt_tests.rs
@@ -31,6 +31,12 @@ fn fmt_test() {
let badly_formatted_json = t.path().join("badly_formatted.json");
badly_formatted_original_json.copy(&badly_formatted_json);
+ let fixed_css = testdata_fmt_dir.join("badly_formatted_fixed.css");
+ let badly_formatted_original_css =
+ testdata_fmt_dir.join("badly_formatted.css");
+ let badly_formatted_css = t.path().join("badly_formatted.css");
+ badly_formatted_original_css.copy(&badly_formatted_css);
+
let fixed_ipynb = testdata_fmt_dir.join("badly_formatted_fixed.ipynb");
let badly_formatted_original_ipynb =
testdata_fmt_dir.join("badly_formatted.ipynb");
@@ -49,12 +55,13 @@ fn fmt_test() {
.current_dir(&testdata_fmt_dir)
.args_vec(vec![
"fmt".to_string(),
+ "--unstable-css".to_string(),
"--unstable-yaml".to_string(),
format!(
- "--ignore={badly_formatted_js},{badly_formatted_md},{badly_formatted_json},{badly_formatted_yaml},{badly_formatted_ipynb}",
+ "--ignore={badly_formatted_js},{badly_formatted_md},{badly_formatted_json},{badly_formatted_css},{badly_formatted_yaml},{badly_formatted_ipynb}",
),
format!(
- "--check {badly_formatted_js} {badly_formatted_md} {badly_formatted_json} {badly_formatted_yaml} {badly_formatted_ipynb}",
+ "--check {badly_formatted_js} {badly_formatted_md} {badly_formatted_json} {badly_formatted_css} {badly_formatted_yaml} {badly_formatted_ipynb}",
),
])
.run();
@@ -70,10 +77,12 @@ fn fmt_test() {
.args_vec(vec![
"fmt".to_string(),
"--check".to_string(),
+ "--unstable-css".to_string(),
"--unstable-yaml".to_string(),
badly_formatted_js.to_string(),
badly_formatted_md.to_string(),
badly_formatted_json.to_string(),
+ badly_formatted_css.to_string(),
badly_formatted_yaml.to_string(),
badly_formatted_ipynb.to_string(),
])
@@ -88,10 +97,12 @@ fn fmt_test() {
.current_dir(&testdata_fmt_dir)
.args_vec(vec![
"fmt".to_string(),
+ "--unstable-css".to_string(),
"--unstable-yaml".to_string(),
badly_formatted_js.to_string(),
badly_formatted_md.to_string(),
badly_formatted_json.to_string(),
+ badly_formatted_css.to_string(),
badly_formatted_yaml.to_string(),
badly_formatted_ipynb.to_string(),
])
@@ -103,16 +114,19 @@ fn fmt_test() {
let expected_js = fixed_js.read_to_string();
let expected_md = fixed_md.read_to_string();
let expected_json = fixed_json.read_to_string();
+ let expected_css = fixed_css.read_to_string();
let expected_yaml = fixed_yaml.read_to_string();
let expected_ipynb = fixed_ipynb.read_to_string();
let actual_js = badly_formatted_js.read_to_string();
let actual_md = badly_formatted_md.read_to_string();
let actual_json = badly_formatted_json.read_to_string();
+ let actual_css = badly_formatted_css.read_to_string();
let actual_yaml = badly_formatted_yaml.read_to_string();
let actual_ipynb = badly_formatted_ipynb.read_to_string();
assert_eq!(expected_js, actual_js);
assert_eq!(expected_md, actual_md);
assert_eq!(expected_json, actual_json);
+ assert_eq!(expected_css, actual_css);
assert_eq!(expected_yaml, actual_yaml);
assert_eq!(expected_ipynb, actual_ipynb);
}
diff --git a/tests/specs/fmt/unstable_css/__test__.jsonc b/tests/specs/fmt/unstable_css/__test__.jsonc
new file mode 100644
index 000000000..32259f3ae
--- /dev/null
+++ b/tests/specs/fmt/unstable_css/__test__.jsonc
@@ -0,0 +1,25 @@
+{
+ "tempDir": true,
+ "tests": {
+ "nothing": {
+ "args": "fmt",
+ "output": "Checked 1 file\n"
+ },
+ "flag": {
+ "args": "fmt --unstable-css",
+ "output": "[WILDLINE]badly_formatted.css\nChecked 1 file\n"
+ },
+ "config_file": {
+ "steps": [{
+ "args": [
+ "eval",
+ "Deno.writeTextFile('deno.json', '{\\n \"unstable\": [\"fmt-css\"]\\n}\\n')"
+ ],
+ "output": "[WILDCARD]"
+ }, {
+ "args": "fmt",
+ "output": "[WILDLINE]badly_formatted.css\nChecked 2 files\n"
+ }]
+ }
+ }
+}
diff --git a/tests/specs/fmt/unstable_css/badly_formatted.css b/tests/specs/fmt/unstable_css/badly_formatted.css
new file mode 100644
index 000000000..e57adb796
--- /dev/null
+++ b/tests/specs/fmt/unstable_css/badly_formatted.css
@@ -0,0 +1 @@
+#app>.btn{ color : #000 }
diff --git a/tests/testdata/fmt/badly_formatted.css b/tests/testdata/fmt/badly_formatted.css
new file mode 100644
index 000000000..bfe18b821
--- /dev/null
+++ b/tests/testdata/fmt/badly_formatted.css
@@ -0,0 +1,2 @@
+#app>.btn{ color : #000 }
+
diff --git a/tests/testdata/fmt/badly_formatted.md b/tests/testdata/fmt/badly_formatted.md
index 05a4b2f97..29d73b365 100644
--- a/tests/testdata/fmt/badly_formatted.md
+++ b/tests/testdata/fmt/badly_formatted.md
@@ -32,7 +32,7 @@ function foo(): number {
{
// Comment in JSON
"key": "value",
- "key2":
+ "key2":
"value2",
}
@@ -49,3 +49,7 @@ function foo(): number {
- item1
- item2
```
+
+```css
+#app>.btn{ color : #000 }
+```
diff --git a/tests/testdata/fmt/badly_formatted_fixed.css b/tests/testdata/fmt/badly_formatted_fixed.css
new file mode 100644
index 000000000..1653551f4
--- /dev/null
+++ b/tests/testdata/fmt/badly_formatted_fixed.css
@@ -0,0 +1,3 @@
+#app > .btn {
+ color: #000;
+}
diff --git a/tests/testdata/fmt/badly_formatted_fixed.md b/tests/testdata/fmt/badly_formatted_fixed.md
index 7a7d1913b..db2afc809 100644
--- a/tests/testdata/fmt/badly_formatted_fixed.md
+++ b/tests/testdata/fmt/badly_formatted_fixed.md
@@ -40,3 +40,9 @@ function foo(): number {
- item1
- item2
```
+
+```css
+#app > .btn {
+ color: #000;
+}
+```