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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +2 -1
  4. data/exe/ruby-lsp-doctor +16 -0
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +5 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +205 -0
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +23 -106
  8. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -6
  9. data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +101 -49
  10. data/lib/ruby_indexer/ruby_indexer.rb +1 -0
  11. data/lib/ruby_indexer/test/classes_and_modules_test.rb +49 -16
  12. data/lib/ruby_indexer/test/constant_test.rb +99 -36
  13. data/lib/ruby_indexer/test/index_test.rb +1 -1
  14. data/lib/ruby_indexer/test/method_test.rb +73 -0
  15. data/lib/ruby_indexer/test/test_case.rb +5 -1
  16. data/lib/ruby_lsp/addon.rb +8 -8
  17. data/lib/ruby_lsp/document.rb +14 -14
  18. data/lib/ruby_lsp/executor.rb +89 -53
  19. data/lib/ruby_lsp/internal.rb +7 -2
  20. data/lib/ruby_lsp/listener.rb +6 -6
  21. data/lib/ruby_lsp/requests/base_request.rb +1 -9
  22. data/lib/ruby_lsp/requests/code_action_resolve.rb +3 -3
  23. data/lib/ruby_lsp/requests/code_lens.rb +30 -30
  24. data/lib/ruby_lsp/requests/completion.rb +83 -32
  25. data/lib/ruby_lsp/requests/definition.rb +21 -15
  26. data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
  27. data/lib/ruby_lsp/requests/document_highlight.rb +508 -31
  28. data/lib/ruby_lsp/requests/document_link.rb +24 -17
  29. data/lib/ruby_lsp/requests/document_symbol.rb +42 -42
  30. data/lib/ruby_lsp/requests/folding_ranges.rb +83 -77
  31. data/lib/ruby_lsp/requests/hover.rb +22 -17
  32. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -6
  33. data/lib/ruby_lsp/requests/selection_ranges.rb +13 -105
  34. data/lib/ruby_lsp/requests/semantic_highlighting.rb +92 -92
  35. data/lib/ruby_lsp/requests/support/annotation.rb +3 -3
  36. data/lib/ruby_lsp/requests/support/common.rb +5 -5
  37. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -0
  38. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +10 -7
  39. data/lib/ruby_lsp/requests/support/sorbet.rb +28 -28
  40. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
  41. data/lib/ruby_lsp/requests.rb +0 -1
  42. data/lib/ruby_lsp/setup_bundler.rb +8 -5
  43. metadata +19 -17
  44. data/lib/ruby_lsp/event_emitter.rb +0 -351
  45. 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: 836f512695d45c2b360a2a6533185bd489a7d1c45eda21c0e67a2602cff23712
4
- data.tar.gz: bdf8fdeefffbb7620ba60cdd91e7e73d7ee9121c5bd810f3456fd46835f5bf5e
3
+ metadata.gz: dd4c9d22a582587f5b28e38863009ac0375ebb66a743da42e823ba3a0ff28987
4
+ data.tar.gz: 90ff34eac484bc0af67529ce798d9b6a94def13a3989f1c94bb6bae67c190023
5
5
  SHA512:
6
- metadata.gz: 4be56dc36e58719d4ccdaea4b720507f21178b0fcfbbc9f634374dd808c33f46872c1cb25dc427f25f007d9b9b1333404c5724e8bbf8bab71ff1a3700c9a766d
7
- data.tar.gz: 6afc266ad6ae954492d9603e456ba1f65ed00ec6fdc31984b1c42c6597867224d2a230ad165ee2c18afc39d7e5e6eb0f88fc2f4f53e09a724ef67cd4e87e53b2
6
+ metadata.gz: a67a91649a58fef931970abd81a1def32f2a8c58b1cbce454aa22f44d7ca944e2b787ec55025e8051922fd6a9a94edcc38ed332f9c1d0086ad840fc99003536b
7
+ data.tar.gz: 93b3584713f16f4655c2a075c008e66f263e0de449bebb6e1c440f4de8b3474366f69a69f511934883e38d5437c3b0c4d9817cc164bb943aabfced4134861aba
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.11.2
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
 
@@ -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(/^\s*#\s*#{@excluded_magic_comments.join("|")}/, T.nilable(Regexp))
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 = (nesting.length + 1).downto(0).flat_map do |i|
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
- sig { params(indexable_paths: T::Array[IndexablePath]).void }
157
- def index_all(indexable_paths: RubyIndexer.configuration.indexables)
158
- indexable_paths.each { |path| index_single(path) }
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 = YARP.parse(content)
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: strict
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 = T.let({}, T::Hash[String, Node[Value]])
137
- @leaf = T.let(false, T::Boolean)
136
+ @children = {}
137
+ @leaf = false
138
138
  end
139
139
 
140
140
  sig { returns(T::Array[Value]) }
141
141
  def collect
142
- result = T.let([], T::Array[Value])
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