feat: add negative assertions, remove duplicate code
This commit is contained in:
parent
c9fd357c06
commit
68b1006a3b
5 changed files with 44 additions and 90 deletions
|
|
@ -18,9 +18,20 @@ pub struct CaptureInfo {
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Assertion {
|
||||
pub position: Point,
|
||||
pub negative: bool,
|
||||
pub expected_capture_name: String,
|
||||
}
|
||||
|
||||
impl Assertion {
|
||||
pub fn new(row: usize, col: usize, negative: bool, expected_capture_name: String) -> Self {
|
||||
Self {
|
||||
position: Point::new(row, col),
|
||||
negative,
|
||||
expected_capture_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the given source code, finding all of the comments that contain
|
||||
/// highlighting assertions. Return a vector of (position, expected highlight name)
|
||||
/// pairs.
|
||||
|
|
@ -54,6 +65,7 @@ pub fn parse_position_comments(
|
|||
// to its own column.
|
||||
let mut has_left_caret = false;
|
||||
let mut has_arrow = false;
|
||||
let mut negative = false;
|
||||
let mut arrow_end = 0;
|
||||
for (i, c) in text.char_indices() {
|
||||
arrow_end = i + 1;
|
||||
|
|
@ -69,6 +81,19 @@ pub fn parse_position_comments(
|
|||
has_left_caret = c == '<';
|
||||
}
|
||||
|
||||
// find any ! after arrows but before capture name
|
||||
if has_arrow {
|
||||
for (i, c) in text[arrow_end..].char_indices() {
|
||||
if c == '!' {
|
||||
negative = true;
|
||||
arrow_end += i + 1;
|
||||
break;
|
||||
} else if !c.is_whitespace() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the comment node contains an arrow and a highlight name, record the
|
||||
// highlight name and the position.
|
||||
if let (true, Some(mat)) =
|
||||
|
|
@ -76,7 +101,8 @@ pub fn parse_position_comments(
|
|||
{
|
||||
assertion_ranges.push((node.start_position(), node.end_position()));
|
||||
result.push(Assertion {
|
||||
position: position,
|
||||
position,
|
||||
negative,
|
||||
expected_capture_name: mat.as_str().to_string(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ pub fn iterate_assertions(
|
|||
let mut actual_highlights = Vec::<&String>::new();
|
||||
for Assertion {
|
||||
position,
|
||||
negative,
|
||||
expected_capture_name: expected_highlight,
|
||||
} in assertions
|
||||
{
|
||||
|
|
@ -117,12 +118,13 @@ pub fn iterate_assertions(
|
|||
break 'highlight_loop;
|
||||
}
|
||||
|
||||
// If the highlight matches the assertion, this test passes. Otherwise,
|
||||
// If the highlight matches the assertion, or if the highlight doesn't
|
||||
// match the assertion but it's negative, this test passes. Otherwise,
|
||||
// add this highlight to the list of actual highlights that span the
|
||||
// assertion's position, in order to generate an error message in the event
|
||||
// of a failure.
|
||||
let highlight_name = &highlight_names[(highlight.2).0];
|
||||
if *highlight_name == *expected_highlight {
|
||||
if (*highlight_name == *expected_highlight) == !negative {
|
||||
passed = true;
|
||||
break 'highlight_loop;
|
||||
} else {
|
||||
|
|
@ -162,68 +164,7 @@ pub fn test_highlight(
|
|||
let assertions =
|
||||
parse_position_comments(highlighter.parser(), highlight_config.language, source)?;
|
||||
|
||||
iterate_assertions(&assertions, &highlights, &highlight_names)?;
|
||||
|
||||
// Iterate through all of the highlighting assertions, checking each one against the
|
||||
// actual highlights.
|
||||
let mut i = 0;
|
||||
let mut actual_highlights = Vec::<&String>::new();
|
||||
for Assertion {
|
||||
position,
|
||||
expected_capture_name: expected_highlight,
|
||||
} in &assertions
|
||||
{
|
||||
let mut passed = false;
|
||||
actual_highlights.clear();
|
||||
|
||||
'highlight_loop: loop {
|
||||
// The assertions are ordered by position, so skip past all of the highlights that
|
||||
// end at or before this assertion's position.
|
||||
if let Some(highlight) = highlights.get(i) {
|
||||
if highlight.1 <= *position {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate through all of the highlights that start at or before this assertion's,
|
||||
// position, looking for one that matches the assertion.
|
||||
let mut j = i;
|
||||
while let (false, Some(highlight)) = (passed, highlights.get(j)) {
|
||||
if highlight.0 > *position {
|
||||
break 'highlight_loop;
|
||||
}
|
||||
|
||||
// If the highlight matches the assertion, this test passes. Otherwise,
|
||||
// add this highlight to the list of actual highlights that span the
|
||||
// assertion's position, in order to generate an error message in the event
|
||||
// of a failure.
|
||||
let highlight_name = &highlight_names[(highlight.2).0];
|
||||
if *highlight_name == *expected_highlight {
|
||||
passed = true;
|
||||
break 'highlight_loop;
|
||||
} else {
|
||||
actual_highlights.push(highlight_name);
|
||||
}
|
||||
|
||||
j += 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !passed {
|
||||
return Err(Failure {
|
||||
row: position.row,
|
||||
column: position.column,
|
||||
expected_highlight: expected_highlight.clone(),
|
||||
actual_highlights: actual_highlights.into_iter().cloned().collect(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(assertions.len())
|
||||
iterate_assertions(&assertions, &highlights, &highlight_names)
|
||||
}
|
||||
|
||||
pub fn get_highlight_positions(
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ pub fn test_tag(
|
|||
let mut actual_tags = Vec::<&String>::new();
|
||||
for Assertion {
|
||||
position,
|
||||
negative,
|
||||
expected_capture_name: expected_tag,
|
||||
} in &assertions
|
||||
{
|
||||
|
|
@ -117,7 +118,7 @@ pub fn test_tag(
|
|||
}
|
||||
|
||||
let tag_name = &tag.2;
|
||||
if *tag_name == *expected_tag {
|
||||
if (*tag_name == *expected_tag) == !negative {
|
||||
passed = true;
|
||||
break 'tag_loop;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ fn test_highlight_test_with_basic_test() {
|
|||
" // ^ keyword",
|
||||
" return d + e;",
|
||||
" // ^ variable.parameter",
|
||||
" // ^ !variable",
|
||||
"};",
|
||||
]
|
||||
.join("\n");
|
||||
|
|
@ -32,18 +33,10 @@ fn test_highlight_test_with_basic_test() {
|
|||
assert_eq!(
|
||||
assertions,
|
||||
&[
|
||||
Assertion {
|
||||
position: Point::new(1, 5),
|
||||
expected_capture_name: "function".to_string()
|
||||
},
|
||||
Assertion {
|
||||
position: Point::new(1, 11),
|
||||
expected_capture_name: "keyword".to_string()
|
||||
},
|
||||
Assertion {
|
||||
position: Point::new(4, 9),
|
||||
expected_capture_name: "variable.parameter".to_string()
|
||||
},
|
||||
Assertion::new(1, 5, false, String::from("function")),
|
||||
Assertion::new(1, 11, false, String::from("keyword")),
|
||||
Assertion::new(4, 9, false, String::from("variable.parameter")),
|
||||
Assertion::new(4, 11, true, String::from("variable")),
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ fn test_tags_test_with_basic_test() {
|
|||
" # ^ reference.call",
|
||||
" return d(e)",
|
||||
" # ^ reference.call",
|
||||
" # ^ !variable.parameter",
|
||||
"",
|
||||
]
|
||||
.join("\n");
|
||||
|
|
@ -26,18 +27,10 @@ fn test_tags_test_with_basic_test() {
|
|||
assert_eq!(
|
||||
assertions,
|
||||
&[
|
||||
Assertion {
|
||||
position: Point::new(1, 4),
|
||||
expected_capture_name: "definition.function".to_string(),
|
||||
},
|
||||
Assertion {
|
||||
position: Point::new(3, 9),
|
||||
expected_capture_name: "reference.call".to_string(),
|
||||
},
|
||||
Assertion {
|
||||
position: Point::new(5, 11),
|
||||
expected_capture_name: "reference.call".to_string(),
|
||||
},
|
||||
Assertion::new(1, 4, false, String::from("definition.function")),
|
||||
Assertion::new(3, 9, false, String::from("reference.call")),
|
||||
Assertion::new(5, 11, false, String::from("reference.call")),
|
||||
Assertion::new(5, 13, true, String::from("variable.parameter")),
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue