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<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
+	) -> Vec<Report> {
 		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<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
+	) -> Vec<Report> {
 		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<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
+	) -> Vec<Report> {
 		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<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
-		let mut result = vec![];
+	) -> Vec<Report> {
+		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<dyn LayoutType>,
 		properties: Option<Match>,
-	) -> Option<Box<dyn Any>> {
+	) -> Result<Option<Box<dyn Any>>, ()> {
 		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<Report<(Rc<dyn Source>, Range<usize>)>> {
+	) -> Vec<Report> {
 		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<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) {
 			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<String> = 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<Report>) {
-		todo!(); // TODO
+		Report::reports_to_stdout(self.colors(), reports);
+		//todo!(); // TODO
 		/*
 		for mut report in reports {
 			let mut sources: HashSet<Rc<dyn Source>> = 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<ariadne::ReportKind<'static>> for &ReportKind
-{
-    fn into(self) -> ariadne::ReportKind<'static> {
-		match self
-		{
+impl Into<ariadne::ReportKind<'static>> 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<dyn Source>,
 	pub message: String,
@@ -38,65 +38,72 @@ pub struct Report
 	pub spans: Vec<ReportSpan>,
 }
 
-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<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 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<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::*;
-#[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<InitializeResult> {
+	async fn initialize(&self, _params: InitializeParams) -> tower_lsp::jsonrpc::Result<InitializeResult> {
 		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<Option<CompletionResponse>> {
+	async fn completion(&self, _params: CompletionParams) -> tower_lsp::jsonrpc::Result<Option<CompletionResponse>> {
 		//let uri = params.text_document_position.text_document.uri;
 		//let position = params.text_document_position.position;
 		let completions = || -> Option<Vec<CompletionItem>> {
@@ -153,7 +152,7 @@ impl LanguageServer for Backend {
 	async fn semantic_tokens_full(
 		&self,
 		params: SemanticTokensParams,
-	) -> Result<Option<SemanticTokensResult>> {
+	) -> tower_lsp::jsonrpc::Result<Option<SemanticTokensResult>> {
 		let uri = params.text_document.uri.to_string();
 		self.client
 			.log_message(MessageType::LOG, "semantic_token_full")