Compare commits

..

No commits in common. "57756d80480e9f0a313d4be2105c4e82fbb65de4" and "08ae603106743119c511d7be7a3e79f78b5de7c2" have entirely different histories.

13 changed files with 155 additions and 816 deletions

View file

@ -325,7 +325,7 @@ impl CodeRule {
) )
.unwrap(), .unwrap(),
], ],
properties: PropertyParser{ properties: props }, properties: PropertyParser::new(props),
} }
} }
} }

View file

@ -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,
}), }),
); );

View file

@ -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 }
}

View file

@ -258,7 +258,7 @@ impl MediaRule {
.multi_line(true) .multi_line(true)
.build() .build()
.unwrap()], .unwrap()],
properties: PropertyParser{ properties: props }, properties: PropertyParser::new(props),
} }
} }

View file

@ -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;

View file

@ -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),
} }
} }
} }

View file

@ -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),
} }
} }

View file

@ -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();

View file

@ -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,69 +15,64 @@ 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();
@ -102,19 +80,12 @@ impl State for StyleState {
//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()
.and_then(|last| {
Some((
last.location().source(),
last.location().end() - 1..last.location().end(),
))
})
.unwrap(); .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(
@ -126,24 +97,19 @@ impl State for StyleState {
.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_color(parser.colors().info),
)
.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().info))
)
.with_note("Styles cannot span multiple documents (i.e @import)") .with_note("Styles cannot span multiple documents (i.e @import)")
.finish(), .finish());
);
}); });
return reports; return result;
} }
} }
@ -162,8 +128,8 @@ impl StyleRule {
// Underline // Underline
Regex::new(r"__").unwrap(), Regex::new(r"__").unwrap(),
// Code // Code
Regex::new(r"`").unwrap(), Regex::new(r"`").unwrap()
], ]
} }
} }
} }
@ -172,27 +138,21 @@ 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

View file

@ -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 {

View file

@ -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;
} }
} }

View file

@ -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

View file

@ -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;
@ -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 {