@@ -36,12 +36,16 @@ use std::str;
36
36
use std:: vec;
37
37
use collections:: HashMap ;
38
38
39
+ use html:: toc:: TocBuilder ;
39
40
use html:: highlight;
40
41
41
42
/// A unit struct which has the `fmt::Show` trait implemented. When
42
43
/// formatted, this struct will emit the HTML corresponding to the rendered
43
44
/// version of the contained markdown string.
44
45
pub struct Markdown < ' a > ( & ' a str ) ;
46
+ /// A unit struct like `Markdown`, that renders the markdown with a
47
+ /// table of contents.
48
+ pub struct MarkdownWithToc < ' a > ( & ' a str ) ;
45
49
46
50
static OUTPUT_UNIT : libc:: size_t = 64 ;
47
51
static MKDEXT_NO_INTRA_EMPHASIS : libc:: c_uint = 1 << 0 ;
@@ -75,6 +79,7 @@ struct html_renderopt {
75
79
struct my_opaque {
76
80
opt : html_renderopt ,
77
81
dfltblk : extern "C" fn ( * buf , * buf , * buf , * libc:: c_void ) ,
82
+ toc_builder : Option < TocBuilder > ,
78
83
}
79
84
80
85
struct buf {
@@ -121,7 +126,7 @@ fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> {
121
126
122
127
local_data_key ! ( used_header_map: HashMap <~str , uint>)
123
128
124
- pub fn render ( w : & mut io:: Writer , s : & str ) -> fmt:: Result {
129
+ pub fn render ( w : & mut io:: Writer , s : & str , print_toc : bool ) -> fmt:: Result {
125
130
extern fn block ( ob : * buf , text : * buf , lang : * buf , opaque : * libc:: c_void ) {
126
131
unsafe {
127
132
let my_opaque: & my_opaque = cast:: transmute ( opaque) ;
@@ -162,7 +167,7 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result {
162
167
}
163
168
164
169
extern fn header ( ob : * buf , text : * buf , level : libc:: c_int ,
165
- _opaque : * libc:: c_void ) {
170
+ opaque : * libc:: c_void ) {
166
171
// sundown does this, we may as well too
167
172
"\n " . with_c_str ( |p| unsafe { bufputs ( ob, p) } ) ;
168
173
@@ -183,6 +188,8 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result {
183
188
}
184
189
} ) . to_owned_vec ( ) . connect ( "-" ) ;
185
190
191
+ let opaque = unsafe { & mut * ( opaque as * mut my_opaque ) } ;
192
+
186
193
// Make sure our hyphenated ID is unique for this page
187
194
let id = local_data:: get_mut ( used_header_map, |map| {
188
195
let map = map. unwrap ( ) ;
@@ -194,9 +201,18 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result {
194
201
id. clone ( )
195
202
} ) ;
196
203
204
+ let sec = match opaque. toc_builder {
205
+ Some ( ref mut builder) => {
206
+ builder. push ( level as u32 , s. clone ( ) , id. clone ( ) )
207
+ }
208
+ None => { "" }
209
+ } ;
210
+
197
211
// Render the HTML
198
- let text = format ! ( r#"<h{lvl} id="{id}">{}</h{lvl}>"# ,
199
- s, lvl = level, id = id) ;
212
+ let text = format ! ( r#"<h{lvl} id="{id}">{sec_len,plural,=0{}other{{sec} }}{}</h{lvl}>"# ,
213
+ s, lvl = level, id = id,
214
+ sec_len = sec. len( ) , sec = sec) ;
215
+
200
216
text. with_c_str ( |p| unsafe { bufputs ( ob, p) } ) ;
201
217
}
202
218
@@ -218,23 +234,30 @@ pub fn render(w: &mut io::Writer, s: &str) -> fmt::Result {
218
234
let mut callbacks: sd_callbacks = mem:: init ( ) ;
219
235
220
236
sdhtml_renderer ( & callbacks, & options, 0 ) ;
221
- let opaque = my_opaque {
237
+ let mut opaque = my_opaque {
222
238
opt : options,
223
239
dfltblk : callbacks. blockcode . unwrap ( ) ,
240
+ toc_builder : if print_toc { Some ( TocBuilder :: new ( ) ) } else { None }
224
241
} ;
225
242
callbacks. blockcode = Some ( block) ;
226
243
callbacks. header = Some ( header) ;
227
244
let markdown = sd_markdown_new ( extensions, 16 , & callbacks,
228
- & opaque as * my_opaque as * libc:: c_void ) ;
245
+ & mut opaque as * mut my_opaque as * libc:: c_void ) ;
229
246
230
247
231
248
sd_markdown_render ( ob, s. as_ptr ( ) , s. len ( ) as libc:: size_t , markdown) ;
232
249
sd_markdown_free ( markdown) ;
233
250
234
- let ret = vec:: raw:: buf_as_slice ( ( * ob) . data , ( * ob) . size as uint , |buf| {
235
- w. write ( buf)
236
- } ) ;
251
+ let mut ret = match opaque. toc_builder {
252
+ Some ( b) => write ! ( w, "<nav id=\" TOC\" >{}</nav>" , b. into_toc( ) ) ,
253
+ None => Ok ( ( ) )
254
+ } ;
237
255
256
+ if ret. is_ok ( ) {
257
+ ret = vec:: raw:: buf_as_slice ( ( * ob) . data , ( * ob) . size as uint , |buf| {
258
+ w. write ( buf)
259
+ } ) ;
260
+ }
238
261
bufrelease ( ob) ;
239
262
ret
240
263
}
@@ -319,6 +342,13 @@ impl<'a> fmt::Show for Markdown<'a> {
319
342
let Markdown ( md) = * self ;
320
343
// This is actually common enough to special-case
321
344
if md. len ( ) == 0 { return Ok ( ( ) ) }
322
- render ( fmt. buf , md. as_slice ( ) )
345
+ render ( fmt. buf , md. as_slice ( ) , false )
346
+ }
347
+ }
348
+
349
+ impl < ' a > fmt:: Show for MarkdownWithToc < ' a > {
350
+ fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
351
+ let MarkdownWithToc ( md) = * self ;
352
+ render ( fmt. buf , md. as_slice ( ) , true )
323
353
}
324
354
}
0 commit comments