Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(css_semantic): improve CSS semantic model structure and API #4908

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub struct DescendingSelector {
/// ^^^^^^^
/// }
/// ```
fn find_tail_selector(selector: &AnyCssSelector) -> Option<String> {
fn find_tail_selector_str(selector: &AnyCssSelector) -> Option<String> {
match selector {
AnyCssSelector::CssCompoundSelector(s) => {
let simple = s
Expand All @@ -103,7 +103,7 @@ fn find_tail_selector(selector: &AnyCssSelector) -> Option<String> {
Some(last_selector)
}
AnyCssSelector::CssComplexSelector(s) => {
s.right().as_ref().ok().and_then(find_tail_selector)
s.right().as_ref().ok().and_then(find_tail_selector_str)
}
_ => None,
}
Expand All @@ -119,35 +119,35 @@ fn find_descending_selector(
visited_selectors: &mut FxHashMap<String, (TextRange, Specificity)>,
descending_selectors: &mut Vec<DescendingSelector>,
) {
if visited_rules.contains(&rule.id) {
if !visited_rules.insert(rule.id()) {
return;
} else {
visited_rules.insert(rule.id);
};
}

for selector in &rule.selectors {
let tail_selector = if let Some(s) = find_tail_selector(&selector.original) {
s
} else {
for selector in rule.selectors() {
let Some(casted_selector) = AnyCssSelector::cast(selector.node().clone()) else {
continue;
};
let Some(tail_selector_str) = find_tail_selector_str(&casted_selector) else {
continue;
};

if let Some((last_textrange, last_specificity)) = visited_selectors.get(&tail_selector) {
if last_specificity > &selector.specificity {
if let Some((last_text_range, last_specificity)) = visited_selectors.get(&tail_selector_str)
{
if last_specificity > &selector.specificity() {
descending_selectors.push(DescendingSelector {
high: (*last_textrange, last_specificity.clone()),
low: (selector.range, selector.specificity.clone()),
high: (*last_text_range, *last_specificity),
low: (selector.range(), selector.specificity()),
});
}
} else {
visited_selectors.insert(
tail_selector,
(selector.range, selector.specificity.clone()),
tail_selector_str,
(selector.range(), selector.specificity()),
);
}
}

for child_id in &rule.child_ids {
for child_id in rule.child_ids() {
if let Some(child_rule) = model.get_rule_by_id(*child_id) {
find_descending_selector(
child_rule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use std::collections::hash_map::Entry;

use crate::services::semantic::Semantic;
use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource};
use biome_console::markup;
use biome_css_syntax::CssDeclarationOrRuleList;
use biome_rowan::{AstNode, TextRange};
use rustc_hash::FxHashMap;

use crate::services::semantic::Semantic;
use std::collections::hash_map::Entry;

declare_lint_rule! {
/// Disallow duplicate custom properties within declaration blocks.
Expand Down Expand Up @@ -56,25 +54,25 @@ impl Rule for NoDuplicateCustomProperties {

let rule = model.get_rule_by_range(node.range())?;

let mut seen: FxHashMap<&str, TextRange> = FxHashMap::default();
let mut seen: FxHashMap<Box<str>, TextRange> = FxHashMap::default();

for declaration in rule.declarations.iter() {
let prop = &declaration.property;
let prop_name = prop.name.as_str();
let prop_range = prop.range;
for declaration in rule.declarations() {
let prop = declaration.property();
let prop_name = prop.value().ok()?;
let prop_range = prop.range();

let is_custom_property = prop_name.starts_with("--");

if !is_custom_property {
continue;
}

match seen.entry(prop_name) {
match seen.entry(prop_name.text().into()) {
Entry::Occupied(entry) => {
return Some((*entry.get(), (prop_range, prop_name.to_string())));
}
Entry::Vacant(_) => {
seen.insert(prop_name, prop_range);
Entry::Vacant(entry) => {
entry.insert(prop_range);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::{borrow::Cow, collections::hash_map::Entry};
use std::collections::hash_map::Entry;

use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource};
use biome_console::markup;
use biome_css_syntax::CssDeclarationOrRuleList;
use biome_rowan::{AstNode, TextRange};
use biome_string_case::StrOnlyExtension;
use rustc_hash::FxHashMap;

use crate::services::semantic::Semantic;
Expand Down Expand Up @@ -55,25 +54,25 @@ impl Rule for NoDuplicateProperties {

let rule = model.get_rule_by_range(node.range())?;

let mut seen: FxHashMap<Cow<'_, str>, TextRange> = FxHashMap::default();
let mut seen: FxHashMap<Box<str>, TextRange> = FxHashMap::default();

for declaration in rule.declarations.iter() {
let prop = &declaration.property;
let prop_name = prop.name.to_lowercase_cow();
let prop_range = prop.range;
for declaration in rule.declarations() {
let prop = declaration.property();
let prop_name = prop.text().to_string();
let prop_range = prop.range();

let is_custom_property = prop_name.starts_with("--");

if is_custom_property {
continue;
}

match seen.entry(prop_name.clone()) {
match seen.entry(prop_name.clone().into()) {
Entry::Occupied(entry) => {
return Some((*entry.get(), (prop_range, prop_name.to_string())));
}
Entry::Vacant(_) => {
seen.insert(prop_name, prop_range);
Entry::Vacant(entry) => {
entry.insert(prop_range);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ declare_lint_rule! {
/// Disallow missing var function for css variables.
///
/// This rule has the following limitations:
/// - It only reports custom properties that are defined and accesible within the same source.
/// - It only reports custom properties that are defined and accessible within the same source.
/// - It does not check properties that can contain author-defined identifiers.
/// - It ignores the following properties:
/// - `animation`
Expand Down Expand Up @@ -164,24 +164,26 @@ impl Rule for NoMissingVarFunction {
let rule = model.get_rule_by_range(node.range())?;

if rule
.declarations
.declarations()
.iter()
.any(|decl| decl.property.name == custom_variable_name)
.flat_map(|decl| decl.property().value())
.any(|value| value.text() == custom_variable_name)
{
return Some(node.clone());
}

let mut parent_id = rule.parent_id;
let mut parent_id = rule.parent_id();
while let Some(id) = parent_id {
let parent_rule = model.get_rule_by_id(id)?;
if parent_rule
.declarations
.declarations()
.iter()
.any(|decl| decl.property.name == custom_variable_name)
.flat_map(|decl| decl.property().value())
.any(|value| value.text() == custom_variable_name)
{
return Some(node.clone());
}
parent_id = parent_rule.parent_id;
parent_id = parent_rule.parent_id();
}

if model
Expand Down
3 changes: 0 additions & 3 deletions crates/biome_css_semantic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,5 @@ biome_css_syntax = { workspace = true }
biome_rowan = { workspace = true }
rustc-hash = { workspace = true }

[dev-dependencies]
biome_css_parser = { path = "../biome_css_parser" }

[lints]
workspace = true
Loading
Loading