Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/biome_css_formatter/src/scss/any/expression_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ impl FormatRule<AnyScssExpressionItem> for FormatAnyScssExpressionItem {
fn fmt(&self, node: &AnyScssExpressionItem, f: &mut CssFormatter) -> FormatResult<()> {
match node {
AnyScssExpressionItem::AnyCssValue(node) => node.format().fmt(f),
AnyScssExpressionItem::CssDeclarationImportant(node) => node.format().fmt(f),
AnyScssExpressionItem::CssGenericDelimiter(node) => node.format().fmt(f),
AnyScssExpressionItem::ScssArbitraryArgument(node) => node.format().fmt(f),
AnyScssExpressionItem::ScssBinaryExpression(node) => node.format().fmt(f),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Trailing newline: true

```scss
$long-list: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11;
$empty-elements: 1,, 3;
$empty-elements: 1, , 3;
$nested-map: (
a: (
b: 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
source: crates/biome_formatter_test/src/snapshot_builder.rs
assertion_line: 212
info: css/scss/expression/formatting.scss
---

Expand Down
6 changes: 3 additions & 3 deletions crates/biome_css_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use biome_css_factory::CssSyntaxFactory;
use biome_css_syntax::{AnyCssRoot, CssFileSource, CssLanguage, CssSyntaxNode};
pub use biome_parser::prelude::*;
use biome_parser::{AnyParse, EmbeddedNodeParse, NodeParse};
use biome_rowan::{AstNode, NodeCache, SyntaxNodeWithOffset};
use biome_rowan::{AstNode, NodeCache, SyntaxNodeWithOffset, TextSize};
pub use parser::{CssModulesKind, CssParserOptions};

mod lexer;
Expand All @@ -22,7 +22,7 @@ pub(crate) type CssLosslessTreeSink<'source> =
LosslessTreeSink<'source, CssLanguage, CssSyntaxFactory>;

pub(crate) type CssOffsetLosslessTreeSink<'source> =
biome_parser::tree_sink::OffsetLosslessTreeSink<'source, CssLanguage, CssSyntaxFactory>;
OffsetLosslessTreeSink<'source, CssLanguage, CssSyntaxFactory>;

pub fn parse_css(source: &str, source_type: CssFileSource, options: CssParserOptions) -> CssParse {
let mut cache = NodeCache::default();
Expand Down Expand Up @@ -166,7 +166,7 @@ impl CssOffsetParse {
}

/// Get the base offset applied to this parse result
pub fn base_offset(&self) -> biome_rowan::TextSize {
pub fn base_offset(&self) -> TextSize {
self.root.base_offset()
}

Expand Down
4 changes: 2 additions & 2 deletions crates/biome_css_parser/src/syntax/at_rule/tailwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::syntax::{is_at_identifier, parse_identifier, parse_regular_identifier
use biome_css_syntax::CssSyntaxKind::{self, *};
use biome_css_syntax::T;
use biome_parser::parse_lists::{ParseNodeList, ParseSeparatedList};
use biome_parser::parse_recovery::ParseRecoveryTokenSet;
use biome_parser::parse_recovery::{ParseRecoveryTokenSet, RecoveryResult};
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::prelude::*;
Expand Down Expand Up @@ -207,7 +207,7 @@ impl ParseNodeList for ApplyClassList {
&mut self,
p: &mut Self::Parser<'_>,
parsed_element: ParsedSyntax,
) -> biome_parser::parse_recovery::RecoveryResult {
) -> RecoveryResult {
parsed_element.or_recover_with_token_set(
p,
&ParseRecoveryTokenSet::new(CSS_BOGUS_CUSTOM_IDENTIFIER, TW_APPLY_CLASS_LIST_END)
Expand Down
14 changes: 0 additions & 14 deletions crates/biome_css_parser/src/syntax/parse_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,20 +229,6 @@ pub(crate) fn expected_component_value(p: &CssParser, range: TextRange) -> Parse
.into_diagnostic(p)
}

pub(crate) fn expected_scss_expression(p: &CssParser, range: TextRange) -> ParseDiagnostic {
expected_node("SCSS expression", range, p)
}

pub(crate) fn scss_ellipsis_not_allowed(p: &CssParser, range: TextRange) -> ParseDiagnostic {
p.err_builder(
"SCSS arbitrary arguments (`...`) are only allowed in function call arguments.",
range,
)
.with_hint(markup! {
"Use `...` only for function arguments, for example `fn($args...)`."
})
}

pub(crate) fn expected_declaration(p: &CssParser, range: TextRange) -> ParseDiagnostic {
expected_node("<declaration>", range, p)
}
Expand Down
1 change: 1 addition & 0 deletions crates/biome_css_parser/src/syntax/scss/declaration/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod nesting;
mod variable;
mod variable_modifier;

pub(crate) use nesting::{is_at_scss_nesting_declaration, parse_scss_nesting_declaration};
pub(crate) use variable::{is_at_scss_declaration, parse_scss_declaration};
11 changes: 5 additions & 6 deletions crates/biome_css_parser/src/syntax/scss/declaration/nesting.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
use crate::parser::CssParser;
use crate::syntax::block::parse_declaration_or_rule_list_block;
use crate::syntax::parse_error::expected_component_value;
use crate::syntax::scss::parse_scss_expression_allow_empty_value_until;
use crate::syntax::scss::{
SCSS_NESTING_VALUE_END_SET, parse_scss_expression_allow_empty_value_until,
};
use crate::syntax::{
CssSyntaxFeatures, is_at_dashed_identifier, is_at_identifier, parse_regular_identifier,
};
use biome_css_syntax::CssSyntaxKind::{
CSS_DECLARATION, CSS_DECLARATION_IMPORTANT, CSS_DECLARATION_WITH_SEMICOLON,
CSS_GENERIC_PROPERTY, EOF, SCSS_NESTING_DECLARATION,
};
use biome_css_syntax::{CssSyntaxKind, T};
use biome_css_syntax::T;
use biome_parser::prelude::ParsedSyntax;
use biome_parser::prelude::ParsedSyntax::{Absent, Present};
use biome_parser::{CompletedMarker, Parser, SyntaxFeature, TokenSet, token_set};

const SCSS_NESTING_VALUE_END_SET: TokenSet<CssSyntaxKind> =
token_set![T!['{'], T![;], T!['}'], T![!], EOF];
use biome_parser::{CompletedMarker, Parser, SyntaxFeature};

/// Detects nested property syntax (`prop: { ... }`) while excluding custom properties
/// and CSS Modules declarations that must remain regular properties.
Expand Down
148 changes: 10 additions & 138 deletions crates/biome_css_parser/src/syntax/scss/declaration/variable.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
use super::super::{is_at_scss_identifier, parse_scss_identifier};
use crate::parser::CssParser;
use crate::syntax::parse_error::expected_scss_expression;
use crate::syntax::scss::parse_scss_expression_until;
use crate::syntax::{is_nth_at_identifier, parse_regular_identifier};
use biome_css_syntax::CssSyntaxKind::{
EOF, ERROR_TOKEN, SCSS_DECLARATION, SCSS_NAMESPACED_IDENTIFIER, SCSS_VARIABLE_MODIFIER,
SCSS_VARIABLE_MODIFIER_LIST,
use super::super::{
is_at_scss_identifier, is_at_scss_namespaced_identifier, parse_scss_identifier,
parse_scss_namespaced_identifier,
};
use biome_css_syntax::{CssSyntaxKind, T, TextRange};
use biome_parser::diagnostic::{ParseDiagnostic, expected_token_any};
use biome_parser::parse_lists::ParseNodeList;
use biome_parser::parse_recovery::{RecoveryError, RecoveryResult};
use super::variable_modifier::parse_scss_variable_modifiers;
use crate::parser::CssParser;
use crate::syntax::scss::{expected_scss_expression, parse_scss_expression_until};
use biome_css_syntax::CssSyntaxKind::{EOF, SCSS_DECLARATION};
use biome_css_syntax::T;
use biome_parser::prelude::ParsedSyntax;
use biome_parser::prelude::ParsedSyntax::{Absent, Present};
use biome_parser::{Parser, TokenSet, token_set};
use biome_parser::{Parser, token_set};

/// Detects a SCSS variable declaration (including module-qualified variables).
///
Expand Down Expand Up @@ -52,7 +48,7 @@ pub(crate) fn parse_scss_declaration(p: &mut CssParser) -> ParsedSyntax {
let m = p.start();

parse_scss_declaration_name(p).ok();
p.expect(T![:]);
p.bump(T![:]);

Comment thread
denbezrukov marked this conversation as resolved.
parse_scss_expression_until(p, token_set![T![!], T![;], T!['}']])
.or_add_diagnostic(p, expected_scss_expression);
Expand All @@ -70,27 +66,6 @@ pub(crate) fn parse_scss_declaration(p: &mut CssParser) -> ParsedSyntax {
Present(m.complete(p, SCSS_DECLARATION))
}

#[inline]
fn is_at_scss_namespaced_identifier(p: &mut CssParser) -> bool {
is_nth_at_identifier(p, 0)
&& p.nth_at(1, T![.])
&& p.nth_at(2, T![$])
&& is_nth_at_identifier(p, 3)
}

#[inline]
fn parse_scss_namespaced_identifier(p: &mut CssParser) -> ParsedSyntax {
if !is_at_scss_namespaced_identifier(p) {
return Absent;
}

let m = p.start();
parse_regular_identifier(p).ok();
p.expect(T![.]);
parse_scss_identifier(p).ok();
Present(m.complete(p, SCSS_NAMESPACED_IDENTIFIER))
}

#[inline]
fn parse_scss_declaration_name(p: &mut CssParser) -> ParsedSyntax {
if is_at_scss_namespaced_identifier(p) {
Expand All @@ -99,106 +74,3 @@ fn parse_scss_declaration_name(p: &mut CssParser) -> ParsedSyntax {
parse_scss_identifier(p)
}
}

const SCSS_VARIABLE_MODIFIER_LIST_END_SET: TokenSet<CssSyntaxKind> =
token_set![T![;], T!['}'], EOF];
const SCSS_VARIABLE_MODIFIER_TOKEN_SET: TokenSet<CssSyntaxKind> =
token_set![T![default], T![global]];

#[inline]
fn parse_scss_variable_modifiers(p: &mut CssParser) {
ScssVariableModifierList.parse_list(p);
recover_scss_variable_modifier_tail(p);
}

#[inline]
fn recover_scss_variable_modifier_tail(p: &mut CssParser) {
loop {
if p.at_ts(SCSS_VARIABLE_MODIFIER_LIST_END_SET) {
return;
}

if p.at(T![!]) {
parse_scss_variable_modifier(p).ok();
continue;
}

if p.at(ERROR_TOKEN) {
p.bump_any();
continue;
}

// Recover malformed modifier separators only when they directly precede
// another modifier (`bar !global`). Otherwise leave the token for
// missing-semicolon recovery at declaration level.
if p.nth_at(1, T![!]) {
let range = p.cur_range();
p.error(
p.err_builder("Unexpected value or character.", range)
.with_hint("Expected a variable modifier or the end of the declaration."),
);
p.bump_any();
continue;
}

return;
}
}

#[inline]
fn parse_scss_variable_modifier(p: &mut CssParser) -> ParsedSyntax {
if !p.at(T![!]) {
return Absent;
}

let m = p.start();
p.bump(T![!]);

if p.at_ts(SCSS_VARIABLE_MODIFIER_TOKEN_SET) {
p.bump_ts(SCSS_VARIABLE_MODIFIER_TOKEN_SET);
} else if p.at(T![important]) {
p.error(important_modifier_not_allowed(p, p.cur_range()));
p.bump(T![important]);
} else {
p.error(expected_token_any(&[T![default], T![global]]));
if !p.at_ts(SCSS_VARIABLE_MODIFIER_LIST_END_SET) {
p.bump_any();
}
}

Present(m.complete(p, SCSS_VARIABLE_MODIFIER))
}

fn important_modifier_not_allowed(p: &CssParser, range: TextRange) -> ParseDiagnostic {
p.err_builder("`!important` is not valid here.", range)
.with_hint(
"SCSS variable declarations only support the `!default` and `!global` modifiers.",
)
}

struct ScssVariableModifierList;

impl ParseNodeList for ScssVariableModifierList {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const LIST_KIND: Self::Kind = SCSS_VARIABLE_MODIFIER_LIST;

fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax {
parse_scss_variable_modifier(p)
}

fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at_ts(SCSS_VARIABLE_MODIFIER_LIST_END_SET) || !p.at(T![!])
}

fn recover(
&mut self,
_p: &mut Self::Parser<'_>,
parsed_element: ParsedSyntax,
) -> RecoveryResult {
match parsed_element {
Absent => Err(RecoveryError::AlreadyRecovered),
Present(m) => Ok(m),
}
}
}
Loading
Loading