ruby-lsp 0.12.2 → 0.13.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 +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
|
|