diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -146,7 +146,16 @@ std::unique_ptr MsgHandler; std::atomic NextCallID = {0}; std::mutex TranspWriter; - void call(StringRef Method, llvm::json::Value Params); + + // A callback that will be invoked when receiving a client reply for a server + // request. + using ReplyCallback = std::function)>; + std::mutex ReplyCallbacksMutex; + // Request ID => reply Callbacks + llvm::StringMap ReplyCallbacks; + + void call(StringRef Method, llvm::json::Value Params, ReplyCallback CB); + void onReply(llvm::json::Value ID, llvm::Expected Result); void notify(StringRef Method, llvm::json::Value Params); const FileSystemProvider &FSProvider; diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -146,11 +146,9 @@ bool onReply(llvm::json::Value ID, llvm::Expected Result) override { WithContext HandlerContext(handlerContext()); - // We ignore replies, just log them. - if (Result) - log("<-- reply({0})", ID); - else - log("<-- reply({0}) error: {1}", ID, llvm::toString(Result.takeError())); + // Log the reply. + log("<-- reply({0})", ID); + Server.onReply(std::move(ID), std::move(Result)); return true; } @@ -310,14 +308,40 @@ }; // call(), notify(), and reply() wrap the Transport, adding logging and locking. -void ClangdLSPServer::call(llvm::StringRef Method, llvm::json::Value Params) { +void ClangdLSPServer::call(llvm::StringRef Method, llvm::json::Value Params, + ReplyCallback CB) { auto ID = NextCallID++; + auto StrID = llvm::to_string(ID); + { + std::lock_guard Lock(ReplyCallbacksMutex); + ReplyCallbacks[StrID] = std::move(CB); + } log("--> {0}({1})", Method, ID); - // We currently don't handle responses, so no need to store ID anywhere. std::lock_guard Lock(TranspWriter); Transp.call(Method, std::move(Params), ID); } +void ClangdLSPServer::onReply(llvm::json::Value ID, + llvm::Expected Result) { + auto StrID = llvm::to_string(ID); + ReplyCallback CB = nullptr; + { + std::lock_guard Lock(ReplyCallbacksMutex); + CB = ReplyCallbacks.lookup(StrID); + ReplyCallbacks.erase(StrID); + } + // No callback for the client reply, just log them. + if (!CB) { + if (Result) + log("no corresponding callback for the reply ({0})", ID); + else + log("no corresponding callback for the reply ({0}) error: {1}", ID, + llvm::toString(Result.takeError())); + return; + } + CB(std::move(Result)); +} + void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) { log("--> {0}", Method); std::lock_guard Lock(TranspWriter); @@ -501,7 +525,7 @@ Edit.edit = std::move(WE); // Ideally, we would wait for the response and if there is no error, we // would reply success/failure to the original RPC. - call("workspace/applyEdit", Edit); + call("workspace/applyEdit", Edit, /*ReplyCallback=*/nullptr); }; if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND && Params.workspaceEdit) {