Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -17,6 +17,7 @@ Compiler.cpp Context.cpp Diagnostics.cpp + DispatcherCommon.cpp DraftStore.cpp FindSymbols.cpp FuzzyMatch.cpp @@ -26,7 +27,6 @@ JSONRPCDispatcher.cpp Logger.cpp Protocol.cpp - ProtocolHandlers.cpp Quality.cpp SourceCode.cpp Threading.cpp Index: ClangdLSPServer.h =================================================================== --- ClangdLSPServer.h +++ ClangdLSPServer.h @@ -23,29 +23,22 @@ namespace clang { namespace clangd { -class JSONOutput; +class LSPOutput; class SymbolIndex; /// This class provides implementation of an LSP server, glueing the JSON /// dispatch and ClangdServer together. -class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { +class ClangdLSPServer : private DiagnosticsConsumer, public ProtocolCallbacks { public: /// If \p CompileCommandsDir has a value, compile_commands.json will be /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look /// for compile_commands.json in all parent directories of each file. - ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, + ClangdLSPServer(const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, const ClangdServer::Options &Opts); - /// Run LSP server loop, receiving input for it from \p In. \p In must be - /// 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. - /// - /// \return Whether we received a 'shutdown' request before an 'exit' request. - bool run(std::FILE *In, - JSONStreamStyle InputStyle = JSONStreamStyle::Standard); - + bool& getIsDone(); + bool getShutdownRequestReceived() const; private: // Implement DiagnosticsConsumer. void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override; @@ -82,7 +75,6 @@ /// compilation database is changed. void reparseOpenedFiles(); - JSONOutput &Out; /// Used to indicate that the 'shutdown' request was received from the /// Language Server client. bool ShutdownRequestReceived = false; Index: ClangdLSPServer.cpp =================================================================== --- ClangdLSPServer.cpp +++ ClangdLSPServer.cpp @@ -9,6 +9,7 @@ #include "ClangdLSPServer.h" #include "Diagnostics.h" +#include "DispatcherCommon.h" #include "JSONRPCDispatcher.h" #include "SourceCode.h" #include "URI.h" @@ -394,33 +395,13 @@ } } -ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, - const clangd::CodeCompleteOptions &CCOpts, +ClangdLSPServer::ClangdLSPServer(const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, const ClangdServer::Options &Opts) - : Out(Out), NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB), + : NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), Server(CDB, FSProvider, /*DiagConsumer=*/*this, Opts) {} -bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) { - assert(!IsDone && "Run was called before"); - - // Set up JSONRPCDispatcher. - JSONRPCDispatcher Dispatcher([](const json::Expr &Params) { - replyError(ErrorCode::MethodNotFound, "method not found"); - }); - registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this); - - // Run the Language Server loop. - runLanguageServerLoop(In, Out, InputStyle, Dispatcher, IsDone); - - // 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 ClangdLSPServer::getFixes(StringRef File, const clangd::Diagnostic &D) { std::lock_guard Lock(FixItsMutex); @@ -463,7 +444,7 @@ } // Publish diagnostics. - Out.writeMessage(json::obj{ + sendMessage(json::obj{ {"jsonrpc", "2.0"}, {"method", "textDocument/publishDiagnostics"}, {"params", @@ -479,3 +460,6 @@ Server.addDocument(FilePath, *DraftMgr.getDraft(FilePath), WantDiagnostics::Auto); } + +bool& ClangdLSPServer::getIsDone() { return IsDone; } +bool ClangdLSPServer::getShutdownRequestReceived() const { return ShutdownRequestReceived; } \ No newline at end of file Index: DispatcherCommon.h =================================================================== --- /dev/null +++ DispatcherCommon.h @@ -0,0 +1,62 @@ +//===--- DispatcherCommon.h - Shared code for dispatchers -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DISPATCHERCOMMON_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DISPATCHERCOMMON_H + +#include "JSONRPCDispatcher.h" +#include "JSONExpr.h" +#include "Trace.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/SourceMgr.h" +#include + +namespace clang { +namespace clangd { + +extern Key RequestID; +extern Key RequestOut; + +// When tracing, we trace a request and attach the repsonse in reply(). +// Because the Span isn't available, we find the current request using Context. +class RequestSpan { + RequestSpan(json::obj *Args) : Args(Args) {} + std::mutex Mu; + json::obj *Args; + static Key> RSKey; + +public: + // Return a context that's aware of the enclosing request, identified by Span. + static Context stash(const trace::Span &Span); + + // If there's an enclosing request and the tracer is interested, calls \p F + // with a json::obj where request info can be added. + template static void attach(Func &&F) { + auto *RequestArgs = Context::current().get(RSKey); + if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args) + return; + std::lock_guard Lock((*RequestArgs)->Mu); + F(*(*RequestArgs)->Args); + } +}; + +void sendMessage(json::Expr &&Message); + +void reply(json::Expr &&Result); + +void replyError(ErrorCode code, const llvm::StringRef &Message); + +void call(StringRef Method, json::Expr &&Params); + +} +} + +#endif \ No newline at end of file Index: DispatcherCommon.cpp =================================================================== --- /dev/null +++ DispatcherCommon.cpp @@ -0,0 +1,83 @@ +//===--- DispatcherCommon.h - Shared code for dispatchers -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DispatcherCommon.h" + +namespace clang { +namespace clangd { + +Key RequestID; +Key RequestOut; + +Context RequestSpan::stash(const trace::Span &Span) { + return Context::current().derive( + RSKey, std::unique_ptr(new RequestSpan(Span.Args))); +} + +Key> RequestSpan::RSKey; + +void sendMessage(json::Expr &&Message) { + Context::current() + .getExisting(RequestOut) + ->writeMessage(Message); +} + +void reply(json::Expr &&Result) { + auto ID = Context::current().get(RequestID); + if (!ID) { + log("Attempted to reply to a notification!"); + return; + } + RequestSpan::attach([&](json::obj &Args) { Args["Reply"] = Result; }); + Context::current() + .getExisting(RequestOut) + ->writeMessage(json::obj{ + {"jsonrpc", "2.0"}, + {"id", *ID}, + {"result", std::move(Result)}, + }); +} + +void replyError(ErrorCode code, const llvm::StringRef &Message) { + log("Error " + Twine(static_cast(code)) + ": " + Message); + RequestSpan::attach([&](json::obj &Args) { + Args["Error"] = + json::obj{{"code", static_cast(code)}, {"message", Message.str()}}; + }); + + if (auto ID = Context::current().get(RequestID)) { + Context::current() + .getExisting(RequestOut) + ->writeMessage(json::obj{ + {"jsonrpc", "2.0"}, + {"id", *ID}, + {"error", + json::obj{{"code", static_cast(code)}, {"message", Message}}}, + }); + } +} + +void call(StringRef Method, json::Expr &&Params) { + // FIXME: Generate/Increment IDs for every request so that we can get proper + // replies once we need to. + RequestSpan::attach([&](json::obj &Args) { + Args["Call"] = json::obj{{"method", Method.str()}, {"params", Params}}; + }); + Context::current() + .getExisting(RequestOut) + ->writeMessage(json::obj{ + {"jsonrpc", "2.0"}, + {"id", 1}, + {"method", Method}, + {"params", std::move(Params)}, + }); +} + +} +} Index: JSONRPCDispatcher.h =================================================================== --- JSONRPCDispatcher.h +++ JSONRPCDispatcher.h @@ -1,4 +1,4 @@ -//===--- JSONRPCDispatcher.h - Main JSON parser entry point -----*- C++ -*-===// +//===--- JSONRPCDispatcher.h - Main JSON RPC entry point --------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -12,6 +12,7 @@ #include "JSONExpr.h" #include "Logger.h" +#include "LSPOutput.h" #include "Protocol.h" #include "Trace.h" #include "clang/Basic/LLVM.h" @@ -25,16 +26,14 @@ /// Encapsulates output and logs streams and provides thread-safe access to /// them. -class JSONOutput : public Logger { - // FIXME(ibiryukov): figure out if we can shrink the public interface of - // JSONOutput now that we pass Context everywhere. +class JSONOutput : public LSPOutput { public: JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs, llvm::raw_ostream *InputMirror = nullptr, bool Pretty = false) : Pretty(Pretty), Outs(Outs), Logs(Logs), InputMirror(InputMirror) {} /// Emit a JSONRPC message. - void writeMessage(const json::Expr &Result); + void writeMessage(const json::Expr &Result) override; /// Write a line to the logging stream. void log(const Twine &Message) override; @@ -42,7 +41,7 @@ /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is /// null. /// Unlike other methods of JSONOutput, mirrorInput is not thread-safe. - void mirrorInput(const Twine &Message); + void mirrorInput(const Twine &Message) override; // Whether output should be pretty-printed. const bool Pretty; @@ -81,7 +80,7 @@ void registerHandler(StringRef Method, Handler H); /// Parses a JSONRPC message and calls the Handler for it. - bool call(const json::Expr &Message, JSONOutput &Out) const; + bool call(const json::Expr &Message, LSPOutput &Out) const; private: llvm::StringMap Handlers; @@ -104,7 +103,7 @@ /// replacements of \r\n with \n. /// We use C-style FILE* for reading as std::istream has unclear interaction /// with signals, which are sent by debuggers on some OSs. -void runLanguageServerLoop(std::FILE *In, JSONOutput &Out, +void runJSONRPCServerLoop(std::FILE *In, JSONOutput &Out, JSONStreamStyle InputStyle, JSONRPCDispatcher &Dispatcher, bool &IsDone); Index: JSONRPCDispatcher.cpp =================================================================== --- JSONRPCDispatcher.cpp +++ JSONRPCDispatcher.cpp @@ -1,4 +1,4 @@ -//===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===// +//===--- JSONRPCDispatcher.h - Main JSON RPC entry point --------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,9 +7,9 @@ // //===----------------------------------------------------------------------===// +#include "DispatcherCommon.h" #include "JSONRPCDispatcher.h" #include "JSONExpr.h" -#include "ProtocolHandlers.h" #include "Trace.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" @@ -21,38 +21,6 @@ using namespace clang; using namespace clangd; -namespace { -static Key RequestID; -static Key RequestOut; - -// When tracing, we trace a request and attach the repsonse in reply(). -// Because the Span isn't available, we find the current request using Context. -class RequestSpan { - RequestSpan(json::obj *Args) : Args(Args) {} - std::mutex Mu; - json::obj *Args; - static Key> RSKey; - -public: - // Return a context that's aware of the enclosing request, identified by Span. - static Context stash(const trace::Span &Span) { - return Context::current().derive( - RSKey, std::unique_ptr(new RequestSpan(Span.Args))); - } - - // If there's an enclosing request and the tracer is interested, calls \p F - // with a json::obj where request info can be added. - template static void attach(Func &&F) { - auto *RequestArgs = Context::current().get(RSKey); - if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args) - return; - std::lock_guard Lock((*RequestArgs)->Mu); - F(*(*RequestArgs)->Args); - } -}; -Key> RequestSpan::RSKey; -} // namespace - void JSONOutput::writeMessage(const json::Expr &Message) { std::string S; llvm::raw_string_ostream OS(S); @@ -86,63 +54,12 @@ InputMirror->flush(); } -void clangd::reply(json::Expr &&Result) { - auto ID = Context::current().get(RequestID); - if (!ID) { - log("Attempted to reply to a notification!"); - return; - } - RequestSpan::attach([&](json::obj &Args) { Args["Reply"] = Result; }); - Context::current() - .getExisting(RequestOut) - ->writeMessage(json::obj{ - {"jsonrpc", "2.0"}, - {"id", *ID}, - {"result", std::move(Result)}, - }); -} - -void clangd::replyError(ErrorCode code, const llvm::StringRef &Message) { - log("Error " + Twine(static_cast(code)) + ": " + Message); - RequestSpan::attach([&](json::obj &Args) { - Args["Error"] = - json::obj{{"code", static_cast(code)}, {"message", Message.str()}}; - }); - - if (auto ID = Context::current().get(RequestID)) { - Context::current() - .getExisting(RequestOut) - ->writeMessage(json::obj{ - {"jsonrpc", "2.0"}, - {"id", *ID}, - {"error", - json::obj{{"code", static_cast(code)}, {"message", Message}}}, - }); - } -} - -void clangd::call(StringRef Method, json::Expr &&Params) { - // FIXME: Generate/Increment IDs for every request so that we can get proper - // replies once we need to. - RequestSpan::attach([&](json::obj &Args) { - Args["Call"] = json::obj{{"method", Method.str()}, {"params", Params}}; - }); - Context::current() - .getExisting(RequestOut) - ->writeMessage(json::obj{ - {"jsonrpc", "2.0"}, - {"id", 1}, - {"method", Method}, - {"params", std::move(Params)}, - }); -} - void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) { assert(!Handlers.count(Method) && "Handler already registered!"); Handlers[Method] = std::move(H); } -bool JSONRPCDispatcher::call(const json::Expr &Message, JSONOutput &Out) const { +bool JSONRPCDispatcher::call(const json::Expr &Message, LSPOutput &Out) const { // Message must be an object with "jsonrpc":"2.0". auto *Object = Message.asObject(); if (!Object || Object->getString("jsonrpc") != Optional("2.0")) @@ -208,7 +125,7 @@ // - ferror() or feof() are set. // - Content-Length is missing or empty (protocol error) static llvm::Optional readStandardMessage(std::FILE *In, - JSONOutput &Out) { + LSPOutput &Out) { // A Language Server Protocol message starts with a set of HTTP headers, // delimited by \r\n, and terminated by an empty line (\r\n). unsigned long long ContentLength = 0; @@ -279,7 +196,7 @@ // This is a testing path, so favor simplicity over performance here. // When returning None, feof() or ferror() will be set. static llvm::Optional readDelimitedMessage(std::FILE *In, - JSONOutput &Out) { + LSPOutput &Out) { std::string JSON; std::string Line; while (readLine(In, Line)) { @@ -311,7 +228,7 @@ // sometimes hang rather than exit on other OSes. The interaction between // istreams and signals isn't well-specified, so it's hard to get this right. // The C APIs seem to be clearer in this respect. -void clangd::runLanguageServerLoop(std::FILE *In, JSONOutput &Out, +void clangd::runJSONRPCServerLoop(std::FILE *In, JSONOutput &Out, JSONStreamStyle InputStyle, JSONRPCDispatcher &Dispatcher, bool &IsDone) { Index: LSPOutput.h =================================================================== --- /dev/null +++ LSPOutput.h @@ -0,0 +1,42 @@ +//===--- LSPOutput.h - LSP output interface ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPOUTPUT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPOUTPUT_H + +#include "JSONExpr.h" +#include "Logger.h" +#include "clang/Basic/LLVM.h" + +namespace clang { +namespace clangd { + +/// Encapsulates output and logs streams and provides thread-safe access to +/// them. +class LSPOutput : public Logger { + // FIXME(ibiryukov): figure out if we can shrink the public interface of + // LSPOutput now that we pass Context everywhere. +public: + /// Emit LSP message. + virtual void writeMessage(const json::Expr &Result) = 0; + + /// Write a line to log. + virtual void log(const Twine &Message) = 0; + + /// FIXME: Does it make sense for all implementations? + /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is + /// null. + /// Unlike other methods of LSPOutput, mirrorInput is not thread-safe. + virtual void mirrorInput(const Twine &Message) = 0; +}; + +} // namespace clangd +} // namespace clang + +#endif Index: ProtocolHandlers.h =================================================================== --- ProtocolHandlers.h +++ ProtocolHandlers.h @@ -56,8 +56,63 @@ virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0; }; -void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, - ProtocolCallbacks &Callbacks); +// Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher. +// Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo) +// onFoo should be: void onFoo(Ctx &C, FooParams &Params) +// FooParams should have a fromJSON function. +template +struct HandlerRegisterer { + template + void operator()(StringRef Method, void (ProtocolCallbacks::*Handler)(Param)) { + // Capture pointers by value, as the lambda will outlive this object. + auto *Callbacks = this->Callbacks; + Dispatcher.registerHandler(Method, [=](const json::Expr &RawParams) { + typename std::remove_reference::type P; + if (fromJSON(RawParams, P)) { + (Callbacks->*Handler)(P); + } else { + log("Failed to decode " + Method + " request."); + } + }); + } + + DispatcherType &Dispatcher; + ProtocolCallbacks *Callbacks; +}; + +template +void registerCallbackHandlers(DispatcherType &Dispatcher, + ProtocolCallbacks &Callbacks) { + HandlerRegisterer Register{Dispatcher, &Callbacks}; + + 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); + Register("textDocument/rangeFormatting", + &ProtocolCallbacks::onDocumentRangeFormatting); + Register("textDocument/onTypeFormatting", + &ProtocolCallbacks::onDocumentOnTypeFormatting); + Register("textDocument/formatting", &ProtocolCallbacks::onDocumentFormatting); + Register("textDocument/codeAction", &ProtocolCallbacks::onCodeAction); + Register("textDocument/completion", &ProtocolCallbacks::onCompletion); + Register("textDocument/signatureHelp", &ProtocolCallbacks::onSignatureHelp); + Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition); + Register("textDocument/switchSourceHeader", + &ProtocolCallbacks::onSwitchSourceHeader); + Register("textDocument/rename", &ProtocolCallbacks::onRename); + Register("textDocument/hover", &ProtocolCallbacks::onHover); + Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent); + Register("workspace/executeCommand", &ProtocolCallbacks::onCommand); + Register("textDocument/documentHighlight", + &ProtocolCallbacks::onDocumentHighlight); + Register("workspace/didChangeConfiguration", + &ProtocolCallbacks::onChangeConfiguration); + Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol); +} + } // namespace clangd } // namespace clang Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//===--- ProtocolHandlers.cpp - LSP callbacks -----------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ProtocolHandlers.h" -#include "ClangdLSPServer.h" -#include "ClangdServer.h" -#include "DraftStore.h" -#include "Trace.h" - -using namespace clang; -using namespace clang::clangd; - -namespace { - -// Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher. -// Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo) -// onFoo should be: void onFoo(Ctx &C, FooParams &Params) -// FooParams should have a fromJSON function. -struct HandlerRegisterer { - template - void operator()(StringRef Method, void (ProtocolCallbacks::*Handler)(Param)) { - // Capture pointers by value, as the lambda will outlive this object. - auto *Callbacks = this->Callbacks; - Dispatcher.registerHandler(Method, [=](const json::Expr &RawParams) { - typename std::remove_reference::type P; - if (fromJSON(RawParams, P)) { - (Callbacks->*Handler)(P); - } else { - log("Failed to decode " + Method + " request."); - } - }); - } - - JSONRPCDispatcher &Dispatcher; - ProtocolCallbacks *Callbacks; -}; - -} // namespace - -void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, - ProtocolCallbacks &Callbacks) { - HandlerRegisterer Register{Dispatcher, &Callbacks}; - - 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); - Register("textDocument/rangeFormatting", - &ProtocolCallbacks::onDocumentRangeFormatting); - Register("textDocument/onTypeFormatting", - &ProtocolCallbacks::onDocumentOnTypeFormatting); - Register("textDocument/formatting", &ProtocolCallbacks::onDocumentFormatting); - Register("textDocument/codeAction", &ProtocolCallbacks::onCodeAction); - Register("textDocument/completion", &ProtocolCallbacks::onCompletion); - Register("textDocument/signatureHelp", &ProtocolCallbacks::onSignatureHelp); - Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition); - Register("textDocument/switchSourceHeader", - &ProtocolCallbacks::onSwitchSourceHeader); - Register("textDocument/rename", &ProtocolCallbacks::onRename); - Register("textDocument/hover", &ProtocolCallbacks::onHover); - Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent); - Register("workspace/executeCommand", &ProtocolCallbacks::onCommand); - Register("textDocument/documentHighlight", - &ProtocolCallbacks::onDocumentHighlight); - Register("workspace/didChangeConfiguration", - &ProtocolCallbacks::onChangeConfiguration); - Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol); -} Index: tool/ClangdMain.cpp =================================================================== --- tool/ClangdMain.cpp +++ tool/ClangdMain.cpp @@ -19,7 +19,6 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include -#include #include #include #include @@ -207,12 +206,6 @@ if (Tracer) TracingSession.emplace(*Tracer); - JSONOutput Out(llvm::outs(), llvm::errs(), - InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, - PrettyPrint); - - clangd::LoggingSession LoggingSession(Out); - // If --compile-commands-dir arg was invoked, check value and override default // path. llvm::Optional CompileCommandsDirPath; @@ -252,11 +245,27 @@ CCOpts.Limit = LimitResults; CCOpts.BundleOverloads = CompletionStyle != Detailed; - // Initialize and run ClangdLSPServer. - ClangdLSPServer LSPServer(Out, CCOpts, CompileCommandsDirPath, Opts); - constexpr int NoShutdownRequestErrorCode = 1; + ClangdLSPServer LSPServer(CCOpts, CompileCommandsDirPath, Opts); + llvm::set_thread_name("clangd.main"); // Change stdin to binary to not lose \r\n on windows. llvm::sys::ChangeStdinToBinary(); - return LSPServer.run(stdin, InputStyle) ? 0 : NoShutdownRequestErrorCode; + { + JSONRPCDispatcher Dispatcher([](const json::Expr &Params) { + replyError(ErrorCode::MethodNotFound, "method not found"); + }); + registerCallbackHandlers(Dispatcher, /*Callbacks=*/LSPServer); + + + JSONOutput Out(llvm::outs(), llvm::errs(), + InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, + PrettyPrint); + + clangd::LoggingSession LoggingSession(Out); + + runJSONRPCServerLoop(stdin, Out, InputStyle, Dispatcher, LSPServer.getIsDone()); + } + + constexpr int NoShutdownRequestErrorCode = 1; + return LSPServer.getShutdownRequestReceived() ? 0 : NoShutdownRequestErrorCode; }