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.
@@ -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