diff options
author | Lukas Wirth <[email protected]> | 2021-06-05 13:02:36 +0100 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-06-05 18:04:50 +0100 |
commit | 5391f9c63c3c59b08a71b2657e8773f3c1d43145 (patch) | |
tree | ea15ccf428fa7c03f76fc6db00337b00fd2484cd /crates | |
parent | 98395f29a417b37a5969594f0cac5ae23585da85 (diff) |
Support goto-definition for include macro paths
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide/src/goto_definition.rs | 52 |
1 files changed, 49 insertions, 3 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index a04333e63..b0bfd646e 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,10 +1,15 @@ | |||
1 | use std::convert::TryInto; | ||
2 | |||
1 | use either::Either; | 3 | use either::Either; |
2 | use hir::{InFile, Semantics}; | 4 | use hir::{InFile, Semantics}; |
3 | use ide_db::{ | 5 | use ide_db::{ |
6 | base_db::{AnchoredPath, FileId, FileLoader}, | ||
4 | defs::{NameClass, NameRefClass}, | 7 | defs::{NameClass, NameRefClass}, |
5 | RootDatabase, | 8 | RootDatabase, |
6 | }; | 9 | }; |
7 | use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 10 | use syntax::{ |
11 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TokenAtOffset, T, | ||
12 | }; | ||
8 | 13 | ||
9 | use crate::{ | 14 | use crate::{ |
10 | display::TryToNav, | 15 | display::TryToNav, |
@@ -32,7 +37,7 @@ pub(crate) fn goto_definition( | |||
32 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 37 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
33 | let token = sema.descend_into_macros(original_token.clone()); | 38 | let token = sema.descend_into_macros(original_token.clone()); |
34 | let parent = token.parent()?; | 39 | let parent = token.parent()?; |
35 | if let Some(_) = ast::Comment::cast(token) { | 40 | if let Some(_) = ast::Comment::cast(token.clone()) { |
36 | let (attributes, def) = doc_attributes(&sema, &parent)?; | 41 | let (attributes, def) = doc_attributes(&sema, &parent)?; |
37 | 42 | ||
38 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 43 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
@@ -45,7 +50,6 @@ pub(crate) fn goto_definition( | |||
45 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; | 50 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; |
46 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | 51 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); |
47 | } | 52 | } |
48 | |||
49 | let nav = match_ast! { | 53 | let nav = match_ast! { |
50 | match parent { | 54 | match parent { |
51 | ast::NameRef(name_ref) => { | 55 | ast::NameRef(name_ref) => { |
@@ -61,6 +65,7 @@ pub(crate) fn goto_definition( | |||
61 | } else { | 65 | } else { |
62 | reference_definition(&sema, Either::Left(<)) | 66 | reference_definition(&sema, Either::Left(<)) |
63 | }, | 67 | }, |
68 | ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id), | ||
64 | _ => return None, | 69 | _ => return None, |
65 | } | 70 | } |
66 | }; | 71 | }; |
@@ -68,6 +73,32 @@ pub(crate) fn goto_definition( | |||
68 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) | 73 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) |
69 | } | 74 | } |
70 | 75 | ||
76 | fn try_lookup_include_path( | ||
77 | db: &RootDatabase, | ||
78 | tt: ast::TokenTree, | ||
79 | token: SyntaxToken, | ||
80 | file_id: FileId, | ||
81 | ) -> Option<NavigationTarget> { | ||
82 | let path = ast::String::cast(token)?.value()?.into_owned(); | ||
83 | let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?; | ||
84 | let name = macro_call.path()?.segment()?.name_ref()?; | ||
85 | if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { | ||
86 | return None; | ||
87 | } | ||
88 | let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; | ||
89 | let size = db.file_text(file_id).len().try_into().ok()?; | ||
90 | Some(NavigationTarget { | ||
91 | file_id, | ||
92 | full_range: TextRange::new(0.into(), size), | ||
93 | name: path.into(), | ||
94 | focus_range: None, | ||
95 | kind: None, | ||
96 | container_name: None, | ||
97 | description: None, | ||
98 | docs: None, | ||
99 | }) | ||
100 | } | ||
101 | |||
71 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 102 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
72 | return tokens.max_by_key(priority); | 103 | return tokens.max_by_key(priority); |
73 | fn priority(n: &SyntaxToken) -> usize { | 104 | fn priority(n: &SyntaxToken) -> usize { |
@@ -1216,4 +1247,19 @@ fn f(e: Enum) { | |||
1216 | "#, | 1247 | "#, |
1217 | ); | 1248 | ); |
1218 | } | 1249 | } |
1250 | |||
1251 | #[test] | ||
1252 | fn goto_include() { | ||
1253 | check( | ||
1254 | r#" | ||
1255 | //- /main.rs | ||
1256 | fn main() { | ||
1257 | let str = include_str!("foo.txt$0"); | ||
1258 | } | ||
1259 | //- /foo.txt | ||
1260 | // empty | ||
1261 | //^ file | ||
1262 | "#, | ||
1263 | ); | ||
1264 | } | ||
1219 | } | 1265 | } |