}
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())
/*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.
// 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<clang::tooling::Replacement>
/// 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.
// 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;
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;
}
};
using ShutdownParams = NoParams;
+using ExitParams = NoParams;
struct InitializeParams {
/// The process Id of the parent process that started
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);
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;
/// 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;
}
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 58\r
\r
{"jsonrpc":"2.0","id":4,"method":"shutdown","params":null}\r
+# CHECK: {"jsonrpc":"2.0","id":4,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":4,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":4,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":4,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":4,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":4,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":4,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 58\r
\r
{"jsonrpc":"2.0","id":2,"method":"shutdown","params":null}\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":5,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":5,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":5,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":5,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
\r
\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":6,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":6,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
-# RUN: clangd -run-synchronously < %s | FileCheck %s\r
-# RUN: clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s\r
+# RUN: not clangd -run-synchronously < %s | FileCheck %s\r
+# RUN: not clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s\r
# vim: fileformat=dos\r
# It is absolutely vital that this file has CRLF line endings.\r
#\r
+# Note that we invert the test because we intent to let clangd exit prematurely.\r
+#\r
# Test protocol parsing\r
Content-Length: 125\r
Content-Type: application/vscode-jsonrpc; charset-utf-8\r
--- /dev/null
+# RUN: clangd -run-synchronously < %s\r
+# vim: fileformat=dos\r
+# It is absolutely vital that this file has CRLF line endings.\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
--- /dev/null
+# RUN: not clangd -run-synchronously < %s\r
+# vim: fileformat=dos\r
+# It is absolutely vital that this file has CRLF line endings.\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 49\r
\r
{"jsonrpc":"2.0","id":100000,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":100000,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r
Content-Length: 44\r
\r
{"jsonrpc":"2.0","id":2,"method":"shutdown"}\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":null}\r
+Content-Length: 33\r
+\r
+{"jsonrpc":"2.0":"method":"exit"}\r