polytypo 1.10.0 → 1.12.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/lib/polytypo/data/VERSION +1 -1
- data/lib/polytypo/data/fixtures/bg.json +253 -0
- 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 +1 -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 +880 -3
- 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 +1 -1
- data/lib/polytypo/data/fixtures/hr.json +253 -0
- data/lib/polytypo/data/fixtures/hu.json +264 -0
- 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 +234 -0
- data/lib/polytypo/data/fixtures/ru.json +1 -1
- data/lib/polytypo/data/fixtures/sk.json +278 -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/bg.json +82 -0
- data/lib/polytypo/data/locales/hr.json +82 -0
- data/lib/polytypo/data/locales/hu.json +88 -0
- data/lib/polytypo/data/locales/registry.json +7 -2
- data/lib/polytypo/data/locales/ro.json +82 -0
- data/lib/polytypo/data/locales/sk.json +94 -0
- data/lib/polytypo/data/rules/dashes.md +2 -1
- data/lib/polytypo/data/rules/modes.md +173 -72
- data/lib/polytypo/data/rules/order.json +1 -1
- data/lib/polytypo/data/rules/quotes.md +9 -0
- data/lib/polytypo/data/rules/ranges.md +6 -5
- data/lib/polytypo/data/schema/locale.schema.json +2 -2
- data/lib/polytypo/modes/yaml.rb +167 -29
- data/lib/polytypo/version.rb +1 -1
- metadata +11 -1
data/lib/polytypo/modes/yaml.rb
CHANGED
|
@@ -34,9 +34,32 @@ module Polytypo
|
|
|
34
34
|
spans = []
|
|
35
35
|
li = 0
|
|
36
36
|
li = scan_line(chars, lines, li, keys, spans) while li < lines.length
|
|
37
|
-
spans
|
|
37
|
+
drop_carriage_return_regions(chars, lines, spans)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# 3.8.4's carriage-return region: a line containing a U+000D yields no spans, and neither does
|
|
41
|
+
# any later line up to the first one that is non-empty, does not begin with U+0020 and
|
|
42
|
+
# contains none. It removes spans and never adds one.
|
|
43
|
+
def self.drop_carriage_return_regions(chars, lines, spans)
|
|
44
|
+
in_region = false
|
|
45
|
+
declined = lines.map do |line|
|
|
46
|
+
if lone_cr?(chars, line)
|
|
47
|
+
in_region = true
|
|
48
|
+
elsif line.end > line.start && chars[line.start] != " "
|
|
49
|
+
in_region = false
|
|
50
|
+
end
|
|
51
|
+
in_region
|
|
52
|
+
end
|
|
53
|
+
return spans unless declined.any?
|
|
54
|
+
|
|
55
|
+
li = 0
|
|
56
|
+
spans.reject do |span|
|
|
57
|
+
li += 1 while li + 1 < lines.length && span.start >= lines[li + 1].start
|
|
58
|
+
declined[li]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
private_class_method :drop_carriage_return_regions
|
|
62
|
+
|
|
40
63
|
# 3.8.4: a line ends at U+000A, and A U+000D IMMEDIATELY BEFORE IT IS NOT PART OF THE LINE
|
|
41
64
|
# -- it is a terminator like the U+000A itself, so it lies outside every span and comes back
|
|
42
65
|
# untouched. Without that clause a CRLF file behaves differently from the same bytes with
|
|
@@ -70,6 +93,12 @@ module Polytypo
|
|
|
70
93
|
end
|
|
71
94
|
private_class_method :blank?
|
|
72
95
|
|
|
96
|
+
# 3.8.4 step 7: a line of the value run that is not blank. Blank lines alone are not one.
|
|
97
|
+
def self.continuation?(chars, lines, li, run_end)
|
|
98
|
+
((li + 1)...run_end).any? { |k| !blank?(chars, lines[k]) }
|
|
99
|
+
end
|
|
100
|
+
private_class_method :continuation?
|
|
101
|
+
|
|
73
102
|
def self.tab?(chars, line)
|
|
74
103
|
(line.start...line.end).any? { |i| chars[i] == TAB }
|
|
75
104
|
end
|
|
@@ -129,72 +158,181 @@ module Polytypo
|
|
|
129
158
|
end
|
|
130
159
|
private_class_method :value_run_end
|
|
131
160
|
|
|
161
|
+
# 3.8.4 step 5: the index of the quote that closes the one at q, or -1. In a double-quoted
|
|
162
|
+
# scalar a U+005C and the code point after it never close the quote, which decodes nothing:
|
|
163
|
+
# no YAML escape ends in anything that would be a closing quote. A quote not closed on the
|
|
164
|
+
# line returns -1.
|
|
165
|
+
def self.closing_quote(chars, line, q)
|
|
166
|
+
quote = chars[q]
|
|
167
|
+
j = q + 1
|
|
168
|
+
while j < line.end
|
|
169
|
+
c = chars[j]
|
|
170
|
+
if (quote == '"' && c == "\\") || (quote == "'" && c == quote && j + 1 < line.end && chars[j + 1] == "'")
|
|
171
|
+
j += 2
|
|
172
|
+
elsif c != quote
|
|
173
|
+
j += 1
|
|
174
|
+
else
|
|
175
|
+
return j
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
-1
|
|
179
|
+
end
|
|
180
|
+
private_class_method :closing_quote
|
|
181
|
+
|
|
182
|
+
# 3.8.4 step 3: the next line that begins in column 0 with a document marker.
|
|
183
|
+
def self.document_run_end(chars, lines, li)
|
|
184
|
+
k = li + 1
|
|
185
|
+
k += 1 while k < lines.length && !document_marker?(chars, lines[k].start, lines[k].end)
|
|
186
|
+
k
|
|
187
|
+
end
|
|
188
|
+
private_class_method :document_run_end
|
|
189
|
+
|
|
190
|
+
# 3.8.4 step 5's own-line node: everything up to the first later line that is non-empty and
|
|
191
|
+
# does not begin with U+0020, because the scan does not track the key above.
|
|
192
|
+
def self.own_line_run_end(chars, lines, li, indent)
|
|
193
|
+
return document_run_end(chars, lines, li) if indent.zero?
|
|
194
|
+
|
|
195
|
+
k = li + 1
|
|
196
|
+
while k < lines.length
|
|
197
|
+
nxt = lines[k]
|
|
198
|
+
break if nxt.end > nxt.start && chars[nxt.start] != SPACE
|
|
199
|
+
|
|
200
|
+
k += 1
|
|
201
|
+
end
|
|
202
|
+
k
|
|
203
|
+
end
|
|
204
|
+
private_class_method :own_line_run_end
|
|
205
|
+
|
|
206
|
+
# 3.8.4 step 4: "-", "?" or ":" followed by U+0020 or the line end.
|
|
207
|
+
def self.node_marker?(chars, j, to)
|
|
208
|
+
return false unless ["-", "?", ":"].include?(chars[j])
|
|
209
|
+
|
|
210
|
+
j + 1 == to || chars[j + 1] == SPACE
|
|
211
|
+
end
|
|
212
|
+
private_class_method :node_marker?
|
|
213
|
+
|
|
214
|
+
# 3.8.4 step 5. Returns [colon, key_end, declined, open_quote]; colon is -1 for a line with
|
|
215
|
+
# no key, which is a node line.
|
|
216
|
+
def self.locate_key(chars, line, i)
|
|
217
|
+
first = chars[i]
|
|
218
|
+
if ['"', "'"].include?(first)
|
|
219
|
+
close = closing_quote(chars, line, i)
|
|
220
|
+
return [-1, -1, false, true] if close.negative?
|
|
221
|
+
|
|
222
|
+
p = close + 1
|
|
223
|
+
p += 1 while p < line.end && chars[p] == SPACE
|
|
224
|
+
return [p, close + 1, true, false] if p < line.end && indicator_colon?(chars, p, line.end)
|
|
225
|
+
|
|
226
|
+
return [-1, -1, false, false]
|
|
227
|
+
end
|
|
228
|
+
return [-1, -1, false, false] if ["{", "[", "&", "*", "!"].include?(first)
|
|
229
|
+
|
|
230
|
+
declined = false
|
|
231
|
+
(i...line.end).each do |j|
|
|
232
|
+
c = chars[j]
|
|
233
|
+
break if c == "#" && chars[j - 1] == SPACE
|
|
234
|
+
|
|
235
|
+
if indicator_colon?(chars, j, line.end)
|
|
236
|
+
key_end = j
|
|
237
|
+
key_end -= 1 while key_end > i && chars[key_end - 1] == SPACE
|
|
238
|
+
return [j, key_end, declined, false]
|
|
239
|
+
end
|
|
240
|
+
declined = true if DECLINING_KEY_CHARS.include?(c)
|
|
241
|
+
end
|
|
242
|
+
[-1, -1, false, false]
|
|
243
|
+
end
|
|
244
|
+
private_class_method :locate_key
|
|
245
|
+
|
|
246
|
+
# 3.8.4 step 7: whether a declined key's value can continue on the next line.
|
|
247
|
+
def self.declined_value_open?(chars, line, v)
|
|
248
|
+
value = chars[v]
|
|
249
|
+
return true if ["|", ">", "{", "[", "&", "!"].include?(value)
|
|
250
|
+
return false unless ['"', "'"].include?(value)
|
|
251
|
+
|
|
252
|
+
close = closing_quote(chars, line, v)
|
|
253
|
+
return true if close.negative?
|
|
254
|
+
|
|
255
|
+
p = close + 1
|
|
256
|
+
p += 1 while p < line.end && chars[p] == SPACE
|
|
257
|
+
p < line.end && chars[p] != "#"
|
|
258
|
+
end
|
|
259
|
+
private_class_method :declined_value_open?
|
|
260
|
+
|
|
132
261
|
# One step of 3.8.4. Returns the index of the next line to scan.
|
|
133
262
|
def self.scan_line(chars, lines, li, keys, spans)
|
|
134
263
|
line = lines[li]
|
|
135
264
|
skip_line = li + 1
|
|
136
265
|
|
|
137
|
-
# step 1 -- blank
|
|
266
|
+
# step 1 -- blank. A tab makes indentation undecidable, so the line and whatever it
|
|
267
|
+
# absorbs yield no spans, but steps 2 to 7 still decide what it absorbs.
|
|
138
268
|
start = first_non_space(chars, line)
|
|
139
|
-
return skip_line if start == line.end
|
|
269
|
+
return skip_line if start == line.end
|
|
140
270
|
|
|
271
|
+
quiet = tab?(chars, line)
|
|
141
272
|
indent = start - line.start
|
|
142
273
|
|
|
143
|
-
# steps 2 and 3 -- comment, directive, document marker.
|
|
274
|
+
# steps 2 and 3 -- comment, directive, document marker. A marker with a node after it
|
|
275
|
+
# absorbs the document.
|
|
144
276
|
return skip_line if ["#", "%"].include?(chars[start])
|
|
145
|
-
return skip_line if document_marker?(chars, start, line.end)
|
|
146
277
|
|
|
147
|
-
|
|
278
|
+
if document_marker?(chars, start, line.end)
|
|
279
|
+
p = start + 3
|
|
280
|
+
p += 1 while p < line.end && chars[p] == SPACE
|
|
281
|
+
return skip_line if p >= line.end || chars[p] == "#"
|
|
282
|
+
|
|
283
|
+
return indent.zero? ? document_run_end(chars, lines, li) : skip_line
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# step 4 -- node markers are consumed, not skipped; "- - key: v" nests.
|
|
148
287
|
i = start
|
|
149
|
-
while i < line.end &&
|
|
288
|
+
while i < line.end && node_marker?(chars, i, line.end)
|
|
150
289
|
i += 2
|
|
151
290
|
i += 1 while i < line.end && chars[i] == SPACE
|
|
152
291
|
end
|
|
153
|
-
return skip_line if i >= line.end
|
|
292
|
+
return skip_line if i >= line.end || chars[i] == "#"
|
|
154
293
|
|
|
155
294
|
# step 5 -- find the key. A colon NOT followed by U+0020 or the line end is an ordinary
|
|
156
295
|
# key character, so "a:b: v" has the key "a:b"; stating that is what keeps five scanners
|
|
157
296
|
# agreeing.
|
|
158
|
-
|
|
159
|
-
colon = -1
|
|
160
|
-
(i...line.end).each do |j|
|
|
161
|
-
return skip_line if DECLINING_KEY_CHARS.include?(chars[j])
|
|
297
|
+
colon, key_end, declined, open_quote = locate_key(chars, line, i)
|
|
162
298
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
299
|
+
# No key: the line is a node of its own, and it absorbs its value run. One that opens a
|
|
300
|
+
# flow collection, a block scalar or a quote and sits on a line of its own absorbs the
|
|
301
|
+
# region.
|
|
302
|
+
if colon.negative?
|
|
303
|
+
own_line = i == start && (open_quote || ["{", "[", "|", ">"].include?(chars[i]))
|
|
304
|
+
return own_line_run_end(chars, lines, li, indent) if own_line
|
|
169
305
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return skip_line if key_end <= key_start
|
|
306
|
+
return value_run_end(chars, lines, li, indent)
|
|
307
|
+
end
|
|
173
308
|
|
|
174
309
|
# step 6 -- an empty value means a nested node, whose lines ARE scanned on their own.
|
|
175
310
|
v = colon + 1
|
|
176
311
|
v += 1 while v < line.end && chars[v] == SPACE
|
|
177
312
|
return skip_line if v >= line.end
|
|
178
313
|
|
|
179
|
-
# step 7 -- the line carries an inline value, so its continuation lines belong to it.
|
|
314
|
+
# step 7 -- the line carries an inline value, so its continuation lines belong to it. A
|
|
315
|
+
# declined key's value absorbs them only if it can continue on the next line.
|
|
316
|
+
if declined
|
|
317
|
+
return declined_value_open?(chars, line, v) ? value_run_end(chars, lines, li, indent) : skip_line
|
|
318
|
+
end
|
|
319
|
+
|
|
180
320
|
nxt = value_run_end(chars, lines, li, indent)
|
|
181
321
|
|
|
182
322
|
# step 8 -- the key must be listed. Checked before the value's form, so an unlisted key
|
|
183
323
|
# costs nothing to decline: this is what makes "run:", "if:" and "image:" unreachable.
|
|
184
|
-
return nxt unless keys.include?(chars[
|
|
185
|
-
|
|
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)
|
|
324
|
+
return nxt unless keys.include?(chars[i...key_end].join)
|
|
325
|
+
return nxt if quiet
|
|
188
326
|
|
|
189
|
-
# step
|
|
327
|
+
# step 9 -- the scalar form.
|
|
190
328
|
value = chars[v]
|
|
191
329
|
return nxt if NON_SCALAR_VALUE_CHARS.include?(value)
|
|
192
330
|
|
|
193
331
|
if ["|", ">"].include?(value)
|
|
194
332
|
block_scalar(chars, line, lines[(li + 1)...nxt], indent, v, spans)
|
|
195
333
|
elsif ['"', "'"].include?(value)
|
|
196
|
-
quoted_scalar(chars, line, v, spans)
|
|
197
|
-
elsif
|
|
334
|
+
quoted_scalar(chars, line, v, spans) unless continuation?(chars, lines, li, nxt)
|
|
335
|
+
elsif !continuation?(chars, lines, li, nxt)
|
|
198
336
|
plain_scalar(chars, line, v, spans)
|
|
199
337
|
end
|
|
200
338
|
nxt
|
data/lib/polytypo/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: polytypo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Iurii Rogulia
|
|
@@ -40,6 +40,7 @@ files:
|
|
|
40
40
|
- lib/polytypo/data/README.md
|
|
41
41
|
- lib/polytypo/data/UNICODE
|
|
42
42
|
- lib/polytypo/data/VERSION
|
|
43
|
+
- lib/polytypo/data/fixtures/bg.json
|
|
43
44
|
- lib/polytypo/data/fixtures/cs.json
|
|
44
45
|
- lib/polytypo/data/fixtures/de-CH.json
|
|
45
46
|
- lib/polytypo/data/fixtures/de-DE.json
|
|
@@ -50,16 +51,21 @@ files:
|
|
|
50
51
|
- lib/polytypo/data/fixtures/fi.json
|
|
51
52
|
- lib/polytypo/data/fixtures/fr-CA.json
|
|
52
53
|
- lib/polytypo/data/fixtures/fr.json
|
|
54
|
+
- lib/polytypo/data/fixtures/hr.json
|
|
55
|
+
- lib/polytypo/data/fixtures/hu.json
|
|
53
56
|
- lib/polytypo/data/fixtures/it.json
|
|
54
57
|
- lib/polytypo/data/fixtures/locale-resolution.json
|
|
55
58
|
- lib/polytypo/data/fixtures/nl.json
|
|
56
59
|
- lib/polytypo/data/fixtures/pl.json
|
|
57
60
|
- lib/polytypo/data/fixtures/pt-BR.json
|
|
58
61
|
- lib/polytypo/data/fixtures/pt-PT.json
|
|
62
|
+
- lib/polytypo/data/fixtures/ro.json
|
|
59
63
|
- lib/polytypo/data/fixtures/ru.json
|
|
64
|
+
- lib/polytypo/data/fixtures/sk.json
|
|
60
65
|
- lib/polytypo/data/fixtures/sv.json
|
|
61
66
|
- lib/polytypo/data/fixtures/tr.json
|
|
62
67
|
- lib/polytypo/data/fixtures/uk.json
|
|
68
|
+
- lib/polytypo/data/locales/bg.json
|
|
63
69
|
- lib/polytypo/data/locales/cs.json
|
|
64
70
|
- lib/polytypo/data/locales/de-CH.json
|
|
65
71
|
- lib/polytypo/data/locales/de-DE.json
|
|
@@ -70,13 +76,17 @@ files:
|
|
|
70
76
|
- lib/polytypo/data/locales/fi.json
|
|
71
77
|
- lib/polytypo/data/locales/fr-CA.json
|
|
72
78
|
- lib/polytypo/data/locales/fr.json
|
|
79
|
+
- lib/polytypo/data/locales/hr.json
|
|
80
|
+
- lib/polytypo/data/locales/hu.json
|
|
73
81
|
- lib/polytypo/data/locales/it.json
|
|
74
82
|
- lib/polytypo/data/locales/nl.json
|
|
75
83
|
- lib/polytypo/data/locales/pl.json
|
|
76
84
|
- lib/polytypo/data/locales/pt-BR.json
|
|
77
85
|
- lib/polytypo/data/locales/pt-PT.json
|
|
78
86
|
- lib/polytypo/data/locales/registry.json
|
|
87
|
+
- lib/polytypo/data/locales/ro.json
|
|
79
88
|
- lib/polytypo/data/locales/ru.json
|
|
89
|
+
- lib/polytypo/data/locales/sk.json
|
|
80
90
|
- lib/polytypo/data/locales/sv.json
|
|
81
91
|
- lib/polytypo/data/locales/tr.json
|
|
82
92
|
- lib/polytypo/data/locales/uk.json
|