solargraph-rspec 0.1.0 β 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 +22 -1
- data/README.md +7 -3
- data/lib/solargraph/rspec/convention.rb +16 -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 +20 -14
- 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 +61 -97
- data/lib/solargraph/rspec/version.rb +1 -1
- data/lib/solargraph/rspec/walker.rb +11 -35
- data/lib/solargraph-rspec.rb +0 -1
- data/solargraph-rspec.gemspec +0 -1
- metadata +7 -17
- 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,28 @@ 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
|
24
|
+
|
25
|
+
## [0.1.1] - 2024-05-13
|
26
|
+
|
27
|
+
### Removed
|
28
|
+
- Removed redundant `active_support` dependency ([Issue #2](https://github.com/lekemula/solargraph-rspec/issues/2))
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
- Fixed completions inside `subject` and `subject!` blocks
|
11
32
|
|
12
33
|
## [0.1.0] - 2024-05-11 (First Release π)
|
13
34
|
|
data/README.md
CHANGED
@@ -1,20 +1,24 @@
|
|
1
|
-
# Solargraph::Rspec - A Solargraph plugin for better RSpec support
|
1
|
+
# Solargraph::Rspec - A [Solargraph](https://solargraph.org/) plugin for better [RSpec](https://rspec.info/) support
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/solargraph-rspec.svg)](https://badge.fury.io/rb/solargraph-rspec)
|
3
4
|
![Tests](https://github.com/lekemula/solargraph-rspec/actions/workflows/ruby.yml/badge.svg)
|
4
5
|
[![codecov](https://codecov.io/gh/lekemula/solargraph-rspec/graph/badge.svg?token=FH7ER8ZDPW)](https://codecov.io/gh/lekemula/solargraph-rspec)
|
5
6
|
|
6
7
|
|
7
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.
|
8
9
|
|
9
|
-
This gem aims to provide
|
10
|
+
This gem aims to provide better support for RSpec in Solargraph and it supports the following features:
|
10
11
|
- `describe` and `it` methods completion
|
11
12
|
- memoized `let` and `let!` methods completion
|
12
13
|
- implicit and explicit `subject` methods
|
13
14
|
- `described_class` with appropriate type inference
|
14
15
|
- `RSpec::Matchers` methods completion
|
16
|
+
- type inference π
|
15
17
|
- and more to come...
|
16
18
|
|
17
|
-
|
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
|
+
|
18
22
|
|
19
23
|
## Installation
|
20
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
|
@@ -18,6 +18,7 @@ module Solargraph
|
|
18
18
|
HELPER_MODULES = ['RSpec::Matchers'].freeze
|
19
19
|
HOOK_METHODS = %w[before after around].freeze
|
20
20
|
LET_METHODS = %w[let let!].freeze
|
21
|
+
SUBJECT_METHODS = %w[subject subject!].freeze
|
21
22
|
EXAMPLE_METHODS = %w[
|
22
23
|
example
|
23
24
|
it
|
@@ -120,14 +121,20 @@ module Solargraph
|
|
120
121
|
|
121
122
|
# @type [Pin::Method, nil]
|
122
123
|
described_class_pin = nil
|
123
|
-
Correctors::DescribedClassCorrector.new(
|
124
|
+
Correctors::DescribedClassCorrector.new(
|
125
|
+
namespace_pins: namespace_pins,
|
126
|
+
rspec_walker: rspec_walker
|
127
|
+
).correct(
|
124
128
|
source_map
|
125
129
|
) do |pins_to_add|
|
126
130
|
described_class_pin = pins_to_add.first
|
127
131
|
pins += pins_to_add
|
128
132
|
end
|
129
133
|
|
130
|
-
Correctors::LetMethodsCorrector.new(
|
134
|
+
Correctors::LetMethodsCorrector.new(
|
135
|
+
namespace_pins: namespace_pins,
|
136
|
+
rspec_walker: rspec_walker
|
137
|
+
).correct(
|
131
138
|
source_map
|
132
139
|
) do |pins_to_add|
|
133
140
|
pins += pins_to_add
|
@@ -135,7 +142,10 @@ module Solargraph
|
|
135
142
|
|
136
143
|
# @type [Pin::Method, nil]
|
137
144
|
subject_pin = nil
|
138
|
-
Correctors::SubjectMethodCorrector.new(
|
145
|
+
Correctors::SubjectMethodCorrector.new(
|
146
|
+
namespace_pins: namespace_pins,
|
147
|
+
rspec_walker: rspec_walker
|
148
|
+
).correct(
|
139
149
|
source_map
|
140
150
|
) do |pins_to_add|
|
141
151
|
subject_pin = pins_to_add.first
|
@@ -198,7 +208,7 @@ module Solargraph
|
|
198
208
|
# @return [Array<Pin::Base>]
|
199
209
|
def include_helper_pins(helper_modules: HELPER_MODULES)
|
200
210
|
helper_modules.map do |helper_module|
|
201
|
-
|
211
|
+
PinFactory.build_module_include(
|
202
212
|
root_example_group_namespace_pin,
|
203
213
|
helper_module,
|
204
214
|
root_example_group_namespace_pin.location
|
@@ -215,7 +225,7 @@ module Solargraph
|
|
215
225
|
def root_example_group_namespace_pin
|
216
226
|
Solargraph::Pin::Namespace.new(
|
217
227
|
name: ROOT_NAMESPACE,
|
218
|
-
location:
|
228
|
+
location: PinFactory.dummy_location('lib/rspec/core/example_group.rb')
|
219
229
|
)
|
220
230
|
end
|
221
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,26 +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
|
+
|
35
|
+
yield [] if block_given?
|
36
|
+
end
|
37
|
+
|
38
|
+
rspec_walker.on_subject do |_method_name, location_range|
|
39
|
+
bind_closest_namespace(location_range, source_map)
|
34
40
|
|
35
41
|
yield [] if block_given?
|
36
42
|
end
|
@@ -38,15 +44,15 @@ module Solargraph
|
|
38
44
|
|
39
45
|
private
|
40
46
|
|
41
|
-
# @param
|
47
|
+
# @param location_range [Solargraph::Range]
|
42
48
|
# @param source_map [Solargraph::SourceMap]
|
43
49
|
# @return [void]
|
44
|
-
def bind_closest_namespace(
|
45
|
-
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)
|
46
52
|
return unless namespace_pin
|
47
53
|
|
48
|
-
original_block_pin = source_map.locate_block_pin(
|
49
|
-
|
54
|
+
original_block_pin = source_map.locate_block_pin(location_range.start.line,
|
55
|
+
location_range.start.column)
|
50
56
|
original_block_pin_index = source_map.pins.index(original_block_pin)
|
51
57
|
fixed_namespace_block_pin = Solargraph::Pin::Block.new(
|
52
58
|
closure: example_run_method(namespace_pin),
|
@@ -61,7 +67,7 @@ module Solargraph
|
|
61
67
|
# @param namespace_pin [Solargraph::Pin::Namespace]
|
62
68
|
# @return [Solargraph::Pin::Method]
|
63
69
|
def example_run_method(namespace_pin)
|
64
|
-
|
70
|
+
PinFactory.build_public_method(
|
65
71
|
namespace_pin,
|
66
72
|
'run',
|
67
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,71 +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 :
|
129
|
+
walker.on :ITER do |block_ast|
|
130
|
+
next unless NodeTypes.a_subject_block?(block_ast)
|
131
|
+
|
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')
|
134
|
+
|
109
135
|
@handlers[:on_subject].each do |handler|
|
110
|
-
handler.call(
|
136
|
+
handler.call(method_name, PinFactory.build_location_range(block_ast.children[0]), fake_method_ast)
|
111
137
|
end
|
112
138
|
end
|
113
139
|
|
114
|
-
walker.on :
|
115
|
-
next
|
116
|
-
|
117
|
-
method_ast = block_ast.children.first
|
118
|
-
method_name = method_ast.children[1]
|
119
|
-
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)
|
120
142
|
|
121
143
|
@handlers[:on_example_block].each do |handler|
|
122
|
-
handler.call(block_ast)
|
144
|
+
handler.call(PinFactory.build_location_range(block_ast))
|
123
145
|
end
|
124
146
|
|
125
|
-
# @param blocks_in_examples [
|
126
|
-
each_block(block_ast.children[
|
147
|
+
# @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
|
148
|
+
each_block(block_ast.children[1]) do |blocks_in_examples|
|
127
149
|
@handlers[:on_blocks_in_examples].each do |handler|
|
128
|
-
handler.call(blocks_in_examples)
|
150
|
+
handler.call(PinFactory.build_location_range(blocks_in_examples))
|
129
151
|
end
|
130
152
|
end
|
131
153
|
end
|
132
154
|
|
133
|
-
walker.on :
|
134
|
-
next
|
135
|
-
|
136
|
-
method_ast = block_ast.children.first
|
137
|
-
method_name = method_ast.children[1]
|
138
|
-
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)
|
139
157
|
|
140
158
|
@handlers[:on_hook_block].each do |handler|
|
141
|
-
handler.call(block_ast)
|
159
|
+
handler.call(PinFactory.build_location_range(block_ast))
|
142
160
|
end
|
143
161
|
|
144
|
-
# @param blocks_in_examples [
|
145
|
-
each_block(block_ast.children[
|
162
|
+
# @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
|
163
|
+
each_block(block_ast.children[1]) do |blocks_in_examples|
|
146
164
|
@handlers[:on_blocks_in_examples].each do |handler|
|
147
|
-
handler.call(blocks_in_examples)
|
165
|
+
handler.call(PinFactory.build_location_range(blocks_in_examples))
|
148
166
|
end
|
149
167
|
end
|
150
168
|
end
|
@@ -159,9 +177,9 @@ module Solargraph
|
|
159
177
|
# @param ast [Parser::AST::Node]
|
160
178
|
# @param parent_result [Object]
|
161
179
|
def each_block(ast, parent_result = nil, &block)
|
162
|
-
return unless ast.is_a?(::
|
180
|
+
return unless ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
163
181
|
|
164
|
-
is_a_block =
|
182
|
+
is_a_block = NodeTypes.a_block?(ast)
|
165
183
|
|
166
184
|
if is_a_block
|
167
185
|
result = block&.call(ast, parent_result)
|
@@ -176,12 +194,11 @@ module Solargraph
|
|
176
194
|
# @yield [String, Parser::AST::Node]
|
177
195
|
def each_context_block(ast, root_namespace = Rspec::ROOT_NAMESPACE, &block)
|
178
196
|
each_block(ast, root_namespace) do |block_ast, parent_namespace|
|
179
|
-
is_a_context =
|
197
|
+
is_a_context = NodeTypes.a_context_block?(block_ast)
|
180
198
|
|
181
199
|
next unless is_a_context
|
182
200
|
|
183
|
-
|
184
|
-
block_name = rspec_describe_class_name(description_node)
|
201
|
+
block_name = RspecContextNamespace.from_block_ast(block_ast)
|
185
202
|
next unless block_name
|
186
203
|
|
187
204
|
parent_namespace = namespace_name = "#{parent_namespace}::#{block_name}"
|
@@ -189,59 +206,6 @@ module Solargraph
|
|
189
206
|
next parent_namespace
|
190
207
|
end
|
191
208
|
end
|
192
|
-
|
193
|
-
# @param ast [Parser::AST::Node]
|
194
|
-
# @return [String, nil]
|
195
|
-
def rspec_describe_class_name(ast)
|
196
|
-
if ast.type == :str
|
197
|
-
string_to_const_name(ast)
|
198
|
-
elsif ast.type == :const
|
199
|
-
full_constant_name(ast).gsub('::', '')
|
200
|
-
else
|
201
|
-
Solargraph.logger.warn "[RSpec] Unexpected AST type #{ast.type}"
|
202
|
-
nil
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
# @param ast [Parser::AST::Node]
|
207
|
-
# @return [String]
|
208
|
-
def full_constant_name(ast)
|
209
|
-
raise 'Node is not a constant' unless ast.type == :const
|
210
|
-
|
211
|
-
name = ast.children[1].to_s
|
212
|
-
if ast.children[0].nil?
|
213
|
-
name
|
214
|
-
else
|
215
|
-
"#{full_constant_name(ast.children[0])}::#{name}"
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
# @see https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example_group.rb#L862
|
220
|
-
# @param ast [Parser::AST::Node]
|
221
|
-
# @return [String]
|
222
|
-
def string_to_const_name(string_ast)
|
223
|
-
return unless string_ast.type == :str
|
224
|
-
|
225
|
-
name = string_ast.children[0]
|
226
|
-
return 'Anonymous'.dup if name.empty?
|
227
|
-
|
228
|
-
# Convert to CamelCase.
|
229
|
-
name = +" #{name}"
|
230
|
-
name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
|
231
|
-
match = ::Regexp.last_match[1]
|
232
|
-
match.upcase!
|
233
|
-
match
|
234
|
-
end
|
235
|
-
|
236
|
-
name.lstrip! # Remove leading whitespace
|
237
|
-
name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
|
238
|
-
|
239
|
-
# Ruby requires first const letter to be A-Z. Use `Nested`
|
240
|
-
# as necessary to enforce that.
|
241
|
-
name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
|
242
|
-
|
243
|
-
name
|
244
|
-
end
|
245
209
|
end
|
246
210
|
end
|
247
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
|
|
data/lib/solargraph-rspec.rb
CHANGED
data/solargraph-rspec.gemspec
CHANGED
@@ -28,7 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
29
|
spec.require_paths = ['lib']
|
30
30
|
|
31
|
-
spec.add_runtime_dependency 'activesupport', '~> 6.0'
|
32
31
|
spec.add_runtime_dependency 'solargraph', '~> 0.49', '>= 0.49.0'
|
33
32
|
|
34
33
|
# For more information and examples about making a new gem, check out our
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
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
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: activesupport
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '6.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '6.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: solargraph
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,8 +58,12 @@ files:
|
|
72
58
|
- lib/solargraph/rspec/correctors/let_methods_corrector.rb
|
73
59
|
- lib/solargraph/rspec/correctors/subject_method_corrector.rb
|
74
60
|
- lib/solargraph/rspec/correctors/walker_base.rb
|
61
|
+
- lib/solargraph/rspec/pin_factory.rb
|
75
62
|
- lib/solargraph/rspec/spec_walker.rb
|
76
|
-
- 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
|
77
67
|
- lib/solargraph/rspec/version.rb
|
78
68
|
- lib/solargraph/rspec/walker.rb
|
79
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
|