ruby-lsp 0.4.5 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
# ![Code action resolve demo](../../
|
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](../../
|
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](../../
|
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
|
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
|
-
# ![Diagnostics demo](../../
|
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](../../
|
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
|