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 {
@@ -1213,6 +1244,21 @@ fn f(e: Enum) {
1213
1244
Enum::Variant2 => {}
1214
1245
}
1215
1246
}
1247
+ "# ,
1248
+ ) ;
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
1216
1262
"# ,
1217
1263
) ;
1218
1264
}
0 commit comments