ruby-lsp 0.13.2 → 0.13.3
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 +30 -0
- data/VERSION +1 -1
- data/lib/ruby_lsp/check_docs.rb +3 -3
- data/lib/ruby_lsp/document.rb +12 -0
- data/lib/ruby_lsp/executor.rb +77 -266
- data/lib/ruby_lsp/listener.rb +1 -50
- data/lib/ruby_lsp/listeners/code_lens.rb +233 -0
- data/lib/ruby_lsp/listeners/completion.rb +275 -0
- data/lib/ruby_lsp/listeners/definition.rb +158 -0
- data/lib/ruby_lsp/listeners/document_highlight.rb +556 -0
- data/lib/ruby_lsp/listeners/document_link.rb +162 -0
- data/lib/ruby_lsp/listeners/document_symbol.rb +223 -0
- data/lib/ruby_lsp/listeners/folding_ranges.rb +271 -0
- data/lib/ruby_lsp/listeners/hover.rb +152 -0
- data/lib/ruby_lsp/listeners/inlay_hints.rb +80 -0
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +430 -0
- data/lib/ruby_lsp/listeners/signature_help.rb +74 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +4 -4
- data/lib/ruby_lsp/requests/code_actions.rb +13 -4
- data/lib/ruby_lsp/requests/code_lens.rb +21 -221
- data/lib/ruby_lsp/requests/completion.rb +64 -244
- data/lib/ruby_lsp/requests/definition.rb +34 -147
- data/lib/ruby_lsp/requests/diagnostics.rb +17 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +12 -536
- data/lib/ruby_lsp/requests/document_link.rb +11 -132
- data/lib/ruby_lsp/requests/document_symbol.rb +23 -210
- data/lib/ruby_lsp/requests/folding_ranges.rb +16 -252
- data/lib/ruby_lsp/requests/formatting.rb +4 -4
- data/lib/ruby_lsp/requests/hover.rb +48 -92
- data/lib/ruby_lsp/requests/inlay_hints.rb +23 -56
- data/lib/ruby_lsp/requests/on_type_formatting.rb +16 -4
- data/lib/ruby_lsp/requests/request.rb +17 -0
- data/lib/ruby_lsp/requests/selection_ranges.rb +4 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +21 -408
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -4
- data/lib/ruby_lsp/requests/signature_help.rb +43 -51
- data/lib/ruby_lsp/requests/support/common.rb +3 -2
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +2 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
- data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
- data/lib/ruby_lsp/requests.rb +1 -1
- data/lib/ruby_lsp/utils.rb +8 -0
- metadata +15 -4
- data/lib/ruby_lsp/requests/base_request.rb +0 -24
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "ruby_lsp/listeners/semantic_highlighting"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
module Requests
|
6
8
|
# 
|
@@ -18,427 +20,38 @@ module RubyLsp
|
|
18
20
|
# var # --> semantic highlighting: local variable
|
19
21
|
# end
|
20
22
|
# ```
|
21
|
-
class SemanticHighlighting <
|
23
|
+
class SemanticHighlighting < Request
|
22
24
|
extend T::Sig
|
23
25
|
extend T::Generic
|
24
26
|
|
25
|
-
ResponseType = type_member { { fixed: T::Array[SemanticToken] } }
|
26
|
-
|
27
|
-
TOKEN_TYPES = T.let(
|
28
|
-
{
|
29
|
-
namespace: 0,
|
30
|
-
type: 1,
|
31
|
-
class: 2,
|
32
|
-
enum: 3,
|
33
|
-
interface: 4,
|
34
|
-
struct: 5,
|
35
|
-
typeParameter: 6,
|
36
|
-
parameter: 7,
|
37
|
-
variable: 8,
|
38
|
-
property: 9,
|
39
|
-
enumMember: 10,
|
40
|
-
event: 11,
|
41
|
-
function: 12,
|
42
|
-
method: 13,
|
43
|
-
macro: 14,
|
44
|
-
keyword: 15,
|
45
|
-
modifier: 16,
|
46
|
-
comment: 17,
|
47
|
-
string: 18,
|
48
|
-
number: 19,
|
49
|
-
regexp: 20,
|
50
|
-
operator: 21,
|
51
|
-
decorator: 22,
|
52
|
-
}.freeze,
|
53
|
-
T::Hash[Symbol, Integer],
|
54
|
-
)
|
55
|
-
|
56
|
-
TOKEN_MODIFIERS = T.let(
|
57
|
-
{
|
58
|
-
declaration: 0,
|
59
|
-
definition: 1,
|
60
|
-
readonly: 2,
|
61
|
-
static: 3,
|
62
|
-
deprecated: 4,
|
63
|
-
abstract: 5,
|
64
|
-
async: 6,
|
65
|
-
modification: 7,
|
66
|
-
documentation: 8,
|
67
|
-
default_library: 9,
|
68
|
-
}.freeze,
|
69
|
-
T::Hash[Symbol, Integer],
|
70
|
-
)
|
71
|
-
|
72
|
-
SPECIAL_RUBY_METHODS = T.let(
|
73
|
-
[
|
74
|
-
Module.instance_methods(false),
|
75
|
-
Kernel.instance_methods(false),
|
76
|
-
Kernel.methods(false),
|
77
|
-
Bundler::Dsl.instance_methods(false),
|
78
|
-
Module.private_instance_methods(false),
|
79
|
-
].flatten.map(&:to_s),
|
80
|
-
T::Array[String],
|
81
|
-
)
|
27
|
+
ResponseType = type_member { { fixed: T::Array[Listeners::SemanticHighlighting::SemanticToken] } }
|
82
28
|
|
83
|
-
class
|
29
|
+
class << self
|
84
30
|
extend T::Sig
|
85
31
|
|
86
|
-
sig { returns(
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
sig { params(location: Prism::Location, length: Integer, type: Integer, modifier: T::Array[Integer]).void }
|
99
|
-
def initialize(location:, length:, type:, modifier:)
|
100
|
-
@location = location
|
101
|
-
@length = length
|
102
|
-
@type = type
|
103
|
-
@modifier = modifier
|
32
|
+
sig { returns(Interface::SemanticTokensRegistrationOptions) }
|
33
|
+
def provider
|
34
|
+
Interface::SemanticTokensRegistrationOptions.new(
|
35
|
+
document_selector: { scheme: "file", language: "ruby" },
|
36
|
+
legend: Interface::SemanticTokensLegend.new(
|
37
|
+
token_types: Listeners::SemanticHighlighting::TOKEN_TYPES.keys,
|
38
|
+
token_modifiers: Listeners::SemanticHighlighting::TOKEN_MODIFIERS.keys,
|
39
|
+
),
|
40
|
+
range: true,
|
41
|
+
full: { delta: false },
|
42
|
+
)
|
104
43
|
end
|
105
44
|
end
|
106
45
|
|
107
|
-
sig { override.returns(ResponseType) }
|
108
|
-
attr_reader :_response
|
109
|
-
|
110
46
|
sig { params(dispatcher: Prism::Dispatcher, range: T.nilable(T::Range[Integer])).void }
|
111
47
|
def initialize(dispatcher, range: nil)
|
112
|
-
super(
|
113
|
-
|
114
|
-
@_response = T.let([], ResponseType)
|
115
|
-
@range = range
|
116
|
-
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
117
|
-
@current_scope = T.let(ParameterScope.new, ParameterScope)
|
118
|
-
@inside_regex_capture = T.let(false, T::Boolean)
|
119
|
-
|
120
|
-
dispatcher.register(
|
121
|
-
self,
|
122
|
-
:on_call_node_enter,
|
123
|
-
:on_class_node_enter,
|
124
|
-
:on_def_node_enter,
|
125
|
-
:on_def_node_leave,
|
126
|
-
:on_block_node_enter,
|
127
|
-
:on_block_node_leave,
|
128
|
-
:on_self_node_enter,
|
129
|
-
:on_module_node_enter,
|
130
|
-
:on_local_variable_write_node_enter,
|
131
|
-
:on_local_variable_read_node_enter,
|
132
|
-
:on_block_parameter_node_enter,
|
133
|
-
:on_required_keyword_parameter_node_enter,
|
134
|
-
:on_optional_keyword_parameter_node_enter,
|
135
|
-
:on_keyword_rest_parameter_node_enter,
|
136
|
-
:on_optional_parameter_node_enter,
|
137
|
-
:on_required_parameter_node_enter,
|
138
|
-
:on_rest_parameter_node_enter,
|
139
|
-
:on_constant_read_node_enter,
|
140
|
-
:on_constant_write_node_enter,
|
141
|
-
:on_constant_and_write_node_enter,
|
142
|
-
:on_constant_operator_write_node_enter,
|
143
|
-
:on_constant_or_write_node_enter,
|
144
|
-
:on_constant_target_node_enter,
|
145
|
-
:on_local_variable_and_write_node_enter,
|
146
|
-
:on_local_variable_operator_write_node_enter,
|
147
|
-
:on_local_variable_or_write_node_enter,
|
148
|
-
:on_local_variable_target_node_enter,
|
149
|
-
:on_block_local_variable_node_enter,
|
150
|
-
:on_match_write_node_enter,
|
151
|
-
:on_match_write_node_leave,
|
152
|
-
)
|
153
|
-
end
|
154
|
-
|
155
|
-
sig { params(node: Prism::CallNode).void }
|
156
|
-
def on_call_node_enter(node)
|
157
|
-
return unless visible?(node, @range)
|
158
|
-
|
159
|
-
message = node.message
|
160
|
-
return unless message
|
161
|
-
|
162
|
-
# We can't push a semantic token for [] and []= because the argument inside the brackets is a part of
|
163
|
-
# the message_loc
|
164
|
-
return if message.start_with?("[") && (message.end_with?("]") || message.end_with?("]="))
|
165
|
-
return if message == "=~"
|
166
|
-
return if special_method?(message)
|
167
|
-
|
168
|
-
type = Support::Sorbet.annotation?(node) ? :type : :method
|
169
|
-
add_token(T.must(node.message_loc), type)
|
170
|
-
end
|
171
|
-
|
172
|
-
sig { params(node: Prism::MatchWriteNode).void }
|
173
|
-
def on_match_write_node_enter(node)
|
174
|
-
call = node.call
|
175
|
-
|
176
|
-
if call.message == "=~"
|
177
|
-
@inside_regex_capture = true
|
178
|
-
process_regexp_locals(call)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
sig { params(node: Prism::MatchWriteNode).void }
|
183
|
-
def on_match_write_node_leave(node)
|
184
|
-
@inside_regex_capture = true if node.call.message == "=~"
|
185
|
-
end
|
186
|
-
|
187
|
-
sig { params(node: Prism::ConstantReadNode).void }
|
188
|
-
def on_constant_read_node_enter(node)
|
189
|
-
return unless visible?(node, @range)
|
190
|
-
# When finding a module or class definition, we will have already pushed a token related to this constant. We
|
191
|
-
# need to look at the previous two tokens and if they match this locatione exactly, avoid pushing another token
|
192
|
-
# on top of the previous one
|
193
|
-
return if @_response.last(2).any? { |token| token.location == node.location }
|
194
|
-
|
195
|
-
add_token(node.location, :namespace)
|
196
|
-
end
|
197
|
-
|
198
|
-
sig { params(node: Prism::ConstantWriteNode).void }
|
199
|
-
def on_constant_write_node_enter(node)
|
200
|
-
return unless visible?(node, @range)
|
201
|
-
|
202
|
-
add_token(node.name_loc, :namespace)
|
203
|
-
end
|
204
|
-
|
205
|
-
sig { params(node: Prism::ConstantAndWriteNode).void }
|
206
|
-
def on_constant_and_write_node_enter(node)
|
207
|
-
return unless visible?(node, @range)
|
208
|
-
|
209
|
-
add_token(node.name_loc, :namespace)
|
210
|
-
end
|
211
|
-
|
212
|
-
sig { params(node: Prism::ConstantOperatorWriteNode).void }
|
213
|
-
def on_constant_operator_write_node_enter(node)
|
214
|
-
return unless visible?(node, @range)
|
215
|
-
|
216
|
-
add_token(node.name_loc, :namespace)
|
217
|
-
end
|
218
|
-
|
219
|
-
sig { params(node: Prism::ConstantOrWriteNode).void }
|
220
|
-
def on_constant_or_write_node_enter(node)
|
221
|
-
return unless visible?(node, @range)
|
222
|
-
|
223
|
-
add_token(node.name_loc, :namespace)
|
224
|
-
end
|
225
|
-
|
226
|
-
sig { params(node: Prism::ConstantTargetNode).void }
|
227
|
-
def on_constant_target_node_enter(node)
|
228
|
-
return unless visible?(node, @range)
|
229
|
-
|
230
|
-
add_token(node.location, :namespace)
|
231
|
-
end
|
232
|
-
|
233
|
-
sig { params(node: Prism::DefNode).void }
|
234
|
-
def on_def_node_enter(node)
|
235
|
-
@current_scope = ParameterScope.new(@current_scope)
|
236
|
-
return unless visible?(node, @range)
|
237
|
-
|
238
|
-
add_token(node.name_loc, :method, [:declaration])
|
239
|
-
end
|
240
|
-
|
241
|
-
sig { params(node: Prism::DefNode).void }
|
242
|
-
def on_def_node_leave(node)
|
243
|
-
@current_scope = T.must(@current_scope.parent)
|
244
|
-
end
|
245
|
-
|
246
|
-
sig { params(node: Prism::BlockNode).void }
|
247
|
-
def on_block_node_enter(node)
|
248
|
-
@current_scope = ParameterScope.new(@current_scope)
|
249
|
-
end
|
250
|
-
|
251
|
-
sig { params(node: Prism::BlockNode).void }
|
252
|
-
def on_block_node_leave(node)
|
253
|
-
@current_scope = T.must(@current_scope.parent)
|
254
|
-
end
|
255
|
-
|
256
|
-
sig { params(node: Prism::BlockLocalVariableNode).void }
|
257
|
-
def on_block_local_variable_node_enter(node)
|
258
|
-
add_token(node.location, :variable)
|
259
|
-
end
|
260
|
-
|
261
|
-
sig { params(node: Prism::BlockParameterNode).void }
|
262
|
-
def on_block_parameter_node_enter(node)
|
263
|
-
name = node.name
|
264
|
-
@current_scope << name.to_sym if name
|
265
|
-
end
|
266
|
-
|
267
|
-
sig { params(node: Prism::RequiredKeywordParameterNode).void }
|
268
|
-
def on_required_keyword_parameter_node_enter(node)
|
269
|
-
@current_scope << node.name
|
270
|
-
return unless visible?(node, @range)
|
271
|
-
|
272
|
-
location = node.name_loc
|
273
|
-
add_token(location.copy(length: location.length - 1), :parameter)
|
274
|
-
end
|
275
|
-
|
276
|
-
sig { params(node: Prism::OptionalKeywordParameterNode).void }
|
277
|
-
def on_optional_keyword_parameter_node_enter(node)
|
278
|
-
@current_scope << node.name
|
279
|
-
return unless visible?(node, @range)
|
280
|
-
|
281
|
-
location = node.name_loc
|
282
|
-
add_token(location.copy(length: location.length - 1), :parameter)
|
283
|
-
end
|
284
|
-
|
285
|
-
sig { params(node: Prism::KeywordRestParameterNode).void }
|
286
|
-
def on_keyword_rest_parameter_node_enter(node)
|
287
|
-
name = node.name
|
288
|
-
|
289
|
-
if name
|
290
|
-
@current_scope << name.to_sym
|
291
|
-
|
292
|
-
add_token(T.must(node.name_loc), :parameter) if visible?(node, @range)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
sig { params(node: Prism::OptionalParameterNode).void }
|
297
|
-
def on_optional_parameter_node_enter(node)
|
298
|
-
@current_scope << node.name
|
299
|
-
return unless visible?(node, @range)
|
300
|
-
|
301
|
-
add_token(node.name_loc, :parameter)
|
302
|
-
end
|
303
|
-
|
304
|
-
sig { params(node: Prism::RequiredParameterNode).void }
|
305
|
-
def on_required_parameter_node_enter(node)
|
306
|
-
@current_scope << node.name
|
307
|
-
return unless visible?(node, @range)
|
308
|
-
|
309
|
-
add_token(node.location, :parameter)
|
310
|
-
end
|
311
|
-
|
312
|
-
sig { params(node: Prism::RestParameterNode).void }
|
313
|
-
def on_rest_parameter_node_enter(node)
|
314
|
-
name = node.name
|
315
|
-
|
316
|
-
if name
|
317
|
-
@current_scope << name.to_sym
|
318
|
-
|
319
|
-
add_token(T.must(node.name_loc), :parameter) if visible?(node, @range)
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
sig { params(node: Prism::SelfNode).void }
|
324
|
-
def on_self_node_enter(node)
|
325
|
-
return unless visible?(node, @range)
|
326
|
-
|
327
|
-
add_token(node.location, :variable, [:default_library])
|
328
|
-
end
|
329
|
-
|
330
|
-
sig { params(node: Prism::LocalVariableWriteNode).void }
|
331
|
-
def on_local_variable_write_node_enter(node)
|
332
|
-
return unless visible?(node, @range)
|
333
|
-
|
334
|
-
add_token(node.name_loc, @current_scope.type_for(node.name))
|
48
|
+
super()
|
49
|
+
@listener = T.let(Listeners::SemanticHighlighting.new(dispatcher, range: range), Listener[ResponseType])
|
335
50
|
end
|
336
51
|
|
337
|
-
sig {
|
338
|
-
def
|
339
|
-
|
340
|
-
|
341
|
-
# Numbered parameters
|
342
|
-
if /_\d+/.match?(node.name)
|
343
|
-
add_token(node.location, :parameter)
|
344
|
-
return
|
345
|
-
end
|
346
|
-
|
347
|
-
add_token(node.location, @current_scope.type_for(node.name))
|
348
|
-
end
|
349
|
-
|
350
|
-
sig { params(node: Prism::LocalVariableAndWriteNode).void }
|
351
|
-
def on_local_variable_and_write_node_enter(node)
|
352
|
-
return unless visible?(node, @range)
|
353
|
-
|
354
|
-
add_token(node.name_loc, @current_scope.type_for(node.name))
|
355
|
-
end
|
356
|
-
|
357
|
-
sig { params(node: Prism::LocalVariableOperatorWriteNode).void }
|
358
|
-
def on_local_variable_operator_write_node_enter(node)
|
359
|
-
return unless visible?(node, @range)
|
360
|
-
|
361
|
-
add_token(node.name_loc, @current_scope.type_for(node.name))
|
362
|
-
end
|
363
|
-
|
364
|
-
sig { params(node: Prism::LocalVariableOrWriteNode).void }
|
365
|
-
def on_local_variable_or_write_node_enter(node)
|
366
|
-
return unless visible?(node, @range)
|
367
|
-
|
368
|
-
add_token(node.name_loc, @current_scope.type_for(node.name))
|
369
|
-
end
|
370
|
-
|
371
|
-
sig { params(node: Prism::LocalVariableTargetNode).void }
|
372
|
-
def on_local_variable_target_node_enter(node)
|
373
|
-
# If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
|
374
|
-
# Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
|
375
|
-
# the entire regex as a local variable. We process these captures in process_regexp_locals instead and then
|
376
|
-
# prevent pushing local variable target tokens. See https://github.com/ruby/prism/issues/1912
|
377
|
-
return if @inside_regex_capture
|
378
|
-
|
379
|
-
return unless visible?(node, @range)
|
380
|
-
|
381
|
-
add_token(node.location, @current_scope.type_for(node.name))
|
382
|
-
end
|
383
|
-
|
384
|
-
sig { params(node: Prism::ClassNode).void }
|
385
|
-
def on_class_node_enter(node)
|
386
|
-
return unless visible?(node, @range)
|
387
|
-
|
388
|
-
add_token(node.constant_path.location, :class, [:declaration])
|
389
|
-
|
390
|
-
superclass = node.superclass
|
391
|
-
add_token(superclass.location, :class) if superclass
|
392
|
-
end
|
393
|
-
|
394
|
-
sig { params(node: Prism::ModuleNode).void }
|
395
|
-
def on_module_node_enter(node)
|
396
|
-
return unless visible?(node, @range)
|
397
|
-
|
398
|
-
add_token(node.constant_path.location, :namespace, [:declaration])
|
399
|
-
end
|
400
|
-
|
401
|
-
private
|
402
|
-
|
403
|
-
sig { params(location: Prism::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
|
404
|
-
def add_token(location, type, modifiers = [])
|
405
|
-
length = location.end_offset - location.start_offset
|
406
|
-
modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
|
407
|
-
@_response.push(
|
408
|
-
SemanticToken.new(
|
409
|
-
location: location,
|
410
|
-
length: length,
|
411
|
-
type: T.must(TOKEN_TYPES[type]),
|
412
|
-
modifier: modifiers_indices,
|
413
|
-
),
|
414
|
-
)
|
415
|
-
end
|
416
|
-
|
417
|
-
# Textmate provides highlighting for a subset of these special Ruby-specific methods. We want to utilize that
|
418
|
-
# highlighting, so we avoid making a semantic token for it.
|
419
|
-
sig { params(method_name: String).returns(T::Boolean) }
|
420
|
-
def special_method?(method_name)
|
421
|
-
SPECIAL_RUBY_METHODS.include?(method_name)
|
422
|
-
end
|
423
|
-
|
424
|
-
sig { params(node: Prism::CallNode).void }
|
425
|
-
def process_regexp_locals(node)
|
426
|
-
receiver = node.receiver
|
427
|
-
|
428
|
-
# The regexp needs to be the receiver of =~ for local variable capture
|
429
|
-
return unless receiver.is_a?(Prism::RegularExpressionNode)
|
430
|
-
|
431
|
-
content = receiver.content
|
432
|
-
loc = receiver.content_loc
|
433
|
-
|
434
|
-
# For each capture name we find in the regexp, look for a local in the current_scope
|
435
|
-
Regexp.new(content, Regexp::FIXEDENCODING).names.each do |name|
|
436
|
-
# The +3 is to compensate for the "(?<" part of the capture name
|
437
|
-
capture_name_offset = T.must(content.index("(?<#{name}>")) + 3
|
438
|
-
local_var_loc = loc.copy(start_offset: loc.start_offset + capture_name_offset, length: name.length)
|
439
|
-
|
440
|
-
add_token(local_var_loc, @current_scope.type_for(name))
|
441
|
-
end
|
52
|
+
sig { override.returns(ResponseType) }
|
53
|
+
def perform
|
54
|
+
@listener.response
|
442
55
|
end
|
443
56
|
end
|
444
57
|
end
|
@@ -17,18 +17,18 @@ module RubyLsp
|
|
17
17
|
# # (program (statements ((binary (int "1") + (int "1")))))
|
18
18
|
# ```
|
19
19
|
#
|
20
|
-
class ShowSyntaxTree <
|
20
|
+
class ShowSyntaxTree < Request
|
21
21
|
extend T::Sig
|
22
22
|
|
23
23
|
sig { params(document: Document, range: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
24
24
|
def initialize(document, range)
|
25
|
-
super(
|
26
|
-
|
25
|
+
super()
|
26
|
+
@document = document
|
27
27
|
@range = range
|
28
28
|
end
|
29
29
|
|
30
30
|
sig { override.returns(String) }
|
31
|
-
def
|
31
|
+
def perform
|
32
32
|
return ast_for_range if @range
|
33
33
|
|
34
34
|
output_string = +""
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "ruby_lsp/listeners/signature_help"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
module Requests
|
6
8
|
# 
|
@@ -22,73 +24,63 @@ module RubyLsp
|
|
22
24
|
# bar( # -> Signature help will show the parameters of `bar`
|
23
25
|
# end
|
24
26
|
# ```
|
25
|
-
class SignatureHelp <
|
27
|
+
class SignatureHelp < Request
|
26
28
|
extend T::Sig
|
27
29
|
extend T::Generic
|
28
30
|
|
29
|
-
|
31
|
+
class << self
|
32
|
+
extend T::Sig
|
30
33
|
|
31
|
-
|
32
|
-
|
34
|
+
sig { returns(Interface::SignatureHelpOptions) }
|
35
|
+
def provider
|
36
|
+
# Identifier characters are automatically included, such as A-Z, a-z, 0-9, _, * or :
|
37
|
+
Interface::SignatureHelpOptions.new(
|
38
|
+
trigger_characters: ["(", " ", ","],
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
ResponseType = type_member { { fixed: T.nilable(T.any(Interface::SignatureHelp, T::Hash[Symbol, T.untyped])) } }
|
33
44
|
|
34
45
|
sig do
|
35
46
|
params(
|
36
|
-
|
37
|
-
nesting: T::Array[String],
|
47
|
+
document: Document,
|
38
48
|
index: RubyIndexer::Index,
|
49
|
+
position: T::Hash[Symbol, T.untyped],
|
50
|
+
context: T.nilable(T::Hash[Symbol, T.untyped]),
|
39
51
|
dispatcher: Prism::Dispatcher,
|
40
52
|
).void
|
41
53
|
end
|
42
|
-
def initialize(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
dispatcher.register(self, :on_call_node_enter)
|
50
|
-
end
|
51
|
-
|
52
|
-
sig { params(node: Prism::CallNode).void }
|
53
|
-
def on_call_node_enter(node)
|
54
|
-
return if DependencyDetector.instance.typechecker
|
55
|
-
return unless self_receiver?(node)
|
56
|
-
|
57
|
-
message = node.message
|
58
|
-
return unless message
|
59
|
-
|
60
|
-
target_method = @index.resolve_method(message, @nesting.join("::"))
|
61
|
-
return unless target_method
|
62
|
-
|
63
|
-
parameters = target_method.parameters
|
64
|
-
name = target_method.name
|
54
|
+
def initialize(document, index, position, context, dispatcher)
|
55
|
+
super()
|
56
|
+
current_signature = context && context[:activeSignatureHelp]
|
57
|
+
target, parent, nesting = document.locate_node(
|
58
|
+
{ line: position[:line], character: position[:character] - 2 },
|
59
|
+
node_types: [Prism::CallNode],
|
60
|
+
)
|
65
61
|
|
66
|
-
# If
|
67
|
-
|
62
|
+
# If we're typing a nested method call (e.g.: `foo(bar)`), then we may end up locating `bar` as the target
|
63
|
+
# method call incorrectly. To correct that, we check if there's an active signature with the same name as the
|
64
|
+
# parent node and then replace the target
|
65
|
+
if current_signature && parent.is_a?(Prism::CallNode)
|
66
|
+
active_signature = current_signature[:activeSignature] || 0
|
68
67
|
|
69
|
-
|
68
|
+
if current_signature.dig(:signatures, active_signature, :label)&.start_with?(parent.message)
|
69
|
+
target = parent
|
70
|
+
end
|
71
|
+
end
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
|
73
|
+
@target = T.let(target, T.nilable(Prism::Node))
|
74
|
+
@dispatcher = dispatcher
|
75
|
+
@listener = T.let(Listeners::SignatureHelp.new(nesting, index, dispatcher), Listener[ResponseType])
|
76
|
+
end
|
74
77
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
node.slice.byteslice(arguments_node.location.end_offset - node.location.start_offset) == ","
|
79
|
-
active_parameter += 1
|
80
|
-
end
|
78
|
+
sig { override.returns(ResponseType) }
|
79
|
+
def perform
|
80
|
+
return unless @target
|
81
81
|
|
82
|
-
@
|
83
|
-
|
84
|
-
Interface::SignatureInformation.new(
|
85
|
-
label: label,
|
86
|
-
parameters: parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
|
87
|
-
documentation: markdown_from_index_entries("", target_method),
|
88
|
-
),
|
89
|
-
],
|
90
|
-
active_parameter: active_parameter,
|
91
|
-
)
|
82
|
+
@dispatcher.dispatch_once(@target)
|
83
|
+
@listener.response
|
92
84
|
end
|
93
85
|
end
|
94
86
|
end
|
@@ -70,8 +70,9 @@ module RubyLsp
|
|
70
70
|
end
|
71
71
|
|
72
72
|
sig { params(file_path: String).returns(T.nilable(T::Boolean)) }
|
73
|
-
def
|
74
|
-
|
73
|
+
def not_in_dependencies?(file_path)
|
74
|
+
BUNDLE_PATH &&
|
75
|
+
!file_path.start_with?(T.must(BUNDLE_PATH)) &&
|
75
76
|
!file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
|
76
77
|
end
|
77
78
|
|
@@ -15,7 +15,7 @@ module RubyLsp
|
|
15
15
|
|
16
16
|
sig do
|
17
17
|
params(
|
18
|
-
tokens: T::Array[SemanticHighlighting::SemanticToken],
|
18
|
+
tokens: T::Array[Listeners::SemanticHighlighting::SemanticToken],
|
19
19
|
).returns(Interface::SemanticTokens)
|
20
20
|
end
|
21
21
|
def encode(tokens)
|
@@ -39,7 +39,7 @@ module RubyLsp
|
|
39
39
|
|
40
40
|
# For more information on how each number is calculated, read:
|
41
41
|
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
|
42
|
-
sig { params(token: SemanticHighlighting::SemanticToken).returns(T::Array[Integer]) }
|
42
|
+
sig { params(token: Listeners::SemanticHighlighting::SemanticToken).returns(T::Array[Integer]) }
|
43
43
|
def compute_delta(token)
|
44
44
|
row = token.location.start_line - 1
|
45
45
|
column = token.location.start_column
|
@@ -18,23 +18,24 @@ module RubyLsp
|
|
18
18
|
# end
|
19
19
|
# ```
|
20
20
|
#
|
21
|
-
class WorkspaceSymbol
|
21
|
+
class WorkspaceSymbol < Request
|
22
22
|
extend T::Sig
|
23
23
|
include Support::Common
|
24
24
|
|
25
25
|
sig { params(query: T.nilable(String), index: RubyIndexer::Index).void }
|
26
26
|
def initialize(query, index)
|
27
|
+
super()
|
27
28
|
@query = query
|
28
29
|
@index = index
|
29
30
|
end
|
30
31
|
|
31
|
-
sig { returns(T::Array[Interface::WorkspaceSymbol]) }
|
32
|
-
def
|
32
|
+
sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
|
33
|
+
def perform
|
33
34
|
@index.fuzzy_search(@query).filter_map do |entry|
|
34
35
|
# If the project is using Sorbet, we let Sorbet handle symbols defined inside the project itself and RBIs, but
|
35
36
|
# we still return entries defined in gems to allow developers to jump directly to the source
|
36
37
|
file_path = entry.file_path
|
37
|
-
next if
|
38
|
+
next if DependencyDetector.instance.typechecker && not_in_dependencies?(file_path)
|
38
39
|
|
39
40
|
# We should never show private symbols when searching the entire workspace
|
40
41
|
next if entry.visibility == :private
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -25,7 +25,7 @@ module RubyLsp
|
|
25
25
|
# - [SignatureHelp](rdoc-ref:RubyLsp::Requests::SignatureHelp)
|
26
26
|
|
27
27
|
module Requests
|
28
|
-
autoload :
|
28
|
+
autoload :Request, "ruby_lsp/requests/request"
|
29
29
|
autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
|
30
30
|
autoload :DocumentLink, "ruby_lsp/requests/document_link"
|
31
31
|
autoload :Hover, "ruby_lsp/requests/hover"
|