@@ -91,7 +91,6 @@ pub fn remove_links(markdown: &str) -> String {
91
91
}
92
92
93
93
pub fn get_doc_link < T : Resolvable + Clone > ( db : & dyn HirDatabase , definition : & T ) -> Option < String > {
94
- eprintln ! ( "hir::doc_links::get_doc_link" ) ;
95
94
let module_def = definition. clone ( ) . try_into_module_def ( ) ?;
96
95
97
96
get_doc_link_impl ( db, & module_def)
@@ -105,8 +104,31 @@ pub fn get_doc_link<T: Resolvable + Clone>(db: &dyn HirDatabase, definition: &T)
105
104
// BUG: For methods
106
105
// import_map.path_of(ns) fails, is not designed to resolve methods
107
106
fn get_doc_link_impl ( db : & dyn HirDatabase , moddef : & ModuleDef ) -> Option < String > {
108
- eprintln ! ( "get_doc_link_impl: {:#?}" , moddef) ;
109
- let ns = ItemInNs :: Types ( moddef. clone ( ) . into ( ) ) ;
107
+ // Get the outermost definition for the moduledef. This is used to resolve the public path to the type,
108
+ // then we can join the method, field, etc onto it if required.
109
+ let target_def: ModuleDef = match moddef {
110
+ ModuleDef :: Function ( f) => match f. as_assoc_item ( db) . map ( |assoc| assoc. container ( db) ) {
111
+ Some ( AssocItemContainer :: Trait ( t) ) => t. into ( ) ,
112
+ Some ( AssocItemContainer :: ImplDef ( imp) ) => {
113
+ let resolver = ModuleId :: from ( imp. module ( db) ) . resolver ( db. upcast ( ) ) ;
114
+ let ctx = TyLoweringContext :: new ( db, & resolver) ;
115
+ Adt :: from (
116
+ Ty :: from_hir (
117
+ & ctx,
118
+ & imp. target_trait ( db) . unwrap_or_else ( || imp. target_type ( db) ) ,
119
+ )
120
+ . as_adt ( )
121
+ . map ( |t| t. 0 )
122
+ . unwrap ( ) ,
123
+ )
124
+ . into ( )
125
+ }
126
+ None => ModuleDef :: Function ( * f) ,
127
+ } ,
128
+ moddef => * moddef,
129
+ } ;
130
+
131
+ let ns = ItemInNs :: Types ( target_def. clone ( ) . into ( ) ) ;
110
132
111
133
let module = moddef. module ( db) ?;
112
134
let krate = module. krate ( ) ;
@@ -117,7 +139,28 @@ fn get_doc_link_impl(db: &dyn HirDatabase, moddef: &ModuleDef) -> Option<String>
117
139
118
140
get_doc_url ( db, & krate)
119
141
. and_then ( |url| url. join ( & base) . ok ( ) )
120
- . and_then ( |url| get_symbol_filename ( db, & moddef) . as_deref ( ) . and_then ( |f| url. join ( f) . ok ( ) ) )
142
+ . and_then ( |url| {
143
+ get_symbol_filename ( db, & target_def) . as_deref ( ) . and_then ( |f| url. join ( f) . ok ( ) )
144
+ } )
145
+ . and_then ( |url| match moddef {
146
+ ModuleDef :: Function ( f) => {
147
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Function ( * f) ) )
148
+ . as_deref ( )
149
+ . and_then ( |f| url. join ( f) . ok ( ) )
150
+ }
151
+ ModuleDef :: Const ( c) => {
152
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Const ( * c) ) )
153
+ . as_deref ( )
154
+ . and_then ( |f| url. join ( f) . ok ( ) )
155
+ }
156
+ ModuleDef :: TypeAlias ( ty) => {
157
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: TypeAlias ( * ty) ) )
158
+ . as_deref ( )
159
+ . and_then ( |f| url. join ( f) . ok ( ) )
160
+ }
161
+ // TODO: Field <- this requires passing in a definition or something
162
+ _ => Some ( url) ,
163
+ } )
121
164
. map ( |url| url. into_string ( ) )
122
165
}
123
166
@@ -307,6 +350,12 @@ fn ns_from_intra_spec(s: &str) -> Option<hir::Namespace> {
307
350
. next ( )
308
351
}
309
352
353
+ /// Get the root URL for the documentation of a crate.
354
+ ///
355
+ /// ```
356
+ /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
357
+ /// ^^^^^^^^^^^^^^^^^^^^^^^^^^
358
+ /// ```
310
359
fn get_doc_url ( db : & RootDatabase , krate : & Crate ) -> Option < Url > {
311
360
krate
312
361
. get_html_root_url ( db)
@@ -323,8 +372,11 @@ fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
323
372
324
373
/// Get the filename and extension generated for a symbol by rustdoc.
325
374
///
326
- /// Example: `struct.Shard.html`
327
- fn get_symbol_filename ( db : & RootDatabase , definition : & ModuleDef ) -> Option < String > {
375
+ /// ```
376
+ /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
377
+ /// ^^^^^^^^^^^^^^^^^^^
378
+ /// ```
379
+ fn get_symbol_filename ( db : & dyn HirDatabase , definition : & ModuleDef ) -> Option < String > {
328
380
Some ( match definition {
329
381
ModuleDef :: Adt ( adt) => match adt {
330
382
Adt :: Struct ( s) => format ! ( "struct.{}.html" , s. name( db) ) ,
@@ -334,7 +386,7 @@ fn get_symbol_filename(db: &RootDatabase, definition: &ModuleDef) -> Option<Stri
334
386
ModuleDef :: Module ( _) => "index.html" . to_string ( ) ,
335
387
ModuleDef :: Trait ( t) => format ! ( "trait.{}.html" , t. name( db) ) ,
336
388
ModuleDef :: TypeAlias ( t) => format ! ( "type.{}.html" , t. name( db) ) ,
337
- ModuleDef :: BuiltinType ( t) => format ! ( "primitive.{}.html" , t) ,
389
+ ModuleDef :: BuiltinType ( t) => format ! ( "primitive.{}.html" , t. as_name ( ) ) ,
338
390
ModuleDef :: Function ( f) => format ! ( "fn.{}.html" , f. name( db) ) ,
339
391
ModuleDef :: EnumVariant ( ev) => {
340
392
format ! ( "enum.{}.html#variant.{}" , ev. parent_enum( db) . name( db) , ev. name( db) )
@@ -344,6 +396,30 @@ fn get_symbol_filename(db: &RootDatabase, definition: &ModuleDef) -> Option<Stri
344
396
} )
345
397
}
346
398
399
+ enum FieldOrAssocItem {
400
+ Field ( Field ) ,
401
+ AssocItem ( AssocItem ) ,
402
+ }
403
+
404
+ /// Get the fragment required to link to a specific field, method, associated type, or associated constant.
405
+ ///
406
+ /// ```
407
+ /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
408
+ /// ^^^^^^^^^^^^^^
409
+ /// ```
410
+ fn get_symbol_fragment ( db : & dyn HirDatabase , field_or_assoc : & FieldOrAssocItem ) -> Option < String > {
411
+ Some ( match field_or_assoc {
412
+ FieldOrAssocItem :: Field ( field) => format ! ( "#structfield.{}" , field. name( db) ) ,
413
+ FieldOrAssocItem :: AssocItem ( assoc) => match assoc {
414
+ // TODO: Rustdoc sometimes uses tymethod instead of method. This case needs to be investigated.
415
+ AssocItem :: Function ( function) => format ! ( "#method.{}" , function. name( db) ) ,
416
+ // TODO: This might be the old method for documenting associated constants, i32::MAX uses a separate page...
417
+ AssocItem :: Const ( constant) => format ! ( "#associatedconstant.{}" , constant. name( db) ?) ,
418
+ AssocItem :: TypeAlias ( ty) => format ! ( "#associatedtype.{}" , ty. name( db) ) ,
419
+ } ,
420
+ } )
421
+ }
422
+
347
423
fn pick_best ( tokens : TokenAtOffset < SyntaxToken > ) -> Option < SyntaxToken > {
348
424
return tokens. max_by_key ( priority) ;
349
425
fn priority ( n : & SyntaxToken ) -> usize {
0 commit comments