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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f59143ad42455a57861a393b556660d68a1efddab1a34490094f3cf1d59b65d
4
- data.tar.gz: 6239e3ccc46fe341f501fea47c6f8cce115df176e359e3f962e9e3c7068f0083
3
+ metadata.gz: 6204c70ec6917f93238449949f16c24892d0145cb10dd32c42819021ef617c83
4
+ data.tar.gz: 510b118788f0469ef5783453042fb5c4f56aef9da2f77f0b1567b77e73d0b959
5
5
  SHA512:
6
- metadata.gz: 261b3ac804b0d81bcd6e615349fcf27ab70b4e6b4c5a013c9c6f510bdd153d9203457f613405cd06ea43dedaec2db02d6563e36a1a6cb635451734f709514368
7
- data.tar.gz: 692ca7238e48d6f370d208cbcfa4264d9e7ac540f6d5a45d16c4e9da6c467230b3e5d217d93c84f42d5cf70d6b800234ed0acdcf369112a523f5bf2cbef554f7
6
+ metadata.gz: '04947d8bf9659b8d8aa213ea2d305f7d2049719a30aa753e30f289da98b6e41991118681405a3166aac93f5c8d23d4f2ce9f808dcf99d6a1f43aabb85c564980'
7
+ data.tar.gz: 2aab5409230f0783b598fe53511d712e4643f84ec506b63278803ccc93002ca37e981c219041f4fa8b33f768432fa5c2eab13e6941bb76261bddfe9a31b70cb1
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
3
  --require spec_helper
4
+ --exclude-pattern **/fixtures/*
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:
@@ -1,7 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "sorbet-runtime"
5
4
  require "ruby_lsp_rspec/version"
6
5
 
7
6
  module RubyLsp
@@ -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
- extend T::Sig
17
+ FORMATTER_PATH = File.expand_path("rspec_formatter.rb", __dir__) #: String
18
+ FORMATTER_NAME = "RubyLsp::RSpec::RSpecFormatter" #: String
16
19
 
17
- sig { returns(T.nilable(String)) }
18
- attr_reader :rspec_command
19
-
20
- sig { returns(T::Boolean) }
20
+ #: bool
21
21
  attr_reader :debug
22
22
 
23
- sig { void }
23
+ #: -> void
24
24
  def initialize
25
25
  super
26
- @debug = T.let(false, T::Boolean)
27
- @rspec_command = T.let(nil, T.nilable(String))
26
+ @debug = false #: bool
27
+ @rspec_command = nil #: String?
28
28
  end
29
29
 
30
- sig { override.params(global_state: GlobalState, message_queue: Thread::Queue).void }
30
+ # @override
31
+ #: (GlobalState, Thread::Queue) -> void
31
32
  def activate(global_state, message_queue)
32
- @index = T.let(global_state.index, T.nilable(RubyIndexer::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 = T.let(settings&.dig(:rspecCommand), T.nilable(String))
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
- sig { override.void }
42
+ # @override
43
+ #: -> void
40
44
  def deactivate; end
41
45
 
42
- sig { override.returns(String) }
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
- sig do
49
- override.params(
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
- CodeLens.new(response_builder, uri, dispatcher, rspec_command: rspec_command, debug: debug)
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
- sig do
62
- override.params(
63
- response_builder: ResponseBuilders::DocumentSymbol,
64
- dispatcher: Prism::Dispatcher,
65
- ).void
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
- sig do
72
- override.params(
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(response_builder, uri, node_context, T.must(@index), dispatcher)
144
+ Definition.new(
145
+ response_builder,
146
+ uri,
147
+ node_context,
148
+ @index, #: as !nil
149
+ dispatcher,
150
+ )
86
151
  end
87
152
 
88
- sig { override.returns(String) }
89
- def name
90
- "Ruby LSP RSpec"
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
- sig do
12
- params(
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
- @path = T.let(T.must(uri.to_standardized_path), String)
24
- @group_id = T.let(1, Integer)
25
- @group_id_stack = T.let([], T::Array[Integer])
26
- @anonymous_example_count = T.let(0, Integer)
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
- sig { params(node: Prism::CallNode).void }
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
- sig { params(node: Prism::CallNode).void }
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
- sig { params(message: String).void }
53
+ #: (String) -> void
79
54
  def log_message(message)
80
55
  puts "[#{self.class}]: #{message}"
81
56
  end
82
57
 
83
- sig { params(node: Prism::CallNode).returns(T::Boolean) }
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
- sig { params(node: Prism::CallNode).returns(String) }
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
- sig { params(node: Prism::Node, name: String, kind: Symbol).void }
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 = "#{@base_command} #{@path}:#{line_number}"
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
- sig do
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
- sig { params(node: Prism::CallNode).void }
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
- sig do
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
- sig { params(node: Prism::CallNode).void }
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: LanguageServer::Protocol::Constant::SymbolKind::METHOD,
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: LanguageServer::Protocol::Constant::SymbolKind::MODULE,
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
- sig { params(node: Prism::CallNode).void }
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
- sig { params(node: Prism::CallNode).returns(T.nilable(String)) }
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
- extend T::Sig
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 = T.must(arguments.arguments.first)
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 = T.must(arguments.arguments.first)
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
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module RSpec
6
- VERSION = "0.1.22"
6
+ VERSION = "0.1.23"
7
7
  end
8
8
  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.22
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-02-03 00:00:00.000000000 Z
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.0
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.0
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.5.11
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: []