Markdown parser fork with extended syntax for personal use.
at hack 157 lines 5.0 kB view raw
1//! GFM: Task list item check occurs in the [text][] content type. 2//! 3//! ## Grammar 4//! 5//! Checks form with the following BNF 6//! (<small>see [construct][crate::construct] for character groups</small>): 7//! 8//! ```bnf 9//! gfm_task_list_item_check ::= '[' (0x09 | ' ' | 'X' | 'x') ']' 10//! ``` 11//! 12//! The check is only allowed at the start of the first paragraph, optionally 13//! following zero or more definitions or a blank line, in a list item. 14//! The check must be followed by whitespace, which is in turn followed by 15//! non-whitespace. 16//! 17//! ## HTML 18//! 19//! Checks relate to the `<input>` element, in the checkbox state 20//! (`type=checkbox`), in HTML. 21//! See [*§ 4.10.5.1.15 Checkbox state (`type=checkbox`)*][html-input-checkbox] 22//! in the HTML spec for more info. 23//! 24//! ## Recommendation 25//! 26//! It is recommended to use lowercase `x` (instead of uppercase `X`), because 27//! in markdown, it is more common to use lowercase in places where casing does 28//! not matter. 29//! It is also recommended to use a space (instead of a tab), as there is no 30//! benefit of using tabs in this case. 31//! 32//! ## Tokens 33//! 34//! * [`GfmTaskListItemCheck`][Name::GfmTaskListItemCheck] 35//! * [`GfmTaskListItemMarker`][Name::GfmTaskListItemMarker] 36//! * [`GfmTaskListItemValueChecked`][Name::GfmTaskListItemValueChecked] 37//! * [`GfmTaskListItemValueUnchecked`][Name::GfmTaskListItemValueUnchecked] 38//! 39//! ## References 40//! 41//! * [`micromark-extension-gfm-task-list-item`](https://github.com/micromark/micromark-extension-gfm-task-list-item) 42//! * [*§ 5.3 Task list items (extension)* in `GFM`](https://github.github.com/gfm/#task-list-items-extension-) 43//! 44//! [text]: crate::construct::text 45//! [html-input-checkbox]: https://html.spec.whatwg.org/multipage/input.html#checkbox-state-(type=checkbox) 46 47use crate::construct::partial_space_or_tab::space_or_tab; 48use crate::event::Name; 49use crate::state::{Name as StateName, State}; 50use crate::tokenizer::Tokenizer; 51 52/// At start of task list item check. 53/// 54/// ```markdown 55/// > | * [x] y. 56/// ^ 57/// ``` 58pub fn start(tokenizer: &mut Tokenizer) -> State { 59 if tokenizer.parse_state.options.constructs.gfm_task_list_item 60 && tokenizer 61 .tokenize_state 62 .document_at_first_paragraph_of_list_item 63 && tokenizer.current == Some(b'[') 64 && tokenizer.previous.is_none() 65 { 66 tokenizer.enter(Name::GfmTaskListItemCheck); 67 tokenizer.enter(Name::GfmTaskListItemMarker); 68 tokenizer.consume(); 69 tokenizer.exit(Name::GfmTaskListItemMarker); 70 State::Next(StateName::GfmTaskListItemCheckInside) 71 } else { 72 State::Nok 73 } 74} 75 76/// In task list item check. 77/// 78/// ```markdown 79/// > | * [x] y. 80/// ^ 81/// ``` 82pub fn inside(tokenizer: &mut Tokenizer) -> State { 83 match tokenizer.current { 84 Some(b'\t' | b'\n' | b' ') => { 85 tokenizer.enter(Name::GfmTaskListItemValueUnchecked); 86 tokenizer.consume(); 87 tokenizer.exit(Name::GfmTaskListItemValueUnchecked); 88 State::Next(StateName::GfmTaskListItemCheckClose) 89 } 90 Some(b'X' | b'x') => { 91 tokenizer.enter(Name::GfmTaskListItemValueChecked); 92 tokenizer.consume(); 93 tokenizer.exit(Name::GfmTaskListItemValueChecked); 94 State::Next(StateName::GfmTaskListItemCheckClose) 95 } 96 _ => State::Nok, 97 } 98} 99 100/// At close of task list item check. 101/// 102/// ```markdown 103/// > | * [x] y. 104/// ^ 105/// ``` 106pub fn close(tokenizer: &mut Tokenizer) -> State { 107 match tokenizer.current { 108 Some(b']') => { 109 tokenizer.enter(Name::GfmTaskListItemMarker); 110 tokenizer.consume(); 111 tokenizer.exit(Name::GfmTaskListItemMarker); 112 tokenizer.exit(Name::GfmTaskListItemCheck); 113 State::Next(StateName::GfmTaskListItemCheckAfter) 114 } 115 _ => State::Nok, 116 } 117} 118 119/// After task list item check. 120/// 121/// ```markdown 122/// > | * [x] y. 123/// ^ 124/// ``` 125pub fn after(tokenizer: &mut Tokenizer) -> State { 126 match tokenizer.current { 127 // EOL in paragraph means there must be something else after it. 128 Some(b'\n') => State::Ok, 129 // Space or tab? 130 // Check what comes after. 131 Some(b'\t' | b' ') => { 132 tokenizer.check(State::Ok, State::Nok); 133 tokenizer.attempt( 134 State::Next(StateName::GfmTaskListItemCheckAfterSpaceOrTab), 135 State::Nok, 136 ); 137 State::Retry(space_or_tab(tokenizer)) 138 } 139 // EOF, or non-whitespace, both wrong. 140 _ => State::Nok, 141 } 142} 143 144/// After whitespace, after task list item check. 145/// 146/// ```markdown 147/// > | * [x] y. 148/// ^ 149/// ``` 150pub fn after_space_or_tab(tokenizer: &mut Tokenizer) -> State { 151 // End of paragraph, after whitespace, after check, is not okay. 152 if tokenizer.current.is_none() { 153 State::Nok 154 } else { 155 State::Ok 156 } 157}