#![allow(dead_code)]
use colored::Colorize;
use pad::PadStr;
use crate::compiler::logger::LogType;
pub struct Displayer {
color: (u8, u8, u8),
row: usize,
col: usize,
len: usize
}
impl Displayer {
pub fn new(color: (u8, u8, u8), row: usize, col: usize, len: usize) -> Self {
Displayer {
color,
row,
col,
len
}
}
pub fn header(self, kind: LogType) -> Self {
let (r, g, b) = self.color;
let name = match kind {
LogType::Error => format!(" ERROR "),
LogType::Warning => format!(" WARN "),
LogType::Info => format!(" INFO ")
};
let formatted = name
.truecolor(0, 0, 0)
.bold()
.on_truecolor(r, g, b);
println!("{formatted}");
self
}
pub fn text(self, text: Option<String>) -> Self {
let (r, g, b) = self.color;
if let Some(text) = text {
println!("{}", text.truecolor(r, g, b));
}
self
}
pub fn padded_text(self, text: Option<String>) -> Self {
let (r, g, b) = self.color;
if let Some(text) = text {
println!("\n{}", text.truecolor(r, g, b));
}
self
}
pub fn path(self, path: Option<String>) -> Self {
let path = path.unwrap_or(format!("[unknown]"));
let (r, g, b) = self.color;
let formatted = format!("at {}:{}:{}", path, self.row, self.col)
.truecolor(r, g, b)
.dimmed();
println!("{formatted}");
self
}
fn get_max_pad_size(&self, length: usize) -> usize {
if self.row < length - 1 {
format!("{}", self.row + 1).len()
}
else {
format!("{}", self.row).len()
}
}
fn get_highlighted_part(&self, line: &String) -> [String;3] {
let begin = self.col - 1;
let end = begin + self.len;
let mut results: [String; 3] = Default::default();
for (index, letter) in line.chars().enumerate() {
if index < begin {
results[0].push(letter);
}
else if index >= end {
results[2].push(letter);
}
else {
results[1].push(letter);
}
}
results
}
fn get_snippet_row(&self, code: &Vec<String>, index: usize, offset: i8, overflow: &mut usize) -> String {
let max_pad = self.get_max_pad_size(code.len());
let index = index as i32 + offset as i32;
let row = self.row as i32 + offset as i32;
let code = code[index as usize].clone();
let line = format!("{row}").pad_to_width(max_pad);
let (r, g, b) = self.color;
if offset == 0 {
let slices = self.get_highlighted_part(&code);
let formatted = format!("{}{}{}", slices[0], slices[1].truecolor(r, g, b), slices[2]);
if self.col - 1 + self.len > code.chars().count() {
*overflow = self.col - 2 + self.len - code.chars().count();
}
format!("{}", format!("{line}| {formatted}"))
}
else {
if *overflow > 0 {
if *overflow > code.chars().count() {
format!("{}", format!("{line}| {}", code.truecolor(r, g, b)).dimmed())
}
else {
let err = code.get(0..*overflow).unwrap().to_string().truecolor(r, g, b);
let rest = code.get(*overflow..).unwrap().to_string();
format!("{}", format!("{line}| {err}{rest}").dimmed())
}
}
else {
format!("{}", format!("{line}| {code}").dimmed())
}
}
}
pub fn snippet<T: AsRef<str>>(self, code: Option<T>) {
if let Some(code) = code {
let mut overflow = 0;
let index = self.row - 1;
let code: String = String::from(code.as_ref());
let code = code.split("\n")
.map(|item| item.to_string())
.collect::<Vec<String>>();
println!("");
if index > 0 {
println!("{}", self.get_snippet_row(&code, index, -1, &mut overflow));
}
println!("{}", self.get_snippet_row(&code, index, 0, &mut overflow));
if index < code.len() - 1 {
println!("{}", self.get_snippet_row(&code, index, 1, &mut overflow));
}
}
}
}
#[cfg(test)]
mod test {
#![allow(unused_imports)]
use std::time::Duration;
use std::thread::sleep;
#[allow(unused_variables)]
#[test]
fn test_displayer() {
let code = vec![
"let a = 12",
"value = 'this",
"is mutltiline",
"code"
].join("\n");
}
}