ruby-lsp 0.7.5 → 0.8.0
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 +8 -19
- data/exe/ruby-lsp-check +2 -2
- data/lib/core_ext/uri.rb +45 -0
- data/lib/ruby_lsp/document.rb +3 -3
- data/lib/ruby_lsp/executor.rb +22 -19
- data/lib/ruby_lsp/extension.rb +24 -0
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listener.rb +15 -14
- data/lib/ruby_lsp/requests/code_actions.rb +3 -3
- data/lib/ruby_lsp/requests/code_lens.rb +10 -24
- data/lib/ruby_lsp/requests/definition.rb +5 -5
- data/lib/ruby_lsp/requests/diagnostics.rb +3 -2
- data/lib/ruby_lsp/requests/document_link.rb +4 -3
- data/lib/ruby_lsp/requests/formatting.rb +3 -2
- data/lib/ruby_lsp/requests/hover.rb +4 -18
- data/lib/ruby_lsp/requests/support/formatter_runner.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +2 -2
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +2 -3
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +2 -3
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -2
- data/lib/ruby_lsp/server.rb +10 -2
- data/lib/ruby_lsp/setup_bundler.rb +87 -49
- data/lib/ruby_lsp/store.rb +14 -13
- data/lib/ruby_lsp/utils.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed62114ae68bf84cf95f96a2517bc2e729ebaf572500b3871f253bc4b584d7ba
|
|
4
|
+
data.tar.gz: d34dd2719e3b8088f59f4c38988b3fa7058c866a0d34b8b0b0ea391e18ec64d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d9d7bdf1a6ce4d3a7711a0032236d25d3b79682239f801354c15c01661a8e3b1110be90dd503e1d75dcd442520893d6cc852ef5a1eaeeb5a0834305a1c3350c7
|
|
7
|
+
data.tar.gz: f55f82e138cb65fb7450afe86092d294bca06bcd6043439723567ed6538a1b373eed10ba5f3702e9d17f16fc4faa6b5447c048fb5683388e52bea1588d23dc63
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.8.0
|
data/exe/ruby-lsp
CHANGED
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# We should make sure that, if we're running on a bundler project, that it has a Gemfile.lock
|
|
5
|
-
if File.exist?("Gemfile") && !File.exist?("Gemfile.lock")
|
|
6
|
-
warn("Project contains a Gemfile, but no Gemfile.lock. Run `bundle install` to lock gems and restart the server")
|
|
7
|
-
exit(78)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
4
|
# When we're running without bundler, then we need to make sure the custom bundle is fully configured and re-execute
|
|
11
5
|
# using `BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle exec ruby-lsp` so that we have access to the gems that are a part of
|
|
12
6
|
# the application's bundle
|
|
13
|
-
if ENV["BUNDLE_GEMFILE"].nil?
|
|
7
|
+
if ENV["BUNDLE_GEMFILE"].nil?
|
|
14
8
|
require_relative "../lib/ruby_lsp/setup_bundler"
|
|
15
|
-
RubyLsp::SetupBundler.new(Dir.pwd).setup!
|
|
16
|
-
|
|
17
|
-
# In some cases, like when the `ruby-lsp` is already a part of the bundle, we don't generate `.ruby-lsp/Gemfile`.
|
|
18
|
-
# However, we still want to run the server with `bundle exec`. We need to make sure we're pointing to the right
|
|
19
|
-
# `Gemfile`
|
|
20
|
-
bundle_gemfile = File.exist?(".ruby-lsp/Gemfile") ? ".ruby-lsp/Gemfile" : "Gemfile"
|
|
21
9
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
begin
|
|
11
|
+
bundle_gemfile, bundle_path = RubyLsp::SetupBundler.new(Dir.pwd).setup!
|
|
12
|
+
rescue RubyLsp::SetupBundler::BundleNotLocked
|
|
13
|
+
warn("Project contains a Gemfile, but no Gemfile.lock. Run `bundle install` to lock gems and restart the server")
|
|
14
|
+
exit(78)
|
|
15
|
+
end
|
|
27
16
|
|
|
28
17
|
env = { "BUNDLE_GEMFILE" => bundle_gemfile }
|
|
29
|
-
env["BUNDLE_PATH"] =
|
|
18
|
+
env["BUNDLE_PATH"] = bundle_path if bundle_path
|
|
30
19
|
exit exec(env, "bundle exec ruby-lsp #{ARGV.join(" ")}")
|
|
31
20
|
end
|
|
32
21
|
|
data/exe/ruby-lsp-check
CHANGED
|
@@ -30,13 +30,13 @@ message_queue = Thread::Queue.new
|
|
|
30
30
|
executor = RubyLsp::Executor.new(store, message_queue)
|
|
31
31
|
|
|
32
32
|
files.each_with_index do |file, index|
|
|
33
|
-
uri = "file://#{file}"
|
|
33
|
+
uri = URI("file://#{file}")
|
|
34
34
|
store.set(uri: uri, source: File.read(file), version: 1)
|
|
35
35
|
|
|
36
36
|
# Executing any of the automatic requests will execute all of them, so here we just pick one
|
|
37
37
|
result = executor.execute({
|
|
38
38
|
method: "textDocument/documentSymbol",
|
|
39
|
-
params: { textDocument: { uri: uri } },
|
|
39
|
+
params: { textDocument: { uri: uri.to_s } },
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
error = result.error
|
data/lib/core_ext/uri.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module URI
|
|
5
|
+
class Generic
|
|
6
|
+
class << self
|
|
7
|
+
extend T::Sig
|
|
8
|
+
|
|
9
|
+
sig { params(path: String, scheme: String).returns(URI::Generic) }
|
|
10
|
+
def from_path(path:, scheme: "file")
|
|
11
|
+
# On Windows, if the path begins with the disk name, we need to add a leading slash to make it a valid URI
|
|
12
|
+
escaped_path = if /^[A-Z]:/.match?(path)
|
|
13
|
+
DEFAULT_PARSER.escape("/#{path}")
|
|
14
|
+
else
|
|
15
|
+
DEFAULT_PARSER.escape(path)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
build(scheme: scheme, path: escaped_path)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
extend T::Sig
|
|
23
|
+
|
|
24
|
+
sig { returns(T.nilable(String)) }
|
|
25
|
+
def to_standardized_path
|
|
26
|
+
parsed_path = path
|
|
27
|
+
return unless parsed_path
|
|
28
|
+
|
|
29
|
+
# On Windows, when we're getting the file system path back from the URI, we need to remove the leading forward
|
|
30
|
+
# slash
|
|
31
|
+
actual_path = if %r{^/[A-Z]:}.match?(parsed_path)
|
|
32
|
+
parsed_path.delete_prefix("/")
|
|
33
|
+
else
|
|
34
|
+
parsed_path
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
CGI.unescape(actual_path)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
sig { returns(String) }
|
|
41
|
+
def storage_key
|
|
42
|
+
T.must(to_standardized_path || opaque)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/ruby_lsp/document.rb
CHANGED
|
@@ -18,16 +18,16 @@ module RubyLsp
|
|
|
18
18
|
sig { returns(Integer) }
|
|
19
19
|
attr_reader :version
|
|
20
20
|
|
|
21
|
-
sig { returns(
|
|
21
|
+
sig { returns(URI::Generic) }
|
|
22
22
|
attr_reader :uri
|
|
23
23
|
|
|
24
|
-
sig { params(source: String, version: Integer, uri:
|
|
24
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, encoding: String).void }
|
|
25
25
|
def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8)
|
|
26
26
|
@cache = T.let({}, T::Hash[String, T.untyped])
|
|
27
27
|
@encoding = T.let(encoding, String)
|
|
28
28
|
@source = T.let(source, String)
|
|
29
29
|
@version = T.let(version, Integer)
|
|
30
|
-
@uri = T.let(uri,
|
|
30
|
+
@uri = T.let(uri, URI::Generic)
|
|
31
31
|
@unparsed_edits = T.let([], T::Array[EditShape])
|
|
32
32
|
@syntax_error = T.let(false, T::Boolean)
|
|
33
33
|
@tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
|
data/lib/ruby_lsp/executor.rb
CHANGED
|
@@ -35,7 +35,7 @@ module RubyLsp
|
|
|
35
35
|
|
|
36
36
|
sig { params(request: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
|
|
37
37
|
def run(request)
|
|
38
|
-
uri = request.dig(:params, :textDocument, :uri)
|
|
38
|
+
uri = URI(request.dig(:params, :textDocument, :uri).to_s)
|
|
39
39
|
|
|
40
40
|
case request[:method]
|
|
41
41
|
when "initialize"
|
|
@@ -70,7 +70,7 @@ module RubyLsp
|
|
|
70
70
|
when "textDocument/didClose"
|
|
71
71
|
@message_queue << Notification.new(
|
|
72
72
|
message: "textDocument/publishDiagnostics",
|
|
73
|
-
params: Interface::PublishDiagnosticsParams.new(uri: uri, diagnostics: []),
|
|
73
|
+
params: Interface::PublishDiagnosticsParams.new(uri: uri.to_s, diagnostics: []),
|
|
74
74
|
)
|
|
75
75
|
|
|
76
76
|
text_document_did_close(uri)
|
|
@@ -174,12 +174,12 @@ module RubyLsp
|
|
|
174
174
|
end
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
-
sig { params(uri:
|
|
177
|
+
sig { params(uri: URI::Generic, range: T.nilable(Document::RangeShape)).returns({ ast: String }) }
|
|
178
178
|
def show_syntax_tree(uri, range)
|
|
179
179
|
{ ast: Requests::ShowSyntaxTree.new(@store.get(uri), range).run }
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
-
sig { params(uri:
|
|
182
|
+
sig { params(uri: URI::Generic, position: Document::PositionShape).returns(T.nilable(Interface::Location)) }
|
|
183
183
|
def definition(uri, position)
|
|
184
184
|
document = @store.get(uri)
|
|
185
185
|
return if document.syntax_error?
|
|
@@ -192,7 +192,7 @@ module RubyLsp
|
|
|
192
192
|
base_listener.response
|
|
193
193
|
end
|
|
194
194
|
|
|
195
|
-
sig { params(uri:
|
|
195
|
+
sig { params(uri: URI::Generic).returns(T::Array[Interface::FoldingRange]) }
|
|
196
196
|
def folding_range(uri)
|
|
197
197
|
@store.cache_fetch(uri, "textDocument/foldingRange") do |document|
|
|
198
198
|
Requests::FoldingRanges.new(document).run
|
|
@@ -201,7 +201,7 @@ module RubyLsp
|
|
|
201
201
|
|
|
202
202
|
sig do
|
|
203
203
|
params(
|
|
204
|
-
uri:
|
|
204
|
+
uri: URI::Generic,
|
|
205
205
|
position: Document::PositionShape,
|
|
206
206
|
).returns(T.nilable(Interface::Hover))
|
|
207
207
|
end
|
|
@@ -227,19 +227,19 @@ module RubyLsp
|
|
|
227
227
|
hover.response
|
|
228
228
|
end
|
|
229
229
|
|
|
230
|
-
sig { params(uri:
|
|
230
|
+
sig { params(uri: URI::Generic, content_changes: T::Array[Document::EditShape], version: Integer).returns(Object) }
|
|
231
231
|
def text_document_did_change(uri, content_changes, version)
|
|
232
232
|
@store.push_edits(uri: uri, edits: content_changes, version: version)
|
|
233
233
|
VOID
|
|
234
234
|
end
|
|
235
235
|
|
|
236
|
-
sig { params(uri:
|
|
236
|
+
sig { params(uri: URI::Generic, text: String, version: Integer).returns(Object) }
|
|
237
237
|
def text_document_did_open(uri, text, version)
|
|
238
238
|
@store.set(uri: uri, source: text, version: version)
|
|
239
239
|
VOID
|
|
240
240
|
end
|
|
241
241
|
|
|
242
|
-
sig { params(uri:
|
|
242
|
+
sig { params(uri: URI::Generic).returns(Object) }
|
|
243
243
|
def text_document_did_close(uri)
|
|
244
244
|
@store.delete(uri)
|
|
245
245
|
VOID
|
|
@@ -247,7 +247,7 @@ module RubyLsp
|
|
|
247
247
|
|
|
248
248
|
sig do
|
|
249
249
|
params(
|
|
250
|
-
uri:
|
|
250
|
+
uri: URI::Generic,
|
|
251
251
|
positions: T::Array[Document::PositionShape],
|
|
252
252
|
).returns(T.nilable(T::Array[T.nilable(Requests::Support::SelectionRange)]))
|
|
253
253
|
end
|
|
@@ -270,7 +270,7 @@ module RubyLsp
|
|
|
270
270
|
end
|
|
271
271
|
end
|
|
272
272
|
|
|
273
|
-
sig { params(uri:
|
|
273
|
+
sig { params(uri: URI::Generic).returns(T.nilable(T::Array[Interface::TextEdit])) }
|
|
274
274
|
def formatting(uri)
|
|
275
275
|
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
|
276
276
|
return if @store.formatter == "none"
|
|
@@ -280,7 +280,7 @@ module RubyLsp
|
|
|
280
280
|
|
|
281
281
|
sig do
|
|
282
282
|
params(
|
|
283
|
-
uri:
|
|
283
|
+
uri: URI::Generic,
|
|
284
284
|
position: Document::PositionShape,
|
|
285
285
|
character: String,
|
|
286
286
|
).returns(T::Array[Interface::TextEdit])
|
|
@@ -291,7 +291,7 @@ module RubyLsp
|
|
|
291
291
|
|
|
292
292
|
sig do
|
|
293
293
|
params(
|
|
294
|
-
uri:
|
|
294
|
+
uri: URI::Generic,
|
|
295
295
|
position: Document::PositionShape,
|
|
296
296
|
).returns(T.nilable(T::Array[Interface::DocumentHighlight]))
|
|
297
297
|
end
|
|
@@ -306,7 +306,7 @@ module RubyLsp
|
|
|
306
306
|
listener.response
|
|
307
307
|
end
|
|
308
308
|
|
|
309
|
-
sig { params(uri:
|
|
309
|
+
sig { params(uri: URI::Generic, range: Document::RangeShape).returns(T.nilable(T::Array[Interface::InlayHint])) }
|
|
310
310
|
def inlay_hint(uri, range)
|
|
311
311
|
document = @store.get(uri)
|
|
312
312
|
return if document.syntax_error?
|
|
@@ -322,7 +322,7 @@ module RubyLsp
|
|
|
322
322
|
|
|
323
323
|
sig do
|
|
324
324
|
params(
|
|
325
|
-
uri:
|
|
325
|
+
uri: URI::Generic,
|
|
326
326
|
range: Document::RangeShape,
|
|
327
327
|
context: T::Hash[Symbol, T.untyped],
|
|
328
328
|
).returns(T.nilable(T::Array[Interface::CodeAction]))
|
|
@@ -335,7 +335,7 @@ module RubyLsp
|
|
|
335
335
|
|
|
336
336
|
sig { params(params: T::Hash[Symbol, T.untyped]).returns(Interface::CodeAction) }
|
|
337
337
|
def code_action_resolve(params)
|
|
338
|
-
uri = params.dig(:data, :uri)
|
|
338
|
+
uri = URI(params.dig(:data, :uri))
|
|
339
339
|
document = @store.get(uri)
|
|
340
340
|
result = Requests::CodeActionResolve.new(document, params).run
|
|
341
341
|
|
|
@@ -363,7 +363,7 @@ module RubyLsp
|
|
|
363
363
|
end
|
|
364
364
|
end
|
|
365
365
|
|
|
366
|
-
sig { params(uri:
|
|
366
|
+
sig { params(uri: URI::Generic).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
|
|
367
367
|
def diagnostic(uri)
|
|
368
368
|
response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
|
|
369
369
|
Requests::Diagnostics.new(document).run
|
|
@@ -372,7 +372,7 @@ module RubyLsp
|
|
|
372
372
|
Interface::FullDocumentDiagnosticReport.new(kind: "full", items: response.map(&:to_lsp_diagnostic)) if response
|
|
373
373
|
end
|
|
374
374
|
|
|
375
|
-
sig { params(uri:
|
|
375
|
+
sig { params(uri: URI::Generic, range: Document::RangeShape).returns(Interface::SemanticTokens) }
|
|
376
376
|
def semantic_tokens_range(uri, range)
|
|
377
377
|
document = @store.get(uri)
|
|
378
378
|
start_line = range.dig(:start, :line)
|
|
@@ -390,7 +390,10 @@ module RubyLsp
|
|
|
390
390
|
end
|
|
391
391
|
|
|
392
392
|
sig do
|
|
393
|
-
params(
|
|
393
|
+
params(
|
|
394
|
+
uri: URI::Generic,
|
|
395
|
+
position: Document::PositionShape,
|
|
396
|
+
).returns(T.nilable(T::Array[Interface::CompletionItem]))
|
|
394
397
|
end
|
|
395
398
|
def completion(uri, position)
|
|
396
399
|
document = @store.get(uri)
|
data/lib/ruby_lsp/extension.rb
CHANGED
|
@@ -97,8 +97,32 @@ module RubyLsp
|
|
|
97
97
|
sig { abstract.void }
|
|
98
98
|
def activate; end
|
|
99
99
|
|
|
100
|
+
# Each extension should implement `MyExtension#deactivate` and use to perform any clean up, like shutting down a
|
|
101
|
+
# child process
|
|
102
|
+
sig { abstract.void }
|
|
103
|
+
def deactivate; end
|
|
104
|
+
|
|
100
105
|
# Extensions should override the `name` method to return the extension name
|
|
101
106
|
sig { abstract.returns(String) }
|
|
102
107
|
def name; end
|
|
108
|
+
|
|
109
|
+
# Creates a new CodeLens listener. This method is invoked on every CodeLens request
|
|
110
|
+
sig do
|
|
111
|
+
overridable.params(
|
|
112
|
+
uri: URI::Generic,
|
|
113
|
+
emitter: EventEmitter,
|
|
114
|
+
message_queue: Thread::Queue,
|
|
115
|
+
).returns(T.nilable(Listener[T::Array[Interface::CodeLens]]))
|
|
116
|
+
end
|
|
117
|
+
def create_code_lens_listener(uri, emitter, message_queue); end
|
|
118
|
+
|
|
119
|
+
# Creates a new Hover listener. This method is invoked on every Hover request
|
|
120
|
+
sig do
|
|
121
|
+
overridable.params(
|
|
122
|
+
emitter: EventEmitter,
|
|
123
|
+
message_queue: Thread::Queue,
|
|
124
|
+
).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
|
|
125
|
+
end
|
|
126
|
+
def create_hover_listener(emitter, message_queue); end
|
|
103
127
|
end
|
|
104
128
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
data/lib/ruby_lsp/listener.rb
CHANGED
|
@@ -18,25 +18,26 @@ module RubyLsp
|
|
|
18
18
|
def initialize(emitter, message_queue)
|
|
19
19
|
@emitter = emitter
|
|
20
20
|
@message_queue = message_queue
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class << self
|
|
24
|
-
extend T::Sig
|
|
25
|
-
|
|
26
|
-
sig { returns(T::Array[T.class_of(Listener)]) }
|
|
27
|
-
def listeners
|
|
28
|
-
@listeners ||= T.let([], T.nilable(T::Array[T.class_of(Listener)]))
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
sig { params(listener: T.class_of(Listener)).void }
|
|
32
|
-
def add_listener(listener)
|
|
33
|
-
listeners << listener
|
|
34
|
-
end
|
|
21
|
+
@external_listeners = T.let([], T::Array[RubyLsp::Listener[ResponseType]])
|
|
35
22
|
end
|
|
36
23
|
|
|
37
24
|
# Override this method with an attr_reader that returns the response of your listener. The listener should
|
|
38
25
|
# accumulate results in a @response variable and then provide the reader so that it is accessible
|
|
39
26
|
sig { abstract.returns(ResponseType) }
|
|
40
27
|
def response; end
|
|
28
|
+
|
|
29
|
+
# Merge responses from all external listeners into the base listener's response. We do this to return a single
|
|
30
|
+
# response to the editor including the results of all extensions
|
|
31
|
+
sig { void }
|
|
32
|
+
def merge_external_listeners_responses!
|
|
33
|
+
@external_listeners.each { |l| merge_response!(l) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Does nothing by default. Requests that accept extensions should override this method to define how to merge
|
|
37
|
+
# responses coming from external listeners
|
|
38
|
+
sig { overridable.params(other: Listener[T.untyped]).returns(T.self_type) }
|
|
39
|
+
def merge_response!(other)
|
|
40
|
+
self
|
|
41
|
+
end
|
|
41
42
|
end
|
|
42
43
|
end
|
|
@@ -29,7 +29,7 @@ module RubyLsp
|
|
|
29
29
|
def initialize(document, range, context)
|
|
30
30
|
super(document)
|
|
31
31
|
|
|
32
|
-
@uri = T.let(document.uri,
|
|
32
|
+
@uri = T.let(document.uri, URI::Generic)
|
|
33
33
|
@range = range
|
|
34
34
|
@context = context
|
|
35
35
|
end
|
|
@@ -63,14 +63,14 @@ module RubyLsp
|
|
|
63
63
|
)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
sig { params(range: Document::RangeShape, uri:
|
|
66
|
+
sig { params(range: Document::RangeShape, uri: URI::Generic).returns(Interface::CodeAction) }
|
|
67
67
|
def refactor_code_action(range, uri)
|
|
68
68
|
Interface::CodeAction.new(
|
|
69
69
|
title: "Refactor: Extract Variable",
|
|
70
70
|
kind: Constant::CodeActionKind::REFACTOR_EXTRACT,
|
|
71
71
|
data: {
|
|
72
72
|
range: range,
|
|
73
|
-
uri: uri,
|
|
73
|
+
uri: uri.to_s,
|
|
74
74
|
},
|
|
75
75
|
)
|
|
76
76
|
end
|
|
@@ -31,15 +31,17 @@ module RubyLsp
|
|
|
31
31
|
sig { override.returns(ResponseType) }
|
|
32
32
|
attr_reader :response
|
|
33
33
|
|
|
34
|
-
sig { params(uri:
|
|
34
|
+
sig { params(uri: URI::Generic, emitter: EventEmitter, message_queue: Thread::Queue, test_library: String).void }
|
|
35
35
|
def initialize(uri, emitter, message_queue, test_library)
|
|
36
36
|
super(emitter, message_queue)
|
|
37
37
|
|
|
38
|
-
@uri = T.let(uri,
|
|
39
|
-
@external_listeners
|
|
38
|
+
@uri = T.let(uri, URI::Generic)
|
|
39
|
+
@external_listeners.concat(
|
|
40
|
+
Extension.extensions.filter_map { |ext| ext.create_code_lens_listener(uri, emitter, message_queue) },
|
|
41
|
+
)
|
|
40
42
|
@test_library = T.let(test_library, String)
|
|
41
43
|
@response = T.let([], ResponseType)
|
|
42
|
-
@path = T.let(
|
|
44
|
+
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
|
43
45
|
# visibility_stack is a stack of [current_visibility, previous_visibility]
|
|
44
46
|
@visibility_stack = T.let([["public", "public"]], T::Array[T::Array[T.nilable(String)]])
|
|
45
47
|
@class_stack = T.let([], T::Array[String])
|
|
@@ -55,22 +57,6 @@ module RubyLsp
|
|
|
55
57
|
:after_call,
|
|
56
58
|
:on_vcall,
|
|
57
59
|
)
|
|
58
|
-
|
|
59
|
-
register_external_listeners!
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
sig { void }
|
|
63
|
-
def register_external_listeners!
|
|
64
|
-
self.class.listeners.each do |l|
|
|
65
|
-
@external_listeners << T.unsafe(l).new(@uri, @emitter, @message_queue)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
sig { void }
|
|
70
|
-
def merge_external_listeners_responses!
|
|
71
|
-
@external_listeners.each do |l|
|
|
72
|
-
merge_response!(l)
|
|
73
|
-
end
|
|
74
60
|
end
|
|
75
61
|
|
|
76
62
|
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
|
@@ -120,7 +106,7 @@ module RubyLsp
|
|
|
120
106
|
if ACCESS_MODIFIERS.include?(node_message) && node.arguments.parts.any?
|
|
121
107
|
visibility, _ = @visibility_stack.pop
|
|
122
108
|
@visibility_stack.push([node_message, visibility])
|
|
123
|
-
elsif @path
|
|
109
|
+
elsif @path&.include?("Gemfile") && node_message.include?("gem") && node.arguments.parts.any?
|
|
124
110
|
remote = resolve_gem_remote(node)
|
|
125
111
|
return unless remote
|
|
126
112
|
|
|
@@ -163,7 +149,7 @@ module RubyLsp
|
|
|
163
149
|
end
|
|
164
150
|
end
|
|
165
151
|
|
|
166
|
-
sig { params(other: Listener[ResponseType]).returns(T.self_type) }
|
|
152
|
+
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
|
167
153
|
def merge_response!(other)
|
|
168
154
|
@response.concat(other.response)
|
|
169
155
|
self
|
|
@@ -174,7 +160,7 @@ module RubyLsp
|
|
|
174
160
|
sig { params(node: SyntaxTree::Node, name: String, command: String, kind: Symbol).void }
|
|
175
161
|
def add_test_code_lens(node, name:, command:, kind:)
|
|
176
162
|
# don't add code lenses if the test library is not supported or unknown
|
|
177
|
-
return unless SUPPORTED_TEST_LIBRARIES.include?(@test_library)
|
|
163
|
+
return unless SUPPORTED_TEST_LIBRARIES.include?(@test_library) && @path
|
|
178
164
|
|
|
179
165
|
arguments = [
|
|
180
166
|
@path,
|
|
@@ -231,7 +217,7 @@ module RubyLsp
|
|
|
231
217
|
|
|
232
218
|
sig { params(class_name: String, method_name: T.nilable(String)).returns(String) }
|
|
233
219
|
def generate_test_command(class_name:, method_name: nil)
|
|
234
|
-
command = BASE_COMMAND + @path
|
|
220
|
+
command = BASE_COMMAND + T.must(@path)
|
|
235
221
|
|
|
236
222
|
case @test_library
|
|
237
223
|
when "minitest"
|
|
@@ -25,7 +25,7 @@ module RubyLsp
|
|
|
25
25
|
sig { override.returns(ResponseType) }
|
|
26
26
|
attr_reader :response
|
|
27
27
|
|
|
28
|
-
sig { params(uri:
|
|
28
|
+
sig { params(uri: URI::Generic, emitter: EventEmitter, message_queue: Thread::Queue).void }
|
|
29
29
|
def initialize(uri, emitter, message_queue)
|
|
30
30
|
super(emitter, message_queue)
|
|
31
31
|
|
|
@@ -53,7 +53,7 @@ module RubyLsp
|
|
|
53
53
|
|
|
54
54
|
if candidate
|
|
55
55
|
@response = Interface::Location.new(
|
|
56
|
-
uri:
|
|
56
|
+
uri: URI::Generic.from_path(path: candidate).to_s,
|
|
57
57
|
range: Interface::Range.new(
|
|
58
58
|
start: Interface::Position.new(line: 0, character: 0),
|
|
59
59
|
end: Interface::Position.new(line: 0, character: 0),
|
|
@@ -61,13 +61,13 @@ module RubyLsp
|
|
|
61
61
|
)
|
|
62
62
|
end
|
|
63
63
|
when "require_relative"
|
|
64
|
-
|
|
65
|
-
current_folder = Pathname.new(
|
|
64
|
+
path = @uri.to_standardized_path
|
|
65
|
+
current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : Dir.pwd
|
|
66
66
|
candidate = File.expand_path(File.join(current_folder, required_file))
|
|
67
67
|
|
|
68
68
|
if candidate
|
|
69
69
|
@response = Interface::Location.new(
|
|
70
|
-
uri:
|
|
70
|
+
uri: URI::Generic.from_path(path: candidate).to_s,
|
|
71
71
|
range: Interface::Range.new(
|
|
72
72
|
start: Interface::Position.new(line: 0, character: 0),
|
|
73
73
|
end: Interface::Position.new(line: 0, character: 0),
|
|
@@ -25,7 +25,7 @@ module RubyLsp
|
|
|
25
25
|
def initialize(document)
|
|
26
26
|
super(document)
|
|
27
27
|
|
|
28
|
-
@uri = T.let(document.uri,
|
|
28
|
+
@uri = T.let(document.uri, URI::Generic)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
sig { override.returns(T.nilable(T.all(T::Array[Support::RuboCopDiagnostic], Object))) }
|
|
@@ -36,7 +36,8 @@ module RubyLsp
|
|
|
36
36
|
return unless defined?(Support::RuboCopDiagnosticsRunner)
|
|
37
37
|
|
|
38
38
|
# Don't try to run RuboCop diagnostics for files outside the current working directory
|
|
39
|
-
|
|
39
|
+
path = @uri.to_standardized_path
|
|
40
|
+
return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
|
|
40
41
|
|
|
41
42
|
Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
|
|
42
43
|
end
|
|
@@ -75,13 +75,14 @@ module RubyLsp
|
|
|
75
75
|
sig { override.returns(ResponseType) }
|
|
76
76
|
attr_reader :response
|
|
77
77
|
|
|
78
|
-
sig { params(uri:
|
|
78
|
+
sig { params(uri: URI::Generic, emitter: EventEmitter, message_queue: Thread::Queue).void }
|
|
79
79
|
def initialize(uri, emitter, message_queue)
|
|
80
80
|
super(emitter, message_queue)
|
|
81
81
|
|
|
82
82
|
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
|
83
83
|
# in the URI
|
|
84
|
-
|
|
84
|
+
path = uri.to_standardized_path
|
|
85
|
+
version_match = path ? /(?<=%40)[\d.]+(?=\.rbi$)/.match(path) : nil
|
|
85
86
|
@gem_version = T.let(version_match && version_match[0], T.nilable(String))
|
|
86
87
|
@response = T.let([], T::Array[Interface::DocumentLink])
|
|
87
88
|
|
|
@@ -95,7 +96,7 @@ module RubyLsp
|
|
|
95
96
|
|
|
96
97
|
uri = T.cast(URI(T.must(match[0])), URI::Source)
|
|
97
98
|
gem_version = T.must(resolve_version(uri))
|
|
98
|
-
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
|
|
99
|
+
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(uri.path))
|
|
99
100
|
return if file_path.nil?
|
|
100
101
|
|
|
101
102
|
@response << Interface::DocumentLink.new(
|
|
@@ -58,7 +58,7 @@ module RubyLsp
|
|
|
58
58
|
def initialize(document, formatter: "auto")
|
|
59
59
|
super(document)
|
|
60
60
|
|
|
61
|
-
@uri = T.let(document.uri,
|
|
61
|
+
@uri = T.let(document.uri, URI::Generic)
|
|
62
62
|
@formatter = formatter
|
|
63
63
|
end
|
|
64
64
|
|
|
@@ -67,7 +67,8 @@ module RubyLsp
|
|
|
67
67
|
return if @formatter == "none"
|
|
68
68
|
|
|
69
69
|
# Don't try to format files outside the current working directory
|
|
70
|
-
|
|
70
|
+
path = @uri.to_standardized_path
|
|
71
|
+
return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
|
|
71
72
|
|
|
72
73
|
return if @document.syntax_error?
|
|
73
74
|
|
|
@@ -39,29 +39,15 @@ module RubyLsp
|
|
|
39
39
|
def initialize(emitter, message_queue)
|
|
40
40
|
super
|
|
41
41
|
|
|
42
|
-
@external_listeners
|
|
42
|
+
@external_listeners.concat(
|
|
43
|
+
Extension.extensions.filter_map { |ext| ext.create_hover_listener(emitter, message_queue) },
|
|
44
|
+
)
|
|
43
45
|
@response = T.let(nil, ResponseType)
|
|
44
46
|
emitter.register(self, :on_command, :on_const_path_ref, :on_call)
|
|
45
|
-
|
|
46
|
-
register_external_listeners!
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
sig { void }
|
|
50
|
-
def register_external_listeners!
|
|
51
|
-
self.class.listeners.each do |l|
|
|
52
|
-
@external_listeners << T.unsafe(l).new(@emitter, @message_queue)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
sig { void }
|
|
57
|
-
def merge_external_listeners_responses!
|
|
58
|
-
@external_listeners.each do |l|
|
|
59
|
-
merge_response!(l)
|
|
60
|
-
end
|
|
61
47
|
end
|
|
62
48
|
|
|
63
49
|
# Merges responses from other hover listeners
|
|
64
|
-
sig { params(other: Listener[ResponseType]).returns(T.self_type) }
|
|
50
|
+
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
|
65
51
|
def merge_response!(other)
|
|
66
52
|
other_response = other.response
|
|
67
53
|
return self unless other_response
|
|
@@ -19,7 +19,7 @@ module RubyLsp
|
|
|
19
19
|
T::Hash[Symbol, Integer],
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
-
sig { params(offense: RuboCop::Cop::Offense, uri:
|
|
22
|
+
sig { params(offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
|
|
23
23
|
def initialize(offense, uri)
|
|
24
24
|
@offense = offense
|
|
25
25
|
@uri = uri
|
|
@@ -34,7 +34,7 @@ module RubyLsp
|
|
|
34
34
|
document_changes: [
|
|
35
35
|
Interface::TextDocumentEdit.new(
|
|
36
36
|
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
|
|
37
|
-
uri: @uri,
|
|
37
|
+
uri: @uri.to_s,
|
|
38
38
|
version: nil,
|
|
39
39
|
),
|
|
40
40
|
edits: @offense.correctable? ? offense_replacements : [],
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
return unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
|
|
5
5
|
|
|
6
|
-
require "cgi"
|
|
7
6
|
require "singleton"
|
|
8
7
|
|
|
9
8
|
module RubyLsp
|
|
@@ -19,9 +18,9 @@ module RubyLsp
|
|
|
19
18
|
@runner = T.let(RuboCopRunner.new, RuboCopRunner)
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
sig { params(uri:
|
|
21
|
+
sig { params(uri: URI::Generic, document: Document).returns(T::Array[Support::RuboCopDiagnostic]) }
|
|
23
22
|
def run(uri, document)
|
|
24
|
-
filename =
|
|
23
|
+
filename = T.must(uri.to_standardized_path || uri.opaque)
|
|
25
24
|
# Invoke RuboCop with just this file in `paths`
|
|
26
25
|
@runner.run(filename, document.source)
|
|
27
26
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
return unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
|
|
5
5
|
|
|
6
|
-
require "cgi"
|
|
7
6
|
require "singleton"
|
|
8
7
|
|
|
9
8
|
module RubyLsp
|
|
@@ -21,9 +20,9 @@ module RubyLsp
|
|
|
21
20
|
@runner = T.let(RuboCopRunner.new("-a"), RuboCopRunner)
|
|
22
21
|
end
|
|
23
22
|
|
|
24
|
-
sig { override.params(uri:
|
|
23
|
+
sig { override.params(uri: URI::Generic, document: Document).returns(String) }
|
|
25
24
|
def run(uri, document)
|
|
26
|
-
filename =
|
|
25
|
+
filename = T.must(uri.to_standardized_path || uri.opaque)
|
|
27
26
|
|
|
28
27
|
# Invoke RuboCop with just this file in `paths`
|
|
29
28
|
@runner.run(filename, document.source)
|
|
@@ -26,9 +26,10 @@ module RubyLsp
|
|
|
26
26
|
)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
sig { override.params(uri:
|
|
29
|
+
sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
|
|
30
30
|
def run(uri, document)
|
|
31
|
-
relative_path = Pathname.new(
|
|
31
|
+
relative_path = Pathname.new(T.must(uri.to_standardized_path || uri.opaque))
|
|
32
|
+
.relative_path_from(T.must(WORKSPACE_URI.to_standardized_path))
|
|
32
33
|
return if @options.ignore_files.any? { |pattern| File.fnmatch(pattern, relative_path) }
|
|
33
34
|
|
|
34
35
|
SyntaxTree.format(
|
data/lib/ruby_lsp/server.rb
CHANGED
|
@@ -86,6 +86,7 @@ module RubyLsp
|
|
|
86
86
|
@message_dispatcher.join
|
|
87
87
|
@store.clear
|
|
88
88
|
|
|
89
|
+
Extension.extensions.each(&:deactivate)
|
|
89
90
|
finalize_request(Result.new(response: nil), request)
|
|
90
91
|
when "exit"
|
|
91
92
|
# We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
|
|
@@ -105,7 +106,7 @@ module RubyLsp
|
|
|
105
106
|
# source. Altering the source reference during parsing will put the parser in an invalid internal state,
|
|
106
107
|
# since it started parsing with one source but then it changed in the middle
|
|
107
108
|
uri = request.dig(:params, :textDocument, :uri)
|
|
108
|
-
@store.get(uri).parse if uri
|
|
109
|
+
@store.get(URI(uri)).parse if uri
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
@job_queue << job
|
|
@@ -192,7 +193,14 @@ module RubyLsp
|
|
|
192
193
|
params[:backtrace] = backtrace.map { |bt| bt.sub(/^#{Dir.home}/, "~") }.join("\n") if backtrace
|
|
193
194
|
end
|
|
194
195
|
|
|
195
|
-
|
|
196
|
+
if uri
|
|
197
|
+
home = URI::Generic.from_path(path: Dir.home)
|
|
198
|
+
|
|
199
|
+
parsed_uri = URI(uri)
|
|
200
|
+
path = parsed_uri.path
|
|
201
|
+
params[:uri] = path ? path.sub(T.must(home.path), "~") : parsed_uri.opaque
|
|
202
|
+
end
|
|
203
|
+
|
|
196
204
|
params
|
|
197
205
|
end
|
|
198
206
|
end
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
require "sorbet-runtime"
|
|
5
5
|
require "bundler"
|
|
6
6
|
require "fileutils"
|
|
7
|
+
require "pathname"
|
|
8
|
+
require "digest"
|
|
7
9
|
|
|
8
10
|
# This file is a script that will configure a custom bundle for the Ruby LSP. The custom bundle allows developers to use
|
|
9
11
|
# the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to the
|
|
@@ -13,107 +15,142 @@ module RubyLsp
|
|
|
13
15
|
class SetupBundler
|
|
14
16
|
extend T::Sig
|
|
15
17
|
|
|
18
|
+
class BundleNotLocked < StandardError; end
|
|
19
|
+
|
|
16
20
|
sig { params(project_path: String).void }
|
|
17
21
|
def initialize(project_path)
|
|
18
22
|
@project_path = project_path
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
|
|
24
|
+
# Custom bundle paths
|
|
25
|
+
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(Dir.pwd), Pathname)
|
|
26
|
+
@custom_gemfile = T.let(@custom_dir + "Gemfile", Pathname)
|
|
27
|
+
@custom_lockfile = T.let(@custom_dir + "Gemfile.lock", Pathname)
|
|
28
|
+
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
|
|
29
|
+
|
|
30
|
+
# Regular bundle paths
|
|
31
|
+
@gemfile = T.let(
|
|
32
|
+
begin
|
|
33
|
+
Bundler.default_gemfile
|
|
34
|
+
rescue Bundler::GemfileNotFound
|
|
35
|
+
nil
|
|
25
36
|
end,
|
|
26
|
-
T
|
|
37
|
+
T.nilable(Pathname),
|
|
27
38
|
)
|
|
39
|
+
@lockfile = T.let(@gemfile ? Bundler.default_lockfile : nil, T.nilable(Pathname))
|
|
40
|
+
|
|
41
|
+
@dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
|
|
28
42
|
end
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
# Setups up the custom bundle and returns the `BUNDLE_GEMFILE` and `BUNDLE_PATH` that should be used for running the
|
|
45
|
+
# server
|
|
46
|
+
sig { returns([String, T.nilable(String)]) }
|
|
31
47
|
def setup!
|
|
32
|
-
|
|
33
|
-
if File.basename(@project_path) == "ruby-lsp"
|
|
34
|
-
warn("Ruby LSP> Skipping custom bundle setup since we're working on the Ruby LSP itself")
|
|
35
|
-
run_bundle_install
|
|
36
|
-
return
|
|
37
|
-
end
|
|
48
|
+
raise BundleNotLocked if @gemfile&.exist? && !@lockfile&.exist?
|
|
38
49
|
|
|
39
50
|
# Do not setup a custom bundle if both `ruby-lsp` and `debug` are already in the Gemfile
|
|
40
51
|
if @dependencies["ruby-lsp"] && @dependencies["debug"]
|
|
41
|
-
warn("Ruby LSP> Skipping custom bundle setup since both `ruby-lsp` and `debug` are already in
|
|
52
|
+
warn("Ruby LSP> Skipping custom bundle setup since both `ruby-lsp` and `debug` are already in #{@gemfile}")
|
|
42
53
|
|
|
43
54
|
# If the user decided to add the `ruby-lsp` and `debug` to their Gemfile after having already run the Ruby LSP,
|
|
44
55
|
# then we need to remove the `.ruby-lsp` folder, otherwise we will run `bundle install` for the top level and
|
|
45
56
|
# try to execute the Ruby LSP using the custom bundle, which will fail since the gems are not installed there
|
|
46
|
-
|
|
47
|
-
run_bundle_install
|
|
48
|
-
return
|
|
57
|
+
@custom_dir.rmtree if @custom_dir.exist?
|
|
58
|
+
return run_bundle_install
|
|
49
59
|
end
|
|
50
60
|
|
|
51
61
|
# Automatically create and ignore the .ruby-lsp folder for users
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
@custom_dir.mkpath unless @custom_dir.exist?
|
|
63
|
+
ignore_file = @custom_dir + ".gitignore"
|
|
64
|
+
ignore_file.write("*") unless ignore_file.exist?
|
|
54
65
|
|
|
55
|
-
|
|
56
|
-
content = custom_gemfile_content
|
|
66
|
+
write_custom_gemfile
|
|
57
67
|
|
|
58
|
-
unless
|
|
59
|
-
|
|
68
|
+
unless @gemfile&.exist? && @lockfile&.exist?
|
|
69
|
+
warn("Ruby LSP> Skipping lockfile copies because there's no top level bundle")
|
|
70
|
+
return run_bundle_install(@custom_gemfile)
|
|
60
71
|
end
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
warn("Ruby LSP> Skipping custom bundle setup since
|
|
67
|
-
run_bundle_install(
|
|
68
|
-
return
|
|
73
|
+
lockfile_contents = @lockfile.read
|
|
74
|
+
current_lockfile_hash = Digest::SHA256.hexdigest(lockfile_contents)
|
|
75
|
+
|
|
76
|
+
if @custom_lockfile.exist? && @lockfile_hash_path.exist? && @lockfile_hash_path.read == current_lockfile_hash
|
|
77
|
+
warn("Ruby LSP> Skipping custom bundle setup since #{@custom_lockfile} already exists and is up to date")
|
|
78
|
+
return run_bundle_install(@custom_gemfile)
|
|
69
79
|
end
|
|
70
80
|
|
|
71
|
-
FileUtils.cp(
|
|
72
|
-
|
|
81
|
+
FileUtils.cp(@lockfile.to_s, @custom_lockfile.to_s)
|
|
82
|
+
@lockfile_hash_path.write(current_lockfile_hash)
|
|
83
|
+
run_bundle_install(@custom_gemfile)
|
|
73
84
|
end
|
|
74
85
|
|
|
75
86
|
private
|
|
76
87
|
|
|
77
|
-
sig { returns(String) }
|
|
78
|
-
def
|
|
88
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
|
89
|
+
def custom_bundle_dependencies
|
|
90
|
+
@custom_bundle_dependencies ||= T.let(
|
|
91
|
+
begin
|
|
92
|
+
if @custom_lockfile.exist?
|
|
93
|
+
ENV["BUNDLE_GEMFILE"] = @custom_gemfile.to_s
|
|
94
|
+
Bundler::LockfileParser.new(@custom_lockfile.read).dependencies
|
|
95
|
+
else
|
|
96
|
+
{}
|
|
97
|
+
end
|
|
98
|
+
end,
|
|
99
|
+
T.nilable(T::Hash[String, T.untyped]),
|
|
100
|
+
)
|
|
101
|
+
ensure
|
|
102
|
+
ENV.delete("BUNDLE_GEMFILE")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
sig { void }
|
|
106
|
+
def write_custom_gemfile
|
|
79
107
|
parts = [
|
|
80
108
|
"# This custom gemfile is automatically generated by the Ruby LSP.",
|
|
81
109
|
"# It should be automatically git ignored, but in any case: do not commit it to your repository.",
|
|
82
110
|
"",
|
|
83
|
-
"eval_gemfile(File.expand_path(\"../Gemfile\", __dir__))",
|
|
84
111
|
]
|
|
85
112
|
|
|
113
|
+
# If there's a top level Gemfile, we want to evaluate from the custom bundle. We get the source from the top level
|
|
114
|
+
# Gemfile, so if there isn't one we need to add a default source
|
|
115
|
+
if @gemfile&.exist?
|
|
116
|
+
parts << "eval_gemfile(File.expand_path(\"../Gemfile\", __dir__))"
|
|
117
|
+
else
|
|
118
|
+
parts.unshift('source "https://rubygems.org"')
|
|
119
|
+
end
|
|
120
|
+
|
|
86
121
|
unless @dependencies["ruby-lsp"]
|
|
87
|
-
parts << 'gem "ruby-lsp", require: false, group: :development
|
|
122
|
+
parts << 'gem "ruby-lsp", require: false, group: :development'
|
|
88
123
|
end
|
|
89
124
|
|
|
90
125
|
unless @dependencies["debug"]
|
|
91
|
-
parts << 'gem "debug", require: false, group: :development, platforms: :mri
|
|
126
|
+
parts << 'gem "debug", require: false, group: :development, platforms: :mri'
|
|
92
127
|
end
|
|
93
128
|
|
|
94
|
-
parts.join("\n")
|
|
129
|
+
content = parts.join("\n")
|
|
130
|
+
@custom_gemfile.write(content) unless @custom_gemfile.exist? && @custom_gemfile.read == content
|
|
95
131
|
end
|
|
96
132
|
|
|
97
133
|
sig { returns(T::Hash[String, T.untyped]) }
|
|
98
134
|
def load_dependencies
|
|
135
|
+
return {} unless @lockfile&.exist?
|
|
136
|
+
|
|
99
137
|
# We need to parse the Gemfile.lock manually here. If we try to do `bundler/setup` to use something more
|
|
100
138
|
# convenient, we may end up with issues when the globally installed `ruby-lsp` version mismatches the one included
|
|
101
139
|
# in the `Gemfile`
|
|
102
|
-
dependencies = Bundler::LockfileParser.new(
|
|
140
|
+
dependencies = Bundler::LockfileParser.new(@lockfile.read).dependencies
|
|
103
141
|
|
|
104
142
|
# When working on a gem, the `ruby-lsp` might be listed as a dependency in the gemspec. We need to make sure we
|
|
105
|
-
# check those as well or else we may get version mismatch errors
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
dependencies.merge!(gemspec_dependencies)
|
|
143
|
+
# check those as well or else we may get version mismatch errors. Notice that bundler allows more than one
|
|
144
|
+
# gemspec, so we need to make sure we go through all of them
|
|
145
|
+
Dir.glob("{,*}.gemspec").each do |path|
|
|
146
|
+
dependencies.merge!(Bundler.load_gemspec(path).dependencies.to_h { |dep| [dep.name, dep] })
|
|
110
147
|
end
|
|
111
148
|
|
|
112
149
|
dependencies
|
|
113
150
|
end
|
|
114
151
|
|
|
115
|
-
sig { params(bundle_gemfile: T.
|
|
116
|
-
def run_bundle_install(bundle_gemfile =
|
|
152
|
+
sig { params(bundle_gemfile: T.nilable(Pathname)).returns([String, T.nilable(String)]) }
|
|
153
|
+
def run_bundle_install(bundle_gemfile = @gemfile)
|
|
117
154
|
# If the user has a custom bundle path configured, we need to ensure that we will use the absolute and not
|
|
118
155
|
# relative version of it when running `bundle install`. This is necessary to avoid installing the gems under the
|
|
119
156
|
# `.ruby-lsp` folder, which is not the user's intention. For example, if the path is configured as `vendor`, we
|
|
@@ -122,7 +159,7 @@ module RubyLsp
|
|
|
122
159
|
|
|
123
160
|
# Use the absolute `BUNDLE_PATH` to prevent accidentally creating unwanted folders under `.ruby-lsp`
|
|
124
161
|
env = {}
|
|
125
|
-
env["BUNDLE_GEMFILE"] = bundle_gemfile
|
|
162
|
+
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
|
|
126
163
|
env["BUNDLE_PATH"] = File.expand_path(path, Dir.pwd) if path
|
|
127
164
|
|
|
128
165
|
# If both `ruby-lsp` and `debug` are already in the Gemfile, then we shouldn't try to upgrade them or else we'll
|
|
@@ -133,7 +170,7 @@ module RubyLsp
|
|
|
133
170
|
command = +""
|
|
134
171
|
|
|
135
172
|
if (@dependencies["ruby-lsp"] && @dependencies["debug"]) ||
|
|
136
|
-
|
|
173
|
+
custom_bundle_dependencies["ruby-lsp"].nil? || custom_bundle_dependencies["debug"].nil?
|
|
137
174
|
# Install gems using the custom bundle
|
|
138
175
|
command << "bundle install "
|
|
139
176
|
else
|
|
@@ -150,6 +187,7 @@ module RubyLsp
|
|
|
150
187
|
# Add bundle update
|
|
151
188
|
warn("Ruby LSP> Running bundle install for the custom bundle. This may take a while...")
|
|
152
189
|
system(env, command)
|
|
190
|
+
[bundle_gemfile.to_s, path]
|
|
153
191
|
end
|
|
154
192
|
end
|
|
155
193
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require "cgi"
|
|
5
|
-
require "uri"
|
|
6
4
|
require "ruby_lsp/document"
|
|
7
5
|
|
|
8
6
|
module RubyLsp
|
|
@@ -22,24 +20,27 @@ module RubyLsp
|
|
|
22
20
|
@formatter = T.let("auto", String)
|
|
23
21
|
end
|
|
24
22
|
|
|
25
|
-
sig { params(uri:
|
|
23
|
+
sig { params(uri: URI::Generic).returns(Document) }
|
|
26
24
|
def get(uri)
|
|
27
|
-
|
|
25
|
+
path = uri.to_standardized_path
|
|
26
|
+
return T.must(@state[T.must(uri.opaque)]) unless path
|
|
27
|
+
|
|
28
|
+
document = @state[path]
|
|
28
29
|
return document unless document.nil?
|
|
29
30
|
|
|
30
|
-
set(uri: uri, source: File.binread(CGI.unescape(
|
|
31
|
-
T.must(@state[
|
|
31
|
+
set(uri: uri, source: File.binread(CGI.unescape(path)), version: 0)
|
|
32
|
+
T.must(@state[path])
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
sig { params(uri:
|
|
35
|
+
sig { params(uri: URI::Generic, source: String, version: Integer).void }
|
|
35
36
|
def set(uri:, source:, version:)
|
|
36
37
|
document = Document.new(source: source, version: version, uri: uri, encoding: @encoding)
|
|
37
|
-
@state[uri] = document
|
|
38
|
+
@state[uri.storage_key] = document
|
|
38
39
|
end
|
|
39
40
|
|
|
40
|
-
sig { params(uri:
|
|
41
|
+
sig { params(uri: URI::Generic, edits: T::Array[Document::EditShape], version: Integer).void }
|
|
41
42
|
def push_edits(uri:, edits:, version:)
|
|
42
|
-
T.must(@state[uri]).push_edits(edits, version: version)
|
|
43
|
+
T.must(@state[uri.storage_key]).push_edits(edits, version: version)
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
sig { void }
|
|
@@ -52,15 +53,15 @@ module RubyLsp
|
|
|
52
53
|
@state.empty?
|
|
53
54
|
end
|
|
54
55
|
|
|
55
|
-
sig { params(uri:
|
|
56
|
+
sig { params(uri: URI::Generic).void }
|
|
56
57
|
def delete(uri)
|
|
57
|
-
@state.delete(uri)
|
|
58
|
+
@state.delete(uri.storage_key)
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
sig do
|
|
61
62
|
type_parameters(:T)
|
|
62
63
|
.params(
|
|
63
|
-
uri:
|
|
64
|
+
uri: URI::Generic,
|
|
64
65
|
request_name: String,
|
|
65
66
|
block: T.proc.params(document: Document).returns(T.type_parameter(:T)),
|
|
66
67
|
).returns(T.type_parameter(:T))
|
data/lib/ruby_lsp/utils.rb
CHANGED
|
@@ -6,7 +6,7 @@ module RubyLsp
|
|
|
6
6
|
VOID = T.let(Object.new.freeze, Object)
|
|
7
7
|
|
|
8
8
|
# This freeze is not redundant since the interpolated string is mutable
|
|
9
|
-
WORKSPACE_URI = T.let(URI(
|
|
9
|
+
WORKSPACE_URI = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
|
10
10
|
|
|
11
11
|
# A notification to be sent to the client
|
|
12
12
|
class Message
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-lsp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-08-
|
|
11
|
+
date: 2023-08-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: language_server-protocol
|
|
@@ -72,6 +72,7 @@ files:
|
|
|
72
72
|
- VERSION
|
|
73
73
|
- exe/ruby-lsp
|
|
74
74
|
- exe/ruby-lsp-check
|
|
75
|
+
- lib/core_ext/uri.rb
|
|
75
76
|
- lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
|
|
76
77
|
- lib/ruby-lsp.rb
|
|
77
78
|
- lib/ruby_lsp/check_docs.rb
|
|
@@ -140,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
140
141
|
- !ruby/object:Gem::Version
|
|
141
142
|
version: '0'
|
|
142
143
|
requirements: []
|
|
143
|
-
rubygems_version: 3.4.
|
|
144
|
+
rubygems_version: 3.4.17
|
|
144
145
|
signing_key:
|
|
145
146
|
specification_version: 4
|
|
146
147
|
summary: An opinionated language server for Ruby
|