Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -23,7 +23,7 @@ // Turn the replacements into the format specified by the Language Server // Protocol. Fuse them into one big JSON array. std::vector Edits; - for (auto &R : Replacements) { + for (const auto &R : Replacements) { Range ReplacementRange = { offsetToPosition(Code, R.getOffset()), offsetToPosition(Code, R.getOffset() + R.getLength())}; @@ -151,25 +151,35 @@ void ClangdLSPServer::onDocumentOnTypeFormatting( Ctx C, DocumentOnTypeFormattingParams &Params) { - auto File = Params.textDocument.uri.file; - std::string Code = Server.getDocument(File); - C.reply(json::ary( - replacementsToEdits(Code, Server.formatOnType(File, Params.position)))); + const PathRef File = Params.textDocument.uri.file; + const auto Code = Server.getDocument(File); + auto ReplacementsOrError = Server.formatOnType(Code, File, Params.position); + if (ReplacementsOrError) + C.reply(json::ary{replacementsToEdits(Code, ReplacementsOrError.get())}); + else + C.replyError(ReplacementsOrError.takeError()); } void ClangdLSPServer::onDocumentRangeFormatting( Ctx C, DocumentRangeFormattingParams &Params) { - auto File = Params.textDocument.uri.file; - std::string Code = Server.getDocument(File); - C.reply(json::ary( - replacementsToEdits(Code, Server.formatRange(File, Params.range)))); + const PathRef File = Params.textDocument.uri.file; + const auto Code = Server.getDocument(File); + auto ReplacementsOrError = Server.formatRange(Code, File, Params.range); + if (ReplacementsOrError) + C.reply(json::ary{replacementsToEdits(Code, ReplacementsOrError.get())}); + else + C.replyError(ReplacementsOrError.takeError()); } void ClangdLSPServer::onDocumentFormatting(Ctx C, DocumentFormattingParams &Params) { - auto File = Params.textDocument.uri.file; - std::string Code = Server.getDocument(File); - C.reply(json::ary(replacementsToEdits(Code, Server.formatFile(File)))); + const PathRef File = Params.textDocument.uri.file; + const auto Code = Server.getDocument(File); + auto ReplacementsOrError = Server.formatFile(Code, File); + if (ReplacementsOrError) + C.reply(json::ary{replacementsToEdits(Code, ReplacementsOrError.get())}); + else + C.replyError(ReplacementsOrError.takeError()); } void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) { @@ -178,8 +188,7 @@ std::string Code = Server.getDocument(Params.textDocument.uri.file); json::ary Commands; for (Diagnostic &D : Params.context.diagnostics) { - std::vector Fixes = - getFixIts(Params.textDocument.uri.file, D); + const auto Fixes = getFixIts(Params.textDocument.uri.file, D); auto Edits = replacementsToEdits(Code, Fixes); if (!Edits.empty()) { WorkspaceEdit WE; Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -284,17 +284,23 @@ /// given a header file and vice versa. llvm::Optional switchSourceHeader(PathRef Path); - /// Run formatting for \p Rng inside \p File. - std::vector formatRange(PathRef File, Range Rng); - /// Run formatting for the whole \p File. - std::vector formatFile(PathRef File); - /// Run formatting after a character was typed at \p Pos in \p File. - std::vector formatOnType(PathRef File, Position Pos); + /// Run formatting for \p Rng inside \p File with content \p Code. + llvm::Expected> + formatRange(llvm::StringRef Code, PathRef File, Range Rng); /// Rename all occurrences of the symbol at the \p Pos in \p File to /// \p NewName. Expected> rename(PathRef File, Position Pos, llvm::StringRef NewName); + /// Run formatting for the whole \p File with content \p Code. + llvm::Expected> + formatFile(llvm::StringRef Code, PathRef File); + + /// Run formatting after a character was typed at \p Pos in \p File with + /// content \p Code. + llvm::Expected> + formatOnType(llvm::StringRef Code, PathRef File, Position Pos); + /// Gets current document contents for \p File. \p File must point to a /// currently tracked file. /// FIXME(ibiryukov): This function is here to allow offset-to-Position @@ -309,6 +315,12 @@ void onFileEvent(const DidChangeWatchedFilesParams &Params); private: + /// FIXME: This stats several files to find a .clang-format file. I/O can be + /// slow. Think of a way to cache this. + llvm::Expected> + formatCode(llvm::StringRef Code, PathRef File, + ArrayRef Ranges); + std::future scheduleReparseAndDiags(PathRef File, VersionedDraft Contents, std::shared_ptr Resources, Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -38,16 +38,6 @@ std::promise &Promise; }; -std::vector formatCode(StringRef Code, StringRef Filename, - ArrayRef Ranges) { - // Call clang-format. - // FIXME: Don't ignore style. - format::FormatStyle Style = format::getLLVMStyle(); - auto Result = format::reformat(Style, Code, Ranges, Filename); - - return std::vector(Result.begin(), Result.end()); -} - std::string getStandardResourceDir() { static int Dummy; // Just an address in this process. return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy); @@ -328,28 +318,25 @@ return make_tagged(std::move(Result), TaggedFS.Tag); } -std::vector ClangdServer::formatRange(PathRef File, - Range Rng) { - std::string Code = getDocument(File); - +llvm::Expected> +ClangdServer::formatRange(llvm::StringRef Code, PathRef File, Range Rng) { size_t Begin = positionToOffset(Code, Rng.start); size_t Len = positionToOffset(Code, Rng.end) - Begin; return formatCode(Code, File, {tooling::Range(Begin, Len)}); } -std::vector ClangdServer::formatFile(PathRef File) { +llvm::Expected> +ClangdServer::formatFile(llvm::StringRef Code, PathRef File) { // Format everything. - std::string Code = getDocument(File); return formatCode(Code, File, {tooling::Range(0, Code.size())}); } -std::vector ClangdServer::formatOnType(PathRef File, - Position Pos) { +llvm::Expected> +ClangdServer::formatOnType(llvm::StringRef Code, PathRef File, Position Pos) { // Look for the previous opening brace from the character position and // format starting from there. - std::string Code = getDocument(File); size_t CursorPos = positionToOffset(Code, Pos); - size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos); + size_t PreviousLBracePos = Code.find_last_of('{', CursorPos); if (PreviousLBracePos == StringRef::npos) PreviousLBracePos = CursorPos; size_t Len = 1 + CursorPos - PreviousLBracePos; @@ -506,6 +493,21 @@ return llvm::None; } +llvm::Expected> +ClangdServer::formatCode(llvm::StringRef Code, PathRef File, + ArrayRef Ranges) { + // Call clang-format. + auto TaggedFS = FSProvider.getTaggedFileSystem(File); + auto StyleOrError = + format::getStyle("file", File, "LLVM", Code, TaggedFS.Value.get()); + if (!StyleOrError) { + return StyleOrError.takeError(); + } else { + auto Result = format::reformat(StyleOrError.get(), Code, Ranges, File); + return std::vector(Result.begin(), Result.end()); + } +} + std::future ClangdServer::scheduleReparseAndDiags( PathRef File, VersionedDraft Contents, std::shared_ptr Resources, Tagged> TaggedFS) { Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ clangd/JSONRPCDispatcher.h @@ -16,6 +16,7 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Support/Error.h" #include "llvm/Support/YAMLParser.h" #include #include @@ -58,11 +59,13 @@ RequestContext(JSONOutput &Out, llvm::Optional ID) : Out(Out), ID(std::move(ID)) {} - /// Sends a successful reply. + /// Send a response to a request from the client. void reply(json::Expr &&Result); - /// Sends an error response to the client, and logs it. + /// Send an error response to the client, and logs it. void replyError(ErrorCode code, const llvm::StringRef &Message); - /// Sends a request to the client. + /// Send all error messages contained in Err to the client, and log them. + void replyError(llvm::Error Err); + /// Send a request to the client. void call(llvm::StringRef Method, json::Expr &&Params); private: Index: clangd/JSONRPCDispatcher.cpp =================================================================== --- clangd/JSONRPCDispatcher.cpp +++ clangd/JSONRPCDispatcher.cpp @@ -59,24 +59,28 @@ Out.log("Attempted to reply to a notification!\n"); return; } - Out.writeMessage(json::obj{ - {"jsonrpc", "2.0"}, - {"id", *ID}, - {"result", std::move(Result)}, - }); + Out.writeMessage( + json::obj{{"jsonrpc", "2.0"}, {"id", *ID}, {"result", Result}}); } -void RequestContext::replyError(ErrorCode code, const llvm::StringRef &Message) { +void RequestContext::replyError(ErrorCode code, + const llvm::StringRef &Message) { Out.log("Error " + Twine(static_cast(code)) + ": " + Message + "\n"); if (ID) { Out.writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", *ID}, - {"error", json::obj{{"code", static_cast(code)}, {"message", Message}}}, + {"error", + json::obj{{"code", static_cast(code)}, {"message", Message}}}, }); } } +void RequestContext::replyError(llvm::Error Err) { + const auto Message = llvm::toString(std::move(Err)); + replyError(ErrorCode::UnknownErrorCode, Message); +} + void RequestContext::call(StringRef Method, json::Expr &&Params) { // FIXME: Generate/Increment IDs for every request so that we can get proper // replies once we need to.