kapusta 0.13.2 → 0.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 +50 -11
- data/bin/check-all +6 -1
- data/bin/compile-examples +7 -2
- data/examples/anagram.kap +3 -3
- data/examples/app/args.kap +9 -0
- data/examples/arrange-coins.kap +3 -3
- data/examples/baseball-game.kap +5 -5
- data/examples/best-time-to-buy-sell-stock.kap +2 -2
- data/examples/binary-search.kap +2 -2
- data/examples/binary-to-decimal.kap +1 -1
- data/examples/blocks-and-kwargs.kap +2 -2
- data/examples/count-effects.kap +1 -1
- data/examples/count-items-matching-rule.kap +18 -0
- data/examples/doto-hygiene.kap +2 -2
- data/examples/doto.kap +2 -2
- data/examples/egg-count.kap +1 -1
- data/examples/exceptions.kap +1 -1
- data/examples/falling-drops.kap +7 -7
- data/examples/fennel-parity-examples.txt +3 -6
- data/examples/good-pairs.kap +18 -0
- data/examples/greet.kap +1 -1
- data/examples/happy-number.kap +2 -2
- data/examples/left-right-difference.kap +60 -0
- data/examples/length-of-last-word.kap +4 -4
- data/examples/manhattan-distance.kap +1 -1
- data/examples/maximum-subarray.kap +23 -7
- data/examples/minimum-start-value.kap +19 -0
- data/examples/move-zeroes.kap +2 -2
- data/examples/mruby-runtime-examples.txt +8 -2
- data/examples/non-constant-local.kap +1 -1
- data/examples/number-of-1-bits.kap +1 -1
- data/examples/number-of-steps.kap +1 -1
- data/examples/palindrome.kap +2 -2
- data/examples/pangram.kap +4 -4
- data/examples/pcall.kap +3 -3
- data/examples/pipeline.kap +4 -4
- data/examples/plus-one.kap +3 -3
- data/examples/raindrops.kap +1 -1
- data/examples/range-width.kap +14 -0
- data/examples/recent-counter.kap +6 -6
- data/examples/require-local-args.kap +9 -0
- data/examples/require-local.kap +7 -0
- data/examples/require-module-local.kap +4 -0
- data/examples/require-module.kap +4 -0
- data/examples/reverse-integer.kap +1 -1
- data/examples/roman-to-integer.kap +3 -3
- data/examples/running-sum.kap +20 -0
- data/examples/safe-lookup.kap +2 -2
- data/examples/single-number.kap +1 -1
- data/examples/stack.kap +14 -14
- data/examples/subtract-product-sum.kap +1 -1
- data/examples/summary-ranges.kap +45 -0
- data/examples/threading.kap +5 -5
- data/examples/tset.kap +1 -1
- data/examples/two-sum-hash.kap +3 -3
- data/examples/two-sum.kap +1 -1
- data/examples/ugly-number.kap +1 -1
- data/examples/underground-system.kap +39 -0
- data/examples/valid-parentheses-1.kap +6 -6
- data/exe/kapusta-ls +49 -2
- data/lib/kapusta/ast.rb +8 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +111 -89
- data/lib/kapusta/compiler/emitter/collections.rb +32 -40
- data/lib/kapusta/compiler/emitter/control_flow.rb +33 -31
- data/lib/kapusta/compiler/emitter/expressions.rb +21 -5
- data/lib/kapusta/compiler/emitter/interop.rb +168 -48
- data/lib/kapusta/compiler/emitter/patterns.rb +12 -14
- data/lib/kapusta/compiler/emitter/support.rb +63 -81
- data/lib/kapusta/compiler/language.rb +522 -0
- data/lib/kapusta/compiler/lua_compat.rb +23 -28
- data/lib/kapusta/compiler/macro_expander.rb +30 -30
- data/lib/kapusta/compiler/macro_lowerer.rb +12 -24
- data/lib/kapusta/compiler/normalizer.rb +25 -17
- data/lib/kapusta/compiler.rb +3 -24
- data/lib/kapusta/env.rb +2 -2
- data/lib/kapusta/errors.rb +2 -1
- data/lib/kapusta/formatter/ast_helpers.rb +78 -0
- data/lib/kapusta/formatter/cli.rb +125 -0
- data/lib/kapusta/formatter/line_helpers.rb +44 -0
- data/lib/kapusta/formatter/validator.rb +32 -0
- data/lib/kapusta/formatter.rb +354 -325
- data/lib/kapusta/lsp/identifier.rb +1 -1
- data/lib/kapusta/lsp/rename.rb +21 -11
- data/lib/kapusta/lsp/scope_walker.rb +122 -212
- data/lib/kapusta/lsp/workspace_index.rb +17 -5
- data/lib/kapusta/reader.rb +4 -2
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +39 -6
- data/spec/cli_spec.rb +13 -0
- data/spec/examples_errors_spec.rb +3 -1
- data/spec/examples_spec.rb +67 -15
- data/spec/formatter_spec.rb +246 -0
- data/spec/lsp_spec.rb +69 -0
- data/spec/require_spec.rb +294 -0
- metadata +20 -2
- data/examples/describe.kap +0 -9
data/lib/kapusta/formatter.rb
CHANGED
|
@@ -1,113 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../kapusta'
|
|
4
|
+
require_relative 'formatter/ast_helpers'
|
|
5
|
+
require_relative 'formatter/cli'
|
|
6
|
+
require_relative 'formatter/line_helpers'
|
|
7
|
+
require_relative 'formatter/validator'
|
|
4
8
|
|
|
5
9
|
module Kapusta
|
|
6
10
|
class Formatter
|
|
7
11
|
MAX_WIDTH = 80
|
|
8
12
|
INDENT = 2
|
|
9
13
|
STDIN_PATH = '-'
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
BODY_ONLY_HEADS = %w[do finally].freeze
|
|
15
|
+
SINGLE_PREFIX_BODY_HEADS = %w[
|
|
16
|
+
while when unless for each icollect collect fcollect accumulate faccumulate module
|
|
17
|
+
].freeze
|
|
18
|
+
CASE_HEADS = %w[case match].freeze
|
|
19
|
+
private_constant :BODY_ONLY_HEADS, :SINGLE_PREFIX_BODY_HEADS, :CASE_HEADS
|
|
20
|
+
include ASTHelpers
|
|
21
|
+
include CLI
|
|
22
|
+
include LineHelpers
|
|
23
|
+
include Validator
|
|
12
24
|
|
|
13
25
|
def self.format(source, path: nil)
|
|
14
26
|
new([]).send(:format_source, source, path)
|
|
15
27
|
end
|
|
16
28
|
|
|
17
|
-
def initialize(argv)
|
|
18
|
-
@mode = :stdout
|
|
19
|
-
@files = []
|
|
20
|
-
@version = false
|
|
21
|
-
parse_args(argv)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def run
|
|
25
|
-
if @version
|
|
26
|
-
puts "kapfmt #{Kapusta::VERSION}"
|
|
27
|
-
return 0
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
validate_args!
|
|
31
|
-
|
|
32
|
-
formatted = @files.map do |path|
|
|
33
|
-
original = read_source(path)
|
|
34
|
-
validate_kapusta_source(original, path)
|
|
35
|
-
[path, original, format_source(original, path)]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
case @mode
|
|
39
|
-
when :stdout
|
|
40
|
-
$stdout.write(formatted.first[2])
|
|
41
|
-
when :fix
|
|
42
|
-
formatted.each do |path, _original, rewritten|
|
|
43
|
-
raise Error, 'Cannot use --fix with stdin (-).' if stdin_path?(path)
|
|
44
|
-
|
|
45
|
-
File.write(path, rewritten)
|
|
46
|
-
end
|
|
47
|
-
when :check
|
|
48
|
-
dirty = formatted.reject { |_path, original, rewritten| original == rewritten }
|
|
49
|
-
dirty.each do |path, _original, _rewritten|
|
|
50
|
-
warn "Not formatted: #{path}"
|
|
51
|
-
end
|
|
52
|
-
return 1 unless dirty.empty?
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
0
|
|
56
|
-
rescue Kapusta::Error => e
|
|
57
|
-
warn e.formatted
|
|
58
|
-
1
|
|
59
|
-
end
|
|
60
|
-
|
|
61
29
|
private
|
|
62
30
|
|
|
63
|
-
def validate_kapusta_source(source, path)
|
|
64
|
-
Kapusta::Compiler.compile(source, path:)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def parse_args(argv)
|
|
68
|
-
argv.each do |arg|
|
|
69
|
-
case arg
|
|
70
|
-
when '--fix'
|
|
71
|
-
ensure_mode!(:fix)
|
|
72
|
-
when '--check'
|
|
73
|
-
ensure_mode!(:check)
|
|
74
|
-
when '--version', '-v'
|
|
75
|
-
@version = true
|
|
76
|
-
when '--help', '-h'
|
|
77
|
-
print_help
|
|
78
|
-
exit 0
|
|
79
|
-
else
|
|
80
|
-
@files << arg
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def ensure_mode!(mode)
|
|
86
|
-
raise Error, 'Use at most one of --fix or --check.' if @mode != :stdout && @mode != mode
|
|
87
|
-
|
|
88
|
-
@mode = mode
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def validate_args!
|
|
92
|
-
raise Error, 'Usage: kapfmt [--fix] [--check] FILENAME...' if @files.empty?
|
|
93
|
-
raise Error, 'stdin (-) may only be specified once.' if @files.count { |path| stdin_path?(path) } > 1
|
|
94
|
-
raise Error, 'Cannot use --fix with stdin (-).' if @mode == :fix && @files.any? { |path| stdin_path?(path) }
|
|
95
|
-
|
|
96
|
-
return unless @mode == :stdout && @files.length != 1
|
|
97
|
-
|
|
98
|
-
raise Error, 'Without --fix or --check, kapfmt accepts exactly one file.'
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def read_source(path)
|
|
102
|
-
return File.read(path) unless stdin_path?(path)
|
|
103
|
-
|
|
104
|
-
@stdin_read ||= false
|
|
105
|
-
raise Error, 'stdin (-) may only be specified once.' if @stdin_read
|
|
106
|
-
|
|
107
|
-
@stdin_read = true
|
|
108
|
-
$stdin.read
|
|
109
|
-
end
|
|
110
|
-
|
|
111
31
|
def format_source(source, path = nil)
|
|
112
32
|
forms = Reader.read_all(source, preserve_comments: true)
|
|
113
33
|
entries = top_level_entries(forms)
|
|
@@ -125,10 +45,6 @@ module Kapusta
|
|
|
125
45
|
raise Error.new(e.message, path:)
|
|
126
46
|
end
|
|
127
47
|
|
|
128
|
-
def separator_for(_previous, _current)
|
|
129
|
-
"\n"
|
|
130
|
-
end
|
|
131
|
-
|
|
132
48
|
def top_level_entries(forms)
|
|
133
49
|
entries = []
|
|
134
50
|
leading_comments = []
|
|
@@ -160,18 +76,6 @@ module Kapusta
|
|
|
160
76
|
parts.join("\n")
|
|
161
77
|
end
|
|
162
78
|
|
|
163
|
-
def comment?(form)
|
|
164
|
-
form.is_a?(Comment)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def blank_line?(form)
|
|
168
|
-
form.is_a?(BlankLine)
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def non_semantic?(form)
|
|
172
|
-
comment?(form) || blank_line?(form)
|
|
173
|
-
end
|
|
174
|
-
|
|
175
79
|
def render(form, indent, layout: nil, top_level: false, force_expand: false)
|
|
176
80
|
flat = flat_render(form)
|
|
177
81
|
return flat if !force_expand && flat && fits?(flat, indent) && allow_flat?(form, top_level:, layout:)
|
|
@@ -200,7 +104,11 @@ module Kapusta
|
|
|
200
104
|
rendered = render(inner, indent + prefix.length, force_expand:)
|
|
201
105
|
lines = rendered.lines(chomp: true)
|
|
202
106
|
pad = ' ' * prefix.length
|
|
203
|
-
lines.each_with_index.map
|
|
107
|
+
lines.each_with_index.map do |line, i|
|
|
108
|
+
next '' if line.empty?
|
|
109
|
+
|
|
110
|
+
i.zero? ? "#{prefix}#{line}" : "#{pad}#{line}"
|
|
111
|
+
end.join("\n")
|
|
204
112
|
end
|
|
205
113
|
|
|
206
114
|
def flat_render(form)
|
|
@@ -212,33 +120,11 @@ module Kapusta
|
|
|
212
120
|
when Sym
|
|
213
121
|
form.name
|
|
214
122
|
when Vec
|
|
215
|
-
|
|
216
|
-
return if multiline_in_source?(form)
|
|
217
|
-
|
|
218
|
-
rendered = form.items.map { |item| flat_render(item) }
|
|
219
|
-
return if rendered.any?(&:nil?)
|
|
220
|
-
|
|
221
|
-
"[#{rendered.join(' ')}]"
|
|
123
|
+
flat_render_vec(form)
|
|
222
124
|
when HashLit
|
|
223
|
-
|
|
224
|
-
return if multiline_in_source?(form)
|
|
225
|
-
|
|
226
|
-
rendered = form.pairs.map { |key, value| flat_hash_pair(key, value) }
|
|
227
|
-
return if rendered.any?(&:nil?)
|
|
228
|
-
|
|
229
|
-
"{#{rendered.join(' ')}}"
|
|
125
|
+
flat_render_hash(form)
|
|
230
126
|
when List
|
|
231
|
-
|
|
232
|
-
return if contains_comments?(form.items)
|
|
233
|
-
return "##{flat_render(semantic_items(form.items)[1])}" if hashfn_literal?(form)
|
|
234
|
-
return if multiline_in_source?(form)
|
|
235
|
-
return if let_with_multiple_bindings?(form)
|
|
236
|
-
return if let_with_nested_binding_value?(form)
|
|
237
|
-
|
|
238
|
-
rendered = form.items.map { |item| flat_render(item) }
|
|
239
|
-
return if rendered.any?(&:nil?)
|
|
240
|
-
|
|
241
|
-
"(#{rendered.join(' ')})"
|
|
127
|
+
flat_render_list(form)
|
|
242
128
|
when Quasiquote
|
|
243
129
|
inner = flat_render(form.form)
|
|
244
130
|
inner ? "`#{inner}" : nil
|
|
@@ -255,46 +141,77 @@ module Kapusta
|
|
|
255
141
|
end
|
|
256
142
|
end
|
|
257
143
|
|
|
144
|
+
def flat_render_vec(vec)
|
|
145
|
+
return if contains_comments?(vec.items)
|
|
146
|
+
return if multiline_in_source?(vec)
|
|
147
|
+
|
|
148
|
+
flat_delimited_render(vec.items, '[', ']') { |item| flat_render(item) }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def flat_render_hash(hash)
|
|
152
|
+
return if contains_comments?(hash.entries)
|
|
153
|
+
return if multiline_in_source?(hash)
|
|
154
|
+
|
|
155
|
+
flat_hash_render(hash.pairs)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def flat_render_list(list)
|
|
159
|
+
return render_sigil(list) if list.sigil
|
|
160
|
+
return if contains_comments?(list.items)
|
|
161
|
+
return flat_render_hashfn(list) if hashfn_literal?(list)
|
|
162
|
+
return if multiline_in_source?(list)
|
|
163
|
+
return if let_with_multiple_bindings?(list)
|
|
164
|
+
return if let_with_nested_binding_value?(list)
|
|
165
|
+
|
|
166
|
+
flat_delimited_render(list.items, '(', ')') { |item| flat_render(item) }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def flat_render_hashfn(list)
|
|
170
|
+
rendered = flat_render(semantic_items(list.items)[1])
|
|
171
|
+
"##{rendered}" if rendered
|
|
172
|
+
end
|
|
173
|
+
|
|
258
174
|
def render_list(list, indent, top_level: false)
|
|
259
175
|
return '()' if list.items.empty?
|
|
260
176
|
return "##{render(semantic_items(list.items)[1], indent, top_level:)}" if hashfn_literal?(list)
|
|
261
177
|
|
|
262
|
-
|
|
263
|
-
return render_generic_list(list, indent) unless head
|
|
178
|
+
return render_generic_list(list, indent) unless list_head(list)
|
|
264
179
|
|
|
265
|
-
|
|
180
|
+
name = head_name(list)
|
|
266
181
|
raw_args = list_raw_rest(list)
|
|
267
182
|
|
|
268
|
-
case
|
|
269
|
-
when
|
|
183
|
+
case name
|
|
184
|
+
when *Compiler::Language::FUNCTION_DEFINITION_HEADS, 'macro'
|
|
185
|
+
render_fn(name, list, indent, top_level:)
|
|
270
186
|
when 'let' then render_let(list, indent)
|
|
271
|
-
when
|
|
187
|
+
when *BODY_ONLY_HEADS then render_prefix_body_form(name, [], raw_args, indent)
|
|
272
188
|
when 'try' then render_try(list, indent)
|
|
273
|
-
when
|
|
274
|
-
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
275
|
-
render_prefix_body_form(head_name, raw_prefix, raw_body, indent)
|
|
276
|
-
when 'module'
|
|
277
|
-
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
278
|
-
render_prefix_body_form('module', raw_prefix, raw_body, indent)
|
|
189
|
+
when *SINGLE_PREFIX_BODY_HEADS then render_single_prefix_body_form(name, raw_args, indent)
|
|
279
190
|
when 'class' then render_class(list, indent)
|
|
280
191
|
when 'catch' then render_catch(list, indent)
|
|
281
192
|
when 'if' then render_if(list, indent)
|
|
282
|
-
when
|
|
283
|
-
|
|
284
|
-
render_sequential_head_form(head_name, raw_args, indent)
|
|
285
|
-
else
|
|
286
|
-
render_case(head_name, list_rest(list), indent)
|
|
287
|
-
end
|
|
288
|
-
when *PIPELINE_FORMS then render_pipeline(head_name, raw_args, indent)
|
|
193
|
+
when *CASE_HEADS then render_case_or_match(name, list, raw_args, indent)
|
|
194
|
+
when *Compiler::Language::PIPELINE_HEADS then render_pipeline(name, raw_args, indent)
|
|
289
195
|
else
|
|
290
196
|
render_call(list, indent)
|
|
291
197
|
end
|
|
292
198
|
end
|
|
293
199
|
|
|
200
|
+
def render_single_prefix_body_form(head, raw_args, indent)
|
|
201
|
+
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
202
|
+
render_prefix_body_form(head, raw_prefix, raw_body, indent)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def render_case_or_match(head, list, raw_args, indent)
|
|
206
|
+
return render_sequential_head_form(head, raw_args, indent) if contains_comments?(raw_args)
|
|
207
|
+
|
|
208
|
+
render_case(head, list_rest(list), indent)
|
|
209
|
+
end
|
|
210
|
+
|
|
294
211
|
def render_fn(head, list, indent, top_level: false)
|
|
295
212
|
args = list_rest(list)
|
|
296
213
|
raw_args = list_raw_rest(list)
|
|
297
|
-
prefix_length =
|
|
214
|
+
prefix_length = Compiler::Language.parse_function_args(args)&.prefix_length || 1
|
|
298
215
|
raw_prefix, raw_body = split_raw_items(raw_args, prefix_length)
|
|
299
216
|
force = top_level || fn_body_has_quasi_list?(raw_body)
|
|
300
217
|
render_prefix_body_form(head, raw_prefix, raw_body, indent, force_body_multiline: force)
|
|
@@ -312,8 +229,8 @@ module Kapusta
|
|
|
312
229
|
def render_class(list, indent)
|
|
313
230
|
args = list_rest(list)
|
|
314
231
|
raw_args = list_raw_rest(list)
|
|
315
|
-
|
|
316
|
-
raw_prefix, raw_body = split_raw_items(raw_args,
|
|
232
|
+
prefix_length = Compiler::Language.parse_class_args(args).prefix_length
|
|
233
|
+
raw_prefix, raw_body = split_raw_items(raw_args, prefix_length)
|
|
317
234
|
render_prefix_body_form('class', raw_prefix, raw_body, indent)
|
|
318
235
|
end
|
|
319
236
|
|
|
@@ -341,10 +258,10 @@ module Kapusta
|
|
|
341
258
|
end
|
|
342
259
|
|
|
343
260
|
def render_let(list, indent)
|
|
344
|
-
|
|
261
|
+
parsed = Compiler::Language.parse_let_args(list_rest(list))
|
|
262
|
+
bindings = parsed.bindings
|
|
345
263
|
raw_args = list_raw_rest(list)
|
|
346
264
|
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
347
|
-
body = list_rest(list).drop(1)
|
|
348
265
|
unless bindings.is_a?(Vec)
|
|
349
266
|
return render_prefix_body_form('let', raw_prefix, raw_body, indent,
|
|
350
267
|
layouts: [:pairwise])
|
|
@@ -358,7 +275,7 @@ module Kapusta
|
|
|
358
275
|
rendered_bindings = render_let_bindings(bindings, indent)
|
|
359
276
|
lines = rendered_bindings.lines(chomp: true)
|
|
360
277
|
lines[0] = "(let #{lines[0]}"
|
|
361
|
-
body.each do |form|
|
|
278
|
+
parsed.body.each do |form|
|
|
362
279
|
lines << indent_block(render(form, indent + INDENT), INDENT)
|
|
363
280
|
end
|
|
364
281
|
append_suffix(lines, ')')
|
|
@@ -406,52 +323,50 @@ module Kapusta
|
|
|
406
323
|
end
|
|
407
324
|
|
|
408
325
|
body_forms.each do |form|
|
|
409
|
-
|
|
410
|
-
lines << ''
|
|
411
|
-
next
|
|
412
|
-
end
|
|
413
|
-
if comment?(form)
|
|
414
|
-
lines << indent_block(render(form, indent + INDENT), INDENT)
|
|
415
|
-
next
|
|
416
|
-
end
|
|
417
|
-
|
|
418
|
-
body = render(
|
|
419
|
-
form,
|
|
420
|
-
indent + INDENT,
|
|
421
|
-
force_expand: force_body_multiline && force_multiline_body?(form)
|
|
422
|
-
)
|
|
423
|
-
lines << indent_block(body, INDENT)
|
|
326
|
+
append_body_form(lines, form, indent, force_body_multiline:)
|
|
424
327
|
end
|
|
425
328
|
|
|
426
329
|
append_suffix(lines, ')')
|
|
427
330
|
end
|
|
428
331
|
|
|
332
|
+
def append_body_form(lines, form, indent, force_body_multiline: false)
|
|
333
|
+
if blank_line?(form)
|
|
334
|
+
lines << ''
|
|
335
|
+
return
|
|
336
|
+
end
|
|
337
|
+
if comment?(form)
|
|
338
|
+
lines << indent_block(render(form, indent + INDENT), INDENT)
|
|
339
|
+
return
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
body = render(
|
|
343
|
+
form,
|
|
344
|
+
indent + INDENT,
|
|
345
|
+
force_expand: force_body_multiline && force_multiline_body?(form)
|
|
346
|
+
)
|
|
347
|
+
lines << indent_block(body, INDENT)
|
|
348
|
+
end
|
|
349
|
+
|
|
429
350
|
def render_if(list, indent)
|
|
430
351
|
args = list_rest(list)
|
|
431
352
|
return render_sequential_head_form('if', list_raw_rest(list), indent) if contains_comments?(list_raw_rest(list))
|
|
432
353
|
|
|
433
354
|
lines = []
|
|
434
|
-
hanging =
|
|
355
|
+
hanging = if_hanging
|
|
435
356
|
|
|
436
357
|
if args.length == 3
|
|
437
358
|
flat = flat_render(list)
|
|
438
359
|
return flat if inline_three_arg_if?(args) && flat && fits?(flat, indent)
|
|
439
360
|
|
|
440
|
-
lines
|
|
441
|
-
lines
|
|
442
|
-
lines
|
|
361
|
+
append_if_form(lines, args[0], indent, '(if ')
|
|
362
|
+
append_if_form(lines, args[1], indent, hanging)
|
|
363
|
+
append_if_form(lines, args[2], indent, hanging)
|
|
443
364
|
return append_suffix(lines, ')')
|
|
444
365
|
end
|
|
445
366
|
|
|
446
367
|
index = 0
|
|
447
368
|
if args.length >= 2
|
|
448
|
-
|
|
449
|
-
if first_pair
|
|
450
|
-
lines << "(if #{first_pair}"
|
|
451
|
-
else
|
|
452
|
-
lines << "(if #{render(args[0], indent + '(if '.length)}"
|
|
453
|
-
lines << prefix_continuation(hanging, render(args[1], indent + '(if '.length))
|
|
454
|
-
end
|
|
369
|
+
append_if_pair(lines, args[0], args[1], indent, '(if ')
|
|
455
370
|
index = 2
|
|
456
371
|
else
|
|
457
372
|
lines << '(if'
|
|
@@ -460,16 +375,10 @@ module Kapusta
|
|
|
460
375
|
while index < args.length
|
|
461
376
|
remaining = args.length - index
|
|
462
377
|
if remaining >= 2
|
|
463
|
-
|
|
464
|
-
if pair
|
|
465
|
-
lines << "#{hanging}#{pair}"
|
|
466
|
-
else
|
|
467
|
-
lines << prefix_continuation(hanging, render(args[index], indent + '(if '.length))
|
|
468
|
-
lines << prefix_continuation(hanging, render(args[index + 1], indent + '(if '.length))
|
|
469
|
-
end
|
|
378
|
+
append_if_pair(lines, args[index], args[index + 1], indent, hanging)
|
|
470
379
|
index += 2
|
|
471
380
|
else
|
|
472
|
-
lines
|
|
381
|
+
append_if_form(lines, args[index], indent, hanging)
|
|
473
382
|
index += 1
|
|
474
383
|
end
|
|
475
384
|
end
|
|
@@ -477,19 +386,44 @@ module Kapusta
|
|
|
477
386
|
append_suffix(lines, ')')
|
|
478
387
|
end
|
|
479
388
|
|
|
389
|
+
def append_if_pair(lines, condition, branch, indent, prefix)
|
|
390
|
+
pair = render_pair(condition, branch, if_value_indent(indent))
|
|
391
|
+
if pair
|
|
392
|
+
lines << "#{prefix}#{pair}"
|
|
393
|
+
else
|
|
394
|
+
append_if_form(lines, condition, indent, prefix)
|
|
395
|
+
append_if_form(lines, branch, indent, if_hanging)
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def append_if_form(lines, form, indent, prefix)
|
|
400
|
+
lines << prefix_continuation(prefix, render(form, if_value_indent(indent)))
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def if_value_indent(indent)
|
|
404
|
+
indent + '(if '.length
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def if_hanging
|
|
408
|
+
' ' * '(if '.length
|
|
409
|
+
end
|
|
410
|
+
|
|
480
411
|
def prefix_continuation(prefix, rendered)
|
|
481
|
-
|
|
412
|
+
prefix_lines(prefix, rendered.lines(chomp: true)).join("\n")
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def prefix_lines(prefix, lines)
|
|
416
|
+
first_line, *rest = lines
|
|
482
417
|
pad = ' ' * prefix.length
|
|
483
|
-
["#{prefix}#{first_line}", *rest.map { |line| "#{pad}#{line}" }]
|
|
418
|
+
["#{prefix}#{first_line}", *rest.map { |line| line.empty? ? '' : "#{pad}#{line}" }]
|
|
484
419
|
end
|
|
485
420
|
|
|
486
421
|
def render_case(head, args, indent)
|
|
487
|
-
|
|
488
|
-
clauses = args.drop(1)
|
|
422
|
+
parsed = Compiler::Language.parse_case_args(args)
|
|
489
423
|
lines = ['(case']
|
|
490
424
|
|
|
491
|
-
if subject
|
|
492
|
-
rendered_subject = render(subject, indent + INDENT)
|
|
425
|
+
if parsed.subject
|
|
426
|
+
rendered_subject = render(parsed.subject, indent + INDENT)
|
|
493
427
|
if single_line?(rendered_subject) && fits?("(#{head} #{rendered_subject}", indent)
|
|
494
428
|
lines[0] = "(#{head} #{rendered_subject}"
|
|
495
429
|
else
|
|
@@ -498,24 +432,27 @@ module Kapusta
|
|
|
498
432
|
end
|
|
499
433
|
end
|
|
500
434
|
|
|
501
|
-
|
|
502
|
-
pattern, value = pair
|
|
503
|
-
if pair.length == 2
|
|
504
|
-
pair = render_pair(pattern, value, indent + INDENT)
|
|
505
|
-
if pair
|
|
506
|
-
lines << indent_block(pair, INDENT)
|
|
507
|
-
else
|
|
508
|
-
lines << indent_block(render(pattern, indent + INDENT), INDENT)
|
|
509
|
-
lines << indent_block(render(value, indent + INDENT), INDENT)
|
|
510
|
-
end
|
|
511
|
-
else
|
|
512
|
-
lines << indent_block(render(pattern, indent + INDENT), INDENT)
|
|
513
|
-
end
|
|
514
|
-
end
|
|
435
|
+
parsed.arm_pairs.each { |arm| append_case_arm(lines, arm, indent) }
|
|
515
436
|
|
|
516
437
|
append_suffix(lines, ')')
|
|
517
438
|
end
|
|
518
439
|
|
|
440
|
+
def append_case_arm(lines, arm, indent)
|
|
441
|
+
pattern, value = arm
|
|
442
|
+
unless arm.length == 2
|
|
443
|
+
lines << indent_block(render(pattern, indent + INDENT), INDENT)
|
|
444
|
+
return
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
rendered_pair = render_pair(pattern, value, indent + INDENT)
|
|
448
|
+
if rendered_pair
|
|
449
|
+
lines << indent_block(rendered_pair, INDENT)
|
|
450
|
+
else
|
|
451
|
+
lines << indent_block(render(pattern, indent + INDENT), INDENT)
|
|
452
|
+
lines << indent_block(render(value, indent + INDENT), INDENT)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
519
456
|
def render_pipeline(head, args, indent)
|
|
520
457
|
base = "(#{head}"
|
|
521
458
|
lines = [base]
|
|
@@ -547,7 +484,7 @@ module Kapusta
|
|
|
547
484
|
append_suffix(lines, ')')
|
|
548
485
|
end
|
|
549
486
|
|
|
550
|
-
def render_call(list, indent)
|
|
487
|
+
def render_call(list, indent, force_hang: false)
|
|
551
488
|
head = flat_render(list_head(list))
|
|
552
489
|
raise Error, "cannot format form head: #{list_head(list).inspect}" unless head
|
|
553
490
|
|
|
@@ -555,7 +492,7 @@ module Kapusta
|
|
|
555
492
|
lines = [base]
|
|
556
493
|
args = list_raw_rest(list)
|
|
557
494
|
semantic_length = semantic_items(args).length
|
|
558
|
-
hang_subsequent_args = hang_call_args?(list, indent)
|
|
495
|
+
hang_subsequent_args = force_hang || hang_call_args?(list, base, indent)
|
|
559
496
|
|
|
560
497
|
semantic_index = 0
|
|
561
498
|
hanging = nil
|
|
@@ -565,6 +502,8 @@ module Kapusta
|
|
|
565
502
|
next
|
|
566
503
|
elsif semantic_index.zero?
|
|
567
504
|
hanging = append_first_call_arg(lines, arg, base, indent, semantic_length)
|
|
505
|
+
elsif append_packed_call_arg?(list, lines, arg, indent)
|
|
506
|
+
nil
|
|
568
507
|
elsif hanging && hang_subsequent_args
|
|
569
508
|
lines << prefix_continuation(hanging, render(arg, indent + hanging.length))
|
|
570
509
|
else
|
|
@@ -597,33 +536,172 @@ module Kapusta
|
|
|
597
536
|
hanging
|
|
598
537
|
end
|
|
599
538
|
|
|
600
|
-
def
|
|
601
|
-
return false unless
|
|
539
|
+
def append_packed_call_arg?(list, lines, arg, indent)
|
|
540
|
+
return false unless packable_call_arg?(list, arg)
|
|
541
|
+
|
|
542
|
+
packed = packed_call_arg(lines.last, list, arg, indent)
|
|
543
|
+
if packed
|
|
544
|
+
lines[-1] = packed.first
|
|
545
|
+
lines.concat(packed.drop(1))
|
|
546
|
+
return true
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
false
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def packable_call_arg?(list, arg)
|
|
553
|
+
return false if fn_form?(arg)
|
|
554
|
+
return true if packable_collection_call_arg?(list, arg)
|
|
555
|
+
|
|
556
|
+
pack_call_args?(list) && flat_render(arg)
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def packable_collection_call_arg?(list, arg)
|
|
560
|
+
return true if arg.is_a?(Vec)
|
|
561
|
+
|
|
562
|
+
arg.is_a?(HashLit) && packable_hash_call_arg?(list)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def packed_call_arg(current_line, list, arg, indent)
|
|
566
|
+
packable_call_arg_renderings(list, arg, indent + current_line.length + 1).each do |rendered|
|
|
567
|
+
first_line, *rest = rendered.lines(chomp: true)
|
|
568
|
+
candidate = "#{current_line} #{first_line}"
|
|
569
|
+
next unless inline_arg_fits?(candidate, indent)
|
|
570
|
+
|
|
571
|
+
hanging = ' ' * (current_line.length + 1)
|
|
572
|
+
return [candidate, *rest.map { |line| "#{hanging}#{line}" }]
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
nil
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def packable_call_arg_renderings(list, arg, indent)
|
|
579
|
+
[
|
|
580
|
+
flat_collection_render(arg),
|
|
581
|
+
flat_render(arg),
|
|
582
|
+
multiline_packable_call_arg_rendering(list, arg, indent)
|
|
583
|
+
].compact.uniq
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def multiline_packable_call_arg_rendering(list, arg, indent)
|
|
587
|
+
return render(arg, indent) if arg.is_a?(Vec)
|
|
588
|
+
return render(arg, indent) if local_hash_call_arg?(list, arg)
|
|
589
|
+
|
|
590
|
+
nil
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
def local_hash_call_arg?(list, arg)
|
|
594
|
+
arg.is_a?(HashLit) && head_name(list) == 'local'
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def flat_collection_render(form)
|
|
598
|
+
case form
|
|
599
|
+
when Vec
|
|
600
|
+
flat_delimited_render(form.items, '[', ']') { |item| flat_render(item) }
|
|
601
|
+
when HashLit
|
|
602
|
+
return if contains_comments?(form.entries)
|
|
603
|
+
|
|
604
|
+
flat_hash_render(form.pairs)
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def flat_delimited_render(items, open, close)
|
|
609
|
+
return if contains_comments?(items)
|
|
610
|
+
|
|
611
|
+
rendered = items.map do |item|
|
|
612
|
+
item.is_a?(Array) ? yield(*item) : yield(item)
|
|
613
|
+
end
|
|
614
|
+
return if rendered.any?(&:nil?)
|
|
615
|
+
|
|
616
|
+
"#{open}#{rendered.join(' ')}#{close}"
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def flat_hash_render(pairs)
|
|
620
|
+
rendered = pairs.map { |pair| flat_hash_pair(pair) }
|
|
621
|
+
return if rendered.any?(&:nil?)
|
|
622
|
+
|
|
623
|
+
"{#{rendered.join(' ')}}"
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def hang_call_args?(list, base, indent)
|
|
627
|
+
return true if source_hangs_call_args?(list, base)
|
|
628
|
+
return true if set_function_value?(list)
|
|
602
629
|
|
|
603
630
|
flat = flat_call_render(list)
|
|
604
|
-
|
|
631
|
+
return false unless flat
|
|
632
|
+
|
|
633
|
+
overflowing = !fits?(flat, indent)
|
|
634
|
+
return true if overflowing && hanging_overflow_call?(list)
|
|
635
|
+
|
|
636
|
+
operator_call?(list) && overflowing
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def pack_call_args?(list)
|
|
640
|
+
head_name(list)&.match?(/\A[a-z0-9_-][\w-]*\./)
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def packable_hash_call_arg?(list)
|
|
644
|
+
head_name(list) == 'local' || pack_call_args?(list)
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def hanging_overflow_call?(list)
|
|
648
|
+
hash_first_call_arg?(list) || pack_call_args?(list)
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
def hash_first_call_arg?(list)
|
|
652
|
+
list_rest(list).first.is_a?(HashLit)
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
def set_function_value?(list)
|
|
656
|
+
head_name(list) == 'set' && fn_form?(list_rest(list)[1])
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def source_hangs_call_args?(list, base)
|
|
660
|
+
return false unless list.respond_to?(:column) && list.column
|
|
661
|
+
|
|
662
|
+
args = list_rest(list)
|
|
663
|
+
return false if args.length < 2
|
|
664
|
+
|
|
665
|
+
expected_column = list.column + base.length + 1
|
|
666
|
+
args.drop(1).all? do |arg|
|
|
667
|
+
arg.respond_to?(:column) && arg.column == expected_column
|
|
668
|
+
end
|
|
605
669
|
end
|
|
606
670
|
|
|
607
671
|
def operator_call?(list)
|
|
608
|
-
|
|
609
|
-
|
|
672
|
+
head_name(list)&.match?(/\A[^\w.]+\z/)
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
def ordinary_call_form?(list)
|
|
676
|
+
name = head_name(list)
|
|
677
|
+
name && !Compiler::Language::SPECIAL_FORMS.include?(name)
|
|
610
678
|
end
|
|
611
679
|
|
|
612
680
|
def flat_call_render(list)
|
|
613
681
|
head = flat_render(list_head(list))
|
|
614
682
|
return unless head
|
|
615
683
|
|
|
616
|
-
rendered_args = semantic_items(list_raw_rest(list)).map { |arg|
|
|
684
|
+
rendered_args = semantic_items(list_raw_rest(list)).map { |arg| flat_call_arg_render(arg) }
|
|
617
685
|
return if rendered_args.any?(&:nil?)
|
|
618
686
|
|
|
619
687
|
"(#{[head, *rendered_args].join(' ')})"
|
|
620
688
|
end
|
|
621
689
|
|
|
690
|
+
def flat_call_arg_render(arg)
|
|
691
|
+
collection = flat_collection_render(arg)
|
|
692
|
+
return collection if collection
|
|
693
|
+
|
|
694
|
+
flat_render(arg)
|
|
695
|
+
end
|
|
696
|
+
|
|
622
697
|
def render_vec(vec, indent, layout: nil, top_level: false, force_expand: false)
|
|
623
698
|
flat = flat_render(vec)
|
|
624
699
|
return flat if !force_expand && flat && fits?(flat, indent) && allow_flat?(vec, top_level:, layout:)
|
|
625
700
|
|
|
626
701
|
return render_pairwise_vec(vec, indent) if layout == :pairwise && !contains_comments?(vec.items)
|
|
702
|
+
if multiline_in_source?(vec) && multiline_vec_items_should_separate?(vec) && !contains_comments?(vec.items)
|
|
703
|
+
return render_multiline_vec(vec, indent)
|
|
704
|
+
end
|
|
627
705
|
return render_filled_vec(vec, indent) if !contains_comments?(vec.items) && !vec.items.empty?
|
|
628
706
|
|
|
629
707
|
lines = ['[']
|
|
@@ -633,6 +711,42 @@ module Kapusta
|
|
|
633
711
|
append_suffix(lines, ']')
|
|
634
712
|
end
|
|
635
713
|
|
|
714
|
+
def render_multiline_vec(vec, indent)
|
|
715
|
+
return '[]' if vec.items.empty?
|
|
716
|
+
|
|
717
|
+
lines = []
|
|
718
|
+
vec.items.each_with_index do |item, idx|
|
|
719
|
+
prefix = idx.zero? ? '[' : ' '
|
|
720
|
+
lines.concat(prefix_lines(prefix, render_multiline_vec_item(item, indent + 1).lines.map(&:chomp)))
|
|
721
|
+
end
|
|
722
|
+
lines[-1] = "#{lines[-1]}]"
|
|
723
|
+
lines.join("\n")
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def render_multiline_vec_item(item, indent)
|
|
727
|
+
return render_call(item, indent, force_hang: true) if hanging_multiline_vec_call_item?(item)
|
|
728
|
+
|
|
729
|
+
render(item, indent)
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
def hanging_multiline_vec_call_item?(item)
|
|
733
|
+
item.is_a?(List) && multiline_in_source?(item) && ordinary_call_form?(item)
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
def multiline_vec_items_should_separate?(vec)
|
|
737
|
+
multiline_vec_items_on_separate_lines?(vec) || semantic_items(vec.items).all? { |item| collection?(item) }
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
def multiline_vec_items_on_separate_lines?(vec)
|
|
741
|
+
items = semantic_items(vec.items)
|
|
742
|
+
return false if items.length < 2
|
|
743
|
+
|
|
744
|
+
lines = items.filter_map { |item| item.line if item.respond_to?(:line) }
|
|
745
|
+
return true if lines.length < 2
|
|
746
|
+
|
|
747
|
+
lines.uniq.length == lines.length
|
|
748
|
+
end
|
|
749
|
+
|
|
636
750
|
def render_filled_vec(vec, indent)
|
|
637
751
|
output_lines = ['[']
|
|
638
752
|
|
|
@@ -688,16 +802,16 @@ module Kapusta
|
|
|
688
802
|
def render_let_bindings(bindings, indent)
|
|
689
803
|
return render(bindings, indent + '(let '.length, force_expand: true) if contains_comments?(bindings.items)
|
|
690
804
|
|
|
691
|
-
hanging = render_hanging_pairwise_vec(bindings)
|
|
805
|
+
hanging = render_hanging_pairwise_vec(bindings, indent)
|
|
692
806
|
hanging || render(bindings, indent + '(let '.length, layout: :pairwise)
|
|
693
807
|
end
|
|
694
808
|
|
|
695
|
-
def render_hanging_pairwise_vec(vec)
|
|
809
|
+
def render_hanging_pairwise_vec(vec, indent)
|
|
696
810
|
pairs = vec.items.each_slice(2).to_a
|
|
697
811
|
return unless pairs.all? { |pair| pair.length == 2 }
|
|
698
812
|
|
|
699
813
|
rendered_pairs = pairs.map do |left, right|
|
|
700
|
-
render_binding_pair(left, right)
|
|
814
|
+
render_binding_pair(left, right, indent)
|
|
701
815
|
end
|
|
702
816
|
return if rendered_pairs.any?(&:nil?)
|
|
703
817
|
|
|
@@ -725,7 +839,7 @@ module Kapusta
|
|
|
725
839
|
|
|
726
840
|
key, value = entry
|
|
727
841
|
first_pair = output_lines.empty?
|
|
728
|
-
if
|
|
842
|
+
if hash_pair_shorthand?(entry)
|
|
729
843
|
output_lines << "#{first_pair ? '{' : ' '}: #{value.name}"
|
|
730
844
|
next
|
|
731
845
|
end
|
|
@@ -745,8 +859,9 @@ module Kapusta
|
|
|
745
859
|
output_lines.join("\n")
|
|
746
860
|
end
|
|
747
861
|
|
|
748
|
-
def flat_hash_pair(
|
|
749
|
-
|
|
862
|
+
def flat_hash_pair(pair)
|
|
863
|
+
key, value = pair
|
|
864
|
+
return ": #{value.name}" if hash_pair_shorthand?(pair)
|
|
750
865
|
|
|
751
866
|
rendered_value = flat_render(value)
|
|
752
867
|
return unless rendered_value
|
|
@@ -772,14 +887,15 @@ module Kapusta
|
|
|
772
887
|
fits?(pair, indent) ? pair : nil
|
|
773
888
|
end
|
|
774
889
|
|
|
775
|
-
def render_binding_pair(left, right)
|
|
890
|
+
def render_binding_pair(left, right, indent)
|
|
776
891
|
left_rendered = flat_render(left)
|
|
777
892
|
return unless left_rendered
|
|
778
893
|
|
|
779
|
-
|
|
894
|
+
right_indent = indent + '(let ['.length + left_rendered.length + 1
|
|
895
|
+
right_rendered = render(right, right_indent)
|
|
780
896
|
first_line, *rest = right_rendered.lines(chomp: true)
|
|
781
897
|
pair = "#{left_rendered} #{first_line}"
|
|
782
|
-
return unless pair.length
|
|
898
|
+
return unless fits?(pair, indent + '(let ['.length)
|
|
783
899
|
|
|
784
900
|
return pair if rest.empty?
|
|
785
901
|
|
|
@@ -787,8 +903,8 @@ module Kapusta
|
|
|
787
903
|
([pair] + rest.map { |line| "#{continuation}#{line}" }).join("\n")
|
|
788
904
|
end
|
|
789
905
|
|
|
790
|
-
def
|
|
791
|
-
|
|
906
|
+
def hash_pair_shorthand?(pair)
|
|
907
|
+
pair.respond_to?(:shorthand?) && pair.shorthand?
|
|
792
908
|
end
|
|
793
909
|
|
|
794
910
|
def hashfn_literal?(form)
|
|
@@ -809,11 +925,10 @@ module Kapusta
|
|
|
809
925
|
return true unless head.is_a?(Sym)
|
|
810
926
|
|
|
811
927
|
case head.name
|
|
812
|
-
when
|
|
813
|
-
'accumulate', 'faccumulate'
|
|
928
|
+
when *Compiler::Language::FLAT_BODY_HEADS
|
|
814
929
|
!top_level
|
|
815
930
|
else
|
|
816
|
-
|
|
931
|
+
!Compiler::Language.never_flat_head?(head.name)
|
|
817
932
|
end
|
|
818
933
|
end
|
|
819
934
|
|
|
@@ -826,18 +941,13 @@ module Kapusta
|
|
|
826
941
|
return false unless head.is_a?(Sym)
|
|
827
942
|
|
|
828
943
|
case head.name
|
|
829
|
-
when
|
|
830
|
-
'fn', 'lambda', 'λ', 'macro'
|
|
944
|
+
when *Compiler::Language::MULTILINE_BODY_HEADS
|
|
831
945
|
true
|
|
832
946
|
else
|
|
833
947
|
false
|
|
834
948
|
end
|
|
835
949
|
end
|
|
836
950
|
|
|
837
|
-
def multiline_in_source?(form)
|
|
838
|
-
form.respond_to?(:multiline_source) && form.multiline_source
|
|
839
|
-
end
|
|
840
|
-
|
|
841
951
|
def let_with_multiple_bindings?(form)
|
|
842
952
|
head = list_head(form)
|
|
843
953
|
return false unless head.is_a?(Sym) && head.name == 'let'
|
|
@@ -860,25 +970,12 @@ module Kapusta
|
|
|
860
970
|
end
|
|
861
971
|
end
|
|
862
972
|
|
|
863
|
-
def contains_collection?(form)
|
|
864
|
-
case form
|
|
865
|
-
when List then semantic_items(form.items).any? { |item| collection?(item) }
|
|
866
|
-
when Vec then form.items.any? { |item| collection?(item) }
|
|
867
|
-
when HashLit then form.pairs.any? { |k, v| collection?(k) || collection?(v) }
|
|
868
|
-
else false
|
|
869
|
-
end
|
|
870
|
-
end
|
|
871
|
-
|
|
872
|
-
def collection?(form)
|
|
873
|
-
form.is_a?(List) || form.is_a?(Vec) || form.is_a?(HashLit)
|
|
874
|
-
end
|
|
875
|
-
|
|
876
973
|
def fn_form?(form)
|
|
877
974
|
return false unless form.is_a?(List)
|
|
878
975
|
|
|
879
976
|
head = list_head(form)
|
|
880
977
|
head.is_a?(Sym) &&
|
|
881
|
-
|
|
978
|
+
Compiler::Language.function_head?(head.name)
|
|
882
979
|
end
|
|
883
980
|
|
|
884
981
|
def inline_three_arg_if?(args)
|
|
@@ -897,33 +994,6 @@ module Kapusta
|
|
|
897
994
|
end
|
|
898
995
|
end
|
|
899
996
|
|
|
900
|
-
def stdin_path?(path)
|
|
901
|
-
path == STDIN_PATH
|
|
902
|
-
end
|
|
903
|
-
|
|
904
|
-
def fits?(text, indent)
|
|
905
|
-
!text.include?("\n") && indent + text.length <= MAX_WIDTH
|
|
906
|
-
end
|
|
907
|
-
|
|
908
|
-
def single_line?(text)
|
|
909
|
-
!text.include?("\n")
|
|
910
|
-
end
|
|
911
|
-
|
|
912
|
-
def indent_block(text, amount)
|
|
913
|
-
prefix = ' ' * amount
|
|
914
|
-
text.lines.map { |line| "#{prefix}#{line}" }.join
|
|
915
|
-
end
|
|
916
|
-
|
|
917
|
-
def append_suffix(lines, suffix)
|
|
918
|
-
updated = lines.dup
|
|
919
|
-
if updated[-1].lstrip.start_with?(';')
|
|
920
|
-
updated << suffix
|
|
921
|
-
else
|
|
922
|
-
updated[-1] = "#{updated[-1]}#{suffix}"
|
|
923
|
-
end
|
|
924
|
-
updated.join("\n")
|
|
925
|
-
end
|
|
926
|
-
|
|
927
997
|
def render_generic_list(list, indent)
|
|
928
998
|
lines = ['(']
|
|
929
999
|
list.items.each do |item|
|
|
@@ -959,47 +1029,6 @@ module Kapusta
|
|
|
959
1029
|
append_suffix(lines, ')')
|
|
960
1030
|
end
|
|
961
1031
|
|
|
962
|
-
def contains_comments?(items)
|
|
963
|
-
items.any? { |item| non_semantic?(item) }
|
|
964
|
-
end
|
|
965
|
-
|
|
966
|
-
def semantic_items(items)
|
|
967
|
-
items.reject { |item| non_semantic?(item) }
|
|
968
|
-
end
|
|
969
|
-
|
|
970
|
-
def list_head(list)
|
|
971
|
-
semantic_items(list.items).first
|
|
972
|
-
end
|
|
973
|
-
|
|
974
|
-
def list_rest(list)
|
|
975
|
-
semantic_items(list.items).drop(1)
|
|
976
|
-
end
|
|
977
|
-
|
|
978
|
-
def list_raw_rest(list)
|
|
979
|
-
index = list.items.index { |item| !comment?(item) }
|
|
980
|
-
return list.items if index.nil?
|
|
981
|
-
|
|
982
|
-
list.items[(index + 1)..] || []
|
|
983
|
-
end
|
|
984
|
-
|
|
985
|
-
def split_raw_items(items, semantic_count)
|
|
986
|
-
split_index = 0
|
|
987
|
-
seen = 0
|
|
988
|
-
|
|
989
|
-
while split_index < items.length && seen < semantic_count
|
|
990
|
-
seen += 1 unless comment?(items[split_index])
|
|
991
|
-
split_index += 1
|
|
992
|
-
end
|
|
993
|
-
|
|
994
|
-
[items.take(split_index), items.drop(split_index)]
|
|
995
|
-
end
|
|
996
|
-
|
|
997
|
-
def print_help
|
|
998
|
-
puts 'Usage: kapfmt [--fix] [--check] FILENAME...'
|
|
999
|
-
puts
|
|
1000
|
-
puts 'Formats Kapusta source using the built-in Kapusta reader and pretty-printer.'
|
|
1001
|
-
end
|
|
1002
|
-
|
|
1003
1032
|
class Error < Kapusta::Error; end
|
|
1004
1033
|
end
|
|
1005
1034
|
end
|