mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
improve and move theming traits
move pretty_print.rs from mers to mers_lib
This commit is contained in:
parent
b3d6b227b5
commit
7a945e80ba
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mers"
|
name = "mers"
|
||||||
version = "0.8.15"
|
version = "0.8.16"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "dynamically typed but type-checked programming language"
|
description = "dynamically typed but type-checked programming language"
|
||||||
@ -12,10 +12,10 @@ repository = "https://github.com/Dummi26/mers"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["colored-output"]
|
default = ["colored-output"]
|
||||||
colored-output = ["mers_lib/ecolor-term", "dep:colored"]
|
colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mers_lib = "0.8.15"
|
mers_lib = "0.8.16"
|
||||||
# mers_lib = { path = "../mers_lib" }
|
# mers_lib = { path = "../mers_lib" }
|
||||||
clap = { version = "4.3.19", features = ["derive"] }
|
clap = { version = "4.3.19", features = ["derive"] }
|
||||||
colored = { version = "2.1.0", optional = true }
|
colored = { version = "2.1.0", optional = true }
|
||||||
|
@ -3,8 +3,6 @@ use mers_lib::prelude_compile::*;
|
|||||||
use std::{path::PathBuf, process::exit, sync::Arc};
|
use std::{path::PathBuf, process::exit, sync::Arc};
|
||||||
|
|
||||||
mod cfg_globals;
|
mod cfg_globals;
|
||||||
#[cfg(feature = "colored-output")]
|
|
||||||
mod pretty_print;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
@ -169,7 +167,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "colored-output")]
|
#[cfg(feature = "colored-output")]
|
||||||
Command::PrettyPrint { source } => {
|
Command::PrettyPrint { source } => {
|
||||||
pretty_print::pretty_print(get_source(source));
|
mers_lib::pretty_print::pretty_print(get_source(source));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "colored-output"))]
|
#[cfg(not(feature = "colored-output"))]
|
||||||
Command::PrettyPrint { source: _ } => {
|
Command::PrettyPrint { source: _ } => {
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
use std::{process::exit, sync::Arc};
|
|
||||||
|
|
||||||
use colored::{Color, Colorize};
|
|
||||||
use mers_lib::prelude_compile::{parse, Source};
|
|
||||||
|
|
||||||
pub fn pretty_print(mut src: Source) {
|
|
||||||
let srca = Arc::new(src.clone());
|
|
||||||
match parse(&mut src, &srca) {
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("{e:?}");
|
|
||||||
exit(28);
|
|
||||||
}
|
|
||||||
Ok(parsed) => {
|
|
||||||
print_parsed(&srca, parsed.as_ref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const COLOR_COMMENT: Color = Color::BrightBlack;
|
|
||||||
const COLOR_VARIABLE: Color = Color::Green;
|
|
||||||
const COLOR_VARIABLE_REF: Color = Color::Green;
|
|
||||||
const COLOR_IF: Color = Color::Red;
|
|
||||||
const COLOR_IF_WITH_ELSE: Color = Color::Red;
|
|
||||||
const COLOR_LOOP: Color = Color::Red;
|
|
||||||
const COLOR_TUPLE: Color = Color::Blue;
|
|
||||||
const COLOR_OBJECT: Color = Color::Blue;
|
|
||||||
const COLOR_VALUE: Color = Color::Cyan;
|
|
||||||
const COLOR_AS_TYPE: Color = Color::BrightBlack;
|
|
||||||
const COLOR_CUSTOM_TYPE: Color = Color::BrightBlack;
|
|
||||||
const COLOR_UNKNOWN: Color = Color::White;
|
|
||||||
|
|
||||||
fn print_parsed(srca: &Arc<Source>, parsed: &dyn mers_lib::program::parsed::MersStatement) {
|
|
||||||
let mut sections = vec![(COLOR_UNKNOWN, srca.src_og().len())];
|
|
||||||
build_print(&mut sections, srca, parsed);
|
|
||||||
for (start, comment) in srca.comments() {
|
|
||||||
let end = start + comment.len();
|
|
||||||
build_print_insert_color(&mut sections, COLOR_COMMENT, *start, end);
|
|
||||||
}
|
|
||||||
let src = srca.src_og();
|
|
||||||
let mut i = 0;
|
|
||||||
for (clr, end) in sections {
|
|
||||||
print!("{}", src[i..end].color(clr));
|
|
||||||
i = end;
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
fn build_print(
|
|
||||||
sections: &mut Vec<(Color, usize)>,
|
|
||||||
srca: &Arc<Source>,
|
|
||||||
parsed: &dyn mers_lib::program::parsed::MersStatement,
|
|
||||||
) {
|
|
||||||
let any = parsed.as_any();
|
|
||||||
let clr = if let Some(v) = any.downcast_ref::<mers_lib::program::parsed::variable::Variable>() {
|
|
||||||
if v.is_ref {
|
|
||||||
COLOR_VARIABLE_REF
|
|
||||||
} else {
|
|
||||||
COLOR_VARIABLE
|
|
||||||
}
|
|
||||||
} else if let Some(v) = any.downcast_ref::<mers_lib::program::parsed::r#if::If>() {
|
|
||||||
if v.on_false.is_some() {
|
|
||||||
COLOR_IF_WITH_ELSE
|
|
||||||
} else {
|
|
||||||
COLOR_IF
|
|
||||||
}
|
|
||||||
} else if let Some(_) = any.downcast_ref::<mers_lib::program::parsed::r#loop::Loop>() {
|
|
||||||
COLOR_LOOP
|
|
||||||
} else if let Some(_) = any.downcast_ref::<mers_lib::program::parsed::tuple::Tuple>() {
|
|
||||||
COLOR_TUPLE
|
|
||||||
} else if let Some(_) = any.downcast_ref::<mers_lib::program::parsed::object::Object>() {
|
|
||||||
COLOR_OBJECT
|
|
||||||
} else if let Some(_) = any.downcast_ref::<mers_lib::program::parsed::value::Value>() {
|
|
||||||
COLOR_VALUE
|
|
||||||
} else if let Some(_) = any.downcast_ref::<mers_lib::program::parsed::as_type::AsType>() {
|
|
||||||
COLOR_AS_TYPE
|
|
||||||
} else if let Some(_) = any.downcast_ref::<mers_lib::program::parsed::custom_type::CustomType>()
|
|
||||||
{
|
|
||||||
COLOR_CUSTOM_TYPE
|
|
||||||
} else {
|
|
||||||
COLOR_UNKNOWN
|
|
||||||
};
|
|
||||||
let range = parsed.source_range();
|
|
||||||
let start = srca.pos_in_og(range.start().pos(), true);
|
|
||||||
let end = srca.pos_in_og(range.end().pos(), false);
|
|
||||||
build_print_insert_color(sections, clr, start, end);
|
|
||||||
for s in parsed.inner_statements() {
|
|
||||||
build_print(sections, srca, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_print_insert_color(
|
|
||||||
sections: &mut Vec<(Color, usize)>,
|
|
||||||
clr: Color,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
) {
|
|
||||||
let thing_at_my_start = sections.iter().position(|v| v.1 > start);
|
|
||||||
let thing_at_my_end = sections.iter().position(|v| v.1 > end);
|
|
||||||
if let Some(thing_at_my_start) = thing_at_my_start {
|
|
||||||
if let Some(thing_at_my_end) = thing_at_my_end {
|
|
||||||
if thing_at_my_start == thing_at_my_end {
|
|
||||||
let (around_color, around_end) = sections[thing_at_my_start];
|
|
||||||
if around_color != clr {
|
|
||||||
sections[thing_at_my_start].1 = start;
|
|
||||||
sections.insert(thing_at_my_start + 1, (clr, end));
|
|
||||||
sections.insert(thing_at_my_start + 2, (around_color, around_end));
|
|
||||||
if sections[thing_at_my_start].1
|
|
||||||
== thing_at_my_start
|
|
||||||
.checked_sub(1)
|
|
||||||
.map(|v| sections[v].1)
|
|
||||||
.unwrap_or(0)
|
|
||||||
{
|
|
||||||
// thing at my start now ends at the same place the thing before it ends, so we can remove it
|
|
||||||
sections.remove(thing_at_my_start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sections[thing_at_my_start].1 = start;
|
|
||||||
sections.insert(thing_at_my_start + 1, (clr, end));
|
|
||||||
for _ in 0..(thing_at_my_end.saturating_sub(thing_at_my_start + 1)) {
|
|
||||||
sections.remove(thing_at_my_start + 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if sections[thing_at_my_start].0 == clr {
|
|
||||||
sections[thing_at_my_start].1 = end;
|
|
||||||
} else {
|
|
||||||
sections[thing_at_my_start].1 = start;
|
|
||||||
sections.push((clr, end));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Some(last) = sections.last_mut().filter(|v| v.0 == clr) {
|
|
||||||
last.1 = end;
|
|
||||||
} else {
|
|
||||||
sections.push((clr, end));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mers_lib"
|
name = "mers_lib"
|
||||||
version = "0.8.15"
|
version = "0.8.16"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "library to use the mers language in other projects"
|
description = "library to use the mers language in other projects"
|
||||||
@ -17,7 +17,12 @@ run = []
|
|||||||
|
|
||||||
# color features are used when formatting error messages.
|
# color features are used when formatting error messages.
|
||||||
ecolor-term = ["dep:colored"]
|
ecolor-term = ["dep:colored"]
|
||||||
|
ecolor-html = ["dep:html-escape"]
|
||||||
|
|
||||||
|
# pretty-print requires ecolor-term, but this is not listed here because I want people to explicitly opt-in to the extra `colored` dependency via the ecolor-term feature.
|
||||||
|
pretty-print = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
colored = { version = "2.1.0", optional = true }
|
|
||||||
line-span = "0.1.5"
|
line-span = "0.1.5"
|
||||||
|
colored = { version = "2.1.0", optional = true }
|
||||||
|
html-escape = { version = "0.2.13", optional = true }
|
||||||
|
@ -8,6 +8,9 @@ use line_span::LineSpans;
|
|||||||
|
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
use crate::parsing::Source;
|
use crate::parsing::Source;
|
||||||
|
use crate::theme::ThemeGen;
|
||||||
|
|
||||||
|
pub mod themes;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct SourcePos(pub(crate) usize);
|
pub struct SourcePos(pub(crate) usize);
|
||||||
@ -62,6 +65,9 @@ pub enum EColor {
|
|||||||
BackslashEscapeUnknown,
|
BackslashEscapeUnknown,
|
||||||
BackslashEscapeEOF,
|
BackslashEscapeEOF,
|
||||||
StringEOF,
|
StringEOF,
|
||||||
|
TypeEOF,
|
||||||
|
/// `&[Int/String` (notice the missing `]`)
|
||||||
|
BracketedRefTypeNoClosingBracket,
|
||||||
IfConditionNotBool,
|
IfConditionNotBool,
|
||||||
ChainWithNonFunction,
|
ChainWithNonFunction,
|
||||||
Function,
|
Function,
|
||||||
@ -92,115 +98,24 @@ pub enum EColor {
|
|||||||
InCodePositionLine,
|
InCodePositionLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Theme<T> {
|
pub trait ETheme: ThemeGen<C = EColor, T = String> {}
|
||||||
fn color(&self, text: &str, color: EColor, t: &mut T);
|
impl<T: ThemeGen<C = EColor, T = String>> ETheme for 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 fn colorize_str(
|
pub fn colorize_str(
|
||||||
message: &Vec<(String, Option<EColor>)>,
|
message: &Vec<(String, Option<EColor>)>,
|
||||||
theme: &(impl Theme<String> + ?Sized),
|
theme: &(impl ETheme + ?Sized),
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut t = String::new();
|
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 {
|
for (text, color) in message {
|
||||||
if let Some(color) = *color {
|
if let Some(color) = *color {
|
||||||
theme.color(text, color, t)
|
theme.color(text, color, &mut t)
|
||||||
} else {
|
} 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)]
|
#[derive(Clone)]
|
||||||
pub enum CheckErrorComponent {
|
pub enum CheckErrorComponent {
|
||||||
Message(Vec<(String, Option<EColor>)>),
|
Message(Vec<(String, Option<EColor>)>),
|
||||||
@ -211,7 +126,7 @@ pub enum CheckErrorComponent {
|
|||||||
pub struct CheckErrorHRConfig {
|
pub struct CheckErrorHRConfig {
|
||||||
color_index_ptr: Rc<AtomicU32>,
|
color_index_ptr: Rc<AtomicU32>,
|
||||||
color_index: u32,
|
color_index: u32,
|
||||||
theme: Rc<dyn Theme<String>>,
|
theme: Rc<dyn ETheme>,
|
||||||
is_inner: bool,
|
is_inner: bool,
|
||||||
style: u8,
|
style: u8,
|
||||||
idt_start: String,
|
idt_start: String,
|
||||||
@ -335,7 +250,7 @@ impl CheckErrorHRConfig {
|
|||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
pub struct CheckErrorDisplay<'a> {
|
pub struct CheckErrorDisplay<'a> {
|
||||||
e: &'a CheckError,
|
e: &'a CheckError,
|
||||||
theme: Rc<dyn Theme<String>>,
|
theme: Rc<dyn ETheme>,
|
||||||
pub show_comments: bool,
|
pub show_comments: bool,
|
||||||
}
|
}
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
@ -409,7 +324,7 @@ impl CheckError {
|
|||||||
self.add_mut(CheckErrorComponent::Source(s))
|
self.add_mut(CheckErrorComponent::Source(s))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "parse")]
|
#[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 {
|
CheckErrorDisplay {
|
||||||
e: self,
|
e: self,
|
||||||
theme: Rc::new(theme),
|
theme: Rc::new(theme),
|
||||||
@ -419,13 +334,13 @@ impl CheckError {
|
|||||||
/// Like `display`, but doesn't use any theme (doesn't colorize its output)
|
/// Like `display`, but doesn't use any theme (doesn't colorize its output)
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
pub fn display_notheme<'a>(&'a self) -> CheckErrorDisplay<'a> {
|
pub fn display_notheme<'a>(&'a self) -> CheckErrorDisplay<'a> {
|
||||||
self.display(NoTheme)
|
self.display(themes::NoTheme)
|
||||||
}
|
}
|
||||||
/// Like `display`, but uses the default terminal theme
|
/// Like `display`, but uses the default terminal theme
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
#[cfg(feature = "ecolor-term")]
|
#[cfg(feature = "ecolor-term")]
|
||||||
pub fn display_term<'a>(&'a self) -> CheckErrorDisplay<'a> {
|
pub fn display_term<'a>(&'a self) -> CheckErrorDisplay<'a> {
|
||||||
self.display(TermDefaultTheme)
|
self.display(themes::TermDefaultTheme)
|
||||||
}
|
}
|
||||||
/// will, unless empty, end in a newline
|
/// will, unless empty, end in a newline
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
@ -435,7 +350,7 @@ impl CheckError {
|
|||||||
cfg: &CheckErrorHRConfig,
|
cfg: &CheckErrorHRConfig,
|
||||||
) -> std::fmt::Result {
|
) -> std::fmt::Result {
|
||||||
const ADD_RIGHT_BITS: bool = false;
|
const ADD_RIGHT_BITS: bool = false;
|
||||||
use crate::parsing::SourceFrom;
|
use crate::{parsing::SourceFrom, theme::ThemeTo};
|
||||||
|
|
||||||
let len = self.0.len();
|
let len = self.0.len();
|
||||||
for (i, component) in self.0.iter().enumerate() {
|
for (i, component) in self.0.iter().enumerate() {
|
||||||
|
177
mers_lib/src/errors/themes.rs
Normal file
177
mers_lib/src/errors/themes.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,10 @@ pub mod info;
|
|||||||
/// parser implementation.
|
/// parser implementation.
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
|
#[cfg(feature = "pretty-print")]
|
||||||
|
pub mod pretty_print;
|
||||||
pub mod program;
|
pub mod program;
|
||||||
|
pub mod theme;
|
||||||
|
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
pub mod prelude_compile {
|
pub mod prelude_compile {
|
||||||
|
@ -308,6 +308,9 @@ impl Source {
|
|||||||
line
|
line
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_pos_last_char(&self) -> SourcePos {
|
||||||
|
SourcePos(self.i.saturating_sub(1))
|
||||||
|
}
|
||||||
pub fn get_pos(&self) -> SourcePos {
|
pub fn get_pos(&self) -> SourcePos {
|
||||||
SourcePos(self.i)
|
SourcePos(self.i)
|
||||||
}
|
}
|
||||||
|
@ -555,7 +555,7 @@ pub fn parse_string(
|
|||||||
srca: &Arc<Source>,
|
srca: &Arc<Source>,
|
||||||
double_quote: SourcePos,
|
double_quote: SourcePos,
|
||||||
) -> Result<String, CheckError> {
|
) -> Result<String, CheckError> {
|
||||||
parse_string_custom_end(src, srca, double_quote, '"', '"')
|
parse_string_custom_end(src, srca, double_quote, '"', '"', "", EColor::StringEOF)
|
||||||
}
|
}
|
||||||
pub fn parse_string_custom_end(
|
pub fn parse_string_custom_end(
|
||||||
src: &mut Source,
|
src: &mut Source,
|
||||||
@ -563,6 +563,8 @@ pub fn parse_string_custom_end(
|
|||||||
opening: SourcePos,
|
opening: SourcePos,
|
||||||
opening_char: char,
|
opening_char: char,
|
||||||
closing_char: char,
|
closing_char: char,
|
||||||
|
string_prefix: &str,
|
||||||
|
eof_color: EColor,
|
||||||
) -> Result<String, CheckError> {
|
) -> Result<String, CheckError> {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
loop {
|
loop {
|
||||||
@ -602,10 +604,10 @@ pub fn parse_string_custom_end(
|
|||||||
return Err(CheckError::new()
|
return Err(CheckError::new()
|
||||||
.src(vec![(
|
.src(vec![(
|
||||||
(opening, src.get_pos(), srca).into(),
|
(opening, src.get_pos(), srca).into(),
|
||||||
Some(EColor::StringEOF),
|
Some(eof_color),
|
||||||
)])
|
)])
|
||||||
.msg_str(format!(
|
.msg_str(format!(
|
||||||
"EOF in string literal{}",
|
"EOF in {string_prefix}string literal{}",
|
||||||
if closing_char != '"' {
|
if closing_char != '"' {
|
||||||
format!(
|
format!(
|
||||||
" {opening_char}...{closing_char} (end string with '{closing_char}')"
|
" {opening_char}...{closing_char} (end string with '{closing_char}')"
|
||||||
|
@ -24,6 +24,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
|||||||
Ok(match src.peek_char() {
|
Ok(match src.peek_char() {
|
||||||
// Reference
|
// Reference
|
||||||
Some('&') => {
|
Some('&') => {
|
||||||
|
let pos_in_src = src.get_pos();
|
||||||
src.next_char();
|
src.next_char();
|
||||||
if let Some('[') = src.peek_char() {
|
if let Some('[') = src.peek_char() {
|
||||||
src.next_char();
|
src.next_char();
|
||||||
@ -35,7 +36,12 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
|||||||
} else {
|
} else {
|
||||||
format!("EOF")
|
format!("EOF")
|
||||||
};
|
};
|
||||||
return Err(CheckError::new().msg_str(format!(
|
return Err(CheckError::new()
|
||||||
|
.src(vec![(
|
||||||
|
(pos_in_src, src.get_pos(), srca).into(),
|
||||||
|
Some(EColor::BracketedRefTypeNoClosingBracket),
|
||||||
|
)])
|
||||||
|
.msg_str(format!(
|
||||||
"No closing ] in reference type with opening [! Found {nc} instead"
|
"No closing ] in reference type with opening [! Found {nc} instead"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@ -177,13 +183,28 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
|
|||||||
src.next_char();
|
src.next_char();
|
||||||
ParsedType::TypeWithInfo(
|
ParsedType::TypeWithInfo(
|
||||||
t,
|
t,
|
||||||
super::statements::parse_string_custom_end(src, srca, pos, '<', '>')?,
|
super::statements::parse_string_custom_end(
|
||||||
|
src,
|
||||||
|
srca,
|
||||||
|
pos,
|
||||||
|
'<',
|
||||||
|
'>',
|
||||||
|
"type-info ",
|
||||||
|
EColor::TypeEOF,
|
||||||
|
)?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ParsedType::Type(t)
|
ParsedType::Type(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => todo!(),
|
None => {
|
||||||
|
return Err(CheckError::new()
|
||||||
|
.src(vec![(
|
||||||
|
(src.get_pos_last_char(), src.get_pos(), srca).into(),
|
||||||
|
Some(EColor::TypeEOF),
|
||||||
|
)])
|
||||||
|
.msg_str(format!("Expected type, got EOF")))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
221
mers_lib/src/pretty_print.rs
Normal file
221
mers_lib/src/pretty_print.rs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
use std::{io::Write, process::exit, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
prelude_compile::{parse, Source},
|
||||||
|
theme::ThemeGen,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ecolor-term"))]
|
||||||
|
compile_error!("feature ecolor-term is required if pretty-print feature is enabled");
|
||||||
|
|
||||||
|
pub fn pretty_print(src: Source) {
|
||||||
|
pretty_print_to(src, &mut std::io::stdout(), DefaultTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_to<O: Write>(mut src: Source, out: &mut O, theme: impl FTheme<O>) {
|
||||||
|
let srca = Arc::new(src.clone());
|
||||||
|
match parse(&mut src, &srca) {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e:?}");
|
||||||
|
exit(28);
|
||||||
|
}
|
||||||
|
Ok(parsed) => {
|
||||||
|
print_parsed(&srca, parsed.as_ref(), out, theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AbstractColor {
|
||||||
|
Gray,
|
||||||
|
Green,
|
||||||
|
Red,
|
||||||
|
Blue,
|
||||||
|
Cyan,
|
||||||
|
}
|
||||||
|
pub fn map_color(color: FColor) -> Option<AbstractColor> {
|
||||||
|
Some(match color {
|
||||||
|
FColor::Comment => AbstractColor::Gray,
|
||||||
|
FColor::Variable => AbstractColor::Green,
|
||||||
|
FColor::VariableRef => AbstractColor::Green,
|
||||||
|
FColor::If => AbstractColor::Red,
|
||||||
|
FColor::IfWithElse => AbstractColor::Red,
|
||||||
|
FColor::Loop => AbstractColor::Red,
|
||||||
|
FColor::Tuple => AbstractColor::Blue,
|
||||||
|
FColor::Object => AbstractColor::Blue,
|
||||||
|
FColor::Value => AbstractColor::Cyan,
|
||||||
|
FColor::AsType => AbstractColor::Gray,
|
||||||
|
FColor::CustomType => AbstractColor::Gray,
|
||||||
|
FColor::Unknown => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum FColor {
|
||||||
|
Comment,
|
||||||
|
Variable,
|
||||||
|
VariableRef,
|
||||||
|
If,
|
||||||
|
IfWithElse,
|
||||||
|
Loop,
|
||||||
|
Tuple,
|
||||||
|
Object,
|
||||||
|
Value,
|
||||||
|
AsType,
|
||||||
|
CustomType,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DefaultTheme;
|
||||||
|
impl ThemeGen for DefaultTheme {
|
||||||
|
type C = FColor;
|
||||||
|
type T = std::io::Stdout;
|
||||||
|
fn color(&self, text: &str, color: Self::C, t: &mut Self::T) {
|
||||||
|
use colored::{Color, Colorize};
|
||||||
|
if let Some(color) = map_color(color) {
|
||||||
|
let _ = write!(
|
||||||
|
t,
|
||||||
|
"{}",
|
||||||
|
text.color(match color {
|
||||||
|
AbstractColor::Gray => Color::BrightBlack,
|
||||||
|
AbstractColor::Green => Color::Green,
|
||||||
|
AbstractColor::Red => Color::Red,
|
||||||
|
AbstractColor::Blue => Color::Blue,
|
||||||
|
AbstractColor::Cyan => Color::Cyan,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let _ = write!(t, "{}", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn nocolor(&self, text: &str, t: &mut Self::T) {
|
||||||
|
let _ = write!(t, "{}", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const FColor::Comment: Color = Color::BrightBlack;
|
||||||
|
// const FColor::Variable: Color = Color::Green;
|
||||||
|
// const FColor::Variable_Ref: Color = Color::Green;
|
||||||
|
// const FColor::If: Color = Color::Red;
|
||||||
|
// const FColor::If_With_Else: Color = Color::Red;
|
||||||
|
// const FColor::Loop: Color = Color::Red;
|
||||||
|
// const FColor::Tuple: Color = Color::Blue;
|
||||||
|
// const FColor::Object: Color = Color::Blue;
|
||||||
|
// const FColor::Value: Color = Color::Cyan;
|
||||||
|
// const FColor::As_Type: Color = Color::BrightBlack;
|
||||||
|
// const FColor::Custom_Type: Color = Color::BrightBlack;
|
||||||
|
// const FColor::Unknown: Color = Color::White;
|
||||||
|
|
||||||
|
pub trait FTheme<O>: ThemeGen<C = FColor, T = O> {}
|
||||||
|
impl<O, T: ThemeGen<C = FColor, T = O>> FTheme<O> for T {}
|
||||||
|
|
||||||
|
fn print_parsed<O: Write>(
|
||||||
|
srca: &Arc<Source>,
|
||||||
|
parsed: &dyn crate::program::parsed::MersStatement,
|
||||||
|
out: &mut O,
|
||||||
|
theme: impl FTheme<O>,
|
||||||
|
) {
|
||||||
|
let mut sections = vec![(FColor::Unknown, srca.src_og().len())];
|
||||||
|
build_print(&mut sections, srca, parsed);
|
||||||
|
for (start, comment) in srca.comments() {
|
||||||
|
let end = start + comment.len();
|
||||||
|
build_print_insert_color(&mut sections, FColor::Comment, *start, end);
|
||||||
|
}
|
||||||
|
let src = srca.src_og();
|
||||||
|
let mut i = 0;
|
||||||
|
for (clr, end) in sections {
|
||||||
|
theme.color(&src[i..end], clr, out);
|
||||||
|
i = end;
|
||||||
|
}
|
||||||
|
let _ = writeln!(out);
|
||||||
|
}
|
||||||
|
fn build_print(
|
||||||
|
sections: &mut Vec<(FColor, usize)>,
|
||||||
|
srca: &Arc<Source>,
|
||||||
|
parsed: &dyn crate::program::parsed::MersStatement,
|
||||||
|
) {
|
||||||
|
let any = parsed.as_any();
|
||||||
|
let clr = if let Some(v) = any.downcast_ref::<crate::program::parsed::variable::Variable>() {
|
||||||
|
if v.is_ref {
|
||||||
|
FColor::VariableRef
|
||||||
|
} else {
|
||||||
|
FColor::Variable
|
||||||
|
}
|
||||||
|
} else if let Some(v) = any.downcast_ref::<crate::program::parsed::r#if::If>() {
|
||||||
|
if v.on_false.is_some() {
|
||||||
|
FColor::IfWithElse
|
||||||
|
} else {
|
||||||
|
FColor::If
|
||||||
|
}
|
||||||
|
} else if let Some(_) = any.downcast_ref::<crate::program::parsed::r#loop::Loop>() {
|
||||||
|
FColor::Loop
|
||||||
|
} else if let Some(_) = any.downcast_ref::<crate::program::parsed::tuple::Tuple>() {
|
||||||
|
FColor::Tuple
|
||||||
|
} else if let Some(_) = any.downcast_ref::<crate::program::parsed::object::Object>() {
|
||||||
|
FColor::Object
|
||||||
|
} else if let Some(_) = any.downcast_ref::<crate::program::parsed::value::Value>() {
|
||||||
|
FColor::Value
|
||||||
|
} else if let Some(_) = any.downcast_ref::<crate::program::parsed::as_type::AsType>() {
|
||||||
|
FColor::AsType
|
||||||
|
} else if let Some(_) = any.downcast_ref::<crate::program::parsed::custom_type::CustomType>() {
|
||||||
|
FColor::CustomType
|
||||||
|
} else {
|
||||||
|
FColor::Unknown
|
||||||
|
};
|
||||||
|
let range = parsed.source_range();
|
||||||
|
let start = srca.pos_in_og(range.start().pos(), true);
|
||||||
|
let end = srca.pos_in_og(range.end().pos(), false);
|
||||||
|
build_print_insert_color(sections, clr, start, end);
|
||||||
|
for s in parsed.inner_statements() {
|
||||||
|
build_print(sections, srca, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_print_insert_color(
|
||||||
|
sections: &mut Vec<(FColor, usize)>,
|
||||||
|
clr: FColor,
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
) {
|
||||||
|
let thing_at_my_start = sections.iter().position(|v| v.1 > start);
|
||||||
|
let thing_at_my_end = sections.iter().position(|v| v.1 > end);
|
||||||
|
if let Some(thing_at_my_start) = thing_at_my_start {
|
||||||
|
if let Some(thing_at_my_end) = thing_at_my_end {
|
||||||
|
if thing_at_my_start == thing_at_my_end {
|
||||||
|
let (around_color, around_end) = sections[thing_at_my_start];
|
||||||
|
if around_color != clr {
|
||||||
|
sections[thing_at_my_start].1 = start;
|
||||||
|
sections.insert(thing_at_my_start + 1, (clr, end));
|
||||||
|
sections.insert(thing_at_my_start + 2, (around_color, around_end));
|
||||||
|
if sections[thing_at_my_start].1
|
||||||
|
== thing_at_my_start
|
||||||
|
.checked_sub(1)
|
||||||
|
.map(|v| sections[v].1)
|
||||||
|
.unwrap_or(0)
|
||||||
|
{
|
||||||
|
// thing at my start now ends at the same place the thing before it ends, so we can remove it
|
||||||
|
sections.remove(thing_at_my_start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sections[thing_at_my_start].1 = start;
|
||||||
|
sections.insert(thing_at_my_start + 1, (clr, end));
|
||||||
|
for _ in 0..(thing_at_my_end.saturating_sub(thing_at_my_start + 1)) {
|
||||||
|
sections.remove(thing_at_my_start + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sections[thing_at_my_start].0 == clr {
|
||||||
|
sections[thing_at_my_start].1 = end;
|
||||||
|
} else {
|
||||||
|
sections[thing_at_my_start].1 = start;
|
||||||
|
sections.push((clr, end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(last) = sections.last_mut().filter(|v| v.0 == clr) {
|
||||||
|
last.1 = end;
|
||||||
|
} else {
|
||||||
|
sections.push((clr, end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -105,7 +105,12 @@ impl MersStatement for Chain {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
todo!("err: not a function");
|
Err(CheckError::new()
|
||||||
|
.msg_str("tried to chain with non-function".to_owned())
|
||||||
|
.src(vec![(
|
||||||
|
self.chained.source_range(),
|
||||||
|
Some(EColor::ChainWithNonFunction),
|
||||||
|
)]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn has_scope(&self) -> bool {
|
fn has_scope(&self) -> bool {
|
||||||
|
16
mers_lib/src/theme.rs
Normal file
16
mers_lib/src/theme.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
pub trait ThemeGen {
|
||||||
|
type C;
|
||||||
|
type T;
|
||||||
|
fn color(&self, text: &str, color: Self::C, t: &mut Self::T);
|
||||||
|
fn nocolor(&self, text: &str, t: &mut Self::T);
|
||||||
|
}
|
||||||
|
pub trait ThemeTo: ThemeGen<T = String> {
|
||||||
|
fn color_to(&self, text: &str, color: <Self as ThemeGen>::C) -> <Self as ThemeGen>::T;
|
||||||
|
}
|
||||||
|
impl<T: ThemeGen<T = String> + ?Sized> ThemeTo for T {
|
||||||
|
fn color_to(&self, text: &str, color: <T as ThemeGen>::C) -> String {
|
||||||
|
let mut t = String::new();
|
||||||
|
self.color(text, color, &mut t);
|
||||||
|
t
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user