diff --git a/docs/development/roadmap.md b/docs/development/roadmap.md index 32a9c64..54362fe 100644 --- a/docs/development/roadmap.md +++ b/docs/development/roadmap.md @@ -52,7 +52,7 @@ - [x] Inline code - [x] Lists - [ ] Nested lists - - [ ] Checkboxes + - [x] Checkboxes - [ ] Move this roadmap to en - [ ] Full-text search - [ ] Begin centralizing state diff --git a/src/syntax/content/parser/context/block.rs b/src/syntax/content/parser/context/block.rs index 608b58b..11fa54f 100644 --- a/src/syntax/content/parser/context/block.rs +++ b/src/syntax/content/parser/context/block.rs @@ -9,8 +9,7 @@ use crate::{ lexeme::Lexeme, state::State, token::{ - Token, header::Header, preformat::PreFormat, - paragraph::Paragraph, literal::Literal, list::List, item::Item, + Token, checkbox::CheckBox, header::Header, item::Item, list::List, literal::Literal, paragraph::Paragraph, preformat::PreFormat }, }, }, @@ -98,7 +97,13 @@ pub fn parse( } }, Block::Item(ordered) => { - if Item::probe_end(lexeme) { + if CheckBox::probe(lexeme) { + log!("Probed CheckBox: {lexeme}"); + tokens.push(Token::CheckBox(CheckBox::lex(lexeme))); + iterator.next(); + iterator.next(); + return true + } else if Item::probe_end(lexeme) { tokens.push(Token::Item(Item::new(false))); log!("Block Context: Item -> List on {lexeme}"); state.context.block = Block::List(ordered); diff --git a/src/syntax/content/parser/segment.rs b/src/syntax/content/parser/segment.rs index 26fdec1..28cfdb1 100644 --- a/src/syntax/content/parser/segment.rs +++ b/src/syntax/content/parser/segment.rs @@ -17,7 +17,7 @@ pub mod delimiter { Delimiters { atomic: vec!['`', '|', '\\'], double: vec!['_', '~'], - flanking: vec!['_', '*', '~', '(', ')', '\'', '"'], + flanking: vec!['_', '*', '~', '(', ')', '[', ']', '\'', '"'], punctuation: vec![',', '.', ';', ':', '?', '!'], whitespace: vec!['\n', ' '], } diff --git a/src/syntax/content/parser/token.rs b/src/syntax/content/parser/token.rs index fb88989..9cead7c 100644 --- a/src/syntax/content/parser/token.rs +++ b/src/syntax/content/parser/token.rs @@ -2,6 +2,7 @@ use crate::syntax::content::Parseable as _; pub mod anchor; pub mod bold; +pub mod checkbox; pub mod code; pub mod header; pub mod item; @@ -19,6 +20,7 @@ pub mod underline; pub enum Token { Anchor(anchor::Anchor), Bold(bold::Bold), + CheckBox(checkbox::CheckBox), Code(code::Code), Strike(strike::Strike), Header(header::Header), @@ -38,6 +40,7 @@ impl Token { match *self { Token::Anchor(ref d) => d.render(), Token::Bold(ref d) => d.render(), + Token::CheckBox(ref d) => d.render(), Token::Code(ref d) => d.render(), Token::Strike(ref d) => d.render(), Token::Header(ref d) => d.render(), @@ -59,6 +62,7 @@ impl std::fmt::Display for Token { let data = match *self { Token::Anchor(ref d) => format!("{d}"), Token::Bold(ref d) => format!("{d}"), + Token::CheckBox(ref d) => format!("{d}"), Token::Code(ref d) => format!("{d}"), Token::Strike(ref d) => format!("{d}"), Token::Header(ref d) => format!("{d}"), diff --git a/src/syntax/content/parser/token/checkbox.rs b/src/syntax/content/parser/token/checkbox.rs new file mode 100644 index 0000000..ebdafcd --- /dev/null +++ b/src/syntax/content/parser/token/checkbox.rs @@ -0,0 +1,61 @@ +use crate::{ + syntax::content::{Parseable, Lexeme}, +}; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct CheckBox { + checked: bool, +} + +impl CheckBox { + pub fn new(checked: bool) -> CheckBox { + CheckBox { checked } + } +} + +impl Parseable for CheckBox { + fn probe(lexeme: &Lexeme) -> bool { + lexeme.match_triple_as_char(('[', ' ', ']')) + || lexeme.match_triple_as_char(('[', 'x', ']')) + } + + fn lex(lexeme: &Lexeme) -> CheckBox { + use crate::prelude::*; + log!("Lexing: {lexeme}"); + if lexeme.match_next_as_char('x') { + CheckBox::new(true) + } else { + CheckBox::new(false) + } + } + + fn render(&self) -> String { + let toggle = if self.checked { + " checked " + } else { + "" + }; + format!(r#""#) + } +} + +impl std::fmt::Display for CheckBox { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let display_state = if self.checked { "checked" } else { "empty" }; + write!(f, "CheckBox [{display_state}]") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn render() { + let code_open = CheckBox::new(true); + assert_eq!(code_open.render(), r#""#); + + let code_closed = CheckBox::new(false); + assert_eq!(code_closed.render(), r#""#); + } +}