Skip to content

Commit

Permalink
Autodoc: implement Markdown autolinks
Browse files Browse the repository at this point in the history
Closes #19265

This commit implements support for Markdown autolinks delimited by angle
brackets. The precise syntax accepted is documented in the doc comment
of `markdown.zig`.
  • Loading branch information
ianprime0509 committed Mar 23, 2024
1 parent 13a9d94 commit d3ca9d5
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 1 deletion.
30 changes: 30 additions & 0 deletions lib/docs/wasm/markdown.zig
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@
//! content. `target` may contain `\`-escaped characters and balanced
//! parentheses.
//!
//! - **Autolink** - an abbreviated link, of the format `<target>`, where
//! `target` serves as both the link target and text. `target` may not
//! contain spaces or `<`, and any `\` in it are interpreted literally (not as
//! escapes). `target` is expected to be an absolute URI: an autolink will not
//! be recognized unless `target` starts with a URI scheme followed by a `:`.
//!
//! - **Image** - a link directly preceded by a `!`. The link text is
//! interpreted as the alt text of the image.
//!
Expand Down Expand Up @@ -710,6 +716,30 @@ test "links" {
);
}

test "autolinks" {
try testRender(
\\<https://example.com>
\\**This is important: <https://example.com/strong>**
\\<https://example.com?query=abc.123#page(parens)>
\\<placeholder>
\\<data:>
\\1 < 2
\\4 > 3
\\Unclosed: <
\\
,
\\<p><a href="https://example.com">https://example.com</a>
\\<strong>This is important: <a href="https://example.com/strong">https://example.com/strong</a></strong>
\\<a href="https://example.com?query=abc.123#page(parens)">https://example.com?query=abc.123#page(parens)</a>
\\&lt;placeholder&gt;
\\<a href="data:">data:</a>
\\1 &lt; 2
\\4 &gt; 3
\\Unclosed: &lt;</p>
\\
);
}

test "images" {
try testRender(
\\![Alt text](https://example.com/image.png)
Expand Down
2 changes: 2 additions & 0 deletions lib/docs/wasm/markdown/Document.zig
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub const Node = struct {
// Inlines
/// Data is `link`.
link,
/// Data is `text`.
autolink,
/// Data is `link`.
image,
/// Data is `container`.
Expand Down
47 changes: 47 additions & 0 deletions lib/docs/wasm/markdown/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@ const InlineParser = struct {
ip.pos += 1;
},
']' => try ip.parseLink(),
'<' => try ip.parseAutolink(),
'*', '_' => try ip.parseEmphasis(),
'`' => try ip.parseCodeSpan(),
else => {},
Expand Down Expand Up @@ -1076,6 +1077,52 @@ const InlineParser = struct {
return @enumFromInt(string_top);
}

/// Parses an autolink, starting at the opening `<`. `ip.pos` is left at the
/// closing `>`, or remains unchanged at the opening `<` if there is none.
fn parseAutolink(ip: *InlineParser) !void {
const start = ip.pos;
ip.pos += 1;
var state: enum {
start,
scheme,
target,
} = .start;
while (ip.pos < ip.content.len) : (ip.pos += 1) {
switch (state) {
.start => switch (ip.content[ip.pos]) {
'A'...'Z', 'a'...'z' => state = .scheme,
else => break,
},
.scheme => switch (ip.content[ip.pos]) {
'A'...'Z', 'a'...'z', '0'...'9', '+', '.', '-' => {},
':' => state = .target,
else => break,
},
.target => switch (ip.content[ip.pos]) {
'<', ' ', '\t', '\n' => break, // Not allowed in autolinks
'>' => {
// Backslash escapes are not recognized in autolink targets.
const target = try ip.parent.addString(ip.content[start + 1 .. ip.pos]);
const node = try ip.parent.addNode(.{
.tag = .autolink,
.data = .{ .text = .{
.content = target,
} },
});
try ip.completed_inlines.append(ip.parent.allocator, .{
.node = node,
.start = start,
.len = ip.pos - start + 1,
});
return;
},
else => {},
},
}
}
ip.pos = start;
}

/// Parses emphasis, starting at the beginning of a run of `*` or `_`
/// characters. `ip.pos` is left at the last character in the run after
/// parsing.
Expand Down
6 changes: 5 additions & 1 deletion lib/docs/wasm/markdown/renderer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ pub fn Renderer(comptime Writer: type, comptime Context: type) type {
}
try writer.writeAll("</a>");
},
.autolink => {
const target = doc.string(data.text.content);
try writer.print("<a href=\"{0}\">{0}</a>", .{fmtHtml(target)});
},
.image => {
const target = doc.string(data.link.target);
try writer.print("<img src=\"{}\" alt=\"", .{fmtHtml(target)});
Expand Down Expand Up @@ -215,7 +219,7 @@ pub fn renderInlineNodeText(
try renderInlineNodeText(doc, child, writer);
}
},
.code_span, .text => {
.autolink, .code_span, .text => {
const content = doc.string(data.text.content);
try writer.print("{}", .{fmtHtml(content)});
},
Expand Down

0 comments on commit d3ca9d5

Please sign in to comment.