1
1
//! Extracts, resolves and rewrites links and intra-doc links in markdown documentation.
2
2
3
- use std:: { convert:: TryFrom , iter:: once, ops:: Range } ;
3
+ use std:: {
4
+ convert:: { TryFrom , TryInto } ,
5
+ iter:: once,
6
+ } ;
4
7
5
8
use itertools:: Itertools ;
6
9
use pulldown_cmark:: { BrokenLink , CowStr , Event , InlineStr , LinkType , Options , Parser , Tag } ;
@@ -16,8 +19,7 @@ use ide_db::{
16
19
RootDatabase ,
17
20
} ;
18
21
use syntax:: {
19
- ast, match_ast, AstNode , AstToken , SyntaxKind :: * , SyntaxNode , SyntaxToken , TextRange , TextSize ,
20
- TokenAtOffset , T ,
22
+ ast, match_ast, AstNode , SyntaxKind :: * , SyntaxNode , SyntaxToken , TextRange , TokenAtOffset , T ,
21
23
} ;
22
24
23
25
use crate :: { FilePosition , Semantics } ;
@@ -26,12 +28,7 @@ pub(crate) type DocumentationLink = String;
26
28
27
29
/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
28
30
pub ( crate ) fn rewrite_links ( db : & RootDatabase , markdown : & str , definition : & Definition ) -> String {
29
- let mut cb = |link : BrokenLink | {
30
- Some ( (
31
- /*url*/ link. reference . to_owned ( ) . into ( ) ,
32
- /*title*/ link. reference . to_owned ( ) . into ( ) ,
33
- ) )
34
- } ;
31
+ let mut cb = broken_link_clone_cb;
35
32
let doc = Parser :: new_with_broken_link_callback ( markdown, Options :: empty ( ) , Some ( & mut cb) ) ;
36
33
37
34
let doc = map_links ( doc, |target, title : & str | {
@@ -123,74 +120,27 @@ pub(crate) fn external_docs(
123
120
/// Extracts all links from a given markdown text.
124
121
pub ( crate ) fn extract_definitions_from_markdown (
125
122
markdown : & str ,
126
- ) -> Vec < ( Range < usize > , String , Option < hir:: Namespace > ) > {
127
- let mut res = vec ! [ ] ;
128
- let mut cb = |link : BrokenLink | {
129
- // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong
130
- // this is fixed in the repo but not on the crates.io release yet
131
- Some ( (
132
- /*url*/ link. reference . to_owned ( ) . into ( ) ,
133
- /*title*/ link. reference . to_owned ( ) . into ( ) ,
134
- ) )
135
- } ;
136
- let doc = Parser :: new_with_broken_link_callback ( markdown, Options :: empty ( ) , Some ( & mut cb) ) ;
137
- for ( event, range) in doc. into_offset_iter ( ) {
123
+ ) -> Vec < ( TextRange , String , Option < hir:: Namespace > ) > {
124
+ Parser :: new_with_broken_link_callback (
125
+ markdown,
126
+ Options :: empty ( ) ,
127
+ Some ( & mut broken_link_clone_cb) ,
128
+ )
129
+ . into_offset_iter ( )
130
+ . filter_map ( |( event, range) | {
138
131
if let Event :: Start ( Tag :: Link ( _, target, title) ) = event {
139
132
let link = if target. is_empty ( ) { title } else { target } ;
140
133
let ( link, ns) = parse_intra_doc_link ( & link) ;
141
- res. push ( ( range, link. to_string ( ) , ns) ) ;
142
- }
143
- }
144
- res
145
- }
146
-
147
- /// Extracts a link from a comment at the given position returning the spanning range, link and
148
- /// optionally it's namespace.
149
- pub ( crate ) fn extract_positioned_link_from_comment (
150
- position : TextSize ,
151
- comment : & ast:: Comment ,
152
- ) -> Option < ( TextRange , String , Option < hir:: Namespace > ) > {
153
- let doc_comment = comment. doc_comment ( ) ?;
154
- let comment_start =
155
- comment. syntax ( ) . text_range ( ) . start ( ) + TextSize :: from ( comment. prefix ( ) . len ( ) as u32 ) ;
156
- let def_links = extract_definitions_from_markdown ( doc_comment) ;
157
- let ( range, def_link, ns) =
158
- def_links. into_iter ( ) . find_map ( |( Range { start, end } , def_link, ns) | {
159
- let range = TextRange :: at (
160
- comment_start + TextSize :: from ( start as u32 ) ,
161
- TextSize :: from ( ( end - start) as u32 ) ,
162
- ) ;
163
- range. contains ( position) . then ( || ( range, def_link, ns) )
164
- } ) ?;
165
- Some ( ( range, def_link, ns) )
166
- }
167
-
168
- /// Turns a syntax node into it's [`Definition`] if it can hold docs.
169
- pub ( crate ) fn doc_owner_to_def (
170
- sema : & Semantics < RootDatabase > ,
171
- item : & SyntaxNode ,
172
- ) -> Option < Definition > {
173
- let res: hir:: ModuleDef = match_ast ! {
174
- match item {
175
- ast:: SourceFile ( _it) => sema. scope( item) . module( ) ?. into( ) ,
176
- ast:: Fn ( it) => sema. to_def( & it) ?. into( ) ,
177
- ast:: Struct ( it) => sema. to_def( & it) ?. into( ) ,
178
- ast:: Enum ( it) => sema. to_def( & it) ?. into( ) ,
179
- ast:: Union ( it) => sema. to_def( & it) ?. into( ) ,
180
- ast:: Trait ( it) => sema. to_def( & it) ?. into( ) ,
181
- ast:: Const ( it) => sema. to_def( & it) ?. into( ) ,
182
- ast:: Static ( it) => sema. to_def( & it) ?. into( ) ,
183
- ast:: TypeAlias ( it) => sema. to_def( & it) ?. into( ) ,
184
- ast:: Variant ( it) => sema. to_def( & it) ?. into( ) ,
185
- ast:: Trait ( it) => sema. to_def( & it) ?. into( ) ,
186
- ast:: Impl ( it) => return sema. to_def( & it) . map( Definition :: SelfType ) ,
187
- ast:: Macro ( it) => return sema. to_def( & it) . map( Definition :: Macro ) ,
188
- ast:: TupleField ( it) => return sema. to_def( & it) . map( Definition :: Field ) ,
189
- ast:: RecordField ( it) => return sema. to_def( & it) . map( Definition :: Field ) ,
190
- _ => return None ,
134
+ Some ( (
135
+ TextRange :: new ( range. start . try_into ( ) . ok ( ) ?, range. end . try_into ( ) . ok ( ) ?) ,
136
+ link. to_string ( ) ,
137
+ ns,
138
+ ) )
139
+ } else {
140
+ None
191
141
}
192
- } ;
193
- Some ( Definition :: ModuleDef ( res ) )
142
+ } )
143
+ . collect ( )
194
144
}
195
145
196
146
pub ( crate ) fn resolve_doc_path_for_def (
@@ -220,6 +170,42 @@ pub(crate) fn resolve_doc_path_for_def(
220
170
}
221
171
}
222
172
173
+ pub ( crate ) fn doc_attributes (
174
+ sema : & Semantics < RootDatabase > ,
175
+ node : & SyntaxNode ,
176
+ ) -> Option < ( hir:: AttrsWithOwner , Definition ) > {
177
+ match_ast ! {
178
+ match node {
179
+ ast:: SourceFile ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Module ( def) ) ) ) ,
180
+ ast:: Module ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Module ( def) ) ) ) ,
181
+ ast:: Fn ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Function ( def) ) ) ) ,
182
+ ast:: Struct ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Adt ( hir:: Adt :: Struct ( def) ) ) ) ) ,
183
+ ast:: Union ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Adt ( hir:: Adt :: Union ( def) ) ) ) ) ,
184
+ ast:: Enum ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Adt ( hir:: Adt :: Enum ( def) ) ) ) ) ,
185
+ ast:: Variant ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Variant ( def) ) ) ) ,
186
+ ast:: Trait ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Trait ( def) ) ) ) ,
187
+ ast:: Static ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Static ( def) ) ) ) ,
188
+ ast:: Const ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: Const ( def) ) ) ) ,
189
+ ast:: TypeAlias ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: ModuleDef ( hir:: ModuleDef :: TypeAlias ( def) ) ) ) ,
190
+ ast:: Impl ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: SelfType ( def) ) ) ,
191
+ ast:: RecordField ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: Field ( def) ) ) ,
192
+ ast:: TupleField ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: Field ( def) ) ) ,
193
+ ast:: Macro ( it) => sema. to_def( & it) . map( |def| ( def. attrs( sema. db) , Definition :: Macro ( def) ) ) ,
194
+ // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
195
+ _ => return None
196
+ }
197
+ }
198
+ }
199
+
200
+ fn broken_link_clone_cb < ' a , ' b > ( link : BrokenLink < ' a > ) -> Option < ( CowStr < ' b > , CowStr < ' b > ) > {
201
+ // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong
202
+ // this is fixed in the repo but not on the crates.io release yet
203
+ Some ( (
204
+ /*url*/ link. reference . to_owned ( ) . into ( ) ,
205
+ /*title*/ link. reference . to_owned ( ) . into ( ) ,
206
+ ) )
207
+ }
208
+
223
209
// FIXME:
224
210
// BUG: For Option::Some
225
211
// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some
0 commit comments