blob: fe6fddb795999d3547e74f505dbf9276eb9c6249 [file] [log] [blame]
Dmitri Gribenkoae99b752012-07-20 21:34:34 +00001//===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines all libclang APIs related to walking comment AST.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang-c/Index.h"
15#include "CXString.h"
16#include "CXComment.h"
17
18#include "clang/AST/CommentVisitor.h"
19
20#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000021#include "llvm/Support/raw_ostream.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000022
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000023#include <climits>
24
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000025using namespace clang;
26using namespace clang::cxstring;
27using namespace clang::comments;
28using namespace clang::cxcomment;
29
30extern "C" {
31
32enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
33 const Comment *C = getASTNode(CXC);
34 if (!C)
35 return CXComment_Null;
36
37 switch (C->getCommentKind()) {
38 case Comment::NoCommentKind:
39 return CXComment_Null;
40
41 case Comment::TextCommentKind:
42 return CXComment_Text;
43
44 case Comment::InlineCommandCommentKind:
45 return CXComment_InlineCommand;
46
47 case Comment::HTMLStartTagCommentKind:
48 return CXComment_HTMLStartTag;
49
50 case Comment::HTMLEndTagCommentKind:
51 return CXComment_HTMLEndTag;
52
53 case Comment::ParagraphCommentKind:
54 return CXComment_Paragraph;
55
56 case Comment::BlockCommandCommentKind:
57 return CXComment_BlockCommand;
58
59 case Comment::ParamCommandCommentKind:
60 return CXComment_ParamCommand;
61
Dmitri Gribenko96b09862012-07-31 22:37:06 +000062 case Comment::TParamCommandCommentKind:
63 return CXComment_TParamCommand;
64
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000065 case Comment::VerbatimBlockCommentKind:
66 return CXComment_VerbatimBlockCommand;
67
68 case Comment::VerbatimBlockLineCommentKind:
69 return CXComment_VerbatimBlockLine;
70
71 case Comment::VerbatimLineCommentKind:
72 return CXComment_VerbatimLine;
73
74 case Comment::FullCommentKind:
75 return CXComment_FullComment;
76 }
77 llvm_unreachable("unknown CommentKind");
78}
79
80unsigned clang_Comment_getNumChildren(CXComment CXC) {
81 const Comment *C = getASTNode(CXC);
82 if (!C)
83 return 0;
84
85 return C->child_count();
86}
87
88CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
89 const Comment *C = getASTNode(CXC);
90 if (!C || ChildIdx >= C->child_count())
91 return createCXComment(NULL);
92
93 return createCXComment(*(C->child_begin() + ChildIdx));
94}
95
96unsigned clang_Comment_isWhitespace(CXComment CXC) {
97 const Comment *C = getASTNode(CXC);
98 if (!C)
99 return false;
100
101 if (const TextComment *TC = dyn_cast<TextComment>(C))
102 return TC->isWhitespace();
103
104 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
105 return PC->isWhitespace();
106
107 return false;
108}
109
110unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
111 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
112 if (!ICC)
113 return false;
114
115 return ICC->hasTrailingNewline();
116}
117
118CXString clang_TextComment_getText(CXComment CXC) {
119 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
120 if (!TC)
121 return createCXString((const char *) 0);
122
123 return createCXString(TC->getText(), /*DupString=*/ false);
124}
125
126CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
127 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
128 if (!ICC)
129 return createCXString((const char *) 0);
130
131 return createCXString(ICC->getCommandName(), /*DupString=*/ false);
132}
133
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000134enum CXCommentInlineCommandRenderKind
135clang_InlineCommandComment_getRenderKind(CXComment CXC) {
136 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
137 if (!ICC)
138 return CXCommentInlineCommandRenderKind_Normal;
139
140 switch (ICC->getRenderKind()) {
141 case InlineCommandComment::RenderNormal:
142 return CXCommentInlineCommandRenderKind_Normal;
143
144 case InlineCommandComment::RenderBold:
145 return CXCommentInlineCommandRenderKind_Bold;
146
147 case InlineCommandComment::RenderMonospaced:
148 return CXCommentInlineCommandRenderKind_Monospaced;
149
150 case InlineCommandComment::RenderEmphasized:
151 return CXCommentInlineCommandRenderKind_Emphasized;
152 }
153 llvm_unreachable("unknown InlineCommandComment::RenderKind");
154}
155
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000156unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
157 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
158 if (!ICC)
159 return 0;
160
161 return ICC->getNumArgs();
162}
163
164CXString clang_InlineCommandComment_getArgText(CXComment CXC,
165 unsigned ArgIdx) {
166 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
167 if (!ICC || ArgIdx >= ICC->getNumArgs())
168 return createCXString((const char *) 0);
169
170 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
171}
172
173CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
174 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
175 if (!HTC)
176 return createCXString((const char *) 0);
177
178 return createCXString(HTC->getTagName(), /*DupString=*/ false);
179}
180
181unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
182 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
183 if (!HST)
184 return false;
185
186 return HST->isSelfClosing();
187}
188
189unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
190 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
191 if (!HST)
192 return 0;
193
194 return HST->getNumAttrs();
195}
196
197CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
198 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
199 if (!HST || AttrIdx >= HST->getNumAttrs())
200 return createCXString((const char *) 0);
201
202 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
203}
204
205CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
206 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
207 if (!HST || AttrIdx >= HST->getNumAttrs())
208 return createCXString((const char *) 0);
209
210 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
211}
212
213CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
214 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
215 if (!BCC)
216 return createCXString((const char *) 0);
217
218 return createCXString(BCC->getCommandName(), /*DupString=*/ false);
219}
220
221unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
222 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
223 if (!BCC)
224 return 0;
225
226 return BCC->getNumArgs();
227}
228
229CXString clang_BlockCommandComment_getArgText(CXComment CXC,
230 unsigned ArgIdx) {
231 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
232 if (!BCC || ArgIdx >= BCC->getNumArgs())
233 return createCXString((const char *) 0);
234
235 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
236}
237
238CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
239 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
240 if (!BCC)
241 return createCXComment(NULL);
242
243 return createCXComment(BCC->getParagraph());
244}
245
246CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
247 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000248 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000249 return createCXString((const char *) 0);
250
251 return createCXString(PCC->getParamName(), /*DupString=*/ false);
252}
253
254unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
255 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
256 if (!PCC)
257 return false;
258
259 return PCC->isParamIndexValid();
260}
261
262unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
263 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000264 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000265 return ParamCommandComment::InvalidParamIndex;
266
267 return PCC->getParamIndex();
268}
269
270unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
271 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
272 if (!PCC)
273 return false;
274
275 return PCC->isDirectionExplicit();
276}
277
278enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
279 CXComment CXC) {
280 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
281 if (!PCC)
282 return CXCommentParamPassDirection_In;
283
284 switch (PCC->getDirection()) {
285 case ParamCommandComment::In:
286 return CXCommentParamPassDirection_In;
287
288 case ParamCommandComment::Out:
289 return CXCommentParamPassDirection_Out;
290
291 case ParamCommandComment::InOut:
292 return CXCommentParamPassDirection_InOut;
293 }
294 llvm_unreachable("unknown ParamCommandComment::PassDirection");
295}
296
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000297CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
298 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
299 if (!TPCC || !TPCC->hasParamName())
300 return createCXString((const char *) 0);
301
302 return createCXString(TPCC->getParamName(), /*DupString=*/ false);
303}
304
305unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
306 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
307 if (!TPCC)
308 return false;
309
310 return TPCC->isPositionValid();
311}
312
313unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
314 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
315 if (!TPCC || !TPCC->isPositionValid())
316 return 0;
317
318 return TPCC->getDepth();
319}
320
321unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
322 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
323 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
324 return 0;
325
326 return TPCC->getIndex(Depth);
327}
328
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000329CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
330 const VerbatimBlockLineComment *VBL =
331 getASTNodeAs<VerbatimBlockLineComment>(CXC);
332 if (!VBL)
333 return createCXString((const char *) 0);
334
335 return createCXString(VBL->getText(), /*DupString=*/ false);
336}
337
338CXString clang_VerbatimLineComment_getText(CXComment CXC) {
339 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
340 if (!VLC)
341 return createCXString((const char *) 0);
342
343 return createCXString(VLC->getText(), /*DupString=*/ false);
344}
345
346} // end extern "C"
347
348//===----------------------------------------------------------------------===//
349// Helpers for converting comment AST to HTML.
350//===----------------------------------------------------------------------===//
351
352namespace {
353
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000354/// This comparison will sort parameters with valid index by index and
355/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000356class ParamCommandCommentCompareIndex {
357public:
358 bool operator()(const ParamCommandComment *LHS,
359 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000360 unsigned LHSIndex = UINT_MAX;
361 unsigned RHSIndex = UINT_MAX;
362 if (LHS->isParamIndexValid())
363 LHSIndex = LHS->getParamIndex();
364 if (RHS->isParamIndexValid())
365 RHSIndex = RHS->getParamIndex();
366
367 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000368 }
369};
370
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000371/// This comparison will sort template parameters in the following order:
372/// \li real template parameters (depth = 1) in index order;
373/// \li all other names (depth > 1);
374/// \li unresolved names.
375class TParamCommandCommentComparePosition {
376public:
377 bool operator()(const TParamCommandComment *LHS,
378 const TParamCommandComment *RHS) const {
379 // Sort unresolved names last.
380 if (!LHS->isPositionValid())
381 return false;
382 if (!RHS->isPositionValid())
383 return true;
384
385 if (LHS->getDepth() > 1)
386 return false;
387 if (RHS->getDepth() > 1)
388 return true;
389
390 // Sort template parameters in index order.
391 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
392 return LHS->getIndex(0) < RHS->getIndex(0);
393
394 // Leave all other names in source order.
395 return true;
396 }
397};
398
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000399/// Separate parts of a FullComment.
400struct FullCommentParts {
401 /// Take a full comment apart and initialize members accordingly.
402 FullCommentParts(const FullComment *C);
403
404 const BlockContentComment *Brief;
405 const ParagraphComment *FirstParagraph;
406 const BlockCommandComment *Returns;
407 SmallVector<const ParamCommandComment *, 8> Params;
408 SmallVector<const TParamCommandComment *, 4> TParams;
409 SmallVector<const BlockContentComment *, 8> MiscBlocks;
410};
411
412FullCommentParts::FullCommentParts(const FullComment *C) :
413 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
414 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
415 I != E; ++I) {
416 const Comment *Child = *I;
417 if (!Child)
418 continue;
419 switch (Child->getCommentKind()) {
420 case Comment::NoCommentKind:
421 continue;
422
423 case Comment::ParagraphCommentKind: {
424 const ParagraphComment *PC = cast<ParagraphComment>(Child);
425 if (PC->isWhitespace())
426 break;
427 if (!FirstParagraph)
428 FirstParagraph = PC;
429
430 MiscBlocks.push_back(PC);
431 break;
432 }
433
434 case Comment::BlockCommandCommentKind: {
435 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
436 StringRef CommandName = BCC->getCommandName();
437 if (!Brief && (CommandName == "brief" || CommandName == "short")) {
438 Brief = BCC;
439 break;
440 }
441 if (!Returns && (CommandName == "returns" || CommandName == "return")) {
442 Returns = BCC;
443 break;
444 }
445 MiscBlocks.push_back(BCC);
446 break;
447 }
448
449 case Comment::ParamCommandCommentKind: {
450 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
451 if (!PCC->hasParamName())
452 break;
453
454 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
455 break;
456
457 Params.push_back(PCC);
458 break;
459 }
460
461 case Comment::TParamCommandCommentKind: {
462 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
463 if (!TPCC->hasParamName())
464 break;
465
466 if (!TPCC->hasNonWhitespaceParagraph())
467 break;
468
469 TParams.push_back(TPCC);
470 break;
471 }
472
473 case Comment::VerbatimBlockCommentKind:
474 case Comment::VerbatimLineCommentKind:
475 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
476 break;
477
478 case Comment::TextCommentKind:
479 case Comment::InlineCommandCommentKind:
480 case Comment::HTMLStartTagCommentKind:
481 case Comment::HTMLEndTagCommentKind:
482 case Comment::VerbatimBlockLineCommentKind:
483 case Comment::FullCommentKind:
484 llvm_unreachable("AST node of this kind can't be a child of "
485 "a FullComment");
486 }
487 }
488
489 // Sort params in order they are declared in the function prototype.
490 // Unresolved parameters are put at the end of the list in the same order
491 // they were seen in the comment.
492 std::stable_sort(Params.begin(), Params.end(),
493 ParamCommandCommentCompareIndex());
494
495 std::stable_sort(TParams.begin(), TParams.end(),
496 TParamCommandCommentComparePosition());
497}
498
499void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
500 llvm::raw_svector_ostream &Result) {
501 Result << "<" << C->getTagName();
502
503 if (C->getNumAttrs() != 0) {
504 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
505 Result << " ";
506 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
507 Result << Attr.Name;
508 if (!Attr.Value.empty())
509 Result << "=\"" << Attr.Value << "\"";
510 }
511 }
512
513 if (!C->isSelfClosing())
514 Result << ">";
515 else
516 Result << "/>";
517}
518
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000519class CommentASTToHTMLConverter :
520 public ConstCommentVisitor<CommentASTToHTMLConverter> {
521public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000522 /// \param Str accumulator for HTML.
523 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000524
525 // Inline content.
526 void visitTextComment(const TextComment *C);
527 void visitInlineCommandComment(const InlineCommandComment *C);
528 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
529 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
530
531 // Block content.
532 void visitParagraphComment(const ParagraphComment *C);
533 void visitBlockCommandComment(const BlockCommandComment *C);
534 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000535 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000536 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
537 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
538 void visitVerbatimLineComment(const VerbatimLineComment *C);
539
540 void visitFullComment(const FullComment *C);
541
542 // Helpers.
543
544 /// Convert a paragraph that is not a block by itself (an argument to some
545 /// command).
546 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
547
548 void appendToResultWithHTMLEscaping(StringRef S);
549
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000550private:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000551 /// Output stream for HTML.
552 llvm::raw_svector_ostream Result;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000553};
554} // end unnamed namespace
555
556void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
557 appendToResultWithHTMLEscaping(C->getText());
558}
559
560void CommentASTToHTMLConverter::visitInlineCommandComment(
561 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000562 // Nothing to render if no arguments supplied.
563 if (C->getNumArgs() == 0)
564 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000565
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000566 // Nothing to render if argument is empty.
567 StringRef Arg0 = C->getArgText(0);
568 if (Arg0.empty())
569 return;
570
571 switch (C->getRenderKind()) {
572 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000573 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
574 appendToResultWithHTMLEscaping(C->getArgText(i));
575 Result << " ";
576 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000577 return;
578
579 case InlineCommandComment::RenderBold:
580 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000581 Result << "<b>";
582 appendToResultWithHTMLEscaping(Arg0);
583 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000584 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000585 case InlineCommandComment::RenderMonospaced:
586 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000587 Result << "<tt>";
588 appendToResultWithHTMLEscaping(Arg0);
589 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000590 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000591 case InlineCommandComment::RenderEmphasized:
592 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000593 Result << "<em>";
594 appendToResultWithHTMLEscaping(Arg0);
595 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000596 return;
597 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000598}
599
600void CommentASTToHTMLConverter::visitHTMLStartTagComment(
601 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000602 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000603}
604
605void CommentASTToHTMLConverter::visitHTMLEndTagComment(
606 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000607 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000608}
609
610void CommentASTToHTMLConverter::visitParagraphComment(
611 const ParagraphComment *C) {
612 if (C->isWhitespace())
613 return;
614
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000615 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000616 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
617 I != E; ++I) {
618 visit(*I);
619 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000620 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000621}
622
623void CommentASTToHTMLConverter::visitBlockCommandComment(
624 const BlockCommandComment *C) {
625 StringRef CommandName = C->getCommandName();
626 if (CommandName == "brief" || CommandName == "short") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000627 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000628 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000629 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000630 return;
631 }
Dmitri Gribenko2c6b00e2012-07-25 17:14:58 +0000632 if (CommandName == "returns" || CommandName == "return" ||
633 CommandName == "result") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000634 Result << "<p class=\"para-returns\">"
635 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000636 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000637 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000638 return;
639 }
640 // We don't know anything about this command. Just render the paragraph.
641 visit(C->getParagraph());
642}
643
644void CommentASTToHTMLConverter::visitParamCommandComment(
645 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000646 if (C->isParamIndexValid()) {
647 Result << "<dt class=\"param-name-index-"
648 << C->getParamIndex()
649 << "\">";
650 } else
651 Result << "<dt class=\"param-name-index-invalid\">";
652
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000653 appendToResultWithHTMLEscaping(C->getParamName());
654 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000655
656 if (C->isParamIndexValid()) {
657 Result << "<dd class=\"param-descr-index-"
658 << C->getParamIndex()
659 << "\">";
660 } else
661 Result << "<dd class=\"param-descr-index-invalid\">";
662
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000663 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000664 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000665}
666
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000667void CommentASTToHTMLConverter::visitTParamCommandComment(
668 const TParamCommandComment *C) {
669 if (C->isPositionValid()) {
670 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000671 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000672 << C->getIndex(0)
673 << "\">";
674 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000675 Result << "<dt class=\"tparam-name-index-other\">";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000676 } else
677 Result << "<dt class=\"tparam-name-index-invalid\">";
678
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000679 appendToResultWithHTMLEscaping(C->getParamName());
680 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000681
682 if (C->isPositionValid()) {
683 if (C->getDepth() == 1)
684 Result << "<dd class=\"tparam-descr-index-"
685 << C->getIndex(0)
686 << "\">";
687 else
688 Result << "<dd class=\"tparam-descr-index-other\">";
689 } else
690 Result << "<dd class=\"tparam-descr-index-invalid\">";
691
692 visitNonStandaloneParagraphComment(C->getParagraph());
693 Result << "</dd>";
694}
695
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000696void CommentASTToHTMLConverter::visitVerbatimBlockComment(
697 const VerbatimBlockComment *C) {
698 unsigned NumLines = C->getNumLines();
699 if (NumLines == 0)
700 return;
701
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000702 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000703 for (unsigned i = 0; i != NumLines; ++i) {
704 appendToResultWithHTMLEscaping(C->getText(i));
705 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000706 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000707 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000708 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000709}
710
711void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
712 const VerbatimBlockLineComment *C) {
713 llvm_unreachable("should not see this AST node");
714}
715
716void CommentASTToHTMLConverter::visitVerbatimLineComment(
717 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000718 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000719 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000720 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000721}
722
723void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000724 FullCommentParts Parts(C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000725
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000726 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000727 if (Parts.Brief)
728 visit(Parts.Brief);
729 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000730 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000731 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000732 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000733 FirstParagraphIsBrief = true;
734 }
735
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000736 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
737 const Comment *C = Parts.MiscBlocks[i];
738 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000739 continue;
740 visit(C);
741 }
742
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000743 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000744 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000745 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
746 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000747 Result << "</dl>";
748 }
749
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000750 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000751 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000752 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
753 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000754 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000755 }
756
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000757 if (Parts.Returns)
758 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000759
760 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000761}
762
763void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
764 const ParagraphComment *C) {
765 if (!C)
766 return;
767
768 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
769 I != E; ++I) {
770 visit(*I);
771 }
772}
773
774void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000775 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
776 const char C = *I;
777 switch (C) {
778 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000779 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000780 break;
781 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000782 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000783 break;
784 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000785 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000786 break;
787 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000788 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000789 break;
790 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000791 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000792 break;
793 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000794 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000795 break;
796 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000797 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000798 break;
799 }
800 }
801}
802
803extern "C" {
804
805CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
806 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
807 if (!HTC)
808 return createCXString((const char *) 0);
809
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000810 SmallString<128> HTML;
811 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000812 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000813 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000814}
815
816CXString clang_FullComment_getAsHTML(CXComment CXC) {
817 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
818 if (!FC)
819 return createCXString((const char *) 0);
820
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000821 SmallString<1024> HTML;
822 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000823 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000824 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000825}
826
827} // end extern "C"
828