ruby-lsp 0.22.0 → 0.23.0

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 (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"