Author: Kadir Cetinkaya Date: 2020-01-07T15:21:11+01:00 New Revision: a000f2e53f5c3433608f6097c3f4096e313b5f56
URL: https://github.com/llvm/llvm-project/commit/a000f2e53f5c3433608f6097c3f4096e313b5f56 DIFF: https://github.com/llvm/llvm-project/commit/a000f2e53f5c3433608f6097c3f4096e313b5f56.diff LOG: [clangd] Introduce bulletlists Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D71422 Added: Modified: clang-tools-extra/clangd/FormattedString.cpp clang-tools-extra/clangd/FormattedString.h clang-tools-extra/clangd/unittests/FormattedStringTests.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/FormattedString.cpp b/clang-tools-extra/clangd/FormattedString.cpp index a7a63a476c3c..cfa1210a2ee6 100644 --- a/clang-tools-extra/clangd/FormattedString.cpp +++ b/clang-tools-extra/clangd/FormattedString.cpp @@ -149,6 +149,21 @@ class CodeBlock : public Block { std::string Contents; std::string Language; }; + +// Inserts two spaces after each `\n` to indent each line. First line is not +// indented. +std::string indentLines(llvm::StringRef Input) { + assert(!Input.endswith("\n") && "Input should've been trimmed."); + std::string IndentedR; + // We'll add 2 spaces after each new line. + IndentedR.reserve(Input.size() + Input.count('\n') * 2); + for (char C : Input) { + IndentedR += C; + if (C == '\n') + IndentedR.append(" "); + } + return IndentedR; +} } // namespace std::string Block::asMarkdown() const { @@ -193,6 +208,24 @@ void Paragraph::renderPlainText(llvm::raw_ostream &OS) const { OS << '\n'; } +void BulletList::renderMarkdown(llvm::raw_ostream &OS) const { + for (auto &D : Items) { + // Instead of doing this we might prefer passing Indent to children to get + // rid of the copies, if it turns out to be a bottleneck. + OS << "- " << indentLines(D.asMarkdown()) << '\n'; + } + // We need a new line after list to terminate it in markdown. + OS << '\n'; +} + +void BulletList::renderPlainText(llvm::raw_ostream &OS) const { + for (auto &D : Items) { + // Instead of doing this we might prefer passing Indent to children to get + // rid of the copies, if it turns out to be a bottleneck. + OS << "- " << indentLines(D.asPlainText()) << '\n'; + } +} + Paragraph &Paragraph::appendText(std::string Text) { Text = canonicalizeSpaces(std::move(Text)); if (Text.empty()) @@ -215,6 +248,11 @@ Paragraph &Paragraph::appendCode(std::string Code) { return *this; } +class Document &BulletList::addItem() { + Items.emplace_back(); + return Items.back(); +} + Paragraph &Document::addParagraph() { Children.push_back(std::make_unique<Paragraph>()); return *static_cast<Paragraph *>(Children.back().get()); @@ -234,6 +272,11 @@ std::string Document::asMarkdown() const { std::string Document::asPlainText() const { return renderBlocks(Children, &Block::renderPlainText); } + +BulletList &Document::addBulletList() { + Children.emplace_back(std::make_unique<BulletList>()); + return *static_cast<BulletList *>(Children.back().get()); +} } // namespace markup } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/FormattedString.h b/clang-tools-extra/clangd/FormattedString.h index 4ded2fdee315..2d1f681e7ebe 100644 --- a/clang-tools-extra/clangd/FormattedString.h +++ b/clang-tools-extra/clangd/FormattedString.h @@ -62,6 +62,19 @@ class Paragraph : public Block { std::vector<Chunk> Chunks; }; +/// Represents a sequence of one or more documents. Knows how to print them in a +/// list like format, e.g. by prepending with "- " and indentation. +class BulletList : public Block { +public: + void renderMarkdown(llvm::raw_ostream &OS) const override; + void renderPlainText(llvm::raw_ostream &OS) const override; + + class Document &addItem(); + +private: + std::vector<class Document> Items; +}; + /// A format-agnostic representation for structured text. Allows rendering into /// markdown and plaintext. class Document { @@ -74,13 +87,16 @@ class Document { /// text representation, the code block will be surrounded by newlines. void addCodeBlock(std::string Code, std::string Language = "cpp"); + BulletList &addBulletList(); + + /// Doesn't contain any trailing newlines. std::string asMarkdown() const; + /// Doesn't contain any trailing newlines. std::string asPlainText() const; private: std::vector<std::unique_ptr<Block>> Children; }; - } // namespace markup } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp b/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp index 06f43d390349..3093ba2ce50d 100644 --- a/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp +++ b/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp @@ -186,6 +186,71 @@ foo)pt"; EXPECT_EQ(D.asPlainText(), ExpectedPlainText); } +TEST(BulletList, Render) { + BulletList L; + // Flat list + L.addItem().addParagraph().appendText("foo"); + EXPECT_EQ(L.asMarkdown(), "- foo"); + EXPECT_EQ(L.asPlainText(), "- foo"); + + L.addItem().addParagraph().appendText("bar"); + EXPECT_EQ(L.asMarkdown(), R"md(- foo +- bar)md"); + EXPECT_EQ(L.asPlainText(), R"pt(- foo +- bar)pt"); + + // Nested list, with a single item. + Document &D = L.addItem(); + // First item with foo\nbaz + D.addParagraph().appendText("foo"); + D.addParagraph().appendText("baz"); + + // Nest one level. + Document &Inner = D.addBulletList().addItem(); + Inner.addParagraph().appendText("foo"); + + // Nest one more level. + BulletList &InnerList = Inner.addBulletList(); + // Single item, baz\nbaz + Document &DeepDoc = InnerList.addItem(); + DeepDoc.addParagraph().appendText("baz"); + DeepDoc.addParagraph().appendText("baz"); + EXPECT_EQ(L.asMarkdown(), R"md(- foo +- bar +- foo + baz + - foo + - baz + baz)md"); + EXPECT_EQ(L.asPlainText(), R"pt(- foo +- bar +- foo + baz + - foo + - baz + baz)pt"); + + // Termination + Inner.addParagraph().appendText("after"); + EXPECT_EQ(L.asMarkdown(), R"md(- foo +- bar +- foo + baz + - foo + - baz + baz + + after)md"); + EXPECT_EQ(L.asPlainText(), R"pt(- foo +- bar +- foo + baz + - foo + - baz + baz + after)pt"); +} + } // namespace } // namespace markup } // namespace clangd _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits