1
+ use std:: convert:: TryInto ;
2
+
1
3
use either:: Either ;
2
4
use hir:: { InFile , Semantics } ;
3
5
use ide_db:: {
6
+ base_db:: { AnchoredPath , FileId , FileLoader } ,
4
7
defs:: { NameClass , NameRefClass } ,
5
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
14
use crate :: {
10
15
display:: TryToNav ,
@@ -32,7 +37,7 @@ pub(crate) fn goto_definition(
32
37
let original_token = pick_best ( file. token_at_offset ( position. offset ) ) ?;
33
38
let token = sema. descend_into_macros ( original_token. clone ( ) ) ;
34
39
let parent = token. parent ( ) ?;
35
- if let Some ( _) = ast:: Comment :: cast ( token) {
40
+ if let Some ( _) = ast:: Comment :: cast ( token. clone ( ) ) {
36
41
let ( attributes, def) = doc_attributes ( & sema, & parent) ?;
37
42
38
43
let ( docs, doc_mapping) = attributes. docs_with_rangemap ( db) ?;
@@ -45,7 +50,6 @@ pub(crate) fn goto_definition(
45
50
let nav = resolve_doc_path_for_def ( db, def, & link, ns) ?. try_to_nav ( db) ?;
46
51
return Some ( RangeInfo :: new ( original_token. text_range ( ) , vec ! [ nav] ) ) ;
47
52
}
48
-
49
53
let nav = match_ast ! {
50
54
match parent {
51
55
ast:: NameRef ( name_ref) => {
@@ -61,13 +65,40 @@ pub(crate) fn goto_definition(
61
65
} else {
62
66
reference_definition( & sema, Either :: Left ( & lt) )
63
67
} ,
68
+ ast:: TokenTree ( tt) => try_lookup_include_path( sema. db, tt, token, position. file_id) ,
64
69
_ => return None ,
65
70
}
66
71
} ;
67
72
68
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
102
fn pick_best ( tokens : TokenAtOffset < SyntaxToken > ) -> Option < SyntaxToken > {
72
103
return tokens. max_by_key ( priority) ;
73
104
fn priority ( n : & SyntaxToken ) -> usize {
@@ -93,6 +124,7 @@ pub(crate) fn reference_definition(
93
124
94
125
#[ cfg( test) ]
95
126
mod tests {
127
+ use expect_test:: { expect, Expect } ;
96
128
use ide_db:: base_db:: FileRange ;
97
129
98
130
use crate :: fixture;
@@ -1216,4 +1248,36 @@ fn f(e: Enum) {
1216
1248
"# ,
1217
1249
) ;
1218
1250
}
1251
+
1252
+ fn check_expect ( ra_fixture : & str , expected : Expect ) {
1253
+ let ( analysis, position) = fixture:: position ( & ra_fixture) ;
1254
+ let navs = analysis. goto_definition ( position) . unwrap ( ) . expect ( "no definition found" ) . info ;
1255
+
1256
+ expected. assert_debug_eq ( & navs)
1257
+ }
1258
+
1259
+ #[ test]
1260
+ fn goto_include ( ) {
1261
+ check_expect (
1262
+ r#"
1263
+ //- /main.rs
1264
+ fn main() {
1265
+ let str = include_str!("foo.txt$0");
1266
+ }
1267
+ //- /foo.txt
1268
+ "foobar"
1269
+ "# ,
1270
+ expect ! [ [ r#"
1271
+ [
1272
+ NavigationTarget {
1273
+ file_id: FileId(
1274
+ 1,
1275
+ ),
1276
+ full_range: 0..9,
1277
+ name: "foo.txt",
1278
+ },
1279
+ ]
1280
+ "# ] ] ,
1281
+ ) ;
1282
+ }
1219
1283
}
0 commit comments