rust/crates/span/src/map.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

82 lines
2.9 KiB
Rust
Raw Normal View History

2023-12-18 13:30:41 +01:00
//! A map that maps a span to every position in a file. Usually maps a span to some range of positions.
//! Allows bidirectional lookup.
2021-05-24 21:47:01 +02:00
2021-08-21 18:06:03 +02:00
use std::hash::Hash;
2023-12-08 11:26:22 +01:00
use stdx::{always, itertools::Itertools};
2023-11-28 10:55:21 +01:00
use syntax::{TextRange, TextSize};
/// Maps absolute text ranges for the corresponding file to the relevant span data.
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
2023-12-18 13:30:41 +01:00
pub struct SpanMap<S> {
2023-11-28 10:55:21 +01:00
spans: Vec<(TextSize, S)>,
2023-12-18 13:30:41 +01:00
// FIXME: Should be
// spans: Vec<(TextSize, crate::SyntaxContextId)>,
2021-05-24 18:43:42 +02:00
}
2023-12-18 13:30:41 +01:00
impl<S: Copy> SpanMap<S> {
2023-12-03 18:50:29 +01:00
/// Creates a new empty [`SpanMap`].
pub fn empty() -> Self {
2023-11-28 10:55:21 +01:00
Self { spans: Vec::new() }
2021-05-24 18:43:42 +02:00
}
2023-12-03 18:50:29 +01:00
/// Finalizes the [`SpanMap`], shrinking its backing storage and validating that the offsets are
/// in order.
pub fn finish(&mut self) {
2023-12-08 11:26:22 +01:00
always!(
self.spans.iter().tuple_windows().all(|(a, b)| a.0 < b.0),
"spans are not in order"
);
2023-11-28 10:55:21 +01:00
self.spans.shrink_to_fit();
}
2023-12-03 18:50:29 +01:00
/// Pushes a new span onto the [`SpanMap`].
pub fn push(&mut self, offset: TextSize, span: S) {
2023-12-08 11:26:22 +01:00
if cfg!(debug_assertions) {
if let Some(&(last_offset, _)) = self.spans.last() {
assert!(
last_offset < offset,
"last_offset({last_offset:?}) must be smaller than offset({offset:?})"
);
}
}
2023-11-28 10:55:21 +01:00
self.spans.push((offset, span));
2021-05-24 18:43:42 +02:00
}
2023-12-03 18:50:29 +01:00
/// Returns all [`TextRange`]s that correspond to the given span.
///
/// Note this does a linear search through the entire backing vector.
2023-12-18 13:30:41 +01:00
pub fn ranges_with_span(&self, span: S) -> impl Iterator<Item = TextRange> + '_
where
S: Eq,
{
2023-12-05 15:42:39 +01:00
// FIXME: This should ignore the syntax context!
2023-11-28 10:55:21 +01:00
self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
if s != span {
return None;
}
let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
Some(TextRange::new(start, end))
})
2021-05-24 18:43:42 +02:00
}
2023-12-03 18:50:29 +01:00
/// Returns the span at the given position.
2023-11-28 10:55:21 +01:00
pub fn span_at(&self, offset: TextSize) -> S {
let entry = self.spans.partition_point(|&(it, _)| it <= offset);
self.spans[entry].1
2023-11-24 16:38:48 +01:00
}
2023-12-03 18:50:29 +01:00
/// Returns the spans associated with the given range.
/// In other words, this will return all spans that correspond to all offsets within the given range.
pub fn spans_for_range(&self, range: TextRange) -> impl Iterator<Item = S> + '_ {
2023-11-28 10:55:21 +01:00
let (start, end) = (range.start(), range.end());
let start_entry = self.spans.partition_point(|&(it, _)| it <= start);
let end_entry = self.spans[start_entry..].partition_point(|&(it, _)| it <= end); // FIXME: this might be wrong?
(&self.spans[start_entry..][..end_entry]).iter().map(|&(_, s)| s)
}
pub fn iter(&self) -> impl Iterator<Item = (TextSize, S)> + '_ {
self.spans.iter().copied()
}
2021-05-24 18:43:42 +02:00
}