774 lines · cpp
1//===-- MarkupTests.cpp ---------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "support/Markup.h"9#include "clang/Basic/LLVM.h"10#include "llvm/ADT/StringRef.h"11#include "gmock/gmock.h"12#include "gtest/gtest.h"13 14namespace clang {15namespace clangd {16namespace markup {17namespace {18 19std::string escape(llvm::StringRef Text) {20 return Paragraph().appendText(Text.str()).asEscapedMarkdown();21}22 23std::string dontEscape(llvm::StringRef Text) {24 return Paragraph().appendText(Text.str()).asMarkdown();25}26 27MATCHER_P(escaped, C, "") {28 return testing::ExplainMatchResult(::testing::HasSubstr(std::string{'\\', C}),29 arg, result_listener);30}31 32MATCHER(escapedNone, "") {33 return testing::ExplainMatchResult(::testing::Not(::testing::HasSubstr("\\")),34 arg, result_listener);35}36 37TEST(Render, Escaping) {38 // Check all ASCII punctuation.39 std::string Punctuation = R"txt(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~)txt";40 std::string EscapedPunc = R"txt(!"#$%&'()\*+,-./:;<=>?@[\\]^\_\`{|}~)txt";41 EXPECT_EQ(escape(Punctuation), EscapedPunc);42 43 // Inline code44 EXPECT_EQ(escape("`foo`"), R"(\`foo\`)");45 EXPECT_EQ(escape("`foo"), R"(\`foo)");46 EXPECT_EQ(escape("foo`"), R"(foo\`)");47 EXPECT_EQ(escape("``foo``"), R"(\`\`foo\`\`)");48 // Code blocks49 EXPECT_EQ(escape("```"), R"(\`\`\`)"); // This could also be inline code!50 EXPECT_EQ(escape("~~~"), R"(\~~~)");51 52 // Rulers and headings53 EXPECT_THAT(escape("## Heading"), escaped('#'));54 EXPECT_THAT(escape("Foo # bar"), escapedNone());55 EXPECT_EQ(escape("---"), R"(\---)");56 EXPECT_EQ(escape("-"), R"(\-)");57 EXPECT_EQ(escape("==="), R"(\===)");58 EXPECT_EQ(escape("="), R"(\=)");59 EXPECT_EQ(escape("***"), R"(\*\*\*)"); // \** could start emphasis!60 61 // HTML tags.62 EXPECT_THAT(escape("<pre"), escaped('<'));63 EXPECT_THAT(escape("< pre"), escapedNone());64 EXPECT_THAT(escape("if a<b then"), escaped('<'));65 EXPECT_THAT(escape("if a<b then c."), escapedNone());66 EXPECT_THAT(escape("if a<b then c='foo'."), escaped('<'));67 EXPECT_THAT(escape("std::vector<T>"), escaped('<'));68 EXPECT_THAT(escape("std::vector<std::string>"), escaped('<'));69 EXPECT_THAT(escape("std::map<int, int>"), escapedNone());70 // Autolinks71 EXPECT_THAT(escape("Email <foo@bar.com>"), escapedNone());72 EXPECT_THAT(escape("Website <http://foo.bar>"), escapedNone());73 74 // Bullet lists.75 EXPECT_THAT(escape("- foo"), escaped('-'));76 EXPECT_THAT(escape("* foo"), escaped('*'));77 EXPECT_THAT(escape("+ foo"), escaped('+'));78 EXPECT_THAT(escape("+"), escaped('+'));79 EXPECT_THAT(escape("a + foo"), escapedNone());80 EXPECT_THAT(escape("a+ foo"), escapedNone());81 EXPECT_THAT(escape("1. foo"), escaped('.'));82 EXPECT_THAT(escape("a. foo"), escapedNone());83 84 // Emphasis.85 EXPECT_EQ(escape("*foo*"), R"(\*foo\*)");86 EXPECT_EQ(escape("**foo**"), R"(\*\*foo\*\*)");87 EXPECT_THAT(escape("*foo"), escaped('*'));88 EXPECT_THAT(escape("foo *"), escapedNone());89 EXPECT_THAT(escape("foo * bar"), escapedNone());90 EXPECT_THAT(escape("foo_bar"), escapedNone());91 EXPECT_THAT(escape("foo _bar"), escaped('_'));92 EXPECT_THAT(escape("foo_ bar"), escaped('_'));93 EXPECT_THAT(escape("foo _ bar"), escapedNone());94 95 // HTML entities.96 EXPECT_THAT(escape("fish &chips;"), escaped('&'));97 EXPECT_THAT(escape("fish & chips;"), escapedNone());98 EXPECT_THAT(escape("fish &chips"), escapedNone());99 EXPECT_THAT(escape("foo * bar"), escaped('&'));100 EXPECT_THAT(escape("foo ¯ bar"), escaped('&'));101 EXPECT_THAT(escape("foo &?; bar"), escapedNone());102 103 // Links.104 EXPECT_THAT(escape("[foo](bar)"), escaped(']'));105 EXPECT_THAT(escape("[foo]: bar"), escaped(']'));106 // No need to escape these, as the target never exists.107 EXPECT_THAT(escape("[foo][]"), escapedNone());108 EXPECT_THAT(escape("[foo][bar]"), escapedNone());109 EXPECT_THAT(escape("[foo]"), escapedNone());110 111 // In code blocks we don't need to escape ASCII punctuation.112 Paragraph P = Paragraph();113 P.appendCode("* foo !+ bar * baz");114 EXPECT_EQ(P.asEscapedMarkdown(), "`* foo !+ bar * baz`");115 116 // But we have to escape the backticks.117 P = Paragraph();118 P.appendCode("foo`bar`baz", /*Preserve=*/true);119 EXPECT_EQ(P.asEscapedMarkdown(), "`foo``bar``baz`");120 // In plain-text, we fall back to different quotes.121 EXPECT_EQ(P.asPlainText(), "'foo`bar`baz'");122 123 // Inline code blocks starting or ending with backticks should add spaces.124 P = Paragraph();125 P.appendCode("`foo");126 EXPECT_EQ(P.asEscapedMarkdown(), "` ``foo `");127 P = Paragraph();128 P.appendCode("foo`");129 EXPECT_EQ(P.asEscapedMarkdown(), "` foo`` `");130 P = Paragraph();131 P.appendCode("`foo`");132 EXPECT_EQ(P.asEscapedMarkdown(), "` ``foo`` `");133 134 // Code blocks might need more than 3 backticks.135 Document D;136 D.addCodeBlock("foobarbaz `\nqux");137 EXPECT_EQ(D.asEscapedMarkdown(), "```cpp\n"138 "foobarbaz `\nqux\n"139 "```");140 D = Document();141 D.addCodeBlock("foobarbaz ``\nqux");142 EXPECT_THAT(D.asEscapedMarkdown(), "```cpp\n"143 "foobarbaz ``\nqux\n"144 "```");145 D = Document();146 D.addCodeBlock("foobarbaz ```\nqux");147 EXPECT_EQ(D.asEscapedMarkdown(), "````cpp\n"148 "foobarbaz ```\nqux\n"149 "````");150 D = Document();151 D.addCodeBlock("foobarbaz ` `` ``` ```` `\nqux");152 EXPECT_EQ(D.asEscapedMarkdown(), "`````cpp\n"153 "foobarbaz ` `` ``` ```` `\nqux\n"154 "`````");155}156 157TEST(Render, NoEscaping) {158 // Check all ASCII punctuation.159 std::string Punctuation = R"txt(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~)txt";160 EXPECT_EQ(dontEscape(Punctuation), Punctuation);161 162 // Inline code163 EXPECT_THAT(dontEscape("`foo`"), escapedNone());164 EXPECT_THAT(dontEscape("`foo"), escapedNone());165 EXPECT_THAT(dontEscape("foo`"), escapedNone());166 EXPECT_THAT(dontEscape("``foo``"), escapedNone());167 // Code blocks168 EXPECT_THAT(dontEscape("```"), escapedNone());169 EXPECT_THAT(dontEscape("~~~"), escapedNone());170 171 // Rulers and headings172 EXPECT_THAT(dontEscape("## Heading"), escapedNone());173 EXPECT_THAT(dontEscape("Foo # bar"), escapedNone());174 EXPECT_THAT(dontEscape("---"), escapedNone());175 EXPECT_THAT(dontEscape("-"), escapedNone());176 EXPECT_THAT(dontEscape("==="), escapedNone());177 EXPECT_THAT(dontEscape("="), escapedNone());178 EXPECT_THAT(dontEscape("***"), escapedNone()); // \** could start emphasis!179 180 // HTML tags.181 EXPECT_THAT(dontEscape("<pre"), escaped('<'));182 EXPECT_THAT(dontEscape("< pre"), escapedNone());183 EXPECT_THAT(dontEscape("if a<b then"), escaped('<'));184 EXPECT_THAT(dontEscape("if a<b then c."), escapedNone());185 EXPECT_THAT(dontEscape("if a<b then c='foo'."), escaped('<'));186 EXPECT_THAT(dontEscape("std::vector<T>"), escaped('<'));187 EXPECT_THAT(dontEscape("std::vector<std::string>"), escaped('<'));188 EXPECT_THAT(dontEscape("std::map<int, int>"), escapedNone());189 // Autolinks190 EXPECT_THAT(dontEscape("Email <foo@bar.com>"), escapedNone());191 EXPECT_THAT(dontEscape("Website <http://foo.bar>"), escapedNone());192 193 // Bullet lists.194 EXPECT_THAT(dontEscape("- foo"), escapedNone());195 EXPECT_THAT(dontEscape("* foo"), escapedNone());196 EXPECT_THAT(dontEscape("+ foo"), escapedNone());197 EXPECT_THAT(dontEscape("+"), escapedNone());198 EXPECT_THAT(dontEscape("a + foo"), escapedNone());199 EXPECT_THAT(dontEscape("a+ foo"), escapedNone());200 EXPECT_THAT(dontEscape("1. foo"), escapedNone());201 EXPECT_THAT(dontEscape("a. foo"), escapedNone());202 203 // Emphasis.204 EXPECT_THAT(dontEscape("*foo*"), escapedNone());205 EXPECT_THAT(dontEscape("**foo**"), escapedNone());206 EXPECT_THAT(dontEscape("*foo"), escapedNone());207 EXPECT_THAT(dontEscape("foo *"), escapedNone());208 EXPECT_THAT(dontEscape("foo * bar"), escapedNone());209 EXPECT_THAT(dontEscape("foo_bar"), escapedNone());210 EXPECT_THAT(dontEscape("foo _bar"), escapedNone());211 EXPECT_THAT(dontEscape("foo_ bar"), escapedNone());212 EXPECT_THAT(dontEscape("foo _ bar"), escapedNone());213 214 // HTML entities.215 EXPECT_THAT(dontEscape("fish &chips;"), escaped('&'));216 EXPECT_THAT(dontEscape("fish & chips;"), escapedNone());217 EXPECT_THAT(dontEscape("fish &chips"), escapedNone());218 EXPECT_THAT(dontEscape("foo * bar"), escaped('&'));219 EXPECT_THAT(dontEscape("foo ¯ bar"), escaped('&'));220 EXPECT_THAT(dontEscape("foo &?; bar"), escapedNone());221 222 // Links.223 EXPECT_THAT(dontEscape("[foo](bar)"), escapedNone());224 EXPECT_THAT(dontEscape("[foo]: bar"), escapedNone());225 // No need to escape these, as the target never exists.226 EXPECT_THAT(dontEscape("[foo][]"), escapedNone());227 EXPECT_THAT(dontEscape("[foo][bar]"), escapedNone());228 EXPECT_THAT(dontEscape("[foo]"), escapedNone());229 230 // In code blocks we don't need to escape ASCII punctuation.231 Paragraph P = Paragraph();232 P.appendCode("* foo !+ bar * baz");233 EXPECT_EQ(P.asMarkdown(), "`* foo !+ bar * baz`");234 235 // But we have to escape the backticks.236 P = Paragraph();237 P.appendCode("foo`bar`baz", /*Preserve=*/true);238 EXPECT_EQ(P.asMarkdown(), "`foo``bar``baz`");239 240 // Inline code blocks starting or ending with backticks should add spaces.241 P = Paragraph();242 P.appendCode("`foo");243 EXPECT_EQ(P.asMarkdown(), "` ``foo `");244 P = Paragraph();245 P.appendCode("foo`");246 EXPECT_EQ(P.asMarkdown(), "` foo`` `");247 P = Paragraph();248 P.appendCode("`foo`");249 EXPECT_EQ(P.asMarkdown(), "` ``foo`` `");250 251 // Code blocks might need more than 3 backticks.252 Document D;253 D.addCodeBlock("foobarbaz `\nqux");254 EXPECT_EQ(D.asMarkdown(), "```cpp\n"255 "foobarbaz `\nqux\n"256 "```");257 D = Document();258 D.addCodeBlock("foobarbaz ``\nqux");259 EXPECT_THAT(D.asMarkdown(), "```cpp\n"260 "foobarbaz ``\nqux\n"261 "```");262 D = Document();263 D.addCodeBlock("foobarbaz ```\nqux");264 EXPECT_EQ(D.asMarkdown(), "````cpp\n"265 "foobarbaz ```\nqux\n"266 "````");267 D = Document();268 D.addCodeBlock("foobarbaz ` `` ``` ```` `\nqux");269 EXPECT_EQ(D.asMarkdown(), "`````cpp\n"270 "foobarbaz ` `` ``` ```` `\nqux\n"271 "`````");272}273 274TEST(Paragraph, SeparationOfChunks) {275 // This test keeps appending contents to a single Paragraph and checks276 // expected accumulated contents after each one.277 // Purpose is to check for separation between different chunks.278 Paragraph P;279 280 P.appendText("after ");281 EXPECT_EQ(P.asEscapedMarkdown(), "after");282 EXPECT_EQ(P.asMarkdown(), "after");283 EXPECT_EQ(P.asPlainText(), "after");284 285 P.appendCode("foobar").appendSpace();286 EXPECT_EQ(P.asEscapedMarkdown(), "after `foobar`");287 EXPECT_EQ(P.asMarkdown(), "after `foobar`");288 EXPECT_EQ(P.asPlainText(), "after foobar");289 290 P.appendText("bat");291 EXPECT_EQ(P.asEscapedMarkdown(), "after `foobar` bat");292 EXPECT_EQ(P.asMarkdown(), "after `foobar` bat");293 EXPECT_EQ(P.asPlainText(), "after foobar bat");294 295 P.appendCode("no").appendCode("space");296 EXPECT_EQ(P.asEscapedMarkdown(), "after `foobar` bat`no` `space`");297 EXPECT_EQ(P.asMarkdown(), "after `foobar` bat`no` `space`");298 EXPECT_EQ(P.asPlainText(), "after foobar batno space");299 300 P.appendText(" text");301 EXPECT_EQ(P.asEscapedMarkdown(), "after `foobar` bat`no` `space` text");302 EXPECT_EQ(P.asMarkdown(), "after `foobar` bat`no` `space` text");303 EXPECT_EQ(P.asPlainText(), "after foobar batno space text");304 305 P.appendSpace().appendCode("code").appendText(".\n newline");306 EXPECT_EQ(P.asEscapedMarkdown(),307 "after `foobar` bat`no` `space` text `code`. \n newline");308 EXPECT_EQ(P.asMarkdown(),309 "after `foobar` bat`no` `space` text `code`. \n newline");310 EXPECT_EQ(P.asPlainText(), "after foobar batno space text code.\nnewline");311}312 313TEST(Paragraph, SeparationOfChunks2) {314 // This test keeps appending contents to a single Paragraph and checks315 // expected accumulated contents after each one.316 // Purpose is to check for separation between different chunks317 // where the spacing is in the appended string rather set by appendSpace.318 Paragraph P;319 320 P.appendText("after ");321 EXPECT_EQ(P.asEscapedMarkdown(), "after");322 EXPECT_EQ(P.asMarkdown(), "after");323 EXPECT_EQ(P.asPlainText(), "after");324 325 P.appendText("foobar");326 EXPECT_EQ(P.asEscapedMarkdown(), "after foobar");327 EXPECT_EQ(P.asMarkdown(), "after foobar");328 EXPECT_EQ(P.asPlainText(), "after foobar");329 330 P.appendText(" bat");331 EXPECT_EQ(P.asEscapedMarkdown(), "after foobar bat");332 EXPECT_EQ(P.asMarkdown(), "after foobar bat");333 EXPECT_EQ(P.asPlainText(), "after foobar bat");334 335 P.appendText("baz");336 EXPECT_EQ(P.asEscapedMarkdown(), "after foobar batbaz");337 EXPECT_EQ(P.asMarkdown(), "after foobar batbaz");338 EXPECT_EQ(P.asPlainText(), "after foobar batbaz");339 340 P.appendText(" faz ");341 EXPECT_EQ(P.asEscapedMarkdown(), "after foobar batbaz faz");342 EXPECT_EQ(P.asMarkdown(), "after foobar batbaz faz");343 EXPECT_EQ(P.asPlainText(), "after foobar batbaz faz");344 345 P.appendText(" bar ");346 EXPECT_EQ(P.asEscapedMarkdown(), "after foobar batbaz faz bar");347 EXPECT_EQ(P.asMarkdown(), "after foobar batbaz faz bar");348 EXPECT_EQ(P.asPlainText(), "after foobar batbaz faz bar");349 350 P.appendText("qux");351 EXPECT_EQ(P.asEscapedMarkdown(), "after foobar batbaz faz bar qux");352 EXPECT_EQ(P.asMarkdown(), "after foobar batbaz faz bar qux");353 EXPECT_EQ(P.asPlainText(), "after foobar batbaz faz bar qux");354}355 356TEST(Paragraph, SeparationOfChunks3) {357 // This test keeps appending contents to a single Paragraph and checks358 // expected accumulated contents after each one.359 // Purpose is to check for separation between different chunks360 // where the spacing is in the appended string rather set by appendSpace.361 Paragraph P;362 363 P.appendText("after \n");364 EXPECT_EQ(P.asEscapedMarkdown(), "after");365 EXPECT_EQ(P.asMarkdown(), "after");366 EXPECT_EQ(P.asPlainText(), "after");367 368 P.appendText(" foobar\n");369 EXPECT_EQ(P.asEscapedMarkdown(), "after \n foobar");370 EXPECT_EQ(P.asMarkdown(), "after \n foobar");371 EXPECT_EQ(P.asPlainText(), "after\nfoobar");372 373 P.appendText("- bat\n");374 EXPECT_EQ(P.asEscapedMarkdown(), "after \n foobar \n\\- bat");375 EXPECT_EQ(P.asMarkdown(), "after \n foobar\n- bat");376 EXPECT_EQ(P.asPlainText(), "after\nfoobar\n- bat");377 378 P.appendText("- baz");379 EXPECT_EQ(P.asEscapedMarkdown(), "after \n foobar \n\\- bat \n\\- baz");380 EXPECT_EQ(P.asMarkdown(), "after \n foobar\n- bat\n- baz");381 EXPECT_EQ(P.asPlainText(), "after\nfoobar\n- bat\n- baz");382 383 P.appendText(" faz ");384 EXPECT_EQ(P.asEscapedMarkdown(),385 "after \n foobar \n\\- bat \n\\- baz faz");386 EXPECT_EQ(P.asMarkdown(), "after \n foobar\n- bat\n- baz faz");387 EXPECT_EQ(P.asPlainText(), "after\nfoobar\n- bat\n- baz faz");388}389 390TEST(Paragraph, PunctuationLineBreaks) {391 392 struct {393 std::string Text;394 std::string EscapedMarkdown;395 std::string Markdown;396 std::string PlainText;397 } Cases[] = {398 {"Line ending with dot.\nForces a visual linebreak.",399 "Line ending with dot. \nForces a visual linebreak.",400 "Line ending with dot. \nForces a visual linebreak.",401 "Line ending with dot.\nForces a visual linebreak."},402 {"Line ending with colon:\nForces a visual linebreak.",403 "Line ending with colon: \nForces a visual linebreak.",404 "Line ending with colon: \nForces a visual linebreak.",405 "Line ending with colon:\nForces a visual linebreak."},406 {"Line ending with semicolon:\nForces a visual linebreak.",407 "Line ending with semicolon: \nForces a visual linebreak.",408 "Line ending with semicolon: \nForces a visual linebreak.",409 "Line ending with semicolon:\nForces a visual linebreak."},410 {"Line ending with comma,\nForces a visual linebreak.",411 "Line ending with comma, \nForces a visual linebreak.",412 "Line ending with comma, \nForces a visual linebreak.",413 "Line ending with comma,\nForces a visual linebreak."},414 {"Line ending with exclamation mark!\nForces a visual linebreak.",415 "Line ending with exclamation mark! \nForces a visual linebreak.",416 "Line ending with exclamation mark! \nForces a visual linebreak.",417 "Line ending with exclamation mark!\nForces a visual linebreak."},418 {"Line ending with question mark?\nForces a visual linebreak.",419 "Line ending with question mark? \nForces a visual linebreak.",420 "Line ending with question mark? \nForces a visual linebreak.",421 "Line ending with question mark?\nForces a visual linebreak."},422 };423 424 for (const auto &C : Cases) {425 Paragraph P;426 P.appendText(C.Text);427 EXPECT_EQ(P.asEscapedMarkdown(), C.EscapedMarkdown);428 EXPECT_EQ(P.asMarkdown(), C.Markdown);429 EXPECT_EQ(P.asPlainText(), C.PlainText);430 }431}432 433TEST(Paragraph, LineBreakIndicators) {434 435 struct {436 std::string Text;437 std::string EscapedMarkdown;438 std::string Markdown;439 std::string PlainText;440 } Cases[] = {441 {"Visual linebreak for\n- list items\n- and so on",442 "Visual linebreak for \n\\- list items \n\\- and so on",443 "Visual linebreak for\n- list items\n- and so on",444 "Visual linebreak for\n- list items\n- and so on"},445 {"Visual linebreak for\n* list items\n* and so on",446 "Visual linebreak for \n\\* list items \n\\* and so on",447 "Visual linebreak for\n* list items\n* and so on",448 "Visual linebreak for\n* list items\n* and so on"},449 {"Visual linebreak for\n@command any doxygen command\n\\other other "450 "doxygen command",451 "Visual linebreak for \n@command any doxygen command \n\\\\other "452 "other doxygen command",453 "Visual linebreak for \n@command any doxygen command \n\\other other "454 "doxygen command",455 "Visual linebreak for\n@command any doxygen command\n\\other other "456 "doxygen command"},457 {"Visual linebreak for\n>blockquoute line 1\n> blockquoute line 2",458 "Visual linebreak for \n\\>blockquoute line 1 \n\\> blockquoute line "459 "2",460 "Visual linebreak for\n>blockquoute line 1\n> blockquoute line 2",461 "Visual linebreak for\n>blockquoute line 1\n> blockquoute line 2"},462 {"Visual linebreak for\n# Heading 1\ntext under heading\n## Heading "463 "2\ntext under heading 2",464 "Visual linebreak for \n\\# Heading 1\ntext under heading \n\\## "465 "Heading 2\ntext under heading 2",466 "Visual linebreak for\n# Heading 1\ntext under heading\n## Heading "467 "2\ntext under heading 2",468 "Visual linebreak for\n# Heading 1 text under heading\n## Heading 2 "469 "text under heading 2"},470 {"Visual linebreak for\n`inline code`",471 "Visual linebreak for \n\\`inline code\\`",472 "Visual linebreak for\n`inline code`",473 "Visual linebreak for\n`inline code`"},474 };475 476 for (const auto &C : Cases) {477 Paragraph P;478 P.appendText(C.Text);479 EXPECT_EQ(P.asEscapedMarkdown(), C.EscapedMarkdown);480 EXPECT_EQ(P.asMarkdown(), C.Markdown);481 EXPECT_EQ(P.asPlainText(), C.PlainText);482 }483}484 485TEST(Paragraph, ExtraSpaces) {486 // Make sure spaces inside chunks are preserved for markdown487 // and dropped for plain text.488 Paragraph P;489 P.appendText("foo\n \t baz");490 P.appendCode(" bar\n");491 EXPECT_EQ(P.asEscapedMarkdown(), "foo\n \t baz`bar`");492 EXPECT_EQ(P.asMarkdown(), "foo\n \t baz`bar`");493 EXPECT_EQ(P.asPlainText(), "foo bazbar");494}495 496TEST(Paragraph, SpacesCollapsed) {497 Paragraph P;498 P.appendText(" foo bar ");499 P.appendText(" baz ");500 EXPECT_EQ(P.asEscapedMarkdown(), "foo bar baz");501 EXPECT_EQ(P.asMarkdown(), "foo bar baz");502 EXPECT_EQ(P.asPlainText(), "foo bar baz");503}504 505TEST(Paragraph, NewLines) {506 // New lines before and after chunks are dropped.507 Paragraph P;508 P.appendText(" \n foo\nbar\n ");509 P.appendCode(" \n foo\nbar \n ");510 EXPECT_EQ(P.asEscapedMarkdown(), "foo\nbar\n `foo bar`");511 EXPECT_EQ(P.asMarkdown(), "foo\nbar\n `foo bar`");512 EXPECT_EQ(P.asPlainText(), "foo bar foo bar");513}514 515TEST(Paragraph, BoldText) {516 Paragraph P;517 P.appendBoldText("");518 EXPECT_EQ(P.asEscapedMarkdown(), "");519 EXPECT_EQ(P.asMarkdown(), "");520 EXPECT_EQ(P.asPlainText(), "");521 522 P.appendBoldText(" \n foo\nbar\n ");523 EXPECT_EQ(P.asEscapedMarkdown(), "\\*\\*foo bar\\*\\*");524 EXPECT_EQ(P.asMarkdown(), "**foo bar**");525 EXPECT_EQ(P.asPlainText(), "**foo bar**");526 527 P.appendSpace().appendBoldText("foobar");528 EXPECT_EQ(P.asEscapedMarkdown(), "\\*\\*foo bar\\*\\* \\*\\*foobar\\*\\*");529 EXPECT_EQ(P.asMarkdown(), "**foo bar** **foobar**");530 EXPECT_EQ(P.asPlainText(), "**foo bar** **foobar**");531}532 533TEST(Paragraph, EmphasizedText) {534 Paragraph P;535 P.appendEmphasizedText("");536 EXPECT_EQ(P.asEscapedMarkdown(), "");537 EXPECT_EQ(P.asMarkdown(), "");538 EXPECT_EQ(P.asPlainText(), "");539 540 P.appendEmphasizedText(" \n foo\nbar\n ");541 EXPECT_EQ(P.asEscapedMarkdown(), "\\*foo bar\\*");542 EXPECT_EQ(P.asMarkdown(), "*foo bar*");543 EXPECT_EQ(P.asPlainText(), "*foo bar*");544 545 P.appendSpace().appendEmphasizedText("foobar");546 EXPECT_EQ(P.asEscapedMarkdown(), "\\*foo bar\\* \\*foobar\\*");547 EXPECT_EQ(P.asMarkdown(), "*foo bar* *foobar*");548 EXPECT_EQ(P.asPlainText(), "*foo bar* *foobar*");549}550 551TEST(Document, Separators) {552 Document D;553 D.addParagraph().appendText("foo");554 D.addCodeBlock("test");555 D.addParagraph().appendText("bar");556 557 const char ExpectedMarkdown[] = R"md(foo558 559```cpp560test561```562 563bar)md";564 EXPECT_EQ(D.asEscapedMarkdown(), ExpectedMarkdown);565 EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);566 567 const char ExpectedText[] = R"pt(foo568 569test570 571bar)pt";572 EXPECT_EQ(D.asPlainText(), ExpectedText);573}574 575TEST(Document, Ruler) {576 Document D;577 D.addParagraph().appendText("foo");578 D.addRuler();579 580 // Ruler followed by paragraph.581 D.addParagraph().appendText("bar");582 EXPECT_EQ(D.asEscapedMarkdown(), "foo\n\n---\nbar");583 EXPECT_EQ(D.asMarkdown(), "foo\n\n---\nbar");584 EXPECT_EQ(D.asPlainText(), "foo\n\nbar");585 586 D = Document();587 D.addParagraph().appendText("foo");588 D.addRuler();589 D.addCodeBlock("bar");590 // Ruler followed by a codeblock.591 EXPECT_EQ(D.asEscapedMarkdown(), "foo\n\n---\n```cpp\nbar\n```");592 EXPECT_EQ(D.asMarkdown(), "foo\n\n---\n```cpp\nbar\n```");593 EXPECT_EQ(D.asPlainText(), "foo\n\nbar");594 595 // Ruler followed by another ruler596 D = Document();597 D.addParagraph().appendText("foo");598 D.addRuler();599 D.addRuler();600 EXPECT_EQ(D.asEscapedMarkdown(), "foo");601 EXPECT_EQ(D.asMarkdown(), "foo");602 EXPECT_EQ(D.asPlainText(), "foo");603 604 // Multiple rulers between blocks605 D.addRuler();606 D.addParagraph().appendText("foo");607 EXPECT_EQ(D.asEscapedMarkdown(), "foo\n\n---\nfoo");608 EXPECT_EQ(D.asMarkdown(), "foo\n\n---\nfoo");609 EXPECT_EQ(D.asPlainText(), "foo\n\nfoo");610}611 612TEST(Document, Append) {613 Document D;614 D.addParagraph().appendText("foo");615 D.addRuler();616 Document E;617 E.addRuler();618 E.addParagraph().appendText("bar");619 D.append(std::move(E));620 EXPECT_EQ(D.asEscapedMarkdown(), "foo\n\n---\nbar");621 EXPECT_EQ(D.asMarkdown(), "foo\n\n---\nbar");622}623 624TEST(Document, Heading) {625 Document D;626 D.addHeading(1).appendText("foo");627 D.addHeading(2).appendText("bar");628 D.addParagraph().appendText("baz");629 EXPECT_EQ(D.asEscapedMarkdown(), "# foo\n\n## bar\n\nbaz");630 EXPECT_EQ(D.asMarkdown(), "# foo\n\n## bar\n\nbaz");631 EXPECT_EQ(D.asPlainText(), "foo\n\nbar\n\nbaz");632}633 634TEST(CodeBlock, Render) {635 Document D;636 // Code blocks preserves any extra spaces.637 D.addCodeBlock("foo\n bar\n baz");638 639 llvm::StringRef ExpectedMarkdown =640 R"md(```cpp641foo642 bar643 baz644```)md";645 llvm::StringRef ExpectedPlainText =646 R"pt(foo647 bar648 baz)pt";649 EXPECT_EQ(D.asEscapedMarkdown(), ExpectedMarkdown);650 EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);651 EXPECT_EQ(D.asPlainText(), ExpectedPlainText);652 D.addCodeBlock("foo");653 ExpectedMarkdown =654 R"md(```cpp655foo656 bar657 baz658```659 660```cpp661foo662```)md";663 EXPECT_EQ(D.asEscapedMarkdown(), ExpectedMarkdown);664 EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);665 ExpectedPlainText =666 R"pt(foo667 bar668 baz669 670foo)pt";671 EXPECT_EQ(D.asPlainText(), ExpectedPlainText);672 673 Document D2;674 D2.addCodeBlock("");675 EXPECT_EQ(D2.asEscapedMarkdown(), "```cpp\n```");676 EXPECT_EQ(D2.asMarkdown(), "```cpp\n```");677 EXPECT_EQ(D2.asPlainText(), "");678}679 680TEST(BulletList, Render) {681 BulletList L;682 // Flat list683 L.addItem().addParagraph().appendText("foo");684 EXPECT_EQ(L.asEscapedMarkdown(), "- foo");685 EXPECT_EQ(L.asMarkdown(), "- foo");686 EXPECT_EQ(L.asPlainText(), "- foo");687 688 L.addItem().addParagraph().appendText("bar");689 llvm::StringRef Expected = R"md(- foo690- bar)md";691 EXPECT_EQ(L.asEscapedMarkdown(), Expected);692 EXPECT_EQ(L.asMarkdown(), Expected);693 EXPECT_EQ(L.asPlainText(), Expected);694 695 // Nested list, with a single item.696 Document &D = L.addItem();697 // First item with 2 paragraphs - foo\n\n baz698 D.addParagraph().appendText("foo");699 D.addParagraph().appendText("baz");700 701 // Nest one level.702 Document &Inner = D.addBulletList().addItem();703 Inner.addParagraph().appendText("foo");704 705 // Nest one more level.706 BulletList &InnerList = Inner.addBulletList();707 // Single item, baz\nbaz708 Document &DeepDoc = InnerList.addItem();709 DeepDoc.addParagraph().appendText("baz");710 DeepDoc.addParagraph().appendText("baz");711 StringRef ExpectedMarkdown = R"md(- foo712- bar713- foo714 715 baz716 717 - foo718 719 - baz720 721 baz)md";722 EXPECT_EQ(L.asEscapedMarkdown(), ExpectedMarkdown);723 EXPECT_EQ(L.asMarkdown(), ExpectedMarkdown);724 StringRef ExpectedPlainText = R"pt(- foo725- bar726- foo727 728 baz729 730 - foo731 732 - baz733 734 baz)pt";735 EXPECT_EQ(L.asPlainText(), ExpectedPlainText);736 737 // Termination738 Inner.addParagraph().appendText("after");739 ExpectedMarkdown = R"md(- foo740- bar741- foo742 743 baz744 745 - foo746 747 - baz748 749 baz750 751 after)md";752 EXPECT_EQ(L.asEscapedMarkdown(), ExpectedMarkdown);753 EXPECT_EQ(L.asMarkdown(), ExpectedMarkdown);754 ExpectedPlainText = R"pt(- foo755- bar756- foo757 758 baz759 760 - foo761 762 - baz763 764 baz765 766 after)pt";767 EXPECT_EQ(L.asPlainText(), ExpectedPlainText);768}769 770} // namespace771} // namespace markup772} // namespace clangd773} // namespace clang774