A block element which is still receiving children.
const Block = struct
const Block = struct {
tag: Tag,
data: Data,
extra_start: usize,
string_start: usize,
const Tag = enum {
/// Data is `list`.
list,
/// Data is `list_item`.
list_item,
/// Data is `table`.
table,
/// Data is `none`.
table_row,
/// Data is `heading`.
heading,
/// Data is `code_block`.
code_block,
/// Data is `none`.
blockquote,
/// Data is `none`.
paragraph,
/// Data is `none`.
thematic_break,
};
const Data = union {
none: void,
list: struct {
marker: ListMarker,
/// Between 0 and 999,999,999, inclusive.
start: u30,
tight: bool,
last_line_blank: bool = false,
},
list_item: struct {
continuation_indent: usize,
},
table: struct {
column_alignments_buffer: [max_table_columns]Node.TableCellAlignment,
column_alignments_len: usize,
},
heading: struct {
/// Between 1 and 6, inclusive.
level: u3,
},
code_block: struct {
tag: StringIndex,
fence_len: usize,
indent: usize,
},
const ListMarker = enum {
@"-",
@"*",
@"+",
number_dot,
number_paren,
};
};
const ContentType = enum {
blocks,
inlines,
raw_inlines,
nothing,
};
fn canAccept(b: Block) ContentType {
return switch (b.tag) {
.list,
.list_item,
.table,
.blockquote,
=> .blocks,
.heading,
.paragraph,
=> .inlines,
.code_block,
=> .raw_inlines,
.table_row,
.thematic_break,
=> .nothing,
};
}
/// Attempts to continue `b` using the contents of `line`. If successful,
/// returns the remaining portion of `line` to be considered part of `b`
/// (e.g. for a blockquote, this would be everything except the leading
/// `>`). If unsuccessful, returns null.
fn match(b: Block, line: []const u8) ?[]const u8 {
const unindented = mem.trimStart(u8, line, " \t");
const indent = line.len - unindented.len;
return switch (b.tag) {
.list => line,
.list_item => if (indent >= b.data.list_item.continuation_indent)
line[b.data.list_item.continuation_indent..]
else if (unindented.len == 0)
// Blank lines should not close list items, since there may be
// more indented contents to follow after the blank line.
""
else
null,
.table => if (unindented.len > 0) line else null,
.table_row => null,
.heading => null,
.code_block => code_block: {
const trimmed = mem.trimEnd(u8, unindented, " \t");
if (mem.indexOfNone(u8, trimmed, "`") != null or trimmed.len != b.data.code_block.fence_len) {
const effective_indent = @min(indent, b.data.code_block.indent);
break :code_block line[effective_indent..];
} else {
break :code_block null;
}
},
.blockquote => if (mem.startsWith(u8, unindented, ">"))
unindented[1..]
else
null,
.paragraph => if (unindented.len > 0) line else null,
.thematic_break => null,
};
}
}