parlour 2.0.0 → 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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
  3. data/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
  4. data/.gitignore +1 -1
  5. data/.parlour +5 -0
  6. data/.rspec +0 -0
  7. data/.travis.yml +3 -3
  8. data/CHANGELOG.md +64 -0
  9. data/CODE_OF_CONDUCT.md +0 -0
  10. data/Gemfile +0 -0
  11. data/LICENSE.txt +0 -0
  12. data/README.md +233 -19
  13. data/Rakefile +0 -0
  14. data/exe/parlour +109 -4
  15. data/lib/parlour.rb +29 -1
  16. data/lib/parlour/conflict_resolver.rb +75 -27
  17. data/lib/parlour/conversion/converter.rb +34 -0
  18. data/lib/parlour/conversion/rbi_to_rbs.rb +223 -0
  19. data/lib/parlour/debugging.rb +0 -0
  20. data/lib/parlour/detached_rbi_generator.rb +1 -6
  21. data/lib/parlour/detached_rbs_generator.rb +25 -0
  22. data/lib/parlour/generator.rb +34 -0
  23. data/lib/parlour/kernel_hack.rb +0 -0
  24. data/lib/parlour/options.rb +71 -0
  25. data/lib/parlour/parse_error.rb +0 -0
  26. data/lib/parlour/plugin.rb +1 -1
  27. data/lib/parlour/rbi_generator.rb +24 -37
  28. data/lib/parlour/rbi_generator/arbitrary.rb +5 -2
  29. data/lib/parlour/rbi_generator/attribute.rb +14 -5
  30. data/lib/parlour/rbi_generator/class_namespace.rb +8 -3
  31. data/lib/parlour/rbi_generator/constant.rb +28 -8
  32. data/lib/parlour/rbi_generator/enum_class_namespace.rb +32 -5
  33. data/lib/parlour/rbi_generator/extend.rb +5 -2
  34. data/lib/parlour/rbi_generator/include.rb +5 -2
  35. data/lib/parlour/rbi_generator/method.rb +15 -10
  36. data/lib/parlour/rbi_generator/module_namespace.rb +7 -2
  37. data/lib/parlour/rbi_generator/namespace.rb +115 -27
  38. data/lib/parlour/rbi_generator/parameter.rb +13 -7
  39. data/lib/parlour/rbi_generator/rbi_object.rb +19 -78
  40. data/lib/parlour/rbi_generator/struct_class_namespace.rb +110 -0
  41. data/lib/parlour/rbi_generator/struct_prop.rb +139 -0
  42. data/lib/parlour/rbi_generator/type_alias.rb +101 -0
  43. data/lib/parlour/rbs_generator.rb +24 -0
  44. data/lib/parlour/rbs_generator/arbitrary.rb +92 -0
  45. data/lib/parlour/rbs_generator/attribute.rb +82 -0
  46. data/lib/parlour/rbs_generator/block.rb +49 -0
  47. data/lib/parlour/rbs_generator/class_namespace.rb +106 -0
  48. data/lib/parlour/rbs_generator/constant.rb +95 -0
  49. data/lib/parlour/rbs_generator/extend.rb +92 -0
  50. data/lib/parlour/rbs_generator/include.rb +92 -0
  51. data/lib/parlour/rbs_generator/interface_namespace.rb +34 -0
  52. data/lib/parlour/rbs_generator/method.rb +146 -0
  53. data/lib/parlour/rbs_generator/method_signature.rb +104 -0
  54. data/lib/parlour/rbs_generator/module_namespace.rb +35 -0
  55. data/lib/parlour/rbs_generator/namespace.rb +627 -0
  56. data/lib/parlour/rbs_generator/parameter.rb +145 -0
  57. data/lib/parlour/rbs_generator/rbs_object.rb +78 -0
  58. data/lib/parlour/rbs_generator/type_alias.rb +96 -0
  59. data/lib/parlour/type_loader.rb +30 -10
  60. data/lib/parlour/type_parser.rb +440 -43
  61. data/lib/parlour/typed_object.rb +87 -0
  62. data/lib/parlour/types.rb +445 -0
  63. data/lib/parlour/version.rb +1 -1
  64. data/parlour.gemspec +2 -2
  65. data/plugin_examples/foobar_plugin.rb +0 -0
  66. data/rbi/parlour.rbi +1799 -0
  67. metadata +42 -15
  68. 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
@@ -42,7 +43,7 @@ module Parlour
42
43
 
43
44
  @kind = :keyword if kind == :normal && name.end_with?(':')
44
45
 
45
- @type = type
46
+ @type = type || 'T.untyped'
46
47
  @default = default
47
48
  end
48
49
 
@@ -80,10 +81,10 @@ module Parlour
80
81
  T.must(name[prefix.length..-1])
81
82
  end
82
83
 
83
- sig { returns(T.nilable(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
- # @return [String, nil]
87
+ # @return [String]
87
88
  attr_reader :type
88
89
 
89
90
  sig { returns(T.nilable(String)) }
@@ -118,8 +119,8 @@ module Parlour
118
119
  #
119
120
  # @return [String]
120
121
  def to_sig_param
121
- "#{name_without_kind}: #{type || 'T.untyped'}"
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
@@ -0,0 +1,110 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbiGenerator
4
+ # Represents an struct definition; that is, a class which subclasses
5
+ # +T::Struct+ and declares `prop` members.
6
+ class StructClassNamespace < ClassNamespace
7
+ extend T::Sig
8
+
9
+ sig do
10
+ params(
11
+ generator: Generator,
12
+ name: String,
13
+ final: T::Boolean,
14
+ props: T::Array[StructProp],
15
+ abstract: T::Boolean,
16
+ block: T.nilable(T.proc.params(x: StructClassNamespace).void)
17
+ ).void
18
+ end
19
+ # Creates a new struct class definition.
20
+ # @note You should use {Namespace#create_struct_class} rather than this directly.
21
+ #
22
+ # @param generator [RbiGenerator] The current RbiGenerator.
23
+ # @param name [String] The name of this class.
24
+ # @param final [Boolean] Whether this namespace is final.
25
+ # @param props [Array<StructProp>] The props of the struct.
26
+ # @param abstract [Boolean] A boolean indicating whether this class is abstract.
27
+ # @param block A block which the new instance yields itself to.
28
+ # @return [void]
29
+ def initialize(generator, name, final, props, abstract, &block)
30
+ super(generator, name, final, 'T::Struct', abstract, &block)
31
+ @props = props
32
+ end
33
+
34
+ sig { returns(T::Array[StructProp]) }
35
+ # The props of the struct.
36
+ # @return [Array<StructProp>]
37
+ attr_reader :props
38
+
39
+ sig do
40
+ override.params(
41
+ indent_level: Integer,
42
+ options: Options,
43
+ ).returns(T::Array[String])
44
+ end
45
+ # Generates the RBI lines for the body of this struct. This consists of
46
+ # {props}, {includes}, {extends} and {children}.
47
+ #
48
+ # @param indent_level [Integer] The indentation level to generate the lines at.
49
+ # @param options [Options] The formatting options to use.
50
+ # @return [Array<String>] The RBI lines for the body, formatted as specified.
51
+ def generate_body(indent_level, options)
52
+ result = []
53
+ props.each do |prop|
54
+ result << options.indented(indent_level, prop.to_prop_call)
55
+ end
56
+ result << ''
57
+
58
+ result + super
59
+ end
60
+
61
+ sig do
62
+ override.params(
63
+ others: T::Array[RbiGenerator::RbiObject]
64
+ ).returns(T::Boolean)
65
+ end
66
+ # Given an array of {StructClassNamespace} instances, returns true if they may
67
+ # be merged into this instance using {merge_into_self}. For instances to
68
+ # be mergeable, they must either all be abstract or all not be abstract,
69
+ # and they must define the same superclass (or none at all).
70
+ #
71
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {StructClassNamespace} instances.
72
+ # @return [Boolean] Whether this instance may be merged with them.
73
+ def mergeable?(others)
74
+ others = T.cast(others, T::Array[Namespace]) rescue (return false)
75
+ all = others + [self]
76
+ all_structs = T.cast(all.select { |x| StructClassNamespace === x }, T::Array[StructClassNamespace])
77
+
78
+ T.must(super && all_structs.map { |s| s.props.map(&:to_prop_call).sort }.reject(&:empty?).uniq.length <= 1)
79
+ end
80
+
81
+ sig do
82
+ override.params(
83
+ others: T::Array[RbiGenerator::RbiObject]
84
+ ).void
85
+ end
86
+ # Given an array of {StructClassNamespace} instances, merges them into this one.
87
+ # You MUST ensure that {mergeable?} is true for those instances.
88
+ #
89
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {StructClassNamespace} instances.
90
+ # @return [void]
91
+ def merge_into_self(others)
92
+ super
93
+
94
+ others.each do |other|
95
+ next unless StructClassNamespace === other
96
+ other = T.cast(other, StructClassNamespace)
97
+
98
+ @props = other.props if props.empty?
99
+ end
100
+ end
101
+
102
+ sig { override.void }
103
+ def generalize_from_rbi!
104
+ super
105
+
106
+ props.each(&:generalize_from_rbi!)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,139 @@
1
+ # typed: true
2
+ module Parlour
3
+ class RbiGenerator < Generator
4
+ # Represents a +T::Struct+ property.
5
+ class StructProp
6
+ extend T::Sig
7
+
8
+ sig do
9
+ params(
10
+ name: String,
11
+ type: Types::TypeLike,
12
+ optional: T.nilable(T.any(T::Boolean, Symbol)),
13
+ enum: T.nilable(String),
14
+ dont_store: T.nilable(T::Boolean),
15
+ foreign: T.nilable(String),
16
+ default: T.nilable(String),
17
+ factory: T.nilable(String),
18
+ immutable: T.nilable(T::Boolean),
19
+ array: T.nilable(String),
20
+ override: T.nilable(T::Boolean),
21
+ redaction: T.nilable(String),
22
+ ).void
23
+ end
24
+ # Create a new struct property.
25
+ #
26
+ # For documentation on all optional properties, please refer to the
27
+ # documentation for T::Struct within the sorbet-runtime gem:
28
+ # https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/props/_props.rb#L31-L106
29
+ #
30
+ # @param name [String] The name of this property.
31
+ # @param type [String] This property's type.
32
+ # @return [void]
33
+ def initialize(name, type, optional: nil, enum: nil, dont_store: nil,
34
+ foreign: nil, default: nil, factory: nil, immutable: nil, array: nil,
35
+ override: nil, redaction: nil)
36
+
37
+ @name = name
38
+ @type = type
39
+ @optional = optional
40
+ @enum = enum
41
+ @dont_store = dont_store
42
+ @foreign = foreign
43
+ @default = default
44
+ @factory = factory
45
+ @immutable = immutable
46
+ @array = array
47
+ @override = override
48
+ @redaction = redaction
49
+ end
50
+
51
+ sig { params(other: Object).returns(T::Boolean) }
52
+ # Returns true if this instance is equal to another instance.
53
+ #
54
+ # @param other [Object] The other instance. If this is not a {StructProp} (or a
55
+ # subclass of it), this will always return false.
56
+ # @return [Boolean]
57
+ def ==(other)
58
+ StructProp === other &&
59
+ name == other.name &&
60
+ type == other.type &&
61
+ optional == other.optional &&
62
+ enum == other.enum &&
63
+ dont_store == other.dont_store &&
64
+ foreign == other.foreign &&
65
+ default == other.default &&
66
+ factory == other.factory &&
67
+ immutable == other.immutable &&
68
+ array == other.array &&
69
+ override == other.override &&
70
+ redaction == other.redaction
71
+ end
72
+
73
+ sig { returns(String) }
74
+ # The name of this parameter, including any prefixes or suffixes such as
75
+ # +*+.
76
+ # @return [String]
77
+ attr_reader :name
78
+
79
+ sig { returns(Types::TypeLike) }
80
+ # This parameter's type.
81
+ # @return [Types::TypeLike, nil]
82
+ attr_reader :type
83
+
84
+ sig { returns(T.nilable(T.any(T::Boolean, Symbol))) }
85
+ attr_reader :optional
86
+
87
+ sig { returns(T.nilable(String)) }
88
+ attr_reader :enum
89
+
90
+ sig { returns(T.nilable(T::Boolean)) }
91
+ attr_reader :dont_store
92
+
93
+ sig { returns(T.nilable(String)) }
94
+ attr_reader :foreign
95
+
96
+ sig { returns(T.nilable(String)) }
97
+ attr_reader :default
98
+
99
+ sig { returns(T.nilable(String)) }
100
+ attr_reader :factory
101
+
102
+ sig { returns(T.nilable(T::Boolean)) }
103
+ attr_reader :immutable
104
+
105
+ sig { returns(T.nilable(String)) }
106
+ attr_reader :array
107
+
108
+ sig { returns(T.nilable(T::Boolean)) }
109
+ attr_reader :override
110
+
111
+ sig { returns(T.nilable(String)) }
112
+ attr_reader :redaction
113
+
114
+ # The optional properties available on instances of this class.
115
+ EXTRA_PROPERTIES = %i{
116
+ optional enum dont_store foreign default factory immutable array override redaction
117
+ }
118
+
119
+ sig { returns(String) }
120
+ # Returns the +prop+ call required to create this property.
121
+ # @return [String]
122
+ def to_prop_call
123
+ call = "prop :#{name}, #{String === @type ? @type : @type.generate_rbi}"
124
+
125
+ EXTRA_PROPERTIES.each do |extra_property|
126
+ value = send extra_property
127
+ call += ", #{extra_property}: #{value}" unless value.nil?
128
+ end
129
+
130
+ call
131
+ end
132
+
133
+ sig { void }
134
+ def generalize_from_rbi!
135
+ @type = TypeParser.parse_single_type(@type) if String === @type
136
+ end
137
+ end
138
+ end
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