qiita_marker 0.23.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +50 -0
  4. data/Rakefile +113 -0
  5. data/bin/qiita_marker +123 -0
  6. data/ext/qiita_marker/arena.c +103 -0
  7. data/ext/qiita_marker/autolink.c +425 -0
  8. data/ext/qiita_marker/autolink.h +8 -0
  9. data/ext/qiita_marker/blocks.c +1596 -0
  10. data/ext/qiita_marker/buffer.c +278 -0
  11. data/ext/qiita_marker/buffer.h +116 -0
  12. data/ext/qiita_marker/case_fold_switch.inc +4327 -0
  13. data/ext/qiita_marker/chunk.h +135 -0
  14. data/ext/qiita_marker/cmark-gfm-core-extensions.h +54 -0
  15. data/ext/qiita_marker/cmark-gfm-extension_api.h +736 -0
  16. data/ext/qiita_marker/cmark-gfm-extensions_export.h +42 -0
  17. data/ext/qiita_marker/cmark-gfm.h +817 -0
  18. data/ext/qiita_marker/cmark-gfm_export.h +42 -0
  19. data/ext/qiita_marker/cmark-gfm_version.h +7 -0
  20. data/ext/qiita_marker/cmark.c +55 -0
  21. data/ext/qiita_marker/cmark_ctype.c +44 -0
  22. data/ext/qiita_marker/cmark_ctype.h +33 -0
  23. data/ext/qiita_marker/commonmark.c +529 -0
  24. data/ext/qiita_marker/config.h +76 -0
  25. data/ext/qiita_marker/core-extensions.c +27 -0
  26. data/ext/qiita_marker/entities.inc +2138 -0
  27. data/ext/qiita_marker/ext_scanners.c +879 -0
  28. data/ext/qiita_marker/ext_scanners.h +24 -0
  29. data/ext/qiita_marker/extconf.rb +7 -0
  30. data/ext/qiita_marker/footnotes.c +63 -0
  31. data/ext/qiita_marker/footnotes.h +27 -0
  32. data/ext/qiita_marker/houdini.h +57 -0
  33. data/ext/qiita_marker/houdini_href_e.c +100 -0
  34. data/ext/qiita_marker/houdini_html_e.c +66 -0
  35. data/ext/qiita_marker/houdini_html_u.c +149 -0
  36. data/ext/qiita_marker/html.c +486 -0
  37. data/ext/qiita_marker/html.h +27 -0
  38. data/ext/qiita_marker/inlines.c +1691 -0
  39. data/ext/qiita_marker/inlines.h +29 -0
  40. data/ext/qiita_marker/iterator.c +159 -0
  41. data/ext/qiita_marker/iterator.h +26 -0
  42. data/ext/qiita_marker/latex.c +466 -0
  43. data/ext/qiita_marker/linked_list.c +37 -0
  44. data/ext/qiita_marker/man.c +278 -0
  45. data/ext/qiita_marker/map.c +122 -0
  46. data/ext/qiita_marker/map.h +41 -0
  47. data/ext/qiita_marker/node.c +979 -0
  48. data/ext/qiita_marker/node.h +125 -0
  49. data/ext/qiita_marker/parser.h +58 -0
  50. data/ext/qiita_marker/plaintext.c +235 -0
  51. data/ext/qiita_marker/plugin.c +36 -0
  52. data/ext/qiita_marker/plugin.h +34 -0
  53. data/ext/qiita_marker/qiita_marker.c +1321 -0
  54. data/ext/qiita_marker/qiita_marker.h +16 -0
  55. data/ext/qiita_marker/references.c +42 -0
  56. data/ext/qiita_marker/references.h +26 -0
  57. data/ext/qiita_marker/registry.c +63 -0
  58. data/ext/qiita_marker/registry.h +24 -0
  59. data/ext/qiita_marker/render.c +205 -0
  60. data/ext/qiita_marker/render.h +62 -0
  61. data/ext/qiita_marker/scanners.c +10520 -0
  62. data/ext/qiita_marker/scanners.h +62 -0
  63. data/ext/qiita_marker/scanners.re +341 -0
  64. data/ext/qiita_marker/strikethrough.c +167 -0
  65. data/ext/qiita_marker/strikethrough.h +9 -0
  66. data/ext/qiita_marker/syntax_extension.c +149 -0
  67. data/ext/qiita_marker/syntax_extension.h +34 -0
  68. data/ext/qiita_marker/table.c +822 -0
  69. data/ext/qiita_marker/table.h +12 -0
  70. data/ext/qiita_marker/tagfilter.c +60 -0
  71. data/ext/qiita_marker/tagfilter.h +8 -0
  72. data/ext/qiita_marker/tasklist.c +156 -0
  73. data/ext/qiita_marker/tasklist.h +8 -0
  74. data/ext/qiita_marker/utf8.c +317 -0
  75. data/ext/qiita_marker/utf8.h +35 -0
  76. data/ext/qiita_marker/xml.c +181 -0
  77. data/lib/qiita_marker/config.rb +52 -0
  78. data/lib/qiita_marker/node/inspect.rb +57 -0
  79. data/lib/qiita_marker/node.rb +83 -0
  80. data/lib/qiita_marker/renderer/html_renderer.rb +252 -0
  81. data/lib/qiita_marker/renderer.rb +135 -0
  82. data/lib/qiita_marker/version.rb +5 -0
  83. data/lib/qiita_marker.rb +45 -0
  84. data/qiita_marker.gemspec +40 -0
  85. data/test/benchmark.rb +32 -0
  86. data/test/fixtures/curly.md +1 -0
  87. data/test/fixtures/dingus.md +10 -0
  88. data/test/fixtures/strong.md +1 -0
  89. data/test/fixtures/table.md +10 -0
  90. data/test/test_attributes.rb +24 -0
  91. data/test/test_basics.rb +35 -0
  92. data/test/test_commands.rb +72 -0
  93. data/test/test_commonmark.rb +36 -0
  94. data/test/test_doc.rb +130 -0
  95. data/test/test_encoding.rb +23 -0
  96. data/test/test_extensions.rb +116 -0
  97. data/test/test_footnotes.rb +60 -0
  98. data/test/test_gc.rb +47 -0
  99. data/test/test_helper.rb +71 -0
  100. data/test/test_linebreaks.rb +15 -0
  101. data/test/test_maliciousness.rb +262 -0
  102. data/test/test_node.rb +89 -0
  103. data/test/test_options.rb +37 -0
  104. data/test/test_pathological_inputs.rb +94 -0
  105. data/test/test_plaintext.rb +46 -0
  106. data/test/test_renderer.rb +47 -0
  107. data/test/test_smartpunct.rb +27 -0
  108. data/test/test_spec.rb +30 -0
  109. data/test/test_tasklists.rb +43 -0
  110. data/test/test_xml.rb +107 -0
  111. metadata +313 -0
@@ -0,0 +1,1321 @@
1
+ #include "qiita_marker.h"
2
+ #include "cmark-gfm.h"
3
+ #include "houdini.h"
4
+ #include "node.h"
5
+ #include "registry.h"
6
+ #include "parser.h"
7
+ #include "syntax_extension.h"
8
+ #include "cmark-gfm-core-extensions.h"
9
+
10
+ static VALUE rb_eNodeError;
11
+ static VALUE rb_cNode;
12
+
13
+ static VALUE sym_document;
14
+ static VALUE sym_blockquote;
15
+ static VALUE sym_list;
16
+ static VALUE sym_list_item;
17
+ static VALUE sym_code_block;
18
+ static VALUE sym_html;
19
+ static VALUE sym_paragraph;
20
+ static VALUE sym_header;
21
+ static VALUE sym_hrule;
22
+ static VALUE sym_text;
23
+ static VALUE sym_softbreak;
24
+ static VALUE sym_linebreak;
25
+ static VALUE sym_code;
26
+ static VALUE sym_inline_html;
27
+ static VALUE sym_emph;
28
+ static VALUE sym_strong;
29
+ static VALUE sym_link;
30
+ static VALUE sym_image;
31
+ static VALUE sym_footnote_reference;
32
+ static VALUE sym_footnote_definition;
33
+
34
+ static VALUE sym_bullet_list;
35
+ static VALUE sym_ordered_list;
36
+
37
+ static VALUE sym_left;
38
+ static VALUE sym_right;
39
+ static VALUE sym_center;
40
+
41
+ static VALUE encode_utf8_string(const char *c_string) {
42
+ VALUE string = rb_str_new2(c_string);
43
+ int enc = rb_enc_find_index("UTF-8");
44
+ rb_enc_associate_index(string, enc);
45
+ return string;
46
+ }
47
+
48
+ /* Encode a C string using the encoding from Ruby string +source+. */
49
+ static VALUE encode_source_string(const char *c_string, VALUE source) {
50
+ VALUE string = rb_str_new2(c_string);
51
+ rb_enc_copy(string, source);
52
+ return string;
53
+ }
54
+
55
+ static void rb_mark_c_struct(void *data) {
56
+ cmark_node *node = data;
57
+ cmark_node *child;
58
+
59
+ /* Mark the parent to make sure that the tree won't be freed as
60
+ long as a child node is referenced. */
61
+ cmark_node *parent = cmark_node_parent(node);
62
+ if (parent) {
63
+ void *user_data = cmark_node_get_user_data(parent);
64
+ if (!user_data) {
65
+ /* This should never happen. Child can nodes can only
66
+ be returned from parents that already are
67
+ associated with a Ruby object. */
68
+ fprintf(stderr, "parent without user_data\n");
69
+ abort();
70
+ }
71
+ rb_gc_mark((VALUE)user_data);
72
+ }
73
+
74
+ /* Mark all children to make sure their cached Ruby objects won't
75
+ be freed. */
76
+ for (child = cmark_node_first_child(node); child != NULL;
77
+ child = cmark_node_next(child)) {
78
+ void *user_data = cmark_node_get_user_data(child);
79
+ if (user_data)
80
+ rb_gc_mark((VALUE)user_data);
81
+ }
82
+ }
83
+
84
+ static void rb_free_c_struct(void *data) {
85
+ /* It's important that the `free` function does not inspect the
86
+ node data, as it may be part of a tree that was already freed. */
87
+ cmark_node_free(data);
88
+ }
89
+
90
+ static VALUE rb_node_to_value(cmark_node *node) {
91
+ void *user_data;
92
+ RUBY_DATA_FUNC free_func;
93
+ VALUE val;
94
+
95
+ if (node == NULL)
96
+ return Qnil;
97
+
98
+ user_data = cmark_node_get_user_data(node);
99
+ if (user_data)
100
+ return (VALUE)user_data;
101
+
102
+ /* Only free tree roots. */
103
+ free_func = cmark_node_parent(node) ? NULL : rb_free_c_struct;
104
+ val = Data_Wrap_Struct(rb_cNode, rb_mark_c_struct, free_func, node);
105
+ cmark_node_set_user_data(node, (void *)val);
106
+
107
+ return val;
108
+ }
109
+
110
+ /* If the node structure is changed, the finalizers must be updated. */
111
+
112
+ static void rb_parent_added(VALUE val) { RDATA(val)->dfree = NULL; }
113
+
114
+ static void rb_parent_removed(VALUE val) {
115
+ RDATA(val)->dfree = rb_free_c_struct;
116
+ }
117
+
118
+ static cmark_parser *prepare_parser(VALUE rb_options, VALUE rb_extensions, cmark_mem *mem) {
119
+ int options;
120
+ int extensions_len;
121
+ VALUE rb_ext_name;
122
+ int i;
123
+
124
+ Check_Type(rb_options, T_FIXNUM);
125
+ Check_Type(rb_extensions, T_ARRAY);
126
+
127
+ options = FIX2INT(rb_options);
128
+ extensions_len = RARRAY_LEN(rb_extensions);
129
+
130
+ cmark_parser *parser = cmark_parser_new_with_mem(options, mem);
131
+ for (i = 0; i < extensions_len; ++i) {
132
+ rb_ext_name = RARRAY_PTR(rb_extensions)[i];
133
+
134
+ if (!SYMBOL_P(rb_ext_name)) {
135
+ cmark_parser_free(parser);
136
+ cmark_arena_reset();
137
+ rb_raise(rb_eTypeError, "extension names should be Symbols; got a %"PRIsVALUE"", rb_obj_class(rb_ext_name));
138
+ }
139
+
140
+ cmark_syntax_extension *syntax_extension =
141
+ cmark_find_syntax_extension(rb_id2name(SYM2ID(rb_ext_name)));
142
+
143
+ if (!syntax_extension) {
144
+ cmark_parser_free(parser);
145
+ cmark_arena_reset();
146
+ rb_raise(rb_eArgError, "extension %s not found", rb_id2name(SYM2ID(rb_ext_name)));
147
+ }
148
+
149
+ cmark_parser_attach_syntax_extension(parser, syntax_extension);
150
+ }
151
+
152
+ return parser;
153
+ }
154
+
155
+ /*
156
+ * Internal: Parses a Markdown string into an HTML string.
157
+ *
158
+ */
159
+ static VALUE rb_markdown_to_html(VALUE self, VALUE rb_text, VALUE rb_options, VALUE rb_extensions) {
160
+ char *str, *html;
161
+ int len;
162
+ cmark_parser *parser;
163
+ cmark_node *doc;
164
+ Check_Type(rb_text, T_STRING);
165
+ Check_Type(rb_options, T_FIXNUM);
166
+
167
+ parser = prepare_parser(rb_options, rb_extensions, cmark_get_arena_mem_allocator());
168
+
169
+ str = (char *)RSTRING_PTR(rb_text);
170
+ len = RSTRING_LEN(rb_text);
171
+
172
+ cmark_parser_feed(parser, str, len);
173
+ doc = cmark_parser_finish(parser);
174
+ if (doc == NULL) {
175
+ cmark_arena_reset();
176
+ rb_raise(rb_eNodeError, "error parsing document");
177
+ }
178
+
179
+ cmark_mem *default_mem = cmark_get_default_mem_allocator();
180
+ html = cmark_render_html_with_mem(doc, FIX2INT(rb_options), parser->syntax_extensions, default_mem);
181
+ cmark_arena_reset();
182
+
183
+ VALUE ruby_html = rb_str_new2(html);
184
+ default_mem->free(html);
185
+
186
+ return ruby_html;
187
+ }
188
+
189
+ /*
190
+ * Internal: Parses a Markdown string into an HTML string.
191
+ *
192
+ */
193
+ static VALUE rb_markdown_to_xml(VALUE self, VALUE rb_text, VALUE rb_options, VALUE rb_extensions) {
194
+ char *str, *xml;
195
+ int len;
196
+ cmark_parser *parser;
197
+ cmark_node *doc;
198
+ Check_Type(rb_text, T_STRING);
199
+ Check_Type(rb_options, T_FIXNUM);
200
+
201
+ parser = prepare_parser(rb_options, rb_extensions, cmark_get_arena_mem_allocator());
202
+
203
+ str = (char *)RSTRING_PTR(rb_text);
204
+ len = RSTRING_LEN(rb_text);
205
+
206
+ cmark_parser_feed(parser, str, len);
207
+ doc = cmark_parser_finish(parser);
208
+ if (doc == NULL) {
209
+ cmark_arena_reset();
210
+ rb_raise(rb_eNodeError, "error parsing document");
211
+ }
212
+
213
+ cmark_mem *default_mem = cmark_get_default_mem_allocator();
214
+ xml = cmark_render_xml_with_mem(doc, FIX2INT(rb_options), default_mem);
215
+ cmark_arena_reset();
216
+
217
+ VALUE ruby_xml = rb_str_new2(xml);
218
+ default_mem->free(xml);
219
+
220
+ return ruby_xml;
221
+ }
222
+
223
+ /*
224
+ * Internal: Creates a node based on a node type.
225
+ *
226
+ * type - A {Symbol} representing the node to be created. Must be one of the
227
+ * following:
228
+ * - `:document`
229
+ * - `:blockquote`
230
+ * - `:list`
231
+ * - `:list_item`
232
+ * - `:code_block`
233
+ * - `:html`
234
+ * - `:paragraph`
235
+ * - `:header`
236
+ * - `:hrule`
237
+ * - `:text`
238
+ * - `:softbreak`
239
+ * - `:linebreak`
240
+ * - `:code`
241
+ * - `:inline_html`
242
+ * - `:emph`
243
+ * - `:strong`
244
+ * - `:link`
245
+ * - `:image`
246
+ */
247
+ static VALUE rb_node_new(VALUE self, VALUE type) {
248
+ cmark_node_type node_type = 0;
249
+ cmark_node *node;
250
+
251
+ Check_Type(type, T_SYMBOL);
252
+
253
+ if (type == sym_document)
254
+ node_type = CMARK_NODE_DOCUMENT;
255
+ else if (type == sym_blockquote)
256
+ node_type = CMARK_NODE_BLOCK_QUOTE;
257
+ else if (type == sym_list)
258
+ node_type = CMARK_NODE_LIST;
259
+ else if (type == sym_list_item)
260
+ node_type = CMARK_NODE_ITEM;
261
+ else if (type == sym_code_block)
262
+ node_type = CMARK_NODE_CODE_BLOCK;
263
+ else if (type == sym_html)
264
+ node_type = CMARK_NODE_HTML;
265
+ else if (type == sym_paragraph)
266
+ node_type = CMARK_NODE_PARAGRAPH;
267
+ else if (type == sym_header)
268
+ node_type = CMARK_NODE_HEADER;
269
+ else if (type == sym_hrule)
270
+ node_type = CMARK_NODE_HRULE;
271
+ else if (type == sym_text)
272
+ node_type = CMARK_NODE_TEXT;
273
+ else if (type == sym_softbreak)
274
+ node_type = CMARK_NODE_SOFTBREAK;
275
+ else if (type == sym_linebreak)
276
+ node_type = CMARK_NODE_LINEBREAK;
277
+ else if (type == sym_code)
278
+ node_type = CMARK_NODE_CODE;
279
+ else if (type == sym_inline_html)
280
+ node_type = CMARK_NODE_INLINE_HTML;
281
+ else if (type == sym_emph)
282
+ node_type = CMARK_NODE_EMPH;
283
+ else if (type == sym_strong)
284
+ node_type = CMARK_NODE_STRONG;
285
+ else if (type == sym_link)
286
+ node_type = CMARK_NODE_LINK;
287
+ else if (type == sym_image)
288
+ node_type = CMARK_NODE_IMAGE;
289
+ else if (type == sym_footnote_reference)
290
+ node_type = CMARK_NODE_FOOTNOTE_REFERENCE;
291
+ else if (type == sym_footnote_definition)
292
+ node_type = CMARK_NODE_FOOTNOTE_DEFINITION;
293
+ else
294
+ rb_raise(rb_eNodeError, "invalid node of type %d", node_type);
295
+
296
+ node = cmark_node_new(node_type);
297
+ if (node == NULL) {
298
+ rb_raise(rb_eNodeError, "could not create node of type %d", node_type);
299
+ }
300
+
301
+ return rb_node_to_value(node);
302
+ }
303
+
304
+ /*
305
+ * Internal: Parses a Markdown string into a document.
306
+ *
307
+ */
308
+ static VALUE rb_parse_document(VALUE self, VALUE rb_text, VALUE rb_len,
309
+ VALUE rb_options, VALUE rb_extensions) {
310
+ char *text;
311
+ int len, options;
312
+ cmark_parser *parser;
313
+ cmark_node *doc;
314
+ Check_Type(rb_text, T_STRING);
315
+ Check_Type(rb_len, T_FIXNUM);
316
+ Check_Type(rb_options, T_FIXNUM);
317
+
318
+ parser = prepare_parser(rb_options, rb_extensions, cmark_get_default_mem_allocator());
319
+
320
+ text = (char *)RSTRING_PTR(rb_text);
321
+ len = FIX2INT(rb_len);
322
+ options = FIX2INT(rb_options);
323
+
324
+ cmark_parser_feed(parser, text, len);
325
+ doc = cmark_parser_finish(parser);
326
+ if (doc == NULL) {
327
+ rb_raise(rb_eNodeError, "error parsing document");
328
+ }
329
+ cmark_parser_free(parser);
330
+
331
+ return rb_node_to_value(doc);
332
+ }
333
+
334
+ /*
335
+ * Public: Fetch the string contents of the node.
336
+ *
337
+ * Returns a {String}.
338
+ */
339
+ static VALUE rb_node_get_string_content(VALUE self) {
340
+ const char *text;
341
+ cmark_node *node;
342
+ Data_Get_Struct(self, cmark_node, node);
343
+
344
+ text = cmark_node_get_literal(node);
345
+ if (text == NULL) {
346
+ rb_raise(rb_eNodeError, "could not get string content");
347
+ }
348
+
349
+ return encode_utf8_string(text);
350
+ }
351
+
352
+ /*
353
+ * Public: Sets the string content of the node.
354
+ *
355
+ * string - A {String} containing new content.
356
+ *
357
+ * Raises NodeError if the string content can't be set.
358
+ */
359
+ static VALUE rb_node_set_string_content(VALUE self, VALUE s) {
360
+ char *text;
361
+ cmark_node *node;
362
+ Check_Type(s, T_STRING);
363
+
364
+ Data_Get_Struct(self, cmark_node, node);
365
+ text = StringValueCStr(s);
366
+
367
+ if (!cmark_node_set_literal(node, text)) {
368
+ rb_raise(rb_eNodeError, "could not set string content");
369
+ }
370
+
371
+ return Qnil;
372
+ }
373
+
374
+ /*
375
+ * Public: Fetches the list type of the node.
376
+ *
377
+ * Returns a {Symbol} representing the node's type.
378
+ */
379
+ static VALUE rb_node_get_type(VALUE self) {
380
+ int node_type;
381
+ cmark_node *node;
382
+ VALUE symbol;
383
+ const char *s;
384
+
385
+ Data_Get_Struct(self, cmark_node, node);
386
+
387
+ node_type = cmark_node_get_type(node);
388
+ symbol = Qnil;
389
+
390
+ switch (node_type) {
391
+ case CMARK_NODE_DOCUMENT:
392
+ symbol = sym_document;
393
+ break;
394
+ case CMARK_NODE_BLOCK_QUOTE:
395
+ symbol = sym_blockquote;
396
+ break;
397
+ case CMARK_NODE_LIST:
398
+ symbol = sym_list;
399
+ break;
400
+ case CMARK_NODE_ITEM:
401
+ symbol = sym_list_item;
402
+ break;
403
+ case CMARK_NODE_CODE_BLOCK:
404
+ symbol = sym_code_block;
405
+ break;
406
+ case CMARK_NODE_HTML:
407
+ symbol = sym_html;
408
+ break;
409
+ case CMARK_NODE_PARAGRAPH:
410
+ symbol = sym_paragraph;
411
+ break;
412
+ case CMARK_NODE_HEADER:
413
+ symbol = sym_header;
414
+ break;
415
+ case CMARK_NODE_HRULE:
416
+ symbol = sym_hrule;
417
+ break;
418
+ case CMARK_NODE_TEXT:
419
+ symbol = sym_text;
420
+ break;
421
+ case CMARK_NODE_SOFTBREAK:
422
+ symbol = sym_softbreak;
423
+ break;
424
+ case CMARK_NODE_LINEBREAK:
425
+ symbol = sym_linebreak;
426
+ break;
427
+ case CMARK_NODE_CODE:
428
+ symbol = sym_code;
429
+ break;
430
+ case CMARK_NODE_INLINE_HTML:
431
+ symbol = sym_inline_html;
432
+ break;
433
+ case CMARK_NODE_EMPH:
434
+ symbol = sym_emph;
435
+ break;
436
+ case CMARK_NODE_STRONG:
437
+ symbol = sym_strong;
438
+ break;
439
+ case CMARK_NODE_LINK:
440
+ symbol = sym_link;
441
+ break;
442
+ case CMARK_NODE_IMAGE:
443
+ symbol = sym_image;
444
+ break;
445
+ case CMARK_NODE_FOOTNOTE_REFERENCE:
446
+ symbol = sym_footnote_reference;
447
+ break;
448
+ case CMARK_NODE_FOOTNOTE_DEFINITION:
449
+ symbol = sym_footnote_definition;
450
+ break;
451
+ default:
452
+ if (node->extension) {
453
+ s = node->extension->get_type_string_func(node->extension, node);
454
+ return ID2SYM(rb_intern(s));
455
+ }
456
+ rb_raise(rb_eNodeError, "invalid node type %d", node_type);
457
+ }
458
+
459
+ return symbol;
460
+ }
461
+
462
+ /*
463
+ * Public: Fetches the sourcepos of the node.
464
+ *
465
+ * Returns a {Hash} containing {Symbol} keys of the positions.
466
+ */
467
+ static VALUE rb_node_get_sourcepos(VALUE self) {
468
+ int start_line, start_column, end_line, end_column;
469
+ VALUE result;
470
+
471
+ cmark_node *node;
472
+ Data_Get_Struct(self, cmark_node, node);
473
+
474
+ start_line = cmark_node_get_start_line(node);
475
+ start_column = cmark_node_get_start_column(node);
476
+ end_line = cmark_node_get_end_line(node);
477
+ end_column = cmark_node_get_end_column(node);
478
+
479
+ result = rb_hash_new();
480
+ rb_hash_aset(result, CSTR2SYM("start_line"), INT2NUM(start_line));
481
+ rb_hash_aset(result, CSTR2SYM("start_column"), INT2NUM(start_column));
482
+ rb_hash_aset(result, CSTR2SYM("end_line"), INT2NUM(end_line));
483
+ rb_hash_aset(result, CSTR2SYM("end_column"), INT2NUM(end_column));
484
+
485
+ return result;
486
+ }
487
+
488
+ /*
489
+ * Public: Returns the type of the current pointer as a string.
490
+ *
491
+ * Returns a {String}.
492
+ */
493
+ static VALUE rb_node_get_type_string(VALUE self) {
494
+ cmark_node *node;
495
+ Data_Get_Struct(self, cmark_node, node);
496
+
497
+ return rb_str_new2(cmark_node_get_type_string(node));
498
+ }
499
+
500
+ /*
501
+ * Internal: Unlinks the node from the tree (fixing pointers in
502
+ * parents and siblings appropriately).
503
+ */
504
+ static VALUE rb_node_unlink(VALUE self) {
505
+ cmark_node *node;
506
+ Data_Get_Struct(self, cmark_node, node);
507
+
508
+ cmark_node_unlink(node);
509
+
510
+ rb_parent_removed(self);
511
+
512
+ return Qnil;
513
+ }
514
+
515
+ /* Public: Fetches the first child of the node.
516
+ *
517
+ * Returns a {Node} if a child exists, `nil` otherise.
518
+ */
519
+ static VALUE rb_node_first_child(VALUE self) {
520
+ cmark_node *node, *child;
521
+ Data_Get_Struct(self, cmark_node, node);
522
+
523
+ child = cmark_node_first_child(node);
524
+
525
+ return rb_node_to_value(child);
526
+ }
527
+
528
+ /* Public: Fetches the next sibling of the node.
529
+ *
530
+ * Returns a {Node} if a sibling exists, `nil` otherwise.
531
+ */
532
+ static VALUE rb_node_next(VALUE self) {
533
+ cmark_node *node, *next;
534
+ Data_Get_Struct(self, cmark_node, node);
535
+
536
+ next = cmark_node_next(node);
537
+
538
+ return rb_node_to_value(next);
539
+ }
540
+
541
+ /*
542
+ * Public: Inserts a node as a sibling before the current node.
543
+ *
544
+ * sibling - A sibling {Node} to insert.
545
+ *
546
+ * Returns `true` if successful.
547
+ * Raises NodeError if the node can't be inserted.
548
+ */
549
+ static VALUE rb_node_insert_before(VALUE self, VALUE sibling) {
550
+ cmark_node *node1, *node2;
551
+ Data_Get_Struct(self, cmark_node, node1);
552
+
553
+ Data_Get_Struct(sibling, cmark_node, node2);
554
+
555
+ if (!cmark_node_insert_before(node1, node2)) {
556
+ rb_raise(rb_eNodeError, "could not insert before");
557
+ }
558
+
559
+ rb_parent_added(sibling);
560
+
561
+ return Qtrue;
562
+ }
563
+
564
+ /* Internal: Convert the node to an HTML string.
565
+ *
566
+ * Returns a {String}.
567
+ */
568
+ static VALUE rb_render_html(VALUE self, VALUE rb_options, VALUE rb_extensions) {
569
+ int options, extensions_len;
570
+ VALUE rb_ext_name;
571
+ int i;
572
+ cmark_node *node;
573
+ cmark_llist *extensions = NULL;
574
+ cmark_mem *mem = cmark_get_default_mem_allocator();
575
+ Check_Type(rb_options, T_FIXNUM);
576
+ Check_Type(rb_extensions, T_ARRAY);
577
+
578
+ options = FIX2INT(rb_options);
579
+ extensions_len = RARRAY_LEN(rb_extensions);
580
+
581
+ Data_Get_Struct(self, cmark_node, node);
582
+
583
+ for (i = 0; i < extensions_len; ++i) {
584
+ rb_ext_name = RARRAY_PTR(rb_extensions)[i];
585
+
586
+ if (!SYMBOL_P(rb_ext_name)) {
587
+ cmark_llist_free(mem, extensions);
588
+ rb_raise(rb_eTypeError, "extension names should be Symbols; got a %"PRIsVALUE"", rb_obj_class(rb_ext_name));
589
+ }
590
+
591
+ cmark_syntax_extension *syntax_extension =
592
+ cmark_find_syntax_extension(rb_id2name(SYM2ID(rb_ext_name)));
593
+
594
+ if (!syntax_extension) {
595
+ cmark_llist_free(mem, extensions);
596
+ rb_raise(rb_eArgError, "extension %s not found\n", rb_id2name(SYM2ID(rb_ext_name)));
597
+ }
598
+
599
+ extensions = cmark_llist_append(mem, extensions, syntax_extension);
600
+ }
601
+
602
+ char *html = cmark_render_html(node, options, extensions);
603
+ VALUE ruby_html = rb_str_new2(html);
604
+
605
+ cmark_llist_free(mem, extensions);
606
+ free(html);
607
+
608
+ return ruby_html;
609
+ }
610
+
611
+ /* Internal: Convert the node to an XML string.
612
+ *
613
+ * Returns a {String}.
614
+ */
615
+ static VALUE rb_render_xml(VALUE self, VALUE rb_options) {
616
+ int options;
617
+ int i;
618
+ cmark_node *node;
619
+ Check_Type(rb_options, T_FIXNUM);
620
+
621
+ options = FIX2INT(rb_options);
622
+
623
+ Data_Get_Struct(self, cmark_node, node);
624
+
625
+ char *xml = cmark_render_xml(node, options);
626
+ VALUE ruby_xml = rb_str_new2(xml);
627
+
628
+ free(xml);
629
+
630
+ return ruby_xml;
631
+ }
632
+
633
+ /* Internal: Convert the node to a CommonMark string.
634
+ *
635
+ * Returns a {String}.
636
+ */
637
+ static VALUE rb_render_commonmark(int argc, VALUE *argv, VALUE self) {
638
+ VALUE rb_options, rb_width;
639
+ rb_scan_args(argc, argv, "11", &rb_options, &rb_width);
640
+
641
+ int width = 120;
642
+ if (!NIL_P(rb_width)) {
643
+ Check_Type(rb_width, T_FIXNUM);
644
+ width = FIX2INT(rb_width);
645
+ }
646
+
647
+ int options;
648
+ cmark_node *node;
649
+ Check_Type(rb_options, T_FIXNUM);
650
+
651
+ options = FIX2INT(rb_options);
652
+ Data_Get_Struct(self, cmark_node, node);
653
+
654
+ char *cmark = cmark_render_commonmark(node, options, width);
655
+ VALUE ruby_cmark = rb_str_new2(cmark);
656
+ free(cmark);
657
+
658
+ return ruby_cmark;
659
+ }
660
+
661
+ /* Internal: Convert the node to a plain textstring.
662
+ *
663
+ * Returns a {String}.
664
+ */
665
+ static VALUE rb_render_plaintext(int argc, VALUE *argv, VALUE self) {
666
+ VALUE rb_options, rb_width;
667
+ rb_scan_args(argc, argv, "11", &rb_options, &rb_width);
668
+
669
+ int width = 120;
670
+ if (!NIL_P(rb_width)) {
671
+ Check_Type(rb_width, T_FIXNUM);
672
+ width = FIX2INT(rb_width);
673
+ }
674
+
675
+ int options;
676
+ cmark_node *node;
677
+ Check_Type(rb_options, T_FIXNUM);
678
+
679
+ options = FIX2INT(rb_options);
680
+ Data_Get_Struct(self, cmark_node, node);
681
+
682
+ char *text = cmark_render_plaintext(node, options, width);
683
+ VALUE ruby_text = rb_str_new2(text);
684
+ free(text);
685
+
686
+ return ruby_text;
687
+ }
688
+
689
+ /*
690
+ * Public: Inserts a node as a sibling after the current node.
691
+ *
692
+ * sibling - A sibling {Node} to insert.
693
+ *
694
+ * Returns `true` if successful.
695
+ * Raises NodeError if the node can't be inserted.
696
+ */
697
+ static VALUE rb_node_insert_after(VALUE self, VALUE sibling) {
698
+ cmark_node *node1, *node2;
699
+ Data_Get_Struct(self, cmark_node, node1);
700
+
701
+ Data_Get_Struct(sibling, cmark_node, node2);
702
+
703
+ if (!cmark_node_insert_after(node1, node2)) {
704
+ rb_raise(rb_eNodeError, "could not insert after");
705
+ }
706
+
707
+ rb_parent_added(sibling);
708
+
709
+ return Qtrue;
710
+ }
711
+
712
+ /*
713
+ * Public: Inserts a node as the first child of the current node.
714
+ *
715
+ * child - A child {Node} to insert.
716
+ *
717
+ * Returns `true` if successful.
718
+ * Raises NodeError if the node can't be inserted.
719
+ */
720
+ static VALUE rb_node_prepend_child(VALUE self, VALUE child) {
721
+ cmark_node *node1, *node2;
722
+ Data_Get_Struct(self, cmark_node, node1);
723
+
724
+ Data_Get_Struct(child, cmark_node, node2);
725
+
726
+ if (!cmark_node_prepend_child(node1, node2)) {
727
+ rb_raise(rb_eNodeError, "could not prepend child");
728
+ }
729
+
730
+ rb_parent_added(child);
731
+
732
+ return Qtrue;
733
+ }
734
+
735
+ /*
736
+ * Public: Inserts a node as the last child of the current node.
737
+ *
738
+ * child - A child {Node} to insert.
739
+ *
740
+ * Returns `true` if successful.
741
+ * Raises NodeError if the node can't be inserted.
742
+ */
743
+ static VALUE rb_node_append_child(VALUE self, VALUE child) {
744
+ cmark_node *node1, *node2;
745
+ Data_Get_Struct(self, cmark_node, node1);
746
+
747
+ Data_Get_Struct(child, cmark_node, node2);
748
+
749
+ if (!cmark_node_append_child(node1, node2)) {
750
+ rb_raise(rb_eNodeError, "could not append child");
751
+ }
752
+
753
+ rb_parent_added(child);
754
+
755
+ return Qtrue;
756
+ }
757
+
758
+ /* Public: Fetches the first child of the current node.
759
+ *
760
+ * Returns a {Node} if a child exists, `nil` otherise.
761
+ */
762
+ static VALUE rb_node_last_child(VALUE self) {
763
+ cmark_node *node, *child;
764
+ Data_Get_Struct(self, cmark_node, node);
765
+
766
+ child = cmark_node_last_child(node);
767
+
768
+ return rb_node_to_value(child);
769
+ }
770
+
771
+ /* Public: Fetches the parent of the current node.
772
+ *
773
+ * Returns a {Node} if a parent exists, `nil` otherise.
774
+ */
775
+ static VALUE rb_node_parent(VALUE self) {
776
+ cmark_node *node, *parent;
777
+ Data_Get_Struct(self, cmark_node, node);
778
+
779
+ parent = cmark_node_parent(node);
780
+
781
+ return rb_node_to_value(parent);
782
+ }
783
+
784
+ /* Public: Fetches the previous sibling of the current node.
785
+ *
786
+ * Returns a {Node} if a parent exists, `nil` otherise.
787
+ */
788
+ static VALUE rb_node_previous(VALUE self) {
789
+ cmark_node *node, *previous;
790
+ Data_Get_Struct(self, cmark_node, node);
791
+
792
+ previous = cmark_node_previous(node);
793
+
794
+ return rb_node_to_value(previous);
795
+ }
796
+
797
+ /*
798
+ * Public: Gets the URL of the current node (must be a `:link` or `:image`).
799
+ *
800
+ * Returns a {String}.
801
+ * Raises a NodeError if the URL can't be retrieved.
802
+ */
803
+ static VALUE rb_node_get_url(VALUE self) {
804
+ const char *text;
805
+ cmark_node *node;
806
+ Data_Get_Struct(self, cmark_node, node);
807
+
808
+ text = cmark_node_get_url(node);
809
+ if (text == NULL) {
810
+ rb_raise(rb_eNodeError, "could not get url");
811
+ }
812
+
813
+ return rb_str_new2(text);
814
+ }
815
+
816
+ /*
817
+ * Public: Sets the URL of the current node (must be a `:link` or `:image`).
818
+ *
819
+ * url - A {String} representing the new URL
820
+ *
821
+ * Raises a NodeError if the URL can't be set.
822
+ */
823
+ static VALUE rb_node_set_url(VALUE self, VALUE url) {
824
+ cmark_node *node;
825
+ char *text;
826
+ Check_Type(url, T_STRING);
827
+
828
+ Data_Get_Struct(self, cmark_node, node);
829
+ text = StringValueCStr(url);
830
+
831
+ if (!cmark_node_set_url(node, text)) {
832
+ rb_raise(rb_eNodeError, "could not set url");
833
+ }
834
+
835
+ return Qnil;
836
+ }
837
+
838
+ /*
839
+ * Public: Gets the title of the current node (must be a `:link` or `:image`).
840
+ *
841
+ * Returns a {String}.
842
+ * Raises a NodeError if the title can't be retrieved.
843
+ */
844
+ static VALUE rb_node_get_title(VALUE self) {
845
+ const char *text;
846
+ cmark_node *node;
847
+ Data_Get_Struct(self, cmark_node, node);
848
+
849
+ text = cmark_node_get_title(node);
850
+ if (text == NULL) {
851
+ rb_raise(rb_eNodeError, "could not get title");
852
+ }
853
+
854
+ return rb_str_new2(text);
855
+ }
856
+
857
+ /*
858
+ * Public: Sets the title of the current node (must be a `:link` or `:image`).
859
+ *
860
+ * title - A {String} representing the new title
861
+ *
862
+ * Raises a NodeError if the title can't be set.
863
+ */
864
+ static VALUE rb_node_set_title(VALUE self, VALUE title) {
865
+ char *text;
866
+ cmark_node *node;
867
+ Check_Type(title, T_STRING);
868
+
869
+ Data_Get_Struct(self, cmark_node, node);
870
+ text = StringValueCStr(title);
871
+
872
+ if (!cmark_node_set_title(node, text)) {
873
+ rb_raise(rb_eNodeError, "could not set title");
874
+ }
875
+
876
+ return Qnil;
877
+ }
878
+
879
+ /*
880
+ * Public: Gets the header level of the current node (must be a `:header`).
881
+ *
882
+ * Returns a {Number} representing the header level.
883
+ * Raises a NodeError if the header level can't be retrieved.
884
+ */
885
+ static VALUE rb_node_get_header_level(VALUE self) {
886
+ int header_level;
887
+ cmark_node *node;
888
+ Data_Get_Struct(self, cmark_node, node);
889
+
890
+ header_level = cmark_node_get_header_level(node);
891
+
892
+ if (header_level == 0) {
893
+ rb_raise(rb_eNodeError, "could not get header_level");
894
+ }
895
+
896
+ return INT2NUM(header_level);
897
+ }
898
+
899
+ /*
900
+ * Public: Sets the header level of the current node (must be a `:header`).
901
+ *
902
+ * level - A {Number} representing the new header level
903
+ *
904
+ * Raises a NodeError if the header level can't be set.
905
+ */
906
+ static VALUE rb_node_set_header_level(VALUE self, VALUE level) {
907
+ int l;
908
+ cmark_node *node;
909
+ Check_Type(level, T_FIXNUM);
910
+
911
+ Data_Get_Struct(self, cmark_node, node);
912
+ l = FIX2INT(level);
913
+
914
+ if (!cmark_node_set_header_level(node, l)) {
915
+ rb_raise(rb_eNodeError, "could not set header_level");
916
+ }
917
+
918
+ return Qnil;
919
+ }
920
+
921
+ /*
922
+ * Public: Gets the list type of the current node (must be a `:list`).
923
+ *
924
+ * Returns a {Symbol}.
925
+ * Raises a NodeError if the title can't be retrieved.
926
+ */
927
+ static VALUE rb_node_get_list_type(VALUE self) {
928
+ int list_type;
929
+ cmark_node *node;
930
+ VALUE symbol;
931
+ Data_Get_Struct(self, cmark_node, node);
932
+
933
+ list_type = cmark_node_get_list_type(node);
934
+
935
+ if (list_type == CMARK_BULLET_LIST) {
936
+ symbol = sym_bullet_list;
937
+ } else if (list_type == CMARK_ORDERED_LIST) {
938
+ symbol = sym_ordered_list;
939
+ } else {
940
+ rb_raise(rb_eNodeError, "could not get list_type");
941
+ }
942
+
943
+ return symbol;
944
+ }
945
+
946
+ /*
947
+ * Public: Sets the list type of the current node (must be a `:list`).
948
+ *
949
+ * level - A {Symbol} representing the new list type
950
+ *
951
+ * Raises a NodeError if the list type can't be set.
952
+ */
953
+ static VALUE rb_node_set_list_type(VALUE self, VALUE list_type) {
954
+ int type = 0;
955
+ cmark_node *node;
956
+ Check_Type(list_type, T_SYMBOL);
957
+
958
+ Data_Get_Struct(self, cmark_node, node);
959
+
960
+ if (list_type == sym_bullet_list) {
961
+ type = CMARK_BULLET_LIST;
962
+ } else if (list_type == sym_ordered_list) {
963
+ type = CMARK_ORDERED_LIST;
964
+ } else {
965
+ rb_raise(rb_eNodeError, "invalid list_type");
966
+ }
967
+
968
+ if (!cmark_node_set_list_type(node, type)) {
969
+ rb_raise(rb_eNodeError, "could not set list_type");
970
+ }
971
+
972
+ return Qnil;
973
+ }
974
+
975
+ /*
976
+ * Public: Gets the starting number the current node (must be an
977
+ * `:ordered_list`).
978
+ *
979
+ * Returns a {Number} representing the starting number.
980
+ * Raises a NodeError if the starting number can't be retrieved.
981
+ */
982
+ static VALUE rb_node_get_list_start(VALUE self) {
983
+ cmark_node *node;
984
+ Data_Get_Struct(self, cmark_node, node);
985
+
986
+ if (cmark_node_get_type(node) != CMARK_NODE_LIST ||
987
+ cmark_node_get_list_type(node) != CMARK_ORDERED_LIST) {
988
+ rb_raise(rb_eNodeError, "can't get list_start for non-ordered list %d",
989
+ cmark_node_get_list_type(node));
990
+ }
991
+
992
+ return INT2NUM(cmark_node_get_list_start(node));
993
+ }
994
+
995
+ /*
996
+ * Public: Sets the starting number of the current node (must be an
997
+ * `:ordered_list`).
998
+ *
999
+ * level - A {Number} representing the new starting number
1000
+ *
1001
+ * Raises a NodeError if the starting number can't be set.
1002
+ */
1003
+ static VALUE rb_node_set_list_start(VALUE self, VALUE start) {
1004
+ int s;
1005
+ cmark_node *node;
1006
+ Check_Type(start, T_FIXNUM);
1007
+
1008
+ Data_Get_Struct(self, cmark_node, node);
1009
+ s = FIX2INT(start);
1010
+
1011
+ if (!cmark_node_set_list_start(node, s)) {
1012
+ rb_raise(rb_eNodeError, "could not set list_start");
1013
+ }
1014
+
1015
+ return Qnil;
1016
+ }
1017
+
1018
+ /*
1019
+ * Public: Gets the tight status the current node (must be a `:list`).
1020
+ *
1021
+ * Returns a `true` if the list is tight, `false` otherwise.
1022
+ * Raises a NodeError if the starting number can't be retrieved.
1023
+ */
1024
+ static VALUE rb_node_get_list_tight(VALUE self) {
1025
+ int flag;
1026
+ cmark_node *node;
1027
+ Data_Get_Struct(self, cmark_node, node);
1028
+
1029
+ if (cmark_node_get_type(node) != CMARK_NODE_LIST) {
1030
+ rb_raise(rb_eNodeError, "can't get list_tight for non-list");
1031
+ }
1032
+
1033
+ flag = cmark_node_get_list_tight(node);
1034
+
1035
+ return flag ? Qtrue : Qfalse;
1036
+ }
1037
+
1038
+ /*
1039
+ * Public: Sets the tight status of the current node (must be a `:list`).
1040
+ *
1041
+ * tight - A {Boolean} representing the new tightness
1042
+ *
1043
+ * Raises a NodeError if the tightness can't be set.
1044
+ */
1045
+ static VALUE rb_node_set_list_tight(VALUE self, VALUE tight) {
1046
+ int t;
1047
+ cmark_node *node;
1048
+ Data_Get_Struct(self, cmark_node, node);
1049
+ t = RTEST(tight);
1050
+
1051
+ if (!cmark_node_set_list_tight(node, t)) {
1052
+ rb_raise(rb_eNodeError, "could not set list_tight");
1053
+ }
1054
+
1055
+ return Qnil;
1056
+ }
1057
+
1058
+ /*
1059
+ * Public: Gets the fence info of the current node (must be a `:code_block`).
1060
+ *
1061
+ * Returns a {String} representing the fence info.
1062
+ * Raises a NodeError if the fence info can't be retrieved.
1063
+ */
1064
+ static VALUE rb_node_get_fence_info(VALUE self) {
1065
+ const char *fence_info;
1066
+ cmark_node *node;
1067
+ Data_Get_Struct(self, cmark_node, node);
1068
+
1069
+ fence_info = cmark_node_get_fence_info(node);
1070
+
1071
+ if (fence_info == NULL) {
1072
+ rb_raise(rb_eNodeError, "could not get fence_info");
1073
+ }
1074
+
1075
+ return rb_str_new2(fence_info);
1076
+ }
1077
+
1078
+ /*
1079
+ * Public: Sets the fence info of the current node (must be a `:code_block`).
1080
+ *
1081
+ * info - A {String} representing the new fence info
1082
+ *
1083
+ * Raises a NodeError if the fence info can't be set.
1084
+ */
1085
+ static VALUE rb_node_set_fence_info(VALUE self, VALUE info) {
1086
+ char *text;
1087
+ cmark_node *node;
1088
+ Check_Type(info, T_STRING);
1089
+
1090
+ Data_Get_Struct(self, cmark_node, node);
1091
+ text = StringValueCStr(info);
1092
+
1093
+ if (!cmark_node_set_fence_info(node, text)) {
1094
+ rb_raise(rb_eNodeError, "could not set fence_info");
1095
+ }
1096
+
1097
+ return Qnil;
1098
+ }
1099
+
1100
+ static VALUE rb_node_get_tasklist_item_checked(VALUE self) {
1101
+ int tasklist_state;
1102
+ cmark_node *node;
1103
+ Data_Get_Struct(self, cmark_node, node);
1104
+
1105
+ tasklist_state = cmark_gfm_extensions_get_tasklist_item_checked(node);
1106
+
1107
+ if (tasklist_state == 1) {
1108
+ return Qtrue;
1109
+ } else {
1110
+ return Qfalse;
1111
+ }
1112
+ }
1113
+
1114
+ /*
1115
+ * Public: Sets the checkbox state of the current node (must be a `:tasklist`).
1116
+ *
1117
+ * item_checked - A {Boolean} representing the new checkbox state
1118
+ *
1119
+ * Returns a {Boolean} representing the new checkbox state.
1120
+ * Raises a NodeError if the checkbox state can't be set.
1121
+ */
1122
+ static VALUE rb_node_set_tasklist_item_checked(VALUE self, VALUE item_checked) {
1123
+ int tasklist_state;
1124
+ cmark_node *node;
1125
+ Data_Get_Struct(self, cmark_node, node);
1126
+ tasklist_state = RTEST(item_checked);
1127
+
1128
+ if (!cmark_gfm_extensions_set_tasklist_item_checked(node, tasklist_state)) {
1129
+ rb_raise(rb_eNodeError, "could not set tasklist_item_checked");
1130
+ };
1131
+
1132
+ if (tasklist_state) {
1133
+ return Qtrue;
1134
+ } else {
1135
+ return Qfalse;
1136
+ }
1137
+ }
1138
+
1139
+ // TODO: remove this, superseded by the above method
1140
+ static VALUE rb_node_get_tasklist_state(VALUE self) {
1141
+ int tasklist_state;
1142
+ cmark_node *node;
1143
+ Data_Get_Struct(self, cmark_node, node);
1144
+
1145
+ tasklist_state = cmark_gfm_extensions_get_tasklist_item_checked(node);
1146
+
1147
+ if (tasklist_state == 1) {
1148
+ return rb_str_new2("checked");
1149
+ } else {
1150
+ return rb_str_new2("unchecked");
1151
+ }
1152
+ }
1153
+
1154
+ static VALUE rb_node_get_table_alignments(VALUE self) {
1155
+ uint16_t column_count, i;
1156
+ uint8_t *alignments;
1157
+ cmark_node *node;
1158
+ VALUE ary;
1159
+ Data_Get_Struct(self, cmark_node, node);
1160
+
1161
+ column_count = cmark_gfm_extensions_get_table_columns(node);
1162
+ alignments = cmark_gfm_extensions_get_table_alignments(node);
1163
+
1164
+ if (!column_count || !alignments) {
1165
+ rb_raise(rb_eNodeError, "could not get column_count or alignments");
1166
+ }
1167
+
1168
+ ary = rb_ary_new();
1169
+ for (i = 0; i < column_count; ++i) {
1170
+ if (alignments[i] == 'l')
1171
+ rb_ary_push(ary, sym_left);
1172
+ else if (alignments[i] == 'c')
1173
+ rb_ary_push(ary, sym_center);
1174
+ else if (alignments[i] == 'r')
1175
+ rb_ary_push(ary, sym_right);
1176
+ else
1177
+ rb_ary_push(ary, Qnil);
1178
+ }
1179
+ return ary;
1180
+ }
1181
+
1182
+ /* Internal: Escapes href URLs safely. */
1183
+ static VALUE rb_html_escape_href(VALUE self, VALUE rb_text) {
1184
+ char *result;
1185
+ cmark_node *node;
1186
+ Check_Type(rb_text, T_STRING);
1187
+
1188
+ Data_Get_Struct(self, cmark_node, node);
1189
+
1190
+ cmark_mem *mem = cmark_node_mem(node);
1191
+ cmark_strbuf buf = CMARK_BUF_INIT(mem);
1192
+
1193
+ if (houdini_escape_href(&buf, (const uint8_t *)RSTRING_PTR(rb_text),
1194
+ RSTRING_LEN(rb_text))) {
1195
+ result = (char *)cmark_strbuf_detach(&buf);
1196
+ return encode_source_string(result, rb_text);
1197
+
1198
+ }
1199
+
1200
+ return rb_text;
1201
+ }
1202
+
1203
+ /* Internal: Escapes HTML content safely. */
1204
+ static VALUE rb_html_escape_html(VALUE self, VALUE rb_text) {
1205
+ char *result;
1206
+ cmark_node *node;
1207
+ Check_Type(rb_text, T_STRING);
1208
+
1209
+ Data_Get_Struct(self, cmark_node, node);
1210
+
1211
+ cmark_mem *mem = cmark_node_mem(node);
1212
+ cmark_strbuf buf = CMARK_BUF_INIT(mem);
1213
+
1214
+ if (houdini_escape_html0(&buf, (const uint8_t *)RSTRING_PTR(rb_text),
1215
+ RSTRING_LEN(rb_text), 0)) {
1216
+ result = (char *)cmark_strbuf_detach(&buf);
1217
+ return encode_source_string(result, rb_text);
1218
+ }
1219
+
1220
+ return rb_text;
1221
+ }
1222
+
1223
+ VALUE rb_extensions(VALUE self) {
1224
+ cmark_llist *exts, *it;
1225
+ cmark_syntax_extension *ext;
1226
+ VALUE ary = rb_ary_new();
1227
+
1228
+ cmark_mem *mem = cmark_get_default_mem_allocator();
1229
+ exts = cmark_list_syntax_extensions(mem);
1230
+ for (it = exts; it; it = it->next) {
1231
+ ext = it->data;
1232
+ rb_ary_push(ary, rb_str_new2(ext->name));
1233
+ }
1234
+ cmark_llist_free(mem, exts);
1235
+
1236
+ return ary;
1237
+ }
1238
+
1239
+ __attribute__((visibility("default"))) void Init_qiita_marker() {
1240
+ VALUE module;
1241
+ sym_document = ID2SYM(rb_intern("document"));
1242
+ sym_blockquote = ID2SYM(rb_intern("blockquote"));
1243
+ sym_list = ID2SYM(rb_intern("list"));
1244
+ sym_list_item = ID2SYM(rb_intern("list_item"));
1245
+ sym_code_block = ID2SYM(rb_intern("code_block"));
1246
+ sym_html = ID2SYM(rb_intern("html"));
1247
+ sym_paragraph = ID2SYM(rb_intern("paragraph"));
1248
+ sym_header = ID2SYM(rb_intern("header"));
1249
+ sym_hrule = ID2SYM(rb_intern("hrule"));
1250
+ sym_text = ID2SYM(rb_intern("text"));
1251
+ sym_softbreak = ID2SYM(rb_intern("softbreak"));
1252
+ sym_linebreak = ID2SYM(rb_intern("linebreak"));
1253
+ sym_code = ID2SYM(rb_intern("code"));
1254
+ sym_inline_html = ID2SYM(rb_intern("inline_html"));
1255
+ sym_emph = ID2SYM(rb_intern("emph"));
1256
+ sym_strong = ID2SYM(rb_intern("strong"));
1257
+ sym_link = ID2SYM(rb_intern("link"));
1258
+ sym_image = ID2SYM(rb_intern("image"));
1259
+ sym_footnote_reference = ID2SYM(rb_intern("footnote_reference"));
1260
+ sym_footnote_definition = ID2SYM(rb_intern("footnote_definition"));
1261
+
1262
+ sym_bullet_list = ID2SYM(rb_intern("bullet_list"));
1263
+ sym_ordered_list = ID2SYM(rb_intern("ordered_list"));
1264
+
1265
+ sym_left = ID2SYM(rb_intern("left"));
1266
+ sym_right = ID2SYM(rb_intern("right"));
1267
+ sym_center = ID2SYM(rb_intern("center"));
1268
+
1269
+ module = rb_define_module("QiitaMarker");
1270
+ rb_define_singleton_method(module, "extensions", rb_extensions, 0);
1271
+ rb_eNodeError = rb_define_class_under(module, "NodeError", rb_eStandardError);
1272
+ rb_cNode = rb_define_class_under(module, "Node", rb_cObject);
1273
+ rb_define_singleton_method(rb_cNode, "markdown_to_html", rb_markdown_to_html,
1274
+ 3);
1275
+ rb_define_singleton_method(rb_cNode, "markdown_to_xml", rb_markdown_to_xml,
1276
+ 3);
1277
+ rb_define_singleton_method(rb_cNode, "new", rb_node_new, 1);
1278
+ rb_define_singleton_method(rb_cNode, "parse_document", rb_parse_document, 4);
1279
+ rb_define_method(rb_cNode, "string_content", rb_node_get_string_content, 0);
1280
+ rb_define_method(rb_cNode, "string_content=", rb_node_set_string_content, 1);
1281
+ rb_define_method(rb_cNode, "type", rb_node_get_type, 0);
1282
+ rb_define_method(rb_cNode, "type_string", rb_node_get_type_string, 0);
1283
+ rb_define_method(rb_cNode, "sourcepos", rb_node_get_sourcepos, 0);
1284
+ rb_define_method(rb_cNode, "delete", rb_node_unlink, 0);
1285
+ rb_define_method(rb_cNode, "first_child", rb_node_first_child, 0);
1286
+ rb_define_method(rb_cNode, "next", rb_node_next, 0);
1287
+ rb_define_method(rb_cNode, "insert_before", rb_node_insert_before, 1);
1288
+ rb_define_method(rb_cNode, "_render_html", rb_render_html, 2);
1289
+ rb_define_method(rb_cNode, "_render_xml", rb_render_xml, 1);
1290
+ rb_define_method(rb_cNode, "_render_commonmark", rb_render_commonmark, -1);
1291
+ rb_define_method(rb_cNode, "_render_plaintext", rb_render_plaintext, -1);
1292
+ rb_define_method(rb_cNode, "insert_after", rb_node_insert_after, 1);
1293
+ rb_define_method(rb_cNode, "prepend_child", rb_node_prepend_child, 1);
1294
+ rb_define_method(rb_cNode, "append_child", rb_node_append_child, 1);
1295
+ rb_define_method(rb_cNode, "last_child", rb_node_last_child, 0);
1296
+ rb_define_method(rb_cNode, "parent", rb_node_parent, 0);
1297
+ rb_define_method(rb_cNode, "previous", rb_node_previous, 0);
1298
+ rb_define_method(rb_cNode, "url", rb_node_get_url, 0);
1299
+ rb_define_method(rb_cNode, "url=", rb_node_set_url, 1);
1300
+ rb_define_method(rb_cNode, "title", rb_node_get_title, 0);
1301
+ rb_define_method(rb_cNode, "title=", rb_node_set_title, 1);
1302
+ rb_define_method(rb_cNode, "header_level", rb_node_get_header_level, 0);
1303
+ rb_define_method(rb_cNode, "header_level=", rb_node_set_header_level, 1);
1304
+ rb_define_method(rb_cNode, "list_type", rb_node_get_list_type, 0);
1305
+ rb_define_method(rb_cNode, "list_type=", rb_node_set_list_type, 1);
1306
+ rb_define_method(rb_cNode, "list_start", rb_node_get_list_start, 0);
1307
+ rb_define_method(rb_cNode, "list_start=", rb_node_set_list_start, 1);
1308
+ rb_define_method(rb_cNode, "list_tight", rb_node_get_list_tight, 0);
1309
+ rb_define_method(rb_cNode, "list_tight=", rb_node_set_list_tight, 1);
1310
+ rb_define_method(rb_cNode, "fence_info", rb_node_get_fence_info, 0);
1311
+ rb_define_method(rb_cNode, "fence_info=", rb_node_set_fence_info, 1);
1312
+ rb_define_method(rb_cNode, "table_alignments", rb_node_get_table_alignments, 0);
1313
+ rb_define_method(rb_cNode, "tasklist_state", rb_node_get_tasklist_state, 0);
1314
+ rb_define_method(rb_cNode, "tasklist_item_checked?", rb_node_get_tasklist_item_checked, 0);
1315
+ rb_define_method(rb_cNode, "tasklist_item_checked=", rb_node_set_tasklist_item_checked, 1);
1316
+
1317
+ rb_define_method(rb_cNode, "html_escape_href", rb_html_escape_href, 1);
1318
+ rb_define_method(rb_cNode, "html_escape_html", rb_html_escape_html, 1);
1319
+
1320
+ cmark_gfm_core_extensions_ensure_registered();
1321
+ }