ruby-lsp-rails 0.3.8 → 0.3.10
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 +4 -4
- data/README.md +2 -0
- data/lib/ruby_lsp/ruby_lsp_rails/addon.rb +1 -1
- data/lib/ruby_lsp/ruby_lsp_rails/code_lens.rb +123 -7
- data/lib/ruby_lsp/ruby_lsp_rails/runner_client.rb +13 -0
- data/lib/ruby_lsp/ruby_lsp_rails/server.rb +28 -0
- data/lib/ruby_lsp_rails/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f90b7d238d437e9ad3c29cad137e14e3094340243802f3d0f5cb7ddf68e582e1
|
4
|
+
data.tar.gz: cb64d09552cf7a0862ceeb8fb27c0c2b40ba596a267bec6aebdaf3ab2db139a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8721f1e2bb8a2fc0acff6a3caab8b216c0225b9fdd69eb883944b10d0159619d35c501b7d3761b9fb086e0662e082dedc28c759cca3481d65ea97b3dae157e23
|
7
|
+
data.tar.gz: 7c329b8da4692954f7382069f8f046672675192536c79ee3c58f0239ee87b82c5aa7083b1e786dc3827a713b9e08232d7c8e92d8513bb2f89a37e97957116070
|
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
|
|
@@ -53,7 +53,7 @@ module RubyLsp
|
|
53
53
|
def create_code_lens_listener(response_builder, uri, dispatcher)
|
54
54
|
return unless T.must(@global_state).test_library == "rails"
|
55
55
|
|
56
|
-
CodeLens.new(response_builder, uri, dispatcher)
|
56
|
+
CodeLens.new(@client, response_builder, uri, dispatcher)
|
57
57
|
end
|
58
58
|
|
59
59
|
sig do
|
@@ -5,11 +5,22 @@ module RubyLsp
|
|
5
5
|
module Rails
|
6
6
|
# 
|
7
7
|
#
|
8
|
-
# This feature adds
|
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
|
46
|
+
# ```
|
47
|
+
#
|
48
|
+
# # Example:
|
49
|
+
# ```ruby
|
50
|
+
# Run
|
51
|
+
# class AddFirstNameToUsers < ActiveRecord::Migration[7.1]
|
52
|
+
# # ...
|
53
|
+
# end
|
35
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,16 +78,19 @@ module RubyLsp
|
|
45
78
|
|
46
79
|
sig do
|
47
80
|
params(
|
81
|
+
client: RunnerClient,
|
48
82
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
|
49
83
|
uri: URI::Generic,
|
50
84
|
dispatcher: Prism::Dispatcher,
|
51
85
|
).void
|
52
86
|
end
|
53
|
-
def initialize(response_builder, uri, dispatcher)
|
87
|
+
def initialize(client, response_builder, uri, dispatcher)
|
88
|
+
@client = client
|
54
89
|
@response_builder = response_builder
|
55
90
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
56
91
|
@group_id = T.let(1, Integer)
|
57
92
|
@group_id_stack = T.let([], T::Array[Integer])
|
93
|
+
@constant_name_stack = T.let([], T::Array[[String, T.nilable(String)]])
|
58
94
|
|
59
95
|
dispatcher.register(self, :on_call_node_enter, :on_class_node_enter, :on_def_node_enter, :on_class_node_leave)
|
60
96
|
end
|
@@ -74,41 +110,121 @@ module RubyLsp
|
|
74
110
|
sig { params(node: Prism::DefNode).void }
|
75
111
|
def on_def_node_enter(node)
|
76
112
|
method_name = node.name.to_s
|
113
|
+
|
77
114
|
if method_name.start_with?("test_")
|
78
115
|
line_number = node.location.start_line
|
79
116
|
command = "#{test_command} #{@path}:#{line_number}"
|
80
117
|
add_test_code_lens(node, name: method_name, command: command, kind: :example)
|
81
118
|
end
|
119
|
+
|
120
|
+
if controller?
|
121
|
+
add_route_code_lens_to_action(node)
|
122
|
+
end
|
82
123
|
end
|
83
124
|
|
84
125
|
sig { params(node: Prism::ClassNode).void }
|
85
126
|
def on_class_node_enter(node)
|
86
127
|
class_name = node.constant_path.slice
|
128
|
+
superclass_name = node.superclass&.slice
|
129
|
+
|
87
130
|
if class_name.end_with?("Test")
|
88
131
|
command = "#{test_command} #{@path}"
|
89
132
|
add_test_code_lens(node, name: class_name, command: command, kind: :group)
|
90
133
|
@group_id_stack.push(@group_id)
|
91
134
|
@group_id += 1
|
92
135
|
end
|
136
|
+
|
137
|
+
if superclass_name&.start_with?("ActiveRecord::Migration")
|
138
|
+
command = "#{migrate_command} VERSION=#{migration_version}"
|
139
|
+
add_migrate_code_lens(node, name: class_name, command: command)
|
140
|
+
end
|
141
|
+
|
142
|
+
# We need to use a stack because someone could define a nested class
|
143
|
+
# inside a controller. When we exit that nested class declaration, we are
|
144
|
+
# back in a controller context. This part is used in other places in the LSP
|
145
|
+
@constant_name_stack << [class_name, superclass_name]
|
93
146
|
end
|
94
147
|
|
95
148
|
sig { params(node: Prism::ClassNode).void }
|
96
149
|
def on_class_node_leave(node)
|
97
150
|
class_name = node.constant_path.slice
|
151
|
+
|
98
152
|
if class_name.end_with?("Test")
|
99
153
|
@group_id_stack.pop
|
100
154
|
end
|
155
|
+
|
156
|
+
@constant_name_stack.pop
|
101
157
|
end
|
102
158
|
|
103
159
|
private
|
104
160
|
|
161
|
+
sig { returns(T.nilable(T::Boolean)) }
|
162
|
+
def controller?
|
163
|
+
class_name, superclass_name = @constant_name_stack.last
|
164
|
+
return false unless class_name && superclass_name
|
165
|
+
|
166
|
+
class_name.end_with?("Controller") && superclass_name.end_with?("Controller")
|
167
|
+
end
|
168
|
+
|
169
|
+
sig { params(node: Prism::DefNode).void }
|
170
|
+
def add_route_code_lens_to_action(node)
|
171
|
+
class_name, _ = T.must(@constant_name_stack.last)
|
172
|
+
route = @client.route(
|
173
|
+
controller: class_name,
|
174
|
+
action: node.name.to_s,
|
175
|
+
)
|
176
|
+
|
177
|
+
return unless route
|
178
|
+
|
179
|
+
path = route[:path]
|
180
|
+
verb = route[:verb]
|
181
|
+
source_location = route[:source_location]
|
182
|
+
|
183
|
+
arguments = [
|
184
|
+
source_location,
|
185
|
+
{
|
186
|
+
start_line: node.location.start_line - 1,
|
187
|
+
start_column: node.location.start_column,
|
188
|
+
end_line: node.location.end_line - 1,
|
189
|
+
end_column: node.location.end_column,
|
190
|
+
},
|
191
|
+
]
|
192
|
+
|
193
|
+
@response_builder << create_code_lens(
|
194
|
+
node,
|
195
|
+
title: [verb, path].join(" "),
|
196
|
+
command_name: "rubyLsp.openFile",
|
197
|
+
arguments: arguments,
|
198
|
+
data: { type: "file" },
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
105
202
|
sig { returns(String) }
|
106
203
|
def test_command
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
204
|
+
"#{RbConfig.ruby} bin/rails test"
|
205
|
+
end
|
206
|
+
|
207
|
+
sig { returns(String) }
|
208
|
+
def migrate_command
|
209
|
+
"#{RbConfig.ruby} bin/rails db:migrate"
|
210
|
+
end
|
211
|
+
|
212
|
+
sig { returns(T.nilable(String)) }
|
213
|
+
def migration_version
|
214
|
+
File.basename(T.must(@path)).split("_").first
|
215
|
+
end
|
216
|
+
|
217
|
+
sig { params(node: Prism::Node, name: String, command: String).void }
|
218
|
+
def add_migrate_code_lens(node, name:, command:)
|
219
|
+
return unless @path
|
220
|
+
|
221
|
+
@response_builder << create_code_lens(
|
222
|
+
node,
|
223
|
+
title: "Run",
|
224
|
+
command_name: "rubyLsp.runTask",
|
225
|
+
arguments: [command],
|
226
|
+
data: { type: "migrate" },
|
227
|
+
)
|
112
228
|
end
|
113
229
|
|
114
230
|
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
|
@@ -122,6 +122,14 @@ module RubyLsp
|
|
122
122
|
nil
|
123
123
|
end
|
124
124
|
|
125
|
+
sig { params(controller: String, action: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
126
|
+
def route(controller:, action:)
|
127
|
+
make_request("route_info", controller: controller, action: action)
|
128
|
+
rescue IncompleteMessageError
|
129
|
+
$stderr.puts("Ruby LSP Rails failed to get route information: #{@stderr.read}")
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
125
133
|
sig { void }
|
126
134
|
def trigger_reload
|
127
135
|
$stderr.puts("Reloading Rails application")
|
@@ -164,6 +172,8 @@ module RubyLsp
|
|
164
172
|
json = message.to_json
|
165
173
|
|
166
174
|
@stdin.write("Content-Length: #{json.length}\r\n\r\n", json)
|
175
|
+
rescue Errno::EPIPE
|
176
|
+
# The server connection died
|
167
177
|
end
|
168
178
|
|
169
179
|
alias_method :send_notification, :send_message
|
@@ -185,6 +195,9 @@ module RubyLsp
|
|
185
195
|
end
|
186
196
|
|
187
197
|
response.fetch(:result)
|
198
|
+
rescue Errno::EPIPE
|
199
|
+
# The server connection died
|
200
|
+
nil
|
188
201
|
end
|
189
202
|
end
|
190
203
|
|
@@ -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) &&
|
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.
|
4
|
+
version: 0.3.10
|
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-
|
11
|
+
date: 2024-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-lsp
|