ruby-lsp-rails 0.4.0 → 0.4.1

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.
@@ -15,18 +15,9 @@ module RubyLsp
15
15
  # # ^ hovering here will show information about the User model
16
16
  # ```
17
17
  class Hover
18
- extend T::Sig
19
18
  include Requests::Support::Common
20
19
 
21
- sig do
22
- params(
23
- client: RunnerClient,
24
- response_builder: ResponseBuilders::Hover,
25
- node_context: NodeContext,
26
- global_state: GlobalState,
27
- dispatcher: Prism::Dispatcher,
28
- ).void
29
- end
20
+ #: (RunnerClient client, ResponseBuilders::Hover response_builder, NodeContext node_context, GlobalState global_state, Prism::Dispatcher dispatcher) -> void
30
21
  def initialize(client, response_builder, node_context, global_state, dispatcher)
31
22
  @client = client
32
23
  @response_builder = response_builder
@@ -35,26 +26,26 @@ module RubyLsp
35
26
  dispatcher.register(self, :on_constant_path_node_enter, :on_constant_read_node_enter)
36
27
  end
37
28
 
38
- sig { params(node: Prism::ConstantPathNode).void }
29
+ #: (Prism::ConstantPathNode node) -> void
39
30
  def on_constant_path_node_enter(node)
40
31
  entries = @index.resolve(node.slice, @nesting)
41
32
  return unless entries
42
33
 
43
- name = T.must(entries.first).name
34
+ name = entries.first.name
44
35
  generate_column_content(name)
45
36
  end
46
37
 
47
- sig { params(node: Prism::ConstantReadNode).void }
38
+ #: (Prism::ConstantReadNode node) -> void
48
39
  def on_constant_read_node_enter(node)
49
40
  entries = @index.resolve(node.name.to_s, @nesting)
50
41
  return unless entries
51
42
 
52
- generate_column_content(T.must(entries.first).name)
43
+ generate_column_content(entries.first.name)
53
44
  end
54
45
 
55
46
  private
56
47
 
57
- sig { params(name: String).void }
48
+ #: (String name) -> void
58
49
  def generate_column_content(name)
59
50
  model = @client.model(name)
60
51
  return if model.nil?
@@ -66,20 +57,41 @@ module RubyLsp
66
57
  category: :documentation,
67
58
  ) if schema_file
68
59
 
69
- @response_builder.push(
70
- model[:columns].map do |name, type, default_value, nullable|
71
- primary_key_suffix = " (PK)" if model[:primary_keys].include?(name)
72
- suffixes = []
73
- suffixes << "default: #{format_default(default_value, type)}" if default_value
74
- suffixes << "not null" unless nullable || primary_key_suffix
75
- suffix_string = " - #{suffixes.join(" - ")}" if suffixes.any?
76
- "**#{name}**: #{type}#{primary_key_suffix}#{suffix_string}\n"
77
- end.join("\n"),
78
- category: :documentation,
79
- )
60
+ if model[:columns].any?
61
+ @response_builder.push(
62
+ "### Columns",
63
+ category: :documentation,
64
+ )
65
+ @response_builder.push(
66
+ model[:columns].map do |name, type, default_value, nullable|
67
+ primary_key_suffix = " (PK)" if model[:primary_keys].include?(name)
68
+ foreign_key_suffix = " (FK)" if model[:foreign_keys].include?(name)
69
+ suffixes = []
70
+ suffixes << "default: #{format_default(default_value, type)}" if default_value
71
+ suffixes << "not null" unless nullable || primary_key_suffix
72
+ suffix_string = " - #{suffixes.join(" - ")}" if suffixes.any?
73
+ "- **#{name}**: #{type}#{primary_key_suffix}#{foreign_key_suffix}#{suffix_string}\n"
74
+ end.join("\n"),
75
+ category: :documentation,
76
+ )
77
+ end
78
+
79
+ if model[:indexes].any?
80
+ @response_builder.push(
81
+ "### Indexes",
82
+ category: :documentation,
83
+ )
84
+ @response_builder.push(
85
+ model[:indexes].map do |index|
86
+ uniqueness = index[:unique] ? " (unique)" : ""
87
+ "- **#{index[:name]}** (#{index[:columns].join(",")})#{uniqueness}"
88
+ end.join("\n"),
89
+ category: :documentation,
90
+ )
91
+ end
80
92
  end
81
93
 
82
- sig { params(default_value: String, type: String).returns(String) }
94
+ #: (String default_value, String type) -> String
83
95
  def format_default(default_value, type)
84
96
  case type
85
97
  when "boolean"
@@ -4,13 +4,8 @@
4
4
  module RubyLsp
5
5
  module Rails
6
6
  class IndexingEnhancement < RubyIndexer::Enhancement
7
- extend T::Sig
8
-
9
- sig do
10
- override.params(
11
- call_node: Prism::CallNode,
12
- ).void
13
- end
7
+ # @override
8
+ #: (Prism::CallNode call_node) -> void
14
9
  def on_call_node_enter(call_node)
15
10
  owner = @listener.current_owner
16
11
  return unless owner
@@ -26,11 +21,8 @@ module RubyLsp
26
21
  end
27
22
  end
28
23
 
29
- sig do
30
- override.params(
31
- call_node: Prism::CallNode,
32
- ).void
33
- end
24
+ # @override
25
+ #: (Prism::CallNode call_node) -> void
34
26
  def on_call_node_leave(call_node)
35
27
  if call_node.name == :class_methods && call_node.block
36
28
  @listener.pop_namespace_stack
@@ -39,12 +31,7 @@ module RubyLsp
39
31
 
40
32
  private
41
33
 
42
- sig do
43
- params(
44
- owner: RubyIndexer::Entry::Namespace,
45
- call_node: Prism::CallNode,
46
- ).void
47
- end
34
+ #: (RubyIndexer::Entry::Namespace owner, Prism::CallNode call_node) -> void
48
35
  def handle_association(owner, call_node)
49
36
  arguments = call_node.arguments&.arguments
50
37
  return unless arguments
@@ -73,7 +60,7 @@ module RubyLsp
73
60
  @listener.add_method("#{name}=", loc, writer_signatures)
74
61
  end
75
62
 
76
- sig { params(owner: RubyIndexer::Entry::Namespace, call_node: Prism::CallNode).void }
63
+ #: (RubyIndexer::Entry::Namespace owner, Prism::CallNode call_node) -> void
77
64
  def handle_concern_extend(owner, call_node)
78
65
  arguments = call_node.arguments&.arguments
79
66
  return unless arguments
@@ -98,7 +85,7 @@ module RubyLsp
98
85
  end
99
86
  end
100
87
 
101
- sig { params(owner: RubyIndexer::Entry::Namespace, call_node: Prism::CallNode).void }
88
+ #: (RubyIndexer::Entry::Namespace owner, Prism::CallNode call_node) -> void
102
89
  def handle_class_methods(owner, call_node)
103
90
  return unless call_node.block
104
91
 
@@ -0,0 +1,150 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Rails
6
+ class RailsTestStyle < Listeners::TestDiscovery
7
+ BASE_COMMAND = "#{RbConfig.ruby} bin/rails test" #: String
8
+
9
+ class << self
10
+ #: (Array[Hash[Symbol, untyped]]) -> Array[String]
11
+ def resolve_test_commands(items)
12
+ commands = []
13
+ queue = items.dup
14
+
15
+ full_files = []
16
+
17
+ until queue.empty?
18
+ item = T.must(queue.shift)
19
+ tags = Set.new(item[:tags])
20
+ next unless tags.include?("framework:rails")
21
+
22
+ children = item[:children]
23
+ uri = URI(item[:uri])
24
+ path = uri.full_path
25
+ next unless path
26
+
27
+ if tags.include?("test_dir")
28
+ if children.empty?
29
+ full_files.concat(Dir.glob(
30
+ "#{path}/**/{*_test,test_*}.rb",
31
+ File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME,
32
+ ))
33
+ end
34
+ elsif tags.include?("test_file")
35
+ full_files << path if children.empty?
36
+ elsif tags.include?("test_group")
37
+ commands << "#{BASE_COMMAND} #{path} --name \"/#{Shellwords.escape(item[:id])}(#|::)/\""
38
+ else
39
+ full_files << "#{path}:#{item.dig(:range, :start, :line) + 1}"
40
+ end
41
+
42
+ queue.concat(children)
43
+ end
44
+
45
+ unless full_files.empty?
46
+ commands << "#{BASE_COMMAND} #{full_files.join(" ")}"
47
+ end
48
+
49
+ commands
50
+ end
51
+ end
52
+
53
+ #: (RunnerClient client, ResponseBuilders::TestCollection response_builder, GlobalState global_state, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
54
+ def initialize(client, response_builder, global_state, dispatcher, uri)
55
+ super(response_builder, global_state, dispatcher, uri)
56
+
57
+ dispatcher.register(
58
+ self,
59
+ :on_class_node_enter,
60
+ :on_call_node_enter,
61
+ :on_def_node_enter,
62
+ )
63
+ end
64
+
65
+ #: (Prism::ClassNode node) -> void
66
+ def on_class_node_enter(node)
67
+ with_test_ancestor_tracking(node) do |name, ancestors|
68
+ if declarative_minitest?(ancestors, name)
69
+ test_item = Requests::Support::TestItem.new(
70
+ name,
71
+ name,
72
+ @uri,
73
+ range_from_node(node),
74
+ framework: :rails,
75
+ )
76
+
77
+ @response_builder.add(test_item)
78
+ end
79
+ end
80
+ end
81
+
82
+ #: (Prism::CallNode node) -> void
83
+ def on_call_node_enter(node)
84
+ return unless node.name == :test
85
+ return unless node.block
86
+
87
+ arguments = node.arguments&.arguments
88
+ first_arg = arguments&.first
89
+ return unless first_arg.is_a?(Prism::StringNode)
90
+
91
+ test_name = first_arg.content
92
+ test_name = "<empty test name>" if test_name.empty?
93
+
94
+ # Rails' `test "foo bar"` helper defines a method `def test_foo_bar`. We normalize test names
95
+ # the same way (spaces to underscores, prefix with `test_`) to match the actual method names
96
+ # Rails uses at runtime, ensuring proper test discovery and execution.
97
+ rails_normalized_name = "test_#{test_name.gsub(/\s+/, "_")}"
98
+
99
+ add_test_item(node, rails_normalized_name)
100
+ end
101
+
102
+ #: (Prism::DefNode node) -> void
103
+ def on_def_node_enter(node)
104
+ return if @visibility_stack.last != :public
105
+
106
+ name = node.name.to_s
107
+ return unless name.start_with?("test_")
108
+
109
+ add_test_item(node, name)
110
+ end
111
+
112
+ private
113
+
114
+ #: (Array[String] attached_ancestors, String fully_qualified_name) -> bool
115
+ def declarative_minitest?(attached_ancestors, fully_qualified_name)
116
+ # The declarative test style is present as long as the class extends
117
+ # ActiveSupport::Testing::Declarative
118
+ name_parts = fully_qualified_name.split("::")
119
+ singleton_name = "#{name_parts.join("::")}::<Class:#{name_parts.last}>"
120
+ @index.linearized_ancestors_of(singleton_name).include?("ActiveSupport::Testing::Declarative")
121
+ rescue RubyIndexer::Index::NonExistingNamespaceError
122
+ false
123
+ end
124
+
125
+ #: (Prism::Node node, String test_name) -> void
126
+ def add_test_item(node, test_name)
127
+ test_item = group_test_item
128
+ return unless test_item
129
+
130
+ test_item.add(Requests::Support::TestItem.new(
131
+ "#{test_item.id}##{test_name}",
132
+ test_name,
133
+ @uri,
134
+ range_from_node(node),
135
+ framework: :rails,
136
+ ))
137
+ end
138
+
139
+ #: -> Requests::Support::TestItem?
140
+ def group_test_item
141
+ current_group_name = RubyIndexer::Index.actual_nesting(@nesting, nil).join("::")
142
+
143
+ # If we're finding a test method, but for the wrong framework, then the group test item will not have been
144
+ # previously pushed and thus we return early and avoid adding items for a framework this listener is not
145
+ # interested in
146
+ @response_builder[current_group_name]
147
+ end
148
+ end
149
+ end
150
+ end
@@ -8,9 +8,7 @@ module RubyLsp
8
8
  module Rails
9
9
  class RunnerClient
10
10
  class << self
11
- extend T::Sig
12
-
13
- sig { params(outgoing_queue: Thread::Queue, global_state: RubyLsp::GlobalState).returns(RunnerClient) }
11
+ #: (Thread::Queue outgoing_queue, RubyLsp::GlobalState global_state) -> RunnerClient
14
12
  def create_client(outgoing_queue, global_state)
15
13
  if File.exist?("bin/rails")
16
14
  new(outgoing_queue, global_state)
@@ -46,12 +44,10 @@ module RubyLsp
46
44
  class MessageError < StandardError; end
47
45
  class EmptyMessageError < MessageError; end
48
46
 
49
- extend T::Sig
50
-
51
- sig { returns(String) }
47
+ #: String
52
48
  attr_reader :rails_root
53
49
 
54
- sig { params(outgoing_queue: Thread::Queue, global_state: RubyLsp::GlobalState).void }
50
+ #: (Thread::Queue outgoing_queue, RubyLsp::GlobalState global_state) -> void
55
51
  def initialize(outgoing_queue, global_state)
56
52
  @outgoing_queue = T.let(outgoing_queue, Thread::Queue)
57
53
  @mutex = T.let(Mutex.new, Mutex)
@@ -128,7 +124,7 @@ module RubyLsp
128
124
  raise InitializationError, @stderr.read
129
125
  end
130
126
 
131
- sig { params(server_addon_path: String).void }
127
+ #: (String server_addon_path) -> void
132
128
  def register_server_addon(server_addon_path)
133
129
  send_notification("server_addon/register", server_addon_path: server_addon_path)
134
130
  rescue MessageError
@@ -139,7 +135,7 @@ module RubyLsp
139
135
  nil
140
136
  end
141
137
 
142
- sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
138
+ #: (String name) -> Hash[Symbol, untyped]?
143
139
  def model(name)
144
140
  make_request("model", name: name)
145
141
  rescue MessageError
@@ -150,12 +146,7 @@ module RubyLsp
150
146
  nil
151
147
  end
152
148
 
153
- sig do
154
- params(
155
- model_name: String,
156
- association_name: String,
157
- ).returns(T.nilable(T::Hash[Symbol, T.untyped]))
158
- end
149
+ #: (model_name: String, association_name: String) -> Hash[Symbol, untyped]?
159
150
  def association_target_location(model_name:, association_name:)
160
151
  make_request(
161
152
  "association_target_location",
@@ -170,7 +161,7 @@ module RubyLsp
170
161
  nil
171
162
  end
172
163
 
173
- sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
164
+ #: (String name) -> Hash[Symbol, untyped]?
174
165
  def route_location(name)
175
166
  make_request("route_location", name: name)
176
167
  rescue MessageError
@@ -181,7 +172,7 @@ module RubyLsp
181
172
  nil
182
173
  end
183
174
 
184
- sig { params(controller: String, action: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
175
+ #: (controller: String, action: String) -> Hash[Symbol, untyped]?
185
176
  def route(controller:, action:)
186
177
  make_request("route_info", controller: controller, action: action)
187
178
  rescue MessageError
@@ -193,7 +184,7 @@ module RubyLsp
193
184
  end
194
185
 
195
186
  # Delegates a notification to a server add-on
196
- sig { params(server_addon_name: String, request_name: String, params: T.untyped).void }
187
+ #: (server_addon_name: String, request_name: String, **untyped params) -> void
197
188
  def delegate_notification(server_addon_name:, request_name:, **params)
198
189
  send_notification(
199
190
  "server_addon/delegate",
@@ -203,7 +194,7 @@ module RubyLsp
203
194
  )
204
195
  end
205
196
 
206
- sig { returns(T.nilable(String)) }
197
+ #: -> String?
207
198
  def pending_migrations_message
208
199
  response = make_request("pending_migrations_message")
209
200
  response[:pending_migrations_message] if response
@@ -215,7 +206,7 @@ module RubyLsp
215
206
  nil
216
207
  end
217
208
 
218
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
209
+ #: -> Hash[Symbol, untyped]?
219
210
  def run_migrations
220
211
  make_request("run_migrations")
221
212
  rescue MessageError
@@ -227,13 +218,7 @@ module RubyLsp
227
218
  end
228
219
 
229
220
  # Delegates a request to a server add-on
230
- sig do
231
- params(
232
- server_addon_name: String,
233
- request_name: String,
234
- params: T.untyped,
235
- ).returns(T.nilable(T::Hash[Symbol, T.untyped]))
236
- end
221
+ #: (server_addon_name: String, request_name: String, **untyped params) -> Hash[Symbol, untyped]?
237
222
  def delegate_request(server_addon_name:, request_name:, **params)
238
223
  make_request(
239
224
  "server_addon/delegate",
@@ -245,7 +230,7 @@ module RubyLsp
245
230
  nil
246
231
  end
247
232
 
248
- sig { void }
233
+ #: -> void
249
234
  def trigger_reload
250
235
  log_message("Reloading Rails application")
251
236
  send_notification("reload")
@@ -257,7 +242,7 @@ module RubyLsp
257
242
  nil
258
243
  end
259
244
 
260
- sig { void }
245
+ #: -> void
261
246
  def shutdown
262
247
  log_message("Ruby LSP Rails shutting down server")
263
248
  send_message("shutdown")
@@ -268,34 +253,30 @@ module RubyLsp
268
253
  force_kill
269
254
  end
270
255
 
271
- sig { returns(T::Boolean) }
256
+ #: -> bool
272
257
  def stopped?
273
258
  [@stdin, @stdout, @stderr].all?(&:closed?) && !@wait_thread.alive?
274
259
  end
275
260
 
276
- sig { returns(T::Boolean) }
261
+ #: -> bool
277
262
  def connected?
278
263
  true
279
264
  end
280
265
 
281
266
  private
282
267
 
283
- sig do
284
- params(
285
- request: String,
286
- params: T.untyped,
287
- ).returns(T.nilable(T::Hash[Symbol, T.untyped]))
288
- end
268
+ #: (String request, **untyped params) -> Hash[Symbol, untyped]?
289
269
  def make_request(request, **params)
290
270
  send_message(request, **params)
291
271
  read_response
292
272
  end
293
273
 
294
274
  # Notifications are like messages, but one-way, with no response sent back.
295
- sig { params(request: String, params: T.untyped).void }
275
+ #: (String request, **untyped params) -> void
296
276
  def send_notification(request, **params) = send_message(request, **params)
297
277
 
298
- sig { overridable.params(request: String, params: T.untyped).void }
278
+ # @overridable
279
+ #: (String request, **untyped params) -> void
299
280
  def send_message(request, **params)
300
281
  message = { method: request }
301
282
  message[:params] = params
@@ -308,7 +289,8 @@ module RubyLsp
308
289
  # The server connection died
309
290
  end
310
291
 
311
- sig { overridable.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
292
+ # @overridable
293
+ #: -> Hash[Symbol, untyped]?
312
294
  def read_response
313
295
  raw_response = @mutex.synchronize do
314
296
  content_length = read_content_length
@@ -334,20 +316,20 @@ module RubyLsp
334
316
  nil
335
317
  end
336
318
 
337
- sig { void }
319
+ #: -> void
338
320
  def force_kill
339
321
  # Windows does not support the `TERM` signal, so we're forced to use `KILL` here
340
322
  Process.kill(T.must(Signal.list["KILL"]), @wait_thread.pid)
341
323
  end
342
324
 
343
- sig { params(message: ::String, type: ::Integer).void }
325
+ #: (::String message, ?type: ::Integer) -> void
344
326
  def log_message(message, type: RubyLsp::Constant::MessageType::LOG)
345
327
  return if @outgoing_queue.closed?
346
328
 
347
329
  @outgoing_queue << RubyLsp::Notification.window_log_message(message, type: type)
348
330
  end
349
331
 
350
- sig { returns(T.nilable(Integer)) }
332
+ #: -> Integer?
351
333
  def read_content_length
352
334
  headers = @stdout.gets("\r\n\r\n")
353
335
  return unless headers
@@ -359,7 +341,7 @@ module RubyLsp
359
341
  end
360
342
 
361
343
  # Read a server notification from stderr. Only intended to be used by notifier thread
362
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
344
+ #: -> Hash[Symbol, untyped]?
363
345
  def read_notification
364
346
  headers = @stderr.gets("\r\n\r\n")
365
347
  return unless headers
@@ -373,7 +355,7 @@ module RubyLsp
373
355
  JSON.parse(raw_content, symbolize_names: true)
374
356
  end
375
357
 
376
- sig { params(global_state: GlobalState).returns(String) }
358
+ #: (GlobalState global_state) -> String
377
359
  def server_relevant_capabilities(global_state)
378
360
  {
379
361
  supports_progress: global_state.client_capabilities.supports_progress,
@@ -382,45 +364,48 @@ module RubyLsp
382
364
  end
383
365
 
384
366
  class NullClient < RunnerClient
385
- extend T::Sig
386
-
387
- sig { void }
367
+ #: -> void
388
368
  def initialize # rubocop:disable Lint/MissingSuper
389
369
  end
390
370
 
391
- sig { override.void }
371
+ # @override
372
+ #: -> void
392
373
  def shutdown
393
374
  # no-op
394
375
  end
395
376
 
396
- sig { override.returns(T::Boolean) }
377
+ # @override
378
+ #: -> bool
397
379
  def stopped?
398
380
  true
399
381
  end
400
382
 
401
- sig { override.returns(String) }
383
+ # @override
384
+ #: -> String
402
385
  def rails_root
403
386
  Dir.pwd
404
387
  end
405
388
 
406
- sig { returns(T::Boolean) }
389
+ #: -> bool
407
390
  def connected?
408
391
  false
409
392
  end
410
393
 
411
394
  private
412
395
 
413
- sig { params(message: ::String, type: ::Integer).void }
396
+ #: (::String message, ?type: ::Integer) -> void
414
397
  def log_message(message, type: RubyLsp::Constant::MessageType::LOG)
415
398
  # no-op
416
399
  end
417
400
 
418
- sig { override.params(request: String, params: T.untyped).void }
401
+ # @override
402
+ #: (String request, **untyped params) -> void
419
403
  def send_message(request, **params)
420
404
  # no-op
421
405
  end
422
406
 
423
- sig { override.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
407
+ # @override
408
+ #: -> Hash[Symbol, untyped]?
424
409
  def read_response
425
410
  # no-op
426
411
  end
@@ -364,6 +364,8 @@ module RubyLsp
364
364
  info = {
365
365
  columns: const.columns.map { |column| [column.name, column.type, column.default, column.null] },
366
366
  primary_keys: Array(const.primary_key),
367
+ foreign_keys: collect_model_foreign_keys(const),
368
+ indexes: collect_model_indexes(const),
367
369
  }
368
370
 
369
371
  if ActiveRecord::Tasks::DatabaseTasks.respond_to?(:schema_dump_path)
@@ -435,6 +437,36 @@ module RubyLsp
435
437
  ::ActionView::PathRegistry.file_system_resolver_hooks.clear
436
438
  end
437
439
  end
440
+
441
+ def collect_model_foreign_keys(model)
442
+ return [] unless model.connection.respond_to?(:supports_foreign_keys?) &&
443
+ model.connection.supports_foreign_keys?
444
+
445
+ model.connection.foreign_keys(model.table_name).map do |key_definition|
446
+ key_definition.options[:column]
447
+ end
448
+ end
449
+
450
+ def collect_model_indexes(model)
451
+ return [] unless database_supports_indexing?(model)
452
+
453
+ model.connection.indexes(model.table_name).map do |index_definition|
454
+ {
455
+ name: index_definition.name,
456
+ columns: index_definition.columns,
457
+ unique: index_definition.unique,
458
+ }
459
+ end
460
+ end
461
+
462
+ def database_supports_indexing?(model)
463
+ return @database_supports_indexing if instance_variable_defined?(:@database_supports_indexing)
464
+
465
+ model.connection.indexes(model.table_name)
466
+ @database_supports_indexing = true
467
+ rescue NotImplementedError
468
+ @database_supports_indexing = false
469
+ end
438
470
  end
439
471
  end
440
472
  end
@@ -4,9 +4,7 @@
4
4
  module RubyLsp
5
5
  module Rails
6
6
  module ActiveSupportTestCaseHelper
7
- extend T::Sig
8
-
9
- sig { params(node: Prism::CallNode).returns(T.nilable(String)) }
7
+ #: (Prism::CallNode node) -> String?
10
8
  def extract_test_case_name(node)
11
9
  message_value = node.message
12
10
  return unless message_value == "test" || message_value == "it"
@@ -6,9 +6,7 @@ module RubyLsp
6
6
  module Support
7
7
  class LocationBuilder
8
8
  class << self
9
- extend T::Sig
10
-
11
- sig { params(location_string: String).returns(Interface::Location) }
9
+ #: (String location_string) -> Interface::Location
12
10
  def line_location_from_s(location_string)
13
11
  *file_parts, line = location_string.split(":")
14
12
  raise ArgumentError, "Invalid location string given" if file_parts.empty?
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Rails
6
- VERSION = "0.4.0"
6
+ VERSION = "0.4.1"
7
7
  end
8
8
  end