help / completion: make "git help" do the hard work

The "help" builtin has been able to emit configuration variables since e17ca926371 (completion: drop the hard coded list of config vars, 2018-05-26), but it hasn't produced exactly the format the completion script wanted. Let's do that. We got partway there in 2675ea1cc0f (completion: use 'sort -u' to deduplicate config variable names, 2019-08-13) and d9438873c4d (completion: deduplicate configuration sections, 2019-08-13), but after both we still needed some sorting, de-duplicating and awk post-processing of the list. We can instead simply do the relevant parsing ourselves (we were doing most of it already), and call string_list_remove_duplicates() after already sorting the list, so the caller doesn't need to invoke "sort -u". The "--config-for-completion" output is the same as before after being passed through "sort -u". Then add a new "--config-sections-for-completion" option. Under that output we'll emit config sections like "alias" (instead of "alias." in the --config-for-completion output). We need to be careful to leave the "--config-for-completion" option compatible with users git, but are still running a shell with an older git-completion.bash. If we e.g. changed the option name they'd see messages about git-completion.bash being unable to find the "--config-for-completion" option. Such backwards compatibility isn't something we should bend over backwards for, it's only helping users who: * Upgrade git * Are in an old shell * The git-completion.bash in that shell hasn't cached the old "--config-for-completion" output already. But since it's easy in this case to retain compatibility, let's do it, the older versions of git-completion.bash won't care that the input they get doesn't change after a "sort -u". While we're at it let's make "--config-for-completion" die if there's anything left over in "argc", and do the same in the new "--config-sections-for-completion" option. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ævar Arnfjörð Bjarmason committed Sep 22, 2021 at 00:40 UTC a9bacccae54cd449821416199f70c4dd2fcb9be4
3 files changed +65 -27
builtin/help.c
+41 -13
@@ -34,11 +34,18 @@ enum help_format {
34 HELP_FORMAT_WEB
35 };
36
37 +enum show_config_type {
38 + SHOW_CONFIG_HUMAN,
39 + SHOW_CONFIG_VARS,
40 + SHOW_CONFIG_SECTIONS,
41 +};
42 +
43 static enum help_action {
44 HELP_ACTION_ALL = 1,
45 HELP_ACTION_GUIDES,
46 HELP_ACTION_CONFIG,
47 HELP_ACTION_CONFIG_FOR_COMPLETION,
48 + HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
49 } cmd_mode;
50
51 static const char *html_path;
@@ -63,6 +70,8 @@ static struct option builtin_help_options[] = {
70 HELP_ACTION_CONFIG),
71 OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "",
72 HELP_ACTION_CONFIG_FOR_COMPLETION, PARSE_OPT_HIDDEN),
73 + OPT_CMDMODE_F(0, "config-sections-for-completion", &cmd_mode, "",
74 + HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, PARSE_OPT_HIDDEN),
75
76 OPT_END(),
77 };
@@ -82,7 +91,7 @@ struct slot_expansion {
91 int found;
92 };
93
85 -static void list_config_help(int for_human)
94 +static void list_config_help(enum show_config_type type)
95 {
96 struct slot_expansion slot_expansions[] = {
97 { "advice", "*", list_config_advices },
@@ -100,6 +109,8 @@ static void list_config_help(int for_human)
109 const char **p;
110 struct slot_expansion *e;
111 struct string_list keys = STRING_LIST_INIT_DUP;
112 + struct string_list keys_uniq = STRING_LIST_INIT_DUP;
113 + struct string_list_item *item;
114 int i;
115
116 for (p = config_name_list; *p; p++) {
@@ -130,34 +141,46 @@ static void list_config_help(int for_human)
141 for (i = 0; i < keys.nr; i++) {
142 const char *var = keys.items[i].string;
143 const char *wildcard, *tag, *cut;
144 + const char *dot = NULL;
145 + struct strbuf sb = STRBUF_INIT;
146
134 - if (for_human) {
147 + switch (type) {
148 + case SHOW_CONFIG_HUMAN:
149 puts(var);
150 continue;
151 + case SHOW_CONFIG_SECTIONS:
152 + dot = strchr(var, '.');
153 + break;
154 + case SHOW_CONFIG_VARS:
155 + break;
156 }
138 -
157 wildcard = strchr(var, '*');
158 tag = strchr(var, '<');
159
142 - if (!wildcard && !tag) {
143 - puts(var);
160 + if (!dot && !wildcard && !tag) {
161 + string_list_append(&keys_uniq, var);
162 continue;
163 }
164
147 - if (wildcard && !tag)
165 + if (dot)
166 + cut = dot;
167 + else if (wildcard && !tag)
168 cut = wildcard;
169 else if (!wildcard && tag)
170 cut = tag;
171 else
172 cut = wildcard < tag ? wildcard : tag;
173
154 - /*
155 - * We may produce duplicates, but that's up to
156 - * git-completion.bash to handle
157 - */
158 - printf("%.*s\n", (int)(cut - var), var);
174 + strbuf_add(&sb, var, cut - var);
175 + string_list_append(&keys_uniq, sb.buf);
176 + strbuf_release(&sb);
177 +
178 }
179 string_list_clear(&keys, 0);
180 + string_list_remove_duplicates(&keys_uniq, 0);
181 + for_each_string_list_item(item, &keys_uniq)
182 + puts(item->string);
183 + string_list_clear(&keys_uniq, 0);
184 }
185
186 static enum help_format parse_help_format(const char *format)
@@ -589,12 +612,17 @@ int cmd_help(int argc, const char **argv, const char *prefix)
612 printf("%s\n", _(git_more_info_string));
613 return 0;
614 case HELP_ACTION_CONFIG_FOR_COMPLETION:
592 - list_config_help(0);
615 + no_extra_argc(argc);
616 + list_config_help(SHOW_CONFIG_VARS);
617 + return 0;
618 + case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION:
619 + no_extra_argc(argc);
620 + list_config_help(SHOW_CONFIG_SECTIONS);
621 return 0;
622 case HELP_ACTION_CONFIG:
623 no_extra_argc(argc);
624 setup_pager();
597 - list_config_help(1);
625 + list_config_help(SHOW_CONFIG_HUMAN);
626 printf("\n%s\n", _("'git help config' for more information"));
627 return 0;
628 }
contrib/completion/git-completion.bash
+10 -11
@@ -2503,7 +2503,14 @@ __git_config_vars=
2503 __git_compute_config_vars ()
2504 {
2505 test -n "$__git_config_vars" ||
2506 - __git_config_vars="$(git help --config-for-completion | sort -u)"
2506 + __git_config_vars="$(git help --config-for-completion)"
2507 +}
2508 +
2509 +__git_config_sections=
2510 +__git_compute_config_sections ()
2511 +{
2512 + test -n "$__git_config_sections" ||
2513 + __git_config_sections="$(git help --config-sections-for-completion)"
2514 }
2515
2516 # Completes possible values of various configuration variables.
@@ -2717,16 +2724,8 @@ __git_complete_config_variable_name ()
2724 __gitcomp "$__git_config_vars" "" "$cur_" "$sfx"
2725 ;;
2726 *)
2720 - __git_compute_config_vars
2721 - __gitcomp "$(echo "$__git_config_vars" |
2722 - awk -F . '{
2723 - sections[$1] = 1
2724 - }
2725 - END {
2726 - for (s in sections)
2727 - print s "."
2728 - }
2729 - ')" "" "$cur_"
2727 + __git_compute_config_sections
2728 + __gitcomp "$__git_config_sections" "" "$cur_" "."
2729 ;;
2730 esac
2731 }
t/t0012-help.sh
+14 -3
@@ -42,7 +42,8 @@ test_expect_success 'invalid usage' '
42 test_expect_code 129 git help -a -g &&
43
44 test_expect_code 129 git help -g -c &&
45 - test_expect_code 0 git help --config-for-completion add
45 + test_expect_code 129 git help --config-for-completion add &&
46 + test_expect_code 129 git help --config-sections-for-completion add
47 '
48
49 test_expect_success "works for commands and guides by default" '
@@ -106,11 +107,21 @@ test_expect_success 'git help --config-for-completion' '
107 sort -u >human.munged &&
108
109 git help --config-for-completion >vars &&
109 - sort -u <vars >vars.new &&
110 - mv vars.new vars &&
110 test_cmp human.munged vars
111 '
112
113 +test_expect_success 'git help --config-sections-for-completion' '
114 + git help -c >human &&
115 + grep -E \
116 + -e "^[^.]+\.[^.]+$" \
117 + -e "^[^.]+\.[^.]+\.[^.]+$" human |
118 + sed -e "s/\..*//" |
119 + sort -u >human.munged &&
120 +
121 + git help --config-sections-for-completion >sections &&
122 + test_cmp human.munged sections
123 +'
124 +
125 test_expect_success 'generate builtin list' '
126 git --list-cmds=builtins >builtins
127 '