diff --git a/bindings/xml/comment-xml-schema.rng b/bindings/xml/comment-xml-schema.rng index e077c7ccbb9c8..df05cfc92d25d 100644 --- a/bindings/xml/comment-xml-schema.rng +++ b/bindings/xml/comment-xml-schema.rng @@ -959,6 +959,13 @@ .*\S.* + + + + + .*\S.* + + diff --git a/include/swift/Markup/AST.h b/include/swift/Markup/AST.h index fa5efdedc6be4..98fd98cf5b8dd 100644 --- a/include/swift/Markup/AST.h +++ b/include/swift/Markup/AST.h @@ -589,6 +589,36 @@ class Image final : public InlineContent, } }; +class InlineAttributes final : public InlineContent, private llvm::TrailingObjects { + friend TrailingObjects; + + // Note that inline attributes are like links, in that there are child inline nodes that are + // collectively styled by the attribute text. The child nodes are the text that should be + // displayed. + + size_t NumChildren; + StringRef Attributes; + + InlineAttributes(StringRef Attributes, ArrayRef Children); + +public: + static InlineAttributes *create(MarkupContext &MC, StringRef Attributes, ArrayRef Children); + + StringRef getAttributes() const { return Attributes; } + + ArrayRef getChildren() { + return {getTrailingObjects(), NumChildren}; + } + + ArrayRef getChildren() const { + return {getTrailingObjects(), NumChildren}; + } + + static bool classof(const MarkupASTNode *N) { + return N->getKind() == ASTNodeKind::InlineAttributes; + } +}; + #pragma mark Private Extensions class PrivateExtension : public MarkupASTNode { diff --git a/include/swift/Markup/ASTNodes.def b/include/swift/Markup/ASTNodes.def index c999fc924e5c3..0704e71116b14 100644 --- a/include/swift/Markup/ASTNodes.def +++ b/include/swift/Markup/ASTNodes.def @@ -40,7 +40,8 @@ ABSTRACT_MARKUP_AST_NODE(InlineContent, MarkupASTNode) MARKUP_AST_NODE(Strong, InlineContent) MARKUP_AST_NODE(Link, InlineContent) MARKUP_AST_NODE(Image, InlineContent) -MARKUP_AST_NODE_RANGE(Inline, Text, Image) + MARKUP_AST_NODE(InlineAttributes, InlineContent) +MARKUP_AST_NODE_RANGE(Inline, Text, InlineAttributes) /// Private Markdown Extensions - these should not be implemented in the /// underlying cmark parser. diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index 412419f9fb4f5..7e3673415b01a 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -142,6 +142,15 @@ namespace { } } + void visitInlineAttributes(const InlineAttributes *A) { + print("^["); + for (const auto *Child : A->getChildren()) + visit(Child); + print("]("); + print(A->getAttributes()); + print(")"); + } + void visitBlockQuote(const BlockQuote *BQ) { indent(); printNewline(); diff --git a/lib/IDE/CommentConversion.cpp b/lib/IDE/CommentConversion.cpp index b9cd2554399e9..db7a501bef18d 100644 --- a/lib/IDE/CommentConversion.cpp +++ b/lib/IDE/CommentConversion.cpp @@ -72,6 +72,18 @@ struct CommentToXMLConverter { llvm_unreachable("Can't print a swift::markup::Document as XML directly"); } + void printInlineAttributes(const InlineAttributes *A) { + OS << "getAttributes()); + OS << "\">"; + + for (const auto *N : A->getChildren()) { + printASTNode(N); + } + + OS << ""; + } + void printBlockQuote(const BlockQuote *BQ) { for (const auto *N : BQ->getChildren()) printASTNode(N); @@ -638,6 +650,12 @@ class DoxygenConverter : public MarkupASTVisitor { visit(Child); } + void visitInlineAttributes(const InlineAttributes *A) { + // attributed strings don't have an analogue in Doxygen, so just print out the text + for (const auto *Child : A->getChildren()) + visit(Child); + } + void visitBlockQuote(const BlockQuote *BQ) { print("
"); printNewline(); diff --git a/lib/Markup/AST.cpp b/lib/Markup/AST.cpp index 9d55716a20382..68dc44cdd6703 100644 --- a/lib/Markup/AST.cpp +++ b/lib/Markup/AST.cpp @@ -157,6 +157,16 @@ Paragraph *Paragraph::create(MarkupContext &MC, return new (Mem) Paragraph(Children); } +InlineAttributes::InlineAttributes(StringRef Attributes, ArrayRef Children) : InlineContent(ASTNodeKind::InlineAttributes), NumChildren(Children.size()), Attributes(Attributes) { + std::uninitialized_copy(Children.begin(), Children.end(), getTrailingObjects()); +} + +InlineAttributes *InlineAttributes::create(MarkupContext &MC, StringRef Attributes, ArrayRef Children) { + void *Mem = MC.allocate(totalSizeToAlloc(Children.size()), alignof(InlineAttributes)); + StringRef AttrsCopy = MC.allocateCopy(Attributes); + return new (Mem) InlineAttributes(AttrsCopy, Children); +} + HRule *HRule::create(MarkupContext &MC) { void *Mem = MC.allocate(sizeof(HRule), alignof(HRule)); return new (Mem) HRule(); @@ -408,6 +418,14 @@ void swift::markup::dump(const MarkupASTNode *Node, llvm::raw_ostream &OS, dumpChildren(Node->getChildren(), OS, indent + 1); break; } + case swift::markup::ASTNodeKind::InlineAttributes: { + auto A = cast(Node); + OS << "Attribute:"; + OS << " Attributes=" << A->getAttributes(); + OS << " Children=" << Node->getChildren().size(); + dumpChildren(Node->getChildren(), OS, indent + 1); + break; + } case swift::markup::ASTNodeKind::BlockQuote: { OS << "BlockQuote: Children=" << Node->getChildren().size(); dumpChildren(Node->getChildren(), OS, indent + 1); diff --git a/lib/Markup/Markup.cpp b/lib/Markup/Markup.cpp index 61275f19c0dc7..0f007c15000c6 100644 --- a/lib/Markup/Markup.cpp +++ b/lib/Markup/Markup.cpp @@ -137,7 +137,7 @@ ParseResult parseStrong(MarkupContext &MC, ParseState State) { } ParseResult
parseHeader(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_HEADER + assert(cmark_node_get_type(State.Node) == CMARK_NODE_HEADING && State.Event == CMARK_EVENT_ENTER); auto Level = cmark_node_get_header_level(State.Node); SmallVector Children; @@ -149,19 +149,19 @@ ParseResult
parseHeader(MarkupContext &MC, ParseState State) { } ParseResult parseHRule(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_HRULE + assert(cmark_node_get_type(State.Node) == CMARK_NODE_THEMATIC_BREAK && State.Event == CMARK_EVENT_ENTER); return { HRule::create(MC), State.next() }; } ParseResult parseHTML(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML + assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML_BLOCK && State.Event == CMARK_EVENT_ENTER); return {HTML::create(MC, getLiteralContent(MC, State.Node)), State.next()}; } ParseResult parseInlineHTML(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_INLINE_HTML + assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML_INLINE && State.Event == CMARK_EVENT_ENTER); return {InlineHTML::create(MC, getLiteralContent(MC, State.Node)), State.next()}; @@ -216,6 +216,15 @@ ParseResult parseLink(MarkupContext &MC, ParseState State) { return { Link::create(MC, Destination, Children), ResultState.next() }; } +ParseResult parseAttribute(MarkupContext &MC, ParseState State) { + assert(cmark_node_get_type(State.Node) == CMARK_NODE_ATTRIBUTE && State.Event == CMARK_EVENT_ENTER); + std::string Attributes(cmark_node_get_attributes(State.Node)); + SmallVector Children; + auto ResultState = parseChildren(MC, State, Children); + assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); + return { InlineAttributes::create(MC, Attributes, Children), ResultState.next() }; +} + ParseResult parseList(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_LIST && State.Event == CMARK_EVENT_ENTER); @@ -245,6 +254,13 @@ ParseResult parseElement(MarkupContext &MC, ParseState State) { case CMARK_NODE_DOCUMENT: { llvm_unreachable("Markup documents cannot be nested"); } + case CMARK_NODE_FOOTNOTE_REFERENCE: + case CMARK_NODE_FOOTNOTE_DEFINITION: { + llvm_unreachable("Footnotes are not currently parsed by swiftMarkup"); + } + case CMARK_NODE_ATTRIBUTE: { + return parseAttribute(MC, State); + } case CMARK_NODE_BLOCK_QUOTE: { return parseBlockQuote(MC, State); } @@ -257,21 +273,18 @@ ParseResult parseElement(MarkupContext &MC, ParseState State) { case CMARK_NODE_EMPH: { return parseEmphasis(MC, State); } - case CMARK_NODE_HEADER: { + case CMARK_NODE_HEADING: { return parseHeader(MC, State); } - case CMARK_NODE_HRULE: { - return parseHRule(MC, State); - } - case CMARK_NODE_HTML: { + case CMARK_NODE_HTML_BLOCK: { return parseHTML(MC, State); } + case CMARK_NODE_HTML_INLINE: { + return parseInlineHTML(MC, State); + } case CMARK_NODE_IMAGE: { return parseImage(MC, State); } - case CMARK_NODE_INLINE_HTML: { - return parseInlineHTML(MC, State); - } case CMARK_NODE_ITEM: { return parseItem(MC, State); } @@ -296,6 +309,9 @@ ParseResult parseElement(MarkupContext &MC, ParseState State) { case CMARK_NODE_TEXT: { return parseText(MC, State); } + case CMARK_NODE_THEMATIC_BREAK: { + return parseHRule(MC, State); + } default: { llvm_unreachable("Can't parse a Markup node of type 'None'"); } diff --git a/test/Inputs/comment_to_something_conversion.swift b/test/Inputs/comment_to_something_conversion.swift index f88565bd005f4..71bd6859238a1 100644 --- a/test/Inputs/comment_to_something_conversion.swift +++ b/test/Inputs/comment_to_something_conversion.swift @@ -62,6 +62,15 @@ public enum A012_AttachToEntities { // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)ATXHeaders(im)f0@objc public func f0()]]>LEVEL ONE]]>]]>LEVEL TWO]]>] } +@objc public class Attributes { +// CHECK: {{.*}}DocCommentAsXML=none + /// Here is an attribute: + /// + /// ^[Attribute text](string: "attributed") + @objc public func f0() {} + // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)Attributes(im)f0@objc public func f0()Here is an attribute:Attribute text] +} + @objc public class AutomaticLink { // CHECK: {{.*}}DocCommentAsXML=none /// And now for a URL. @@ -198,6 +207,18 @@ public enum A012_AttachToEntities { // CHECK: {{.*}}DocCommentAsXML=[f4()c:@M@comment_to_xml@objc(cs)EmptyComments(im)f4@objc public func f4()Aaa.] } +@objc public class Footnotes { +// CHECK: {{.*}}DocCommentAsXML=none + /// Has some footnotes. + /// + /// Footnotes aren't handled by swiftMarkup yet[^footnote], but they may in the future. + /// + /// [^footnote]: Footnotes aren't parsed by default in swift-cmark, and swiftMarkup doesn't + /// enable the feature. + @objc public func f0() {} + // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)Footnotes(im)f0@objc public func f0()Has some footnotes.Footnotes aren’t handled by swiftMarkup yet[^footnote], but they may in the future.[^footnote]: Footnotes aren’t parsed by default in swift-cmark, and swiftMarkup doesn’t enable the feature.] +} + @objc public class HasThrowingFunction { // CHECK: {{.*}}DocCommentAsXML=none diff --git a/test/PrintAsObjC/Inputs/comments-expected-output.h b/test/PrintAsObjC/Inputs/comments-expected-output.h index 2a23d5ed3d579..f25746a6164b7 100644 --- a/test/PrintAsObjC/Inputs/comments-expected-output.h +++ b/test/PrintAsObjC/Inputs/comments-expected-output.h @@ -32,6 +32,14 @@ SWIFT_CLASS("_TtC8comments10ATXHeaders") @end +SWIFT_CLASS("_TtC8comments10Attributes") +@interface Attributes +/// Here is an attribute: +/// Attribute text +- (void)f0; +@end + + SWIFT_CLASS("_TtC8comments13AutomaticLink") @interface AutomaticLink /// And now for a URL. @@ -185,6 +193,16 @@ SWIFT_CLASS("_TtC8comments13EmptyComments") @end +SWIFT_CLASS("_TtC8comments9Footnotes") +@interface Footnotes +/// Has some footnotes. +/// Footnotes aren’t handled by swiftMarkup yet[^footnote], but they may in the future. +/// [^footnote]: Footnotes aren’t parsed by default in swift-cmark, and swiftMarkup doesn’t +/// enable the feature. +- (void)f0; +@end + + SWIFT_CLASS("_TtC8comments19HasThrowingFunction") @interface HasThrowingFunction /// Might throw something.