blob: 9bdab61942281ef977ece9a0ee619c0cfdd19341 [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 Gribenkoae99b752012-07-20 21:34:34 +0000399class CommentASTToHTMLConverter :
400 public ConstCommentVisitor<CommentASTToHTMLConverter> {
401public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000402 /// \param Str accumulator for HTML.
403 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000404
405 // Inline content.
406 void visitTextComment(const TextComment *C);
407 void visitInlineCommandComment(const InlineCommandComment *C);
408 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
409 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
410
411 // Block content.
412 void visitParagraphComment(const ParagraphComment *C);
413 void visitBlockCommandComment(const BlockCommandComment *C);
414 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000415 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000416 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
417 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
418 void visitVerbatimLineComment(const VerbatimLineComment *C);
419
420 void visitFullComment(const FullComment *C);
421
422 // Helpers.
423
424 /// Convert a paragraph that is not a block by itself (an argument to some
425 /// command).
426 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
427
428 void appendToResultWithHTMLEscaping(StringRef S);
429
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000430private:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000431 /// Output stream for HTML.
432 llvm::raw_svector_ostream Result;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000433};
434} // end unnamed namespace
435
436void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
437 appendToResultWithHTMLEscaping(C->getText());
438}
439
440void CommentASTToHTMLConverter::visitInlineCommandComment(
441 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000442 // Nothing to render if no arguments supplied.
443 if (C->getNumArgs() == 0)
444 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000445
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000446 // Nothing to render if argument is empty.
447 StringRef Arg0 = C->getArgText(0);
448 if (Arg0.empty())
449 return;
450
451 switch (C->getRenderKind()) {
452 case InlineCommandComment::RenderNormal:
453 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
454 Result << C->getArgText(i) << " ";
455 return;
456
457 case InlineCommandComment::RenderBold:
458 assert(C->getNumArgs() == 1);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000459 Result << "<b>" << Arg0 << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000460 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000461 case InlineCommandComment::RenderMonospaced:
462 assert(C->getNumArgs() == 1);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000463 Result << "<tt>" << Arg0 << "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000464 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000465 case InlineCommandComment::RenderEmphasized:
466 assert(C->getNumArgs() == 1);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000467 Result << "<em>" << Arg0 << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000468 return;
469 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000470}
471
472void CommentASTToHTMLConverter::visitHTMLStartTagComment(
473 const HTMLStartTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000474 Result << "<" << C->getTagName();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000475
476 if (C->getNumAttrs() != 0) {
477 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000478 Result << " ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000479 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000480 Result << Attr.Name;
481 if (!Attr.Value.empty())
482 Result << "=\"" << Attr.Value << "\"";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000483 }
484 }
485
486 if (!C->isSelfClosing())
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000487 Result << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000488 else
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000489 Result << "/>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000490}
491
492void CommentASTToHTMLConverter::visitHTMLEndTagComment(
493 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000494 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000495}
496
497void CommentASTToHTMLConverter::visitParagraphComment(
498 const ParagraphComment *C) {
499 if (C->isWhitespace())
500 return;
501
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000502 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000503 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
504 I != E; ++I) {
505 visit(*I);
506 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000507 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000508}
509
510void CommentASTToHTMLConverter::visitBlockCommandComment(
511 const BlockCommandComment *C) {
512 StringRef CommandName = C->getCommandName();
513 if (CommandName == "brief" || CommandName == "short") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000514 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000515 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000516 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000517 return;
518 }
Dmitri Gribenko2c6b00e2012-07-25 17:14:58 +0000519 if (CommandName == "returns" || CommandName == "return" ||
520 CommandName == "result") {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000521 Result << "<p class=\"para-returns\">"
522 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000523 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000524 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000525 return;
526 }
527 // We don't know anything about this command. Just render the paragraph.
528 visit(C->getParagraph());
529}
530
531void CommentASTToHTMLConverter::visitParamCommandComment(
532 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000533 if (C->isParamIndexValid()) {
534 Result << "<dt class=\"param-name-index-"
535 << C->getParamIndex()
536 << "\">";
537 } else
538 Result << "<dt class=\"param-name-index-invalid\">";
539
540 Result << C->getParamName() << "</dt>";
541
542 if (C->isParamIndexValid()) {
543 Result << "<dd class=\"param-descr-index-"
544 << C->getParamIndex()
545 << "\">";
546 } else
547 Result << "<dd class=\"param-descr-index-invalid\">";
548
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000549 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000550 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000551}
552
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000553void CommentASTToHTMLConverter::visitTParamCommandComment(
554 const TParamCommandComment *C) {
555 if (C->isPositionValid()) {
556 if (C->getDepth() == 1)
557 Result << "<dt class=\"taram-name-index-"
558 << C->getIndex(0)
559 << "\">";
560 else
561 Result << "<dt class=\"taram-name-index-other\">";
562 } else
563 Result << "<dt class=\"tparam-name-index-invalid\">";
564
565 Result << C->getParamName() << "</dt>";
566
567 if (C->isPositionValid()) {
568 if (C->getDepth() == 1)
569 Result << "<dd class=\"tparam-descr-index-"
570 << C->getIndex(0)
571 << "\">";
572 else
573 Result << "<dd class=\"tparam-descr-index-other\">";
574 } else
575 Result << "<dd class=\"tparam-descr-index-invalid\">";
576
577 visitNonStandaloneParagraphComment(C->getParagraph());
578 Result << "</dd>";
579}
580
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000581void CommentASTToHTMLConverter::visitVerbatimBlockComment(
582 const VerbatimBlockComment *C) {
583 unsigned NumLines = C->getNumLines();
584 if (NumLines == 0)
585 return;
586
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000587 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000588 for (unsigned i = 0; i != NumLines; ++i) {
589 appendToResultWithHTMLEscaping(C->getText(i));
590 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000591 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000592 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000593 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000594}
595
596void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
597 const VerbatimBlockLineComment *C) {
598 llvm_unreachable("should not see this AST node");
599}
600
601void CommentASTToHTMLConverter::visitVerbatimLineComment(
602 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000603 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000604 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000605 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000606}
607
608void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
609 const BlockContentComment *Brief = NULL;
610 const ParagraphComment *FirstParagraph = NULL;
611 const BlockCommandComment *Returns = NULL;
612 SmallVector<const ParamCommandComment *, 8> Params;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000613 SmallVector<const TParamCommandComment *, 4> TParams;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000614 SmallVector<const BlockContentComment *, 8> MiscBlocks;
615
616 // Extract various blocks into separate variables and vectors above.
617 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
618 I != E; ++I) {
619 const Comment *Child = *I;
620 if (!Child)
621 continue;
622 switch (Child->getCommentKind()) {
623 case Comment::NoCommentKind:
624 continue;
625
626 case Comment::ParagraphCommentKind: {
627 const ParagraphComment *PC = cast<ParagraphComment>(Child);
628 if (PC->isWhitespace())
629 break;
630 if (!FirstParagraph)
631 FirstParagraph = PC;
632
633 MiscBlocks.push_back(PC);
634 break;
635 }
636
637 case Comment::BlockCommandCommentKind: {
638 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
639 StringRef CommandName = BCC->getCommandName();
640 if (!Brief && (CommandName == "brief" || CommandName == "short")) {
641 Brief = BCC;
642 break;
643 }
644 if (!Returns && (CommandName == "returns" || CommandName == "return")) {
645 Returns = BCC;
646 break;
647 }
648 MiscBlocks.push_back(BCC);
649 break;
650 }
651
652 case Comment::ParamCommandCommentKind: {
653 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
654 if (!PCC->hasParamName())
655 break;
656
657 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
658 break;
659
660 Params.push_back(PCC);
661 break;
662 }
663
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000664 case Comment::TParamCommandCommentKind: {
665 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
666 if (!TPCC->hasParamName())
667 break;
668
669 TParams.push_back(TPCC);
670 break;
671 }
672
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000673 case Comment::VerbatimBlockCommentKind:
674 case Comment::VerbatimLineCommentKind:
675 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
676 break;
677
678 case Comment::TextCommentKind:
679 case Comment::InlineCommandCommentKind:
680 case Comment::HTMLStartTagCommentKind:
681 case Comment::HTMLEndTagCommentKind:
682 case Comment::VerbatimBlockLineCommentKind:
683 case Comment::FullCommentKind:
684 llvm_unreachable("AST node of this kind can't be a child of "
685 "a FullComment");
686 }
687 }
688
689 // Sort params in order they are declared in the function prototype.
690 // Unresolved parameters are put at the end of the list in the same order
691 // they were seen in the comment.
692 std::stable_sort(Params.begin(), Params.end(),
693 ParamCommandCommentCompareIndex());
694
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000695 std::stable_sort(TParams.begin(), TParams.end(),
696 TParamCommandCommentComparePosition());
697
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000698 bool FirstParagraphIsBrief = false;
699 if (Brief)
700 visit(Brief);
701 else if (FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000702 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000703 visitNonStandaloneParagraphComment(FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000704 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000705 FirstParagraphIsBrief = true;
706 }
707
708 for (unsigned i = 0, e = MiscBlocks.size(); i != e; ++i) {
709 const Comment *C = MiscBlocks[i];
710 if (FirstParagraphIsBrief && C == FirstParagraph)
711 continue;
712 visit(C);
713 }
714
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000715 if (TParams.size() != 0) {
716 Result << "<dl>";
717 for (unsigned i = 0, e = TParams.size(); i != e; ++i)
718 visit(TParams[i]);
719 Result << "</dl>";
720 }
721
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000722 if (Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000723 Result << "<dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000724 for (unsigned i = 0, e = Params.size(); i != e; ++i)
725 visit(Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000726 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000727 }
728
729 if (Returns)
730 visit(Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000731
732 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000733}
734
735void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
736 const ParagraphComment *C) {
737 if (!C)
738 return;
739
740 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
741 I != E; ++I) {
742 visit(*I);
743 }
744}
745
746void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000747 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
748 const char C = *I;
749 switch (C) {
750 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000751 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000752 break;
753 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000754 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000755 break;
756 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000757 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000758 break;
759 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000760 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000761 break;
762 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000763 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000764 break;
765 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000766 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000767 break;
768 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000769 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000770 break;
771 }
772 }
773}
774
775extern "C" {
776
777CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
778 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
779 if (!HTC)
780 return createCXString((const char *) 0);
781
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000782 SmallString<128> HTML;
783 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000784 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000785 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000786}
787
788CXString clang_FullComment_getAsHTML(CXComment CXC) {
789 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
790 if (!FC)
791 return createCXString((const char *) 0);
792
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000793 SmallString<1024> HTML;
794 CommentASTToHTMLConverter Converter(HTML);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000795 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000796 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000797}
798
799} // end extern "C"
800