@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
66use std:: process:: ExitStatus ;
77use std:: str:: FromStr ;
88
9- use anyhow:: { anyhow, Error , Result } ;
9+ use anyhow:: { anyhow, Context , Error , Result } ;
1010use clap:: { builder:: PossibleValue , Args , CommandFactory , Parser , Subcommand , ValueEnum } ;
1111use clap_complete:: Shell ;
1212use itertools:: Itertools ;
@@ -1471,6 +1471,29 @@ impl DocPage {
14711471 fn name ( & self ) -> Option < & ' static str > {
14721472 Some ( self . path_str ( ) ?. rsplit_once ( '/' ) ?. 0 )
14731473 }
1474+
1475+ fn resolve < ' t > ( & self , root : & Path , topic : & ' t str ) -> Option < ( PathBuf , Option < & ' t str > ) > {
1476+ // Use `.parent()` to chop off the default top-level `index.html`.
1477+ let mut base = root. join ( Path :: new ( self . path ( ) ?) . parent ( ) ?) ;
1478+ base. extend ( topic. split ( "::" ) ) ;
1479+ let base_index_html = base. join ( "index.html" ) ;
1480+
1481+ if base_index_html. is_file ( ) {
1482+ return Some ( ( base_index_html, None ) ) ;
1483+ }
1484+
1485+ let base_html = base. with_extension ( "html" ) ;
1486+ if base_html. is_file ( ) {
1487+ return Some ( ( base_html, None ) ) ;
1488+ }
1489+
1490+ let parent_html = base. parent ( ) ?. with_extension ( "html" ) ;
1491+ if parent_html. is_file ( ) {
1492+ return Some ( ( parent_html, topic. rsplit_once ( "::" ) . map ( |( _, s) | s) ) ) ;
1493+ }
1494+
1495+ None
1496+ }
14741497}
14751498
14761499async fn doc (
@@ -1507,14 +1530,22 @@ async fn doc(
15071530 }
15081531 } ;
15091532
1510- let doc_path = if let Some ( topic) = topic {
1511- Cow :: Owned ( topical_doc:: local_path (
1512- & toolchain. doc_path ( "" ) . unwrap ( ) ,
1513- topic,
1514- ) ?)
1515- } else {
1516- topic = doc_page. name ( ) ;
1517- Cow :: Borrowed ( doc_page. path ( ) . unwrap_or ( Path :: new ( "index.html" ) ) )
1533+ let ( doc_path, fragment) = match ( topic, doc_page. name ( ) ) {
1534+ ( Some ( topic) , Some ( name) ) => {
1535+ let ( doc_path, fragment) = doc_page
1536+ . resolve ( & toolchain. doc_path ( "" ) ?, topic)
1537+ . context ( format ! ( "no document for {name} on {topic}" ) ) ?;
1538+ ( Cow :: Owned ( doc_path) , fragment)
1539+ }
1540+ ( Some ( topic) , None ) => {
1541+ let doc_path = topical_doc:: local_path ( & toolchain. doc_path ( "" ) . unwrap ( ) , topic) ?;
1542+ ( Cow :: Owned ( doc_path) , None )
1543+ }
1544+ ( None , name) => {
1545+ topic = name;
1546+ let doc_path = doc_page. path ( ) . unwrap_or ( Path :: new ( "index.html" ) ) ;
1547+ ( Cow :: Borrowed ( doc_path) , None )
1548+ }
15181549 } ;
15191550
15201551 if path_only {
@@ -1531,7 +1562,7 @@ async fn doc(
15311562 } else {
15321563 writeln ! ( cfg. process. stderr( ) . lock( ) , "Opening docs in your browser" ) ?;
15331564 }
1534- toolchain. open_docs ( & doc_path, None ) ?;
1565+ toolchain. open_docs ( & doc_path, fragment ) ?;
15351566 Ok ( utils:: ExitCode ( 0 ) )
15361567}
15371568
0 commit comments