use crate::mdx_parser::event::Name;
use crate::mdx_parser::state::{Name as StateName, State};
use crate::mdx_parser::tokenizer::{ByteSet, LINE_STOP, RawTextRuns, Tokenizer, with_bytes};
const CODE_STOP: ByteSet = with_bytes(LINE_STOP, b"`");
const MATH_STOP: ByteSet = with_bytes(LINE_STOP, b"$");
use alloc::boxed::Box;
pub fn start(tokenizer: &mut Tokenizer) -> State {
if ((tokenizer.parse_state.options.constructs.code_text && tokenizer.current == Some(b'`'))
|| (tokenizer.parse_state.options.constructs.math_text && tokenizer.current == Some(b'$')))
&& (tokenizer.previous != tokenizer.current
|| (!tokenizer.events.is_empty()
&& tokenizer.events[tokenizer.events.len() - 1].name == Name::CharacterEscape))
{
let marker = tokenizer.current.unwrap();
if marker == b'`' {
tokenizer.tokenize_state.token_1 = Name::CodeText;
tokenizer.tokenize_state.token_2 = Name::CodeTextSequence;
tokenizer.tokenize_state.token_3 = Name::CodeTextData;
} else {
tokenizer.tokenize_state.token_1 = Name::MathText;
tokenizer.tokenize_state.token_2 = Name::MathTextSequence;
tokenizer.tokenize_state.token_3 = Name::MathTextData;
}
tokenizer.tokenize_state.marker = marker;
tokenizer.enter(tokenizer.tokenize_state.token_1.clone());
tokenizer.enter(tokenizer.tokenize_state.token_2.clone());
State::Retry(StateName::RawTextSequenceOpen)
} else {
State::Nok
}
}
pub fn sequence_open(tokenizer: &mut Tokenizer) -> State {
if tokenizer.current == Some(tokenizer.tokenize_state.marker) {
tokenizer.tokenize_state.size += 1;
tokenizer.consume();
State::Next(StateName::RawTextSequenceOpen)
}
else if (tokenizer.tokenize_state.marker == b'$'
&& tokenizer.tokenize_state.size == 1
&& !tokenizer.parse_state.options.math_text_single_dollar)
|| !has_closing_run(tokenizer)
{
tokenizer.tokenize_state.marker = 0;
tokenizer.tokenize_state.size = 0;
tokenizer.tokenize_state.token_1 = Name::Data;
tokenizer.tokenize_state.token_2 = Name::Data;
tokenizer.tokenize_state.token_3 = Name::Data;
State::Nok
} else {
let index = tokenizer.point.index;
if let Some(runs) = runs_mut(tokenizer) {
if runs.complete_from.is_none() {
runs.recording = Some(index);
}
}
tokenizer.exit(tokenizer.tokenize_state.token_2.clone());
State::Retry(StateName::RawTextBetween)
}
}
pub fn between(tokenizer: &mut Tokenizer) -> State {
match tokenizer.current {
None => {
let runs = runs_mut(tokenizer).get_or_insert_with(Box::default);
if let Some(from) = runs.recording.take() {
runs.complete_from = Some(from);
}
tokenizer.tokenize_state.marker = 0;
tokenizer.tokenize_state.size = 0;
tokenizer.tokenize_state.token_1 = Name::Data;
tokenizer.tokenize_state.token_2 = Name::Data;
tokenizer.tokenize_state.token_3 = Name::Data;
State::Nok
}
Some(b'\n') => {
tokenizer.enter(Name::LineEnding);
tokenizer.consume();
tokenizer.exit(Name::LineEnding);
State::Next(StateName::RawTextBetween)
}
_ => {
if tokenizer.current == Some(tokenizer.tokenize_state.marker) {
tokenizer.enter(tokenizer.tokenize_state.token_2.clone());
State::Retry(StateName::RawTextSequenceClose)
} else {
tokenizer.enter(tokenizer.tokenize_state.token_3.clone());
State::Retry(StateName::RawTextData)
}
}
}
}
pub fn data(tokenizer: &mut Tokenizer) -> State {
if matches!(tokenizer.current, None | Some(b'\n')) || tokenizer.current == Some(tokenizer.tokenize_state.marker) {
tokenizer.exit(tokenizer.tokenize_state.token_3.clone());
State::Retry(StateName::RawTextBetween)
} else {
tokenizer.consume_run(if tokenizer.tokenize_state.marker == b'$' {
&MATH_STOP
} else {
&CODE_STOP
});
State::Next(StateName::RawTextData)
}
}
pub fn sequence_close(tokenizer: &mut Tokenizer) -> State {
if tokenizer.current == Some(tokenizer.tokenize_state.marker) {
tokenizer.tokenize_state.size_b += 1;
tokenizer.consume();
State::Next(StateName::RawTextSequenceClose)
} else {
tokenizer.exit(tokenizer.tokenize_state.token_2.clone());
if tokenizer.tokenize_state.size == tokenizer.tokenize_state.size_b {
if let Some(runs) = runs_mut(tokenizer) {
runs.recording = None;
}
tokenizer.exit(tokenizer.tokenize_state.token_1.clone());
tokenizer.tokenize_state.marker = 0;
tokenizer.tokenize_state.size = 0;
tokenizer.tokenize_state.size_b = 0;
tokenizer.tokenize_state.token_1 = Name::Data;
tokenizer.tokenize_state.token_2 = Name::Data;
tokenizer.tokenize_state.token_3 = Name::Data;
State::Ok
} else {
let size = tokenizer.tokenize_state.size_b;
let start = tokenizer.point.index - size;
if let Some(runs) = runs_mut(tokenizer) {
if runs.recording.is_some() {
match runs.last_start.binary_search_by_key(&size, |run| run.0) {
Ok(position) => runs.last_start[position].1 = start,
Err(position) => runs.last_start.insert(position, (size, start)),
}
}
}
let len = tokenizer.events.len();
tokenizer.events[len - 2].name = tokenizer.tokenize_state.token_3.clone();
tokenizer.events[len - 1].name = tokenizer.tokenize_state.token_3.clone();
tokenizer.tokenize_state.size_b = 0;
State::Retry(StateName::RawTextBetween)
}
}
}
fn runs_mut<'a>(tokenizer: &'a mut Tokenizer) -> &'a mut Option<Box<RawTextRuns>> {
let index = usize::from(tokenizer.tokenize_state.marker == b'$');
&mut tokenizer.tokenize_state.raw_text_runs[index]
}
fn has_closing_run(tokenizer: &mut Tokenizer) -> bool {
let index = tokenizer.point.index;
let size = tokenizer.tokenize_state.size;
match runs_mut(tokenizer) {
Some(runs) if runs.complete_from.map_or(false, |from| from <= index) => runs
.last_start
.binary_search_by_key(&size, |run| run.0)
.map_or(false, |position| runs.last_start[position].1 >= index),
_ => true,
}
}