Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use window log messages instead of printing to stderr #2419

Merged
merged 1 commit into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/ruby_lsp/addon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,18 @@ def inherited(child_class)
super
end

# Discovers and loads all addons. Returns the list of activated addons
sig { params(global_state: GlobalState, outgoing_queue: Thread::Queue).returns(T::Array[Addon]) }
# Discovers and loads all addons. Returns a list of errors when trying to require addons
sig do
params(global_state: GlobalState, outgoing_queue: Thread::Queue).returns(T::Array[StandardError])
end
def load_addons(global_state, outgoing_queue)
# Require all addons entry points, which should be placed under
# `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
Gem.find_files("ruby_lsp/**/addon.rb").each do |addon|
errors = Gem.find_files("ruby_lsp/**/addon.rb").filter_map do |addon|
require File.expand_path(addon)
nil
rescue => e
$stderr.puts(e.full_message)
e
end

# Instantiate all discovered addon classes
Expand All @@ -71,6 +74,8 @@ def load_addons(global_state, outgoing_queue)
rescue => e
addon.add_error(e)
end

errors
end

# Intended for use by tests for addons
Expand Down
9 changes: 7 additions & 2 deletions lib/ruby_lsp/base_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def start
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
process_message(message)
when "shutdown"
$stderr.puts("Shutting down Ruby LSP...")
send_log_message("Shutting down Ruby LSP...")

shutdown

Expand All @@ -76,7 +76,7 @@ def start
when "exit"
@mutex.synchronize do
status = @incoming_queue.closed? ? 0 : 1
$stderr.puts("Shutdown complete with status #{status}")
send_log_message("Shutdown complete with status #{status}")
exit(status)
end
else
Expand Down Expand Up @@ -145,5 +145,10 @@ def send_message(message)
def send_empty_response(id)
send_message(Result.new(id: id, response: nil))
end

sig { params(message: String, type: Integer).void }
def send_log_message(message, type: Constant::MessageType::LOG)
send_message(Notification.window_log_message(message, type: Constant::MessageType::LOG))
end
end
end
46 changes: 33 additions & 13 deletions lib/ruby_lsp/global_state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,48 @@ def active_linters
@linters.filter_map { |name| @supported_formatters[name] }
end

sig { params(options: T::Hash[Symbol, T.untyped]).void }
# Applies the options provided by the editor and returns an array of notifications to send back to the client
sig { params(options: T::Hash[Symbol, T.untyped]).returns(T::Array[Notification]) }
def apply_options(options)
notifications = []
direct_dependencies = gather_direct_dependencies
all_dependencies = gather_direct_and_indirect_dependencies
workspace_uri = options.dig(:workspaceFolders, 0, :uri)
@workspace_uri = URI(workspace_uri) if workspace_uri

specified_formatter = options.dig(:initializationOptions, :formatter)
@formatter = specified_formatter if specified_formatter
@formatter = detect_formatter(direct_dependencies, all_dependencies) if @formatter == "auto"

if specified_formatter
@formatter = specified_formatter

if specified_formatter != "auto"
notifications << Notification.window_log_message("Using formatter specified by user: #{@formatter}")
end
end

if @formatter == "auto"
@formatter = detect_formatter(direct_dependencies, all_dependencies)
notifications << Notification.window_log_message("Auto detected formatter: #{@formatter}")
end

specified_linters = options.dig(:initializationOptions, :linters)
@linters = specified_linters || detect_linters(direct_dependencies, all_dependencies)

notifications << if specified_linters
Notification.window_log_message("Using linters specified by user: #{@linters.join(", ")}")
else
Notification.window_log_message("Auto detected linters: #{@linters.join(", ")}")
end

@test_library = detect_test_library(direct_dependencies)
notifications << Notification.window_log_message("Detected test library: #{@test_library}")

@has_type_checker = detect_typechecker(direct_dependencies)
if @has_type_checker
notifications << Notification.window_log_message(
"Ruby LSP detected this is a Sorbet project and will defer to the Sorbet LSP for some functionality",
)
end

encodings = options.dig(:capabilities, :general, :positionEncodings)
@encoding = if !encodings || encodings.empty?
Expand All @@ -91,6 +118,8 @@ def apply_options(options)

@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
@type_inferrer.experimental_features = @experimental_features

notifications
end

sig { returns(String) }
Expand Down Expand Up @@ -163,16 +192,7 @@ def detect_test_library(dependencies)
def detect_typechecker(dependencies)
return false if ENV["RUBY_LSP_BYPASS_TYPECHECKER"]

# We can't read the env from within `Bundle.with_original_env` so we need to set it here.
ruby_lsp_env_is_test = (ENV["RUBY_LSP_ENV"] == "test")
Bundler.with_original_env do
sorbet_static_detected = dependencies.any?(/^sorbet-static/)
# Don't show message while running tests, since it's noisy
if sorbet_static_detected && !ruby_lsp_env_is_test
$stderr.puts("Ruby LSP detected this is a Sorbet project so will defer to Sorbet LSP for some functionality")
end
sorbet_static_detected
end
dependencies.any?(/^sorbet-static/)
rescue Bundler::GemfileNotFound
false
end
Expand Down
27 changes: 21 additions & 6 deletions lib/ruby_lsp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def initialize(test_mode: false)
def process_message(message)
case message[:method]
when "initialize"
$stderr.puts("Initializing Ruby LSP v#{VERSION}...")
send_log_message("Initializing Ruby LSP v#{VERSION}...")
run_initialize(message)
when "initialized"
$stderr.puts("Finished initializing Ruby LSP!") unless @test_mode
send_log_message("Finished initializing Ruby LSP!") unless @test_mode
run_initialized
when "textDocument/didOpen"
text_document_did_open(message)
Expand Down Expand Up @@ -121,12 +121,20 @@ def process_message(message)
end
end

$stderr.puts("Error processing #{message[:method]}: #{e.full_message}")
send_log_message("Error processing #{message[:method]}: #{e.full_message}", type: Constant::MessageType::ERROR)
end

sig { void }
def load_addons
Addon.load_addons(@global_state, @outgoing_queue)
errors = Addon.load_addons(@global_state, @outgoing_queue)

if errors.any?
send_log_message(
"Error loading addons:\n\n#{errors.map(&:full_message).join("\n\n")}",
type: Constant::MessageType::WARNING,
)
end

errored_addons = Addon.addons.select(&:error?)

if errored_addons.any?
Expand All @@ -140,7 +148,12 @@ def load_addons
),
)

$stderr.puts(errored_addons.map(&:errors_details).join("\n\n")) unless @test_mode
unless @test_mode
send_log_message(
errored_addons.map(&:errors_details).join("\n\n"),
type: Constant::MessageType::WARNING,
)
end
end
end

Expand All @@ -149,7 +162,7 @@ def load_addons
sig { params(message: T::Hash[Symbol, T.untyped]).void }
def run_initialize(message)
options = message[:params]
@global_state.apply_options(options)
global_state_notifications = @global_state.apply_options(options)

client_name = options.dig(:clientInfo, :name)
@store.client_name = client_name if client_name
Expand Down Expand Up @@ -258,6 +271,8 @@ def run_initialize(message)
process_indexing_configuration(options.dig(:initializationOptions, :indexing))

begin_progress("indexing-progress", "Ruby LSP: indexing files")

global_state_notifications.each { |notification| send_message(notification) }
end

sig { void }
Expand Down
12 changes: 12 additions & 0 deletions lib/ruby_lsp/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def to_hash; end
class Notification < Message
class << self
extend T::Sig

sig { params(message: String).returns(Notification) }
def window_show_error(message)
new(
Expand All @@ -63,6 +64,14 @@ def window_show_error(message)
),
)
end

sig { params(message: String, type: Integer).returns(Notification) }
def window_log_message(message, type: Constant::MessageType::LOG)
new(
method: "window/logMessage",
params: Interface::LogMessageParams.new(type: type, message: message),
)
end
end

extend T::Sig
Expand Down Expand Up @@ -122,6 +131,9 @@ class Result
sig { returns(T.untyped) }
attr_reader :response

sig { returns(Integer) }
attr_reader :id

sig { params(id: Integer, response: T.untyped).void }
def initialize(id:, response:)
@id = id
Expand Down
Loading
Loading