ruby-lsp-rails 0.3.8 → 0.3.13

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: accc5f9539ddceaa7fde3dfdb64fcc74f697e9d17178c45e9271df463cad4f18
4
- data.tar.gz: d594ee73741270b0d6837680a5bacc6e912c1ca856aeaa5806e7d1ad9e96aeb8
3
+ metadata.gz: e2543aa7d39f0a6b231112f9ddb365692c7e8d06eb14681da551ab55fd8f5a4d
4
+ data.tar.gz: e466ccd6d90934e083e638efdcedfb5d6b8f85fbce70b139e1542eeb2ab8a139
5
5
  SHA512:
6
- metadata.gz: 955347d73c343311571e62d7c0bb656a3cc9176f8e6ff87ab010287345d754b49f1889c436c716ce0a40c2aa45938f094124cd18315f787c854db7aed2172ee1
7
- data.tar.gz: 65731a4414f5b68884ea6003097b5b1e67caaaaf2a954b0d25de0ed937cc7600d1b1e4e8d2c25a28b25f526c72fc05e6f9a1996874f95584c4450a4212ef9269
6
+ metadata.gz: 8f7c82462682d93fd6b944c2705c8b62b424b4abc3636d1f2540eeee1ab9f7e2896eb77d338697bb65a0a64752529085f38e49bd5a75bfc668fa9d31dd5b0d10
7
+ data.tar.gz: a8978fdebb07ba8b76dd10a725b3b3c736961f103cefa806fbc0302b494516e0a196105bf7fac9d114c963a62c8a706959d53f1509a4ed343e2e9d24633c4372
data/README.md CHANGED
@@ -9,6 +9,8 @@ Ruby LSP Rails is a [Ruby LSP](https://github.com/Shopify/ruby-lsp) addon for ex
9
9
  * Navigate to associations, validations, callbacks and test cases using your editor's "Go to Symbol" feature, or outline view.
10
10
  * Jump to the definition of callbacks using your editor's "Go to Definition" feature.
11
11
  * Jump to the declaration of a route.
12
+ * Code Lens allowing fast-forwarding or rewinding of migrations.
13
+ * Code Lens showing the path that a route action corresponds to.
12
14
 
13
15
  ## Installation
14
16
 
@@ -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 "indexing_enhancement"
16
17
 
17
18
  module RubyLsp
18
19
  module Rails
@@ -35,6 +36,8 @@ module RubyLsp
35
36
  # Start booting the real client in a background thread. Until this completes, the client will be a NullClient
36
37
  Thread.new { @client = RunnerClient.create_client }
37
38
  register_additional_file_watchers(global_state: global_state, message_queue: message_queue)
39
+
40
+ T.must(@global_state).index.register_enhancement(IndexingEnhancement.new)
38
41
  end
39
42
 
40
43
  sig { override.void }
@@ -51,9 +54,7 @@ module RubyLsp
51
54
  ).void
52
55
  end
53
56
  def create_code_lens_listener(response_builder, uri, dispatcher)
54
- return unless T.must(@global_state).test_library == "rails"
55
-
56
- CodeLens.new(response_builder, uri, dispatcher)
57
+ CodeLens.new(@client, T.must(@global_state), response_builder, uri, dispatcher)
57
58
  end
58
59
 
59
60
  sig do
@@ -79,7 +80,9 @@ module RubyLsp
79
80
 
80
81
  sig do
81
82
  override.params(
82
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
83
+ response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
84
+ Interface::Location, Interface::LocationLink
85
+ )],
83
86
  uri: URI::Generic,
84
87
  node_context: NodeContext,
85
88
  dispatcher: Prism::Dispatcher,
@@ -5,11 +5,22 @@ module RubyLsp
5
5
  module Rails
6
6
  # ![CodeLens demo](../../code_lens.gif)
7
7
  #
8
- # This feature adds several CodeLens features for Rails applications using Active Support test cases:
8
+ # This feature adds Code Lens features for Rails applications.
9
+ #
10
+ # For Active Support test cases:
9
11
  #
10
12
  # - Run tests in the VS Terminal
11
13
  # - Run tests in the VS Code Test Explorer
12
14
  # - Debug tests
15
+ # - Run migrations in the VS Terminal
16
+ #
17
+ # For Rails controllers:
18
+ #
19
+ # - See the path corresponding to an action
20
+ # - Click on the action's Code Lens to jump to its declaration in the routes.
21
+ #
22
+ # Note: This depends on a support for the `rubyLsp.openFile` command.
23
+ # For the VS Code extension this is built-in, but for other editors this may require some custom configuration.
13
24
  #
14
25
  # The
15
26
  # [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
@@ -32,12 +43,34 @@ module RubyLsp
32
43
  # # ...
33
44
  # end
34
45
  # end
35
- # ````
46
+ # ```
47
+ #
48
+ # # Example:
49
+ # ```ruby
50
+ # Run
51
+ # class AddFirstNameToUsers < ActiveRecord::Migration[7.1]
52
+ # # ...
53
+ # end
54
+ # ```
36
55
  #
37
56
  # The code lenses will be displayed above the class and above each test method.
38
57
  #
39
58
  # Note: When using the Test Explorer view, if your code contains a statement to pause execution (e.g. `debugger`) it
40
59
  # will cause the test runner to hang.
60
+ #
61
+ # For the following code, assuming the routing contains `resources :users`, a Code Lens will be seen above each
62
+ # action.
63
+ #
64
+ # ```ruby
65
+ # class UsersController < ApplicationController
66
+ # GET /users(.:format)
67
+ # def index # <- Will show code lens above for the path
68
+ # end
69
+ # end
70
+ # ```
71
+ #
72
+ # Note: Complex routing configurations may not be supported.
73
+ #
41
74
  class CodeLens
42
75
  extend T::Sig
43
76
  include Requests::Support::Common
@@ -45,18 +78,31 @@ module RubyLsp
45
78
 
46
79
  sig do
47
80
  params(
81
+ client: RunnerClient,
82
+ global_state: GlobalState,
48
83
  response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
49
84
  uri: URI::Generic,
50
85
  dispatcher: Prism::Dispatcher,
51
86
  ).void
52
87
  end
53
- def initialize(response_builder, uri, dispatcher)
88
+ def initialize(client, global_state, response_builder, uri, dispatcher)
89
+ @client = client
90
+ @global_state = global_state
54
91
  @response_builder = response_builder
55
92
  @path = T.let(uri.to_standardized_path, T.nilable(String))
56
93
  @group_id = T.let(1, Integer)
57
94
  @group_id_stack = T.let([], T::Array[Integer])
95
+ @constant_name_stack = T.let([], T::Array[[String, T.nilable(String)]])
58
96
 
59
- dispatcher.register(self, :on_call_node_enter, :on_class_node_enter, :on_def_node_enter, :on_class_node_leave)
97
+ dispatcher.register(
98
+ self,
99
+ :on_call_node_enter,
100
+ :on_class_node_enter,
101
+ :on_def_node_enter,
102
+ :on_class_node_leave,
103
+ :on_module_node_enter,
104
+ :on_module_node_leave,
105
+ )
60
106
  end
61
107
 
62
108
  sig { params(node: Prism::CallNode).void }
@@ -74,46 +120,146 @@ module RubyLsp
74
120
  sig { params(node: Prism::DefNode).void }
75
121
  def on_def_node_enter(node)
76
122
  method_name = node.name.to_s
123
+
77
124
  if method_name.start_with?("test_")
78
125
  line_number = node.location.start_line
79
126
  command = "#{test_command} #{@path}:#{line_number}"
80
127
  add_test_code_lens(node, name: method_name, command: command, kind: :example)
81
128
  end
129
+
130
+ if controller?
131
+ add_route_code_lens_to_action(node)
132
+ add_jump_to_view(node)
133
+ end
82
134
  end
83
135
 
84
136
  sig { params(node: Prism::ClassNode).void }
85
137
  def on_class_node_enter(node)
86
138
  class_name = node.constant_path.slice
139
+ superclass_name = node.superclass&.slice
140
+
87
141
  if class_name.end_with?("Test")
88
142
  command = "#{test_command} #{@path}"
89
143
  add_test_code_lens(node, name: class_name, command: command, kind: :group)
90
144
  @group_id_stack.push(@group_id)
91
145
  @group_id += 1
92
146
  end
147
+
148
+ if superclass_name&.start_with?("ActiveRecord::Migration")
149
+ command = "#{migrate_command} VERSION=#{migration_version}"
150
+ add_migrate_code_lens(node, name: class_name, command: command)
151
+ end
152
+
153
+ # We need to use a stack because someone could define a nested class
154
+ # inside a controller. When we exit that nested class declaration, we are
155
+ # back in a controller context. This part is used in other places in the LSP
156
+ @constant_name_stack << [class_name, superclass_name]
93
157
  end
94
158
 
95
159
  sig { params(node: Prism::ClassNode).void }
96
160
  def on_class_node_leave(node)
97
161
  class_name = node.constant_path.slice
162
+
98
163
  if class_name.end_with?("Test")
99
164
  @group_id_stack.pop
100
165
  end
166
+
167
+ @constant_name_stack.pop
168
+ end
169
+
170
+ sig { params(node: Prism::ModuleNode).void }
171
+ def on_module_node_enter(node)
172
+ @constant_name_stack << [node.constant_path.slice, nil]
173
+ end
174
+
175
+ sig { params(node: Prism::ModuleNode).void }
176
+ def on_module_node_leave(node)
177
+ @constant_name_stack.pop
101
178
  end
102
179
 
103
180
  private
104
181
 
182
+ sig { returns(T.nilable(T::Boolean)) }
183
+ def controller?
184
+ class_name, superclass_name = @constant_name_stack.last
185
+ return false unless class_name && superclass_name
186
+
187
+ class_name.end_with?("Controller") && superclass_name.end_with?("Controller")
188
+ end
189
+
190
+ sig { params(node: Prism::DefNode).void }
191
+ def add_jump_to_view(node)
192
+ class_name = @constant_name_stack.map(&:first).join("::")
193
+ action_name = node.name
194
+ controller_name = class_name
195
+ .delete_suffix("Controller")
196
+ .gsub(/([a-z])([A-Z])/, "\\1_\\2")
197
+ .gsub("::", "/")
198
+ .downcase
199
+
200
+ view_uris = Dir.glob("#{@client.rails_root}/app/views/#{controller_name}/#{action_name}*").map! do |path|
201
+ URI::Generic.from_path(path: path).to_s
202
+ end
203
+ return if view_uris.empty?
204
+
205
+ @response_builder << create_code_lens(
206
+ node,
207
+ title: "Jump to view",
208
+ command_name: "rubyLsp.openFile",
209
+ arguments: [view_uris],
210
+ data: { type: "file" },
211
+ )
212
+ end
213
+
214
+ sig { params(node: Prism::DefNode).void }
215
+ def add_route_code_lens_to_action(node)
216
+ class_name, _ = T.must(@constant_name_stack.last)
217
+ route = @client.route(controller: class_name, action: node.name.to_s)
218
+ return unless route
219
+
220
+ file_path, line = route[:source_location]
221
+
222
+ @response_builder << create_code_lens(
223
+ node,
224
+ title: "#{route[:verb]} #{route[:path]}",
225
+ command_name: "rubyLsp.openFile",
226
+ arguments: [["file://#{file_path}#L#{line}"]],
227
+ data: { type: "file" },
228
+ )
229
+ end
230
+
105
231
  sig { returns(String) }
106
232
  def test_command
107
- if Gem.win_platform?
108
- "ruby bin/rails test"
109
- else
110
- "bin/rails test"
111
- end
233
+ "#{RbConfig.ruby} bin/rails test"
234
+ end
235
+
236
+ sig { returns(String) }
237
+ def migrate_command
238
+ "#{RbConfig.ruby} bin/rails db:migrate"
239
+ end
240
+
241
+ sig { returns(T.nilable(String)) }
242
+ def migration_version
243
+ File.basename(T.must(@path)).split("_").first
244
+ end
245
+
246
+ sig { params(node: Prism::Node, name: String, command: String).void }
247
+ def add_migrate_code_lens(node, name:, command:)
248
+ return unless @path
249
+
250
+ @response_builder << create_code_lens(
251
+ node,
252
+ title: "Run",
253
+ command_name: "rubyLsp.runTask",
254
+ arguments: [command],
255
+ data: { type: "migrate" },
256
+ )
112
257
  end
113
258
 
114
259
  sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
115
260
  def add_test_code_lens(node, name:, command:, kind:)
116
261
  return unless @path
262
+ return unless @global_state.test_library == "rails"
117
263
 
118
264
  arguments = [
119
265
  @path,
@@ -17,7 +17,7 @@ module RubyLsp
17
17
  # # Example
18
18
  #
19
19
  # ```ruby
20
- # before_action :foo # <- Go to definition on this symbol will jump to the method if it is defined in the same class
20
+ # before_action :foo # <- Go to definition on this symbol will jump to the method
21
21
  # ```
22
22
  #
23
23
  # Notes for named routes:
@@ -34,7 +34,9 @@ module RubyLsp
34
34
  sig do
35
35
  params(
36
36
  client: RunnerClient,
37
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
37
+ response_builder: RubyLsp::ResponseBuilders::CollectionResponseBuilder[T.any(
38
+ Interface::Location, Interface::LocationLink
39
+ )],
38
40
  node_context: NodeContext,
39
41
  index: RubyIndexer::Index,
40
42
  dispatcher: Prism::Dispatcher,
@@ -0,0 +1,113 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Rails
6
+ class IndexingEnhancement
7
+ extend T::Sig
8
+ include RubyIndexer::Enhancement
9
+
10
+ sig do
11
+ override.params(
12
+ index: RubyIndexer::Index,
13
+ owner: T.nilable(RubyIndexer::Entry::Namespace),
14
+ node: Prism::CallNode,
15
+ file_path: String,
16
+ ).void
17
+ end
18
+ def on_call_node(index, owner, node, file_path)
19
+ return unless owner
20
+
21
+ name = node.name
22
+
23
+ case name
24
+ when :extend
25
+ handle_concern_extend(index, owner, node)
26
+ when :has_one, :has_many, :belongs_to, :has_and_belongs_to_many
27
+ handle_association(index, owner, node, file_path)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ sig do
34
+ params(
35
+ index: RubyIndexer::Index,
36
+ owner: RubyIndexer::Entry::Namespace,
37
+ node: Prism::CallNode,
38
+ file_path: String,
39
+ ).void
40
+ end
41
+ def handle_association(index, owner, node, file_path)
42
+ arguments = node.arguments&.arguments
43
+ return unless arguments
44
+
45
+ name_arg = arguments.first
46
+
47
+ name = case name_arg
48
+ when Prism::StringNode
49
+ name_arg.content
50
+ when Prism::SymbolNode
51
+ name_arg.value
52
+ end
53
+
54
+ return unless name
55
+
56
+ # Reader
57
+ index.add(RubyIndexer::Entry::Method.new(
58
+ name,
59
+ file_path,
60
+ name_arg.location,
61
+ name_arg.location,
62
+ [],
63
+ [RubyIndexer::Entry::Signature.new([])],
64
+ RubyIndexer::Entry::Visibility::PUBLIC,
65
+ owner,
66
+ ))
67
+
68
+ # Writer
69
+ index.add(RubyIndexer::Entry::Method.new(
70
+ "#{name}=",
71
+ file_path,
72
+ name_arg.location,
73
+ name_arg.location,
74
+ [],
75
+ [RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: name.to_sym)])],
76
+ RubyIndexer::Entry::Visibility::PUBLIC,
77
+ owner,
78
+ ))
79
+ end
80
+
81
+ sig do
82
+ params(
83
+ index: RubyIndexer::Index,
84
+ owner: RubyIndexer::Entry::Namespace,
85
+ node: Prism::CallNode,
86
+ ).void
87
+ end
88
+ def handle_concern_extend(index, owner, node)
89
+ arguments = node.arguments&.arguments
90
+ return unless arguments
91
+
92
+ arguments.each do |node|
93
+ next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
94
+
95
+ module_name = node.full_name
96
+ next unless module_name == "ActiveSupport::Concern"
97
+
98
+ index.register_included_hook(owner.name) do |index, base|
99
+ class_methods_name = "#{owner.name}::ClassMethods"
100
+
101
+ if index.indexed?(class_methods_name)
102
+ singleton = index.existing_or_new_singleton_class(base.name)
103
+ singleton.mixin_operations << RubyIndexer::Entry::Include.new(class_methods_name)
104
+ end
105
+ end
106
+ rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
107
+ Prism::ConstantPathNode::MissingNodesInConstantPathError
108
+ # Do nothing
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -36,8 +36,12 @@ module RubyLsp
36
36
 
37
37
  extend T::Sig
38
38
 
39
+ sig { returns(String) }
40
+ attr_reader :rails_root
41
+
39
42
  sig { void }
40
43
  def initialize
44
+ @mutex = T.let(Mutex.new, Mutex)
41
45
  # Spring needs a Process session ID. It uses this ID to "attach" itself to the parent process, so that when the
42
46
  # parent ends, the spring process ends as well. If this is not set, Spring will throw an error while trying to
43
47
  # set its own session ID
@@ -67,7 +71,8 @@ module RubyLsp
67
71
 
68
72
  begin
69
73
  count += 1
70
- read_response
74
+ initialize_response = T.must(read_response)
75
+ @rails_root = T.let(initialize_response[:root], String)
71
76
  rescue EmptyMessageError
72
77
  $stderr.puts("Ruby LSP Rails is retrying initialize (#{count})")
73
78
  retry if count < MAX_RETRIES
@@ -80,9 +85,7 @@ module RubyLsp
80
85
  if @wait_thread.alive?
81
86
  $stderr.puts("Ruby LSP Rails is force killing the server")
82
87
  sleep(0.5) # give the server a bit of time if we already issued a shutdown notification
83
-
84
- # Windows does not support the `TERM` signal, so we're forced to use `KILL` here
85
- Process.kill(T.must(Signal.list["KILL"]), @wait_thread.pid)
88
+ force_kill
86
89
  end
87
90
  end
88
91
  end
@@ -122,6 +125,14 @@ module RubyLsp
122
125
  nil
123
126
  end
124
127
 
128
+ sig { params(controller: String, action: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
129
+ def route(controller:, action:)
130
+ make_request("route_info", controller: controller, action: action)
131
+ rescue IncompleteMessageError
132
+ $stderr.puts("Ruby LSP Rails failed to get route information: #{@stderr.read}")
133
+ nil
134
+ end
135
+
125
136
  sig { void }
126
137
  def trigger_reload
127
138
  $stderr.puts("Reloading Rails application")
@@ -137,6 +148,9 @@ module RubyLsp
137
148
  send_message("shutdown")
138
149
  sleep(0.5) # give the server a bit of time to shutdown
139
150
  [@stdin, @stdout, @stderr].each(&:close)
151
+ rescue IOError
152
+ # The server connection may have died
153
+ force_kill
140
154
  end
141
155
 
142
156
  sig { returns(T::Boolean) }
@@ -157,26 +171,35 @@ module RubyLsp
157
171
  read_response
158
172
  end
159
173
 
160
- sig { params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
174
+ sig { overridable.params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
161
175
  def send_message(request, params = nil)
162
176
  message = { method: request }
163
177
  message[:params] = params if params
164
178
  json = message.to_json
165
179
 
166
- @stdin.write("Content-Length: #{json.length}\r\n\r\n", json)
180
+ @mutex.synchronize do
181
+ @stdin.write("Content-Length: #{json.length}\r\n\r\n", json)
182
+ end
183
+ rescue Errno::EPIPE
184
+ # The server connection died
167
185
  end
168
186
 
169
- alias_method :send_notification, :send_message
187
+ # Notifications are like messages, but one-way, with no response sent back.
188
+ sig { params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
189
+ def send_notification(request, params = nil) = send_message(request, params)
170
190
 
171
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
191
+ sig { overridable.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
172
192
  def read_response
173
- headers = @stdout.gets("\r\n\r\n")
174
- raise IncompleteMessageError unless headers
193
+ raw_response = @mutex.synchronize do
194
+ headers = @stdout.gets("\r\n\r\n")
195
+ raise IncompleteMessageError unless headers
175
196
 
176
- content_length = headers[/Content-Length: (\d+)/i, 1].to_i
177
- raise EmptyMessageError if content_length.zero?
197
+ content_length = headers[/Content-Length: (\d+)/i, 1].to_i
198
+ raise EmptyMessageError if content_length.zero?
199
+
200
+ @stdout.read(content_length)
201
+ end
178
202
 
179
- raw_response = @stdout.read(content_length)
180
203
  response = JSON.parse(T.must(raw_response), symbolize_names: true)
181
204
 
182
205
  if response[:error]
@@ -185,6 +208,15 @@ module RubyLsp
185
208
  end
186
209
 
187
210
  response.fetch(:result)
211
+ rescue Errno::EPIPE
212
+ # The server connection died
213
+ nil
214
+ end
215
+
216
+ sig { void }
217
+ def force_kill
218
+ # Windows does not support the `TERM` signal, so we're forced to use `KILL` here
219
+ Process.kill(T.must(Signal.list["KILL"]), @wait_thread.pid)
188
220
  end
189
221
  end
190
222
 
@@ -205,6 +237,11 @@ module RubyLsp
205
237
  true
206
238
  end
207
239
 
240
+ sig { override.returns(String) }
241
+ def rails_root
242
+ Dir.pwd
243
+ end
244
+
208
245
  private
209
246
 
210
247
  sig { override.params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
@@ -24,7 +24,7 @@ module RubyLsp
24
24
  routes_reloader = ::Rails.application.routes_reloader
25
25
  routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
26
26
 
27
- initialize_result = { result: { message: "ok" } }.to_json
27
+ initialize_result = { result: { message: "ok", root: ::Rails.root.to_s } }.to_json
28
28
  $stdout.write("Content-Length: #{initialize_result.length}\r\n\r\n#{initialize_result}")
29
29
 
30
30
  while @running
@@ -54,6 +54,8 @@ module RubyLsp
54
54
  VOID
55
55
  when "route_location"
56
56
  route_location(params.fetch(:name))
57
+ when "route_info"
58
+ resolve_route_info(params)
57
59
  else
58
60
  VOID
59
61
  end
@@ -63,6 +65,32 @@ module RubyLsp
63
65
 
64
66
  private
65
67
 
68
+ def resolve_route_info(requirements)
69
+ if requirements[:controller]
70
+ requirements[:controller] = requirements.fetch(:controller).underscore.delete_suffix("_controller")
71
+ end
72
+
73
+ # In Rails 7.2 we can use `from_requirements, otherwise we fall back to a private API
74
+ route = if ::Rails.application.routes.respond_to?(:from_requirements)
75
+ ::Rails.application.routes.from_requirements(requirements)
76
+ else
77
+ ::Rails.application.routes.routes.find { |route| route.requirements == requirements }
78
+ end
79
+
80
+ if route&.source_location
81
+ file, _, line = route.source_location.rpartition(":")
82
+ body = {
83
+ source_location: [::Rails.root.join(file).to_s, line],
84
+ verb: route.verb,
85
+ path: route.path.spec.to_s,
86
+ }
87
+
88
+ { result: body }
89
+ else
90
+ { result: nil }
91
+ end
92
+ end
93
+
66
94
  # Older versions of Rails don't support `route_source_locations`.
67
95
  # We also check that it's enabled.
68
96
  if ActionDispatch::Routing::Mapper.respond_to?(:route_source_locations) &&
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Rails
6
- VERSION = "0.3.8"
6
+ VERSION = "0.3.13"
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.3.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-02 00:00:00.000000000 Z
11
+ date: 2024-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-lsp
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.17.2
19
+ version: 0.17.12
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: 0.18.0
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.17.2
29
+ version: 0.17.12
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: 0.18.0
@@ -46,6 +46,7 @@ files:
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
49
+ - lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb
49
50
  - lib/ruby_lsp/ruby_lsp_rails/runner_client.rb
50
51
  - lib/ruby_lsp/ruby_lsp_rails/server.rb
51
52
  - lib/ruby_lsp/ruby_lsp_rails/support/active_support_test_case_helper.rb
@@ -79,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
80
  - !ruby/object:Gem::Version
80
81
  version: '0'
81
82
  requirements: []
82
- rubygems_version: 3.5.14
83
+ rubygems_version: 3.5.17
83
84
  signing_key:
84
85
  specification_version: 4
85
86
  summary: A Ruby LSP addon for Rails