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());
43template <
typename T> std::string getArgText(
const T *Command) {
45 for (
unsigned I = 0; I < Command->getNumArgs(); ++I) {
48 ArgText += Command->getArgText(I);
56 :
public comments::ConstCommentVisitor<ParagraphToMarkupDocument> {
59 const comments::CommandTraits &Traits)
60 : Out(Out), Traits(Traits) {}
66 for (
const auto *Child = C->child_begin(); Child != C->child_end();
74 StringRef
Text = C->getText();
75 if (LastChunkEndsWithNewline && C->getText().starts_with(
' '))
78 LastChunkEndsWithNewline = C->hasTrailingNewline();
79 Out.appendText(
Text.str() + (LastChunkEndsWithNewline ?
"\n" :
""));
84 if (C->getNumArgs() > 0) {
85 std::string ArgText = getArgText(C);
87 switch (C->getRenderKind()) {
88 case comments::InlineCommandRenderKind::Monospaced:
89 Out.appendCode(ArgText);
91 case comments::InlineCommandRenderKind::Bold:
92 Out.appendBoldText(ArgText);
94 case comments::InlineCommandRenderKind::Emphasized:
95 Out.appendEmphasizedText(ArgText);
98 commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(),
103 if (C->getCommandName(Traits) ==
"n") {
105 Out.appendText(
" \n");
106 LastChunkEndsWithNewline =
true;
110 commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(),
112 LastChunkEndsWithNewline =
false;
117 std::string TagText =
"<" + STC->getTagName().str();
119 for (
unsigned I = 0; I < STC->getNumAttrs(); ++I) {
120 const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I);
121 TagText +=
" " + Attr.Name.str() +
"=\"" + Attr.Value.str() +
"\"";
124 if (STC->isSelfClosing())
128 LastChunkEndsWithNewline = STC->hasTrailingNewline();
129 Out.appendText(TagText + (LastChunkEndsWithNewline ?
"\n" :
""));
133 LastChunkEndsWithNewline = ETC->hasTrailingNewline();
134 Out.appendText(
"</" + ETC->getTagName().str() +
">" +
135 (LastChunkEndsWithNewline ?
"\n" :
""));
140 const comments::CommandTraits &Traits;
144 bool LastChunkEndsWithNewline =
true;
148 :
public comments::ConstCommentVisitor<ParagraphToString> {
151 const comments::CommandTraits &Traits)
152 : Out(Out), Traits(Traits) {}
158 for (
const auto *Child = C->child_begin(); Child != C->child_end();
166 if (C->hasTrailingNewline())
171 Out << commandMarkerAsString(C->getCommandMarker());
172 Out << C->getCommandName(Traits);
173 std::string ArgText = getArgText(C);
174 if (!ArgText.empty())
175 Out <<
" " << ArgText;
179 Out <<
"<" << STC->getTagName().str();
181 for (
unsigned I = 0; I < STC->getNumAttrs(); ++I) {
182 const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I);
183 Out <<
" " << Attr.Name.str();
184 if (!Attr.Value.str().empty())
185 Out <<
"=\"" << Attr.Value.str() <<
"\"";
188 if (STC->isSelfClosing())
192 Out << (STC->hasTrailingNewline() ?
"\n" :
"");
196 Out <<
"</" << ETC->getTagName().str() <<
">"
197 << (ETC->hasTrailingNewline() ?
"\n" :
"");
201 llvm::raw_string_ostream &Out;
202 const comments::CommandTraits &Traits;
206 :
public comments::ConstCommentVisitor<BlockCommentToMarkupDocument> {
209 const comments::CommandTraits &Traits)
210 : Out(Out), Traits(Traits) {}
214 switch (B->getCommandID()) {
215 case comments::CommandTraits::KCI_arg:
216 case comments::CommandTraits::KCI_li:
221 .visit(B->getParagraph());
223 case comments::CommandTraits::KCI_note:
224 case comments::CommandTraits::KCI_warning:
225 commandToHeadedParagraph(B);
227 case comments::CommandTraits::KCI_retval: {
232 auto &P = Out.addParagraph().appendCode(getArgText(B));
233 if (B->getParagraph() && !B->getParagraph()->isWhitespace()) {
239 case comments::CommandTraits::KCI_details: {
244 if (B->getParagraph() && !B->getParagraph()->isWhitespace()) {
246 .visit(B->getParagraph());
254 std::string ArgText = getArgText(B);
255 auto &P = Out.addParagraph();
256 commandToMarkup(P, B->getCommandName(Traits), B->getCommandMarker(),
258 if (B->getParagraph() && !B->getParagraph()->isWhitespace()) {
267 std::string CodeLang =
"";
268 auto *FirstLine = VB->child_begin();
273 if (VB->getNumLines() > 0) {
274 if (
const auto *Line =
275 cast<comments::VerbatimBlockLineComment>(*FirstLine)) {
276 llvm::StringRef
Text = Line->getText();
278 if (llvm::none_of(
Text, llvm::isSpace) &&
Text.consume_front(
"{") &&
279 Text.consume_back(
"}")) {
282 Text.consume_front(
".");
284 CodeLang =
Text.take_while([](
char C) {
285 return llvm::isAlnum(C) || C ==
'+';
294 std::string CodeBlockText;
296 for (
const auto *LI = FirstLine; LI != VB->child_end(); ++LI) {
297 if (
const auto *Line = cast<comments::VerbatimBlockLineComment>(*LI)) {
298 CodeBlockText += Line->getText().str() +
"\n";
302 Out.addCodeBlock(CodeBlockText, CodeLang);
308 if (VB->getCommandID() == comments::CommandTraits::KCI_code) {
313 commandToMarkup(Out.addParagraph(), VB->getCommandName(Traits),
314 VB->getCommandMarker(),
"");
316 std::string VerbatimText;
318 for (
const auto *LI = VB->child_begin(); LI != VB->child_end(); ++LI) {
319 if (
const auto *Line = cast<comments::VerbatimBlockLineComment>(*LI)) {
320 VerbatimText += Line->getText().str() +
"\n";
324 Out.addCodeBlock(VerbatimText,
"");
326 commandToMarkup(Out.addParagraph(), VB->getCloseName(),
327 VB->getCommandMarker(),
"");
331 auto &P = Out.addParagraph();
332 commandToMarkup(P, VL->getCommandName(Traits), VL->getCommandMarker(),
"");
333 P.appendSpace().appendCode(VL->getText().str(),
true).appendSpace();
337 markup::Document &Out;
338 const comments::CommandTraits &Traits;
339 StringRef CommentEscapeMarker;
343 void commandToHeadedParagraph(
const comments::BlockCommandComment *B) {
344 auto &P = Out.addParagraph();
345 std::string
Heading = B->getCommandName(Traits).slice(0, 1).upper() +
346 B->getCommandName(Traits).drop_front().str();
347 P.appendBoldText(
Heading +
":");
358 std::string CodeFence;
360 llvm::raw_string_ostream OS(CommentWithMarkers);
375 bool InCodeSpan =
false;
377 llvm::StringRef Line, Rest;
378 for (std::tie(Line, Rest) = Doc.split(
'\n'); !(Line.empty() && Rest.empty());
379 std::tie(Line, Rest) = Rest.split(
'\n')) {
382 if (State == Normal) {
383 llvm::StringRef Trimmed = Line.ltrim();
384 if (Trimmed.starts_with(
"```") || Trimmed.starts_with(
"~~~")) {
387 Trimmed.take_while([](
char C) {
return C ==
'`' || C ==
'~'; })
391 llvm::StringRef AfterFence =
392 Trimmed.drop_front(CodeFence.size()).ltrim();
395 AfterFence.consume_front(
"{");
397 StringRef CodeLang = AfterFence.take_while(
398 [](
char C) {
return llvm::isAlnum(C) || C ==
'.' || C ==
'+'; });
402 if (!CodeLang.empty())
403 OS <<
"{" << CodeLang.str() <<
"}";
407 State = FencedCodeblock;
415 }
else if (State == FencedCodeblock) {
417 if (Line.ltrim().starts_with(CodeFence)) {
418 OS <<
"///@endcode\n";
422 OS <<
"///" << Line <<
"\n";
429 if (Line.empty() || Line.trim().empty()) {
436 if (Line.starts_with(
"<"))
441 for (
char C : Line) {
443 InCodeSpan = !InCodeSpan;
444 else if (InCodeSpan && (C ==
'@' || C ==
'\\'))
453 if (State == FencedCodeblock)
454 OS <<
"///@endcode\n";
458 const comments::BlockCommandComment *B) {
459 switch (B->getCommandID()) {
460 case comments::CommandTraits::KCI_brief: {
461 if (!BriefParagraph) {
462 BriefParagraph = B->getParagraph();
467 case comments::CommandTraits::KCI_return:
468 case comments::CommandTraits::KCI_returns:
469 if (!ReturnParagraph) {
470 ReturnParagraph = B->getParagraph();
474 case comments::CommandTraits::KCI_retval:
478 if (B->getNumArgs() == 1)
479 RetvalCommands.push_back(B);
487 BlockCommands[CommentPartIndex] = B;
498 if (!ReturnParagraph)
505 if (ParamName.empty())
508 if (
const auto *P = Parameters.lookup(ParamName)) {
514 StringRef ParamName, llvm::raw_string_ostream &Out)
const {
515 if (ParamName.empty())
518 if (
const auto *P = Parameters.lookup(ParamName)) {
524 for (
unsigned I = 0; I < CommentPartIndex; ++I) {
525 if (
const auto *BC = BlockCommands.lookup(I)) {
527 }
else if (
const auto *P = FreeParagraphs.lookup(I)) {
535 if (TemplateParamName.empty())
538 if (
const auto *TP = TemplateParameters.lookup(TemplateParamName)) {
544 StringRef TemplateParamName, llvm::raw_string_ostream &Out)
const {
545 if (TemplateParamName.empty())
548 if (
const auto *P = TemplateParameters.lookup(TemplateParamName)) {
554 if (RetvalCommands.empty())
556 markup::BulletList &BL = Out.addBulletList();
557 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++ -*-===//