rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs

101 lines
2.7 KiB
Rust
Raw Normal View History

//! Collects a tree of highlighted ranges and flattens it.
use std::{cmp::Ordering, iter};
use stdx::equal_range_by;
use syntax::TextRange;
2021-01-09 11:48:15 +00:00
use crate::{HlRange, HlTag};
pub(super) struct Highlights {
root: Node,
}
struct Node {
2021-01-09 11:48:15 +00:00
hl_range: HlRange,
nested: Vec<Node>,
}
impl Highlights {
pub(super) fn new(range: TextRange) -> Highlights {
Highlights {
2021-01-09 11:48:15 +00:00
root: Node::new(HlRange { range, highlight: HlTag::None.into(), binding_hash: None }),
}
}
2021-01-09 11:48:15 +00:00
pub(super) fn add(&mut self, hl_range: HlRange) {
self.root.add(hl_range);
}
2021-01-09 11:48:15 +00:00
pub(super) fn to_vec(self) -> Vec<HlRange> {
let mut res = Vec::new();
self.root.flatten(&mut res);
res
}
}
impl Node {
2021-01-09 11:48:15 +00:00
fn new(hl_range: HlRange) -> Node {
Node { hl_range, nested: Vec::new() }
}
2021-01-09 11:48:15 +00:00
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() {
2021-01-09 11:48:15 +00:00
if last.hl_range.range.contains_range(hl_range.range) {
return last.add(hl_range);
}
2021-01-09 11:48:15 +00:00
if last.hl_range.range.end() <= hl_range.range.start() {
return self.nested.push(Node::new(hl_range));
}
}
2021-01-09 11:48:15 +00:00
let (start, len) =
equal_range_by(&self.nested, |n| ordering(n.hl_range.range, hl_range.range));
2021-01-09 11:48:15 +00:00
if len == 1 && self.nested[start].hl_range.range.contains_range(hl_range.range) {
return self.nested[start].add(hl_range);
}
let nested = self
.nested
2021-01-09 11:48:15 +00:00
.splice(start..start + len, iter::once(Node::new(hl_range)))
.collect::<Vec<_>>();
self.nested[start].nested = nested;
}
2021-01-09 11:48:15 +00:00
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();
2021-01-09 11:48:15 +00:00
let end = next.map_or(self.hl_range.range.end(), |it| it.hl_range.range.start());
if start < end {
2021-01-09 11:48:15 +00:00
acc.push(HlRange {
range: TextRange::new(start, end),
2021-01-09 11:48:15 +00:00
highlight: self.hl_range.highlight,
binding_hash: self.hl_range.binding_hash,
});
}
start = match next {
Some(child) => {
child.flatten(acc);
2021-01-09 11:48:15 +00:00
child.hl_range.range.end()
}
None => break,
}
}
}
}
pub(super) fn ordering(r1: TextRange, r2: TextRange) -> Ordering {
if r1.end() <= r2.start() {
Ordering::Less
} else if r2.end() <= r1.start() {
Ordering::Greater
} else {
Ordering::Equal
}
}