Skip to content

Commit 8002896

Browse files
committed
Use log trace instead of printing to stderr
1 parent c5043be commit 8002896

File tree

8 files changed

+141
-57
lines changed

8 files changed

+141
-57
lines changed

lib/ruby_lsp/ruby_lsp_rails/addon.rb

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@ def initialize
2828
# the real client is initialized, features that depend on it will not be blocked by using the NullClient
2929
@rails_runner_client = T.let(NullClient.new, RunnerClient)
3030
@global_state = T.let(nil, T.nilable(GlobalState))
31+
@outgoing_queue = T.let(nil, T.nilable(Thread::Queue))
3132
@addon_mutex = T.let(Mutex.new, Mutex)
3233
@client_mutex = T.let(Mutex.new, Mutex)
3334
@client_mutex.lock
3435

3536
Thread.new do
3637
@addon_mutex.synchronize do
3738
# We need to ensure the Rails client is fully loaded before we activate the server addons
38-
@client_mutex.synchronize { @rails_runner_client = RunnerClient.create_client }
39+
@client_mutex.synchronize { @rails_runner_client = RunnerClient.create_client(T.must(@outgoing_queue)) }
3940
end
4041
end
4142
end
@@ -45,11 +46,13 @@ def rails_runner_client
4546
@addon_mutex.synchronize { @rails_runner_client }
4647
end
4748

48-
sig { override.params(global_state: GlobalState, message_queue: Thread::Queue).void }
49-
def activate(global_state, message_queue)
49+
sig { override.params(global_state: GlobalState, outgoing_queue: Thread::Queue).void }
50+
def activate(global_state, outgoing_queue)
5051
@global_state = global_state
51-
$stderr.puts("Activating Ruby LSP Rails add-on v#{version}")
52-
register_additional_file_watchers(global_state: global_state, message_queue: message_queue)
52+
@outgoing_queue = outgoing_queue
53+
@outgoing_queue << Notification.window_log_message("Activating Ruby LSP Rails add-on v#{VERSION}")
54+
55+
register_additional_file_watchers(global_state: global_state, outgoing_queue: outgoing_queue)
5356
@global_state.index.register_enhancement(IndexingEnhancement.new)
5457

5558
# Start booting the real client in a background thread. Until this completes, the client will be a NullClient
@@ -123,11 +126,11 @@ def workspace_did_change_watched_files(changes)
123126
end
124127
end
125128

126-
sig { params(global_state: GlobalState, message_queue: Thread::Queue).void }
127-
def register_additional_file_watchers(global_state:, message_queue:)
129+
sig { params(global_state: GlobalState, outgoing_queue: Thread::Queue).void }
130+
def register_additional_file_watchers(global_state:, outgoing_queue:)
128131
return unless global_state.supports_watching_files
129132

130-
message_queue << Request.new(
133+
outgoing_queue << Request.new(
131134
id: "ruby-lsp-rails-file-watcher",
132135
method: "client/registerCapability",
133136
params: Interface::RegistrationParams.new(

lib/ruby_lsp/ruby_lsp_rails/runner_client.rb

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,34 @@ class RunnerClient
1010
class << self
1111
extend T::Sig
1212

13-
sig { returns(RunnerClient) }
14-
def create_client
13+
sig { params(outgoing_queue: Thread::Queue).returns(RunnerClient) }
14+
def create_client(outgoing_queue)
1515
if File.exist?("bin/rails")
16-
new
16+
new(outgoing_queue)
1717
else
18-
$stderr.puts(<<~MSG)
19-
Ruby LSP Rails failed to locate bin/rails in the current directory: #{Dir.pwd}"
20-
MSG
21-
$stderr.puts("Server dependent features will not be available")
18+
unless outgoing_queue.closed?
19+
outgoing_queue << RubyLsp::Notification.window_log_message(
20+
<<~MESSAGE.chomp,
21+
Ruby LSP Rails failed to locate bin/rails in the current directory: #{Dir.pwd}
22+
Server dependent features will not be available
23+
MESSAGE
24+
type: RubyLsp::Constant::MessageType::WARNING,
25+
)
26+
end
27+
2228
NullClient.new
2329
end
2430
rescue Errno::ENOENT, StandardError => e # rubocop:disable Lint/ShadowedException
25-
$stderr.puts("Ruby LSP Rails failed to initialize server: #{e.message}\n#{e.backtrace&.join("\n")}")
26-
$stderr.puts("Server dependent features will not be available")
31+
unless outgoing_queue.closed?
32+
outgoing_queue << RubyLsp::Notification.window_log_message(
33+
<<~MESSAGE.chomp,
34+
Ruby LSP Rails failed to initialize server: #{e.full_message}
35+
Server dependent features will not be available
36+
MESSAGE
37+
type: Constant::MessageType::ERROR,
38+
)
39+
end
40+
2741
NullClient.new
2842
end
2943
end
@@ -39,8 +53,9 @@ class EmptyMessageError < StandardError; end
3953
sig { returns(String) }
4054
attr_reader :rails_root
4155

42-
sig { void }
43-
def initialize
56+
sig { params(outgoing_queue: Thread::Queue).void }
57+
def initialize(outgoing_queue)
58+
@outgoing_queue = T.let(outgoing_queue, Thread::Queue)
4459
@mutex = T.let(Mutex.new, Mutex)
4560
# Spring needs a Process session ID. It uses this ID to "attach" itself to the parent process, so that when the
4661
# parent ends, the spring process ends as well. If this is not set, Spring will throw an error while trying to
@@ -69,24 +84,23 @@ def initialize
6984
@stdout.binmode
7085
@stderr.binmode
7186

72-
$stderr.puts("Ruby LSP Rails booting server")
87+
log_message("Ruby LSP Rails booting server")
7388
count = 0
7489

7590
begin
7691
count += 1
7792
initialize_response = T.must(read_response)
7893
@rails_root = T.let(initialize_response[:root], String)
7994
rescue EmptyMessageError
80-
$stderr.puts("Ruby LSP Rails is retrying initialize (#{count})")
95+
log_message("Ruby LSP Rails is retrying initialize (#{count})")
8196
retry if count < MAX_RETRIES
8297
end
8398

84-
$stderr.puts("Finished booting Ruby LSP Rails server")
99+
log_message("Finished booting Ruby LSP Rails server")
85100

86101
unless ENV["RAILS_ENV"] == "test"
87102
at_exit do
88103
if @wait_thread.alive?
89-
$stderr.puts("Ruby LSP Rails is force killing the server")
90104
sleep(0.5) # give the server a bit of time if we already issued a shutdown notification
91105
force_kill
92106
end
@@ -100,15 +114,21 @@ def initialize
100114
def register_server_addon(server_addon_path)
101115
send_notification("server_addon/register", server_addon_path: server_addon_path)
102116
rescue IncompleteMessageError
103-
$stderr.puts("Ruby LSP Rails failed to register server addon #{server_addon_path}")
117+
log_message(
118+
"Ruby LSP Rails failed to register server addon #{server_addon_path}",
119+
type: RubyLsp::Constant::MessageType::ERROR,
120+
)
104121
nil
105122
end
106123

107124
sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
108125
def model(name)
109126
make_request("model", name: name)
110127
rescue IncompleteMessageError
111-
$stderr.puts("Ruby LSP Rails failed to get model information: #{@stderr.read}")
128+
log_message(
129+
"Ruby LSP Rails failed to get model information: #{@stderr.read}",
130+
type: RubyLsp::Constant::MessageType::ERROR,
131+
)
112132
nil
113133
end
114134

@@ -125,37 +145,50 @@ def association_target_location(model_name:, association_name:)
125145
association_name: association_name,
126146
)
127147
rescue => e
128-
$stderr.puts("Ruby LSP Rails failed with #{e.message}: #{@stderr.read}")
148+
log_message(
149+
"Ruby LSP Rails failed with #{e.message}: #{@stderr.read}",
150+
type: RubyLsp::Constant::MessageType::ERROR,
151+
)
152+
nil
129153
end
130154

131155
sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
132156
def route_location(name)
133157
make_request("route_location", name: name)
134158
rescue IncompleteMessageError
135-
$stderr.puts("Ruby LSP Rails failed to get route location: #{@stderr.read}")
159+
log_message(
160+
"Ruby LSP Rails failed to get route location: #{@stderr.read}",
161+
type: RubyLsp::Constant::MessageType::ERROR,
162+
)
136163
nil
137164
end
138165

139166
sig { params(controller: String, action: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
140167
def route(controller:, action:)
141168
make_request("route_info", controller: controller, action: action)
142169
rescue IncompleteMessageError
143-
$stderr.puts("Ruby LSP Rails failed to get route information: #{@stderr.read}")
170+
log_message(
171+
"Ruby LSP Rails failed to get route information: #{@stderr.read}",
172+
type: RubyLsp::Constant::MessageType::ERROR,
173+
)
144174
nil
145175
end
146176

147177
sig { void }
148178
def trigger_reload
149-
$stderr.puts("Reloading Rails application")
179+
log_message("Reloading Rails application")
150180
send_notification("reload")
151181
rescue IncompleteMessageError
152-
$stderr.puts("Ruby LSP Rails failed to trigger reload")
182+
log_message(
183+
"Ruby LSP Rails failed to trigger reload",
184+
type: RubyLsp::Constant::MessageType::ERROR,
185+
)
153186
nil
154187
end
155188

156189
sig { void }
157190
def shutdown
158-
$stderr.puts("Ruby LSP Rails shutting down server")
191+
log_message("Ruby LSP Rails shutting down server")
159192
send_message("shutdown")
160193
sleep(0.5) # give the server a bit of time to shutdown
161194
[@stdin, @stdout, @stderr].each(&:close)
@@ -214,7 +247,10 @@ def read_response
214247
response = JSON.parse(T.must(raw_response), symbolize_names: true)
215248

216249
if response[:error]
217-
$stderr.puts("Ruby LSP Rails error: " + response[:error])
250+
log_message(
251+
"Ruby LSP Rails error: #{response[:error]}",
252+
type: RubyLsp::Constant::MessageType::ERROR,
253+
)
218254
return
219255
end
220256

@@ -229,6 +265,13 @@ def force_kill
229265
# Windows does not support the `TERM` signal, so we're forced to use `KILL` here
230266
Process.kill(T.must(Signal.list["KILL"]), @wait_thread.pid)
231267
end
268+
269+
sig { params(message: ::String, type: ::Integer).void }
270+
def log_message(message, type: RubyLsp::Constant::MessageType::LOG)
271+
return if @outgoing_queue.closed?
272+
273+
@outgoing_queue << RubyLsp::Notification.window_log_message(message, type: type)
274+
end
232275
end
233276

234277
class NullClient < RunnerClient
@@ -255,6 +298,11 @@ def rails_root
255298

256299
private
257300

301+
sig { params(message: ::String, type: ::Integer).void }
302+
def log_message(message, type: RubyLsp::Constant::MessageType::LOG)
303+
# no-op
304+
end
305+
258306
sig { override.params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
259307
def send_message(request, params = nil)
260308
# no-op

test/ruby_lsp_rails/code_lens_test.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,7 @@ def generate_code_lens_for_source(source, file: "/fake.rb")
410410
params: { textDocument: { uri: uri }, position: { line: 0, character: 0 } },
411411
)
412412

413-
result = server.pop_response
414-
415-
assert_instance_of(RubyLsp::Result, result)
413+
result = pop_result(server)
416414
result.response
417415
end
418416
end

test/ruby_lsp_rails/definition_test.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,7 @@ def generate_definitions_for_source(source, position)
233233
params: { textDocument: { uri: uri }, position: position },
234234
)
235235

236-
result = server.pop_response
237-
assert_instance_of(RubyLsp::Result, result)
236+
result = pop_result(server)
238237
result.response
239238
end
240239
end

test/ruby_lsp_rails/document_symbol_test.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,8 @@ def generate_document_symbols_for_source(source)
437437
params: { textDocument: { uri: uri }, position: { line: 0, character: 0 } },
438438
)
439439

440-
server.pop_response.response
440+
result = pop_result(server)
441+
result.response
441442
end
442443
end
443444
end

test/ruby_lsp_rails/hover_test.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ def hover_on_source(source, position)
194194
params: { textDocument: { uri: uri }, position: position },
195195
)
196196

197-
server.pop_response.response
197+
result = pop_result(server)
198+
result.response
198199
end
199200
end
200201
end

test/ruby_lsp_rails/runner_client_test.rb

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ module RubyLsp
88
module Rails
99
class RunnerClientTest < ActiveSupport::TestCase
1010
setup do
11-
@client = T.let(RunnerClient.new, RunnerClient)
11+
@outgoing_queue = Thread::Queue.new
12+
@client = T.let(RunnerClient.new(@outgoing_queue), RunnerClient)
1213
end
1314

1415
teardown do
@@ -17,6 +18,7 @@ class RunnerClientTest < ActiveSupport::TestCase
1718
# On Windows, the server process sometimes takes a lot longer to shutdown and may end up getting force killed,
1819
# which makes this assertion flaky
1920
assert_predicate(@client, :stopped?) unless Gem.win_platform?
21+
@outgoing_queue.close
2022
end
2123

2224
# These are integration tests which start the server. For the more fine-grained tests, see `server_test.rb`.
@@ -44,30 +46,37 @@ class RunnerClientTest < ActiveSupport::TestCase
4446
test "falls back to null client when bin/rails is not found" do
4547
FileUtils.mv("bin/rails", "bin/rails_backup")
4648

47-
assert_output("", %r{Ruby LSP Rails failed to locate bin/rails in the current directory}) do
48-
client = RunnerClient.create_client
49+
outgoing_queue = Thread::Queue.new
50+
client = RunnerClient.create_client(outgoing_queue)
4951

50-
assert_instance_of(NullClient, client)
51-
assert_nil(client.model("User"))
52-
assert_predicate(client, :stopped?)
53-
end
52+
assert_instance_of(NullClient, client)
53+
assert_nil(client.model("User"))
54+
assert_predicate(client, :stopped?)
55+
log = pop_log_notification(outgoing_queue, RubyLsp::Constant::MessageType::WARNING)
56+
57+
assert_instance_of(RubyLsp::Notification, log)
58+
assert_match("Ruby LSP Rails failed to locate bin/rails in the current directory", log.params.message)
5459
ensure
60+
T.must(outgoing_queue).close
5561
FileUtils.mv("bin/rails_backup", "bin/rails")
5662
end
5763

5864
test "failing to spawn server creates a null client" do
5965
FileUtils.mv("test/dummy/config/application.rb", "test/dummy/config/application.rb.bak")
60-
assert_output(
61-
"",
62-
/Ruby LSP Rails failed to initialize server/,
63-
) do
64-
client = RunnerClient.create_client
65-
66-
assert_instance_of(NullClient, client)
67-
assert_nil(client.model("User"))
68-
assert_predicate(client, :stopped?)
69-
end
66+
67+
outgoing_queue = Thread::Queue.new
68+
client = RunnerClient.create_client(outgoing_queue)
69+
70+
assert_instance_of(NullClient, client)
71+
assert_nil(client.model("User"))
72+
assert_predicate(client, :stopped?)
73+
74+
log = pop_log_notification(outgoing_queue, RubyLsp::Constant::MessageType::ERROR)
75+
76+
assert_instance_of(RubyLsp::Notification, log)
77+
assert_match("Ruby LSP Rails failed to initialize server", log.params.message)
7078
ensure
79+
T.must(outgoing_queue).close
7180
FileUtils.mv("test/dummy/config/application.rb.bak", "test/dummy/config/application.rb")
7281
end
7382

@@ -78,14 +87,25 @@ class RunnerClientTest < ActiveSupport::TestCase
7887
File.write("test/dummy/config/application.rb", content + junk)
7988

8089
capture_subprocess_io do
81-
client = RunnerClient.create_client
90+
outgoing_queue = Thread::Queue.new
91+
client = RunnerClient.create_client(outgoing_queue)
8292

8393
response = T.must(client.model("User"))
8494
assert(response.key?(:columns))
95+
ensure
96+
T.must(outgoing_queue).close
8597
end
8698
ensure
8799
FileUtils.mv("test/dummy/config/application.rb.bak", "test/dummy/config/application.rb")
88100
end
101+
102+
private
103+
104+
def pop_log_notification(message_queue, type)
105+
log = message_queue.pop
106+
log = message_queue.pop until log.params.type == type
107+
log
108+
end
89109
end
90110

91111
class NullClientTest < ActiveSupport::TestCase

0 commit comments

Comments
 (0)