From e92f2a3454e2c17a03085ee6c6a5080ff493991f Mon Sep 17 00:00:00 2001 From: Shark Date: Thu, 21 Nov 2024 19:41:04 +0100 Subject: [PATCH] fix specificity of shorthand props --- crates/gosub_css3/src/matcher/shorthands.rs | 103 ++++++++++++++------ crates/gosub_css3/src/matcher/styling.rs | 21 +++- crates/gosub_css3/src/stylesheet.rs | 25 ++++- crates/gosub_css3/src/system.rs | 2 +- 4 files changed, 112 insertions(+), 39 deletions(-) diff --git a/crates/gosub_css3/src/matcher/shorthands.rs b/crates/gosub_css3/src/matcher/shorthands.rs index 38a678f9c..db4be19e8 100644 --- a/crates/gosub_css3/src/matcher/shorthands.rs +++ b/crates/gosub_css3/src/matcher/shorthands.rs @@ -106,8 +106,18 @@ pub struct Shorthands { #[derive(Debug, Clone, PartialEq)] pub struct FixList { - list: Vec<(String, Vec)>, + list: Vec<(String, Vec)>, multipliers: Vec<(String, usize)>, + + current_info: Option, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct FixListInfo { + origin: CssOrigin, + important: bool, + location: String, + specificity: Specificity, } #[derive(Debug, Clone)] @@ -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, CompleteStep<'a>> { let snapshot = Some(self.snapshot()); @@ -318,18 +329,46 @@ impl FixList { Self { list: Vec::new(), multipliers: Vec::new(), + current_info: None, } } - pub fn insert(&mut self, name: String, value: Vec) { + 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) { @@ -337,7 +376,7 @@ impl FixList { 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; }; @@ -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 { @@ -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(); @@ -396,8 +429,10 @@ impl FixList { impl CompleteStep<'_> { pub fn complete(mut self, value: Vec) { + 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; @@ -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 } ); @@ -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 } ); @@ -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 } ); @@ -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 } ); diff --git a/crates/gosub_css3/src/matcher/styling.rs b/crates/gosub_css3/src/matcher/styling.rs index 5cbd40488..451d7e679 100644 --- a/crates/gosub_css3/src/matcher/styling.rs +++ b/crates/gosub_css3/src/matcher/styling.rs @@ -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)) } } @@ -446,10 +448,7 @@ impl CssProperty { } fn find_cascaded_value(&self) -> Option { - 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 { @@ -528,6 +527,18 @@ impl From for CssProperty { } } +impl From 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) diff --git a/crates/gosub_css3/src/stylesheet.rs b/crates/gosub_css3/src/stylesheet.rs index 8b0f7d9fd..4620773e4 100644 --- a/crates/gosub_css3/src/stylesheet.rs +++ b/crates/gosub_css3/src/stylesheet.rs @@ -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; @@ -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 { @@ -438,6 +439,28 @@ impl CssValue { } } + pub fn from_vec(mut value: Vec) -> 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 { + 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 { match *node.node_type.clone() { diff --git a/crates/gosub_css3/src/system.rs b/crates/gosub_css3/src/system.rs index 01e29ef53..f0b925f76 100644 --- a/crates/gosub_css3/src/system.rs +++ b/crates/gosub_css3/src/system.rs @@ -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); } } }