@@ -32,6 +32,8 @@ use std::iter::Peekable;
32
32
use std:: ops:: { ControlFlow , Range } ;
33
33
use std:: path:: PathBuf ;
34
34
use std:: str:: { self , CharIndices } ;
35
+ use std:: sync:: atomic:: AtomicUsize ;
36
+ use std:: sync:: { Arc , Weak } ;
35
37
36
38
use pulldown_cmark:: {
37
39
BrokenLink , CodeBlockKind , CowStr , Event , LinkType , Options , Parser , Tag , TagEnd , html,
@@ -1301,8 +1303,20 @@ impl LangString {
1301
1303
}
1302
1304
}
1303
1305
1304
- impl Markdown < ' _ > {
1306
+ impl < ' a > Markdown < ' a > {
1305
1307
pub fn into_string ( self ) -> String {
1308
+ // This is actually common enough to special-case
1309
+ if self . content . is_empty ( ) {
1310
+ return String :: new ( ) ;
1311
+ }
1312
+
1313
+ let mut s = String :: with_capacity ( self . content . len ( ) * 3 / 2 ) ;
1314
+ html:: push_html ( & mut s, self . into_iter ( ) ) ;
1315
+
1316
+ s
1317
+ }
1318
+
1319
+ fn into_iter ( self ) -> CodeBlocks < ' a , ' a , impl Iterator < Item = Event < ' a > > > {
1306
1320
let Markdown {
1307
1321
content : md,
1308
1322
links,
@@ -1313,32 +1327,72 @@ impl Markdown<'_> {
1313
1327
heading_offset,
1314
1328
} = self ;
1315
1329
1316
- // This is actually common enough to special-case
1317
- if md. is_empty ( ) {
1318
- return String :: new ( ) ;
1319
- }
1320
- let mut replacer = |broken_link : BrokenLink < ' _ > | {
1330
+ let replacer = move |broken_link : BrokenLink < ' _ > | {
1321
1331
links
1322
1332
. iter ( )
1323
1333
. find ( |link| * link. original_text == * broken_link. reference )
1324
1334
. map ( |link| ( link. href . as_str ( ) . into ( ) , link. tooltip . as_str ( ) . into ( ) ) )
1325
1335
} ;
1326
1336
1327
- let p = Parser :: new_with_broken_link_callback ( md, main_body_opts ( ) , Some ( & mut replacer) ) ;
1337
+ let p = Parser :: new_with_broken_link_callback ( md, main_body_opts ( ) , Some ( replacer) ) ;
1328
1338
let p = p. into_offset_iter ( ) ;
1329
1339
1330
- let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
1331
-
1332
1340
ids. handle_footnotes ( |ids, existing_footnotes| {
1333
1341
let p = HeadingLinks :: new ( p, None , ids, heading_offset) ;
1334
1342
let p = footnotes:: Footnotes :: new ( p, existing_footnotes) ;
1335
1343
let p = LinkReplacer :: new ( p. map ( |( ev, _) | ev) , links) ;
1336
1344
let p = TableWrapper :: new ( p) ;
1337
- let p = CodeBlocks :: new ( p, codes, edition, playground) ;
1338
- html :: push_html ( & mut s , p ) ;
1339
- } ) ;
1345
+ CodeBlocks :: new ( p, codes, edition, playground)
1346
+ } )
1347
+ }
1340
1348
1341
- s
1349
+ /// Convert markdown to (summary, remaining) HTML.
1350
+ ///
1351
+ /// - The summary is the first top-level Markdown element (usually a paragraph, but potentially
1352
+ /// any block).
1353
+ /// - The remaining docs contain everything after the summary.
1354
+ pub ( crate ) fn split_summary_and_content ( self ) -> ( Option < String > , Option < String > ) {
1355
+ if self . content . is_empty ( ) {
1356
+ return ( None , None ) ;
1357
+ }
1358
+ let mut p = self . into_iter ( ) ;
1359
+
1360
+ let mut event_level = 0 ;
1361
+ let mut summary_events = Vec :: new ( ) ;
1362
+ let mut get_next_tag = false ;
1363
+
1364
+ let mut end_of_summary = false ;
1365
+ while let Some ( event) = p. next ( ) {
1366
+ match event {
1367
+ Event :: Start ( _) => event_level += 1 ,
1368
+ Event :: End ( kind) => {
1369
+ event_level -= 1 ;
1370
+ if event_level == 0 {
1371
+ // We're back at the "top" so it means we're done with the summary.
1372
+ end_of_summary = true ;
1373
+ // We surround tables with `<div>` HTML tags so this is a special case.
1374
+ get_next_tag = kind == TagEnd :: Table ;
1375
+ }
1376
+ }
1377
+ _ => { }
1378
+ }
1379
+ summary_events. push ( event) ;
1380
+ if end_of_summary {
1381
+ if get_next_tag && let Some ( event) = p. next ( ) {
1382
+ summary_events. push ( event) ;
1383
+ }
1384
+ break ;
1385
+ }
1386
+ }
1387
+ let mut summary = String :: new ( ) ;
1388
+ html:: push_html ( & mut summary, summary_events. into_iter ( ) ) ;
1389
+ if summary. is_empty ( ) {
1390
+ return ( None , None ) ;
1391
+ }
1392
+ let mut content = String :: new ( ) ;
1393
+ html:: push_html ( & mut content, p) ;
1394
+
1395
+ if content. is_empty ( ) { ( Some ( summary) , None ) } else { ( Some ( summary) , Some ( content) ) }
1342
1396
}
1343
1397
}
1344
1398
@@ -1882,7 +1936,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
1882
1936
#[ derive( Clone , Default , Debug ) ]
1883
1937
pub struct IdMap {
1884
1938
map : FxHashMap < String , usize > ,
1885
- existing_footnotes : usize ,
1939
+ existing_footnotes : Arc < AtomicUsize > ,
1886
1940
}
1887
1941
1888
1942
fn is_default_id ( id : & str ) -> bool {
@@ -1942,7 +1996,7 @@ fn is_default_id(id: &str) -> bool {
1942
1996
1943
1997
impl IdMap {
1944
1998
pub fn new ( ) -> Self {
1945
- IdMap { map : FxHashMap :: default ( ) , existing_footnotes : 0 }
1999
+ IdMap { map : FxHashMap :: default ( ) , existing_footnotes : Arc :: new ( AtomicUsize :: new ( 0 ) ) }
1946
2000
}
1947
2001
1948
2002
pub ( crate ) fn derive < S : AsRef < str > + ToString > ( & mut self , candidate : S ) -> String {
@@ -1970,15 +2024,17 @@ impl IdMap {
1970
2024
1971
2025
/// Method to handle `existing_footnotes` increment automatically (to prevent forgetting
1972
2026
/// about it).
1973
- pub ( crate ) fn handle_footnotes < F : FnOnce ( & mut Self , & mut usize ) > ( & mut self , closure : F ) {
1974
- let mut existing_footnotes = self . existing_footnotes ;
2027
+ pub ( crate ) fn handle_footnotes < ' a , T , F : FnOnce ( & ' a mut Self , Weak < AtomicUsize > ) -> T > (
2028
+ & ' a mut self ,
2029
+ closure : F ,
2030
+ ) -> T {
2031
+ let existing_footnotes = Arc :: downgrade ( & self . existing_footnotes ) ;
1975
2032
1976
- closure ( self , & mut existing_footnotes) ;
1977
- self . existing_footnotes = existing_footnotes;
2033
+ closure ( self , existing_footnotes)
1978
2034
}
1979
2035
1980
2036
pub ( crate ) fn clear ( & mut self ) {
1981
2037
self . map . clear ( ) ;
1982
- self . existing_footnotes = 0 ;
2038
+ self . existing_footnotes = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
1983
2039
}
1984
2040
}
0 commit comments