Merge branch 'ab/help-config-vars'

Teach "git help -c" into helping the command line completion of
configuration variables.

* ab/help-config-vars:
  help: move column config discovery to help.c library
  help / completion: make "git help" do the hard work
  help tests: test --config-for-completion option & output
  help: simplify by moving to OPT_CMDMODE()
  help: correct logic error in combining --all and --guides
  help: correct logic error in combining --all and --config
  help tests: add test for --config output
  help: correct usage & behavior of "git help --guides"
  help: correct the usage string in -h and documentation
This commit is contained in:
Junio C Hamano 2021-10-13 15:15:58 -07:00
commit 62f035aee3
7 changed files with 167 additions and 69 deletions

View file

@ -8,8 +8,10 @@ git-help - Display help information about Git
SYNOPSIS
--------
[verse]
'git help' [-a|--all [--[no-]verbose]] [-g|--guides]
[-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
'git help' [-a|--all [--[no-]verbose]]
[[-i|--info] [-m|--man] [-w|--web]] [COMMAND|GUIDE]
'git help' [-g|--guides]
'git help' [-c|--config]
DESCRIPTION
-----------
@ -58,8 +60,7 @@ OPTIONS
-g::
--guides::
Prints a list of the Git concept guides on the standard output. This
option overrides any given command or guide name.
Prints a list of the Git concept guides on the standard output.
-i::
--info::

View file

@ -7,7 +7,6 @@
#include "exec-cmd.h"
#include "parse-options.h"
#include "run-command.h"
#include "column.h"
#include "config-list.h"
#include "help.h"
#include "alias.h"
@ -34,32 +33,52 @@ enum help_format {
HELP_FORMAT_WEB
};
static const char *html_path;
enum show_config_type {
SHOW_CONFIG_HUMAN,
SHOW_CONFIG_VARS,
SHOW_CONFIG_SECTIONS,
};
static int show_all = 0;
static int show_guides = 0;
static int show_config;
static enum help_action {
HELP_ACTION_ALL = 1,
HELP_ACTION_GUIDES,
HELP_ACTION_CONFIG,
HELP_ACTION_CONFIG_FOR_COMPLETION,
HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
} cmd_mode;
static const char *html_path;
static int verbose = 1;
static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
static int exclude_guides;
static struct option builtin_help_options[] = {
OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
OPT_CMDMODE('a', "all", &cmd_mode, N_("print all available commands"),
HELP_ACTION_ALL),
OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")),
OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN),
OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
HELP_FORMAT_WEB),
OPT_SET_INT('i', "info", &help_format, N_("show info page"),
HELP_FORMAT_INFO),
OPT__VERBOSE(&verbose, N_("print command description")),
OPT_CMDMODE('g', "guides", &cmd_mode, N_("print list of useful guides"),
HELP_ACTION_GUIDES),
OPT_CMDMODE('c', "config", &cmd_mode, N_("print all configuration variable names"),
HELP_ACTION_CONFIG),
OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "",
HELP_ACTION_CONFIG_FOR_COMPLETION, PARSE_OPT_HIDDEN),
OPT_CMDMODE_F(0, "config-sections-for-completion", &cmd_mode, "",
HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, PARSE_OPT_HIDDEN),
OPT_END(),
};
static const char * const builtin_help_usage[] = {
N_("git help [--all] [--guides] [--man | --web | --info] [<command>]"),
N_("git help [-a|--all] [--[no-]verbose]]\n"
" [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
N_("git help [-g|--guides]"),
N_("git help [-c|--config]"),
NULL
};
@ -70,7 +89,7 @@ struct slot_expansion {
int found;
};
static void list_config_help(int for_human)
static void list_config_help(enum show_config_type type)
{
struct slot_expansion slot_expansions[] = {
{ "advice", "*", list_config_advices },
@ -88,6 +107,8 @@ static void list_config_help(int for_human)
const char **p;
struct slot_expansion *e;
struct string_list keys = STRING_LIST_INIT_DUP;
struct string_list keys_uniq = STRING_LIST_INIT_DUP;
struct string_list_item *item;
int i;
for (p = config_name_list; *p; p++) {
@ -118,34 +139,46 @@ static void list_config_help(int for_human)
for (i = 0; i < keys.nr; i++) {
const char *var = keys.items[i].string;
const char *wildcard, *tag, *cut;
const char *dot = NULL;
struct strbuf sb = STRBUF_INIT;
if (for_human) {
switch (type) {
case SHOW_CONFIG_HUMAN:
puts(var);
continue;
case SHOW_CONFIG_SECTIONS:
dot = strchr(var, '.');
break;
case SHOW_CONFIG_VARS:
break;
}
wildcard = strchr(var, '*');
tag = strchr(var, '<');
if (!wildcard && !tag) {
puts(var);
if (!dot && !wildcard && !tag) {
string_list_append(&keys_uniq, var);
continue;
}
if (wildcard && !tag)
if (dot)
cut = dot;
else if (wildcard && !tag)
cut = wildcard;
else if (!wildcard && tag)
cut = tag;
else
cut = wildcard < tag ? wildcard : tag;
/*
* We may produce duplicates, but that's up to
* git-completion.bash to handle
*/
printf("%.*s\n", (int)(cut - var), var);
strbuf_add(&sb, var, cut - var);
string_list_append(&keys_uniq, sb.buf);
strbuf_release(&sb);
}
string_list_clear(&keys, 0);
string_list_remove_duplicates(&keys_uniq, 0);
for_each_string_list_item(item, &keys_uniq)
puts(item->string);
string_list_clear(&keys_uniq, 0);
}
static enum help_format parse_help_format(const char *format)
@ -349,8 +382,6 @@ static int add_man_viewer_info(const char *var, const char *value)
static int git_help_config(const char *var, const char *value, void *cb)
{
if (starts_with(var, "column."))
return git_column_config(var, value, "help", &colopts);
if (!strcmp(var, "help.format")) {
if (!value)
return config_error_nonbool(var);
@ -544,6 +575,13 @@ static const char *check_git_cmd(const char* cmd)
return cmd;
}
static void no_extra_argc(int argc)
{
if (argc)
usage_msg_opt(_("this option doesn't take any other arguments"),
builtin_help_usage, builtin_help_options);
}
int cmd_help(int argc, const char **argv, const char *prefix)
{
int nongit;
@ -554,8 +592,8 @@ int cmd_help(int argc, const char **argv, const char *prefix)
builtin_help_usage, 0);
parsed_help_format = help_format;
if (show_all) {
git_config(git_help_config, NULL);
switch (cmd_mode) {
case HELP_ACTION_ALL:
if (verbose) {
setup_pager();
list_all_cmds_help();
@ -563,30 +601,27 @@ int cmd_help(int argc, const char **argv, const char *prefix)
}
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
load_command_list("git-", &main_cmds, &other_cmds);
list_commands(colopts, &main_cmds, &other_cmds);
}
if (show_config) {
int for_human = show_config == 1;
if (!for_human) {
list_config_help(for_human);
return 0;
}
setup_pager();
list_config_help(for_human);
printf("\n%s\n", _("'git help config' for more information"));
return 0;
}
if (show_guides)
list_guides_help();
if (show_all || show_guides) {
list_commands(&main_cmds, &other_cmds);
printf("%s\n", _(git_more_info_string));
/*
* We're done. Ignore any remaining args
*/
break;
case HELP_ACTION_GUIDES:
no_extra_argc(argc);
list_guides_help();
printf("%s\n", _(git_more_info_string));
return 0;
case HELP_ACTION_CONFIG_FOR_COMPLETION:
no_extra_argc(argc);
list_config_help(SHOW_CONFIG_VARS);
return 0;
case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION:
no_extra_argc(argc);
list_config_help(SHOW_CONFIG_SECTIONS);
return 0;
case HELP_ACTION_CONFIG:
no_extra_argc(argc);
setup_pager();
list_config_help(SHOW_CONFIG_HUMAN);
printf("\n%s\n", _("'git help config' for more information"));
return 0;
}

View file

@ -2503,7 +2503,14 @@ __git_config_vars=
__git_compute_config_vars ()
{
test -n "$__git_config_vars" ||
__git_config_vars="$(git help --config-for-completion | sort -u)"
__git_config_vars="$(git help --config-for-completion)"
}
__git_config_sections=
__git_compute_config_sections ()
{
test -n "$__git_config_sections" ||
__git_config_sections="$(git help --config-sections-for-completion)"
}
# Completes possible values of various configuration variables.
@ -2717,16 +2724,8 @@ __git_complete_config_variable_name ()
__gitcomp "$__git_config_vars" "" "$cur_" "$sfx"
;;
*)
__git_compute_config_vars
__gitcomp "$(echo "$__git_config_vars" |
awk -F . '{
sections[$1] = 1
}
END {
for (s in sections)
print s "."
}
')" "" "$cur_"
__git_compute_config_sections
__gitcomp "$__git_config_sections" "" "$cur_" "."
;;
esac
}

16
help.c
View file

@ -293,9 +293,21 @@ void load_command_list(const char *prefix,
exclude_cmds(other_cmds, main_cmds);
}
void list_commands(unsigned int colopts,
struct cmdnames *main_cmds, struct cmdnames *other_cmds)
static int get_colopts(const char *var, const char *value, void *data)
{
unsigned int *colopts = data;
if (starts_with(var, "column."))
return git_column_config(var, value, "help", colopts);
return 0;
}
void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds)
{
unsigned int colopts = 0;
git_config(get_colopts, &colopts);
if (main_cmds->cnt) {
const char *exec_path = git_exec_path();
printf_ln(_("available git commands in '%s'"), exec_path);

2
help.h
View file

@ -37,7 +37,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len);
/* Here we require that excludes is a sorted list. */
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
int is_in_cmdlist(struct cmdnames *cmds, const char *name);
void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds);
void get_version_info(struct strbuf *buf, int show_build_options);
/*

View file

@ -166,8 +166,10 @@ struct option {
#define OPT_BOOL(s, l, v, h) OPT_BOOL_F(s, l, v, h, 0)
#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \
(h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
#define OPT_CMDMODE(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, \
(h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) }
#define OPT_CMDMODE_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
(h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), NULL, (i) }
#define OPT_CMDMODE(s, l, v, h, i) OPT_CMDMODE_F(s, l, v, h, i, 0)
#define OPT_INTEGER(s, l, v, h) OPT_INTEGER_F(s, l, v, h, 0)
#define OPT_MAGNITUDE(s, l, v, h) { OPTION_MAGNITUDE, (s), (l), (v), \
N_("n"), (h), PARSE_OPT_NONEG }

View file

@ -34,6 +34,18 @@ test_expect_success 'basic help commands' '
git help -a >/dev/null
'
test_expect_success 'invalid usage' '
test_expect_code 129 git help -g add &&
test_expect_code 129 git help -a -c &&
test_expect_code 129 git help -g add &&
test_expect_code 129 git help -a -g &&
test_expect_code 129 git help -g -c &&
test_expect_code 129 git help --config-for-completion add &&
test_expect_code 129 git help --config-sections-for-completion add
'
test_expect_success "works for commands and guides by default" '
configure_help &&
git help status &&
@ -89,6 +101,43 @@ test_expect_success 'git help succeeds without git.html' '
test_cmp expect test-browser.log
'
test_expect_success 'git help -c' '
git help -c >help.output &&
cat >expect <<-\EOF &&
'\''git help config'\'' for more information
EOF
grep -v -E \
-e "^[^.]+\.[^.]+$" \
-e "^[^.]+\.[^.]+\.[^.]+$" \
help.output >actual &&
test_cmp expect actual
'
test_expect_success 'git help --config-for-completion' '
git help -c >human &&
grep -E \
-e "^[^.]+\.[^.]+$" \
-e "^[^.]+\.[^.]+\.[^.]+$" human |
sed -e "s/\*.*//" -e "s/<.*//" |
sort -u >human.munged &&
git help --config-for-completion >vars &&
test_cmp human.munged vars
'
test_expect_success 'git help --config-sections-for-completion' '
git help -c >human &&
grep -E \
-e "^[^.]+\.[^.]+$" \
-e "^[^.]+\.[^.]+\.[^.]+$" human |
sed -e "s/\..*//" |
sort -u >human.munged &&
git help --config-sections-for-completion >sections &&
test_cmp human.munged sections
'
test_expect_success 'generate builtin list' '
git --list-cmds=builtins >builtins
'