parlour 1.0.0 → 4.0.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 (44) 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 -0
  5. data/.parlour +5 -0
  6. data/.rspec +0 -0
  7. data/.travis.yml +4 -3
  8. data/CHANGELOG.md +65 -0
  9. data/CODE_OF_CONDUCT.md +0 -0
  10. data/Gemfile +0 -0
  11. data/LICENSE.txt +0 -0
  12. data/README.md +54 -1
  13. data/Rakefile +0 -0
  14. data/exe/parlour +68 -2
  15. data/lib/parlour.rb +7 -0
  16. data/lib/parlour/conflict_resolver.rb +129 -19
  17. data/lib/parlour/debugging.rb +0 -0
  18. data/lib/parlour/detached_rbi_generator.rb +25 -0
  19. data/lib/parlour/kernel_hack.rb +2 -0
  20. data/lib/parlour/parse_error.rb +19 -0
  21. data/lib/parlour/plugin.rb +1 -1
  22. data/lib/parlour/rbi_generator.rb +13 -7
  23. data/lib/parlour/rbi_generator/arbitrary.rb +0 -0
  24. data/lib/parlour/rbi_generator/attribute.rb +0 -0
  25. data/lib/parlour/rbi_generator/class_namespace.rb +8 -5
  26. data/lib/parlour/rbi_generator/constant.rb +11 -2
  27. data/lib/parlour/rbi_generator/enum_class_namespace.rb +24 -2
  28. data/lib/parlour/rbi_generator/extend.rb +0 -0
  29. data/lib/parlour/rbi_generator/include.rb +0 -0
  30. data/lib/parlour/rbi_generator/method.rb +0 -0
  31. data/lib/parlour/rbi_generator/module_namespace.rb +6 -4
  32. data/lib/parlour/rbi_generator/namespace.rb +81 -15
  33. data/lib/parlour/rbi_generator/options.rb +15 -2
  34. data/lib/parlour/rbi_generator/parameter.rb +5 -5
  35. data/lib/parlour/rbi_generator/rbi_object.rb +0 -0
  36. data/lib/parlour/rbi_generator/struct_class_namespace.rb +103 -0
  37. data/lib/parlour/rbi_generator/struct_prop.rb +136 -0
  38. data/lib/parlour/type_loader.rb +104 -0
  39. data/lib/parlour/type_parser.rb +854 -0
  40. data/lib/parlour/version.rb +1 -1
  41. data/parlour.gemspec +6 -5
  42. data/plugin_examples/foobar_plugin.rb +0 -0
  43. data/rbi/parlour.rbi +893 -0
  44. metadata +40 -18
File without changes
@@ -0,0 +1,25 @@
1
+ # typed: true
2
+
3
+ module Parlour
4
+ class DetachedRbiGenerator < RbiGenerator
5
+ sig { returns(T.untyped) }
6
+ def detached!
7
+ raise "cannot call methods on a detached RBI 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.params(strictness: String).returns(String) }
21
+ def rbi(strictness = 'strong')
22
+ detached!
23
+ end
24
+ end
25
+ end
@@ -3,4 +3,6 @@ module Kernel
3
3
  return to_enum(__method__) { 1 } unless block_given?
4
4
  yield self
5
5
  end unless method_defined? :yield_self
6
+
7
+ alias then yield_self
6
8
  end
@@ -0,0 +1,19 @@
1
+ # typed: true
2
+
3
+ module Parlour
4
+ class ParseError < StandardError
5
+ extend T::Sig
6
+
7
+ sig { returns(Parser::Source::Buffer) }
8
+ attr_reader :buffer
9
+
10
+ sig { returns(Parser::Source::Range) }
11
+ attr_reader :range
12
+
13
+ def initialize(buffer, range)
14
+ super()
15
+ @buffer = buffer
16
+ @range = range
17
+ end
18
+ end
19
+ end
@@ -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 }
@@ -4,7 +4,7 @@ module Parlour
4
4
  class RbiGenerator
5
5
  extend T::Sig
6
6
 
7
- sig { params(break_params: Integer, tab_size: Integer).void }
7
+ sig { params(break_params: Integer, tab_size: Integer, sort_namespaces: T::Boolean).void }
8
8
  # Creates a new RBI generator.
9
9
  #
10
10
  # @example Create a default generator.
@@ -16,29 +16,35 @@ module Parlour
16
16
  # @param break_params [Integer] If there are at least this many parameters in a
17
17
  # Sorbet +sig+, then it is broken onto separate lines.
18
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.
19
21
  # @return [void]
20
- def initialize(break_params: 4, tab_size: 2)
21
- @options = Options.new(break_params: break_params, tab_size: tab_size)
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
+ )
22
28
  @root = Namespace.new(self)
23
29
  end
24
30
 
25
- sig { returns(Options) }
31
+ sig { overridable.returns(Options) }
26
32
  # The formatting options for this generator.
27
33
  # @return [Options]
28
34
  attr_reader :options
29
35
 
30
- sig { returns(Namespace) }
36
+ sig { overridable.returns(Namespace) }
31
37
  # The root {Namespace} of this generator.
32
38
  # @return [Namespace]
33
39
  attr_reader :root
34
40
 
35
- sig { returns(T.nilable(Plugin)) }
41
+ sig { overridable.returns(T.nilable(Plugin)) }
36
42
  # The plugin which is currently generating new definitions.
37
43
  # {Plugin#run_plugins} controls this value.
38
44
  # @return [Plugin, nil]
39
45
  attr_accessor :current_plugin
40
46
 
41
- sig { params(strictness: String).returns(String) }
47
+ sig { overridable.params(strictness: String).returns(String) }
42
48
  # Returns the complete contents of the generated RBI file as a string.
43
49
  #
44
50
  # @return [String] The generated RBI file
File without changes
File without changes
@@ -70,19 +70,21 @@ module Parlour
70
70
  others: T::Array[RbiGenerator::RbiObject]
71
71
  ).returns(T::Boolean)
72
72
  end
73
- # Given an array of {ClassNamespace} instances, returns true if they may
73
+ # Given an array of {Namespace} instances, returns true if they may
74
74
  # be merged into this instance using {merge_into_self}. For instances to
75
75
  # be mergeable, they must either all be abstract or all not be abstract,
76
76
  # and they must define the same superclass (or none at all).
77
77
  #
78
- # @param others [Array<RbiGenerator::RbiObject>] An array of other {ClassNamespace} instances.
78
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances.
79
79
  # @return [Boolean] Whether this instance may be merged with them.
80
80
  def mergeable?(others)
81
- others = T.cast(others, T::Array[ClassNamespace]) rescue (return false)
81
+ others = T.cast(others, T::Array[Namespace]) rescue (return false)
82
82
  all = others + [self]
83
83
 
84
- all.map(&:abstract).uniq.length == 1 &&
85
- all.map(&:superclass).compact.uniq.length <= 1
84
+ all_classes = T.cast(all.select { |x| ClassNamespace === x }, T::Array[ClassNamespace])
85
+
86
+ all_classes.map(&:abstract).uniq.length == 1 &&
87
+ all_classes.map(&:superclass).compact.uniq.length <= 1
86
88
  end
87
89
 
88
90
  sig do
@@ -99,6 +101,7 @@ module Parlour
99
101
  super
100
102
 
101
103
  others.each do |other|
104
+ next unless ClassNamespace === other
102
105
  other = T.cast(other, ClassNamespace)
103
106
 
104
107
  @superclass = other.superclass unless superclass
@@ -8,6 +8,7 @@ module Parlour
8
8
  generator: RbiGenerator,
9
9
  name: String,
10
10
  value: String,
11
+ eigen_constant: T::Boolean,
11
12
  block: T.nilable(T.proc.params(x: Constant).void)
12
13
  ).void
13
14
  end
@@ -15,9 +16,12 @@ 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
 
@@ -25,6 +29,10 @@ module Parlour
25
29
  sig { returns(String) }
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
@@ -80,10 +80,32 @@ module Parlour
80
80
  # @param others [Array<RbiGenerator::RbiObject>] An array of other {EnumClassNamespace} instances.
81
81
  # @return [Boolean] Whether this instance may be merged with them.
82
82
  def mergeable?(others)
83
- others = T.cast(others, T::Array[EnumClassNamespace]) rescue (return false)
83
+ others = T.cast(others, T::Array[Namespace]) rescue (return false)
84
84
  all = others + [self]
85
+ all_enums = T.cast(all.select { |x| EnumClassNamespace === x }, T::Array[EnumClassNamespace])
85
86
 
86
- T.must(super && all.map(&:enums).uniq.length <= 1)
87
+ T.must(super && all_enums.map { |e| e.enums.sort }.reject(&:empty?).uniq.length <= 1)
88
+ end
89
+
90
+ sig do
91
+ override.params(
92
+ others: T::Array[RbiGenerator::RbiObject]
93
+ ).void
94
+ end
95
+ # Given an array of {EnumClassNamespace} instances, merges them into this one.
96
+ # You MUST ensure that {mergeable?} is true for those instances.
97
+ #
98
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {EnumClassNamespace} instances.
99
+ # @return [void]
100
+ def merge_into_self(others)
101
+ super
102
+
103
+ others.each do |other|
104
+ next unless EnumClassNamespace === other
105
+ other = T.cast(other, EnumClassNamespace)
106
+
107
+ @enums = other.enums if enums.empty?
108
+ end
87
109
  end
88
110
  end
89
111
  end
File without changes
File without changes
File without changes
@@ -59,18 +59,20 @@ module Parlour
59
59
  others: T::Array[RbiGenerator::RbiObject]
60
60
  ).returns(T::Boolean)
61
61
  end
62
- # Given an array of {ModuleNamespace} instances, returns true if they may
62
+ # Given an array of {Namespace} instances, returns true if they may
63
63
  # be merged into this instance using {merge_into_self}. For instances to
64
64
  # be mergeable, they must either all be interfaces or all not be
65
65
  # interfaces.
66
66
  #
67
- # @param others [Array<RbiGenerator::RbiObject>] An array of other {ModuleNamespace} instances.
67
+ # @param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances.
68
68
  # @return [Boolean] Whether this instance may be merged with them.
69
69
  def mergeable?(others)
70
- others = T.cast(others, T::Array[RbiGenerator::ModuleNamespace]) rescue (return false)
70
+ others = T.cast(others, T::Array[Namespace]) rescue (return false)
71
71
  all = others + [self]
72
72
 
73
- all.map(&:interface).uniq.length == 1
73
+ all_modules = T.cast(all.select { |x| ModuleNamespace === x }, T::Array[ModuleNamespace])
74
+
75
+ all_modules.map(&:interface).uniq.length == 1
74
76
  end
75
77
 
76
78
  sig do
@@ -196,6 +196,35 @@ module Parlour
196
196
  new_enum_class
197
197
  end
198
198
 
199
+ sig do
200
+ params(
201
+ name: String,
202
+ final: T::Boolean,
203
+ props: T.nilable(T::Array[StructProp]),
204
+ abstract: T::Boolean,
205
+ block: T.nilable(T.proc.params(x: StructClassNamespace).void)
206
+ ).returns(StructClassNamespace)
207
+ end
208
+ # Creates a new struct class definition as a child of this namespace.
209
+ #
210
+ # @example Create a person struct.
211
+ # namespace.create_class('Person', props: [
212
+ # Parlour::RbiGenerator::StructProp.new('name', 'String')
213
+ # ])
214
+ #
215
+ # @param name [String] The name of this class.
216
+ # @param final [Boolean] Whether this namespace is final.
217
+ # @param props [Array<StructProp>] The props of the struct.
218
+ # @param abstract [Boolean] A boolean indicating whether this class is abstract.
219
+ # @param block A block which the new instance yields itself to.
220
+ # @return [EnumClassNamespace]
221
+ def create_struct_class(name, final: false, props: nil, abstract: false, &block)
222
+ new_struct_class = StructClassNamespace.new(generator, name, final, props || [], abstract, &block)
223
+ move_next_comments(new_struct_class)
224
+ children << new_struct_class
225
+ new_struct_class
226
+ end
227
+
199
228
  sig do
200
229
  params(
201
230
  name: String,
@@ -247,13 +276,13 @@ module Parlour
247
276
  #
248
277
  # @param name [String] The name of this method. You should not specify +self.+ in
249
278
  # this - use the +class_method+ parameter instead.
250
- # @param parameters [Array<Parameter>] An array of {Parameter} instances representing this
279
+ # @param parameters [Array<Parameter>] An array of {Parameter} instances representing this
251
280
  # method's parameters.
252
281
  # @param return_type [String, nil] A Sorbet string of what this method returns, such as
253
282
  # +"String"+ or +"T.untyped"+. Passing nil denotes a void return.
254
283
  # @param returns [String, nil] Same as return_type.
255
284
  # @param abstract [Boolean] Whether this method is abstract.
256
- # @param implementation [Boolean] DEPRECATED: Whether this method is an
285
+ # @param implementation [Boolean] DEPRECATED: Whether this method is an
257
286
  # implementation of a parent abstract method.
258
287
  # @param override [Boolean] Whether this method is overriding a parent overridable
259
288
  # method, or implementing a parent abstract method.
@@ -498,7 +527,7 @@ module Parlour
498
527
  returned_includables
499
528
  end
500
529
 
501
- sig { params(name: String, value: String, block: T.nilable(T.proc.params(x: Constant).void)).returns(Constant) }
530
+ sig { params(name: String, value: String, eigen_constant: T::Boolean, block: T.nilable(T.proc.params(x: Constant).void)).returns(Constant) }
502
531
  # Adds a new constant definition to this namespace.
503
532
  #
504
533
  # @example Add an +Elem+ constant to the class.
@@ -506,13 +535,16 @@ module Parlour
506
535
  #
507
536
  # @param name [String] The name of the constant.
508
537
  # @param value [String] The value of the constant, as a Ruby code string.
538
+ # @param eigen_constant [Boolean] Whether this constant is defined on the
539
+ # eigenclass of the current namespace.
509
540
  # @param block A block which the new instance yields itself to.
510
541
  # @return [RbiGenerator::Constant]
511
- def create_constant(name, value:, &block)
542
+ def create_constant(name, value:, eigen_constant: false, &block)
512
543
  new_constant = RbiGenerator::Constant.new(
513
544
  generator,
514
545
  name: name,
515
546
  value: value,
547
+ eigen_constant: eigen_constant,
516
548
  &block
517
549
  )
518
550
  move_next_comments(new_constant)
@@ -544,26 +576,30 @@ module Parlour
544
576
  # can be merged into each other, as they lack definitions for themselves,
545
577
  # so there is nothing to conflict. (This isn't the case for subclasses
546
578
  # such as {ClassNamespace}.)
547
- #
579
+ #
548
580
  # @param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances.
549
581
  # @return [true] Always true.
550
582
  def mergeable?(others)
551
583
  true
552
584
  end
553
585
 
554
- sig do
586
+ sig do
555
587
  override.overridable.params(
556
588
  others: T::Array[RbiGenerator::RbiObject]
557
589
  ).void
558
590
  end
559
591
  # Given an array of {Namespace} instances, merges them into this one.
560
- # All children, constants, extends and includes are copied into this
592
+ # All children, constants, extends and includes are copied into this
561
593
  # instance.
562
- #
594
+ #
595
+ # There may also be {RbiGenerator::Method} instances in the stream, which
596
+ # are ignored.
597
+ #
563
598
  # @param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances.
564
599
  # @return [void]
565
600
  def merge_into_self(others)
566
601
  others.each do |other|
602
+ next if other.is_a?(RbiGenerator::Method)
567
603
  other = T.cast(other, Namespace)
568
604
 
569
605
  other.children.each { |c| children << c }
@@ -598,31 +634,61 @@ module Parlour
598
634
 
599
635
  result += [options.indented(indent_level, 'final!'), ''] if final
600
636
 
601
- if includes.any? || extends.any? || constants.any?
602
- result += includes
637
+ # Split away the eigen constants; these need to be put in a
638
+ # "class << self" block later
639
+ eigen_constants, non_eigen_constants = constants.partition(&:eigen_constant)
640
+ eigen_constants.sort_by!(&:name) if options.sort_namespaces
641
+
642
+ if includes.any? || extends.any? || non_eigen_constants.any?
643
+ result += (options.sort_namespaces ? includes.sort_by(&:name) : includes)
603
644
  .flat_map { |x| x.generate_rbi(indent_level, options) }
604
645
  .reject { |x| x.strip == '' }
605
- result += extends
646
+ result += (options.sort_namespaces ? extends.sort_by(&:name) : extends)
606
647
  .flat_map { |x| x.generate_rbi(indent_level, options) }
607
648
  .reject { |x| x.strip == '' }
608
- result += constants
649
+ result += (options.sort_namespaces ? non_eigen_constants.sort_by(&:name) : non_eigen_constants)
609
650
  .flat_map { |x| x.generate_rbi(indent_level, options) }
610
651
  .reject { |x| x.strip == '' }
611
652
  result << ""
612
653
  end
613
654
 
614
655
  # Process singleton class attributes
615
- class_attributes, remaining_children = children.partition do |child|
656
+ sorted_children = (
657
+ if options.sort_namespaces
658
+ # sort_by can be unstable (and is in current MRI).
659
+ # Use the this work around to preserve order for ties
660
+ children.sort_by.with_index { |child, i| [child.name, i] }
661
+ else
662
+ children
663
+ end
664
+ )
665
+ class_attributes, remaining_children = sorted_children.partition do |child|
616
666
  child.is_a?(Attribute) && child.class_attribute
617
667
  end
618
- if class_attributes.any?
619
- result << options.indented(indent_level, 'class << self')
620
668
 
669
+ # Handle the "class << self block"
670
+ result << options.indented(indent_level, 'class << self') \
671
+ if class_attributes.any? || eigen_constants.any?
672
+
673
+ if eigen_constants.any?
674
+ first, *rest = eigen_constants
675
+ result += T.must(first).generate_rbi(indent_level + 1, options) + T.must(rest)
676
+ .map { |obj| obj.generate_rbi(indent_level + 1, options) }
677
+ .map { |lines| [""] + lines }
678
+ .flatten
679
+ end
680
+
681
+ result << '' if eigen_constants.any? && class_attributes.any?
682
+
683
+ if class_attributes.any?
621
684
  first, *rest = class_attributes
622
685
  result += T.must(first).generate_rbi(indent_level + 1, options) + T.must(rest)
623
686
  .map { |obj| obj.generate_rbi(indent_level + 1, options) }
624
687
  .map { |lines| [""] + lines }
625
688
  .flatten
689
+ end
690
+
691
+ if class_attributes.any? || eigen_constants.any?
626
692
  result << options.indented(indent_level, 'end')
627
693
  result << ''
628
694
  end
@@ -6,7 +6,7 @@ module Parlour
6
6
  class Options
7
7
  extend T::Sig
8
8
 
9
- sig { params(break_params: Integer, tab_size: Integer).void }
9
+ sig { params(break_params: Integer, tab_size: Integer, sort_namespaces: T::Boolean).void }
10
10
  # Creates a new set of formatting options.
11
11
  #
12
12
  # @example Create Options with +break_params+ of +4+ and +tab_size+ of +2+.
@@ -15,10 +15,13 @@ module Parlour
15
15
  # @param break_params [Integer] If there are at least this many parameters in a
16
16
  # Sorbet +sig+, then it is broken onto separate lines.
17
17
  # @param tab_size [Integer] The number of spaces to use per indent.
18
+ # @param sort_namespaces [Boolean] Whether to sort all items within a
19
+ # namespace alphabetically.
18
20
  # @return [void]
19
- def initialize(break_params:, tab_size:)
21
+ def initialize(break_params:, tab_size:, sort_namespaces:)
20
22
  @break_params = break_params
21
23
  @tab_size = tab_size
24
+ @sort_namespaces = sort_namespaces
22
25
  end
23
26
 
24
27
  sig { returns(Integer) }
@@ -46,6 +49,16 @@ module Parlour
46
49
  # @return [Integer]
47
50
  attr_reader :tab_size
48
51
 
52
+ sig { returns(T::Boolean) }
53
+ # Whether to sort all items within a namespace alphabetically.
54
+ # Items which are typically grouped together, such as "include" or
55
+ # "extend" calls, will remain grouped together when sorted.
56
+ # If true, items are sorted by their name when the RBI is generated.
57
+ # If false, items are generated in the order they are added to the
58
+ # namespace.
59
+ # @return [Boolean]
60
+ attr_reader :sort_namespaces
61
+
49
62
  sig { params(level: Integer, str: String).returns(String) }
50
63
  # Returns a string indented to the given indent level, according to the
51
64
  # set {tab_size}.