greenmat 3.2.2.2 → 3.5.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +21 -8
- data/CHANGELOG.md +24 -0
- data/COPYING +17 -11
- data/Gemfile +2 -2
- data/README.md +19 -13
- data/Rakefile +1 -0
- data/bin/greenmat +4 -40
- data/ext/greenmat/autolink.c +24 -12
- data/ext/greenmat/buffer.c +24 -17
- data/ext/greenmat/buffer.h +18 -13
- data/ext/greenmat/gm_markdown.c +41 -14
- data/ext/greenmat/gm_render.c +68 -21
- data/ext/greenmat/greenmat.h +22 -0
- data/ext/greenmat/houdini.h +22 -0
- data/ext/greenmat/houdini_href_e.c +27 -11
- data/ext/greenmat/houdini_html_e.c +22 -1
- data/ext/greenmat/html.c +177 -47
- data/ext/greenmat/html.h +19 -14
- data/ext/greenmat/html_smartypants.c +47 -20
- data/ext/greenmat/markdown.c +181 -30
- data/ext/greenmat/markdown.h +20 -16
- data/ext/greenmat/stack.c +22 -0
- data/ext/greenmat/stack.h +22 -0
- data/greenmat.gemspec +4 -3
- data/lib/greenmat.rb +1 -1
- data/lib/greenmat/cli.rb +86 -0
- data/lib/greenmat/compat.rb +0 -5
- data/lib/greenmat/render_strip.rb +13 -1
- data/lib/greenmat/version.rb +1 -1
- data/spec/greenmat/markdown_spec.rb +166 -0
- data/test/custom_render_test.rb +41 -2
- data/test/greenmat_bin_test.rb +80 -0
- data/test/greenmat_compat_test.rb +6 -6
- data/test/html5_test.rb +60 -38
- data/test/html_render_test.rb +162 -128
- data/test/html_toc_render_test.rb +74 -11
- data/test/markdown_test.rb +258 -182
- data/test/safe_render_test.rb +5 -6
- data/test/smarty_html_test.rb +19 -13
- data/test/smarty_pants_test.rb +10 -0
- data/test/stripdown_render_test.rb +38 -9
- data/test/test_helper.rb +30 -9
- metadata +29 -13
data/ext/greenmat/gm_render.c
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
/*
|
2
|
-
* Copyright (c)
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
3
|
*
|
4
|
-
* Permission
|
5
|
-
*
|
6
|
-
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
7
10
|
*
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
14
|
-
* OR
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
15
21
|
*/
|
16
22
|
|
17
23
|
#include "greenmat.h"
|
@@ -34,10 +40,10 @@
|
|
34
40
|
}
|
35
41
|
|
36
42
|
extern VALUE rb_mGreenmat;
|
43
|
+
extern VALUE rb_cRenderHTML_TOC;
|
37
44
|
VALUE rb_mRender;
|
38
45
|
VALUE rb_cRenderBase;
|
39
46
|
VALUE rb_cRenderHTML;
|
40
|
-
VALUE rb_cRenderHTML_TOC;
|
41
47
|
VALUE rb_mSmartyPants;
|
42
48
|
|
43
49
|
#define buf2str(t) ((t) ? rb_enc_str_new((const char*)(t)->data, (t)->size, opt->active_enc) : Qnil)
|
@@ -48,6 +54,12 @@ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, v
|
|
48
54
|
BLOCK_CALLBACK("block_code", 2, buf2str(text), buf2str(lang));
|
49
55
|
}
|
50
56
|
|
57
|
+
static void
|
58
|
+
rndr_blockcustom(struct buf *ob, const struct buf *text, const struct buf *type, void *opaque)
|
59
|
+
{
|
60
|
+
BLOCK_CALLBACK("block_custom", 2, buf2str(text), buf2str(type));
|
61
|
+
}
|
62
|
+
|
51
63
|
static void
|
52
64
|
rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)
|
53
65
|
{
|
@@ -287,6 +299,7 @@ rndr_link_attributes(struct buf *ob, const struct buf *url, void *opaque)
|
|
287
299
|
|
288
300
|
static struct sd_callbacks rb_greenmat_callbacks = {
|
289
301
|
rndr_blockcode,
|
302
|
+
rndr_blockcustom,
|
290
303
|
rndr_blockquote,
|
291
304
|
rndr_raw_block,
|
292
305
|
rndr_header,
|
@@ -325,6 +338,7 @@ static struct sd_callbacks rb_greenmat_callbacks = {
|
|
325
338
|
|
326
339
|
static const char *rb_greenmat_method_names[] = {
|
327
340
|
"block_code",
|
341
|
+
"block_custom",
|
328
342
|
"block_quote",
|
329
343
|
"block_html",
|
330
344
|
"header",
|
@@ -369,16 +383,22 @@ static void rb_greenmat_rbase_mark(struct rb_greenmat_rndr *rndr)
|
|
369
383
|
rb_gc_mark(rndr->options.link_attributes);
|
370
384
|
}
|
371
385
|
|
386
|
+
static void rndr_deallocate(void *rndr)
|
387
|
+
{
|
388
|
+
xfree(rndr);
|
389
|
+
}
|
390
|
+
|
372
391
|
static VALUE rb_greenmat_rbase_alloc(VALUE klass)
|
373
392
|
{
|
374
393
|
struct rb_greenmat_rndr *rndr = ALLOC(struct rb_greenmat_rndr);
|
375
394
|
memset(rndr, 0x0, sizeof(struct rb_greenmat_rndr));
|
376
|
-
return Data_Wrap_Struct(klass, rb_greenmat_rbase_mark,
|
395
|
+
return Data_Wrap_Struct(klass, rb_greenmat_rbase_mark, rndr_deallocate, rndr);
|
377
396
|
}
|
378
397
|
|
379
398
|
static void rb_greenmat__overload(VALUE self, VALUE base_class)
|
380
399
|
{
|
381
400
|
struct rb_greenmat_rndr *rndr;
|
401
|
+
VALUE options_ivar;
|
382
402
|
|
383
403
|
Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
|
384
404
|
rndr->options.self = self;
|
@@ -399,6 +419,10 @@ static void rb_greenmat__overload(VALUE self, VALUE base_class)
|
|
399
419
|
dest[i] = source[i];
|
400
420
|
}
|
401
421
|
}
|
422
|
+
|
423
|
+
options_ivar = rb_attr_get(self, rb_intern("@options"));
|
424
|
+
if (options_ivar == Qundef || options_ivar == Qnil)
|
425
|
+
rb_iv_set(self, "@options", rb_hash_new());
|
402
426
|
}
|
403
427
|
|
404
428
|
static VALUE rb_greenmat_rbase_init(VALUE self)
|
@@ -418,6 +442,9 @@ static VALUE rb_greenmat_html_init(int argc, VALUE *argv, VALUE self)
|
|
418
442
|
if (rb_scan_args(argc, argv, "01", &hash) == 1) {
|
419
443
|
Check_Type(hash, T_HASH);
|
420
444
|
|
445
|
+
/* Give access to the passed options through `@options` */
|
446
|
+
rb_iv_set(self, "@options", hash);
|
447
|
+
|
421
448
|
/* escape_html */
|
422
449
|
if (rb_hash_aref(hash, CSTR2SYM("escape_html")) == Qtrue)
|
423
450
|
render_flags |= HTML_ESCAPE;
|
@@ -472,25 +499,45 @@ static VALUE rb_greenmat_html_init(int argc, VALUE *argv, VALUE self)
|
|
472
499
|
static VALUE rb_greenmat_htmltoc_init(int argc, VALUE *argv, VALUE self)
|
473
500
|
{
|
474
501
|
struct rb_greenmat_rndr *rndr;
|
475
|
-
int
|
476
|
-
VALUE hash,
|
502
|
+
unsigned int render_flags = HTML_TOC;
|
503
|
+
VALUE hash, nesting_level = Qnil;
|
477
504
|
|
478
505
|
Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
|
479
506
|
|
480
507
|
if (rb_scan_args(argc, argv, "01", &hash) == 1) {
|
481
508
|
Check_Type(hash, T_HASH);
|
482
509
|
|
483
|
-
|
510
|
+
/* Give access to the passed options through `@options` */
|
511
|
+
rb_iv_set(self, "@options", hash);
|
484
512
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
513
|
+
/* escape_html */
|
514
|
+
if (rb_hash_aref(hash, CSTR2SYM("escape_html")) == Qtrue)
|
515
|
+
render_flags |= HTML_ESCAPE;
|
516
|
+
|
517
|
+
/* Nesting level */
|
518
|
+
nesting_level = rb_hash_aref(hash, CSTR2SYM("nesting_level"));
|
489
519
|
}
|
490
520
|
|
491
|
-
sdhtml_toc_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html,
|
521
|
+
sdhtml_toc_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html, render_flags);
|
492
522
|
rb_greenmat__overload(self, rb_cRenderHTML_TOC);
|
493
523
|
|
524
|
+
/* Check whether we are dealing with a Range object by
|
525
|
+
checking whether the object responds to min and max */
|
526
|
+
if (rb_respond_to(nesting_level, rb_intern("min")) &&
|
527
|
+
rb_respond_to(nesting_level, rb_intern("max"))) {
|
528
|
+
int min = NUM2INT(rb_funcall(nesting_level, rb_intern("min"), 0));
|
529
|
+
int max = NUM2INT(rb_funcall(nesting_level, rb_intern("max"), 0));
|
530
|
+
|
531
|
+
rndr->options.html.toc_data.nesting_bounds[0] = min;
|
532
|
+
rndr->options.html.toc_data.nesting_bounds[1] = max;
|
533
|
+
} else if (FIXNUM_P(nesting_level)) {
|
534
|
+
rndr->options.html.toc_data.nesting_bounds[0] = 1;
|
535
|
+
rndr->options.html.toc_data.nesting_bounds[1] = NUM2INT(nesting_level);
|
536
|
+
} else {
|
537
|
+
rndr->options.html.toc_data.nesting_bounds[0] = 1;
|
538
|
+
rndr->options.html.toc_data.nesting_bounds[1] = 6;
|
539
|
+
}
|
540
|
+
|
494
541
|
return Qnil;
|
495
542
|
}
|
496
543
|
|
data/ext/greenmat/greenmat.h
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#ifndef GREENMAT_H__
|
2
24
|
#define GREENMAT_H__
|
3
25
|
|
data/ext/greenmat/houdini.h
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#ifndef HOUDINI_H__
|
2
24
|
#define HOUDINI_H__
|
3
25
|
|
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#include <assert.h>
|
2
24
|
#include <stdio.h>
|
3
25
|
#include <string.h>
|
@@ -22,10 +44,10 @@
|
|
22
44
|
* have its native function (i.e. as an URL
|
23
45
|
* component/separator) and hence needs no escaping.
|
24
46
|
*
|
25
|
-
* There
|
26
|
-
*
|
27
|
-
*
|
28
|
-
*
|
47
|
+
* There is one exception: the ' (single quote)
|
48
|
+
* character does not appear in the table.
|
49
|
+
* It is meant to appear in the URL as components,
|
50
|
+
* however it require special HTML-entity escaping
|
29
51
|
* to generate valid HTML markup.
|
30
52
|
*
|
31
53
|
* All other characters will be escaped to %XX.
|
@@ -34,7 +56,7 @@
|
|
34
56
|
static const char HREF_SAFE[] = {
|
35
57
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
36
58
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
37
|
-
0, 1, 0, 1, 1, 1,
|
59
|
+
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
38
60
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
|
39
61
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
40
62
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
|
@@ -73,12 +95,6 @@ houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size)
|
|
73
95
|
break;
|
74
96
|
|
75
97
|
switch (src[i]) {
|
76
|
-
/* amp appears all the time in URLs, but needs
|
77
|
-
* HTML-entity escaping to be inside an href */
|
78
|
-
case '&':
|
79
|
-
BUFPUTSL(ob, "&");
|
80
|
-
break;
|
81
|
-
|
82
98
|
/* the single quote is a valid URL character
|
83
99
|
* according to the standard; it needs HTML
|
84
100
|
* entity escaping too */
|
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#include <assert.h>
|
2
24
|
#include <stdio.h>
|
3
25
|
#include <string.h>
|
@@ -80,4 +102,3 @@ houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size)
|
|
80
102
|
{
|
81
103
|
houdini_escape_html0(ob, src, size, 1);
|
82
104
|
}
|
83
|
-
|
data/ext/greenmat/html.c
CHANGED
@@ -1,23 +1,28 @@
|
|
1
1
|
/*
|
2
2
|
* Copyright (c) 2009, Natacha Porté
|
3
|
-
* Copyright (c)
|
3
|
+
* Copyright (c) 2015, Vicent Marti
|
4
4
|
*
|
5
|
-
* Permission
|
6
|
-
*
|
7
|
-
*
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
10
|
+
* furnished to do so, subject to the following conditions:
|
8
11
|
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
14
|
-
*
|
15
|
-
* OR
|
12
|
+
* The above copyright notice and this permission notice shall be included in
|
13
|
+
* all copies or substantial portions of the Software.
|
14
|
+
*
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
* THE SOFTWARE.
|
16
22
|
*/
|
17
23
|
|
18
24
|
#include "markdown.h"
|
19
25
|
#include "html.h"
|
20
|
-
#include "ruby.h"
|
21
26
|
#include <string.h>
|
22
27
|
#include <stdlib.h>
|
23
28
|
#include <stdio.h>
|
@@ -115,6 +120,52 @@ rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, vo
|
|
115
120
|
return 1;
|
116
121
|
}
|
117
122
|
|
123
|
+
static void
|
124
|
+
rndr_blockcustom(struct buf *ob, const struct buf *text, const struct buf *type, void *opaque)
|
125
|
+
{
|
126
|
+
struct html_renderopt *options = opaque;
|
127
|
+
|
128
|
+
if (ob->size) bufputc(ob, '\n');
|
129
|
+
|
130
|
+
if (type && type->size) {
|
131
|
+
size_t i, cls;
|
132
|
+
if (options->flags & HTML_PRETTIFY) {
|
133
|
+
BUFPUTSL(ob, "<div data-type=\"customblock prettyprint\" data-metadata=\"");
|
134
|
+
cls++;
|
135
|
+
} else {
|
136
|
+
BUFPUTSL(ob, "<div data-type=\"customblock\" data-metadata=\"");
|
137
|
+
}
|
138
|
+
|
139
|
+
for (i = 0, cls = 0; i < type->size; ++i, ++cls) {
|
140
|
+
while (i < type->size && isspace(type->data[i]))
|
141
|
+
i++;
|
142
|
+
|
143
|
+
if (i < type->size) {
|
144
|
+
size_t org = i;
|
145
|
+
while (i < type->size && is_non_space(type->data[i]))
|
146
|
+
i++;
|
147
|
+
|
148
|
+
if (type->data[org] == '.')
|
149
|
+
org++;
|
150
|
+
|
151
|
+
if (cls) bufputc(ob, ' ');
|
152
|
+
escape_html(ob, type->data + org, i - org);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
BUFPUTSL(ob, "\">");
|
157
|
+
} else if (options->flags & HTML_PRETTIFY) {
|
158
|
+
BUFPUTSL(ob, "<div data-type=\"customblock prettyprint\">");
|
159
|
+
} else {
|
160
|
+
BUFPUTSL(ob, "<div data-type=\"customblock\">");
|
161
|
+
}
|
162
|
+
|
163
|
+
if (text)
|
164
|
+
escape_html(ob, text->data, text->size);
|
165
|
+
|
166
|
+
BUFPUTSL(ob, "</div>\n");
|
167
|
+
}
|
168
|
+
|
118
169
|
static void
|
119
170
|
rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)
|
120
171
|
{
|
@@ -125,10 +176,10 @@ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, v
|
|
125
176
|
if (lang && lang->size) {
|
126
177
|
size_t i, cls;
|
127
178
|
if (options->flags & HTML_PRETTIFY) {
|
128
|
-
BUFPUTSL(ob, "<pre><code class=\"prettyprint ");
|
179
|
+
BUFPUTSL(ob, "<pre><code class=\"prettyprint\" data-metadata=\"");
|
129
180
|
cls++;
|
130
181
|
} else {
|
131
|
-
BUFPUTSL(ob, "<pre><code
|
182
|
+
BUFPUTSL(ob, "<pre><code data-metadata=\"");
|
132
183
|
}
|
133
184
|
|
134
185
|
for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {
|
@@ -250,8 +301,15 @@ rndr_quote(struct buf *ob, const struct buf *text, void *opaque)
|
|
250
301
|
if (!text || !text->size)
|
251
302
|
return 0;
|
252
303
|
|
304
|
+
struct html_renderopt *options = opaque;
|
305
|
+
|
253
306
|
BUFPUTSL(ob, "<q>");
|
254
|
-
|
307
|
+
|
308
|
+
if (options->flags & HTML_ESCAPE)
|
309
|
+
escape_html(ob, text->data, text->size);
|
310
|
+
else
|
311
|
+
bufput(ob, text->data, text->size);
|
312
|
+
|
255
313
|
BUFPUTSL(ob, "</q>");
|
256
314
|
|
257
315
|
return 1;
|
@@ -265,17 +323,52 @@ rndr_linebreak(struct buf *ob, void *opaque)
|
|
265
323
|
return 1;
|
266
324
|
}
|
267
325
|
|
268
|
-
|
326
|
+
static void
|
327
|
+
rndr_header_anchor(struct buf *out, const struct buf *anchor)
|
269
328
|
{
|
270
|
-
|
271
|
-
VALUE space_regex = rb_reg_new(" +", 2 /* length */, 0);
|
272
|
-
VALUE tags_regex = rb_reg_new("<\\/?[^>]*>", 10, 0);
|
329
|
+
static const char *STRIPPED = " -&+$,/:;=?@\"#{}|^~[]`\\*()%.!'";
|
273
330
|
|
274
|
-
|
275
|
-
|
276
|
-
|
331
|
+
const uint8_t *a = anchor->data;
|
332
|
+
const size_t size = anchor->size;
|
333
|
+
size_t i = 0;
|
334
|
+
int stripped = 0, inserted = 0;
|
277
335
|
|
278
|
-
|
336
|
+
for (; i < size; ++i) {
|
337
|
+
// skip html tags
|
338
|
+
if (a[i] == '<') {
|
339
|
+
while (i < size && a[i] != '>')
|
340
|
+
i++;
|
341
|
+
// skip html entities
|
342
|
+
} else if (a[i] == '&') {
|
343
|
+
while (i < size && a[i] != ';')
|
344
|
+
i++;
|
345
|
+
}
|
346
|
+
// replace non-ascii or invalid characters with dashes
|
347
|
+
else if (!isascii(a[i]) || strchr(STRIPPED, a[i])) {
|
348
|
+
if (inserted && !stripped)
|
349
|
+
bufputc(out, '-');
|
350
|
+
// and do it only once
|
351
|
+
stripped = 1;
|
352
|
+
}
|
353
|
+
else {
|
354
|
+
bufputc(out, tolower(a[i]));
|
355
|
+
stripped = 0;
|
356
|
+
inserted++;
|
357
|
+
}
|
358
|
+
}
|
359
|
+
|
360
|
+
// replace the last dash if there was anything added
|
361
|
+
if (stripped && inserted)
|
362
|
+
out->size--;
|
363
|
+
|
364
|
+
// if anchor found empty, use djb2 hash for it
|
365
|
+
if (!inserted && anchor->size) {
|
366
|
+
unsigned long hash = 5381;
|
367
|
+
for (i = 0; i < size; ++i) {
|
368
|
+
hash = ((hash << 5) + hash) + a[i]; /* h * 33 + c */
|
369
|
+
}
|
370
|
+
bufprintf(out, "part-%lx", hash);
|
371
|
+
}
|
279
372
|
}
|
280
373
|
|
281
374
|
static void
|
@@ -286,8 +379,12 @@ rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)
|
|
286
379
|
if (ob->size)
|
287
380
|
bufputc(ob, '\n');
|
288
381
|
|
289
|
-
if ((options->flags & HTML_TOC) &&
|
290
|
-
|
382
|
+
if ((options->flags & HTML_TOC) && level >= options->toc_data.nesting_bounds[0] &&
|
383
|
+
level <= options->toc_data.nesting_bounds[1]) {
|
384
|
+
bufprintf(ob, "<h%d id=\"", level);
|
385
|
+
rndr_header_anchor(ob, text);
|
386
|
+
BUFPUTSL(ob, "\">");
|
387
|
+
}
|
291
388
|
else
|
292
389
|
bufprintf(ob, "<h%d>", level);
|
293
390
|
|
@@ -395,15 +492,30 @@ rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
|
|
395
492
|
static void
|
396
493
|
rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
|
397
494
|
{
|
398
|
-
size_t org,
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
495
|
+
size_t org, size;
|
496
|
+
struct html_renderopt *options = opaque;
|
497
|
+
|
498
|
+
if (!text)
|
499
|
+
return;
|
500
|
+
|
501
|
+
size = text->size;
|
502
|
+
while (size > 0 && text->data[size - 1] == '\n')
|
503
|
+
size--;
|
504
|
+
|
505
|
+
for (org = 0; org < size && text->data[org] == '\n'; ++org)
|
506
|
+
|
507
|
+
if (org >= size)
|
508
|
+
return;
|
509
|
+
|
510
|
+
/* Remove style tags if the `:no_styles` option is enabled */
|
511
|
+
if ((options->flags & HTML_SKIP_STYLE) != 0 &&
|
512
|
+
sdhtml_is_tag(text->data, size, "style"))
|
513
|
+
return;
|
514
|
+
|
515
|
+
if (ob->size)
|
516
|
+
bufputc(ob, '\n');
|
517
|
+
|
518
|
+
bufput(ob, text->data + org, size - org);
|
407
519
|
bufputc(ob, '\n');
|
408
520
|
}
|
409
521
|
|
@@ -429,10 +541,15 @@ static int
|
|
429
541
|
rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)
|
430
542
|
{
|
431
543
|
struct html_renderopt *options = opaque;
|
432
|
-
|
544
|
+
|
545
|
+
if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
|
546
|
+
return 0;
|
433
547
|
|
434
548
|
BUFPUTSL(ob, "<img src=\"");
|
435
|
-
|
549
|
+
|
550
|
+
if (link && link->size)
|
551
|
+
escape_href(ob, link->data, link->size);
|
552
|
+
|
436
553
|
BUFPUTSL(ob, "\" alt=\"");
|
437
554
|
|
438
555
|
if (alt && alt->size)
|
@@ -440,7 +557,8 @@ rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, cons
|
|
440
557
|
|
441
558
|
if (title && title->size) {
|
442
559
|
BUFPUTSL(ob, "\" title=\"");
|
443
|
-
escape_html(ob, title->data, title->size);
|
560
|
+
escape_html(ob, title->data, title->size);
|
561
|
+
}
|
444
562
|
|
445
563
|
bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");
|
446
564
|
return 1;
|
@@ -452,7 +570,7 @@ rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
|
|
452
570
|
struct html_renderopt *options = opaque;
|
453
571
|
|
454
572
|
/* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
|
455
|
-
|
573
|
+
It doesn't see if there are any valid tags, just escape all of them. */
|
456
574
|
if((options->flags & HTML_ESCAPE) != 0) {
|
457
575
|
escape_html(ob, text->data, text->size);
|
458
576
|
return 1;
|
@@ -591,7 +709,7 @@ rndr_footnote_def(struct buf *ob, const struct buf *text, unsigned int num, void
|
|
591
709
|
bufprintf(ob, "\n<li id=\"fn%d\">\n", num);
|
592
710
|
if (pfound) {
|
593
711
|
bufput(ob, text->data, i);
|
594
|
-
bufprintf(ob, " <a href=\"#fnref%d\"
|
712
|
+
bufprintf(ob, " <a href=\"#fnref%d\">↩</a>", num);
|
595
713
|
bufput(ob, text->data + i, text->size - i);
|
596
714
|
} else if (text) {
|
597
715
|
bufput(ob, text->data, text->size);
|
@@ -602,7 +720,7 @@ rndr_footnote_def(struct buf *ob, const struct buf *text, unsigned int num, void
|
|
602
720
|
static int
|
603
721
|
rndr_footnote_ref(struct buf *ob, unsigned int num, void *opaque)
|
604
722
|
{
|
605
|
-
bufprintf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\"
|
723
|
+
bufprintf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\">%d</a></sup>", num, num, num);
|
606
724
|
return 1;
|
607
725
|
}
|
608
726
|
|
@@ -611,7 +729,8 @@ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
|
|
611
729
|
{
|
612
730
|
struct html_renderopt *options = opaque;
|
613
731
|
|
614
|
-
if (level
|
732
|
+
if (level >= options->toc_data.nesting_bounds[0] &&
|
733
|
+
level <= options->toc_data.nesting_bounds[1]) {
|
615
734
|
/* set the level offset if this is the first header
|
616
735
|
* we're parsing for the document */
|
617
736
|
if (options->toc_data.current_level == 0)
|
@@ -635,8 +754,17 @@ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
|
|
635
754
|
BUFPUTSL(ob,"</li>\n<li>\n");
|
636
755
|
}
|
637
756
|
|
638
|
-
bufprintf(ob, "<a href=\"
|
639
|
-
|
757
|
+
bufprintf(ob, "<a href=\"#");
|
758
|
+
rndr_header_anchor(ob, text);
|
759
|
+
BUFPUTSL(ob, "\">");
|
760
|
+
|
761
|
+
if (text) {
|
762
|
+
if (options->flags & HTML_ESCAPE)
|
763
|
+
escape_html(ob, text->data, text->size);
|
764
|
+
else
|
765
|
+
bufput(ob, text->data, text->size);
|
766
|
+
}
|
767
|
+
|
640
768
|
BUFPUTSL(ob, "</a>\n");
|
641
769
|
}
|
642
770
|
}
|
@@ -661,12 +789,13 @@ toc_finalize(struct buf *ob, void *opaque)
|
|
661
789
|
}
|
662
790
|
|
663
791
|
void
|
664
|
-
sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, int
|
792
|
+
sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)
|
665
793
|
{
|
666
794
|
static const struct sd_callbacks cb_default = {
|
667
795
|
NULL,
|
668
796
|
NULL,
|
669
797
|
NULL,
|
798
|
+
NULL,
|
670
799
|
toc_header,
|
671
800
|
NULL,
|
672
801
|
NULL,
|
@@ -702,8 +831,7 @@ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *optio
|
|
702
831
|
};
|
703
832
|
|
704
833
|
memset(options, 0x0, sizeof(struct html_renderopt));
|
705
|
-
options->flags =
|
706
|
-
options->toc_data.nesting_level = nesting_level;
|
834
|
+
options->flags = render_flags;
|
707
835
|
|
708
836
|
memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
|
709
837
|
}
|
@@ -713,6 +841,7 @@ sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options,
|
|
713
841
|
{
|
714
842
|
static const struct sd_callbacks cb_default = {
|
715
843
|
rndr_blockcode,
|
844
|
+
rndr_blockcustom,
|
716
845
|
rndr_blockquote,
|
717
846
|
rndr_raw_block,
|
718
847
|
rndr_header,
|
@@ -752,7 +881,8 @@ sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options,
|
|
752
881
|
/* Prepare the options pointer */
|
753
882
|
memset(options, 0x0, sizeof(struct html_renderopt));
|
754
883
|
options->flags = render_flags;
|
755
|
-
options->toc_data.
|
884
|
+
options->toc_data.nesting_bounds[0] = 1;
|
885
|
+
options->toc_data.nesting_bounds[1] = 6;
|
756
886
|
|
757
887
|
/* Prepare the callbacks */
|
758
888
|
memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
|