diff --git a/clang-tools-extra/clangd/FormattedString.h b/clang-tools-extra/clangd/FormattedString.h --- a/clang-tools-extra/clangd/FormattedString.h +++ b/clang-tools-extra/clangd/FormattedString.h @@ -70,6 +70,9 @@ Paragraph &addParagraph(); /// Inserts a vertical space into the document. void addSpacer(); + /// Adds a block of code. This translates to a ``` block in markdown. In plain + /// text representation, the code block will be surrounded by newlines. + void addCodeBlock(std::string Code, std::string Language = "cpp"); std::string asMarkdown() const; std::string asPlainText() const; diff --git a/clang-tools-extra/clangd/FormattedString.cpp b/clang-tools-extra/clangd/FormattedString.cpp --- a/clang-tools-extra/clangd/FormattedString.cpp +++ b/clang-tools-extra/clangd/FormattedString.cpp @@ -131,6 +131,25 @@ void renderPlainText(llvm::raw_ostream &OS) const override { OS << '\n'; } }; +class CodeBlock : public Block { +public: + void renderMarkdown(llvm::raw_ostream &OS) const override { + // No need to pad from previous blocks, as they should end with a new line. + OS << renderCodeBlock(Contents, Language) << '\n'; + } + + void renderPlainText(llvm::raw_ostream &OS) const override { + // In plaintext we want one empty line before and after codeblocks. + OS << '\n' << Contents << "\n\n"; + } + + CodeBlock(std::string Contents, std::string Language) + : Contents(std::move(Contents)), Language(std::move(Language)) {} + +private: + std::string Contents; + std::string Language; +}; } // namespace std::string Block::asMarkdown() const { @@ -204,6 +223,11 @@ void Document::addSpacer() { Children.push_back(std::make_unique()); } +void Document::addCodeBlock(std::string Code, std::string Language) { + Children.emplace_back( + std::make_unique(std::move(Code), std::move(Language))); +} + std::string Document::asMarkdown() const { return renderBlocks(Children, &Block::renderMarkdown); } diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -95,7 +95,7 @@ } void printParams(llvm::raw_ostream &OS, - const std::vector &Params) { + const std::vector &Params) { for (size_t I = 0, E = Params.size(); I != E; ++I) { if (I) OS << ", "; @@ -456,12 +456,11 @@ P.appendCode(llvm::StringRef(*NamespaceScope).drop_back(2)); } - Output.addSpacer(); if (!Definition.empty()) { - Output.addParagraph().appendCode(Definition); + Output.addCodeBlock(Definition); } else { // Builtin types - Output.addParagraph().appendCode(Name); + Output.addCodeBlock(Name); } if (!Documentation.empty()) diff --git a/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp b/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp --- a/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp +++ b/clang-tools-extra/clangd/unittests/FormattedStringTests.cpp @@ -54,6 +54,28 @@ P = Paragraph(); P.appendCode("`foo`"); EXPECT_EQ(P.asMarkdown(), "` ``foo`` `"); + + // Code blocks might need more than 3 backticks. + Document D; + D.addCodeBlock("foobarbaz `\nqux"); + EXPECT_EQ(D.asMarkdown(), "```cpp\n" + "foobarbaz `\nqux\n" + "```"); + D = Document(); + D.addCodeBlock("foobarbaz ``\nqux"); + EXPECT_THAT(D.asMarkdown(), "```cpp\n" + "foobarbaz ``\nqux\n" + "```"); + D = Document(); + D.addCodeBlock("foobarbaz ```\nqux"); + EXPECT_EQ(D.asMarkdown(), "````cpp\n" + "foobarbaz ```\nqux\n" + "````"); + D = Document(); + D.addCodeBlock("foobarbaz ` `` ``` ```` `\nqux"); + EXPECT_EQ(D.asMarkdown(), "`````cpp\n" + "foobarbaz ` `` ``` ```` `\nqux\n" + "`````"); } TEST(Paragraph, SeparationOfChunks) { @@ -96,9 +118,18 @@ TEST(Document, Separators) { Document D; D.addParagraph().appendText("foo"); + D.addCodeBlock("test"); D.addParagraph().appendText("bar"); - EXPECT_EQ(D.asMarkdown(), "foo\nbar"); - EXPECT_EQ(D.asPlainText(), "foo\nbar"); + EXPECT_EQ(D.asMarkdown(), R"md(foo +```cpp +test +``` +bar)md"); + EXPECT_EQ(D.asPlainText(), R"pt(foo + +test + +bar)pt"); } TEST(Document, Spacer) { @@ -110,6 +141,20 @@ EXPECT_EQ(D.asPlainText(), "foo\n\nbar"); } +TEST(CodeBlock, Render) { + Document D; + // Code blocks preserves any extra spaces. + D.addCodeBlock("foo\n bar\n baz"); + EXPECT_EQ(D.asMarkdown(), R"md(```cpp +foo + bar + baz +```)md"); + EXPECT_EQ(D.asPlainText(), R"pt(foo + bar + baz)pt"); +} + } // namespace } // namespace markup } // namespace clangd