All report text types rendered in color with tests

This commit is contained in:
Chad Stearns 2020-03-22 21:56:22 -04:00
parent 3ea27dab39
commit 57cf650ac7
2 changed files with 295 additions and 24 deletions

View file

@ -13,17 +13,39 @@ pub struct Report {
pub struct Palette { pub struct Palette {
pub primary: Color, pub primary: Color,
pub code_block: Color,
pub variable: Color,
pub flex_var: Color,
pub rigid_var: Color,
pub structure: Color,
pub alias: Color,
pub error: Color, pub error: Color,
pub line_number: Color,
pub gutter_bar: Color,
} }
#[derive(Copy, Clone)]
pub enum Color { pub enum Color {
White, White,
Red, Red,
Blue,
Yellow,
Green,
Cyan,
Magenta,
} }
pub const DEFAULT_PALETTE: Palette = Palette { pub const TEST_PALETTE: Palette = Palette {
primary: Color::White, primary: Color::White,
code_block: Color::White,
variable: Color::Blue,
flex_var: Color::Yellow,
rigid_var: Color::Yellow,
structure: Color::Green,
alias: Color::Yellow,
error: Color::Red, error: Color::Red,
line_number: Color::Cyan,
gutter_bar: Color::Magenta,
}; };
impl Color { impl Color {
@ -33,6 +55,11 @@ impl Color {
match self { match self {
Red => red(str), Red => red(str),
White => white(str), White => white(str),
Blue => blue(str),
Yellow => yellow(str),
Green => green(str),
Cyan => cyan(str),
Magenta => magenta(str),
} }
} }
} }
@ -99,31 +126,78 @@ pub fn plain_text(str: &str) -> ReportText {
Plain(Box::from(str)) Plain(Box::from(str))
} }
pub fn em_text(str: &str) -> ReportText {
use ReportText::*;
EmText(Box::from(str))
}
pub fn url(str: &str) -> ReportText {
use ReportText::*;
Url(Box::from(str))
}
fn newline() -> ReportText { fn newline() -> ReportText {
plain_text("\n") plain_text("\n")
} }
pub const RED_CODE: &str = "\u{001b}[31m"; pub const RED_CODE: &str = "\u{001b}[31m";
pub const WHITE_CODE: &str = "\u{001b}[31m"; pub const WHITE_CODE: &str = "\u{001b}[37m";
pub const BLUE_CODE: &str = "\u{001b}[34m";
pub const YELLOW_CODE: &str = "\u{001b}[33m";
pub const GREEN_CODE: &str = "\u{001b}[42m";
pub const CYAN_CODE: &str = "\u{001b}[36m";
pub const MAGENTA_CODE: &str = "\u{001b}[35m";
fn red(str: &str) -> String { pub const BOLD_CODE: &str = "\u{001b}[1m";
pub const UNDERLINE_CODE: &str = "\u{001b}[4m";
fn code(code_str: &str, str: &str) -> String {
let mut buf = String::new(); let mut buf = String::new();
buf.push_str(RED_CODE); buf.push_str(code_str);
buf.push_str(str); buf.push_str(str);
buf.push_str(RESET_CODE); buf.push_str(RESET_CODE);
buf buf
} }
pub fn underline(str: &str) -> String {
code(UNDERLINE_CODE, str)
}
pub fn bold(str: &str) -> String {
code(BOLD_CODE, str)
}
fn cyan(str: &str) -> String {
code(CYAN_CODE, str)
}
fn magenta(str: &str) -> String {
code(MAGENTA_CODE, str)
}
fn green(str: &str) -> String {
code(GREEN_CODE, str)
}
fn yellow(str: &str) -> String {
code(YELLOW_CODE, str)
}
fn blue(str: &str) -> String {
code(BLUE_CODE, str)
}
fn red(str: &str) -> String {
code(RED_CODE, str)
}
fn white(str: &str) -> String { fn white(str: &str) -> String {
let mut buf = String::new(); code(WHITE_CODE, str)
buf.push_str(WHITE_CODE);
buf.push_str(str);
buf.push_str(RESET_CODE);
buf
} }
pub const RESET_CODE: &str = "\u{001b}[0m"; pub const RESET_CODE: &str = "\u{001b}[0m";
@ -206,21 +280,94 @@ impl ReportText {
/// Render to a color terminal using ANSI escape sequences /// Render to a color terminal using ANSI escape sequences
pub fn render_color_terminal( pub fn render_color_terminal(
&self, self,
buf: &mut String, buf: &mut String,
_subs: &mut Subs, subs: &mut Subs,
_home: ModuleId, home: ModuleId,
_src_lines: &[&str], src_lines: &[&str],
_interns: &Interns, interns: &Interns,
palette: Palette, palette: &Palette,
) { ) {
use ReportText::*; use ReportText::*;
match self { match self {
Plain(string) => { Plain(string) => {
buf.push_str(&palette.primary.render(string)); buf.push_str(&palette.primary.render(&string));
} }
EmText(string) => {
buf.push_str(&bold(&string));
}
Url(url) => {
buf.push_str(&underline(&url));
}
Value(symbol) => {
if symbol.module_id() == home {
// Render it unqualified if it's in the current module.
buf.push_str(&palette.variable.render(symbol.ident_string(interns)));
} else {
let mut module_str = String::new();
module_str.push_str(symbol.module_string(interns));
module_str.push('.');
module_str.push_str(symbol.ident_string(interns));
buf.push_str(&palette.variable.render(&module_str));
}
}
Type(content) => match content {
Content::FlexVar(flex_var) => buf.push_str(&palette.flex_var.render(
content_to_string(Content::FlexVar(flex_var), subs, home, interns).as_str(),
)),
Content::RigidVar(rigid_var) => buf.push_str(&palette.rigid_var.render(
content_to_string(Content::RigidVar(rigid_var), subs, home, interns).as_str(),
)),
Content::Structure(structure) => buf.push_str(&palette.structure.render(
// TODO give greater specificity to how structures are colored. Empty record colored differently than tags, etc.
content_to_string(Content::Structure(structure), subs, home, interns).as_str(),
)),
Content::Alias(symbol, vars, var) => buf.push_str(
&palette.alias.render(
content_to_string(Content::Alias(symbol, vars, var), subs, home, interns)
.as_str(),
),
),
Content::Error => {}
},
Region(region) => {
let max_line_number_length = region.end_line.to_string().len();
for i in region.start_line..=region.end_line {
let i_one_indexed = i + 1;
let line_number_string = i_one_indexed.to_string();
let line_number = line_number_string.as_str();
let this_line_number_length = line_number.len();
buf.push_str(
" ".repeat(max_line_number_length - this_line_number_length)
.as_str(),
);
buf.push_str(&palette.line_number.render(line_number));
buf.push_str(&palette.gutter_bar.render(""));
let line = src_lines[i as usize];
if !line.trim().is_empty() {
buf.push_str(" ");
buf.push_str(&palette.code_block.render(src_lines[i as usize]));
}
if i != region.end_line {
buf.push('\n');
}
}
}
Batch(report_texts) => {
for report_text in report_texts {
report_text.render_color_terminal(buf, subs, home, src_lines, interns, palette);
}
}
_ => panic!("TODO implement more ReportTexts in render color terminal"), _ => panic!("TODO implement more ReportTexts in render color terminal"),
} }
} }

View file

@ -12,7 +12,9 @@ mod test_report {
use crate::helpers::test_home; use crate::helpers::test_home;
use roc_module::symbol::{Interns, ModuleId}; use roc_module::symbol::{Interns, ModuleId};
use roc_reporting::report::{ use roc_reporting::report::{
can_problem, plain_text, Report, ReportText, DEFAULT_PALETTE, RED_CODE, RESET_CODE, can_problem, em_text, plain_text, url, Report, ReportText, BLUE_CODE, BOLD_CODE, CYAN_CODE,
GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, TEST_PALETTE, UNDERLINE_CODE, WHITE_CODE,
YELLOW_CODE,
}; };
use roc_types::pretty_print::name_all_type_vars; use roc_types::pretty_print::name_all_type_vars;
use roc_types::subs::Subs; use roc_types::subs::Subs;
@ -20,7 +22,7 @@ mod test_report {
use std::path::PathBuf; use std::path::PathBuf;
// use roc_region::all; // use roc_region::all;
use crate::helpers::{can_expr, infer_expr, CanExprOut}; use crate::helpers::{can_expr, infer_expr, CanExprOut};
use roc_reporting::report::ReportText::{Batch, EmText, Region, Type, Url, Value}; use roc_reporting::report::ReportText::{Batch, Region, Type, Value};
use roc_types::subs::Content::{FlexVar, RigidVar, Structure}; use roc_types::subs::Content::{FlexVar, RigidVar, Structure};
use roc_types::subs::FlatType::EmptyRecord; use roc_types::subs::FlatType::EmptyRecord;
@ -87,7 +89,15 @@ mod test_report {
fn human_readable(str: &str) -> String { fn human_readable(str: &str) -> String {
return str return str
.replace(RED_CODE, "<red>") .replace(RED_CODE, "<red>")
.replace(RESET_CODE, "<reset>"); .replace(WHITE_CODE, "<white>")
.replace(BLUE_CODE, "<blue>")
.replace(YELLOW_CODE, "<yellow>")
.replace(GREEN_CODE, "<green>")
.replace(CYAN_CODE, "<cyan>")
.replace(MAGENTA_CODE, "<magenta>")
.replace(RESET_CODE, "<reset>")
.replace(BOLD_CODE, "<bold>")
.replace(UNDERLINE_CODE, "<underline>");
} }
fn report_renders_in_color_from_src(src: &str, report: Report, expected_rendering: &str) { fn report_renders_in_color_from_src(src: &str, report: Report, expected_rendering: &str) {
@ -101,7 +111,7 @@ mod test_report {
home, home,
&src_lines, &src_lines,
&interns, &interns,
DEFAULT_PALETTE, &TEST_PALETTE,
); );
assert_eq!(human_readable(&buf), expected_rendering); assert_eq!(human_readable(&buf), expected_rendering);
@ -255,8 +265,122 @@ mod test_report {
} }
#[test] #[test]
fn report_in_color() { fn report_plain_text_color() {
report_renders_in_color(to_simple_report(plain_text("y")), "<red>y<reset>"); report_renders_in_color(to_simple_report(plain_text("y")), "<white>y<reset>");
}
#[test]
fn report_em_text_color() {
report_renders_in_color(to_simple_report(em_text("HELLO!")), "<bold>HELLO!<reset>");
}
#[test]
fn report_url_color() {
report_renders_in_color(
to_simple_report(url("www.roc.com/blog")),
"<underline>www.roc.com/blog<reset>",
);
}
#[test]
fn report_value_color() {
let src: &str = indoc!(
r#"
activityIndicatorLarge = div
view activityIndicatorLarge
"#
);
let (_type_problems, _can_problems, mut subs, home, interns) = infer_expr_help(src);
let mut buf = String::new();
let src_lines: Vec<&str> = src.split('\n').collect();
to_simple_report(Value(
interns.symbol(test_home(), "activityIndicatorLarge".into()),
))
.text
.render_color_terminal(
&mut buf,
&mut subs,
home,
&src_lines,
&interns,
&TEST_PALETTE,
);
assert_eq!(human_readable(&buf), "<blue>activityIndicatorLarge<reset>");
}
#[test]
fn report_wildcard_in_color() {
report_renders_in_color(to_simple_report(Type(FlexVar(None))), "<yellow>*<reset>");
}
#[test]
fn report_flex_var_in_color() {
report_renders_in_color(
to_simple_report(Type(FlexVar(Some("msg".into())))),
"<yellow>msg<reset>",
);
}
#[test]
fn report_rigid_var_in_color() {
report_renders_in_color(
to_simple_report(Type(RigidVar("Str".into()))),
"<yellow>Str<reset>",
);
}
#[test]
fn report_empty_record_in_color() {
report_renders_in_color(
to_simple_report(Type(Structure(EmptyRecord))),
"<green>{}<reset>",
);
}
#[test]
fn report_batch_in_color() {
let mut report_texts = Vec::new();
report_texts.push(Type(RigidVar("List".into())));
report_texts.push(plain_text(" "));
report_texts.push(Type(Structure(EmptyRecord)));
report_renders_in_color(
to_simple_report(Batch(report_texts)),
"<yellow>List<reset><white> <reset><green>{}<reset>",
);
}
#[test]
fn report_region_in_color() {
report_renders_in_color_from_src(
indoc!(
r#"
isDisabled = \user -> user.isAdmin
theAdmin
|> isDabled
"#
),
to_simple_report(Region(roc_region::all::Region {
start_line: 0,
end_line: 3,
start_col: 0,
end_col: 0,
})),
indoc!(
r#"
<cyan>1<reset><magenta> <reset> <white>isDisabled = \user -> user.isAdmin<reset>
<cyan>2<reset><magenta> <reset>
<cyan>3<reset><magenta> <reset> <white>theAdmin<reset>
<cyan>4<reset><magenta> <reset> <white> |> isDabled<reset>"#
),
);
} }
#[test] #[test]