ruby-lsp 0.12.2 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp-check +20 -4
- data/exe/ruby-lsp-doctor +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/{visitor.rb → collector.rb} +144 -61
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +9 -4
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +89 -12
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +22 -4
- data/lib/ruby_indexer/ruby_indexer.rb +1 -1
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/index_test.rb +64 -0
- data/lib/ruby_indexer/test/method_test.rb +80 -0
- data/lib/ruby_lsp/addon.rb +9 -13
- data/lib/ruby_lsp/document.rb +7 -9
- data/lib/ruby_lsp/executor.rb +54 -51
- data/lib/ruby_lsp/internal.rb +4 -0
- data/lib/ruby_lsp/listener.rb +4 -5
- data/lib/ruby_lsp/requests/code_action_resolve.rb +8 -4
- data/lib/ruby_lsp/requests/code_lens.rb +16 -7
- data/lib/ruby_lsp/requests/completion.rb +60 -8
- data/lib/ruby_lsp/requests/definition.rb +55 -29
- data/lib/ruby_lsp/requests/diagnostics.rb +0 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +20 -11
- data/lib/ruby_lsp/requests/document_link.rb +2 -3
- data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
- data/lib/ruby_lsp/requests/folding_ranges.rb +12 -15
- data/lib/ruby_lsp/requests/formatting.rb +0 -5
- data/lib/ruby_lsp/requests/hover.rb +23 -4
- data/lib/ruby_lsp/requests/inlay_hints.rb +42 -4
- data/lib/ruby_lsp/requests/on_type_formatting.rb +18 -4
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +41 -16
- data/lib/ruby_lsp/requests/support/common.rb +22 -2
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +0 -1
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -8
- data/lib/ruby_lsp/requests/workspace_symbol.rb +6 -11
- data/lib/ruby_lsp/ruby_document.rb +14 -0
- data/lib/ruby_lsp/setup_bundler.rb +2 -0
- data/lib/ruby_lsp/store.rb +5 -3
- data/lib/ruby_lsp/utils.rb +8 -3
- metadata +8 -7
@@ -193,5 +193,69 @@ module RubyIndexer
|
|
193
193
|
|
194
194
|
assert_instance_of(Entry::UnresolvedAlias, entry)
|
195
195
|
end
|
196
|
+
|
197
|
+
def test_visitor_does_not_visit_unnecessary_nodes
|
198
|
+
concats = (0...10_000).map do |i|
|
199
|
+
<<~STRING
|
200
|
+
"string#{i}" \\
|
201
|
+
STRING
|
202
|
+
end.join
|
203
|
+
|
204
|
+
index(<<~RUBY)
|
205
|
+
module Foo
|
206
|
+
local_var = #{concats}
|
207
|
+
"final"
|
208
|
+
@class_instance_var = #{concats}
|
209
|
+
"final"
|
210
|
+
@@class_var = #{concats}
|
211
|
+
"final"
|
212
|
+
$global_var = #{concats}
|
213
|
+
"final"
|
214
|
+
CONST = #{concats}
|
215
|
+
"final"
|
216
|
+
end
|
217
|
+
RUBY
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_resolve_method_with_known_receiver
|
221
|
+
index(<<~RUBY)
|
222
|
+
module Foo
|
223
|
+
module Bar
|
224
|
+
def baz; end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
RUBY
|
228
|
+
|
229
|
+
entry = T.must(@index.resolve_method("baz", "Foo::Bar"))
|
230
|
+
assert_equal("baz", entry.name)
|
231
|
+
assert_equal("Foo::Bar", T.must(entry.owner).name)
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_prefix_search_for_methods
|
235
|
+
index(<<~RUBY)
|
236
|
+
module Foo
|
237
|
+
module Bar
|
238
|
+
def baz; end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
RUBY
|
242
|
+
|
243
|
+
entries = @index.prefix_search("ba")
|
244
|
+
refute_empty(entries)
|
245
|
+
|
246
|
+
entry = T.must(entries.first).first
|
247
|
+
assert_equal("baz", entry.name)
|
248
|
+
end
|
249
|
+
|
250
|
+
def test_indexing_prism_fixtures_succeeds
|
251
|
+
fixtures = Dir.glob("test/fixtures/prism/test/prism/fixtures/**/*.txt")
|
252
|
+
|
253
|
+
fixtures.each do |fixture|
|
254
|
+
indexable_path = IndexablePath.new("", fixture)
|
255
|
+
@index.index_single(indexable_path)
|
256
|
+
end
|
257
|
+
|
258
|
+
refute_empty(@index.instance_variable_get(:@entries))
|
259
|
+
end
|
196
260
|
end
|
197
261
|
end
|
@@ -69,5 +69,85 @@ module RubyIndexer
|
|
69
69
|
assert_equal(:"(a, (b, ))", parameter.name)
|
70
70
|
assert_instance_of(Entry::RequiredParameter, parameter)
|
71
71
|
end
|
72
|
+
|
73
|
+
def test_method_with_optional_parameters
|
74
|
+
index(<<~RUBY)
|
75
|
+
class Foo
|
76
|
+
def bar(a = 123)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
RUBY
|
80
|
+
|
81
|
+
assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
|
82
|
+
entry = T.must(@index["bar"].first)
|
83
|
+
assert_equal(1, entry.parameters.length)
|
84
|
+
parameter = entry.parameters.first
|
85
|
+
assert_equal(:a, parameter.name)
|
86
|
+
assert_instance_of(Entry::OptionalParameter, parameter)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_method_with_keyword_parameters
|
90
|
+
index(<<~RUBY)
|
91
|
+
class Foo
|
92
|
+
def bar(a:, b: 123)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
|
97
|
+
assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
|
98
|
+
entry = T.must(@index["bar"].first)
|
99
|
+
assert_equal(2, entry.parameters.length)
|
100
|
+
a, b = entry.parameters
|
101
|
+
|
102
|
+
assert_equal(:a, a.name)
|
103
|
+
assert_instance_of(Entry::KeywordParameter, a)
|
104
|
+
|
105
|
+
assert_equal(:b, b.name)
|
106
|
+
assert_instance_of(Entry::OptionalKeywordParameter, b)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_keeps_track_of_method_owner
|
110
|
+
index(<<~RUBY)
|
111
|
+
class Foo
|
112
|
+
def bar
|
113
|
+
end
|
114
|
+
end
|
115
|
+
RUBY
|
116
|
+
|
117
|
+
entry = T.must(@index["bar"].first)
|
118
|
+
owner_name = T.must(entry.owner).name
|
119
|
+
|
120
|
+
assert_equal("Foo", owner_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_keeps_track_of_attributes
|
124
|
+
index(<<~RUBY)
|
125
|
+
class Foo
|
126
|
+
# Hello there
|
127
|
+
attr_reader :bar, :other
|
128
|
+
attr_writer :baz
|
129
|
+
attr_accessor :qux
|
130
|
+
end
|
131
|
+
RUBY
|
132
|
+
|
133
|
+
assert_entry("bar", Entry::Accessor, "/fake/path/foo.rb:2-15:2-18")
|
134
|
+
assert_equal("Hello there", @index["bar"].first.comments.join("\n"))
|
135
|
+
assert_entry("other", Entry::Accessor, "/fake/path/foo.rb:2-21:2-26")
|
136
|
+
assert_equal("Hello there", @index["other"].first.comments.join("\n"))
|
137
|
+
assert_entry("baz=", Entry::Accessor, "/fake/path/foo.rb:3-15:3-18")
|
138
|
+
assert_entry("qux", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
|
139
|
+
assert_entry("qux=", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_ignores_attributes_invoked_on_constant
|
143
|
+
index(<<~RUBY)
|
144
|
+
class Foo
|
145
|
+
end
|
146
|
+
|
147
|
+
Foo.attr_reader :bar
|
148
|
+
RUBY
|
149
|
+
|
150
|
+
assert_no_entry("bar")
|
151
|
+
end
|
72
152
|
end
|
73
153
|
end
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -41,8 +41,8 @@ module RubyLsp
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Discovers and loads all addons. Returns the list of activated addons
|
44
|
-
sig { returns(T::Array[Addon]) }
|
45
|
-
def load_addons
|
44
|
+
sig { params(message_queue: Thread::Queue).returns(T::Array[Addon]) }
|
45
|
+
def load_addons(message_queue)
|
46
46
|
# Require all addons entry points, which should be placed under
|
47
47
|
# `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
|
48
48
|
Gem.find_files("ruby_lsp/**/addon.rb").each do |addon|
|
@@ -55,7 +55,7 @@ module RubyLsp
|
|
55
55
|
# Activate each one of the discovered addons. If any problems occur in the addons, we don't want to
|
56
56
|
# fail to boot the server
|
57
57
|
addons.each do |addon|
|
58
|
-
addon.activate
|
58
|
+
addon.activate(message_queue)
|
59
59
|
nil
|
60
60
|
rescue => e
|
61
61
|
addon.add_error(e)
|
@@ -94,8 +94,8 @@ module RubyLsp
|
|
94
94
|
|
95
95
|
# Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
|
96
96
|
# reading information into memory or even spawning a separate process
|
97
|
-
sig { abstract.void }
|
98
|
-
def activate; end
|
97
|
+
sig { abstract.params(message_queue: Thread::Queue).void }
|
98
|
+
def activate(message_queue); end
|
99
99
|
|
100
100
|
# Each addon should implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
|
101
101
|
# child process
|
@@ -111,10 +111,9 @@ module RubyLsp
|
|
111
111
|
overridable.params(
|
112
112
|
uri: URI::Generic,
|
113
113
|
dispatcher: Prism::Dispatcher,
|
114
|
-
message_queue: Thread::Queue,
|
115
114
|
).returns(T.nilable(Listener[T::Array[Interface::CodeLens]]))
|
116
115
|
end
|
117
|
-
def create_code_lens_listener(uri, dispatcher
|
116
|
+
def create_code_lens_listener(uri, dispatcher); end
|
118
117
|
|
119
118
|
# Creates a new Hover listener. This method is invoked on every Hover request
|
120
119
|
sig do
|
@@ -122,19 +121,17 @@ module RubyLsp
|
|
122
121
|
nesting: T::Array[String],
|
123
122
|
index: RubyIndexer::Index,
|
124
123
|
dispatcher: Prism::Dispatcher,
|
125
|
-
message_queue: Thread::Queue,
|
126
124
|
).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
|
127
125
|
end
|
128
|
-
def create_hover_listener(nesting, index, dispatcher
|
126
|
+
def create_hover_listener(nesting, index, dispatcher); end
|
129
127
|
|
130
128
|
# Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
|
131
129
|
sig do
|
132
130
|
overridable.params(
|
133
131
|
dispatcher: Prism::Dispatcher,
|
134
|
-
message_queue: Thread::Queue,
|
135
132
|
).returns(T.nilable(Listener[T::Array[Interface::DocumentSymbol]]))
|
136
133
|
end
|
137
|
-
def create_document_symbol_listener(dispatcher
|
134
|
+
def create_document_symbol_listener(dispatcher); end
|
138
135
|
|
139
136
|
# Creates a new Definition listener. This method is invoked on every Definition request
|
140
137
|
sig do
|
@@ -143,9 +140,8 @@ module RubyLsp
|
|
143
140
|
nesting: T::Array[String],
|
144
141
|
index: RubyIndexer::Index,
|
145
142
|
dispatcher: Prism::Dispatcher,
|
146
|
-
message_queue: Thread::Queue,
|
147
143
|
).returns(T.nilable(Listener[T.nilable(T.any(T::Array[Interface::Location], Interface::Location))]))
|
148
144
|
end
|
149
|
-
def create_definition_listener(uri, nesting, index, dispatcher
|
145
|
+
def create_definition_listener(uri, nesting, index, dispatcher); end
|
150
146
|
end
|
151
147
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -4,6 +4,9 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
class Document
|
6
6
|
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
|
9
|
+
abstract!
|
7
10
|
|
8
11
|
PositionShape = T.type_alias { { line: Integer, character: Integer } }
|
9
12
|
RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
|
@@ -28,8 +31,8 @@ module RubyLsp
|
|
28
31
|
@source = T.let(source, String)
|
29
32
|
@version = T.let(version, Integer)
|
30
33
|
@uri = T.let(uri, URI::Generic)
|
31
|
-
@needs_parsing = T.let(
|
32
|
-
@parse_result = T.let(
|
34
|
+
@needs_parsing = T.let(true, T::Boolean)
|
35
|
+
@parse_result = T.let(parse, Prism::ParseResult)
|
33
36
|
end
|
34
37
|
|
35
38
|
sig { returns(Prism::ProgramNode) }
|
@@ -91,13 +94,8 @@ module RubyLsp
|
|
91
94
|
@cache.clear
|
92
95
|
end
|
93
96
|
|
94
|
-
sig {
|
95
|
-
def parse
|
96
|
-
return unless @needs_parsing
|
97
|
-
|
98
|
-
@needs_parsing = false
|
99
|
-
@parse_result = Prism.parse(@source)
|
100
|
-
end
|
97
|
+
sig { abstract.returns(Prism::ParseResult) }
|
98
|
+
def parse; end
|
101
99
|
|
102
100
|
sig { returns(T::Boolean) }
|
103
101
|
def syntax_error?
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -41,7 +41,7 @@ module RubyLsp
|
|
41
41
|
when "initialize"
|
42
42
|
initialize_request(request.dig(:params))
|
43
43
|
when "initialized"
|
44
|
-
Addon.load_addons
|
44
|
+
Addon.load_addons(@message_queue)
|
45
45
|
|
46
46
|
errored_addons = Addon.addons.select(&:error?)
|
47
47
|
|
@@ -57,6 +57,8 @@ module RubyLsp
|
|
57
57
|
warn(errored_addons.map(&:backtraces).join("\n\n"))
|
58
58
|
end
|
59
59
|
|
60
|
+
RubyVM::YJIT.enable if defined? RubyVM::YJIT.enable
|
61
|
+
|
60
62
|
perform_initial_indexing
|
61
63
|
check_formatter_is_available
|
62
64
|
|
@@ -93,12 +95,12 @@ module RubyLsp
|
|
93
95
|
|
94
96
|
# Run listeners for the document
|
95
97
|
dispatcher = Prism::Dispatcher.new
|
96
|
-
folding_range = Requests::FoldingRanges.new(document.parse_result.comments, dispatcher
|
97
|
-
document_symbol = Requests::DocumentSymbol.new(dispatcher
|
98
|
-
document_link = Requests::DocumentLink.new(uri, document.comments, dispatcher
|
99
|
-
code_lens = Requests::CodeLens.new(uri, dispatcher
|
98
|
+
folding_range = Requests::FoldingRanges.new(document.parse_result.comments, dispatcher)
|
99
|
+
document_symbol = Requests::DocumentSymbol.new(dispatcher)
|
100
|
+
document_link = Requests::DocumentLink.new(uri, document.comments, dispatcher)
|
101
|
+
code_lens = Requests::CodeLens.new(uri, dispatcher)
|
100
102
|
|
101
|
-
semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher
|
103
|
+
semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher)
|
102
104
|
dispatcher.dispatch(document.tree)
|
103
105
|
|
104
106
|
# Store all responses retrieve in this round of visits in the cache and then return the response for the request
|
@@ -263,13 +265,7 @@ module RubyLsp
|
|
263
265
|
target = parent if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
|
264
266
|
|
265
267
|
dispatcher = Prism::Dispatcher.new
|
266
|
-
base_listener = Requests::Definition.new(
|
267
|
-
uri,
|
268
|
-
nesting,
|
269
|
-
@index,
|
270
|
-
dispatcher,
|
271
|
-
@message_queue,
|
272
|
-
)
|
268
|
+
base_listener = Requests::Definition.new(uri, nesting, @index, dispatcher)
|
273
269
|
dispatcher.dispatch_once(target)
|
274
270
|
base_listener.response
|
275
271
|
end
|
@@ -295,7 +291,7 @@ module RubyLsp
|
|
295
291
|
|
296
292
|
# Instantiate all listeners
|
297
293
|
dispatcher = Prism::Dispatcher.new
|
298
|
-
hover = Requests::Hover.new(@index, nesting, dispatcher
|
294
|
+
hover = Requests::Hover.new(@index, nesting, dispatcher)
|
299
295
|
|
300
296
|
# Emit events for all listeners
|
301
297
|
dispatcher.dispatch_once(target)
|
@@ -353,6 +349,11 @@ module RubyLsp
|
|
353
349
|
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
354
350
|
return if @store.formatter == "none"
|
355
351
|
|
352
|
+
# Do not format files outside of the workspace. For example, if someone is looking at a gem's source code, we
|
353
|
+
# don't want to format it
|
354
|
+
path = uri.to_standardized_path
|
355
|
+
return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
|
356
|
+
|
356
357
|
Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
|
357
358
|
end
|
358
359
|
|
@@ -378,7 +379,7 @@ module RubyLsp
|
|
378
379
|
|
379
380
|
target, parent = document.locate_node(position)
|
380
381
|
dispatcher = Prism::Dispatcher.new
|
381
|
-
listener = Requests::DocumentHighlight.new(target, parent, dispatcher
|
382
|
+
listener = Requests::DocumentHighlight.new(target, parent, dispatcher)
|
382
383
|
dispatcher.visit(document.tree)
|
383
384
|
listener.response
|
384
385
|
end
|
@@ -391,7 +392,7 @@ module RubyLsp
|
|
391
392
|
end_line = range.dig(:end, :line)
|
392
393
|
|
393
394
|
dispatcher = Prism::Dispatcher.new
|
394
|
-
listener = Requests::InlayHints.new(start_line..end_line, dispatcher
|
395
|
+
listener = Requests::InlayHints.new(start_line..end_line, dispatcher)
|
395
396
|
dispatcher.visit(document.tree)
|
396
397
|
listener.response
|
397
398
|
end
|
@@ -441,6 +442,11 @@ module RubyLsp
|
|
441
442
|
|
442
443
|
sig { params(uri: URI::Generic).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
|
443
444
|
def diagnostic(uri)
|
445
|
+
# Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
|
446
|
+
# source code, we don't want to show diagnostics for it
|
447
|
+
path = uri.to_standardized_path
|
448
|
+
return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
|
449
|
+
|
444
450
|
response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
|
445
451
|
Requests::Diagnostics.new(document).run
|
446
452
|
end
|
@@ -455,11 +461,7 @@ module RubyLsp
|
|
455
461
|
end_line = range.dig(:end, :line)
|
456
462
|
|
457
463
|
dispatcher = Prism::Dispatcher.new
|
458
|
-
listener = Requests::SemanticHighlighting.new(
|
459
|
-
dispatcher,
|
460
|
-
@message_queue,
|
461
|
-
range: start_line..end_line,
|
462
|
-
)
|
464
|
+
listener = Requests::SemanticHighlighting.new(dispatcher, range: start_line..end_line)
|
463
465
|
dispatcher.visit(document.tree)
|
464
466
|
|
465
467
|
Requests::Support::SemanticTokenEncoder.new.encode(listener.response)
|
@@ -474,34 +476,32 @@ module RubyLsp
|
|
474
476
|
def completion(uri, position)
|
475
477
|
document = @store.get(uri)
|
476
478
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
else
|
486
|
-
[Prism::CallNode]
|
487
|
-
end
|
488
|
-
|
489
|
-
matched, parent, nesting = document.locate(document.tree, char_position, node_types: target_node_types)
|
479
|
+
# Completion always receives the position immediately after the character that was just typed. Here we adjust it
|
480
|
+
# back by 1, so that we find the right node
|
481
|
+
char_position = document.create_scanner.find_char_position(position) - 1
|
482
|
+
matched, parent, nesting = document.locate(
|
483
|
+
document.tree,
|
484
|
+
char_position,
|
485
|
+
node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode],
|
486
|
+
)
|
490
487
|
return unless matched && parent
|
491
488
|
|
492
489
|
target = case matched
|
493
490
|
when Prism::CallNode
|
494
491
|
message = matched.message
|
495
|
-
return unless message == "require"
|
496
492
|
|
497
|
-
|
498
|
-
|
493
|
+
if message == "require"
|
494
|
+
args = matched.arguments&.arguments
|
495
|
+
return if args.nil? || args.is_a?(Prism::ForwardingArgumentsNode)
|
499
496
|
|
500
|
-
|
501
|
-
|
502
|
-
|
497
|
+
argument = args.first
|
498
|
+
return unless argument.is_a?(Prism::StringNode)
|
499
|
+
return unless (argument.location.start_offset..argument.location.end_offset).cover?(char_position)
|
503
500
|
|
504
|
-
|
501
|
+
argument
|
502
|
+
else
|
503
|
+
matched
|
504
|
+
end
|
505
505
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
506
506
|
if parent.is_a?(Prism::ConstantPathNode) && matched.is_a?(Prism::ConstantReadNode)
|
507
507
|
parent
|
@@ -513,12 +513,7 @@ module RubyLsp
|
|
513
513
|
return unless target
|
514
514
|
|
515
515
|
dispatcher = Prism::Dispatcher.new
|
516
|
-
listener = Requests::Completion.new(
|
517
|
-
@index,
|
518
|
-
nesting,
|
519
|
-
dispatcher,
|
520
|
-
@message_queue,
|
521
|
-
)
|
516
|
+
listener = Requests::Completion.new(@index, nesting, dispatcher)
|
522
517
|
dispatcher.dispatch_once(target)
|
523
518
|
listener.response
|
524
519
|
end
|
@@ -579,10 +574,13 @@ module RubyLsp
|
|
579
574
|
# notification
|
580
575
|
end
|
581
576
|
|
582
|
-
sig { params(options: T::Hash[Symbol, T.untyped]).returns(
|
577
|
+
sig { params(options: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
583
578
|
def initialize_request(options)
|
584
579
|
@store.clear
|
585
580
|
|
581
|
+
workspace_uri = options.dig(:workspaceFolders, 0, :uri)
|
582
|
+
@store.workspace_uri = URI(workspace_uri) if workspace_uri
|
583
|
+
|
586
584
|
encodings = options.dig(:capabilities, :general, :positionEncodings)
|
587
585
|
@store.encoding = if encodings.nil? || encodings.empty?
|
588
586
|
Constant::PositionEncodingKind::UTF16
|
@@ -680,7 +678,7 @@ module RubyLsp
|
|
680
678
|
completion_provider = if enabled_features["completion"]
|
681
679
|
Interface::CompletionOptions.new(
|
682
680
|
resolve_provider: false,
|
683
|
-
trigger_characters: ["/"
|
681
|
+
trigger_characters: ["/"],
|
684
682
|
completion_item: {
|
685
683
|
labelDetailsSupport: true,
|
686
684
|
},
|
@@ -716,7 +714,7 @@ module RubyLsp
|
|
716
714
|
|
717
715
|
begin_progress("indexing-progress", "Ruby LSP: indexing files")
|
718
716
|
|
719
|
-
|
717
|
+
{
|
720
718
|
capabilities: Interface::ServerCapabilities.new(
|
721
719
|
text_document_sync: Interface::TextDocumentSyncOptions.new(
|
722
720
|
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
@@ -740,7 +738,12 @@ module RubyLsp
|
|
740
738
|
definition_provider: enabled_features["definition"],
|
741
739
|
workspace_symbol_provider: enabled_features["workspaceSymbol"],
|
742
740
|
),
|
743
|
-
|
741
|
+
serverInfo: {
|
742
|
+
name: "Ruby LSP",
|
743
|
+
version: VERSION,
|
744
|
+
},
|
745
|
+
formatter: @store.formatter,
|
746
|
+
}
|
744
747
|
end
|
745
748
|
|
746
749
|
sig { void }
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -24,6 +24,10 @@ require "ruby_lsp/server"
|
|
24
24
|
require "ruby_lsp/executor"
|
25
25
|
require "ruby_lsp/requests"
|
26
26
|
require "ruby_lsp/listener"
|
27
|
+
require "ruby_lsp/document"
|
28
|
+
require "ruby_lsp/ruby_document"
|
27
29
|
require "ruby_lsp/store"
|
28
30
|
require "ruby_lsp/addon"
|
29
31
|
require "ruby_lsp/requests/support/rubocop_runner"
|
32
|
+
|
33
|
+
Bundler.ui.level = :silent
|
data/lib/ruby_lsp/listener.rb
CHANGED
@@ -14,10 +14,9 @@ module RubyLsp
|
|
14
14
|
|
15
15
|
abstract!
|
16
16
|
|
17
|
-
sig { params(dispatcher: Prism::Dispatcher
|
18
|
-
def initialize(dispatcher
|
17
|
+
sig { params(dispatcher: Prism::Dispatcher).void }
|
18
|
+
def initialize(dispatcher)
|
19
19
|
@dispatcher = dispatcher
|
20
|
-
@message_queue = message_queue
|
21
20
|
end
|
22
21
|
|
23
22
|
sig { returns(ResponseType) }
|
@@ -43,8 +42,8 @@ module RubyLsp
|
|
43
42
|
# When inheriting from ExtensibleListener, the `super` of constructor must be called **after** the subclass's own
|
44
43
|
# ivars have been initialized. This is because the constructor of ExtensibleListener calls
|
45
44
|
# `initialize_external_listener` which may depend on the subclass's ivars.
|
46
|
-
sig { params(dispatcher: Prism::Dispatcher
|
47
|
-
def initialize(dispatcher
|
45
|
+
sig { params(dispatcher: Prism::Dispatcher).void }
|
46
|
+
def initialize(dispatcher)
|
48
47
|
super
|
49
48
|
@response_merged = T.let(false, T::Boolean)
|
50
49
|
@external_listeners = T.let(
|
@@ -87,15 +87,19 @@ module RubyLsp
|
|
87
87
|
:start,
|
88
88
|
:line,
|
89
89
|
) && closest_node_loc.end_line - 1 >= source_range.dig(:end, :line)
|
90
|
-
|
91
|
-
target_line =
|
90
|
+
indentation_line_number = closest_node_loc.start_line - 1
|
91
|
+
target_line = indentation_line_number
|
92
92
|
else
|
93
93
|
target_line = closest_node_loc.end_line
|
94
|
-
|
94
|
+
indentation_line_number = closest_node_loc.end_line - 1
|
95
95
|
end
|
96
96
|
|
97
97
|
lines = @document.source.lines
|
98
|
-
|
98
|
+
|
99
|
+
indentation_line = lines[indentation_line_number]
|
100
|
+
return Error::InvalidTargetRange unless indentation_line
|
101
|
+
|
102
|
+
indentation = T.must(indentation_line[/\A */]).size
|
99
103
|
|
100
104
|
target_range = {
|
101
105
|
start: { line: target_line, character: indentation },
|
@@ -47,16 +47,18 @@ module RubyLsp
|
|
47
47
|
sig { override.returns(ResponseType) }
|
48
48
|
attr_reader :_response
|
49
49
|
|
50
|
-
sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher
|
51
|
-
def initialize(uri, dispatcher
|
50
|
+
sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher).void }
|
51
|
+
def initialize(uri, dispatcher)
|
52
52
|
@uri = T.let(uri, URI::Generic)
|
53
53
|
@_response = T.let([], ResponseType)
|
54
54
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
55
55
|
# visibility_stack is a stack of [current_visibility, previous_visibility]
|
56
56
|
@visibility_stack = T.let([[:public, :public]], T::Array[T::Array[T.nilable(Symbol)]])
|
57
57
|
@class_stack = T.let([], T::Array[String])
|
58
|
+
@group_id = T.let(1, Integer)
|
59
|
+
@group_id_stack = T.let([], T::Array[Integer])
|
58
60
|
|
59
|
-
super(dispatcher
|
61
|
+
super(dispatcher)
|
60
62
|
|
61
63
|
dispatcher.register(
|
62
64
|
self,
|
@@ -82,12 +84,16 @@ module RubyLsp
|
|
82
84
|
kind: :group,
|
83
85
|
)
|
84
86
|
end
|
87
|
+
|
88
|
+
@group_id_stack.push(@group_id)
|
89
|
+
@group_id += 1
|
85
90
|
end
|
86
91
|
|
87
92
|
sig { params(node: Prism::ClassNode).void }
|
88
93
|
def on_class_node_leave(node)
|
89
94
|
@visibility_stack.pop
|
90
95
|
@class_stack.pop
|
96
|
+
@group_id_stack.pop
|
91
97
|
end
|
92
98
|
|
93
99
|
sig { params(node: Prism::DefNode).void }
|
@@ -146,7 +152,7 @@ module RubyLsp
|
|
146
152
|
|
147
153
|
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
148
154
|
def initialize_external_listener(addon)
|
149
|
-
addon.create_code_lens_listener(@uri, @dispatcher
|
155
|
+
addon.create_code_lens_listener(@uri, @dispatcher)
|
150
156
|
end
|
151
157
|
|
152
158
|
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
@@ -174,12 +180,15 @@ module RubyLsp
|
|
174
180
|
},
|
175
181
|
]
|
176
182
|
|
183
|
+
grouping_data = { group_id: @group_id_stack.last, kind: kind }
|
184
|
+
grouping_data[:id] = @group_id if kind == :group
|
185
|
+
|
177
186
|
@_response << create_code_lens(
|
178
187
|
node,
|
179
188
|
title: "Run",
|
180
189
|
command_name: "rubyLsp.runTest",
|
181
190
|
arguments: arguments,
|
182
|
-
data: { type: "test",
|
191
|
+
data: { type: "test", **grouping_data },
|
183
192
|
)
|
184
193
|
|
185
194
|
@_response << create_code_lens(
|
@@ -187,7 +196,7 @@ module RubyLsp
|
|
187
196
|
title: "Run In Terminal",
|
188
197
|
command_name: "rubyLsp.runTestInTerminal",
|
189
198
|
arguments: arguments,
|
190
|
-
data: { type: "test_in_terminal",
|
199
|
+
data: { type: "test_in_terminal", **grouping_data },
|
191
200
|
)
|
192
201
|
|
193
202
|
@_response << create_code_lens(
|
@@ -195,7 +204,7 @@ module RubyLsp
|
|
195
204
|
title: "Debug",
|
196
205
|
command_name: "rubyLsp.debugTest",
|
197
206
|
arguments: arguments,
|
198
|
-
data: { type: "debug",
|
207
|
+
data: { type: "debug", **grouping_data },
|
199
208
|
)
|
200
209
|
end
|
201
210
|
|