mirror of
https://github.com/Dummi26/mers.git
synced 2026-03-13 03:52:27 +01:00
add support for custom theming in mers errors
this also includes support for the NoTheme, a theme which doesn't add any color to mers' output. If you compile mers with --no-default-features, the `colored` dependency will disappear and mers_lib will fall back to NoTheme.
This commit is contained in:
@@ -4,7 +4,7 @@ use line_span::{LineSpan, LineSpanExt};
|
||||
|
||||
use crate::{
|
||||
data::Type,
|
||||
errors::{error_colors, CheckError, SourcePos},
|
||||
errors::{CheckError, EColor, SourcePos},
|
||||
program::{
|
||||
self,
|
||||
parsed::{block::Block, CompInfo},
|
||||
@@ -53,7 +53,7 @@ pub fn check_mut(
|
||||
for (try_stmt, used) in info.global.unused_try_statements.lock().unwrap().iter() {
|
||||
if used.iter().any(|v| v.is_some()) {
|
||||
let err = err.get_or_insert_with(|| {
|
||||
CheckError::new().msg(format!(
|
||||
CheckError::from(format!(
|
||||
"There are `.try` statements with unused functions!"
|
||||
))
|
||||
});
|
||||
@@ -62,7 +62,7 @@ pub fn check_mut(
|
||||
.enumerate()
|
||||
.filter_map(|v| Some((v.0, v.1.clone()?)))
|
||||
.collect::<Vec<_>>();
|
||||
err.msg_mut(format!(
|
||||
err.msg_mut_str(format!(
|
||||
"Here, {}function{} {} {} unused:",
|
||||
if unused.len() == 1 { "the " } else { "" },
|
||||
if unused.len() == 1 { "" } else { "s" },
|
||||
@@ -87,9 +87,9 @@ pub fn check_mut(
|
||||
src.push((
|
||||
src_range,
|
||||
Some(if i % 2 == 0 {
|
||||
error_colors::TryUnusedFunction1
|
||||
EColor::TryUnusedFunction1
|
||||
} else {
|
||||
error_colors::TryUnusedFunction2
|
||||
EColor::TryUnusedFunction2
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{path::PathBuf, sync::Arc};
|
||||
use super::{Source, SourceFrom, SourcePos};
|
||||
use crate::{
|
||||
data::Data,
|
||||
errors::{error_colors, CheckError},
|
||||
errors::{CheckError, EColor},
|
||||
program::{
|
||||
self,
|
||||
parsed::{as_type::AsType, MersStatement},
|
||||
@@ -30,9 +30,9 @@ pub fn parse(
|
||||
let name = name.trim().to_owned();
|
||||
src.skip_whitespace();
|
||||
if !matches!(src.next_char(), Some(']')) {
|
||||
return Err(
|
||||
CheckError::new().msg(format!("Expected ']' after type name in [[type_name]]"))
|
||||
);
|
||||
return Err(CheckError::from(format!(
|
||||
"Expected ']' after type name in [[type_name]]"
|
||||
)));
|
||||
}
|
||||
src.skip_whitespace();
|
||||
if src.peek_word_allow_colon() == ":=" {
|
||||
@@ -43,12 +43,12 @@ pub fn parse(
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `[[...] := ...]` type definition")))
|
||||
.msg_str(format!("EOF after `[[...] := ...]` type definition")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
if !matches!(src.next_char(), Some(']')) {
|
||||
return Err(CheckError::new().msg(format!(
|
||||
return Err(CheckError::new().msg_str(format!(
|
||||
"Expected ']' after statement in [[type_name] := statement]"
|
||||
)));
|
||||
}
|
||||
@@ -63,7 +63,7 @@ pub fn parse(
|
||||
let as_type = super::types::parse_type(src, srca)?;
|
||||
src.skip_whitespace();
|
||||
if !matches!(src.next_char(), Some(']')) {
|
||||
return Err(CheckError::new().msg(format!(
|
||||
return Err(CheckError::new().msg_str(format!(
|
||||
"Expected ']' after type definition in [[type_name] type_definition]"
|
||||
)));
|
||||
}
|
||||
@@ -84,16 +84,16 @@ pub fn parse(
|
||||
return Err(CheckError::new()
|
||||
.src(vec![(
|
||||
(pos_in_src, src.get_pos(), srca).into(),
|
||||
Some(error_colors::TypeAnnotationNoClosingBracket),
|
||||
Some(EColor::TypeAnnotationNoClosingBracket),
|
||||
)])
|
||||
.msg(format!("Missing closing bracket ']' after type annotation")));
|
||||
.msg_str(format!("Missing closing bracket ']' after type annotation")));
|
||||
}
|
||||
let statement = match parse(src, srca) {
|
||||
Ok(Some(v)) => v,
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `[...]` type annotation")))
|
||||
.msg_str(format!("EOF after `[...]` type annotation")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
@@ -120,7 +120,7 @@ pub fn parse(
|
||||
let source = parse(src, srca)?.ok_or_else(|| {
|
||||
CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `:=`"))
|
||||
.msg_str(format!("EOF after `:=`"))
|
||||
})?;
|
||||
first = Box::new(program::parsed::init_to::InitTo {
|
||||
pos_in_src: (first.source_range().start(), src.get_pos(), srca).into(),
|
||||
@@ -137,7 +137,7 @@ pub fn parse(
|
||||
(first.source_range().start(), src.get_pos(), srca).into(),
|
||||
None,
|
||||
)])
|
||||
.msg(format!("EOF after `=`"))
|
||||
.msg_str(format!("EOF after `=`"))
|
||||
})?;
|
||||
first = Box::new(program::parsed::assign_to::AssignTo {
|
||||
pos_in_src: (pos_in_src, src.get_pos(), srca).into(),
|
||||
@@ -153,7 +153,7 @@ pub fn parse(
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `->`")))
|
||||
.msg_str(format!("EOF after `->`")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
@@ -182,10 +182,10 @@ pub fn parse(
|
||||
pos_after_first = src.get_pos();
|
||||
} else {
|
||||
return Err(CheckError::new()
|
||||
.msg(format!("Expected `(` after `.try`"))
|
||||
.msg_str(format!("Expected `(` after `.try`"))
|
||||
.src(vec![(
|
||||
(dot_in_src, src.get_pos(), srca).into(),
|
||||
Some(error_colors::TryBadSyntax),
|
||||
Some(EColor::TryBadSyntax),
|
||||
)]));
|
||||
}
|
||||
} else {
|
||||
@@ -194,7 +194,7 @@ pub fn parse(
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((dot_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `.`")))
|
||||
.msg_str(format!("EOF after `.`")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
@@ -265,16 +265,16 @@ pub fn parse_no_chain(
|
||||
if src.peek_char().is_none() {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after #")));
|
||||
.msg_str(format!("EOF after #")));
|
||||
}
|
||||
if src.peek_char().is_some_and(|ch| ch.is_whitespace()) {
|
||||
src.skip_whitespace();
|
||||
return Err(CheckError::new()
|
||||
.src(vec![(
|
||||
(pos_in_src, src.get_pos(), srca).into(),
|
||||
Some(error_colors::WhitespaceAfterHashtag),
|
||||
Some(EColor::WhitespaceAfterHashtag),
|
||||
)])
|
||||
.msg(format!("Whitespace after #")));
|
||||
.msg_str(format!("Whitespace after #")));
|
||||
}
|
||||
match src.next_word() {
|
||||
"include" => {
|
||||
@@ -313,19 +313,19 @@ pub fn parse_no_chain(
|
||||
((pos_in_src, end_in_src, srca).into(), None),
|
||||
(
|
||||
(string_in_src, src.get_pos(), srca).into(),
|
||||
Some(error_colors::HashIncludeCantLoadFile),
|
||||
Some(EColor::HashIncludeCantLoadFile),
|
||||
),
|
||||
])
|
||||
.msg(format!("Can't load file '{file_path_str}': {e}")));
|
||||
.msg_str(format!("Can't load file '{file_path_str}': {e}")));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![
|
||||
((pos_in_src, end_in_src, srca).into(), None),
|
||||
((string_in_src, src.get_pos(), srca).into(), Some(error_colors::HashIncludeNotAString)),
|
||||
((string_in_src, src.get_pos(), srca).into(), Some(EColor::HashIncludeNotAString)),
|
||||
])
|
||||
.msg(format!(
|
||||
.msg_str(format!(
|
||||
"#include must be followed by a string literal like \"file.mers\" (\" expected)."
|
||||
)));
|
||||
}
|
||||
@@ -335,9 +335,9 @@ pub fn parse_no_chain(
|
||||
return Err(CheckError::new()
|
||||
.src(vec![(
|
||||
(pos_in_src, src.get_pos(), srca).into(),
|
||||
Some(error_colors::HashUnknown),
|
||||
Some(EColor::HashUnknown),
|
||||
)])
|
||||
.msg(msg));
|
||||
.msg_str(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +371,7 @@ pub fn parse_no_chain(
|
||||
(pos_in_src, src.get_pos(), srca).into(),
|
||||
None,
|
||||
)])
|
||||
.msg(format!("EOF after `:` in object")))
|
||||
.msg_str(format!("EOF after `:` in object")))
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(CheckError::new()
|
||||
@@ -379,7 +379,9 @@ pub fn parse_no_chain(
|
||||
(pos_in_src, src.get_pos(), srca).into(),
|
||||
None,
|
||||
)])
|
||||
.msg(format!("Error in statement after `:` in object"))
|
||||
.msg_str(format!(
|
||||
"Error in statement after `:` in object"
|
||||
))
|
||||
.err(e))
|
||||
}
|
||||
},
|
||||
@@ -430,7 +432,7 @@ pub fn parse_no_chain(
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF in `if`")))
|
||||
.msg_str(format!("EOF in `if`")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
@@ -439,7 +441,7 @@ pub fn parse_no_chain(
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `if <condition>`")))
|
||||
.msg_str(format!("EOF after `if <condition>`")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
@@ -453,7 +455,7 @@ pub fn parse_no_chain(
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `else`")))
|
||||
.msg_str(format!("EOF after `else`")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
})
|
||||
@@ -476,7 +478,7 @@ pub fn parse_no_chain(
|
||||
Ok(None) => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("EOF after `loop`")))
|
||||
.msg_str(format!("EOF after `loop`")))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
@@ -578,17 +580,17 @@ pub fn parse_string_custom_end(
|
||||
return Err(CheckError::new()
|
||||
.src(vec![(
|
||||
(backslash_in_src, src.get_pos(), srca).into(),
|
||||
Some(error_colors::BackslashEscapeUnknown),
|
||||
Some(EColor::BackslashEscapeUnknown),
|
||||
)])
|
||||
.msg(format!("unknown backslash escape '\\{o}'")));
|
||||
.msg_str(format!("unknown backslash escape '\\{o}'")));
|
||||
}
|
||||
None => {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![(
|
||||
(backslash_in_src, src.get_pos(), srca).into(),
|
||||
Some(error_colors::BackslashEscapeEOF),
|
||||
Some(EColor::BackslashEscapeEOF),
|
||||
)])
|
||||
.msg(format!("EOF in backslash escape")));
|
||||
.msg_str(format!("EOF in backslash escape")));
|
||||
}
|
||||
});
|
||||
} else if ch == closing_char {
|
||||
@@ -600,9 +602,9 @@ pub fn parse_string_custom_end(
|
||||
return Err(CheckError::new()
|
||||
.src(vec![(
|
||||
(opening, src.get_pos(), srca).into(),
|
||||
Some(error_colors::StringEOF),
|
||||
Some(EColor::StringEOF),
|
||||
)])
|
||||
.msg(format!(
|
||||
.msg_str(format!(
|
||||
"EOF in string literal{}",
|
||||
if closing_char != '"' {
|
||||
format!(
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
data::{self, Type},
|
||||
errors::{error_colors, CheckError},
|
||||
errors::{CheckError, EColor},
|
||||
};
|
||||
|
||||
use super::Source;
|
||||
@@ -35,7 +35,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
||||
} else {
|
||||
format!("EOF")
|
||||
};
|
||||
return Err(CheckError::new().msg(format!(
|
||||
return Err(CheckError::new().msg_str(format!(
|
||||
"No closing ] in reference type with opening [! Found {nc} instead"
|
||||
)));
|
||||
}
|
||||
@@ -75,10 +75,10 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
||||
((pos_in_src, src.get_pos(), srca).into(), None),
|
||||
(
|
||||
(pos1, src.get_pos(), srca).into(),
|
||||
Some(error_colors::BadCharInFunctionType),
|
||||
Some(EColor::BadCharInFunctionType),
|
||||
),
|
||||
]).msg(format!("Unexpected character in function type, expected arrow `->` but found `,`."))
|
||||
.msg(format!("If you wanted this to be a tuple type instead, you may have used `Input -> Output` instead of `(Input -> Output)` for a function type somewhere.")));
|
||||
]).msg_str(format!("Unexpected character in function type, expected arrow `->` but found `,`."))
|
||||
.msg_str(format!("If you wanted this to be a tuple type instead, you may have used `Input -> Output` instead of `(Input -> Output)` for a function type somewhere.")));
|
||||
}
|
||||
}
|
||||
Some('-') if src.peek_word() == "->" => {
|
||||
@@ -92,8 +92,8 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
||||
Some(')') => break,
|
||||
_ => return Err(CheckError::new().src(vec![
|
||||
((pos_in_src, src.get_pos(), srca).into(), None),
|
||||
((pos2, src.get_pos(), srca).into(), Some(error_colors::BadCharInFunctionType)),
|
||||
]).msg(format!("Expected comma `,` after `In -> Out` part of function type")))
|
||||
((pos2, src.get_pos(), srca).into(), Some(EColor::BadCharInFunctionType)),
|
||||
]).msg_str(format!("Expected comma `,` after `In -> Out` part of function type")))
|
||||
}
|
||||
} else {
|
||||
let pos1 = src.get_pos();
|
||||
@@ -102,10 +102,10 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
||||
((pos_in_src, src.get_pos(), srca).into(), None),
|
||||
(
|
||||
(pos1, src.get_pos(), srca).into(),
|
||||
Some(error_colors::BadCharInTupleType),
|
||||
Some(EColor::BadCharInTupleType),
|
||||
),
|
||||
]).msg(format!("Unexpected character in tuple type, expected comma `,` but found arrow `->`."))
|
||||
.msg(format!("If you wanted to write a function type, use `(Input -> Output)` instead of `Input -> Output`.")));
|
||||
]).msg_str(format!("Unexpected character in tuple type, expected comma `,` but found arrow `->`."))
|
||||
.msg_str(format!("If you wanted to write a function type, use `(Input -> Output)` instead of `Input -> Output`.")));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -116,10 +116,10 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
||||
((pos_in_src, src.get_pos(), srca).into(), None),
|
||||
(
|
||||
(ppos, src.get_pos(), srca).into(),
|
||||
Some(error_colors::BadCharInTupleType),
|
||||
Some(EColor::BadCharInTupleType),
|
||||
),
|
||||
])
|
||||
.msg(format!(
|
||||
.msg_str(format!(
|
||||
"Unexpected character in tuple type, expected comma ',' or ')'."
|
||||
)));
|
||||
}
|
||||
@@ -150,7 +150,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
||||
if src.next_char() != Some(':') {
|
||||
return Err(CheckError::new()
|
||||
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
|
||||
.msg(format!("Expected colon ':' in object type")));
|
||||
.msg_str(format!("Expected colon ':' in object type")));
|
||||
}
|
||||
src.skip_whitespace();
|
||||
inner.push((field, parse_type(src, srca)?));
|
||||
@@ -236,11 +236,11 @@ pub fn type_from_parsed(
|
||||
{
|
||||
Some(Ok(t)) => as_type.add_all(&*t),
|
||||
Some(Err(_)) => {
|
||||
return Err(CheckError::new().msg(format!(
|
||||
return Err(CheckError::new().msg_str(format!(
|
||||
"Type: specified type without info, but type needs additional info"
|
||||
)))
|
||||
}
|
||||
None => return Err(CheckError::new().msg(format!("Unknown type '{name}'"))),
|
||||
None => return Err(CheckError::new().msg_str(format!("Unknown type '{name}'"))),
|
||||
},
|
||||
ParsedType::TypeWithInfo(name, additional_info) => match info
|
||||
.scopes
|
||||
@@ -248,12 +248,12 @@ pub fn type_from_parsed(
|
||||
.find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v))
|
||||
{
|
||||
Some(Ok(t)) => {
|
||||
return Err(CheckError::new().msg(format!(
|
||||
return Err(CheckError::new().msg_str(format!(
|
||||
"Type: specified type with info, but type {t} doesn't need it"
|
||||
)))
|
||||
}
|
||||
Some(Err(f)) => as_type.add_all(&*f(&additional_info, info)?),
|
||||
None => return Err(CheckError::new().msg(format!("Unknown type '{name}'"))),
|
||||
None => return Err(CheckError::new().msg_str(format!("Unknown type '{name}'"))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user