mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-08 11:18:48 +00:00
92 lines
2.6 KiB
Rust
92 lines
2.6 KiB
Rust
//! 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<Node>,
|
|
}
|
|
|
|
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<HlRange> {
|
|
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::<Vec<_>>();
|
|
self.nested[overlapping.start].nested = nested;
|
|
}
|
|
|
|
fn flatten(&self, acc: &mut Vec<HlRange>) {
|
|
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,
|
|
}
|
|
}
|
|
}
|
|
}
|