2
2
//!
3
3
//! Most of the implementation can be found in [`hir::doc_links`].
4
4
5
- use hir :: { Adt , Crate , HasAttrs , ModuleDef } ;
6
- use ide_db :: { defs :: Definition , RootDatabase } ;
7
- use pulldown_cmark :: { CowStr , Event , LinkType , Options , Parser , Tag } ;
5
+ use std :: iter :: once ;
6
+
7
+ use itertools :: Itertools ;
8
8
use pulldown_cmark_to_cmark:: { cmark_with_options, Options as CmarkOptions } ;
9
+ use pulldown_cmark:: { CowStr , Event , LinkType , Options , Parser , Tag } ;
9
10
use url:: Url ;
10
11
11
- use crate :: { FilePosition , Semantics } ;
12
- use hir:: { get_doc_link, resolve_doc_link} ;
12
+ use ide_db:: { defs:: Definition , RootDatabase } ;
13
+
14
+ use hir:: {
15
+ db:: { DefDatabase , HirDatabase } ,
16
+ Adt , AsName , AssocItem , Crate , Field , HasAttrs , ItemInNs , ModuleDef ,
17
+ } ;
13
18
use ide_db:: {
14
19
defs:: { classify_name, classify_name_ref, Definition } ,
15
20
RootDatabase ,
16
21
} ;
17
22
use syntax:: { ast, match_ast, AstNode , SyntaxKind :: * , SyntaxToken , TokenAtOffset , T } ;
18
23
24
+ use crate :: { FilePosition , Semantics } ;
25
+
19
26
pub type DocumentationLink = String ;
20
27
21
28
/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
@@ -100,64 +107,58 @@ pub fn get_doc_link<T: Resolvable + Clone>(db: &dyn HirDatabase, definition: &T)
100
107
// BUG: For Option
101
108
// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some
102
109
// Instead of https://doc.rust-lang.org/nightly/core/option/enum.Option.html
103
- fn get_doc_link_impl ( db : & dyn HirDatabase , moddef : & ModuleDef ) -> Option < String > {
110
+ // This could be worked around by turning the `EnumVariant` into `Enum` before attempting resolution,
111
+ // but it's really just working around the problem. Ideally we need to implement a slightly different
112
+ // version of import map which follows the same process as rustdoc. Otherwise there'll always be some
113
+ // edge cases where we select the wrong import path.
114
+ fn get_doc_link ( db : & RootDatabase , definition : Definition ) -> Option < String > {
104
115
// Get the outermost definition for the moduledef. This is used to resolve the public path to the type,
105
116
// then we can join the method, field, etc onto it if required.
106
- let target_def: ModuleDef = match moddef {
107
- ModuleDef :: Function ( f) => match f. as_assoc_item ( db) . map ( |assoc| assoc. container ( db) ) {
108
- Some ( AssocItemContainer :: Trait ( t) ) => t. into ( ) ,
109
- Some ( AssocItemContainer :: ImplDef ( imp) ) => {
110
- let resolver = ModuleId :: from ( imp. module ( db) ) . resolver ( db. upcast ( ) ) ;
111
- let ctx = TyLoweringContext :: new ( db, & resolver) ;
112
- Adt :: from (
113
- Ty :: from_hir (
114
- & ctx,
115
- & imp. target_trait ( db) . unwrap_or_else ( || imp. target_type ( db) ) ,
116
- )
117
- . as_adt ( )
118
- . map ( |t| t. 0 )
119
- . unwrap ( ) ,
120
- )
121
- . into ( )
117
+ let target_def: ModuleDef = match definition {
118
+ Definition :: ModuleDef ( moddef) => match moddef {
119
+ ModuleDef :: Function ( f) => {
120
+ f. parent_def ( db) . map ( |mowner| mowner. into ( ) ) . unwrap_or_else ( || f. clone ( ) . into ( ) )
122
121
}
123
- None => ModuleDef :: Function ( * f ) ,
122
+ moddef => moddef ,
124
123
} ,
125
- moddef => * moddef,
124
+ Definition :: Field ( f) => f. parent_def ( db) . into ( ) ,
125
+ // FIXME: Handle macros
126
+ _ => return None ,
126
127
} ;
127
128
128
129
let ns = ItemInNs :: Types ( target_def. clone ( ) . into ( ) ) ;
129
130
130
- let module = moddef . module ( db) ?;
131
+ let module = definition . module ( db) ?;
131
132
let krate = module. krate ( ) ;
132
133
let import_map = db. import_map ( krate. into ( ) ) ;
133
134
let base = once ( krate. display_name ( db) . unwrap ( ) )
134
135
. chain ( import_map. path_of ( ns) . unwrap ( ) . segments . iter ( ) . map ( |name| format ! ( "{}" , name) ) )
135
136
. join ( "/" ) ;
136
137
137
- get_doc_url ( db, & krate)
138
- . and_then ( |url| url. join ( & base) . ok ( ) )
139
- . and_then ( |url| {
140
- get_symbol_filename ( db, & target_def) . as_deref ( ) . and_then ( |f| url. join ( f) . ok ( ) )
141
- } )
142
- . and_then ( |url| match moddef {
138
+ let filename = get_symbol_filename ( db, & target_def) ;
139
+ let fragment = match definition {
140
+ Definition :: ModuleDef ( moddef) => match moddef {
143
141
ModuleDef :: Function ( f) => {
144
- get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Function ( * f) ) )
145
- . as_deref ( )
146
- . and_then ( |f| url. join ( f) . ok ( ) )
142
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Function ( f) ) )
147
143
}
148
144
ModuleDef :: Const ( c) => {
149
- get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Const ( * c) ) )
150
- . as_deref ( )
151
- . and_then ( |f| url. join ( f) . ok ( ) )
145
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: Const ( c) ) )
152
146
}
153
147
ModuleDef :: TypeAlias ( ty) => {
154
- get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: TypeAlias ( * ty) ) )
155
- . as_deref ( )
156
- . and_then ( |f| url. join ( f) . ok ( ) )
148
+ get_symbol_fragment ( db, & FieldOrAssocItem :: AssocItem ( AssocItem :: TypeAlias ( ty) ) )
157
149
}
158
- // TODO: Field <- this requires passing in a definition or something
159
- _ => Some ( url) ,
160
- } )
150
+ _ => None ,
151
+ } ,
152
+ Definition :: Field ( field) => get_symbol_fragment ( db, & FieldOrAssocItem :: Field ( field) ) ,
153
+ _ => None ,
154
+ } ;
155
+
156
+ get_doc_url ( db, & krate)
157
+ . and_then ( |url| url. join ( & base) . ok ( ) )
158
+ . and_then ( |url| filename. as_deref ( ) . and_then ( |f| url. join ( f) . ok ( ) ) )
159
+ . and_then (
160
+ |url| if let Some ( fragment) = fragment { url. join ( & fragment) . ok ( ) } else { Some ( url) } ,
161
+ )
161
162
. map ( |url| url. into_string ( ) )
162
163
}
163
164
@@ -219,9 +220,8 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
219
220
. map ( |url| url. into_string ( ) )
220
221
}
221
222
222
- // FIXME: This should either be moved, or the module should be renamed.
223
223
/// Retrieve a link to documentation for the given symbol.
224
- pub fn get_doc_url ( db : & RootDatabase , position : & FilePosition ) -> Option < DocumentationLink > {
224
+ pub fn external_docs ( db : & RootDatabase , position : & FilePosition ) -> Option < DocumentationLink > {
225
225
let sema = Semantics :: new ( db) ;
226
226
let file = sema. parse ( position. file_id ) . syntax ( ) . clone ( ) ;
227
227
let token = pick_best ( file. token_at_offset ( position. offset ) ) ?;
@@ -236,14 +236,7 @@ pub fn get_doc_url(db: &RootDatabase, position: &FilePosition) -> Option<Documen
236
236
}
237
237
} ;
238
238
239
- match definition? {
240
- Definition :: Macro ( t) => get_doc_link ( db, & t) ,
241
- Definition :: Field ( t) => get_doc_link ( db, & t) ,
242
- Definition :: ModuleDef ( t) => get_doc_link ( db, & t) ,
243
- Definition :: SelfType ( t) => get_doc_link ( db, & t) ,
244
- Definition :: Local ( t) => get_doc_link ( db, & t) ,
245
- Definition :: TypeParam ( t) => get_doc_link ( db, & t) ,
246
- }
239
+ get_doc_link ( db, definition?)
247
240
}
248
241
249
242
/// Rewrites a markdown document, applying 'callback' to each link.
0 commit comments