blob: 09739133ce26246b86d3e528c61aac2bd36da067 (
plain) (
tree)
|
|
//! Infer things from events.
//!
//! Used to share between `to_html` and `to_mdast`.
use crate::event::{Event, Kind, Name};
use crate::mdast::AlignKind;
use alloc::{vec, vec::Vec};
/// Figure out if a list is spread or not.
///
/// When `include_items: true` is passed, infers whether the list as a whole
/// is “loose”.
pub fn list_loose(events: &[Event], mut index: usize, include_items: bool) -> bool {
let mut balance = 0;
let name = &events[index].name;
debug_assert!(
matches!(name, Name::ListOrdered | Name::ListUnordered),
"expected list"
);
while index < events.len() {
let event = &events[index];
if event.kind == Kind::Enter {
balance += 1;
if include_items
&& balance == 2
&& event.name == Name::ListItem
&& list_item_loose(events, index)
{
return true;
}
} else {
balance -= 1;
if balance == 1 && event.name == Name::BlankLineEnding {
// Blank line directly after item, which is just a prefix.
//
// ```markdown
// > | -␊
// ^
// | - a
// ```
let mut at_empty_list_item = false;
// Blank line at block quote prefix:
//
// ```markdown
// > | * >␊
// ^
// | * a
// ```
let mut at_empty_block_quote = false;
// List.
let mut before = index - 2;
if events[before].name == Name::ListItem {
before -= 1;
if events[before].name == Name::SpaceOrTab {
before -= 2;
}
if events[before].name == Name::BlockQuote
&& events[before - 1].name == Name::BlockQuotePrefix
{
at_empty_block_quote = true;
} else if events[before].name == Name::ListItemPrefix {
at_empty_list_item = true;
}
}
if !at_empty_list_item && !at_empty_block_quote {
return true;
}
}
// Done.
if balance == 0 && event.name == *name {
break;
}
}
index += 1;
}
false
}
/// Figure out if an item is spread or not.
pub fn list_item_loose(events: &[Event], mut index: usize) -> bool {
debug_assert!(
matches!(events[index].name, Name::ListItem),
"expected list item"
);
let mut balance = 0;
while index < events.len() {
let event = &events[index];
if event.kind == Kind::Enter {
balance += 1;
} else {
balance -= 1;
if balance == 1 && event.name == Name::BlankLineEnding {
// Blank line directly after a prefix:
//
// ```markdown
// > | -␊
// ^
// | a
// ```
let mut at_prefix = false;
// List item.
let mut before = index - 2;
if events[before].name == Name::SpaceOrTab {
before -= 2;
}
if events[before].name == Name::ListItemPrefix {
at_prefix = true;
}
if !at_prefix {
return true;
}
}
// Done.
if balance == 0 && event.name == Name::ListItem {
break;
}
}
index += 1;
}
false
}
/// Figure out the alignment of a GFM table.
pub fn gfm_table_align(events: &[Event], mut index: usize) -> Vec<AlignKind> {
debug_assert!(
matches!(events[index].name, Name::GfmTable),
"expected table"
);
let mut in_delimiter_row = false;
let mut align = vec![];
while index < events.len() {
let event = &events[index];
if in_delimiter_row {
if event.kind == Kind::Enter {
// Start of alignment value: set a new column.
if event.name == Name::GfmTableDelimiterCellValue {
align.push(if events[index + 1].name == Name::GfmTableDelimiterMarker {
AlignKind::Left
} else {
AlignKind::None
});
}
} else {
// End of alignment value: change the column.
if event.name == Name::GfmTableDelimiterCellValue {
if events[index - 1].name == Name::GfmTableDelimiterMarker {
let align_index = align.len() - 1;
align[align_index] = if align[align_index] == AlignKind::Left {
AlignKind::Center
} else {
AlignKind::Right
}
}
}
// Done!
else if event.name == Name::GfmTableDelimiterRow {
break;
}
}
} else if event.kind == Kind::Enter && event.name == Name::GfmTableDelimiterRow {
in_delimiter_row = true;
}
index += 1;
}
align
}
|