aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-11-18 11:23:24 +0000
committerAleksey Kladov <[email protected]>2019-11-18 11:23:24 +0000
commit9fcd98e956a46d90c708abb9739f067a88ae3c4a (patch)
treeffc9afa4bd6f436e51eaa3de9758916a1bdc7124 /crates
parent6d8ca870b3d8a12e7b2460532712f9a6daf80afb (diff)
Add ra_ide_api::expand
This module should handle all tricky bits with mapping macro-expanded HirFileId to original files the user actually can see in the editor
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs54
-rw-r--r--crates/ra_ide_api/src/expand.rs42
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs27
-rw-r--r--crates/ra_ide_api/src/lib.rs1
4 files changed, 69 insertions, 55 deletions
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 4703cfaf7..291b5ee40 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -9,8 +9,9 @@ use ra_syntax::{
9 SyntaxNode, TextRange, 9 SyntaxNode, TextRange,
10}; 10};
11 11
12use crate::{db::RootDatabase, expand::original_range, FileSymbol};
13
12use super::short_label::ShortLabel; 14use super::short_label::ShortLabel;
13use crate::{db::RootDatabase, FileSymbol};
14 15
15/// `NavigationTarget` represents and element in the editor's UI which you can 16/// `NavigationTarget` represents and element in the editor's UI which you can
16/// click on to navigate to a particular piece of code. 17/// click on to navigate to a particular piece of code.
@@ -79,13 +80,12 @@ impl NavigationTarget {
79 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 80 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
80 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 81 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
81 if let Some(src) = module.declaration_source(db) { 82 if let Some(src) = module.declaration_source(db) {
82 let (file_id, text_range) = 83 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
83 find_range_from_node(db, src.as_ref().map(|it| it.syntax()));
84 return NavigationTarget::from_syntax( 84 return NavigationTarget::from_syntax(
85 file_id, 85 frange.file_id,
86 name, 86 name,
87 None, 87 None,
88 text_range, 88 frange.range,
89 src.ast.syntax(), 89 src.ast.syntax(),
90 src.ast.doc_comment_text(), 90 src.ast.doc_comment_text(),
91 src.ast.short_label(), 91 src.ast.short_label(),
@@ -149,14 +149,14 @@ impl NavigationTarget {
149 //FIXME: use `_` instead of empty string 149 //FIXME: use `_` instead of empty string
150 let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); 150 let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
151 let focus_range = 151 let focus_range =
152 node.name().map(|it| find_range_from_node(db, Source::new(file_id, it.syntax())).1); 152 node.name().map(|it| original_range(db, Source::new(file_id, it.syntax())).range);
153 let (file_id, full_range) = find_range_from_node(db, Source::new(file_id, node.syntax())); 153 let frange = original_range(db, Source::new(file_id, node.syntax()));
154 154
155 NavigationTarget::from_syntax( 155 NavigationTarget::from_syntax(
156 file_id, 156 frange.file_id,
157 name, 157 name,
158 focus_range, 158 focus_range,
159 full_range, 159 frange.range,
160 node.syntax(), 160 node.syntax(),
161 docs, 161 docs,
162 description, 162 description,
@@ -234,26 +234,26 @@ impl ToNav for hir::Module {
234 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 234 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default();
235 match &src.ast { 235 match &src.ast {
236 ModuleSource::SourceFile(node) => { 236 ModuleSource::SourceFile(node) => {
237 let (file_id, text_range) = find_range_from_node(db, src.with_ast(node.syntax())); 237 let frange = original_range(db, src.with_ast(node.syntax()));
238 238
239 NavigationTarget::from_syntax( 239 NavigationTarget::from_syntax(
240 file_id, 240 frange.file_id,
241 name, 241 name,
242 None, 242 None,
243 text_range, 243 frange.range,
244 node.syntax(), 244 node.syntax(),
245 None, 245 None,
246 None, 246 None,
247 ) 247 )
248 } 248 }
249 ModuleSource::Module(node) => { 249 ModuleSource::Module(node) => {
250 let (file_id, text_range) = find_range_from_node(db, src.with_ast(node.syntax())); 250 let frange = original_range(db, src.with_ast(node.syntax()));
251 251
252 NavigationTarget::from_syntax( 252 NavigationTarget::from_syntax(
253 file_id, 253 frange.file_id,
254 name, 254 name,
255 None, 255 None,
256 text_range, 256 frange.range,
257 node.syntax(), 257 node.syntax(),
258 node.doc_comment_text(), 258 node.doc_comment_text(),
259 node.short_label(), 259 node.short_label(),
@@ -266,13 +266,13 @@ impl ToNav for hir::Module {
266impl ToNav for hir::ImplBlock { 266impl ToNav for hir::ImplBlock {
267 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 267 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
268 let src = self.source(db); 268 let src = self.source(db);
269 let (file_id, text_range) = find_range_from_node(db, src.as_ref().map(|it| it.syntax())); 269 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
270 270
271 NavigationTarget::from_syntax( 271 NavigationTarget::from_syntax(
272 file_id, 272 frange.file_id,
273 "impl".into(), 273 "impl".into(),
274 None, 274 None,
275 text_range, 275 frange.range,
276 src.ast.syntax(), 276 src.ast.syntax(),
277 None, 277 None,
278 None, 278 None,
@@ -293,12 +293,12 @@ impl ToNav for hir::StructField {
293 it.short_label(), 293 it.short_label(),
294 ), 294 ),
295 FieldSource::Pos(it) => { 295 FieldSource::Pos(it) => {
296 let (file_id, text_range) = find_range_from_node(db, src.with_ast(it.syntax())); 296 let frange = original_range(db, src.with_ast(it.syntax()));
297 NavigationTarget::from_syntax( 297 NavigationTarget::from_syntax(
298 file_id, 298 frange.file_id,
299 "".into(), 299 "".into(),
300 None, 300 None,
301 text_range, 301 frange.range,
302 it.syntax(), 302 it.syntax(),
303 None, 303 None,
304 None, 304 None,
@@ -362,18 +362,6 @@ impl ToNav for hir::Local {
362 } 362 }
363} 363}
364 364
365fn find_range_from_node(db: &RootDatabase, node: Source<&SyntaxNode>) -> (FileId, TextRange) {
366 let text_range = node.ast.text_range();
367 let (file_id, text_range) = node
368 .file_id
369 .expansion_info(db)
370 .and_then(|expansion_info| expansion_info.find_range(text_range))
371 .unwrap_or((node.file_id, text_range));
372
373 // FIXME: handle recursive macro generated macro
374 (file_id.original_file(db), text_range)
375}
376
377pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> { 365pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
378 let parse = db.parse(symbol.file_id); 366 let parse = db.parse(symbol.file_id);
379 let node = symbol.ptr.to_node(parse.tree().syntax()); 367 let node = symbol.ptr.to_node(parse.tree().syntax());
diff --git a/crates/ra_ide_api/src/expand.rs b/crates/ra_ide_api/src/expand.rs
new file mode 100644
index 000000000..5f1fb9a12
--- /dev/null
+++ b/crates/ra_ide_api/src/expand.rs
@@ -0,0 +1,42 @@
1//! Utilities to work with files, produced by macros.
2use std::iter::successors;
3
4use hir::Source;
5use ra_db::FileId;
6use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken};
7
8use crate::{db::RootDatabase, FileRange};
9
10pub(crate) fn original_range(db: &RootDatabase, node: Source<&SyntaxNode>) -> FileRange {
11 let text_range = node.ast.text_range();
12 let (file_id, range) = node
13 .file_id
14 .expansion_info(db)
15 .and_then(|expansion_info| expansion_info.find_range(text_range))
16 .unwrap_or((node.file_id, text_range));
17
18 // FIXME: handle recursive macro generated macro
19 FileRange { file_id: file_id.original_file(db), range }
20}
21
22pub(crate) fn descend_into_macros(
23 db: &RootDatabase,
24 file_id: FileId,
25 token: SyntaxToken,
26) -> Source<SyntaxToken> {
27 let src = Source::new(file_id.into(), token);
28
29 successors(Some(src), |token| {
30 let macro_call = token.ast.ancestors().find_map(ast::MacroCall::cast)?;
31 let tt = macro_call.token_tree()?;
32 if !token.ast.text_range().is_subrange(&tt.syntax().text_range()) {
33 return None;
34 }
35 let source_analyzer =
36 hir::SourceAnalyzer::new(db, token.with_ast(token.ast.parent()).as_ref(), None);
37 let exp = source_analyzer.expand(db, &macro_call)?;
38 exp.map_token_down(db, token.as_ref())
39 })
40 .last()
41 .unwrap()
42}
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index b693a4c31..1a8db0ea0 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -1,16 +1,15 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::iter::successors;
4
5use hir::{db::AstDatabase, Source}; 3use hir::{db::AstDatabase, Source};
6use ra_syntax::{ 4use ra_syntax::{
7 ast::{self, DocCommentsOwner}, 5 ast::{self, DocCommentsOwner},
8 match_ast, AstNode, SyntaxNode, SyntaxToken, 6 match_ast, AstNode, SyntaxNode,
9}; 7};
10 8
11use crate::{ 9use crate::{
12 db::RootDatabase, 10 db::RootDatabase,
13 display::{ShortLabel, ToNav}, 11 display::{ShortLabel, ToNav},
12 expand::descend_into_macros,
14 references::{classify_name_ref, NameKind::*}, 13 references::{classify_name_ref, NameKind::*},
15 FilePosition, NavigationTarget, RangeInfo, 14 FilePosition, NavigationTarget, RangeInfo,
16}; 15};
@@ -19,7 +18,9 @@ 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 token = descend_into_macros(db, position)?; 21 let file = db.parse_or_expand(position.file_id.into())?;
22 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
23 let token = descend_into_macros(db, position.file_id, token);
23 24
24 let res = match_ast! { 25 let res = match_ast! {
25 match (token.ast.parent()) { 26 match (token.ast.parent()) {
@@ -39,24 +40,6 @@ pub(crate) fn goto_definition(
39 Some(res) 40 Some(res)
40} 41}
41 42
42fn descend_into_macros(db: &RootDatabase, position: FilePosition) -> Option<Source<SyntaxToken>> {
43 let file = db.parse_or_expand(position.file_id.into())?;
44 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
45
46 successors(Some(Source::new(position.file_id.into(), token)), |token| {
47 let macro_call = token.ast.ancestors().find_map(ast::MacroCall::cast)?;
48 let tt = macro_call.token_tree()?;
49 if !token.ast.text_range().is_subrange(&tt.syntax().text_range()) {
50 return None;
51 }
52 let source_analyzer =
53 hir::SourceAnalyzer::new(db, token.with_ast(token.ast.parent()).as_ref(), None);
54 let exp = source_analyzer.expand(db, &macro_call)?;
55 exp.map_token_down(db, token.as_ref())
56 })
57 .last()
58}
59
60#[derive(Debug)] 43#[derive(Debug)]
61pub(crate) enum ReferenceResult { 44pub(crate) enum ReferenceResult {
62 Exact(NavigationTarget), 45 Exact(NavigationTarget),
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 484fbcc82..110ddcd62 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -41,6 +41,7 @@ mod matching_brace;
41mod display; 41mod display;
42mod inlay_hints; 42mod inlay_hints;
43mod wasm_shims; 43mod wasm_shims;
44mod expand;
44 45
45#[cfg(test)] 46#[cfg(test)]
46mod marks; 47mod marks;