inkmark 0.1.3 → 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 +11 -0
- data/Cargo.lock +126 -107
- data/README.md +25 -3
- data/ext/inkmark/Cargo.toml +4 -4
- data/ext/inkmark/src/chunks_by_heading.rs +3 -2
- data/ext/inkmark/src/chunks_by_size.rs +3 -3
- data/ext/inkmark/src/document.rs +46 -4
- data/ext/inkmark/src/lib.rs +28 -3
- data/ext/inkmark/src/truncate.rs +3 -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
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: []
|