Compare commits
No commits in common. "57756d80480e9f0a313d4be2105c4e82fbb65de4" and "08ae603106743119c511d7be7a3e79f78b5de7c2" have entirely different histories.
57756d8048
...
08ae603106
13 changed files with 155 additions and 816 deletions
|
@ -325,7 +325,7 @@ impl CodeRule {
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
],
|
],
|
||||||
properties: PropertyParser{ properties: props },
|
properties: PropertyParser::new(props),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ struct Graphviz {
|
||||||
pub dot: String,
|
pub dot: String,
|
||||||
pub layout: Layout,
|
pub layout: Layout,
|
||||||
pub width: String,
|
pub width: String,
|
||||||
|
pub caption: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_from_str(value: &str) -> Result<Layout, String> {
|
fn layout_from_str(value: &str) -> Result<Layout, String> {
|
||||||
|
@ -99,7 +100,6 @@ impl Cached for Graphviz {
|
||||||
fn key(&self) -> <Self as Cached>::Key {
|
fn key(&self) -> <Self as Cached>::Key {
|
||||||
let mut hasher = Sha512::new();
|
let mut hasher = Sha512::new();
|
||||||
hasher.input((self.layout as usize).to_be_bytes().as_slice());
|
hasher.input((self.layout as usize).to_be_bytes().as_slice());
|
||||||
hasher.input(self.width.as_bytes());
|
|
||||||
hasher.input(self.dot.as_bytes());
|
hasher.input(self.dot.as_bytes());
|
||||||
|
|
||||||
hasher.result_str()
|
hasher.result_str()
|
||||||
|
@ -179,7 +179,7 @@ impl GraphRule {
|
||||||
r"\[graph\](?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\[/graph\])?",
|
r"\[graph\](?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\[/graph\])?",
|
||||||
)
|
)
|
||||||
.unwrap()],
|
.unwrap()],
|
||||||
properties: PropertyParser{ properties: props },
|
properties: PropertyParser::new(props),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,6 +354,8 @@ impl RegexRule for GraphRule {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Caption
|
||||||
|
|
||||||
parser.push(
|
parser.push(
|
||||||
document,
|
document,
|
||||||
Box::new(Graphviz {
|
Box::new(Graphviz {
|
||||||
|
@ -361,6 +363,7 @@ impl RegexRule for GraphRule {
|
||||||
dot: graph_content,
|
dot: graph_content,
|
||||||
layout: graph_layout,
|
layout: graph_layout,
|
||||||
width: graph_width,
|
width: graph_width,
|
||||||
|
caption: None,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,617 +0,0 @@
|
||||||
use crate::compiler::compiler::Compiler;
|
|
||||||
use crate::compiler::compiler::Target;
|
|
||||||
use crate::document::document::Document;
|
|
||||||
use crate::document::element::ElemKind;
|
|
||||||
use crate::document::element::Element;
|
|
||||||
use crate::parser::parser::Parser;
|
|
||||||
use crate::parser::parser::ReportColors;
|
|
||||||
use crate::parser::rule::RegexRule;
|
|
||||||
use crate::parser::source::Source;
|
|
||||||
use crate::parser::source::Token;
|
|
||||||
use crate::parser::state::Scope;
|
|
||||||
use crate::parser::state::State;
|
|
||||||
use crate::parser::util::process_escaped;
|
|
||||||
use ariadne::Fmt;
|
|
||||||
use ariadne::Label;
|
|
||||||
use ariadne::Report;
|
|
||||||
use ariadne::ReportKind;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use mlua::Function;
|
|
||||||
use mlua::Lua;
|
|
||||||
use regex::Captures;
|
|
||||||
use regex::Match;
|
|
||||||
use regex::Regex;
|
|
||||||
use std::any::Any;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ops::Range;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub(crate) enum LayoutToken {
|
|
||||||
BEGIN,
|
|
||||||
NEXT,
|
|
||||||
END,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the type of a layout
|
|
||||||
pub trait LayoutType: core::fmt::Debug {
|
|
||||||
/// Name of the layout
|
|
||||||
fn name(&self) -> &'static str;
|
|
||||||
|
|
||||||
/// Parses layout properties
|
|
||||||
fn parse_properties(&self, properties: &str) -> Result<Option<Box<dyn Any>>, String>;
|
|
||||||
|
|
||||||
/// Expected number of blocks
|
|
||||||
fn expects(&self) -> Range<usize>;
|
|
||||||
|
|
||||||
/// Compile layout
|
|
||||||
fn compile(
|
|
||||||
&self,
|
|
||||||
token: LayoutToken,
|
|
||||||
id: usize,
|
|
||||||
properties: &Option<Box<dyn Any>>,
|
|
||||||
compiler: &Compiler,
|
|
||||||
document: &dyn Document,
|
|
||||||
) -> Result<String, String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
mod default_layouts {
|
|
||||||
use std::any::Any;
|
|
||||||
|
|
||||||
use crate::parser::util::Property;
|
|
||||||
use crate::parser::util::PropertyParser;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Centered;
|
|
||||||
|
|
||||||
impl LayoutType for Centered {
|
|
||||||
fn name(&self) -> &'static str { "Centered" }
|
|
||||||
|
|
||||||
fn expects(&self) -> Range<usize> { 1..1 }
|
|
||||||
|
|
||||||
fn parse_properties(&self, properties: &str) -> Result<Option<Box<dyn Any>>, String> {
|
|
||||||
if !properties.is_empty() {
|
|
||||||
return Err(format!("Layout {} excepts no properties", self.name()));
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile(
|
|
||||||
&self,
|
|
||||||
token: LayoutToken,
|
|
||||||
_id: usize,
|
|
||||||
_properties: &Option<Box<dyn Any>>,
|
|
||||||
compiler: &Compiler,
|
|
||||||
_document: &dyn Document,
|
|
||||||
) -> Result<String, String> {
|
|
||||||
match compiler.target() {
|
|
||||||
Target::HTML => match token {
|
|
||||||
LayoutToken::BEGIN => Ok(r#"<div class="centered">"#.to_string()),
|
|
||||||
LayoutToken::NEXT => panic!(),
|
|
||||||
LayoutToken::END => Ok(r#"</div>"#.to_string()),
|
|
||||||
},
|
|
||||||
_ => todo!(""),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Split {
|
|
||||||
properties: PropertyParser,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Split {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut properties = HashMap::new();
|
|
||||||
properties.insert(
|
|
||||||
"style".to_string(),
|
|
||||||
Property::new(
|
|
||||||
true,
|
|
||||||
"Additional style for the split".to_string(),
|
|
||||||
Some("".to_string()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
properties: PropertyParser { properties },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LayoutType for Split {
|
|
||||||
fn name(&self) -> &'static str { "Split" }
|
|
||||||
|
|
||||||
fn expects(&self) -> Range<usize> { 2..usize::MAX }
|
|
||||||
|
|
||||||
fn parse_properties(&self, properties: &str) -> Result<Option<Box<dyn Any>>, String> {
|
|
||||||
let props = if properties.is_empty() {
|
|
||||||
self.properties.default()
|
|
||||||
} else {
|
|
||||||
self.properties.parse(properties)
|
|
||||||
}
|
|
||||||
.map_err(|err| {
|
|
||||||
format!(
|
|
||||||
"Failed to parse properties for layout {}: {err}",
|
|
||||||
self.name()
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let style = props
|
|
||||||
.get("style", |_, value| -> Result<String, ()> {
|
|
||||||
Ok(value.clone())
|
|
||||||
})
|
|
||||||
.map_err(|err| format!("Failed to parse style: {err:#?}"))
|
|
||||||
.map(|(_, value)| value)?;
|
|
||||||
|
|
||||||
Ok(Some(Box::new(style)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile(
|
|
||||||
&self,
|
|
||||||
token: LayoutToken,
|
|
||||||
_id: usize,
|
|
||||||
properties: &Option<Box<dyn Any>>,
|
|
||||||
compiler: &Compiler,
|
|
||||||
_document: &dyn Document,
|
|
||||||
) -> Result<String, String> {
|
|
||||||
match compiler.target() {
|
|
||||||
Target::HTML => {
|
|
||||||
let style = match properties
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.downcast_ref::<String>()
|
|
||||||
.unwrap().as_str()
|
|
||||||
{
|
|
||||||
"" => "".to_string(),
|
|
||||||
str => format!(r#" style={}"#, Compiler::sanitize(compiler.target(), str))
|
|
||||||
};
|
|
||||||
match token {
|
|
||||||
LayoutToken::BEGIN => Ok(format!(
|
|
||||||
r#"<div class="split-container"><div class="split"{style}>"#
|
|
||||||
)),
|
|
||||||
LayoutToken::NEXT => Ok(format!(r#"</div><div class="split"{style}>"#)),
|
|
||||||
LayoutToken::END => Ok(r#"</div></div>"#.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => todo!(""),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Layout {
|
|
||||||
pub(self) location: Token,
|
|
||||||
pub(self) layout: Rc<dyn LayoutType>,
|
|
||||||
pub(self) id: usize,
|
|
||||||
pub(self) token: LayoutToken,
|
|
||||||
pub(self) properties: Option<Box<dyn Any>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element for Layout {
|
|
||||||
fn location(&self) -> &Token { &self.location }
|
|
||||||
fn kind(&self) -> ElemKind { ElemKind::Block }
|
|
||||||
fn element_name(&self) -> &'static str { "Layout" }
|
|
||||||
fn to_string(&self) -> String { format!("{self:#?}") }
|
|
||||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
|
||||||
self.layout
|
|
||||||
.compile(self.token, self.id, &self.properties, compiler, document)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LayoutState {
|
|
||||||
/// The layout stack
|
|
||||||
pub(self) stack: Vec<(Vec<Token>, Rc<dyn LayoutType>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State for LayoutState {
|
|
||||||
fn scope(&self) -> Scope { Scope::DOCUMENT }
|
|
||||||
|
|
||||||
fn on_remove<'a>(
|
|
||||||
&self,
|
|
||||||
parser: &dyn Parser,
|
|
||||||
document: &dyn Document,
|
|
||||||
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
|
||||||
let mut reports = vec![];
|
|
||||||
|
|
||||||
let doc_borrow = document.content().borrow();
|
|
||||||
let at = doc_borrow.last().unwrap().location();
|
|
||||||
|
|
||||||
for (tokens, layout_type) in &self.stack {
|
|
||||||
let start = tokens.first().unwrap();
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, start.source(), start.start())
|
|
||||||
.with_message("Unterminated Layout")
|
|
||||||
//.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(
|
|
||||||
Label::new((start.source(), start.range.start + 1..start.range.end))
|
|
||||||
.with_order(1)
|
|
||||||
.with_message(format!(
|
|
||||||
"Layout {} stars here",
|
|
||||||
layout_type.name().fg(parser.colors().info)
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.with_label(
|
|
||||||
Label::new((at.source(), at.range.clone()))
|
|
||||||
.with_order(2)
|
|
||||||
.with_message("Document ends here".to_string())
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LayoutRule {
|
|
||||||
re: [Regex; 3],
|
|
||||||
layouts: HashMap<String, Rc<dyn LayoutType>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LayoutRule {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut layouts: HashMap<String, Rc<dyn LayoutType>> = HashMap::new();
|
|
||||||
|
|
||||||
let layout_centered = default_layouts::Centered::default();
|
|
||||||
layouts.insert(layout_centered.name().to_string(), Rc::new(layout_centered));
|
|
||||||
|
|
||||||
let layout_split = default_layouts::Split::default();
|
|
||||||
layouts.insert(layout_split.name().to_string(), Rc::new(layout_split));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
re: [
|
|
||||||
Regex::new(r"(?:^|\n)#\+LAYOUT_BEGIN(?:\[((?:\\.|[^\\\\])*?)\])?(.*)").unwrap(),
|
|
||||||
Regex::new(r"(?:^|\n)#\+LAYOUT_NEXT(?:\[((?:\\.|[^\\\\])*?)\])?(?:$|\n)").unwrap(),
|
|
||||||
Regex::new(r"(?:^|\n)#\+LAYOUT_END(?:\[((?:\\.|[^\\\\])*?)\])?(?:$|\n)").unwrap(),
|
|
||||||
],
|
|
||||||
layouts,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_properties<'a>(
|
|
||||||
colors: &ReportColors,
|
|
||||||
token: &Token,
|
|
||||||
layout_type: Rc<dyn LayoutType>,
|
|
||||||
properties: Option<Match>,
|
|
||||||
) -> Result<Option<Box<dyn Any>>, Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
|
||||||
match properties {
|
|
||||||
None => match layout_type.parse_properties("") {
|
|
||||||
Ok(props) => Ok(props),
|
|
||||||
Err(err) => Err(
|
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
|
||||||
.with_message("Unable to parse layout properties")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), token.range.clone()))
|
|
||||||
.with_message(err)
|
|
||||||
.with_color(colors.error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Some(props) => {
|
|
||||||
let trimmed = props.as_str().trim_start().trim_end();
|
|
||||||
let content = process_escaped('\\', "]", trimmed);
|
|
||||||
match layout_type.parse_properties(content.as_str()) {
|
|
||||||
Ok(props) => Ok(props),
|
|
||||||
Err(err) => {
|
|
||||||
Err(
|
|
||||||
Report::build(ReportKind::Error, token.source(), props.start())
|
|
||||||
.with_message("Unable to parse layout properties")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), props.range()))
|
|
||||||
.with_message(err)
|
|
||||||
.with_color(colors.error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref STATE_NAME: String = "elements.layout".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RegexRule for LayoutRule {
|
|
||||||
fn name(&self) -> &'static str { "Layout" }
|
|
||||||
|
|
||||||
fn regexes(&self) -> &[regex::Regex] { &self.re }
|
|
||||||
|
|
||||||
fn on_regex_match(
|
|
||||||
&self,
|
|
||||||
index: usize,
|
|
||||||
parser: &dyn Parser,
|
|
||||||
document: &dyn Document,
|
|
||||||
token: Token,
|
|
||||||
matches: Captures,
|
|
||||||
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
|
||||||
let mut reports = vec![];
|
|
||||||
|
|
||||||
let query = parser.state().query(&STATE_NAME);
|
|
||||||
let state = match query {
|
|
||||||
Some(state) => state,
|
|
||||||
None => {
|
|
||||||
// Insert as a new state
|
|
||||||
match parser.state_mut().insert(
|
|
||||||
STATE_NAME.clone(),
|
|
||||||
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
|
|
||||||
) {
|
|
||||||
Err(_) => panic!("Unknown error"),
|
|
||||||
Ok(state) => state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if index == 0
|
|
||||||
// BEGIN_LAYOUT
|
|
||||||
{
|
|
||||||
match matches.get(2) {
|
|
||||||
None => {
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
|
||||||
.with_message("Missing Layout Name")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), token.range.clone()))
|
|
||||||
.with_message(format!(
|
|
||||||
"Missing layout name after `{}`",
|
|
||||||
"#+BEGIN_LAYOUT".fg(parser.colors().highlight)
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
Some(name) => {
|
|
||||||
let trimmed = name.as_str().trim_start().trim_end();
|
|
||||||
if name.as_str().is_empty() || trimmed.is_empty()
|
|
||||||
// Empty name
|
|
||||||
{
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
|
||||||
.with_message("Empty Layout Name")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), token.range.clone()))
|
|
||||||
.with_message(format!(
|
|
||||||
"Empty layout name after `{}`",
|
|
||||||
"#+BEGIN_LAYOUT".fg(parser.colors().highlight)
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
} else if !name.as_str().chars().next().unwrap().is_whitespace()
|
|
||||||
// Missing space
|
|
||||||
{
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
|
||||||
.with_message("Invalid Layout Name")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), name.range()))
|
|
||||||
.with_message(format!(
|
|
||||||
"Missing a space before layout name `{}`",
|
|
||||||
name.as_str().fg(parser.colors().highlight)
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get layout
|
|
||||||
let layout_type = match self.layouts.get(trimmed) {
|
|
||||||
None => {
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
|
||||||
.with_message("Unknown Layout")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), name.range()))
|
|
||||||
.with_message(format!(
|
|
||||||
"Cannot find layout `{}`",
|
|
||||||
trimmed.fg(parser.colors().highlight)
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
Some(layout_type) => layout_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse properties
|
|
||||||
let properties = match LayoutRule::parse_properties(
|
|
||||||
parser.colors(),
|
|
||||||
&token,
|
|
||||||
layout_type.clone(),
|
|
||||||
matches.get(1),
|
|
||||||
) {
|
|
||||||
Ok(props) => props,
|
|
||||||
Err(rep) => {
|
|
||||||
reports.push(rep);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
parser.push(
|
|
||||||
document,
|
|
||||||
Box::new(Layout {
|
|
||||||
location: token.clone(),
|
|
||||||
layout: layout_type.clone(),
|
|
||||||
id: 0,
|
|
||||||
token: LayoutToken::BEGIN,
|
|
||||||
properties,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
state
|
|
||||||
.borrow_mut()
|
|
||||||
.downcast_mut::<LayoutState>()
|
|
||||||
.map_or_else(
|
|
||||||
|| panic!("Invalid state at: `{}`", STATE_NAME.as_str()),
|
|
||||||
|s| s.stack.push((vec![token.clone()], layout_type.clone())),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (id, token_type, layout_type, properties) = if index == 1
|
|
||||||
// LAYOUT_NEXT
|
|
||||||
{
|
|
||||||
let mut state_borrow = state.borrow_mut();
|
|
||||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
|
||||||
|
|
||||||
let (tokens, layout_type) = match state.stack.last_mut() {
|
|
||||||
None => {
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
|
||||||
.with_message("Invalid #+LAYOUT_NEXT")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), token.range.clone()))
|
|
||||||
.with_message("No active layout found".to_string())
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
Some(last) => last,
|
|
||||||
};
|
|
||||||
|
|
||||||
if layout_type.expects().end < tokens.len()
|
|
||||||
// Too many blocks
|
|
||||||
{
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
|
||||||
.with_message("Unexpected #+LAYOUT_NEXT")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), token.range.clone()))
|
|
||||||
.with_message(format!(
|
|
||||||
"Layout expects a maximum of {} blocks, currently at {}",
|
|
||||||
layout_type.expects().end.fg(parser.colors().info),
|
|
||||||
tokens.len().fg(parser.colors().info),
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse properties
|
|
||||||
let properties = match LayoutRule::parse_properties(
|
|
||||||
parser.colors(),
|
|
||||||
&token,
|
|
||||||
layout_type.clone(),
|
|
||||||
matches.get(1),
|
|
||||||
) {
|
|
||||||
Ok(props) => props,
|
|
||||||
Err(rep) => {
|
|
||||||
reports.push(rep);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.push(token.clone());
|
|
||||||
(
|
|
||||||
tokens.len() - 1,
|
|
||||||
LayoutToken::NEXT,
|
|
||||||
layout_type.clone(),
|
|
||||||
properties,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// LAYOUT_END
|
|
||||||
let mut state_borrow = state.borrow_mut();
|
|
||||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
|
||||||
|
|
||||||
let (tokens, layout_type) = match state.stack.last_mut() {
|
|
||||||
None => {
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
|
||||||
.with_message("Invalid #+LAYOUT_END")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), token.range.clone()))
|
|
||||||
.with_message("No active layout found".to_string())
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
Some(last) => last,
|
|
||||||
};
|
|
||||||
|
|
||||||
if layout_type.expects().start > tokens.len()
|
|
||||||
// Not enough blocks
|
|
||||||
{
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
|
||||||
.with_message("Unexpected #+LAYOUT_END")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), token.range.clone()))
|
|
||||||
.with_message(format!(
|
|
||||||
"Layout expects a minimum of {} blocks, currently at {}",
|
|
||||||
layout_type.expects().start.fg(parser.colors().info),
|
|
||||||
tokens.len().fg(parser.colors().info),
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse properties
|
|
||||||
let properties = match LayoutRule::parse_properties(
|
|
||||||
parser.colors(),
|
|
||||||
&token,
|
|
||||||
layout_type.clone(),
|
|
||||||
matches.get(1),
|
|
||||||
) {
|
|
||||||
Ok(props) => props,
|
|
||||||
Err(rep) => {
|
|
||||||
reports.push(rep);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let layout_type = layout_type.clone();
|
|
||||||
let id = tokens.len();
|
|
||||||
state.stack.pop();
|
|
||||||
(id, LayoutToken::END, layout_type, properties)
|
|
||||||
};
|
|
||||||
|
|
||||||
parser.push(
|
|
||||||
document,
|
|
||||||
Box::new(Layout {
|
|
||||||
location: token,
|
|
||||||
layout: layout_type,
|
|
||||||
id,
|
|
||||||
token: token_type,
|
|
||||||
properties,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
|
||||||
}
|
|
|
@ -258,7 +258,7 @@ impl MediaRule {
|
||||||
.multi_line(true)
|
.multi_line(true)
|
||||||
.build()
|
.build()
|
||||||
.unwrap()],
|
.unwrap()],
|
||||||
properties: PropertyParser{ properties: props },
|
properties: PropertyParser::new(props),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
pub mod code;
|
|
||||||
pub mod comment;
|
|
||||||
pub mod graphviz;
|
|
||||||
pub mod import;
|
|
||||||
pub mod layout;
|
|
||||||
pub mod link;
|
|
||||||
pub mod list;
|
|
||||||
pub mod media;
|
|
||||||
pub mod paragraph;
|
|
||||||
pub mod raw;
|
|
||||||
pub mod reference;
|
|
||||||
pub mod registrar;
|
pub mod registrar;
|
||||||
pub mod script;
|
|
||||||
pub mod section;
|
|
||||||
pub mod style;
|
|
||||||
pub mod tex;
|
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
pub mod comment;
|
||||||
|
pub mod paragraph;
|
||||||
pub mod variable;
|
pub mod variable;
|
||||||
|
pub mod import;
|
||||||
|
pub mod script;
|
||||||
|
pub mod list;
|
||||||
|
pub mod style;
|
||||||
|
pub mod section;
|
||||||
|
pub mod link;
|
||||||
|
pub mod code;
|
||||||
|
pub mod tex;
|
||||||
|
pub mod graphviz;
|
||||||
|
pub mod raw;
|
||||||
|
pub mod media;
|
||||||
|
pub mod reference;
|
||||||
|
|
|
@ -33,6 +33,16 @@ struct Raw {
|
||||||
pub(self) content: String,
|
pub(self) content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Raw {
|
||||||
|
fn new(location: Token, kind: ElemKind, content: String) -> Self {
|
||||||
|
Self {
|
||||||
|
location,
|
||||||
|
kind,
|
||||||
|
content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Element for Raw {
|
impl Element for Raw {
|
||||||
fn location(&self) -> &Token { &self.location }
|
fn location(&self) -> &Token { &self.location }
|
||||||
fn kind(&self) -> ElemKind { self.kind.clone() }
|
fn kind(&self) -> ElemKind { self.kind.clone() }
|
||||||
|
@ -67,7 +77,7 @@ impl RawRule {
|
||||||
Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?")
|
Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
],
|
],
|
||||||
properties: PropertyParser{ properties: props },
|
properties: PropertyParser::new(props),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ impl ReferenceRule {
|
||||||
);
|
);
|
||||||
Self {
|
Self {
|
||||||
re: [Regex::new(r"§\{(.*)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
|
re: [Regex::new(r"§\{(.*)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
|
||||||
properties: PropertyParser{ properties: props },
|
properties: PropertyParser::new(props),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ use super::code::CodeRule;
|
||||||
use super::comment::CommentRule;
|
use super::comment::CommentRule;
|
||||||
use super::graphviz::GraphRule;
|
use super::graphviz::GraphRule;
|
||||||
use super::import::ImportRule;
|
use super::import::ImportRule;
|
||||||
use super::layout::LayoutRule;
|
|
||||||
use super::link::LinkRule;
|
use super::link::LinkRule;
|
||||||
use super::list::ListRule;
|
use super::list::ListRule;
|
||||||
use super::media::MediaRule;
|
use super::media::MediaRule;
|
||||||
|
@ -32,7 +31,6 @@ pub fn register<P: Parser>(parser: &mut P) {
|
||||||
parser.add_rule(Box::new(TexRule::new()), None).unwrap();
|
parser.add_rule(Box::new(TexRule::new()), None).unwrap();
|
||||||
parser.add_rule(Box::new(GraphRule::new()), None).unwrap();
|
parser.add_rule(Box::new(GraphRule::new()), None).unwrap();
|
||||||
parser.add_rule(Box::new(MediaRule::new()), None).unwrap();
|
parser.add_rule(Box::new(MediaRule::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(SectionRule::new()), None).unwrap();
|
parser.add_rule(Box::new(SectionRule::new()), None).unwrap();
|
||||||
|
|
|
@ -1,27 +1,10 @@
|
||||||
use crate::compiler::compiler::Compiler;
|
use mlua::{Function, Lua};
|
||||||
use crate::compiler::compiler::Target;
|
use regex::{Captures, Regex};
|
||||||
use crate::document::document::Document;
|
use crate::{compiler::compiler::{Compiler, Target}, document::{document::{DocumentAccessors, Document}, element::{ElemKind, Element}}, parser::{parser::Parser, rule::RegexRule, source::{Source, Token}, state::State}};
|
||||||
use crate::document::document::DocumentAccessors;
|
use ariadne::{Fmt, Label, Report, ReportKind};
|
||||||
use crate::document::element::ElemKind;
|
|
||||||
use crate::document::element::Element;
|
|
||||||
use crate::parser::parser::Parser;
|
|
||||||
use crate::parser::rule::RegexRule;
|
|
||||||
use crate::parser::source::Source;
|
|
||||||
use crate::parser::source::Token;
|
|
||||||
use crate::parser::state::Scope;
|
use crate::parser::state::Scope;
|
||||||
use crate::parser::state::State;
|
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||||
use ariadne::Fmt;
|
|
||||||
use ariadne::Label;
|
|
||||||
use ariadne::Report;
|
|
||||||
use ariadne::ReportKind;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use mlua::Function;
|
|
||||||
use mlua::Lua;
|
|
||||||
use regex::Captures;
|
|
||||||
use regex::Regex;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ops::Range;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use super::paragraph::Paragraph;
|
use super::paragraph::Paragraph;
|
||||||
|
|
||||||
|
@ -32,118 +15,101 @@ pub struct Style {
|
||||||
close: bool,
|
close: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style
|
||||||
|
{
|
||||||
pub fn new(location: Token, kind: usize, close: bool) -> Self {
|
pub fn new(location: Token, kind: usize, close: bool) -> Self {
|
||||||
Self {
|
Self { location, kind, close }
|
||||||
location,
|
|
||||||
kind,
|
|
||||||
close,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Style {
|
impl Element for Style
|
||||||
|
{
|
||||||
fn location(&self) -> &Token { &self.location }
|
fn location(&self) -> &Token { &self.location }
|
||||||
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
||||||
fn element_name(&self) -> &'static str { "Section" }
|
fn element_name(&self) -> &'static str { "Section" }
|
||||||
fn to_string(&self) -> String { format!("{self:#?}") }
|
fn to_string(&self) -> String { format!("{self:#?}") }
|
||||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||||
match compiler.target() {
|
match compiler.target()
|
||||||
|
{
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
Ok([
|
Ok([
|
||||||
// Bold
|
// Bold
|
||||||
"<b>", "</b>", // Italic
|
"<b>", "</b>",
|
||||||
"<i>", "</i>", // Underline
|
// Italic
|
||||||
"<u>", "</u>", // Code
|
"<i>", "</i>",
|
||||||
|
// Underline
|
||||||
|
"<u>", "</u>",
|
||||||
|
// Code
|
||||||
"<em>", "</em>",
|
"<em>", "</em>",
|
||||||
][self.kind * 2 + self.close as usize]
|
][self.kind*2 + self.close as usize].to_string())
|
||||||
.to_string())
|
|
||||||
}
|
}
|
||||||
Target::LATEX => Err("Unimplemented compiler".to_string()),
|
Target::LATEX => Err("Unimplemented compiler".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StyleState {
|
struct StyleState
|
||||||
toggled: [Option<Token>; 4],
|
{
|
||||||
|
toggled: [Option<Token>; 4]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyleState {
|
impl StyleState {
|
||||||
const NAMES: [&'static str; 4] = ["Bold", "Italic", "Underline", "Code"];
|
const NAMES : [&'static str; 4] = ["Bold", "Italic", "Underline", "Code"];
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self { toggled: [None, None, None, None] }
|
||||||
toggled: [None, None, None, None],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State for StyleState {
|
impl State for StyleState
|
||||||
|
{
|
||||||
fn scope(&self) -> Scope { Scope::PARAGRAPH }
|
fn scope(&self) -> Scope { Scope::PARAGRAPH }
|
||||||
|
|
||||||
fn on_remove<'a>(
|
fn on_remove<'a>(&self, parser: &dyn Parser, document: &dyn Document) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
||||||
&self,
|
let mut result = Vec::new();
|
||||||
parser: &dyn Parser,
|
|
||||||
document: &dyn Document,
|
|
||||||
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
|
||||||
let mut reports = vec![];
|
|
||||||
|
|
||||||
self.toggled
|
self.toggled
|
||||||
.iter()
|
.iter()
|
||||||
.zip(StyleState::NAMES)
|
.zip(StyleState::NAMES)
|
||||||
.for_each(|(token, name)| {
|
.for_each(|(token, name)|
|
||||||
if token.is_none() {
|
{
|
||||||
return;
|
if token.is_none() { return } // 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 range = range.as_ref().unwrap();
|
||||||
|
|
||||||
//let active_range = range.start .. paragraph.location().end()-1;
|
//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.last()
|
||||||
.content
|
.and_then(|last| Some((last.location().source(), last.location().end()-1 .. last.location().end())))
|
||||||
.last()
|
.unwrap();
|
||||||
.and_then(|last| {
|
|
||||||
Some((
|
|
||||||
last.location().source(),
|
|
||||||
last.location().end() - 1..last.location().end(),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// TODO: Allow style to span multiple documents if they don't break paragraph.
|
// TODO: Allow style to span multiple documents if they don't break paragraph.
|
||||||
reports.push(
|
result.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(
|
//.with_label(
|
||||||
// Label::new((document.source(), active_range.clone()))
|
// Label::new((document.source(), active_range.clone()))
|
||||||
// .with_order(0)
|
// .with_order(0)
|
||||||
// .with_message(format!("Style {} is not terminated before the end of paragraph",
|
// .with_message(format!("Style {} is not terminated before the end of paragraph",
|
||||||
// name.fg(parser.colors().info)))
|
// name.fg(parser.colors().info)))
|
||||||
// .with_color(parser.colors().error))
|
// .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)
|
||||||
.with_message(format!(
|
.with_message(format!("Style {} starts here",
|
||||||
"Style {} starts here",
|
name.fg(parser.colors().info)))
|
||||||
name.fg(parser.colors().info)
|
.with_color(parser.colors().info))
|
||||||
))
|
.with_label(
|
||||||
.with_color(parser.colors().info),
|
Label::new(paragraph_end)
|
||||||
)
|
.with_order(1)
|
||||||
.with_label(
|
.with_message(format!("Paragraph ends here"))
|
||||||
Label::new(paragraph_end)
|
.with_color(parser.colors().info))
|
||||||
.with_order(1)
|
.with_note("Styles cannot span multiple documents (i.e @import)")
|
||||||
.with_message(format!("Paragraph ends here"))
|
.finish());
|
||||||
.with_color(parser.colors().info),
|
});
|
||||||
)
|
|
||||||
.with_note("Styles cannot span multiple documents (i.e @import)")
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return reports;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,37 +128,31 @@ impl StyleRule {
|
||||||
// Underline
|
// Underline
|
||||||
Regex::new(r"__").unwrap(),
|
Regex::new(r"__").unwrap(),
|
||||||
// Code
|
// Code
|
||||||
Regex::new(r"`").unwrap(),
|
Regex::new(r"`").unwrap()
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref STATE_NAME: String = "elements.style".to_string();
|
static ref STATE_NAME : String = "elements.style".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegexRule for StyleRule {
|
impl RegexRule for StyleRule
|
||||||
|
{
|
||||||
fn name(&self) -> &'static str { "Style" }
|
fn name(&self) -> &'static str { "Style" }
|
||||||
|
|
||||||
fn regexes(&self) -> &[regex::Regex] { &self.re }
|
fn regexes(&self) -> &[regex::Regex] { &self.re }
|
||||||
|
|
||||||
fn on_regex_match(
|
fn on_regex_match(&self, index: usize, parser: &dyn Parser, document: &dyn Document, token: Token, _matches: Captures) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||||
&self,
|
let result = vec![];
|
||||||
index: usize,
|
|
||||||
parser: &dyn Parser,
|
|
||||||
document: &dyn Document,
|
|
||||||
token: Token,
|
|
||||||
_matches: Captures,
|
|
||||||
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
|
||||||
let query = parser.state().query(&STATE_NAME);
|
let query = parser.state().query(&STATE_NAME);
|
||||||
let state = match query {
|
let state = match query
|
||||||
|
{
|
||||||
Some(state) => state,
|
Some(state) => state,
|
||||||
None => {
|
None => { // Insert as a new state
|
||||||
// Insert as a new state
|
match parser.state_mut().insert(STATE_NAME.clone(), Rc::new(RefCell::new(StyleState::new())))
|
||||||
match parser
|
|
||||||
.state_mut()
|
|
||||||
.insert(STATE_NAME.clone(), Rc::new(RefCell::new(StyleState::new())))
|
|
||||||
{
|
{
|
||||||
Err(_) => panic!("Unknown error"),
|
Err(_) => panic!("Unknown error"),
|
||||||
Ok(state) => state,
|
Ok(state) => state,
|
||||||
|
@ -200,23 +160,26 @@ impl RegexRule for StyleRule {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(style_state) = state.borrow_mut().as_any_mut().downcast_mut::<StyleState>() {
|
if let Some(style_state) = state
|
||||||
style_state.toggled[index] = style_state.toggled[index]
|
.borrow_mut()
|
||||||
.clone()
|
.as_any_mut()
|
||||||
.map_or(Some(token.clone()), |_| None);
|
.downcast_mut::<StyleState>()
|
||||||
parser.push(
|
{
|
||||||
document,
|
style_state.toggled[index] = style_state.toggled[index].clone().map_or(Some(token.clone()), |_| None);
|
||||||
Box::new(Style::new(
|
parser.push(document, Box::new(
|
||||||
|
Style::new(
|
||||||
token.clone(),
|
token.clone(),
|
||||||
index,
|
index,
|
||||||
!style_state.toggled[index].is_some(),
|
!style_state.toggled[index].is_some()
|
||||||
)),
|
)
|
||||||
);
|
));
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
panic!("Invalid state at `{}`", STATE_NAME.as_str());
|
panic!("Invalid state at `{}`", STATE_NAME.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return vec![];
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -189,7 +189,7 @@ impl Element for Tex {
|
||||||
Tex::format_latex(&fontsize, &preamble, &format!("{prepend}{}", self.tex))
|
Tex::format_latex(&fontsize, &preamble, &format!("{prepend}{}", self.tex))
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = if let Some(mut con) = compiler.cache() {
|
let mut result = if let Some(mut con) = compiler.cache() {
|
||||||
match latex.cached(&mut con, |s| s.latex_to_svg(&exec, &fontsize)) {
|
match latex.cached(&mut con, |s| s.latex_to_svg(&exec, &fontsize)) {
|
||||||
Ok(s) => Ok(s),
|
Ok(s) => Ok(s),
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
|
@ -253,7 +253,7 @@ impl TexRule {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Regex::new(r"\$(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\$)?").unwrap(),
|
Regex::new(r"\$(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\$)?").unwrap(),
|
||||||
],
|
],
|
||||||
properties: PropertyParser{ properties: props },
|
properties: PropertyParser::new(props),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ impl RegexRule for TexRule {
|
||||||
);
|
);
|
||||||
return reports;
|
return reports;
|
||||||
}
|
}
|
||||||
PropertyMapError::NotFoundError(_) => {
|
PropertyMapError::NotFoundError(err) => {
|
||||||
if index == 1 {
|
if index == 1 {
|
||||||
TexKind::Inline
|
TexKind::Inline
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -199,7 +199,7 @@ fn main() -> ExitCode {
|
||||||
let input_meta = match std::fs::metadata(&input) {
|
let input_meta = match std::fs::metadata(&input) {
|
||||||
Ok(meta) => meta,
|
Ok(meta) => meta,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Unable to get metadata for input `{input}`: {e}");
|
eprintln!("Unable to get metadata for input: `{input}`");
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -218,7 +218,7 @@ fn main() -> ExitCode {
|
||||||
match std::fs::metadata(&output) {
|
match std::fs::metadata(&output) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Unable to get metadata for output `{output}`: {e}");
|
eprintln!("Unable to get metadata for output: `{output}`");
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ fn main() -> ExitCode {
|
||||||
let output_meta = match std::fs::metadata(&output) {
|
let output_meta = match std::fs::metadata(&output) {
|
||||||
Ok(meta) => meta,
|
Ok(meta) => meta,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Unable to get metadata for output `{output}`: {e}");
|
eprintln!("Unable to get metadata for output: `{output}`");
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -302,7 +302,7 @@ fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Faield to get metadata for `{entry:#?}`: {e}");
|
eprintln!("Faield to get metadata for `{entry:#?}`");
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,12 +229,13 @@ impl<'a> PropertyMap<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PropertyParser {
|
pub struct PropertyParser {
|
||||||
pub properties: HashMap<String, Property>,
|
properties: HashMap<String, Property>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PropertyParser {
|
impl PropertyParser {
|
||||||
|
pub fn new(properties: HashMap<String, Property>) -> Self { Self { properties } }
|
||||||
|
|
||||||
/// Attempts to build a default propertymap
|
/// Attempts to build a default propertymap
|
||||||
///
|
///
|
||||||
/// Returns an error if at least one [`Property`] is required and doesn't provide a default
|
/// Returns an error if at least one [`Property`] is required and doesn't provide a default
|
||||||
|
@ -270,7 +271,7 @@ impl PropertyParser {
|
||||||
/// properties.insert("width".to_string(),
|
/// properties.insert("width".to_string(),
|
||||||
/// Property::new(true, "Width of the element in em".to_string(), None));
|
/// Property::new(true, "Width of the element in em".to_string(), None));
|
||||||
///
|
///
|
||||||
/// let parser = PropertyParser { properties };
|
/// let parser = PropertyParser::new(properties);
|
||||||
/// let pm = parser.parse("width=15").unwrap();
|
/// let pm = parser.parse("width=15").unwrap();
|
||||||
///
|
///
|
||||||
/// assert_eq!(pm.get("width", |_, s| s.parse::<i32>()).unwrap().1, 15);
|
/// assert_eq!(pm.get("width", |_, s| s.parse::<i32>()).unwrap().1, 15);
|
||||||
|
@ -490,7 +491,7 @@ mod tests {
|
||||||
Property::new(false, "Weight in %".to_string(), Some("0.42".to_string())),
|
Property::new(false, "Weight in %".to_string(), Some("0.42".to_string())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let parser = PropertyParser { properties };
|
let parser = PropertyParser::new(properties);
|
||||||
let pm = parser.parse("width=15,length=-10").unwrap();
|
let pm = parser.parse("width=15,length=-10").unwrap();
|
||||||
|
|
||||||
// Ok
|
// Ok
|
||||||
|
|
56
style.css
56
style.css
|
@ -17,24 +17,6 @@ body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Layouts */
|
|
||||||
div.centered {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.split-container {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.split-container > div.split {
|
|
||||||
flex: 1;
|
|
||||||
flex-shrink: 0;
|
|
||||||
overflow-x: auto;
|
|
||||||
|
|
||||||
margin: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles */
|
/* Styles */
|
||||||
em {
|
em {
|
||||||
padding-left: .1em;
|
padding-left: .1em;
|
||||||
|
@ -60,22 +42,22 @@ a.inline-code {
|
||||||
.navbar {
|
.navbar {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: max(calc((100vw - 99ch) / 2 - 15vw), 24ch);
|
width: max(calc((100vw - 99ch) / 2 - 15vw), 24ch);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overscroll-behavior-y: contain;
|
overscroll-behavior-y: contain;
|
||||||
|
|
||||||
background-color: #161a26;
|
background-color: #161a26;
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
|
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,15 +108,15 @@ a.inline-code {
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar summary:before {
|
.navbar summary:before {
|
||||||
content: "+";
|
content: "+";
|
||||||
color: #ffb454;
|
color: #ffb454;
|
||||||
float: left;
|
float: left;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar details[open] > summary:before {
|
.navbar details[open] > summary:before {
|
||||||
content: "–";
|
content: "–";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code blocks */
|
/* Code blocks */
|
||||||
|
@ -144,10 +126,11 @@ div.code-block-title {
|
||||||
}
|
}
|
||||||
|
|
||||||
div.code-block-content {
|
div.code-block-content {
|
||||||
max-height: 38em;
|
max-height: 20em;
|
||||||
margin-bottom: 0.2em;
|
margin-bottom: 0.2em;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
overflow: scroll;
|
overflow: auto;
|
||||||
|
|
||||||
background-color: #0f141a;
|
background-color: #0f141a;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +143,6 @@ div.code-block-content td {
|
||||||
div.code-block-content pre {
|
div.code-block-content pre {
|
||||||
border: 0;
|
border: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
tab-size: 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.code-block-content .code-block-gutter {
|
div.code-block-content .code-block-gutter {
|
||||||
|
@ -238,6 +220,6 @@ a:hover.medium-ref img {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
box-shadow: 0px 0px 6px 2px rgba(0, 0, 0, 0.75);
|
box-shadow: 0px 0px 6px 2px rgba(0,0,0,0.75);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue