polytypo 1.12.0 → 1.14.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 +21 -1
- data/lib/polytypo/data/VERSION +1 -1
- data/lib/polytypo/data/fixtures/bg.json +1 -1
- data/lib/polytypo/data/fixtures/cs.json +1 -1
- data/lib/polytypo/data/fixtures/de-CH.json +1 -1
- data/lib/polytypo/data/fixtures/de-DE.json +77 -1
- data/lib/polytypo/data/fixtures/el.json +1 -1
- data/lib/polytypo/data/fixtures/en-GB.json +89 -1
- data/lib/polytypo/data/fixtures/en-US.json +450 -1
- data/lib/polytypo/data/fixtures/es.json +1 -1
- data/lib/polytypo/data/fixtures/fi.json +1 -1
- data/lib/polytypo/data/fixtures/fr-CA.json +1 -1
- data/lib/polytypo/data/fixtures/fr.json +41 -1
- data/lib/polytypo/data/fixtures/hr.json +1 -1
- data/lib/polytypo/data/fixtures/hu.json +1 -1
- data/lib/polytypo/data/fixtures/it.json +1 -1
- data/lib/polytypo/data/fixtures/locale-resolution.json +1 -1
- data/lib/polytypo/data/fixtures/nl.json +1 -1
- data/lib/polytypo/data/fixtures/pl.json +1 -1
- data/lib/polytypo/data/fixtures/pt-BR.json +1 -1
- data/lib/polytypo/data/fixtures/pt-PT.json +1 -1
- data/lib/polytypo/data/fixtures/ro.json +1 -1
- data/lib/polytypo/data/fixtures/ru.json +1 -1
- data/lib/polytypo/data/fixtures/sk.json +1 -1
- data/lib/polytypo/data/fixtures/sr.json +183 -0
- 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 +3 -2
- data/lib/polytypo/data/locales/sr.json +82 -0
- data/lib/polytypo/data/rules/analyze.md +16 -4
- data/lib/polytypo/data/rules/modes.md +262 -9
- data/lib/polytypo/data/rules/order.json +1 -1
- data/lib/polytypo/data/schema/fixtures.schema.json +160 -18
- data/lib/polytypo/engine/origin.rb +13 -1
- data/lib/polytypo/engine/protect.rb +122 -0
- data/lib/polytypo/errors.rb +1 -1
- data/lib/polytypo/modes/runner.rb +0 -1
- data/lib/polytypo/version.rb +1 -1
- data/lib/polytypo.rb +34 -19
- metadata +4 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "1.14.0",
|
|
3
|
+
"locale": "sr",
|
|
4
|
+
"cases": [
|
|
5
|
+
{
|
|
6
|
+
"id": "sr-quotes-primary",
|
|
7
|
+
"rule": "quotes",
|
|
8
|
+
"mode": "text",
|
|
9
|
+
"in": "Рекао је: \"да\" и отишао.",
|
|
10
|
+
"out": "Рекао је: „да” и отишао.",
|
|
11
|
+
"note": "Klikovac 2024 on Pravopis 2010, pp. 343–344: the base form is „…” (U+201E, U+201D); the close is U+201D, not U+201C."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "sr-quotes-primary-latin",
|
|
15
|
+
"rule": "quotes",
|
|
16
|
+
"mode": "text",
|
|
17
|
+
"in": "Rekao je: \"da\" i otišao.",
|
|
18
|
+
"out": "Rekao je: „da” i otišao.",
|
|
19
|
+
"note": "No rule depends on the script: the Latin spelling gets the same pair."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "sr-quotes-nested-secondary",
|
|
23
|
+
"rule": "quotes",
|
|
24
|
+
"mode": "text",
|
|
25
|
+
"in": "Рекао је: \"песма 'Лепа' је\" и отишао.",
|
|
26
|
+
"out": "Рекао је: „песма ’Лепа’ је” и отишао.",
|
|
27
|
+
"note": "Klikovac 2024 on Pravopis 2010, p. 361: a quotation inside a quotation takes the single pair, written with U+2019 on both sides."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "sr-quotes-nested-roundtrip",
|
|
31
|
+
"rule": "quotes",
|
|
32
|
+
"mode": "text",
|
|
33
|
+
"in": "Рекао је: „песма ’Лепа’ је” и отишао.",
|
|
34
|
+
"out": "Рекао је: „песма ’Лепа’ је” и отишао.",
|
|
35
|
+
"note": "The same-glyph secondary pair is a fixed point."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "sr-quotes-apostrophe-inside-secondary",
|
|
39
|
+
"rule": "quotes",
|
|
40
|
+
"mode": "text",
|
|
41
|
+
"in": "Рекао је: \"песма 'McDonald's' је\" и отишао.",
|
|
42
|
+
"out": "Рекао је: „песма ’McDonald’s’ је” и отишао.",
|
|
43
|
+
"note": "An apostrophe inside the same-glyph secondary pair is kept and the pair still closes; the result is a fixed point."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "sr-quotes-already-typeset-kept",
|
|
47
|
+
"rule": "quotes",
|
|
48
|
+
"mode": "text",
|
|
49
|
+
"in": "„да” и „не”",
|
|
50
|
+
"out": "„да” и „не”",
|
|
51
|
+
"note": "Already typeset: left as it is."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "sr-quotes-no-inner-space",
|
|
55
|
+
"rule": "quotes",
|
|
56
|
+
"mode": "text",
|
|
57
|
+
"in": "„ да ”",
|
|
58
|
+
"out": "„да”",
|
|
59
|
+
"note": "No space after the opening or before the closing mark."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "sr-ellipsis-three-dots",
|
|
63
|
+
"rule": "ellipsis",
|
|
64
|
+
"mode": "text",
|
|
65
|
+
"in": "Чекај... шта?",
|
|
66
|
+
"out": "Чекај… шта?",
|
|
67
|
+
"note": "Pravopis 2010 (via Klikovac, p. 136): three dots, here the single ellipsis character."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "sr-ellipsis-not-abbreviated",
|
|
71
|
+
"rule": "ellipsis",
|
|
72
|
+
"mode": "text",
|
|
73
|
+
"in": "Шта?.. да",
|
|
74
|
+
"out": "Шта?… да",
|
|
75
|
+
"note": "abbreviatedAfterTerminal is false (decision): `?..` is an ordinary ellipsis."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "sr-dashes-parenthetical",
|
|
79
|
+
"rule": "dashes",
|
|
80
|
+
"mode": "text",
|
|
81
|
+
"in": "Идемо - ако пада - касније.",
|
|
82
|
+
"out": "Идемо – ако пада – касније.",
|
|
83
|
+
"note": "Klikovac p. 328: the separating dash is spaced; U+2013 is a decision."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "sr-dashes-existing-em-dash",
|
|
87
|
+
"rule": "dashes",
|
|
88
|
+
"mode": "text",
|
|
89
|
+
"in": "Идемо — ако пада — касније.",
|
|
90
|
+
"out": "Идемо – ако пада – касније.",
|
|
91
|
+
"note": "An authored em dash becomes the locale's en dash."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "sr-ranges-digits-tight",
|
|
95
|
+
"rule": "ranges",
|
|
96
|
+
"mode": "text",
|
|
97
|
+
"in": "рат 1941-1945 и стр. 145-162",
|
|
98
|
+
"out": "рат 1941–1945 и стр. 145–162",
|
|
99
|
+
"note": "ranges.md §3, Klikovac pp. 330–331: a dash between numbers is closed up («рат 1941–1945»). en-tight inserts U+2060 on both sides of the dash so a line cannot break there (ranges.md).",
|
|
100
|
+
"rules": {
|
|
101
|
+
"ranges": true
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "sr-ranges-already-tight",
|
|
106
|
+
"rule": "ranges",
|
|
107
|
+
"mode": "text",
|
|
108
|
+
"in": "рат 1941–1945",
|
|
109
|
+
"out": "рат 1941–1945",
|
|
110
|
+
"note": "ranges.md §3: already an en dash between numbers: left as it is.",
|
|
111
|
+
"rules": {
|
|
112
|
+
"ranges": true
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"id": "sr-ranges-word-hyphen-untouched",
|
|
117
|
+
"rule": "ranges",
|
|
118
|
+
"mode": "text",
|
|
119
|
+
"in": "Нови Сад-Београд и српско-хрватски",
|
|
120
|
+
"out": "Нови Сад-Београд и српско-хрватски",
|
|
121
|
+
"note": "ranges.md §3: a hyphenated word is not a digit range.",
|
|
122
|
+
"rules": {
|
|
123
|
+
"ranges": true
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "sr-hyphen-noop",
|
|
128
|
+
"rule": "hyphen",
|
|
129
|
+
"mode": "text",
|
|
130
|
+
"in": "српско-хрватски речник и без-",
|
|
131
|
+
"out": "српско-хрватски речник и без-",
|
|
132
|
+
"note": "No closed list ships, so nothing changes."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "sr-nbsp-units-inert",
|
|
136
|
+
"rule": "nbsp",
|
|
137
|
+
"mode": "text",
|
|
138
|
+
"in": "3 kg, 50 % и 99,90 €",
|
|
139
|
+
"out": "3 kg, 50 % и 99,90 €",
|
|
140
|
+
"note": "No verified rule: every nbsp list ships empty, so the plain spaces stay."
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": "sr-nbsp-short-words-inert",
|
|
144
|
+
"rule": "nbsp",
|
|
145
|
+
"mode": "text",
|
|
146
|
+
"in": "у кући и с пријатељем",
|
|
147
|
+
"out": "у кући и с пријатељем",
|
|
148
|
+
"note": "afterShortWords ships empty."
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "sr-nbsp-initials-unbound",
|
|
152
|
+
"rule": "nbsp",
|
|
153
|
+
"mode": "text",
|
|
154
|
+
"in": "Б. Ћопић",
|
|
155
|
+
"out": "Б. Ћопић",
|
|
156
|
+
"note": "initialBinding is none."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "sr-spaces-brackets",
|
|
160
|
+
"rule": "spaces",
|
|
161
|
+
"mode": "text",
|
|
162
|
+
"in": "Текст ( нормалан ) остаје .",
|
|
163
|
+
"out": "Текст (нормалан) остаје.",
|
|
164
|
+
"note": "spaces.md: brackets tighten, no space before the final stop."
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "sr-symbols-copyright",
|
|
168
|
+
"rule": "symbols",
|
|
169
|
+
"mode": "text",
|
|
170
|
+
"in": "Copyright (c) 2026",
|
|
171
|
+
"out": "Copyright © 2026",
|
|
172
|
+
"note": "symbols rule, locale-neutral."
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "sr-apostrophe-foreign-name",
|
|
176
|
+
"rule": "apostrophe",
|
|
177
|
+
"mode": "text",
|
|
178
|
+
"in": "То је McDonald's",
|
|
179
|
+
"out": "То је McDonald’s",
|
|
180
|
+
"note": "apostrophe rule, locale-neutral."
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"spec": "1.
|
|
2
|
+
"spec": "1.14.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",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"ro",
|
|
26
26
|
"hu",
|
|
27
27
|
"bg",
|
|
28
|
-
"hr"
|
|
28
|
+
"hr",
|
|
29
|
+
"sr"
|
|
29
30
|
],
|
|
30
31
|
"aliases": {
|
|
31
32
|
"en": "en-US",
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"locale": "sr",
|
|
3
|
+
"name": "Serbian",
|
|
4
|
+
"quotes": {
|
|
5
|
+
"primary": {
|
|
6
|
+
"open": "„",
|
|
7
|
+
"close": "”",
|
|
8
|
+
"innerSpace": "none"
|
|
9
|
+
},
|
|
10
|
+
"secondary": {
|
|
11
|
+
"open": "’",
|
|
12
|
+
"close": "’",
|
|
13
|
+
"innerSpace": "none"
|
|
14
|
+
},
|
|
15
|
+
"elisionIdioms": [],
|
|
16
|
+
"elisionClitics": {
|
|
17
|
+
"before": [],
|
|
18
|
+
"after": []
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"dash": {
|
|
22
|
+
"parenthetical": "en-spaced",
|
|
23
|
+
"range": "en-tight"
|
|
24
|
+
},
|
|
25
|
+
"ellipsis": {
|
|
26
|
+
"abbreviatedAfterTerminal": false
|
|
27
|
+
},
|
|
28
|
+
"hyphen": {
|
|
29
|
+
"prefixes": [],
|
|
30
|
+
"suffixes": [],
|
|
31
|
+
"compounds": []
|
|
32
|
+
},
|
|
33
|
+
"nbsp": {
|
|
34
|
+
"beforePunctuation": [],
|
|
35
|
+
"narrowBeforePunctuation": [],
|
|
36
|
+
"afterShortWords": [],
|
|
37
|
+
"abbreviations": [],
|
|
38
|
+
"beforeUnits": [],
|
|
39
|
+
"beforeNumber": [],
|
|
40
|
+
"beforeWord": [],
|
|
41
|
+
"afterSymbols": [],
|
|
42
|
+
"initialBinding": "none"
|
|
43
|
+
},
|
|
44
|
+
"sources": [
|
|
45
|
+
{
|
|
46
|
+
"rule": "quotes",
|
|
47
|
+
"cite": "Д. Кликовац, Српска интерпункција, Филолошки факултет Универзитета у Београду, 2024 (серија «Монографије – језик»), ISBN 978-86-6153-752-3, analysing Пешикан/Јерковић/Пижурица, Правопис српскога језика, Матица српска, Нови Сад, 2010, pp. 343–345 (table 5) and 360–363 (table 11)",
|
|
48
|
+
"url": "https://www.fil.bg.ac.rs/uploads/files/Strane/Biblioteke/Izdavacka%20delatnost/Monografije%20-%20Jezik/SRSKA%20INTERPUNKCIJA%20elek%20izd.pdf",
|
|
49
|
+
"note": "Primary: U+201E open, U+201D close. p. 343, table 5: «Пижурица и др. 2010: „ ” (То је једини облик који је наведен у наслову.)»; p. 344: the 2010 Pravopis prescribes and uses only this form. Secondary: U+2019 on both sides (the same-glyph pair of fi and sv), p. 361, table 11: «Полунаводницима се издваја ужи навод у оквиру ширег навода» (2010:135); p. 363 fn. 30 notes the form ‘ ’ at 2010:135 as «вероватно грешком». innerSpace none for both. CONFLICT, recorded not resolved: Пешикан и др. 1993:286 also allowed „ “ and » «; the dictionaries РМС, РСАНУ and РСЈ use „ “ (p. 345); Klikovac and Milanović 2019 argue for „ “ or free choice. U+201D follows the current Pravopis, as hr does. Q-W: primary WIDE, secondary NARROW. The secondary U+2019/U+2019 is a DECISION (MEDIUM): table 11 attributes ’ ’ to 2010, but fn. 30 reports ‘ ’ (U+2018/U+2019) printed at 2010:135 and calls it probably a mistake without the note stating which form Klikovac holds correct; pinned as U+2019 both sides, the form table 11 gives. The issue text «„…“ / „…”» was an observation, not a norm. elisionIdioms and elisionClitics are empty: no rule found. Confidence MEDIUM-HIGH: the glyph form is named in words by Klikovac, not read from a text layer. The Pravopis 2010 itself was not read (paid); every statement is the edition of 2010 as Klikovac reports it. The PDF has no text layer and was read as page images. Retrieved 2026-10-24."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"rule": "dashes",
|
|
53
|
+
"cite": "Д. Кликовац, Српска интерпункција, Филолошки факултет Универзитета у Београду, 2024 (серија «Монографије – језик»), ISBN 978-86-6153-752-3, analysing Пешикан/Јерковић/Пижурица, Правопис српскога језика, Матица српска, Нови Сад, 2010, pp. 302, 320 (table 8), 328",
|
|
54
|
+
"url": "https://www.fil.bg.ac.rs/uploads/files/Strane/Biblioteke/Izdavacka%20delatnost/Monografije%20-%20Jezik/SRSKA%20INTERPUNKCIJA%20elek%20izd.pdf",
|
|
55
|
+
"note": "dash.parenthetical = en-spaced. The spaces are normative: the separating dash is set as a spaced mark (Пешикан и др. 1993, p. 328; 2010:122 confirms the function and every example is set spaced). The WIDTH is a DECISION: p. 302 says the dash should be about twice as long as the hyphen or more, which does not choose between U+2013 and U+2014; the monograph sets U+2013. MEDIUM, as hr and bg. Klikovac's p. 328 cites «Пижурица и др. (1993: 126)», probably a misprint for 2010; noted, not corrected. The Pravopis 2010 itself was not read (paid); every statement is the edition of 2010 as Klikovac reports it. The PDF has no text layer and was read as page images. Retrieved 2026-10-24."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"rule": "ranges",
|
|
59
|
+
"cite": "Д. Кликовац, Српска интерпункција, Филолошки факултет Универзитета у Београду, 2024 (серија «Монографије – језик»), ISBN 978-86-6153-752-3, analysing Пешикан/Јерковић/Пижурица, Правопис српскога језика, Матица српска, Нови Сад, 2010, pp. 330–331",
|
|
60
|
+
"url": "https://www.fil.bg.ac.rs/uploads/files/Strane/Biblioteke/Izdavacka%20delatnost/Monografije%20-%20Jezik/SRSKA%20INTERPUNKCIJA%20elek%20izd.pdf",
|
|
61
|
+
"note": "dash.range = en-tight. A dash between numbers is written closed up (Пешикан 1993:278, Пижурица 2010:126, Дешић 2015): «рат 1941–1945», «у XV–XVIII веку». ACCEPTED COSTS: a multi-word range member takes spaces in 2010:126–127 («рат 6. IV 1941 – 9. V 1945»), and the rule «од … до» (no dash after «од») is an algorithm, not data; locale data can express neither. The ranges rule is off by default. The Pravopis 2010 itself was not read (paid); every statement is the edition of 2010 as Klikovac reports it. The PDF has no text layer and was read as page images. Retrieved 2026-10-24."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"rule": "ellipsis",
|
|
65
|
+
"cite": "Д. Кликовац, Српска интерпункција, Филолошки факултет Универзитета у Београду, 2024 (серија «Монографије – језик»), ISBN 978-86-6153-752-3, analysing Пешикан/Јерковић/Пижурица, Правопис српскога језика, Матица српска, Нови Сад, 2010, p. 136 (table 1, fn. 5) and p. 164 (§12.1)",
|
|
66
|
+
"url": "https://www.fil.bg.ac.rs/uploads/files/Strane/Biblioteke/Izdavacka%20delatnost/Monografije%20-%20Jezik/SRSKA%20INTERPUNKCIJA%20elek%20izd.pdf",
|
|
67
|
+
"note": "abbreviatedAfterTerminal = false is a DECISION, not a citation. 2010 gives three dots closed up (p. 136) and allows them after ? and ! (p. 164); the two-dot form after a terminal appears only as older practice (Мирко Николић 1973, p. 136 fn. 5) and is never prescribed. MEDIUM. The Pravopis 2010 itself was not read (paid); every statement is the edition of 2010 as Klikovac reports it. The PDF has no text layer and was read as page images. Retrieved 2026-10-24."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"rule": "hyphen",
|
|
71
|
+
"cite": "Д. Кликовац, Српска интерпункција, Филолошки факултет Универзитета у Београду, 2024 (серија «Монографије – језик»), ISBN 978-86-6153-752-3, analysing Пешикан/Јерковић/Пижурица, Правопис српскога језика, Матица српска, Нови Сад, 2010, (not consulted)",
|
|
72
|
+
"url": "https://www.fil.bg.ac.rs/uploads/files/Strane/Biblioteke/Izdavacka%20delatnost/Monografije%20-%20Jezik/SRSKA%20INTERPUNKCIJA%20elek%20izd.pdf",
|
|
73
|
+
"note": "All three lists are empty: no source consulted gives a closed list of unbreakable hyphenated forms. The 2010 Pravopis chapters on word division were not consulted. Operator decision (no paid sources, pick and pin): ship empty. The Pravopis 2010 itself was not read (paid); every statement is the edition of 2010 as Klikovac reports it. The PDF has no text layer and was read as page images. Retrieved 2026-10-24."
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"rule": "nbsp",
|
|
77
|
+
"cite": "Д. Кликовац, Српска интерпункција, Филолошки факултет Универзитета у Београду, 2024 (серија «Монографије – језик»), ISBN 978-86-6153-752-3, analysing Пешикан/Јерковић/Пижурица, Правопис српскога језика, Матица српска, Нови Сад, 2010, (not consulted)",
|
|
78
|
+
"url": "https://www.fil.bg.ac.rs/uploads/files/Strane/Biblioteke/Izdavacka%20delatnost/Monografije%20-%20Jezik/SRSKA%20INTERPUNKCIJA%20elek%20izd.pdf",
|
|
79
|
+
"note": "Every list is empty and initialBinding is none: no source consulted gives a rule for a space between a number and a unit or percent sign, for one-letter words or initials at a line end. Nothing is derived from another language. The 2010 Pravopis chapters on numbers and word division were not consulted. Operator decision (no paid sources, pick and pin): ship empty. The Pravopis 2010 itself was not read (paid); every statement is the edition of 2010 as Klikovac reports it. The PDF has no text layer and was read as page images. Retrieved 2026-10-24."
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
**Not a rule.** No entry in `spec/rules/order.json`, no locale data of its own, no edits. This
|
|
4
4
|
document specifies a **second public entry point** beside `transform`, which runs the identical
|
|
5
5
|
pipeline and reports what it would do instead of doing it.
|
|
6
|
-
**Spec version:** 1.
|
|
6
|
+
**Spec version:** 1.13.0 (new in 1.3.0; `end` of a non-insertion defined in §2, and the matching
|
|
7
|
+
obligation in §6, in 1.13.0).
|
|
7
8
|
|
|
8
9
|
---
|
|
9
10
|
|
|
@@ -23,7 +24,7 @@ transform(input, options) -> string
|
|
|
23
24
|
analyze(input, options) -> Change[]
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
Same `options
|
|
27
|
+
Same `options` (`protect` included, modes.md §3.9), same validation, same errors, same purity (`ARCHITECTURE.md` §7: no I/O, no
|
|
27
28
|
clock, no globals, reentrant). Everything §2 of every rule document says about what the pipeline
|
|
28
29
|
does applies unchanged — `analyze` **is** the pipeline; it merely keeps the edits instead of
|
|
29
30
|
discarding them after applying.
|
|
@@ -49,8 +50,10 @@ Change {
|
|
|
49
50
|
whose strings are UTF-16 must convert; a runtime whose strings are bytes must convert.
|
|
50
51
|
- **Offsets are into `input` exactly as the caller passed it.** In `html`, `markdown` and
|
|
51
52
|
`yaml` mode that means offsets into the **document**, not into the span the rules actually ran
|
|
52
|
-
over.
|
|
53
|
-
|
|
53
|
+
over. A non-insertion's `end` is one past the offset of the last code point it replaced that
|
|
54
|
+
came from the input, so it can never run across a skipped region or a protected run that
|
|
55
|
+
follows (since 1.13.0; 1.12.0 scanned forward to the next code point with an origin). The
|
|
56
|
+
document-offset rule is not a new obligation: [modes.md](modes.md) §4 already defines the output as "the input
|
|
54
57
|
source with a set of disjoint substring replacements applied **at recorded offsets**", and
|
|
55
58
|
those are the offsets meant.
|
|
56
59
|
- `start == end` is a pure insertion; `before` is then empty. `after` empty with `end > start`
|
|
@@ -137,6 +140,15 @@ naming:
|
|
|
137
140
|
|
|
138
141
|
- **A3 against the whole fixture corpus.** For every canonical fixture case, `analyze` returns
|
|
139
142
|
an empty list exactly when `in == out`. That is a strong test and it costs one loop.
|
|
143
|
+
- **A range that crosses nothing it did not change (since 1.13.0).** A change's `start` and `end`
|
|
144
|
+
come from the code points it replaced, and `end` is not found by scanning forward from the
|
|
145
|
+
edit's end index to the next code point that has an origin: the boundary marker has none, so a
|
|
146
|
+
change ending beside a skipped element or a protected run (modes.md §3.9) would be carried
|
|
147
|
+
across it. Test a change whose last code point is a span's last, followed by a skipped region
|
|
148
|
+
longer than the change: no reported range may overlap a skipped region or a protected run, and
|
|
149
|
+
a non-insertion's `end` is one past the input offset of the last replaced code point that has
|
|
150
|
+
one. (`end - start` need not equal the length of `before`: `before` is what a later rule saw,
|
|
151
|
+
§5.) 1.12.0 reported `<p>x--<code>cc</code>y</p>` as `start` 4, `end` 21.
|
|
140
152
|
- **A5 under the mode adapters.** A runtime that reports span-local offsets in `html`,
|
|
141
153
|
`markdown` or `yaml` mode passes every text-mode test and is still wrong. Test a document
|
|
142
154
|
whose first span does not start at offset 0. `yaml` is the cheapest of the three to get wrong
|