Skip to content

Commit

Permalink
Use correct error response for cancellations
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Nov 29, 2024
1 parent b0eb396 commit 3f7505f
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 2 deletions.
15 changes: 13 additions & 2 deletions lib/ruby_lsp/base_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def start
# The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
# else is pushed into the incoming queue
case method
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange",
"$/cancelRequest"
process_message(message)
when "shutdown"
send_log_message("Shutting down Ruby LSP...")
Expand Down Expand Up @@ -133,6 +134,12 @@ def pop_response
@outgoing_queue.pop
end

# This method is only intended to be used in tests! Pushes a message to the incoming queue directly
sig { params(message: T::Hash[Symbol, T.untyped]).void }
def push_message(message)
@incoming_queue << message
end

sig { abstract.params(message: T::Hash[Symbol, T.untyped]).void }
def process_message(message); end

Expand All @@ -154,7 +161,11 @@ def new_worker
# Check if the request was cancelled before trying to process it
@mutex.synchronize do
if id && @cancelled_requests.include?(id)
send_message(Result.new(id: id, response: nil))
send_message(Error.new(
id: id,
code: Constant::ErrorCodes::REQUEST_CANCELLED,
message: "Request #{id} was cancelled",
))
@cancelled_requests.delete(id)
next
end
Expand Down
3 changes: 3 additions & 0 deletions lib/ruby_lsp/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ class Error
sig { returns(String) }
attr_reader :message

sig { returns(Integer) }
attr_reader :code

sig { params(id: Integer, code: Integer, message: String, data: T.nilable(T::Hash[Symbol, T.untyped])).void }
def initialize(id:, code:, message:, data: nil)
@id = id
Expand Down
45 changes: 45 additions & 0 deletions test/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,51 @@ def handle_window_show_message_response(title)
end
end

def test_cancelling_requests_returns_expected_error_code
uri = URI("file:///foo.rb")

@server.process_message({
method: "textDocument/didOpen",
params: {
textDocument: {
uri: uri,
text: "class Foo\nend",
version: 1,
languageId: "ruby",
},
},
})

mutex = Mutex.new
mutex.lock

# Use a mutex to lock the request in the middle so that we can cancel it before it finishes
@server.stubs(:text_document_definition) do |_message|
mutex.synchronize { 123 }
end

thread = Thread.new do
@server.push_message({
id: 1,
method: "textDocument/definition",
params: {
textDocument: {
uri: uri,
},
position: { line: 0, character: 6 },
},
})
end

@server.process_message({ method: "$/cancelRequest", params: { id: 1 } })
mutex.unlock
thread.join

error = find_message(RubyLsp::Error)
assert_equal(RubyLsp::Constant::ErrorCodes::REQUEST_CANCELLED, error.code)
assert_equal("Request 1 was cancelled", error.message)
end

private

def with_uninstalled_rubocop(&block)
Expand Down

0 comments on commit 3f7505f

Please sign in to comment.