Fix incorrect handling of field names in property sheets
This commit is contained in:
parent
7cd1d77c08
commit
f64ee1eb75
2 changed files with 179 additions and 9 deletions
|
|
@ -206,11 +206,29 @@ impl Builder {
|
|||
}
|
||||
}
|
||||
|
||||
// If there are multiple transitions that could *both* match (e.g. one based on a
|
||||
// a node type and one based on a field name), then create an additional transition
|
||||
// for the intersection of the two.
|
||||
let mut i = 0;
|
||||
let mut transition_list = transitions.into_iter().collect::<Vec<_>>();
|
||||
while i < transition_list.len() {
|
||||
for j in 0..i {
|
||||
if let Some(intersection) =
|
||||
self.intersect_transitions(&transition_list[j].0, &transition_list[i].0)
|
||||
{
|
||||
transition_list.push((
|
||||
intersection,
|
||||
u32::max(transition_list[i].1, transition_list[j].1),
|
||||
));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Ensure that for a given node type, more specific transitions are tried
|
||||
// first, and in the event of a tie, transitions corresponding to later rules
|
||||
// in the cascade are tried first.
|
||||
let mut transition_list: Vec<(PropertyTransitionJSON, u32)> =
|
||||
transitions.into_iter().collect();
|
||||
// in the cascade are tried first. Also, sort the non-intersecting transitions
|
||||
// by name to guarantee a deterministic order.
|
||||
transition_list.sort_by(|a, b| {
|
||||
(transition_specificity(&b.0).cmp(&transition_specificity(&a.0)))
|
||||
.then_with(|| b.1.cmp(&a.1))
|
||||
|
|
@ -290,6 +308,31 @@ impl Builder {
|
|||
.extend(transition_list.into_iter().map(|i| i.0));
|
||||
}
|
||||
|
||||
fn intersect_transitions(
|
||||
&self,
|
||||
left: &PropertyTransitionJSON,
|
||||
right: &PropertyTransitionJSON,
|
||||
) -> Option<PropertyTransitionJSON> {
|
||||
// If one transition is based on the node's type and the other
|
||||
// is based on its field name, then create a combined transition
|
||||
// wiht *both* criteria.
|
||||
if let (Some(left_field), None) = (&left.field, &left.kind) {
|
||||
if right.field.is_none() {
|
||||
let mut result = right.clone();
|
||||
result.field = Some(left_field.clone());
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
if let (Some(right_field), None) = (&right.field, &right.kind) {
|
||||
if left.field.is_none() {
|
||||
let mut result = left.clone();
|
||||
result.field = Some(right_field.clone());
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn remove_duplicate_states(&mut self) {
|
||||
let mut state_replacements = BTreeMap::new();
|
||||
let mut done = false;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,13 @@ fn test_walk_with_properties_with_nth_child() {
|
|||
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(*cursor.node_properties(), Properties { a: Some("z".to_string()), b: None });
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("z".to_string()),
|
||||
b: None
|
||||
}
|
||||
);
|
||||
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "=");
|
||||
|
|
@ -58,13 +64,25 @@ fn test_walk_with_properties_with_nth_child() {
|
|||
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(*cursor.node_properties(), Properties { a: Some("y".to_string()), b: None });
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("y".to_string()),
|
||||
b: None
|
||||
}
|
||||
);
|
||||
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "||");
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(*cursor.node_properties(), Properties { a: Some("x".to_string()), b: None });
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("x".to_string()),
|
||||
b: None
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -110,7 +128,13 @@ fn test_walk_with_properties_with_regexes() {
|
|||
// The later selector with a text regex overrides the earlier one.
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(*cursor.node_properties(), Properties { a: Some("z".to_string()), b: None });
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("z".to_string()),
|
||||
b: None
|
||||
}
|
||||
);
|
||||
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "=");
|
||||
|
|
@ -120,7 +144,13 @@ fn test_walk_with_properties_with_regexes() {
|
|||
// The selectors with text regexes override the selector without one.
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(*cursor.node_properties(), Properties { a: Some("y".to_string()), b: None });
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("y".to_string()),
|
||||
b: None
|
||||
}
|
||||
);
|
||||
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "arguments");
|
||||
|
|
@ -130,5 +160,102 @@ fn test_walk_with_properties_with_regexes() {
|
|||
// This node doesn't match either of the regexes.
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(*cursor.node_properties(), Properties { a: Some("x".to_string()), b: None });
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("x".to_string()),
|
||||
b: None
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_walk_with_properties_based_on_fields() {
|
||||
let language = get_language("javascript");
|
||||
let property_sheet = PropertySheet::<Properties>::new(
|
||||
language,
|
||||
&properties::generate_property_sheet_string(
|
||||
"/some/path.css",
|
||||
"
|
||||
arrow_function > .parameter {
|
||||
a: x;
|
||||
}
|
||||
|
||||
function_declaration {
|
||||
& > .parameters > identifier {
|
||||
a: y;
|
||||
}
|
||||
|
||||
& > .name {
|
||||
b: z;
|
||||
}
|
||||
}
|
||||
|
||||
identifier {
|
||||
a: w;
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let source_code = "function a(b) { return c => c + b; }";
|
||||
|
||||
let mut parser = Parser::new();
|
||||
parser.set_language(language).unwrap();
|
||||
let tree = parser.parse(source_code, None).unwrap();
|
||||
let mut cursor = tree.walk_with_properties(&property_sheet, source_code.as_bytes());
|
||||
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "function_declaration");
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "function");
|
||||
assert_eq!(*cursor.node_properties(), Properties::default());
|
||||
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: None,
|
||||
b: Some("z".to_string())
|
||||
}
|
||||
);
|
||||
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "formal_parameters");
|
||||
assert_eq!(*cursor.node_properties(), Properties::default());
|
||||
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "(");
|
||||
assert_eq!(*cursor.node_properties(), Properties::default());
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("y".to_string()),
|
||||
b: None,
|
||||
}
|
||||
);
|
||||
|
||||
assert!(cursor.goto_parent());
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "statement_block");
|
||||
assert!(cursor.goto_first_child());
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "return_statement");
|
||||
assert!(cursor.goto_first_child());
|
||||
assert!(cursor.goto_next_sibling());
|
||||
assert_eq!(cursor.node().kind(), "arrow_function");
|
||||
assert!(cursor.goto_first_child());
|
||||
assert_eq!(cursor.node().kind(), "identifier");
|
||||
assert_eq!(
|
||||
*cursor.node_properties(),
|
||||
Properties {
|
||||
a: Some("x".to_string()),
|
||||
b: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue