9 #include "llvm/ADT/ArrayRef.h"
10 #include "llvm/ADT/STLExtras.h"
11 #include "llvm/ADT/SmallVector.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/Support/Compiler.h"
15 #include "llvm/Support/ErrorHandling.h"
16 #include "llvm/Support/FormatVariadic.h"
17 #include "llvm/Support/raw_ostream.h"
32 bool looksLikeTag(llvm::StringRef
Contents) {
39 if (!llvm::isAlpha(
Contents.front()))
43 .drop_while([](
char C) {
44 return llvm::isAlnum(C) || C ==
'-' || C ==
'_' || C ==
':';
46 .drop_while(llvm::isSpace);
69 bool needsLeadingEscape(
char C, llvm::StringRef Before, llvm::StringRef After,
71 assert(Before.take_while(llvm::isSpace).empty());
72 auto RulerLength = [&]() ->
unsigned {
73 if (!StartsLine || !Before.empty())
75 llvm::StringRef A = After.rtrim();
76 return llvm::all_of(A, [C](
char D) {
return C == D; }) ? 1 + A.size() : 0;
78 auto IsBullet = [&]() {
79 return StartsLine && Before.empty() &&
80 (After.empty() || After.startswith(
" "));
82 auto SpaceSurrounds = [&]() {
83 return (After.empty() || llvm::isSpace(After.front())) &&
84 (Before.empty() || llvm::isSpace(Before.back()));
86 auto WordSurrounds = [&]() {
87 return (!After.empty() && llvm::isAlnum(After.front())) &&
88 (!Before.empty() && llvm::isAlnum(Before.back()));
99 return StartsLine && Before.empty() && After.startswith(
"~~");
101 if (!StartsLine || !Before.empty())
103 llvm::StringRef Rest = After.ltrim(C);
104 return Rest.empty() || Rest.startswith(
" ");
114 return After.startswith(
":") || After.startswith(
"(");
116 return RulerLength() > 0;
118 if (RulerLength() >= 3)
122 return !(SpaceSurrounds() || WordSurrounds());
124 if (RulerLength() > 0)
130 return IsBullet() || RulerLength() >= 3 || !SpaceSurrounds();
132 return looksLikeTag(After);
134 return StartsLine && Before.empty();
136 auto End = After.find(
';');
137 if (End == llvm::StringRef::npos)
139 llvm::StringRef Content = After.substr(0, End);
140 if (Content.consume_front(
"#")) {
141 if (Content.consume_front(
"x") || Content.consume_front(
"X"))
142 return llvm::all_of(Content, llvm::isHexDigit);
143 return llvm::all_of(Content, llvm::isDigit);
145 return llvm::all_of(Content, llvm::isAlpha);
149 return StartsLine && !Before.empty() &&
150 llvm::all_of(Before, llvm::isDigit) && After.startswith(
" ");
158 std::string renderText(llvm::StringRef Input,
bool StartsLine) {
160 for (
unsigned I = 0; I < Input.size(); ++I) {
161 if (needsLeadingEscape(Input[I], Input.substr(0, I), Input.substr(I + 1),
164 R.push_back(Input[I]);
171 std::string renderInlineBlock(llvm::StringRef Input) {
174 for (
size_t From = 0; From < Input.size();) {
175 size_t Next = Input.find(
"`", From);
176 R += Input.substr(From, Next - From);
177 if (Next == llvm::StringRef::npos)
185 if (llvm::StringRef(R).startswith(
"`") || llvm::StringRef(R).endswith(
"`"))
186 return "` " + std::move(R) +
" `";
190 if (llvm::StringRef(R).startswith(
" ") && llvm::StringRef(R).endswith(
" "))
191 return "` " + std::move(R) +
" `";
192 return "`" + std::move(R) +
"`";
198 std::string getMarkerForCodeBlock(llvm::StringRef Input) {
201 unsigned MaxBackticks = 0;
202 unsigned Backticks = 0;
203 for (
char C : Input) {
208 MaxBackticks = std::max(MaxBackticks, Backticks);
211 MaxBackticks = std::max(Backticks, MaxBackticks);
213 return std::string(std::max(3u, MaxBackticks + 1),
'`');
217 std::string canonicalizeSpaces(llvm::StringRef Input) {
218 llvm::SmallVector<llvm::StringRef, 4> Words;
219 llvm::SplitString(Input, Words);
223 std::string renderBlocks(llvm::ArrayRef<std::unique_ptr<Block>>
Children,
224 void (
Block::*RenderFunc)(llvm::raw_ostream &)
const) {
226 llvm::raw_string_ostream
OS(R);
230 [](
const std::unique_ptr<Block> &C) {
return C->isRuler(); });
231 auto Last = llvm::find_if(
233 [](
const std::unique_ptr<Block> &C) {
return !C->isRuler(); });
236 bool LastBlockWasRuler =
true;
238 if (C->isRuler() && LastBlockWasRuler)
240 LastBlockWasRuler = C->isRuler();
241 ((*C).*RenderFunc)(
OS);
246 std::string AdjustedResult;
247 llvm::StringRef TrimmedText(
OS.str());
248 TrimmedText = TrimmedText.trim();
250 llvm::copy_if(TrimmedText, std::back_inserter(AdjustedResult),
251 [&TrimmedText](
const char &C) {
252 return !llvm::StringRef(TrimmedText.data(),
253 &C - TrimmedText.data() + 1)
258 return AdjustedResult;
264 class Ruler :
public Block {
266 void renderMarkdown(llvm::raw_ostream &
OS)
const override {
271 void renderPlainText(llvm::raw_ostream &
OS)
const override {
OS <<
'\n'; }
272 std::unique_ptr<Block> clone()
const override {
273 return std::make_unique<Ruler>(*
this);
275 bool isRuler()
const override {
return true; }
278 class CodeBlock :
public Block {
280 void renderMarkdown(llvm::raw_ostream &
OS)
const override {
281 std::string Marker = getMarkerForCodeBlock(
Contents);
283 OS << Marker << Language <<
'\n' <<
Contents <<
'\n' << Marker <<
'\n';
286 void renderPlainText(llvm::raw_ostream &
OS)
const override {
291 std::unique_ptr<Block> clone()
const override {
292 return std::make_unique<CodeBlock>(*
this);
295 CodeBlock(std::string
Contents, std::string Language)
300 std::string Language;
305 std::string indentLines(llvm::StringRef Input) {
306 assert(!Input.endswith(
"\n") &&
"Input should've been trimmed.");
307 std::string IndentedR;
309 IndentedR.reserve(Input.size() + Input.count(
'\n') * 2);
310 for (
char C : Input) {
313 IndentedR.append(
" ");
320 Heading(
size_t Level) : Level(Level) {}
321 void renderMarkdown(llvm::raw_ostream &
OS)
const override {
322 OS << std::string(Level,
'#') <<
' ';
334 llvm::raw_string_ostream
OS(R);
336 return llvm::StringRef(
OS.str()).trim().str();
341 llvm::raw_string_ostream
OS(R);
343 return llvm::StringRef(
OS.str()).trim().str();
347 bool NeedsSpace =
false;
348 bool HasChunks =
false;
349 for (
auto &C : Chunks) {
350 if (C.SpaceBefore || NeedsSpace)
354 OS << renderText(C.Contents, !HasChunks);
356 case Chunk::InlineCode:
357 OS << renderInlineBlock(C.Contents);
361 NeedsSpace = C.SpaceAfter;
370 return std::make_unique<Paragraph>(*
this);
376 llvm::StringRef Text) {
378 for (llvm::StringRef S : Options)
379 if (
Text.find_first_of(S) == llvm::StringRef::npos)
381 return Options.front();
385 bool NeedsSpace =
false;
386 for (
auto &C : Chunks) {
387 if (C.SpaceBefore || NeedsSpace)
389 llvm::StringRef Marker =
"";
390 if (C.Preserve && C.Kind == Chunk::InlineCode)
392 OS << Marker << C.Contents << Marker;
393 NeedsSpace = C.SpaceAfter;
399 for (
auto &D : Items) {
402 OS <<
"- " << indentLines(D.asMarkdown()) <<
'\n';
409 for (
auto &D : Items) {
412 OS <<
"- " << indentLines(D.asPlainText()) <<
'\n';
418 Chunks.back().SpaceAfter =
true;
423 std::string Norm = canonicalizeSpaces(
Text);
426 Chunks.emplace_back();
427 Chunk &C = Chunks.back();
428 C.Contents = std::move(Norm);
430 C.SpaceBefore = llvm::isSpace(
Text.front());
431 C.SpaceAfter = llvm::isSpace(
Text.back());
437 !Chunks.empty() && Chunks.back().Kind == Chunk::InlineCode;
438 std::string Norm = canonicalizeSpaces(std::move(
Code));
441 Chunks.emplace_back();
442 Chunk &C = Chunks.back();
443 C.Contents = std::move(Norm);
444 C.Kind = Chunk::InlineCode;
445 C.Preserve = Preserve;
447 C.SpaceBefore = AdjacentCode;
452 return std::make_unique<BulletList>(*
this);
456 Items.emplace_back();
462 for (
const auto &C : Other.Children)
468 std::move(Other.Children.begin(), Other.Children.end(),
473 Children.push_back(std::make_unique<Paragraph>());
474 return *static_cast<Paragraph *>(
Children.back().get());
481 std::make_unique<CodeBlock>(std::move(
Code), std::move(Language)));
493 Children.emplace_back(std::make_unique<BulletList>());
494 return *static_cast<BulletList *>(
Children.back().get());
499 Children.emplace_back(std::make_unique<Heading>(Level));
500 return *static_cast<Paragraph *>(
Children.back().get());