parlour 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f603ddca16331f43a36b4120b7d12aa5a4b7c83f753e8527b91bc1391109e9fe
4
- data.tar.gz: f550cdf4cc358dd6ac0c21853ad26756d4729c7f28f3695af53d25775b3f45f0
3
+ metadata.gz: 382331994bebd14df33b96b084d725b0375fa84e0a3aa263e944bad22bb5ef3f
4
+ data.tar.gz: 33b9014173a85bc6dddec5aa3ad38cc7e23d869636694fbbe96d4e07c7e78886
5
5
  SHA512:
6
- metadata.gz: a962191c42becbe6dbe213eb63be142df100643786006093e5663af51f80049890c26ff8974b718cfd89198387cf0ec801b8d3e10cbf032aaf3aa86f746fda97
7
- data.tar.gz: 1bfafd7afbcf81105379d7b747e0c204e47fdd593b0b1a67e659275b0ebd8d236753e48b5bc05896e1e1759ac6495ad3e8132f85a497f28b35c436ddb3cb1585
6
+ metadata.gz: 6ecd35a473c689909a7ef3b84af799cfa718b1901929743676c557d95f6dd717b3c9cd60dcec1c4274ce1ecc4e69310aa3213505c6f3ef479cfa580ccb39f187
7
+ data.tar.gz: 7dddda7cb3a9d123e74f0ed218d62b17697f63451b7389d6d5c92bb4712d41b1cc920380eb8ee3b84c7916be5ad8c32c5d2bb0f3b46c7828acefa45fa0581683
data/.gitignore CHANGED
@@ -8,6 +8,7 @@
8
8
  /tmp/
9
9
  Gemfile.lock
10
10
  /.vscode
11
+ /sorbte/rbi/hidden-definitions/errors.txt
11
12
 
12
13
  # rspec failure tracking
13
14
  .rspec_status
@@ -8,6 +8,7 @@ rvm:
8
8
  - 2.4
9
9
  - 2.5
10
10
  - 2.6
11
+ - 2.7
11
12
  - ruby-head
12
13
  matrix:
13
14
  allow_failures:
@@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [2.0.0] - 2020-02-10
7
+ ### Added
8
+ - Parlour can now load types back out of RBI files or Ruby source files by
9
+ parsing them, using the `TypeLoader` module.
10
+ - The `sort_namespaces` option has been added to `RbiGenerator` to
11
+ alphabetically sort all namespace children.
12
+ - Added `DetachedRbiGenerator`, which can be used to create instances of
13
+ `RbiObject` which are not bound to a particular set of options. This is
14
+ used internally for `TypeLoader`.
15
+ - Parlour will now create a polyfill for `then` on `Kernel`.
16
+ - Added `NodePath#sibling`.
17
+
18
+ ### Changed
19
+ - Version restrictions on _rainbow_ and _commander_ have been slightly relaxed.
20
+ - The version of _sorbet-runtime_ is now restricted to `>= 0.5` after previously
21
+ being unrestricted.
22
+ - Instances of `Namespace` can now be merged with instances of `ClassNamespace`
23
+ or `MethodNamespace`.
24
+ - A method and a namespace can now have the same name without causing a merge
25
+ conflict.
26
+
27
+ ### Fixed
28
+ - Parameter names are no longer nilable.
29
+ **Potentially breaking if you were doing something cursed with Parameter names.**
30
+
6
31
  ## [1.0.0] - 2019-11-22
7
32
  ### Added
8
33
  - `T::Enum` classes have been implemented, and can be generated using
data/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  [![Build Status](https://travis-ci.org/AaronC81/parlour.svg?branch=master)](https://travis-ci.org/AaronC81/parlour)
4
4
  ![Gem](https://img.shields.io/gem/v/parlour.svg)
5
5
 
6
- Parlour is an RBI generator and merger for Sorbet. It consists of two key parts:
6
+ Parlour is an RBI generator, merger and parser for Sorbet. It consists of three
7
+ key parts:
7
8
 
8
9
  - The generator, which outputs beautifully formatted RBI files, created using
9
10
  an intuitive DSL.
@@ -12,6 +13,9 @@ Parlour is an RBI generator and merger for Sorbet. It consists of two key parts:
12
13
  RBIs for the same codebase. These are combined automatically as much as
13
14
  possible, but any other conflicts can be resolved manually through prompts.
14
15
 
16
+ - The parser, which can read an RBI and convert it back into a tree of
17
+ generator objects.
18
+
15
19
  ## Why should I use this?
16
20
 
17
21
  - Parlour enables **much easier creation of RBI generators**, as formatting
@@ -21,6 +25,8 @@ Parlour is an RBI generator and merger for Sorbet. It consists of two key parts:
21
25
  single command and consolidating all of their definitions into a single
22
26
  RBI output file.
23
27
 
28
+ - You can **effortlessly build tools which need to access types within an RBI**;
29
+ no need to write your own parser!
24
30
 
25
31
  Please [**read the wiki**](https://github.com/AaronC81/parlour/wiki) to get
26
32
  started!
@@ -147,6 +153,27 @@ plugins:
147
153
  Gem3::Plugin: {}
148
154
  ```
149
155
 
156
+ ## Parsing RBIs
157
+
158
+ You can either parse individual RBI files, or point Parlour to the root of a
159
+ project and it will locate, parse and merge all RBI files.
160
+
161
+ Note that Parlour isn't limited to just RBIs; it can parse inline `sigs` out
162
+ of your Ruby source too!
163
+
164
+ ```ruby
165
+ require 'parlour'
166
+
167
+ # Return the object tree of a particular file
168
+ Parlour::TypeLoader.load_file('path/to/your/file.rbis')
169
+
170
+ # Return the object tree for an entire Sorbet project - slow but thorough!
171
+ Parlour::TypeLoader.load_project('root/of/the/project')
172
+ ```
173
+
174
+ The structure of the returned object trees is identical to those you would
175
+ create when generating an RBI, built of instances of `RbiObject` subclasses.
176
+
150
177
  ## Parlour Plugins
151
178
 
152
179
  _Have you written an awesome Parlour plugin? Please submit a PR to add it to this list!_
@@ -23,5 +23,10 @@ require 'parlour/rbi_generator/module_namespace'
23
23
  require 'parlour/rbi_generator/class_namespace'
24
24
  require 'parlour/rbi_generator/enum_class_namespace'
25
25
  require 'parlour/rbi_generator'
26
+ require 'parlour/detached_rbi_generator'
26
27
 
27
28
  require 'parlour/conflict_resolver'
29
+
30
+ require 'parlour/parse_error'
31
+ require 'parlour/type_parser'
32
+ require 'parlour/type_loader'
@@ -88,11 +88,11 @@ module Parlour
88
88
  namespace.children.delete(c)
89
89
  end
90
90
 
91
- # We can only try to resolve automatically if they're all the same
92
- # type of object, so check that first
93
- children_type = single_type_of_array(children)
94
- unless children_type
95
- Debugging.debug_puts(self, Debugging::Tree.end("Children are different types; requesting manual resolution"))
91
+ # Check that the types of the given objects allow them to be merged,
92
+ # and get the strategy to use
93
+ strategy = merge_strategy(children)
94
+ unless strategy
95
+ Debugging.debug_puts(self, Debugging::Tree.end("Children are unmergeable types; requesting manual resolution"))
96
96
  # The types aren't the same, so ask the resolver what to do, and
97
97
  # insert that (if not nil)
98
98
  choice = resolver.call("Different kinds of definition for the same name", children)
@@ -100,8 +100,36 @@ module Parlour
100
100
  next
101
101
  end
102
102
 
103
+ case strategy
104
+ when :normal
105
+ first, *rest = children
106
+ when :differing_namespaces
107
+ # Let the namespaces be merged normally, but handle the method here
108
+ namespaces, non_namespaces = children.partition { |x| RbiGenerator::Namespace === x }
109
+
110
+ # If there is any non-namespace item in this conflict, it should be
111
+ # a single method
112
+ if non_namespaces.length != 0
113
+ unless non_namespaces.length == 1 && RbiGenerator::Method === non_namespaces.first
114
+ Debugging.debug_puts(self, Debugging::Tree.end("Non-namespace item in a differing namespace conflict is not a single method; requesting manual resolution"))
115
+ # The types aren't the same, so ask the resolver what to do, and
116
+ # insert that (if not nil)
117
+ choice = resolver.call("Non-namespace item in a differing namespace conflict is not a single method", non_namespaces)
118
+ non_namespaces = []
119
+ non_namespaces << choice if choice
120
+ end
121
+ end
122
+
123
+ non_namespaces.each do |x|
124
+ namespace.children << x
125
+ end
126
+
127
+ first, *rest = namespaces
128
+ else
129
+ raise 'unknown merge strategy; this is a Parlour bug'
130
+ end
131
+
103
132
  # Can the children merge themselves automatically? If so, let them
104
- first, *rest = children
105
133
  first, rest = T.must(first), T.must(rest)
106
134
  if T.must(first).mergeable?(T.must(rest))
107
135
  Debugging.debug_puts(self, Debugging::Tree.end("Children are all mergeable; resolving automatically"))
@@ -131,15 +159,49 @@ module Parlour
131
159
 
132
160
  private
133
161
 
134
- sig { params(arr: T::Array[T.untyped]).returns(T.nilable(Class)) }
162
+ sig { params(arr: T::Array[T.untyped]).returns(T.nilable(Symbol)) }
135
163
  # Given an array, if all elements in the array are instances of the exact
136
- # same class, returns that class. If they are not, returns nil.
164
+ # same class or are otherwise mergeable (for example Namespace and
165
+ # ClassNamespace), returns the kind of merge which needs to be made. A
166
+ # return value of nil indicates that the values cannot be merged.
167
+ #
168
+ # The following kinds are available:
169
+ # - They are all the same. (:normal)
170
+ # - There are exactly two types, one of which is Namespace and other is a
171
+ # subclass of it. (:differing_namespaces)
172
+ # - One of them is Namespace or a subclass (or both, as described above),
173
+ # and the only other is Method. (also :differing_namespaces)
137
174
  #
138
175
  # @param arr [Array] The array.
139
- # @return [Class, nil] Either a class, or nil.
140
- def single_type_of_array(arr)
176
+ # @return [Symbol] The merge strategy to use, or nil if they can't be
177
+ # merged.
178
+ def merge_strategy(arr)
179
+ # If they're all the same type, they can be merged easily
141
180
  array_types = arr.map { |c| c.class }.uniq
142
- array_types.length == 1 ? array_types.first : nil
181
+ return :normal if array_types.length == 1
182
+
183
+ # Find all the namespaces and non-namespaces
184
+ namespace_types, non_namespace_types = array_types.partition { |x| x <= RbiGenerator::Namespace }
185
+
186
+ # If there are two namespace types, one should be Namespace and the other
187
+ # should be a subclass of it
188
+ if namespace_types.length == 2
189
+ exactly_namespace, exactly_one_subclass = namespace_types.partition { |x| x == RbiGenerator::Namespace }
190
+
191
+ return nil unless exactly_namespace.length == 1 \
192
+ && exactly_one_subclass.length == 1 \
193
+ && exactly_one_subclass.first < RbiGenerator::Namespace
194
+ elsif namespace_types.length != 1
195
+ # The only other valid number of namespaces is 1, where we don't need to
196
+ # check anything
197
+ return nil
198
+ end
199
+
200
+ # It's OK, albeit cursed, for there to be a method with the same name as
201
+ # a namespace (Rainbow does this)
202
+ return nil if non_namespace_types.length != 0 && non_namespace_types != [RbiGenerator::Method]
203
+
204
+ :differing_namespaces
143
205
  end
144
206
 
145
207
  sig { params(arr: T::Array[T.untyped]).returns(T::Boolean) }
@@ -0,0 +1,30 @@
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(Namespace) }
16
+ def root
17
+ detached!
18
+ end
19
+
20
+ sig { override.returns(T.nilable(Plugin)) }
21
+ def current_plugin
22
+ nil
23
+ end
24
+
25
+ sig { override.params(strictness: String).returns(String) }
26
+ def rbi(strictness = 'strong')
27
+ detached!
28
+ end
29
+ end
30
+ 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
@@ -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
@@ -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
@@ -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
@@ -559,11 +559,15 @@ module Parlour
559
559
  # Given an array of {Namespace} instances, merges them into this one.
560
560
  # All children, constants, extends and includes are copied into this
561
561
  # instance.
562
+ #
563
+ # There may also be {RbiGenerator::Method} instances in the stream, which
564
+ # are ignored.
562
565
  #
563
566
  # @param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances.
564
567
  # @return [void]
565
568
  def merge_into_self(others)
566
569
  others.each do |other|
570
+ next if other.is_a?(RbiGenerator::Method)
567
571
  other = T.cast(other, Namespace)
568
572
 
569
573
  other.children.each { |c| children << c }
@@ -599,22 +603,23 @@ module Parlour
599
603
  result += [options.indented(indent_level, 'final!'), ''] if final
600
604
 
601
605
  if includes.any? || extends.any? || constants.any?
602
- result += includes
606
+ result += (options.sort_namespaces ? includes.sort_by(&:name) : includes)
603
607
  .flat_map { |x| x.generate_rbi(indent_level, options) }
604
608
  .reject { |x| x.strip == '' }
605
- result += extends
609
+ result += (options.sort_namespaces ? extends.sort_by(&:name) : extends)
606
610
  .flat_map { |x| x.generate_rbi(indent_level, options) }
607
611
  .reject { |x| x.strip == '' }
608
- result += constants
612
+ result += (options.sort_namespaces ? constants.sort_by(&:name) : constants)
609
613
  .flat_map { |x| x.generate_rbi(indent_level, options) }
610
614
  .reject { |x| x.strip == '' }
611
615
  result << ""
612
616
  end
613
617
 
614
618
  # Process singleton class attributes
615
- class_attributes, remaining_children = children.partition do |child|
616
- child.is_a?(Attribute) && child.class_attribute
617
- end
619
+ class_attributes, remaining_children = \
620
+ (options.sort_namespaces ? children.sort_by(&:name) : children)
621
+ .partition { |child| child.is_a?(Attribute) && child.class_attribute }
622
+
618
623
  if class_attributes.any?
619
624
  result << options.indented(indent_level, 'class << self')
620
625