Compare commits

...

2 commits

Author SHA1 Message Date
e43cf4a8f3 Fix links 2024-08-02 10:34:56 +02:00
eaadfca9b9 Refactor lists 2024-08-02 10:32:00 +02:00
3 changed files with 476 additions and 299 deletions

View file

@ -21,13 +21,11 @@ use regex::Regex;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use super::paragraph::Paragraph;
#[derive(Debug)] #[derive(Debug)]
pub struct Link { pub struct Link {
pub(self) location: Token, pub(self) location: Token,
/// Display content of link /// Display content of link
pub(self) display: Paragraph, pub(self) display: Vec<Box<dyn Element>>,
/// Url of link /// Url of link
pub(self) url: String, pub(self) url: String,
} }
@ -45,11 +43,9 @@ impl Element for Link {
Compiler::sanitize(compiler.target(), self.url.as_str()) Compiler::sanitize(compiler.target(), self.url.as_str())
); );
result += self for elem in &self.display {
.display result += elem.compile(compiler, document)?.as_str();
.compile(compiler, document) }
.as_ref()
.map(|r| r.as_str())?;
result += "</a>"; result += "</a>";
Ok(result) Ok(result)
@ -62,13 +58,14 @@ impl Element for Link {
} }
impl ContainerElement for Link { impl ContainerElement for Link {
fn contained(&self) -> &Vec<Box<dyn Element>> { &self.display.content } fn contained(&self) -> &Vec<Box<dyn Element>> { &self.display }
fn push(&mut self, elem: Box<dyn Element>) -> Result<(), String> { fn push(&mut self, elem: Box<dyn Element>) -> Result<(), String> {
if elem.downcast_ref::<Link>().is_some() { if elem.downcast_ref::<Link>().is_some() {
return Err("Tried to push a link inside of a link".to_string()); return Err("Tried to push a link inside of a link".to_string());
} }
self.display.push(elem) self.display.push(elem);
Ok(())
} }
} }
@ -152,7 +149,7 @@ impl RegexRule for LinkRule {
); );
return reports; return reports;
} }
Ok(paragraph) => *paragraph, Ok(mut paragraph) => std::mem::replace(&mut paragraph.content, vec![]),
} }
} }
_ => panic!("Empty link name"), _ => panic!("Empty link name"),
@ -215,8 +212,9 @@ impl RegexRule for LinkRule {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::elements::paragraph::Paragraph;
use crate::elements::style::Style; use crate::elements::style::Style;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;

View file

@ -1,235 +1,251 @@
use std::{any::Any, cell::Ref, ops::Range, rc::Rc}; use std::any::Any;
use std::cell::Ref;
use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc;
use crate::{compiler::compiler::{Compiler, Target}, document::{document::{Document, DocumentAccessors}, element::{ElemKind, Element}}, parser::{parser::Parser, rule::Rule, source::{Cursor, Source, Token, VirtualSource}}}; use crate::compiler::compiler::Compiler;
use ariadne::{Label, Report, ReportKind}; use crate::compiler::compiler::Target;
use mlua::{Function, Lua}; use crate::document::document::Document;
use crate::document::document::DocumentAccessors;
use crate::document::element::ContainerElement;
use crate::document::element::ElemKind;
use crate::document::element::Element;
use crate::parser::parser::Parser;
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::util;
use crate::parser::util::process_escaped;
use crate::parser::util::Property;
use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Match;
use regex::Regex; use regex::Regex;
use super::paragraph::Paragraph; #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum MarkerKind {
#[derive(Debug)] Open,
pub struct ListEntry { Close,
location: Token,
numbering: Vec<(bool, usize)>,
content: Vec<Box<dyn Element>>,
// TODO bullet_maker : FnMut<...>
}
impl ListEntry {
pub fn new(location: Token, numbering: Vec<(bool, usize)>, content: Vec<Box<dyn Element>>) -> Self {
Self { location, numbering, content }
}
} }
#[derive(Debug)] #[derive(Debug)]
pub struct List pub struct ListMarker {
{ pub(self) location: Token,
location: Token, pub(self) numbered: bool,
entries: Vec<ListEntry> pub(self) kind: MarkerKind,
} }
impl List impl Element for ListMarker {
{
pub fn new(location: Token) -> Self
{
Self
{
location,
entries: Vec::new()
}
}
pub fn push(&mut self, entry: ListEntry)
{
self.location.range = self.location.start()..entry.location.end();
self.entries.push(entry);
}
}
impl Element for List
{
fn location(&self) -> &Token { &self.location } fn location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { ElemKind::Block } fn kind(&self) -> ElemKind { ElemKind::Block }
fn element_name(&self) -> &'static str { "List" } fn element_name(&self) -> &'static str { "List Marker" }
fn to_string(&self) -> String { format!("{self:#?}") } fn to_string(&self) -> String { format!("{self:#?}") }
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> { fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
match compiler.target() match compiler.target() {
{ Target::HTML => match (self.kind, self.numbered) {
(MarkerKind::Close, true) => Ok("</ol>".to_string()),
(MarkerKind::Close, false) => Ok("</ul>".to_string()),
(MarkerKind::Open, true) => Ok("<ol>".to_string()),
(MarkerKind::Open, false) => Ok("<ul>".to_string()),
},
_ => todo!(),
}
}
}
#[derive(Debug)]
pub struct ListEntry {
pub(self) location: Token,
pub(self) numbering: Vec<(bool, usize)>,
pub(self) content: Vec<Box<dyn Element>>,
pub(self) bullet: Option<String>,
}
impl Element for ListEntry {
fn location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { ElemKind::Block }
fn element_name(&self) -> &'static str { "List Entry" }
fn to_string(&self) -> String { format!("{self:#?}") }
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
match compiler.target() {
Target::HTML => { Target::HTML => {
let mut result = String::new(); let mut result = "<li>".to_string();
for elem in &self.content {
//TODO: Do something about indexing result += elem.compile(compiler, document)?.as_str();
let mut current_list: Vec<bool> = vec![]; }
let mut match_stack = |result: &mut String, target: &Vec<(bool, usize)>| { result += "</li>";
Ok(result)
// Find index after which current_list and target differ }
let mut match_idx = 0usize; _ => todo!(),
for i in 0..current_list.len() }
{
if i >= target.len() || current_list[i] != target[i].0 { break }
else { match_idx = i+1; }
} }
// Close until same match fn as_container(&self) -> Option<&dyn ContainerElement> { Some(self) }
for _ in match_idx..current_list.len() }
{
result.push_str(["</ul>", "</ol>"][current_list.pop().unwrap() as usize]); impl ContainerElement for ListEntry {
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 list".to_string());
} }
// Open self.content.push(elem);
for i in match_idx..target.len()
{
result.push_str(["<ul>", "<ol>"][target[i].0 as usize]);
current_list.push(target[i].0);
}
};
match self.entries.iter()
.try_for_each(|ent|
{
match_stack(&mut result, &ent.numbering);
result.push_str("<li>");
match ent.content.iter().enumerate()
.try_for_each(|(_idx, elem)| {
match elem.compile(compiler, document) {
Err(e) => Err(e),
Ok(s) => { result.push_str(s.as_str()); Ok(()) }
}
})
{
Err(e) => Err(e),
_ => {
result.push_str("</li>");
Ok(()) Ok(())
} }
}
})
{
Err(e) => return Err(e),
_ => {}
}
match_stack(&mut result, &Vec::<(bool, usize)>::new());
Ok(result)
}
Target::LATEX => Err("Unimplemented compiler".to_string())
}
}
} }
/* pub struct ListRule {
impl Element for ListEntry
{
fn location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { ElemKind::Inline }
fn element_name(&self) -> &'static str { "List" }
fn to_string(&self) -> String { format!("{self:#?}") }
fn compile(&self, compiler: &Compiler) -> Result<String, String> {
lazy_static! {
static ref STATE_NAME : &'static str = "list.state";
static ref LIST_OPEN : [&'static str; 2] = ["<ul>", "<ol>"];
static ref LIST_CLOSE : [&'static str; 2] = ["</ul>", "</ol>"];
}
// TODO: State.shouldpreserve?
// Called upon every element
//let state = compiler.get_state_mut::<ListState, _>(*STATE_NAME)
//.or_else(|| {
// compiler.insert_state(STATE_NAME.to_string(), Box::new(ListState(Vec::new())) as Box<dyn Any>);
// compiler.get_state_mut::<ListState, _>(*STATE_NAME)
//}).unwrap();
match compiler.target()
{
Target::HTML => {
let mut result = String::new();
//TODO: Do something about indexing
//&self.numbering.iter()
// .zip(&state.0)
// .for_each(|((wants_numbered, _), is_numbered)|
// {
//
// });
result.push_str("<li>");
match self.content.iter()
.try_for_each(|ent| match ent.compile(compiler) {
Err(e) => Err(e),
Ok(s) => Ok(result.push_str(s.as_str())),
})
{
Err(e) => return Err(e),
_ => {}
}
result.push_str("</li>");
//result.push_str(LIST_OPEN[self.numbered as usize]);
//self.entries.iter()
// .for_each(|(_index, entry)|
// result.push_str(format!("<li>{}</li>", compiler.compile(entry)).as_str()));
//result.push_str(LIST_CLOSE[self.numbered as usize]);
Ok(result)
}
Target::LATEX => Err("Unimplemented compiler".to_string())
}
}
}
*/
pub struct ListRule
{
start_re: Regex, start_re: Regex,
continue_re: Regex continue_re: Regex,
properties: PropertyParser,
} }
impl ListRule { impl ListRule {
pub fn new() -> Self { pub fn new() -> Self {
let mut props = HashMap::new();
props.insert(
"offset".to_string(),
Property::new(false, "Entry numbering offset".to_string(), None),
);
props.insert(
"bullet".to_string(),
Property::new(false, "Entry bullet".to_string(), None),
);
Self { Self {
start_re: Regex::new(r"(?:^|\n)(?:[^\S\r\n]+)([*-]+).*").unwrap(), start_re: Regex::new(r"(?:^|\n)(?:[^\S\r\n]+)([*-]+)(?:\[((?:\\.|[^\\\\])*?)\])?(.*)")
continue_re: Regex::new(r"(?:^|\n)([^\S\r\n]+).*").unwrap(), .unwrap(),
continue_re: Regex::new(r"(?:^|\n)([^\S\r\n]+)([^\s].*)").unwrap(),
properties: PropertyParser { properties: props },
}
} }
fn push_markers(
token: &Token,
parser: &dyn Parser,
document: &dyn Document,
current: &Vec<(bool, usize)>,
target: &Vec<(bool, usize)>,
) {
let mut start_pos = 0;
for i in 0..std::cmp::min(target.len(), current.len()) {
if current[i].0 != target[i].0 {
break;
} }
fn parse_depth(depth: &str, document: &dyn Document) -> Vec<(bool, usize)> start_pos += 1;
{ }
// Close
for i in start_pos..current.len() {
parser.push(
document,
Box::new(ListMarker {
location: token.clone(),
kind: MarkerKind::Close,
numbered: current[current.len() - 1 - (i - start_pos)].0,
}),
);
}
// Open
for i in start_pos..target.len() {
parser.push(
document,
Box::new(ListMarker {
location: token.clone(),
kind: MarkerKind::Open,
numbered: target[i].0,
}),
);
}
}
fn parse_properties(&self, m: Match) -> Result<(Option<usize>, Option<String>), String> {
let processed = process_escaped('\\', "]", m.as_str());
let pm = self.properties.parse(processed.as_str())?;
let offset = match pm.get("offset", |_, s| s.parse::<usize>()) {
Ok((prop, val)) => Some(val),
Err(err) => match err {
PropertyMapError::ParseError(err) => {
return Err(format!("Failed to parse `offset`: {err}"))
}
PropertyMapError::NotFoundError(err) => None,
},
};
let bullet = pm
.get("bullet", |_, s| -> Result<String, ()> { Ok(s.to_string()) })
.map(|(_, s)| s)
.ok();
Ok((offset, bullet))
}
fn parse_depth(depth: &str, document: &dyn Document, offset: usize) -> Vec<(bool, usize)> {
let mut parsed = vec![]; let mut parsed = vec![];
// FIXME: Previous iteration used to recursively retrieve the list indent // FIXME: Previous iteration used to recursively retrieve the list indent
let prev_entry = document.last_element::<List>() let prev_entry = document
.and_then(|list| Ref::filter_map(list, |m| m.entries.last() ).ok() ) .last_element::<ListEntry>()
.and_then(|entry| Ref::filter_map(entry, |e| Some(&e.numbering)).ok() ); .and_then(|entry| Ref::filter_map(entry, |e| Some(&e.numbering)).ok());
let mut continue_match = true; let mut continue_match = true;
depth.chars().enumerate().for_each(|(idx, c)| depth.chars().enumerate().for_each(|(idx, c)| {
{ let number = if offset == 0 {
let number = prev_entry.as_ref() prev_entry
.as_ref()
.and_then(|v| { .and_then(|v| {
if !continue_match { return None } if !continue_match {
return None;
}
let numbered = c == '-'; let numbered = c == '-';
match v.get(idx) match v.get(idx) {
{
None => None, None => None,
Some((prev_numbered, prev_idx)) => { Some((prev_numbered, prev_idx)) => {
if *prev_numbered != numbered { continue_match = false; None } // New depth if *prev_numbered != numbered {
else if idx+1 == v.len() { Some(prev_idx+1) } // Increase from previous continue_match = false;
else { Some(*prev_idx) } // Do nothing None
}
// New depth
else if idx + 1 == v.len() {
Some(prev_idx + 1)
}
// Increase from previous
else {
Some(*prev_idx)
} // Do nothing
} }
} }
}) })
.or(Some(0usize)) .unwrap_or(1)
.unwrap(); } else {
offset
};
match c match c {
{
'*' => parsed.push((false, number)), '*' => parsed.push((false, number)),
'-' => parsed.push((true, number)), '-' => parsed.push((true, number)),
_ => panic!("Unimplemented") _ => panic!("Unimplemented"),
} }
}); });
@ -237,101 +253,186 @@ impl ListRule {
} }
} }
impl Rule for ListRule impl Rule for ListRule {
{
fn name(&self) -> &'static str { "List" } fn name(&self) -> &'static str { "List" }
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.start_re.find_at(cursor.source.content(), cursor.pos) self.start_re
.map_or(None, .find_at(cursor.source.content(), cursor.pos)
|m| Some((m.start(), Box::new([false;0]) as Box<dyn Any>)) ) .map_or(None, |m| {
Some((m.start(), Box::new([false; 0]) as Box<dyn Any>))
})
} }
fn on_match<'a>(&self, parser: &dyn Parser, document: &'a dyn Document<'a>, cursor: Cursor, _match_data: Option<Box<dyn Any>>) fn on_match<'a>(
-> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { &self,
parser: &dyn Parser,
document: &'a dyn Document<'a>,
cursor: Cursor,
_match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let mut reports = vec![]; let mut reports = vec![];
let content = cursor.source.content(); let content = cursor.source.content();
let (end_cursor, numbering, source) = match self.start_re.captures_at(content, cursor.pos) { let mut end_cursor = cursor.clone();
None => panic!("Unknown error"),
Some(caps) => {
let mut end_pos = caps.get(0).unwrap().end();
let mut spacing = None; // Spacing used to continue list entry
loop { loop {
// If another entry starts on the next line, don't continue matching if let Some(captures) = self.start_re.captures_at(content, end_cursor.pos) {
match self.next_match(&cursor.at(end_pos)) if captures.get(0).unwrap().start() != end_cursor.pos {
{ break;
Some((pos, _)) => {
if pos == end_pos { break }
}
None => {},
} }
// Advance cursor
end_cursor = end_cursor.at(captures.get(0).unwrap().end());
// Continue matching as current entry // Properties
match self.continue_re.captures_at(content, end_pos) { let mut offset = None;
None => break, let mut bullet = None;
Some(continue_caps) => { if let Some(properties) = captures.get(2) {
if continue_caps.get(0).unwrap().start() != end_pos { break } match self.parse_properties(properties) {
Err(err) => {
// Get the spacing
let cap_spacing = continue_caps.get(1).unwrap();
match &spacing {
None => spacing = Some(cap_spacing.range()),
Some(spacing) => 'some: {
if content[cap_spacing.range()] == content[spacing.clone()] { break 'some }
reports.push( reports.push(
Report::build(ReportKind::Warning, cursor.source.clone(), continue_caps.get(1).unwrap().start()) Report::build(
ReportKind::Warning,
cursor.source.clone(),
properties.start(),
)
.with_message("Invalid List Entry Properties")
.with_label(
Label::new((cursor.source.clone(), properties.range()))
.with_message(err)
.with_color(parser.colors().warning),
)
.finish(),
);
break;
}
Ok(props) => (offset, bullet) = props,
}
}
// Get bullet from previous entry if it exists
if bullet.is_none() {
bullet = document
.last_element::<ListEntry>()
.and_then(|prev| prev.bullet.clone())
}
// Depth
let depth = ListRule::parse_depth(
captures.get(1).unwrap().as_str(),
document,
offset.unwrap_or(0),
);
// Content
let entry_start = captures.get(0).unwrap().start();
let mut entry_content = captures.get(3).unwrap().as_str().to_string();
let mut spacing: Option<(Range<usize>, &str)> = None;
while let Some(captures) = self.continue_re.captures_at(content, end_cursor.pos) {
// Break if next element is another entry
if captures.get(0).unwrap().start() != end_cursor.pos
|| captures
.get(2)
.unwrap()
.as_str()
.find(|c| c == '*' || c == '-')
== Some(0)
{
break;
}
// Advance cursor
end_cursor = end_cursor.at(captures.get(0).unwrap().end());
// Spacing
let current_spacing = captures.get(1).unwrap().as_str();
if let Some(spacing) = &spacing {
if spacing.1 != current_spacing {
reports.push(
Report::build(
ReportKind::Warning,
cursor.source.clone(),
captures.get(1).unwrap().start(),
)
.with_message("Invalid list entry spacing") .with_message("Invalid list entry spacing")
.with_label( .with_label(
Label::new((cursor.source.clone(), cap_spacing.range())) Label::new((
.with_message("Spacing for list entries must match") cursor.source.clone(),
.with_color(parser.colors().warning)) captures.get(1).unwrap().range(),
))
.with_message("Spacing for list entries do not match")
.with_color(parser.colors().warning),
)
.with_label( .with_label(
Label::new((cursor.source.clone(), spacing.clone())) Label::new((cursor.source.clone(), spacing.0.clone()))
.with_message("Previous spacing") .with_message("Previous spacing")
.with_color(parser.colors().warning)) .with_color(parser.colors().warning),
.finish()); )
}, .finish(),
} );
end_pos = continue_caps.get(0).unwrap().end();
}
} }
} else {
spacing = Some((captures.get(1).unwrap().range(), current_spacing));
} }
let start_pos = caps.get(1).unwrap().end(); entry_content += " ";
let source = VirtualSource::new( entry_content += captures.get(2).unwrap().as_str();
Token::new(start_pos..end_pos, cursor.source.clone()), }
// Parse entry content
let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone());
let entry_src = Rc::new(VirtualSource::new(
token.clone(),
"List Entry".to_string(), "List Entry".to_string(),
content.as_str()[start_pos..end_pos].to_string(), entry_content,
));
let parsed_content = match util::parse_paragraph(parser, entry_src, document) {
Err(err) => {
reports.push(
Report::build(ReportKind::Warning, token.source(), token.range.start)
.with_message("Unable to Parse List Entry")
.with_label(
Label::new((token.source(), token.range.clone()))
.with_message(err)
.with_color(parser.colors().warning),
)
.finish(),
); );
break;
(cursor.at(end_pos),
ListRule::parse_depth(caps.get(1).unwrap().as_str(), document),
source)
},
};
let parsed_entry = parser.parse(Rc::new(source), Some(document));
let mut parsed_paragraph = parsed_entry.last_element_mut::<Paragraph>().unwrap(); // Extract content from paragraph
let entry = ListEntry::new(
Token::new(cursor.pos..end_cursor.pos, cursor.source.clone()),
numbering,
std::mem::replace(&mut parsed_paragraph.content, Vec::new())
);
// Ger previous list, if none insert a new list
let mut list = match document.last_element_mut::<List>()
{
Some(last) => last,
None => {
parser.push(document,
Box::new(List::new(
Token::new(cursor.pos..end_cursor.pos, cursor.source.clone()))));
document.last_element_mut::<List>().unwrap()
} }
Ok(mut paragraph) => std::mem::replace(&mut paragraph.content, vec![]),
}; };
list.push(entry);
if let Some(previous_depth) = document
.last_element::<ListEntry>()
.map(|ent| ent.numbering.clone())
{
ListRule::push_markers(&token, parser, document, &previous_depth, &depth);
} else {
ListRule::push_markers(&token, parser, document, &vec![], &depth);
}
parser.push(
document,
Box::new(ListEntry {
location: Token::new(
entry_start..end_cursor.pos,
end_cursor.source.clone(),
),
numbering: depth,
content: parsed_content,
bullet,
}),
);
} else {
break;
}
}
// Close all lists
let current = document
.last_element::<ListEntry>()
.map(|ent| ent.numbering.clone())
.unwrap();
let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone());
ListRule::push_markers(&token, parser, document, &current, &Vec::new());
(end_cursor, reports) (end_cursor, reports)
} }
@ -339,3 +440,78 @@ impl Rule for ListRule
// TODO // TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None } fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::compiler::compiler::Target;
use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text;
use crate::parser::langparser::LangParser;
use crate::parser::source::SourceFile;
use crate::validate_document;
#[test]
fn parser() {
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
* 1
*[offset=7] 2
continued
* 3
* New list
*-[bullet=(*)] A
*- B
* Back
*-* More nested
"#
.to_string(),
None,
));
let parser = LangParser::default();
let compiler = Compiler::new(Target::HTML, None);
let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0,
ListMarker { numbered == false, kind == MarkerKind::Open };
ListEntry { numbering == vec![(false, 1)] } {
Text { content == "1" };
};
ListEntry { numbering == vec![(false, 7)] } {
Text { /*content == "2 continued"*/ };
};
ListEntry { numbering == vec![(false, 8)] } {
Text { content == "3" };
};
ListMarker { numbered == false, kind == MarkerKind::Close };
Paragraph;
ListMarker { numbered == false, kind == MarkerKind::Open };
ListEntry { numbering == vec![(false, 1)] } {
Text { content == "New list" };
};
ListMarker { numbered == true, kind == MarkerKind::Open };
ListEntry { numbering == vec![(false, 2), (true, 1)], bullet == Some("(*)".to_string()) } {
Text { content == "A" };
};
ListEntry { numbering == vec![(false, 2), (true, 2)], bullet == Some("(*)".to_string()) } {
Text { content == "B" };
};
ListMarker { numbered == true, kind == MarkerKind::Close };
ListEntry { numbering == vec![(false, 2)] } {
Text { content == "Back" };
};
ListMarker { numbered == true, kind == MarkerKind::Open };
ListMarker { numbered == false, kind == MarkerKind::Open };
ListEntry { numbering == vec![(false, 3), (true, 1), (false, 1)] } {
Text { content == "More nested" };
};
ListMarker { numbered == false, kind == MarkerKind::Close };
ListMarker { numbered == true, kind == MarkerKind::Close };
ListMarker { numbered == false, kind == MarkerKind::Close };
);
}
}

View file

@ -67,7 +67,7 @@ impl RawRule {
Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?") Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?")
.unwrap(), .unwrap(),
], ],
properties: PropertyParser{ properties: props }, properties: PropertyParser { properties: props },
} }
} }
} }
@ -268,15 +268,18 @@ impl RegexRule for RawRule {
mod tests { mod tests {
use super::*; use super::*;
use crate::compiler::compiler::Target; use crate::compiler::compiler::Target;
use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document;
#[test] #[test]
fn raw_tests() { fn parser() {
let source = Rc::new(SourceFile::with_content( let source = Rc::new(SourceFile::with_content(
"".to_string(), "".to_string(),
r#" r#"
Break{?[kind=block]<RAW>?}NewParagraph Break{?[kind=block] Raw?}NewParagraph{?<b>?}
"# "#
.to_string(), .to_string(),
None, None,
@ -285,13 +288,13 @@ Break{?[kind=block]<RAW>?}NewParagraph
let compiler = Compiler::new(Target::HTML, None); let compiler = Compiler::new(Target::HTML, None);
let doc = parser.parse(source, None); let doc = parser.parse(source, None);
let borrow = doc.content().borrow(); validate_document!(doc.content().borrow(), 0,
let found = borrow Paragraph;
.iter() Raw { kind == ElemKind::Block, content == "Raw" };
.filter_map(|e| e.downcast_ref::<Raw>()) Paragraph {
.collect::<Vec<_>>(); Text;
Raw { kind == ElemKind::Inline, content == "<b>" };
assert_eq!(found[0].compile(&compiler, &*doc), Ok("<RAW>".to_string())); };
//assert_eq!(found[1].compile(&compiler, &*doc), Ok("<RAW>".to_string())); );
} }
} }