@@ -3,10 +3,7 @@ use crate::{templates::docs::TocLink, utils::config};
33use std:: cell:: RefCell ;
44use std:: collections:: { HashMap , HashSet } ;
55use std:: path:: { Path , PathBuf } ;
6- use std:: sync:: {
7- atomic:: { AtomicUsize , Ordering } ,
8- Arc ,
9- } ;
6+ use std:: sync:: Arc ;
107
118use anyhow:: Result ;
129use comrak:: {
@@ -15,25 +12,27 @@ use comrak::{
1512 nodes:: { Ast , AstNode , NodeValue } ,
1613 parse_document, Arena , ComrakExtensionOptions , ComrakOptions , ComrakRenderOptions ,
1714} ;
15+ use convert_case;
1816use itertools:: Itertools ;
1917use regex:: Regex ;
2018use tantivy:: collector:: TopDocs ;
2119use tantivy:: query:: { QueryParser , RegexQuery } ;
2220use tantivy:: schema:: * ;
2321use tantivy:: tokenizer:: { LowerCaser , NgramTokenizer , TextAnalyzer } ;
2422use tantivy:: { Index , IndexReader , SnippetGenerator } ;
25- use url:: Url ;
23+
24+ use std:: sync:: Mutex ;
2625
2726use std:: fmt;
2827
2928pub struct MarkdownHeadings {
30- counter : Arc < AtomicUsize > ,
29+ header_map : Arc < Mutex < HashMap < String , usize > > > ,
3130}
3231
3332impl Default for MarkdownHeadings {
3433fn default ( ) ->Self {
3534Self {
36- counter : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
35+ header_map : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
3736}
3837}
3938}
@@ -44,31 +43,42 @@ impl MarkdownHeadings {
4443}
4544}
4645
46+ /// Sets the document headers
47+ ///
48+ /// uses toclink to ensure header id matches what the TOC expects
49+ ///
4750impl HeadingAdapter for MarkdownHeadings {
4851fn enter ( & self , meta : & HeadingMeta ) ->String {
49- // let id = meta.content.to_case(convert_case::Case::Kebab);
50- let id =self . counter . fetch_add ( 1 , Ordering :: SeqCst ) ;
51- let id =format ! ( "header-{}" , id) ;
52+ let conv = convert_case:: Converter :: new ( ) . to_case ( convert_case:: Case :: Kebab ) ;
53+ let id = conv. convert ( meta. content . to_string ( ) ) ;
54+
55+ let index =match self . header_map . lock ( ) . unwrap ( ) . get ( & id) {
56+ Some ( value) => value +1 ,
57+ _ =>0 ,
58+ } ;
59+ self . header_map . lock ( ) . unwrap ( ) . insert ( id. clone ( ) , index) ;
60+
61+ let id =TocLink :: new ( & id, index) . id ;
5262
5363match meta. level {
54- 1 =>format ! ( r#"<h1>" # ) ,
55- 2 =>format ! ( r#"<h2>" # ) ,
56- 3 =>format ! ( r#"<h3>" # ) ,
57- 4 =>format ! ( r#"<h4>" # ) ,
58- 5 =>format ! ( r#"<h5>" # ) ,
59- 6 =>format ! ( r#"<h6>" # ) ,
64+ 1 =>format ! ( r## "<h1><a href="#{id}">"# # ) ,
65+ 2 =>format ! ( r## "<h2><a href="#{id}">"# # ) ,
66+ 3 =>format ! ( r## "<h3><a href="#{id}">"# # ) ,
67+ 4 =>format ! ( r## "<h4><a href="#{id}">"# # ) ,
68+ 5 =>format ! ( r## "<h5><a href="#{id}">"# # ) ,
69+ 6 =>format ! ( r## "<h6><a href="#{id}">"# # ) ,
6070 _ =>unreachable ! ( ) ,
6171}
6272}
6373
6474fn exit ( & self , meta : & HeadingMeta ) ->String {
6575match meta. level {
66- 1 =>r#"</h1>"# ,
67- 2 =>r#"</h2>"# ,
68- 3 =>r#"</h3>"# ,
69- 4 =>r#"</h4>"# ,
70- 5 =>r#"</h5>"# ,
71- 6 =>r#"</h6>"# ,
76+ 1 =>r#"</a></ h1>"# ,
77+ 2 =>r#"</a></ h2>"# ,
78+ 3 =>r#"</a></ h3>"# ,
79+ 4 =>r#"</a></ h4>"# ,
80+ 5 =>r#"</a></ h5>"# ,
81+ 6 =>r#"</a></ h6>"# ,
7282 _ =>unreachable ! ( ) ,
7383}
7484. into ( )
@@ -335,38 +345,6 @@ where
335345Ok ( ( ) )
336346}
337347
338- pub fn nest_relative_links ( node : & mut markdown:: mdast:: Node , path : & PathBuf ) {
339- let _ =iter_mut_all ( node, & mut |node|{
340- if let markdown:: mdast:: Node :: Link ( ref mut link) = node{
341- match Url :: parse ( & link. url ) {
342- Ok ( url) =>{
343- if !url. has_host ( ) {
344- let mut url_path = url. path ( ) . to_string ( ) ;
345- let url_path_path =Path :: new ( & url_path) ;
346- match url_path_path. extension ( ) {
347- Some ( ext) =>{
348- if ext. to_str ( ) ==Some ( ".md" ) {
349- let base = url_path_path. with_extension ( "" ) ;
350- url_path = base. into_os_string ( ) . into_string ( ) . unwrap ( ) ;
351- }
352- }
353- _ =>{
354- warn ! ( "not markdown path: {:?}" , path)
355- }
356- }
357- link. url = path. join ( url_path) . into_os_string ( ) . into_string ( ) . unwrap ( ) ;
358- }
359- }
360- Err ( e) =>{
361- warn ! ( "could not parse url in markdown: {}" , e)
362- }
363- }
364- }
365-
366- Ok ( ( ) )
367- } ) ;
368- }
369-
370348/// Get the title of the article.
371349///
372350/// # Arguments
@@ -462,11 +440,10 @@ pub fn wrap_tables<'a>(root: &'a AstNode<'a>, arena: &'a Arena<AstNode<'a>>) ->
462440///
463441pub fn get_toc < ' a > ( root : & ' a AstNode < ' a > ) -> anyhow:: Result < Vec < TocLink > > {
464442let mut links =Vec :: new ( ) ;
465- let mut header_counter = 0 ;
443+ let mut header_count : HashMap < String , usize > = HashMap :: new ( ) ;
466444
467445iter_nodes ( root, & mut |node|{
468446if let NodeValue :: Heading ( header) =& node. data . borrow ( ) . value {
469- header_counter +=1 ;
470447if header. level !=1 {
471448let sibling =match node. first_child ( ) {
472449Some ( child) => child,
@@ -476,7 +453,14 @@ pub fn get_toc<'a>(root: &'a AstNode<'a>) -> anyhow::Result<Vec<TocLink>> {
476453}
477454} ;
478455if let NodeValue :: Text ( text) =& sibling. data . borrow ( ) . value {
479- links. push ( TocLink :: new ( text, header_counter -1 ) . level ( header. level ) ) ;
456+ let index =match header_count. get ( text) {
457+ Some ( index) => index +1 ,
458+ _ =>0 ,
459+ } ;
460+
461+ header_count. insert ( text. clone ( ) , index) ;
462+
463+ links. push ( TocLink :: new ( text, index) . level ( header. level ) ) ;
480464return Ok ( false ) ;
481465}
482466}
@@ -753,11 +737,25 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena<AstNode<'a>>) -> anyho
753737let path =Path :: new ( link. url . as_str ( ) ) ;
754738
755739if path. is_relative ( ) {
740+ let fragment =match link. url . find ( "#" ) {
741+ Some ( index) => link. url [ index +1 ..link. url . len ( ) ] . to_string ( ) ,
742+ _ =>"" . to_string ( ) ,
743+ } ;
744+
745+ for _in 0 ..fragment. len ( ) +1 {
746+ link. url . pop ( ) ;
747+ }
748+
756749if link. url . ends_with ( ".md" ) {
757750for _in 0 ..".md" . len ( ) {
758751 link. url . pop ( ) ;
759752}
760753}
754+
755+ let header_id =TocLink :: from_fragment ( fragment) . id ;
756+ for cin header_id. chars ( ) {
757+ link. url . push ( c)
758+ }
761759}
762760
763761Ok ( true )