Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/lib.rs')
| -rw-r--r-- | crates/hir-expand/src/lib.rs | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 8136e7f047..924f0da6bd 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -25,13 +25,16 @@ pub mod span_map; mod cfg_process; mod fixup; use attrs::collect_attrs; +use rustc_hash::FxHashMap; use triomphe::Arc; use std::{fmt, hash::Hash}; use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId}; use either::Either; -use span::{ErasedFileAstId, FileRange, HirFileIdRepr, Span, SyntaxContextData, SyntaxContextId}; +use span::{ + ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId, +}; use syntax::{ ast::{self, AstNode}, SyntaxNode, SyntaxToken, TextRange, TextSize, @@ -683,6 +686,8 @@ impl ExpansionInfo { } /// Maps the passed in file range down into a macro expansion if it is the input to a macro call. + /// + /// Note this does a linear search through the entire backing vector of the spanmap. pub fn map_range_down( &self, span: Span, @@ -793,7 +798,34 @@ impl ExpansionInfo { } } +/// Maps up the text range out of the expansion hierarchy back into the original file its from only +/// considering the root spans contained. +/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ. +pub fn map_node_range_up_rooted( + db: &dyn ExpandDatabase, + exp_map: &ExpansionSpanMap, + range: TextRange, +) -> Option<FileRange> { + let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root()); + let Span { range, anchor, ctx: _ } = spans.next()?; + let mut start = range.start(); + let mut end = range.end(); + + for span in spans { + if span.anchor != anchor { + return None; + } + start = start.min(span.range.start()); + end = end.max(span.range.end()); + } + let anchor_offset = + db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start(); + Some(FileRange { file_id: anchor.file_id, range: TextRange::new(start, end) + anchor_offset }) +} + /// Maps up the text range out of the expansion hierarchy back into the original file its from. +/// +/// this will return `None` if any anchors or syntax contexts differ. pub fn map_node_range_up( db: &dyn ExpandDatabase, exp_map: &ExpansionSpanMap, @@ -819,6 +851,29 @@ pub fn map_node_range_up( )) } +/// Maps up the text range out of the expansion hierarchy back into the original file its from. +/// This version will aggregate the ranges of all spans with the same anchor and syntax context. +pub fn map_node_range_up_aggregated( + db: &dyn ExpandDatabase, + exp_map: &ExpansionSpanMap, + range: TextRange, +) -> FxHashMap<(SpanAnchor, SyntaxContextId), TextRange> { + let mut map = FxHashMap::default(); + for span in exp_map.spans_for_range(range) { + let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range); + *range = TextRange::new( + range.start().min(span.range.start()), + range.end().max(span.range.end()), + ); + } + for ((anchor, _), range) in &mut map { + let anchor_offset = + db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start(); + *range += anchor_offset; + } + map +} + /// Looks up the span at the given offset. pub fn span_for_offset( db: &dyn ExpandDatabase, |