commonmarker 0.23.10 → 2.8.0

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1164 -0
  3. data/Cargo.toml +12 -0
  4. data/README.md +252 -176
  5. data/ext/commonmarker/Cargo.toml +21 -0
  6. data/ext/commonmarker/build.rs +9 -0
  7. data/ext/commonmarker/extconf.rb +3 -6
  8. data/ext/commonmarker/src/lib.rs +168 -0
  9. data/ext/commonmarker/src/node.rs +1263 -0
  10. data/ext/commonmarker/src/options.rs +241 -0
  11. data/ext/commonmarker/src/plugins/syntax_highlighting.rs +171 -0
  12. data/ext/commonmarker/src/plugins.rs +6 -0
  13. data/ext/commonmarker/src/utils.rs +5 -0
  14. data/lib/commonmarker/config.rb +103 -40
  15. data/lib/commonmarker/constants.rb +7 -0
  16. data/lib/commonmarker/extension.rb +14 -0
  17. data/lib/commonmarker/node/ast.rb +8 -0
  18. data/lib/commonmarker/node/inspect.rb +14 -4
  19. data/lib/commonmarker/node.rb +29 -47
  20. data/lib/commonmarker/renderer.rb +1 -127
  21. data/lib/commonmarker/utils.rb +22 -0
  22. data/lib/commonmarker/version.rb +2 -2
  23. data/lib/commonmarker.rb +27 -25
  24. metadata +38 -187
  25. data/Rakefile +0 -109
  26. data/bin/commonmarker +0 -118
  27. data/commonmarker.gemspec +0 -38
  28. data/ext/commonmarker/arena.c +0 -104
  29. data/ext/commonmarker/autolink.c +0 -508
  30. data/ext/commonmarker/autolink.h +0 -8
  31. data/ext/commonmarker/blocks.c +0 -1622
  32. data/ext/commonmarker/buffer.c +0 -278
  33. data/ext/commonmarker/buffer.h +0 -116
  34. data/ext/commonmarker/case_fold_switch.inc +0 -4327
  35. data/ext/commonmarker/chunk.h +0 -135
  36. data/ext/commonmarker/cmark-gfm-core-extensions.h +0 -54
  37. data/ext/commonmarker/cmark-gfm-extension_api.h +0 -737
  38. data/ext/commonmarker/cmark-gfm-extensions_export.h +0 -42
  39. data/ext/commonmarker/cmark-gfm.h +0 -833
  40. data/ext/commonmarker/cmark-gfm_export.h +0 -42
  41. data/ext/commonmarker/cmark-gfm_version.h +0 -7
  42. data/ext/commonmarker/cmark.c +0 -55
  43. data/ext/commonmarker/cmark_ctype.c +0 -44
  44. data/ext/commonmarker/cmark_ctype.h +0 -33
  45. data/ext/commonmarker/commonmark.c +0 -514
  46. data/ext/commonmarker/commonmarker.c +0 -1308
  47. data/ext/commonmarker/commonmarker.h +0 -16
  48. data/ext/commonmarker/config.h +0 -76
  49. data/ext/commonmarker/core-extensions.c +0 -27
  50. data/ext/commonmarker/entities.inc +0 -2138
  51. data/ext/commonmarker/ext_scanners.c +0 -879
  52. data/ext/commonmarker/ext_scanners.h +0 -24
  53. data/ext/commonmarker/footnotes.c +0 -63
  54. data/ext/commonmarker/footnotes.h +0 -27
  55. data/ext/commonmarker/houdini.h +0 -57
  56. data/ext/commonmarker/houdini_href_e.c +0 -100
  57. data/ext/commonmarker/houdini_html_e.c +0 -66
  58. data/ext/commonmarker/houdini_html_u.c +0 -149
  59. data/ext/commonmarker/html.c +0 -502
  60. data/ext/commonmarker/html.h +0 -27
  61. data/ext/commonmarker/inlines.c +0 -1788
  62. data/ext/commonmarker/inlines.h +0 -29
  63. data/ext/commonmarker/iterator.c +0 -159
  64. data/ext/commonmarker/iterator.h +0 -26
  65. data/ext/commonmarker/latex.c +0 -468
  66. data/ext/commonmarker/linked_list.c +0 -37
  67. data/ext/commonmarker/man.c +0 -274
  68. data/ext/commonmarker/map.c +0 -129
  69. data/ext/commonmarker/map.h +0 -44
  70. data/ext/commonmarker/node.c +0 -1045
  71. data/ext/commonmarker/node.h +0 -167
  72. data/ext/commonmarker/parser.h +0 -59
  73. data/ext/commonmarker/plaintext.c +0 -218
  74. data/ext/commonmarker/plugin.c +0 -36
  75. data/ext/commonmarker/plugin.h +0 -34
  76. data/ext/commonmarker/references.c +0 -43
  77. data/ext/commonmarker/references.h +0 -26
  78. data/ext/commonmarker/registry.c +0 -63
  79. data/ext/commonmarker/registry.h +0 -24
  80. data/ext/commonmarker/render.c +0 -213
  81. data/ext/commonmarker/render.h +0 -62
  82. data/ext/commonmarker/scanners.c +0 -14056
  83. data/ext/commonmarker/scanners.h +0 -70
  84. data/ext/commonmarker/scanners.re +0 -341
  85. data/ext/commonmarker/strikethrough.c +0 -167
  86. data/ext/commonmarker/strikethrough.h +0 -9
  87. data/ext/commonmarker/syntax_extension.c +0 -149
  88. data/ext/commonmarker/syntax_extension.h +0 -34
  89. data/ext/commonmarker/table.c +0 -917
  90. data/ext/commonmarker/table.h +0 -12
  91. data/ext/commonmarker/tagfilter.c +0 -60
  92. data/ext/commonmarker/tagfilter.h +0 -8
  93. data/ext/commonmarker/tasklist.c +0 -156
  94. data/ext/commonmarker/tasklist.h +0 -8
  95. data/ext/commonmarker/utf8.c +0 -317
  96. data/ext/commonmarker/utf8.h +0 -35
  97. data/ext/commonmarker/xml.c +0 -182
  98. data/lib/commonmarker/renderer/html_renderer.rb +0 -256
@@ -0,0 +1,1263 @@
1
+ use comrak::arena_tree::Node as ComrakNode;
2
+ use comrak::nodes::{
3
+ AlertType, Ast as ComrakAst, AstNode as ComrakAstNode, ListDelimType, ListType, NodeAlert,
4
+ NodeCode, NodeCodeBlock, NodeDescriptionItem, NodeFootnoteDefinition, NodeFootnoteReference,
5
+ NodeHeading, NodeHtmlBlock, NodeLink, NodeList, NodeMath, NodeMultilineBlockQuote,
6
+ NodeShortCode, NodeTable, NodeTaskItem, NodeValue as ComrakNodeValue, NodeWikiLink,
7
+ TableAlignment,
8
+ };
9
+ use magnus::{function, method, scan_args, Module, Object, RHash, RModule, Symbol, Value};
10
+ use magnus::{RArray, Ruby};
11
+ use rctree::Node;
12
+ use typed_arena::Arena;
13
+
14
+ use std::cell::RefCell;
15
+
16
+ use crate::options::{iterate_extension_options, iterate_parse_options, iterate_render_options};
17
+ use crate::plugins::syntax_highlighting::construct_syntax_highlighter_from_plugin;
18
+
19
+ #[derive(Debug, Clone)]
20
+ #[magnus::wrap(class = "Commonmarker::Node::Ast", size, mark)]
21
+ pub struct CommonmarkerAst {
22
+ data: ComrakAst,
23
+ }
24
+
25
+ #[derive(Debug, Clone)]
26
+ #[magnus::wrap(class = "Commonmarker::Node", size, mark)]
27
+ pub struct CommonmarkerNode {
28
+ inner: Node<CommonmarkerAst>,
29
+ }
30
+
31
+ /// SAFETY: This is safe because we only access this data when the GVL is held.
32
+ unsafe impl Send for CommonmarkerNode {}
33
+
34
+ impl CommonmarkerNode {
35
+ pub fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, magnus::Error> {
36
+ let args = scan_args::scan_args::<_, (), (), (), _, ()>(args)?;
37
+ let (node_type,): (Symbol,) = args.required;
38
+
39
+ let node = match node_type.to_string().as_str() {
40
+ "document" => ComrakNodeValue::Document,
41
+ "block_quote" => ComrakNodeValue::BlockQuote,
42
+ "footnote_definition" => {
43
+ let kwargs = scan_args::get_kwargs::<_, (String,), (Option<u32>,), ()>(
44
+ args.keywords,
45
+ &["name"],
46
+ &["total_references"],
47
+ )?;
48
+ let (name,) = kwargs.required;
49
+ let (total_reference,) = kwargs.optional;
50
+
51
+ ComrakNodeValue::FootnoteDefinition(NodeFootnoteDefinition {
52
+ // The name of the footnote.
53
+ name,
54
+ // Total number of references to this footnote
55
+ total_references: total_reference.unwrap_or(1),
56
+ })
57
+ }
58
+ "list" => {
59
+ let kwargs = scan_args::get_kwargs::<
60
+ _,
61
+ (Symbol,),
62
+ (
63
+ Option<usize>,
64
+ Option<usize>,
65
+ Option<usize>,
66
+ Option<String>,
67
+ Option<u8>,
68
+ Option<bool>,
69
+ Option<bool>,
70
+ ),
71
+ (),
72
+ >(
73
+ args.keywords,
74
+ &["type"],
75
+ &[
76
+ "marker_offset",
77
+ "padding",
78
+ "start",
79
+ "delimiter",
80
+ "bullet_char",
81
+ "tight",
82
+ "task_list",
83
+ ],
84
+ )?;
85
+
86
+ let (list_type,) = kwargs.required;
87
+ let (marker_offset, padding, start, delimiter, bullet_char, tight, task_list) =
88
+ kwargs.optional;
89
+
90
+ let commonmark_list_type = list_type.to_string();
91
+
92
+ if commonmark_list_type != "bullet" && commonmark_list_type != "ordered" {
93
+ return Err(magnus::Error::new(
94
+ ruby.exception_arg_error(),
95
+ "list type must be `bullet` or `ordered`",
96
+ ));
97
+ }
98
+
99
+ let comrak_list_type = if commonmark_list_type == "ordered" {
100
+ ListType::Ordered
101
+ } else {
102
+ ListType::Bullet
103
+ };
104
+
105
+ let comrak_delimiter = match delimiter.unwrap_or("".to_string()).as_str() {
106
+ ")" => ListDelimType::Paren,
107
+ _ => ListDelimType::Period,
108
+ };
109
+
110
+ ComrakNodeValue::List(NodeList {
111
+ // The kind of list (bullet (unordered) or ordered).
112
+ list_type: comrak_list_type,
113
+ // Number of spaces before the list marker.
114
+ marker_offset: marker_offset.unwrap_or(0),
115
+ // Number of characters between the start of the list marker and the item text (including the list marker(s)).
116
+ padding: padding.unwrap_or(0),
117
+ // For ordered lists, the ordinal the list starts at.
118
+ start: start.unwrap_or(0),
119
+ // For ordered lists, the delimiter after each number.
120
+ delimiter: comrak_delimiter,
121
+ // For bullet lists, the character used for each bullet.
122
+ bullet_char: bullet_char.unwrap_or(0),
123
+ // Whether the list is [tight](https://github.github.com/gfm/#tight), i.e. whether the
124
+ // paragraphs are wrapped in `<p>` tags when formatted as HTML.
125
+ tight: tight.unwrap_or(false),
126
+ is_task_list: task_list.unwrap_or(false),
127
+ })
128
+ }
129
+ "description_list" => ComrakNodeValue::DescriptionList,
130
+ "description_item" => {
131
+ let kwargs = scan_args::get_kwargs::<
132
+ _,
133
+ (),
134
+ (Option<usize>, Option<usize>, Option<bool>),
135
+ (),
136
+ >(
137
+ args.keywords, &[], &["marker_offset", "padding", "tight"]
138
+ )?;
139
+
140
+ let (marker_offset, padding, tight) = kwargs.optional;
141
+
142
+ ComrakNodeValue::DescriptionItem(NodeDescriptionItem {
143
+ // Number of spaces before the list marker.
144
+ marker_offset: marker_offset.unwrap_or(0),
145
+ // Number of characters between the start of the list marker and the item text (including the list marker(s)).
146
+ padding: padding.unwrap_or(0),
147
+ // Whether the list is [tight](https://github.github.com/gfm/#tight), i.e. whether the
148
+ // paragraphs are wrapped in `<p>` tags when formatted as HTML.
149
+ tight: tight.unwrap_or(false),
150
+ })
151
+ }
152
+ "description_term" => ComrakNodeValue::DescriptionTerm,
153
+ "description_details" => ComrakNodeValue::DescriptionDetails,
154
+ "code_block" => {
155
+ let kwargs = scan_args::get_kwargs::<
156
+ _,
157
+ (bool,),
158
+ (
159
+ Option<u8>,
160
+ Option<usize>,
161
+ Option<usize>,
162
+ Option<String>,
163
+ Option<String>,
164
+ Option<bool>,
165
+ ),
166
+ (),
167
+ >(
168
+ args.keywords,
169
+ &["fenced"],
170
+ &[
171
+ "fence_char",
172
+ "fence_length",
173
+ "fence_offset",
174
+ "info",
175
+ "literal",
176
+ "closed",
177
+ ],
178
+ )?;
179
+ let (fenced,) = kwargs.required;
180
+ let (fence_char, fence_length, fence_offset, info, literal, closed) =
181
+ kwargs.optional;
182
+
183
+ ComrakNodeValue::CodeBlock(Box::new(NodeCodeBlock {
184
+ // Whether the code block is fenced.
185
+ fenced,
186
+ // For fenced code blocks, the fence character itself (`` ` `` or `~`).
187
+ fence_char: fence_char.unwrap_or(b'`'),
188
+ // For fenced code blocks, the length of the fence.
189
+ fence_length: fence_length.unwrap_or(0),
190
+ // For fenced code blocks, the indentation level of the code within the block.
191
+ fence_offset: fence_offset.unwrap_or(0),
192
+
193
+ // For fenced code blocks, the [info string](https://github.github.com/gfm/#info-string) after
194
+ // the opening fence, if any.
195
+ info: info.unwrap_or(String::with_capacity(10)),
196
+
197
+ // The literal contents of the code block. As the contents are not interpreted as Markdown at
198
+ // all, they are contained within this structure, rather than inserted into a child inline of
199
+ // any kind.
200
+ literal: literal.unwrap_or(String::new()),
201
+
202
+ // For fenced code blocks, whether the code block is closed. (If not, it was terminated by
203
+ // some other condition, like the end of the document, or the end of the indentation level the
204
+ // code block was introduced at.)
205
+ closed: closed.unwrap_or(true),
206
+ }))
207
+ }
208
+ "html_block" => {
209
+ let kwargs = scan_args::get_kwargs::<_, (), (Option<u8>, Option<String>), ()>(
210
+ args.keywords,
211
+ &[],
212
+ &["block_type", "literal"],
213
+ )?;
214
+
215
+ let (block_type, literal) = kwargs.optional;
216
+
217
+ ComrakNodeValue::HtmlBlock(NodeHtmlBlock {
218
+ // Number of spaces before the list marker.
219
+ block_type: block_type.unwrap_or(0),
220
+ // Number of characters between the start of the list marker and the item text (including the list marker(s)).
221
+ literal: literal.unwrap_or(String::new()),
222
+ })
223
+ }
224
+ "paragraph" => ComrakNodeValue::Paragraph,
225
+ "heading" => {
226
+ let kwargs = scan_args::get_kwargs::<_, (u8,), (Option<bool>, Option<bool>), ()>(
227
+ args.keywords,
228
+ &["level"],
229
+ &["setext", "closed"],
230
+ )?;
231
+
232
+ let (level,) = kwargs.required;
233
+ let (setext, closed) = kwargs.optional;
234
+
235
+ ComrakNodeValue::Heading(NodeHeading {
236
+ // The heading level. For setext headings, an underline made of "=" characters is level 1, and made of
237
+ // "-" is level 2. For ATX headings, the number of leading "#" characters (from 1 to 6) is its level.
238
+ level,
239
+ // Whether the heading is setext style (marked by an underline). If not, it is ATX style (marked by leading,
240
+ // and possibly trailing, "#" characters).
241
+ setext: setext.unwrap_or(false),
242
+ // For ATX headings, whether the the leading "#" are terminated by a sequence of closing "#" characters.
243
+ closed: closed.unwrap_or(false),
244
+ })
245
+ }
246
+ "thematic_break" => ComrakNodeValue::ThematicBreak,
247
+ "table" => {
248
+ let kwargs = scan_args::get_kwargs::<_, (RArray, usize, usize, usize), (), ()>(
249
+ args.keywords,
250
+ &[
251
+ "alignments",
252
+ "num_columns",
253
+ "num_rows",
254
+ "num_nonempty_cells",
255
+ ],
256
+ &[],
257
+ )?;
258
+
259
+ let (alignments, num_columns, num_rows, num_nonempty_cells) = kwargs.required;
260
+
261
+ let mut comrak_alignments = vec![];
262
+ alignments
263
+ .into_iter()
264
+ .for_each(|alignment| match alignment.to_string().as_str() {
265
+ "left" => {
266
+ comrak_alignments.push(TableAlignment::Left);
267
+ }
268
+ "right" => {
269
+ comrak_alignments.push(TableAlignment::Right);
270
+ }
271
+ "center" => {
272
+ comrak_alignments.push(TableAlignment::Center);
273
+ }
274
+ _ => {
275
+ comrak_alignments.push(TableAlignment::None);
276
+ }
277
+ });
278
+ ComrakNodeValue::Table(Box::new(NodeTable {
279
+ // The table alignments
280
+ alignments: comrak_alignments,
281
+
282
+ // Number of columns of the table
283
+ num_columns,
284
+
285
+ // Number of rows of the table
286
+ num_rows,
287
+
288
+ // Number of non-empty, non-autocompleted cells
289
+ num_nonempty_cells,
290
+ }))
291
+ }
292
+ "table_row" => {
293
+ let kwargs =
294
+ scan_args::get_kwargs::<_, (bool,), (), ()>(args.keywords, &["header"], &[])?;
295
+
296
+ let (header,) = kwargs.required;
297
+
298
+ ComrakNodeValue::TableRow(header)
299
+ }
300
+ "table_cell" => ComrakNodeValue::TableCell,
301
+ "text" => {
302
+ let kwargs = scan_args::get_kwargs::<_, (), (Option<String>,), ()>(
303
+ args.keywords,
304
+ &[],
305
+ &["content"],
306
+ )?;
307
+
308
+ let (content,) = kwargs.optional;
309
+
310
+ ComrakNodeValue::Text(content.unwrap_or_default().into())
311
+ }
312
+ "taskitem" => {
313
+ let kwargs = scan_args::get_kwargs::<_, (), (Option<char>,), ()>(
314
+ args.keywords,
315
+ &[],
316
+ &["mark"],
317
+ )?;
318
+
319
+ let (mark,) = kwargs.optional;
320
+
321
+ ComrakNodeValue::TaskItem(NodeTaskItem {
322
+ symbol: mark,
323
+ symbol_sourcepos: (0, 0, 0, 0).into(),
324
+ })
325
+ }
326
+ "softbreak" => ComrakNodeValue::SoftBreak,
327
+ "linebreak" => ComrakNodeValue::LineBreak,
328
+ "code" => {
329
+ let kwargs = scan_args::get_kwargs::<_, (), (Option<usize>, Option<String>), ()>(
330
+ args.keywords,
331
+ &[],
332
+ &["num_backticks", "literal"],
333
+ )?;
334
+
335
+ let (num_backticks, literal) = kwargs.optional;
336
+
337
+ ComrakNodeValue::Code(NodeCode {
338
+ // The number of backticks
339
+ num_backticks: num_backticks.unwrap_or(1),
340
+ // The content of the inline code span.
341
+ // As the contents are not interpreted as Markdown at all,
342
+ // they are contained within this structure,
343
+ // rather than inserted into a child inline of any kind
344
+ literal: literal.unwrap_or_default(),
345
+ })
346
+ }
347
+ "html_inline" => {
348
+ let kwargs = scan_args::get_kwargs::<_, (), (Option<String>,), ()>(
349
+ args.keywords,
350
+ &[],
351
+ &["content"],
352
+ )?;
353
+
354
+ let (content,) = kwargs.optional;
355
+
356
+ ComrakNodeValue::HtmlInline(content.unwrap_or_default())
357
+ }
358
+ "emph" => ComrakNodeValue::Emph,
359
+ "strong" => ComrakNodeValue::Strong,
360
+ "strikethrough" => ComrakNodeValue::Strikethrough,
361
+ "superscript" => ComrakNodeValue::Superscript,
362
+ "subscript" => ComrakNodeValue::Subscript,
363
+ "link" => {
364
+ let kwargs = scan_args::get_kwargs::<_, (String,), (Option<String>,), ()>(
365
+ args.keywords,
366
+ &["url"],
367
+ &["title"],
368
+ )?;
369
+
370
+ let (url,) = kwargs.required;
371
+ let (title,) = kwargs.optional;
372
+
373
+ ComrakNodeValue::Link(Box::new(NodeLink {
374
+ // The URL for the link destination or image source.
375
+ url,
376
+ // The title for the link or image.
377
+ //
378
+ // Note this field is used for the `title` attribute by the HTML formatter even for images;
379
+ // `alt` text is supplied in the image inline text.
380
+ title: title.unwrap_or_default(),
381
+ }))
382
+ }
383
+ "image" => {
384
+ let kwargs = scan_args::get_kwargs::<_, (String,), (Option<String>,), ()>(
385
+ args.keywords,
386
+ &["url"],
387
+ &["title"],
388
+ )?;
389
+
390
+ let (url,) = kwargs.required;
391
+ let (title,) = kwargs.optional;
392
+
393
+ ComrakNodeValue::Image(Box::new(NodeLink {
394
+ // The URL for the link destination or image source.
395
+ url,
396
+ // The title for the link or image.
397
+ //
398
+ // Note this field is used for the `title` attribute by the HTML formatter even for images;
399
+ // `alt` text is supplied in the image inline text.
400
+ title: title.unwrap_or_default(),
401
+ }))
402
+ }
403
+ "footnote_reference" => {
404
+ let kwargs = scan_args::get_kwargs::<_, (String,), (Option<u32>, Option<u32>), ()>(
405
+ args.keywords,
406
+ &["name"],
407
+ &["ref_num", "ix"],
408
+ )?;
409
+
410
+ let (name,) = kwargs.required;
411
+ let (ref_num, ix) = kwargs.optional;
412
+
413
+ ComrakNodeValue::FootnoteReference(Box::new(NodeFootnoteReference {
414
+ // The name of the footnote.
415
+ name,
416
+ // The original text and sourcepos column spans that comprised the footnote ref.
417
+ texts: vec![],
418
+ // The index of reference to the same footnote
419
+ ref_num: ref_num.unwrap_or(0),
420
+ // The index of the footnote in the document.
421
+ ix: ix.unwrap_or(0),
422
+ }))
423
+ }
424
+ // #[cfg(feature = "shortcodes")]
425
+ "shortcode" => {
426
+ let kwargs =
427
+ scan_args::get_kwargs::<_, (String,), (), ()>(args.keywords, &["code"], &[])?;
428
+
429
+ let (code,) = kwargs.required;
430
+
431
+ match NodeShortCode::resolve(code.as_str()) {
432
+ Some(shortcode) => ComrakNodeValue::ShortCode(Box::new(shortcode)),
433
+ None => {
434
+ return Err(magnus::Error::new(
435
+ ruby.exception_arg_error(),
436
+ "could not resolve shortcode",
437
+ ));
438
+ }
439
+ }
440
+ }
441
+ "math" => {
442
+ let kwargs = scan_args::get_kwargs::<_, (bool, bool, String), (), ()>(
443
+ args.keywords,
444
+ &["dollar_math", "display_math", "literal"],
445
+ &[],
446
+ )?;
447
+
448
+ let (dollar_math, display_math, literal) = kwargs.required;
449
+
450
+ ComrakNodeValue::Math(NodeMath {
451
+ // Whether this is dollar math (`$` or `$$`).
452
+ // `false` indicates it is code math
453
+ dollar_math,
454
+
455
+ // Whether this is display math (using `$$`)
456
+ display_math,
457
+
458
+ // The literal contents of the math span.
459
+ // As the contents are not interpreted as Markdown at all,
460
+ // they are contained within this structure,
461
+ // rather than inserted into a child inline of any kind.
462
+ literal,
463
+ })
464
+ }
465
+ "multiline_block_quote" => {
466
+ let kwargs = scan_args::get_kwargs::<_, (usize, usize), (), ()>(
467
+ args.keywords,
468
+ &["fence_length", "fence_offset"],
469
+ &[],
470
+ )?;
471
+
472
+ let (fence_length, fence_offset) = kwargs.required;
473
+
474
+ ComrakNodeValue::MultilineBlockQuote(NodeMultilineBlockQuote {
475
+ // The length of the fence.
476
+ fence_length,
477
+ // The indentation level of the fence marker.
478
+ fence_offset,
479
+ })
480
+ }
481
+
482
+ "escaped" => ComrakNodeValue::Escaped,
483
+
484
+ "wikilink" => {
485
+ let kwargs =
486
+ scan_args::get_kwargs::<_, (String,), (), ()>(args.keywords, &["url"], &[])?;
487
+
488
+ let (url,) = kwargs.required;
489
+
490
+ ComrakNodeValue::WikiLink(NodeWikiLink { url })
491
+ }
492
+
493
+ "raw" => {
494
+ let kwargs = scan_args::get_kwargs::<_, (), (Option<String>,), ()>(
495
+ args.keywords,
496
+ &[],
497
+ &["content"],
498
+ )?;
499
+
500
+ let (content,) = kwargs.optional;
501
+
502
+ ComrakNodeValue::Raw(content.unwrap_or_default())
503
+ }
504
+
505
+ "alert" => {
506
+ let kwargs = scan_args::get_kwargs::<
507
+ _,
508
+ (Symbol,),
509
+ (Option<String>, Option<bool>, Option<usize>, Option<usize>),
510
+ (),
511
+ >(
512
+ args.keywords,
513
+ &["type"],
514
+ &["title", "multiline", "fence_length", "fence_offset"],
515
+ )?;
516
+
517
+ let (alert_name,) = kwargs.required;
518
+ let (title, multiline, fence_length, fence_offset) = kwargs.optional;
519
+
520
+ let alert_type = match alert_name.to_string().as_str() {
521
+ "note" => AlertType::Note,
522
+ "tip" => AlertType::Tip,
523
+ "important" => AlertType::Important,
524
+ "warning" => AlertType::Warning,
525
+ _ => {
526
+ return Err(magnus::Error::new(
527
+ ruby.exception_arg_error(),
528
+ "alert type must be `note`, `tip`, `important`, or `warning`",
529
+ ));
530
+ }
531
+ };
532
+
533
+ ComrakNodeValue::Alert(Box::new(NodeAlert {
534
+ alert_type,
535
+ // Overridden title. If None, then use the default title.
536
+ title,
537
+ // Originated from a multiline blockquote.
538
+ multiline: multiline.unwrap_or(false),
539
+ // The length of the fence (multiline only).
540
+ fence_length: fence_length.unwrap_or(0),
541
+ // The indentation level of the fence marker (multiline only)
542
+ fence_offset: fence_offset.unwrap_or(0),
543
+ }))
544
+ }
545
+
546
+ _ => panic!("unknown node type {}", node_type),
547
+ };
548
+
549
+ Ok(CommonmarkerNode {
550
+ inner: Node::new(CommonmarkerAst {
551
+ data: ComrakAst::new(node, (0, 0).into()),
552
+ }),
553
+ })
554
+ }
555
+
556
+ pub fn new_from_comrak_node<'a>(
557
+ comrak_root_node: &'a ComrakAstNode<'a>,
558
+ ) -> Result<CommonmarkerNode, magnus::Error> {
559
+ let comrak_ast = comrak_root_node.data.clone().into_inner();
560
+
561
+ fn iter_nodes<'a>(comrak_node: &'a ComrakAstNode<'a>) -> CommonmarkerNode {
562
+ let comrak_node_ast = comrak_node.data.clone().into_inner();
563
+ let commonmark_node = CommonmarkerNode {
564
+ inner: Node::new(CommonmarkerAst {
565
+ data: comrak_node_ast,
566
+ }),
567
+ };
568
+
569
+ for c in comrak_node.children() {
570
+ match commonmark_node.append_child_node(&iter_nodes(c)) {
571
+ Ok(_) => {}
572
+ Err(e) => {
573
+ panic!("cannot append node: {}", e);
574
+ }
575
+ }
576
+ }
577
+
578
+ commonmark_node
579
+ }
580
+
581
+ let commonmarker_root_node = CommonmarkerNode {
582
+ inner: Node::new(CommonmarkerAst { data: comrak_ast }),
583
+ };
584
+
585
+ for child in comrak_root_node.children() {
586
+ let new_child = iter_nodes(child);
587
+
588
+ commonmarker_root_node.append_child_node(&new_child)?;
589
+ }
590
+
591
+ Ok(commonmarker_root_node)
592
+ }
593
+
594
+ fn type_to_symbol(ruby: &Ruby, rb_self: &Self) -> Symbol {
595
+ let node = rb_self.inner.borrow();
596
+ ruby.to_symbol(node.data.value.xml_node_name())
597
+ }
598
+
599
+ fn get_parent(&self) -> Option<CommonmarkerNode> {
600
+ self.inner.parent().map(|n| CommonmarkerNode { inner: n })
601
+ }
602
+
603
+ fn get_previous_sibling(&self) -> Option<CommonmarkerNode> {
604
+ self.inner
605
+ .previous_sibling()
606
+ .map(|n| CommonmarkerNode { inner: n })
607
+ }
608
+
609
+ fn get_next_sibling(&self) -> Option<CommonmarkerNode> {
610
+ self.inner
611
+ .next_sibling()
612
+ .map(|n| CommonmarkerNode { inner: n })
613
+ }
614
+
615
+ fn get_first_child(&self) -> Option<CommonmarkerNode> {
616
+ self.inner
617
+ .first_child()
618
+ .map(|n| CommonmarkerNode { inner: n })
619
+ }
620
+
621
+ fn get_last_child(&self) -> Option<CommonmarkerNode> {
622
+ self.inner
623
+ .last_child()
624
+ .map(|n| CommonmarkerNode { inner: n })
625
+ }
626
+
627
+ fn prepend_child_node(&self, new_child: &CommonmarkerNode) -> Result<bool, magnus::Error> {
628
+ let node = new_child.inner.clone();
629
+ node.detach();
630
+ self.inner.prepend(node);
631
+
632
+ Ok(true)
633
+ }
634
+
635
+ fn append_child_node(&self, new_child: &CommonmarkerNode) -> Result<bool, magnus::Error> {
636
+ let node = new_child.inner.clone();
637
+ node.detach();
638
+ self.inner.append(node);
639
+
640
+ Ok(true)
641
+ }
642
+
643
+ fn detach_node(&self) -> Result<CommonmarkerNode, magnus::Error> {
644
+ let node = self.inner.make_copy().borrow().data.clone();
645
+ self.inner.detach();
646
+
647
+ Ok(CommonmarkerNode {
648
+ inner: Node::new(CommonmarkerAst { data: node }),
649
+ })
650
+ }
651
+
652
+ fn get_sourcepos(ruby: &Ruby, rb_self: &Self) -> Result<RHash, magnus::Error> {
653
+ let node = rb_self.inner.borrow();
654
+
655
+ let result = ruby.hash_new();
656
+ result.aset(ruby.to_symbol("start_line"), node.data.sourcepos.start.line)?;
657
+ result.aset(
658
+ ruby.to_symbol("start_column"),
659
+ node.data.sourcepos.start.column,
660
+ )?;
661
+ result.aset(ruby.to_symbol("end_line"), node.data.sourcepos.end.line)?;
662
+ result.aset(ruby.to_symbol("end_column"), node.data.sourcepos.end.column)?;
663
+
664
+ Ok(result)
665
+ }
666
+
667
+ fn replace_node(&self, new_node: &CommonmarkerNode) -> Result<bool, magnus::Error> {
668
+ self.insert_node_after(new_node)?;
669
+ match self.detach_node() {
670
+ Ok(_) => Ok(true),
671
+ Err(e) => Err(e),
672
+ }
673
+ }
674
+
675
+ fn insert_node_before(&self, new_sibling: &CommonmarkerNode) -> Result<bool, magnus::Error> {
676
+ let node = new_sibling.inner.clone();
677
+ node.detach();
678
+ self.inner.insert_before(node);
679
+
680
+ Ok(true)
681
+ }
682
+
683
+ fn insert_node_after(&self, new_sibling: &CommonmarkerNode) -> Result<bool, magnus::Error> {
684
+ let node = new_sibling.inner.clone();
685
+ node.detach();
686
+ self.inner.insert_after(node);
687
+
688
+ Ok(true)
689
+ }
690
+
691
+ fn get_url(ruby: &Ruby, rb_self: &Self) -> Result<String, magnus::Error> {
692
+ let node = rb_self.inner.borrow();
693
+
694
+ match &node.data.value {
695
+ ComrakNodeValue::Link(link) => Ok(link.url.to_string()),
696
+ ComrakNodeValue::Image(image) => Ok(image.url.to_string()),
697
+ _ => Err(magnus::Error::new(
698
+ ruby.exception_type_error(),
699
+ "node is not an image or link node",
700
+ )),
701
+ }
702
+ }
703
+
704
+ fn set_url(ruby: &Ruby, rb_self: &Self, new_url: String) -> Result<bool, magnus::Error> {
705
+ let mut node = rb_self.inner.borrow_mut();
706
+
707
+ match node.data.value {
708
+ ComrakNodeValue::Link(ref mut link) => {
709
+ link.url = new_url;
710
+ Ok(true)
711
+ }
712
+ ComrakNodeValue::Image(ref mut image) => {
713
+ image.url = new_url;
714
+ Ok(true)
715
+ }
716
+ _ => Err(magnus::Error::new(
717
+ ruby.exception_type_error(),
718
+ "node is not an image or link node",
719
+ )),
720
+ }
721
+ }
722
+
723
+ fn get_string_content(ruby: &Ruby, rb_self: &Self) -> Result<String, magnus::Error> {
724
+ let node = rb_self.inner.borrow();
725
+
726
+ match node.data.value {
727
+ ComrakNodeValue::Code(ref code) => return Ok(code.literal.to_string()),
728
+ ComrakNodeValue::CodeBlock(ref code_block) => {
729
+ return Ok(code_block.literal.to_string())
730
+ }
731
+ _ => {}
732
+ }
733
+
734
+ match node.data.value.text() {
735
+ Some(s) => Ok(s.to_string()),
736
+ None => Err(magnus::Error::new(
737
+ ruby.exception_type_error(),
738
+ "node does not have string content",
739
+ )),
740
+ }
741
+ }
742
+
743
+ fn set_string_content(
744
+ ruby: &Ruby,
745
+ rb_self: &Self,
746
+ new_content: String,
747
+ ) -> Result<bool, magnus::Error> {
748
+ let mut node = rb_self.inner.borrow_mut();
749
+
750
+ match node.data.value {
751
+ ComrakNodeValue::Code(ref mut code) => {
752
+ code.literal = new_content;
753
+ return Ok(true);
754
+ }
755
+ ComrakNodeValue::CodeBlock(ref mut code_block) => {
756
+ code_block.literal = new_content;
757
+ return Ok(true);
758
+ }
759
+ _ => {}
760
+ }
761
+
762
+ match node.data.value.text_mut() {
763
+ Some(s) => {
764
+ *s = new_content.into();
765
+ Ok(true)
766
+ }
767
+ None => Err(magnus::Error::new(
768
+ ruby.exception_type_error(),
769
+ "node does not have string content",
770
+ )),
771
+ }
772
+ }
773
+
774
+ fn get_title(ruby: &Ruby, rb_self: &Self) -> Result<String, magnus::Error> {
775
+ let node = rb_self.inner.borrow();
776
+
777
+ match &node.data.value {
778
+ ComrakNodeValue::Link(link) => Ok(link.title.to_string()),
779
+ ComrakNodeValue::Image(image) => Ok(image.title.to_string()),
780
+ _ => Err(magnus::Error::new(
781
+ ruby.exception_type_error(),
782
+ "node is not an image or link node",
783
+ )),
784
+ }
785
+ }
786
+
787
+ fn set_title(ruby: &Ruby, rb_self: &Self, new_title: String) -> Result<bool, magnus::Error> {
788
+ let mut node = rb_self.inner.borrow_mut();
789
+
790
+ match node.data.value {
791
+ ComrakNodeValue::Link(ref mut link) => {
792
+ link.title = new_title;
793
+ Ok(true)
794
+ }
795
+ ComrakNodeValue::Image(ref mut image) => {
796
+ image.title = new_title;
797
+ Ok(true)
798
+ }
799
+ _ => Err(magnus::Error::new(
800
+ ruby.exception_type_error(),
801
+ "node is not an image or link node",
802
+ )),
803
+ }
804
+ }
805
+
806
+ fn get_header_level(ruby: &Ruby, rb_self: &Self) -> Result<u8, magnus::Error> {
807
+ let node = rb_self.inner.borrow();
808
+
809
+ match &node.data.value {
810
+ ComrakNodeValue::Heading(heading) => Ok(heading.level),
811
+ _ => Err(magnus::Error::new(
812
+ ruby.exception_type_error(),
813
+ "node is not a heading node",
814
+ )),
815
+ }
816
+ }
817
+
818
+ fn set_header_level(ruby: &Ruby, rb_self: &Self, new_level: u8) -> Result<bool, magnus::Error> {
819
+ let mut node = rb_self.inner.borrow_mut();
820
+
821
+ match node.data.value {
822
+ ComrakNodeValue::Heading(ref mut heading) => {
823
+ heading.level = new_level;
824
+ Ok(true)
825
+ }
826
+ _ => Err(magnus::Error::new(
827
+ ruby.exception_type_error(),
828
+ "node is not a heading node",
829
+ )),
830
+ }
831
+ }
832
+
833
+ fn get_list_type(ruby: &Ruby, rb_self: &Self) -> Result<Symbol, magnus::Error> {
834
+ let node = rb_self.inner.borrow();
835
+
836
+ match &node.data.value {
837
+ ComrakNodeValue::List(list) => match list.list_type {
838
+ comrak::nodes::ListType::Bullet => Ok(ruby.to_symbol("bullet")),
839
+ comrak::nodes::ListType::Ordered => Ok(ruby.to_symbol("ordered")),
840
+ },
841
+ _ => Err(magnus::Error::new(
842
+ ruby.exception_type_error(),
843
+ "node is not a list node",
844
+ )),
845
+ }
846
+ }
847
+
848
+ fn set_list_type(ruby: &Ruby, rb_self: &Self, new_type: Symbol) -> Result<bool, magnus::Error> {
849
+ let mut node = rb_self.inner.borrow_mut();
850
+
851
+ match node.data.value {
852
+ ComrakNodeValue::List(ref mut list) => {
853
+ match new_type.to_string().as_str() {
854
+ "bullet" => list.list_type = comrak::nodes::ListType::Bullet,
855
+ "ordered" => list.list_type = comrak::nodes::ListType::Ordered,
856
+ _ => return Ok(false),
857
+ }
858
+ Ok(true)
859
+ }
860
+ _ => Err(magnus::Error::new(
861
+ ruby.exception_type_error(),
862
+ "node is not a list node",
863
+ )),
864
+ }
865
+ }
866
+
867
+ fn get_list_start(ruby: &Ruby, rb_self: &Self) -> Result<usize, magnus::Error> {
868
+ let node = rb_self.inner.borrow();
869
+
870
+ match &node.data.value {
871
+ ComrakNodeValue::List(list) => Ok(list.start),
872
+ _ => Err(magnus::Error::new(
873
+ ruby.exception_type_error(),
874
+ "node is not a list node",
875
+ )),
876
+ }
877
+ }
878
+
879
+ fn set_list_start(
880
+ ruby: &Ruby,
881
+ rb_self: &Self,
882
+ new_start: usize,
883
+ ) -> Result<bool, magnus::Error> {
884
+ let mut node = rb_self.inner.borrow_mut();
885
+
886
+ match node.data.value {
887
+ ComrakNodeValue::List(ref mut list) => {
888
+ list.start = new_start;
889
+ Ok(true)
890
+ }
891
+ _ => Err(magnus::Error::new(
892
+ ruby.exception_type_error(),
893
+ "node is not a list node",
894
+ )),
895
+ }
896
+ }
897
+
898
+ fn get_list_tight(ruby: &Ruby, rb_self: &Self) -> Result<bool, magnus::Error> {
899
+ let node = rb_self.inner.borrow();
900
+
901
+ match &node.data.value {
902
+ ComrakNodeValue::List(list) => Ok(list.tight),
903
+ _ => Err(magnus::Error::new(
904
+ ruby.exception_type_error(),
905
+ "node is not a list node",
906
+ )),
907
+ }
908
+ }
909
+
910
+ fn set_list_tight(ruby: &Ruby, rb_self: &Self, new_tight: bool) -> Result<bool, magnus::Error> {
911
+ let mut node = rb_self.inner.borrow_mut();
912
+
913
+ match node.data.value {
914
+ ComrakNodeValue::List(ref mut list) => {
915
+ list.tight = new_tight;
916
+ Ok(true)
917
+ }
918
+ _ => Err(magnus::Error::new(
919
+ ruby.exception_type_error(),
920
+ "node is not a list node",
921
+ )),
922
+ }
923
+ }
924
+
925
+ fn get_fenced(ruby: &Ruby, rb_self: &Self) -> Result<bool, magnus::Error> {
926
+ let node = rb_self.inner.borrow();
927
+
928
+ match &node.data.value {
929
+ ComrakNodeValue::CodeBlock(code_block) => Ok(code_block.fenced),
930
+ _ => Err(magnus::Error::new(
931
+ ruby.exception_type_error(),
932
+ "node is not a code block node",
933
+ )),
934
+ }
935
+ }
936
+
937
+ fn set_fenced(ruby: &Ruby, rb_self: &Self, new_fenced: bool) -> Result<bool, magnus::Error> {
938
+ let mut node = rb_self.inner.borrow_mut();
939
+
940
+ match node.data.value {
941
+ ComrakNodeValue::CodeBlock(ref mut code_block) => {
942
+ code_block.fenced = new_fenced;
943
+ Ok(true)
944
+ }
945
+ _ => Err(magnus::Error::new(
946
+ ruby.exception_type_error(),
947
+ "node is not a code block node",
948
+ )),
949
+ }
950
+ }
951
+
952
+ fn get_fence_info(ruby: &Ruby, rb_self: &Self) -> Result<String, magnus::Error> {
953
+ let node = rb_self.inner.borrow();
954
+
955
+ match &node.data.value {
956
+ ComrakNodeValue::CodeBlock(code_block) => Ok(code_block.info.to_string()),
957
+ _ => Err(magnus::Error::new(
958
+ ruby.exception_type_error(),
959
+ "node is not a code block node",
960
+ )),
961
+ }
962
+ }
963
+
964
+ fn set_fence_info(
965
+ ruby: &Ruby,
966
+ rb_self: &Self,
967
+ new_info: String,
968
+ ) -> Result<bool, magnus::Error> {
969
+ let mut node = rb_self.inner.borrow_mut();
970
+
971
+ match node.data.value {
972
+ ComrakNodeValue::CodeBlock(ref mut code_block) => {
973
+ code_block.info = new_info;
974
+ Ok(true)
975
+ }
976
+ _ => Err(magnus::Error::new(
977
+ ruby.exception_type_error(),
978
+ "node is not a code block node",
979
+ )),
980
+ }
981
+ }
982
+
983
+ fn to_html(ruby: &Ruby, rb_self: &Self, args: &[Value]) -> Result<String, magnus::Error> {
984
+ let args = scan_args::scan_args::<(), (), (), (), _, ()>(args)?;
985
+
986
+ let kwargs = scan_args::get_kwargs::<
987
+ _,
988
+ (),
989
+ (Option<RHash>, Option<RHash>, Option<RHash>, Option<RHash>),
990
+ (),
991
+ >(
992
+ args.keywords,
993
+ &[],
994
+ &["parse", "render", "extension", "plugins"],
995
+ )?;
996
+ let (rb_parse, rb_render, rb_extension, rb_plugins) = kwargs.optional;
997
+
998
+ let mut comrak_parse_options = comrak::options::Parse::default();
999
+ let mut comrak_render_options = comrak::options::Render::default();
1000
+ let mut comrak_extension_options = comrak::options::Extension::default();
1001
+
1002
+ if let Some(rb_parse) = rb_parse {
1003
+ iterate_parse_options(&mut comrak_parse_options, rb_parse);
1004
+ }
1005
+ if let Some(rb_render) = rb_render {
1006
+ iterate_render_options(&mut comrak_render_options, rb_render);
1007
+ }
1008
+ if let Some(rb_extension) = rb_extension {
1009
+ iterate_extension_options(&mut comrak_extension_options, rb_extension);
1010
+ }
1011
+
1012
+ let comrak_options = comrak::Options {
1013
+ parse: comrak_parse_options,
1014
+ render: comrak_render_options,
1015
+ extension: comrak_extension_options,
1016
+ };
1017
+
1018
+ let mut comrak_plugins = comrak::options::Plugins::default();
1019
+
1020
+ let syntect_adapter = match construct_syntax_highlighter_from_plugin(ruby, rb_plugins) {
1021
+ Ok(Some(adapter)) => Some(adapter),
1022
+ Ok(None) => None,
1023
+ Err(err) => return Err(err),
1024
+ };
1025
+
1026
+ match syntect_adapter {
1027
+ Some(ref adapter) => comrak_plugins.render.codefence_syntax_highlighter = Some(adapter),
1028
+ None => comrak_plugins.render.codefence_syntax_highlighter = None,
1029
+ }
1030
+
1031
+ let arena: Arena<ComrakAstNode> = Arena::new();
1032
+ fn iter_nodes<'a>(
1033
+ arena: &'a Arena<comrak::arena_tree::Node<'a, RefCell<ComrakAst>>>,
1034
+ node: &CommonmarkerNode,
1035
+ ) -> &'a comrak::arena_tree::Node<'a, std::cell::RefCell<comrak::nodes::Ast>> {
1036
+ let comrak_node: &'a mut ComrakAstNode = arena.alloc(ComrakNode::new(RefCell::new(
1037
+ node.inner.borrow().data.clone(),
1038
+ )));
1039
+
1040
+ for c in node.inner.children() {
1041
+ let child = CommonmarkerNode { inner: c };
1042
+ let child_node = iter_nodes(arena, &child);
1043
+ comrak_node.append(child_node);
1044
+ }
1045
+
1046
+ comrak_node
1047
+ }
1048
+
1049
+ let comrak_root_node: ComrakNode<RefCell<ComrakAst>> =
1050
+ ComrakNode::new(RefCell::new(rb_self.inner.borrow().data.clone()));
1051
+
1052
+ for c in rb_self.inner.children() {
1053
+ let child = CommonmarkerNode { inner: c };
1054
+
1055
+ let new_child = iter_nodes(&arena, &child);
1056
+
1057
+ comrak_root_node.append(new_child);
1058
+ }
1059
+
1060
+ let mut output = String::new();
1061
+ match comrak::format_html_with_plugins(
1062
+ &comrak_root_node,
1063
+ &comrak_options,
1064
+ &mut output,
1065
+ &comrak_plugins,
1066
+ ) {
1067
+ Ok(_) => {}
1068
+ Err(e) => {
1069
+ return Err(magnus::Error::new(
1070
+ ruby.exception_runtime_error(),
1071
+ format!("cannot convert into html: {}", e),
1072
+ ));
1073
+ }
1074
+ }
1075
+
1076
+ Ok(output)
1077
+ }
1078
+
1079
+ fn to_commonmark(ruby: &Ruby, rb_self: &Self, args: &[Value]) -> Result<String, magnus::Error> {
1080
+ let args = scan_args::scan_args::<(), (), (), (), _, ()>(args)?;
1081
+
1082
+ let kwargs = scan_args::get_kwargs::<
1083
+ _,
1084
+ (),
1085
+ (Option<RHash>, Option<RHash>, Option<RHash>, Option<RHash>),
1086
+ (),
1087
+ >(
1088
+ args.keywords,
1089
+ &[],
1090
+ &["render", "parse", "extension", "plugins"],
1091
+ )?;
1092
+ let (rb_render, rb_parse, rb_extension, rb_plugins) = kwargs.optional;
1093
+
1094
+ let mut comrak_parse_options = comrak::options::Parse::default();
1095
+ let mut comrak_render_options = comrak::options::Render::default();
1096
+ let mut comrak_extension_options = comrak::options::Extension::default();
1097
+
1098
+ if let Some(rb_parse) = rb_parse {
1099
+ iterate_parse_options(&mut comrak_parse_options, rb_parse);
1100
+ }
1101
+ if let Some(rb_render) = rb_render {
1102
+ iterate_render_options(&mut comrak_render_options, rb_render);
1103
+ }
1104
+ if let Some(rb_extension) = rb_extension {
1105
+ iterate_extension_options(&mut comrak_extension_options, rb_extension);
1106
+ }
1107
+
1108
+ let comrak_options = comrak::Options {
1109
+ parse: comrak_parse_options,
1110
+ render: comrak_render_options,
1111
+ extension: comrak_extension_options,
1112
+ };
1113
+
1114
+ let mut comrak_plugins = comrak::options::Plugins::default();
1115
+
1116
+ let syntect_adapter = match construct_syntax_highlighter_from_plugin(ruby, rb_plugins) {
1117
+ Ok(Some(adapter)) => Some(adapter),
1118
+ Ok(None) => None,
1119
+ Err(err) => return Err(err),
1120
+ };
1121
+
1122
+ match syntect_adapter {
1123
+ Some(ref adapter) => comrak_plugins.render.codefence_syntax_highlighter = Some(adapter),
1124
+ None => comrak_plugins.render.codefence_syntax_highlighter = None,
1125
+ }
1126
+
1127
+ let arena: Arena<ComrakAstNode> = Arena::new();
1128
+ fn iter_nodes<'a>(
1129
+ arena: &'a Arena<comrak::arena_tree::Node<'a, RefCell<ComrakAst>>>,
1130
+ node: &CommonmarkerNode,
1131
+ ) -> &'a comrak::arena_tree::Node<'a, std::cell::RefCell<comrak::nodes::Ast>> {
1132
+ let comrak_node: &'a mut ComrakAstNode = arena.alloc(ComrakNode::new(RefCell::new(
1133
+ node.inner.borrow().data.clone(),
1134
+ )));
1135
+
1136
+ for c in node.inner.children() {
1137
+ let child = CommonmarkerNode { inner: c };
1138
+ let child_node = iter_nodes(arena, &child);
1139
+ comrak_node.append(child_node);
1140
+ }
1141
+
1142
+ comrak_node
1143
+ }
1144
+
1145
+ let comrak_root_node: ComrakNode<RefCell<ComrakAst>> =
1146
+ ComrakNode::new(RefCell::new(rb_self.inner.borrow().data.clone()));
1147
+
1148
+ for c in rb_self.inner.children() {
1149
+ let child = CommonmarkerNode { inner: c };
1150
+
1151
+ let new_child = iter_nodes(&arena, &child);
1152
+
1153
+ comrak_root_node.append(new_child);
1154
+ }
1155
+
1156
+ let mut output = String::new();
1157
+ match comrak::format_commonmark_with_plugins(
1158
+ &comrak_root_node,
1159
+ &comrak_options,
1160
+ &mut output,
1161
+ &comrak_plugins,
1162
+ ) {
1163
+ Ok(_) => {}
1164
+ Err(e) => {
1165
+ return Err(magnus::Error::new(
1166
+ ruby.exception_runtime_error(),
1167
+ format!("cannot convert into html: {}", e),
1168
+ ));
1169
+ }
1170
+ }
1171
+
1172
+ Ok(output)
1173
+ }
1174
+ }
1175
+
1176
+ pub fn init(ruby: &Ruby, m_commonmarker: RModule) -> Result<(), magnus::Error> {
1177
+ let c_node = m_commonmarker
1178
+ .define_class("Node", ruby.class_object())
1179
+ .expect("cannot define class Commonmarker::Node");
1180
+
1181
+ c_node.define_singleton_method("new", function!(CommonmarkerNode::new, -1))?;
1182
+
1183
+ c_node.define_method("type", method!(CommonmarkerNode::type_to_symbol, 0))?;
1184
+ c_node.define_method("parent", method!(CommonmarkerNode::get_parent, 0))?;
1185
+ c_node.define_method("first_child", method!(CommonmarkerNode::get_first_child, 0))?;
1186
+ c_node.define_method("last_child", method!(CommonmarkerNode::get_last_child, 0))?;
1187
+ c_node.define_method(
1188
+ "previous_sibling",
1189
+ method!(CommonmarkerNode::get_previous_sibling, 0),
1190
+ )?;
1191
+ c_node.define_method(
1192
+ "next_sibling",
1193
+ method!(CommonmarkerNode::get_next_sibling, 0),
1194
+ )?;
1195
+
1196
+ c_node.define_method("node_to_html", method!(CommonmarkerNode::to_html, -1))?;
1197
+ c_node.define_method(
1198
+ "node_to_commonmark",
1199
+ method!(CommonmarkerNode::to_commonmark, -1),
1200
+ )?;
1201
+
1202
+ c_node.define_method("replace", method!(CommonmarkerNode::replace_node, 1))?;
1203
+
1204
+ c_node.define_method(
1205
+ "insert_before",
1206
+ method!(CommonmarkerNode::insert_node_before, 1),
1207
+ )?;
1208
+ c_node.define_method(
1209
+ "insert_after",
1210
+ method!(CommonmarkerNode::insert_node_after, 1),
1211
+ )?;
1212
+
1213
+ c_node.define_method(
1214
+ "prepend_child",
1215
+ method!(CommonmarkerNode::prepend_child_node, 1),
1216
+ )?;
1217
+ c_node.define_method(
1218
+ "append_child",
1219
+ method!(CommonmarkerNode::append_child_node, 1),
1220
+ )?;
1221
+
1222
+ c_node.define_method("delete", method!(CommonmarkerNode::detach_node, 0))?;
1223
+
1224
+ c_node.define_method(
1225
+ "source_position",
1226
+ method!(CommonmarkerNode::get_sourcepos, 0),
1227
+ )?;
1228
+
1229
+ c_node.define_method(
1230
+ "string_content",
1231
+ method!(CommonmarkerNode::get_string_content, 0),
1232
+ )?;
1233
+ c_node.define_method(
1234
+ "string_content=",
1235
+ method!(CommonmarkerNode::set_string_content, 1),
1236
+ )?;
1237
+
1238
+ c_node.define_method("url", method!(CommonmarkerNode::get_url, 0))?;
1239
+ c_node.define_method("url=", method!(CommonmarkerNode::set_url, 1))?;
1240
+ c_node.define_method("title", method!(CommonmarkerNode::get_title, 0))?;
1241
+ c_node.define_method("title=", method!(CommonmarkerNode::set_title, 1))?;
1242
+
1243
+ c_node.define_method(
1244
+ "header_level",
1245
+ method!(CommonmarkerNode::get_header_level, 0),
1246
+ )?;
1247
+ c_node.define_method(
1248
+ "header_level=",
1249
+ method!(CommonmarkerNode::set_header_level, 1),
1250
+ )?;
1251
+ c_node.define_method("list_type", method!(CommonmarkerNode::get_list_type, 0))?;
1252
+ c_node.define_method("list_type=", method!(CommonmarkerNode::set_list_type, 1))?;
1253
+ c_node.define_method("list_start", method!(CommonmarkerNode::get_list_start, 0))?;
1254
+ c_node.define_method("list_start=", method!(CommonmarkerNode::set_list_start, 1))?;
1255
+ c_node.define_method("list_tight", method!(CommonmarkerNode::get_list_tight, 0))?;
1256
+ c_node.define_method("list_tight=", method!(CommonmarkerNode::set_list_tight, 1))?;
1257
+ c_node.define_method("fenced?", method!(CommonmarkerNode::get_fenced, 0))?;
1258
+ c_node.define_method("fenced=", method!(CommonmarkerNode::set_fenced, 1))?;
1259
+ c_node.define_method("fence_info", method!(CommonmarkerNode::get_fence_info, 0))?;
1260
+ c_node.define_method("fence_info=", method!(CommonmarkerNode::set_fence_info, 1))?;
1261
+
1262
+ Ok(())
1263
+ }