commonmarker 2.9.0 → 2.10.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 051a8f17a827d1329de0031ac4f933b3a78aee387707562979698f810bb64a87
4
- data.tar.gz: e4f6e3c99d4c680db8d41f6d2bea08fb394db91cd628fed345a47c8b2f078e9f
3
+ metadata.gz: 4d5e959f519ad6befde6dac13c3d66d8df0eb8a5607553466d49544d75fb53dd
4
+ data.tar.gz: c4b1fa07305b5781a5a4ddb94090e474221df13ca946724068407b925d7c7e57
5
5
  SHA512:
6
- metadata.gz: dce8bf8e72268b9304ad04bc91d741be82f6e35de32a6914cacbce2be17b89ea1349c04dc45e83b06f91eeecb3eb36b697d099044b53926a1cafdb9bf7003894
7
- data.tar.gz: 9dbcbfea2785a3e5567f605e0b96ff87e78cee59aa4219b524a55fd1d4241261acbc76fd5d3950565ca457795e0b957a3b19c5a27f6284eddce227de41653cff
6
+ metadata.gz: 0e91a96c44c65c948f9e4631ca57f4e0a735f6e23ca5f8eb76e8d5a1ae7f5909b102712501a3a01abc25109e18830333f7557ed676a87bd1b458bdc6f0ed61a3
7
+ data.tar.gz: 858617c6e1444fafaa3fa8b70ee128ed4ad4fe9e5802ff1155adb2102a209cc53d4721ae85c78392fc2f26a7b8c386bcdd6a84cc7e0e38411ddae389bb06e87e
data/Cargo.lock CHANGED
@@ -47,7 +47,7 @@ dependencies = [
47
47
  "regex",
48
48
  "rustc-hash",
49
49
  "shlex 1.3.0",
50
- "syn",
50
+ "syn 2.0.118",
51
51
  ]
52
52
 
53
53
  [[package]]
@@ -124,23 +124,22 @@ dependencies = [
124
124
  "magnus",
125
125
  "rb-allocator",
126
126
  "rb-sys",
127
- "rctree",
128
127
  "syntect",
129
128
  "typed-arena",
130
129
  ]
131
130
 
132
131
  [[package]]
133
132
  name = "comrak"
134
- version = "0.54.0"
133
+ version = "0.56.0"
135
134
  source = "registry+https://github.com/rust-lang/crates.io-index"
136
- checksum = "0d5910408554659ed848ff469e67ec83b30f179e72cec286cfdae64d1616f466"
135
+ checksum = "2544c4d6b6da7d54e95ac2833b464bf3dffa0666237868f2bf4ec5e11c9ba8cc"
137
136
  dependencies = [
138
137
  "caseless",
139
138
  "emojis",
140
139
  "entities",
141
140
  "finl_unicode",
142
- "jetscii",
143
- "phf",
141
+ "memchr-n",
142
+ "phf 0.14.0",
144
143
  "phf_codegen",
145
144
  "rustc-hash",
146
145
  "smallvec",
@@ -171,11 +170,11 @@ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
171
170
 
172
171
  [[package]]
173
172
  name = "emojis"
174
- version = "0.8.2"
173
+ version = "0.9.0"
175
174
  source = "registry+https://github.com/rust-lang/crates.io-index"
176
- checksum = "0a4d5d50b0b58df5173d8ff1192b4d1422ceae5d981b30d4b6f8ed1d673a2bc4"
175
+ checksum = "0b1514ced566c94991ed9563258ecf61e311fd773bf0a3f20606830386bf66e3"
177
176
  dependencies = [
178
- "phf",
177
+ "phf 0.13.1",
179
178
  ]
180
179
 
181
180
  [[package]]
@@ -207,6 +206,23 @@ version = "2.4.1"
207
206
  source = "registry+https://github.com/rust-lang/crates.io-index"
208
207
  checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
209
208
 
209
+ [[package]]
210
+ name = "fearless_simd"
211
+ version = "1.1.0"
212
+ source = "registry+https://github.com/rust-lang/crates.io-index"
213
+ checksum = "3296eb97d869113eaf148a6bdf01884e90b3828ad7ae2340b0f93362fc158611"
214
+
215
+ [[package]]
216
+ name = "fearless_simd_macros"
217
+ version = "0.1.0"
218
+ source = "registry+https://github.com/rust-lang/crates.io-index"
219
+ checksum = "3e91d94c6885390b81782d7ee60fd6583b33907333a8b340de25ccf468353315"
220
+ dependencies = [
221
+ "proc-macro2",
222
+ "quote",
223
+ "syn 3.0.9",
224
+ ]
225
+
210
226
  [[package]]
211
227
  name = "find-msvc-tools"
212
228
  version = "0.1.9"
@@ -272,12 +288,6 @@ version = "1.0.18"
272
288
  source = "registry+https://github.com/rust-lang/crates.io-index"
273
289
  checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
274
290
 
275
- [[package]]
276
- name = "jetscii"
277
- version = "0.5.3"
278
- source = "registry+https://github.com/rust-lang/crates.io-index"
279
- checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e"
280
-
281
291
  [[package]]
282
292
  name = "lazy_static"
283
293
  version = "1.5.0"
@@ -308,9 +318,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
308
318
 
309
319
  [[package]]
310
320
  name = "magnus"
311
- version = "0.8.2"
321
+ version = "0.9.3"
312
322
  source = "registry+https://github.com/rust-lang/crates.io-index"
313
- checksum = "3b36a5b126bbe97eb0d02d07acfeb327036c6319fd816139a49824a83b7f9012"
323
+ checksum = "40ef424404d166cbb71c8b8d19c9c589c748c4547c1afa1928c5504299f6c713"
314
324
  dependencies = [
315
325
  "magnus-macros",
316
326
  "rb-sys",
@@ -320,13 +330,13 @@ dependencies = [
320
330
 
321
331
  [[package]]
322
332
  name = "magnus-macros"
323
- version = "0.8.0"
333
+ version = "0.9.1"
324
334
  source = "registry+https://github.com/rust-lang/crates.io-index"
325
- checksum = "47607461fd8e1513cb4f2076c197d8092d921a1ea75bd08af97398f593751892"
335
+ checksum = "9dafde2eeb66f090e0f326112323eb8324b12cb32b5d1210e541d3df6b0ce77e"
326
336
  dependencies = [
327
337
  "proc-macro2",
328
338
  "quote",
329
- "syn",
339
+ "syn 2.0.118",
330
340
  ]
331
341
 
332
342
  [[package]]
@@ -335,6 +345,16 @@ version = "2.8.3"
335
345
  source = "registry+https://github.com/rust-lang/crates.io-index"
336
346
  checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
337
347
 
348
+ [[package]]
349
+ name = "memchr-n"
350
+ version = "0.1.9"
351
+ source = "registry+https://github.com/rust-lang/crates.io-index"
352
+ checksum = "1805571b8e7b1284ad43f27c88477840c1c0e5807c150021ac83d2228806540e"
353
+ dependencies = [
354
+ "fearless_simd",
355
+ "fearless_simd_macros",
356
+ ]
357
+
338
358
  [[package]]
339
359
  name = "minimal-lexical"
340
360
  version = "0.2.1"
@@ -401,28 +421,37 @@ version = "0.13.1"
401
421
  source = "registry+https://github.com/rust-lang/crates.io-index"
402
422
  checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
403
423
  dependencies = [
404
- "phf_shared",
424
+ "phf_shared 0.13.1",
425
+ ]
426
+
427
+ [[package]]
428
+ name = "phf"
429
+ version = "0.14.0"
430
+ source = "registry+https://github.com/rust-lang/crates.io-index"
431
+ checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6"
432
+ dependencies = [
433
+ "phf_shared 0.14.0",
405
434
  "serde",
406
435
  ]
407
436
 
408
437
  [[package]]
409
438
  name = "phf_codegen"
410
- version = "0.13.1"
439
+ version = "0.14.0"
411
440
  source = "registry+https://github.com/rust-lang/crates.io-index"
412
- checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
441
+ checksum = "41b585a510fb76fdebead6897982ef2a03a21d8e6cbcca904999742a4afc6ffe"
413
442
  dependencies = [
414
443
  "phf_generator",
415
- "phf_shared",
444
+ "phf_shared 0.14.0",
416
445
  ]
417
446
 
418
447
  [[package]]
419
448
  name = "phf_generator"
420
- version = "0.13.1"
449
+ version = "0.14.0"
421
450
  source = "registry+https://github.com/rust-lang/crates.io-index"
422
- checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
451
+ checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100"
423
452
  dependencies = [
424
453
  "fastrand",
425
- "phf_shared",
454
+ "phf_shared 0.14.0",
426
455
  ]
427
456
 
428
457
  [[package]]
@@ -434,6 +463,15 @@ dependencies = [
434
463
  "siphasher",
435
464
  ]
436
465
 
466
+ [[package]]
467
+ name = "phf_shared"
468
+ version = "0.14.0"
469
+ source = "registry+https://github.com/rust-lang/crates.io-index"
470
+ checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89"
471
+ dependencies = [
472
+ "siphasher",
473
+ ]
474
+
437
475
  [[package]]
438
476
  name = "pkg-config"
439
477
  version = "0.3.33"
@@ -497,18 +535,18 @@ dependencies = [
497
535
 
498
536
  [[package]]
499
537
  name = "rb-sys"
500
- version = "0.9.128"
538
+ version = "0.9.130"
501
539
  source = "registry+https://github.com/rust-lang/crates.io-index"
502
- checksum = "45ca28513560e56cfb79a62b1fce363c73af170a182024ce880c77ee9429920a"
540
+ checksum = "02faf625bb10ba893e3ae620f19c9fb1b5f8fcae0fe4eb86bb3f2230fad75edb"
503
541
  dependencies = [
504
542
  "rb-sys-build",
505
543
  ]
506
544
 
507
545
  [[package]]
508
546
  name = "rb-sys-build"
509
- version = "0.9.128"
547
+ version = "0.9.130"
510
548
  source = "registry+https://github.com/rust-lang/crates.io-index"
511
- checksum = "ce04b2c55eff3a21aaa623fcc655d94373238e72cac6b3e1a3641ff31649f99a"
549
+ checksum = "05be6f9c86fe5482808162826f6c047d093b3670582296c770de1d94f8066694"
512
550
  dependencies = [
513
551
  "bindgen",
514
552
  "lazy_static",
@@ -516,7 +554,7 @@ dependencies = [
516
554
  "quote",
517
555
  "regex",
518
556
  "shell-words",
519
- "syn",
557
+ "syn 2.0.118",
520
558
  ]
521
559
 
522
560
  [[package]]
@@ -525,12 +563,6 @@ version = "0.2.3"
525
563
  source = "registry+https://github.com/rust-lang/crates.io-index"
526
564
  checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a"
527
565
 
528
- [[package]]
529
- name = "rctree"
530
- version = "0.6.0"
531
- source = "registry+https://github.com/rust-lang/crates.io-index"
532
- checksum = "e03e7866abec1101869ffa8e2c8355c4c2419d0214ece0cc3e428e5b94dea6e9"
533
-
534
566
  [[package]]
535
567
  name = "regex"
536
568
  version = "1.13.0"
@@ -607,7 +639,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
607
639
  dependencies = [
608
640
  "proc-macro2",
609
641
  "quote",
610
- "syn",
642
+ "syn 2.0.118",
611
643
  ]
612
644
 
613
645
  [[package]]
@@ -670,6 +702,17 @@ dependencies = [
670
702
  "unicode-ident",
671
703
  ]
672
704
 
705
+ [[package]]
706
+ name = "syn"
707
+ version = "3.0.9"
708
+ source = "registry+https://github.com/rust-lang/crates.io-index"
709
+ checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
710
+ dependencies = [
711
+ "proc-macro2",
712
+ "quote",
713
+ "unicode-ident",
714
+ ]
715
+
673
716
  [[package]]
674
717
  name = "syntect"
675
718
  version = "5.3.0"
@@ -709,7 +752,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
709
752
  dependencies = [
710
753
  "proc-macro2",
711
754
  "quote",
712
- "syn",
755
+ "syn 2.0.118",
713
756
  ]
714
757
 
715
758
  [[package]]
data/README.md CHANGED
@@ -5,7 +5,7 @@ Ruby wrapper for Rust's [comrak](https://github.com/kivikakk/comrak) crate.
5
5
  It passes all of the CommonMark test suite, and is therefore spec-complete. It also includes extensions to the CommonMark spec as documented in the [GitHub Flavored Markdown spec](http://github.github.com/gfm/), such as support for tables, strikethroughs, and autolinking.
6
6
 
7
7
  > [!NOTE]
8
- > By default, the following extensions are enabled for end user convenience: `strikethrough`, `tagfilter`, `table`, `autolink`, `tasklist` (all from the [GFM spec](http://github.github.com/gfm/)), and `shortcodes`. The `syntax_highlighter` plugin is also enabled by default, using the `"base16-ocean.dark"` theme.
8
+ > By default, the following extensions are enabled for end user convenience: `strikethrough`, `table`, `autolink`, `tasklist` (all from the [GFM spec](http://github.github.com/gfm/)), and `shortcodes`. The `syntax_highlighter` plugin is also enabled by default, using the `"base16-ocean.dark"` theme.
9
9
  >
10
10
  > For more information on the available options and extensions, see [the documentation below](#options-and-plugins).
11
11
 
@@ -140,6 +140,24 @@ doc.to_commonmark
140
140
  # => # The site\n\nGitHub\n
141
141
  ```
142
142
 
143
+ ### Reading and writing node content
144
+
145
+ `string_content` reads and writes the text of nodes whose content is plain text (like `:text`, `:code`, and `:code_block`).
146
+
147
+ `literal` reads and writes the raw string a node carries, for every node type that has one (like `:text`, `:code`, `:code_block`, `:html_block`, `:html_inline`, `:raw`, `:math`, and `:frontmatter`).
148
+
149
+ The two exist separately because they mean different things. Rewriting `string_content` only ever swaps text for text. Rewriting `literal` on an `:html_block`, `:html_inline`, or `:raw` node writes markup that is emitted unescaped:
150
+
151
+ ```ruby
152
+ doc = Commonmarker.parse("A <b>bold</b> claim")
153
+
154
+ doc.walk do |node|
155
+ node.literal = "<i>" if node.type == :html_inline && node.literal == "<b>"
156
+ end
157
+ ```
158
+
159
+ Note that a `:frontmatter` node's literal includes its delimiters and trailing newlines, so anything you assign must include them too.
160
+
143
161
  ## Options and plugins
144
162
 
145
163
  ### Options
@@ -166,24 +184,25 @@ Note that there is a distinction in comrak for "parse" options and "render" opti
166
184
  | `leave_footnote_definitions` | Allow footnote definitions to remain in their original positions instead of being moved to the document's end (only affects AST) | `false` |
167
185
  | `ignore_setext` | Ignores setext-style headings. | `false` |
168
186
  | `sourcepos_chars` | Use character-based column tracking in source positions instead of byte-based. Relevant for multi-byte UTF-8 documents with `sourcepos`. | `false` |
187
+ | `max_nesting_depth` | The deepest a node may nest below the document. Deeper documents raise `Commonmarker::NestingDepthExceededError` instead of being parsed. | `1000` |
169
188
 
170
189
  ### Render options
171
190
 
172
- | Name | Description | Default |
173
- | -------------------- | ------------------------------------------------------------------------------------------------------ | ------- |
174
- | `hardbreaks` | [Soft line breaks](http://spec.commonmark.org/0.27/#soft-line-breaks) translate into hard line breaks. | `true` |
175
- | `github_pre_lang` | GitHub-style `<pre lang="xyz">` is used for fenced code blocks with info tags. | `true` |
176
- | `full_info_string` | Gives info string data after a space in a `data-meta` attribute on code blocks. | `false` |
177
- | `width` | The wrap column when outputting CommonMark. | `80` |
178
- | `unsafe` | Allow rendering of raw HTML and potentially dangerous links. | `false` |
179
- | `escape` | Escape raw HTML instead of clobbering it. | `false` |
180
- | `sourcepos` | Include source position attribute in HTML and XML output. | `false` |
181
- | `escaped_char_spans` | Wrap escaped characters in span tags. | `true` |
182
- | `ignore_empty_links` | Ignores empty links, leaving the Markdown text in place. | `false` |
183
- | `gfm_quirks` | Outputs HTML with GFM-style quirks; namely, not nesting `<strong>` inlines. | `false` |
184
- | `prefer_fenced` | Always output fenced code blocks, even where an indented one could be used. | `false` |
185
- | `tasklist_classes` | Add CSS classes to the HTML output of the tasklist extension | `false` |
186
- | `compact_html` | Suppress newlines in pretty-printed HTML output. | `false` |
191
+ | Name | Description | Default |
192
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------ |
193
+ | `hardbreaks` | [Soft line breaks](http://spec.commonmark.org/0.27/#soft-line-breaks) translate into hard line breaks. | `true` |
194
+ | `github_pre_lang` | GitHub-style `<pre lang="xyz">` is used for fenced code blocks with info tags. | `true` |
195
+ | `full_info_string` | Gives info string data after a space in a `data-meta` attribute on code blocks. | `false` |
196
+ | `width` | The wrap column when outputting CommonMark. | `80` |
197
+ | `unsafe` | Allow rendering of raw HTML and potentially dangerous links. | `false` |
198
+ | `escape` | Escape raw HTML instead of clobbering it. | `false` |
199
+ | `sourcepos` | Include source position attribute in HTML and XML output. | `false` |
200
+ | `escaped_char_spans` | Wrap escaped characters in span tags. | `true` |
201
+ | `ignore_empty_links` | Ignores empty links, leaving the Markdown text in place. | `false` |
202
+ | `gfm_quirks` | Outputs HTML with GFM-style quirks; namely, not nesting `<strong>` inlines. | `false` |
203
+ | `prefer_fenced` | Always output fenced code blocks, even where an indented one could be used. | `false` |
204
+ | `tasklist_classes` | Add CSS classes to the HTML output of the tasklist extension | `false` |
205
+ | `compact_html` | Suppress newlines in pretty-printed HTML output. | `false` |
187
206
  | `alert_style` | The style of alert output: `"specific"` (`<div class="markdown-alert">`) or `"semantic"` (`<aside class="admonition">`). | `"specific"` |
188
207
 
189
208
  As well, there are several extensions which you can toggle in the same manner:
@@ -200,7 +219,6 @@ Commonmarker.to_html('"Hi *there*"', options: {
200
219
  | Name | Description | Default |
201
220
  | ----------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------- |
202
221
  | `strikethrough` | Enables the [strikethrough extension](https://github.github.com/gfm/#strikethrough-extension-) from the GFM spec. | `true` |
203
- | `tagfilter` | Enables the [tagfilter extension](https://github.github.com/gfm/#disallowed-raw-html-extension-) from the GFM spec. | `true` |
204
222
  | `table` | Enables the [table extension](https://github.github.com/gfm/#tables-extension-) from the GFM spec. | `true` |
205
223
  | `autolink` | Enables the [autolink extension](https://github.github.com/gfm/#autolinks-extension-) from the GFM spec. | `true` |
206
224
  | `tasklist` | Enables the [task list extension](https://github.github.com/gfm/#task-list-items-extension-) from the GFM spec. | `true` |
@@ -6,12 +6,11 @@ rust-version = "1.75.0"
6
6
  publish = false
7
7
 
8
8
  [dependencies]
9
- magnus = { version = "0.8", features = ["rb-sys"] }
9
+ magnus = { version = "0.9", features = ["rb-sys"] }
10
10
  rb-sys = { version = "*", default-features = false, features = [
11
11
  "stable-api-compiled-fallback",
12
12
  ] }
13
13
  typed-arena = "2.0"
14
- rctree = "0.6"
15
14
  rb-allocator = "0.9"
16
15
 
17
16
  syntect = { version = "5.3", default-features = false, features = [
@@ -21,7 +20,7 @@ syntect = { version = "5.3", default-features = false, features = [
21
20
 
22
21
  # Everywhere except Windows: Oniguruma, the faster engine.
23
22
  [target.'cfg(not(windows))'.dependencies]
24
- comrak = { version = "0.54", default-features = false, features = [
23
+ comrak = { version = "0.56", default-features = false, features = [
25
24
  "shortcodes",
26
25
  "syntect-onig",
27
26
  ] }
@@ -30,7 +29,7 @@ comrak = { version = "0.54", default-features = false, features = [
30
29
  # linking a second copy (onig_sys) collides at link time and segfaults (xref #467).
31
30
  # syntect-fancy swaps in the pure-Rust fancy-regex backend instead.
32
31
  [target.'cfg(windows)'.dependencies]
33
- comrak = { version = "0.54", default-features = false, features = [
32
+ comrak = { version = "0.56", default-features = false, features = [
34
33
  "shortcodes",
35
34
  "syntect-fancy",
36
35
  ] }
@@ -16,7 +16,9 @@ use typed_arena::Arena;
16
16
 
17
17
  use crate::options::{iterate_extension_options, iterate_parse_options, iterate_render_options};
18
18
 
19
+ mod nesting;
19
20
  mod node;
21
+ mod rctree;
20
22
  mod utils;
21
23
 
22
24
  pub const EMPTY_STR: &str = "";
@@ -55,6 +57,8 @@ fn commonmark_parse(ruby: &Ruby, args: &[Value]) -> Result<CommonmarkerNode, mag
55
57
  )?;
56
58
  let (rb_parse, rb_render, rb_extension) = kwargs.optional;
57
59
 
60
+ let max_nesting_depth = nesting::max_nesting_depth(ruby, rb_parse)?;
61
+
58
62
  let mut comrak_parse_options = comrak::options::Parse::default();
59
63
  let mut comrak_render_options = comrak::options::Render::default();
60
64
  let mut comrak_extension_options = comrak::options::Extension::default();
@@ -78,7 +82,7 @@ fn commonmark_parse(ruby: &Ruby, args: &[Value]) -> Result<CommonmarkerNode, mag
78
82
  let arena = Arena::new();
79
83
  let root = parse_document(&arena, commonmark_str, &comrak_options);
80
84
 
81
- CommonmarkerNode::new_from_comrak_node(root)
85
+ CommonmarkerNode::new_from_comrak_node(ruby, root, max_nesting_depth)
82
86
  }
83
87
 
84
88
  fn commonmark_to_html(ruby: &Ruby, args: &[Value]) -> Result<RString, magnus::Error> {
@@ -107,6 +111,8 @@ fn commonmark_to_html(ruby: &Ruby, args: &[Value]) -> Result<RString, magnus::Er
107
111
  )?;
108
112
  let (rb_render, rb_parse, rb_extension, rb_plugins) = kwargs.optional;
109
113
 
114
+ let max_nesting_depth = nesting::max_nesting_depth(ruby, rb_parse)?;
115
+
110
116
  let mut comrak_parse_options = comrak::options::Parse::default();
111
117
  let mut comrak_render_options = comrak::options::Render::default();
112
118
  let mut comrak_extension_options = comrak::options::Extension::default();
@@ -130,7 +136,9 @@ fn commonmark_to_html(ruby: &Ruby, args: &[Value]) -> Result<RString, magnus::Er
130
136
  };
131
137
 
132
138
  match syntect_adapter {
133
- Some(ref adapter) => comrak_plugins.render.codefence_syntax_highlighter = Some(adapter),
139
+ Some(ref adapter) => {
140
+ comrak_plugins.render.codefence_syntax_highlighter = Some(adapter.as_ref())
141
+ }
134
142
  None => comrak_plugins.render.codefence_syntax_highlighter = None,
135
143
  }
136
144
 
@@ -148,6 +156,8 @@ fn commonmark_to_html(ruby: &Ruby, args: &[Value]) -> Result<RString, magnus::Er
148
156
  let arena = Arena::new();
149
157
  let root = parse_document(&arena, commonmark_str, &comrak_options);
150
158
 
159
+ nesting::check(ruby, root, max_nesting_depth)?;
160
+
151
161
  comrak::html::format_document_with_plugins(root, &comrak_options, &mut writer, &comrak_plugins)
152
162
  .map_err(|e| magnus::Error::new(ruby.exception_runtime_error(), e.to_string()))?;
153
163
 
@@ -0,0 +1,71 @@
1
+ use comrak::arena_tree::NodeEdge;
2
+ use comrak::nodes::AstNode;
3
+ use magnus::value::ReprValue;
4
+ use magnus::{ExceptionClass, Module, RHash, Ruby, TryConvert};
5
+
6
+ /// Default for the `max_nesting_depth` parse option. Mirrors
7
+ /// `Commonmarker::Config::OPTIONS[:parse][:max_nesting_depth]`, and also applies
8
+ /// when the option is passed as `nil`, which `Commonmarker::Config` strips.
9
+ pub const DEFAULT_MAX_NESTING_DEPTH: usize = 1000;
10
+
11
+ const PARSE_MAX_NESTING_DEPTH: &str = "max_nesting_depth";
12
+
13
+ /// Reads the `max_nesting_depth` parse option, falling back to the default.
14
+ pub fn max_nesting_depth(ruby: &Ruby, rb_parse: Option<RHash>) -> Result<usize, magnus::Error> {
15
+ let value = rb_parse.and_then(|options| options.get(ruby.to_symbol(PARSE_MAX_NESTING_DEPTH)));
16
+
17
+ match value {
18
+ Some(value) if !value.is_nil() => {
19
+ let depth: i64 = TryConvert::try_convert(value)?;
20
+ usize::try_from(depth).map_err(|_| {
21
+ magnus::Error::new(
22
+ ruby.exception_arg_error(),
23
+ format!("parse option `:max_nesting_depth` must be a non-negative Integer; got {depth}"),
24
+ )
25
+ })
26
+ }
27
+ _ => Ok(DEFAULT_MAX_NESTING_DEPTH),
28
+ }
29
+ }
30
+
31
+ /// The error raised when a document nests deeper than `max_nesting_depth`.
32
+ pub fn exceeded_error(ruby: &Ruby, max_nesting_depth: usize) -> magnus::Error {
33
+ let class = ruby
34
+ .define_module("Commonmarker")
35
+ .and_then(|module| module.const_get::<_, ExceptionClass>("NestingDepthExceededError"));
36
+
37
+ match class {
38
+ Ok(class) => magnus::Error::new(
39
+ class,
40
+ format!(
41
+ "nesting depth exceeds the max_nesting_depth parse option ({max_nesting_depth})"
42
+ ),
43
+ ),
44
+ Err(error) => error,
45
+ }
46
+ }
47
+
48
+ /// Fails if any node sits more than `max_nesting_depth` levels below the
49
+ /// document root. `traverse` is iterative, so this is safe on arbitrarily deep
50
+ /// trees.
51
+ pub fn check<'a>(
52
+ ruby: &Ruby,
53
+ root: &'a AstNode<'a>,
54
+ max_nesting_depth: usize,
55
+ ) -> Result<(), magnus::Error> {
56
+ let mut depth: usize = 0;
57
+
58
+ for edge in root.traverse() {
59
+ match edge {
60
+ NodeEdge::Start(_) => {
61
+ if depth > max_nesting_depth {
62
+ return Err(exceeded_error(ruby, max_nesting_depth));
63
+ }
64
+ depth += 1;
65
+ }
66
+ NodeEdge::End(_) => depth -= 1,
67
+ }
68
+ }
69
+
70
+ Ok(())
71
+ }