parlour 4.0.1 → 5.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +208 -20
  4. data/exe/parlour +45 -6
  5. data/lib/parlour.rb +27 -1
  6. data/lib/parlour/conversion/converter.rb +34 -0
  7. data/lib/parlour/conversion/rbi_to_rbs.rb +223 -0
  8. data/lib/parlour/detached_rbs_generator.rb +25 -0
  9. data/lib/parlour/generator.rb +34 -0
  10. data/lib/parlour/options.rb +71 -0
  11. data/lib/parlour/rbi_generator.rb +24 -37
  12. data/lib/parlour/rbi_generator/arbitrary.rb +5 -2
  13. data/lib/parlour/rbi_generator/attribute.rb +14 -5
  14. data/lib/parlour/rbi_generator/class_namespace.rb +8 -3
  15. data/lib/parlour/rbi_generator/constant.rb +17 -6
  16. data/lib/parlour/rbi_generator/enum_class_namespace.rb +8 -3
  17. data/lib/parlour/rbi_generator/extend.rb +5 -2
  18. data/lib/parlour/rbi_generator/include.rb +5 -2
  19. data/lib/parlour/rbi_generator/method.rb +15 -10
  20. data/lib/parlour/rbi_generator/module_namespace.rb +7 -2
  21. data/lib/parlour/rbi_generator/namespace.rb +39 -12
  22. data/lib/parlour/rbi_generator/parameter.rb +11 -5
  23. data/lib/parlour/rbi_generator/rbi_object.rb +19 -78
  24. data/lib/parlour/rbi_generator/struct_class_namespace.rb +9 -2
  25. data/lib/parlour/rbi_generator/struct_prop.rb +12 -9
  26. data/lib/parlour/rbi_generator/type_alias.rb +101 -0
  27. data/lib/parlour/rbs_generator.rb +24 -0
  28. data/lib/parlour/rbs_generator/arbitrary.rb +92 -0
  29. data/lib/parlour/rbs_generator/attribute.rb +82 -0
  30. data/lib/parlour/rbs_generator/block.rb +49 -0
  31. data/lib/parlour/rbs_generator/class_namespace.rb +106 -0
  32. data/lib/parlour/rbs_generator/constant.rb +95 -0
  33. data/lib/parlour/rbs_generator/extend.rb +92 -0
  34. data/lib/parlour/rbs_generator/include.rb +92 -0
  35. data/lib/parlour/rbs_generator/interface_namespace.rb +34 -0
  36. data/lib/parlour/rbs_generator/method.rb +146 -0
  37. data/lib/parlour/rbs_generator/method_signature.rb +104 -0
  38. data/lib/parlour/rbs_generator/module_namespace.rb +35 -0
  39. data/lib/parlour/rbs_generator/namespace.rb +627 -0
  40. data/lib/parlour/rbs_generator/parameter.rb +145 -0
  41. data/lib/parlour/rbs_generator/rbs_object.rb +78 -0
  42. data/lib/parlour/rbs_generator/type_alias.rb +96 -0
  43. data/lib/parlour/type_parser.rb +152 -0
  44. data/lib/parlour/typed_object.rb +87 -0
  45. data/lib/parlour/types.rb +445 -0
  46. data/lib/parlour/version.rb +1 -1
  47. data/parlour.gemspec +1 -1
  48. data/rbi/parlour.rbi +982 -76
  49. metadata +30 -7
  50. data/lib/parlour/rbi_generator/options.rb +0 -74
@@ -0,0 +1,34 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbsGenerator < Generator
4
+ # Represents an interface definition.
5
+ class InterfaceNamespace < Namespace
6
+ extend T::Sig
7
+
8
+ sig do
9
+ override.params(
10
+ indent_level: Integer,
11
+ options: Options
12
+ ).returns(T::Array[String])
13
+ end
14
+ # Generates the RBS lines for this interface.
15
+ #
16
+ # @param indent_level [Integer] The indentation level to generate the lines at.
17
+ # @param options [Options] The formatting options to use.
18
+ # @return [Array<String>] The RBS lines, formatted as specified.
19
+ def generate_rbs(indent_level, options)
20
+ lines = generate_comments(indent_level, options)
21
+ lines << options.indented(indent_level, "interface #{name}")
22
+ lines += generate_body(indent_level + 1, options)
23
+ lines << options.indented(indent_level, "end")
24
+ end
25
+
26
+ sig { override.returns(String) }
27
+ # Returns a human-readable brief string description of this interface.
28
+ # @return [String]
29
+ def describe
30
+ "Interface #{name} - #{children.length}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,146 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbsGenerator < Generator
4
+ # Represents a method definition.
5
+ class Method < RbsObject
6
+ extend T::Sig
7
+
8
+ sig do
9
+ params(
10
+ generator: Generator,
11
+ name: String,
12
+ signatures: T::Array[MethodSignature],
13
+ class_method: T::Boolean,
14
+ block: T.nilable(T.proc.params(x: Method).void)
15
+ ).void
16
+ end
17
+ # Creates a new method definition.
18
+ # @note You should use {Namespace#create_method} rather than this directly.
19
+ #
20
+ # @param generator [RbsGenerator] The current RbsGenerator.
21
+ # @param name [String] The name of this method. You should not specify +self.+ in
22
+ # this - use the +class_method+ parameter instead.
23
+ # @param signatures [Array<MethodSignature>] The signatures for each
24
+ # overload of this method.
25
+ # @param class_method [Boolean] Whether this method is a class method; that is, it
26
+ # it is defined using +self.+.
27
+ # @param block A block which the new instance yields itself to.
28
+ # @return [void]
29
+ def initialize(generator, name, signatures, class_method: false, &block)
30
+ super(generator, name)
31
+ @signatures = signatures
32
+ @class_method = class_method
33
+ yield_self(&block) if block
34
+ end
35
+
36
+ sig { overridable.params(other: Object).returns(T::Boolean).checked(:never) }
37
+ # Returns true if this instance is equal to another method.
38
+ #
39
+ # @param other [Object] The other instance. If this is not a {Method} (or a
40
+ # subclass of it), this will always return false.
41
+ # @return [Boolean]
42
+ def ==(other)
43
+ Method === other &&
44
+ name == other.name &&
45
+ signatures == other.signatures &&
46
+ class_method == other.class_method
47
+ end
48
+
49
+ sig { returns(T::Array[MethodSignature]) }
50
+ # The signatures for each overload of this method.
51
+ # @return [Array<MethodSignature>]
52
+ attr_reader :signatures
53
+
54
+ sig { returns(T::Boolean) }
55
+ # Whether this method is a class method; that is, it it is defined using
56
+ # +self.+.
57
+ # @return [Boolean]
58
+ attr_reader :class_method
59
+
60
+ sig do
61
+ override.params(
62
+ indent_level: Integer,
63
+ options: Options
64
+ ).returns(T::Array[String])
65
+ end
66
+ # Generates the RBS lines for this method.
67
+ #
68
+ # @param indent_level [Integer] The indentation level to generate the lines at.
69
+ # @param options [Options] The formatting options to use.
70
+ # @return [Array<String>] The RBS lines, formatted as specified.
71
+ def generate_rbs(indent_level, options)
72
+ definition = "def #{class_method ? 'self.' : ''}#{name}: "
73
+ lines = generate_comments(indent_level, options)
74
+
75
+ # Handle each signature
76
+ signatures.each.with_index do |sig, i|
77
+ this_sig_lines = []
78
+
79
+ # Start off the first line of the signature, either with the definition
80
+ # for the first signature, or a pipe for the rest
81
+ if i == 0
82
+ this_sig_lines << options.indented(indent_level, definition)
83
+ else
84
+ this_sig_lines << options.indented(indent_level, "#{' ' * (definition.length - 2)}| ")
85
+ end
86
+
87
+ # Generate the signature's lines, we'll append them afterwards
88
+ partial_sig_lines = sig.generate_rbs(options)
89
+
90
+ # Merge the first signature line, and indent & concat the rest
91
+ first_line, *rest_lines = *partial_sig_lines
92
+ this_sig_lines[0] += first_line
93
+ rest_lines&.each do |line|
94
+ this_sig_lines << ' ' * definition.length + options.indented(indent_level, line)
95
+ end
96
+
97
+ # Add on all this signature's lines to the complete lines
98
+ lines += this_sig_lines
99
+ end
100
+
101
+ lines
102
+ end
103
+
104
+ sig do
105
+ override.params(
106
+ others: T::Array[RbsGenerator::RbsObject]
107
+ ).returns(T::Boolean)
108
+ end
109
+ # Given an array of {Method} instances, returns true if they may be merged
110
+ # into this instance using {merge_into_self}. For instances to be
111
+ # mergeable, their signatures and definitions must be identical.
112
+ #
113
+ # @param others [Array<RbsGenerator::RbsObject>] An array of other {Method} instances.
114
+ # @return [Boolean] Whether this instance may be merged with them.
115
+ def mergeable?(others)
116
+ others.all? { |other| self == other }
117
+ end
118
+
119
+ sig do
120
+ override.params(
121
+ others: T::Array[RbsGenerator::RbsObject]
122
+ ).void
123
+ end
124
+ # Given an array of {Method} instances, merges them into this one.
125
+ # This particular implementation in fact does nothing, because {Method}
126
+ # instances are only mergeable if they are identical, so nothing needs
127
+ # to be changed.
128
+ # You MUST ensure that {mergeable?} is true for those instances.
129
+ #
130
+ # @param others [Array<RbsGenerator::RbsObject>] An array of other {Method} instances.
131
+ # @return [void]
132
+ def merge_into_self(others)
133
+ # TODO: merge signatures of different definitions
134
+ end
135
+
136
+ sig { override.returns(String) }
137
+ # Returns a human-readable brief string description of this method.
138
+ #
139
+ # @return [String]
140
+ def describe
141
+ # TODO: more info
142
+ "Method #{name} - #{signatures.length} signatures"
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,104 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbsGenerator < Generator
4
+ # Represents one signature in a method definition.
5
+ # (This is not an RbsObject because it doesn't generate a full line.)
6
+ class MethodSignature
7
+ extend T::Sig
8
+
9
+ sig do
10
+ params(
11
+ parameters: T::Array[Parameter],
12
+ return_type: T.nilable(Types::TypeLike),
13
+ block: T.nilable(Block),
14
+ type_parameters: T.nilable(T::Array[Symbol])
15
+ ).void
16
+ end
17
+ # Creates a new method signature.
18
+ #
19
+ # @param parameters [Array<Parameter>] An array of {Parameter} instances representing this
20
+ # method's parameters.
21
+ # @param return_type [Types::TypeLike, nil] What this method returns. Passing nil denotes a void return.
22
+ # @param block [Types::TypeLike, nil] The block this method accepts. Passing nil denotes none.
23
+ # @param type_parameters [Array<Symbol>, nil] This method's type parameters.
24
+ # @return [void]
25
+ def initialize(parameters, return_type = nil, block: nil, type_parameters: nil)
26
+ @parameters = parameters
27
+ @return_type = return_type
28
+ @block = block
29
+ @type_parameters = type_parameters || []
30
+ end
31
+
32
+ sig { overridable.params(other: Object).returns(T::Boolean).checked(:never) }
33
+ # Returns true if this instance is equal to another method signature.
34
+ #
35
+ # @param other [Object] The other instance. If this is not a {MethodSignature} (or a
36
+ # subclass of it), this will always return false.
37
+ # @return [Boolean]
38
+ def ==(other)
39
+ MethodSignature === other &&
40
+ parameters == other.parameters &&
41
+ return_type == other.return_type &&
42
+ block == other.block &&
43
+ type_parameters == other.type_parameters
44
+ end
45
+
46
+ sig { returns(T::Array[Parameter]) }
47
+ # An array of {Parameter} instances representing this method's parameters.
48
+ # @return [Array<Parameter>]
49
+ attr_reader :parameters
50
+
51
+ sig { returns(T.nilable(Types::TypeLike)) }
52
+ # What this method returns. Passing nil denotes a void return.
53
+ # @return [Types::TypeLike, nil]
54
+ attr_reader :return_type
55
+
56
+ sig { returns(T.nilable(Block)) }
57
+ # The block this method accepts.
58
+ # @return [Block, nil]
59
+ attr_reader :block
60
+
61
+ sig { returns(T::Array[Symbol]) }
62
+ # This method's type parameters.
63
+ # @return [Array<Symbol>]
64
+ attr_reader :type_parameters
65
+
66
+ sig { params(options: Options).returns(T::Array[String]) }
67
+ # Generates the RBS string for this signature.
68
+ #
69
+ # @param options [Options] The formatting options to use.
70
+ # @return [Array<String>] The RBS string, formatted as specified.
71
+ def generate_rbs(options)
72
+ block_type = @block&.generate_rbs(options)
73
+
74
+ rbs_params = parameters.reject { |x| x.kind == :block }.map(&:to_rbs_param)
75
+ rbs_return_type = String === @return_type ? @return_type : @return_type&.generate_rbs
76
+
77
+ generated_params = parameters.length >= options.break_params \
78
+ ? ["("] +
79
+ (
80
+ parameters.empty? ? [] : rbs_params.map.with_index do |x, i|
81
+ options.indented(
82
+ 1,
83
+ # Don't include the comma on the last parameter.
84
+ parameters.length == i + 1 ? "#{x}" : "#{x},"
85
+ )
86
+ end
87
+ ) +
88
+ [")"]
89
+
90
+ : ["(#{rbs_params.join(', ')})"]
91
+
92
+ generated_params[0] = "#{
93
+ type_parameters.any? ? "[#{type_parameters.join(', ')}] " : ''
94
+ }" + T.must(generated_params[0])
95
+
96
+ generated_params[-1] = T.must(generated_params[-1]) + "#{
97
+ (block_type && block_type != 'untyped') ? block_type.first : '' # TODO: doesn't support multi-line block types
98
+ } -> #{rbs_return_type || 'void'}"
99
+
100
+ generated_params
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,35 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbsGenerator < Generator
4
+ # Represents a module definition.
5
+ class ModuleNamespace < Namespace
6
+ extend T::Sig
7
+
8
+ sig do
9
+ override.params(
10
+ indent_level: Integer,
11
+ options: Options
12
+ ).returns(T::Array[String])
13
+ end
14
+ # Generates the RBS lines for this module.
15
+ #
16
+ # @param indent_level [Integer] The indentation level to generate the lines at.
17
+ # @param options [Options] The formatting options to use.
18
+ # @return [Array<String>] The RBS lines, formatted as specified.
19
+ def generate_rbs(indent_level, options)
20
+ lines = generate_comments(indent_level, options)
21
+ lines << options.indented(indent_level, "module #{name}")
22
+ lines += generate_body(indent_level + 1, options)
23
+ lines << options.indented(indent_level, "end")
24
+ end
25
+
26
+ sig { override.returns(String) }
27
+ # Returns a human-readable brief string description of this module.
28
+ # @return [String]
29
+ def describe
30
+ "Module #{name} - #{children.length} " +
31
+ "children, #{includes.length} includes, #{extends.length} extends"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,627 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbsGenerator < Generator
4
+ # A generic namespace. This shouldn't be used, except as the type of
5
+ # {RbsGenerator#root}.
6
+ class Namespace < RbsObject
7
+ extend T::Sig
8
+
9
+ sig do
10
+ override.overridable.params(
11
+ indent_level: Integer,
12
+ options: Options
13
+ ).returns(T::Array[String])
14
+ end
15
+ # Generates the RBS lines for this namespace.
16
+ #
17
+ # @param indent_level [Integer] The indentation level to generate the lines at.
18
+ # @param options [Options] The formatting options to use.
19
+ # @return [Array<String>] The RBS lines, formatted as specified.
20
+ def generate_rbs(indent_level, options)
21
+ generate_comments(indent_level, options) +
22
+ generate_body(indent_level, options)
23
+ end
24
+
25
+ sig do
26
+ params(
27
+ generator: Generator,
28
+ name: T.nilable(String),
29
+ block: T.nilable(T.proc.params(x: Namespace).void)
30
+ ).void
31
+ end
32
+ # Creates a new namespace.
33
+ # @note Unless you're doing something impressively hacky, this shouldn't
34
+ # be invoked outside of {RbsGenerator#initialize}.
35
+ #
36
+ # @param generator [RbsGenerator] The current RbsGenerator.
37
+ # @param name [String, nil] The name of this module.
38
+ # @param final [Boolean] Whether this namespace is final.
39
+ # @param block A block which the new instance yields itself to.
40
+ # @return [void]
41
+ def initialize(generator, name = nil, &block)
42
+ super(generator, name || '<anonymous namespace>')
43
+ @children = []
44
+ @next_comments = []
45
+ yield_self(&block) if block
46
+ end
47
+
48
+ sig { returns(T::Array[RbsObject]) }
49
+ # The child {RbsObject} instances inside this namespace.
50
+ # @return [Array<RbsObject>]
51
+ attr_reader :children
52
+
53
+ sig { returns(T::Array[RbsGenerator::Extend]) }
54
+ # The {RbsGenerator::Extend} objects from {children}.
55
+ # @return [Array<RbsGenerator::Extend>]
56
+ def extends
57
+ T.cast(
58
+ children.select { |c| c.is_a?(RbsGenerator::Extend) },
59
+ T::Array[RbsGenerator::Extend]
60
+ )
61
+ end
62
+
63
+ sig { returns(T::Array[RbsGenerator::Include]) }
64
+ # The {RbsGenerator::Include} objects from {children}.
65
+ # @return [Array<RbsGenerator::Include>]
66
+ def includes
67
+ T.cast(
68
+ children.select { |c| c.is_a?(RbsGenerator::Include) },
69
+ T::Array[RbsGenerator::Include]
70
+ )
71
+ end
72
+
73
+ sig { returns(T::Array[RbsGenerator::TypeAlias]) }
74
+ # The {RbsGenerator::TypeAlias} objects from {children}.
75
+ # @return [Array<RbsGenerator::TypeAlias>]
76
+ def aliases
77
+ T.cast(
78
+ children.select { |c| c.is_a?(RbsGenerator::TypeAlias) },
79
+ T::Array[RbsGenerator::TypeAlias]
80
+ )
81
+ end
82
+ alias type_aliases aliases
83
+
84
+ sig { returns(T::Array[RbsGenerator::Constant]) }
85
+ # The {RbsGenerator::Constant} objects from {children}.
86
+ # @return [Array<RbsGenerator::Constant>]
87
+ def constants
88
+ T.cast(
89
+ children.select { |c| c.is_a?(RbsGenerator::Constant) },
90
+ T::Array[RbsGenerator::Constant]
91
+ )
92
+ end
93
+
94
+ sig { params(object: T.untyped, block: T.proc.params(x: Namespace).void).void }
95
+ # Given a Class or Module object, generates all classes and modules in the
96
+ # path to that object, then executes the given block on the last
97
+ # {Namespace}. This should only be executed on the root namespace.
98
+ # @param [Class, Module] object
99
+ # @param block A block which the new {Namespace} yields itself to.
100
+ def path(object, &block)
101
+ raise 'only call #path on root' if is_a?(ClassNamespace) || is_a?(ModuleNamespace)
102
+
103
+ parts = object.to_s.split('::')
104
+ parts_with_types = parts.size.times.map do |i|
105
+ [parts[i], Module.const_get(parts[0..i].join('::')).class]
106
+ end
107
+
108
+ current_part = self
109
+ parts_with_types.each do |(name, type)|
110
+ if type == Class
111
+ current_part = current_part.create_class(name)
112
+ elsif type == Module
113
+ current_part = current_part.create_module(name)
114
+ else
115
+ raise "unexpected type: path part #{name} is a #{type}"
116
+ end
117
+ end
118
+
119
+ block.call(current_part)
120
+ end
121
+
122
+ sig { params(comment: T.any(String, T::Array[String])).void }
123
+ # Adds one or more comments to the next child RBS object to be created.
124
+ #
125
+ # @example Creating a module with a comment.
126
+ # namespace.add_comment_to_next_child('This is a module')
127
+ # namespace.create_module('M')
128
+ #
129
+ # @example Creating a class with a multi-line comment.
130
+ # namespace.add_comment_to_next_child(['This is a multi-line comment!', 'It can be as long as you want!'])
131
+ # namespace.create_class('C')
132
+ #
133
+ # @param comment [String, Array<String>] The new comment(s).
134
+ # @return [void]
135
+ def add_comment_to_next_child(comment)
136
+ if comment.is_a?(String)
137
+ @next_comments << comment
138
+ elsif comment.is_a?(Array)
139
+ @next_comments.concat(comment)
140
+ end
141
+ end
142
+
143
+ sig do
144
+ params(
145
+ name: String,
146
+ superclass: T.nilable(Types::TypeLike),
147
+ block: T.nilable(T.proc.params(x: ClassNamespace).void)
148
+ ).returns(ClassNamespace)
149
+ end
150
+ # Creates a new class definition as a child of this namespace.
151
+ #
152
+ # @example Create a class with a nested module.
153
+ # namespace.create_class('Foo') do |foo|
154
+ # foo.create_module('Bar')
155
+ # end
156
+ #
157
+ # @example Create a class that is the child of another class.
158
+ # namespace.create_class('Bar', superclass: 'Foo') #=> class Bar < Foo
159
+ #
160
+ # @param name [String] The name of this class.
161
+ # @param superclass [String, nil] The superclass of this class, or nil if it doesn't
162
+ # have one.
163
+ # @param block A block which the new instance yields itself to.
164
+ # @return [ClassNamespace]
165
+ def create_class(name, superclass: nil, &block)
166
+ new_class = ClassNamespace.new(generator, name, superclass, &block)
167
+ move_next_comments(new_class)
168
+ children << new_class
169
+ new_class
170
+ end
171
+
172
+ sig do
173
+ params(
174
+ name: String,
175
+ block: T.nilable(T.proc.params(x: Namespace).void)
176
+ ).returns(ModuleNamespace)
177
+ end
178
+ # Creates a new module definition as a child of this namespace.
179
+ #
180
+ # @example Create a basic module.
181
+ # namespace.create_module('Foo')
182
+ #
183
+ # @param name [String] The name of this module.
184
+ # @param block A block which the new instance yields itself to.
185
+ # @return [ModuleNamespace]
186
+ def create_module(name, &block)
187
+ new_module = ModuleNamespace.new(generator, name, &block)
188
+ move_next_comments(new_module)
189
+ children << new_module
190
+ new_module
191
+ end
192
+
193
+ sig do
194
+ params(
195
+ name: String,
196
+ block: T.nilable(T.proc.params(x: Namespace).void)
197
+ ).returns(InterfaceNamespace)
198
+ end
199
+ # Creates a new interface definition as a child of this namespace.
200
+ #
201
+ # @example Create a basic interface.
202
+ # namespace.create_interface('Foo')
203
+ #
204
+ # @param name [String] The name of this interface.
205
+ # @param block A block which the new instance yields itself to.
206
+ # @return [InterfaceNamespace]
207
+ def create_interface(name, &block)
208
+ new_interface = InterfaceNamespace.new(generator, name, &block)
209
+ move_next_comments(new_interface)
210
+ children << new_interface
211
+ new_interface
212
+ end
213
+
214
+ sig do
215
+ params(
216
+ name: String,
217
+ signatures: T.nilable(T::Array[MethodSignature]),
218
+ class_method: T::Boolean,
219
+ block: T.nilable(T.proc.params(x: Method).void)
220
+ ).returns(Method)
221
+ end
222
+ # Creates a new method definition as a child of this namespace.
223
+ #
224
+ # @param name [String] The name of this method. You should not specify +self.+ in
225
+ # this - use the +class_method+ parameter instead.
226
+ # @param signatures [Array<MethodSignature>] The signatures for each
227
+ # overload of this method.
228
+ # @param class_method [Boolean] Whether this method is a class method; that is, it
229
+ # it is defined using +self.+.
230
+ # @param block A block which the new instance yields itself to.
231
+ # @return [Method]
232
+ def create_method(name, signatures = nil, class_method: false, &block)
233
+ raise 'cannot have a method with no signatures' if signatures&.empty?
234
+ new_method = RbsGenerator::Method.new(
235
+ generator,
236
+ name,
237
+ signatures || [MethodSignature.new([], nil)],
238
+ class_method: class_method,
239
+ &block
240
+ )
241
+ move_next_comments(new_method)
242
+ children << new_method
243
+ new_method
244
+ end
245
+
246
+ sig do
247
+ params(
248
+ name: String,
249
+ kind: Symbol,
250
+ type: Types::TypeLike,
251
+ block: T.nilable(T.proc.params(x: Attribute).void)
252
+ ).returns(Attribute)
253
+ end
254
+ # Creates a new attribute.
255
+ #
256
+ # @example Create an +attr_reader+.
257
+ # module.create_attribute('readable', kind: :reader, type: 'String')
258
+ # # #=> attr_reader readable: String
259
+ #
260
+ # @example Create an +attr_writer+.
261
+ # module.create_attribute('writable', kind: :writer, type: 'Integer')
262
+ # # #=> attr_writer writable: Integer
263
+ #
264
+ # @example Create an +attr_accessor+.
265
+ # module.create_attribute('accessible', kind: :accessor, type: Types::Boolean.new)
266
+ # # #=> attr_accessor accessible: bool
267
+ #
268
+ # @param name [String] The name of this attribute.
269
+ # @param kind [Symbol] The kind of attribute this is; one of +:writer+, +:reader+, or
270
+ # +:accessor+.
271
+ # @param type [Types::TypeLike] This attribute's type.
272
+ # @param class_attribute [Boolean] Whether this attribute belongs to the
273
+ # singleton class.
274
+ # @param block A block which the new instance yields itself to.
275
+ # @return [RbsGenerator::Attribute]
276
+ def create_attribute(name, kind:, type:, &block)
277
+ new_attribute = RbsGenerator::Attribute.new(
278
+ generator,
279
+ name,
280
+ kind,
281
+ type,
282
+ &block
283
+ )
284
+ move_next_comments(new_attribute)
285
+ children << new_attribute
286
+ new_attribute
287
+ end
288
+ alias_method :create_attr, :create_attribute
289
+
290
+ sig do
291
+ params(
292
+ name: String,
293
+ type: Types::TypeLike,
294
+ block: T.nilable(T.proc.params(x: Attribute).void)
295
+ ).returns(Attribute)
296
+ end
297
+ # Creates a new read-only attribute (+attr_reader+).
298
+ #
299
+ # @param name [String] The name of this attribute.
300
+ # @param type [Types::TypeLike] This attribute's type.
301
+ # @param class_attribute [Boolean] Whether this attribute belongs to the
302
+ # singleton class.
303
+ # @param block A block which the new instance yields itself to.
304
+ # @return [RbsGenerator::Attribute]
305
+ def create_attr_reader(name, type:, &block)
306
+ create_attribute(name, kind: :reader, type: type, &block)
307
+ end
308
+
309
+ sig do
310
+ params(
311
+ name: String,
312
+ type: Types::TypeLike,
313
+ block: T.nilable(T.proc.params(x: Attribute).void)
314
+ ).returns(Attribute)
315
+ end
316
+ # Creates a new write-only attribute (+attr_writer+).
317
+ #
318
+ # @param name [String] The name of this attribute.
319
+ # @param type [Types::TypeLike] This attribute's type.
320
+ # @param class_attribute [Boolean] Whether this attribute belongs to the
321
+ # singleton class.
322
+ # @param block A block which the new instance yields itself to.
323
+ # @return [RbsGenerator::Attribute]
324
+ def create_attr_writer(name, type:, &block)
325
+ create_attribute(name, kind: :writer, type: type, &block)
326
+ end
327
+
328
+ sig do
329
+ params(
330
+ name: String,
331
+ type: Types::TypeLike,
332
+ block: T.nilable(T.proc.params(x: Attribute).void)
333
+ ).returns(Attribute)
334
+ end
335
+ # Creates a new read and write attribute (+attr_accessor+).
336
+ #
337
+ # @param name [String] The name of this attribute.
338
+ # @param type [Types::TypeLike] This attribute's type.
339
+ # @param class_attribute [Boolean] Whether this attribute belongs to the
340
+ # singleton class.
341
+ # @param block A block which the new instance yields itself to.
342
+ # @return [RbsGenerator::Attribute]
343
+ def create_attr_accessor(name, type:, &block)
344
+ create_attribute(name, kind: :accessor, type: type, &block)
345
+ end
346
+
347
+ # Creates a new arbitrary code section.
348
+ # You should rarely have to use this!
349
+ #
350
+ # @param code [String] The code to insert.
351
+ # @param block A block which the new instance yields itself to.
352
+ # @return [RbsGenerator::Arbitrary]
353
+ def create_arbitrary(code:, &block)
354
+ new_arbitrary = RbsGenerator::Arbitrary.new(
355
+ generator,
356
+ code: code,
357
+ &block
358
+ )
359
+ move_next_comments(new_arbitrary)
360
+ children << new_arbitrary
361
+ new_arbitrary
362
+ end
363
+
364
+ sig { params(type: Types::TypeLike, block: T.nilable(T.proc.params(x: Extend).void)).returns(RbsGenerator::Extend) }
365
+ # Adds a new +extend+ to this namespace.
366
+ #
367
+ # @example Add an +extend+ to a class.
368
+ # class.create_extend('ExtendableClass') #=> extend ExtendableClass
369
+ #
370
+ # @param type [Types::TypeLike] The type to extend.
371
+ # @param block A block which the new instance yields itself to.
372
+ # @return [RbsGenerator::Extend]
373
+ def create_extend(type, &block)
374
+ new_extend = RbsGenerator::Extend.new(
375
+ generator,
376
+ type: type,
377
+ &block
378
+ )
379
+ move_next_comments(new_extend)
380
+ children << new_extend
381
+ new_extend
382
+ end
383
+
384
+ sig { params(extendables: T::Array[Types::TypeLike]).returns(T::Array[Extend]) }
385
+ # Adds new +extend+s to this namespace.
386
+ #
387
+ # @example Add +extend+s to a class.
388
+ # class.create_extends(['Foo', 'Bar'])
389
+ #
390
+ # @param [Array<Types::TypeLike>] extendables An array of types to extend.
391
+ # @return [Array<RbsGenerator::Extend>]
392
+ def create_extends(extendables)
393
+ returned_extendables = []
394
+ extendables.each do |extendable|
395
+ returned_extendables << create_extend(extendable)
396
+ end
397
+ returned_extendables
398
+ end
399
+
400
+ sig { params(type: Types::TypeLike, block: T.nilable(T.proc.params(x: Include).void)).returns(Include) }
401
+ # Adds a new +include+ to this namespace.
402
+ #
403
+ # @example Add an +include+ to a class.
404
+ # class.create_include('IncludableClass') #=> include IncludableClass
405
+ #
406
+ # @param type [Types::TypeLike] The type to extend.
407
+ # @param block A block which the new instance yields itself to.
408
+ # @return [RbsGenerator::Include]
409
+ def create_include(type, &block)
410
+ new_include = RbsGenerator::Include.new(
411
+ generator,
412
+ type: type,
413
+ &block
414
+ )
415
+ move_next_comments(new_include)
416
+ children << new_include
417
+ new_include
418
+ end
419
+
420
+ sig { params(includables: T::Array[Types::TypeLike]).returns(T::Array[Include]) }
421
+ # Adds new +include+s to this namespace.
422
+ #
423
+ # @example Add +include+s to a class.
424
+ # class.create_includes(['Foo', 'Bar'])
425
+ #
426
+ # @param [Array<Types::TypeLike>] includables An array of types to extend.
427
+ # @return [Array<RbsGenerator::Include>]
428
+ def create_includes(includables)
429
+ returned_includables = []
430
+ includables.each do |includable|
431
+ returned_includables << create_include(includable)
432
+ end
433
+ returned_includables
434
+ end
435
+
436
+ sig { params(name: String, type: Types::TypeLike, block: T.nilable(T.proc.params(x: Constant).void)).returns(Constant) }
437
+ # Adds a new constant definition to this namespace.
438
+ #
439
+ # @example Add an +Elem+ constant to the class.
440
+ # class.create_constant('FOO', type: 'String') #=> FOO: String
441
+ #
442
+ # @param name [String] The name of the constant.
443
+ # @param type [Types::TypeLike] The type of the constant, as a Ruby code string.
444
+ # @param block A block which the new instance yields itself to.
445
+ # @return [RbsGenerator::Constant]
446
+ def create_constant(name, type:, &block)
447
+ new_constant = RbsGenerator::Constant.new(
448
+ generator,
449
+ name,
450
+ type: type,
451
+ &block
452
+ )
453
+ move_next_comments(new_constant)
454
+ children << new_constant
455
+ new_constant
456
+ end
457
+
458
+ sig { params(name: String, type: Types::TypeLike, block: T.nilable(T.proc.params(x: TypeAlias).void)).returns(TypeAlias) }
459
+ # Adds a new type alias, in the form of a constant, to this namespace.
460
+ #
461
+ # @example Add a +MyType+ type alias, to +Integer+, to the class.
462
+ # class.create_type_alias('MyType', type: 'Integer') #=> type MyType = Integer
463
+ #
464
+ # @param name [String] The name of the type alias.
465
+ # @param value [Types::TypeLike] The type to alias.
466
+ # @param block A block which the new instance yields itself to.
467
+ # @return [RbsGenerator::TypeAlias]
468
+ def create_type_alias(name, type:, &block)
469
+ new_type_alias = TypeAlias.new(
470
+ generator,
471
+ name: name,
472
+ type: type,
473
+ &block
474
+ )
475
+ move_next_comments(new_type_alias)
476
+ children << new_type_alias
477
+ new_type_alias
478
+ end
479
+
480
+ sig do
481
+ override.overridable.params(
482
+ others: T::Array[RbsGenerator::RbsObject]
483
+ ).returns(T::Boolean)
484
+ end
485
+ # Given an array of {Namespace} instances, returns true if they may be
486
+ # merged into this instance using {merge_into_self}. All bare namespaces
487
+ # can be merged into each other, as they lack definitions for themselves,
488
+ # so there is nothing to conflict. (This isn't the case for subclasses
489
+ # such as {ClassNamespace}.)
490
+ #
491
+ # @param others [Array<RbsGenerator::RbsObject>] An array of other {Namespace} instances.
492
+ # @return [true] Always true.
493
+ def mergeable?(others)
494
+ true
495
+ end
496
+
497
+ sig do
498
+ override.overridable.params(
499
+ others: T::Array[RbsGenerator::RbsObject]
500
+ ).void
501
+ end
502
+ # Given an array of {Namespace} instances, merges them into this one.
503
+ # All children, constants, extends and includes are copied into this
504
+ # instance.
505
+ #
506
+ # There may also be {RbsGenerator::Method} instances in the stream, which
507
+ # are ignored.
508
+ #
509
+ # @param others [Array<RbsGenerator::RbsObject>] An array of other {Namespace} instances.
510
+ # @return [void]
511
+ def merge_into_self(others)
512
+ others.each do |other|
513
+ next if other.is_a?(RbsGenerator::Method)
514
+ other = T.cast(other, Namespace)
515
+
516
+ other.children.each { |c| children << c }
517
+ end
518
+ end
519
+
520
+ sig { override.overridable.returns(String) }
521
+ # Returns a human-readable brief string description of this namespace.
522
+ #
523
+ # @return [String]
524
+ def describe
525
+ "Namespace #{name} - #{children.length} children, #{includes.length} " +
526
+ "includes, #{extends.length} extends, #{constants.length} constants"
527
+ end
528
+
529
+ private
530
+
531
+ sig do
532
+ overridable.params(
533
+ indent_level: Integer,
534
+ options: Options,
535
+ ).returns(T::Array[String])
536
+ end
537
+ # Generates the RBS lines for the body of this namespace. This consists of
538
+ # {includes}, {extends} and {children}.
539
+ #
540
+ # @param indent_level [Integer] The indentation level to generate the lines at.
541
+ # @param options [Options] The formatting options to use.
542
+ # @param mode [Symbol] The symbol to send to generate children: one of
543
+ # :generate_rbs or :generate_rbs.
544
+ # @return [Array<String>] The RBS lines for the body, formatted as specified.
545
+ def generate_body(indent_level, options)
546
+ result = []
547
+
548
+ if includes.any? || extends.any? || aliases.any? || constants.any?
549
+ result += (options.sort_namespaces \
550
+ ? includes.sort_by { |x| t = x.type; String === t ? t : t.generate_rbs }
551
+ : includes)
552
+ .flat_map { |x| x.generate_rbs(indent_level, options) }
553
+ .reject { |x| x.strip == '' }
554
+ result += (options.sort_namespaces \
555
+ ? extends.sort_by { |x| t = x.type; String === t ? t : t.generate_rbs }
556
+ : extends)
557
+ .flat_map { |x| x.generate_rbs(indent_level, options) }
558
+ .reject { |x| x.strip == '' }
559
+ result += (options.sort_namespaces \
560
+ ? aliases.sort_by { |x| t = x.type; String === t ? t : t.generate_rbs }
561
+ : aliases)
562
+ .flat_map { |x| x.generate_rbs(indent_level, options) }
563
+ .reject { |x| x.strip == '' }
564
+ result += (options.sort_namespaces \
565
+ ? constants.sort_by { |x| t = x.type; String === t ? t : t.generate_rbs }
566
+ : constants)
567
+ .flat_map { |x| x.generate_rbs(indent_level, options) }
568
+ .reject { |x| x.strip == '' }
569
+ result << ""
570
+ end
571
+
572
+ # Sort children
573
+ sorted_children = (
574
+ if options.sort_namespaces
575
+ # sort_by can be unstable (and is in current MRI).
576
+ # Use the this work around to preserve order for ties
577
+ children.sort_by.with_index { |child, i| [child.name, i] }
578
+ else
579
+ children
580
+ end
581
+ )
582
+
583
+ first, *rest = sorted_children.reject do |child|
584
+ # We already processed these kinds of children
585
+ child.is_a?(Include) || child.is_a?(Extend) || child.is_a?(Constant) || child.is_a?(TypeAlias)
586
+ end.reject do |child|
587
+ next if is_a?(ClassNamespace) || is_a?(ModuleNamespace) # next if this is not root
588
+
589
+ if child.is_a?(RbsGenerator::Method)
590
+ unless $VERBOSE.nil?
591
+ print Rainbow("Parlour warning: ").yellow.dark.bold
592
+ print Rainbow("RBS generation: ").magenta.bright.bold
593
+ puts "RBS does not support top-level method definitions, ignoring #{child.name}"
594
+ print Rainbow(" └ at object: ").blue.bright.bold
595
+ puts describe
596
+ end
597
+ next true
598
+ end
599
+
600
+ false
601
+ end
602
+ unless first
603
+ # Remove any trailing whitespace due to includes or class attributes
604
+ result.pop while result.last == ''
605
+ return result
606
+ end
607
+
608
+ result += first.generate_rbs(indent_level, options) + T.must(rest)
609
+ .map { |obj| obj.generate_rbs(indent_level, options) }
610
+ .map { |lines| [""] + lines }
611
+ .flatten
612
+
613
+ result
614
+ end
615
+
616
+ sig { params(object: RbsObject).void }
617
+ # Copies the comments added with {#add_comment_to_next_child} into the
618
+ # given object, and clears the list of pending comments.
619
+ # @param object [RbsObject] The object to move the comments into.
620
+ # @return [void]
621
+ def move_next_comments(object)
622
+ object.comments.unshift(*@next_comments)
623
+ @next_comments.clear
624
+ end
625
+ end
626
+ end
627
+ end