kapusta 0.1.0 → 0.1.1
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 +1 -1
- data/examples/accumulator.kap +1 -1
- data/examples/anagram.kap +3 -3
- data/examples/binary-search.kap +2 -2
- data/examples/block-sort.kap +1 -1
- data/examples/blocks-and-kwargs.kap +20 -0
- data/examples/calc.kap +1 -1
- data/examples/counter.kap +1 -1
- data/examples/doto.kap +1 -1
- data/examples/even-squares.kap +1 -1
- data/examples/exceptions.kap +2 -2
- data/examples/files.kap +13 -0
- data/examples/greet.kap +1 -1
- data/examples/inheritance.kap +13 -0
- data/examples/kwargs.kap +1 -1
- data/examples/match.kap +3 -3
- data/examples/module-header.kap +2 -1
- data/examples/palindrome.kap +3 -3
- data/examples/pangram.kap +2 -2
- data/examples/pcall.kap +6 -6
- data/examples/pipeline.kap +4 -1
- data/examples/points.kap +1 -1
- data/examples/record.kap +1 -1
- data/examples/regex.kap +3 -1
- data/examples/ruby-eval.kap +1 -1
- data/examples/safe-lookup.kap +2 -2
- data/examples/scopes.kap +4 -4
- data/examples/threading.kap +28 -0
- data/examples/tset.kap +2 -2
- data/examples/two-sum.kap +3 -3
- data/kapusta.gemspec +4 -0
- data/lib/kapusta/ast.rb +16 -4
- data/lib/kapusta/cli.rb +8 -2
- data/lib/kapusta/formatter.rb +262 -110
- data/lib/kapusta/reader.rb +51 -24
- data/lib/kapusta/version.rb +1 -1
- data/spec/cli_spec.rb +15 -0
- data/spec/examples_spec.rb +25 -0
- data/spec/formatter_spec.rb +114 -5
- metadata +9 -2
data/lib/kapusta/formatter.rb
CHANGED
|
@@ -13,10 +13,16 @@ module Kapusta
|
|
|
13
13
|
def initialize(argv)
|
|
14
14
|
@mode = :stdout
|
|
15
15
|
@files = []
|
|
16
|
+
@version = false
|
|
16
17
|
parse_args(argv)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def run
|
|
21
|
+
if @version
|
|
22
|
+
puts "kapfmt #{Kapusta::VERSION}"
|
|
23
|
+
return 0
|
|
24
|
+
end
|
|
25
|
+
|
|
20
26
|
validate_args!
|
|
21
27
|
|
|
22
28
|
formatted = @files.map do |path|
|
|
@@ -56,6 +62,8 @@ module Kapusta
|
|
|
56
62
|
ensure_mode!(:fix)
|
|
57
63
|
when '--check'
|
|
58
64
|
ensure_mode!(:check)
|
|
65
|
+
when '--version', '-v'
|
|
66
|
+
@version = true
|
|
59
67
|
when '--help', '-h'
|
|
60
68
|
print_help
|
|
61
69
|
exit 0
|
|
@@ -92,16 +100,14 @@ module Kapusta
|
|
|
92
100
|
end
|
|
93
101
|
|
|
94
102
|
def format_source(source)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
rendered = forms.map { |form| render(form, 0, top_level: true) }
|
|
99
|
-
return '' if rendered.empty?
|
|
103
|
+
forms = Reader.read_all(source, preserve_comments: true)
|
|
104
|
+
entries = top_level_entries(forms)
|
|
105
|
+
return '' if entries.empty?
|
|
100
106
|
|
|
101
107
|
output = +''
|
|
102
|
-
|
|
103
|
-
output <<
|
|
104
|
-
output <<
|
|
108
|
+
entries.each_with_index do |entry, index|
|
|
109
|
+
output << separator_for_entries(entries[index - 1], entry) unless index.zero?
|
|
110
|
+
output << render_top_level_entry(entry)
|
|
105
111
|
end
|
|
106
112
|
output << "\n"
|
|
107
113
|
rescue StandardError => e
|
|
@@ -117,37 +123,37 @@ module Kapusta
|
|
|
117
123
|
end
|
|
118
124
|
end
|
|
119
125
|
|
|
120
|
-
def
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
while index < source.length
|
|
124
|
-
char = source[index]
|
|
126
|
+
def top_level_entries(forms)
|
|
127
|
+
entries = []
|
|
128
|
+
leading_comments = []
|
|
125
129
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
raise Error, 'kapfmt does not support comments yet.'
|
|
130
|
+
forms.each do |form|
|
|
131
|
+
if comment?(form)
|
|
132
|
+
leading_comments << form
|
|
130
133
|
else
|
|
131
|
-
|
|
134
|
+
entries << { comments: leading_comments, form: }
|
|
135
|
+
leading_comments = []
|
|
132
136
|
end
|
|
133
137
|
end
|
|
138
|
+
|
|
139
|
+
entries << { comments: leading_comments, form: nil } unless leading_comments.empty?
|
|
140
|
+
entries
|
|
134
141
|
end
|
|
135
142
|
|
|
136
|
-
def
|
|
137
|
-
|
|
143
|
+
def separator_for_entries(previous, current)
|
|
144
|
+
return "\n" unless previous[:form] && current[:form]
|
|
138
145
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
index += 1
|
|
146
|
+
separator_for(previous[:form], current[:form])
|
|
147
|
+
end
|
|
142
148
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
end
|
|
149
|
+
def render_top_level_entry(entry)
|
|
150
|
+
parts = entry[:comments].map { |comment| render(comment, 0) }
|
|
151
|
+
parts << render(entry[:form], 0, top_level: true) if entry[:form]
|
|
152
|
+
parts.join("\n")
|
|
153
|
+
end
|
|
149
154
|
|
|
150
|
-
|
|
155
|
+
def comment?(form)
|
|
156
|
+
form.is_a?(Comment)
|
|
151
157
|
end
|
|
152
158
|
|
|
153
159
|
def render(form, indent, layout: nil, top_level: false, force_expand: false)
|
|
@@ -155,6 +161,7 @@ module Kapusta
|
|
|
155
161
|
return flat if !force_expand && flat && fits?(flat, indent) && allow_flat?(form, top_level:, layout:)
|
|
156
162
|
|
|
157
163
|
case form
|
|
164
|
+
when Comment then form.text
|
|
158
165
|
when List then render_list(form, indent, top_level:)
|
|
159
166
|
when Vec then render_vec(form, indent, layout:, top_level:, force_expand:)
|
|
160
167
|
when HashLit then render_hash(form, indent)
|
|
@@ -165,14 +172,21 @@ module Kapusta
|
|
|
165
172
|
|
|
166
173
|
def flat_render(form)
|
|
167
174
|
case form
|
|
175
|
+
when Comment
|
|
176
|
+
nil
|
|
168
177
|
when Sym
|
|
169
178
|
form.name
|
|
170
179
|
when Vec
|
|
180
|
+
return nil if contains_comments?(form.items)
|
|
181
|
+
|
|
171
182
|
"[#{form.items.map { |item| flat_render(item) }.join(' ')}]"
|
|
172
183
|
when HashLit
|
|
184
|
+
return nil if contains_comments?(form.entries)
|
|
185
|
+
|
|
173
186
|
"{#{form.pairs.map { |key, value| flat_hash_pair(key, value) }.join(' ')}}"
|
|
174
187
|
when List
|
|
175
|
-
return
|
|
188
|
+
return nil if contains_comments?(form.items)
|
|
189
|
+
return "##{flat_render(semantic_items(form.items)[1])}" if hashfn_literal?(form)
|
|
176
190
|
|
|
177
191
|
"(#{form.items.map { |item| flat_render(item) }.join(' ')})"
|
|
178
192
|
when String, Numeric, true, false, nil
|
|
@@ -183,55 +197,66 @@ module Kapusta
|
|
|
183
197
|
end
|
|
184
198
|
|
|
185
199
|
def render_list(list, indent, top_level: false)
|
|
186
|
-
return '()' if list.empty?
|
|
187
|
-
return "##{render(list.items[1], indent, top_level:)}" if hashfn_literal?(list)
|
|
200
|
+
return '()' if list.items.empty?
|
|
201
|
+
return "##{render(semantic_items(list.items)[1], indent, top_level:)}" if hashfn_literal?(list)
|
|
202
|
+
|
|
203
|
+
head = list_head(list)
|
|
204
|
+
return render_generic_list(list, indent) unless head
|
|
188
205
|
|
|
189
|
-
head = list.head
|
|
190
206
|
head_name = head.is_a?(Sym) ? head.name : nil
|
|
207
|
+
raw_args = list_raw_rest(list)
|
|
191
208
|
|
|
192
209
|
case head_name
|
|
193
210
|
when 'fn', 'lambda', 'λ' then render_fn(head_name, list, indent, top_level:)
|
|
194
211
|
when 'let' then render_let(list, indent)
|
|
195
|
-
when 'do', 'finally' then render_prefix_body_form(head_name, [],
|
|
212
|
+
when 'do', 'finally' then render_prefix_body_form(head_name, [], raw_args, indent)
|
|
196
213
|
when 'try' then render_try(list, indent)
|
|
197
214
|
when 'while', 'when', 'unless', 'for', 'each', 'icollect', 'collect', 'fcollect', 'accumulate', 'faccumulate'
|
|
198
|
-
|
|
199
|
-
|
|
215
|
+
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
216
|
+
render_prefix_body_form(head_name, raw_prefix, raw_body, indent)
|
|
217
|
+
when 'module'
|
|
218
|
+
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
219
|
+
render_prefix_body_form('module', raw_prefix, raw_body, indent)
|
|
200
220
|
when 'class' then render_class(list, indent)
|
|
201
221
|
when 'catch' then render_catch(list, indent)
|
|
202
222
|
when 'if' then render_if(list, indent)
|
|
203
|
-
when 'case', 'match'
|
|
204
|
-
|
|
223
|
+
when 'case', 'match'
|
|
224
|
+
if contains_comments?(raw_args)
|
|
225
|
+
render_sequential_head_form(head_name, raw_args, indent)
|
|
226
|
+
else
|
|
227
|
+
render_case(head_name, list_rest(list), indent)
|
|
228
|
+
end
|
|
229
|
+
when *PIPELINE_FORMS then render_pipeline(head_name, raw_args, indent)
|
|
205
230
|
else
|
|
206
231
|
render_call(list, indent)
|
|
207
232
|
end
|
|
208
233
|
end
|
|
209
234
|
|
|
210
235
|
def render_fn(head, list, indent, top_level: false)
|
|
211
|
-
args = list
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
end
|
|
236
|
+
args = list_rest(list)
|
|
237
|
+
raw_args = list_raw_rest(list)
|
|
238
|
+
prefix_length = args[0].is_a?(Sym) && args[1].is_a?(Vec) ? 2 : 1
|
|
239
|
+
raw_prefix, raw_body = split_raw_items(raw_args, prefix_length)
|
|
240
|
+
render_prefix_body_form(head, raw_prefix, raw_body, indent, force_body_multiline: top_level)
|
|
217
241
|
end
|
|
218
242
|
|
|
219
243
|
def render_catch(list, indent)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
body = args.drop(2)
|
|
223
|
-
render_prefix_body_form('catch', prefix, body, indent)
|
|
244
|
+
raw_prefix, raw_body = split_raw_items(list_raw_rest(list), 2)
|
|
245
|
+
render_prefix_body_form('catch', raw_prefix, raw_body, indent)
|
|
224
246
|
end
|
|
225
247
|
|
|
226
248
|
def render_class(list, indent)
|
|
227
|
-
args = list
|
|
249
|
+
args = list_rest(list)
|
|
250
|
+
raw_args = list_raw_rest(list)
|
|
228
251
|
prefix = args[1].is_a?(Vec) ? args.take(2) : args.take(1)
|
|
229
|
-
|
|
230
|
-
render_prefix_body_form('class',
|
|
252
|
+
raw_prefix, raw_body = split_raw_items(raw_args, prefix.length)
|
|
253
|
+
render_prefix_body_form('class', raw_prefix, raw_body, indent)
|
|
231
254
|
end
|
|
232
255
|
|
|
233
256
|
def render_try(list, indent)
|
|
234
|
-
args = list
|
|
257
|
+
args = list_rest(list)
|
|
258
|
+
return render_sequential_head_form('try', list_raw_rest(list), indent) if contains_comments?(list_raw_rest(list))
|
|
259
|
+
|
|
235
260
|
lines = ['(try']
|
|
236
261
|
|
|
237
262
|
if args.any?
|
|
@@ -252,10 +277,17 @@ module Kapusta
|
|
|
252
277
|
end
|
|
253
278
|
|
|
254
279
|
def render_let(list, indent)
|
|
255
|
-
bindings = list.
|
|
256
|
-
|
|
280
|
+
bindings = list_rest(list).first
|
|
281
|
+
raw_args = list_raw_rest(list)
|
|
282
|
+
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
283
|
+
body = list_rest(list).drop(1)
|
|
257
284
|
unless bindings.is_a?(Vec)
|
|
258
|
-
return render_prefix_body_form('let',
|
|
285
|
+
return render_prefix_body_form('let', raw_prefix, raw_body, indent,
|
|
286
|
+
layouts: [:pairwise])
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
if contains_comments?(raw_args) || contains_comments?(bindings.items)
|
|
290
|
+
return render_prefix_body_form('let', raw_prefix, raw_body, indent,
|
|
259
291
|
layouts: [:pairwise])
|
|
260
292
|
end
|
|
261
293
|
|
|
@@ -272,20 +304,35 @@ module Kapusta
|
|
|
272
304
|
line = "(#{head}"
|
|
273
305
|
lines = [line]
|
|
274
306
|
current_first_line = line
|
|
307
|
+
layout_index = 0
|
|
308
|
+
inline_prefix = true
|
|
309
|
+
|
|
310
|
+
prefix_forms.each do |form|
|
|
311
|
+
if comment?(form)
|
|
312
|
+
lines << indent_block(render(form, indent + INDENT), INDENT)
|
|
313
|
+
inline_prefix = false
|
|
314
|
+
next
|
|
315
|
+
end
|
|
275
316
|
|
|
276
|
-
|
|
277
|
-
|
|
317
|
+
rendered = render(form, indent + INDENT, layout: layouts[layout_index])
|
|
318
|
+
layout_index += 1
|
|
278
319
|
candidate = "#{current_first_line} #{rendered}"
|
|
279
320
|
|
|
280
|
-
if single_line?(rendered) && fits?(candidate, indent)
|
|
321
|
+
if inline_prefix && single_line?(rendered) && fits?(candidate, indent)
|
|
281
322
|
current_first_line = candidate
|
|
282
323
|
lines[0] = current_first_line
|
|
283
324
|
else
|
|
284
325
|
lines << indent_block(rendered, INDENT)
|
|
326
|
+
inline_prefix = false
|
|
285
327
|
end
|
|
286
328
|
end
|
|
287
329
|
|
|
288
330
|
body_forms.each do |form|
|
|
331
|
+
if comment?(form)
|
|
332
|
+
lines << indent_block(render(form, indent + INDENT), INDENT)
|
|
333
|
+
next
|
|
334
|
+
end
|
|
335
|
+
|
|
289
336
|
body = render(
|
|
290
337
|
form,
|
|
291
338
|
indent + INDENT,
|
|
@@ -298,7 +345,9 @@ module Kapusta
|
|
|
298
345
|
end
|
|
299
346
|
|
|
300
347
|
def render_if(list, indent)
|
|
301
|
-
args = list
|
|
348
|
+
args = list_rest(list)
|
|
349
|
+
return render_sequential_head_form('if', list_raw_rest(list), indent) if contains_comments?(list_raw_rest(list))
|
|
350
|
+
|
|
302
351
|
lines = []
|
|
303
352
|
hanging = ' ' * '(if '.length
|
|
304
353
|
|
|
@@ -384,9 +433,15 @@ module Kapusta
|
|
|
384
433
|
lines = [base]
|
|
385
434
|
hanging = ' ' * (base.length + 1)
|
|
386
435
|
|
|
387
|
-
|
|
436
|
+
semantic_index = 0
|
|
437
|
+
args.each do |form|
|
|
438
|
+
if comment?(form)
|
|
439
|
+
lines << "#{hanging}#{render(form, indent + base.length + 1)}"
|
|
440
|
+
next
|
|
441
|
+
end
|
|
442
|
+
|
|
388
443
|
rendered = render(form, indent + base.length + 1)
|
|
389
|
-
if
|
|
444
|
+
if semantic_index.zero?
|
|
390
445
|
first_line, *rest = rendered.lines(chomp: true)
|
|
391
446
|
candidate = "#{base} #{first_line}"
|
|
392
447
|
if fits?(candidate, indent)
|
|
@@ -398,39 +453,49 @@ module Kapusta
|
|
|
398
453
|
else
|
|
399
454
|
lines << "#{hanging}#{rendered}"
|
|
400
455
|
end
|
|
456
|
+
semantic_index += 1
|
|
401
457
|
end
|
|
402
458
|
|
|
403
459
|
append_suffix(lines, ')')
|
|
404
460
|
end
|
|
405
461
|
|
|
406
462
|
def render_call(list, indent)
|
|
407
|
-
head = flat_render(list
|
|
408
|
-
raise Error, "cannot format form head: #{list.
|
|
463
|
+
head = flat_render(list_head(list))
|
|
464
|
+
raise Error, "cannot format form head: #{list_head(list).inspect}" unless head
|
|
409
465
|
|
|
410
466
|
base = "(#{head}"
|
|
411
467
|
lines = [base]
|
|
412
|
-
args = list
|
|
468
|
+
args = list_raw_rest(list)
|
|
469
|
+
semantic_length = semantic_items(args).length
|
|
413
470
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
)
|
|
420
|
-
first_line, *rest = first.lines(chomp: true)
|
|
421
|
-
candidate = "#{base} #{first_line}"
|
|
422
|
-
|
|
423
|
-
if fits?(candidate, indent)
|
|
424
|
-
lines[0] = candidate
|
|
425
|
-
hanging = ' ' * (base.length + 1)
|
|
426
|
-
rest.each { |line| lines << "#{hanging}#{line}" }
|
|
427
|
-
else
|
|
428
|
-
lines << indent_block(first, INDENT)
|
|
471
|
+
semantic_index = 0
|
|
472
|
+
args.each do |arg|
|
|
473
|
+
if comment?(arg)
|
|
474
|
+
lines << indent_block(render(arg, indent + INDENT), INDENT)
|
|
475
|
+
next
|
|
429
476
|
end
|
|
430
477
|
|
|
431
|
-
|
|
478
|
+
if semantic_index.zero?
|
|
479
|
+
first = render(
|
|
480
|
+
arg,
|
|
481
|
+
indent + base.length + 1,
|
|
482
|
+
force_expand: semantic_length == 1 && fn_form?(arg)
|
|
483
|
+
)
|
|
484
|
+
first_line, *rest = first.lines(chomp: true)
|
|
485
|
+
candidate = "#{base} #{first_line}"
|
|
486
|
+
|
|
487
|
+
if lines.length == 1 && fits?(candidate, indent)
|
|
488
|
+
lines[0] = candidate
|
|
489
|
+
hanging = ' ' * (base.length + 1)
|
|
490
|
+
rest.each { |line| lines << "#{hanging}#{line}" }
|
|
491
|
+
else
|
|
492
|
+
lines << indent_block(first, INDENT)
|
|
493
|
+
end
|
|
494
|
+
else
|
|
432
495
|
lines << indent_block(render(arg, indent + INDENT), INDENT)
|
|
433
496
|
end
|
|
497
|
+
|
|
498
|
+
semantic_index += 1
|
|
434
499
|
end
|
|
435
500
|
|
|
436
501
|
append_suffix(lines, ')')
|
|
@@ -440,15 +505,14 @@ module Kapusta
|
|
|
440
505
|
flat = flat_render(vec)
|
|
441
506
|
return flat if !force_expand && flat && fits?(flat, indent) && allow_flat?(vec, top_level:, layout:)
|
|
442
507
|
|
|
443
|
-
if layout == :pairwise
|
|
508
|
+
if layout == :pairwise && !contains_comments?(vec.items)
|
|
444
509
|
render_pairwise_vec(vec, indent)
|
|
445
510
|
else
|
|
446
511
|
lines = ['[']
|
|
447
512
|
vec.items.each do |item|
|
|
448
513
|
lines << indent_block(render(item, indent + INDENT), INDENT)
|
|
449
514
|
end
|
|
450
|
-
lines
|
|
451
|
-
lines.join("\n")
|
|
515
|
+
append_suffix(lines, ']')
|
|
452
516
|
end
|
|
453
517
|
end
|
|
454
518
|
|
|
@@ -474,6 +538,7 @@ module Kapusta
|
|
|
474
538
|
end
|
|
475
539
|
|
|
476
540
|
def render_let_bindings(bindings, indent)
|
|
541
|
+
return render(bindings, indent + '(let '.length, force_expand: true) if contains_comments?(bindings.items)
|
|
477
542
|
return render(bindings, indent + '(let '.length, layout: :pairwise) if bindings.items.length <= 2
|
|
478
543
|
|
|
479
544
|
hanging = render_hanging_pairwise_vec(bindings)
|
|
@@ -504,18 +569,22 @@ module Kapusta
|
|
|
504
569
|
|
|
505
570
|
lines = ['{']
|
|
506
571
|
|
|
507
|
-
hash.
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
lines << indent_block(pair, INDENT)
|
|
572
|
+
hash.entries.each do |entry|
|
|
573
|
+
if comment?(entry)
|
|
574
|
+
lines << indent_block(render(entry, indent + INDENT), INDENT)
|
|
511
575
|
else
|
|
512
|
-
|
|
513
|
-
|
|
576
|
+
key, value = entry
|
|
577
|
+
pair = flat_hash_pair(key, value)
|
|
578
|
+
if pair && fits?(pair, indent + INDENT)
|
|
579
|
+
lines << indent_block(pair, INDENT)
|
|
580
|
+
else
|
|
581
|
+
lines << indent_block(render_hash_key(key), INDENT)
|
|
582
|
+
lines << indent_block(render(value, indent + INDENT), INDENT)
|
|
583
|
+
end
|
|
514
584
|
end
|
|
515
585
|
end
|
|
516
586
|
|
|
517
|
-
lines
|
|
518
|
-
lines.join("\n")
|
|
587
|
+
append_suffix(lines, '}')
|
|
519
588
|
end
|
|
520
589
|
|
|
521
590
|
def flat_hash_pair(key, value)
|
|
@@ -564,17 +633,19 @@ module Kapusta
|
|
|
564
633
|
end
|
|
565
634
|
|
|
566
635
|
def hashfn_literal?(form)
|
|
567
|
-
form.is_a?(List)
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
636
|
+
return false unless form.is_a?(List)
|
|
637
|
+
|
|
638
|
+
items = semantic_items(form.items)
|
|
639
|
+
items.length == 2 &&
|
|
640
|
+
items[0].is_a?(Sym) &&
|
|
641
|
+
items[0].name == 'hashfn'
|
|
571
642
|
end
|
|
572
643
|
|
|
573
644
|
def allow_flat?(form, top_level:, layout:)
|
|
574
|
-
return false if layout == :pairwise && form.is_a?(Vec) && form.items.length > 2
|
|
645
|
+
return false if layout == :pairwise && form.is_a?(Vec) && semantic_items(form.items).length > 2
|
|
575
646
|
return true unless form.is_a?(List)
|
|
576
647
|
|
|
577
|
-
head = form
|
|
648
|
+
head = list_head(form)
|
|
578
649
|
return true unless head.is_a?(Sym)
|
|
579
650
|
|
|
580
651
|
case head.name
|
|
@@ -587,9 +658,12 @@ module Kapusta
|
|
|
587
658
|
end
|
|
588
659
|
|
|
589
660
|
def force_multiline_body?(form)
|
|
590
|
-
return false unless form.is_a?(List)
|
|
661
|
+
return false unless form.is_a?(List)
|
|
591
662
|
|
|
592
|
-
|
|
663
|
+
head = list_head(form)
|
|
664
|
+
return false unless head.is_a?(Sym)
|
|
665
|
+
|
|
666
|
+
case head.name
|
|
593
667
|
when 'if', 'case', 'match', 'let', 'try', 'catch', 'finally', 'do', 'for', '->', '->>', '-?>', '-?>>', 'doto',
|
|
594
668
|
'fn', 'lambda', 'λ'
|
|
595
669
|
true
|
|
@@ -600,7 +674,7 @@ module Kapusta
|
|
|
600
674
|
end
|
|
601
675
|
|
|
602
676
|
def fn_body(form)
|
|
603
|
-
args = form
|
|
677
|
+
args = list_rest(form)
|
|
604
678
|
if args[0].is_a?(Sym) && args[1].is_a?(Vec)
|
|
605
679
|
args.drop(2)
|
|
606
680
|
else
|
|
@@ -616,23 +690,27 @@ module Kapusta
|
|
|
616
690
|
return true if require_form?(form)
|
|
617
691
|
return false unless form.is_a?(List) && flat_render(form)
|
|
618
692
|
|
|
619
|
-
head = form
|
|
693
|
+
head = list_head(form)
|
|
620
694
|
return false unless head.is_a?(Sym)
|
|
621
695
|
|
|
622
696
|
!%w[fn module class let].include?(head.name)
|
|
623
697
|
end
|
|
624
698
|
|
|
625
699
|
def require_form?(form)
|
|
626
|
-
form.is_a?(List)
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
700
|
+
return false unless form.is_a?(List)
|
|
701
|
+
|
|
702
|
+
items = semantic_items(form.items)
|
|
703
|
+
items.length == 2 &&
|
|
704
|
+
items[0].is_a?(Sym) &&
|
|
705
|
+
items[0].name == 'require'
|
|
630
706
|
end
|
|
631
707
|
|
|
632
708
|
def fn_form?(form)
|
|
633
|
-
form.is_a?(List)
|
|
634
|
-
|
|
635
|
-
|
|
709
|
+
return false unless form.is_a?(List)
|
|
710
|
+
|
|
711
|
+
head = list_head(form)
|
|
712
|
+
head.is_a?(Sym) &&
|
|
713
|
+
%w[fn lambda λ].include?(head.name)
|
|
636
714
|
end
|
|
637
715
|
|
|
638
716
|
def inline_three_arg_if?(args)
|
|
@@ -670,10 +748,84 @@ module Kapusta
|
|
|
670
748
|
|
|
671
749
|
def append_suffix(lines, suffix)
|
|
672
750
|
updated = lines.dup
|
|
673
|
-
|
|
751
|
+
if updated[-1].lstrip.start_with?(';')
|
|
752
|
+
updated << suffix
|
|
753
|
+
else
|
|
754
|
+
updated[-1] = "#{updated[-1]}#{suffix}"
|
|
755
|
+
end
|
|
674
756
|
updated.join("\n")
|
|
675
757
|
end
|
|
676
758
|
|
|
759
|
+
def render_generic_list(list, indent)
|
|
760
|
+
lines = ['(']
|
|
761
|
+
list.items.each do |item|
|
|
762
|
+
lines << indent_block(render(item, indent + INDENT), INDENT)
|
|
763
|
+
end
|
|
764
|
+
append_suffix(lines, ')')
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
def render_sequential_head_form(head, raw_items, indent)
|
|
768
|
+
lines = ["(#{head}"]
|
|
769
|
+
semantic_index = 0
|
|
770
|
+
|
|
771
|
+
raw_items.each do |item|
|
|
772
|
+
if comment?(item)
|
|
773
|
+
lines << indent_block(render(item, indent + INDENT), INDENT)
|
|
774
|
+
next
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
rendered = render(item, indent + INDENT)
|
|
778
|
+
if semantic_index.zero?
|
|
779
|
+
candidate = "(#{head} #{rendered}"
|
|
780
|
+
if lines.length == 1 && single_line?(rendered) && fits?(candidate, indent)
|
|
781
|
+
lines[0] = candidate
|
|
782
|
+
else
|
|
783
|
+
lines << indent_block(rendered, INDENT)
|
|
784
|
+
end
|
|
785
|
+
else
|
|
786
|
+
lines << indent_block(rendered, INDENT)
|
|
787
|
+
end
|
|
788
|
+
semantic_index += 1
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
append_suffix(lines, ')')
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
def contains_comments?(items)
|
|
795
|
+
items.any? { |item| comment?(item) }
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
def semantic_items(items)
|
|
799
|
+
items.reject { |item| comment?(item) }
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
def list_head(list)
|
|
803
|
+
semantic_items(list.items).first
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
def list_rest(list)
|
|
807
|
+
semantic_items(list.items).drop(1)
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
def list_raw_rest(list)
|
|
811
|
+
index = list.items.index { |item| !comment?(item) }
|
|
812
|
+
return list.items if index.nil?
|
|
813
|
+
|
|
814
|
+
list.items[(index + 1)..] || []
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
def split_raw_items(items, semantic_count)
|
|
818
|
+
split_index = 0
|
|
819
|
+
seen = 0
|
|
820
|
+
|
|
821
|
+
while split_index < items.length && seen < semantic_count
|
|
822
|
+
seen += 1 unless comment?(items[split_index])
|
|
823
|
+
split_index += 1
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
[items.take(split_index), items.drop(split_index)]
|
|
827
|
+
end
|
|
828
|
+
|
|
677
829
|
def print_help
|
|
678
830
|
puts 'Usage: kapfmt [--fix] [--check] FILENAME...'
|
|
679
831
|
puts
|