diff --git a/compiler/erg_common/style.rs b/compiler/erg_common/style.rs index 479672c5..f466ac1c 100644 --- a/compiler/erg_common/style.rs +++ b/compiler/erg_common/style.rs @@ -1,94 +1,317 @@ -use std::fmt::Display; - -pub const ATT_RESET: &str = "\x1b[0m"; +pub const ATTR_RESET: &str = "\x1b[0m"; pub const BOLD: &str = "\x1b[1m"; pub const UNDERLINE: &str = "\x1b[4m"; // Escape sequences change the color of the terminal pub const RESET: &str = "\x1b[m"; pub const BLACK: &str = "\x1b[30m"; -pub const DEEP_RED: &str = "\x1b[31m"; -pub const DEEP_GREEN: &str = "\x1b[32m"; -pub const DEEP_YELLOW: &str = "\x1b[33m"; -pub const DEEP_BLUE: &str = "\x1b[34m"; -pub const DEEP_MAGENTA: &str = "\x1b[35m"; -pub const DEEP_CYAN: &str = "\x1b[36m"; -pub const GRAY: &str = "\x1b[37m"; -pub const RED: &str = "\x1b[91m"; -pub const GREEN: &str = "\x1b[92m"; -pub const YELLOW: &str = "\x1b[93m"; pub const BLUE: &str = "\x1b[94m"; -pub const MAGENTA: &str = "\x1b[95m"; pub const CYAN: &str = "\x1b[96m"; +pub const GRAY: &str = "\x1b[37m"; +pub const GREEN: &str = "\x1b[92m"; +pub const MAGENTA: &str = "\x1b[95m"; +pub const RED: &str = "\x1b[91m"; pub const WHITE: &str = "\x1b[97m"; +pub const YELLOW: &str = "\x1b[93m"; +// custom colors when use `pretty` +pub const CUSTOM_RED: &str = "\x1b[38;2;185;64;71m"; +pub const CUSTOM_BLUE: &str = "\x1b[38;2;230;234;227m"; +pub const CUSTOM_GRAY: &str = "\x1b[38;2;244;0;25m"; +pub const CUSTOM_CYAN: &str = "\x1b[38;2;160;216;239m"; +pub const CUSTOM_MAGENTA: &str = "\x1b[38;2;103;65;150m"; +pub const CUSTOM_GREEN: &str = "\x1b[38;2;170;209;71m"; +pub const CUSTOM_YELLOW: &str = "\x1b[38;2;230;180;34m"; -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] pub enum Color { + Reset, + Black, + Blue, Cyan, - Green, Gray, + Green, Magenta, Red, + White, Yellow, + CustomRed, + CustomBlue, + CustomGray, + CustomCyan, + CustomMagenta, + CustomGreen, + CustomYellow, } impl Color { - fn as_str<'a>(self) -> &'a str { + pub fn as_str(&self) -> &'static str { match self { + Color::Reset => RESET, + Color::Black => BLACK, + Color::Blue => BLUE, Color::Cyan => CYAN, - Color::Green => GREEN, Color::Gray => GRAY, + Color::Green => GREEN, Color::Magenta => MAGENTA, Color::Red => RED, Color::Yellow => YELLOW, + Color::White => WHITE, + Color::CustomRed => CUSTOM_RED, + Color::CustomBlue => CUSTOM_BLUE, + Color::CustomGray => CUSTOM_GRAY, + Color::CustomCyan => CUSTOM_CYAN, + Color::CustomMagenta => CUSTOM_MAGENTA, + Color::CustomGreen => CUSTOM_GREEN, + Color::CustomYellow => CUSTOM_YELLOW, } } } -pub struct Span<'a> { - text: &'a str, - color: Color, +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] +pub enum Attribute { + Reset, + Underline, + Bold, } -impl<'a> Span<'a> { - pub fn new(text: &'a str, color: Color) -> Self { - Self { text, color } +impl Attribute { + pub fn as_str(&self) -> &'static str { + match self { + Attribute::Reset => ATTR_RESET, + Attribute::Underline => UNDERLINE, + Attribute::Bold => BOLD, + } } } -impl Display for Span<'_> { +#[derive(Debug, Clone, Copy)] +pub struct ThemeColors { + pub error: Color, + pub warning: Color, + pub exception: Color, + pub gutter: Color, + pub hint: Color, +} + +#[derive(Debug, Clone, Copy)] +pub struct Characters { + pub hat: char, // error + pub wave: char, // exception + pub line: char, // warning and left bottom line + pub vbar: char, // gutter separator + pub lbot: char, // left bottom curve + pub vbreak: char, // gutter omission + pub lbrac: char, // error kind modifier left bracket + pub rbrac: char, // error kind modifier right bracket +} + +impl Characters { + pub fn mark(&self, kind: &str) -> String { + let mark = match kind { + "Error" => self.hat, + "Warning" => self.line, + "Exception" => self.wave, + invalid => panic!("In Characters, Invalid parameter: {invalid}"), + }; + mark.to_string() + } + pub fn left_bottom_line(&self) -> String { + format!(" {}{} ", self.lbot, self.line) + } + + pub fn error_kind_format(&self, kind: &str, err_num: usize) -> String { + const PADDING: usize = 4; + format!("{kind}{}#{err_num:>0PADDING$}{}", self.lbrac, self.rbrac,) + } +#[derive(Debug, Clone, Copy)] +pub struct Theme { + pub colors: ThemeColors, + pub characters: Characters, +} + +impl Theme { + pub const fn characters(&self) -> (Color, &Characters) { + (self.colors.gutter, &self.characters) + } + + pub const fn error(&self) -> (Color, char) { + (self.colors.error, self.characters.hat) + } + + pub const fn warning(&self) -> (Color, char) { + (self.colors.warning, self.characters.line) + } + + pub const fn exception(&self) -> (Color, char) { + (self.colors.exception, self.characters.wave) + } + + pub const fn hint(&self) -> (Color, char) { + (self.colors.hint, self.characters.wave) + } + + pub fn error_kind_format(&self, kind: &str, err_num: usize) -> String { + self.characters.error_kind_format(kind, err_num) + } +} + +pub const THEME: Theme = Theme { + colors: COLORS, + characters: CHARS, +}; + +pub const CHARS: Characters = Characters { + hat: '-', + line: '-', + vbar: '|', + wave: '~', + lbot: '`', + vbreak: ':', + lbrac: '[', + rbrac: ']', +}; +pub const COLORS: ThemeColors = ThemeColors { + error: Color::Red, + warning: Color::Yellow, + exception: Color::Magenta, + gutter: Color::Cyan, + hint: Color::Green, +}; + +#[derive(Debug)] +pub struct StrSpan<'a> { + span: &'a str, + color: Option, + attribute: Option, +} + +impl<'a> StrSpan<'a> { + pub const fn new<'b: 'a>( + span: &'b str, + color: Option, + attribute: Option, + ) -> Self { + Self { + span, + color, + attribute, + } + } +} + +impl std::fmt::Display for StrSpan<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let color = self.color.as_str(); - write!(f, "{}{}{RESET}", color, self.text) + match (self.color, self.attribute) { + (None, None) => todo!(), + (None, Some(attr)) => write!(f, "{}{}{}", attr.as_str(), self.span, ATTR_RESET), + (Some(color), None) => write!(f, "{}{}{}", color.as_str(), self.span, RESET), + (Some(color), Some(attr)) => { + write!( + f, + "{}{}{}{}{}", + color.as_str(), + attr.as_str(), + self.span, + RESET, + ATTR_RESET + ) + } + } + } +} +#[derive(Debug)] +pub struct StringSpan { + span: String, + color: Option, + attribute: Option, +} + +impl StringSpan { + pub fn new(s: &str, color: Option, attribute: Option) -> Self { + Self { + span: String::from(s), + color, + attribute, + } + } + + pub fn push_str(&mut self, s: &str) { + self.span.push_str(s); } } -pub struct Spans<'a>(Vec>); - -impl<'a> Spans<'a> { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn from(s: Vec>) -> Self { - Self(s) - } - - pub fn push_str(&mut self, text: &str, color: Color) { - let span = Span::new(text, color); - } - - pub fn push_span(&mut self, span: Span<'a>) { - self.0.push(span); - } - - fn connect(self) -> String { - let mut s = String::new(); - for x in self.0.into_iter() { - s.push_str(x.color.as_str()); - s.push_str(x.text); +impl std::fmt::Display for StringSpan { + fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result { + match (self.color, self.attribute) { + (None, None) => write!(f, "{}", self.span), + (None, Some(attr)) => write!(f, "{}{}{}", attr.as_str(), self.span, ATTR_RESET), + (Some(color), None) => write!(f, "{}{}{}", color.as_str(), self.span, RESET), + (Some(color), Some(attr)) => write!( + f, + "{}{}{}{}{}", + attr.as_str(), + color.as_str(), + self.span, + RESET, + ATTR_RESET + ), } - s + RESET + } +} + +#[derive(Debug, Default)] +pub struct StringSpans { + spans: Vec, +} + +impl StringSpans { + pub fn push_str(&mut self, s: &str) { + if self.is_same_color(Color::Gray) { + self.spans.last_mut().unwrap().span.push_str(s); + } else { + self.spans.push(StringSpan::new(s, None, None)); + } + } + + pub fn push_str_with_color(&mut self, s: &str, color: Color) { + if self.is_same_color(color) { + self.spans.last_mut().unwrap().span.push_str(s); + } else { + self.spans.push(StringSpan::new(s, Some(color), None)); + } + } + + pub fn push_str_with_color_and_attribute(&mut self, s: &str, color: Color, attr: Attribute) { + if self.is_same_color(color) && self.is_same_attribute(attr) { + self.spans.last_mut().unwrap().span.push_str(s); + } else { + self.spans.push(StringSpan::new(s, Some(color), Some(attr))); + } + } + + pub fn is_same_color(&self, color: Color) -> bool { + if let Some(span) = self.spans.last() { + return span.color == Some(color); + } + false + } + + pub fn is_same_attribute(&self, attr: Attribute) -> bool { + if let Some(span) = self.spans.last() { + if let Some(span_attr) = span.attribute { + return span_attr == attr; + } + } + false + } +} + +impl std::fmt::Display for StringSpans { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + for span in self.spans.iter() { + write!(f, "{}", span)?; + } + Ok(()) } }