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
@@ -1,6 +1,7 @@
1
1
  # typed: true
2
+ require 'rainbow'
2
3
  module Parlour
3
- class RbiGenerator
4
+ class RbiGenerator < Generator
4
5
  # Represents a method parameter with a Sorbet type signature.
5
6
  class Parameter
6
7
  extend T::Sig
@@ -8,7 +9,7 @@ module Parlour
8
9
  sig do
9
10
  params(
10
11
  name: String,
11
- type: T.nilable(String),
12
+ type: T.nilable(Types::TypeLike),
12
13
  default: T.nilable(String)
13
14
  ).void
14
15
  end
@@ -80,7 +81,7 @@ module Parlour
80
81
  T.must(name[prefix.length..-1])
81
82
  end
82
83
 
83
- sig { returns(String) }
84
+ sig { returns(Types::TypeLike) }
84
85
  # A Sorbet string of this parameter's type, such as +"String"+ or
85
86
  # +"T.untyped"+.
86
87
  # @return [String]
@@ -118,8 +119,8 @@ module Parlour
118
119
  #
119
120
  # @return [String]
120
121
  def to_sig_param
121
- "#{name_without_kind}: #{type}"
122
- end#
122
+ "#{name_without_kind}: #{String === @type ? @type : @type.generate_rbi}"
123
+ end
123
124
 
124
125
  # A mapping of {kind} values to the characteristic prefixes each kind has.
125
126
  PREFIXES = {
@@ -128,6 +129,11 @@ module Parlour
128
129
  double_splat: '**',
129
130
  block: '&'
130
131
  }.freeze
132
+
133
+ sig { void }
134
+ def generalize_from_rbi!
135
+ @type = TypeParser.parse_single_type(@type) if String === @type
136
+ end
131
137
  end
132
138
  end
133
139
  end
@@ -1,17 +1,15 @@
1
1
  # typed: true
2
2
  module Parlour
3
- class RbiGenerator
3
+ class RbiGenerator < Generator
4
4
  # An abstract class which is subclassed by any classes which can generate
5
5
  # entire lines of an RBI, such as {Namespace} and {Method}. (As an example,
6
6
  # {Parameter} is _not_ a subclass because it does not generate lines, only
7
7
  # segments of definition and signature lines.)
8
8
  # @abstract
9
- class RbiObject
10
- extend T::Helpers
11
- extend T::Sig
9
+ class RbiObject < TypedObject
12
10
  abstract!
13
-
14
- sig { params(generator: RbiGenerator, name: String).void }
11
+
12
+ sig { params(generator: Generator, name: String).void }
15
13
  # Creates a new RBI object.
16
14
  # @note Don't call this directly.
17
15
  #
@@ -19,60 +17,16 @@ module Parlour
19
17
  # @param name [String] The name of this module.
20
18
  # @return [void]
21
19
  def initialize(generator, name)
20
+ super(name)
22
21
  @generator = generator
23
- @generated_by = generator.current_plugin
24
- @name = name
25
- @comments = []
22
+ @generated_by = RbiGenerator === generator ? generator.current_plugin : nil
26
23
  end
27
24
 
28
- sig { returns(RbiGenerator) }
25
+ sig { returns(Generator) }
29
26
  # The generator which this object belongs to.
30
- # @return [RbiGenerator]
27
+ # @return [Generator]
31
28
  attr_reader :generator
32
29
 
33
- sig { returns(T.nilable(Plugin)) }
34
- # The {Plugin} which was controlling the {generator} when this object was
35
- # created.
36
- # @return [Plugin, nil]
37
- attr_reader :generated_by
38
-
39
- sig { returns(String) }
40
- # The name of this object.
41
- # @return [String]
42
- attr_reader :name
43
-
44
- sig { returns(T::Array[String]) }
45
- # An array of comments which will be placed above the object in the RBI
46
- # file.
47
- # @return [Array<String>]
48
- attr_reader :comments
49
-
50
- sig { params(comment: T.any(String, T::Array[String])).void }
51
- # Adds one or more comments to this RBI object. Comments always go above
52
- # the definition for this object, not in the definition's body.
53
- #
54
- # @example Creating a module with a comment.
55
- # namespace.create_module('M') do |m|
56
- # m.add_comment('This is a module')
57
- # end
58
- #
59
- # @example Creating a class with a multi-line comment.
60
- # namespace.create_class('C') do |c|
61
- # c.add_comment(['This is a multi-line comment!', 'It can be as long as you want!'])
62
- # end
63
- #
64
- # @param comment [String, Array<String>] The new comment(s).
65
- # @return [void]
66
- def add_comment(comment)
67
- if comment.is_a?(String)
68
- comments << comment
69
- elsif comment.is_a?(Array)
70
- comments.concat(comment)
71
- end
72
- end
73
-
74
- alias_method :add_comments, :add_comment
75
-
76
30
  sig do
77
31
  abstract.params(
78
32
  indent_level: Integer,
@@ -115,32 +69,19 @@ module Parlour
115
69
  # @return [void]
116
70
  def merge_into_self(others); end
117
71
 
118
- sig { abstract.returns(String) }
119
- # Returns a human-readable brief string description of this object. This
120
- # is displayed during manual conflict resolution with the +parlour+ CLI.
121
- #
122
- # @abstract
123
- # @return [String]
124
- def describe; end
125
-
126
- private
127
-
128
- sig do
129
- params(
130
- indent_level: Integer,
131
- options: Options
132
- ).returns(T::Array[String])
72
+ sig { override.overridable.returns(String) }
73
+ def describe
74
+ 'RBI object'
133
75
  end
134
- # Generates the RBI lines for this object's comments.
76
+
77
+ sig { abstract.void }
78
+ # Assuming that the types throughout this object and its children have
79
+ # been specified as RBI-style types, generalises them into type instances
80
+ # from the {Parlour::Types} module.
135
81
  #
136
- # @param indent_level [Integer] The indentation level to generate the lines at.
137
- # @param options [Options] The formatting options to use.
138
- # @return [Array<String>] The RBI lines for each comment, formatted as specified.
139
- def generate_comments(indent_level, options)
140
- comments.any? \
141
- ? comments.map { |c| options.indented(indent_level, "# #{c}") }
142
- : []
143
- end
82
+ # @abstract
83
+ # @return [void]
84
+ def generalize_from_rbi!; end
144
85
  end
145
86
  end
146
87
  end
@@ -8,7 +8,7 @@ module Parlour
8
8
 
9
9
  sig do
10
10
  params(
11
- generator: RbiGenerator,
11
+ generator: Generator,
12
12
  name: String,
13
13
  final: T::Boolean,
14
14
  props: T::Array[StructProp],
@@ -39,7 +39,7 @@ module Parlour
39
39
  sig do
40
40
  override.params(
41
41
  indent_level: Integer,
42
- options: Options
42
+ options: Options,
43
43
  ).returns(T::Array[String])
44
44
  end
45
45
  # Generates the RBI lines for the body of this struct. This consists of
@@ -98,6 +98,13 @@ module Parlour
98
98
  @props = other.props if props.empty?
99
99
  end
100
100
  end
101
+
102
+ sig { override.void }
103
+ def generalize_from_rbi!
104
+ super
105
+
106
+ props.each(&:generalize_from_rbi!)
107
+ end
101
108
  end
102
109
  end
103
110
  end
@@ -1,6 +1,6 @@
1
1
  # typed: true
2
2
  module Parlour
3
- class RbiGenerator
3
+ class RbiGenerator < Generator
4
4
  # Represents a +T::Struct+ property.
5
5
  class StructProp
6
6
  extend T::Sig
@@ -8,7 +8,7 @@ module Parlour
8
8
  sig do
9
9
  params(
10
10
  name: String,
11
- type: String,
11
+ type: Types::TypeLike,
12
12
  optional: T.nilable(T.any(T::Boolean, Symbol)),
13
13
  enum: T.nilable(String),
14
14
  dont_store: T.nilable(T::Boolean),
@@ -28,8 +28,7 @@ module Parlour
28
28
  # https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/props/_props.rb#L31-L106
29
29
  #
30
30
  # @param name [String] The name of this property.
31
- # @param type [String] A Sorbet string of this property's type, such as
32
- # +"String"+.
31
+ # @param type [String] This property's type.
33
32
  # @return [void]
34
33
  def initialize(name, type, optional: nil, enum: nil, dont_store: nil,
35
34
  foreign: nil, default: nil, factory: nil, immutable: nil, array: nil,
@@ -77,10 +76,9 @@ module Parlour
77
76
  # @return [String]
78
77
  attr_reader :name
79
78
 
80
- sig { returns(T.nilable(String)) }
81
- # A Sorbet string of this parameter's type, such as +"String"+ or
82
- # +"T.untyped"+.
83
- # @return [String, nil]
79
+ sig { returns(Types::TypeLike) }
80
+ # This parameter's type.
81
+ # @return [Types::TypeLike, nil]
84
82
  attr_reader :type
85
83
 
86
84
  sig { returns(T.nilable(T.any(T::Boolean, Symbol))) }
@@ -122,7 +120,7 @@ module Parlour
122
120
  # Returns the +prop+ call required to create this property.
123
121
  # @return [String]
124
122
  def to_prop_call
125
- call = "prop :#{name}, #{type}"
123
+ call = "prop :#{name}, #{String === @type ? @type : @type.generate_rbi}"
126
124
 
127
125
  EXTRA_PROPERTIES.each do |extra_property|
128
126
  value = send extra_property
@@ -131,6 +129,11 @@ module Parlour
131
129
 
132
130
  call
133
131
  end
132
+
133
+ sig { void }
134
+ def generalize_from_rbi!
135
+ @type = TypeParser.parse_single_type(@type) if String === @type
136
+ end
134
137
  end
135
138
  end
136
139
  end
@@ -0,0 +1,101 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbiGenerator < Generator
4
+ # Represents a type alias.
5
+ class TypeAlias < RbiObject
6
+ sig do
7
+ params(
8
+ generator: Generator,
9
+ name: String,
10
+ type: Types::TypeLike,
11
+ block: T.nilable(T.proc.params(x: TypeAlias).void)
12
+ ).void
13
+ end
14
+ # Creates a new type alias.
15
+ #
16
+ # @param name [String] The name of the alias.
17
+ # @param value [String] The type to alias to.
18
+ def initialize(generator, name:, type:, &block)
19
+ super(generator, name)
20
+ @type = type
21
+ yield_self(&block) if block
22
+ end
23
+
24
+ # @return [String] The type to alias to.
25
+ sig { returns(Types::TypeLike) }
26
+ attr_reader :type
27
+
28
+ sig { params(other: Object).returns(T::Boolean) }
29
+ # Returns true if this instance is equal to another type alias.
30
+ #
31
+ # @param other [Object] The other instance. If this is not a {TypeAlias} (or a
32
+ # subclass of it), this will always return false.
33
+ # @return [Boolean]
34
+ def ==(other)
35
+ TypeAlias === other && name == other.name && type == other.type
36
+ end
37
+
38
+ sig do
39
+ override.params(
40
+ indent_level: Integer,
41
+ options: Options
42
+ ).returns(T::Array[String])
43
+ end
44
+ # Generates the RBI lines for this type alias.
45
+ #
46
+ # @param indent_level [Integer] The indentation level to generate the lines at.
47
+ # @param options [Options] The formatting options to use.
48
+ # @return [Array<String>] The RBI lines, formatted as specified.
49
+ def generate_rbi(indent_level, options)
50
+ [options.indented(indent_level,
51
+ "#{name} = T.type_alias { #{String === @type ? @type : @type.generate_rbi} }"
52
+ )]
53
+ end
54
+
55
+ sig do
56
+ override.params(
57
+ others: T::Array[RbiGenerator::RbiObject]
58
+ ).returns(T::Boolean)
59
+ end
60
+ # Given an array of {TypeAlias} instances, returns true if they may be
61
+ # merged into this instance using {merge_into_self}. This is always false.
62
+ #
63
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other
64
+ # {TypeAlias} instances.
65
+ # @return [Boolean] Whether this instance may be merged with them.
66
+ def mergeable?(others)
67
+ others.all? { |other| self == other }
68
+ end
69
+
70
+ sig do
71
+ override.params(
72
+ others: T::Array[RbiGenerator::RbiObject]
73
+ ).void
74
+ end
75
+ # Given an array of {TypeAlias} instances, merges them into this one.
76
+ # This particular implementation will simply do nothing, as instances
77
+ # are only mergeable if they are indentical.
78
+ # You MUST ensure that {mergeable?} is true for those instances.
79
+ #
80
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other
81
+ # {TypeAlias} instances.
82
+ # @return [void]
83
+ def merge_into_self(others)
84
+ # We don't need to change anything! We only merge identical type alias
85
+ end
86
+
87
+ sig { override.returns(String) }
88
+ # Returns a human-readable brief string description of this code.
89
+ #
90
+ # @return [String]
91
+ def describe
92
+ "Type Alias (#{name} = #{type})"
93
+ end
94
+
95
+ sig { override.void }
96
+ def generalize_from_rbi!
97
+ @type = TypeParser.parse_single_type(@type) if String === @type
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,24 @@
1
+ # typed: true
2
+ module Parlour
3
+ # The RBS generator.
4
+ class RbsGenerator < Generator
5
+ def initialize(**hash)
6
+ super
7
+ @root = RbsGenerator::Namespace.new(self)
8
+ end
9
+
10
+ sig { overridable.returns(RbsGenerator::Namespace) }
11
+ # The root {Namespace} of this generator.
12
+ # @return [Namespace]
13
+ attr_reader :root
14
+
15
+ sig { overridable.returns(String) }
16
+ # Returns the complete contents of the generated RBS file as a string.
17
+ #
18
+ # @return [String] The generated RBS file
19
+ def rbs
20
+ root.generate_rbs(0, options).join("\n")
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,92 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbsGenerator < Generator
4
+ # Represents miscellaneous Ruby code.
5
+ class Arbitrary < RbsObject
6
+ sig do
7
+ params(
8
+ generator: Generator,
9
+ code: String,
10
+ block: T.nilable(T.proc.params(x: Arbitrary).void)
11
+ ).void
12
+ end
13
+ # Creates new arbitrary code.
14
+ #
15
+ # @param code [String] The arbitrary code string. Indentation is added to
16
+ # the beginning of each line.
17
+ def initialize(generator, code: '', &block)
18
+ super(generator, '')
19
+ @code = code
20
+ yield_self(&block) if block
21
+ end
22
+
23
+ sig { returns(String) }
24
+ # Returns arbitrary code string.
25
+ attr_accessor :code
26
+
27
+ sig { params(other: Object).returns(T::Boolean) }
28
+ # Returns true if this instance is equal to another arbitrary code line.
29
+ #
30
+ # @param other [Object] The other instance. If this is not a {Arbitrary} (or a
31
+ # subclass of it), this will always return false.
32
+ # @return [Boolean]
33
+ def ==(other)
34
+ Arbitrary === other && code == other.code
35
+ end
36
+
37
+ sig do
38
+ override.params(
39
+ indent_level: Integer,
40
+ options: Options
41
+ ).returns(T::Array[String])
42
+ end
43
+ # Generates the RBS lines for this arbitrary code.
44
+ #
45
+ # @param indent_level [Integer] The indentation level to generate the lines at.
46
+ # @param options [Options] The formatting options to use.
47
+ # @return [Array<String>] The RBS lines, formatted as specified.
48
+ def generate_rbs(indent_level, options)
49
+ code.split("\n").map { |l| options.indented(indent_level, l) }
50
+ end
51
+
52
+ sig do
53
+ override.params(
54
+ others: T::Array[RbsGenerator::RbsObject]
55
+ ).returns(T::Boolean)
56
+ end
57
+ # Given an array of {Arbitrary} instances, returns true if they may be
58
+ # merged into this instance using {merge_into_self}. This is always false.
59
+ #
60
+ # @param others [Array<RbsGenerator::RbsObject>] An array of other
61
+ # {Arbitrary} instances.
62
+ # @return [Boolean] Whether this instance may be merged with them.
63
+ def mergeable?(others)
64
+ false
65
+ end
66
+
67
+ sig do
68
+ override.params(
69
+ others: T::Array[RbsGenerator::RbsObject]
70
+ ).void
71
+ end
72
+ # Given an array of {Arbitrary} instances, merges them into this one.
73
+ # This particular implementation always throws an exception, because
74
+ # {mergeable?} is always false.
75
+ #
76
+ # @param others [Array<RbsGenerator::RbsObject>] An array of other
77
+ # {Arbitrary} instances.
78
+ # @return [void]
79
+ def merge_into_self(others)
80
+ raise 'arbitrary code is never mergeable'
81
+ end
82
+
83
+ sig { override.returns(String) }
84
+ # Returns a human-readable brief string description of this code.
85
+ #
86
+ # @return [String]
87
+ def describe
88
+ "Arbitrary code (#{code})"
89
+ end
90
+ end
91
+ end
92
+ end