ruby-lsp 0.23.2 → 0.23.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp-launcher +15 -9
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +31 -29
- data/lib/ruby_indexer/test/method_test.rb +15 -0
- data/lib/ruby_lsp/base_server.rb +1 -2
- data/lib/ruby_lsp/document.rb +19 -21
- data/lib/ruby_lsp/listeners/definition.rb +7 -2
- data/lib/ruby_lsp/requests/rename.rb +2 -2
- data/lib/ruby_lsp/requests/workspace_symbol.rb +2 -2
- data/lib/ruby_lsp/server.rb +41 -35
- data/lib/ruby_lsp/store.rb +8 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95642b8034d20684e2596755f391591d6dfffc9021e23a6e2efd0b9a0e500eb0
|
4
|
+
data.tar.gz: 6db469fda24144ce71eb0c2d8434251b3bf01a800035d85bea5e8e6801cf5120
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69365797e36c6597b43b5067da2e10b6c634cdb9121081db0e7c16c657fe12e2aa72042ba7beb07480b1cbb4a24c9a37a7a71f5a49a1be42923adb7a85a80715
|
7
|
+
data.tar.gz: 22695e4a4fd4e72738b43c32e9a693ed9895033e82240317700cde5583537b0ad6e7d8ce530a110624c57278d5a08c4eeb207ae010a4db99b3885bc241c5caa4
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.23.
|
1
|
+
0.23.3
|
data/exe/ruby-lsp-launcher
CHANGED
@@ -45,6 +45,12 @@ rescue Errno::ECHILD
|
|
45
45
|
# In theory, the child process can finish before we even get to the wait call, but that is not an error
|
46
46
|
end
|
47
47
|
|
48
|
+
error_path = File.join(".ruby-lsp", "install_error")
|
49
|
+
|
50
|
+
install_error = if File.exist?(error_path)
|
51
|
+
Marshal.load(File.read(error_path))
|
52
|
+
end
|
53
|
+
|
48
54
|
begin
|
49
55
|
bundle_env_path = File.join(".ruby-lsp", "bundle_env")
|
50
56
|
# We can't require `bundler/setup` because that file prematurely exits the process if setup fails. However, we can't
|
@@ -67,21 +73,21 @@ begin
|
|
67
73
|
rescue StandardError => e
|
68
74
|
# If installing gems failed for any reason, we don't want to exit the process prematurely. We can still provide most
|
69
75
|
# features in a degraded mode. We simply save the error so that we can report to the user that certain gems might be
|
70
|
-
# missing, but we respect the LSP life cycle
|
71
|
-
|
72
|
-
|
76
|
+
# missing, but we respect the LSP life cycle.
|
77
|
+
#
|
78
|
+
# If an install error occurred and one of the gems is not installed, Bundler.setup is guaranteed to fail with
|
79
|
+
# `Bundler::GemNotFound`. We don't set `setup_error` here because that would essentially report the same problem twice
|
80
|
+
# to telemetry, which is not useful
|
81
|
+
unless install_error && e.is_a?(Bundler::GemNotFound)
|
82
|
+
setup_error = e
|
83
|
+
$stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
|
84
|
+
end
|
73
85
|
|
74
86
|
# If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
|
75
87
|
# in degraded mode
|
76
88
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
77
89
|
end
|
78
90
|
|
79
|
-
error_path = File.join(".ruby-lsp", "install_error")
|
80
|
-
|
81
|
-
install_error = if File.exist?(error_path)
|
82
|
-
Marshal.load(File.read(error_path))
|
83
|
-
end
|
84
|
-
|
85
91
|
# Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
|
86
92
|
# configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
|
87
93
|
# paths into the load path manually or we may end up requiring the wrong version of the gem
|
@@ -287,7 +287,6 @@ module RubyIndexer
|
|
287
287
|
when :module_function
|
288
288
|
handle_module_function(node)
|
289
289
|
when :private_class_method
|
290
|
-
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
|
291
290
|
handle_private_class_method(node)
|
292
291
|
end
|
293
292
|
|
@@ -975,39 +974,42 @@ module RubyIndexer
|
|
975
974
|
|
976
975
|
sig { params(node: Prism::CallNode).void }
|
977
976
|
def handle_private_class_method(node)
|
978
|
-
node.arguments&.arguments
|
979
|
-
|
980
|
-
when Prism::StringNode, Prism::SymbolNode
|
981
|
-
[argument]
|
982
|
-
when Prism::ArrayNode
|
983
|
-
argument.elements
|
984
|
-
else
|
985
|
-
[]
|
986
|
-
end
|
977
|
+
arguments = node.arguments&.arguments
|
978
|
+
return unless arguments
|
987
979
|
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
980
|
+
# If we're passing a method definition directly to `private_class_method`, push a new private scope. That will be
|
981
|
+
# applied when the indexer finds the method definition and then popped on `call_node_leave`
|
982
|
+
if arguments.first.is_a?(Prism::DefNode)
|
983
|
+
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
|
984
|
+
return
|
985
|
+
end
|
992
986
|
|
993
|
-
|
994
|
-
|
995
|
-
when Prism::StringNode
|
996
|
-
string_or_symbol_node.content
|
997
|
-
when Prism::SymbolNode
|
998
|
-
string_or_symbol_node.value
|
999
|
-
end
|
1000
|
-
next unless method_name
|
987
|
+
owner_name = @owner_stack.last&.name
|
988
|
+
return unless owner_name
|
1001
989
|
|
1002
|
-
|
1003
|
-
|
990
|
+
# private_class_method accepts strings, symbols or arrays of strings and symbols as arguments. Here we build a
|
991
|
+
# single list of all of the method names that have to be made private
|
992
|
+
arrays, others = T.cast(
|
993
|
+
arguments.partition { |argument| argument.is_a?(Prism::ArrayNode) },
|
994
|
+
[T::Array[Prism::ArrayNode], T::Array[Prism::Node]],
|
995
|
+
)
|
996
|
+
arrays.each { |array| others.concat(array.elements) }
|
1004
997
|
|
1005
|
-
|
1006
|
-
|
998
|
+
names = others.filter_map do |argument|
|
999
|
+
case argument
|
1000
|
+
when Prism::StringNode
|
1001
|
+
argument.unescaped
|
1002
|
+
when Prism::SymbolNode
|
1003
|
+
argument.value
|
1004
|
+
end
|
1005
|
+
end
|
1007
1006
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1007
|
+
names.each do |name|
|
1008
|
+
entries = @index.resolve_method(name, @index.existing_or_new_singleton_class(owner_name).name)
|
1009
|
+
next unless entries
|
1010
|
+
|
1011
|
+
entries.each do |entry|
|
1012
|
+
entry.visibility = Entry::Visibility::PRIVATE
|
1011
1013
|
end
|
1012
1014
|
end
|
1013
1015
|
end
|
@@ -909,6 +909,21 @@ module RubyIndexer
|
|
909
909
|
assert_equal("Foo", T.must(entry.owner).name)
|
910
910
|
end
|
911
911
|
|
912
|
+
def test_making_several_class_methods_private
|
913
|
+
index(<<~RUBY)
|
914
|
+
class Foo
|
915
|
+
def self.bar; end
|
916
|
+
def self.baz; end
|
917
|
+
def self.qux; end
|
918
|
+
|
919
|
+
private_class_method :bar, :baz, :qux
|
920
|
+
|
921
|
+
def initialize
|
922
|
+
end
|
923
|
+
end
|
924
|
+
RUBY
|
925
|
+
end
|
926
|
+
|
912
927
|
private
|
913
928
|
|
914
929
|
sig { params(entry: Entry::Method, call_string: String).void }
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -90,8 +90,7 @@ module RubyLsp
|
|
90
90
|
# The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
|
91
91
|
# else is pushed into the incoming queue
|
92
92
|
case method
|
93
|
-
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
94
|
-
"$/cancelRequest"
|
93
|
+
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
95
94
|
process_message(message)
|
96
95
|
when "shutdown"
|
97
96
|
@global_state.synchronize do
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -97,33 +97,31 @@ module RubyLsp
|
|
97
97
|
|
98
98
|
sig { params(edits: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).void }
|
99
99
|
def push_edits(edits, version:)
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
scanner = create_scanner
|
100
|
+
edits.each do |edit|
|
101
|
+
range = edit[:range]
|
102
|
+
scanner = create_scanner
|
104
103
|
|
105
|
-
|
106
|
-
|
104
|
+
start_position = scanner.find_char_position(range[:start])
|
105
|
+
end_position = scanner.find_char_position(range[:end])
|
107
106
|
|
108
|
-
|
109
|
-
|
107
|
+
@source[start_position...end_position] = edit[:text]
|
108
|
+
end
|
110
109
|
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
@version = version
|
111
|
+
@needs_parsing = true
|
112
|
+
@cache.clear
|
114
113
|
|
115
|
-
|
116
|
-
|
114
|
+
last_edit = edits.last
|
115
|
+
return unless last_edit
|
117
116
|
|
118
|
-
|
117
|
+
last_edit_range = last_edit[:range]
|
119
118
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
119
|
+
@last_edit = if last_edit_range[:start] == last_edit_range[:end]
|
120
|
+
Insert.new(last_edit_range)
|
121
|
+
elsif last_edit[:text].empty?
|
122
|
+
Delete.new(last_edit_range)
|
123
|
+
else
|
124
|
+
Replace.new(last_edit_range)
|
127
125
|
end
|
128
126
|
end
|
129
127
|
|
@@ -330,7 +330,8 @@ module RubyLsp
|
|
330
330
|
|
331
331
|
methods.each do |target_method|
|
332
332
|
uri = target_method.uri
|
333
|
-
|
333
|
+
full_path = uri.full_path
|
334
|
+
next if sorbet_level_true_or_higher?(@sorbet_level) && (!full_path || not_in_dependencies?(full_path))
|
334
335
|
|
335
336
|
@response_builder << Interface::LocationLink.new(
|
336
337
|
target_uri: uri.to_s,
|
@@ -403,7 +404,11 @@ module RubyLsp
|
|
403
404
|
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
404
405
|
# ignore
|
405
406
|
uri = entry.uri
|
406
|
-
|
407
|
+
full_path = uri.full_path
|
408
|
+
|
409
|
+
if @sorbet_level != RubyDocument::SorbetLevel::Ignore && (!full_path || not_in_dependencies?(full_path))
|
410
|
+
next
|
411
|
+
end
|
407
412
|
|
408
413
|
@response_builder << Interface::LocationLink.new(
|
409
414
|
target_uri: uri.to_s,
|
@@ -116,8 +116,8 @@ module RubyLsp
|
|
116
116
|
T.must(@global_state.index[fully_qualified_name]).each do |entry|
|
117
117
|
# Do not rename files that are not part of the workspace
|
118
118
|
uri = entry.uri
|
119
|
-
file_path =
|
120
|
-
next unless file_path
|
119
|
+
file_path = uri.full_path
|
120
|
+
next unless file_path&.start_with?(@global_state.workspace_path)
|
121
121
|
|
122
122
|
case entry
|
123
123
|
when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
|
@@ -22,10 +22,10 @@ module RubyLsp
|
|
22
22
|
def perform
|
23
23
|
@index.fuzzy_search(@query).filter_map do |entry|
|
24
24
|
uri = entry.uri
|
25
|
-
file_path =
|
25
|
+
file_path = uri.full_path
|
26
26
|
|
27
27
|
# We only show symbols declared in the workspace
|
28
|
-
in_dependencies = !not_in_dependencies?(file_path)
|
28
|
+
in_dependencies = file_path && !not_in_dependencies?(file_path)
|
29
29
|
next if in_dependencies
|
30
30
|
|
31
31
|
# We should never show private symbols when searching the entire workspace
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -359,48 +359,52 @@ module RubyLsp
|
|
359
359
|
|
360
360
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
361
361
|
def text_document_did_open(message)
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
362
|
+
@global_state.synchronize do
|
363
|
+
text_document = message.dig(:params, :textDocument)
|
364
|
+
language_id = case text_document[:languageId]
|
365
|
+
when "erb", "eruby"
|
366
|
+
Document::LanguageId::ERB
|
367
|
+
when "rbs"
|
368
|
+
Document::LanguageId::RBS
|
369
|
+
else
|
370
|
+
Document::LanguageId::Ruby
|
371
|
+
end
|
371
372
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
373
|
+
document = @store.set(
|
374
|
+
uri: text_document[:uri],
|
375
|
+
source: text_document[:text],
|
376
|
+
version: text_document[:version],
|
377
|
+
language_id: language_id,
|
378
|
+
)
|
378
379
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
380
|
+
if document.past_expensive_limit? && text_document[:uri].scheme == "file"
|
381
|
+
log_message = <<~MESSAGE
|
382
|
+
The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
|
383
|
+
diagnostics will be disabled.
|
384
|
+
MESSAGE
|
384
385
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
386
|
+
send_message(
|
387
|
+
Notification.new(
|
388
|
+
method: "window/logMessage",
|
389
|
+
params: Interface::LogMessageParams.new(
|
390
|
+
type: Constant::MessageType::WARNING,
|
391
|
+
message: log_message,
|
392
|
+
),
|
391
393
|
),
|
392
|
-
)
|
393
|
-
|
394
|
+
)
|
395
|
+
end
|
394
396
|
end
|
395
397
|
end
|
396
398
|
|
397
399
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
398
400
|
def text_document_did_close(message)
|
399
|
-
|
400
|
-
|
401
|
+
@global_state.synchronize do
|
402
|
+
uri = message.dig(:params, :textDocument, :uri)
|
403
|
+
@store.delete(uri)
|
401
404
|
|
402
|
-
|
403
|
-
|
405
|
+
# Clear diagnostics for the closed file, so that they no longer appear in the problems tab
|
406
|
+
send_message(Notification.publish_diagnostics(uri.to_s, []))
|
407
|
+
end
|
404
408
|
end
|
405
409
|
|
406
410
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
@@ -408,7 +412,9 @@ module RubyLsp
|
|
408
412
|
params = message[:params]
|
409
413
|
text_document = params[:textDocument]
|
410
414
|
|
411
|
-
@
|
415
|
+
@global_state.synchronize do
|
416
|
+
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
|
417
|
+
end
|
412
418
|
end
|
413
419
|
|
414
420
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
@@ -1254,10 +1260,10 @@ module RubyLsp
|
|
1254
1260
|
return
|
1255
1261
|
end
|
1256
1262
|
|
1257
|
-
return unless indexing_options
|
1258
|
-
|
1259
1263
|
configuration = @global_state.index.configuration
|
1260
1264
|
configuration.workspace_path = @global_state.workspace_path
|
1265
|
+
return unless indexing_options
|
1266
|
+
|
1261
1267
|
# The index expects snake case configurations, but VS Code standardizes on camel case settings
|
1262
1268
|
configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
|
1263
1269
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -65,15 +65,13 @@ module RubyLsp
|
|
65
65
|
).returns(Document[T.untyped])
|
66
66
|
end
|
67
67
|
def set(uri:, source:, version:, language_id:)
|
68
|
-
@
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
RubyDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
76
|
-
end
|
68
|
+
@state[uri.to_s] = case language_id
|
69
|
+
when Document::LanguageId::ERB
|
70
|
+
ERBDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
71
|
+
when Document::LanguageId::RBS
|
72
|
+
RBSDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
73
|
+
else
|
74
|
+
RubyDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
|
77
75
|
end
|
78
76
|
end
|
79
77
|
|
@@ -94,9 +92,7 @@ module RubyLsp
|
|
94
92
|
|
95
93
|
sig { params(uri: URI::Generic).void }
|
96
94
|
def delete(uri)
|
97
|
-
@
|
98
|
-
@state.delete(uri.to_s)
|
99
|
-
end
|
95
|
+
@state.delete(uri.to_s)
|
100
96
|
end
|
101
97
|
|
102
98
|
sig { params(uri: URI::Generic).returns(T::Boolean) }
|
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.3
|
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-09 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: language_server-protocol
|