aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-11-16 13:49:26 +0000
committerAleksey Kladov <[email protected]>2019-11-16 14:00:54 +0000
commit2eaa8c94a8a6b5cd86139c5e010ae95268b28658 (patch)
treeba86bbb6e4e82fb1a85ab6a830173c00f2415d5e /crates
parent786cae520ad62c9a0a13f5ab18e5bd7e5b0c9825 (diff)
Goto definition works inside macros
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/source_binder.rs23
-rw-r--r--crates/ra_hir_expand/src/lib.rs9
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs50
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs6
4 files changed, 77 insertions, 11 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 5764dc26d..75a467fb3 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -11,7 +11,7 @@ use hir_def::{
11 expr::{ExprId, PatId}, 11 expr::{ExprId, PatId},
12 path::known, 12 path::known,
13}; 13};
14use hir_expand::{name::AsName, Source}; 14use hir_expand::{name::AsName, AstId, MacroCallId, MacroCallLoc, MacroFileKind, Source};
15use ra_syntax::{ 15use ra_syntax::{
16 ast::{self, AstNode}, 16 ast::{self, AstNode},
17 match_ast, AstPtr, 17 match_ast, AstPtr,
@@ -126,6 +126,20 @@ pub struct ReferenceDescriptor {
126 pub name: String, 126 pub name: String,
127} 127}
128 128
129pub struct Expansion {
130 macro_call_id: MacroCallId,
131}
132
133impl Expansion {
134 pub fn translate_offset(&self, db: &impl HirDatabase, offset: TextUnit) -> Option<TextUnit> {
135 let exp_info = self.file_id().expansion_info(db)?;
136 exp_info.translate_offset(offset)
137 }
138 pub fn file_id(&self) -> HirFileId {
139 self.macro_call_id.as_file(MacroFileKind::Items)
140 }
141}
142
129impl SourceAnalyzer { 143impl SourceAnalyzer {
130 pub fn new( 144 pub fn new(
131 db: &impl HirDatabase, 145 db: &impl HirDatabase,
@@ -386,6 +400,13 @@ impl SourceAnalyzer {
386 implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait) 400 implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait)
387 } 401 }
388 402
403 pub fn expand(&self, db: &impl HirDatabase, macro_call: &ast::MacroCall) -> Option<Expansion> {
404 let def = self.resolve_macro_call(db, macro_call)?.id;
405 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(macro_call));
406 let macro_call_loc = MacroCallLoc { def, ast_id };
407 Some(Expansion { macro_call_id: db.intern_macro(macro_call_loc) })
408 }
409
389 #[cfg(test)] 410 #[cfg(test)]
390 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { 411 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
391 self.body_source_map.clone().unwrap() 412 self.body_source_map.clone().unwrap()
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 26531cb05..6bfbb2f79 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -160,6 +160,15 @@ pub struct ExpansionInfo {
160} 160}
161 161
162impl ExpansionInfo { 162impl ExpansionInfo {
163 pub fn translate_offset(&self, offset: TextUnit) -> Option<TextUnit> {
164 let offset = offset.checked_sub(self.arg_start.1)?;
165 let token_id = self.macro_arg.1.token_by_offset(offset)?;
166 let token_id = tt::TokenId(token_id.0 + self.shift);
167
168 let (r, _) = self.exp_map.ranges.iter().find(|(_, tid)| *tid == token_id)?;
169 Some(r.start())
170 }
171
163 pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> { 172 pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> {
164 let token_id = look_in_rev_map(&self.exp_map, from)?; 173 let token_id = look_in_rev_map(&self.exp_map, from)?;
165 174
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 821796e5f..4b1581499 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -1,11 +1,10 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::Source; 3use hir::{db::AstDatabase, Source};
4use ra_db::SourceDatabase;
5use ra_syntax::{ 4use ra_syntax::{
6 algo::find_node_at_offset, 5 algo::find_node_at_offset,
7 ast::{self, DocCommentsOwner}, 6 ast::{self, DocCommentsOwner},
8 match_ast, AstNode, SyntaxNode, 7 match_ast, AstNode, SyntaxNode, TextUnit,
9}; 8};
10 9
11use crate::{ 10use crate::{
@@ -19,17 +18,29 @@ pub(crate) fn goto_definition(
19 db: &RootDatabase, 18 db: &RootDatabase,
20 position: FilePosition, 19 position: FilePosition,
21) -> Option<RangeInfo<Vec<NavigationTarget>>> { 20) -> Option<RangeInfo<Vec<NavigationTarget>>> {
22 let parse = db.parse(position.file_id); 21 go(db, Source::new(position.file_id.into(), position.offset))
23 let syntax = parse.tree().syntax().clone(); 22}
24 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&syntax, position.offset) { 23
25 let navs = 24fn go(db: &RootDatabase, offset: Source<TextUnit>) -> Option<RangeInfo<Vec<NavigationTarget>>> {
26 reference_definition(db, Source::new(position.file_id.into(), &name_ref)).to_vec(); 25 let syntax = db.parse_or_expand(offset.file_id)?;
26
27 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&syntax, offset.ast) {
28 let navs = reference_definition(db, offset.with_ast(&name_ref)).to_vec();
27 return Some(RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec())); 29 return Some(RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec()));
28 } 30 }
29 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { 31 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, offset.ast) {
30 let navs = name_definition(db, Source::new(position.file_id.into(), &name))?; 32 let navs = name_definition(db, offset.with_ast(&name))?;
31 return Some(RangeInfo::new(name.syntax().text_range(), navs)); 33 return Some(RangeInfo::new(name.syntax().text_range(), navs));
32 } 34 }
35 if let Some(macro_call) = find_node_at_offset::<ast::MacroCall>(&syntax, offset.ast) {
36 let source_analyzer =
37 hir::SourceAnalyzer::new(db, offset.with_ast(macro_call.syntax()), None);
38 if let Some(exp) = source_analyzer.expand(db, &macro_call) {
39 if let Some(offset) = exp.translate_offset(db, offset.ast) {
40 return go(db, Source::new(exp.file_id(), offset));
41 }
42 }
43 }
33 None 44 None
34} 45}
35 46
@@ -677,4 +688,23 @@ mod tests {
677 "bar MODULE FileId(1) [0; 11) [4; 7)", 688 "bar MODULE FileId(1) [0; 11) [4; 7)",
678 ); 689 );
679 } 690 }
691
692 #[test]
693 fn goto_from_macro() {
694 check_goto(
695 "
696 //- /lib.rs
697 macro_rules! id {
698 ($($tt:tt)*) => { $($tt)* }
699 }
700 fn foo() {}
701 id! {
702 fn bar() {
703 fo<|>o();
704 }
705 }
706 ",
707 "foo FN_DEF FileId(1) [52; 63) [55; 58)",
708 );
709 }
680} 710}
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 3f57ce3b5..37382d2df 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -77,6 +77,12 @@ pub fn token_tree_to_syntax_node(
77} 77}
78 78
79impl TokenMap { 79impl TokenMap {
80 pub fn token_by_offset(&self, relative_offset: TextUnit) -> Option<tt::TokenId> {
81 let (idx, _) =
82 self.tokens.iter().enumerate().find(|(_, range)| range.contains(relative_offset))?;
83 Some(tt::TokenId(idx as u32))
84 }
85
80 pub fn relative_range_of(&self, tt: tt::TokenId) -> Option<TextRange> { 86 pub fn relative_range_of(&self, tt: tt::TokenId) -> Option<TextRange> {
81 let idx = tt.0 as usize; 87 let idx = tt.0 as usize;
82 self.tokens.get(idx).copied() 88 self.tokens.get(idx).copied()