ruby-lsp-rails 0.3.25 → 0.3.27
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2df24f43b3e6666c310cb91e43d3b4311eea8326e27aa7bb4278c9fd03de42d1
|
4
|
+
data.tar.gz: 116f736d81055adf4a287d3d76a4f9e641cf2ac81f2a39b63ccb9603bfbd351e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c462629b903ebbee663323f52385423fb697bd338bcf7c57574874de21f323230d381c5d6e5eb30602248f33021bc2d0082602fa3b5c0b65d674f9c9c1668ff5
|
7
|
+
data.tar.gz: e909ea43dc3a8012f1de165e1aa4cbf4ad0873134199273103e329fec5ac141792113adb5eb1eb788f065f63e3c2fba299c0501a837719a816b2698e119e50bf
|
@@ -56,7 +56,6 @@ module RubyLsp
|
|
56
56
|
@outgoing_queue << Notification.window_log_message("Activating Ruby LSP Rails add-on v#{VERSION}")
|
57
57
|
|
58
58
|
register_additional_file_watchers(global_state: global_state, outgoing_queue: outgoing_queue)
|
59
|
-
@global_state.index.register_enhancement(IndexingEnhancement.new(@global_state.index))
|
60
59
|
|
61
60
|
# Start booting the real client in a background thread. Until this completes, the client will be a NullClient
|
62
61
|
@client_mutex.unlock
|
@@ -8,25 +8,32 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
sig do
|
10
10
|
override.params(
|
11
|
-
|
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(
|
14
|
+
def on_call_node_enter(call_node)
|
15
|
+
owner = @listener.current_owner
|
21
16
|
return unless owner
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
case name
|
18
|
+
case call_node.name
|
26
19
|
when :extend
|
27
|
-
handle_concern_extend(owner,
|
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,
|
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
|
-
|
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,
|
47
|
-
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 =
|
63
|
+
loc = name_arg.location
|
62
64
|
|
63
65
|
# Reader
|
64
|
-
|
65
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
89
|
-
|
90
|
-
|
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
|
-
@
|
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
|
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
|
111
|
+
rescue StandardError
|
113
112
|
raise InitializationError, @stderr.read
|
114
113
|
end
|
115
114
|
|
@@ -20,6 +20,43 @@ module RubyLsp
|
|
20
20
|
def log_message(message)
|
21
21
|
$stderr.puts(message)
|
22
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)}")
|
59
|
+
end
|
23
60
|
end
|
24
61
|
|
25
62
|
class ServerAddon
|
@@ -87,11 +124,8 @@ module RubyLsp
|
|
87
124
|
end
|
88
125
|
|
89
126
|
def start
|
90
|
-
|
91
|
-
|
92
|
-
routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
|
93
|
-
|
94
|
-
send_message({ result: { message: "ok", root: ::Rails.root.to_s } })
|
127
|
+
load_routes
|
128
|
+
send_result({ message: "ok", root: ::Rails.root.to_s })
|
95
129
|
|
96
130
|
while @running
|
97
131
|
headers = @stdin.gets("\r\n\r\n")
|
@@ -107,34 +141,46 @@ module RubyLsp
|
|
107
141
|
when "shutdown"
|
108
142
|
@running = false
|
109
143
|
when "model"
|
110
|
-
|
144
|
+
with_request_error_handling(request) do
|
145
|
+
send_result(resolve_database_info_from_model(params.fetch(:name)))
|
146
|
+
end
|
111
147
|
when "association_target_location"
|
112
|
-
|
148
|
+
with_request_error_handling(request) do
|
149
|
+
send_result(resolve_association_target(params))
|
150
|
+
end
|
113
151
|
when "pending_migrations_message"
|
114
|
-
|
152
|
+
with_request_error_handling(request) do
|
153
|
+
send_result({ pending_migrations_message: pending_migrations_message })
|
154
|
+
end
|
115
155
|
when "run_migrations"
|
116
|
-
|
156
|
+
with_request_error_handling(request) do
|
157
|
+
send_result(run_migrations)
|
158
|
+
end
|
117
159
|
when "reload"
|
118
|
-
|
160
|
+
with_notification_error_handling(request) do
|
161
|
+
::Rails.application.reloader.reload!
|
162
|
+
end
|
119
163
|
when "route_location"
|
120
|
-
|
164
|
+
with_request_error_handling(request) do
|
165
|
+
send_result(route_location(params.fetch(:name)))
|
166
|
+
end
|
121
167
|
when "route_info"
|
122
|
-
|
168
|
+
with_request_error_handling(request) do
|
169
|
+
send_result(resolve_route_info(params))
|
170
|
+
end
|
123
171
|
when "server_addon/register"
|
124
|
-
|
125
|
-
|
172
|
+
with_notification_error_handling(request) do
|
173
|
+
require params[:server_addon_path]
|
174
|
+
ServerAddon.finalize_registrations!(@stdout)
|
175
|
+
end
|
126
176
|
when "server_addon/delegate"
|
127
177
|
server_addon_name = params[:server_addon_name]
|
128
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.
|
129
182
|
ServerAddon.delegate(server_addon_name, request_name, params.except(:request_name, :server_addon_name))
|
130
183
|
end
|
131
|
-
request_name = request
|
132
|
-
request_name = "#{params[:server_addon_name]}##{params[:request_name]}" if request == "server_addon/delegate"
|
133
|
-
# Since this is a common problem, we show a specific error message to the user, instead of the full stack trace.
|
134
|
-
rescue ActiveRecord::ConnectionNotEstablished
|
135
|
-
log_message("Request #{request_name} failed because database connection was not established.")
|
136
|
-
rescue => e
|
137
|
-
log_message("Request #{request_name} failed:\n" + e.full_message(highlight: false))
|
138
184
|
end
|
139
185
|
|
140
186
|
private
|
@@ -152,19 +198,15 @@ module RubyLsp
|
|
152
198
|
end
|
153
199
|
|
154
200
|
source_location = route&.respond_to?(:source_location) && route.source_location
|
201
|
+
return unless source_location
|
155
202
|
|
156
|
-
|
157
|
-
file, _, line = source_location.rpartition(":")
|
158
|
-
body = {
|
159
|
-
source_location: [::Rails.root.join(file).to_s, line],
|
160
|
-
verb: route.verb,
|
161
|
-
path: route.path.spec.to_s,
|
162
|
-
}
|
203
|
+
file, _, line = source_location.rpartition(":")
|
163
204
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
205
|
+
{
|
206
|
+
source_location: [::Rails.root.join(file).to_s, line],
|
207
|
+
verb: route.verb,
|
208
|
+
path: route.path.spec.to_s,
|
209
|
+
}
|
168
210
|
end
|
169
211
|
|
170
212
|
# Older versions of Rails don't support `route_source_locations`.
|
@@ -178,74 +220,48 @@ module RubyLsp
|
|
178
220
|
end
|
179
221
|
|
180
222
|
match_data = name.match(/^(.+)(_path|_url)$/)
|
181
|
-
return
|
223
|
+
return unless match_data
|
182
224
|
|
183
225
|
key = match_data[1]
|
184
226
|
|
185
227
|
# A token could match the _path or _url pattern, but not be an actual route.
|
186
228
|
route = ::Rails.application.routes.named_routes.get(key)
|
187
|
-
return
|
188
|
-
|
189
|
-
{
|
190
|
-
result: {
|
191
|
-
location: ::Rails.root.join(route.source_location).to_s,
|
192
|
-
},
|
193
|
-
}
|
194
|
-
rescue => e
|
195
|
-
{ error: e.full_message(highlight: false) }
|
229
|
+
return unless route&.source_location
|
230
|
+
|
231
|
+
{ location: ::Rails.root.join(route.source_location).to_s }
|
196
232
|
end
|
197
233
|
else
|
198
234
|
def route_location(name)
|
199
|
-
|
235
|
+
nil
|
200
236
|
end
|
201
237
|
end
|
202
238
|
|
203
239
|
def resolve_database_info_from_model(model_name)
|
204
240
|
const = ActiveSupport::Inflector.safe_constantize(model_name)
|
205
|
-
unless active_record_model?(const)
|
206
|
-
return {
|
207
|
-
result: nil,
|
208
|
-
}
|
209
|
-
end
|
241
|
+
return unless active_record_model?(const)
|
210
242
|
|
211
243
|
info = {
|
212
|
-
|
213
|
-
|
214
|
-
primary_keys: Array(const.primary_key),
|
215
|
-
},
|
244
|
+
columns: const.columns.map { |column| [column.name, column.type, column.default, column.null] },
|
245
|
+
primary_keys: Array(const.primary_key),
|
216
246
|
}
|
217
247
|
|
218
248
|
if ActiveRecord::Tasks::DatabaseTasks.respond_to?(:schema_dump_path)
|
219
|
-
info[:
|
220
|
-
ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
|
221
|
-
|
249
|
+
info[:schema_file] = ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
|
222
250
|
end
|
251
|
+
|
223
252
|
info
|
224
|
-
rescue => e
|
225
|
-
{ error: e.full_message(highlight: false) }
|
226
253
|
end
|
227
254
|
|
228
255
|
def resolve_association_target(params)
|
229
256
|
const = ActiveSupport::Inflector.safe_constantize(params[:model_name])
|
230
|
-
unless active_record_model?(const)
|
231
|
-
return {
|
232
|
-
result: nil,
|
233
|
-
}
|
234
|
-
end
|
257
|
+
return unless active_record_model?(const)
|
235
258
|
|
236
259
|
association_klass = const.reflect_on_association(params[:association_name].intern).klass
|
237
|
-
|
238
260
|
source_location = Object.const_source_location(association_klass.to_s)
|
239
261
|
|
240
|
-
{
|
241
|
-
result: {
|
242
|
-
location: source_location.first + ":" + source_location.second.to_s,
|
243
|
-
},
|
244
|
-
}
|
262
|
+
{ location: source_location.first + ":" + source_location.second.to_s }
|
245
263
|
rescue NameError
|
246
|
-
|
247
|
-
result: nil,
|
248
|
-
}
|
264
|
+
nil
|
249
265
|
end
|
250
266
|
|
251
267
|
def active_record_model?(const)
|
@@ -278,6 +294,14 @@ module RubyLsp
|
|
278
294
|
|
279
295
|
{ message: stdout, status: status.exitstatus }
|
280
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
|
281
305
|
end
|
282
306
|
end
|
283
307
|
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.
|
4
|
+
version: 0.3.27
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-lsp
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.22.0
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.
|
22
|
+
version: 0.23.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.
|
29
|
+
version: 0.22.0
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.
|
32
|
+
version: 0.23.0
|
33
33
|
description: A Ruby LSP addon that adds extra editor functionality for Rails applications
|
34
34
|
email:
|
35
35
|
- ruby@shopify.com
|