Skip to content

Commit 68b1006

Browse files
committed
feat: add negative assertions, remove duplicate code
1 parent c9fd357 commit 68b1006

File tree

5 files changed

+44
-90
lines changed

5 files changed

+44
-90
lines changed

cli/src/query_testing.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,20 @@ pub struct CaptureInfo {
1818
#[derive(Debug, PartialEq, Eq)]
1919
pub struct Assertion {
2020
pub position: Point,
21+
pub negative: bool,
2122
pub expected_capture_name: String,
2223
}
2324

25+
impl Assertion {
26+
pub fn new(row: usize, col: usize, negative: bool, expected_capture_name: String) -> Self {
27+
Self {
28+
position: Point::new(row, col),
29+
negative,
30+
expected_capture_name,
31+
}
32+
}
33+
}
34+
2435
/// Parse the given source code, finding all of the comments that contain
2536
/// highlighting assertions. Return a vector of (position, expected highlight name)
2637
/// pairs.
@@ -54,6 +65,7 @@ pub fn parse_position_comments(
5465
// to its own column.
5566
let mut has_left_caret = false;
5667
let mut has_arrow = false;
68+
let mut negative = false;
5769
let mut arrow_end = 0;
5870
for (i, c) in text.char_indices() {
5971
arrow_end = i + 1;
@@ -69,14 +81,28 @@ pub fn parse_position_comments(
6981
has_left_caret = c == '<';
7082
}
7183

84+
// find any ! after arrows but before capture name
85+
if has_arrow {
86+
for (i, c) in text[arrow_end..].char_indices() {
87+
if c == '!' {
88+
negative = true;
89+
arrow_end += i + 1;
90+
break;
91+
} else if !c.is_whitespace() {
92+
break;
93+
}
94+
}
95+
}
96+
7297
// If the comment node contains an arrow and a highlight name, record the
7398
// highlight name and the position.
7499
if let (true, Some(mat)) =
75100
(has_arrow, CAPTURE_NAME_REGEX.find(&text[arrow_end..]))
76101
{
77102
assertion_ranges.push((node.start_position(), node.end_position()));
78103
result.push(Assertion {
79-
position: position,
104+
position,
105+
negative,
80106
expected_capture_name: mat.as_str().to_string(),
81107
});
82108
}

cli/src/test_highlight.rs

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub fn iterate_assertions(
9494
let mut actual_highlights = Vec::<&String>::new();
9595
for Assertion {
9696
position,
97+
negative,
9798
expected_capture_name: expected_highlight,
9899
} in assertions
99100
{
@@ -117,12 +118,13 @@ pub fn iterate_assertions(
117118
break 'highlight_loop;
118119
}
119120

120-
// If the highlight matches the assertion, this test passes. Otherwise,
121+
// If the highlight matches the assertion, or if the highlight doesn't
122+
// match the assertion but it's negative, this test passes. Otherwise,
121123
// add this highlight to the list of actual highlights that span the
122124
// assertion's position, in order to generate an error message in the event
123125
// of a failure.
124126
let highlight_name = &highlight_names[(highlight.2).0];
125-
if *highlight_name == *expected_highlight {
127+
if (*highlight_name == *expected_highlight) == !negative {
126128
passed = true;
127129
break 'highlight_loop;
128130
} else {
@@ -162,68 +164,7 @@ pub fn test_highlight(
162164
let assertions =
163165
parse_position_comments(highlighter.parser(), highlight_config.language, source)?;
164166

165-
iterate_assertions(&assertions, &highlights, &highlight_names)?;
166-
167-
// Iterate through all of the highlighting assertions, checking each one against the
168-
// actual highlights.
169-
let mut i = 0;
170-
let mut actual_highlights = Vec::<&String>::new();
171-
for Assertion {
172-
position,
173-
expected_capture_name: expected_highlight,
174-
} in &assertions
175-
{
176-
let mut passed = false;
177-
actual_highlights.clear();
178-
179-
'highlight_loop: loop {
180-
// The assertions are ordered by position, so skip past all of the highlights that
181-
// end at or before this assertion's position.
182-
if let Some(highlight) = highlights.get(i) {
183-
if highlight.1 <= *position {
184-
i += 1;
185-
continue;
186-
}
187-
188-
// Iterate through all of the highlights that start at or before this assertion's,
189-
// position, looking for one that matches the assertion.
190-
let mut j = i;
191-
while let (false, Some(highlight)) = (passed, highlights.get(j)) {
192-
if highlight.0 > *position {
193-
break 'highlight_loop;
194-
}
195-
196-
// If the highlight matches the assertion, this test passes. Otherwise,
197-
// add this highlight to the list of actual highlights that span the
198-
// assertion's position, in order to generate an error message in the event
199-
// of a failure.
200-
let highlight_name = &highlight_names[(highlight.2).0];
201-
if *highlight_name == *expected_highlight {
202-
passed = true;
203-
break 'highlight_loop;
204-
} else {
205-
actual_highlights.push(highlight_name);
206-
}
207-
208-
j += 1;
209-
}
210-
} else {
211-
break;
212-
}
213-
}
214-
215-
if !passed {
216-
return Err(Failure {
217-
row: position.row,
218-
column: position.column,
219-
expected_highlight: expected_highlight.clone(),
220-
actual_highlights: actual_highlights.into_iter().cloned().collect(),
221-
}
222-
.into());
223-
}
224-
}
225-
226-
Ok(assertions.len())
167+
iterate_assertions(&assertions, &highlights, &highlight_names)
227168
}
228169

229170
pub fn get_highlight_positions(

cli/src/test_tags.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub fn test_tag(
9696
let mut actual_tags = Vec::<&String>::new();
9797
for Assertion {
9898
position,
99+
negative,
99100
expected_capture_name: expected_tag,
100101
} in &assertions
101102
{
@@ -117,7 +118,7 @@ pub fn test_tag(
117118
}
118119

119120
let tag_name = &tag.2;
120-
if *tag_name == *expected_tag {
121+
if (*tag_name == *expected_tag) == !negative {
121122
passed = true;
122123
break 'tag_loop;
123124
} else {

cli/src/tests/test_highlight_test.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn test_highlight_test_with_basic_test() {
2323
" // ^ keyword",
2424
" return d + e;",
2525
" // ^ variable.parameter",
26+
" // ^ !variable",
2627
"};",
2728
]
2829
.join("\n");
@@ -32,18 +33,10 @@ fn test_highlight_test_with_basic_test() {
3233
assert_eq!(
3334
assertions,
3435
&[
35-
Assertion {
36-
position: Point::new(1, 5),
37-
expected_capture_name: "function".to_string()
38-
},
39-
Assertion {
40-
position: Point::new(1, 11),
41-
expected_capture_name: "keyword".to_string()
42-
},
43-
Assertion {
44-
position: Point::new(4, 9),
45-
expected_capture_name: "variable.parameter".to_string()
46-
},
36+
Assertion::new(1, 5, false, String::from("function")),
37+
Assertion::new(1, 11, false, String::from("keyword")),
38+
Assertion::new(4, 9, false, String::from("variable.parameter")),
39+
Assertion::new(4, 11, true, String::from("variable")),
4740
]
4841
);
4942

cli/src/tests/test_tags_test.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ fn test_tags_test_with_basic_test() {
1616
" # ^ reference.call",
1717
" return d(e)",
1818
" # ^ reference.call",
19+
" # ^ !variable.parameter",
1920
"",
2021
]
2122
.join("\n");
@@ -26,18 +27,10 @@ fn test_tags_test_with_basic_test() {
2627
assert_eq!(
2728
assertions,
2829
&[
29-
Assertion {
30-
position: Point::new(1, 4),
31-
expected_capture_name: "definition.function".to_string(),
32-
},
33-
Assertion {
34-
position: Point::new(3, 9),
35-
expected_capture_name: "reference.call".to_string(),
36-
},
37-
Assertion {
38-
position: Point::new(5, 11),
39-
expected_capture_name: "reference.call".to_string(),
40-
},
30+
Assertion::new(1, 4, false, String::from("definition.function")),
31+
Assertion::new(3, 9, false, String::from("reference.call")),
32+
Assertion::new(5, 11, false, String::from("reference.call")),
33+
Assertion::new(5, 13, true, String::from("variable.parameter")),
4134
]
4235
);
4336

0 commit comments

Comments
 (0)