ruby-lsp 0.27.0.beta1 → 0.27.0.beta3
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 +0 -46
- data/exe/ruby-lsp-check +0 -15
- data/lib/ruby_lsp/addon.rb +19 -19
- data/lib/ruby_lsp/global_state.rb +1 -6
- data/lib/ruby_lsp/internal.rb +3 -2
- data/lib/ruby_lsp/listeners/code_lens.rb +1 -1
- data/lib/ruby_lsp/listeners/completion.rb +246 -382
- data/lib/ruby_lsp/listeners/definition.rb +7 -10
- data/lib/ruby_lsp/listeners/document_link.rb +4 -0
- data/lib/ruby_lsp/listeners/hover.rb +234 -82
- data/lib/ruby_lsp/listeners/signature_help.rb +11 -12
- data/lib/ruby_lsp/listeners/spec_style.rb +6 -1
- data/lib/ruby_lsp/listeners/test_discovery.rb +38 -15
- data/lib/ruby_lsp/listeners/test_style.rb +21 -9
- data/lib/ruby_lsp/node_context.rb +31 -8
- data/lib/ruby_lsp/requests/completion_resolve.rb +55 -39
- data/lib/ruby_lsp/requests/discover_tests.rb +5 -41
- data/lib/ruby_lsp/requests/hover.rb +2 -5
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +66 -22
- data/lib/ruby_lsp/requests/references.rb +180 -66
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/request.rb +3 -33
- data/lib/ruby_lsp/requests/support/common.rb +82 -68
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +82 -46
- data/lib/ruby_lsp/ruby_document.rb +0 -73
- data/lib/ruby_lsp/rubydex/declaration.rb +174 -0
- data/lib/ruby_lsp/rubydex/definition.rb +73 -0
- data/lib/ruby_lsp/rubydex/reference.rb +6 -1
- data/lib/ruby_lsp/rubydex/signature.rb +107 -0
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +56 -171
- data/lib/ruby_lsp/test_helper.rb +0 -1
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +89 -11
- metadata +12 -18
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +0 -276
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +0 -1101
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +0 -44
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +0 -605
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +0 -1077
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +0 -37
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +0 -149
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +0 -294
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +0 -335
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +0 -32
- data/lib/ruby_indexer/ruby_indexer.rb +0 -20
- data/lib/ruby_lsp/static_docs.rb +0 -20
- data/static_docs/break.md +0 -103
- data/static_docs/yield.md +0 -81
- /data/lib/{ruby_indexer/lib/ruby_indexer → ruby_lsp}/uri.rb +0 -0
|
@@ -106,7 +106,7 @@ module RubyLsp
|
|
|
106
106
|
|
|
107
107
|
#: (Prism::ConstantPathNode node) -> void
|
|
108
108
|
def on_constant_path_node_enter(node)
|
|
109
|
-
name =
|
|
109
|
+
name = constant_name(node)
|
|
110
110
|
return if name.nil?
|
|
111
111
|
|
|
112
112
|
handle_constant_definition(name)
|
|
@@ -114,7 +114,7 @@ module RubyLsp
|
|
|
114
114
|
|
|
115
115
|
#: (Prism::ConstantReadNode node) -> void
|
|
116
116
|
def on_constant_read_node_enter(node)
|
|
117
|
-
name =
|
|
117
|
+
name = constant_name(node)
|
|
118
118
|
return if name.nil?
|
|
119
119
|
|
|
120
120
|
handle_constant_definition(name)
|
|
@@ -249,7 +249,7 @@ module RubyLsp
|
|
|
249
249
|
return unless surrounding_method
|
|
250
250
|
|
|
251
251
|
handle_method_definition(
|
|
252
|
-
surrounding_method,
|
|
252
|
+
surrounding_method.name,
|
|
253
253
|
@type_inferrer.infer_receiver_type(@node_context),
|
|
254
254
|
inherited_only: true,
|
|
255
255
|
)
|
|
@@ -302,6 +302,9 @@ module RubyLsp
|
|
|
302
302
|
return unless declaration
|
|
303
303
|
|
|
304
304
|
Array(declaration).each do |decl|
|
|
305
|
+
next if decl.is_a?(Rubydex::Method) &&
|
|
306
|
+
!method_reachable_from_call_site?(decl, receiver_type, @graph, @node_context)
|
|
307
|
+
|
|
305
308
|
decl.definitions.each do |definition|
|
|
306
309
|
location = definition.location
|
|
307
310
|
uri = URI(location.uri)
|
|
@@ -367,13 +370,7 @@ module RubyLsp
|
|
|
367
370
|
def handle_constant_definition(value)
|
|
368
371
|
declaration = @graph.resolve_constant(value, @node_context.nesting)
|
|
369
372
|
return unless declaration
|
|
370
|
-
|
|
371
|
-
# [RUBYDEX] TODO: temporarily commented out until we have the visibility API
|
|
372
|
-
#
|
|
373
|
-
# We should only allow jumping to the definition of private constants if the constant is defined in the same
|
|
374
|
-
# namespace as the reference
|
|
375
|
-
#
|
|
376
|
-
# return if declaration.private? && declaration.name != "#{@node_context.fully_qualified_name}::#{value}"
|
|
373
|
+
return unless constant_reachable_from_call_site?(declaration, value, @node_context)
|
|
377
374
|
|
|
378
375
|
declaration.definitions.each do |definition|
|
|
379
376
|
# If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
|
|
@@ -21,6 +21,8 @@ module RubyLsp
|
|
|
21
21
|
|
|
22
22
|
Gem::Specification.stubs.each do |stub|
|
|
23
23
|
spec = stub.to_spec
|
|
24
|
+
next unless spec
|
|
25
|
+
|
|
24
26
|
lookup[spec.name] = {}
|
|
25
27
|
lookup[spec.name][spec.version.to_s] = {}
|
|
26
28
|
|
|
@@ -31,6 +33,8 @@ module RubyLsp
|
|
|
31
33
|
|
|
32
34
|
Gem::Specification.default_stubs.each do |stub|
|
|
33
35
|
spec = stub.to_spec
|
|
36
|
+
next unless spec
|
|
37
|
+
|
|
34
38
|
lookup[spec.name] = {}
|
|
35
39
|
lookup[spec.name][spec.version.to_s] = {}
|
|
36
40
|
prefix_matchers = Regexp.union(spec.require_paths.map do |rp|
|
|
@@ -6,92 +6,94 @@ module RubyLsp
|
|
|
6
6
|
class Hover
|
|
7
7
|
include Requests::Support::Common
|
|
8
8
|
|
|
9
|
-
ALLOWED_TARGETS = [
|
|
10
|
-
Prism::BreakNode,
|
|
11
|
-
Prism::CallNode,
|
|
12
|
-
Prism::ConstantReadNode,
|
|
13
|
-
Prism::ConstantWriteNode,
|
|
14
|
-
Prism::ConstantPathNode,
|
|
15
|
-
Prism::GlobalVariableAndWriteNode,
|
|
16
|
-
Prism::GlobalVariableOperatorWriteNode,
|
|
17
|
-
Prism::GlobalVariableOrWriteNode,
|
|
18
|
-
Prism::GlobalVariableReadNode,
|
|
19
|
-
Prism::GlobalVariableTargetNode,
|
|
20
|
-
Prism::GlobalVariableWriteNode,
|
|
21
|
-
Prism::InstanceVariableReadNode,
|
|
22
|
-
Prism::InstanceVariableAndWriteNode,
|
|
23
|
-
Prism::InstanceVariableOperatorWriteNode,
|
|
24
|
-
Prism::InstanceVariableOrWriteNode,
|
|
25
|
-
Prism::InstanceVariableTargetNode,
|
|
26
|
-
Prism::InstanceVariableWriteNode,
|
|
27
|
-
Prism::SymbolNode,
|
|
28
|
-
Prism::StringNode,
|
|
29
|
-
Prism::InterpolatedStringNode,
|
|
30
|
-
Prism::SuperNode,
|
|
31
|
-
Prism::ForwardingSuperNode,
|
|
32
|
-
Prism::YieldNode,
|
|
33
|
-
Prism::ClassVariableAndWriteNode,
|
|
34
|
-
Prism::ClassVariableOperatorWriteNode,
|
|
35
|
-
Prism::ClassVariableOrWriteNode,
|
|
36
|
-
Prism::ClassVariableReadNode,
|
|
37
|
-
Prism::ClassVariableTargetNode,
|
|
38
|
-
Prism::ClassVariableWriteNode,
|
|
39
|
-
] #: Array[singleton(Prism::Node)]
|
|
40
|
-
|
|
41
9
|
ALLOWED_REMOTE_PROVIDERS = [
|
|
42
10
|
"https://github.com",
|
|
43
11
|
"https://gitlab.com",
|
|
44
12
|
].freeze #: Array[String]
|
|
45
13
|
|
|
46
|
-
#: (ResponseBuilders::Hover response_builder, GlobalState global_state, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level) -> void
|
|
47
|
-
def initialize(response_builder, global_state, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
|
14
|
+
#: (ResponseBuilders::Hover response_builder, GlobalState global_state, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level, Hash[Symbol, untyped] position) -> void
|
|
15
|
+
def initialize(response_builder, global_state, uri, node_context, dispatcher, sorbet_level, position) # rubocop:disable Metrics/ParameterLists
|
|
48
16
|
@response_builder = response_builder
|
|
49
17
|
@global_state = global_state
|
|
50
|
-
@index = global_state.index #: RubyIndexer::Index
|
|
51
18
|
@graph = global_state.graph #: Rubydex::Graph
|
|
52
19
|
@type_inferrer = global_state.type_inferrer #: TypeInferrer
|
|
53
20
|
@path = uri.to_standardized_path #: String?
|
|
54
21
|
@node_context = node_context
|
|
55
22
|
@sorbet_level = sorbet_level
|
|
23
|
+
@position = position
|
|
56
24
|
|
|
57
25
|
dispatcher.register(
|
|
58
26
|
self,
|
|
27
|
+
:on_alias_global_variable_node_enter,
|
|
28
|
+
:on_alias_method_node_enter,
|
|
29
|
+
:on_and_node_enter,
|
|
30
|
+
:on_begin_node_enter,
|
|
31
|
+
:on_block_node_enter,
|
|
59
32
|
:on_break_node_enter,
|
|
33
|
+
:on_call_node_enter,
|
|
34
|
+
:on_case_match_node_enter,
|
|
35
|
+
:on_case_node_enter,
|
|
36
|
+
:on_class_node_enter,
|
|
37
|
+
:on_singleton_class_node_enter,
|
|
38
|
+
:on_lambda_node_enter,
|
|
39
|
+
:on_class_variable_and_write_node_enter,
|
|
40
|
+
:on_class_variable_operator_write_node_enter,
|
|
41
|
+
:on_class_variable_or_write_node_enter,
|
|
42
|
+
:on_class_variable_read_node_enter,
|
|
43
|
+
:on_class_variable_target_node_enter,
|
|
44
|
+
:on_class_variable_write_node_enter,
|
|
45
|
+
:on_constant_path_node_enter,
|
|
60
46
|
:on_constant_read_node_enter,
|
|
61
47
|
:on_constant_write_node_enter,
|
|
62
|
-
:
|
|
63
|
-
:
|
|
48
|
+
:on_def_node_enter,
|
|
49
|
+
:on_defined_node_enter,
|
|
50
|
+
:on_else_node_enter,
|
|
51
|
+
:on_ensure_node_enter,
|
|
52
|
+
:on_false_node_enter,
|
|
53
|
+
:on_for_node_enter,
|
|
54
|
+
:on_forwarding_super_node_enter,
|
|
64
55
|
:on_global_variable_and_write_node_enter,
|
|
65
56
|
:on_global_variable_operator_write_node_enter,
|
|
66
57
|
:on_global_variable_or_write_node_enter,
|
|
67
58
|
:on_global_variable_read_node_enter,
|
|
68
59
|
:on_global_variable_target_node_enter,
|
|
69
60
|
:on_global_variable_write_node_enter,
|
|
70
|
-
:
|
|
71
|
-
:
|
|
61
|
+
:on_if_node_enter,
|
|
62
|
+
:on_in_node_enter,
|
|
72
63
|
:on_instance_variable_and_write_node_enter,
|
|
73
64
|
:on_instance_variable_operator_write_node_enter,
|
|
74
65
|
:on_instance_variable_or_write_node_enter,
|
|
66
|
+
:on_instance_variable_read_node_enter,
|
|
75
67
|
:on_instance_variable_target_node_enter,
|
|
76
|
-
:
|
|
77
|
-
:on_forwarding_super_node_enter,
|
|
78
|
-
:on_string_node_enter,
|
|
68
|
+
:on_instance_variable_write_node_enter,
|
|
79
69
|
:on_interpolated_string_node_enter,
|
|
70
|
+
:on_module_node_enter,
|
|
71
|
+
:on_next_node_enter,
|
|
72
|
+
:on_nil_node_enter,
|
|
73
|
+
:on_or_node_enter,
|
|
74
|
+
:on_post_execution_node_enter,
|
|
75
|
+
:on_pre_execution_node_enter,
|
|
76
|
+
:on_redo_node_enter,
|
|
77
|
+
:on_rescue_modifier_node_enter,
|
|
78
|
+
:on_rescue_node_enter,
|
|
79
|
+
:on_retry_node_enter,
|
|
80
|
+
:on_return_node_enter,
|
|
81
|
+
:on_self_node_enter,
|
|
82
|
+
:on_source_encoding_node_enter,
|
|
83
|
+
:on_source_file_node_enter,
|
|
84
|
+
:on_source_line_node_enter,
|
|
85
|
+
:on_string_node_enter,
|
|
86
|
+
:on_super_node_enter,
|
|
87
|
+
:on_true_node_enter,
|
|
88
|
+
:on_undef_node_enter,
|
|
89
|
+
:on_unless_node_enter,
|
|
90
|
+
:on_until_node_enter,
|
|
91
|
+
:on_when_node_enter,
|
|
92
|
+
:on_while_node_enter,
|
|
80
93
|
:on_yield_node_enter,
|
|
81
|
-
:on_class_variable_and_write_node_enter,
|
|
82
|
-
:on_class_variable_operator_write_node_enter,
|
|
83
|
-
:on_class_variable_or_write_node_enter,
|
|
84
|
-
:on_class_variable_read_node_enter,
|
|
85
|
-
:on_class_variable_target_node_enter,
|
|
86
|
-
:on_class_variable_write_node_enter,
|
|
87
94
|
)
|
|
88
95
|
end
|
|
89
96
|
|
|
90
|
-
#: (Prism::BreakNode node) -> void
|
|
91
|
-
def on_break_node_enter(node)
|
|
92
|
-
handle_keyword_documentation(node.keyword)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
97
|
#: (Prism::StringNode node) -> void
|
|
96
98
|
def on_string_node_enter(node)
|
|
97
99
|
if @path && File.basename(@path) == GEMFILE_NAME
|
|
@@ -114,7 +116,7 @@ module RubyLsp
|
|
|
114
116
|
def on_constant_read_node_enter(node)
|
|
115
117
|
return unless @sorbet_level.ignore?
|
|
116
118
|
|
|
117
|
-
name =
|
|
119
|
+
name = constant_name(node)
|
|
118
120
|
return if name.nil?
|
|
119
121
|
|
|
120
122
|
generate_hover(name, node.location)
|
|
@@ -131,7 +133,7 @@ module RubyLsp
|
|
|
131
133
|
def on_constant_path_node_enter(node)
|
|
132
134
|
return unless @sorbet_level.ignore?
|
|
133
135
|
|
|
134
|
-
name =
|
|
136
|
+
name = constant_name(node)
|
|
135
137
|
return if name.nil?
|
|
136
138
|
|
|
137
139
|
generate_hover(name, node.location)
|
|
@@ -144,6 +146,12 @@ module RubyLsp
|
|
|
144
146
|
message = node.message
|
|
145
147
|
return unless message
|
|
146
148
|
|
|
149
|
+
# `not x` is parsed as a call to `!` whose message_loc slices to "not"
|
|
150
|
+
if node.name == :! && message == "not"
|
|
151
|
+
handle_keyword_documentation("not")
|
|
152
|
+
return
|
|
153
|
+
end
|
|
154
|
+
|
|
147
155
|
handle_method_hover(message)
|
|
148
156
|
end
|
|
149
157
|
|
|
@@ -209,19 +217,150 @@ module RubyLsp
|
|
|
209
217
|
|
|
210
218
|
#: (Prism::SuperNode node) -> void
|
|
211
219
|
def on_super_node_enter(node)
|
|
212
|
-
handle_super_node_hover
|
|
220
|
+
handle_super_node_hover(node.keyword_loc)
|
|
213
221
|
end
|
|
214
222
|
|
|
215
223
|
#: (Prism::ForwardingSuperNode node) -> void
|
|
216
224
|
def on_forwarding_super_node_enter(node)
|
|
217
|
-
handle_super_node_hover
|
|
225
|
+
handle_super_node_hover(node.location)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
#: (Prism::AliasGlobalVariableNode) -> void
|
|
229
|
+
def on_alias_global_variable_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
230
|
+
|
|
231
|
+
#: (Prism::AliasMethodNode) -> void
|
|
232
|
+
def on_alias_method_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
233
|
+
|
|
234
|
+
#: (Prism::AndNode) -> void
|
|
235
|
+
def on_and_node_enter(node) = handle_keyword_at_location(node.operator_loc)
|
|
236
|
+
|
|
237
|
+
#: (Prism::BeginNode) -> void
|
|
238
|
+
def on_begin_node_enter(node) = handle_keyword_at_location(node.begin_keyword_loc, node.end_keyword_loc)
|
|
239
|
+
|
|
240
|
+
#: (Prism::BlockNode) -> void
|
|
241
|
+
def on_block_node_enter(node) = handle_keyword_at_location(node.opening_loc, node.closing_loc)
|
|
242
|
+
|
|
243
|
+
#: (Prism::BreakNode) -> void
|
|
244
|
+
def on_break_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
245
|
+
|
|
246
|
+
#: (Prism::CaseMatchNode) -> void
|
|
247
|
+
def on_case_match_node_enter(node) = handle_keyword_at_location(node.case_keyword_loc, node.end_keyword_loc)
|
|
248
|
+
|
|
249
|
+
#: (Prism::CaseNode) -> void
|
|
250
|
+
def on_case_node_enter(node) = handle_keyword_at_location(node.case_keyword_loc, node.end_keyword_loc)
|
|
251
|
+
|
|
252
|
+
#: (Prism::ClassNode) -> void
|
|
253
|
+
def on_class_node_enter(node) = handle_keyword_at_location(node.class_keyword_loc, node.end_keyword_loc)
|
|
254
|
+
|
|
255
|
+
#: (Prism::SingletonClassNode) -> void
|
|
256
|
+
def on_singleton_class_node_enter(node)
|
|
257
|
+
handle_keyword_at_location(node.class_keyword_loc, node.end_keyword_loc)
|
|
218
258
|
end
|
|
219
259
|
|
|
220
|
-
#: (Prism::
|
|
221
|
-
def
|
|
222
|
-
|
|
260
|
+
#: (Prism::LambdaNode) -> void
|
|
261
|
+
def on_lambda_node_enter(node) = handle_keyword_at_location(node.opening_loc, node.closing_loc)
|
|
262
|
+
|
|
263
|
+
#: (Prism::DefNode) -> void
|
|
264
|
+
def on_def_node_enter(node) = handle_keyword_at_location(node.def_keyword_loc, node.end_keyword_loc)
|
|
265
|
+
|
|
266
|
+
#: (Prism::DefinedNode) -> void
|
|
267
|
+
def on_defined_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
268
|
+
|
|
269
|
+
#: (Prism::ElseNode) -> void
|
|
270
|
+
def on_else_node_enter(node) = handle_keyword_at_location(node.else_keyword_loc, node.end_keyword_loc)
|
|
271
|
+
|
|
272
|
+
#: (Prism::EnsureNode) -> void
|
|
273
|
+
def on_ensure_node_enter(node) = handle_keyword_at_location(node.ensure_keyword_loc, node.end_keyword_loc)
|
|
274
|
+
|
|
275
|
+
#: (Prism::FalseNode) -> void
|
|
276
|
+
def on_false_node_enter(node) = handle_keyword_at_location(node.location)
|
|
277
|
+
|
|
278
|
+
#: (Prism::ForNode) -> void
|
|
279
|
+
def on_for_node_enter(node)
|
|
280
|
+
handle_keyword_at_location(
|
|
281
|
+
node.for_keyword_loc,
|
|
282
|
+
node.in_keyword_loc,
|
|
283
|
+
node.do_keyword_loc,
|
|
284
|
+
node.end_keyword_loc,
|
|
285
|
+
)
|
|
223
286
|
end
|
|
224
287
|
|
|
288
|
+
#: (Prism::IfNode) -> void
|
|
289
|
+
def on_if_node_enter(node)
|
|
290
|
+
handle_keyword_at_location(node.if_keyword_loc, node.then_keyword_loc, node.end_keyword_loc)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
#: (Prism::InNode) -> void
|
|
294
|
+
def on_in_node_enter(node) = handle_keyword_at_location(node.in_loc, node.then_loc)
|
|
295
|
+
|
|
296
|
+
#: (Prism::ModuleNode) -> void
|
|
297
|
+
def on_module_node_enter(node) = handle_keyword_at_location(node.module_keyword_loc, node.end_keyword_loc)
|
|
298
|
+
|
|
299
|
+
#: (Prism::NextNode) -> void
|
|
300
|
+
def on_next_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
301
|
+
|
|
302
|
+
#: (Prism::NilNode) -> void
|
|
303
|
+
def on_nil_node_enter(node) = handle_keyword_at_location(node.location)
|
|
304
|
+
|
|
305
|
+
#: (Prism::OrNode) -> void
|
|
306
|
+
def on_or_node_enter(node) = handle_keyword_at_location(node.operator_loc)
|
|
307
|
+
|
|
308
|
+
#: (Prism::PostExecutionNode) -> void
|
|
309
|
+
def on_post_execution_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
310
|
+
|
|
311
|
+
#: (Prism::PreExecutionNode) -> void
|
|
312
|
+
def on_pre_execution_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
313
|
+
|
|
314
|
+
#: (Prism::RedoNode) -> void
|
|
315
|
+
def on_redo_node_enter(node) = handle_keyword_at_location(node.location)
|
|
316
|
+
|
|
317
|
+
#: (Prism::RescueModifierNode) -> void
|
|
318
|
+
def on_rescue_modifier_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
319
|
+
|
|
320
|
+
#: (Prism::RescueNode) -> void
|
|
321
|
+
def on_rescue_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.then_keyword_loc)
|
|
322
|
+
|
|
323
|
+
#: (Prism::RetryNode) -> void
|
|
324
|
+
def on_retry_node_enter(node) = handle_keyword_at_location(node.location)
|
|
325
|
+
|
|
326
|
+
#: (Prism::ReturnNode) -> void
|
|
327
|
+
def on_return_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
328
|
+
|
|
329
|
+
#: (Prism::SelfNode) -> void
|
|
330
|
+
def on_self_node_enter(node) = handle_keyword_at_location(node.location)
|
|
331
|
+
|
|
332
|
+
#: (Prism::SourceEncodingNode) -> void
|
|
333
|
+
def on_source_encoding_node_enter(node) = handle_keyword_at_location(node.location)
|
|
334
|
+
|
|
335
|
+
#: (Prism::SourceFileNode) -> void
|
|
336
|
+
def on_source_file_node_enter(node) = handle_keyword_at_location(node.location)
|
|
337
|
+
|
|
338
|
+
#: (Prism::SourceLineNode) -> void
|
|
339
|
+
def on_source_line_node_enter(node) = handle_keyword_at_location(node.location)
|
|
340
|
+
|
|
341
|
+
#: (Prism::TrueNode) -> void
|
|
342
|
+
def on_true_node_enter(node) = handle_keyword_at_location(node.location)
|
|
343
|
+
|
|
344
|
+
#: (Prism::UndefNode) -> void
|
|
345
|
+
def on_undef_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
346
|
+
|
|
347
|
+
#: (Prism::UnlessNode) -> void
|
|
348
|
+
def on_unless_node_enter(node)
|
|
349
|
+
handle_keyword_at_location(node.keyword_loc, node.then_keyword_loc, node.end_keyword_loc)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
#: (Prism::UntilNode) -> void
|
|
353
|
+
def on_until_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.do_keyword_loc, node.closing_loc)
|
|
354
|
+
|
|
355
|
+
#: (Prism::WhenNode) -> void
|
|
356
|
+
def on_when_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.then_keyword_loc)
|
|
357
|
+
|
|
358
|
+
#: (Prism::WhileNode) -> void
|
|
359
|
+
def on_while_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.do_keyword_loc, node.closing_loc)
|
|
360
|
+
|
|
361
|
+
#: (Prism::YieldNode) -> void
|
|
362
|
+
def on_yield_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
363
|
+
|
|
225
364
|
#: (Prism::ClassVariableAndWriteNode node) -> void
|
|
226
365
|
def on_class_variable_and_write_node_enter(node)
|
|
227
366
|
handle_variable_hover(node.name.to_s)
|
|
@@ -279,27 +418,37 @@ module RubyLsp
|
|
|
279
418
|
end
|
|
280
419
|
end
|
|
281
420
|
|
|
282
|
-
#: (String
|
|
283
|
-
def handle_keyword_documentation(
|
|
284
|
-
|
|
285
|
-
return unless
|
|
421
|
+
#: (String) -> void
|
|
422
|
+
def handle_keyword_documentation(name)
|
|
423
|
+
keyword = @graph.keyword(name)
|
|
424
|
+
return unless keyword
|
|
286
425
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
@response_builder.push("```ruby\n#{keyword}\n```", category: :title)
|
|
290
|
-
@response_builder.push("[Read more](#{doc_uri})", category: :links)
|
|
291
|
-
@response_builder.push(content, category: :documentation)
|
|
426
|
+
@response_builder.push("```ruby\n#{keyword.name}\n```", category: :title)
|
|
427
|
+
@response_builder.push(keyword.documentation, category: :documentation)
|
|
292
428
|
end
|
|
293
429
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
430
|
+
# Push keyword documentation when the cursor is on one of the provided locations. The keyword name is taken from
|
|
431
|
+
# the covering location's slice so that operator forms (`&&`, `||`, `{`, `}`, ternary `? :`) yield no hover —
|
|
432
|
+
# their slice is not a keyword in the Rubydex graph.
|
|
433
|
+
#
|
|
434
|
+
#: (*Prism::Location?) -> void
|
|
435
|
+
def handle_keyword_at_location(*locations)
|
|
436
|
+
loc = locations.find { |l| l && covers_position?(l, @position) }
|
|
437
|
+
return unless loc
|
|
438
|
+
|
|
439
|
+
handle_keyword_documentation(loc.slice)
|
|
440
|
+
end
|
|
298
441
|
|
|
299
|
-
|
|
300
|
-
|
|
442
|
+
#: (Prism::Location keyword_location) -> void
|
|
443
|
+
def handle_super_node_hover(keyword_location)
|
|
444
|
+
# Sorbet can handle the inherited-method hover on typed true or higher, but it does not surface keyword docs, so
|
|
445
|
+
# we still push those
|
|
446
|
+
unless @sorbet_level.true_or_higher?
|
|
447
|
+
surrounding_method = @node_context.surrounding_method
|
|
448
|
+
handle_method_hover(surrounding_method.name, inherited_only: true) if surrounding_method
|
|
449
|
+
end
|
|
301
450
|
|
|
302
|
-
|
|
451
|
+
handle_keyword_at_location(keyword_location)
|
|
303
452
|
end
|
|
304
453
|
|
|
305
454
|
#: (String message, ?inherited_only: bool) -> void
|
|
@@ -307,20 +456,22 @@ module RubyLsp
|
|
|
307
456
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
|
308
457
|
return unless type
|
|
309
458
|
|
|
310
|
-
|
|
311
|
-
return unless
|
|
459
|
+
owner = @graph[type.name]
|
|
460
|
+
return unless owner.is_a?(Rubydex::Namespace)
|
|
312
461
|
|
|
313
|
-
|
|
462
|
+
method = owner.find_member("#{message}()", only_inherited: inherited_only)
|
|
463
|
+
return unless method.is_a?(Rubydex::Method)
|
|
464
|
+
return unless method_reachable_from_call_site?(method, type, @graph, @node_context)
|
|
314
465
|
|
|
315
|
-
title = "#{message}#{
|
|
316
|
-
title <<
|
|
466
|
+
title = +"#{message}#{method.decorated_parameters}"
|
|
467
|
+
title << method.formatted_signatures
|
|
317
468
|
|
|
318
469
|
if type.is_a?(TypeInferrer::GuessedType)
|
|
319
470
|
title << "\n\nGuessed receiver: #{type.name}"
|
|
320
471
|
@response_builder.push("[Learn more about guessed types](#{GUESSED_TYPES_URL})\n", category: :links)
|
|
321
472
|
end
|
|
322
473
|
|
|
323
|
-
|
|
474
|
+
categorized_markdown_from_definitions(title, method.definitions).each do |category, content|
|
|
324
475
|
@response_builder.push(content, category: category)
|
|
325
476
|
end
|
|
326
477
|
end
|
|
@@ -363,6 +514,7 @@ module RubyLsp
|
|
|
363
514
|
def generate_hover(name, location)
|
|
364
515
|
declaration = @graph.resolve_constant(name, @node_context.nesting)
|
|
365
516
|
return unless declaration
|
|
517
|
+
return unless constant_reachable_from_call_site?(declaration, name, @node_context)
|
|
366
518
|
|
|
367
519
|
categorized_markdown_from_definitions(declaration.name, declaration.definitions).each do |category, content|
|
|
368
520
|
@response_builder.push(content, category: category)
|
|
@@ -11,7 +11,7 @@ module RubyLsp
|
|
|
11
11
|
@sorbet_level = sorbet_level
|
|
12
12
|
@response_builder = response_builder
|
|
13
13
|
@global_state = global_state
|
|
14
|
-
@
|
|
14
|
+
@graph = global_state.graph #: Rubydex::Graph
|
|
15
15
|
@type_inferrer = global_state.type_inferrer #: TypeInferrer
|
|
16
16
|
@node_context = node_context
|
|
17
17
|
dispatcher.register(self, :on_call_node_enter)
|
|
@@ -27,18 +27,17 @@ module RubyLsp
|
|
|
27
27
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
|
28
28
|
return unless type
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
return unless
|
|
30
|
+
owner = @graph[type.name]
|
|
31
|
+
return unless owner.is_a?(Rubydex::Namespace)
|
|
32
32
|
|
|
33
|
-
target_method =
|
|
34
|
-
return unless target_method
|
|
33
|
+
target_method = owner.find_member("#{message}()")
|
|
34
|
+
return unless target_method.is_a?(Rubydex::Method)
|
|
35
35
|
|
|
36
36
|
signatures = target_method.signatures
|
|
37
37
|
|
|
38
|
-
# If the method doesn't have any
|
|
38
|
+
# If the method doesn't have any signatures, there's nothing to show
|
|
39
39
|
return if signatures.empty?
|
|
40
40
|
|
|
41
|
-
name = target_method.name
|
|
42
41
|
title = +""
|
|
43
42
|
|
|
44
43
|
extra_links = if type.is_a?(TypeInferrer::GuessedType)
|
|
@@ -49,7 +48,7 @@ module RubyLsp
|
|
|
49
48
|
active_signature, active_parameter = determine_active_signature_and_parameter(node, signatures)
|
|
50
49
|
|
|
51
50
|
signature_help = Interface::SignatureHelp.new(
|
|
52
|
-
signatures: generate_signatures(signatures,
|
|
51
|
+
signatures: generate_signatures(signatures, message, target_method, title, extra_links),
|
|
53
52
|
active_signature: active_signature,
|
|
54
53
|
active_parameter: active_parameter,
|
|
55
54
|
)
|
|
@@ -58,7 +57,7 @@ module RubyLsp
|
|
|
58
57
|
|
|
59
58
|
private
|
|
60
59
|
|
|
61
|
-
#: (Prism::CallNode node, Array[
|
|
60
|
+
#: (Prism::CallNode node, Array[Rubydex::Signature] signatures) -> [Integer, Integer]
|
|
62
61
|
def determine_active_signature_and_parameter(node, signatures)
|
|
63
62
|
arguments_node = node.arguments
|
|
64
63
|
arguments = arguments_node&.arguments || []
|
|
@@ -86,15 +85,15 @@ module RubyLsp
|
|
|
86
85
|
[active_sig_index, active_parameter]
|
|
87
86
|
end
|
|
88
87
|
|
|
89
|
-
#: (Array[
|
|
90
|
-
def generate_signatures(signatures, method_name,
|
|
88
|
+
#: (Array[Rubydex::Signature] signatures, String method_name, Rubydex::Method method, String title, String? extra_links) -> Array[Interface::SignatureInformation]
|
|
89
|
+
def generate_signatures(signatures, method_name, method, title, extra_links)
|
|
91
90
|
signatures.map do |signature|
|
|
92
91
|
Interface::SignatureInformation.new(
|
|
93
92
|
label: "#{method_name}(#{signature.format})",
|
|
94
93
|
parameters: signature.parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
|
|
95
94
|
documentation: Interface::MarkupContent.new(
|
|
96
95
|
kind: "markdown",
|
|
97
|
-
value:
|
|
96
|
+
value: markdown_from_definitions(title, method.definitions, extra_links: extra_links),
|
|
98
97
|
),
|
|
99
98
|
)
|
|
100
99
|
end
|
|
@@ -34,7 +34,7 @@ module RubyLsp
|
|
|
34
34
|
#: (Prism::ClassNode) -> void
|
|
35
35
|
def on_class_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
36
36
|
with_test_ancestor_tracking(node) do |name, ancestors|
|
|
37
|
-
@spec_group_id_stack << (
|
|
37
|
+
@spec_group_id_stack << (spec_group?(ancestors, name) ? ClassGroup.new(name) : nil)
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -81,6 +81,11 @@ module RubyLsp
|
|
|
81
81
|
|
|
82
82
|
private
|
|
83
83
|
|
|
84
|
+
#: (Array[String], String) -> bool
|
|
85
|
+
def spec_group?(ancestors, fully_qualified_name)
|
|
86
|
+
fully_qualified_name != "Minitest::Spec" && ancestors.include?("Minitest::Spec")
|
|
87
|
+
end
|
|
88
|
+
|
|
84
89
|
#: (Prism::CallNode) -> void
|
|
85
90
|
def handle_describe(node)
|
|
86
91
|
# Describes will include the nesting of all classes and all outer describes as part of its ID, unlike classes
|
|
@@ -13,7 +13,7 @@ module RubyLsp
|
|
|
13
13
|
def initialize(response_builder, global_state, uri)
|
|
14
14
|
@response_builder = response_builder
|
|
15
15
|
@uri = uri
|
|
16
|
-
@
|
|
16
|
+
@graph = global_state.graph #: Rubydex::Graph
|
|
17
17
|
@visibility_stack = [:public] #: Array[Symbol]
|
|
18
18
|
@nesting = [] #: Array[String]
|
|
19
19
|
end
|
|
@@ -56,7 +56,23 @@ module RubyLsp
|
|
|
56
56
|
|
|
57
57
|
#: (String? name) -> String
|
|
58
58
|
def calc_fully_qualified_name(name)
|
|
59
|
-
|
|
59
|
+
parts = name ? @nesting + [name] : @nesting
|
|
60
|
+
return "" if parts.empty?
|
|
61
|
+
|
|
62
|
+
last = parts.last #: as !nil
|
|
63
|
+
rest = parts[0...-1] #: as !nil
|
|
64
|
+
|
|
65
|
+
resolved = @graph.resolve_constant(last, rest)
|
|
66
|
+
return resolved.name if resolved
|
|
67
|
+
|
|
68
|
+
# Fallback for unresolved constants (e.g. dynamic references): preserve top-level reset semantics by
|
|
69
|
+
# truncating at the first `::`-prefixed part when scanning from the innermost out.
|
|
70
|
+
corrected = []
|
|
71
|
+
parts.reverse_each do |part|
|
|
72
|
+
corrected.prepend(part.delete_prefix("::"))
|
|
73
|
+
break if part.start_with?("::")
|
|
74
|
+
end
|
|
75
|
+
corrected.join("::")
|
|
60
76
|
end
|
|
61
77
|
|
|
62
78
|
#: (Prism::ClassNode node, String fully_qualified_name) -> Array[String]
|
|
@@ -64,22 +80,29 @@ module RubyLsp
|
|
|
64
80
|
superclass = node.superclass
|
|
65
81
|
|
|
66
82
|
begin
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
declaration = @graph[fully_qualified_name]
|
|
84
|
+
|
|
85
|
+
unless declaration.is_a?(Rubydex::Namespace)
|
|
86
|
+
# When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
|
|
87
|
+
# provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
|
|
88
|
+
return [superclass&.slice].compact
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
ancestors = declaration.ancestors.map(&:name)
|
|
92
|
+
superclass_ref = declaration.definitions
|
|
93
|
+
.filter_map { |d| d.superclass if d.is_a?(Rubydex::ClassDefinition) }
|
|
94
|
+
.find { |ref| !ref.is_a?(Rubydex::ResolvedConstantReference) || ref.declaration.name != "Object" }
|
|
95
|
+
|
|
96
|
+
# If we couldn't resolve the parent class, then artificially inject it into the ancestors
|
|
97
|
+
if superclass_ref.is_a?(Rubydex::UnresolvedConstantReference) && superclass
|
|
98
|
+
insert_index = ancestors.index(fully_qualified_name) #: as !nil
|
|
99
|
+
insert_index += 1
|
|
100
|
+
ancestors.insert(insert_index, superclass.slice)
|
|
101
|
+
return ancestors
|
|
76
102
|
end
|
|
77
103
|
|
|
104
|
+
# If the parent class is properly resolved or if there isn't one, then just use the ancestors
|
|
78
105
|
ancestors
|
|
79
|
-
rescue RubyIndexer::Index::NonExistingNamespaceError
|
|
80
|
-
# When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
|
|
81
|
-
# provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
|
|
82
|
-
[superclass&.slice].compact
|
|
83
106
|
end
|
|
84
107
|
end
|
|
85
108
|
|