ilya-biryukov created this revision. ilya-biryukov added reviewers: malaperle, sammccall, ioeric. Herald added subscribers: kadircet, arphaman, jkorous, MaskRay, mgorny. Herald added a project: clang.
That can render to markdown or plain text. Used for findHover requests. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D58547 Files: clang-tools-extra/clangd/CMakeLists.txt clang-tools-extra/clangd/ClangdLSPServer.cpp clang-tools-extra/clangd/ClangdServer.cpp clang-tools-extra/clangd/ClangdServer.h clang-tools-extra/clangd/FormattedString.cpp clang-tools-extra/clangd/FormattedString.h clang-tools-extra/clangd/XRefs.cpp clang-tools-extra/clangd/XRefs.h clang-tools-extra/unittests/clangd/XRefsTests.cpp
Index: clang-tools-extra/unittests/clangd/XRefsTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/XRefsTests.cpp +++ clang-tools-extra/unittests/clangd/XRefsTests.cpp @@ -1154,7 +1154,7 @@ auto AST = TU.build(); if (auto H = getHover(AST, T.point())) { EXPECT_NE("", Test.ExpectedHover) << Test.Input; - EXPECT_EQ(H->contents.value, Test.ExpectedHover.str()) << Test.Input; + EXPECT_EQ(H->renderAsPlainText(), Test.ExpectedHover.str()) << Test.Input; } else EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input; } Index: clang-tools-extra/clangd/XRefs.h =================================================================== --- clang-tools-extra/clangd/XRefs.h +++ clang-tools-extra/clangd/XRefs.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XREFS_H #include "ClangdUnit.h" +#include "FormattedString.h" #include "Protocol.h" #include "index/Index.h" #include "llvm/ADT/Optional.h" @@ -47,7 +48,7 @@ Position Pos); /// Get the hover information when hovering at \p Pos. -llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos); +llvm::Optional<FormattedString> getHover(ParsedAST &AST, Position Pos); /// Returns reference locations of the symbol at a specified \p Pos. /// \p Limit limits the number of results returned (0 means no limit). Index: clang-tools-extra/clangd/XRefs.cpp =================================================================== --- clang-tools-extra/clangd/XRefs.cpp +++ clang-tools-extra/clangd/XRefs.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "XRefs.h" #include "AST.h" +#include "FormattedString.h" #include "Logger.h" #include "SourceCode.h" #include "URI.h" @@ -515,17 +516,17 @@ } /// Generate a \p Hover object given the declaration \p D. -static Hover getHoverContents(const Decl *D) { - Hover H; +static FormattedString getHoverContents(const Decl *D) { + FormattedString R; llvm::Optional<std::string> NamedScope = getScopeName(D); // Generate the "Declared in" section. if (NamedScope) { assert(!NamedScope->empty()); - H.contents.value += "Declared in "; - H.contents.value += *NamedScope; - H.contents.value += "\n\n"; + R.appendText("Declared in "); + R.appendText(*NamedScope); + R.appendText("\n"); } // We want to include the template in the Hover. @@ -537,35 +538,30 @@ PrintingPolicy Policy = printingPolicyForDecls(D->getASTContext().getPrintingPolicy()); - D->print(OS, Policy); - OS.flush(); - - H.contents.value += DeclText; - return H; + R.appendCodeBlock(OS.str()); + return R; } /// Generate a \p Hover object given the type \p T. -static Hover getHoverContents(QualType T, ASTContext &ASTCtx) { - Hover H; - std::string TypeText; - llvm::raw_string_ostream OS(TypeText); +static FormattedString getHoverContents(QualType T, ASTContext &ASTCtx) { + FormattedString R; + + std::string Code; + llvm::raw_string_ostream OS(Code); PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy()); T.print(OS, Policy); - OS.flush(); - H.contents.value += TypeText; - return H; + + R.appendCodeBlock(OS.str()); + return R; } /// Generate a \p Hover object given the macro \p MacroInf. -static Hover getHoverContents(llvm::StringRef MacroName) { - Hover H; - - H.contents.value = "#define "; - H.contents.value += MacroName; - - return H; +static FormattedString getHoverContents(llvm::StringRef MacroName) { + FormattedString S; + S.appendCodeBlock("#define " + MacroName.str()); + return S; } namespace { @@ -680,7 +676,7 @@ return V.getDeducedType(); } -llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos) { +llvm::Optional<FormattedString> getHover(ParsedAST &AST, Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID()); Index: clang-tools-extra/clangd/FormattedString.h =================================================================== --- /dev/null +++ clang-tools-extra/clangd/FormattedString.h @@ -0,0 +1,56 @@ +//===--- FormattedString.h ----------------------------------*- C++-*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// A simple intermediate representation of formatted text that could be +// converted to plaintext or markdown. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATTEDSTRING_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FORMATTEDSTRING_H + +#include <string> +#include <vector> + +namespace clang { +namespace clangd { + +/// A structured string representation that could be converted to markdown or +/// plaintext upon requrest. +class FormattedString { +public: + /// Append plain text to the end of the string. + void appendText(std::string Text); + /// Append a block of C++ code. This translates to a ``` block in markdown. + /// In a plain text representation, the code block will be surrounded by + /// newlines. + void appendCodeBlock(std::string Code); + /// Append an inline block of C++ code. This translates to the ` block in + /// markdown. + /// EXPECTS: Code does not contain newlines. + void appendInlineCode(std::string Code); + + std::string renderAsMarkdown() const; + std::string renderAsPlainText() const; + +private: + enum class ChunkKind { + PlainText, // A plain text paragraph. + CodeBlock, // A block of code. + InlineCodeBlock, // An inline block of code. + }; + struct Chunk { + ChunkKind Kind = ChunkKind::PlainText; + std::string Contents; + }; + std::vector<Chunk> Chunks; +}; + +} // namespace clangd +} // namespace clang + +#endif Index: clang-tools-extra/clangd/FormattedString.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clangd/FormattedString.cpp @@ -0,0 +1,103 @@ +//===--- FormattedString.cpp --------------------------------*- C++-*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "FormattedString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <string> + +namespace clang { +namespace clangd { + +namespace { +std::string escapeBackticks(llvm::StringRef Input) { + std::string R; + for (size_t From = 0; From < Input.size();) { + size_t Next = Input.find("`", From); + R += Input.substr(From, Next); + if (Next == llvm::StringRef::npos) + break; + R += "``"; // backticks should be doubled. + From = Next + 1; + } + return R; +} +} // namespace + +void FormattedString::appendText(std::string Text) { + if (Chunks.empty() || Chunks.back().Kind != ChunkKind::PlainText) { + Chunk C; + C.Kind = ChunkKind::PlainText; + Chunks.push_back(C); + } + Chunks.back().Contents += Text; +} + +void FormattedString::appendCodeBlock(std::string Code) { + Chunk C; + C.Kind = ChunkKind::CodeBlock; + C.Contents = std::move(Code); + Chunks.push_back(std::move(C)); +} + +void FormattedString::appendInlineCode(std::string Code) { + Chunk C; + C.Kind = ChunkKind::InlineCodeBlock; + C.Contents = std::move(Code); + Chunks.push_back(std::move(C)); +} + +std::string FormattedString::renderAsMarkdown() const { + std::string R; + for (const auto &C : Chunks) { + switch (C.Kind) { + case ChunkKind::PlainText: + R += C.Contents; + continue; + case ChunkKind::InlineCodeBlock: + R += " `"; + R += escapeBackticks(C.Contents); + R += "` "; + continue; + case ChunkKind::CodeBlock: + R += "\n```"; + R += escapeBackticks(C.Contents); + R += "\n```\n"; + continue; + } + llvm_unreachable("unhanlded ChunkKind"); + } + return R; +} + +std::string FormattedString::renderAsPlainText() const { + std::string R; + bool NeedsLineBreak = false; + for (const auto &C : Chunks) { + if (NeedsLineBreak) { + R += "\n"; + NeedsLineBreak = false; + } + switch (C.Kind) { + case ChunkKind::PlainText: + case ChunkKind::InlineCodeBlock: + R += C.Contents; + continue; + case ChunkKind::CodeBlock: + if (!R.empty()) + R += "\n"; + R += C.Contents; + NeedsLineBreak = true; + continue; + } + llvm_unreachable("unhanlded ChunkKind"); + } + return R; +} +} // namespace clangd +} // namespace clang Index: clang-tools-extra/clangd/ClangdServer.h =================================================================== --- clang-tools-extra/clangd/ClangdServer.h +++ clang-tools-extra/clangd/ClangdServer.h @@ -14,6 +14,7 @@ #include "ClangdUnit.h" #include "CodeComplete.h" #include "FSProvider.h" +#include "FormattedString.h" #include "Function.h" #include "GlobalCompilationDatabase.h" #include "Protocol.h" @@ -182,7 +183,7 @@ /// Get code hover for a given position. void findHover(PathRef File, Position Pos, - Callback<llvm::Optional<Hover>> CB); + Callback<llvm::Optional<FormattedString>> CB); /// Retrieve the top symbols from the workspace matching a query. void workspaceSymbols(StringRef Query, int Limit, Index: clang-tools-extra/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdServer.cpp +++ clang-tools-extra/clangd/ClangdServer.cpp @@ -10,6 +10,7 @@ #include "ClangdUnit.h" #include "CodeComplete.h" #include "FindSymbols.h" +#include "FormattedString.h" #include "Headers.h" #include "SourceCode.h" #include "Trace.h" @@ -512,14 +513,13 @@ } void ClangdServer::findHover(PathRef File, Position Pos, - Callback<llvm::Optional<Hover>> CB) { - auto Action = [Pos](Callback<llvm::Optional<Hover>> CB, + Callback<llvm::Optional<FormattedString>> CB) { + auto Action = [Pos](Callback<llvm::Optional<FormattedString>> CB, llvm::Expected<InputsAndAST> InpAST) { if (!InpAST) return CB(InpAST.takeError()); CB(clangd::getHover(InpAST->AST, Pos)); }; - WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB))); } Index: clang-tools-extra/clangd/ClangdLSPServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdLSPServer.cpp +++ clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -8,6 +8,7 @@ #include "ClangdLSPServer.h" #include "Diagnostics.h" +#include "FormattedString.h" #include "Protocol.h" #include "SourceCode.h" #include "Trace.h" @@ -803,7 +804,20 @@ void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, Callback<llvm::Optional<Hover>> Reply) { Server->findHover(Params.textDocument.uri.file(), Params.position, - std::move(Reply)); + Bind( + [](decltype(Reply) Reply, + llvm::Expected<llvm::Optional<FormattedString>> H) { + if (!H) + return Reply(H.takeError()); + if (!*H) + return Reply(llvm::None); + // FIXME: render as markdown if client supports it. + Hover R; + R.contents.kind = MarkupKind::PlainText; + R.contents.value = (*H)->renderAsPlainText(); + return Reply(std::move(R)); + }, + std::move(Reply))); } void ClangdLSPServer::applyConfiguration( Index: clang-tools-extra/clangd/CMakeLists.txt =================================================================== --- clang-tools-extra/clangd/CMakeLists.txt +++ clang-tools-extra/clangd/CMakeLists.txt @@ -37,6 +37,7 @@ FileDistance.cpp FS.cpp FSProvider.cpp + FormattedString.cpp FuzzyMatch.cpp GlobalCompilationDatabase.cpp Headers.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits