Re: [PATCH v5 2/2] sideband: highlight keywords in remote sideband output

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Han-Wen Nienhuys <hanwen@xxxxxxxxxx> writes:

> The Git push process itself prints lots of non-actionable messages
> (eg. bandwidth statistics, object counters for different phases of the
> process), which obscures actionable error messages that servers may

s/which obscures/which obscure/, as I think that "which" refers to
"messages" above.

> The highlighting is done on the client rather than server side, so
> servers don't have to grow capabilities to understand terminal escape
> codes and terminal state. It also consistent with the current state
> where Git is control of the local display (eg. prefixing messages with
> "remote: ").

Yup.

When we introduce "the receiving end asks messages to be sent with
such and such decoration" protocol support, we would want a lot more
than just painting messages in color, e.g. i18n, verbosity, and even
"Hey, I am a script, send them in json".

Until that happens, let's keep things simpler.  No i18n messages and
no colored output over the wire.

> +color.remote::
> +	If set, keywords at the start of the line are highlighted. The
> +	keywords are "error", "warning", "hint" and "success", and are
> +	matched case-insensitively. Maybe set to `always`, `false` (or
> +	`never`) or `auto` (or `true`). If unset, then the value of
> +	`color.ui` is used (`auto` by default).

Reads much better.

I am still trying to find a concise way to help readers who saw a
line that begins with "Warnings: foo bar bla" and accept that it is
OK the early 7 chars are not painted.  "... case-insensitively and
honoring word boundary" is the best I came up so far, but  I am
afraid that is adding more words without hinting what I want to convey
strongly enough, so I am not going to suggest that (at least not yet).

> diff --git a/help.h b/help.h
> index f8b15323a6..9eab6a3f89 100644
> --- a/help.h
> +++ b/help.h
> @@ -83,6 +83,7 @@ void list_config_color_diff_slots(struct string_list *list, const char *prefix);
>  void list_config_color_grep_slots(struct string_list *list, const char *prefix);
>  void list_config_color_interactive_slots(struct string_list *list, const char *prefix);
>  void list_config_color_status_slots(struct string_list *list, const char *prefix);
> +void list_config_color_sideband_slots(struct string_list *list, const char *prefix);
>  void list_config_fsck_msg_ids(struct string_list *list, const char *prefix);
>  
>  #endif /* HELP_H */
> diff --git a/sideband.c b/sideband.c
> index 325bf0e974..239be2ec85 100644
> --- a/sideband.c
> +++ b/sideband.c
> @@ -1,6 +1,108 @@
>  #include "cache.h"
> +#include "color.h"
> +#include "config.h"
>  #include "pkt-line.h"
>  #include "sideband.h"
> +#include "help.h"
> +
> +struct keyword_entry {
> +	/*
> +	 * We use keyword as config key so it should be a single alphanumeric word.
> +	 */
> +	const char *keyword;
> +	char color[COLOR_MAXLEN];
> +};
> +
> +static struct keyword_entry keywords[] = {
> +	{ "hint",	GIT_COLOR_YELLOW },
> +	{ "warning",	GIT_COLOR_BOLD_YELLOW },
> +	{ "success",	GIT_COLOR_BOLD_GREEN },
> +	{ "error",	GIT_COLOR_BOLD_RED },
> +};
> +/* Returns a color setting (GIT_COLOR_NEVER, etc). */
> +static int use_sideband_colors(void)
> +{
> +	static int use_sideband_colors_cached = -1;
> +
> +	const char *key = "color.remote";
> +	struct strbuf sb = STRBUF_INIT;
> +	char *value;
> +	int i;
> +
> +	if (use_sideband_colors_cached >= 0)
> +		return use_sideband_colors_cached;
> +
> +	if (!git_config_get_string(key, &value)) {
> +		use_sideband_colors_cached = git_config_colorbool(key, value);
> +	} else if (!git_config_get_string("color.ui", &value)) {
> +		use_sideband_colors_cached = git_config_colorbool("color.ui", value);
> +	} else {
> +		use_sideband_colors_cached = GIT_COLOR_AUTO;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(keywords); i++) {
> +		strbuf_reset(&sb);
> +		strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword);

This design means future enhancement to allow more keywords will
have to be done in the form of adding more "color.remote.<key>",
which means a few restrictions on them are cast in stone at the
end-user facing design level, which we need to be careful about.

	Side note. I do not worry about the implementation level
	limitation at all.  For example, the current code will not
	allow end-users and projects to add new keywords to be
	painted, as it relies on the keywords[] static array we can
	see above.  But that implementation detail does not prevent
	us from improving it later to support more code in this
	codepath that notices "color.remote.failure" in config file
	and paint a line that begins with "failure:".

Because the third-level "variable" name is case insensive, matching
of any future keyword MUST be also done case insensitively.

Also, as you mentioned elsewhere in this patch, the string that can
be in the keyword MUST begin with an alphabetic and consists only of
alphanumeric or dash.

I do not think these limitations imposed by the design decision this
patch is making are particularly bad ones---we just need to be aware
of and firm about them.  When somebody else comes in the future and
wants to recognize "F A I L" as a custom keyword case sensitively,
we must be able to comfortably say "no" to it.

	Side note. We _could_ instead use "remotemsg.<key>.color"
	namespace, as the subsection names at the second level is a
	lot looser, but I do not think it is a good idea to use in
	this application, as they are case sensitive.

The above discussion may deserve to be in the log message as a
record to tell future ourselves why we decided to use
color.remote.<key>.

> +		if (git_config_get_string(sb.buf, &value))
> +			continue;
> +		if (color_parse(value, keywords[i].color))
> +			die(_("config value %s is not a color: %s"), sb.buf, value);

That's a bit inconsistent, isn't it?  If the configuration is not
even a string, we ignore it and continue, but if it is a string, we
insist that it names a color and otherwise die?

> +	}
> +	strbuf_release(&sb);
> +	return use_sideband_colors_cached;
> +}

> +/*
> + * Optionally highlight one keyword in remote output if it appears at the start
> + * of the line. This should be called for a single line only, which must be
> + * passed as the first N characters of the SRC array.
> + */

Saying "MUST be" is cheap, but do we have anybody who polices that
requirement?

I think the code is OK without any assert() or BUG(), and that is
because the design is "we just paint the keyword at the beginning of
what the other side of the sideband wants us to show as a single
unit".  If the other side sends a payload with an embedded LF in a
single packet, that's their choice and we are free not to paint the
beginning of the second line after that LF.  So from that point of
view, perhaps we shouldn't even talk about "a single line only".

> +static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
> +{
> +	int i;
> +
> +	if (!want_color_stderr(use_sideband_colors())) {
> +		strbuf_add(dest, src, n);
> +		return;
> +	}
> +
> +	while (isspace(*src)) {
> +		strbuf_addch(dest, *src);
> +		src++;
> +		n--;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(keywords); i++) {
> +		struct keyword_entry *p = keywords + i;
> +		int len = strlen(p->keyword);
> +		/*
> +		 * Match case insensitively, so we colorize output from existing
> +		 * servers regardless of the case that they use for their
> +		 * messages. We only highlight the word precisely, so
> +		 * "successful" stays uncolored.
> +		 */
> +		if (!strncasecmp(p->keyword, src, len) && !isalnum(src[len])) {
> +			strbuf_addstr(dest, p->color);
> +			strbuf_add(dest, src, len);
> +			strbuf_addstr(dest, GIT_COLOR_RESET);
> +			n -= len;
> +			src += len;
> +			break;
> +		}
> +	}
> +
> +	strbuf_add(dest, src, n);
> +}
> +
>  
>  /*
>   * Receive multiplexed output stream over git native protocol.
> @@ -16,7 +118,7 @@
>  #define DISPLAY_PREFIX "remote: "
>  
>  #define ANSI_SUFFIX "\033[K"
> -#define DUMB_SUFFIX "        "
> +#define DUMB_SUFFIX "	     "
>  

Was this change intended and if so for what purpose?  I can drop
this hunk if this is a mere finger-slip without proofreading, but I
do not want to do so without making sure I am not missing anything
and not discarding a meaningful change.

>  int recv_sideband(const char *me, int in_stream, int out)
>  {


> diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh
> new file mode 100755
> index 0000000000..a9afb55ef1
> --- /dev/null
> +++ b/t/t5409-colorize-remote-messages.sh
> @@ -0,0 +1,80 @@
> +#!/bin/sh
> +
> +test_description='remote messages are colorized on the client'
> +
> +. ./test-lib.sh
> +
> +test_expect_success 'setup' '
> +	mkdir .git/hooks &&
> +	write_script .git/hooks/update <<-\EOF &&
> +echo error: error
> +echo ERROR: also highlighted
> +echo hint: hint
> +echo hinting: not highlighted
> +echo success: success
> +echo warning: warning
> +echo prefixerror: error
> +echo "  error: leading space"
> +exit 0
> +EOF

Noticing the dash in "<<-", I would have expected all of the above
lines to be indented with a tab to align with 'w' in 'write_script'.

> +	chmod +x .git/hooks/update &&

No need for this "chmod +x"; that's one of the points in using
write_script.

Thanks.



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux