ruby-lsp 0.7.6 → 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-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 +19 -9
- data/lib/ruby_lsp/store.rb +14 -13
- data/lib/ruby_lsp/utils.rb +1 -1
- metadata +3 -2
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-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
|
@@ -5,6 +5,7 @@ require "sorbet-runtime"
|
|
5
5
|
require "bundler"
|
6
6
|
require "fileutils"
|
7
7
|
require "pathname"
|
8
|
+
require "digest"
|
8
9
|
|
9
10
|
# This file is a script that will configure a custom bundle for the Ruby LSP. The custom bundle allows developers to use
|
10
11
|
# the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to the
|
@@ -24,6 +25,7 @@ module RubyLsp
|
|
24
25
|
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(Dir.pwd), Pathname)
|
25
26
|
@custom_gemfile = T.let(@custom_dir + "Gemfile", Pathname)
|
26
27
|
@custom_lockfile = T.let(@custom_dir + "Gemfile.lock", Pathname)
|
28
|
+
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
|
27
29
|
|
28
30
|
# Regular bundle paths
|
29
31
|
@gemfile = T.let(
|
@@ -37,7 +39,6 @@ module RubyLsp
|
|
37
39
|
@lockfile = T.let(@gemfile ? Bundler.default_lockfile : nil, T.nilable(Pathname))
|
38
40
|
|
39
41
|
@dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
|
40
|
-
@custom_bundle_dependencies = T.let(custom_bundle_dependencies, T::Hash[String, T.untyped])
|
41
42
|
end
|
42
43
|
|
43
44
|
# Setups up the custom bundle and returns the `BUNDLE_GEMFILE` and `BUNDLE_PATH` that should be used for running the
|
@@ -69,14 +70,16 @@ module RubyLsp
|
|
69
70
|
return run_bundle_install(@custom_gemfile)
|
70
71
|
end
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
75
77
|
warn("Ruby LSP> Skipping custom bundle setup since #{@custom_lockfile} already exists and is up to date")
|
76
78
|
return run_bundle_install(@custom_gemfile)
|
77
79
|
end
|
78
80
|
|
79
81
|
FileUtils.cp(@lockfile.to_s, @custom_lockfile.to_s)
|
82
|
+
@lockfile_hash_path.write(current_lockfile_hash)
|
80
83
|
run_bundle_install(@custom_gemfile)
|
81
84
|
end
|
82
85
|
|
@@ -84,10 +87,17 @@ module RubyLsp
|
|
84
87
|
|
85
88
|
sig { returns(T::Hash[String, T.untyped]) }
|
86
89
|
def custom_bundle_dependencies
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
+
)
|
91
101
|
ensure
|
92
102
|
ENV.delete("BUNDLE_GEMFILE")
|
93
103
|
end
|
@@ -160,7 +170,7 @@ module RubyLsp
|
|
160
170
|
command = +""
|
161
171
|
|
162
172
|
if (@dependencies["ruby-lsp"] && @dependencies["debug"]) ||
|
163
|
-
|
173
|
+
custom_bundle_dependencies["ruby-lsp"].nil? || custom_bundle_dependencies["debug"].nil?
|
164
174
|
# Install gems using the custom bundle
|
165
175
|
command << "bundle install "
|
166
176
|
else
|
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
|