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
|
@@ -9,126 +9,240 @@ module RubyLsp
|
|
|
9
9
|
class References < Request
|
|
10
10
|
include Support::Common
|
|
11
11
|
|
|
12
|
+
MAX_NUMBER_OF_METHOD_CANDIDATES_WITHOUT_RECEIVER = 30
|
|
13
|
+
|
|
12
14
|
#: (GlobalState global_state, Store store, (RubyDocument | ERBDocument) document, Hash[Symbol, untyped] params) -> void
|
|
13
15
|
def initialize(global_state, store, document, params)
|
|
14
16
|
super()
|
|
15
17
|
@global_state = global_state
|
|
18
|
+
@type_inferrer = global_state.type_inferrer #: TypeInferrer
|
|
19
|
+
@graph = global_state.graph #: Rubydex::Graph
|
|
16
20
|
@store = store
|
|
17
21
|
@document = document
|
|
18
22
|
@params = params
|
|
19
23
|
@locations = [] #: Array[Interface::Location]
|
|
24
|
+
@char_position = 0 #: Integer
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
# @override
|
|
23
28
|
#: -> Array[Interface::Location]
|
|
24
29
|
def perform
|
|
25
|
-
|
|
26
|
-
char_position, _ = @document.find_index_by_position(position)
|
|
30
|
+
include_declarations = @params.dig(:context, :includeDeclaration) || false
|
|
31
|
+
@char_position, _ = @document.find_index_by_position(@params[:position])
|
|
27
32
|
|
|
28
33
|
node_context = RubyDocument.locate(
|
|
29
34
|
@document.ast,
|
|
30
|
-
char_position,
|
|
35
|
+
@char_position,
|
|
31
36
|
node_types: [
|
|
32
37
|
Prism::ConstantReadNode,
|
|
33
38
|
Prism::ConstantPathNode,
|
|
34
39
|
Prism::ConstantPathTargetNode,
|
|
40
|
+
Prism::ConstantAndWriteNode,
|
|
41
|
+
Prism::ConstantOperatorWriteNode,
|
|
42
|
+
Prism::ConstantOrWriteNode,
|
|
43
|
+
Prism::ConstantTargetNode,
|
|
44
|
+
Prism::ConstantWriteNode,
|
|
35
45
|
Prism::InstanceVariableAndWriteNode,
|
|
36
46
|
Prism::InstanceVariableOperatorWriteNode,
|
|
37
47
|
Prism::InstanceVariableOrWriteNode,
|
|
38
48
|
Prism::InstanceVariableReadNode,
|
|
39
49
|
Prism::InstanceVariableTargetNode,
|
|
40
50
|
Prism::InstanceVariableWriteNode,
|
|
51
|
+
Prism::ClassVariableAndWriteNode,
|
|
52
|
+
Prism::ClassVariableOperatorWriteNode,
|
|
53
|
+
Prism::ClassVariableOrWriteNode,
|
|
54
|
+
Prism::ClassVariableReadNode,
|
|
55
|
+
Prism::ClassVariableTargetNode,
|
|
56
|
+
Prism::ClassVariableWriteNode,
|
|
57
|
+
Prism::GlobalVariableAndWriteNode,
|
|
58
|
+
Prism::GlobalVariableOperatorWriteNode,
|
|
59
|
+
Prism::GlobalVariableOrWriteNode,
|
|
60
|
+
Prism::GlobalVariableReadNode,
|
|
61
|
+
Prism::GlobalVariableTargetNode,
|
|
62
|
+
Prism::GlobalVariableWriteNode,
|
|
41
63
|
Prism::CallNode,
|
|
64
|
+
Prism::CallAndWriteNode,
|
|
65
|
+
Prism::CallOperatorWriteNode,
|
|
66
|
+
Prism::CallOrWriteNode,
|
|
42
67
|
Prism::DefNode,
|
|
43
68
|
],
|
|
44
69
|
code_units_cache: @document.code_units_cache,
|
|
45
70
|
)
|
|
46
71
|
target = node_context.node
|
|
47
|
-
parent = node_context.parent
|
|
48
72
|
return @locations if !target || target.is_a?(Prism::ProgramNode)
|
|
49
73
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
)
|
|
74
|
+
case target
|
|
75
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode
|
|
76
|
+
name = constant_name(target)
|
|
77
|
+
handle_constant_references(name, node_context, include_declarations) if name
|
|
78
|
+
when Prism::ConstantTargetNode
|
|
79
|
+
handle_constant_references(target.name.to_s, node_context, include_declarations)
|
|
80
|
+
when Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, Prism::ConstantOrWriteNode,
|
|
81
|
+
Prism::ConstantWriteNode
|
|
82
|
+
if cursor_on_name?(target.name_loc)
|
|
83
|
+
handle_constant_references(target.name.to_s, node_context, include_declarations)
|
|
84
|
+
end
|
|
85
|
+
when Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode,
|
|
86
|
+
Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
|
|
87
|
+
handle_variable_references(target.name.to_s, node_context, include_declarations)
|
|
88
|
+
when Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode,
|
|
89
|
+
Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableWriteNode,
|
|
90
|
+
Prism::ClassVariableAndWriteNode, Prism::ClassVariableOperatorWriteNode,
|
|
91
|
+
Prism::ClassVariableOrWriteNode, Prism::ClassVariableWriteNode
|
|
92
|
+
if cursor_on_name?(target.name_loc)
|
|
93
|
+
handle_variable_references(target.name.to_s, node_context, include_declarations)
|
|
94
|
+
end
|
|
95
|
+
when Prism::GlobalVariableReadNode, Prism::GlobalVariableTargetNode
|
|
96
|
+
handle_global_variable_references(target.name.to_s, include_declarations)
|
|
97
|
+
when Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode,
|
|
98
|
+
Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableWriteNode
|
|
99
|
+
if cursor_on_name?(target.name_loc)
|
|
100
|
+
handle_global_variable_references(target.name.to_s, include_declarations)
|
|
101
|
+
end
|
|
102
|
+
when Prism::CallNode
|
|
103
|
+
message_loc = target.message_loc
|
|
104
|
+
message = target.message
|
|
105
|
+
if message && message_loc && cursor_on_name?(message_loc)
|
|
106
|
+
resolve_method_references(message, node_context, include_declarations)
|
|
107
|
+
end
|
|
108
|
+
when Prism::CallAndWriteNode, Prism::CallOperatorWriteNode, Prism::CallOrWriteNode
|
|
109
|
+
message_loc = target.message_loc
|
|
110
|
+
if message_loc && cursor_on_name?(message_loc)
|
|
111
|
+
resolve_method_references(target.read_name.to_s, node_context, include_declarations)
|
|
112
|
+
end
|
|
113
|
+
when Prism::DefNode
|
|
114
|
+
handle_def_node_references(target, node_context, include_declarations) if cursor_on_name?(target.name_loc)
|
|
56
115
|
end
|
|
57
116
|
|
|
58
|
-
|
|
117
|
+
@locations
|
|
118
|
+
end
|
|
59
119
|
|
|
60
|
-
|
|
61
|
-
return @locations unless reference_target
|
|
120
|
+
private
|
|
62
121
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
next if @store.key?(uri)
|
|
122
|
+
#: (String name, NodeContext node_context, bool include_declarations) -> void
|
|
123
|
+
def handle_constant_references(name, node_context, include_declarations)
|
|
124
|
+
declaration = @graph.resolve_constant(name, node_context.nesting)
|
|
125
|
+
return unless declaration
|
|
68
126
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
127
|
+
collect_references(declaration.references, [declaration], include_declarations)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
#: (String message, NodeContext node_context, bool include_declarations) -> void
|
|
131
|
+
def resolve_method_references(message, node_context, include_declarations)
|
|
132
|
+
receiver_type = @type_inferrer.infer_receiver_type(node_context)
|
|
133
|
+
|
|
134
|
+
declaration = if receiver_type
|
|
135
|
+
owner = @graph[receiver_type.name]
|
|
136
|
+
owner.find_member("#{message}()") if owner.is_a?(Rubydex::Namespace)
|
|
73
137
|
end
|
|
74
138
|
|
|
75
|
-
|
|
76
|
-
|
|
139
|
+
declarations = if receiver_type.nil? || (receiver_type.is_a?(TypeInferrer::GuessedType) && declaration.nil?)
|
|
140
|
+
@graph.search("##{message}()").take(MAX_NUMBER_OF_METHOD_CANDIDATES_WITHOUT_RECEIVER)
|
|
141
|
+
elsif declaration
|
|
142
|
+
[declaration]
|
|
143
|
+
else
|
|
144
|
+
[]
|
|
77
145
|
end
|
|
146
|
+
return if declarations.empty?
|
|
78
147
|
|
|
79
|
-
|
|
148
|
+
collect_references(method_references_for(message, declarations), declarations, include_declarations)
|
|
80
149
|
end
|
|
81
150
|
|
|
82
|
-
|
|
151
|
+
# Handles instance and class variable references. Resolves the receiver type from the node context to locate
|
|
152
|
+
# the owning namespace, then looks up the member through the ancestor chain via `find_member`.
|
|
153
|
+
#: (String name, NodeContext node_context, bool include_declarations) -> void
|
|
154
|
+
def handle_variable_references(name, node_context, include_declarations)
|
|
155
|
+
type = @type_inferrer.infer_receiver_type(node_context)
|
|
156
|
+
return unless type
|
|
83
157
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
158
|
+
owner = @graph[type.name]
|
|
159
|
+
return unless owner.is_a?(Rubydex::Namespace)
|
|
160
|
+
|
|
161
|
+
declaration = owner.find_member(name)
|
|
162
|
+
return unless declaration
|
|
163
|
+
|
|
164
|
+
collect_references(declaration.references, [declaration], include_declarations)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Handles global variable references. Globals are keyed by their full name (including `$`) in the graph, so we
|
|
168
|
+
# can look them up directly without needing to resolve a receiver type.
|
|
169
|
+
#: (String name, bool include_declarations) -> void
|
|
170
|
+
def handle_global_variable_references(name, include_declarations)
|
|
171
|
+
declaration = @graph[name]
|
|
172
|
+
return unless declaration
|
|
173
|
+
|
|
174
|
+
collect_references(declaration.references, [declaration], include_declarations)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
#: (Prism::DefNode target, NodeContext node_context, bool include_declarations) -> void
|
|
178
|
+
def handle_def_node_references(target, node_context, include_declarations)
|
|
179
|
+
method_name = target.name.to_s
|
|
90
180
|
|
|
91
|
-
|
|
92
|
-
|
|
181
|
+
owner_type = @type_inferrer.infer_receiver_type(node_context)
|
|
182
|
+
return unless owner_type
|
|
93
183
|
|
|
94
|
-
|
|
184
|
+
owner = @graph[owner_type.name]
|
|
185
|
+
return unless owner.is_a?(Rubydex::Namespace)
|
|
186
|
+
|
|
187
|
+
declaration = owner.find_member("#{method_name}()")
|
|
188
|
+
return unless declaration
|
|
189
|
+
|
|
190
|
+
collect_references(method_references_for(method_name, [declaration]), [declaration], include_declarations)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
#: (String, Array[Rubydex::Declaration]) -> Array[Rubydex::MethodReference]
|
|
194
|
+
def method_references_for(method_name, declarations)
|
|
195
|
+
target_owner_names = declarations.map do |d|
|
|
196
|
+
d.owner #: as Rubydex::Namespace
|
|
95
197
|
.name
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
when Prism::CallNode, Prism::DefNode
|
|
110
|
-
RubyIndexer::ReferenceFinder::MethodTarget.new(target_node.name.to_s)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
@graph.method_references.select do |reference|
|
|
201
|
+
next false unless reference.name == method_name
|
|
202
|
+
|
|
203
|
+
receiver = reference.receiver
|
|
204
|
+
next true if receiver.nil? || target_owner_names.empty?
|
|
205
|
+
|
|
206
|
+
if receiver.is_a?(Rubydex::Namespace)
|
|
207
|
+
receiver.ancestors.any? { |ancestor| target_owner_names.include?(ancestor.name) }
|
|
208
|
+
else
|
|
209
|
+
target_owner_names.include?(receiver.name)
|
|
210
|
+
end
|
|
111
211
|
end
|
|
112
212
|
end
|
|
113
213
|
|
|
114
|
-
#: (
|
|
115
|
-
def collect_references(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
target,
|
|
119
|
-
@global_state.index,
|
|
120
|
-
dispatcher,
|
|
121
|
-
uri,
|
|
122
|
-
include_declarations: @params.dig(:context, :includeDeclaration) || true,
|
|
123
|
-
)
|
|
124
|
-
dispatcher.visit(parse_result.value.first)
|
|
214
|
+
#: (Enumerable[Rubydex::Reference] references, Array[Rubydex::Declaration] declarations, bool include_declarations) -> void
|
|
215
|
+
def collect_references(references, declarations, include_declarations)
|
|
216
|
+
references.each do |reference|
|
|
217
|
+
next if rubydex_internal_uri?(reference.location.uri)
|
|
125
218
|
|
|
126
|
-
|
|
127
|
-
@locations << Interface::Location.new(
|
|
128
|
-
uri: uri.to_s,
|
|
129
|
-
range: range_from_location(reference.location),
|
|
130
|
-
)
|
|
219
|
+
@locations << reference.to_lsp_location
|
|
131
220
|
end
|
|
221
|
+
|
|
222
|
+
return unless include_declarations
|
|
223
|
+
|
|
224
|
+
declarations.each do |declaration|
|
|
225
|
+
declaration.definitions.each do |definition|
|
|
226
|
+
next if rubydex_internal_uri?(definition.location.uri)
|
|
227
|
+
|
|
228
|
+
@locations << definition.to_lsp_selection_location
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
#: (String uri) -> bool
|
|
234
|
+
def rubydex_internal_uri?(uri)
|
|
235
|
+
URI(uri).scheme == "rubydex"
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Write, operator-write, and call-with-message nodes cover more than just the identifier —
|
|
239
|
+
# they span the whole assignment or call expression. We only resolve references when the
|
|
240
|
+
# cursor is positioned directly on the name itself, not on operators, values, or arguments.
|
|
241
|
+
#: (Prism::Location name_loc) -> bool
|
|
242
|
+
def cursor_on_name?(name_loc)
|
|
243
|
+
start = name_loc.cached_start_code_units_offset(@document.code_units_cache)
|
|
244
|
+
finish = name_loc.cached_end_code_units_offset(@document.code_units_cache)
|
|
245
|
+
(start...finish).cover?(@char_position)
|
|
132
246
|
end
|
|
133
247
|
end
|
|
134
248
|
end
|
|
@@ -54,7 +54,7 @@ module RubyLsp
|
|
|
54
54
|
|
|
55
55
|
target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode
|
|
56
56
|
|
|
57
|
-
name =
|
|
57
|
+
name = constant_name(target)
|
|
58
58
|
return unless name
|
|
59
59
|
|
|
60
60
|
declaration = @graph.resolve_constant(name, node_context.nesting)
|
|
@@ -5,6 +5,8 @@ module RubyLsp
|
|
|
5
5
|
module Requests
|
|
6
6
|
# @abstract
|
|
7
7
|
class Request
|
|
8
|
+
include Support::Common
|
|
9
|
+
|
|
8
10
|
class InvalidFormatter < StandardError; end
|
|
9
11
|
|
|
10
12
|
# @abstract
|
|
@@ -26,24 +28,6 @@ module RubyLsp
|
|
|
26
28
|
end
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
# Checks if a location covers a position
|
|
30
|
-
#: (Prism::Location location, untyped position) -> bool
|
|
31
|
-
def cover?(location, position)
|
|
32
|
-
start_covered =
|
|
33
|
-
location.start_line - 1 < position[:line] ||
|
|
34
|
-
(
|
|
35
|
-
location.start_line - 1 == position[:line] &&
|
|
36
|
-
location.start_column <= position[:character]
|
|
37
|
-
)
|
|
38
|
-
end_covered =
|
|
39
|
-
location.end_line - 1 > position[:line] ||
|
|
40
|
-
(
|
|
41
|
-
location.end_line - 1 == position[:line] &&
|
|
42
|
-
location.end_column >= position[:character]
|
|
43
|
-
)
|
|
44
|
-
start_covered && end_covered
|
|
45
|
-
end
|
|
46
|
-
|
|
47
31
|
# Based on a constant node target, a constant path node parent and a position, this method will find the exact
|
|
48
32
|
# portion of the constant path that matches the requested position, for higher precision in hover and
|
|
49
33
|
# definition. For example:
|
|
@@ -62,27 +46,13 @@ module RubyLsp
|
|
|
62
46
|
parent = target #: as Prism::ConstantPathNode
|
|
63
47
|
.parent #: Prism::Node?
|
|
64
48
|
|
|
65
|
-
while parent &&
|
|
49
|
+
while parent && covers_position?(parent.location, position)
|
|
66
50
|
target = parent
|
|
67
51
|
parent = target.is_a?(Prism::ConstantPathNode) ? target.parent : nil
|
|
68
52
|
end
|
|
69
53
|
|
|
70
54
|
target
|
|
71
55
|
end
|
|
72
|
-
|
|
73
|
-
# Checks if a given location covers the position requested
|
|
74
|
-
#: (Prism::Location? location, Hash[Symbol, untyped] position) -> bool
|
|
75
|
-
def covers_position?(location, position)
|
|
76
|
-
return false unless location
|
|
77
|
-
|
|
78
|
-
start_line = location.start_line - 1
|
|
79
|
-
end_line = location.end_line - 1
|
|
80
|
-
line = position[:line]
|
|
81
|
-
character = position[:character]
|
|
82
|
-
|
|
83
|
-
(start_line < line || (start_line == line && location.start_column <= character)) &&
|
|
84
|
-
(end_line > line || (end_line == line && location.end_column >= character))
|
|
85
|
-
end
|
|
86
56
|
end
|
|
87
57
|
end
|
|
88
58
|
end
|
|
@@ -23,7 +23,7 @@ module RubyLsp
|
|
|
23
23
|
)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
#: ((Prism::Location |
|
|
26
|
+
#: ((Prism::Location | Rubydex::Location) location) -> Interface::Range
|
|
27
27
|
def range_from_location(location)
|
|
28
28
|
Interface::Range.new(
|
|
29
29
|
start: Interface::Position.new(
|
|
@@ -34,6 +34,19 @@ module RubyLsp
|
|
|
34
34
|
)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
#: (Prism::Location? location, Hash[Symbol, untyped] position) -> bool
|
|
38
|
+
def covers_position?(location, position)
|
|
39
|
+
return false unless location
|
|
40
|
+
|
|
41
|
+
start_line = location.start_line - 1
|
|
42
|
+
end_line = location.end_line - 1
|
|
43
|
+
line = position[:line]
|
|
44
|
+
character = position[:character]
|
|
45
|
+
|
|
46
|
+
(start_line < line || (start_line == line && location.start_column <= character)) &&
|
|
47
|
+
(end_line > line || (end_line == line && location.end_column >= character))
|
|
48
|
+
end
|
|
49
|
+
|
|
37
50
|
#: (Prism::Node node, title: String, command_name: String, arguments: Array[untyped]?, data: Hash[untyped, untyped]?) -> Interface::CodeLens
|
|
38
51
|
def create_code_lens(node, title:, command_name:, arguments:, data:)
|
|
39
52
|
range = range_from_node(node)
|
|
@@ -64,6 +77,44 @@ module RubyLsp
|
|
|
64
77
|
receiver.nil? || receiver.is_a?(Prism::SelfNode)
|
|
65
78
|
end
|
|
66
79
|
|
|
80
|
+
# Returns true when a constant declaration is reachable from the call site. Private constants are only
|
|
81
|
+
# reachable from within the namespace where they are defined.
|
|
82
|
+
#
|
|
83
|
+
#: (Rubydex::Declaration declaration, String value, NodeContext node_context) -> bool
|
|
84
|
+
def constant_reachable_from_call_site?(declaration, value, node_context)
|
|
85
|
+
return true unless declaration.is_a?(Rubydex::Visibility) && declaration.private?
|
|
86
|
+
|
|
87
|
+
declaration.name == "#{node_context.fully_qualified_name}::#{value}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Returns true when a method is reachable from the call site, considering visibility and receiver type.
|
|
91
|
+
# A method is reachable when:
|
|
92
|
+
# - there's no concrete receiver type to compare against
|
|
93
|
+
# - the call site is inside the receiver's own namespace (implicit/self call)
|
|
94
|
+
# - it is public
|
|
95
|
+
# - it is protected and the call site's class is in the same hierarchy as the method's defining class
|
|
96
|
+
#
|
|
97
|
+
# The `method_decl` is duck-typed to support `Rubydex::Method`, `RubyIndexer::Entry::Member` and
|
|
98
|
+
# `RubyIndexer::Entry::MethodAlias`. All respond to `public?`, `private?` and `owner` (an object with a
|
|
99
|
+
# `name` attribute).
|
|
100
|
+
#
|
|
101
|
+
#: (Rubydex::Method method_decl, TypeInferrer::Type? receiver_type, Rubydex::Graph graph, NodeContext node_context) -> bool
|
|
102
|
+
def method_reachable_from_call_site?(method_decl, receiver_type, graph, node_context)
|
|
103
|
+
return true unless receiver_type
|
|
104
|
+
|
|
105
|
+
caller_namespace = node_context.fully_qualified_name
|
|
106
|
+
return true if caller_namespace == receiver_type.name
|
|
107
|
+
|
|
108
|
+
return true if method_decl.public?
|
|
109
|
+
return false if method_decl.private?
|
|
110
|
+
|
|
111
|
+
caller_declaration = graph[caller_namespace]
|
|
112
|
+
return false unless caller_declaration.is_a?(Rubydex::Namespace)
|
|
113
|
+
|
|
114
|
+
owner_name = method_decl.owner.name
|
|
115
|
+
caller_declaration.ancestors.any? { |ancestor| ancestor.name == owner_name }
|
|
116
|
+
end
|
|
117
|
+
|
|
67
118
|
#: (String, Enumerable[Rubydex::Definition], ?Integer?) -> Hash[Symbol, String]
|
|
68
119
|
def categorized_markdown_from_definitions(title, definitions, max_entries = nil)
|
|
69
120
|
markdown_title = "```ruby\n#{title}\n```"
|
|
@@ -74,17 +125,22 @@ module RubyLsp
|
|
|
74
125
|
# For Markdown links, we need 1 based display locations
|
|
75
126
|
loc = definition.location.to_display
|
|
76
127
|
uri = URI(loc.uri)
|
|
77
|
-
|
|
128
|
+
|
|
129
|
+
file_name = case uri.scheme
|
|
130
|
+
when "file"
|
|
131
|
+
full_path = uri.full_path #: as !nil
|
|
132
|
+
File.basename(full_path)
|
|
133
|
+
when "untitled"
|
|
78
134
|
uri.opaque #: as !nil
|
|
79
|
-
else
|
|
80
|
-
File.basename(
|
|
81
|
-
uri.full_path, #: as !nil
|
|
82
|
-
)
|
|
83
135
|
end
|
|
84
136
|
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
137
|
+
# Omit the link for magic schemes like rubydex:built-in
|
|
138
|
+
if file_name
|
|
139
|
+
# The format for VS Code file URIs is `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
|
|
140
|
+
string_uri = "#{loc.uri}#L#{loc.start_line},#{loc.start_column}-#{loc.end_line},#{loc.end_column}"
|
|
141
|
+
file_links << "[#{file_name}](#{string_uri})"
|
|
142
|
+
end
|
|
143
|
+
|
|
88
144
|
content << "\n\n#{definition.comments.map { |comment| comment.string.delete_prefix("# ") }.join("\n")}" unless definition.comments.empty?
|
|
89
145
|
end
|
|
90
146
|
|
|
@@ -104,42 +160,9 @@ module RubyLsp
|
|
|
104
160
|
}
|
|
105
161
|
end
|
|
106
162
|
|
|
107
|
-
#: (String title,
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
definitions = []
|
|
111
|
-
content = +""
|
|
112
|
-
entries = Array(entries)
|
|
113
|
-
entries_to_format = max_entries ? entries.take(max_entries) : entries
|
|
114
|
-
entries_to_format.each do |entry|
|
|
115
|
-
loc = entry.location
|
|
116
|
-
|
|
117
|
-
# We always handle locations as zero based. However, for file links in Markdown we need them to be one
|
|
118
|
-
# based, which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to
|
|
119
|
-
# columns. The format for VS Code file URIs is
|
|
120
|
-
# `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
|
|
121
|
-
uri = "#{entry.uri}#L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}"
|
|
122
|
-
definitions << "[#{entry.file_name}](#{uri})"
|
|
123
|
-
content << "\n\n#{entry.comments}" unless entry.comments.empty?
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
additional_entries_text = if max_entries && entries.length > max_entries
|
|
127
|
-
additional = entries.length - max_entries
|
|
128
|
-
" | #{additional} other#{additional > 1 ? "s" : ""}"
|
|
129
|
-
else
|
|
130
|
-
""
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
{
|
|
134
|
-
title: markdown_title,
|
|
135
|
-
links: "**Definitions**: #{definitions.join(" | ")}#{additional_entries_text}",
|
|
136
|
-
documentation: content,
|
|
137
|
-
}
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
#: (String title, (Array[RubyIndexer::Entry] | RubyIndexer::Entry) entries, ?Integer? max_entries, ?extra_links: String?) -> String
|
|
141
|
-
def markdown_from_index_entries(title, entries, max_entries = nil, extra_links: nil)
|
|
142
|
-
categorized_markdown = categorized_markdown_from_index_entries(title, entries, max_entries)
|
|
163
|
+
#: (String title, Enumerable[Rubydex::Definition] definitions, ?Integer? max_entries, ?extra_links: String?) -> String
|
|
164
|
+
def markdown_from_definitions(title, definitions, max_entries = nil, extra_links: nil)
|
|
165
|
+
categorized_markdown = categorized_markdown_from_definitions(title, definitions, max_entries)
|
|
143
166
|
|
|
144
167
|
markdown = +(categorized_markdown[:title] || "")
|
|
145
168
|
markdown << "\n\n#{extra_links}" if extra_links
|
|
@@ -155,7 +178,20 @@ module RubyLsp
|
|
|
155
178
|
|
|
156
179
|
#: ((Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode) node) -> String?
|
|
157
180
|
def constant_name(node)
|
|
158
|
-
|
|
181
|
+
Common.constant_name(node)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
class << self
|
|
185
|
+
#: ((Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode) node) -> String?
|
|
186
|
+
def constant_name(node)
|
|
187
|
+
case node
|
|
188
|
+
when Prism::ConstantPathNode, Prism::ConstantReadNode, Prism::ConstantPathTargetNode
|
|
189
|
+
node.full_name
|
|
190
|
+
end
|
|
191
|
+
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
|
|
192
|
+
Prism::ConstantPathNode::MissingNodesInConstantPathError
|
|
193
|
+
nil
|
|
194
|
+
end
|
|
159
195
|
end
|
|
160
196
|
|
|
161
197
|
#: ((Prism::ModuleNode | Prism::ClassNode) node) -> String?
|
|
@@ -179,28 +215,6 @@ module RubyLsp
|
|
|
179
215
|
current = current.parent
|
|
180
216
|
end
|
|
181
217
|
end
|
|
182
|
-
|
|
183
|
-
#: (RubyIndexer::Entry entry) -> Integer
|
|
184
|
-
def kind_for_entry(entry)
|
|
185
|
-
case entry
|
|
186
|
-
when RubyIndexer::Entry::Class
|
|
187
|
-
Constant::SymbolKind::CLASS
|
|
188
|
-
when RubyIndexer::Entry::Module
|
|
189
|
-
Constant::SymbolKind::NAMESPACE
|
|
190
|
-
when RubyIndexer::Entry::Constant, RubyIndexer::Entry::UnresolvedConstantAlias, RubyIndexer::Entry::ConstantAlias
|
|
191
|
-
Constant::SymbolKind::CONSTANT
|
|
192
|
-
when RubyIndexer::Entry::Method, RubyIndexer::Entry::UnresolvedMethodAlias, RubyIndexer::Entry::MethodAlias
|
|
193
|
-
entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
|
194
|
-
when RubyIndexer::Entry::Accessor
|
|
195
|
-
Constant::SymbolKind::PROPERTY
|
|
196
|
-
when RubyIndexer::Entry::InstanceVariable, RubyIndexer::Entry::ClassVariable
|
|
197
|
-
Constant::SymbolKind::FIELD
|
|
198
|
-
when RubyIndexer::Entry::GlobalVariable
|
|
199
|
-
Constant::SymbolKind::VARIABLE
|
|
200
|
-
else
|
|
201
|
-
Constant::SymbolKind::NULL
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
218
|
end
|
|
205
219
|
end
|
|
206
220
|
end
|