From 0d7a7fe34f6d34addcf1451a8569028a9c01776d Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Thu, 17 Mar 2022 09:31:14 +0000 Subject: [PATCH] reporting: generalise text styles to work with HTML as well as ANSI --- reporting/src/report.rs | 155 ++++++++++++++++++++---------- reporting/tests/test_reporting.rs | 26 ++--- 2 files changed, 116 insertions(+), 65 deletions(-) diff --git a/reporting/src/report.rs b/reporting/src/report.rs index f7dc3c3ba8..eb2b1ea409 100644 --- a/reporting/src/report.rs +++ b/reporting/src/report.rs @@ -128,59 +128,110 @@ impl<'b> Report<'b> { } } -pub struct Palette<'a> { - pub primary: &'a str, - pub code_block: &'a str, - pub keyword: &'a str, - pub variable: &'a str, - pub type_variable: &'a str, - pub structure: &'a str, - pub alias: &'a str, - pub opaque: &'a str, - pub error: &'a str, - pub line_number: &'a str, - pub header: &'a str, - pub gutter_bar: &'a str, - pub module_name: &'a str, - pub binop: &'a str, - pub typo: &'a str, - pub typo_suggestion: &'a str, - pub parser_suggestion: &'a str, +/// This struct is a combination of several things +/// 1. A set of StyleCodes suitable for the platform we're running on (web or terminal) +/// 2. A set of colors we decided to use +/// 3. A mapping from UI elements to the styles we use for them +/// Note: This should really be called Theme! Usually a "palette" is just (2). +pub struct Palette { + pub primary: &'static str, + pub code_block: &'static str, + pub keyword: &'static str, + pub variable: &'static str, + pub type_variable: &'static str, + pub structure: &'static str, + pub alias: &'static str, + pub opaque: &'static str, + pub error: &'static str, + pub line_number: &'static str, + pub header: &'static str, + pub gutter_bar: &'static str, + pub module_name: &'static str, + pub binop: &'static str, + pub typo: &'static str, + pub typo_suggestion: &'static str, + pub parser_suggestion: &'static str, + pub bold: &'static str, + pub underline: &'static str, + pub reset: &'static str, } -pub const DEFAULT_PALETTE: Palette = Palette { - primary: WHITE_CODE, - code_block: WHITE_CODE, - keyword: GREEN_CODE, - variable: BLUE_CODE, - type_variable: YELLOW_CODE, - structure: GREEN_CODE, - alias: YELLOW_CODE, - opaque: YELLOW_CODE, - error: RED_CODE, - line_number: CYAN_CODE, - header: CYAN_CODE, - gutter_bar: CYAN_CODE, - module_name: GREEN_CODE, - binop: GREEN_CODE, - typo: YELLOW_CODE, - typo_suggestion: GREEN_CODE, - parser_suggestion: YELLOW_CODE, +/// Set the default styles for various semantic elements, +/// given a set of StyleCodes for a platform (web or terminal). +const fn default_palette_from_style_codes(codes: StyleCodes) -> Palette { + Palette { + primary: codes.white, + code_block: codes.white, + keyword: codes.green, + variable: codes.blue, + type_variable: codes.yellow, + structure: codes.green, + alias: codes.yellow, + opaque: codes.yellow, + error: codes.red, + line_number: codes.cyan, + header: codes.cyan, + gutter_bar: codes.cyan, + module_name: codes.green, + binop: codes.green, + typo: codes.yellow, + typo_suggestion: codes.green, + parser_suggestion: codes.yellow, + bold: codes.bold, + underline: codes.underline, + reset: codes.reset, + } +} + +pub const DEFAULT_PALETTE: Palette = default_palette_from_style_codes(ANSI_STYLE_CODES); + +pub const DEFAULT_PALETTE_HTML: Palette = default_palette_from_style_codes(HTML_STYLE_CODES); + +/// A machine-readable format for text styles (colors and other styles) +pub struct StyleCodes { + pub red: &'static str, + pub green: &'static str, + pub yellow: &'static str, + pub blue: &'static str, + pub magenta: &'static str, + pub cyan: &'static str, + pub white: &'static str, + pub bold: &'static str, + pub underline: &'static str, + pub reset: &'static str, +} + +pub const ANSI_STYLE_CODES: StyleCodes = StyleCodes { + red: "\u{001b}[31m", + green: "\u{001b}[32m", + yellow: "\u{001b}[33m", + blue: "\u{001b}[34m", + magenta: "\u{001b}[35m", + cyan: "\u{001b}[36m", + white: "\u{001b}[37m", + bold: "\u{001b}[1m", + underline: "\u{001b}[4m", + reset: "\u{001b}[0m", }; -pub const RED_CODE: &str = "\u{001b}[31m"; -pub const GREEN_CODE: &str = "\u{001b}[32m"; -pub const YELLOW_CODE: &str = "\u{001b}[33m"; -pub const BLUE_CODE: &str = "\u{001b}[34m"; -pub const MAGENTA_CODE: &str = "\u{001b}[35m"; -pub const CYAN_CODE: &str = "\u{001b}[36m"; -pub const WHITE_CODE: &str = "\u{001b}[37m"; +macro_rules! html_color { + ($name: expr) => { + concat!("") + }; +} -pub const BOLD_CODE: &str = "\u{001b}[1m"; - -pub const UNDERLINE_CODE: &str = "\u{001b}[4m"; - -pub const RESET_CODE: &str = "\u{001b}[0m"; +pub const HTML_STYLE_CODES: StyleCodes = StyleCodes { + red: html_color!("red"), + green: html_color!("green"), + yellow: html_color!("yellow"), + blue: html_color!("blue"), + magenta: html_color!("magenta"), + cyan: html_color!("cyan"), + white: html_color!("white"), + bold: "", +}; // define custom allocator struct so we can `impl RocDocAllocator` custom helpers pub struct RocDocAllocator<'a> { @@ -742,7 +793,7 @@ impl CiWrite { /// Render with fancy formatting pub struct ColorWrite<'a, W> { style_stack: Vec, - palette: &'a Palette<'a>, + palette: &'a Palette, upstream: W, } @@ -858,10 +909,10 @@ where use Annotation::*; match annotation { Emphasized => { - self.write_str(BOLD_CODE)?; + self.write_str(self.palette.bold)?; } Url | Tip => { - self.write_str(UNDERLINE_CODE)?; + self.write_str(self.palette.underline)?; } PlainText => { self.write_str(self.palette.primary)?; @@ -929,7 +980,7 @@ where Emphasized | Url | TypeVariable | Alias | Symbol | BinOp | Error | GutterBar | Typo | TypoSuggestion | ParserSuggestion | Structure | CodeBlock | PlainText | LineNumber | Tip | Module | Header | Keyword => { - self.write_str(RESET_CODE)?; + self.write_str(self.palette.reset)?; } TypeBlock | GlobalTag | PrivateTag | Opaque | RecordField => { /* nothing yet */ } diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 8f72580fda..97e0cd6130 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -12,14 +12,14 @@ mod test_reporting { use crate::helpers::test_home; use crate::helpers::{can_expr, infer_expr, CanExprOut, ParseErrOut}; use bumpalo::Bump; + use indoc::indoc; use roc_module::symbol::{Interns, ModuleId}; use roc_mono::ir::{Procs, Stmt, UpdateModeIds}; use roc_mono::layout::LayoutCache; use roc_region::all::LineInfo; use roc_reporting::report::{ - can_problem, mono_problem, parse_problem, type_problem, Report, Severity, BLUE_CODE, - BOLD_CODE, CYAN_CODE, DEFAULT_PALETTE, GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, - UNDERLINE_CODE, WHITE_CODE, YELLOW_CODE, + can_problem, mono_problem, parse_problem, type_problem, Report, Severity, ANSI_STYLE_CODES, + DEFAULT_PALETTE, }; use roc_reporting::report::{RocDocAllocator, RocDocBuilder}; use roc_solve::solve; @@ -288,16 +288,16 @@ mod test_reporting { } fn human_readable(str: &str) -> String { - str.replace(RED_CODE, "") - .replace(WHITE_CODE, "") - .replace(BLUE_CODE, "") - .replace(YELLOW_CODE, "") - .replace(GREEN_CODE, "") - .replace(CYAN_CODE, "") - .replace(MAGENTA_CODE, "") - .replace(RESET_CODE, "") - .replace(BOLD_CODE, "") - .replace(UNDERLINE_CODE, "") + str.replace(ANSI_STYLE_CODES.red, "") + .replace(ANSI_STYLE_CODES.white, "") + .replace(ANSI_STYLE_CODES.blue, "") + .replace(ANSI_STYLE_CODES.yellow, "") + .replace(ANSI_STYLE_CODES.green, "") + .replace(ANSI_STYLE_CODES.cyan, "") + .replace(ANSI_STYLE_CODES.magenta, "") + .replace(ANSI_STYLE_CODES.reset, "") + .replace(ANSI_STYLE_CODES.bold, "") + .replace(ANSI_STYLE_CODES.underline, "") } #[test]