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; /// Spanmap for a macro file or a real file #[derive(Clone, Debug)] pub enum SpanMap { /// Spanmap for a macro file ExpansionSpanMap(Arc), /// Spanmap for a real file RealSpanMap(Arc), } #[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 for SpanMap { fn span_for(&self, range: TextRange) -> SpanData { self.span_for_range(range) } } impl mbe::SpanMapper for SpanMapRef<'_> { fn span_for(&self, range: TextRange) -> SpanData { self.span_for_range(range) } } impl mbe::SpanMapper 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 { /// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id). pub fn absolute(file_id: FileId) -> Self { 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, } } }