{"thread":{"id":"55728","subject":"[PATCH v3] help: colorize man pages","startedAt":"2021-05-18T20:48:21Z","lastAt":"2021-05-18T20:48:21Z","messageCount":1,"participants":["Felipe Contreras"],"isPatch":true,"patchVersion":3,"patchTotal":null},"messages":[{"id":"424901","messageId":"20210518204751.13836-1-felipe.contreras@gmail.com","threadId":"55728","inReplyTo":null,"subject":"[PATCH v3] help: colorize man pages","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2021-05-18T20:47:51Z","receivedAt":"2021-05-18T20:48:21Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Our man pages don't contain many useful colors (just blue links),\nmoreover, many people have groff SGR disabled, so they don't see any\ncolors with man pages.\n\nWe can set LESS_TERMCAP variables to render bold and underlined text\nwith colors in the pager; a common trick[1].\n\nBold is rendered as red, underlined as blue, and standout (messages and\nhighlighted search) as inverse magenta.\n\nThis only works when the pager is less.\n\nIf the user already has LESS_TERMCAP variables set in his/her\nenvironment, those are respected and not overwritten.\n\nA new color configuration is added: `color.man` for the people that want\nto turn this feature off, otherwise `color.ui` is respected, and in\naddition color.pager needs to be turned on.\n\nNormally check_auto_color() would check the value of `color.pager`, but\nin this particular case it's not git the one executing the pager, but\nman. Therefore we need to check pager_use_color ourselves.\n\nAlso, unlike other color.* configurations, color.man=always does not\nmake any sense; git help is always run for a tty (it would be very\nstrange for a user to do `git help $page > output`, but in fact, that\nworks automatically [probably thanks to less being smart], we don't even\nneed to check if stdout is a tty, but just to be consistent we do). So\nit's simply a boolean in our case.\n\nSo in order for this to have an effect:\n\n 1. The user must use less\n 2. Not have the same LESS_TERMCAP variables set\n 3. Have color.ui enabled\n 4. Have color.pager enabled\n 5. Not have color.man disabled\n 6. Run git with stdout on a tty\n\nOtherwise the current behavior remains.\n\n[1] https://unix.stackexchange.com/questions/119/colors-in-man-pages/147\n\nSuggested-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nComments-by: Jeff King <peff@peff.net>\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\nRange-diff against v2:\n1:  9e34a49fa9 ! 1:  db93bf432b help: colorize man pages\n    @@ Commit message\n         If the user already has LESS_TERMCAP variables set in his/her\n         environment, those are respected and not overwritten.\n     \n    -    A new color configuration is added: `color.man`, otherwise `color.ui` is\n    -    respected, and in addition color.pager needs to turned on.\n    +    A new color configuration is added: `color.man` for the people that want\n    +    to turn this feature off, otherwise `color.ui` is respected, and in\n    +    addition color.pager needs to be turned on.\n     \n         Normally check_auto_color() would check the value of `color.pager`, but\n         in this particular case it's not git the one executing the pager, but\n         man. Therefore we need to check pager_use_color ourselves.\n     \n    +    Also, unlike other color.* configurations, color.man=always does not\n    +    make any sense; git help is always run for a tty (it would be very\n    +    strange for a user to do `git help $page > output`, but in fact, that\n    +    works automatically [probably thanks to less being smart], we don't even\n    +    need to check if stdout is a tty, but just to be consistent we do). So\n    +    it's simply a boolean in our case.\n    +\n    +    So in order for this to have an effect:\n    +\n    +     1. The user must use less\n    +     2. Not have the same LESS_TERMCAP variables set\n    +     3. Have color.ui enabled\n    +     4. Have color.pager enabled\n    +     5. Not have color.man disabled\n    +     6. Run git with stdout on a tty\n    +\n    +    Otherwise the current behavior remains.\n    +\n         [1] https://unix.stackexchange.com/questions/119/colors-in-man-pages/147\n     \n         Suggested-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n    @@ Documentation/config/color.txt: color.interactive.<slot>::\n      \tinteractive commands.\n      \n     +color.man::\n    -+\tWhen set to `always`, always colorize the man pages. When `false`\n    -+\t(or `never`), never.  When set to `true` or `auto`, use color only\n    -+\twhen the output is written to the terminal. If unset, then the\n    -+\tvalue of `color.ui` is used (`auto` by default).\n    ++\tThis flag can be used to disable the automatic colorizaton of man\n    ++\tpages when using the less pager. It's activated only when color.ui\n    ++\tallows it, and also when color.pager is on. (`true` by default).\n     +\n      color.pager::\n      \tA boolean to enable/disable colored output when the pager is in\n    @@ builtin/help.c: static int verbose = 1;\n      static unsigned int colopts;\n      static enum help_format help_format = HELP_FORMAT_NONE;\n      static int exclude_guides;\n    -+static int man_color = GIT_COLOR_UNKNOWN;\n    ++static int man_color = 1;\n      static struct option builtin_help_options[] = {\n      \tOPT_BOOL('a', \"all\", &show_all, N_(\"print all available commands\")),\n      \tOPT_HIDDEN_BOOL(0, \"exclude-guides\", &exclude_guides, N_(\"exclude guides\")),\n    @@ builtin/help.c: static void exec_man_konqueror(const char *path, const char *pag\n      \n     +static void colorize_man(void)\n     +{\n    -+\tif (!want_color(man_color) || !pager_use_color)\n    ++\tif (!man_color || !want_color(GIT_COLOR_UNKNOWN) || !pager_use_color)\n     +\t\treturn;\n     +\n     +\t/* Disable groff colors */\n    @@ builtin/help.c: static int git_help_config(const char *var, const char *value, v\n      \tif (starts_with(var, \"man.\"))\n      \t\treturn add_man_viewer_info(var, value);\n     +\tif (!strcmp(var, \"color.man\")) {\n    -+\t\tman_color = git_config_colorbool(var, value);\n    ++\t\tman_color = git_config_bool(var, value);\n     +\t\treturn 0;\n     +\t}\n      \n\n Documentation/config/color.txt |  5 +++++\n builtin/help.c                 | 32 +++++++++++++++++++++++++++++++-\n color.h                        |  1 +\n 3 files changed, 37 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/color.txt b/Documentation/config/color.txt\nindex d5daacb13a..11278b7f72 100644\n--- a/Documentation/config/color.txt\n+++ b/Documentation/config/color.txt\n@@ -126,6 +126,11 @@ color.interactive.<slot>::\n \tor `error`, for four distinct types of normal output from\n \tinteractive commands.\n \n+color.man::\n+\tThis flag can be used to disable the automatic colorizaton of man\n+\tpages when using the less pager. It's activated only when color.ui\n+\tallows it, and also when color.pager is on. (`true` by default).\n+\n color.pager::\n \tA boolean to enable/disable colored output when the pager is in\n \tuse (default is true).\ndiff --git a/builtin/help.c b/builtin/help.c\nindex bb339f0fc8..9025e9a56a 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -11,6 +11,7 @@\n #include \"config-list.h\"\n #include \"help.h\"\n #include \"alias.h\"\n+#include \"color.h\"\n \n #ifndef DEFAULT_HELP_FORMAT\n #define DEFAULT_HELP_FORMAT \"man\"\n@@ -43,6 +44,7 @@ static int verbose = 1;\n static unsigned int colopts;\n static enum help_format help_format = HELP_FORMAT_NONE;\n static int exclude_guides;\n+static int man_color = 1;\n static struct option builtin_help_options[] = {\n \tOPT_BOOL('a', \"all\", &show_all, N_(\"print all available commands\")),\n \tOPT_HIDDEN_BOOL(0, \"exclude-guides\", &exclude_guides, N_(\"exclude guides\")),\n@@ -253,10 +255,33 @@ static void exec_man_konqueror(const char *path, const char *page)\n \t}\n }\n \n+static void colorize_man(void)\n+{\n+\tif (!man_color || !want_color(GIT_COLOR_UNKNOWN) || !pager_use_color)\n+\t\treturn;\n+\n+\t/* Disable groff colors */\n+\tsetenv(\"GROFF_NO_SGR\", \"1\", 0);\n+\n+\t/* Bold */\n+\tsetenv(\"LESS_TERMCAP_md\", GIT_COLOR_BOLD_RED, 0);\n+\tsetenv(\"LESS_TERMCAP_me\", GIT_COLOR_RESET, 0);\n+\n+\t/* Underline */\n+\tsetenv(\"LESS_TERMCAP_us\", GIT_COLOR_BLUE GIT_COLOR_UNDERLINE, 0);\n+\tsetenv(\"LESS_TERMCAP_ue\", GIT_COLOR_RESET, 0);\n+\n+\t/* Standout */\n+\tsetenv(\"LESS_TERMCAP_so\", GIT_COLOR_MAGENTA GIT_COLOR_REVERSE, 0);\n+\tsetenv(\"LESS_TERMCAP_se\", GIT_COLOR_RESET, 0);\n+}\n+\n static void exec_man_man(const char *path, const char *page)\n {\n \tif (!path)\n \t\tpath = \"man\";\n+\n+\tcolorize_man();\n \texeclp(path, \"man\", page, (char *)NULL);\n \twarning_errno(_(\"failed to exec '%s'\"), path);\n }\n@@ -264,6 +289,7 @@ static void exec_man_man(const char *path, const char *page)\n static void exec_man_cmd(const char *cmd, const char *page)\n {\n \tstruct strbuf shell_cmd = STRBUF_INIT;\n+\tcolorize_man();\n \tstrbuf_addf(&shell_cmd, \"%s %s\", cmd, page);\n \texecl(SHELL_PATH, SHELL_PATH, \"-c\", shell_cmd.buf, (char *)NULL);\n \twarning(_(\"failed to exec '%s'\"), cmd);\n@@ -371,8 +397,12 @@ static int git_help_config(const char *var, const char *value, void *cb)\n \t}\n \tif (starts_with(var, \"man.\"))\n \t\treturn add_man_viewer_info(var, value);\n+\tif (!strcmp(var, \"color.man\")) {\n+\t\tman_color = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \n-\treturn git_default_config(var, value, cb);\n+\treturn git_color_default_config(var, value, cb);\n }\n \n static struct cmdnames main_cmds, other_cmds;\ndiff --git a/color.h b/color.h\nindex 98894d6a17..d012add4e8 100644\n--- a/color.h\n+++ b/color.h\n@@ -51,6 +51,7 @@ struct strbuf;\n #define GIT_COLOR_FAINT\t\t\"\\033[2m\"\n #define GIT_COLOR_FAINT_ITALIC\t\"\\033[2;3m\"\n #define GIT_COLOR_REVERSE\t\"\\033[7m\"\n+#define GIT_COLOR_UNDERLINE\t\"\\033[4m\"\n \n /* A special value meaning \"no color selected\" */\n #define GIT_COLOR_NIL \"NIL\"\n-- \n2.31.1\n\n"}]}