ruby-lsp 0.12.2 → 0.12.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f60569fbb67f1c78fcffed58dbf540cb4e68fe10200841e54cd8a68859cf0de
4
- data.tar.gz: 07c4419c763e590e1fbed7fe14a2660c1bf6ddbd92ca791948a8c2235208f6e9
3
+ metadata.gz: 70a84356e392893c6f7b20e1701ef31621d9902af3a120c7013a13f80ec09568
4
+ data.tar.gz: bd360c2a5426631816f227ff115af967813ae7284d0a61845daf743b0a99d948
5
5
  SHA512:
6
- metadata.gz: 5284be197a27351a845cbcfc1735a0e9546472d60f9600ffd4ee55ba3ab0998438110c1ec59f684e0cf2fbaa7ff99f89878919ae3954022372e50116182c59dd
7
- data.tar.gz: f706c17b0cd47c1001bffb42f6c9a1c43efbd390e548ffe7b51fb3c58bdb433c868a7f907a3aa32175d7742ccbb3c1c29606a0c7c979246bfe887500e6531676
6
+ metadata.gz: 598494207add9412e909836e65f6288403189133711dbb17e39663eecc719089dba9f8e758069fda91666715d8c8fc5326055192ba5558f6d8151bbc21f40338
7
+ data.tar.gz: b86880d5b342e817d8cda46c4977a4229896d19b1fa495c6235f4d86ba74fa1a8ec737b7bad8a19af8859f315980f491b7bd8bcd8f25c1ad21d1ec3f119b9aaf
data/README.md CHANGED
@@ -52,6 +52,7 @@ The Ruby LSP provides an addon system that allows other gems to enhance the base
52
52
  features. This is the mechanism that powers addons like
53
53
 
54
54
  - [Ruby LSP Rails](https://github.com/Shopify/ruby-lsp-rails)
55
+ - [Ruby LSP RSpec](https://github.com/st0012/ruby-lsp-rspec)
55
56
 
56
57
  For instructions on how to create addons, see the [addons documentation](ADDONS.md).
57
58
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.12.2
1
+ 0.12.4
data/exe/ruby-lsp-doctor CHANGED
@@ -10,6 +10,6 @@ RubyIndexer.configuration.indexables.each do |indexable|
10
10
  puts "indexing: #{indexable.full_path}"
11
11
  content = File.read(indexable.full_path)
12
12
  result = Prism.parse(content)
13
- visitor = RubyIndexer::IndexVisitor.new(index, result, indexable.full_path)
14
- result.value.accept(visitor)
13
+ collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
14
+ collector.collect(result.value)
15
15
  end
@@ -2,9 +2,11 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyIndexer
5
- class IndexVisitor < Prism::Visitor
5
+ class Collector
6
6
  extend T::Sig
7
7
 
8
+ LEAVE_EVENT = T.let(Object.new.freeze, Object)
9
+
8
10
  sig { params(index: Index, parse_result: Prism::ParseResult, file_path: String).void }
9
11
  def initialize(index, parse_result, file_path)
10
12
  @index = index
@@ -16,26 +18,67 @@ module RubyIndexer
16
18
  end,
17
19
  T::Hash[Integer, Prism::Comment],
18
20
  )
21
+ @queue = T.let([], T::Array[Object])
22
+ @current_owner = T.let(nil, T.nilable(Entry::Namespace))
19
23
 
20
24
  super()
21
25
  end
22
26
 
23
- sig { override.params(node: Prism::ClassNode).void }
24
- def visit_class_node(node)
25
- add_class_entry(node)
27
+ sig { params(node: Prism::Node).void }
28
+ def collect(node)
29
+ @queue = [node]
30
+
31
+ until @queue.empty?
32
+ node_or_event = @queue.shift
33
+
34
+ case node_or_event
35
+ when Prism::ProgramNode
36
+ @queue << node_or_event.statements
37
+ when Prism::StatementsNode
38
+ T.unsafe(@queue).prepend(*node_or_event.body)
39
+ when Prism::ClassNode
40
+ add_class_entry(node_or_event)
41
+ when Prism::ModuleNode
42
+ add_module_entry(node_or_event)
43
+ when Prism::MultiWriteNode
44
+ handle_multi_write_node(node_or_event)
45
+ when Prism::ConstantPathWriteNode
46
+ handle_constant_path_write_node(node_or_event)
47
+ when Prism::ConstantPathOrWriteNode
48
+ handle_constant_path_or_write_node(node_or_event)
49
+ when Prism::ConstantPathOperatorWriteNode
50
+ handle_constant_path_operator_write_node(node_or_event)
51
+ when Prism::ConstantPathAndWriteNode
52
+ handle_constant_path_and_write_node(node_or_event)
53
+ when Prism::ConstantWriteNode
54
+ handle_constant_write_node(node_or_event)
55
+ when Prism::ConstantOrWriteNode
56
+ name = fully_qualify_name(node_or_event.name.to_s)
57
+ add_constant(node_or_event, name)
58
+ when Prism::ConstantAndWriteNode
59
+ name = fully_qualify_name(node_or_event.name.to_s)
60
+ add_constant(node_or_event, name)
61
+ when Prism::ConstantOperatorWriteNode
62
+ name = fully_qualify_name(node_or_event.name.to_s)
63
+ add_constant(node_or_event, name)
64
+ when Prism::CallNode
65
+ handle_call_node(node_or_event)
66
+ when Prism::DefNode
67
+ handle_def_node(node_or_event)
68
+ when LEAVE_EVENT
69
+ @stack.pop
70
+ end
71
+ end
26
72
  end
27
73
 
28
- sig { override.params(node: Prism::ModuleNode).void }
29
- def visit_module_node(node)
30
- add_module_entry(node)
31
- end
74
+ private
32
75
 
33
- sig { override.params(node: Prism::MultiWriteNode).void }
34
- def visit_multi_write_node(node)
76
+ sig { params(node: Prism::MultiWriteNode).void }
77
+ def handle_multi_write_node(node)
35
78
  value = node.value
36
79
  values = value.is_a?(Prism::ArrayNode) && value.opening_loc ? value.elements : []
37
80
 
38
- node.targets.each_with_index do |target, i|
81
+ [*node.lefts, *node.rest, *node.rights].each_with_index do |target, i|
39
82
  current_value = values[i]
40
83
  # The moment we find a splat on the right hand side of the assignment, we can no longer figure out which value
41
84
  # gets assigned to what
@@ -50,8 +93,8 @@ module RubyIndexer
50
93
  end
51
94
  end
52
95
 
53
- sig { override.params(node: Prism::ConstantPathWriteNode).void }
54
- def visit_constant_path_write_node(node)
96
+ sig { params(node: Prism::ConstantPathWriteNode).void }
97
+ def handle_constant_path_write_node(node)
55
98
  # ignore variable constants like `var::FOO` or `self.class::FOO`
56
99
  target = node.target
57
100
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -60,8 +103,8 @@ module RubyIndexer
60
103
  add_constant(node, name)
61
104
  end
62
105
 
63
- sig { override.params(node: Prism::ConstantPathOrWriteNode).void }
64
- def visit_constant_path_or_write_node(node)
106
+ sig { params(node: Prism::ConstantPathOrWriteNode).void }
107
+ def handle_constant_path_or_write_node(node)
65
108
  # ignore variable constants like `var::FOO` or `self.class::FOO`
66
109
  target = node.target
67
110
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -70,8 +113,8 @@ module RubyIndexer
70
113
  add_constant(node, name)
71
114
  end
72
115
 
73
- sig { override.params(node: Prism::ConstantPathOperatorWriteNode).void }
74
- def visit_constant_path_operator_write_node(node)
116
+ sig { params(node: Prism::ConstantPathOperatorWriteNode).void }
117
+ def handle_constant_path_operator_write_node(node)
75
118
  # ignore variable constants like `var::FOO` or `self.class::FOO`
76
119
  target = node.target
77
120
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -80,8 +123,8 @@ module RubyIndexer
80
123
  add_constant(node, name)
81
124
  end
82
125
 
83
- sig { override.params(node: Prism::ConstantPathAndWriteNode).void }
84
- def visit_constant_path_and_write_node(node)
126
+ sig { params(node: Prism::ConstantPathAndWriteNode).void }
127
+ def handle_constant_path_and_write_node(node)
85
128
  # ignore variable constants like `var::FOO` or `self.class::FOO`
86
129
  target = node.target
87
130
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -90,50 +133,44 @@ module RubyIndexer
90
133
  add_constant(node, name)
91
134
  end
92
135
 
93
- sig { override.params(node: Prism::ConstantWriteNode).void }
94
- def visit_constant_write_node(node)
95
- name = fully_qualify_name(node.name.to_s)
96
- add_constant(node, name)
97
- end
98
-
99
- sig { override.params(node: Prism::ConstantOrWriteNode).void }
100
- def visit_constant_or_write_node(node)
101
- name = fully_qualify_name(node.name.to_s)
102
- add_constant(node, name)
103
- end
104
-
105
- sig { override.params(node: Prism::ConstantAndWriteNode).void }
106
- def visit_constant_and_write_node(node)
136
+ sig { params(node: Prism::ConstantWriteNode).void }
137
+ def handle_constant_write_node(node)
107
138
  name = fully_qualify_name(node.name.to_s)
108
139
  add_constant(node, name)
109
140
  end
110
141
 
111
- sig { override.params(node: Prism::ConstantOperatorWriteNode).void }
112
- def visit_constant_operator_write_node(node)
113
- name = fully_qualify_name(node.name.to_s)
114
- add_constant(node, name)
115
- end
116
-
117
- sig { override.params(node: Prism::CallNode).void }
118
- def visit_call_node(node)
142
+ sig { params(node: Prism::CallNode).void }
143
+ def handle_call_node(node)
119
144
  message = node.message
120
145
  handle_private_constant(node) if message == "private_constant"
121
146
  end
122
147
 
123
- sig { override.params(node: Prism::DefNode).void }
124
- def visit_def_node(node)
148
+ sig { params(node: Prism::DefNode).void }
149
+ def handle_def_node(node)
125
150
  method_name = node.name.to_s
126
151
  comments = collect_comments(node)
127
152
  case node.receiver
128
153
  when nil
129
- @index << Entry::InstanceMethod.new(method_name, @file_path, node.location, comments, node.parameters)
154
+ @index << Entry::InstanceMethod.new(
155
+ method_name,
156
+ @file_path,
157
+ node.location,
158
+ comments,
159
+ node.parameters,
160
+ @current_owner,
161
+ )
130
162
  when Prism::SelfNode
131
- @index << Entry::SingletonMethod.new(method_name, @file_path, node.location, comments, node.parameters)
163
+ @index << Entry::SingletonMethod.new(
164
+ method_name,
165
+ @file_path,
166
+ node.location,
167
+ comments,
168
+ node.parameters,
169
+ @current_owner,
170
+ )
132
171
  end
133
172
  end
134
173
 
135
- private
136
-
137
174
  sig { params(node: Prism::CallNode).void }
138
175
  def handle_private_constant(node)
139
176
  arguments = node.arguments&.arguments
@@ -189,12 +226,12 @@ module RubyIndexer
189
226
 
190
227
  # If the right hand side is another constant assignment, we need to visit it because that constant has to be
191
228
  # indexed too
192
- visit(value)
229
+ @queue.prepend(value)
193
230
  Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
194
231
  when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
195
232
  Prism::ConstantPathAndWriteNode
196
233
 
197
- visit(value)
234
+ @queue.prepend(value)
198
235
  Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
199
236
  else
200
237
  Entry::Constant.new(name, @file_path, node.location, comments)
@@ -204,20 +241,26 @@ module RubyIndexer
204
241
  sig { params(node: Prism::ModuleNode).void }
205
242
  def add_module_entry(node)
206
243
  name = node.constant_path.location.slice
207
- return visit_child_nodes(node) unless /^[A-Z:]/.match?(name)
244
+ unless /^[A-Z:]/.match?(name)
245
+ @queue << node.body
246
+ return
247
+ end
208
248
 
209
249
  comments = collect_comments(node)
210
-
211
- @index << Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
250
+ @current_owner = Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
251
+ @index << @current_owner
212
252
  @stack << name
213
- visit_child_nodes(node)
214
- @stack.pop
253
+ @queue.prepend(node.body, LEAVE_EVENT)
215
254
  end
216
255
 
217
256
  sig { params(node: Prism::ClassNode).void }
218
257
  def add_class_entry(node)
219
258
  name = node.constant_path.location.slice
220
- return visit_child_nodes(node) unless /^[A-Z:]/.match?(name)
259
+
260
+ unless /^[A-Z:]/.match?(name)
261
+ @queue << node.body
262
+ return
263
+ end
221
264
 
222
265
  comments = collect_comments(node)
223
266
 
@@ -227,10 +270,16 @@ module RubyIndexer
227
270
  superclass.slice
228
271
  end
229
272
 
230
- @index << Entry::Class.new(fully_qualify_name(name), @file_path, node.location, comments, parent_class)
273
+ @current_owner = Entry::Class.new(
274
+ fully_qualify_name(name),
275
+ @file_path,
276
+ node.location,
277
+ comments,
278
+ parent_class,
279
+ )
280
+ @index << @current_owner
231
281
  @stack << name
232
- visit(node.body)
233
- @stack.pop
282
+ @queue.prepend(node.body, LEAVE_EVENT)
234
283
  end
235
284
 
236
285
  sig { params(node: Prism::Node).returns(T::Array[String]) }
@@ -249,7 +298,7 @@ module RubyIndexer
249
298
 
250
299
  comment_content.delete_prefix!("#")
251
300
  comment_content.delete_prefix!(" ")
252
- comments.unshift(comment_content)
301
+ comments.prepend(comment_content)
253
302
  end
254
303
 
255
304
  comments
@@ -71,8 +71,13 @@ module RubyIndexer
71
71
 
72
72
  Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
73
73
  # All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
74
- # entry is expensive, we memoize it for the entire pattern
75
- load_path_entry ||= $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
74
+ # entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
75
+ # on repositories that define multiple gems, like Rails. All frameworks are defined inside the Dir.pwd, but
76
+ # each one of them belongs to a different $LOAD_PATH entry
77
+ if load_path_entry.nil? || !path.start_with?(load_path_entry)
78
+ load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
79
+ end
80
+
76
81
  IndexablePath.new(load_path_entry, path)
77
82
  end
78
83
  end
@@ -144,7 +149,7 @@ module RubyIndexer
144
149
  # just ignore if they're missing
145
150
  end
146
151
 
147
- indexables.uniq!
152
+ indexables.uniq!(&:full_path)
148
153
  indexables
149
154
  end
150
155
 
@@ -101,6 +101,9 @@ module RubyIndexer
101
101
  sig { returns(T::Array[Parameter]) }
102
102
  attr_reader :parameters
103
103
 
104
+ sig { returns(T.nilable(Entry::Namespace)) }
105
+ attr_reader :owner
106
+
104
107
  sig do
105
108
  params(
106
109
  name: String,
@@ -108,11 +111,13 @@ module RubyIndexer
108
111
  location: Prism::Location,
109
112
  comments: T::Array[String],
110
113
  parameters_node: T.nilable(Prism::ParametersNode),
114
+ owner: T.nilable(Entry::Namespace),
111
115
  ).void
112
116
  end
113
- def initialize(name, file_path, location, comments, parameters_node)
117
+ def initialize(name, file_path, location, comments, parameters_node, owner) # rubocop:disable Metrics/ParameterLists
114
118
  super(name, file_path, location, comments)
115
119
  @parameters = T.let(list_params(parameters_node), T::Array[Parameter])
120
+ @owner = owner
116
121
  end
117
122
 
118
123
  private
@@ -136,8 +141,9 @@ module RubyIndexer
136
141
  case node
137
142
  when Prism::RequiredParameterNode
138
143
  node.name
139
- when Prism::RequiredDestructuredParameterNode
140
- names = node.parameters.map { |parameter_node| parameter_name(parameter_node) }
144
+ when Prism::MultiTargetNode
145
+ names = [*node.lefts, *node.rest, *node.rights].map { |parameter_node| parameter_name(parameter_node) }
146
+
141
147
  names_with_commas = names.join(", ")
142
148
  :"(#{names_with_commas})"
143
149
  end
@@ -93,8 +93,14 @@ module RubyIndexer
93
93
  # [#<Entry::Class name="Foo::Baz">],
94
94
  # ]
95
95
  # ```
96
- sig { params(query: String, nesting: T::Array[String]).returns(T::Array[T::Array[Entry]]) }
97
- def prefix_search(query, nesting)
96
+ sig { params(query: String, nesting: T.nilable(T::Array[String])).returns(T::Array[T::Array[Entry]]) }
97
+ def prefix_search(query, nesting = nil)
98
+ unless nesting
99
+ results = @entries_tree.search(query)
100
+ results.uniq!
101
+ return results
102
+ end
103
+
98
104
  results = nesting.length.downto(0).flat_map do |i|
99
105
  prefix = T.must(nesting[0...i]).join("::")
100
106
  namespaced_query = prefix.empty? ? query : "#{prefix}::#{query}"
@@ -180,8 +186,8 @@ module RubyIndexer
180
186
  def index_single(indexable_path, source = nil)
181
187
  content = source || File.read(indexable_path.full_path)
182
188
  result = Prism.parse(content)
183
- visitor = IndexVisitor.new(self, result, indexable_path.full_path)
184
- result.value.accept(visitor)
189
+ collector = Collector.new(self, result, indexable_path.full_path)
190
+ collector.collect(result.value)
185
191
 
186
192
  require_path = indexable_path.require_path
187
193
  @require_paths_tree.insert(require_path, indexable_path) if require_path
@@ -231,6 +237,18 @@ module RubyIndexer
231
237
  real_parts.join("::")
232
238
  end
233
239
 
240
+ # Attempts to find a given method for a resolved fully qualified receiver name. Returns `nil` if the method does not
241
+ # exist on that receiver
242
+ sig { params(method_name: String, receiver_name: String).returns(T.nilable(Entry::Method)) }
243
+ def resolve_method(method_name, receiver_name)
244
+ method_entries = T.cast(self[method_name], T.nilable(T::Array[Entry::Method]))
245
+ owner_entries = self[receiver_name]
246
+ return unless owner_entries && method_entries
247
+
248
+ owner_name = T.must(owner_entries.first).name
249
+ method_entries.find { |entry| entry.owner&.name == owner_name }
250
+ end
251
+
234
252
  private
235
253
 
236
254
  # Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
@@ -5,7 +5,7 @@ require "yaml"
5
5
  require "did_you_mean"
6
6
 
7
7
  require "ruby_indexer/lib/ruby_indexer/indexable_path"
8
- require "ruby_indexer/lib/ruby_indexer/visitor"
8
+ require "ruby_indexer/lib/ruby_indexer/collector"
9
9
  require "ruby_indexer/lib/ruby_indexer/index"
10
10
  require "ruby_indexer/lib/ruby_indexer/entry"
11
11
  require "ruby_indexer/lib/ruby_indexer/configuration"
@@ -193,5 +193,58 @@ module RubyIndexer
193
193
 
194
194
  assert_instance_of(Entry::UnresolvedAlias, entry)
195
195
  end
196
+
197
+ def test_visitor_does_not_visit_unnecessary_nodes
198
+ concats = (0...10_000).map do |i|
199
+ <<~STRING
200
+ "string#{i}" \\
201
+ STRING
202
+ end.join
203
+
204
+ index(<<~RUBY)
205
+ module Foo
206
+ local_var = #{concats}
207
+ "final"
208
+ @class_instance_var = #{concats}
209
+ "final"
210
+ @@class_var = #{concats}
211
+ "final"
212
+ $global_var = #{concats}
213
+ "final"
214
+ CONST = #{concats}
215
+ "final"
216
+ end
217
+ RUBY
218
+ end
219
+
220
+ def test_resolve_method_with_known_receiver
221
+ index(<<~RUBY)
222
+ module Foo
223
+ module Bar
224
+ def baz; end
225
+ end
226
+ end
227
+ RUBY
228
+
229
+ entry = T.must(@index.resolve_method("baz", "Foo::Bar"))
230
+ assert_equal("baz", entry.name)
231
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
232
+ end
233
+
234
+ def test_prefix_search_for_methods
235
+ index(<<~RUBY)
236
+ module Foo
237
+ module Bar
238
+ def baz; end
239
+ end
240
+ end
241
+ RUBY
242
+
243
+ entries = @index.prefix_search("ba")
244
+ refute_empty(entries)
245
+
246
+ entry = T.must(entries.first).first
247
+ assert_equal("baz", entry.name)
248
+ end
196
249
  end
197
250
  end
@@ -69,5 +69,19 @@ module RubyIndexer
69
69
  assert_equal(:"(a, (b, ))", parameter.name)
70
70
  assert_instance_of(Entry::RequiredParameter, parameter)
71
71
  end
72
+
73
+ def test_keeps_track_of_method_owner
74
+ index(<<~RUBY)
75
+ class Foo
76
+ def bar
77
+ end
78
+ end
79
+ RUBY
80
+
81
+ entry = T.must(@index["bar"].first)
82
+ owner_name = T.must(entry.owner).name
83
+
84
+ assert_equal("Foo", owner_name)
85
+ end
72
86
  end
73
87
  end
@@ -4,6 +4,9 @@
4
4
  module RubyLsp
5
5
  class Document
6
6
  extend T::Sig
7
+ extend T::Helpers
8
+
9
+ abstract!
7
10
 
8
11
  PositionShape = T.type_alias { { line: Integer, character: Integer } }
9
12
  RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
@@ -28,8 +31,8 @@ module RubyLsp
28
31
  @source = T.let(source, String)
29
32
  @version = T.let(version, Integer)
30
33
  @uri = T.let(uri, URI::Generic)
31
- @needs_parsing = T.let(false, T::Boolean)
32
- @parse_result = T.let(Prism.parse(@source), Prism::ParseResult)
34
+ @needs_parsing = T.let(true, T::Boolean)
35
+ @parse_result = T.let(parse, Prism::ParseResult)
33
36
  end
34
37
 
35
38
  sig { returns(Prism::ProgramNode) }
@@ -91,13 +94,8 @@ module RubyLsp
91
94
  @cache.clear
92
95
  end
93
96
 
94
- sig { void }
95
- def parse
96
- return unless @needs_parsing
97
-
98
- @needs_parsing = false
99
- @parse_result = Prism.parse(@source)
100
- end
97
+ sig { abstract.returns(Prism::ParseResult) }
98
+ def parse; end
101
99
 
102
100
  sig { returns(T::Boolean) }
103
101
  def syntax_error?
@@ -57,6 +57,8 @@ module RubyLsp
57
57
  warn(errored_addons.map(&:backtraces).join("\n\n"))
58
58
  end
59
59
 
60
+ RubyVM::YJIT.enable if defined? RubyVM::YJIT.enable
61
+
60
62
  perform_initial_indexing
61
63
  check_formatter_is_available
62
64
 
@@ -474,34 +476,32 @@ module RubyLsp
474
476
  def completion(uri, position)
475
477
  document = @store.get(uri)
476
478
 
477
- char_position = document.create_scanner.find_char_position(position)
478
-
479
- # When the user types in the first letter of a constant name, we actually receive the position of the next
480
- # immediate character. We check to see if the character is uppercase and then remove the offset to try to locate
481
- # the node, as it could not be a constant
482
- target_node_types = if ("A".."Z").cover?(document.source[char_position - 1])
483
- char_position -= 1
484
- [Prism::ConstantReadNode, Prism::ConstantPathNode]
485
- else
486
- [Prism::CallNode]
487
- end
488
-
489
- matched, parent, nesting = document.locate(document.tree, char_position, node_types: target_node_types)
479
+ # Completion always receives the position immediately after the character that was just typed. Here we adjust it
480
+ # back by 1, so that we find the right node
481
+ char_position = document.create_scanner.find_char_position(position) - 1
482
+ matched, parent, nesting = document.locate(
483
+ document.tree,
484
+ char_position,
485
+ node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode],
486
+ )
490
487
  return unless matched && parent
491
488
 
492
489
  target = case matched
493
490
  when Prism::CallNode
494
491
  message = matched.message
495
- return unless message == "require"
496
492
 
497
- args = matched.arguments&.arguments
498
- return if args.nil? || args.is_a?(Prism::ForwardingArgumentsNode)
493
+ if message == "require"
494
+ args = matched.arguments&.arguments
495
+ return if args.nil? || args.is_a?(Prism::ForwardingArgumentsNode)
499
496
 
500
- argument = args.first
501
- return unless argument.is_a?(Prism::StringNode)
502
- return unless (argument.location.start_offset..argument.location.end_offset).cover?(char_position)
497
+ argument = args.first
498
+ return unless argument.is_a?(Prism::StringNode)
499
+ return unless (argument.location.start_offset..argument.location.end_offset).cover?(char_position)
503
500
 
504
- argument
501
+ argument
502
+ else
503
+ matched
504
+ end
505
505
  when Prism::ConstantReadNode, Prism::ConstantPathNode
506
506
  if parent.is_a?(Prism::ConstantPathNode) && matched.is_a?(Prism::ConstantReadNode)
507
507
  parent
@@ -579,7 +579,7 @@ module RubyLsp
579
579
  # notification
580
580
  end
581
581
 
582
- sig { params(options: T::Hash[Symbol, T.untyped]).returns(Interface::InitializeResult) }
582
+ sig { params(options: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
583
583
  def initialize_request(options)
584
584
  @store.clear
585
585
 
@@ -680,7 +680,7 @@ module RubyLsp
680
680
  completion_provider = if enabled_features["completion"]
681
681
  Interface::CompletionOptions.new(
682
682
  resolve_provider: false,
683
- trigger_characters: ["/", *"A".."Z"],
683
+ trigger_characters: ["/"],
684
684
  completion_item: {
685
685
  labelDetailsSupport: true,
686
686
  },
@@ -716,7 +716,7 @@ module RubyLsp
716
716
 
717
717
  begin_progress("indexing-progress", "Ruby LSP: indexing files")
718
718
 
719
- Interface::InitializeResult.new(
719
+ {
720
720
  capabilities: Interface::ServerCapabilities.new(
721
721
  text_document_sync: Interface::TextDocumentSyncOptions.new(
722
722
  change: Constant::TextDocumentSyncKind::INCREMENTAL,
@@ -740,7 +740,12 @@ module RubyLsp
740
740
  definition_provider: enabled_features["definition"],
741
741
  workspace_symbol_provider: enabled_features["workspaceSymbol"],
742
742
  ),
743
- )
743
+ serverInfo: {
744
+ name: "Ruby LSP",
745
+ version: VERSION,
746
+ },
747
+ formatter: @store.formatter,
748
+ }
744
749
  end
745
750
 
746
751
  sig { void }
@@ -24,6 +24,10 @@ require "ruby_lsp/server"
24
24
  require "ruby_lsp/executor"
25
25
  require "ruby_lsp/requests"
26
26
  require "ruby_lsp/listener"
27
+ require "ruby_lsp/document"
28
+ require "ruby_lsp/ruby_document"
27
29
  require "ruby_lsp/store"
28
30
  require "ruby_lsp/addon"
29
31
  require "ruby_lsp/requests/support/rubocop_runner"
32
+
33
+ Bundler.ui.level = :silent