Skip to content

Commit

Permalink
feat(css_formatter): Basic css formatting of rules, selectors, and de…
Browse files Browse the repository at this point in the history
…clarations (#1256)
  • Loading branch information
faultyserver authored Dec 22, 2023
1 parent c6fff38 commit 90e1a83
Show file tree
Hide file tree
Showing 74 changed files with 2,089 additions and 136 deletions.
16 changes: 6 additions & 10 deletions crates/biome_cli/tests/cases/overrides_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ const UNFORMATTED: &str = " statement( ) ";
const UNFORMATTED_JSON: &str = r#"{ "asta": ["lorem", "ipsum", "first", "second"] }"#;
const FORMATTED_JSON: &str =
"{\n \"asta\": [\n \"lorem\",\n \"ipsum\",\n \"first\",\n \"second\"\n ]\n}\n";
// TODO(faulty): re-add when the CSS formatter is available from the CLI.
// const UNFORMATTED_CSS: &str = "html {}";
// const FORMATTED_CSS: &str = "html {\n}\n";
const UNFORMATTED_CSS: &str = "html {}";
const FORMATTED_CSS: &str = "html {\n}\n";

const UNFORMATTED_LINE_WIDTH: &str = r#"const a = ["loreum", "ipsum"]"#;
const FORMATTED: &str = "statement();\n";
Expand Down Expand Up @@ -321,9 +320,8 @@ fn does_include_file_with_different_languages_and_files() {
let json_file = Path::new("test3.json");
fs.insert(json_file.into(), UNFORMATTED_JSON.as_bytes());

// TODO(faulty): re-add when the CSS formatter is available from the CLI.
// let css_file = Path::new("test4.css");
// fs.insert(css_file.into(), UNFORMATTED_CSS.as_bytes());
let css_file = Path::new("test4.css");
fs.insert(css_file.into(), UNFORMATTED_CSS.as_bytes());

let result = run_cli(
DynRef::Borrowed(&mut fs),
Expand All @@ -335,8 +333,7 @@ fn does_include_file_with_different_languages_and_files() {
test.as_os_str().to_str().unwrap(),
test2.as_os_str().to_str().unwrap(),
json_file.as_os_str().to_str().unwrap(),
// TODO(faulty): re-add when the CSS formatter is available from the CLI.
// css_file.as_os_str().to_str().unwrap(),
css_file.as_os_str().to_str().unwrap(),
]
.as_slice(),
),
Expand All @@ -347,8 +344,7 @@ fn does_include_file_with_different_languages_and_files() {
assert_file_contents(&fs, test, FORMATTED_WITH_SINGLE_QUOTES);
assert_file_contents(&fs, test2, FORMATTED_WITH_NO_SEMICOLONS);
assert_file_contents(&fs, json_file, FORMATTED_JSON);
// TODO(faulty): re-add when the CSS formatter is available from the CLI.
// assert_file_contents(&fs, css_file, FORMATTED_CSS);
assert_file_contents(&fs, css_file, FORMATTED_CSS);

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,18 @@ const a = ["loreum", "ipsum"]

```

## `test4.css`

```css
html {
}

```

# Emitted Messages

```block
Formatted 3 file(s) in <TIME>
Formatted 4 file(s) in <TIME>
```


Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ expression: content
## `input.css`

```css
html {}
html {
}

```

## `input.js`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ expression: content
## `input.css`

```css
html {}
html {
}

```

## `input.js`
Expand Down
12 changes: 9 additions & 3 deletions crates/biome_css_formatter/src/css/auxiliary/any_function.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use crate::prelude::*;
use biome_css_syntax::CssAnyFunction;
use biome_rowan::AstNode;
use biome_css_syntax::{CssAnyFunction, CssAnyFunctionFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssAnyFunction;
impl FormatNodeRule<CssAnyFunction> for FormatCssAnyFunction {
// TODO: This is really `AnyCssFunction` and will probably get moved to be that later.
fn fmt_fields(&self, node: &CssAnyFunction, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssAnyFunctionFields {
css_simple_function,
} = node.as_fields();

write!(f, [css_simple_function.format()])
}
}
19 changes: 16 additions & 3 deletions crates/biome_css_formatter/src/css/auxiliary/attribute_matcher.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
use crate::prelude::*;
use biome_css_syntax::CssAttributeMatcher;
use biome_rowan::AstNode;
use biome_css_syntax::{CssAttributeMatcher, CssAttributeMatcherFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssAttributeMatcher;
impl FormatNodeRule<CssAttributeMatcher> for FormatCssAttributeMatcher {
fn fmt_fields(&self, node: &CssAttributeMatcher, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssAttributeMatcherFields {
operator,
value,
modifier,
} = node.as_fields();

write!(f, [operator.format(), value.format()])?;

if modifier.is_some() {
write!(f, [space(), modifier.format()])?;
}

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::prelude::*;
use biome_css_syntax::CssAttributeMatcherValue;
use biome_rowan::AstNode;
use biome_css_syntax::{CssAttributeMatcherValue, CssAttributeMatcherValueFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssAttributeMatcherValue;
impl FormatNodeRule<CssAttributeMatcherValue> for FormatCssAttributeMatcherValue {
Expand All @@ -9,6 +10,8 @@ impl FormatNodeRule<CssAttributeMatcherValue> for FormatCssAttributeMatcherValue
node: &CssAttributeMatcherValue,
f: &mut CssFormatter,
) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssAttributeMatcherValueFields { name } = node.as_fields();

write!(f, [name.format()])
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::prelude::*;
use biome_css_syntax::CssAttributeName;
use biome_rowan::AstNode;
use biome_css_syntax::{CssAttributeName, CssAttributeNameFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssAttributeName;
impl FormatNodeRule<CssAttributeName> for FormatCssAttributeName {
fn fmt_fields(&self, node: &CssAttributeName, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssAttributeNameFields { namespace, name } = node.as_fields();

write!(f, [namespace.format(), name.format()])
}
}
23 changes: 20 additions & 3 deletions crates/biome_css_formatter/src/css/auxiliary/declaration.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
use crate::prelude::*;
use biome_css_syntax::CssDeclaration;
use biome_rowan::AstNode;
use biome_css_syntax::{CssDeclaration, CssDeclarationFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssDeclaration;
impl FormatNodeRule<CssDeclaration> for FormatCssDeclaration {
fn fmt_fields(&self, node: &CssDeclaration, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssDeclarationFields {
name,
colon_token,
value,
important,
} = node.as_fields();

write!(
f,
[name.format(), colon_token.format(), space(), value.format()]
)?;

if important.is_some() {
write!(f, [space(), important.format()])?;
}

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use crate::prelude::*;
use biome_css_syntax::CssDeclarationImportant;
use biome_rowan::AstNode;
use biome_css_syntax::{CssDeclarationImportant, CssDeclarationImportantFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssDeclarationImportant;
impl FormatNodeRule<CssDeclarationImportant> for FormatCssDeclarationImportant {
fn fmt_fields(&self, node: &CssDeclarationImportant, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssDeclarationImportantFields {
excl_token,
important_token,
} = node.as_fields();

write!(f, [excl_token.format(), important_token.format()])
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
use crate::prelude::*;
use biome_css_syntax::CssDeclarationListBlock;
use biome_rowan::AstNode;
use biome_css_syntax::{CssDeclarationListBlock, CssDeclarationListBlockFields};
use biome_formatter::write;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssDeclarationListBlock;
impl FormatNodeRule<CssDeclarationListBlock> for FormatCssDeclarationListBlock {
fn fmt_fields(&self, node: &CssDeclarationListBlock, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssDeclarationListBlockFields {
l_curly_token,
declaration_list,
r_curly_token,
} = node.as_fields();

// When the list is empty, we still print a hard line to put the
// closing curly on the next line.
if declaration_list.is_empty() {
write!(
f,
[
l_curly_token.format(),
hard_line_break(),
r_curly_token.format()
]
)
} else {
write!(
f,
[
l_curly_token.format(),
block_indent(&declaration_list.format()),
r_curly_token.format()
]
)
}
}
}
54 changes: 50 additions & 4 deletions crates/biome_css_formatter/src/css/auxiliary/identifier.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
use std::borrow::Cow;

use crate::prelude::*;
use biome_css_syntax::CssIdentifier;
use biome_rowan::AstNode;
use biome_css_syntax::{CssIdentifier, CssIdentifierFields};
use biome_formatter::{token::string::ToAsciiLowercaseCow, write, FormatRuleWithOptions};

#[derive(Default, Debug)]
pub(crate) struct FormatCssIdentifierOptions {
/// Whether the formatter should rewrite the identifier using lowercase
/// letters.
pub(crate) forced_lowercase: bool,
}

impl FormatRuleWithOptions<CssIdentifier> for FormatCssIdentifier {
type Options = FormatCssIdentifierOptions;

fn with_options(mut self, options: Self::Options) -> Self {
self.forced_lowercase = options.forced_lowercase;
self
}
}

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssIdentifier;
pub(crate) struct FormatCssIdentifier {
forced_lowercase: bool,
}

impl FormatNodeRule<CssIdentifier> for FormatCssIdentifier {
fn fmt_fields(&self, node: &CssIdentifier, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssIdentifierFields { value_token } = node.as_fields();

// The parser uses identifiers to represent a few different things:
// selector names, rule names, values, and also units. For formatting,
// we always want to write units in lowercase, but all of the others
// we want to preserve their casing.
if self.forced_lowercase {
let value_token = value_token?;

let original = value_token.text_trimmed();
match original.to_ascii_lowercase_cow() {
Cow::Borrowed(_) => write![f, [value_token.format()]],
Cow::Owned(lowercase) => {
write!(
f,
[format_replaced(
&value_token,
&dynamic_text(&lowercase, value_token.text_trimmed_range().start())
)]
)
}
}
} else {
write!(f, [value_token.format()])
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::prelude::*;
use biome_css_syntax::CssNamedNamespacePrefix;
use biome_rowan::AstNode;
use biome_css_syntax::{CssNamedNamespacePrefix, CssNamedNamespacePrefixFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssNamedNamespacePrefix;
impl FormatNodeRule<CssNamedNamespacePrefix> for FormatCssNamedNamespacePrefix {
fn fmt_fields(&self, node: &CssNamedNamespacePrefix, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssNamedNamespacePrefixFields { name } = node.as_fields();

write!(f, [name.format()])
}
}
12 changes: 9 additions & 3 deletions crates/biome_css_formatter/src/css/auxiliary/namespace.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use crate::prelude::*;
use biome_css_syntax::CssNamespace;
use biome_rowan::AstNode;
use biome_css_syntax::{CssNamespace, CssNamespaceFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssNamespace;
impl FormatNodeRule<CssNamespace> for FormatCssNamespace {
fn fmt_fields(&self, node: &CssNamespace, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssNamespaceFields {
prefix,
bitwise_or_token,
} = node.as_fields();

write!(f, [prefix.format(), bitwise_or_token.format()])
}
}
11 changes: 8 additions & 3 deletions crates/biome_css_formatter/src/css/auxiliary/parameter.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use crate::prelude::*;
use biome_css_syntax::CssParameter;
use biome_rowan::AstNode;
use biome_css_syntax::{CssParameter, CssParameterFields};
use biome_formatter::write;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssParameter;
impl FormatNodeRule<CssParameter> for FormatCssParameter {
fn fmt_fields(&self, node: &CssParameter, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
let CssParameterFields {
css_component_value_list,
} = node.as_fields();

write!(f, [css_component_value_list.format()])
}
}
Loading

0 comments on commit 90e1a83

Please sign in to comment.