Index: clang-tools-extra/trunk/clangd/ClangdLSPServer.h =================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.h +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h @@ -39,7 +39,9 @@ /// opened in binary mode. Output will be written using Out variable passed to /// class constructor. This method must not be executed more than once for /// each instance of ClangdLSPServer. - void run(std::istream &In); + /// + /// \return Wether we received a 'shutdown' request before an 'exit' request + bool run(std::istream &In); private: // Implement DiagnosticsConsumer. @@ -50,6 +52,7 @@ // Implement ProtocolCallbacks. void onInitialize(Ctx C, InitializeParams &Params) override; void onShutdown(Ctx C, ShutdownParams &Params) override; + void onExit(Ctx C, ExitParams &Params) override; void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) override; void onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) override; void onDocumentDidClose(Ctx C, DidCloseTextDocumentParams &Params) override; @@ -79,6 +82,10 @@ JSONOutput &Out; /// Used to indicate that the 'shutdown' request was received from the /// Language Server client. + bool ShutdownRequestReceived = false; + + /// Used to indicate that the 'exit' notification was received from the + /// Language Server client. /// It's used to break out of the LSP parsing loop. bool IsDone = false; Index: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp @@ -56,9 +56,13 @@ } void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) { - IsDone = true; + // Do essentially nothing, just say we're ready to exit. + ShutdownRequestReceived = true; + C.reply("null"); } +void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone = true; } + void ClangdLSPServer::onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) { if (Params.metadata && !Params.metadata->extraFlags.empty()) @@ -197,7 +201,7 @@ /*EnableSnippetsAndCodePatterns=*/SnippetCompletions), /*Logger=*/Out, ResourceDir) {} -void ClangdLSPServer::run(std::istream &In) { +bool ClangdLSPServer::run(std::istream &In) { assert(!IsDone && "Run was called before"); // Set up JSONRPCDispatcher. @@ -213,6 +217,8 @@ // Make sure IsDone is set to true after this method exits to ensure assertion // at the start of the method fires if it's ever executed again. IsDone = true; + + return ShutdownRequestReceived; } std::vector Index: clang-tools-extra/trunk/clangd/Protocol.h =================================================================== --- clang-tools-extra/trunk/clangd/Protocol.h +++ clang-tools-extra/trunk/clangd/Protocol.h @@ -173,6 +173,7 @@ } }; using ShutdownParams = NoParams; +using ExitParams = NoParams; struct InitializeParams { /// The process Id of the parent process that started Index: clang-tools-extra/trunk/clangd/ProtocolHandlers.h =================================================================== --- clang-tools-extra/trunk/clangd/ProtocolHandlers.h +++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h @@ -34,6 +34,7 @@ virtual void onInitialize(Ctx C, InitializeParams &Params) = 0; virtual void onShutdown(Ctx C, ShutdownParams &Params) = 0; + virtual void onExit(Ctx C, ExitParams &Params) = 0; virtual void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) = 0; virtual void onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) = 0; Index: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp +++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp @@ -52,6 +52,7 @@ Register("initialize", &ProtocolCallbacks::onInitialize); Register("shutdown", &ProtocolCallbacks::onShutdown); + Register("exit", &ProtocolCallbacks::onExit); Register("textDocument/didOpen", &ProtocolCallbacks::onDocumentDidOpen); Register("textDocument/didClose", &ProtocolCallbacks::onDocumentDidClose); Register("textDocument/didChange", &ProtocolCallbacks::onDocumentDidChange); Index: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp =================================================================== --- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp +++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp @@ -114,5 +114,7 @@ /// Initialize and run ClangdLSPServer. ClangdLSPServer LSPServer(Out, WorkerThreadsCount, EnableSnippets, ResourceDirRef, CompileCommandsDirPath); - LSPServer.run(std::cin); + + constexpr int NoShutdownRequestErrorCode = 1; + return LSPServer.run(std::cin) ? 0 : NoShutdownRequestErrorCode; } Index: clang-tools-extra/trunk/test/clangd/authority-less-uri.test =================================================================== --- clang-tools-extra/trunk/test/clangd/authority-less-uri.test +++ clang-tools-extra/trunk/test/clangd/authority-less-uri.test @@ -30,3 +30,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":3,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/completion-priorities.test =================================================================== --- clang-tools-extra/trunk/test/clangd/completion-priorities.test +++ clang-tools-extra/trunk/test/clangd/completion-priorities.test @@ -34,3 +34,7 @@ Content-Length: 58 {"jsonrpc":"2.0","id":4,"method":"shutdown","params":null} +# CHECK: {"jsonrpc":"2.0","id":4,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/completion-qualifiers.test =================================================================== --- clang-tools-extra/trunk/test/clangd/completion-qualifiers.test +++ clang-tools-extra/trunk/test/clangd/completion-qualifiers.test @@ -16,3 +16,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":4,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":4,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/completion-snippet.test =================================================================== --- clang-tools-extra/trunk/test/clangd/completion-snippet.test +++ clang-tools-extra/trunk/test/clangd/completion-snippet.test @@ -52,3 +52,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":4,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":4,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/completion.test =================================================================== --- clang-tools-extra/trunk/test/clangd/completion.test +++ clang-tools-extra/trunk/test/clangd/completion.test @@ -52,3 +52,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":4,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":4,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/definitions.test =================================================================== --- clang-tools-extra/trunk/test/clangd/definitions.test +++ clang-tools-extra/trunk/test/clangd/definitions.test @@ -166,3 +166,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":3,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test =================================================================== --- clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test +++ clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test @@ -13,3 +13,7 @@ Content-Length: 58 {"jsonrpc":"2.0","id":2,"method":"shutdown","params":null} +# CHECK: {"jsonrpc":"2.0","id":2,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/diagnostics.test =================================================================== --- clang-tools-extra/trunk/test/clangd/diagnostics.test +++ clang-tools-extra/trunk/test/clangd/diagnostics.test @@ -15,3 +15,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":5,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":5,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/did-change-watch-files.test =================================================================== --- clang-tools-extra/trunk/test/clangd/did-change-watch-files.test +++ clang-tools-extra/trunk/test/clangd/did-change-watch-files.test @@ -59,3 +59,6 @@ Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/extra-flags.test =================================================================== --- clang-tools-extra/trunk/test/clangd/extra-flags.test +++ clang-tools-extra/trunk/test/clangd/extra-flags.test @@ -18,5 +18,9 @@ Content-Length: 44 {"jsonrpc":"2.0","id":5,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":5,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/fixits.test =================================================================== --- clang-tools-extra/trunk/test/clangd/fixits.test +++ clang-tools-extra/trunk/test/clangd/fixits.test @@ -26,3 +26,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":3,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/formatting.test =================================================================== --- clang-tools-extra/trunk/test/clangd/formatting.test +++ clang-tools-extra/trunk/test/clangd/formatting.test @@ -65,3 +65,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":6,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":6,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test =================================================================== --- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test +++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test @@ -20,3 +20,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":3,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/initialize-params.test =================================================================== --- clang-tools-extra/trunk/test/clangd/initialize-params.test +++ clang-tools-extra/trunk/test/clangd/initialize-params.test @@ -20,3 +20,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":3,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/input-mirror.test =================================================================== --- clang-tools-extra/trunk/test/clangd/input-mirror.test +++ clang-tools-extra/trunk/test/clangd/input-mirror.test @@ -152,3 +152,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":3,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/protocol.test =================================================================== --- clang-tools-extra/trunk/test/clangd/protocol.test +++ clang-tools-extra/trunk/test/clangd/protocol.test @@ -1,8 +1,10 @@ -# RUN: clangd -run-synchronously < %s | FileCheck %s -# RUN: clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s +# RUN: not clangd -run-synchronously < %s | FileCheck %s +# RUN: not clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s # vim: fileformat=dos # It is absolutely vital that this file has CRLF line endings. # +# Note that we invert the test because we intent to let clangd exit prematurely. +# # Test protocol parsing Content-Length: 125 Content-Type: application/vscode-jsonrpc; charset-utf-8 Index: clang-tools-extra/trunk/test/clangd/shutdown-with-exit.test =================================================================== --- clang-tools-extra/trunk/test/clangd/shutdown-with-exit.test +++ clang-tools-extra/trunk/test/clangd/shutdown-with-exit.test @@ -0,0 +1,9 @@ +# RUN: clangd -run-synchronously < %s +# vim: fileformat=dos +# It is absolutely vital that this file has CRLF line endings. +Content-Length: 44 + +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/shutdown-without-exit.test =================================================================== --- clang-tools-extra/trunk/test/clangd/shutdown-without-exit.test +++ clang-tools-extra/trunk/test/clangd/shutdown-without-exit.test @@ -0,0 +1,6 @@ +# RUN: not clangd -run-synchronously < %s +# vim: fileformat=dos +# It is absolutely vital that this file has CRLF line endings. +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/signature-help.test =================================================================== --- clang-tools-extra/trunk/test/clangd/signature-help.test +++ clang-tools-extra/trunk/test/clangd/signature-help.test @@ -40,3 +40,7 @@ Content-Length: 49 {"jsonrpc":"2.0","id":100000,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":100000,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"} Index: clang-tools-extra/trunk/test/clangd/unsupported-method.test =================================================================== --- clang-tools-extra/trunk/test/clangd/unsupported-method.test +++ clang-tools-extra/trunk/test/clangd/unsupported-method.test @@ -17,3 +17,7 @@ Content-Length: 44 {"jsonrpc":"2.0","id":2,"method":"shutdown"} +# CHECK: {"jsonrpc":"2.0","id":2,"result":null} +Content-Length: 33 + +{"jsonrpc":"2.0":"method":"exit"}