kapusta 0.13.2 → 0.15.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/digit-tools.kap +24 -0
- data/examples/doto-hygiene.kap +2 -2
- data/examples/doto.kap +2 -2
- data/examples/egg-count.kap +1 -1
- data/examples/excel-column.kap +26 -0
- 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/logger-rate-limiter.kap +26 -0
- 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 +10 -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 +172 -48
- data/lib/kapusta/compiler/emitter/patterns.rb +12 -14
- data/lib/kapusta/compiler/emitter/support.rb +70 -81
- data/lib/kapusta/compiler/language.rb +533 -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 +363 -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 +99 -15
- data/spec/formatter_spec.rb +246 -0
- data/spec/lsp_spec.rb +69 -0
- data/spec/require_spec.rb +294 -0
- metadata +23 -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
|
|
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,78 @@ 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)
|
|
191
|
+
when 'module' then render_module(list, indent)
|
|
280
192
|
when 'catch' then render_catch(list, indent)
|
|
281
193
|
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)
|
|
194
|
+
when *CASE_HEADS then render_case_or_match(name, list, raw_args, indent)
|
|
195
|
+
when *Compiler::Language::PIPELINE_HEADS then render_pipeline(name, raw_args, indent)
|
|
289
196
|
else
|
|
290
197
|
render_call(list, indent)
|
|
291
198
|
end
|
|
292
199
|
end
|
|
293
200
|
|
|
201
|
+
def render_single_prefix_body_form(head, raw_args, indent)
|
|
202
|
+
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
203
|
+
render_prefix_body_form(head, raw_prefix, raw_body, indent)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def render_case_or_match(head, list, raw_args, indent)
|
|
207
|
+
return render_sequential_head_form(head, raw_args, indent) if contains_comments?(raw_args)
|
|
208
|
+
|
|
209
|
+
render_case(head, list_rest(list), indent)
|
|
210
|
+
end
|
|
211
|
+
|
|
294
212
|
def render_fn(head, list, indent, top_level: false)
|
|
295
213
|
args = list_rest(list)
|
|
296
214
|
raw_args = list_raw_rest(list)
|
|
297
|
-
prefix_length =
|
|
215
|
+
prefix_length = Compiler::Language.parse_function_args(args)&.prefix_length || 1
|
|
298
216
|
raw_prefix, raw_body = split_raw_items(raw_args, prefix_length)
|
|
299
217
|
force = top_level || fn_body_has_quasi_list?(raw_body)
|
|
300
218
|
render_prefix_body_form(head, raw_prefix, raw_body, indent, force_body_multiline: force)
|
|
@@ -312,11 +230,19 @@ module Kapusta
|
|
|
312
230
|
def render_class(list, indent)
|
|
313
231
|
args = list_rest(list)
|
|
314
232
|
raw_args = list_raw_rest(list)
|
|
315
|
-
|
|
316
|
-
raw_prefix, raw_body = split_raw_items(raw_args,
|
|
233
|
+
prefix_length = Compiler::Language.parse_class_args(args).prefix_length
|
|
234
|
+
raw_prefix, raw_body = split_raw_items(raw_args, prefix_length)
|
|
317
235
|
render_prefix_body_form('class', raw_prefix, raw_body, indent)
|
|
318
236
|
end
|
|
319
237
|
|
|
238
|
+
def render_module(list, indent)
|
|
239
|
+
args = list_rest(list)
|
|
240
|
+
raw_args = list_raw_rest(list)
|
|
241
|
+
prefix_length = Compiler::Language.parse_module_args(args).prefix_length
|
|
242
|
+
raw_prefix, raw_body = split_raw_items(raw_args, prefix_length)
|
|
243
|
+
render_prefix_body_form('module', raw_prefix, raw_body, indent)
|
|
244
|
+
end
|
|
245
|
+
|
|
320
246
|
def render_try(list, indent)
|
|
321
247
|
args = list_rest(list)
|
|
322
248
|
return render_sequential_head_form('try', list_raw_rest(list), indent) if contains_comments?(list_raw_rest(list))
|
|
@@ -341,10 +267,10 @@ module Kapusta
|
|
|
341
267
|
end
|
|
342
268
|
|
|
343
269
|
def render_let(list, indent)
|
|
344
|
-
|
|
270
|
+
parsed = Compiler::Language.parse_let_args(list_rest(list))
|
|
271
|
+
bindings = parsed.bindings
|
|
345
272
|
raw_args = list_raw_rest(list)
|
|
346
273
|
raw_prefix, raw_body = split_raw_items(raw_args, 1)
|
|
347
|
-
body = list_rest(list).drop(1)
|
|
348
274
|
unless bindings.is_a?(Vec)
|
|
349
275
|
return render_prefix_body_form('let', raw_prefix, raw_body, indent,
|
|
350
276
|
layouts: [:pairwise])
|
|
@@ -358,7 +284,7 @@ module Kapusta
|
|
|
358
284
|
rendered_bindings = render_let_bindings(bindings, indent)
|
|
359
285
|
lines = rendered_bindings.lines(chomp: true)
|
|
360
286
|
lines[0] = "(let #{lines[0]}"
|
|
361
|
-
body.each do |form|
|
|
287
|
+
parsed.body.each do |form|
|
|
362
288
|
lines << indent_block(render(form, indent + INDENT), INDENT)
|
|
363
289
|
end
|
|
364
290
|
append_suffix(lines, ')')
|
|
@@ -406,52 +332,50 @@ module Kapusta
|
|
|
406
332
|
end
|
|
407
333
|
|
|
408
334
|
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)
|
|
335
|
+
append_body_form(lines, form, indent, force_body_multiline:)
|
|
424
336
|
end
|
|
425
337
|
|
|
426
338
|
append_suffix(lines, ')')
|
|
427
339
|
end
|
|
428
340
|
|
|
341
|
+
def append_body_form(lines, form, indent, force_body_multiline: false)
|
|
342
|
+
if blank_line?(form)
|
|
343
|
+
lines << ''
|
|
344
|
+
return
|
|
345
|
+
end
|
|
346
|
+
if comment?(form)
|
|
347
|
+
lines << indent_block(render(form, indent + INDENT), INDENT)
|
|
348
|
+
return
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
body = render(
|
|
352
|
+
form,
|
|
353
|
+
indent + INDENT,
|
|
354
|
+
force_expand: force_body_multiline && force_multiline_body?(form)
|
|
355
|
+
)
|
|
356
|
+
lines << indent_block(body, INDENT)
|
|
357
|
+
end
|
|
358
|
+
|
|
429
359
|
def render_if(list, indent)
|
|
430
360
|
args = list_rest(list)
|
|
431
361
|
return render_sequential_head_form('if', list_raw_rest(list), indent) if contains_comments?(list_raw_rest(list))
|
|
432
362
|
|
|
433
363
|
lines = []
|
|
434
|
-
hanging =
|
|
364
|
+
hanging = if_hanging
|
|
435
365
|
|
|
436
366
|
if args.length == 3
|
|
437
367
|
flat = flat_render(list)
|
|
438
368
|
return flat if inline_three_arg_if?(args) && flat && fits?(flat, indent)
|
|
439
369
|
|
|
440
|
-
lines
|
|
441
|
-
lines
|
|
442
|
-
lines
|
|
370
|
+
append_if_form(lines, args[0], indent, '(if ')
|
|
371
|
+
append_if_form(lines, args[1], indent, hanging)
|
|
372
|
+
append_if_form(lines, args[2], indent, hanging)
|
|
443
373
|
return append_suffix(lines, ')')
|
|
444
374
|
end
|
|
445
375
|
|
|
446
376
|
index = 0
|
|
447
377
|
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
|
|
378
|
+
append_if_pair(lines, args[0], args[1], indent, '(if ')
|
|
455
379
|
index = 2
|
|
456
380
|
else
|
|
457
381
|
lines << '(if'
|
|
@@ -460,16 +384,10 @@ module Kapusta
|
|
|
460
384
|
while index < args.length
|
|
461
385
|
remaining = args.length - index
|
|
462
386
|
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
|
|
387
|
+
append_if_pair(lines, args[index], args[index + 1], indent, hanging)
|
|
470
388
|
index += 2
|
|
471
389
|
else
|
|
472
|
-
lines
|
|
390
|
+
append_if_form(lines, args[index], indent, hanging)
|
|
473
391
|
index += 1
|
|
474
392
|
end
|
|
475
393
|
end
|
|
@@ -477,19 +395,44 @@ module Kapusta
|
|
|
477
395
|
append_suffix(lines, ')')
|
|
478
396
|
end
|
|
479
397
|
|
|
398
|
+
def append_if_pair(lines, condition, branch, indent, prefix)
|
|
399
|
+
pair = render_pair(condition, branch, if_value_indent(indent))
|
|
400
|
+
if pair
|
|
401
|
+
lines << "#{prefix}#{pair}"
|
|
402
|
+
else
|
|
403
|
+
append_if_form(lines, condition, indent, prefix)
|
|
404
|
+
append_if_form(lines, branch, indent, if_hanging)
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def append_if_form(lines, form, indent, prefix)
|
|
409
|
+
lines << prefix_continuation(prefix, render(form, if_value_indent(indent)))
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def if_value_indent(indent)
|
|
413
|
+
indent + '(if '.length
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def if_hanging
|
|
417
|
+
' ' * '(if '.length
|
|
418
|
+
end
|
|
419
|
+
|
|
480
420
|
def prefix_continuation(prefix, rendered)
|
|
481
|
-
|
|
421
|
+
prefix_lines(prefix, rendered.lines(chomp: true)).join("\n")
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def prefix_lines(prefix, lines)
|
|
425
|
+
first_line, *rest = lines
|
|
482
426
|
pad = ' ' * prefix.length
|
|
483
|
-
["#{prefix}#{first_line}", *rest.map { |line| "#{pad}#{line}" }]
|
|
427
|
+
["#{prefix}#{first_line}", *rest.map { |line| line.empty? ? '' : "#{pad}#{line}" }]
|
|
484
428
|
end
|
|
485
429
|
|
|
486
430
|
def render_case(head, args, indent)
|
|
487
|
-
|
|
488
|
-
clauses = args.drop(1)
|
|
431
|
+
parsed = Compiler::Language.parse_case_args(args)
|
|
489
432
|
lines = ['(case']
|
|
490
433
|
|
|
491
|
-
if subject
|
|
492
|
-
rendered_subject = render(subject, indent + INDENT)
|
|
434
|
+
if parsed.subject
|
|
435
|
+
rendered_subject = render(parsed.subject, indent + INDENT)
|
|
493
436
|
if single_line?(rendered_subject) && fits?("(#{head} #{rendered_subject}", indent)
|
|
494
437
|
lines[0] = "(#{head} #{rendered_subject}"
|
|
495
438
|
else
|
|
@@ -498,24 +441,27 @@ module Kapusta
|
|
|
498
441
|
end
|
|
499
442
|
end
|
|
500
443
|
|
|
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
|
|
444
|
+
parsed.arm_pairs.each { |arm| append_case_arm(lines, arm, indent) }
|
|
515
445
|
|
|
516
446
|
append_suffix(lines, ')')
|
|
517
447
|
end
|
|
518
448
|
|
|
449
|
+
def append_case_arm(lines, arm, indent)
|
|
450
|
+
pattern, value = arm
|
|
451
|
+
unless arm.length == 2
|
|
452
|
+
lines << indent_block(render(pattern, indent + INDENT), INDENT)
|
|
453
|
+
return
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
rendered_pair = render_pair(pattern, value, indent + INDENT)
|
|
457
|
+
if rendered_pair
|
|
458
|
+
lines << indent_block(rendered_pair, INDENT)
|
|
459
|
+
else
|
|
460
|
+
lines << indent_block(render(pattern, indent + INDENT), INDENT)
|
|
461
|
+
lines << indent_block(render(value, indent + INDENT), INDENT)
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
519
465
|
def render_pipeline(head, args, indent)
|
|
520
466
|
base = "(#{head}"
|
|
521
467
|
lines = [base]
|
|
@@ -547,7 +493,7 @@ module Kapusta
|
|
|
547
493
|
append_suffix(lines, ')')
|
|
548
494
|
end
|
|
549
495
|
|
|
550
|
-
def render_call(list, indent)
|
|
496
|
+
def render_call(list, indent, force_hang: false)
|
|
551
497
|
head = flat_render(list_head(list))
|
|
552
498
|
raise Error, "cannot format form head: #{list_head(list).inspect}" unless head
|
|
553
499
|
|
|
@@ -555,7 +501,7 @@ module Kapusta
|
|
|
555
501
|
lines = [base]
|
|
556
502
|
args = list_raw_rest(list)
|
|
557
503
|
semantic_length = semantic_items(args).length
|
|
558
|
-
hang_subsequent_args = hang_call_args?(list, indent)
|
|
504
|
+
hang_subsequent_args = force_hang || hang_call_args?(list, base, indent)
|
|
559
505
|
|
|
560
506
|
semantic_index = 0
|
|
561
507
|
hanging = nil
|
|
@@ -565,6 +511,8 @@ module Kapusta
|
|
|
565
511
|
next
|
|
566
512
|
elsif semantic_index.zero?
|
|
567
513
|
hanging = append_first_call_arg(lines, arg, base, indent, semantic_length)
|
|
514
|
+
elsif append_packed_call_arg?(list, lines, arg, indent)
|
|
515
|
+
nil
|
|
568
516
|
elsif hanging && hang_subsequent_args
|
|
569
517
|
lines << prefix_continuation(hanging, render(arg, indent + hanging.length))
|
|
570
518
|
else
|
|
@@ -597,33 +545,172 @@ module Kapusta
|
|
|
597
545
|
hanging
|
|
598
546
|
end
|
|
599
547
|
|
|
600
|
-
def
|
|
601
|
-
return false unless
|
|
548
|
+
def append_packed_call_arg?(list, lines, arg, indent)
|
|
549
|
+
return false unless packable_call_arg?(list, arg)
|
|
550
|
+
|
|
551
|
+
packed = packed_call_arg(lines.last, list, arg, indent)
|
|
552
|
+
if packed
|
|
553
|
+
lines[-1] = packed.first
|
|
554
|
+
lines.concat(packed.drop(1))
|
|
555
|
+
return true
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
false
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def packable_call_arg?(list, arg)
|
|
562
|
+
return false if fn_form?(arg)
|
|
563
|
+
return true if packable_collection_call_arg?(list, arg)
|
|
564
|
+
|
|
565
|
+
pack_call_args?(list) && flat_render(arg)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def packable_collection_call_arg?(list, arg)
|
|
569
|
+
return true if arg.is_a?(Vec)
|
|
570
|
+
|
|
571
|
+
arg.is_a?(HashLit) && packable_hash_call_arg?(list)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def packed_call_arg(current_line, list, arg, indent)
|
|
575
|
+
packable_call_arg_renderings(list, arg, indent + current_line.length + 1).each do |rendered|
|
|
576
|
+
first_line, *rest = rendered.lines(chomp: true)
|
|
577
|
+
candidate = "#{current_line} #{first_line}"
|
|
578
|
+
next unless inline_arg_fits?(candidate, indent)
|
|
579
|
+
|
|
580
|
+
hanging = ' ' * (current_line.length + 1)
|
|
581
|
+
return [candidate, *rest.map { |line| "#{hanging}#{line}" }]
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
nil
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def packable_call_arg_renderings(list, arg, indent)
|
|
588
|
+
[
|
|
589
|
+
flat_collection_render(arg),
|
|
590
|
+
flat_render(arg),
|
|
591
|
+
multiline_packable_call_arg_rendering(list, arg, indent)
|
|
592
|
+
].compact.uniq
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def multiline_packable_call_arg_rendering(list, arg, indent)
|
|
596
|
+
return render(arg, indent) if arg.is_a?(Vec)
|
|
597
|
+
return render(arg, indent) if local_hash_call_arg?(list, arg)
|
|
598
|
+
|
|
599
|
+
nil
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
def local_hash_call_arg?(list, arg)
|
|
603
|
+
arg.is_a?(HashLit) && head_name(list) == 'local'
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def flat_collection_render(form)
|
|
607
|
+
case form
|
|
608
|
+
when Vec
|
|
609
|
+
flat_delimited_render(form.items, '[', ']') { |item| flat_render(item) }
|
|
610
|
+
when HashLit
|
|
611
|
+
return if contains_comments?(form.entries)
|
|
612
|
+
|
|
613
|
+
flat_hash_render(form.pairs)
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def flat_delimited_render(items, open, close)
|
|
618
|
+
return if contains_comments?(items)
|
|
619
|
+
|
|
620
|
+
rendered = items.map do |item|
|
|
621
|
+
item.is_a?(Array) ? yield(*item) : yield(item)
|
|
622
|
+
end
|
|
623
|
+
return if rendered.any?(&:nil?)
|
|
624
|
+
|
|
625
|
+
"#{open}#{rendered.join(' ')}#{close}"
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def flat_hash_render(pairs)
|
|
629
|
+
rendered = pairs.map { |pair| flat_hash_pair(pair) }
|
|
630
|
+
return if rendered.any?(&:nil?)
|
|
631
|
+
|
|
632
|
+
"{#{rendered.join(' ')}}"
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def hang_call_args?(list, base, indent)
|
|
636
|
+
return true if source_hangs_call_args?(list, base)
|
|
637
|
+
return true if set_function_value?(list)
|
|
602
638
|
|
|
603
639
|
flat = flat_call_render(list)
|
|
604
|
-
|
|
640
|
+
return false unless flat
|
|
641
|
+
|
|
642
|
+
overflowing = !fits?(flat, indent)
|
|
643
|
+
return true if overflowing && hanging_overflow_call?(list)
|
|
644
|
+
|
|
645
|
+
operator_call?(list) && overflowing
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
def pack_call_args?(list)
|
|
649
|
+
head_name(list)&.match?(/\A[a-z0-9_-][\w-]*\./)
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def packable_hash_call_arg?(list)
|
|
653
|
+
head_name(list) == 'local' || pack_call_args?(list)
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
def hanging_overflow_call?(list)
|
|
657
|
+
hash_first_call_arg?(list) || pack_call_args?(list)
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def hash_first_call_arg?(list)
|
|
661
|
+
list_rest(list).first.is_a?(HashLit)
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def set_function_value?(list)
|
|
665
|
+
head_name(list) == 'set' && fn_form?(list_rest(list)[1])
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
def source_hangs_call_args?(list, base)
|
|
669
|
+
return false unless list.respond_to?(:column) && list.column
|
|
670
|
+
|
|
671
|
+
args = list_rest(list)
|
|
672
|
+
return false if args.length < 2
|
|
673
|
+
|
|
674
|
+
expected_column = list.column + base.length + 1
|
|
675
|
+
args.drop(1).all? do |arg|
|
|
676
|
+
arg.respond_to?(:column) && arg.column == expected_column
|
|
677
|
+
end
|
|
605
678
|
end
|
|
606
679
|
|
|
607
680
|
def operator_call?(list)
|
|
608
|
-
|
|
609
|
-
|
|
681
|
+
head_name(list)&.match?(/\A[^\w.]+\z/)
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
def ordinary_call_form?(list)
|
|
685
|
+
name = head_name(list)
|
|
686
|
+
name && !Compiler::Language::SPECIAL_FORMS.include?(name)
|
|
610
687
|
end
|
|
611
688
|
|
|
612
689
|
def flat_call_render(list)
|
|
613
690
|
head = flat_render(list_head(list))
|
|
614
691
|
return unless head
|
|
615
692
|
|
|
616
|
-
rendered_args = semantic_items(list_raw_rest(list)).map { |arg|
|
|
693
|
+
rendered_args = semantic_items(list_raw_rest(list)).map { |arg| flat_call_arg_render(arg) }
|
|
617
694
|
return if rendered_args.any?(&:nil?)
|
|
618
695
|
|
|
619
696
|
"(#{[head, *rendered_args].join(' ')})"
|
|
620
697
|
end
|
|
621
698
|
|
|
699
|
+
def flat_call_arg_render(arg)
|
|
700
|
+
collection = flat_collection_render(arg)
|
|
701
|
+
return collection if collection
|
|
702
|
+
|
|
703
|
+
flat_render(arg)
|
|
704
|
+
end
|
|
705
|
+
|
|
622
706
|
def render_vec(vec, indent, layout: nil, top_level: false, force_expand: false)
|
|
623
707
|
flat = flat_render(vec)
|
|
624
708
|
return flat if !force_expand && flat && fits?(flat, indent) && allow_flat?(vec, top_level:, layout:)
|
|
625
709
|
|
|
626
710
|
return render_pairwise_vec(vec, indent) if layout == :pairwise && !contains_comments?(vec.items)
|
|
711
|
+
if multiline_in_source?(vec) && multiline_vec_items_should_separate?(vec) && !contains_comments?(vec.items)
|
|
712
|
+
return render_multiline_vec(vec, indent)
|
|
713
|
+
end
|
|
627
714
|
return render_filled_vec(vec, indent) if !contains_comments?(vec.items) && !vec.items.empty?
|
|
628
715
|
|
|
629
716
|
lines = ['[']
|
|
@@ -633,6 +720,42 @@ module Kapusta
|
|
|
633
720
|
append_suffix(lines, ']')
|
|
634
721
|
end
|
|
635
722
|
|
|
723
|
+
def render_multiline_vec(vec, indent)
|
|
724
|
+
return '[]' if vec.items.empty?
|
|
725
|
+
|
|
726
|
+
lines = []
|
|
727
|
+
vec.items.each_with_index do |item, idx|
|
|
728
|
+
prefix = idx.zero? ? '[' : ' '
|
|
729
|
+
lines.concat(prefix_lines(prefix, render_multiline_vec_item(item, indent + 1).lines.map(&:chomp)))
|
|
730
|
+
end
|
|
731
|
+
lines[-1] = "#{lines[-1]}]"
|
|
732
|
+
lines.join("\n")
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
def render_multiline_vec_item(item, indent)
|
|
736
|
+
return render_call(item, indent, force_hang: true) if hanging_multiline_vec_call_item?(item)
|
|
737
|
+
|
|
738
|
+
render(item, indent)
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def hanging_multiline_vec_call_item?(item)
|
|
742
|
+
item.is_a?(List) && multiline_in_source?(item) && ordinary_call_form?(item)
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
def multiline_vec_items_should_separate?(vec)
|
|
746
|
+
multiline_vec_items_on_separate_lines?(vec) || semantic_items(vec.items).all? { |item| collection?(item) }
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
def multiline_vec_items_on_separate_lines?(vec)
|
|
750
|
+
items = semantic_items(vec.items)
|
|
751
|
+
return false if items.length < 2
|
|
752
|
+
|
|
753
|
+
lines = items.filter_map { |item| item.line if item.respond_to?(:line) }
|
|
754
|
+
return true if lines.length < 2
|
|
755
|
+
|
|
756
|
+
lines.uniq.length == lines.length
|
|
757
|
+
end
|
|
758
|
+
|
|
636
759
|
def render_filled_vec(vec, indent)
|
|
637
760
|
output_lines = ['[']
|
|
638
761
|
|
|
@@ -688,16 +811,16 @@ module Kapusta
|
|
|
688
811
|
def render_let_bindings(bindings, indent)
|
|
689
812
|
return render(bindings, indent + '(let '.length, force_expand: true) if contains_comments?(bindings.items)
|
|
690
813
|
|
|
691
|
-
hanging = render_hanging_pairwise_vec(bindings)
|
|
814
|
+
hanging = render_hanging_pairwise_vec(bindings, indent)
|
|
692
815
|
hanging || render(bindings, indent + '(let '.length, layout: :pairwise)
|
|
693
816
|
end
|
|
694
817
|
|
|
695
|
-
def render_hanging_pairwise_vec(vec)
|
|
818
|
+
def render_hanging_pairwise_vec(vec, indent)
|
|
696
819
|
pairs = vec.items.each_slice(2).to_a
|
|
697
820
|
return unless pairs.all? { |pair| pair.length == 2 }
|
|
698
821
|
|
|
699
822
|
rendered_pairs = pairs.map do |left, right|
|
|
700
|
-
render_binding_pair(left, right)
|
|
823
|
+
render_binding_pair(left, right, indent)
|
|
701
824
|
end
|
|
702
825
|
return if rendered_pairs.any?(&:nil?)
|
|
703
826
|
|
|
@@ -725,7 +848,7 @@ module Kapusta
|
|
|
725
848
|
|
|
726
849
|
key, value = entry
|
|
727
850
|
first_pair = output_lines.empty?
|
|
728
|
-
if
|
|
851
|
+
if hash_pair_shorthand?(entry)
|
|
729
852
|
output_lines << "#{first_pair ? '{' : ' '}: #{value.name}"
|
|
730
853
|
next
|
|
731
854
|
end
|
|
@@ -745,8 +868,9 @@ module Kapusta
|
|
|
745
868
|
output_lines.join("\n")
|
|
746
869
|
end
|
|
747
870
|
|
|
748
|
-
def flat_hash_pair(
|
|
749
|
-
|
|
871
|
+
def flat_hash_pair(pair)
|
|
872
|
+
key, value = pair
|
|
873
|
+
return ": #{value.name}" if hash_pair_shorthand?(pair)
|
|
750
874
|
|
|
751
875
|
rendered_value = flat_render(value)
|
|
752
876
|
return unless rendered_value
|
|
@@ -772,14 +896,15 @@ module Kapusta
|
|
|
772
896
|
fits?(pair, indent) ? pair : nil
|
|
773
897
|
end
|
|
774
898
|
|
|
775
|
-
def render_binding_pair(left, right)
|
|
899
|
+
def render_binding_pair(left, right, indent)
|
|
776
900
|
left_rendered = flat_render(left)
|
|
777
901
|
return unless left_rendered
|
|
778
902
|
|
|
779
|
-
|
|
903
|
+
right_indent = indent + '(let ['.length + left_rendered.length + 1
|
|
904
|
+
right_rendered = render(right, right_indent)
|
|
780
905
|
first_line, *rest = right_rendered.lines(chomp: true)
|
|
781
906
|
pair = "#{left_rendered} #{first_line}"
|
|
782
|
-
return unless pair.length
|
|
907
|
+
return unless fits?(pair, indent + '(let ['.length)
|
|
783
908
|
|
|
784
909
|
return pair if rest.empty?
|
|
785
910
|
|
|
@@ -787,8 +912,8 @@ module Kapusta
|
|
|
787
912
|
([pair] + rest.map { |line| "#{continuation}#{line}" }).join("\n")
|
|
788
913
|
end
|
|
789
914
|
|
|
790
|
-
def
|
|
791
|
-
|
|
915
|
+
def hash_pair_shorthand?(pair)
|
|
916
|
+
pair.respond_to?(:shorthand?) && pair.shorthand?
|
|
792
917
|
end
|
|
793
918
|
|
|
794
919
|
def hashfn_literal?(form)
|
|
@@ -809,11 +934,10 @@ module Kapusta
|
|
|
809
934
|
return true unless head.is_a?(Sym)
|
|
810
935
|
|
|
811
936
|
case head.name
|
|
812
|
-
when
|
|
813
|
-
'accumulate', 'faccumulate'
|
|
937
|
+
when *Compiler::Language::FLAT_BODY_HEADS
|
|
814
938
|
!top_level
|
|
815
939
|
else
|
|
816
|
-
|
|
940
|
+
!Compiler::Language.never_flat_head?(head.name)
|
|
817
941
|
end
|
|
818
942
|
end
|
|
819
943
|
|
|
@@ -826,18 +950,13 @@ module Kapusta
|
|
|
826
950
|
return false unless head.is_a?(Sym)
|
|
827
951
|
|
|
828
952
|
case head.name
|
|
829
|
-
when
|
|
830
|
-
'fn', 'lambda', 'λ', 'macro'
|
|
953
|
+
when *Compiler::Language::MULTILINE_BODY_HEADS
|
|
831
954
|
true
|
|
832
955
|
else
|
|
833
956
|
false
|
|
834
957
|
end
|
|
835
958
|
end
|
|
836
959
|
|
|
837
|
-
def multiline_in_source?(form)
|
|
838
|
-
form.respond_to?(:multiline_source) && form.multiline_source
|
|
839
|
-
end
|
|
840
|
-
|
|
841
960
|
def let_with_multiple_bindings?(form)
|
|
842
961
|
head = list_head(form)
|
|
843
962
|
return false unless head.is_a?(Sym) && head.name == 'let'
|
|
@@ -860,25 +979,12 @@ module Kapusta
|
|
|
860
979
|
end
|
|
861
980
|
end
|
|
862
981
|
|
|
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
982
|
def fn_form?(form)
|
|
877
983
|
return false unless form.is_a?(List)
|
|
878
984
|
|
|
879
985
|
head = list_head(form)
|
|
880
986
|
head.is_a?(Sym) &&
|
|
881
|
-
|
|
987
|
+
Compiler::Language.function_head?(head.name)
|
|
882
988
|
end
|
|
883
989
|
|
|
884
990
|
def inline_three_arg_if?(args)
|
|
@@ -897,33 +1003,6 @@ module Kapusta
|
|
|
897
1003
|
end
|
|
898
1004
|
end
|
|
899
1005
|
|
|
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
1006
|
def render_generic_list(list, indent)
|
|
928
1007
|
lines = ['(']
|
|
929
1008
|
list.items.each do |item|
|
|
@@ -959,47 +1038,6 @@ module Kapusta
|
|
|
959
1038
|
append_suffix(lines, ')')
|
|
960
1039
|
end
|
|
961
1040
|
|
|
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
1041
|
class Error < Kapusta::Error; end
|
|
1004
1042
|
end
|
|
1005
1043
|
end
|