polytypo 1.8.1 → 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 +4 -4
- data/README.md +3 -3
- 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 +12 -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 +194 -9
- 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 +38 -3
- 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/registry.json +1 -1
- data/lib/polytypo/data/rules/modes.md +141 -42
- data/lib/polytypo/data/rules/order.json +1 -1
- data/lib/polytypo/modes/yaml.rb +34 -13
- data/lib/polytypo/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e84bade0cfecbed7a333cf29ef6bb7ea793a349805cfbd7b8f1f18f7d5ccd721
|
|
4
|
+
data.tar.gz: 9ba16ce6e90eb2f47c3f7478ef541bad6287c48fa6b968d54e7b9f59102c15bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ea98e7ac865b6d023a77381c07592961be171b73a9349062b7e4b83758d89447062932642d45d0e337408d4f5888c8e231fda775e5ecee81cc83e7f10c8ef144
|
|
7
|
+
data.tar.gz: '0298aaf8bca95858b5735f407031b86c1a893bb6ca2d3275061b4ef48aaf5367ecad2359565fa937bf05be5a0352c2e77e7a77dae2a8f84ff5d89dabc1ac05e3'
|
data/README.md
CHANGED
|
@@ -104,9 +104,9 @@ Polytypo.transform(
|
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
Without `frontmatter_keys:` nothing in the block changes, and a key you do not name never does. The
|
|
107
|
-
block is read with the same scan `yaml` mode uses, so it refuses the same constructs
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
block is read with the same scan `yaml` mode uses, so it refuses the same constructs, and
|
|
108
|
+
reads a doubled apostrophe in a single-quoted scalar as an opaque unit: in `title: 'It''s a test'`
|
|
109
|
+
the `''` is left alone and the text on either side is processed. The block is also processed separately from
|
|
110
110
|
the body, which shows up in exactly one place: an unbalanced quotation mark in `title` cannot pair
|
|
111
111
|
with a mark in your first paragraph.
|
|
112
112
|
|
data/lib/polytypo/data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.10.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"spec": "1.
|
|
2
|
+
"spec": "1.10.0",
|
|
3
3
|
"locale": "de-DE",
|
|
4
4
|
"cases": [
|
|
5
5
|
{
|
|
@@ -776,6 +776,17 @@
|
|
|
776
776
|
"in": "---\ntitle: Er sagte \"hallo\" laut\nsummary: Noch \"eins\" hier\nslug: \"er-sagte-hallo\"\n---\n\nIm Text \"so\" hier.\n",
|
|
777
777
|
"out": "---\ntitle: Er sagte „hallo“ laut\nsummary: Noch „eins“ hier\nslug: \"er-sagte-hallo\"\n---\n\nIm Text „so“ hier.\n",
|
|
778
778
|
"note": "modes.md §3.7.4: two listed plain scalars in a locale whose quote glyphs differ from English, with an unlisted slug between the block and the body. Each listed scalar is its own span and the marks pair inside it, so the German pair appears three times and the slug keeps its straight quotes."
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
"id": "de-de-yaml-single-quoted-escape-growth-declined",
|
|
782
|
+
"rule": "dashes",
|
|
783
|
+
"mode": "yaml",
|
|
784
|
+
"keys": [
|
|
785
|
+
"description"
|
|
786
|
+
],
|
|
787
|
+
"in": "description: 'a''--''b'\n",
|
|
788
|
+
"out": "description: 'a''--''b'\n",
|
|
789
|
+
"note": "modes.md §3.8.6 and §3.4: de-DE is a `-spaced` locale, so the dash would grow against a unit and the edge-growth rule discards it, as in fr. A second locale so that an implementation that does not split cannot pass on one cell alone."
|
|
779
790
|
}
|
|
780
791
|
]
|
|
781
792
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"spec": "1.
|
|
2
|
+
"spec": "1.10.0",
|
|
3
3
|
"locale": "en-US",
|
|
4
4
|
"cases": [
|
|
5
5
|
{
|
|
@@ -1974,16 +1974,16 @@
|
|
|
1974
1974
|
"note": "modes.md §3.7.4 inherits every bail of §3.8.6: a double-quoted scalar whose content contains U+005C yields no spans, because the source spells the content with more characters than it has. The listed key is not enough on its own."
|
|
1975
1975
|
},
|
|
1976
1976
|
{
|
|
1977
|
-
"id": "en-us-markdown-commonmark-frontmatter-keys-single-quote-
|
|
1978
|
-
"rule": "
|
|
1977
|
+
"id": "en-us-markdown-commonmark-frontmatter-keys-single-quote-escape",
|
|
1978
|
+
"rule": "ellipsis",
|
|
1979
1979
|
"mode": "markdown",
|
|
1980
1980
|
"dialect": "commonmark",
|
|
1981
1981
|
"frontmatterKeys": [
|
|
1982
1982
|
"title"
|
|
1983
1983
|
],
|
|
1984
|
-
"in": "---\ntitle: 'It''s a test'\n---\n\nBody it's here.\n",
|
|
1985
|
-
"out": "---\ntitle: 'It''s a test'\n---\n\nBody it’s here.\n",
|
|
1986
|
-
"note": "modes.md §3.7.4
|
|
1984
|
+
"in": "---\ntitle: 'It''s a test...'\n---\n\nBody it's here.\n",
|
|
1985
|
+
"out": "---\ntitle: 'It''s a test…'\n---\n\nBody it’s here.\n",
|
|
1986
|
+
"note": "modes.md §3.7.4 inherits §3.8.6's split of `''` (spec 1.9.0): the value converts on both sides of the unit and the apostrophe it spells stays straight, while the body's own apostrophe converts. Until 1.9.0 the whole value yielded no spans."
|
|
1987
1987
|
},
|
|
1988
1988
|
{
|
|
1989
1989
|
"id": "en-us-markdown-commonmark-frontmatter-keys-separate-unit",
|
|
@@ -2545,15 +2545,70 @@
|
|
|
2545
2545
|
"note": "modes.md §3.8.6: a single-quoted scalar's content is one span; the quotes themselves are outside it."
|
|
2546
2546
|
},
|
|
2547
2547
|
{
|
|
2548
|
-
"id": "en-us-yaml-single-quoted-escape-
|
|
2548
|
+
"id": "en-us-yaml-single-quoted-escape-split",
|
|
2549
2549
|
"rule": "ellipsis",
|
|
2550
2550
|
"mode": "yaml",
|
|
2551
2551
|
"keys": [
|
|
2552
2552
|
"description"
|
|
2553
2553
|
],
|
|
2554
2554
|
"in": "description: 'it''s one...two'\n",
|
|
2555
|
-
"out": "description: 'it''s one
|
|
2556
|
-
"note": "modes.md §3.8.6: `''`
|
|
2555
|
+
"out": "description: 'it''s one…two'\n",
|
|
2556
|
+
"note": "modes.md §3.8.6 (spec 1.9.0): `''` is one apostrophe of content spelled with two code points, so it is an opaque skipped unit and the scalar is split around it. Everything on either side converts; the apostrophe it spells is outside every span and stays U+0027. Until 1.9.0 the whole scalar yielded no spans."
|
|
2557
|
+
},
|
|
2558
|
+
{
|
|
2559
|
+
"id": "en-us-yaml-single-quoted-escape-quotes",
|
|
2560
|
+
"rule": "quotes",
|
|
2561
|
+
"mode": "yaml",
|
|
2562
|
+
"keys": [
|
|
2563
|
+
"description"
|
|
2564
|
+
],
|
|
2565
|
+
"in": "description: 'It''s -- he said \"hi\" to me...'\n",
|
|
2566
|
+
"out": "description: 'It''s—he said “hi” to me…'\n",
|
|
2567
|
+
"note": "modes.md §3.8.6: the same scalar with a double-quoted run after the unit. The run pairs and converts, the dash and ellipsis convert, and the apostrophe stays straight."
|
|
2568
|
+
},
|
|
2569
|
+
{
|
|
2570
|
+
"id": "en-us-yaml-single-quoted-escape-at-ends",
|
|
2571
|
+
"rule": "ellipsis",
|
|
2572
|
+
"mode": "yaml",
|
|
2573
|
+
"keys": [
|
|
2574
|
+
"description"
|
|
2575
|
+
],
|
|
2576
|
+
"in": "description: '''one...two'''\n",
|
|
2577
|
+
"out": "description: '''one…two'''\n",
|
|
2578
|
+
"note": "modes.md §3.8.6: a unit directly after the opening quote and directly before the closing one. The scan claims each pair as it is met, so `'''` is the unit followed by content and `'''` at the end is the unit followed by the closing quote."
|
|
2579
|
+
},
|
|
2580
|
+
{
|
|
2581
|
+
"id": "en-us-yaml-single-quoted-escape-only",
|
|
2582
|
+
"rule": "ellipsis",
|
|
2583
|
+
"mode": "yaml",
|
|
2584
|
+
"keys": [
|
|
2585
|
+
"description"
|
|
2586
|
+
],
|
|
2587
|
+
"in": "description: ''''\n",
|
|
2588
|
+
"out": "description: ''''\n",
|
|
2589
|
+
"note": "modes.md §3.8.6: a scalar that is nothing but one unit yields no non-empty segment, so no span, and comes back byte for byte."
|
|
2590
|
+
},
|
|
2591
|
+
{
|
|
2592
|
+
"id": "en-us-yaml-single-quoted-escape-contraction-at-unit",
|
|
2593
|
+
"rule": "dashes",
|
|
2594
|
+
"mode": "yaml",
|
|
2595
|
+
"keys": [
|
|
2596
|
+
"description"
|
|
2597
|
+
],
|
|
2598
|
+
"in": "description: 'a''--''b'\n",
|
|
2599
|
+
"out": "description: 'a''—''b'\n",
|
|
2600
|
+
"note": "modes.md §3.8.6 and §3.4: the dash sits between two units, so it is a whole span. A contraction (2 → 1) is applied at a span extremity, exactly as it is against `:` and `#` in a plain scalar."
|
|
2601
|
+
},
|
|
2602
|
+
{
|
|
2603
|
+
"id": "en-us-yaml-single-quoted-escape-unterminated",
|
|
2604
|
+
"rule": "ellipsis",
|
|
2605
|
+
"mode": "yaml",
|
|
2606
|
+
"keys": [
|
|
2607
|
+
"description"
|
|
2608
|
+
],
|
|
2609
|
+
"in": "description: 'it''s one...two\n",
|
|
2610
|
+
"out": "description: 'it''s one...two\n",
|
|
2611
|
+
"note": "modes.md §3.8.6: after a unit the scan keeps looking for a closing quote on the same line, finds none, and the value yields no spans — a trailing unit is not mistaken for a close."
|
|
2557
2612
|
},
|
|
2558
2613
|
{
|
|
2559
2614
|
"id": "en-us-yaml-double-quoted-escape-untouched",
|
|
@@ -3090,6 +3145,136 @@
|
|
|
3090
3145
|
"in": "---\rtitle: a \"b\"\r---\r\rBody \"q\".\r",
|
|
3091
3146
|
"out": "---\rtitle: a \"b\"\r---\r\rBody “q”.\r",
|
|
3092
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."
|
|
3093
3278
|
}
|
|
3094
3279
|
]
|
|
3095
3280
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"spec": "1.
|
|
2
|
+
"spec": "1.10.0",
|
|
3
3
|
"locale": "fr",
|
|
4
4
|
"cases": [
|
|
5
5
|
{
|
|
@@ -961,7 +961,7 @@
|
|
|
961
961
|
"note": "modes.md §3.8.6 in a locale where the apostrophe is elision rather than possession: the same cell as en-us-yaml-plain-apostrophe, and §8 item 4's requirement that a construct be pinned in more than one locale rather than the same English output with the locale field changed."
|
|
962
962
|
},
|
|
963
963
|
{
|
|
964
|
-
"id": "fr-yaml-single-quoted-escape-
|
|
964
|
+
"id": "fr-yaml-single-quoted-escape-elision-stays",
|
|
965
965
|
"rule": "apostrophe",
|
|
966
966
|
"mode": "yaml",
|
|
967
967
|
"keys": [
|
|
@@ -969,7 +969,29 @@
|
|
|
969
969
|
],
|
|
970
970
|
"in": "description: 'l''idee et l''autre'\n",
|
|
971
971
|
"out": "description: 'l''idee et l''autre'\n",
|
|
972
|
-
"note": "modes.md §3.8.6:
|
|
972
|
+
"note": "modes.md §3.8.6: in a locale where the apostrophe is elision, the unit is the elision, and it is outside every span. The scalar has nothing else to convert and comes back unchanged — the cost §7.13 measures, pinned so that it is a documented property and not a surprise."
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
"id": "fr-yaml-single-quoted-escape-split",
|
|
976
|
+
"rule": "dashes",
|
|
977
|
+
"mode": "yaml",
|
|
978
|
+
"keys": [
|
|
979
|
+
"description"
|
|
980
|
+
],
|
|
981
|
+
"in": "description: 'l''idee -- et ''l''autre...'\n",
|
|
982
|
+
"out": "description: 'l''idee — et ''l''autre…'\n",
|
|
983
|
+
"note": "modes.md §3.8.6: a spaced dash and an ellipsis interior to a span convert, either side of units; the elisions stay straight."
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
"id": "fr-yaml-single-quoted-escape-growth-declined",
|
|
987
|
+
"rule": "dashes",
|
|
988
|
+
"mode": "yaml",
|
|
989
|
+
"keys": [
|
|
990
|
+
"description"
|
|
991
|
+
],
|
|
992
|
+
"in": "description: 'a''--''b'\n",
|
|
993
|
+
"out": "description: 'a''--''b'\n",
|
|
994
|
+
"note": "modes.md §3.8.6 and §3.4: `fr` is a `-spaced` locale, so the dash would grow from 2 to 3 code points against a unit. The edge-growth rule discards it, as it does against `:` and `#`. The en-US case with the same input is applied, which is what makes this one discriminate."
|
|
973
995
|
},
|
|
974
996
|
{
|
|
975
997
|
"id": "fr-yaml-single-quoted-no-escape",
|
|
@@ -1003,6 +1025,19 @@
|
|
|
1003
1025
|
"in": "---\ntitle: \"a\rb\"\nslug: c \"d\"\n---\n\nBody \"q\" here.\n",
|
|
1004
1026
|
"out": "---\ntitle: \"a\rb\"\nslug: c « d »\n---\n\nBody « q » here.\n",
|
|
1005
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."
|
|
1006
1041
|
}
|
|
1007
1042
|
]
|
|
1008
1043
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"spec": "1.
|
|
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,13 +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.
|
|
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
|
-
bullet, §3.8.6's accepted-cost paragraph, §5, §6 and §7, added in 1.7.0,
|
|
16
|
-
1.8.0
|
|
15
|
+
bullet, §3.8.6's accepted-cost paragraph, §5, §6 and §7, added in 1.7.0, §3.7.3a, added in
|
|
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, and §3.8.4
|
|
18
|
+
step 9 with §3.8.5's content-line condition, added in 1.10.0).
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
|
@@ -727,8 +729,11 @@ already recognises, and the option only changes what happens inside it:
|
|
|
727
729
|
because the window includes it. So `title` / `slug` / a content-final U+000D converts `slug` in
|
|
728
730
|
`yaml` mode and does not here. The wider decline is the direction this section takes everywhere
|
|
729
731
|
else — a miss is invisible, a U+000D inside a span is not — and the cost is conversions lost in
|
|
730
|
-
documents written with line endings from the last century.
|
|
731
|
-
|
|
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;
|
|
732
737
|
- **both delimiter lines stay outside every span**, as does every line terminator, so no edit
|
|
733
738
|
can reach `---` itself and §3.7.3's setext-underline hazard is unreachable;
|
|
734
739
|
- an **unterminated** block is not a block — §3.7.3 already yields no frontmatter construct
|
|
@@ -859,14 +864,13 @@ and it is the only way this corpus can show a conversion at all.
|
|
|
859
864
|
name `"HTPBE?"` becomes `"HTPBE ?"`. The other two are improvements a caller might well want
|
|
860
865
|
(`"Niamh O'Sullivan"` → `"Niamh O’Sullivan"`), which is the point: only the caller can tell
|
|
861
866
|
those apart, and that is the same argument §3.8.2 makes.
|
|
862
|
-
- **The inherited escape bail, measured.** Re-emitting each of the 247
|
|
863
|
-
one quoting style and running the scan over it
|
|
864
|
-
double-quoted, none
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
where a reader will look for it.
|
|
867
|
+
- **The inherited escape bail, measured — and removed in 1.9.0.** Re-emitting each of the 247
|
|
868
|
+
convertible values in one quoting style and running the scan over it gave **single-quoted, 130 of
|
|
869
|
+
247 yield no spans**; double-quoted, none. The reason was §3.8.6's bail on `''`: an apostrophe
|
|
870
|
+
inside a single-quoted scalar is written that way. Every listed scalar in this corpus as written
|
|
871
|
+
is double-quoted, so the bail never fired on it; a caller whose YAML style is single quotes got
|
|
872
|
+
nothing on half of their prose, silently. 1.9.0 replaced the bail with a split at each `''`
|
|
873
|
+
(§3.8.6), and §7.13 records what that did and did not recover.
|
|
870
874
|
|
|
871
875
|
### 3.8 Skip list — `yaml`
|
|
872
876
|
|
|
@@ -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.
|
|
992
|
-
|
|
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.
|
|
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
|
|
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`.
|
|
@@ -1091,16 +1111,43 @@ that line the value yields no spans, and its continuation lines are consumed by
|
|
|
1091
1111
|
rather than rescanned. After the closing quote only U+0020 and an optional `#` comment may
|
|
1092
1112
|
follow. The span is the content between the quotes, exclusive of both.
|
|
1093
1113
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1114
|
+
The offset model of §3.1 requires that source characters and content characters be the same thing,
|
|
1115
|
+
and two constructs break that:
|
|
1096
1116
|
|
|
1097
1117
|
- a double-quoted scalar whose content contains U+005C yields no spans — `\n`, `\"` and `é`
|
|
1098
1118
|
are content the source spells with more characters than it has;
|
|
1099
|
-
- a single-quoted scalar
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1119
|
+
- in a single-quoted scalar, **two consecutive U+0027 are one apostrophe of content, spelled with
|
|
1120
|
+
two code points.** Since 1.9.0 that pair is an **opaque skipped unit**, treated as §3.8.6 treats
|
|
1121
|
+
`:` and `#` in a plain scalar: the scalar's range is split around it, and the unit lies outside
|
|
1122
|
+
every span. Until 1.9.0 the whole scalar yielded no spans instead.
|
|
1123
|
+
|
|
1124
|
+
Neither is a mapping from source to content. A backslash escape has dozens of forms and each would
|
|
1125
|
+
need its own decoding table in five runtimes; `''` is one fixed pair, and a skipped unit needs no
|
|
1126
|
+
table at all. That is the treatment `html` gives a character reference (§3.6), reached from the
|
|
1127
|
+
same constraint rather than by analogy.
|
|
1128
|
+
|
|
1129
|
+
> **The single-quoted scan, normative.** Starting after the opening quote, advance `j` one code
|
|
1130
|
+
> point at a time. A U+0027 at `j` followed by another U+0027 is a unit: end the current segment
|
|
1131
|
+
> before `j`, begin the next at `j + 2`, and continue at `j + 2`. A U+0027 at `j` not followed by
|
|
1132
|
+
> another U+0027 is the closing quote. If the line ends first, the value yields no spans, as for
|
|
1133
|
+
> any unterminated quoted scalar. The segments, each non-empty one a span, are what remains once
|
|
1134
|
+
> the units are removed. The scan is left to right and each pair is claimed as it is met, so
|
|
1135
|
+
> `'''a'` is the unit followed by `a`, and `''''` is one unit and the closing quote.
|
|
1136
|
+
|
|
1137
|
+
**A span may begin or end at a unit, and §3.4 decides what that allows — identically to `:` and
|
|
1138
|
+
`#`.** Nothing about `''` needs a rule of its own. A replacement that is longer than what it
|
|
1139
|
+
replaced is discarded at an extremity, so `fr`'s spaced dash does not grow against a unit
|
|
1140
|
+
(`'a''--''b'` is left alone in `fr` and `de-DE`), while a contraction is applied (`'a''--''b'` →
|
|
1141
|
+
`'a''—''b'` in `en-US`). The unit is a boundary marker (§3.2) between the two spans it separates:
|
|
1142
|
+
no rule edits a code point that lies outside every span, so the apostrophe it spells **stays
|
|
1143
|
+
U+0027**. That is the accepted cost of the split, and it is not small: in `'l''idee'` everything
|
|
1144
|
+
but the elision converts, and a value whose only convertible character was that apostrophe still
|
|
1145
|
+
comes back unchanged. §7.13 measures it.
|
|
1146
|
+
|
|
1147
|
+
**Why not convert the apostrophe too.** Replacing `''` with U+2019 outright would convert it and
|
|
1148
|
+
the result still parses, since U+2019 is ordinary content in a single-quoted scalar. It is
|
|
1149
|
+
rejected for the reason §3.8.1 gives: that is decode-then-remap, the offset mapping is no longer
|
|
1150
|
+
the identity, and five runtimes would each invent it. **No colon test applies to a quoted scalar** — quoting neutralises the
|
|
1104
1151
|
colon, and applying the plain-scalar test here is what made the first draft decline
|
|
1105
1152
|
`title: "Chapter 1: the beginning"`.
|
|
1106
1153
|
|
|
@@ -1177,6 +1224,36 @@ clause reverted, the same run reports **480 structural changes** — `k: a:---a`
|
|
|
1177
1224
|
measurement that passes both with and without the thing it is supposed to protect proves nothing,
|
|
1178
1225
|
and two earlier versions of this sweep were in exactly that position.
|
|
1179
1226
|
|
|
1227
|
+
**The same sweep, extended for `''` (1.9.0).** The alphabet gained `''` as one token beside the
|
|
1228
|
+
lone U+0027, and this run used four single-quoted templates alone, because the apostrophe check below cannot hold on a plain scalar, where `apostrophe` legitimately converts `a'a`. They are the payload alone, followed by a
|
|
1229
|
+
comment, between fixed `x''` and `''y`, and as a value in a sequence of mappings. Payloads of
|
|
1230
|
+
length 1–4, 19 locales, the sources that are not valid YAML discarded before the run,
|
|
1231
|
+
**872 328 cases over those four templates, 65 033 of them changed by the transform: no parse failure, no structural change, no idempotency failure, and the number of U+0027 in the output
|
|
1232
|
+
equal to the number in the source in every case** — the last check is what a structure-and-type
|
|
1233
|
+
comparison cannot see, since a `''` turned into `’’` leaves the document's shape intact. It
|
|
1234
|
+
discriminates, but narrowly, and that should be said: with the unit's width off by one (the
|
|
1235
|
+
second U+0027 left inside the span) the same run reports failures — `k: '''a'` becoming
|
|
1236
|
+
`k: ''’a'`, in `cs` and others — whereas an implementation that does not split at all, and hands
|
|
1237
|
+
the pair to the rules as two ordinary characters, **also passes**, because `quotes`' same-kind
|
|
1238
|
+
adjacency veto leaves an adjacent pair alone. The split is therefore specified for the reason
|
|
1239
|
+
§3.8.1 gives, that it needs no mapping and puts the pair out of every rule's reach by construction,
|
|
1240
|
+
and not because the sweep shows the unsplit alternative to be unsafe at payload length four.
|
|
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
|
+
|
|
1180
1257
|
**Three things it could not find**, each worth naming because each cost something.
|
|
1181
1258
|
|
|
1182
1259
|
- It did not find the workflow damage, because a shell script inside a block scalar is a string
|
|
@@ -1372,7 +1449,13 @@ tests none of this document. In `yaml` the sweep alphabet must include `:`, `#`,
|
|
|
1372
1449
|
as one token**, since those are the characters whose adjacency the argument above turns on and a
|
|
1373
1450
|
three-dash run is not reachable from single dashes at a bounded payload length; and it must
|
|
1374
1451
|
include U+0022 and U+005C, without which the sweep cannot reach a quoted scalar's delimiters at
|
|
1375
|
-
all. Since 1.
|
|
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
|
|
1456
|
+
single-quoted templates must carry the pair at the start, in the middle and at the end of the
|
|
1457
|
+
payload, because a unit's behaviour at a span extremity is a different path from its behaviour
|
|
1458
|
+
between two letters. Since 1.7.0 the `markdown` run must also carry a template with a **frontmatter block and
|
|
1376
1459
|
`frontmatterKeys` naming a key in it** — a document with two text units (§3.1) tests a composition
|
|
1377
1460
|
the single-unit templates cannot reach. §3.8.7 records what each run did and did not establish — including that a sweep comparing
|
|
1378
1461
|
structure and types is blind to a string whose content is damaged, and that an alphabet without
|
|
@@ -1589,7 +1672,11 @@ rule-local.
|
|
|
1589
1672
|
- **a flow collection** — `tags: [one, two]` and `{a: b}` — even on one line;
|
|
1590
1673
|
- **a quoted scalar containing an escape** — `"say \"hi\""` — where the source spells the
|
|
1591
1674
|
content with more characters than it has;
|
|
1592
|
-
- **a block scalar whose indentation is ambiguous**, per §3.8.5's
|
|
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.
|
|
1593
1680
|
|
|
1594
1681
|
12. **`keys` matches a bare name at any depth, and nothing narrower.** `description` is
|
|
1595
1682
|
processable wherever it occurs, which is what makes the option portable — no paths, no
|
|
@@ -1627,20 +1714,32 @@ rule-local.
|
|
|
1627
1714
|
line, so that a stray one inside a single quoted value costs that value and not the block.
|
|
1628
1715
|
The block is still skipped, so nothing machine-read is typeset either way. A lone-U+000D
|
|
1629
1716
|
document is one line to §3.8.4 and therefore loses the whole block, which is the price of
|
|
1630
|
-
not widening that section here.
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
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.
|
|
1724
|
+
- **§3.8.6's treatment of `''` in a single-quoted scalar: the cost, before and after 1.9.0.**
|
|
1725
|
+
Until 1.9.0 a single-quoted scalar containing `''` yielded no spans. An apostrophe inside
|
|
1726
|
+
such a scalar is spelled `''`, and an apostrophe is exactly what `apostrophe` and `quotes`
|
|
1727
|
+
convert, so the bail fell hardest on the values the option exists for. On the author's own
|
|
1728
|
+
207-file corpus, de-typeset and re-emitted as single-quoted, in eight locales: **1104 of
|
|
1729
|
+
1913 values a locale would convert came back unchanged**. Since 1.9.0 the scalar is split at
|
|
1730
|
+
each `''` (§3.8.6) and **672 of 1913** do — 35.1% against 57.7%. The remainder is not one
|
|
1731
|
+
cost but two. **602 of the 672 have nothing else to convert**: the apostrophe is the only
|
|
1732
|
+
thing a locale would have changed, it lies outside every span by construction, and it stays
|
|
1733
|
+
straight. **The other 70 also change when their `'` are already curly**, so something besides
|
|
1734
|
+
an apostrophe is lost — in the samples read, a phrase written `'like this'`, which a locale
|
|
1735
|
+
would pair as a quotation, and whose two marks are units here. Not all 70 were read.
|
|
1736
|
+
Double-quoted and plain scalars are unaffected, and the corpus as authored is entirely
|
|
1737
|
+
double-quoted, so its own author never met either figure.
|
|
1738
|
+
The "before" figure is what `scripts/miss-census.mjs` prints with a 1.8.x engine
|
|
1739
|
+
installed; the "after" figure is the same run with the 1.9.0 engine, and the 602/70 split
|
|
1740
|
+
is a separate run of that comparison on values with every `'` already curly, which the
|
|
1741
|
+
script does not perform. The corpus has grown since 1.7.0 measured it (187 files, 1040 of
|
|
1742
|
+
1858), which is why the denominator differs from that figure.
|
|
1644
1743
|
|
|
1645
1744
|
## 8. Fixture coverage strategy (non-normative)
|
|
1646
1745
|
|
|
@@ -1677,7 +1776,7 @@ changes.
|
|
|
1677
1776
|
blocks, and the JSX-vs-skipped-element case distinction (`tests/modes/markdown.test.ts`).
|
|
1678
1777
|
3a. **YAML span selection** — the three scalar forms, the `keys` option (a listed key, an
|
|
1679
1778
|
unlisted one, a quoted one, the same name at two depths), block-scalar indentation and
|
|
1680
|
-
chomping, the `:`/`#` split of §3.8.6, 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
|
|
1681
1780
|
lines, and every bail of §3.8.4. `yaml` carries a heavier fixture burden than the other two
|
|
1682
1781
|
for a reason §8's opening argument does not cover: its span selection is **specified rather
|
|
1683
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.
|
|
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
|
{
|
data/lib/polytypo/modes/yaml.rb
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
228
|
-
# block as written, or a later line dedented
|
|
229
|
-
# guessable. All
|
|
230
|
-
|
|
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?
|
|
@@ -246,21 +257,30 @@ module Polytypo
|
|
|
246
257
|
end
|
|
247
258
|
private_class_method :block_scalar
|
|
248
259
|
|
|
249
|
-
# 3.8.6. The span is the content between the quotes.
|
|
250
|
-
# characters and content characters are the same thing, which the
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
#
|
|
260
|
+
# 3.8.6. The span is the content between the quotes. A backslash in a double-quoted scalar
|
|
261
|
+
# bails, so that source characters and content characters are the same thing, which the
|
|
262
|
+
# offset model of 3.1 requires. In a single-quoted scalar each doubled apostrophe is an
|
|
263
|
+
# opaque two-code-point unit that lies outside every span, the same treatment an HTML
|
|
264
|
+
# character reference gets in 3.6; the scalar's range is split around it. No colon test
|
|
265
|
+
# applies here: quoting neutralises the colon, and applying the plain-scalar test would
|
|
266
|
+
# decline `title: "Chapter 1: the beginning"`.
|
|
254
267
|
def self.quoted_scalar(chars, line, v, spans)
|
|
255
268
|
quote = chars[v]
|
|
269
|
+
starts = []
|
|
256
270
|
close = -1
|
|
271
|
+
segment = v + 1
|
|
257
272
|
j = v + 1
|
|
258
273
|
while j < line.end
|
|
259
274
|
c = chars[j]
|
|
260
275
|
return if quote == '"' && c == "\\"
|
|
261
|
-
return if quote == "'" && c == "'" && j + 1 < line.end && chars[j + 1] == "'"
|
|
262
276
|
|
|
263
277
|
if c == quote
|
|
278
|
+
if quote == "'" && j + 1 < line.end && chars[j + 1] == "'"
|
|
279
|
+
starts << [segment, j] if j > segment
|
|
280
|
+
segment = j + 2
|
|
281
|
+
j += 2
|
|
282
|
+
next
|
|
283
|
+
end
|
|
264
284
|
close = j
|
|
265
285
|
break
|
|
266
286
|
end
|
|
@@ -272,7 +292,8 @@ module Polytypo
|
|
|
272
292
|
after += 1 while after < line.end && chars[after] == SPACE
|
|
273
293
|
return if after < line.end && chars[after] != "#"
|
|
274
294
|
|
|
275
|
-
|
|
295
|
+
starts << [segment, close] if close > segment
|
|
296
|
+
starts.each { |from, to| spans << Spans::Span.new(from, to) }
|
|
276
297
|
end
|
|
277
298
|
private_class_method :quoted_scalar
|
|
278
299
|
|
data/lib/polytypo/version.rb
CHANGED