inkmark 0.1.4 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Cargo.lock +126 -107
- data/README.md +25 -3
- data/ext/inkmark/Cargo.toml +4 -4
- data/ext/inkmark/src/lib.rs +28 -3
- data/lib/inkmark/options.rb +97 -36
- data/lib/inkmark/version.rb +1 -1
- data/lib/inkmark.rb +70 -24
- data/sig/inkmark.rbs +1 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 426fe02b791d8fcf0b3d080cea874692f3027eb1528e07300de6685b869108e3
|
|
4
|
+
data.tar.gz: b1e8947dc0811dfcd70bc29b62c34c95536526f512941e73bd66664d5c5b7473
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 07d38e9bf03538f52a444d033fa48648bd235673c3c435e7137bd5435b31e82dce00b758b038840577540d451a1d8d82bbd8660d9be99429b0a7ed35a666b4b3
|
|
7
|
+
data.tar.gz: 9898e5c9780a4ea2423861f62b3054459d7c3dadad7ae9fb999fe3b5416a51a513df9b7d428f1a4af5093a67cc91eb86f7be5fc2c3054d9456b805a65e7b4438
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [0.2.0] - 2026-09-06
|
|
2
|
+
|
|
3
|
+
- Ractor-safe: the native extension declares `rb_ext_ractor_safe`, and every public API works from non-main Ractors.
|
|
4
|
+
- `Inkmark.default_options` is now frozen; configure process-wide defaults with `Inkmark.configure { |o| ... }` or `Inkmark.default_options=`.
|
|
5
|
+
- Load YAML on the first `frontmatter` parse instead of at require time.
|
|
6
|
+
- Updated dependencies.
|
|
7
|
+
|
|
1
8
|
## [0.1.4] - 2026-06-25
|
|
2
9
|
|
|
3
10
|
- Fix `frontmatter: true` leaking the frontmatter block into `to_markdown`, `chunks_by_heading`, and `chunks_by_size` output. Bug report by @freesteph [#3]
|
data/Cargo.lock
CHANGED
|
@@ -10,9 +10,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
|
|
10
10
|
|
|
11
11
|
[[package]]
|
|
12
12
|
name = "aho-corasick"
|
|
13
|
-
version = "1.1.
|
|
13
|
+
version = "1.1.5"
|
|
14
14
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
-
checksum = "
|
|
15
|
+
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
|
16
16
|
dependencies = [
|
|
17
17
|
"memchr",
|
|
18
18
|
]
|
|
@@ -47,7 +47,7 @@ dependencies = [
|
|
|
47
47
|
"regex",
|
|
48
48
|
"rustc-hash",
|
|
49
49
|
"shlex",
|
|
50
|
-
"syn",
|
|
50
|
+
"syn 2.0.119",
|
|
51
51
|
]
|
|
52
52
|
|
|
53
53
|
[[package]]
|
|
@@ -67,18 +67,18 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
|
|
67
67
|
|
|
68
68
|
[[package]]
|
|
69
69
|
name = "bitflags"
|
|
70
|
-
version = "2.13.
|
|
70
|
+
version = "2.13.1"
|
|
71
71
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
72
|
-
checksum = "
|
|
72
|
+
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
|
73
73
|
|
|
74
74
|
[[package]]
|
|
75
75
|
name = "bstr"
|
|
76
|
-
version = "1.
|
|
76
|
+
version = "1.13.1"
|
|
77
77
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
78
|
-
checksum = "
|
|
78
|
+
checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
|
|
79
79
|
dependencies = [
|
|
80
80
|
"memchr",
|
|
81
|
-
"
|
|
81
|
+
"serde_core",
|
|
82
82
|
]
|
|
83
83
|
|
|
84
84
|
[[package]]
|
|
@@ -98,9 +98,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
|
98
98
|
|
|
99
99
|
[[package]]
|
|
100
100
|
name = "clang-sys"
|
|
101
|
-
version = "1.
|
|
101
|
+
version = "1.9.1"
|
|
102
102
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
103
|
-
checksum = "
|
|
103
|
+
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
|
|
104
104
|
dependencies = [
|
|
105
105
|
"glob",
|
|
106
106
|
"libc",
|
|
@@ -109,9 +109,9 @@ dependencies = [
|
|
|
109
109
|
|
|
110
110
|
[[package]]
|
|
111
111
|
name = "crc32fast"
|
|
112
|
-
version = "1.5.
|
|
112
|
+
version = "1.5.1"
|
|
113
113
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
114
|
-
checksum = "
|
|
114
|
+
checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
|
|
115
115
|
dependencies = [
|
|
116
116
|
"cfg-if",
|
|
117
117
|
]
|
|
@@ -124,26 +124,26 @@ checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
|
|
|
124
124
|
|
|
125
125
|
[[package]]
|
|
126
126
|
name = "displaydoc"
|
|
127
|
-
version = "0.2.
|
|
127
|
+
version = "0.2.7"
|
|
128
128
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
129
|
-
checksum = "
|
|
129
|
+
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
|
130
130
|
dependencies = [
|
|
131
131
|
"proc-macro2",
|
|
132
132
|
"quote",
|
|
133
|
-
"syn",
|
|
133
|
+
"syn 3.0.5",
|
|
134
134
|
]
|
|
135
135
|
|
|
136
136
|
[[package]]
|
|
137
137
|
name = "either"
|
|
138
|
-
version = "1.
|
|
138
|
+
version = "1.18.0"
|
|
139
139
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
140
|
-
checksum = "
|
|
140
|
+
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
|
141
141
|
|
|
142
142
|
[[package]]
|
|
143
143
|
name = "emojis"
|
|
144
|
-
version = "0.
|
|
144
|
+
version = "0.9.0"
|
|
145
145
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
146
|
-
checksum = "
|
|
146
|
+
checksum = "0b1514ced566c94991ed9563258ecf61e311fd773bf0a3f20606830386bf66e3"
|
|
147
147
|
dependencies = [
|
|
148
148
|
"phf",
|
|
149
149
|
]
|
|
@@ -167,12 +167,13 @@ dependencies = [
|
|
|
167
167
|
|
|
168
168
|
[[package]]
|
|
169
169
|
name = "flate2"
|
|
170
|
-
version = "1.1.
|
|
170
|
+
version = "1.1.10"
|
|
171
171
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
172
|
-
checksum = "
|
|
172
|
+
checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
|
|
173
173
|
dependencies = [
|
|
174
174
|
"crc32fast",
|
|
175
175
|
"miniz_oxide",
|
|
176
|
+
"zlib-rs",
|
|
176
177
|
]
|
|
177
178
|
|
|
178
179
|
[[package]]
|
|
@@ -198,15 +199,15 @@ dependencies = [
|
|
|
198
199
|
|
|
199
200
|
[[package]]
|
|
200
201
|
name = "glob"
|
|
201
|
-
version = "0.3.
|
|
202
|
+
version = "0.3.4"
|
|
202
203
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
203
|
-
checksum = "
|
|
204
|
+
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
|
204
205
|
|
|
205
206
|
[[package]]
|
|
206
207
|
name = "globset"
|
|
207
|
-
version = "0.4.
|
|
208
|
+
version = "0.4.20"
|
|
208
209
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
209
|
-
checksum = "
|
|
210
|
+
checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988"
|
|
210
211
|
dependencies = [
|
|
211
212
|
"aho-corasick",
|
|
212
213
|
"bstr",
|
|
@@ -228,9 +229,9 @@ dependencies = [
|
|
|
228
229
|
|
|
229
230
|
[[package]]
|
|
230
231
|
name = "icu_collections"
|
|
231
|
-
version = "2.
|
|
232
|
+
version = "2.3.0"
|
|
232
233
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
233
|
-
checksum = "
|
|
234
|
+
checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
|
|
234
235
|
dependencies = [
|
|
235
236
|
"displaydoc",
|
|
236
237
|
"potential_utf",
|
|
@@ -242,9 +243,9 @@ dependencies = [
|
|
|
242
243
|
|
|
243
244
|
[[package]]
|
|
244
245
|
name = "icu_locale_core"
|
|
245
|
-
version = "2.
|
|
246
|
+
version = "2.3.0"
|
|
246
247
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
247
|
-
checksum = "
|
|
248
|
+
checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
|
|
248
249
|
dependencies = [
|
|
249
250
|
"displaydoc",
|
|
250
251
|
"litemap",
|
|
@@ -255,9 +256,9 @@ dependencies = [
|
|
|
255
256
|
|
|
256
257
|
[[package]]
|
|
257
258
|
name = "icu_normalizer"
|
|
258
|
-
version = "2.
|
|
259
|
+
version = "2.3.0"
|
|
259
260
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
260
|
-
checksum = "
|
|
261
|
+
checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
|
|
261
262
|
dependencies = [
|
|
262
263
|
"icu_collections",
|
|
263
264
|
"icu_normalizer_data",
|
|
@@ -269,16 +270,17 @@ dependencies = [
|
|
|
269
270
|
|
|
270
271
|
[[package]]
|
|
271
272
|
name = "icu_normalizer_data"
|
|
272
|
-
version = "2.
|
|
273
|
+
version = "2.3.0"
|
|
273
274
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
274
|
-
checksum = "
|
|
275
|
+
checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
|
|
275
276
|
|
|
276
277
|
[[package]]
|
|
277
278
|
name = "icu_properties"
|
|
278
|
-
version = "2.
|
|
279
|
+
version = "2.3.0"
|
|
279
280
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
280
|
-
checksum = "
|
|
281
|
+
checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
|
|
281
282
|
dependencies = [
|
|
283
|
+
"displaydoc",
|
|
282
284
|
"icu_collections",
|
|
283
285
|
"icu_locale_core",
|
|
284
286
|
"icu_properties_data",
|
|
@@ -289,15 +291,15 @@ dependencies = [
|
|
|
289
291
|
|
|
290
292
|
[[package]]
|
|
291
293
|
name = "icu_properties_data"
|
|
292
|
-
version = "2.
|
|
294
|
+
version = "2.3.0"
|
|
293
295
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
294
|
-
checksum = "
|
|
296
|
+
checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
|
|
295
297
|
|
|
296
298
|
[[package]]
|
|
297
299
|
name = "icu_provider"
|
|
298
|
-
version = "2.
|
|
300
|
+
version = "2.3.1"
|
|
299
301
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
300
|
-
checksum = "
|
|
302
|
+
checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
|
|
301
303
|
dependencies = [
|
|
302
304
|
"displaydoc",
|
|
303
305
|
"icu_locale_core",
|
|
@@ -331,7 +333,7 @@ dependencies = [
|
|
|
331
333
|
|
|
332
334
|
[[package]]
|
|
333
335
|
name = "inkmark"
|
|
334
|
-
version = "0.
|
|
336
|
+
version = "0.2.0"
|
|
335
337
|
dependencies = [
|
|
336
338
|
"deunicode",
|
|
337
339
|
"emojis",
|
|
@@ -367,9 +369,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
|
367
369
|
|
|
368
370
|
[[package]]
|
|
369
371
|
name = "libc"
|
|
370
|
-
version = "0.2.
|
|
372
|
+
version = "0.2.189"
|
|
371
373
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
372
|
-
checksum = "
|
|
374
|
+
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
|
373
375
|
|
|
374
376
|
[[package]]
|
|
375
377
|
name = "libloading"
|
|
@@ -392,15 +394,15 @@ dependencies = [
|
|
|
392
394
|
|
|
393
395
|
[[package]]
|
|
394
396
|
name = "litemap"
|
|
395
|
-
version = "0.8.
|
|
397
|
+
version = "0.8.3"
|
|
396
398
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
397
|
-
checksum = "
|
|
399
|
+
checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
|
|
398
400
|
|
|
399
401
|
[[package]]
|
|
400
402
|
name = "log"
|
|
401
|
-
version = "0.4.
|
|
403
|
+
version = "0.4.34"
|
|
402
404
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
403
|
-
checksum = "
|
|
405
|
+
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
|
404
406
|
|
|
405
407
|
[[package]]
|
|
406
408
|
name = "magnus"
|
|
@@ -422,14 +424,14 @@ checksum = "47607461fd8e1513cb4f2076c197d8092d921a1ea75bd08af97398f593751892"
|
|
|
422
424
|
dependencies = [
|
|
423
425
|
"proc-macro2",
|
|
424
426
|
"quote",
|
|
425
|
-
"syn",
|
|
427
|
+
"syn 2.0.119",
|
|
426
428
|
]
|
|
427
429
|
|
|
428
430
|
[[package]]
|
|
429
431
|
name = "memchr"
|
|
430
|
-
version = "2.8.
|
|
432
|
+
version = "2.8.3"
|
|
431
433
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
432
|
-
checksum = "
|
|
434
|
+
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
433
435
|
|
|
434
436
|
[[package]]
|
|
435
437
|
name = "minimal-lexical"
|
|
@@ -439,9 +441,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|
|
439
441
|
|
|
440
442
|
[[package]]
|
|
441
443
|
name = "miniz_oxide"
|
|
442
|
-
version = "0.
|
|
444
|
+
version = "0.9.1"
|
|
443
445
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
444
|
-
checksum = "
|
|
446
|
+
checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
|
|
445
447
|
dependencies = [
|
|
446
448
|
"adler2",
|
|
447
449
|
"simd-adler32",
|
|
@@ -489,18 +491,18 @@ dependencies = [
|
|
|
489
491
|
|
|
490
492
|
[[package]]
|
|
491
493
|
name = "potential_utf"
|
|
492
|
-
version = "0.1.
|
|
494
|
+
version = "0.1.6"
|
|
493
495
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
494
|
-
checksum = "
|
|
496
|
+
checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661"
|
|
495
497
|
dependencies = [
|
|
496
498
|
"zerovec",
|
|
497
499
|
]
|
|
498
500
|
|
|
499
501
|
[[package]]
|
|
500
502
|
name = "proc-macro2"
|
|
501
|
-
version = "1.0.
|
|
503
|
+
version = "1.0.107"
|
|
502
504
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
503
|
-
checksum = "
|
|
505
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
504
506
|
dependencies = [
|
|
505
507
|
"unicode-ident",
|
|
506
508
|
]
|
|
@@ -525,36 +527,36 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
|
|
|
525
527
|
|
|
526
528
|
[[package]]
|
|
527
529
|
name = "pulldown-cmark-to-cmark"
|
|
528
|
-
version = "22.0.
|
|
530
|
+
version = "22.0.1"
|
|
529
531
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
530
|
-
checksum = "
|
|
532
|
+
checksum = "ab1ad36992cead65f02aa399a373a42730922f1525d988172634fdefdecb8a60"
|
|
531
533
|
dependencies = [
|
|
532
534
|
"pulldown-cmark",
|
|
533
535
|
]
|
|
534
536
|
|
|
535
537
|
[[package]]
|
|
536
538
|
name = "quote"
|
|
537
|
-
version = "1.0.
|
|
539
|
+
version = "1.0.47"
|
|
538
540
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
539
|
-
checksum = "
|
|
541
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
540
542
|
dependencies = [
|
|
541
543
|
"proc-macro2",
|
|
542
544
|
]
|
|
543
545
|
|
|
544
546
|
[[package]]
|
|
545
547
|
name = "rb-sys"
|
|
546
|
-
version = "0.9.
|
|
548
|
+
version = "0.9.130"
|
|
547
549
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
548
|
-
checksum = "
|
|
550
|
+
checksum = "02faf625bb10ba893e3ae620f19c9fb1b5f8fcae0fe4eb86bb3f2230fad75edb"
|
|
549
551
|
dependencies = [
|
|
550
552
|
"rb-sys-build",
|
|
551
553
|
]
|
|
552
554
|
|
|
553
555
|
[[package]]
|
|
554
556
|
name = "rb-sys-build"
|
|
555
|
-
version = "0.9.
|
|
557
|
+
version = "0.9.130"
|
|
556
558
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
557
|
-
checksum = "
|
|
559
|
+
checksum = "05be6f9c86fe5482808162826f6c047d093b3670582296c770de1d94f8066694"
|
|
558
560
|
dependencies = [
|
|
559
561
|
"bindgen",
|
|
560
562
|
"lazy_static",
|
|
@@ -562,7 +564,7 @@ dependencies = [
|
|
|
562
564
|
"quote",
|
|
563
565
|
"regex",
|
|
564
566
|
"shell-words",
|
|
565
|
-
"syn",
|
|
567
|
+
"syn 2.0.119",
|
|
566
568
|
]
|
|
567
569
|
|
|
568
570
|
[[package]]
|
|
@@ -573,9 +575,9 @@ checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a"
|
|
|
573
575
|
|
|
574
576
|
[[package]]
|
|
575
577
|
name = "rb-sys-test-helpers"
|
|
576
|
-
version = "0.
|
|
578
|
+
version = "0.3.0"
|
|
577
579
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
578
|
-
checksum = "
|
|
580
|
+
checksum = "ccc637c53cad6a6b49ad51c5857c64e36bd70c955ffafe9dc9cc5bc9dd0b3b18"
|
|
579
581
|
dependencies = [
|
|
580
582
|
"rb-sys",
|
|
581
583
|
"rb-sys-env",
|
|
@@ -584,19 +586,19 @@ dependencies = [
|
|
|
584
586
|
|
|
585
587
|
[[package]]
|
|
586
588
|
name = "rb-sys-test-helpers-macros"
|
|
587
|
-
version = "0.
|
|
589
|
+
version = "0.3.0"
|
|
588
590
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
589
|
-
checksum = "
|
|
591
|
+
checksum = "b1c1fce35d9ac77a2745e539232fb82a75856675a5daf4ef8b1ca994d89f8b3f"
|
|
590
592
|
dependencies = [
|
|
591
593
|
"quote",
|
|
592
|
-
"syn",
|
|
594
|
+
"syn 2.0.119",
|
|
593
595
|
]
|
|
594
596
|
|
|
595
597
|
[[package]]
|
|
596
598
|
name = "regex"
|
|
597
|
-
version = "1.
|
|
599
|
+
version = "1.13.1"
|
|
598
600
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
599
|
-
checksum = "
|
|
601
|
+
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
|
600
602
|
dependencies = [
|
|
601
603
|
"aho-corasick",
|
|
602
604
|
"memchr",
|
|
@@ -606,9 +608,9 @@ dependencies = [
|
|
|
606
608
|
|
|
607
609
|
[[package]]
|
|
608
610
|
name = "regex-automata"
|
|
609
|
-
version = "0.4.
|
|
611
|
+
version = "0.4.18"
|
|
610
612
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
611
|
-
checksum = "
|
|
613
|
+
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
|
612
614
|
dependencies = [
|
|
613
615
|
"aho-corasick",
|
|
614
616
|
"memchr",
|
|
@@ -623,9 +625,9 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
|
623
625
|
|
|
624
626
|
[[package]]
|
|
625
627
|
name = "rustc-hash"
|
|
626
|
-
version = "2.1.
|
|
628
|
+
version = "2.1.3"
|
|
627
629
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
628
|
-
checksum = "
|
|
630
|
+
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
|
629
631
|
|
|
630
632
|
[[package]]
|
|
631
633
|
name = "same-file"
|
|
@@ -644,31 +646,31 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
|
|
644
646
|
|
|
645
647
|
[[package]]
|
|
646
648
|
name = "serde"
|
|
647
|
-
version = "1.0.
|
|
649
|
+
version = "1.0.229"
|
|
648
650
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
649
|
-
checksum = "
|
|
651
|
+
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
|
650
652
|
dependencies = [
|
|
651
653
|
"serde_core",
|
|
652
654
|
]
|
|
653
655
|
|
|
654
656
|
[[package]]
|
|
655
657
|
name = "serde_core"
|
|
656
|
-
version = "1.0.
|
|
658
|
+
version = "1.0.229"
|
|
657
659
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
658
|
-
checksum = "
|
|
660
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
659
661
|
dependencies = [
|
|
660
662
|
"serde_derive",
|
|
661
663
|
]
|
|
662
664
|
|
|
663
665
|
[[package]]
|
|
664
666
|
name = "serde_derive"
|
|
665
|
-
version = "1.0.
|
|
667
|
+
version = "1.0.229"
|
|
666
668
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
667
|
-
checksum = "
|
|
669
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
668
670
|
dependencies = [
|
|
669
671
|
"proc-macro2",
|
|
670
672
|
"quote",
|
|
671
|
-
"syn",
|
|
673
|
+
"syn 3.0.5",
|
|
672
674
|
]
|
|
673
675
|
|
|
674
676
|
[[package]]
|
|
@@ -685,9 +687,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
|
685
687
|
|
|
686
688
|
[[package]]
|
|
687
689
|
name = "simd-adler32"
|
|
688
|
-
version = "0.3.
|
|
690
|
+
version = "0.3.10"
|
|
689
691
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
690
|
-
checksum = "
|
|
692
|
+
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
|
691
693
|
|
|
692
694
|
[[package]]
|
|
693
695
|
name = "siphasher"
|
|
@@ -697,9 +699,9 @@ checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
|
|
|
697
699
|
|
|
698
700
|
[[package]]
|
|
699
701
|
name = "smallvec"
|
|
700
|
-
version = "1.
|
|
702
|
+
version = "1.16.0"
|
|
701
703
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
702
|
-
checksum = "
|
|
704
|
+
checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f"
|
|
703
705
|
|
|
704
706
|
[[package]]
|
|
705
707
|
name = "stable_deref_trait"
|
|
@@ -709,9 +711,20 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
|
|
709
711
|
|
|
710
712
|
[[package]]
|
|
711
713
|
name = "syn"
|
|
712
|
-
version = "2.0.
|
|
714
|
+
version = "2.0.119"
|
|
715
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
716
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
717
|
+
dependencies = [
|
|
718
|
+
"proc-macro2",
|
|
719
|
+
"quote",
|
|
720
|
+
"unicode-ident",
|
|
721
|
+
]
|
|
722
|
+
|
|
723
|
+
[[package]]
|
|
724
|
+
name = "syn"
|
|
725
|
+
version = "3.0.5"
|
|
713
726
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
714
|
-
checksum = "
|
|
727
|
+
checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
|
|
715
728
|
dependencies = [
|
|
716
729
|
"proc-macro2",
|
|
717
730
|
"quote",
|
|
@@ -726,7 +739,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|
|
726
739
|
dependencies = [
|
|
727
740
|
"proc-macro2",
|
|
728
741
|
"quote",
|
|
729
|
-
"syn",
|
|
742
|
+
"syn 2.0.119",
|
|
730
743
|
]
|
|
731
744
|
|
|
732
745
|
[[package]]
|
|
@@ -749,29 +762,29 @@ dependencies = [
|
|
|
749
762
|
|
|
750
763
|
[[package]]
|
|
751
764
|
name = "thiserror"
|
|
752
|
-
version = "2.0.
|
|
765
|
+
version = "2.0.20"
|
|
753
766
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
754
|
-
checksum = "
|
|
767
|
+
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
|
755
768
|
dependencies = [
|
|
756
769
|
"thiserror-impl",
|
|
757
770
|
]
|
|
758
771
|
|
|
759
772
|
[[package]]
|
|
760
773
|
name = "thiserror-impl"
|
|
761
|
-
version = "2.0.
|
|
774
|
+
version = "2.0.20"
|
|
762
775
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
763
|
-
checksum = "
|
|
776
|
+
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
|
|
764
777
|
dependencies = [
|
|
765
778
|
"proc-macro2",
|
|
766
779
|
"quote",
|
|
767
|
-
"syn",
|
|
780
|
+
"syn 3.0.5",
|
|
768
781
|
]
|
|
769
782
|
|
|
770
783
|
[[package]]
|
|
771
784
|
name = "tinystr"
|
|
772
|
-
version = "0.8.
|
|
785
|
+
version = "0.8.4"
|
|
773
786
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
774
|
-
checksum = "
|
|
787
|
+
checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
|
|
775
788
|
dependencies = [
|
|
776
789
|
"displaydoc",
|
|
777
790
|
"zerovec",
|
|
@@ -858,9 +871,9 @@ dependencies = [
|
|
|
858
871
|
|
|
859
872
|
[[package]]
|
|
860
873
|
name = "writeable"
|
|
861
|
-
version = "0.6.
|
|
874
|
+
version = "0.6.4"
|
|
862
875
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
863
|
-
checksum = "
|
|
876
|
+
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
|
864
877
|
|
|
865
878
|
[[package]]
|
|
866
879
|
name = "yoke"
|
|
@@ -881,7 +894,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
|
|
|
881
894
|
dependencies = [
|
|
882
895
|
"proc-macro2",
|
|
883
896
|
"quote",
|
|
884
|
-
"syn",
|
|
897
|
+
"syn 2.0.119",
|
|
885
898
|
"synstructure",
|
|
886
899
|
]
|
|
887
900
|
|
|
@@ -902,15 +915,15 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
|
|
|
902
915
|
dependencies = [
|
|
903
916
|
"proc-macro2",
|
|
904
917
|
"quote",
|
|
905
|
-
"syn",
|
|
918
|
+
"syn 2.0.119",
|
|
906
919
|
"synstructure",
|
|
907
920
|
]
|
|
908
921
|
|
|
909
922
|
[[package]]
|
|
910
923
|
name = "zerotrie"
|
|
911
|
-
version = "0.2.
|
|
924
|
+
version = "0.2.5"
|
|
912
925
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
913
|
-
checksum = "
|
|
926
|
+
checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f"
|
|
914
927
|
dependencies = [
|
|
915
928
|
"displaydoc",
|
|
916
929
|
"yoke",
|
|
@@ -919,9 +932,9 @@ dependencies = [
|
|
|
919
932
|
|
|
920
933
|
[[package]]
|
|
921
934
|
name = "zerovec"
|
|
922
|
-
version = "0.11.
|
|
935
|
+
version = "0.11.8"
|
|
923
936
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
924
|
-
checksum = "
|
|
937
|
+
checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
|
|
925
938
|
dependencies = [
|
|
926
939
|
"yoke",
|
|
927
940
|
"zerofrom",
|
|
@@ -930,11 +943,17 @@ dependencies = [
|
|
|
930
943
|
|
|
931
944
|
[[package]]
|
|
932
945
|
name = "zerovec-derive"
|
|
933
|
-
version = "0.11.
|
|
946
|
+
version = "0.11.6"
|
|
934
947
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
935
|
-
checksum = "
|
|
948
|
+
checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
|
|
936
949
|
dependencies = [
|
|
937
950
|
"proc-macro2",
|
|
938
951
|
"quote",
|
|
939
|
-
"syn",
|
|
952
|
+
"syn 3.0.5",
|
|
940
953
|
]
|
|
954
|
+
|
|
955
|
+
[[package]]
|
|
956
|
+
name = "zlib-rs"
|
|
957
|
+
version = "0.6.7"
|
|
958
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
959
|
+
checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
|
data/README.md
CHANGED
|
@@ -38,6 +38,7 @@ A very fast, feature-packed, AI-first Markdown gem for Ruby.
|
|
|
38
38
|
- [Plain-text extraction](#plain-text-extraction)
|
|
39
39
|
- [Markdown-to-Markdown pipeline](#markdown-to-markdown-pipeline)
|
|
40
40
|
- [Event handlers](#event-handlers)
|
|
41
|
+
- [Ractors](#ractors)
|
|
41
42
|
- [Benchmarks](#benchmarks)
|
|
42
43
|
- [Contributing](#contributing)
|
|
43
44
|
- [Acknowledgements](#acknowledgements)
|
|
@@ -177,9 +178,15 @@ g.options.math = true
|
|
|
177
178
|
g.options.tables = false
|
|
178
179
|
g.to_html
|
|
179
180
|
|
|
180
|
-
# Process-
|
|
181
|
-
Inkmark.
|
|
182
|
-
|
|
181
|
+
# Process-wide defaults, set once in your application initializer
|
|
182
|
+
Inkmark.configure do |options|
|
|
183
|
+
options.math = true
|
|
184
|
+
options.links = { nofollow: true }
|
|
185
|
+
end
|
|
186
|
+
Inkmark.new(md).to_html # picks up the defaults
|
|
187
|
+
|
|
188
|
+
# Or replace them wholesale
|
|
189
|
+
Inkmark.default_options = { preset: :recommended, math: true }
|
|
183
190
|
```
|
|
184
191
|
|
|
185
192
|
Unknown option keys raise `ArgumentError` immediately, including via the
|
|
@@ -1029,6 +1036,21 @@ Post-render filters (`syntax_highlight`, allowlists, `images: { lazy: true }`,
|
|
|
1029
1036
|
highlighter
|
|
1030
1037
|
- Handler-set `dest=` values pass through host and scheme allowlists
|
|
1031
1038
|
|
|
1039
|
+
## Ractors
|
|
1040
|
+
|
|
1041
|
+
Inkmark is Ractor-safe: every public API works inside non-main Ractors,
|
|
1042
|
+
so documents can be rendered in parallel without leaving the native fast
|
|
1043
|
+
path.
|
|
1044
|
+
|
|
1045
|
+
```ruby
|
|
1046
|
+
workers = sources.each_slice(250).map do |slice|
|
|
1047
|
+
Ractor.new(slice) do |batch|
|
|
1048
|
+
batch.map { |src| Inkmark.to_html(src, options: { preset: :recommended }) }
|
|
1049
|
+
end
|
|
1050
|
+
end
|
|
1051
|
+
html = workers.flat_map(&:value) # Ruby 4.0; use #take on 3.3 and 3.4
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1032
1054
|
## Benchmarks
|
|
1033
1055
|
|
|
1034
1056
|
Inkmark ships a benchmark harness comparing it against `kramdown`,
|
data/ext/inkmark/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "inkmark"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Yaroslav Markin <yaroslav@markin.net>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -11,11 +11,11 @@ crate-type = ["cdylib"]
|
|
|
11
11
|
|
|
12
12
|
[dependencies]
|
|
13
13
|
magnus = { version = "0.8.2" }
|
|
14
|
-
rb-sys = { version = "0.9.
|
|
14
|
+
rb-sys = { version = "0.9.130", features = ["stable-api-compiled-fallback"] }
|
|
15
15
|
pulldown-cmark = { version = "0.13", default-features = false, features = ["html", "simd"] }
|
|
16
16
|
pulldown-cmark-escape = "0.11"
|
|
17
17
|
deunicode = "1.6"
|
|
18
|
-
emojis = "0.
|
|
18
|
+
emojis = "0.9"
|
|
19
19
|
whatlang = "0.18"
|
|
20
20
|
unicode-segmentation = "1.12"
|
|
21
21
|
syntect = { version = "5.2", default-features = false, features = ["parsing", "html", "default-themes", "default-syntaxes", "regex-fancy"] }
|
|
@@ -28,4 +28,4 @@ pulldown-cmark-to-cmark = "22"
|
|
|
28
28
|
rb-sys-env = "0.2.2"
|
|
29
29
|
|
|
30
30
|
[dev-dependencies]
|
|
31
|
-
rb-sys-test-helpers = { version = "0.
|
|
31
|
+
rb-sys-test-helpers = { version = "0.3.0" }
|
data/ext/inkmark/src/lib.rs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// `deny` rather than `forbid`: the single `unsafe` block in `init` is
|
|
2
|
+
// allowed explicitly; everything else stays unsafe-free.
|
|
3
|
+
#![deny(unsafe_code)]
|
|
2
4
|
|
|
3
5
|
use magnus::{function, prelude::*, Error, Ruby};
|
|
4
6
|
|
|
@@ -22,11 +24,34 @@ mod truncate;
|
|
|
22
24
|
mod url_match;
|
|
23
25
|
|
|
24
26
|
#[magnus::init]
|
|
27
|
+
#[allow(unsafe_code)]
|
|
25
28
|
fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
29
|
+
// Declare every method defined below Ractor-safe. Ruby marks the methods an
|
|
30
|
+
// extension defines while `Init_*` runs as unsafe unless this flag is set,
|
|
31
|
+
// and calling such a method from a non-main Ractor raises
|
|
32
|
+
// `Ractor::UnsafeError`. The extension keeps no Ruby `VALUE`s in
|
|
33
|
+
// process-global state: the `OnceLock` caches in `highlight` hold plain
|
|
34
|
+
// syntect data (`Sync`, enforced by the compiler) and the `sym_id!`
|
|
35
|
+
// statics in `options` hold interned symbol ids, both safe to share across
|
|
36
|
+
// Ractors. This must run before `define_class` so the flag covers every
|
|
37
|
+
// method registered below.
|
|
38
|
+
//
|
|
39
|
+
// SAFETY: `rb_ext_ractor_safe` takes no pointers and only flips a flag on
|
|
40
|
+
// the VM's extension-loading state. Its one precondition is being called
|
|
41
|
+
// while `Init_*` runs, which is exactly where `#[magnus::init]` invokes
|
|
42
|
+
// this function.
|
|
43
|
+
unsafe { rb_sys::rb_ext_ractor_safe(true) };
|
|
44
|
+
|
|
26
45
|
let inkmark = ruby.define_class("Inkmark", ruby.class_object())?;
|
|
27
46
|
inkmark.define_singleton_method("_native_to_html", function!(document::native_to_html, 2))?;
|
|
28
|
-
inkmark.define_singleton_method(
|
|
29
|
-
|
|
47
|
+
inkmark.define_singleton_method(
|
|
48
|
+
"_native_to_markdown",
|
|
49
|
+
function!(document::native_to_markdown, 2),
|
|
50
|
+
)?;
|
|
51
|
+
inkmark.define_singleton_method(
|
|
52
|
+
"_native_to_plain_text",
|
|
53
|
+
function!(document::native_to_plain_text, 2),
|
|
54
|
+
)?;
|
|
30
55
|
inkmark.define_singleton_method(
|
|
31
56
|
"_native_chunks_by_heading",
|
|
32
57
|
function!(chunks_by_heading::native_chunks_by_heading, 2),
|
data/lib/inkmark/options.rb
CHANGED
|
@@ -22,22 +22,28 @@ class Inkmark
|
|
|
22
22
|
# Per-element-policy schemas. Each entry is +{ default:, types: }+; the
|
|
23
23
|
# validators use +types+ for type checking and +default+ to seed fresh
|
|
24
24
|
# nested hashes. Keep in sync with {NESTED_TO_FLAT}.
|
|
25
|
+
#
|
|
26
|
+
# Every constant in this class is frozen all the way down, not just
|
|
27
|
+
# at the top level: a non-main Ractor may only read constants whose
|
|
28
|
+
# whole object graph is frozen (+Ractor.shareable?+), so the inner
|
|
29
|
+
# Hashes and Arrays are frozen explicitly and the larger nested
|
|
30
|
+
# tables go through +Ractor.make_shareable+.
|
|
25
31
|
HEADINGS_SCHEMA = {
|
|
26
|
-
attributes: {default: false, types: [TrueClass, FalseClass]},
|
|
27
|
-
ids: {default: false, types: [TrueClass, FalseClass]}
|
|
32
|
+
attributes: {default: false, types: [TrueClass, FalseClass].freeze}.freeze,
|
|
33
|
+
ids: {default: false, types: [TrueClass, FalseClass].freeze}.freeze
|
|
28
34
|
}.freeze
|
|
29
35
|
|
|
30
36
|
IMAGES_SCHEMA = {
|
|
31
|
-
lazy: {default: false, types: [TrueClass, FalseClass]},
|
|
32
|
-
allowed_hosts: {default: nil, types: [NilClass, Array]},
|
|
33
|
-
allowed_schemes: {default: nil, types: [NilClass, Array]}
|
|
37
|
+
lazy: {default: false, types: [TrueClass, FalseClass].freeze}.freeze,
|
|
38
|
+
allowed_hosts: {default: nil, types: [NilClass, Array].freeze}.freeze,
|
|
39
|
+
allowed_schemes: {default: nil, types: [NilClass, Array].freeze}.freeze
|
|
34
40
|
}.freeze
|
|
35
41
|
|
|
36
42
|
LINKS_SCHEMA = {
|
|
37
|
-
autolink: {default: false, types: [TrueClass, FalseClass]},
|
|
38
|
-
nofollow: {default: false, types: [TrueClass, FalseClass]},
|
|
39
|
-
allowed_hosts: {default: nil, types: [NilClass, Array]},
|
|
40
|
-
allowed_schemes: {default: nil, types: [NilClass, Array]}
|
|
43
|
+
autolink: {default: false, types: [TrueClass, FalseClass].freeze}.freeze,
|
|
44
|
+
nofollow: {default: false, types: [TrueClass, FalseClass].freeze}.freeze,
|
|
45
|
+
allowed_hosts: {default: nil, types: [NilClass, Array].freeze}.freeze,
|
|
46
|
+
allowed_schemes: {default: nil, types: [NilClass, Array].freeze}.freeze
|
|
41
47
|
}.freeze
|
|
42
48
|
|
|
43
49
|
# Registry of nested hash options => their schemas. Iterated by the
|
|
@@ -52,7 +58,7 @@ class Inkmark
|
|
|
52
58
|
# Map from +(parent, child)+ user-facing keys to the flat key name the
|
|
53
59
|
# Rust side reads. Used by {#to_native_hash} / {#to_native_hash_frozen}
|
|
54
60
|
# to serialize the user-shaped hash into the FFI wire format.
|
|
55
|
-
NESTED_TO_FLAT = {
|
|
61
|
+
NESTED_TO_FLAT = Ractor.make_shareable({
|
|
56
62
|
[:headings, :attributes] => :heading_attributes,
|
|
57
63
|
[:headings, :ids] => :heading_ids,
|
|
58
64
|
[:images, :lazy] => :lazy_images,
|
|
@@ -62,7 +68,7 @@ class Inkmark
|
|
|
62
68
|
[:links, :nofollow] => :nofollow_external_links,
|
|
63
69
|
[:links, :allowed_hosts] => :allowed_link_hosts,
|
|
64
70
|
[:links, :allowed_schemes] => :allowed_link_schemes
|
|
65
|
-
}
|
|
71
|
+
})
|
|
66
72
|
|
|
67
73
|
# Build a frozen defaults hash for a nested schema from its +default+
|
|
68
74
|
# entries.
|
|
@@ -200,11 +206,11 @@ class Inkmark
|
|
|
200
206
|
# the accepted type set (nil-default-but-Array-when-set, polymorphic
|
|
201
207
|
# +toc+, nested-hash element-policy groups).
|
|
202
208
|
TYPES = {
|
|
203
|
-
extract: [NilClass, Hash],
|
|
204
|
-
toc: [TrueClass, FalseClass, Hash],
|
|
205
|
-
headings: [Hash],
|
|
206
|
-
images: [Hash],
|
|
207
|
-
links: [Hash]
|
|
209
|
+
extract: [NilClass, Hash].freeze,
|
|
210
|
+
toc: [TrueClass, FalseClass, Hash].freeze,
|
|
211
|
+
headings: [Hash].freeze,
|
|
212
|
+
images: [Hash].freeze,
|
|
213
|
+
links: [Hash].freeze
|
|
208
214
|
}.freeze
|
|
209
215
|
|
|
210
216
|
# Element kinds accepted inside +extract: { ... }+. Mirrors the match
|
|
@@ -237,7 +243,7 @@ class Inkmark
|
|
|
237
243
|
# The GFM tagfilter stays on. **Dangerous.** Use only for content
|
|
238
244
|
# the caller fully trusts (internal team-authored docs). The
|
|
239
245
|
# caller is fully responsible for sanitizing output.
|
|
240
|
-
PRESETS = {
|
|
246
|
+
PRESETS = Ractor.make_shareable({
|
|
241
247
|
commonmark: {
|
|
242
248
|
gfm: false,
|
|
243
249
|
gfm_tag_filter: false,
|
|
@@ -245,7 +251,7 @@ class Inkmark
|
|
|
245
251
|
strikethrough: false,
|
|
246
252
|
tasklists: false,
|
|
247
253
|
footnotes: false
|
|
248
|
-
}
|
|
254
|
+
},
|
|
249
255
|
|
|
250
256
|
gfm: {
|
|
251
257
|
gfm: true,
|
|
@@ -254,7 +260,7 @@ class Inkmark
|
|
|
254
260
|
strikethrough: true,
|
|
255
261
|
tasklists: true,
|
|
256
262
|
footnotes: true
|
|
257
|
-
}
|
|
263
|
+
},
|
|
258
264
|
|
|
259
265
|
recommended: {
|
|
260
266
|
gfm: true,
|
|
@@ -272,7 +278,7 @@ class Inkmark
|
|
|
272
278
|
syntax_highlight: true,
|
|
273
279
|
hard_wrap: true,
|
|
274
280
|
frontmatter: true
|
|
275
|
-
}
|
|
281
|
+
},
|
|
276
282
|
|
|
277
283
|
trusted: {
|
|
278
284
|
gfm: true,
|
|
@@ -290,8 +296,8 @@ class Inkmark
|
|
|
290
296
|
syntax_highlight: true,
|
|
291
297
|
hard_wrap: true,
|
|
292
298
|
frontmatter: true
|
|
293
|
-
}
|
|
294
|
-
}
|
|
299
|
+
}
|
|
300
|
+
})
|
|
295
301
|
|
|
296
302
|
# Preset applied by {#initialize} when the caller doesn't pass
|
|
297
303
|
# +preset:+. +:gfm+ matches {DEFAULTS}, so the default constructor
|
|
@@ -387,7 +393,16 @@ class Inkmark
|
|
|
387
393
|
# hashes; the input value as-is otherwise)
|
|
388
394
|
# @raise [ArgumentError] if +key+ is unknown, or the value (or any
|
|
389
395
|
# nested sub-value) has the wrong type
|
|
396
|
+
# @raise [FrozenError] if this instance is frozen (as
|
|
397
|
+
# {Inkmark.default_options} always is)
|
|
390
398
|
def []=(key, value)
|
|
399
|
+
if frozen?
|
|
400
|
+
raise FrozenError.new(
|
|
401
|
+
"can't modify frozen #{self.class}: use Inkmark.configure to change " \
|
|
402
|
+
"the process-wide defaults, or dup for a mutable copy",
|
|
403
|
+
receiver: self
|
|
404
|
+
)
|
|
405
|
+
end
|
|
391
406
|
validate_key!(key)
|
|
392
407
|
# Deep-merge partial nested-hash overrides (+:headings+,
|
|
393
408
|
# +:images+, +:links+) so callers pass only the sub-keys they
|
|
@@ -429,11 +444,17 @@ class Inkmark
|
|
|
429
444
|
# FFI calls that don't need to add per-call params. The cache is
|
|
430
445
|
# invalidated in {#[]=} and {#initialize_copy}.
|
|
431
446
|
#
|
|
432
|
-
#
|
|
433
|
-
#
|
|
447
|
+
# The hash is frozen all the way down, as a copy: nested Arrays and
|
|
448
|
+
# Hashes are duplicated before freezing so caller-supplied values
|
|
449
|
+
# (an +allowed_hosts+ Array, say) stay mutable in the caller's hands.
|
|
450
|
+
# A deeply frozen memo is what lets a frozen +Options+ be shared
|
|
451
|
+
# across Ractors.
|
|
452
|
+
#
|
|
453
|
+
# @return [Hash{Symbol => Object}] deeply frozen, shared across calls
|
|
454
|
+
# until a mutation invalidates it
|
|
434
455
|
# @api private
|
|
435
456
|
def to_native_hash_frozen
|
|
436
|
-
@frozen_native_hash ||= build_native_hash
|
|
457
|
+
@frozen_native_hash ||= deep_frozen_copy(build_native_hash)
|
|
437
458
|
end
|
|
438
459
|
|
|
439
460
|
# Return a new Options instance with +other+'s values applied on top.
|
|
@@ -457,6 +478,18 @@ class Inkmark
|
|
|
457
478
|
end
|
|
458
479
|
alias_method :eql?, :==
|
|
459
480
|
|
|
481
|
+
# Freeze this instance. The memoized FFI hash is computed first, while
|
|
482
|
+
# the instance is still mutable, so {#to_native_hash_frozen} never has
|
|
483
|
+
# to write to a frozen object. +Ractor.make_shareable+ calls +freeze+
|
|
484
|
+
# on every object it visits, which is what makes a shared
|
|
485
|
+
# {Inkmark.default_options} renderable from any Ractor.
|
|
486
|
+
#
|
|
487
|
+
# @return [self]
|
|
488
|
+
def freeze
|
|
489
|
+
to_native_hash_frozen
|
|
490
|
+
super
|
|
491
|
+
end
|
|
492
|
+
|
|
460
493
|
# Duplicate this instance, deep-copying the internal values hash so the
|
|
461
494
|
# clone is fully independent from the original.
|
|
462
495
|
def initialize_copy(orig)
|
|
@@ -466,14 +499,26 @@ class Inkmark
|
|
|
466
499
|
@frozen_native_hash = nil
|
|
467
500
|
end
|
|
468
501
|
|
|
469
|
-
#
|
|
470
|
-
#
|
|
471
|
-
#
|
|
472
|
-
#
|
|
473
|
-
#
|
|
502
|
+
# Reader and writer for every option key. The writer routes through
|
|
503
|
+
# {#[]=} so key validation and (for nested groups) deep-merge apply
|
|
504
|
+
# uniformly.
|
|
505
|
+
#
|
|
506
|
+
# Generated from source strings rather than +define_method+ blocks:
|
|
507
|
+
# a block-defined method carries its Proc, and Ruby refuses to call
|
|
508
|
+
# such a method from a non-main Ractor ("defined with an un-shareable
|
|
509
|
+
# Proc in a different Ractor"). Plain +def+ bodies have no such
|
|
510
|
+
# baggage. +key+ is always a Symbol from {DEFAULTS}, so interpolating
|
|
511
|
+
# it is safe.
|
|
474
512
|
DEFAULTS.each_key do |key|
|
|
475
|
-
|
|
476
|
-
|
|
513
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
|
514
|
+
def #{key} # def tables
|
|
515
|
+
@values[:#{key}] # @values[:tables]
|
|
516
|
+
end # end
|
|
517
|
+
|
|
518
|
+
def #{key}=(value) # def tables=(value)
|
|
519
|
+
self[:#{key}] = value # self[:tables] = value
|
|
520
|
+
end # end
|
|
521
|
+
RUBY
|
|
477
522
|
end
|
|
478
523
|
|
|
479
524
|
private
|
|
@@ -517,6 +562,19 @@ class Inkmark
|
|
|
517
562
|
def validate_key!(key) = self.class.send(:validate_key!, key)
|
|
518
563
|
def validate_value!(key, value) = self.class.send(:validate_value!, key, value)
|
|
519
564
|
|
|
565
|
+
# Return a frozen copy of +value+ with every nested Hash, Array, and
|
|
566
|
+
# String frozen too. Scalars (booleans, nil, Integers, Symbols) are
|
|
567
|
+
# returned as is. Only the container shapes {#build_native_hash} can
|
|
568
|
+
# produce are handled.
|
|
569
|
+
def deep_frozen_copy(value)
|
|
570
|
+
case value
|
|
571
|
+
when Hash then value.transform_values { |v| deep_frozen_copy(v) }.freeze
|
|
572
|
+
when Array then value.map { |v| deep_frozen_copy(v) }.freeze
|
|
573
|
+
when String then -value
|
|
574
|
+
else value
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
520
578
|
# Build the Rust-facing flat hash: nested element-policy hashes expand
|
|
521
579
|
# into their flat keys via {NESTED_TO_FLAT}; the internal +@toc_depth+
|
|
522
580
|
# is injected when set.
|
|
@@ -624,10 +682,13 @@ class Inkmark
|
|
|
624
682
|
# +options: { preset: :name }+ call pattern, which would otherwise
|
|
625
683
|
# build a fresh +Options+ instance (seed defaults, 6–14 +[]=+
|
|
626
684
|
# with validation, +build_native_hash+) on every call. The cached
|
|
627
|
-
# hashes are frozen and safe to share across threads
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
685
|
+
# hashes are deeply frozen and safe to share across threads and
|
|
686
|
+
# Ractors.
|
|
687
|
+
PRESETS_NATIVE_HASH = Ractor.make_shareable(
|
|
688
|
+
PRESETS.keys.each_with_object({}) do |name, h|
|
|
689
|
+
h[name] = new(preset: name).to_native_hash_frozen
|
|
690
|
+
end
|
|
691
|
+
)
|
|
631
692
|
|
|
632
693
|
# Build a Rust-facing flat hash from +overrides+ without allocating
|
|
633
694
|
# an +Options+ instance or walking +build_native_hash+. Starts from
|
data/lib/inkmark/version.rb
CHANGED
data/lib/inkmark.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "yaml"
|
|
4
|
-
|
|
5
3
|
# Inkmark is a very fast, feature-rich, AI-first CommonMark/GFM
|
|
6
4
|
# markdown renderer backed by the Rust pulldown-cmark parser.
|
|
7
5
|
#
|
|
@@ -64,12 +62,11 @@ class Inkmark
|
|
|
64
62
|
# This is a class-method fast path that skips Inkmark instance and
|
|
65
63
|
# Options copy allocation for the common one-shot render pattern.
|
|
66
64
|
# When the caller passes +options: nil+ (the default), we reuse the
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# bugs.
|
|
65
|
+
# frozen hash memoized on {default_options} by
|
|
66
|
+
# {Inkmark::Options#to_native_hash_frozen}. {configure} and
|
|
67
|
+
# {default_options=} install a whole new frozen instance rather than
|
|
68
|
+
# mutating the shared one, so the next render sees the new values
|
|
69
|
+
# without any stale-cache bugs.
|
|
73
70
|
#
|
|
74
71
|
# **Raw HTML safety.** +raw_html: false+ (the default) escapes
|
|
75
72
|
# every raw HTML tag in the source—safe for untrusted input.
|
|
@@ -351,33 +348,65 @@ class Inkmark
|
|
|
351
348
|
end
|
|
352
349
|
|
|
353
350
|
# Return an array of available syntax-highlighting theme names.
|
|
354
|
-
# Memoized—the theme list is fixed at compile time.
|
|
351
|
+
# Memoized—the theme list is fixed at compile time. The memo is
|
|
352
|
+
# warmed while this file loads (see the bottom of the file), so
|
|
353
|
+
# non-main Ractors only ever read it.
|
|
355
354
|
#
|
|
356
|
-
# @return [Array<String>]
|
|
355
|
+
# @return [Array<String>] frozen, with frozen elements
|
|
357
356
|
def highlight_themes
|
|
358
|
-
@highlight_themes ||= _syntax_themes.freeze
|
|
357
|
+
@highlight_themes ||= _syntax_themes.each(&:freeze).freeze
|
|
359
358
|
end
|
|
360
359
|
|
|
361
|
-
# The
|
|
360
|
+
# The process-wide default options, used when a render is given no
|
|
361
|
+
# options of its own. The instance is frozen all the way down and
|
|
362
|
+
# shared by every thread and Ractor in the process; change it with
|
|
363
|
+
# {configure} or {default_options=}, never in place.
|
|
362
364
|
#
|
|
363
|
-
# @return [Inkmark::Options]
|
|
365
|
+
# @return [Inkmark::Options] frozen
|
|
364
366
|
def default_options
|
|
365
|
-
@default_options
|
|
367
|
+
@default_options || DEFAULT_OPTIONS
|
|
366
368
|
end
|
|
367
369
|
|
|
368
|
-
# Replace the
|
|
370
|
+
# Replace the process-wide default options. The value is copied and
|
|
371
|
+
# frozen all the way down (+Ractor.make_shareable+), so the caller
|
|
372
|
+
# keeps its own object mutable and worker Ractors can read the
|
|
373
|
+
# result. Call this on the main Ractor before spawning workers.
|
|
369
374
|
#
|
|
370
|
-
# @param value [Hash, Inkmark::Options] new defaults; a Hash is
|
|
371
|
-
#
|
|
372
|
-
# @return [Inkmark::Options] the stored options
|
|
375
|
+
# @param value [Hash, Inkmark::Options] new defaults; a Hash is
|
|
376
|
+
# converted to Inkmark::Options
|
|
377
|
+
# @return [Inkmark::Options] the stored, frozen options
|
|
373
378
|
# @raise [TypeError] if +value+ is not a Hash or Inkmark::Options
|
|
379
|
+
# @example
|
|
380
|
+
# Inkmark.default_options = { preset: :recommended, math: true }
|
|
374
381
|
def default_options=(value)
|
|
375
|
-
|
|
382
|
+
options =
|
|
376
383
|
case value
|
|
377
|
-
when Inkmark::Options then value
|
|
384
|
+
when Inkmark::Options then value
|
|
378
385
|
when Hash then Inkmark::Options.new(value)
|
|
379
386
|
else raise TypeError, "default_options must be a Hash or Inkmark::Options, got #{value.class}"
|
|
380
387
|
end
|
|
388
|
+
# Copy-on-write of a shareable value: main-Ractor configuration.
|
|
389
|
+
@default_options = Ractor.make_shareable(options, copy: true) # audition:disable class-level-state
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Adjust the process-wide default options. Yields a mutable copy of
|
|
393
|
+
# the current {default_options}; the result is stored frozen through
|
|
394
|
+
# {default_options=}. Successive calls build on each other. Call this
|
|
395
|
+
# on the main Ractor before spawning workers.
|
|
396
|
+
#
|
|
397
|
+
# @yieldparam options [Inkmark::Options] a mutable copy of the
|
|
398
|
+
# current defaults
|
|
399
|
+
# @return [Inkmark::Options] the stored, frozen options
|
|
400
|
+
# @example In an application initializer
|
|
401
|
+
# Inkmark.configure do |options|
|
|
402
|
+
# options.math = true
|
|
403
|
+
# options.links = { nofollow: true }
|
|
404
|
+
# end
|
|
405
|
+
def configure
|
|
406
|
+
options = default_options.dup
|
|
407
|
+
yield options
|
|
408
|
+
self.default_options = options
|
|
409
|
+
default_options
|
|
381
410
|
end
|
|
382
411
|
|
|
383
412
|
private
|
|
@@ -413,12 +442,18 @@ class Inkmark
|
|
|
413
442
|
end
|
|
414
443
|
end
|
|
415
444
|
|
|
445
|
+
# Built-in defaults, served by {default_options} until {configure} or
|
|
446
|
+
# {default_options=} installs a replacement. Frozen all the way down
|
|
447
|
+
# (which also memoizes its FFI hash, see {Inkmark::Options#freeze}).
|
|
448
|
+
DEFAULT_OPTIONS = Ractor.make_shareable(Inkmark::Options.new)
|
|
449
|
+
private_constant :DEFAULT_OPTIONS
|
|
450
|
+
|
|
416
451
|
# Create a new renderer for +source+.
|
|
417
452
|
#
|
|
418
453
|
# @param source [String, nil] markdown source; +nil+ is treated as an
|
|
419
454
|
# empty string
|
|
420
455
|
# @param options [Hash, Inkmark::Options, nil] rendering options; falls back
|
|
421
|
-
# to a
|
|
456
|
+
# to a mutable copy of {Inkmark.default_options} when nil
|
|
422
457
|
# @raise [TypeError] if +options+ is not a Hash, Inkmark::Options, or nil
|
|
423
458
|
def initialize(source = nil, options: nil)
|
|
424
459
|
self.source = source
|
|
@@ -459,8 +494,8 @@ class Inkmark
|
|
|
459
494
|
|
|
460
495
|
# Set rendering options.
|
|
461
496
|
#
|
|
462
|
-
# @param value [Hash, Inkmark::Options, nil] new options; nil resets to a
|
|
463
|
-
# of {Inkmark.default_options}
|
|
497
|
+
# @param value [Hash, Inkmark::Options, nil] new options; nil resets to a
|
|
498
|
+
# mutable copy of {Inkmark.default_options}
|
|
464
499
|
# @return [Inkmark::Options] the stored options object
|
|
465
500
|
# @raise [TypeError] if +value+ is not a Hash, Inkmark::Options, or nil
|
|
466
501
|
def options=(value)
|
|
@@ -681,6 +716,9 @@ class Inkmark
|
|
|
681
716
|
# The raw YAML text is extracted by Rust during the event walk;
|
|
682
717
|
# parsing uses Ruby's stdlib +YAML.safe_load+ so all standard YAML
|
|
683
718
|
# types (strings, numbers, arrays, nested hashes) are supported.
|
|
719
|
+
# Psych is loaded here, on first use, rather than with the gem: front
|
|
720
|
+
# matter is opt-in, and this keeps Psych's load time and its own
|
|
721
|
+
# constants out of processes that never enable it.
|
|
684
722
|
#
|
|
685
723
|
# @return [Hash, nil] parsed frontmatter or nil
|
|
686
724
|
# @example
|
|
@@ -691,7 +729,10 @@ class Inkmark
|
|
|
691
729
|
return @frontmatter if defined?(@frontmatter)
|
|
692
730
|
return @frontmatter = nil unless @options[:frontmatter]
|
|
693
731
|
to_html unless @frontmatter_raw
|
|
694
|
-
@frontmatter =
|
|
732
|
+
return @frontmatter = nil unless @frontmatter_raw
|
|
733
|
+
|
|
734
|
+
require "yaml" unless defined?(::YAML) # audition:disable runtime-require
|
|
735
|
+
@frontmatter = YAML.safe_load(@frontmatter_raw)
|
|
695
736
|
end
|
|
696
737
|
|
|
697
738
|
private
|
|
@@ -715,3 +756,8 @@ class Inkmark
|
|
|
715
756
|
extract.is_a?(Hash) && extract.any? { |_, v| v }
|
|
716
757
|
end
|
|
717
758
|
end
|
|
759
|
+
|
|
760
|
+
# Warm the class-level memo on the main Ractor while loading, so worker
|
|
761
|
+
# Ractors only ever read it (a first write from a worker would raise
|
|
762
|
+
# Ractor::IsolationError).
|
|
763
|
+
Inkmark.highlight_themes
|
data/sig/inkmark.rbs
CHANGED
|
@@ -179,6 +179,7 @@ class Inkmark
|
|
|
179
179
|
def self.highlight_themes: () -> Array[String]
|
|
180
180
|
def self.default_options: () -> Inkmark::Options
|
|
181
181
|
def self.default_options=: (options_input value) -> Inkmark::Options
|
|
182
|
+
def self.configure: () { (Inkmark::Options options) -> void } -> Inkmark::Options
|
|
182
183
|
|
|
183
184
|
# ── Native bindings (private in spirit; registered on the class) ──────
|
|
184
185
|
def self._native_to_html: (String source, Hash[Symbol, untyped]? opts_hash) -> String
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inkmark
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Markin
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.9.
|
|
18
|
+
version: 0.9.130
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.9.
|
|
25
|
+
version: 0.9.130
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: rake
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -57,14 +57,14 @@ dependencies:
|
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
60
|
+
version: '4.0'
|
|
61
61
|
type: :development
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
67
|
+
version: '4.0'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: yard
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -202,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
202
202
|
- !ruby/object:Gem::Version
|
|
203
203
|
version: '0'
|
|
204
204
|
requirements: []
|
|
205
|
-
rubygems_version: 4.0.
|
|
205
|
+
rubygems_version: 4.0.16
|
|
206
206
|
specification_version: 4
|
|
207
207
|
summary: Very fast, feature-packed, AI-first Markdown gem for Ruby.
|
|
208
208
|
test_files: []
|