diff options
| author | 2022-07-15 12:07:06 +0200 | |
|---|---|---|
| committer | 2022-07-15 12:07:06 +0200 | |
| commit | 8e025d192743b2cdc2d9313d9074dc42630c811f (patch) | |
| tree | 35ba093702d0c04737a15f3c17e4a278c54136ef /src/compiler.rs | |
| parent | ff066a485354f1a39585450f1cbbbab93c5ffd2a (diff) | |
| download | markdown-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.rs | 299 | 
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)); -} | 
