ruby-lsp 0.23.15 → 0.26.9

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +17 -14
  4. data/exe/ruby-lsp-check +0 -4
  5. data/exe/ruby-lsp-launcher +41 -14
  6. data/exe/ruby-lsp-test-exec +6 -0
  7. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +0 -1
  8. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +0 -1
  9. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +4 -3
  10. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +42 -20
  11. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -7
  12. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +49 -62
  13. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +84 -74
  14. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -9
  15. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +9 -14
  16. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +12 -8
  17. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +4 -4
  18. data/lib/ruby_lsp/addon.rb +44 -15
  19. data/lib/ruby_lsp/base_server.rb +56 -37
  20. data/lib/ruby_lsp/client_capabilities.rb +6 -1
  21. data/lib/ruby_lsp/document.rb +174 -62
  22. data/lib/ruby_lsp/erb_document.rb +10 -8
  23. data/lib/ruby_lsp/global_state.rb +86 -33
  24. data/lib/ruby_lsp/internal.rb +6 -3
  25. data/lib/ruby_lsp/listeners/completion.rb +22 -11
  26. data/lib/ruby_lsp/listeners/definition.rb +41 -21
  27. data/lib/ruby_lsp/listeners/document_highlight.rb +26 -1
  28. data/lib/ruby_lsp/listeners/document_link.rb +64 -28
  29. data/lib/ruby_lsp/listeners/hover.rb +27 -16
  30. data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
  31. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +2 -2
  32. data/lib/ruby_lsp/listeners/signature_help.rb +2 -2
  33. data/lib/ruby_lsp/listeners/spec_style.rb +155 -79
  34. data/lib/ruby_lsp/listeners/test_discovery.rb +39 -21
  35. data/lib/ruby_lsp/listeners/test_style.rb +75 -35
  36. data/lib/ruby_lsp/rbs_document.rb +3 -6
  37. data/lib/ruby_lsp/requests/code_action_resolve.rb +83 -58
  38. data/lib/ruby_lsp/requests/code_actions.rb +20 -5
  39. data/lib/ruby_lsp/requests/code_lens.rb +27 -6
  40. data/lib/ruby_lsp/requests/completion.rb +3 -3
  41. data/lib/ruby_lsp/requests/completion_resolve.rb +8 -6
  42. data/lib/ruby_lsp/requests/definition.rb +4 -7
  43. data/lib/ruby_lsp/requests/discover_tests.rb +2 -2
  44. data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
  45. data/lib/ruby_lsp/requests/document_link.rb +1 -1
  46. data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
  47. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +64 -12
  48. data/lib/ruby_lsp/requests/hover.rb +3 -6
  49. data/lib/ruby_lsp/requests/inlay_hints.rb +4 -4
  50. data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
  51. data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
  52. data/lib/ruby_lsp/requests/references.rb +10 -21
  53. data/lib/ruby_lsp/requests/rename.rb +9 -10
  54. data/lib/ruby_lsp/requests/request.rb +8 -8
  55. data/lib/ruby_lsp/requests/selection_ranges.rb +2 -2
  56. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  57. data/lib/ruby_lsp/requests/show_syntax_tree.rb +2 -2
  58. data/lib/ruby_lsp/requests/signature_help.rb +2 -2
  59. data/lib/ruby_lsp/requests/support/annotation.rb +1 -1
  60. data/lib/ruby_lsp/requests/support/common.rb +9 -12
  61. data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
  62. data/lib/ruby_lsp/requests/support/package_url.rb +414 -0
  63. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +7 -1
  64. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
  65. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -3
  66. data/lib/ruby_lsp/requests/support/source_uri.rb +7 -4
  67. data/lib/ruby_lsp/requests/support/test_item.rb +7 -1
  68. data/lib/ruby_lsp/requests/workspace_symbol.rb +20 -12
  69. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +1 -4
  70. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -3
  71. data/lib/ruby_lsp/response_builders/hover.rb +1 -4
  72. data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
  73. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +4 -5
  74. data/lib/ruby_lsp/response_builders/signature_help.rb +1 -2
  75. data/lib/ruby_lsp/response_builders/test_collection.rb +29 -3
  76. data/lib/ruby_lsp/ruby_document.rb +14 -42
  77. data/lib/ruby_lsp/scripts/compose_bundle.rb +3 -3
  78. data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +3 -1
  79. data/lib/ruby_lsp/server.rb +173 -130
  80. data/lib/ruby_lsp/setup_bundler.rb +114 -47
  81. data/lib/ruby_lsp/static_docs.rb +1 -0
  82. data/lib/ruby_lsp/store.rb +6 -16
  83. data/lib/ruby_lsp/test_helper.rb +1 -4
  84. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +121 -17
  85. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +65 -25
  86. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +16 -18
  87. data/lib/ruby_lsp/utils.rb +102 -13
  88. data/static_docs/break.md +103 -0
  89. metadata +8 -33
  90. data/lib/ruby_indexer/test/class_variables_test.rb +0 -140
  91. data/lib/ruby_indexer/test/classes_and_modules_test.rb +0 -770
  92. data/lib/ruby_indexer/test/configuration_test.rb +0 -280
  93. data/lib/ruby_indexer/test/constant_test.rb +0 -402
  94. data/lib/ruby_indexer/test/enhancements_test.rb +0 -325
  95. data/lib/ruby_indexer/test/global_variable_test.rb +0 -49
  96. data/lib/ruby_indexer/test/index_test.rb +0 -2190
  97. data/lib/ruby_indexer/test/instance_variables_test.rb +0 -240
  98. data/lib/ruby_indexer/test/method_test.rb +0 -973
  99. data/lib/ruby_indexer/test/prefix_tree_test.rb +0 -150
  100. data/lib/ruby_indexer/test/rbs_indexer_test.rb +0 -380
  101. data/lib/ruby_indexer/test/reference_finder_test.rb +0 -330
  102. data/lib/ruby_indexer/test/test_case.rb +0 -51
  103. data/lib/ruby_indexer/test/uri_test.rb +0 -85
  104. data/lib/ruby_lsp/load_sorbet.rb +0 -62
@@ -0,0 +1,414 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ # This is a copy of the implementation from the `package_url` gem with the
5
+ # following license. Original source can be found at:
6
+ # https://github.com/package-url/packageurl-ruby/blob/main/lib/package_url.rb
7
+
8
+ # MIT License
9
+ #
10
+ # Copyright (c) 2021 package-url
11
+ #
12
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ # of this software and associated documentation files (the "Software"), to deal
14
+ # in the Software without restriction, including without limitation the rights
15
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ # copies of the Software, and to permit persons to whom the Software is
17
+ # furnished to do so, subject to the following conditions:
18
+ #
19
+ # The above copyright notice and this permission notice shall be included in all
20
+ # copies or substantial portions of the Software.
21
+ #
22
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ # SOFTWARE.
29
+
30
+ require "uri"
31
+
32
+ # A package URL, or _purl_, is a URL string used to
33
+ # identify and locate a software package in a mostly universal and uniform way
34
+ # across programing languages, package managers, packaging conventions, tools,
35
+ # APIs and databases.
36
+ #
37
+ # A purl is a URL composed of seven components:
38
+ #
39
+ # ```
40
+ # scheme:type/namespace/name@version?qualifiers#subpath
41
+ # ```
42
+ #
43
+ # For example,
44
+ # the package URL for this Ruby package at version 0.1.0 is
45
+ # `pkg:ruby/mattt/packageurl-ruby@0.1.0`.
46
+ module RubyLsp
47
+ class PackageURL
48
+ # Raised when attempting to parse an invalid package URL string.
49
+ # @see #parse
50
+ class InvalidPackageURL < ArgumentError; end
51
+
52
+ # The URL scheme, which has a constant value of `"pkg"`.
53
+ def scheme
54
+ "pkg"
55
+ end
56
+
57
+ # The package type or protocol, such as `"gem"`, `"npm"`, and `"github"`.
58
+ attr_reader :type
59
+
60
+ # A name prefix, specific to the type of package.
61
+ # For example, an npm scope, a Docker image owner, or a GitHub user.
62
+ attr_reader :namespace
63
+
64
+ # The name of the package.
65
+ attr_reader :name
66
+
67
+ # The version of the package.
68
+ attr_reader :version
69
+
70
+ # Extra qualifying data for a package, specific to the type of package.
71
+ # For example, the operating system or architecture.
72
+ attr_reader :qualifiers
73
+
74
+ # An extra subpath within a package, relative to the package root.
75
+ attr_reader :subpath
76
+
77
+ # Constructs a package URL from its components
78
+ # @param type [String] The package type or protocol.
79
+ # @param namespace [String] A name prefix, specific to the type of package.
80
+ # @param name [String] The name of the package.
81
+ # @param version [String] The version of the package.
82
+ # @param qualifiers [Hash] Extra qualifying data for a package, specific to the type of package.
83
+ # @param subpath [String] An extra subpath within a package, relative to the package root.
84
+ def initialize(type:, name:, namespace: nil, version: nil, qualifiers: nil, subpath: nil)
85
+ raise ArgumentError, "type is required" if type.nil? || type.empty?
86
+ raise ArgumentError, "name is required" if name.nil? || name.empty?
87
+
88
+ @type = type.downcase
89
+ @namespace = namespace
90
+ @name = name
91
+ @version = version
92
+ @qualifiers = qualifiers
93
+ @subpath = subpath
94
+ end
95
+
96
+ # Creates a new PackageURL from a string.
97
+ # @param [String] string The package URL string.
98
+ # @raise [InvalidPackageURL] If the string is not a valid package URL.
99
+ # @return [PackageURL]
100
+ def self.parse(string)
101
+ components = {
102
+ type: nil,
103
+ namespace: nil,
104
+ name: nil,
105
+ version: nil,
106
+ qualifiers: nil,
107
+ subpath: nil,
108
+ }
109
+
110
+ # Split the purl string once from right on '#'
111
+ # - The left side is the remainder
112
+ # - Strip the right side from leading and trailing '/'
113
+ # - Split this on '/'
114
+ # - Discard any empty string segment from that split
115
+ # - Discard any '.' or '..' segment from that split
116
+ # - Percent-decode each segment
117
+ # - UTF-8-decode each segment if needed in your programming language
118
+ # - Join segments back with a '/'
119
+ # - This is the subpath
120
+ case string.rpartition("#")
121
+ in String => remainder, separator, String => subpath unless separator.empty?
122
+ subpath_components = []
123
+ subpath.split("/").each do |segment|
124
+ next if segment.empty? || segment == "." || segment == ".."
125
+
126
+ subpath_components << URI.decode_www_form_component(segment)
127
+ end
128
+
129
+ components[:subpath] = subpath_components.compact.join("/")
130
+
131
+ string = remainder
132
+ else
133
+ components[:subpath] = nil
134
+ end
135
+
136
+ # Split the remainder once from right on '?'
137
+ # - The left side is the remainder
138
+ # - The right side is the qualifiers string
139
+ # - Split the qualifiers on '&'. Each part is a key=value pair
140
+ # - For each pair, split the key=value once from left on '=':
141
+ # - The key is the lowercase left side
142
+ # - The value is the percent-decoded right side
143
+ # - UTF-8-decode the value if needed in your programming language
144
+ # - Discard any key/value pairs where the value is empty
145
+ # - If the key is checksums,
146
+ # split the value on ',' to create a list of checksums
147
+ # - This list of key/value is the qualifiers object
148
+ case string.rpartition("?")
149
+ in String => remainder, separator, String => qualifiers unless separator.empty?
150
+ components[:qualifiers] = {}
151
+
152
+ qualifiers.split("&").each do |pair|
153
+ case pair.partition("=")
154
+ in String => key, separator, String => value unless separator.empty?
155
+ key = key.downcase
156
+ value = URI.decode_www_form_component(value)
157
+ next if value.empty?
158
+
159
+ components[:qualifiers][key] = case key
160
+ when "checksums"
161
+ value.split(",")
162
+ else
163
+ value
164
+ end
165
+ else
166
+ next
167
+ end
168
+ end
169
+
170
+ string = remainder
171
+ else
172
+ components[:qualifiers] = nil
173
+ end
174
+
175
+ # Split the remainder once from left on ':'
176
+ # - The left side lowercased is the scheme
177
+ # - The right side is the remainder
178
+ case string.partition(":")
179
+ in "pkg", separator, String => remainder unless separator.empty?
180
+ string = remainder
181
+ else
182
+ raise InvalidPackageURL, 'invalid or missing "pkg:" URL scheme'
183
+ end
184
+
185
+ # Strip the remainder from leading and trailing '/'
186
+ # Use gsub to remove ALL leading slashes instead of just one
187
+ string = string.gsub(%r{^/+}, "").delete_suffix("/")
188
+ # - Split this once from left on '/'
189
+ # - The left side lowercased is the type
190
+ # - The right side is the remainder
191
+ case string.partition("/")
192
+ in String => type, separator, remainder unless separator.empty?
193
+ components[:type] = type
194
+
195
+ string = remainder
196
+ else
197
+ raise InvalidPackageURL, "invalid or missing package type"
198
+ end
199
+
200
+ # Split the remainder once from right on '@'
201
+ # - The left side is the remainder
202
+ # - Percent-decode the right side. This is the version.
203
+ # - UTF-8-decode the version if needed in your programming language
204
+ # - This is the version
205
+ case string.rpartition("@")
206
+ in String => remainder, separator, String => version unless separator.empty?
207
+ components[:version] = URI.decode_www_form_component(version)
208
+
209
+ string = remainder
210
+ else
211
+ components[:version] = nil
212
+ end
213
+
214
+ # Split the remainder once from right on '/'
215
+ # - The left side is the remainder
216
+ # - Percent-decode the right side. This is the name
217
+ # - UTF-8-decode this name if needed in your programming language
218
+ # - Apply type-specific normalization to the name if needed
219
+ # - This is the name
220
+ case string.rpartition("/")
221
+ in String => remainder, separator, String => name unless separator.empty?
222
+ components[:name] = URI.decode_www_form_component(name)
223
+
224
+ # Split the remainder on '/'
225
+ # - Discard any empty segment from that split
226
+ # - Percent-decode each segment
227
+ # - UTF-8-decode the each segment if needed in your programming language
228
+ # - Apply type-specific normalization to each segment if needed
229
+ # - Join segments back with a '/'
230
+ # - This is the namespace
231
+ components[:namespace] = remainder.split("/").map { |s| URI.decode_www_form_component(s) }.compact.join("/")
232
+ in _, _, String => name
233
+ components[:name] = URI.decode_www_form_component(name)
234
+ components[:namespace] = nil
235
+ end
236
+
237
+ # Ensure type and name are not nil before creating the PackageURL instance
238
+ raise InvalidPackageURL, "missing package type" if components[:type].nil?
239
+ raise InvalidPackageURL, "missing package name" if components[:name].nil?
240
+
241
+ # Create a new PackageURL with validated components
242
+ type = components[:type] || "" # This ensures type is never nil
243
+ name = components[:name] || "" # This ensures name is never nil
244
+
245
+ new(
246
+ type: type,
247
+ name: name,
248
+ namespace: components[:namespace],
249
+ version: components[:version],
250
+ qualifiers: components[:qualifiers],
251
+ subpath: components[:subpath],
252
+ )
253
+ end
254
+
255
+ # Returns a hash containing the
256
+ # scheme, type, namespace, name, version, qualifiers, and subpath components
257
+ # of the package URL.
258
+ def to_h
259
+ {
260
+ scheme: scheme,
261
+ type: @type,
262
+ namespace: @namespace,
263
+ name: @name,
264
+ version: @version,
265
+ qualifiers: @qualifiers,
266
+ subpath: @subpath,
267
+ }
268
+ end
269
+
270
+ # Returns a string representation of the package URL.
271
+ # Package URL representations are created according to the instructions from
272
+ # https://github.com/package-url/purl-spec/blob/0b1559f76b79829e789c4f20e6d832c7314762c5/PURL-SPECIFICATION.rst#how-to-build-purl-string-from-its-components.
273
+ def to_s
274
+ # Start a purl string with the "pkg:" scheme as a lowercase ASCII string
275
+ purl = "pkg:"
276
+
277
+ # Append the type string to the purl as a lowercase ASCII string
278
+ # Append '/' to the purl
279
+
280
+ purl += @type
281
+ purl += "/"
282
+
283
+ # If the namespace is not empty:
284
+ # - Strip the namespace from leading and trailing '/'
285
+ # - Split on '/' as segments
286
+ # - Apply type-specific normalization to each segment if needed
287
+ # - UTF-8-encode each segment if needed in your programming language
288
+ # - Percent-encode each segment
289
+ # - Join the segments with '/'
290
+ # - Append this to the purl
291
+ # - Append '/' to the purl
292
+ # - Strip the name from leading and trailing '/'
293
+ # - Apply type-specific normalization to the name if needed
294
+ # - UTF-8-encode the name if needed in your programming language
295
+ # - Append the percent-encoded name to the purl
296
+ #
297
+ # If the namespace is empty:
298
+ # - Apply type-specific normalization to the name if needed
299
+ # - UTF-8-encode the name if needed in your programming language
300
+ # - Append the percent-encoded name to the purl
301
+ case @namespace
302
+ in String => namespace unless namespace.empty?
303
+ segments = []
304
+ @namespace.delete_prefix("/").delete_suffix("/").split("/").each do |segment|
305
+ next if segment.empty?
306
+
307
+ segments << URI.encode_www_form_component(segment)
308
+ end
309
+ purl += segments.join("/")
310
+
311
+ purl += "/"
312
+ purl += URI.encode_www_form_component(@name.delete_prefix("/").delete_suffix("/"))
313
+ else
314
+ purl += URI.encode_www_form_component(@name)
315
+ end
316
+
317
+ # If the version is not empty:
318
+ # - Append '@' to the purl
319
+ # - UTF-8-encode the version if needed in your programming language
320
+ # - Append the percent-encoded version to the purl
321
+ case @version
322
+ in String => version unless version.empty?
323
+ purl += "@"
324
+ purl += URI.encode_www_form_component(@version)
325
+ else
326
+ nil
327
+ end
328
+
329
+ # If the qualifiers are not empty and not composed only of key/value pairs
330
+ # where the value is empty:
331
+ # - Append '?' to the purl
332
+ # - Build a list from all key/value pair:
333
+ # - discard any pair where the value is empty.
334
+ # - UTF-8-encode each value if needed in your programming language
335
+ # - If the key is checksums and this is a list of checksums
336
+ # join this list with a ',' to create this qualifier value
337
+ # - create a string by joining the lowercased key,
338
+ # the equal '=' sign and the percent-encoded value to create a qualifier
339
+ # - sort this list of qualifier strings lexicographically
340
+ # - join this list of qualifier strings with a '&' ampersand
341
+ # - Append this string to the purl
342
+ case @qualifiers
343
+ in Hash => qualifiers unless qualifiers.empty?
344
+ list = []
345
+ qualifiers.each do |key, value|
346
+ next if value.empty?
347
+
348
+ list << case [key, value]
349
+ in "checksums", Array => checksums
350
+ "#{key.downcase}=#{checksums.join(",")}"
351
+ else
352
+ "#{key.downcase}=#{URI.encode_www_form_component(value)}"
353
+ end
354
+ end
355
+
356
+ unless list.empty?
357
+ purl += "?"
358
+ purl += list.sort.join("&")
359
+ end
360
+ else
361
+ nil
362
+ end
363
+
364
+ # If the subpath is not empty and not composed only of
365
+ # empty, '.' and '..' segments:
366
+ # - Append '#' to the purl
367
+ # - Strip the subpath from leading and trailing '/'
368
+ # - Split this on '/' as segments
369
+ # - Discard empty, '.' and '..' segments
370
+ # - Percent-encode each segment
371
+ # - UTF-8-encode each segment if needed in your programming language
372
+ # - Join the segments with '/'
373
+ # - Append this to the purl
374
+ case @subpath
375
+ in String => subpath unless subpath.empty?
376
+ segments = []
377
+ subpath.delete_prefix("/").delete_suffix("/").split("/").each do |segment|
378
+ next if segment.empty? || segment == "." || segment == ".."
379
+
380
+ # Custom encoding for URL fragment segments:
381
+ # 1. Explicitly encode % as %25 to prevent double-encoding issues
382
+ # 2. Percent-encode special characters according to URL fragment rules
383
+ # 3. This ensures proper round-trip encoding/decoding with the parse method
384
+ segments << segment.gsub(/%|[^A-Za-z0-9\-\._~]/) do |m|
385
+ m == "%" ? "%25" : format("%%%02X", m.ord)
386
+ end
387
+ end
388
+
389
+ unless segments.empty?
390
+ purl += "#"
391
+ purl += segments.join("/")
392
+ end
393
+ else
394
+ nil
395
+ end
396
+
397
+ purl
398
+ end
399
+
400
+ # Returns an array containing the
401
+ # scheme, type, namespace, name, version, qualifiers, and subpath components
402
+ # of the package URL.
403
+ def deconstruct
404
+ [scheme, @type, @namespace, @name, @version, @qualifiers, @subpath]
405
+ end
406
+
407
+ # Returns a hash containing the
408
+ # scheme, type, namespace, name, version, qualifiers, and subpath components
409
+ # of the package URL.
410
+ def deconstruct_keys(_keys)
411
+ to_h
412
+ end
413
+ end
414
+ end
@@ -14,6 +14,12 @@ module RubyLsp
14
14
  fatal: Constant::DiagnosticSeverity::ERROR,
15
15
  }.freeze #: Hash[Symbol, Integer]
16
16
 
17
+ # Cops where adding a `rubocop:disable` inline comment would itself resolve the offense,
18
+ # causing Lint/RedundantCopDisableDirective to flag the disable as unnecessary.
19
+ SELF_RESOLVING_DISABLE_COPS = Set.new([
20
+ "Layout/EmptyComment",
21
+ ]).freeze #: Set[String]
22
+
17
23
  ENHANCED_DOC_URL = begin
18
24
  gem("rubocop", ">= 1.64.0")
19
25
  true
@@ -35,7 +41,7 @@ module RubyLsp
35
41
  code_actions = []
36
42
 
37
43
  code_actions << autocorrect_action if correctable?
38
- code_actions << disable_line_action
44
+ code_actions << disable_line_action unless SELF_RESOLVING_DISABLE_COPS.include?(@offense.cop_name)
39
45
 
40
46
  code_actions
41
47
  end
@@ -24,7 +24,7 @@ module RubyLsp
24
24
  filename = uri.to_standardized_path || uri.opaque #: as !nil
25
25
 
26
26
  # Invoke RuboCop with just this file in `paths`
27
- @format_runner.run(filename, document.source)
27
+ @format_runner.run(filename, document.source, document.parse_result)
28
28
  @format_runner.formatted_source
29
29
  end
30
30
 
@@ -40,7 +40,7 @@ module RubyLsp
40
40
  def run_diagnostic(uri, document)
41
41
  filename = uri.to_standardized_path || uri.opaque #: as !nil
42
42
  # Invoke RuboCop with just this file in `paths`
43
- @diagnostic_runner.run(filename, document.source)
43
+ @diagnostic_runner.run(filename, document.source, document.parse_result)
44
44
 
45
45
  @diagnostic_runner.offenses.map do |offense|
46
46
  Support::RuboCopDiagnostic.new(
@@ -81,6 +81,7 @@ module RubyLsp
81
81
  @offenses = [] #: Array[::RuboCop::Cop::Offense]
82
82
  @errors = [] #: Array[String]
83
83
  @warnings = [] #: Array[String]
84
+ # @prism_result = nil #: Prism::ParseLexResult?
84
85
 
85
86
  args += DEFAULT_ARGS
86
87
  rubocop_options = ::RuboCop::Options.new.parse(args).first
@@ -92,8 +93,8 @@ module RubyLsp
92
93
  super(rubocop_options, config_store)
93
94
  end
94
95
 
95
- #: (String path, String contents) -> void
96
- def run(path, contents)
96
+ #: (String, String, Prism::ParseLexResult) -> void
97
+ def run(path, contents, prism_result)
97
98
  # Clear Runner state between runs since we get a single instance of this class
98
99
  # on every use site.
99
100
  @errors = []
@@ -101,6 +102,11 @@ module RubyLsp
101
102
  @offenses = []
102
103
  @options[:stdin] = contents
103
104
 
105
+ # Setting the Prism result before running the RuboCop runner makes it reuse the existing AST and avoids
106
+ # double-parsing. Unfortunately, this leads to a bunch of cops failing to execute properly under LSP mode.
107
+ # Uncomment this once reusing the Prism result is more stable
108
+ # @prism_result = prism_result
109
+
104
110
  super([path])
105
111
 
106
112
  # RuboCop rescues interrupts and then sets the `@aborting` variable to true. We don't want them to be rescued,
@@ -111,7 +117,11 @@ module RubyLsp
111
117
  rescue ::RuboCop::ValidationError => error
112
118
  raise ConfigurationError, error.message
113
119
  rescue StandardError => error
114
- raise InternalRuboCopError, error
120
+ # Maintain the original backtrace so that debugging cops that are breaking is easier, but re-raise as a
121
+ # different error class
122
+ internal_error = InternalRuboCopError.new(error)
123
+ internal_error.set_backtrace(error.backtrace)
124
+ raise internal_error
115
125
  end
116
126
 
117
127
  #: -> String
@@ -5,6 +5,7 @@ require "uri/file"
5
5
 
6
6
  module URI
7
7
  # Must be kept in sync with the one in Tapioca
8
+ # https://github.com/Shopify/tapioca/blob/main/lib/tapioca/helpers/source_uri.rb
8
9
  class Source < URI::File
9
10
  COMPONENT = [
10
11
  :scheme,
@@ -21,14 +22,14 @@ module URI
21
22
  # have the uri gem in their own bundle and thus not use a compatible version.
22
23
  PARSER = const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER #: RFC2396_Parser
23
24
 
24
- T.unsafe(self).alias_method(:gem_name, :host)
25
- T.unsafe(self).alias_method(:line_number, :fragment)
25
+ alias_method(:gem_name, :host)
26
+ alias_method(:line_number, :fragment)
26
27
 
27
28
  #: String?
28
29
  attr_reader :gem_version
29
30
 
30
31
  class << self
31
- #: (gem_name: String, gem_version: String?, path: String, line_number: String?) -> URI::Source
32
+ #: (gem_name: String, gem_version: String?, path: String, line_number: String?) -> instance
32
33
  def build(gem_name:, gem_version:, path:, line_number:)
33
34
  super(
34
35
  {
@@ -65,12 +66,14 @@ module URI
65
66
 
66
67
  #: -> String
67
68
  def to_s
68
- "source://#{gem_name}/#{gem_version}#{path}##{line_number}"
69
+ "source://#{gem_name}/#{gem_version}/#{path}##{line_number}"
69
70
  end
70
71
 
71
72
  if URI.respond_to?(:register_scheme)
73
+ # Handle URI 0.11.0 and newer https://github.com/ruby/uri/pull/26
72
74
  URI.register_scheme("SOURCE", self)
73
75
  else
76
+ # Fallback for URI <0.11.0
74
77
  @@schemes = @@schemes #: Hash[String, untyped] # rubocop:disable Style/ClassVars
75
78
  @@schemes["SOURCE"] = self
76
79
  end
@@ -13,7 +13,13 @@ module RubyLsp
13
13
  #: String
14
14
  attr_reader :id, :label
15
15
 
16
- #: (String id, String label, URI::Generic uri, Interface::Range range, Symbol framework) -> void
16
+ #: URI::Generic
17
+ attr_reader :uri
18
+
19
+ #: Interface::Range
20
+ attr_reader :range
21
+
22
+ #: (String id, String label, URI::Generic uri, Interface::Range range, framework: Symbol) -> void
17
23
  def initialize(id, label, uri, range, framework:)
18
24
  @id = id
19
25
  @label = label
@@ -20,17 +20,7 @@ module RubyLsp
20
20
  # @override
21
21
  #: -> Array[Interface::WorkspaceSymbol]
22
22
  def perform
23
- @index.fuzzy_search(@query).filter_map do |entry|
24
- uri = entry.uri
25
- file_path = uri.full_path
26
-
27
- # We only show symbols declared in the workspace
28
- in_dependencies = file_path && !not_in_dependencies?(file_path)
29
- next if in_dependencies
30
-
31
- # We should never show private symbols when searching the entire workspace
32
- next if entry.private?
33
-
23
+ fuzzy_search.filter_map do |entry|
34
24
  kind = kind_for_entry(entry)
35
25
  loc = entry.location
36
26
 
@@ -44,7 +34,7 @@ module RubyLsp
44
34
  container_name: container.join("::"),
45
35
  kind: kind,
46
36
  location: Interface::Location.new(
47
- uri: uri.to_s,
37
+ uri: entry.uri.to_s,
48
38
  range: Interface::Range.new(
49
39
  start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column),
50
40
  end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
@@ -53,6 +43,24 @@ module RubyLsp
53
43
  )
54
44
  end
55
45
  end
46
+
47
+ private
48
+
49
+ #: -> Array[RubyIndexer::Entry]
50
+ def fuzzy_search
51
+ @index.fuzzy_search(@query) do |entry|
52
+ file_path = entry.uri.full_path
53
+
54
+ # We only show symbols declared in the workspace
55
+ in_dependencies = file_path && !not_in_dependencies?(file_path)
56
+ next if in_dependencies
57
+
58
+ # We should never show private symbols when searching the entire workspace
59
+ next if entry.private?
60
+
61
+ true
62
+ end
63
+ end
56
64
  end
57
65
  end
58
66
  end
@@ -3,11 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType < Object]
6
7
  class CollectionResponseBuilder < ResponseBuilder
7
- extend T::Generic
8
-
9
- ResponseType = type_member { { upper: Object } }
10
-
11
8
  #: -> void
12
9
  def initialize
13
10
  super
@@ -3,9 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType = Array[Interface::DocumentSymbol]]
6
7
  class DocumentSymbol < ResponseBuilder
7
- ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
8
-
9
8
  class SymbolHierarchyRoot
10
9
  #: Array[Interface::DocumentSymbol]
11
10
  attr_reader :children
@@ -32,7 +31,7 @@ module RubyLsp
32
31
  #: -> Interface::DocumentSymbol?
33
32
  def pop
34
33
  if @stack.size > 1
35
- T.cast(@stack.pop, Interface::DocumentSymbol)
34
+ @stack.pop #: as Interface::DocumentSymbol
36
35
  end
37
36
  end
38
37
 
@@ -3,11 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType = String]
6
7
  class Hover < ResponseBuilder
7
- extend T::Generic
8
-
9
- ResponseType = type_member { { fixed: String } }
10
-
11
8
  #: -> void
12
9
  def initialize
13
10
  super
@@ -3,14 +3,13 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ # @abstract
6
7
  class ResponseBuilder
7
- extend T::Generic
8
- extend T::Sig
9
-
10
- abstract!
11
-
12
- sig { abstract.returns(T.anything) }
13
- def response; end
8
+ # @abstract
9
+ #: -> top
10
+ def response
11
+ raise AbstractMethodInvokedError
12
+ end
14
13
  end
15
14
  end
16
15
  end