//! Collects a tree of highlighted ranges and flattens it. use std::iter; use stdx::equal_range_by; use syntax::TextRange; use crate::{HlRange, HlTag}; pub(super) struct Highlights { root: Node, } struct Node { hl_range: HlRange, nested: Vec, } impl Highlights { pub(super) fn new(range: TextRange) -> Highlights { Highlights { root: Node::new(HlRange { range, highlight: HlTag::None.into(), binding_hash: None }), } } pub(super) fn add(&mut self, hl_range: HlRange) { self.root.add(hl_range); } pub(super) fn to_vec(self) -> Vec { let mut res = Vec::new(); self.root.flatten(&mut res); res } } impl Node { fn new(hl_range: HlRange) -> Node { Node { hl_range, nested: Vec::new() } } fn add(&mut self, hl_range: HlRange) { assert!(self.hl_range.range.contains_range(hl_range.range)); // Fast path if let Some(last) = self.nested.last_mut() { if last.hl_range.range.contains_range(hl_range.range) { return last.add(hl_range); } if last.hl_range.range.end() <= hl_range.range.start() { return self.nested.push(Node::new(hl_range)); } } let overlapping = equal_range_by(&self.nested, |n| TextRange::ordering(n.hl_range.range, hl_range.range)); if overlapping.len() == 1 && self.nested[overlapping.start].hl_range.range.contains_range(hl_range.range) { return self.nested[overlapping.start].add(hl_range); } let nested = self .nested .splice(overlapping.clone(), iter::once(Node::new(hl_range))) .collect::>(); self.nested[overlapping.start].nested = nested; } fn flatten(&self, acc: &mut Vec) { let mut start = self.hl_range.range.start(); let mut nested = self.nested.iter(); loop { let next = nested.next(); let end = next.map_or(self.hl_range.range.end(), |it| it.hl_range.range.start()); if start < end { acc.push(HlRange { range: TextRange::new(start, end), highlight: self.hl_range.highlight, binding_hash: self.hl_range.binding_hash, }); } start = match next { Some(child) => { child.flatten(acc); child.hl_range.range.end() } None => break, } } } }