diff --git a/vlib/term/colors.v b/vlib/term/colors.v index 8a771376cc..7dce2f490b 100644 --- a/vlib/term/colors.v +++ b/vlib/term/colors.v @@ -3,6 +3,8 @@ // that can be found in the LICENSE file. module term +import strings + // format_esc produces an ANSI escape code, for selecting the graphics rendition of the following // text. Each of the attributes that can be passed in `code`, separated by `;`, will be in effect, // until the terminal encounters another SGR ANSI escape code. For more details about the different @@ -275,3 +277,77 @@ pub fn bright_bg_white(msg string) string { pub fn highlight_command(command string) string { return bright_white(bg_cyan(' ${command} ')) } + +pub enum TextStyle { + bold = 1 + dim = 2 + italic = 3 + underline = 4 + blink = 5 + reverse = 7 +} + +pub enum FgColor { + black = 30 + red = 31 + green = 32 + yellow = 33 + blue = 34 + magenta = 35 + cyan = 36 + white = 37 +} + +pub enum BgColor { + black = 40 + red = 41 + green = 42 + yellow = 43 + blue = 44 + magenta = 45 + cyan = 46 + white = 47 +} + +@[params] +pub struct ColorConfig { +pub mut: + styles []TextStyle + fg ?FgColor + bg ?BgColor + custom string +} + +// write_color appends the ANSI colorful string `s` to the buffer. +pub fn write_color(mut b strings.Builder, s string, config ColorConfig) { + mut codes := []string{cap: 3} + + for style in config.styles { + codes << int(style).str() + } + + if fg := config.fg { + codes << int(fg).str() + } + + if bg := config.bg { + codes << int(bg).str() + } + + if config.custom != '' { + codes << config.custom + } + + if codes.len > 0 { + code_str := codes.join(';') + b.write_string('\x1b[${code_str}m${s}\x1b[0m') + } else { + b.write_string(s) + } +} + +// writeln_color appends the ANSI colorful string `s`, and then a newline character. +pub fn writeln_color(mut b strings.Builder, s string, color ColorConfig) { + write_color(mut b, s, color) + b.writeln('') +} diff --git a/vlib/term/term_test.v b/vlib/term/term_test.v index 1338229558..0b43a9adc2 100644 --- a/vlib/term/term_test.v +++ b/vlib/term/term_test.v @@ -1,5 +1,6 @@ import os import term +import strings fn test_get_terminal_size() { cols, _ := term.get_terminal_size() @@ -113,14 +114,32 @@ fn test_set_tab_title() { } fn test_strip_ansi() { - strings := [ + string_list := [ 'abc', term.bold('abc'), term.yellow('abc'), term.bold(term.red('abc')), term.strikethrough(term.inverse(term.dim(term.bold(term.bright_bg_blue('abc'))))), ] - for s in strings { + for s in string_list { assert term.strip_ansi(s) == 'abc' } } + +fn test_write_color() { + mut sb := strings.new_builder(100) + term.writeln_color(mut sb, 'hello') + term.writeln_color(mut sb, 'hello', fg: .red) + term.writeln_color(mut sb, 'hello', bg: .cyan) + term.writeln_color(mut sb, 'hello', styles: [.bold, .italic, .underline], fg: .red, bg: .cyan) + term.writeln_color(mut sb, 'hello', custom: '38;5;214') + + output := sb.str() + println(output) + + assert output.contains('\x1b[0m') + assert output.contains('\x1b[31m') + assert output.contains('\x1b[46m') + assert output.contains('\x1b[1;3;4;31;46m') + assert output.contains('\x1b[38;5;214m') +}