ruby-lsp 0.22.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +10 -9
  4. data/exe/ruby-lsp-check +5 -5
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +131 -23
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +73 -55
  9. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
  10. data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -1
  12. data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
  13. data/lib/ruby_indexer/test/classes_and_modules_test.rb +11 -6
  14. data/lib/ruby_indexer/test/configuration_test.rb +116 -51
  15. data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
  16. data/lib/ruby_indexer/test/index_test.rb +72 -43
  17. data/lib/ruby_indexer/test/method_test.rb +80 -0
  18. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  19. data/lib/ruby_indexer/test/reference_finder_test.rb +1 -1
  20. data/lib/ruby_indexer/test/test_case.rb +2 -2
  21. data/lib/ruby_indexer/test/uri_test.rb +72 -0
  22. data/lib/ruby_lsp/addon.rb +9 -0
  23. data/lib/ruby_lsp/base_server.rb +15 -6
  24. data/lib/ruby_lsp/document.rb +10 -1
  25. data/lib/ruby_lsp/global_state.rb +1 -1
  26. data/lib/ruby_lsp/internal.rb +1 -1
  27. data/lib/ruby_lsp/listeners/code_lens.rb +8 -4
  28. data/lib/ruby_lsp/listeners/completion.rb +73 -4
  29. data/lib/ruby_lsp/listeners/definition.rb +73 -17
  30. data/lib/ruby_lsp/listeners/document_symbol.rb +49 -5
  31. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  32. data/lib/ruby_lsp/listeners/hover.rb +57 -0
  33. data/lib/ruby_lsp/requests/completion.rb +6 -0
  34. data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
  35. data/lib/ruby_lsp/requests/definition.rb +6 -0
  36. data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
  37. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
  38. data/lib/ruby_lsp/requests/rename.rb +14 -4
  39. data/lib/ruby_lsp/requests/support/common.rb +1 -5
  40. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  41. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -2
  42. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  43. data/lib/ruby_lsp/server.rb +42 -7
  44. data/lib/ruby_lsp/setup_bundler.rb +54 -46
  45. data/lib/ruby_lsp/test_helper.rb +45 -11
  46. data/lib/ruby_lsp/type_inferrer.rb +22 -0
  47. data/lib/ruby_lsp/utils.rb +3 -0
  48. metadata +7 -8
  49. data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
@@ -6,11 +6,11 @@ require_relative "test_case"
6
6
  module RubyIndexer
7
7
  class IndexTest < TestCase
8
8
  def test_deleting_one_entry_for_a_class
9
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
9
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
10
10
  class Foo
11
11
  end
12
12
  RUBY
13
- @index.index_single(IndexablePath.new(nil, "/fake/path/other_foo.rb"), <<~RUBY)
13
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/other_foo.rb"), <<~RUBY)
14
14
  class Foo
15
15
  end
16
16
  RUBY
@@ -18,13 +18,13 @@ module RubyIndexer
18
18
  entries = @index["Foo"]
19
19
  assert_equal(2, entries.length)
20
20
 
21
- @index.delete(IndexablePath.new(nil, "/fake/path/other_foo.rb"))
21
+ @index.delete(URI::Generic.from_path(path: "/fake/path/other_foo.rb"))
22
22
  entries = @index["Foo"]
23
23
  assert_equal(1, entries.length)
24
24
  end
25
25
 
26
26
  def test_deleting_all_entries_for_a_class
27
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
27
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
28
28
  class Foo
29
29
  end
30
30
  RUBY
@@ -32,13 +32,13 @@ module RubyIndexer
32
32
  entries = @index["Foo"]
33
33
  assert_equal(1, entries.length)
34
34
 
35
- @index.delete(IndexablePath.new(nil, "/fake/path/foo.rb"))
35
+ @index.delete(URI::Generic.from_path(path: "/fake/path/foo.rb"))
36
36
  entries = @index["Foo"]
37
37
  assert_nil(entries)
38
38
  end
39
39
 
40
40
  def test_index_resolve
41
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
41
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
42
42
  class Bar; end
43
43
 
44
44
  module Foo
@@ -72,7 +72,7 @@ module RubyIndexer
72
72
  end
73
73
 
74
74
  def test_accessing_with_colon_colon_prefix
75
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
75
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
76
76
  class Bar; end
77
77
 
78
78
  module Foo
@@ -92,7 +92,7 @@ module RubyIndexer
92
92
  end
93
93
 
94
94
  def test_fuzzy_search
95
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
95
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
96
96
  class Zws; end
97
97
 
98
98
  module Qtl
@@ -120,18 +120,22 @@ module RubyIndexer
120
120
  end
121
121
 
122
122
  def test_index_single_ignores_directories
123
- FileUtils.mkdir("lib/this_is_a_dir.rb")
124
- @index.index_single(IndexablePath.new(nil, "lib/this_is_a_dir.rb"))
125
- ensure
126
- FileUtils.rm_r("lib/this_is_a_dir.rb")
123
+ path = "#{Dir.pwd}/lib/this_is_a_dir.rb"
124
+ FileUtils.mkdir(path)
125
+
126
+ begin
127
+ @index.index_file(URI::Generic.from_path(path: path))
128
+ ensure
129
+ FileUtils.rm_r(path)
130
+ end
127
131
  end
128
132
 
129
133
  def test_searching_for_require_paths
130
- @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
134
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY)
131
135
  class Foo
132
136
  end
133
137
  RUBY
134
- @index.index_single(IndexablePath.new("/fake", "/fake/path/other_foo.rb"), <<~RUBY)
138
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/other_foo.rb", load_path_entry: "/fake"), <<~RUBY)
135
139
  class Foo
136
140
  end
137
141
  RUBY
@@ -140,11 +144,11 @@ module RubyIndexer
140
144
  end
141
145
 
142
146
  def test_searching_for_entries_based_on_prefix
143
- @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
147
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY)
144
148
  class Foo::Bizw
145
149
  end
146
150
  RUBY
147
- @index.index_single(IndexablePath.new("/fake", "/fake/path/other_foo.rb"), <<~RUBY)
151
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/other_foo.rb", load_path_entry: "/fake"), <<~RUBY)
148
152
  class Foo::Bizw
149
153
  end
150
154
 
@@ -160,7 +164,7 @@ module RubyIndexer
160
164
  end
161
165
 
162
166
  def test_resolve_normalizes_top_level_names
163
- @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
167
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY)
164
168
  class Bar; end
165
169
 
166
170
  module Foo
@@ -180,7 +184,7 @@ module RubyIndexer
180
184
  end
181
185
 
182
186
  def test_resolving_aliases_to_non_existing_constants_with_conflicting_names
183
- @index.index_single(IndexablePath.new("/fake", "/fake/path/foo.rb"), <<~RUBY)
187
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb", load_path_entry: "/fake"), <<~RUBY)
184
188
  class Bar
185
189
  end
186
190
 
@@ -343,18 +347,18 @@ module RubyIndexer
343
347
  raise "Prism fixtures not found. Run `git submodule update --init` to fetch them."
344
348
  end
345
349
 
346
- fixtures = Dir.glob("test/fixtures/prism/test/prism/fixtures/**/*.txt")
350
+ fixtures = Dir.glob("#{Dir.pwd}/test/fixtures/prism/test/prism/fixtures/**/*.txt")
347
351
 
348
352
  fixtures.each do |fixture|
349
- indexable_path = IndexablePath.new("", fixture)
350
- @index.index_single(indexable_path)
353
+ uri = URI::Generic.from_path(path: fixture)
354
+ @index.index_file(uri)
351
355
  end
352
356
 
353
357
  refute_empty(@index)
354
358
  end
355
359
 
356
360
  def test_index_single_does_not_fail_for_non_existing_file
357
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"))
361
+ @index.index_file(URI::Generic.from_path(path: "/fake/path/foo.rb"))
358
362
  entries_after_indexing = @index.names
359
363
  assert_equal(@default_indexed_entries.keys, entries_after_indexing)
360
364
  end
@@ -782,8 +786,8 @@ module RubyIndexer
782
786
  end
783
787
  RUBY
784
788
 
785
- indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
786
- @index.index_single(indexable_path)
789
+ uri = URI::Generic.from_path(path: File.join(dir, "foo.rb"))
790
+ @index.index_file(uri)
787
791
 
788
792
  assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
789
793
 
@@ -796,7 +800,7 @@ module RubyIndexer
796
800
  end
797
801
  RUBY
798
802
 
799
- @index.handle_change(indexable_path)
803
+ @index.handle_change(uri, File.read(T.must(uri.full_path)))
800
804
  assert_empty(@index.instance_variable_get(:@ancestors))
801
805
  assert_equal(["Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
802
806
  end
@@ -816,8 +820,8 @@ module RubyIndexer
816
820
  end
817
821
  RUBY
818
822
 
819
- indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
820
- @index.index_single(indexable_path)
823
+ uri = URI::Generic.from_path(path: File.join(dir, "foo.rb"))
824
+ @index.index_file(uri)
821
825
 
822
826
  assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
823
827
 
@@ -833,7 +837,7 @@ module RubyIndexer
833
837
  end
834
838
  RUBY
835
839
 
836
- @index.handle_change(indexable_path)
840
+ @index.handle_change(uri, File.read(T.must(uri.full_path)))
837
841
  refute_empty(@index.instance_variable_get(:@ancestors))
838
842
  assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
839
843
  end
@@ -852,8 +856,8 @@ module RubyIndexer
852
856
  end
853
857
  RUBY
854
858
 
855
- indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
856
- @index.index_single(indexable_path)
859
+ uri = URI::Generic.from_path(path: File.join(dir, "foo.rb"))
860
+ @index.index_file(uri)
857
861
 
858
862
  assert_equal(["Bar", "Foo", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
859
863
 
@@ -866,7 +870,7 @@ module RubyIndexer
866
870
  end
867
871
  RUBY
868
872
 
869
- @index.handle_change(indexable_path)
873
+ @index.handle_change(uri, File.read(T.must(uri.full_path)))
870
874
  assert_empty(@index.instance_variable_get(:@ancestors))
871
875
  assert_equal(["Bar", "Object", "Kernel", "BasicObject"], @index.linearized_ancestors_of("Bar"))
872
876
  end
@@ -1300,7 +1304,7 @@ module RubyIndexer
1300
1304
  end
1301
1305
 
1302
1306
  def test_resolving_method_inside_singleton_context
1303
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1307
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1304
1308
  module Foo
1305
1309
  class Bar
1306
1310
  class << self
@@ -1321,7 +1325,7 @@ module RubyIndexer
1321
1325
  end
1322
1326
 
1323
1327
  def test_resolving_constants_in_singleton_contexts
1324
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1328
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1325
1329
  module Foo
1326
1330
  class Bar
1327
1331
  CONST = 3
@@ -1346,7 +1350,7 @@ module RubyIndexer
1346
1350
  end
1347
1351
 
1348
1352
  def test_resolving_instance_variables_in_singleton_contexts
1349
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1353
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1350
1354
  module Foo
1351
1355
  class Bar
1352
1356
  @a = 123
@@ -1376,7 +1380,7 @@ module RubyIndexer
1376
1380
  end
1377
1381
 
1378
1382
  def test_instance_variable_completion_in_singleton_contexts
1379
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1383
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1380
1384
  module Foo
1381
1385
  class Bar
1382
1386
  @a = 123
@@ -1622,7 +1626,7 @@ module RubyIndexer
1622
1626
  end
1623
1627
 
1624
1628
  def test_linearizing_singleton_ancestors_of_singleton_when_class_has_parent
1625
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1629
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1626
1630
  class Foo; end
1627
1631
 
1628
1632
  class Bar < Foo
@@ -1673,7 +1677,7 @@ module RubyIndexer
1673
1677
  end
1674
1678
 
1675
1679
  def test_extend_self
1676
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1680
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1677
1681
  module Foo
1678
1682
  def bar
1679
1683
  end
@@ -1705,7 +1709,7 @@ module RubyIndexer
1705
1709
  end
1706
1710
 
1707
1711
  def test_linearizing_singleton_ancestors
1708
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1712
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1709
1713
  module First
1710
1714
  end
1711
1715
 
@@ -1746,7 +1750,7 @@ module RubyIndexer
1746
1750
  end
1747
1751
 
1748
1752
  def test_linearizing_singleton_ancestors_when_class_has_parent
1749
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1753
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1750
1754
  class Foo; end
1751
1755
 
1752
1756
  class Bar < Foo
@@ -1776,7 +1780,7 @@ module RubyIndexer
1776
1780
  end
1777
1781
 
1778
1782
  def test_linearizing_a_module_singleton_class
1779
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1783
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1780
1784
  module A; end
1781
1785
  RUBY
1782
1786
 
@@ -1885,13 +1889,13 @@ module RubyIndexer
1885
1889
  end
1886
1890
  RUBY
1887
1891
 
1888
- entries = @index.entries_for("/fake/path/foo.rb", Entry)
1892
+ entries = @index.entries_for("file:///fake/path/foo.rb", Entry)
1889
1893
  assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
1890
1894
 
1891
- entries = @index.entries_for("/fake/path/foo.rb", RubyIndexer::Entry::Namespace)
1895
+ entries = @index.entries_for("file:///fake/path/foo.rb", RubyIndexer::Entry::Namespace)
1892
1896
  assert_equal(["Foo", "Bar", "Bar::<Class:Bar>"], entries.map(&:name))
1893
1897
 
1894
- entries = @index.entries_for("/fake/path/foo.rb")
1898
+ entries = @index.entries_for("file:///fake/path/foo.rb")
1895
1899
  assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
1896
1900
  end
1897
1901
 
@@ -2062,5 +2066,30 @@ module RubyIndexer
2062
2066
  @index.index_all
2063
2067
  end
2064
2068
  end
2069
+
2070
+ def test_index_can_handle_entries_from_untitled_scheme
2071
+ uri = URI("untitled:Untitled-1")
2072
+
2073
+ index(<<~RUBY, uri: uri)
2074
+ class Foo
2075
+ end
2076
+ RUBY
2077
+
2078
+ entry = @index["Foo"]&.first
2079
+ refute_nil(entry, "Expected indexer to be able to handle unsaved URIs")
2080
+ assert_equal("untitled:Untitled-1", entry.uri.to_s)
2081
+ assert_equal("Untitled-1", entry.file_name)
2082
+ assert_nil(entry.file_path)
2083
+
2084
+ @index.handle_change(uri, <<~RUBY)
2085
+ # I added this comment!
2086
+ class Foo
2087
+ end
2088
+ RUBY
2089
+
2090
+ entry = @index["Foo"]&.first
2091
+ refute_nil(entry, "Expected indexer to be able to handle unsaved URIs")
2092
+ assert_equal("I added this comment!", entry.comments)
2093
+ end
2065
2094
  end
2066
2095
  end
@@ -149,6 +149,86 @@ module RubyIndexer
149
149
  end
150
150
  end
151
151
 
152
+ def test_private_class_method_visibility_tracking_string_symbol_arguments
153
+ index(<<~RUBY)
154
+ class Test
155
+ def self.foo
156
+ end
157
+
158
+ def self.bar
159
+ end
160
+
161
+ private_class_method("foo", :bar)
162
+
163
+ def self.baz
164
+ end
165
+ end
166
+ RUBY
167
+
168
+ ["foo", "bar"].each do |keyword|
169
+ entries = T.must(@index[keyword])
170
+ assert_equal(1, entries.size)
171
+ entry = entries.first
172
+ assert_predicate(entry, :private?)
173
+ end
174
+
175
+ entries = T.must(@index["baz"])
176
+ assert_equal(1, entries.size)
177
+ entry = entries.first
178
+ assert_predicate(entry, :public?)
179
+ end
180
+
181
+ def test_private_class_method_visibility_tracking_array_argument
182
+ index(<<~RUBY)
183
+ class Test
184
+ def self.foo
185
+ end
186
+
187
+ def self.bar
188
+ end
189
+
190
+ private_class_method(["foo", :bar])
191
+
192
+ def self.baz
193
+ end
194
+ end
195
+ RUBY
196
+
197
+ ["foo", "bar"].each do |keyword|
198
+ entries = T.must(@index[keyword])
199
+ assert_equal(1, entries.size)
200
+ entry = entries.first
201
+ assert_predicate(entry, :private?)
202
+ end
203
+
204
+ entries = T.must(@index["baz"])
205
+ assert_equal(1, entries.size)
206
+ entry = entries.first
207
+ assert_predicate(entry, :public?)
208
+ end
209
+
210
+ def test_private_class_method_visibility_tracking_method_argument
211
+ index(<<~RUBY)
212
+ class Test
213
+ private_class_method def self.foo
214
+ end
215
+
216
+ def self.bar
217
+ end
218
+ end
219
+ RUBY
220
+
221
+ entries = T.must(@index["foo"])
222
+ assert_equal(1, entries.size)
223
+ entry = entries.first
224
+ assert_predicate(entry, :private?)
225
+
226
+ entries = T.must(@index["bar"])
227
+ assert_equal(1, entries.size)
228
+ entry = entries.first
229
+ assert_predicate(entry, :public?)
230
+ end
231
+
152
232
  def test_comments_documentation
153
233
  index(<<~RUBY)
154
234
  # Documentation for Foo
@@ -377,7 +377,7 @@ module RubyIndexer
377
377
  _, _, declarations = RBS::Parser.parse_signature(buffer)
378
378
  index = RubyIndexer::Index.new
379
379
  indexer = RubyIndexer::RBSIndexer.new(index)
380
- pathname = Pathname.new("file.rbs")
380
+ pathname = Pathname.new("/file.rbs")
381
381
  indexer.process_signature(pathname, declarations)
382
382
  entry = T.must(index[method_name]).first
383
383
  T.cast(entry, Entry::Method).signatures
@@ -231,7 +231,7 @@ module RubyIndexer
231
231
  def find_references(target, source)
232
232
  file_path = "/fake.rb"
233
233
  index = Index.new
234
- index.index_single(IndexablePath.new(nil, file_path), source)
234
+ index.index_single(URI::Generic.from_path(path: file_path), source)
235
235
  parse_result = Prism.parse(source)
236
236
  dispatcher = Prism::Dispatcher.new
237
237
  finder = ReferenceFinder.new(target, index, dispatcher)
@@ -13,8 +13,8 @@ module RubyIndexer
13
13
 
14
14
  private
15
15
 
16
- def index(source)
17
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), source)
16
+ def index(source, uri: URI::Generic.from_path(path: "/fake/path/foo.rb"))
17
+ @index.index_single(uri, source)
18
18
  end
19
19
 
20
20
  def assert_entry(expected_name, type, expected_location, visibility: nil)
@@ -0,0 +1,72 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "test_helper"
5
+
6
+ module RubyIndexer
7
+ class URITest < Minitest::Test
8
+ def test_from_path_on_unix
9
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
10
+ assert_equal("/some/unix/path/to/file.rb", uri.path)
11
+ end
12
+
13
+ def test_from_path_on_windows
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)
16
+ end
17
+
18
+ def test_from_path_on_windows_with_lowercase_drive
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)
21
+ end
22
+
23
+ def test_to_standardized_path_on_unix
24
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
25
+ assert_equal(uri.path, uri.to_standardized_path)
26
+ end
27
+
28
+ def test_to_standardized_path_on_windows
29
+ uri = URI::Generic.from_path(path: "C:/some/windows/path/to/file.rb")
30
+ assert_equal("C:/some/windows/path/to/file.rb", uri.to_standardized_path)
31
+ end
32
+
33
+ def test_to_standardized_path_on_windows_with_lowercase_drive
34
+ uri = URI::Generic.from_path(path: "c:/some/windows/path/to/file.rb")
35
+ assert_equal("c:/some/windows/path/to/file.rb", uri.to_standardized_path)
36
+ end
37
+
38
+ def test_to_standardized_path_on_windows_with_received_uri
39
+ uri = URI("file:///c%3A/some/windows/path/to/file.rb")
40
+ assert_equal("c:/some/windows/path/to/file.rb", uri.to_standardized_path)
41
+ end
42
+
43
+ def test_plus_signs_are_properly_unescaped
44
+ path = "/opt/rubies/3.3.0/lib/ruby/3.3.0+0/pathname.rb"
45
+ uri = URI::Generic.from_path(path: path)
46
+ assert_equal(path, uri.to_standardized_path)
47
+ end
48
+
49
+ def test_from_path_with_fragment
50
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb", fragment: "L1,3-2,9")
51
+ assert_equal("file:///some/unix/path/to/file.rb#L1,3-2,9", uri.to_s)
52
+ end
53
+
54
+ def test_from_path_windows_long_file_paths
55
+ uri = URI::Generic.from_path(path: "//?/C:/hostedtoolcache/windows/Ruby/3.3.1/x64/lib/ruby/3.3.0/open-uri.rb")
56
+ assert_equal("C:/hostedtoolcache/windows/Ruby/3.3.1/x64/lib/ruby/3.3.0/open-uri.rb", uri.to_standardized_path)
57
+ end
58
+
59
+ def test_from_path_computes_require_path_when_load_path_entry_is_given
60
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb", load_path_entry: "/some/unix/path")
61
+ assert_equal("to/file", uri.require_path)
62
+ end
63
+
64
+ def test_allows_adding_require_path_with_load_path_entry
65
+ uri = URI::Generic.from_path(path: "/some/unix/path/to/file.rb")
66
+ assert_nil(uri.require_path)
67
+
68
+ uri.add_require_path_from_load_entry("/some/unix/path")
69
+ assert_equal("to/file", uri.require_path)
70
+ end
71
+ end
72
+ end
@@ -97,6 +97,15 @@ module RubyLsp
97
97
  errors
98
98
  end
99
99
 
100
+ # Unloads all add-ons. Only intended to be invoked once when shutting down the Ruby LSP server
101
+ sig { void }
102
+ def unload_addons
103
+ @addons.each(&:deactivate)
104
+ @addons.clear
105
+ @addon_classes.clear
106
+ @file_watcher_addons.clear
107
+ end
108
+
100
109
  # Get a reference to another add-on object by name and version. If an add-on exports an API that can be used by
101
110
  # other add-ons, this is the way to get access to that API.
102
111
  #
@@ -91,14 +91,13 @@ module RubyLsp
91
91
  # The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
92
92
  # else is pushed into the incoming queue
93
93
  case method
94
- when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
94
+ when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange",
95
+ "$/cancelRequest"
95
96
  process_message(message)
96
97
  when "shutdown"
97
- send_log_message("Shutting down Ruby LSP...")
98
-
99
- shutdown
100
-
101
98
  @mutex.synchronize do
99
+ send_log_message("Shutting down Ruby LSP...")
100
+ shutdown
102
101
  run_shutdown
103
102
  @writer.write(Result.new(id: message[:id], response: nil).to_hash)
104
103
  end
@@ -133,6 +132,12 @@ module RubyLsp
133
132
  @outgoing_queue.pop
134
133
  end
135
134
 
135
+ # This method is only intended to be used in tests! Pushes a message to the incoming queue directly
136
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
137
+ def push_message(message)
138
+ @incoming_queue << message
139
+ end
140
+
136
141
  sig { abstract.params(message: T::Hash[Symbol, T.untyped]).void }
137
142
  def process_message(message); end
138
143
 
@@ -154,7 +159,11 @@ module RubyLsp
154
159
  # Check if the request was cancelled before trying to process it
155
160
  @mutex.synchronize do
156
161
  if id && @cancelled_requests.include?(id)
157
- send_message(Result.new(id: id, response: nil))
162
+ send_message(Error.new(
163
+ id: id,
164
+ code: Constant::ErrorCodes::REQUEST_CANCELLED,
165
+ message: "Request #{id} was cancelled",
166
+ ))
158
167
  @cancelled_requests.delete(id)
159
168
  next
160
169
  end
@@ -15,6 +15,7 @@ module RubyLsp
15
15
  extend T::Helpers
16
16
  extend T::Generic
17
17
 
18
+ class LocationNotFoundError < StandardError; end
18
19
  ParseResultType = type_member
19
20
 
20
21
  # This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
@@ -144,7 +145,15 @@ module RubyLsp
144
145
  def find_char_position(position)
145
146
  # Find the character index for the beginning of the requested line
146
147
  until @current_line == position[:line]
147
- @pos += 1 until LINE_BREAK == @source[@pos]
148
+ until LINE_BREAK == @source[@pos]
149
+ @pos += 1
150
+
151
+ if @pos >= @source.length
152
+ # Pack the code points back into the original string to provide context in the error message
153
+ raise LocationNotFoundError, "Requested position: #{position}\nSource:\n\n#{@source.pack("U*")}"
154
+ end
155
+ end
156
+
148
157
  @pos += 1
149
158
  @current_line += 1
150
159
  end
@@ -146,7 +146,7 @@ module RubyLsp
146
146
 
147
147
  sig { params(flag: Symbol).returns(T.nilable(T::Boolean)) }
148
148
  def enabled_feature?(flag)
149
- @enabled_feature_flags[flag]
149
+ @enabled_feature_flags[:all] || @enabled_feature_flags[flag]
150
150
  end
151
151
 
152
152
  sig { returns(String) }
@@ -26,7 +26,6 @@ require "fileutils"
26
26
  require "ruby-lsp"
27
27
  require "ruby_lsp/base_server"
28
28
  require "ruby_indexer/ruby_indexer"
29
- require "core_ext/uri"
30
29
  require "ruby_lsp/utils"
31
30
  require "ruby_lsp/static_docs"
32
31
  require "ruby_lsp/scope"
@@ -78,6 +77,7 @@ require "ruby_lsp/requests/hover"
78
77
  require "ruby_lsp/requests/inlay_hints"
79
78
  require "ruby_lsp/requests/on_type_formatting"
80
79
  require "ruby_lsp/requests/prepare_type_hierarchy"
80
+ require "ruby_lsp/requests/prepare_rename"
81
81
  require "ruby_lsp/requests/range_formatting"
82
82
  require "ruby_lsp/requests/references"
83
83
  require "ruby_lsp/requests/rename"
@@ -15,7 +15,7 @@ module RubyLsp
15
15
  "bundle exec ruby"
16
16
  rescue Bundler::GemfileNotFound
17
17
  "ruby"
18
- end + " -Itest ",
18
+ end,
19
19
  String,
20
20
  )
21
21
  ACCESS_MODIFIERS = T.let([:public, :private, :protected], T::Array[Symbol])
@@ -198,7 +198,7 @@ module RubyLsp
198
198
 
199
199
  @response_builder << create_code_lens(
200
200
  node,
201
- title: "Run",
201
+ title: "Run",
202
202
  command_name: "rubyLsp.runTest",
203
203
  arguments: arguments,
204
204
  data: { type: "test", **grouping_data },
@@ -206,7 +206,7 @@ module RubyLsp
206
206
 
207
207
  @response_builder << create_code_lens(
208
208
  node,
209
- title: "Run In Terminal",
209
+ title: "Run In Terminal",
210
210
  command_name: "rubyLsp.runTestInTerminal",
211
211
  arguments: arguments,
212
212
  data: { type: "test_in_terminal", **grouping_data },
@@ -229,7 +229,11 @@ module RubyLsp
229
229
  ).returns(String)
230
230
  end
231
231
  def generate_test_command(group_stack: [], spec_name: nil, method_name: nil)
232
- command = BASE_COMMAND + T.must(@path)
232
+ path = T.must(@path)
233
+ command = BASE_COMMAND
234
+ command += " -Itest" if File.fnmatch?("**/test/**/*", path, File::FNM_PATHNAME)
235
+ command += " -Ispec" if File.fnmatch?("**/spec/**/*", path, File::FNM_PATHNAME)
236
+ command += " #{path}"
233
237
 
234
238
  case @global_state.test_library
235
239
  when "minitest"