ruby-lsp 0.12.2 → 0.12.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp-doctor +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/{visitor.rb → collector.rb} +108 -59
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +8 -3
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +9 -3
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +22 -4
- data/lib/ruby_indexer/ruby_indexer.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +53 -0
- data/lib/ruby_indexer/test/method_test.rb +14 -0
- data/lib/ruby_lsp/document.rb +7 -9
- data/lib/ruby_lsp/executor.rb +29 -24
- data/lib/ruby_lsp/internal.rb +4 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +8 -4
- data/lib/ruby_lsp/requests/completion.rb +49 -3
- data/lib/ruby_lsp/requests/definition.rb +52 -25
- data/lib/ruby_lsp/requests/document_highlight.rb +18 -8
- data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +20 -0
- data/lib/ruby_lsp/requests/inlay_hints.rb +39 -1
- data/lib/ruby_lsp/requests/on_type_formatting.rb +16 -1
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +13 -5
- data/lib/ruby_lsp/requests/support/common.rb +22 -2
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +0 -1
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -11
- data/lib/ruby_lsp/ruby_document.rb +14 -0
- data/lib/ruby_lsp/setup_bundler.rb +2 -0
- data/lib/ruby_lsp/store.rb +1 -3
- data/lib/ruby_lsp/utils.rb +9 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70a84356e392893c6f7b20e1701ef31621d9902af3a120c7013a13f80ec09568
|
4
|
+
data.tar.gz: bd360c2a5426631816f227ff115af967813ae7284d0a61845daf743b0a99d948
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 598494207add9412e909836e65f6288403189133711dbb17e39663eecc719089dba9f8e758069fda91666715d8c8fc5326055192ba5558f6d8151bbc21f40338
|
7
|
+
data.tar.gz: b86880d5b342e817d8cda46c4977a4229896d19b1fa495c6235f4d86ba74fa1a8ec737b7bad8a19af8859f315980f491b7bd8bcd8f25c1ad21d1ec3f119b9aaf
|
data/README.md
CHANGED
@@ -52,6 +52,7 @@ The Ruby LSP provides an addon system that allows other gems to enhance the base
|
|
52
52
|
features. This is the mechanism that powers addons like
|
53
53
|
|
54
54
|
- [Ruby LSP Rails](https://github.com/Shopify/ruby-lsp-rails)
|
55
|
+
- [Ruby LSP RSpec](https://github.com/st0012/ruby-lsp-rspec)
|
55
56
|
|
56
57
|
For instructions on how to create addons, see the [addons documentation](ADDONS.md).
|
57
58
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.12.
|
1
|
+
0.12.4
|
data/exe/ruby-lsp-doctor
CHANGED
@@ -10,6 +10,6 @@ RubyIndexer.configuration.indexables.each do |indexable|
|
|
10
10
|
puts "indexing: #{indexable.full_path}"
|
11
11
|
content = File.read(indexable.full_path)
|
12
12
|
result = Prism.parse(content)
|
13
|
-
|
14
|
-
result.value
|
13
|
+
collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
|
14
|
+
collector.collect(result.value)
|
15
15
|
end
|
@@ -2,9 +2,11 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyIndexer
|
5
|
-
class
|
5
|
+
class Collector
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
+
LEAVE_EVENT = T.let(Object.new.freeze, Object)
|
9
|
+
|
8
10
|
sig { params(index: Index, parse_result: Prism::ParseResult, file_path: String).void }
|
9
11
|
def initialize(index, parse_result, file_path)
|
10
12
|
@index = index
|
@@ -16,26 +18,67 @@ module RubyIndexer
|
|
16
18
|
end,
|
17
19
|
T::Hash[Integer, Prism::Comment],
|
18
20
|
)
|
21
|
+
@queue = T.let([], T::Array[Object])
|
22
|
+
@current_owner = T.let(nil, T.nilable(Entry::Namespace))
|
19
23
|
|
20
24
|
super()
|
21
25
|
end
|
22
26
|
|
23
|
-
sig {
|
24
|
-
def
|
25
|
-
|
27
|
+
sig { params(node: Prism::Node).void }
|
28
|
+
def collect(node)
|
29
|
+
@queue = [node]
|
30
|
+
|
31
|
+
until @queue.empty?
|
32
|
+
node_or_event = @queue.shift
|
33
|
+
|
34
|
+
case node_or_event
|
35
|
+
when Prism::ProgramNode
|
36
|
+
@queue << node_or_event.statements
|
37
|
+
when Prism::StatementsNode
|
38
|
+
T.unsafe(@queue).prepend(*node_or_event.body)
|
39
|
+
when Prism::ClassNode
|
40
|
+
add_class_entry(node_or_event)
|
41
|
+
when Prism::ModuleNode
|
42
|
+
add_module_entry(node_or_event)
|
43
|
+
when Prism::MultiWriteNode
|
44
|
+
handle_multi_write_node(node_or_event)
|
45
|
+
when Prism::ConstantPathWriteNode
|
46
|
+
handle_constant_path_write_node(node_or_event)
|
47
|
+
when Prism::ConstantPathOrWriteNode
|
48
|
+
handle_constant_path_or_write_node(node_or_event)
|
49
|
+
when Prism::ConstantPathOperatorWriteNode
|
50
|
+
handle_constant_path_operator_write_node(node_or_event)
|
51
|
+
when Prism::ConstantPathAndWriteNode
|
52
|
+
handle_constant_path_and_write_node(node_or_event)
|
53
|
+
when Prism::ConstantWriteNode
|
54
|
+
handle_constant_write_node(node_or_event)
|
55
|
+
when Prism::ConstantOrWriteNode
|
56
|
+
name = fully_qualify_name(node_or_event.name.to_s)
|
57
|
+
add_constant(node_or_event, name)
|
58
|
+
when Prism::ConstantAndWriteNode
|
59
|
+
name = fully_qualify_name(node_or_event.name.to_s)
|
60
|
+
add_constant(node_or_event, name)
|
61
|
+
when Prism::ConstantOperatorWriteNode
|
62
|
+
name = fully_qualify_name(node_or_event.name.to_s)
|
63
|
+
add_constant(node_or_event, name)
|
64
|
+
when Prism::CallNode
|
65
|
+
handle_call_node(node_or_event)
|
66
|
+
when Prism::DefNode
|
67
|
+
handle_def_node(node_or_event)
|
68
|
+
when LEAVE_EVENT
|
69
|
+
@stack.pop
|
70
|
+
end
|
71
|
+
end
|
26
72
|
end
|
27
73
|
|
28
|
-
|
29
|
-
def visit_module_node(node)
|
30
|
-
add_module_entry(node)
|
31
|
-
end
|
74
|
+
private
|
32
75
|
|
33
|
-
sig {
|
34
|
-
def
|
76
|
+
sig { params(node: Prism::MultiWriteNode).void }
|
77
|
+
def handle_multi_write_node(node)
|
35
78
|
value = node.value
|
36
79
|
values = value.is_a?(Prism::ArrayNode) && value.opening_loc ? value.elements : []
|
37
80
|
|
38
|
-
node.
|
81
|
+
[*node.lefts, *node.rest, *node.rights].each_with_index do |target, i|
|
39
82
|
current_value = values[i]
|
40
83
|
# The moment we find a splat on the right hand side of the assignment, we can no longer figure out which value
|
41
84
|
# gets assigned to what
|
@@ -50,8 +93,8 @@ module RubyIndexer
|
|
50
93
|
end
|
51
94
|
end
|
52
95
|
|
53
|
-
sig {
|
54
|
-
def
|
96
|
+
sig { params(node: Prism::ConstantPathWriteNode).void }
|
97
|
+
def handle_constant_path_write_node(node)
|
55
98
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
56
99
|
target = node.target
|
57
100
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -60,8 +103,8 @@ module RubyIndexer
|
|
60
103
|
add_constant(node, name)
|
61
104
|
end
|
62
105
|
|
63
|
-
sig {
|
64
|
-
def
|
106
|
+
sig { params(node: Prism::ConstantPathOrWriteNode).void }
|
107
|
+
def handle_constant_path_or_write_node(node)
|
65
108
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
66
109
|
target = node.target
|
67
110
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -70,8 +113,8 @@ module RubyIndexer
|
|
70
113
|
add_constant(node, name)
|
71
114
|
end
|
72
115
|
|
73
|
-
sig {
|
74
|
-
def
|
116
|
+
sig { params(node: Prism::ConstantPathOperatorWriteNode).void }
|
117
|
+
def handle_constant_path_operator_write_node(node)
|
75
118
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
76
119
|
target = node.target
|
77
120
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -80,8 +123,8 @@ module RubyIndexer
|
|
80
123
|
add_constant(node, name)
|
81
124
|
end
|
82
125
|
|
83
|
-
sig {
|
84
|
-
def
|
126
|
+
sig { params(node: Prism::ConstantPathAndWriteNode).void }
|
127
|
+
def handle_constant_path_and_write_node(node)
|
85
128
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
86
129
|
target = node.target
|
87
130
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -90,50 +133,44 @@ module RubyIndexer
|
|
90
133
|
add_constant(node, name)
|
91
134
|
end
|
92
135
|
|
93
|
-
sig {
|
94
|
-
def
|
95
|
-
name = fully_qualify_name(node.name.to_s)
|
96
|
-
add_constant(node, name)
|
97
|
-
end
|
98
|
-
|
99
|
-
sig { override.params(node: Prism::ConstantOrWriteNode).void }
|
100
|
-
def visit_constant_or_write_node(node)
|
101
|
-
name = fully_qualify_name(node.name.to_s)
|
102
|
-
add_constant(node, name)
|
103
|
-
end
|
104
|
-
|
105
|
-
sig { override.params(node: Prism::ConstantAndWriteNode).void }
|
106
|
-
def visit_constant_and_write_node(node)
|
136
|
+
sig { params(node: Prism::ConstantWriteNode).void }
|
137
|
+
def handle_constant_write_node(node)
|
107
138
|
name = fully_qualify_name(node.name.to_s)
|
108
139
|
add_constant(node, name)
|
109
140
|
end
|
110
141
|
|
111
|
-
sig {
|
112
|
-
def
|
113
|
-
name = fully_qualify_name(node.name.to_s)
|
114
|
-
add_constant(node, name)
|
115
|
-
end
|
116
|
-
|
117
|
-
sig { override.params(node: Prism::CallNode).void }
|
118
|
-
def visit_call_node(node)
|
142
|
+
sig { params(node: Prism::CallNode).void }
|
143
|
+
def handle_call_node(node)
|
119
144
|
message = node.message
|
120
145
|
handle_private_constant(node) if message == "private_constant"
|
121
146
|
end
|
122
147
|
|
123
|
-
sig {
|
124
|
-
def
|
148
|
+
sig { params(node: Prism::DefNode).void }
|
149
|
+
def handle_def_node(node)
|
125
150
|
method_name = node.name.to_s
|
126
151
|
comments = collect_comments(node)
|
127
152
|
case node.receiver
|
128
153
|
when nil
|
129
|
-
@index << Entry::InstanceMethod.new(
|
154
|
+
@index << Entry::InstanceMethod.new(
|
155
|
+
method_name,
|
156
|
+
@file_path,
|
157
|
+
node.location,
|
158
|
+
comments,
|
159
|
+
node.parameters,
|
160
|
+
@current_owner,
|
161
|
+
)
|
130
162
|
when Prism::SelfNode
|
131
|
-
@index << Entry::SingletonMethod.new(
|
163
|
+
@index << Entry::SingletonMethod.new(
|
164
|
+
method_name,
|
165
|
+
@file_path,
|
166
|
+
node.location,
|
167
|
+
comments,
|
168
|
+
node.parameters,
|
169
|
+
@current_owner,
|
170
|
+
)
|
132
171
|
end
|
133
172
|
end
|
134
173
|
|
135
|
-
private
|
136
|
-
|
137
174
|
sig { params(node: Prism::CallNode).void }
|
138
175
|
def handle_private_constant(node)
|
139
176
|
arguments = node.arguments&.arguments
|
@@ -189,12 +226,12 @@ module RubyIndexer
|
|
189
226
|
|
190
227
|
# If the right hand side is another constant assignment, we need to visit it because that constant has to be
|
191
228
|
# indexed too
|
192
|
-
|
229
|
+
@queue.prepend(value)
|
193
230
|
Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
|
194
231
|
when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
|
195
232
|
Prism::ConstantPathAndWriteNode
|
196
233
|
|
197
|
-
|
234
|
+
@queue.prepend(value)
|
198
235
|
Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
|
199
236
|
else
|
200
237
|
Entry::Constant.new(name, @file_path, node.location, comments)
|
@@ -204,20 +241,26 @@ module RubyIndexer
|
|
204
241
|
sig { params(node: Prism::ModuleNode).void }
|
205
242
|
def add_module_entry(node)
|
206
243
|
name = node.constant_path.location.slice
|
207
|
-
|
244
|
+
unless /^[A-Z:]/.match?(name)
|
245
|
+
@queue << node.body
|
246
|
+
return
|
247
|
+
end
|
208
248
|
|
209
249
|
comments = collect_comments(node)
|
210
|
-
|
211
|
-
@index <<
|
250
|
+
@current_owner = Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
|
251
|
+
@index << @current_owner
|
212
252
|
@stack << name
|
213
|
-
|
214
|
-
@stack.pop
|
253
|
+
@queue.prepend(node.body, LEAVE_EVENT)
|
215
254
|
end
|
216
255
|
|
217
256
|
sig { params(node: Prism::ClassNode).void }
|
218
257
|
def add_class_entry(node)
|
219
258
|
name = node.constant_path.location.slice
|
220
|
-
|
259
|
+
|
260
|
+
unless /^[A-Z:]/.match?(name)
|
261
|
+
@queue << node.body
|
262
|
+
return
|
263
|
+
end
|
221
264
|
|
222
265
|
comments = collect_comments(node)
|
223
266
|
|
@@ -227,10 +270,16 @@ module RubyIndexer
|
|
227
270
|
superclass.slice
|
228
271
|
end
|
229
272
|
|
230
|
-
@
|
273
|
+
@current_owner = Entry::Class.new(
|
274
|
+
fully_qualify_name(name),
|
275
|
+
@file_path,
|
276
|
+
node.location,
|
277
|
+
comments,
|
278
|
+
parent_class,
|
279
|
+
)
|
280
|
+
@index << @current_owner
|
231
281
|
@stack << name
|
232
|
-
|
233
|
-
@stack.pop
|
282
|
+
@queue.prepend(node.body, LEAVE_EVENT)
|
234
283
|
end
|
235
284
|
|
236
285
|
sig { params(node: Prism::Node).returns(T::Array[String]) }
|
@@ -249,7 +298,7 @@ module RubyIndexer
|
|
249
298
|
|
250
299
|
comment_content.delete_prefix!("#")
|
251
300
|
comment_content.delete_prefix!(" ")
|
252
|
-
comments.
|
301
|
+
comments.prepend(comment_content)
|
253
302
|
end
|
254
303
|
|
255
304
|
comments
|
@@ -71,8 +71,13 @@ module RubyIndexer
|
|
71
71
|
|
72
72
|
Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
|
73
73
|
# All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
|
74
|
-
# entry is expensive, we memoize it
|
75
|
-
|
74
|
+
# entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
|
75
|
+
# on repositories that define multiple gems, like Rails. All frameworks are defined inside the Dir.pwd, but
|
76
|
+
# each one of them belongs to a different $LOAD_PATH entry
|
77
|
+
if load_path_entry.nil? || !path.start_with?(load_path_entry)
|
78
|
+
load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
|
79
|
+
end
|
80
|
+
|
76
81
|
IndexablePath.new(load_path_entry, path)
|
77
82
|
end
|
78
83
|
end
|
@@ -144,7 +149,7 @@ module RubyIndexer
|
|
144
149
|
# just ignore if they're missing
|
145
150
|
end
|
146
151
|
|
147
|
-
indexables.uniq!
|
152
|
+
indexables.uniq!(&:full_path)
|
148
153
|
indexables
|
149
154
|
end
|
150
155
|
|
@@ -101,6 +101,9 @@ module RubyIndexer
|
|
101
101
|
sig { returns(T::Array[Parameter]) }
|
102
102
|
attr_reader :parameters
|
103
103
|
|
104
|
+
sig { returns(T.nilable(Entry::Namespace)) }
|
105
|
+
attr_reader :owner
|
106
|
+
|
104
107
|
sig do
|
105
108
|
params(
|
106
109
|
name: String,
|
@@ -108,11 +111,13 @@ module RubyIndexer
|
|
108
111
|
location: Prism::Location,
|
109
112
|
comments: T::Array[String],
|
110
113
|
parameters_node: T.nilable(Prism::ParametersNode),
|
114
|
+
owner: T.nilable(Entry::Namespace),
|
111
115
|
).void
|
112
116
|
end
|
113
|
-
def initialize(name, file_path, location, comments, parameters_node)
|
117
|
+
def initialize(name, file_path, location, comments, parameters_node, owner) # rubocop:disable Metrics/ParameterLists
|
114
118
|
super(name, file_path, location, comments)
|
115
119
|
@parameters = T.let(list_params(parameters_node), T::Array[Parameter])
|
120
|
+
@owner = owner
|
116
121
|
end
|
117
122
|
|
118
123
|
private
|
@@ -136,8 +141,9 @@ module RubyIndexer
|
|
136
141
|
case node
|
137
142
|
when Prism::RequiredParameterNode
|
138
143
|
node.name
|
139
|
-
when Prism::
|
140
|
-
names = node.
|
144
|
+
when Prism::MultiTargetNode
|
145
|
+
names = [*node.lefts, *node.rest, *node.rights].map { |parameter_node| parameter_name(parameter_node) }
|
146
|
+
|
141
147
|
names_with_commas = names.join(", ")
|
142
148
|
:"(#{names_with_commas})"
|
143
149
|
end
|
@@ -93,8 +93,14 @@ module RubyIndexer
|
|
93
93
|
# [#<Entry::Class name="Foo::Baz">],
|
94
94
|
# ]
|
95
95
|
# ```
|
96
|
-
sig { params(query: String, nesting: T::Array[String]).returns(T::Array[T::Array[Entry]]) }
|
97
|
-
def prefix_search(query, nesting)
|
96
|
+
sig { params(query: String, nesting: T.nilable(T::Array[String])).returns(T::Array[T::Array[Entry]]) }
|
97
|
+
def prefix_search(query, nesting = nil)
|
98
|
+
unless nesting
|
99
|
+
results = @entries_tree.search(query)
|
100
|
+
results.uniq!
|
101
|
+
return results
|
102
|
+
end
|
103
|
+
|
98
104
|
results = nesting.length.downto(0).flat_map do |i|
|
99
105
|
prefix = T.must(nesting[0...i]).join("::")
|
100
106
|
namespaced_query = prefix.empty? ? query : "#{prefix}::#{query}"
|
@@ -180,8 +186,8 @@ module RubyIndexer
|
|
180
186
|
def index_single(indexable_path, source = nil)
|
181
187
|
content = source || File.read(indexable_path.full_path)
|
182
188
|
result = Prism.parse(content)
|
183
|
-
|
184
|
-
result.value
|
189
|
+
collector = Collector.new(self, result, indexable_path.full_path)
|
190
|
+
collector.collect(result.value)
|
185
191
|
|
186
192
|
require_path = indexable_path.require_path
|
187
193
|
@require_paths_tree.insert(require_path, indexable_path) if require_path
|
@@ -231,6 +237,18 @@ module RubyIndexer
|
|
231
237
|
real_parts.join("::")
|
232
238
|
end
|
233
239
|
|
240
|
+
# Attempts to find a given method for a resolved fully qualified receiver name. Returns `nil` if the method does not
|
241
|
+
# exist on that receiver
|
242
|
+
sig { params(method_name: String, receiver_name: String).returns(T.nilable(Entry::Method)) }
|
243
|
+
def resolve_method(method_name, receiver_name)
|
244
|
+
method_entries = T.cast(self[method_name], T.nilable(T::Array[Entry::Method]))
|
245
|
+
owner_entries = self[receiver_name]
|
246
|
+
return unless owner_entries && method_entries
|
247
|
+
|
248
|
+
owner_name = T.must(owner_entries.first).name
|
249
|
+
method_entries.find { |entry| entry.owner&.name == owner_name }
|
250
|
+
end
|
251
|
+
|
234
252
|
private
|
235
253
|
|
236
254
|
# Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
|
@@ -5,7 +5,7 @@ require "yaml"
|
|
5
5
|
require "did_you_mean"
|
6
6
|
|
7
7
|
require "ruby_indexer/lib/ruby_indexer/indexable_path"
|
8
|
-
require "ruby_indexer/lib/ruby_indexer/
|
8
|
+
require "ruby_indexer/lib/ruby_indexer/collector"
|
9
9
|
require "ruby_indexer/lib/ruby_indexer/index"
|
10
10
|
require "ruby_indexer/lib/ruby_indexer/entry"
|
11
11
|
require "ruby_indexer/lib/ruby_indexer/configuration"
|
@@ -193,5 +193,58 @@ module RubyIndexer
|
|
193
193
|
|
194
194
|
assert_instance_of(Entry::UnresolvedAlias, entry)
|
195
195
|
end
|
196
|
+
|
197
|
+
def test_visitor_does_not_visit_unnecessary_nodes
|
198
|
+
concats = (0...10_000).map do |i|
|
199
|
+
<<~STRING
|
200
|
+
"string#{i}" \\
|
201
|
+
STRING
|
202
|
+
end.join
|
203
|
+
|
204
|
+
index(<<~RUBY)
|
205
|
+
module Foo
|
206
|
+
local_var = #{concats}
|
207
|
+
"final"
|
208
|
+
@class_instance_var = #{concats}
|
209
|
+
"final"
|
210
|
+
@@class_var = #{concats}
|
211
|
+
"final"
|
212
|
+
$global_var = #{concats}
|
213
|
+
"final"
|
214
|
+
CONST = #{concats}
|
215
|
+
"final"
|
216
|
+
end
|
217
|
+
RUBY
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_resolve_method_with_known_receiver
|
221
|
+
index(<<~RUBY)
|
222
|
+
module Foo
|
223
|
+
module Bar
|
224
|
+
def baz; end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
RUBY
|
228
|
+
|
229
|
+
entry = T.must(@index.resolve_method("baz", "Foo::Bar"))
|
230
|
+
assert_equal("baz", entry.name)
|
231
|
+
assert_equal("Foo::Bar", T.must(entry.owner).name)
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_prefix_search_for_methods
|
235
|
+
index(<<~RUBY)
|
236
|
+
module Foo
|
237
|
+
module Bar
|
238
|
+
def baz; end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
RUBY
|
242
|
+
|
243
|
+
entries = @index.prefix_search("ba")
|
244
|
+
refute_empty(entries)
|
245
|
+
|
246
|
+
entry = T.must(entries.first).first
|
247
|
+
assert_equal("baz", entry.name)
|
248
|
+
end
|
196
249
|
end
|
197
250
|
end
|
@@ -69,5 +69,19 @@ module RubyIndexer
|
|
69
69
|
assert_equal(:"(a, (b, ))", parameter.name)
|
70
70
|
assert_instance_of(Entry::RequiredParameter, parameter)
|
71
71
|
end
|
72
|
+
|
73
|
+
def test_keeps_track_of_method_owner
|
74
|
+
index(<<~RUBY)
|
75
|
+
class Foo
|
76
|
+
def bar
|
77
|
+
end
|
78
|
+
end
|
79
|
+
RUBY
|
80
|
+
|
81
|
+
entry = T.must(@index["bar"].first)
|
82
|
+
owner_name = T.must(entry.owner).name
|
83
|
+
|
84
|
+
assert_equal("Foo", owner_name)
|
85
|
+
end
|
72
86
|
end
|
73
87
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -4,6 +4,9 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
class Document
|
6
6
|
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
|
9
|
+
abstract!
|
7
10
|
|
8
11
|
PositionShape = T.type_alias { { line: Integer, character: Integer } }
|
9
12
|
RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
|
@@ -28,8 +31,8 @@ module RubyLsp
|
|
28
31
|
@source = T.let(source, String)
|
29
32
|
@version = T.let(version, Integer)
|
30
33
|
@uri = T.let(uri, URI::Generic)
|
31
|
-
@needs_parsing = T.let(
|
32
|
-
@parse_result = T.let(
|
34
|
+
@needs_parsing = T.let(true, T::Boolean)
|
35
|
+
@parse_result = T.let(parse, Prism::ParseResult)
|
33
36
|
end
|
34
37
|
|
35
38
|
sig { returns(Prism::ProgramNode) }
|
@@ -91,13 +94,8 @@ module RubyLsp
|
|
91
94
|
@cache.clear
|
92
95
|
end
|
93
96
|
|
94
|
-
sig {
|
95
|
-
def parse
|
96
|
-
return unless @needs_parsing
|
97
|
-
|
98
|
-
@needs_parsing = false
|
99
|
-
@parse_result = Prism.parse(@source)
|
100
|
-
end
|
97
|
+
sig { abstract.returns(Prism::ParseResult) }
|
98
|
+
def parse; end
|
101
99
|
|
102
100
|
sig { returns(T::Boolean) }
|
103
101
|
def syntax_error?
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -57,6 +57,8 @@ module RubyLsp
|
|
57
57
|
warn(errored_addons.map(&:backtraces).join("\n\n"))
|
58
58
|
end
|
59
59
|
|
60
|
+
RubyVM::YJIT.enable if defined? RubyVM::YJIT.enable
|
61
|
+
|
60
62
|
perform_initial_indexing
|
61
63
|
check_formatter_is_available
|
62
64
|
|
@@ -474,34 +476,32 @@ module RubyLsp
|
|
474
476
|
def completion(uri, position)
|
475
477
|
document = @store.get(uri)
|
476
478
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
else
|
486
|
-
[Prism::CallNode]
|
487
|
-
end
|
488
|
-
|
489
|
-
matched, parent, nesting = document.locate(document.tree, char_position, node_types: target_node_types)
|
479
|
+
# Completion always receives the position immediately after the character that was just typed. Here we adjust it
|
480
|
+
# back by 1, so that we find the right node
|
481
|
+
char_position = document.create_scanner.find_char_position(position) - 1
|
482
|
+
matched, parent, nesting = document.locate(
|
483
|
+
document.tree,
|
484
|
+
char_position,
|
485
|
+
node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode],
|
486
|
+
)
|
490
487
|
return unless matched && parent
|
491
488
|
|
492
489
|
target = case matched
|
493
490
|
when Prism::CallNode
|
494
491
|
message = matched.message
|
495
|
-
return unless message == "require"
|
496
492
|
|
497
|
-
|
498
|
-
|
493
|
+
if message == "require"
|
494
|
+
args = matched.arguments&.arguments
|
495
|
+
return if args.nil? || args.is_a?(Prism::ForwardingArgumentsNode)
|
499
496
|
|
500
|
-
|
501
|
-
|
502
|
-
|
497
|
+
argument = args.first
|
498
|
+
return unless argument.is_a?(Prism::StringNode)
|
499
|
+
return unless (argument.location.start_offset..argument.location.end_offset).cover?(char_position)
|
503
500
|
|
504
|
-
|
501
|
+
argument
|
502
|
+
else
|
503
|
+
matched
|
504
|
+
end
|
505
505
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
506
506
|
if parent.is_a?(Prism::ConstantPathNode) && matched.is_a?(Prism::ConstantReadNode)
|
507
507
|
parent
|
@@ -579,7 +579,7 @@ module RubyLsp
|
|
579
579
|
# notification
|
580
580
|
end
|
581
581
|
|
582
|
-
sig { params(options: T::Hash[Symbol, T.untyped]).returns(
|
582
|
+
sig { params(options: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
583
583
|
def initialize_request(options)
|
584
584
|
@store.clear
|
585
585
|
|
@@ -680,7 +680,7 @@ module RubyLsp
|
|
680
680
|
completion_provider = if enabled_features["completion"]
|
681
681
|
Interface::CompletionOptions.new(
|
682
682
|
resolve_provider: false,
|
683
|
-
trigger_characters: ["/"
|
683
|
+
trigger_characters: ["/"],
|
684
684
|
completion_item: {
|
685
685
|
labelDetailsSupport: true,
|
686
686
|
},
|
@@ -716,7 +716,7 @@ module RubyLsp
|
|
716
716
|
|
717
717
|
begin_progress("indexing-progress", "Ruby LSP: indexing files")
|
718
718
|
|
719
|
-
|
719
|
+
{
|
720
720
|
capabilities: Interface::ServerCapabilities.new(
|
721
721
|
text_document_sync: Interface::TextDocumentSyncOptions.new(
|
722
722
|
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
@@ -740,7 +740,12 @@ module RubyLsp
|
|
740
740
|
definition_provider: enabled_features["definition"],
|
741
741
|
workspace_symbol_provider: enabled_features["workspaceSymbol"],
|
742
742
|
),
|
743
|
-
|
743
|
+
serverInfo: {
|
744
|
+
name: "Ruby LSP",
|
745
|
+
version: VERSION,
|
746
|
+
},
|
747
|
+
formatter: @store.formatter,
|
748
|
+
}
|
744
749
|
end
|
745
750
|
|
746
751
|
sig { void }
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -24,6 +24,10 @@ require "ruby_lsp/server"
|
|
24
24
|
require "ruby_lsp/executor"
|
25
25
|
require "ruby_lsp/requests"
|
26
26
|
require "ruby_lsp/listener"
|
27
|
+
require "ruby_lsp/document"
|
28
|
+
require "ruby_lsp/ruby_document"
|
27
29
|
require "ruby_lsp/store"
|
28
30
|
require "ruby_lsp/addon"
|
29
31
|
require "ruby_lsp/requests/support/rubocop_runner"
|
32
|
+
|
33
|
+
Bundler.ui.level = :silent
|