polytypo 1.12.0 → 1.13.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/README.md +20 -0
- data/lib/polytypo/data/VERSION +1 -1
- data/lib/polytypo/data/fixtures/bg.json +1 -1
- data/lib/polytypo/data/fixtures/cs.json +1 -1
- data/lib/polytypo/data/fixtures/de-CH.json +1 -1
- data/lib/polytypo/data/fixtures/de-DE.json +77 -1
- data/lib/polytypo/data/fixtures/el.json +1 -1
- data/lib/polytypo/data/fixtures/en-GB.json +89 -1
- data/lib/polytypo/data/fixtures/en-US.json +450 -1
- data/lib/polytypo/data/fixtures/es.json +1 -1
- data/lib/polytypo/data/fixtures/fi.json +1 -1
- data/lib/polytypo/data/fixtures/fr-CA.json +1 -1
- data/lib/polytypo/data/fixtures/fr.json +41 -1
- data/lib/polytypo/data/fixtures/hr.json +1 -1
- data/lib/polytypo/data/fixtures/hu.json +1 -1
- data/lib/polytypo/data/fixtures/it.json +1 -1
- data/lib/polytypo/data/fixtures/locale-resolution.json +1 -1
- data/lib/polytypo/data/fixtures/nl.json +1 -1
- data/lib/polytypo/data/fixtures/pl.json +1 -1
- data/lib/polytypo/data/fixtures/pt-BR.json +1 -1
- data/lib/polytypo/data/fixtures/pt-PT.json +1 -1
- data/lib/polytypo/data/fixtures/ro.json +1 -1
- data/lib/polytypo/data/fixtures/ru.json +1 -1
- data/lib/polytypo/data/fixtures/sk.json +1 -1
- data/lib/polytypo/data/fixtures/sv.json +1 -1
- data/lib/polytypo/data/fixtures/tr.json +1 -1
- data/lib/polytypo/data/fixtures/uk.json +1 -1
- data/lib/polytypo/data/locales/registry.json +1 -1
- data/lib/polytypo/data/rules/analyze.md +16 -4
- data/lib/polytypo/data/rules/modes.md +262 -9
- data/lib/polytypo/data/rules/order.json +1 -1
- data/lib/polytypo/data/schema/fixtures.schema.json +160 -18
- data/lib/polytypo/engine/origin.rb +13 -1
- data/lib/polytypo/engine/protect.rb +122 -0
- data/lib/polytypo/modes/runner.rb +0 -1
- data/lib/polytypo/version.rb +1 -1
- data/lib/polytypo.rb +34 -19
- metadata +2 -1
|
@@ -54,6 +54,18 @@ module Polytypo
|
|
|
54
54
|
out
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
# Where a change ends in the input (analyze.md section 2). A non-empty edit ends one past
|
|
58
|
+
# the input offset of the last code point it replaced that has an origin. Scanning forward
|
|
59
|
+
# from edit.end instead, as origin_at does for a start, would carry the end across a skipped
|
|
60
|
+
# element or a protected run that follows -- the boundary marker has no origin -- and the
|
|
61
|
+
# reported range would contain text the rules never saw.
|
|
62
|
+
def self.end_of(origin, edit, input_length)
|
|
63
|
+
(edit.end - 1).downto(edit.start) do |i|
|
|
64
|
+
return origin[i] + 1 unless origin[i] == NO_ORIGIN
|
|
65
|
+
end
|
|
66
|
+
origin_at(origin, edit.end, input_length)
|
|
67
|
+
end
|
|
68
|
+
|
|
57
69
|
# One rule's edits, in the coordinates that rule saw, rendered as Changes in input
|
|
58
70
|
# coordinates. +before+ is the text this rule replaced and +after+ what it replaced it with
|
|
59
71
|
# (analyze.md section 2), so on a text two rules have both touched, +before+ is what the
|
|
@@ -64,7 +76,7 @@ module Polytypo
|
|
|
64
76
|
Polytypo::Change.new(
|
|
65
77
|
rule_id: rule_id,
|
|
66
78
|
start: origin_at(origin, edit.start, input_length),
|
|
67
|
-
end:
|
|
79
|
+
end: end_of(origin, edit, input_length),
|
|
68
80
|
before: Codepoints.from_codepoints(cp[edit.start...edit.end]),
|
|
69
81
|
after: Codepoints.from_codepoints(edit.replacement)
|
|
70
82
|
)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "codepoints"
|
|
4
|
+
require_relative "origin"
|
|
5
|
+
require_relative "pipeline"
|
|
6
|
+
require_relative "sentinels"
|
|
7
|
+
require_relative "../errors"
|
|
8
|
+
require_relative "../modes/spans"
|
|
9
|
+
require_relative "../modes/runner"
|
|
10
|
+
|
|
11
|
+
module Polytypo
|
|
12
|
+
module Engine
|
|
13
|
+
# modes.md 3.9: the caller's own skipped regions, "text" mode only (spec 1.13.0). Each maximal
|
|
14
|
+
# protected run becomes ONE boundary marker in the code-point array -- at the very start or
|
|
15
|
+
# end too, which is the one difference from the html/markdown/yaml span concatenation -- the
|
|
16
|
+
# rules run once over that array, and the protected code points are put back at their markers.
|
|
17
|
+
module Protect
|
|
18
|
+
LINE_TERMINATORS = [0x0A, 0x0D, 0x0B, 0x0C, 0x85, 0x2028, 0x2029].freeze
|
|
19
|
+
|
|
20
|
+
def self.invalid(message)
|
|
21
|
+
raise Polytypo::Error.new(Polytypo::CODE_INVALID_OPTION, message)
|
|
22
|
+
end
|
|
23
|
+
private_class_method :invalid
|
|
24
|
+
|
|
25
|
+
# modes.md 3.9.2: validates +value+ against the input's length in code points and returns
|
|
26
|
+
# the protected runs as [start, end] pairs with touching ranges merged. nil and [] are the
|
|
27
|
+
# same call.
|
|
28
|
+
def self.resolve(value, length)
|
|
29
|
+
return [] if value.nil?
|
|
30
|
+
|
|
31
|
+
invalid("\"protect\" must be an Array of [start, end] pairs. Received #{value.inspect}.") unless value.is_a?(Array)
|
|
32
|
+
|
|
33
|
+
holes = []
|
|
34
|
+
previous_end = 0
|
|
35
|
+
value.each do |range|
|
|
36
|
+
unless range.is_a?(Array) && range.length == 2 && range.all?(Integer)
|
|
37
|
+
invalid("\"protect\" entries must be [start, end] pairs of Integers; received #{range.inspect}.")
|
|
38
|
+
end
|
|
39
|
+
s, e = range
|
|
40
|
+
invalid("protect range [#{s}, #{e}) is empty or negative.") if s.negative? || e <= s
|
|
41
|
+
invalid("protect range [#{s}, #{e}) overlaps or precedes the previous one.") if s < previous_end
|
|
42
|
+
invalid("protect range [#{s}, #{e}) runs past the end of the input (#{length}).") if e > length
|
|
43
|
+
|
|
44
|
+
if !holes.empty? && holes.last[1] == s
|
|
45
|
+
holes.last[1] = e
|
|
46
|
+
else
|
|
47
|
+
holes << [s, e]
|
|
48
|
+
end
|
|
49
|
+
previous_end = e
|
|
50
|
+
end
|
|
51
|
+
holes
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# modes.md 3.9.2: outside "text" mode a non-empty +protect+ is refused rather than ignored.
|
|
55
|
+
def self.refuse_outside_text(value, mode)
|
|
56
|
+
return if value.nil? || (value.is_a?(Array) && value.empty?)
|
|
57
|
+
|
|
58
|
+
invalid("\"protect\" is only available when mode is \"text\", not #{mode.inspect}.")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# [S0] m [S1] m ... [Sk], a marker for every run, edges included, and the origin map.
|
|
62
|
+
def self.layout(cp, holes)
|
|
63
|
+
array = []
|
|
64
|
+
origin = []
|
|
65
|
+
cursor = 0
|
|
66
|
+
holes.each do |s, e|
|
|
67
|
+
(cursor...s).each do |i|
|
|
68
|
+
array << cp[i]
|
|
69
|
+
origin << i
|
|
70
|
+
end
|
|
71
|
+
line = (s...e).any? { |i| LINE_TERMINATORS.include?(cp[i]) }
|
|
72
|
+
array << (line ? Engine::LINE_MARKER : Engine::MARKER)
|
|
73
|
+
origin << Origin::NO_ORIGIN
|
|
74
|
+
cursor = e
|
|
75
|
+
end
|
|
76
|
+
(cursor...cp.length).each do |i|
|
|
77
|
+
array << cp[i]
|
|
78
|
+
origin << i
|
|
79
|
+
end
|
|
80
|
+
[array, origin]
|
|
81
|
+
end
|
|
82
|
+
private_class_method :layout
|
|
83
|
+
|
|
84
|
+
# Returns [text, moved] where +moved+ is the protected runs located in the output, the
|
|
85
|
+
# ranges a second call has to be given. Public API returns the text alone.
|
|
86
|
+
def self.run_mapped(cp, holes, plan, locale_data, ctx)
|
|
87
|
+
array, = layout(cp, holes)
|
|
88
|
+
out = Modes::Runner.run_rules_over_spans(array, plan, locale_data, ctx)
|
|
89
|
+
result = []
|
|
90
|
+
moved = []
|
|
91
|
+
index = 0
|
|
92
|
+
out.each do |value|
|
|
93
|
+
unless Engine.marker?(value)
|
|
94
|
+
result << value
|
|
95
|
+
next
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
s, e = holes[index]
|
|
99
|
+
moved << [result.length, result.length + (e - s)]
|
|
100
|
+
result.concat(cp[s...e])
|
|
101
|
+
index += 1
|
|
102
|
+
end
|
|
103
|
+
if index != holes.length
|
|
104
|
+
raise Polytypo::Error.new(Polytypo::CODE_RULE_CONTRACT, "boundary markers did not survive the pipeline")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
[Codepoints.from_codepoints(result), moved]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.run(cp, holes, plan, locale_data, ctx)
|
|
111
|
+
run_mapped(cp, holes, plan, locale_data, ctx).first
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.analyze(cp, holes, plan, locale_data, ctx)
|
|
115
|
+
array, origin = layout(cp, holes)
|
|
116
|
+
Pipeline.run_rules_recording(array, plan, locale_data, ctx, origin, cp.length) do |current, edits|
|
|
117
|
+
Modes::Spans.filter_boundary_edits(current, edits, Modes::Spans.span_ranges_of(current))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -25,7 +25,6 @@ module Polytypo
|
|
|
25
25
|
end
|
|
26
26
|
current
|
|
27
27
|
end
|
|
28
|
-
private_class_method :run_rules_over_spans
|
|
29
28
|
|
|
30
29
|
# The output is the input with a set of disjoint substring replacements applied and nothing
|
|
31
30
|
# else (modes.md 4). A span whose content the rules did not change contributes no
|
data/lib/polytypo/version.rb
CHANGED
data/lib/polytypo.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative "polytypo/errors"
|
|
|
5
5
|
require_relative "polytypo/engine/codepoints"
|
|
6
6
|
require_relative "polytypo/engine/origin"
|
|
7
7
|
require_relative "polytypo/engine/pipeline"
|
|
8
|
+
require_relative "polytypo/engine/protect"
|
|
8
9
|
require_relative "polytypo/engine/rules" # side effect: registers all 9 rules
|
|
9
10
|
require_relative "polytypo/modes/spans"
|
|
10
11
|
require_relative "polytypo/modes/runner"
|
|
@@ -24,7 +25,9 @@ module Polytypo
|
|
|
24
25
|
# "mdx" is not implemented by this runtime). +keys+ is required iff mode is "yaml" and names
|
|
25
26
|
# the mapping keys whose scalar values are prose (modes.md 3.8.2) -- it has no default, because
|
|
26
27
|
# nothing in YAML's syntax separates "description:" from "run:". +rules+ is an opt-out Hash
|
|
27
|
-
# keyed by rule id.
|
|
28
|
+
# keyed by rule id. +protect+ is "text" mode only: an Array of [start, end] pairs of
|
|
29
|
+
# code-point offsets (half-open, ascending, non-overlapping) into +input+ that are never edited
|
|
30
|
+
# and that the rules read as opaque content (modes.md 3.9). nil and [] are the same call.
|
|
28
31
|
#
|
|
29
32
|
# Pure: no I/O, no environment, no clock, no globals, no class-level mutable state --
|
|
30
33
|
# thread-safe and reentrant, callable concurrently from any number of Threads with no external
|
|
@@ -33,7 +36,7 @@ module Polytypo
|
|
|
33
36
|
# Only `mode: "text"`/`"html"` ever touch this file's own requires; `mode: "markdown"` lazily
|
|
34
37
|
# requires "commonmarker" from within Modes::Markdown, never at load time of this file.
|
|
35
38
|
def self.transform(input, locale:, mode: "text", dialect: nil, keys: nil, rules: nil, narrow_nbsp: nil,
|
|
36
|
-
frontmatter_keys: nil)
|
|
39
|
+
frontmatter_keys: nil, protect: nil)
|
|
37
40
|
resolved_mode = resolve_mode(mode)
|
|
38
41
|
narrow_target = Engine.resolve_narrow_target(narrow_nbsp)
|
|
39
42
|
|
|
@@ -42,19 +45,19 @@ module Polytypo
|
|
|
42
45
|
if dialect
|
|
43
46
|
raise Error.new(CODE_INVALID_DIALECT, '"dialect" is only valid when mode is "markdown"')
|
|
44
47
|
end
|
|
45
|
-
transform_text(input, locale, rules, narrow_target)
|
|
48
|
+
transform_text(input, locale, rules, narrow_target, protect)
|
|
46
49
|
when "html"
|
|
47
50
|
if dialect
|
|
48
51
|
raise Error.new(CODE_INVALID_DIALECT, '"dialect" is only valid when mode is "markdown"')
|
|
49
52
|
end
|
|
50
|
-
transform_html(input, locale, rules, narrow_target)
|
|
53
|
+
transform_html(input, locale, rules, narrow_target, protect)
|
|
51
54
|
when "yaml"
|
|
52
55
|
if dialect
|
|
53
56
|
raise Error.new(CODE_INVALID_DIALECT, '"dialect" is only valid when mode is "markdown"')
|
|
54
57
|
end
|
|
55
|
-
transform_yaml(input, locale, keys, rules, narrow_target)
|
|
58
|
+
transform_yaml(input, locale, keys, rules, narrow_target, protect)
|
|
56
59
|
else # "markdown"
|
|
57
|
-
transform_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys)
|
|
60
|
+
transform_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys: frontmatter_keys, protect: protect)
|
|
58
61
|
end
|
|
59
62
|
end
|
|
60
63
|
|
|
@@ -71,7 +74,7 @@ module Polytypo
|
|
|
71
74
|
#
|
|
72
75
|
# Pure and thread-safe on the same terms as .transform.
|
|
73
76
|
def self.analyze(input, locale:, mode: "text", dialect: nil, keys: nil, rules: nil, narrow_nbsp: nil,
|
|
74
|
-
frontmatter_keys: nil)
|
|
77
|
+
frontmatter_keys: nil, protect: nil)
|
|
75
78
|
resolved_mode = resolve_mode(mode)
|
|
76
79
|
narrow_target = Engine.resolve_narrow_target(narrow_nbsp)
|
|
77
80
|
|
|
@@ -80,19 +83,19 @@ module Polytypo
|
|
|
80
83
|
if dialect
|
|
81
84
|
raise Error.new(CODE_INVALID_DIALECT, '"dialect" is only valid when mode is "markdown"')
|
|
82
85
|
end
|
|
83
|
-
analyze_text(input, locale, rules, narrow_target)
|
|
86
|
+
analyze_text(input, locale, rules, narrow_target, protect)
|
|
84
87
|
when "html"
|
|
85
88
|
if dialect
|
|
86
89
|
raise Error.new(CODE_INVALID_DIALECT, '"dialect" is only valid when mode is "markdown"')
|
|
87
90
|
end
|
|
88
|
-
analyze_html(input, locale, rules, narrow_target)
|
|
91
|
+
analyze_html(input, locale, rules, narrow_target, protect)
|
|
89
92
|
when "yaml"
|
|
90
93
|
if dialect
|
|
91
94
|
raise Error.new(CODE_INVALID_DIALECT, '"dialect" is only valid when mode is "markdown"')
|
|
92
95
|
end
|
|
93
|
-
analyze_yaml(input, locale, keys, rules, narrow_target)
|
|
96
|
+
analyze_yaml(input, locale, keys, rules, narrow_target, protect)
|
|
94
97
|
else # "markdown"
|
|
95
|
-
analyze_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys)
|
|
98
|
+
analyze_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys: frontmatter_keys, protect: protect)
|
|
96
99
|
end
|
|
97
100
|
end
|
|
98
101
|
|
|
@@ -109,18 +112,22 @@ module Polytypo
|
|
|
109
112
|
end
|
|
110
113
|
private_class_method :resolve_mode
|
|
111
114
|
|
|
112
|
-
def self.transform_text(input, locale, rules, narrow_target)
|
|
115
|
+
def self.transform_text(input, locale, rules, narrow_target, protect)
|
|
113
116
|
_resolved, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
114
117
|
cp = Engine::Codepoints.to_codepoints(input)
|
|
118
|
+
holes = Engine::Protect.resolve(protect, cp.length)
|
|
115
119
|
ctx = Engine::RuleContext.new(mode: "text", dialect: nil, locale: locale,
|
|
116
120
|
narrow_target: narrow_target)
|
|
121
|
+
return Engine::Protect.run(cp, holes, plan, locale_data, ctx) unless holes.empty?
|
|
122
|
+
|
|
117
123
|
result = Engine::Pipeline.run_rules(cp, plan, locale_data, ctx)
|
|
118
124
|
Engine::Codepoints.from_codepoints(result)
|
|
119
125
|
end
|
|
120
126
|
private_class_method :transform_text
|
|
121
127
|
|
|
122
|
-
def self.transform_html(input, locale, rules, narrow_target)
|
|
128
|
+
def self.transform_html(input, locale, rules, narrow_target, protect)
|
|
123
129
|
resolved_locale, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
130
|
+
Engine::Protect.refuse_outside_text(protect, "html")
|
|
124
131
|
spans = Modes::Html.html_spans(input)
|
|
125
132
|
ctx = Engine::RuleContext.new(mode: "html", dialect: nil, locale: resolved_locale,
|
|
126
133
|
narrow_target: narrow_target)
|
|
@@ -134,9 +141,10 @@ module Polytypo
|
|
|
134
141
|
# counterpart: with no declared grammar to violate, a file that is not YAML yields few spans or
|
|
135
142
|
# none and comes back byte for byte (modes.md 3.8.3). The only raise this mode adds is +keys+,
|
|
136
143
|
# which is about the call and not the input.
|
|
137
|
-
def self.transform_yaml(input, locale, keys, rules, narrow_target)
|
|
144
|
+
def self.transform_yaml(input, locale, keys, rules, narrow_target, protect)
|
|
138
145
|
resolved_locale, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
139
146
|
resolved_keys = Engine.resolve_yaml_keys(keys)
|
|
147
|
+
Engine::Protect.refuse_outside_text(protect, "yaml")
|
|
140
148
|
spans = Modes::Yaml.yaml_spans(input, resolved_keys)
|
|
141
149
|
ctx = Engine::RuleContext.new(mode: "yaml", dialect: nil, locale: resolved_locale,
|
|
142
150
|
narrow_target: narrow_target)
|
|
@@ -145,7 +153,7 @@ module Polytypo
|
|
|
145
153
|
end
|
|
146
154
|
private_class_method :transform_yaml
|
|
147
155
|
|
|
148
|
-
def self.transform_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys
|
|
156
|
+
def self.transform_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys: nil, protect: nil)
|
|
149
157
|
# Validation order is public, tested behaviour, identical across every runtime: rules (an
|
|
150
158
|
# unknown rule id), then locale (an unknown locale), then dialect, then frontmatter_keys, then
|
|
151
159
|
# parsing (modes.md 3.7.4).
|
|
@@ -153,6 +161,7 @@ module Polytypo
|
|
|
153
161
|
require_relative "polytypo/modes/markdown"
|
|
154
162
|
Modes::Markdown.resolve_dialect(dialect)
|
|
155
163
|
resolved_keys = Engine.resolve_frontmatter_keys(frontmatter_keys)
|
|
164
|
+
Engine::Protect.refuse_outside_text(protect, "markdown")
|
|
156
165
|
ctx = Engine::RuleContext.new(mode: "markdown", dialect: dialect, locale: resolved_locale,
|
|
157
166
|
narrow_target: narrow_target)
|
|
158
167
|
cp = Engine::Codepoints.to_codepoints(input)
|
|
@@ -171,17 +180,21 @@ module Polytypo
|
|
|
171
180
|
private_class_method :markdown_units
|
|
172
181
|
private_class_method :transform_markdown
|
|
173
182
|
|
|
174
|
-
def self.analyze_text(input, locale, rules, narrow_target)
|
|
183
|
+
def self.analyze_text(input, locale, rules, narrow_target, protect)
|
|
175
184
|
_resolved, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
176
185
|
cp = Engine::Codepoints.to_codepoints(input)
|
|
186
|
+
holes = Engine::Protect.resolve(protect, cp.length)
|
|
177
187
|
ctx = Engine::RuleContext.new(mode: "text", dialect: nil, locale: locale,
|
|
178
188
|
narrow_target: narrow_target)
|
|
189
|
+
return Engine::Protect.analyze(cp, holes, plan, locale_data, ctx) unless holes.empty?
|
|
190
|
+
|
|
179
191
|
Engine::Pipeline.run_rules_recording(cp, plan, locale_data, ctx, (0...cp.length).to_a, cp.length)
|
|
180
192
|
end
|
|
181
193
|
private_class_method :analyze_text
|
|
182
194
|
|
|
183
|
-
def self.analyze_html(input, locale, rules, narrow_target)
|
|
195
|
+
def self.analyze_html(input, locale, rules, narrow_target, protect)
|
|
184
196
|
resolved_locale, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
197
|
+
Engine::Protect.refuse_outside_text(protect, "html")
|
|
185
198
|
spans = Modes::Html.html_spans(input)
|
|
186
199
|
ctx = Engine::RuleContext.new(mode: "html", dialect: nil, locale: resolved_locale,
|
|
187
200
|
narrow_target: narrow_target)
|
|
@@ -191,9 +204,10 @@ module Polytypo
|
|
|
191
204
|
|
|
192
205
|
# analyze.md section 1, "yaml" mode: offsets are into the document, not into a span
|
|
193
206
|
# (analyze.md section 6).
|
|
194
|
-
def self.analyze_yaml(input, locale, keys, rules, narrow_target)
|
|
207
|
+
def self.analyze_yaml(input, locale, keys, rules, narrow_target, protect)
|
|
195
208
|
resolved_locale, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
196
209
|
resolved_keys = Engine.resolve_yaml_keys(keys)
|
|
210
|
+
Engine::Protect.refuse_outside_text(protect, "yaml")
|
|
197
211
|
spans = Modes::Yaml.yaml_spans(input, resolved_keys)
|
|
198
212
|
ctx = Engine::RuleContext.new(mode: "yaml", dialect: nil, locale: resolved_locale,
|
|
199
213
|
narrow_target: narrow_target)
|
|
@@ -201,13 +215,14 @@ module Polytypo
|
|
|
201
215
|
end
|
|
202
216
|
private_class_method :analyze_yaml
|
|
203
217
|
|
|
204
|
-
def self.analyze_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys
|
|
218
|
+
def self.analyze_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys: nil, protect: nil)
|
|
205
219
|
# Validation order is public, tested behaviour and is shared with .transform: rules, then
|
|
206
220
|
# locale, then dialect, then frontmatter_keys, then parsing (analyze.md section 4, A1).
|
|
207
221
|
resolved_locale, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
208
222
|
require_relative "polytypo/modes/markdown"
|
|
209
223
|
Modes::Markdown.resolve_dialect(dialect)
|
|
210
224
|
resolved_keys = Engine.resolve_frontmatter_keys(frontmatter_keys)
|
|
225
|
+
Engine::Protect.refuse_outside_text(protect, "markdown")
|
|
211
226
|
ctx = Engine::RuleContext.new(mode: "markdown", dialect: dialect, locale: resolved_locale,
|
|
212
227
|
narrow_target: narrow_target)
|
|
213
228
|
Modes::Runner.analyze_over_units(Engine::Codepoints.to_codepoints(input),
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: polytypo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Iurii Rogulia
|
|
@@ -113,6 +113,7 @@ files:
|
|
|
113
113
|
- lib/polytypo/engine/locale.rb
|
|
114
114
|
- lib/polytypo/engine/origin.rb
|
|
115
115
|
- lib/polytypo/engine/pipeline.rb
|
|
116
|
+
- lib/polytypo/engine/protect.rb
|
|
116
117
|
- lib/polytypo/engine/registry.rb
|
|
117
118
|
- lib/polytypo/engine/rules.rb
|
|
118
119
|
- lib/polytypo/engine/rules/apostrophe.rb
|