ruby-lsp 0.23.6 → 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/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +3 -3
- data/exe/ruby-lsp-launcher +25 -8
- 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/code_actions.rb +1 -1
- data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
- data/lib/ruby_lsp/requests/folding_ranges.rb +2 -6
- data/lib/ruby_lsp/requests/formatting.rb +2 -6
- data/lib/ruby_lsp/requests/on_type_formatting.rb +2 -2
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/server.rb +56 -0
- data/lib/ruby_lsp/setup_bundler.rb +19 -2
- data/lib/ruby_lsp/utils.rb +2 -0
- metadata +3 -3
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/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
[](https://github.com/Shopify/ruby-lsp/actions/workflows/ci.yml)
|
6
6
|
[](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
|
7
|
-
[](https://join.slack.com/t/ruby-dx/shared_invite/zt-
|
7
|
+
[](https://join.slack.com/t/ruby-dx/shared_invite/zt-2yd77ayis-yAiVc1TX_kH0mHMBbi89dA)
|
8
8
|
|
9
9
|
# Ruby LSP
|
10
10
|
|
@@ -13,7 +13,7 @@ for Ruby, used to improve rich features in editors. It is a part of a wider goal
|
|
13
13
|
experience to Ruby developers using modern standards for cross-editor features, documentation and debugging.
|
14
14
|
|
15
15
|
Want to discuss Ruby developer experience? Consider joining the public
|
16
|
-
[Ruby DX Slack workspace](https://join.slack.com/t/ruby-dx/shared_invite/zt-
|
16
|
+
[Ruby DX Slack workspace](https://join.slack.com/t/ruby-dx/shared_invite/zt-2yd77ayis-yAiVc1TX_kH0mHMBbi89dA).
|
17
17
|
|
18
18
|
## Getting Started
|
19
19
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.23.
|
1
|
+
0.23.8
|
data/exe/ruby-lsp
CHANGED
@@ -64,9 +64,9 @@ if ENV["BUNDLE_GEMFILE"].nil?
|
|
64
64
|
# which gives us the opportunity to control which specs are activated and enter degraded mode if any gems failed to
|
65
65
|
# install rather than failing to boot the server completely
|
66
66
|
if options[:launcher]
|
67
|
-
|
68
|
-
|
69
|
-
exit exec(
|
67
|
+
flags = []
|
68
|
+
flags << "--debug" if options[:debug]
|
69
|
+
exit exec(Gem.ruby, File.expand_path("ruby-lsp-launcher", __dir__), *flags)
|
70
70
|
end
|
71
71
|
|
72
72
|
require_relative "../lib/ruby_lsp/setup_bundler"
|
data/exe/ruby-lsp-launcher
CHANGED
@@ -8,14 +8,24 @@
|
|
8
8
|
|
9
9
|
setup_error = nil
|
10
10
|
install_error = nil
|
11
|
+
reboot = false
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
workspace_uri = ARGV.first
|
14
|
+
|
15
|
+
raw_initialize = if workspace_uri && !workspace_uri.start_with?("--")
|
16
|
+
# If there's an argument without `--`, then it's the server asking to compose the bundle and passing to this
|
17
|
+
# executable the workspace URI. We can't require gems at this point, so we built a fake initialize request manually
|
18
|
+
reboot = true
|
19
|
+
"{\"params\":{\"workspaceFolders\":[{\"uri\":\"#{workspace_uri}\"}]}}"
|
20
|
+
else
|
21
|
+
# Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
|
22
|
+
# Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
|
23
|
+
# to ensure that we're setting up the bundle in the right place
|
24
|
+
$stdin.binmode
|
25
|
+
headers = $stdin.gets("\r\n\r\n")
|
26
|
+
content_length = headers[/Content-Length: (\d+)/i, 1].to_i
|
27
|
+
$stdin.read(content_length)
|
28
|
+
end
|
19
29
|
|
20
30
|
# Compose the Ruby LSP bundle in a forked process so that we can require gems without polluting the main process
|
21
31
|
# `$LOAD_PATH` and `Gem.loaded_specs`. Windows doesn't support forking, so we need a separate path to support it
|
@@ -41,7 +51,7 @@ end
|
|
41
51
|
|
42
52
|
begin
|
43
53
|
# Wait until the composed Bundle is finished
|
44
|
-
Process.
|
54
|
+
_, status = Process.wait2(pid)
|
45
55
|
rescue Errno::ECHILD
|
46
56
|
# In theory, the child process can finish before we even get to the wait call, but that is not an error
|
47
57
|
end
|
@@ -91,6 +101,13 @@ rescue StandardError => e
|
|
91
101
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
92
102
|
end
|
93
103
|
|
104
|
+
# When performing a lockfile re-boot, this executable is invoked to set up the composed bundle ahead of time. In this
|
105
|
+
# flow, we are not booting the LSP yet, just checking if the bundle is valid before rebooting
|
106
|
+
if reboot
|
107
|
+
# Use the exit status to signal to the server if composing the bundle succeeded
|
108
|
+
exit(install_error || setup_error ? 1 : status&.exitstatus || 0)
|
109
|
+
end
|
110
|
+
|
94
111
|
# Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
|
95
112
|
# configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
|
96
113
|
# paths into the load path manually or we may end up requiring the wrong version of the gem
|
@@ -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
@@ -19,7 +19,7 @@ module RubyLsp
|
|
19
19
|
sig { returns(Interface::CodeActionRegistrationOptions) }
|
20
20
|
def provider
|
21
21
|
Interface::CodeActionRegistrationOptions.new(
|
22
|
-
document_selector:
|
22
|
+
document_selector: nil,
|
23
23
|
resolve_provider: true,
|
24
24
|
)
|
25
25
|
end
|
@@ -15,7 +15,7 @@ module RubyLsp
|
|
15
15
|
sig { returns(Interface::DiagnosticRegistrationOptions) }
|
16
16
|
def provider
|
17
17
|
Interface::DiagnosticRegistrationOptions.new(
|
18
|
-
document_selector:
|
18
|
+
document_selector: nil,
|
19
19
|
inter_file_dependencies: false,
|
20
20
|
workspace_diagnostics: false,
|
21
21
|
)
|
@@ -13,13 +13,9 @@ module RubyLsp
|
|
13
13
|
class << self
|
14
14
|
extend T::Sig
|
15
15
|
|
16
|
-
sig { returns(
|
16
|
+
sig { returns(TrueClass) }
|
17
17
|
def provider
|
18
|
-
|
19
|
-
document_selector: [
|
20
|
-
Interface::DocumentFilter.new(language: "ruby"),
|
21
|
-
],
|
22
|
-
)
|
18
|
+
true
|
23
19
|
end
|
24
20
|
end
|
25
21
|
|
@@ -14,13 +14,9 @@ module RubyLsp
|
|
14
14
|
class << self
|
15
15
|
extend T::Sig
|
16
16
|
|
17
|
-
sig { returns(
|
17
|
+
sig { returns(TrueClass) }
|
18
18
|
def provider
|
19
|
-
|
20
|
-
document_selector: [
|
21
|
-
Interface::DocumentFilter.new(language: "ruby"),
|
22
|
-
],
|
23
|
-
)
|
19
|
+
true
|
24
20
|
end
|
25
21
|
end
|
26
22
|
|
@@ -14,7 +14,7 @@ module RubyLsp
|
|
14
14
|
sig { returns(Interface::DocumentOnTypeFormattingRegistrationOptions) }
|
15
15
|
def provider
|
16
16
|
Interface::DocumentOnTypeFormattingRegistrationOptions.new(
|
17
|
-
document_selector:
|
17
|
+
document_selector: nil,
|
18
18
|
first_trigger_character: "{",
|
19
19
|
more_trigger_character: ["\n", "|", "d"],
|
20
20
|
)
|
@@ -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,
|
@@ -17,7 +17,7 @@ module RubyLsp
|
|
17
17
|
sig { returns(Interface::SemanticTokensRegistrationOptions) }
|
18
18
|
def provider
|
19
19
|
Interface::SemanticTokensRegistrationOptions.new(
|
20
|
-
document_selector:
|
20
|
+
document_selector: nil,
|
21
21
|
legend: Interface::SemanticTokensLegend.new(
|
22
22
|
token_types: ResponseBuilders::SemanticHighlighting::TOKEN_TYPES.keys,
|
23
23
|
token_modifiers: ResponseBuilders::SemanticHighlighting::TOKEN_MODIFIERS.keys,
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -106,6 +106,8 @@ module RubyLsp
|
|
106
106
|
end,
|
107
107
|
),
|
108
108
|
)
|
109
|
+
when "rubyLsp/composeBundle"
|
110
|
+
compose_bundle(message)
|
109
111
|
when "$/cancelRequest"
|
110
112
|
@global_state.synchronize { @cancelled_requests << message[:params][:id] }
|
111
113
|
when nil
|
@@ -283,6 +285,7 @@ module RubyLsp
|
|
283
285
|
document_range_formatting_provider: true,
|
284
286
|
experimental: {
|
285
287
|
addon_detection: true,
|
288
|
+
compose_bundle: true,
|
286
289
|
},
|
287
290
|
),
|
288
291
|
serverInfo: {
|
@@ -1033,6 +1036,10 @@ module RubyLsp
|
|
1033
1036
|
when Constant::FileChangeType::DELETED
|
1034
1037
|
index.delete(uri)
|
1035
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.
|
1036
1043
|
end
|
1037
1044
|
|
1038
1045
|
sig { params(uri: URI::Generic).void }
|
@@ -1282,5 +1289,54 @@ module RubyLsp
|
|
1282
1289
|
|
1283
1290
|
addon.handle_window_show_message_response(result[:title])
|
1284
1291
|
end
|
1292
|
+
|
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)) }
|
1297
|
+
def compose_bundle(message)
|
1298
|
+
already_composed_path = File.join(@global_state.workspace_path, ".ruby-lsp", "bundle_is_composed")
|
1299
|
+
id = message[:id]
|
1300
|
+
|
1301
|
+
begin
|
1302
|
+
Bundler.with_original_env do
|
1303
|
+
Bundler::LockfileParser.new(Bundler.default_lockfile.read)
|
1304
|
+
end
|
1305
|
+
rescue Bundler::LockfileError => e
|
1306
|
+
send_message(Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: e.message))
|
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
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
# We compose the bundle in a thread so that the LSP continues to work while we're checking for its validity. Once
|
1313
|
+
# we return the response back to the editor, then the restart is triggered
|
1314
|
+
Thread.new do
|
1315
|
+
send_log_message("Recomposing the bundle ahead of restart")
|
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
|
1327
|
+
|
1328
|
+
if status&.exitstatus == 0
|
1329
|
+
# Create a signal for the restart that it can skip composing the bundle and launch directly
|
1330
|
+
FileUtils.touch(already_composed_path)
|
1331
|
+
send_message(Result.new(id: id, response: { success: true }))
|
1332
|
+
else
|
1333
|
+
# This special error code makes the extension avoid restarting in case we already know that the composed
|
1334
|
+
# bundle is not valid
|
1335
|
+
send_message(
|
1336
|
+
Error.new(id: id, code: BUNDLE_COMPOSE_FAILED_CODE, message: "Failed to compose bundle\n#{stderr}"),
|
1337
|
+
)
|
1338
|
+
end
|
1339
|
+
end
|
1340
|
+
end
|
1285
1341
|
end
|
1286
1342
|
end
|
@@ -57,6 +57,7 @@ module RubyLsp
|
|
57
57
|
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
|
58
58
|
@last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
|
59
59
|
@error_path = T.let(@custom_dir + "install_error", Pathname)
|
60
|
+
@already_composed_path = T.let(@custom_dir + "bundle_is_composed", Pathname)
|
60
61
|
|
61
62
|
dependencies, bundler_version = load_dependencies
|
62
63
|
@dependencies = T.let(dependencies, T::Hash[String, T.untyped])
|
@@ -71,6 +72,23 @@ module RubyLsp
|
|
71
72
|
def setup!
|
72
73
|
raise BundleNotLocked if !@launcher && @gemfile&.exist? && !@lockfile&.exist?
|
73
74
|
|
75
|
+
# If the bundle was composed ahead of time using our custom `rubyLsp/composeBundle` request, then we can skip the
|
76
|
+
# entire process and just return the composed environment
|
77
|
+
if @already_composed_path.exist?
|
78
|
+
$stderr.puts("Ruby LSP> Composed bundle was set up ahead of time. Skipping...")
|
79
|
+
@already_composed_path.delete
|
80
|
+
|
81
|
+
env = bundler_settings_as_env
|
82
|
+
env["BUNDLE_GEMFILE"] = @custom_gemfile.exist? ? @custom_gemfile.to_s : @gemfile.to_s
|
83
|
+
|
84
|
+
if env["BUNDLE_PATH"]
|
85
|
+
env["BUNDLE_PATH"] = File.expand_path(env["BUNDLE_PATH"], @project_path)
|
86
|
+
end
|
87
|
+
|
88
|
+
env["BUNDLER_VERSION"] = @bundler_version.to_s if @bundler_version
|
89
|
+
return env
|
90
|
+
end
|
91
|
+
|
74
92
|
# Automatically create and ignore the .ruby-lsp folder for users
|
75
93
|
@custom_dir.mkpath unless @custom_dir.exist?
|
76
94
|
ignore_file = @custom_dir + ".gitignore"
|
@@ -223,8 +241,7 @@ module RubyLsp
|
|
223
241
|
|
224
242
|
# If either the Gemfile or the lockfile have been modified during the process of setting up the bundle, retry
|
225
243
|
# composing the bundle from scratch
|
226
|
-
|
227
|
-
if @gemfile && @lockfile
|
244
|
+
if @gemfile&.exist? && @lockfile&.exist?
|
228
245
|
current_gemfile_hash = Digest::SHA256.hexdigest(@gemfile.read)
|
229
246
|
current_lockfile_hash = Digest::SHA256.hexdigest(@lockfile.read)
|
230
247
|
|
data/lib/ruby_lsp/utils.rb
CHANGED
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
|
@@ -218,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
218
|
- !ruby/object:Gem::Version
|
219
219
|
version: '0'
|
220
220
|
requirements: []
|
221
|
-
rubygems_version: 3.6.
|
221
|
+
rubygems_version: 3.6.3
|
222
222
|
specification_version: 4
|
223
223
|
summary: An opinionated language server for Ruby
|
224
224
|
test_files: []
|