ruby-lsp-rspec 0.1.22 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.rubocop.yml +4 -1
- data/lib/ruby-lsp-rspec.rb +0 -1
- data/lib/ruby_lsp/ruby_lsp_rspec/addon.rb +121 -41
- data/lib/ruby_lsp/ruby_lsp_rspec/code_lens.rb +15 -40
- data/lib/ruby_lsp/ruby_lsp_rspec/definition.rb +2 -15
- data/lib/ruby_lsp/ruby_lsp_rspec/document_symbol.rb +6 -13
- data/lib/ruby_lsp/ruby_lsp_rspec/indexing_enhancement.rb +4 -5
- data/lib/ruby_lsp/ruby_lsp_rspec/rspec_formatter.rb +66 -0
- data/lib/ruby_lsp/ruby_lsp_rspec/spec_style_patch.rb +14 -0
- data/lib/ruby_lsp/ruby_lsp_rspec/test_discovery.rb +131 -0
- data/lib/ruby_lsp_rspec/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6204c70ec6917f93238449949f16c24892d0145cb10dd32c42819021ef617c83
|
4
|
+
data.tar.gz: 510b118788f0469ef5783453042fb5c4f56aef9da2f77f0b1567b77e73d0b959
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '04947d8bf9659b8d8aa213ea2d305f7d2049719a30aa753e30f289da98b6e41991118681405a3166aac93f5c8d23d4f2ce9f808dcf99d6a1f43aabb85c564980'
|
7
|
+
data.tar.gz: 2aab5409230f0783b598fe53511d712e4643f84ec506b63278803ccc93002ca37e981c219041f4fa8b33f768432fa5c2eab13e6941bb76261bddfe9a31b70cb1
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
@@ -2,9 +2,11 @@ inherit_gem:
|
|
2
2
|
rubocop-shopify: rubocop.yml
|
3
3
|
|
4
4
|
require:
|
5
|
-
- rubocop-sorbet
|
6
5
|
- rubocop-rake
|
7
6
|
|
7
|
+
plugins:
|
8
|
+
- rubocop-sorbet
|
9
|
+
|
8
10
|
AllCops:
|
9
11
|
NewCops: disable
|
10
12
|
SuggestExtensions: false
|
@@ -30,6 +32,7 @@ Sorbet/StrictSigil:
|
|
30
32
|
- "lib/**/*.rb"
|
31
33
|
Exclude:
|
32
34
|
- "**/*.rake"
|
35
|
+
- "lib/ruby_lsp/ruby_lsp_rspec/rspec_formatter.rb"
|
33
36
|
- "spec/**/*.rb"
|
34
37
|
|
35
38
|
Style/StderrPuts:
|
data/lib/ruby-lsp-rspec.rb
CHANGED
@@ -8,86 +8,166 @@ require_relative "code_lens"
|
|
8
8
|
require_relative "document_symbol"
|
9
9
|
require_relative "definition"
|
10
10
|
require_relative "indexing_enhancement"
|
11
|
+
require_relative "test_discovery"
|
12
|
+
require_relative "spec_style_patch"
|
11
13
|
|
12
14
|
module RubyLsp
|
13
15
|
module RSpec
|
14
16
|
class Addon < ::RubyLsp::Addon
|
15
|
-
|
17
|
+
FORMATTER_PATH = File.expand_path("rspec_formatter.rb", __dir__) #: String
|
18
|
+
FORMATTER_NAME = "RubyLsp::RSpec::RSpecFormatter" #: String
|
16
19
|
|
17
|
-
|
18
|
-
attr_reader :rspec_command
|
19
|
-
|
20
|
-
sig { returns(T::Boolean) }
|
20
|
+
#: bool
|
21
21
|
attr_reader :debug
|
22
22
|
|
23
|
-
|
23
|
+
#: -> void
|
24
24
|
def initialize
|
25
25
|
super
|
26
|
-
@debug =
|
27
|
-
@rspec_command =
|
26
|
+
@debug = false #: bool
|
27
|
+
@rspec_command = nil #: String?
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
# @override
|
31
|
+
#: (GlobalState, Thread::Queue) -> void
|
31
32
|
def activate(global_state, message_queue)
|
32
|
-
@index =
|
33
|
+
@index = global_state.index #: RubyIndexer::Index?
|
34
|
+
@global_state = global_state #: GlobalState?
|
33
35
|
|
34
36
|
settings = global_state.settings_for_addon(name)
|
35
|
-
@rspec_command =
|
37
|
+
@rspec_command = rspec_command(settings)
|
38
|
+
@workspace_path = global_state.workspace_path #: String?
|
36
39
|
@debug = settings&.dig(:debug) || false
|
37
40
|
end
|
38
41
|
|
39
|
-
|
42
|
+
# @override
|
43
|
+
#: -> void
|
40
44
|
def deactivate; end
|
41
45
|
|
42
|
-
|
46
|
+
# @override
|
47
|
+
#: -> String
|
48
|
+
def name
|
49
|
+
"ruby-lsp-rspec"
|
50
|
+
end
|
51
|
+
|
52
|
+
# @override
|
53
|
+
#: -> String
|
43
54
|
def version
|
44
55
|
VERSION
|
45
56
|
end
|
46
57
|
|
47
58
|
# Creates a new CodeLens listener. This method is invoked on every CodeLens request
|
48
|
-
|
49
|
-
|
50
|
-
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
|
51
|
-
uri: URI::Generic,
|
52
|
-
dispatcher: Prism::Dispatcher,
|
53
|
-
).void
|
54
|
-
end
|
59
|
+
# @override
|
60
|
+
#: (ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens], URI::Generic, Prism::Dispatcher) -> void
|
55
61
|
def create_code_lens_listener(response_builder, uri, dispatcher)
|
56
62
|
return unless uri.to_standardized_path&.end_with?("_test.rb") || uri.to_standardized_path&.end_with?("_spec.rb")
|
63
|
+
return if @global_state&.enabled_feature?(:fullTestDiscovery)
|
64
|
+
|
65
|
+
CodeLens.new(
|
66
|
+
response_builder,
|
67
|
+
uri,
|
68
|
+
dispatcher,
|
69
|
+
@rspec_command, #: as !nil
|
70
|
+
debug: debug,
|
71
|
+
)
|
72
|
+
end
|
57
73
|
|
58
|
-
|
74
|
+
# Creates a new Discover Tests listener. This method is invoked on every DiscoverTests request
|
75
|
+
# @override
|
76
|
+
#: (ResponseBuilders::TestCollection, Prism::Dispatcher, URI::Generic) -> void
|
77
|
+
def create_discover_tests_listener(response_builder, dispatcher, uri)
|
78
|
+
return unless uri.to_standardized_path&.end_with?("_spec.rb")
|
79
|
+
|
80
|
+
TestDiscovery.new(
|
81
|
+
response_builder,
|
82
|
+
dispatcher,
|
83
|
+
uri,
|
84
|
+
@workspace_path, #: as !nil
|
85
|
+
)
|
59
86
|
end
|
60
87
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
88
|
+
# Resolves the minimal set of commands required to execute the requested tests
|
89
|
+
# @override
|
90
|
+
#: (Array[Hash[Symbol, untyped]]) -> Array[String]
|
91
|
+
def resolve_test_commands(items)
|
92
|
+
commands = []
|
93
|
+
queue = items.dup
|
94
|
+
|
95
|
+
full_files = []
|
96
|
+
|
97
|
+
until queue.empty?
|
98
|
+
item = queue.shift #: as !nil
|
99
|
+
tags = Set.new(item[:tags])
|
100
|
+
next unless tags.include?("framework:rspec")
|
101
|
+
|
102
|
+
children = item[:children]
|
103
|
+
uri = URI(item[:uri])
|
104
|
+
path = uri.full_path
|
105
|
+
next unless path
|
106
|
+
|
107
|
+
if tags.include?("test_dir")
|
108
|
+
if children.empty?
|
109
|
+
full_files.concat(Dir.glob(
|
110
|
+
"#{path}/**/*_spec.rb",
|
111
|
+
File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME,
|
112
|
+
))
|
113
|
+
end
|
114
|
+
elsif tags.include?("test_file")
|
115
|
+
full_files << path if children.empty?
|
116
|
+
elsif tags.include?("test_group")
|
117
|
+
start_line = item.dig(:range, :start, :line)
|
118
|
+
commands << "#{@rspec_command} -r #{FORMATTER_PATH} -f #{FORMATTER_NAME} #{path}:#{start_line + 1}"
|
119
|
+
else
|
120
|
+
full_files << "#{path}:#{item.dig(:range, :start, :line) + 1}"
|
121
|
+
end
|
122
|
+
|
123
|
+
queue.concat(children)
|
124
|
+
end
|
125
|
+
|
126
|
+
unless full_files.empty?
|
127
|
+
commands << "#{@rspec_command} -r #{FORMATTER_PATH} -f #{FORMATTER_NAME} #{full_files.join(" ")}"
|
128
|
+
end
|
129
|
+
|
130
|
+
commands
|
66
131
|
end
|
132
|
+
|
133
|
+
# @override
|
134
|
+
#: (ResponseBuilders::DocumentSymbol, Prism::Dispatcher) -> void
|
67
135
|
def create_document_symbol_listener(response_builder, dispatcher)
|
68
136
|
DocumentSymbol.new(response_builder, dispatcher)
|
69
137
|
end
|
70
138
|
|
71
|
-
|
72
|
-
|
73
|
-
response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
|
74
|
-
Interface::Location,
|
75
|
-
Interface::LocationLink,
|
76
|
-
)],
|
77
|
-
uri: URI::Generic,
|
78
|
-
node_context: NodeContext,
|
79
|
-
dispatcher: Prism::Dispatcher,
|
80
|
-
).void
|
81
|
-
end
|
139
|
+
# @override
|
140
|
+
#: (ResponseBuilders::CollectionResponseBuilder[Interface::Location | Interface::LocationLink], URI::Generic, NodeContext, Prism::Dispatcher) -> void
|
82
141
|
def create_definition_listener(response_builder, uri, node_context, dispatcher)
|
83
142
|
return unless uri.to_standardized_path&.end_with?("_test.rb") || uri.to_standardized_path&.end_with?("_spec.rb")
|
84
143
|
|
85
|
-
Definition.new(
|
144
|
+
Definition.new(
|
145
|
+
response_builder,
|
146
|
+
uri,
|
147
|
+
node_context,
|
148
|
+
@index, #: as !nil
|
149
|
+
dispatcher,
|
150
|
+
)
|
86
151
|
end
|
87
152
|
|
88
|
-
|
89
|
-
|
90
|
-
|
153
|
+
private
|
154
|
+
|
155
|
+
#: (Hash[Symbol, untyped]?) -> String
|
156
|
+
def rspec_command(settings)
|
157
|
+
@rspec_command ||= settings&.dig(:rspecCommand) || begin
|
158
|
+
cmd = if File.exist?(File.join(Dir.pwd, "bin", "rspec"))
|
159
|
+
"bin/rspec"
|
160
|
+
else
|
161
|
+
"rspec"
|
162
|
+
end
|
163
|
+
|
164
|
+
begin
|
165
|
+
Bundler.with_original_env { Bundler.default_lockfile }
|
166
|
+
"bundle exec #{cmd}"
|
167
|
+
rescue Bundler::GemfileNotFound
|
168
|
+
cmd
|
169
|
+
end
|
170
|
+
end
|
91
171
|
end
|
92
172
|
end
|
93
173
|
end
|
@@ -4,49 +4,24 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
module RSpec
|
6
6
|
class CodeLens
|
7
|
-
extend T::Sig
|
8
|
-
|
9
7
|
include ::RubyLsp::Requests::Support::Common
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
|
14
|
-
uri: URI::Generic,
|
15
|
-
dispatcher: Prism::Dispatcher,
|
16
|
-
rspec_command: T.nilable(String),
|
17
|
-
debug: T::Boolean,
|
18
|
-
).void
|
19
|
-
end
|
20
|
-
def initialize(response_builder, uri, dispatcher, rspec_command: nil, debug: false)
|
9
|
+
#: (ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens], URI::Generic, Prism::Dispatcher, String, ?debug: bool) -> void
|
10
|
+
def initialize(response_builder, uri, dispatcher, rspec_command, debug: false)
|
21
11
|
@response_builder = response_builder
|
22
12
|
# Listener is only initialized if uri.to_standardized_path is valid
|
23
|
-
|
24
|
-
@
|
25
|
-
@
|
26
|
-
@
|
13
|
+
path = uri.to_standardized_path #: as !nil
|
14
|
+
@path = path #: String
|
15
|
+
@group_id = 1 #: Integer
|
16
|
+
@group_id_stack = [] #: Array[Integer]
|
17
|
+
@rspec_command = rspec_command
|
18
|
+
@anonymous_example_count = 0 #: Integer
|
27
19
|
dispatcher.register(self, :on_call_node_enter, :on_call_node_leave)
|
28
20
|
|
29
21
|
@debug = debug
|
30
|
-
@base_command = T.let(
|
31
|
-
# The user-configured command takes precedence over inferred command default
|
32
|
-
rspec_command || begin
|
33
|
-
cmd = if File.exist?(File.join(Dir.pwd, "bin", "rspec"))
|
34
|
-
"bin/rspec"
|
35
|
-
else
|
36
|
-
"rspec"
|
37
|
-
end
|
38
|
-
|
39
|
-
if File.exist?("Gemfile.lock")
|
40
|
-
"bundle exec #{cmd}"
|
41
|
-
else
|
42
|
-
cmd
|
43
|
-
end
|
44
|
-
end,
|
45
|
-
String,
|
46
|
-
)
|
47
22
|
end
|
48
23
|
|
49
|
-
|
24
|
+
#: (Prism::CallNode) -> void
|
50
25
|
def on_call_node_enter(node)
|
51
26
|
case node.message
|
52
27
|
when "example", "it", "specify"
|
@@ -63,7 +38,7 @@ module RubyLsp
|
|
63
38
|
end
|
64
39
|
end
|
65
40
|
|
66
|
-
|
41
|
+
#: (Prism::CallNode) -> void
|
67
42
|
def on_call_node_leave(node)
|
68
43
|
case node.message
|
69
44
|
when "context", "describe"
|
@@ -75,17 +50,17 @@ module RubyLsp
|
|
75
50
|
|
76
51
|
private
|
77
52
|
|
78
|
-
|
53
|
+
#: (String) -> void
|
79
54
|
def log_message(message)
|
80
55
|
puts "[#{self.class}]: #{message}"
|
81
56
|
end
|
82
57
|
|
83
|
-
|
58
|
+
#: (Prism::CallNode) -> bool
|
84
59
|
def valid_group?(node)
|
85
60
|
!(node.block.nil? || (node.receiver && node.receiver&.slice != "RSpec"))
|
86
61
|
end
|
87
62
|
|
88
|
-
|
63
|
+
#: (Prism::CallNode) -> String
|
89
64
|
def generate_name(node)
|
90
65
|
arguments = node.arguments&.arguments
|
91
66
|
|
@@ -108,10 +83,10 @@ module RubyLsp
|
|
108
83
|
end
|
109
84
|
end
|
110
85
|
|
111
|
-
|
86
|
+
#: (Prism::Node, name: String, kind: Symbol) -> void
|
112
87
|
def add_test_code_lens(node, name:, kind:)
|
113
88
|
line_number = node.location.start_line
|
114
|
-
command = "#{@
|
89
|
+
command = "#{@rspec_command} #{@path}:#{line_number}"
|
115
90
|
|
116
91
|
log_message("Full command: `#{command}`") if @debug
|
117
92
|
|
@@ -4,22 +4,9 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
module RSpec
|
6
6
|
class Definition
|
7
|
-
extend T::Sig
|
8
|
-
|
9
7
|
include ::RubyLsp::Requests::Support::Common
|
10
8
|
|
11
|
-
|
12
|
-
params(
|
13
|
-
response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
|
14
|
-
Interface::Location,
|
15
|
-
Interface::LocationLink,
|
16
|
-
)],
|
17
|
-
uri: URI::Generic,
|
18
|
-
node_context: NodeContext,
|
19
|
-
index: RubyIndexer::Index,
|
20
|
-
dispatcher: Prism::Dispatcher,
|
21
|
-
).void
|
22
|
-
end
|
9
|
+
#: (ResponseBuilders::CollectionResponseBuilder[Interface::LocationLink | Interface::Location], URI::Generic, NodeContext, RubyIndexer::Index, Prism::Dispatcher) -> void
|
23
10
|
def initialize(response_builder, uri, node_context, index, dispatcher)
|
24
11
|
@response_builder = response_builder
|
25
12
|
@uri = uri
|
@@ -28,7 +15,7 @@ module RubyLsp
|
|
28
15
|
dispatcher.register(self, :on_call_node_enter)
|
29
16
|
end
|
30
17
|
|
31
|
-
|
18
|
+
#: (Prism::CallNode) -> void
|
32
19
|
def on_call_node_enter(node)
|
33
20
|
message = node.message
|
34
21
|
return unless message
|
@@ -4,23 +4,16 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
module RSpec
|
6
6
|
class DocumentSymbol
|
7
|
-
extend T::Sig
|
8
|
-
|
9
7
|
include ::RubyLsp::Requests::Support::Common
|
10
8
|
|
11
|
-
|
12
|
-
params(
|
13
|
-
response_builder: ResponseBuilders::DocumentSymbol,
|
14
|
-
dispatcher: Prism::Dispatcher,
|
15
|
-
).void
|
16
|
-
end
|
9
|
+
#: (ResponseBuilders::DocumentSymbol, Prism::Dispatcher) -> void
|
17
10
|
def initialize(response_builder, dispatcher)
|
18
11
|
@response_builder = response_builder
|
19
12
|
|
20
13
|
dispatcher.register(self, :on_call_node_enter, :on_call_node_leave)
|
21
14
|
end
|
22
15
|
|
23
|
-
|
16
|
+
#: (Prism::CallNode) -> void
|
24
17
|
def on_call_node_enter(node)
|
25
18
|
case node.message
|
26
19
|
when "example", "it", "specify"
|
@@ -30,7 +23,7 @@ module RubyLsp
|
|
30
23
|
|
31
24
|
@response_builder.last.children << RubyLsp::Interface::DocumentSymbol.new(
|
32
25
|
name: name,
|
33
|
-
kind:
|
26
|
+
kind: RubyLsp::Constant::SymbolKind::METHOD,
|
34
27
|
selection_range: range_from_node(node),
|
35
28
|
range: range_from_node(node),
|
36
29
|
)
|
@@ -43,7 +36,7 @@ module RubyLsp
|
|
43
36
|
|
44
37
|
symbol = RubyLsp::Interface::DocumentSymbol.new(
|
45
38
|
name: name,
|
46
|
-
kind:
|
39
|
+
kind: RubyLsp::Constant::SymbolKind::MODULE,
|
47
40
|
selection_range: range_from_node(node),
|
48
41
|
range: range_from_node(node),
|
49
42
|
children: [],
|
@@ -54,7 +47,7 @@ module RubyLsp
|
|
54
47
|
end
|
55
48
|
end
|
56
49
|
|
57
|
-
|
50
|
+
#: (Prism::CallNode) -> void
|
58
51
|
def on_call_node_leave(node)
|
59
52
|
case node.message
|
60
53
|
when "context", "describe", "shared_examples", "shared_context", "shared_examples_for"
|
@@ -64,7 +57,7 @@ module RubyLsp
|
|
64
57
|
end
|
65
58
|
end
|
66
59
|
|
67
|
-
|
60
|
+
#: (Prism::CallNode) -> String?
|
68
61
|
def generate_name(node)
|
69
62
|
arguments = node.arguments&.arguments
|
70
63
|
|
@@ -4,9 +4,8 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
module RSpec
|
6
6
|
class IndexingEnhancement < RubyIndexer::Enhancement
|
7
|
-
|
8
|
-
|
9
|
-
sig { override.params(node: Prism::CallNode).void }
|
7
|
+
# @override
|
8
|
+
#: (Prism::CallNode) -> void
|
10
9
|
def on_call_node_enter(node)
|
11
10
|
return if node.receiver
|
12
11
|
|
@@ -22,7 +21,7 @@ module RubyLsp
|
|
22
21
|
|
23
22
|
return if arguments.arguments.count != 1
|
24
23
|
|
25
|
-
method_name_node =
|
24
|
+
method_name_node = arguments.arguments.first #: as !nil
|
26
25
|
|
27
26
|
method_name = case method_name_node
|
28
27
|
when Prism::StringNode
|
@@ -41,7 +40,7 @@ module RubyLsp
|
|
41
40
|
arguments = node.arguments
|
42
41
|
|
43
42
|
if arguments && arguments.arguments.count == 1
|
44
|
-
method_name_node =
|
43
|
+
method_name_node = arguments.arguments.first #: as !nil
|
45
44
|
end
|
46
45
|
|
47
46
|
method_name = if method_name_node
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "rspec/core/formatters"
|
5
|
+
require "ruby_lsp/test_reporters/lsp_reporter"
|
6
|
+
|
7
|
+
module RubyLsp
|
8
|
+
module RSpec
|
9
|
+
class RSpecFormatter
|
10
|
+
::RSpec::Core::Formatters.register(
|
11
|
+
self,
|
12
|
+
:example_passed,
|
13
|
+
:example_pending,
|
14
|
+
:example_failed,
|
15
|
+
:example_started,
|
16
|
+
:stop,
|
17
|
+
)
|
18
|
+
|
19
|
+
def initialize(output)
|
20
|
+
@output = output
|
21
|
+
end
|
22
|
+
|
23
|
+
def example_started(notification)
|
24
|
+
example = notification.example
|
25
|
+
uri = uri_for(example)
|
26
|
+
id = generate_id(example)
|
27
|
+
line = example.location.split(":").last
|
28
|
+
RubyLsp::LspReporter.instance.start_test(id: id, uri: uri, line: line)
|
29
|
+
end
|
30
|
+
|
31
|
+
def example_passed(notification)
|
32
|
+
example = notification.example
|
33
|
+
uri = uri_for(example)
|
34
|
+
id = generate_id(example)
|
35
|
+
RubyLsp::LspReporter.instance.record_pass(id: id, uri: uri)
|
36
|
+
end
|
37
|
+
|
38
|
+
def example_failed(notification)
|
39
|
+
example = notification.example
|
40
|
+
uri = uri_for(example)
|
41
|
+
id = generate_id(example)
|
42
|
+
RubyLsp::LspReporter.instance.record_fail(id: id, message: notification.exception.message, uri: uri)
|
43
|
+
end
|
44
|
+
|
45
|
+
def example_pending(notification)
|
46
|
+
example = notification.example
|
47
|
+
uri = uri_for(example)
|
48
|
+
id = generate_id(example)
|
49
|
+
RubyLsp::LspReporter.instance.record_skip(id: id, uri: uri)
|
50
|
+
end
|
51
|
+
|
52
|
+
def stop(notification)
|
53
|
+
RubyLsp::LspReporter.instance.shutdown
|
54
|
+
end
|
55
|
+
|
56
|
+
def uri_for(example)
|
57
|
+
absolute_path = File.expand_path(example.file_path)
|
58
|
+
URI::Generic.from_path(path: absolute_path)
|
59
|
+
end
|
60
|
+
|
61
|
+
def generate_id(example)
|
62
|
+
[example, *example.example_group.parent_groups].reverse.map(&:location).join("::")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
# Patching this listener so it doesn't generate test items for RSpec tests
|
7
|
+
class SpecStyle
|
8
|
+
#: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void
|
9
|
+
def initialize(response_builder, global_state, dispatcher, uri)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module RSpec
|
6
|
+
class TestDiscovery
|
7
|
+
include ::RubyLsp::Requests::Support::Common
|
8
|
+
|
9
|
+
#: (ResponseBuilders::TestCollection, Prism::Dispatcher, URI::Generic, String) -> void
|
10
|
+
def initialize(response_builder, dispatcher, uri, workspace_path)
|
11
|
+
@response_builder = response_builder
|
12
|
+
@dispatcher = dispatcher
|
13
|
+
@uri = uri
|
14
|
+
|
15
|
+
path = uri.to_standardized_path #: as !nil
|
16
|
+
@path = path #: String
|
17
|
+
@workspace_path = workspace_path #: String
|
18
|
+
@group_stack = [] #: Array[::RubyLsp::Requests::Support::TestItem]
|
19
|
+
|
20
|
+
dispatcher.register(
|
21
|
+
self,
|
22
|
+
:on_call_node_enter,
|
23
|
+
:on_call_node_leave,
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
#: (Prism::CallNode) -> void
|
28
|
+
def on_call_node_enter(node)
|
29
|
+
return unless ["describe", "context", "it", "specify", "example"].include?(node.message)
|
30
|
+
|
31
|
+
case node.message
|
32
|
+
when "describe", "context"
|
33
|
+
handle_describe(node)
|
34
|
+
when "it", "specify", "example"
|
35
|
+
handle_example(node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#: (Prism::CallNode) -> void
|
40
|
+
def on_call_node_leave(node)
|
41
|
+
case node.message
|
42
|
+
when "context", "describe"
|
43
|
+
return unless valid_group?(node)
|
44
|
+
|
45
|
+
@group_stack.pop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
#: (Prism::CallNode) -> String?
|
52
|
+
def extract_description(node)
|
53
|
+
# Try to extract the description from a string literal argument
|
54
|
+
first_arg = node.arguments&.arguments&.first
|
55
|
+
return "example at #{relative_location(node)}" if first_arg.nil?
|
56
|
+
|
57
|
+
case first_arg
|
58
|
+
when Prism::StringNode
|
59
|
+
first_arg.content
|
60
|
+
when Prism::SymbolNode
|
61
|
+
first_arg.value
|
62
|
+
when Prism::ConstantReadNode
|
63
|
+
first_arg.name.to_s
|
64
|
+
when Prism::ConstantPathNode
|
65
|
+
first_arg.full_name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#: (Prism::CallNode) -> void
|
70
|
+
def handle_describe(node)
|
71
|
+
description = extract_description(node)
|
72
|
+
return if description.nil?
|
73
|
+
|
74
|
+
parent = find_parent_test_group
|
75
|
+
parent_id = parent ? "#{parent.id}::" : ""
|
76
|
+
|
77
|
+
test_item = ::RubyLsp::Requests::Support::TestItem.new(
|
78
|
+
"#{parent_id}#{relative_location(node)}",
|
79
|
+
description,
|
80
|
+
@uri,
|
81
|
+
range_from_node(node),
|
82
|
+
framework: :rspec,
|
83
|
+
)
|
84
|
+
|
85
|
+
if parent
|
86
|
+
parent.add(test_item)
|
87
|
+
else
|
88
|
+
@response_builder.add(test_item)
|
89
|
+
end
|
90
|
+
|
91
|
+
@response_builder.add_code_lens(test_item)
|
92
|
+
@group_stack.push(test_item)
|
93
|
+
end
|
94
|
+
|
95
|
+
#: (Prism::CallNode) -> void
|
96
|
+
def handle_example(node)
|
97
|
+
description = extract_description(node)
|
98
|
+
parent = find_parent_test_group
|
99
|
+
return unless parent
|
100
|
+
|
101
|
+
test_item = ::RubyLsp::Requests::Support::TestItem.new(
|
102
|
+
"#{parent.id}::#{relative_location(node)}",
|
103
|
+
description,
|
104
|
+
@uri,
|
105
|
+
range_from_node(node),
|
106
|
+
framework: :rspec,
|
107
|
+
)
|
108
|
+
|
109
|
+
parent.add(test_item)
|
110
|
+
@response_builder.add_code_lens(test_item)
|
111
|
+
end
|
112
|
+
|
113
|
+
#: -> ::RubyLsp::Requests::Support::TestItem??
|
114
|
+
def find_parent_test_group
|
115
|
+
@group_stack.last
|
116
|
+
end
|
117
|
+
|
118
|
+
#: (Prism::CallNode) -> bool
|
119
|
+
def valid_group?(node)
|
120
|
+
!(node.block.nil? || (node.receiver && node.receiver&.slice != "RSpec"))
|
121
|
+
end
|
122
|
+
|
123
|
+
#: (Prism::CallNode) -> String
|
124
|
+
def relative_location(node)
|
125
|
+
uri_path = @uri.to_standardized_path #: as !nil
|
126
|
+
relative_path = Pathname.new(uri_path).relative_path_from(Pathname.new(@workspace_path))
|
127
|
+
"./#{relative_path}:#{node.location.start_line}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stan Lo
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-
|
10
|
+
date: 2025-05-14 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: ruby-lsp
|
@@ -16,14 +15,14 @@ dependencies:
|
|
16
15
|
requirements:
|
17
16
|
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.23.
|
18
|
+
version: 0.23.19
|
20
19
|
type: :runtime
|
21
20
|
prerelease: false
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
23
22
|
requirements:
|
24
23
|
- - "~>"
|
25
24
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.23.
|
25
|
+
version: 0.23.19
|
27
26
|
description: RSpec addon for ruby-lsp
|
28
27
|
email:
|
29
28
|
- stan001212@gmail.com
|
@@ -44,6 +43,9 @@ files:
|
|
44
43
|
- lib/ruby_lsp/ruby_lsp_rspec/definition.rb
|
45
44
|
- lib/ruby_lsp/ruby_lsp_rspec/document_symbol.rb
|
46
45
|
- lib/ruby_lsp/ruby_lsp_rspec/indexing_enhancement.rb
|
46
|
+
- lib/ruby_lsp/ruby_lsp_rspec/rspec_formatter.rb
|
47
|
+
- lib/ruby_lsp/ruby_lsp_rspec/spec_style_patch.rb
|
48
|
+
- lib/ruby_lsp/ruby_lsp_rspec/test_discovery.rb
|
47
49
|
- lib/ruby_lsp_rspec/version.rb
|
48
50
|
homepage: https://github.com/st0012/ruby-lsp-rspec
|
49
51
|
licenses:
|
@@ -52,7 +54,6 @@ metadata:
|
|
52
54
|
homepage_uri: https://github.com/st0012/ruby-lsp-rspec
|
53
55
|
source_code_uri: https://github.com/st0012/ruby-lsp-rspec
|
54
56
|
changelog_uri: https://github.com/st0012/ruby-lsp-rspec/releases
|
55
|
-
post_install_message:
|
56
57
|
rdoc_options: []
|
57
58
|
require_paths:
|
58
59
|
- lib
|
@@ -67,8 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
68
|
- !ruby/object:Gem::Version
|
68
69
|
version: '0'
|
69
70
|
requirements: []
|
70
|
-
rubygems_version: 3.
|
71
|
-
signing_key:
|
71
|
+
rubygems_version: 3.6.3
|
72
72
|
specification_version: 4
|
73
73
|
summary: RSpec addon for ruby-lsp
|
74
74
|
test_files: []
|