@@ -17,7 +17,7 @@ use pulldown_cmark::{
1717} ; 
1818use  regex:: Regex ; 
1919
20- use  crate :: { Bibliography ,  Context ,  Outline ,   ts,  typst} ; 
20+ use  crate :: { Bibliography ,  Context ,  ts,  typst} ; 
2121
2222const  OPTS_MARKDOWN :  OptsMarkdown  = OptsMarkdown :: empty ( ) 
2323    . union ( OptsMarkdown :: ENABLE_MATH ) 
@@ -125,14 +125,81 @@ pub fn parse(
125125
126126    Ok ( Article  { 
127127        text, 
128-         outline :  Outline ( outline) , 
128+         outline :  outline. into ( ) , 
129129        scripts, 
130130        bibliography :  Bibliography ( bibliography) , 
131131    } ) 
132132} 
133133
134134// StreamHeading 
135135
136+ pub  struct  Heading ( String ,  String ,  Vec < Heading > ) ; 
137+ pub  struct  Outline ( Vec < Heading > ) ; 
138+ 
139+ impl  hypertext:: Renderable  for  Outline  { 
140+     fn  render_to ( & self ,  output :  & mut  String )  { 
141+         use  hypertext:: prelude:: * ; 
142+ 
143+         fn  render_heading_list ( headings :  & [ Heading ] )  -> impl  Renderable  { 
144+             maud ! ( 
145+                 ul { 
146+                     @for  Heading ( title,  id,  children)  in headings { 
147+                         li { 
148+                             a href=( format!( "#{id}" ) )  {  ( title)  } 
149+ 
150+                             @if  !children. is_empty( )  { 
151+                                 ( render_heading_list( children) ) 
152+                             } 
153+                         } 
154+                     } 
155+                 } 
156+             ) 
157+         } 
158+ 
159+         maud ! ( 
160+             aside . outline { 
161+                 section { 
162+                     h2 { 
163+                         a href="#top"  {  "Outline"  } 
164+                     } 
165+                     nav #table-of-contents { 
166+                         ( render_heading_list( & self . 0 ) ) 
167+                     } 
168+                 } 
169+             } 
170+         ) 
171+         . render_to ( output) ; 
172+     } 
173+ } 
174+ 
175+ impl  From < Vec < ( String ,  String ,  usize ) > >  for  Outline  { 
176+     fn  from ( flat_vec :  Vec < ( String ,  String ,  usize ) > )  -> Self  { 
177+         let  mut  res = Vec :: new ( ) ; 
178+ 
179+         for  ( title,  url,  level)  in  flat_vec { 
180+             let  mut  ptr = & mut  res; 
181+             let  new = Heading ( title,  url,  vec ! [ ] ) ; 
182+ 
183+             for  _ in  2 ..level { 
184+                 if  ptr. is_empty ( )  { 
185+                     break ; 
186+                 } 
187+ 
188+                 match  ptr. last_mut ( )  { 
189+                     Some ( Heading ( _,  _,  children) )  => { 
190+                         ptr = children; 
191+                     } 
192+                     None  => unreachable ! ( ) , 
193+                 } 
194+             } 
195+ 
196+             ptr. push ( new) ; 
197+         } 
198+ 
199+         Outline ( res) 
200+     } 
201+ } 
202+ 
136203struct  StreamHeading < ' a ,  I > 
137204where 
138205    I :  Iterator < Item  = Event < ' a > > , 
@@ -142,22 +209,20 @@ where
142209    buffer :  String , 
143210    handle :  Option < Tag < ' a > > , 
144211    events :  VecDeque < Event < ' a > > , 
145-     finish :  bool , 
146-     out :  & ' a  mut  Vec < ( String ,  String ) > , 
212+     out :  & ' a  mut  Vec < ( String ,  String ,  usize ) > , 
147213} 
148214
149215impl < ' a ,  I >  StreamHeading < ' a ,  I > 
150216where 
151217    I :  Iterator < Item  = Event < ' a > > , 
152218{ 
153-     pub  fn  new ( iter :  I ,  out :  & ' a  mut  Vec < ( String ,  String ) > )  -> Self  { 
219+     pub  fn  new ( iter :  I ,  out :  & ' a  mut  Vec < ( String ,  String ,   usize ) > )  -> Self  { 
154220        Self  { 
155221            iter, 
156222            counts :  HashMap :: new ( ) , 
157223            buffer :  String :: new ( ) , 
158224            handle :  None , 
159225            events :  VecDeque :: new ( ) , 
160-             finish :  false , 
161226            out, 
162227        } 
163228    } 
@@ -170,9 +235,8 @@ where
170235    type  Item  = Event < ' a > ; 
171236
172237    fn  next ( & mut  self )  -> Option < Self :: Item >  { 
173-         match  self . finish  && !self . events . is_empty ( )  { 
174-             true  => return  self . events . pop_front ( ) , 
175-             false  => self . finish  = false , 
238+         if  self . handle . is_none ( )  && !self . events . is_empty ( )  { 
239+             return  self . events . pop_front ( ) ; 
176240        } 
177241
178242        for  event in  self . iter . by_ref ( )  { 
@@ -181,37 +245,41 @@ where
181245                    debug_assert ! ( self . handle. is_none( ) ) ; 
182246                    self . handle  = Some ( tag) ; 
183247                } 
184-                 Event :: Text ( text)  if  self . handle . is_some ( )  => { 
185-                     self . buffer . push_str ( & text) ; 
186-                     self . events . push_back ( Event :: Text ( text) ) ; 
187-                 } 
188248                event @ Event :: End ( TagEnd :: Heading ( ..) )  => { 
189249                    debug_assert ! ( self . handle. is_some( ) ) ; 
190250                    self . events . push_back ( event) ; 
191251
192-                     let  txt  = std:: mem:: take ( & mut  self . buffer ) ; 
193-                     let  mut  url  = txt . to_lowercase ( ) . replace ( ' ' ,  "-" ) ; 
252+                     let  text  = std:: mem:: take ( & mut  self . buffer ) ; 
253+                     let  mut  slug  = text . to_lowercase ( ) . replace ( ' ' ,  "-" ) ; 
194254
195-                     match  self . counts . get_mut ( & url )  { 
255+                     match  self . counts . get_mut ( & slug )  { 
196256                        Some ( count)  => { 
197257                            * count += 1 ; 
198-                             url  = format ! ( "{url }-{count}" ) ; 
258+                             slug  = format ! ( "{slug }-{count}" ) ; 
199259                        } 
200260                        None  => { 
201-                             self . counts . insert ( url . clone ( ) ,  0 ) ; 
261+                             self . counts . insert ( slug . clone ( ) ,  0 ) ; 
202262                        } 
203263                    } 
204264
205265                    let  mut  handle = self . handle . take ( ) . unwrap ( ) ; 
206-                     match  handle { 
207-                         Tag :: Heading  {  ref  mut  id,  .. }  => * id = Some ( url. clone ( ) . into ( ) ) , 
266+                     let  level = match  & mut  handle { 
267+                         Tag :: Heading  {  id,  level,  .. }  => { 
268+                             * id = Some ( slug. clone ( ) . into ( ) ) ; 
269+                             * level as  usize 
270+                         } 
208271                        _ => unreachable ! ( ) , 
209-                     } 
272+                     } ; 
210273
211-                     self . out . push ( ( txt,  url. clone ( ) ) ) ; 
212-                     self . finish  = true ; 
274+                     self . out . push ( ( text,  slug. clone ( ) ,  level) ) ; 
213275                    return  Some ( Event :: Start ( handle) ) ; 
214276                } 
277+                 event if  self . handle . is_some ( )  => { 
278+                     if  let  Event :: Text ( text)  = & event { 
279+                         self . buffer . push_str ( text) ; 
280+                     } 
281+                     self . events . push_back ( event) ; 
282+                 } 
215283                _ => return  Some ( event) , 
216284            } 
217285        } 
0 commit comments