Skip to content

Commit

Permalink
Allow server to print debug messages
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Oct 4, 2024
1 parent 8f7fc5b commit 836da15
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 24 deletions.
19 changes: 14 additions & 5 deletions lib/ruby_lsp/ruby_lsp_rails/runner_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ def initialize(outgoing_queue)
end
end
end

@logger_thread = T.let(
Thread.new do
while (content = @stderr.gets("\n"))
log_message(content, type: RubyLsp::Constant::MessageType::LOG)
end
end,
Thread,
)
rescue Errno::EPIPE, IncompleteMessageError
raise InitializationError, @stderr.read
end
Expand All @@ -126,7 +135,7 @@ def model(name)
make_request("model", name: name)
rescue IncompleteMessageError
log_message(
"Ruby LSP Rails failed to get model information: #{@stderr.read}",
"Ruby LSP Rails failed to get model information",
type: RubyLsp::Constant::MessageType::ERROR,
)
nil
Expand All @@ -144,9 +153,9 @@ def association_target_location(model_name:, association_name:)
model_name: model_name,
association_name: association_name,
)
rescue => e
rescue IncompleteMessageError
log_message(
"Ruby LSP Rails failed with #{e.message}: #{@stderr.read}",
"Ruby LSP Rails failed to get association location",
type: RubyLsp::Constant::MessageType::ERROR,
)
nil
Expand All @@ -157,7 +166,7 @@ def route_location(name)
make_request("route_location", name: name)
rescue IncompleteMessageError
log_message(
"Ruby LSP Rails failed to get route location: #{@stderr.read}",
"Ruby LSP Rails failed to get route location",
type: RubyLsp::Constant::MessageType::ERROR,
)
nil
Expand All @@ -168,7 +177,7 @@ def route(controller:, action:)
make_request("route_info", controller: controller, action: action)
rescue IncompleteMessageError
log_message(
"Ruby LSP Rails failed to get route information: #{@stderr.read}",
"Ruby LSP Rails failed to get route information",
type: RubyLsp::Constant::MessageType::ERROR,
)
nil
Expand Down
41 changes: 23 additions & 18 deletions lib/ruby_lsp/ruby_lsp_rails/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@

module RubyLsp
module Rails
module Common
# Write a message to the client. Can be used for sending notifications to the editor
def send_message(message)
json_message = message.to_json
@stdout.write("Content-Length: #{json_message.length}\r\n\r\n#{json_message}")
end

# Log a debug message to the editor's output
def debug_message(message)
$stderr.puts(message)
end
end

class ServerAddon
include Common

@server_addon_classes = []
@server_addons = {}

Expand Down Expand Up @@ -38,12 +53,6 @@ def initialize(stdout)
@stdout = stdout
end

# Write a response back. Can be used for sending notifications to the editor
def write_response(response)
json_response = response.to_json
@stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
end

def name
raise NotImplementedError, "Not implemented!"
end
Expand All @@ -54,6 +63,8 @@ def execute(request, params)
end

class Server
include Common

def initialize(stdout: $stdout, override_default_output_device: true)
# Grab references to the original pipes so that we can change the default output device further down
@stdin = $stdin
Expand All @@ -79,8 +90,7 @@ def start
routes_reloader = ::Rails.application.routes_reloader
routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)

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

while @running
headers = @stdin.gets("\r\n\r\n")
Expand All @@ -96,15 +106,15 @@ def execute(request, params)
when "shutdown"
@running = false
when "model"
write_response(resolve_database_info_from_model(params.fetch(:name)))
send_message(resolve_database_info_from_model(params.fetch(:name)))
when "association_target_location"
write_response(resolve_association_target(params))
send_message(resolve_association_target(params))
when "reload"
::Rails.application.reloader.reload!
when "route_location"
write_response(route_location(params.fetch(:name)))
send_message(route_location(params.fetch(:name)))
when "route_info"
write_response(resolve_route_info(params))
send_message(resolve_route_info(params))
when "server_addon/register"
require params[:server_addon_path]
ServerAddon.finalize_registrations!(@stdout)
Expand All @@ -114,16 +124,11 @@ def execute(request, params)
ServerAddon.delegate(server_addon_name, request_name, params)
end
rescue => e
write_response({ error: e.full_message(highlight: false) })
send_message({ error: e.full_message(highlight: false) })
end

private

def write_response(response)
json_response = response.to_json
@stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
end

def resolve_route_info(requirements)
if requirements[:controller]
requirements[:controller] = requirements.fetch(:controller).underscore.delete_suffix("_controller")
Expand Down
34 changes: 34 additions & 0 deletions test/ruby_lsp_rails/runner_client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,40 @@ class RunnerClientTest < ActiveSupport::TestCase
@client.delegate_request(server_addon_name: "My Add-on", request_name: "do_something", id: 5)
end

test "server add-ons can log messages with the editor" do
File.write("server_addon.rb", <<~RUBY)
class TapiocaServerAddon < RubyLsp::Rails::ServerAddon
def name
"Tapioca"
end
def execute(request, params)
debug_message("Hello!")
send_message({ request:, params: })
end
end
RUBY

@client.register_server_addon(File.expand_path("server_addon.rb"))
@client.delegate_notification(server_addon_name: "Tapioca", request_name: "dsl")

# Started booting server
pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)
# Finished booting server
pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)

log = pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)

# Sometimes we get warnings concerning deprecations and they mess up this expectation
unless log.params.message.match?(/Hello!/)
log = pop_log_notification(@outgoing_queue, RubyLsp::Constant::MessageType::LOG)
end

assert_match("Hello!", log.params.message)
ensure
FileUtils.rm("server_addon.rb")
end

private

def pop_log_notification(message_queue, type)
Expand Down
2 changes: 1 addition & 1 deletion test/ruby_lsp_rails/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def name
end
def execute(request, params)
write_response({ request:, params: })
send_message({ request:, params: })
end
end
RUBY
Expand Down

0 comments on commit 836da15

Please sign in to comment.