aboutsummaryrefslogtreecommitdiffstats
path: root/src/compiler.rs
diff options
context:
space:
mode:
authorLibravatar Titus Wormer <tituswormer@gmail.com>2022-07-15 12:07:06 +0200
committerLibravatar Titus Wormer <tituswormer@gmail.com>2022-07-15 12:07:06 +0200
commit8e025d192743b2cdc2d9313d9074dc42630c811f (patch)
tree35ba093702d0c04737a15f3c17e4a278c54136ef /src/compiler.rs
parentff066a485354f1a39585450f1cbbbab93c5ffd2a (diff)
downloadmarkdown-rs-8e025d192743b2cdc2d9313d9074dc42630c811f.tar.gz
markdown-rs-8e025d192743b2cdc2d9313d9074dc42630c811f.tar.bz2
markdown-rs-8e025d192743b2cdc2d9313d9074dc42630c811f.zip
Refactor to sort, document lists in compiler
Diffstat (limited to 'src/compiler.rs')
-rw-r--r--src/compiler.rs299
1 files changed, 146 insertions, 153 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index fc0f986..7b50147 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -435,6 +435,9 @@ pub fn compile(events: &[Event], codes: &[Code], options: &Options) -> String {
enter_map.insert(Token::Image, on_enter_image);
enter_map.insert(Token::Label, on_enter_buffer);
enter_map.insert(Token::Link, on_enter_link);
+ enter_map.insert(Token::ListItemMarker, on_enter_list_item_marker);
+ enter_map.insert(Token::ListOrdered, on_enter_list);
+ enter_map.insert(Token::ListUnordered, on_enter_list);
enter_map.insert(Token::Paragraph, on_enter_paragraph);
enter_map.insert(Token::ReferenceString, on_enter_buffer);
enter_map.insert(Token::Resource, on_enter_resource);
@@ -445,11 +448,6 @@ pub fn compile(events: &[Event], codes: &[Code], options: &Options) -> String {
enter_map.insert(Token::ResourceTitleString, on_enter_buffer);
enter_map.insert(Token::Strong, on_enter_strong);
- // To do: sort.
- enter_map.insert(Token::ListItemMarker, on_enter_list_item_marker);
- enter_map.insert(Token::ListOrdered, on_enter_list);
- enter_map.insert(Token::ListUnordered, on_enter_list);
-
let mut exit_map: Map = HashMap::new();
exit_map.insert(Token::AutolinkEmail, on_exit_autolink_email);
exit_map.insert(Token::AutolinkProtocol, on_exit_autolink_protocol);
@@ -513,6 +511,10 @@ pub fn compile(events: &[Event], codes: &[Code], options: &Options) -> String {
exit_map.insert(Token::Label, on_exit_label);
exit_map.insert(Token::LabelText, on_exit_label_text);
exit_map.insert(Token::LineEnding, on_exit_line_ending);
+ exit_map.insert(Token::ListOrdered, on_exit_list);
+ exit_map.insert(Token::ListUnordered, on_exit_list);
+ exit_map.insert(Token::ListItem, on_exit_list_item);
+ exit_map.insert(Token::ListItemValue, on_exit_list_item_value);
exit_map.insert(Token::Link, on_exit_media);
exit_map.insert(Token::Paragraph, on_exit_paragraph);
exit_map.insert(Token::ReferenceString, on_exit_reference_string);
@@ -525,12 +527,6 @@ pub fn compile(events: &[Event], codes: &[Code], options: &Options) -> String {
exit_map.insert(Token::Strong, on_exit_strong);
exit_map.insert(Token::ThematicBreak, on_exit_thematic_break);
- // To do: sort.
- exit_map.insert(Token::ListItemValue, on_exit_list_item_value);
- exit_map.insert(Token::ListItem, on_exit_list_item);
- exit_map.insert(Token::ListOrdered, on_exit_list);
- exit_map.insert(Token::ListUnordered, on_exit_list);
-
// Handle one event.
let handle = |context: &mut CompileContext, index: usize| {
let event = &events[index];
@@ -709,6 +705,91 @@ fn on_enter_link(context: &mut CompileContext) {
});
}
+/// Handle [`Enter`][EventType::Enter]:{[`ListOrdered`][Token::ListOrdered],[`ListUnordered`][Token::ListUnordered]}.
+fn on_enter_list(context: &mut CompileContext) {
+ let events = &context.events;
+ let mut index = context.index;
+ let mut balance = 0;
+ let mut loose = false;
+ let token_type = &events[index].token_type;
+
+ while index < events.len() {
+ let event = &events[index];
+
+ if event.event_type == EventType::Enter {
+ balance += 1;
+ } else {
+ balance -= 1;
+
+ // Blank line directly in list or directly in list item,
+ // but not a blank line after an empty list item.
+ if balance < 3 && event.token_type == Token::BlankLineEnding
+ {
+ let at_marker = balance == 2
+ && events[skip::opt_back(
+ events,
+ index - 2,
+ &[Token::BlankLineEnding, Token::SpaceOrTab],
+ )]
+ .token_type
+ == Token::ListItemPrefix;
+ let at_list_item = balance == 1 && events[index - 2].token_type == Token::ListItem;
+ let at_empty_list_item = if at_list_item {
+ let before_item = skip::opt_back(events, index - 2, &[Token::ListItem]);
+ let before_prefix = skip::opt_back(
+ events,
+ index - 3,
+ &[Token::ListItemPrefix, Token::SpaceOrTab],
+ );
+ before_item + 1 == before_prefix
+ } else {
+ false
+ };
+
+ if !at_marker && !at_list_item && !at_empty_list_item {
+ loose = true;
+ break;
+ }
+ }
+
+ // Done.
+ if balance == 0 && event.token_type == *token_type {
+ break;
+ }
+ }
+
+ index += 1;
+ }
+
+ context.tight_stack.push(!loose);
+ context.line_ending_if_needed();
+ // Note: no `>`.
+ context.tag(format!(
+ "<{}",
+ if *token_type == Token::ListOrdered {
+ "ol"
+ } else {
+ "ul"
+ }
+ ));
+ context.expect_first_item = Some(true);
+}
+
+/// Handle [`Enter`][EventType::Enter]:[`ListItemMarker`][Token::ListItemMarker].
+fn on_enter_list_item_marker(context: &mut CompileContext) {
+ let expect_first_item = context.expect_first_item.take().unwrap();
+
+ if expect_first_item {
+ context.tag(">".to_string());
+ }
+
+ context.line_ending_if_needed();
+ context.tag("<li>".to_string());
+ context.expect_first_item = Some(false);
+ // “Hack” to prevent a line ending from showing up if the item is empty.
+ context.last_was_tag = false;
+}
+
/// Handle [`Enter`][EventType::Enter]:[`Paragraph`][Token::Paragraph].
fn on_enter_paragraph(context: &mut CompileContext) {
let tight = context.tight_stack.last().unwrap_or(&false);
@@ -1082,6 +1163,60 @@ fn on_exit_line_ending(context: &mut CompileContext) {
}
}
+/// Handle [`Exit`][EventType::Exit]:{[`ListOrdered`][Token::ListOrdered],[`ListUnordered`][Token::ListUnordered]}.
+fn on_exit_list(context: &mut CompileContext) {
+ let tag_name = if context.events[context.index].token_type == Token::ListOrdered {
+ "ol"
+ } else {
+ "ul"
+ };
+ context.tight_stack.pop();
+ context.line_ending();
+ context.tag(format!("</{}>", tag_name));
+}
+
+/// Handle [`Exit`][EventType::Exit]:[`ListItem`][Token::ListItem].
+fn on_exit_list_item(context: &mut CompileContext) {
+ let tight = context.tight_stack.last().unwrap_or(&false);
+ let before_item = skip::opt_back(
+ context.events,
+ context.index - 1,
+ &[
+ Token::BlankLineEnding,
+ Token::LineEnding,
+ Token::SpaceOrTab,
+ Token::BlockQuotePrefix,
+ ],
+ );
+ let previous = &context.events[before_item];
+ let tight_paragraph = *tight && previous.token_type == Token::Paragraph;
+ let empty_item = previous.token_type == Token::ListItemPrefix;
+
+ if !tight_paragraph && !empty_item {
+ context.line_ending_if_needed();
+ }
+
+ context.tag("</li>".to_string());
+}
+
+/// Handle [`Exit`][EventType::Exit]:[`ListItemValue`][Token::ListItemValue].
+fn on_exit_list_item_value(context: &mut CompileContext) {
+ let expect_first_item = context.expect_first_item.unwrap();
+
+ if expect_first_item {
+ let slice = serialize(
+ context.codes,
+ &from_exit_event(context.events, context.index),
+ false,
+ );
+ let value = slice.parse::<u32>().ok().unwrap();
+
+ if value != 1 {
+ context.tag(format!(" start=\"{}\"", encode(&value.to_string())));
+ }
+ }
+}
+
/// Handle [`Exit`][EventType::Exit]:{[`Image`][Token::Image],[`Link`][Token::Link]}.
fn on_exit_media(context: &mut CompileContext) {
let mut is_in_image = false;
@@ -1194,145 +1329,3 @@ fn on_exit_thematic_break(context: &mut CompileContext) {
context.line_ending_if_needed();
context.tag("<hr />".to_string());
}
-
-// To do: sort.
-/// To do (onenterlist{un,}ordered)
-fn on_enter_list(context: &mut CompileContext) {
- let events = &context.events;
- let mut index = context.index;
- let mut balance = 0;
- let mut loose = false;
- let token_type = &events[index].token_type;
-
- while index < events.len() {
- let event = &events[index];
-
- if event.event_type == EventType::Enter {
- balance += 1;
- } else {
- balance -= 1;
-
- // Blank line directly in list or directly in list item,
- // but not a blank line after an empty list item.
- // To do: does this check if the item is empty?
- if balance < 3 && event.token_type == Token::BlankLineEnding
- // && !(balance == 1 && events[index - 2].token_type == Token::ListItem)
- {
- let at_marker = balance == 2
- && events[skip::opt_back(
- events,
- index - 2,
- &[Token::BlankLineEnding, Token::SpaceOrTab],
- )]
- .token_type
- == Token::ListItemPrefix;
- let at_list_item = balance == 1 && events[index - 2].token_type == Token::ListItem;
- let at_empty_list_item = if at_list_item {
- let before_item = skip::opt_back(events, index - 2, &[Token::ListItem]);
- let before_prefix = skip::opt_back(
- events,
- index - 3,
- &[Token::ListItemPrefix, Token::SpaceOrTab],
- );
- before_item + 1 == before_prefix
- } else {
- false
- };
-
- if !at_marker && !at_list_item && !at_empty_list_item {
- loose = true;
- break;
- }
- }
-
- // Done.
- if balance == 0 && event.token_type == *token_type {
- break;
- }
- }
-
- index += 1;
- }
-
- context.tight_stack.push(!loose);
- context.line_ending_if_needed();
- // Note: no `>`.
- context.tag(format!(
- "<{}",
- if *token_type == Token::ListOrdered {
- "ol"
- } else {
- "ul"
- }
- ));
- context.expect_first_item = Some(true);
-}
-
-/// To do
-fn on_enter_list_item_marker(context: &mut CompileContext) {
- let expect_first_item = context.expect_first_item.take().unwrap();
-
- if expect_first_item {
- context.tag(">".to_string());
- }
-
- context.line_ending_if_needed();
- context.tag("<li>".to_string());
- context.expect_first_item = Some(false);
- // “Hack” to prevent a line ending from showing up if the item is empty.
- context.last_was_tag = false;
-}
-
-/// To do
-fn on_exit_list_item_value(context: &mut CompileContext) {
- let expect_first_item = context.expect_first_item.unwrap();
-
- if expect_first_item {
- let slice = serialize(
- context.codes,
- &from_exit_event(context.events, context.index),
- false,
- );
- let value = slice.parse::<u32>().ok().unwrap();
-
- if value != 1 {
- context.tag(format!(" start=\"{}\"", encode(&value.to_string())));
- }
- }
-}
-
-/// To do.
-fn on_exit_list_item(context: &mut CompileContext) {
- let tight = context.tight_stack.last().unwrap_or(&false);
- let before_item = skip::opt_back(
- context.events,
- context.index - 1,
- &[
- Token::BlankLineEnding,
- Token::LineEnding,
- Token::SpaceOrTab,
- Token::BlockQuotePrefix,
- ],
- );
- let previous = &context.events[before_item];
- let tight_paragraph = *tight && previous.token_type == Token::Paragraph;
- let empty_item = previous.token_type == Token::ListItemPrefix;
-
- if !tight_paragraph && !empty_item {
- context.line_ending_if_needed();
- }
-
- context.tag("</li>".to_string());
-}
-
-/// To do.
-fn on_exit_list(context: &mut CompileContext) {
- let tag_name = if context.events[context.index].token_type == Token::ListOrdered {
- "ol"
- } else {
- "ul"
- };
- context.tight_stack.pop();
- context.line_ending();
- context.tag(format!("</{}>", tag_name));
-}