polytypo 1.9.0 → 1.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1cbf7a3df3731842997237add29466678684ff9e0cc645389008bd5263dae02c
4
- data.tar.gz: ff61539488d6a7a3dd84db11e05db7096511f3bbaba8a3fd8fb4fa3b9d45a616
3
+ metadata.gz: e84bade0cfecbed7a333cf29ef6bb7ea793a349805cfbd7b8f1f18f7d5ccd721
4
+ data.tar.gz: 9ba16ce6e90eb2f47c3f7478ef541bad6287c48fa6b968d54e7b9f59102c15bc
5
5
  SHA512:
6
- metadata.gz: 2dd0a932acc4441b112d8efe4152b43fe2cab631a1afca3e457b45cd739240685bd022167f6cc241c5eeb580f734c2e64a5bc5c05843fa354236a54400d612d5
7
- data.tar.gz: 88e51e4b4b5c59c9c3ad1d8a5299abb6c7eb5f53634f45dbb9b44878034d8bae40201e2ec8e19da6c674cc718fa4fc0799455d8f9896e2fbc20a49a250e63617
6
+ metadata.gz: ea98e7ac865b6d023a77381c07592961be171b73a9349062b7e4b83758d89447062932642d45d0e337408d4f5888c8e231fda775e5ecee81cc83e7f10c8ef144
7
+ data.tar.gz: '0298aaf8bca95858b5735f407031b86c1a893bb6ca2d3275061b4ef48aaf5367ecad2359565fa937bf05be5a0352c2e77e7a77dae2a8f84ff5d89dabc1ac05e3'
@@ -1 +1 @@
1
- 1.9.0
1
+ 1.10.0
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "cs",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "de-CH",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "de-DE",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "el",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "en-GB",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "en-US",
4
4
  "cases": [
5
5
  {
@@ -3145,6 +3145,136 @@
3145
3145
  "in": "---\rtitle: a \"b\"\r---\r\rBody \"q\".\r",
3146
3146
  "out": "---\rtitle: a \"b\"\r---\r\rBody “q”.\r",
3147
3147
  "note": "modes.md §3.7.3a steps 1 and 5 composed: a byte-order mark in front of a document whose lines end with lone U+000D. Each clause is pinned alone and their combination is where one runtime lost the body entirely — found by an adversarial pass over a finished port, not by any fixture then in the suite. The block is located and skipped, its content is declined for the U+000D, and the body converts."
3148
+ },
3149
+ {
3150
+ "id": "en-us-yaml-lone-cr-plain-untouched",
3151
+ "rule": "dashes",
3152
+ "mode": "yaml",
3153
+ "keys": [
3154
+ "title",
3155
+ "slug",
3156
+ "summary"
3157
+ ],
3158
+ "in": "title: a\rb -- c\n",
3159
+ "out": "title: a\rb -- c\n",
3160
+ "note": "modes.md §3.8.4 step 9 (spec 1.10.0): a U+000D that is not a line's terminator would land inside the scalar's span, and line terminators are never inside a span. The line yields no spans and comes back byte for byte. Until 1.10.0 this converted, with the carriage return inside the span."
3161
+ },
3162
+ {
3163
+ "id": "en-us-yaml-lone-cr-quoted-untouched",
3164
+ "rule": "dashes",
3165
+ "mode": "yaml",
3166
+ "keys": [
3167
+ "title",
3168
+ "slug",
3169
+ "summary"
3170
+ ],
3171
+ "in": "title: \"a\rb -- c\"\n",
3172
+ "out": "title: \"a\rb -- c\"\n",
3173
+ "note": "modes.md §3.8.4 step 9: the same line with a double-quoted scalar. The decline is on the line, not on the scalar form."
3174
+ },
3175
+ {
3176
+ "id": "en-us-yaml-lone-cr-block-scalar-untouched",
3177
+ "rule": "dashes",
3178
+ "mode": "yaml",
3179
+ "keys": [
3180
+ "title",
3181
+ "slug",
3182
+ "summary"
3183
+ ],
3184
+ "in": "title: |\n a -- b\n c\rd -- e\n",
3185
+ "out": "title: |\n a -- b\n c\rd -- e\n",
3186
+ "note": "modes.md §3.8.5 (spec 1.10.0): a U+000D on any line of a block scalar's run makes the whole block yield no spans, as a tab does — the first line, which is clean, is not converted either."
3187
+ },
3188
+ {
3189
+ "id": "en-us-yaml-lone-cr-value-run-consumed",
3190
+ "rule": "dashes",
3191
+ "mode": "yaml",
3192
+ "keys": [
3193
+ "title",
3194
+ "slug",
3195
+ "summary"
3196
+ ],
3197
+ "in": "summary: a\rb\n title: c -- d\n",
3198
+ "out": "summary: a\rb\n title: c -- d\n",
3199
+ "note": "modes.md §3.8.4 step 9: the declined line's value run is still consumed, so the indented `title: c -- d` stays a continuation of `summary` and is not read as a mapping line of its own. Declining without consuming would convert a key that is not a key."
3200
+ },
3201
+ {
3202
+ "id": "en-us-yaml-trailing-cr-is-a-terminator",
3203
+ "rule": "dashes",
3204
+ "mode": "yaml",
3205
+ "keys": [
3206
+ "title",
3207
+ "slug",
3208
+ "summary"
3209
+ ],
3210
+ "in": "title: a -- b\r",
3211
+ "out": "title: a—b\r",
3212
+ "note": "modes.md §3.8.4: a U+000D that ends the input with no U+000A after it is a terminator, like one before a U+000A, so it is not a lone carriage return and the line converts."
3213
+ },
3214
+ {
3215
+ "id": "en-us-yaml-crlf-still-converts",
3216
+ "rule": "dashes",
3217
+ "mode": "yaml",
3218
+ "keys": [
3219
+ "title",
3220
+ "slug",
3221
+ "summary"
3222
+ ],
3223
+ "in": "title: a -- b\r\nslug: c -- d\r\n",
3224
+ "out": "title: a—b\r\nslug: c—d\r\n",
3225
+ "note": "modes.md §3.8.4: CRLF is two terminator characters and neither is lone, so step 9 does not apply to a Windows-line-ended document."
3226
+ },
3227
+ {
3228
+ "id": "en-us-yaml-cr-line-is-not-blank",
3229
+ "rule": "dashes",
3230
+ "mode": "yaml",
3231
+ "keys": [
3232
+ "title",
3233
+ "slug",
3234
+ "summary"
3235
+ ],
3236
+ "in": "title: |\n a -- b\n\r \n c -- d\n",
3237
+ "out": "title: |\n a—b\n\r \n c -- d\n",
3238
+ "note": "modes.md §3.8.4 step 1 (spec 1.10.0): a line is blank only if it is empty or all U+0020, so `<CR>␣␣` is not blank. It has indent 0, which ends the block's value run, and `a -- b` converts while `c -- d` is outside the block. A port that trims U+000D before testing blankness keeps the line in the run and declines the whole block instead."
3239
+ },
3240
+ {
3241
+ "id": "en-us-yaml-double-trailing-cr-untouched",
3242
+ "rule": "dashes",
3243
+ "mode": "yaml",
3244
+ "keys": [
3245
+ "title",
3246
+ "slug",
3247
+ "summary"
3248
+ ],
3249
+ "in": "title: a -- b\r\r",
3250
+ "out": "title: a -- b\r\r",
3251
+ "note": "modes.md §3.8.4: only the last U+000D at the end of the input is a terminator, so the first is a lone one and the line yields no spans. A port that strips every trailing U+000D converts it."
3252
+ },
3253
+ {
3254
+ "id": "en-us-yaml-cr-before-lf-after-cr-untouched",
3255
+ "rule": "dashes",
3256
+ "mode": "yaml",
3257
+ "keys": [
3258
+ "title",
3259
+ "slug",
3260
+ "summary"
3261
+ ],
3262
+ "in": "title: a -- b\r\r\n",
3263
+ "out": "title: a -- b\r\r\n",
3264
+ "note": "modes.md §3.8.4: a U+000D immediately before a U+000A is a terminator, and only that one; the U+000D before it is lone. The same line as the previous case with a final U+000A."
3265
+ },
3266
+ {
3267
+ "id": "en-us-yaml-cr-then-hash-untouched",
3268
+ "rule": "dashes",
3269
+ "mode": "yaml",
3270
+ "keys": [
3271
+ "title",
3272
+ "slug",
3273
+ "summary"
3274
+ ],
3275
+ "in": "title: a\r#c -- d\n",
3276
+ "out": "title: a\r#c -- d\n",
3277
+ "note": "modes.md §3.8.4 step 9: YAML 1.2 reads `<CR>#c -- d` as a comment line, which step 2 declines when it starts a line. The LF-only model sees one line, so the decline has to be on the carriage return itself."
3148
3278
  }
3149
3279
  ]
3150
3280
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "es",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "fi",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "fr-CA",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "fr",
4
4
  "cases": [
5
5
  {
@@ -1025,6 +1025,19 @@
1025
1025
  "in": "---\ntitle: \"a\rb\"\nslug: c \"d\"\n---\n\nBody \"q\" here.\n",
1026
1026
  "out": "---\ntitle: \"a\rb\"\nslug: c « d »\n---\n\nBody « q » here.\n",
1027
1027
  "note": "modes.md §3.7.4 per-line U+000D bail in a locale whose quotation marks carry their own spacing, so the declined line and the converted one are visibly different constructions rather than the same English output relabelled (§8 item 4)."
1028
+ },
1029
+ {
1030
+ "id": "fr-yaml-lone-cr-costs-one-line",
1031
+ "rule": "quotes",
1032
+ "mode": "yaml",
1033
+ "keys": [
1034
+ "title",
1035
+ "slug",
1036
+ "summary"
1037
+ ],
1038
+ "in": "title: a \"b\"\nslug: c\rd \"e\"\n",
1039
+ "out": "title: a « b »\nslug: c\rd \"e\"\n",
1040
+ "note": "modes.md §3.8.4 step 9 in a locale whose quotation marks carry their own spacing: the line with the lone U+000D is declined and the line before it converts, so the cost is one line and not the document."
1028
1041
  }
1029
1042
  ]
1030
1043
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "it",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "cases": [
4
4
  {
5
5
  "id": "exact-en-us",
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "nl",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "pl",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "pt-BR",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "pt-PT",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "ru",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "sv",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "tr",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "locale": "uk",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "$comment": "Locale resolution input. Algorithm is specified in spec/rules/locale-resolution.md and is identical in every runtime — never delegate it to a platform locale-negotiation library. \"spec\" here must track spec/VERSION exactly — it is not itself the global version source; scripts/validate-spec.mjs enforces the match.",
4
4
  "locales": [
5
5
  "en-US",
@@ -7,14 +7,15 @@ for all five runtimes and is parser-agnostic by construction: `parse5`, `nokogir
7
7
  `golang.org/x/net/html` and PHP's DOM disagree about almost everything this document does not
8
8
  forbid them from doing. `yaml` mode is parser-**free** rather than parser-agnostic, for the
9
9
  reason §3.8.1 measures.
10
- **Spec version:** 1.9.0 (0.1.0 for everything except §3.3's class-membership table rows for
10
+ **Spec version:** 1.10.0 (0.1.0 for everything except §3.3's class-membership table rows for
11
11
  `nbsp` and `apostrophe`, split in 1.2.0, §3.8, added in 1.3.0, §3.3's note on `quotes`'
12
12
  span-boundary elision veto reading the marker as a trigger, added in 1.4.0 — which changes no
13
13
  row of the table it follows — §3.3's `CLOSEDELIM` entry, added in 1.5.0, and §3.7.4 with the
14
14
  amendments it carries to §3.1's definitions, §3.2's Model C, §3.5 step 3, §3.7.3's frontmatter
15
15
  bullet, §3.8.6's accepted-cost paragraph, §5, §6 and §7, added in 1.7.0, §3.7.3a, added in
16
16
  1.8.0, and §3.8.6's treatment of `''` in a single-quoted scalar, which replaces the bail it had
17
- until 1.9.0, with the matching amendments to §3.7.4.1, §3.8.7, §5, §7.13 and §8).
17
+ until 1.9.0, with the matching amendments to §3.7.4.1, §3.8.7, §5, §7.13 and §8, and §3.8.4
18
+ step 9 with §3.8.5's content-line condition, added in 1.10.0).
18
19
 
19
20
  ---
20
21
 
@@ -728,8 +729,11 @@ already recognises, and the option only changes what happens inside it:
728
729
  because the window includes it. So `title` / `slug` / a content-final U+000D converts `slug` in
729
730
  `yaml` mode and does not here. The wider decline is the direction this section takes everywhere
730
731
  else — a miss is invisible, a U+000D inside a span is not — and the cost is conversions lost in
731
- documents written with line endings from the last century. Widening §3.8.4 instead would be a
732
- change to `yaml` mode for every caller and wants its own measurement;
732
+ documents written with line endings from the last century. Since 1.10.0 `yaml` mode declines a
733
+ line containing a lone U+000D itself (§3.8.4 step 9), so the two paths agree on a stray U+000D
734
+ inside a line; they still part on the shape named just above. It does
735
+ so by declining rather than by widening §3.8.4's line model to YAML's or CommonMark's, which
736
+ would change what `yaml` mode converts for every caller;
733
737
  - **both delimiter lines stay outside every span**, as does every line terminator, so no edit
734
738
  can reach `---` itself and §3.7.3's setext-underline hazard is unreachable;
735
739
  - an **unterminated** block is not a block — §3.7.3 already yields no frontmatter construct
@@ -988,8 +992,10 @@ throw this mode adds is the option throw of §3.8.2, which is about the call, no
988
992
 
989
993
  The source is the code-point array of §3.1. A **line** is a maximal run containing no U+000A,
990
994
  **and a U+000D immediately before that U+000A is not part of the line** — it is a terminator like
991
- the U+000A itself, so it lies outside every span and comes back untouched. Line terminators are
992
- never inside a span, so no rule can create or destroy one. `indent` is the number of leading
995
+ the U+000A itself, so it lies outside every span and comes back untouched. **The same holds for a
996
+ U+000D that ends the input with no U+000A after it.** Line terminators are never inside a span,
997
+ so no rule can create or destroy one — and since 1.10.0 that sentence is true of a lone U+000D as
998
+ well, by step 9 below and §3.8.5, rather than only of the terminators this paragraph defines. `indent` is the number of leading
993
999
  U+0020 on the line and `i` the index of the first code point after them.
994
1000
 
995
1001
  The carriage-return clause is normative rather than obvious, and it is here because five runtimes
@@ -1006,7 +1012,10 @@ return **inside** its span and hands it to the rules as content.
1006
1012
  Lines are processed in order.
1007
1013
 
1008
1014
  1. The line yields no spans if it is empty or all U+0020, or if it contains U+0009 anywhere. A
1009
- tab makes indentation undecidable, which is what every step below depends on.
1015
+ tab makes indentation undecidable, which is what every step below depends on. **A line is
1016
+ blank only if it is empty or all U+0020**, here and wherever this section says blank (step 7,
1017
+ §3.8.5): a line containing U+0009 or U+000D is never blank, and a port must not test blankness
1018
+ with a trim that strips either.
1010
1019
  2. The line yields no spans if `cp[i]` is `#` (comment) or `%` (directive).
1011
1020
  3. The line yields no spans if it begins at `i` with `---` or with `...` and the next code point
1012
1021
  is U+0020 or the line ends there. The trailing-content form is declined as well as the bare
@@ -1041,7 +1050,14 @@ Lines are processed in order.
1041
1050
  and its value run yield no spans. This is the step that makes `run:`, `if:` and `image:`
1042
1051
  unreachable, and it is checked before the value's form so that an unlisted key costs nothing
1043
1052
  to decline.
1044
- 9. `cp[v]` selects the form:
1053
+ 9. **If the line contains a U+000D, it yields no spans, and its value run (step 7) is still
1054
+ consumed.** The U+000D here is one the line definition above did not take for a terminator — a
1055
+ *lone* one, which YAML 1.2.2 §5.4 reads as a line break and this section's LF-only model does
1056
+ not. It would otherwise land inside a span and be handed to the rules as content, which the
1057
+ opening of this section forbids in terms. The value run is consumed rather than left to be
1058
+ rescanned for the reason step 7 gives: a continuation line is not a mapping line, and
1059
+ `summary: a<CR>b` followed by an indented `title: c` must not make `title` a key. Otherwise
1060
+ `cp[v]` selects the form:
1045
1061
  - `&`, `*` or `!` — anchor, alias or tag — and `{` or `[` — a flow collection: no spans.
1046
1062
  - `#`: the value is absent and only a comment follows: no spans.
1047
1063
  - `|` or `>`: §3.8.5, block scalar.
@@ -1057,10 +1073,14 @@ spans. **The header is never inside a span.**
1057
1073
 
1058
1074
  The block's content is the value run of §3.8.4 step 7. Let `bi` be the indentation of its first
1059
1075
  non-blank line, or `indent + n` when the header carried an explicit indentation indicator `n`.
1060
- **One definition, and three conditions that make the whole block yield no spans** — the block is
1076
+ **One definition, and four conditions that make the whole block yield no spans** — the block is
1061
1077
  consumed either way, never rescanned:
1062
1078
 
1063
1079
  - any line of the run contains U+0009;
1080
+ - any line of the run contains a U+000D (§3.8.4 step 9, 1.10.0) — a lone one, since a terminator's
1081
+ is not part of its line. The whole block goes, as for a tab, rather than the one line: the
1082
+ block's lines are one construct, and a carriage return in the middle of it is evidence that
1083
+ the author's line model is not the one this section reads;
1064
1084
  - the run's first non-blank line is indented less than `bi`, which can only happen when an
1065
1085
  explicit indicator disagrees with the block as written;
1066
1086
  - any later non-blank line of the run is indented less than `bi`.
@@ -1219,6 +1239,21 @@ adjacency veto leaves an adjacent pair alone. The split is therefore specified f
1219
1239
  §3.8.1 gives, that it needs no mapping and puts the pair out of every rule's reach by construction,
1220
1240
  and not because the sweep shows the unsplit alternative to be unsafe at payload length four.
1221
1241
 
1242
+ **A targeted sweep for U+000D (1.10.0).** It is narrower than the sweeps above on purpose —
1243
+ its alphabet carries neither U+005C, `''` nor `---`, which §5 requires of the idempotency sweeps
1244
+ and which those sweeps still carry — because it checks one property, §3.8.4's, that they never
1245
+ tested: **no span contains a line terminator.** Over documents built from the
1246
+ alphabet `a`, U+0020, U+000D, U+000A, `:`, `#`, `'`, `"`, `-` and `|`, in seven templates — a
1247
+ plain, a double-quoted and a single-quoted scalar, a block scalar, an unterminated final line, a
1248
+ payload ahead of a key, and a payload on the continuation line of a CRLF value — at payload
1249
+ lengths 1–5, **777 770 documents**, the scan's spans were read out and tested directly. Before
1250
+ 1.10.0, **95 505 of them (12.3%) had a span containing a U+000D**; `title: a<CR>b -- c` was
1251
+ converted with the carriage return inside the span, and `"aaaa<CR>"` was a quoted scalar whose
1252
+ span ended in one. After, **none**. This check is discriminating by construction, since it
1253
+ failed 95 505 times on the engine it was written against. It reads spans rather than output
1254
+ because no rule emits or deletes a U+000D, so the output never showed it — the guarantee was
1255
+ false and nothing downstream noticed.
1256
+
1222
1257
  **Three things it could not find**, each worth naming because each cost something.
1223
1258
 
1224
1259
  - It did not find the workflow damage, because a shell script inside a block scalar is a string
@@ -1414,7 +1449,10 @@ tests none of this document. In `yaml` the sweep alphabet must include `:`, `#`,
1414
1449
  as one token**, since those are the characters whose adjacency the argument above turns on and a
1415
1450
  three-dash run is not reachable from single dashes at a bounded payload length; and it must
1416
1451
  include U+0022 and U+005C, without which the sweep cannot reach a quoted scalar's delimiters at
1417
- all. Since 1.9.0 it must also include **`''` as one token** next to a lone U+0027, and its
1452
+ all. Since 1.10.0 it must also include **U+000D**, and a `yaml` run must test the property §3.8.4
1453
+ states — that no span contains a line terminator — directly on the spans, because a rule that
1454
+ neither emits nor deletes a U+000D leaves the output unchanged whether or not one is inside a
1455
+ span. Since 1.9.0 it must also include **`''` as one token** next to a lone U+0027, and its
1418
1456
  single-quoted templates must carry the pair at the start, in the middle and at the end of the
1419
1457
  payload, because a unit's behaviour at a span extremity is a different path from its behaviour
1420
1458
  between two letters. Since 1.7.0 the `markdown` run must also carry a template with a **frontmatter block and
@@ -1634,7 +1672,11 @@ rule-local.
1634
1672
  - **a flow collection** — `tags: [one, two]` and `{a: b}` — even on one line;
1635
1673
  - **a quoted scalar containing an escape** — `"say \"hi\""` — where the source spells the
1636
1674
  content with more characters than it has;
1637
- - **a block scalar whose indentation is ambiguous**, per §3.8.5's three bail conditions.
1675
+ - **a block scalar whose indentation is ambiguous**, per §3.8.5's bail conditions;
1676
+ - **a line carrying a lone U+000D** (1.10.0), and a block scalar with one on any line. Measured
1677
+ on the author's 367-file content directory: no file contains one. It is the price of keeping §3.8.4's line model
1678
+ LF-only while not letting a carriage return into a span, and it falls on documents
1679
+ written with Classic Mac line endings.
1638
1680
 
1639
1681
  12. **`keys` matches a bare name at any depth, and nothing narrower.** `description` is
1640
1682
  processable wherever it occurs, which is what makes the option portable — no paths, no
@@ -1672,7 +1714,13 @@ rule-local.
1672
1714
  line, so that a stray one inside a single quoted value costs that value and not the block.
1673
1715
  The block is still skipped, so nothing machine-read is typeset either way. A lone-U+000D
1674
1716
  document is one line to §3.8.4 and therefore loses the whole block, which is the price of
1675
- not widening that section here.
1717
+ not widening that section here. Since 1.10.0 `yaml` mode makes the same decline for itself
1718
+ (§3.8.4 step 9), per line and consuming the value run, so the two paths no longer differ on
1719
+ a stray U+000D inside a line. One shape of the same character is still missed in both, and
1720
+ is recorded rather than fixed: a U+000D at column 0 of a line inside an *unlisted* key's
1721
+ block scalar ends that value run under §3.8.4's LF-only model, so a listed-looking line
1722
+ after it is scanned as a mapping line of its own, which YAML would read as block content.
1723
+ Closing it needs the line model widened, which this release declines to do.
1676
1724
  - **§3.8.6's treatment of `''` in a single-quoted scalar: the cost, before and after 1.9.0.**
1677
1725
  Until 1.9.0 a single-quoted scalar containing `''` yielded no spans. An apostrophe inside
1678
1726
  such a scalar is spelled `''`, and an apostrophe is exactly what `apostrophe` and `quotes`
@@ -1728,7 +1776,7 @@ changes.
1728
1776
  blocks, and the JSX-vs-skipped-element case distinction (`tests/modes/markdown.test.ts`).
1729
1777
  3a. **YAML span selection** — the three scalar forms, the `keys` option (a listed key, an
1730
1778
  unlisted one, a quoted one, the same name at two depths), block-scalar indentation and
1731
- chomping, the `:`/`#` split of §3.8.6, the `''` split of §3.8.6 (since 1.9.0), step 7's consumption of an inline value's continuation
1779
+ chomping, the `:`/`#` split of §3.8.6, the `''` split of §3.8.6 (since 1.9.0), a lone U+000D (since 1.10.0), step 7's consumption of an inline value's continuation
1732
1780
  lines, and every bail of §3.8.4. `yaml` carries a heavier fixture burden than the other two
1733
1781
  for a reason §8's opening argument does not cover: its span selection is **specified rather
1734
1782
  than delegated** (§3.8.1), so a fixture is the only thing standing between five hand-written
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.9.0",
2
+ "spec": "1.10.0",
3
3
  "$comment": "Single source of truth for pipeline order. Rules run in ascending `order`. Disabling a rule removes it from the sequence and never reorders the rest. Rule ids are public API (see docs/ARCHITECTURE.md section 5). \"spec\" here must track spec/VERSION exactly — it is not itself the global version source; scripts/validate-spec.mjs enforces the match.",
4
4
  "rules": [
5
5
  {
@@ -75,6 +75,13 @@ module Polytypo
75
75
  end
76
76
  private_class_method :tab?
77
77
 
78
+ # A U+000D the line definition did not take for a terminator, i.e. one that is not part of
79
+ # a CRLF and does not end the input. It would otherwise land inside a span.
80
+ def self.lone_cr?(chars, line)
81
+ (line.start...line.end).any? { |i| chars[i] == CR }
82
+ end
83
+ private_class_method :lone_cr?
84
+
78
85
  # 3.8.4 step 3. "---" and "..." at the head of a line, bare or introducing a node: the
79
86
  # trailing-content form is declined too, so "--- key: value" never yields a key of
80
87
  # "--- key".
@@ -176,7 +183,10 @@ module Polytypo
176
183
  # costs nothing to decline: this is what makes "run:", "if:" and "image:" unreachable.
177
184
  return nxt unless keys.include?(chars[key_start...key_end].join)
178
185
 
179
- # step 9 -- the scalar form.
186
+ # step 9 -- a lone U+000D declines the line, and its value run is still consumed.
187
+ return nxt if lone_cr?(chars, line)
188
+
189
+ # step 10 -- the scalar form.
180
190
  value = chars[v]
181
191
  return nxt if NON_SCALAR_VALUE_CHARS.include?(value)
182
192
 
@@ -217,17 +227,18 @@ module Polytypo
217
227
  h += 1 while h < line.end && chars[h] == SPACE
218
228
  return if h < line.end && chars[h] != "#"
219
229
 
220
- # One definition of the run, and three conditions that make the whole block yield no
230
+ # One definition of the run, and four conditions that make the whole block yield no
221
231
  # spans.
222
232
  content = []
223
233
  content_indent = -1
224
234
  run_lines.each do |nxt|
225
235
  next if blank?(chars, nxt) # blank lines belong to the block and yield no span
226
236
 
227
- # A tab makes indentation undecidable; an explicit indicator that disagrees with the
228
- # block as written, or a later line dedented inside it, is ambiguous rather than
229
- # guessable. All three make the WHOLE block yield no spans -- bail rather than choose.
230
- break content_indent = -1 if tab?(chars, nxt)
237
+ # A tab makes indentation undecidable, and a lone U+000D would land inside a span; an
238
+ # explicit indicator that disagrees with the block as written, or a later line dedented
239
+ # inside it, is ambiguous rather than guessable. All of them make the WHOLE block yield
240
+ # no spans -- bail rather than choose.
241
+ break content_indent = -1 if tab?(chars, nxt) || lone_cr?(chars, nxt)
231
242
 
232
243
  next_indent = first_non_space(chars, nxt) - nxt.start
233
244
  if content_indent.negative?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polytypo
4
- VERSION = "1.9.0"
4
+ VERSION = "1.10.0"
5
5
  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.9.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iurii Rogulia