2023-11-24 09:38:48 -06:00
|
|
|
use base_db::{
|
|
|
|
span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
|
|
|
|
FileId,
|
|
|
|
};
|
|
|
|
use mbe::TokenMap;
|
|
|
|
use syntax::{ast::HasModuleItem, AstNode, TextRange, TextSize};
|
|
|
|
use triomphe::Arc;
|
|
|
|
|
|
|
|
use crate::db::ExpandDatabase;
|
|
|
|
|
|
|
|
pub type ExpansionSpanMap = TokenMap<SpanData>;
|
|
|
|
|
|
|
|
/// Spanmap for a macro file or a real file
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub enum SpanMap {
|
|
|
|
/// Spanmap for a macro file
|
|
|
|
ExpansionSpanMap(Arc<ExpansionSpanMap>),
|
|
|
|
/// Spanmap for a real file
|
|
|
|
RealSpanMap(Arc<RealSpanMap>),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum SpanMapRef<'a> {
|
|
|
|
/// Spanmap for a macro file
|
|
|
|
ExpansionSpanMap(&'a ExpansionSpanMap),
|
|
|
|
/// Spanmap for a real file
|
|
|
|
RealSpanMap(&'a RealSpanMap),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl mbe::SpanMapper<SpanData> for SpanMap {
|
|
|
|
fn span_for(&self, range: TextRange) -> SpanData {
|
|
|
|
self.span_for_range(range)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl mbe::SpanMapper<SpanData> for SpanMapRef<'_> {
|
|
|
|
fn span_for(&self, range: TextRange) -> SpanData {
|
|
|
|
self.span_for_range(range)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl mbe::SpanMapper<SpanData> for RealSpanMap {
|
|
|
|
fn span_for(&self, range: TextRange) -> SpanData {
|
|
|
|
self.span_for_range(range)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SpanMap {
|
|
|
|
pub fn span_for_range(&self, range: TextRange) -> SpanData {
|
|
|
|
match self {
|
|
|
|
Self::ExpansionSpanMap(span_map) => span_map.span_for_range(range),
|
|
|
|
Self::RealSpanMap(span_map) => span_map.span_for_range(range),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn as_ref(&self) -> SpanMapRef<'_> {
|
|
|
|
match self {
|
|
|
|
Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map),
|
|
|
|
Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SpanMapRef<'_> {
|
|
|
|
pub fn span_for_range(self, range: TextRange) -> SpanData {
|
|
|
|
match self {
|
|
|
|
Self::ExpansionSpanMap(span_map) => span_map.span_for_range(range),
|
|
|
|
Self::RealSpanMap(span_map) => span_map.span_for_range(range),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
|
|
|
pub struct RealSpanMap {
|
|
|
|
file_id: FileId,
|
|
|
|
/// Invariant: Sorted vec over TextSize
|
|
|
|
// FIXME: SortedVec<(TextSize, ErasedFileAstId)>?
|
|
|
|
pairs: Box<[(TextSize, ErasedFileAstId)]>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RealSpanMap {
|
2023-11-25 10:10:18 -06:00
|
|
|
/// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id).
|
|
|
|
pub fn absolute(file_id: FileId) -> Self {
|
2023-11-24 09:38:48 -06:00
|
|
|
RealSpanMap { file_id, pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]) }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_file(db: &dyn ExpandDatabase, file_id: FileId) -> Self {
|
|
|
|
let mut pairs = vec![(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)];
|
|
|
|
let ast_id_map = db.ast_id_map(file_id.into());
|
|
|
|
pairs.extend(
|
|
|
|
db.parse(file_id)
|
|
|
|
.tree()
|
|
|
|
.items()
|
|
|
|
.map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())),
|
|
|
|
);
|
|
|
|
RealSpanMap { file_id, pairs: pairs.into_boxed_slice() }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn span_for_range(&self, range: TextRange) -> SpanData {
|
|
|
|
let start = range.start();
|
|
|
|
let idx = self
|
|
|
|
.pairs
|
|
|
|
.binary_search_by(|&(it, _)| it.cmp(&start).then(std::cmp::Ordering::Less))
|
|
|
|
.unwrap_err();
|
|
|
|
let (offset, ast_id) = self.pairs[idx - 1];
|
|
|
|
SpanData {
|
|
|
|
range: range - offset,
|
|
|
|
anchor: SpanAnchor { file_id: self.file_id, ast_id },
|
|
|
|
ctx: SyntaxContextId::ROOT,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|