ruby-lsp 0.4.5 → 0.5.1

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -86
  3. data/VERSION +1 -1
  4. data/lib/ruby_lsp/check_docs.rb +112 -0
  5. data/lib/ruby_lsp/document.rb +13 -2
  6. data/lib/ruby_lsp/event_emitter.rb +84 -18
  7. data/lib/ruby_lsp/executor.rb +126 -48
  8. data/lib/ruby_lsp/internal.rb +2 -0
  9. data/lib/ruby_lsp/listener.rb +6 -13
  10. data/lib/ruby_lsp/requests/base_request.rb +0 -5
  11. data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
  12. data/lib/ruby_lsp/requests/code_actions.rb +1 -1
  13. data/lib/ruby_lsp/requests/code_lens.rb +82 -66
  14. data/lib/ruby_lsp/requests/diagnostics.rb +2 -2
  15. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  16. data/lib/ruby_lsp/requests/document_link.rb +17 -15
  17. data/lib/ruby_lsp/requests/document_symbol.rb +51 -31
  18. data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
  19. data/lib/ruby_lsp/requests/formatting.rb +10 -11
  20. data/lib/ruby_lsp/requests/hover.rb +20 -20
  21. data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
  22. data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
  23. data/lib/ruby_lsp/requests/path_completion.rb +21 -57
  24. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  25. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  26. data/lib/ruby_lsp/requests/support/common.rb +36 -0
  27. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -1
  28. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -1
  29. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +5 -2
  30. data/lib/ruby_lsp/requests.rb +15 -15
  31. data/lib/ruby_lsp/server.rb +44 -20
  32. data/lib/ruby_lsp/store.rb +1 -1
  33. data/lib/ruby_lsp/utils.rb +2 -7
  34. metadata +3 -2
@@ -6,12 +6,12 @@ module RubyLsp
6
6
  class Executor
7
7
  extend T::Sig
8
8
 
9
- sig { params(store: Store).void }
10
- def initialize(store)
9
+ sig { params(store: Store, message_queue: Thread::Queue).void }
10
+ def initialize(store, message_queue)
11
11
  # Requests that mutate the store must be run sequentially! Parallel requests only receive a temporary copy of the
12
12
  # store
13
13
  @store = store
14
- @messages = T.let([], T::Array[Message])
14
+ @message_queue = message_queue
15
15
  end
16
16
 
17
17
  sig { params(request: T::Hash[Symbol, T.untyped]).returns(Result) }
@@ -25,7 +25,7 @@ module RubyLsp
25
25
  error = e
26
26
  end
27
27
 
28
- Result.new(response: response, error: error, request_time: request_time, messages: @messages)
28
+ Result.new(response: response, error: error, request_time: request_time)
29
29
  end
30
30
 
31
31
  private
@@ -43,7 +43,7 @@ module RubyLsp
43
43
  errored_extensions = Extension.extensions.select(&:error?)
44
44
 
45
45
  if errored_extensions.any?
46
- @messages << Notification.new(
46
+ @message_queue << Notification.new(
47
47
  message: "window/showMessage",
48
48
  params: Interface::ShowMessageParams.new(
49
49
  type: Constant::MessageType::WARNING,
@@ -54,6 +54,8 @@ module RubyLsp
54
54
  warn(errored_extensions.map(&:backtraces).join("\n\n"))
55
55
  end
56
56
 
57
+ check_formatter_is_available
58
+
57
59
  warn("Ruby LSP is ready")
58
60
  VOID
59
61
  when "textDocument/didOpen"
@@ -63,7 +65,7 @@ module RubyLsp
63
65
  request.dig(:params, :textDocument, :version),
64
66
  )
65
67
  when "textDocument/didClose"
66
- @messages << Notification.new(
68
+ @message_queue << Notification.new(
67
69
  message: "textDocument/publishDiagnostics",
68
70
  params: Interface::PublishDiagnosticsParams.new(uri: uri, diagnostics: []),
69
71
  )
@@ -77,12 +79,33 @@ module RubyLsp
77
79
  )
78
80
  when "textDocument/foldingRange"
79
81
  folding_range(uri)
80
- when "textDocument/documentLink"
81
- document_link(uri)
82
82
  when "textDocument/selectionRange"
83
83
  selection_range(uri, request.dig(:params, :positions))
84
- when "textDocument/documentSymbol"
85
- document_symbol(uri)
84
+ when "textDocument/documentSymbol", "textDocument/documentLink", "textDocument/codeLens"
85
+ document = @store.get(uri)
86
+
87
+ # If the response has already been cached by another request, return it
88
+ cached_response = document.cache_get(request[:method])
89
+ return cached_response if cached_response
90
+
91
+ # Run listeners for the document
92
+ emitter = EventEmitter.new
93
+ document_symbol = Requests::DocumentSymbol.new(emitter, @message_queue)
94
+ document_link = Requests::DocumentLink.new(uri, emitter, @message_queue)
95
+ code_lens = Requests::CodeLens.new(uri, emitter, @message_queue)
96
+ code_lens_extensions_listeners = Requests::CodeLens.listeners.map do |l|
97
+ T.unsafe(l).new(document.uri, emitter, @message_queue)
98
+ end
99
+ emitter.visit(document.tree) if document.parsed?
100
+
101
+ code_lens_extensions_listeners.each { |ext| code_lens.merge_response!(ext) }
102
+
103
+ # Store all responses retrieve in this round of visits in the cache and then return the response for the request
104
+ # we actually received
105
+ document.cache_set("textDocument/documentSymbol", document_symbol.response)
106
+ document.cache_set("textDocument/documentLink", document_link.response)
107
+ document.cache_set("textDocument/codeLens", code_lens.response)
108
+ document.cache_get(request[:method])
86
109
  when "textDocument/semanticTokens/full"
87
110
  semantic_tokens_full(uri)
88
111
  when "textDocument/semanticTokens/range"
@@ -91,7 +114,7 @@ module RubyLsp
91
114
  begin
92
115
  formatting(uri)
93
116
  rescue Requests::Formatting::InvalidFormatter => error
94
- @messages << Notification.new(
117
+ @message_queue << Notification.new(
95
118
  message: "window/showMessage",
96
119
  params: Interface::ShowMessageParams.new(
97
120
  type: Constant::MessageType::ERROR,
@@ -101,7 +124,7 @@ module RubyLsp
101
124
 
102
125
  nil
103
126
  rescue StandardError => error
104
- @messages << Notification.new(
127
+ @message_queue << Notification.new(
105
128
  message: "window/showMessage",
106
129
  params: Interface::ShowMessageParams.new(
107
130
  type: Constant::MessageType::ERROR,
@@ -127,7 +150,7 @@ module RubyLsp
127
150
  begin
128
151
  diagnostic(uri)
129
152
  rescue StandardError => error
130
- @messages << Notification.new(
153
+ @message_queue << Notification.new(
131
154
  message: "window/showMessage",
132
155
  params: Interface::ShowMessageParams.new(
133
156
  type: Constant::MessageType::ERROR,
@@ -139,25 +162,16 @@ module RubyLsp
139
162
  end
140
163
  when "textDocument/completion"
141
164
  completion(uri, request.dig(:params, :position))
142
- when "textDocument/codeLens"
143
- code_lens(uri)
144
165
  end
145
166
  end
146
167
 
147
168
  sig { params(uri: String).returns(T::Array[Interface::FoldingRange]) }
148
169
  def folding_range(uri)
149
- @store.cache_fetch(uri, :folding_ranges) do |document|
170
+ @store.cache_fetch(uri, "textDocument/foldingRange") do |document|
150
171
  Requests::FoldingRanges.new(document).run
151
172
  end
152
173
  end
153
174
 
154
- sig { params(uri: String).returns(T::Array[Interface::CodeLens]) }
155
- def code_lens(uri)
156
- @store.cache_fetch(uri, :code_lens) do |document|
157
- Requests::CodeLens.new(document).run
158
- end
159
- end
160
-
161
175
  sig do
162
176
  params(
163
177
  uri: String,
@@ -166,7 +180,6 @@ module RubyLsp
166
180
  end
167
181
  def hover(uri, position)
168
182
  document = @store.get(uri)
169
- document.parse
170
183
  return if document.syntax_error?
171
184
 
172
185
  target, parent = document.locate_node(position)
@@ -177,31 +190,18 @@ module RubyLsp
177
190
  end
178
191
 
179
192
  # Instantiate all listeners
180
- base_listener = Requests::Hover.new
181
- listeners = Requests::Hover.listeners.map(&:new)
193
+ emitter = EventEmitter.new
194
+ base_listener = Requests::Hover.new(emitter, @message_queue)
195
+ listeners = Requests::Hover.listeners.map { |l| l.new(emitter, @message_queue) }
182
196
 
183
197
  # Emit events for all listeners
184
- T.unsafe(EventEmitter).new(base_listener, *listeners).emit_for_target(target)
198
+ emitter.emit_for_target(target)
185
199
 
186
200
  # Merge all responses into a single hover
187
201
  listeners.each { |ext| base_listener.merge_response!(ext) }
188
202
  base_listener.response
189
203
  end
190
204
 
191
- sig { params(uri: String).returns(T::Array[Interface::DocumentLink]) }
192
- def document_link(uri)
193
- @store.cache_fetch(uri, :document_link) do |document|
194
- RubyLsp::Requests::DocumentLink.new(document).run
195
- end
196
- end
197
-
198
- sig { params(uri: String).returns(T::Array[Interface::DocumentSymbol]) }
199
- def document_symbol(uri)
200
- @store.cache_fetch(uri, :document_symbol) do |document|
201
- Requests::DocumentSymbol.new(document).run
202
- end
203
- end
204
-
205
205
  sig { params(uri: String, content_changes: T::Array[Document::EditShape], version: Integer).returns(Object) }
206
206
  def text_document_did_change(uri, content_changes, version)
207
207
  @store.push_edits(uri: uri, edits: content_changes, version: version)
@@ -227,7 +227,7 @@ module RubyLsp
227
227
  ).returns(T.nilable(T::Array[T.nilable(Requests::Support::SelectionRange)]))
228
228
  end
229
229
  def selection_range(uri, positions)
230
- ranges = @store.cache_fetch(uri, :selection_ranges) do |document|
230
+ ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
231
231
  Requests::SelectionRanges.new(document).run
232
232
  end
233
233
 
@@ -247,7 +247,7 @@ module RubyLsp
247
247
 
248
248
  sig { params(uri: String).returns(Interface::SemanticTokens) }
249
249
  def semantic_tokens_full(uri)
250
- @store.cache_fetch(uri, :semantic_highlighting) do |document|
250
+ @store.cache_fetch(uri, "textDocument/semanticTokens/full") do |document|
251
251
  T.cast(
252
252
  Requests::SemanticHighlighting.new(
253
253
  document,
@@ -260,6 +260,9 @@ module RubyLsp
260
260
 
261
261
  sig { params(uri: String).returns(T.nilable(T::Array[Interface::TextEdit])) }
262
262
  def formatting(uri)
263
+ # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
264
+ return if @store.formatter == "none"
265
+
263
266
  Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
264
267
  end
265
268
 
@@ -314,7 +317,7 @@ module RubyLsp
314
317
 
315
318
  case result
316
319
  when Requests::CodeActionResolve::Error::EmptySelection
317
- @messages << Notification.new(
320
+ @message_queue << Notification.new(
318
321
  message: "window/showMessage",
319
322
  params: Interface::ShowMessageParams.new(
320
323
  type: Constant::MessageType::ERROR,
@@ -323,7 +326,7 @@ module RubyLsp
323
326
  )
324
327
  raise Requests::CodeActionResolve::CodeActionError
325
328
  when Requests::CodeActionResolve::Error::InvalidTargetRange
326
- @messages << Notification.new(
329
+ @message_queue << Notification.new(
327
330
  message: "window/showMessage",
328
331
  params: Interface::ShowMessageParams.new(
329
332
  type: Constant::MessageType::ERROR,
@@ -338,7 +341,7 @@ module RubyLsp
338
341
 
339
342
  sig { params(uri: String).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
340
343
  def diagnostic(uri)
341
- response = @store.cache_fetch(uri, :diagnostics) do |document|
344
+ response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
342
345
  Requests::Diagnostics.new(document).run
343
346
  end
344
347
 
@@ -365,7 +368,44 @@ module RubyLsp
365
368
  params(uri: String, position: Document::PositionShape).returns(T.nilable(T::Array[Interface::CompletionItem]))
366
369
  end
367
370
  def completion(uri, position)
368
- Requests::PathCompletion.new(@store.get(uri), position).run
371
+ document = @store.get(uri)
372
+ return unless document.parsed?
373
+
374
+ char_position = document.create_scanner.find_char_position(position)
375
+ matched, parent = document.locate(
376
+ T.must(document.tree),
377
+ char_position,
378
+ node_types: [SyntaxTree::Command, SyntaxTree::CommandCall, SyntaxTree::CallNode],
379
+ )
380
+
381
+ return unless matched && parent
382
+
383
+ target = case matched
384
+ when SyntaxTree::Command, SyntaxTree::CallNode, SyntaxTree::CommandCall
385
+ message = matched.message
386
+ return if message.is_a?(Symbol)
387
+ return unless message.value == "require"
388
+
389
+ args = matched.arguments
390
+ args = args.arguments if args.is_a?(SyntaxTree::ArgParen)
391
+ return if args.nil? || args.is_a?(SyntaxTree::ArgsForward)
392
+
393
+ argument = args.parts.first
394
+ return unless argument.is_a?(SyntaxTree::StringLiteral)
395
+
396
+ path_node = argument.parts.first
397
+ return unless path_node.is_a?(SyntaxTree::TStringContent)
398
+ return unless (path_node.location.start_char..path_node.location.end_char).cover?(char_position)
399
+
400
+ path_node
401
+ end
402
+
403
+ return unless target
404
+
405
+ emitter = EventEmitter.new
406
+ listener = Requests::PathCompletion.new(emitter, @message_queue)
407
+ emitter.emit_for_target(target)
408
+ listener.response
369
409
  end
370
410
 
371
411
  sig { params(options: T::Hash[Symbol, T.untyped]).returns(Interface::InitializeResult) }
@@ -382,7 +422,11 @@ module RubyLsp
382
422
  end
383
423
 
384
424
  formatter = options.dig(:initializationOptions, :formatter)
385
- @store.formatter = formatter unless formatter.nil?
425
+ @store.formatter = if formatter == "auto"
426
+ detected_formatter
427
+ else
428
+ formatter
429
+ end
386
430
 
387
431
  configured_features = options.dig(:initializationOptions, :enabledFeatures)
388
432
  experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled)
@@ -491,5 +535,39 @@ module RubyLsp
491
535
  ),
492
536
  )
493
537
  end
538
+
539
+ sig { returns(String) }
540
+ def detected_formatter
541
+ # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
542
+ if direct_dependency?(/^rubocop/)
543
+ "rubocop"
544
+ elsif direct_dependency?(/^syntax_tree$/)
545
+ "syntax_tree"
546
+ else
547
+ "none"
548
+ end
549
+ end
550
+
551
+ sig { params(gem_pattern: Regexp).returns(T::Boolean) }
552
+ def direct_dependency?(gem_pattern)
553
+ Bundler.locked_gems.dependencies.keys.grep(gem_pattern).any?
554
+ end
555
+
556
+ sig { void }
557
+ def check_formatter_is_available
558
+ # Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
559
+ # Syntax Tree will always be available via Ruby LSP so we don't need to check for it.
560
+ return unless @store.formatter == "rubocop"
561
+
562
+ unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
563
+ @message_queue << Notification.new(
564
+ message: "window/showMessage",
565
+ params: Interface::ShowMessageParams.new(
566
+ type: Constant::MessageType::ERROR,
567
+ message: "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the bundle.",
568
+ ),
569
+ )
570
+ end
571
+ end
494
572
  end
495
573
  end
@@ -6,6 +6,7 @@ require "syntax_tree"
6
6
  require "language_server-protocol"
7
7
  require "benchmark"
8
8
  require "bundler"
9
+ require "uri"
9
10
 
10
11
  require "ruby-lsp"
11
12
  require "ruby_lsp/utils"
@@ -16,3 +17,4 @@ require "ruby_lsp/requests"
16
17
  require "ruby_lsp/listener"
17
18
  require "ruby_lsp/store"
18
19
  require "ruby_lsp/extension"
20
+ require "ruby_lsp/requests/support/rubocop_runner"
@@ -14,12 +14,15 @@ module RubyLsp
14
14
 
15
15
  abstract!
16
16
 
17
+ sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
18
+ def initialize(emitter, message_queue)
19
+ @emitter = emitter
20
+ @message_queue = message_queue
21
+ end
22
+
17
23
  class << self
18
24
  extend T::Sig
19
25
 
20
- sig { returns(T.nilable(T::Array[Symbol])) }
21
- attr_reader :events
22
-
23
26
  sig { returns(T::Array[T.class_of(Listener)]) }
24
27
  def listeners
25
28
  @listeners ||= T.let([], T.nilable(T::Array[T.class_of(Listener)]))
@@ -29,16 +32,6 @@ module RubyLsp
29
32
  def add_listener(listener)
30
33
  listeners << listener
31
34
  end
32
-
33
- # All listener events must be defined inside of a `listener_events` block. This is to ensure we know which events
34
- # have been registered. Defining an event outside of this block will simply not register it and it'll never be
35
- # invoked
36
- sig { params(block: T.proc.void).void }
37
- def listener_events(&block)
38
- current_methods = instance_methods
39
- block.call
40
- @events = T.let(instance_methods - current_methods, T.nilable(T::Array[Symbol]))
41
- end
42
35
  end
43
36
 
44
37
  # Override this method with an attr_reader that returns the response of your listener. The listener should
@@ -18,11 +18,6 @@ module RubyLsp
18
18
  sig { params(document: Document, _kwargs: T.untyped).void }
19
19
  def initialize(document, **_kwargs)
20
20
  @document = document
21
-
22
- # Parsing the document here means we're taking a lazy approach by only doing it when the first feature request
23
- # is received by the server. This happens because {Document#parse} remembers if there are new edits to be parsed
24
- @document.parse
25
-
26
21
  super()
27
22
  end
28
23
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Code action resolve demo](../../misc/code_action_resolve.gif)
6
+ # ![Code action resolve demo](../../code_action_resolve.gif)
7
7
  #
8
8
  # The [code action resolve](https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve)
9
9
  # request is used to to resolve the edit field for a given code action, if it is not already provided in the
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Code actions demo](../../misc/code_actions.gif)
6
+ # ![Code actions demo](../../code_actions.gif)
7
7
  #
8
8
  # The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
9
9
  # request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Code lens demo](../../misc/code_lens.gif)
6
+ # ![Code lens demo](../../code_lens.gif)
7
7
  #
8
8
  # This feature is currently experimental. Clients will need to pass `experimentalFeaturesEnabled`
9
9
  # in the initialization options to enable it.
@@ -19,41 +19,41 @@ module RubyLsp
19
19
  # class Test < Minitest::Test
20
20
  # end
21
21
  # ```
22
+ class CodeLens < Listener
23
+ extend T::Sig
24
+ extend T::Generic
25
+
26
+ ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } }
22
27
 
23
- class CodeLens < BaseRequest
24
28
  BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
25
29
  ACCESS_MODIFIERS = T.let(["public", "private", "protected"], T::Array[String])
26
30
 
27
- sig do
28
- params(
29
- document: Document,
30
- ).void
31
- end
32
- def initialize(document)
33
- super(document)
34
- @results = T.let([], T::Array[Interface::CodeLens])
35
- @path = T.let(document.uri.delete_prefix("file://"), String)
36
- @modifier = T.let("public", String)
37
- end
31
+ sig { override.returns(ResponseType) }
32
+ attr_reader :response
33
+
34
+ sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
35
+ def initialize(uri, emitter, message_queue)
36
+ super(emitter, message_queue)
37
+
38
+ @response = T.let([], ResponseType)
39
+ @path = T.let(T.must(URI(uri).path), String)
40
+ @visibility = T.let("public", String)
41
+ @prev_visibility = T.let("public", String)
38
42
 
39
- sig { override.returns(T.all(T::Array[Interface::CodeLens], Object)) }
40
- def run
41
- visit(@document.tree) if @document.parsed?
42
- @results
43
+ emitter.register(self, :on_class, :on_def, :on_command, :after_command, :on_call, :after_call, :on_vcall)
43
44
  end
44
45
 
45
- sig { override.params(node: SyntaxTree::ClassDeclaration).void }
46
- def visit_class(node)
46
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
47
+ def on_class(node)
47
48
  class_name = node.constant.constant.value
48
49
  if class_name.end_with?("Test")
49
50
  add_code_lens(node, name: class_name, command: BASE_COMMAND + @path)
50
51
  end
51
- visit(node.bodystmt)
52
52
  end
53
53
 
54
- sig { override.params(node: SyntaxTree::DefNode).void }
55
- def visit_def(node)
56
- if @modifier == "public"
54
+ sig { params(node: SyntaxTree::DefNode).void }
55
+ def on_def(node)
56
+ if @visibility == "public"
57
57
  method_name = node.name.value
58
58
  if method_name.start_with?("test_")
59
59
  add_code_lens(
@@ -65,69 +65,85 @@ module RubyLsp
65
65
  end
66
66
  end
67
67
 
68
- sig { override.params(node: SyntaxTree::Command).void }
69
- def visit_command(node)
70
- if node.message.value == "public"
71
- with_visiblity("public", node)
68
+ sig { params(node: SyntaxTree::Command).void }
69
+ def on_command(node)
70
+ if ACCESS_MODIFIERS.include?(node.message.value) && node.arguments.parts.any?
71
+ @prev_visibility = @visibility
72
+ @visibility = node.message.value
72
73
  end
73
74
  end
74
75
 
75
- sig { override.params(node: SyntaxTree::CallNode).void }
76
- def visit_call(node)
76
+ sig { params(node: SyntaxTree::Command).void }
77
+ def after_command(node)
78
+ @visibility = @prev_visibility
79
+ end
80
+
81
+ sig { params(node: SyntaxTree::CallNode).void }
82
+ def on_call(node)
77
83
  ident = node.message if node.message.is_a?(SyntaxTree::Ident)
78
84
 
79
85
  if ident
80
- if T.cast(ident, SyntaxTree::Ident).value == "public"
81
- with_visiblity("public", node)
86
+ ident_value = T.cast(ident, SyntaxTree::Ident).value
87
+ if ACCESS_MODIFIERS.include?(ident_value)
88
+ @prev_visibility = @visibility
89
+ @visibility = ident_value
82
90
  end
83
91
  end
84
92
  end
85
93
 
86
- sig { override.params(node: SyntaxTree::VCall).void }
87
- def visit_vcall(node)
94
+ sig { params(node: SyntaxTree::CallNode).void }
95
+ def after_call(node)
96
+ @visibility = @prev_visibility
97
+ end
98
+
99
+ sig { params(node: SyntaxTree::VCall).void }
100
+ def on_vcall(node)
88
101
  vcall_value = node.value.value
89
102
 
90
103
  if ACCESS_MODIFIERS.include?(vcall_value)
91
- @modifier = vcall_value
104
+ @prev_visibility = vcall_value
105
+ @visibility = vcall_value
92
106
  end
93
107
  end
94
108
 
95
- private
96
-
97
- sig do
98
- params(
99
- visibility: String,
100
- node: T.any(SyntaxTree::CallNode, SyntaxTree::Command),
101
- ).void
102
- end
103
- def with_visiblity(visibility, node)
104
- current_visibility = @modifier
105
- @modifier = visibility
106
- visit(node.arguments)
107
- ensure
108
- @modifier = T.must(current_visibility)
109
+ sig { params(other: Listener[ResponseType]).returns(T.self_type) }
110
+ def merge_response!(other)
111
+ @response.concat(other.response)
112
+ self
109
113
  end
110
114
 
115
+ private
116
+
111
117
  sig { params(node: SyntaxTree::Node, name: String, command: String).void }
112
118
  def add_code_lens(node, name:, command:)
113
- @results << Interface::CodeLens.new(
114
- range: range_from_syntax_tree_node(node),
115
- command: Interface::Command.new(
116
- title: "Run",
117
- command: "rubyLsp.runTest",
118
- arguments: [
119
- @path,
120
- name,
121
- command,
122
- {
123
- start_line: node.location.start_line - 1,
124
- start_column: node.location.start_column,
125
- end_line: node.location.end_line - 1,
126
- end_column: node.location.end_column,
127
- },
128
- ],
129
- ),
130
- data: { type: "test" },
119
+ @response << create_code_lens(
120
+ node,
121
+ title: "Run",
122
+ command_name: "rubyLsp.runTest",
123
+ path: @path,
124
+ name: name,
125
+ test_command: command,
126
+ type: "test",
127
+ )
128
+
129
+ @response << create_code_lens(
130
+ node,
131
+ title: "Run In Terminal",
132
+ command_name: "rubyLsp.runTestInTerminal",
133
+ path: @path,
134
+ name: name,
135
+ test_command: command,
136
+ type: "test_in_terminal",
137
+ )
138
+
139
+ @response << create_code_lens(
140
+ node,
141
+ title: "Debug",
142
+ command_name: "rubyLsp.debugTest",
143
+ path: @path,
144
+ name: name,
145
+ test_command: command,
146
+ type: "debug",
131
147
  )
132
148
  end
133
149
  end
@@ -5,7 +5,7 @@ require "ruby_lsp/requests/support/rubocop_diagnostics_runner"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Diagnostics demo](../../misc/diagnostics.gif)
8
+ # ![Diagnostics demo](../../diagnostics.gif)
9
9
  #
10
10
  # The
11
11
  # [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
@@ -34,7 +34,7 @@ module RubyLsp
34
34
  return unless defined?(Support::RuboCopDiagnosticsRunner)
35
35
 
36
36
  # Don't try to run RuboCop diagnostics for files outside the current working directory
37
- return unless @uri.start_with?(WORKSPACE_URI)
37
+ return unless URI(@uri).path&.start_with?(T.must(WORKSPACE_URI.path))
38
38
 
39
39
  Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
40
40
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Document highlight demo](../../misc/document_highlight.gif)
6
+ # ![Document highlight demo](../../document_highlight.gif)
7
7
  #
8
8
  # The [document highlight](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight)
9
9
  # informs the editor all relevant elements of the currently pointed item for highlighting. For example, when