improve and move theming traits

move pretty_print.rs from mers to mers_lib
This commit is contained in:
Mark
2024-06-26 12:54:04 +02:00
parent b3d6b227b5
commit 7a945e80ba
13 changed files with 488 additions and 260 deletions

View File

@@ -8,6 +8,9 @@ use line_span::LineSpans;
#[cfg(feature = "parse")]
use crate::parsing::Source;
use crate::theme::ThemeGen;
pub mod themes;
#[derive(Clone, Copy, Debug)]
pub struct SourcePos(pub(crate) usize);
@@ -62,6 +65,9 @@ pub enum EColor {
BackslashEscapeUnknown,
BackslashEscapeEOF,
StringEOF,
TypeEOF,
/// `&[Int/String` (notice the missing `]`)
BracketedRefTypeNoClosingBracket,
IfConditionNotBool,
ChainWithNonFunction,
Function,
@@ -92,115 +98,24 @@ pub enum EColor {
InCodePositionLine,
}
pub trait Theme<T> {
fn color(&self, text: &str, color: EColor, t: &mut T);
}
pub trait ThemeTo<T>: Theme<T> {
fn color_to(&self, text: &str, color: EColor) -> T;
}
impl<T: Theme<String> + ?Sized> ThemeTo<String> for T {
fn color_to(&self, text: &str, color: EColor) -> String {
let mut t = String::new();
self.color(text, color, &mut t);
t
}
}
pub trait ETheme: ThemeGen<C = EColor, T = String> {}
impl<T: ThemeGen<C = EColor, T = String>> ETheme for T {}
pub fn colorize_str(
message: &Vec<(String, Option<EColor>)>,
theme: &(impl Theme<String> + ?Sized),
theme: &(impl ETheme + ?Sized),
) -> String {
let mut t = String::new();
colorize_gen(message, &mut t, |t, a| a.push_str(t), theme);
t
}
pub fn colorize_gen<T>(
message: &Vec<(String, Option<EColor>)>,
t: &mut T,
direct: impl Fn(&str, &mut T),
theme: &(impl Theme<T> + ?Sized),
) {
for (text, color) in message {
if let Some(color) = *color {
theme.color(text, color, t)
theme.color(text, color, &mut t)
} else {
direct(text, t)
theme.nocolor(text, &mut t)
}
}
t
}
pub struct NoTheme;
impl Theme<String> for NoTheme {
fn color(&self, text: &str, _color: EColor, t: &mut String) {
t.push_str(text);
}
}
#[cfg(feature = "ecolor-term")]
pub struct TermDefaultTheme;
#[cfg(feature = "ecolor-term")]
impl Theme<String> for TermDefaultTheme {
fn color(&self, text: &str, color: EColor, t: &mut String) {
use colored::{Color, Colorize};
t.push_str(
&text
.color(match color {
EColor::Indent(n) => match n % 6 {
0 => Color::Red,
1 => Color::Green,
2 => Color::Yellow,
3 => Color::Blue,
4 => Color::Magenta,
_ => Color::Cyan,
},
EColor::UnknownVariable => Color::Red,
EColor::WhitespaceAfterHashtag => Color::Red,
EColor::HashUnknown => Color::Red,
EColor::HashIncludeCantLoadFile => Color::Red,
EColor::HashIncludeNotAString => Color::Red,
EColor::HashIncludeErrorInIncludedFile => Color::Red,
EColor::BackslashEscapeUnknown => Color::Red,
EColor::BackslashEscapeEOF => Color::Red,
EColor::StringEOF => Color::Red,
EColor::IfConditionNotBool => Color::Red,
EColor::ChainWithNonFunction => Color::Yellow,
EColor::Function => Color::BrightMagenta,
EColor::FunctionArgument => Color::BrightBlue,
EColor::InitFrom
| EColor::AssignFrom
| EColor::AsTypeStatementWithTooBroadType => Color::BrightCyan,
EColor::InitTo | EColor::AssignTo | EColor::AsTypeTypeAnnotation => {
Color::Green
}
EColor::AssignTargetNonReference => Color::BrightYellow,
EColor::BadCharInTupleType => Color::Red,
EColor::BadCharInFunctionType => Color::Red,
EColor::BadTypeFromParsed => Color::Blue,
EColor::TypeAnnotationNoClosingBracket => Color::Blue,
EColor::TryBadSyntax => Color::Red,
EColor::TryNoFunctionFound => Color::Red,
EColor::TryNotAFunction => Color::Red,
EColor::TryUnusedFunction1 => Color::Red,
EColor::TryUnusedFunction2 => Color::BrightRed,
EColor::CustomTypeTestFailed => Color::BrightRed,
EColor::StacktraceDescend => Color::Yellow,
EColor::StacktraceDescendHashInclude => Color::Red,
EColor::MaximumRuntimeExceeded => Color::BrightYellow,
EColor::InCodePositionLine => Color::BrightBlack,
})
.to_string(),
);
}
}
#[derive(Clone)]
pub enum CheckErrorComponent {
Message(Vec<(String, Option<EColor>)>),
@@ -211,7 +126,7 @@ pub enum CheckErrorComponent {
pub struct CheckErrorHRConfig {
color_index_ptr: Rc<AtomicU32>,
color_index: u32,
theme: Rc<dyn Theme<String>>,
theme: Rc<dyn ETheme>,
is_inner: bool,
style: u8,
idt_start: String,
@@ -335,7 +250,7 @@ impl CheckErrorHRConfig {
#[cfg(feature = "parse")]
pub struct CheckErrorDisplay<'a> {
e: &'a CheckError,
theme: Rc<dyn Theme<String>>,
theme: Rc<dyn ETheme>,
pub show_comments: bool,
}
#[cfg(feature = "parse")]
@@ -409,7 +324,7 @@ impl CheckError {
self.add_mut(CheckErrorComponent::Source(s))
}
#[cfg(feature = "parse")]
pub fn display<'a>(&'a self, theme: impl Theme<String> + 'static) -> CheckErrorDisplay<'a> {
pub fn display<'a>(&'a self, theme: impl ETheme + 'static) -> CheckErrorDisplay<'a> {
CheckErrorDisplay {
e: self,
theme: Rc::new(theme),
@@ -419,13 +334,13 @@ impl CheckError {
/// Like `display`, but doesn't use any theme (doesn't colorize its output)
#[cfg(feature = "parse")]
pub fn display_notheme<'a>(&'a self) -> CheckErrorDisplay<'a> {
self.display(NoTheme)
self.display(themes::NoTheme)
}
/// Like `display`, but uses the default terminal theme
#[cfg(feature = "parse")]
#[cfg(feature = "ecolor-term")]
pub fn display_term<'a>(&'a self) -> CheckErrorDisplay<'a> {
self.display(TermDefaultTheme)
self.display(themes::TermDefaultTheme)
}
/// will, unless empty, end in a newline
#[cfg(feature = "parse")]
@@ -435,7 +350,7 @@ impl CheckError {
cfg: &CheckErrorHRConfig,
) -> std::fmt::Result {
const ADD_RIGHT_BITS: bool = false;
use crate::parsing::SourceFrom;
use crate::{parsing::SourceFrom, theme::ThemeTo};
let len = self.0.len();
for (i, component) in self.0.iter().enumerate() {

View File

@@ -0,0 +1,177 @@
use super::{EColor, ThemeGen};
pub struct NoTheme;
impl ThemeGen for NoTheme {
type C = EColor;
type T = String;
fn color(&self, text: &str, _color: EColor, t: &mut String) {
self.nocolor(text, t);
}
fn nocolor(&self, text: &str, t: &mut String) {
t.push_str(text);
}
}
/// converts an `EColor` to the color type you need for your theme.
/// This theme is optimized for ANSI terminal colors,
/// as most of mers' colored output will be printed to a terminal.
pub fn default_theme<C>(
color: EColor,
gray: C,
red: C,
red_bright: C,
green: C,
green_bright: C,
yellow: C,
yellow_bright: C,
blue: C,
blue_bright: C,
magenta: C,
magenta_bright: C,
cyan: C,
cyan_bright: C,
) -> Option<C> {
if let Indent(n) = color {
return Some(match n % 7 {
1 => red,
2 => green,
3 => yellow,
4 => blue,
5 => magenta,
6 => cyan,
_ => return None,
});
}
let hard_err = red;
let type_right = blue;
let type_wrong = magenta;
let type_wrong_b = magenta_bright;
let function = blue_bright; // used in combination with TYPE_WRONG
let missing = cyan;
let runtime = yellow;
let runtime_b = yellow_bright;
let unused = green;
let unused_b = green_bright;
drop(red_bright);
drop(cyan_bright);
use EColor::*;
Some(match color {
Indent(_) => unreachable!(),
// macros (#...)
WhitespaceAfterHashtag
| HashUnknown
| HashIncludeCantLoadFile
| HashIncludeNotAString
| HashIncludeErrorInIncludedFile
| StacktraceDescendHashInclude => hard_err,
// -- bad syntax --
UnknownVariable => hard_err,
BackslashEscapeUnknown => hard_err,
BackslashEscapeEOF | StringEOF | TypeEOF => missing,
BadCharInTupleType => hard_err,
BadCharInFunctionType => hard_err,
TryBadSyntax => hard_err,
TypeAnnotationNoClosingBracket | BracketedRefTypeNoClosingBracket => missing,
BadTypeFromParsed => type_wrong_b,
// -- type-errors --
IfConditionNotBool => type_wrong,
TryNoFunctionFound => type_wrong_b,
TryNotAFunction => type_wrong,
TryUnusedFunction1 => unused,
TryUnusedFunction2 => unused_b,
CustomTypeTestFailed => hard_err,
ChainWithNonFunction => type_wrong,
AssignTargetNonReference => type_wrong,
Function => function,
FunctionArgument => type_wrong,
InitFrom | AssignFrom | AsTypeStatementWithTooBroadType => type_wrong,
InitTo | AssignTo | AsTypeTypeAnnotation => type_right,
// -- runtime-errors --
StacktraceDescend => runtime,
MaximumRuntimeExceeded => runtime_b,
InCodePositionLine => gray,
})
}
#[cfg(feature = "ecolor-term")]
pub struct TermDefaultTheme;
#[cfg(feature = "ecolor-term")]
impl ThemeGen for TermDefaultTheme {
type C = EColor;
type T = String;
fn color(&self, text: &str, color: EColor, t: &mut String) {
use colored::Color::*;
if let Some(color) = default_theme(
color,
BrightBlack,
Red,
BrightRed,
Green,
BrightGreen,
Yellow,
BrightYellow,
Blue,
BrightBlue,
Magenta,
BrightMagenta,
Cyan,
BrightCyan,
) {
use colored::Colorize;
t.push_str(&text.color(color).to_string());
} else {
self.nocolor(text, t)
}
}
fn nocolor(&self, text: &str, t: &mut String) {
t.push_str(text);
}
}
#[cfg(feature = "ecolor-html")]
pub struct HtmlDefaultTheme;
#[cfg(feature = "ecolor-html")]
impl ThemeGen for HtmlDefaultTheme {
type C = EColor;
type T = String;
fn color(&self, text: &str, color: EColor, t: &mut String) {
if let Some(color) = default_theme(
color,
"Gray",
"Crimson",
"Red",
"Green",
"LimeGreen",
"Gold",
"Yellow",
"RoyalBlue",
"DeepSkyBlue",
"Purple",
"Orchid",
"DarkCyan",
"Turquoise",
) {
t.push_str("<span style=\"color:");
t.push_str(color);
t.push_str(";\">");
html_escape::encode_text_to_string(text, t);
t.push_str("</span>");
} else {
html_escape::encode_text_to_string(text, t);
}
}
fn nocolor(&self, text: &str, t: &mut String) {
t.push_str(text);
}
}