//! To do. use crate::constant::{LIST_ITEM_VALUE_SIZE_MAX, TAB_SIZE}; use crate::construct::partial_space_or_tab::space_or_tab_min_max; use crate::token::Token; use crate::tokenizer::{Code, State, StateFnResult, Tokenizer}; /// Type of title. #[derive(Debug, PartialEq)] enum Kind { /// In a dot (`.`) list. /// /// ## Example /// /// ```markdown /// 1. a /// ``` Dot, /// In a paren (`)`) list. /// /// ## Example /// /// ```markdown /// 1) a /// ``` Paren, /// In an asterisk (`*`) list. /// /// ## Example /// /// ```markdown /// * a /// ``` Asterisk, /// In a plus (`+`) list. /// /// ## Example /// /// ```markdown /// + a /// ``` Plus, /// In a dash (`-`) list. /// /// ## Example /// /// ```markdown /// - a /// ``` Dash, } impl Kind { /// Turn the kind into a [char]. fn as_char(&self) -> char { match self { Kind::Dot => '.', Kind::Paren => ')', Kind::Asterisk => '*', Kind::Plus => '+', Kind::Dash => '-', } } /// Turn a [char] into a kind. /// /// ## Panics /// /// Panics if `char` is not `.`, `)`, `*`, `+`, or `-`. fn from_char(char: char) -> Kind { match char { '.' => Kind::Dot, ')' => Kind::Paren, '*' => Kind::Asterisk, '+' => Kind::Plus, '-' => Kind::Dash, _ => unreachable!("invalid char"), } } /// Turn [Code] into a kind. /// /// ## Panics /// /// Panics if `code` is not `Code::Char('.' | ')' | '*' | '+' | '-')`. fn from_code(code: Code) -> Kind { match code { Code::Char(char) => Kind::from_char(char), _ => unreachable!("invalid code"), } } } /// To do. pub fn start(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { // To do: allow arbitrary when code (indented) is turned off. tokenizer.go(space_or_tab_min_max(0, TAB_SIZE - 1), before)(tokenizer, code) } /// To do. fn before(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { match code { // Unordered. Code::Char('*' | '+' | '-') => { // To do: check if this is a thematic break? tokenizer.enter(Token::List); tokenizer.enter(Token::ListItemPrefix); marker(tokenizer, code) } // Ordered. Code::Char(char) if char.is_ascii_digit() => { tokenizer.enter(Token::List); tokenizer.enter(Token::ListItemPrefix); tokenizer.enter(Token::ListItemValue); // To do: `interrupt || !1`? inside(tokenizer, code, 0) } _ => (State::Nok, None), } } /// To do. fn inside(tokenizer: &mut Tokenizer, code: Code, mut size: usize) -> StateFnResult { match code { Code::Char(char) if char.is_ascii_digit() && size < LIST_ITEM_VALUE_SIZE_MAX => { tokenizer.consume(code); size += 1; (State::Fn(Box::new(move |t, c| inside(t, c, size))), None) } // To do: `(!self.interrupt || size < 2)` Code::Char('.' | ')') => { tokenizer.exit(Token::ListItemValue); marker(tokenizer, code) } _ => (State::Nok, None), } } /// To do. fn marker(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { let kind = Kind::from_code(code); println!("list item kind: {:?}", kind); tokenizer.enter(Token::ListItemMarker); tokenizer.consume(code); tokenizer.exit(Token::ListItemMarker); // To do: check blank line, if true `State::Nok` else `on_blank`. (State::Fn(Box::new(marker_after)), None) } /// To do. fn marker_after(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { tokenizer.attempt(list_item_prefix_whitespace, |ok| { let func = if ok { prefix_end } else { prefix_other }; Box::new(func) })(tokenizer, code) } // To do: `on_blank`. /// To do. fn prefix_other(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { match code { Code::VirtualSpace | Code::Char('\t' | ' ') => { tokenizer.enter(Token::SpaceOrTab); tokenizer.consume(code); tokenizer.exit(Token::SpaceOrTab); (State::Fn(Box::new(prefix_end)), None) } _ => (State::Nok, None), } } /// To do. fn prefix_end(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { // To do: calculate size. tokenizer.exit(Token::ListItemPrefix); (State::Ok, Some(vec![code])) } /// To do. fn list_item_prefix_whitespace(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { // To do: check how big this should be? tokenizer.go( space_or_tab_min_max(1, TAB_SIZE - 1), list_item_prefix_whitespace_after, )(tokenizer, code) } fn list_item_prefix_whitespace_after(_tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { // To do: check some stuff? (State::Ok, Some(vec![code])) } /// End of a block quote. pub fn end() -> Vec { vec![Token::List] }