ruby-lsp 0.23.1 → 0.23.5
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/VERSION +1 -1
- data/exe/ruby-lsp-launcher +18 -9
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +89 -58
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +21 -10
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/index_test.rb +2 -1
- data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
- data/lib/ruby_indexer/test/method_test.rb +86 -8
- data/lib/ruby_lsp/base_server.rb +11 -21
- data/lib/ruby_lsp/document.rb +62 -9
- data/lib/ruby_lsp/erb_document.rb +5 -3
- data/lib/ruby_lsp/global_state.rb +9 -0
- data/lib/ruby_lsp/listeners/definition.rb +7 -2
- data/lib/ruby_lsp/rbs_document.rb +2 -2
- data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -6
- data/lib/ruby_lsp/requests/completion.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +1 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +1 -1
- data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
- data/lib/ruby_lsp/requests/references.rb +1 -1
- data/lib/ruby_lsp/requests/rename.rb +3 -3
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
- data/lib/ruby_lsp/requests/signature_help.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +3 -3
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -13
- data/lib/ruby_lsp/requests/workspace_symbol.rb +2 -2
- data/lib/ruby_lsp/ruby_document.rb +75 -6
- data/lib/ruby_lsp/server.rb +69 -49
- data/lib/ruby_lsp/store.rb +7 -7
- data/lib/ruby_lsp/type_inferrer.rb +39 -17
- data/lib/ruby_lsp/utils.rb +43 -0
- metadata +3 -2
data/lib/ruby_lsp/server.rb
CHANGED
@@ -107,7 +107,7 @@ module RubyLsp
|
|
107
107
|
),
|
108
108
|
)
|
109
109
|
when "$/cancelRequest"
|
110
|
-
@
|
110
|
+
@global_state.synchronize { @cancelled_requests << message[:params][:id] }
|
111
111
|
when nil
|
112
112
|
process_response(message) if message[:result]
|
113
113
|
end
|
@@ -298,29 +298,11 @@ module RubyLsp
|
|
298
298
|
|
299
299
|
# Not every client supports dynamic registration or file watching
|
300
300
|
if @global_state.client_capabilities.supports_watching_files
|
301
|
-
send_message(
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
registrations: [
|
307
|
-
# Register watching Ruby files
|
308
|
-
Interface::Registration.new(
|
309
|
-
id: "workspace/didChangeWatchedFiles",
|
310
|
-
method: "workspace/didChangeWatchedFiles",
|
311
|
-
register_options: Interface::DidChangeWatchedFilesRegistrationOptions.new(
|
312
|
-
watchers: [
|
313
|
-
Interface::FileSystemWatcher.new(
|
314
|
-
glob_pattern: "**/*.rb",
|
315
|
-
kind: Constant::WatchKind::CREATE | Constant::WatchKind::CHANGE | Constant::WatchKind::DELETE,
|
316
|
-
),
|
317
|
-
],
|
318
|
-
),
|
319
|
-
),
|
320
|
-
],
|
321
|
-
),
|
322
|
-
),
|
323
|
-
)
|
301
|
+
send_message(Request.register_watched_files(@current_request_id, "**/*.rb"))
|
302
|
+
send_message(Request.register_watched_files(
|
303
|
+
@current_request_id,
|
304
|
+
Interface::RelativePattern.new(base_uri: @global_state.workspace_uri.to_s, pattern: ".rubocop.yml"),
|
305
|
+
))
|
324
306
|
end
|
325
307
|
|
326
308
|
process_indexing_configuration(options.dig(:initializationOptions, :indexing))
|
@@ -357,7 +339,7 @@ module RubyLsp
|
|
357
339
|
if defined?(Requests::Support::RuboCopFormatter)
|
358
340
|
begin
|
359
341
|
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
|
360
|
-
rescue RuboCop::Error => e
|
342
|
+
rescue ::RuboCop::Error => e
|
361
343
|
# The user may have provided unknown config switches in .rubocop or
|
362
344
|
# is trying to load a non-existent config file.
|
363
345
|
send_message(Notification.window_show_message(
|
@@ -377,7 +359,7 @@ module RubyLsp
|
|
377
359
|
|
378
360
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
379
361
|
def text_document_did_open(message)
|
380
|
-
@
|
362
|
+
@global_state.synchronize do
|
381
363
|
text_document = message.dig(:params, :textDocument)
|
382
364
|
language_id = case text_document[:languageId]
|
383
365
|
when "erb", "eruby"
|
@@ -392,7 +374,6 @@ module RubyLsp
|
|
392
374
|
uri: text_document[:uri],
|
393
375
|
source: text_document[:text],
|
394
376
|
version: text_document[:version],
|
395
|
-
encoding: @global_state.encoding,
|
396
377
|
language_id: language_id,
|
397
378
|
)
|
398
379
|
|
@@ -417,17 +398,12 @@ module RubyLsp
|
|
417
398
|
|
418
399
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
419
400
|
def text_document_did_close(message)
|
420
|
-
@
|
401
|
+
@global_state.synchronize do
|
421
402
|
uri = message.dig(:params, :textDocument, :uri)
|
422
403
|
@store.delete(uri)
|
423
404
|
|
424
405
|
# Clear diagnostics for the closed file, so that they no longer appear in the problems tab
|
425
|
-
send_message(
|
426
|
-
Notification.new(
|
427
|
-
method: "textDocument/publishDiagnostics",
|
428
|
-
params: Interface::PublishDiagnosticsParams.new(uri: uri.to_s, diagnostics: []),
|
429
|
-
),
|
430
|
-
)
|
406
|
+
send_message(Notification.publish_diagnostics(uri.to_s, []))
|
431
407
|
end
|
432
408
|
end
|
433
409
|
|
@@ -436,7 +412,7 @@ module RubyLsp
|
|
436
412
|
params = message[:params]
|
437
413
|
text_document = params[:textDocument]
|
438
414
|
|
439
|
-
@
|
415
|
+
@global_state.synchronize do
|
440
416
|
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
441
417
|
end
|
442
418
|
end
|
@@ -493,7 +469,22 @@ module RubyLsp
|
|
493
469
|
document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
|
494
470
|
code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
|
495
471
|
inlay_hint = Requests::InlayHints.new(document, T.must(@store.features_configuration.dig(:inlayHint)), dispatcher)
|
496
|
-
|
472
|
+
|
473
|
+
if document.is_a?(RubyDocument) && document.last_edit_may_change_declarations?
|
474
|
+
# Re-index the file as it is modified. This mode of indexing updates entries only. Require path trees are only
|
475
|
+
# updated on save
|
476
|
+
@global_state.synchronize do
|
477
|
+
send_log_message("Detected that last edit may have modified declarations. Re-indexing #{uri}")
|
478
|
+
|
479
|
+
@global_state.index.handle_change(uri) do |index|
|
480
|
+
index.delete(uri, skip_require_paths_tree: true)
|
481
|
+
RubyIndexer::DeclarationListener.new(index, dispatcher, parse_result, uri, collect_comments: true)
|
482
|
+
dispatcher.dispatch(parse_result.value)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
else
|
486
|
+
dispatcher.dispatch(parse_result.value)
|
487
|
+
end
|
497
488
|
|
498
489
|
# Store all responses retrieve in this round of visits in the cache and then return the response for the request
|
499
490
|
# we actually received
|
@@ -1011,26 +1002,55 @@ module RubyLsp
|
|
1011
1002
|
uri = URI(change[:uri])
|
1012
1003
|
file_path = uri.to_standardized_path
|
1013
1004
|
next if file_path.nil? || File.directory?(file_path)
|
1014
|
-
next unless file_path.end_with?(".rb")
|
1015
1005
|
|
1016
|
-
|
1017
|
-
|
1006
|
+
if file_path.end_with?(".rb")
|
1007
|
+
handle_ruby_file_change(index, file_path, change[:type])
|
1008
|
+
next
|
1009
|
+
end
|
1018
1010
|
|
1019
|
-
|
1011
|
+
file_name = File.basename(file_path)
|
1020
1012
|
|
1021
|
-
|
1022
|
-
|
1023
|
-
index.index_single(uri, content)
|
1024
|
-
when Constant::FileChangeType::CHANGED
|
1025
|
-
index.handle_change(uri, content)
|
1026
|
-
when Constant::FileChangeType::DELETED
|
1027
|
-
index.delete(uri)
|
1013
|
+
if file_name == ".rubocop.yml" || file_name == ".rubocop"
|
1014
|
+
handle_rubocop_config_change(uri)
|
1028
1015
|
end
|
1029
1016
|
end
|
1030
1017
|
|
1031
1018
|
Addon.file_watcher_addons.each { |addon| T.unsafe(addon).workspace_did_change_watched_files(changes) }
|
1032
1019
|
end
|
1033
1020
|
|
1021
|
+
sig { params(index: RubyIndexer::Index, file_path: String, change_type: Integer).void }
|
1022
|
+
def handle_ruby_file_change(index, file_path, change_type)
|
1023
|
+
load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
|
1024
|
+
uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
|
1025
|
+
|
1026
|
+
content = File.read(file_path)
|
1027
|
+
|
1028
|
+
case change_type
|
1029
|
+
when Constant::FileChangeType::CREATED
|
1030
|
+
index.index_single(uri, content)
|
1031
|
+
when Constant::FileChangeType::CHANGED
|
1032
|
+
index.handle_change(uri, content)
|
1033
|
+
when Constant::FileChangeType::DELETED
|
1034
|
+
index.delete(uri)
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
sig { params(uri: URI::Generic).void }
|
1039
|
+
def handle_rubocop_config_change(uri)
|
1040
|
+
return unless defined?(Requests::Support::RuboCopFormatter)
|
1041
|
+
|
1042
|
+
send_log_message("Reloading RuboCop since #{uri} changed")
|
1043
|
+
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
|
1044
|
+
|
1045
|
+
# Clear all existing diagnostics since the config changed. This has to happen under a mutex because the `state`
|
1046
|
+
# hash cannot be mutated during iteration or that will throw an error
|
1047
|
+
@global_state.synchronize do
|
1048
|
+
@store.each do |uri, _document|
|
1049
|
+
send_message(Notification.publish_diagnostics(uri.to_s, []))
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1034
1054
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
1035
1055
|
def workspace_symbol(message)
|
1036
1056
|
send_message(
|
@@ -1240,10 +1260,10 @@ module RubyLsp
|
|
1240
1260
|
return
|
1241
1261
|
end
|
1242
1262
|
|
1243
|
-
return unless indexing_options
|
1244
|
-
|
1245
1263
|
configuration = @global_state.index.configuration
|
1246
1264
|
configuration.workspace_path = @global_state.workspace_path
|
1265
|
+
return unless indexing_options
|
1266
|
+
|
1247
1267
|
# The index expects snake case configurations, but VS Code standardizes on camel case settings
|
1248
1268
|
configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
|
1249
1269
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -13,8 +13,9 @@ module RubyLsp
|
|
13
13
|
sig { returns(String) }
|
14
14
|
attr_accessor :client_name
|
15
15
|
|
16
|
-
sig { void }
|
17
|
-
def initialize
|
16
|
+
sig { params(global_state: GlobalState).void }
|
17
|
+
def initialize(global_state)
|
18
|
+
@global_state = global_state
|
18
19
|
@state = T.let({}, T::Hash[String, Document[T.untyped]])
|
19
20
|
@features_configuration = T.let(
|
20
21
|
{
|
@@ -61,17 +62,16 @@ module RubyLsp
|
|
61
62
|
source: String,
|
62
63
|
version: Integer,
|
63
64
|
language_id: Document::LanguageId,
|
64
|
-
encoding: Encoding,
|
65
65
|
).returns(Document[T.untyped])
|
66
66
|
end
|
67
|
-
def set(uri:, source:, version:, language_id
|
67
|
+
def set(uri:, source:, version:, language_id:)
|
68
68
|
@state[uri.to_s] = case language_id
|
69
69
|
when Document::LanguageId::ERB
|
70
|
-
ERBDocument.new(source: source, version: version, uri: uri,
|
70
|
+
ERBDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
71
71
|
when Document::LanguageId::RBS
|
72
|
-
RBSDocument.new(source: source, version: version, uri: uri,
|
72
|
+
RBSDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
73
73
|
else
|
74
|
-
RubyDocument.new(source: source, version: version, uri: uri,
|
74
|
+
RubyDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -91,29 +91,45 @@ module RubyLsp
|
|
91
91
|
return Type.new("#{last}::<Class:#{last}>") if parts.empty?
|
92
92
|
|
93
93
|
Type.new("#{parts.join("::")}::#{last}::<Class:#{last}>")
|
94
|
-
|
94
|
+
when Prism::CallNode
|
95
|
+
raw_receiver = receiver.message
|
95
96
|
|
96
|
-
raw_receiver
|
97
|
-
receiver
|
98
|
-
|
99
|
-
receiver
|
100
|
-
end
|
97
|
+
if raw_receiver == "new"
|
98
|
+
# When invoking `new`, we recursively infer the type of the receiver to get the class type its being invoked
|
99
|
+
# on and then return the attached version of that type, since it's being instantiated.
|
100
|
+
type = infer_receiver_for_call_node(receiver, node_context)
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
name = entries&.first&.name
|
112
|
-
GuessedType.new(name) if name
|
102
|
+
return unless type
|
103
|
+
|
104
|
+
# If the method `new` was overridden, then we cannot assume that it will return a new instance of the class
|
105
|
+
new_method = @index.resolve_method("new", type.name)&.first
|
106
|
+
return if new_method && new_method.owner&.name != "Class"
|
107
|
+
|
108
|
+
type.attached
|
109
|
+
elsif raw_receiver
|
110
|
+
guess_type(raw_receiver, node_context.nesting)
|
113
111
|
end
|
112
|
+
else
|
113
|
+
guess_type(receiver.slice, node_context.nesting)
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
+
sig { params(raw_receiver: String, nesting: T::Array[String]).returns(T.nilable(GuessedType)) }
|
118
|
+
def guess_type(raw_receiver, nesting)
|
119
|
+
guessed_name = raw_receiver
|
120
|
+
.delete_prefix("@")
|
121
|
+
.delete_prefix("@@")
|
122
|
+
.split("_")
|
123
|
+
.map(&:capitalize)
|
124
|
+
.join
|
125
|
+
|
126
|
+
entries = @index.resolve(guessed_name, nesting) || @index.first_unqualified_const(guessed_name)
|
127
|
+
name = entries&.first&.name
|
128
|
+
return unless name
|
129
|
+
|
130
|
+
GuessedType.new(name)
|
131
|
+
end
|
132
|
+
|
117
133
|
sig { params(node_context: NodeContext).returns(Type) }
|
118
134
|
def self_receiver_handling(node_context)
|
119
135
|
nesting = node_context.nesting
|
@@ -176,6 +192,12 @@ module RubyLsp
|
|
176
192
|
def initialize(name)
|
177
193
|
@name = name
|
178
194
|
end
|
195
|
+
|
196
|
+
# Returns the attached version of this type by removing the `<Class:...>` part from its name
|
197
|
+
sig { returns(Type) }
|
198
|
+
def attached
|
199
|
+
Type.new(T.must(@name.split("::")[..-2]).join("::"))
|
200
|
+
end
|
179
201
|
end
|
180
202
|
|
181
203
|
# A type that was guessed based on the receiver raw name
|
data/lib/ruby_lsp/utils.rb
CHANGED
@@ -142,6 +142,20 @@ module RubyLsp
|
|
142
142
|
),
|
143
143
|
)
|
144
144
|
end
|
145
|
+
|
146
|
+
sig do
|
147
|
+
params(
|
148
|
+
uri: String,
|
149
|
+
diagnostics: T::Array[Interface::Diagnostic],
|
150
|
+
version: T.nilable(Integer),
|
151
|
+
).returns(Notification)
|
152
|
+
end
|
153
|
+
def publish_diagnostics(uri, diagnostics, version: nil)
|
154
|
+
new(
|
155
|
+
method: "textDocument/publishDiagnostics",
|
156
|
+
params: Interface::PublishDiagnosticsParams.new(uri: uri, diagnostics: diagnostics, version: version),
|
157
|
+
)
|
158
|
+
end
|
145
159
|
end
|
146
160
|
|
147
161
|
extend T::Sig
|
@@ -155,6 +169,35 @@ module RubyLsp
|
|
155
169
|
class Request < Message
|
156
170
|
extend T::Sig
|
157
171
|
|
172
|
+
class << self
|
173
|
+
extend T::Sig
|
174
|
+
|
175
|
+
sig { params(id: Integer, pattern: T.any(Interface::RelativePattern, String), kind: Integer).returns(Request) }
|
176
|
+
def register_watched_files(
|
177
|
+
id,
|
178
|
+
pattern,
|
179
|
+
kind: Constant::WatchKind::CREATE | Constant::WatchKind::CHANGE | Constant::WatchKind::DELETE
|
180
|
+
)
|
181
|
+
new(
|
182
|
+
id: id,
|
183
|
+
method: "client/registerCapability",
|
184
|
+
params: Interface::RegistrationParams.new(
|
185
|
+
registrations: [
|
186
|
+
Interface::Registration.new(
|
187
|
+
id: "workspace/didChangeWatchedFiles",
|
188
|
+
method: "workspace/didChangeWatchedFiles",
|
189
|
+
register_options: Interface::DidChangeWatchedFilesRegistrationOptions.new(
|
190
|
+
watchers: [
|
191
|
+
Interface::FileSystemWatcher.new(glob_pattern: pattern, kind: kind),
|
192
|
+
],
|
193
|
+
),
|
194
|
+
),
|
195
|
+
],
|
196
|
+
),
|
197
|
+
)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
158
201
|
sig { params(id: T.any(Integer, String), method: String, params: Object).void }
|
159
202
|
def initialize(id:, method:, params:)
|
160
203
|
@id = id
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.23.
|
4
|
+
version: 0.23.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-01-
|
10
|
+
date: 2025-01-10 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: language_server-protocol
|
@@ -106,6 +106,7 @@ files:
|
|
106
106
|
- lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
|
107
107
|
- lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb
|
108
108
|
- lib/ruby_indexer/lib/ruby_indexer/uri.rb
|
109
|
+
- lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb
|
109
110
|
- lib/ruby_indexer/ruby_indexer.rb
|
110
111
|
- lib/ruby_indexer/test/class_variables_test.rb
|
111
112
|
- lib/ruby_indexer/test/classes_and_modules_test.rb
|