ruby-lsp 0.11.2 → 0.12.0
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 +2 -1
- data/exe/ruby-lsp-doctor +16 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +5 -1
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +205 -0
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +23 -106
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -6
- data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +101 -49
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +49 -16
- data/lib/ruby_indexer/test/constant_test.rb +99 -36
- data/lib/ruby_indexer/test/index_test.rb +1 -1
- data/lib/ruby_indexer/test/method_test.rb +73 -0
- data/lib/ruby_indexer/test/test_case.rb +5 -1
- data/lib/ruby_lsp/addon.rb +8 -8
- data/lib/ruby_lsp/document.rb +14 -14
- data/lib/ruby_lsp/executor.rb +89 -53
- data/lib/ruby_lsp/internal.rb +7 -2
- data/lib/ruby_lsp/listener.rb +6 -6
- data/lib/ruby_lsp/requests/base_request.rb +1 -9
- data/lib/ruby_lsp/requests/code_action_resolve.rb +3 -3
- data/lib/ruby_lsp/requests/code_lens.rb +30 -30
- data/lib/ruby_lsp/requests/completion.rb +83 -32
- data/lib/ruby_lsp/requests/definition.rb +21 -15
- data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +508 -31
- data/lib/ruby_lsp/requests/document_link.rb +24 -17
- data/lib/ruby_lsp/requests/document_symbol.rb +42 -42
- data/lib/ruby_lsp/requests/folding_ranges.rb +83 -77
- data/lib/ruby_lsp/requests/hover.rb +22 -17
- data/lib/ruby_lsp/requests/inlay_hints.rb +6 -6
- data/lib/ruby_lsp/requests/selection_ranges.rb +13 -105
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +92 -92
- data/lib/ruby_lsp/requests/support/annotation.rb +3 -3
- data/lib/ruby_lsp/requests/support/common.rb +5 -5
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +10 -7
- data/lib/ruby_lsp/requests/support/sorbet.rb +28 -28
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
- data/lib/ruby_lsp/requests.rb +0 -1
- data/lib/ruby_lsp/setup_bundler.rb +8 -5
- metadata +19 -17
- data/lib/ruby_lsp/event_emitter.rb +0 -351
- data/lib/ruby_lsp/requests/support/highlight_target.rb +0 -118
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd4c9d22a582587f5b28e38863009ac0375ebb66a743da42e823ba3a0ff28987
|
4
|
+
data.tar.gz: 90ff34eac484bc0af67529ce798d9b6a94def13a3989f1c94bb6bae67c190023
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a67a91649a58fef931970abd81a1def32f2a8c58b1cbce454aa22f44d7ca944e2b787ec55025e8051922fd6a9a94edcc38ed332f9c1d0086ad840fc99003536b
|
7
|
+
data.tar.gz: 93b3584713f16f4655c2a075c008e66f263e0de449bebb6e1c440f4de8b3474366f69a69f511934883e38d5437c3b0c4d9817cc164bb943aabfced4134861aba
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.12.0
|
data/exe/ruby-lsp
CHANGED
@@ -49,7 +49,7 @@ if ENV["BUNDLE_GEMFILE"].nil?
|
|
49
49
|
require_relative "../lib/ruby_lsp/setup_bundler"
|
50
50
|
|
51
51
|
begin
|
52
|
-
bundle_gemfile, bundle_path = RubyLsp::SetupBundler.new(Dir.pwd, branch: options[:branch]).setup!
|
52
|
+
bundle_gemfile, bundle_path, bundle_app_config = RubyLsp::SetupBundler.new(Dir.pwd, branch: options[:branch]).setup!
|
53
53
|
rescue RubyLsp::SetupBundler::BundleNotLocked
|
54
54
|
warn("Project contains a Gemfile, but no Gemfile.lock. Run `bundle install` to lock gems and restart the server")
|
55
55
|
exit(78)
|
@@ -57,6 +57,7 @@ if ENV["BUNDLE_GEMFILE"].nil?
|
|
57
57
|
|
58
58
|
env = { "BUNDLE_GEMFILE" => bundle_gemfile }
|
59
59
|
env["BUNDLE_PATH"] = bundle_path if bundle_path
|
60
|
+
env["BUNDLE_APP_CONFIG"] = bundle_app_config if bundle_app_config
|
60
61
|
exit exec(env, "bundle exec ruby-lsp #{original_args.join(" ")}")
|
61
62
|
end
|
62
63
|
|
data/exe/ruby-lsp-doctor
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
|
6
|
+
require "ruby_lsp/internal"
|
7
|
+
|
8
|
+
index = RubyIndexer::Index.new
|
9
|
+
|
10
|
+
RubyIndexer.configuration.indexables.each do |indexable|
|
11
|
+
puts "indexing: #{indexable.full_path}"
|
12
|
+
content = File.read(indexable.full_path)
|
13
|
+
result = Prism.parse(content)
|
14
|
+
visitor = RubyIndexer::IndexVisitor.new(index, result, indexable.full_path)
|
15
|
+
result.value.accept(visitor)
|
16
|
+
end
|
@@ -98,6 +98,10 @@ module RubyIndexer
|
|
98
98
|
next if locked_gems&.any? do |locked_spec|
|
99
99
|
locked_spec.name == short_name &&
|
100
100
|
!Gem::Specification.find_by_name(short_name).full_gem_path.start_with?(RbConfig::CONFIG["rubylibprefix"])
|
101
|
+
rescue Gem::MissingSpecError
|
102
|
+
# If a default gem is scoped to a specific platform, then `find_by_name` will raise. We want to skip those
|
103
|
+
# cases
|
104
|
+
true
|
101
105
|
end
|
102
106
|
|
103
107
|
if pathname.directory?
|
@@ -142,7 +146,7 @@ module RubyIndexer
|
|
142
146
|
|
143
147
|
sig { returns(Regexp) }
|
144
148
|
def magic_comment_regex
|
145
|
-
@magic_comment_regex ||= T.let(
|
149
|
+
@magic_comment_regex ||= T.let(/^#\s*#{@excluded_magic_comments.join("|")}/, T.nilable(Regexp))
|
146
150
|
end
|
147
151
|
|
148
152
|
private
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyIndexer
|
5
|
+
class Entry
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { returns(String) }
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
sig { returns(String) }
|
12
|
+
attr_reader :file_path
|
13
|
+
|
14
|
+
sig { returns(Prism::Location) }
|
15
|
+
attr_reader :location
|
16
|
+
|
17
|
+
sig { returns(T::Array[String]) }
|
18
|
+
attr_reader :comments
|
19
|
+
|
20
|
+
sig { returns(Symbol) }
|
21
|
+
attr_accessor :visibility
|
22
|
+
|
23
|
+
sig { params(name: String, file_path: String, location: Prism::Location, comments: T::Array[String]).void }
|
24
|
+
def initialize(name, file_path, location, comments)
|
25
|
+
@name = name
|
26
|
+
@file_path = file_path
|
27
|
+
@location = location
|
28
|
+
@comments = comments
|
29
|
+
@visibility = T.let(:public, Symbol)
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { returns(String) }
|
33
|
+
def file_name
|
34
|
+
File.basename(@file_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
class Namespace < Entry
|
38
|
+
extend T::Sig
|
39
|
+
extend T::Helpers
|
40
|
+
|
41
|
+
abstract!
|
42
|
+
|
43
|
+
sig { returns(String) }
|
44
|
+
def short_name
|
45
|
+
T.must(@name.split("::").last)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Module < Namespace
|
50
|
+
end
|
51
|
+
|
52
|
+
class Class < Namespace
|
53
|
+
extend T::Sig
|
54
|
+
|
55
|
+
# The unresolved name of the parent class. This may return `nil`, which indicates the lack of an explicit parent
|
56
|
+
# and therefore ::Object is the correct parent class
|
57
|
+
sig { returns(T.nilable(String)) }
|
58
|
+
attr_reader :parent_class
|
59
|
+
|
60
|
+
sig do
|
61
|
+
params(
|
62
|
+
name: String,
|
63
|
+
file_path: String,
|
64
|
+
location: Prism::Location,
|
65
|
+
comments: T::Array[String],
|
66
|
+
parent_class: T.nilable(String),
|
67
|
+
).void
|
68
|
+
end
|
69
|
+
def initialize(name, file_path, location, comments, parent_class)
|
70
|
+
super(name, file_path, location, comments)
|
71
|
+
@parent_class = T.let(parent_class, T.nilable(String))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Constant < Entry
|
76
|
+
end
|
77
|
+
|
78
|
+
class Parameter
|
79
|
+
extend T::Helpers
|
80
|
+
extend T::Sig
|
81
|
+
|
82
|
+
abstract!
|
83
|
+
|
84
|
+
sig { returns(Symbol) }
|
85
|
+
attr_reader :name
|
86
|
+
|
87
|
+
sig { params(name: Symbol).void }
|
88
|
+
def initialize(name:)
|
89
|
+
@name = name
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class RequiredParameter < Parameter
|
94
|
+
end
|
95
|
+
|
96
|
+
class Method < Entry
|
97
|
+
extend T::Sig
|
98
|
+
extend T::Helpers
|
99
|
+
abstract!
|
100
|
+
|
101
|
+
sig { returns(T::Array[Parameter]) }
|
102
|
+
attr_reader :parameters
|
103
|
+
|
104
|
+
sig do
|
105
|
+
params(
|
106
|
+
name: String,
|
107
|
+
file_path: String,
|
108
|
+
location: Prism::Location,
|
109
|
+
comments: T::Array[String],
|
110
|
+
parameters_node: T.nilable(Prism::ParametersNode),
|
111
|
+
).void
|
112
|
+
end
|
113
|
+
def initialize(name, file_path, location, comments, parameters_node)
|
114
|
+
super(name, file_path, location, comments)
|
115
|
+
@parameters = T.let(list_params(parameters_node), T::Array[Parameter])
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
sig { params(parameters_node: T.nilable(Prism::ParametersNode)).returns(T::Array[Parameter]) }
|
121
|
+
def list_params(parameters_node)
|
122
|
+
return [] unless parameters_node
|
123
|
+
|
124
|
+
parameters_node.requireds.filter_map do |required|
|
125
|
+
name = parameter_name(required)
|
126
|
+
next unless name
|
127
|
+
|
128
|
+
RequiredParameter.new(name: name)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
sig do
|
133
|
+
params(node: Prism::Node).returns(T.nilable(Symbol))
|
134
|
+
end
|
135
|
+
def parameter_name(node)
|
136
|
+
case node
|
137
|
+
when Prism::RequiredParameterNode
|
138
|
+
node.name
|
139
|
+
when Prism::RequiredDestructuredParameterNode
|
140
|
+
names = node.parameters.map { |parameter_node| parameter_name(parameter_node) }
|
141
|
+
names_with_commas = names.join(", ")
|
142
|
+
:"(#{names_with_commas})"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class SingletonMethod < Method
|
148
|
+
end
|
149
|
+
|
150
|
+
class InstanceMethod < Method
|
151
|
+
end
|
152
|
+
|
153
|
+
# An UnresolvedAlias points to a constant alias with a right hand side that has not yet been resolved. For
|
154
|
+
# example, if we find
|
155
|
+
#
|
156
|
+
# ```ruby
|
157
|
+
# CONST = Foo
|
158
|
+
# ```
|
159
|
+
# Before we have discovered `Foo`, there's no way to eagerly resolve this alias to the correct target constant.
|
160
|
+
# All aliases are inserted as UnresolvedAlias in the index first and then we lazily resolve them to the correct
|
161
|
+
# target in [rdoc-ref:Index#resolve]. If the right hand side contains a constant that doesn't exist, then it's not
|
162
|
+
# possible to resolve the alias and it will remain an UnresolvedAlias until the right hand side constant exists
|
163
|
+
class UnresolvedAlias < Entry
|
164
|
+
extend T::Sig
|
165
|
+
|
166
|
+
sig { returns(String) }
|
167
|
+
attr_reader :target
|
168
|
+
|
169
|
+
sig { returns(T::Array[String]) }
|
170
|
+
attr_reader :nesting
|
171
|
+
|
172
|
+
sig do
|
173
|
+
params(
|
174
|
+
target: String,
|
175
|
+
nesting: T::Array[String],
|
176
|
+
name: String,
|
177
|
+
file_path: String,
|
178
|
+
location: Prism::Location,
|
179
|
+
comments: T::Array[String],
|
180
|
+
).void
|
181
|
+
end
|
182
|
+
def initialize(target, nesting, name, file_path, location, comments) # rubocop:disable Metrics/ParameterLists
|
183
|
+
super(name, file_path, location, comments)
|
184
|
+
|
185
|
+
@target = target
|
186
|
+
@nesting = nesting
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Alias represents a resolved alias, which points to an existing constant target
|
191
|
+
class Alias < Entry
|
192
|
+
extend T::Sig
|
193
|
+
|
194
|
+
sig { returns(String) }
|
195
|
+
attr_reader :target
|
196
|
+
|
197
|
+
sig { params(target: String, unresolved_alias: UnresolvedAlias).void }
|
198
|
+
def initialize(target, unresolved_alias)
|
199
|
+
super(unresolved_alias.name, unresolved_alias.file_path, unresolved_alias.location, unresolved_alias.comments)
|
200
|
+
|
201
|
+
@target = target
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -95,7 +95,7 @@ module RubyIndexer
|
|
95
95
|
# ```
|
96
96
|
sig { params(query: String, nesting: T::Array[String]).returns(T::Array[T::Array[Entry]]) }
|
97
97
|
def prefix_search(query, nesting)
|
98
|
-
results =
|
98
|
+
results = nesting.length.downto(0).flat_map do |i|
|
99
99
|
prefix = T.must(nesting[0...i]).join("::")
|
100
100
|
namespaced_query = prefix.empty? ? query : "#{prefix}::#{query}"
|
101
101
|
@entries_tree.search(namespaced_query)
|
@@ -153,15 +153,33 @@ module RubyIndexer
|
|
153
153
|
nil
|
154
154
|
end
|
155
155
|
|
156
|
-
|
157
|
-
|
158
|
-
|
156
|
+
# Index all files for the given indexable paths, which defaults to what is configured. A block can be used to track
|
157
|
+
# and control indexing progress. That block is invoked with the current progress percentage and should return `true`
|
158
|
+
# to continue indexing or `false` to stop indexing.
|
159
|
+
sig do
|
160
|
+
params(
|
161
|
+
indexable_paths: T::Array[IndexablePath],
|
162
|
+
block: T.nilable(T.proc.params(progress: Integer).returns(T::Boolean)),
|
163
|
+
).void
|
164
|
+
end
|
165
|
+
def index_all(indexable_paths: RubyIndexer.configuration.indexables, &block)
|
166
|
+
# Calculate how many paths are worth 1% of progress
|
167
|
+
progress_step = (indexable_paths.length / 100.0).ceil
|
168
|
+
|
169
|
+
indexable_paths.each_with_index do |path, index|
|
170
|
+
if block && index % progress_step == 0
|
171
|
+
progress = (index / progress_step) + 1
|
172
|
+
break unless block.call(progress)
|
173
|
+
end
|
174
|
+
|
175
|
+
index_single(path)
|
176
|
+
end
|
159
177
|
end
|
160
178
|
|
161
179
|
sig { params(indexable_path: IndexablePath, source: T.nilable(String)).void }
|
162
180
|
def index_single(indexable_path, source = nil)
|
163
181
|
content = source || File.read(indexable_path.full_path)
|
164
|
-
result =
|
182
|
+
result = Prism.parse(content)
|
165
183
|
visitor = IndexVisitor.new(self, result, indexable_path.full_path)
|
166
184
|
result.value.accept(visitor)
|
167
185
|
|
@@ -234,106 +252,5 @@ module RubyIndexer
|
|
234
252
|
|
235
253
|
resolved_alias
|
236
254
|
end
|
237
|
-
|
238
|
-
class Entry
|
239
|
-
extend T::Sig
|
240
|
-
|
241
|
-
sig { returns(String) }
|
242
|
-
attr_reader :name
|
243
|
-
|
244
|
-
sig { returns(String) }
|
245
|
-
attr_reader :file_path
|
246
|
-
|
247
|
-
sig { returns(YARP::Location) }
|
248
|
-
attr_reader :location
|
249
|
-
|
250
|
-
sig { returns(T::Array[String]) }
|
251
|
-
attr_reader :comments
|
252
|
-
|
253
|
-
sig { returns(Symbol) }
|
254
|
-
attr_accessor :visibility
|
255
|
-
|
256
|
-
sig { params(name: String, file_path: String, location: YARP::Location, comments: T::Array[String]).void }
|
257
|
-
def initialize(name, file_path, location, comments)
|
258
|
-
@name = name
|
259
|
-
@file_path = file_path
|
260
|
-
@location = location
|
261
|
-
@comments = comments
|
262
|
-
@visibility = T.let(:public, Symbol)
|
263
|
-
end
|
264
|
-
|
265
|
-
sig { returns(String) }
|
266
|
-
def file_name
|
267
|
-
File.basename(@file_path)
|
268
|
-
end
|
269
|
-
|
270
|
-
class Namespace < Entry
|
271
|
-
sig { returns(String) }
|
272
|
-
def short_name
|
273
|
-
T.must(@name.split("::").last)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
class Module < Namespace
|
278
|
-
end
|
279
|
-
|
280
|
-
class Class < Namespace
|
281
|
-
end
|
282
|
-
|
283
|
-
class Constant < Entry
|
284
|
-
end
|
285
|
-
|
286
|
-
# An UnresolvedAlias points to a constant alias with a right hand side that has not yet been resolved. For
|
287
|
-
# example, if we find
|
288
|
-
#
|
289
|
-
# ```ruby
|
290
|
-
# CONST = Foo
|
291
|
-
# ```
|
292
|
-
# Before we have discovered `Foo`, there's no way to eagerly resolve this alias to the correct target constant.
|
293
|
-
# All aliases are inserted as UnresolvedAlias in the index first and then we lazily resolve them to the correct
|
294
|
-
# target in [rdoc-ref:Index#resolve]. If the right hand side contains a constant that doesn't exist, then it's not
|
295
|
-
# possible to resolve the alias and it will remain an UnresolvedAlias until the right hand side constant exists
|
296
|
-
class UnresolvedAlias < Entry
|
297
|
-
extend T::Sig
|
298
|
-
|
299
|
-
sig { returns(String) }
|
300
|
-
attr_reader :target
|
301
|
-
|
302
|
-
sig { returns(T::Array[String]) }
|
303
|
-
attr_reader :nesting
|
304
|
-
|
305
|
-
sig do
|
306
|
-
params(
|
307
|
-
target: String,
|
308
|
-
nesting: T::Array[String],
|
309
|
-
name: String,
|
310
|
-
file_path: String,
|
311
|
-
location: YARP::Location,
|
312
|
-
comments: T::Array[String],
|
313
|
-
).void
|
314
|
-
end
|
315
|
-
def initialize(target, nesting, name, file_path, location, comments) # rubocop:disable Metrics/ParameterLists
|
316
|
-
super(name, file_path, location, comments)
|
317
|
-
|
318
|
-
@target = target
|
319
|
-
@nesting = nesting
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
# Alias represents a resolved alias, which points to an existing constant target
|
324
|
-
class Alias < Entry
|
325
|
-
extend T::Sig
|
326
|
-
|
327
|
-
sig { returns(String) }
|
328
|
-
attr_reader :target
|
329
|
-
|
330
|
-
sig { params(target: String, unresolved_alias: UnresolvedAlias).void }
|
331
|
-
def initialize(target, unresolved_alias)
|
332
|
-
super(unresolved_alias.name, unresolved_alias.file_path, unresolved_alias.location, unresolved_alias.comments)
|
333
|
-
|
334
|
-
@target = target
|
335
|
-
end
|
336
|
-
end
|
337
|
-
end
|
338
255
|
end
|
339
256
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyIndexer
|
@@ -133,16 +133,16 @@ module RubyIndexer
|
|
133
133
|
@key = key
|
134
134
|
@value = value
|
135
135
|
@parent = parent
|
136
|
-
@children =
|
137
|
-
@leaf =
|
136
|
+
@children = {}
|
137
|
+
@leaf = false
|
138
138
|
end
|
139
139
|
|
140
140
|
sig { returns(T::Array[Value]) }
|
141
141
|
def collect
|
142
|
-
result =
|
143
|
-
result << value if leaf
|
142
|
+
result = []
|
143
|
+
result << @value if @leaf
|
144
144
|
|
145
|
-
children.each_value do |node|
|
145
|
+
@children.each_value do |node|
|
146
146
|
result.concat(node.collect)
|
147
147
|
end
|
148
148
|
|