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.
- checksums.yaml +4 -4
- data/README.md +5 -86
- data/VERSION +1 -1
- data/lib/ruby_lsp/check_docs.rb +112 -0
- data/lib/ruby_lsp/document.rb +13 -2
- data/lib/ruby_lsp/event_emitter.rb +84 -18
- data/lib/ruby_lsp/executor.rb +126 -48
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listener.rb +6 -13
- data/lib/ruby_lsp/requests/base_request.rb +0 -5
- data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/code_actions.rb +1 -1
- data/lib/ruby_lsp/requests/code_lens.rb +82 -66
- data/lib/ruby_lsp/requests/diagnostics.rb +2 -2
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/document_link.rb +17 -15
- data/lib/ruby_lsp/requests/document_symbol.rb +51 -31
- data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/formatting.rb +10 -11
- data/lib/ruby_lsp/requests/hover.rb +20 -20
- data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
- data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
- data/lib/ruby_lsp/requests/path_completion.rb +21 -57
- data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/requests/support/common.rb +36 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -1
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +5 -2
- data/lib/ruby_lsp/requests.rb +15 -15
- data/lib/ruby_lsp/server.rb +44 -20
- data/lib/ruby_lsp/store.rb +1 -1
- data/lib/ruby_lsp/utils.rb +2 -7
- metadata +3 -2
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -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
|
-
@
|
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
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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,
|
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
|
-
|
181
|
-
|
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
|
-
|
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,
|
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,
|
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
|
-
@
|
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
|
-
@
|
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,
|
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
|
-
|
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
|
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
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -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"
|
data/lib/ruby_lsp/listener.rb
CHANGED
@@ -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
|
-
# 
|
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
|
-
# 
|
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
|
-
# 
|
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
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@
|
35
|
-
@path = T.let(
|
36
|
-
@
|
37
|
-
|
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
|
-
|
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 {
|
46
|
-
def
|
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 {
|
55
|
-
def
|
56
|
-
if @
|
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 {
|
69
|
-
def
|
70
|
-
if node.message.value
|
71
|
-
|
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 {
|
76
|
-
def
|
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
|
-
|
81
|
-
|
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 {
|
87
|
-
def
|
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
|
-
@
|
104
|
+
@prev_visibility = vcall_value
|
105
|
+
@visibility = vcall_value
|
92
106
|
end
|
93
107
|
end
|
94
108
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
@
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
-
# 
|
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
|
-
# 
|
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
|