ruby-lsp 0.22.1 → 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 (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)