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.