ruby-lsp 0.27.0.beta1 → 0.27.0.beta2
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/lib/ruby_indexer/ruby_indexer.rb +0 -1
- data/lib/ruby_lsp/addon.rb +19 -19
- data/lib/ruby_lsp/global_state.rb +1 -1
- data/lib/ruby_lsp/internal.rb +1 -1
- data/lib/ruby_lsp/listeners/definition.rb +1 -1
- data/lib/ruby_lsp/listeners/document_link.rb +4 -0
- data/lib/ruby_lsp/listeners/hover.rb +223 -73
- data/lib/ruby_lsp/listeners/spec_style.rb +6 -1
- data/lib/ruby_lsp/listeners/test_discovery.rb +21 -14
- data/lib/ruby_lsp/listeners/test_style.rb +20 -8
- data/lib/ruby_lsp/node_context.rb +31 -8
- data/lib/ruby_lsp/requests/completion_resolve.rb +9 -13
- 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 +170 -70
- data/lib/ruby_lsp/requests/request.rb +3 -33
- data/lib/ruby_lsp/requests/support/common.rb +13 -0
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +82 -46
- data/lib/ruby_lsp/rubydex/declaration.rb +48 -0
- data/lib/ruby_lsp/rubydex/definition.rb +57 -0
- data/lib/ruby_lsp/rubydex/reference.rb +6 -1
- data/lib/ruby_lsp/server.rb +49 -9
- data/lib/ruby_lsp/type_inferrer.rb +87 -9
- metadata +2 -5
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +0 -335
- data/lib/ruby_lsp/static_docs.rb +0 -20
- data/static_docs/break.md +0 -103
- data/static_docs/yield.md +0 -81
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 11c508848c7c0db2c86807dd05b5de3a0cf33c8d69259cffd34865af8f50d93b
|
|
4
|
+
data.tar.gz: d030d7223e7714008fef42f5b8635b373cf42f88f60e3c22a878dd1e48ed2ee8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23d4677fea98fe4723442e9162c9835ce9813eab354882c9b3cbd45deb4fc9cd81a91d1618cdb8e580c68eeaf840a45cc7271c031b2b4bf559d92a50d64755ec
|
|
7
|
+
data.tar.gz: 1e044d2758e1872a1a94df7f288e8e2ff4b1b4ca2a089db8958acf5830bacd8247086c1c0df167fe072a6586a8391332ceaaa1827add9280e9c42cd3ce96bdf2
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.27.0.
|
|
1
|
+
0.27.0.beta2
|
|
@@ -7,7 +7,6 @@ require "did_you_mean"
|
|
|
7
7
|
require "ruby_indexer/lib/ruby_indexer/uri"
|
|
8
8
|
require "ruby_indexer/lib/ruby_indexer/visibility_scope"
|
|
9
9
|
require "ruby_indexer/lib/ruby_indexer/declaration_listener"
|
|
10
|
-
require "ruby_indexer/lib/ruby_indexer/reference_finder"
|
|
11
10
|
require "ruby_indexer/lib/ruby_indexer/enhancement"
|
|
12
11
|
require "ruby_indexer/lib/ruby_indexer/index"
|
|
13
12
|
require "ruby_indexer/lib/ruby_indexer/entry"
|
data/lib/ruby_lsp/addon.rb
CHANGED
|
@@ -56,26 +56,9 @@ module RubyLsp
|
|
|
56
56
|
addon_files = Gem.find_files("ruby_lsp/**/addon.rb")
|
|
57
57
|
|
|
58
58
|
if include_project_addons
|
|
59
|
-
project_addons = Dir.glob("#{global_state.workspace_path}/**/ruby_lsp/**/addon.rb")
|
|
60
59
|
bundle_path = Bundler.bundle_path.to_s
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# Create an array of rejection glob patterns to ignore add-ons already discovered through Gem.find_files if
|
|
64
|
-
# they are also copied inside the workspace for whatever reason. We received reports of projects having gems
|
|
65
|
-
# installed in vendor/bundle despite BUNDLE_PATH pointing elsewhere. Without this mechanism, we will
|
|
66
|
-
# double-require the same add-on, potentially for different versions of the same gem, which leads to incorrect
|
|
67
|
-
# behavior
|
|
68
|
-
reject_glob_patterns = addon_files.map do |path|
|
|
69
|
-
relative_gem_path = Pathname.new(path).relative_path_from(gems_dir)
|
|
70
|
-
first_part, *parts = relative_gem_path.to_s.split(File::SEPARATOR)
|
|
71
|
-
first_part&.gsub!(/-([0-9.]+)$/, "*")
|
|
72
|
-
"**/#{first_part}/#{parts.join("/")}"
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
project_addons.reject! do |path|
|
|
76
|
-
path.start_with?(bundle_path) ||
|
|
77
|
-
reject_glob_patterns.any? { |pattern| File.fnmatch?(pattern, path, File::Constants::FNM_PATHNAME) }
|
|
78
|
-
end
|
|
60
|
+
project_addons = Dir.glob("#{global_state.workspace_path}/**/ruby_lsp/**/addon.rb")
|
|
61
|
+
project_addons.reject! { |path| path.start_with?(bundle_path) || gem_installation_path?(path) }
|
|
79
62
|
|
|
80
63
|
addon_files.concat(project_addons)
|
|
81
64
|
end
|
|
@@ -162,6 +145,23 @@ module RubyLsp
|
|
|
162
145
|
"Add-on is not compatible with this version of the Ruby LSP. Skipping its activation"
|
|
163
146
|
end
|
|
164
147
|
end
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
# Checks if a path appears to be inside a versioned gem installation directory (e.g., `rubocop-1.73.0/lib/...`) by
|
|
152
|
+
# looking for a directory segment matching `name-version` before the `lib` component
|
|
153
|
+
#
|
|
154
|
+
#: (String path) -> bool
|
|
155
|
+
def gem_installation_path?(path)
|
|
156
|
+
parts = path.split(%r{[/\\]})
|
|
157
|
+
lib_index = parts.rindex("lib")
|
|
158
|
+
return false unless lib_index
|
|
159
|
+
|
|
160
|
+
prefix = parts[0...lib_index] #: Array[String]?
|
|
161
|
+
return false unless prefix
|
|
162
|
+
|
|
163
|
+
prefix.any? { |part| part.match?(/-\d+(\.\d+)+$/) }
|
|
164
|
+
end
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
#: -> void
|
data/lib/ruby_lsp/internal.rb
CHANGED
|
@@ -32,6 +32,7 @@ require "shellwords"
|
|
|
32
32
|
require "set"
|
|
33
33
|
|
|
34
34
|
# Rubydex LSP additions
|
|
35
|
+
require "ruby_lsp/rubydex/declaration"
|
|
35
36
|
require "ruby_lsp/rubydex/definition"
|
|
36
37
|
require "ruby_lsp/rubydex/reference"
|
|
37
38
|
|
|
@@ -39,7 +40,6 @@ require "ruby-lsp"
|
|
|
39
40
|
require "ruby_lsp/base_server"
|
|
40
41
|
require "ruby_indexer/ruby_indexer"
|
|
41
42
|
require "ruby_lsp/utils"
|
|
42
|
-
require "ruby_lsp/static_docs"
|
|
43
43
|
require "ruby_lsp/scope"
|
|
44
44
|
require "ruby_lsp/client_capabilities"
|
|
45
45
|
require "ruby_lsp/global_state"
|
|
@@ -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,45 +6,13 @@ 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
18
|
@index = global_state.index #: RubyIndexer::Index
|
|
@@ -53,45 +21,80 @@ module RubyLsp
|
|
|
53
21
|
@path = uri.to_standardized_path #: String?
|
|
54
22
|
@node_context = node_context
|
|
55
23
|
@sorbet_level = sorbet_level
|
|
24
|
+
@position = position
|
|
56
25
|
|
|
57
26
|
dispatcher.register(
|
|
58
27
|
self,
|
|
28
|
+
:on_alias_global_variable_node_enter,
|
|
29
|
+
:on_alias_method_node_enter,
|
|
30
|
+
:on_and_node_enter,
|
|
31
|
+
:on_begin_node_enter,
|
|
32
|
+
:on_block_node_enter,
|
|
59
33
|
:on_break_node_enter,
|
|
34
|
+
:on_call_node_enter,
|
|
35
|
+
:on_case_match_node_enter,
|
|
36
|
+
:on_case_node_enter,
|
|
37
|
+
:on_class_node_enter,
|
|
38
|
+
:on_singleton_class_node_enter,
|
|
39
|
+
:on_lambda_node_enter,
|
|
40
|
+
:on_class_variable_and_write_node_enter,
|
|
41
|
+
:on_class_variable_operator_write_node_enter,
|
|
42
|
+
:on_class_variable_or_write_node_enter,
|
|
43
|
+
:on_class_variable_read_node_enter,
|
|
44
|
+
:on_class_variable_target_node_enter,
|
|
45
|
+
:on_class_variable_write_node_enter,
|
|
46
|
+
:on_constant_path_node_enter,
|
|
60
47
|
:on_constant_read_node_enter,
|
|
61
48
|
:on_constant_write_node_enter,
|
|
62
|
-
:
|
|
63
|
-
:
|
|
49
|
+
:on_def_node_enter,
|
|
50
|
+
:on_defined_node_enter,
|
|
51
|
+
:on_else_node_enter,
|
|
52
|
+
:on_ensure_node_enter,
|
|
53
|
+
:on_false_node_enter,
|
|
54
|
+
:on_for_node_enter,
|
|
55
|
+
:on_forwarding_super_node_enter,
|
|
64
56
|
:on_global_variable_and_write_node_enter,
|
|
65
57
|
:on_global_variable_operator_write_node_enter,
|
|
66
58
|
:on_global_variable_or_write_node_enter,
|
|
67
59
|
:on_global_variable_read_node_enter,
|
|
68
60
|
:on_global_variable_target_node_enter,
|
|
69
61
|
:on_global_variable_write_node_enter,
|
|
70
|
-
:
|
|
71
|
-
:
|
|
62
|
+
:on_if_node_enter,
|
|
63
|
+
:on_in_node_enter,
|
|
72
64
|
:on_instance_variable_and_write_node_enter,
|
|
73
65
|
:on_instance_variable_operator_write_node_enter,
|
|
74
66
|
:on_instance_variable_or_write_node_enter,
|
|
67
|
+
:on_instance_variable_read_node_enter,
|
|
75
68
|
:on_instance_variable_target_node_enter,
|
|
76
|
-
:
|
|
77
|
-
:on_forwarding_super_node_enter,
|
|
78
|
-
:on_string_node_enter,
|
|
69
|
+
:on_instance_variable_write_node_enter,
|
|
79
70
|
:on_interpolated_string_node_enter,
|
|
71
|
+
:on_module_node_enter,
|
|
72
|
+
:on_next_node_enter,
|
|
73
|
+
:on_nil_node_enter,
|
|
74
|
+
:on_or_node_enter,
|
|
75
|
+
:on_post_execution_node_enter,
|
|
76
|
+
:on_pre_execution_node_enter,
|
|
77
|
+
:on_redo_node_enter,
|
|
78
|
+
:on_rescue_modifier_node_enter,
|
|
79
|
+
:on_rescue_node_enter,
|
|
80
|
+
:on_retry_node_enter,
|
|
81
|
+
:on_return_node_enter,
|
|
82
|
+
:on_self_node_enter,
|
|
83
|
+
:on_source_encoding_node_enter,
|
|
84
|
+
:on_source_file_node_enter,
|
|
85
|
+
:on_source_line_node_enter,
|
|
86
|
+
:on_string_node_enter,
|
|
87
|
+
:on_super_node_enter,
|
|
88
|
+
:on_true_node_enter,
|
|
89
|
+
:on_undef_node_enter,
|
|
90
|
+
:on_unless_node_enter,
|
|
91
|
+
:on_until_node_enter,
|
|
92
|
+
:on_when_node_enter,
|
|
93
|
+
:on_while_node_enter,
|
|
80
94
|
: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
95
|
)
|
|
88
96
|
end
|
|
89
97
|
|
|
90
|
-
#: (Prism::BreakNode node) -> void
|
|
91
|
-
def on_break_node_enter(node)
|
|
92
|
-
handle_keyword_documentation(node.keyword)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
98
|
#: (Prism::StringNode node) -> void
|
|
96
99
|
def on_string_node_enter(node)
|
|
97
100
|
if @path && File.basename(@path) == GEMFILE_NAME
|
|
@@ -144,6 +147,12 @@ module RubyLsp
|
|
|
144
147
|
message = node.message
|
|
145
148
|
return unless message
|
|
146
149
|
|
|
150
|
+
# `not x` is parsed as a call to `!` whose message_loc slices to "not"
|
|
151
|
+
if node.name == :! && message == "not"
|
|
152
|
+
handle_keyword_documentation("not")
|
|
153
|
+
return
|
|
154
|
+
end
|
|
155
|
+
|
|
147
156
|
handle_method_hover(message)
|
|
148
157
|
end
|
|
149
158
|
|
|
@@ -209,19 +218,150 @@ module RubyLsp
|
|
|
209
218
|
|
|
210
219
|
#: (Prism::SuperNode node) -> void
|
|
211
220
|
def on_super_node_enter(node)
|
|
212
|
-
handle_super_node_hover
|
|
221
|
+
handle_super_node_hover(node.keyword_loc)
|
|
213
222
|
end
|
|
214
223
|
|
|
215
224
|
#: (Prism::ForwardingSuperNode node) -> void
|
|
216
225
|
def on_forwarding_super_node_enter(node)
|
|
217
|
-
handle_super_node_hover
|
|
226
|
+
handle_super_node_hover(node.location)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
#: (Prism::AliasGlobalVariableNode) -> void
|
|
230
|
+
def on_alias_global_variable_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
231
|
+
|
|
232
|
+
#: (Prism::AliasMethodNode) -> void
|
|
233
|
+
def on_alias_method_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
234
|
+
|
|
235
|
+
#: (Prism::AndNode) -> void
|
|
236
|
+
def on_and_node_enter(node) = handle_keyword_at_location(node.operator_loc)
|
|
237
|
+
|
|
238
|
+
#: (Prism::BeginNode) -> void
|
|
239
|
+
def on_begin_node_enter(node) = handle_keyword_at_location(node.begin_keyword_loc, node.end_keyword_loc)
|
|
240
|
+
|
|
241
|
+
#: (Prism::BlockNode) -> void
|
|
242
|
+
def on_block_node_enter(node) = handle_keyword_at_location(node.opening_loc, node.closing_loc)
|
|
243
|
+
|
|
244
|
+
#: (Prism::BreakNode) -> void
|
|
245
|
+
def on_break_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
246
|
+
|
|
247
|
+
#: (Prism::CaseMatchNode) -> void
|
|
248
|
+
def on_case_match_node_enter(node) = handle_keyword_at_location(node.case_keyword_loc, node.end_keyword_loc)
|
|
249
|
+
|
|
250
|
+
#: (Prism::CaseNode) -> void
|
|
251
|
+
def on_case_node_enter(node) = handle_keyword_at_location(node.case_keyword_loc, node.end_keyword_loc)
|
|
252
|
+
|
|
253
|
+
#: (Prism::ClassNode) -> void
|
|
254
|
+
def on_class_node_enter(node) = handle_keyword_at_location(node.class_keyword_loc, node.end_keyword_loc)
|
|
255
|
+
|
|
256
|
+
#: (Prism::SingletonClassNode) -> void
|
|
257
|
+
def on_singleton_class_node_enter(node)
|
|
258
|
+
handle_keyword_at_location(node.class_keyword_loc, node.end_keyword_loc)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
#: (Prism::LambdaNode) -> void
|
|
262
|
+
def on_lambda_node_enter(node) = handle_keyword_at_location(node.opening_loc, node.closing_loc)
|
|
263
|
+
|
|
264
|
+
#: (Prism::DefNode) -> void
|
|
265
|
+
def on_def_node_enter(node) = handle_keyword_at_location(node.def_keyword_loc, node.end_keyword_loc)
|
|
266
|
+
|
|
267
|
+
#: (Prism::DefinedNode) -> void
|
|
268
|
+
def on_defined_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
269
|
+
|
|
270
|
+
#: (Prism::ElseNode) -> void
|
|
271
|
+
def on_else_node_enter(node) = handle_keyword_at_location(node.else_keyword_loc, node.end_keyword_loc)
|
|
272
|
+
|
|
273
|
+
#: (Prism::EnsureNode) -> void
|
|
274
|
+
def on_ensure_node_enter(node) = handle_keyword_at_location(node.ensure_keyword_loc, node.end_keyword_loc)
|
|
275
|
+
|
|
276
|
+
#: (Prism::FalseNode) -> void
|
|
277
|
+
def on_false_node_enter(node) = handle_keyword_at_location(node.location)
|
|
278
|
+
|
|
279
|
+
#: (Prism::ForNode) -> void
|
|
280
|
+
def on_for_node_enter(node)
|
|
281
|
+
handle_keyword_at_location(
|
|
282
|
+
node.for_keyword_loc,
|
|
283
|
+
node.in_keyword_loc,
|
|
284
|
+
node.do_keyword_loc,
|
|
285
|
+
node.end_keyword_loc,
|
|
286
|
+
)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
#: (Prism::IfNode) -> void
|
|
290
|
+
def on_if_node_enter(node)
|
|
291
|
+
handle_keyword_at_location(node.if_keyword_loc, node.then_keyword_loc, node.end_keyword_loc)
|
|
218
292
|
end
|
|
219
293
|
|
|
220
|
-
#: (Prism::
|
|
221
|
-
def
|
|
222
|
-
|
|
294
|
+
#: (Prism::InNode) -> void
|
|
295
|
+
def on_in_node_enter(node) = handle_keyword_at_location(node.in_loc, node.then_loc)
|
|
296
|
+
|
|
297
|
+
#: (Prism::ModuleNode) -> void
|
|
298
|
+
def on_module_node_enter(node) = handle_keyword_at_location(node.module_keyword_loc, node.end_keyword_loc)
|
|
299
|
+
|
|
300
|
+
#: (Prism::NextNode) -> void
|
|
301
|
+
def on_next_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
302
|
+
|
|
303
|
+
#: (Prism::NilNode) -> void
|
|
304
|
+
def on_nil_node_enter(node) = handle_keyword_at_location(node.location)
|
|
305
|
+
|
|
306
|
+
#: (Prism::OrNode) -> void
|
|
307
|
+
def on_or_node_enter(node) = handle_keyword_at_location(node.operator_loc)
|
|
308
|
+
|
|
309
|
+
#: (Prism::PostExecutionNode) -> void
|
|
310
|
+
def on_post_execution_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
311
|
+
|
|
312
|
+
#: (Prism::PreExecutionNode) -> void
|
|
313
|
+
def on_pre_execution_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
314
|
+
|
|
315
|
+
#: (Prism::RedoNode) -> void
|
|
316
|
+
def on_redo_node_enter(node) = handle_keyword_at_location(node.location)
|
|
317
|
+
|
|
318
|
+
#: (Prism::RescueModifierNode) -> void
|
|
319
|
+
def on_rescue_modifier_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
320
|
+
|
|
321
|
+
#: (Prism::RescueNode) -> void
|
|
322
|
+
def on_rescue_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.then_keyword_loc)
|
|
323
|
+
|
|
324
|
+
#: (Prism::RetryNode) -> void
|
|
325
|
+
def on_retry_node_enter(node) = handle_keyword_at_location(node.location)
|
|
326
|
+
|
|
327
|
+
#: (Prism::ReturnNode) -> void
|
|
328
|
+
def on_return_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
329
|
+
|
|
330
|
+
#: (Prism::SelfNode) -> void
|
|
331
|
+
def on_self_node_enter(node) = handle_keyword_at_location(node.location)
|
|
332
|
+
|
|
333
|
+
#: (Prism::SourceEncodingNode) -> void
|
|
334
|
+
def on_source_encoding_node_enter(node) = handle_keyword_at_location(node.location)
|
|
335
|
+
|
|
336
|
+
#: (Prism::SourceFileNode) -> void
|
|
337
|
+
def on_source_file_node_enter(node) = handle_keyword_at_location(node.location)
|
|
338
|
+
|
|
339
|
+
#: (Prism::SourceLineNode) -> void
|
|
340
|
+
def on_source_line_node_enter(node) = handle_keyword_at_location(node.location)
|
|
341
|
+
|
|
342
|
+
#: (Prism::TrueNode) -> void
|
|
343
|
+
def on_true_node_enter(node) = handle_keyword_at_location(node.location)
|
|
344
|
+
|
|
345
|
+
#: (Prism::UndefNode) -> void
|
|
346
|
+
def on_undef_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
347
|
+
|
|
348
|
+
#: (Prism::UnlessNode) -> void
|
|
349
|
+
def on_unless_node_enter(node)
|
|
350
|
+
handle_keyword_at_location(node.keyword_loc, node.then_keyword_loc, node.end_keyword_loc)
|
|
223
351
|
end
|
|
224
352
|
|
|
353
|
+
#: (Prism::UntilNode) -> void
|
|
354
|
+
def on_until_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.do_keyword_loc, node.closing_loc)
|
|
355
|
+
|
|
356
|
+
#: (Prism::WhenNode) -> void
|
|
357
|
+
def on_when_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.then_keyword_loc)
|
|
358
|
+
|
|
359
|
+
#: (Prism::WhileNode) -> void
|
|
360
|
+
def on_while_node_enter(node) = handle_keyword_at_location(node.keyword_loc, node.do_keyword_loc, node.closing_loc)
|
|
361
|
+
|
|
362
|
+
#: (Prism::YieldNode) -> void
|
|
363
|
+
def on_yield_node_enter(node) = handle_keyword_at_location(node.keyword_loc)
|
|
364
|
+
|
|
225
365
|
#: (Prism::ClassVariableAndWriteNode node) -> void
|
|
226
366
|
def on_class_variable_and_write_node_enter(node)
|
|
227
367
|
handle_variable_hover(node.name.to_s)
|
|
@@ -279,27 +419,37 @@ module RubyLsp
|
|
|
279
419
|
end
|
|
280
420
|
end
|
|
281
421
|
|
|
282
|
-
#: (String
|
|
283
|
-
def handle_keyword_documentation(
|
|
284
|
-
|
|
285
|
-
return unless
|
|
422
|
+
#: (String) -> void
|
|
423
|
+
def handle_keyword_documentation(name)
|
|
424
|
+
keyword = @graph.keyword(name)
|
|
425
|
+
return unless keyword
|
|
286
426
|
|
|
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)
|
|
427
|
+
@response_builder.push("```ruby\n#{keyword.name}\n```", category: :title)
|
|
428
|
+
@response_builder.push(keyword.documentation, category: :documentation)
|
|
292
429
|
end
|
|
293
430
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
431
|
+
# Push keyword documentation when the cursor is on one of the provided locations. The keyword name is taken from
|
|
432
|
+
# the covering location's slice so that operator forms (`&&`, `||`, `{`, `}`, ternary `? :`) yield no hover —
|
|
433
|
+
# their slice is not a keyword in the Rubydex graph.
|
|
434
|
+
#
|
|
435
|
+
#: (*Prism::Location?) -> void
|
|
436
|
+
def handle_keyword_at_location(*locations)
|
|
437
|
+
loc = locations.find { |l| l && covers_position?(l, @position) }
|
|
438
|
+
return unless loc
|
|
298
439
|
|
|
299
|
-
|
|
300
|
-
|
|
440
|
+
handle_keyword_documentation(loc.slice)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
#: (Prism::Location keyword_location) -> void
|
|
444
|
+
def handle_super_node_hover(keyword_location)
|
|
445
|
+
# Sorbet can handle the inherited-method hover on typed true or higher, but it does not surface keyword docs, so
|
|
446
|
+
# we still push those
|
|
447
|
+
unless @sorbet_level.true_or_higher?
|
|
448
|
+
surrounding_method = @node_context.surrounding_method
|
|
449
|
+
handle_method_hover(surrounding_method.name, inherited_only: true) if surrounding_method
|
|
450
|
+
end
|
|
301
451
|
|
|
302
|
-
|
|
452
|
+
handle_keyword_at_location(keyword_location)
|
|
303
453
|
end
|
|
304
454
|
|
|
305
455
|
#: (String message, ?inherited_only: bool) -> void
|
|
@@ -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
|
|
@@ -64,22 +64,29 @@ module RubyLsp
|
|
|
64
64
|
superclass = node.superclass
|
|
65
65
|
|
|
66
66
|
begin
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
declaration = @graph[fully_qualified_name]
|
|
68
|
+
|
|
69
|
+
unless declaration.is_a?(Rubydex::Namespace)
|
|
70
|
+
# When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
|
|
71
|
+
# provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
|
|
72
|
+
return [superclass&.slice].compact
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
ancestors = declaration.ancestors.map(&:name)
|
|
76
|
+
superclass_ref = declaration.definitions
|
|
77
|
+
.filter_map { |d| d.superclass if d.is_a?(Rubydex::ClassDefinition) }
|
|
78
|
+
.find { |ref| !ref.is_a?(Rubydex::ResolvedConstantReference) || ref.declaration.name != "Object" }
|
|
79
|
+
|
|
80
|
+
# If we couldn't resolve the parent class, then artificially inject it into the ancestors
|
|
81
|
+
if superclass_ref.is_a?(Rubydex::UnresolvedConstantReference) && superclass
|
|
82
|
+
insert_index = ancestors.index(fully_qualified_name) #: as !nil
|
|
83
|
+
insert_index += 1
|
|
84
|
+
ancestors.insert(insert_index, superclass.slice)
|
|
85
|
+
return ancestors
|
|
76
86
|
end
|
|
77
87
|
|
|
88
|
+
# If the parent class is properly resolved or if there isn't one, then just use the ancestors
|
|
78
89
|
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
90
|
end
|
|
84
91
|
end
|
|
85
92
|
|
|
@@ -174,9 +174,10 @@ module RubyLsp
|
|
|
174
174
|
#: (Prism::ClassNode node) -> void
|
|
175
175
|
def on_class_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
176
176
|
with_test_ancestor_tracking(node) do |name, ancestors|
|
|
177
|
-
|
|
177
|
+
is_test_unit = test_unit_group?(ancestors, name)
|
|
178
|
+
@framework = :test_unit if is_test_unit
|
|
178
179
|
|
|
179
|
-
if
|
|
180
|
+
if is_test_unit || non_declarative_minitest?(ancestors, name)
|
|
180
181
|
test_item = Requests::Support::TestItem.new(
|
|
181
182
|
name,
|
|
182
183
|
name,
|
|
@@ -259,17 +260,28 @@ module RubyLsp
|
|
|
259
260
|
@parent_stack[index] #: as Requests::Support::TestItem | ResponseBuilders::TestCollection
|
|
260
261
|
end
|
|
261
262
|
|
|
262
|
-
#: (Array[String]
|
|
263
|
+
#: (Array[String], String) -> bool
|
|
264
|
+
def test_unit_group?(ancestors, fully_qualified_name)
|
|
265
|
+
fully_qualified_name != "Test::Unit::TestCase" && ancestors.include?("Test::Unit::TestCase")
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
#: (Array[String], String) -> bool
|
|
263
269
|
def non_declarative_minitest?(attached_ancestors, fully_qualified_name)
|
|
270
|
+
return false if ["Minitest::Spec", "Minitest::Test", "ActiveSupport::TestCase"].include?(fully_qualified_name)
|
|
264
271
|
return false unless attached_ancestors.include?("Minitest::Test")
|
|
265
272
|
|
|
266
273
|
# We only support regular Minitest tests. The declarative syntax provided by ActiveSupport is handled by the
|
|
267
274
|
# Rails add-on
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
true
|
|
275
|
+
|
|
276
|
+
declaration = @graph[fully_qualified_name]
|
|
277
|
+
# If we don't find the fully qualified name in the graph, it means there's a dynamic portion in the test class
|
|
278
|
+
# definition. In that case, if the ancestors did include `Minitest::Test`, we always assume it's a test
|
|
279
|
+
return true unless declaration.is_a?(Rubydex::Namespace)
|
|
280
|
+
|
|
281
|
+
singleton = declaration.singleton_class
|
|
282
|
+
return !singleton.ancestors.map(&:name).include?("ActiveSupport::Testing::Declarative") if singleton
|
|
283
|
+
|
|
284
|
+
!attached_ancestors.include?("ActiveSupport::TestCase")
|
|
273
285
|
end
|
|
274
286
|
end
|
|
275
287
|
end
|