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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp-launcher +9 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +1 -1
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +6 -3
  6. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +4 -2
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +59 -29
  8. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +5 -4
  9. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +5 -1
  10. data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
  11. data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
  12. data/lib/ruby_indexer/test/configuration_test.rb +6 -4
  13. data/lib/ruby_indexer/test/constant_test.rb +34 -34
  14. data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
  15. data/lib/ruby_indexer/test/index_test.rb +139 -135
  16. data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
  17. data/lib/ruby_indexer/test/method_test.rb +118 -118
  18. data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
  19. data/lib/ruby_indexer/test/rbs_indexer_test.rb +64 -70
  20. data/lib/ruby_indexer/test/test_case.rb +2 -2
  21. data/lib/ruby_lsp/erb_document.rb +12 -4
  22. data/lib/ruby_lsp/global_state.rb +1 -1
  23. data/lib/ruby_lsp/listeners/code_lens.rb +3 -3
  24. data/lib/ruby_lsp/listeners/completion.rb +24 -11
  25. data/lib/ruby_lsp/listeners/definition.rb +1 -1
  26. data/lib/ruby_lsp/listeners/document_link.rb +3 -1
  27. data/lib/ruby_lsp/listeners/document_symbol.rb +3 -3
  28. data/lib/ruby_lsp/listeners/folding_ranges.rb +8 -4
  29. data/lib/ruby_lsp/listeners/hover.rb +2 -2
  30. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +12 -5
  31. data/lib/ruby_lsp/listeners/signature_help.rb +5 -1
  32. data/lib/ruby_lsp/listeners/spec_style.rb +1 -1
  33. data/lib/ruby_lsp/listeners/test_style.rb +3 -3
  34. data/lib/ruby_lsp/requests/code_action_resolve.rb +4 -3
  35. data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
  36. data/lib/ruby_lsp/requests/hover.rb +2 -2
  37. data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -2
  38. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -2
  39. data/lib/ruby_lsp/requests/references.rb +2 -1
  40. data/lib/ruby_lsp/requests/rename.rb +8 -5
  41. data/lib/ruby_lsp/requests/semantic_highlighting.rb +4 -4
  42. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
  43. data/lib/ruby_lsp/requests/support/common.rb +3 -1
  44. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
  45. data/lib/ruby_lsp/requests/support/source_uri.rb +1 -1
  46. data/lib/ruby_lsp/response_builders/document_symbol.rb +3 -2
  47. data/lib/ruby_lsp/response_builders/hover.rb +1 -1
  48. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +1 -1
  49. data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
  50. data/lib/ruby_lsp/server.rb +12 -4
  51. data/lib/ruby_lsp/setup_bundler.rb +5 -2
  52. data/lib/ruby_lsp/static_docs.rb +8 -1
  53. data/lib/ruby_lsp/store.rb +3 -2
  54. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +152 -0
  55. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
  56. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
  57. data/lib/ruby_lsp/type_inferrer.rb +4 -1
  58. metadata +6 -6
  59. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +0 -109
  60. data/lib/ruby_lsp/test_reporter.rb +0 -207
  61. 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(T.must(@name.split("::")[..-2]).join("::"))
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.14
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: 2025-04-09 00:00:00.000000000 Z
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/test_reporter.rb
207
- - lib/ruby_lsp/test_unit_test_runner.rb
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.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