diff --git a/src/elements/blockquote.rs b/src/elements/blockquote.rs index 364bfb3..bbfcee2 100644 --- a/src/elements/blockquote.rs +++ b/src/elements/blockquote.rs @@ -1,7 +1,6 @@ use core::fmt; use std::any::Any; use std::collections::HashMap; -use std::ops::Range; use std::rc::Rc; use blockquote_style::AuthorPos::After; diff --git a/src/elements/code.rs b/src/elements/code.rs index cbb1230..5b656df 100644 --- a/src/elements/code.rs +++ b/src/elements/code.rs @@ -4,9 +4,6 @@ use std::rc::Rc; use std::sync::Once; use ariadne::Fmt; -use ariadne::Label; -use ariadne::Report; -use ariadne::ReportKind; use crypto::digest::Digest; use crypto::sha2::Sha512; use mlua::Function; @@ -36,6 +33,8 @@ use crate::parser::util::PropertyMapError; use crate::parser::util::PropertyParser; use crate::parser::util::{self}; use lazy_static::lazy_static; +use crate::parser::reports::*; +use crate::parser::reports::macros::*; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum CodeKind { @@ -335,22 +334,21 @@ impl RegexRule for CodeRule { document: &dyn Document, token: Token, matches: Captures, - ) -> Vec, Range)>> { + ) -> Vec { let mut reports = vec![]; let properties = match matches.get(1) { None => match self.properties.default() { Ok(properties) => properties, Err(e) => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid code") - .with_label( - Label::new((token.source().clone(), token.range.clone())) - .with_message(format!("Code is missing properties: {e}")) - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid Code Properties".into(), + span( + token.range.clone(), + format!("Code is missing properties: {e}") + ) ); return reports; } @@ -360,15 +358,14 @@ impl RegexRule for CodeRule { util::escape_text('\\', "]", props.as_str().trim_start().trim_end()); match self.properties.parse(processed.as_str()) { Err(e) => { - reports.push( - Report::build(ReportKind::Error, token.source(), props.start()) - .with_message("Invalid Code Properties") - .with_label( - Label::new((token.source().clone(), props.range())) - .with_message(e) - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid Code Properties".into(), + span( + props.range(), + e + ) ); return reports; } @@ -382,15 +379,14 @@ impl RegexRule for CodeRule { Some(lang) => { let code_lang = lang.as_str().trim_start().trim_end().to_string(); if code_lang.is_empty() { - reports.push( - Report::build(ReportKind::Error, token.source(), lang.start()) - .with_message("Missing Code Language") - .with_label( - Label::new((token.source().clone(), lang.range())) - .with_message("No language specified") - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Missing Code Language".into(), + span( + lang.range(), + "No language specified".into() + ) ); return reports; @@ -399,18 +395,17 @@ impl RegexRule for CodeRule { .find_syntax_by_name(code_lang.as_str()) .is_none() { - reports.push( - Report::build(ReportKind::Error, token.source(), lang.start()) - .with_message("Unknown Code Language") - .with_label( - Label::new((token.source().clone(), lang.range())) - .with_message(format!( - "Language `{}` cannot be found", - code_lang.fg(state.parser.colors().info) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Unknown Code Language".into(), + span( + lang.range(), + format!( + "Language `{}` cannot be found", + code_lang.fg(state.parser.colors().info) ) - .finish(), + ) ); return reports; @@ -432,15 +427,14 @@ impl RegexRule for CodeRule { } if code_content.is_empty() { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Missing code content") - .with_label( - Label::new((token.source().clone(), token.range.clone())) - .with_message("Code content cannot be empty") - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Empty Code Content".into(), + span( + token.range.clone(), + "Code content cannot be empty".into() + ) ); return reports; } @@ -464,34 +458,31 @@ impl RegexRule for CodeRule { Err(e) => { match e { PropertyMapError::ParseError((prop, err)) => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid Code Property") - .with_label( - Label::new((token.source().clone(), token.start()+1..token.end())) - .with_message(format!("Property `line_offset: {}` cannot be converted: {}", - prop.fg(state.parser.colors().info), - err.fg(state.parser.colors().error))) - .with_color(state.parser.colors().warning)) - .finish()); + report_err!( + &mut reports, + token.source(), + "Invalid Code Property".into(), + span( + token.start()+1..token.end(), + format!("Property `line_offset: {}` cannot be converted: {}", + prop.fg(state.parser.colors().info), + err.fg(state.parser.colors().error)) + ) + ); return reports; } PropertyMapError::NotFoundError(err) => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid Code Property") - .with_label( - Label::new(( - token.source().clone(), - token.start() + 1..token.end(), - )) - .with_message(format!( - "Property `{}` doesn't exist", - err.fg(state.parser.colors().info) - )) - .with_color(state.parser.colors().warning), + report_err!( + &mut reports, + token.source(), + "Invalid Code Property".into(), + span( + token.start()+1..token.end(), + format!( + "Property `{}` doesn't exist", + err.fg(state.parser.colors().info) ) - .finish(), + ) ); return reports; } diff --git a/src/elements/comment.rs b/src/elements/comment.rs index e063246..fc8f662 100644 --- a/src/elements/comment.rs +++ b/src/elements/comment.rs @@ -8,13 +8,12 @@ use crate::parser::parser::ParserState; use crate::parser::rule::RegexRule; use crate::parser::source::Source; use crate::parser::source::Token; -use ariadne::Label; -use ariadne::Report; -use ariadne::ReportKind; use regex::Captures; use regex::Regex; use std::ops::Range; use std::rc::Rc; +use crate::parser::reports::*; +use crate::parser::reports::macros::*; #[derive(Debug)] pub struct Comment { @@ -66,7 +65,7 @@ impl RegexRule for CommentRule { document: &dyn Document, token: Token, matches: Captures, - ) -> Vec, Range)>> { + ) -> Vec { let mut reports = vec![]; let content = match matches.get(1) { @@ -74,15 +73,14 @@ impl RegexRule for CommentRule { Some(comment) => { let trimmed = comment.as_str().trim_start().trim_end().to_string(); if trimmed.is_empty() { - reports.push( - Report::build(ReportKind::Warning, token.source(), comment.start()) - .with_message("Empty comment") - .with_label( - Label::new((token.source(), comment.range())) - .with_message("Comment is empty") - .with_color(state.parser.colors().warning), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Empty Comment".into(), + span( + comment.range(), + "Comment is empty".into() + ) ); } diff --git a/src/elements/graphviz.rs b/src/elements/graphviz.rs index c715dd7..b194641 100644 --- a/src/elements/graphviz.rs +++ b/src/elements/graphviz.rs @@ -11,9 +11,6 @@ use crate::parser::util::Property; use crate::parser::util::PropertyMapError; use crate::parser::util::PropertyParser; use ariadne::Fmt; -use ariadne::Label; -use ariadne::Report; -use ariadne::ReportKind; use crypto::digest::Digest; use crypto::sha2::Sha512; use graphviz_rust::cmd::Format; @@ -36,6 +33,8 @@ use crate::parser::rule::RegexRule; use crate::parser::source::Source; use crate::parser::source::Token; use crate::parser::util; +use crate::parser::reports::*; +use crate::parser::reports::macros::*; #[derive(Debug)] struct Graphviz { @@ -204,25 +203,24 @@ impl RegexRule for GraphRule { document: &dyn Document, token: Token, matches: Captures, - ) -> Vec, Range)>> { + ) -> Vec { let mut reports = vec![]; let graph_content = match matches.get(2) { // Unterminated `[graph]` None => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Unterminated Graph Code") - .with_label( - Label::new((token.source().clone(), token.range.clone())) - .with_message(format!( - "Missing terminating `{}` after first `{}`", - "[/graph]".fg(state.parser.colors().info), - "[graph]".fg(state.parser.colors().info) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Unterminamted Graph Code".into(), + span( + token.range.clone(), + format!( + "Missing terminating `{}` after first `{}`", + "[/graph]".fg(state.parser.colors().info), + "[graph]".fg(state.parser.colors().info) ) - .finish(), + ) ); return reports; } @@ -230,19 +228,18 @@ impl RegexRule for GraphRule { let processed = util::escape_text( '\\', "[/graph]", - content.as_str().trim_start().trim_end(), + content.as_str(), ); if processed.is_empty() { - reports.push( - Report::build(ReportKind::Error, token.source(), content.start()) - .with_message("Empty Graph Code") - .with_label( - Label::new((token.source().clone(), content.range())) - .with_message("Graph code is empty") - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Empty Graph Code".into(), + span( + content.range(), + "Graph code is empty".into() + ) ); return reports; } @@ -255,15 +252,14 @@ impl RegexRule for GraphRule { None => match self.properties.default() { Ok(properties) => properties, Err(e) => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid Graph") - .with_label( - Label::new((token.source().clone(), token.range.clone())) - .with_message(format!("Graph is missing property: {e}")) - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid Graph Properties".into(), + span( + token.range.clone(), + format!("Graph is missing property: {e}") + ) ); return reports; } @@ -273,15 +269,14 @@ impl RegexRule for GraphRule { util::escape_text('\\', "]", props.as_str().trim_start().trim_end()); match self.properties.parse(processed.as_str()) { Err(e) => { - reports.push( - Report::build(ReportKind::Error, token.source(), props.start()) - .with_message("Invalid Graph Properties") - .with_label( - Label::new((token.source().clone(), props.range())) - .with_message(e) - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid Graph Properties".into(), + span( + props.range(), + e + ) ); return reports; } @@ -297,35 +292,30 @@ impl RegexRule for GraphRule { Ok((_prop, kind)) => kind, Err(e) => match e { PropertyMapError::ParseError((prop, err)) => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid Graph Property") - .with_label( - Label::new((token.source().clone(), token.range.clone())) - .with_message(format!( - "Property `layout: {}` cannot be converted: {}", + report_err!( + &mut reports, + token.source(), + "Invalid Graph Property".into(), + span( + token.range.clone(), + format!( + "Property `{}` cannot be converted: {}", prop.fg(state.parser.colors().info), err.fg(state.parser.colors().error) - )) - .with_color(state.parser.colors().warning), - ) - .finish(), + ) + ) ); return reports; } PropertyMapError::NotFoundError(err) => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid Graph Property") - .with_label( - Label::new(( - token.source().clone(), - token.start() + 1..token.end(), - )) - .with_message(err) - .with_color(state.parser.colors().warning), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid Graph Property".into(), + span( + token.start() + 1..token.end(), + err + ) ); return reports; } @@ -340,21 +330,17 @@ impl RegexRule for GraphRule { Ok((_, kind)) => kind, Err(e) => match e { PropertyMapError::NotFoundError(err) => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid Graph Property") - .with_label( - Label::new(( - token.source().clone(), - token.start() + 1..token.end(), - )) - .with_message(format!( - "Property `{}` is missing", - err.fg(state.parser.colors().info) - )) - .with_color(state.parser.colors().warning), + report_err!( + &mut reports, + token.source(), + "Invalid Graph Property".into(), + span( + token.start() + 1..token.end(), + format!( + "Property `{}` is missing", + err.fg(state.parser.colors().info) ) - .finish(), + ) ); return reports; } diff --git a/src/elements/import.rs b/src/elements/import.rs index e469f2a..e417cc7 100644 --- a/src/elements/import.rs +++ b/src/elements/import.rs @@ -9,13 +9,12 @@ use crate::parser::source::Source; use crate::parser::source::SourceFile; use crate::parser::source::Token; use ariadne::Fmt; -use ariadne::Label; -use ariadne::Report; -use ariadne::ReportKind; use regex::Captures; use regex::Regex; use std::ops::Range; use std::rc::Rc; +use crate::parser::reports::*; +use crate::parser::reports::macros::*; use super::paragraph::Paragraph; @@ -57,65 +56,62 @@ impl RegexRule for ImportRule { document: &'a dyn Document<'a>, token: Token, matches: Captures, - ) -> Vec, Range)>> { - let mut result = vec![]; + ) -> Vec { + let mut reports = vec![]; // Path let import_file = match matches.get(2) { Some(name) => match ImportRule::validate_name(state.parser.colors(), name.as_str()) { Err(msg) => { - result.push( - Report::build(ReportKind::Error, token.source(), name.start()) - .with_message("Invalid name for import") - .with_label( - Label::new((token.source(), name.range())) - .with_message(format!( - "Import name `{}` is invalid. {msg}", - name.as_str().fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Invalid Import Name".into(), + span( + name.range(), + format!( + "Import name `{}` is invalid. {msg}", + name.as_str().fg(state.parser.colors().highlight) ) - .finish(), + ) ); - return result; + return reports; } Ok(filename) => { let meta = match std::fs::metadata(filename.as_str()) { Err(_) => { - result.push( - Report::build(ReportKind::Error, token.source(), name.start()) - .with_message("Invalid import path") - .with_label( - Label::new((token.source(), name.range())) - .with_message(format!( - "Unable to access file `{}`", - filename.fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Invalid Import Path".into(), + span( + name.range(), + format!( + "Unable to access file `{}`", + filename.fg(state.parser.colors().highlight) ) - .finish(), + ) ); - return result; + return reports; } Ok(meta) => meta, }; if !meta.is_file() { - result.push( - Report::build(ReportKind::Error, token.source(), name.start()) - .with_message("Invalid import path") - .with_label( - Label::new((token.source(), name.range())) - .with_message(format!( - "Path `{}` is not a file!", - filename.fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Invalid Import Path".into(), + span( + name.range(), + format!( + "Path `{}` is not a file!", + filename.fg(state.parser.colors().highlight) ) - .finish(), + ) ); - return result; + return reports; } filename @@ -130,21 +126,20 @@ impl RegexRule for ImportRule { { Ok(as_name) => as_name, Err(msg) => { - result.push( - Report::build(ReportKind::Error, token.source(), as_name.start()) - .with_message("Invalid name for import as") - .with_label( - Label::new((token.source(), as_name.range())) - .with_message(format!( - "Canot import `{import_file}` as `{}`. {msg}", - as_name.as_str().fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Invalid Import As".into(), + span( + as_name.range(), + format!( + "Canot import `{import_file}` as `{}`. {msg}", + as_name.as_str().fg(state.parser.colors().highlight) ) - .finish(), + ) ); - return result; + return reports; } }, _ => "".to_string(), @@ -153,17 +148,16 @@ impl RegexRule for ImportRule { let import = match SourceFile::new(import_file, Some(token.clone())) { Ok(import) => Rc::new(import), Err(path) => { - result.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Unable to read file content") - .with_label( - Label::new((token.source(), token.range)) - .with_message(format!("Failed to read content from path `{path}`")) - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid Import File".into(), + span( + token.range.clone(), + format!("Failed to read content from path `{path}`") + ) ); - return result; + return reports; } }; @@ -212,6 +206,6 @@ impl RegexRule for ImportRule { sems.add(path, tokens.import_path); } - result + reports } } diff --git a/src/elements/layout.rs b/src/elements/layout.rs index d4cc825..a2a352c 100644 --- a/src/elements/layout.rs +++ b/src/elements/layout.rs @@ -341,10 +341,10 @@ impl LayoutRule { token: &Token, layout_type: Rc, properties: Option, - ) -> Option> { + ) -> Result>, ()> { match properties { None => match layout_type.parse_properties("") { - Ok(props) => props, + Ok(props) => Ok(props), Err(err) => { report_err!( &mut reports, @@ -352,28 +352,28 @@ impl LayoutRule { "Invalid Layout Properties".into(), span( token.range.clone(), - format!("Layout is missing required property: {eee}") + format!("Layout is missing required property: {err}") ) ); - None + Err(()) } }, Some(props) => { - let trimmed = props.as_str().trim_start().trim_end(); - let content = escape_text('\\', "]", trimmed); + let content = escape_text('\\', "]", props.as_str()); match layout_type.parse_properties(content.as_str()) { Ok(props) => Ok(props), Err(err) => { - Err( - Report::build(ReportKind::Error, token.source(), props.start()) - .with_message("Unable to parse layout properties") - .with_label( - Label::new((token.source(), props.range())) - .with_message(err) - .with_color(colors.error), - ) - .finish(), - ) + + report_err!( + &mut reports, + token.source(), + "Invalid Layout Properties".into(), + span( + props.range(), + err + ) + ); + Err(()) } } } @@ -399,7 +399,7 @@ impl RegexRule for LayoutRule { document: &dyn Document, token: Token, matches: Captures, - ) -> Vec, Range)>> { + ) -> Vec { let mut reports = vec![]; let rule_state = LayoutRule::initialize_state(state); @@ -409,18 +409,17 @@ impl RegexRule for LayoutRule { { match matches.get(2) { None => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Missing Layout Name") - .with_label( - Label::new((token.source(), token.range.clone())) - .with_message(format!( - "Missing layout name after `{}`", - "#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Missing Layout Name".into(), + span( + token.range.clone(), + format!( + "Missing layout name after `{}`", + "#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) ) - .finish(), + ) ); return reports; } @@ -429,35 +428,33 @@ impl RegexRule for LayoutRule { if name.as_str().is_empty() || trimmed.is_empty() // Empty name { - reports.push( - Report::build(ReportKind::Error, token.source(), name.start()) - .with_message("Empty Layout Name") - .with_label( - Label::new((token.source(), token.range.clone())) - .with_message(format!( - "Empty layout name after `{}`", - "#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Empty Layout Name".into(), + span( + name.range(), + format!( + "Empty layout name after `{}`", + "#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) ) - .finish(), + ) ); return reports; } else if !name.as_str().chars().next().unwrap().is_whitespace() // Missing space { - reports.push( - Report::build(ReportKind::Error, token.source(), name.start()) - .with_message("Invalid Layout Name") - .with_label( - Label::new((token.source(), name.range())) - .with_message(format!( - "Missing a space before layout name `{}`", - name.as_str().fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Invalid Layout Name".into(), + span( + name.range(), + format!( + "Missing a space before layout name `{}`", + name.as_str().fg(state.parser.colors().highlight) ) - .finish(), + ) ); return reports; } @@ -465,18 +462,18 @@ impl RegexRule for LayoutRule { // Get layout let layout_type = match state.shared.layouts.borrow().get(trimmed) { None => { - reports.push( - Report::build(ReportKind::Error, token.source(), name.start()) - .with_message("Unknown Layout") - .with_label( - Label::new((token.source(), name.range())) - .with_message(format!( - "Cannot find layout `{}`", - trimmed.fg(state.parser.colors().highlight) - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Unknown Layout".into(), + span( + name.range(), + format!( + "Cannot find layout `{}`", + trimmed.fg(state.parser.colors().highlight) ) - .finish(), + ) + ); return reports; } @@ -485,16 +482,13 @@ impl RegexRule for LayoutRule { // Parse properties let properties = match LayoutRule::parse_properties( - state.parser.colors(), + &mut reports, &token, layout_type.clone(), matches.get(1), ) { Ok(props) => props, - Err(rep) => { - reports.push(rep); - return reports; - } + Err(()) => return reports, }; state.push( @@ -548,15 +542,14 @@ impl RegexRule for LayoutRule { let (tokens, layout_type) = match layout_state.stack.last_mut() { None => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid #+LAYOUT_NEXT") - .with_label( - Label::new((token.source(), token.range.clone())) - .with_message("No active layout found".to_string()) - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid #+LAYOUT_NEXT".into(), + span( + token.range.clone(), + "No active layout found".into() + ) ); return reports; } @@ -566,35 +559,31 @@ impl RegexRule for LayoutRule { if layout_type.expects().end < tokens.len() // Too many blocks { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Unexpected #+LAYOUT_NEXT") - .with_label( - Label::new((token.source(), token.range.clone())) - .with_message(format!( - "Layout expects a maximum of {} blocks, currently at {}", - layout_type.expects().end.fg(state.parser.colors().info), - tokens.len().fg(state.parser.colors().info), - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Unexpected #+LAYOUT_NEXT".into(), + span( + token.range.clone(), + format!( + "Layout expects a maximum of {} blocks, currently at {}", + layout_type.expects().end.fg(state.parser.colors().info), + tokens.len().fg(state.parser.colors().info), ) - .finish(), + ) ); return reports; } // Parse properties let properties = match LayoutRule::parse_properties( - state.parser.colors(), + &mut reports, &token, layout_type.clone(), matches.get(1), ) { Ok(props) => props, - Err(rep) => { - reports.push(rep); - return reports; - } + Err(rep) => return reports, }; if let Some((sems, tokens)) = @@ -630,15 +619,14 @@ impl RegexRule for LayoutRule { let (tokens, layout_type) = match layout_state.stack.last_mut() { None => { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Invalid #+LAYOUT_END") - .with_label( - Label::new((token.source(), token.range.clone())) - .with_message("No active layout found".to_string()) - .with_color(state.parser.colors().error), - ) - .finish(), + report_err!( + &mut reports, + token.source(), + "Invalid #+LAYOUT_NEXT".into(), + span( + token.range.clone(), + "No active layout found".into() + ) ); return reports; } @@ -648,35 +636,31 @@ impl RegexRule for LayoutRule { if layout_type.expects().start > tokens.len() // Not enough blocks { - reports.push( - Report::build(ReportKind::Error, token.source(), token.start()) - .with_message("Unexpected #+LAYOUT_END") - .with_label( - Label::new((token.source(), token.range.clone())) - .with_message(format!( - "Layout expects a minimum of {} blocks, currently at {}", - layout_type.expects().start.fg(state.parser.colors().info), - tokens.len().fg(state.parser.colors().info), - )) - .with_color(state.parser.colors().error), + report_err!( + &mut reports, + token.source(), + "Unexpected #+LAYOUT_NEXT".into(), + span( + token.range.clone(), + format!( + "Layout expects a minimum of {} blocks, currently at {}", + layout_type.expects().start.fg(state.parser.colors().info), + tokens.len().fg(state.parser.colors().info), ) - .finish(), + ) ); return reports; } // Parse properties let properties = match LayoutRule::parse_properties( - state.parser.colors(), + &mut reports, &token, layout_type.clone(), matches.get(1), ) { Ok(props) => props, - Err(rep) => { - reports.push(rep); - return reports; - } + Err(rep) => return reports, }; let layout_type = layout_type.clone(); diff --git a/src/elements/list.rs b/src/elements/list.rs index 045a591..480c840 100644 --- a/src/elements/list.rs +++ b/src/elements/list.rs @@ -1,7 +1,6 @@ use std::any::Any; use std::cell::Ref; use std::collections::HashMap; -use std::ops::Range; use std::rc::Rc; use crate::compiler::compiler::Compiler; @@ -429,6 +428,7 @@ impl Rule for ListRule { #[cfg(test)] mod tests { use super::*; + use crate::parser::source::Source; use crate::elements::paragraph::Paragraph; use crate::elements::text::Text; use crate::parser::langparser::LangParser; diff --git a/src/elements/paragraph.rs b/src/elements/paragraph.rs index de34324..f31979a 100644 --- a/src/elements/paragraph.rs +++ b/src/elements/paragraph.rs @@ -1,8 +1,5 @@ use std::any::Any; -use std::ops::Range; -use std::rc::Rc; -use ariadne::Report; use regex::Regex; use crate::compiler::compiler::Compiler; @@ -17,6 +14,7 @@ use crate::parser::rule::Rule; use crate::parser::source::Cursor; use crate::parser::source::Source; use crate::parser::source::Token; +use crate::parser::reports::*; // TODO: Full refactor // Problem is that document parsed from other sources i.e by variables @@ -132,7 +130,7 @@ impl Rule for ParagraphRule { document: &dyn Document, cursor: Cursor, _match_data: Box, - ) -> (Cursor, Vec, Range)>>) { + ) -> (Cursor, Vec) { let end_cursor = match self.re.captures_at(cursor.source.content(), cursor.pos) { None => panic!("Unknown error"), Some(capture) => cursor.at(capture.get(0).unwrap().end() - 1), @@ -152,6 +150,8 @@ impl Rule for ParagraphRule { #[cfg(test)] mod tests { + use super::*; + use std::rc::Rc; use crate::elements::paragraph::Paragraph; use crate::elements::text::Text; use crate::parser::langparser::LangParser; @@ -159,7 +159,6 @@ mod tests { use crate::parser::source::SourceFile; use crate::validate_document; - use super::*; #[test] fn parse() { diff --git a/src/elements/raw.rs b/src/elements/raw.rs index cfa190e..24c2370 100644 --- a/src/elements/raw.rs +++ b/src/elements/raw.rs @@ -272,6 +272,8 @@ impl RegexRule for RawRule { #[cfg(test)] mod tests { use super::*; + use crate::parser::source::Source; + use std::rc::Rc; use crate::elements::paragraph::Paragraph; use crate::elements::text::Text; use crate::parser::langparser::LangParser; diff --git a/src/elements/script.rs b/src/elements/script.rs index 9a05338..4172027 100644 --- a/src/elements/script.rs +++ b/src/elements/script.rs @@ -15,7 +15,6 @@ use ariadne::Fmt; use mlua::Lua; use regex::Captures; use regex::Regex; -use std::ops::Range; use std::rc::Rc; use crate::parser::reports::*; use crate::parser::reports::macros::*; @@ -140,7 +139,7 @@ impl RegexRule for ScriptRule { "Invalid Kernel Code".into(), span( script_range, - "Kernel code is empty".into(), + "Kernel code is empty".into() ) ); return reports; diff --git a/src/elements/variable.rs b/src/elements/variable.rs index 1fd7f7a..65b3541 100644 --- a/src/elements/variable.rs +++ b/src/elements/variable.rs @@ -353,7 +353,7 @@ impl RegexRule for VariableSubstitutionRule { "Empty Variable Name".into(), span( name.range(), - "Missing variable name for substitution".into(), + "Missing variable name for substitution".into() ) ); @@ -367,7 +367,7 @@ impl RegexRule for VariableSubstitutionRule { "Invalid Variable Name".into(), span( name.range(), - "Variable names contains leading spaces".into(), + "Variable names contains leading spaces".into() ), help("Remove leading spaces".into()) ); @@ -382,7 +382,7 @@ impl RegexRule for VariableSubstitutionRule { "Invalid Variable Name".into(), span( name.range(), - "Variable names contains trailing spaces".into(), + "Variable names contains trailing spaces".into() ), help("Remove trailing spaces".into()) ); diff --git a/src/main.rs b/src/main.rs index b47bb03..3dc9690 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,7 @@ fn main() -> ExitCode { let args: Vec = env::args().collect(); let program = args[0].clone(); - slet mut opts = Options::new(); + let mut opts = Options::new(); opts.optopt("i", "input", "Input path", "PATH"); opts.optopt("o", "output", "Output path", "PATH"); opts.optopt("d", "database", "Cache database location", "PATH"); diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 519af86..8a88b29 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -434,7 +434,8 @@ pub trait Parser { /// Handles the reports produced by parsing. The default is to output them /// to stderr, but you are free to modify it. fn handle_reports(&self, reports: Vec) { - todo!(); // TODO + Report::reports_to_stdout(self.colors(), reports); + //todo!(); // TODO /* for mut report in reports { let mut sources: HashSet> = HashSet::new(); diff --git a/src/parser/reports.rs b/src/parser/reports.rs index 419b19b..ba9184c 100644 --- a/src/parser/reports.rs +++ b/src/parser/reports.rs @@ -1,35 +1,35 @@ -use std::{ops::Range, rc::Rc}; +use std::collections::HashMap; +use std::ops::Range; +use std::rc::Rc; -use super::{parser::Parser, source::{Source, SourcePosition, Token}}; +use super::parser::ReportColors; +use super::source::Source; +use super::source::SourcePosition; +use super::source::Token; #[derive(Debug)] -pub enum ReportKind -{ +pub enum ReportKind { Error, Warning, } -impl Into> for &ReportKind -{ - fn into(self) -> ariadne::ReportKind<'static> { - match self - { +impl Into> for &ReportKind { + fn into(self) -> ariadne::ReportKind<'static> { + match self { ReportKind::Error => ariadne::ReportKind::Error, ReportKind::Warning => ariadne::ReportKind::Warning, } - } + } } #[derive(Debug)] -pub struct ReportSpan -{ +pub struct ReportSpan { pub token: Token, - pub message: String + pub message: String, } #[derive(Debug)] -pub struct Report -{ +pub struct Report { pub kind: ReportKind, pub source: Rc, pub message: String, @@ -38,65 +38,72 @@ pub struct Report pub spans: Vec, } -impl Report -{ - fn ariadne_format(fmt: &str, parser: &dyn Parser) -> String - { - // TODO: Colors - return fmt.to_string(); - } - - fn ariadne_color(kind: &ReportKind, parser: &dyn Parser) -> ariadne::Color - { - match kind - { - ReportKind::Error => parser.colors().error, - ReportKind::Warning => parser.colors().warning, +impl Report { + fn ariadne_color(kind: &ReportKind, colors: &ReportColors) -> ariadne::Color { + match kind { + ReportKind::Error => colors.error, + ReportKind::Warning => colors.warning, } } - pub fn to_ariadne(self, parser: &dyn Parser) -> ariadne::Report<'static, (Rc, Range)> - { + pub fn to_ariadne( + self, + colors: &ReportColors, + ) -> ( + ariadne::Report<'static, (Rc, Range)>, + impl ariadne::Cache>, + ) { + let mut cache = HashMap::new(); let source = self.source.original_position(0).0; let mut start = usize::MAX; - for span in &self.spans - { + for span in &self.spans { let (osource, opos) = span.token.source().original_position(span.token.start()); - if &osource == &source && opos < start - { + if &osource == &source && opos < start { start = opos; } } - if start == usize::MAX - { + if start == usize::MAX { start = 0; } + cache.insert(source.clone(), source.content().clone()); let mut builder = ariadne::Report::build((&self.kind).into(), self.source, start) - .with_message(Self::ariadne_format(self.message.as_str(), parser)); + .with_message(self.message); - for span in self.spans - { + for span in self.spans { + cache.insert(span.token.source(), span.token.source().content().clone()); builder = builder.with_label( - ariadne::Label::new((span.token.source(), span.token.range)) - .with_message(Self::ariadne_format(span.message.as_str(), parser)) - .with_color(Self::ariadne_color(&self.kind, parser)) - ) + ariadne::Label::new(span.token.source().original_range(span.token.range)) + .with_message(span.message) + .with_color(Self::ariadne_color(&self.kind, colors)), + ) + } + if let Some(help) = &self.help { + builder.set_help(help); + } + if let Some(note) = &self.note { + builder.set_note(note); } - builder.finish() + (builder.finish(), ariadne::sources(cache)) + } + + pub fn reports_to_stdout(colors: &ReportColors, mut reports: Vec) { + reports.drain(..).for_each(|report| { + let (report, cache) = report.to_ariadne(colors); + report.eprint(cache).unwrap(); + }); } } -pub mod macros -{ +pub mod macros { pub use super::*; -#[macro_export] + #[macro_export] macro_rules! report_label { ($r:expr,) => {{ }}; - ($r:expr, span($source:expr, $range:expr, $message:expr), $(, $($tail:tt)*)?) => {{ + ($r:expr, span($source:expr, $range:expr, $message:expr) $(, $($tail:tt)*)?) => {{ $r.spans.push(ReportSpan { - token: crate::parser::source::Token::Token::new($range, $source), + token: crate::parser::source::Token::new($range, $source), message: $message, }); report_label!($r, $($($tail)*)?); @@ -152,34 +159,3 @@ pub mod macros pub use crate::*; } - -#[cfg(test)] -mod tests -{ - use crate::parser::source::SourceFile; - use super::*; - - #[test] - fn te() - { - let source = Rc::new(SourceFile::with_content( - "".to_string(), - r#" -Sit - Lorem - Ipsum -Dolor - "# - .to_string(), - None, - )); - - let mut reports = vec![]; - //let la = report_label!(source.clone(), 5..9, "Msg".into()); - report_err!(&mut reports, source.clone(), "Some message".into(), - span(5..9, "Msg".into()), - span(5..9, "Another".into()), - ); - println!("Report = {reports:#?}"); - } -} diff --git a/src/server.rs b/src/server.rs index a47e5d7..33e2101 100644 --- a/src/server.rs +++ b/src/server.rs @@ -14,7 +14,6 @@ use parser::parser::ParseMode; use parser::parser::Parser; use parser::parser::ParserState; use parser::source::SourceFile; -use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::Client; use tower_lsp::LanguageServer; @@ -65,7 +64,7 @@ impl Backend { #[tower_lsp::async_trait] impl LanguageServer for Backend { - async fn initialize(&self, _params: InitializeParams) -> Result { + async fn initialize(&self, _params: InitializeParams) -> tower_lsp::jsonrpc::Result { Ok(InitializeResult { capabilities: ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Kind( @@ -118,7 +117,7 @@ impl LanguageServer for Backend { .await; } - async fn shutdown(&self) -> Result<()> { Ok(()) } + async fn shutdown(&self) -> tower_lsp::jsonrpc::Result<()> { Ok(()) } async fn did_open(&self, params: DidOpenTextDocumentParams) { self.client @@ -139,7 +138,7 @@ impl LanguageServer for Backend { .await } - async fn completion(&self, _params: CompletionParams) -> Result> { + async fn completion(&self, _params: CompletionParams) -> tower_lsp::jsonrpc::Result> { //let uri = params.text_document_position.text_document.uri; //let position = params.text_document_position.position; let completions = || -> Option> { @@ -153,7 +152,7 @@ impl LanguageServer for Backend { async fn semantic_tokens_full( &self, params: SemanticTokensParams, - ) -> Result> { + ) -> tower_lsp::jsonrpc::Result> { let uri = params.text_document.uri.to_string(); self.client .log_message(MessageType::LOG, "semantic_token_full")