Skip to content

Commit 35f8e1e

Browse files
committed
Fallback to NullClient if initializing server fails
1 parent 315db8e commit 35f8e1e

File tree

4 files changed

+68
-7
lines changed

4 files changed

+68
-7
lines changed

lib/ruby_lsp/ruby_lsp_rails/addon.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Addon < ::RubyLsp::Addon
1414

1515
sig { returns(RunnerClient) }
1616
def client
17-
@client ||= T.let(RunnerClient.new, T.nilable(RunnerClient))
17+
@client ||= T.let(RunnerClient.create_client, T.nilable(RunnerClient))
1818
end
1919

2020
sig { override.params(message_queue: Thread::Queue).void }

lib/ruby_lsp/ruby_lsp_rails/runner_client.rb

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,22 @@
44
require "json"
55
require "open3"
66

7-
# NOTE: We should avoid printing to stderr since it causes problems. We never read the standard error pipe
8-
# from the client, so it will become full and eventually hang or crash.
9-
# Instead, return a response with an `error` key.
10-
117
module RubyLsp
128
module Rails
139
class RunnerClient
10+
class << self
11+
extend T::Sig
12+
13+
sig { returns(RunnerClient) }
14+
def create_client
15+
new
16+
rescue Errno::ENOENT, StandardError => e # rubocop:disable Lint/ShadowedException
17+
warn("Ruby LSP Rails failed to initialize server: #{e.message}")
18+
warn("Server dependent features will not be available")
19+
NullClient.new
20+
end
21+
end
22+
1423
extend T::Sig
1524

1625
sig { void }
@@ -48,13 +57,18 @@ def stopped?
4857

4958
private
5059

51-
sig { params(request: T.untyped, params: T.untyped).returns(T.untyped) }
60+
sig do
61+
params(
62+
request: String,
63+
params: T.nilable(T::Hash[Symbol, T.untyped]),
64+
).returns(T.nilable(T::Hash[Symbol, T.untyped]))
65+
end
5266
def make_request(request, params = nil)
5367
send_message(request, params)
5468
read_response
5569
end
5670

57-
sig { params(request: T.untyped, params: T.untyped).void }
71+
sig { params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
5872
def send_message(request, params = nil)
5973
message = { method: request }
6074
message[:params] = params if params
@@ -80,5 +94,35 @@ def read_response
8094
response.fetch(:result)
8195
end
8296
end
97+
98+
class NullClient < RunnerClient
99+
extend T::Sig
100+
101+
sig { void }
102+
def initialize # rubocop:disable Lint/MissingSuper
103+
end
104+
105+
sig { override.void }
106+
def shutdown
107+
# no-op
108+
end
109+
110+
sig { override.returns(T::Boolean) }
111+
def stopped?
112+
true
113+
end
114+
115+
private
116+
117+
sig { override.params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
118+
def send_message(request, params = nil)
119+
# no-op
120+
end
121+
122+
sig { override.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
123+
def read_response
124+
# no-op
125+
end
126+
end
83127
end
84128
end

lib/ruby_lsp/ruby_lsp_rails/server.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
nil
2020
end
2121

22+
# NOTE: We should avoid printing to stderr since it causes problems. We never read the standard error pipe from the
23+
# client, so it will become full and eventually hang or crash. Instead, return a response with an `error` key.
24+
2225
module RubyLsp
2326
module Rails
2427
class Server

test/ruby_lsp_rails/runner_client_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ class RunnerClientTest < ActiveSupport::TestCase
3636
test "returns nil if the request returns a nil response" do
3737
assert_nil @client.model("ApplicationRecord") # ApplicationRecord is abstract
3838
end
39+
40+
test "failing to spawn server creates a null client" do
41+
FileUtils.mv("bin/rails", "bin/rails_backup")
42+
43+
assert_output("", %r{No such file or directory - bin/rails}) do
44+
client = RunnerClient.create_client
45+
46+
assert_instance_of(NullClient, client)
47+
assert_nil(client.model("User"))
48+
assert_predicate(client, :stopped?)
49+
end
50+
ensure
51+
FileUtils.mv("bin/rails_backup", "bin/rails")
52+
end
3953
end
4054
end
4155
end

0 commit comments

Comments
 (0)