Implement list token
This commit is contained in:
parent
67b6d5db9f
commit
0ec784034a
5 changed files with 166 additions and 9 deletions
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::syntax::content::parser::{
|
use crate::syntax::content::parser::{
|
||||||
state::State,
|
state::State,
|
||||||
token::{Token, paragraph::Paragraph, preformat::PreFormat},
|
token::{
|
||||||
|
Token, paragraph::Paragraph, preformat::PreFormat, list::List,
|
||||||
|
item::Item,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod anchor;
|
pub mod anchor;
|
||||||
|
|
@ -17,6 +20,8 @@ pub struct Context {
|
||||||
pub enum Block {
|
pub enum Block {
|
||||||
Paragraph,
|
Paragraph,
|
||||||
Header(u8),
|
Header(u8),
|
||||||
|
Item(bool),
|
||||||
|
List(bool),
|
||||||
PreFormat,
|
PreFormat,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
@ -38,6 +43,13 @@ pub fn close(state: &State, tokens: &mut Vec<Token>) {
|
||||||
Block::Paragraph => {
|
Block::Paragraph => {
|
||||||
tokens.push(Token::Paragraph(Paragraph::new(false)));
|
tokens.push(Token::Paragraph(Paragraph::new(false)));
|
||||||
},
|
},
|
||||||
|
Block::Item(ordered) => {
|
||||||
|
tokens.push(Token::Item(Item::new(false)));
|
||||||
|
tokens.push(Token::List(List::new(false, ordered)));
|
||||||
|
},
|
||||||
|
Block::List(ordered) => {
|
||||||
|
tokens.push(Token::List(List::new(false, ordered)));
|
||||||
|
},
|
||||||
Block::Header(_) => panic!("End of input with open header"),
|
Block::Header(_) => panic!("End of input with open header"),
|
||||||
Block::None => (),
|
Block::None => (),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
state::State,
|
state::State,
|
||||||
token::{
|
token::{
|
||||||
Token, header::Header, preformat::PreFormat,
|
Token, header::Header, preformat::PreFormat,
|
||||||
paragraph::Paragraph, literal::Literal,
|
paragraph::Paragraph, literal::Literal, list::List, item::Item,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -42,6 +42,16 @@ pub fn parse(
|
||||||
state.context.block = Block::Header(header.level());
|
state.context.block = Block::Header(header.level());
|
||||||
tokens.push(Token::Header(header));
|
tokens.push(Token::Header(header));
|
||||||
return true;
|
return true;
|
||||||
|
} else if List::probe(lexeme) {
|
||||||
|
let ordered = lexeme.match_as_char('+');
|
||||||
|
log!("Block Context: None -> Item on {lexeme}");
|
||||||
|
state.context.block = Block::Item(ordered);
|
||||||
|
tokens.push(Token::List(List::new(true, ordered)));
|
||||||
|
tokens.push(Token::Item(Item::new(true)));
|
||||||
|
// List::probe implies a dash followed by a space,
|
||||||
|
// both of which sould not be rendered literally
|
||||||
|
iterator.next();
|
||||||
|
return true;
|
||||||
} else if Paragraph::probe(lexeme) {
|
} else if Paragraph::probe(lexeme) {
|
||||||
log!("Block Context: None -> Paragraph on {lexeme}");
|
log!("Block Context: None -> Paragraph on {lexeme}");
|
||||||
state.context.block = Block::Paragraph;
|
state.context.block = Block::Paragraph;
|
||||||
|
|
@ -72,6 +82,28 @@ pub fn parse(
|
||||||
state.context.block = Block::None;
|
state.context.block = Block::None;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Block::List(ordered) => {
|
||||||
|
if List::probe_end(lexeme) {
|
||||||
|
tokens.push(Token::List(List::new(false, ordered)));
|
||||||
|
log!("Block Context: List -> None on {lexeme}");
|
||||||
|
state.context.block = Block::None;
|
||||||
|
} else if Item::probe(lexeme) {
|
||||||
|
tokens.push(Token::Item(Item::new(true)));
|
||||||
|
log!("Block Context: List -> Item on {lexeme}");
|
||||||
|
state.context.block = Block::Item(ordered);
|
||||||
|
// Item::probe implies a dash followed by a space,
|
||||||
|
// both of which sould not be rendered literally
|
||||||
|
iterator.next();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Block::Item(ordered) => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -139,4 +171,28 @@ mod tests {
|
||||||
let level = Level::from(u);
|
let level = Level::from(u);
|
||||||
assert_eq!(level.to_string(), "6");
|
assert_eq!(level.to_string(), "6");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unordered_list_at_eoi() {
|
||||||
|
assert_eq!(
|
||||||
|
read("- a\n- b\n- c"),
|
||||||
|
"<ul><li>a</li>\n<li>b</li>\n<li>c</li></ul>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unordered_list_with_content_before() {
|
||||||
|
assert_eq!(
|
||||||
|
read("_e e_\n\n- a\n- b\n- c"),
|
||||||
|
"<p><em>e e</em></p>\n\n<ul><li>a</li>\n<li>b</li>\n<li>c</li></ul>",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unordered_list_with_content_after() {
|
||||||
|
assert_eq!(
|
||||||
|
read("- a\n- b\n- c\n\nd",),
|
||||||
|
"<ul><li>a</li>\n<li>b</li>\n<li>c</li>\n</ul>\n<p>d</p>"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,17 @@ pub mod header;
|
||||||
pub mod preformat;
|
pub mod preformat;
|
||||||
pub mod code;
|
pub mod code;
|
||||||
pub mod oblique;
|
pub mod oblique;
|
||||||
|
pub mod list;
|
||||||
|
pub mod item;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Anchor(anchor::Anchor),
|
Anchor(anchor::Anchor),
|
||||||
Code(code::Code),
|
Code(code::Code),
|
||||||
Header(header::Header),
|
Header(header::Header),
|
||||||
|
Item(item::Item),
|
||||||
LineBreak(linebreak::LineBreak),
|
LineBreak(linebreak::LineBreak),
|
||||||
|
List(list::List),
|
||||||
Literal(literal::Literal),
|
Literal(literal::Literal),
|
||||||
Oblique(oblique::Oblique),
|
Oblique(oblique::Oblique),
|
||||||
Paragraph(paragraph::Paragraph),
|
Paragraph(paragraph::Paragraph),
|
||||||
|
|
@ -29,7 +33,9 @@ impl Token {
|
||||||
Token::Anchor(ref d) => d.render(),
|
Token::Anchor(ref d) => d.render(),
|
||||||
Token::Code(ref d) => d.render(),
|
Token::Code(ref d) => d.render(),
|
||||||
Token::Header(ref d) => d.render(),
|
Token::Header(ref d) => d.render(),
|
||||||
|
Token::Item(ref d) => d.render(),
|
||||||
Token::LineBreak(ref d) => d.render(),
|
Token::LineBreak(ref d) => d.render(),
|
||||||
|
Token::List(ref d) => d.render(),
|
||||||
Token::Literal(ref d) => d.render(),
|
Token::Literal(ref d) => d.render(),
|
||||||
Token::Oblique(ref d) => d.render(),
|
Token::Oblique(ref d) => d.render(),
|
||||||
Token::Paragraph(ref d) => d.render(),
|
Token::Paragraph(ref d) => d.render(),
|
||||||
|
|
@ -45,7 +51,9 @@ impl std::fmt::Display for Token {
|
||||||
Token::Anchor(ref d) => format!("{d}"),
|
Token::Anchor(ref d) => format!("{d}"),
|
||||||
Token::Code(ref d) => format!("{d}"),
|
Token::Code(ref d) => format!("{d}"),
|
||||||
Token::Header(ref d) => format!("{d}"),
|
Token::Header(ref d) => format!("{d}"),
|
||||||
|
Token::Item(ref d) => format!("{d}"),
|
||||||
Token::LineBreak(ref d) => format!("{d}"),
|
Token::LineBreak(ref d) => format!("{d}"),
|
||||||
|
Token::List(ref d) => format!("{d}"),
|
||||||
Token::Literal(ref d) => format!("{d}"),
|
Token::Literal(ref d) => format!("{d}"),
|
||||||
Token::Oblique(ref d) => format!("{d}"),
|
Token::Oblique(ref d) => format!("{d}"),
|
||||||
Token::Paragraph(ref d) => format!("{d}"),
|
Token::Paragraph(ref d) => format!("{d}"),
|
||||||
|
|
@ -56,10 +64,3 @@ impl std::fmt::Display for Token {
|
||||||
write!(f, "Tk:{data}")
|
write!(f, "Tk:{data}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn smoke() {}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
41
src/syntax/content/parser/token/item.rs
Normal file
41
src/syntax/content/parser/token/item.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
use crate::syntax::content::{Parseable, Lexeme};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct Item {
|
||||||
|
open: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parseable for Item {
|
||||||
|
fn probe(lexeme: &Lexeme) -> bool {
|
||||||
|
(lexeme.match_as_char('-') || lexeme.match_as_char('+'))
|
||||||
|
&& lexeme.match_next_as_char(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex(_lexeme: &Lexeme) -> Item {
|
||||||
|
Item { open: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> String {
|
||||||
|
if self.open {
|
||||||
|
String::from("<li>")
|
||||||
|
} else {
|
||||||
|
String::from("</li>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
pub fn new(open: bool) -> Item {
|
||||||
|
Item { open }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn probe_end(lexeme: &Lexeme) -> bool {
|
||||||
|
lexeme.match_as_char('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Item {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "Item [{}]", if self.open { "open" } else { "closed" })
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/syntax/content/parser/token/list.rs
Normal file
47
src/syntax/content/parser/token/list.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::syntax::content::{Parseable, Lexeme};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct List {
|
||||||
|
open: bool,
|
||||||
|
ordered: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parseable for List {
|
||||||
|
fn probe(lexeme: &Lexeme) -> bool {
|
||||||
|
(lexeme.match_as_char('-') || lexeme.match_as_char('+'))
|
||||||
|
&& lexeme.match_next_as_char(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex(_lexeme: &Lexeme) -> List {
|
||||||
|
panic!("Attempt to lex a List directly from a lexeme")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> String {
|
||||||
|
|
||||||
|
let bar = if self.open { "" } else { "/" };
|
||||||
|
let tag = if self.ordered { "ol" } else { "ul" };
|
||||||
|
|
||||||
|
format!("<{bar}{tag}>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl List {
|
||||||
|
pub fn new(open: bool, ordered: bool) -> List {
|
||||||
|
List { open, ordered }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn probe_end(lexeme: &Lexeme) -> bool {
|
||||||
|
lexeme.match_as_char('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for List {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"List [{} {}]",
|
||||||
|
if self.open { "open" } else { "closed" },
|
||||||
|
if self.ordered { "ordered" } else { "unordered" },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue