ruby-lsp 0.23.10 → 0.23.12
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp-launcher +12 -11
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +52 -77
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +61 -144
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +8 -6
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +74 -183
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +55 -181
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +12 -14
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +21 -44
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +40 -58
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +9 -16
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +75 -0
- data/lib/ruby_indexer/test/configuration_test.rb +32 -2
- data/lib/ruby_indexer/test/index_test.rb +21 -0
- data/lib/ruby_indexer/test/method_test.rb +2 -2
- data/lib/ruby_lsp/addon.rb +32 -67
- data/lib/ruby_lsp/base_server.rb +12 -11
- data/lib/ruby_lsp/client_capabilities.rb +4 -6
- data/lib/ruby_lsp/document.rb +21 -32
- data/lib/ruby_lsp/erb_document.rb +17 -27
- data/lib/ruby_lsp/global_state.rb +30 -32
- data/lib/ruby_lsp/internal.rb +6 -0
- data/lib/ruby_lsp/listeners/code_lens.rb +21 -39
- data/lib/ruby_lsp/listeners/completion.rb +34 -53
- data/lib/ruby_lsp/listeners/definition.rb +35 -49
- data/lib/ruby_lsp/listeners/document_highlight.rb +60 -69
- data/lib/ruby_lsp/listeners/document_link.rb +9 -19
- data/lib/ruby_lsp/listeners/document_symbol.rb +34 -48
- data/lib/ruby_lsp/listeners/folding_ranges.rb +31 -38
- data/lib/ruby_lsp/listeners/hover.rb +37 -47
- data/lib/ruby_lsp/listeners/inlay_hints.rb +3 -10
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +29 -35
- data/lib/ruby_lsp/listeners/signature_help.rb +4 -23
- data/lib/ruby_lsp/listeners/spec_style.rb +199 -0
- data/lib/ruby_lsp/listeners/test_style.rb +270 -0
- data/lib/ruby_lsp/node_context.rb +8 -35
- data/lib/ruby_lsp/rbs_document.rb +7 -5
- data/lib/ruby_lsp/requests/code_action_resolve.rb +10 -10
- data/lib/ruby_lsp/requests/code_actions.rb +5 -14
- data/lib/ruby_lsp/requests/code_lens.rb +4 -13
- data/lib/ruby_lsp/requests/completion.rb +4 -15
- data/lib/ruby_lsp/requests/completion_resolve.rb +4 -4
- data/lib/ruby_lsp/requests/definition.rb +4 -12
- data/lib/ruby_lsp/requests/diagnostics.rb +6 -9
- data/lib/ruby_lsp/requests/discover_tests.rb +74 -0
- data/lib/ruby_lsp/requests/document_highlight.rb +3 -11
- data/lib/ruby_lsp/requests/document_link.rb +4 -13
- data/lib/ruby_lsp/requests/document_symbol.rb +4 -7
- data/lib/ruby_lsp/requests/folding_ranges.rb +4 -7
- data/lib/ruby_lsp/requests/formatting.rb +4 -7
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
- data/lib/ruby_lsp/requests/hover.rb +6 -16
- data/lib/ruby_lsp/requests/inlay_hints.rb +4 -13
- data/lib/ruby_lsp/requests/on_type_formatting.rb +17 -24
- data/lib/ruby_lsp/requests/prepare_rename.rb +3 -8
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +4 -13
- data/lib/ruby_lsp/requests/range_formatting.rb +3 -4
- data/lib/ruby_lsp/requests/references.rb +5 -35
- data/lib/ruby_lsp/requests/rename.rb +9 -35
- data/lib/ruby_lsp/requests/request.rb +5 -17
- data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +6 -23
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -5
- data/lib/ruby_lsp/requests/signature_help.rb +6 -24
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +12 -47
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -14
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +7 -10
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -15
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +1 -7
- data/lib/ruby_lsp/requests/support/source_uri.rb +5 -16
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +7 -10
- data/lib/ruby_lsp/requests/support/test_item.rb +60 -0
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +4 -5
- data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -3
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +4 -4
- data/lib/ruby_lsp/response_builders/document_symbol.rb +8 -11
- data/lib/ruby_lsp/response_builders/hover.rb +5 -5
- data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +18 -40
- data/lib/ruby_lsp/response_builders/signature_help.rb +4 -5
- data/lib/ruby_lsp/response_builders/test_collection.rb +34 -0
- data/lib/ruby_lsp/ruby_document.rb +15 -40
- data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +106 -0
- data/lib/ruby_lsp/scope.rb +6 -10
- data/lib/ruby_lsp/server.rb +169 -72
- data/lib/ruby_lsp/setup_bundler.rb +25 -17
- data/lib/ruby_lsp/store.rb +12 -28
- data/lib/ruby_lsp/test_helper.rb +3 -12
- data/lib/ruby_lsp/test_reporter.rb +71 -0
- data/lib/ruby_lsp/test_unit_test_runner.rb +96 -0
- data/lib/ruby_lsp/type_inferrer.rb +9 -13
- data/lib/ruby_lsp/utils.rb +27 -65
- metadata +12 -3
@@ -27,7 +27,7 @@ module RubyLsp
|
|
27
27
|
|
28
28
|
FOUR_HOURS = T.let(4 * 60 * 60, Integer)
|
29
29
|
|
30
|
-
|
30
|
+
#: (String project_path, **untyped options) -> void
|
31
31
|
def initialize(project_path, **options)
|
32
32
|
@project_path = project_path
|
33
33
|
@branch = T.let(options[:branch], T.nilable(String))
|
@@ -68,7 +68,7 @@ module RubyLsp
|
|
68
68
|
|
69
69
|
# Sets up the composed bundle and returns the `BUNDLE_GEMFILE`, `BUNDLE_PATH` and `BUNDLE_APP_CONFIG` that should be
|
70
70
|
# used for running the server
|
71
|
-
|
71
|
+
#: -> Hash[String, String]
|
72
72
|
def setup!
|
73
73
|
raise BundleNotLocked if !@launcher && @gemfile&.exist? && !@lockfile&.exist?
|
74
74
|
|
@@ -128,7 +128,7 @@ module RubyLsp
|
|
128
128
|
|
129
129
|
private
|
130
130
|
|
131
|
-
|
131
|
+
#: -> Hash[String, untyped]
|
132
132
|
def composed_bundle_dependencies
|
133
133
|
@composed_bundle_dependencies ||= T.let(
|
134
134
|
begin
|
@@ -147,7 +147,7 @@ module RubyLsp
|
|
147
147
|
)
|
148
148
|
end
|
149
149
|
|
150
|
-
|
150
|
+
#: -> void
|
151
151
|
def write_custom_gemfile
|
152
152
|
parts = [
|
153
153
|
"# This custom gemfile is automatically generated by the Ruby LSP.",
|
@@ -158,7 +158,8 @@ module RubyLsp
|
|
158
158
|
# If there's a top level Gemfile, we want to evaluate from the composed bundle. We get the source from the top
|
159
159
|
# level Gemfile, so if there isn't one we need to add a default source
|
160
160
|
if @gemfile&.exist? && @lockfile&.exist?
|
161
|
-
|
161
|
+
gemfile_path = @gemfile.relative_path_from(@custom_dir.realpath)
|
162
|
+
parts << "eval_gemfile(File.expand_path(\"#{gemfile_path}\", __dir__))"
|
162
163
|
else
|
163
164
|
parts.unshift('source "https://rubygems.org"')
|
164
165
|
end
|
@@ -187,7 +188,7 @@ module RubyLsp
|
|
187
188
|
@custom_gemfile.write(content) unless @custom_gemfile.exist? && @custom_gemfile.read == content
|
188
189
|
end
|
189
190
|
|
190
|
-
|
191
|
+
#: -> [Hash[String, untyped], Gem::Version?]
|
191
192
|
def load_dependencies
|
192
193
|
return [{}, nil] unless @lockfile&.exist?
|
193
194
|
|
@@ -207,7 +208,7 @@ module RubyLsp
|
|
207
208
|
[dependencies, lockfile_parser.bundler_version]
|
208
209
|
end
|
209
210
|
|
210
|
-
|
211
|
+
#: (?Pathname? bundle_gemfile) -> Hash[String, String]
|
211
212
|
def run_bundle_install(bundle_gemfile = @gemfile)
|
212
213
|
env = bundler_settings_as_env
|
213
214
|
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
|
@@ -258,7 +259,7 @@ module RubyLsp
|
|
258
259
|
env
|
259
260
|
end
|
260
261
|
|
261
|
-
|
262
|
+
#: (Hash[String, String] env, ?force_install: bool) -> Hash[String, String]
|
262
263
|
def run_bundle_install_directly(env, force_install: false)
|
263
264
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
264
265
|
|
@@ -287,7 +288,7 @@ module RubyLsp
|
|
287
288
|
@retry ? env : run_bundle_install_directly(env, force_install: true)
|
288
289
|
end
|
289
290
|
|
290
|
-
|
291
|
+
#: (Hash[String, String] env) -> Hash[String, String]
|
291
292
|
def run_bundle_install_through_command(env)
|
292
293
|
# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
|
293
294
|
# to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
|
@@ -344,7 +345,7 @@ module RubyLsp
|
|
344
345
|
end
|
345
346
|
|
346
347
|
# Gather all Bundler settings (global and local) and return them as a hash that can be used as the environment
|
347
|
-
|
348
|
+
#: -> Hash[String, String]
|
348
349
|
def bundler_settings_as_env
|
349
350
|
local_config_path = File.join(@project_path, ".bundle")
|
350
351
|
|
@@ -356,18 +357,25 @@ module RubyLsp
|
|
356
357
|
Bundler::Settings.new
|
357
358
|
end
|
358
359
|
|
360
|
+
# List of Bundler settings that don't make sense for the composed bundle and are better controlled manually by the
|
361
|
+
# user
|
362
|
+
ignored_settings = ["bin", "cache_all", "cache_all_platforms"]
|
363
|
+
|
359
364
|
# Map all settings to their environment variable names with `key_for` and their values. For example, the if the
|
360
365
|
# setting name `e` is `path` with a value of `vendor/bundle`, then it will return `"BUNDLE_PATH" =>
|
361
366
|
# "vendor/bundle"`
|
362
|
-
settings
|
363
|
-
|
367
|
+
settings
|
368
|
+
.all
|
369
|
+
.reject { |setting| ignored_settings.include?(setting) }
|
370
|
+
.to_h do |e|
|
371
|
+
key = settings.key_for(e)
|
364
372
|
value = Array(settings[e]).join(":").tr(" ", ":")
|
365
373
|
|
366
374
|
[key, value]
|
367
375
|
end
|
368
376
|
end
|
369
377
|
|
370
|
-
|
378
|
+
#: -> void
|
371
379
|
def install_bundler_if_needed
|
372
380
|
# Try to find the bundler version specified in the lockfile in installed gems. If not found, install it
|
373
381
|
requirement = Gem::Requirement.new(@bundler_version.to_s)
|
@@ -376,7 +384,7 @@ module RubyLsp
|
|
376
384
|
Gem.install("bundler", @bundler_version.to_s)
|
377
385
|
end
|
378
386
|
|
379
|
-
|
387
|
+
#: -> bool
|
380
388
|
def should_bundle_update?
|
381
389
|
# If `ruby-lsp`, `ruby-lsp-rails` and `debug` are in the Gemfile, then we shouldn't try to upgrade them or else it
|
382
390
|
# will produce version control changes
|
@@ -399,7 +407,7 @@ module RubyLsp
|
|
399
407
|
|
400
408
|
# When a lockfile has remote references based on relative file paths, we need to ensure that they are pointing to
|
401
409
|
# the correct place since after copying the relative path is no longer valid
|
402
|
-
|
410
|
+
#: -> void
|
403
411
|
def correct_relative_remote_paths
|
404
412
|
content = @custom_lockfile.read
|
405
413
|
content.gsub!(/remote: (.*)/) do |match|
|
@@ -421,7 +429,7 @@ module RubyLsp
|
|
421
429
|
end
|
422
430
|
|
423
431
|
# Detects if the project is a Rails app by looking if the superclass of the main class is `Rails::Application`
|
424
|
-
|
432
|
+
#: -> bool
|
425
433
|
def rails_app?
|
426
434
|
config = Pathname.new("config/application.rb").expand_path
|
427
435
|
application_contents = config.read(external_encoding: Encoding::UTF_8) if config.exist?
|
@@ -430,7 +438,7 @@ module RubyLsp
|
|
430
438
|
/class .* < (::)?Rails::Application/.match?(application_contents)
|
431
439
|
end
|
432
440
|
|
433
|
-
|
441
|
+
#: -> void
|
434
442
|
def patch_thor_to_print_progress_to_stderr!
|
435
443
|
return unless defined?(Bundler::Thor::Shell::Basic)
|
436
444
|
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -3,17 +3,15 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
class Store
|
6
|
-
extend T::Sig
|
7
|
-
|
8
6
|
class NonExistingDocumentError < StandardError; end
|
9
7
|
|
10
|
-
|
8
|
+
#: Hash[Symbol, RequestConfig]
|
11
9
|
attr_accessor :features_configuration
|
12
10
|
|
13
|
-
|
11
|
+
#: String
|
14
12
|
attr_accessor :client_name
|
15
13
|
|
16
|
-
|
14
|
+
#: (GlobalState global_state) -> void
|
17
15
|
def initialize(global_state)
|
18
16
|
@global_state = global_state
|
19
17
|
@state = T.let({}, T::Hash[String, Document[T.untyped]])
|
@@ -30,7 +28,7 @@ module RubyLsp
|
|
30
28
|
@client_name = T.let("Unknown", String)
|
31
29
|
end
|
32
30
|
|
33
|
-
|
31
|
+
#: (URI::Generic uri) -> Document[untyped]
|
34
32
|
def get(uri)
|
35
33
|
document = @state[uri.to_s]
|
36
34
|
return document unless document.nil?
|
@@ -56,14 +54,7 @@ module RubyLsp
|
|
56
54
|
raise NonExistingDocumentError, uri.to_s
|
57
55
|
end
|
58
56
|
|
59
|
-
|
60
|
-
params(
|
61
|
-
uri: URI::Generic,
|
62
|
-
source: String,
|
63
|
-
version: Integer,
|
64
|
-
language_id: Document::LanguageId,
|
65
|
-
).returns(Document[T.untyped])
|
66
|
-
end
|
57
|
+
#: (uri: URI::Generic, source: String, version: Integer, language_id: Document::LanguageId) -> Document[untyped]
|
67
58
|
def set(uri:, source:, version:, language_id:)
|
68
59
|
@state[uri.to_s] = case language_id
|
69
60
|
when Document::LanguageId::ERB
|
@@ -75,46 +66,39 @@ module RubyLsp
|
|
75
66
|
end
|
76
67
|
end
|
77
68
|
|
78
|
-
|
69
|
+
#: (uri: URI::Generic, edits: Array[Hash[Symbol, untyped]], version: Integer) -> void
|
79
70
|
def push_edits(uri:, edits:, version:)
|
80
71
|
T.must(@state[uri.to_s]).push_edits(edits, version: version)
|
81
72
|
end
|
82
73
|
|
83
|
-
|
74
|
+
#: -> void
|
84
75
|
def clear
|
85
76
|
@state.clear
|
86
77
|
end
|
87
78
|
|
88
|
-
|
79
|
+
#: -> bool
|
89
80
|
def empty?
|
90
81
|
@state.empty?
|
91
82
|
end
|
92
83
|
|
93
|
-
|
84
|
+
#: (URI::Generic uri) -> void
|
94
85
|
def delete(uri)
|
95
86
|
@state.delete(uri.to_s)
|
96
87
|
end
|
97
88
|
|
98
|
-
|
89
|
+
#: (URI::Generic uri) -> bool
|
99
90
|
def key?(uri)
|
100
91
|
@state.key?(uri.to_s)
|
101
92
|
end
|
102
93
|
|
103
|
-
|
94
|
+
#: { (String uri, Document[untyped] document) -> void } -> void
|
104
95
|
def each(&block)
|
105
96
|
@state.each do |uri, document|
|
106
97
|
block.call(uri, document)
|
107
98
|
end
|
108
99
|
end
|
109
100
|
|
110
|
-
|
111
|
-
type_parameters(:T)
|
112
|
-
.params(
|
113
|
-
uri: URI::Generic,
|
114
|
-
request_name: String,
|
115
|
-
block: T.proc.params(document: Document[T.untyped]).returns(T.type_parameter(:T)),
|
116
|
-
).returns(T.type_parameter(:T))
|
117
|
-
end
|
101
|
+
#: [T] (URI::Generic uri, String request_name) { (Document[untyped] document) -> T } -> T
|
118
102
|
def cache_fetch(uri, request_name, &block)
|
119
103
|
get(uri).cache_fetch(request_name, &block)
|
120
104
|
end
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
@@ -7,21 +7,11 @@ module RubyLsp
|
|
7
7
|
module TestHelper
|
8
8
|
class TestError < StandardError; end
|
9
9
|
|
10
|
-
extend T::Sig
|
11
10
|
extend T::Helpers
|
12
11
|
|
13
12
|
requires_ancestor { Kernel }
|
14
13
|
|
15
|
-
|
16
|
-
type_parameters(:T)
|
17
|
-
.params(
|
18
|
-
source: T.nilable(String),
|
19
|
-
uri: URI::Generic,
|
20
|
-
stub_no_typechecker: T::Boolean,
|
21
|
-
load_addons: T::Boolean,
|
22
|
-
block: T.proc.params(server: RubyLsp::Server, uri: URI::Generic).returns(T.type_parameter(:T)),
|
23
|
-
).returns(T.type_parameter(:T))
|
24
|
-
end
|
14
|
+
#: [T] (?String? source, ?URI::Generic uri, ?stub_no_typechecker: bool, ?load_addons: bool) { (RubyLsp::Server server, URI::Generic uri) -> T } -> T
|
25
15
|
def with_server(source = nil, uri = Kernel.URI("file:///fake.rb"), stub_no_typechecker: false, load_addons: true,
|
26
16
|
&block)
|
27
17
|
server = RubyLsp::Server.new(test_mode: true)
|
@@ -52,13 +42,14 @@ module RubyLsp
|
|
52
42
|
ensure
|
53
43
|
if load_addons
|
54
44
|
RubyLsp::Addon.addons.each(&:deactivate)
|
45
|
+
RubyLsp::Addon.addon_classes.clear
|
55
46
|
RubyLsp::Addon.addons.clear
|
56
47
|
end
|
57
48
|
server.run_shutdown
|
58
49
|
end
|
59
50
|
end
|
60
51
|
|
61
|
-
|
52
|
+
#: (RubyLsp::Server server) -> RubyLsp::Result
|
62
53
|
def pop_result(server)
|
63
54
|
result = server.pop_response
|
64
55
|
result = server.pop_response until result.is_a?(RubyLsp::Result) || result.is_a?(RubyLsp::Error)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
$stdout.binmode
|
7
|
+
$stdout.sync = true
|
8
|
+
$stderr.binmode
|
9
|
+
$stderr.sync = true
|
10
|
+
|
11
|
+
module RubyLsp
|
12
|
+
module TestReporter
|
13
|
+
class << self
|
14
|
+
#: (id: String, uri: URI::Generic) -> void
|
15
|
+
def start_test(id:, uri:)
|
16
|
+
params = {
|
17
|
+
id: id,
|
18
|
+
uri: uri.to_s,
|
19
|
+
}
|
20
|
+
send_message("start", params)
|
21
|
+
end
|
22
|
+
|
23
|
+
#: (id: String, uri: URI::Generic) -> void
|
24
|
+
def record_pass(id:, uri:)
|
25
|
+
params = {
|
26
|
+
id: id,
|
27
|
+
uri: uri.to_s,
|
28
|
+
}
|
29
|
+
send_message("pass", params)
|
30
|
+
end
|
31
|
+
|
32
|
+
#: (id: String, message: String, uri: URI::Generic) -> void
|
33
|
+
def record_fail(id:, message:, uri:)
|
34
|
+
params = {
|
35
|
+
id: id,
|
36
|
+
message: message,
|
37
|
+
uri: uri.to_s,
|
38
|
+
}
|
39
|
+
send_message("fail", params)
|
40
|
+
end
|
41
|
+
|
42
|
+
#: (id: String, message: String?, uri: URI::Generic) -> void
|
43
|
+
def record_skip(id:, message:, uri:)
|
44
|
+
params = {
|
45
|
+
id: id,
|
46
|
+
message: message,
|
47
|
+
uri: uri.to_s,
|
48
|
+
}
|
49
|
+
send_message("skip", params)
|
50
|
+
end
|
51
|
+
|
52
|
+
#: (id: String, message: String?, uri: URI::Generic) -> void
|
53
|
+
def record_error(id:, message:, uri:)
|
54
|
+
params = {
|
55
|
+
id: id,
|
56
|
+
message: message,
|
57
|
+
uri: uri.to_s,
|
58
|
+
}
|
59
|
+
send_message("error", params)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
#: (method_name: String?, params: Hash[String, untyped]) -> void
|
65
|
+
def send_message(method_name, params)
|
66
|
+
json_message = { method: method_name, params: params }.to_json
|
67
|
+
$stdout.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "test/unit"
|
5
|
+
require "test/unit/ui/testrunner"
|
6
|
+
require "ruby_lsp/test_reporter"
|
7
|
+
require "ruby_indexer/lib/ruby_indexer/uri"
|
8
|
+
|
9
|
+
module RubyLsp
|
10
|
+
class TestRunner < ::Test::Unit::UI::TestRunner
|
11
|
+
private
|
12
|
+
|
13
|
+
#: (::Test::Unit::TestCase test) -> void
|
14
|
+
def test_started(test)
|
15
|
+
current_test = test
|
16
|
+
@current_uri = uri_for_test(current_test)
|
17
|
+
return unless @current_uri
|
18
|
+
|
19
|
+
@current_test_id = "#{current_test.class.name}##{current_test.method_name}"
|
20
|
+
TestReporter.start_test(
|
21
|
+
id: @current_test_id,
|
22
|
+
uri: @current_uri,
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
#: (::Test::Unit::TestCase test) -> void
|
27
|
+
def test_finished(test)
|
28
|
+
if test.passed?
|
29
|
+
TestReporter.record_pass(
|
30
|
+
id: @current_test_id,
|
31
|
+
uri: @current_uri,
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#: (::Test::Unit::Failure | ::Test::Unit::Error | ::Test::Unit::Pending result) -> void
|
37
|
+
def result_fault(result)
|
38
|
+
case result
|
39
|
+
when ::Test::Unit::Failure
|
40
|
+
record_failure(result)
|
41
|
+
when ::Test::Unit::Error
|
42
|
+
record_error(result)
|
43
|
+
when ::Test::Unit::Pending
|
44
|
+
record_skip(result)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#: (::Test::Unit::Failure failure) -> void
|
49
|
+
def record_failure(failure)
|
50
|
+
TestReporter.record_fail(
|
51
|
+
id: @current_test_id,
|
52
|
+
message: failure.message,
|
53
|
+
uri: @current_uri,
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
#: (::Test::Unit::Error error) -> void
|
58
|
+
def record_error(error)
|
59
|
+
TestReporter.record_error(
|
60
|
+
id: @current_test_id,
|
61
|
+
message: error.message,
|
62
|
+
uri: @current_uri,
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
#: (::Test::Unit::Pending pending) -> void
|
67
|
+
def record_skip(pending)
|
68
|
+
TestReporter.record_skip(
|
69
|
+
id: @current_test_id,
|
70
|
+
message: pending.message,
|
71
|
+
uri: @current_uri,
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
#: (::Test::Unit::TestCase test) -> URI::Generic?
|
76
|
+
def uri_for_test(test)
|
77
|
+
location = test.method(test.method_name).source_location
|
78
|
+
return unless location # TODO: when might this be nil?
|
79
|
+
|
80
|
+
file, _line = location
|
81
|
+
return if file.start_with?("(eval at ") # test is dynamically defined (TODO: better way to check?)
|
82
|
+
|
83
|
+
absolute_path = File.expand_path(file, Dir.pwd)
|
84
|
+
URI::Generic.from_path(path: absolute_path)
|
85
|
+
end
|
86
|
+
|
87
|
+
#: -> void
|
88
|
+
def attach_to_mediator
|
89
|
+
@mediator.add_listener(Test::Unit::TestResult::FAULT, &method(:result_fault))
|
90
|
+
@mediator.add_listener(Test::Unit::TestCase::STARTED_OBJECT, &method(:test_started))
|
91
|
+
@mediator.add_listener(Test::Unit::TestCase::FINISHED_OBJECT, &method(:test_finished))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Test::Unit::AutoRunner.register_runner(:ruby_lsp) { |_auto_runner| RubyLsp::TestRunner }
|
@@ -5,14 +5,12 @@ module RubyLsp
|
|
5
5
|
# A minimalistic type checker to try to resolve types that can be inferred without requiring a type system or
|
6
6
|
# annotations
|
7
7
|
class TypeInferrer
|
8
|
-
|
9
|
-
|
10
|
-
sig { params(index: RubyIndexer::Index).void }
|
8
|
+
#: (RubyIndexer::Index index) -> void
|
11
9
|
def initialize(index)
|
12
10
|
@index = index
|
13
11
|
end
|
14
12
|
|
15
|
-
|
13
|
+
#: (NodeContext node_context) -> Type?
|
16
14
|
def infer_receiver_type(node_context)
|
17
15
|
node = node_context.node
|
18
16
|
|
@@ -31,7 +29,7 @@ module RubyLsp
|
|
31
29
|
|
32
30
|
private
|
33
31
|
|
34
|
-
|
32
|
+
#: (Prism::CallNode node, NodeContext node_context) -> Type?
|
35
33
|
def infer_receiver_for_call_node(node, node_context)
|
36
34
|
receiver = node.receiver
|
37
35
|
|
@@ -114,7 +112,7 @@ module RubyLsp
|
|
114
112
|
end
|
115
113
|
end
|
116
114
|
|
117
|
-
|
115
|
+
#: (String raw_receiver, Array[String] nesting) -> GuessedType?
|
118
116
|
def guess_type(raw_receiver, nesting)
|
119
117
|
guessed_name = raw_receiver
|
120
118
|
.delete_prefix("@")
|
@@ -130,7 +128,7 @@ module RubyLsp
|
|
130
128
|
GuessedType.new(name)
|
131
129
|
end
|
132
130
|
|
133
|
-
|
131
|
+
#: (NodeContext node_context) -> Type
|
134
132
|
def self_receiver_handling(node_context)
|
135
133
|
nesting = node_context.nesting
|
136
134
|
# If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
|
@@ -147,7 +145,7 @@ module RubyLsp
|
|
147
145
|
Type.new("#{parts.join("::")}::<Class:#{parts.last}>")
|
148
146
|
end
|
149
147
|
|
150
|
-
|
148
|
+
#: (NodeContext node_context) -> Type?
|
151
149
|
def infer_receiver_for_class_variables(node_context)
|
152
150
|
nesting_parts = node_context.nesting.dup
|
153
151
|
|
@@ -168,18 +166,16 @@ module RubyLsp
|
|
168
166
|
|
169
167
|
# A known type
|
170
168
|
class Type
|
171
|
-
|
172
|
-
|
173
|
-
sig { returns(String) }
|
169
|
+
#: String
|
174
170
|
attr_reader :name
|
175
171
|
|
176
|
-
|
172
|
+
#: (String name) -> void
|
177
173
|
def initialize(name)
|
178
174
|
@name = name
|
179
175
|
end
|
180
176
|
|
181
177
|
# Returns the attached version of this type by removing the `<Class:...>` part from its name
|
182
|
-
|
178
|
+
#: -> Type
|
183
179
|
def attached
|
184
180
|
Type.new(T.must(@name.split("::")[..-2]).join("::"))
|
185
181
|
end
|