Better style parsing
This commit is contained in:
parent
48d2064d0c
commit
8489796510
3 changed files with 197 additions and 90 deletions
|
@ -31,9 +31,7 @@ pub trait StyleHolder {
|
||||||
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>>;
|
fn 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_registered(&self, style_key: &str) -> bool {
|
fn is_registered(&self, style_key: &str) -> bool { self.styles().contains_key(style_key) }
|
||||||
self.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
|
||||||
|
@ -43,7 +41,7 @@ pub trait StyleHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the [`style`]
|
/// Sets the [`style`]
|
||||||
fn set_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.styles_mut().insert(style.key().to_string(), style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,134 +1,243 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use ariadne::{Fmt, Label, Report, ReportKind};
|
use ariadne::Fmt;
|
||||||
use regex::{Captures, Regex};
|
use ariadne::Label;
|
||||||
|
use ariadne::Report;
|
||||||
|
use ariadne::ReportKind;
|
||||||
|
use regex::Captures;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::{self};
|
use crate::document::{self};
|
||||||
use crate::parser::parser::Parser;
|
use crate::parser::parser::Parser;
|
||||||
use crate::parser::rule::RegexRule;
|
use crate::parser::rule::RegexRule;
|
||||||
|
use crate::parser::rule::Rule;
|
||||||
|
use crate::parser::source::Cursor;
|
||||||
use crate::parser::source::Source;
|
use crate::parser::source::Source;
|
||||||
use crate::parser::source::Token;
|
use crate::parser::source::Token;
|
||||||
|
|
||||||
use super::variable::VariableRule;
|
use super::variable::VariableRule;
|
||||||
|
|
||||||
pub struct ElemStyleRule {
|
pub struct ElemStyleRule {
|
||||||
re: [Regex; 1],
|
start_re: Regex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElemStyleRule {
|
impl ElemStyleRule {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
re: [Regex::new(r"(?:^|\n)@@(.*?)=((?:\\\n|.)*)").unwrap()],
|
start_re: Regex::new(r"(?:^|\n)@@(.*?)=\s*\{").unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds the json substring inside aother string
|
||||||
|
pub fn json_substring(str: &str) -> Option<&str> {
|
||||||
|
let mut in_string = false;
|
||||||
|
let mut brace_depth = 0;
|
||||||
|
let mut escaped = false;
|
||||||
|
|
||||||
|
for (pos, c) in str.char_indices() {
|
||||||
|
match c {
|
||||||
|
'{' if !in_string => brace_depth += 1,
|
||||||
|
'}' if !in_string => brace_depth -= 1,
|
||||||
|
'\\' if in_string => escaped = !escaped,
|
||||||
|
'"' if !escaped => in_string = !in_string,
|
||||||
|
_ => escaped = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if brace_depth == 0 {
|
||||||
|
return Some(&str[..=pos]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegexRule for ElemStyleRule {
|
impl Rule for ElemStyleRule {
|
||||||
fn name(&self) -> &'static str { "Element Style" }
|
fn name(&self) -> &'static str { "Element Style" }
|
||||||
|
|
||||||
fn regexes(&self) -> &[regex::Regex] { &self.re }
|
/*
|
||||||
|
fn on_regex_match<'a>(
|
||||||
|
&self,
|
||||||
|
_: usize,
|
||||||
|
parser: &dyn Parser,
|
||||||
|
_document: &'a dyn Document,
|
||||||
|
token: Token,
|
||||||
|
matches: Captures,
|
||||||
|
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
||||||
|
let mut reports = vec![];
|
||||||
|
|
||||||
fn on_regex_match<'a>(
|
|
||||||
|
// Get value
|
||||||
|
let new_style = if let Some(value) = matches.get(2) {
|
||||||
|
let value_str = match VariableRule::validate_value(value.as_str()) {
|
||||||
|
Err(err) => {
|
||||||
|
reports.push(
|
||||||
|
Report::build(ReportKind::Error, token.source(), value.start())
|
||||||
|
.with_message("Invalid Style Value")
|
||||||
|
.with_label(
|
||||||
|
Label::new((token.source(), value.range()))
|
||||||
|
.with_message(format!(
|
||||||
|
"Value `{}` is not allowed: {err}",
|
||||||
|
value.as_str().fg(parser.colors().highlight)
|
||||||
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
return reports;
|
||||||
|
}
|
||||||
|
Ok(value) => value,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attempt to serialize
|
||||||
|
match style.from_json(value_str.as_str()) {
|
||||||
|
Err(err) => {
|
||||||
|
reports.push(
|
||||||
|
Report::build(ReportKind::Error, token.source(), value.start())
|
||||||
|
.with_message("Invalid Style Value")
|
||||||
|
.with_label(
|
||||||
|
Label::new((token.source(), value.range()))
|
||||||
|
.with_message(format!(
|
||||||
|
"Failed to serialize `{}` into style with key `{}`: {err}",
|
||||||
|
value_str.fg(parser.colors().highlight),
|
||||||
|
style.key().fg(parser.colors().info)
|
||||||
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
return reports;
|
||||||
|
}
|
||||||
|
Ok(style) => style,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Unknown error")
|
||||||
|
};
|
||||||
|
|
||||||
|
parser.set_style(new_style);
|
||||||
|
|
||||||
|
reports
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||||
|
self.start_re
|
||||||
|
.find_at(cursor.source.content(), cursor.pos)
|
||||||
|
.map_or(None, |m| {
|
||||||
|
Some((m.start(), Box::new([false; 0]) as Box<dyn Any>))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_match<'a>(
|
||||||
&self,
|
&self,
|
||||||
_: usize,
|
|
||||||
parser: &dyn Parser,
|
parser: &dyn Parser,
|
||||||
_document: &'a dyn Document,
|
_document: &'a (dyn Document<'a> + 'a),
|
||||||
token: Token,
|
cursor: Cursor,
|
||||||
matches: Captures,
|
_match_data: Option<Box<dyn Any>>,
|
||||||
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||||
let mut reports = vec![];
|
let mut reports = vec![];
|
||||||
|
let matches = self
|
||||||
|
.start_re
|
||||||
|
.captures_at(cursor.source.content(), cursor.pos)
|
||||||
|
.unwrap();
|
||||||
|
let mut cursor = cursor.at(matches.get(0).unwrap().end() - 1);
|
||||||
|
|
||||||
let style = if let Some(key) = matches.get(1)
|
let style = if let Some(key) = matches.get(1) {
|
||||||
{
|
|
||||||
let trimmed = key.as_str().trim_start().trim_end();
|
let trimmed = key.as_str().trim_start().trim_end();
|
||||||
|
|
||||||
// Check if empty
|
// Check if empty
|
||||||
if trimmed.is_empty()
|
if trimmed.is_empty() {
|
||||||
{
|
|
||||||
reports.push(
|
reports.push(
|
||||||
Report::build(ReportKind::Error, token.source(), key.start())
|
Report::build(ReportKind::Error, cursor.source.clone(), key.start())
|
||||||
.with_message("Empty Style Key")
|
.with_message("Empty Style Key")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), key.range()))
|
Label::new((cursor.source.clone(), key.range()))
|
||||||
.with_message(format!(
|
.with_message(format!("Expected a non-empty style key",))
|
||||||
"Expected a non-empty style key",
|
.with_color(parser.colors().error),
|
||||||
))
|
)
|
||||||
.with_color(parser.colors().error),
|
.finish(),
|
||||||
)
|
);
|
||||||
.finish());
|
return (cursor, reports);
|
||||||
return reports;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if key exists
|
// Check if key exists
|
||||||
if !parser.is_registered(trimmed)
|
if !parser.is_registered(trimmed) {
|
||||||
{
|
|
||||||
reports.push(
|
reports.push(
|
||||||
Report::build(ReportKind::Error, token.source(), key.start())
|
Report::build(ReportKind::Error, cursor.source.clone(), key.start())
|
||||||
.with_message("Unknown Style Key")
|
.with_message("Unknown Style Key")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), key.range()))
|
Label::new((cursor.source.clone(), key.range()))
|
||||||
.with_message(format!(
|
.with_message(format!(
|
||||||
"Could not find a style with key: {}",
|
"Could not find a style with key: {}",
|
||||||
trimmed.fg(parser.colors().info)
|
trimmed.fg(parser.colors().info)
|
||||||
))
|
))
|
||||||
.with_color(parser.colors().error),
|
.with_color(parser.colors().error),
|
||||||
)
|
)
|
||||||
.finish());
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return reports;
|
return (cursor, reports);
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.current_style(trimmed)
|
parser.current_style(trimmed)
|
||||||
} else { panic!("Unknown error") };
|
} else {
|
||||||
|
panic!("Unknown error")
|
||||||
|
};
|
||||||
|
|
||||||
// Get value
|
// Get value
|
||||||
let new_style = if let Some(value) = matches.get(2) {
|
let new_style = match ElemStyleRule::json_substring(
|
||||||
let value_str = match VariableRule::validate_value(value.as_str()) {
|
&cursor.source.clone().content().as_str()[cursor.pos..],
|
||||||
Err(err) => {
|
) {
|
||||||
reports.push(
|
None => {
|
||||||
Report::build(ReportKind::Error, token.source(), value.start())
|
reports.push(
|
||||||
.with_message("Invalid Style Value")
|
Report::build(ReportKind::Error, cursor.source.clone(), cursor.pos)
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), value.range()))
|
|
||||||
.with_message(format!(
|
|
||||||
"Value `{}` is not allowed: {err}",
|
|
||||||
value.as_str().fg(parser.colors().highlight)
|
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish());
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
Ok(value) => value,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attempt to serialize
|
|
||||||
match style.from_json(value_str.as_str())
|
|
||||||
{
|
|
||||||
Err(err) => {
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), value.start())
|
|
||||||
.with_message("Invalid Style Value")
|
.with_message("Invalid Style Value")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), value.range()))
|
Label::new((cursor.source.clone(), matches.get(0).unwrap().range()))
|
||||||
.with_message(format!(
|
.with_message(format!(
|
||||||
"Failed to serialize `{}` into style with key `{}`: {err}",
|
"Unable to parse json string after style key",
|
||||||
value_str.fg(parser.colors().highlight),
|
))
|
||||||
style.key().fg(parser.colors().info)
|
.with_color(parser.colors().error),
|
||||||
))
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
)
|
||||||
.finish());
|
.finish(),
|
||||||
return reports;
|
);
|
||||||
},
|
return (cursor, reports);
|
||||||
Ok(style) => style,
|
|
||||||
}
|
}
|
||||||
} else { panic!("Unknown error") };
|
Some(json) => {
|
||||||
|
cursor = cursor.at(cursor.pos + json.len());
|
||||||
|
|
||||||
parser.set_style(new_style);
|
// Attempt to deserialize
|
||||||
|
match style.from_json(json) {
|
||||||
|
Err(err) => {
|
||||||
|
reports.push(
|
||||||
|
Report::build(ReportKind::Error, cursor.source.clone(), cursor.pos)
|
||||||
|
.with_message("Invalid Style Value")
|
||||||
|
.with_label(
|
||||||
|
Label::new((
|
||||||
|
cursor.source.clone(),
|
||||||
|
cursor.pos..cursor.pos + json.len(),
|
||||||
|
))
|
||||||
|
.with_message(format!(
|
||||||
|
"Failed to serialize `{}` into style with key `{}`: {err}",
|
||||||
|
json.fg(parser.colors().highlight),
|
||||||
|
style.key().fg(parser.colors().info)
|
||||||
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
return (cursor, reports);
|
||||||
|
}
|
||||||
|
Ok(style) => style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
reports
|
parser.set_current_style(new_style);
|
||||||
|
|
||||||
|
(cursor, reports)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,7 +367,7 @@ impl RegexRule for SectionRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_styles(&self, parser: &dyn Parser) {
|
fn register_styles(&self, parser: &dyn Parser) {
|
||||||
parser.set_style(Rc::new(SectionStyle::default()));
|
parser.set_current_style(Rc::new(SectionStyle::default()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue