syntax_tree-rbs 0.2.0 → 0.5.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/.github/workflows/main.yml +3 -1
- data/CHANGELOG.md +26 -1
- data/Gemfile.lock +7 -4
- data/Rakefile +7 -0
- data/lib/syntax_tree/rbs/entrypoints.rb +392 -0
- data/lib/syntax_tree/rbs/format.rb +697 -0
- data/lib/syntax_tree/rbs/pretty_print.rb +533 -0
- data/lib/syntax_tree/rbs/version.rb +1 -1
- data/lib/syntax_tree/rbs.rb +20 -40
- data/syntax_tree-rbs.gemspec +15 -14
- metadata +20 -7
- data/lib/syntax_tree/rbs/declarations.rb +0 -226
- data/lib/syntax_tree/rbs/members.rb +0 -414
- data/lib/syntax_tree/rbs/types.rb +0 -293
- data/lib/syntax_tree/rbs/utils.rb +0 -432
@@ -0,0 +1,697 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module RBS
|
5
|
+
class Format < Visitor
|
6
|
+
attr_reader :q
|
7
|
+
|
8
|
+
def initialize(q)
|
9
|
+
@q = q
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_base_type(node)
|
13
|
+
q.text(node.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Visit a RBS::AST::Declarations::Alias node.
|
17
|
+
def visit_alias_declaration(node)
|
18
|
+
print_comment(node)
|
19
|
+
print_annotations(node)
|
20
|
+
|
21
|
+
q.group do
|
22
|
+
q.text("type ")
|
23
|
+
visit(node.name)
|
24
|
+
q.text(" =")
|
25
|
+
q.group do
|
26
|
+
q.indent do
|
27
|
+
q.breakable
|
28
|
+
visit(node.type)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Visit a RBS::AST::Members::Alias node.
|
35
|
+
def visit_alias_member(node)
|
36
|
+
print_comment(node)
|
37
|
+
print_annotations(node)
|
38
|
+
|
39
|
+
if node.kind == :singleton
|
40
|
+
q.text("alias self.")
|
41
|
+
q.text(node.new_name)
|
42
|
+
q.text(" self.")
|
43
|
+
q.text(node.old_name)
|
44
|
+
else
|
45
|
+
q.text("alias ")
|
46
|
+
q.text(node.new_name)
|
47
|
+
q.text(" ")
|
48
|
+
q.text(node.old_name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Visit a RBS::Types::Alias node.
|
53
|
+
def visit_alias_type(node)
|
54
|
+
visit(node.name)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Visit a RBS::Types::Bases::Any node.
|
58
|
+
alias visit_any_type visit_base_type
|
59
|
+
|
60
|
+
# Visit a RBS::AST::Members::AttrAccessor node.
|
61
|
+
def visit_attr_accessor_member(node)
|
62
|
+
print_comment(node)
|
63
|
+
print_annotations(node)
|
64
|
+
print_attribute(:accessor, node)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Visit a RBS::AST::Members::AttrReader node.
|
68
|
+
def visit_attr_reader_member(node)
|
69
|
+
print_comment(node)
|
70
|
+
print_annotations(node)
|
71
|
+
print_attribute(:reader, node)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Visit a RBS::AST::Members::AttrWriter node.
|
75
|
+
def visit_attr_writer_member(node)
|
76
|
+
print_comment(node)
|
77
|
+
print_annotations(node)
|
78
|
+
print_attribute(:writer, node)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Visit a RBS::Types::Bases::Bool node.
|
82
|
+
alias visit_bool_type visit_base_type
|
83
|
+
|
84
|
+
# Visit a RBS::Types::Bases::Bottom node.
|
85
|
+
alias visit_bottom_type visit_base_type
|
86
|
+
|
87
|
+
# Visit a RBS::AST::Declarations::Class node.
|
88
|
+
def visit_class_declaration(node)
|
89
|
+
print_comment(node)
|
90
|
+
print_annotations(node)
|
91
|
+
|
92
|
+
q.group do
|
93
|
+
q.text("class ")
|
94
|
+
print_name_and_type_params(node)
|
95
|
+
|
96
|
+
if node.super_class
|
97
|
+
q.text(" < ")
|
98
|
+
print_name_and_args(node.super_class)
|
99
|
+
end
|
100
|
+
|
101
|
+
q.indent { print_members(node) }
|
102
|
+
q.breakable(force: true)
|
103
|
+
q.text("end")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Visit a RBS::Types::ClassInstance node.
|
108
|
+
def visit_class_instance_type(node)
|
109
|
+
print_name_and_args(node)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Visit a RBS::AST::Members::ClassInstanceVariable node.
|
113
|
+
def visit_class_instance_variable_member(node)
|
114
|
+
print_comment(node)
|
115
|
+
|
116
|
+
q.group do
|
117
|
+
q.text("self.")
|
118
|
+
q.text(node.name)
|
119
|
+
q.text(": ")
|
120
|
+
visit(node.type)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Visit a RBS::Types::ClassSingleton node.
|
125
|
+
def visit_class_singleton_type(node)
|
126
|
+
q.text("singleton(")
|
127
|
+
visit(node.name)
|
128
|
+
q.text(")")
|
129
|
+
end
|
130
|
+
|
131
|
+
# Visit a RBS::Types::Bases::Class node.
|
132
|
+
alias visit_class_type visit_base_type
|
133
|
+
|
134
|
+
# Visit a RBS::AST::Members::ClassVariable node.
|
135
|
+
def visit_class_variable_member(node)
|
136
|
+
print_comment(node)
|
137
|
+
|
138
|
+
q.group do
|
139
|
+
q.text(node.name)
|
140
|
+
q.text(": ")
|
141
|
+
visit(node.type)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Visit a RBS::AST::Declarations::Constant node.
|
146
|
+
def visit_constant_declaration(node)
|
147
|
+
print_comment(node)
|
148
|
+
|
149
|
+
q.group do
|
150
|
+
visit(node.name)
|
151
|
+
q.text(": ")
|
152
|
+
visit(node.type)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Visit a RBS::AST::Members::Extend node.
|
157
|
+
def visit_extend_member(node)
|
158
|
+
print_comment(node)
|
159
|
+
print_annotations(node)
|
160
|
+
|
161
|
+
q.group do
|
162
|
+
q.text("extend ")
|
163
|
+
print_name_and_args(node)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Visit a RBS::Types::Function::Param node.
|
168
|
+
def visit_function_param_type(node)
|
169
|
+
visit(node.type)
|
170
|
+
|
171
|
+
if node.name
|
172
|
+
q.text(" ")
|
173
|
+
|
174
|
+
if ::RBS::Parser::KEYWORDS.include?(node.name.to_s)
|
175
|
+
q.text("`#{node.name}`")
|
176
|
+
else
|
177
|
+
q.text(node.name)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Visit a RBS::AST::Declarations::Global node.
|
183
|
+
def visit_global_declaration(node)
|
184
|
+
print_comment(node)
|
185
|
+
|
186
|
+
q.group do
|
187
|
+
q.text(node.name)
|
188
|
+
q.text(": ")
|
189
|
+
visit(node.type)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Visit a RBS::AST::Members::Include node.
|
194
|
+
def visit_include_member(node)
|
195
|
+
print_comment(node)
|
196
|
+
print_annotations(node)
|
197
|
+
|
198
|
+
q.group do
|
199
|
+
q.text("include ")
|
200
|
+
print_name_and_args(node)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Visit a RBS::Types::Bases::Instance node.
|
205
|
+
alias visit_instance_type visit_base_type
|
206
|
+
|
207
|
+
# Visit a RBS::AST::Members::InstanceVariable node.
|
208
|
+
def visit_instance_variable_member(node)
|
209
|
+
print_comment(node)
|
210
|
+
|
211
|
+
q.group do
|
212
|
+
q.text(node.name)
|
213
|
+
q.text(": ")
|
214
|
+
visit(node.type)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Visit a RBS::AST::Declarations::Interface node.
|
219
|
+
def visit_interface_declaration(node)
|
220
|
+
print_comment(node)
|
221
|
+
print_annotations(node)
|
222
|
+
|
223
|
+
q.group do
|
224
|
+
q.text("interface ")
|
225
|
+
print_name_and_type_params(node)
|
226
|
+
q.indent { print_members(node) }
|
227
|
+
q.breakable(force: true)
|
228
|
+
q.text("end")
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Visit a RBS::Types::Interface node.
|
233
|
+
def visit_interface_type(node)
|
234
|
+
print_name_and_args(node)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Visit a RBS::Types::Intersection node.
|
238
|
+
def visit_intersection_type(node)
|
239
|
+
separator =
|
240
|
+
lambda do
|
241
|
+
q.breakable
|
242
|
+
q.text("& ")
|
243
|
+
end
|
244
|
+
|
245
|
+
q.text("(") if q.force_parens?
|
246
|
+
q.group do
|
247
|
+
q.force_parens do
|
248
|
+
q.seplist(node.types, separator) { |type| visit(type) }
|
249
|
+
end
|
250
|
+
end
|
251
|
+
q.text(")") if q.force_parens?
|
252
|
+
end
|
253
|
+
|
254
|
+
# Visit a RBS::Types::Literal node.
|
255
|
+
def visit_literal_type(node)
|
256
|
+
unless node.literal.is_a?(String)
|
257
|
+
q.text(node.literal.inspect)
|
258
|
+
return
|
259
|
+
end
|
260
|
+
|
261
|
+
# We're going to go straight to the source here, as if we don't then
|
262
|
+
# we're going to end up with the result of String#inspect, which does
|
263
|
+
# weird things to escape sequences.
|
264
|
+
source = q.source[node.location.range]
|
265
|
+
quote = source.include?("\\") ? source[0] : "\""
|
266
|
+
source = SyntaxTree::Quotes.normalize(source[1..-2], quote)
|
267
|
+
|
268
|
+
q.text(quote)
|
269
|
+
q.seplist(
|
270
|
+
source.split(/\r?\n/),
|
271
|
+
-> { q.breakable(force: true) }
|
272
|
+
) { |line| q.text(line) }
|
273
|
+
q.text(quote)
|
274
|
+
end
|
275
|
+
|
276
|
+
# Visit a RBS::AST::Members::MethodDefinition node.
|
277
|
+
def visit_method_definition_member(node)
|
278
|
+
print_comment(node)
|
279
|
+
print_annotations(node)
|
280
|
+
|
281
|
+
q.group do
|
282
|
+
q.text("#{node.visibility} ") if node.visibility
|
283
|
+
q.text("def ")
|
284
|
+
|
285
|
+
if node.kind == :singleton
|
286
|
+
q.text("self.")
|
287
|
+
elsif node.kind == :singleton_instance
|
288
|
+
q.text("self?.")
|
289
|
+
end
|
290
|
+
|
291
|
+
q.text(
|
292
|
+
(
|
293
|
+
if ::RBS::Parser::KEYWORDS.include?(node.name.to_s)
|
294
|
+
"`#{node.name}`"
|
295
|
+
else
|
296
|
+
node.name
|
297
|
+
end
|
298
|
+
)
|
299
|
+
)
|
300
|
+
q.text(":")
|
301
|
+
|
302
|
+
if node.types.length == 1 && !node.overload?
|
303
|
+
q.text(" ")
|
304
|
+
print_method_signature(node.types.first)
|
305
|
+
else
|
306
|
+
separator =
|
307
|
+
lambda do
|
308
|
+
q.breakable
|
309
|
+
q.text("| ")
|
310
|
+
end
|
311
|
+
|
312
|
+
q.group do
|
313
|
+
q.indent do
|
314
|
+
q.breakable
|
315
|
+
q.seplist(node.types, separator) do |type|
|
316
|
+
print_method_signature(type)
|
317
|
+
end
|
318
|
+
|
319
|
+
if node.overload?
|
320
|
+
separator.call
|
321
|
+
q.text("...")
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Visit a RBS::AST::Declarations::Module node.
|
330
|
+
def visit_module_declaration(node)
|
331
|
+
print_comment(node)
|
332
|
+
print_annotations(node)
|
333
|
+
|
334
|
+
q.group do
|
335
|
+
q.text("module ")
|
336
|
+
print_name_and_type_params(node)
|
337
|
+
|
338
|
+
if node.self_types.any?
|
339
|
+
q.text(" : ")
|
340
|
+
q.seplist(node.self_types, -> { q.text(", ") }) do |self_type|
|
341
|
+
print_name_and_args(self_type)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
q.indent { print_members(node) }
|
346
|
+
q.breakable(force: true)
|
347
|
+
q.text("end")
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Visit a RBS::Types::Bases::Nil node.
|
352
|
+
alias visit_nil_type visit_base_type
|
353
|
+
|
354
|
+
# Visit a RBS::Types::Optional node.
|
355
|
+
def visit_optional_type(node)
|
356
|
+
q.force_parens { visit(node.type) }
|
357
|
+
q.text("?")
|
358
|
+
end
|
359
|
+
|
360
|
+
# Visit a RBS::AST::Members::Prepend node.
|
361
|
+
def visit_prepend_member(node)
|
362
|
+
print_comment(node)
|
363
|
+
print_annotations(node)
|
364
|
+
|
365
|
+
q.group do
|
366
|
+
q.text("prepend ")
|
367
|
+
print_name_and_args(node)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
# Visit a RBS::AST::Members::Private node.
|
372
|
+
def visit_private_member(node)
|
373
|
+
q.text("private")
|
374
|
+
end
|
375
|
+
|
376
|
+
# Visit a RBS::Types::Proc node.
|
377
|
+
def visit_proc_type(node)
|
378
|
+
q.group do
|
379
|
+
q.text("(") if q.force_parens?
|
380
|
+
q.text("^")
|
381
|
+
print_method_signature(node)
|
382
|
+
q.text(")") if q.force_parens?
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# Visit a RBS::AST::Members::Public node.
|
387
|
+
def visit_public_member(node)
|
388
|
+
q.text("public")
|
389
|
+
end
|
390
|
+
|
391
|
+
# Visit a RBS::Types::Record node.
|
392
|
+
def visit_record_type(node)
|
393
|
+
separator =
|
394
|
+
lambda do
|
395
|
+
q.text(",")
|
396
|
+
q.breakable
|
397
|
+
end
|
398
|
+
|
399
|
+
q.group do
|
400
|
+
q.text("{")
|
401
|
+
q.indent do
|
402
|
+
q.breakable
|
403
|
+
q.seplist(node.fields, separator, :each_pair) do |key, type|
|
404
|
+
if key.is_a?(Symbol) && key.match?(/\A[A-Za-z_][A-Za-z_]*\z/)
|
405
|
+
q.text("#{key}: ")
|
406
|
+
else
|
407
|
+
q.text("#{key.inspect} => ")
|
408
|
+
end
|
409
|
+
|
410
|
+
visit(type)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
q.breakable
|
414
|
+
q.text("}")
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
# Visit a SyntaxTree::RBS::Root node.
|
419
|
+
def visit_root(node)
|
420
|
+
separator =
|
421
|
+
lambda do
|
422
|
+
q.breakable(force: true)
|
423
|
+
q.breakable(force: true)
|
424
|
+
end
|
425
|
+
|
426
|
+
q.seplist(node.declarations, separator) do |declaration|
|
427
|
+
visit(declaration)
|
428
|
+
end
|
429
|
+
|
430
|
+
q.breakable(force: true)
|
431
|
+
end
|
432
|
+
|
433
|
+
# Visit a RBS::Types::Self node.
|
434
|
+
alias visit_self_type visit_base_type
|
435
|
+
|
436
|
+
# Visit a RBS::Types::Top node.
|
437
|
+
alias visit_top_type visit_base_type
|
438
|
+
|
439
|
+
# Visit a RBS::Types::Tuple node.
|
440
|
+
def visit_tuple_type(node)
|
441
|
+
# If we don't have any sub types, we explicitly need the space in
|
442
|
+
# between the brackets to not confuse the parser.
|
443
|
+
if node.types.empty?
|
444
|
+
q.text("[ ]")
|
445
|
+
return
|
446
|
+
end
|
447
|
+
|
448
|
+
q.group do
|
449
|
+
q.text("[")
|
450
|
+
q.seplist(node.types, -> { q.text(", ") }) { |type| visit(type) }
|
451
|
+
q.text("]")
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
# Visit a RBS::TypeName node.
|
456
|
+
def visit_type_name(node)
|
457
|
+
q.text(node.to_s)
|
458
|
+
end
|
459
|
+
|
460
|
+
# Visit a RBS::Types::Union node.
|
461
|
+
def visit_union_type(node)
|
462
|
+
separator =
|
463
|
+
lambda do
|
464
|
+
q.breakable
|
465
|
+
q.text("| ")
|
466
|
+
end
|
467
|
+
|
468
|
+
q.text("(") if q.force_parens?
|
469
|
+
q.group { q.seplist(node.types, separator) { |type| visit(type) } }
|
470
|
+
q.text(")") if q.force_parens?
|
471
|
+
end
|
472
|
+
|
473
|
+
# Visit a RBS::Types::Variable node.
|
474
|
+
def visit_variable_type(node)
|
475
|
+
q.text(node.name)
|
476
|
+
end
|
477
|
+
|
478
|
+
# Visit a RBS::Types::Bases::Void node.
|
479
|
+
alias visit_void_type visit_base_type
|
480
|
+
|
481
|
+
private
|
482
|
+
|
483
|
+
# An annotation can be attached to many kinds of nodes, and should be
|
484
|
+
# printed using %a{}.
|
485
|
+
def print_annotations(node)
|
486
|
+
annotations = node.annotations
|
487
|
+
return if annotations.empty?
|
488
|
+
|
489
|
+
q.seplist(annotations, -> { q.breakable(force: true) }) do |annotation|
|
490
|
+
if annotation.string.match?(/[{}]/)
|
491
|
+
# Bail out and just print the source string if there are any braces
|
492
|
+
# because we don't want to mess with escaping them.
|
493
|
+
q.text(q.source[annotation.location.range])
|
494
|
+
else
|
495
|
+
q.text("%a{")
|
496
|
+
q.text(annotation.string)
|
497
|
+
q.text("}")
|
498
|
+
end
|
499
|
+
end
|
500
|
+
q.breakable(force: true)
|
501
|
+
end
|
502
|
+
|
503
|
+
def print_attribute(type, node)
|
504
|
+
q.group do
|
505
|
+
q.text("#{node.visibility} ") if node.visibility
|
506
|
+
q.text("attr_#{type} ")
|
507
|
+
q.text("self.") if node.kind == :singleton
|
508
|
+
q.text(node.name)
|
509
|
+
|
510
|
+
if node.ivar_name == false
|
511
|
+
q.text("()")
|
512
|
+
elsif node.ivar_name
|
513
|
+
q.text("(")
|
514
|
+
q.text(node.ivar_name)
|
515
|
+
q.text(")")
|
516
|
+
end
|
517
|
+
|
518
|
+
q.text(": ")
|
519
|
+
visit(node.type)
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
# Comments come in as one whole string, so here we split it up into
|
524
|
+
# multiple lines and then prefix it with the pound sign.
|
525
|
+
def print_comment(node)
|
526
|
+
comment = node.comment
|
527
|
+
return unless comment
|
528
|
+
|
529
|
+
q.seplist(
|
530
|
+
comment.string.split(/\r?\n/),
|
531
|
+
-> { q.breakable(force: true) }
|
532
|
+
) { |line| q.text("# #{line}") }
|
533
|
+
q.breakable(force: true)
|
534
|
+
end
|
535
|
+
|
536
|
+
# Nodes which have members will all flow their printing through this
|
537
|
+
# class, which keeps track of
|
538
|
+
def print_members(node)
|
539
|
+
last_line = nil
|
540
|
+
|
541
|
+
node.members.each do |member|
|
542
|
+
q.breakable(force: true)
|
543
|
+
|
544
|
+
if last_line && (member.location.start_line - last_line >= 2)
|
545
|
+
q.breakable(force: true)
|
546
|
+
end
|
547
|
+
|
548
|
+
visit(member)
|
549
|
+
last_line = member.location.end_line
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
# (T t) -> void
|
554
|
+
def print_method_signature(node)
|
555
|
+
q.group do
|
556
|
+
# We won't have a type_params key if we're printing a block
|
557
|
+
if node.respond_to?(:type_params) && node.type_params.any?
|
558
|
+
q.text("[")
|
559
|
+
q.seplist(node.type_params, -> { q.text(", ") }) do |param|
|
560
|
+
# We need to do a type check here to support RBS 1.0
|
561
|
+
q.text(param.is_a?(Symbol) ? param.to_s : param.name)
|
562
|
+
end
|
563
|
+
q.text("] ")
|
564
|
+
end
|
565
|
+
|
566
|
+
params = []
|
567
|
+
|
568
|
+
# Directly visit each of the required positional parameters.
|
569
|
+
node.type.required_positionals.each do |param|
|
570
|
+
params << -> { visit(param) }
|
571
|
+
end
|
572
|
+
|
573
|
+
# Prefix each of the optional positional parameters with a ?.
|
574
|
+
node.type.optional_positionals.each do |param|
|
575
|
+
params << -> do
|
576
|
+
q.text("?")
|
577
|
+
visit(param)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
# If a rest positional is present, print it and prefix it with a *.
|
582
|
+
if node.type.rest_positionals
|
583
|
+
params << -> do
|
584
|
+
q.text("*")
|
585
|
+
visit(node.type.rest_positionals)
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
# Directly visit any required positional parameters that occur after
|
590
|
+
# the rest positional.
|
591
|
+
node.type.trailing_positionals.each do |param|
|
592
|
+
params << -> { visit(param) }
|
593
|
+
end
|
594
|
+
|
595
|
+
# Print all of the required keyword parameters with their name and
|
596
|
+
# parameter separated by a colon.
|
597
|
+
node.type.required_keywords.each do |name, param|
|
598
|
+
params << -> do
|
599
|
+
q.text(name)
|
600
|
+
q.text(": ")
|
601
|
+
visit(param)
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
# Print all of the required keyword parameters with their name and
|
606
|
+
# parameter separated by a colon, prefixed by a ?.
|
607
|
+
node.type.optional_keywords.each do |name, param|
|
608
|
+
params << -> do
|
609
|
+
q.text("?")
|
610
|
+
q.text(name)
|
611
|
+
q.text(": ")
|
612
|
+
visit(param)
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
# Print the rest keyword parameter if it exists by prefixing it with
|
617
|
+
# a ** operator.
|
618
|
+
if node.type.rest_keywords
|
619
|
+
params << -> do
|
620
|
+
q.text("**")
|
621
|
+
visit(node.type.rest_keywords)
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
if params.any?
|
626
|
+
q.text("(")
|
627
|
+
q.indent do
|
628
|
+
q.breakable("")
|
629
|
+
q.seplist(params, &:call)
|
630
|
+
end
|
631
|
+
q.breakable("")
|
632
|
+
q.text(") ")
|
633
|
+
end
|
634
|
+
|
635
|
+
if node.respond_to?(:block) && node.block
|
636
|
+
q.text("?") unless node.block.required
|
637
|
+
q.text("{")
|
638
|
+
q.indent do
|
639
|
+
q.breakable
|
640
|
+
print_method_signature(node.block)
|
641
|
+
end
|
642
|
+
q.breakable
|
643
|
+
q.text("} ")
|
644
|
+
end
|
645
|
+
|
646
|
+
q.text("-> ")
|
647
|
+
q.force_parens { visit(node.type.return_type) }
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
# Certain nodes are names with optional arguments attached, as in
|
652
|
+
# Array[A]. We handle all of that printing centralized here.
|
653
|
+
def print_name_and_args(node)
|
654
|
+
q.group do
|
655
|
+
visit(node.name)
|
656
|
+
|
657
|
+
if node.args.any?
|
658
|
+
q.text("[")
|
659
|
+
q.seplist(node.args, -> { q.text(", ") }) { |arg| visit(arg) }
|
660
|
+
q.text("]")
|
661
|
+
end
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
# Prints out the name of a class, interface, or module declaration.
|
666
|
+
# Additionally loops through each type parameter if there are any and
|
667
|
+
# print them out joined by commas. Checks for validation and variance.
|
668
|
+
def print_name_and_type_params(node)
|
669
|
+
visit(node.name)
|
670
|
+
return if node.type_params.empty?
|
671
|
+
|
672
|
+
q.text("[")
|
673
|
+
q.seplist(node.type_params, -> { q.text(", ") }) do |param|
|
674
|
+
parts = []
|
675
|
+
|
676
|
+
parts << "unchecked" if param.unchecked?
|
677
|
+
|
678
|
+
if param.variance == :covariant
|
679
|
+
parts << "out"
|
680
|
+
elsif param.variance == :contravariant
|
681
|
+
parts << "in"
|
682
|
+
end
|
683
|
+
|
684
|
+
parts << param.name
|
685
|
+
q.text(parts.join(" "))
|
686
|
+
|
687
|
+
if param.upper_bound
|
688
|
+
q.text(" < ")
|
689
|
+
visit(param.upper_bound)
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
q.text("]")
|
694
|
+
end
|
695
|
+
end
|
696
|
+
end
|
697
|
+
end
|