parlour 4.0.1 → 5.0.0.beta.1
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/CHANGELOG.md +24 -0
- data/README.md +208 -20
- data/exe/parlour +45 -6
- data/lib/parlour.rb +27 -1
- data/lib/parlour/conversion/converter.rb +34 -0
- data/lib/parlour/conversion/rbi_to_rbs.rb +223 -0
- data/lib/parlour/detached_rbs_generator.rb +25 -0
- data/lib/parlour/generator.rb +34 -0
- data/lib/parlour/options.rb +71 -0
- data/lib/parlour/rbi_generator.rb +24 -37
- data/lib/parlour/rbi_generator/arbitrary.rb +5 -2
- data/lib/parlour/rbi_generator/attribute.rb +14 -5
- data/lib/parlour/rbi_generator/class_namespace.rb +8 -3
- data/lib/parlour/rbi_generator/constant.rb +17 -6
- data/lib/parlour/rbi_generator/enum_class_namespace.rb +8 -3
- data/lib/parlour/rbi_generator/extend.rb +5 -2
- data/lib/parlour/rbi_generator/include.rb +5 -2
- data/lib/parlour/rbi_generator/method.rb +15 -10
- data/lib/parlour/rbi_generator/module_namespace.rb +7 -2
- data/lib/parlour/rbi_generator/namespace.rb +39 -12
- data/lib/parlour/rbi_generator/parameter.rb +11 -5
- data/lib/parlour/rbi_generator/rbi_object.rb +19 -78
- data/lib/parlour/rbi_generator/struct_class_namespace.rb +9 -2
- data/lib/parlour/rbi_generator/struct_prop.rb +12 -9
- data/lib/parlour/rbi_generator/type_alias.rb +101 -0
- data/lib/parlour/rbs_generator.rb +24 -0
- data/lib/parlour/rbs_generator/arbitrary.rb +92 -0
- data/lib/parlour/rbs_generator/attribute.rb +82 -0
- data/lib/parlour/rbs_generator/block.rb +49 -0
- data/lib/parlour/rbs_generator/class_namespace.rb +106 -0
- data/lib/parlour/rbs_generator/constant.rb +95 -0
- data/lib/parlour/rbs_generator/extend.rb +92 -0
- data/lib/parlour/rbs_generator/include.rb +92 -0
- data/lib/parlour/rbs_generator/interface_namespace.rb +34 -0
- data/lib/parlour/rbs_generator/method.rb +146 -0
- data/lib/parlour/rbs_generator/method_signature.rb +104 -0
- data/lib/parlour/rbs_generator/module_namespace.rb +35 -0
- data/lib/parlour/rbs_generator/namespace.rb +627 -0
- data/lib/parlour/rbs_generator/parameter.rb +145 -0
- data/lib/parlour/rbs_generator/rbs_object.rb +78 -0
- data/lib/parlour/rbs_generator/type_alias.rb +96 -0
- data/lib/parlour/type_parser.rb +152 -0
- data/lib/parlour/typed_object.rb +87 -0
- data/lib/parlour/types.rb +445 -0
- data/lib/parlour/version.rb +1 -1
- data/parlour.gemspec +1 -1
- data/rbi/parlour.rbi +982 -76
- metadata +30 -7
- data/lib/parlour/rbi_generator/options.rb +0 -74
@@ -0,0 +1,34 @@
|
|
1
|
+
# typed: true
|
2
|
+
require 'rainbow'
|
3
|
+
|
4
|
+
module Parlour
|
5
|
+
module Conversion
|
6
|
+
# An abstract class which converts between the node trees of two type
|
7
|
+
# systems.
|
8
|
+
class Converter
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Helpers
|
11
|
+
abstract!
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@warnings = []
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { returns(T::Array[[String, TypedObject]]) }
|
18
|
+
attr_reader :warnings
|
19
|
+
|
20
|
+
sig { params(msg: String, node: RbiGenerator::RbiObject).void }
|
21
|
+
def add_warning(msg, node)
|
22
|
+
warnings << [msg, node]
|
23
|
+
|
24
|
+
return if $VERBOSE.nil?
|
25
|
+
class_name = T.must(self.class.name).split('::').last
|
26
|
+
print Rainbow("Parlour warning: ").yellow.dark.bold
|
27
|
+
print Rainbow("#{class_name}: ").magenta.bright.bold
|
28
|
+
puts msg
|
29
|
+
print Rainbow(" └ at object: ").blue.bright.bold
|
30
|
+
puts node.describe
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# typed: true
|
2
|
+
module Parlour
|
3
|
+
module Conversion
|
4
|
+
# Converts RBI types to RBS types.
|
5
|
+
class RbiToRbs < Converter
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { params(rbs_gen: RbsGenerator).void }
|
9
|
+
def initialize(rbs_gen)
|
10
|
+
super()
|
11
|
+
@rbs_gen = rbs_gen
|
12
|
+
end
|
13
|
+
|
14
|
+
sig { returns(RbsGenerator) }
|
15
|
+
attr_reader :rbs_gen
|
16
|
+
|
17
|
+
sig { params(from: RbiGenerator::Namespace, to: RbsGenerator::Namespace).void }
|
18
|
+
def convert_all(from, to)
|
19
|
+
from.children.each do |child|
|
20
|
+
convert_object(child, to)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sig do
|
25
|
+
params(
|
26
|
+
node: RbiGenerator::RbiObject,
|
27
|
+
new_parent: RbsGenerator::Namespace,
|
28
|
+
).void
|
29
|
+
end
|
30
|
+
def convert_object(node, new_parent)
|
31
|
+
case node
|
32
|
+
when RbiGenerator::StructClassNamespace
|
33
|
+
add_warning 'performing a one-way conversion of an RBI struct to RBS', node
|
34
|
+
|
35
|
+
klass = new_parent.create_class(node.name)
|
36
|
+
klass.add_comments(node.comments)
|
37
|
+
|
38
|
+
# Create a constructor
|
39
|
+
klass.create_method('initialize', [
|
40
|
+
RbsGenerator::MethodSignature.new(
|
41
|
+
node.props.map do |prop|
|
42
|
+
RbsGenerator::Parameter.new(
|
43
|
+
"#{prop.name}:",
|
44
|
+
type: prop.type,
|
45
|
+
required: !prop.optional,
|
46
|
+
)
|
47
|
+
end,
|
48
|
+
nil,
|
49
|
+
)
|
50
|
+
])
|
51
|
+
|
52
|
+
# Make each prop a getter (and setter, if not immutable) attribute
|
53
|
+
node.props.each do |prop|
|
54
|
+
klass.create_attribute(
|
55
|
+
prop.name,
|
56
|
+
kind: prop.immutable ? :reader : :accessor,
|
57
|
+
type: prop.type,
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
klass
|
62
|
+
|
63
|
+
when RbiGenerator::EnumClassNamespace
|
64
|
+
add_warning 'performing a one-way conversion of an RBI enum to RBS', node
|
65
|
+
|
66
|
+
klass = new_parent.create_class(node.name)
|
67
|
+
klass.add_comments(node.comments)
|
68
|
+
|
69
|
+
# Define .values
|
70
|
+
klass.create_method('values', [
|
71
|
+
RbsGenerator::MethodSignature.new([], Types::Array.new(node.name))
|
72
|
+
], class_method: true)
|
73
|
+
|
74
|
+
# Define each enum variant
|
75
|
+
node.enums.each do |variant|
|
76
|
+
# We don't care about any extra value
|
77
|
+
variant = variant[0] if Array === variant
|
78
|
+
|
79
|
+
klass.create_constant(variant, type: node.name)
|
80
|
+
end
|
81
|
+
|
82
|
+
klass
|
83
|
+
|
84
|
+
when RbiGenerator::Arbitrary
|
85
|
+
add_warning 'converting type of Arbitrary is likely to cause syntax errors; doing it anyway', node
|
86
|
+
new_parent.create_arbitrary(
|
87
|
+
code: node.code,
|
88
|
+
).add_comments(node.comments)
|
89
|
+
|
90
|
+
when RbiGenerator::Attribute
|
91
|
+
if node.class_attribute
|
92
|
+
add_warning 'RBS does not support class attributes; dropping', node
|
93
|
+
return
|
94
|
+
end
|
95
|
+
new_parent.create_attribute(
|
96
|
+
node.name,
|
97
|
+
kind: node.kind,
|
98
|
+
type: node.type,
|
99
|
+
).add_comments(node.comments)
|
100
|
+
|
101
|
+
when RbiGenerator::ClassNamespace
|
102
|
+
if node.abstract
|
103
|
+
add_warning 'RBS does not support abstract classes', node
|
104
|
+
end
|
105
|
+
klass = new_parent.create_class(
|
106
|
+
node.name,
|
107
|
+
superclass: node.superclass
|
108
|
+
)
|
109
|
+
klass.add_comments(node.comments)
|
110
|
+
node.children.each do |child|
|
111
|
+
convert_object(child, klass)
|
112
|
+
end
|
113
|
+
|
114
|
+
when RbiGenerator::Constant
|
115
|
+
if node.eigen_constant
|
116
|
+
add_warning 'RBS does not support constants on eigenclasses; dropping', node
|
117
|
+
return
|
118
|
+
end
|
119
|
+
new_parent.create_constant(
|
120
|
+
node.name,
|
121
|
+
type: node.value,
|
122
|
+
).add_comments(node.comments)
|
123
|
+
|
124
|
+
when RbiGenerator::Extend
|
125
|
+
new_parent.create_extend(node.name).add_comments(node.comments)
|
126
|
+
|
127
|
+
when RbiGenerator::Include
|
128
|
+
new_parent.create_include(node.name).add_comments(node.comments)
|
129
|
+
|
130
|
+
when RbiGenerator::Method
|
131
|
+
# Convert parameters
|
132
|
+
parameters = node.parameters
|
133
|
+
.reject { |param| param.kind == :block }
|
134
|
+
.map do |param|
|
135
|
+
RbsGenerator::Parameter.new(
|
136
|
+
param.name,
|
137
|
+
type: param.type,
|
138
|
+
required: param.default.nil?
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Find block if there is one
|
143
|
+
block_param = node.parameters.find { |param| param.kind == :block }
|
144
|
+
if block_param
|
145
|
+
if String === block_param.type
|
146
|
+
add_warning "block must have a Types::Type for conversion; dropping block", node
|
147
|
+
block = nil
|
148
|
+
else
|
149
|
+
# A nilable proc is an optional block
|
150
|
+
block_param_type = block_param.type
|
151
|
+
if Types::Nilable === block_param_type && Types::Proc === block_param_type.type
|
152
|
+
t = T.cast(block_param_type.type, Types::Proc)
|
153
|
+
required = false
|
154
|
+
block = RbsGenerator::Block.new(t, required)
|
155
|
+
elsif Types::Proc === block_param_type
|
156
|
+
t = block_param_type
|
157
|
+
required = true
|
158
|
+
block = RbsGenerator::Block.new(t, required)
|
159
|
+
elsif Types::Untyped === block_param_type
|
160
|
+
# Consider there to be a block of unknown types
|
161
|
+
block = RbsGenerator::Block.new(
|
162
|
+
Types::Proc.new(
|
163
|
+
[
|
164
|
+
Types::Proc::Parameter.new('*args', Types::Untyped.new),
|
165
|
+
Types::Proc::Parameter.new('**kwargs', Types::Untyped.new),
|
166
|
+
],
|
167
|
+
Types::Untyped.new,
|
168
|
+
),
|
169
|
+
false,
|
170
|
+
)
|
171
|
+
else
|
172
|
+
add_warning 'block type must be a Types::Proc (or nilable one); dropping block', node
|
173
|
+
end
|
174
|
+
end
|
175
|
+
else
|
176
|
+
block = nil
|
177
|
+
end
|
178
|
+
|
179
|
+
new_parent.create_method(
|
180
|
+
node.name,
|
181
|
+
[
|
182
|
+
RbsGenerator::MethodSignature.new(
|
183
|
+
parameters,
|
184
|
+
node.return_type,
|
185
|
+
block: block,
|
186
|
+
type_parameters: node.type_parameters,
|
187
|
+
)
|
188
|
+
],
|
189
|
+
class_method: node.class_method,
|
190
|
+
).add_comments(node.comments)
|
191
|
+
|
192
|
+
when RbiGenerator::ModuleNamespace
|
193
|
+
if node.interface
|
194
|
+
rbs_node = new_parent.create_interface(
|
195
|
+
node.name,
|
196
|
+
)
|
197
|
+
else
|
198
|
+
rbs_node = new_parent.create_module(
|
199
|
+
node.name,
|
200
|
+
)
|
201
|
+
end
|
202
|
+
rbs_node.add_comments(node.comments)
|
203
|
+
node.children.each do |child|
|
204
|
+
convert_object(child, rbs_node)
|
205
|
+
end
|
206
|
+
|
207
|
+
when RbiGenerator::Namespace
|
208
|
+
add_warning 'unspecialized namespaces are not supposed to be in the tree; you may run into issues', node
|
209
|
+
namespace = RbsGenerator::Namespace.new(rbs_gen)
|
210
|
+
namespace.add_comments(node.comments)
|
211
|
+
node.children.each do |child|
|
212
|
+
convert_object(child, namespace)
|
213
|
+
end
|
214
|
+
new_parent.children << namespace
|
215
|
+
|
216
|
+
else
|
217
|
+
raise "missing conversion for #{node.describe}"
|
218
|
+
# TODO: stick a T.absurd here
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
3
|
+
module Parlour
|
4
|
+
class DetachedRbsGenerator < RbsGenerator
|
5
|
+
sig { returns(T.untyped) }
|
6
|
+
def detached!
|
7
|
+
raise "cannot call methods on a detached RBS generator"
|
8
|
+
end
|
9
|
+
|
10
|
+
sig { override.returns(Options) }
|
11
|
+
def options
|
12
|
+
detached!
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { override.returns(T.nilable(Plugin)) }
|
16
|
+
def current_plugin
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { override.returns(String) }
|
21
|
+
def rbs
|
22
|
+
detached!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# typed: true
|
2
|
+
module Parlour
|
3
|
+
class Generator
|
4
|
+
extend T::Sig
|
5
|
+
|
6
|
+
sig { params(break_params: Integer, tab_size: Integer, sort_namespaces: T::Boolean).void }
|
7
|
+
# Creates a new generator.
|
8
|
+
#
|
9
|
+
# @param break_params [Integer] If there are at least this many parameters in a
|
10
|
+
# signature, then it is broken onto separate lines.
|
11
|
+
# @param tab_size [Integer] The number of spaces to use per indent.
|
12
|
+
# @param sort_namespaces [Boolean] Whether to sort all items within a
|
13
|
+
# namespace alphabetically.
|
14
|
+
# @return [void]
|
15
|
+
def initialize(break_params: 4, tab_size: 2, sort_namespaces: false)
|
16
|
+
@options = Options.new(
|
17
|
+
break_params: break_params,
|
18
|
+
tab_size: tab_size,
|
19
|
+
sort_namespaces: sort_namespaces
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { overridable.returns(Options) }
|
24
|
+
# The formatting options for this generator. Currently ignored.
|
25
|
+
# @return [Options]
|
26
|
+
attr_reader :options
|
27
|
+
|
28
|
+
sig { overridable.returns(T.nilable(Plugin)) }
|
29
|
+
# The plugin which is currently generating new definitions.
|
30
|
+
# {Plugin#run_plugins} controls this value.
|
31
|
+
# @return [Plugin, nil]
|
32
|
+
attr_accessor :current_plugin
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# typed: true
|
2
|
+
module Parlour
|
3
|
+
# A set of immutable formatting options.
|
4
|
+
class Options
|
5
|
+
extend T::Sig
|
6
|
+
|
7
|
+
sig { params(break_params: Integer, tab_size: Integer, sort_namespaces: T::Boolean).void }
|
8
|
+
# Creates a new set of formatting options.
|
9
|
+
#
|
10
|
+
# @example Create Options with +break_params+ of +4+ and +tab_size+ of +2+.
|
11
|
+
# Parlour::Options.new(break_params: 4, tab_size: 2)
|
12
|
+
#
|
13
|
+
# @param break_params [Integer] If there are at least this many parameters in a
|
14
|
+
# signature, then it is broken onto separate lines.
|
15
|
+
# @param tab_size [Integer] The number of spaces to use per indent.
|
16
|
+
# @param sort_namespaces [Boolean] Whether to sort all items within a
|
17
|
+
# namespace alphabetically.
|
18
|
+
# @return [void]
|
19
|
+
def initialize(break_params:, tab_size:, sort_namespaces:)
|
20
|
+
@break_params = break_params
|
21
|
+
@tab_size = tab_size
|
22
|
+
@sort_namespaces = sort_namespaces
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { returns(Integer) }
|
26
|
+
# If there are at least this many parameters in a signature, then it
|
27
|
+
# is broken onto separate lines.
|
28
|
+
#
|
29
|
+
# # With break_params: 5
|
30
|
+
# sig { params(name: String, age: Integer, hobbies: T::Array(String), country: Symbol).void }
|
31
|
+
#
|
32
|
+
# # With break_params: 4
|
33
|
+
# sig do
|
34
|
+
# params(
|
35
|
+
# name: String,
|
36
|
+
# age: Integer,
|
37
|
+
# hobbies: T::Array(String),
|
38
|
+
# country: Symbol
|
39
|
+
# ).void
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# @return [Integer]
|
43
|
+
attr_reader :break_params
|
44
|
+
|
45
|
+
sig { returns(Integer) }
|
46
|
+
# The number of spaces to use per indent.
|
47
|
+
# @return [Integer]
|
48
|
+
attr_reader :tab_size
|
49
|
+
|
50
|
+
sig { returns(T::Boolean) }
|
51
|
+
# Whether to sort all items within a namespace alphabetically.
|
52
|
+
# Items which are typically grouped together, such as "include" or
|
53
|
+
# "extend" calls, will remain grouped together when sorted.
|
54
|
+
# If true, items are sorted by their name when the RBI is generated.
|
55
|
+
# If false, items are generated in the order they are added to the
|
56
|
+
# namespace.
|
57
|
+
# @return [Boolean]
|
58
|
+
attr_reader :sort_namespaces
|
59
|
+
|
60
|
+
sig { params(level: Integer, str: String).returns(String) }
|
61
|
+
# Returns a string indented to the given indent level, according to the
|
62
|
+
# set {tab_size}.
|
63
|
+
#
|
64
|
+
# @param level [Integer] The indent level, as an integer. 0 is totally unindented.
|
65
|
+
# @param str [String] The string to indent.
|
66
|
+
# @return [String] The indented string.
|
67
|
+
def indented(level, str)
|
68
|
+
" " * (level * tab_size) + str
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,55 +1,42 @@
|
|
1
1
|
# typed: true
|
2
2
|
module Parlour
|
3
3
|
# The RBI generator.
|
4
|
-
class RbiGenerator
|
5
|
-
|
4
|
+
class RbiGenerator < Generator
|
5
|
+
# For backwards compatibility.
|
6
|
+
# Before Parlour 5.0, Parlour::Options was Parlour::RbiGenerator::Options.
|
7
|
+
Options = Parlour::Options
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# @example Create a default generator.
|
11
|
-
# generator = Parlour::RbiGenerator.new
|
12
|
-
#
|
13
|
-
# @example Create a generator with a custom +tab_size+ of 3.
|
14
|
-
# generator = Parlour::RbiGenerator.new(tab_size: 3)
|
15
|
-
#
|
16
|
-
# @param break_params [Integer] If there are at least this many parameters in a
|
17
|
-
# Sorbet +sig+, then it is broken onto separate lines.
|
18
|
-
# @param tab_size [Integer] The number of spaces to use per indent.
|
19
|
-
# @param sort_namespaces [Boolean] Whether to sort all items within a
|
20
|
-
# namespace alphabetically.
|
21
|
-
# @return [void]
|
22
|
-
def initialize(break_params: 4, tab_size: 2, sort_namespaces: false)
|
23
|
-
@options = Options.new(
|
24
|
-
break_params: break_params,
|
25
|
-
tab_size: tab_size,
|
26
|
-
sort_namespaces: sort_namespaces
|
27
|
-
)
|
28
|
-
@root = Namespace.new(self)
|
9
|
+
def initialize(**hash)
|
10
|
+
super
|
11
|
+
@root = RbiGenerator::Namespace.new(self)
|
29
12
|
end
|
30
13
|
|
31
|
-
sig { overridable.returns(
|
32
|
-
# The formatting options for this generator.
|
33
|
-
# @return [Options]
|
34
|
-
attr_reader :options
|
35
|
-
|
36
|
-
sig { overridable.returns(Namespace) }
|
14
|
+
sig { overridable.returns(RbiGenerator::Namespace) }
|
37
15
|
# The root {Namespace} of this generator.
|
38
16
|
# @return [Namespace]
|
39
17
|
attr_reader :root
|
40
18
|
|
41
|
-
sig { overridable.returns(T.nilable(Plugin)) }
|
42
|
-
# The plugin which is currently generating new definitions.
|
43
|
-
# {Plugin#run_plugins} controls this value.
|
44
|
-
# @return [Plugin, nil]
|
45
|
-
attr_accessor :current_plugin
|
46
|
-
|
47
19
|
sig { overridable.params(strictness: String).returns(String) }
|
48
20
|
# Returns the complete contents of the generated RBI file as a string.
|
49
21
|
#
|
50
22
|
# @return [String] The generated RBI file
|
51
23
|
def rbi(strictness = 'strong')
|
52
|
-
|
24
|
+
# TODO: Early test option - convert to RBS if requested
|
25
|
+
# Absolutely remove this later on
|
26
|
+
if ENV['PARLOUR_CONVERT_TO_RBS']
|
27
|
+
# Perform conversion
|
28
|
+
root.generalize_from_rbi!
|
29
|
+
rbs_gen = Parlour::RbsGenerator.new
|
30
|
+
converter = Parlour::Conversion::RbiToRbs.new(rbs_gen)
|
31
|
+
root.children.each do |child|
|
32
|
+
converter.convert_object(child, rbs_gen.root)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Write the final RBS
|
36
|
+
rbs_gen.rbs
|
37
|
+
else
|
38
|
+
"# typed: #{strictness}\n" + root.generate_rbi(0, options).join("\n") + "\n"
|
39
|
+
end
|
53
40
|
end
|
54
41
|
end
|
55
42
|
end
|