ruby-lsp 0.23.7 → 0.23.8
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 +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +51 -6
- data/lib/ruby_indexer/test/index_test.rb +69 -0
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
- data/lib/ruby_lsp/server.rb +27 -6
- data/lib/ruby_lsp/setup_bundler.rb +1 -2
- 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: 918079e906832b0289a053cd4bfe849422082c25bd0f4bdada25cab3a532005f
|
4
|
+
data.tar.gz: 7935b2bdc52a2bb83264376fb98f7ef8d3bfc3c5cb9dadd23642a1ebafbca013
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fb084d322308bb2e69c2c0d5cfed2d275fe53f5df40aa2a49a848aab4c771858dfa1b5d5df913516c1f1c90ce5101b2f461865093f5e6787c79886cadc40bdb
|
7
|
+
data.tar.gz: 06206026d180e9d044edda0a83018ae31895cce3e6d91cb9ae4bdb3c365565a27881dd17ffd6d8861d8126441e327cb8fdf91c41e6003c1d84a4038c8b9003ea
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.23.
|
1
|
+
0.23.8
|
data/exe/ruby-lsp-launcher
CHANGED
@@ -51,7 +51,7 @@ end
|
|
51
51
|
|
52
52
|
begin
|
53
53
|
# Wait until the composed Bundle is finished
|
54
|
-
Process.
|
54
|
+
_, status = Process.wait2(pid)
|
55
55
|
rescue Errno::ECHILD
|
56
56
|
# In theory, the child process can finish before we even get to the wait call, but that is not an error
|
57
57
|
end
|
@@ -105,7 +105,7 @@ end
|
|
105
105
|
# flow, we are not booting the LSP yet, just checking if the bundle is valid before rebooting
|
106
106
|
if reboot
|
107
107
|
# Use the exit status to signal to the server if composing the bundle succeeded
|
108
|
-
exit(install_error || setup_error ? 1 : 0)
|
108
|
+
exit(install_error || setup_error ? 1 : status&.exitstatus || 0)
|
109
109
|
end
|
110
110
|
|
111
111
|
# Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
|
@@ -120,8 +120,16 @@ module RubyIndexer
|
|
120
120
|
)]))
|
121
121
|
end
|
122
122
|
def first_unqualified_const(name)
|
123
|
+
# Look for an exact match first
|
123
124
|
_name, entries = @entries.find do |const_name, _entries|
|
124
|
-
const_name.end_with?(name)
|
125
|
+
const_name == name || const_name.end_with?("::#{name}")
|
126
|
+
end
|
127
|
+
|
128
|
+
# If an exact match is not found, then try to find a constant that ends with the name
|
129
|
+
unless entries
|
130
|
+
_name, entries = @entries.find do |const_name, _entries|
|
131
|
+
const_name.end_with?(name)
|
132
|
+
end
|
125
133
|
end
|
126
134
|
|
127
135
|
T.cast(
|
@@ -593,7 +601,7 @@ module RubyIndexer
|
|
593
601
|
entries = self[variable_name]&.grep(Entry::ClassVariable)
|
594
602
|
return unless entries&.any?
|
595
603
|
|
596
|
-
ancestors =
|
604
|
+
ancestors = linearized_attached_ancestors(owner_name)
|
597
605
|
return if ancestors.empty?
|
598
606
|
|
599
607
|
entries.select { |e| ancestors.include?(e.owner&.name) }
|
@@ -601,12 +609,33 @@ module RubyIndexer
|
|
601
609
|
|
602
610
|
# Returns a list of possible candidates for completion of instance variables for a given owner name. The name must
|
603
611
|
# include the `@` prefix
|
604
|
-
sig
|
612
|
+
sig do
|
613
|
+
params(name: String, owner_name: String).returns(T::Array[T.any(Entry::InstanceVariable, Entry::ClassVariable)])
|
614
|
+
end
|
605
615
|
def instance_variable_completion_candidates(name, owner_name)
|
606
|
-
entries = T.cast(prefix_search(name).flatten, T::Array[Entry::InstanceVariable])
|
616
|
+
entries = T.cast(prefix_search(name).flatten, T::Array[T.any(Entry::InstanceVariable, Entry::ClassVariable)])
|
617
|
+
# Avoid wasting time linearizing ancestors if we didn't find anything
|
618
|
+
return entries if entries.empty?
|
619
|
+
|
607
620
|
ancestors = linearized_ancestors_of(owner_name)
|
608
621
|
|
609
|
-
|
622
|
+
instance_variables, class_variables = entries.partition { |e| e.is_a?(Entry::InstanceVariable) }
|
623
|
+
variables = instance_variables.select { |e| ancestors.any?(e.owner&.name) }
|
624
|
+
|
625
|
+
# Class variables are only owned by the attached class in our representation. If the owner is in a singleton
|
626
|
+
# context, we have to search for ancestors of the attached class
|
627
|
+
if class_variables.any?
|
628
|
+
name_parts = owner_name.split("::")
|
629
|
+
|
630
|
+
if name_parts.last&.start_with?("<Class:")
|
631
|
+
attached_name = T.must(name_parts[0..-2]).join("::")
|
632
|
+
attached_ancestors = linearized_ancestors_of(attached_name)
|
633
|
+
variables.concat(class_variables.select { |e| attached_ancestors.any?(e.owner&.name) })
|
634
|
+
else
|
635
|
+
variables.concat(class_variables.select { |e| ancestors.any?(e.owner&.name) })
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
610
639
|
variables.uniq!(&:name)
|
611
640
|
variables
|
612
641
|
end
|
@@ -614,8 +643,10 @@ module RubyIndexer
|
|
614
643
|
sig { params(name: String, owner_name: String).returns(T::Array[Entry::ClassVariable]) }
|
615
644
|
def class_variable_completion_candidates(name, owner_name)
|
616
645
|
entries = T.cast(prefix_search(name).flatten, T::Array[Entry::ClassVariable])
|
617
|
-
ancestors
|
646
|
+
# Avoid wasting time linearizing ancestors if we didn't find anything
|
647
|
+
return entries if entries.empty?
|
618
648
|
|
649
|
+
ancestors = linearized_attached_ancestors(owner_name)
|
619
650
|
variables = entries.select { |e| ancestors.any?(e.owner&.name) }
|
620
651
|
variables.uniq!(&:name)
|
621
652
|
variables
|
@@ -717,6 +748,20 @@ module RubyIndexer
|
|
717
748
|
|
718
749
|
private
|
719
750
|
|
751
|
+
# Always returns the linearized ancestors for the attached class, regardless of whether `name` refers to a singleton
|
752
|
+
# or attached namespace
|
753
|
+
sig { params(name: String).returns(T::Array[String]) }
|
754
|
+
def linearized_attached_ancestors(name)
|
755
|
+
name_parts = name.split("::")
|
756
|
+
|
757
|
+
if name_parts.last&.start_with?("<Class:")
|
758
|
+
attached_name = T.must(name_parts[0..-2]).join("::")
|
759
|
+
linearized_ancestors_of(attached_name)
|
760
|
+
else
|
761
|
+
linearized_ancestors_of(name)
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
720
765
|
# Runs the registered included hooks
|
721
766
|
sig { params(fully_qualified_name: String, nesting: T::Array[String]).void }
|
722
767
|
def run_included_hooks(fully_qualified_name, nesting)
|
@@ -1561,6 +1561,23 @@ module RubyIndexer
|
|
1561
1561
|
assert_equal("Foo::Bar", entry.name)
|
1562
1562
|
end
|
1563
1563
|
|
1564
|
+
def test_first_unqualified_const_prefers_exact_matches
|
1565
|
+
index(<<~RUBY)
|
1566
|
+
module Foo
|
1567
|
+
class ParseResultType
|
1568
|
+
end
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
module Namespace
|
1572
|
+
class Type
|
1573
|
+
end
|
1574
|
+
end
|
1575
|
+
RUBY
|
1576
|
+
|
1577
|
+
entry = T.must(@index.first_unqualified_const("Type")&.first)
|
1578
|
+
assert_equal("Namespace::Type", entry.name)
|
1579
|
+
end
|
1580
|
+
|
1564
1581
|
def test_completion_does_not_duplicate_overridden_methods
|
1565
1582
|
index(<<~RUBY)
|
1566
1583
|
class Foo
|
@@ -2092,5 +2109,57 @@ module RubyIndexer
|
|
2092
2109
|
refute_nil(entry, "Expected indexer to be able to handle unsaved URIs")
|
2093
2110
|
assert_equal("I added this comment!", entry.comments)
|
2094
2111
|
end
|
2112
|
+
|
2113
|
+
def test_instance_variable_completion_returns_class_variables_too
|
2114
|
+
index(<<~RUBY)
|
2115
|
+
class Parent
|
2116
|
+
@@abc = 123
|
2117
|
+
end
|
2118
|
+
|
2119
|
+
class Child < Parent
|
2120
|
+
@@adf = 123
|
2121
|
+
|
2122
|
+
def self.do
|
2123
|
+
end
|
2124
|
+
end
|
2125
|
+
RUBY
|
2126
|
+
|
2127
|
+
abc, adf = @index.instance_variable_completion_candidates("@", "Child::<Class:Child>")
|
2128
|
+
|
2129
|
+
refute_nil(abc)
|
2130
|
+
refute_nil(adf)
|
2131
|
+
|
2132
|
+
assert_equal("@@abc", abc.name)
|
2133
|
+
assert_equal("@@adf", adf.name)
|
2134
|
+
end
|
2135
|
+
|
2136
|
+
def test_class_variable_completion_from_singleton_context
|
2137
|
+
index(<<~RUBY)
|
2138
|
+
class Foo
|
2139
|
+
@@hello = 123
|
2140
|
+
|
2141
|
+
def self.do
|
2142
|
+
end
|
2143
|
+
end
|
2144
|
+
RUBY
|
2145
|
+
|
2146
|
+
candidates = @index.class_variable_completion_candidates("@@", "Foo::<Class:Foo>")
|
2147
|
+
refute_empty(candidates)
|
2148
|
+
|
2149
|
+
assert_equal("@@hello", candidates.first&.name)
|
2150
|
+
end
|
2151
|
+
|
2152
|
+
def test_resolve_class_variable_in_singleton_context
|
2153
|
+
index(<<~RUBY)
|
2154
|
+
class Foo
|
2155
|
+
@@hello = 123
|
2156
|
+
end
|
2157
|
+
RUBY
|
2158
|
+
|
2159
|
+
candidates = @index.resolve_class_variable("@@hello", "Foo::<Class:Foo>")
|
2160
|
+
refute_empty(candidates)
|
2161
|
+
|
2162
|
+
assert_equal("@@hello", candidates.first&.name)
|
2163
|
+
end
|
2095
2164
|
end
|
2096
2165
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -170,7 +170,7 @@ module RubyLsp
|
|
170
170
|
|
171
171
|
sig { params(line: Integer, character: Integer).void }
|
172
172
|
def move_cursor_to(line, character)
|
173
|
-
return unless
|
173
|
+
return unless /Visual Studio Code|Cursor/.match?(@client_name)
|
174
174
|
|
175
175
|
position = Interface::Position.new(
|
176
176
|
line: line,
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -1036,6 +1036,10 @@ module RubyLsp
|
|
1036
1036
|
when Constant::FileChangeType::DELETED
|
1037
1037
|
index.delete(uri)
|
1038
1038
|
end
|
1039
|
+
rescue Errno::ENOENT
|
1040
|
+
# If a file is created and then delete immediately afterwards, we will process the created notification before we
|
1041
|
+
# receive the deleted one, but the file no longer exists. This may happen when running a test suite that creates
|
1042
|
+
# and deletes files automatically.
|
1039
1043
|
end
|
1040
1044
|
|
1041
1045
|
sig { params(uri: URI::Generic).void }
|
@@ -1286,25 +1290,40 @@ module RubyLsp
|
|
1286
1290
|
addon.handle_window_show_message_response(result[:title])
|
1287
1291
|
end
|
1288
1292
|
|
1289
|
-
|
1293
|
+
# NOTE: all servers methods are void because they can produce several messages for the client. The only reason this
|
1294
|
+
# method returns the created thread is to that we can join it in tests and avoid flakiness. The implementation is
|
1295
|
+
# not supposed to rely on the return of this method
|
1296
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).returns(T.nilable(Thread)) }
|
1290
1297
|
def compose_bundle(message)
|
1291
1298
|
already_composed_path = File.join(@global_state.workspace_path, ".ruby-lsp", "bundle_is_composed")
|
1292
|
-
command = "#{Gem.ruby} #{File.expand_path("../../exe/ruby-lsp-launcher", __dir__)} #{@global_state.workspace_uri}"
|
1293
1299
|
id = message[:id]
|
1294
1300
|
|
1295
1301
|
begin
|
1296
|
-
Bundler
|
1302
|
+
Bundler.with_original_env do
|
1303
|
+
Bundler::LockfileParser.new(Bundler.default_lockfile.read)
|
1304
|
+
end
|
1297
1305
|
rescue Bundler::LockfileError => e
|
1298
1306
|
send_message(Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: e.message))
|
1299
1307
|
return
|
1308
|
+
rescue Bundler::GemfileNotFound, Errno::ENOENT
|
1309
|
+
# We still compose the bundle if there's no Gemfile or if the lockfile got deleted
|
1300
1310
|
end
|
1301
1311
|
|
1302
1312
|
# We compose the bundle in a thread so that the LSP continues to work while we're checking for its validity. Once
|
1303
1313
|
# we return the response back to the editor, then the restart is triggered
|
1304
1314
|
Thread.new do
|
1305
1315
|
send_log_message("Recomposing the bundle ahead of restart")
|
1306
|
-
|
1307
|
-
|
1316
|
+
|
1317
|
+
_stdout, stderr, status = Bundler.with_unbundled_env do
|
1318
|
+
Open3.capture3(
|
1319
|
+
Gem.ruby,
|
1320
|
+
"-I",
|
1321
|
+
File.dirname(T.must(__dir__)),
|
1322
|
+
File.expand_path("../../exe/ruby-lsp-launcher", __dir__),
|
1323
|
+
@global_state.workspace_uri.to_s,
|
1324
|
+
chdir: @global_state.workspace_path,
|
1325
|
+
)
|
1326
|
+
end
|
1308
1327
|
|
1309
1328
|
if status&.exitstatus == 0
|
1310
1329
|
# Create a signal for the restart that it can skip composing the bundle and launch directly
|
@@ -1313,7 +1332,9 @@ module RubyLsp
|
|
1313
1332
|
else
|
1314
1333
|
# This special error code makes the extension avoid restarting in case we already know that the composed
|
1315
1334
|
# bundle is not valid
|
1316
|
-
send_message(
|
1335
|
+
send_message(
|
1336
|
+
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
|
1337
|
+
)
|
1317
1338
|
end
|
1318
1339
|
end
|
1319
1340
|
end
|
@@ -241,8 +241,7 @@ module RubyLsp
|
|
241
241
|
|
242
242
|
# If either the Gemfile or the lockfile have been modified during the process of setting up the bundle, retry
|
243
243
|
# composing the bundle from scratch
|
244
|
-
|
245
|
-
if @gemfile && @lockfile
|
244
|
+
if @gemfile&.exist? && @lockfile&.exist?
|
246
245
|
current_gemfile_hash = Digest::SHA256.hexdigest(@gemfile.read)
|
247
246
|
current_lockfile_hash = Digest::SHA256.hexdigest(@lockfile.read)
|
248
247
|
|
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.8
|
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-31 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: language_server-protocol
|