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