11//! Extracts, resolves and rewrites links and intra-doc links in markdown documentation.
22
3- use std:: { convert:: TryFrom , iter:: once, ops:: Range } ;
3+ use std:: {
4+ convert:: { TryFrom , TryInto } ,
5+ iter:: once,
6+ } ;
47
58use itertools:: Itertools ;
69use pulldown_cmark:: { BrokenLink , CowStr , Event , InlineStr , LinkType , Options , Parser , Tag } ;
@@ -16,8 +19,7 @@ use ide_db::{
1619 RootDatabase ,
1720} ;
1821use 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 ,
2123} ;
2224
2325use crate :: { FilePosition , Semantics } ;
@@ -26,12 +28,7 @@ pub(crate) type DocumentationLink = String;
2628
2729/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
2830pub ( 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;
3532 let doc = Parser :: new_with_broken_link_callback ( markdown, Options :: empty ( ) , Some ( & mut cb) ) ;
3633
3734 let doc = map_links ( doc, |target, title : & str | {
@@ -123,74 +120,27 @@ pub(crate) fn external_docs(
123120/// Extracts all links from a given markdown text.
124121pub ( crate ) fn extract_definitions_from_markdown (
125122 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) | {
138131 if let Event :: Start ( Tag :: Link ( _, target, title) ) = event {
139132 let link = if target. is_empty ( ) { title } else { target } ;
140133 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
191141 }
192- } ;
193- Some ( Definition :: ModuleDef ( res ) )
142+ } )
143+ . collect ( )
194144}
195145
196146pub ( crate ) fn resolve_doc_path_for_def (
@@ -220,6 +170,42 @@ pub(crate) fn resolve_doc_path_for_def(
220170 }
221171}
222172
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+
223209// FIXME:
224210// BUG: For Option::Some
225211// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some
0 commit comments