remove old macro support
This commit is contained in:
parent
ee3cf6172b
commit
6955e392f8
8 changed files with 94 additions and 260 deletions
|
@ -4,12 +4,10 @@ use ra_syntax::{SyntaxNode, TreeArc, SourceFile};
|
|||
use ra_db::{SourceDatabase, salsa, FileId};
|
||||
|
||||
use crate::{
|
||||
MacroCallId, HirFileId,
|
||||
SourceFileItems, SourceItemId, Crate, Module, HirInterner,
|
||||
HirFileId, SourceFileItems, SourceItemId, Crate, Module, HirInterner,
|
||||
Function, FnSignature, ExprScopes, TypeAlias,
|
||||
Struct, Enum, StructField,
|
||||
Const, ConstSignature, Static,
|
||||
macros::MacroExpansion,
|
||||
nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap},
|
||||
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig},
|
||||
adt::{StructData, EnumData},
|
||||
|
@ -23,9 +21,6 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> {
|
|||
#[salsa::invoke(HirFileId::hir_parse)]
|
||||
fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>;
|
||||
|
||||
#[salsa::invoke(crate::macros::expand_macro_invocation)]
|
||||
fn expand_macro_invocation(&self, invoc: MacroCallId) -> Option<Arc<MacroExpansion>>;
|
||||
|
||||
#[salsa::invoke(crate::adt::StructData::struct_data_query)]
|
||||
fn struct_data(&self, s: Struct) -> Arc<StructData>;
|
||||
|
||||
|
|
|
@ -89,17 +89,31 @@ impl HirFileId {
|
|||
) -> TreeArc<SourceFile> {
|
||||
match file_id.0 {
|
||||
HirFileIdRepr::File(file_id) => db.parse(file_id),
|
||||
HirFileIdRepr::Macro(m) => {
|
||||
if let Some(exp) = db.expand_macro_invocation(m) {
|
||||
return exp.file();
|
||||
}
|
||||
HirFileIdRepr::Macro(macro_call_id) => {
|
||||
// returning an empty string looks fishy...
|
||||
SourceFile::parse("")
|
||||
parse_macro(db, macro_call_id).unwrap_or_else(|| SourceFile::parse(""))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_macro(
|
||||
db: &impl PersistentHirDatabase,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> Option<TreeArc<SourceFile>> {
|
||||
let loc = macro_call_id.loc(db);
|
||||
let syntax = db.file_item(loc.source_item_id);
|
||||
let macro_call = ast::MacroCall::cast(&syntax).unwrap();
|
||||
let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?;
|
||||
|
||||
let def_map = db.crate_def_map(loc.module.krate);
|
||||
let (krate, macro_id) = def_map.resolve_macro(macro_call_id)?;
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let macro_rules = &def_map[macro_id];
|
||||
let tt = macro_rules.expand(¯o_arg).ok()?;
|
||||
Some(mbe::token_tree_to_ast_item_list(&tt))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum HirFileIdRepr {
|
||||
File(FileId),
|
||||
|
@ -373,7 +387,6 @@ impl SourceFileItems {
|
|||
impl std::ops::Index<SourceFileItemId> for SourceFileItems {
|
||||
type Output = SyntaxNodePtr;
|
||||
fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr {
|
||||
eprintln!("invalid SourceFileItemId({:?}) for file({:?})", idx, self.file_id);
|
||||
&self.arena[idx]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ mod path;
|
|||
pub mod source_binder;
|
||||
|
||||
mod ids;
|
||||
mod macros;
|
||||
mod name;
|
||||
mod nameres;
|
||||
mod adt;
|
||||
|
@ -53,7 +52,6 @@ pub use self::{
|
|||
path::{Path, PathKind},
|
||||
name::Name,
|
||||
ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner},
|
||||
macros::{MacroDef, MacroInput, MacroExpansion},
|
||||
nameres::{PerNs, Namespace},
|
||||
ty::{Ty, Substs, display::HirDisplay},
|
||||
impl_block::{ImplBlock, ImplItem},
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
/// Machinery for macro expansion.
|
||||
///
|
||||
/// One of the more complicated things about macros is managing the source code
|
||||
/// that is produced after expansion. See `HirFileId` and `MacroCallId` for how
|
||||
/// do we do that.
|
||||
///
|
||||
/// When the file-management question is resolved, all that is left is a
|
||||
/// token-tree-to-token-tree transformation plus hygiene. We don't have either of
|
||||
/// those yet, so all macros are string based at the moment!
|
||||
use std::sync::Arc;
|
||||
|
||||
use ra_syntax::{
|
||||
TextRange, TextUnit, SourceFile, AstNode, SyntaxNode, TreeArc, SyntaxNodePtr,
|
||||
ast,
|
||||
};
|
||||
|
||||
use crate::{MacroCallId, PersistentHirDatabase};
|
||||
|
||||
// Hard-coded defs for now :-(
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum MacroDef {
|
||||
Vec,
|
||||
}
|
||||
|
||||
impl MacroDef {
|
||||
/// Expands macro call, returning the expansion and offset to be used to
|
||||
/// convert ranges between expansion and original source.
|
||||
pub fn ast_expand(macro_call: &ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> {
|
||||
let (def, input) = MacroDef::from_call(macro_call)?;
|
||||
let exp = def.expand(input)?;
|
||||
let off = macro_call.token_tree()?.syntax().range().start();
|
||||
Some((off, exp))
|
||||
}
|
||||
|
||||
fn from_call(macro_call: &ast::MacroCall) -> Option<(MacroDef, MacroInput)> {
|
||||
let def = {
|
||||
let path = macro_call.path()?;
|
||||
let name_ref = path.segment()?.name_ref()?;
|
||||
if name_ref.text() == "vec" {
|
||||
MacroDef::Vec
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let input = {
|
||||
let arg = macro_call.token_tree()?.syntax();
|
||||
MacroInput { text: arg.text().to_string() }
|
||||
};
|
||||
Some((def, input))
|
||||
}
|
||||
|
||||
fn expand(self, input: MacroInput) -> Option<MacroExpansion> {
|
||||
match self {
|
||||
MacroDef::Vec => self.expand_vec(input),
|
||||
}
|
||||
}
|
||||
fn expand_vec(self, input: MacroInput) -> Option<MacroExpansion> {
|
||||
let text = format!(r"fn dummy() {{ {}; }}", input.text);
|
||||
let file = SourceFile::parse(&text);
|
||||
let array_expr = file.syntax().descendants().find_map(ast::ArrayExpr::cast)?;
|
||||
let ptr = SyntaxNodePtr::new(array_expr.syntax());
|
||||
let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text));
|
||||
let ranges_map = vec![(src_range, array_expr.syntax().range())];
|
||||
let res = MacroExpansion { text, ranges_map, ptr };
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MacroInput {
|
||||
// Should be token trees
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MacroExpansion {
|
||||
/// The result of macro expansion. Should be token tree as well.
|
||||
text: String,
|
||||
/// Correspondence between ranges in the original source code and ranges in
|
||||
/// the macro.
|
||||
ranges_map: Vec<(TextRange, TextRange)>,
|
||||
/// Implementation detail: internally, a macro is expanded to the whole file,
|
||||
/// even if it is an expression. This `ptr` selects the actual expansion from
|
||||
/// the expanded file.
|
||||
ptr: SyntaxNodePtr,
|
||||
}
|
||||
|
||||
impl MacroExpansion {
|
||||
// FIXME: does not really make sense, macro expansion is not necessary a
|
||||
// whole file. See `MacroExpansion::ptr` as well.
|
||||
pub(crate) fn file(&self) -> TreeArc<SourceFile> {
|
||||
SourceFile::parse(&self.text)
|
||||
}
|
||||
|
||||
pub fn syntax(&self) -> TreeArc<SyntaxNode> {
|
||||
self.ptr.to_node(&self.file()).to_owned()
|
||||
}
|
||||
/// Maps range in the source code to the range in the expanded code.
|
||||
pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
|
||||
for (s_range, t_range) in self.ranges_map.iter() {
|
||||
if src_range.is_subrange(&s_range) {
|
||||
let src_at_zero_range = src_range - src_range.start();
|
||||
let src_range_offset = src_range.start() - s_range.start();
|
||||
let src_range = src_at_zero_range + src_range_offset + t_range.start();
|
||||
return Some(src_range);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
/// Maps range in the expanded code to the range in the source code.
|
||||
pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
|
||||
for (s_range, t_range) in self.ranges_map.iter() {
|
||||
if tgt_range.is_subrange(&t_range) {
|
||||
let tgt_at_zero_range = tgt_range - tgt_range.start();
|
||||
let tgt_range_offset = tgt_range.start() - t_range.start();
|
||||
let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
|
||||
return Some(src_range);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expand_macro_invocation(
|
||||
db: &impl PersistentHirDatabase,
|
||||
invoc: MacroCallId,
|
||||
) -> Option<Arc<MacroExpansion>> {
|
||||
let loc = invoc.loc(db);
|
||||
let syntax = db.file_item(loc.source_item_id);
|
||||
let macro_call = ast::MacroCall::cast(&syntax).unwrap();
|
||||
|
||||
let (def, input) = MacroDef::from_call(macro_call)?;
|
||||
def.expand(input).map(Arc::new)
|
||||
}
|
|
@ -57,7 +57,7 @@ use test_utils::tested_by;
|
|||
use crate::{
|
||||
ModuleDef, Name, Crate, Module, Problem,
|
||||
PersistentHirDatabase, Path, PathKind, HirFileId,
|
||||
ids::{SourceItemId, SourceFileItemId},
|
||||
ids::{SourceItemId, SourceFileItemId, MacroCallId},
|
||||
};
|
||||
|
||||
pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap};
|
||||
|
@ -76,7 +76,9 @@ pub struct CrateDefMap {
|
|||
extern_prelude: FxHashMap<Name, ModuleDef>,
|
||||
root: CrateModuleId,
|
||||
modules: Arena<CrateModuleId, ModuleData>,
|
||||
public_macros: FxHashMap<Name, mbe::MacroRules>,
|
||||
macros: Arena<CrateMacroId, mbe::MacroRules>,
|
||||
public_macros: FxHashMap<Name, CrateMacroId>,
|
||||
macro_resolutions: FxHashMap<MacroCallId, (Crate, CrateMacroId)>,
|
||||
problems: CrateDefMapProblems,
|
||||
}
|
||||
|
||||
|
@ -87,9 +89,21 @@ impl std::ops::Index<CrateModuleId> for CrateDefMap {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<CrateMacroId> for CrateDefMap {
|
||||
type Output = mbe::MacroRules;
|
||||
fn index(&self, id: CrateMacroId) -> &mbe::MacroRules {
|
||||
&self.macros[id]
|
||||
}
|
||||
}
|
||||
|
||||
/// An ID of a macro, **local** to a specific crate
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct CrateMacroId(RawId);
|
||||
impl_arena_id!(CrateMacroId);
|
||||
|
||||
/// An ID of a module, **local** to a specific crate
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct CrateModuleId(RawId);
|
||||
pub(crate) struct CrateModuleId(RawId);
|
||||
impl_arena_id!(CrateModuleId);
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
|
@ -192,7 +206,9 @@ impl CrateDefMap {
|
|||
prelude: None,
|
||||
root,
|
||||
modules,
|
||||
macros: Arena::default(),
|
||||
public_macros: FxHashMap::default(),
|
||||
macro_resolutions: FxHashMap::default(),
|
||||
problems: CrateDefMapProblems::default(),
|
||||
}
|
||||
};
|
||||
|
@ -221,6 +237,13 @@ impl CrateDefMap {
|
|||
&self.extern_prelude
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_macro(
|
||||
&self,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> Option<(Crate, CrateMacroId)> {
|
||||
self.macro_resolutions.get(¯o_call_id).map(|&it| it)
|
||||
}
|
||||
|
||||
pub(crate) fn find_module_by_source(
|
||||
&self,
|
||||
file_id: HirFileId,
|
||||
|
|
|
@ -6,13 +6,13 @@ use ra_db::FileId;
|
|||
|
||||
use crate::{
|
||||
Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
|
||||
PersistentHirDatabase, HirFileId, Name, Path, Problem,
|
||||
PersistentHirDatabase, HirFileId, Name, Path, Problem, Crate,
|
||||
KnownName,
|
||||
nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw},
|
||||
ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId},
|
||||
};
|
||||
|
||||
use super::{CrateDefMap, CrateModuleId, ModuleData};
|
||||
use super::{CrateDefMap, CrateModuleId, ModuleData, CrateMacroId};
|
||||
|
||||
pub(super) fn collect_defs(
|
||||
db: &impl PersistentHirDatabase,
|
||||
|
@ -52,7 +52,7 @@ struct DefCollector<DB> {
|
|||
glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
|
||||
unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
|
||||
unexpanded_macros: Vec<(CrateModuleId, MacroCallId, Path, tt::Subtree)>,
|
||||
global_macro_scope: FxHashMap<Name, mbe::MacroRules>,
|
||||
global_macro_scope: FxHashMap<Name, CrateMacroId>,
|
||||
}
|
||||
|
||||
impl<'a, DB> DefCollector<&'a DB>
|
||||
|
@ -95,10 +95,11 @@ where
|
|||
|
||||
fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) {
|
||||
if let Ok(rules) = mbe::MacroRules::parse(tt) {
|
||||
let macro_id = self.def_map.macros.alloc(rules);
|
||||
if export {
|
||||
self.def_map.public_macros.insert(name.clone(), rules.clone());
|
||||
self.def_map.public_macros.insert(name.clone(), macro_id);
|
||||
}
|
||||
self.global_macro_scope.insert(name, rules);
|
||||
self.global_macro_scope.insert(name, macro_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,6 +296,7 @@ where
|
|||
fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
|
||||
let mut resolved = Vec::new();
|
||||
let mut res = ReachedFixedPoint::Yes;
|
||||
macros.retain(|(module_id, call_id, path, tt)| {
|
||||
if path.segments.len() != 2 {
|
||||
return true;
|
||||
|
@ -308,19 +310,16 @@ where
|
|||
Some(it) => it,
|
||||
_ => return true,
|
||||
};
|
||||
res = ReachedFixedPoint::No;
|
||||
let def_map = self.db.crate_def_map(krate);
|
||||
let rules = def_map.public_macros.get(&path.segments[1].name).cloned();
|
||||
resolved.push((*module_id, *call_id, rules, tt.clone()));
|
||||
if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() {
|
||||
resolved.push((*module_id, *call_id, (krate, macro_id), tt.clone()));
|
||||
}
|
||||
false
|
||||
});
|
||||
let res = if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No };
|
||||
|
||||
for (module_id, macro_call_id, rules, arg) in resolved {
|
||||
if let Some(rules) = rules {
|
||||
if let Ok(tt) = rules.expand(&arg) {
|
||||
self.collect_macro_expansion(module_id, macro_call_id, tt);
|
||||
}
|
||||
}
|
||||
for (module_id, macro_call_id, macro_def_id, arg) in resolved {
|
||||
self.collect_macro_expansion(module_id, macro_call_id, macro_def_id, arg);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
@ -329,20 +328,32 @@ where
|
|||
&mut self,
|
||||
module_id: CrateModuleId,
|
||||
macro_call_id: MacroCallId,
|
||||
expansion: tt::Subtree,
|
||||
macro_def_id: (Crate, CrateMacroId),
|
||||
macro_arg: tt::Subtree,
|
||||
) {
|
||||
// XXX: this **does not** go through a database, because we can't
|
||||
// identify macro_call without adding the whole state of name resolution
|
||||
// as a parameter to the query.
|
||||
//
|
||||
// So, we run the queries "manually" and we must ensure that
|
||||
// `db.hir_parse(macro_call_id)` returns the same source_file.
|
||||
let file_id: HirFileId = macro_call_id.into();
|
||||
let source_file = mbe::token_tree_to_ast_item_list(&expansion);
|
||||
let (macro_krate, macro_id) = macro_def_id;
|
||||
let dm;
|
||||
let rules = if macro_krate == self.def_map.krate {
|
||||
&self.def_map[macro_id]
|
||||
} else {
|
||||
dm = self.db.crate_def_map(macro_krate);
|
||||
&dm[macro_id]
|
||||
};
|
||||
if let Ok(expansion) = rules.expand(¯o_arg) {
|
||||
self.def_map.macro_resolutions.insert(macro_call_id, macro_def_id);
|
||||
// XXX: this **does not** go through a database, because we can't
|
||||
// identify macro_call without adding the whole state of name resolution
|
||||
// as a parameter to the query.
|
||||
//
|
||||
// So, we run the queries "manually" and we must ensure that
|
||||
// `db.hir_parse(macro_call_id)` returns the same source_file.
|
||||
let file_id: HirFileId = macro_call_id.into();
|
||||
let source_file = mbe::token_tree_to_ast_item_list(&expansion);
|
||||
|
||||
let raw_items = raw::RawItems::from_source_file(&source_file, file_id);
|
||||
ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items }
|
||||
.collect(raw_items.items())
|
||||
let raw_items = raw::RawItems::from_source_file(&source_file, file_id);
|
||||
ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items }
|
||||
.collect(raw_items.items())
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(self) -> CrateDefMap {
|
||||
|
@ -486,12 +497,15 @@ where
|
|||
|
||||
// Case 2: try to expand macro_rules from this crate, triggering
|
||||
// recursive item collection.
|
||||
if let Some(rules) =
|
||||
if let Some(¯o_id) =
|
||||
mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name))
|
||||
{
|
||||
if let Ok(tt) = rules.expand(&mac.arg) {
|
||||
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, tt);
|
||||
}
|
||||
self.def_collector.collect_macro_expansion(
|
||||
self.module_id,
|
||||
macro_call_id,
|
||||
(self.def_collector.def_map.krate, macro_id),
|
||||
mac.arg.clone(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,55 +1,13 @@
|
|||
use ra_db::SourceDatabase;
|
||||
use ra_syntax::{
|
||||
SyntaxNode, AstNode, SourceFile,
|
||||
ast, algo::find_covering_node,
|
||||
};
|
||||
use ra_syntax::AstNode;
|
||||
|
||||
use crate::{
|
||||
TextRange, FileRange,
|
||||
db::RootDatabase,
|
||||
};
|
||||
|
||||
// FIXME: restore macro support
|
||||
pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
|
||||
let source_file = db.parse(frange.file_id);
|
||||
if let Some(range) = extend_selection_in_macro(db, &source_file, frange) {
|
||||
return range;
|
||||
}
|
||||
ra_ide_api_light::extend_selection(source_file.syntax(), frange.range).unwrap_or(frange.range)
|
||||
}
|
||||
|
||||
fn extend_selection_in_macro(
|
||||
_db: &RootDatabase,
|
||||
source_file: &SourceFile,
|
||||
frange: FileRange,
|
||||
) -> Option<TextRange> {
|
||||
let macro_call = find_macro_call(source_file.syntax(), frange.range)?;
|
||||
let (off, exp) = hir::MacroDef::ast_expand(macro_call)?;
|
||||
let dst_range = exp.map_range_forward(frange.range - off)?;
|
||||
let dst_range = ra_ide_api_light::extend_selection(&exp.syntax(), dst_range)?;
|
||||
let src_range = exp.map_range_back(dst_range)? + off;
|
||||
Some(src_range)
|
||||
}
|
||||
|
||||
fn find_macro_call(node: &SyntaxNode, range: TextRange) -> Option<&ast::MacroCall> {
|
||||
find_covering_node(node, range).ancestors().find_map(ast::MacroCall::cast)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ra_syntax::TextRange;
|
||||
|
||||
use crate::mock_analysis::single_file_with_range;
|
||||
|
||||
#[test]
|
||||
fn extend_selection_inside_macros() {
|
||||
let (analysis, frange) = single_file_with_range(
|
||||
"
|
||||
fn main() {
|
||||
vec![foo(|x| <|>x<|>)];
|
||||
}
|
||||
",
|
||||
);
|
||||
let r = analysis.extend_selection(frange).unwrap();
|
||||
assert_eq!(r, TextRange::from_to(50.into(), 55.into()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ra_syntax::{ast, AstNode,};
|
||||
use ra_syntax::AstNode;
|
||||
use ra_db::SourceDatabase;
|
||||
|
||||
use crate::{
|
||||
|
@ -8,37 +8,5 @@ use crate::{
|
|||
|
||||
pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
|
||||
let source_file = db.parse(file_id);
|
||||
let mut res = ra_ide_api_light::highlight(source_file.syntax());
|
||||
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
|
||||
if let Some((off, exp)) = hir::MacroDef::ast_expand(macro_call) {
|
||||
let mapped_ranges =
|
||||
ra_ide_api_light::highlight(&exp.syntax()).into_iter().filter_map(|r| {
|
||||
let mapped_range = exp.map_range_back(r.range)?;
|
||||
let res = HighlightedRange { range: mapped_range + off, tag: r.tag };
|
||||
Some(res)
|
||||
});
|
||||
res.extend(mapped_ranges);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::mock_analysis::single_file;
|
||||
|
||||
use insta::assert_debug_snapshot_matches;
|
||||
|
||||
#[test]
|
||||
fn highlights_code_inside_macros() {
|
||||
let (analysis, file_id) = single_file(
|
||||
"
|
||||
fn main() {
|
||||
vec![{ let x = 92; x}];
|
||||
}
|
||||
",
|
||||
);
|
||||
let highlights = analysis.highlight(file_id).unwrap();
|
||||
assert_debug_snapshot_matches!("highlights_code_inside_macros", &highlights);
|
||||
}
|
||||
ra_ide_api_light::highlight(source_file.syntax())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue