Implement blockquote token

This commit is contained in:
Juno Takano 2026-02-08 17:08:16 -03:00
commit 260610c4a0
11 changed files with 263 additions and 120 deletions

View file

@ -50,7 +50,7 @@ pub fn parse(
} else if Quote::probe(lexeme) {
log!(VERBOSE, "Block Context: None -> Quote on {lexeme}");
state.context.block = Block::Quote;
tokens.push(Token::Quote(Quote::new(true)));
iterator.next();
return true;
} else if Verse::probe(lexeme) {
log!(VERBOSE, "Block Context: None -> Verse on {lexeme}");
@ -93,15 +93,7 @@ pub fn parse(
return super::list::parse(lexeme, state, tokens, iterator, graph);
},
Block::Quote => {
if lexeme.match_char_sequence('\n', '>') {
tokens.push(Token::LineBreak(LineBreak::default()));
iterator.next();
return true;
} else if Quote::probe_end(lexeme) {
tokens.push(Token::Quote(Quote::new(false)));
log!(VERBOSE, "Block Context: Quote -> None on {lexeme}");
state.context.block = Block::None;
}
return super::quote::parse(lexeme, state, tokens, iterator, graph);
},
Block::Verse => {
if Verse::probe_end(lexeme) {

View file

@ -30,6 +30,7 @@ pub fn parse(
let candidate = &mut buffer.candidate;
let item_candidate = &mut buffer.item_candidate;
#[allow(clippy::wildcard_enum_match_arm)]
match state.context.block {
Block::List => {
if lexeme.match_char(' ') && item_candidate.depth.is_none() {

View file

@ -0,0 +1,97 @@
use std::{iter::Peekable, slice::Iter};
use crate::{
graph::Graph,
prelude::*,
syntax::content::parser::{
Lexeme, State, Token,
context::Block,
format, state,
token::{Anchor, Quote},
},
};
/// Handles open quote contexts until a quote is fully parsed.
///
/// A return of `true` will trigger a continue in the outer parser,
/// skipping any further parsing of the current lexeme.
///
/// # Panics
/// This parser can handle only the Quote context, and will panic if passed an
/// unrelated context since it has no knowledge on how to handle them.
pub fn parse(
lexeme: &Lexeme,
state: &mut State,
tokens: &mut Vec<Token>,
iterator: &mut Peekable<Iter<'_, Lexeme>>,
graph: &Graph,
) -> bool {
let buffer = &mut state.buffers.quote;
let candidate = &mut buffer.candidate;
#[allow(clippy::wildcard_enum_match_arm)]
match state.context.block {
Block::Quote => {
if Quote::probe_end(lexeme) {
log!("Probed end of quote on {lexeme}");
let (text, text_tokens) = format(&candidate.text, graph);
candidate.text = text;
state.format_tokens.extend_from_slice(&text_tokens);
if let Some(citation) = &candidate.citation {
let (formatted_citation, citation_tokens) =
format(citation, graph);
candidate.citation = Some(formatted_citation);
state.format_tokens.extend_from_slice(&citation_tokens);
let mut first_anchor = Anchor::default();
for token in citation_tokens {
if let Token::Anchor(token_data) = token {
first_anchor = *token_data.clone();
break;
}
}
if first_anchor.external() {
candidate.url = first_anchor.destination();
}
}
tokens.push(Token::Quote(candidate.clone()));
log!(VERBOSE, "Block Context: Quote -> None on {lexeme}");
state.context.block = Block::None;
*buffer = state::QuoteBuffer::default();
} else if !buffer.in_citation
&& lexeme.match_char('\n')
&& lexeme.next() == "--"
{
log!("Matched citation start on {lexeme}");
buffer.in_citation = true;
iterator.next();
iterator.next();
} else if lexeme.match_char_sequence('\n', '>') {
log!("Matched break-aware sequence on {lexeme}");
candidate.text.push_str(" <\n");
iterator.next();
} else {
log!("Entered quote else branch on {lexeme}");
if buffer.in_citation {
log!("Extending citation on {lexeme}");
candidate.extend_citation(&lexeme.text());
if lexeme.match_char('\n') && lexeme.next() == "--" {
candidate.text.push('\n');
iterator.next();
} else if lexeme.match_char('\n') {
buffer.in_citation = false;
}
} else {
log!("Extending quote on {lexeme}");
candidate.text.push_str(&lexeme.text());
}
}
},
_ => {
panic!("Quote context parser called to handle non-quote context")
},
}
true
}