From 366980562702ec5ba976a7b67009c441434680f0 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Thu, 21 Jul 2016 10:55:28 -0700 Subject: [PATCH 1/5] Improve compatibility with 0-16 color terminals. Fish assumed that it could use tparm to emit escapes to set colors as long as the color was under 16 or max_colors from terminfo was 256:: if (idx < 16 || term256_support_is_native()) { // Use tparm to emit color escape writembs(tparm(todo, idx); If a terminal has max_colors = 8, here is what happenened, except inside fish: > env TERM=xterm tput setaf 7 | xxd 00000000: 1b5b 3337 6d .[37m > env TERM=xterm tput setaf 9 | xxd 00000000: 1b5b 3338 6d .[39m The first escape is good, that second escape is not valid. Bright colors should start at \e[90m: > env TERM=xterm-16color tput setaf 9 | xxd 00000000: 1b5b 3931 6d .[91m This is what caused "white" not to work in #3176 in Terminal.app, and obviously isn't good for real low-color terminals either. So we replace the term256_support_is_native(), which just checked if max_colors is 256 or not, with a function that takes an argument and checks terminfo for that to see if tparm can handle it. We only use this test, because otherwise, tparm should be expected to output garbage: /// Returns true if we think tparm can handle outputting a color index static bool term_supports_color_natively(unsigned int c) { return max_colors >= c; } ... if (term_supports_color_natively(idx) { And if terminfo can't do it, the "forced" escapes no longer use the fancy format when handling colors under 16, as this is not going to be compatible with low color terminals. The code before used: else { char buff[16] = ""; snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx); I added an intermediate format for colors 0-15: else { // We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. char buff[16] = ""; if (idx < 16) { snprintf(buff, sizeof buff, "\x1b[%dm", ((idx > 7) ? 82 : 30) + idx + !is_fg * 10); } else { snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx); } Restores harmony to white, brwhite, brblack, black color names. We don't want "white" to refer to color color #16, but to the standard color #8. #16 is "brwhite". Move comments from output.h to output.cpp Nuke the config.fish set_color hack for linux VTs. Sync up our various incomplete color lists and fix all color values. Colors 0-8 are assumed to be brights - e.g. red was FF0000. Perplexing! Using this table: Fixes #3176 --- .../functions/__fish_config_interactive.fish | 20 ----- share/tools/web_config/webconfig.py | 34 +++++---- src/color.cpp | 74 +++++++++++-------- src/output.cpp | 57 +++++++++++--- src/output.h | 55 +++----------- 5 files changed, 120 insertions(+), 120 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index e12c27c04..e26e6bc55 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -303,26 +303,6 @@ function __fish_config_interactive -d "Initializations that should be performed set -g fish_pager_color_description yellow set -g fish_pager_color_progress cyan - # Don't allow setting color other than what linux offers (see #2001) - functions -e set_color - function set_color --shadow-builtin - set -l term_colors black red green yellow blue magenta cyan white normal - for a in $argv - if not contains -- $a $term_colors - switch $a - # Also allow options - case "-*" - continue - case "*" - echo "Color not valid in TERM = linux: $a" - return 1 - end - end - end - builtin set_color $argv - return $status - end - # Set fish_prompt to a VT-friendly version # without color or unicode function fish_prompt diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 782751705..c020bea66 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -65,23 +65,27 @@ def escape_fish_cmd(text): named_colors = { 'black' : '000000', - 'red' : 'FF0000', - 'green' : '00FF00', + 'red' : '800000', + 'green' : '008000', 'brown' : '725000', - 'yellow' : 'FFFF00', - 'blue' : '0000FF', - 'magenta' : 'FF00FF', - 'purple' : 'FF00FF', - 'cyan' : '00FFFF', - 'grey' : 'E5E5E5', + 'yellow' : '808000', + 'blue' : '000080', + 'magenta' : '800080', + 'purple' : '800080', + 'cyan' : '008080', + 'grey' : 'e5e5e5', 'brgrey' : '555555', - 'brbrown' : 'FFFF55', - 'bryellow' : 'FFFF55', - 'brblue' : '5555FF', - 'brmagenta' : 'FF55FF', - 'brpurple' : 'FF55FF', - 'brcyan' : '55FFFF', - 'white' : 'FFFFFF' + 'white' : 'c0c0c0', + 'brblack' : '808080', + 'brred' : 'ff0000', + 'brgreen' : '00ff00', + 'brbrown' : 'ffff00', + 'bryellow' : 'ffff00', + 'brblue' : '0000ff', + 'brmagenta' : 'ff00ff', + 'brpurple' : 'ff00ff', + 'brcyan' : '00ffff', + 'brwhite' : 'ffffff' } def parse_one_color(comp): diff --git a/src/color.cpp b/src/color.cpp index 41f41acfe..7b6590481 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -149,29 +149,32 @@ struct named_color_t { const wchar_t *name; unsigned char idx; unsigned char rgb[3]; + bool hidden; }; static const named_color_t named_colors[] = { - {L"black", 0, {0, 0, 0}}, - {L"red", 1, {0xFF, 0, 0}}, - {L"green", 2, {0, 0xFF, 0}}, - {L"brown", 3, {0x72, 0x50, 0}}, - {L"yellow", 3, {0xFF, 0xFF, 0}}, - {L"blue", 4, {0, 0, 0xFF}}, - {L"magenta", 5, {0xFF, 0, 0xFF}}, - {L"purple", 5, {0xFF, 0, 0xFF}}, - {L"cyan", 6, {0, 0xFF, 0xFF}}, - {L"grey", 7, {0xE5, 0xE5, 0xE5}}, - {L"brgrey", 8, {0x55, 0x55, 0x55}}, - {L"brred", 9, {0xFF, 0x55, 0x55}}, - {L"brgreen", 10, {0x55, 0xFF, 0x55}}, - {L"brbrown", 11, {0xFF, 0xFF, 0x55}}, - {L"bryellow", 11, {0xFF, 0xFF, 0x55}}, - {L"brblue", 12, {0x55, 0x55, 0xFF}}, - {L"brmagenta", 13, {0xFF, 0x55, 0xFF}}, - {L"brpurple", 13, {0xFF, 0x55, 0xFF}}, - {L"brcyan", 14, {0x55, 0xFF, 0xFF}}, - {L"white", 15, {0xFF, 0xFF, 0xFF}}, + {L"black", 0, {0, 0, 0}, false}, + {L"red", 1, {0x80, 0, 0}, false}, + {L"green", 2, {0, 0x80, 0}, false}, + {L"brown", 3, {0x72, 0x50, 0}, true}, + {L"yellow", 3, {0x80, 0x80, 0}, false}, + {L"blue", 4, {0, 0, 0x80}, false}, + {L"magenta", 5, {0x80, 0, 0x80}, false}, + {L"purple", 5, {0x80, 0, 0x80}, true}, + {L"cyan", 6, {0, 0x80, 0x80}, false}, + {L"white", 7, {0xC0, 0xC0, 0xC0}, false}, + {L"grey", 7, {0xe5, 0xe5, 0xe5}, true}, + {L"brblack", 8, {0x80, 0x80, 0x80}, false}, + {L"brgrey", 8, {0055, 0x55, 0x55}, true}, + {L"brred", 9, {0xFF, 0x00, 0x00}, false}, + {L"brgreen", 10, {0x00, 0xFF, 0x00}, false}, + {L"brbrown", 11, {0xFF, 0xFF, 0x00}, true}, + {L"bryellow", 11, {0xFF, 0xFF, 0x00}, false}, + {L"brblue", 12, {0x00, 0, 0xFF}, false}, + {L"brmagenta", 13, {0xFF, 0, 0xFF}, false}, + {L"brpurple", 13, {0xFF, 0, 0xFF}, true}, + {L"brcyan", 14, {0x00, 0xFF, 0xFF}, false}, + {L"brwhite", 15, {0xFF, 0xFF, 0xFF}, false}, }; wcstring_list_t rgb_color_t::named_color_names(void) { @@ -179,7 +182,9 @@ wcstring_list_t rgb_color_t::named_color_names(void) { wcstring_list_t result; result.reserve(1 + count); for (size_t i = 0; i < count; i++) { - result.push_back(named_colors[i].name); + if (named_colors[i].hidden == false) { + result.push_back(named_colors[i].name); + } } // "normal" isn't really a color and does not have a color palette index or // RGB value. Therefore, it does not appear in the named_colors table. @@ -227,16 +232,24 @@ rgb_color_t rgb_color_t::white() { return rgb_color_t(type_named, 7); } rgb_color_t rgb_color_t::black() { return rgb_color_t(type_named, 0); } -static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) { +static unsigned char term16_color_for_rgb(const unsigned char rgb[3]) { const uint32_t kColors[] = { 0x000000, // Black - 0xFF0000, // Red - 0x00FF00, // Green - 0xFFFF00, // Yellow - 0x0000FF, // Blue - 0xFF00FF, // Magenta - 0x00FFFF, // Cyan - 0xFFFFFF, // White + 0x800000, // Red + 0x008000, // Green + 0x808000, // Yellow + 0x000080, // Blue + 0x800080, // Magenta + 0x008080, // Cyan + 0xc0c0c0, // White + 0x808080, // Bright Black + 0xFF0000, // Bright Red + 0x00FF00, // Bright Green + 0xFFFF00, // Bright Yellow + 0x0000FF, // Bright Blue + 0xFF00FF, // Bright Magenta + 0x00FFFF, // Bright Cyan + 0xFFFFFF // Bright White }; return convert_color(rgb, kColors, sizeof kColors / sizeof *kColors); } @@ -284,9 +297,10 @@ color24_t rgb_color_t::to_color24() const { } unsigned char rgb_color_t::to_name_index() const { + // XXX this should look for the nearest color assert(type == type_named || type == type_rgb); if (type == type_named) return data.name_idx; - if (type == type_rgb) return term8_color_for_rgb(data.color.rgb); + if (type == type_rgb) return term16_color_for_rgb(data.color.rgb); return (unsigned char)-1; // this is an error } diff --git a/src/output.cpp b/src/output.cpp index 85f1f1b9c..937e78114 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -37,15 +37,19 @@ static int (*out)(char c) = writeb_internal; /// Whether term256 and term24bit are supported. static color_support_t color_support = 0; +/// Set the function used for writing in move_cursor, writespace and set_color and all other output +/// functions in this library. By default, the write call is used to give completely unbuffered +/// output to stdout. void output_set_writer(int (*writer)(char)) { CHECK(writer, ); out = writer; } +/// Return the current output writer. int (*output_get_writer())(char) { return out; } -// Returns true if we think the term256 support is "native" as opposed to forced. -static bool term256_support_is_native(void) { return max_colors >= 256; } +/// Returns true if we think tparm can handle outputting a color index +static bool term_supports_color_natively(unsigned int c) { return max_colors >= c; } color_support_t output_get_color_support(void) { return color_support; } @@ -59,15 +63,18 @@ unsigned char index_for_color(rgb_color_t c) { } static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { - bool result = false; - if (idx < 16 || term256_support_is_native()) { + if (term_supports_color_natively(idx)) { // Use tparm to emit color escape. writembs(tparm(todo, idx)); - result = true; + return true; } else { // We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. - char buff[128] = ""; - snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx); + char buff[16] = ""; + if (idx < 16) { + snprintf(buff, sizeof buff, "\x1b[%dm", ((idx > 7) ? 82 : 30) + idx + !is_fg * 10); + } else { + snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx); + } int (*writer)(char) = output_get_writer(); if (writer) { @@ -76,9 +83,8 @@ static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { } } - result = true; + return true; } - return result; } static bool write_foreground_color(unsigned char idx) { @@ -99,6 +105,7 @@ static bool write_background_color(unsigned char idx) { return false; } +// Exported for builtin_set_color's usage only. void write_color(rgb_color_t color, bool is_fg) { bool supports_term24bit = !!(output_get_color_support() & color_support_term24bit); if (!supports_term24bit || !color.is_rgb()) { @@ -122,6 +129,27 @@ void write_color(rgb_color_t color, bool is_fg) { } } +/// Sets the fg and bg color. May be called as often as you like, since if the new color is the same +/// as the previous, nothing will be written. Negative values for set_color will also be ignored. +/// Since the terminfo string this function emits can potentially cause the screen to flicker, the +/// function takes care to write as little as possible. +/// +/// Possible values for color are any form the FISH_COLOR_* enum and FISH_COLOR_RESET. +/// FISH_COLOR_RESET will perform an exit_attribute_mode, even if set_color thinks it is already in +/// FISH_COLOR_NORMAL mode. +/// +/// In order to set the color to normal, three terminfo strings may have to be written. +/// +/// - First a string to set the color, such as set_a_foreground. This is needed because otherwise +/// the previous strings colors might be removed as well. +/// +/// - After that we write the exit_attribute_mode string to reset all color attributes. +/// +/// - Lastly we may need to write set_a_background or set_a_foreground to set the other half of the +/// color pair to what it should be. +/// +/// \param c Foreground color. +/// \param c2 Background color. void set_color(rgb_color_t c, rgb_color_t c2) { #if 0 wcstring tmp = c.description(); @@ -260,11 +288,15 @@ static int writeb_internal(char c) { // cppcheck return 0; } +/// This is for writing process notification messages. Has to write to stdout, so clr_eol and such +/// functions will work correctly. Not an issue since this function is only used in interactive mode +/// anyway. int writeb(tputs_arg_t b) { out(b); return 0; } +/// Write a wide character using the output method specified using output_set_writer(). int writech(wint_t ch) { char buff[MB_LEN_MAX + 1]; size_t len; @@ -294,6 +326,7 @@ int writech(wint_t ch) { return 0; } +/// Write a wide character string to FD 1. void writestr(const wchar_t *str) { CHECK(str, ); @@ -329,6 +362,8 @@ void writestr(const wchar_t *str) { if (buffer != static_buffer) delete[] buffer; } +/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns +/// rgb_color_t::none() if empty. rgb_color_t best_color(const std::vector &candidates, color_support_t support) { if (candidates.empty()) { return rgb_color_t::none(); @@ -358,7 +393,8 @@ rgb_color_t best_color(const std::vector &candidates, color_support return result; } -// This code should be refactored to enable sharing with builtin_set_color. +/// Return the internal color code representing the specified color. +/// XXX This code should be refactored to enable sharing with builtin_set_color. rgb_color_t parse_color(const wcstring &val, bool is_background) { int is_bold = 0; int is_underline = 0; @@ -408,6 +444,7 @@ rgb_color_t parse_color(const wcstring &val, bool is_background) { return result; } +/// Write specified multibyte string. void writembs_check(char *mbs, const char *mbs_name, const char *file, long line) { if (mbs != NULL) { tputs(mbs, 1, &writeb); diff --git a/src/output.h b/src/output.h index a7f87fab4..71f94de80 100644 --- a/src/output.h +++ b/src/output.h @@ -14,65 +14,33 @@ /// Constants for various colors as used by the set_color function. enum { - FISH_COLOR_BLACK, - FISH_COLOR_RED, - FISH_COLOR_GREEN, - FISH_COLOR_YELLOW, - FISH_COLOR_BLUE, - FISH_COLOR_MAGENTA, - FISH_COLOR_CYAN, - FISH_COLOR_WHITE, - FISH_COLOR_NORMAL, // the default fg color of the terminal - FISH_COLOR_RESET + FISH_COLOR_BLACK, // 0 + FISH_COLOR_RED, // 1 + FISH_COLOR_GREEN, // 2 + FISH_COLOR_YELLOW, // 3 + FISH_COLOR_BLUE, // 4 + FISH_COLOR_MAGENTA, // 5 + FISH_COLOR_CYAN, // 6 + FISH_COLOR_WHITE, // 7 + FISH_COLOR_NORMAL, // 8 terminal default + FISH_COLOR_RESET // 9 }; -/// Sets the fg and bg color. May be called as often as you like, since if the new color is the same -/// as the previous, nothing will be written. Negative values for set_color will also be ignored. -/// Since the terminfo string this function emits can potentially cause the screen to flicker, the -/// function takes care to write as little as possible. -/// -/// Possible values for color are any form the FISH_COLOR_* enum and FISH_COLOR_RESET. -/// FISH_COLOR_RESET will perform an exit_attribute_mode, even if set_color thinks it is already in -/// FISH_COLOR_NORMAL mode. -/// -/// In order to set the color to normal, three terminfo strings may have to be written. -/// -/// - First a string to set the color, such as set_a_foreground. This is needed because otherwise -/// the previous strings colors might be removed as well. -/// -/// - After that we write the exit_attribute_mode string to reset all color attributes. -/// -/// - Lastly we may need to write set_a_background or set_a_foreground to set the other half of the -/// color pair to what it should be. -/// -/// \param c Foreground color. -/// \param c2 Background color. void set_color(rgb_color_t c, rgb_color_t c2); -/// Write specified multibyte string. void writembs_check(char *mbs, const char *mbs_name, const char *file, long line); #define writembs(mbs) writembs_check((mbs), #mbs, __FILE__, __LINE__) -/// Write a wide character using the output method specified using output_set_writer(). int writech(wint_t ch); -/// Write a wide character string to FD 1. void writestr(const wchar_t *str); -/// Return the internal color code representing the specified color. rgb_color_t parse_color(const wcstring &val, bool is_background); -/// This is for writing process notification messages. Has to write to stdout, so clr_eol and such -/// functions will work correctly. Not an issue since this function is only used in interactive mode -/// anyway. int writeb(tputs_arg_t b); -/// Set the function used for writing in move_cursor, writespace and set_color and all other output -/// functions in this library. By default, the write call is used to give completely unbuffered -/// output to stdout. void output_set_writer(int (*writer)(char)); -/// Return the current output writer. int (*output_get_writer())(char); /// Sets what colors are supported. @@ -81,11 +49,8 @@ typedef unsigned int color_support_t; color_support_t output_get_color_support(); void output_set_color_support(color_support_t support); -/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns -/// rgb_color_t::none() if empty. rgb_color_t best_color(const std::vector &colors, color_support_t support); -// Exported for builtin_set_color's usage only. void write_color(rgb_color_t color, bool is_fg); unsigned char index_for_color(rgb_color_t c); From 644ea82c2ff9232424a448f87c1f8fb9197231cf Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 23 Jul 2016 00:28:24 -0700 Subject: [PATCH 2/5] Update set_color documentation Update docs for "brblack", "brwhite" existing. We no longer mention colors like grey, brown and purple, which are aliases for yellow, magenta, white/black. The color names still work but there isn't a good argument for there being two ways to do that: especially in the age of 24-bit terminals where one might expect yellow and brown or magenta and purple to actually be different colors. Copyedit rest of document for inaccuracies, strange advice, brevity (a lot of "you" pronouns, for example.) Document the color fallback feature (set_color 313554 blue) that's been present quite a while. --- doc_src/set_color.txt | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/doc_src/set_color.txt b/doc_src/set_color.txt index 11885b97d..d475c49c8 100644 --- a/doc_src/set_color.txt +++ b/doc_src/set_color.txt @@ -2,36 +2,35 @@ \subsection set_color-synopsis Synopsis \fish{synopsis} -set_color [OPTIONS] [COLOR] +set_color [OPTIONS] VALUE \endfish \subsection set_color-description Description -`set_color` changes the foreground and/or background color of the terminal. `COLOR` is one of `black`, `red`, `green`, `brown`, `yellow`, `blue`, `magenta`, `purple`, `cyan`, `brred`, `brgreen`, `brbrown`, `bryellow`, `brblue`, `brmagenta`, `brpurple`, `brcyan`, `white`. The `br`, bright, forms are most useful as background colors. The special color `normal` resets the background and foreground to whatever is normal for your terminal. +`set_color` is used to control the color and styling of text in the terminal. `VALUE` corresponds to a reserved color name such as *red* or a RGB color value given as 3 or 6 hexadecimal digits. The *br*-, as in 'bright', forms are full-brightness variants of the 8 standard-brightness colors on many terminals. *brblack* has higher brightness than *black* - towards gray. A special keyword *normal* resets text formatting to terminal defaults. -You can also specify an RGB value with three or six hex digits, such as A0FF33 or f2f. `fish` will choose the closest supported color. A three digit value is equivalent to specifying each digit twice; e.g., `#2BC` is the same as `#22BBCC`. Hex RGB values can be in lower or uppercase, optionally prefixed with the pound-sign character. Depending on the capabilities of your terminal the actual color may be approximated by the closest known matching color in the [ANSI X3.64](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) color palette. +Valid colors include: + + - *black*, *red*, *green*, *yellow*, *blue*, *magenta*, *cyan*, *white* + - *brblack*, *brred*, *brgreen*, *bryellow*, *brblue*, *brmagenta*, *brcyan*, *brwhite* + +An RGB value with three or six hex digits, such as A0FF33 or f2f can be used. `fish` will choose the closest supported color. A three digit value is equivalent to specifying each digit twice; e.g., `set_color 2BC` is the same as `set_color 22BBCC`. Hexadecimal RGB values can be in lower or uppercase. Depending on the capabilities of your terminal (and the level of support `set_color` has for it) the actual color may be approximated by a nearby matching reserved color name or `set_color` may not have an effect on color. A second color may be given as a desired fallback color. e.g. `set_color 124212` *brblue* will instruct set_color to use *brblue* if a terminal is not capable of the exact shade of grey desired. This is very useful when an 8 or 16 color terminal might otherwise not use a color. The following options are available: -- `-b`, `--background` `COLOR` sets the background color. - -- `-c`, `--print-colors` prints a list of all valid color names. - -- `-o`, `--bold` sets bold or extra bright mode. - +- `-b`, `--background` *COLOR* sets the background color. +- `-c`, `--print-colors` prints a list of the 16 named colors. +- `-o`, `--bold` sets bold mode. - `-u`, `--underline` sets underlined mode. -Calling `set_color normal` will set the terminal background and foreground colors to the defaults for the terminal. +Using the *normal* keyword will reset foreground, background, and all formatting back to default. -Some terminals use the `--bold` escape sequence to switch to a brighter color set rather than bolding the characters. This only applies to the foreground color. You should probably use the `br` color name variants listed above for both the foreground and background "bright" colors rather than use this option. The only use for this option is on a black&white terminal (e.g., a DEC VT220) to select foreground black text that is bolder than the normal text. +\subsection set_color-notes Notes -Not all terminal emulators support all these features. - -Note 1: Setting either color to "normal" will reset both background and foreground colors to whatever is the default for the terminal. - -Note 2: Setting the background color only affects subsequently written characters. Fish provides no way to set the background color for the entire terminal window. Configuring the window background color (and other attributes such as its opacity) has to be done using whatever mechanisms the terminal provides. - -Note 3: `set_color` works by printing sequences of characters to its stdout. If you use it in a command substitution or pipe, these characters will also be captured (and can then later be printed to the terminal). To avoid printing these sequences (and hence not add color) if not connected to a terminal, use `isatty stdout`. +1. Using the *normal* keyword will reset both background and foreground colors to whatever is the default for the terminal. +2. Setting the background color only affects subsequently written characters. Fish provides no way to set the background color for the entire terminal window. Configuring the window background color (and other attributes such as its opacity) has to be done using whatever mechanisms the terminal provides. +3. Some terminals use the `--bold` escape sequence to switch to a brighter color set rather than increasing the weight of text. +4. `set_color` works by printing sequences of characters to *stdout*. If used in command substitution or a pipe, these characters will also be captured. This may or may not be desirable. Checking the exit code of `isatty stdout` before using `set_color` can be useful to decide not to colorize output in a script. \subsection set_color-example Examples @@ -44,8 +43,10 @@ set_color normal; echo "Normal is nice" # This will reset background, too \subsection set_color-detection Terminal Capability Detection -Fish uses a heuristic to decide if your terminal supports the 256 color palette (as opposed to the more limited 16 color palette of older terminals). If you've done the equivalent of `set fish_term256 1` that will be true. If the $TERM value contains "256color" (e.g., "xterm-256color") that will be true. If your $TERM value is "xterm" and $TERM_PROGRAM is not set to "Apple_Terminal" that will be true. If your terminal supports the full 256 color palette (which is pretty much every color terminal emulator written in the past decade) you should ensure one of the aforementioned conditions is true. +Fish uses a heuristic to decide if a terminal supports the 256-color palette as opposed to the more limited 16 color palette of older terminals. Support can be forced on by setting `fish_term256` to *1*. If `$TERM` contains "256color" (e.g., *xterm-256color*), 256-color support is enabled. If `$TERM` contains *xterm*, 256 color support is enabled (except for MacOS: `$TERM_PROGRAM` and `$TERM_PROGRAM_VERSION` are used to detect Terminal.app from MacOS 10.6; support is disabled here it because it is known that it reports `xterm` and only supports 16 colors. -Many terminals support 24-bit (i.e., true-color) color escape sequences. This includes modern xterms, Gnome Terminal, KDE Konsole, and iTerm2. Fish currently does some limited attempts to detect whether a given `$TERM` supports 24-bit colors. You can explicitly enable that support via `set fish_term24bit 1`. If you do so fish will not map your RGB color values to the closest known matching color in the ANSI X3.64 color palette. +If terminfo reports 256 color support for a terminal, support will always be enabled. To debug color palette problems, `tput colors` may be useful to see the number of colors in terminfo for a terminal. Fish launched as `fish -d2` will include diagnostic messages that indicate the color support mode in use. -The `set_color` command uses the terminfo database to look up how to change terminal colors on whatever terminal is in use. Some systems have old and incomplete terminfo databases, and may lack color information for terminals that support it. Fish will use the [ANSI X3.64](https://en.wikipedia.org/wiki/ANSI_escape_code) escape sequences if the terminfo definition says less than 256 colors are supported; otherwise it will use the terminfo definition. +Many terminals support 24-bit (i.e., true-color) color escape sequences. This includes modern xterm, Gnome Terminal, Konsole, and iTerm2. Fish attempts to detect such terminals through various means in `config.fish` You can explicitly force that support via `set fish_term24bit 1`. + +The `set_color` command uses the terminfo database to look up how to change terminal colors on whatever terminal is in use. Some systems have old and incomplete terminfo databases, and may lack color information for terminals that support it. Fish will assume that all terminals can use the [ANSI X3.64](https://en.wikipedia.org/wiki/ANSI_escape_code) escape sequences if the terminfo definition indicates a color below 16 is not supported. From 5afd939f3e7558c91404e8c563c53351b71cfb43 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 23 Jul 2016 13:40:01 -0700 Subject: [PATCH 3/5] Stop swallowing the cartesian product This should work: > env TERM=vt100 ./fish -c 'echo (set_color red)"hi"' We do a ::reset() if setting the color doesn't happen. Fixes #2951 --- src/builtin_set_color.cpp | 7 ++++++- src/output.cpp | 5 +++-- src/output.h | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index e6679562f..35917a63d 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -179,7 +179,12 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { write_color(rgb_color_t::black(), true /* is_fg */); writembs(tparm(exit_attribute_mode)); } else { - write_color(fg, true /* is_fg */); + if (!write_color(fg, true /* is_fg */)) { + // We need to do *something* or the lack of any output messes up + // when the cartesian product here would make "foo" disappear: + // $ echo (set_color foo)bar + set_color(rgb_color_t::reset(), rgb_color_t::none()); + } } } diff --git a/src/output.cpp b/src/output.cpp index 937e78114..4905c5a50 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -106,12 +106,12 @@ static bool write_background_color(unsigned char idx) { } // Exported for builtin_set_color's usage only. -void write_color(rgb_color_t color, bool is_fg) { +bool write_color(rgb_color_t color, bool is_fg) { bool supports_term24bit = !!(output_get_color_support() & color_support_term24bit); if (!supports_term24bit || !color.is_rgb()) { // Indexed or non-24 bit color. unsigned char idx = index_for_color(color); - (is_fg ? write_foreground_color : write_background_color)(idx); + return (is_fg ? write_foreground_color : write_background_color)(idx); } else { // 24 bit! No tparm here, just ANSI escape sequences. // Foreground: ^[38;2;;;m @@ -127,6 +127,7 @@ void write_color(rgb_color_t color, bool is_fg) { } } } + return true; } /// Sets the fg and bg color. May be called as often as you like, since if the new color is the same diff --git a/src/output.h b/src/output.h index 71f94de80..6ca31d92c 100644 --- a/src/output.h +++ b/src/output.h @@ -51,7 +51,7 @@ void output_set_color_support(color_support_t support); rgb_color_t best_color(const std::vector &colors, color_support_t support); -void write_color(rgb_color_t color, bool is_fg); +bool write_color(rgb_color_t color, bool is_fg); unsigned char index_for_color(rgb_color_t c); #endif From 82b298dcc53feca574b7317f4829f06413e20625 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 10 Sep 2016 15:07:58 -0700 Subject: [PATCH 4/5] __fish_config_interactive: remove Linux exception And update colors. --- .../functions/__fish_config_interactive.fish | 79 +++++-------------- 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index e26e6bc55..1be7cd465 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -37,7 +37,7 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_normal or set -U fish_color_normal normal set -q fish_color_command - or set -U fish_color_command brblue + or set -U fish_color_command --bold set -q fish_color_param or set -U fish_color_param cyan set -q fish_color_redirection @@ -45,19 +45,19 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_comment or set -U fish_color_comment red set -q fish_color_error - or set -U fish_color_error red --bold + or set -U fish_color_error brred set -q fish_color_escape - or set -U fish_color_escape cyan + or set -U fish_color_escape bryellow set -q fish_color_operator or set -U fish_color_operator cyan set -q fish_color_end or set -U fish_color_end green set -q fish_color_quote - or set -U fish_color_quote brown + or set -U fish_color_quote yellow set -q fish_color_autosuggestion - or set -U fish_color_autosuggestion brgrey + or set -U fish_color_autosuggestion 222 brblack set -q fish_color_user - or set -U fish_color_user green + or set -U fish_color_user brgreen set -q fish_color_host or set -U fish_color_host normal @@ -71,23 +71,23 @@ function __fish_config_interactive -d "Initializations that should be performed # Background color for matching quotes and parenthesis set -q fish_color_match - or set -U fish_color_match cyan + or set -U fish_color_match brwhite # Background color for search matches set -q fish_color_search_match - or set -U fish_color_search_match --background=purple + or set -U fish_color_search_match --background=magenta # Background color for selections set -q fish_color_selection - or set -U fish_color_selection --background=purple + or set -U fish_color_selection --background=magenta # Pager colors set -q fish_pager_color_prefix - or set -U fish_pager_color_prefix cyan + or set -U fish_pager_color_prefix brcyan set -q fish_pager_color_completion or set -U fish_pager_color_completion normal set -q fish_pager_color_description - or set -U fish_pager_color_description brgrey + or set -U fish_pager_color_description brblack set -q fish_pager_color_progress or set -U fish_pager_color_progress cyan @@ -146,11 +146,13 @@ function __fish_config_interactive -d "Initializations that should be performed # Completions for SysV startup scripts. These aren't bound to any # specific command, so they can't be autoloaded. # - complete -x -p "/etc/init.d/*" -a start --description 'Start service' - complete -x -p "/etc/init.d/*" -a stop --description 'Stop service' - complete -x -p "/etc/init.d/*" -a status --description 'Print service status' - complete -x -p "/etc/init.d/*" -a restart --description 'Stop and then start service' - complete -x -p "/etc/init.d/*" -a reload --description 'Reload service configuration' + if test -d /etc/init.d + complete -x -p "/etc/init.d/*" -a start --description 'Start service' + complete -x -p "/etc/init.d/*" -a stop --description 'Stop service' + complete -x -p "/etc/init.d/*" -a status --description 'Print service status' + complete -x -p "/etc/init.d/*" -a restart --description 'Stop and then start service' + complete -x -p "/etc/init.d/*" -a reload --description 'Reload service configuration' + end # Make sure some key bindings are set if not set -q fish_key_bindings @@ -264,49 +266,4 @@ function __fish_config_interactive -d "Initializations that should be performed end end end - - if test $TERM = "linux" # A linux in-kernel VT with 8 colors and 256/512 glyphs - # In a VT we have - # black (invisible) - # red - # green - # yellow - # blue - # magenta - # cyan - # white - - # Pretty much just set at random - set -g fish_color_normal normal - set -g fish_color_command yellow - set -g fish_color_param cyan - set -g fish_color_redirection normal - set -g fish_color_comment red - set -g fish_color_error red - set -g fish_color_escape cyan - set -g fish_color_operator cyan - set -g fish_color_quote blue - set -g fish_color_autosuggestion yellow - set -g fish_color_valid_path - set -g fish_color_cwd green - set -g fish_color_cwd_root red - set -g fish_color_match cyan - set -g fish_color_history_current cyan - set -g fish_color_search_match cyan - set -g fish_color_selection blue - set -g fish_color_end yellow - set -g fish_color_host normal - set -g fish_color_status red - set -g fish_color_user green - set -g fish_pager_color_prefix cyan - set -g fish_pager_color_completion normal - set -g fish_pager_color_description yellow - set -g fish_pager_color_progress cyan - - # Set fish_prompt to a VT-friendly version - # without color or unicode - function fish_prompt - fish_fallback_prompt - end - end end From 90e535f66f27e2ef39f8522840abb74db12d2f17 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 10 Sep 2016 20:28:06 -0700 Subject: [PATCH 5/5] Update pager colors, tweak pager.cpp Adds a color reset thing, to ensure fish tries to use hard colors during testing. Also, work on a discrepancy (not introduced by my changes, afaik) when with some combinations of color settings, and usage of --bold, caused super flakey color paninting in the pager. Downwards movements that trigger scrolling vs. upwards movement in the pager would only apply bold to selections when moving upwards. The bold state of the command completions in the pager was flipping flops on and off, depending on if there is a description on the preceding line. Implement a lame fix by reseting the color to normal and applying a different style on the rightmost ')' which seems to be what was influencing it. Makes fish use terminfo for coloring the newline glich char. --- share/config.fish | 4 +- .../functions/__fish_config_interactive.fish | 64 +++++++++++-------- src/pager.cpp | 5 +- src/screen.cpp | 32 +++++----- 4 files changed, 61 insertions(+), 44 deletions(-) diff --git a/share/config.fish b/share/config.fish index 902719378..4dc24903e 100644 --- a/share/config.fish +++ b/share/config.fish @@ -1,8 +1,6 @@ # Main file for fish command completions. This file contains various # common helper functions for the command completions. All actual # completions are located in the completions subdirectory. -# - # # Set default field separators # @@ -15,6 +13,8 @@ function __fish_default_command_not_found_handler echo "fish: Unknown command '$argv'" >&2 end +set -g version $FISH_VERSION + if status --is-interactive # The user has seemingly explicitly launched an old fish with too-new scripts installed. if not contains "string" (builtin -n) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 54846dc39..fce49da2d 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -22,16 +22,28 @@ function __fish_config_interactive -d "Initializations that should be performed set userdatadir $XDG_DATA_HOME end + if not set -q fish_greeting + set -l line1 (printf (_ 'Welcome to fish, the friendly interactive shell' )) + if not set -q __fish_init_2_3_0 + set -l line2 \n(printf (_ 'Type %shelp%s for instructions on how to use fish %s') (set_color green) (set_color normal)) + else + set -l line2 '' + end + set -U fish_greeting $line1$line2 + end + # # If we are starting up for the first time, set various defaults - # - if not set -q __fish_init_1_50_0 - if not set -q fish_greeting - set -l line1 (printf (_ 'Welcome to fish, the friendly interactive shell') ) - set -l line2 (printf (_ 'Type %shelp%s for instructions on how to use fish') (set_color green) (set_color normal)) - set -U fish_greeting $line1\n$line2 + # + if not set -q __fish_init_2_39_8 # bump this to 2_4_0 when rolling release if anything changes after 9/10/2016 + set -g colors_backup "$HOME/fish_previous_colors-(date).txt" + + echo Backing up uvars to:\n (set_color --underline)$colors_backup(set_color normal) + set -U >>$colors_backup + for option in (set -Un | string match "fish*color_*") + set -eU $option end - set -U __fish_init_1_50_0 + echo \"Normalized\" colors on upgrade. # Regular syntax highlighting colors set -q fish_color_normal @@ -41,7 +53,7 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_param or set -U fish_color_param cyan set -q fish_color_redirection - or set -U fish_color_redirection normal + or set -U fish_color_redirection brblue set -q fish_color_comment or set -U fish_color_comment red set -q fish_color_error @@ -49,13 +61,13 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_escape or set -U fish_color_escape bryellow set -q fish_color_operator - or set -U fish_color_operator cyan + or set -U fish_color_operator bryellow set -q fish_color_end - or set -U fish_color_end green + or set -U fish_color_end brmagenta set -q fish_color_quote or set -U fish_color_quote yellow set -q fish_color_autosuggestion - or set -U fish_color_autosuggestion 222 brblack + or set -U fish_color_autosuggestion 555 brblack set -q fish_color_user or set -U fish_color_user brgreen @@ -71,31 +83,34 @@ function __fish_config_interactive -d "Initializations that should be performed # Background color for matching quotes and parenthesis set -q fish_color_match - or set -U fish_color_match brwhite + or set -U fish_color_match --background=blue # Background color for search matches set -q fish_color_search_match - or set -U fish_color_search_match --background=magenta + or set -U fish_color_search_match bryellow --background=brgrey # Background color for selections set -q fish_color_selection - or set -U fish_color_selection --background=magenta + or set -U fish_color_selection white --bold --background=brgrey # Pager colors set -q fish_pager_color_prefix - or set -U fish_pager_color_prefix brcyan - set -q fish_pager_color_completion - or set -U fish_pager_color_completion normal + or set -U fish_pager_color_prefix white --bold --underline + #set -q fish_pager_color_completion + #or set -U fish_pager_color_completion set -q fish_pager_color_description - or set -U fish_pager_color_description brblack + or set -U fish_pager_color_description B3A06D yellow set -q fish_pager_color_progress - or set -U fish_pager_color_progress cyan + or set -U fish_pager_color_progress brwhite --background=cyan # # Directory history colors # set -q fish_color_history_current - or set -U fish_color_history_current cyan + or set -U fish_color_history_current --bold + + + set -U __fish_init_2_39_8 end # @@ -210,15 +225,14 @@ function __fish_config_interactive -d "Initializations that should be performed commandline -f repaint end - - # Notify vte-based terminals when $PWD changes (issue #906) + # Notify terminals when $PWD changes (issue #906) if test "$VTE_VERSION" -ge 3405 -o "$TERM_PROGRAM" = "Apple_Terminal" - function __update_vte_cwd --on-variable PWD --description 'Notify VTE of change to $PWD' + function __update_cwd_osc --on-variable PWD --description 'Notify VTE of change to $PWD' status --is-command-substitution and return - printf '\033]7;file://%s%s\a' (hostname) (pwd | __fish_urlencode) + printf \e\]7\;file://\%s\%s\a (hostname) (pwd | __fish_urlencode) end - __update_vte_cwd # Run once because we might have already inherited a PWD from an old tab + __update_cwd_osc # Run once because we might have already inherited a PWD from an old tab end ### Command-not-found handlers diff --git a/src/pager.cpp b/src/pager.cpp index 90e9aa8f4..edbddb22b 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -130,9 +130,10 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s { written += print_max(L" ", packed_color, 1, false, &line_data); } - print_max(L"(", packed_color, 1, false, &line_data); + // hack - this just works around the issue + print_max(L"(", highlight_spec_pager_completion | highlight_make_background(bg_color), 1, false, &line_data); print_max(c->desc, packed_color, desc_width, false, &line_data); - print_max(L")", packed_color, 1, false, &line_data); + print_max(L")", highlight_spec_pager_completion | highlight_make_background(bg_color), 1, false, &line_data); } else { while (written < width) { written += print_max(L" ", 0, 1, false, &line_data); diff --git a/src/screen.cpp b/src/screen.cpp index c4159e36a..a74047835 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -205,8 +205,8 @@ size_t escape_code_length(const wchar_t *code) { bool found = false; if (cur_term != NULL) { - // Detect these terminfo color escapes with parameter value 0..7, all of which don't move - // the cursor. + // Detect these terminfo color escapes with parameter value 0..16, all of which don't move + // the cursor. char *const esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, }; @@ -232,12 +232,15 @@ size_t escape_code_length(const wchar_t *code) { enter_superscript_mode, exit_superscript_mode, enter_blink_mode, enter_italics_mode, exit_italics_mode, enter_reverse_mode, enter_shadow_mode, exit_shadow_mode, enter_standout_mode, - exit_standout_mode, enter_secure_mode}; + exit_standout_mode, enter_secure_mode, enter_dim_mode, + enter_blink_mode, enter_protected_mode, enter_alt_charset_mode, + exit_alt_charset_mode}; for (size_t p = 0; p < sizeof esc2 / sizeof *esc2 && !found; p++) { if (!esc2[p]) continue; // Test both padded and unpadded version, just to be safe. Most versions of tparm don't // actually seem to do anything these days. + size_t len = maxi(try_sequence(tparm(esc2[p]), code), try_sequence(esc2[p], code)); if (len) { resulting_length = len; @@ -601,9 +604,8 @@ static bool perform_any_impending_soft_wrap(screen_t *scr, int x, int y) { /// Make sure we don't soft wrap. static void invalidate_soft_wrap(screen_t *scr) { scr->soft_wrap_location = INVALID_LOCATION; } -// Various code for testing term behavior. - #if 0 +/// Various code for testing term behavior. static bool test_stuff(screen_t *scr) { data_buffer_t output; @@ -1186,18 +1188,18 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) { // omitted_newline_char in common.cpp. int non_space_width = fish_wcwidth(omitted_newline_char); if (screen_width >= non_space_width) { - bool has_256_colors = output_get_color_support() & color_support_term256; - if (has_256_colors) { - // Draw the string in term256 gray. - abandon_line_string.append(L"\x1b[38;5;245m"); - } else { - // Draw in "bright black" (gray). - abandon_line_string.append( - L"\x1b[0m" // bright - L"\x1b[30;1m"); // black + if (enter_dim_mode) { + // Use dim if they have it, so the color will be based on their actual normal color and the background of the termianl. + abandon_line_string.append(str2wcstring(tparm(enter_dim_mode))); } + else if (set_a_foreground && max_colors >= 8) { + // Draw the string in gray. + abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 8))); + } + abandon_line_string.push_back(omitted_newline_char); - abandon_line_string.append(L"\x1b[0m"); // normal text ANSI escape sequence + if (exit_attribute_mode) + abandon_line_string.append(str2wcstring(tparm(exit_attribute_mode))); // normal text ANSI escape sequence abandon_line_string.append(screen_width - non_space_width, L' '); } abandon_line_string.push_back(L'\r');