Experimental custom styles
This commit is contained in:
parent
67ea2f9411
commit
b5c8fbbfea
21 changed files with 755 additions and 96 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -741,6 +741,7 @@ dependencies = [
|
|||
"lsp-server",
|
||||
"lsp-types 0.97.0",
|
||||
"mlua",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"rusqlite",
|
||||
"rust-crypto",
|
||||
|
|
|
@ -37,3 +37,6 @@ tokio = { version = "1.38.1", features = ["macros", "rt-multi-thread", "io-std"]
|
|||
tower-lsp = "0.20.0"
|
||||
unicode-segmentation = "1.11.0"
|
||||
walkdir = "2.5.0"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
|
|
57
src/document/customstyle.rs
Normal file
57
src/document/customstyle.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use std::cell::Ref;
|
||||
use std::cell::RefMut;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ariadne::Report;
|
||||
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
|
||||
use super::document::Document;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum CustomStyleToken {
|
||||
Toggle(String),
|
||||
Pair(String, String),
|
||||
}
|
||||
|
||||
pub trait CustomStyle: core::fmt::Debug {
|
||||
/// Name for the custom style
|
||||
fn name(&self) -> &str;
|
||||
/// Gets the begin and end token for a custom style
|
||||
fn tokens(&self) -> &CustomStyleToken;
|
||||
|
||||
fn on_start<'a>(
|
||||
&self,
|
||||
location: Token,
|
||||
parser: &dyn Parser,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>>;
|
||||
fn on_end<'a>(
|
||||
&self,
|
||||
location: Token,
|
||||
parser: &dyn Parser,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>>;
|
||||
}
|
||||
|
||||
pub trait CustomStyleHolder {
|
||||
/// gets a reference to all defined custom styles
|
||||
fn custom_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn CustomStyle>>>;
|
||||
|
||||
/// gets a (mutable) reference to all defined custom styles
|
||||
fn custom_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn CustomStyle>>>;
|
||||
|
||||
fn get_custom_style(&self, style_name: &str) -> Option<Rc<dyn CustomStyle>> {
|
||||
self.custom_styles()
|
||||
.get(style_name)
|
||||
.map(|style| style.clone())
|
||||
}
|
||||
|
||||
fn insert_custom_style(&self, style: Rc<dyn CustomStyle>) {
|
||||
self.custom_styles_mut().insert(style.name().into(), style);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ use std::str::FromStr;
|
|||
use crate::compiler::compiler::Compiler;
|
||||
use crate::elements::reference::Reference;
|
||||
use crate::parser::source::Token;
|
||||
use crate::parser::util::PropertyParser;
|
||||
use downcast_rs::impl_downcast;
|
||||
use downcast_rs::Downcast;
|
||||
|
||||
|
|
|
@ -5,3 +5,4 @@ pub mod element;
|
|||
pub mod variable;
|
||||
pub mod style;
|
||||
pub mod layout;
|
||||
pub mod customstyle;
|
||||
|
|
|
@ -18,9 +18,6 @@ pub trait ElementStyle: Downcast + core::fmt::Debug {
|
|||
/// Will fail if deserialization fails
|
||||
fn from_json(&self, json: &str) -> Result<Rc<dyn ElementStyle>, String>;
|
||||
|
||||
/// Serializes sytle into json string
|
||||
fn to_json(&self) -> String;
|
||||
|
||||
/// Attempts to deserialize lua table into a new style
|
||||
fn from_lua(
|
||||
&self,
|
||||
|
@ -32,24 +29,24 @@ impl_downcast!(ElementStyle);
|
|||
|
||||
pub trait StyleHolder {
|
||||
/// gets a reference to all defined styles
|
||||
fn styles(&self) -> Ref<'_, HashMap<String, Rc<dyn ElementStyle>>>;
|
||||
fn element_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn ElementStyle>>>;
|
||||
|
||||
/// gets a (mutable) reference to all defined styles
|
||||
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>>;
|
||||
fn element_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>>;
|
||||
|
||||
/// Checks if a given style key is registered
|
||||
fn is_style_registered(&self, style_key: &str) -> bool { self.styles().contains_key(style_key) }
|
||||
fn is_style_registered(&self, style_key: &str) -> bool { self.element_styles().contains_key(style_key) }
|
||||
|
||||
/// Gets the current active style for an element
|
||||
/// NOTE: Will panic if a style is not defined for a given element
|
||||
/// If you need to process user input, use [`is_registered`]
|
||||
fn current_style(&self, style_key: &str) -> Rc<dyn ElementStyle> {
|
||||
self.styles().get(style_key).map(|rc| rc.clone()).unwrap()
|
||||
self.element_styles().get(style_key).map(|rc| rc.clone()).unwrap()
|
||||
}
|
||||
|
||||
/// Sets the [`style`]
|
||||
fn set_current_style(&self, style: Rc<dyn ElementStyle>) {
|
||||
self.styles_mut().insert(style.key().to_string(), style);
|
||||
self.element_styles_mut().insert(style.key().to_string(), style);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,8 +62,6 @@ macro_rules! impl_elementstyle {
|
|||
.map(|obj| std::rc::Rc::new(obj) as std::rc::Rc<dyn ElementStyle>)
|
||||
}
|
||||
|
||||
fn to_json(&self) -> String { serde_json::to_string(self).unwrap() }
|
||||
|
||||
fn from_lua(
|
||||
&self,
|
||||
lua: &mlua::Lua,
|
||||
|
|
557
src/elements/customstyle.rs
Normal file
557
src/elements/customstyle.rs
Normal file
|
@ -0,0 +1,557 @@
|
|||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ariadne::Fmt;
|
||||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Error::BadArgument;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
|
||||
use crate::document::customstyle::CustomStyle;
|
||||
use crate::document::customstyle::CustomStyleToken;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::document::DocumentAccessors;
|
||||
use crate::lua::kernel::function_with_context;
|
||||
use crate::lua::kernel::KernelContext;
|
||||
use crate::lua::kernel::CTX;
|
||||
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::state::Scope;
|
||||
use crate::parser::state::State;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use super::paragraph::Paragraph;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LuaCustomStyle {
|
||||
pub(self) name: String,
|
||||
pub(self) tokens: CustomStyleToken,
|
||||
pub(self) start: String,
|
||||
pub(self) end: String,
|
||||
}
|
||||
|
||||
impl CustomStyle for LuaCustomStyle {
|
||||
fn name(&self) -> &str { self.name.as_str() }
|
||||
|
||||
fn tokens(&self) -> &CustomStyleToken { &self.tokens }
|
||||
|
||||
fn on_start<'a>(
|
||||
&self,
|
||||
location: Token,
|
||||
parser: &dyn Parser,
|
||||
document: &'a dyn Document<'a>,
|
||||
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let kernel = parser.get_kernel("main").unwrap();
|
||||
let ctx = KernelContext {
|
||||
location: location.clone(),
|
||||
parser,
|
||||
document,
|
||||
};
|
||||
|
||||
let mut result = Ok(());
|
||||
kernel.run_with_context(ctx, |lua| {
|
||||
let chunk = lua.load(self.start.as_str());
|
||||
if let Err(err) = chunk.eval::<()>() {
|
||||
result = Err(
|
||||
Report::build(ReportKind::Error, location.source(), location.start())
|
||||
.with_message("Lua execution failed")
|
||||
.with_label(
|
||||
Label::new((location.source(), location.range.clone()))
|
||||
.with_message(err.to_string())
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.with_note(format!(
|
||||
"When trying to start custom style {}",
|
||||
self.name().fg(parser.colors().info)
|
||||
))
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn on_end<'a>(
|
||||
&self,
|
||||
location: Token,
|
||||
parser: &dyn Parser,
|
||||
document: &'a dyn Document<'a>,
|
||||
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let kernel = parser.get_kernel("main").unwrap();
|
||||
let ctx = KernelContext {
|
||||
location: location.clone(),
|
||||
parser,
|
||||
document,
|
||||
};
|
||||
|
||||
let mut result = Ok(());
|
||||
kernel.run_with_context(ctx, |lua| {
|
||||
let chunk = lua.load(self.end.as_str());
|
||||
if let Err(err) = chunk.eval::<()>() {
|
||||
result = Err(
|
||||
Report::build(ReportKind::Error, location.source(), location.start())
|
||||
.with_message("Lua execution failed")
|
||||
.with_label(
|
||||
Label::new((location.source(), location.range.clone()))
|
||||
.with_message(err.to_string())
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.with_note(format!(
|
||||
"When trying to end custom style {}",
|
||||
self.name().fg(parser.colors().info)
|
||||
))
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomStyleState {
|
||||
toggled: HashMap<String, Token>,
|
||||
}
|
||||
|
||||
impl State for CustomStyleState {
|
||||
fn scope(&self) -> Scope { Scope::PARAGRAPH }
|
||||
|
||||
fn on_remove<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
document: &dyn Document,
|
||||
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut reports = vec![];
|
||||
|
||||
self.toggled.iter().for_each(|(style, token)| {
|
||||
let paragraph = document.last_element::<Paragraph>().unwrap();
|
||||
let paragraph_end = paragraph
|
||||
.content
|
||||
.last()
|
||||
.and_then(|last| {
|
||||
Some((
|
||||
last.location().source(),
|
||||
last.location().end() - 1..last.location().end(),
|
||||
))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), token.start())
|
||||
.with_message("Unterminated Custom Style")
|
||||
.with_label(
|
||||
Label::new((token.source(), token.range.clone()))
|
||||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Style {} starts here",
|
||||
style.fg(parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.with_label(
|
||||
Label::new(paragraph_end)
|
||||
.with_order(1)
|
||||
.with_message(format!("Paragraph ends here"))
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.with_note("Styles cannot span multiple documents (i.e @import)")
|
||||
.finish(),
|
||||
);
|
||||
});
|
||||
|
||||
return reports;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CustomStyleRule;
|
||||
|
||||
lazy_static! {
|
||||
static ref STATE_NAME: String = "elements.custom_style".to_string();
|
||||
}
|
||||
|
||||
impl Rule for CustomStyleRule {
|
||||
fn name(&self) -> &'static str { "Custom Style" }
|
||||
|
||||
fn next_match(&self, parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
let content = cursor.source.content();
|
||||
|
||||
let mut closest_match = usize::MAX;
|
||||
let mut matched_style = (None, false);
|
||||
parser
|
||||
.custom_styles()
|
||||
.iter()
|
||||
.for_each(|(_name, style)| match style.tokens() {
|
||||
CustomStyleToken::Toggle(s) => {
|
||||
if let Some(pos) = &content[cursor.pos..].find(s) {
|
||||
if *pos < closest_match {
|
||||
closest_match = *pos;
|
||||
matched_style = (Some(style.clone()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomStyleToken::Pair(begin, end) => {
|
||||
if let Some(pos) = &content[cursor.pos..].find(begin) {
|
||||
if *pos < closest_match {
|
||||
closest_match = *pos;
|
||||
matched_style = (Some(style.clone()), false);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pos) = &content[cursor.pos..].find(end) {
|
||||
if *pos < closest_match {
|
||||
closest_match = *pos;
|
||||
matched_style = (Some(style.clone()), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if closest_match == usize::MAX {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
closest_match + cursor.pos,
|
||||
Box::new((matched_style.0.unwrap().clone(), matched_style.1)) 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>>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||
let (style, end) = match_data
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.downcast_ref::<(Rc<dyn CustomStyle>, bool)>()
|
||||
.unwrap();
|
||||
|
||||
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(CustomStyleState {
|
||||
toggled: HashMap::new(),
|
||||
})),
|
||||
) {
|
||||
Err(_) => panic!("Unknown error"),
|
||||
Ok(state) => state,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (close, token) = match style.tokens() {
|
||||
CustomStyleToken::Toggle(s) => {
|
||||
let mut borrow = state.borrow_mut();
|
||||
let state = borrow.downcast_mut::<CustomStyleState>().unwrap();
|
||||
|
||||
match state.toggled.get(style.name()) {
|
||||
Some(_) => {
|
||||
// Terminate style
|
||||
let token =
|
||||
Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
|
||||
|
||||
state.toggled.remove(style.name());
|
||||
(true, token)
|
||||
}
|
||||
None => {
|
||||
// Start style
|
||||
let token =
|
||||
Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
|
||||
|
||||
state.toggled.insert(style.name().into(), token.clone());
|
||||
(false, token)
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomStyleToken::Pair(s_begin, s_end) => {
|
||||
let mut borrow = state.borrow_mut();
|
||||
let state = borrow.downcast_mut::<CustomStyleState>().unwrap();
|
||||
|
||||
if *end {
|
||||
// Terminate style
|
||||
let token =
|
||||
Token::new(cursor.pos..cursor.pos + s_end.len(), cursor.source.clone());
|
||||
if state.toggled.get(style.name()).is_none() {
|
||||
return (
|
||||
cursor.at(cursor.pos + s_end.len()),
|
||||
vec![
|
||||
Report::build(ReportKind::Error, token.source(), token.start())
|
||||
.with_message("Invalid End of Style")
|
||||
.with_label(
|
||||
Label::new((token.source(), token.range.clone()))
|
||||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Cannot end style {} here, is it not started anywhere",
|
||||
style.name().fg(parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
state.toggled.remove(style.name());
|
||||
(true, token)
|
||||
} else {
|
||||
// Start style
|
||||
let token = Token::new(
|
||||
cursor.pos..cursor.pos + s_begin.len(),
|
||||
cursor.source.clone(),
|
||||
);
|
||||
if let Some(start_token) = state.toggled.get(style.name()) {
|
||||
return (
|
||||
cursor.at(cursor.pos + s_end.len()),
|
||||
vec![Report::build(
|
||||
ReportKind::Error,
|
||||
start_token.source(),
|
||||
start_token.start(),
|
||||
)
|
||||
.with_message("Invalid Start of Style")
|
||||
.with_label(
|
||||
Label::new((token.source(), token.range.clone()))
|
||||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Style cannot {} starts here",
|
||||
style.name().fg(parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.with_label(
|
||||
Label::new((start_token.source(), start_token.range.clone()))
|
||||
.with_order(2)
|
||||
.with_message(format!(
|
||||
"Style {} starts previously here",
|
||||
style.name().fg(parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.finish()],
|
||||
);
|
||||
}
|
||||
|
||||
state.toggled.insert(style.name().into(), token.clone());
|
||||
(false, token)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(rep) = if close {
|
||||
style.on_end(token.clone(), parser, document)
|
||||
} else {
|
||||
style.on_start(token.clone(), parser, document)
|
||||
} {
|
||||
return (
|
||||
cursor.at(token.end()),
|
||||
vec![unsafe {
|
||||
// TODO
|
||||
std::mem::transmute(rep)
|
||||
}],
|
||||
);
|
||||
} else {
|
||||
(cursor.at(token.end()), vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
"define_toggled".into(),
|
||||
lua.create_function(
|
||||
|_, (name, token, on_start, on_end): (String, String, String, String)| {
|
||||
let mut result = Ok(());
|
||||
|
||||
let style = LuaCustomStyle {
|
||||
tokens: CustomStyleToken::Toggle(token),
|
||||
name: name.clone(),
|
||||
start: on_start,
|
||||
end: on_end,
|
||||
};
|
||||
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
if let Some(_) = ctx.parser.get_custom_style(name.as_str()) {
|
||||
result = Err(BadArgument {
|
||||
to: Some("define_toggled".to_string()),
|
||||
pos: 1,
|
||||
name: Some("name".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!(
|
||||
"Custom style with name `{name}` already exists"
|
||||
))),
|
||||
});
|
||||
return;
|
||||
}
|
||||
ctx.parser.insert_custom_style(Rc::new(style));
|
||||
});
|
||||
});
|
||||
|
||||
result
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
bindings.push((
|
||||
"define_paired".into(),
|
||||
lua.create_function(
|
||||
|_,
|
||||
(name, token_start, token_end, on_start, on_end): (
|
||||
String,
|
||||
String,
|
||||
String,
|
||||
String,
|
||||
String,
|
||||
)| {
|
||||
let mut result = Ok(());
|
||||
|
||||
let style = LuaCustomStyle {
|
||||
tokens: CustomStyleToken::Pair(token_start, token_end),
|
||||
name: name.clone(),
|
||||
start: on_start,
|
||||
end: on_end,
|
||||
};
|
||||
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
if let Some(_) = ctx.parser.get_custom_style(name.as_str()) {
|
||||
result = Err(BadArgument {
|
||||
to: Some("define_toggled".to_string()),
|
||||
pos: 1,
|
||||
name: Some("name".to_string()),
|
||||
cause: Arc::new(mlua::Error::external(format!(
|
||||
"Custom style with name `{name}` already exists"
|
||||
))),
|
||||
});
|
||||
return;
|
||||
}
|
||||
ctx.parser.insert_custom_style(Rc::new(style));
|
||||
});
|
||||
});
|
||||
|
||||
result
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::elements::raw::Raw;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn toggle() {
|
||||
let source = Rc::new(SourceFile::with_content(
|
||||
"".to_string(),
|
||||
r#"
|
||||
%<[main]
|
||||
function my_style_start()
|
||||
nml.raw.push("inline", "start")
|
||||
end
|
||||
function my_style_end()
|
||||
nml.raw.push("inline", "end")
|
||||
end
|
||||
function red_style_start()
|
||||
nml.raw.push("inline", "<a style=\"color:red\">")
|
||||
end
|
||||
function red_style_end()
|
||||
nml.raw.push("inline", "</a>")
|
||||
end
|
||||
nml.custom_style.define_toggled("My Style", "|", "my_style_start()", "my_style_end()")
|
||||
nml.custom_style.define_toggled("My Style2", "°", "red_style_start()", "red_style_end()")
|
||||
>%
|
||||
pre |styled| post °Hello°.
|
||||
"#
|
||||
.to_string(),
|
||||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
Text { content == "pre " };
|
||||
Raw { content == "start" };
|
||||
Text { content == "styled" };
|
||||
Raw { content == "end" };
|
||||
Text { content == " post " };
|
||||
Raw { content == "<a style=\"color:red\">" };
|
||||
Text { content == "Hello" };
|
||||
Raw { content == "</a>" };
|
||||
Text { content == "." };
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paired() {
|
||||
let source = Rc::new(SourceFile::with_content(
|
||||
"".to_string(),
|
||||
r#"
|
||||
%<[main]
|
||||
function my_style_start()
|
||||
nml.raw.push("inline", "start")
|
||||
end
|
||||
function my_style_end()
|
||||
nml.raw.push("inline", "end")
|
||||
end
|
||||
function red_style_start()
|
||||
nml.raw.push("inline", "<a style=\"color:red\">")
|
||||
end
|
||||
function red_style_end()
|
||||
nml.raw.push("inline", "</a>")
|
||||
end
|
||||
nml.custom_style.define_paired("My Style", "[", "]", "my_style_start()", "my_style_end()")
|
||||
nml.custom_style.define_paired("My Style2", "(", ")", "red_style_start()", "red_style_end()")
|
||||
>%
|
||||
pre [styled] post (Hello).
|
||||
"#
|
||||
.to_string(),
|
||||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
Text { content == "pre " };
|
||||
Raw { content == "start" };
|
||||
Text { content == "styled" };
|
||||
Raw { content == "end" };
|
||||
Text { content == " post " };
|
||||
Raw { content == "<a style=\"color:red\">" };
|
||||
Text { content == "Hello" };
|
||||
Raw { content == "</a>" };
|
||||
Text { content == "." };
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,23 +10,15 @@ use ariadne::ReportKind;
|
|||
use mlua::Error::BadArgument;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use mlua::LuaSerdeExt;
|
||||
use mlua::Table;
|
||||
use mlua::Value;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::document::document::Document;
|
||||
use crate::document::{self};
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::rule::Rule;
|
||||
use crate::parser::source::Cursor;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
|
||||
use super::variable::VariableRule;
|
||||
|
||||
pub struct ElemStyleRule {
|
||||
start_re: Regex,
|
||||
|
@ -66,7 +58,7 @@ impl ElemStyleRule {
|
|||
impl Rule for ElemStyleRule {
|
||||
fn name(&self) -> &'static str { "Element Style" }
|
||||
|
||||
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
self.start_re
|
||||
.find_at(cursor.source.content(), cursor.pos)
|
||||
.map_or(None, |m| {
|
||||
|
|
|
@ -252,7 +252,7 @@ impl ListRule {
|
|||
impl Rule for ListRule {
|
||||
fn name(&self) -> &'static str { "List" }
|
||||
|
||||
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
self.start_re
|
||||
.find_at(cursor.source.content(), cursor.pos)
|
||||
.map_or(None, |m| {
|
||||
|
@ -440,7 +440,6 @@ impl Rule for ListRule {
|
|||
#[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;
|
||||
|
|
|
@ -17,3 +17,4 @@ pub mod tex;
|
|||
pub mod text;
|
||||
pub mod variable;
|
||||
pub mod elemstyle;
|
||||
pub mod customstyle;
|
||||
|
|
|
@ -108,7 +108,7 @@ impl ParagraphRule {
|
|||
impl Rule for ParagraphRule {
|
||||
fn name(&self) -> &'static str { "Paragraphing" }
|
||||
|
||||
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
self.re
|
||||
.find_at(cursor.source.content(), cursor.pos)
|
||||
.and_then(|m| Some((m.start(), Box::new([false; 0]) as Box<dyn Any>)))
|
||||
|
|
|
@ -27,10 +27,10 @@ use std::str::FromStr;
|
|||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Raw {
|
||||
pub(self) location: Token,
|
||||
pub(self) kind: ElemKind,
|
||||
pub(self) content: String,
|
||||
pub struct Raw {
|
||||
pub location: Token,
|
||||
pub kind: ElemKind,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl Element for Raw {
|
||||
|
@ -265,7 +265,6 @@ impl RegexRule for RawRule {
|
|||
#[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;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserStrategy;
|
||||
|
||||
use super::code::CodeRule;
|
||||
use super::comment::CommentRule;
|
||||
|
@ -14,6 +15,7 @@ use super::raw::RawRule;
|
|||
use super::script::ScriptRule;
|
||||
use super::section::SectionRule;
|
||||
use super::style::StyleRule;
|
||||
use super::customstyle::CustomStyleRule;
|
||||
use super::tex::TexRule;
|
||||
use super::text::TextRule;
|
||||
use super::variable::VariableRule;
|
||||
|
@ -37,6 +39,7 @@ pub fn register<P: Parser>(parser: &mut P) {
|
|||
parser.add_rule(Box::new(LayoutRule::new()), None).unwrap();
|
||||
|
||||
parser.add_rule(Box::new(StyleRule::new()), None).unwrap();
|
||||
parser.add_rule(Box::new(CustomStyleRule{}), None).unwrap();
|
||||
parser.add_rule(Box::new(SectionRule::new()), None).unwrap();
|
||||
parser.add_rule(Box::new(LinkRule::new()), None).unwrap();
|
||||
parser.add_rule(Box::new(TextRule::default()), None).unwrap();
|
||||
|
|
|
@ -96,10 +96,6 @@ impl State for StyleState {
|
|||
} // Style not enabled
|
||||
let token = token.as_ref().unwrap();
|
||||
|
||||
//let range = range.as_ref().unwrap();
|
||||
|
||||
//let active_range = range.start .. paragraph.location().end()-1;
|
||||
|
||||
let paragraph = document.last_element::<Paragraph>().unwrap();
|
||||
let paragraph_end = paragraph
|
||||
.content
|
||||
|
@ -112,16 +108,9 @@ impl State for StyleState {
|
|||
})
|
||||
.unwrap();
|
||||
|
||||
// TODO: Allow style to span multiple documents if they don't break paragraph.
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), token.start())
|
||||
.with_message("Unterminated style")
|
||||
//.with_label(
|
||||
// Label::new((document.source(), active_range.clone()))
|
||||
// .with_order(0)
|
||||
// .with_message(format!("Style {} is not terminated before the end of paragraph",
|
||||
// name.fg(parser.colors().info)))
|
||||
// .with_color(parser.colors().error))
|
||||
.with_message("Unterminated Style")
|
||||
.with_label(
|
||||
Label::new((token.source(), token.range.clone()))
|
||||
.with_order(1)
|
||||
|
@ -129,13 +118,13 @@ impl State for StyleState {
|
|||
"Style {} starts here",
|
||||
name.fg(parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().info),
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.with_label(
|
||||
Label::new(paragraph_end)
|
||||
.with_order(1)
|
||||
.with_message(format!("Paragraph ends here"))
|
||||
.with_color(parser.colors().info),
|
||||
.with_color(parser.colors().error),
|
||||
)
|
||||
.with_note("Styles cannot span multiple documents (i.e @import)")
|
||||
.finish(),
|
||||
|
@ -199,7 +188,7 @@ impl RegexRule for StyleRule {
|
|||
}
|
||||
};
|
||||
|
||||
if let Some(style_state) = state.borrow_mut().as_any_mut().downcast_mut::<StyleState>() {
|
||||
if let Some(style_state) = state.borrow_mut().downcast_mut::<StyleState>() {
|
||||
style_state.toggled[index] = style_state.toggled[index]
|
||||
.clone()
|
||||
.map_or(Some(token.clone()), |_| None);
|
||||
|
|
|
@ -48,7 +48,7 @@ pub struct TextRule;
|
|||
impl Rule for TextRule {
|
||||
fn name(&self) -> &'static str { "Text" }
|
||||
|
||||
fn next_match(&self, _cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { None }
|
||||
fn next_match(&self, _parser: &dyn Parser, _cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { None }
|
||||
|
||||
fn on_match(
|
||||
&self,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{cell::{RefCell, RefMut}, collections::HashMap, rc::Rc};
|
||||
use std::{cell::{Ref, RefCell, RefMut}, collections::HashMap, rc::Rc};
|
||||
|
||||
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}};
|
||||
use crate::{document::{customstyle::{CustomStyle, CustomStyleHolder}, 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
|
||||
|
@ -112,7 +112,7 @@ impl Parser for LsParser
|
|||
fn rules(&self) -> &Vec<Box<dyn Rule>> { &self.rules }
|
||||
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>> { &mut self.rules }
|
||||
|
||||
fn state(&self) -> std::cell::Ref<'_, StateHolder> { self.state.borrow() }
|
||||
fn state(&self) -> Ref<'_, StateHolder> { self.state.borrow() }
|
||||
fn state_mut(&self) -> std::cell::RefMut<'_, StateHolder> { self.state.borrow_mut() }
|
||||
|
||||
fn has_error(&self) -> bool { true }
|
||||
|
@ -148,17 +148,17 @@ impl KernelHolder for LsParser
|
|||
}
|
||||
|
||||
impl StyleHolder for LsParser {
|
||||
fn styles(&self) -> std::cell::Ref<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||
fn element_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||
fn element_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHolder for LsParser {
|
||||
fn layouts(&self) -> std::cell::Ref<'_, HashMap<String, Rc<dyn LayoutType>>> {
|
||||
fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn LayoutType>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -166,3 +166,13 @@ impl LayoutHolder for LsParser {
|
|||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomStyleHolder for LsParser {
|
||||
fn custom_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn CustomStyle>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn custom_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn CustomStyle>>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use std::cell::RefCell;
|
||||
use std::cell::RefMut;
|
||||
|
||||
use mlua::Error;
|
||||
use mlua::FromLuaMulti;
|
||||
use mlua::Function;
|
||||
use mlua::IntoLuaMulti;
|
||||
use mlua::Lua;
|
||||
|
||||
use crate::document::document::Document;
|
||||
|
@ -72,3 +76,19 @@ pub trait KernelHolder {
|
|||
|
||||
fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel>;
|
||||
}
|
||||
|
||||
/// Runs a lua function with a context
|
||||
///
|
||||
/// This is the only way lua functions shoule be ran, because exported
|
||||
/// functions may require the context in order to operate
|
||||
pub fn function_with_context<'lua, A, R>(context: KernelContext, fun: &Function<'lua>, args: A) -> Result<R, Error>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
CTX.set(Some(unsafe { std::mem::transmute(context) }));
|
||||
let ret = fun.call::<A, R>(args);
|
||||
CTX.set(None);
|
||||
|
||||
ret
|
||||
}
|
||||
|
|
|
@ -215,7 +215,13 @@ fn main() -> ExitCode {
|
|||
}
|
||||
}
|
||||
match std::fs::metadata(&output) {
|
||||
Ok(_) => {}
|
||||
Ok(output_meta) => {
|
||||
if !output_meta.is_dir()
|
||||
{
|
||||
eprintln!("Input is a directory, but ouput is not a directory, halting");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Unable to get metadata for output `{output}`: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
|
|
|
@ -9,6 +9,8 @@ use std::rc::Rc;
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
|
||||
use crate::document::customstyle::CustomStyle;
|
||||
use crate::document::customstyle::CustomStyleHolder;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::document::DocumentAccessors;
|
||||
use crate::document::element::ContainerElement;
|
||||
|
@ -29,6 +31,7 @@ use crate::parser::source::SourceFile;
|
|||
use crate::parser::source::VirtualSource;
|
||||
|
||||
use super::parser::Parser;
|
||||
use super::parser::ParserStrategy;
|
||||
use super::parser::ReportColors;
|
||||
use super::rule::Rule;
|
||||
use super::source::Cursor;
|
||||
|
@ -49,6 +52,7 @@ pub struct LangParser {
|
|||
pub kernels: RefCell<HashMap<String, Kernel>>,
|
||||
pub styles: RefCell<HashMap<String, Rc<dyn ElementStyle>>>,
|
||||
pub layouts: RefCell<HashMap<String, Rc<dyn LayoutType>>>,
|
||||
pub custom_styles: RefCell<HashMap<String, Rc<dyn CustomStyle>>>,
|
||||
}
|
||||
|
||||
impl LangParser {
|
||||
|
@ -61,6 +65,7 @@ impl LangParser {
|
|||
kernels: RefCell::new(HashMap::new()),
|
||||
styles: RefCell::new(HashMap::new()),
|
||||
layouts: RefCell::new(HashMap::new()),
|
||||
custom_styles: RefCell::new(HashMap::new()),
|
||||
};
|
||||
// Register rules
|
||||
register(&mut s);
|
||||
|
@ -316,21 +321,29 @@ impl KernelHolder for LangParser {
|
|||
}
|
||||
|
||||
impl StyleHolder for LangParser {
|
||||
fn styles(&self) -> Ref<'_, HashMap<String, Rc<dyn ElementStyle>>> { self.styles.borrow() }
|
||||
fn element_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||
self.styles.borrow()
|
||||
}
|
||||
|
||||
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||
fn element_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||
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(&self) -> Ref<'_, HashMap<String, Rc<dyn LayoutType>>> { self.layouts.borrow() }
|
||||
|
||||
fn layouts_mut(
|
||||
&self,
|
||||
) -> RefMut<'_, HashMap<String, Rc<dyn crate::document::layout::LayoutType>>> {
|
||||
fn layouts_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn LayoutType>>> {
|
||||
self.layouts.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomStyleHolder for LangParser {
|
||||
fn custom_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn CustomStyle>>> {
|
||||
self.custom_styles.borrow()
|
||||
}
|
||||
|
||||
fn custom_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn CustomStyle>>> {
|
||||
self.custom_styles.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use super::rule::Rule;
|
|||
use super::source::Cursor;
|
||||
use super::source::Source;
|
||||
use super::state::StateHolder;
|
||||
use crate::document::customstyle::CustomStyleHolder;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::element::Element;
|
||||
use crate::document::layout::LayoutHolder;
|
||||
|
@ -43,7 +44,7 @@ impl ReportColors {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
||||
pub trait Parser: KernelHolder + StyleHolder + LayoutHolder + CustomStyleHolder {
|
||||
/// Gets the colors for formatting errors
|
||||
///
|
||||
/// When colors are disabled, all colors should resolve to empty string
|
||||
|
@ -52,6 +53,36 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
|||
fn rules(&self) -> &Vec<Box<dyn Rule>>;
|
||||
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
|
||||
|
||||
fn state(&self) -> Ref<'_, StateHolder>;
|
||||
fn state_mut(&self) -> RefMut<'_, StateHolder>;
|
||||
|
||||
fn has_error(&self) -> bool;
|
||||
|
||||
/// Add an [`Element`] to the [`Document`]
|
||||
fn push<'a>(&self, doc: &dyn Document, elem: Box<dyn Element>);
|
||||
|
||||
/// Parse [`Source`] into a new [`Document`]
|
||||
fn parse<'a>(
|
||||
&self,
|
||||
source: Rc<dyn Source>,
|
||||
parent: Option<&'a dyn Document<'a>>,
|
||||
) -> Box<dyn Document<'a> + 'a>;
|
||||
|
||||
/// Parse [`Source`] into an already existing [`Document`]
|
||||
fn parse_into<'a>(&self, source: Rc<dyn Source>, document: &'a dyn Document<'a>);
|
||||
}
|
||||
|
||||
pub trait ParserStrategy {
|
||||
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String>;
|
||||
|
||||
fn update_matches(
|
||||
&self,
|
||||
cursor: &Cursor,
|
||||
matches: &mut Vec<(usize, Option<Box<dyn Any>>)>,
|
||||
) -> (Cursor, Option<&Box<dyn Rule>>, Option<Box<dyn Any>>);
|
||||
}
|
||||
|
||||
impl<T: Parser> ParserStrategy for T {
|
||||
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String> {
|
||||
// Error on duplicate rule
|
||||
let rule_name = (*rule).name();
|
||||
|
@ -91,14 +122,6 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn state(&self) -> Ref<'_, StateHolder>;
|
||||
fn state_mut(&self) -> RefMut<'_, StateHolder>;
|
||||
|
||||
fn has_error(&self) -> bool;
|
||||
|
||||
// Update [`matches`] and returns the position of the next matched rule.
|
||||
// If rule is empty, it means that there are no rules left to parse (i.e
|
||||
// end of document).
|
||||
fn update_matches(
|
||||
&self,
|
||||
cursor: &Cursor,
|
||||
|
@ -111,11 +134,12 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
|||
.zip(matches.iter_mut())
|
||||
.for_each(|(rule, (matched_at, match_data))| {
|
||||
// Don't upate if not stepped over yet
|
||||
if *matched_at > cursor.pos {
|
||||
if *matched_at > cursor.pos && rule.downcast_ref::<crate::elements::customstyle::CustomStyleRule>().is_none() {
|
||||
// TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule
|
||||
return;
|
||||
}
|
||||
|
||||
(*matched_at, *match_data) = match rule.next_match(cursor) {
|
||||
(*matched_at, *match_data) = match rule.next_match(self, cursor) {
|
||||
None => (usize::MAX, None),
|
||||
Some((mut pos, mut data)) => {
|
||||
// Check if escaped
|
||||
|
@ -136,7 +160,7 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
|||
}
|
||||
|
||||
// Find next potential match
|
||||
(pos, data) = match rule.next_match(&cursor.at(pos + 1)) {
|
||||
(pos, data) = match rule.next_match(self, &cursor.at(pos + 1)) {
|
||||
Some((new_pos, new_data)) => (new_pos, new_data),
|
||||
None => (usize::MAX, data), // Stop iterating
|
||||
}
|
||||
|
@ -167,17 +191,4 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
|||
std::mem::replace(&mut matches[winner].1, None),
|
||||
)
|
||||
}
|
||||
|
||||
/// Add an [`Element`] to the [`Document`]
|
||||
fn push<'a>(&self, doc: &dyn Document, elem: Box<dyn Element>);
|
||||
|
||||
/// Parse [`Source`] into a new [`Document`]
|
||||
fn parse<'a>(
|
||||
&self,
|
||||
source: Rc<dyn Source>,
|
||||
parent: Option<&'a dyn Document<'a>>,
|
||||
) -> Box<dyn Document<'a> + 'a>;
|
||||
|
||||
/// Parse [`Source`] into an already existing [`Document`]
|
||||
fn parse_into<'a>(&self, source: Rc<dyn Source>, document: &'a dyn Document<'a>);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ use super::source::Source;
|
|||
use super::source::Token;
|
||||
use crate::document::document::Document;
|
||||
use ariadne::Report;
|
||||
use downcast_rs::impl_downcast;
|
||||
use downcast_rs::Downcast;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
|
||||
|
@ -11,11 +13,11 @@ use std::any::Any;
|
|||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait Rule {
|
||||
pub trait Rule: Downcast {
|
||||
/// Returns rule's name
|
||||
fn name(&self) -> &'static str;
|
||||
/// Finds the next match starting from [`cursor`]
|
||||
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)>;
|
||||
fn next_match(&self, parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)>;
|
||||
/// Callback when rule matches
|
||||
fn on_match<'a>(
|
||||
&self,
|
||||
|
@ -33,6 +35,7 @@ pub trait Rule {
|
|||
/// Registers default layouts
|
||||
fn register_layouts(&self, _parser: &dyn Parser) {}
|
||||
}
|
||||
impl_downcast!(Rule);
|
||||
|
||||
impl core::fmt::Debug for dyn Rule {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -61,13 +64,13 @@ pub trait RegexRule {
|
|||
fn register_layouts(&self, _parser: &dyn Parser) {}
|
||||
}
|
||||
|
||||
impl<T: RegexRule> Rule for T {
|
||||
impl<T: RegexRule + 'static> Rule for T {
|
||||
fn name(&self) -> &'static str {
|
||||
RegexRule::name(self)
|
||||
}
|
||||
|
||||
/// Finds the next match starting from [`cursor`]
|
||||
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
let content = cursor.source.content();
|
||||
let mut found: Option<(usize, usize)> = None;
|
||||
self.regexes().iter().enumerate().for_each(|(id, re)| {
|
||||
|
|
Loading…
Reference in a new issue