@@ -17,7 +17,7 @@ use ra_ide_db::{
17
17
RootDatabase ,
18
18
} ;
19
19
use ra_syntax:: {
20
- ast, ast:: Path , match_ast, AstNode , SyntaxKind :: * , SyntaxNode , SyntaxToken , TokenAtOffset ,
20
+ ast, ast:: { Path , MacroCall } , match_ast, AstNode , SyntaxKind :: * , SyntaxNode , SyntaxToken , TokenAtOffset ,
21
21
} ;
22
22
use ra_tt:: { Ident , Leaf , Literal , TokenTree } ;
23
23
use url:: Url ;
@@ -394,35 +394,36 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
394
394
// Rewrites a markdown document, resolving links using `callback` and additionally striping prefixes/suffixes on link titles.
395
395
fn map_links < ' e > (
396
396
events : impl Iterator < Item = Event < ' e > > ,
397
- callback : impl Fn ( & str , & str ) -> String ,
397
+ callback : impl Fn ( & str , & str ) -> ( String , String ) ,
398
398
) -> impl Iterator < Item = Event < ' e > > {
399
399
let mut in_link = false ;
400
- let mut link_text = CowStr :: Borrowed ( "" ) ;
400
+ let mut link_target: Option < CowStr > = None ;
401
+
401
402
events. map ( move |evt| match evt {
402
- Event :: Start ( Tag :: Link ( .. ) ) => {
403
+ Event :: Start ( Tag :: Link ( _link_type , ref target , _ ) ) => {
403
404
in_link = true ;
405
+ link_target = Some ( target. clone ( ) ) ;
404
406
evt
405
407
}
406
- Event :: End ( Tag :: Link ( link_type, target , _) ) => {
408
+ Event :: End ( Tag :: Link ( link_type, _target , _) ) => {
407
409
in_link = false ;
408
- let target = callback ( & target, & link_text) ;
409
- Event :: End ( Tag :: Link ( link_type, CowStr :: Boxed ( target. into ( ) ) , CowStr :: Borrowed ( "" ) ) )
410
+ Event :: End ( Tag :: Link ( link_type, link_target. take ( ) . unwrap ( ) , CowStr :: Borrowed ( "" ) ) )
410
411
}
411
412
Event :: Text ( s) if in_link => {
412
- link_text = s. clone ( ) ;
413
- // TODO: This can unintentionally strip words from path-based links.
414
- // See std::box::Box -> std::box link as an example.
415
- Event :: Text ( CowStr :: Boxed ( strip_prefixes_suffixes ( & s) . into ( ) ) )
413
+ let ( link_target_s, link_name) = callback ( & link_target. take ( ) . unwrap ( ) , & s) ;
414
+ link_target = Some ( CowStr :: Boxed ( link_target_s. into ( ) ) ) ;
415
+ Event :: Text ( CowStr :: Boxed ( link_name. into ( ) ) )
416
416
}
417
417
Event :: Code ( s) if in_link => {
418
- link_text = s. clone ( ) ;
419
- Event :: Code ( CowStr :: Boxed ( strip_prefixes_suffixes ( & s) . into ( ) ) )
418
+ let ( link_target_s, link_name) = callback ( & link_target. take ( ) . unwrap ( ) , & s) ;
419
+ link_target = Some ( CowStr :: Boxed ( link_target_s. into ( ) ) ) ;
420
+ Event :: Code ( CowStr :: Boxed ( link_name. into ( ) ) )
420
421
}
421
422
_ => evt,
422
423
} )
423
424
}
424
425
425
- /// Rewrite documentation links in markdown to point to local documentation/ docs.rs
426
+ /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
426
427
fn rewrite_links ( db : & RootDatabase , markdown : & str , definition : & Definition ) -> Option < String > {
427
428
let doc = Parser :: new_with_broken_link_callback (
428
429
markdown,
@@ -431,21 +432,22 @@ fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) ->
431
432
) ;
432
433
433
434
let doc = map_links ( doc, |target, title : & str | {
434
- match Url :: parse ( target) {
435
- // If this is a valid absolute URL don't touch it
436
- Ok ( _) => target. to_string ( ) ,
437
- // Otherwise there are two main possibilities
438
- // path-based links: `../../module/struct.MyStruct.html`
439
- // module-based links (AKA intra-doc links): `super::super::module::MyStruct`
440
- Err ( _) => {
441
- let resolved = try_resolve_intra ( db, definition, title, & target)
442
- . or_else ( || try_resolve_path ( db, definition, & target) ) ;
443
-
444
- if let Some ( resolved) = resolved {
445
- resolved
446
- } else {
447
- target. to_string ( )
448
- }
435
+ // This check is imperfect, there's some overlap between valid intra-doc links
436
+ // and valid URLs so we choose to be too eager to try to resolve what might be
437
+ // a URL.
438
+ if target. contains ( "://" ) {
439
+ ( target. to_string ( ) , title. to_string ( ) )
440
+ } else {
441
+ // Two posibilities:
442
+ // * path-based links: `../../module/struct.MyStruct.html`
443
+ // * module-based links (AKA intra-doc links): `super::super::module::MyStruct`
444
+ let resolved = try_resolve_intra ( db, definition, title, & target)
445
+ . or_else ( || try_resolve_path ( db, definition, & target) . map ( |target| ( target, title. to_string ( ) ) ) ) ;
446
+
447
+ if let Some ( ( target, title) ) = resolved {
448
+ ( target, title)
449
+ } else {
450
+ ( target. to_string ( ) , title. to_string ( ) )
449
451
}
450
452
}
451
453
} ) ;
@@ -520,7 +522,7 @@ fn try_resolve_intra(
520
522
definition : & Definition ,
521
523
link_text : & str ,
522
524
link_target : & str ,
523
- ) -> Option < String > {
525
+ ) -> Option < ( String , String ) > {
524
526
// Set link_target for implied shortlinks
525
527
let link_target =
526
528
if link_target. is_empty ( ) { link_text. trim_matches ( '`' ) } else { link_target } ;
@@ -534,6 +536,7 @@ fn try_resolve_intra(
534
536
// Parse link as a module path
535
537
// This expects a full document, which a single path isn't, but we can just ignore the errors.
536
538
let parsed = SyntaxNode :: new_root ( ra_syntax:: parse_text ( link_target) . 0 ) ;
539
+ // TODO: Proper parsing
537
540
let path = parsed. descendants ( ) . filter_map ( Path :: cast) . next ( ) ?;
538
541
let modpath = ModPath :: from_src ( path, & Hygiene :: new_unhygienic ( ) ) . unwrap ( ) ;
539
542
@@ -566,7 +569,7 @@ fn try_resolve_intra(
566
569
let import_map = db. import_map ( krate. into ( ) ) ;
567
570
let path = import_map. path_of ( ns) ?;
568
571
569
- Some (
572
+ Some ( (
570
573
get_doc_url ( db, & krate) ?
571
574
. join ( & format ! ( "{}/" , krate. display_name( db) ?) )
572
575
. ok ( ) ?
@@ -575,7 +578,7 @@ fn try_resolve_intra(
575
578
. join ( & get_symbol_filename ( db, & Definition :: ModuleDef ( def) ) ?)
576
579
. ok ( ) ?
577
580
. into_string ( ) ,
578
- )
581
+ strip_prefixes_suffixes ( link_text ) . to_string ( ) ) )
579
582
}
580
583
581
584
/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
@@ -1485,15 +1488,30 @@ fn func(foo: i32) { if true { <|>foo; }; }
1485
1488
}
1486
1489
1487
1490
#[ test]
1488
- fn test_hover_intra_link ( ) {
1491
+ fn test_hover_path_link_no_strip ( ) {
1489
1492
check_hover_result (
1490
1493
r"
1491
1494
//- /lib.rs
1492
1495
pub struct Foo;
1493
- /// [Foo](Foo)
1496
+ /// [struct Foo](struct. Foo.html )
1494
1497
pub struct B<|>ar
1495
1498
" ,
1496
- & [ "pub struct Bar\n ```\n ___\n \n [Foo](https://docs.rs/test/*/test/struct.Foo.html)" ] ,
1499
+ & [ "pub struct Bar\n ```\n ___\n \n [struct Foo](https://docs.rs/test/*/test/struct.Foo.html)" ] ,
1500
+ ) ;
1501
+ }
1502
+
1503
+ #[ test]
1504
+ fn test_hover_intra_link ( ) {
1505
+ check_hover_result (
1506
+ r"
1507
+ //- /lib.rs
1508
+ pub mod foo {
1509
+ pub struct Foo;
1510
+ }
1511
+ /// [Foo](foo::Foo)
1512
+ pub struct B<|>ar
1513
+ " ,
1514
+ & [ "pub struct Bar\n ```\n ___\n \n [Foo](https://docs.rs/test/*/test/foo/struct.Foo.html)" ] ,
1497
1515
) ;
1498
1516
}
1499
1517
0 commit comments