diff --git a/src/configuration.rs b/src/configuration.rs index 4f0281f..341cb57 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -115,6 +115,13 @@ pub struct Constructs { /// ^^^ /// ```` pub frontmatter: bool, + /// wikilink syntax. + /// + /// ```markdown + /// > | [[a]] + /// ^^^^^ + /// ``` + pub wikilink: bool, /// GFM: autolink literal. /// /// ```markdown @@ -379,6 +386,7 @@ impl Default for Constructs { mdx_jsx_flow: false, mdx_jsx_text: false, thematic_break: true, + wikilink: false, } } } @@ -1456,7 +1464,7 @@ mod tests { assert_eq!( format!("{:?}", ParseOptions::default()), - "ParseOptions { constructs: Constructs { attention: true, autolink: true, block_quote: true, character_escape: true, character_reference: true, code_indented: true, code_fenced: true, code_text: true, definition: true, frontmatter: false, gfm_autolink_literal: false, gfm_footnote_definition: false, gfm_label_start_footnote: false, gfm_strikethrough: false, gfm_table: false, gfm_task_list_item: false, hard_break_escape: true, hard_break_trailing: true, heading_atx: true, heading_setext: true, html_flow: true, html_text: true, label_start_image: true, label_start_link: true, label_end: true, list_item: true, math_flow: false, math_text: false, mdx_esm: false, mdx_expression_flow: false, mdx_expression_text: false, mdx_jsx_flow: false, mdx_jsx_text: false, thematic_break: true }, gfm_strikethrough_single_tilde: true, math_text_single_dollar: true, mdx_expression_parse: None, mdx_esm_parse: None }", + "ParseOptions { constructs: Constructs { attention: true, autolink: true, block_quote: true, character_escape: true, character_reference: true, code_indented: true, code_fenced: true, code_text: true, definition: true, frontmatter: false, wikilink: false, gfm_autolink_literal: false, gfm_footnote_definition: false, gfm_label_start_footnote: false, gfm_strikethrough: false, gfm_table: false, gfm_task_list_item: false, hard_break_escape: true, hard_break_trailing: true, heading_atx: true, heading_setext: true, html_flow: true, html_text: true, label_start_image: true, label_start_link: true, label_end: true, list_item: true, math_flow: false, math_text: false, mdx_esm: false, mdx_expression_flow: false, mdx_expression_text: false, mdx_jsx_flow: false, mdx_jsx_text: false, thematic_break: true }, gfm_strikethrough_single_tilde: true, math_text_single_dollar: true, mdx_expression_parse: None, mdx_esm_parse: None }", "should support `Debug` trait" ); assert_eq!( @@ -1469,7 +1477,7 @@ mod tests { })), ..Default::default() }), - "ParseOptions { constructs: Constructs { attention: true, autolink: true, block_quote: true, character_escape: true, character_reference: true, code_indented: true, code_fenced: true, code_text: true, definition: true, frontmatter: false, gfm_autolink_literal: false, gfm_footnote_definition: false, gfm_label_start_footnote: false, gfm_strikethrough: false, gfm_table: false, gfm_task_list_item: false, hard_break_escape: true, hard_break_trailing: true, heading_atx: true, heading_setext: true, html_flow: true, html_text: true, label_start_image: true, label_start_link: true, label_end: true, list_item: true, math_flow: false, math_text: false, mdx_esm: false, mdx_expression_flow: false, mdx_expression_text: false, mdx_jsx_flow: false, mdx_jsx_text: false, thematic_break: true }, gfm_strikethrough_single_tilde: true, math_text_single_dollar: true, mdx_expression_parse: Some(\"[Function]\"), mdx_esm_parse: Some(\"[Function]\") }", + "ParseOptions { constructs: Constructs { attention: true, autolink: true, block_quote: true, character_escape: true, character_reference: true, code_indented: true, code_fenced: true, code_text: true, definition: true, frontmatter: false, wikilink: false, gfm_autolink_literal: false, gfm_footnote_definition: false, gfm_label_start_footnote: false, gfm_strikethrough: false, gfm_table: false, gfm_task_list_item: false, hard_break_escape: true, hard_break_trailing: true, heading_atx: true, heading_setext: true, html_flow: true, html_text: true, label_start_image: true, label_start_link: true, label_end: true, list_item: true, math_flow: false, math_text: false, mdx_esm: false, mdx_expression_flow: false, mdx_expression_text: false, mdx_jsx_flow: false, mdx_jsx_text: false, thematic_break: true }, gfm_strikethrough_single_tilde: true, math_text_single_dollar: true, mdx_expression_parse: Some(\"[Function]\"), mdx_esm_parse: Some(\"[Function]\") }", "should support `Debug` trait on mdx functions" ); } diff --git a/src/construct/mod.rs b/src/construct/mod.rs index 6098313..8f030b0 100644 --- a/src/construct/mod.rs +++ b/src/construct/mod.rs @@ -196,3 +196,5 @@ pub mod raw_text; pub mod string; pub mod text; pub mod thematic_break; +pub mod wikilink; +pub mod wikilink_label; diff --git a/src/construct/text.rs b/src/construct/text.rs index a461d02..82037b3 100644 --- a/src/construct/text.rs +++ b/src/construct/text.rs @@ -138,7 +138,7 @@ pub fn before(tokenizer: &mut Tokenizer) -> State { Some(b'[') => { tokenizer.attempt( State::Next(StateName::TextBefore), - State::Next(StateName::TextBeforeLabelStartLink), + State::Next(StateName::TextBeforeWikilinkStart), ); State::Retry(StateName::GfmLabelStartFootnoteStart) } @@ -215,8 +215,25 @@ pub fn before_hard_break_escape(tokenizer: &mut Tokenizer) -> State { State::Retry(StateName::HardBreakEscapeStart) } +/// Before wikilink start. +/// +/// At `[`, which wasn’t a GFM label start (footnote). +/// +/// ```markdown +/// > | [[a]] +/// ^ +/// ``` +pub fn before_label_wikilink_start(tokenizer: &mut Tokenizer) -> State { + tokenizer.attempt( + State::Next(StateName::TextBefore), + State::Next(StateName::TextBeforeData), + ); + State::Retry(StateName::WikilinkStart) +} + /// Before label start (link). /// +/// /// At `[`, which wasn’t a GFM label start (footnote). /// /// ```markdown diff --git a/src/construct/wikilink.rs b/src/construct/wikilink.rs new file mode 100644 index 0000000..929ccd7 --- /dev/null +++ b/src/construct/wikilink.rs @@ -0,0 +1,88 @@ +//! wikilink occurs in the [[text]][] content type. +//! +//! ## Grammar +//! +//! ```bnf +//! wikilink ::= '[[' label opt_alias opt_heading opt_block ']]' +//! +//! opt_alias ::= '|' label | '' +//! opt_heading ::= '#' label | '' +//! opt_block ::= '^' label | '' +//! +//! ; See the `label` constructs for the BNF of +//! ; those parts. +//! ``` +//! + +use crate::event::Name; +use crate::state::Name as MoveName; +use crate::state::State; +use crate::tokenizer::{Label, LabelKind, LabelStart, Tokenizer}; + +/// Start of label (link) start. +/// +/// ```markdown +/// > | a [b] c +/// ^ +/// ``` +pub fn start(tokenizer: &mut Tokenizer) -> State { + let start_conditions = + tokenizer.parse_state.options.constructs.wikilink && tokenizer.current == Some(b'['); + + if start_conditions && tokenizer.tokenize_state.size == 0 { + tokenizer.tokenize_state.size += 1; + tokenizer.attempt(State::Next(MoveName::WikilinkStart), State::Nok); + tokenizer.consume(); + return State::Ok; + } else if start_conditions && tokenizer.tokenize_state.size == 1 { + tokenizer.tokenize_state.size = 0; + let start = tokenizer.events.len(); + + tokenizer.enter(Name::WikilinkStart); + // // tokenizer.enter(Name::LabelMarker); + tokenizer.consume(); + // // tokenizer.exit(Name::LabelMarker); + tokenizer.exit(Name::WikilinkStart); + + tokenizer.tokenize_state.wikilink_starts.push(LabelStart { + kind: LabelKind::Link, + start: (start, tokenizer.events.len()), + inactive: false, + }); + State::Next(MoveName::WikilinkLabelStart) + } else { + tokenizer.tokenize_state.size = 0; + State::Nok + } +} + +pub fn end(tokenizer: &mut Tokenizer) -> State { + if Some(b']') == tokenizer.current + && Some(b']') == tokenizer.previous + && tokenizer.parse_state.options.constructs.wikilink + && !tokenizer.tokenize_state.wikilink_starts.is_empty() + { + let wikilink_start = tokenizer.tokenize_state.wikilink_starts.last().unwrap(); + tokenizer.tokenize_state.end = tokenizer.events.len(); + + // If the corresponding label (link) start is marked as inactive, + // it means we’d be wrapping a link, like this: + // + // ```markdown + // > | a [b [c](d) e](f) g. + // ^ + // ``` + // + // We can’t have that, so it’s just balanced brackets. + if wikilink_start.inactive { + return State::Retry(MoveName::WikilinkEndNok); + } + + tokenizer.enter(Name::WikilinkEnd); + tokenizer.consume(); + tokenizer.exit(Name::WikilinkEnd); + return State::Ok; + } + + State::Nok +} diff --git a/src/construct/wikilink_label.rs b/src/construct/wikilink_label.rs new file mode 100644 index 0000000..f46510a --- /dev/null +++ b/src/construct/wikilink_label.rs @@ -0,0 +1,152 @@ +use crate::construct::partial_space_or_tab_eol::{space_or_tab_eol_with_options, Options}; +use crate::event::{Content, Link, Name}; +use crate::state::{Name as StateName, State}; +use crate::subtokenize::link; +use crate::tokenizer::Tokenizer; +use crate::util::constant::LINK_REFERENCE_SIZE_MAX; + +/// Start of label. +/// +/// ```markdown +/// > | [a] +/// ^ +/// ``` +pub fn start(tokenizer: &mut Tokenizer) -> State { + State::Retry(StateName::WikilinkLabelAtBreak) +} + +/// In label, at something, before something else. +/// +/// ```markdown +/// > | [a] +/// ^ +/// ``` +pub fn at_break(tokenizer: &mut Tokenizer) -> State { + if tokenizer.tokenize_state.size > LINK_REFERENCE_SIZE_MAX || matches!(tokenizer.current, None) + { + State::Retry(StateName::WikilinkLabelNok) + } else { + match tokenizer.current { + Some(b'\n') => { + tokenizer.attempt( + State::Next(StateName::WikilinkLabelEolAfter), + State::Next(StateName::WikilinkLabelNok), + ); + State::Retry(space_or_tab_eol_with_options( + tokenizer, + Options { + content: Some(Content::String), + connect: tokenizer.tokenize_state.connect, + }, + )) + } + + Some(b']') if tokenizer.tokenize_state.size_b == 1 => { + tokenizer.tokenize_state.size_b = 0; + State::Retry(StateName::WikilinkEnd) + } + _ => { + tokenizer.enter_link( + Name::Data, + Link { + previous: None, + next: None, + content: Content::String, + }, + ); + + if tokenizer.tokenize_state.connect { + let index = tokenizer.events.len() - 1; + link(&mut tokenizer.events, index); + } else { + tokenizer.tokenize_state.connect = true; + } + + State::Retry(StateName::WikilinkLabelInside) + } + } + } +} + +/// In label, after whitespace. +/// +/// ```markdown +/// | [a␊ +/// > | b] +/// ^ +/// ``` +pub fn eol_after(tokenizer: &mut Tokenizer) -> State { + tokenizer.tokenize_state.connect = true; + State::Retry(StateName::WikilinkLabelAtBreak) +} + +/// In label, on something disallowed. +/// +/// ```markdown +/// > | [] +/// ^ +/// ``` +pub fn nok(tokenizer: &mut Tokenizer) -> State { + tokenizer.tokenize_state.connect = false; + tokenizer.tokenize_state.seen = false; + tokenizer.tokenize_state.size = 0; + State::Nok +} + +/// In label, in text. +/// +/// ```markdown +/// > | [a] +/// ^ +/// ``` +pub fn inside(tokenizer: &mut Tokenizer) -> State { + match tokenizer.current { + None | Some(b'\n') => { + tokenizer.exit(Name::Data); + State::Retry(StateName::WikilinkLabelAtBreak) + } + Some(b']') => { + tokenizer.exit(Name::Data); + tokenizer.consume(); + tokenizer.tokenize_state.size += 1; + tokenizer.tokenize_state.size_b = 1; + tokenizer.tokenize_state.seen = false; // ?? + + State::Next(StateName::WikilinkLabelAtBreak) + } + Some(byte) => { + if tokenizer.tokenize_state.size > LINK_REFERENCE_SIZE_MAX { + tokenizer.exit(Name::Data); + State::Retry(StateName::WikilinkLabelAtBreak) + } else { + tokenizer.consume(); + tokenizer.tokenize_state.size += 1; + if !tokenizer.tokenize_state.seen && !matches!(byte, b'\t' | b' ') { + tokenizer.tokenize_state.seen = true; + } + State::Next(if matches!(byte, b'\\') { + StateName::WikilinkLabelEscape + } else { + StateName::WikilinkLabelInside + }) + } + } + } +} + +/// After `\`, at a special character. +/// +/// ```markdown +/// > | [a\*a] +/// ^ +/// ``` +pub fn escape(tokenizer: &mut Tokenizer) -> State { + match tokenizer.current { + Some(b'[' | b'\\' | b']') => { + tokenizer.consume(); + tokenizer.tokenize_state.size += 1; + State::Next(StateName::WikilinkLabelInside) + } + _ => State::Retry(StateName::WikilinkLabelInside), + } +} diff --git a/src/event.rs b/src/event.rs index 87254f7..3b310f4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1907,6 +1907,9 @@ pub enum Name { /// ^^^^^^^ /// ``` Image, + WikilinkText, + WikilinkStart, + WikilinkEnd, /// Label. /// /// ## Info @@ -3427,6 +3430,9 @@ pub const VOID_EVENTS: [Name; 76] = [ Name::HeadingSetextUnderlineSequence, Name::HtmlFlowData, Name::HtmlTextData, + // Name::WikilinkStart, + // Name::WikilinkStart, + // Name::WikilinkEnd, Name::LabelImageMarker, Name::LabelMarker, Name::LineEnding, diff --git a/src/state.rs b/src/state.rs index 5e765c7..ef5cb8d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -153,6 +153,21 @@ pub enum Name { FrontmatterCloseSequence, FrontmatterCloseAfter, + WikilinkStart, + WikilinkAlias, + WikilinkHeading, + WikilinkBlock, + WikilinkEnd, + WikilinkEndOk, + WikilinkEndNok, + + WikilinkLabelStart, + WikilinkLabelAtBreak, + WikilinkLabelEolAfter, + WikilinkLabelEscape, + WikilinkLabelInside, + WikilinkLabelNok, + GfmAutolinkLiteralProtocolStart, GfmAutolinkLiteralProtocolAfter, GfmAutolinkLiteralProtocolPrefixInside, @@ -451,6 +466,7 @@ pub enum Name { TextBeforeHtml, TextBeforeMdxJsx, TextBeforeHardBreakEscape, + TextBeforeWikilinkStart, TextBeforeLabelStartLink, TextBeforeData, @@ -474,7 +490,6 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { let func = match name { Name::AttentionStart => construct::attention::start, Name::AttentionInside => construct::attention::inside, - Name::AutolinkStart => construct::autolink::start, Name::AutolinkOpen => construct::autolink::open, Name::AutolinkSchemeOrEmailAtext => construct::autolink::scheme_or_email_atext, @@ -484,26 +499,20 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::AutolinkEmailAtext => construct::autolink::email_atext, Name::AutolinkEmailValue => construct::autolink::email_value, Name::AutolinkEmailLabel => construct::autolink::email_label, - Name::BlankLineStart => construct::blank_line::start, Name::BlankLineAfter => construct::blank_line::after, - Name::BlockQuoteStart => construct::block_quote::start, Name::BlockQuoteContStart => construct::block_quote::cont_start, Name::BlockQuoteContBefore => construct::block_quote::cont_before, Name::BlockQuoteContAfter => construct::block_quote::cont_after, - Name::BomStart => construct::partial_bom::start, Name::BomInside => construct::partial_bom::inside, - Name::CharacterEscapeStart => construct::character_escape::start, Name::CharacterEscapeInside => construct::character_escape::inside, - Name::CharacterReferenceStart => construct::character_reference::start, Name::CharacterReferenceOpen => construct::character_reference::open, Name::CharacterReferenceNumeric => construct::character_reference::numeric, Name::CharacterReferenceValue => construct::character_reference::value, - Name::CodeIndentedStart => construct::code_indented::start, Name::CodeIndentedAtBreak => construct::code_indented::at_break, Name::CodeIndentedAfter => construct::code_indented::after, @@ -511,16 +520,13 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::CodeIndentedInside => construct::code_indented::inside, Name::CodeIndentedFurtherBegin => construct::code_indented::further_begin, Name::CodeIndentedFurtherAfter => construct::code_indented::further_after, - Name::ContentChunkStart => construct::content::chunk_start, Name::ContentChunkInside => construct::content::chunk_inside, Name::ContentDefinitionBefore => construct::content::definition_before, Name::ContentDefinitionAfter => construct::content::definition_after, - Name::DataStart => construct::partial_data::start, Name::DataInside => construct::partial_data::inside, Name::DataAtBreak => construct::partial_data::at_break, - Name::DefinitionStart => construct::definition::start, Name::DefinitionBefore => construct::definition::before, Name::DefinitionLabelAfter => construct::definition::label_after, @@ -537,14 +543,12 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::DefinitionTitleAfterOptionalWhitespace => { construct::definition::title_after_optional_whitespace } - Name::DestinationStart => construct::partial_destination::start, Name::DestinationEnclosedBefore => construct::partial_destination::enclosed_before, Name::DestinationEnclosed => construct::partial_destination::enclosed, Name::DestinationEnclosedEscape => construct::partial_destination::enclosed_escape, Name::DestinationRaw => construct::partial_destination::raw, Name::DestinationRawEscape => construct::partial_destination::raw_escape, - Name::DocumentStart => construct::document::start, Name::DocumentBeforeFrontmatter => construct::document::before_frontmatter, Name::DocumentContainerExistingBefore => construct::document::container_existing_before, @@ -563,7 +567,6 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::DocumentContainersAfter => construct::document::containers_after, Name::DocumentFlowEnd => construct::document::flow_end, Name::DocumentFlowInside => construct::document::flow_inside, - Name::FlowStart => construct::flow::start, Name::FlowBeforeGfmTable => construct::flow::before_gfm_table, Name::FlowBeforeCodeIndented => construct::flow::before_code_indented, @@ -578,7 +581,6 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::FlowBlankLineBefore => construct::flow::blank_line_before, Name::FlowBlankLineAfter => construct::flow::blank_line_after, Name::FlowBeforeContent => construct::flow::before_content, - Name::FrontmatterStart => construct::frontmatter::start, Name::FrontmatterOpenSequence => construct::frontmatter::open_sequence, Name::FrontmatterOpenAfter => construct::frontmatter::open_after, @@ -589,7 +591,8 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::FrontmatterCloseStart => construct::frontmatter::close_start, Name::FrontmatterCloseSequence => construct::frontmatter::close_sequence, Name::FrontmatterCloseAfter => construct::frontmatter::close_after, - + Name::WikilinkStart => construct::wikilink::start, + Name::WikilinkEnd => construct::wikilink::end, Name::GfmAutolinkLiteralProtocolStart => construct::gfm_autolink_literal::protocol_start, Name::GfmAutolinkLiteralProtocolAfter => construct::gfm_autolink_literal::protocol_after, Name::GfmAutolinkLiteralProtocolPrefixInside => { @@ -624,7 +627,6 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::GfmAutolinkLiteralTrailBracketAfter => { construct::gfm_autolink_literal::trail_bracket_after } - Name::GfmFootnoteDefinitionStart => construct::gfm_footnote_definition::start, Name::GfmFootnoteDefinitionLabelBefore => construct::gfm_footnote_definition::label_before, Name::GfmFootnoteDefinitionLabelAtMarker => { @@ -639,10 +641,8 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::GfmFootnoteDefinitionContStart => construct::gfm_footnote_definition::cont_start, Name::GfmFootnoteDefinitionContBlank => construct::gfm_footnote_definition::cont_blank, Name::GfmFootnoteDefinitionContFilled => construct::gfm_footnote_definition::cont_filled, - Name::GfmLabelStartFootnoteStart => construct::gfm_label_start_footnote::start, Name::GfmLabelStartFootnoteOpen => construct::gfm_label_start_footnote::open, - Name::GfmTableStart => construct::gfm_table::start, Name::GfmTableHeadRowBefore => construct::gfm_table::head_row_before, Name::GfmTableHeadRowStart => construct::gfm_table::head_row_start, @@ -666,7 +666,6 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::GfmTableBodyRowBreak => construct::gfm_table::body_row_break, Name::GfmTableBodyRowData => construct::gfm_table::body_row_data, Name::GfmTableBodyRowEscape => construct::gfm_table::body_row_escape, - Name::GfmTaskListItemCheckStart => construct::gfm_task_list_item_check::start, Name::GfmTaskListItemCheckInside => construct::gfm_task_list_item_check::inside, Name::GfmTaskListItemCheckClose => construct::gfm_task_list_item_check::close, @@ -674,22 +673,18 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::GfmTaskListItemCheckAfterSpaceOrTab => { construct::gfm_task_list_item_check::after_space_or_tab } - Name::HardBreakEscapeStart => construct::hard_break_escape::start, Name::HardBreakEscapeAfter => construct::hard_break_escape::after, - Name::HeadingAtxStart => construct::heading_atx::start, Name::HeadingAtxBefore => construct::heading_atx::before, Name::HeadingAtxSequenceOpen => construct::heading_atx::sequence_open, Name::HeadingAtxAtBreak => construct::heading_atx::at_break, Name::HeadingAtxSequenceFurther => construct::heading_atx::sequence_further, Name::HeadingAtxData => construct::heading_atx::data, - Name::HeadingSetextStart => construct::heading_setext::start, Name::HeadingSetextBefore => construct::heading_setext::before, Name::HeadingSetextInside => construct::heading_setext::inside, Name::HeadingSetextAfter => construct::heading_setext::after, - Name::HtmlFlowStart => construct::html_flow::start, Name::HtmlFlowBefore => construct::html_flow::before, Name::HtmlFlowOpen => construct::html_flow::open, @@ -737,7 +732,6 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::HtmlFlowContinuationClose => construct::html_flow::continuation_close, Name::HtmlFlowContinuationCdataInside => construct::html_flow::continuation_cdata_inside, Name::HtmlFlowContinuationStartNonLazy => construct::html_flow::continuation_start_non_lazy, - Name::HtmlTextStart => construct::html_text::start, Name::HtmlTextOpen => construct::html_text::open, Name::HtmlTextDeclarationOpen => construct::html_text::declaration_open, @@ -777,14 +771,12 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::HtmlTextLineEndingBefore => construct::html_text::line_ending_before, Name::HtmlTextLineEndingAfter => construct::html_text::line_ending_after, Name::HtmlTextLineEndingAfterPrefix => construct::html_text::line_ending_after_prefix, - Name::LabelStart => construct::partial_label::start, Name::LabelAtBreak => construct::partial_label::at_break, Name::LabelEolAfter => construct::partial_label::eol_after, Name::LabelEscape => construct::partial_label::escape, Name::LabelInside => construct::partial_label::inside, Name::LabelNok => construct::partial_label::nok, - Name::LabelEndStart => construct::label_end::start, Name::LabelEndAfter => construct::label_end::after, Name::LabelEndResourceStart => construct::label_end::resource_start, @@ -805,12 +797,10 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::LabelEndReferenceNotFull => construct::label_end::reference_not_full, Name::LabelEndReferenceCollapsed => construct::label_end::reference_collapsed, Name::LabelEndReferenceCollapsedOpen => construct::label_end::reference_collapsed_open, - Name::LabelStartImageStart => construct::label_start_image::start, Name::LabelStartImageOpen => construct::label_start_image::open, Name::LabelStartImageAfter => construct::label_start_image::after, Name::LabelStartLinkStart => construct::label_start_link::start, - Name::ListItemStart => construct::list_item::start, Name::ListItemBefore => construct::list_item::before, Name::ListItemBeforeOrdered => construct::list_item::before_ordered, @@ -826,7 +816,6 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::ListItemContStart => construct::list_item::cont_start, Name::ListItemContBlank => construct::list_item::cont_blank, Name::ListItemContFilled => construct::list_item::cont_filled, - Name::MdxEsmStart => construct::mdx_esm::start, Name::MdxEsmWord => construct::mdx_esm::word, Name::MdxEsmInside => construct::mdx_esm::inside, @@ -834,31 +823,25 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::MdxEsmBlankLineBefore => construct::mdx_esm::blank_line_before, Name::MdxEsmContinuationStart => construct::mdx_esm::continuation_start, Name::MdxEsmAtEnd => construct::mdx_esm::at_end, - Name::MdxExpressionStart => construct::partial_mdx_expression::start, Name::MdxExpressionPrefix => construct::partial_mdx_expression::prefix, Name::MdxExpressionBefore => construct::partial_mdx_expression::before, Name::MdxExpressionInside => construct::partial_mdx_expression::inside, Name::MdxExpressionEolAfter => construct::partial_mdx_expression::eol_after, - Name::MdxExpressionFlowStart => construct::mdx_expression_flow::start, Name::MdxExpressionFlowBefore => construct::mdx_expression_flow::before, Name::MdxExpressionFlowAfter => construct::mdx_expression_flow::after, Name::MdxExpressionFlowEnd => construct::mdx_expression_flow::end, - Name::MdxExpressionTextStart => construct::mdx_expression_text::start, Name::MdxExpressionTextAfter => construct::mdx_expression_text::after, - Name::MdxJsxFlowStart => construct::mdx_jsx_flow::start, Name::MdxJsxFlowBefore => construct::mdx_jsx_flow::before, Name::MdxJsxFlowAfter => construct::mdx_jsx_flow::after, Name::MdxJsxFlowEnd => construct::mdx_jsx_flow::end, Name::MdxJsxFlowNok => construct::mdx_jsx_flow::nok, - Name::MdxJsxTextStart => construct::mdx_jsx_text::start, Name::MdxJsxTextAfter => construct::mdx_jsx_text::after, Name::MdxJsxTextNok => construct::mdx_jsx_text::nok, - Name::MdxJsxStart => construct::partial_mdx_jsx::start, Name::MdxJsxStartAfter => construct::partial_mdx_jsx::start_after, Name::MdxJsxNameBefore => construct::partial_mdx_jsx::name_before, @@ -899,14 +882,11 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::MdxJsxEsWhitespaceStart => construct::partial_mdx_jsx::es_whitespace_start, Name::MdxJsxEsWhitespaceInside => construct::partial_mdx_jsx::es_whitespace_inside, Name::MdxJsxEsWhitespaceEolAfter => construct::partial_mdx_jsx::es_whitespace_eol_after, - Name::NonLazyContinuationStart => construct::partial_non_lazy_continuation::start, Name::NonLazyContinuationAfter => construct::partial_non_lazy_continuation::after, - Name::ParagraphStart => construct::paragraph::start, Name::ParagraphLineStart => construct::paragraph::line_start, Name::ParagraphInside => construct::paragraph::inside, - Name::RawFlowStart => construct::raw_flow::start, Name::RawFlowBeforeSequenceOpen => construct::raw_flow::before_sequence_open, Name::RawFlowSequenceOpen => construct::raw_flow::sequence_open, @@ -924,40 +904,34 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::RawFlowBeforeContentChunk => construct::raw_flow::before_content_chunk, Name::RawFlowContentChunk => construct::raw_flow::content_chunk, Name::RawFlowAfter => construct::raw_flow::after, - Name::RawTextStart => construct::raw_text::start, Name::RawTextSequenceOpen => construct::raw_text::sequence_open, Name::RawTextBetween => construct::raw_text::between, Name::RawTextData => construct::raw_text::data, Name::RawTextSequenceClose => construct::raw_text::sequence_close, - Name::SpaceOrTabStart => construct::partial_space_or_tab::start, Name::SpaceOrTabInside => construct::partial_space_or_tab::inside, Name::SpaceOrTabAfter => construct::partial_space_or_tab::after, - Name::SpaceOrTabEolStart => construct::partial_space_or_tab_eol::start, Name::SpaceOrTabEolAfterFirst => construct::partial_space_or_tab_eol::after_first, Name::SpaceOrTabEolAfterEol => construct::partial_space_or_tab_eol::after_eol, Name::SpaceOrTabEolAtEol => construct::partial_space_or_tab_eol::at_eol, Name::SpaceOrTabEolAfterMore => construct::partial_space_or_tab_eol::after_more, - Name::StringStart => construct::string::start, Name::StringBefore => construct::string::before, Name::StringBeforeData => construct::string::before_data, - Name::TextStart => construct::text::start, Name::TextBefore => construct::text::before, Name::TextBeforeHtml => construct::text::before_html, Name::TextBeforeMdxJsx => construct::text::before_mdx_jsx, Name::TextBeforeHardBreakEscape => construct::text::before_hard_break_escape, + Name::TextBeforeWikilinkStart => construct::text::before_label_wikilink_start, Name::TextBeforeLabelStartLink => construct::text::before_label_start_link, Name::TextBeforeData => construct::text::before_data, - Name::ThematicBreakStart => construct::thematic_break::start, Name::ThematicBreakBefore => construct::thematic_break::before, Name::ThematicBreakSequence => construct::thematic_break::sequence, Name::ThematicBreakAtBreak => construct::thematic_break::at_break, - Name::TitleStart => construct::partial_title::start, Name::TitleBegin => construct::partial_title::begin, Name::TitleAfterEol => construct::partial_title::after_eol, @@ -965,6 +939,17 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::TitleEscape => construct::partial_title::escape, Name::TitleInside => construct::partial_title::inside, Name::TitleNok => construct::partial_title::nok, + Name::WikilinkAlias => todo!(), + Name::WikilinkHeading => todo!(), + Name::WikilinkBlock => todo!(), + Name::WikilinkEndOk => todo!(), + Name::WikilinkEndNok => todo!(), + Name::WikilinkLabelStart => construct::wikilink_label::start, + Name::WikilinkLabelAtBreak => construct::wikilink_label::at_break, + Name::WikilinkLabelEolAfter => construct::wikilink_label::eol_after, + Name::WikilinkLabelEscape => construct::wikilink_label::escape, + Name::WikilinkLabelInside => construct::wikilink_label::inside, + Name::WikilinkLabelNok => construct::wikilink_label::nok, }; func(tokenizer) diff --git a/src/to_html.rs b/src/to_html.rs index b50ec91..b563c71 100644 --- a/src/to_html.rs +++ b/src/to_html.rs @@ -350,6 +350,7 @@ fn enter(context: &mut CompileContext) { Name::HtmlText => on_enter_html_text(context), Name::Image => on_enter_image(context), Name::Link => on_enter_link(context), + Name::WikilinkStart => on_enter_wikilink(context), Name::ListItemMarker => on_enter_list_item_marker(context), Name::ListOrdered | Name::ListUnordered => on_enter_list(context), Name::Paragraph => on_enter_paragraph(context), @@ -425,6 +426,7 @@ fn exit(context: &mut CompileContext) { Name::HtmlFlow | Name::HtmlText => on_exit_html(context), Name::HtmlFlowData | Name::HtmlTextData => on_exit_html_data(context), Name::Image | Name::Link => on_exit_media(context), + Name::WikilinkEnd => on_exit_wikilink(context), Name::Label => on_exit_label(context), Name::LabelText => on_exit_label_text(context), Name::LineEnding => on_exit_line_ending(context), @@ -648,6 +650,19 @@ fn on_enter_link(context: &mut CompileContext) { }); } +/// Handle [`Enter`][Kind::Enter]:[`Link`][Name::WikilinkStart]. +fn on_enter_wikilink(context: &mut CompileContext) { + if !context.image_alt_inside { + context.push(""); + } +} + +fn on_exit_wikilink(context: &mut CompileContext) { + if !context.image_alt_inside { + context.push(""); + } +} + /// Handle [`Enter`][Kind::Enter]:{[`ListOrdered`][Name::ListOrdered],[`ListUnordered`][Name::ListUnordered]}. fn on_enter_list(context: &mut CompileContext) { let loose = list_loose(context.events, context.index, true); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index b30098c..584cc76 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -213,6 +213,9 @@ pub struct TokenizeState<'a> { pub space_or_tab_size: usize, pub space_or_tab_token: Name, + pub wikilinks: Vec