@@ -47,9 +47,12 @@ use test;
47
47
/// formatted, this struct will emit the HTML corresponding to the rendered
48
48
/// version of the contained markdown string.
49
49
pub struct Markdown < ' a > ( pub & ' a str ) ;
50
- /// A unit struct like `Markdown`, that renders the markdown with a
50
+ /// A unit struct like `Markdown`, that can expand '::/...' links
51
+ /// using the root prefix provided in the second string.
52
+ pub struct MarkdownRelative < ' a > ( pub & ' a str , pub & ' a str ) ;
53
+ /// A unit struct like `MarkdownRelative`, that renders the markdown with a
51
54
/// table of contents.
52
- pub struct MarkdownWithToc < ' a > ( pub & ' a str ) ;
55
+ pub struct MarkdownWithToc < ' a > ( pub & ' a str , pub & ' a str ) ;
53
56
54
57
const DEF_OUNIT : libc:: size_t = 64 ;
55
58
const HOEDOWN_EXT_NO_INTRA_EMPHASIS : libc:: c_uint = 1 << 11 ;
@@ -143,9 +146,13 @@ struct html_toc_data {
143
146
nesting_level : libc:: c_int ,
144
147
}
145
148
146
- struct MyOpaque {
149
+ struct MyOpaque < ' a > {
147
150
dfltblk : extern "C" fn ( * mut hoedown_buffer , * const hoedown_buffer ,
148
151
* const hoedown_buffer , * const hoedown_renderer_data ) ,
152
+ dfltlink : extern "C" fn ( * mut hoedown_buffer , * const hoedown_buffer ,
153
+ * const hoedown_buffer , * const hoedown_buffer ,
154
+ * const hoedown_renderer_data ) -> libc:: c_int ,
155
+ root_path : & ' a str ,
149
156
toc_builder : Option < TocBuilder > ,
150
157
}
151
158
@@ -219,7 +226,7 @@ thread_local!(pub static PLAYGROUND_KRATE: RefCell<Option<Option<String>>> = {
219
226
RefCell :: new( None )
220
227
} ) ;
221
228
222
- pub fn render ( w : & mut fmt:: Formatter , s : & str , print_toc : bool ) -> fmt:: Result {
229
+ pub fn render ( w : & mut fmt:: Formatter , s : & str , print_toc : bool , root_path : & str ) -> fmt:: Result {
223
230
extern fn block ( ob : * mut hoedown_buffer , orig_text : * const hoedown_buffer ,
224
231
lang : * const hoedown_buffer , data : * const hoedown_renderer_data ) {
225
232
unsafe {
@@ -349,18 +356,58 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
349
356
1
350
357
}
351
358
359
+ // Intercept the default link logic to process mod-relative links starting with '::/'
360
+ extern fn link (
361
+ ob : * mut hoedown_buffer ,
362
+ content : * const hoedown_buffer ,
363
+ link : * const hoedown_buffer ,
364
+ title : * const hoedown_buffer ,
365
+ data : * const hoedown_renderer_data ,
366
+ ) -> libc:: c_int {
367
+
368
+ let my_opaque : & MyOpaque = unsafe {
369
+ let opaque = ( * data) . opaque as * mut hoedown_html_renderer_state ;
370
+ & * ( ( * opaque) . opaque as * const MyOpaque )
371
+ } ;
372
+
373
+ if link. is_null ( ) {
374
+ return ( my_opaque. dfltlink ) ( ob, content, link, title, data) ;
375
+ }
376
+
377
+ let bytes = unsafe { ( * link) . as_bytes ( ) } ;
378
+ let link_str = str:: from_utf8 ( bytes) . unwrap ( ) . to_owned ( ) ;
379
+
380
+ if !link_str. starts_with ( "::/" ) {
381
+ return ( my_opaque. dfltlink ) ( ob, content, link, title, data) ;
382
+ }
383
+
384
+ let new_link = my_opaque. root_path . to_owned ( ) + & link_str[ 3 ..] ;
385
+ let new_link = CString :: new ( new_link) . unwrap ( ) ;
386
+
387
+ unsafe {
388
+ let link_buffer = hoedown_buffer_new ( DEF_OUNIT ) ;
389
+ hoedown_buffer_puts ( link_buffer, new_link. as_ptr ( ) ) ;
390
+ let result = ( my_opaque. dfltlink ) ( ob, content, link_buffer, title, data) ;
391
+ hoedown_buffer_free ( link_buffer) ;
392
+ result
393
+ }
394
+ }
395
+
352
396
unsafe {
353
397
let ob = hoedown_buffer_new ( DEF_OUNIT ) ;
354
398
let renderer = hoedown_html_renderer_new ( 0 , 0 ) ;
355
399
let mut opaque = MyOpaque {
356
400
dfltblk : ( * renderer) . blockcode . unwrap ( ) ,
401
+ dfltlink : ( * renderer) . link . unwrap ( ) ,
402
+ root_path : root_path,
357
403
toc_builder : if print_toc { Some ( TocBuilder :: new ( ) ) } else { None }
358
404
} ;
359
405
( * ( ( * renderer) . opaque as * mut hoedown_html_renderer_state ) ) . opaque
360
406
= & mut opaque as * mut _ as * mut libc:: c_void ;
361
407
( * renderer) . blockcode = Some ( block) ;
362
408
( * renderer) . header = Some ( header) ;
363
409
( * renderer) . codespan = Some ( codespan) ;
410
+ ( * renderer) . link = Some ( link) ;
364
411
365
412
let document = hoedown_document_new ( renderer, HOEDOWN_EXTENSIONS , 16 ) ;
366
413
hoedown_document_render ( document, ob, s. as_ptr ( ) ,
@@ -524,14 +571,23 @@ impl<'a> fmt::Display for Markdown<'a> {
524
571
let Markdown ( md) = * self ;
525
572
// This is actually common enough to special-case
526
573
if md. is_empty ( ) { return Ok ( ( ) ) }
527
- render ( fmt, md, false )
574
+ render ( fmt, md, false , "" )
575
+ }
576
+ }
577
+
578
+ impl < ' a > fmt:: Display for MarkdownRelative < ' a > {
579
+ fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
580
+ let MarkdownRelative ( md, root_path) = * self ;
581
+ // This is actually common enough to special-case
582
+ if md. is_empty ( ) { return Ok ( ( ) ) }
583
+ render ( fmt, md, false , root_path)
528
584
}
529
585
}
530
586
531
587
impl < ' a > fmt:: Display for MarkdownWithToc < ' a > {
532
588
fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
533
- let MarkdownWithToc ( md) = * self ;
534
- render ( fmt, md, true )
589
+ let MarkdownWithToc ( md, root_path ) = * self ;
590
+ render ( fmt, md, true , root_path )
535
591
}
536
592
}
537
593
@@ -690,4 +746,18 @@ mod tests {
690
746
t ( "# top header" , "top header" ) ;
691
747
t ( "## header" , "header" ) ;
692
748
}
749
+
750
+ #[ test]
751
+ fn test_root_relative_links ( ) {
752
+ fn t ( input : & str , root : & str , expect : & str ) {
753
+ let output = format ! ( "{}" , MarkdownRelative ( input, root) ) ;
754
+ assert_eq ! ( output, format!( "<p><a href=\" {}\" >Link</a></p>\n " , expect) ) ;
755
+ }
756
+
757
+ t ( "[Link](::/path/file.html)" , "" , "path/file.html" ) ;
758
+ t ( "[Link](::/path/file.html)" , "../" , "../path/file.html" ) ;
759
+ t ( "[Link](::/path/file.html)" , "../../" , "../../path/file.html" ) ;
760
+ t ( "[Link]\n [Link]: ::/path/file.html" , "../" , "../path/file.html" ) ;
761
+ }
762
+
693
763
}
0 commit comments