2
2
3
3
use std:: iter:: once;
4
4
5
- use hir_def:: resolver:: Resolver ;
5
+ use hir_def:: {
6
+ resolver:: { HasResolver , Resolver } ,
7
+ ModuleId ,
8
+ } ;
6
9
use itertools:: Itertools ;
7
10
use syntax:: ast:: Path ;
8
11
use url:: Url ;
9
12
10
- use crate :: { db:: HirDatabase , Adt , AsName , Crate , Hygiene , ItemInNs , ModPath , ModuleDef } ;
13
+ use crate :: {
14
+ db:: HirDatabase , Adt , AsAssocItem , AsName , AssocItem , AssocItemContainer , Crate , Field ,
15
+ Hygiene , ImplDef , ItemInNs , Local , MacroDef , ModPath , ModuleDef , TypeParam ,
16
+ } ;
17
+ use hir_ty:: { Ty , TyLoweringContext } ;
11
18
12
19
pub fn resolve_doc_link < T : Resolvable + Clone > (
13
20
db : & dyn HirDatabase ,
@@ -21,7 +28,6 @@ pub fn resolve_doc_link<T: Resolvable + Clone>(
21
28
}
22
29
23
30
pub fn get_doc_link < T : Resolvable + Clone > ( db : & dyn HirDatabase , definition : & T ) -> Option < String > {
24
- eprintln ! ( "hir::doc_links::get_doc_link" ) ;
25
31
let module_def = definition. clone ( ) . try_into_module_def ( ) ?;
26
32
27
33
get_doc_link_impl ( db, & module_def)
@@ -35,8 +41,31 @@ pub fn get_doc_link<T: Resolvable + Clone>(db: &dyn HirDatabase, definition: &T)
35
41
// BUG: For methods
36
42
// import_map.path_of(ns) fails, is not designed to resolve methods
37
43
fn get_doc_link_impl ( db : & dyn HirDatabase , moddef : & ModuleDef ) -> Option < String > {
38
- eprintln ! ( "get_doc_link_impl: {:#?}" , moddef) ;
39
- let ns = ItemInNs :: Types ( moddef. clone ( ) . into ( ) ) ;
44
+ // Get the outermost definition for the moduledef. This is used to resolve the public path to the type,
45
+ // then we can join the method, field, etc onto it if required.
46
+ let target_def: ModuleDef = match moddef {
47
+ ModuleDef :: Function ( f) => match f. as_assoc_item ( db) . map ( |assoc| assoc. container ( db) ) {
48
+ Some ( AssocItemContainer :: Trait ( t) ) => t. into ( ) ,
49
+ Some ( AssocItemContainer :: ImplDef ( imp) ) => {
50
+ let resolver = ModuleId :: from ( imp. module ( db) ) . resolver ( db. upcast ( ) ) ;
51
+ let ctx = TyLoweringContext :: new ( db, & resolver) ;
52
+ Adt :: from (
53
+ Ty :: from_hir (
54
+ & ctx,
55
+ & imp. target_trait ( db) . unwrap_or_else ( || imp. target_type ( db) ) ,
56
+ )
57
+ . as_adt ( )
58
+ . map ( |t| t. 0 )
59
+ . unwrap ( ) ,
60
+ )
61
+ . into ( )
62
+ }
63
+ None => ModuleDef :: Function ( * f) ,
64
+ } ,
65
+ moddef => * moddef,
66
+ } ;
67
+
68
+ let ns = ItemInNs :: Types ( target_def. clone ( ) . into ( ) ) ;
40
69
41
70
let module = moddef. module ( db) ?;
42
71
let krate = module. krate ( ) ;
@@ -47,7 +76,28 @@ fn get_doc_link_impl(db: &dyn HirDatabase, moddef: &ModuleDef) -> Option<String>
47
76
48
77
get_doc_url ( db, & krate)
49
78
. and_then ( |url| url. join ( & base) . ok ( ) )
50
- . and_then ( |url| get_symbol_filename ( db, & moddef) . as_deref ( ) . and_then ( |f| url. join ( f) . ok ( ) ) )
79
+ . and_then ( |url| {
80
+ get_symbol_filename ( db, & target_def) . as_deref ( ) . and_then ( |f| url. join ( f) . ok ( ) )
81
+ } )
82
+ . and_then ( |url| match moddef {
83
+ ModuleDef :: Function ( f) => {
84
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Function ( * f) ) )
85
+ . as_deref ( )
86
+ . and_then ( |f| url. join ( f) . ok ( ) )
87
+ }
88
+ ModuleDef :: Const ( c) => {
89
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Const ( * c) ) )
90
+ . as_deref ( )
91
+ . and_then ( |f| url. join ( f) . ok ( ) )
92
+ }
93
+ ModuleDef :: TypeAlias ( ty) => {
94
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: TypeAlias ( * ty) ) )
95
+ . as_deref ( )
96
+ . and_then ( |f| url. join ( f) . ok ( ) )
97
+ }
98
+ // TODO: Field <- this requires passing in a definition or something
99
+ _ => Some ( url) ,
100
+ } )
51
101
. map ( |url| url. into_string ( ) )
52
102
}
53
103
@@ -145,24 +195,12 @@ fn try_resolve_path(db: &dyn HirDatabase, moddef: &ModuleDef, link_target: &str)
145
195
. map ( |url| url. into_string ( ) )
146
196
}
147
197
148
- /// Strip prefixes, suffixes, and inline code marks from the given string.
149
- fn strip_prefixes_suffixes ( mut s : & str ) -> & str {
150
- s = s. trim_matches ( '`' ) ;
151
-
152
- [
153
- ( TYPES . 0 . iter ( ) , TYPES . 1 . iter ( ) ) ,
154
- ( VALUES . 0 . iter ( ) , VALUES . 1 . iter ( ) ) ,
155
- ( MACROS . 0 . iter ( ) , MACROS . 1 . iter ( ) ) ,
156
- ]
157
- . iter ( )
158
- . for_each ( |( prefixes, suffixes) | {
159
- prefixes. clone ( ) . for_each ( |prefix| s = s. trim_start_matches ( * prefix) ) ;
160
- suffixes. clone ( ) . for_each ( |suffix| s = s. trim_end_matches ( * suffix) ) ;
161
- } ) ;
162
- let s = s. trim_start_matches ( "@" ) . trim ( ) ;
163
- s
164
- }
165
-
198
+ /// Get the root URL for the documentation of a crate.
199
+ ///
200
+ /// ```
201
+ /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
202
+ /// ^^^^^^^^^^^^^^^^^^^^^^^^^^
203
+ /// ```
166
204
fn get_doc_url ( db : & dyn HirDatabase , krate : & Crate ) -> Option < Url > {
167
205
krate
168
206
. get_html_root_url ( db)
@@ -175,7 +213,10 @@ fn get_doc_url(db: &dyn HirDatabase, krate: &Crate) -> Option<Url> {
175
213
176
214
/// Get the filename and extension generated for a symbol by rustdoc.
177
215
///
178
- /// Example: `struct.Shard.html`
216
+ /// ```
217
+ /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
218
+ /// ^^^^^^^^^^^^^^^^^^^
219
+ /// ```
179
220
fn get_symbol_filename ( db : & dyn HirDatabase , definition : & ModuleDef ) -> Option < String > {
180
221
Some ( match definition {
181
222
ModuleDef :: Adt ( adt) => match adt {
@@ -196,6 +237,30 @@ fn get_symbol_filename(db: &dyn HirDatabase, definition: &ModuleDef) -> Option<S
196
237
} )
197
238
}
198
239
240
+ enum FieldOrAssocItem {
241
+ Field ( Field ) ,
242
+ AssocItem ( AssocItem ) ,
243
+ }
244
+
245
+ /// Get the fragment required to link to a specific field, method, associated type, or associated constant.
246
+ ///
247
+ /// ```
248
+ /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
249
+ /// ^^^^^^^^^^^^^^
250
+ /// ```
251
+ fn get_symbol_fragment ( db : & dyn HirDatabase , field_or_assoc : & FieldOrAssocItem ) -> Option < String > {
252
+ Some ( match field_or_assoc {
253
+ FieldOrAssocItem :: Field ( field) => format ! ( "#structfield.{}" , field. name( db) ) ,
254
+ FieldOrAssocItem :: AssocItem ( assoc) => match assoc {
255
+ // TODO: Rustdoc sometimes uses tymethod instead of method. This case needs to be investigated.
256
+ AssocItem :: Function ( function) => format ! ( "#method.{}" , function. name( db) ) ,
257
+ // TODO: This might be the old method for documenting associated constants, i32::MAX uses a separate page...
258
+ AssocItem :: Const ( constant) => format ! ( "#associatedconstant.{}" , constant. name( db) ?) ,
259
+ AssocItem :: TypeAlias ( ty) => format ! ( "#associatedtype.{}" , ty. name( db) ) ,
260
+ } ,
261
+ } )
262
+ }
263
+
199
264
struct IntraDocLink < ' s > {
200
265
path : & ' s str ,
201
266
namespace : Option < Namespace > ,
@@ -262,6 +327,24 @@ impl Namespace {
262
327
}
263
328
}
264
329
330
+ /// Strip prefixes, suffixes, and inline code marks from the given string.
331
+ fn strip_prefixes_suffixes ( mut s : & str ) -> & str {
332
+ s = s. trim_matches ( '`' ) ;
333
+
334
+ [
335
+ ( TYPES . 0 . iter ( ) , TYPES . 1 . iter ( ) ) ,
336
+ ( VALUES . 0 . iter ( ) , VALUES . 1 . iter ( ) ) ,
337
+ ( MACROS . 0 . iter ( ) , MACROS . 1 . iter ( ) ) ,
338
+ ]
339
+ . iter ( )
340
+ . for_each ( |( prefixes, suffixes) | {
341
+ prefixes. clone ( ) . for_each ( |prefix| s = s. trim_start_matches ( * prefix) ) ;
342
+ suffixes. clone ( ) . for_each ( |suffix| s = s. trim_end_matches ( * suffix) ) ;
343
+ } ) ;
344
+ let s = s. trim_start_matches ( "@" ) . trim ( ) ;
345
+ s
346
+ }
347
+
265
348
/// Sealed trait used solely for the generic bound on [`resolve_doc_link`].
266
349
pub trait Resolvable {
267
350
fn resolver ( & self , db : & dyn HirDatabase ) -> Option < Resolver > ;
0 commit comments