mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 12:51:10 +00:00
Update: add colors, attributes and Span structure
This commit is contained in:
parent
e29eb0b6ed
commit
2e8ac2848d
1 changed files with 275 additions and 52 deletions
|
@ -1,94 +1,317 @@
|
||||||
use std::fmt::Display;
|
pub const ATTR_RESET: &str = "\x1b[0m";
|
||||||
|
|
||||||
pub const ATT_RESET: &str = "\x1b[0m";
|
|
||||||
pub const BOLD: &str = "\x1b[1m";
|
pub const BOLD: &str = "\x1b[1m";
|
||||||
pub const UNDERLINE: &str = "\x1b[4m";
|
pub const UNDERLINE: &str = "\x1b[4m";
|
||||||
|
|
||||||
// Escape sequences change the color of the terminal
|
// Escape sequences change the color of the terminal
|
||||||
pub const RESET: &str = "\x1b[m";
|
pub const RESET: &str = "\x1b[m";
|
||||||
pub const BLACK: &str = "\x1b[30m";
|
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 BLUE: &str = "\x1b[94m";
|
||||||
pub const MAGENTA: &str = "\x1b[95m";
|
|
||||||
pub const CYAN: &str = "\x1b[96m";
|
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 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 {
|
pub enum Color {
|
||||||
|
Reset,
|
||||||
|
Black,
|
||||||
|
Blue,
|
||||||
Cyan,
|
Cyan,
|
||||||
Green,
|
|
||||||
Gray,
|
Gray,
|
||||||
|
Green,
|
||||||
Magenta,
|
Magenta,
|
||||||
Red,
|
Red,
|
||||||
|
White,
|
||||||
Yellow,
|
Yellow,
|
||||||
|
CustomRed,
|
||||||
|
CustomBlue,
|
||||||
|
CustomGray,
|
||||||
|
CustomCyan,
|
||||||
|
CustomMagenta,
|
||||||
|
CustomGreen,
|
||||||
|
CustomYellow,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
fn as_str<'a>(self) -> &'a str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
Color::Reset => RESET,
|
||||||
|
Color::Black => BLACK,
|
||||||
|
Color::Blue => BLUE,
|
||||||
Color::Cyan => CYAN,
|
Color::Cyan => CYAN,
|
||||||
Color::Green => GREEN,
|
|
||||||
Color::Gray => GRAY,
|
Color::Gray => GRAY,
|
||||||
|
Color::Green => GREEN,
|
||||||
Color::Magenta => MAGENTA,
|
Color::Magenta => MAGENTA,
|
||||||
Color::Red => RED,
|
Color::Red => RED,
|
||||||
Color::Yellow => YELLOW,
|
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> {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
||||||
text: &'a str,
|
pub enum Attribute {
|
||||||
color: Color,
|
Reset,
|
||||||
|
Underline,
|
||||||
|
Bold,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Span<'a> {
|
impl Attribute {
|
||||||
pub fn new(text: &'a str, color: Color) -> Self {
|
pub fn as_str(&self) -> &'static str {
|
||||||
Self { text, color }
|
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<Color>,
|
||||||
|
attribute: Option<Attribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StrSpan<'a> {
|
||||||
|
pub const fn new<'b: 'a>(
|
||||||
|
span: &'b str,
|
||||||
|
color: Option<Color>,
|
||||||
|
attribute: Option<Attribute>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
span,
|
||||||
|
color,
|
||||||
|
attribute,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for StrSpan<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let color = self.color.as_str();
|
match (self.color, self.attribute) {
|
||||||
write!(f, "{}{}{RESET}", color, self.text)
|
(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<Color>,
|
||||||
|
attribute: Option<Attribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringSpan {
|
||||||
|
pub fn new(s: &str, color: Option<Color>, attribute: Option<Attribute>) -> Self {
|
||||||
|
Self {
|
||||||
|
span: String::from(s),
|
||||||
|
color,
|
||||||
|
attribute,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Spans<'a>(Vec<Span<'a>>);
|
pub fn push_str(&mut self, s: &str) {
|
||||||
|
self.span.push_str(s);
|
||||||
impl<'a> Spans<'a> {
|
}
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(Vec::new())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from(s: Vec<Span<'a>>) -> Self {
|
impl std::fmt::Display for StringSpan {
|
||||||
Self(s)
|
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
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_str(&mut self, text: &str, color: Color) {
|
#[derive(Debug, Default)]
|
||||||
let span = Span::new(text, color);
|
pub struct StringSpans {
|
||||||
|
spans: Vec<StringSpan>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_span(&mut self, span: Span<'a>) {
|
impl StringSpans {
|
||||||
self.0.push(span);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect(self) -> String {
|
pub fn push_str_with_color(&mut self, s: &str, color: Color) {
|
||||||
let mut s = String::new();
|
if self.is_same_color(color) {
|
||||||
for x in self.0.into_iter() {
|
self.spans.last_mut().unwrap().span.push_str(s);
|
||||||
s.push_str(x.color.as_str());
|
} else {
|
||||||
s.push_str(x.text);
|
self.spans.push(StringSpan::new(s, Some(color), None));
|
||||||
}
|
}
|
||||||
s + RESET
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue