ruby-lsp 0.22.1 → 0.23.10

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +12 -11
  5. data/exe/ruby-lsp-check +5 -5
  6. data/exe/ruby-lsp-launcher +41 -15
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +191 -100
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +174 -61
  11. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +12 -0
  12. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
  13. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +82 -61
  14. data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
  15. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
  16. data/lib/ruby_indexer/ruby_indexer.rb +2 -1
  17. data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
  18. data/lib/ruby_indexer/test/classes_and_modules_test.rb +30 -6
  19. data/lib/ruby_indexer/test/configuration_test.rb +116 -51
  20. data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
  21. data/lib/ruby_indexer/test/index_test.rb +143 -44
  22. data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
  23. data/lib/ruby_indexer/test/method_test.rb +86 -8
  24. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  25. data/lib/ruby_indexer/test/reference_finder_test.rb +90 -2
  26. data/lib/ruby_indexer/test/test_case.rb +2 -2
  27. data/lib/ruby_indexer/test/uri_test.rb +72 -0
  28. data/lib/ruby_lsp/addon.rb +9 -0
  29. data/lib/ruby_lsp/base_server.rb +17 -18
  30. data/lib/ruby_lsp/client_capabilities.rb +7 -1
  31. data/lib/ruby_lsp/document.rb +72 -10
  32. data/lib/ruby_lsp/erb_document.rb +5 -3
  33. data/lib/ruby_lsp/global_state.rb +42 -3
  34. data/lib/ruby_lsp/internal.rb +3 -1
  35. data/lib/ruby_lsp/listeners/code_lens.rb +9 -5
  36. data/lib/ruby_lsp/listeners/completion.rb +78 -6
  37. data/lib/ruby_lsp/listeners/definition.rb +80 -19
  38. data/lib/ruby_lsp/listeners/document_highlight.rb +3 -2
  39. data/lib/ruby_lsp/listeners/document_link.rb +21 -3
  40. data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
  41. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  42. data/lib/ruby_lsp/listeners/hover.rb +59 -2
  43. data/lib/ruby_lsp/load_sorbet.rb +3 -3
  44. data/lib/ruby_lsp/rbs_document.rb +2 -2
  45. data/lib/ruby_lsp/requests/code_action_resolve.rb +90 -6
  46. data/lib/ruby_lsp/requests/code_actions.rb +57 -1
  47. data/lib/ruby_lsp/requests/completion.rb +8 -1
  48. data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
  49. data/lib/ruby_lsp/requests/definition.rb +7 -1
  50. data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
  51. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  52. data/lib/ruby_lsp/requests/folding_ranges.rb +2 -6
  53. data/lib/ruby_lsp/requests/formatting.rb +2 -6
  54. data/lib/ruby_lsp/requests/hover.rb +1 -1
  55. data/lib/ruby_lsp/requests/on_type_formatting.rb +2 -2
  56. data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
  57. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
  58. data/lib/ruby_lsp/requests/references.rb +29 -2
  59. data/lib/ruby_lsp/requests/rename.rb +17 -7
  60. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  61. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
  62. data/lib/ruby_lsp/requests/signature_help.rb +1 -1
  63. data/lib/ruby_lsp/requests/support/common.rb +2 -9
  64. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +3 -3
  65. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -13
  66. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  67. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -3
  68. data/lib/ruby_lsp/ruby_document.rb +80 -6
  69. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  70. data/lib/ruby_lsp/server.rb +205 -61
  71. data/lib/ruby_lsp/setup_bundler.rb +50 -43
  72. data/lib/ruby_lsp/store.rb +7 -7
  73. data/lib/ruby_lsp/test_helper.rb +45 -11
  74. data/lib/ruby_lsp/type_inferrer.rb +60 -31
  75. data/lib/ruby_lsp/utils.rb +63 -3
  76. metadata +8 -8
  77. data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
@@ -57,6 +57,7 @@ module RubyLsp
57
57
  @lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
58
58
  @last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
59
59
  @error_path = T.let(@custom_dir + "install_error", Pathname)
60
+ @already_composed_path = T.let(@custom_dir + "bundle_is_composed", Pathname)
60
61
 
61
62
  dependencies, bundler_version = load_dependencies
62
63
  @dependencies = T.let(dependencies, T::Hash[String, T.untyped])
@@ -71,6 +72,23 @@ module RubyLsp
71
72
  def setup!
72
73
  raise BundleNotLocked if !@launcher && @gemfile&.exist? && !@lockfile&.exist?
73
74
 
75
+ # If the bundle was composed ahead of time using our custom `rubyLsp/composeBundle` request, then we can skip the
76
+ # entire process and just return the composed environment
77
+ if @already_composed_path.exist?
78
+ $stderr.puts("Ruby LSP> Composed bundle was set up ahead of time. Skipping...")
79
+ @already_composed_path.delete
80
+
81
+ env = bundler_settings_as_env
82
+ env["BUNDLE_GEMFILE"] = @custom_gemfile.exist? ? @custom_gemfile.to_s : @gemfile.to_s
83
+
84
+ if env["BUNDLE_PATH"]
85
+ env["BUNDLE_PATH"] = File.expand_path(env["BUNDLE_PATH"], @project_path)
86
+ end
87
+
88
+ env["BUNDLER_VERSION"] = @bundler_version.to_s if @bundler_version
89
+ return env
90
+ end
91
+
74
92
  # Automatically create and ignore the .ruby-lsp folder for users
75
93
  @custom_dir.mkpath unless @custom_dir.exist?
76
94
  ignore_file = @custom_dir + ".gitignore"
@@ -152,7 +170,13 @@ module RubyLsp
152
170
  end
153
171
 
154
172
  unless @dependencies["debug"]
155
- parts << 'gem "debug", require: false, group: :development, platforms: :mri'
173
+ # The `mri` platform excludes Windows. We want to install the debug gem only on MRI for any operating system,
174
+ # but that constraint doesn't yet exist in Bundler. On Windows, we are manually checking if the engine is MRI
175
+ parts << if Gem.win_platform?
176
+ 'gem "debug", require: false, group: :development, install_if: -> { RUBY_ENGINE == "ruby" }'
177
+ else
178
+ 'gem "debug", require: false, group: :development, platforms: :mri'
179
+ end
156
180
  end
157
181
 
158
182
  if @rails_app && !@dependencies["ruby-lsp-rails"]
@@ -196,15 +220,15 @@ module RubyLsp
196
220
  env["BUNDLE_PATH"] = File.expand_path(env["BUNDLE_PATH"], @project_path)
197
221
  end
198
222
 
199
- return run_bundle_install_through_command(env) unless @launcher
200
-
201
- # This same check happens conditionally when running through the command. For invoking the CLI directly, it's
202
- # important that we ensure the Bundler version is set to avoid restarts
223
+ # Set the specific Bundler version used by the main app. This avoids issues with Bundler restarts, which clean the
224
+ # environment and lead to the `ruby-lsp` executable not being found
203
225
  if @bundler_version
204
226
  env["BUNDLER_VERSION"] = @bundler_version.to_s
205
227
  install_bundler_if_needed
206
228
  end
207
229
 
230
+ return run_bundle_install_through_command(env) unless @launcher
231
+
208
232
  begin
209
233
  run_bundle_install_directly(env)
210
234
  # If no error occurred, then clear previous errors
@@ -217,8 +241,7 @@ module RubyLsp
217
241
 
218
242
  # If either the Gemfile or the lockfile have been modified during the process of setting up the bundle, retry
219
243
  # composing the bundle from scratch
220
-
221
- if @gemfile && @lockfile
244
+ if @gemfile&.exist? && @lockfile&.exist?
222
245
  current_gemfile_hash = Digest::SHA256.hexdigest(@gemfile.read)
223
246
  current_lockfile_hash = Digest::SHA256.hexdigest(@lockfile.read)
224
247
 
@@ -235,12 +258,16 @@ module RubyLsp
235
258
  env
236
259
  end
237
260
 
238
- sig { params(env: T::Hash[String, String]).returns(T::Hash[String, String]) }
239
- def run_bundle_install_directly(env)
261
+ sig { params(env: T::Hash[String, String], force_install: T::Boolean).returns(T::Hash[String, String]) }
262
+ def run_bundle_install_directly(env, force_install: false)
240
263
  RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
264
+
265
+ # The ENV can only be merged after checking if an update is required because we depend on the original value of
266
+ # ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
267
+ should_update = should_bundle_update?
241
268
  T.unsafe(ENV).merge!(env)
242
269
 
243
- unless should_bundle_update?
270
+ unless should_update && !force_install
244
271
  Bundler::CLI::Install.new({}).run
245
272
  correct_relative_remote_paths if @custom_lockfile.exist?
246
273
  return env
@@ -255,12 +282,13 @@ module RubyLsp
255
282
  correct_relative_remote_paths if @custom_lockfile.exist?
256
283
  @last_updated_path.write(Time.now.iso8601)
257
284
  env
285
+ rescue Bundler::GemNotFound, Bundler::GitError
286
+ # If a gem is not installed, skip the upgrade and try to install it with a single retry
287
+ @retry ? env : run_bundle_install_directly(env, force_install: true)
258
288
  end
259
289
 
260
290
  sig { params(env: T::Hash[String, String]).returns(T::Hash[String, String]) }
261
291
  def run_bundle_install_through_command(env)
262
- base_bundle = base_bundle_command(env)
263
-
264
292
  # If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
265
293
  # to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
266
294
  # and any of `ruby-lsp`, `ruby-lsp-rails` or `debug` weren't a part of the Gemfile, then we need to run `bundle
@@ -269,13 +297,20 @@ module RubyLsp
269
297
 
270
298
  # When not updating, we run `(bundle check || bundle install)`
271
299
  # When updating, we run `((bundle check && bundle update ruby-lsp debug) || bundle install)`
272
- command = +"(#{base_bundle} check"
300
+ bundler_path = File.join(Gem.default_bindir, "bundle")
301
+ base_command = (!Gem.win_platform? && File.exist?(bundler_path) ? "#{Gem.ruby} #{bundler_path}" : "bundle").dup
302
+
303
+ if env["BUNDLER_VERSION"]
304
+ base_command << " _#{env["BUNDLER_VERSION"]}_"
305
+ end
306
+
307
+ command = +"(#{base_command} check"
273
308
 
274
309
  if should_bundle_update?
275
310
  # If any of `ruby-lsp`, `ruby-lsp-rails` or `debug` are not in the Gemfile, try to update them to the latest
276
311
  # version
277
312
  command.prepend("(")
278
- command << " && #{base_bundle} update "
313
+ command << " && #{base_command} update "
279
314
  command << "ruby-lsp " unless @dependencies["ruby-lsp"]
280
315
  command << "debug " unless @dependencies["debug"]
281
316
  command << "ruby-lsp-rails " if @rails_app && !@dependencies["ruby-lsp-rails"]
@@ -285,7 +320,7 @@ module RubyLsp
285
320
  @last_updated_path.write(Time.now.iso8601)
286
321
  end
287
322
 
288
- command << " || #{base_bundle} install) "
323
+ command << " || #{base_command} install) "
289
324
 
290
325
  # Redirect stdout to stderr to prevent going into an infinite loop. The extension might confuse stdout output with
291
326
  # responses
@@ -395,34 +430,6 @@ module RubyLsp
395
430
  /class .* < (::)?Rails::Application/.match?(application_contents)
396
431
  end
397
432
 
398
- # Returns the base bundle command we should use for this project, which will be:
399
- # - `bundle` if there's no locked Bundler version and no `bin/bundle` binstub in the $PATH
400
- # - `bundle _<version>_` if there's a locked Bundler version
401
- # - `bin/bundle` if there's a `bin/bundle` binstub in the $PATH
402
- sig { params(env: T::Hash[String, String]).returns(String) }
403
- def base_bundle_command(env)
404
- path_parts = if Gem.win_platform?
405
- ENV["Path"] || ENV["PATH"] || ENV["path"] || ""
406
- else
407
- ENV["PATH"] || ""
408
- end.split(File::PATH_SEPARATOR)
409
-
410
- bin_dir = File.expand_path("bin", @project_path)
411
- bundle_binstub = File.join(@project_path, "bin", "bundle")
412
-
413
- if File.exist?(bundle_binstub) && path_parts.any? { |path| File.expand_path(path, @project_path) == bin_dir }
414
- return bundle_binstub
415
- end
416
-
417
- if @bundler_version
418
- env["BUNDLER_VERSION"] = @bundler_version.to_s
419
- install_bundler_if_needed
420
- return "bundle _#{@bundler_version}_"
421
- end
422
-
423
- "bundle"
424
- end
425
-
426
433
  sig { void }
427
434
  def patch_thor_to_print_progress_to_stderr!
428
435
  return unless defined?(Bundler::Thor::Shell::Basic)
@@ -13,8 +13,9 @@ module RubyLsp
13
13
  sig { returns(String) }
14
14
  attr_accessor :client_name
15
15
 
16
- sig { void }
17
- def initialize
16
+ sig { params(global_state: GlobalState).void }
17
+ def initialize(global_state)
18
+ @global_state = global_state
18
19
  @state = T.let({}, T::Hash[String, Document[T.untyped]])
19
20
  @features_configuration = T.let(
20
21
  {
@@ -61,17 +62,16 @@ module RubyLsp
61
62
  source: String,
62
63
  version: Integer,
63
64
  language_id: Document::LanguageId,
64
- encoding: Encoding,
65
65
  ).returns(Document[T.untyped])
66
66
  end
67
- def set(uri:, source:, version:, language_id:, encoding: Encoding::UTF_8)
67
+ def set(uri:, source:, version:, language_id:)
68
68
  @state[uri.to_s] = case language_id
69
69
  when Document::LanguageId::ERB
70
- ERBDocument.new(source: source, version: version, uri: uri, encoding: encoding)
70
+ ERBDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
71
71
  when Document::LanguageId::RBS
72
- RBSDocument.new(source: source, version: version, uri: uri, encoding: encoding)
72
+ RBSDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
73
73
  else
74
- RubyDocument.new(source: source, version: version, uri: uri, encoding: encoding)
74
+ RubyDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
75
75
  end
76
76
  end
77
77
 
@@ -1,11 +1,16 @@
1
- # typed: strict
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # NOTE: This module is intended to be used by addons for writing their own tests, so keep that in mind if changing.
5
5
 
6
6
  module RubyLsp
7
7
  module TestHelper
8
+ class TestError < StandardError; end
9
+
8
10
  extend T::Sig
11
+ extend T::Helpers
12
+
13
+ requires_ancestor { Kernel }
9
14
 
10
15
  sig do
11
16
  type_parameters(:T)
@@ -36,20 +41,49 @@ module RubyLsp
36
41
  },
37
42
  },
38
43
  })
44
+
45
+ server.global_state.index.index_single(uri, source)
39
46
  end
40
47
 
41
- server.global_state.index.index_single(
42
- RubyIndexer::IndexablePath.new(nil, T.must(uri.to_standardized_path)),
43
- source,
44
- )
45
48
  server.load_addons(include_project_addons: false) if load_addons
46
- block.call(server, uri)
47
- ensure
48
- if load_addons
49
- RubyLsp::Addon.addons.each(&:deactivate)
50
- RubyLsp::Addon.addons.clear
49
+
50
+ begin
51
+ block.call(server, uri)
52
+ ensure
53
+ if load_addons
54
+ RubyLsp::Addon.addons.each(&:deactivate)
55
+ RubyLsp::Addon.addons.clear
56
+ end
57
+ server.run_shutdown
58
+ end
59
+ end
60
+
61
+ sig { params(server: RubyLsp::Server).returns(RubyLsp::Result) }
62
+ def pop_result(server)
63
+ result = server.pop_response
64
+ result = server.pop_response until result.is_a?(RubyLsp::Result) || result.is_a?(RubyLsp::Error)
65
+
66
+ if result.is_a?(RubyLsp::Error)
67
+ raise TestError, "Failed to execute request #{result.message}"
68
+ else
69
+ result
51
70
  end
52
- T.must(server).run_shutdown
71
+ end
72
+
73
+ def pop_log_notification(message_queue, type)
74
+ log = message_queue.pop
75
+ return log if log.params.type == type
76
+
77
+ log = message_queue.pop until log.params.type == type
78
+ log
79
+ end
80
+
81
+ def pop_message(outgoing_queue, &block)
82
+ message = outgoing_queue.pop
83
+ return message if block.call(message)
84
+
85
+ message = outgoing_queue.pop until block.call(message)
86
+ message
53
87
  end
54
88
  end
55
89
  end
@@ -23,6 +23,9 @@ module RubyLsp
23
23
  Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableTargetNode,
24
24
  Prism::SuperNode, Prism::ForwardingSuperNode
25
25
  self_receiver_handling(node_context)
26
+ when Prism::ClassVariableAndWriteNode, Prism::ClassVariableWriteNode, Prism::ClassVariableOperatorWriteNode,
27
+ Prism::ClassVariableOrWriteNode, Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
28
+ infer_receiver_for_class_variables(node_context)
26
29
  end
27
30
  end
28
31
 
@@ -77,7 +80,7 @@ module RubyLsp
77
80
  # When the receiver is a constant reference, we have to try to resolve it to figure out the right
78
81
  # receiver. But since the invocation is directly on the constant, that's the singleton context of that
79
82
  # class/module
80
- receiver_name = constant_name(receiver)
83
+ receiver_name = RubyIndexer::Index.constant_name(receiver)
81
84
  return unless receiver_name
82
85
 
83
86
  resolved_receiver = @index.resolve(receiver_name, node_context.nesting)
@@ -88,29 +91,45 @@ module RubyLsp
88
91
  return Type.new("#{last}::<Class:#{last}>") if parts.empty?
89
92
 
90
93
  Type.new("#{parts.join("::")}::#{last}::<Class:#{last}>")
91
- else
94
+ when Prism::CallNode
95
+ raw_receiver = receiver.message
92
96
 
93
- raw_receiver = if receiver.is_a?(Prism::CallNode)
94
- receiver.message
95
- else
96
- receiver.slice
97
- end
97
+ if raw_receiver == "new"
98
+ # When invoking `new`, we recursively infer the type of the receiver to get the class type its being invoked
99
+ # on and then return the attached version of that type, since it's being instantiated.
100
+ type = infer_receiver_for_call_node(receiver, node_context)
101
+
102
+ return unless type
103
+
104
+ # If the method `new` was overridden, then we cannot assume that it will return a new instance of the class
105
+ new_method = @index.resolve_method("new", type.name)&.first
106
+ return if new_method && new_method.owner&.name != "Class"
98
107
 
99
- if raw_receiver
100
- guessed_name = raw_receiver
101
- .delete_prefix("@")
102
- .delete_prefix("@@")
103
- .split("_")
104
- .map(&:capitalize)
105
- .join
106
-
107
- entries = @index.resolve(guessed_name, node_context.nesting) || @index.first_unqualified_const(guessed_name)
108
- name = entries&.first&.name
109
- GuessedType.new(name) if name
108
+ type.attached
109
+ elsif raw_receiver
110
+ guess_type(raw_receiver, node_context.nesting)
110
111
  end
112
+ else
113
+ guess_type(receiver.slice, node_context.nesting)
111
114
  end
112
115
  end
113
116
 
117
+ sig { params(raw_receiver: String, nesting: T::Array[String]).returns(T.nilable(GuessedType)) }
118
+ def guess_type(raw_receiver, nesting)
119
+ guessed_name = raw_receiver
120
+ .delete_prefix("@")
121
+ .delete_prefix("@@")
122
+ .split("_")
123
+ .map(&:capitalize)
124
+ .join
125
+
126
+ entries = @index.resolve(guessed_name, nesting) || @index.first_unqualified_const(guessed_name)
127
+ name = entries&.first&.name
128
+ return unless name
129
+
130
+ GuessedType.new(name)
131
+ end
132
+
114
133
  sig { params(node_context: NodeContext).returns(Type) }
115
134
  def self_receiver_handling(node_context)
116
135
  nesting = node_context.nesting
@@ -128,19 +147,23 @@ module RubyLsp
128
147
  Type.new("#{parts.join("::")}::<Class:#{parts.last}>")
129
148
  end
130
149
 
131
- sig do
132
- params(
133
- node: T.any(
134
- Prism::ConstantPathNode,
135
- Prism::ConstantReadNode,
136
- ),
137
- ).returns(T.nilable(String))
138
- end
139
- def constant_name(node)
140
- node.full_name
141
- rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
142
- Prism::ConstantPathNode::MissingNodesInConstantPathError
143
- nil
150
+ sig { params(node_context: NodeContext).returns(T.nilable(Type)) }
151
+ def infer_receiver_for_class_variables(node_context)
152
+ nesting_parts = node_context.nesting.dup
153
+
154
+ return Type.new("Object") if nesting_parts.empty?
155
+
156
+ nesting_parts.reverse_each do |part|
157
+ break unless part.include?("<Class:")
158
+
159
+ nesting_parts.pop
160
+ end
161
+
162
+ receiver_name = nesting_parts.join("::")
163
+ resolved_receiver = @index.resolve(receiver_name, node_context.nesting)&.first
164
+ return unless resolved_receiver&.name
165
+
166
+ Type.new(resolved_receiver.name)
144
167
  end
145
168
 
146
169
  # A known type
@@ -154,6 +177,12 @@ module RubyLsp
154
177
  def initialize(name)
155
178
  @name = name
156
179
  end
180
+
181
+ # Returns the attached version of this type by removing the `<Class:...>` part from its name
182
+ sig { returns(Type) }
183
+ def attached
184
+ Type.new(T.must(@name.split("::")[..-2]).join("::"))
185
+ end
157
186
  end
158
187
 
159
188
  # A type that was guessed based on the receiver raw name
@@ -25,7 +25,7 @@ module RubyLsp
25
25
  end,
26
26
  String,
27
27
  )
28
- GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/design-and-roadmap.html#guessed-types"
28
+ GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/#guessed-types"
29
29
 
30
30
  # Request delegation for embedded languages is not yet standardized into the language server specification. Here we
31
31
  # use this custom error class as a way to return a signal to the client that the request should be delegated to the
@@ -37,6 +37,8 @@ module RubyLsp
37
37
  CODE = -32900
38
38
  end
39
39
 
40
+ BUNDLE_COMPOSE_FAILED_CODE = -33000
41
+
40
42
  # A notification to be sent to the client
41
43
  class Message
42
44
  extend T::Sig
@@ -142,19 +144,72 @@ module RubyLsp
142
144
  ),
143
145
  )
144
146
  end
147
+
148
+ sig do
149
+ params(
150
+ uri: String,
151
+ diagnostics: T::Array[Interface::Diagnostic],
152
+ version: T.nilable(Integer),
153
+ ).returns(Notification)
154
+ end
155
+ def publish_diagnostics(uri, diagnostics, version: nil)
156
+ new(
157
+ method: "textDocument/publishDiagnostics",
158
+ params: Interface::PublishDiagnosticsParams.new(uri: uri, diagnostics: diagnostics, version: version),
159
+ )
160
+ end
145
161
  end
146
162
 
147
163
  extend T::Sig
148
164
 
149
165
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
150
166
  def to_hash
151
- { method: @method, params: T.unsafe(@params).to_hash }
167
+ hash = { method: @method }
168
+ hash[:params] = T.unsafe(@params).to_hash if @params
169
+ hash
152
170
  end
153
171
  end
154
172
 
155
173
  class Request < Message
156
174
  extend T::Sig
157
175
 
176
+ class << self
177
+ extend T::Sig
178
+
179
+ sig do
180
+ params(
181
+ id: Integer,
182
+ pattern: T.any(Interface::RelativePattern, String),
183
+ kind: Integer,
184
+ registration_id: T.nilable(String),
185
+ ).returns(Request)
186
+ end
187
+ def register_watched_files(
188
+ id,
189
+ pattern,
190
+ kind: Constant::WatchKind::CREATE | Constant::WatchKind::CHANGE | Constant::WatchKind::DELETE,
191
+ registration_id: nil
192
+ )
193
+ new(
194
+ id: id,
195
+ method: "client/registerCapability",
196
+ params: Interface::RegistrationParams.new(
197
+ registrations: [
198
+ Interface::Registration.new(
199
+ id: registration_id || SecureRandom.uuid,
200
+ method: "workspace/didChangeWatchedFiles",
201
+ register_options: Interface::DidChangeWatchedFilesRegistrationOptions.new(
202
+ watchers: [
203
+ Interface::FileSystemWatcher.new(glob_pattern: pattern, kind: kind),
204
+ ],
205
+ ),
206
+ ),
207
+ ],
208
+ ),
209
+ )
210
+ end
211
+ end
212
+
158
213
  sig { params(id: T.any(Integer, String), method: String, params: Object).void }
159
214
  def initialize(id:, method:, params:)
160
215
  @id = id
@@ -163,7 +218,9 @@ module RubyLsp
163
218
 
164
219
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
165
220
  def to_hash
166
- { id: @id, method: @method, params: T.unsafe(@params).to_hash }
221
+ hash = { id: @id, method: @method }
222
+ hash[:params] = T.unsafe(@params).to_hash if @params
223
+ hash
167
224
  end
168
225
  end
169
226
 
@@ -173,6 +230,9 @@ module RubyLsp
173
230
  sig { returns(String) }
174
231
  attr_reader :message
175
232
 
233
+ sig { returns(Integer) }
234
+ attr_reader :code
235
+
176
236
  sig { params(id: Integer, code: Integer, message: String, data: T.nilable(T::Hash[Symbol, T.untyped])).void }
177
237
  def initialize(id:, code:, message:, data: nil)
178
238
  @id = id
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.1
4
+ version: 0.23.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-11-22 00:00:00.000000000 Z
10
+ date: 2025-02-10 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: language_server-protocol
@@ -94,7 +93,6 @@ files:
94
93
  - exe/ruby-lsp
95
94
  - exe/ruby-lsp-check
96
95
  - exe/ruby-lsp-launcher
97
- - lib/core_ext/uri.rb
98
96
  - lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
99
97
  - lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
100
98
  - lib/ruby-lsp.rb
@@ -103,12 +101,14 @@ files:
103
101
  - lib/ruby_indexer/lib/ruby_indexer/enhancement.rb
104
102
  - lib/ruby_indexer/lib/ruby_indexer/entry.rb
105
103
  - lib/ruby_indexer/lib/ruby_indexer/index.rb
106
- - lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb
107
104
  - lib/ruby_indexer/lib/ruby_indexer/location.rb
108
105
  - lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
109
106
  - lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
110
107
  - lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb
108
+ - lib/ruby_indexer/lib/ruby_indexer/uri.rb
109
+ - lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb
111
110
  - lib/ruby_indexer/ruby_indexer.rb
111
+ - lib/ruby_indexer/test/class_variables_test.rb
112
112
  - lib/ruby_indexer/test/classes_and_modules_test.rb
113
113
  - lib/ruby_indexer/test/configuration_test.rb
114
114
  - lib/ruby_indexer/test/constant_test.rb
@@ -121,6 +121,7 @@ files:
121
121
  - lib/ruby_indexer/test/rbs_indexer_test.rb
122
122
  - lib/ruby_indexer/test/reference_finder_test.rb
123
123
  - lib/ruby_indexer/test/test_case.rb
124
+ - lib/ruby_indexer/test/uri_test.rb
124
125
  - lib/ruby_lsp/addon.rb
125
126
  - lib/ruby_lsp/base_server.rb
126
127
  - lib/ruby_lsp/client_capabilities.rb
@@ -157,6 +158,7 @@ files:
157
158
  - lib/ruby_lsp/requests/hover.rb
158
159
  - lib/ruby_lsp/requests/inlay_hints.rb
159
160
  - lib/ruby_lsp/requests/on_type_formatting.rb
161
+ - lib/ruby_lsp/requests/prepare_rename.rb
160
162
  - lib/ruby_lsp/requests/prepare_type_hierarchy.rb
161
163
  - lib/ruby_lsp/requests/range_formatting.rb
162
164
  - lib/ruby_lsp/requests/references.rb
@@ -202,7 +204,6 @@ licenses:
202
204
  metadata:
203
205
  allowed_push_host: https://rubygems.org
204
206
  documentation_uri: https://shopify.github.io/ruby-lsp/
205
- post_install_message:
206
207
  rdoc_options: []
207
208
  require_paths:
208
209
  - lib
@@ -217,8 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
218
  - !ruby/object:Gem::Version
218
219
  version: '0'
219
220
  requirements: []
220
- rubygems_version: 3.5.23
221
- signing_key:
221
+ rubygems_version: 3.6.3
222
222
  specification_version: 4
223
223
  summary: An opinionated language server for Ruby
224
224
  test_files: []
@@ -1,29 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyIndexer
5
- class IndexablePath
6
- extend T::Sig
7
-
8
- sig { returns(T.nilable(String)) }
9
- attr_reader :require_path
10
-
11
- sig { returns(String) }
12
- attr_reader :full_path
13
-
14
- # An IndexablePath is instantiated with a load_path_entry and a full_path. The load_path_entry is where the file can
15
- # be found in the $LOAD_PATH, which we use to determine the require_path. The load_path_entry may be `nil` if the
16
- # indexer is configured to go through files that do not belong in the $LOAD_PATH. For example,
17
- # `sorbet/tapioca/require.rb` ends up being a part of the paths to be indexed because it's a Ruby file inside the
18
- # project, but the `sorbet` folder is not a part of the $LOAD_PATH. That means that both its load_path_entry and
19
- # require_path will be `nil`, since it cannot be required by the project
20
- sig { params(load_path_entry: T.nilable(String), full_path: String).void }
21
- def initialize(load_path_entry, full_path)
22
- @full_path = full_path
23
- @require_path = T.let(
24
- load_path_entry ? full_path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb") : nil,
25
- T.nilable(String),
26
- )
27
- end
28
- end
29
- end