-
Notifications
You must be signed in to change notification settings - Fork 144
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
syntest
and assertions that extend over the end of the line
#175
Comments
I had a go at this, with keeping the "read and process one line at a time" behavior (as opposed to Sublime's "read the whole syntax test file into memory first" implementation) but my Rust skills aren't good enough yet - I'm fighting the borrow checker it seems. My idea was to queue up any test assertions that belong to a line we haven't read yet. Here's the diff in case it helps someone else patch it up. I'm creating the queue with an initial capacity of 2 - I think no syntax test files reference more than one line below at the moment. --- syntect/master/examples/syntest.rs
+++ syntect/syntest/examples/syntest.rs
@@ -25,6 +25,7 @@
use std::cmp::{min, max};
use std::time::Instant;
use std::str::FromStr;
+use std::collections::VecDeque;
use getopts::Options;
use regex::Regex;
@@ -64,6 +65,7 @@
#[derive(Debug)]
struct AssertionRange<'a> {
+ line_number: usize,
begin_char: usize,
end_char: usize,
scope_selector_text: &'a str,
@@ -84,7 +86,7 @@
success: bool,
}
-fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str) -> Option<AssertionRange<'a>> {
+fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str, line_number: usize) -> Option<AssertionRange<'a>> {
// if the test start token specified in the test file's header is on the line
if let Some(index) = line.find(testtoken_start) {
let (before_token_start, token_and_rest_of_line) = line.split_at(index);
@@ -101,6 +103,7 @@
}
}
return Some(AssertionRange {
+ line_number: line_number,
begin_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().start() } else { 0 },
end_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().end() } else { 1 },
scope_selector_text: sst,
@@ -126,17 +129,6 @@
};
results.push(result);
}
- // don't ignore assertions after the newline, they should be treated as though they are asserting against the newline
- let last = test_against_line_scopes.last().unwrap();
- if last.char_start + last.text_len < assertion.end_char {
- let match_value = selector.does_match(last.scope.as_slice());
- let result = RangeTestResult {
- column_begin: max(last.char_start + last.text_len, assertion.begin_char),
- column_end: assertion.end_char,
- success: match_value.is_some()
- };
- results.push(result);
- }
results
}
@@ -175,7 +167,8 @@
let mut current_line_number = 1;
let mut test_against_line_number = 1;
- let mut scopes_on_line_being_tested = Vec::new();
+ let mut scopes_on_line_being_tested : Vec<ScopedText> = Vec::new();
+ let mut assertions : VecDeque<AssertionRange> = VecDeque::with_capacity(2);
let mut previous_non_assertion_line = line.to_string();
let mut assertion_failures: usize = 0;
@@ -184,7 +177,32 @@
loop { // over lines of file, starting with the header line
let mut line_only_has_assertion = false;
let mut line_has_assertion = false;
- if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line) {
+
+ // get assertion details from the current line
+ if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line, current_line_number) {
+ // add them to the end of the queue, we will process any remainders from the previous line(s) first
+ assertions.push_back(assertion);
+ }
+ let mut assertions_left_to_check = assertions.len();
+ loop { // loop over unprocessed assertion ranges
+ if assertions_left_to_check == 0 {
+ break;
+ }
+ let assertion = assertions.pop_front().unwrap();
+ assertions_left_to_check -= 1;
+
+ // assertions after the newline reference a future line, probably the next one
+ let last = &scopes_on_line_being_tested.last().unwrap();
+ if last.char_start + last.text_len < assertion.end_char {
+ assertions.push_back(AssertionRange {
+ line_number: assertion.line_number,
+ begin_char: last.char_start - assertion.begin_char, // adjust the offset of the range to start from position 0 on the next line
+ end_char: last.char_start - assertion.end_char,
+ scope_selector_text: assertion.scope_selector_text,
+ is_pure_assertion_line: false
+ });
+ }
+
let result = process_assertions(&assertion, &scopes_on_line_being_tested);
total_assertions += &assertion.end_char - &assertion.begin_char;
for failure in result.iter().filter(|r|!r.success) {
@@ -196,15 +214,18 @@
(with text {:?}) \
has scope {:?}",
assertion.scope_selector_text.trim(),
- current_line_number, test_against_line_number, failure.column_begin, failure.column_end,
+ assertion.line_number, test_against_line_number, failure.column_begin, failure.column_end,
text,
scopes_on_line_being_tested.iter().skip_while(|s|s.char_start + s.text_len <= failure.column_begin).next().unwrap_or(scopes_on_line_being_tested.last().unwrap()).scope
);
}
assertion_failures += failure.column_end - failure.column_begin;
}
- line_only_has_assertion = assertion.is_pure_assertion_line;
- line_has_assertion = true;
+
+ if assertion.line_number == current_line_number { // if the assertion range is on the line being processed
+ line_only_has_assertion = assertion.is_pure_assertion_line;
+ line_has_assertion = true;
+ }
}
if !line_only_has_assertion || parse_test_lines {
if !line_has_assertion { // ST seems to ignore lines that have assertions when calculating which line the assertion tests against but it doesn't compile (so there could be lots more problems with my logic that I haven't found yet):
|
I think a better approach, and probably the one used in Sublime, is to parse all assertions as referring to a byte range in the document, sort those, then while iterating through the file keep track of the current byte index and iterate through the assertions list with that. |
While looking at the CI failures from #166, I identified the reason why
syntest
reports failures for theGit Rebase
syntax:The syntax test file contains assertions that extend beyond the
\n
character.syntest
currently compares these assertion scope selectors against the scope stack at the new line character. But in Sublime Text, the following syntax test passes:which means, in Sublime Text, the assertions wrap onto the next line. Probably we should adapt
syntest
to also work this way.The text was updated successfully, but these errors were encountered: