ruby-lsp 0.27.0.beta1 → 0.27.0.beta3
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 +0 -46
- data/exe/ruby-lsp-check +0 -15
- data/lib/ruby_lsp/addon.rb +19 -19
- data/lib/ruby_lsp/global_state.rb +1 -6
- data/lib/ruby_lsp/internal.rb +3 -2
- data/lib/ruby_lsp/listeners/code_lens.rb +1 -1
- data/lib/ruby_lsp/listeners/completion.rb +246 -382
- data/lib/ruby_lsp/listeners/definition.rb +7 -10
- data/lib/ruby_lsp/listeners/document_link.rb +4 -0
- data/lib/ruby_lsp/listeners/hover.rb +234 -82
- data/lib/ruby_lsp/listeners/signature_help.rb +11 -12
- data/lib/ruby_lsp/listeners/spec_style.rb +6 -1
- data/lib/ruby_lsp/listeners/test_discovery.rb +38 -15
- data/lib/ruby_lsp/listeners/test_style.rb +21 -9
- data/lib/ruby_lsp/node_context.rb +31 -8
- data/lib/ruby_lsp/requests/completion_resolve.rb +55 -39
- data/lib/ruby_lsp/requests/discover_tests.rb +5 -41
- data/lib/ruby_lsp/requests/hover.rb +2 -5
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +66 -22
- data/lib/ruby_lsp/requests/references.rb +180 -66
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/request.rb +3 -33
- data/lib/ruby_lsp/requests/support/common.rb +82 -68
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +82 -46
- data/lib/ruby_lsp/ruby_document.rb +0 -73
- data/lib/ruby_lsp/rubydex/declaration.rb +174 -0
- data/lib/ruby_lsp/rubydex/definition.rb +73 -0
- data/lib/ruby_lsp/rubydex/reference.rb +6 -1
- data/lib/ruby_lsp/rubydex/signature.rb +107 -0
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +56 -171
- data/lib/ruby_lsp/test_helper.rb +0 -1
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +89 -11
- metadata +12 -18
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +0 -276
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +0 -1101
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +0 -44
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +0 -605
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +0 -1077
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +0 -37
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +0 -149
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +0 -294
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +0 -335
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +0 -32
- data/lib/ruby_indexer/ruby_indexer.rb +0 -20
- data/lib/ruby_lsp/static_docs.rb +0 -20
- data/static_docs/break.md +0 -103
- data/static_docs/yield.md +0 -81
- /data/lib/{ruby_indexer/lib/ruby_indexer → ruby_lsp}/uri.rb +0 -0
data/lib/ruby_lsp/server.rb
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
class Server < BaseServer
|
|
6
|
-
NON_REPORTABLE_SETUP_ERRORS = [
|
|
6
|
+
NON_REPORTABLE_SETUP_ERRORS = [
|
|
7
|
+
Bundler::GemNotFound,
|
|
8
|
+
Bundler::GitError,
|
|
9
|
+
Bundler::Dsl::DSLError,
|
|
10
|
+
].freeze #: Array[singleton(StandardError)]
|
|
7
11
|
|
|
8
12
|
# Only for testing
|
|
9
13
|
#: GlobalState
|
|
@@ -120,6 +124,16 @@ module RubyLsp
|
|
|
120
124
|
@global_state.synchronize { @cancelled_requests << message[:params][:id] }
|
|
121
125
|
when nil
|
|
122
126
|
process_response(message) if message[:result]
|
|
127
|
+
else
|
|
128
|
+
id = message[:id]
|
|
129
|
+
|
|
130
|
+
if id
|
|
131
|
+
send_message(Error.new(
|
|
132
|
+
id: id,
|
|
133
|
+
code: Constant::ErrorCodes::METHOD_NOT_FOUND,
|
|
134
|
+
message: "Method not found: #{message[:method]}",
|
|
135
|
+
))
|
|
136
|
+
end
|
|
123
137
|
end
|
|
124
138
|
rescue DelegateRequestError
|
|
125
139
|
send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
|
|
@@ -311,8 +325,6 @@ module RubyLsp
|
|
|
311
325
|
))
|
|
312
326
|
end
|
|
313
327
|
|
|
314
|
-
process_indexing_configuration(options.dig(:initializationOptions, :indexing))
|
|
315
|
-
|
|
316
328
|
begin_progress("indexing-progress", "Ruby LSP: indexing files")
|
|
317
329
|
|
|
318
330
|
global_state_notifications.each { |notification| send_message(notification) }
|
|
@@ -422,8 +434,11 @@ module RubyLsp
|
|
|
422
434
|
|
|
423
435
|
if [:ruby, :rbs].include?(language_id)
|
|
424
436
|
graph = @global_state.graph
|
|
425
|
-
|
|
426
|
-
|
|
437
|
+
|
|
438
|
+
benchmark("index_source") do
|
|
439
|
+
graph.index_source(text_document[:uri].to_s, document.source, language_id.to_s)
|
|
440
|
+
end
|
|
441
|
+
benchmark("incremental_resolve") { graph.resolve }
|
|
427
442
|
end
|
|
428
443
|
end
|
|
429
444
|
|
|
@@ -482,39 +497,15 @@ module RubyLsp
|
|
|
482
497
|
document,
|
|
483
498
|
dispatcher,
|
|
484
499
|
)
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
# the dispatcher's state
|
|
488
|
-
code_lens = nil #: Requests::CodeLens?
|
|
489
|
-
|
|
490
|
-
if document.is_a?(RubyDocument) && document.should_index?
|
|
491
|
-
# Re-index the file as it is modified. This mode of indexing updates entries only. Require path trees are only
|
|
492
|
-
# updated on save
|
|
493
|
-
@global_state.synchronize do
|
|
494
|
-
send_log_message("Determined that document should be indexed: #{uri}")
|
|
495
|
-
|
|
496
|
-
@global_state.index.handle_change(uri) do |index|
|
|
497
|
-
index.delete(uri, skip_require_paths_tree: true)
|
|
498
|
-
RubyIndexer::DeclarationListener.new(index, dispatcher, parse_result, uri, collect_comments: true)
|
|
499
|
-
code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
|
|
500
|
-
dispatcher.dispatch(document.ast)
|
|
501
|
-
end
|
|
502
|
-
end
|
|
503
|
-
else
|
|
504
|
-
code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
|
|
505
|
-
dispatcher.dispatch(document.ast)
|
|
506
|
-
end
|
|
500
|
+
code_lens = Requests::CodeLens.new(@global_state, document, dispatcher)
|
|
501
|
+
dispatcher.dispatch(document.ast)
|
|
507
502
|
|
|
508
503
|
# Store all responses retrieve in this round of visits in the cache and then return the response for the request
|
|
509
504
|
# we actually received
|
|
510
505
|
document.cache_set("textDocument/foldingRange", folding_range.perform)
|
|
511
506
|
document.cache_set("textDocument/documentSymbol", document_symbol.perform)
|
|
512
507
|
document.cache_set("textDocument/documentLink", document_link.perform)
|
|
513
|
-
document.cache_set(
|
|
514
|
-
"textDocument/codeLens",
|
|
515
|
-
code_lens #: as !nil
|
|
516
|
-
.perform,
|
|
517
|
-
)
|
|
508
|
+
document.cache_set("textDocument/codeLens", code_lens.perform)
|
|
518
509
|
document.cache_set("textDocument/inlayHint", inlay_hint.perform)
|
|
519
510
|
|
|
520
511
|
send_message(Result.new(id: message[:id], response: document.cache_get(message[:method])))
|
|
@@ -1021,21 +1012,6 @@ module RubyLsp
|
|
|
1021
1012
|
|
|
1022
1013
|
#: (Hash[Symbol, untyped] message) -> void
|
|
1023
1014
|
def workspace_did_change_watched_files(message)
|
|
1024
|
-
# If indexing is not complete yet, delay processing did change watched file notifications. We need initial
|
|
1025
|
-
# indexing to be in place so that we can handle file changes appropriately without risking duplicates. We also
|
|
1026
|
-
# have to sleep before re-inserting the notification in the queue otherwise the worker can get stuck in its own
|
|
1027
|
-
# loop of pushing and popping the same notification
|
|
1028
|
-
unless @global_state.index.initial_indexing_completed
|
|
1029
|
-
Thread.new do
|
|
1030
|
-
sleep(2)
|
|
1031
|
-
# We have to ensure that the queue is not closed yet, since nothing stops the user from saving a file and then
|
|
1032
|
-
# immediately telling the LSP to shutdown
|
|
1033
|
-
@incoming_queue << message unless @incoming_queue.closed?
|
|
1034
|
-
end
|
|
1035
|
-
|
|
1036
|
-
return
|
|
1037
|
-
end
|
|
1038
|
-
|
|
1039
1015
|
changes = message.dig(:params, :changes)
|
|
1040
1016
|
# We allow add-ons to register for watching files and we have no restrictions for what they register for. If the
|
|
1041
1017
|
# same pattern is registered more than once, the LSP will receive duplicate change notifications. Receiving them
|
|
@@ -1056,21 +1032,15 @@ module RubyLsp
|
|
|
1056
1032
|
acc << path
|
|
1057
1033
|
end
|
|
1058
1034
|
end
|
|
1059
|
-
graph.index_all(additions_and_changes)
|
|
1060
|
-
graph.resolve
|
|
1035
|
+
benchmark("index_all") { graph.index_all(additions_and_changes) }
|
|
1036
|
+
benchmark("incremental_resolve") { graph.resolve }
|
|
1061
1037
|
|
|
1062
|
-
index = @global_state.index
|
|
1063
1038
|
changes.each do |change|
|
|
1064
1039
|
# File change events include folders, but we're only interested in files
|
|
1065
1040
|
uri = URI(change[:uri])
|
|
1066
1041
|
file_path = uri.to_standardized_path
|
|
1067
1042
|
next if file_path.nil? || File.directory?(file_path)
|
|
1068
1043
|
|
|
1069
|
-
if file_path.end_with?(".rb")
|
|
1070
|
-
handle_ruby_file_change(index, file_path, change[:type])
|
|
1071
|
-
next
|
|
1072
|
-
end
|
|
1073
|
-
|
|
1074
1044
|
file_name = File.basename(file_path)
|
|
1075
1045
|
|
|
1076
1046
|
if file_name == ".rubocop.yml" || file_name == ".rubocop" || file_name == ".rubocop_todo.yml"
|
|
@@ -1089,33 +1059,6 @@ module RubyLsp
|
|
|
1089
1059
|
end
|
|
1090
1060
|
end
|
|
1091
1061
|
|
|
1092
|
-
#: (RubyIndexer::Index index, String file_path, Integer change_type) -> void
|
|
1093
|
-
def handle_ruby_file_change(index, file_path, change_type)
|
|
1094
|
-
@global_state.synchronize do
|
|
1095
|
-
load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
|
|
1096
|
-
uri = URI::Generic.from_path(load_path_entry: load_path_entry, path: file_path)
|
|
1097
|
-
|
|
1098
|
-
case change_type
|
|
1099
|
-
when Constant::FileChangeType::CREATED
|
|
1100
|
-
content = File.read(file_path)
|
|
1101
|
-
# If we receive a late created notification for a file that has already been claimed by the client, we want to
|
|
1102
|
-
# handle change for that URI so that the require path tree is updated
|
|
1103
|
-
@store.key?(uri) ? index.handle_change(uri, content) : index.index_single(uri, content)
|
|
1104
|
-
when Constant::FileChangeType::CHANGED
|
|
1105
|
-
content = File.read(file_path)
|
|
1106
|
-
# We only handle changes on file watched notifications if the client is not the one managing this URI.
|
|
1107
|
-
# Otherwise, these changes are handled when running the combined requests
|
|
1108
|
-
index.handle_change(uri, content) unless @store.key?(uri)
|
|
1109
|
-
when Constant::FileChangeType::DELETED
|
|
1110
|
-
index.delete(uri)
|
|
1111
|
-
end
|
|
1112
|
-
rescue Errno::ENOENT
|
|
1113
|
-
# If a file is created and then delete immediately afterwards, we will process the created notification before
|
|
1114
|
-
# we receive the deleted one, but the file no longer exists. This may happen when running a test suite that
|
|
1115
|
-
# creates and deletes files automatically.
|
|
1116
|
-
end
|
|
1117
|
-
end
|
|
1118
|
-
|
|
1119
1062
|
#: (URI::Generic uri) -> void
|
|
1120
1063
|
def handle_rubocop_config_change(uri)
|
|
1121
1064
|
return unless defined?(Requests::Support::RuboCopFormatter)
|
|
@@ -1195,7 +1138,7 @@ module RubyLsp
|
|
|
1195
1138
|
|
|
1196
1139
|
response = Requests::PrepareTypeHierarchy.new(
|
|
1197
1140
|
document,
|
|
1198
|
-
@global_state
|
|
1141
|
+
@global_state,
|
|
1199
1142
|
params[:position],
|
|
1200
1143
|
).perform
|
|
1201
1144
|
|
|
@@ -1205,7 +1148,7 @@ module RubyLsp
|
|
|
1205
1148
|
#: (Hash[Symbol, untyped] message) -> void
|
|
1206
1149
|
def type_hierarchy_supertypes(message)
|
|
1207
1150
|
response = Requests::TypeHierarchySupertypes.new(
|
|
1208
|
-
@global_state
|
|
1151
|
+
@global_state,
|
|
1209
1152
|
message.dig(:params, :item),
|
|
1210
1153
|
).perform
|
|
1211
1154
|
send_message(Result.new(id: message[:id], response: response))
|
|
@@ -1254,56 +1197,16 @@ module RubyLsp
|
|
|
1254
1197
|
|
|
1255
1198
|
#: -> void
|
|
1256
1199
|
def perform_initial_indexing
|
|
1200
|
+
# Index
|
|
1257
1201
|
progress("indexing-progress", message: "Indexing workspace...")
|
|
1258
|
-
@global_state.graph.index_workspace
|
|
1202
|
+
benchmark("index_workspace") { @global_state.graph.index_workspace }
|
|
1259
1203
|
|
|
1204
|
+
# Resolve
|
|
1260
1205
|
progress("indexing-progress", message: "Resolving graph...")
|
|
1261
|
-
@global_state.graph.resolve
|
|
1262
|
-
|
|
1263
|
-
# The begin progress invocation happens during `initialize`, so that the notification is sent before we are
|
|
1264
|
-
# stuck indexing files
|
|
1265
|
-
Thread.new do
|
|
1266
|
-
begin
|
|
1267
|
-
@global_state.index.index_all do |percentage|
|
|
1268
|
-
progress("indexing-progress", percentage: percentage)
|
|
1269
|
-
true
|
|
1270
|
-
rescue ClosedQueueError
|
|
1271
|
-
# Since we run indexing on a separate thread, it's possible to kill the server before indexing is complete.
|
|
1272
|
-
# In those cases, the message queue will be closed and raise a ClosedQueueError. By returning `false`, we
|
|
1273
|
-
# tell the index to stop working immediately
|
|
1274
|
-
false
|
|
1275
|
-
end
|
|
1276
|
-
rescue StandardError => error
|
|
1277
|
-
message = "Error while indexing (see [troubleshooting steps]" \
|
|
1278
|
-
"(https://shopify.github.io/ruby-lsp/troubleshooting#indexing)): #{error.message}"
|
|
1279
|
-
send_message(Notification.window_show_message(message, type: Constant::MessageType::ERROR))
|
|
1280
|
-
end
|
|
1281
|
-
|
|
1282
|
-
# Indexing produces a high number of short lived object allocations. That might lead to some fragmentation and
|
|
1283
|
-
# an unnecessarily expanded heap. Compacting ensures that the heap is as small as possible and that future
|
|
1284
|
-
# allocations and garbage collections are faster
|
|
1285
|
-
GC.compact unless @test_mode
|
|
1286
|
-
|
|
1287
|
-
@global_state.synchronize do
|
|
1288
|
-
# If we linearize ancestors while the index is not fully populated, we may end up caching incorrect results
|
|
1289
|
-
# that were missing namespaces. After indexing is complete, we need to clear the ancestors cache and start
|
|
1290
|
-
# again
|
|
1291
|
-
@global_state.index.clear_ancestors
|
|
1292
|
-
|
|
1293
|
-
# The results for code lens depend on ancestor linearization, so we need to clear any previously computed
|
|
1294
|
-
# responses
|
|
1295
|
-
@store.each { |_uri, document| document.clear_cache("textDocument/codeLens") }
|
|
1296
|
-
end
|
|
1297
|
-
|
|
1298
|
-
# Always end the progress notification even if indexing failed or else it never goes away and the user has no
|
|
1299
|
-
# way of dismissing it
|
|
1300
|
-
end_progress("indexing-progress")
|
|
1206
|
+
benchmark("full_resolve") { @global_state.graph.resolve }
|
|
1301
1207
|
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
send_message(Request.new(id: @current_request_id, method: "workspace/codeLens/refresh", params: nil))
|
|
1305
|
-
end
|
|
1306
|
-
end
|
|
1208
|
+
# End
|
|
1209
|
+
end_progress("indexing-progress")
|
|
1307
1210
|
end
|
|
1308
1211
|
|
|
1309
1212
|
#: (String id, String title, ?percentage: Integer) -> void
|
|
@@ -1356,47 +1259,6 @@ module RubyLsp
|
|
|
1356
1259
|
end
|
|
1357
1260
|
end
|
|
1358
1261
|
|
|
1359
|
-
#: (Hash[Symbol, untyped]? indexing_options) -> void
|
|
1360
|
-
def process_indexing_configuration(indexing_options)
|
|
1361
|
-
# Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
|
|
1362
|
-
index_path = File.join(@global_state.workspace_path, ".index.yml")
|
|
1363
|
-
|
|
1364
|
-
if File.exist?(index_path)
|
|
1365
|
-
begin
|
|
1366
|
-
@global_state.index.configuration.apply_config(YAML.parse_file(index_path).to_ruby)
|
|
1367
|
-
send_message(
|
|
1368
|
-
Notification.new(
|
|
1369
|
-
method: "window/showMessage",
|
|
1370
|
-
params: Interface::ShowMessageParams.new(
|
|
1371
|
-
type: Constant::MessageType::WARNING,
|
|
1372
|
-
message: "The .index.yml configuration file is deprecated. " \
|
|
1373
|
-
"Please use editor settings to configure the index",
|
|
1374
|
-
),
|
|
1375
|
-
),
|
|
1376
|
-
)
|
|
1377
|
-
rescue Psych::SyntaxError => e
|
|
1378
|
-
message = "Syntax error while loading configuration: #{e.message}"
|
|
1379
|
-
send_message(
|
|
1380
|
-
Notification.new(
|
|
1381
|
-
method: "window/showMessage",
|
|
1382
|
-
params: Interface::ShowMessageParams.new(
|
|
1383
|
-
type: Constant::MessageType::WARNING,
|
|
1384
|
-
message: message,
|
|
1385
|
-
),
|
|
1386
|
-
),
|
|
1387
|
-
)
|
|
1388
|
-
end
|
|
1389
|
-
return
|
|
1390
|
-
end
|
|
1391
|
-
|
|
1392
|
-
configuration = @global_state.index.configuration
|
|
1393
|
-
configuration.workspace_path = @global_state.workspace_path
|
|
1394
|
-
return unless indexing_options
|
|
1395
|
-
|
|
1396
|
-
# The index expects snake case configurations, but VS Code standardizes on camel case settings
|
|
1397
|
-
configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
|
|
1398
|
-
end
|
|
1399
|
-
|
|
1400
1262
|
#: (Hash[Symbol, untyped] message) -> void
|
|
1401
1263
|
def window_show_message_request(message)
|
|
1402
1264
|
result = message[:result]
|
|
@@ -1558,5 +1420,28 @@ module RubyLsp
|
|
|
1558
1420
|
response: code_lens,
|
|
1559
1421
|
))
|
|
1560
1422
|
end
|
|
1423
|
+
|
|
1424
|
+
#: [T] (String) { () -> T } -> T
|
|
1425
|
+
def benchmark(label, &block)
|
|
1426
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
|
1427
|
+
result = block.call
|
|
1428
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start
|
|
1429
|
+
|
|
1430
|
+
send_message(Notification.telemetry({
|
|
1431
|
+
type: "data",
|
|
1432
|
+
eventName: "ruby_lsp.response_time",
|
|
1433
|
+
data: {
|
|
1434
|
+
type: "histogram",
|
|
1435
|
+
value: duration,
|
|
1436
|
+
attributes: {
|
|
1437
|
+
message: label,
|
|
1438
|
+
lspVersion: RubyLsp::VERSION,
|
|
1439
|
+
rubyVersion: RUBY_VERSION,
|
|
1440
|
+
},
|
|
1441
|
+
},
|
|
1442
|
+
}))
|
|
1443
|
+
|
|
1444
|
+
result
|
|
1445
|
+
end
|
|
1561
1446
|
end
|
|
1562
1447
|
end
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
|
@@ -19,7 +19,7 @@ module RubyLsp
|
|
|
19
19
|
infer_receiver_for_call_node(node, node_context)
|
|
20
20
|
when Prism::InstanceVariableReadNode, Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableWriteNode,
|
|
21
21
|
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableTargetNode,
|
|
22
|
-
Prism::SuperNode, Prism::ForwardingSuperNode
|
|
22
|
+
Prism::SuperNode, Prism::ForwardingSuperNode, Prism::DefNode
|
|
23
23
|
self_receiver_handling(node_context)
|
|
24
24
|
when Prism::ClassVariableAndWriteNode, Prism::ClassVariableWriteNode, Prism::ClassVariableOperatorWriteNode,
|
|
25
25
|
Prism::ClassVariableOrWriteNode, Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
|
|
@@ -78,7 +78,7 @@ module RubyLsp
|
|
|
78
78
|
# When the receiver is a constant reference, we have to try to resolve it to figure out the right
|
|
79
79
|
# receiver. But since the invocation is directly on the constant, that's the singleton context of that
|
|
80
80
|
# class/module
|
|
81
|
-
receiver_name =
|
|
81
|
+
receiver_name = Requests::Support::Common.constant_name(receiver)
|
|
82
82
|
return unless receiver_name
|
|
83
83
|
|
|
84
84
|
resolved_receiver = @graph.resolve_constant(receiver_name, node_context.nesting)
|
|
@@ -124,26 +124,104 @@ module RubyLsp
|
|
|
124
124
|
|
|
125
125
|
declaration = @graph.resolve_constant(guessed_name, nesting)
|
|
126
126
|
declaration ||= @graph.search(guessed_name).first
|
|
127
|
-
return unless declaration
|
|
127
|
+
return unless declaration.is_a?(Rubydex::Namespace)
|
|
128
128
|
|
|
129
129
|
GuessedType.new(declaration.name)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
#: (NodeContext node_context) -> Type
|
|
132
|
+
#: (NodeContext node_context) -> Type?
|
|
133
133
|
def self_receiver_handling(node_context)
|
|
134
134
|
nesting = node_context.nesting
|
|
135
135
|
# If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
|
|
136
136
|
# inherits from Object
|
|
137
137
|
return Type.new("Object") if nesting.empty?
|
|
138
|
-
|
|
138
|
+
|
|
139
|
+
surrounding_method = node_context.surrounding_method
|
|
140
|
+
|
|
141
|
+
if surrounding_method
|
|
142
|
+
receiver_name = surrounding_method.receiver
|
|
143
|
+
|
|
144
|
+
case receiver_name
|
|
145
|
+
when "self"
|
|
146
|
+
# `def self.foo` — self is the singleton of the enclosing class/module
|
|
147
|
+
return resolve_singleton_type_from_nesting(nesting)
|
|
148
|
+
when "none"
|
|
149
|
+
# Instance method — self is an instance of the enclosing class/module
|
|
150
|
+
return resolve_type_from_nesting(nesting)
|
|
151
|
+
when nil
|
|
152
|
+
# Dynamic receiver that we cannot handle
|
|
153
|
+
return
|
|
154
|
+
else
|
|
155
|
+
# Explicit constant receiver (e.g. `def Bar.baz`) — self is that constant's singleton class
|
|
156
|
+
resolved = resolve_receiver_singleton_type(receiver_name, nesting)
|
|
157
|
+
return resolved if resolved
|
|
158
|
+
|
|
159
|
+
return resolve_type_from_nesting(nesting)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
139
162
|
|
|
140
163
|
# If we're not inside a method, then we're inside the body of a class or module, which is a singleton
|
|
141
|
-
# context.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
# context. Resolve through the graph to get the correct fully qualified name
|
|
165
|
+
resolve_singleton_type_from_nesting(nesting)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Resolves the fully qualified name of the innermost constant from the nesting and returns it as a type.
|
|
169
|
+
# For instance methods, the nesting won't have singleton markers, so the result is an instance type.
|
|
170
|
+
# For `def self.` methods, the nesting includes a singleton marker, which is preserved in the result.
|
|
171
|
+
#: (Array[String] nesting) -> Type
|
|
172
|
+
def resolve_type_from_nesting(nesting)
|
|
173
|
+
resolved_name = resolve_nesting_fully_qualified_name(nesting)
|
|
174
|
+
Type.new(resolved_name)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Resolves the nesting and returns a singleton type (appends `::<Last>`)
|
|
178
|
+
#: (Array[String] nesting) -> Type
|
|
179
|
+
def resolve_singleton_type_from_nesting(nesting)
|
|
180
|
+
resolved_name = resolve_nesting_fully_qualified_name(nesting)
|
|
181
|
+
last_part = resolved_name.split("::").last #: as !nil
|
|
182
|
+
Type.new("#{resolved_name}::<#{last_part}>")
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Resolves the innermost constant in the nesting through the graph, handling compact-path definitions
|
|
186
|
+
# like `class Bar::Baz` inside a different module where the lexical nesting doesn't reflect the true
|
|
187
|
+
# constant hierarchy. Falls back to lexical joining if resolution fails.
|
|
188
|
+
#: (Array[String] nesting) -> String
|
|
189
|
+
def resolve_nesting_fully_qualified_name(nesting)
|
|
190
|
+
nesting_parts = nesting.dup
|
|
191
|
+
trailing_singletons = [] #: Array[String]
|
|
192
|
+
|
|
193
|
+
nesting_parts.reverse_each do |part|
|
|
194
|
+
break unless part.start_with?("<")
|
|
195
|
+
|
|
196
|
+
popped = nesting_parts.pop #: as !nil
|
|
197
|
+
trailing_singletons.unshift(popped)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
if nesting_parts.any?
|
|
201
|
+
resolved = @graph.resolve_constant(
|
|
202
|
+
nesting_parts.last, #: as !nil
|
|
203
|
+
nesting_parts[0...-1], #: as !nil
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if resolved
|
|
207
|
+
parts = resolved.name.split("::") + trailing_singletons
|
|
208
|
+
return parts.join("::")
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Fallback to lexical joining if resolution fails
|
|
213
|
+
nesting.flat_map { |part| part.split("::") }.join("::")
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
#: (String, Array[String]) -> Type?
|
|
217
|
+
def resolve_receiver_singleton_type(receiver_name, nesting)
|
|
218
|
+
receiver_declaration = @graph.resolve_constant(receiver_name, nesting)
|
|
219
|
+
return unless receiver_declaration.is_a?(Rubydex::Namespace)
|
|
220
|
+
|
|
221
|
+
singleton = receiver_declaration.singleton_class
|
|
222
|
+
return unless singleton
|
|
223
|
+
|
|
224
|
+
Type.new(singleton.name)
|
|
147
225
|
end
|
|
148
226
|
|
|
149
227
|
#: (NodeContext node_context) -> Type?
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-lsp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.27.0.
|
|
4
|
+
version: 0.27.0.beta3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
@@ -69,14 +69,20 @@ dependencies:
|
|
|
69
69
|
requirements:
|
|
70
70
|
- - "~>"
|
|
71
71
|
- !ruby/object:Gem::Version
|
|
72
|
-
version: 0.
|
|
72
|
+
version: 0.2.0
|
|
73
|
+
- - "<"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 0.3.0
|
|
73
76
|
type: :runtime
|
|
74
77
|
prerelease: false
|
|
75
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
76
79
|
requirements:
|
|
77
80
|
- - "~>"
|
|
78
81
|
- !ruby/object:Gem::Version
|
|
79
|
-
version: 0.
|
|
82
|
+
version: 0.2.0
|
|
83
|
+
- - "<"
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: 0.3.0
|
|
80
86
|
description: An opinionated language server for Ruby
|
|
81
87
|
email:
|
|
82
88
|
- ruby@shopify.com
|
|
@@ -98,18 +104,6 @@ files:
|
|
|
98
104
|
- lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
|
|
99
105
|
- lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
|
|
100
106
|
- lib/ruby-lsp.rb
|
|
101
|
-
- lib/ruby_indexer/lib/ruby_indexer/configuration.rb
|
|
102
|
-
- lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb
|
|
103
|
-
- lib/ruby_indexer/lib/ruby_indexer/enhancement.rb
|
|
104
|
-
- lib/ruby_indexer/lib/ruby_indexer/entry.rb
|
|
105
|
-
- lib/ruby_indexer/lib/ruby_indexer/index.rb
|
|
106
|
-
- lib/ruby_indexer/lib/ruby_indexer/location.rb
|
|
107
|
-
- lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
|
|
108
|
-
- lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
|
|
109
|
-
- lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb
|
|
110
|
-
- lib/ruby_indexer/lib/ruby_indexer/uri.rb
|
|
111
|
-
- lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb
|
|
112
|
-
- lib/ruby_indexer/ruby_indexer.rb
|
|
113
107
|
- lib/ruby_lsp/addon.rb
|
|
114
108
|
- lib/ruby_lsp/base_server.rb
|
|
115
109
|
- lib/ruby_lsp/client_capabilities.rb
|
|
@@ -182,23 +176,23 @@ files:
|
|
|
182
176
|
- lib/ruby_lsp/response_builders/signature_help.rb
|
|
183
177
|
- lib/ruby_lsp/response_builders/test_collection.rb
|
|
184
178
|
- lib/ruby_lsp/ruby_document.rb
|
|
179
|
+
- lib/ruby_lsp/rubydex/declaration.rb
|
|
185
180
|
- lib/ruby_lsp/rubydex/definition.rb
|
|
186
181
|
- lib/ruby_lsp/rubydex/reference.rb
|
|
182
|
+
- lib/ruby_lsp/rubydex/signature.rb
|
|
187
183
|
- lib/ruby_lsp/scope.rb
|
|
188
184
|
- lib/ruby_lsp/scripts/compose_bundle.rb
|
|
189
185
|
- lib/ruby_lsp/scripts/compose_bundle_windows.rb
|
|
190
186
|
- lib/ruby_lsp/server.rb
|
|
191
187
|
- lib/ruby_lsp/setup_bundler.rb
|
|
192
|
-
- lib/ruby_lsp/static_docs.rb
|
|
193
188
|
- lib/ruby_lsp/store.rb
|
|
194
189
|
- lib/ruby_lsp/test_helper.rb
|
|
195
190
|
- lib/ruby_lsp/test_reporters/lsp_reporter.rb
|
|
196
191
|
- lib/ruby_lsp/test_reporters/minitest_reporter.rb
|
|
197
192
|
- lib/ruby_lsp/test_reporters/test_unit_reporter.rb
|
|
198
193
|
- lib/ruby_lsp/type_inferrer.rb
|
|
194
|
+
- lib/ruby_lsp/uri.rb
|
|
199
195
|
- lib/ruby_lsp/utils.rb
|
|
200
|
-
- static_docs/break.md
|
|
201
|
-
- static_docs/yield.md
|
|
202
196
|
homepage: https://github.com/Shopify/ruby-lsp
|
|
203
197
|
licenses:
|
|
204
198
|
- MIT
|