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-server",
|
||||||
"lsp-types 0.97.0",
|
"lsp-types 0.97.0",
|
||||||
"mlua",
|
"mlua",
|
||||||
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"rust-crypto",
|
"rust-crypto",
|
||||||
|
|
|
@ -37,3 +37,6 @@ tokio = { version = "1.38.1", features = ["macros", "rt-multi-thread", "io-std"]
|
||||||
tower-lsp = "0.20.0"
|
tower-lsp = "0.20.0"
|
||||||
unicode-segmentation = "1.11.0"
|
unicode-segmentation = "1.11.0"
|
||||||
walkdir = "2.5.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::compiler::compiler::Compiler;
|
||||||
use crate::elements::reference::Reference;
|
use crate::elements::reference::Reference;
|
||||||
use crate::parser::source::Token;
|
use crate::parser::source::Token;
|
||||||
use crate::parser::util::PropertyParser;
|
|
||||||
use downcast_rs::impl_downcast;
|
use downcast_rs::impl_downcast;
|
||||||
use downcast_rs::Downcast;
|
use downcast_rs::Downcast;
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,4 @@ pub mod element;
|
||||||
pub mod variable;
|
pub mod variable;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
pub mod customstyle;
|
||||||
|
|
|
@ -18,9 +18,6 @@ pub trait ElementStyle: Downcast + core::fmt::Debug {
|
||||||
/// Will fail if deserialization fails
|
/// Will fail if deserialization fails
|
||||||
fn from_json(&self, json: &str) -> Result<Rc<dyn ElementStyle>, String>;
|
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
|
/// Attempts to deserialize lua table into a new style
|
||||||
fn from_lua(
|
fn from_lua(
|
||||||
&self,
|
&self,
|
||||||
|
@ -32,24 +29,24 @@ impl_downcast!(ElementStyle);
|
||||||
|
|
||||||
pub trait StyleHolder {
|
pub trait StyleHolder {
|
||||||
/// gets a reference to all defined styles
|
/// 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
|
/// 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
|
/// 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
|
/// Gets the current active style for an element
|
||||||
/// NOTE: Will panic if a style is not defined for a given element
|
/// NOTE: Will panic if a style is not defined for a given element
|
||||||
/// If you need to process user input, use [`is_registered`]
|
/// If you need to process user input, use [`is_registered`]
|
||||||
fn current_style(&self, style_key: &str) -> Rc<dyn ElementStyle> {
|
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`]
|
/// Sets the [`style`]
|
||||||
fn set_current_style(&self, style: Rc<dyn ElementStyle>) {
|
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>)
|
.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(
|
fn from_lua(
|
||||||
&self,
|
&self,
|
||||||
lua: &mlua::Lua,
|
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::Error::BadArgument;
|
||||||
use mlua::Function;
|
use mlua::Function;
|
||||||
use mlua::Lua;
|
use mlua::Lua;
|
||||||
use mlua::LuaSerdeExt;
|
|
||||||
use mlua::Table;
|
|
||||||
use mlua::Value;
|
use mlua::Value;
|
||||||
use regex::Captures;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::{self};
|
|
||||||
use crate::lua::kernel::CTX;
|
use crate::lua::kernel::CTX;
|
||||||
use crate::parser::parser::Parser;
|
use crate::parser::parser::Parser;
|
||||||
use crate::parser::rule::RegexRule;
|
|
||||||
use crate::parser::rule::Rule;
|
use crate::parser::rule::Rule;
|
||||||
use crate::parser::source::Cursor;
|
use crate::parser::source::Cursor;
|
||||||
use crate::parser::source::Source;
|
use crate::parser::source::Source;
|
||||||
use crate::parser::source::Token;
|
|
||||||
|
|
||||||
use super::variable::VariableRule;
|
|
||||||
|
|
||||||
pub struct ElemStyleRule {
|
pub struct ElemStyleRule {
|
||||||
start_re: Regex,
|
start_re: Regex,
|
||||||
|
@ -66,7 +58,7 @@ impl ElemStyleRule {
|
||||||
impl Rule for ElemStyleRule {
|
impl Rule for ElemStyleRule {
|
||||||
fn name(&self) -> &'static str { "Element Style" }
|
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
|
self.start_re
|
||||||
.find_at(cursor.source.content(), cursor.pos)
|
.find_at(cursor.source.content(), cursor.pos)
|
||||||
.map_or(None, |m| {
|
.map_or(None, |m| {
|
||||||
|
|
|
@ -252,7 +252,7 @@ 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, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||||
self.start_re
|
self.start_re
|
||||||
.find_at(cursor.source.content(), cursor.pos)
|
.find_at(cursor.source.content(), cursor.pos)
|
||||||
.map_or(None, |m| {
|
.map_or(None, |m| {
|
||||||
|
@ -440,7 +440,6 @@ impl Rule for ListRule {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::compiler::compiler::Target;
|
|
||||||
use crate::elements::paragraph::Paragraph;
|
use crate::elements::paragraph::Paragraph;
|
||||||
use crate::elements::text::Text;
|
use crate::elements::text::Text;
|
||||||
use crate::parser::langparser::LangParser;
|
use crate::parser::langparser::LangParser;
|
||||||
|
|
|
@ -17,3 +17,4 @@ pub mod tex;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod variable;
|
pub mod variable;
|
||||||
pub mod elemstyle;
|
pub mod elemstyle;
|
||||||
|
pub mod customstyle;
|
||||||
|
|
|
@ -108,7 +108,7 @@ impl ParagraphRule {
|
||||||
impl Rule for ParagraphRule {
|
impl Rule for ParagraphRule {
|
||||||
fn name(&self) -> &'static str { "Paragraphing" }
|
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
|
self.re
|
||||||
.find_at(cursor.source.content(), cursor.pos)
|
.find_at(cursor.source.content(), cursor.pos)
|
||||||
.and_then(|m| Some((m.start(), Box::new([false; 0]) as Box<dyn Any>)))
|
.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;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Raw {
|
pub struct Raw {
|
||||||
pub(self) location: Token,
|
pub location: Token,
|
||||||
pub(self) kind: ElemKind,
|
pub kind: ElemKind,
|
||||||
pub(self) content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Raw {
|
impl Element for Raw {
|
||||||
|
@ -265,7 +265,6 @@ impl RegexRule for RawRule {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::compiler::compiler::Target;
|
|
||||||
use crate::elements::paragraph::Paragraph;
|
use crate::elements::paragraph::Paragraph;
|
||||||
use crate::elements::text::Text;
|
use crate::elements::text::Text;
|
||||||
use crate::parser::langparser::LangParser;
|
use crate::parser::langparser::LangParser;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::parser::parser::Parser;
|
use crate::parser::parser::Parser;
|
||||||
|
use crate::parser::parser::ParserStrategy;
|
||||||
|
|
||||||
use super::code::CodeRule;
|
use super::code::CodeRule;
|
||||||
use super::comment::CommentRule;
|
use super::comment::CommentRule;
|
||||||
|
@ -14,6 +15,7 @@ use super::raw::RawRule;
|
||||||
use super::script::ScriptRule;
|
use super::script::ScriptRule;
|
||||||
use super::section::SectionRule;
|
use super::section::SectionRule;
|
||||||
use super::style::StyleRule;
|
use super::style::StyleRule;
|
||||||
|
use super::customstyle::CustomStyleRule;
|
||||||
use super::tex::TexRule;
|
use super::tex::TexRule;
|
||||||
use super::text::TextRule;
|
use super::text::TextRule;
|
||||||
use super::variable::VariableRule;
|
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(LayoutRule::new()), None).unwrap();
|
||||||
|
|
||||||
parser.add_rule(Box::new(StyleRule::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(SectionRule::new()), None).unwrap();
|
||||||
parser.add_rule(Box::new(LinkRule::new()), None).unwrap();
|
parser.add_rule(Box::new(LinkRule::new()), None).unwrap();
|
||||||
parser.add_rule(Box::new(TextRule::default()), None).unwrap();
|
parser.add_rule(Box::new(TextRule::default()), None).unwrap();
|
||||||
|
|
|
@ -96,10 +96,6 @@ impl State for StyleState {
|
||||||
} // Style not enabled
|
} // Style not enabled
|
||||||
let token = token.as_ref().unwrap();
|
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 = document.last_element::<Paragraph>().unwrap();
|
||||||
let paragraph_end = paragraph
|
let paragraph_end = paragraph
|
||||||
.content
|
.content
|
||||||
|
@ -112,16 +108,9 @@ impl State for StyleState {
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// TODO: Allow style to span multiple documents if they don't break paragraph.
|
|
||||||
reports.push(
|
reports.push(
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
Report::build(ReportKind::Error, token.source(), token.start())
|
||||||
.with_message("Unterminated style")
|
.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_label(
|
.with_label(
|
||||||
Label::new((token.source(), token.range.clone()))
|
Label::new((token.source(), token.range.clone()))
|
||||||
.with_order(1)
|
.with_order(1)
|
||||||
|
@ -129,13 +118,13 @@ impl State for StyleState {
|
||||||
"Style {} starts here",
|
"Style {} starts here",
|
||||||
name.fg(parser.colors().info)
|
name.fg(parser.colors().info)
|
||||||
))
|
))
|
||||||
.with_color(parser.colors().info),
|
.with_color(parser.colors().error),
|
||||||
)
|
)
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new(paragraph_end)
|
Label::new(paragraph_end)
|
||||||
.with_order(1)
|
.with_order(1)
|
||||||
.with_message(format!("Paragraph ends here"))
|
.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)")
|
.with_note("Styles cannot span multiple documents (i.e @import)")
|
||||||
.finish(),
|
.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]
|
style_state.toggled[index] = style_state.toggled[index]
|
||||||
.clone()
|
.clone()
|
||||||
.map_or(Some(token.clone()), |_| None);
|
.map_or(Some(token.clone()), |_| None);
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub struct TextRule;
|
||||||
impl Rule for TextRule {
|
impl Rule for TextRule {
|
||||||
fn name(&self) -> &'static str { "Text" }
|
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(
|
fn on_match(
|
||||||
&self,
|
&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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LineCursor
|
pub struct LineCursor
|
||||||
|
@ -112,7 +112,7 @@ impl Parser for LsParser
|
||||||
fn rules(&self) -> &Vec<Box<dyn Rule>> { &self.rules }
|
fn rules(&self) -> &Vec<Box<dyn Rule>> { &self.rules }
|
||||||
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>> { &mut 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 state_mut(&self) -> std::cell::RefMut<'_, StateHolder> { self.state.borrow_mut() }
|
||||||
|
|
||||||
fn has_error(&self) -> bool { true }
|
fn has_error(&self) -> bool { true }
|
||||||
|
@ -148,17 +148,17 @@ impl KernelHolder for LsParser
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyleHolder 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!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
fn element_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutHolder for LsParser {
|
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!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,3 +166,13 @@ impl LayoutHolder for LsParser {
|
||||||
todo!()
|
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::RefCell;
|
||||||
use std::cell::RefMut;
|
use std::cell::RefMut;
|
||||||
|
|
||||||
|
use mlua::Error;
|
||||||
|
use mlua::FromLuaMulti;
|
||||||
|
use mlua::Function;
|
||||||
|
use mlua::IntoLuaMulti;
|
||||||
use mlua::Lua;
|
use mlua::Lua;
|
||||||
|
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
|
@ -72,3 +76,19 @@ pub trait KernelHolder {
|
||||||
|
|
||||||
fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel>;
|
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) {
|
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) => {
|
Err(e) => {
|
||||||
eprintln!("Unable to get metadata for output `{output}`: {e}");
|
eprintln!("Unable to get metadata for output `{output}`: {e}");
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
|
|
|
@ -9,6 +9,8 @@ use std::rc::Rc;
|
||||||
use ariadne::Label;
|
use ariadne::Label;
|
||||||
use ariadne::Report;
|
use ariadne::Report;
|
||||||
|
|
||||||
|
use crate::document::customstyle::CustomStyle;
|
||||||
|
use crate::document::customstyle::CustomStyleHolder;
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::document::DocumentAccessors;
|
use crate::document::document::DocumentAccessors;
|
||||||
use crate::document::element::ContainerElement;
|
use crate::document::element::ContainerElement;
|
||||||
|
@ -29,6 +31,7 @@ use crate::parser::source::SourceFile;
|
||||||
use crate::parser::source::VirtualSource;
|
use crate::parser::source::VirtualSource;
|
||||||
|
|
||||||
use super::parser::Parser;
|
use super::parser::Parser;
|
||||||
|
use super::parser::ParserStrategy;
|
||||||
use super::parser::ReportColors;
|
use super::parser::ReportColors;
|
||||||
use super::rule::Rule;
|
use super::rule::Rule;
|
||||||
use super::source::Cursor;
|
use super::source::Cursor;
|
||||||
|
@ -49,6 +52,7 @@ pub struct LangParser {
|
||||||
pub kernels: RefCell<HashMap<String, Kernel>>,
|
pub kernels: RefCell<HashMap<String, Kernel>>,
|
||||||
pub styles: RefCell<HashMap<String, Rc<dyn ElementStyle>>>,
|
pub styles: RefCell<HashMap<String, Rc<dyn ElementStyle>>>,
|
||||||
pub layouts: RefCell<HashMap<String, Rc<dyn LayoutType>>>,
|
pub layouts: RefCell<HashMap<String, Rc<dyn LayoutType>>>,
|
||||||
|
pub custom_styles: RefCell<HashMap<String, Rc<dyn CustomStyle>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LangParser {
|
impl LangParser {
|
||||||
|
@ -61,6 +65,7 @@ impl LangParser {
|
||||||
kernels: RefCell::new(HashMap::new()),
|
kernels: RefCell::new(HashMap::new()),
|
||||||
styles: RefCell::new(HashMap::new()),
|
styles: RefCell::new(HashMap::new()),
|
||||||
layouts: RefCell::new(HashMap::new()),
|
layouts: RefCell::new(HashMap::new()),
|
||||||
|
custom_styles: RefCell::new(HashMap::new()),
|
||||||
};
|
};
|
||||||
// Register rules
|
// Register rules
|
||||||
register(&mut s);
|
register(&mut s);
|
||||||
|
@ -316,21 +321,29 @@ impl KernelHolder for LangParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyleHolder 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()
|
self.styles.borrow_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutHolder for LangParser {
|
impl LayoutHolder for LangParser {
|
||||||
fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn crate::document::layout::LayoutType>>> {
|
fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn LayoutType>>> { self.layouts.borrow() }
|
||||||
self.layouts.borrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layouts_mut(
|
fn layouts_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn LayoutType>>> {
|
||||||
&self,
|
|
||||||
) -> RefMut<'_, HashMap<String, Rc<dyn crate::document::layout::LayoutType>>> {
|
|
||||||
self.layouts.borrow_mut()
|
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::Cursor;
|
||||||
use super::source::Source;
|
use super::source::Source;
|
||||||
use super::state::StateHolder;
|
use super::state::StateHolder;
|
||||||
|
use crate::document::customstyle::CustomStyleHolder;
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::element::Element;
|
use crate::document::element::Element;
|
||||||
use crate::document::layout::LayoutHolder;
|
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
|
/// Gets the colors for formatting errors
|
||||||
///
|
///
|
||||||
/// When colors are disabled, all colors should resolve to empty string
|
/// When colors are disabled, all colors should resolve to empty string
|
||||||
|
@ -52,7 +53,37 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
||||||
fn rules(&self) -> &Vec<Box<dyn Rule>>;
|
fn rules(&self) -> &Vec<Box<dyn Rule>>;
|
||||||
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
|
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
|
||||||
|
|
||||||
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String> {
|
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
|
// Error on duplicate rule
|
||||||
let rule_name = (*rule).name();
|
let rule_name = (*rule).name();
|
||||||
if let Err(e) = self.rules().iter().try_for_each(|rule| {
|
if let Err(e) = self.rules().iter().try_for_each(|rule| {
|
||||||
|
@ -89,21 +120,13 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> Ref<'_, StateHolder>;
|
fn update_matches(
|
||||||
fn state_mut(&self) -> RefMut<'_, StateHolder>;
|
&self,
|
||||||
|
cursor: &Cursor,
|
||||||
fn has_error(&self) -> bool;
|
matches: &mut Vec<(usize, Option<Box<dyn Any>>)>,
|
||||||
|
) -> (Cursor, Option<&Box<dyn Rule>>, Option<Box<dyn Any>>) {
|
||||||
// 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,
|
|
||||||
matches: &mut Vec<(usize, Option<Box<dyn Any>>)>,
|
|
||||||
) -> (Cursor, Option<&Box<dyn Rule>>, Option<Box<dyn Any>>) {
|
|
||||||
// Update matches
|
// Update matches
|
||||||
// TODO: Trivially parellalizable
|
// TODO: Trivially parellalizable
|
||||||
self.rules()
|
self.rules()
|
||||||
|
@ -111,11 +134,12 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
||||||
.zip(matches.iter_mut())
|
.zip(matches.iter_mut())
|
||||||
.for_each(|(rule, (matched_at, match_data))| {
|
.for_each(|(rule, (matched_at, match_data))| {
|
||||||
// Don't upate if not stepped over yet
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*matched_at, *match_data) = match rule.next_match(cursor) {
|
(*matched_at, *match_data) = match rule.next_match(self, cursor) {
|
||||||
None => (usize::MAX, None),
|
None => (usize::MAX, None),
|
||||||
Some((mut pos, mut data)) => {
|
Some((mut pos, mut data)) => {
|
||||||
// Check if escaped
|
// Check if escaped
|
||||||
|
@ -136,7 +160,7 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find next potential match
|
// 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),
|
Some((new_pos, new_data)) => (new_pos, new_data),
|
||||||
None => (usize::MAX, data), // Stop iterating
|
None => (usize::MAX, data), // Stop iterating
|
||||||
}
|
}
|
||||||
|
@ -166,18 +190,5 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder {
|
||||||
Some(&self.rules()[winner]),
|
Some(&self.rules()[winner]),
|
||||||
std::mem::replace(&mut matches[winner].1, None),
|
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 super::source::Token;
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use ariadne::Report;
|
use ariadne::Report;
|
||||||
|
use downcast_rs::impl_downcast;
|
||||||
|
use downcast_rs::Downcast;
|
||||||
use mlua::Function;
|
use mlua::Function;
|
||||||
use mlua::Lua;
|
use mlua::Lua;
|
||||||
|
|
||||||
|
@ -11,11 +13,11 @@ use std::any::Any;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub trait Rule {
|
pub trait Rule: Downcast {
|
||||||
/// Returns rule's name
|
/// Returns rule's name
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
/// Finds the next match starting from [`cursor`]
|
/// 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
|
/// Callback when rule matches
|
||||||
fn on_match<'a>(
|
fn on_match<'a>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -33,6 +35,7 @@ pub trait Rule {
|
||||||
/// Registers default layouts
|
/// Registers default layouts
|
||||||
fn register_layouts(&self, _parser: &dyn Parser) {}
|
fn register_layouts(&self, _parser: &dyn Parser) {}
|
||||||
}
|
}
|
||||||
|
impl_downcast!(Rule);
|
||||||
|
|
||||||
impl core::fmt::Debug for dyn Rule {
|
impl core::fmt::Debug for dyn Rule {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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) {}
|
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 {
|
fn name(&self) -> &'static str {
|
||||||
RegexRule::name(self)
|
RegexRule::name(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the next match starting from [`cursor`]
|
/// 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 content = cursor.source.content();
|
||||||
let mut found: Option<(usize, usize)> = None;
|
let mut found: Option<(usize, usize)> = None;
|
||||||
self.regexes().iter().enumerate().for_each(|(id, re)| {
|
self.regexes().iter().enumerate().for_each(|(id, re)| {
|
||||||
|
|
Loading…
Reference in a new issue