Autodoc: fix Markdown indented lists (#19577)

Previously, indentation was not being handled correctly in some cases,
causing examples such as `std.json.WriteStream` to be rendered with
improper list nesting.

Additionally, some more test cases have been added to ensure
indentation (or lack of indentation) is handled correctly in some other
constructs.
This commit is contained in:
Ian Johnson 2024-04-08 05:49:22 -04:00 committed by GitHub
parent 355cceebc7
commit 6dcbad780c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 145 additions and 13 deletions

View file

@ -376,6 +376,106 @@ test "lists with block content" {
);
}
test "indented lists" {
try testRender(
\\Test:
\\ * a1
\\ * a2
\\ * b1
\\ * b2
\\
\\---
\\
\\ Test:
\\ - One
\\Two
\\ - Three
\\Four
\\ Five
\\Six
\\
\\---
\\
\\None of these items are indented far enough from the previous one to
\\start a nested list:
\\ - One
\\ - Two
\\ - Three
\\ - Four
\\ - Five
\\ - Six
\\ - Seven
\\ - Eight
\\ - Nine
\\
\\---
\\
\\ - One
\\ - Two
\\ - Three
\\ - Four
\\ - Five
\\ - Six
\\- Seven
\\
,
\\<p>Test:</p>
\\<ul>
\\<li>a1</li>
\\<li>a2<ul>
\\<li>b1</li>
\\<li>b2</li>
\\</ul>
\\</li>
\\</ul>
\\<hr />
\\<p>Test:</p>
\\<ul>
\\<li>One
\\Two<ul>
\\<li>Three
\\Four
\\Five
\\Six</li>
\\</ul>
\\</li>
\\</ul>
\\<hr />
\\<p>None of these items are indented far enough from the previous one to
\\start a nested list:</p>
\\<ul>
\\<li>One</li>
\\<li>Two</li>
\\<li>Three</li>
\\<li>Four</li>
\\<li>Five</li>
\\<li>Six</li>
\\<li>Seven</li>
\\<li>Eight</li>
\\<li>Nine</li>
\\</ul>
\\<hr />
\\<ul>
\\<li>One<ul>
\\<li>Two<ul>
\\<li>Three<ul>
\\<li>Four</li>
\\</ul>
\\</li>
\\</ul>
\\</li>
\\<li>Five<ul>
\\<li>Six</li>
\\</ul>
\\</li>
\\</ul>
\\</li>
\\<li>Seven</li>
\\</ul>
\\
);
}
test "tables" {
try testRender(
\\| Operator | Meaning |
@ -394,6 +494,10 @@ test "tables" {
\\| :--- | :----: | ----: |
\\| Left | Center | Right |
\\
\\ | One | Two |
\\ | Three | Four |
\\ | Five | Six |
\\
,
\\<table>
\\<tr>
@ -446,6 +550,20 @@ test "tables" {
\\<td style="text-align: right">Right</td>
\\</tr>
\\</table>
\\<table>
\\<tr>
\\<td>One</td>
\\<td>Two</td>
\\</tr>
\\<tr>
\\<td>Three</td>
\\<td>Four</td>
\\</tr>
\\<tr>
\\<td>Five</td>
\\<td>Six</td>
\\</tr>
\\</table>
\\
);
}
@ -597,6 +715,14 @@ test "code blocks" {
\\ try std.testing.expect(2 + 2 == 4);
\\}
\\```
\\ ```
\\ Indentation up to the fence is removed.
\\ Like this.
\\ Doesn't need to be fully indented.
\\ ```
\\```
\\Overly indented closing fence is fine:
\\ ```
\\
,
\\<pre><code>Hello, world!
@ -608,6 +734,12 @@ test "code blocks" {
\\ try std.testing.expect(2 + 2 == 4);
\\}
\\</code></pre>
\\<pre><code>Indentation up to the fence is removed.
\\ Like this.
\\Doesn't need to be fully indented.
\\</code></pre>
\\<pre><code>Overly indented closing fence is fine:
\\</code></pre>
\\
);
}

View file

@ -152,7 +152,7 @@ const Block = struct {
""
else
null,
.table => if (unindented.len > 0) unindented else null,
.table => if (unindented.len > 0) line else null,
.table_row => null,
.heading => null,
.code_block => code_block: {
@ -168,7 +168,7 @@ const Block = struct {
unindented[1..]
else
null,
.paragraph => if (unindented.len > 0) unindented else null,
.paragraph => if (unindented.len > 0) line else null,
.thematic_break => null,
};
}
@ -225,7 +225,7 @@ pub fn feedLine(p: *Parser, line: []const u8) Allocator.Error!void {
p.pending_blocks.items.len > 0 and
p.pending_blocks.getLast().tag == .paragraph)
{
try p.addScratchStringLine(rest_line);
try p.addScratchStringLine(mem.trimLeft(u8, rest_line, " \t"));
return;
}
@ -271,8 +271,8 @@ pub fn feedLine(p: *Parser, line: []const u8) Allocator.Error!void {
// loose, since we might just be looking at a blank line after the
// end of the last item in the list. The final determination will be
// made when appending the next child of the list or list item.
const maybe_containing_list = if (p.pending_blocks.items.len > 0 and p.pending_blocks.getLast().tag == .list_item)
&p.pending_blocks.items[p.pending_blocks.items.len - 2]
const maybe_containing_list_index = if (p.pending_blocks.items.len > 0 and p.pending_blocks.getLast().tag == .list_item)
p.pending_blocks.items.len - 2
else
null;
@ -285,8 +285,8 @@ pub fn feedLine(p: *Parser, line: []const u8) Allocator.Error!void {
try p.addScratchStringLine(rest_line_trimmed);
}
if (maybe_containing_list) |containing_list| {
containing_list.data.list.last_line_blank = rest_line_trimmed.len == 0;
if (maybe_containing_list_index) |containing_list_index| {
p.pending_blocks.items[containing_list_index].data.list.last_line_blank = rest_line_trimmed.len == 0;
}
},
.inlines => try p.addScratchStringLine(rest_line_trimmed),
@ -515,7 +515,7 @@ fn startBlock(p: *Parser, line: []const u8) !?BlockStart {
.data = .{ .list_item = .{
.marker = list_item.marker,
.number = list_item.number,
.continuation_indent = list_item.continuation_indent,
.continuation_indent = indent + list_item.marker_len,
} },
.rest = list_item.rest,
};
@ -559,7 +559,7 @@ fn startBlock(p: *Parser, line: []const u8) !?BlockStart {
const ListItemStart = struct {
marker: Block.Data.ListMarker,
number: u30,
continuation_indent: usize,
marker_len: usize,
rest: []const u8,
};
@ -568,21 +568,21 @@ fn startListItem(unindented_line: []const u8) ?ListItemStart {
return .{
.marker = .@"-",
.number = undefined,
.continuation_indent = 2,
.marker_len = 2,
.rest = unindented_line[2..],
};
} else if (mem.startsWith(u8, unindented_line, "* ")) {
return .{
.marker = .@"*",
.number = undefined,
.continuation_indent = 2,
.marker_len = 2,
.rest = unindented_line[2..],
};
} else if (mem.startsWith(u8, unindented_line, "+ ")) {
return .{
.marker = .@"+",
.number = undefined,
.continuation_indent = 2,
.marker_len = 2,
.rest = unindented_line[2..],
};
}
@ -600,7 +600,7 @@ fn startListItem(unindented_line: []const u8) ?ListItemStart {
return .{
.marker = marker,
.number = number,
.continuation_indent = number_end + 2,
.marker_len = number_end + 2,
.rest = after_number[2..],
};
}