ruby-lsp 0.22.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) 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 +88 -22
  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/rbs_indexer_test.rb +1 -1
  18. data/lib/ruby_indexer/test/reference_finder_test.rb +1 -1
  19. data/lib/ruby_indexer/test/test_case.rb +2 -2
  20. data/lib/ruby_indexer/test/uri_test.rb +72 -0
  21. data/lib/ruby_lsp/addon.rb +9 -0
  22. data/lib/ruby_lsp/base_server.rb +15 -6
  23. data/lib/ruby_lsp/document.rb +10 -1
  24. data/lib/ruby_lsp/internal.rb +1 -1
  25. data/lib/ruby_lsp/listeners/code_lens.rb +8 -4
  26. data/lib/ruby_lsp/listeners/completion.rb +73 -4
  27. data/lib/ruby_lsp/listeners/definition.rb +73 -17
  28. data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
  29. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  30. data/lib/ruby_lsp/listeners/hover.rb +57 -0
  31. data/lib/ruby_lsp/requests/completion.rb +6 -0
  32. data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
  33. data/lib/ruby_lsp/requests/definition.rb +6 -0
  34. data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
  35. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
  36. data/lib/ruby_lsp/requests/rename.rb +14 -4
  37. data/lib/ruby_lsp/requests/support/common.rb +1 -5
  38. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  39. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -2
  40. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  41. data/lib/ruby_lsp/server.rb +42 -7
  42. data/lib/ruby_lsp/setup_bundler.rb +31 -41
  43. data/lib/ruby_lsp/test_helper.rb +45 -11
  44. data/lib/ruby_lsp/type_inferrer.rb +22 -0
  45. data/lib/ruby_lsp/utils.rb +3 -0
  46. metadata +7 -8
  47. data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
@@ -13,63 +13,62 @@ module RubyIndexer
13
13
 
14
14
  def test_load_configuration_executes_configure_block
15
15
  @config.apply_config({ "excluded_patterns" => ["**/fixtures/**/*.rb"] })
16
- indexables = @config.indexables
16
+ uris = @config.indexable_uris
17
17
 
18
- assert(indexables.none? { |indexable| indexable.full_path.include?("test/fixtures") })
19
- assert(indexables.none? { |indexable| indexable.full_path.include?("minitest-reporters") })
20
- assert(indexables.none? { |indexable| indexable.full_path.include?("ansi") })
21
- assert(indexables.any? { |indexable| indexable.full_path.include?("sorbet-runtime") })
22
- assert(indexables.none? { |indexable| indexable.full_path == __FILE__ })
18
+ bundle_path = Bundler.bundle_path.join("gems")
19
+
20
+ assert(uris.none? { |uri| uri.full_path.include?("test/fixtures") })
21
+ assert(uris.none? { |uri| uri.full_path.include?(bundle_path.join("minitest-reporters").to_s) })
22
+ assert(uris.none? { |uri| uri.full_path.include?(bundle_path.join("ansi").to_s) })
23
+ assert(uris.any? { |uri| uri.full_path.include?(bundle_path.join("sorbet-runtime").to_s) })
24
+ assert(uris.none? { |uri| uri.full_path == __FILE__ })
23
25
  end
24
26
 
25
- def test_indexables_have_expanded_full_paths
27
+ def test_indexable_uris_have_expanded_full_paths
26
28
  @config.apply_config({ "included_patterns" => ["**/*.rb"] })
27
- indexables = @config.indexables
29
+ uris = @config.indexable_uris
28
30
 
29
31
  # All paths should be expanded
30
- assert(indexables.all? { |indexable| File.absolute_path?(indexable.full_path) })
32
+ assert(uris.all? { |uri| File.absolute_path?(uri.full_path) })
31
33
  end
32
34
 
33
- def test_indexables_only_includes_gem_require_paths
34
- indexables = @config.indexables
35
+ def test_indexable_uris_only_includes_gem_require_paths
36
+ uris = @config.indexable_uris
35
37
 
36
38
  Bundler.locked_gems.specs.each do |lazy_spec|
37
39
  next if lazy_spec.name == "ruby-lsp"
38
40
 
39
41
  spec = Gem::Specification.find_by_name(lazy_spec.name)
40
- assert(indexables.none? { |indexable| indexable.full_path.start_with?("#{spec.full_gem_path}/test/") })
42
+ assert(uris.none? { |uri| uri.full_path.start_with?("#{spec.full_gem_path}/test/") })
41
43
  rescue Gem::MissingSpecError
42
44
  # Transitive dependencies might be missing when running tests on Windows
43
45
  end
44
46
  end
45
47
 
46
- def test_indexables_does_not_include_default_gem_path_when_in_bundle
47
- indexables = @config.indexables
48
-
49
- assert(
50
- indexables.none? { |indexable| indexable.full_path.start_with?("#{RbConfig::CONFIG["rubylibdir"]}/psych") },
51
- )
48
+ def test_indexable_uris_does_not_include_default_gem_path_when_in_bundle
49
+ uris = @config.indexable_uris
50
+ assert(uris.none? { |uri| uri.full_path.start_with?("#{RbConfig::CONFIG["rubylibdir"]}/psych") })
52
51
  end
53
52
 
54
- def test_indexables_includes_default_gems
55
- indexables = @config.indexables.map(&:full_path)
53
+ def test_indexable_uris_includes_default_gems
54
+ paths = @config.indexable_uris.map(&:full_path)
56
55
 
57
- assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")
58
- assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb")
59
- assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb")
56
+ assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")
57
+ assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb")
58
+ assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb")
60
59
  end
61
60
 
62
- def test_indexables_includes_project_files
63
- indexables = @config.indexables.map(&:full_path)
61
+ def test_indexable_uris_includes_project_files
62
+ paths = @config.indexable_uris.map(&:full_path)
64
63
 
65
64
  Dir.glob("#{Dir.pwd}/lib/**/*.rb").each do |path|
66
65
  next if path.end_with?("_test.rb")
67
66
 
68
- assert_includes(indexables, path)
67
+ assert_includes(paths, path)
69
68
  end
70
69
  end
71
70
 
72
- def test_indexables_avoids_duplicates_if_bundle_path_is_inside_project
71
+ def test_indexable_uris_avoids_duplicates_if_bundle_path_is_inside_project
73
72
  Bundler.settings.temporary(path: "vendor/bundle") do
74
73
  config = Configuration.new
75
74
 
@@ -77,33 +76,32 @@ module RubyIndexer
77
76
  end
78
77
  end
79
78
 
80
- def test_indexables_does_not_include_gems_own_installed_files
81
- indexables = @config.indexables
82
- indexables_inside_bundled_lsp = indexables.select do |indexable|
83
- indexable.full_path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s)
79
+ def test_indexable_uris_does_not_include_gems_own_installed_files
80
+ uris = @config.indexable_uris
81
+ uris_inside_bundled_lsp = uris.select do |uri|
82
+ uri.full_path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s)
84
83
  end
85
84
 
86
85
  assert_empty(
87
- indexables_inside_bundled_lsp,
88
- "Indexables should not include files from the gem currently being worked on. " \
89
- "Included: #{indexables_inside_bundled_lsp.map(&:full_path)}",
86
+ uris_inside_bundled_lsp,
87
+ "Indexable URIs should not include files from the gem currently being worked on. " \
88
+ "Included: #{uris_inside_bundled_lsp.map(&:full_path)}",
90
89
  )
91
90
  end
92
91
 
93
- def test_indexables_does_not_include_non_ruby_files_inside_rubylibdir
92
+ def test_indexable_uris_does_not_include_non_ruby_files_inside_rubylibdir
94
93
  path = Pathname.new(RbConfig::CONFIG["rubylibdir"]).join("extra_file.txt").to_s
95
94
  FileUtils.touch(path)
96
- indexables = @config.indexables
97
95
 
98
- assert(indexables.none? { |indexable| indexable.full_path == path })
96
+ uris = @config.indexable_uris
97
+ assert(uris.none? { |uri| uri.full_path == path })
99
98
  ensure
100
99
  FileUtils.rm(T.must(path))
101
100
  end
102
101
 
103
102
  def test_paths_are_unique
104
- indexables = @config.indexables
105
-
106
- assert_equal(indexables.uniq.length, indexables.length)
103
+ uris = @config.indexable_uris
104
+ assert_equal(uris.uniq.length, uris.length)
107
105
  end
108
106
 
109
107
  def test_configuration_raises_for_unknown_keys
@@ -132,7 +130,7 @@ module RubyIndexer
132
130
  end
133
131
  end
134
132
 
135
- def test_indexables_respect_given_workspace_path
133
+ def test_indexable_uris_respect_given_workspace_path
136
134
  Dir.mktmpdir do |dir|
137
135
  FileUtils.mkdir(File.join(dir, "ignore"))
138
136
  FileUtils.touch(File.join(dir, "ignore", "file0.rb"))
@@ -141,21 +139,21 @@ module RubyIndexer
141
139
 
142
140
  @config.apply_config({ "excluded_patterns" => ["ignore/**/*.rb"] })
143
141
  @config.workspace_path = dir
144
- indexables = @config.indexables
145
142
 
146
- assert(indexables.none? { |indexable| indexable.full_path.start_with?(File.join(dir, "ignore")) })
143
+ uris = @config.indexable_uris
144
+ assert(uris.none? { |uri| uri.full_path.start_with?(File.join(dir, "ignore")) })
147
145
 
148
- # After switching the workspace path, all indexables will be found in one of these places:
146
+ # After switching the workspace path, all indexable URIs will be found in one of these places:
149
147
  # - The new workspace path
150
148
  # - The Ruby LSP's own code (because Bundler is requiring the dependency from source)
151
149
  # - Bundled gems
152
150
  # - Default gems
153
151
  assert(
154
- indexables.all? do |i|
155
- i.full_path.start_with?(dir) ||
156
- i.full_path.start_with?(File.join(Dir.pwd, "lib")) ||
157
- i.full_path.start_with?(Bundler.bundle_path.to_s) ||
158
- i.full_path.start_with?(RbConfig::CONFIG["rubylibdir"])
152
+ uris.all? do |u|
153
+ u.full_path.start_with?(dir) ||
154
+ u.full_path.start_with?(File.join(Dir.pwd, "lib")) ||
155
+ u.full_path.start_with?(Bundler.bundle_path.to_s) ||
156
+ u.full_path.start_with?(RbConfig::CONFIG["rubylibdir"])
159
157
  end,
160
158
  )
161
159
  end
@@ -166,8 +164,75 @@ module RubyIndexer
166
164
  FileUtils.touch(File.join(dir, "find_me.rb"))
167
165
  @config.workspace_path = dir
168
166
 
169
- indexables = @config.indexables
170
- assert(indexables.find { |i| File.basename(i.full_path) == "find_me.rb" })
167
+ uris = @config.indexable_uris
168
+ assert(uris.find { |u| File.basename(u.full_path) == "find_me.rb" })
169
+ end
170
+ end
171
+
172
+ def test_transitive_dependencies_for_non_dev_gems_are_not_excluded
173
+ Dir.mktmpdir do |dir|
174
+ Dir.chdir(dir) do
175
+ # Both IRB and debug depend on reline. Since IRB is in the default group, reline should not be excluded
176
+ File.write(File.join(dir, "Gemfile"), <<~RUBY)
177
+ source "https://rubygems.org"
178
+ gem "irb"
179
+ gem "ruby-lsp", path: "#{Bundler.root}"
180
+
181
+ group :development do
182
+ gem "debug"
183
+ end
184
+ RUBY
185
+
186
+ Bundler.with_unbundled_env do
187
+ capture_subprocess_io do
188
+ system("bundle install")
189
+ end
190
+
191
+ stdout, _stderr = capture_subprocess_io do
192
+ script = [
193
+ "require \"ruby_lsp/internal\"",
194
+ "print RubyIndexer::Configuration.new.instance_variable_get(:@excluded_gems).join(\",\")",
195
+ ].join(";")
196
+ system("bundle exec ruby -e '#{script}'")
197
+ end
198
+
199
+ excluded_gems = stdout.split(",")
200
+ assert_includes(excluded_gems, "debug")
201
+ refute_includes(excluded_gems, "reline")
202
+ refute_includes(excluded_gems, "irb")
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ def test_does_not_fail_if_there_are_missing_specs_due_to_platform_constraints
209
+ Dir.mktmpdir do |dir|
210
+ Dir.chdir(dir) do
211
+ File.write(File.join(dir, "Gemfile"), <<~RUBY)
212
+ source "https://rubygems.org"
213
+ gem "ruby-lsp", path: "#{Bundler.root}"
214
+
215
+ platforms :windows do
216
+ gem "tzinfo"
217
+ gem "tzinfo-data"
218
+ end
219
+ RUBY
220
+
221
+ Bundler.with_unbundled_env do
222
+ capture_subprocess_io { system("bundle install") }
223
+
224
+ _stdout, stderr = capture_subprocess_io do
225
+ script = [
226
+ "require \"ruby_lsp/internal\"",
227
+ "RubyIndexer::Configuration.new.indexable_uris",
228
+ ].join(";")
229
+
230
+ system("bundle exec ruby -e '#{script}'")
231
+ end
232
+
233
+ assert_empty(stderr)
234
+ end
235
+ end
171
236
  end
172
237
  end
173
238
  end
@@ -172,7 +172,7 @@ module RubyIndexer
172
172
  end
173
173
 
174
174
  assert_match(
175
- %r{Indexing error in /fake/path/foo\.rb with 'TestEnhancement' on call node enter enhancement},
175
+ %r{Indexing error in file:///fake/path/foo\.rb with 'TestEnhancement' on call node enter enhancement},
176
176
  stderr,
177
177
  )
178
178
  # The module should still be indexed
@@ -205,7 +205,7 @@ module RubyIndexer
205
205
  end
206
206
 
207
207
  assert_match(
208
- %r{Indexing error in /fake/path/foo\.rb with 'TestEnhancement' on call node leave enhancement},
208
+ %r{Indexing error in file:///fake/path/foo\.rb with 'TestEnhancement' on call node leave enhancement},
209
209
  stderr,
210
210
  )
211
211
  # The module should still be indexed
@@ -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
@@ -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)