ruby-lsp 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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