Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -25,11 +25,9 @@ FuzzyMatch.cpp GlobalCompilationDatabase.cpp Headers.cpp - JSONRPCDispatcher.cpp JSONTransport.cpp Logger.cpp Protocol.cpp - ProtocolHandlers.cpp Quality.cpp RIFF.cpp SourceCode.cpp Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -16,7 +16,7 @@ #include "GlobalCompilationDatabase.h" #include "Path.h" #include "Protocol.h" -#include "ProtocolHandlers.h" +#include "Transport.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/Optional.h" #include @@ -28,10 +28,10 @@ /// This class exposes ClangdServer's capabilities via Language Server Protocol. /// -/// JSONRPCDispatcher binds the implemented ProtocolCallbacks methods -/// (e.g. onInitialize) to corresponding JSON-RPC methods ("initialize"). -/// The server also supports $/cancelRequest (JSONRPCDispatcher provides this). -class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { +/// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to +/// corresponding JSON-RPC methods ("initialize"). +/// The server also supports $/cancelRequest (MessageHandler provides this). +class ClangdLSPServer : private DiagnosticsConsumer { public: /// If \p CompileCommandsDir has a value, compile_commands.json will be /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look @@ -39,6 +39,7 @@ ClangdLSPServer(Transport &Transp, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts); + ~ClangdLSPServer(); /// Run LSP server loop, communicating with the Transport provided in the /// constructor. This method must not be executed more than once. @@ -50,32 +51,41 @@ // Implement DiagnosticsConsumer. void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override; - // Implement ProtocolCallbacks. - void onInitialize(InitializeParams &Params) override; - void onShutdown(ShutdownParams &Params) override; - void onExit(ExitParams &Params) override; - void onDocumentDidOpen(DidOpenTextDocumentParams &Params) override; - void onDocumentDidChange(DidChangeTextDocumentParams &Params) override; - void onDocumentDidClose(DidCloseTextDocumentParams &Params) override; - void - onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams &Params) override; - void - onDocumentRangeFormatting(DocumentRangeFormattingParams &Params) override; - void onDocumentFormatting(DocumentFormattingParams &Params) override; - void onDocumentSymbol(DocumentSymbolParams &Params) override; - void onCodeAction(CodeActionParams &Params) override; - void onCompletion(TextDocumentPositionParams &Params) override; - void onSignatureHelp(TextDocumentPositionParams &Params) override; - void onGoToDefinition(TextDocumentPositionParams &Params) override; - void onReference(ReferenceParams &Params) override; - void onSwitchSourceHeader(TextDocumentIdentifier &Params) override; - void onDocumentHighlight(TextDocumentPositionParams &Params) override; - void onFileEvent(DidChangeWatchedFilesParams &Params) override; - void onCommand(ExecuteCommandParams &Params) override; - void onWorkspaceSymbol(WorkspaceSymbolParams &Params) override; - void onRename(RenameParams &Parames) override; - void onHover(TextDocumentPositionParams &Params) override; - void onChangeConfiguration(DidChangeConfigurationParams &Params) override; + // LSP methods. Notifications have signature void(const Params&). + // Calls have signature void(const Params&, Callback). + void onInitialize(const InitializeParams &, Callback); + void onShutdown(const ShutdownParams &, Callback); + void onDocumentDidOpen(const DidOpenTextDocumentParams &); + void onDocumentDidChange(const DidChangeTextDocumentParams &); + void onDocumentDidClose(const DidCloseTextDocumentParams &); + void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &, + Callback>); + void onDocumentRangeFormatting(const DocumentRangeFormattingParams &, + Callback>); + void onDocumentFormatting(const DocumentFormattingParams &, + Callback>); + void onDocumentSymbol(const DocumentSymbolParams &, + Callback>); + void onCodeAction(const CodeActionParams &, Callback); + void onCompletion(const TextDocumentPositionParams &, + Callback); + void onSignatureHelp(const TextDocumentPositionParams &, + Callback); + void onGoToDefinition(const TextDocumentPositionParams &, + Callback>); + void onReference(const ReferenceParams &, Callback>); + void onSwitchSourceHeader(const TextDocumentIdentifier &, + Callback); + void onDocumentHighlight(const TextDocumentPositionParams &, + Callback>); + void onFileEvent(const DidChangeWatchedFilesParams &); + void onCommand(const ExecuteCommandParams &, Callback); + void onWorkspaceSymbol(const WorkspaceSymbolParams &, + Callback>); + void onRename(const RenameParams &, Callback); + void onHover(const TextDocumentPositionParams &, + Callback>); + void onChangeConfiguration(const DidChangeConfigurationParams &); std::vector getFixes(StringRef File, const clangd::Diagnostic &D); @@ -143,7 +153,17 @@ bool IsDirectoryBased; }; + // Most code should not deal with Transport directly. + // MessageHandler deals with incoming messages, use call() etc for outgoing. clangd::Transport &Transp; + class MessageHandler; + std::unique_ptr MsgHandler; + std::atomic NextCallID = {0}; + std::mutex TranspWriter; + void call(StringRef Method, llvm::json::Value Params); + void notify(StringRef Method, llvm::json::Value Params); + void reply(llvm::json::Value ID, llvm::Expected Result); + // Various ClangdServer parameters go here. It's important they're created // before ClangdServer. CompilationDB CDB; Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -9,13 +9,14 @@ #include "ClangdLSPServer.h" #include "Diagnostics.h" -#include "JSONRPCDispatcher.h" #include "SourceCode.h" +#include "Trace.h" #include "URI.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" +#include "llvm/Support/ScopedPrinter.h" using namespace clang::clangd; using namespace clang; @@ -80,7 +81,179 @@ } // namespace -void ClangdLSPServer::onInitialize(InitializeParams &Params) { +// MessageHandler dispatches incoming LSP messages. +// It handles cross-cutting concerns: +// - serializes/deserializes protocol objects to JSON +// - logging of inbound messages +// - cancellation handling +// - basic call tracing +class ClangdLSPServer::MessageHandler : public Transport::MessageHandler { +public: + MessageHandler(ClangdLSPServer &Server) : Server(Server) {} + + bool onNotify(StringRef Method, json::Value Params) override { + log("<-- {0}", Method); + if (Method == "exit") + return false; + if (Method == "$/cancelRequest") + onCancel(std::move(Params)); + else if (auto Handler = Notifications.lookup(Method)) + Handler(std::move(Params)); + else + log("unhandled notification {0}", Method); + return true; + } + + bool onCall(StringRef Method, json::Value Params, json::Value ID) override { + log("<-- {0}({1})", Method, ID); + if (auto Handler = Calls.lookup(Method)) + Handler(std::move(Params), std::move(ID)); + else + Server.reply(ID, llvm::make_error("method not found", + ErrorCode::MethodNotFound)); + return true; + } + + bool onReply(json::Value ID, Expected Result) override { + // We ignore replies, just log them. + if (Result) + log("<-- reply({0})", ID); + else + log("<-- reply({0}) error: {1}", ID, llvm::toString(Result.takeError())); + return true; + } + + // Bind an LSP method name to a call. + template + void bind(const char *Method, + void (ClangdLSPServer::*Handler)(const Param &, Callback)) { + Calls[Method] = [Method, Handler, this](json::Value RawParams, + json::Value ID) { + Param P; + if (!fromJSON(RawParams, P)) { + elog("Failed to decode {0} request.", Method); + Server.reply(ID, make_error("failed to decode request", + ErrorCode::InvalidRequest)); + return; + } + trace::Span Tracer(Method); + SPAN_ATTACH(Tracer, "Params", RawParams); + auto *Trace = Tracer.Args; // We attach reply from another thread. + // Calls can be canceled by the client. Add cancellation context. + WithContext WithCancel(cancelableRequestContext(ID)); + // FIXME: this function should assert it's called exactly once. + (Server.*Handler)(P, [this, ID, Trace](llvm::Expected Result) { + if (Result) { + if (Trace) + (*Trace)["Reply"] = *Result; + Server.reply(ID, json::Value(std::move(*Result))); + } else { + auto Err = Result.takeError(); + if (Trace) + (*Trace)["Error"] = llvm::to_string(Err); + Server.reply(ID, std::move(Err)); + } + }); + }; + } + + // Bind an LSP method name to a notification. + template + void bind(const char *Method, + void (ClangdLSPServer::*Handler)(const Param &)) { + Notifications[Method] = [Method, Handler, this](json::Value RawParams) { + Param P; + if (!fromJSON(RawParams, P)) { + elog("Failed to decode {0} request.", Method); + return; + } + trace::Span Tracer(Method); + SPAN_ATTACH(Tracer, "Params", RawParams); + (Server.*Handler)(P); + }; + } + +private: + llvm::StringMap> Notifications; + llvm::StringMap> Calls; + + // Method calls may be cancelled by ID, so keep track of their state. + // This needs a mutex: handlers may finish on a different thread, and that's + // when we clean up entries in the map. + mutable std::mutex RequestCancelersMutex; + llvm::StringMap> RequestCancelers; + unsigned NextRequestCookie = 0; + void onCancel(const llvm::json::Value &Params) { + const json::Value *ID = nullptr; + if (auto *O = Params.getAsObject()) + ID = O->get("id"); + if (!ID) { + elog("Bad cancellation request: {0}", Params); + return; + } + auto StrID = llvm::to_string(*ID); + std::lock_guard Lock(RequestCancelersMutex); + auto It = RequestCancelers.find(StrID); + if (It != RequestCancelers.end()) + It->second.first(); // Invoke the canceler. + } + // We run cancelable requests in a context that does two things: + // - allows cancellation using RequestCancelers[ID] + // - cleans up the entry in RequestCancelers when it's no longer needed + // If a client reuses an ID, the last wins and the first cannot be canceled. + Context cancelableRequestContext(const json::Value &ID) { + auto Task = cancelableTask(); + auto StrID = llvm::to_string(ID); // JSON-serialize ID for map key. + auto Cookie = NextRequestCookie++; // No lock, only called on main thread. + { + std::lock_guard Lock(RequestCancelersMutex); + RequestCancelers[StrID] = {std::move(Task.second), Cookie}; + } + // When the request ends, we can clean up the entry we just added. + // The cookie lets us check that it hasn't been overwritten due to ID + // reuse. + return Task.first.derive(make_scope_exit([this, StrID, Cookie] { + std::lock_guard Lock(RequestCancelersMutex); + auto It = RequestCancelers.find(StrID); + if (It != RequestCancelers.end() && It->second.second == Cookie) + RequestCancelers.erase(It); + })); + } + + ClangdLSPServer &Server; +}; + +// call(), notify(), and reply() wrap the Transport, adding logging and locking. +void ClangdLSPServer::call(StringRef Method, json::Value Params) { + auto ID = NextCallID++; + 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::notify(StringRef Method, json::Value Params) { + log("--> {0}", Method); + std::lock_guard Lock(TranspWriter); + Transp.notify(Method, std::move(Params)); +} + +void ClangdLSPServer::reply(llvm::json::Value ID, + llvm::Expected Result) { + if (Result) { + log("--> reply({0})", ID); + std::lock_guard Lock(TranspWriter); + Transp.reply(std::move(ID), std::move(Result)); + } else { + Error Err = Result.takeError(); + log("--> reply({0}) error: {1}", ID, Err); + std::lock_guard Lock(TranspWriter); + Transp.reply(std::move(ID), std::move(Err)); + } +} + +void ClangdLSPServer::onInitialize(const InitializeParams &Params, + Callback Reply) { if (Params.initializationOptions) { const ClangdInitializationOptions &Opts = *Params.initializationOptions; @@ -106,7 +279,7 @@ SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds; SupportsCodeAction = Params.capabilities.CodeActionStructure; - reply(json::Object{ + Reply(json::Object{ {{"capabilities", json::Object{ {"textDocumentSync", (int)TextDocumentSyncKind::Incremental}, @@ -141,29 +314,27 @@ }}}}); } -void ClangdLSPServer::onShutdown(ShutdownParams &Params) { +void ClangdLSPServer::onShutdown(const ShutdownParams &Params, + Callback Reply) { // Do essentially nothing, just say we're ready to exit. ShutdownRequestReceived = true; - reply(nullptr); + Reply(nullptr); } -void ClangdLSPServer::onExit(ExitParams &Params) { - // No work to do. - // JSONRPCDispatcher shuts down the transport after this notification. -} - -void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) { +void ClangdLSPServer::onDocumentDidOpen( + const DidOpenTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); if (Params.metadata && !Params.metadata->extraFlags.empty()) CDB.setExtraFlagsForFile(File, std::move(Params.metadata->extraFlags)); - std::string &Contents = Params.textDocument.text; + const std::string &Contents = Params.textDocument.text; DraftMgr.addDraft(File, Contents); Server->addDocument(File, Contents, WantDiagnostics::Yes); } -void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams &Params) { +void ClangdLSPServer::onDocumentDidChange( + const DidChangeTextDocumentParams &Params) { auto WantDiags = WantDiagnostics::Auto; if (Params.wantDiagnostics.hasValue()) WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes @@ -186,15 +357,15 @@ Server->addDocument(File, *Contents, WantDiags); } -void ClangdLSPServer::onFileEvent(DidChangeWatchedFilesParams &Params) { +void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { Server->onFileEvent(Params); } -void ClangdLSPServer::onCommand(ExecuteCommandParams &Params) { - auto ApplyEdit = [](WorkspaceEdit WE) { +void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params, + Callback Reply) { + auto ApplyEdit = [&](WorkspaceEdit WE) { ApplyWorkspaceEditParams Edit; Edit.edit = std::move(WE); - // We don't need the response so id == 1 is OK. // 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); @@ -210,59 +381,67 @@ // 6. The editor applies the changes (applyEdit), and sends us a reply (but // we ignore it) - reply("Fix applied."); + Reply("Fix applied."); ApplyEdit(*Params.workspaceEdit); } else { // We should not get here because ExecuteCommandParams would not have // parsed in the first place and this handler should not be called. But if // more commands are added, this will be here has a safe guard. - replyError( - ErrorCode::InvalidParams, - llvm::formatv("Unsupported command \"{0}\".", Params.command).str()); + Reply(make_error( + llvm::formatv("Unsupported command \"{0}\".", Params.command).str(), + ErrorCode::InvalidParams)); } } -void ClangdLSPServer::onWorkspaceSymbol(WorkspaceSymbolParams &Params) { +void ClangdLSPServer::onWorkspaceSymbol( + const WorkspaceSymbolParams &Params, + Callback> Reply) { Server->workspaceSymbols( Params.query, CCOpts.Limit, - [this](llvm::Expected> Items) { - if (!Items) - return replyError(ErrorCode::InternalError, - llvm::toString(Items.takeError())); - for (auto &Sym : *Items) - Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); - - reply(json::Array(*Items)); - }); + Bind( + [this](decltype(Reply) Reply, + llvm::Expected> Items) { + if (!Items) + return Reply(Items.takeError()); + for (auto &Sym : *Items) + Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); + + Reply(std::move(*Items)); + }, + std::move(Reply))); } -void ClangdLSPServer::onRename(RenameParams &Params) { +void ClangdLSPServer::onRename(const RenameParams &Params, + Callback Reply) { Path File = Params.textDocument.uri.file(); llvm::Optional Code = DraftMgr.getDraft(File); if (!Code) - return replyError(ErrorCode::InvalidParams, - "onRename called for non-added file"); + return Reply(make_error("onRename called for non-added file", + ErrorCode::InvalidParams)); Server->rename( File, Params.position, Params.newName, - [File, Code, - Params](llvm::Expected> Replacements) { - if (!Replacements) - return replyError(ErrorCode::InternalError, - llvm::toString(Replacements.takeError())); - - // Turn the replacements into the format specified by the Language - // Server Protocol. Fuse them into one big JSON array. - std::vector Edits; - for (const auto &R : *Replacements) - Edits.push_back(replacementToEdit(*Code, R)); - WorkspaceEdit WE; - WE.changes = {{Params.textDocument.uri.uri(), Edits}}; - reply(WE); - }); -} - -void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams &Params) { + Bind( + [File, Code, Params]( + decltype(Reply) Reply, + llvm::Expected> Replacements) { + if (!Replacements) + return Reply(Replacements.takeError()); + + // Turn the replacements into the format specified by the Language + // Server Protocol. Fuse them into one big JSON array. + std::vector Edits; + for (const auto &R : *Replacements) + Edits.push_back(replacementToEdit(*Code, R)); + WorkspaceEdit WE; + WE.changes = {{Params.textDocument.uri.uri(), Edits}}; + Reply(WE); + }, + std::move(Reply))); +} + +void ClangdLSPServer::onDocumentDidClose( + const DidCloseTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); DraftMgr.removeDraft(File); Server->removeDocument(File); @@ -270,63 +449,71 @@ } void ClangdLSPServer::onDocumentOnTypeFormatting( - DocumentOnTypeFormattingParams &Params) { + const DocumentOnTypeFormattingParams &Params, + Callback> Reply) { auto File = Params.textDocument.uri.file(); auto Code = DraftMgr.getDraft(File); if (!Code) - return replyError(ErrorCode::InvalidParams, - "onDocumentOnTypeFormatting called for non-added file"); + return Reply(make_error( + "onDocumentOnTypeFormatting called for non-added file", + ErrorCode::InvalidParams)); auto ReplacementsOrError = Server->formatOnType(*Code, File, Params.position); if (ReplacementsOrError) - reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); + Reply(replacementsToEdits(*Code, ReplacementsOrError.get())); else - replyError(ErrorCode::UnknownErrorCode, - llvm::toString(ReplacementsOrError.takeError())); + Reply(ReplacementsOrError.takeError()); } void ClangdLSPServer::onDocumentRangeFormatting( - DocumentRangeFormattingParams &Params) { + const DocumentRangeFormattingParams &Params, + Callback> Reply) { auto File = Params.textDocument.uri.file(); auto Code = DraftMgr.getDraft(File); if (!Code) - return replyError(ErrorCode::InvalidParams, - "onDocumentRangeFormatting called for non-added file"); + return Reply(make_error( + "onDocumentRangeFormatting called for non-added file", + ErrorCode::InvalidParams)); auto ReplacementsOrError = Server->formatRange(*Code, File, Params.range); if (ReplacementsOrError) - reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); + Reply(replacementsToEdits(*Code, ReplacementsOrError.get())); else - replyError(ErrorCode::UnknownErrorCode, - llvm::toString(ReplacementsOrError.takeError())); + Reply(ReplacementsOrError.takeError()); } -void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams &Params) { +void ClangdLSPServer::onDocumentFormatting( + const DocumentFormattingParams &Params, + Callback> Reply) { auto File = Params.textDocument.uri.file(); auto Code = DraftMgr.getDraft(File); if (!Code) - return replyError(ErrorCode::InvalidParams, - "onDocumentFormatting called for non-added file"); + return Reply( + make_error("onDocumentFormatting called for non-added file", + ErrorCode::InvalidParams)); auto ReplacementsOrError = Server->formatFile(*Code, File); if (ReplacementsOrError) - reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); + Reply(replacementsToEdits(*Code, ReplacementsOrError.get())); else - replyError(ErrorCode::UnknownErrorCode, - llvm::toString(ReplacementsOrError.takeError())); + Reply(ReplacementsOrError.takeError()); } -void ClangdLSPServer::onDocumentSymbol(DocumentSymbolParams &Params) { +void ClangdLSPServer::onDocumentSymbol( + const DocumentSymbolParams &Params, + Callback> Reply) { Server->documentSymbols( Params.textDocument.uri.file(), - [this](llvm::Expected> Items) { - if (!Items) - return replyError(ErrorCode::InvalidParams, - llvm::toString(Items.takeError())); - for (auto &Sym : *Items) - Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); - reply(json::Array(*Items)); - }); + Bind( + [this](decltype(Reply) Reply, + llvm::Expected> Items) { + if (!Items) + return Reply(Items.takeError()); + for (auto &Sym : *Items) + Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); + Reply(std::move(*Items)); + }, + std::move(Reply))); } static Optional asCommand(const CodeAction &Action) { @@ -347,14 +534,15 @@ return Cmd; } -void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { +void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, + Callback Reply) { + auto Code = DraftMgr.getDraft(Params.textDocument.uri.file()); + if (!Code) + return Reply(make_error("onCodeAction called for non-added file", + ErrorCode::InvalidParams)); // We provide a code action for Fixes on the specified diagnostics. - if (!DraftMgr.getDraft(Params.textDocument.uri.file())) - return replyError(ErrorCode::InvalidParams, - "onCodeAction called for non-added file"); - std::vector Actions; - for (Diagnostic &D : Params.context.diagnostics) { + for (const Diagnostic &D : Params.context.diagnostics) { for (auto &F : getFixes(Params.textDocument.uri.file(), D)) { Actions.emplace_back(); Actions.back().title = F.Message; @@ -368,82 +556,66 @@ } if (SupportsCodeAction) - reply(json::Array(Actions)); + Reply(json::Array(Actions)); else { std::vector Commands; for (const auto &Action : Actions) if (auto Command = asCommand(Action)) Commands.push_back(std::move(*Command)); - reply(json::Array(Commands)); + Reply(json::Array(Commands)); } } -void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { +void ClangdLSPServer::onCompletion(const TextDocumentPositionParams &Params, + Callback Reply) { Server->codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, - [this](llvm::Expected List) { - if (!List) - return replyError(List.takeError()); - CompletionList LSPList; - LSPList.isIncomplete = List->HasMore; - for (const auto &R : List->Completions) { - CompletionItem C = R.render(CCOpts); - C.kind = adjustKindToCapability( - C.kind, SupportedCompletionItemKinds); - LSPList.items.push_back(std::move(C)); - } - return reply(std::move(LSPList)); - }); -} - -void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) { + Bind( + [this](decltype(Reply) Reply, + llvm::Expected List) { + if (!List) + return Reply(List.takeError()); + CompletionList LSPList; + LSPList.isIncomplete = List->HasMore; + for (const auto &R : List->Completions) { + CompletionItem C = R.render(CCOpts); + C.kind = adjustKindToCapability( + C.kind, SupportedCompletionItemKinds); + LSPList.items.push_back(std::move(C)); + } + return Reply(std::move(LSPList)); + }, + std::move(Reply))); +} + +void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params, + Callback Reply) { Server->signatureHelp(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected SignatureHelp) { - if (!SignatureHelp) - return replyError( - ErrorCode::InvalidParams, - llvm::toString(SignatureHelp.takeError())); - reply(*SignatureHelp); - }); + std::move(Reply)); } -void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams &Params) { +void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams &Params, + Callback> Reply) { Server->findDefinitions(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> Items) { - if (!Items) - return replyError( - ErrorCode::InvalidParams, - llvm::toString(Items.takeError())); - reply(json::Array(*Items)); - }); + std::move(Reply)); } -void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier &Params) { +void ClangdLSPServer::onSwitchSourceHeader(const TextDocumentIdentifier &Params, + Callback Reply) { llvm::Optional Result = Server->switchSourceHeader(Params.uri.file()); - reply(Result ? URI::createFile(*Result).toString() : ""); + Reply(Result ? URI::createFile(*Result).toString() : ""); } -void ClangdLSPServer::onDocumentHighlight(TextDocumentPositionParams &Params) { - Server->findDocumentHighlights( - Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> Highlights) { - if (!Highlights) - return replyError(ErrorCode::InternalError, - llvm::toString(Highlights.takeError())); - reply(json::Array(*Highlights)); - }); +void ClangdLSPServer::onDocumentHighlight( + const TextDocumentPositionParams &Params, + Callback> Reply) { + Server->findDocumentHighlights(Params.textDocument.uri.file(), + Params.position, std::move(Reply)); } -void ClangdLSPServer::onHover(TextDocumentPositionParams &Params) { +void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, + Callback> Reply) { Server->findHover(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> H) { - if (!H) { - replyError(ErrorCode::InternalError, - llvm::toString(H.takeError())); - return; - } - - reply(*H); - }); + std::move(Reply)); } void ClangdLSPServer::applyConfiguration( @@ -470,19 +642,14 @@ // FIXME: This function needs to be properly tested. void ClangdLSPServer::onChangeConfiguration( - DidChangeConfigurationParams &Params) { + const DidChangeConfigurationParams &Params) { applyConfiguration(Params.settings); } -void ClangdLSPServer::onReference(ReferenceParams &Params) { +void ClangdLSPServer::onReference(const ReferenceParams &Params, + Callback> Reply) { Server->findReferences(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> Locations) { - if (!Locations) - return replyError( - ErrorCode::InternalError, - llvm::toString(Locations.takeError())); - reply(llvm::json::Array(*Locations)); - }); + std::move(Reply)); } ClangdLSPServer::ClangdLSPServer(class Transport &Transp, @@ -490,28 +657,48 @@ llvm::Optional CompileCommandsDir, bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts) - : Transp(Transp), + : Transp(Transp), MsgHandler(new MessageHandler(*this)), CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory() : CompilationDB::makeDirectoryBased( std::move(CompileCommandsDir))), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), SupportedCompletionItemKinds(defaultCompletionItemKinds()), Server(new ClangdServer(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, - Opts)) {} + Opts)) { + // clang-format off + MsgHandler->bind("initialize", &ClangdLSPServer::onInitialize); + MsgHandler->bind("shutdown", &ClangdLSPServer::onShutdown); + MsgHandler->bind("textDocument/rangeFormatting", &ClangdLSPServer::onDocumentRangeFormatting); + MsgHandler->bind("textDocument/onTypeFormatting", &ClangdLSPServer::onDocumentOnTypeFormatting); + MsgHandler->bind("textDocument/formatting", &ClangdLSPServer::onDocumentFormatting); + MsgHandler->bind("textDocument/codeAction", &ClangdLSPServer::onCodeAction); + MsgHandler->bind("textDocument/completion", &ClangdLSPServer::onCompletion); + MsgHandler->bind("textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp); + MsgHandler->bind("textDocument/definition", &ClangdLSPServer::onGoToDefinition); + MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference); + MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader); + MsgHandler->bind("textDocument/rename", &ClangdLSPServer::onRename); + MsgHandler->bind("textDocument/hover", &ClangdLSPServer::onHover); + MsgHandler->bind("textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol); + MsgHandler->bind("workspace/executeCommand", &ClangdLSPServer::onCommand); + MsgHandler->bind("textDocument/documentHighlight", &ClangdLSPServer::onDocumentHighlight); + MsgHandler->bind("workspace/symbol", &ClangdLSPServer::onWorkspaceSymbol); + MsgHandler->bind("textDocument/didOpen", &ClangdLSPServer::onDocumentDidOpen); + MsgHandler->bind("textDocument/didClose", &ClangdLSPServer::onDocumentDidClose); + MsgHandler->bind("textDocument/didChange", &ClangdLSPServer::onDocumentDidChange); + MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent); + MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration); + // clang-format on +} + +ClangdLSPServer::~ClangdLSPServer() = default; bool ClangdLSPServer::run() { assert(Server); - // Set up JSONRPCDispatcher. - JSONRPCDispatcher Dispatcher([](const json::Value &Params) { - replyError(ErrorCode::MethodNotFound, "method not found"); - return true; - }); - registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this); - // Run the Language Server loop. bool CleanExit = true; - if (auto Err = Dispatcher.runLanguageServerLoop(Transp)) { + if (auto Err = Transp.loop(*MsgHandler)) { elog("Transport error: {0}", std::move(Err)); CleanExit = false; } @@ -579,11 +766,11 @@ } // Publish diagnostics. - Transp.notify("textDocument/publishDiagnostics", - json::Object{ - {"uri", URIForFile{File}}, - {"diagnostics", std::move(DiagnosticsJSON)}, - }); + notify("textDocument/publishDiagnostics", + json::Object{ + {"uri", URIForFile{File}}, + {"diagnostics", std::move(DiagnosticsJSON)}, + }); } void ClangdLSPServer::reparseOpenedFiles() { Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ /dev/null @@ -1,95 +0,0 @@ -//===--- JSONRPCDispatcher.h - Main JSON parser entry point -----*- 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_JSONRPCDISPATCHER_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H - -#include "Cancellation.h" -#include "Logger.h" -#include "Protocol.h" -#include "Trace.h" -#include "Transport.h" -#include "clang/Basic/LLVM.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/Support/JSON.h" -#include -#include - -namespace clang { -namespace clangd { - -/// Sends a successful reply. -/// Current context must derive from JSONRPCDispatcher::Handler. -void reply(llvm::json::Value &&Result); -/// Sends an error response to the client, and logs it. -/// Current context must derive from JSONRPCDispatcher::Handler. -void replyError(ErrorCode Code, const llvm::StringRef &Message); -/// Implements ErrorCode and message extraction from a given llvm::Error. It -/// fetches the related message from error's message method. If error doesn't -/// match any known errors, uses ErrorCode::InvalidParams for the error. -void replyError(llvm::Error E); -/// Sends a request to the client. -/// Current context must derive from JSONRPCDispatcher::Handler. -void call(llvm::StringRef Method, llvm::json::Value &&Params); - -/// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the -/// registered Handler for the method received. -/// -/// The `$/cancelRequest` notification is handled by the dispatcher itself. -/// It marks the matching request as cancelled, if it's still running. -class JSONRPCDispatcher : private Transport::MessageHandler { -public: - /// A handler responds to requests for a particular method name. - /// It returns false if the server should now shut down. - /// - /// JSONRPCDispatcher will mark the handler's context as cancelled if a - /// matching cancellation request is received. Handlers are encouraged to - /// check for cancellation and fail quickly in this case. - using Handler = std::function; - - /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown - /// method is received. - JSONRPCDispatcher(Handler UnknownHandler); - - /// Registers a Handler for the specified Method. - void registerHandler(StringRef Method, Handler H); - - /// Parses input queries from LSP client (coming from \p In) and runs call - /// method for each query. - /// - /// Input stream(\p In) must be opened in binary mode to avoid - /// preliminary 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. - llvm::Error runLanguageServerLoop(Transport &); - -private: - bool onReply(llvm::json::Value ID, - llvm::Expected Result) override; - bool onNotify(llvm::StringRef Method, llvm::json::Value Message) override; - bool onCall(llvm::StringRef Method, llvm::json::Value Message, - llvm::json::Value ID) override; - - // Tracking cancellations needs a mutex: handlers may finish on a different - // thread, and that's when we clean up entries in the map. - mutable std::mutex RequestCancelersMutex; - llvm::StringMap> RequestCancelers; - unsigned NextRequestCookie = 0; - Context cancelableRequestContext(const llvm::json::Value &ID); - void cancelRequest(const llvm::json::Value &ID); - - llvm::StringMap Handlers; - Handler UnknownHandler; -}; - -} // namespace clangd -} // namespace clang - -#endif Index: clangd/JSONRPCDispatcher.cpp =================================================================== --- clangd/JSONRPCDispatcher.cpp +++ /dev/null @@ -1,208 +0,0 @@ -//===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "JSONRPCDispatcher.h" -#include "Cancellation.h" -#include "ProtocolHandlers.h" -#include "Trace.h" -#include "Transport.h" -#include "llvm/ADT/ScopeExit.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Errno.h" -#include "llvm/Support/JSON.h" -#include "llvm/Support/ScopedPrinter.h" -#include "llvm/Support/SourceMgr.h" -#include - -using namespace llvm; -using namespace clang; -using namespace clangd; - -namespace { -static Key RequestID; -static Key CurrentTransport; - -// When tracing, we trace a request and attach the response in reply(). -// Because the Span isn't available, we find the current request using Context. -class RequestSpan { - RequestSpan(llvm::json::Object *Args) : Args(Args) {} - std::mutex Mu; - llvm::json::Object *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::Object 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 clangd::reply(json::Value &&Result) { - auto ID = Context::current().get(RequestID); - if (!ID) { - elog("Attempted to reply to a notification!"); - return; - } - RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; }); - log("--> reply({0})", *ID); - Context::current() - .getExisting(CurrentTransport) - ->reply(std::move(*ID), std::move(Result)); -} - -void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) { - elog("Error {0}: {1}", static_cast(Code), Message); - RequestSpan::attach([&](json::Object &Args) { - Args["Error"] = json::Object{{"code", static_cast(Code)}, - {"message", Message.str()}}; - }); - - if (auto ID = Context::current().get(RequestID)) { - log("--> reply({0}) error: {1}", *ID, Message); - Context::current() - .getExisting(CurrentTransport) - ->reply(std::move(*ID), make_error(Message, Code)); - } -} - -void clangd::replyError(Error E) { - handleAllErrors(std::move(E), - [](const CancelledError &TCE) { - replyError(ErrorCode::RequestCancelled, TCE.message()); - }, - [](const ErrorInfoBase &EIB) { - replyError(ErrorCode::InvalidParams, EIB.message()); - }); -} - -void clangd::call(StringRef Method, json::Value &&Params) { - RequestSpan::attach([&](json::Object &Args) { - Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}}; - }); - // FIXME: Generate/Increment IDs for every request so that we can get proper - // replies once we need to. - auto ID = 1; - log("--> {0}({1})", Method, ID); - Context::current() - .getExisting(CurrentTransport) - ->call(Method, std::move(Params), ID); -} - -JSONRPCDispatcher::JSONRPCDispatcher(Handler UnknownHandler) - : UnknownHandler(std::move(UnknownHandler)) { - registerHandler("$/cancelRequest", [this](const json::Value &Params) { - if (auto *O = Params.getAsObject()) - if (auto *ID = O->get("id")) { - cancelRequest(*ID); - return true; - } - log("Bad cancellation request: {0}", Params); - return true; - }); -} - -void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) { - assert(!Handlers.count(Method) && "Handler already registered!"); - Handlers[Method] = std::move(H); -} - -bool JSONRPCDispatcher::onCall(StringRef Method, json::Value Params, - json::Value ID) { - log("<-- {0}({1})", Method, ID); - auto I = Handlers.find(Method); - auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; - - // Create a Context that contains request information. - WithContextValue WithID(RequestID, ID); - - // Create a tracing Span covering the whole request lifetime. - trace::Span Tracer(Method); - SPAN_ATTACH(Tracer, "ID", ID); - SPAN_ATTACH(Tracer, "Params", Params); - - // Calls can be canceled by the client. Add cancellation context. - WithContext WithCancel(cancelableRequestContext(ID)); - - // Stash a reference to the span args, so later calls can add metadata. - WithContext WithRequestSpan(RequestSpan::stash(Tracer)); - return Handler(std::move(Params)); -} - -bool JSONRPCDispatcher::onNotify(StringRef Method, json::Value Params) { - log("<-- {0}", Method); - auto I = Handlers.find(Method); - auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; - - // Create a tracing Span covering the whole request lifetime. - trace::Span Tracer(Method); - SPAN_ATTACH(Tracer, "Params", Params); - - // Stash a reference to the span args, so later calls can add metadata. - WithContext WithRequestSpan(RequestSpan::stash(Tracer)); - return Handler(std::move(Params)); -} - -bool JSONRPCDispatcher::onReply(json::Value ID, Expected Result) { - // We ignore replies, just log them. - if (Result) - log("<-- reply({0})", ID); - else - log("<-- reply({0}) error: {1}", ID, llvm::toString(Result.takeError())); - return true; -} - -// We run cancelable requests in a context that does two things: -// - allows cancellation using RequestCancelers[ID] -// - cleans up the entry in RequestCancelers when it's no longer needed -// If a client reuses an ID, the last one wins and the first cannot be canceled. -Context JSONRPCDispatcher::cancelableRequestContext(const json::Value &ID) { - auto Task = cancelableTask(); - auto StrID = llvm::to_string(ID); // JSON-serialize ID for map key. - auto Cookie = NextRequestCookie++; // No lock, only called on main thread. - { - std::lock_guard Lock(RequestCancelersMutex); - RequestCancelers[StrID] = {std::move(Task.second), Cookie}; - } - // When the request ends, we can clean up the entry we just added. - // The cookie lets us check that it hasn't been overwritten due to ID reuse. - return Task.first.derive(make_scope_exit([this, StrID, Cookie] { - std::lock_guard Lock(RequestCancelersMutex); - auto It = RequestCancelers.find(StrID); - if (It != RequestCancelers.end() && It->second.second == Cookie) - RequestCancelers.erase(It); - })); -} - -void JSONRPCDispatcher::cancelRequest(const json::Value &ID) { - auto StrID = llvm::to_string(ID); - std::lock_guard Lock(RequestCancelersMutex); - auto It = RequestCancelers.find(StrID); - if (It != RequestCancelers.end()) - It->second.first(); // Invoke the canceler. -} - -llvm::Error JSONRPCDispatcher::runLanguageServerLoop(Transport &Transport) { - // Propagate transport to all handlers so they can reply. - WithContextValue WithTransport(CurrentTransport, &Transport); - return Transport.loop(*this); -} Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ /dev/null @@ -1,67 +0,0 @@ -//===--- ProtocolHandlers.h - LSP callbacks ---------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// ProtocolHandlers translates incoming JSON requests from JSONRPCDispatcher -// into method calls on ClangLSPServer. -// -// Currently it parses requests into objects, but the ClangLSPServer is -// responsible for producing JSON responses. We should move that here, too. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOLHANDLERS_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOLHANDLERS_H - -#include "JSONRPCDispatcher.h" -#include "Protocol.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -namespace clangd { - -// The interface implemented by ClangLSPServer to handle incoming requests. -class ProtocolCallbacks { -public: - virtual ~ProtocolCallbacks() = default; - - virtual void onInitialize(InitializeParams &Params) = 0; - virtual void onShutdown(ShutdownParams &Params) = 0; - virtual void onExit(ExitParams &Params) = 0; - virtual void onDocumentDidOpen(DidOpenTextDocumentParams &Params) = 0; - virtual void onDocumentDidChange(DidChangeTextDocumentParams &Params) = 0; - virtual void onDocumentDidClose(DidCloseTextDocumentParams &Params) = 0; - virtual void onDocumentFormatting(DocumentFormattingParams &Params) = 0; - virtual void onDocumentSymbol(DocumentSymbolParams &Params) = 0; - virtual void - onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams &Params) = 0; - virtual void - onDocumentRangeFormatting(DocumentRangeFormattingParams &Params) = 0; - virtual void onCodeAction(CodeActionParams &Params) = 0; - virtual void onCompletion(TextDocumentPositionParams &Params) = 0; - virtual void onSignatureHelp(TextDocumentPositionParams &Params) = 0; - virtual void onGoToDefinition(TextDocumentPositionParams &Params) = 0; - virtual void onReference(ReferenceParams &Params) = 0; - virtual void onSwitchSourceHeader(TextDocumentIdentifier &Params) = 0; - virtual void onFileEvent(DidChangeWatchedFilesParams &Params) = 0; - virtual void onCommand(ExecuteCommandParams &Params) = 0; - virtual void onWorkspaceSymbol(WorkspaceSymbolParams &Params) = 0; - virtual void onRename(RenameParams &Parames) = 0; - virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0; - virtual void onHover(TextDocumentPositionParams &Params) = 0; - virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0; -}; - -void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, - ProtocolCallbacks &Callbacks); - -} // namespace clangd -} // namespace clang - -#endif Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ /dev/null @@ -1,80 +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; -using namespace llvm; - -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::Value &RawParams) { - typename std::remove_reference::type P; - if (fromJSON(RawParams, P)) { - (Callbacks->*Handler)(P); - } else { - elog("Failed to decode {0} request.", Method); - } - return Method != "exit"; // Shut down after exit notification. - }); - } - - 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/references", &ProtocolCallbacks::onReference); - Register("textDocument/switchSourceHeader", - &ProtocolCallbacks::onSwitchSourceHeader); - Register("textDocument/rename", &ProtocolCallbacks::onRename); - Register("textDocument/hover", &ProtocolCallbacks::onHover); - Register("textDocument/documentSymbol", &ProtocolCallbacks::onDocumentSymbol); - 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: clangd/TUScheduler.cpp =================================================================== --- clangd/TUScheduler.cpp +++ clangd/TUScheduler.cpp @@ -728,9 +728,8 @@ llvm::unique_function)> Action) { auto It = Files.find(File); if (It == Files.end()) { - Action(llvm::make_error( - "trying to get AST for non-added document", - llvm::errc::invalid_argument)); + Action(llvm::make_error( + "trying to get AST for non-added document", ErrorCode::InvalidParams)); return; } @@ -742,9 +741,9 @@ llvm::unique_function)> Action) { auto It = Files.find(File); if (It == Files.end()) { - Action(llvm::make_error( + Action(llvm::make_error( "trying to get preamble for non-added document", - llvm::errc::invalid_argument)); + ErrorCode::InvalidParams)); return; } Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -8,10 +8,9 @@ //===----------------------------------------------------------------------===// #include "ClangdLSPServer.h" -#include "JSONRPCDispatcher.h" #include "Path.h" -#include "RIFF.h" #include "Trace.h" +#include "Transport.h" #include "index/Serialization.h" #include "clang/Basic/Version.h" #include "llvm/Support/CommandLine.h" Index: test/clangd/crash-non-added-files.test =================================================================== --- test/clangd/crash-non-added-files.test +++ test/clangd/crash-non-added-files.test @@ -32,5 +32,3 @@ {"jsonrpc":"2.0","id":6,"method":"shutdown"} --- {"jsonrpc":"2.0","method":"exit"} ---- -{"jsonrpc":"2.0","method":"exit"} Index: test/clangd/delimited-input-comment-at-the-end.test =================================================================== --- test/clangd/delimited-input-comment-at-the-end.test +++ test/clangd/delimited-input-comment-at-the-end.test @@ -8,5 +8,4 @@ --- {"jsonrpc":"2.0","id":3,"method":"shutdown"} --- -{"jsonrpc":"2.0","id":3,"method":"exit"} -# comment at the end +{"jsonrpc":"2.0","method":"exit"} Index: test/clangd/fixits-command.test =================================================================== --- test/clangd/fixits-command.test +++ test/clangd/fixits-command.test @@ -167,7 +167,7 @@ # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "result": "Fix applied." # -# CHECK: "id": 1, +# CHECK: "id": 0, # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "method": "workspace/applyEdit", # CHECK-NEXT: "params": { Index: test/clangd/rename.test =================================================================== --- test/clangd/rename.test +++ test/clangd/rename.test @@ -28,7 +28,7 @@ --- {"jsonrpc":"2.0","id":2,"method":"textDocument/rename","params":{"textDocument":{"uri":"test:///foo.cpp"},"position":{"line":0,"character":2},"newName":"bar"}} # CHECK: "error": { -# CHECK-NEXT: "code": -32603, +# CHECK-NEXT: "code": -32001, # CHECK-NEXT: "message": "clang diagnostic" # CHECK-NEXT: }, # CHECK-NEXT: "id": 2, Index: test/clangd/spaces-in-delimited-input.test =================================================================== --- test/clangd/spaces-in-delimited-input.test +++ test/clangd/spaces-in-delimited-input.test @@ -9,5 +9,5 @@ --- -{"jsonrpc":"2.0","id":3,"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} # CHECK-NOT: JSON parse error