1use span::{Span, SyntaxContext};
4use syntax::{AstNode, TextRange, ast};
5use triomphe::Arc;
6
7pub use span::RealSpanMap;
8
9use crate::{HirFileId, MacroCallId, db::ExpandDatabase};
10
11pub type ExpansionSpanMap = span::SpanMap<SyntaxContext>;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum SpanMap {
16 ExpansionSpanMap(Arc<ExpansionSpanMap>),
18 RealSpanMap(Arc<RealSpanMap>),
20}
21
22#[derive(Copy, Clone)]
23pub enum SpanMapRef<'a> {
24 ExpansionSpanMap(&'a ExpansionSpanMap),
26 RealSpanMap(&'a RealSpanMap),
28}
29
30impl syntax_bridge::SpanMapper<Span> for SpanMap {
31 fn span_for(&self, range: TextRange) -> Span {
32 self.span_for_range(range)
33 }
34}
35
36impl syntax_bridge::SpanMapper<Span> for SpanMapRef<'_> {
37 fn span_for(&self, range: TextRange) -> Span {
38 self.span_for_range(range)
39 }
40}
41
42impl SpanMap {
43 pub fn span_for_range(&self, range: TextRange) -> Span {
44 match self {
45 Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()),
50 Self::RealSpanMap(span_map) => span_map.span_for_range(range),
51 }
52 }
53
54 pub fn as_ref(&self) -> SpanMapRef<'_> {
55 match self {
56 Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map),
57 Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map),
58 }
59 }
60
61 #[inline]
62 pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap {
63 match file_id {
64 HirFileId::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)),
65 HirFileId::MacroFile(m) => {
66 SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1)
67 }
68 }
69 }
70}
71
72impl SpanMapRef<'_> {
73 pub fn span_for_range(self, range: TextRange) -> Span {
74 match self {
75 Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()),
76 Self::RealSpanMap(span_map) => span_map.span_for_range(range),
77 }
78 }
79}
80
81pub(crate) fn real_span_map(
82 db: &dyn ExpandDatabase,
83 editioned_file_id: base_db::EditionedFileId,
84) -> Arc<RealSpanMap> {
85 use syntax::ast::HasModuleItem;
86 let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)];
87 let ast_id_map = db.ast_id_map(editioned_file_id.into());
88
89 let tree = db.parse(editioned_file_id).tree();
90 let item_to_entry =
105 |item: ast::Item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase());
106 pairs.extend(tree.items().map(item_to_entry));
108 tree.items().for_each(|item| match &item {
112 ast::Item::ExternBlock(it) if ast::attrs_including_inner(it).next().is_none() => {
113 if let Some(extern_item_list) = it.extern_item_list() {
114 pairs.extend(
115 extern_item_list.extern_items().map(ast::Item::from).map(item_to_entry),
116 );
117 }
118 }
119 ast::Item::Impl(it) if ast::attrs_including_inner(it).next().is_none() => {
120 if let Some(assoc_item_list) = it.assoc_item_list() {
121 pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
122 }
123 }
124 ast::Item::Module(it) if ast::attrs_including_inner(it).next().is_none() => {
125 if let Some(item_list) = it.item_list() {
126 pairs.extend(item_list.items().map(item_to_entry));
127 }
128 }
129 ast::Item::Trait(it) if ast::attrs_including_inner(it).next().is_none() => {
130 if let Some(assoc_item_list) = it.assoc_item_list() {
131 pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
132 }
133 }
134 _ => (),
135 });
136
137 Arc::new(RealSpanMap::from_file(
138 editioned_file_id.editioned_file_id(db),
139 pairs.into_boxed_slice(),
140 tree.syntax().text_range().end(),
141 ))
142}
143
144pub(crate) fn expansion_span_map(
145 db: &dyn ExpandDatabase,
146 file_id: MacroCallId,
147) -> Arc<ExpansionSpanMap> {
148 db.parse_macro_expansion(file_id).value.1
149}