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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efd5671eae595026a17c3114a00de1053865ac828726dd7ac5a66548f6b1ad56
4
- data.tar.gz: ccf5b7af12a6e1e327cc2d458be11f39a9ac7319ab9049863ed5a74e333e8a6f
3
+ metadata.gz: 3a0d78055f2f6bf861e581a5b5a2d1321d3a8452b23d638becb6e64da925d842
4
+ data.tar.gz: '0397aa5a123ed08757606db1be3690b50cba847a372f52da25fb64a6fb5816ce'
5
5
  SHA512:
6
- metadata.gz: 8fbcdfaac508788a17fb3b7e939ce290e33049a0080ff6260abfaeb609ee76a033234ccc2efa5724587e9c4cbf4552e2cd5d2cb0af1186971b8ed06fec290cb3
7
- data.tar.gz: 3e37acf250618e60df2addbce7f9acf86df4bf3e771b7b49c4cd8f4ad326ea82e3d41ad80c5ce71e3b0429dab98385df51f4645824cd419e32dac5ea13778760
6
+ metadata.gz: a14d101646bd32467724aeedb60892c5c06e775a57d749623cc1e76dbc42ee6d82eca10e2fa1149900365eff59ee57c20300bb043a3fdcf1330961b6bbd94918
7
+ data.tar.gz: 23b339c5854620771e7b505381dad64cf3e8d89ed2850a19c7a299e72d2363202e1e74327c05d27d8fb7fae508abb7141e5a858319b2841c8c914233e28e076e
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![Build Status](https://github.com/Shopify/ruby-lsp/workflows/CI/badge.svg)](https://github.com/Shopify/ruby-lsp/actions/workflows/ci.yml)
6
6
  [![Ruby LSP extension](https://img.shields.io/badge/VS%20Code-Ruby%20LSP-success?logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
7
- [![Ruby DX Slack](https://img.shields.io/badge/Slack-Ruby%20DX-success?logo=slack)](https://join.slack.com/t/ruby-dx/shared_invite/zt-2c8zjlir6-uUDJl8oIwcen_FS_aA~b6Q)
7
+ [![Ruby DX Slack](https://img.shields.io/badge/Slack-Ruby%20DX-success?logo=slack)](https://join.slack.com/t/ruby-dx/shared_invite/zt-2yd77ayis-yAiVc1TX_kH0mHMBbi89dA)
8
8
 
9
9
  # Ruby LSP
10
10
 
@@ -13,7 +13,7 @@ for Ruby, used to improve rich features in editors. It is a part of a wider goal
13
13
  experience to Ruby developers using modern standards for cross-editor features, documentation and debugging.
14
14
 
15
15
  Want to discuss Ruby developer experience? Consider joining the public
16
- [Ruby DX Slack workspace](https://join.slack.com/t/ruby-dx/shared_invite/zt-2c8zjlir6-uUDJl8oIwcen_FS_aA~b6Q).
16
+ [Ruby DX Slack workspace](https://join.slack.com/t/ruby-dx/shared_invite/zt-2yd77ayis-yAiVc1TX_kH0mHMBbi89dA).
17
17
 
18
18
  ## Getting Started
19
19
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.22.1
1
+ 0.23.10
data/exe/ruby-lsp CHANGED
@@ -64,9 +64,9 @@ if ENV["BUNDLE_GEMFILE"].nil?
64
64
  # which gives us the opportunity to control which specs are activated and enter degraded mode if any gems failed to
65
65
  # install rather than failing to boot the server completely
66
66
  if options[:launcher]
67
- command = +File.expand_path("ruby-lsp-launcher", __dir__)
68
- command << " --debug" if options[:debug]
69
- exit exec(command)
67
+ flags = []
68
+ flags << "--debug" if options[:debug]
69
+ exit exec(Gem.ruby, File.expand_path("ruby-lsp-launcher", __dir__), *flags)
70
70
  end
71
71
 
72
72
  require_relative "../lib/ruby_lsp/setup_bundler"
@@ -78,13 +78,14 @@ if ENV["BUNDLE_GEMFILE"].nil?
78
78
  exit(78)
79
79
  end
80
80
 
81
- base_bundle = if env["BUNDLER_VERSION"]
82
- "bundle _#{env["BUNDLER_VERSION"]}_"
83
- else
84
- "bundle"
81
+ bundler_path = File.join(Gem.default_bindir, "bundle")
82
+ base_command = (!Gem.win_platform? && File.exist?(bundler_path) ? "#{Gem.ruby} #{bundler_path}" : "bundle").dup
83
+
84
+ if env["BUNDLER_VERSION"]
85
+ base_command << " _#{env["BUNDLER_VERSION"]}_"
85
86
  end
86
87
 
87
- exit exec(env, "#{base_bundle} exec ruby-lsp #{original_args.join(" ")}".strip)
88
+ exit exec(env, "#{base_command} exec ruby-lsp #{original_args.join(" ")}".strip)
88
89
  end
89
90
 
90
91
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
@@ -139,9 +140,9 @@ if options[:doctor]
139
140
 
140
141
  puts "Globbing for indexable files"
141
142
 
142
- index.configuration.indexables.each do |indexable|
143
- puts "indexing: #{indexable.full_path}"
144
- index.index_single(indexable)
143
+ index.configuration.indexable_uris.each do |uri|
144
+ puts "indexing: #{uri}"
145
+ index.index_file(uri)
145
146
  end
146
147
  return
147
148
  end
data/exe/ruby-lsp-check CHANGED
@@ -44,14 +44,14 @@ puts "\n"
44
44
  puts "Verifying that indexing executes successfully. This may take a while..."
45
45
 
46
46
  index = RubyIndexer::Index.new
47
- indexables = index.configuration.indexables
47
+ uris = index.configuration.indexable_uris
48
48
 
49
- indexables.each_with_index do |indexable, i|
50
- index.index_single(indexable)
49
+ uris.each_with_index do |uri, i|
50
+ index.index_file(uri)
51
51
  rescue => e
52
- errors[indexable.full_path] = e
52
+ errors[uri.full_path] = e
53
53
  ensure
54
- print("\033[M\033[0KIndexed #{i + 1}/#{indexables.length}") unless ENV["CI"]
54
+ print("\033[M\033[0KIndexed #{i + 1}/#{uris.length}") unless ENV["CI"]
55
55
  end
56
56
  puts "\n"
57
57
 
@@ -7,14 +7,25 @@
7
7
  # !!!!!!!
8
8
 
9
9
  setup_error = nil
10
+ install_error = nil
11
+ reboot = false
10
12
 
11
- # Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
12
- # Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
13
- # to ensure that we're setting up the bundle in the right place
14
- $stdin.binmode
15
- headers = $stdin.gets("\r\n\r\n")
16
- content_length = headers[/Content-Length: (\d+)/i, 1].to_i
17
- raw_initialize = $stdin.read(content_length)
13
+ workspace_uri = ARGV.first
14
+
15
+ raw_initialize = if workspace_uri && !workspace_uri.start_with?("--")
16
+ # If there's an argument without `--`, then it's the server asking to compose the bundle and passing to this
17
+ # executable the workspace URI. We can't require gems at this point, so we built a fake initialize request manually
18
+ reboot = true
19
+ "{\"params\":{\"workspaceFolders\":[{\"uri\":\"#{workspace_uri}\"}]}}"
20
+ else
21
+ # Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
22
+ # Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
23
+ # to ensure that we're setting up the bundle in the right place
24
+ $stdin.binmode
25
+ headers = $stdin.gets("\r\n\r\n")
26
+ content_length = headers[/Content-Length: (\d+)/i, 1].to_i
27
+ $stdin.read(content_length)
28
+ end
18
29
 
19
30
  # Compose the Ruby LSP bundle in a forked process so that we can require gems without polluting the main process
20
31
  # `$LOAD_PATH` and `Gem.loaded_specs`. Windows doesn't support forking, so we need a separate path to support it
@@ -40,7 +51,7 @@ end
40
51
 
41
52
  begin
42
53
  # Wait until the composed Bundle is finished
43
- Process.wait(pid)
54
+ _, status = Process.wait2(pid)
44
55
  rescue Errno::ECHILD
45
56
  # In theory, the child process can finish before we even get to the wait call, but that is not an error
46
57
  end
@@ -61,25 +72,40 @@ begin
61
72
 
62
73
  require "bundler"
63
74
  Bundler.ui.level = :silent
75
+
76
+ # This Marshal load can only happen after requiring Bundler because it will load a custom error class from Bundler
77
+ # itself. If we try to load before requiring, the class will not be defined and loading will fail
78
+ error_path = File.join(".ruby-lsp", "install_error")
79
+ install_error = if File.exist?(error_path)
80
+ Marshal.load(File.read(error_path))
81
+ end
82
+
64
83
  Bundler.setup
65
84
  $stderr.puts("Composed Bundle set up successfully")
66
85
  end
67
86
  rescue StandardError => e
68
87
  # If installing gems failed for any reason, we don't want to exit the process prematurely. We can still provide most
69
88
  # features in a degraded mode. We simply save the error so that we can report to the user that certain gems might be
70
- # missing, but we respect the LSP life cycle
71
- setup_error = e
72
- $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
89
+ # missing, but we respect the LSP life cycle.
90
+ #
91
+ # If an install error occurred and one of the gems is not installed, Bundler.setup is guaranteed to fail with
92
+ # `Bundler::GemNotFound`. We don't set `setup_error` here because that would essentially report the same problem twice
93
+ # to telemetry, which is not useful
94
+ unless install_error && e.is_a?(Bundler::GemNotFound)
95
+ setup_error = e
96
+ $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
97
+ end
73
98
 
74
99
  # If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
75
100
  # in degraded mode
76
101
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
77
102
  end
78
103
 
79
- error_path = File.join(".ruby-lsp", "install_error")
80
-
81
- install_error = if File.exist?(error_path)
82
- Marshal.load(File.read(error_path))
104
+ # When performing a lockfile re-boot, this executable is invoked to set up the composed bundle ahead of time. In this
105
+ # flow, we are not booting the LSP yet, just checking if the bundle is valid before rebooting
106
+ if reboot
107
+ # Use the exit status to signal to the server if composing the bundle succeeded
108
+ exit(install_error || setup_error ? 1 : status&.exitstatus || 0)
83
109
  end
84
110
 
85
111
  # Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
@@ -81,8 +81,8 @@ module RubyIndexer
81
81
  .then { |dirs| File.join(@workspace_path, "{#{dirs.join(",")}}/**/*") }
82
82
  end
83
83
 
84
- sig { returns(T::Array[IndexablePath]) }
85
- def indexables
84
+ sig { returns(T::Array[URI::Generic]) }
85
+ def indexable_uris
86
86
  excluded_gems = @excluded_gems - @included_gems
87
87
  locked_gems = Bundler.locked_gems&.specs
88
88
 
@@ -107,12 +107,12 @@ module RubyIndexer
107
107
  [included_path, relative_path]
108
108
  end
109
109
 
110
- indexables = T.let([], T::Array[IndexablePath])
110
+ uris = T.let([], T::Array[URI::Generic])
111
111
 
112
112
  # Handle top level files separately. The path below is an optimization to prevent descending down directories that
113
113
  # are going to be excluded anyway, so we need to handle top level scripts separately
114
114
  Dir.glob(File.join(@workspace_path, "*.rb"), flags).each do |path|
115
- indexables << IndexablePath.new(nil, path)
115
+ uris << URI::Generic.from_path(path: path)
116
116
  end
117
117
 
118
118
  # Add user specified patterns
@@ -134,7 +134,7 @@ module RubyIndexer
134
134
  load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
135
135
  end
136
136
 
137
- indexables << IndexablePath.new(load_path_entry, path)
137
+ uris << URI::Generic.from_path(path: path, load_path_entry: load_path_entry)
138
138
  end
139
139
  end
140
140
  end
@@ -150,9 +150,9 @@ module RubyIndexer
150
150
  end
151
151
 
152
152
  # Remove user specified patterns
153
- indexables.reject! do |indexable|
153
+ uris.reject! do |indexable|
154
154
  excluded_patterns.any? do |pattern|
155
- File.fnmatch?(pattern, indexable.full_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
155
+ File.fnmatch?(pattern, T.must(indexable.full_path), File::FNM_PATHNAME | File::FNM_EXTGLOB)
156
156
  end
157
157
  end
158
158
 
@@ -182,14 +182,14 @@ module RubyIndexer
182
182
 
183
183
  if pathname.directory?
184
184
  # If the default_path is a directory, we index all the Ruby files in it
185
- indexables.concat(
185
+ uris.concat(
186
186
  Dir.glob(File.join(default_path, "**", "*.rb"), File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
187
- IndexablePath.new(RbConfig::CONFIG["rubylibdir"], path)
187
+ URI::Generic.from_path(path: path, load_path_entry: RbConfig::CONFIG["rubylibdir"])
188
188
  end,
189
189
  )
190
190
  elsif pathname.extname == ".rb"
191
191
  # If the default_path is a Ruby file, we index it
192
- indexables << IndexablePath.new(RbConfig::CONFIG["rubylibdir"], default_path)
192
+ uris << URI::Generic.from_path(path: default_path, load_path_entry: RbConfig::CONFIG["rubylibdir"])
193
193
  end
194
194
  end
195
195
 
@@ -204,10 +204,12 @@ module RubyIndexer
204
204
  # duplicates or accidentally ignoring exclude patterns
205
205
  next if spec.full_gem_path == @workspace_path
206
206
 
207
- indexables.concat(
207
+ uris.concat(
208
208
  spec.require_paths.flat_map do |require_path|
209
209
  load_path_entry = File.join(spec.full_gem_path, require_path)
210
- Dir.glob(File.join(load_path_entry, "**", "*.rb")).map! { |path| IndexablePath.new(load_path_entry, path) }
210
+ Dir.glob(File.join(load_path_entry, "**", "*.rb")).map! do |path|
211
+ URI::Generic.from_path(path: path, load_path_entry: load_path_entry)
212
+ end
211
213
  end,
212
214
  )
213
215
  rescue Gem::MissingSpecError
@@ -216,8 +218,8 @@ module RubyIndexer
216
218
  # just ignore if they're missing
217
219
  end
218
220
 
219
- indexables.uniq!(&:full_path)
220
- indexables
221
+ uris.uniq!(&:to_s)
222
+ uris
221
223
  end
222
224
 
223
225
  sig { returns(Regexp) }
@@ -271,6 +273,15 @@ module RubyIndexer
271
273
  end
272
274
 
273
275
  others.concat(this_gem.to_spec.dependencies) if this_gem
276
+ others.concat(
277
+ others.filter_map do |d|
278
+ d.to_spec&.dependencies
279
+ rescue Gem::MissingSpecError
280
+ nil
281
+ end.flatten,
282
+ )
283
+ others.uniq!
284
+ others.map!(&:name)
274
285
 
275
286
  excluded.each do |dependency|
276
287
  next unless dependency.runtime?
@@ -279,12 +290,7 @@ module RubyIndexer
279
290
  next unless spec
280
291
 
281
292
  spec.dependencies.each do |transitive_dependency|
282
- # If the transitive dependency is included in other groups, skip it
283
- next if others.any? { |d| d.name == transitive_dependency.name }
284
-
285
- # If the transitive dependency is included as a transitive dependency of a gem outside of the development
286
- # group, skip it
287
- next if others.any? { |d| d.to_spec&.dependencies&.include?(transitive_dependency) }
293
+ next if others.include?(transitive_dependency.name)
288
294
 
289
295
  excluded << transitive_dependency
290
296
  end