Merge branch 'nd/command-list'
The list of commands with their various attributes were spread across a few places in the build procedure, but it now is getting a bit more consolidated to allow more automation. * nd/command-list: completion: allow to customize the completable command list completion: add and use --list-cmds=alias completion: add and use --list-cmds=nohelpers Move declaration for alias.c to alias.h completion: reduce completable command list completion: let git provide the completable command list command-list.txt: documentation and guide line help: use command-list.txt for the source of guides help: add "-a --verbose" to list all commands with synopsis git: support --list-cmds=list-<category> completion: implement and use --list-cmds=main,others git --list-cmds: collect command list in a string_list git.c: convert --list-* to --list-cmds=* Remove common-cmds.h help: use command-list.h for common command list generate-cmds.sh: export all commands to command-list.h generate-cmds.sh: factor out synopsis extract code
This commit is contained in:
256
help.c
256
help.c
@ -5,13 +5,127 @@
|
||||
#include "run-command.h"
|
||||
#include "levenshtein.h"
|
||||
#include "help.h"
|
||||
#include "common-cmds.h"
|
||||
#include "command-list.h"
|
||||
#include "string-list.h"
|
||||
#include "column.h"
|
||||
#include "version.h"
|
||||
#include "refs.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
struct category_description {
|
||||
uint32_t category;
|
||||
const char *desc;
|
||||
};
|
||||
static uint32_t common_mask =
|
||||
CAT_init | CAT_worktree | CAT_info |
|
||||
CAT_history | CAT_remote;
|
||||
static struct category_description common_categories[] = {
|
||||
{ CAT_init, N_("start a working area (see also: git help tutorial)") },
|
||||
{ CAT_worktree, N_("work on the current change (see also: git help everyday)") },
|
||||
{ CAT_info, N_("examine the history and state (see also: git help revisions)") },
|
||||
{ CAT_history, N_("grow, mark and tweak your common history") },
|
||||
{ CAT_remote, N_("collaborate (see also: git help workflows)") },
|
||||
{ 0, NULL }
|
||||
};
|
||||
static struct category_description main_categories[] = {
|
||||
{ CAT_mainporcelain, N_("Main Porcelain Commands") },
|
||||
{ CAT_ancillarymanipulators, N_("Ancillary Commands / Manipulators") },
|
||||
{ CAT_ancillaryinterrogators, N_("Ancillary Commands / Interrogators") },
|
||||
{ CAT_foreignscminterface, N_("Interacting with Others") },
|
||||
{ CAT_plumbingmanipulators, N_("Low-level Commands / Manipulators") },
|
||||
{ CAT_plumbinginterrogators, N_("Low-level Commands / Interrogators") },
|
||||
{ CAT_synchingrepositories, N_("Low-level Commands / Synching Repositories") },
|
||||
{ CAT_purehelpers, N_("Low-level Commands / Internal Helpers") },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const char *drop_prefix(const char *name, uint32_t category)
|
||||
{
|
||||
const char *new_name;
|
||||
|
||||
if (skip_prefix(name, "git-", &new_name))
|
||||
return new_name;
|
||||
if (category == CAT_guide && skip_prefix(name, "git", &new_name))
|
||||
return new_name;
|
||||
return name;
|
||||
|
||||
}
|
||||
|
||||
static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)
|
||||
{
|
||||
int i, nr = 0;
|
||||
struct cmdname_help *cmds;
|
||||
|
||||
if (ARRAY_SIZE(command_list) == 0)
|
||||
BUG("empty command_list[] is a sign of broken generate-cmdlist.sh");
|
||||
|
||||
ALLOC_ARRAY(cmds, ARRAY_SIZE(command_list) + 1);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(command_list); i++) {
|
||||
const struct cmdname_help *cmd = command_list + i;
|
||||
|
||||
if (!(cmd->category & mask))
|
||||
continue;
|
||||
|
||||
cmds[nr] = *cmd;
|
||||
cmds[nr].name = drop_prefix(cmd->name, cmd->category);
|
||||
|
||||
nr++;
|
||||
}
|
||||
cmds[nr].name = NULL;
|
||||
*p_cmds = cmds;
|
||||
}
|
||||
|
||||
static void print_command_list(const struct cmdname_help *cmds,
|
||||
uint32_t mask, int longest)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; cmds[i].name; i++) {
|
||||
if (cmds[i].category & mask) {
|
||||
printf(" %s ", cmds[i].name);
|
||||
mput_char(' ', longest - strlen(cmds[i].name));
|
||||
puts(_(cmds[i].help));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_name_cmp(const void *elem1, const void *elem2)
|
||||
{
|
||||
const struct cmdname_help *e1 = elem1;
|
||||
const struct cmdname_help *e2 = elem2;
|
||||
|
||||
return strcmp(e1->name, e2->name);
|
||||
}
|
||||
|
||||
static void print_cmd_by_category(const struct category_description *catdesc)
|
||||
{
|
||||
struct cmdname_help *cmds;
|
||||
int longest = 0;
|
||||
int i, nr = 0;
|
||||
uint32_t mask = 0;
|
||||
|
||||
for (i = 0; catdesc[i].desc; i++)
|
||||
mask |= catdesc[i].category;
|
||||
|
||||
extract_cmds(&cmds, mask);
|
||||
|
||||
for (i = 0; cmds[i].name; i++, nr++) {
|
||||
if (longest < strlen(cmds[i].name))
|
||||
longest = strlen(cmds[i].name);
|
||||
}
|
||||
QSORT(cmds, nr, cmd_name_cmp);
|
||||
|
||||
for (i = 0; catdesc[i].desc; i++) {
|
||||
uint32_t mask = catdesc[i].category;
|
||||
const char *desc = catdesc[i].desc;
|
||||
|
||||
printf("\n%s\n", _(desc));
|
||||
print_command_list(cmds, mask, longest);
|
||||
}
|
||||
free(cmds);
|
||||
}
|
||||
|
||||
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
|
||||
{
|
||||
struct cmdname *ent;
|
||||
@ -190,42 +304,114 @@ void list_commands(unsigned int colopts,
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_group_cmp(const void *elem1, const void *elem2)
|
||||
{
|
||||
const struct cmdname_help *e1 = elem1;
|
||||
const struct cmdname_help *e2 = elem2;
|
||||
|
||||
if (e1->group < e2->group)
|
||||
return -1;
|
||||
if (e1->group > e2->group)
|
||||
return 1;
|
||||
return strcmp(e1->name, e2->name);
|
||||
}
|
||||
|
||||
void list_common_cmds_help(void)
|
||||
{
|
||||
int i, longest = 0;
|
||||
int current_grp = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
if (longest < strlen(common_cmds[i].name))
|
||||
longest = strlen(common_cmds[i].name);
|
||||
}
|
||||
|
||||
QSORT(common_cmds, ARRAY_SIZE(common_cmds), cmd_group_cmp);
|
||||
|
||||
puts(_("These are common Git commands used in various situations:"));
|
||||
print_cmd_by_category(common_categories);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
if (common_cmds[i].group != current_grp) {
|
||||
printf("\n%s\n", _(common_cmd_groups[common_cmds[i].group]));
|
||||
current_grp = common_cmds[i].group;
|
||||
void list_all_main_cmds(struct string_list *list)
|
||||
{
|
||||
struct cmdnames main_cmds, other_cmds;
|
||||
int i;
|
||||
|
||||
memset(&main_cmds, 0, sizeof(main_cmds));
|
||||
memset(&other_cmds, 0, sizeof(other_cmds));
|
||||
load_command_list("git-", &main_cmds, &other_cmds);
|
||||
|
||||
for (i = 0; i < main_cmds.cnt; i++)
|
||||
string_list_append(list, main_cmds.names[i]->name);
|
||||
|
||||
clean_cmdnames(&main_cmds);
|
||||
clean_cmdnames(&other_cmds);
|
||||
}
|
||||
|
||||
void list_all_other_cmds(struct string_list *list)
|
||||
{
|
||||
struct cmdnames main_cmds, other_cmds;
|
||||
int i;
|
||||
|
||||
memset(&main_cmds, 0, sizeof(main_cmds));
|
||||
memset(&other_cmds, 0, sizeof(other_cmds));
|
||||
load_command_list("git-", &main_cmds, &other_cmds);
|
||||
|
||||
for (i = 0; i < other_cmds.cnt; i++)
|
||||
string_list_append(list, other_cmds.names[i]->name);
|
||||
|
||||
clean_cmdnames(&main_cmds);
|
||||
clean_cmdnames(&other_cmds);
|
||||
}
|
||||
|
||||
void list_cmds_by_category(struct string_list *list,
|
||||
const char *cat)
|
||||
{
|
||||
int i, n = ARRAY_SIZE(command_list);
|
||||
uint32_t cat_id = 0;
|
||||
|
||||
for (i = 0; category_names[i]; i++) {
|
||||
if (!strcmp(cat, category_names[i])) {
|
||||
cat_id = 1UL << i;
|
||||
break;
|
||||
}
|
||||
|
||||
printf(" %s ", common_cmds[i].name);
|
||||
mput_char(' ', longest - strlen(common_cmds[i].name));
|
||||
puts(_(common_cmds[i].help));
|
||||
}
|
||||
if (!cat_id)
|
||||
die(_("unsupported command listing type '%s'"), cat);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
struct cmdname_help *cmd = command_list + i;
|
||||
|
||||
if (!(cmd->category & cat_id))
|
||||
continue;
|
||||
string_list_append(list, drop_prefix(cmd->name, cmd->category));
|
||||
}
|
||||
}
|
||||
|
||||
void list_cmds_by_config(struct string_list *list)
|
||||
{
|
||||
const char *cmd_list;
|
||||
|
||||
/*
|
||||
* There's no actual repository setup at this point (and even
|
||||
* if there is, we don't really care; only global config
|
||||
* matters). If we accidentally set up a repository, it's ok
|
||||
* too since the caller (git --list-cmds=) should exit shortly
|
||||
* anyway.
|
||||
*/
|
||||
if (git_config_get_string_const("completion.commands", &cmd_list))
|
||||
return;
|
||||
|
||||
string_list_sort(list);
|
||||
string_list_remove_duplicates(list, 0);
|
||||
|
||||
while (*cmd_list) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *p = strchrnul(cmd_list, ' ');
|
||||
|
||||
strbuf_add(&sb, cmd_list, p - cmd_list);
|
||||
if (*cmd_list == '-')
|
||||
string_list_remove(list, cmd_list + 1, 0);
|
||||
else
|
||||
string_list_insert(list, sb.buf);
|
||||
strbuf_release(&sb);
|
||||
while (*p == ' ')
|
||||
p++;
|
||||
cmd_list = p;
|
||||
}
|
||||
}
|
||||
|
||||
void list_common_guides_help(void)
|
||||
{
|
||||
struct category_description catdesc[] = {
|
||||
{ CAT_guide, N_("The common Git guides are:") },
|
||||
{ 0, NULL }
|
||||
};
|
||||
print_cmd_by_category(catdesc);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void list_all_cmds_help(void)
|
||||
{
|
||||
print_cmd_by_category(main_categories);
|
||||
}
|
||||
|
||||
int is_in_cmdlist(struct cmdnames *c, const char *s)
|
||||
@ -285,6 +471,7 @@ const char *help_unknown_cmd(const char *cmd)
|
||||
{
|
||||
int i, n, best_similarity = 0;
|
||||
struct cmdnames main_cmds, other_cmds;
|
||||
struct cmdname_help *common_cmds;
|
||||
|
||||
memset(&main_cmds, 0, sizeof(main_cmds));
|
||||
memset(&other_cmds, 0, sizeof(other_cmds));
|
||||
@ -299,6 +486,8 @@ const char *help_unknown_cmd(const char *cmd)
|
||||
QSORT(main_cmds.names, main_cmds.cnt, cmdname_compare);
|
||||
uniq(&main_cmds);
|
||||
|
||||
extract_cmds(&common_cmds, common_mask);
|
||||
|
||||
/* This abuses cmdname->len for levenshtein distance */
|
||||
for (i = 0, n = 0; i < main_cmds.cnt; i++) {
|
||||
int cmp = 0; /* avoid compiler stupidity */
|
||||
@ -313,10 +502,10 @@ const char *help_unknown_cmd(const char *cmd)
|
||||
die(_(bad_interpreter_advice), cmd, cmd);
|
||||
|
||||
/* Does the candidate appear in common_cmds list? */
|
||||
while (n < ARRAY_SIZE(common_cmds) &&
|
||||
while (common_cmds[n].name &&
|
||||
(cmp = strcmp(common_cmds[n].name, candidate)) < 0)
|
||||
n++;
|
||||
if ((n < ARRAY_SIZE(common_cmds)) && !cmp) {
|
||||
if (common_cmds[n].name && !cmp) {
|
||||
/* Yes, this is one of the common commands */
|
||||
n++; /* use the entry from common_cmds[] */
|
||||
if (starts_with(candidate, cmd)) {
|
||||
@ -329,6 +518,7 @@ const char *help_unknown_cmd(const char *cmd)
|
||||
main_cmds.names[i]->len =
|
||||
levenshtein(cmd, candidate, 0, 2, 1, 3) + 1;
|
||||
}
|
||||
FREE_AND_NULL(common_cmds);
|
||||
|
||||
QSORT(main_cmds.names, main_cmds.cnt, levenshtein_compare);
|
||||
|
||||
|
Reference in New Issue
Block a user