//===--- 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 << "" #XMLKind << ">"; \
}
#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";
}