Reports refactor [1/2]

This commit is contained in:
ef3d0c3e 2024-10-23 22:25:34 +02:00
parent f57173b9be
commit 72954cdad8
20 changed files with 757 additions and 884 deletions

View file

@ -4,9 +4,6 @@ use std::collections::HashMap;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use blockquote_style::AuthorPos::After; use blockquote_style::AuthorPos::After;
use blockquote_style::AuthorPos::Before; use blockquote_style::AuthorPos::Before;
use blockquote_style::BlockquoteStyle; use blockquote_style::BlockquoteStyle;
@ -36,6 +33,8 @@ use crate::parser::style::StyleHolder;
use crate::parser::util::escape_text; use crate::parser::util::escape_text;
use crate::parser::util::Property; use crate::parser::util::Property;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Blockquote { pub struct Blockquote {
@ -254,7 +253,7 @@ impl Rule for BlockquoteRule {
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
cursor: Cursor, cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Box<dyn Any>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report>) {
let mut reports = vec![]; let mut reports = vec![];
let content = cursor.source.content(); let content = cursor.source.content();
@ -273,19 +272,8 @@ impl Rule for BlockquoteRule {
if let Some(properties) = captures.get(1) { if let Some(properties) = captures.get(1) {
match self.parse_properties(properties) { match self.parse_properties(properties) {
Err(err) => { Err(err) => {
reports.push( report_err!(&mut reports, cursor.source.clone(), "Invalid Blockquote Properties".into(),
Report::build( span(properties.range(), err)
ReportKind::Warning,
cursor.source.clone(),
properties.start(),
)
.with_message("Invalid Blockquote Properties")
.with_label(
Label::new((cursor.source.clone(), properties.range()))
.with_message(err)
.with_color(state.parser.colors().warning),
)
.finish(),
); );
return (end_cursor, reports); return (end_cursor, reports);
} }
@ -342,15 +330,8 @@ impl Rule for BlockquoteRule {
} else if elem.downcast_ref::<Blockquote>().is_some() { } else if elem.downcast_ref::<Blockquote>().is_some() {
parsed_content.push(elem); parsed_content.push(elem);
} else { } else {
reports.push( report_err!(&mut reports, token.source(), "Unable to Parse Blockquote Entry".into(),
Report::build(ReportKind::Error, token.source(), token.range.start) span(token.range.clone(), "Blockquotes may only contain paragraphs and other blockquotes".into())
.with_message("Unable to Parse Blockquote Entry")
.with_label(
Label::new((token.source(), token.range.clone()))
.with_message("Blockquotes may only contain paragraphs and other blockquotes")
.with_color(state.parser.colors().error),
)
.finish(),
); );
return (end_cursor, reports); return (end_cursor, reports);
} }

View file

@ -9,9 +9,6 @@ use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument; use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
@ -29,6 +26,8 @@ use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::state::RuleState; use crate::parser::state::RuleState;
use crate::parser::state::Scope; use crate::parser::state::Scope;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
use super::paragraph::Paragraph; use super::paragraph::Paragraph;
@ -50,7 +49,7 @@ impl CustomStyle for LuaCustomStyle {
location: Token, location: Token,
state: &ParserState, state: &ParserState,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let kernel: Ref<'_, Kernel> = let kernel: Ref<'_, Kernel> =
Ref::map(state.shared.kernels.borrow(), |b| b.get("main").unwrap()); Ref::map(state.shared.kernels.borrow(), |b| b.get("main").unwrap());
//let kernel = RefMut::map(parser_state.shared.kernels.borrow(), |ker| ker.get("main").unwrap()); //let kernel = RefMut::map(parser_state.shared.kernels.borrow(), |ker| ker.get("main").unwrap());
@ -64,19 +63,12 @@ impl CustomStyle for LuaCustomStyle {
kernel.run_with_context(ctx, |lua| { kernel.run_with_context(ctx, |lua| {
let chunk = lua.load(self.start.as_str()); let chunk = lua.load(self.start.as_str());
if let Err(err) = chunk.eval::<()>() { if let Err(err) = chunk.eval::<()>() {
reports.push( report_err!(&mut reports, location.source(), "Lua execution failed".into(),
Report::build(ReportKind::Error, location.source(), location.start()) span(location.range.clone(), err.to_string()),
.with_message("Lua execution failed") note(format!(
.with_label(
Label::new((location.source(), location.range.clone()))
.with_message(err.to_string())
.with_color(state.parser.colors().error),
)
.with_note(format!(
"When trying to start custom style {}", "When trying to start custom style {}",
self.name().fg(state.parser.colors().info) self.name().fg(state.parser.colors().info)
)) ))
.finish(),
); );
} }
}); });
@ -89,7 +81,7 @@ impl CustomStyle for LuaCustomStyle {
location: Token, location: Token,
state: &ParserState, state: &ParserState,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let kernel: Ref<'_, Kernel> = let kernel: Ref<'_, Kernel> =
Ref::map(state.shared.kernels.borrow(), |b| b.get("main").unwrap()); Ref::map(state.shared.kernels.borrow(), |b| b.get("main").unwrap());
let ctx = KernelContext { let ctx = KernelContext {
@ -102,19 +94,12 @@ impl CustomStyle for LuaCustomStyle {
kernel.run_with_context(ctx, |lua| { kernel.run_with_context(ctx, |lua| {
let chunk = lua.load(self.end.as_str()); let chunk = lua.load(self.end.as_str());
if let Err(err) = chunk.eval::<()>() { if let Err(err) = chunk.eval::<()>() {
reports.push( report_err!(&mut reports, location.source(), "Lua execution failed".into(),
Report::build(ReportKind::Error, location.source(), location.start()) span(location.range.clone(), err.to_string()),
.with_message("Lua execution failed") note(format!(
.with_label(
Label::new((location.source(), location.range.clone()))
.with_message(err.to_string())
.with_color(state.parser.colors().error),
)
.with_note(format!(
"When trying to end custom style {}", "When trying to end custom style {}",
self.name().fg(state.parser.colors().info) self.name().fg(state.parser.colors().info)
)) ))
.finish(),
); );
} }
}); });
@ -130,11 +115,11 @@ struct CustomStyleState {
impl RuleState for CustomStyleState { impl RuleState for CustomStyleState {
fn scope(&self) -> Scope { Scope::PARAGRAPH } fn scope(&self) -> Scope { Scope::PARAGRAPH }
fn on_remove<'a>( fn on_remove(
&self, &self,
state: &ParserState, state: &ParserState,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
self.toggled.iter().for_each(|(style, token)| { self.toggled.iter().for_each(|(style, token)| {
@ -150,26 +135,13 @@ impl RuleState for CustomStyleState {
}) })
.unwrap(); .unwrap();
reports.push( report_err!(&mut reports, token.source(), "Unterminated Custom Style".into(),
Report::build(ReportKind::Error, token.source(), token.start()) span(token.range.clone(), format!(
.with_message("Unterminated Custom Style")
.with_label(
Label::new((token.source(), token.range.clone()))
.with_order(1)
.with_message(format!(
"Style {} starts here", "Style {} starts here",
style.fg(state.parser.colors().info) style.fg(state.parser.colors().info)
)) )),
.with_color(state.parser.colors().error), span(paragraph_end.1, "Paragraph ends here".into()),
) note("Styles cannot span multiple documents (i.e @import)".into())
.with_label(
Label::new(paragraph_end)
.with_order(1)
.with_message("Paragraph ends here".to_string())
.with_color(state.parser.colors().error),
)
.with_note("Styles cannot span multiple documents (i.e @import)")
.finish(),
); );
}); });
@ -248,7 +220,7 @@ impl Rule for CustomStyleRule {
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
cursor: Cursor, cursor: Cursor,
match_data: Box<dyn Any>, match_data: Box<dyn Any>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report>) {
let (style, end) = match_data let (style, end) = match_data
.downcast_ref::<(Rc<dyn CustomStyle>, bool)>() .downcast_ref::<(Rc<dyn CustomStyle>, bool)>()
.unwrap(); .unwrap();
@ -299,22 +271,16 @@ impl Rule for CustomStyleRule {
let token = let token =
Token::new(cursor.pos..cursor.pos + s_end.len(), cursor.source.clone()); Token::new(cursor.pos..cursor.pos + s_end.len(), cursor.source.clone());
if style_state.toggled.get(style.name()).is_none() { if style_state.toggled.get(style.name()).is_none() {
return ( let mut reports = vec![];
cursor.at(cursor.pos + s_end.len()), report_err!(&mut reports, token.source(), "Invalid End of Style".into(),
vec![ span(token.range.clone(), format!(
Report::build(ReportKind::Error, token.source(), token.start()) "Cannot end style {} here, it does not started anywhere",
.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(state.parser.colors().info) style.name().fg(state.parser.colors().info)
)) ))
.with_color(state.parser.colors().error), );
) return (
.finish(), cursor.at(cursor.pos + s_end.len()),
], reports
); );
} }
@ -327,33 +293,20 @@ impl Rule for CustomStyleRule {
cursor.source.clone(), cursor.source.clone(),
); );
if let Some(start_token) = style_state.toggled.get(style.name()) { if let Some(start_token) = style_state.toggled.get(style.name()) {
let mut reports = vec![];
report_err!(&mut reports, token.source(), "Invalid Start of Style".into(),
span(token.range.clone(), format!(
"When trying to start custom style {}",
self.name().fg(state.parser.colors().info)
)),
span(start_token.range.clone(), format!(
"Style {} previously starts here",
self.name().fg(state.parser.colors().info)
)),
);
return ( return (
cursor.at(cursor.pos + s_end.len()), cursor.at(cursor.pos + s_end.len()),
vec![Report::build( reports
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(state.parser.colors().info)
))
.with_color(state.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(state.parser.colors().info)
))
.with_color(state.parser.colors().error),
)
.finish()],
); );
} }

View file

@ -6,9 +6,6 @@ use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument; use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
@ -18,9 +15,10 @@ use regex::Regex;
use crate::document::document::Document; use crate::document::document::Document;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::reports::macros::*;
use crate::parser::reports::*;
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;
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::elemstyle")] #[auto_registry::auto_registry(registry = "rules", path = "crate::elements::elemstyle")]
pub struct ElemStyleRule { pub struct ElemStyleRule {
@ -80,7 +78,7 @@ impl Rule for ElemStyleRule {
_document: &'a (dyn Document<'a> + 'a), _document: &'a (dyn Document<'a> + 'a),
cursor: Cursor, cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Box<dyn Any>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report>) {
let mut reports = vec![]; let mut reports = vec![];
let matches = self let matches = self
.start_re .start_re
@ -93,33 +91,28 @@ impl Rule for ElemStyleRule {
// Check if empty // Check if empty
if trimmed.is_empty() { if trimmed.is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, cursor.source.clone(), key.start()) &mut reports,
.with_message("Empty Style Key") cursor.source.clone(),
.with_label( "Empty Style Key".into(),
Label::new((cursor.source.clone(), key.range())) span(key.range(), "Expected a non-empty style key".into()),
.with_message("Expected a non-empty style key".to_string())
.with_color(state.parser.colors().error),
)
.finish(),
); );
return (cursor, reports); return (cursor, reports);
} }
// Check if key exists // Check if key exists
if !state.shared.styles.borrow().is_registered(trimmed) { if !state.shared.styles.borrow().is_registered(trimmed) {
reports.push( report_err!(
Report::build(ReportKind::Error, cursor.source.clone(), key.start()) &mut reports,
.with_message("Unknown Style Key") cursor.source.clone(),
.with_label( "Unknown Style Key".into(),
Label::new((cursor.source.clone(), key.range())) span(
.with_message(format!( key.range(),
format!(
"Could not find a style with key: {}", "Could not find a style with key: {}",
trimmed.fg(state.parser.colors().info) trimmed.fg(state.parser.colors().info)
))
.with_color(state.parser.colors().error),
) )
.finish(), ),
); );
return (cursor, reports); return (cursor, reports);
@ -135,17 +128,14 @@ impl Rule for ElemStyleRule {
&cursor.source.clone().content().as_str()[cursor.pos..], &cursor.source.clone().content().as_str()[cursor.pos..],
) { ) {
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, cursor.source.clone(), cursor.pos) &mut reports,
.with_message("Invalid Style Value") cursor.source.clone(),
.with_label( "Invalid Style Value".into(),
Label::new((cursor.source.clone(), matches.get(0).unwrap().range())) span(
.with_message( matches.get(0).unwrap().range(),
"Unable to parse json string after style key".to_string(), "Unable to parse json string after style key".into()
) )
.with_color(state.parser.colors().error),
)
.finish(),
); );
return (cursor, reports); return (cursor, reports);
} }
@ -155,22 +145,18 @@ impl Rule for ElemStyleRule {
// Attempt to deserialize // Attempt to deserialize
match style.from_json(json) { match style.from_json(json) {
Err(err) => { Err(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, cursor.source.clone(), cursor.pos) &mut reports,
.with_message("Invalid Style Value")
.with_label(
Label::new((
cursor.source.clone(), cursor.source.clone(),
"Invalid Style Value".into(),
span(
cursor.pos..cursor.pos + json.len(), cursor.pos..cursor.pos + json.len(),
)) format!(
.with_message(format!(
"Failed to serialize `{}` into style with key `{}`: {err}", "Failed to serialize `{}` into style with key `{}`: {err}",
json.fg(state.parser.colors().highlight), json.fg(state.parser.colors().highlight),
style.key().fg(state.parser.colors().info) style.key().fg(state.parser.colors().info)
))
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return (cursor, reports); return (cursor, reports);
} }

View file

@ -17,9 +17,6 @@ use crate::parser::state::RuleState;
use crate::parser::state::Scope; use crate::parser::state::Scope;
use crate::parser::util::escape_text; use crate::parser::util::escape_text;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument; use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
@ -34,6 +31,8 @@ use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum LayoutToken { pub(crate) enum LayoutToken {
@ -253,11 +252,11 @@ struct LayoutState {
impl RuleState for LayoutState { impl RuleState for LayoutState {
fn scope(&self) -> Scope { Scope::DOCUMENT } fn scope(&self) -> Scope { Scope::DOCUMENT }
fn on_remove<'a>( fn on_remove(
&self, &self,
state: &ParserState, state: &ParserState,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
let doc_borrow = document.content().borrow(); let doc_borrow = document.content().borrow();
@ -265,25 +264,23 @@ impl RuleState for LayoutState {
for (tokens, layout_type) in &self.stack { for (tokens, layout_type) in &self.stack {
let start = tokens.first().unwrap(); let start = tokens.first().unwrap();
reports.push( report_err!(
Report::build(ReportKind::Error, start.source(), start.start()) &mut reports,
.with_message("Unterminated Layout") start.source(),
.with_label( "Unterminated Layout".into(),
Label::new((start.source(), start.range.start + 1..start.range.end)) span(
.with_order(1) start.source(),
.with_message(format!( start.range.start+1..start.range.end,
format!(
"Layout {} stars here", "Layout {} stars here",
layout_type.name().fg(state.parser.colors().info) layout_type.name().fg(state.parser.colors().info)
))
.with_color(state.parser.colors().error),
) )
.with_label( ),
Label::new((at.source(), at.range.clone())) span(
.with_order(2) at.source(),
.with_message("Document ends here".to_string()) at.range.clone(),
.with_color(state.parser.colors().error), "Document ends here".into()
) )
.finish(),
); );
} }
@ -340,24 +337,26 @@ impl LayoutRule {
} }
pub fn parse_properties<'a>( pub fn parse_properties<'a>(
colors: &ReportColors, mut reports: &mut Vec<Report>,
token: &Token, token: &Token,
layout_type: Rc<dyn LayoutType>, layout_type: Rc<dyn LayoutType>,
properties: Option<Match>, properties: Option<Match>,
) -> Result<Option<Box<dyn Any>>, Report<'a, (Rc<dyn Source>, Range<usize>)>> { ) -> Option<Box<dyn Any>> {
match properties { match properties {
None => match layout_type.parse_properties("") { None => match layout_type.parse_properties("") {
Ok(props) => Ok(props), Ok(props) => props,
Err(err) => Err( Err(err) => {
Report::build(ReportKind::Error, token.source(), token.start()) report_err!(
.with_message("Unable to parse layout properties") &mut reports,
.with_label( token.source(),
Label::new((token.source(), token.range.clone())) "Invalid Layout Properties".into(),
.with_message(err) span(
.with_color(colors.error), token.range.clone(),
format!("Layout is missing required property: {eee}")
) )
.finish(), );
), None
}
}, },
Some(props) => { Some(props) => {
let trimmed = props.as_str().trim_start().trim_end(); let trimmed = props.as_str().trim_start().trim_end();

View file

@ -14,9 +14,6 @@ use crate::parser::source::Token;
use crate::parser::source::VirtualSource; use crate::parser::source::VirtualSource;
use crate::parser::util; use crate::parser::util;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument; use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
@ -25,6 +22,8 @@ use regex::Regex;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Link { pub struct Link {
@ -109,39 +108,37 @@ impl RegexRule for LinkRule {
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: Captures, matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
let link_display = match matches.get(1) { let link_display = match matches.get(1) {
Some(display) => { Some(display) => {
if display.as_str().is_empty() { if display.as_str().is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), display.start()) &mut reports,
.with_message("Empty link name") token.source(),
.with_label( "Empty Link Display".into(),
Label::new((token.source().clone(), display.range())) span(
.with_message("Link name is empty") display.range(),
.with_color(state.parser.colors().error), "Link display is empty".into()
) )
.finish(),
); );
return reports; return reports;
} }
let display_source = util::escape_source(token.source(), display.range(), "Link Display".into(), '\\', "]("); let display_source = util::escape_source(token.source(), display.range(), "Link Display".into(), '\\', "](");
if display_source.content().is_empty() { if display_source.content().is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), display.start()) &mut reports,
.with_message("Empty link name") token.source(),
.with_label( "Empty Link Display".into(),
Label::new((token.source(), display.range())) span(
.with_message(format!( display.range(),
format!(
"Link name is empty. Once processed, `{}` yields `{}`", "Link name is empty. Once processed, `{}` yields `{}`",
display.as_str().fg(state.parser.colors().highlight), display.as_str().fg(state.parser.colors().highlight),
display_source.fg(state.parser.colors().highlight), display_source.fg(state.parser.colors().highlight),
))
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -156,15 +153,14 @@ impl RegexRule for LinkRule {
} }
match util::parse_paragraph(state, display_source, document) { match util::parse_paragraph(state, display_source, document) {
Err(err) => { Err(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), display.start()) &mut reports,
.with_message("Failed to parse link display") token.source(),
.with_label( "Invalid Link Display".into(),
Label::new((token.source(), display.range())) span(
.with_message(err.to_string()) display.range(),
.with_color(state.parser.colors().error), format!("Failed to parse link display:\n{err}")
) )
.finish(),
); );
return reports; return reports;
} }
@ -177,34 +173,32 @@ impl RegexRule for LinkRule {
let link_url = match matches.get(2) { let link_url = match matches.get(2) {
Some(url) => { Some(url) => {
if url.as_str().is_empty() { if url.as_str().is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), url.start()) &mut reports,
.with_message("Empty link url") token.source(),
.with_label( "Empty Link URL".into(),
Label::new((token.source(), url.range())) span(
.with_message("Link url is empty") url.range(),
.with_color(state.parser.colors().error), "Link url is empty".into()
) )
.finish(),
); );
return reports; return reports;
} }
let text_content = util::process_text(document, url.as_str()); let text_content = util::process_text(document, url.as_str());
if text_content.is_empty() { if text_content.is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), url.start()) &mut reports,
.with_message("Empty link url") token.source(),
.with_label( "Empty Link URL".into(),
Label::new((token.source(), url.range())) span(
.with_message(format!( url.range(),
format!(
"Link url is empty. Once processed, `{}` yields `{}`", "Link url is empty. Once processed, `{}` yields `{}`",
url.as_str().fg(state.parser.colors().highlight), url.as_str().fg(state.parser.colors().highlight),
text_content.as_str().fg(state.parser.colors().highlight), text_content.as_str().fg(state.parser.colors().highlight),
))
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }

View file

@ -14,9 +14,9 @@ use crate::document::element::Element;
use crate::lsp::semantic::Semantics; use crate::lsp::semantic::Semantics;
use crate::parser::parser::ParseMode; use crate::parser::parser::ParseMode;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::reports::Report;
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::Token; use crate::parser::source::Token;
use crate::parser::source::VirtualSource; use crate::parser::source::VirtualSource;
use crate::parser::util; use crate::parser::util;
@ -24,9 +24,8 @@ use crate::parser::util::escape_text;
use crate::parser::util::Property; use crate::parser::util::Property;
use crate::parser::util::PropertyMapError; use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
use ariadne::Label; use crate::parser::reports::*;
use ariadne::Report; use crate::parser::reports::macros::*;
use ariadne::ReportKind;
use regex::Match; use regex::Match;
use regex::Regex; use regex::Regex;
@ -293,7 +292,7 @@ impl Rule for ListRule {
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
cursor: Cursor, cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Box<dyn Any>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report>) {
let mut reports = vec![]; let mut reports = vec![];
let content = cursor.source.content(); let content = cursor.source.content();
@ -312,19 +311,8 @@ impl Rule for ListRule {
if let Some(properties) = captures.get(2) { if let Some(properties) = captures.get(2) {
match self.parse_properties(properties) { match self.parse_properties(properties) {
Err(err) => { Err(err) => {
reports.push( report_err!(&mut reports, cursor.source.clone(), "Invalid List Entry Properties".into(),
Report::build( span(properties.range(), err)
ReportKind::Warning,
cursor.source.clone(),
properties.start(),
)
.with_message("Invalid List Entry Properties")
.with_label(
Label::new((cursor.source.clone(), properties.range()))
.with_message(err)
.with_color(state.parser.colors().warning),
)
.finish(),
); );
return (cursor.at(captures.get(0).unwrap().end()), reports); return (cursor.at(captures.get(0).unwrap().end()), reports);
} }
@ -391,15 +379,8 @@ impl Rule for ListRule {
)); ));
let parsed_content = match util::parse_paragraph(state, entry_src, document) { let parsed_content = match util::parse_paragraph(state, entry_src, document) {
Err(err) => { Err(err) => {
reports.push( report_warn!(&mut reports, token.source(), "Unable to parse List Entry".into(),
Report::build(ReportKind::Warning, token.source(), token.range.start) span(token.range.clone(), err.into())
.with_message("Unable to Parse List Entry")
.with_label(
Label::new((token.source(), token.range.clone()))
.with_message(err)
.with_color(state.parser.colors().warning),
)
.finish(),
); );
// Return an empty paragraph // Return an empty paragraph
vec![] vec![]

View file

@ -4,9 +4,6 @@ use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use regex::Captures; use regex::Captures;
use regex::Match; use regex::Match;
use regex::Regex; use regex::Regex;
@ -34,6 +31,8 @@ use crate::parser::util::Property;
use crate::parser::util::PropertyMap; use crate::parser::util::PropertyMap;
use crate::parser::util::PropertyMapError; use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
use super::paragraph::Paragraph; use super::paragraph::Paragraph;
use super::reference::InternalReference; use super::reference::InternalReference;
@ -286,39 +285,43 @@ impl MediaRule {
fn parse_properties( fn parse_properties(
&self, &self,
colors: &ReportColors, mut reports: &mut Vec<Report>,
token: &Token, token: &Token,
m: &Option<Match>, m: &Option<Match>,
) -> Result<PropertyMap, Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Option<PropertyMap> {
match m { match m {
None => match self.properties.default() { None => match self.properties.default() {
Ok(properties) => Ok(properties), Ok(properties) => Some(properties),
Err(e) => Err( Err(e) => {
Report::build(ReportKind::Error, token.source(), token.start()) report_err!(
.with_message("Invalid Media Properties") &mut reports,
.with_label( token.source(),
Label::new((token.source().clone(), token.range.clone())) "Invalid Media Properties".into(),
.with_message(format!("Media is missing required property: {e}")) span(
.with_color(colors.error), token.range.clone(),
format!("Media is missing required property: {e}")
) )
.finish(), );
), None
}
}, },
Some(props) => { Some(props) => {
let processed = let processed =
util::escape_text('\\', "]", props.as_str().trim_start().trim_end()); util::escape_text('\\', "]", props.as_str().trim_start().trim_end());
match self.properties.parse(processed.as_str()) { match self.properties.parse(processed.as_str()) {
Err(e) => Err( Err(e) => {
Report::build(ReportKind::Error, token.source(), props.start()) report_err!(
.with_message("Invalid Media Properties") &mut reports,
.with_label( token.source(),
Label::new((token.source().clone(), props.range())) "Invalid Media Properties".into(),
.with_message(e) span(
.with_color(colors.error), props.range(),
e
) )
.finish(), );
), None
Ok(properties) => Ok(properties), },
Ok(properties) => Some(properties),
} }
} }
} }
@ -357,7 +360,7 @@ impl RegexRule for MediaRule {
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: Captures, matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
let refname = match ( let refname = match (
@ -366,13 +369,14 @@ impl RegexRule for MediaRule {
) { ) {
(_, Ok(refname)) => refname.to_string(), (_, Ok(refname)) => refname.to_string(),
(m, Err(err)) => { (m, Err(err)) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), m.start()) &mut reports,
.with_message("Invalid Media Refname") token.source(),
.with_label( "Invalid Media Refname".into(),
Label::new((token.source().clone(), m.range())).with_message(err), span(
m.range(),
err
) )
.finish(),
); );
return reports; return reports;
} }
@ -384,26 +388,24 @@ impl RegexRule for MediaRule {
) { ) {
(_, Ok(uri)) => util::escape_text('\\', ")", uri), (_, Ok(uri)) => util::escape_text('\\', ")", uri),
(m, Err(err)) => { (m, Err(err)) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), m.start()) &mut reports,
.with_message("Invalid Media URI") token.source(),
.with_label( "Invalid Media URI".into(),
Label::new((token.source().clone(), m.range())).with_message(err), span(
m.range(),
err
) )
.finish(),
); );
return reports; return reports;
} }
}; };
// Properties // Properties
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(3)) let properties = match self.parse_properties(&mut reports, &token, &matches.get(3))
{ {
Ok(pm) => pm, Some(pm) => pm,
Err(report) => { None => return reports,
reports.push(report);
return reports;
}
}; };
let media_type = let media_type =
@ -415,35 +417,30 @@ impl RegexRule for MediaRule {
Ok((_prop, kind)) => kind, Ok((_prop, kind)) => kind,
Err(e) => match e { Err(e) => match e {
PropertyMapError::ParseError((prop, err)) => { PropertyMapError::ParseError((prop, err)) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid Media Property") token.source(),
.with_label( "Invalid Media Property".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!( token.start()+1..token.end(),
format!(
"Property `type: {}` cannot be converted: {}", "Property `type: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(state.parser.colors().info),
err.fg(state.parser.colors().error) err.fg(state.parser.colors().error)
))
.with_color(state.parser.colors().warning),
) )
.finish(), )
); );
return reports; return reports;
} }
PropertyMapError::NotFoundError(err) => { PropertyMapError::NotFoundError(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid Media Property") token.source(),
.with_label( "Invalid Media Property".into(),
Label::new(( span(
token.source().clone(),
token.start()+1..token.end(), token.start()+1..token.end(),
)) format!("{err}. Required because mediatype could not be detected")
.with_message(format!("{err}. Required because mediatype could not be detected"))
.with_color(state.parser.colors().error),
) )
.finish(),
); );
return reports; return reports;
} }
@ -478,17 +475,16 @@ impl RegexRule for MediaRule {
match parse_paragraph(state, source, document) { match parse_paragraph(state, source, document) {
Ok(paragraph) => Some(*paragraph), Ok(paragraph) => Some(*paragraph),
Err(err) => { Err(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), content.start()) &mut reports,
.with_message("Invalid Media Description") token.source(),
.with_label( "Invalid Media Description".into(),
Label::new((token.source().clone(), content.range())) span(
.with_message(format!( content.range(),
format!(
"Could not parse description: {err}" "Could not parse description: {err}"
))
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -522,15 +518,15 @@ impl RegexRule for MediaRule {
caption, caption,
description, description,
})) { })) {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid Media") token.source(),
.with_label( "Invalid Media".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(err) token.range.clone(),
.with_color(state.parser.colors().error), err
) )
.finish(),
); );
} }

View file

@ -6,25 +6,21 @@ use crate::lsp::semantic::Semantics;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParseMode; use crate::parser::parser::ParseMode;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::util::Property; use crate::parser::util::Property;
use crate::parser::util::PropertyMapError; use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
use crate::parser::util::{self}; use crate::parser::util::{self};
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument; use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
@ -94,25 +90,24 @@ impl RegexRule for RawRule {
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
let raw_content = match matches.get(2) { let raw_content = match matches.get(2) {
// Unterminated // Unterminated
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Unterminated Raw Code") token.source(),
.with_label( "Unterminated Raw Code".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
format!(
"Missing terminating `{}` after first `{}`", "Missing terminating `{}` after first `{}`",
"?}".fg(state.parser.colors().info), "?}".fg(state.parser.colors().info),
"{?".fg(state.parser.colors().info) "{?".fg(state.parser.colors().info)
))
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -121,15 +116,11 @@ impl RegexRule for RawRule {
util::escape_text('\\', "?}", content.as_str().trim_start().trim_end()); util::escape_text('\\', "?}", content.as_str().trim_start().trim_end());
if processed.is_empty() { if processed.is_empty() {
reports.push( report_warn!(
Report::build(ReportKind::Warning, token.source(), content.start()) &mut reports,
.with_message("Empty Raw Code") token.source(),
.with_label( "Empty Raw Code".into(),
Label::new((token.source().clone(), content.range())) span(content.range(), "Raw code is empty".into())
.with_message("Raw code is empty")
.with_color(state.parser.colors().warning),
)
.finish(),
); );
} }
processed processed
@ -140,15 +131,14 @@ impl RegexRule for RawRule {
None => match self.properties.default() { None => match self.properties.default() {
Ok(properties) => properties, Ok(properties) => properties,
Err(e) => { Err(e) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid Raw Code") token.source(),
.with_label( "Invalid Raw Code".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!("Raw code is missing properties: {e}")) token.range.clone(),
.with_color(state.parser.colors().error), format!("Raw code is missing properties: {e}")
) )
.finish(),
); );
return reports; return reports;
} }
@ -158,15 +148,11 @@ impl RegexRule for RawRule {
util::escape_text('\\', "]", props.as_str().trim_start().trim_end()); util::escape_text('\\', "]", props.as_str().trim_start().trim_end());
match self.properties.parse(processed.as_str()) { match self.properties.parse(processed.as_str()) {
Err(e) => { Err(e) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), props.start()) &mut reports,
.with_message("Invalid Raw Code Properties") token.source(),
.with_label( "Invalid Raw Code Properties".into(),
Label::new((token.source().clone(), props.range())) span(props.range(), e)
.with_message(e)
.with_color(state.parser.colors().error),
)
.finish(),
); );
return reports; return reports;
} }
@ -181,38 +167,33 @@ impl RegexRule for RawRule {
Ok((_prop, kind)) => kind, Ok((_prop, kind)) => kind,
Err(e) => match e { Err(e) => match e {
PropertyMapError::ParseError((prop, err)) => { PropertyMapError::ParseError((prop, err)) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid Raw Code Property") token.source(),
.with_label( "Invalid Raw Code Properties".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
format!(
"Property `kind: {}` cannot be converted: {}", "Property `kind: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(state.parser.colors().info),
err.fg(state.parser.colors().error) err.fg(state.parser.colors().error)
))
.with_color(state.parser.colors().warning),
) )
.finish(), )
); );
return reports; return reports;
} }
PropertyMapError::NotFoundError(err) => { PropertyMapError::NotFoundError(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid Code Property") token.source(),
.with_label( "Invalid Raw Code Properties".into(),
Label::new(( span(
token.source().clone(), token.range.clone(),
token.start() + 1..token.end(), format!(
))
.with_message(format!(
"Property `{}` is missing", "Property `{}` is missing",
err.fg(state.parser.colors().info) err.fg(state.parser.colors().info)
))
.with_color(state.parser.colors().warning),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -233,8 +214,7 @@ impl RegexRule for RawRule {
{ {
let range = matches.get(0).unwrap().range(); let range = matches.get(0).unwrap().range();
sems.add(range.start..range.start + 2, tokens.raw_sep); sems.add(range.start..range.start + 2, tokens.raw_sep);
if let Some(props) = matches.get(1).map(|m| m.range()) if let Some(props) = matches.get(1).map(|m| m.range()) {
{
sems.add(props.start - 1..props.start, tokens.raw_props_sep); sems.add(props.start - 1..props.start, tokens.raw_props_sep);
sems.add(props.clone(), tokens.raw_props); sems.add(props.clone(), tokens.raw_props);
sems.add(props.end..props.end + 1, tokens.raw_props_sep); sems.add(props.end..props.end + 1, tokens.raw_props_sep);
@ -297,7 +277,8 @@ mod tests {
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::{validate_document, validate_semantics}; use crate::validate_document;
use crate::validate_semantics;
#[test] #[test]
fn parser() { fn parser() {

View file

@ -2,9 +2,6 @@ use std::collections::HashMap;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use reference_style::ExternalReferenceStyle; use reference_style::ExternalReferenceStyle;
use regex::Captures; use regex::Captures;
use regex::Match; use regex::Match;
@ -32,6 +29,8 @@ use crate::parser::util;
use crate::parser::util::Property; use crate::parser::util::Property;
use crate::parser::util::PropertyMap; use crate::parser::util::PropertyMap;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Debug)] #[derive(Debug)]
pub struct InternalReference { pub struct InternalReference {
@ -186,39 +185,43 @@ impl ReferenceRule {
fn parse_properties( fn parse_properties(
&self, &self,
colors: &ReportColors, mut reports: &mut Vec<Report>,
token: &Token, token: &Token,
m: &Option<Match>, m: &Option<Match>,
) -> Result<PropertyMap, Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Option<PropertyMap> {
match m { match m {
None => match self.properties.default() { None => match self.properties.default() {
Ok(properties) => Ok(properties), Ok(properties) => Some(properties),
Err(e) => Err( Err(e) => {
Report::build(ReportKind::Error, token.source(), token.start()) report_err!(
.with_message("Invalid Media Properties") &mut reports,
.with_label( token.source(),
Label::new((token.source().clone(), token.range.clone())) "Invalid Reference Properties".into(),
.with_message(format!("Media is missing required property: {e}")) span(
.with_color(colors.error), token.range.clone(),
format!("Reference is missing required property: {e}")
) )
.finish(), );
), None
}
}, },
Some(props) => { Some(props) => {
let processed = let processed =
util::escape_text('\\', "]", props.as_str().trim_start().trim_end()); util::escape_text('\\', "]", props.as_str().trim_start().trim_end());
match self.properties.parse(processed.as_str()) { match self.properties.parse(processed.as_str()) {
Err(e) => Err( Err(e) => {
Report::build(ReportKind::Error, token.source(), props.start()) report_err!(
.with_message("Invalid Media Properties") &mut reports,
.with_label( token.source(),
Label::new((token.source().clone(), props.range())) "Invalid Reference Properties".into(),
.with_message(e) span(
.with_color(colors.error), props.range(),
e
) )
.finish(), );
), None
Ok(properties) => Ok(properties), },
Ok(properties) => Some(properties),
} }
} }
} }
@ -241,7 +244,7 @@ impl RegexRule for ReferenceRule {
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: Captures, matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
let (refdoc, refname) = if let Some(refname_match) = matches.get(1) { let (refdoc, refname) = if let Some(refname_match) = matches.get(1) {
@ -252,14 +255,14 @@ impl RegexRule for ReferenceRule {
match validate_refname(document, refname_match.as_str().split_at(sep + 1).1, false) match validate_refname(document, refname_match.as_str().split_at(sep + 1).1, false)
{ {
Err(err) => { Err(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), refname_match.start()) &mut reports,
.with_message("Invalid Reference Refname") token.source(),
.with_label( "Invalid Reference Refname".into(),
Label::new((token.source().clone(), refname_match.range())) span(
.with_message(err), refname_match.range(),
err
) )
.finish(),
); );
return reports; return reports;
} }
@ -270,14 +273,14 @@ impl RegexRule for ReferenceRule {
{ {
match validate_refname(document, refname_match.as_str(), false) { match validate_refname(document, refname_match.as_str(), false) {
Err(err) => { Err(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), refname_match.start()) &mut reports,
.with_message("Invalid Reference Refname") token.source(),
.with_label( "Invalid Reference Refname".into(),
Label::new((token.source().clone(), refname_match.range())) span(
.with_message(err), refname_match.range(),
err
) )
.finish(),
); );
return reports; return reports;
} }
@ -289,13 +292,10 @@ impl RegexRule for ReferenceRule {
}; };
// Properties // Properties
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(2)) let properties = match self.parse_properties(&mut reports, &token, &matches.get(2))
{ {
Ok(pm) => pm, Some(pm) => pm,
Err(report) => { None => return reports,
reports.push(report);
return reports;
}
}; };
let caption = properties let caption = properties

View file

@ -12,14 +12,13 @@ use crate::parser::source::VirtualSource;
use crate::parser::util; use crate::parser::util;
use crate::parser::util::escape_source; use crate::parser::util::escape_source;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Lua; use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
use super::text::Text; use super::text::Text;
@ -95,7 +94,7 @@ impl RegexRule for ScriptRule {
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
token: Token, token: Token,
matches: Captures, matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
let kernel_name = match matches.get(1) { let kernel_name = match matches.get(1) {
@ -104,15 +103,14 @@ impl RegexRule for ScriptRule {
match ScriptRule::validate_kernel_name(state.parser.colors(), name.as_str()) { match ScriptRule::validate_kernel_name(state.parser.colors(), name.as_str()) {
Ok(name) => name, Ok(name) => name,
Err(e) => { Err(e) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid kernel name") token.source(),
.with_label( "Invalid Kernel Name".into(),
Label::new((token.source(), name.range())) span(
.with_message(e) name.range(),
.with_color(state.parser.colors().error), e
) )
.finish(),
); );
return reports; return reports;
} }
@ -136,15 +134,14 @@ impl RegexRule for ScriptRule {
), '\\', ">@"); ), '\\', ">@");
if source.content().is_empty() if source.content().is_empty()
{ {
reports.push( report_warn!(
Report::build(ReportKind::Warning, token.source(), token.start()) &mut reports,
.with_message("Invalid kernel code") token.source(),
.with_label( "Invalid Kernel Code".into(),
Label::new((token.source(), script_range)) span(
.with_message("Kernel code is empty") script_range,
.with_color(state.parser.colors().warning), "Kernel code is empty".into(),
) )
.finish(),
); );
return reports; return reports;
} }
@ -157,15 +154,14 @@ impl RegexRule for ScriptRule {
// Exec // Exec
{ {
if let Err(e) = chunk.exec() { if let Err(e) = chunk.exec() {
reports.push( report_err!(
Report::build(ReportKind::Error, source.clone(), 0) &mut reports,
.with_message("Invalid kernel code") source.clone(),
.with_label( "Invalid Kernel Code".into(),
Label::new((source.clone(), 0..source.content().len())) span(
.with_message(format!("Kernel execution failed:\n{}", e)) 0..source.content().len(),
.with_color(state.parser.colors().error), format!("Kernel execution failed:\n{}", e)
) )
.finish(),
); );
return reports; return reports;
} }
@ -178,15 +174,14 @@ impl RegexRule for ScriptRule {
Some(kind) => match self.validate_kind(state.parser.colors(), kind.as_str()) { Some(kind) => match self.validate_kind(state.parser.colors(), kind.as_str()) {
Ok(kind) => kind, Ok(kind) => kind,
Err(msg) => { Err(msg) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), kind.start()) &mut reports,
.with_message("Invalid kernel code kind") token.source(),
.with_label( "Invalid Kernel Code Kind".into(),
Label::new((token.source(), kind.range())) span(
.with_message(msg) kind.range(),
.with_color(state.parser.colors().error), msg
) )
.finish(),
); );
return reports; return reports;
} }
@ -197,15 +192,14 @@ impl RegexRule for ScriptRule {
// Eval // Eval
{ {
if let Err(e) = chunk.eval::<()>() { if let Err(e) = chunk.eval::<()>() {
reports.push( report_err!(
Report::build(ReportKind::Error, source.clone(), 0) &mut reports,
.with_message("Invalid kernel code") source.clone(),
.with_label( "Invalid Kernel Code".into(),
Label::new((source.clone(), 0..source.content().len())) span(
.with_message(format!("Kernel evaluation failed:\n{}", e)) 0..source.content().len(),
.with_color(state.parser.colors().error), format!("Kernel evaluation failed:\n{}", e)
) )
.finish(),
); );
} }
} else } else
@ -245,18 +239,14 @@ impl RegexRule for ScriptRule {
} }
} }
Err(e) => { Err(e) => {
reports.push( report_err!(
Report::build(ReportKind::Error, source.clone(), 0) &mut reports,
.with_message("Invalid kernel code") source.clone(),
.with_label( "Invalid Kernel Code".into(),
Label::new((source.clone(), 0..source.content().len())) span(
.with_message(format!( 0..source.content().len(),
"Kernel evaluation failed:\n{}", format!("Kernel evaluation failed:\n{}", e)
e
))
.with_color(state.parser.colors().error),
) )
.finish(),
); );
} }
} }

View file

@ -13,9 +13,6 @@ use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::style::StyleHolder; use crate::parser::style::StyleHolder;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument; use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
@ -25,6 +22,8 @@ use section_style::SectionStyle;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
use super::reference::InternalReference; use super::reference::InternalReference;
@ -183,22 +182,22 @@ impl RegexRule for SectionRule {
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut result = vec![]; let mut reports = vec![];
let section_depth = match matches.get(1) { let section_depth = match matches.get(1) {
Some(depth) => { Some(depth) => {
if depth.len() > 6 { if depth.len() > 6 {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), depth.start()) &mut reports,
.with_message("Invalid section depth") token.source(),
.with_label( "Invalid Section Depth".into(),
Label::new((token.source(), depth.range())) span(
.with_message(format!("Section is of depth {}, which is greather than {} (maximum depth allowed)", depth.range(),
format!("Section is of depth {}, which is greather than {} (maximum depth allowed)",
depth.len().fg(state.parser.colors().info), depth.len().fg(state.parser.colors().info),
6.fg(state.parser.colors().info))) 6.fg(state.parser.colors().info))
.with_color(state.parser.colors().error)) )
.finish()); );
return result;
} }
depth.len() depth.len()
@ -215,25 +214,27 @@ impl RegexRule for SectionRule {
if let Some(elem_reference) = document.get_reference(refname.as_str()) { if let Some(elem_reference) = document.get_reference(refname.as_str()) {
let elem = document.get_from_reference(&elem_reference).unwrap(); let elem = document.get_from_reference(&elem_reference).unwrap();
result.push(
Report::build(ReportKind::Warning, token.source(), refname.start()) report_warn!(
.with_message("Duplicate reference name") &mut reports,
.with_label( token.source(),
Label::new((token.source(), refname.range())) "Duplicate Reference Name".into(),
.with_message(format!("Reference with name `{}` is already defined in `{}`", span(
refname.range(),
format!("Reference with name `{}` is already defined in `{}`. `{}` conflicts with previously defined reference to {}",
refname.as_str().fg(state.parser.colors().highlight), refname.as_str().fg(state.parser.colors().highlight),
elem.location().source().name().as_str().fg(state.parser.colors().highlight))) elem.location().source().name().as_str().fg(state.parser.colors().highlight),
.with_message(format!("`{}` conflicts with previously defined reference to {}",
refname.as_str().fg(state.parser.colors().highlight), refname.as_str().fg(state.parser.colors().highlight),
elem.element_name().fg(state.parser.colors().highlight))) elem.element_name().fg(state.parser.colors().highlight))
.with_color(state.parser.colors().warning)) ),
.with_label( span(
Label::new((elem.location().source(), elem.location().start()..elem.location().end() )) elem.location().source(),
.with_message(format!("`{}` previously defined here", elem.location().start()..elem.location().end(),
refname.as_str().fg(state.parser.colors().highlight))) format!("`{}` previously defined here",
.with_color(state.parser.colors().warning)) refname.as_str().fg(state.parser.colors().highlight))
.with_note("Previous reference was overwritten".to_string()) ),
.finish()); note("Previous reference was overwritten".into())
);
} }
Some(refname.as_str().to_string()) Some(refname.as_str().to_string())
}, },
@ -247,19 +248,20 @@ impl RegexRule for SectionRule {
"+" => section_kind::NO_TOC, "+" => section_kind::NO_TOC,
"" => section_kind::NONE, "" => section_kind::NONE,
_ => { _ => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), kind.start()) &mut reports,
.with_message("Invalid section numbering kind") token.source(),
.with_label( "Invalid Section Numbering Kind".into(),
Label::new((token.source(), kind.range())) span(
.with_message(format!("Section numbering kind must be a combination of `{}` for unnumbered, and `{}` for non-listing; got `{}`", kind.range(),
format!("Section numbering kind must be a combination of `{}` for unnumbered, and `{}` for non-listing; got `{}`",
"*".fg(state.parser.colors().info), "*".fg(state.parser.colors().info),
"+".fg(state.parser.colors().info), "+".fg(state.parser.colors().info),
kind.as_str().fg(state.parser.colors().highlight))) kind.as_str().fg(state.parser.colors().highlight))
.with_color(state.parser.colors().error)) ),
.with_help("Leave empty for a numbered listed section".to_string()) help("Leave empty for a numbered listed section".into())
.finish()); );
return result; return reports;
} }
}, },
_ => section_kind::NONE, _ => section_kind::NONE,
@ -278,31 +280,31 @@ impl RegexRule for SectionRule {
if section_name.is_empty() if section_name.is_empty()
// No name // No name
{ {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Missing section name") token.source(),
.with_label( "Missing Section Name".into(),
Label::new((token.source(), name.range())) span(
.with_message("Sections require a name before line end") name.range(),
.with_color(state.parser.colors().error), "Section name must be specified before line end".into()
) ),
.finish(),
); );
return result; return reports;
} }
// No spacing // No spacing
if split == 0 { if split == 0 {
result.push( report_err!(
Report::build(ReportKind::Warning, token.source(), name.start()) &mut reports,
.with_message("Missing section spacing") token.source(),
.with_label( "Missing Section Spacing".into(),
Label::new((token.source(), name.range())) span(
.with_message("Sections require at least one whitespace before the section's name") name.range(),
.with_color(state.parser.colors().warning)) "Sections require at least one whitespace before the section's name".into()
.with_help(format!("Add a space before `{}`", section_name.fg(state.parser.colors().highlight))) ),
.finish()); help(format!("Add a space before `{}`", section_name.fg(state.parser.colors().highlight)))
return result; );
return reports;
} }
section_name.to_string() section_name.to_string()
@ -347,7 +349,7 @@ impl RegexRule for SectionRule {
sems.add(matches.get(5).unwrap().range(), tokens.section_name); sems.add(matches.get(5).unwrap().range(), tokens.section_name);
} }
result reports
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {

View file

@ -14,9 +14,6 @@ use crate::parser::source::Token;
use crate::parser::state::RuleState; use crate::parser::state::RuleState;
use crate::parser::state::Scope; use crate::parser::state::Scope;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function; use mlua::Function;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
@ -24,6 +21,8 @@ use std::cell::RefCell;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
use super::paragraph::Paragraph; use super::paragraph::Paragraph;
@ -87,11 +86,11 @@ impl StyleState {
impl RuleState for StyleState { impl RuleState for StyleState {
fn scope(&self) -> Scope { Scope::PARAGRAPH } fn scope(&self) -> Scope { Scope::PARAGRAPH }
fn on_remove<'a>( fn on_remove(
&self, &self,
state: &ParserState, state: &ParserState,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
self.toggled self.toggled
@ -114,27 +113,22 @@ impl RuleState for StyleState {
) )
}) })
.unwrap(); .unwrap();
report_err!(
reports.push( &mut reports,
Report::build(ReportKind::Error, token.source(), token.start()) token.source(),
.with_message("Unterminated Style") "Unterminated Style".into(),
.with_label( span(
Label::new((token.source(), token.range.clone())) token.range.clone(),
.with_order(1) format!(
.with_message(format!(
"Style {} starts here", "Style {} starts here",
name.fg(state.parser.colors().info) name.fg(state.parser.colors().info)
))
.with_color(state.parser.colors().error),
) )
.with_label( ),
Label::new(paragraph_end) span(
.with_order(1) paragraph_end.1,
.with_message("Paragraph ends here".to_string()) "Paragraph ends here".into()
.with_color(state.parser.colors().error), ),
) note("Styles cannot span multiple documents (i.e @import)".into())
.with_note("Styles cannot span multiple documents (i.e @import)")
.finish(),
); );
}); });
@ -182,7 +176,7 @@ impl RegexRule for StyleRule {
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
_matches: Captures, _matches: Captures,
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let query = state.shared.rule_state.borrow().get(STATE_NAME); let query = state.shared.rule_state.borrow().get(STATE_NAME);
let style_state = match query { let style_state = match query {
Some(state) => state, Some(state) => state,

View file

@ -10,9 +10,6 @@ use std::sync::Arc;
use std::sync::Once; use std::sync::Once;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha2::Sha512; use crypto::sha2::Sha512;
use mlua::Function; use mlua::Function;
@ -41,6 +38,8 @@ use crate::parser::util::Property;
use crate::parser::util::PropertyMap; use crate::parser::util::PropertyMap;
use crate::parser::util::PropertyMapError; use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
enum TexKind { enum TexKind {
@ -266,39 +265,45 @@ impl TexRule {
fn parse_properties( fn parse_properties(
&self, &self,
mut reports: &mut Vec<Report>,
colors: &ReportColors, colors: &ReportColors,
token: &Token, token: &Token,
m: &Option<Match>, m: &Option<Match>,
) -> Result<PropertyMap, Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Option<PropertyMap> {
match m { match m {
None => match self.properties.default() { None => match self.properties.default() {
Ok(properties) => Ok(properties), Ok(properties) => Some(properties),
Err(e) => Err( Err(e) =>
Report::build(ReportKind::Error, token.source(), token.start()) {
.with_message("Invalid Tex Properties") report_err!(
.with_label( &mut reports,
Label::new((token.source().clone(), token.range.clone())) token.source(),
.with_message(format!("Tex is missing required property: {e}")) "Invalid Tex Properties".into(),
.with_color(colors.error), span(
token.range.clone(),
format!("Tex is missing required property: {e}")
) )
.finish(), );
), None
}
}, },
Some(props) => { Some(props) => {
let processed = let processed =
util::escape_text('\\', "]", props.as_str().trim_start().trim_end()); util::escape_text('\\', "]", props.as_str().trim_start().trim_end());
match self.properties.parse(processed.as_str()) { match self.properties.parse(processed.as_str()) {
Err(e) => Err( Err(e) => {
Report::build(ReportKind::Error, token.source(), props.start()) report_err!(
.with_message("Invalid Tex Properties") &mut reports,
.with_label( token.source(),
Label::new((token.source().clone(), props.range())) "Invalid Tex Properties".into(),
.with_message(e) span(
.with_color(colors.error), props.range(),
e
) )
.finish(), );
), None
Ok(properties) => Ok(properties), },
Ok(properties) => Some(properties),
} }
} }
} }
@ -321,25 +326,24 @@ impl RegexRule for TexRule {
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
let tex_content = match matches.get(2) { let tex_content = match matches.get(2) {
// Unterminated `$` // Unterminated `$`
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Unterminated Tex Code") token.source(),
.with_label( "Unterminated Tex Code".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
format!(
"Missing terminating `{}` after first `{}`", "Missing terminating `{}` after first `{}`",
["|$", "$"][index].fg(state.parser.colors().info), ["|$", "$"][index].fg(state.parser.colors().info),
["$|", "$"][index].fg(state.parser.colors().info) ["$|", "$"][index].fg(state.parser.colors().info)
))
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -351,15 +355,14 @@ impl RegexRule for TexRule {
); );
if processed.is_empty() { if processed.is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Warning, token.source(), content.start()) &mut reports,
.with_message("Empty Tex Code") token.source(),
.with_label( "Empty Tex Code".into(),
Label::new((token.source().clone(), content.range())) span(
.with_message("Tex code is empty") content.range(),
.with_color(state.parser.colors().warning), "Tex code is empty".into()
) )
.finish(),
); );
} }
processed processed
@ -367,13 +370,10 @@ impl RegexRule for TexRule {
}; };
// Properties // Properties
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(1)) let properties = match self.parse_properties(&mut reports, state.parser.colors(), &token, &matches.get(1))
{ {
Ok(pm) => pm, Some(pm) => pm,
Err(report) => { None => return reports,
reports.push(report);
return reports;
}
}; };
// Tex kind // Tex kind
@ -383,19 +383,19 @@ impl RegexRule for TexRule {
Ok((_prop, kind)) => kind, Ok((_prop, kind)) => kind,
Err(e) => match e { Err(e) => match e {
PropertyMapError::ParseError((prop, err)) => { PropertyMapError::ParseError((prop, err)) => {
reports.push(
Report::build(ReportKind::Error, token.source(), token.start()) report_err!(
.with_message("Invalid Tex Property") &mut reports,
.with_label( token.source(),
Label::new((token.source().clone(), token.range.clone())) "Invalid Tex Property".into(),
.with_message(format!( span(
token.range.clone(),
format!(
"Property `kind: {}` cannot be converted: {}", "Property `kind: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(state.parser.colors().info),
err.fg(state.parser.colors().error) err.fg(state.parser.colors().error)
))
.with_color(state.parser.colors().warning),
) )
.finish(), )
); );
return reports; return reports;
} }

View file

@ -2,7 +2,6 @@ use std::any::Any;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use ariadne::Report;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
@ -17,6 +16,7 @@ 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 crate::parser::source::Token;
use crate::parser::reports::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Text { pub struct Text {
@ -70,7 +70,7 @@ impl Rule for TextRule {
_document: &dyn Document, _document: &dyn Document,
_cursor: Cursor, _cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Box<dyn Any>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report>) {
panic!("Text cannot match"); panic!("Text cannot match");
} }

View file

@ -11,15 +11,14 @@ use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
use regex::Regex; use regex::Regex;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum VariableKind { enum VariableKind {
@ -134,8 +133,8 @@ impl RegexRule for VariableRule {
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut result = vec![]; let mut reports = vec![];
// [Optional] variable kind // [Optional] variable kind
let var_kind = match matches.get(1) { let var_kind = match matches.get(1) {
Some(kind) => { Some(kind) => {
@ -148,18 +147,18 @@ impl RegexRule for VariableRule {
// Unknown kind specified // Unknown kind specified
if r.is_none() { if r.is_none() {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), kind.start()) &mut reports,
.with_message("Unknown variable kind") token.source(),
.with_label( "Unknowm Variable Kind".into(),
Label::new((token.source(), kind.range())) span(
.with_message(format!( kind.range(),
format!(
"Variable kind `{}` is unknown", "Variable kind `{}` is unknown",
kind.as_str().fg(state.parser.colors().highlight) kind.as_str().fg(state.parser.colors().highlight)
))
.with_color(state.parser.colors().error),
) )
.with_help(format!( ),
help(format!(
"Leave empty for regular variables. Available variable kinds:{}", "Leave empty for regular variables. Available variable kinds:{}",
self.kinds.iter().skip(1).fold( self.kinds.iter().skip(1).fold(
"".to_string(), "".to_string(),
@ -173,10 +172,8 @@ impl RegexRule for VariableRule {
} }
) )
)) ))
.finish(),
); );
return reports;
return result;
} }
r.unwrap().0 r.unwrap().0
@ -188,21 +185,20 @@ impl RegexRule for VariableRule {
Some(name) => match VariableRule::validate_name(state.parser.colors(), name.as_str()) { Some(name) => match VariableRule::validate_name(state.parser.colors(), name.as_str()) {
Ok(var_name) => var_name, Ok(var_name) => var_name,
Err(msg) => { Err(msg) => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid variable name") token.source(),
.with_label( "Invalid Variable Name".into(),
Label::new((token.source(), name.range())) span(
.with_message(format!( name.range(),
format!(
"Variable name `{}` is not allowed. {msg}", "Variable name `{}` is not allowed. {msg}",
name.as_str().fg(state.parser.colors().highlight) name.as_str().fg(state.parser.colors().highlight)
))
.with_color(state.parser.colors().error),
) )
.finish(), ),
); );
return result; return reports;
} }
}, },
_ => panic!("Unknown variable name"), _ => panic!("Unknown variable name"),
@ -212,21 +208,20 @@ impl RegexRule for VariableRule {
Some(value) => match VariableRule::validate_value(value.as_str()) { Some(value) => match VariableRule::validate_value(value.as_str()) {
Ok(var_value) => var_value, Ok(var_value) => var_value,
Err(msg) => { Err(msg) => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), value.start()) &mut reports,
.with_message("Invalid variable value") token.source(),
.with_label( "Invalid Variable Value".into(),
Label::new((token.source(), value.range())) span(
.with_message(format!( value.range(),
format!(
"Variable value `{}` is not allowed. {msg}", "Variable value `{}` is not allowed. {msg}",
value.as_str().fg(state.parser.colors().highlight) value.as_str().fg(state.parser.colors().highlight)
))
.with_color(state.parser.colors().error),
) )
.finish(), ),
); );
return result; return reports;
} }
}, },
_ => panic!("Invalid variable value"), _ => panic!("Invalid variable value"),
@ -242,22 +237,21 @@ impl RegexRule for VariableRule {
Ok(variable) => document.add_variable(variable), Ok(variable) => document.add_variable(variable),
Err(msg) => { Err(msg) => {
let m = matches.get(0).unwrap(); let m = matches.get(0).unwrap();
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), m.start()) &mut reports,
.with_message("Unable to create variable") token.source(),
.with_label( "Unable to Create Variable".into(),
Label::new((token.source(), m.start() + 1..m.end())) span(
.with_message(format!( m.start() + 1..m.end(),
format!(
"Unable to create variable `{}`. {}", "Unable to create variable `{}`. {}",
var_name.fg(state.parser.colors().highlight), var_name.fg(state.parser.colors().highlight),
msg msg
))
.with_color(state.parser.colors().error),
) )
.finish(), ),
); );
return result; return reports;
} }
} }
@ -277,7 +271,7 @@ impl RegexRule for VariableRule {
sems.add(value.clone(), tokens.variable_value); sems.add(value.clone(), tokens.variable_value);
} }
result reports
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
@ -346,98 +340,87 @@ impl RegexRule for VariableSubstitutionRule {
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut result = vec![]; let mut reports = vec![];
let variable = match matches.get(1) { let variable = match matches.get(1) {
Some(name) => { Some(name) => {
// Empty name // Empty name
if name.as_str().is_empty() { if name.as_str().is_empty() {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Empty variable name") token.source(),
.with_label( "Empty Variable Name".into(),
Label::new((token.source(), matches.get(0).unwrap().range())) span(
.with_message( name.range(),
"Missing variable name for substitution".to_string(), "Missing variable name for substitution".into(),
) )
.with_color(state.parser.colors().error),
)
.finish(),
); );
return result; return reports;
} }
// Leading spaces // Leading spaces
else if name.as_str().trim_start() != name.as_str() { else if name.as_str().trim_start() != name.as_str() {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid variable name") token.source(),
.with_label( "Invalid Variable Name".into(),
Label::new((token.source(), name.range())) span(
.with_message( name.range(),
"Variable names contains leading spaces".to_string(), "Variable names contains leading spaces".into(),
) ),
.with_color(state.parser.colors().error), help("Remove leading spaces".into())
)
.with_help("Remove leading spaces")
.finish(),
); );
return result; return reports;
} }
// Trailing spaces // Trailing spaces
else if name.as_str().trim_end() != name.as_str() { else if name.as_str().trim_end() != name.as_str() {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid variable name") token.source(),
.with_label( "Invalid Variable Name".into(),
Label::new((token.source(), name.range())) span(
.with_message( name.range(),
"Variable names contains trailing spaces".to_string(), "Variable names contains trailing spaces".into(),
) ),
.with_color(state.parser.colors().error), help("Remove trailing spaces".into())
)
.with_help("Remove trailing spaces")
.finish(),
); );
return result; return reports;
} }
// Invalid name // Invalid name
if let Err(msg) = VariableRule::validate_name(state.parser.colors(), name.as_str()) if let Err(msg) = VariableRule::validate_name(state.parser.colors(), name.as_str())
{ {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid variable name") token.source(),
.with_label( "Invalid Variable Name".into(),
Label::new((token.source(), name.range())) span(
.with_message(msg) name.range(),
.with_color(state.parser.colors().error), msg
) )
.finish(),
); );
return result; return reports;
} }
// Get variable // Get variable
match document.get_variable(name.as_str()) { match document.get_variable(name.as_str()) {
None => { None => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Unknown variable name") token.source(),
.with_label( "Unknown Variable Name".into(),
Label::new((token.source(), name.range())) span(
.with_message(format!( name.range(),
format!(
"Unable to find variable with name: `{}`", "Unable to find variable with name: `{}`",
name.as_str().fg(state.parser.colors().highlight) name.as_str().fg(state.parser.colors().highlight)
))
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return result; return reports;
} }
Some(var) => var, Some(var) => var,
} }
@ -456,6 +439,6 @@ impl RegexRule for VariableSubstitutionRule {
sems.add(name.end..name.end + 1, tokens.variable_sub_sep); sems.add(name.end..name.end + 1, tokens.variable_sub_sep);
} }
result reports
} }
} }

View file

@ -1,14 +1,13 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::ops::Deref; use std::ops::Deref;
use ariadne::Report;
use crate::document::document::Document; use crate::document::document::Document;
use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::reports::*;
use super::parser::ParserState; use super::parser::ParserState;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -28,13 +27,13 @@ pub trait CustomStyle: core::fmt::Debug {
location: Token, location: Token,
state: &ParserState, state: &ParserState,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>>; ) -> Vec<Report>;
fn on_end<'a>( fn on_end<'a>(
&self, &self,
location: Token, location: Token,
state: &ParserState, state: &ParserState,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>>; ) -> Vec<Report>;
} }
#[derive(Default)] #[derive(Default)]

View file

@ -1,5 +1,3 @@
use ariadne::Label;
use ariadne::Report;
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashSet; use std::collections::HashSet;
@ -9,6 +7,7 @@ use unicode_segmentation::UnicodeSegmentation;
use super::customstyle::CustomStyleHolder; use super::customstyle::CustomStyleHolder;
use super::layout::LayoutHolder; use super::layout::LayoutHolder;
use super::reports::Report;
use super::rule::Rule; use super::rule::Rule;
use super::source::Cursor; use super::source::Cursor;
use super::source::Source; use super::source::Source;
@ -434,7 +433,9 @@ pub trait Parser {
/// Handles the reports produced by parsing. The default is to output them /// Handles the reports produced by parsing. The default is to output them
/// to stderr, but you are free to modify it. /// to stderr, but you are free to modify it.
fn handle_reports(&self, reports: Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { fn handle_reports(&self, reports: Vec<Report>) {
todo!(); // TODO
/*
for mut report in reports { for mut report in reports {
let mut sources: HashSet<Rc<dyn Source>> = HashSet::new(); let mut sources: HashSet<Rc<dyn Source>> = HashSet::new();
fn recurse_source(sources: &mut HashSet<Rc<dyn Source>>, source: Rc<dyn Source>) { fn recurse_source(sources: &mut HashSet<Rc<dyn Source>>, source: Rc<dyn Source>) {
@ -483,5 +484,6 @@ pub trait Parser {
}); });
report.eprint(ariadne::sources(cache)).unwrap() report.eprint(ariadne::sources(cache)).unwrap()
} }
*/
} }
} }

View file

@ -3,7 +3,7 @@ use std::{ops::Range, rc::Rc};
use super::{parser::Parser, source::{Source, SourcePosition, Token}}; use super::{parser::Parser, source::{Source, SourcePosition, Token}};
#[derive(Debug)] #[derive(Debug)]
enum ReportKind pub enum ReportKind
{ {
Error, Error,
Warning, Warning,
@ -21,18 +21,20 @@ impl Into<ariadne::ReportKind<'static>> for &ReportKind
} }
#[derive(Debug)] #[derive(Debug)]
struct ReportSpan pub struct ReportSpan
{ {
pub token: Token, pub token: Token,
pub message: String pub message: String
} }
#[derive(Debug)] #[derive(Debug)]
struct Report pub struct Report
{ {
pub kind: ReportKind, pub kind: ReportKind,
pub source: Rc<dyn Source>, pub source: Rc<dyn Source>,
pub message: String, pub message: String,
pub note: Option<String>,
pub help: Option<String>,
pub spans: Vec<ReportSpan>, pub spans: Vec<ReportSpan>,
} }
@ -86,38 +88,71 @@ impl Report
} }
} }
pub mod macros
{
pub use super::*;
#[macro_export]
macro_rules! report_label { macro_rules! report_label {
($spans:expr, $psource:expr,) => {{ }}; ($r:expr,) => {{ }};
($spans:expr, $psource:expr, span($source:expr, $range:expr, $message:expr), $(, $($tail:tt)*)?) => {{ ($r:expr, span($source:expr, $range:expr, $message:expr), $(, $($tail:tt)*)?) => {{
$spans.push(ReportSpan { $r.spans.push(ReportSpan {
token: Token::new($range, $source), token: crate::parser::source::Token::Token::new($range, $source),
message: $message, message: $message,
}); });
report_label!($spans, $psource, $($($tail)*)?); report_label!($r, $($($tail)*)?);
}}; }};
($spans:expr, $psource:expr, span($range:expr, $message:expr) $(, $($tail:tt)*)?) => {{ ($r:expr, span($range:expr, $message:expr) $(, $($tail:tt)*)?) => {{
$spans.push(ReportSpan { $r.spans.push(ReportSpan {
token: Token::new($range, $psource), token: crate::parser::source::Token::new($range, $r.source.clone()),
message: $message, message: $message,
}); });
report_label!($spans, $psource, $($($tail)*)?); report_label!($r, $($($tail)*)?);
}};
($r:expr, note($message:expr) $(, $($tail:tt)*)?) => {{
$r.note = Some($message);
report_label!($r, $($($tail)*)?);
}};
($r:expr, help($message:expr) $(, $($tail:tt)*)?) => {{
$r.help = Some($message);
report_label!($r, $($($tail)*)?);
}} }}
} }
#[macro_export] #[macro_export]
macro_rules! report_err { macro_rules! report_err {
($reports:expr, $source:expr, $message:expr, $($tail:tt)*) => {{ ($reports:expr, $source:expr, $message:expr, $($tail:tt)*) => {{
let mut spans = Vec::new(); let mut r = Report {
report_label!(spans, $source.clone(), $($tail)*);
$reports.push(Report {
kind: ReportKind::Error, kind: ReportKind::Error,
source: $source, source: $source,
message: $message, message: $message,
spans, note: None,
}); help: None,
spans: vec![],
};
report_label!(r, $($tail)*);
$reports.push(r);
}} }}
} }
#[macro_export]
macro_rules! report_warn {
($reports:expr, $source:expr, $message:expr, $($tail:tt)*) => {{
let mut r = Report {
kind: ReportKind::Warning,
source: $source,
message: $message,
note: None,
help: None,
spans: vec![],
};
report_label!(r, $($tail)*);
$reports.push(r);
}}
}
pub use crate::*;
}
#[cfg(test)] #[cfg(test)]
mod tests mod tests
{ {
@ -140,7 +175,6 @@ Dolor
)); ));
let mut reports = vec![]; let mut reports = vec![];
//let la = report_label!(source.clone(), 5..9, "Msg".into()); //let la = report_label!(source.clone(), 5..9, "Msg".into());
report_err!(&mut reports, source.clone(), "Some message".into(), report_err!(&mut reports, source.clone(), "Some message".into(),
span(5..9, "Msg".into()), span(5..9, "Msg".into()),

View file

@ -1,12 +1,12 @@
use super::layout::LayoutHolder; use super::layout::LayoutHolder;
use super::parser::ParseMode; use super::parser::ParseMode;
use super::parser::ParserState; use super::parser::ParserState;
use super::reports::Report;
use super::source::Cursor; use super::source::Cursor;
use super::source::Source; use super::source::Source;
use super::source::Token; use super::source::Token;
use super::style::StyleHolder; use super::style::StyleHolder;
use crate::document::document::Document; use crate::document::document::Document;
use ariadne::Report;
use downcast_rs::impl_downcast; use downcast_rs::impl_downcast;
use downcast_rs::Downcast; use downcast_rs::Downcast;
use mlua::Function; use mlua::Function;
@ -88,7 +88,7 @@ pub trait Rule: Downcast {
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
cursor: Cursor, cursor: Cursor,
match_data: Box<dyn Any>, match_data: Box<dyn Any>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>); ) -> (Cursor, Vec<Report>);
/// Registers lua bindings /// Registers lua bindings
fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] } fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
@ -128,7 +128,7 @@ pub trait RegexRule {
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>; ) -> Vec<Report>;
fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] } fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
fn register_styles(&self, _holder: &mut StyleHolder) {} fn register_styles(&self, _holder: &mut StyleHolder) {}
@ -175,7 +175,7 @@ impl<T: RegexRule + 'static> Rule for T {
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
cursor: Cursor, cursor: Cursor,
match_data: Box<dyn Any>, match_data: Box<dyn Any>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report>) {
let content = cursor.source.content(); let content = cursor.source.content();
let index = match_data.downcast::<usize>().unwrap(); let index = match_data.downcast::<usize>().unwrap();
let re = &self.regexes()[*index]; let re = &self.regexes()[*index];

View file

@ -1,16 +1,14 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use ariadne::Report;
use downcast_rs::impl_downcast; use downcast_rs::impl_downcast;
use downcast_rs::Downcast; use downcast_rs::Downcast;
use crate::document::document::Document; use crate::document::document::Document;
use super::parser::ParserState; use super::parser::ParserState;
use super::source::Source; use super::reports::Report;
/// Scope for state objects /// Scope for state objects
#[derive(PartialEq, PartialOrd, Debug)] #[derive(PartialEq, PartialOrd, Debug)]
@ -30,11 +28,11 @@ pub trait RuleState: Downcast {
fn scope(&self) -> Scope; fn scope(&self) -> Scope;
/// Callback called when state goes out of scope /// Callback called when state goes out of scope
fn on_remove<'a>( fn on_remove(
&self, &self,
state: &ParserState, state: &ParserState,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>>; ) -> Vec<Report>;
} }
impl_downcast!(RuleState); impl_downcast!(RuleState);
@ -72,7 +70,7 @@ impl RuleStateHolder {
state: &ParserState, state: &ParserState,
document: &dyn Document, document: &dyn Document,
scope: Scope, scope: Scope,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report> {
let mut reports = vec![]; let mut reports = vec![];
self.states.retain(|_name, rule_state| { self.states.retain(|_name, rule_state| {