commonmarker 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of commonmarker might be problematic. Click here for more details.

Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -1
  3. data/Rakefile +3 -3
  4. data/ext/commonmarker/cmark/build/CMakeCache.txt +8 -8
  5. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CMakeCCompiler.cmake +0 -0
  6. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CMakeCXXCompiler.cmake +0 -0
  7. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CMakeDetermineCompilerABI_C.bin +0 -0
  8. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CMakeDetermineCompilerABI_CXX.bin +0 -0
  9. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CMakeSystem.cmake +0 -0
  10. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CompilerIdC/CMakeCCompilerId.c +0 -0
  11. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CompilerIdC/a.out +0 -0
  12. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -0
  13. data/ext/commonmarker/cmark/build/CMakeFiles/{3.5.1 → 3.5.2}/CompilerIdCXX/a.out +0 -0
  14. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeError.log +6 -6
  15. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeOutput.log +143 -143
  16. data/ext/commonmarker/cmark/build/CMakeFiles/Makefile.cmake +106 -106
  17. data/ext/commonmarker/cmark/build/CMakeFiles/Makefile2 +2 -2
  18. data/ext/commonmarker/cmark/build/Makefile +9 -9
  19. data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/build.make +2 -2
  20. data/ext/commonmarker/cmark/build/api_test/Makefile +9 -9
  21. data/ext/commonmarker/cmark/build/man/Makefile +9 -9
  22. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/build.make +2 -2
  23. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/build.make +2 -2
  24. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/build.make +2 -2
  25. data/ext/commonmarker/cmark/build/src/Makefile +9 -9
  26. data/ext/commonmarker/cmark/build/src/libcmark.a +0 -0
  27. data/ext/commonmarker/cmark/build/testdir/Makefile +9 -9
  28. data/ext/commonmarker/commonmarker.c +579 -662
  29. data/ext/commonmarker/commonmarker.c.orig +1041 -0
  30. data/ext/commonmarker/commonmarker.h +1 -1
  31. data/lib/commonmarker.rb +1 -32
  32. data/lib/commonmarker/node.rb +45 -0
  33. data/lib/commonmarker/renderer.rb +2 -2
  34. data/lib/commonmarker/version.rb +1 -1
  35. data/test/test_basics.rb +1 -40
  36. data/test/test_node.rb +78 -0
  37. data/test/test_renderer.rb +13 -0
  38. metadata +17 -11
@@ -0,0 +1,1041 @@
1
+ #include "commonmarker.h"
2
+ #include "cmark.h"
3
+ #include "node.h"
4
+ #include "houdini.h"
5
+
6
+ static VALUE rb_mNodeError;
7
+ static VALUE rb_mNode;
8
+
9
+ static VALUE sym_document;
10
+ static VALUE sym_blockquote;
11
+ static VALUE sym_list;
12
+ static VALUE sym_list_item;
13
+ static VALUE sym_code_block;
14
+ static VALUE sym_html;
15
+ static VALUE sym_paragraph;
16
+ static VALUE sym_header;
17
+ static VALUE sym_hrule;
18
+ static VALUE sym_text;
19
+ static VALUE sym_softbreak;
20
+ static VALUE sym_linebreak;
21
+ static VALUE sym_code;
22
+ static VALUE sym_inline_html;
23
+ static VALUE sym_emph;
24
+ static VALUE sym_strong;
25
+ static VALUE sym_link;
26
+ static VALUE sym_image;
27
+
28
+ static VALUE sym_bullet_list;
29
+ static VALUE sym_ordered_list;
30
+
31
+ static void
32
+ rb_mark_c_struct(void *data)
33
+ {
34
+ cmark_node *node = data;
35
+ cmark_node *child;
36
+
37
+ /* Mark the parent to make sure that the tree won't be freed as
38
+ long as a child node is referenced. */
39
+ cmark_node *parent = cmark_node_parent(node);
40
+ if (parent) {
41
+ void *user_data = cmark_node_get_user_data(parent);
42
+ if (!user_data) {
43
+ /* This should never happen. Child can nodes can only
44
+ be returned from parents that already are
45
+ associated with a Ruby object. */
46
+ fprintf(stderr, "parent without user_data\n");
47
+ abort();
48
+ }
49
+ rb_gc_mark((VALUE)user_data);
50
+ }
51
+
52
+ /* Mark all children to make sure their cached Ruby objects won't
53
+ be freed. */
54
+ for (child = cmark_node_first_child(node);
55
+ child != NULL;
56
+ child = cmark_node_next(child)
57
+ ) {
58
+ void *user_data = cmark_node_get_user_data(child);
59
+ if (user_data)
60
+ rb_gc_mark((VALUE)user_data);
61
+ }
62
+ }
63
+
64
+ static void
65
+ rb_free_c_struct(void *data)
66
+ {
67
+ /* It's important that the `free` function does not inspect the
68
+ node data, as it may be part of a tree that was already freed. */
69
+ cmark_node_free(data);
70
+ }
71
+
72
+ static VALUE
73
+ rb_node_to_value(cmark_node *node)
74
+ {
75
+ void *user_data;
76
+ RUBY_DATA_FUNC free_func;
77
+ VALUE val;
78
+
79
+ if (node == NULL)
80
+ return Qnil;
81
+
82
+ user_data = cmark_node_get_user_data(node);
83
+ if (user_data)
84
+ return (VALUE)user_data;
85
+
86
+ /* Only free tree roots. */
87
+ free_func = cmark_node_parent(node)
88
+ ? NULL
89
+ : rb_free_c_struct;
90
+ val = Data_Wrap_Struct(rb_mNode, rb_mark_c_struct, free_func,
91
+ node);
92
+ cmark_node_set_user_data(node, (void *)val);
93
+
94
+ return val;
95
+ }
96
+
97
+ /* If the node structure is changed, the finalizers must be updated. */
98
+
99
+ static void
100
+ rb_parent_added(VALUE val)
101
+ {
102
+ RDATA(val)->dfree = NULL;
103
+ }
104
+
105
+ static void
106
+ rb_parent_removed(VALUE val)
107
+ {
108
+ RDATA(val)->dfree = rb_free_c_struct;
109
+ }
110
+
111
+ /*
112
+ * Internal: Parses a Markdown string into an HTML string.
113
+ *
114
+ */
115
+ static VALUE
116
+ rb_markdown_to_html(VALUE self, VALUE rb_text, VALUE rb_options)
117
+ {
118
+ char *str;
119
+ int len, options;
120
+
121
+ Check_Type(rb_text, T_STRING);
122
+ Check_Type(rb_options, T_FIXNUM);
123
+
124
+ str = (char *)RSTRING_PTR(rb_text);
125
+ len = RSTRING_LEN(rb_text);
126
+ options = FIX2INT(rb_options);
127
+
128
+ return rb_str_new2(cmark_markdown_to_html(str, len, options));
129
+ }
130
+
131
+ /*
132
+ * Internal: Creates a node based on a node type.
133
+ *
134
+ * type - A {Symbol} representing the node to be created. Must be one of the following:
135
+ * - `:document`
136
+ * - `:blockquote`
137
+ * - `:list`
138
+ * - `:list_item`
139
+ * - `:code_block`
140
+ * - `:html`
141
+ * - `:paragraph`
142
+ * - `:header`
143
+ * - `:hrule`
144
+ * - `:text`
145
+ * - `:softbreak`
146
+ * - `:linebreak`
147
+ * - `:code`
148
+ * - `:inline_html`
149
+ * - `:emph`
150
+ * - `:strong`
151
+ * - `:link`
152
+ * - `:image`
153
+ */
154
+ static VALUE
155
+ rb_node_new(VALUE self, VALUE type)
156
+ {
157
+ cmark_node_type node_type = 0;
158
+ cmark_node *node;
159
+
160
+ Check_Type(type, T_SYMBOL);
161
+
162
+ if (type == sym_document)
163
+ node_type = CMARK_NODE_DOCUMENT;
164
+ else if (type == sym_blockquote)
165
+ node_type = CMARK_NODE_BLOCK_QUOTE;
166
+ else if (type == sym_list)
167
+ node_type = CMARK_NODE_LIST;
168
+ else if (type == sym_list_item)
169
+ node_type = CMARK_NODE_ITEM;
170
+ else if (type == sym_code_block)
171
+ node_type = CMARK_NODE_CODE_BLOCK;
172
+ else if (type == sym_html)
173
+ node_type = CMARK_NODE_HTML;
174
+ else if (type == sym_paragraph)
175
+ node_type = CMARK_NODE_PARAGRAPH;
176
+ else if (type == sym_header)
177
+ node_type = CMARK_NODE_HEADER;
178
+ else if (type == sym_hrule)
179
+ node_type = CMARK_NODE_HRULE;
180
+ else if (type == sym_text)
181
+ node_type = CMARK_NODE_TEXT;
182
+ else if (type == sym_softbreak)
183
+ node_type = CMARK_NODE_SOFTBREAK;
184
+ else if (type == sym_linebreak)
185
+ node_type = CMARK_NODE_LINEBREAK;
186
+ else if (type == sym_code)
187
+ node_type = CMARK_NODE_CODE;
188
+ else if (type == sym_inline_html)
189
+ node_type = CMARK_NODE_INLINE_HTML;
190
+ else if (type == sym_emph)
191
+ node_type = CMARK_NODE_EMPH;
192
+ else if (type == sym_strong)
193
+ node_type = CMARK_NODE_STRONG;
194
+ else if (type == sym_link)
195
+ node_type = CMARK_NODE_LINK;
196
+ else if (type == sym_image)
197
+ node_type = CMARK_NODE_IMAGE;
198
+ else
199
+ rb_raise(rb_mNodeError, "invalid node of type %d", node_type);
200
+
201
+ node = cmark_node_new(node_type);
202
+ if (node == NULL) {
203
+ rb_raise(rb_mNodeError, "could not create node of type %d", node_type);
204
+ }
205
+
206
+ return rb_node_to_value(node);
207
+ }
208
+
209
+ /*
210
+ * Internal: Parses a Markdown string into a document.
211
+ *
212
+ */
213
+ static VALUE
214
+ rb_parse_document(VALUE self, VALUE rb_text, VALUE rb_len, VALUE rb_options)
215
+ {
216
+ char *text;
217
+ int len, options;
218
+ cmark_node *doc;
219
+ Check_Type(rb_text, T_STRING);
220
+ Check_Type(rb_len, T_FIXNUM);
221
+ Check_Type(rb_options, T_FIXNUM);
222
+
223
+ text = (char *)RSTRING_PTR(rb_text);
224
+ len = FIX2INT(rb_len);
225
+ options = FIX2INT(rb_options);
226
+
227
+ doc = cmark_parse_document(text, len, options);
228
+ if (doc == NULL) {
229
+ rb_raise(rb_mNodeError, "error parsing document");
230
+ }
231
+
232
+ return rb_node_to_value(doc);
233
+ }
234
+
235
+ /*
236
+ * Public: Fetch the string contents of the node.
237
+ *
238
+ * Returns a {String}.
239
+ */
240
+ static VALUE
241
+ rb_node_get_string_content(VALUE self)
242
+ {
243
+ const char *text;
244
+ cmark_node *node;
245
+ Data_Get_Struct(self, cmark_node, node);
246
+
247
+ text = cmark_node_get_literal(node);
248
+ if (text == NULL) {
249
+ rb_raise(rb_mNodeError, "could not get string content");
250
+ }
251
+
252
+ return rb_str_new2(text);
253
+ }
254
+
255
+ /*
256
+ * Public: Sets the string content of the node.
257
+ *
258
+ * string - A {String} containing new content.
259
+ *
260
+ * Raises NodeError if the string content can't be set.
261
+ */
262
+ static VALUE
263
+ rb_node_set_string_content(VALUE self, VALUE s)
264
+ {
265
+ char *text;
266
+ cmark_node *node;
267
+ Check_Type(s, T_STRING);
268
+
269
+ Data_Get_Struct(self, cmark_node, node);
270
+ text = StringValueCStr(s);
271
+
272
+ if (!cmark_node_set_literal(node, text)) {
273
+ rb_raise(rb_mNodeError, "could not set string content");
274
+ }
275
+
276
+ return Qnil;
277
+ }
278
+
279
+ /*
280
+ * Public: Fetches the list type of the node.
281
+ *
282
+ * Returns a {Symbol} representing the node's type.
283
+ */
284
+ static VALUE
285
+ rb_node_get_type(VALUE self)
286
+ {
287
+ int node_type;
288
+ cmark_node *node;
289
+ VALUE symbol;
290
+
291
+ Data_Get_Struct(self, cmark_node, node);
292
+
293
+ node_type = cmark_node_get_type(node);
294
+ symbol = Qnil;
295
+
296
+ switch (node_type) {
297
+ case CMARK_NODE_DOCUMENT:
298
+ symbol = sym_document;
299
+ break;
300
+ case CMARK_NODE_BLOCK_QUOTE:
301
+ symbol = sym_blockquote;
302
+ break;
303
+ case CMARK_NODE_LIST:
304
+ symbol = sym_list;
305
+ break;
306
+ case CMARK_NODE_ITEM:
307
+ symbol = sym_list_item;
308
+ break;
309
+ case CMARK_NODE_CODE_BLOCK:
310
+ symbol = sym_code_block;
311
+ break;
312
+ case CMARK_NODE_HTML:
313
+ symbol = sym_html;
314
+ break;
315
+ case CMARK_NODE_PARAGRAPH:
316
+ symbol = sym_paragraph;
317
+ break;
318
+ case CMARK_NODE_HEADER:
319
+ symbol = sym_header;
320
+ break;
321
+ case CMARK_NODE_HRULE:
322
+ symbol = sym_hrule;
323
+ break;
324
+ case CMARK_NODE_TEXT:
325
+ symbol = sym_text;
326
+ break;
327
+ case CMARK_NODE_SOFTBREAK:
328
+ symbol = sym_softbreak;
329
+ break;
330
+ case CMARK_NODE_LINEBREAK:
331
+ symbol = sym_linebreak;
332
+ break;
333
+ case CMARK_NODE_CODE:
334
+ symbol = sym_code;
335
+ break;
336
+ case CMARK_NODE_INLINE_HTML:
337
+ symbol = sym_inline_html;
338
+ break;
339
+ case CMARK_NODE_EMPH:
340
+ symbol = sym_emph;
341
+ break;
342
+ case CMARK_NODE_STRONG:
343
+ symbol = sym_strong;
344
+ break;
345
+ case CMARK_NODE_LINK:
346
+ symbol = sym_link;
347
+ break;
348
+ case CMARK_NODE_IMAGE:
349
+ symbol = sym_image;
350
+ break;
351
+ default:
352
+ rb_raise(rb_mNodeError, "invalid node type %d", node_type);
353
+ }
354
+
355
+ return symbol;
356
+ }
357
+
358
+ /*
359
+ * Public: Fetches the sourcepos of the node.
360
+ *
361
+ * Returns a {Hash} containing {Symbol} keys of the positions.
362
+ */
363
+ static VALUE
364
+ rb_node_get_sourcepos(VALUE self)
365
+ {
366
+ int start_line, start_column, end_line, end_column;
367
+ VALUE result;
368
+
369
+ cmark_node *node;
370
+ Data_Get_Struct(self, cmark_node, node);
371
+
372
+ start_line = cmark_node_get_start_line(node);
373
+ start_column = cmark_node_get_start_column(node);
374
+ end_line = cmark_node_get_end_line(node);
375
+ end_column = cmark_node_get_end_column(node);
376
+
377
+ result = rb_hash_new();
378
+ rb_hash_aset(result, CSTR2SYM("start_line"), INT2NUM(start_line));
379
+ rb_hash_aset(result, CSTR2SYM("start_column"), INT2NUM(start_column));
380
+ rb_hash_aset(result, CSTR2SYM("end_line"), INT2NUM(end_line));
381
+ rb_hash_aset(result, CSTR2SYM("end_column"), INT2NUM(end_column));
382
+
383
+ return result;
384
+ }
385
+
386
+ /*
387
+ * Public: Returns the type of the current pointer as a string.
388
+ *
389
+ * Returns a {String}.
390
+ */
391
+ static VALUE
392
+ rb_node_get_type_string(VALUE self)
393
+ {
394
+ cmark_node *node;
395
+ Data_Get_Struct(self, cmark_node, node);
396
+
397
+ return rb_str_new2(cmark_node_get_type_string(node));
398
+ }
399
+
400
+ /*
401
+ * Internal: Unlinks the node from the tree (fixing pointers in
402
+ * parents and siblings appropriately).
403
+ */
404
+ static VALUE
405
+ rb_node_unlink(VALUE self)
406
+ {
407
+ cmark_node *node;
408
+ Data_Get_Struct(self, cmark_node, node);
409
+
410
+ cmark_node_unlink(node);
411
+
412
+ rb_parent_removed(self);
413
+
414
+ return Qnil;
415
+ }
416
+
417
+ /* Public: Fetches the first child of the node.
418
+ *
419
+ * Returns a {Node} if a child exists, `nil` otherise.
420
+ */
421
+ static VALUE
422
+ rb_node_first_child(VALUE self)
423
+ {
424
+ cmark_node *node, *child;
425
+ Data_Get_Struct(self, cmark_node, node);
426
+
427
+ child = cmark_node_first_child(node);
428
+
429
+ return rb_node_to_value(child);
430
+ }
431
+
432
+ /* Public: Fetches the next sibling of the node.
433
+ *
434
+ * Returns a {Node} if a sibling exists, `nil` otherwise.
435
+ */
436
+ static VALUE
437
+ rb_node_next(VALUE self)
438
+ {
439
+ cmark_node *node, *next;
440
+ Data_Get_Struct(self, cmark_node, node);
441
+
442
+ next = cmark_node_next(node);
443
+
444
+ return rb_node_to_value(next);
445
+ }
446
+
447
+ /*
448
+ * Public: Inserts a node as a sibling before the current node.
449
+ *
450
+ * sibling - A sibling {Node} to insert.
451
+ *
452
+ * Returns `true` if successful.
453
+ * Raises NodeError if the node can't be inserted.
454
+ */
455
+ static VALUE
456
+ rb_node_insert_before(VALUE self, VALUE sibling)
457
+ {
458
+ cmark_node *node1, *node2;
459
+ Data_Get_Struct(self, cmark_node, node1);
460
+
461
+ Data_Get_Struct(sibling, cmark_node, node2);
462
+
463
+ if (!cmark_node_insert_before(node1, node2)) {
464
+ rb_raise(rb_mNodeError, "could not insert before");
465
+ }
466
+
467
+ rb_parent_added(sibling);
468
+
469
+ return Qtrue;
470
+ }
471
+
472
+ /* Internal: Convert the node to an HTML string.
473
+ *
474
+ * Returns a {String}.
475
+ */
476
+ static VALUE
477
+ rb_render_html(VALUE n, VALUE rb_options)
478
+ {
479
+ int options;
480
+ cmark_node *node;
481
+ Check_Type(rb_options, T_FIXNUM);
482
+
483
+ options = FIX2INT(rb_options);
484
+
485
+ Data_Get_Struct(n, cmark_node, node);
486
+
487
+ return rb_str_new2(cmark_render_html(node, options));
488
+ }
489
+
490
+ /*
491
+ * Public: Inserts a node as a sibling after the current node.
492
+ *
493
+ * sibling - A sibling {Node} to insert.
494
+ *
495
+ * Returns `true` if successful.
496
+ * Raises NodeError if the node can't be inserted.
497
+ */
498
+ static VALUE
499
+ rb_node_insert_after(VALUE self, VALUE sibling)
500
+ {
501
+ cmark_node *node1, *node2;
502
+ Data_Get_Struct(self, cmark_node, node1);
503
+
504
+ Data_Get_Struct(sibling, cmark_node, node2);
505
+
506
+ if (!cmark_node_insert_after(node1, node2)) {
507
+ rb_raise(rb_mNodeError, "could not insert after");
508
+ }
509
+
510
+ rb_parent_added(sibling);
511
+
512
+ return Qtrue;
513
+ }
514
+
515
+ /*
516
+ * Public: Inserts a node as the first child of the current node.
517
+ *
518
+ * child - A child {Node} to insert.
519
+ *
520
+ * Returns `true` if successful.
521
+ * Raises NodeError if the node can't be inserted.
522
+ */
523
+ static VALUE
524
+ rb_node_prepend_child(VALUE self, VALUE child)
525
+ {
526
+ cmark_node *node1, *node2;
527
+ Data_Get_Struct(self, cmark_node, node1);
528
+
529
+ Data_Get_Struct(child, cmark_node, node2);
530
+
531
+ if (!cmark_node_prepend_child(node1, node2)) {
532
+ rb_raise(rb_mNodeError, "could not prepend child");
533
+ }
534
+
535
+ rb_parent_added(child);
536
+
537
+ return Qtrue;
538
+ }
539
+
540
+ /*
541
+ * Public: Inserts a node as the last child of the current node.
542
+ *
543
+ * child - A child {Node} to insert.
544
+ *
545
+ * Returns `true` if successful.
546
+ * Raises NodeError if the node can't be inserted.
547
+ */
548
+ static VALUE
549
+ rb_node_append_child(VALUE self, VALUE child)
550
+ {
551
+ cmark_node *node1, *node2;
552
+ Data_Get_Struct(self, cmark_node, node1);
553
+
554
+ Data_Get_Struct(child, cmark_node, node2);
555
+
556
+ if (!cmark_node_append_child(node1, node2)) {
557
+ rb_raise(rb_mNodeError, "could not append child");
558
+ }
559
+
560
+ rb_parent_added(child);
561
+
562
+ return Qtrue;
563
+ }
564
+
565
+ /* Public: Fetches the first child of the current node.
566
+ *
567
+ * Returns a {Node} if a child exists, `nil` otherise.
568
+ */
569
+ static VALUE
570
+ rb_node_last_child(VALUE self)
571
+ {
572
+ cmark_node *node, *child;
573
+ Data_Get_Struct(self, cmark_node, node);
574
+
575
+ child = cmark_node_last_child(node);
576
+
577
+ return rb_node_to_value(child);
578
+ }
579
+
580
+ /* Public: Fetches the parent of the current node.
581
+ *
582
+ * Returns a {Node} if a parent exists, `nil` otherise.
583
+ */
584
+ static VALUE
585
+ rb_node_parent(VALUE self)
586
+ {
587
+ cmark_node *node, *parent;
588
+ Data_Get_Struct(self, cmark_node, node);
589
+
590
+ parent = cmark_node_parent(node);
591
+
592
+ return rb_node_to_value(parent);
593
+ }
594
+
595
+ /* Public: Fetches the previous sibling of the current node.
596
+ *
597
+ * Returns a {Node} if a parent exists, `nil` otherise.
598
+ */
599
+ static VALUE
600
+ rb_node_previous(VALUE self)
601
+ {
602
+ cmark_node *node, *previous;
603
+ Data_Get_Struct(self, cmark_node, node);
604
+
605
+ previous = cmark_node_previous(node);
606
+
607
+ return rb_node_to_value(previous);
608
+ }
609
+
610
+ /*
611
+ * Public: Gets the URL of the current node (must be a `:link` or `:image`).
612
+ *
613
+ * Returns a {String}.
614
+ * Raises a NodeError if the URL can't be retrieved.
615
+ */
616
+ static VALUE
617
+ rb_node_get_url(VALUE self)
618
+ {
619
+ const char *text;
620
+ cmark_node *node;
621
+ Data_Get_Struct(self, cmark_node, node);
622
+
623
+ text = cmark_node_get_url(node);
624
+ if (text == NULL) {
625
+ rb_raise(rb_mNodeError, "could not get url");
626
+ }
627
+
628
+ return rb_str_new2(text);
629
+ }
630
+
631
+ /*
632
+ * Public: Sets the URL of the current node (must be a `:link` or `:image`).
633
+ *
634
+ * url - A {String} representing the new URL
635
+ *
636
+ * Raises a NodeError if the URL can't be set.
637
+ */
638
+ static VALUE
639
+ rb_node_set_url(VALUE self, VALUE url)
640
+ {
641
+ cmark_node *node;
642
+ char *text;
643
+ Check_Type(url, T_STRING);
644
+
645
+ Data_Get_Struct(self, cmark_node, node);
646
+ text = StringValueCStr(url);
647
+
648
+ if (!cmark_node_set_url(node, text)) {
649
+ rb_raise(rb_mNodeError, "could not set url");
650
+ }
651
+
652
+ return Qnil;
653
+ }
654
+
655
+ /*
656
+ * Public: Gets the title of the current node (must be a `:link` or `:image`).
657
+ *
658
+ * Returns a {String}.
659
+ * Raises a NodeError if the title can't be retrieved.
660
+ */
661
+ static VALUE
662
+ rb_node_get_title(VALUE self)
663
+ {
664
+ const char *text;
665
+ cmark_node *node;
666
+ Data_Get_Struct(self, cmark_node, node);
667
+
668
+ text = cmark_node_get_title(node);
669
+ if (text == NULL) {
670
+ rb_raise(rb_mNodeError, "could not get title");
671
+ }
672
+
673
+ return rb_str_new2(text);
674
+ }
675
+
676
+ /*
677
+ * Public: Sets the title of the current node (must be a `:link` or `:image`).
678
+ *
679
+ * title - A {String} representing the new title
680
+ *
681
+ * Raises a NodeError if the title can't be set.
682
+ */
683
+ static VALUE
684
+ rb_node_set_title(VALUE self, VALUE title)
685
+ {
686
+ char *text;
687
+ cmark_node *node;
688
+ Check_Type(title, T_STRING);
689
+
690
+ Data_Get_Struct(self, cmark_node, node);
691
+ text = StringValueCStr(title);
692
+
693
+ if (!cmark_node_set_title(node, text)) {
694
+ rb_raise(rb_mNodeError, "could not set title");
695
+ }
696
+
697
+ return Qnil;
698
+ }
699
+
700
+ /*
701
+ * Public: Gets the header level of the current node (must be a `:header`).
702
+ *
703
+ * Returns a {Number} representing the header level.
704
+ * Raises a NodeError if the header level can't be retrieved.
705
+ */
706
+ static VALUE
707
+ rb_node_get_header_level(VALUE self)
708
+ {
709
+ int header_level;
710
+ cmark_node *node;
711
+ Data_Get_Struct(self, cmark_node, node);
712
+
713
+ header_level = cmark_node_get_header_level(node);
714
+
715
+ if (header_level == 0) {
716
+ rb_raise(rb_mNodeError, "could not get header_level");
717
+ }
718
+
719
+ return INT2NUM(header_level);
720
+ }
721
+
722
+ /*
723
+ * Public: Sets the header level of the current node (must be a `:header`).
724
+ *
725
+ * level - A {Number} representing the new header level
726
+ *
727
+ * Raises a NodeError if the header level can't be set.
728
+ */
729
+ static VALUE
730
+ rb_node_set_header_level(VALUE self, VALUE level)
731
+ {
732
+ int l;
733
+ cmark_node *node;
734
+ Check_Type(level, T_FIXNUM);
735
+
736
+ Data_Get_Struct(self, cmark_node, node);
737
+ l = FIX2INT(level);
738
+
739
+ if (!cmark_node_set_header_level(node, l)) {
740
+ rb_raise(rb_mNodeError, "could not set header_level");
741
+ }
742
+
743
+ return Qnil;
744
+ }
745
+
746
+ /*
747
+ * Public: Gets the list type of the current node (must be a `:list`).
748
+ *
749
+ * Returns a {Symbol}.
750
+ * Raises a NodeError if the title can't be retrieved.
751
+ */
752
+ static VALUE
753
+ rb_node_get_list_type(VALUE self)
754
+ {
755
+ int list_type;
756
+ cmark_node *node;
757
+ VALUE symbol;
758
+ Data_Get_Struct(self, cmark_node, node);
759
+
760
+ list_type = cmark_node_get_list_type(node);
761
+
762
+ if (list_type == CMARK_BULLET_LIST) {
763
+ symbol = sym_bullet_list;
764
+ } else if (list_type == CMARK_ORDERED_LIST) {
765
+ symbol = sym_ordered_list;
766
+ } else {
767
+ rb_raise(rb_mNodeError, "could not get list_type");
768
+ }
769
+
770
+ return symbol;
771
+ }
772
+
773
+ /*
774
+ * Public: Sets the list type of the current node (must be a `:list`).
775
+ *
776
+ * level - A {Symbol} representing the new list type
777
+ *
778
+ * Raises a NodeError if the list type can't be set.
779
+ */
780
+ static VALUE
781
+ rb_node_set_list_type(VALUE self, VALUE list_type)
782
+ {
783
+ int type = 0;
784
+ cmark_node *node;
785
+ Check_Type(list_type, T_SYMBOL);
786
+
787
+ Data_Get_Struct(self, cmark_node, node);
788
+
789
+ if (list_type == sym_bullet_list) {
790
+ type = CMARK_BULLET_LIST;
791
+ } else if (list_type == sym_ordered_list) {
792
+ type = CMARK_ORDERED_LIST;
793
+ } else {
794
+ rb_raise(rb_mNodeError, "invalid list_type");
795
+ }
796
+
797
+ if (!cmark_node_set_list_type(node, type)) {
798
+ rb_raise(rb_mNodeError, "could not set list_type");
799
+ }
800
+
801
+ return Qnil;
802
+ }
803
+
804
+ /*
805
+ * Public: Gets the starting number the current node (must be an `:ordered_list`).
806
+ *
807
+ * Returns a {Number} representing the starting number.
808
+ * Raises a NodeError if the starting number can't be retrieved.
809
+ */
810
+ static VALUE
811
+ rb_node_get_list_start(VALUE self)
812
+ {
813
+ cmark_node *node;
814
+ Data_Get_Struct(self, cmark_node, node);
815
+
816
+ if (cmark_node_get_type(node) != CMARK_NODE_LIST
817
+ || cmark_node_get_list_type(node) != CMARK_ORDERED_LIST) {
818
+ rb_raise(rb_mNodeError,
819
+ "can't get list_start for non-ordered list %d",
820
+ cmark_node_get_list_type(node));
821
+ }
822
+
823
+ return INT2NUM(cmark_node_get_list_start(node));
824
+ }
825
+
826
+ /*
827
+ * Public: Sets the starting number of the current node (must be an `:ordered_list`).
828
+ *
829
+ * level - A {Number} representing the new starting number
830
+ *
831
+ * Raises a NodeError if the starting number can't be set.
832
+ */
833
+ static VALUE
834
+ rb_node_set_list_start(VALUE self, VALUE start)
835
+ {
836
+ int s;
837
+ cmark_node *node;
838
+ Check_Type(start, T_FIXNUM);
839
+
840
+ Data_Get_Struct(self, cmark_node, node);
841
+ s = FIX2INT(start);
842
+
843
+ if (!cmark_node_set_list_start(node, s)) {
844
+ rb_raise(rb_mNodeError, "could not set list_start");
845
+ }
846
+
847
+ return Qnil;
848
+ }
849
+
850
+ /*
851
+ * Public: Gets the tight status the current node (must be a `:list`).
852
+ *
853
+ * Returns a `true` if the list is tight, `false` otherwise.
854
+ * Raises a NodeError if the starting number can't be retrieved.
855
+ */
856
+ static VALUE
857
+ rb_node_get_list_tight(VALUE self)
858
+ {
859
+ int flag;
860
+ cmark_node *node;
861
+ Data_Get_Struct(self, cmark_node, node);
862
+
863
+ if (cmark_node_get_type(node) != CMARK_NODE_LIST) {
864
+ rb_raise(rb_mNodeError,
865
+ "can't get list_tight for non-list");
866
+ }
867
+
868
+ flag = cmark_node_get_list_tight(node);
869
+
870
+ return flag ? Qtrue : Qfalse;
871
+ }
872
+
873
+ /*
874
+ * Public: Sets the tight status of the current node (must be a `:list`).
875
+ *
876
+ * tight - A {Boolean} representing the new tightness
877
+ *
878
+ * Raises a NodeError if the tightness can't be set.
879
+ */
880
+ static VALUE
881
+ rb_node_set_list_tight(VALUE self, VALUE tight)
882
+ {
883
+ int t;
884
+ cmark_node *node;
885
+ Data_Get_Struct(self, cmark_node, node);
886
+ t = RTEST(tight);
887
+
888
+ if (!cmark_node_set_list_tight(node, t)) {
889
+ rb_raise(rb_mNodeError, "could not set list_tight");
890
+ }
891
+
892
+ return Qnil;
893
+ }
894
+
895
+ /*
896
+ * Public: Gets the fence info of the current node (must be a `:code_block`).
897
+ *
898
+ * Returns a {String} representing the fence info.
899
+ * Raises a NodeError if the fence info can't be retrieved.
900
+ */
901
+ static VALUE
902
+ rb_node_get_fence_info(VALUE self)
903
+ {
904
+ const char *fence_info;
905
+ cmark_node *node;
906
+ Data_Get_Struct(self, cmark_node, node);
907
+
908
+ fence_info = cmark_node_get_fence_info(node);
909
+
910
+ if (fence_info == NULL) {
911
+ rb_raise(rb_mNodeError, "could not get fence_info");
912
+ }
913
+
914
+ return rb_str_new2(fence_info);
915
+ }
916
+
917
+ /*
918
+ * Public: Sets the fence info of the current node (must be a `:code_block`).
919
+ *
920
+ * info - A {String} representing the new fence info
921
+ *
922
+ * Raises a NodeError if the fence info can't be set.
923
+ */
924
+ static VALUE
925
+ rb_node_set_fence_info(VALUE self, VALUE info)
926
+ {
927
+ char *text;
928
+ cmark_node *node;
929
+ Check_Type(info, T_STRING);
930
+
931
+ Data_Get_Struct(self, cmark_node, node);
932
+ text = StringValueCStr(info);
933
+
934
+ if (!cmark_node_set_fence_info(node, text)) {
935
+ rb_raise(rb_mNodeError, "could not set fence_info");
936
+ }
937
+
938
+ return Qnil;
939
+ }
940
+
941
+ /* Internal: Escapes href URLs safely. */
942
+ static VALUE
943
+ rb_html_escape_href(VALUE self, VALUE rb_text)
944
+ {
945
+ char *result;
946
+ cmark_strbuf buf = GH_BUF_INIT;
947
+
948
+ Check_Type(rb_text, T_STRING);
949
+
950
+ if (houdini_escape_href(&buf, (const uint8_t *)RSTRING_PTR(rb_text), RSTRING_LEN(rb_text))) {
951
+ result = (char *)cmark_strbuf_detach(&buf);
952
+ return rb_str_new2(result);
953
+ }
954
+
955
+ return rb_text;
956
+
957
+ }
958
+
959
+ /* Internal: Escapes HTML content safely. */
960
+ static VALUE
961
+ rb_html_escape_html(VALUE self, VALUE rb_text)
962
+ {
963
+ char *result;
964
+ cmark_strbuf buf = GH_BUF_INIT;
965
+
966
+ Check_Type(rb_text, T_STRING);
967
+
968
+ if (houdini_escape_html0(&buf, (const uint8_t *)RSTRING_PTR(rb_text), RSTRING_LEN(rb_text), 0)) {
969
+ result = (char *)cmark_strbuf_detach(&buf);
970
+ return rb_str_new2(result);
971
+ }
972
+
973
+ return rb_text;
974
+ }
975
+
976
+ __attribute__((visibility("default")))
977
+ void Init_commonmarker()
978
+ {
979
+ VALUE module;
980
+ sym_document = ID2SYM(rb_intern("document"));
981
+ sym_blockquote = ID2SYM(rb_intern("blockquote"));
982
+ sym_list = ID2SYM(rb_intern("list"));
983
+ sym_list_item = ID2SYM(rb_intern("list_item"));
984
+ sym_code_block = ID2SYM(rb_intern("code_block"));
985
+ sym_html = ID2SYM(rb_intern("html"));
986
+ sym_paragraph = ID2SYM(rb_intern("paragraph"));
987
+ sym_header = ID2SYM(rb_intern("header"));
988
+ sym_hrule = ID2SYM(rb_intern("hrule"));
989
+ sym_text = ID2SYM(rb_intern("text"));
990
+ sym_softbreak = ID2SYM(rb_intern("softbreak"));
991
+ sym_linebreak = ID2SYM(rb_intern("linebreak"));
992
+ sym_code = ID2SYM(rb_intern("code"));
993
+ sym_inline_html = ID2SYM(rb_intern("inline_html"));
994
+ sym_emph = ID2SYM(rb_intern("emph"));
995
+ sym_strong = ID2SYM(rb_intern("strong"));
996
+ sym_link = ID2SYM(rb_intern("link"));
997
+ sym_image = ID2SYM(rb_intern("image"));
998
+
999
+ sym_bullet_list = ID2SYM(rb_intern("bullet_list"));
1000
+ sym_ordered_list = ID2SYM(rb_intern("ordered_list"));
1001
+
1002
+ module = rb_define_module("CommonMarker");
1003
+ rb_mNodeError = rb_define_class_under(module, "NodeError", rb_eStandardError);
1004
+ rb_mNode = rb_define_class_under(module, "Node", rb_cObject);
1005
+ rb_define_singleton_method(rb_mNode, "markdown_to_html", rb_markdown_to_html, 2);
1006
+ rb_define_singleton_method(rb_mNode, "new", rb_node_new, 1);
1007
+ rb_define_singleton_method(rb_mNode, "parse_document", rb_parse_document, 3);
1008
+ rb_define_method(rb_mNode, "string_content", rb_node_get_string_content, 0);
1009
+ rb_define_method(rb_mNode, "string_content=", rb_node_set_string_content, 1);
1010
+ rb_define_method(rb_mNode, "type", rb_node_get_type, 0);
1011
+ rb_define_method(rb_mNode, "type_string", rb_node_get_type_string, 0);
1012
+ rb_define_method(rb_mNode, "sourcepos", rb_node_get_sourcepos, 0);
1013
+ rb_define_method(rb_mNode, "delete", rb_node_unlink, 0);
1014
+ rb_define_method(rb_mNode, "first_child", rb_node_first_child, 0);
1015
+ rb_define_method(rb_mNode, "next", rb_node_next, 0);
1016
+ rb_define_method(rb_mNode, "insert_before", rb_node_insert_before, 1);
1017
+ rb_define_method(rb_mNode, "_render_html", rb_render_html, 1);
1018
+ rb_define_method(rb_mNode, "insert_after", rb_node_insert_after, 1);
1019
+ rb_define_method(rb_mNode, "prepend_child", rb_node_prepend_child, 1);
1020
+ rb_define_method(rb_mNode, "append_child", rb_node_append_child, 1);
1021
+ rb_define_method(rb_mNode, "last_child", rb_node_last_child, 0);
1022
+ rb_define_method(rb_mNode, "parent", rb_node_parent, 0);
1023
+ rb_define_method(rb_mNode, "previous", rb_node_previous, 0);
1024
+ rb_define_method(rb_mNode, "url", rb_node_get_url, 0);
1025
+ rb_define_method(rb_mNode, "url=", rb_node_set_url, 1);
1026
+ rb_define_method(rb_mNode, "title", rb_node_get_title, 0);
1027
+ rb_define_method(rb_mNode, "title=", rb_node_set_title, 1);
1028
+ rb_define_method(rb_mNode, "header_level", rb_node_get_header_level, 0);
1029
+ rb_define_method(rb_mNode, "header_level=", rb_node_set_header_level, 1);
1030
+ rb_define_method(rb_mNode, "list_type", rb_node_get_list_type, 0);
1031
+ rb_define_method(rb_mNode, "list_type=", rb_node_set_list_type, 1);
1032
+ rb_define_method(rb_mNode, "list_start", rb_node_get_list_start, 0);
1033
+ rb_define_method(rb_mNode, "list_start=", rb_node_set_list_start, 1);
1034
+ rb_define_method(rb_mNode, "list_tight", rb_node_get_list_tight, 0);
1035
+ rb_define_method(rb_mNode, "list_tight=", rb_node_set_list_tight, 1);
1036
+ rb_define_method(rb_mNode, "fence_info", rb_node_get_fence_info, 0);
1037
+ rb_define_method(rb_mNode, "fence_info=", rb_node_set_fence_info, 1);
1038
+
1039
+ rb_define_singleton_method(rb_mNode, "html_escape_href", rb_html_escape_href, 1);
1040
+ rb_define_singleton_method(rb_mNode, "html_escape_html", rb_html_escape_html, 1);
1041
+ }