1+ use std:: convert:: TryInto ;
2+
13use either:: Either ;
24use hir:: { InFile , Semantics } ;
35use ide_db:: {
6+ base_db:: { AnchoredPath , FileId , FileLoader } ,
47 defs:: { NameClass , NameRefClass } ,
58 RootDatabase ,
69} ;
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+ } ;
813
914use crate :: {
1015 display:: TryToNav ,
@@ -32,7 +37,7 @@ pub(crate) fn goto_definition(
3237 let original_token = pick_best ( file. token_at_offset ( position. offset ) ) ?;
3338 let token = sema. descend_into_macros ( original_token. clone ( ) ) ;
3439 let parent = token. parent ( ) ?;
35- if let Some ( _) = ast:: Comment :: cast ( token) {
40+ if let Some ( _) = ast:: Comment :: cast ( token. clone ( ) ) {
3641 let ( attributes, def) = doc_attributes ( & sema, & parent) ?;
3742
3843 let ( docs, doc_mapping) = attributes. docs_with_rangemap ( db) ?;
@@ -45,7 +50,6 @@ pub(crate) fn goto_definition(
4550 let nav = resolve_doc_path_for_def ( db, def, & link, ns) ?. try_to_nav ( db) ?;
4651 return Some ( RangeInfo :: new ( original_token. text_range ( ) , vec ! [ nav] ) ) ;
4752 }
48-
4953 let nav = match_ast ! {
5054 match parent {
5155 ast:: NameRef ( name_ref) => {
@@ -61,13 +65,40 @@ pub(crate) fn goto_definition(
6165 } else {
6266 reference_definition( & sema, Either :: Left ( & lt) )
6367 } ,
68+ ast:: TokenTree ( tt) => try_lookup_include_path( sema. db, tt, token, position. file_id) ,
6469 _ => return None ,
6570 }
6671 } ;
6772
6873 Some ( RangeInfo :: new ( original_token. text_range ( ) , nav. into_iter ( ) . collect ( ) ) )
6974}
7075
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+
71102fn pick_best ( tokens : TokenAtOffset < SyntaxToken > ) -> Option < SyntaxToken > {
72103 return tokens. max_by_key ( priority) ;
73104 fn priority ( n : & SyntaxToken ) -> usize {
@@ -1213,6 +1244,21 @@ fn f(e: Enum) {
12131244 Enum::Variant2 => {}
12141245 }
12151246}
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
12161262"# ,
12171263 ) ;
12181264 }
0 commit comments