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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +21 -8
  3. data/CHANGELOG.md +24 -0
  4. data/COPYING +17 -11
  5. data/Gemfile +2 -2
  6. data/README.md +19 -13
  7. data/Rakefile +1 -0
  8. data/bin/greenmat +4 -40
  9. data/ext/greenmat/autolink.c +24 -12
  10. data/ext/greenmat/buffer.c +24 -17
  11. data/ext/greenmat/buffer.h +18 -13
  12. data/ext/greenmat/gm_markdown.c +41 -14
  13. data/ext/greenmat/gm_render.c +68 -21
  14. data/ext/greenmat/greenmat.h +22 -0
  15. data/ext/greenmat/houdini.h +22 -0
  16. data/ext/greenmat/houdini_href_e.c +27 -11
  17. data/ext/greenmat/houdini_html_e.c +22 -1
  18. data/ext/greenmat/html.c +177 -47
  19. data/ext/greenmat/html.h +19 -14
  20. data/ext/greenmat/html_smartypants.c +47 -20
  21. data/ext/greenmat/markdown.c +181 -30
  22. data/ext/greenmat/markdown.h +20 -16
  23. data/ext/greenmat/stack.c +22 -0
  24. data/ext/greenmat/stack.h +22 -0
  25. data/greenmat.gemspec +4 -3
  26. data/lib/greenmat.rb +1 -1
  27. data/lib/greenmat/cli.rb +86 -0
  28. data/lib/greenmat/compat.rb +0 -5
  29. data/lib/greenmat/render_strip.rb +13 -1
  30. data/lib/greenmat/version.rb +1 -1
  31. data/spec/greenmat/markdown_spec.rb +166 -0
  32. data/test/custom_render_test.rb +41 -2
  33. data/test/greenmat_bin_test.rb +80 -0
  34. data/test/greenmat_compat_test.rb +6 -6
  35. data/test/html5_test.rb +60 -38
  36. data/test/html_render_test.rb +162 -128
  37. data/test/html_toc_render_test.rb +74 -11
  38. data/test/markdown_test.rb +258 -182
  39. data/test/safe_render_test.rb +5 -6
  40. data/test/smarty_html_test.rb +19 -13
  41. data/test/smarty_pants_test.rb +10 -0
  42. data/test/stripdown_render_test.rb +38 -9
  43. data/test/test_helper.rb +30 -9
  44. metadata +29 -13
@@ -1,17 +1,23 @@
1
1
  /*
2
- * Copyright (c) 2011, Vicent Marti
2
+ * Copyright (c) 2015, Vicent Marti
3
3
  *
4
- * Permission to use, copy, modify, and distribute this software for any
5
- * purpose with or without fee is hereby granted, provided that the above
6
- * copyright notice and this permission notice appear in all copies.
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
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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, NULL, rndr);
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 nesting_level = 6;
476
- VALUE hash, key = Qnil;
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
- key = CSTR2SYM("nesting_level");
510
+ /* Give access to the passed options through `@options` */
511
+ rb_iv_set(self, "@options", hash);
484
512
 
485
- if (RTEST(rb_hash_aref(hash, key))) {
486
- Check_Type(rb_hash_aref(hash, key), T_FIXNUM);
487
- nesting_level = NUM2INT(rb_hash_aref(hash, key));
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, nesting_level);
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
 
@@ -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
 
@@ -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 are two exceptions: the chacters & (amp)
26
- * and ' (single quote) do not appear in the table.
27
- * They are meant to appear in the URL as components,
28
- * yet they require special HTML-entity escaping
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, 0, 0, 1, 1, 1, 1, 1, 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, "&amp;");
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) 2011, Vicent Marti
3
+ * Copyright (c) 2015, Vicent Marti
4
4
  *
5
- * Permission to use, copy, modify, and distribute this software for any
6
- * purpose with or without fee is hereby granted, provided that the above
7
- * copyright notice and this permission notice appear in all copies.
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
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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 class=\"");
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
- bufput(ob, text->data, text->size);
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
- char *header_anchor(struct buf *text)
326
+ static void
327
+ rndr_header_anchor(struct buf *out, const struct buf *anchor)
269
328
  {
270
- VALUE str = rb_str_new2(bufcstr(text));
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
- VALUE heading = rb_funcall(str, rb_intern("gsub"), 2, space_regex, rb_str_new2("-"));
275
- heading = rb_funcall(heading, rb_intern("gsub"), 2, tags_regex, rb_str_new2(""));
276
- heading = rb_funcall(heading, rb_intern("downcase"), 0);
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
- return StringValueCStr(heading);
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) && (level <= options->toc_data.nesting_level))
290
- bufprintf(ob, "<h%d id=\"%s\">", level, header_anchor(text));
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, sz;
399
- if (!text) return;
400
- sz = text->size;
401
- while (sz > 0 && text->data[sz - 1] == '\n') sz--;
402
- org = 0;
403
- while (org < sz && text->data[org] == '\n') org++;
404
- if (org >= sz) return;
405
- if (ob->size) bufputc(ob, '\n');
406
- bufput(ob, text->data + org, sz - org);
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
- if (!link || !link->size) return 0;
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
- escape_href(ob, link->data, link->size);
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
- * It doens't see if there are any valid tags, just escape all of them. */
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, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
712
+ bufprintf(ob, "&nbsp;<a href=\"#fnref%d\">&#8617;</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\" rel=\"footnote\">%d</a></sup>", num, num, num);
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 <= options->toc_data.nesting_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=\"#%s\">", header_anchor(text));
639
- if (text) escape_html(ob, text->data, text->size);
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 nesting_level)
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 = HTML_TOC;
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.nesting_level = 99;
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));