ruby-lsp 0.23.10 → 0.23.14

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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-launcher +12 -11
  5. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
  6. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +81 -115
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +117 -166
  9. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -7
  10. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +89 -201
  11. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +63 -192
  12. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  13. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +14 -16
  14. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +22 -45
  15. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +42 -60
  16. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
  17. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
  18. data/lib/ruby_indexer/test/classes_and_modules_test.rb +75 -0
  19. data/lib/ruby_indexer/test/configuration_test.rb +42 -3
  20. data/lib/ruby_indexer/test/index_test.rb +21 -0
  21. data/lib/ruby_indexer/test/method_test.rb +28 -2
  22. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  23. data/lib/ruby_indexer/test/uri_test.rb +15 -2
  24. data/lib/ruby_lsp/addon.rb +44 -71
  25. data/lib/ruby_lsp/base_server.rb +31 -33
  26. data/lib/ruby_lsp/client_capabilities.rb +10 -12
  27. data/lib/ruby_lsp/document.rb +34 -45
  28. data/lib/ruby_lsp/erb_document.rb +24 -36
  29. data/lib/ruby_lsp/global_state.rb +51 -56
  30. data/lib/ruby_lsp/internal.rb +6 -0
  31. data/lib/ruby_lsp/listeners/code_lens.rb +81 -88
  32. data/lib/ruby_lsp/listeners/completion.rb +36 -55
  33. data/lib/ruby_lsp/listeners/definition.rb +37 -51
  34. data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
  35. data/lib/ruby_lsp/listeners/document_link.rb +43 -62
  36. data/lib/ruby_lsp/listeners/document_symbol.rb +35 -49
  37. data/lib/ruby_lsp/listeners/folding_ranges.rb +32 -39
  38. data/lib/ruby_lsp/listeners/hover.rb +81 -100
  39. data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
  40. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +42 -51
  41. data/lib/ruby_lsp/listeners/signature_help.rb +6 -25
  42. data/lib/ruby_lsp/listeners/spec_style.rb +155 -0
  43. data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
  44. data/lib/ruby_lsp/listeners/test_style.rb +236 -0
  45. data/lib/ruby_lsp/node_context.rb +12 -39
  46. data/lib/ruby_lsp/rbs_document.rb +8 -6
  47. data/lib/ruby_lsp/requests/code_action_resolve.rb +10 -10
  48. data/lib/ruby_lsp/requests/code_actions.rb +14 -26
  49. data/lib/ruby_lsp/requests/code_lens.rb +6 -17
  50. data/lib/ruby_lsp/requests/completion.rb +7 -20
  51. data/lib/ruby_lsp/requests/completion_resolve.rb +5 -5
  52. data/lib/ruby_lsp/requests/definition.rb +8 -17
  53. data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
  54. data/lib/ruby_lsp/requests/discover_tests.rb +75 -0
  55. data/lib/ruby_lsp/requests/document_highlight.rb +5 -15
  56. data/lib/ruby_lsp/requests/document_link.rb +6 -17
  57. data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
  58. data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
  59. data/lib/ruby_lsp/requests/formatting.rb +6 -9
  60. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
  61. data/lib/ruby_lsp/requests/hover.rb +8 -18
  62. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
  63. data/lib/ruby_lsp/requests/on_type_formatting.rb +28 -38
  64. data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
  65. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +4 -13
  66. data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
  67. data/lib/ruby_lsp/requests/references.rb +6 -36
  68. data/lib/ruby_lsp/requests/rename.rb +11 -37
  69. data/lib/ruby_lsp/requests/request.rb +7 -19
  70. data/lib/ruby_lsp/requests/selection_ranges.rb +5 -5
  71. data/lib/ruby_lsp/requests/semantic_highlighting.rb +12 -31
  72. data/lib/ruby_lsp/requests/show_syntax_tree.rb +5 -6
  73. data/lib/ruby_lsp/requests/signature_help.rb +8 -26
  74. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  75. data/lib/ruby_lsp/requests/support/common.rb +13 -48
  76. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
  77. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +9 -12
  78. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +22 -34
  79. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  80. data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
  81. data/lib/ruby_lsp/requests/support/source_uri.rb +16 -30
  82. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
  83. data/lib/ruby_lsp/requests/support/test_item.rb +55 -0
  84. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
  85. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
  86. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +5 -5
  87. data/lib/ruby_lsp/response_builders/document_symbol.rb +10 -16
  88. data/lib/ruby_lsp/response_builders/hover.rb +10 -13
  89. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  90. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +59 -87
  91. data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
  92. data/lib/ruby_lsp/response_builders/test_collection.rb +34 -0
  93. data/lib/ruby_lsp/ruby_document.rb +22 -60
  94. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +109 -0
  95. data/lib/ruby_lsp/scope.rb +7 -11
  96. data/lib/ruby_lsp/server.rb +177 -72
  97. data/lib/ruby_lsp/setup_bundler.rb +61 -59
  98. data/lib/ruby_lsp/static_docs.rb +4 -7
  99. data/lib/ruby_lsp/store.rb +21 -40
  100. data/lib/ruby_lsp/test_helper.rb +2 -12
  101. data/lib/ruby_lsp/test_reporter.rb +207 -0
  102. data/lib/ruby_lsp/test_unit_test_runner.rb +98 -0
  103. data/lib/ruby_lsp/type_inferrer.rb +9 -13
  104. data/lib/ruby_lsp/utils.rb +37 -81
  105. metadata +13 -3
@@ -1,35 +1,33 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "uri"
5
+
4
6
  module URI
5
7
  class Generic
6
- extend T::Sig
7
-
8
8
  # Avoid a deprecation warning with Ruby 3.4 where the default parser was changed to RFC3986.
9
9
  # This condition must remain even after support for 3.4 has been dropped for users that have
10
10
  # `uri` in their lockfile, decoupling it from the ruby version.
11
- PARSER = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser)
12
11
 
13
- class << self
14
- extend T::Sig
12
+ # NOTE: We also define this in the shim
13
+ PARSER = const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER
15
14
 
16
- sig do
17
- params(
18
- path: String,
19
- fragment: T.nilable(String),
20
- scheme: String,
21
- load_path_entry: T.nilable(String),
22
- ).returns(URI::Generic)
23
- end
15
+ class << self
16
+ #: (path: String, ?fragment: String?, ?scheme: String, ?load_path_entry: String?) -> URI::Generic
24
17
  def from_path(path:, fragment: nil, scheme: "file", load_path_entry: nil)
18
+ # This unsafe regex is the same one used in the URI::RFC2396_REGEXP class with the exception of the fact that we
19
+ # do not include colon as a safe character. VS Code URIs always escape colons and we need to ensure we do the
20
+ # same to avoid inconsistencies in our URIs, which are used to identify resources
21
+ unsafe_regex = %r{[^\-_.!~*'()a-zA-Z\d;/?@&=+$,\[\]]}
22
+
25
23
  # On Windows, if the path begins with the disk name, we need to add a leading slash to make it a valid URI
26
24
  escaped_path = if /^[A-Z]:/i.match?(path)
27
- PARSER.escape("/#{path}")
25
+ PARSER.escape("/#{path}", unsafe_regex)
28
26
  elsif path.start_with?("//?/")
29
27
  # Some paths on Windows start with "//?/". This is a special prefix that allows for long file paths
30
- PARSER.escape(path.delete_prefix("//?"))
28
+ PARSER.escape(path.delete_prefix("//?"), unsafe_regex)
31
29
  else
32
- PARSER.escape(path)
30
+ PARSER.escape(path, unsafe_regex)
33
31
  end
34
32
 
35
33
  uri = build(scheme: scheme, path: escaped_path, fragment: fragment)
@@ -42,10 +40,10 @@ module URI
42
40
  end
43
41
  end
44
42
 
45
- sig { returns(T.nilable(String)) }
43
+ #: String?
46
44
  attr_accessor :require_path
47
45
 
48
- sig { params(load_path_entry: String).void }
46
+ #: (String load_path_entry) -> void
49
47
  def add_require_path_from_load_entry(load_path_entry)
50
48
  path = to_standardized_path
51
49
  return unless path
@@ -53,7 +51,7 @@ module URI
53
51
  self.require_path = path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb")
54
52
  end
55
53
 
56
- sig { returns(T.nilable(String)) }
54
+ #: -> String?
57
55
  def to_standardized_path
58
56
  parsed_path = path
59
57
  return unless parsed_path
@@ -5,29 +5,25 @@ module RubyIndexer
5
5
  # Represents the visibility scope in a Ruby namespace. This keeps track of whether methods are in a public, private or
6
6
  # protected section, and whether they are module functions.
7
7
  class VisibilityScope
8
- extend T::Sig
9
-
10
8
  class << self
11
- extend T::Sig
12
-
13
- sig { returns(T.attached_class) }
9
+ #: -> instance
14
10
  def module_function_scope
15
11
  new(module_func: true, visibility: Entry::Visibility::PRIVATE)
16
12
  end
17
13
 
18
- sig { returns(T.attached_class) }
14
+ #: -> instance
19
15
  def public_scope
20
16
  new
21
17
  end
22
18
  end
23
19
 
24
- sig { returns(Entry::Visibility) }
20
+ #: Entry::Visibility
25
21
  attr_reader :visibility
26
22
 
27
- sig { returns(T::Boolean) }
23
+ #: bool
28
24
  attr_reader :module_func
29
25
 
30
- sig { params(visibility: Entry::Visibility, module_func: T::Boolean).void }
26
+ #: (?visibility: Entry::Visibility, ?module_func: bool) -> void
31
27
  def initialize(visibility: Entry::Visibility::PUBLIC, module_func: false)
32
28
  @visibility = visibility
33
29
  @module_func = module_func
@@ -666,5 +666,80 @@ module RubyIndexer
666
666
  method = @index["baz"]&.first
667
667
  assert_equal("Foo::Bar::<Class:Bar>", method.owner.name)
668
668
  end
669
+
670
+ def test_lazy_comments_with_spaces_are_properly_attributed
671
+ path = File.join(Dir.pwd, "lib", "foo.rb")
672
+ source = <<~RUBY
673
+ require "whatever"
674
+
675
+ # These comments belong to the declaration below
676
+ # They have to be associated with it
677
+
678
+ class Foo
679
+ end
680
+ RUBY
681
+ File.write(path, source)
682
+ @index.index_single(URI::Generic.from_path(path: path), source, collect_comments: false)
683
+
684
+ entry = @index["Foo"].first
685
+
686
+ begin
687
+ assert_equal(<<~COMMENTS.chomp, entry.comments)
688
+ These comments belong to the declaration below
689
+ They have to be associated with it
690
+ COMMENTS
691
+ ensure
692
+ FileUtils.rm(path)
693
+ end
694
+ end
695
+
696
+ def test_lazy_comments_with_no_spaces_are_properly_attributed
697
+ path = File.join(Dir.pwd, "lib", "foo.rb")
698
+ source = <<~RUBY
699
+ require "whatever"
700
+
701
+ # These comments belong to the declaration below
702
+ # They have to be associated with it
703
+ class Foo
704
+ end
705
+ RUBY
706
+ File.write(path, source)
707
+ @index.index_single(URI::Generic.from_path(path: path), source, collect_comments: false)
708
+
709
+ entry = @index["Foo"].first
710
+
711
+ begin
712
+ assert_equal(<<~COMMENTS.chomp, entry.comments)
713
+ These comments belong to the declaration below
714
+ They have to be associated with it
715
+ COMMENTS
716
+ ensure
717
+ FileUtils.rm(path)
718
+ end
719
+ end
720
+
721
+ def test_lazy_comments_with_two_extra_spaces_are_properly_ignored
722
+ path = File.join(Dir.pwd, "lib", "foo.rb")
723
+ source = <<~RUBY
724
+ require "whatever"
725
+
726
+ # These comments don't belong to the declaration below
727
+ # They will not be associated with it
728
+
729
+
730
+ class Foo
731
+ end
732
+ RUBY
733
+ File.write(path, source)
734
+ @index.index_single(URI::Generic.from_path(path: path), source, collect_comments: false)
735
+
736
+ entry = @index["Foo"].first
737
+
738
+ begin
739
+ assert_empty(entry.comments)
740
+ ensure
741
+ FileUtils.rm(path)
742
+ end
743
+ end
669
744
  end
670
745
  end
@@ -12,7 +12,7 @@ module RubyIndexer
12
12
  end
13
13
 
14
14
  def test_load_configuration_executes_configure_block
15
- @config.apply_config({ "excluded_patterns" => ["**/fixtures/**/*.rb"] })
15
+ @config.apply_config({ "excluded_patterns" => ["**/fixtures/**/*"] })
16
16
  uris = @config.indexable_uris
17
17
 
18
18
  bundle_path = Bundler.bundle_path.join("gems")
@@ -39,7 +39,11 @@ module RubyIndexer
39
39
  next if lazy_spec.name == "ruby-lsp"
40
40
 
41
41
  spec = Gem::Specification.find_by_name(lazy_spec.name)
42
- assert(uris.none? { |uri| uri.full_path.start_with?("#{spec.full_gem_path}/test/") })
42
+
43
+ test_uris = uris.select do |uri|
44
+ File.fnmatch?(File.join(spec.full_gem_path, "test/**/*"), uri.full_path, File::Constants::FNM_PATHNAME)
45
+ end
46
+ assert_empty(test_uris)
43
47
  rescue Gem::MissingSpecError
44
48
  # Transitive dependencies might be missing when running tests on Windows
45
49
  end
@@ -143,17 +147,26 @@ module RubyIndexer
143
147
  uris = @config.indexable_uris
144
148
  assert(uris.none? { |uri| uri.full_path.start_with?(File.join(dir, "ignore")) })
145
149
 
150
+ # The regular default gem path is ~/.rubies/3.4.1/lib/ruby/3.4.0
151
+ # The alternative default gem path is ~/.rubies/3.4.1/lib/ruby/gems/3.4.0
152
+ # Here part_1 contains ~/.rubies/3.4.1/lib/ruby/ and part_2 contains 3.4.0, so that we can turn it into the
153
+ # alternative path
154
+ part_1, part_2 = Pathname.new(RbConfig::CONFIG["rubylibdir"]).split
155
+ other_default_gem_dir = part_1.join("gems").join(part_2).to_s
156
+
146
157
  # After switching the workspace path, all indexable URIs will be found in one of these places:
147
158
  # - The new workspace path
148
159
  # - The Ruby LSP's own code (because Bundler is requiring the dependency from source)
149
160
  # - Bundled gems
150
161
  # - Default gems
162
+ # - Other default gem directory
151
163
  assert(
152
164
  uris.all? do |u|
153
165
  u.full_path.start_with?(dir) ||
154
166
  u.full_path.start_with?(File.join(Dir.pwd, "lib")) ||
155
167
  u.full_path.start_with?(Bundler.bundle_path.to_s) ||
156
- u.full_path.start_with?(RbConfig::CONFIG["rubylibdir"])
168
+ u.full_path.start_with?(RbConfig::CONFIG["rubylibdir"]) ||
169
+ u.full_path.start_with?(other_default_gem_dir)
157
170
  end,
158
171
  )
159
172
  end
@@ -235,5 +248,31 @@ module RubyIndexer
235
248
  end
236
249
  end
237
250
  end
251
+
252
+ def test_indexables_include_non_test_files_in_test_directories
253
+ # In order to linearize test parent classes and accurately detect the framework being used, then intermediate
254
+ # parent classes _must_ also be indexed. Otherwise, we have no way of linearizing the rest of the ancestors to
255
+ # determine what the test class ultimately inherits from.
256
+ #
257
+ # Therefore, we need to ensure that test files are excluded, but non test files inside test directories have to be
258
+ # indexed
259
+ FileUtils.touch("test/test_case.rb")
260
+
261
+ uris = @config.indexable_uris
262
+ project_paths = uris.filter_map do |uri|
263
+ path = uri.full_path
264
+ next if path.start_with?(Bundler.bundle_path.to_s) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
265
+
266
+ Pathname.new(path).relative_path_from(Dir.pwd).to_s
267
+ end
268
+
269
+ begin
270
+ assert_includes(project_paths, "test/requests/support/expectations_test_runner.rb")
271
+ assert_includes(project_paths, "test/test_helper.rb")
272
+ assert_includes(project_paths, "test/test_case.rb")
273
+ ensure
274
+ FileUtils.rm("test/test_case.rb")
275
+ end
276
+ end
238
277
  end
239
278
  end
@@ -2161,5 +2161,26 @@ module RubyIndexer
2161
2161
 
2162
2162
  assert_equal("@@hello", candidates.first&.name)
2163
2163
  end
2164
+
2165
+ def test_actual_nesting
2166
+ assert_equal(["Foo"], Index.actual_nesting([], "Foo"))
2167
+ assert_equal(["TopLevel", "Foo"], Index.actual_nesting(["First", "::TopLevel"], "Foo"))
2168
+ assert_equal(["TopLevel", "Another", "Foo"], Index.actual_nesting(["::TopLevel", "Another"], "Foo"))
2169
+ assert_equal(["TopLevel"], Index.actual_nesting(["First", "::TopLevel"], nil))
2170
+ end
2171
+
2172
+ def test_constant_name
2173
+ node = Prism.parse("class var::Foo; end").value.statements.body.first.constant_path
2174
+ assert_nil(Index.constant_name(node))
2175
+
2176
+ node = Prism.parse("class ; end").value.statements.body.first.constant_path
2177
+ assert_nil(Index.constant_name(node))
2178
+
2179
+ node = Prism.parse("class method_call; end").value.statements.body.first.constant_path
2180
+ assert_nil(Index.constant_name(node))
2181
+
2182
+ node = Prism.parse("class Foo; end").value.statements.body.first.constant_path
2183
+ assert_equal("Foo", Index.constant_name(node))
2184
+ end
2164
2185
  end
2165
2186
  end
@@ -924,16 +924,42 @@ module RubyIndexer
924
924
  RUBY
925
925
  end
926
926
 
927
+ def test_changing_visibility_post_definition
928
+ index(<<~RUBY)
929
+ class Foo
930
+ def bar; end
931
+ private :bar
932
+
933
+ def baz; end
934
+ protected :baz
935
+
936
+ private
937
+ def qux; end
938
+
939
+ public :qux
940
+ end
941
+ RUBY
942
+
943
+ entry = T.must(@index["bar"].first)
944
+ assert_predicate(entry, :private?)
945
+
946
+ entry = T.must(@index["baz"].first)
947
+ assert_predicate(entry, :protected?)
948
+
949
+ entry = T.must(@index["qux"].first)
950
+ assert_predicate(entry, :public?)
951
+ end
952
+
927
953
  private
928
954
 
929
- sig { params(entry: Entry::Method, call_string: String).void }
955
+ #: (Entry::Method entry, String call_string) -> void
930
956
  def assert_signature_matches(entry, call_string)
931
957
  sig = T.must(entry.signatures.first)
932
958
  arguments = parse_prism_args(call_string)
933
959
  assert(sig.matches?(arguments), "Expected #{call_string} to match #{entry.name}#{entry.decorated_parameters}")
934
960
  end
935
961
 
936
- sig { params(entry: Entry::Method, call_string: String).void }
962
+ #: (Entry::Method entry, String call_string) -> void
937
963
  def refute_signature_matches(entry, call_string)
938
964
  sig = T.must(entry.signatures.first)
939
965
  arguments = parse_prism_args(call_string)
@@ -359,7 +359,7 @@ module RubyIndexer
359
359
  assert_equal("all?", entry.old_name)
360
360
  assert_equal("Array", entry.owner.name)
361
361
  assert(entry.file_path.end_with?("core/array.rbs"))
362
- assert_includes(entry.comments, "Returns `true` if any element of `self` meets a given criterion.")
362
+ refute_empty(entry.comments)
363
363
  end
364
364
 
365
365
  def test_indexing_untyped_functions
@@ -12,12 +12,12 @@ module RubyIndexer
12
12
 
13
13
  def test_from_path_on_windows
14
14
  uri = URI::Generic.from_path(path: "C:/some/windows/path/to/file.rb")
15
- assert_equal("/C:/some/windows/path/to/file.rb", uri.path)
15
+ assert_equal("/C%3A/some/windows/path/to/file.rb", uri.path)
16
16
  end
17
17
 
18
18
  def test_from_path_on_windows_with_lowercase_drive
19
19
  uri = URI::Generic.from_path(path: "c:/some/windows/path/to/file.rb")
20
- assert_equal("/c:/some/windows/path/to/file.rb", uri.path)
20
+ assert_equal("/c%3A/some/windows/path/to/file.rb", uri.path)
21
21
  end
22
22
 
23
23
  def test_to_standardized_path_on_unix
@@ -68,5 +68,18 @@ module RubyIndexer
68
68
  uri.add_require_path_from_load_entry("/some/unix/path")
69
69
  assert_equal("to/file", uri.require_path)
70
70
  end
71
+
72
+ def test_from_path_escapes_colon_characters
73
+ uri = URI::Generic.from_path(path: "c:/some/windows/path with/spaces/file.rb")
74
+ assert_equal("c:/some/windows/path with/spaces/file.rb", uri.to_standardized_path)
75
+ assert_equal("file:///c%3A/some/windows/path%20with/spaces/file.rb", uri.to_s)
76
+ end
77
+
78
+ def test_from_path_with_unicode_characters
79
+ path = "/path/with/unicode/文件.rb"
80
+ uri = URI::Generic.from_path(path: path)
81
+ assert_equal(path, uri.to_standardized_path)
82
+ assert_equal("file:///path/with/unicode/%E6%96%87%E4%BB%B6.rb", uri.to_s)
83
+ end
71
84
  end
72
85
  end
@@ -25,42 +25,34 @@ module RubyLsp
25
25
 
26
26
  abstract!
27
27
 
28
- @addons = T.let([], T::Array[Addon])
29
- @addon_classes = T.let([], T::Array[T.class_of(Addon)])
28
+ @addons = [] #: Array[Addon]
29
+ @addon_classes = [] #: Array[singleton(Addon)]
30
30
  # Add-on instances that have declared a handler to accept file watcher events
31
- @file_watcher_addons = T.let([], T::Array[Addon])
31
+ @file_watcher_addons = [] #: Array[Addon]
32
32
 
33
33
  AddonNotFoundError = Class.new(StandardError)
34
34
 
35
35
  class IncompatibleApiError < StandardError; end
36
36
 
37
37
  class << self
38
- extend T::Sig
39
-
40
- sig { returns(T::Array[Addon]) }
38
+ #: Array[Addon]
41
39
  attr_accessor :addons
42
40
 
43
- sig { returns(T::Array[Addon]) }
41
+ #: Array[Addon]
44
42
  attr_accessor :file_watcher_addons
45
43
 
46
- sig { returns(T::Array[T.class_of(Addon)]) }
44
+ #: Array[singleton(Addon)]
47
45
  attr_reader :addon_classes
48
46
 
49
47
  # Automatically track and instantiate add-on classes
50
- sig { params(child_class: T.class_of(Addon)).void }
48
+ #: (singleton(Addon) child_class) -> void
51
49
  def inherited(child_class)
52
50
  addon_classes << child_class
53
51
  super
54
52
  end
55
53
 
56
54
  # Discovers and loads all add-ons. Returns a list of errors when trying to require add-ons
57
- sig do
58
- params(
59
- global_state: GlobalState,
60
- outgoing_queue: Thread::Queue,
61
- include_project_addons: T::Boolean,
62
- ).returns(T::Array[StandardError])
63
- end
55
+ #: (GlobalState global_state, Thread::Queue outgoing_queue, ?include_project_addons: bool) -> Array[StandardError]
64
56
  def load_addons(global_state, outgoing_queue, include_project_addons: true)
65
57
  # Require all add-ons entry points, which should be placed under
66
58
  # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb` or in the workspace under
@@ -98,7 +90,7 @@ module RubyLsp
98
90
  end
99
91
 
100
92
  # Unloads all add-ons. Only intended to be invoked once when shutting down the Ruby LSP server
101
- sig { void }
93
+ #: -> void
102
94
  def unload_addons
103
95
  @addons.each(&:deactivate)
104
96
  @addons.clear
@@ -112,7 +104,7 @@ module RubyLsp
112
104
  # Important: if the add-on is not found, AddonNotFoundError will be raised. If the add-on is found, but its
113
105
  # current version does not satisfy the given version constraint, then IncompatibleApiError will be raised. It is
114
106
  # the responsibility of the add-ons using this API to handle these errors appropriately.
115
- sig { params(addon_name: String, version_constraints: String).returns(Addon) }
107
+ #: (String addon_name, *String version_constraints) -> Addon
116
108
  def get(addon_name, *version_constraints)
117
109
  if version_constraints.empty?
118
110
  raise IncompatibleApiError, "Must specify version constraints when accessing other add-ons"
@@ -144,7 +136,7 @@ module RubyLsp
144
136
  # end
145
137
  # end
146
138
  # ```
147
- sig { params(version_constraints: String).void }
139
+ #: (*String version_constraints) -> void
148
140
  def depend_on_ruby_lsp!(*version_constraints)
149
141
  version_object = Gem::Version.new(RubyLsp::VERSION)
150
142
 
@@ -155,23 +147,23 @@ module RubyLsp
155
147
  end
156
148
  end
157
149
 
158
- sig { void }
150
+ #: -> void
159
151
  def initialize
160
- @errors = T.let([], T::Array[StandardError])
152
+ @errors = [] #: Array[StandardError]
161
153
  end
162
154
 
163
- sig { params(error: StandardError).returns(T.self_type) }
155
+ #: (StandardError error) -> self
164
156
  def add_error(error)
165
157
  @errors << error
166
158
  self
167
159
  end
168
160
 
169
- sig { returns(T::Boolean) }
161
+ #: -> bool
170
162
  def error?
171
163
  @errors.any?
172
164
  end
173
165
 
174
- sig { returns(String) }
166
+ #: -> String
175
167
  def formatted_errors
176
168
  <<~ERRORS
177
169
  #{name}:
@@ -179,7 +171,7 @@ module RubyLsp
179
171
  ERRORS
180
172
  end
181
173
 
182
- sig { returns(String) }
174
+ #: -> String
183
175
  def errors_details
184
176
  @errors.map(&:full_message).join("\n\n")
185
177
  end
@@ -207,69 +199,50 @@ module RubyLsp
207
199
  # original request so that the response is delegated to the correct add-on and must override this method to handle
208
200
  # the response
209
201
  # https://microsoft.github.io/language-server-protocol/specification#window_showMessageRequest
210
- sig { overridable.params(title: String).void }
202
+ # @overridable
203
+ #: (String title) -> void
211
204
  def handle_window_show_message_response(title); end
212
205
 
213
206
  # Creates a new CodeLens listener. This method is invoked on every CodeLens request
214
- sig do
215
- overridable.params(
216
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
217
- uri: URI::Generic,
218
- dispatcher: Prism::Dispatcher,
219
- ).void
220
- end
207
+ # @overridable
208
+ #: (ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens] response_builder, URI::Generic uri, Prism::Dispatcher dispatcher) -> void
221
209
  def create_code_lens_listener(response_builder, uri, dispatcher); end
222
210
 
223
211
  # Creates a new Hover listener. This method is invoked on every Hover request
224
- sig do
225
- overridable.params(
226
- response_builder: ResponseBuilders::Hover,
227
- node_context: NodeContext,
228
- dispatcher: Prism::Dispatcher,
229
- ).void
230
- end
212
+ # @overridable
213
+ #: (ResponseBuilders::Hover response_builder, NodeContext node_context, Prism::Dispatcher dispatcher) -> void
231
214
  def create_hover_listener(response_builder, node_context, dispatcher); end
232
215
 
233
216
  # Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
234
- sig do
235
- overridable.params(
236
- response_builder: ResponseBuilders::DocumentSymbol,
237
- dispatcher: Prism::Dispatcher,
238
- ).void
239
- end
217
+ # @overridable
218
+ #: (ResponseBuilders::DocumentSymbol response_builder, Prism::Dispatcher dispatcher) -> void
240
219
  def create_document_symbol_listener(response_builder, dispatcher); end
241
220
 
242
- sig do
243
- overridable.params(
244
- response_builder: ResponseBuilders::SemanticHighlighting,
245
- dispatcher: Prism::Dispatcher,
246
- ).void
247
- end
221
+ # @overridable
222
+ #: (ResponseBuilders::SemanticHighlighting response_builder, Prism::Dispatcher dispatcher) -> void
248
223
  def create_semantic_highlighting_listener(response_builder, dispatcher); end
249
224
 
250
225
  # Creates a new Definition listener. This method is invoked on every Definition request
251
- sig do
252
- overridable.params(
253
- response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
254
- Interface::Location,
255
- Interface::LocationLink,
256
- )],
257
- uri: URI::Generic,
258
- node_context: NodeContext,
259
- dispatcher: Prism::Dispatcher,
260
- ).void
261
- end
226
+ # @overridable
227
+ #: (ResponseBuilders::CollectionResponseBuilder[(Interface::Location | Interface::LocationLink)] response_builder, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher) -> void
262
228
  def create_definition_listener(response_builder, uri, node_context, dispatcher); end
263
229
 
264
230
  # Creates a new Completion listener. This method is invoked on every Completion request
265
- sig do
266
- overridable.params(
267
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
268
- node_context: NodeContext,
269
- dispatcher: Prism::Dispatcher,
270
- uri: URI::Generic,
271
- ).void
272
- end
231
+ # @overridable
232
+ #: (ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem] response_builder, NodeContext node_context, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
273
233
  def create_completion_listener(response_builder, node_context, dispatcher, uri); end
234
+
235
+ # Creates a new Discover Tests listener. This method is invoked on every DiscoverTests request
236
+ # @overridable
237
+ #: (ResponseBuilders::TestCollection response_builder, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
238
+ def create_discover_tests_listener(response_builder, dispatcher, uri); end
239
+
240
+ # Resolves the minimal set of commands required to execute the requested tests. Add-ons are responsible for only
241
+ # handling items related to the framework they add support for and have discovered themselves
242
+ # @overridable
243
+ #: (Array[Hash[Symbol, untyped]]) -> Array[String]
244
+ def resolve_test_commands(items)
245
+ []
246
+ end
274
247
  end
275
248
  end