ruby-lsp 0.23.14 → 0.23.15
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/VERSION +1 -1
- data/exe/ruby-lsp-launcher +9 -1
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +6 -3
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +4 -2
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +59 -29
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +5 -4
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +5 -1
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +6 -4
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +139 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
- data/lib/ruby_indexer/test/method_test.rb +118 -118
- data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +64 -70
- data/lib/ruby_indexer/test/test_case.rb +2 -2
- data/lib/ruby_lsp/erb_document.rb +12 -4
- data/lib/ruby_lsp/global_state.rb +1 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +3 -3
- data/lib/ruby_lsp/listeners/completion.rb +24 -11
- data/lib/ruby_lsp/listeners/definition.rb +1 -1
- data/lib/ruby_lsp/listeners/document_link.rb +3 -1
- data/lib/ruby_lsp/listeners/document_symbol.rb +3 -3
- data/lib/ruby_lsp/listeners/folding_ranges.rb +8 -4
- data/lib/ruby_lsp/listeners/hover.rb +2 -2
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +12 -5
- data/lib/ruby_lsp/listeners/signature_help.rb +5 -1
- data/lib/ruby_lsp/listeners/spec_style.rb +1 -1
- data/lib/ruby_lsp/listeners/test_style.rb +3 -3
- data/lib/ruby_lsp/requests/code_action_resolve.rb +4 -3
- data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +2 -2
- data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -2
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -2
- data/lib/ruby_lsp/requests/references.rb +2 -1
- data/lib/ruby_lsp/requests/rename.rb +8 -5
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +4 -4
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
- data/lib/ruby_lsp/requests/support/common.rb +3 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/source_uri.rb +1 -1
- data/lib/ruby_lsp/response_builders/document_symbol.rb +3 -2
- data/lib/ruby_lsp/response_builders/hover.rb +1 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +12 -4
- data/lib/ruby_lsp/setup_bundler.rb +5 -2
- data/lib/ruby_lsp/static_docs.rb +8 -1
- data/lib/ruby_lsp/store.rb +3 -2
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +152 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
- data/lib/ruby_lsp/type_inferrer.rb +4 -1
- metadata +6 -6
- data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +0 -109
- data/lib/ruby_lsp/test_reporter.rb +0 -207
- data/lib/ruby_lsp/test_unit_test_runner.rb +0 -98
@@ -0,0 +1,105 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "minitest"
|
6
|
+
rescue LoadError
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative "lsp_reporter"
|
11
|
+
require "ruby_indexer/lib/ruby_indexer/uri"
|
12
|
+
|
13
|
+
module RubyLsp
|
14
|
+
# An override of the default progress reporter in Minitest to add color to the output
|
15
|
+
class ProgressReporterWithColor < Minitest::ProgressReporter
|
16
|
+
#: (Minitest::Result) -> void
|
17
|
+
def record(result)
|
18
|
+
color = if result.error?
|
19
|
+
"\e[31m" # red
|
20
|
+
elsif result.passed?
|
21
|
+
"\e[32m" # green
|
22
|
+
elsif result.skipped?
|
23
|
+
"\e[33m" # yellow
|
24
|
+
elsif result.failure
|
25
|
+
"\e[31m" # red
|
26
|
+
else
|
27
|
+
"\e[0m" # no color
|
28
|
+
end
|
29
|
+
|
30
|
+
io.print("#{color}#{result.result_code}\e[0m") # Reset color after printing
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class MinitestReporter < Minitest::AbstractReporter
|
35
|
+
class << self
|
36
|
+
#: (Hash[untyped, untyped]) -> void
|
37
|
+
def minitest_plugin_init(_options)
|
38
|
+
# Remove the original progress reporter, so that we replace it with our own. We only do this if no other
|
39
|
+
# reporters were included by the application itself to avoid double reporting
|
40
|
+
reporters = Minitest.reporter.reporters
|
41
|
+
|
42
|
+
if reporters.all? { |r| r.is_a?(Minitest::ProgressReporter) || r.is_a?(Minitest::SummaryReporter) }
|
43
|
+
reporters.delete_if { |r| r.is_a?(Minitest::ProgressReporter) }
|
44
|
+
reporters << ProgressReporterWithColor.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add the JSON RPC reporter
|
48
|
+
reporters << MinitestReporter.new
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#: (singleton(Minitest::Test) test_class, String method_name) -> void
|
53
|
+
def prerecord(test_class, method_name)
|
54
|
+
uri = uri_from_test_class(test_class, method_name)
|
55
|
+
return unless uri
|
56
|
+
|
57
|
+
LspReporter.instance.start_test(id: "#{test_class.name}##{method_name}", uri: uri)
|
58
|
+
end
|
59
|
+
|
60
|
+
#: (Minitest::Result result) -> void
|
61
|
+
def record(result)
|
62
|
+
id = "#{result.klass}##{result.name}"
|
63
|
+
uri = uri_from_result(result)
|
64
|
+
|
65
|
+
if result.error?
|
66
|
+
message = result.failures.first.message
|
67
|
+
LspReporter.instance.record_error(id: id, uri: uri, message: message)
|
68
|
+
elsif result.passed?
|
69
|
+
LspReporter.instance.record_pass(id: id, uri: uri)
|
70
|
+
elsif result.skipped?
|
71
|
+
LspReporter.instance.record_skip(id: id, uri: uri)
|
72
|
+
elsif result.failure
|
73
|
+
message = result.failure.message
|
74
|
+
LspReporter.instance.record_fail(id: id, uri: uri, message: message)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#: -> void
|
79
|
+
def report
|
80
|
+
LspReporter.instance.shutdown
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
#: (Minitest::Result result) -> URI::Generic
|
86
|
+
def uri_from_result(result)
|
87
|
+
file = result.source_location[0]
|
88
|
+
absolute_path = File.expand_path(file, Dir.pwd)
|
89
|
+
URI::Generic.from_path(path: absolute_path)
|
90
|
+
end
|
91
|
+
|
92
|
+
#: (singleton(Minitest::Test) test_class, String method_name) -> URI::Generic?
|
93
|
+
def uri_from_test_class(test_class, method_name)
|
94
|
+
file, _line = test_class.instance_method(method_name).source_location
|
95
|
+
return unless file
|
96
|
+
|
97
|
+
return if file.start_with?("(eval at ") # test is dynamically defined
|
98
|
+
|
99
|
+
absolute_path = File.expand_path(file, Dir.pwd)
|
100
|
+
URI::Generic.from_path(path: absolute_path)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
Minitest.extensions << RubyLsp::MinitestReporter
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "test/unit"
|
6
|
+
require "test/unit/ui/testrunner"
|
7
|
+
require "test/unit/ui/console/testrunner"
|
8
|
+
rescue LoadError
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
require_relative "lsp_reporter"
|
13
|
+
require "ruby_indexer/lib/ruby_indexer/uri"
|
14
|
+
|
15
|
+
module RubyLsp
|
16
|
+
class TestUnitReporter < Test::Unit::UI::Console::TestRunner
|
17
|
+
def initialize(suite, options = {})
|
18
|
+
super
|
19
|
+
@current_uri = nil #: URI::Generic?
|
20
|
+
@current_test_id = nil #: String?
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
#: (::Test::Unit::TestCase test) -> void
|
26
|
+
def test_started(test)
|
27
|
+
super
|
28
|
+
|
29
|
+
current_test = test
|
30
|
+
@current_uri = uri_for_test(current_test)
|
31
|
+
return unless @current_uri
|
32
|
+
|
33
|
+
@current_test_id = "#{current_test.class.name}##{current_test.method_name}"
|
34
|
+
LspReporter.instance.start_test(id: @current_test_id, uri: @current_uri)
|
35
|
+
end
|
36
|
+
|
37
|
+
#: (::Test::Unit::TestCase test) -> void
|
38
|
+
def test_finished(test)
|
39
|
+
super
|
40
|
+
return unless test.passed? && @current_uri && @current_test_id
|
41
|
+
|
42
|
+
LspReporter.instance.record_pass(id: @current_test_id, uri: @current_uri)
|
43
|
+
end
|
44
|
+
|
45
|
+
#: (::Test::Unit::Failure | ::Test::Unit::Error | ::Test::Unit::Pending result) -> void
|
46
|
+
def add_fault(result)
|
47
|
+
super
|
48
|
+
return unless @current_uri && @current_test_id
|
49
|
+
|
50
|
+
case result
|
51
|
+
when ::Test::Unit::Failure
|
52
|
+
LspReporter.instance.record_fail(id: @current_test_id, message: result.message, uri: @current_uri)
|
53
|
+
when ::Test::Unit::Error
|
54
|
+
LspReporter.instance.record_error(id: @current_test_id, message: result.message, uri: @current_uri)
|
55
|
+
when ::Test::Unit::Pending
|
56
|
+
LspReporter.instance.record_skip(id: @current_test_id, uri: @current_uri)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#: (Float) -> void
|
61
|
+
def finished(elapsed_time)
|
62
|
+
LspReporter.instance.shutdown
|
63
|
+
end
|
64
|
+
|
65
|
+
#: (::Test::Unit::TestCase test) -> URI::Generic?
|
66
|
+
def uri_for_test(test)
|
67
|
+
location = test.method(test.method_name).source_location
|
68
|
+
return unless location
|
69
|
+
|
70
|
+
file, _line = location
|
71
|
+
return if file.start_with?("(eval at ")
|
72
|
+
|
73
|
+
absolute_path = File.expand_path(file, Dir.pwd)
|
74
|
+
URI::Generic.from_path(path: absolute_path)
|
75
|
+
end
|
76
|
+
|
77
|
+
#: -> void
|
78
|
+
def attach_to_mediator
|
79
|
+
# Events we care about
|
80
|
+
@mediator.add_listener(Test::Unit::TestResult::FAULT, &method(:add_fault))
|
81
|
+
@mediator.add_listener(Test::Unit::TestCase::STARTED_OBJECT, &method(:test_started))
|
82
|
+
@mediator.add_listener(Test::Unit::TestCase::FINISHED_OBJECT, &method(:test_finished))
|
83
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED, &method(:finished))
|
84
|
+
|
85
|
+
# Other events needed for the console test runner to print
|
86
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::STARTED, &method(:started))
|
87
|
+
@mediator.add_listener(Test::Unit::TestSuite::STARTED_OBJECT, &method(:test_suite_started))
|
88
|
+
@mediator.add_listener(Test::Unit::TestSuite::FINISHED_OBJECT, &method(:test_suite_finished))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
Test::Unit::AutoRunner.register_runner(:ruby_lsp) { |_auto_runner| RubyLsp::TestUnitReporter }
|
94
|
+
Test::Unit::AutoRunner.default_runner = :ruby_lsp
|
@@ -177,7 +177,10 @@ module RubyLsp
|
|
177
177
|
# Returns the attached version of this type by removing the `<Class:...>` part from its name
|
178
178
|
#: -> Type
|
179
179
|
def attached
|
180
|
-
Type.new(
|
180
|
+
Type.new(
|
181
|
+
@name.split("::")[..-2] #: as !nil
|
182
|
+
.join("::"),
|
183
|
+
)
|
181
184
|
end
|
182
185
|
end
|
183
186
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.23.
|
4
|
+
version: 0.23.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: language_server-protocol
|
@@ -194,7 +194,6 @@ files:
|
|
194
194
|
- lib/ruby_lsp/response_builders/signature_help.rb
|
195
195
|
- lib/ruby_lsp/response_builders/test_collection.rb
|
196
196
|
- lib/ruby_lsp/ruby_document.rb
|
197
|
-
- lib/ruby_lsp/ruby_lsp_reporter_plugin.rb
|
198
197
|
- lib/ruby_lsp/scope.rb
|
199
198
|
- lib/ruby_lsp/scripts/compose_bundle.rb
|
200
199
|
- lib/ruby_lsp/scripts/compose_bundle_windows.rb
|
@@ -203,8 +202,9 @@ files:
|
|
203
202
|
- lib/ruby_lsp/static_docs.rb
|
204
203
|
- lib/ruby_lsp/store.rb
|
205
204
|
- lib/ruby_lsp/test_helper.rb
|
206
|
-
- lib/ruby_lsp/
|
207
|
-
- lib/ruby_lsp/
|
205
|
+
- lib/ruby_lsp/test_reporters/lsp_reporter.rb
|
206
|
+
- lib/ruby_lsp/test_reporters/minitest_reporter.rb
|
207
|
+
- lib/ruby_lsp/test_reporters/test_unit_reporter.rb
|
208
208
|
- lib/ruby_lsp/type_inferrer.rb
|
209
209
|
- lib/ruby_lsp/utils.rb
|
210
210
|
- static_docs/yield.md
|
@@ -228,7 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
228
228
|
- !ruby/object:Gem::Version
|
229
229
|
version: '0'
|
230
230
|
requirements: []
|
231
|
-
rubygems_version: 3.6.
|
231
|
+
rubygems_version: 3.6.8
|
232
232
|
specification_version: 4
|
233
233
|
summary: An opinionated language server for Ruby
|
234
234
|
test_files: []
|
@@ -1,109 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
begin
|
5
|
-
require "minitest"
|
6
|
-
rescue LoadError
|
7
|
-
return
|
8
|
-
end
|
9
|
-
|
10
|
-
require_relative "test_reporter"
|
11
|
-
require "ruby_indexer/lib/ruby_indexer/uri"
|
12
|
-
|
13
|
-
module Minitest
|
14
|
-
module Reporters
|
15
|
-
class RubyLspReporter < ::Minitest::AbstractReporter
|
16
|
-
class << self
|
17
|
-
#: (Hash[untyped, untyped]) -> void
|
18
|
-
def minitest_plugin_init(_options)
|
19
|
-
Minitest.reporter.reporters << RubyLspReporter.new
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
#: (singleton(Minitest::Test) test_class, String method_name) -> void
|
24
|
-
def prerecord(test_class, method_name)
|
25
|
-
uri = uri_from_test_class(test_class, method_name)
|
26
|
-
return unless uri
|
27
|
-
|
28
|
-
RubyLsp::TestReporter.start_test(
|
29
|
-
id: "#{test_class.name}##{method_name}",
|
30
|
-
uri: uri,
|
31
|
-
)
|
32
|
-
end
|
33
|
-
|
34
|
-
#: (Minitest::Result result) -> void
|
35
|
-
def record(result)
|
36
|
-
if result.error?
|
37
|
-
record_error(result)
|
38
|
-
elsif result.passed?
|
39
|
-
record_pass(result)
|
40
|
-
elsif result.skipped?
|
41
|
-
record_skip(result)
|
42
|
-
elsif result.failure
|
43
|
-
record_fail(result)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
#: (Minitest::Result result) -> void
|
50
|
-
def record_pass(result)
|
51
|
-
RubyLsp::TestReporter.record_pass(
|
52
|
-
id: id_from_result(result),
|
53
|
-
uri: uri_from_result(result),
|
54
|
-
)
|
55
|
-
end
|
56
|
-
|
57
|
-
#: (Minitest::Result result) -> void
|
58
|
-
def record_skip(result)
|
59
|
-
RubyLsp::TestReporter.record_skip(
|
60
|
-
id: id_from_result(result),
|
61
|
-
uri: uri_from_result(result),
|
62
|
-
)
|
63
|
-
end
|
64
|
-
|
65
|
-
#: (Minitest::Result result) -> void
|
66
|
-
def record_fail(result)
|
67
|
-
RubyLsp::TestReporter.record_fail(
|
68
|
-
id: id_from_result(result),
|
69
|
-
message: result.failure.message,
|
70
|
-
uri: uri_from_result(result),
|
71
|
-
)
|
72
|
-
end
|
73
|
-
|
74
|
-
#: (Minitest::Result result) -> void
|
75
|
-
def record_error(result)
|
76
|
-
RubyLsp::TestReporter.record_error(
|
77
|
-
id: id_from_result(result),
|
78
|
-
uri: uri_from_result(result),
|
79
|
-
message: result.failures.first.message,
|
80
|
-
)
|
81
|
-
end
|
82
|
-
|
83
|
-
#: (Minitest::Result result) -> String
|
84
|
-
def id_from_result(result)
|
85
|
-
"#{result.klass}##{result.name}"
|
86
|
-
end
|
87
|
-
|
88
|
-
#: (Minitest::Result result) -> URI::Generic
|
89
|
-
def uri_from_result(result)
|
90
|
-
file = result.source_location[0]
|
91
|
-
absolute_path = File.expand_path(file, Dir.pwd)
|
92
|
-
URI::Generic.from_path(path: absolute_path)
|
93
|
-
end
|
94
|
-
|
95
|
-
#: (singleton(Minitest::Test) test_class, String method_name) -> URI::Generic?
|
96
|
-
def uri_from_test_class(test_class, method_name)
|
97
|
-
file, _line = test_class.instance_method(method_name).source_location
|
98
|
-
return unless file
|
99
|
-
|
100
|
-
return if file.start_with?("(eval at ") # test is dynamically defined
|
101
|
-
|
102
|
-
absolute_path = File.expand_path(file, Dir.pwd)
|
103
|
-
URI::Generic.from_path(path: absolute_path)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
Minitest.extensions << Minitest::Reporters::RubyLspReporter
|
@@ -1,207 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "json"
|
5
|
-
require "delegate"
|
6
|
-
|
7
|
-
$stdout.binmode
|
8
|
-
$stdout.sync = true
|
9
|
-
$stderr.binmode
|
10
|
-
$stderr.sync = true
|
11
|
-
|
12
|
-
module RubyLsp
|
13
|
-
module TestReporter
|
14
|
-
class << self
|
15
|
-
#: (id: String, uri: URI::Generic) -> void
|
16
|
-
def start_test(id:, uri:)
|
17
|
-
params = {
|
18
|
-
id: id,
|
19
|
-
uri: uri.to_s,
|
20
|
-
}
|
21
|
-
send_message("start", params)
|
22
|
-
end
|
23
|
-
|
24
|
-
#: (id: String, uri: URI::Generic) -> void
|
25
|
-
def record_pass(id:, uri:)
|
26
|
-
params = {
|
27
|
-
id: id,
|
28
|
-
uri: uri.to_s,
|
29
|
-
}
|
30
|
-
send_message("pass", params)
|
31
|
-
end
|
32
|
-
|
33
|
-
#: (id: String, message: String, uri: URI::Generic) -> void
|
34
|
-
def record_fail(id:, message:, uri:)
|
35
|
-
params = {
|
36
|
-
id: id,
|
37
|
-
message: message,
|
38
|
-
uri: uri.to_s,
|
39
|
-
}
|
40
|
-
send_message("fail", params)
|
41
|
-
end
|
42
|
-
|
43
|
-
#: (id: String, uri: URI::Generic) -> void
|
44
|
-
def record_skip(id:, uri:)
|
45
|
-
params = {
|
46
|
-
id: id,
|
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
|
-
#: (message: String) -> void
|
63
|
-
def append_output(message:)
|
64
|
-
params = {
|
65
|
-
message: message,
|
66
|
-
}
|
67
|
-
send_message("append_output", params)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Gather the results returned by Coverage.result and format like the VS Code test explorer expects
|
71
|
-
#
|
72
|
-
# Coverage result format:
|
73
|
-
#
|
74
|
-
# Lines are reported in order as an array where each number is the number of times it was executed. For example,
|
75
|
-
# the following says that line 0 was executed 1 time and line 1 executed 3 times: [1, 3].
|
76
|
-
# Nil values represent lines for which coverage is not available, like empty lines, comments or keywords like
|
77
|
-
# `else`
|
78
|
-
#
|
79
|
-
# Branches are a hash containing the name of the branch and the location where it is found in tuples with the
|
80
|
-
# following elements: [NAME, ID, START_LINE, START_COLUMN, END_LINE, END_COLUMN] as the keys and the value is the
|
81
|
-
# number of times it was executed
|
82
|
-
#
|
83
|
-
# Methods are a similar hash [ClassName, :method_name, START_LINE, START_COLUMN, END_LINE, END_COLUMN] => NUMBER
|
84
|
-
# OF EXECUTIONS
|
85
|
-
#
|
86
|
-
# Example:
|
87
|
-
# {
|
88
|
-
# "file_path" => {
|
89
|
-
# "lines" => [1, 2, 3, nil],
|
90
|
-
# "branches" => {
|
91
|
-
# ["&.", 0, 6, 21, 6, 65] => { [:then, 1, 6, 21, 6, 65] => 0, [:else, 5, 7, 0, 7, 87] => 1 }
|
92
|
-
# },
|
93
|
-
# "methods" => {
|
94
|
-
# ["Foo", :bar, 6, 21, 6, 65] => 0
|
95
|
-
# }
|
96
|
-
# }
|
97
|
-
#: () -> Hash[String, StatementCoverage]
|
98
|
-
def gather_coverage_results
|
99
|
-
# Ignore coverage results inside dependencies
|
100
|
-
bundle_path = Bundler.bundle_path.to_s
|
101
|
-
default_gems_path = File.dirname(RbConfig::CONFIG["rubylibdir"])
|
102
|
-
|
103
|
-
result = Coverage.result.reject do |file_path, _coverage_info|
|
104
|
-
file_path.start_with?(bundle_path) ||
|
105
|
-
file_path.start_with?(default_gems_path) ||
|
106
|
-
file_path.start_with?("eval")
|
107
|
-
end
|
108
|
-
|
109
|
-
result.to_h do |file_path, coverage_info|
|
110
|
-
# Format the branch coverage information as VS Code expects it and then group it based on the start line of
|
111
|
-
# the conditional that causes the branching. We need to match each line coverage data with the branches that
|
112
|
-
# spawn from that line
|
113
|
-
branch_by_line = coverage_info[:branches]
|
114
|
-
.flat_map do |branch, data|
|
115
|
-
branch_name, _branch_id, branch_start_line, _branch_start_col, _branch_end_line, _branch_end_col = branch
|
116
|
-
|
117
|
-
data.map do |then_or_else, execution_count|
|
118
|
-
name, _id, start_line, start_column, end_line, end_column = then_or_else
|
119
|
-
|
120
|
-
{
|
121
|
-
groupingLine: branch_start_line,
|
122
|
-
executed: execution_count,
|
123
|
-
location: {
|
124
|
-
start: { line: start_line, character: start_column },
|
125
|
-
end: { line: end_line, character: end_column },
|
126
|
-
},
|
127
|
-
label: "#{branch_name} #{name}",
|
128
|
-
}
|
129
|
-
end
|
130
|
-
end
|
131
|
-
.group_by { |branch| branch[:groupingLine] }
|
132
|
-
|
133
|
-
# Format the line coverage information, gathering any branch coverage data associated with that line
|
134
|
-
data = coverage_info[:lines].filter_map.with_index do |execution_count, line_index|
|
135
|
-
next if execution_count.nil?
|
136
|
-
|
137
|
-
{
|
138
|
-
executed: execution_count,
|
139
|
-
location: { line: line_index, character: 0 },
|
140
|
-
branches: branch_by_line[line_index] || [],
|
141
|
-
}
|
142
|
-
end
|
143
|
-
|
144
|
-
# The expected format is URI => { executed: number_of_times_executed, location: { ... }, branches: [ ... ] }
|
145
|
-
[URI::Generic.from_path(path: File.expand_path(file_path)).to_s, data]
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
private
|
150
|
-
|
151
|
-
#: (method_name: String?, params: Hash[String, untyped]) -> void
|
152
|
-
def send_message(method_name, params)
|
153
|
-
json_message = { method: method_name, params: params }.to_json
|
154
|
-
ORIGINAL_STDOUT.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
ORIGINAL_STDOUT = $stdout #: IO
|
159
|
-
|
160
|
-
class IOWrapper < SimpleDelegator
|
161
|
-
#: (Object) -> void
|
162
|
-
def puts(*args)
|
163
|
-
args.each { |arg| log(convert_line_breaks(arg) + "\r\n") }
|
164
|
-
end
|
165
|
-
|
166
|
-
#: (Object) -> void
|
167
|
-
def print(*args)
|
168
|
-
args.each { |arg| log(convert_line_breaks(arg)) }
|
169
|
-
end
|
170
|
-
|
171
|
-
#: (Object) -> void
|
172
|
-
def write(*args)
|
173
|
-
args.each { |arg| log(convert_line_breaks(arg)) }
|
174
|
-
end
|
175
|
-
|
176
|
-
private
|
177
|
-
|
178
|
-
#: (Object) -> String
|
179
|
-
def convert_line_breaks(message)
|
180
|
-
message.to_s.gsub("\n", "\r\n")
|
181
|
-
end
|
182
|
-
|
183
|
-
#: (String) -> void
|
184
|
-
def log(message)
|
185
|
-
TestReporter.append_output(message: message)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
if ENV["RUBY_LSP_TEST_RUNNER"]
|
192
|
-
# We wrap the default output stream so that we can capture anything written to stdout and emit it as part of the JSON
|
193
|
-
# event stream.
|
194
|
-
$> = RubyLsp::TestReporter::IOWrapper.new($stdout)
|
195
|
-
|
196
|
-
if ENV["RUBY_LSP_TEST_RUNNER"] == "coverage"
|
197
|
-
# Auto start coverage when running tests under that profile. This avoids the user from having to configure coverage
|
198
|
-
# manually for their project or adding extra dependencies
|
199
|
-
require "coverage"
|
200
|
-
Coverage.start(:all)
|
201
|
-
|
202
|
-
at_exit do
|
203
|
-
coverage_results = RubyLsp::TestReporter.gather_coverage_results
|
204
|
-
File.write(File.join(".ruby-lsp", "coverage_result.json"), coverage_results.to_json)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
begin
|
5
|
-
require "test/unit"
|
6
|
-
require "test/unit/ui/testrunner"
|
7
|
-
rescue LoadError
|
8
|
-
return
|
9
|
-
end
|
10
|
-
|
11
|
-
require_relative "test_reporter"
|
12
|
-
require "ruby_indexer/lib/ruby_indexer/uri"
|
13
|
-
|
14
|
-
module RubyLsp
|
15
|
-
class TestRunner < ::Test::Unit::UI::TestRunner
|
16
|
-
private
|
17
|
-
|
18
|
-
#: (::Test::Unit::TestCase test) -> void
|
19
|
-
def test_started(test)
|
20
|
-
current_test = test
|
21
|
-
@current_uri = uri_for_test(current_test)
|
22
|
-
return unless @current_uri
|
23
|
-
|
24
|
-
@current_test_id = "#{current_test.class.name}##{current_test.method_name}"
|
25
|
-
TestReporter.start_test(
|
26
|
-
id: @current_test_id,
|
27
|
-
uri: @current_uri,
|
28
|
-
)
|
29
|
-
end
|
30
|
-
|
31
|
-
#: (::Test::Unit::TestCase test) -> void
|
32
|
-
def test_finished(test)
|
33
|
-
if test.passed?
|
34
|
-
TestReporter.record_pass(
|
35
|
-
id: @current_test_id,
|
36
|
-
uri: @current_uri,
|
37
|
-
)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
#: (::Test::Unit::Failure | ::Test::Unit::Error | ::Test::Unit::Pending result) -> void
|
42
|
-
def result_fault(result)
|
43
|
-
case result
|
44
|
-
when ::Test::Unit::Failure
|
45
|
-
record_failure(result)
|
46
|
-
when ::Test::Unit::Error
|
47
|
-
record_error(result)
|
48
|
-
when ::Test::Unit::Pending
|
49
|
-
record_skip(result)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
#: (::Test::Unit::Failure failure) -> void
|
54
|
-
def record_failure(failure)
|
55
|
-
TestReporter.record_fail(
|
56
|
-
id: @current_test_id,
|
57
|
-
message: failure.message,
|
58
|
-
uri: @current_uri,
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
#: (::Test::Unit::Error error) -> void
|
63
|
-
def record_error(error)
|
64
|
-
TestReporter.record_error(
|
65
|
-
id: @current_test_id,
|
66
|
-
message: error.message,
|
67
|
-
uri: @current_uri,
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
|
-
#: (::Test::Unit::Pending pending) -> void
|
72
|
-
def record_skip(pending)
|
73
|
-
TestReporter.record_skip(id: @current_test_id, uri: @current_uri)
|
74
|
-
end
|
75
|
-
|
76
|
-
#: (::Test::Unit::TestCase test) -> URI::Generic?
|
77
|
-
def uri_for_test(test)
|
78
|
-
location = test.method(test.method_name).source_location
|
79
|
-
return unless location # TODO: when might this be nil?
|
80
|
-
|
81
|
-
file, _line = location
|
82
|
-
return if file.start_with?("(eval at ") # test is dynamically defined (TODO: better way to check?)
|
83
|
-
|
84
|
-
absolute_path = File.expand_path(file, Dir.pwd)
|
85
|
-
URI::Generic.from_path(path: absolute_path)
|
86
|
-
end
|
87
|
-
|
88
|
-
#: -> void
|
89
|
-
def attach_to_mediator
|
90
|
-
@mediator.add_listener(Test::Unit::TestResult::FAULT, &method(:result_fault))
|
91
|
-
@mediator.add_listener(Test::Unit::TestCase::STARTED_OBJECT, &method(:test_started))
|
92
|
-
@mediator.add_listener(Test::Unit::TestCase::FINISHED_OBJECT, &method(:test_finished))
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
Test::Unit::AutoRunner.register_runner(:ruby_lsp) { |_auto_runner| RubyLsp::TestRunner }
|
98
|
-
Test::Unit::AutoRunner.default_runner = :ruby_lsp
|