Skip to content

[Markup] add support for swift-cmark's inline attributes #60173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bindings/xml/comment-xml-schema.rng
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,13 @@
<param name="pattern">.*\S.*</param>
</data>
</element>
<element name="InlineAttributes">
<attribute name="attributes"/>
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
</choice>
</define>

Expand Down
30 changes: 30 additions & 0 deletions include/swift/Markup/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,36 @@ class Image final : public InlineContent,
}
};

class InlineAttributes final : public InlineContent, private llvm::TrailingObjects<Image, MarkupASTNode *> {
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<MarkupASTNode *> Children);

public:
static InlineAttributes *create(MarkupContext &MC, StringRef Attributes, ArrayRef<MarkupASTNode *> Children);

StringRef getAttributes() const { return Attributes; }

ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}

ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}

static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::InlineAttributes;
}
};

#pragma mark Private Extensions

class PrivateExtension : public MarkupASTNode {
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Markup/ASTNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 9 additions & 0 deletions lib/Frontend/PrintingDiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
18 changes: 18 additions & 0 deletions lib/IDE/CommentConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ struct CommentToXMLConverter {
llvm_unreachable("Can't print a swift::markup::Document as XML directly");
}

void printInlineAttributes(const InlineAttributes *A) {
OS << "<InlineAttributes attributes=\"";
appendWithXMLEscaping(OS, A->getAttributes());
OS << "\">";

for (const auto *N : A->getChildren()) {
printASTNode(N);
}

OS << "</InlineAttributes>";
}

void printBlockQuote(const BlockQuote *BQ) {
for (const auto *N : BQ->getChildren())
printASTNode(N);
Expand Down Expand Up @@ -638,6 +650,12 @@ class DoxygenConverter : public MarkupASTVisitor<DoxygenConverter> {
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("<blockquote>");
printNewline();
Expand Down
18 changes: 18 additions & 0 deletions lib/Markup/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ Paragraph *Paragraph::create(MarkupContext &MC,
return new (Mem) Paragraph(Children);
}

InlineAttributes::InlineAttributes(StringRef Attributes, ArrayRef<MarkupASTNode *> Children) : InlineContent(ASTNodeKind::InlineAttributes), NumChildren(Children.size()), Attributes(Attributes) {
std::uninitialized_copy(Children.begin(), Children.end(), getTrailingObjects<MarkupASTNode *>());
}

InlineAttributes *InlineAttributes::create(MarkupContext &MC, StringRef Attributes, ArrayRef<MarkupASTNode *> Children) {
void *Mem = MC.allocate(totalSizeToAlloc<MarkupASTNode *>(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();
Expand Down Expand Up @@ -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<InlineAttributes>(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);
Expand Down
40 changes: 28 additions & 12 deletions lib/Markup/Markup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ ParseResult<Strong> parseStrong(MarkupContext &MC, ParseState State) {
}

ParseResult<Header> 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<MarkupASTNode *, 2> Children;
Expand All @@ -149,19 +149,19 @@ ParseResult<Header> parseHeader(MarkupContext &MC, ParseState State) {
}

ParseResult<HRule> 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<HTML> 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<InlineHTML> 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()};
Expand Down Expand Up @@ -216,6 +216,15 @@ ParseResult<Link> parseLink(MarkupContext &MC, ParseState State) {
return { Link::create(MC, Destination, Children), ResultState.next() };
}

ParseResult<InlineAttributes> 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<MarkupASTNode *, 2> 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<List> parseList(MarkupContext &MC, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_LIST
&& State.Event == CMARK_EVENT_ENTER);
Expand Down Expand Up @@ -245,6 +254,13 @@ ParseResult<MarkupASTNode> 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);
}
Expand All @@ -257,21 +273,18 @@ ParseResult<MarkupASTNode> 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);
}
Expand All @@ -296,6 +309,9 @@ ParseResult<MarkupASTNode> 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'");
}
Expand Down
21 changes: 21 additions & 0 deletions test/Inputs/comment_to_something_conversion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ public enum A012_AttachToEntities {
// CHECK: {{.*}}DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>f0()</Name><USR>c:@M@comment_to_xml@objc(cs)ATXHeaders(im)f0</USR><Declaration>@objc public func f0()</Declaration><CommentParts><Discussion><rawHTML><![CDATA[<h1>]]></rawHTML>LEVEL ONE<rawHTML><![CDATA[</h1>]]></rawHTML><rawHTML><![CDATA[<h2>]]></rawHTML>LEVEL TWO<rawHTML><![CDATA[</h2>]]></rawHTML></Discussion></CommentParts></Function>]
}

@objc public class Attributes {
// CHECK: {{.*}}DocCommentAsXML=none
/// Here is an attribute:
///
/// ^[Attribute text](string: "attributed")
@objc public func f0() {}
// CHECK: {{.*}}DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>f0()</Name><USR>c:@M@comment_to_xml@objc(cs)Attributes(im)f0</USR><Declaration>@objc public func f0()</Declaration><CommentParts><Abstract><Para>Here is an attribute:</Para></Abstract><Discussion><Para><InlineAttributes attributes="string: &quot;attributed&quot;">Attribute text</InlineAttributes></Para></Discussion></CommentParts></Function>]
}

@objc public class AutomaticLink {
// CHECK: {{.*}}DocCommentAsXML=none
/// And now for a URL.
Expand Down Expand Up @@ -198,6 +207,18 @@ public enum A012_AttachToEntities {
// CHECK: {{.*}}DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>f4()</Name><USR>c:@M@comment_to_xml@objc(cs)EmptyComments(im)f4</USR><Declaration>@objc public func f4()</Declaration><CommentParts><Abstract><Para>Aaa.</Para></Abstract></CommentParts></Function>]
}

@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=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>f0()</Name><USR>c:@M@comment_to_xml@objc(cs)Footnotes(im)f0</USR><Declaration>@objc public func f0()</Declaration><CommentParts><Abstract><Para>Has some footnotes.</Para></Abstract><Discussion><Para>Footnotes aren’t handled by swiftMarkup yet[^footnote], but they may in the future.</Para><Para>[^footnote]: Footnotes aren’t parsed by default in swift-cmark, and swiftMarkup doesn’t enable the feature.</Para></Discussion></CommentParts></Function>]
}

@objc public class HasThrowingFunction {
// CHECK: {{.*}}DocCommentAsXML=none

Expand Down
18 changes: 18 additions & 0 deletions test/PrintAsObjC/Inputs/comments-expected-output.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down