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

fix specificity of shorthand props #689

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
103 changes: 71 additions & 32 deletions crates/gosub_css3/src/matcher/shorthands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,18 @@ pub struct Shorthands {

#[derive(Debug, Clone, PartialEq)]
pub struct FixList {
list: Vec<(String, Vec<CssValue>)>,
list: Vec<(String, Vec<DeclarationProperty>)>,
multipliers: Vec<(String, usize)>,

current_info: Option<FixListInfo>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct FixListInfo {
origin: CssOrigin,
important: bool,
location: String,
specificity: Specificity,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -192,6 +202,7 @@ pub struct Snapshot {
}

impl<'a> ShorthandResolver<'a> {
#[allow(clippy::result_large_err)]
pub fn step(&'a mut self, idx: usize) -> Result<Option<Self>, CompleteStep<'a>> {
let snapshot = Some(self.snapshot());

Expand Down Expand Up @@ -318,26 +329,54 @@ impl FixList {
Self {
list: Vec::new(),
multipliers: Vec::new(),
current_info: None,
}
}

pub fn insert(&mut self, name: String, value: Vec<CssValue>) {
pub fn set_info(&mut self, info: FixListInfo) {
self.current_info = Some(info);
}

fn get_declaration(&self, value: CssValue) -> DeclarationProperty {
if let Some(info) = &self.current_info {
DeclarationProperty {
value,
origin: info.origin,
important: info.important,
specificity: info.specificity,
location: info.location.clone(),
}
} else {
DeclarationProperty {
value,
origin: CssOrigin::Author,
important: false,
specificity: Specificity::new(0, 0, 0),
location: String::new(),
}
}
}

pub fn insert(&mut self, name: String, value: CssValue) {
let value = self.get_declaration(value);

for (k, v) in &mut self.list {
if *k == name {
*v = value;
v.clear(); //TODO: This is a hack, we should not have to clear the vec
v.push(value);
return;
}
}

self.list.push((name, value));
self.list.push((name, vec![value]));
}

pub fn resolve_nested(&mut self, definitions: &CssDefinitions) {
let mut fix_list = FixList::new();

let mut had_shorthands = false;

for (name, value) in &self.list {
for (name, decl) in &self.list {
let Some(prop) = definitions.find_property(name) else {
continue;
};
Expand All @@ -346,9 +385,11 @@ impl FixList {
continue;
}

let Some(decl) = decl.iter().max() else { continue };

had_shorthands = true;

prop.matches_and_shorthands(value, &mut fix_list);
prop.matches_and_shorthands(decl.value.to_slice(), &mut fix_list);
}

if had_shorthands {
Expand All @@ -364,18 +405,10 @@ impl FixList {

pub fn apply(&mut self, props: &mut CssProperties) {
for (name, value) in &self.list {
let Some(value) = value.first().cloned() else {
let Some(decl) = value.iter().max().cloned() else {
continue;
};

let decl = DeclarationProperty {
value,
origin: CssOrigin::Author,
important: false,
location: "".to_string(),
specificity: Specificity::new(0, 1, 0),
};

match props.properties.entry(name.clone()) {
Entry::Occupied(mut entry) => {
let prop = entry.get_mut();
Expand All @@ -396,8 +429,10 @@ impl FixList {

impl CompleteStep<'_> {
pub fn complete(mut self, value: Vec<CssValue>) {
let val = CssValue::from_vec(value);

for name in self.name.clone() {
self.list.insert(name.to_string(), value.clone());
self.list.insert(name.to_string(), val.clone())
}

self.completed = true;
Expand Down Expand Up @@ -619,12 +654,13 @@ mod tests {
fix_list,
FixList {
list: vec![
("margin-bottom".to_string(), vec![unit!(1.0, "px")]),
("margin-left".to_string(), vec![unit!(1.0, "px")]),
("margin-right".to_string(), vec![unit!(1.0, "px")]),
("margin-top".to_string(), vec![unit!(1.0, "px")]),
("margin-bottom".to_string(), vec![unit!(1.0, "px").into()]),
("margin-left".to_string(), vec![unit!(1.0, "px").into()]),
("margin-right".to_string(), vec![unit!(1.0, "px").into()]),
("margin-top".to_string(), vec![unit!(1.0, "px").into()]),
],
multipliers: vec![("margin".to_string(), 1),],
current_info: None
}
);

Expand All @@ -638,12 +674,13 @@ mod tests {
fix_list,
FixList {
list: vec![
("margin-bottom".to_string(), vec![unit!(1.0, "px")]),
("margin-left".to_string(), vec![unit!(2.0, "px")]),
("margin-right".to_string(), vec![unit!(2.0, "px")]),
("margin-top".to_string(), vec![unit!(1.0, "px")]),
("margin-bottom".to_string(), vec![unit!(1.0, "px").into()]),
("margin-left".to_string(), vec![unit!(2.0, "px").into()]),
("margin-right".to_string(), vec![unit!(2.0, "px").into()]),
("margin-top".to_string(), vec![unit!(1.0, "px").into()]),
],
multipliers: vec![("margin".to_string(), 2),],
current_info: None
}
);

Expand All @@ -656,12 +693,13 @@ mod tests {
fix_list,
FixList {
list: vec![
("margin-bottom".to_string(), vec![unit!(3.0, "px")]),
("margin-left".to_string(), vec![unit!(2.0, "px")]),
("margin-right".to_string(), vec![unit!(2.0, "px")]),
("margin-top".to_string(), vec![unit!(1.0, "px")]),
("margin-bottom".to_string(), vec![unit!(3.0, "px").into()]),
("margin-left".to_string(), vec![unit!(2.0, "px").into()]),
("margin-right".to_string(), vec![unit!(2.0, "px").into()]),
("margin-top".to_string(), vec![unit!(1.0, "px").into()]),
],
multipliers: vec![("margin".to_string(), 3),],
current_info: None
}
);

Expand All @@ -675,12 +713,13 @@ mod tests {
fix_list,
FixList {
list: vec![
("margin-bottom".to_string(), vec![unit!(3.0, "px")]),
("margin-left".to_string(), vec![unit!(4.0, "px")]),
("margin-right".to_string(), vec![unit!(2.0, "px")]),
("margin-top".to_string(), vec![unit!(1.0, "px")]),
("margin-bottom".to_string(), vec![unit!(3.0, "px").into()]),
("margin-left".to_string(), vec![unit!(4.0, "px").into()]),
("margin-right".to_string(), vec![unit!(2.0, "px").into()]),
("margin-top".to_string(), vec![unit!(1.0, "px").into()]),
],
multipliers: vec![("margin".to_string(), 4),],
current_info: None
}
);

Expand Down
21 changes: 16 additions & 5 deletions crates/gosub_css3/src/matcher/styling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,9 @@ impl Eq for DeclarationProperty {}

impl Ord for DeclarationProperty {
fn cmp(&self, other: &Self) -> Ordering {
self.priority().cmp(&other.priority())
self.priority()
.cmp(&other.priority())
.then_with(|| self.specificity.cmp(&other.specificity))
}
}

Expand Down Expand Up @@ -446,10 +448,7 @@ impl CssProperty {
}

fn find_cascaded_value(&self) -> Option<CssValue> {
self.declared
.iter()
.max_by(|a, b| a.cmp(b).then_with(|| a.specificity.cmp(&b.specificity)))
.map(|v| v.value.clone())
self.declared.iter().max().map(|v| v.value.clone())
}

fn find_specified_value(&self) -> CssValue {
Expand Down Expand Up @@ -528,6 +527,18 @@ impl From<CssValue> for CssProperty {
}
}

impl From<CssValue> for DeclarationProperty {
fn from(value: CssValue) -> Self {
Self {
location: "".into(),
important: false,
value,
origin: CssOrigin::Author,
specificity: Specificity::new(0, 0, 0),
}
}
}

impl Display for CssProperty {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.actual, f)
Expand Down
25 changes: 24 additions & 1 deletion crates/gosub_css3/src/stylesheet.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::fmt::Debug;
use core::slice;
use gosub_shared::byte_stream::Location;
use gosub_shared::errors::CssError;
use gosub_shared::errors::CssResult;
Expand Down Expand Up @@ -286,7 +287,7 @@ impl Display for MatcherType {
}

/// Defines the specificity for a selector
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Specificity(u32, u32, u32);

impl Specificity {
Expand Down Expand Up @@ -438,6 +439,28 @@ impl CssValue {
}
}

pub fn from_vec(mut value: Vec<Self>) -> Self {
match value.len() {
0 => Self::None,
1 => value.swap_remove(0),
_ => Self::List(value),
}
}

pub fn to_slice(&self) -> &[Self] {
match self {
Self::List(l) => l,
this => slice::from_ref(this),
}
}

pub fn into_vec(self) -> Vec<Self> {
match self {
Self::List(l) => l,
this => vec![this],
}
}

/// Converts a CSS AST node to a CSS value
pub fn parse_ast_node(node: &crate::node::Node) -> CssResult<CssValue> {
match *node.node_type.clone() {
Expand Down
2 changes: 1 addition & 1 deletion crates/gosub_css3/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl CssSystem for Css3System {
important: declaration.important,
};

add_property_to_map(&mut css_map_entry, sheet, specificity.clone(), &decl);
add_property_to_map(&mut css_map_entry, sheet, specificity, &decl);
}
}
}
Expand Down
Loading