diff --git a/helix-core/src/syntax/span.rs b/helix-core/src/syntax/span.rs index 56110034c333..19349c943ed0 100644 --- a/helix-core/src/syntax/span.rs +++ b/helix-core/src/syntax/span.rs @@ -139,49 +139,33 @@ impl Iterator for SpanIter { self.cursor = span.start; - // Handle all spans that share this starting point. Either subslice - // or fully consume the span. - let mut i = self.index; - let mut subslices = 0; - loop { - match self.spans.get_mut(i) { - Some(span) if span.start == self.cursor => { - self.event_queue - .push_back(HighlightStart(Highlight(span.scope))); - i += 1; - - match subslice { - Some(intersect) => { - // If this span needs to be subsliced, consume the - // left part of the subslice and leave the right. - self.range_ends.push(intersect); - span.start = intersect; - subslices += 1; - } - None => { - // If there is no subslice, consume the span. - self.range_ends.push(span.end); - self.index = i; - } - } + if let Some(intersect) = subslice { + // Handle all spans that share this starting point. Either subslice + // or fully consume the span. + let mut i = self.index; + // If this span needs to be subsliced, consume the + // left part of the subslice and leave the right. + while let Some(span) = self.spans.get_mut(i) { + if span.start != self.cursor || span.end < intersect { + break; } - _ => break, + + self.event_queue + .push_back(HighlightStart(Highlight(span.scope))); + self.range_ends.push(intersect); + span.start = intersect; + i += 1; } - } - // Ensure range-ends are sorted ascending. Ranges which start at the - // same point may be in descending order because of the assumed - // sort-order of input ranges. - self.range_ends.sort_unstable(); + let subslices = i - self.index; - // When spans are subsliced, the span Vec may need to be re-sorted - // because the `range.start` may now be greater than some `range.start` - // later in the Vec. This is not a classic "sort": we take several - // shortcuts to improve the runtime so that the sort may be done in - // time linear to the cardinality of the span Vec. Practically speaking - // the runtime is even better since we only scan from `self.index` to - // the first element of the Vec with a `range.start` after this range. - if let Some(intersect) = subslice { + // When spans are subsliced, the span Vec may need to be re-sorted + // because the `range.start` may now be greater than some `range.start` + // later in the Vec. This is not a classic "sort": we take several + // shortcuts to improve the runtime so that the sort may be done in + // time linear to the cardinality of the span Vec. Practically speaking + // the runtime is even better since we only scan from `self.index` to + // the first element of the Vec with a `range.start` after this range. let mut after = None; // Find the index of the largest span smaller than the intersect point. @@ -206,6 +190,24 @@ impl Iterator for SpanIter { } } + for span in &self.spans[self.index..] { + if span.start != self.cursor { + break; + } + + self.event_queue + .push_back(HighlightStart(Highlight(span.scope))); + self.range_ends.push(span.end); + + // If there is no subslice, consume the span. + self.index += 1; + } + + // Ensure range-ends are sorted ascending. Ranges which start at the + // same point may be in descending order because of the assumed + // sort-order of input ranges. + self.range_ends.sort_unstable(); + self.event_queue.pop_front() } } diff --git a/helix-core/src/syntax/span/test.rs b/helix-core/src/syntax/span/test.rs index 6565f93ff587..56d15f2f7bf2 100644 --- a/helix-core/src/syntax/span/test.rs +++ b/helix-core/src/syntax/span/test.rs @@ -318,3 +318,100 @@ fn empty_span_at_sublice_start() { ], ); } + +#[test] +fn intercept_in_subslice() { + use HighlightEvent::*; + /* + Input: + 3 + |-------| + 2 + |----------------| + 1 + |-----------| + + |---|---|---|---|---| + 0 1 2 3 4 5 */ + let input = vec![span!(1, 0..3), span!(2, 1..5), span!(3, 2..4)]; + + /* + Output: + 3 + |---| + 2 3 + |-------|---| + 1 2 + |-----------|-------| + + |---|---|---|---|---| + 0 1 2 3 4 5 */ + let output: Vec<_> = span_iter(input).collect(); + assert_eq!( + output, + &[ + HighlightStart(Highlight(1)), + Source { start: 0, end: 1 }, + HighlightStart(Highlight(2)), + Source { start: 1, end: 2 }, + HighlightStart(Highlight(3)), + Source { start: 2, end: 3 }, + HighlightEnd, // ends 3 + HighlightEnd, // ends 2 + HighlightEnd, // ends 1 + HighlightStart(Highlight(2)), + HighlightStart(Highlight(3)), + Source { start: 3, end: 4 }, + HighlightEnd, // ends 3 + Source { start: 4, end: 5 }, + HighlightEnd, // ends 2 + ], + ); +} + +#[test] +fn subslice_in_intercept() { + use HighlightEvent::*; + /* + Input: + 3 + |---| + 2 + |-----------| + 1 + |-----------| + + |---|---|---|---|---| + 0 1 2 3 4 5 */ + let input = vec![span!(1, 0..3), span!(2, 1..4), span!(3, 1..2)]; + + /* + Output: + 3 + |---| + 2 + |-------| + 1 2 + |-----------|---| + + |---|---|---|---| + 0 1 2 3 4 */ + let output: Vec<_> = span_iter(input).collect(); + assert_eq!( + output, + &[ + HighlightStart(Highlight(1)), + Source { start: 0, end: 1 }, + HighlightStart(Highlight(2)), + HighlightStart(Highlight(3)), + Source { start: 1, end: 2 }, + HighlightEnd, // ends 3 + Source { start: 2, end: 3 }, + HighlightEnd, // ends 2 + HighlightEnd, // ends 1 + HighlightStart(Highlight(2)), + Source { start: 3, end: 4 }, + HighlightEnd, // ends 2 + ], + ); +}