ruby-lsp 0.17.2 → 0.17.4
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 +2 -0
- data/VERSION +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +280 -74
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +102 -102
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +234 -56
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +147 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +49 -2
- data/lib/ruby_indexer/test/configuration_test.rb +1 -1
- data/lib/ruby_indexer/test/constant_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +702 -71
- data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
- data/lib/ruby_indexer/test/method_test.rb +74 -24
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +67 -0
- data/lib/ruby_indexer/test/test_case.rb +7 -0
- data/lib/ruby_lsp/document.rb +37 -8
- data/lib/ruby_lsp/global_state.rb +43 -18
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/code_lens.rb +2 -2
- data/lib/ruby_lsp/listeners/completion.rb +53 -14
- data/lib/ruby_lsp/listeners/definition.rb +11 -7
- data/lib/ruby_lsp/listeners/hover.rb +14 -7
- data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
- data/lib/ruby_lsp/node_context.rb +6 -1
- data/lib/ruby_lsp/requests/completion.rb +5 -4
- data/lib/ruby_lsp/requests/completion_resolve.rb +8 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
- data/lib/ruby_lsp/requests/support/common.rb +19 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +91 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/server.rb +54 -4
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +86 -0
- metadata +29 -4
@@ -15,15 +15,26 @@ module RubyLsp
|
|
15
15
|
typechecker_enabled: T::Boolean,
|
16
16
|
dispatcher: Prism::Dispatcher,
|
17
17
|
uri: URI::Generic,
|
18
|
+
trigger_character: T.nilable(String),
|
18
19
|
).void
|
19
20
|
end
|
20
|
-
def initialize(
|
21
|
+
def initialize( # rubocop:disable Metrics/ParameterLists
|
22
|
+
response_builder,
|
23
|
+
global_state,
|
24
|
+
node_context,
|
25
|
+
typechecker_enabled,
|
26
|
+
dispatcher,
|
27
|
+
uri,
|
28
|
+
trigger_character
|
29
|
+
)
|
21
30
|
@response_builder = response_builder
|
22
31
|
@global_state = global_state
|
23
32
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
33
|
+
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
24
34
|
@node_context = node_context
|
25
35
|
@typechecker_enabled = typechecker_enabled
|
26
36
|
@uri = uri
|
37
|
+
@trigger_character = trigger_character
|
27
38
|
|
28
39
|
dispatcher.register(
|
29
40
|
self,
|
@@ -42,7 +53,7 @@ module RubyLsp
|
|
42
53
|
# Handle completion on regular constant references (e.g. `Bar`)
|
43
54
|
sig { params(node: Prism::ConstantReadNode).void }
|
44
55
|
def on_constant_read_node_enter(node)
|
45
|
-
return if @global_state.
|
56
|
+
return if @global_state.has_type_checker
|
46
57
|
|
47
58
|
name = constant_name(node)
|
48
59
|
return if name.nil?
|
@@ -63,7 +74,7 @@ module RubyLsp
|
|
63
74
|
# Handle completion on namespaced constant references (e.g. `Foo::Bar`)
|
64
75
|
sig { params(node: Prism::ConstantPathNode).void }
|
65
76
|
def on_constant_path_node_enter(node)
|
66
|
-
return if @global_state.
|
77
|
+
return if @global_state.has_type_checker
|
67
78
|
|
68
79
|
name = constant_name(node)
|
69
80
|
return if name.nil?
|
@@ -107,7 +118,7 @@ module RubyLsp
|
|
107
118
|
when "require_relative"
|
108
119
|
complete_require_relative(node)
|
109
120
|
else
|
110
|
-
|
121
|
+
complete_methods(node, name) unless @typechecker_enabled
|
111
122
|
end
|
112
123
|
end
|
113
124
|
|
@@ -158,7 +169,7 @@ module RubyLsp
|
|
158
169
|
name.delete_suffix("::")
|
159
170
|
else
|
160
171
|
*namespace, incomplete_name = name.split("::")
|
161
|
-
|
172
|
+
namespace.join("::")
|
162
173
|
end
|
163
174
|
|
164
175
|
nesting = @node_context.nesting
|
@@ -192,7 +203,10 @@ module RubyLsp
|
|
192
203
|
|
193
204
|
sig { params(name: String, location: Prism::Location).void }
|
194
205
|
def handle_instance_variable_completion(name, location)
|
195
|
-
@
|
206
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
207
|
+
return unless type
|
208
|
+
|
209
|
+
@index.instance_variable_completion_candidates(name, type).each do |entry|
|
196
210
|
variable_name = entry.name
|
197
211
|
|
198
212
|
@response_builder << Interface::CompletionItem.new(
|
@@ -257,20 +271,45 @@ module RubyLsp
|
|
257
271
|
end
|
258
272
|
|
259
273
|
sig { params(node: Prism::CallNode, name: String).void }
|
260
|
-
def
|
261
|
-
|
262
|
-
return unless
|
274
|
+
def complete_methods(node, name)
|
275
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
276
|
+
return unless type
|
277
|
+
|
278
|
+
# When the trigger character is a dot, Prism matches the name of the call node to whatever is next in the source
|
279
|
+
# code, leading to us searching for the wrong name. What we want to do instead is show every available method
|
280
|
+
# when dot is pressed
|
281
|
+
method_name = @trigger_character == "." ? nil : name
|
282
|
+
|
283
|
+
range = if method_name
|
284
|
+
range_from_location(T.must(node.message_loc))
|
285
|
+
else
|
286
|
+
loc = T.must(node.call_operator_loc)
|
287
|
+
Interface::Range.new(
|
288
|
+
start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column + 1),
|
289
|
+
end: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column + 1),
|
290
|
+
)
|
291
|
+
end
|
263
292
|
|
264
|
-
|
293
|
+
@index.method_completion_candidates(method_name, type).each do |entry|
|
294
|
+
entry_name = entry.name
|
265
295
|
|
266
|
-
|
267
|
-
|
296
|
+
@response_builder << Interface::CompletionItem.new(
|
297
|
+
label: entry_name,
|
298
|
+
filter_text: entry_name,
|
299
|
+
text_edit: Interface::TextEdit.new(range: range, new_text: entry_name),
|
300
|
+
kind: Constant::CompletionItemKind::METHOD,
|
301
|
+
data: {
|
302
|
+
owner_name: entry.owner&.name,
|
303
|
+
},
|
304
|
+
)
|
268
305
|
end
|
306
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
307
|
+
# We have not indexed this namespace, so we can't provide any completions
|
269
308
|
end
|
270
309
|
|
271
310
|
sig do
|
272
311
|
params(
|
273
|
-
entry: RubyIndexer::Entry::Member,
|
312
|
+
entry: T.any(RubyIndexer::Entry::Member, RubyIndexer::Entry::MethodAlias),
|
274
313
|
node: Prism::CallNode,
|
275
314
|
).returns(Interface::CompletionItem)
|
276
315
|
end
|
@@ -283,7 +322,7 @@ module RubyLsp
|
|
283
322
|
text_edit: Interface::TextEdit.new(range: range_from_location(T.must(node.message_loc)), new_text: name),
|
284
323
|
kind: Constant::CompletionItemKind::METHOD,
|
285
324
|
label_details: Interface::CompletionItemLabelDetails.new(
|
286
|
-
detail:
|
325
|
+
detail: entry.decorated_parameters,
|
287
326
|
description: entry.file_name,
|
288
327
|
),
|
289
328
|
documentation: Interface::MarkupContent.new(
|
@@ -23,6 +23,7 @@ module RubyLsp
|
|
23
23
|
@response_builder = response_builder
|
24
24
|
@global_state = global_state
|
25
25
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
26
|
+
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
26
27
|
@uri = uri
|
27
28
|
@node_context = node_context
|
28
29
|
@typechecker_enabled = typechecker_enabled
|
@@ -48,7 +49,7 @@ module RubyLsp
|
|
48
49
|
message = node.message
|
49
50
|
return unless message
|
50
51
|
|
51
|
-
handle_method_definition(message,
|
52
|
+
handle_method_definition(message, @type_inferrer.infer_receiver_type(@node_context))
|
52
53
|
end
|
53
54
|
|
54
55
|
sig { params(node: Prism::StringNode).void }
|
@@ -70,7 +71,7 @@ module RubyLsp
|
|
70
71
|
value = expression.value
|
71
72
|
return unless value
|
72
73
|
|
73
|
-
handle_method_definition(value,
|
74
|
+
handle_method_definition(value, nil)
|
74
75
|
end
|
75
76
|
|
76
77
|
sig { params(node: Prism::ConstantPathNode).void }
|
@@ -123,7 +124,10 @@ module RubyLsp
|
|
123
124
|
|
124
125
|
sig { params(name: String).void }
|
125
126
|
def handle_instance_variable_definition(name)
|
126
|
-
|
127
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
128
|
+
return unless type
|
129
|
+
|
130
|
+
entries = @index.resolve_instance_variable(name, type)
|
127
131
|
return unless entries
|
128
132
|
|
129
133
|
entries.each do |entry|
|
@@ -141,10 +145,10 @@ module RubyLsp
|
|
141
145
|
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
142
146
|
end
|
143
147
|
|
144
|
-
sig { params(message: String,
|
145
|
-
def handle_method_definition(message,
|
146
|
-
methods = if
|
147
|
-
@index.resolve_method(message,
|
148
|
+
sig { params(message: String, receiver_type: T.nilable(String)).void }
|
149
|
+
def handle_method_definition(message, receiver_type)
|
150
|
+
methods = if receiver_type
|
151
|
+
@index.resolve_method(message, receiver_type)
|
148
152
|
else
|
149
153
|
# If the method doesn't have a receiver, then we provide a few candidates to jump to
|
150
154
|
# But we don't want to provide too many candidates, as it can be overwhelming
|
@@ -47,6 +47,7 @@ module RubyLsp
|
|
47
47
|
@response_builder = response_builder
|
48
48
|
@global_state = global_state
|
49
49
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
50
|
+
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
50
51
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
51
52
|
@node_context = node_context
|
52
53
|
@typechecker_enabled = typechecker_enabled
|
@@ -78,14 +79,14 @@ module RubyLsp
|
|
78
79
|
|
79
80
|
sig { params(node: Prism::ConstantWriteNode).void }
|
80
81
|
def on_constant_write_node_enter(node)
|
81
|
-
return if @global_state.
|
82
|
+
return if @global_state.has_type_checker
|
82
83
|
|
83
84
|
generate_hover(node.name.to_s, node.name_loc)
|
84
85
|
end
|
85
86
|
|
86
87
|
sig { params(node: Prism::ConstantPathNode).void }
|
87
88
|
def on_constant_path_node_enter(node)
|
88
|
-
return if @global_state.
|
89
|
+
return if @global_state.has_type_checker
|
89
90
|
|
90
91
|
name = constant_name(node)
|
91
92
|
return if name.nil?
|
@@ -95,8 +96,6 @@ module RubyLsp
|
|
95
96
|
|
96
97
|
sig { params(node: Prism::CallNode).void }
|
97
98
|
def on_call_node_enter(node)
|
98
|
-
return unless self_receiver?(node)
|
99
|
-
|
100
99
|
if @path && File.basename(@path) == GEMFILE_NAME && node.name == :gem
|
101
100
|
generate_gem_hover(node)
|
102
101
|
return
|
@@ -107,10 +106,15 @@ module RubyLsp
|
|
107
106
|
message = node.message
|
108
107
|
return unless message
|
109
108
|
|
110
|
-
|
109
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
110
|
+
return unless type
|
111
|
+
|
112
|
+
methods = @index.resolve_method(message, type)
|
111
113
|
return unless methods
|
112
114
|
|
113
|
-
|
115
|
+
title = "#{message}#{T.must(methods.first).decorated_parameters}"
|
116
|
+
|
117
|
+
categorized_markdown_from_index_entries(title, methods).each do |category, content|
|
114
118
|
@response_builder.push(content, category: category)
|
115
119
|
end
|
116
120
|
end
|
@@ -149,7 +153,10 @@ module RubyLsp
|
|
149
153
|
|
150
154
|
sig { params(name: String).void }
|
151
155
|
def handle_instance_variable_hover(name)
|
152
|
-
|
156
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
157
|
+
return unless type
|
158
|
+
|
159
|
+
entries = @index.resolve_instance_variable(name, type)
|
153
160
|
return unless entries
|
154
161
|
|
155
162
|
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
@@ -21,6 +21,7 @@ module RubyLsp
|
|
21
21
|
@response_builder = response_builder
|
22
22
|
@global_state = global_state
|
23
23
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
24
|
+
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
24
25
|
@node_context = node_context
|
25
26
|
dispatcher.register(self, :on_call_node_enter)
|
26
27
|
end
|
@@ -28,12 +29,14 @@ module RubyLsp
|
|
28
29
|
sig { params(node: Prism::CallNode).void }
|
29
30
|
def on_call_node_enter(node)
|
30
31
|
return if @typechecker_enabled
|
31
|
-
return unless self_receiver?(node)
|
32
32
|
|
33
33
|
message = node.message
|
34
34
|
return unless message
|
35
35
|
|
36
|
-
|
36
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
37
|
+
return unless type
|
38
|
+
|
39
|
+
methods = @index.resolve_method(message, type)
|
37
40
|
return unless methods
|
38
41
|
|
39
42
|
target_method = methods.first
|
@@ -16,19 +16,24 @@ module RubyLsp
|
|
16
16
|
sig { returns(T.nilable(Prism::CallNode)) }
|
17
17
|
attr_reader :call_node
|
18
18
|
|
19
|
+
sig { returns(T.nilable(String)) }
|
20
|
+
attr_reader :surrounding_method
|
21
|
+
|
19
22
|
sig do
|
20
23
|
params(
|
21
24
|
node: T.nilable(Prism::Node),
|
22
25
|
parent: T.nilable(Prism::Node),
|
23
26
|
nesting: T::Array[String],
|
24
27
|
call_node: T.nilable(Prism::CallNode),
|
28
|
+
surrounding_method: T.nilable(String),
|
25
29
|
).void
|
26
30
|
end
|
27
|
-
def initialize(node, parent, nesting, call_node)
|
31
|
+
def initialize(node, parent, nesting, call_node, surrounding_method)
|
28
32
|
@node = node
|
29
33
|
@parent = parent
|
30
34
|
@nesting = nesting
|
31
35
|
@call_node = call_node
|
36
|
+
@surrounding_method = surrounding_method
|
32
37
|
end
|
33
38
|
|
34
39
|
sig { returns(String) }
|
@@ -36,7 +36,7 @@ module RubyLsp
|
|
36
36
|
def provider
|
37
37
|
Interface::CompletionOptions.new(
|
38
38
|
resolve_provider: true,
|
39
|
-
trigger_characters: ["/", "\"", "'", ":", "@"],
|
39
|
+
trigger_characters: ["/", "\"", "'", ":", "@", "."],
|
40
40
|
completion_item: {
|
41
41
|
labelDetailsSupport: true,
|
42
42
|
},
|
@@ -48,18 +48,18 @@ module RubyLsp
|
|
48
48
|
params(
|
49
49
|
document: Document,
|
50
50
|
global_state: GlobalState,
|
51
|
-
|
51
|
+
params: T::Hash[Symbol, T.untyped],
|
52
52
|
typechecker_enabled: T::Boolean,
|
53
53
|
dispatcher: Prism::Dispatcher,
|
54
54
|
).void
|
55
55
|
end
|
56
|
-
def initialize(document, global_state,
|
56
|
+
def initialize(document, global_state, params, typechecker_enabled, dispatcher)
|
57
57
|
super()
|
58
58
|
@target = T.let(nil, T.nilable(Prism::Node))
|
59
59
|
@dispatcher = dispatcher
|
60
60
|
# Completion always receives the position immediately after the character that was just typed. Here we adjust it
|
61
61
|
# back by 1, so that we find the right node
|
62
|
-
char_position = document.create_scanner.find_char_position(position) - 1
|
62
|
+
char_position = document.create_scanner.find_char_position(params[:position]) - 1
|
63
63
|
node_context = document.locate(
|
64
64
|
document.tree,
|
65
65
|
char_position,
|
@@ -87,6 +87,7 @@ module RubyLsp
|
|
87
87
|
typechecker_enabled,
|
88
88
|
dispatcher,
|
89
89
|
document.uri,
|
90
|
+
params.dig(:context, :triggerCharacter),
|
90
91
|
)
|
91
92
|
|
92
93
|
Addon.addons.each do |addon|
|
@@ -56,8 +56,16 @@ module RubyLsp
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
first_entry = T.must(entries.first)
|
60
|
+
|
61
|
+
if first_entry.is_a?(RubyIndexer::Entry::Member)
|
62
|
+
detail = first_entry.decorated_parameters
|
63
|
+
label = "#{label}#{first_entry.decorated_parameters}"
|
64
|
+
end
|
65
|
+
|
59
66
|
@item[:labelDetails] = Interface::CompletionItemLabelDetails.new(
|
60
67
|
description: entries.take(MAX_DOCUMENTATION_ENTRIES).map(&:file_name).join(","),
|
68
|
+
detail: detail,
|
61
69
|
)
|
62
70
|
|
63
71
|
@item[:documentation] = Interface::MarkupContent.new(
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# 
|
7
|
+
#
|
8
|
+
# The [prepare type hierarchy
|
9
|
+
# request](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareTypeHierarchy)
|
10
|
+
# displays the list of ancestors (supertypes) and descendants (subtypes) for the selected type.
|
11
|
+
#
|
12
|
+
# Currently only supports supertypes due to a limitation of the index.
|
13
|
+
#
|
14
|
+
# # Example
|
15
|
+
#
|
16
|
+
# ```ruby
|
17
|
+
# class Foo; end
|
18
|
+
# class Bar < Foo; end
|
19
|
+
#
|
20
|
+
# puts Bar # <-- right click on `Bar` and select "Show Type Hierarchy"
|
21
|
+
# ```
|
22
|
+
class PrepareTypeHierarchy < Request
|
23
|
+
extend T::Sig
|
24
|
+
|
25
|
+
include Support::Common
|
26
|
+
|
27
|
+
class << self
|
28
|
+
extend T::Sig
|
29
|
+
|
30
|
+
sig { returns(Interface::TypeHierarchyOptions) }
|
31
|
+
def provider
|
32
|
+
Interface::TypeHierarchyOptions.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
sig do
|
37
|
+
params(
|
38
|
+
document: Document,
|
39
|
+
index: RubyIndexer::Index,
|
40
|
+
position: T::Hash[Symbol, T.untyped],
|
41
|
+
).void
|
42
|
+
end
|
43
|
+
def initialize(document, index, position)
|
44
|
+
super()
|
45
|
+
|
46
|
+
@document = document
|
47
|
+
@index = index
|
48
|
+
@position = position
|
49
|
+
end
|
50
|
+
|
51
|
+
sig { override.returns(T.nilable(T::Array[Interface::TypeHierarchyItem])) }
|
52
|
+
def perform
|
53
|
+
context = @document.locate_node(
|
54
|
+
@position,
|
55
|
+
node_types: [
|
56
|
+
Prism::ConstantReadNode,
|
57
|
+
Prism::ConstantWriteNode,
|
58
|
+
Prism::ConstantPathNode,
|
59
|
+
],
|
60
|
+
)
|
61
|
+
|
62
|
+
node = context.node
|
63
|
+
parent = context.parent
|
64
|
+
return unless node && parent
|
65
|
+
|
66
|
+
target = determine_target(node, parent, @position)
|
67
|
+
entries = @index.resolve(target.slice, context.nesting)
|
68
|
+
return unless entries
|
69
|
+
|
70
|
+
# While the spec allows for multiple entries, VSCode seems to only support one
|
71
|
+
# We'll just return the first one for now
|
72
|
+
first_entry = T.must(entries.first)
|
73
|
+
|
74
|
+
range = range_from_location(first_entry.location)
|
75
|
+
|
76
|
+
[
|
77
|
+
Interface::TypeHierarchyItem.new(
|
78
|
+
name: first_entry.name,
|
79
|
+
kind: kind_for_entry(first_entry),
|
80
|
+
uri: URI::Generic.from_path(path: first_entry.file_path).to_s,
|
81
|
+
range: range,
|
82
|
+
selection_range: range,
|
83
|
+
),
|
84
|
+
]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -26,7 +26,7 @@ module RubyLsp
|
|
26
26
|
)
|
27
27
|
end
|
28
28
|
|
29
|
-
sig { params(location: Prism::Location).returns(Interface::Range) }
|
29
|
+
sig { params(location: T.any(Prism::Location, RubyIndexer::Location)).returns(Interface::Range) }
|
30
30
|
def range_from_location(location)
|
31
31
|
Interface::Range.new(
|
32
32
|
start: Interface::Position.new(
|
@@ -186,6 +186,24 @@ module RubyLsp
|
|
186
186
|
current = current.parent
|
187
187
|
end
|
188
188
|
end
|
189
|
+
|
190
|
+
sig { params(entry: RubyIndexer::Entry).returns(T.nilable(Integer)) }
|
191
|
+
def kind_for_entry(entry)
|
192
|
+
case entry
|
193
|
+
when RubyIndexer::Entry::Class
|
194
|
+
Constant::SymbolKind::CLASS
|
195
|
+
when RubyIndexer::Entry::Module
|
196
|
+
Constant::SymbolKind::NAMESPACE
|
197
|
+
when RubyIndexer::Entry::Constant
|
198
|
+
Constant::SymbolKind::CONSTANT
|
199
|
+
when RubyIndexer::Entry::Method
|
200
|
+
entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
201
|
+
when RubyIndexer::Entry::Accessor
|
202
|
+
Constant::SymbolKind::PROPERTY
|
203
|
+
when RubyIndexer::Entry::InstanceVariable
|
204
|
+
Constant::SymbolKind::FIELD
|
205
|
+
end
|
206
|
+
end
|
189
207
|
end
|
190
208
|
end
|
191
209
|
end
|
@@ -42,7 +42,7 @@ module RubyLsp
|
|
42
42
|
def to_lsp_code_actions
|
43
43
|
code_actions = []
|
44
44
|
|
45
|
-
code_actions << autocorrect_action if
|
45
|
+
code_actions << autocorrect_action if correctable?
|
46
46
|
code_actions << disable_line_action
|
47
47
|
|
48
48
|
code_actions
|
@@ -70,7 +70,7 @@ module RubyLsp
|
|
70
70
|
),
|
71
71
|
),
|
72
72
|
data: {
|
73
|
-
correctable:
|
73
|
+
correctable: correctable?,
|
74
74
|
code_actions: to_lsp_code_actions,
|
75
75
|
},
|
76
76
|
)
|
@@ -81,7 +81,7 @@ module RubyLsp
|
|
81
81
|
sig { returns(String) }
|
82
82
|
def message
|
83
83
|
message = @offense.message
|
84
|
-
message += "\n\nThis offense is not auto-correctable.\n" unless
|
84
|
+
message += "\n\nThis offense is not auto-correctable.\n" unless correctable?
|
85
85
|
message
|
86
86
|
end
|
87
87
|
|
@@ -115,7 +115,7 @@ module RubyLsp
|
|
115
115
|
uri: @uri.to_s,
|
116
116
|
version: nil,
|
117
117
|
),
|
118
|
-
edits:
|
118
|
+
edits: correctable? ? offense_replacements : [],
|
119
119
|
),
|
120
120
|
],
|
121
121
|
),
|
@@ -193,6 +193,14 @@ module RubyLsp
|
|
193
193
|
line.length
|
194
194
|
end
|
195
195
|
end
|
196
|
+
|
197
|
+
# When `RuboCop::LSP.enable` is called, contextual autocorrect will not offer itself
|
198
|
+
# as `correctable?` to prevent annoying changes while typing. Instead check if
|
199
|
+
# a corrector is present. If it is, then that means some code transformation can be applied.
|
200
|
+
sig { returns(T::Boolean) }
|
201
|
+
def correctable?
|
202
|
+
!@offense.corrector.nil?
|
203
|
+
end
|
196
204
|
end
|
197
205
|
end
|
198
206
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# 
|
7
|
+
#
|
8
|
+
# The [type hierarchy supertypes
|
9
|
+
# request](https://microsoft.github.io/language-server-protocol/specification#typeHierarchy_supertypes)
|
10
|
+
# displays the list of ancestors (supertypes) for the selected type.
|
11
|
+
#
|
12
|
+
# # Example
|
13
|
+
#
|
14
|
+
# ```ruby
|
15
|
+
# class Foo; end
|
16
|
+
# class Bar < Foo; end
|
17
|
+
#
|
18
|
+
# puts Bar # <-- right click on `Bar` and select "Show Type Hierarchy"
|
19
|
+
# ```
|
20
|
+
class TypeHierarchySupertypes < Request
|
21
|
+
extend T::Sig
|
22
|
+
|
23
|
+
include Support::Common
|
24
|
+
|
25
|
+
sig { params(index: RubyIndexer::Index, item: T::Hash[Symbol, T.untyped]).void }
|
26
|
+
def initialize(index, item)
|
27
|
+
super()
|
28
|
+
|
29
|
+
@index = index
|
30
|
+
@item = item
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { override.returns(T.nilable(T::Array[Interface::TypeHierarchyItem])) }
|
34
|
+
def perform
|
35
|
+
name = @item[:name]
|
36
|
+
entries = @index[name]
|
37
|
+
|
38
|
+
parents = T.let(Set.new, T::Set[RubyIndexer::Entry::Namespace])
|
39
|
+
return unless entries&.any?
|
40
|
+
|
41
|
+
entries.each do |entry|
|
42
|
+
next unless entry.is_a?(RubyIndexer::Entry::Namespace)
|
43
|
+
|
44
|
+
if entry.is_a?(RubyIndexer::Entry::Class)
|
45
|
+
parent_class_name = entry.parent_class
|
46
|
+
if parent_class_name
|
47
|
+
resolved_parent_entries = @index.resolve(parent_class_name, entry.nesting)
|
48
|
+
resolved_parent_entries&.each do |entry|
|
49
|
+
next unless entry.is_a?(RubyIndexer::Entry::Class)
|
50
|
+
|
51
|
+
parents << entry
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
entry.mixin_operations.each do |mixin_operation|
|
57
|
+
next if mixin_operation.is_a?(RubyIndexer::Entry::Extend)
|
58
|
+
|
59
|
+
mixin_name = mixin_operation.module_name
|
60
|
+
resolved_mixin_entries = @index.resolve(mixin_name, entry.nesting)
|
61
|
+
next unless resolved_mixin_entries
|
62
|
+
|
63
|
+
resolved_mixin_entries.each do |mixin_entry|
|
64
|
+
next unless mixin_entry.is_a?(RubyIndexer::Entry::Module)
|
65
|
+
|
66
|
+
parents << mixin_entry
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
parents.map { |entry| hierarchy_item(entry) }
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
sig { params(entry: RubyIndexer::Entry).returns(Interface::TypeHierarchyItem) }
|
77
|
+
def hierarchy_item(entry)
|
78
|
+
range = range_from_location(entry.location)
|
79
|
+
|
80
|
+
Interface::TypeHierarchyItem.new(
|
81
|
+
name: entry.name,
|
82
|
+
kind: kind_for_entry(entry),
|
83
|
+
uri: URI::Generic.from_path(path: entry.file_path).to_s,
|
84
|
+
range: range,
|
85
|
+
selection_range: range,
|
86
|
+
detail: entry.file_name,
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -52,7 +52,7 @@ module RubyLsp
|
|
52
52
|
|
53
53
|
Interface::WorkspaceSymbol.new(
|
54
54
|
name: entry.name,
|
55
|
-
container_name:
|
55
|
+
container_name: container.join("::"),
|
56
56
|
kind: kind,
|
57
57
|
location: Interface::Location.new(
|
58
58
|
uri: URI::Generic.from_path(path: file_path).to_s,
|
@@ -64,26 +64,6 @@ module RubyLsp
|
|
64
64
|
)
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
sig { params(entry: RubyIndexer::Entry).returns(T.nilable(Integer)) }
|
71
|
-
def kind_for_entry(entry)
|
72
|
-
case entry
|
73
|
-
when RubyIndexer::Entry::Class
|
74
|
-
Constant::SymbolKind::CLASS
|
75
|
-
when RubyIndexer::Entry::Module
|
76
|
-
Constant::SymbolKind::NAMESPACE
|
77
|
-
when RubyIndexer::Entry::Constant
|
78
|
-
Constant::SymbolKind::CONSTANT
|
79
|
-
when RubyIndexer::Entry::Method
|
80
|
-
entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
81
|
-
when RubyIndexer::Entry::Accessor
|
82
|
-
Constant::SymbolKind::PROPERTY
|
83
|
-
when RubyIndexer::Entry::InstanceVariable
|
84
|
-
Constant::SymbolKind::FIELD
|
85
|
-
end
|
86
|
-
end
|
87
67
|
end
|
88
68
|
end
|
89
69
|
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -47,6 +47,8 @@ module RubyLsp
|
|
47
47
|
autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
|
48
48
|
autoload :WorkspaceSymbol, "ruby_lsp/requests/workspace_symbol"
|
49
49
|
autoload :SignatureHelp, "ruby_lsp/requests/signature_help"
|
50
|
+
autoload :PrepareTypeHierarchy, "ruby_lsp/requests/prepare_type_hierarchy"
|
51
|
+
autoload :TypeHierarchySupertypes, "ruby_lsp/requests/type_hierarchy_supertypes"
|
50
52
|
|
51
53
|
# :nodoc:
|
52
54
|
module Support
|