ruby-lsp 0.11.2 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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