ruby-lsp-rails 0.3.26 → 0.3.28

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df7c6791cb1fc007fc4705e034eb60cde1d5905a1771b145c2d4bb6f36224658
4
- data.tar.gz: 1fdd80b7787bf31a237aa9f9f196c3170ee5e612ce8105c2fc20a63fd9027f73
3
+ metadata.gz: 231b488bf88047c642f2556980d6c0ce96edbeafbf2ded65a4104515584a5100
4
+ data.tar.gz: 207b91a283859f74804b4e825d4ca71ee645b0f73ac84e5d7e7348b382bf2745
5
5
  SHA512:
6
- metadata.gz: 8de8df639f440b7bcedb854e03ead56eb56517569dc31938759593cce32ff87d968b07b88598de01939fab8d3e1b8bb73108cf21b85c7f30493093d1f8d2973a
7
- data.tar.gz: 69550bcd5575bf7d8f6f82e7e738e53392fd776534dc88495274ab500e9ea3e11472e1008d05fe27de3ae40beee9a43d039be49866902e569ec7fdd0c91af161
6
+ metadata.gz: 8ef4e5363a31a1821da8d43738c5049239e827d609ffa8c8214f9e8fae148739770614a4f06af082da98a755bfd3522426560936d1ac57f0c14092ace8ced86b
7
+ data.tar.gz: 348fab1dc3ada6a6a2672c4c9b54e0ae0f67f2f056023929af7c76dac5d66583c32c9fbdcc1347fc3913cb59a3e30ae7e495977b762228aad6a52f502254a930
@@ -2,15 +2,3 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "ruby_lsp_rails/version"
5
- require "ruby_lsp_rails/railtie"
6
-
7
- module RubyLsp
8
- # # Supported features
9
- #
10
- # - [Hover](rdoc-ref:RubyLsp::Rails::Hover)
11
- # - [CodeLens](rdoc-ref:RubyLsp::Rails::CodeLens)
12
- # - [DocumentSymbol](rdoc-ref:RubyLsp::Rails::DocumentSymbol)
13
- # - [Definition](rdoc-ref:RubyLsp::Rails::Definition)
14
- module Rails
15
- end
16
- end
@@ -13,6 +13,7 @@ require_relative "hover"
13
13
  require_relative "code_lens"
14
14
  require_relative "document_symbol"
15
15
  require_relative "definition"
16
+ require_relative "completion"
16
17
  require_relative "indexing_enhancement"
17
18
 
18
19
  module RubyLsp
@@ -56,7 +57,6 @@ module RubyLsp
56
57
  @outgoing_queue << Notification.window_log_message("Activating Ruby LSP Rails add-on v#{VERSION}")
57
58
 
58
59
  register_additional_file_watchers(global_state: global_state, outgoing_queue: outgoing_queue)
59
- @global_state.index.register_enhancement(IndexingEnhancement.new(@global_state.index))
60
60
 
61
61
  # Start booting the real client in a background thread. Until this completes, the client will be a NullClient
62
62
  @client_mutex.unlock
@@ -120,6 +120,18 @@ module RubyLsp
120
120
  Definition.new(@rails_runner_client, response_builder, node_context, index, dispatcher)
121
121
  end
122
122
 
123
+ sig do
124
+ override.params(
125
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
126
+ node_context: NodeContext,
127
+ dispatcher: Prism::Dispatcher,
128
+ uri: URI::Generic,
129
+ ).void
130
+ end
131
+ def create_completion_listener(response_builder, node_context, dispatcher, uri)
132
+ Completion.new(@rails_runner_client, response_builder, node_context, dispatcher, uri)
133
+ end
134
+
123
135
  sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
124
136
  def workspace_did_change_watched_files(changes)
125
137
  if changes.any? { |c| c[:uri].end_with?("db/schema.rb") || c[:uri].end_with?("structure.sql") }
@@ -254,7 +254,7 @@ module RubyLsp
254
254
 
255
255
  @response_builder << create_code_lens(
256
256
  node,
257
- title: "Run",
257
+ title: "Run",
258
258
  command_name: "rubyLsp.runTask",
259
259
  arguments: [command],
260
260
  data: { type: "migrate" },
@@ -283,7 +283,7 @@ module RubyLsp
283
283
 
284
284
  @response_builder << create_code_lens(
285
285
  node,
286
- title: "Run",
286
+ title: "Run",
287
287
  command_name: "rubyLsp.runTest",
288
288
  arguments: arguments,
289
289
  data: { type: "test", **grouping_data },
@@ -291,7 +291,7 @@ module RubyLsp
291
291
 
292
292
  @response_builder << create_code_lens(
293
293
  node,
294
- title: "Run In Terminal",
294
+ title: "Run In Terminal",
295
295
  command_name: "rubyLsp.runTestInTerminal",
296
296
  arguments: arguments,
297
297
  data: { type: "test_in_terminal", **grouping_data },
@@ -0,0 +1,92 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Rails
6
+ class Completion
7
+ extend T::Sig
8
+ include Requests::Support::Common
9
+
10
+ sig do
11
+ override.params(
12
+ client: RunnerClient,
13
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
14
+ node_context: NodeContext,
15
+ dispatcher: Prism::Dispatcher,
16
+ uri: URI::Generic,
17
+ ).void
18
+ end
19
+ def initialize(client, response_builder, node_context, dispatcher, uri)
20
+ @response_builder = response_builder
21
+ @client = client
22
+ @node_context = node_context
23
+ dispatcher.register(
24
+ self,
25
+ :on_call_node_enter,
26
+ )
27
+ end
28
+
29
+ sig { params(node: Prism::CallNode).void }
30
+ def on_call_node_enter(node)
31
+ call_node = @node_context.call_node
32
+ return unless call_node
33
+
34
+ receiver = call_node.receiver
35
+ if call_node.name == :where && receiver.is_a?(Prism::ConstantReadNode)
36
+ handle_active_record_where_completions(node: node, receiver: receiver)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ sig { params(node: Prism::CallNode, receiver: Prism::ConstantReadNode).void }
43
+ def handle_active_record_where_completions(node:, receiver:)
44
+ resolved_class = @client.model(receiver.name.to_s)
45
+ return if resolved_class.nil?
46
+
47
+ arguments = T.must(@node_context.call_node).arguments&.arguments
48
+ indexed_call_node_args = T.let({}, T::Hash[String, Prism::Node])
49
+
50
+ if arguments
51
+ indexed_call_node_args = index_call_node_args(arguments: arguments)
52
+ return if indexed_call_node_args.values.any? { |v| v == node }
53
+ end
54
+
55
+ range = range_from_location(node.location)
56
+
57
+ resolved_class[:columns].each do |column|
58
+ next unless column[0].start_with?(node.name.to_s)
59
+ next if indexed_call_node_args.key?(column[0])
60
+
61
+ @response_builder << Interface::CompletionItem.new(
62
+ label: column[0],
63
+ filter_text: column[0],
64
+ label_details: Interface::CompletionItemLabelDetails.new(
65
+ description: "Filter #{receiver.name} records by #{column[0]}",
66
+ ),
67
+ text_edit: Interface::TextEdit.new(range: range, new_text: "#{column[0]}: "),
68
+ kind: Constant::CompletionItemKind::FIELD,
69
+ )
70
+ end
71
+ end
72
+
73
+ sig { params(arguments: T::Array[Prism::Node]).returns(T::Hash[String, Prism::Node]) }
74
+ def index_call_node_args(arguments:)
75
+ indexed_call_node_args = {}
76
+ arguments.each do |argument|
77
+ next unless argument.is_a?(Prism::KeywordHashNode)
78
+
79
+ argument.elements.each do |e|
80
+ next unless e.is_a?(Prism::AssocNode)
81
+
82
+ key = e.key
83
+ if key.is_a?(Prism::SymbolNode)
84
+ indexed_call_node_args[key.value] = e.value
85
+ end
86
+ end
87
+ end
88
+ indexed_call_node_args
89
+ end
90
+ end
91
+ end
92
+ end
@@ -144,15 +144,9 @@ module RubyLsp
144
144
  return unless methods
145
145
 
146
146
  methods.each do |target_method|
147
- location = target_method.location
148
- file_path = target_method.file_path
149
-
150
147
  @response_builder << Interface::Location.new(
151
- uri: URI::Generic.from_path(path: file_path).to_s,
152
- range: Interface::Range.new(
153
- start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
154
- end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
155
- ),
148
+ uri: target_method.uri.to_s,
149
+ range: range_from_location(target_method.location),
156
150
  )
157
151
  end
158
152
  end
@@ -8,25 +8,32 @@ module RubyLsp
8
8
 
9
9
  sig do
10
10
  override.params(
11
- owner: T.nilable(RubyIndexer::Entry::Namespace),
12
- node: Prism::CallNode,
13
- file_path: String,
14
- code_units_cache: T.any(
15
- T.proc.params(arg0: Integer).returns(Integer),
16
- Prism::CodeUnitsCache,
17
- ),
11
+ call_node: Prism::CallNode,
18
12
  ).void
19
13
  end
20
- def on_call_node_enter(owner, node, file_path, code_units_cache)
14
+ def on_call_node_enter(call_node)
15
+ owner = @listener.current_owner
21
16
  return unless owner
22
17
 
23
- name = node.name
24
-
25
- case name
18
+ case call_node.name
26
19
  when :extend
27
- handle_concern_extend(owner, node)
20
+ handle_concern_extend(owner, call_node)
28
21
  when :has_one, :has_many, :belongs_to, :has_and_belongs_to_many
29
- handle_association(owner, node, file_path, code_units_cache)
22
+ handle_association(owner, call_node)
23
+ # for `class_methods do` blocks within concerns
24
+ when :class_methods
25
+ handle_class_methods(owner, call_node)
26
+ end
27
+ end
28
+
29
+ sig do
30
+ override.params(
31
+ call_node: Prism::CallNode,
32
+ ).void
33
+ end
34
+ def on_call_node_leave(call_node)
35
+ if call_node.name == :class_methods && call_node.block
36
+ @listener.pop_namespace_stack
30
37
  end
31
38
  end
32
39
 
@@ -35,16 +42,11 @@ module RubyLsp
35
42
  sig do
36
43
  params(
37
44
  owner: RubyIndexer::Entry::Namespace,
38
- node: Prism::CallNode,
39
- file_path: String,
40
- code_units_cache: T.any(
41
- T.proc.params(arg0: Integer).returns(Integer),
42
- Prism::CodeUnitsCache,
43
- ),
45
+ call_node: Prism::CallNode,
44
46
  ).void
45
47
  end
46
- def handle_association(owner, node, file_path, code_units_cache)
47
- arguments = node.arguments&.arguments
48
+ def handle_association(owner, call_node)
49
+ arguments = call_node.arguments&.arguments
48
50
  return unless arguments
49
51
 
50
52
  name_arg = arguments.first
@@ -58,41 +60,22 @@ module RubyLsp
58
60
 
59
61
  return unless name
60
62
 
61
- loc = RubyIndexer::Location.from_prism_location(name_arg.location, code_units_cache)
63
+ loc = name_arg.location
62
64
 
63
65
  # Reader
64
- @index.add(RubyIndexer::Entry::Method.new(
65
- name,
66
- file_path,
67
- loc,
68
- loc,
69
- nil,
70
- [RubyIndexer::Entry::Signature.new([])],
71
- RubyIndexer::Entry::Visibility::PUBLIC,
72
- owner,
73
- ))
66
+ reader_signatures = [RubyIndexer::Entry::Signature.new([])]
67
+ @listener.add_method(name, loc, reader_signatures)
74
68
 
75
69
  # Writer
76
- @index.add(RubyIndexer::Entry::Method.new(
77
- "#{name}=",
78
- file_path,
79
- loc,
80
- loc,
81
- nil,
82
- [RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: name.to_sym)])],
83
- RubyIndexer::Entry::Visibility::PUBLIC,
84
- owner,
85
- ))
70
+ writer_signatures = [
71
+ RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: name.to_sym)]),
72
+ ]
73
+ @listener.add_method("#{name}=", loc, writer_signatures)
86
74
  end
87
75
 
88
- sig do
89
- params(
90
- owner: RubyIndexer::Entry::Namespace,
91
- node: Prism::CallNode,
92
- ).void
93
- end
94
- def handle_concern_extend(owner, node)
95
- arguments = node.arguments&.arguments
76
+ sig { params(owner: RubyIndexer::Entry::Namespace, call_node: Prism::CallNode).void }
77
+ def handle_concern_extend(owner, call_node)
78
+ arguments = call_node.arguments&.arguments
96
79
  return unless arguments
97
80
 
98
81
  arguments.each do |node|
@@ -101,7 +84,7 @@ module RubyLsp
101
84
  module_name = node.full_name
102
85
  next unless module_name == "ActiveSupport::Concern"
103
86
 
104
- @index.register_included_hook(owner.name) do |index, base|
87
+ @listener.register_included_hook do |index, base|
105
88
  class_methods_name = "#{owner.name}::ClassMethods"
106
89
 
107
90
  if index.indexed?(class_methods_name)
@@ -114,6 +97,13 @@ module RubyLsp
114
97
  # Do nothing
115
98
  end
116
99
  end
100
+
101
+ sig { params(owner: RubyIndexer::Entry::Namespace, call_node: Prism::CallNode).void }
102
+ def handle_class_methods(owner, call_node)
103
+ return unless call_node.block
104
+
105
+ @listener.add_module("ClassMethods", call_node.location, call_node.location)
106
+ end
117
107
  end
118
108
  end
119
109
  end
@@ -27,7 +27,7 @@ module RubyLsp
27
27
 
28
28
  NullClient.new
29
29
  end
30
- rescue Errno::ENOENT, StandardError => e # rubocop:disable Lint/ShadowedException
30
+ rescue StandardError => e
31
31
  unless outgoing_queue.closed?
32
32
  outgoing_queue << RubyLsp::Notification.window_log_message(
33
33
  <<~MESSAGE.chomp,
@@ -44,7 +44,6 @@ module RubyLsp
44
44
 
45
45
  class InitializationError < StandardError; end
46
46
  class MessageError < StandardError; end
47
- class IncompleteMessageError < MessageError; end
48
47
  class EmptyMessageError < MessageError; end
49
48
 
50
49
  extend T::Sig
@@ -109,7 +108,7 @@ module RubyLsp
109
108
  end,
110
109
  Thread,
111
110
  )
112
- rescue Errno::EPIPE, IncompleteMessageError
111
+ rescue StandardError
113
112
  raise InitializationError, @stderr.read
114
113
  end
115
114
 
@@ -282,7 +281,7 @@ module RubyLsp
282
281
  json = message.to_json
283
282
 
284
283
  @mutex.synchronize do
285
- @stdin.write("Content-Length: #{json.length}\r\n\r\n", json)
284
+ @stdin.write("Content-Length: #{json.bytesize}\r\n\r\n", json)
286
285
  end
287
286
  rescue Errno::EPIPE
288
287
  # The server connection died
@@ -13,13 +13,49 @@ module RubyLsp
13
13
  # Write a message to the client. Can be used for sending notifications to the editor
14
14
  def send_message(message)
15
15
  json_message = message.to_json
16
- @stdout.write("Content-Length: #{json_message.length}\r\n\r\n#{json_message}")
16
+ @stdout.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
17
17
  end
18
18
 
19
19
  # Log a message to the editor's output panel
20
20
  def log_message(message)
21
21
  $stderr.puts(message)
22
- send_message({ result: nil })
22
+ end
23
+
24
+ # Sends an error result to a request, if the request failed. DO NOT INVOKE THIS METHOD FOR NOTIFICATIONS! Use
25
+ # `log_message` instead, otherwise the client/server communication will go out of sync
26
+ def send_error_response(message)
27
+ send_message({ error: message })
28
+ end
29
+
30
+ # Sends a result back to the client
31
+ def send_result(result)
32
+ send_message({ result: result })
33
+ end
34
+
35
+ # Handle possible errors for a request. This should only be used for requests, which means messages that return a
36
+ # response back to the client. Errors are returned as an error object back to the client
37
+ def with_request_error_handling(request_name, &block)
38
+ block.call
39
+ rescue ActiveRecord::ConnectionNotEstablished
40
+ # Since this is a common problem, we show a specific error message to the user, instead of the full stack trace.
41
+ send_error_response("Request #{request_name} failed because database connection was not established.")
42
+ rescue ActiveRecord::NoDatabaseError
43
+ send_error_response("Request #{request_name} failed because the database does not exist.")
44
+ rescue => e
45
+ send_error_response("Request #{request_name} failed:\n#{e.full_message(highlight: false)}")
46
+ end
47
+
48
+ # Handle possible errors for a notification. This should only be used for notifications, which means messages that
49
+ # do not return a response back to the client. Errors are logged to the editor's output panel
50
+ def with_notification_error_handling(notification_name, &block)
51
+ block.call
52
+ rescue ActiveRecord::ConnectionNotEstablished
53
+ # Since this is a common problem, we show a specific error message to the user, instead of the full stack trace.
54
+ log_message("Request #{notification_name} failed because database connection was not established.")
55
+ rescue ActiveRecord::NoDatabaseError
56
+ log_message("Request #{notification_name} failed because the database does not exist.")
57
+ rescue => e
58
+ log_message("Request #{notification_name} failed:\n#{e.full_message(highlight: false)}")
23
59
  end
24
60
  end
25
61
 
@@ -88,11 +124,8 @@ module RubyLsp
88
124
  end
89
125
 
90
126
  def start
91
- # Load routes if they haven't been loaded yet (see https://github.com/rails/rails/pull/51614).
92
- routes_reloader = ::Rails.application.routes_reloader
93
- routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
94
-
95
- send_message({ result: { message: "ok", root: ::Rails.root.to_s } })
127
+ load_routes
128
+ send_result({ message: "ok", root: ::Rails.root.to_s })
96
129
 
97
130
  while @running
98
131
  headers = @stdin.gets("\r\n\r\n")
@@ -104,41 +137,50 @@ module RubyLsp
104
137
  end
105
138
 
106
139
  def execute(request, params)
107
- request_name = request
108
- request_name = "#{params[:server_addon_name]}##{params[:request_name]}" if request == "server_addon/delegate"
109
-
110
140
  case request
111
141
  when "shutdown"
112
142
  @running = false
113
143
  when "model"
114
- send_message(resolve_database_info_from_model(params.fetch(:name)))
144
+ with_request_error_handling(request) do
145
+ send_result(resolve_database_info_from_model(params.fetch(:name)))
146
+ end
115
147
  when "association_target_location"
116
- send_message(resolve_association_target(params))
148
+ with_request_error_handling(request) do
149
+ send_result(resolve_association_target(params))
150
+ end
117
151
  when "pending_migrations_message"
118
- send_message({ result: { pending_migrations_message: pending_migrations_message } })
152
+ with_request_error_handling(request) do
153
+ send_result({ pending_migrations_message: pending_migrations_message })
154
+ end
119
155
  when "run_migrations"
120
- send_message({ result: run_migrations })
156
+ with_request_error_handling(request) do
157
+ send_result(run_migrations)
158
+ end
121
159
  when "reload"
122
- ::Rails.application.reloader.reload!
160
+ with_notification_error_handling(request) do
161
+ ::Rails.application.reloader.reload!
162
+ end
123
163
  when "route_location"
124
- send_message(route_location(params.fetch(:name)))
164
+ with_request_error_handling(request) do
165
+ send_result(route_location(params.fetch(:name)))
166
+ end
125
167
  when "route_info"
126
- send_message(resolve_route_info(params))
168
+ with_request_error_handling(request) do
169
+ send_result(resolve_route_info(params))
170
+ end
127
171
  when "server_addon/register"
128
- require params[:server_addon_path]
129
- ServerAddon.finalize_registrations!(@stdout)
172
+ with_notification_error_handling(request) do
173
+ require params[:server_addon_path]
174
+ ServerAddon.finalize_registrations!(@stdout)
175
+ end
130
176
  when "server_addon/delegate"
131
177
  server_addon_name = params[:server_addon_name]
132
178
  request_name = params[:request_name]
179
+
180
+ # Do not wrap this in error handlers. Server add-ons need to have the flexibility to choose if they want to
181
+ # include a response or not as part of error handling, so a blanket approach is not appropriate.
133
182
  ServerAddon.delegate(server_addon_name, request_name, params.except(:request_name, :server_addon_name))
134
183
  end
135
- # Since this is a common problem, we show a specific error message to the user, instead of the full stack trace.
136
- rescue ActiveRecord::ConnectionNotEstablished
137
- log_message("Request #{request_name} failed because database connection was not established.")
138
- rescue ActiveRecord::NoDatabaseError
139
- log_message("Request #{request_name} failed because the database does not exist.")
140
- rescue => e
141
- log_message("Request #{request_name} failed:\n" + e.full_message(highlight: false))
142
184
  end
143
185
 
144
186
  private
@@ -156,19 +198,15 @@ module RubyLsp
156
198
  end
157
199
 
158
200
  source_location = route&.respond_to?(:source_location) && route.source_location
201
+ return unless source_location
159
202
 
160
- if source_location
161
- file, _, line = source_location.rpartition(":")
162
- body = {
163
- source_location: [::Rails.root.join(file).to_s, line],
164
- verb: route.verb,
165
- path: route.path.spec.to_s,
166
- }
203
+ file, _, line = source_location.rpartition(":")
167
204
 
168
- { result: body }
169
- else
170
- { result: nil }
171
- end
205
+ {
206
+ source_location: [::Rails.root.join(file).to_s, line],
207
+ verb: route.verb,
208
+ path: route.path.spec.to_s,
209
+ }
172
210
  end
173
211
 
174
212
  # Older versions of Rails don't support `route_source_locations`.
@@ -182,74 +220,48 @@ module RubyLsp
182
220
  end
183
221
 
184
222
  match_data = name.match(/^(.+)(_path|_url)$/)
185
- return { result: nil } unless match_data
223
+ return unless match_data
186
224
 
187
225
  key = match_data[1]
188
226
 
189
227
  # A token could match the _path or _url pattern, but not be an actual route.
190
228
  route = ::Rails.application.routes.named_routes.get(key)
191
- return { result: nil } unless route&.source_location
192
-
193
- {
194
- result: {
195
- location: ::Rails.root.join(route.source_location).to_s,
196
- },
197
- }
198
- rescue => e
199
- { error: e.full_message(highlight: false) }
229
+ return unless route&.source_location
230
+
231
+ { location: ::Rails.root.join(route.source_location).to_s }
200
232
  end
201
233
  else
202
234
  def route_location(name)
203
- { result: nil }
235
+ nil
204
236
  end
205
237
  end
206
238
 
207
239
  def resolve_database_info_from_model(model_name)
208
240
  const = ActiveSupport::Inflector.safe_constantize(model_name)
209
- unless active_record_model?(const)
210
- return {
211
- result: nil,
212
- }
213
- end
241
+ return unless active_record_model?(const)
214
242
 
215
243
  info = {
216
- result: {
217
- columns: const.columns.map { |column| [column.name, column.type, column.default, column.null] },
218
- primary_keys: Array(const.primary_key),
219
- },
244
+ columns: const.columns.map { |column| [column.name, column.type, column.default, column.null] },
245
+ primary_keys: Array(const.primary_key),
220
246
  }
221
247
 
222
248
  if ActiveRecord::Tasks::DatabaseTasks.respond_to?(:schema_dump_path)
223
- info[:result][:schema_file] =
224
- ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
225
-
249
+ info[:schema_file] = ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
226
250
  end
251
+
227
252
  info
228
- rescue => e
229
- { error: e.full_message(highlight: false) }
230
253
  end
231
254
 
232
255
  def resolve_association_target(params)
233
256
  const = ActiveSupport::Inflector.safe_constantize(params[:model_name])
234
- unless active_record_model?(const)
235
- return {
236
- result: nil,
237
- }
238
- end
257
+ return unless active_record_model?(const)
239
258
 
240
259
  association_klass = const.reflect_on_association(params[:association_name].intern).klass
241
-
242
260
  source_location = Object.const_source_location(association_klass.to_s)
243
261
 
244
- {
245
- result: {
246
- location: source_location.first + ":" + source_location.second.to_s,
247
- },
248
- }
262
+ { location: source_location.first + ":" + source_location.second.to_s }
249
263
  rescue NameError
250
- {
251
- result: nil,
252
- }
264
+ nil
253
265
  end
254
266
 
255
267
  def active_record_model?(const)
@@ -282,6 +294,14 @@ module RubyLsp
282
294
 
283
295
  { message: stdout, status: status.exitstatus }
284
296
  end
297
+
298
+ def load_routes
299
+ with_notification_error_handling("initial_load_routes") do
300
+ # Load routes if they haven't been loaded yet (see https://github.com/rails/rails/pull/51614).
301
+ routes_reloader = ::Rails.application.routes_reloader
302
+ routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
303
+ end
304
+ end
285
305
  end
286
306
  end
287
307
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Rails
6
- VERSION = "0.3.26"
6
+ VERSION = "0.3.28"
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.26
4
+ version: 0.3.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-08 00:00:00.000000000 Z
10
+ date: 2025-01-07 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: ruby-lsp
@@ -16,20 +15,20 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: 0.21.2
18
+ version: 0.23.0
20
19
  - - "<"
21
20
  - !ruby/object:Gem::Version
22
- version: 0.22.0
21
+ version: 0.24.0
23
22
  type: :runtime
24
23
  prerelease: false
25
24
  version_requirements: !ruby/object:Gem::Requirement
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- version: 0.21.2
28
+ version: 0.23.0
30
29
  - - "<"
31
30
  - !ruby/object:Gem::Version
32
- version: 0.22.0
31
+ version: 0.24.0
33
32
  description: A Ruby LSP addon that adds extra editor functionality for Rails applications
34
33
  email:
35
34
  - ruby@shopify.com
@@ -43,6 +42,7 @@ files:
43
42
  - lib/ruby-lsp-rails.rb
44
43
  - lib/ruby_lsp/ruby_lsp_rails/addon.rb
45
44
  - lib/ruby_lsp/ruby_lsp_rails/code_lens.rb
45
+ - lib/ruby_lsp/ruby_lsp_rails/completion.rb
46
46
  - lib/ruby_lsp/ruby_lsp_rails/definition.rb
47
47
  - lib/ruby_lsp/ruby_lsp_rails/document_symbol.rb
48
48
  - lib/ruby_lsp/ruby_lsp_rails/hover.rb
@@ -53,7 +53,6 @@ files:
53
53
  - lib/ruby_lsp/ruby_lsp_rails/support/associations.rb
54
54
  - lib/ruby_lsp/ruby_lsp_rails/support/callbacks.rb
55
55
  - lib/ruby_lsp/ruby_lsp_rails/support/location_builder.rb
56
- - lib/ruby_lsp_rails/railtie.rb
57
56
  - lib/ruby_lsp_rails/version.rb
58
57
  - lib/tasks/ruby_lsp_rails_tasks.rake
59
58
  homepage: https://github.com/Shopify/ruby-lsp-rails
@@ -65,7 +64,6 @@ metadata:
65
64
  source_code_uri: https://github.com/Shopify/ruby-lsp-rails
66
65
  changelog_uri: https://github.com/Shopify/ruby-lsp-rails/releases
67
66
  documentation_uri: https://shopify.github.io/ruby-lsp/rails-add-on.html
68
- post_install_message:
69
67
  rdoc_options: []
70
68
  require_paths:
71
69
  - lib
@@ -80,8 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
78
  - !ruby/object:Gem::Version
81
79
  version: '0'
82
80
  requirements: []
83
- rubygems_version: 3.5.23
84
- signing_key:
81
+ rubygems_version: 3.6.2
85
82
  specification_version: 4
86
83
  summary: A Ruby LSP addon for Rails
87
84
  test_files: []
@@ -1,21 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "rails"
5
-
6
- module RubyLsp
7
- module Rails
8
- class Railtie < ::Rails::Railtie
9
- config.ruby_lsp_rails = ActiveSupport::OrderedOptions.new
10
-
11
- initializer "ruby_lsp_rails.setup" do |_app|
12
- config.after_initialize do |_app|
13
- unless config.ruby_lsp_rails.server.nil?
14
- ActiveSupport::Deprecation.new.warn("The `ruby_lsp_rails.server` configuration option is no longer " \
15
- "needed and will be removed in a future release.")
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end