ruby-lsp 0.5.0 → 0.6.0
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 +4 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp +33 -1
- data/lib/ruby_lsp/check_docs.rb +1 -1
- data/lib/ruby_lsp/event_emitter.rb +61 -0
- data/lib/ruby_lsp/executor.rb +31 -46
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/requests/base_request.rb +2 -6
- data/lib/ruby_lsp/requests/code_lens.rb +121 -33
- data/lib/ruby_lsp/requests/diagnostics.rb +2 -0
- data/lib/ruby_lsp/requests/folding_ranges.rb +2 -1
- data/lib/ruby_lsp/requests/formatting.rb +30 -11
- data/lib/ruby_lsp/requests/inlay_hints.rb +17 -15
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +85 -113
- data/lib/ruby_lsp/requests/support/common.rb +6 -17
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +41 -0
- data/lib/ruby_lsp/requests/support/formatter_runner.rb +18 -0
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +2 -1
- data/lib/ruby_lsp/requests/support/source_uri.rb +1 -1
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +2 -1
- data/lib/ruby_lsp/requests.rb +1 -0
- data/lib/ruby_lsp/server.rb +5 -2
- data/lib/ruby_lsp/utils.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d7205054d59bb5a292bc8e2d87a984663ce1f0dac527d6d69d3940bc6e86735
|
4
|
+
data.tar.gz: 9b760d79ca5406fe3e78e4bd1276cb507464d72d2fc2e68cf5abf1f9920df1a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf465a2d9af7e109b21ecd861cb88b556ee872e4d46e292e791f8f85ce131af7791254187bcc68216b9df0f1276296b5cf80e78be7193c11fdf16831b8da4f0f
|
7
|
+
data.tar.gz: 57fa298a96b7cd3eeb2be68d21261ae634f8e70d818a5c68b2e7cdcfa464d7d27b16e90aecbfb8192a4a3ff9fecc4fa8b94baa3b3b51d9c7c887f4fba2b07bd5
|
data/README.md
CHANGED
@@ -39,10 +39,14 @@ end
|
|
39
39
|
See the [documentation](https://shopify.github.io/ruby-lsp) for more in-depth details about the
|
40
40
|
[supported features](https://shopify.github.io/ruby-lsp/RubyLsp/Requests.html).
|
41
41
|
|
42
|
+
For creating rich themes for Ruby using the semantic highlighting information, see the [semantic highlighting
|
43
|
+
documentation](SEMANTIC_HIGHLIGHTING.md).
|
44
|
+
|
42
45
|
## Learn More
|
43
46
|
|
44
47
|
* [RubyConf 2022: Improving the development experience with language servers](https://www.youtube.com/watch?v=kEfXPTm1aCI) ([Vinicius Stock](https://github.com/vinistock))
|
45
48
|
* [Remote Ruby: Ruby Language Server with Vinicius Stock](https://remoteruby.com/221)
|
49
|
+
* [RubyKaigi 2023: Code indexing - How language servers understand our code](https://www.youtube.com/watch?v=ks3tQojSJLU) ([Vinicius Stock](https://github.com/vinistock))
|
46
50
|
|
47
51
|
## Contributing
|
48
52
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/exe/ruby-lsp
CHANGED
@@ -20,7 +20,39 @@ end
|
|
20
20
|
|
21
21
|
require_relative "../lib/ruby_lsp/internal"
|
22
22
|
|
23
|
-
|
23
|
+
require "optparse"
|
24
|
+
|
25
|
+
options = {}
|
26
|
+
parser = OptionParser.new do |opts|
|
27
|
+
opts.banner = "Usage: ruby-lsp [options]"
|
28
|
+
|
29
|
+
opts.on("--version", "Print ruby-lsp version") do
|
30
|
+
puts RubyLsp::VERSION
|
31
|
+
exit(0)
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("--debug", "Launch the Ruby LSP with a debugger attached") do
|
35
|
+
options[:debug] = true
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("-h", "--help", "Print this help") do
|
39
|
+
puts opts.help
|
40
|
+
puts
|
41
|
+
puts "See https://shopify.github.io/ruby-lsp/ for more information"
|
42
|
+
exit(0)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
parser.parse!
|
48
|
+
rescue OptionParser::InvalidOption => e
|
49
|
+
warn(e)
|
50
|
+
warn("")
|
51
|
+
warn(parser.help)
|
52
|
+
exit(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
if options[:debug]
|
24
56
|
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
25
57
|
puts "Debugging is not supported on Windows"
|
26
58
|
exit 1
|
data/lib/ruby_lsp/check_docs.rb
CHANGED
@@ -42,7 +42,7 @@ module RubyLsp
|
|
42
42
|
# Find all classes that inherit from BaseRequest or Listener, which are the ones we want to make sure are
|
43
43
|
# documented
|
44
44
|
features = ObjectSpace.each_object(Class).filter_map do |k|
|
45
|
-
klass = T.cast(k, Class)
|
45
|
+
klass = T.cast(k, T::Class[T.anything])
|
46
46
|
klass if klass < RubyLsp::Requests::BaseRequest || klass < RubyLsp::Listener
|
47
47
|
end
|
48
48
|
|
@@ -20,6 +20,7 @@ module RubyLsp
|
|
20
20
|
# ```
|
21
21
|
class EventEmitter < SyntaxTree::Visitor
|
22
22
|
extend T::Sig
|
23
|
+
include SyntaxTree::WithScope
|
23
24
|
|
24
25
|
sig { void }
|
25
26
|
def initialize
|
@@ -73,6 +74,12 @@ module RubyLsp
|
|
73
74
|
@listeners[:after_command]&.each { |l| T.unsafe(l).after_command(node) }
|
74
75
|
end
|
75
76
|
|
77
|
+
sig { override.params(node: SyntaxTree::CommandCall).void }
|
78
|
+
def visit_command_call(node)
|
79
|
+
@listeners[:on_command_call]&.each { |l| T.unsafe(l).on_command_call(node) }
|
80
|
+
super
|
81
|
+
end
|
82
|
+
|
76
83
|
sig { override.params(node: SyntaxTree::CallNode).void }
|
77
84
|
def visit_call(node)
|
78
85
|
@listeners[:on_call]&.each { |l| T.unsafe(l).on_call(node) }
|
@@ -116,5 +123,59 @@ module RubyLsp
|
|
116
123
|
@listeners[:on_comment]&.each { |l| T.unsafe(l).on_comment(node) }
|
117
124
|
super
|
118
125
|
end
|
126
|
+
|
127
|
+
sig { override.params(node: SyntaxTree::Rescue).void }
|
128
|
+
def visit_rescue(node)
|
129
|
+
@listeners[:on_rescue]&.each { |l| T.unsafe(l).on_rescue(node) }
|
130
|
+
super
|
131
|
+
end
|
132
|
+
|
133
|
+
sig { override.params(node: SyntaxTree::Kw).void }
|
134
|
+
def visit_kw(node)
|
135
|
+
@listeners[:on_kw]&.each { |l| T.unsafe(l).on_kw(node) }
|
136
|
+
super
|
137
|
+
end
|
138
|
+
|
139
|
+
sig { override.params(node: SyntaxTree::Params).void }
|
140
|
+
def visit_params(node)
|
141
|
+
@listeners[:on_params]&.each { |l| T.unsafe(l).on_params(node) }
|
142
|
+
super
|
143
|
+
end
|
144
|
+
|
145
|
+
sig { override.params(node: SyntaxTree::Field).void }
|
146
|
+
def visit_field(node)
|
147
|
+
@listeners[:on_field]&.each { |l| T.unsafe(l).on_field(node) }
|
148
|
+
super
|
149
|
+
end
|
150
|
+
|
151
|
+
sig { override.params(node: SyntaxTree::VarRef).void }
|
152
|
+
def visit_var_ref(node)
|
153
|
+
@listeners[:on_var_ref]&.each { |l| T.unsafe(l).on_var_ref(node) }
|
154
|
+
super
|
155
|
+
end
|
156
|
+
|
157
|
+
sig { override.params(node: SyntaxTree::BlockVar).void }
|
158
|
+
def visit_block_var(node)
|
159
|
+
@listeners[:on_block_var]&.each { |l| T.unsafe(l).on_block_var(node) }
|
160
|
+
super
|
161
|
+
end
|
162
|
+
|
163
|
+
sig { override.params(node: SyntaxTree::LambdaVar).void }
|
164
|
+
def visit_lambda_var(node)
|
165
|
+
@listeners[:on_lambda_var]&.each { |l| T.unsafe(l).on_lambda_var(node) }
|
166
|
+
super
|
167
|
+
end
|
168
|
+
|
169
|
+
sig { override.params(node: SyntaxTree::Binary).void }
|
170
|
+
def visit_binary(node)
|
171
|
+
super
|
172
|
+
@listeners[:after_binary]&.each { |l| T.unsafe(l).after_binary(node) }
|
173
|
+
end
|
174
|
+
|
175
|
+
sig { override.params(node: SyntaxTree::Const).void }
|
176
|
+
def visit_const(node)
|
177
|
+
@listeners[:on_const]&.each { |l| T.unsafe(l).on_const(node) }
|
178
|
+
super
|
179
|
+
end
|
119
180
|
end
|
120
181
|
end
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "ruby_lsp/requests/support/dependency_detector"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
# This class dispatches a request execution to the right request class. No IO should happen anywhere here!
|
6
8
|
class Executor
|
@@ -11,6 +13,7 @@ module RubyLsp
|
|
11
13
|
# Requests that mutate the store must be run sequentially! Parallel requests only receive a temporary copy of the
|
12
14
|
# store
|
13
15
|
@store = store
|
16
|
+
@test_library = T.let(DependencyDetector.detected_test_library, String)
|
14
17
|
@message_queue = message_queue
|
15
18
|
end
|
16
19
|
|
@@ -81,7 +84,8 @@ module RubyLsp
|
|
81
84
|
folding_range(uri)
|
82
85
|
when "textDocument/selectionRange"
|
83
86
|
selection_range(uri, request.dig(:params, :positions))
|
84
|
-
when "textDocument/documentSymbol", "textDocument/documentLink", "textDocument/codeLens"
|
87
|
+
when "textDocument/documentSymbol", "textDocument/documentLink", "textDocument/codeLens",
|
88
|
+
"textDocument/semanticTokens/full"
|
85
89
|
document = @store.get(uri)
|
86
90
|
|
87
91
|
# If the response has already been cached by another request, return it
|
@@ -92,10 +96,11 @@ module RubyLsp
|
|
92
96
|
emitter = EventEmitter.new
|
93
97
|
document_symbol = Requests::DocumentSymbol.new(emitter, @message_queue)
|
94
98
|
document_link = Requests::DocumentLink.new(uri, emitter, @message_queue)
|
95
|
-
code_lens = Requests::CodeLens.new(uri, emitter, @message_queue)
|
99
|
+
code_lens = Requests::CodeLens.new(uri, emitter, @message_queue, @test_library)
|
96
100
|
code_lens_extensions_listeners = Requests::CodeLens.listeners.map do |l|
|
97
101
|
T.unsafe(l).new(document.uri, emitter, @message_queue)
|
98
102
|
end
|
103
|
+
semantic_highlighting = Requests::SemanticHighlighting.new(emitter, @message_queue)
|
99
104
|
emitter.visit(document.tree) if document.parsed?
|
100
105
|
|
101
106
|
code_lens_extensions_listeners.each { |ext| code_lens.merge_response!(ext) }
|
@@ -105,9 +110,11 @@ module RubyLsp
|
|
105
110
|
document.cache_set("textDocument/documentSymbol", document_symbol.response)
|
106
111
|
document.cache_set("textDocument/documentLink", document_link.response)
|
107
112
|
document.cache_set("textDocument/codeLens", code_lens.response)
|
113
|
+
document.cache_set(
|
114
|
+
"textDocument/semanticTokens/full",
|
115
|
+
Requests::Support::SemanticTokenEncoder.new.encode(semantic_highlighting.response),
|
116
|
+
)
|
108
117
|
document.cache_get(request[:method])
|
109
|
-
when "textDocument/semanticTokens/full"
|
110
|
-
semantic_tokens_full(uri)
|
111
118
|
when "textDocument/semanticTokens/range"
|
112
119
|
semantic_tokens_range(uri, request.dig(:params, :range))
|
113
120
|
when "textDocument/formatting"
|
@@ -245,19 +252,6 @@ module RubyLsp
|
|
245
252
|
end
|
246
253
|
end
|
247
254
|
|
248
|
-
sig { params(uri: String).returns(Interface::SemanticTokens) }
|
249
|
-
def semantic_tokens_full(uri)
|
250
|
-
@store.cache_fetch(uri, "textDocument/semanticTokens/full") do |document|
|
251
|
-
T.cast(
|
252
|
-
Requests::SemanticHighlighting.new(
|
253
|
-
document,
|
254
|
-
encoder: Requests::Support::SemanticTokenEncoder.new,
|
255
|
-
).run,
|
256
|
-
Interface::SemanticTokens,
|
257
|
-
)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
255
|
sig { params(uri: String).returns(T.nilable(T::Array[Interface::TextEdit])) }
|
262
256
|
def formatting(uri)
|
263
257
|
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
@@ -287,13 +281,18 @@ module RubyLsp
|
|
287
281
|
Requests::DocumentHighlight.new(@store.get(uri), position).run
|
288
282
|
end
|
289
283
|
|
290
|
-
sig { params(uri: String, range: Document::RangeShape).returns(T::Array[Interface::InlayHint]) }
|
284
|
+
sig { params(uri: String, range: Document::RangeShape).returns(T.nilable(T::Array[Interface::InlayHint])) }
|
291
285
|
def inlay_hint(uri, range)
|
292
286
|
document = @store.get(uri)
|
287
|
+
return if document.syntax_error?
|
288
|
+
|
293
289
|
start_line = range.dig(:start, :line)
|
294
290
|
end_line = range.dig(:end, :line)
|
295
291
|
|
296
|
-
|
292
|
+
emitter = EventEmitter.new
|
293
|
+
listener = Requests::InlayHints.new(start_line..end_line, emitter, @message_queue)
|
294
|
+
emitter.visit(document.tree)
|
295
|
+
listener.response
|
297
296
|
end
|
298
297
|
|
299
298
|
sig do
|
@@ -354,14 +353,15 @@ module RubyLsp
|
|
354
353
|
start_line = range.dig(:start, :line)
|
355
354
|
end_line = range.dig(:end, :line)
|
356
355
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
).run,
|
363
|
-
Interface::SemanticTokens,
|
356
|
+
emitter = EventEmitter.new
|
357
|
+
listener = Requests::SemanticHighlighting.new(
|
358
|
+
emitter,
|
359
|
+
@message_queue,
|
360
|
+
range: start_line..end_line,
|
364
361
|
)
|
362
|
+
emitter.visit(document.tree) if document.parsed?
|
363
|
+
|
364
|
+
Requests::Support::SemanticTokenEncoder.new.encode(listener.response)
|
365
365
|
end
|
366
366
|
|
367
367
|
sig do
|
@@ -421,9 +421,9 @@ module RubyLsp
|
|
421
421
|
encodings.first
|
422
422
|
end
|
423
423
|
|
424
|
-
formatter = options.dig(:initializationOptions, :formatter)
|
424
|
+
formatter = options.dig(:initializationOptions, :formatter) || "auto"
|
425
425
|
@store.formatter = if formatter == "auto"
|
426
|
-
detected_formatter
|
426
|
+
DependencyDetector.detected_formatter
|
427
427
|
else
|
428
428
|
formatter
|
429
429
|
end
|
@@ -536,23 +536,6 @@ module RubyLsp
|
|
536
536
|
)
|
537
537
|
end
|
538
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
539
|
sig { void }
|
557
540
|
def check_formatter_is_available
|
558
541
|
# Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
|
@@ -560,11 +543,13 @@ module RubyLsp
|
|
560
543
|
return unless @store.formatter == "rubocop"
|
561
544
|
|
562
545
|
unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
|
546
|
+
@store.formatter = "none"
|
547
|
+
|
563
548
|
@message_queue << Notification.new(
|
564
549
|
message: "window/showMessage",
|
565
550
|
params: Interface::ShowMessageParams.new(
|
566
551
|
type: Constant::MessageType::ERROR,
|
567
|
-
message: "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the
|
552
|
+
message: "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
|
568
553
|
),
|
569
554
|
)
|
570
555
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -11,12 +11,8 @@ module RubyLsp
|
|
11
11
|
|
12
12
|
abstract!
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
# anything. We can remove these arguments once we drop support for Ruby 2.7
|
17
|
-
# https://github.com/ruby-syntax-tree/syntax_tree/blob/4dac90b53df388f726dce50ce638a1ba71cc59f8/lib/syntax_tree/with_scope.rb#L122
|
18
|
-
sig { params(document: Document, _kwargs: T.untyped).void }
|
19
|
-
def initialize(document, **_kwargs)
|
14
|
+
sig { params(document: Document).void }
|
15
|
+
def initialize(document)
|
20
16
|
@document = document
|
21
17
|
super()
|
22
18
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "shellwords"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
module Requests
|
6
8
|
# 
|
@@ -31,35 +33,63 @@ module RubyLsp
|
|
31
33
|
sig { override.returns(ResponseType) }
|
32
34
|
attr_reader :response
|
33
35
|
|
34
|
-
sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
|
35
|
-
def initialize(uri, emitter, message_queue)
|
36
|
+
sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue, test_library: String).void }
|
37
|
+
def initialize(uri, emitter, message_queue, test_library)
|
36
38
|
super(emitter, message_queue)
|
37
39
|
|
40
|
+
@test_library = T.let(test_library, String)
|
38
41
|
@response = T.let([], ResponseType)
|
39
42
|
@path = T.let(T.must(URI(uri).path), String)
|
40
|
-
|
41
|
-
@
|
42
|
-
|
43
|
-
|
43
|
+
# visibility_stack is a stack of [current_visibility, previous_visibility]
|
44
|
+
@visibility_stack = T.let([["public", "public"]], T::Array[T::Array[T.nilable(String)]])
|
45
|
+
@class_stack = T.let([], T::Array[String])
|
46
|
+
|
47
|
+
emitter.register(
|
48
|
+
self,
|
49
|
+
:on_class,
|
50
|
+
:after_class,
|
51
|
+
:on_def,
|
52
|
+
:on_command,
|
53
|
+
:after_command,
|
54
|
+
:on_call,
|
55
|
+
:after_call,
|
56
|
+
:on_vcall,
|
57
|
+
)
|
44
58
|
end
|
45
59
|
|
46
60
|
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
47
61
|
def on_class(node)
|
62
|
+
@visibility_stack.push(["public", "public"])
|
48
63
|
class_name = node.constant.constant.value
|
49
64
|
if class_name.end_with?("Test")
|
50
|
-
|
65
|
+
@class_stack.push(class_name)
|
66
|
+
add_test_code_lens(
|
67
|
+
node,
|
68
|
+
name: class_name,
|
69
|
+
command: generate_test_command(class_name: class_name),
|
70
|
+
kind: :group,
|
71
|
+
)
|
51
72
|
end
|
52
73
|
end
|
53
74
|
|
75
|
+
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
76
|
+
def after_class(node)
|
77
|
+
@visibility_stack.pop
|
78
|
+
@class_stack.pop
|
79
|
+
end
|
80
|
+
|
54
81
|
sig { params(node: SyntaxTree::DefNode).void }
|
55
82
|
def on_def(node)
|
56
|
-
|
83
|
+
visibility, _ = @visibility_stack.last
|
84
|
+
if visibility == "public"
|
57
85
|
method_name = node.name.value
|
58
86
|
if method_name.start_with?("test_")
|
59
|
-
|
87
|
+
class_name = T.must(@class_stack.last)
|
88
|
+
add_test_code_lens(
|
60
89
|
node,
|
61
90
|
name: method_name,
|
62
|
-
command:
|
91
|
+
command: generate_test_command(method_name: method_name, class_name: class_name),
|
92
|
+
kind: :example,
|
63
93
|
)
|
64
94
|
end
|
65
95
|
end
|
@@ -67,15 +97,22 @@ module RubyLsp
|
|
67
97
|
|
68
98
|
sig { params(node: SyntaxTree::Command).void }
|
69
99
|
def on_command(node)
|
70
|
-
|
71
|
-
|
72
|
-
|
100
|
+
node_message = node.message.value
|
101
|
+
if ACCESS_MODIFIERS.include?(node_message) && node.arguments.parts.any?
|
102
|
+
visibility, _ = @visibility_stack.pop
|
103
|
+
@visibility_stack.push([node_message, visibility])
|
104
|
+
elsif @path.include?("Gemfile") && node_message.include?("gem") && node.arguments.parts.any?
|
105
|
+
remote = resolve_gem_remote(node)
|
106
|
+
return unless remote
|
107
|
+
|
108
|
+
add_open_gem_remote_code_lens(node, remote)
|
73
109
|
end
|
74
110
|
end
|
75
111
|
|
76
112
|
sig { params(node: SyntaxTree::Command).void }
|
77
113
|
def after_command(node)
|
78
|
-
|
114
|
+
_, prev_visibility = @visibility_stack.pop
|
115
|
+
@visibility_stack.push([prev_visibility, prev_visibility])
|
79
116
|
end
|
80
117
|
|
81
118
|
sig { params(node: SyntaxTree::CallNode).void }
|
@@ -85,15 +122,16 @@ module RubyLsp
|
|
85
122
|
if ident
|
86
123
|
ident_value = T.cast(ident, SyntaxTree::Ident).value
|
87
124
|
if ACCESS_MODIFIERS.include?(ident_value)
|
88
|
-
|
89
|
-
@visibility
|
125
|
+
visibility, _ = @visibility_stack.pop
|
126
|
+
@visibility_stack.push([ident_value, visibility])
|
90
127
|
end
|
91
128
|
end
|
92
129
|
end
|
93
130
|
|
94
131
|
sig { params(node: SyntaxTree::CallNode).void }
|
95
132
|
def after_call(node)
|
96
|
-
|
133
|
+
_, prev_visibility = @visibility_stack.pop
|
134
|
+
@visibility_stack.push([prev_visibility, prev_visibility])
|
97
135
|
end
|
98
136
|
|
99
137
|
sig { params(node: SyntaxTree::VCall).void }
|
@@ -101,8 +139,8 @@ module RubyLsp
|
|
101
139
|
vcall_value = node.value.value
|
102
140
|
|
103
141
|
if ACCESS_MODIFIERS.include?(vcall_value)
|
104
|
-
@
|
105
|
-
@
|
142
|
+
@visibility_stack.pop
|
143
|
+
@visibility_stack.push([vcall_value, vcall_value])
|
106
144
|
end
|
107
145
|
end
|
108
146
|
|
@@ -114,36 +152,86 @@ module RubyLsp
|
|
114
152
|
|
115
153
|
private
|
116
154
|
|
117
|
-
sig { params(node: SyntaxTree::Node, name: String, command: String).void }
|
118
|
-
def
|
155
|
+
sig { params(node: SyntaxTree::Node, name: String, command: String, kind: Symbol).void }
|
156
|
+
def add_test_code_lens(node, name:, command:, kind:)
|
157
|
+
arguments = [
|
158
|
+
@path,
|
159
|
+
name,
|
160
|
+
command,
|
161
|
+
{
|
162
|
+
start_line: node.location.start_line - 1,
|
163
|
+
start_column: node.location.start_column,
|
164
|
+
end_line: node.location.end_line - 1,
|
165
|
+
end_column: node.location.end_column,
|
166
|
+
},
|
167
|
+
]
|
168
|
+
|
119
169
|
@response << create_code_lens(
|
120
170
|
node,
|
121
171
|
title: "Run",
|
122
172
|
command_name: "rubyLsp.runTest",
|
123
|
-
|
124
|
-
|
125
|
-
test_command: command,
|
126
|
-
type: "test",
|
173
|
+
arguments: arguments,
|
174
|
+
data: { type: "test", kind: kind },
|
127
175
|
)
|
128
176
|
|
129
177
|
@response << create_code_lens(
|
130
178
|
node,
|
131
179
|
title: "Run In Terminal",
|
132
180
|
command_name: "rubyLsp.runTestInTerminal",
|
133
|
-
|
134
|
-
|
135
|
-
test_command: command,
|
136
|
-
type: "test_in_terminal",
|
181
|
+
arguments: arguments,
|
182
|
+
data: { type: "test_in_terminal", kind: kind },
|
137
183
|
)
|
138
184
|
|
139
185
|
@response << create_code_lens(
|
140
186
|
node,
|
141
187
|
title: "Debug",
|
142
188
|
command_name: "rubyLsp.debugTest",
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
189
|
+
arguments: arguments,
|
190
|
+
data: { type: "debug", kind: kind },
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
sig { params(node: SyntaxTree::Command).returns(T.nilable(String)) }
|
195
|
+
def resolve_gem_remote(node)
|
196
|
+
gem_name = node.arguments.parts.flat_map(&:child_nodes).first.value
|
197
|
+
spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name }&.to_spec
|
198
|
+
return if spec.nil?
|
199
|
+
|
200
|
+
[spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
|
201
|
+
page.start_with?("https://github.com", "https://gitlab.com")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
sig { params(class_name: String, method_name: T.nilable(String)).returns(String) }
|
206
|
+
def generate_test_command(class_name:, method_name: nil)
|
207
|
+
command = BASE_COMMAND + @path
|
208
|
+
|
209
|
+
case @test_library
|
210
|
+
when "minitest"
|
211
|
+
command += if method_name
|
212
|
+
" --name " + "/#{Shellwords.escape(class_name + "#" + method_name)}/"
|
213
|
+
else
|
214
|
+
" --name " + "/#{Shellwords.escape(class_name)}/"
|
215
|
+
end
|
216
|
+
when "test-unit"
|
217
|
+
command += " --testcase " + "/#{Shellwords.escape(class_name)}/"
|
218
|
+
|
219
|
+
if method_name
|
220
|
+
command += " --name " + Shellwords.escape(method_name)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
command
|
225
|
+
end
|
226
|
+
|
227
|
+
sig { params(node: SyntaxTree::Command, remote: String).void }
|
228
|
+
def add_open_gem_remote_code_lens(node, remote)
|
229
|
+
@response << create_code_lens(
|
230
|
+
node,
|
231
|
+
title: "Open remote",
|
232
|
+
command_name: "rubyLsp.openLink",
|
233
|
+
arguments: [remote],
|
234
|
+
data: { type: "link" },
|
147
235
|
)
|
148
236
|
end
|
149
237
|
end
|
@@ -30,7 +30,9 @@ module RubyLsp
|
|
30
30
|
|
31
31
|
sig { override.returns(T.nilable(T.all(T::Array[Support::RuboCopDiagnostic], Object))) }
|
32
32
|
def run
|
33
|
+
# Running RuboCop is slow, so to avoid excessive runs we only do so if the file is syntactically valid
|
33
34
|
return if @document.syntax_error?
|
35
|
+
|
34
36
|
return unless defined?(Support::RuboCopDiagnosticsRunner)
|
35
37
|
|
36
38
|
# Don't try to run RuboCop diagnostics for files outside the current working directory
|
@@ -27,7 +27,6 @@ module RubyLsp
|
|
27
27
|
SyntaxTree::For,
|
28
28
|
SyntaxTree::HashLiteral,
|
29
29
|
SyntaxTree::Heredoc,
|
30
|
-
SyntaxTree::IfNode,
|
31
30
|
SyntaxTree::ModuleDeclaration,
|
32
31
|
SyntaxTree::SClass,
|
33
32
|
SyntaxTree::UnlessNode,
|
@@ -42,6 +41,7 @@ module RubyLsp
|
|
42
41
|
|
43
42
|
NODES_WITH_STATEMENTS = T.let(
|
44
43
|
[
|
44
|
+
SyntaxTree::IfNode,
|
45
45
|
SyntaxTree::Elsif,
|
46
46
|
SyntaxTree::In,
|
47
47
|
SyntaxTree::Rescue,
|
@@ -52,6 +52,7 @@ module RubyLsp
|
|
52
52
|
|
53
53
|
StatementNode = T.type_alias do
|
54
54
|
T.any(
|
55
|
+
SyntaxTree::IfNode,
|
55
56
|
SyntaxTree::Elsif,
|
56
57
|
SyntaxTree::In,
|
57
58
|
SyntaxTree::Rescue,
|