2024-08-12 22:33:25 +02:00
|
|
|
use core::fmt;
|
2024-08-12 11:25:17 +02:00
|
|
|
use std::any::Any;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::ops::Range;
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
use ariadne::Label;
|
|
|
|
use ariadne::Report;
|
|
|
|
use ariadne::ReportKind;
|
|
|
|
use blockquote_style::AuthorPos::After;
|
|
|
|
use blockquote_style::AuthorPos::Before;
|
|
|
|
use blockquote_style::BlockquoteStyle;
|
|
|
|
use regex::Match;
|
|
|
|
use regex::Regex;
|
2024-08-12 22:33:25 +02:00
|
|
|
use runtime_format::FormatArgs;
|
|
|
|
use runtime_format::FormatKey;
|
|
|
|
use runtime_format::FormatKeyError;
|
2024-08-12 11:25:17 +02:00
|
|
|
|
|
|
|
use crate::compiler::compiler::Compiler;
|
2024-08-12 22:33:25 +02:00
|
|
|
use crate::compiler::compiler::Target;
|
2024-08-12 11:25:17 +02:00
|
|
|
use crate::compiler::compiler::Target::HTML;
|
|
|
|
use crate::document::document::Document;
|
|
|
|
use crate::document::element::ContainerElement;
|
|
|
|
use crate::document::element::ElemKind;
|
|
|
|
use crate::document::element::Element;
|
2024-08-25 14:16:08 +02:00
|
|
|
use crate::elements::paragraph::Paragraph;
|
2024-08-25 14:46:06 +02:00
|
|
|
use crate::elements::text::Text;
|
2024-08-12 11:25:17 +02:00
|
|
|
use crate::parser::parser::ParserState;
|
|
|
|
use crate::parser::rule::Rule;
|
|
|
|
use crate::parser::source::Cursor;
|
|
|
|
use crate::parser::source::Source;
|
|
|
|
use crate::parser::source::Token;
|
|
|
|
use crate::parser::source::VirtualSource;
|
|
|
|
use crate::parser::style::StyleHolder;
|
|
|
|
use crate::parser::util::process_escaped;
|
|
|
|
use crate::parser::util::Property;
|
|
|
|
use crate::parser::util::PropertyParser;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Blockquote {
|
|
|
|
pub(self) location: Token,
|
|
|
|
pub(self) content: Vec<Box<dyn Element>>,
|
|
|
|
pub(self) author: Option<String>,
|
|
|
|
pub(self) cite: Option<String>,
|
|
|
|
pub(self) url: Option<String>,
|
|
|
|
/// Style of the blockquote
|
|
|
|
pub(self) style: Rc<blockquote_style::BlockquoteStyle>,
|
|
|
|
}
|
|
|
|
|
2024-08-12 22:33:25 +02:00
|
|
|
struct FmtPair<'a>(Target, &'a Blockquote);
|
|
|
|
|
|
|
|
impl FormatKey for FmtPair<'_> {
|
|
|
|
fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
|
|
|
|
match key {
|
|
|
|
"author" => write!(
|
|
|
|
f,
|
|
|
|
"{}",
|
|
|
|
Compiler::sanitize(self.0, self.1.author.as_ref().unwrap_or(&"".into()))
|
|
|
|
)
|
|
|
|
.map_err(FormatKeyError::Fmt),
|
|
|
|
"cite" => write!(
|
|
|
|
f,
|
|
|
|
"{}",
|
|
|
|
Compiler::sanitize(self.0, self.1.cite.as_ref().unwrap_or(&"".into()))
|
|
|
|
)
|
|
|
|
.map_err(FormatKeyError::Fmt),
|
|
|
|
_ => Err(FormatKeyError::UnknownKey),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-12 11:25:17 +02:00
|
|
|
impl Element for Blockquote {
|
|
|
|
fn location(&self) -> &Token { &self.location }
|
|
|
|
|
|
|
|
fn kind(&self) -> ElemKind { ElemKind::Block }
|
|
|
|
|
|
|
|
fn element_name(&self) -> &'static str { "Blockquote" }
|
|
|
|
|
2024-08-25 13:19:24 +02:00
|
|
|
fn compile(
|
|
|
|
&self,
|
|
|
|
compiler: &Compiler,
|
|
|
|
document: &dyn Document,
|
|
|
|
cursor: usize,
|
|
|
|
) -> Result<String, String> {
|
2024-08-12 11:25:17 +02:00
|
|
|
match compiler.target() {
|
|
|
|
HTML => {
|
|
|
|
let mut result = r#"<div class="blockquote-content">"#.to_string();
|
2024-08-26 10:59:15 +02:00
|
|
|
let format_author = || -> Result<String, String> {
|
2024-08-12 11:25:17 +02:00
|
|
|
let mut result = String::new();
|
2024-08-12 22:33:25 +02:00
|
|
|
|
2024-08-12 11:25:17 +02:00
|
|
|
if self.cite.is_some() || self.author.is_some() {
|
|
|
|
result += r#"<p class="blockquote-author">"#;
|
2024-08-14 22:50:32 +02:00
|
|
|
let fmt_pair = FmtPair(compiler.target(), self);
|
2024-08-26 10:59:15 +02:00
|
|
|
let format_string = match (self.author.is_some(), self.cite.is_some()) {
|
2024-08-12 22:33:25 +02:00
|
|
|
(true, true) => {
|
2024-08-26 10:59:15 +02:00
|
|
|
Compiler::sanitize_format(fmt_pair.0, self.style.format[0].as_str())
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
2024-08-12 22:33:25 +02:00
|
|
|
(true, false) => {
|
2024-08-26 10:59:15 +02:00
|
|
|
Compiler::sanitize_format(fmt_pair.0, self.style.format[1].as_str())
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
2024-08-12 22:33:25 +02:00
|
|
|
(false, false) => {
|
2024-08-26 10:59:15 +02:00
|
|
|
Compiler::sanitize_format(fmt_pair.0, self.style.format[2].as_str())
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
2024-08-12 22:33:25 +02:00
|
|
|
_ => panic!(""),
|
2024-08-26 10:59:15 +02:00
|
|
|
};
|
|
|
|
let args = FormatArgs::new(format_string.as_str(), &fmt_pair);
|
|
|
|
args.status().map_err(|err| {
|
|
|
|
format!("Failed to format Blockquote style `{format_string}`: {err}")
|
|
|
|
})?;
|
|
|
|
result += args.to_string().as_str();
|
2024-08-12 11:25:17 +02:00
|
|
|
result += "</p>";
|
|
|
|
}
|
|
|
|
Ok(result)
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(url) = &self.url {
|
|
|
|
result += format!(r#"<blockquote cite="{}">"#, Compiler::sanitize(HTML, url))
|
|
|
|
.as_str();
|
|
|
|
} else {
|
|
|
|
result += "<blockquote>";
|
|
|
|
}
|
|
|
|
if self.style.author_pos == Before {
|
2024-08-26 10:59:15 +02:00
|
|
|
result += format_author()?.as_str();
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
|
|
|
|
2024-08-25 14:16:08 +02:00
|
|
|
let mut in_paragraph = false;
|
2024-08-12 11:25:17 +02:00
|
|
|
for elem in &self.content {
|
2024-08-27 10:39:30 +02:00
|
|
|
if elem.downcast_ref::<Blockquote>().is_some() {
|
2024-08-26 10:59:15 +02:00
|
|
|
if in_paragraph {
|
2024-08-25 14:16:08 +02:00
|
|
|
result += "</p>";
|
|
|
|
in_paragraph = false;
|
|
|
|
}
|
|
|
|
result += elem
|
|
|
|
.compile(compiler, document, cursor + result.len())?
|
|
|
|
.as_str();
|
2024-08-26 10:59:15 +02:00
|
|
|
} else {
|
2024-08-25 14:16:08 +02:00
|
|
|
if !in_paragraph {
|
|
|
|
result += "<p>";
|
|
|
|
in_paragraph = true;
|
|
|
|
}
|
|
|
|
result += elem
|
|
|
|
.compile(compiler, document, cursor + result.len())?
|
|
|
|
.as_str();
|
|
|
|
}
|
|
|
|
}
|
2024-08-26 10:59:15 +02:00
|
|
|
if in_paragraph {
|
2024-08-25 14:16:08 +02:00
|
|
|
result += "</p>";
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
2024-08-25 14:16:08 +02:00
|
|
|
result += "</blockquote>";
|
2024-08-12 11:25:17 +02:00
|
|
|
if self.style.author_pos == After {
|
2024-08-12 22:33:25 +02:00
|
|
|
result += format_author().map_err(|err| err.to_string())?.as_str();
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
result += "</div>";
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
_ => todo!(""),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_container(&self) -> Option<&dyn ContainerElement> { Some(self) }
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContainerElement for Blockquote {
|
|
|
|
fn contained(&self) -> &Vec<Box<dyn Element>> { &self.content }
|
|
|
|
|
|
|
|
fn push(&mut self, elem: Box<dyn Element>) -> Result<(), String> {
|
|
|
|
if elem.kind() == ElemKind::Block {
|
|
|
|
return Err("Cannot add block element inside a blockquote".to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.content.push(elem);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::blockquote")]
|
|
|
|
pub struct BlockquoteRule {
|
|
|
|
start_re: Regex,
|
|
|
|
continue_re: Regex,
|
|
|
|
properties: PropertyParser,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BlockquoteRule {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
let mut props = HashMap::new();
|
|
|
|
props.insert(
|
|
|
|
"author".to_string(),
|
|
|
|
Property::new(false, "Quote author".to_string(), None),
|
|
|
|
);
|
|
|
|
props.insert(
|
|
|
|
"cite".to_string(),
|
|
|
|
Property::new(false, "Quote source".to_string(), None),
|
|
|
|
);
|
|
|
|
props.insert(
|
|
|
|
"url".to_string(),
|
|
|
|
Property::new(false, "Quote source url".to_string(), None),
|
|
|
|
);
|
|
|
|
|
|
|
|
Self {
|
2024-08-26 12:38:43 +02:00
|
|
|
start_re: Regex::new(r"(?:^|\n)>(?:\[((?:\\.|[^\\\\])*?)\])?\s*?(.*)").unwrap(),
|
2024-08-25 14:46:06 +02:00
|
|
|
continue_re: Regex::new(r"(?:^|\n)>\s*?(.*)").unwrap(),
|
2024-08-12 11:25:17 +02:00
|
|
|
properties: PropertyParser { properties: props },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_properties(
|
|
|
|
&self,
|
|
|
|
m: Match,
|
|
|
|
) -> Result<(Option<String>, Option<String>, Option<String>), String> {
|
|
|
|
let processed = process_escaped('\\', "]", m.as_str());
|
|
|
|
let pm = self.properties.parse(processed.as_str())?;
|
|
|
|
|
|
|
|
let author = pm
|
|
|
|
.get("author", |_, s| -> Result<String, ()> { Ok(s.to_string()) })
|
|
|
|
.map(|(_, s)| s)
|
|
|
|
.ok();
|
|
|
|
let cite = pm
|
|
|
|
.get("cite", |_, s| -> Result<String, ()> { Ok(s.to_string()) })
|
|
|
|
.map(|(_, s)| s)
|
|
|
|
.ok();
|
|
|
|
let url = pm
|
|
|
|
.get("url", |_, s| -> Result<String, ()> { Ok(s.to_string()) })
|
|
|
|
.map(|(_, s)| s)
|
|
|
|
.ok();
|
|
|
|
|
|
|
|
Ok((author, cite, url))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rule for BlockquoteRule {
|
|
|
|
fn name(&self) -> &'static str { "Blockquote" }
|
|
|
|
|
|
|
|
fn previous(&self) -> Option<&'static str> { Some("List") }
|
|
|
|
|
|
|
|
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
|
|
|
self.start_re
|
2024-08-25 13:19:24 +02:00
|
|
|
.find_at(cursor.source.content(), cursor.pos)
|
|
|
|
.map(|m| (m.start(), Box::new([false; 0]) as Box<dyn Any>))
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn on_match<'a>(
|
|
|
|
&self,
|
|
|
|
state: &ParserState,
|
|
|
|
document: &'a (dyn Document<'a> + 'a),
|
|
|
|
cursor: Cursor,
|
|
|
|
_match_data: Box<dyn Any>,
|
|
|
|
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
|
|
|
let mut reports = vec![];
|
|
|
|
|
|
|
|
let content = cursor.source.content();
|
|
|
|
let mut end_cursor = cursor.clone();
|
2024-08-25 13:19:24 +02:00
|
|
|
if let Some(captures) = self.start_re.captures_at(content, end_cursor.pos) {
|
|
|
|
if captures.get(0).unwrap().start() != end_cursor.pos {
|
|
|
|
return (end_cursor, reports);
|
|
|
|
}
|
|
|
|
// Advance cursor
|
|
|
|
end_cursor = end_cursor.at(captures.get(0).unwrap().end());
|
|
|
|
|
|
|
|
// Properties
|
|
|
|
let mut author = None;
|
|
|
|
let mut cite = None;
|
|
|
|
let mut url = None;
|
|
|
|
if let Some(properties) = captures.get(1) {
|
|
|
|
match self.parse_properties(properties) {
|
|
|
|
Err(err) => {
|
|
|
|
reports.push(
|
|
|
|
Report::build(
|
|
|
|
ReportKind::Warning,
|
|
|
|
cursor.source.clone(),
|
|
|
|
properties.start(),
|
|
|
|
)
|
|
|
|
.with_message("Invalid Blockquote Properties")
|
|
|
|
.with_label(
|
|
|
|
Label::new((cursor.source.clone(), properties.range()))
|
|
|
|
.with_message(err)
|
|
|
|
.with_color(state.parser.colors().warning),
|
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
|
|
|
return (end_cursor, reports);
|
|
|
|
}
|
|
|
|
Ok(props) => (author, cite, url) = props,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Content
|
|
|
|
let entry_start = captures.get(0).unwrap().start();
|
|
|
|
let mut entry_content = captures.get(2).unwrap().as_str().to_string();
|
|
|
|
while let Some(captures) = self.continue_re.captures_at(content, end_cursor.pos) {
|
2024-08-12 11:25:17 +02:00
|
|
|
if captures.get(0).unwrap().start() != end_cursor.pos {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Advance cursor
|
|
|
|
end_cursor = end_cursor.at(captures.get(0).unwrap().end());
|
|
|
|
|
2024-08-25 14:46:06 +02:00
|
|
|
let trimmed = captures.get(1).unwrap().as_str().trim_start().trim_end();
|
2024-08-26 10:59:15 +02:00
|
|
|
entry_content += "\n";
|
|
|
|
entry_content += trimmed;
|
2024-08-25 13:19:24 +02:00
|
|
|
}
|
2024-08-12 11:25:17 +02:00
|
|
|
|
2024-08-25 13:19:24 +02:00
|
|
|
// Parse entry content
|
|
|
|
let token = Token::new(entry_start..end_cursor.pos, end_cursor.source.clone());
|
|
|
|
let entry_src = Rc::new(VirtualSource::new(
|
|
|
|
token.clone(),
|
|
|
|
"Blockquote Entry".to_string(),
|
|
|
|
entry_content,
|
|
|
|
));
|
2024-08-25 14:16:08 +02:00
|
|
|
// Parse content
|
|
|
|
let parsed_doc = state.with_state(|new_state| {
|
2024-08-26 10:59:15 +02:00
|
|
|
new_state
|
|
|
|
.parser
|
|
|
|
.parse(new_state, entry_src, Some(document))
|
|
|
|
.0
|
2024-08-25 14:16:08 +02:00
|
|
|
});
|
2024-08-26 10:59:15 +02:00
|
|
|
|
2024-08-25 14:16:08 +02:00
|
|
|
// Extract paragraph and nested blockquotes
|
2024-08-26 10:59:15 +02:00
|
|
|
let mut parsed_content: Vec<Box<dyn Element>> = vec![];
|
|
|
|
for mut elem in parsed_doc.content().borrow_mut().drain(..) {
|
|
|
|
if let Some(paragraph) = elem.downcast_mut::<Paragraph>() {
|
|
|
|
if let Some(last) = parsed_content.last() {
|
|
|
|
if last.kind() == ElemKind::Inline {
|
2024-08-25 14:46:06 +02:00
|
|
|
parsed_content.push(Box::new(Text {
|
2024-08-26 10:59:15 +02:00
|
|
|
location: Token::new(
|
|
|
|
last.location().end()..last.location().end(),
|
|
|
|
last.location().source(),
|
|
|
|
),
|
|
|
|
content: " ".to_string(),
|
2024-08-25 14:46:06 +02:00
|
|
|
}) as Box<dyn Element>);
|
|
|
|
}
|
|
|
|
}
|
2024-08-25 14:16:08 +02:00
|
|
|
parsed_content.extend(std::mem::take(&mut paragraph.content));
|
2024-08-26 10:59:15 +02:00
|
|
|
} else if elem.downcast_ref::<Blockquote>().is_some() {
|
2024-08-25 14:16:08 +02:00
|
|
|
parsed_content.push(elem);
|
2024-08-26 10:59:15 +02:00
|
|
|
} else {
|
2024-08-25 13:19:24 +02:00
|
|
|
reports.push(
|
2024-08-25 14:16:08 +02:00
|
|
|
Report::build(ReportKind::Error, token.source(), token.range.start)
|
2024-08-25 13:19:24 +02:00
|
|
|
.with_message("Unable to Parse Blockquote Entry")
|
|
|
|
.with_label(
|
|
|
|
Label::new((token.source(), token.range.clone()))
|
2024-08-25 14:16:08 +02:00
|
|
|
.with_message("Blockquotes may only contain paragraphs and other blockquotes")
|
|
|
|
.with_color(state.parser.colors().error),
|
2024-08-25 13:19:24 +02:00
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
|
|
|
return (end_cursor, reports);
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
2024-08-25 14:16:08 +02:00
|
|
|
}
|
2024-08-12 11:25:17 +02:00
|
|
|
|
2024-08-25 13:19:24 +02:00
|
|
|
// Get style
|
|
|
|
let style = state
|
|
|
|
.shared
|
|
|
|
.styles
|
|
|
|
.borrow()
|
|
|
|
.current(blockquote_style::STYLE_KEY)
|
|
|
|
.downcast_rc::<BlockquoteStyle>()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
state.push(
|
|
|
|
document,
|
|
|
|
Box::new(Blockquote {
|
|
|
|
location: Token::new(entry_start..end_cursor.pos, end_cursor.source.clone()),
|
|
|
|
content: parsed_content,
|
|
|
|
author,
|
|
|
|
cite,
|
|
|
|
url,
|
|
|
|
style,
|
|
|
|
}),
|
|
|
|
);
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
(end_cursor, reports)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_styles(&self, holder: &mut StyleHolder) {
|
|
|
|
holder.set_current(Rc::new(BlockquoteStyle::default()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod blockquote_style {
|
|
|
|
use serde::Deserialize;
|
|
|
|
use serde::Serialize;
|
|
|
|
|
|
|
|
use crate::impl_elementstyle;
|
|
|
|
|
2024-08-14 22:50:32 +02:00
|
|
|
pub static STYLE_KEY: &str = "style.blockquote";
|
2024-08-12 11:25:17 +02:00
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
pub enum AuthorPos {
|
|
|
|
Before,
|
|
|
|
After,
|
|
|
|
None,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
pub struct BlockquoteStyle {
|
|
|
|
pub author_pos: AuthorPos,
|
|
|
|
pub format: [String; 3],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for BlockquoteStyle {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
author_pos: AuthorPos::After,
|
|
|
|
format: [
|
2024-08-12 22:33:25 +02:00
|
|
|
"{author}, {cite}".into(),
|
|
|
|
"{author}".into(),
|
|
|
|
"{cite}".into(),
|
2024-08-12 11:25:17 +02:00
|
|
|
],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_elementstyle!(BlockquoteStyle, STYLE_KEY);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::elements::paragraph::Paragraph;
|
|
|
|
use crate::elements::style::Style;
|
|
|
|
use crate::elements::text::Text;
|
|
|
|
use crate::parser::langparser::LangParser;
|
|
|
|
use crate::parser::parser::Parser;
|
|
|
|
use crate::parser::source::SourceFile;
|
|
|
|
use crate::validate_document;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn parser() {
|
|
|
|
let source = Rc::new(SourceFile::with_content(
|
|
|
|
"".to_string(),
|
|
|
|
r#"
|
|
|
|
BEFORE
|
|
|
|
>[author=A, cite=B, url=C] Some entry
|
|
|
|
> contin**ued here
|
|
|
|
> **
|
|
|
|
AFTER
|
2024-08-25 14:46:06 +02:00
|
|
|
> Another
|
|
|
|
>
|
|
|
|
> quote
|
2024-08-25 14:16:08 +02:00
|
|
|
>>[author=B] Nested
|
|
|
|
>>> More nested
|
2024-08-25 13:19:24 +02:00
|
|
|
END
|
2024-08-12 11:25:17 +02:00
|
|
|
"#
|
|
|
|
.to_string(),
|
|
|
|
None,
|
|
|
|
));
|
|
|
|
let parser = LangParser::default();
|
2024-08-12 22:33:25 +02:00
|
|
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
2024-08-12 11:25:17 +02:00
|
|
|
|
|
|
|
validate_document!(doc.content().borrow(), 0,
|
|
|
|
Paragraph { Text{ content == "BEFORE" }; };
|
|
|
|
Blockquote {
|
|
|
|
author == Some("A".to_string()),
|
|
|
|
cite == Some("B".to_string()),
|
|
|
|
url == Some("C".to_string())
|
|
|
|
} {
|
|
|
|
Text { content == "Some entry contin" };
|
|
|
|
Style;
|
2024-08-25 14:16:08 +02:00
|
|
|
Text { content == "ued here" };
|
2024-08-12 11:25:17 +02:00
|
|
|
Style;
|
|
|
|
};
|
|
|
|
Paragraph { Text{ content == "AFTER" }; };
|
2024-08-25 13:19:24 +02:00
|
|
|
Blockquote {
|
2024-08-25 14:46:06 +02:00
|
|
|
Text { content == "Another" };
|
|
|
|
Text { content == " " };
|
|
|
|
Text { content == "quote" };
|
2024-08-25 14:16:08 +02:00
|
|
|
Blockquote { author == Some("B".to_string()) } {
|
|
|
|
Text { content == "Nested" };
|
|
|
|
Blockquote {
|
|
|
|
Text { content == "More nested" };
|
|
|
|
};
|
|
|
|
};
|
2024-08-25 13:19:24 +02:00
|
|
|
};
|
|
|
|
Paragraph { Text{ content == "END" }; };
|
2024-08-12 11:25:17 +02:00
|
|
|
);
|
|
|
|
}
|
2024-08-12 22:33:25 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn style() {
|
|
|
|
let source = Rc::new(SourceFile::with_content(
|
|
|
|
"".to_string(),
|
|
|
|
r#"
|
|
|
|
@@style.blockquote = {
|
|
|
|
"author_pos": "Before",
|
|
|
|
"format": ["{cite} by {author}", "Author: {author}", "From: {cite}"]
|
|
|
|
}
|
|
|
|
PRE
|
|
|
|
>[author=A, cite=B, url=C] Some entry
|
|
|
|
> contin**ued here
|
|
|
|
> **
|
|
|
|
AFTER
|
|
|
|
"#
|
|
|
|
.to_string(),
|
|
|
|
None,
|
|
|
|
));
|
|
|
|
let parser = LangParser::default();
|
|
|
|
let (_, state) = parser.parse(ParserState::new(&parser, None), source, None);
|
|
|
|
|
|
|
|
let style = state
|
|
|
|
.shared
|
|
|
|
.styles
|
|
|
|
.borrow()
|
|
|
|
.current(blockquote_style::STYLE_KEY)
|
|
|
|
.downcast_rc::<BlockquoteStyle>()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(style.author_pos, Before);
|
|
|
|
assert_eq!(
|
|
|
|
style.format,
|
|
|
|
[
|
|
|
|
"{cite} by {author}".to_string(),
|
|
|
|
"Author: {author}".to_string(),
|
|
|
|
"From: {cite}".to_string()
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
2024-08-12 11:25:17 +02:00
|
|
|
}
|