greenmat 3.2.2.2 → 3.5.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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));