aboutsummaryrefslogtreecommitdiff
path: root/helix-core
diff options
context:
space:
mode:
Diffstat (limited to 'helix-core')
-rw-r--r--helix-core/src/syntax.rs119
1 files changed, 119 insertions, 0 deletions
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index 42c275d3..d03ca6bf 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -1652,6 +1652,125 @@ fn shrink_and_clear<T>(vec: &mut Vec<T>, capacity: usize) {
vec.clear();
}
+pub struct Merge<I> {
+ iter: I,
+ spans: Box<dyn Iterator<Item = (usize, std::ops::Range<usize>)>>,
+
+ next_event: Option<HighlightEvent>,
+ next_span: Option<(usize, std::ops::Range<usize>)>,
+
+ queue: Vec<HighlightEvent>,
+}
+
+/// Merge a list of spans into the highlight event stream.
+pub fn merge<I: Iterator<Item = HighlightEvent>>(
+ iter: I,
+ spans: Vec<(usize, std::ops::Range<usize>)>,
+) -> Merge<I> {
+ let spans = Box::new(spans.into_iter());
+ let mut merge = Merge {
+ iter,
+ spans,
+ next_event: None,
+ next_span: None,
+ queue: Vec::new(),
+ };
+ merge.next_event = merge.iter.next();
+ merge.next_span = merge.spans.next();
+ merge
+}
+
+impl<I: Iterator<Item = HighlightEvent>> Iterator for Merge<I> {
+ type Item = HighlightEvent;
+ fn next(&mut self) -> Option<Self::Item> {
+ use HighlightEvent::*;
+ if let Some(event) = self.queue.pop() {
+ return Some(event);
+ }
+
+ loop {
+ match (self.next_event, &self.next_span) {
+ // this happens when range is partially or fully offscreen
+ (Some(Source { start, end }), Some((span, range))) if start > range.start => {
+ if start > range.end {
+ self.next_span = self.spans.next();
+ } else {
+ self.next_span = Some((*span, start..range.end));
+ };
+ }
+ _ => break,
+ }
+ }
+
+ match (self.next_event, &self.next_span) {
+ (Some(HighlightStart(i)), _) => {
+ self.next_event = self.iter.next();
+ Some(HighlightStart(i))
+ }
+ (Some(HighlightEnd), _) => {
+ self.next_event = self.iter.next();
+ Some(HighlightEnd)
+ }
+ (Some(Source { start, end }), Some((span, range))) if start < range.start => {
+ let intersect = range.start.min(end);
+ let event = Source {
+ start,
+ end: intersect,
+ };
+
+ if end == intersect {
+ // the event is complete
+ self.next_event = self.iter.next();
+ } else {
+ // subslice the event
+ self.next_event = Some(Source {
+ start: intersect,
+ end,
+ });
+ };
+
+ Some(event)
+ }
+ (Some(Source { start, end }), Some((span, range))) if start == range.start => {
+ let intersect = range.end.min(end);
+ let event = HighlightStart(Highlight(*span));
+
+ // enqueue in reverse order
+ self.queue.push(HighlightEnd);
+ self.queue.push(Source {
+ start,
+ end: intersect,
+ });
+
+ if end == intersect {
+ // the event is complete
+ self.next_event = self.iter.next();
+ } else {
+ // subslice the event
+ self.next_event = Some(Source {
+ start: intersect,
+ end,
+ });
+ };
+
+ if intersect == range.end {
+ self.next_span = self.spans.next();
+ } else {
+ self.next_span = Some((*span, intersect..range.end));
+ }
+
+ Some(event)
+ }
+ (Some(event), None) => {
+ self.next_event = self.iter.next();
+ Some(event)
+ }
+ (None, None) => None,
+ e => unreachable!("{:?}", e),
+ }
+ }
+}
+
#[test]
fn test_parser() {
let highlight_names: Vec<String> = [