Implement underline and strikethrough tokens
This commit is contained in:
parent
1faa0d5c3b
commit
0d910634c6
9 changed files with 182 additions and 29 deletions
|
|
@ -41,7 +41,7 @@ fn lex(text: &str, map: LexMap, config: &Config) -> Vec<Token> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if point::parse(lexeme, &mut state, &mut tokens) {
|
||||
if point::parse(lexeme, &mut state, &mut tokens, &mut iterator) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ use crate::{
|
|||
syntax::content::{
|
||||
Parseable as _,
|
||||
parser::{
|
||||
context, Inline,
|
||||
Inline, context,
|
||||
lexeme::Lexeme,
|
||||
state::State,
|
||||
token::{Token, code::Code, anchor::Anchor},
|
||||
state::{AnchorBuffer, State},
|
||||
token::{Token, anchor::Anchor, code::Code},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -29,7 +29,7 @@ pub fn parse(
|
|||
} else if Anchor::probe(lexeme) {
|
||||
log!("Inline Context: None -> Anchor on {lexeme}");
|
||||
state.context.inline = Inline::Anchor;
|
||||
state.buffers.anchor.clear();
|
||||
state.buffers.anchor = AnchorBuffer::default();
|
||||
|
||||
if lexeme.match_as_char('|') {
|
||||
state.buffers.anchor.candidate.leading = true;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
use std::{iter::Peekable, slice::Iter};
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
syntax::content::{
|
||||
Parseable as _,
|
||||
parser::{
|
||||
lexeme::Lexeme,
|
||||
token::{Token, oblique::Oblique, bold::Bold},
|
||||
token::{
|
||||
Token, oblique::Oblique, bold::Bold, underline::Underline,
|
||||
strike::Strike,
|
||||
},
|
||||
state::State,
|
||||
},
|
||||
},
|
||||
|
|
@ -14,14 +19,28 @@ pub fn parse(
|
|||
lexeme: &Lexeme,
|
||||
state: &mut State,
|
||||
tokens: &mut Vec<Token>,
|
||||
iterator: &mut Peekable<Iter<'_, Lexeme>>,
|
||||
) -> bool {
|
||||
if Oblique::probe(lexeme) {
|
||||
log!("Oblique probed {lexeme}");
|
||||
if Underline::probe(lexeme) {
|
||||
log!("Underline probed: {lexeme}");
|
||||
tokens
|
||||
.push(Token::Underline(Underline::new(!state.switches.underline)));
|
||||
state.switches.underline = !state.switches.underline;
|
||||
iterator.next();
|
||||
return true;
|
||||
} else if Oblique::probe(lexeme) {
|
||||
log!("Oblique probed: {lexeme}");
|
||||
tokens.push(Token::Oblique(Oblique::new(!state.switches.oblique)));
|
||||
state.switches.oblique = !state.switches.oblique;
|
||||
return true;
|
||||
} else if Strike::probe(lexeme) {
|
||||
log!("Strike probed: {lexeme}");
|
||||
tokens.push(Token::Strike(Strike::new(!state.switches.crossout)));
|
||||
state.switches.crossout = !state.switches.crossout;
|
||||
iterator.next();
|
||||
return true;
|
||||
} else if Bold::probe(lexeme) {
|
||||
log!("Bold probed {lexeme}");
|
||||
log!("Bold probed: {lexeme}");
|
||||
tokens.push(Token::Bold(Bold::new(!state.switches.bold)));
|
||||
state.switches.bold = !state.switches.bold;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -9,13 +9,15 @@ pub mod delimiter {
|
|||
pub flanking: Vec<char>,
|
||||
pub punctuation: Vec<char>,
|
||||
pub whitespace: Vec<char>,
|
||||
pub double: Vec<char>,
|
||||
}
|
||||
|
||||
impl Default for Delimiters {
|
||||
fn default() -> Self {
|
||||
Delimiters {
|
||||
atomic: vec!['`', '|'],
|
||||
flanking: vec!['_', '*', '(', ')', '\'', '"'],
|
||||
double: vec!['_', '~'],
|
||||
flanking: vec!['_', '*', '~', '(', ')', '\'', '"'],
|
||||
punctuation: vec![',', '.', ';', ':', '?', '!'],
|
||||
whitespace: vec!['\n', ' '],
|
||||
}
|
||||
|
|
@ -55,12 +57,21 @@ pub mod delimiter {
|
|||
|
||||
let mut iterator = text.chars().peekable();
|
||||
while let Some(c) = iterator.next() {
|
||||
// if the current char is a boundary
|
||||
// if current char is a boundary
|
||||
if delimiters.is_boundary(c) {
|
||||
atomized.push(c.to_string());
|
||||
continue;
|
||||
|
||||
// if the current char is a flanking delimiter
|
||||
// if current char is a double delimiter and the next its double
|
||||
} else if delimiters.double.contains(&c)
|
||||
&& iterator.peek().is_some_and(|n| *n == c)
|
||||
{
|
||||
atomized.push(c.to_string());
|
||||
iterator.next();
|
||||
atomized.push(c.to_string());
|
||||
continue;
|
||||
|
||||
// if current char is a flanking delimiter
|
||||
} else if delimiters.flanking.contains(&c) {
|
||||
// if next char is a boundary
|
||||
if iterator
|
||||
|
|
@ -70,7 +81,7 @@ pub mod delimiter {
|
|||
atomized.push(c.to_string());
|
||||
continue;
|
||||
|
||||
// if the previous char was whitespace
|
||||
// if previous char was whitespace
|
||||
} else if let Some(last_string) = atomized.last()
|
||||
&& let Some(last_char) = last_string.chars().last()
|
||||
&& delimiters.whitespace.contains(&last_char)
|
||||
|
|
@ -82,7 +93,7 @@ pub mod delimiter {
|
|||
|
||||
// if there is a last atomized element
|
||||
if let Some(last) = atomized.last_mut() {
|
||||
// if the last atomized element is a boundary
|
||||
// if last atomized element is a boundary
|
||||
if delimiters.is_str_delimiter(last) {
|
||||
atomized.push(c.to_string());
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ pub struct State {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Switches {
|
||||
pub oblique: bool,
|
||||
pub bold: bool,
|
||||
pub oblique: bool,
|
||||
pub crossout: bool,
|
||||
pub underline: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -24,21 +26,13 @@ pub struct Buffers {
|
|||
pub anchor: AnchorBuffer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct AnchorBuffer {
|
||||
pub candidate: Anchor,
|
||||
pub text: String,
|
||||
pub destination: String,
|
||||
}
|
||||
|
||||
impl AnchorBuffer {
|
||||
pub fn clear(&mut self) {
|
||||
self.candidate = Anchor::default();
|
||||
self.text = String::default();
|
||||
self.destination = String::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AnchorBuffer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let display_text = if self.text.is_empty() {
|
||||
|
|
@ -72,8 +66,10 @@ impl Default for State {
|
|||
},
|
||||
dom_ids: HashMap::default(),
|
||||
switches: Switches {
|
||||
oblique: false,
|
||||
bold: false,
|
||||
crossout: false,
|
||||
oblique: false,
|
||||
underline: false,
|
||||
},
|
||||
buffers: Buffers {
|
||||
anchor: AnchorBuffer {
|
||||
|
|
|
|||
|
|
@ -12,12 +12,15 @@ pub mod oblique;
|
|||
pub mod paragraph;
|
||||
pub mod preformat;
|
||||
pub mod span;
|
||||
pub mod strike;
|
||||
pub mod underline;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub enum Token {
|
||||
Anchor(anchor::Anchor),
|
||||
Bold(bold::Bold),
|
||||
Code(code::Code),
|
||||
Strike(strike::Strike),
|
||||
Header(header::Header),
|
||||
Item(item::Item),
|
||||
LineBreak(linebreak::LineBreak),
|
||||
|
|
@ -27,6 +30,7 @@ pub enum Token {
|
|||
Paragraph(paragraph::Paragraph),
|
||||
PreFormat(preformat::PreFormat),
|
||||
Span(span::Span),
|
||||
Underline(underline::Underline),
|
||||
}
|
||||
|
||||
impl Token {
|
||||
|
|
@ -35,6 +39,7 @@ impl Token {
|
|||
Token::Anchor(ref d) => d.render(),
|
||||
Token::Bold(ref d) => d.render(),
|
||||
Token::Code(ref d) => d.render(),
|
||||
Token::Strike(ref d) => d.render(),
|
||||
Token::Header(ref d) => d.render(),
|
||||
Token::Item(ref d) => d.render(),
|
||||
Token::LineBreak(ref d) => d.render(),
|
||||
|
|
@ -44,6 +49,7 @@ impl Token {
|
|||
Token::Paragraph(ref d) => d.render(),
|
||||
Token::PreFormat(ref d) => d.render(),
|
||||
Token::Span(ref d) => d.render(),
|
||||
Token::Underline(ref d) => d.render(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +60,7 @@ impl std::fmt::Display for Token {
|
|||
Token::Anchor(ref d) => format!("{d}"),
|
||||
Token::Bold(ref d) => format!("{d}"),
|
||||
Token::Code(ref d) => format!("{d}"),
|
||||
Token::Strike(ref d) => format!("{d}"),
|
||||
Token::Header(ref d) => format!("{d}"),
|
||||
Token::Item(ref d) => format!("{d}"),
|
||||
Token::LineBreak(ref d) => format!("{d}"),
|
||||
|
|
@ -63,6 +70,7 @@ impl std::fmt::Display for Token {
|
|||
Token::Paragraph(ref d) => format!("{d}"),
|
||||
Token::PreFormat(ref d) => format!("{d}"),
|
||||
Token::Span(ref d) => format!("{d}"),
|
||||
Token::Underline(ref d) => format!("{d}"),
|
||||
};
|
||||
|
||||
write!(f, "Tk:{data}")
|
||||
|
|
|
|||
58
src/syntax/content/parser/token/strike.rs
Normal file
58
src/syntax/content/parser/token/strike.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use crate::{
|
||||
syntax::content::{Parseable, Lexeme},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Strike {
|
||||
open: bool,
|
||||
}
|
||||
|
||||
impl Strike {
|
||||
pub fn new(open: bool) -> Strike {
|
||||
Strike { open }
|
||||
}
|
||||
}
|
||||
|
||||
impl Parseable for Strike {
|
||||
fn probe(lexeme: &Lexeme) -> bool {
|
||||
lexeme.match_as_char('~') && lexeme.match_next_as_char('~')
|
||||
}
|
||||
|
||||
fn lex(_lexeme: &Lexeme) -> Strike {
|
||||
panic!("Attempt to lex a strike tag directly from a lexeme")
|
||||
}
|
||||
|
||||
fn render(&self) -> String {
|
||||
let tag = if self.open { "<s>" } else { "</s>" };
|
||||
String::from(tag)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Strike {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let display_open_state = if self.open { "open" } else { "closed" };
|
||||
write!(f, "Strike [{display_open_state}]")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn render() {
|
||||
let code_open = Strike::new(true);
|
||||
assert_eq!(code_open.render(), "<s>");
|
||||
|
||||
let code_closed = Strike::new(false);
|
||||
assert_eq!(code_closed.render(), "</s>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "Attempt to lex a strike tag directly from a lexeme"
|
||||
)]
|
||||
fn lex() {
|
||||
Strike::lex(&Lexeme::new("", ""));
|
||||
}
|
||||
}
|
||||
61
src/syntax/content/parser/token/underline.rs
Normal file
61
src/syntax/content/parser/token/underline.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use crate::{
|
||||
syntax::content::{Parseable, Lexeme},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Underline {
|
||||
open: bool,
|
||||
}
|
||||
|
||||
impl Underline {
|
||||
pub fn new(open: bool) -> Underline {
|
||||
Underline { open }
|
||||
}
|
||||
}
|
||||
|
||||
impl Parseable for Underline {
|
||||
fn probe(lexeme: &Lexeme) -> bool {
|
||||
lexeme.match_as_char('_') && lexeme.match_next_as_char('_')
|
||||
}
|
||||
|
||||
fn lex(_lexeme: &Lexeme) -> Underline {
|
||||
panic!("Attempt to lex an underline tag directly from a lexeme")
|
||||
}
|
||||
|
||||
fn render(&self) -> String {
|
||||
if self.open {
|
||||
String::from("<u>")
|
||||
} else {
|
||||
String::from("</u>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Underline {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let display_open_state = if self.open { "open" } else { "closed" };
|
||||
write!(f, "Underline [{display_open_state}]")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn render() {
|
||||
let code_open = Underline::new(true);
|
||||
assert_eq!(code_open.render(), "<u>");
|
||||
|
||||
let code_closed = Underline::new(false);
|
||||
assert_eq!(code_closed.render(), "</u>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "Attempt to lex an underline tag directly from a lexeme"
|
||||
)]
|
||||
fn lex() {
|
||||
Underline::lex(&Lexeme::new("", ""));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue