//===--- CommentConversion.cpp - Conversion of comments to other formats --===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/IDE/CommentConversion.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Comment.h" #include "swift/AST/Decl.h" #include "swift/AST/USRGeneration.h" #include "swift/AST/RawComment.h" #include "swift/Basic/SourceManager.h" #include "swift/Markup/Markup.h" #include "swift/Markup/XMLUtils.h" #include "swift/Parse/Token.h" #include "swift/Subsystems.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Index/CommentToXML.h" using namespace swift::markup; using namespace swift; //===----------------------------------------------------------------------===// // Conversion to XML. //===----------------------------------------------------------------------===// namespace { struct CommentToXMLConverter { raw_ostream &OS; CommentToXMLConverter(raw_ostream &OS) : OS(OS) {} void printRawHTML(StringRef Tag) { OS << ""; appendWithCDATAEscaping(OS, Tag); OS << ""; } void printASTNode(const MarkupASTNode *N) { switch (N->getKind()) { #define MARKUP_AST_NODE(Id, Parent) \ case ASTNodeKind::Id: \ print##Id(cast(N)); \ break; #define ABSTRACT_MARKUP_AST_NODE(Id, Parent) #define MARKUP_AST_NODE_RANGE(Id, FirstId, LastId) #include "swift/Markup/ASTNodes.def" } } #define MARKUP_SIMPLE_FIELD(Id, Keyword, XMLKind) \ void print##Id(const Id *Field) { \ OS << "<" #XMLKind << ">"; \ for (auto Child : Field->getChildren()) \ printASTNode(Child); \ \ OS << ""; \ } #include "swift/Markup/SimpleFields.def" void printDocument(const Document *D) { llvm_unreachable("Can't print an swift::markup::Document as XML directly"); } void printBlockQuote(const BlockQuote *BQ) { for (const auto *N : BQ->getChildren()) printASTNode(N); } void printList(const List *L) { OS << (L->isOrdered() ? "" : ""); for (const auto *N : L->getChildren()) printASTNode(N); OS << (L->isOrdered() ? "" : ""); } void printItem(const Item *I) { OS << ""; for (const auto *N : I->getChildren()) printASTNode(N); OS << ""; } void printCode(const Code *C) { OS << ""; appendWithXMLEscaping(OS, C->getLiteralContent()); OS << ""; } void printCodeBlock(const CodeBlock *CB) { OS << "getLanguage()); OS << "\">"; SmallVector CodeLines; CB->getLiteralContent().split(CodeLines, "\n"); for (auto Line : CodeLines) { OS << ""; appendWithCDATAEscaping(OS, Line); OS << ""; } OS << ""; } void printParagraph(const Paragraph *P) { OS << ""; for (const auto *N : P->getChildren()) printASTNode(N); OS << ""; } void printHeader(const Header *H) { llvm::SmallString<4> Tag; llvm::raw_svector_ostream TagStream(Tag); TagStream << "getLevel() << ">"; printRawHTML(TagStream.str()); for (auto Child : H->getChildren()) printASTNode(Child); llvm::SmallString<5> EndTag; llvm::raw_svector_ostream EndTagStream(EndTag); EndTagStream << "getLevel() << ">"; printRawHTML(EndTagStream.str()); } void printHRule(const HRule *HR) { printRawHTML("
"); } void printText(const Text *T) { appendWithXMLEscaping(OS, T->getLiteralContent()); } void printHTML(const HTML *H) { printRawHTML(H->getLiteralContent()); } void printInlineHTML(const InlineHTML *IH) { printRawHTML(IH->getLiteralContent()); } void printEmphasis(const Emphasis *E) { OS << ""; for (const auto *IC : E->getChildren()) printASTNode(IC); OS << ""; } void printStrong(const Strong *S) { OS << ""; for (const auto *N : S->getChildren()) printASTNode(N); OS << ""; } void printLink(const Link *L) { SmallString<32> Tag; llvm::raw_svector_ostream S(Tag); S << "getDestination()); S << "\">"; OS << S.str(); for (const auto N : L->getChildren()) printASTNode(N); OS << ""; } void printSoftBreak(const SoftBreak *SB) { OS << " "; } void printLineBreak(const LineBreak *LB) { printRawHTML("
"); } void printPrivateExtension(const PrivateExtension *PE) { llvm_unreachable("Can't directly print a Swift Markup PrivateExtension"); } void printImage(const Image *I) { SmallString<64> Tag; llvm::raw_svector_ostream S(Tag); S << "getDestination() << "\""; if (I->hasTitle()) S << " title=\"" << I->getTitle() << "\""; if (I->getChildren().size()) { S << " alt=\""; for (const auto N : I->getChildren()) printInlinesUnder(N, S); S << "\""; } S << "\\>"; printRawHTML(S.str()); } void printParamField(const ParamField *PF) { OS << ""; OS << ""; OS << PF->getName(); OS << ""; OS << "in"; if (PF->isClosureParameter()) { OS << ""; visitCommentParts(PF->getParts().getValue()); OS << ""; } else { OS << ""; for (auto Child : PF->getChildren()) { printASTNode(Child); } OS << ""; } OS << ""; } void printResultDiscussion(const ReturnsField *RF) { OS << ""; for (auto Child : RF->getChildren()) printASTNode(Child); OS << ""; } void printThrowsDiscussion(const ThrowsField *RF) { OS << ""; for (auto Child : RF->getChildren()) printASTNode(Child); OS << ""; } void visitDocComment(const DocComment *DC); void visitCommentParts(const swift::markup::CommentParts &Parts); }; } // unnamed namespace void CommentToXMLConverter::visitCommentParts(const swift::markup::CommentParts &Parts) { if (Parts.Brief.hasValue()) { OS << ""; printASTNode(Parts.Brief.getValue()); OS << ""; } if (!Parts.ParamFields.empty()) { OS << ""; for (const auto *PF : Parts.ParamFields) printParamField(PF); OS << ""; } if (Parts.ReturnsField.hasValue()) printResultDiscussion(Parts.ReturnsField.getValue()); if (Parts.ThrowsField.hasValue()) printThrowsDiscussion(Parts.ThrowsField.getValue()); if (!Parts.BodyNodes.empty()) { OS << ""; for (const auto *N : Parts.BodyNodes) printASTNode(N); OS << ""; } } void CommentToXMLConverter::visitDocComment(const DocComment *DC) { const Decl *D = DC->getDecl(); StringRef RootEndTag; if (isa(D)) { OS << "(D) || isa(D) || isa(D)) { OS << "getLoc(); if (Loc.isValid()) { const auto &SM = D->getASTContext().SourceMgr; unsigned BufferID = SM.findBufferContainingLoc(Loc); StringRef FileName = SM.getIdentifierForBuffer(BufferID); auto LineAndColumn = SM.getLineAndColumn(Loc); OS << " file=\""; appendWithXMLEscaping(OS, FileName); OS << "\""; OS << " line=\"" << LineAndColumn.first << "\" column=\"" << LineAndColumn.second << "\""; } } // Finish the root tag. OS << ">"; auto *VD = dyn_cast(D); OS << ""; if (VD && VD->hasName()) { llvm::SmallString<64> SS; llvm::raw_svector_ostream NameOS(SS); NameOS << VD->getFullName(); appendWithXMLEscaping(OS, NameOS.str()); } OS << ""; if (VD) { llvm::SmallString<64> SS; bool Failed; { llvm::raw_svector_ostream OS(SS); Failed = ide::printDeclUSR(VD, OS); } if (!Failed && !SS.empty()) { OS << "" << SS << ""; } } { PrintOptions PO = PrintOptions::printInterface(); PO.PrintAccessibility = false; PO.AccessibilityFilter = Accessibility::Private; PO.PrintDocumentationComments = false; PO.TypeDefinitions = false; PO.VarInitializers = false; OS << ""; llvm::SmallString<32> DeclSS; { llvm::raw_svector_ostream DeclOS(DeclSS); D->print(DeclOS, PO); } appendWithXMLEscaping(OS, DeclSS); OS << ""; } visitCommentParts(DC->getParts()); OS << RootEndTag; } static bool getClangDocumentationCommentAsXML(const clang::Decl *D, raw_ostream &OS) { const auto &ClangContext = D->getASTContext(); const clang::comments::FullComment *FC = ClangContext.getCommentForDecl(D, /*PP=*/nullptr); if (!FC) return false; // FIXME: hang the converter object somewhere so that it is persistent // between requests to this AST. clang::index::CommentToXMLConverter Converter; llvm::SmallString<1024> XML; Converter.convertCommentToXML(FC, XML, ClangContext); OS << XML; return true; } static void replaceObjcDeclarationsWithSwiftOnes(const Decl *D, StringRef Doc, raw_ostream &OS) { StringRef Open = ""; StringRef Close = ""; PrintOptions Options = PrintOptions::printQuickHelpDeclaration(); std::string S; llvm::raw_string_ostream SS(S); D->print(SS, Options); std::string Signature = SS.str(); auto OI = Doc.find(Open); auto CI = Doc.find(Close); if (StringRef::npos != OI && StringRef::npos != CI && CI > OI) OS << Doc.substr(0, OI) << Open << Signature << Close << Doc.substr(CI + Close.size()); else OS << Doc; } std::string ide::extractPlainTextFromComment(const StringRef Text) { LangOptions LangOpts; SourceManager SourceMgr; auto Tokens = swift::tokenize(LangOpts, SourceMgr, SourceMgr.addMemBufferCopy(Text)); std::vector Comments; Comments.reserve(Tokens.size()); for (auto &Tok : Tokens) { if (Tok.is(tok::comment)) { Comments.push_back(SingleRawComment(Tok.getText(), 0)); } } if (Comments.empty()) return {}; RawComment Comment(Comments); swift::markup::MarkupContext MC; return MC.getLineList(Comment).str(); } bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS) { auto MaybeClangNode = D->getClangNode(); if (MaybeClangNode) { if (auto *CD = MaybeClangNode.getAsDecl()) { std::string S; llvm::raw_string_ostream SS(S); if (getClangDocumentationCommentAsXML(CD, SS)) { replaceObjcDeclarationsWithSwiftOnes(D, SS.str(), OS); return true; } } return false; } swift::markup::MarkupContext MC; auto DC = getDocComment(MC, D); if (!DC.hasValue()) return false; CommentToXMLConverter Converter(OS); Converter.visitDocComment(DC.getValue()); OS.flush(); return true; } //===----------------------------------------------------------------------===// // Conversion to Doxygen. //===----------------------------------------------------------------------===// namespace { struct CommentToDoxygenConverter { raw_ostream &OS; unsigned PendingNewlines = 1; CommentToDoxygenConverter(raw_ostream &OS) : OS(OS) {} void print(StringRef Text) { for (unsigned i = 0; i != PendingNewlines; ++i) { OS << "\n///"; if (i == PendingNewlines - 1) OS << " "; } PendingNewlines = 0; OS << Text; } void printNewline() { PendingNewlines++; } void printASTNode(const MarkupASTNode *N) { switch (N->getKind()) { #define MARKUP_AST_NODE(Id, Parent) \ case ASTNodeKind::Id: \ print##Id(cast(N)); \ break; #define ABSTRACT_MARKUP_AST_NODE(Id, Parent) #define MARKUP_AST_NODE_RANGE(Id, FirstId, LastId) #include "swift/Markup/ASTNodes.def" } } #define MARKUP_SIMPLE_FIELD(Id, Keyword, XMLKind) \ void print##Id(const Id *Field) { \ OS << "\\" << #XMLKind << " "; \ for (auto Child : Field->getChildren()) \ printASTNode(Child); \ printNewline(); \ } #include "swift/Markup/SimpleFields.def" void printDocument(const Document *D) { // FIXME: Why keep doing this? llvm_unreachable("Can't print an swift::markup::Document as XML directly"); } void printBlockQuote(const BlockQuote *BQ) { print("
"); for (const auto *N : BQ->getChildren()) printASTNode(N); print("
"); } void printList(const List *BL) { print(BL->isOrdered() ? "
    " : "
      "); for (const auto *I : BL->getChildren()) printASTNode(I); print(BL->isOrdered() ? "
" : ""); } void printItem(const Item *I) { print("
  • "); for (const auto *N : I->getChildren()) printASTNode(N); print("
  • "); } void printCodeBlock(const CodeBlock *CB) { print(""); SmallVector Lines; CB->getLiteralContent().split(Lines, "\n"); for (auto Line : Lines) { print(Line); printNewline(); } print(""); } void printCode(const Code *C) { print(""); SmallVector Lines; C->getLiteralContent().split(Lines, "\n"); for (auto Line : Lines) { print(Line); printNewline(); } print(""); } void printHTML(const HTML *H) { print(H->getLiteralContent().str()); } void printInlineHTML(const InlineHTML *IH) { print(IH->getLiteralContent().str()); } void printSoftBreak(const SoftBreak *SB) { printNewline(); } void printLineBreak(const LineBreak *LB) { print("
    "); printNewline(); } void printLink(const Link *L) { SmallString<32> Tag; llvm::raw_svector_ostream S(Tag); S << "getDestination() << "\">"; print(S.str()); for (const auto N : L->getChildren()) printASTNode(N); print(""); } void printImage(const Image *I) { SmallString<64> Tag; llvm::raw_svector_ostream S(Tag); S << "getDestination() << "\""; if (I->hasTitle()) S << " title=\"" << I->getTitle() << "\""; if (I->getChildren().size()) { S << " alt=\""; for (const auto N : I->getChildren()) printInlinesUnder(N, S); S << "\""; } S << "\\>"; print(S.str()); } void printParagraph(const Paragraph *P) { for (const auto *N : P->getChildren()) printASTNode(N); } void printEmphasis(const Emphasis *E) { print(""); for (const auto *N : E->getChildren()) printASTNode(N); print(""); } void printStrong(const Strong *E) { print(""); for (const auto *N : E->getChildren()) printASTNode(N); print(""); } void printHRule(const HRule *HR) { print("
    "); } void printHeader(const Header *H) { llvm::SmallString<4> Tag; llvm::raw_svector_ostream TagStream(Tag); TagStream << "getLevel() << ">"; print(TagStream.str()); for (auto Child : H->getChildren()) printASTNode(Child); llvm::SmallString<5> EndTag; llvm::raw_svector_ostream EndTagStream(EndTag); EndTagStream << "getLevel() << ">"; print(EndTagStream.str()); } void printText(const Text *T) { print(T->getLiteralContent().str()); } void printPrivateExtension(const PrivateExtension *PE) { llvm_unreachable("Can't directly print Doxygen for a Swift Markup PrivateExtension"); } void printNestedParamField(const ParamField *PF) { auto Parts = PF->getParts().getValue(); if (Parts.Brief.hasValue()) { printASTNode(Parts.Brief.getValue()); printNewline(); } if (!Parts.ParamFields.empty()) { printNewline(); print("\\a "); print(PF->getName()); print(" parameters:"); printNewline(); print("
      "); printNewline(); for (auto Param : Parts.ParamFields) { print("
    • "); printNewline(); print(Param->getName()); print(": "); printNestedParamField(Param); print("
    • "); printNewline(); } print("
    "); printNewline(); printNewline(); } if (Parts.ReturnsField.hasValue()) { printNewline(); print("\\a "); print(PF->getName()); print(" returns: "); for (auto Child : Parts.ReturnsField.getValue()->getChildren()) { printASTNode(Child); printNewline(); } } if (Parts.ThrowsField.hasValue()) { printNewline(); print("\\a "); print(PF->getName()); print(" error: "); for (auto Child : Parts.ThrowsField.getValue()->getChildren()) { printASTNode(Child); printNewline(); } } for (auto BodyNode : Parts.BodyNodes) { printASTNode(BodyNode); printNewline(); } printNewline(); } void printParamField(const ParamField *PF) { print("\\param "); print(PF->getName()); print(" "); if (PF->isClosureParameter()) { printNestedParamField(PF); } else { for (auto Child : PF->getChildren()) { printASTNode(Child); } } printNewline(); } void printReturnField(const ReturnsField *RF) { print("\\returns "); print(" "); for (auto Child : RF->getChildren()) printASTNode(Child); printNewline(); } void printThrowField(const ThrowsField *TF) { print("\\param error "); for (auto Child : TF->getChildren()) printASTNode(Child); printNewline(); } }; } // unnamed namespace void ide::getDocumentationCommentAsDoxygen(const DocComment *DC, raw_ostream &OS) { CommentToDoxygenConverter Converter(OS); auto Brief = DC->getBrief(); if (Brief.hasValue()) { SmallString<256> BriefStr; llvm::raw_svector_ostream OS(BriefStr); swift::markup::printInlinesUnder(Brief.getValue(), OS); Converter.print(OS.str()); Converter.printNewline(); Converter.printNewline(); } for (const auto *N : DC->getBodyNodes()) { if (const auto *P = dyn_cast(N)) { Converter.printASTNode(P); Converter.printNewline(); Converter.printNewline(); continue; } Converter.printASTNode(N); Converter.printNewline(); } for (const auto PF : DC->getParamFields()) { Converter.printParamField(PF); Converter.printNewline(); } auto TF = DC->getThrowsField(); if (TF.hasValue()) { Converter.printThrowField(TF.getValue()); Converter.printNewline(); } auto RF = DC->getReturnsField(); if (RF.hasValue()) { Converter.printReturnField(RF.getValue()); Converter.printNewline(); } if (Converter.PendingNewlines != 0) OS << "\n"; }