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
File without changes
@@ -6,17 +6,12 @@ module Parlour
6
6
  def detached!
7
7
  raise "cannot call methods on a detached RBI generator"
8
8
  end
9
-
9
+
10
10
  sig { override.returns(Options) }
11
11
  def options
12
12
  detached!
13
13
  end
14
14
 
15
- sig { override.returns(Namespace) }
16
- def root
17
- detached!
18
- end
19
-
20
15
  sig { override.returns(T.nilable(Plugin)) }
21
16
  def current_plugin
22
17
  nil
@@ -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
File without changes
@@ -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
File without changes
@@ -50,7 +50,7 @@ module Parlour
50
50
  end
51
51
  end
52
52
 
53
- sig { params(options: Hash).void }
53
+ sig { params(options: T::Hash[T.untyped, T.untyped]).void }
54
54
  def initialize(options); end
55
55
 
56
56
  sig { abstract.params(root: RbiGenerator::Namespace).void }
@@ -1,55 +1,42 @@
1
1
  # typed: true
2
2
  module Parlour
3
3
  # The RBI generator.
4
- class RbiGenerator
5
- extend T::Sig
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
- sig { params(break_params: Integer, tab_size: Integer, sort_namespaces: T::Boolean).void }
8
- # Creates a new RBI generator.
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(Options) }
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
- "# typed: #{strictness}\n" + root.generate_rbi(0, options).join("\n") + "\n"
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
@@ -1,11 +1,11 @@
1
1
  # typed: true
2
2
  module Parlour
3
- class RbiGenerator
3
+ class RbiGenerator < Generator
4
4
  # Represents miscellaneous Ruby code.
5
5
  class Arbitrary < RbiObject
6
6
  sig do
7
7
  params(
8
- generator: RbiGenerator,
8
+ generator: Generator,
9
9
  code: String,
10
10
  block: T.nilable(T.proc.params(x: Arbitrary).void)
11
11
  ).void
@@ -87,6 +87,9 @@ module Parlour
87
87
  def describe
88
88
  "Arbitrary code (#{code})"
89
89
  end
90
+
91
+ sig { override.void }
92
+ def generalize_from_rbi!; end # Nothing to do
90
93
  end
91
94
  end
92
95
  end
@@ -1,14 +1,14 @@
1
1
  # typed: true
2
2
  module Parlour
3
- class RbiGenerator
3
+ class RbiGenerator < Generator
4
4
  # Represents an attribute reader, writer or accessor.
5
5
  class Attribute < Method
6
6
  sig do
7
7
  params(
8
- generator: RbiGenerator,
8
+ generator: Generator,
9
9
  name: String,
10
10
  kind: Symbol,
11
- type: String,
11
+ type: Types::TypeLike,
12
12
  class_attribute: T::Boolean,
13
13
  block: T.nilable(T.proc.params(x: Attribute).void)
14
14
  ).void
@@ -20,8 +20,7 @@ module Parlour
20
20
  # @param name [String] The name of this attribute.
21
21
  # @param kind [Symbol] The kind of attribute this is; one of :writer, :reader or
22
22
  # :accessor.
23
- # @param type [String] A Sorbet string of this attribute's type, such as
24
- # +"String"+ or +"T.untyped"+.
23
+ # @param type [String, Types::Type] This attribute's type.
25
24
  # @param class_attribute [Boolean] Whether this attribute belongs to the
26
25
  # singleton class.
27
26
  # @param block A block which the new instance yields itself to.
@@ -32,6 +31,7 @@ module Parlour
32
31
  # attr_accessor and attr_reader should have: sig { returns(X) }
33
32
  # attr_writer :foo should have: sig { params(foo: X).returns(X) }
34
33
 
34
+ @type = type
35
35
  @kind = kind
36
36
  @class_attribute = class_attribute
37
37
  case kind
@@ -55,6 +55,10 @@ module Parlour
55
55
  # Whether this attribute belongs to the singleton class.
56
56
  attr_reader :class_attribute
57
57
 
58
+ sig { returns(Types::TypeLike) }
59
+ # The type of this attribute.
60
+ attr_reader :type
61
+
58
62
  sig { override.params(other: Object).returns(T::Boolean) }
59
63
  # Returns true if this instance is equal to another attribute.
60
64
  #
@@ -69,6 +73,11 @@ module Parlour
69
73
  )
70
74
  end
71
75
 
76
+ sig { override.void }
77
+ def generalize_from_rbi!
78
+ @type = TypeParser.parse_single_type(@type) if String === @type
79
+ end
80
+
72
81
  private
73
82
 
74
83
  sig do
@@ -1,13 +1,13 @@
1
1
  # typed: true
2
2
  module Parlour
3
- class RbiGenerator
3
+ class RbiGenerator < Generator
4
4
  # Represents a class definition.
5
5
  class ClassNamespace < Namespace
6
6
  extend T::Sig
7
7
 
8
8
  sig do
9
9
  params(
10
- generator: RbiGenerator,
10
+ generator: Generator,
11
11
  name: String,
12
12
  final: T::Boolean,
13
13
  superclass: T.nilable(String),
@@ -47,7 +47,7 @@ module Parlour
47
47
  class_definition = superclass.nil? \
48
48
  ? "class #{name}"
49
49
  : "class #{name} < #{superclass}"
50
-
50
+
51
51
  lines = generate_comments(indent_level, options)
52
52
  lines << options.indented(indent_level, class_definition)
53
53
  lines += [options.indented(indent_level + 1, "abstract!"), ""] if abstract
@@ -116,6 +116,11 @@ module Parlour
116
116
  "#{"abstract, " if abstract}#{children.length} children, " +
117
117
  "#{includes.length} includes, #{extends.length} extends"
118
118
  end
119
+
120
+ sig { override.void }
121
+ def generalize_from_rbi!
122
+ super
123
+ end
119
124
  end
120
125
  end
121
126
  end
@@ -1,13 +1,14 @@
1
1
  # typed: true
2
2
  module Parlour
3
- class RbiGenerator
3
+ class RbiGenerator < Generator
4
4
  # Represents a constant definition.
5
5
  class Constant < RbiObject
6
6
  sig do
7
7
  params(
8
- generator: RbiGenerator,
8
+ generator: Generator,
9
9
  name: String,
10
- value: String,
10
+ value: Types::TypeLike,
11
+ eigen_constant: T::Boolean,
11
12
  block: T.nilable(T.proc.params(x: Constant).void)
12
13
  ).void
13
14
  end
@@ -15,16 +16,23 @@ module Parlour
15
16
  #
16
17
  # @param name [String] The name of the constant.
17
18
  # @param value [String] The value of the constant, as a Ruby code string.
18
- def initialize(generator, name: '', value: '', &block)
19
+ # @param eigen_constant [Boolean] Whether this constant is defined on the
20
+ # eigenclass of the current namespace.
21
+ def initialize(generator, name: '', value: '', eigen_constant: false, &block)
19
22
  super(generator, name)
20
23
  @value = value
24
+ @eigen_constant = eigen_constant
21
25
  yield_self(&block) if block
22
26
  end
23
27
 
24
- # @return [String] The value of the constant, as a Ruby code string.
25
- sig { returns(String) }
28
+ # @return [String] The value or type of the constant.
29
+ sig { returns(Types::TypeLike) }
26
30
  attr_reader :value
27
31
 
32
+ # @return [Boolean] Whether this constant is defined on the eigenclass
33
+ # of the current namespace.
34
+ attr_reader :eigen_constant
35
+
28
36
  sig { params(other: Object).returns(T::Boolean) }
29
37
  # Returns true if this instance is equal to another extend.
30
38
  #
@@ -32,7 +40,8 @@ module Parlour
32
40
  # subclass of it), this will always return false.
33
41
  # @return [Boolean]
34
42
  def ==(other)
35
- Constant === other && name == other.name && value == other.value
43
+ Constant === other && name == other.name && value == other.value \
44
+ && eigen_constant == other.eigen_constant
36
45
  end
37
46
 
38
47
  sig do
@@ -47,7 +56,11 @@ module Parlour
47
56
  # @param options [Options] The formatting options to use.
48
57
  # @return [Array<String>] The RBI lines, formatted as specified.
49
58
  def generate_rbi(indent_level, options)
50
- [options.indented(indent_level, "#{name} = #{value}")]
59
+ if String === @value
60
+ [options.indented(indent_level, "#{name} = #{@value}")]
61
+ else
62
+ [options.indented(indent_level, "#{name} = T.let(nil, #{@value.generate_rbi})")]
63
+ end
51
64
  end
52
65
 
53
66
  sig do
@@ -89,6 +102,13 @@ module Parlour
89
102
  def describe
90
103
  "Constant (#{name} = #{value})"
91
104
  end
105
+
106
+ sig { override.void }
107
+ def generalize_from_rbi!
108
+ # There's a good change this is an untyped constant, so rescue
109
+ # ParseError and use untyped
110
+ @value = (TypeParser.parse_single_type(@value) if String === @value) rescue Types::Untyped.new
111
+ end
92
112
  end
93
113
  end
94
114
  end