ruby-lsp 0.23.12 → 0.23.13

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +31 -40
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +56 -22
  6. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -1
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +15 -18
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -11
  9. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +2 -2
  10. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +1 -1
  11. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +2 -2
  12. data/lib/ruby_indexer/test/configuration_test.rb +10 -1
  13. data/lib/ruby_indexer/test/method_test.rb +26 -0
  14. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  15. data/lib/ruby_lsp/addon.rb +12 -4
  16. data/lib/ruby_lsp/base_server.rb +19 -22
  17. data/lib/ruby_lsp/client_capabilities.rb +6 -6
  18. data/lib/ruby_lsp/document.rb +13 -13
  19. data/lib/ruby_lsp/erb_document.rb +7 -9
  20. data/lib/ruby_lsp/global_state.rb +21 -24
  21. data/lib/ruby_lsp/listeners/code_lens.rb +59 -48
  22. data/lib/ruby_lsp/listeners/completion.rb +2 -2
  23. data/lib/ruby_lsp/listeners/definition.rb +2 -2
  24. data/lib/ruby_lsp/listeners/document_highlight.rb +62 -80
  25. data/lib/ruby_lsp/listeners/document_link.rb +34 -43
  26. data/lib/ruby_lsp/listeners/document_symbol.rb +1 -1
  27. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  28. data/lib/ruby_lsp/listeners/hover.rb +43 -52
  29. data/lib/ruby_lsp/listeners/inlay_hints.rb +1 -1
  30. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +11 -14
  31. data/lib/ruby_lsp/listeners/signature_help.rb +2 -2
  32. data/lib/ruby_lsp/listeners/spec_style.rb +15 -59
  33. data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
  34. data/lib/ruby_lsp/listeners/test_style.rb +47 -81
  35. data/lib/ruby_lsp/node_context.rb +4 -4
  36. data/lib/ruby_lsp/rbs_document.rb +1 -1
  37. data/lib/ruby_lsp/requests/code_actions.rb +9 -12
  38. data/lib/ruby_lsp/requests/code_lens.rb +2 -4
  39. data/lib/ruby_lsp/requests/completion.rb +3 -5
  40. data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
  41. data/lib/ruby_lsp/requests/definition.rb +4 -5
  42. data/lib/ruby_lsp/requests/diagnostics.rb +2 -2
  43. data/lib/ruby_lsp/requests/discover_tests.rb +3 -2
  44. data/lib/ruby_lsp/requests/document_highlight.rb +2 -4
  45. data/lib/ruby_lsp/requests/document_link.rb +2 -4
  46. data/lib/ruby_lsp/requests/document_symbol.rb +1 -1
  47. data/lib/ruby_lsp/requests/folding_ranges.rb +3 -8
  48. data/lib/ruby_lsp/requests/formatting.rb +2 -2
  49. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +3 -3
  50. data/lib/ruby_lsp/requests/hover.rb +2 -2
  51. data/lib/ruby_lsp/requests/inlay_hints.rb +2 -4
  52. data/lib/ruby_lsp/requests/on_type_formatting.rb +10 -13
  53. data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
  54. data/lib/ruby_lsp/requests/range_formatting.rb +2 -2
  55. data/lib/ruby_lsp/requests/references.rb +1 -1
  56. data/lib/ruby_lsp/requests/rename.rb +2 -2
  57. data/lib/ruby_lsp/requests/request.rb +2 -2
  58. data/lib/ruby_lsp/requests/selection_ranges.rb +2 -2
  59. data/lib/ruby_lsp/requests/semantic_highlighting.rb +5 -7
  60. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
  61. data/lib/ruby_lsp/requests/signature_help.rb +2 -2
  62. data/lib/ruby_lsp/requests/support/common.rb +1 -1
  63. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +15 -21
  64. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
  65. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +12 -18
  66. data/lib/ruby_lsp/requests/support/sorbet.rb +28 -31
  67. data/lib/ruby_lsp/requests/support/source_uri.rb +11 -14
  68. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +5 -9
  69. data/lib/ruby_lsp/requests/support/test_item.rb +4 -9
  70. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  71. data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -1
  72. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +1 -1
  73. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -5
  74. data/lib/ruby_lsp/response_builders/hover.rb +5 -8
  75. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +41 -47
  76. data/lib/ruby_lsp/response_builders/signature_help.rb +1 -1
  77. data/lib/ruby_lsp/response_builders/test_collection.rb +1 -1
  78. data/lib/ruby_lsp/ruby_document.rb +7 -20
  79. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +7 -4
  80. data/lib/ruby_lsp/scope.rb +1 -1
  81. data/lib/ruby_lsp/server.rb +9 -1
  82. data/lib/ruby_lsp/setup_bundler.rb +36 -42
  83. data/lib/ruby_lsp/static_docs.rb +4 -7
  84. data/lib/ruby_lsp/store.rb +9 -12
  85. data/lib/ruby_lsp/test_reporter.rb +140 -4
  86. data/lib/ruby_lsp/test_unit_test_runner.rb +10 -8
  87. data/lib/ruby_lsp/utils.rb +10 -16
  88. metadata +3 -2
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "json"
5
+ require "delegate"
5
6
 
6
7
  $stdout.binmode
7
8
  $stdout.sync = true
@@ -39,11 +40,10 @@ module RubyLsp
39
40
  send_message("fail", params)
40
41
  end
41
42
 
42
- #: (id: String, message: String?, uri: URI::Generic) -> void
43
- def record_skip(id:, message:, uri:)
43
+ #: (id: String, uri: URI::Generic) -> void
44
+ def record_skip(id:, uri:)
44
45
  params = {
45
46
  id: id,
46
- message: message,
47
47
  uri: uri.to_s,
48
48
  }
49
49
  send_message("skip", params)
@@ -59,13 +59,149 @@ module RubyLsp
59
59
  send_message("error", params)
60
60
  end
61
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
+
62
149
  private
63
150
 
64
151
  #: (method_name: String?, params: Hash[String, untyped]) -> void
65
152
  def send_message(method_name, params)
66
153
  json_message = { method: method_name, params: params }.to_json
67
- $stdout.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
154
+ ORIGINAL_STDOUT.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
68
155
  end
69
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
70
206
  end
71
207
  end
@@ -1,9 +1,14 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "test/unit"
5
- require "test/unit/ui/testrunner"
6
- require "ruby_lsp/test_reporter"
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"
7
12
  require "ruby_indexer/lib/ruby_indexer/uri"
8
13
 
9
14
  module RubyLsp
@@ -65,11 +70,7 @@ module RubyLsp
65
70
 
66
71
  #: (::Test::Unit::Pending pending) -> void
67
72
  def record_skip(pending)
68
- TestReporter.record_skip(
69
- id: @current_test_id,
70
- message: pending.message,
71
- uri: @current_uri,
72
- )
73
+ TestReporter.record_skip(id: @current_test_id, uri: @current_uri)
73
74
  end
74
75
 
75
76
  #: (::Test::Unit::TestCase test) -> URI::Generic?
@@ -94,3 +95,4 @@ module RubyLsp
94
95
  end
95
96
 
96
97
  Test::Unit::AutoRunner.register_runner(:ruby_lsp) { |_auto_runner| RubyLsp::TestRunner }
98
+ Test::Unit::AutoRunner.default_runner = :ruby_lsp
@@ -9,22 +9,16 @@ module RubyLsp
9
9
  # rubocop:enable RubyLsp/UseLanguageServerAliases
10
10
 
11
11
  # Used to indicate that a request shouldn't return a response
12
- BUNDLE_PATH = T.let(
13
- begin
14
- Bundler.bundle_path.to_s
15
- rescue Bundler::GemfileNotFound
16
- nil
17
- end,
18
- T.nilable(String),
19
- )
20
- GEMFILE_NAME = T.let(
21
- begin
22
- Bundler.with_original_env { Bundler.default_gemfile.basename.to_s }
23
- rescue Bundler::GemfileNotFound
24
- "Gemfile"
25
- end,
26
- String,
27
- )
12
+ BUNDLE_PATH = begin
13
+ Bundler.bundle_path.to_s
14
+ rescue Bundler::GemfileNotFound
15
+ nil
16
+ end #: String?
17
+ GEMFILE_NAME = begin
18
+ Bundler.with_original_env { Bundler.default_gemfile.basename.to_s }
19
+ rescue Bundler::GemfileNotFound
20
+ "Gemfile"
21
+ end #: String
28
22
  GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/#guessed-types"
29
23
 
30
24
  # Request delegation for embedded languages is not yet standardized into the language server specification. Here we
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.12
4
+ version: 0.23.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-18 00:00:00.000000000 Z
10
+ date: 2025-03-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: language_server-protocol
@@ -141,6 +141,7 @@ files:
141
141
  - lib/ruby_lsp/listeners/semantic_highlighting.rb
142
142
  - lib/ruby_lsp/listeners/signature_help.rb
143
143
  - lib/ruby_lsp/listeners/spec_style.rb
144
+ - lib/ruby_lsp/listeners/test_discovery.rb
144
145
  - lib/ruby_lsp/listeners/test_style.rb
145
146
  - lib/ruby_lsp/load_sorbet.rb
146
147
  - lib/ruby_lsp/node_context.rb