Skip to content

Commit 8b049d7

Browse files
authored
Allow server to print debug messages (#478)
1 parent 8f7fc5b commit 8b049d7

File tree

4 files changed

+74
-24
lines changed

4 files changed

+74
-24
lines changed

lib/ruby_lsp/ruby_lsp_rails/runner_client.rb

+14-5
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,15 @@ def initialize(outgoing_queue)
106106
end
107107
end
108108
end
109+
110+
@logger_thread = T.let(
111+
Thread.new do
112+
while (content = @stderr.gets("\n"))
113+
log_message(content, type: RubyLsp::Constant::MessageType::LOG)
114+
end
115+
end,
116+
Thread,
117+
)
109118
rescue Errno::EPIPE, IncompleteMessageError
110119
raise InitializationError, @stderr.read
111120
end
@@ -126,7 +135,7 @@ def model(name)
126135
make_request("model", name: name)
127136
rescue IncompleteMessageError
128137
log_message(
129-
"Ruby LSP Rails failed to get model information: #{@stderr.read}",
138+
"Ruby LSP Rails failed to get model information",
130139
type: RubyLsp::Constant::MessageType::ERROR,
131140
)
132141
nil
@@ -144,9 +153,9 @@ def association_target_location(model_name:, association_name:)
144153
model_name: model_name,
145154
association_name: association_name,
146155
)
147-
rescue => e
156+
rescue IncompleteMessageError
148157
log_message(
149-
"Ruby LSP Rails failed with #{e.message}: #{@stderr.read}",
158+
"Ruby LSP Rails failed to get association location",
150159
type: RubyLsp::Constant::MessageType::ERROR,
151160
)
152161
nil
@@ -157,7 +166,7 @@ def route_location(name)
157166
make_request("route_location", name: name)
158167
rescue IncompleteMessageError
159168
log_message(
160-
"Ruby LSP Rails failed to get route location: #{@stderr.read}",
169+
"Ruby LSP Rails failed to get route location",
161170
type: RubyLsp::Constant::MessageType::ERROR,
162171
)
163172
nil
@@ -168,7 +177,7 @@ def route(controller:, action:)
168177
make_request("route_info", controller: controller, action: action)
169178
rescue IncompleteMessageError
170179
log_message(
171-
"Ruby LSP Rails failed to get route information: #{@stderr.read}",
180+
"Ruby LSP Rails failed to get route information",
172181
type: RubyLsp::Constant::MessageType::ERROR,
173182
)
174183
nil

lib/ruby_lsp/ruby_lsp_rails/server.rb

+23-18
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,22 @@
88

99
module RubyLsp
1010
module Rails
11+
module Common
12+
# Write a message to the client. Can be used for sending notifications to the editor
13+
def send_message(message)
14+
json_message = message.to_json
15+
@stdout.write("Content-Length: #{json_message.length}\r\n\r\n#{json_message}")
16+
end
17+
18+
# Log a debug message to the editor's output
19+
def debug_message(message)
20+
$stderr.puts(message)
21+
end
22+
end
23+
1124
class ServerAddon
25+
include Common
26+
1227
@server_addon_classes = []
1328
@server_addons = {}
1429

@@ -38,12 +53,6 @@ def initialize(stdout)
3853
@stdout = stdout
3954
end
4055

41-
# Write a response back. Can be used for sending notifications to the editor
42-
def write_response(response)
43-
json_response = response.to_json
44-
@stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
45-
end
46-
4756
def name
4857
raise NotImplementedError, "Not implemented!"
4958
end
@@ -54,6 +63,8 @@ def execute(request, params)
5463
end
5564

5665
class Server
66+
include Common
67+
5768
def initialize(stdout: $stdout, override_default_output_device: true)
5869
# Grab references to the original pipes so that we can change the default output device further down
5970
@stdin = $stdin
@@ -79,8 +90,7 @@ def start
7990
routes_reloader = ::Rails.application.routes_reloader
8091
routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
8192

82-
initialize_result = { result: { message: "ok", root: ::Rails.root.to_s } }.to_json
83-
@stdout.write("Content-Length: #{initialize_result.length}\r\n\r\n#{initialize_result}")
93+
send_message({ result: { message: "ok", root: ::Rails.root.to_s } })
8494

8595
while @running
8696
headers = @stdin.gets("\r\n\r\n")
@@ -96,15 +106,15 @@ def execute(request, params)
96106
when "shutdown"
97107
@running = false
98108
when "model"
99-
write_response(resolve_database_info_from_model(params.fetch(:name)))
109+
send_message(resolve_database_info_from_model(params.fetch(:name)))
100110
when "association_target_location"
101-
write_response(resolve_association_target(params))
111+
send_message(resolve_association_target(params))
102112
when "reload"
103113
::Rails.application.reloader.reload!
104114
when "route_location"
105-
write_response(route_location(params.fetch(:name)))
115+
send_message(route_location(params.fetch(:name)))
106116
when "route_info"
107-
write_response(resolve_route_info(params))
117+
send_message(resolve_route_info(params))
108118
when "server_addon/register"
109119
require params[:server_addon_path]
110120
ServerAddon.finalize_registrations!(@stdout)
@@ -114,16 +124,11 @@ def execute(request, params)
114124
ServerAddon.delegate(server_addon_name, request_name, params)
115125
end
116126
rescue => e
117-
write_response({ error: e.full_message(highlight: false) })
127+
send_message({ error: e.full_message(highlight: false) })
118128
end
119129

120130
private
121131

122-
def write_response(response)
123-
json_response = response.to_json
124-
@stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
125-
end
126-
127132
def resolve_route_info(requirements)
128133
if requirements[:controller]
129134
requirements[:controller] = requirements.fetch(:controller).underscore.delete_suffix("_controller")

test/ruby_lsp_rails/runner_client_test.rb

+36
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,42 @@ class RunnerClientTest < ActiveSupport::TestCase
119119
@client.delegate_request(server_addon_name: "My Add-on", request_name: "do_something", id: 5)
120120
end
121121

122+
test "server add-ons can log messages with the editor" do
123+
File.write("server_addon.rb", <<~RUBY)
124+
class TapiocaServerAddon < RubyLsp::Rails::ServerAddon
125+
def name
126+
"Tapioca"
127+
end
128+
129+
def execute(request, params)
130+
debug_message("Hello!")
131+
send_message({ request:, params: })
132+
end
133+
end
134+
RUBY
135+
136+
@client.register_server_addon(File.expand_path("server_addon.rb"))
137+
@client.delegate_notification(server_addon_name: "Tapioca", request_name: "dsl")
138+
139+
# Started booting server
140+
pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)
141+
# Finished booting server
142+
pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)
143+
144+
log = pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)
145+
146+
# Sometimes we get warnings concerning deprecations and they mess up this expectation
147+
3.times do
148+
unless log.params.message.match?(/Hello!/)
149+
log = pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)
150+
end
151+
end
152+
153+
assert_match("Hello!", log.params.message)
154+
ensure
155+
FileUtils.rm("server_addon.rb")
156+
end
157+
122158
private
123159

124160
def pop_log_notification(message_queue, type)

test/ruby_lsp_rails/server_test.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def name
144144
end
145145
146146
def execute(request, params)
147-
write_response({ request:, params: })
147+
send_message({ request:, params: })
148148
end
149149
end
150150
RUBY

0 commit comments

Comments
 (0)