ruby-lsp 0.22.1 → 0.23.10

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +12 -11
  5. data/exe/ruby-lsp-check +5 -5
  6. data/exe/ruby-lsp-launcher +41 -15
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +191 -100
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +174 -61
  11. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +12 -0
  12. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
  13. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +82 -61
  14. data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
  15. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
  16. data/lib/ruby_indexer/ruby_indexer.rb +2 -1
  17. data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
  18. data/lib/ruby_indexer/test/classes_and_modules_test.rb +30 -6
  19. data/lib/ruby_indexer/test/configuration_test.rb +116 -51
  20. data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
  21. data/lib/ruby_indexer/test/index_test.rb +143 -44
  22. data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
  23. data/lib/ruby_indexer/test/method_test.rb +86 -8
  24. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  25. data/lib/ruby_indexer/test/reference_finder_test.rb +90 -2
  26. data/lib/ruby_indexer/test/test_case.rb +2 -2
  27. data/lib/ruby_indexer/test/uri_test.rb +72 -0
  28. data/lib/ruby_lsp/addon.rb +9 -0
  29. data/lib/ruby_lsp/base_server.rb +17 -18
  30. data/lib/ruby_lsp/client_capabilities.rb +7 -1
  31. data/lib/ruby_lsp/document.rb +72 -10
  32. data/lib/ruby_lsp/erb_document.rb +5 -3
  33. data/lib/ruby_lsp/global_state.rb +42 -3
  34. data/lib/ruby_lsp/internal.rb +3 -1
  35. data/lib/ruby_lsp/listeners/code_lens.rb +9 -5
  36. data/lib/ruby_lsp/listeners/completion.rb +78 -6
  37. data/lib/ruby_lsp/listeners/definition.rb +80 -19
  38. data/lib/ruby_lsp/listeners/document_highlight.rb +3 -2
  39. data/lib/ruby_lsp/listeners/document_link.rb +21 -3
  40. data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
  41. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  42. data/lib/ruby_lsp/listeners/hover.rb +59 -2
  43. data/lib/ruby_lsp/load_sorbet.rb +3 -3
  44. data/lib/ruby_lsp/rbs_document.rb +2 -2
  45. data/lib/ruby_lsp/requests/code_action_resolve.rb +90 -6
  46. data/lib/ruby_lsp/requests/code_actions.rb +57 -1
  47. data/lib/ruby_lsp/requests/completion.rb +8 -1
  48. data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
  49. data/lib/ruby_lsp/requests/definition.rb +7 -1
  50. data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
  51. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  52. data/lib/ruby_lsp/requests/folding_ranges.rb +2 -6
  53. data/lib/ruby_lsp/requests/formatting.rb +2 -6
  54. data/lib/ruby_lsp/requests/hover.rb +1 -1
  55. data/lib/ruby_lsp/requests/on_type_formatting.rb +2 -2
  56. data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
  57. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
  58. data/lib/ruby_lsp/requests/references.rb +29 -2
  59. data/lib/ruby_lsp/requests/rename.rb +17 -7
  60. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  61. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
  62. data/lib/ruby_lsp/requests/signature_help.rb +1 -1
  63. data/lib/ruby_lsp/requests/support/common.rb +2 -9
  64. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +3 -3
  65. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -13
  66. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  67. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -3
  68. data/lib/ruby_lsp/ruby_document.rb +80 -6
  69. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  70. data/lib/ruby_lsp/server.rb +205 -61
  71. data/lib/ruby_lsp/setup_bundler.rb +50 -43
  72. data/lib/ruby_lsp/store.rb +7 -7
  73. data/lib/ruby_lsp/test_helper.rb +45 -11
  74. data/lib/ruby_lsp/type_inferrer.rb +60 -31
  75. data/lib/ruby_lsp/utils.rb +63 -3
  76. metadata +8 -8
  77. 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
@@ -1557,6 +1561,23 @@ module RubyIndexer
1557
1561
  assert_equal("Foo::Bar", entry.name)
1558
1562
  end
1559
1563
 
1564
+ def test_first_unqualified_const_prefers_exact_matches
1565
+ index(<<~RUBY)
1566
+ module Foo
1567
+ class ParseResultType
1568
+ end
1569
+ end
1570
+
1571
+ module Namespace
1572
+ class Type
1573
+ end
1574
+ end
1575
+ RUBY
1576
+
1577
+ entry = T.must(@index.first_unqualified_const("Type")&.first)
1578
+ assert_equal("Namespace::Type", entry.name)
1579
+ end
1580
+
1560
1581
  def test_completion_does_not_duplicate_overridden_methods
1561
1582
  index(<<~RUBY)
1562
1583
  class Foo
@@ -1622,7 +1643,7 @@ module RubyIndexer
1622
1643
  end
1623
1644
 
1624
1645
  def test_linearizing_singleton_ancestors_of_singleton_when_class_has_parent
1625
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1646
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1626
1647
  class Foo; end
1627
1648
 
1628
1649
  class Bar < Foo
@@ -1673,7 +1694,7 @@ module RubyIndexer
1673
1694
  end
1674
1695
 
1675
1696
  def test_extend_self
1676
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1697
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1677
1698
  module Foo
1678
1699
  def bar
1679
1700
  end
@@ -1705,7 +1726,7 @@ module RubyIndexer
1705
1726
  end
1706
1727
 
1707
1728
  def test_linearizing_singleton_ancestors
1708
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1729
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1709
1730
  module First
1710
1731
  end
1711
1732
 
@@ -1746,7 +1767,7 @@ module RubyIndexer
1746
1767
  end
1747
1768
 
1748
1769
  def test_linearizing_singleton_ancestors_when_class_has_parent
1749
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1770
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1750
1771
  class Foo; end
1751
1772
 
1752
1773
  class Bar < Foo
@@ -1776,7 +1797,7 @@ module RubyIndexer
1776
1797
  end
1777
1798
 
1778
1799
  def test_linearizing_a_module_singleton_class
1779
- @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
1800
+ @index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), <<~RUBY)
1780
1801
  module A; end
1781
1802
  RUBY
1782
1803
 
@@ -1885,13 +1906,13 @@ module RubyIndexer
1885
1906
  end
1886
1907
  RUBY
1887
1908
 
1888
- entries = @index.entries_for("/fake/path/foo.rb", Entry)
1909
+ entries = @index.entries_for("file:///fake/path/foo.rb", Entry)
1889
1910
  assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
1890
1911
 
1891
- entries = @index.entries_for("/fake/path/foo.rb", RubyIndexer::Entry::Namespace)
1912
+ entries = @index.entries_for("file:///fake/path/foo.rb", RubyIndexer::Entry::Namespace)
1892
1913
  assert_equal(["Foo", "Bar", "Bar::<Class:Bar>"], entries.map(&:name))
1893
1914
 
1894
- entries = @index.entries_for("/fake/path/foo.rb")
1915
+ entries = @index.entries_for("file:///fake/path/foo.rb")
1895
1916
  assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
1896
1917
  end
1897
1918
 
@@ -2057,10 +2078,88 @@ module RubyIndexer
2057
2078
  end
2058
2079
 
2059
2080
  def test_prevents_multiple_calls_to_index_all
2060
- # For this test class, `index_all` is already called once in `setup`.
2081
+ @index.index_all
2082
+
2061
2083
  assert_raises(Index::IndexNotEmptyError) do
2062
2084
  @index.index_all
2063
2085
  end
2064
2086
  end
2087
+
2088
+ def test_index_can_handle_entries_from_untitled_scheme
2089
+ uri = URI("untitled:Untitled-1")
2090
+
2091
+ index(<<~RUBY, uri: uri)
2092
+ class Foo
2093
+ end
2094
+ RUBY
2095
+
2096
+ entry = @index["Foo"]&.first
2097
+ refute_nil(entry, "Expected indexer to be able to handle unsaved URIs")
2098
+ assert_equal("untitled:Untitled-1", entry.uri.to_s)
2099
+ assert_equal("Untitled-1", entry.file_name)
2100
+ assert_nil(entry.file_path)
2101
+
2102
+ @index.handle_change(uri, <<~RUBY)
2103
+ # I added this comment!
2104
+ class Foo
2105
+ end
2106
+ RUBY
2107
+
2108
+ entry = @index["Foo"]&.first
2109
+ refute_nil(entry, "Expected indexer to be able to handle unsaved URIs")
2110
+ assert_equal("I added this comment!", entry.comments)
2111
+ end
2112
+
2113
+ def test_instance_variable_completion_returns_class_variables_too
2114
+ index(<<~RUBY)
2115
+ class Parent
2116
+ @@abc = 123
2117
+ end
2118
+
2119
+ class Child < Parent
2120
+ @@adf = 123
2121
+
2122
+ def self.do
2123
+ end
2124
+ end
2125
+ RUBY
2126
+
2127
+ abc, adf = @index.instance_variable_completion_candidates("@", "Child::<Class:Child>")
2128
+
2129
+ refute_nil(abc)
2130
+ refute_nil(adf)
2131
+
2132
+ assert_equal("@@abc", abc.name)
2133
+ assert_equal("@@adf", adf.name)
2134
+ end
2135
+
2136
+ def test_class_variable_completion_from_singleton_context
2137
+ index(<<~RUBY)
2138
+ class Foo
2139
+ @@hello = 123
2140
+
2141
+ def self.do
2142
+ end
2143
+ end
2144
+ RUBY
2145
+
2146
+ candidates = @index.class_variable_completion_candidates("@@", "Foo::<Class:Foo>")
2147
+ refute_empty(candidates)
2148
+
2149
+ assert_equal("@@hello", candidates.first&.name)
2150
+ end
2151
+
2152
+ def test_resolve_class_variable_in_singleton_context
2153
+ index(<<~RUBY)
2154
+ class Foo
2155
+ @@hello = 123
2156
+ end
2157
+ RUBY
2158
+
2159
+ candidates = @index.resolve_class_variable("@@hello", "Foo::<Class:Foo>")
2160
+ refute_empty(candidates)
2161
+
2162
+ assert_equal("@@hello", candidates.first&.name)
2163
+ end
2065
2164
  end
2066
2165
  end
@@ -216,5 +216,25 @@ module RubyIndexer
216
216
  assert_instance_of(Entry::Class, owner)
217
217
  assert_equal("Foo", owner.name)
218
218
  end
219
+
220
+ def test_module_function_does_not_impact_instance_variables
221
+ # One possible way of implementing `module_function` would be to push a fake singleton class to the stack, so that
222
+ # methods are inserted into it. However, that would be incorrect because it would then bind instance variables to
223
+ # the wrong type. This test is here to prevent that from happening.
224
+ index(<<~RUBY)
225
+ module Foo
226
+ module_function
227
+
228
+ def something; end
229
+
230
+ @a = 123
231
+ end
232
+ RUBY
233
+
234
+ entry = T.must(@index["@a"]&.first)
235
+ owner = T.must(entry.owner)
236
+ assert_instance_of(Entry::SingletonClass, owner)
237
+ assert_equal("Foo::<Class:Foo>", owner.name)
238
+ end
219
239
  end
220
240
  end
@@ -88,19 +88,21 @@ module RubyIndexer
88
88
 
89
89
  def test_visibility_tracking
90
90
  index(<<~RUBY)
91
- private def foo
92
- end
91
+ class Foo
92
+ private def foo
93
+ end
93
94
 
94
- def bar; end
95
+ def bar; end
95
96
 
96
- protected
97
+ protected
97
98
 
98
- def baz; end
99
+ def baz; end
100
+ end
99
101
  RUBY
100
102
 
101
- assert_entry("foo", Entry::Method, "/fake/path/foo.rb:0-8:1-3", visibility: Entry::Visibility::PRIVATE)
102
- assert_entry("bar", Entry::Method, "/fake/path/foo.rb:3-0:3-12", visibility: Entry::Visibility::PUBLIC)
103
- assert_entry("baz", Entry::Method, "/fake/path/foo.rb:7-0:7-12", visibility: Entry::Visibility::PROTECTED)
103
+ assert_entry("foo", Entry::Method, "/fake/path/foo.rb:1-10:2-5", visibility: Entry::Visibility::PRIVATE)
104
+ assert_entry("bar", Entry::Method, "/fake/path/foo.rb:4-2:4-14", visibility: Entry::Visibility::PUBLIC)
105
+ assert_entry("baz", Entry::Method, "/fake/path/foo.rb:8-2:8-14", visibility: Entry::Visibility::PROTECTED)
104
106
  end
105
107
 
106
108
  def test_visibility_tracking_with_nested_class_or_modules
@@ -846,6 +848,82 @@ module RubyIndexer
846
848
  assert_signature_matches(entry, "baz(1)")
847
849
  end
848
850
 
851
+ def test_module_function_with_no_arguments
852
+ index(<<~RUBY)
853
+ module Foo
854
+ def bar; end
855
+
856
+ module_function
857
+
858
+ def baz; end
859
+ attr_reader :attribute
860
+
861
+ public
862
+
863
+ def qux; end
864
+ end
865
+ RUBY
866
+
867
+ entry = T.must(@index["bar"].first)
868
+ assert_predicate(entry, :public?)
869
+ assert_equal("Foo", T.must(entry.owner).name)
870
+
871
+ instance_baz, singleton_baz = T.must(@index["baz"])
872
+ assert_predicate(instance_baz, :private?)
873
+ assert_equal("Foo", T.must(instance_baz.owner).name)
874
+
875
+ assert_predicate(singleton_baz, :public?)
876
+ assert_equal("Foo::<Class:Foo>", T.must(singleton_baz.owner).name)
877
+
878
+ # After invoking `public`, the state of `module_function` is reset
879
+ instance_qux, singleton_qux = T.must(@index["qux"])
880
+ assert_nil(singleton_qux)
881
+ assert_predicate(instance_qux, :public?)
882
+ assert_equal("Foo", T.must(instance_baz.owner).name)
883
+
884
+ # Attributes are not turned into class methods, they do become private
885
+ instance_attribute, singleton_attribute = @index["attribute"]
886
+ assert_nil(singleton_attribute)
887
+ assert_equal("Foo", T.must(instance_attribute.owner).name)
888
+ assert_predicate(instance_attribute, :private?)
889
+ end
890
+
891
+ def test_module_function_does_nothing_in_classes
892
+ # Invoking `module_function` in a class raises an error. We simply ignore it
893
+ index(<<~RUBY)
894
+ class Foo
895
+ def bar; end
896
+
897
+ module_function
898
+
899
+ def baz; end
900
+ end
901
+ RUBY
902
+
903
+ entry = T.must(@index["bar"].first)
904
+ assert_predicate(entry, :public?)
905
+ assert_equal("Foo", T.must(entry.owner).name)
906
+
907
+ entry = T.must(@index["baz"].first)
908
+ assert_predicate(entry, :public?)
909
+ assert_equal("Foo", T.must(entry.owner).name)
910
+ end
911
+
912
+ def test_making_several_class_methods_private
913
+ index(<<~RUBY)
914
+ class Foo
915
+ def self.bar; end
916
+ def self.baz; end
917
+ def self.qux; end
918
+
919
+ private_class_method :bar, :baz, :qux
920
+
921
+ def initialize
922
+ end
923
+ end
924
+ RUBY
925
+ end
926
+
849
927
  private
850
928
 
851
929
  sig { params(entry: Entry::Method, call_string: String).void }
@@ -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
@@ -216,6 +216,88 @@ module RubyIndexer
216
216
  assert_equal(11, refs[2].location.start_line)
217
217
  end
218
218
 
219
+ def test_finds_instance_variable_read_references
220
+ refs = find_instance_variable_references("@foo", <<~RUBY)
221
+ class Foo
222
+ def foo
223
+ @foo
224
+ end
225
+ end
226
+ RUBY
227
+ assert_equal(1, refs.size)
228
+
229
+ assert_equal("@foo", refs[0].name)
230
+ assert_equal(3, refs[0].location.start_line)
231
+ end
232
+
233
+ def test_finds_instance_variable_write_references
234
+ refs = find_instance_variable_references("@foo", <<~RUBY)
235
+ class Foo
236
+ def write
237
+ @foo = 1
238
+ @foo &&= 2
239
+ @foo ||= 3
240
+ @foo += 4
241
+ @foo, @bar = []
242
+ end
243
+ end
244
+ RUBY
245
+ assert_equal(5, refs.size)
246
+
247
+ assert_equal(["@foo"], refs.map(&:name).uniq)
248
+ assert_equal(3, refs[0].location.start_line)
249
+ assert_equal(4, refs[1].location.start_line)
250
+ assert_equal(5, refs[2].location.start_line)
251
+ assert_equal(6, refs[3].location.start_line)
252
+ assert_equal(7, refs[4].location.start_line)
253
+ end
254
+
255
+ def test_finds_instance_variable_references_ignore_context
256
+ refs = find_instance_variable_references("@name", <<~RUBY)
257
+ class Foo
258
+ def name
259
+ @name = "foo"
260
+ end
261
+ end
262
+ class Bar
263
+ def name
264
+ @name = "bar"
265
+ end
266
+ end
267
+ RUBY
268
+ assert_equal(2, refs.size)
269
+
270
+ assert_equal("@name", refs[0].name)
271
+ assert_equal(3, refs[0].location.start_line)
272
+
273
+ assert_equal("@name", refs[1].name)
274
+ assert_equal(8, refs[1].location.start_line)
275
+ end
276
+
277
+ def test_accounts_for_reopened_classes
278
+ refs = find_const_references("Foo", <<~RUBY)
279
+ class Foo
280
+ end
281
+ class Foo
282
+ class Bar
283
+ end
284
+ end
285
+
286
+ Foo.new
287
+ RUBY
288
+
289
+ assert_equal(3, refs.size)
290
+
291
+ assert_equal("Foo", refs[0].name)
292
+ assert_equal(1, refs[0].location.start_line)
293
+
294
+ assert_equal("Foo", refs[1].name)
295
+ assert_equal(3, refs[1].location.start_line)
296
+
297
+ assert_equal("Foo", refs[2].name)
298
+ assert_equal(8, refs[2].location.start_line)
299
+ end
300
+
219
301
  private
220
302
 
221
303
  def find_const_references(const_name, source)
@@ -228,13 +310,19 @@ module RubyIndexer
228
310
  find_references(target, source)
229
311
  end
230
312
 
313
+ def find_instance_variable_references(instance_variable_name, source)
314
+ target = ReferenceFinder::InstanceVariableTarget.new(instance_variable_name)
315
+ find_references(target, source)
316
+ end
317
+
231
318
  def find_references(target, source)
232
319
  file_path = "/fake.rb"
320
+ uri = URI::Generic.from_path(path: file_path)
233
321
  index = Index.new
234
- index.index_single(IndexablePath.new(nil, file_path), source)
322
+ index.index_single(uri, source)
235
323
  parse_result = Prism.parse(source)
236
324
  dispatcher = Prism::Dispatcher.new
237
- finder = ReferenceFinder.new(target, index, dispatcher)
325
+ finder = ReferenceFinder.new(target, index, dispatcher, uri)
238
326
  dispatcher.visit(parse_result.value)
239
327
  finder.references
240
328
  end
@@ -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)