12#include "clang/AST/Comment.h"
13#include "clang/AST/CommentCommandTraits.h"
14#include "clang/AST/CommentVisitor.h"
15#include "llvm/ADT/DenseMap.h"
16#include "llvm/ADT/StringExtras.h"
17#include "llvm/ADT/StringRef.h"
23std::string commandMarkerAsString(comments::CommandMarkerKind CommandMarker) {
24 switch (CommandMarker) {
25 case comments::CommandMarkerKind::CMK_At:
27 case comments::CommandMarkerKind::CMK_Backslash:
30 llvm_unreachable(
"Unknown command marker kind");
34 comments::CommandMarkerKind CommandMarker,
36 Out.appendBoldText(commandMarkerAsString(CommandMarker) + Command.str());
39 Out.appendCode(Args.str());
42template <
typename T> std::string getArgText(
const T *Command) {
44 for (
unsigned I = 0; I < Command->getNumArgs(); ++I) {
47 ArgText += Command->getArgText(I);
55 :
public comments::ConstCommentVisitor<ParagraphToMarkupDocument> {
58 const comments::CommandTraits &Traits)
59 : Out(Out), Traits(Traits) {}
65 for (
const auto *Child = C->child_begin(); Child != C->child_end();
73 StringRef
Text = C->getText();
74 if (LastChunkEndsWithNewline && C->getText().starts_with(
' '))
77 LastChunkEndsWithNewline = C->hasTrailingNewline();
78 Out.appendText(
Text.str() + (LastChunkEndsWithNewline ?
"\n" :
""));
83 if (C->getNumArgs() > 0) {
84 std::string ArgText = getArgText(C);
86 switch (C->getRenderKind()) {
87 case comments::InlineCommandRenderKind::Monospaced:
88 Out.appendCode(ArgText);
90 case comments::InlineCommandRenderKind::Bold:
91 Out.appendBoldText(ArgText);
93 case comments::InlineCommandRenderKind::Emphasized:
94 Out.appendEmphasizedText(ArgText);
97 commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(),
102 if (C->getCommandName(Traits) ==
"n") {
104 Out.appendText(
" \n");
105 LastChunkEndsWithNewline =
true;
109 commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(),
115 std::string TagText =
"<" + STC->getTagName().str();
117 for (
unsigned I = 0; I < STC->getNumAttrs(); ++I) {
118 const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I);
119 TagText +=
" " + Attr.Name.str() +
"=\"" + Attr.Value.str() +
"\"";
122 if (STC->isSelfClosing())
126 LastChunkEndsWithNewline = STC->hasTrailingNewline();
127 Out.appendText(TagText + (LastChunkEndsWithNewline ?
"\n" :
""));
131 LastChunkEndsWithNewline = ETC->hasTrailingNewline();
132 Out.appendText(
"</" + ETC->getTagName().str() +
">" +
133 (LastChunkEndsWithNewline ?
"\n" :
""));
138 const comments::CommandTraits &Traits;
142 bool LastChunkEndsWithNewline =
true;
146 :
public comments::ConstCommentVisitor<ParagraphToString> {
149 const comments::CommandTraits &Traits)
150 : Out(Out), Traits(Traits) {}
156 for (
const auto *Child = C->child_begin(); Child != C->child_end();
165 Out << commandMarkerAsString(C->getCommandMarker());
166 Out << C->getCommandName(Traits);
167 std::string ArgText = getArgText(C);
168 if (!ArgText.empty())
169 Out <<
" " << ArgText;
174 Out <<
"<" << STC->getTagName().str();
176 for (
unsigned I = 0; I < STC->getNumAttrs(); ++I) {
177 const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I);
178 Out <<
" " << Attr.Name.str();
179 if (!Attr.Value.str().empty())
180 Out <<
"=\"" << Attr.Value.str() <<
"\"";
183 if (STC->isSelfClosing())
187 Out << (STC->hasTrailingNewline() ?
"\n" :
"");
191 Out <<
"</" << ETC->getTagName().str() <<
">"
192 << (ETC->hasTrailingNewline() ?
"\n" :
"");
196 llvm::raw_string_ostream &Out;
197 const comments::CommandTraits &Traits;
201 :
public comments::ConstCommentVisitor<BlockCommentToMarkupDocument> {
204 const comments::CommandTraits &Traits)
205 : Out(Out), Traits(Traits) {}
209 switch (B->getCommandID()) {
210 case comments::CommandTraits::KCI_arg:
211 case comments::CommandTraits::KCI_li:
216 .visit(B->getParagraph());
218 case comments::CommandTraits::KCI_note:
219 case comments::CommandTraits::KCI_warning:
220 commandToHeadedParagraph(B);
222 case comments::CommandTraits::KCI_retval: {
227 auto &P = Out.addParagraph().appendCode(getArgText(B));
228 if (B->getParagraph() && !B->getParagraph()->isWhitespace()) {
234 case comments::CommandTraits::KCI_details: {
239 if (B->getParagraph() && !B->getParagraph()->isWhitespace()) {
241 .visit(B->getParagraph());
249 std::string ArgText = getArgText(B);
250 auto &P = Out.addParagraph();
251 commandToMarkup(P, B->getCommandName(Traits), B->getCommandMarker(),
253 if (B->getParagraph() && !B->getParagraph()->isWhitespace()) {
256 if (!ArgText.empty())
265 std::string CodeLang =
"";
266 auto *FirstLine = VB->child_begin();
271 if (VB->getNumLines() > 0) {
272 if (
const auto *Line =
273 cast<comments::VerbatimBlockLineComment>(*FirstLine)) {
274 llvm::StringRef
Text = Line->getText();
276 if (llvm::none_of(
Text, llvm::isSpace) &&
Text.consume_front(
"{") &&
277 Text.consume_back(
"}")) {
280 Text.consume_front(
".");
282 CodeLang =
Text.take_while([](
char C) {
283 return llvm::isAlnum(C) || C ==
'+';
292 std::string CodeBlockText;
294 for (
const auto *LI = FirstLine; LI != VB->child_end(); ++LI) {
295 if (
const auto *Line = cast<comments::VerbatimBlockLineComment>(*LI)) {
296 CodeBlockText += Line->getText().str() +
"\n";
300 Out.addCodeBlock(CodeBlockText, CodeLang);
306 if (VB->getCommandID() == comments::CommandTraits::KCI_code) {
311 commandToMarkup(Out.addParagraph(), VB->getCommandName(Traits),
312 VB->getCommandMarker(),
"");
314 std::string VerbatimText;
316 for (
const auto *LI = VB->child_begin(); LI != VB->child_end(); ++LI) {
317 if (
const auto *Line = cast<comments::VerbatimBlockLineComment>(*LI)) {
318 VerbatimText += Line->getText().str() +
"\n";
322 Out.addCodeBlock(VerbatimText,
"");
324 commandToMarkup(Out.addParagraph(), VB->getCloseName(),
325 VB->getCommandMarker(),
"");
329 auto &P = Out.addParagraph();
330 commandToMarkup(P, VL->getCommandName(Traits), VL->getCommandMarker(),
"");
331 P.appendSpace().appendCode(VL->getText().str(),
true).appendSpace();
335 markup::Document &Out;
336 const comments::CommandTraits &Traits;
337 StringRef CommentEscapeMarker;
341 void commandToHeadedParagraph(
const comments::BlockCommandComment *B) {
342 auto &P = Out.addParagraph();
343 std::string
Heading = B->getCommandName(Traits).slice(0, 1).upper() +
344 B->getCommandName(Traits).drop_front().str();
345 P.appendBoldText(
Heading +
":");
356 std::string CodeFence;
358 llvm::raw_string_ostream OS(CommentWithMarkers);
373 bool InCodeSpan =
false;
375 llvm::StringRef Line, Rest;
376 for (std::tie(Line, Rest) = Doc.split(
'\n'); !(Line.empty() && Rest.empty());
377 std::tie(Line, Rest) = Rest.split(
'\n')) {
380 if (State == Normal) {
381 llvm::StringRef Trimmed = Line.ltrim();
382 if (Trimmed.starts_with(
"```") || Trimmed.starts_with(
"~~~")) {
385 Trimmed.take_while([](
char C) {
return C ==
'`' || C ==
'~'; })
389 llvm::StringRef AfterFence =
390 Trimmed.drop_front(CodeFence.size()).ltrim();
393 AfterFence.consume_front(
"{");
395 StringRef CodeLang = AfterFence.take_while(
396 [](
char C) {
return llvm::isAlnum(C) || C ==
'.' || C ==
'+'; });
400 if (!CodeLang.empty())
401 OS <<
"{" << CodeLang.str() <<
"}";
405 State = FencedCodeblock;
413 }
else if (State == FencedCodeblock) {
415 if (Line.ltrim().starts_with(CodeFence)) {
416 OS <<
"///@endcode\n";
420 OS <<
"///" << Line <<
"\n";
427 if (Line.empty() || Line.trim().empty()) {
434 if (Line.starts_with(
"<"))
439 for (
char C : Line) {
441 InCodeSpan = !InCodeSpan;
442 else if (InCodeSpan && (C ==
'@' || C ==
'\\'))
451 if (State == FencedCodeblock)
452 OS <<
"///@endcode\n";
456 const comments::BlockCommandComment *B) {
457 switch (B->getCommandID()) {
458 case comments::CommandTraits::KCI_brief: {
459 if (!BriefParagraph) {
460 BriefParagraph = B->getParagraph();
465 case comments::CommandTraits::KCI_return:
466 case comments::CommandTraits::KCI_returns:
467 if (!ReturnParagraph) {
468 ReturnParagraph = B->getParagraph();
472 case comments::CommandTraits::KCI_retval:
476 if (B->getNumArgs() == 1)
477 RetvalCommands.push_back(B);
485 BlockCommands[CommentPartIndex] = B;
496 if (!ReturnParagraph)
503 if (ParamName.empty())
506 if (
const auto *P = Parameters.lookup(ParamName)) {
512 StringRef ParamName, llvm::raw_string_ostream &Out)
const {
513 if (ParamName.empty())
516 if (
const auto *P = Parameters.lookup(ParamName)) {
522 for (
unsigned I = 0; I < CommentPartIndex; ++I) {
523 if (
const auto *BC = BlockCommands.lookup(I)) {
525 }
else if (
const auto *P = FreeParagraphs.lookup(I)) {
533 if (TemplateParamName.empty())
536 if (
const auto *TP = TemplateParameters.lookup(TemplateParamName)) {
542 StringRef TemplateParamName, llvm::raw_string_ostream &Out)
const {
543 if (TemplateParamName.empty())
546 if (
const auto *P = TemplateParameters.lookup(TemplateParamName)) {
552 if (RetvalCommands.empty())
554 markup::BulletList &BL = Out.addBulletList();
555 for (
const auto *P : RetvalCommands) {
void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC)
void visitTextComment(const comments::TextComment *C)
void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC)
void visitParagraphComment(const comments::ParagraphComment *C)
void visitInlineCommandComment(const comments::InlineCommandComment *C)
ParagraphToMarkupDocument(markup::Paragraph &Out, const comments::CommandTraits &Traits)
void visitInlineCommandComment(const comments::InlineCommandComment *C)
void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC)
void visitParagraphComment(const comments::ParagraphComment *C)
ParagraphToString(llvm::raw_string_ostream &Out, const comments::CommandTraits &Traits)
void visitTextComment(const comments::TextComment *C)
void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC)
Represents parts of the markup that can contain strings, like inline code, code block or plain text.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//