ruby-lsp 0.8.0 → 0.9.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.
@@ -0,0 +1,220 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "test_case"
5
+
6
+ module RubyIndexer
7
+ class ClassesAndModulesTest < TestCase
8
+ def test_empty_statements_class
9
+ index(<<~RUBY)
10
+ class Foo
11
+ end
12
+ RUBY
13
+
14
+ assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
15
+ end
16
+
17
+ def test_class_with_statements
18
+ index(<<~RUBY)
19
+ class Foo
20
+ def something; end
21
+ end
22
+ RUBY
23
+
24
+ assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:2-2")
25
+ end
26
+
27
+ def test_colon_colon_class
28
+ index(<<~RUBY)
29
+ class ::Foo
30
+ end
31
+ RUBY
32
+
33
+ assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
34
+ end
35
+
36
+ def test_colon_colon_class_inside_class
37
+ index(<<~RUBY)
38
+ class Bar
39
+ class ::Foo
40
+ end
41
+ end
42
+ RUBY
43
+
44
+ assert_entry("Bar", Index::Entry::Class, "/fake/path/foo.rb:0-0:3-2")
45
+ assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:1-2:2-4")
46
+ end
47
+
48
+ def test_namespaced_class
49
+ index(<<~RUBY)
50
+ class Foo::Bar
51
+ end
52
+ RUBY
53
+
54
+ assert_entry("Foo::Bar", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
55
+ end
56
+
57
+ def test_dynamically_namespaced_class
58
+ index(<<~RUBY)
59
+ class self::Bar
60
+ end
61
+ RUBY
62
+
63
+ refute_entry("self::Bar")
64
+ end
65
+
66
+ def test_empty_statements_module
67
+ index(<<~RUBY)
68
+ module Foo
69
+ end
70
+ RUBY
71
+
72
+ assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-2")
73
+ end
74
+
75
+ def test_module_with_statements
76
+ index(<<~RUBY)
77
+ module Foo
78
+ def something; end
79
+ end
80
+ RUBY
81
+
82
+ assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:2-2")
83
+ end
84
+
85
+ def test_colon_colon_module
86
+ index(<<~RUBY)
87
+ module ::Foo
88
+ end
89
+ RUBY
90
+
91
+ assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-2")
92
+ end
93
+
94
+ def test_namespaced_module
95
+ index(<<~RUBY)
96
+ module Foo::Bar
97
+ end
98
+ RUBY
99
+
100
+ assert_entry("Foo::Bar", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-2")
101
+ end
102
+
103
+ def test_dynamically_namespaced_module
104
+ index(<<~RUBY)
105
+ module self::Bar
106
+ end
107
+ RUBY
108
+
109
+ refute_entry("self::Bar")
110
+ end
111
+
112
+ def test_nested_modules_and_classes
113
+ index(<<~RUBY)
114
+ module Foo
115
+ class Bar
116
+ end
117
+
118
+ module Baz
119
+ class Qux
120
+ class Something
121
+ end
122
+ end
123
+ end
124
+ end
125
+ RUBY
126
+
127
+ assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:10-2")
128
+ assert_entry("Foo::Bar", Index::Entry::Class, "/fake/path/foo.rb:1-2:2-4")
129
+ assert_entry("Foo::Baz", Index::Entry::Module, "/fake/path/foo.rb:4-2:9-4")
130
+ assert_entry("Foo::Baz::Qux", Index::Entry::Class, "/fake/path/foo.rb:5-4:8-6")
131
+ assert_entry("Foo::Baz::Qux::Something", Index::Entry::Class, "/fake/path/foo.rb:6-6:7-8")
132
+ end
133
+
134
+ def test_deleting_from_index_based_on_file_path
135
+ index(<<~RUBY)
136
+ class Foo
137
+ end
138
+ RUBY
139
+
140
+ assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
141
+
142
+ @index.delete("/fake/path/foo.rb")
143
+ refute_entry("Foo")
144
+ assert_empty(@index.instance_variable_get(:@files_to_entries))
145
+ end
146
+
147
+ def test_comments_can_be_attached_to_a_class
148
+ index(<<~RUBY)
149
+ # This is method comment
150
+ def foo; end
151
+ # This is a Foo comment
152
+ # This is another Foo comment
153
+ class Foo
154
+ # This should not be attached
155
+ end
156
+
157
+ # Ignore me
158
+
159
+ # This Bar comment has 1 line padding
160
+
161
+ class Bar; end
162
+ RUBY
163
+
164
+ foo_entry = @index["Foo"].first
165
+ assert_equal("This is a Foo comment\nThis is another Foo comment", foo_entry.comments.join("\n"))
166
+
167
+ bar_entry = @index["Bar"].first
168
+ assert_equal("This Bar comment has 1 line padding", bar_entry.comments.join("\n"))
169
+ end
170
+
171
+ def test_comments_can_be_attached_to_a_namespaced_class
172
+ index(<<~RUBY)
173
+ # This is a Foo comment
174
+ # This is another Foo comment
175
+ class Foo
176
+ # This is a Bar comment
177
+ class Bar; end
178
+ end
179
+ RUBY
180
+
181
+ foo_entry = @index["Foo"].first
182
+ assert_equal("This is a Foo comment\nThis is another Foo comment", foo_entry.comments.join("\n"))
183
+
184
+ bar_entry = @index["Foo::Bar"].first
185
+ assert_equal("This is a Bar comment", bar_entry.comments.join("\n"))
186
+ end
187
+
188
+ def test_comments_can_be_attached_to_a_reopened_class
189
+ index(<<~RUBY)
190
+ # This is a Foo comment
191
+ class Foo; end
192
+
193
+ # This is another Foo comment
194
+ class Foo; end
195
+ RUBY
196
+
197
+ first_foo_entry = @index["Foo"][0]
198
+ assert_equal("This is a Foo comment", first_foo_entry.comments.join("\n"))
199
+
200
+ second_foo_entry = @index["Foo"][1]
201
+ assert_equal("This is another Foo comment", second_foo_entry.comments.join("\n"))
202
+ end
203
+
204
+ def test_comments_removes_the_leading_pound_and_space
205
+ index(<<~RUBY)
206
+ # This is a Foo comment
207
+ class Foo; end
208
+
209
+ #This is a Bar comment
210
+ class Bar; end
211
+ RUBY
212
+
213
+ first_foo_entry = @index["Foo"][0]
214
+ assert_equal("This is a Foo comment", first_foo_entry.comments.join("\n"))
215
+
216
+ second_foo_entry = @index["Bar"][0]
217
+ assert_equal("This is a Bar comment", second_foo_entry.comments.join("\n"))
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,114 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "test_helper"
5
+
6
+ module RubyIndexer
7
+ class ConfigurationTest < Minitest::Test
8
+ def setup
9
+ @config = Configuration.new
10
+ end
11
+
12
+ def test_load_configuration_executes_configure_block
13
+ @config.load_config
14
+ files_to_index = @config.files_to_index
15
+
16
+ assert(files_to_index.none? { |path| path.include?("test/fixtures") })
17
+ assert(files_to_index.none? { |path| path.include?("minitest-reporters") })
18
+ assert(files_to_index.none? { |path| path == __FILE__ })
19
+ end
20
+
21
+ def test_files_to_index_only_includes_gem_require_paths
22
+ @config.load_config
23
+ files_to_index = @config.files_to_index
24
+
25
+ Bundler.locked_gems.specs.each do |lazy_spec|
26
+ next if lazy_spec.name == "ruby-lsp"
27
+
28
+ spec = Gem::Specification.find_by_name(lazy_spec.name)
29
+ assert(files_to_index.none? { |path| path.start_with?("#{spec.full_gem_path}/test/") })
30
+ rescue Gem::MissingSpecError
31
+ # Transitive dependencies might be missing when running tests on Windows
32
+ end
33
+ end
34
+
35
+ def test_files_to_index_does_not_include_default_gem_path_when_in_bundle
36
+ @config.load_config
37
+ files_to_index = @config.files_to_index
38
+
39
+ assert(files_to_index.none? { |path| path.start_with?("#{RbConfig::CONFIG["rubylibdir"]}/psych") })
40
+ end
41
+
42
+ def test_files_to_index_includes_default_gems
43
+ @config.load_config
44
+ files_to_index = @config.files_to_index
45
+
46
+ assert_includes(files_to_index, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")
47
+ assert_includes(files_to_index, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb")
48
+ assert_includes(files_to_index, "#{RbConfig::CONFIG["rubylibdir"]}/abbrev.rb")
49
+ end
50
+
51
+ def test_files_to_index_includes_project_files
52
+ @config.load_config
53
+ files_to_index = @config.files_to_index
54
+
55
+ Dir.glob("#{Dir.pwd}/lib/**/*.rb").each do |path|
56
+ next if path.end_with?("_test.rb")
57
+
58
+ assert_includes(files_to_index, path)
59
+ end
60
+ end
61
+
62
+ def test_files_to_index_avoids_duplicates_if_bundle_path_is_inside_project
63
+ Bundler.settings.set_global("path", "vendor/bundle")
64
+ config = Configuration.new
65
+ config.load_config
66
+
67
+ assert_includes(config.instance_variable_get(:@excluded_patterns), "#{Dir.pwd}/vendor/bundle/**/*.rb")
68
+ ensure
69
+ Bundler.settings.set_global("path", nil)
70
+ end
71
+
72
+ def test_files_to_index_does_not_include_gems_own_installed_files
73
+ @config.load_config
74
+ files_to_index = @config.files_to_index
75
+
76
+ assert(files_to_index.none? { |path| path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s) })
77
+ end
78
+
79
+ def test_paths_are_unique
80
+ @config.load_config
81
+ files_to_index = @config.files_to_index
82
+
83
+ assert_equal(files_to_index.uniq.length, files_to_index.length)
84
+ end
85
+
86
+ def test_configuration_raises_for_unknown_keys
87
+ Psych::Nodes::Document.any_instance.expects(:to_ruby).returns({ "unknown_config" => 123 })
88
+
89
+ assert_raises(ArgumentError) do
90
+ @config.load_config
91
+ end
92
+ end
93
+
94
+ def test_magic_comments_regex
95
+ regex = RubyIndexer.configuration.magic_comment_regex
96
+
97
+ [
98
+ "# frozen_string_literal:",
99
+ "# typed:",
100
+ "# compiled:",
101
+ "# encoding:",
102
+ "# shareable_constant_value:",
103
+ "# warn_indent:",
104
+ "# rubocop:",
105
+ "# nodoc:",
106
+ "# doc:",
107
+ "# coding:",
108
+ "# warn_past_scope:",
109
+ ].each do |comment|
110
+ assert_match(regex, comment)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,108 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "test_case"
5
+
6
+ module RubyIndexer
7
+ class ConstantTest < TestCase
8
+ def test_constant_writes
9
+ index(<<~RUBY)
10
+ FOO = 1
11
+
12
+ class ::Bar
13
+ FOO = 2
14
+ end
15
+ RUBY
16
+
17
+ assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-6")
18
+ assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-8")
19
+ end
20
+
21
+ def test_constant_or_writes
22
+ index(<<~RUBY)
23
+ FOO ||= 1
24
+
25
+ class ::Bar
26
+ FOO ||= 2
27
+ end
28
+ RUBY
29
+
30
+ assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-8")
31
+ assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-10")
32
+ end
33
+
34
+ def test_constant_path_writes
35
+ index(<<~RUBY)
36
+ class A
37
+ FOO = 1
38
+ ::BAR = 1
39
+
40
+ module B
41
+ FOO = 1
42
+ end
43
+ end
44
+
45
+ A::BAZ = 1
46
+ RUBY
47
+
48
+ assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-8")
49
+ assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-10")
50
+ assert_entry("A::B::FOO", Index::Entry::Constant, "/fake/path/foo.rb:5-4:5-10")
51
+ assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:9-0:9-9")
52
+ end
53
+
54
+ def test_constant_path_or_writes
55
+ index(<<~RUBY)
56
+ class A
57
+ FOO ||= 1
58
+ ::BAR ||= 1
59
+ end
60
+
61
+ A::BAZ ||= 1
62
+ RUBY
63
+
64
+ assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-10")
65
+ assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-12")
66
+ assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:5-0:5-11")
67
+ end
68
+
69
+ def test_comments_for_constants
70
+ index(<<~RUBY)
71
+ # FOO comment
72
+ FOO = 1
73
+
74
+ class A
75
+ # A::FOO comment
76
+ FOO = 1
77
+
78
+ # ::BAR comment
79
+ ::BAR = 1
80
+ end
81
+
82
+ # A::BAZ comment
83
+ A::BAZ = 1
84
+ RUBY
85
+
86
+ foo_comment = @index["FOO"].first.comments.join("\n")
87
+ assert_equal("FOO comment", foo_comment)
88
+
89
+ a_foo_comment = @index["A::FOO"].first.comments.join("\n")
90
+ assert_equal("A::FOO comment", a_foo_comment)
91
+
92
+ bar_comment = @index["BAR"].first.comments.join("\n")
93
+ assert_equal("::BAR comment", bar_comment)
94
+
95
+ a_baz_comment = @index["A::BAZ"].first.comments.join("\n")
96
+ assert_equal("A::BAZ comment", a_baz_comment)
97
+ end
98
+
99
+ def test_variable_path_constants_are_ignored
100
+ index(<<~RUBY)
101
+ var::FOO = 1
102
+ self.class::FOO = 1
103
+ RUBY
104
+
105
+ assert_no_entry
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,129 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "test_case"
5
+
6
+ module RubyIndexer
7
+ class IndexTest < TestCase
8
+ def test_deleting_one_entry_for_a_class
9
+ @index.index_single("/fake/path/foo.rb", <<~RUBY)
10
+ class Foo
11
+ end
12
+ RUBY
13
+ @index.index_single("/fake/path/other_foo.rb", <<~RUBY)
14
+ class Foo
15
+ end
16
+ RUBY
17
+
18
+ entries = @index["Foo"]
19
+ assert_equal(2, entries.length)
20
+
21
+ @index.delete("/fake/path/other_foo.rb")
22
+ entries = @index["Foo"]
23
+ assert_equal(1, entries.length)
24
+ end
25
+
26
+ def test_deleting_all_entries_for_a_class
27
+ @index.index_single("/fake/path/foo.rb", <<~RUBY)
28
+ class Foo
29
+ end
30
+ RUBY
31
+
32
+ entries = @index["Foo"]
33
+ assert_equal(1, entries.length)
34
+
35
+ @index.delete("/fake/path/foo.rb")
36
+ entries = @index["Foo"]
37
+ assert_nil(entries)
38
+ end
39
+
40
+ def test_index_resolve
41
+ @index.index_single("/fake/path/foo.rb", <<~RUBY)
42
+ class Bar; end
43
+
44
+ module Foo
45
+ class Bar
46
+ end
47
+
48
+ class Baz
49
+ class Something
50
+ end
51
+ end
52
+ end
53
+ RUBY
54
+
55
+ entries = @index.resolve("Something", ["Foo", "Baz"])
56
+ refute_empty(entries)
57
+ assert_equal("Foo::Baz::Something", entries.first.name)
58
+
59
+ entries = @index.resolve("Bar", ["Foo"])
60
+ refute_empty(entries)
61
+ assert_equal("Foo::Bar", entries.first.name)
62
+
63
+ entries = @index.resolve("Bar", ["Foo", "Baz"])
64
+ refute_empty(entries)
65
+ assert_equal("Foo::Bar", entries.first.name)
66
+
67
+ entries = @index.resolve("Foo::Bar", ["Foo", "Baz"])
68
+ refute_empty(entries)
69
+ assert_equal("Foo::Bar", entries.first.name)
70
+
71
+ assert_nil(@index.resolve("DoesNotExist", ["Foo"]))
72
+ end
73
+
74
+ def test_accessing_with_colon_colon_prefix
75
+ @index.index_single("/fake/path/foo.rb", <<~RUBY)
76
+ class Bar; end
77
+
78
+ module Foo
79
+ class Bar
80
+ end
81
+
82
+ class Baz
83
+ class Something
84
+ end
85
+ end
86
+ end
87
+ RUBY
88
+
89
+ entries = @index["::Foo::Baz::Something"]
90
+ refute_empty(entries)
91
+ assert_equal("Foo::Baz::Something", entries.first.name)
92
+ end
93
+
94
+ def test_fuzzy_search
95
+ @index.index_single("/fake/path/foo.rb", <<~RUBY)
96
+ class Bar; end
97
+
98
+ module Foo
99
+ class Bar
100
+ end
101
+
102
+ class Baz
103
+ class Something
104
+ end
105
+ end
106
+ end
107
+ RUBY
108
+
109
+ result = @index.fuzzy_search("Bar")
110
+ assert_equal(1, result.length)
111
+ assert_equal(@index["Bar"].first, result.first)
112
+
113
+ result = @index.fuzzy_search("foobarsomeking")
114
+ assert_equal(5, result.length)
115
+ assert_equal(["Foo::Baz::Something", "Foo::Bar", "Foo::Baz", "Foo", "Bar"], result.map(&:name))
116
+
117
+ result = @index.fuzzy_search("FooBaz")
118
+ assert_equal(4, result.length)
119
+ assert_equal(["Foo::Baz", "Foo::Bar", "Foo", "Foo::Baz::Something"], result.map(&:name))
120
+ end
121
+
122
+ def test_index_single_ignores_directories
123
+ FileUtils.mkdir("lib/this_is_a_dir.rb")
124
+ @index.index_single("lib/this_is_a_dir.rb")
125
+ ensure
126
+ FileUtils.rm_r("lib/this_is_a_dir.rb")
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,42 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "test_helper"
5
+
6
+ module RubyIndexer
7
+ class TestCase < Minitest::Test
8
+ def setup
9
+ @index = Index.new
10
+ end
11
+
12
+ private
13
+
14
+ def index(source)
15
+ @index.index_single("/fake/path/foo.rb", source)
16
+ end
17
+
18
+ def assert_entry(expected_name, type, expected_location)
19
+ entries = @index[expected_name]
20
+ refute_empty(entries, "Expected #{expected_name} to be indexed")
21
+
22
+ entry = entries.first
23
+ assert_instance_of(type, entry, "Expected #{expected_name} to be a #{type}")
24
+
25
+ location = entry.location
26
+ location_string =
27
+ "#{entry.file_path}:#{location.start_line - 1}-#{location.start_column}" \
28
+ ":#{location.end_line - 1}-#{location.end_column}"
29
+
30
+ assert_equal(expected_location, location_string)
31
+ end
32
+
33
+ def refute_entry(expected_name)
34
+ entries = @index[expected_name]
35
+ assert_nil(entries, "Expected #{expected_name} to not be indexed")
36
+ end
37
+
38
+ def assert_no_entry
39
+ assert_empty(@index.instance_variable_get(:@entries), "Expected nothing to be indexed")
40
+ end
41
+ end
42
+ end