Reports refactor [2/2]

This commit is contained in:
ef3d0c3e 2024-10-24 09:54:52 +02:00
parent 72954cdad8
commit ea29c4bb53
15 changed files with 379 additions and 451 deletions

View file

@ -1,7 +1,6 @@
use core::fmt; use core::fmt;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use blockquote_style::AuthorPos::After; use blockquote_style::AuthorPos::After;

View file

@ -4,9 +4,6 @@ use std::rc::Rc;
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;
@ -36,6 +33,8 @@ 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 lazy_static::lazy_static; use lazy_static::lazy_static;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CodeKind { enum CodeKind {
@ -335,22 +334,21 @@ impl RegexRule for CodeRule {
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 properties = match matches.get(1) { let properties = match matches.get(1) {
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 code") token.source(),
.with_label( "Invalid Code Properties".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!("Code is missing properties: {e}")) token.range.clone(),
.with_color(state.parser.colors().error), format!("Code is missing properties: {e}")
) )
.finish(),
); );
return reports; return reports;
} }
@ -360,15 +358,14 @@ impl RegexRule for CodeRule {
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 Code Properties") token.source(),
.with_label( "Invalid Code Properties".into(),
Label::new((token.source().clone(), props.range())) span(
.with_message(e) props.range(),
.with_color(state.parser.colors().error), e
) )
.finish(),
); );
return reports; return reports;
} }
@ -382,15 +379,14 @@ impl RegexRule for CodeRule {
Some(lang) => { Some(lang) => {
let code_lang = lang.as_str().trim_start().trim_end().to_string(); let code_lang = lang.as_str().trim_start().trim_end().to_string();
if code_lang.is_empty() { if code_lang.is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), lang.start()) &mut reports,
.with_message("Missing Code Language") token.source(),
.with_label( "Missing Code Language".into(),
Label::new((token.source().clone(), lang.range())) span(
.with_message("No language specified") lang.range(),
.with_color(state.parser.colors().error), "No language specified".into()
) )
.finish(),
); );
return reports; return reports;
@ -399,18 +395,17 @@ impl RegexRule for CodeRule {
.find_syntax_by_name(code_lang.as_str()) .find_syntax_by_name(code_lang.as_str())
.is_none() .is_none()
{ {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), lang.start()) &mut reports,
.with_message("Unknown Code Language") token.source(),
.with_label( "Unknown Code Language".into(),
Label::new((token.source().clone(), lang.range())) span(
.with_message(format!( lang.range(),
"Language `{}` cannot be found", format!(
code_lang.fg(state.parser.colors().info) "Language `{}` cannot be found",
)) code_lang.fg(state.parser.colors().info)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
@ -432,15 +427,14 @@ impl RegexRule for CodeRule {
} }
if code_content.is_empty() { if code_content.is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Missing code content") token.source(),
.with_label( "Empty Code Content".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message("Code content cannot be empty") token.range.clone(),
.with_color(state.parser.colors().error), "Code content cannot be empty".into()
) )
.finish(),
); );
return reports; return reports;
} }
@ -464,34 +458,31 @@ impl RegexRule for CodeRule {
Err(e) => { Err(e) => {
match 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 Code Property") token.source(),
.with_label( "Invalid Code Property".into(),
Label::new((token.source().clone(), token.start()+1..token.end())) span(
.with_message(format!("Property `line_offset: {}` cannot be converted: {}", token.start()+1..token.end(),
prop.fg(state.parser.colors().info), format!("Property `line_offset: {}` cannot be converted: {}",
err.fg(state.parser.colors().error))) prop.fg(state.parser.colors().info),
.with_color(state.parser.colors().warning)) err.fg(state.parser.colors().error))
.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 Code Property".into(),
Label::new(( span(
token.source().clone(), token.start()+1..token.end(),
token.start() + 1..token.end(), format!(
)) "Property `{}` doesn't exist",
.with_message(format!( err.fg(state.parser.colors().info)
"Property `{}` doesn't exist",
err.fg(state.parser.colors().info)
))
.with_color(state.parser.colors().warning),
) )
.finish(), )
); );
return reports; return reports;
} }

View file

@ -8,13 +8,12 @@ use crate::parser::parser::ParserState;
use crate::parser::rule::RegexRule; 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::Label;
use ariadne::Report;
use ariadne::ReportKind;
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::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Comment { pub struct Comment {
@ -66,7 +65,7 @@ impl RegexRule for CommentRule {
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 content = match matches.get(1) { let content = match matches.get(1) {
@ -74,15 +73,14 @@ impl RegexRule for CommentRule {
Some(comment) => { Some(comment) => {
let trimmed = comment.as_str().trim_start().trim_end().to_string(); let trimmed = comment.as_str().trim_start().trim_end().to_string();
if trimmed.is_empty() { if trimmed.is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Warning, token.source(), comment.start()) &mut reports,
.with_message("Empty comment") token.source(),
.with_label( "Empty Comment".into(),
Label::new((token.source(), comment.range())) span(
.with_message("Comment is empty") comment.range(),
.with_color(state.parser.colors().warning), "Comment is empty".into()
) )
.finish(),
); );
} }

View file

@ -11,9 +11,6 @@ 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::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 graphviz_rust::cmd::Format; use graphviz_rust::cmd::Format;
@ -36,6 +33,8 @@ 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 crate::parser::util; use crate::parser::util;
use crate::parser::reports::*;
use crate::parser::reports::macros::*;
#[derive(Debug)] #[derive(Debug)]
struct Graphviz { struct Graphviz {
@ -204,25 +203,24 @@ impl RegexRule for GraphRule {
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 graph_content = match matches.get(2) { let graph_content = match matches.get(2) {
// Unterminated `[graph]` // Unterminated `[graph]`
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Unterminated Graph Code") token.source(),
.with_label( "Unterminamted Graph Code".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
"Missing terminating `{}` after first `{}`", format!(
"[/graph]".fg(state.parser.colors().info), "Missing terminating `{}` after first `{}`",
"[graph]".fg(state.parser.colors().info) "[/graph]".fg(state.parser.colors().info),
)) "[graph]".fg(state.parser.colors().info)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -230,19 +228,18 @@ impl RegexRule for GraphRule {
let processed = util::escape_text( let processed = util::escape_text(
'\\', '\\',
"[/graph]", "[/graph]",
content.as_str().trim_start().trim_end(), content.as_str(),
); );
if processed.is_empty() { if processed.is_empty() {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), content.start()) &mut reports,
.with_message("Empty Graph Code") token.source(),
.with_label( "Empty Graph Code".into(),
Label::new((token.source().clone(), content.range())) span(
.with_message("Graph code is empty") content.range(),
.with_color(state.parser.colors().error), "Graph code is empty".into()
) )
.finish(),
); );
return reports; return reports;
} }
@ -255,15 +252,14 @@ impl RegexRule for GraphRule {
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 Graph") token.source(),
.with_label( "Invalid Graph Properties".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!("Graph is missing property: {e}")) token.range.clone(),
.with_color(state.parser.colors().error), format!("Graph is missing property: {e}")
) )
.finish(),
); );
return reports; return reports;
} }
@ -273,15 +269,14 @@ impl RegexRule for GraphRule {
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 Graph Properties") token.source(),
.with_label( "Invalid Graph Properties".into(),
Label::new((token.source().clone(), props.range())) span(
.with_message(e) props.range(),
.with_color(state.parser.colors().error), e
) )
.finish(),
); );
return reports; return reports;
} }
@ -297,35 +292,30 @@ impl RegexRule for GraphRule {
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 Graph Property") token.source(),
.with_label( "Invalid Graph Property".into(),
Label::new((token.source().clone(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
"Property `layout: {}` cannot be converted: {}", format!(
"Property `{}` 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 Graph Property") token.source(),
.with_label( "Invalid Graph Property".into(),
Label::new(( span(
token.source().clone(), token.start() + 1..token.end(),
token.start() + 1..token.end(), err
)) )
.with_message(err)
.with_color(state.parser.colors().warning),
)
.finish(),
); );
return reports; return reports;
} }
@ -340,21 +330,17 @@ impl RegexRule for GraphRule {
Ok((_, kind)) => kind, Ok((_, kind)) => kind,
Err(e) => match e { Err(e) => match e {
PropertyMapError::NotFoundError(err) => { PropertyMapError::NotFoundError(err) => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid Graph Property") token.source(),
.with_label( "Invalid Graph Property".into(),
Label::new(( span(
token.source().clone(), token.start() + 1..token.end(),
token.start() + 1..token.end(), format!(
)) "Property `{}` is missing",
.with_message(format!( err.fg(state.parser.colors().info)
"Property `{}` is missing",
err.fg(state.parser.colors().info)
))
.with_color(state.parser.colors().warning),
) )
.finish(), )
); );
return reports; return reports;
} }

View file

@ -9,13 +9,12 @@ use crate::parser::source::Source;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
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 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::paragraph::Paragraph; use super::paragraph::Paragraph;
@ -57,65 +56,62 @@ impl RegexRule for ImportRule {
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 result = vec![]; let mut reports = vec![];
// Path // Path
let import_file = match matches.get(2) { let import_file = match matches.get(2) {
Some(name) => match ImportRule::validate_name(state.parser.colors(), name.as_str()) { Some(name) => match ImportRule::validate_name(state.parser.colors(), name.as_str()) {
Err(msg) => { Err(msg) => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid name for import") token.source(),
.with_label( "Invalid Import Name".into(),
Label::new((token.source(), name.range())) span(
.with_message(format!( name.range(),
"Import name `{}` is invalid. {msg}", format!(
name.as_str().fg(state.parser.colors().highlight) "Import name `{}` is invalid. {msg}",
)) name.as_str().fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return result; return reports;
} }
Ok(filename) => { Ok(filename) => {
let meta = match std::fs::metadata(filename.as_str()) { let meta = match std::fs::metadata(filename.as_str()) {
Err(_) => { Err(_) => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid import path") token.source(),
.with_label( "Invalid Import Path".into(),
Label::new((token.source(), name.range())) span(
.with_message(format!( name.range(),
"Unable to access file `{}`", format!(
filename.fg(state.parser.colors().highlight) "Unable to access file `{}`",
)) filename.fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return result; return reports;
} }
Ok(meta) => meta, Ok(meta) => meta,
}; };
if !meta.is_file() { if !meta.is_file() {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid import path") token.source(),
.with_label( "Invalid Import Path".into(),
Label::new((token.source(), name.range())) span(
.with_message(format!( name.range(),
"Path `{}` is not a file!", format!(
filename.fg(state.parser.colors().highlight) "Path `{}` is not a file!",
)) filename.fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return result; return reports;
} }
filename filename
@ -130,21 +126,20 @@ impl RegexRule for ImportRule {
{ {
Ok(as_name) => as_name, Ok(as_name) => as_name,
Err(msg) => { Err(msg) => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), as_name.start()) &mut reports,
.with_message("Invalid name for import as") token.source(),
.with_label( "Invalid Import As".into(),
Label::new((token.source(), as_name.range())) span(
.with_message(format!( as_name.range(),
"Canot import `{import_file}` as `{}`. {msg}", format!(
as_name.as_str().fg(state.parser.colors().highlight) "Canot import `{import_file}` as `{}`. {msg}",
)) as_name.as_str().fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return result; return reports;
} }
}, },
_ => "".to_string(), _ => "".to_string(),
@ -153,17 +148,16 @@ impl RegexRule for ImportRule {
let import = match SourceFile::new(import_file, Some(token.clone())) { let import = match SourceFile::new(import_file, Some(token.clone())) {
Ok(import) => Rc::new(import), Ok(import) => Rc::new(import),
Err(path) => { Err(path) => {
result.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Unable to read file content") token.source(),
.with_label( "Invalid Import File".into(),
Label::new((token.source(), token.range)) span(
.with_message(format!("Failed to read content from path `{path}`")) token.range.clone(),
.with_color(state.parser.colors().error), format!("Failed to read content from path `{path}`")
) )
.finish(),
); );
return result; return reports;
} }
}; };
@ -212,6 +206,6 @@ impl RegexRule for ImportRule {
sems.add(path, tokens.import_path); sems.add(path, tokens.import_path);
} }
result reports
} }
} }

View file

@ -341,10 +341,10 @@ impl LayoutRule {
token: &Token, token: &Token,
layout_type: Rc<dyn LayoutType>, layout_type: Rc<dyn LayoutType>,
properties: Option<Match>, properties: Option<Match>,
) -> Option<Box<dyn Any>> { ) -> Result<Option<Box<dyn Any>>, ()> {
match properties { match properties {
None => match layout_type.parse_properties("") { None => match layout_type.parse_properties("") {
Ok(props) => props, Ok(props) => Ok(props),
Err(err) => { Err(err) => {
report_err!( report_err!(
&mut reports, &mut reports,
@ -352,28 +352,28 @@ impl LayoutRule {
"Invalid Layout Properties".into(), "Invalid Layout Properties".into(),
span( span(
token.range.clone(), token.range.clone(),
format!("Layout is missing required property: {eee}") format!("Layout is missing required property: {err}")
) )
); );
None Err(())
} }
}, },
Some(props) => { Some(props) => {
let trimmed = props.as_str().trim_start().trim_end(); let content = escape_text('\\', "]", props.as_str());
let content = escape_text('\\', "]", trimmed);
match layout_type.parse_properties(content.as_str()) { match layout_type.parse_properties(content.as_str()) {
Ok(props) => Ok(props), Ok(props) => Ok(props),
Err(err) => { Err(err) => {
Err(
Report::build(ReportKind::Error, token.source(), props.start()) report_err!(
.with_message("Unable to parse layout properties") &mut reports,
.with_label( token.source(),
Label::new((token.source(), props.range())) "Invalid Layout Properties".into(),
.with_message(err) span(
.with_color(colors.error), props.range(),
) err
.finish(), )
) );
Err(())
} }
} }
} }
@ -399,7 +399,7 @@ impl RegexRule for LayoutRule {
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 rule_state = LayoutRule::initialize_state(state); let rule_state = LayoutRule::initialize_state(state);
@ -409,18 +409,17 @@ impl RegexRule for LayoutRule {
{ {
match matches.get(2) { match matches.get(2) {
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Missing Layout Name") token.source(),
.with_label( "Missing Layout Name".into(),
Label::new((token.source(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
"Missing layout name after `{}`", format!(
"#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) "Missing layout name after `{}`",
)) "#+BEGIN_LAYOUT".fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -429,35 +428,33 @@ impl RegexRule for LayoutRule {
if name.as_str().is_empty() || trimmed.is_empty() if name.as_str().is_empty() || trimmed.is_empty()
// Empty name // Empty name
{ {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Empty Layout Name") token.source(),
.with_label( "Empty Layout Name".into(),
Label::new((token.source(), token.range.clone())) span(
.with_message(format!( name.range(),
"Empty layout name after `{}`", format!(
"#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) "Empty layout name after `{}`",
)) "#+BEGIN_LAYOUT".fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} else if !name.as_str().chars().next().unwrap().is_whitespace() } else if !name.as_str().chars().next().unwrap().is_whitespace()
// Missing space // Missing space
{ {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Invalid Layout Name") token.source(),
.with_label( "Invalid Layout Name".into(),
Label::new((token.source(), name.range())) span(
.with_message(format!( name.range(),
"Missing a space before layout name `{}`", format!(
name.as_str().fg(state.parser.colors().highlight) "Missing a space before layout name `{}`",
)) name.as_str().fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -465,18 +462,18 @@ impl RegexRule for LayoutRule {
// Get layout // Get layout
let layout_type = match state.shared.layouts.borrow().get(trimmed) { let layout_type = match state.shared.layouts.borrow().get(trimmed) {
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), name.start()) &mut reports,
.with_message("Unknown Layout") token.source(),
.with_label( "Unknown Layout".into(),
Label::new((token.source(), name.range())) span(
.with_message(format!( name.range(),
"Cannot find layout `{}`", format!(
trimmed.fg(state.parser.colors().highlight) "Cannot find layout `{}`",
)) trimmed.fg(state.parser.colors().highlight)
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
@ -485,16 +482,13 @@ impl RegexRule for LayoutRule {
// Parse properties // Parse properties
let properties = match LayoutRule::parse_properties( let properties = match LayoutRule::parse_properties(
state.parser.colors(), &mut reports,
&token, &token,
layout_type.clone(), layout_type.clone(),
matches.get(1), matches.get(1),
) { ) {
Ok(props) => props, Ok(props) => props,
Err(rep) => { Err(()) => return reports,
reports.push(rep);
return reports;
}
}; };
state.push( state.push(
@ -548,15 +542,14 @@ impl RegexRule for LayoutRule {
let (tokens, layout_type) = match layout_state.stack.last_mut() { let (tokens, layout_type) = match layout_state.stack.last_mut() {
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid #+LAYOUT_NEXT") token.source(),
.with_label( "Invalid #+LAYOUT_NEXT".into(),
Label::new((token.source(), token.range.clone())) span(
.with_message("No active layout found".to_string()) token.range.clone(),
.with_color(state.parser.colors().error), "No active layout found".into()
) )
.finish(),
); );
return reports; return reports;
} }
@ -566,35 +559,31 @@ impl RegexRule for LayoutRule {
if layout_type.expects().end < tokens.len() if layout_type.expects().end < tokens.len()
// Too many blocks // Too many blocks
{ {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Unexpected #+LAYOUT_NEXT") token.source(),
.with_label( "Unexpected #+LAYOUT_NEXT".into(),
Label::new((token.source(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
"Layout expects a maximum of {} blocks, currently at {}", format!(
layout_type.expects().end.fg(state.parser.colors().info), "Layout expects a maximum of {} blocks, currently at {}",
tokens.len().fg(state.parser.colors().info), layout_type.expects().end.fg(state.parser.colors().info),
)) tokens.len().fg(state.parser.colors().info),
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
// Parse properties // Parse properties
let properties = match LayoutRule::parse_properties( let properties = match LayoutRule::parse_properties(
state.parser.colors(), &mut reports,
&token, &token,
layout_type.clone(), layout_type.clone(),
matches.get(1), matches.get(1),
) { ) {
Ok(props) => props, Ok(props) => props,
Err(rep) => { Err(rep) => return reports,
reports.push(rep);
return reports;
}
}; };
if let Some((sems, tokens)) = if let Some((sems, tokens)) =
@ -630,15 +619,14 @@ impl RegexRule for LayoutRule {
let (tokens, layout_type) = match layout_state.stack.last_mut() { let (tokens, layout_type) = match layout_state.stack.last_mut() {
None => { None => {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Invalid #+LAYOUT_END") token.source(),
.with_label( "Invalid #+LAYOUT_NEXT".into(),
Label::new((token.source(), token.range.clone())) span(
.with_message("No active layout found".to_string()) token.range.clone(),
.with_color(state.parser.colors().error), "No active layout found".into()
) )
.finish(),
); );
return reports; return reports;
} }
@ -648,35 +636,31 @@ impl RegexRule for LayoutRule {
if layout_type.expects().start > tokens.len() if layout_type.expects().start > tokens.len()
// Not enough blocks // Not enough blocks
{ {
reports.push( report_err!(
Report::build(ReportKind::Error, token.source(), token.start()) &mut reports,
.with_message("Unexpected #+LAYOUT_END") token.source(),
.with_label( "Unexpected #+LAYOUT_NEXT".into(),
Label::new((token.source(), token.range.clone())) span(
.with_message(format!( token.range.clone(),
"Layout expects a minimum of {} blocks, currently at {}", format!(
layout_type.expects().start.fg(state.parser.colors().info), "Layout expects a minimum of {} blocks, currently at {}",
tokens.len().fg(state.parser.colors().info), layout_type.expects().start.fg(state.parser.colors().info),
)) tokens.len().fg(state.parser.colors().info),
.with_color(state.parser.colors().error),
) )
.finish(), )
); );
return reports; return reports;
} }
// Parse properties // Parse properties
let properties = match LayoutRule::parse_properties( let properties = match LayoutRule::parse_properties(
state.parser.colors(), &mut reports,
&token, &token,
layout_type.clone(), layout_type.clone(),
matches.get(1), matches.get(1),
) { ) {
Ok(props) => props, Ok(props) => props,
Err(rep) => { Err(rep) => return reports,
reports.push(rep);
return reports;
}
}; };
let layout_type = layout_type.clone(); let layout_type = layout_type.clone();

View file

@ -1,7 +1,6 @@
use std::any::Any; use std::any::Any;
use std::cell::Ref; use std::cell::Ref;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use crate::compiler::compiler::Compiler; use crate::compiler::compiler::Compiler;
@ -429,6 +428,7 @@ impl Rule for ListRule {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::source::Source;
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;

View file

@ -1,8 +1,5 @@
use std::any::Any; use std::any::Any;
use std::ops::Range;
use std::rc::Rc;
use ariadne::Report;
use regex::Regex; use regex::Regex;
use crate::compiler::compiler::Compiler; use crate::compiler::compiler::Compiler;
@ -17,6 +14,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::*;
// TODO: Full refactor // TODO: Full refactor
// Problem is that document parsed from other sources i.e by variables // Problem is that document parsed from other sources i.e by variables
@ -132,7 +130,7 @@ impl Rule for ParagraphRule {
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>) {
let end_cursor = match self.re.captures_at(cursor.source.content(), cursor.pos) { let end_cursor = match self.re.captures_at(cursor.source.content(), cursor.pos) {
None => panic!("Unknown error"), None => panic!("Unknown error"),
Some(capture) => cursor.at(capture.get(0).unwrap().end() - 1), Some(capture) => cursor.at(capture.get(0).unwrap().end() - 1),
@ -152,6 +150,8 @@ impl Rule for ParagraphRule {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use std::rc::Rc;
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
@ -159,7 +159,6 @@ mod tests {
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
use super::*;
#[test] #[test]
fn parse() { fn parse() {

View file

@ -272,6 +272,8 @@ impl RegexRule for RawRule {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::source::Source;
use std::rc::Rc;
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;

View file

@ -15,7 +15,6 @@ use ariadne::Fmt;
use mlua::Lua; use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use crate::parser::reports::*; use crate::parser::reports::*;
use crate::parser::reports::macros::*; use crate::parser::reports::macros::*;
@ -140,7 +139,7 @@ impl RegexRule for ScriptRule {
"Invalid Kernel Code".into(), "Invalid Kernel Code".into(),
span( span(
script_range, script_range,
"Kernel code is empty".into(), "Kernel code is empty".into()
) )
); );
return reports; return reports;

View file

@ -353,7 +353,7 @@ impl RegexRule for VariableSubstitutionRule {
"Empty Variable Name".into(), "Empty Variable Name".into(),
span( span(
name.range(), 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(), "Invalid Variable Name".into(),
span( span(
name.range(), name.range(),
"Variable names contains leading spaces".into(), "Variable names contains leading spaces".into()
), ),
help("Remove leading spaces".into()) help("Remove leading spaces".into())
); );
@ -382,7 +382,7 @@ impl RegexRule for VariableSubstitutionRule {
"Invalid Variable Name".into(), "Invalid Variable Name".into(),
span( span(
name.range(), name.range(),
"Variable names contains trailing spaces".into(), "Variable names contains trailing spaces".into()
), ),
help("Remove trailing spaces".into()) help("Remove trailing spaces".into())
); );

View file

@ -41,7 +41,7 @@ fn main() -> ExitCode {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let program = args[0].clone(); let program = args[0].clone();
slet mut opts = Options::new(); let mut opts = Options::new();
opts.optopt("i", "input", "Input path", "PATH"); opts.optopt("i", "input", "Input path", "PATH");
opts.optopt("o", "output", "Output path", "PATH"); opts.optopt("o", "output", "Output path", "PATH");
opts.optopt("d", "database", "Cache database location", "PATH"); opts.optopt("d", "database", "Cache database location", "PATH");

View file

@ -434,7 +434,8 @@ 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>) { fn handle_reports(&self, reports: Vec<Report>) {
todo!(); // TODO Report::reports_to_stdout(self.colors(), reports);
//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();

View file

@ -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)] #[derive(Debug)]
pub enum ReportKind pub enum ReportKind {
{
Error, Error,
Warning, Warning,
} }
impl Into<ariadne::ReportKind<'static>> for &ReportKind impl Into<ariadne::ReportKind<'static>> for &ReportKind {
{ fn into(self) -> ariadne::ReportKind<'static> {
fn into(self) -> ariadne::ReportKind<'static> { match self {
match self
{
ReportKind::Error => ariadne::ReportKind::Error, ReportKind::Error => ariadne::ReportKind::Error,
ReportKind::Warning => ariadne::ReportKind::Warning, ReportKind::Warning => ariadne::ReportKind::Warning,
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ReportSpan pub struct ReportSpan {
{
pub token: Token, pub token: Token,
pub message: String pub message: String,
} }
#[derive(Debug)] #[derive(Debug)]
pub 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,
@ -38,65 +38,72 @@ pub struct Report
pub spans: Vec<ReportSpan>, pub spans: Vec<ReportSpan>,
} }
impl Report impl Report {
{ fn ariadne_color(kind: &ReportKind, colors: &ReportColors) -> ariadne::Color {
fn ariadne_format(fmt: &str, parser: &dyn Parser) -> String match kind {
{ ReportKind::Error => colors.error,
// TODO: Colors ReportKind::Warning => colors.warning,
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,
} }
} }
pub fn to_ariadne(self, parser: &dyn Parser) -> ariadne::Report<'static, (Rc<dyn Source>, Range<usize>)> pub fn to_ariadne(
{ self,
colors: &ReportColors,
) -> (
ariadne::Report<'static, (Rc<dyn Source>, Range<usize>)>,
impl ariadne::Cache<Rc<dyn Source>>,
) {
let mut cache = HashMap::new();
let source = self.source.original_position(0).0; let source = self.source.original_position(0).0;
let mut start = usize::MAX; 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()); let (osource, opos) = span.token.source().original_position(span.token.start());
if &osource == &source && opos < start if &osource == &source && opos < start {
{
start = opos; start = opos;
} }
} }
if start == usize::MAX if start == usize::MAX {
{
start = 0; start = 0;
} }
cache.insert(source.clone(), source.content().clone());
let mut builder = ariadne::Report::build((&self.kind).into(), self.source, start) 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( builder = builder.with_label(
ariadne::Label::new((span.token.source(), span.token.range)) ariadne::Label::new(span.token.source().original_range(span.token.range))
.with_message(Self::ariadne_format(span.message.as_str(), parser)) .with_message(span.message)
.with_color(Self::ariadne_color(&self.kind, parser)) .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<Report>) {
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::*; pub use super::*;
#[macro_export] #[macro_export]
macro_rules! report_label { macro_rules! report_label {
($r:expr,) => {{ }}; ($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 { $r.spans.push(ReportSpan {
token: crate::parser::source::Token::Token::new($range, $source), token: crate::parser::source::Token::new($range, $source),
message: $message, message: $message,
}); });
report_label!($r, $($($tail)*)?); report_label!($r, $($($tail)*)?);
@ -152,34 +159,3 @@ pub mod macros
pub use crate::*; 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:#?}");
}
}

View file

@ -14,7 +14,6 @@ use parser::parser::ParseMode;
use parser::parser::Parser; use parser::parser::Parser;
use parser::parser::ParserState; use parser::parser::ParserState;
use parser::source::SourceFile; use parser::source::SourceFile;
use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use tower_lsp::Client; use tower_lsp::Client;
use tower_lsp::LanguageServer; use tower_lsp::LanguageServer;
@ -65,7 +64,7 @@ impl Backend {
#[tower_lsp::async_trait] #[tower_lsp::async_trait]
impl LanguageServer for Backend { impl LanguageServer for Backend {
async fn initialize(&self, _params: InitializeParams) -> Result<InitializeResult> { async fn initialize(&self, _params: InitializeParams) -> tower_lsp::jsonrpc::Result<InitializeResult> {
Ok(InitializeResult { Ok(InitializeResult {
capabilities: ServerCapabilities { capabilities: ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind( text_document_sync: Some(TextDocumentSyncCapability::Kind(
@ -118,7 +117,7 @@ impl LanguageServer for Backend {
.await; .await;
} }
async fn shutdown(&self) -> Result<()> { Ok(()) } async fn shutdown(&self) -> tower_lsp::jsonrpc::Result<()> { Ok(()) }
async fn did_open(&self, params: DidOpenTextDocumentParams) { async fn did_open(&self, params: DidOpenTextDocumentParams) {
self.client self.client
@ -139,7 +138,7 @@ impl LanguageServer for Backend {
.await .await
} }
async fn completion(&self, _params: CompletionParams) -> Result<Option<CompletionResponse>> { async fn completion(&self, _params: CompletionParams) -> tower_lsp::jsonrpc::Result<Option<CompletionResponse>> {
//let uri = params.text_document_position.text_document.uri; //let uri = params.text_document_position.text_document.uri;
//let position = params.text_document_position.position; //let position = params.text_document_position.position;
let completions = || -> Option<Vec<CompletionItem>> { let completions = || -> Option<Vec<CompletionItem>> {
@ -153,7 +152,7 @@ impl LanguageServer for Backend {
async fn semantic_tokens_full( async fn semantic_tokens_full(
&self, &self,
params: SemanticTokensParams, params: SemanticTokensParams,
) -> Result<Option<SemanticTokensResult>> { ) -> tower_lsp::jsonrpc::Result<Option<SemanticTokensResult>> {
let uri = params.text_document.uri.to_string(); let uri = params.text_document.uri.to_string();
self.client self.client
.log_message(MessageType::LOG, "semantic_token_full") .log_message(MessageType::LOG, "semantic_token_full")