polytypo 1.9.0 → 1.11.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/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 +1010 -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 +14 -1
- data/lib/polytypo/data/fixtures/it.json +1 -1
- data/lib/polytypo/data/fixtures/locale-resolution.json +1 -1
- data/lib/polytypo/data/fixtures/nl.json +1 -1
- data/lib/polytypo/data/fixtures/pl.json +1 -1
- data/lib/polytypo/data/fixtures/pt-BR.json +1 -1
- data/lib/polytypo/data/fixtures/pt-PT.json +1 -1
- data/lib/polytypo/data/fixtures/ru.json +1 -1
- data/lib/polytypo/data/fixtures/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/registry.json +3 -2
- data/lib/polytypo/data/locales/sk.json +94 -0
- data/lib/polytypo/data/rules/modes.md +206 -57
- data/lib/polytypo/data/rules/order.json +1 -1
- data/lib/polytypo/modes/yaml.rb +179 -30
- data/lib/polytypo/version.rb +1 -1
- metadata +3 -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,11 +93,24 @@ 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
|
|
76
105
|
private_class_method :tab?
|
|
77
106
|
|
|
107
|
+
# A U+000D the line definition did not take for a terminator, i.e. one that is not part of
|
|
108
|
+
# a CRLF and does not end the input. It would otherwise land inside a span.
|
|
109
|
+
def self.lone_cr?(chars, line)
|
|
110
|
+
(line.start...line.end).any? { |i| chars[i] == CR }
|
|
111
|
+
end
|
|
112
|
+
private_class_method :lone_cr?
|
|
113
|
+
|
|
78
114
|
# 3.8.4 step 3. "---" and "..." at the head of a line, bare or introducing a node: the
|
|
79
115
|
# trailing-content form is declined too, so "--- key: value" never yields a key of
|
|
80
116
|
# "--- key".
|
|
@@ -122,59 +158,171 @@ module Polytypo
|
|
|
122
158
|
end
|
|
123
159
|
private_class_method :value_run_end
|
|
124
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
|
+
|
|
125
261
|
# One step of 3.8.4. Returns the index of the next line to scan.
|
|
126
262
|
def self.scan_line(chars, lines, li, keys, spans)
|
|
127
263
|
line = lines[li]
|
|
128
264
|
skip_line = li + 1
|
|
129
265
|
|
|
130
|
-
# 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.
|
|
131
268
|
start = first_non_space(chars, line)
|
|
132
|
-
return skip_line if start == line.end
|
|
269
|
+
return skip_line if start == line.end
|
|
133
270
|
|
|
271
|
+
quiet = tab?(chars, line)
|
|
134
272
|
indent = start - line.start
|
|
135
273
|
|
|
136
|
-
# 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.
|
|
137
276
|
return skip_line if ["#", "%"].include?(chars[start])
|
|
138
|
-
return skip_line if document_marker?(chars, start, line.end)
|
|
139
277
|
|
|
140
|
-
|
|
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.
|
|
141
287
|
i = start
|
|
142
|
-
while i < line.end &&
|
|
288
|
+
while i < line.end && node_marker?(chars, i, line.end)
|
|
143
289
|
i += 2
|
|
144
290
|
i += 1 while i < line.end && chars[i] == SPACE
|
|
145
291
|
end
|
|
146
|
-
return skip_line if i >= line.end
|
|
292
|
+
return skip_line if i >= line.end || chars[i] == "#"
|
|
147
293
|
|
|
148
294
|
# step 5 -- find the key. A colon NOT followed by U+0020 or the line end is an ordinary
|
|
149
295
|
# key character, so "a:b: v" has the key "a:b"; stating that is what keeps five scanners
|
|
150
296
|
# agreeing.
|
|
151
|
-
|
|
152
|
-
colon = -1
|
|
153
|
-
(i...line.end).each do |j|
|
|
154
|
-
return skip_line if DECLINING_KEY_CHARS.include?(chars[j])
|
|
297
|
+
colon, key_end, declined, open_quote = locate_key(chars, line, i)
|
|
155
298
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
162
305
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return skip_line if key_end <= key_start
|
|
306
|
+
return value_run_end(chars, lines, li, indent)
|
|
307
|
+
end
|
|
166
308
|
|
|
167
309
|
# step 6 -- an empty value means a nested node, whose lines ARE scanned on their own.
|
|
168
310
|
v = colon + 1
|
|
169
311
|
v += 1 while v < line.end && chars[v] == SPACE
|
|
170
312
|
return skip_line if v >= line.end
|
|
171
313
|
|
|
172
|
-
# 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
|
+
|
|
173
320
|
nxt = value_run_end(chars, lines, li, indent)
|
|
174
321
|
|
|
175
322
|
# step 8 -- the key must be listed. Checked before the value's form, so an unlisted key
|
|
176
323
|
# costs nothing to decline: this is what makes "run:", "if:" and "image:" unreachable.
|
|
177
|
-
return nxt unless keys.include?(chars[
|
|
324
|
+
return nxt unless keys.include?(chars[i...key_end].join)
|
|
325
|
+
return nxt if quiet
|
|
178
326
|
|
|
179
327
|
# step 9 -- the scalar form.
|
|
180
328
|
value = chars[v]
|
|
@@ -183,8 +331,8 @@ module Polytypo
|
|
|
183
331
|
if ["|", ">"].include?(value)
|
|
184
332
|
block_scalar(chars, line, lines[(li + 1)...nxt], indent, v, spans)
|
|
185
333
|
elsif ['"', "'"].include?(value)
|
|
186
|
-
quoted_scalar(chars, line, v, spans)
|
|
187
|
-
elsif
|
|
334
|
+
quoted_scalar(chars, line, v, spans) unless continuation?(chars, lines, li, nxt)
|
|
335
|
+
elsif !continuation?(chars, lines, li, nxt)
|
|
188
336
|
plain_scalar(chars, line, v, spans)
|
|
189
337
|
end
|
|
190
338
|
nxt
|
|
@@ -217,17 +365,18 @@ module Polytypo
|
|
|
217
365
|
h += 1 while h < line.end && chars[h] == SPACE
|
|
218
366
|
return if h < line.end && chars[h] != "#"
|
|
219
367
|
|
|
220
|
-
# One definition of the run, and
|
|
368
|
+
# One definition of the run, and four conditions that make the whole block yield no
|
|
221
369
|
# spans.
|
|
222
370
|
content = []
|
|
223
371
|
content_indent = -1
|
|
224
372
|
run_lines.each do |nxt|
|
|
225
373
|
next if blank?(chars, nxt) # blank lines belong to the block and yield no span
|
|
226
374
|
|
|
227
|
-
# A tab makes indentation undecidable
|
|
228
|
-
# block as written, or a later line dedented
|
|
229
|
-
# guessable. All
|
|
230
|
-
|
|
375
|
+
# A tab makes indentation undecidable, and a lone U+000D would land inside a span; an
|
|
376
|
+
# explicit indicator that disagrees with the block as written, or a later line dedented
|
|
377
|
+
# inside it, is ambiguous rather than guessable. All of them make the WHOLE block yield
|
|
378
|
+
# no spans -- bail rather than choose.
|
|
379
|
+
break content_indent = -1 if tab?(chars, nxt) || lone_cr?(chars, nxt)
|
|
231
380
|
|
|
232
381
|
next_indent = first_non_space(chars, nxt) - nxt.start
|
|
233
382
|
if content_indent.negative?
|
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.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Iurii Rogulia
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- lib/polytypo/data/fixtures/pt-BR.json
|
|
58
58
|
- lib/polytypo/data/fixtures/pt-PT.json
|
|
59
59
|
- lib/polytypo/data/fixtures/ru.json
|
|
60
|
+
- lib/polytypo/data/fixtures/sk.json
|
|
60
61
|
- lib/polytypo/data/fixtures/sv.json
|
|
61
62
|
- lib/polytypo/data/fixtures/tr.json
|
|
62
63
|
- lib/polytypo/data/fixtures/uk.json
|
|
@@ -77,6 +78,7 @@ files:
|
|
|
77
78
|
- lib/polytypo/data/locales/pt-PT.json
|
|
78
79
|
- lib/polytypo/data/locales/registry.json
|
|
79
80
|
- lib/polytypo/data/locales/ru.json
|
|
81
|
+
- lib/polytypo/data/locales/sk.json
|
|
80
82
|
- lib/polytypo/data/locales/sv.json
|
|
81
83
|
- lib/polytypo/data/locales/tr.json
|
|
82
84
|
- lib/polytypo/data/locales/uk.json
|