@@ -4,19 +4,21 @@ use std::sync::Arc;
4
4
5
5
use hir:: {
6
6
Adt , AsAssocItem , AssocItemContainer , FieldSource , HasSource , HirDisplay , ModuleDef ,
7
- ModuleSource , Semantics , Documentation , AttrDef , Crate
7
+ ModuleSource , Semantics , Documentation , AttrDef , Crate , GenericDef , ModPath , Hygiene
8
8
} ;
9
9
use itertools:: Itertools ;
10
10
use ra_db:: SourceDatabase ;
11
11
use ra_ide_db:: {
12
12
defs:: { classify_name, classify_name_ref, Definition } ,
13
13
RootDatabase ,
14
14
} ;
15
- use ra_syntax:: { ast, match_ast, AstNode , SyntaxKind :: * , SyntaxToken , TokenAtOffset } ;
15
+ use ra_syntax:: { ast, match_ast, AstNode , SyntaxKind :: * , SyntaxToken , SyntaxNode , TokenAtOffset , ast :: Path } ;
16
16
use ra_project_model:: ProjectWorkspace ;
17
- use ra_hir_def:: { item_scope:: ItemInNs , db:: DefDatabase } ;
17
+ use ra_hir_def:: { item_scope:: ItemInNs , db:: DefDatabase , GenericDefId , ModuleId , resolver :: HasResolver } ;
18
18
use ra_tt:: { Literal , Ident , Punct , TokenTree , Leaf } ;
19
19
use ra_hir_expand:: name:: AsName ;
20
+ use ra_parser:: FragmentKind ;
21
+ use maplit:: { hashset, hashmap} ;
20
22
21
23
use comrak:: { parse_document, format_commonmark, ComrakOptions , Arena } ;
22
24
use comrak:: nodes:: NodeValue ;
@@ -412,8 +414,9 @@ fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition, wor
412
414
// module-based links (AKA intra-doc links): `super::super::module::MyStruct`
413
415
Err ( _) => {
414
416
let link_str = String :: from_utf8 ( link. url . clone ( ) ) . unwrap ( ) ;
417
+ let link_text = String :: from_utf8 ( link. title . clone ( ) ) . unwrap ( ) ;
415
418
let resolved = try_resolve_path ( db, & mut doc_target_dirs. clone ( ) , definition, & link_str, UrlMode :: Url )
416
- . or_else ( || try_resolve_intra ( db, & mut doc_target_dirs. clone ( ) , definition, & link_str) ) ;
419
+ . or_else ( || try_resolve_intra ( db, & mut doc_target_dirs. clone ( ) , definition, & link_text , & link_str) ) ;
417
420
418
421
if let Some ( resolved) = resolved {
419
422
link. url = resolved. as_bytes ( ) . to_vec ( ) ;
@@ -430,11 +433,113 @@ fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition, wor
430
433
Some ( String :: from_utf8 ( out) . unwrap ( ) )
431
434
}
432
435
436
+ #[ derive( PartialEq , Eq , Hash , Copy , Clone , Debug ) ]
437
+ enum Namespace {
438
+ Types ,
439
+ Values ,
440
+ Macros
441
+ }
442
+
443
+ impl Namespace {
444
+ /// Extract the specified namespace from an intra-doc-link if one exists.
445
+ fn from_intra_spec ( s : & str ) -> Option < Self > {
446
+ let ns_map = hashmap ! {
447
+ Self :: Types => ( hashset!{ "type" , "struct" , "enum" , "mod" , "trait" , "union" , "module" } , hashset!{ } ) ,
448
+ Self :: Values => ( hashset!{ "value" , "function" , "fn" , "method" , "const" , "static" , "mod" , "module" } , hashset!{ "()" } ) ,
449
+ Self :: Macros => ( hashset!{ "macro" } , hashset!{ "!" } )
450
+ } ;
451
+
452
+ ns_map
453
+ . iter ( )
454
+ . filter ( |( ns, ( prefixes, suffixes) ) | {
455
+ prefixes. iter ( ) . map ( |prefix| s. starts_with ( prefix) && s. chars ( ) . nth ( prefix. len ( ) +1 ) . map ( |c| c == '@' || c == ' ' ) . unwrap_or ( false ) ) . any ( |cond| cond) ||
456
+ suffixes. iter ( ) . map ( |suffix| s. starts_with ( suffix) && s. chars ( ) . nth ( suffix. len ( ) +1 ) . map ( |c| c == '@' || c == ' ' ) . unwrap_or ( false ) ) . any ( |cond| cond)
457
+ } )
458
+ . map ( |( ns, ( _, _) ) | * ns)
459
+ . next ( )
460
+ }
461
+ }
462
+
433
463
/// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`).
434
464
///
435
465
/// See [RFC1946](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md).
436
- fn try_resolve_intra ( db : & RootDatabase , doc_target_dirs : impl Iterator < Item = PathBuf > , definition : & Definition , link : & str ) -> Option < String > {
437
- None
466
+ fn try_resolve_intra ( db : & RootDatabase , doc_target_dirs : impl Iterator < Item = PathBuf > , definition : & Definition , link_text : & str , link_target : & str ) -> Option < String > {
467
+ eprintln ! ( "try_resolve_intra" ) ;
468
+
469
+ // Set link_target for implied shortlinks
470
+ let link_target = if link_target. is_empty ( ) {
471
+ link_text. trim_matches ( '`' )
472
+ } else {
473
+ link_target
474
+ } ;
475
+
476
+ // Parse link as a module path
477
+ // This expects a full document, which a single path isn't, but we can just ignore the errors.
478
+ let parsed = SyntaxNode :: new_root ( ra_syntax:: parse_text ( link_target) . 0 ) ;
479
+ let path = parsed. descendants ( ) . filter_map ( Path :: cast) . next ( ) ?;
480
+ let modpath = ModPath :: from_src ( path, & Hygiene :: new_unhygienic ( ) ) . unwrap ( ) ;
481
+
482
+ // Resolve it relative to symbol's location (according to the RFC this should consider small scopes
483
+ let resolver = {
484
+ use ra_hir_def:: * ;
485
+ use hir:: * ;
486
+
487
+ // TODO: This should be replaced by implementing HasResolver/TryHasResolver on ModuleDef and Definition.
488
+ match definition {
489
+ Definition :: ModuleDef ( def) => match def {
490
+ ModuleDef :: Module ( m) => Into :: < ModuleId > :: into ( m. clone ( ) ) . resolver ( db) ,
491
+ ModuleDef :: Function ( f) => Into :: < FunctionId > :: into ( f. clone ( ) ) . resolver ( db) ,
492
+ ModuleDef :: Adt ( adt) => Into :: < AdtId > :: into ( adt. clone ( ) ) . resolver ( db) ,
493
+ ModuleDef :: EnumVariant ( ev) => Into :: < GenericDefId > :: into ( Into :: < GenericDef > :: into ( ev. clone ( ) ) ) . resolver ( db) ,
494
+ ModuleDef :: Const ( c) => Into :: < GenericDefId > :: into ( Into :: < GenericDef > :: into ( c. clone ( ) ) ) . resolver ( db) ,
495
+ ModuleDef :: Static ( s) => Into :: < StaticId > :: into ( s. clone ( ) ) . resolver ( db) ,
496
+ ModuleDef :: Trait ( t) => Into :: < TraitId > :: into ( t. clone ( ) ) . resolver ( db) ,
497
+ ModuleDef :: TypeAlias ( t) => Into :: < ModuleId > :: into ( t. module ( db) ) . resolver ( db) ,
498
+ // TODO: This should be a resolver relative to `std`
499
+ ModuleDef :: BuiltinType ( t) => Into :: < ModuleId > :: into ( definition. module ( db) ?) . resolver ( db)
500
+ } ,
501
+ Definition :: Field ( field) => Into :: < VariantId > :: into ( Into :: < VariantDef > :: into ( field. parent_def ( db) ) ) . resolver ( db) ,
502
+ Definition :: Macro ( m) => Into :: < ModuleId > :: into ( m. module ( db) ?) . resolver ( db) ,
503
+ Definition :: SelfType ( imp) => Into :: < ImplId > :: into ( imp. clone ( ) ) . resolver ( db) ,
504
+ // it's possible, read probable, that other arms of this are also unreachable
505
+ Definition :: Local ( local) => unreachable ! ( ) ,
506
+ Definition :: TypeParam ( tp) => Into :: < ModuleId > :: into ( tp. module ( db) ) . resolver ( db)
507
+ }
508
+ } ;
509
+
510
+ // Namespace disambiguation
511
+ let namespace = Namespace :: from_intra_spec ( link_target) ;
512
+
513
+ let resolved = resolver. resolve_module_path_in_items ( db, & modpath) ;
514
+ let ( defid, namespace) = match namespace {
515
+ // TODO: .or(resolved.macros)
516
+ None => resolved. types . map ( |t| ( t. 0 , Namespace :: Types ) ) . or ( resolved. values . map ( |t| ( t. 0 , Namespace :: Values ) ) ) ?,
517
+ Some ( ns @ Namespace :: Types ) => ( resolved. types ?. 0 , ns) ,
518
+ Some ( ns @ Namespace :: Values ) => ( resolved. values ?. 0 , ns) ,
519
+ // TODO:
520
+ Some ( Namespace :: Macros ) => None ?
521
+ } ;
522
+
523
+ // Get the filepath of the final symbol
524
+ let def: ModuleDef = defid. into ( ) ;
525
+ let module = def. module ( db) ?;
526
+ let krate = module. krate ( ) ;
527
+ let ns = match namespace {
528
+ Namespace :: Types => ItemInNs :: Types ( defid) ,
529
+ Namespace :: Values => ItemInNs :: Values ( defid) ,
530
+ // TODO:
531
+ Namespace :: Macros => None ?
532
+ } ;
533
+ let import_map = db. import_map ( krate. into ( ) ) ;
534
+ let path = import_map. path_of ( ns) ?;
535
+
536
+ Some (
537
+ get_doc_url ( db, & krate) ?
538
+ . join ( & format ! ( "{}/" , krate. display_name( db) ?) ) . ok ( ) ?
539
+ . join ( & path. segments . iter ( ) . map ( |name| format ! ( "{}" , name) ) . join ( "/" ) ) . ok ( ) ?
540
+ . join ( & get_symbol_filename ( db, definition) ?) . ok ( ) ?
541
+ . into_string ( )
542
+ )
438
543
}
439
544
440
545
enum UrlMode {
@@ -444,6 +549,7 @@ enum UrlMode {
444
549
445
550
/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
446
551
fn try_resolve_path ( db : & RootDatabase , doc_target_dirs : impl Iterator < Item = PathBuf > , definition : & Definition , link : & str , mode : UrlMode ) -> Option < String > {
552
+ eprintln ! ( "try_resolve_path" ) ;
447
553
let ns = if let Definition :: ModuleDef ( moddef) = definition {
448
554
ItemInNs :: Types ( moddef. clone ( ) . into ( ) )
449
555
} else {
@@ -481,6 +587,7 @@ fn try_resolve_path(db: &RootDatabase, doc_target_dirs: impl Iterator<Item = Pat
481
587
}
482
588
483
589
/// Try to get the root URL of the documentation of a crate.
590
+ // TODO: Special case standard, core, alloc libraries
484
591
fn get_doc_url ( db : & RootDatabase , krate : & Crate ) -> Option < Url > {
485
592
// Look for #![doc(html_root_url = "...")]
486
593
let attrs = db. attrs ( AttrDef :: from ( krate. root_module ( db) ?) . into ( ) ) ;
0 commit comments