Layout refactor & bindings
This commit is contained in:
parent
cc7bc7169c
commit
67ea2f9411
11 changed files with 533 additions and 105 deletions
|
@ -216,7 +216,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn sort() {
|
||||
let mut entries: Vec<(String, String, Option<String>)> = vec![
|
||||
let entries: Vec<(String, String, Option<String>)> = vec![
|
||||
("Index".into(), "".into(), None),
|
||||
("AB".into(), "".into(), Some("Index".into())),
|
||||
("Getting Started".into(), "".into(), Some("Index".into())),
|
||||
|
|
49
src/document/layout.rs
Normal file
49
src/document/layout.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use std::any::Any;
|
||||
use std::cell::Ref;
|
||||
use std::cell::RefMut;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::compiler::compiler::Compiler;
|
||||
use crate::elements::layout::LayoutToken;
|
||||
|
||||
use super::document::Document;
|
||||
|
||||
/// Represents the type of a layout
|
||||
pub trait LayoutType: core::fmt::Debug {
|
||||
/// Name of the layout
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Parses layout properties
|
||||
fn parse_properties(&self, properties: &str) -> Result<Option<Box<dyn Any>>, String>;
|
||||
|
||||
/// Expected number of blocks
|
||||
fn expects(&self) -> Range<usize>;
|
||||
|
||||
/// Compile layout
|
||||
fn compile(
|
||||
&self,
|
||||
token: LayoutToken,
|
||||
id: usize,
|
||||
properties: &Option<Box<dyn Any>>,
|
||||
compiler: &Compiler,
|
||||
document: &dyn Document,
|
||||
) -> Result<String, String>;
|
||||
}
|
||||
|
||||
pub trait LayoutHolder {
|
||||
/// gets a reference to all defined layouts
|
||||
fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn LayoutType>>>;
|
||||
|
||||
/// gets a (mutable) reference to all defined layours
|
||||
fn layouts_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn LayoutType>>>;
|
||||
|
||||
fn get_layout(&self, layout_name: &str) -> Option<Rc<dyn LayoutType>> {
|
||||
self.layouts().get(layout_name).map(|layout| layout.clone())
|
||||
}
|
||||
|
||||
fn insert_layout(&self, layout: Rc<dyn LayoutType>) {
|
||||
self.layouts_mut().insert(layout.name().into(), layout);
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@ pub mod langdocument;
|
|||
pub mod element;
|
||||
pub mod variable;
|
||||
pub mod style;
|
||||
pub mod layout;
|
||||
|
|
|
@ -3,6 +3,8 @@ use crate::compiler::compiler::Target;
|
|||
use crate::document::document::Document;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::document::layout::LayoutType;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
|
@ -16,6 +18,7 @@ use ariadne::Label;
|
|||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use lazy_static::lazy_static;
|
||||
use mlua::Error::BadArgument;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
|
@ -27,6 +30,8 @@ use std::cell::RefCell;
|
|||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum LayoutToken {
|
||||
|
@ -35,31 +40,20 @@ pub(crate) enum LayoutToken {
|
|||
End,
|
||||
}
|
||||
|
||||
/// Represents the type of a layout
|
||||
pub trait LayoutType: core::fmt::Debug {
|
||||
/// Name of the layout
|
||||
fn name(&self) -> &'static str;
|
||||
impl FromStr for LayoutToken {
|
||||
type Err = String;
|
||||
|
||||
/// Parses layout properties
|
||||
fn parse_properties(&self, properties: &str) -> Result<Option<Box<dyn Any>>, String>;
|
||||
|
||||
/// Expected number of blocks
|
||||
fn expects(&self) -> Range<usize>;
|
||||
|
||||
/// Compile layout
|
||||
fn compile(
|
||||
&self,
|
||||
token: LayoutToken,
|
||||
id: usize,
|
||||
properties: &Option<Box<dyn Any>>,
|
||||
compiler: &Compiler,
|
||||
document: &dyn Document,
|
||||
) -> Result<String, String>;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"Begin" | "begin" => Ok(LayoutToken::Begin),
|
||||
"Next" | "next" => Ok(LayoutToken::Next),
|
||||
"End" | "end" => Ok(LayoutToken::End),
|
||||
_ => Err(format!("Unable to find LayoutToken with name: {s}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod default_layouts {
|
||||
use std::any::Any;
|
||||
|
||||
use crate::parser::util::Property;
|
||||
use crate::parser::util::PropertyParser;
|
||||
|
||||
|
@ -291,19 +285,10 @@ impl State for LayoutState {
|
|||
|
||||
pub struct LayoutRule {
|
||||
re: [Regex; 3],
|
||||
layouts: HashMap<String, Rc<dyn LayoutType>>,
|
||||
}
|
||||
|
||||
impl LayoutRule {
|
||||
pub fn new() -> Self {
|
||||
let mut layouts: HashMap<String, Rc<dyn LayoutType>> = HashMap::new();
|
||||
|
||||
let layout_centered = default_layouts::Centered::default();
|
||||
layouts.insert(layout_centered.name().to_string(), Rc::new(layout_centered));
|
||||
|
||||
let layout_split = default_layouts::Split::default();
|
||||
layouts.insert(layout_split.name().to_string(), Rc::new(layout_split));
|
||||
|
||||
Self {
|
||||
re: [
|
||||
RegexBuilder::new(
|
||||
|
@ -325,7 +310,23 @@ impl LayoutRule {
|
|||
.build()
|
||||
.unwrap(),
|
||||
],
|
||||
layouts,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize_state(parser: &dyn Parser) -> Rc<RefCell<dyn State>> {
|
||||
let query = parser.state().query(&STATE_NAME);
|
||||
match query {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
// Insert as a new state
|
||||
match parser.state_mut().insert(
|
||||
STATE_NAME.clone(),
|
||||
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
|
||||
) {
|
||||
Err(_) => panic!("Unknown error"),
|
||||
Ok(state) => state,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,20 +392,7 @@ impl RegexRule for LayoutRule {
|
|||
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut reports = vec![];
|
||||
|
||||
let query = parser.state().query(&STATE_NAME);
|
||||
let state = match query {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
// Insert as a new state
|
||||
match parser.state_mut().insert(
|
||||
STATE_NAME.clone(),
|
||||
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
|
||||
) {
|
||||
Err(_) => panic!("Unknown error"),
|
||||
Ok(state) => state,
|
||||
}
|
||||
}
|
||||
};
|
||||
let state = LayoutRule::initialize_state(parser);
|
||||
|
||||
if index == 0
|
||||
// BEGIN_LAYOUT
|
||||
|
@ -465,7 +453,7 @@ impl RegexRule for LayoutRule {
|
|||
}
|
||||
|
||||
// Get layout
|
||||
let layout_type = match self.layouts.get(trimmed) {
|
||||
let layout_type = match parser.get_layout(trimmed) {
|
||||
None => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), name.start())
|
||||
|
@ -663,7 +651,212 @@ impl RegexRule for LayoutRule {
|
|||
}
|
||||
|
||||
// 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>)>> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
"push".to_string(),
|
||||
lua.create_function(
|
||||
|_, (token, layout, properties): (String, String, String)| {
|
||||
let mut result = Ok(());
|
||||
|
||||
// Parse token
|
||||
let layout_token = match LayoutToken::from_str(token.as_str())
|
||||
{
|
||||
Err(err) => {
|
||||
return Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 1,
|
||||
name: Some("token".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(err))
|
||||
});
|
||||
},
|
||||
Ok(token) => token,
|
||||
};
|
||||
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
// Make sure the state has been initialized
|
||||
let state = LayoutRule::initialize_state(ctx.parser);
|
||||
|
||||
// Get layout
|
||||
let layout_type = match ctx.parser.get_layout(layout.as_str())
|
||||
{
|
||||
None => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 2,
|
||||
name: Some("layout".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!(
|
||||
"Cannot find layout with name `{layout}`"
|
||||
))),
|
||||
});
|
||||
return;
|
||||
},
|
||||
Some(layout) => layout,
|
||||
};
|
||||
|
||||
// Parse properties
|
||||
let layout_properties = match layout_type.parse_properties(properties.as_str()) {
|
||||
Err(err) => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 3,
|
||||
name: Some("properties".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(err)),
|
||||
});
|
||||
return;
|
||||
},
|
||||
Ok(properties) => properties,
|
||||
};
|
||||
|
||||
let id = match layout_token {
|
||||
LayoutToken::Begin => {
|
||||
ctx.parser.push(
|
||||
ctx.document,
|
||||
Box::new(Layout {
|
||||
location: ctx.location.clone(),
|
||||
layout: layout_type.clone(),
|
||||
id: 0,
|
||||
token: LayoutToken::Begin,
|
||||
properties: layout_properties,
|
||||
}),
|
||||
);
|
||||
|
||||
state
|
||||
.borrow_mut()
|
||||
.downcast_mut::<LayoutState>()
|
||||
.map_or_else(
|
||||
|| panic!("Invalid state at: `{}`", STATE_NAME.as_str()),
|
||||
|s| s.stack.push((vec![ctx.location.clone()], layout_type.clone())),
|
||||
);
|
||||
return;
|
||||
},
|
||||
LayoutToken::Next => {
|
||||
let mut state_borrow = state.borrow_mut();
|
||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
|
||||
let (tokens, current_layout_type) = match state.stack.last_mut() {
|
||||
None => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 1,
|
||||
name: Some("token".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!("Unable set next layout: No active layout found"))),
|
||||
});
|
||||
return;
|
||||
}
|
||||
Some(last) => last,
|
||||
};
|
||||
|
||||
if !Rc::ptr_eq(&layout_type, current_layout_type) {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 2,
|
||||
name: Some("layout".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!("Invalid layout next, current layout is {} vs {}",
|
||||
current_layout_type.name(),
|
||||
layout_type.name())))
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if layout_type.expects().end < tokens.len()
|
||||
// Too many blocks
|
||||
{
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 1,
|
||||
name: Some("token".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!("Unable set layout next: layout {} expect at most {} blocks, currently at {} blocks",
|
||||
layout_type.name(),
|
||||
layout_type.expects().end,
|
||||
tokens.len()
|
||||
))),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
tokens.push(ctx.location.clone());
|
||||
tokens.len() - 1
|
||||
},
|
||||
LayoutToken::End => {
|
||||
let mut state_borrow = state.borrow_mut();
|
||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
|
||||
let (tokens, current_layout_type) = match state.stack.last_mut() {
|
||||
None => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 1,
|
||||
name: Some("token".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!("Unable set layout end: No active layout found"))),
|
||||
});
|
||||
return;
|
||||
}
|
||||
Some(last) => last,
|
||||
};
|
||||
|
||||
if !Rc::ptr_eq(&layout_type, current_layout_type) {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 2,
|
||||
name: Some("layout".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!("Invalid layout end, current layout is {} vs {}",
|
||||
current_layout_type.name(),
|
||||
layout_type.name())))
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if layout_type.expects().start > tokens.len()
|
||||
// Not enough blocks
|
||||
{
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 1,
|
||||
name: Some("token".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!("Unable set next layout: layout {} expect at least {} blocks, currently at {} blocks",
|
||||
layout_type.name(),
|
||||
layout_type.expects().start,
|
||||
tokens.len()
|
||||
))),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let id = tokens.len();
|
||||
state.stack.pop();
|
||||
id
|
||||
}
|
||||
};
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.document,
|
||||
Box::new(Layout {
|
||||
location: ctx.location.clone(),
|
||||
layout: layout_type.clone(),
|
||||
id,
|
||||
token: layout_token,
|
||||
properties: layout_properties,
|
||||
}),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
result
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
}
|
||||
|
||||
fn register_layouts(&self, parser: &dyn Parser) {
|
||||
parser.insert_layout(Rc::new(default_layouts::Centered::default()));
|
||||
parser.insert_layout(Rc::new(default_layouts::Split::default()));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -727,4 +920,56 @@ mod tests {
|
|||
Layout { token == LayoutToken::End, id == 2 };
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua() {
|
||||
let source = Rc::new(SourceFile::with_content(
|
||||
"".to_string(),
|
||||
r#"
|
||||
%<nml.layout.push("begin", "Split", "style=A")>%
|
||||
A
|
||||
%<nml.layout.push("Begin", "Centered", "style=B")>%
|
||||
B
|
||||
%<nml.layout.push("end", "Centered", "")>%
|
||||
%<nml.layout.push("next", "Split", "style=C")>%
|
||||
C
|
||||
%<nml.layout.push("Begin", "Split", "style=D")>%
|
||||
D
|
||||
%<nml.layout.push("Next", "Split", "style=E")>%
|
||||
E
|
||||
%<nml.layout.push("End", "Split", "")>%
|
||||
%<nml.layout.push("End", "Split", "")>%
|
||||
"#
|
||||
.to_string(),
|
||||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Layout { token == LayoutToken::Begin, id == 0 };
|
||||
Paragraph {
|
||||
Text { content == "A" };
|
||||
};
|
||||
Layout { token == LayoutToken::Begin, id == 0 };
|
||||
Paragraph {
|
||||
Text { content == "B" };
|
||||
};
|
||||
Layout { token == LayoutToken::End, id == 1 };
|
||||
Layout { token == LayoutToken::Next, id == 1 };
|
||||
Paragraph {
|
||||
Text { content == "C" };
|
||||
};
|
||||
Layout { token == LayoutToken::Begin, id == 0 };
|
||||
Paragraph {
|
||||
Text { content == "D" };
|
||||
};
|
||||
Layout { token == LayoutToken::Next, id == 1 };
|
||||
Paragraph {
|
||||
Text { content == "E" };
|
||||
};
|
||||
Layout { token == LayoutToken::End, id == 2 };
|
||||
Layout { token == LayoutToken::End, id == 2 };
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::document::document::Document;
|
|||
use crate::document::element::ContainerElement;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -14,12 +15,14 @@ use ariadne::Fmt;
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Error::BadArgument;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Link {
|
||||
|
@ -205,8 +208,56 @@ impl RegexRule for LinkRule {
|
|||
return reports;
|
||||
}
|
||||
|
||||
// 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>)>> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
"push".to_string(),
|
||||
lua.create_function(|_, (display, url): (String, String)| {
|
||||
let mut result = Ok(());
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
let source = Rc::new(VirtualSource::new(
|
||||
ctx.location.clone(),
|
||||
"Link Display".to_string(),
|
||||
display,
|
||||
));
|
||||
let display_content =
|
||||
match util::parse_paragraph(ctx.parser, source, ctx.document) {
|
||||
Err(err) => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
pos: 1,
|
||||
name: Some("display".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!(
|
||||
"Failed to parse link display: {err}"
|
||||
))),
|
||||
});
|
||||
return;
|
||||
}
|
||||
Ok(mut paragraph) => {
|
||||
std::mem::replace(&mut paragraph.content, vec![])
|
||||
}
|
||||
};
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.document,
|
||||
Box::new(Link {
|
||||
location: ctx.location.clone(),
|
||||
display: display_content,
|
||||
url,
|
||||
}),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
result
|
||||
})
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -247,4 +298,34 @@ Some [link](url).
|
|||
};
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua() {
|
||||
let source = Rc::new(SourceFile::with_content(
|
||||
"".to_string(),
|
||||
r#"
|
||||
Some %<nml.link.push("link", "url")>%.
|
||||
%<
|
||||
nml.link.push("**BOLD link**", "another url")
|
||||
>%
|
||||
"#
|
||||
.to_string(),
|
||||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
Text { content == "Some " };
|
||||
Link { url == "url" } { Text { content == "link" }; };
|
||||
Text { content == "." };
|
||||
Link { url == "another url" } {
|
||||
Style;
|
||||
Text { content == "BOLD link" };
|
||||
Style;
|
||||
};
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -283,7 +283,6 @@ Break{?[kind=block] Raw?}NewParagraph{?<b>?}
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let compiler = Compiler::new(Target::HTML, None);
|
||||
let doc = parser.parse(source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
|
@ -295,4 +294,27 @@ Break{?[kind=block] Raw?}NewParagraph{?<b>?}
|
|||
};
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua() {
|
||||
let source = Rc::new(SourceFile::with_content(
|
||||
"".to_string(),
|
||||
r#"
|
||||
Break%<nml.raw.push("block", "Raw")>%NewParagraph%<nml.raw.push("inline", "<b>")>%
|
||||
"#
|
||||
.to_string(),
|
||||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph;
|
||||
Raw { kind == ElemKind::Block, content == "Raw" };
|
||||
Paragraph {
|
||||
Text;
|
||||
Raw { kind == ElemKind::Inline, content == "<b>" };
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,8 +50,7 @@ impl Element for Section {
|
|||
let numbering = compiler.section_counter(self.depth);
|
||||
|
||||
let mut result = String::new();
|
||||
for num in numbering.iter()
|
||||
{
|
||||
for num in numbering.iter() {
|
||||
result = result + num.to_string().as_str() + ".";
|
||||
}
|
||||
result += " ";
|
||||
|
@ -409,18 +408,18 @@ mod section_style {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
use crate::{parser::{langparser::LangParser, source::SourceFile}, validate_document};
|
||||
mod tests {
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parser()
|
||||
{
|
||||
fn parser() {
|
||||
let source = Rc::new(SourceFile::with_content(
|
||||
"".to_string(),
|
||||
r#"
|
||||
"".to_string(),
|
||||
r#"
|
||||
# 1
|
||||
##+ 2
|
||||
###* 3
|
||||
|
@ -428,8 +427,38 @@ use super::*;
|
|||
#####*+ 5
|
||||
######{refname} 6
|
||||
"#
|
||||
.to_string(),
|
||||
None,
|
||||
.to_string(),
|
||||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Section { depth == 1, title == "1" };
|
||||
Section { depth == 2, title == "2", kind == section_kind::NO_TOC };
|
||||
Section { depth == 3, title == "3", kind == section_kind::NO_NUMBER };
|
||||
Section { depth == 4, title == "4", kind == section_kind::NO_NUMBER | section_kind::NO_TOC };
|
||||
Section { depth == 5, title == "5", kind == section_kind::NO_NUMBER | section_kind::NO_TOC };
|
||||
Section { depth == 6, title == "6", reference == Some("refname".to_string()) };
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua() {
|
||||
let source = Rc::new(SourceFile::with_content(
|
||||
"".to_string(),
|
||||
r#"
|
||||
%<
|
||||
nml.section.push("1", 1, "", nil)
|
||||
nml.section.push("2", 2, "+", nil)
|
||||
nml.section.push("3", 3, "*", nil)
|
||||
nml.section.push("4", 4, "+*", nil)
|
||||
nml.section.push("5", 5, "*+", nil)
|
||||
nml.section.push("6", 6, "", "refname")
|
||||
>%
|
||||
"#
|
||||
.to_string(),
|
||||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{cell::{RefCell, RefMut}, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::{document::{document::Document, element::Element, style::{ElementStyle, StyleHolder}}, lua::kernel::{Kernel, KernelHolder}, parser::{parser::{Parser, ReportColors}, rule::Rule, source::{Cursor, Source}, state::StateHolder}};
|
||||
use crate::{document::{document::Document, element::Element, layout::{LayoutHolder, LayoutType}, style::{ElementStyle, StyleHolder}}, lua::kernel::{Kernel, KernelHolder}, parser::{parser::{Parser, ReportColors}, rule::Rule, source::{Cursor, Source}, state::StateHolder}};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LineCursor
|
||||
|
@ -156,3 +156,13 @@ impl StyleHolder for LsParser {
|
|||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHolder for LsParser {
|
||||
fn layouts(&self) -> std::cell::Ref<'_, HashMap<String, Rc<dyn LayoutType>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn layouts_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn LayoutType>>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ use crate::document::element::DocumentEnd;
|
|||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::document::langdocument::LangDocument;
|
||||
use crate::document::layout::LayoutHolder;
|
||||
use crate::document::layout::LayoutType;
|
||||
use crate::document::style::ElementStyle;
|
||||
use crate::document::style::StyleHolder;
|
||||
use crate::elements::paragraph::Paragraph;
|
||||
|
@ -46,6 +48,7 @@ pub struct LangParser {
|
|||
pub state: RefCell<StateHolder>,
|
||||
pub kernels: RefCell<HashMap<String, Kernel>>,
|
||||
pub styles: RefCell<HashMap<String, Rc<dyn ElementStyle>>>,
|
||||
pub layouts: RefCell<HashMap<String, Rc<dyn LayoutType>>>,
|
||||
}
|
||||
|
||||
impl LangParser {
|
||||
|
@ -57,11 +60,11 @@ impl LangParser {
|
|||
state: RefCell::new(StateHolder::new()),
|
||||
kernels: RefCell::new(HashMap::new()),
|
||||
styles: RefCell::new(HashMap::new()),
|
||||
layouts: RefCell::new(HashMap::new()),
|
||||
};
|
||||
// Register rules
|
||||
register(&mut s);
|
||||
|
||||
|
||||
// Register default kernel
|
||||
s.kernels
|
||||
.borrow_mut()
|
||||
|
@ -71,6 +74,12 @@ impl LangParser {
|
|||
for rule in &s.rules {
|
||||
rule.register_styles(&s);
|
||||
}
|
||||
|
||||
// Register default layouts
|
||||
for rule in &s.rules {
|
||||
rule.register_layouts(&s);
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
|
@ -313,3 +322,15 @@ impl StyleHolder for LangParser {
|
|||
self.styles.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHolder for LangParser {
|
||||
fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn crate::document::layout::LayoutType>>> {
|
||||
self.layouts.borrow()
|
||||
}
|
||||
|
||||
fn layouts_mut(
|
||||
&self,
|
||||
) -> RefMut<'_, HashMap<String, Rc<dyn crate::document::layout::LayoutType>>> {
|
||||
self.layouts.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use super::source::Source;
|
|||
use super::state::StateHolder;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::element::Element;
|
||||
use crate::document::layout::LayoutHolder;
|
||||
use crate::document::style::StyleHolder;
|
||||
use crate::lua::kernel::KernelHolder;
|
||||
use ariadne::Color;
|
||||
|
@ -42,7 +43,7 @@ impl ReportColors {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Parser: KernelHolder + StyleHolder {
|
||||
pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
||||
/// Gets the colors for formatting errors
|
||||
///
|
||||
/// When colors are disabled, all colors should resolve to empty string
|
||||
|
|
|
@ -29,6 +29,9 @@ pub trait Rule {
|
|||
|
||||
/// Registers default styles
|
||||
fn register_styles(&self, _parser: &dyn Parser) {}
|
||||
|
||||
/// Registers default layouts
|
||||
fn register_layouts(&self, _parser: &dyn Parser) {}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for dyn Rule {
|
||||
|
@ -37,45 +40,6 @@ impl core::fmt::Debug for dyn Rule {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub trait RegexRule: Rule
|
||||
{
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Returns the rule's regex
|
||||
fn regex(&self) -> ®ex::Regex;
|
||||
/// Callback on regex rule match
|
||||
fn on_regex_match<'a>(&self, parser: &Parser, document: &Document, token: Token<'a>, matches: regex::Captures) -> Vec<Report<'a, (String, Range<usize>)>>;
|
||||
}
|
||||
|
||||
impl<T: RegexRule> Rule for T {
|
||||
fn name(&self) -> &'static str { RegexRule::name(self) }
|
||||
|
||||
/// Finds the next match starting from [`cursor`]
|
||||
fn next_match<'a>(&self, cursor: &'a Cursor) -> Option<usize>
|
||||
{
|
||||
let re = self.regex();
|
||||
|
||||
let content = cursor.file.content.as_ref().unwrap();
|
||||
match re.find_at(content.as_str(), cursor.pos)
|
||||
{
|
||||
Some(m) => Some(m.start()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_match<'a>(&self, parser: &Parser, document: &Document, cursor: Cursor<'a>) -> (Cursor<'a>, Vec<Report<'a, (String, Range<usize>)>>)
|
||||
{
|
||||
let content = cursor.file.content.as_ref().unwrap();
|
||||
let matches = self.regex().captures_at(content.as_str(), cursor.pos).unwrap();
|
||||
let token = Token::new(cursor.pos, matches.get(0).unwrap().len(), cursor.file);
|
||||
|
||||
let token_end = token.end();
|
||||
(cursor.at(token_end), self.on_regex_match(parser, document, token, matches))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub trait RegexRule {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
|
@ -94,6 +58,7 @@ pub trait RegexRule {
|
|||
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
fn register_styles(&self, _parser: &dyn Parser) {}
|
||||
fn register_layouts(&self, _parser: &dyn Parser) {}
|
||||
}
|
||||
|
||||
impl<T: RegexRule> Rule for T {
|
||||
|
@ -155,4 +120,8 @@ impl<T: RegexRule> Rule for T {
|
|||
fn register_styles(&self, parser: &dyn Parser) {
|
||||
self.register_styles(parser);
|
||||
}
|
||||
|
||||
fn register_layouts(&self, parser: &dyn Parser) {
|
||||
self.register_layouts(parser);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue