ruby-lsp 0.22.1 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -9
- data/exe/ruby-lsp-check +5 -5
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +88 -22
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +73 -55
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
- data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
- data/lib/ruby_indexer/ruby_indexer.rb +1 -1
- data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +11 -6
- data/lib/ruby_indexer/test/configuration_test.rb +116 -51
- data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
- data/lib/ruby_indexer/test/index_test.rb +72 -43
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_indexer/test/reference_finder_test.rb +1 -1
- data/lib/ruby_indexer/test/test_case.rb +2 -2
- data/lib/ruby_indexer/test/uri_test.rb +72 -0
- data/lib/ruby_lsp/addon.rb +9 -0
- data/lib/ruby_lsp/base_server.rb +15 -6
- data/lib/ruby_lsp/document.rb +10 -1
- data/lib/ruby_lsp/internal.rb +1 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +8 -4
- data/lib/ruby_lsp/listeners/completion.rb +73 -4
- data/lib/ruby_lsp/listeners/definition.rb +73 -17
- data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
- data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/listeners/hover.rb +57 -0
- data/lib/ruby_lsp/requests/completion.rb +6 -0
- data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +6 -0
- data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
- data/lib/ruby_lsp/requests/rename.rb +14 -4
- data/lib/ruby_lsp/requests/support/common.rb +1 -5
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
- data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -2
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +42 -7
- data/lib/ruby_lsp/setup_bundler.rb +31 -41
- data/lib/ruby_lsp/test_helper.rb +45 -11
- data/lib/ruby_lsp/type_inferrer.rb +22 -0
- data/lib/ruby_lsp/utils.rb +3 -0
- metadata +7 -8
- data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
@@ -0,0 +1,51 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# The
|
7
|
+
# [prepare_rename](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename)
|
8
|
+
# # request checks the validity of a rename operation at a given location.
|
9
|
+
class PrepareRename < Request
|
10
|
+
extend T::Sig
|
11
|
+
include Support::Common
|
12
|
+
|
13
|
+
sig do
|
14
|
+
params(
|
15
|
+
document: RubyDocument,
|
16
|
+
position: T::Hash[Symbol, T.untyped],
|
17
|
+
).void
|
18
|
+
end
|
19
|
+
def initialize(document, position)
|
20
|
+
super()
|
21
|
+
@document = document
|
22
|
+
@position = T.let(position, T::Hash[Symbol, Integer])
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { override.returns(T.nilable(Interface::Range)) }
|
26
|
+
def perform
|
27
|
+
char_position = @document.create_scanner.find_char_position(@position)
|
28
|
+
|
29
|
+
node_context = RubyDocument.locate(
|
30
|
+
@document.parse_result.value,
|
31
|
+
char_position,
|
32
|
+
node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
|
33
|
+
code_units_cache: @document.code_units_cache,
|
34
|
+
)
|
35
|
+
target = node_context.node
|
36
|
+
parent = node_context.parent
|
37
|
+
return if !target || target.is_a?(Prism::ProgramNode)
|
38
|
+
|
39
|
+
if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
|
40
|
+
target = determine_target(
|
41
|
+
target,
|
42
|
+
parent,
|
43
|
+
@position,
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
range_from_location(target.location)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -66,7 +66,7 @@ module RubyLsp
|
|
66
66
|
Interface::TypeHierarchyItem.new(
|
67
67
|
name: first_entry.name,
|
68
68
|
kind: kind_for_entry(first_entry),
|
69
|
-
uri:
|
69
|
+
uri: first_entry.uri.to_s,
|
70
70
|
range: range,
|
71
71
|
selection_range: range,
|
72
72
|
),
|
@@ -12,6 +12,15 @@ module RubyLsp
|
|
12
12
|
|
13
13
|
class InvalidNameError < StandardError; end
|
14
14
|
|
15
|
+
class << self
|
16
|
+
extend T::Sig
|
17
|
+
|
18
|
+
sig { returns(Interface::RenameOptions) }
|
19
|
+
def provider
|
20
|
+
Interface::RenameOptions.new(prepare_provider: true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
15
24
|
sig do
|
16
25
|
params(
|
17
26
|
global_state: GlobalState,
|
@@ -106,7 +115,9 @@ module RubyLsp
|
|
106
115
|
|
107
116
|
T.must(@global_state.index[fully_qualified_name]).each do |entry|
|
108
117
|
# Do not rename files that are not part of the workspace
|
109
|
-
|
118
|
+
uri = entry.uri
|
119
|
+
file_path = T.must(uri.full_path)
|
120
|
+
next unless file_path.start_with?(@global_state.workspace_path)
|
110
121
|
|
111
122
|
case entry
|
112
123
|
when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
|
@@ -117,13 +128,12 @@ module RubyLsp
|
|
117
128
|
if "#{file_name}.rb" == entry.file_name
|
118
129
|
new_file_name = file_from_constant_name(T.must(@new_name.split("::").last))
|
119
130
|
|
120
|
-
old_uri = URI::Generic.from_path(path: entry.file_path).to_s
|
121
131
|
new_uri = URI::Generic.from_path(path: File.join(
|
122
|
-
File.dirname(
|
132
|
+
File.dirname(file_path),
|
123
133
|
"#{new_file_name}.rb",
|
124
134
|
)).to_s
|
125
135
|
|
126
|
-
document_changes << Interface::RenameFile.new(kind: "rename", old_uri:
|
136
|
+
document_changes << Interface::RenameFile.new(kind: "rename", old_uri: uri.to_s, new_uri: new_uri)
|
127
137
|
end
|
128
138
|
end
|
129
139
|
end
|
@@ -93,11 +93,7 @@ module RubyLsp
|
|
93
93
|
# based, which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to
|
94
94
|
# columns. The format for VS Code file URIs is
|
95
95
|
# `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
|
96
|
-
uri =
|
97
|
-
path: entry.file_path,
|
98
|
-
fragment: "L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}",
|
99
|
-
)
|
100
|
-
|
96
|
+
uri = "#{entry.uri}#L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}"
|
101
97
|
definitions << "[#{entry.file_name}](#{uri})"
|
102
98
|
content << "\n\n#{entry.comments}" unless entry.comments.empty?
|
103
99
|
end
|
@@ -65,7 +65,7 @@ module RubyLsp
|
|
65
65
|
Interface::TypeHierarchyItem.new(
|
66
66
|
name: entry.name,
|
67
67
|
kind: kind_for_entry(entry),
|
68
|
-
uri:
|
68
|
+
uri: entry.uri.to_s,
|
69
69
|
range: range_from_location(entry.location),
|
70
70
|
selection_range: range_from_location(entry.name_location),
|
71
71
|
detail: entry.file_name,
|
@@ -21,7 +21,8 @@ module RubyLsp
|
|
21
21
|
sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
|
22
22
|
def perform
|
23
23
|
@index.fuzzy_search(@query).filter_map do |entry|
|
24
|
-
|
24
|
+
uri = entry.uri
|
25
|
+
file_path = T.must(uri.full_path)
|
25
26
|
|
26
27
|
# We only show symbols declared in the workspace
|
27
28
|
in_dependencies = !not_in_dependencies?(file_path)
|
@@ -43,7 +44,7 @@ module RubyLsp
|
|
43
44
|
container_name: container.join("::"),
|
44
45
|
kind: kind,
|
45
46
|
location: Interface::Location.new(
|
46
|
-
uri:
|
47
|
+
uri: uri.to_s,
|
47
48
|
range: Interface::Range.new(
|
48
49
|
start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column),
|
49
50
|
end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
|
@@ -5,7 +5,7 @@ def compose(raw_initialize)
|
|
5
5
|
require_relative "../setup_bundler"
|
6
6
|
require "json"
|
7
7
|
require "uri"
|
8
|
-
|
8
|
+
require "ruby_indexer/lib/ruby_indexer/uri"
|
9
9
|
|
10
10
|
initialize_request = JSON.parse(raw_initialize, symbolize_names: true)
|
11
11
|
workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri)
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -71,6 +71,8 @@ module RubyLsp
|
|
71
71
|
text_document_prepare_type_hierarchy(message)
|
72
72
|
when "textDocument/rename"
|
73
73
|
text_document_rename(message)
|
74
|
+
when "textDocument/prepareRename"
|
75
|
+
text_document_prepare_rename(message)
|
74
76
|
when "textDocument/references"
|
75
77
|
text_document_references(message)
|
76
78
|
when "typeHierarchy/supertypes"
|
@@ -117,12 +119,24 @@ module RubyLsp
|
|
117
119
|
# If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
|
118
120
|
# from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
|
119
121
|
# reporting these to our telemetry
|
120
|
-
|
122
|
+
case e
|
123
|
+
when Store::NonExistingDocumentError
|
121
124
|
send_message(Error.new(
|
122
125
|
id: message[:id],
|
123
126
|
code: Constant::ErrorCodes::INVALID_PARAMS,
|
124
127
|
message: e.full_message,
|
125
128
|
))
|
129
|
+
when Document::LocationNotFoundError
|
130
|
+
send_message(Error.new(
|
131
|
+
id: message[:id],
|
132
|
+
code: Constant::ErrorCodes::REQUEST_FAILED,
|
133
|
+
message: <<~MESSAGE,
|
134
|
+
Request #{message[:method]} failed to find the target position.
|
135
|
+
The file might have been modified while the server was in the middle of searching for the target.
|
136
|
+
If you experience this regularly, please report any findings and extra information on
|
137
|
+
https://github.com/Shopify/ruby-lsp/issues/2446
|
138
|
+
MESSAGE
|
139
|
+
))
|
126
140
|
else
|
127
141
|
send_message(Error.new(
|
128
142
|
id: message[:id],
|
@@ -237,6 +251,7 @@ module RubyLsp
|
|
237
251
|
completion_provider = Requests::Completion.provider if enabled_features["completion"]
|
238
252
|
signature_help_provider = Requests::SignatureHelp.provider if enabled_features["signatureHelp"]
|
239
253
|
type_hierarchy_provider = Requests::PrepareTypeHierarchy.provider if enabled_features["typeHierarchy"]
|
254
|
+
rename_provider = Requests::Rename.provider unless @global_state.has_type_checker
|
240
255
|
|
241
256
|
response = {
|
242
257
|
capabilities: Interface::ServerCapabilities.new(
|
@@ -263,7 +278,7 @@ module RubyLsp
|
|
263
278
|
workspace_symbol_provider: enabled_features["workspaceSymbol"] && !@global_state.has_type_checker,
|
264
279
|
signature_help_provider: signature_help_provider,
|
265
280
|
type_hierarchy_provider: type_hierarchy_provider,
|
266
|
-
rename_provider:
|
281
|
+
rename_provider: rename_provider,
|
267
282
|
references_provider: !@global_state.has_type_checker,
|
268
283
|
document_range_formatting_provider: true,
|
269
284
|
experimental: {
|
@@ -727,6 +742,24 @@ module RubyLsp
|
|
727
742
|
send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
|
728
743
|
end
|
729
744
|
|
745
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
746
|
+
def text_document_prepare_rename(message)
|
747
|
+
params = message[:params]
|
748
|
+
document = @store.get(params.dig(:textDocument, :uri))
|
749
|
+
|
750
|
+
unless document.is_a?(RubyDocument)
|
751
|
+
send_empty_response(message[:id])
|
752
|
+
return
|
753
|
+
end
|
754
|
+
|
755
|
+
send_message(
|
756
|
+
Result.new(
|
757
|
+
id: message[:id],
|
758
|
+
response: Requests::PrepareRename.new(document, params[:position]).perform,
|
759
|
+
),
|
760
|
+
)
|
761
|
+
end
|
762
|
+
|
730
763
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
731
764
|
def text_document_references(message)
|
732
765
|
params = message[:params]
|
@@ -981,15 +1014,17 @@ module RubyLsp
|
|
981
1014
|
next unless file_path.end_with?(".rb")
|
982
1015
|
|
983
1016
|
load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
|
984
|
-
|
1017
|
+
uri.add_require_path_from_load_entry(load_path_entry) if load_path_entry
|
1018
|
+
|
1019
|
+
content = File.read(file_path)
|
985
1020
|
|
986
1021
|
case change[:type]
|
987
1022
|
when Constant::FileChangeType::CREATED
|
988
|
-
index.index_single(
|
1023
|
+
index.index_single(uri, content)
|
989
1024
|
when Constant::FileChangeType::CHANGED
|
990
|
-
index.handle_change(
|
1025
|
+
index.handle_change(uri, content)
|
991
1026
|
when Constant::FileChangeType::DELETED
|
992
|
-
index.delete(
|
1027
|
+
index.delete(uri)
|
993
1028
|
end
|
994
1029
|
end
|
995
1030
|
|
@@ -1088,7 +1123,7 @@ module RubyLsp
|
|
1088
1123
|
|
1089
1124
|
sig { override.void }
|
1090
1125
|
def shutdown
|
1091
|
-
Addon.
|
1126
|
+
Addon.unload_addons
|
1092
1127
|
end
|
1093
1128
|
|
1094
1129
|
sig { void }
|
@@ -152,7 +152,13 @@ module RubyLsp
|
|
152
152
|
end
|
153
153
|
|
154
154
|
unless @dependencies["debug"]
|
155
|
-
|
155
|
+
# The `mri` platform excludes Windows. We want to install the debug gem only on MRI for any operating system,
|
156
|
+
# but that constraint doesn't yet exist in Bundler. On Windows, we are manually checking if the engine is MRI
|
157
|
+
parts << if Gem.win_platform?
|
158
|
+
'gem "debug", require: false, group: :development, install_if: -> { RUBY_ENGINE == "ruby" }'
|
159
|
+
else
|
160
|
+
'gem "debug", require: false, group: :development, platforms: :mri'
|
161
|
+
end
|
156
162
|
end
|
157
163
|
|
158
164
|
if @rails_app && !@dependencies["ruby-lsp-rails"]
|
@@ -196,15 +202,15 @@ module RubyLsp
|
|
196
202
|
env["BUNDLE_PATH"] = File.expand_path(env["BUNDLE_PATH"], @project_path)
|
197
203
|
end
|
198
204
|
|
199
|
-
|
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
|
205
|
+
# Set the specific Bundler version used by the main app. This avoids issues with Bundler restarts, which clean the
|
206
|
+
# environment and lead to the `ruby-lsp` executable not being found
|
203
207
|
if @bundler_version
|
204
208
|
env["BUNDLER_VERSION"] = @bundler_version.to_s
|
205
209
|
install_bundler_if_needed
|
206
210
|
end
|
207
211
|
|
212
|
+
return run_bundle_install_through_command(env) unless @launcher
|
213
|
+
|
208
214
|
begin
|
209
215
|
run_bundle_install_directly(env)
|
210
216
|
# If no error occurred, then clear previous errors
|
@@ -235,12 +241,16 @@ module RubyLsp
|
|
235
241
|
env
|
236
242
|
end
|
237
243
|
|
238
|
-
sig { params(env: T::Hash[String, String]).returns(T::Hash[String, String]) }
|
239
|
-
def run_bundle_install_directly(env)
|
244
|
+
sig { params(env: T::Hash[String, String], force_install: T::Boolean).returns(T::Hash[String, String]) }
|
245
|
+
def run_bundle_install_directly(env, force_install: false)
|
240
246
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
247
|
+
|
248
|
+
# The ENV can only be merged after checking if an update is required because we depend on the original value of
|
249
|
+
# ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
|
250
|
+
should_update = should_bundle_update?
|
241
251
|
T.unsafe(ENV).merge!(env)
|
242
252
|
|
243
|
-
unless
|
253
|
+
unless should_update && !force_install
|
244
254
|
Bundler::CLI::Install.new({}).run
|
245
255
|
correct_relative_remote_paths if @custom_lockfile.exist?
|
246
256
|
return env
|
@@ -255,12 +265,13 @@ module RubyLsp
|
|
255
265
|
correct_relative_remote_paths if @custom_lockfile.exist?
|
256
266
|
@last_updated_path.write(Time.now.iso8601)
|
257
267
|
env
|
268
|
+
rescue Bundler::GemNotFound, Bundler::GitError
|
269
|
+
# If a gem is not installed, skip the upgrade and try to install it with a single retry
|
270
|
+
@retry ? env : run_bundle_install_directly(env, force_install: true)
|
258
271
|
end
|
259
272
|
|
260
273
|
sig { params(env: T::Hash[String, String]).returns(T::Hash[String, String]) }
|
261
274
|
def run_bundle_install_through_command(env)
|
262
|
-
base_bundle = base_bundle_command(env)
|
263
|
-
|
264
275
|
# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
|
265
276
|
# to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
|
266
277
|
# 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 +280,20 @@ module RubyLsp
|
|
269
280
|
|
270
281
|
# When not updating, we run `(bundle check || bundle install)`
|
271
282
|
# When updating, we run `((bundle check && bundle update ruby-lsp debug) || bundle install)`
|
272
|
-
|
283
|
+
bundler_path = File.join(Gem.default_bindir, "bundle")
|
284
|
+
base_command = (File.exist?(bundler_path) ? "#{Gem.ruby} #{bundler_path}" : "bundle").dup
|
285
|
+
|
286
|
+
if env["BUNDLER_VERSION"]
|
287
|
+
base_command << " _#{env["BUNDLER_VERSION"]}_"
|
288
|
+
end
|
289
|
+
|
290
|
+
command = +"(#{base_command} check"
|
273
291
|
|
274
292
|
if should_bundle_update?
|
275
293
|
# If any of `ruby-lsp`, `ruby-lsp-rails` or `debug` are not in the Gemfile, try to update them to the latest
|
276
294
|
# version
|
277
295
|
command.prepend("(")
|
278
|
-
command << " && #{
|
296
|
+
command << " && #{base_command} update "
|
279
297
|
command << "ruby-lsp " unless @dependencies["ruby-lsp"]
|
280
298
|
command << "debug " unless @dependencies["debug"]
|
281
299
|
command << "ruby-lsp-rails " if @rails_app && !@dependencies["ruby-lsp-rails"]
|
@@ -285,7 +303,7 @@ module RubyLsp
|
|
285
303
|
@last_updated_path.write(Time.now.iso8601)
|
286
304
|
end
|
287
305
|
|
288
|
-
command << " || #{
|
306
|
+
command << " || #{base_command} install) "
|
289
307
|
|
290
308
|
# Redirect stdout to stderr to prevent going into an infinite loop. The extension might confuse stdout output with
|
291
309
|
# responses
|
@@ -395,34 +413,6 @@ module RubyLsp
|
|
395
413
|
/class .* < (::)?Rails::Application/.match?(application_contents)
|
396
414
|
end
|
397
415
|
|
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
416
|
sig { void }
|
427
417
|
def patch_thor_to_print_progress_to_stderr!
|
428
418
|
return unless defined?(Bundler::Thor::Shell::Basic)
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
|
-
# typed:
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
|
@@ -143,6 +146,25 @@ module RubyLsp
|
|
143
146
|
nil
|
144
147
|
end
|
145
148
|
|
149
|
+
sig { params(node_context: NodeContext).returns(T.nilable(Type)) }
|
150
|
+
def infer_receiver_for_class_variables(node_context)
|
151
|
+
nesting_parts = node_context.nesting.dup
|
152
|
+
|
153
|
+
return Type.new("Object") if nesting_parts.empty?
|
154
|
+
|
155
|
+
nesting_parts.reverse_each do |part|
|
156
|
+
break unless part.include?("<Class:")
|
157
|
+
|
158
|
+
nesting_parts.pop
|
159
|
+
end
|
160
|
+
|
161
|
+
receiver_name = nesting_parts.join("::")
|
162
|
+
resolved_receiver = @index.resolve(receiver_name, node_context.nesting)&.first
|
163
|
+
return unless resolved_receiver&.name
|
164
|
+
|
165
|
+
Type.new(resolved_receiver.name)
|
166
|
+
end
|
167
|
+
|
146
168
|
# A known type
|
147
169
|
class Type
|
148
170
|
extend T::Sig
|
data/lib/ruby_lsp/utils.rb
CHANGED
@@ -173,6 +173,9 @@ module RubyLsp
|
|
173
173
|
sig { returns(String) }
|
174
174
|
attr_reader :message
|
175
175
|
|
176
|
+
sig { returns(Integer) }
|
177
|
+
attr_reader :code
|
178
|
+
|
176
179
|
sig { params(id: Integer, code: Integer, message: String, data: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
177
180
|
def initialize(id:, code:, message:, data: nil)
|
178
181
|
@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.
|
4
|
+
version: 0.23.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-06 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,13 @@ 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
|
111
109
|
- lib/ruby_indexer/ruby_indexer.rb
|
110
|
+
- lib/ruby_indexer/test/class_variables_test.rb
|
112
111
|
- lib/ruby_indexer/test/classes_and_modules_test.rb
|
113
112
|
- lib/ruby_indexer/test/configuration_test.rb
|
114
113
|
- lib/ruby_indexer/test/constant_test.rb
|
@@ -121,6 +120,7 @@ files:
|
|
121
120
|
- lib/ruby_indexer/test/rbs_indexer_test.rb
|
122
121
|
- lib/ruby_indexer/test/reference_finder_test.rb
|
123
122
|
- lib/ruby_indexer/test/test_case.rb
|
123
|
+
- lib/ruby_indexer/test/uri_test.rb
|
124
124
|
- lib/ruby_lsp/addon.rb
|
125
125
|
- lib/ruby_lsp/base_server.rb
|
126
126
|
- lib/ruby_lsp/client_capabilities.rb
|
@@ -157,6 +157,7 @@ files:
|
|
157
157
|
- lib/ruby_lsp/requests/hover.rb
|
158
158
|
- lib/ruby_lsp/requests/inlay_hints.rb
|
159
159
|
- lib/ruby_lsp/requests/on_type_formatting.rb
|
160
|
+
- lib/ruby_lsp/requests/prepare_rename.rb
|
160
161
|
- lib/ruby_lsp/requests/prepare_type_hierarchy.rb
|
161
162
|
- lib/ruby_lsp/requests/range_formatting.rb
|
162
163
|
- lib/ruby_lsp/requests/references.rb
|
@@ -202,7 +203,6 @@ licenses:
|
|
202
203
|
metadata:
|
203
204
|
allowed_push_host: https://rubygems.org
|
204
205
|
documentation_uri: https://shopify.github.io/ruby-lsp/
|
205
|
-
post_install_message:
|
206
206
|
rdoc_options: []
|
207
207
|
require_paths:
|
208
208
|
- lib
|
@@ -217,8 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
217
217
|
- !ruby/object:Gem::Version
|
218
218
|
version: '0'
|
219
219
|
requirements: []
|
220
|
-
rubygems_version: 3.
|
221
|
-
signing_key:
|
220
|
+
rubygems_version: 3.6.2
|
222
221
|
specification_version: 4
|
223
222
|
summary: An opinionated language server for Ruby
|
224
223
|
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
|