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.
@@ -8,12 +8,10 @@ module RubyLsp
8
8
  module Rails
9
9
  class RunnerClient
10
10
  class << self
11
- extend T::Sig
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
- extend T::Sig
50
-
51
- sig { returns(String) }
47
+ #: String
52
48
  attr_reader :rails_root
53
49
 
54
- sig { params(outgoing_queue: Thread::Queue).void }
55
- def initialize(outgoing_queue)
56
- @outgoing_queue = T.let(outgoing_queue, Thread::Queue)
57
- @mutex = T.let(Mutex.new, 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("bundle", "exec", "rails", "runner", "#{__dir__}/server.rb", "start")
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 = T.let(stdin, IO)
78
- @stdout = T.let(stdout, IO)
79
- @stderr = T.let(stderr, IO)
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 = T.let(wait_thread, Process::Waiter)
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 = T.must(read_response)
91
- @rails_root = T.let(initialize_response[:root], String)
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
- @logger_thread = T.let(
104
- Thread.new do
105
- while (content = @stderr.gets("\n"))
106
- log_message(content, type: RubyLsp::Constant::MessageType::LOG)
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
- rescue IOError
109
- # The server was shutdown and stderr is already closed
110
- end,
111
- Thread,
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
- sig { params(server_addon_path: String).void }
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
- sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
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
- sig do
140
- params(
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
- "association_target_location",
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
- sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
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
- sig { params(controller: String, action: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
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
- sig { params(server_addon_name: String, request_name: String, params: T.untyped).void }
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
- sig { returns(T.nilable(String)) }
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
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
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
- sig do
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
- sig { void }
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
- sig { void }
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
- sig { returns(T::Boolean) }
253
+ #: -> bool
258
254
  def stopped?
259
255
  [@stdin, @stdout, @stderr].all?(&:closed?) && !@wait_thread.alive?
260
256
  end
261
257
 
262
- sig do
263
- params(
264
- request: String,
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
- sig { params(request: String, params: T.untyped).void }
272
+ #: (String request, **untyped params) -> void
275
273
  def send_notification(request, **params) = send_message(request, **params)
276
274
 
277
- private
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
- sig { overridable.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
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(T.must(raw_response), symbolize_names: true)
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
- sig { void }
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(T.must(Signal.list["KILL"]), @wait_thread.pid)
319
+ Process.kill(
320
+ Signal.list["KILL"], #: as !nil
321
+ @wait_thread.pid,
322
+ )
322
323
  end
323
324
 
324
- sig { params(message: ::String, type: ::Integer).void }
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
- sig { returns(T.nilable(Integer)) }
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
- extend T::Sig
345
-
346
- sig { void }
367
+ #: -> void
347
368
  def initialize # rubocop:disable Lint/MissingSuper
348
369
  end
349
370
 
350
- sig { override.void }
371
+ # @override
372
+ #: -> void
351
373
  def shutdown
352
374
  # no-op
353
375
  end
354
376
 
355
- sig { override.returns(T::Boolean) }
377
+ # @override
378
+ #: -> bool
356
379
  def stopped?
357
380
  true
358
381
  end
359
382
 
360
- sig { override.returns(String) }
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
- sig { params(message: ::String, type: ::Integer).void }
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
- sig { override.params(request: String, params: T.untyped).void }
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
- sig { override.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
407
+ # @override
408
+ #: -> Hash[Symbol, untyped]?
378
409
  def read_response
379
410
  # no-op
380
411
  end