@@ -32,6 +32,8 @@ use std::iter::Peekable;
3232use  std:: ops:: { ControlFlow ,  Range } ; 
3333use  std:: path:: PathBuf ; 
3434use  std:: str:: { self ,  CharIndices } ; 
35+ use  std:: sync:: atomic:: AtomicUsize ; 
36+ use  std:: sync:: { Arc ,  Weak } ; 
3537
3638use  pulldown_cmark:: { 
3739    BrokenLink ,  CodeBlockKind ,  CowStr ,  Event ,  LinkType ,  Options ,  Parser ,  Tag ,  TagEnd ,  html, 
@@ -1301,8 +1303,20 @@ impl LangString {
13011303    } 
13021304} 
13031305
1304- impl  Markdown < ' _ >  { 
1306+ impl < ' a >  Markdown < ' a >  { 
13051307    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 > > >  { 
13061320        let  Markdown  { 
13071321            content :  md, 
13081322            links, 
@@ -1313,32 +1327,72 @@ impl Markdown<'_> {
13131327            heading_offset, 
13141328        }  = self ; 
13151329
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 < ' _ > | { 
13211331            links
13221332                . iter ( ) 
13231333                . find ( |link| * link. original_text  == * broken_link. reference ) 
13241334                . map ( |link| ( link. href . as_str ( ) . into ( ) ,  link. tooltip . as_str ( ) . into ( ) ) ) 
13251335        } ; 
13261336
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) ) ; 
13281338        let  p = p. into_offset_iter ( ) ; 
13291339
1330-         let  mut  s = String :: with_capacity ( md. len ( )  *  3  / 2 ) ; 
1331- 
13321340        ids. handle_footnotes ( |ids,  existing_footnotes| { 
13331341            let  p = HeadingLinks :: new ( p,  None ,  ids,  heading_offset) ; 
13341342            let  p = footnotes:: Footnotes :: new ( p,  existing_footnotes) ; 
13351343            let  p = LinkReplacer :: new ( p. map ( |( ev,  _) | ev) ,  links) ; 
13361344            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+     } 
13401348
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) )  } 
13421396    } 
13431397} 
13441398
@@ -1882,7 +1936,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
18821936#[ derive( Clone ,  Default ,  Debug ) ]  
18831937pub  struct  IdMap  { 
18841938    map :  FxHashMap < String ,  usize > , 
1885-     existing_footnotes :  usize , 
1939+     existing_footnotes :  Arc < AtomicUsize > , 
18861940} 
18871941
18881942fn  is_default_id ( id :  & str )  -> bool  { 
@@ -1942,7 +1996,7 @@ fn is_default_id(id: &str) -> bool {
19421996
19431997impl  IdMap  { 
19441998    pub  fn  new ( )  -> Self  { 
1945-         IdMap  {  map :  FxHashMap :: default ( ) ,  existing_footnotes :  0  } 
1999+         IdMap  {  map :  FxHashMap :: default ( ) ,  existing_footnotes :  Arc :: new ( AtomicUsize :: new ( 0 ) )  } 
19462000    } 
19472001
19482002    pub ( crate )  fn  derive < S :  AsRef < str >  + ToString > ( & mut  self ,  candidate :  S )  -> String  { 
@@ -1970,15 +2024,17 @@ impl IdMap {
19702024
19712025    /// Method to handle `existing_footnotes` increment automatically (to prevent forgetting 
19722026     /// 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 ) ; 
19752032
1976-         closure ( self ,  & mut  existing_footnotes) ; 
1977-         self . existing_footnotes  = existing_footnotes; 
2033+         closure ( self ,  existing_footnotes) 
19782034    } 
19792035
19802036    pub ( crate )  fn  clear ( & mut  self )  { 
19812037        self . map . clear ( ) ; 
1982-         self . existing_footnotes  = 0 ; 
2038+         self . existing_footnotes  = Arc :: new ( AtomicUsize :: new ( 0 ) ) ; 
19832039    } 
19842040} 
0 commit comments