@@ -10,18 +10,30 @@ use super::SpannedEvent;
10
10
/// references.
11
11
pub ( super ) struct Footnotes < ' a , I > {
12
12
inner : I ,
13
- footnotes : FxIndexMap < String , ( Vec < Event < ' a > > , u16 ) > ,
13
+ footnotes : FxIndexMap < String , FootnoteDef < ' a > > ,
14
+ }
15
+
16
+ /// The definition of a single footnote.
17
+ struct FootnoteDef < ' a > {
18
+ content : Vec < Event < ' a > > ,
19
+ /// The number that appears in the footnote reference and list.
20
+ id : u16 ,
14
21
}
15
22
16
23
impl < ' a , I > Footnotes < ' a , I > {
17
24
pub ( super ) fn new ( iter : I ) -> Self {
18
25
Footnotes { inner : iter, footnotes : FxIndexMap :: default ( ) }
19
26
}
20
27
21
- fn get_entry ( & mut self , key : & str ) -> & mut ( Vec < Event < ' a > > , u16 ) {
28
+ fn get_entry ( & mut self , key : & str ) -> ( & mut Vec < Event < ' a > > , u16 ) {
22
29
let new_id = self . footnotes . len ( ) + 1 ;
23
30
let key = key. to_owned ( ) ;
24
- self . footnotes . entry ( key) . or_insert ( ( Vec :: new ( ) , new_id as u16 ) )
31
+ let FootnoteDef { content, id } = self
32
+ . footnotes
33
+ . entry ( key)
34
+ . or_insert ( FootnoteDef { content : Vec :: new ( ) , id : new_id as u16 } ) ;
35
+ // Don't allow changing the ID of existing entrys, but allow changing the contents.
36
+ ( content, * id)
25
37
}
26
38
}
27
39
@@ -32,46 +44,28 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
32
44
loop {
33
45
match self . inner . next ( ) {
34
46
Some ( ( Event :: FootnoteReference ( ref reference) , range) ) => {
35
- let entry = self . get_entry ( reference) ;
36
- let reference = format ! (
37
- "<sup id= \" fnref{0} \" ><a href= \" #fn{0} \" >{0}</a></sup>" ,
38
- ( * entry ) . 1
39
- ) ;
47
+ // When we see a reference (to a footnote we may not know) the definition of,
48
+ // reserve a number for it, and emit a link to that number.
49
+ let ( _ , id ) = self . get_entry ( reference ) ;
50
+ let reference =
51
+ format ! ( "<sup id= \" fnref{0} \" ><a href= \" #fn{0} \" >{0}</a></sup>" , id ) ;
40
52
return Some ( ( Event :: Html ( reference. into ( ) ) , range) ) ;
41
53
}
42
54
Some ( ( Event :: Start ( Tag :: FootnoteDefinition ( def) ) , _) ) => {
43
- let mut content = Vec :: new ( ) ;
44
- for ( event, _) in & mut self . inner {
45
- if let Event :: End ( TagEnd :: FootnoteDefinition ) = event {
46
- break ;
47
- }
48
- content. push ( event) ;
49
- }
50
- let entry = self . get_entry ( & def) ;
51
- ( * entry) . 0 = content;
55
+ // When we see a footnote definition, collect the assocated content, and store
56
+ // that for rendering later.
57
+ let content = collect_footnote_def ( & mut self . inner ) ;
58
+ let ( entry_content, _) = self . get_entry ( & def) ;
59
+ * entry_content = content;
52
60
}
53
61
Some ( e) => return Some ( e) ,
54
62
None => {
55
63
if !self . footnotes . is_empty ( ) {
56
- let mut v: Vec < _ > = self . footnotes . drain ( ..) . map ( |( _, x) | x) . collect ( ) ;
57
- v. sort_by ( |a, b| a. 1 . cmp ( & b. 1 ) ) ;
58
- let mut ret = String :: from ( "<div class=\" footnotes\" ><hr><ol>" ) ;
59
- for ( mut content, id) in v {
60
- write ! ( ret, "<li id=\" fn{id}\" >" ) . unwrap ( ) ;
61
- let mut is_paragraph = false ;
62
- if let Some ( & Event :: End ( TagEnd :: Paragraph ) ) = content. last ( ) {
63
- content. pop ( ) ;
64
- is_paragraph = true ;
65
- }
66
- html:: push_html ( & mut ret, content. into_iter ( ) ) ;
67
- write ! ( ret, " <a href=\" #fnref{id}\" >↩</a>" ) . unwrap ( ) ;
68
- if is_paragraph {
69
- ret. push_str ( "</p>" ) ;
70
- }
71
- ret. push_str ( "</li>" ) ;
72
- }
73
- ret. push_str ( "</ol></div>" ) ;
74
- return Some ( ( Event :: Html ( ret. into ( ) ) , 0 ..0 ) ) ;
64
+ // After all the markdown is emmited, emit an <hr> then all the footnotes
65
+ // in a list.
66
+ let defs: Vec < _ > = self . footnotes . drain ( ..) . map ( |( _, x) | x) . collect ( ) ;
67
+ let defs_html = render_footnotes_defs ( defs) ;
68
+ return Some ( ( Event :: Html ( defs_html. into ( ) ) , 0 ..0 ) ) ;
75
69
} else {
76
70
return None ;
77
71
}
@@ -80,3 +74,40 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
80
74
}
81
75
}
82
76
}
77
+
78
+ fn collect_footnote_def < ' a > ( events : impl Iterator < Item = SpannedEvent < ' a > > ) -> Vec < Event < ' a > > {
79
+ let mut content = Vec :: new ( ) ;
80
+ for ( event, _) in events {
81
+ if let Event :: End ( TagEnd :: FootnoteDefinition ) = event {
82
+ break ;
83
+ }
84
+ content. push ( event) ;
85
+ }
86
+ content
87
+ }
88
+
89
+ fn render_footnotes_defs ( mut footnotes : Vec < FootnoteDef < ' _ > > ) -> String {
90
+ let mut ret = String :: from ( "<div class=\" footnotes\" ><hr><ol>" ) ;
91
+
92
+ // Footnotes must listed in order of id, so the numbers the
93
+ // browser generated for <li> are right.
94
+ footnotes. sort_by_key ( |x| x. id ) ;
95
+
96
+ for FootnoteDef { mut content, id } in footnotes {
97
+ write ! ( ret, "<li id=\" fn{id}\" >" ) . unwrap ( ) ;
98
+ let mut is_paragraph = false ;
99
+ if let Some ( & Event :: End ( TagEnd :: Paragraph ) ) = content. last ( ) {
100
+ content. pop ( ) ;
101
+ is_paragraph = true ;
102
+ }
103
+ html:: push_html ( & mut ret, content. into_iter ( ) ) ;
104
+ write ! ( ret, " <a href=\" #fnref{id}\" >↩</a>" ) . unwrap ( ) ;
105
+ if is_paragraph {
106
+ ret. push_str ( "</p>" ) ;
107
+ }
108
+ ret. push_str ( "</li>" ) ;
109
+ }
110
+ ret. push_str ( "</ol></div>" ) ;
111
+
112
+ ret
113
+ }
0 commit comments