polytypo 1.6.2 → 1.7.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 +25 -0
- data/lib/polytypo/data/.not-vendored +11 -0
- data/lib/polytypo/data/README.md +16 -11
- data/lib/polytypo/data/VERSION +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 +14 -1
- data/lib/polytypo/data/fixtures/el.json +1 -1
- data/lib/polytypo/data/fixtures/en-GB.json +1 -1
- data/lib/polytypo/data/fixtures/en-US.json +143 -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 +13 -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/ru.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/cs.json +56 -37
- data/lib/polytypo/data/locales/de-CH.json +43 -34
- data/lib/polytypo/data/locales/de-DE.json +47 -32
- data/lib/polytypo/data/locales/el.json +51 -1
- data/lib/polytypo/data/locales/en-GB.json +56 -4
- data/lib/polytypo/data/locales/en-US.json +68 -4
- data/lib/polytypo/data/locales/es.json +66 -29
- data/lib/polytypo/data/locales/fi.json +77 -7
- data/lib/polytypo/data/locales/fr-CA.json +63 -36
- data/lib/polytypo/data/locales/fr.json +70 -41
- data/lib/polytypo/data/locales/it.json +65 -22
- data/lib/polytypo/data/locales/nl.json +61 -29
- data/lib/polytypo/data/locales/pl.json +61 -28
- data/lib/polytypo/data/locales/pt-BR.json +53 -28
- data/lib/polytypo/data/locales/pt-PT.json +55 -28
- data/lib/polytypo/data/locales/registry.json +1 -1
- data/lib/polytypo/data/locales/ru.json +45 -30
- data/lib/polytypo/data/locales/sv.json +63 -4
- data/lib/polytypo/data/locales/tr.json +72 -32
- data/lib/polytypo/data/locales/uk.json +55 -27
- data/lib/polytypo/data/rules/modes.md +243 -11
- data/lib/polytypo/data/rules/order.json +1 -1
- data/lib/polytypo/data/rules/quotes.md +27 -9
- data/lib/polytypo/data/schema/fixtures.schema.json +12 -1
- data/lib/polytypo/engine/pipeline.rb +29 -0
- data/lib/polytypo/modes/markdown.rb +51 -0
- data/lib/polytypo/modes/runner.rb +33 -3
- data/lib/polytypo/version.rb +1 -1
- data/lib/polytypo.rb +27 -12
- metadata +2 -1
|
@@ -605,12 +605,18 @@ spec 1.4.0. Measured on this change, `markdown`/`commonmark`, `en-GB`:
|
|
|
605
605
|
words early and the author's own closing mark is left to `apostrophe` as a stray U+2019. §1 of
|
|
606
606
|
that issue is closed by the veto above; §3 is not, and is not going to be.
|
|
607
607
|
|
|
608
|
-
**Why §3 is decided and not merely unfixed.** Two measurements
|
|
608
|
+
**Why §3 is decided and not merely unfixed.** Two measurements — the first on a published
|
|
609
|
+
package, the second on a build patched with the veto it measures, since no release contains it.
|
|
609
610
|
|
|
610
611
|
First, **the U+2019 at the possessive's own position is produced by the pairing, and nothing else
|
|
611
|
-
can produce it.** `apostrophe` cannot
|
|
612
|
-
|
|
613
|
-
|
|
612
|
+
can produce it.** Not because `apostrophe` cannot see past a span boundary — it can, and
|
|
613
|
+
[apostrophe.md](apostrophe.md) §3.3 case 4 says so: the marker is in that rule's `OPENISH`
|
|
614
|
+
(`modes.md` §3.3), which is exactly why `` `x`'s `` reaches case 4 and comes out `` `x`’s ``. It is
|
|
615
|
+
this shape it cannot reach. Case 4 is the only case in the ladder that accepts the marker on the
|
|
616
|
+
left, and it requires `ALNUM` on the **right**; a plural possessive has a space there. Cases 2 and
|
|
617
|
+
2a read `ALNUM` and `CLOSEDELIM` on the left, and cases 3 and 3a read `LETTER`; the marker is in
|
|
618
|
+
none of those three classes (`modes.md` §3.3). So the ladder falls through to case 5 and emits
|
|
619
|
+
nothing. Measured on published 1.6.1,
|
|
614
620
|
`markdown`/`commonmark`, `en-GB`: `` The `xs`' printer works. `` comes back **unchanged** — an
|
|
615
621
|
unmatched span-boundary plural possessive keeps its U+0027. So the cost of this behaviour is a
|
|
616
622
|
consumed *pairing*, and the visible symptom of that appears elsewhere (a stray U+0027 at the real
|
|
@@ -1597,11 +1603,23 @@ reach — the plural possessive, which is byte-identical to a closing mark after
|
|
|
1597
1603
|
1.6.2 corrects §3.2's own measurements of the decision above; no behaviour, fixture or locale
|
|
1598
1604
|
field changes. Two errors, both introduced with the decision record on 2026-09-24 and both
|
|
1599
1605
|
overstating how cheap the decided behaviour is. The first claimed the glyph at the possessive's
|
|
1600
|
-
position was "correct either way" — it is not: `apostrophe`
|
|
1601
|
-
|
|
1602
|
-
published 1.6.1 package. The second counted **7 of 2801 conformance cases** where 2801 is
|
|
1603
|
-
polytypo-js's
|
|
1604
|
-
1366 fixture cases**, and the sixth — this section's own `en-GB` fixture — is now described
|
|
1606
|
+
position was "correct either way" — it is not: `apostrophe` does not reach *this* mark, the plural
|
|
1607
|
+
shape, for the reason the 1.6.3 entry below states, so declining the pairing leaves U+0027 there.
|
|
1608
|
+
Measured on the published 1.6.1 package. The second counted **7 of 2801 conformance cases** where 2801 is the test
|
|
1609
|
+
count of polytypo-js's conformance runner, a figure no other runtime can reproduce; the cost is
|
|
1610
|
+
**6 of the 1366 fixture cases**, and the sixth — this section's own `en-GB` fixture — is now described
|
|
1605
1611
|
rather than left in the count, because the veto moves the pair to the author's mark correctly and
|
|
1606
1612
|
moves the typewriter apostrophe to the possessive at the same time. A conformance count in this
|
|
1607
1613
|
document is a count of fixture cases, never of one runner's tests.
|
|
1614
|
+
|
|
1615
|
+
1.6.3 corrects §3.2's account of *why* the first of those measurements comes out the way it does;
|
|
1616
|
+
again no behaviour, fixture or locale field changes. The 1.6.2 text said `apostrophe` cannot reach
|
|
1617
|
+
a mark whose left neighbour is the inline marker and attributed that to the marker's exclusion
|
|
1618
|
+
from [apostrophe.md](apostrophe.md) §3.1's `CLOSEDELIM`. Both halves were wrong, and the second
|
|
1619
|
+
contradicted `apostrophe.md` §3.3 case 2a's own note in the same vendored tree: the marker is in
|
|
1620
|
+
that rule's `OPENISH`, so `` `x`'s `` does reach case 4 and is curled. What the ladder cannot
|
|
1621
|
+
reach is the *plural* shape, because case 4 — the only case accepting the marker on the left —
|
|
1622
|
+
requires `ALNUM` on the right, and cases 2, 2a, 3 and 3a all read a class the marker is not in.
|
|
1623
|
+
`CLOSEDELIM` is irrelevant to it either way, since case 2a reads `ALNUM` on the right as well. The
|
|
1624
|
+
same entry also called 2801 a vitest total; it is one runner's conformance test count, and the
|
|
1625
|
+
runtime's own total is 4440.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://polytypo.org/schema/fixtures.schema.json",
|
|
4
4
|
"title": "polytypo conformance fixtures",
|
|
5
|
-
"description": "Executable form of the rule semantics. Every case is also an idempotency case: a runner must additionally assert transform(out) == out, passing the case's own options (mode, dialect, keys, rules, narrowNbsp) on that second call exactly as on the first — a case is a fixed point under its own options, not under the defaults.",
|
|
5
|
+
"description": "Executable form of the rule semantics. Every case is also an idempotency case: a runner must additionally assert transform(out) == out, passing the case's own options (mode, dialect, keys, frontmatterKeys, rules, narrowNbsp) on that second call exactly as on the first — a case is a fixed point under its own options, not under the defaults.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["spec", "locale", "cases"],
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
"if": { "properties": { "mode": { "const": "yaml" } }, "required": ["mode"] },
|
|
30
30
|
"then": { "required": ["keys"] },
|
|
31
31
|
"else": { "not": { "required": ["keys"] } }
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"if": { "properties": { "mode": { "const": "markdown" } }, "required": ["mode"] },
|
|
35
|
+
"then": true,
|
|
36
|
+
"else": { "not": { "required": ["frontmatterKeys"] } }
|
|
32
37
|
}
|
|
33
38
|
],
|
|
34
39
|
"oneOf": [{ "required": ["out"] }, { "required": ["throws"] }],
|
|
@@ -62,6 +67,12 @@
|
|
|
62
67
|
"items": { "type": "string" },
|
|
63
68
|
"description": "Required when mode is \"yaml\" — spec/rules/modes.md §3.8.2 gives the option no default, so a fixture must name the processable keys exactly as a caller would. An empty array is legal and processes nothing. Meaningless for the other modes."
|
|
64
69
|
},
|
|
70
|
+
"frontmatterKeys": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"items": { "type": "string" },
|
|
73
|
+
"description": "Optional, spec 1.7.0, and only meaningful when mode is \"markdown\" — spec/rules/modes.md §3.7.4's opt-out from the frontmatter skip. Absent means the block is skipped whole, exactly as before 1.7.0; an empty array is legal and yields no spans. A runner passes it through to transform() as it passes dialect.",
|
|
74
|
+
"$comment": "The block is its own text unit (§3.7.4), so this option cannot change a byte of the body — every case released before 1.7.0 keeps its recorded output whatever it is set to."
|
|
75
|
+
},
|
|
65
76
|
"in": { "type": "string" },
|
|
66
77
|
"out": { "type": "string" },
|
|
67
78
|
"throws": {
|
|
@@ -59,6 +59,35 @@ module Polytypo
|
|
|
59
59
|
value.to_set
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# modes.md 3.7.4: "markdown" mode's `frontmatter_keys` option (spec 1.7.0).
|
|
63
|
+
#
|
|
64
|
+
# Optional, unlike `keys` -- nil means the frontmatter block is skipped whole, which is every
|
|
65
|
+
# pre-1.7.0 document's behaviour -- and an empty Array is legal, exactly as it is for `keys`.
|
|
66
|
+
# Checked after dialect and before the parse, which is what decides that a document failing to
|
|
67
|
+
# parse in its dialect still reports the option error rather than CODE_MALFORMED_INPUT.
|
|
68
|
+
def self.resolve_frontmatter_keys(value)
|
|
69
|
+
return nil if value.nil?
|
|
70
|
+
|
|
71
|
+
unless value.is_a?(Array)
|
|
72
|
+
raise Polytypo::Error.new(
|
|
73
|
+
Polytypo::CODE_INVALID_OPTION,
|
|
74
|
+
'"frontmatter_keys" must be an Array of Strings when given (modes.md 3.7.4). ' \
|
|
75
|
+
"Received #{value.inspect}.",
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
value.each do |key|
|
|
80
|
+
next if key.is_a?(String)
|
|
81
|
+
|
|
82
|
+
raise Polytypo::Error.new(
|
|
83
|
+
Polytypo::CODE_INVALID_OPTION,
|
|
84
|
+
"\"frontmatter_keys\" must contain only Strings; received #{key.inspect}.",
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
value.to_set
|
|
89
|
+
end
|
|
90
|
+
|
|
62
91
|
# Resolves the locale, builds the rule plan, and runs each enabled rule in
|
|
63
92
|
# spec/rules/order.json order over a code-point array, applying its edits before the next
|
|
64
93
|
# rule sees it. No module-level mutable state beyond the immutable, load-once-on-first-use
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "spans"
|
|
4
4
|
require_relative "html"
|
|
5
|
+
require_relative "yaml"
|
|
5
6
|
require_relative "parse_error"
|
|
6
7
|
require_relative "../errors"
|
|
7
8
|
|
|
@@ -66,6 +67,56 @@ module Polytypo
|
|
|
66
67
|
end
|
|
67
68
|
private_class_method :detect_frontmatter_delimiter
|
|
68
69
|
|
|
70
|
+
# modes.md 3.7.4, spec 1.7.0. The frontmatter block's own spans, which form a SECOND TEXT
|
|
71
|
+
# UNIT: the pipeline runs over them separately from the body's, so an unbalanced mark in a
|
|
72
|
+
# metadata field can never pair with one in the first paragraph, and the option cannot
|
|
73
|
+
# change a byte outside the block.
|
|
74
|
+
#
|
|
75
|
+
# Spans come from the scan of modes.md 3.8 -- frontmatter IS YAML, and implementing that
|
|
76
|
+
# grammar twice is how two implementations of one spec drift -- with +keys+ as step 8's key
|
|
77
|
+
# predicate. The block is the construct markdown_spans skips (3.7.3, the :frontmatter node),
|
|
78
|
+
# so the option only ever adds spans where the skip removed them: no source position belongs
|
|
79
|
+
# to both units. A TOML block yields nothing, with the option or without it -- its quoting is
|
|
80
|
+
# a second grammar this scan does not claim (modes.md 7.13).
|
|
81
|
+
#
|
|
82
|
+
# The content range comes from the node rather than from a second scan of the text: comrak
|
|
83
|
+
# reports the block from its opening delimiter line through its closing one, so the content
|
|
84
|
+
# is every line between them, and both delimiters and every line terminator stay outside
|
|
85
|
+
# every span.
|
|
86
|
+
def self.frontmatter_spans(source, keys)
|
|
87
|
+
require "commonmarker"
|
|
88
|
+
return [] if keys.empty?
|
|
89
|
+
return [] unless detect_frontmatter_delimiter(source) == "---"
|
|
90
|
+
|
|
91
|
+
cp = Polytypo::Engine::Codepoints.to_codepoints(source)
|
|
92
|
+
chars = cp.map { |c| [c].pack("U") }
|
|
93
|
+
line_starts = build_line_starts(chars)
|
|
94
|
+
|
|
95
|
+
spans = []
|
|
96
|
+
ParseError.wrap do
|
|
97
|
+
doc = Commonmarker.parse(
|
|
98
|
+
source,
|
|
99
|
+
options: { parse: { sourcepos_chars: true },
|
|
100
|
+
extension: { front_matter_delimiter: "---" } },
|
|
101
|
+
)
|
|
102
|
+
doc.each do |node|
|
|
103
|
+
next unless node.type == :frontmatter
|
|
104
|
+
|
|
105
|
+
pos = node.source_position
|
|
106
|
+
content_start = line_starts[pos[:start_line]]
|
|
107
|
+
content_end = line_starts[pos[:end_line] - 1]
|
|
108
|
+
break if content_start.nil? || content_end.nil? || content_end <= content_start
|
|
109
|
+
|
|
110
|
+
content = chars[content_start...content_end].join
|
|
111
|
+
Yaml.yaml_spans(content, keys).each do |span|
|
|
112
|
+
spans << Spans::Span.new(span.start + content_start, span.end + content_start)
|
|
113
|
+
end
|
|
114
|
+
break
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
spans
|
|
118
|
+
end
|
|
119
|
+
|
|
69
120
|
# Locates the processable spans of a Markdown document. dialect must already be validated
|
|
70
121
|
# via resolve_dialect (== "commonmark"); this function does not re-check it.
|
|
71
122
|
def self.markdown_spans(source)
|
|
@@ -34,17 +34,46 @@ module Polytypo
|
|
|
34
34
|
# source_cp is the whole input already converted to code points; spans address it by
|
|
35
35
|
# code-point index.
|
|
36
36
|
def self.run_over_spans(source_cp, spans, plan, locale_data, ctx)
|
|
37
|
+
emit(source_cp, replacements_of_unit(source_cp, spans, plan, locale_data, ctx))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# modes.md 3.1 and 3.5 step 3 (spec 1.7.0). A document has one text unit, except in
|
|
41
|
+
# "markdown" with frontmatter_keys, where the frontmatter block's spans form a unit of their
|
|
42
|
+
# own. The pipeline runs once per unit and the two edit sets are disjoint, because no span of
|
|
43
|
+
# one unit lies inside the other -- which is what the body's walk skipping the block
|
|
44
|
+
# guarantees. Only step 5 is shared: the source is emitted once, in document order.
|
|
45
|
+
def self.run_over_units(source_cp, units, plan, locale_data, ctx)
|
|
46
|
+
replacements = units.flat_map do |spans|
|
|
47
|
+
replacements_of_unit(source_cp, spans, plan, locale_data, ctx)
|
|
48
|
+
end
|
|
49
|
+
emit(source_cp, replacements.sort_by { |span, _piece| span.start })
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# analyze_over_spans per text unit (modes.md 3.1), reported in document order.
|
|
53
|
+
def self.analyze_over_units(source_cp, units, plan, locale_data, ctx)
|
|
54
|
+
units.flat_map { |spans| analyze_over_spans(source_cp, spans, plan, locale_data, ctx) }
|
|
55
|
+
.sort_by(&:start)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# One text unit: the marker-separated concatenation, the pipeline, and the pieces it
|
|
59
|
+
# produced, paired with the spans they replace.
|
|
60
|
+
def self.replacements_of_unit(source_cp, spans, plan, locale_data, ctx)
|
|
37
61
|
normalized = Spans.normalize_spans(spans)
|
|
38
|
-
return
|
|
62
|
+
return [] if normalized.empty?
|
|
39
63
|
|
|
40
64
|
concatenated = Spans.concatenate_spans(source_cp, normalized)
|
|
41
65
|
transformed = run_rules_over_spans(concatenated, plan, locale_data, ctx)
|
|
42
66
|
pieces = Spans.split_on_marker(transformed, normalized.length)
|
|
67
|
+
normalized.each_with_index.map { |span, i| [span, pieces[i]] }
|
|
68
|
+
end
|
|
69
|
+
private_class_method :replacements_of_unit
|
|
43
70
|
|
|
71
|
+
# modes.md 4: the source with disjoint replacements applied at recorded offsets, and nothing
|
|
72
|
+
# else changed.
|
|
73
|
+
def self.emit(source_cp, replacements)
|
|
44
74
|
out = []
|
|
45
75
|
cursor = 0
|
|
46
|
-
|
|
47
|
-
piece = pieces[i]
|
|
76
|
+
replacements.each do |span, piece|
|
|
48
77
|
original = source_cp[span.start...span.end]
|
|
49
78
|
out.concat(source_cp[cursor...span.start])
|
|
50
79
|
out.concat(piece == original ? original : piece)
|
|
@@ -53,6 +82,7 @@ module Polytypo
|
|
|
53
82
|
out.concat(source_cp[cursor..])
|
|
54
83
|
out.pack("U*")
|
|
55
84
|
end
|
|
85
|
+
private_class_method :emit
|
|
56
86
|
|
|
57
87
|
# run_over_spans, reporting instead of applying (analyze.md section 1). The span table
|
|
58
88
|
# supplies the origin map, so every change comes back in DOCUMENT coordinates -- analyze.md
|
data/lib/polytypo/version.rb
CHANGED
data/lib/polytypo.rb
CHANGED
|
@@ -32,7 +32,8 @@ module Polytypo
|
|
|
32
32
|
#
|
|
33
33
|
# Only `mode: "text"`/`"html"` ever touch this file's own requires; `mode: "markdown"` lazily
|
|
34
34
|
# requires "commonmarker" from within Modes::Markdown, never at load time of this file.
|
|
35
|
-
def self.transform(input, locale:, mode: "text", dialect: nil, keys: nil, rules: nil, narrow_nbsp: nil
|
|
35
|
+
def self.transform(input, locale:, mode: "text", dialect: nil, keys: nil, rules: nil, narrow_nbsp: nil,
|
|
36
|
+
frontmatter_keys: nil)
|
|
36
37
|
resolved_mode = resolve_mode(mode)
|
|
37
38
|
narrow_target = Engine.resolve_narrow_target(narrow_nbsp)
|
|
38
39
|
|
|
@@ -53,7 +54,7 @@ module Polytypo
|
|
|
53
54
|
end
|
|
54
55
|
transform_yaml(input, locale, keys, rules, narrow_target)
|
|
55
56
|
else # "markdown"
|
|
56
|
-
transform_markdown(input, locale, dialect, rules, narrow_target)
|
|
57
|
+
transform_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys)
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
|
|
@@ -69,7 +70,8 @@ module Polytypo
|
|
|
69
70
|
# for the text (analyze.md sections 4 and 5).
|
|
70
71
|
#
|
|
71
72
|
# Pure and thread-safe on the same terms as .transform.
|
|
72
|
-
def self.analyze(input, locale:, mode: "text", dialect: nil, keys: nil, rules: nil, narrow_nbsp: nil
|
|
73
|
+
def self.analyze(input, locale:, mode: "text", dialect: nil, keys: nil, rules: nil, narrow_nbsp: nil,
|
|
74
|
+
frontmatter_keys: nil)
|
|
73
75
|
resolved_mode = resolve_mode(mode)
|
|
74
76
|
narrow_target = Engine.resolve_narrow_target(narrow_nbsp)
|
|
75
77
|
|
|
@@ -90,7 +92,7 @@ module Polytypo
|
|
|
90
92
|
end
|
|
91
93
|
analyze_yaml(input, locale, keys, rules, narrow_target)
|
|
92
94
|
else # "markdown"
|
|
93
|
-
analyze_markdown(input, locale, dialect, rules, narrow_target)
|
|
95
|
+
analyze_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys)
|
|
94
96
|
end
|
|
95
97
|
end
|
|
96
98
|
|
|
@@ -143,18 +145,30 @@ module Polytypo
|
|
|
143
145
|
end
|
|
144
146
|
private_class_method :transform_yaml
|
|
145
147
|
|
|
146
|
-
def self.transform_markdown(input, locale, dialect, rules, narrow_target)
|
|
148
|
+
def self.transform_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys = nil)
|
|
147
149
|
# Validation order is public, tested behaviour, identical across every runtime: rules (an
|
|
148
|
-
# unknown rule id), then locale (an unknown locale), then dialect
|
|
150
|
+
# unknown rule id), then locale (an unknown locale), then dialect, then frontmatter_keys, then
|
|
151
|
+
# parsing (modes.md 3.7.4).
|
|
149
152
|
resolved_locale, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
150
153
|
require_relative "polytypo/modes/markdown"
|
|
151
154
|
Modes::Markdown.resolve_dialect(dialect)
|
|
152
|
-
|
|
155
|
+
resolved_keys = Engine.resolve_frontmatter_keys(frontmatter_keys)
|
|
153
156
|
ctx = Engine::RuleContext.new(mode: "markdown", dialect: dialect, locale: resolved_locale,
|
|
154
157
|
narrow_target: narrow_target)
|
|
155
158
|
cp = Engine::Codepoints.to_codepoints(input)
|
|
156
|
-
Modes::Runner.
|
|
159
|
+
Modes::Runner.run_over_units(cp, markdown_units(input, resolved_keys), plan, locale_data, ctx)
|
|
157
160
|
end
|
|
161
|
+
|
|
162
|
+
# modes.md 3.7.4: the body, and -- only when the caller named frontmatter keys -- the
|
|
163
|
+
# frontmatter block as a second text unit. With frontmatter_keys nil this is exactly the single
|
|
164
|
+
# unit every document had before spec 1.7.0, which is why no released output can move.
|
|
165
|
+
def self.markdown_units(input, resolved_keys)
|
|
166
|
+
body = Modes::Markdown.markdown_spans(input)
|
|
167
|
+
return [body] if resolved_keys.nil?
|
|
168
|
+
|
|
169
|
+
[Modes::Markdown.frontmatter_spans(input, resolved_keys), body]
|
|
170
|
+
end
|
|
171
|
+
private_class_method :markdown_units
|
|
158
172
|
private_class_method :transform_markdown
|
|
159
173
|
|
|
160
174
|
def self.analyze_text(input, locale, rules, narrow_target)
|
|
@@ -187,16 +201,17 @@ module Polytypo
|
|
|
187
201
|
end
|
|
188
202
|
private_class_method :analyze_yaml
|
|
189
203
|
|
|
190
|
-
def self.analyze_markdown(input, locale, dialect, rules, narrow_target)
|
|
204
|
+
def self.analyze_markdown(input, locale, dialect, rules, narrow_target, frontmatter_keys = nil)
|
|
191
205
|
# Validation order is public, tested behaviour and is shared with .transform: rules, then
|
|
192
|
-
# locale, then dialect
|
|
206
|
+
# locale, then dialect, then frontmatter_keys, then parsing (analyze.md section 4, A1).
|
|
193
207
|
resolved_locale, locale_data, plan = Engine::Pipeline.prepare(locale, rules)
|
|
194
208
|
require_relative "polytypo/modes/markdown"
|
|
195
209
|
Modes::Markdown.resolve_dialect(dialect)
|
|
196
|
-
|
|
210
|
+
resolved_keys = Engine.resolve_frontmatter_keys(frontmatter_keys)
|
|
197
211
|
ctx = Engine::RuleContext.new(mode: "markdown", dialect: dialect, locale: resolved_locale,
|
|
198
212
|
narrow_target: narrow_target)
|
|
199
|
-
Modes::Runner.
|
|
213
|
+
Modes::Runner.analyze_over_units(Engine::Codepoints.to_codepoints(input),
|
|
214
|
+
markdown_units(input, resolved_keys), plan, locale_data, ctx)
|
|
200
215
|
end
|
|
201
216
|
private_class_method :analyze_markdown
|
|
202
217
|
end
|
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.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Iurii Rogulia
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- README.md
|
|
37
37
|
- lib/polytypo.rb
|
|
38
38
|
- lib/polytypo/data/.not-canonical
|
|
39
|
+
- lib/polytypo/data/.not-vendored
|
|
39
40
|
- lib/polytypo/data/README.md
|
|
40
41
|
- lib/polytypo/data/UNICODE
|
|
41
42
|
- lib/polytypo/data/VERSION
|