solargraph-rspec 0.1.1 β 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +5 -2
- data/lib/solargraph/rspec/convention.rb +15 -6
- data/lib/solargraph/rspec/correctors/base.rb +1 -1
- data/lib/solargraph/rspec/correctors/context_block_namespace_corrector.rb +6 -5
- data/lib/solargraph/rspec/correctors/described_class_corrector.rb +8 -8
- data/lib/solargraph/rspec/correctors/dsl_methods_corrector.rb +2 -2
- data/lib/solargraph/rspec/correctors/example_and_hook_blocks_binding_corrector.rb +16 -16
- data/lib/solargraph/rspec/correctors/implicit_subject_method_corrector.rb +2 -2
- data/lib/solargraph/rspec/correctors/let_methods_corrector.rb +9 -11
- data/lib/solargraph/rspec/correctors/subject_method_corrector.rb +5 -3
- data/lib/solargraph/rspec/pin_factory.rb +100 -0
- data/lib/solargraph/rspec/spec_walker/fake_let_method.rb +35 -0
- data/lib/solargraph/rspec/spec_walker/full_constant_name.rb +29 -0
- data/lib/solargraph/rspec/spec_walker/node_types.rb +82 -0
- data/lib/solargraph/rspec/spec_walker/rspec_context_namespace.rb +56 -0
- data/lib/solargraph/rspec/spec_walker.rb +59 -101
- data/lib/solargraph/rspec/version.rb +1 -1
- data/lib/solargraph/rspec/walker.rb +11 -35
- metadata +7 -3
- data/lib/solargraph/rspec/util.rb +0 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f73a95cc73aa3ce52cff1e5e4e8e06509f3a59ccf6a3371b9087fad38e135b0
|
4
|
+
data.tar.gz: 221342fb0163f67fce1648aa4b14310b31907aacc9637def0b61e690b8ff750e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47f84a714dc459f67e354b68ff1c037e0ee9170a364db3fb4aa8e7a04cef1ce81952fade0a109a41de91f57b0b18201d3d8ab95b901f94de679357688bc9e41f
|
7
|
+
data.tar.gz: a5bff062303a4fd717315083944c19dbe1c21a94ee795e9f29d5e2971d574038bb75677c596645dacd5861f5f624a7a36a93347c48e443a549ab7decc387dae3
|
data/CHANGELOG.md
CHANGED
@@ -7,7 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
-
-
|
10
|
+
## [0.2.0] - 2024-05-20
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- `let` and `subject` type inference π (Resolves: [Issue #1](https://github.com/lekemula/solargraph-rspec/issues/1))
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
- Migrate from `parser` gem to using ruby's built-in RubyVM::AbstractSyntaxTree ([see why](https://github.com/castwide/solargraph/issues/522#issuecomment-993016664))
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
|
22
|
+
- Fix subject without name block completion: `subject { ... }`
|
23
|
+
- Fix subject return class overlap with `Rspec::ExampleGroups::` when class has no namespace
|
11
24
|
|
12
25
|
## [0.1.1] - 2024-05-13
|
13
26
|
|
data/README.md
CHANGED
@@ -7,15 +7,18 @@
|
|
7
7
|
|
8
8
|
RSpec is a testing framework of choice for many Ruby developers. But at the same time is highly dynamic and heavily relying on metaprogramming making it hard to provide accurate code completion and type inference.
|
9
9
|
|
10
|
-
This gem aims to provide
|
10
|
+
This gem aims to provide better support for RSpec in Solargraph and it supports the following features:
|
11
11
|
- `describe` and `it` methods completion
|
12
12
|
- memoized `let` and `let!` methods completion
|
13
13
|
- implicit and explicit `subject` methods
|
14
14
|
- `described_class` with appropriate type inference
|
15
15
|
- `RSpec::Matchers` methods completion
|
16
|
+
- type inference π
|
16
17
|
- and more to come...
|
17
18
|
|
18
|
-
|
19
|
+
![solargraph-rspec-with-types](https://github.com/lekemula/solargraph-rspec/assets/9197495/077e74f8-a800-4e90-8922-fa5351adcda3)
|
20
|
+
![solargraph-rspec-with-types-vs-code](https://github.com/lekemula/solargraph-rspec/assets/9197495/6a942460-256d-46ca-82de-9869a65309ec)
|
21
|
+
|
19
22
|
|
20
23
|
## Installation
|
21
24
|
|
@@ -10,7 +10,7 @@ require_relative 'correctors/subject_method_corrector'
|
|
10
10
|
require_relative 'correctors/context_block_methods_corrector'
|
11
11
|
require_relative 'correctors/implicit_subject_method_corrector'
|
12
12
|
require_relative 'correctors/dsl_methods_corrector'
|
13
|
-
require_relative '
|
13
|
+
require_relative 'pin_factory'
|
14
14
|
|
15
15
|
module Solargraph
|
16
16
|
module Rspec
|
@@ -121,14 +121,20 @@ module Solargraph
|
|
121
121
|
|
122
122
|
# @type [Pin::Method, nil]
|
123
123
|
described_class_pin = nil
|
124
|
-
Correctors::DescribedClassCorrector.new(
|
124
|
+
Correctors::DescribedClassCorrector.new(
|
125
|
+
namespace_pins: namespace_pins,
|
126
|
+
rspec_walker: rspec_walker
|
127
|
+
).correct(
|
125
128
|
source_map
|
126
129
|
) do |pins_to_add|
|
127
130
|
described_class_pin = pins_to_add.first
|
128
131
|
pins += pins_to_add
|
129
132
|
end
|
130
133
|
|
131
|
-
Correctors::LetMethodsCorrector.new(
|
134
|
+
Correctors::LetMethodsCorrector.new(
|
135
|
+
namespace_pins: namespace_pins,
|
136
|
+
rspec_walker: rspec_walker
|
137
|
+
).correct(
|
132
138
|
source_map
|
133
139
|
) do |pins_to_add|
|
134
140
|
pins += pins_to_add
|
@@ -136,7 +142,10 @@ module Solargraph
|
|
136
142
|
|
137
143
|
# @type [Pin::Method, nil]
|
138
144
|
subject_pin = nil
|
139
|
-
Correctors::SubjectMethodCorrector.new(
|
145
|
+
Correctors::SubjectMethodCorrector.new(
|
146
|
+
namespace_pins: namespace_pins,
|
147
|
+
rspec_walker: rspec_walker
|
148
|
+
).correct(
|
140
149
|
source_map
|
141
150
|
) do |pins_to_add|
|
142
151
|
subject_pin = pins_to_add.first
|
@@ -199,7 +208,7 @@ module Solargraph
|
|
199
208
|
# @return [Array<Pin::Base>]
|
200
209
|
def include_helper_pins(helper_modules: HELPER_MODULES)
|
201
210
|
helper_modules.map do |helper_module|
|
202
|
-
|
211
|
+
PinFactory.build_module_include(
|
203
212
|
root_example_group_namespace_pin,
|
204
213
|
helper_module,
|
205
214
|
root_example_group_namespace_pin.location
|
@@ -216,7 +225,7 @@ module Solargraph
|
|
216
225
|
def root_example_group_namespace_pin
|
217
226
|
Solargraph::Pin::Namespace.new(
|
218
227
|
name: ROOT_NAMESPACE,
|
219
|
-
location:
|
228
|
+
location: PinFactory.dummy_location('lib/rspec/core/example_group.rb')
|
220
229
|
)
|
221
230
|
end
|
222
231
|
end
|
@@ -27,7 +27,7 @@ module Solargraph
|
|
27
27
|
def root_example_group_namespace_pin
|
28
28
|
Solargraph::Pin::Namespace.new(
|
29
29
|
name: ROOT_NAMESPACE,
|
30
|
-
location:
|
30
|
+
location: PinFactory.dummy_location('lib/rspec/core/example_group.rb')
|
31
31
|
)
|
32
32
|
end
|
33
33
|
|
@@ -9,10 +9,11 @@ module Solargraph
|
|
9
9
|
class ContextBlockNamespaceCorrector < WalkerBase
|
10
10
|
# @param source_map [Solargraph::SourceMap]
|
11
11
|
def correct(source_map)
|
12
|
-
|
13
|
-
|
12
|
+
# @param location_range [Solargraph::Range]
|
13
|
+
rspec_walker.on_each_context_block do |namespace_name, location_range|
|
14
|
+
original_block_pin = source_map.locate_block_pin(location_range.start.line, location_range.start.column)
|
14
15
|
original_block_pin_index = source_map.pins.index(original_block_pin)
|
15
|
-
location =
|
16
|
+
location = PinFactory.build_location(location_range, source_map.filename)
|
16
17
|
|
17
18
|
# Define a dynamic module for the example group block
|
18
19
|
# Example:
|
@@ -36,7 +37,7 @@ module Solargraph
|
|
36
37
|
|
37
38
|
# Include DSL methods in the example group block
|
38
39
|
# TOOD: This does not work on solagraph! Class methods are not included from parent class.
|
39
|
-
namespace_extend_pin =
|
40
|
+
namespace_extend_pin = PinFactory.build_module_extend(
|
40
41
|
namespace_pin,
|
41
42
|
root_example_group_namespace_pin.name,
|
42
43
|
location
|
@@ -44,7 +45,7 @@ module Solargraph
|
|
44
45
|
|
45
46
|
# Include parent example groups to share let definitions
|
46
47
|
parent_namespace_name = namespace_name.split('::')[0..-2].join('::')
|
47
|
-
namespace_include_pin =
|
48
|
+
namespace_include_pin = PinFactory.build_module_include(
|
48
49
|
namespace_pin,
|
49
50
|
parent_namespace_name,
|
50
51
|
location
|
@@ -9,11 +9,11 @@ module Solargraph
|
|
9
9
|
# @param source_map [Solargraph::SourceMap]
|
10
10
|
# @return [void]
|
11
11
|
def correct(_source_map)
|
12
|
-
rspec_walker.on_described_class do |
|
13
|
-
namespace_pin = closest_namespace_pin(namespace_pins,
|
12
|
+
rspec_walker.on_described_class do |described_class_name, location_range|
|
13
|
+
namespace_pin = closest_namespace_pin(namespace_pins, location_range.start.line)
|
14
14
|
next unless namespace_pin
|
15
15
|
|
16
|
-
described_class_pin = rspec_described_class_method(namespace_pin,
|
16
|
+
described_class_pin = rspec_described_class_method(namespace_pin, location_range, described_class_name)
|
17
17
|
yield [described_class_pin].compact if block_given?
|
18
18
|
end
|
19
19
|
end
|
@@ -21,15 +21,15 @@ module Solargraph
|
|
21
21
|
private
|
22
22
|
|
23
23
|
# @param namespace [Pin::Namespace]
|
24
|
-
# @param
|
24
|
+
# @param location_range [Solargraph::Range]
|
25
25
|
# @param described_class_name [String]
|
26
26
|
# @return [Pin::Method, nil]
|
27
|
-
def rspec_described_class_method(namespace,
|
28
|
-
|
27
|
+
def rspec_described_class_method(namespace, location_range, described_class_name)
|
28
|
+
PinFactory.build_public_method(
|
29
29
|
namespace,
|
30
30
|
'described_class',
|
31
|
-
types: ["Class
|
32
|
-
location:
|
31
|
+
types: ["Class<::#{described_class_name}>"],
|
32
|
+
location: PinFactory.build_location(location_range, namespace.filename),
|
33
33
|
scope: :instance
|
34
34
|
)
|
35
35
|
end
|
@@ -35,7 +35,7 @@ module Solargraph
|
|
35
35
|
# @return [Array<Solargraph::Pin::Method>]
|
36
36
|
def add_methods_with_example_binding(namespace_pin)
|
37
37
|
rspec_context_block_methods.map do |method|
|
38
|
-
|
38
|
+
PinFactory.build_public_method(
|
39
39
|
namespace_pin,
|
40
40
|
method.to_s,
|
41
41
|
comments: ["@yieldself [#{namespace_pin.path}]"], # Fixes the binding of the block to the correct class
|
@@ -50,7 +50,7 @@ module Solargraph
|
|
50
50
|
# @return [Array<Solargraph::Pin::Base>]
|
51
51
|
def add_context_dsl_methods(namespace_pin)
|
52
52
|
Rspec::CONTEXT_METHODS.map do |method|
|
53
|
-
|
53
|
+
PinFactory.build_public_method(
|
54
54
|
namespace_pin,
|
55
55
|
method.to_s,
|
56
56
|
scope: :class
|
@@ -11,32 +11,32 @@ module Solargraph
|
|
11
11
|
# @param source_map [Solargraph::SourceMap]
|
12
12
|
# @return [void]
|
13
13
|
def correct(source_map)
|
14
|
-
rspec_walker.on_example_block do |
|
15
|
-
bind_closest_namespace(
|
14
|
+
rspec_walker.on_example_block do |location_range|
|
15
|
+
bind_closest_namespace(location_range, source_map)
|
16
16
|
|
17
17
|
yield [] if block_given?
|
18
18
|
end
|
19
19
|
|
20
|
-
rspec_walker.on_hook_block do |
|
21
|
-
bind_closest_namespace(
|
20
|
+
rspec_walker.on_hook_block do |location_range|
|
21
|
+
bind_closest_namespace(location_range, source_map)
|
22
22
|
|
23
23
|
yield [] if block_given?
|
24
24
|
end
|
25
25
|
|
26
|
-
rspec_walker.on_let_method do |
|
27
|
-
bind_closest_namespace(
|
26
|
+
rspec_walker.on_let_method do |_method_name, location_range|
|
27
|
+
bind_closest_namespace(location_range, source_map)
|
28
28
|
|
29
29
|
yield [] if block_given?
|
30
30
|
end
|
31
31
|
|
32
|
-
rspec_walker.on_blocks_in_examples do |
|
33
|
-
bind_closest_namespace(
|
32
|
+
rspec_walker.on_blocks_in_examples do |location_range|
|
33
|
+
bind_closest_namespace(location_range, source_map)
|
34
34
|
|
35
35
|
yield [] if block_given?
|
36
36
|
end
|
37
37
|
|
38
|
-
rspec_walker.on_subject do |
|
39
|
-
bind_closest_namespace(
|
38
|
+
rspec_walker.on_subject do |_method_name, location_range|
|
39
|
+
bind_closest_namespace(location_range, source_map)
|
40
40
|
|
41
41
|
yield [] if block_given?
|
42
42
|
end
|
@@ -44,15 +44,15 @@ module Solargraph
|
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
-
# @param
|
47
|
+
# @param location_range [Solargraph::Range]
|
48
48
|
# @param source_map [Solargraph::SourceMap]
|
49
49
|
# @return [void]
|
50
|
-
def bind_closest_namespace(
|
51
|
-
namespace_pin = closest_namespace_pin(namespace_pins,
|
50
|
+
def bind_closest_namespace(location_range, source_map)
|
51
|
+
namespace_pin = closest_namespace_pin(namespace_pins, location_range.start.line)
|
52
52
|
return unless namespace_pin
|
53
53
|
|
54
|
-
original_block_pin = source_map.locate_block_pin(
|
55
|
-
|
54
|
+
original_block_pin = source_map.locate_block_pin(location_range.start.line,
|
55
|
+
location_range.start.column)
|
56
56
|
original_block_pin_index = source_map.pins.index(original_block_pin)
|
57
57
|
fixed_namespace_block_pin = Solargraph::Pin::Block.new(
|
58
58
|
closure: example_run_method(namespace_pin),
|
@@ -67,7 +67,7 @@ module Solargraph
|
|
67
67
|
# @param namespace_pin [Solargraph::Pin::Namespace]
|
68
68
|
# @return [Solargraph::Pin::Method]
|
69
69
|
def example_run_method(namespace_pin)
|
70
|
-
|
70
|
+
PinFactory.build_public_method(
|
71
71
|
namespace_pin,
|
72
72
|
'run',
|
73
73
|
# https://github.com/rspec/rspec-core/blob/main/lib/rspec/core/example.rb#L246
|
@@ -34,10 +34,10 @@ module Solargraph
|
|
34
34
|
def implicit_subject_pin(described_class_pin, namespace_pin)
|
35
35
|
described_class = described_class_pin.return_type.first.subtypes.first.name
|
36
36
|
|
37
|
-
|
37
|
+
PinFactory.build_public_method(
|
38
38
|
namespace_pin,
|
39
39
|
'subject',
|
40
|
-
types: [described_class],
|
40
|
+
types: ["::#{described_class}"],
|
41
41
|
location: described_class_pin.location,
|
42
42
|
scope: :instance
|
43
43
|
)
|
@@ -10,11 +10,11 @@ module Solargraph
|
|
10
10
|
# @param source_map [Solargraph::SourceMap]
|
11
11
|
# @return [void]
|
12
12
|
def correct(_source_map)
|
13
|
-
rspec_walker.on_let_method do |
|
14
|
-
namespace_pin = closest_namespace_pin(namespace_pins,
|
13
|
+
rspec_walker.on_let_method do |let_name, location_range, fake_method_ast|
|
14
|
+
namespace_pin = closest_namespace_pin(namespace_pins, location_range.start.line)
|
15
15
|
next unless namespace_pin
|
16
16
|
|
17
|
-
pin = rspec_let_method(namespace_pin,
|
17
|
+
pin = rspec_let_method(namespace_pin, let_name, location_range, fake_method_ast)
|
18
18
|
yield [pin] if block_given?
|
19
19
|
end
|
20
20
|
end
|
@@ -22,19 +22,17 @@ module Solargraph
|
|
22
22
|
private
|
23
23
|
|
24
24
|
# @param namespace [Pin::Namespace]
|
25
|
-
# @param
|
25
|
+
# @param method_name [String]
|
26
|
+
# @param node [RubyVM::AbstractSyntaxTree::Node]
|
26
27
|
# @param types [Array<String>, nil]
|
27
28
|
# @return [Pin::Method, nil]
|
28
|
-
def rspec_let_method(namespace,
|
29
|
-
|
30
|
-
return unless ast.children[2]&.children
|
31
|
-
|
32
|
-
method_name = ast.children[2].children[0]&.to_s or return
|
33
|
-
Util.build_public_method(
|
29
|
+
def rspec_let_method(namespace, method_name, location_range, node = nil, types: nil)
|
30
|
+
PinFactory.build_public_method(
|
34
31
|
namespace,
|
35
32
|
method_name,
|
33
|
+
node: node,
|
36
34
|
types: types,
|
37
|
-
location:
|
35
|
+
location: PinFactory.build_location(location_range, namespace.filename),
|
38
36
|
scope: :instance
|
39
37
|
)
|
40
38
|
end
|
@@ -10,11 +10,13 @@ module Solargraph
|
|
10
10
|
# @param source_map [Solargraph::SourceMap]
|
11
11
|
# @return [void]
|
12
12
|
def correct(_source_map)
|
13
|
-
rspec_walker.on_subject do |
|
14
|
-
|
13
|
+
rspec_walker.on_subject do |subject_name, location_range, fake_method_ast|
|
14
|
+
next unless subject_name
|
15
|
+
|
16
|
+
namespace_pin = closest_namespace_pin(namespace_pins, location_range.start.line)
|
15
17
|
next unless namespace_pin
|
16
18
|
|
17
|
-
subject_pin = rspec_let_method(namespace_pin,
|
19
|
+
subject_pin = rspec_let_method(namespace_pin, subject_name, location_range, fake_method_ast)
|
18
20
|
yield [subject_pin].compact if block_given?
|
19
21
|
end
|
20
22
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Credits: This file is a copy of the file from the solargraph-rspec gem
|
4
|
+
|
5
|
+
module Solargraph
|
6
|
+
module Rspec
|
7
|
+
# Factory class for building pins and references.
|
8
|
+
module PinFactory
|
9
|
+
# @param namespace [Solargraph::Pin::Namespace]
|
10
|
+
# @param name [String]
|
11
|
+
# @param types [Array<String>]
|
12
|
+
# @param location [Solargraph::Location]
|
13
|
+
# @param comments [Array<String>]
|
14
|
+
# @param attribute [Boolean]
|
15
|
+
# @param scope [:instance, :class]
|
16
|
+
# @return [Solargraph::Pin::Method]
|
17
|
+
def self.build_public_method(
|
18
|
+
namespace,
|
19
|
+
name,
|
20
|
+
types: nil,
|
21
|
+
location: nil,
|
22
|
+
comments: [],
|
23
|
+
attribute: false,
|
24
|
+
scope: :instance,
|
25
|
+
node: nil
|
26
|
+
)
|
27
|
+
opts = {
|
28
|
+
name: name,
|
29
|
+
location: location,
|
30
|
+
closure: namespace,
|
31
|
+
scope: scope,
|
32
|
+
attribute: attribute,
|
33
|
+
comments: [],
|
34
|
+
node: node
|
35
|
+
}
|
36
|
+
|
37
|
+
comments << "@return [#{types.join(",")}]" if types
|
38
|
+
|
39
|
+
opts[:comments] = comments.join("\n")
|
40
|
+
|
41
|
+
Solargraph::Pin::Method.new(**opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param namespace [Solargraph::Pin::Namespace]
|
45
|
+
# @param name [String]
|
46
|
+
# @param location [Solargraph::Location]
|
47
|
+
# @return [Solargraph::Pin::Reference::Include]
|
48
|
+
def self.build_module_include(namespace, module_name, location)
|
49
|
+
Solargraph::Pin::Reference::Include.new(
|
50
|
+
closure: namespace,
|
51
|
+
name: module_name,
|
52
|
+
location: location
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param namespace [Solargraph::Pin::Namespace]
|
57
|
+
# @param module_name [String]
|
58
|
+
# @param location [Solargraph::Location]
|
59
|
+
# @return [Solargraph::Pin::Reference::Extend]
|
60
|
+
def self.build_module_extend(namespace, module_name, location)
|
61
|
+
Solargraph::Pin::Reference::Extend.new(
|
62
|
+
closure: namespace,
|
63
|
+
name: module_name,
|
64
|
+
location: location
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param path [String]
|
69
|
+
# @return [Solargraph::Location]
|
70
|
+
def self.dummy_location(path)
|
71
|
+
Solargraph::Location.new(
|
72
|
+
File.expand_path(path),
|
73
|
+
Solargraph::Range.from_to(0, 0, 0, 0)
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
78
|
+
# @see [RubyVM::AbstractSyntaxTree::NodeWrapper] - for why we need -1 for lineno
|
79
|
+
# @return [Solargraph::Range]
|
80
|
+
def self.build_location_range(ast)
|
81
|
+
Solargraph::Range.from_to(
|
82
|
+
ast.first_lineno - 1,
|
83
|
+
ast.first_column,
|
84
|
+
ast.last_lineno - 1,
|
85
|
+
ast.last_column
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param location_range [Solargraph::Range]
|
90
|
+
# @param path [String]
|
91
|
+
# @return [Solargraph::Location]
|
92
|
+
def self.build_location(location_range, path)
|
93
|
+
Solargraph::Location.new(
|
94
|
+
File.expand_path(path),
|
95
|
+
location_range
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Rspec
|
5
|
+
class SpecWalker
|
6
|
+
class FakeLetMethod
|
7
|
+
MATCH_BODY = Regexp.union(
|
8
|
+
/do(.*)end/m,
|
9
|
+
/{(.*)}/m
|
10
|
+
)
|
11
|
+
|
12
|
+
# @param block_ast [RubyVM::AbstractSyntaxTree::Node]
|
13
|
+
# @return [RubyVM::AbstractSyntaxTree::Node]
|
14
|
+
def self.transform_block(block_ast, code, method_name = nil)
|
15
|
+
method_name ||= NodeTypes.let_method_name(block_ast)
|
16
|
+
block_body = block_ast.children[1]
|
17
|
+
matches = code.lines[block_body.first_lineno - 1..block_body.last_lineno - 1].join.match(MATCH_BODY)
|
18
|
+
method_body = (matches[1] || matches[2]).strip
|
19
|
+
|
20
|
+
ast = RubyVM::AbstractSyntaxTree.parse <<~RUBY
|
21
|
+
def #{method_name}
|
22
|
+
#{method_body}
|
23
|
+
end
|
24
|
+
RUBY
|
25
|
+
|
26
|
+
ast.children[2]
|
27
|
+
rescue SyntaxError
|
28
|
+
raise "Failed to build fake let method: #{block_ast.inspect}, message: #{e.message}"
|
29
|
+
ensure
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Rspec
|
5
|
+
class SpecWalker
|
6
|
+
class FullConstantName
|
7
|
+
class << self
|
8
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
9
|
+
# @return [String]
|
10
|
+
def from_ast(ast)
|
11
|
+
raise 'Node is not a constant' unless NodeTypes.a_constant?(ast)
|
12
|
+
|
13
|
+
if ast.type == :CONST
|
14
|
+
ast.children[0].to_s
|
15
|
+
elsif ast.type == :COLON2
|
16
|
+
name = ast.children[1].to_s
|
17
|
+
"#{from_ast(ast.children[0])}::#{name}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_context_block_ast(block_ast)
|
22
|
+
ast = NodeTypes.context_description_node(block_ast)
|
23
|
+
from_ast(ast)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Rspec
|
5
|
+
class SpecWalker
|
6
|
+
class NodeTypes
|
7
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
8
|
+
# @return [Boolean]
|
9
|
+
def self.a_block?(ast)
|
10
|
+
return false unless ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
11
|
+
|
12
|
+
%i[ITER LAMBDA].include?(ast.type)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
16
|
+
# @return [Boolean]
|
17
|
+
def self.a_context_block?(block_ast)
|
18
|
+
Solargraph::Rspec::CONTEXT_METHODS.include?(method_with_block_name(block_ast))
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
22
|
+
# @return [Boolean]
|
23
|
+
def self.a_subject_block?(block_ast)
|
24
|
+
Solargraph::Rspec::SUBJECT_METHODS.include?(method_with_block_name(block_ast))
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
28
|
+
# @return [Boolean]
|
29
|
+
def self.a_example_block?(block_ast)
|
30
|
+
Solargraph::Rspec::EXAMPLE_METHODS.include?(method_with_block_name(block_ast))
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
34
|
+
# @param config [Config]
|
35
|
+
# @return [Boolean]
|
36
|
+
def self.a_let_block?(block_ast, config)
|
37
|
+
config.let_methods.map(&:to_s).include?(method_with_block_name(block_ast))
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
41
|
+
# @return [Boolean]
|
42
|
+
def self.a_hook_block?(block_ast)
|
43
|
+
Solargraph::Rspec::HOOK_METHODS.include?(method_with_block_name(block_ast))
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.a_constant?(ast)
|
47
|
+
%i[CONST COLON2].include?(ast.type)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param block_ast [RubyVM::AbstractSyntaxTree::Node]
|
51
|
+
# @return [String, nil]
|
52
|
+
def self.method_with_block_name(block_ast)
|
53
|
+
return nil unless a_block?(block_ast)
|
54
|
+
|
55
|
+
method_call = %i[CALL FCALL].include?(block_ast.children[0].type)
|
56
|
+
return nil unless method_call
|
57
|
+
|
58
|
+
block_ast.children[0].children.select { |child| child.is_a?(Symbol) }.first&.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param block_ast [RubyVM::AbstractSyntaxTree::Node]
|
62
|
+
# @return [RubyVM::AbstractSyntaxTree::Node]
|
63
|
+
def self.context_description_node(block_ast)
|
64
|
+
return nil unless a_context_block?(block_ast)
|
65
|
+
|
66
|
+
case block_ast.children[0].type
|
67
|
+
when :CALL # RSpec.describe "something" do end
|
68
|
+
block_ast.children[0].children[2].children[0]
|
69
|
+
when :FCALL # describe "something" do end
|
70
|
+
block_ast.children[0].children[1].children[0]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param block_ast [RubyVM::AbstractSyntaxTree::Node]
|
75
|
+
# @return [String]
|
76
|
+
def self.let_method_name(block_ast)
|
77
|
+
block_ast.children[0].children[1]&.children&.[](0)&.children&.[](0)&.to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Rspec
|
5
|
+
class SpecWalker
|
6
|
+
class RspecContextNamespace
|
7
|
+
class << self
|
8
|
+
# @param block_ast [RubyVM::AbstractSyntaxTree::Node]
|
9
|
+
# @return [String, nil]
|
10
|
+
def from_block_ast(block_ast)
|
11
|
+
return unless block_ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
12
|
+
|
13
|
+
ast = NodeTypes.context_description_node(block_ast)
|
14
|
+
if ast.type == :STR
|
15
|
+
string_to_const_name(ast)
|
16
|
+
elsif NodeTypes.a_constant?(ast)
|
17
|
+
FullConstantName.from_ast(ast).gsub('::', '')
|
18
|
+
else
|
19
|
+
Solargraph.logger.warn "[RSpec] Unexpected AST type #{ast.type}"
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @see https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example_group.rb#L862
|
27
|
+
# @param ast [Parser::AST::Node]
|
28
|
+
# @return [String]
|
29
|
+
def string_to_const_name(string_ast)
|
30
|
+
return unless string_ast.type == :STR
|
31
|
+
|
32
|
+
name = string_ast.children[0]
|
33
|
+
return 'Anonymous'.dup if name.empty?
|
34
|
+
|
35
|
+
# Convert to CamelCase.
|
36
|
+
name = +" #{name}"
|
37
|
+
name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
|
38
|
+
match = ::Regexp.last_match[1]
|
39
|
+
match.upcase!
|
40
|
+
match
|
41
|
+
end
|
42
|
+
|
43
|
+
name.lstrip! # Remove leading whitespace
|
44
|
+
name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
|
45
|
+
|
46
|
+
# Ruby requires first const letter to be A-Z. Use `Nested`
|
47
|
+
# as necessary to enforce that.
|
48
|
+
name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
|
49
|
+
|
50
|
+
name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'walker'
|
4
|
+
require_relative 'spec_walker/node_types'
|
5
|
+
require_relative 'spec_walker/full_constant_name'
|
6
|
+
require_relative 'spec_walker/rspec_context_namespace'
|
7
|
+
require_relative 'spec_walker/fake_let_method'
|
4
8
|
|
5
9
|
module Solargraph
|
6
10
|
module Rspec
|
@@ -10,7 +14,7 @@ module Solargraph
|
|
10
14
|
def initialize(source_map:, config:)
|
11
15
|
@source_map = source_map
|
12
16
|
@config = config
|
13
|
-
@walker = Rspec::Walker.
|
17
|
+
@walker = Rspec::Walker.new(source_map.source.node)
|
14
18
|
@handlers = {
|
15
19
|
on_described_class: [],
|
16
20
|
on_let_method: [],
|
@@ -30,24 +34,34 @@ module Solargraph
|
|
30
34
|
attr_reader :config
|
31
35
|
|
32
36
|
# @param block [Proc]
|
37
|
+
# @yieldparam class_name [String]
|
38
|
+
# @yieldparam location_range [Solargraph::Range]
|
33
39
|
# @return [void]
|
34
40
|
def on_described_class(&block)
|
35
41
|
@handlers[:on_described_class] << block
|
36
42
|
end
|
37
43
|
|
38
44
|
# @param block [Proc]
|
45
|
+
# @yieldparam method_name [String]
|
46
|
+
# @yieldparam location_range [Solargraph::Range]
|
47
|
+
# @yieldparam fake_method_ast [RubyVM::AbstractSyntaxTree::Node]
|
39
48
|
# @return [void]
|
40
49
|
def on_let_method(&block)
|
41
50
|
@handlers[:on_let_method] << block
|
42
51
|
end
|
43
52
|
|
44
53
|
# @param block [Proc]
|
54
|
+
# @yieldparam method_name [String]
|
55
|
+
# @yieldparam location_range [Solargraph::Range]
|
56
|
+
# @yieldparam fake_method_ast [RubyVM::AbstractSyntaxTree::Node]
|
45
57
|
# @return [void]
|
46
58
|
def on_subject(&block)
|
47
59
|
@handlers[:on_subject] << block
|
48
60
|
end
|
49
61
|
|
50
62
|
# @param block [Proc]
|
63
|
+
# @yieldparam namespace_name [String]
|
64
|
+
# @yieldparam location_range [Solargraph::Range]
|
51
65
|
# @return [void]
|
52
66
|
def on_each_context_block(&block)
|
53
67
|
@handlers[:on_each_context_block] << block
|
@@ -55,18 +69,21 @@ module Solargraph
|
|
55
69
|
|
56
70
|
#
|
57
71
|
# @param block [Proc]
|
72
|
+
# @yieldparam location_range [Solargraph::Range]
|
58
73
|
# @return [void]
|
59
74
|
def on_example_block(&block)
|
60
75
|
@handlers[:on_example_block] << block
|
61
76
|
end
|
62
77
|
|
63
78
|
# @param block [Proc]
|
79
|
+
# @yieldparam location_range [Solargraph::Range]
|
64
80
|
# @return [void]
|
65
81
|
def on_hook_block(&block)
|
66
82
|
@handlers[:on_hook_block] << block
|
67
83
|
end
|
68
84
|
|
69
85
|
# @param block [Proc]
|
86
|
+
# @yieldparam location_range [Solargraph::Range]
|
70
87
|
# @return [void]
|
71
88
|
def on_blocks_in_examples(&block)
|
72
89
|
@handlers[:on_blocks_in_examples] << block
|
@@ -80,77 +97,72 @@ module Solargraph
|
|
80
97
|
|
81
98
|
# @return [void]
|
82
99
|
def walk!
|
83
|
-
each_context_block(@walker.ast, Rspec::ROOT_NAMESPACE) do |namespace_name,
|
100
|
+
each_context_block(@walker.ast, Rspec::ROOT_NAMESPACE) do |namespace_name, block_ast|
|
101
|
+
desc_node = NodeTypes.context_description_node(block_ast)
|
102
|
+
|
84
103
|
@handlers[:on_each_context_block].each do |handler|
|
85
|
-
handler.call(namespace_name,
|
104
|
+
handler.call(namespace_name, PinFactory.build_location_range(block_ast))
|
86
105
|
end
|
87
|
-
end
|
88
106
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
class_name = full_constant_name(class_ast)
|
96
|
-
handler.call(class_ast, class_name)
|
107
|
+
if NodeTypes.a_constant?(desc_node) # rubocop:disable Style/Next
|
108
|
+
@handlers[:on_described_class].each do |handler|
|
109
|
+
class_name_ast = NodeTypes.context_description_node(block_ast)
|
110
|
+
class_name = FullConstantName.from_ast(class_name_ast)
|
111
|
+
handler.call(class_name, PinFactory.build_location_range(class_name_ast))
|
112
|
+
end
|
97
113
|
end
|
98
114
|
end
|
99
115
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
116
|
+
walker.on :ITER do |block_ast|
|
117
|
+
next unless NodeTypes.a_let_block?(block_ast, config)
|
118
|
+
|
119
|
+
method_name = NodeTypes.let_method_name(block_ast)
|
120
|
+
next unless method_name
|
121
|
+
|
122
|
+
fake_method_ast = FakeLetMethod.transform_block(block_ast, @source_map.source.code)
|
123
|
+
|
124
|
+
@handlers[:on_let_method].each do |handler|
|
125
|
+
handler.call(method_name, PinFactory.build_location_range(block_ast.children[0]), fake_method_ast)
|
105
126
|
end
|
106
127
|
end
|
107
128
|
|
108
|
-
walker.on :
|
109
|
-
next
|
129
|
+
walker.on :ITER do |block_ast|
|
130
|
+
next unless NodeTypes.a_subject_block?(block_ast)
|
110
131
|
|
111
|
-
|
112
|
-
|
113
|
-
next unless Rspec::SUBJECT_METHODS.include?(method_name.to_s)
|
132
|
+
method_name = NodeTypes.let_method_name(block_ast)
|
133
|
+
fake_method_ast = FakeLetMethod.transform_block(block_ast, @source_map.source.code, method_name || 'subject')
|
114
134
|
|
115
135
|
@handlers[:on_subject].each do |handler|
|
116
|
-
handler.call(
|
136
|
+
handler.call(method_name, PinFactory.build_location_range(block_ast.children[0]), fake_method_ast)
|
117
137
|
end
|
118
138
|
end
|
119
139
|
|
120
|
-
walker.on :
|
121
|
-
next
|
122
|
-
|
123
|
-
method_ast = block_ast.children.first
|
124
|
-
method_name = method_ast.children[1]
|
125
|
-
next unless Rspec::EXAMPLE_METHODS.include?(method_name.to_s)
|
140
|
+
walker.on :ITER do |block_ast|
|
141
|
+
next unless NodeTypes.a_example_block?(block_ast)
|
126
142
|
|
127
143
|
@handlers[:on_example_block].each do |handler|
|
128
|
-
handler.call(block_ast)
|
144
|
+
handler.call(PinFactory.build_location_range(block_ast))
|
129
145
|
end
|
130
146
|
|
131
|
-
# @param blocks_in_examples [
|
132
|
-
each_block(block_ast.children[
|
147
|
+
# @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
|
148
|
+
each_block(block_ast.children[1]) do |blocks_in_examples|
|
133
149
|
@handlers[:on_blocks_in_examples].each do |handler|
|
134
|
-
handler.call(blocks_in_examples)
|
150
|
+
handler.call(PinFactory.build_location_range(blocks_in_examples))
|
135
151
|
end
|
136
152
|
end
|
137
153
|
end
|
138
154
|
|
139
|
-
walker.on :
|
140
|
-
next
|
141
|
-
|
142
|
-
method_ast = block_ast.children.first
|
143
|
-
method_name = method_ast.children[1]
|
144
|
-
next unless Rspec::HOOK_METHODS.include?(method_name.to_s)
|
155
|
+
walker.on :ITER do |block_ast|
|
156
|
+
next unless NodeTypes.a_hook_block?(block_ast)
|
145
157
|
|
146
158
|
@handlers[:on_hook_block].each do |handler|
|
147
|
-
handler.call(block_ast)
|
159
|
+
handler.call(PinFactory.build_location_range(block_ast))
|
148
160
|
end
|
149
161
|
|
150
|
-
# @param blocks_in_examples [
|
151
|
-
each_block(block_ast.children[
|
162
|
+
# @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
|
163
|
+
each_block(block_ast.children[1]) do |blocks_in_examples|
|
152
164
|
@handlers[:on_blocks_in_examples].each do |handler|
|
153
|
-
handler.call(blocks_in_examples)
|
165
|
+
handler.call(PinFactory.build_location_range(blocks_in_examples))
|
154
166
|
end
|
155
167
|
end
|
156
168
|
end
|
@@ -165,9 +177,9 @@ module Solargraph
|
|
165
177
|
# @param ast [Parser::AST::Node]
|
166
178
|
# @param parent_result [Object]
|
167
179
|
def each_block(ast, parent_result = nil, &block)
|
168
|
-
return unless ast.is_a?(::
|
180
|
+
return unless ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
169
181
|
|
170
|
-
is_a_block =
|
182
|
+
is_a_block = NodeTypes.a_block?(ast)
|
171
183
|
|
172
184
|
if is_a_block
|
173
185
|
result = block&.call(ast, parent_result)
|
@@ -182,12 +194,11 @@ module Solargraph
|
|
182
194
|
# @yield [String, Parser::AST::Node]
|
183
195
|
def each_context_block(ast, root_namespace = Rspec::ROOT_NAMESPACE, &block)
|
184
196
|
each_block(ast, root_namespace) do |block_ast, parent_namespace|
|
185
|
-
is_a_context =
|
197
|
+
is_a_context = NodeTypes.a_context_block?(block_ast)
|
186
198
|
|
187
199
|
next unless is_a_context
|
188
200
|
|
189
|
-
|
190
|
-
block_name = rspec_describe_class_name(description_node)
|
201
|
+
block_name = RspecContextNamespace.from_block_ast(block_ast)
|
191
202
|
next unless block_name
|
192
203
|
|
193
204
|
parent_namespace = namespace_name = "#{parent_namespace}::#{block_name}"
|
@@ -195,59 +206,6 @@ module Solargraph
|
|
195
206
|
next parent_namespace
|
196
207
|
end
|
197
208
|
end
|
198
|
-
|
199
|
-
# @param ast [Parser::AST::Node]
|
200
|
-
# @return [String, nil]
|
201
|
-
def rspec_describe_class_name(ast)
|
202
|
-
if ast.type == :str
|
203
|
-
string_to_const_name(ast)
|
204
|
-
elsif ast.type == :const
|
205
|
-
full_constant_name(ast).gsub('::', '')
|
206
|
-
else
|
207
|
-
Solargraph.logger.warn "[RSpec] Unexpected AST type #{ast.type}"
|
208
|
-
nil
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
# @param ast [Parser::AST::Node]
|
213
|
-
# @return [String]
|
214
|
-
def full_constant_name(ast)
|
215
|
-
raise 'Node is not a constant' unless ast.type == :const
|
216
|
-
|
217
|
-
name = ast.children[1].to_s
|
218
|
-
if ast.children[0].nil?
|
219
|
-
name
|
220
|
-
else
|
221
|
-
"#{full_constant_name(ast.children[0])}::#{name}"
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
# @see https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example_group.rb#L862
|
226
|
-
# @param ast [Parser::AST::Node]
|
227
|
-
# @return [String]
|
228
|
-
def string_to_const_name(string_ast)
|
229
|
-
return unless string_ast.type == :str
|
230
|
-
|
231
|
-
name = string_ast.children[0]
|
232
|
-
return 'Anonymous'.dup if name.empty?
|
233
|
-
|
234
|
-
# Convert to CamelCase.
|
235
|
-
name = +" #{name}"
|
236
|
-
name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
|
237
|
-
match = ::Regexp.last_match[1]
|
238
|
-
match.upcase!
|
239
|
-
match
|
240
|
-
end
|
241
|
-
|
242
|
-
name.lstrip! # Remove leading whitespace
|
243
|
-
name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
|
244
|
-
|
245
|
-
# Ruby requires first const letter to be A-Z. Use `Nested`
|
246
|
-
# as necessary to enforce that.
|
247
|
-
name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
|
248
|
-
|
249
|
-
name
|
250
|
-
end
|
251
209
|
end
|
252
210
|
end
|
253
211
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Credits: This file is a copy of the original file from the solargraph-rails gem.
|
4
|
-
|
5
3
|
module Solargraph
|
6
4
|
module Rspec
|
7
5
|
class Walker
|
6
|
+
class ParsingError < StandardError; end
|
7
|
+
|
8
8
|
class Hook
|
9
9
|
attr_reader :node_type
|
10
10
|
|
@@ -17,7 +17,7 @@ module Solargraph
|
|
17
17
|
@proc = Proc.new(&block)
|
18
18
|
end
|
19
19
|
|
20
|
-
# @param node [
|
20
|
+
# @param node [RubyVM::AbstractSyntaxTree::Node]
|
21
21
|
# @return [void]
|
22
22
|
def visit(node)
|
23
23
|
return unless matches?(node)
|
@@ -33,15 +33,15 @@ module Solargraph
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
# @param node [
|
36
|
+
# @param node [RubyVM::AbstractSyntaxTree::Node]
|
37
37
|
# @return [Boolean]
|
38
38
|
def matches?(node)
|
39
39
|
return false unless node.type == node_type
|
40
40
|
return false unless node.children
|
41
41
|
return true if @args.empty?
|
42
42
|
|
43
|
-
a_child_matches = node.children.first.is_a?(::
|
44
|
-
child.is_a?(::
|
43
|
+
a_child_matches = node.children.first.is_a?(RubyVM::AbstractSyntaxTree::Node) && node.children.any? do |child|
|
44
|
+
child.is_a?(RubyVM::AbstractSyntaxTree::Node) &&
|
45
45
|
match_children(child.children, @args[1..])
|
46
46
|
end
|
47
47
|
|
@@ -50,12 +50,12 @@ module Solargraph
|
|
50
50
|
match_children(node.children)
|
51
51
|
end
|
52
52
|
|
53
|
-
# @param children [Array<
|
53
|
+
# @param children [Array<RubyVM::AbstractSyntaxTree::Node>]
|
54
54
|
def match_children(children, args = @args)
|
55
55
|
args.each_with_index.all? do |arg, i|
|
56
56
|
if arg == :any
|
57
57
|
true
|
58
|
-
elsif children[i].is_a?(::
|
58
|
+
elsif children[i].is_a?(RubyVM::AbstractSyntaxTree::Node) && arg.is_a?(Symbol)
|
59
59
|
children[i].type == arg
|
60
60
|
else
|
61
61
|
children[i] == arg
|
@@ -64,28 +64,9 @@ module Solargraph
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
|
-
def self.normalize_ast(source)
|
69
|
-
ast = source.node
|
70
|
-
|
71
|
-
if ast.is_a?(::Parser::AST::Node)
|
72
|
-
ast
|
73
|
-
else
|
74
|
-
NodeParser.parse_with_comments(source.code, source.filename)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# @param source [Solargraph::Source]
|
79
|
-
def self.from_source(source)
|
80
|
-
new(*normalize_ast(source))
|
81
|
-
end
|
82
|
-
|
83
|
-
# @return ast [Parser::AST::Node]
|
84
|
-
attr_reader :ast
|
85
|
-
# @return comments [Hash]
|
86
|
-
attr_reader :comments
|
67
|
+
attr_reader :ast, :comments
|
87
68
|
|
88
|
-
# @param ast [
|
69
|
+
# @param ast [RubyVM::AbstractSyntaxTree::Node]
|
89
70
|
# @param comments [Hash]
|
90
71
|
def initialize(ast, comments = {})
|
91
72
|
@comments = comments
|
@@ -93,23 +74,18 @@ module Solargraph
|
|
93
74
|
@hooks = Hash.new([])
|
94
75
|
end
|
95
76
|
|
96
|
-
# @param node_type [Symbol]
|
97
|
-
# @param args [Array]
|
98
|
-
# @param block [Proc]
|
99
77
|
def on(node_type, args = [], &block)
|
100
78
|
@hooks[node_type] << Hook.new(node_type, args, &block)
|
101
79
|
end
|
102
80
|
|
103
|
-
# @return [void]
|
104
81
|
def walk
|
105
82
|
@ast.is_a?(Array) ? @ast.each { |node| traverse(node) } : traverse(@ast)
|
106
83
|
end
|
107
84
|
|
108
85
|
private
|
109
86
|
|
110
|
-
# @param node [Parser::AST::Node]
|
111
87
|
def traverse(node)
|
112
|
-
return unless node.is_a?(::
|
88
|
+
return unless node.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
113
89
|
|
114
90
|
@hooks[node.type].each { |hook| hook.visit(node) }
|
115
91
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solargraph-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LekΓ« Mula
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: solargraph
|
@@ -58,8 +58,12 @@ files:
|
|
58
58
|
- lib/solargraph/rspec/correctors/let_methods_corrector.rb
|
59
59
|
- lib/solargraph/rspec/correctors/subject_method_corrector.rb
|
60
60
|
- lib/solargraph/rspec/correctors/walker_base.rb
|
61
|
+
- lib/solargraph/rspec/pin_factory.rb
|
61
62
|
- lib/solargraph/rspec/spec_walker.rb
|
62
|
-
- lib/solargraph/rspec/
|
63
|
+
- lib/solargraph/rspec/spec_walker/fake_let_method.rb
|
64
|
+
- lib/solargraph/rspec/spec_walker/full_constant_name.rb
|
65
|
+
- lib/solargraph/rspec/spec_walker/node_types.rb
|
66
|
+
- lib/solargraph/rspec/spec_walker/rspec_context_namespace.rb
|
63
67
|
- lib/solargraph/rspec/version.rb
|
64
68
|
- lib/solargraph/rspec/walker.rb
|
65
69
|
- lib/solargraph_rspec.rb
|
@@ -1,77 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Credits: This file is a copy of the file from the solargraph-rspec gem
|
4
|
-
|
5
|
-
# rubocop:disable Naming/MethodParameterName
|
6
|
-
module Solargraph
|
7
|
-
module Rspec
|
8
|
-
# Utility methods for building pins and references.
|
9
|
-
module Util
|
10
|
-
def self.build_public_method(
|
11
|
-
ns,
|
12
|
-
name,
|
13
|
-
types: nil,
|
14
|
-
location: nil,
|
15
|
-
comments: [],
|
16
|
-
attribute: false,
|
17
|
-
scope: :instance
|
18
|
-
)
|
19
|
-
opts = {
|
20
|
-
name: name,
|
21
|
-
location: location,
|
22
|
-
closure: ns,
|
23
|
-
scope: scope,
|
24
|
-
attribute: attribute,
|
25
|
-
comments: []
|
26
|
-
}
|
27
|
-
|
28
|
-
comments << "@return [#{types.join(",")}]" if types
|
29
|
-
|
30
|
-
opts[:comments] = comments.join("\n")
|
31
|
-
|
32
|
-
Solargraph::Pin::Method.new(**opts)
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.build_module_include(ns, module_name, location)
|
36
|
-
Solargraph::Pin::Reference::Include.new(
|
37
|
-
closure: ns,
|
38
|
-
name: module_name,
|
39
|
-
location: location
|
40
|
-
)
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.build_module_extend(ns, module_name, location)
|
44
|
-
Solargraph::Pin::Reference::Extend.new(
|
45
|
-
closure: ns,
|
46
|
-
name: module_name,
|
47
|
-
location: location
|
48
|
-
)
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.dummy_location(path)
|
52
|
-
Solargraph::Location.new(
|
53
|
-
File.expand_path(path),
|
54
|
-
Solargraph::Range.from_to(0, 0, 0, 0)
|
55
|
-
)
|
56
|
-
end
|
57
|
-
|
58
|
-
# @param ast [Parser::AST::Node]
|
59
|
-
def self.build_location(ast, path)
|
60
|
-
Solargraph::Location.new(
|
61
|
-
File.expand_path(path),
|
62
|
-
Solargraph::Range.from_to(
|
63
|
-
ast.location.first_line,
|
64
|
-
ast.location.column,
|
65
|
-
ast.location.last_line,
|
66
|
-
ast.location.last_column
|
67
|
-
)
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.method_return(path, type)
|
72
|
-
Solargraph::Pin::Reference::Override.method_return(path, type)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
# rubocop:enable Naming/MethodParameterName
|