| //===-- FormattedStringTests.cpp ------------------------------------------===// |
| // |
| // 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 "clang/Basic/LLVM.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace clang { |
| namespace clangd { |
| namespace markup { |
| namespace { |
| |
| TEST(Render, Escaping) { |
| // Check some ASCII punctuation |
| Paragraph P; |
| P.appendText("*!`"); |
| EXPECT_EQ(P.asMarkdown(), "\\*\\!\\`"); |
| |
| // Check all ASCII punctuation. |
| P = Paragraph(); |
| std::string Punctuation = R"txt(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~)txt"; |
| // Same text, with each character escaped. |
| std::string EscapedPunctuation; |
| EscapedPunctuation.reserve(2 * Punctuation.size()); |
| for (char C : Punctuation) |
| EscapedPunctuation += std::string("\\") + C; |
| P.appendText(Punctuation); |
| EXPECT_EQ(P.asMarkdown(), EscapedPunctuation); |
| |
| // In code blocks we don't need to escape ASCII punctuation. |
| P = Paragraph(); |
| P.appendCode("* foo !+ bar * baz"); |
| EXPECT_EQ(P.asMarkdown(), "`* foo !+ bar * baz`"); |
| |
| // But we have to escape the backticks. |
| P = Paragraph(); |
| P.appendCode("foo`bar`baz"); |
| EXPECT_EQ(P.asMarkdown(), "`foo``bar``baz`"); |
| |
| // Inline code blocks starting or ending with backticks should add spaces. |
| P = Paragraph(); |
| P.appendCode("`foo"); |
| EXPECT_EQ(P.asMarkdown(), "` ``foo `"); |
| P = Paragraph(); |
| P.appendCode("foo`"); |
| EXPECT_EQ(P.asMarkdown(), "` foo`` `"); |
| 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) { |
| // This test keeps appending contents to a single Paragraph and checks |
| // expected accumulated contents after each one. |
| // Purpose is to check for separation between different chunks. |
| Paragraph P; |
| |
| P.appendText("after"); |
| EXPECT_EQ(P.asMarkdown(), "after"); |
| EXPECT_EQ(P.asPlainText(), "after"); |
| |
| P.appendCode("foobar"); |
| EXPECT_EQ(P.asMarkdown(), "after `foobar`"); |
| EXPECT_EQ(P.asPlainText(), "after foobar"); |
| |
| P.appendText("bat"); |
| EXPECT_EQ(P.asMarkdown(), "after `foobar` bat"); |
| EXPECT_EQ(P.asPlainText(), "after foobar bat"); |
| } |
| |
| TEST(Paragraph, ExtraSpaces) { |
| // Make sure spaces inside chunks are dropped. |
| Paragraph P; |
| P.appendText("foo\n \t baz"); |
| P.appendCode(" bar\n"); |
| EXPECT_EQ(P.asMarkdown(), "foo baz `bar`"); |
| EXPECT_EQ(P.asPlainText(), "foo baz bar"); |
| } |
| |
| TEST(Paragraph, NewLines) { |
| // New lines before and after chunks are dropped. |
| Paragraph P; |
| P.appendText(" \n foo\nbar\n "); |
| P.appendCode(" \n foo\nbar \n "); |
| EXPECT_EQ(P.asMarkdown(), "foo bar `foo bar`"); |
| EXPECT_EQ(P.asPlainText(), "foo bar foo bar"); |
| } |
| |
| TEST(Document, Separators) { |
| Document D; |
| D.addParagraph().appendText("foo"); |
| D.addCodeBlock("test"); |
| D.addParagraph().appendText("bar"); |
| |
| const char ExpectedMarkdown[] = R"md(foo |
| ```cpp |
| test |
| ``` |
| bar)md"; |
| EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown); |
| |
| const char ExpectedText[] = R"pt(foo |
| |
| test |
| |
| bar)pt"; |
| EXPECT_EQ(D.asPlainText(), ExpectedText); |
| } |
| |
| TEST(Document, Ruler) { |
| Document D; |
| D.addParagraph().appendText("foo"); |
| D.addRuler(); |
| |
| // Ruler followed by paragraph. |
| D.addParagraph().appendText("bar"); |
| EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nbar"); |
| EXPECT_EQ(D.asPlainText(), "foo\n\nbar"); |
| |
| D = Document(); |
| D.addParagraph().appendText("foo"); |
| D.addRuler(); |
| D.addCodeBlock("bar"); |
| // Ruler followed by a codeblock. |
| EXPECT_EQ(D.asMarkdown(), "foo \n\n---\n```cpp\nbar\n```"); |
| EXPECT_EQ(D.asPlainText(), "foo\n\nbar"); |
| |
| // Ruler followed by another ruler |
| D = Document(); |
| D.addParagraph().appendText("foo"); |
| D.addRuler(); |
| D.addRuler(); |
| EXPECT_EQ(D.asMarkdown(), "foo"); |
| EXPECT_EQ(D.asPlainText(), "foo"); |
| |
| // Multiple rulers between blocks |
| D.addRuler(); |
| D.addParagraph().appendText("foo"); |
| EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nfoo"); |
| EXPECT_EQ(D.asPlainText(), "foo\n\nfoo"); |
| } |
| |
| TEST(Document, Heading) { |
| Document D; |
| D.addHeading(1).appendText("foo"); |
| D.addHeading(2).appendText("bar"); |
| D.addParagraph().appendText("baz"); |
| EXPECT_EQ(D.asMarkdown(), "# foo \n## bar \nbaz"); |
| EXPECT_EQ(D.asPlainText(), "foo\nbar\nbaz"); |
| } |
| |
| TEST(CodeBlock, Render) { |
| Document D; |
| // Code blocks preserves any extra spaces. |
| D.addCodeBlock("foo\n bar\n baz"); |
| |
| llvm::StringRef ExpectedMarkdown = |
| R"md(```cpp |
| foo |
| bar |
| baz |
| ```)md"; |
| llvm::StringRef ExpectedPlainText = |
| R"pt(foo |
| bar |
| baz)pt"; |
| EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown); |
| EXPECT_EQ(D.asPlainText(), ExpectedPlainText); |
| D.addCodeBlock("foo"); |
| ExpectedMarkdown = |
| R"md(```cpp |
| foo |
| bar |
| baz |
| ``` |
| ```cpp |
| foo |
| ```)md"; |
| EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown); |
| ExpectedPlainText = |
| R"pt(foo |
| bar |
| baz |
| |
| 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"); |
| llvm::StringRef Expected = R"md(- foo |
| - bar)md"; |
| EXPECT_EQ(L.asMarkdown(), Expected); |
| EXPECT_EQ(L.asPlainText(), Expected); |
| |
| // 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"); |
| StringRef ExpectedMarkdown = R"md(- foo |
| - bar |
| - foo |
| baz |
| - foo |
| - baz |
| baz)md"; |
| EXPECT_EQ(L.asMarkdown(), ExpectedMarkdown); |
| StringRef ExpectedPlainText = R"pt(- foo |
| - bar |
| - foo |
| baz |
| - foo |
| - baz |
| baz)pt"; |
| EXPECT_EQ(L.asPlainText(), ExpectedPlainText); |
| |
| // Termination |
| Inner.addParagraph().appendText("after"); |
| ExpectedMarkdown = R"md(- foo |
| - bar |
| - foo |
| baz |
| - foo |
| - baz |
| baz |
| |
| after)md"; |
| EXPECT_EQ(L.asMarkdown(), ExpectedMarkdown); |
| ExpectedPlainText = R"pt(- foo |
| - bar |
| - foo |
| baz |
| - foo |
| - baz |
| baz |
| after)pt"; |
| EXPECT_EQ(L.asPlainText(), ExpectedPlainText); |
| } |
| |
| } // namespace |
| } // namespace markup |
| } // namespace clangd |
| } // namespace clang |