//! 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 { if !tokenizer.parse_state.options.constructs.wikilink { return State::Retry(MoveName::TextBeforeLabelStartLink); } let start_conditions = tokenizer.current == Some(b'['); if start_conditions && tokenizer.tokenize_state.size == 0 { tokenizer.tokenize_state.size += 1; tokenizer.attempt( State::Next(MoveName::WikilinkLabelStart), State::Next(MoveName::TextBeforeLabelStartLink), ); tokenizer.consume(); State::Next(MoveName::WikilinkStart) } 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::Ok } else { tokenizer.tokenize_state.size = 0; // Resume the "regular" sequence of state transitions State::Nok // State::Retry(MoveName::TextBeforeLabelStartLink) } } 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 }