ruby-lsp-rails 0.3.31 → 0.4.8
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/Rakefile +1 -1
- data/lib/ruby_lsp/ruby_lsp_rails/addon.rb +74 -66
- data/lib/ruby_lsp/ruby_lsp_rails/code_lens.rb +39 -38
- data/lib/ruby_lsp/ruby_lsp_rails/completion.rb +7 -15
- data/lib/ruby_lsp/ruby_lsp_rails/definition.rb +14 -23
- data/lib/ruby_lsp/ruby_lsp_rails/document_symbol.rb +24 -30
- data/lib/ruby_lsp/ruby_lsp_rails/hover.rb +106 -32
- data/lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb +7 -20
- data/lib/ruby_lsp/ruby_lsp_rails/rails_test_style.rb +171 -0
- data/lib/ruby_lsp/ruby_lsp_rails/runner_client.rb +109 -78
- data/lib/ruby_lsp/ruby_lsp_rails/server.rb +254 -39
- data/lib/ruby_lsp/ruby_lsp_rails/support/active_support_test_case_helper.rb +3 -4
- data/lib/ruby_lsp/ruby_lsp_rails/support/associations.rb +6 -9
- data/lib/ruby_lsp/ruby_lsp_rails/support/callbacks.rb +54 -57
- data/lib/ruby_lsp/ruby_lsp_rails/support/location_builder.rb +1 -3
- data/lib/ruby_lsp_rails/version.rb +1 -1
- metadata +8 -7
|
@@ -8,12 +8,10 @@ module RubyLsp
|
|
|
8
8
|
module Rails
|
|
9
9
|
class RunnerClient
|
|
10
10
|
class << self
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
sig { params(outgoing_queue: Thread::Queue).returns(RunnerClient) }
|
|
14
|
-
def create_client(outgoing_queue)
|
|
11
|
+
#: (Thread::Queue outgoing_queue, RubyLsp::GlobalState global_state) -> RunnerClient
|
|
12
|
+
def create_client(outgoing_queue, global_state)
|
|
15
13
|
if File.exist?("bin/rails")
|
|
16
|
-
new(outgoing_queue)
|
|
14
|
+
new(outgoing_queue, global_state)
|
|
17
15
|
else
|
|
18
16
|
unless outgoing_queue.closed?
|
|
19
17
|
outgoing_queue << RubyLsp::Notification.window_log_message(
|
|
@@ -46,15 +44,13 @@ module RubyLsp
|
|
|
46
44
|
class MessageError < StandardError; end
|
|
47
45
|
class EmptyMessageError < MessageError; end
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
sig { returns(String) }
|
|
47
|
+
#: String
|
|
52
48
|
attr_reader :rails_root
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
def initialize(outgoing_queue)
|
|
56
|
-
@outgoing_queue =
|
|
57
|
-
@mutex =
|
|
50
|
+
#: (Thread::Queue outgoing_queue, RubyLsp::GlobalState global_state) -> void
|
|
51
|
+
def initialize(outgoing_queue, global_state)
|
|
52
|
+
@outgoing_queue = outgoing_queue #: Thread::Queue
|
|
53
|
+
@mutex = Mutex.new #: Mutex
|
|
58
54
|
# Spring needs a Process session ID. It uses this ID to "attach" itself to the parent process, so that when the
|
|
59
55
|
# parent ends, the spring process ends as well. If this is not set, Spring will throw an error while trying to
|
|
60
56
|
# set its own session ID
|
|
@@ -71,24 +67,32 @@ module RubyLsp
|
|
|
71
67
|
log_message("Ruby LSP Rails booting server")
|
|
72
68
|
|
|
73
69
|
stdin, stdout, stderr, wait_thread = Bundler.with_original_env do
|
|
74
|
-
Open3.popen3(
|
|
70
|
+
Open3.popen3(
|
|
71
|
+
"bundle",
|
|
72
|
+
"exec",
|
|
73
|
+
"rails",
|
|
74
|
+
"runner",
|
|
75
|
+
"#{__dir__}/server.rb",
|
|
76
|
+
"start",
|
|
77
|
+
server_relevant_capabilities(global_state),
|
|
78
|
+
)
|
|
75
79
|
end
|
|
76
80
|
|
|
77
|
-
@stdin =
|
|
78
|
-
@stdout =
|
|
79
|
-
@stderr =
|
|
81
|
+
@stdin = stdin #: IO
|
|
82
|
+
@stdout = stdout #: IO
|
|
83
|
+
@stderr = stderr #: IO
|
|
80
84
|
@stdin.sync = true
|
|
81
85
|
@stdout.sync = true
|
|
82
86
|
@stderr.sync = true
|
|
83
|
-
@wait_thread =
|
|
87
|
+
@wait_thread = wait_thread #: Process::Waiter
|
|
84
88
|
|
|
85
89
|
# We set binmode for Windows compatibility
|
|
86
90
|
@stdin.binmode
|
|
87
91
|
@stdout.binmode
|
|
88
92
|
@stderr.binmode
|
|
89
93
|
|
|
90
|
-
initialize_response =
|
|
91
|
-
@rails_root =
|
|
94
|
+
initialize_response = read_response #: as !nil
|
|
95
|
+
@rails_root = initialize_response[:root] #: String
|
|
92
96
|
log_message("Finished booting Ruby LSP Rails server")
|
|
93
97
|
|
|
94
98
|
unless ENV["RAILS_ENV"] == "test"
|
|
@@ -100,21 +104,24 @@ module RubyLsp
|
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
# Responsible for transmitting notifications coming from the server to the outgoing queue, so that we can do
|
|
108
|
+
# things such as showing progress notifications initiated by the server
|
|
109
|
+
@notifier_thread = Thread.new do
|
|
110
|
+
until @stderr.closed?
|
|
111
|
+
notification = read_notification
|
|
112
|
+
|
|
113
|
+
unless @outgoing_queue.closed? || !notification
|
|
114
|
+
@outgoing_queue << notification
|
|
107
115
|
end
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
116
|
+
end
|
|
117
|
+
rescue IOError
|
|
118
|
+
# The server was shutdown and stderr is already closed
|
|
119
|
+
end #: Thread
|
|
113
120
|
rescue StandardError
|
|
114
121
|
raise InitializationError, @stderr.read
|
|
115
122
|
end
|
|
116
123
|
|
|
117
|
-
|
|
124
|
+
#: (String server_addon_path) -> void
|
|
118
125
|
def register_server_addon(server_addon_path)
|
|
119
126
|
send_notification("server_addon/register", server_addon_path: server_addon_path)
|
|
120
127
|
rescue MessageError
|
|
@@ -125,7 +132,7 @@ module RubyLsp
|
|
|
125
132
|
nil
|
|
126
133
|
end
|
|
127
134
|
|
|
128
|
-
|
|
135
|
+
#: (String name) -> Hash[Symbol, untyped]?
|
|
129
136
|
def model(name)
|
|
130
137
|
make_request("model", name: name)
|
|
131
138
|
rescue MessageError
|
|
@@ -136,15 +143,10 @@ module RubyLsp
|
|
|
136
143
|
nil
|
|
137
144
|
end
|
|
138
145
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
model_name: String,
|
|
142
|
-
association_name: String,
|
|
143
|
-
).returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
|
144
|
-
end
|
|
145
|
-
def association_target_location(model_name:, association_name:)
|
|
146
|
+
#: (model_name: String, association_name: String) -> Hash[Symbol, untyped]?
|
|
147
|
+
def association_target(model_name:, association_name:)
|
|
146
148
|
make_request(
|
|
147
|
-
"
|
|
149
|
+
"association_target",
|
|
148
150
|
model_name: model_name,
|
|
149
151
|
association_name: association_name,
|
|
150
152
|
)
|
|
@@ -156,7 +158,7 @@ module RubyLsp
|
|
|
156
158
|
nil
|
|
157
159
|
end
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
#: (String name) -> Hash[Symbol, untyped]?
|
|
160
162
|
def route_location(name)
|
|
161
163
|
make_request("route_location", name: name)
|
|
162
164
|
rescue MessageError
|
|
@@ -167,7 +169,7 @@ module RubyLsp
|
|
|
167
169
|
nil
|
|
168
170
|
end
|
|
169
171
|
|
|
170
|
-
|
|
172
|
+
#: (controller: String, action: String) -> Hash[Symbol, untyped]?
|
|
171
173
|
def route(controller:, action:)
|
|
172
174
|
make_request("route_info", controller: controller, action: action)
|
|
173
175
|
rescue MessageError
|
|
@@ -179,7 +181,7 @@ module RubyLsp
|
|
|
179
181
|
end
|
|
180
182
|
|
|
181
183
|
# Delegates a notification to a server add-on
|
|
182
|
-
|
|
184
|
+
#: (server_addon_name: String, request_name: String, **untyped params) -> void
|
|
183
185
|
def delegate_notification(server_addon_name:, request_name:, **params)
|
|
184
186
|
send_notification(
|
|
185
187
|
"server_addon/delegate",
|
|
@@ -189,7 +191,7 @@ module RubyLsp
|
|
|
189
191
|
)
|
|
190
192
|
end
|
|
191
193
|
|
|
192
|
-
|
|
194
|
+
#: -> String?
|
|
193
195
|
def pending_migrations_message
|
|
194
196
|
response = make_request("pending_migrations_message")
|
|
195
197
|
response[:pending_migrations_message] if response
|
|
@@ -201,7 +203,7 @@ module RubyLsp
|
|
|
201
203
|
nil
|
|
202
204
|
end
|
|
203
205
|
|
|
204
|
-
|
|
206
|
+
#: -> Hash[Symbol, untyped]?
|
|
205
207
|
def run_migrations
|
|
206
208
|
make_request("run_migrations")
|
|
207
209
|
rescue MessageError
|
|
@@ -213,13 +215,7 @@ module RubyLsp
|
|
|
213
215
|
end
|
|
214
216
|
|
|
215
217
|
# Delegates a request to a server add-on
|
|
216
|
-
|
|
217
|
-
params(
|
|
218
|
-
server_addon_name: String,
|
|
219
|
-
request_name: String,
|
|
220
|
-
params: T.untyped,
|
|
221
|
-
).returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
|
222
|
-
end
|
|
218
|
+
#: (server_addon_name: String, request_name: String, **untyped params) -> Hash[Symbol, untyped]?
|
|
223
219
|
def delegate_request(server_addon_name:, request_name:, **params)
|
|
224
220
|
make_request(
|
|
225
221
|
"server_addon/delegate",
|
|
@@ -231,7 +227,7 @@ module RubyLsp
|
|
|
231
227
|
nil
|
|
232
228
|
end
|
|
233
229
|
|
|
234
|
-
|
|
230
|
+
#: -> void
|
|
235
231
|
def trigger_reload
|
|
236
232
|
log_message("Reloading Rails application")
|
|
237
233
|
send_notification("reload")
|
|
@@ -243,7 +239,7 @@ module RubyLsp
|
|
|
243
239
|
nil
|
|
244
240
|
end
|
|
245
241
|
|
|
246
|
-
|
|
242
|
+
#: -> void
|
|
247
243
|
def shutdown
|
|
248
244
|
log_message("Ruby LSP Rails shutting down server")
|
|
249
245
|
send_message("shutdown")
|
|
@@ -254,29 +250,30 @@ module RubyLsp
|
|
|
254
250
|
force_kill
|
|
255
251
|
end
|
|
256
252
|
|
|
257
|
-
|
|
253
|
+
#: -> bool
|
|
258
254
|
def stopped?
|
|
259
255
|
[@stdin, @stdout, @stderr].all?(&:closed?) && !@wait_thread.alive?
|
|
260
256
|
end
|
|
261
257
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
params: T.untyped,
|
|
266
|
-
).returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
|
258
|
+
#: -> bool
|
|
259
|
+
def connected?
|
|
260
|
+
true
|
|
267
261
|
end
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
#: (String request, **untyped params) -> Hash[Symbol, untyped]?
|
|
268
266
|
def make_request(request, **params)
|
|
269
267
|
send_message(request, **params)
|
|
270
268
|
read_response
|
|
271
269
|
end
|
|
272
270
|
|
|
273
271
|
# Notifications are like messages, but one-way, with no response sent back.
|
|
274
|
-
|
|
272
|
+
#: (String request, **untyped params) -> void
|
|
275
273
|
def send_notification(request, **params) = send_message(request, **params)
|
|
276
274
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
sig { overridable.params(request: String, params: T.untyped).void }
|
|
275
|
+
# @overridable
|
|
276
|
+
#: (String request, **untyped params) -> void
|
|
280
277
|
def send_message(request, **params)
|
|
281
278
|
message = { method: request }
|
|
282
279
|
message[:params] = params
|
|
@@ -289,7 +286,8 @@ module RubyLsp
|
|
|
289
286
|
# The server connection died
|
|
290
287
|
end
|
|
291
288
|
|
|
292
|
-
|
|
289
|
+
# @overridable
|
|
290
|
+
#: -> Hash[Symbol, untyped]?
|
|
293
291
|
def read_response
|
|
294
292
|
raw_response = @mutex.synchronize do
|
|
295
293
|
content_length = read_content_length
|
|
@@ -297,9 +295,9 @@ module RubyLsp
|
|
|
297
295
|
raise EmptyMessageError unless content_length
|
|
298
296
|
|
|
299
297
|
@stdout.read(content_length)
|
|
300
|
-
end
|
|
298
|
+
end #: as !nil
|
|
301
299
|
|
|
302
|
-
response = JSON.parse(
|
|
300
|
+
response = JSON.parse(raw_response, symbolize_names: true)
|
|
303
301
|
|
|
304
302
|
if response[:error]
|
|
305
303
|
log_message(
|
|
@@ -315,20 +313,23 @@ module RubyLsp
|
|
|
315
313
|
nil
|
|
316
314
|
end
|
|
317
315
|
|
|
318
|
-
|
|
316
|
+
#: -> void
|
|
319
317
|
def force_kill
|
|
320
318
|
# Windows does not support the `TERM` signal, so we're forced to use `KILL` here
|
|
321
|
-
Process.kill(
|
|
319
|
+
Process.kill(
|
|
320
|
+
Signal.list["KILL"], #: as !nil
|
|
321
|
+
@wait_thread.pid,
|
|
322
|
+
)
|
|
322
323
|
end
|
|
323
324
|
|
|
324
|
-
|
|
325
|
+
#: (::String message, ?type: ::Integer) -> void
|
|
325
326
|
def log_message(message, type: RubyLsp::Constant::MessageType::LOG)
|
|
326
327
|
return if @outgoing_queue.closed?
|
|
327
328
|
|
|
328
329
|
@outgoing_queue << RubyLsp::Notification.window_log_message(message, type: type)
|
|
329
330
|
end
|
|
330
331
|
|
|
331
|
-
|
|
332
|
+
#: -> Integer?
|
|
332
333
|
def read_content_length
|
|
333
334
|
headers = @stdout.gets("\r\n\r\n")
|
|
334
335
|
return unless headers
|
|
@@ -338,43 +339,73 @@ module RubyLsp
|
|
|
338
339
|
|
|
339
340
|
length.to_i
|
|
340
341
|
end
|
|
342
|
+
|
|
343
|
+
# Read a server notification from stderr. Only intended to be used by notifier thread
|
|
344
|
+
#: -> Hash[Symbol, untyped]?
|
|
345
|
+
def read_notification
|
|
346
|
+
headers = @stderr.gets("\r\n\r\n")
|
|
347
|
+
return unless headers
|
|
348
|
+
|
|
349
|
+
length = headers[/Content-Length: (\d+)/i, 1]
|
|
350
|
+
return unless length
|
|
351
|
+
|
|
352
|
+
raw_content = @stderr.read(length.to_i)
|
|
353
|
+
return unless raw_content
|
|
354
|
+
|
|
355
|
+
JSON.parse(raw_content, symbolize_names: true)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
#: (GlobalState global_state) -> String
|
|
359
|
+
def server_relevant_capabilities(global_state)
|
|
360
|
+
{
|
|
361
|
+
supports_progress: global_state.client_capabilities.supports_progress,
|
|
362
|
+
}.to_json
|
|
363
|
+
end
|
|
341
364
|
end
|
|
342
365
|
|
|
343
366
|
class NullClient < RunnerClient
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
sig { void }
|
|
367
|
+
#: -> void
|
|
347
368
|
def initialize # rubocop:disable Lint/MissingSuper
|
|
348
369
|
end
|
|
349
370
|
|
|
350
|
-
|
|
371
|
+
# @override
|
|
372
|
+
#: -> void
|
|
351
373
|
def shutdown
|
|
352
374
|
# no-op
|
|
353
375
|
end
|
|
354
376
|
|
|
355
|
-
|
|
377
|
+
# @override
|
|
378
|
+
#: -> bool
|
|
356
379
|
def stopped?
|
|
357
380
|
true
|
|
358
381
|
end
|
|
359
382
|
|
|
360
|
-
|
|
383
|
+
# @override
|
|
384
|
+
#: -> String
|
|
361
385
|
def rails_root
|
|
362
386
|
Dir.pwd
|
|
363
387
|
end
|
|
364
388
|
|
|
389
|
+
#: -> bool
|
|
390
|
+
def connected?
|
|
391
|
+
false
|
|
392
|
+
end
|
|
393
|
+
|
|
365
394
|
private
|
|
366
395
|
|
|
367
|
-
|
|
396
|
+
#: (::String message, ?type: ::Integer) -> void
|
|
368
397
|
def log_message(message, type: RubyLsp::Constant::MessageType::LOG)
|
|
369
398
|
# no-op
|
|
370
399
|
end
|
|
371
400
|
|
|
372
|
-
|
|
401
|
+
# @override
|
|
402
|
+
#: (String request, **untyped params) -> void
|
|
373
403
|
def send_message(request, **params)
|
|
374
404
|
# no-op
|
|
375
405
|
end
|
|
376
406
|
|
|
377
|
-
|
|
407
|
+
# @override
|
|
408
|
+
#: -> Hash[Symbol, untyped]?
|
|
378
409
|
def read_response
|
|
379
410
|
# no-op
|
|
380
411
|
end
|