[PATCH v2 2/5] help: refactor command autocorrection handling
- From
- Jiamu Sun <39@barroit.sh>
- Date
- Mar 8, 2026, 23:16 UTC
- Message-ID
- <SY0P300MB0801DD4E14C17C5D199788B0CE78A@SY0P300MB0801.AUSP300.PROD.OUTLOOK.COM>
- In-Reply-To
- <SY0P300MB08013E35DCA8FC31B0662125CE78A@SY0P300MB0801.AUSP300.PROD.OUTLOOK.COM>
Refactor autocorrect config into struct autocorr, with an explicit mode enum and a separate delay field. Move config parsing, TTY validation, and prompt handling into autocorrect.c.
This simplifies help.c and makes state handling for autocorrect explicit and easier to maintain.
Signed-off-by: Jiamu Sun <39@barroit.sh> --- Makefile | 1 + autocorrect.c | 92 +++++++++++++++++++++++++++++++++++++++++++ autocorrect.h | 23 +++++++++++ help.c | 106 ++++++++++---------------------------------------- 4 files changed, 136 insertions(+), 86 deletions(-) create mode 100644 autocorrect.c create mode 100644 autocorrect.h
diff --git a/Makefile b/Makefile index f3264d0a37cc..6111631c2caa 100644 --- a/Makefile +++ b/Makefile @@ -1098,6 +1098,7 @@ LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o LIB_OBJS += archive.o LIB_OBJS += attr.o +LIB_OBJS += autocorrect.o LIB_OBJS += base85.o LIB_OBJS += bisect.o LIB_OBJS += blame.o diff --git a/autocorrect.c b/autocorrect.c new file mode 100644 index 000000000000..eaae01645910 --- /dev/null +++ b/autocorrect.c @@ -0,0 +1,92 @@ +#define USE_THE_REPOSITORY_VARIABLE + +#include "git-compat-util.h" +#include "autocorrect.h" +#include "config.h" +#include "parse.h" +#include "strbuf.h" +#include "prompt.h" +#include "gettext.h" + +static enum autocorr_mode parse_autocorr(const char *value) +{ + switch (git_parse_maybe_bool_text(value)) { + case 1: + return AUTOCORR_IMMEDIATELY; + case 0: + return AUTOCORR_HINTONLY; + default: /* other random text */ + break; + } + + if (!strcmp(value, "prompt")) + return AUTOCORR_PROMPT; + else if (!strcmp(value, "never")) + return AUTOCORR_NEVER; + else if (!strcmp(value, "immediate")) + return AUTOCORR_IMMEDIATELY; + else if (!strcmp(value, "show")) + return AUTOCORR_HINTONLY; + else + return AUTOCORR_DELAY; +} + +static int config_cb(const char *var, const char *value, + const struct config_context *ctx, void *data) +{ + struct autocorr *conf = data; + + if (strcmp(var, "help.autocorrect")) + return 0; + + conf->mode = parse_autocorr(value); + + /* + * Disable autocorrection prompt in a non-interactive session + */ + if (conf->mode == AUTOCORR_PROMPT && (!isatty(0) || !isatty(2))) + conf->mode = AUTOCORR_NEVER; + + if (conf->mode == AUTOCORR_DELAY) { + conf->delay = git_config_int(var, value, ctx->kvi); + + if (conf->delay == 0) + conf->mode = AUTOCORR_HINTONLY; + else if (conf->delay <= 1) + conf->mode = AUTOCORR_IMMEDIATELY; + } + + return 0; +} + +void autocorr_resolve(struct autocorr *conf) +{ + read_early_config(the_repository, config_cb, conf); +} + +void autocorr_prompt_or_delay(struct autocorr *conf, const char *assumed) +{ + if (conf->mode == AUTOCORR_IMMEDIATELY) { + fprintf_ln(stderr, + _("Continuing under the assumption that you meant '%s'."), + assumed); + + } else if (conf->mode == AUTOCORR_PROMPT) { + char *answer; + struct strbuf msg = STRBUF_INIT; + + strbuf_addf(&msg, _("Run '%s' instead [y/N]? "), assumed); + answer = git_prompt(msg.buf, PROMPT_ECHO); + strbuf_release(&msg); + + if (!(starts_with(answer, "y") || starts_with(answer, "Y"))) + exit(1); + + } else if (conf->mode == AUTOCORR_DELAY) { + fprintf_ln(stderr, + _("Continuing in %0.1f seconds, assuming that you meant '%s'."), + (float)conf->delay / 10.0, assumed); + + sleep_millisec(conf->delay * 100); + } +} diff --git a/autocorrect.h b/autocorrect.h new file mode 100644 index 000000000000..ea21811a43f2 --- /dev/null +++ b/autocorrect.h @@ -0,0 +1,23 @@ +#ifndef AUTOCORRECT_H +#define AUTOCORRECT_H + +struct config_context; + +enum autocorr_mode { + AUTOCORR_HINTONLY, + AUTOCORR_NEVER, + AUTOCORR_PROMPT, + AUTOCORR_IMMEDIATELY, + AUTOCORR_DELAY, +}; + +struct autocorr { + enum autocorr_mode mode; + int delay; +}; + +void autocorr_resolve(struct autocorr *conf); + +void autocorr_prompt_or_delay(struct autocorr *conf, const char *assumed); + +#endif /* AUTOCORRECT_H */ diff --git a/help.c b/help.c index 95f576c5c81d..5eec6a34de33 100644 --- a/help.c +++ b/help.c @@ -22,6 +22,7 @@ #include "repository.h" #include "alias.h" #include "utf8.h" +#include "autocorrect.h" #ifndef NO_CURL #include "git-curl-compat.h" /* For LIBCURL_VERSION only */ @@ -536,70 +537,23 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) return 0; } -struct help_unknown_cmd_config { - int autocorrect; - struct cmdnames aliases; -}; - -#define AUTOCORRECT_SHOW (-4) -#define AUTOCORRECT_PROMPT (-3) -#define AUTOCORRECT_NEVER (-2) -#define AUTOCORRECT_IMMEDIATELY (-1) - -static int parse_autocorrect(const char *value) +static int resolve_aliases(const char *var, const char *value, + const struct config_context *ctx, void *data) { - switch (git_parse_maybe_bool_text(value)) { - case 1: - return AUTOCORRECT_IMMEDIATELY; - case 0: - return AUTOCORRECT_SHOW; - default: /* other random text */ - break; - } - - if (!strcmp(value, "prompt")) - return AUTOCORRECT_PROMPT; - if (!strcmp(value, "never")) - return AUTOCORRECT_NEVER; - if (!strcmp(value, "immediate")) - return AUTOCORRECT_IMMEDIATELY; - if (!strcmp(value, "show")) - return AUTOCORRECT_SHOW; - - return 0; -} - -static int git_unknown_cmd_config(const char *var, const char *value, - const struct config_context *ctx, - void *cb) -{ - struct help_unknown_cmd_config *cfg = cb; + struct cmdnames *aliases = data; const char *subsection, *key; size_t subsection_len; - if (!strcmp(var, "help.autocorrect")) { - int v = parse_autocorrect(value); - - if (!v) { - v = git_config_int(var, value, ctx->kvi); - if (v < 0 || v == 1) - v = AUTOCORRECT_IMMEDIATELY; - } - - cfg->autocorrect = v; - } - - /* Also use aliases for command lookup */ if (!parse_config_key(var, "alias", &subsection, &subsection_len, &key)) { if (subsection) { /* [alias "name"] command = value */ if (!strcmp(key, "command")) - add_cmdname(&cfg->aliases, subsection, + add_cmdname(aliases, subsection, subsection_len); } else { /* alias.name = value */ - add_cmdname(&cfg->aliases, key, strlen(key)); + add_cmdname(aliases, key, strlen(key)); } } @@ -636,28 +590,27 @@ static const char bad_interpreter_advice[] = char *help_unknown_cmd(const char *cmd) { - struct help_unknown_cmd_config cfg = { 0 }; + struct cmdnames aliases = { 0 }; + struct autocorr autocorr = { 0 }; + int i, n, best_similarity = 0; struct cmdnames main_cmds = { 0 }; struct cmdnames other_cmds = { 0 }; struct cmdname_help *common_cmds; - read_early_config(the_repository, git_unknown_cmd_config, &cfg); + autocorr_resolve(&autocorr); - /* - * Disable autocorrection prompt in a non-interactive session - */ - if ((cfg.autocorrect == AUTOCORRECT_PROMPT) && (!isatty(0) || !isatty(2))) - cfg.autocorrect = AUTOCORRECT_NEVER; + /* Also use aliases for command lookup */ + read_early_config(the_repository, resolve_aliases, &aliases); - if (cfg.autocorrect == AUTOCORRECT_NEVER) { + if (autocorr.mode == AUTOCORR_NEVER) { fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd); exit(1); } load_command_list("git-", &main_cmds, &other_cmds); - add_cmd_list(&main_cmds, &cfg.aliases); + add_cmd_list(&main_cmds, &aliases); add_cmd_list(&main_cmds, &other_cmds); QSORT(main_cmds.names, main_cmds.cnt, cmdname_compare); uniq(&main_cmds); @@ -716,37 +669,18 @@ char *help_unknown_cmd(const char *cmd) n++) ; /* still counting */ } - if (cfg.autocorrect && cfg.autocorrect != AUTOCORRECT_SHOW && n == 1 && + + if (autocorr.mode != AUTOCORR_HINTONLY && n == 1 && SIMILAR_ENOUGH(best_similarity)) { char *assumed = xstrdup(main_cmds.names[0]->name); fprintf_ln(stderr, - _("WARNING: You called a Git command named '%s', " - "which does not exist."), + _("WARNING: You called a Git command named '%s', which does not exist."), cmd); - if (cfg.autocorrect == AUTOCORRECT_IMMEDIATELY) - fprintf_ln(stderr, - _("Continuing under the assumption that " - "you meant '%s'."), - assumed); - else if (cfg.autocorrect == AUTOCORRECT_PROMPT) { - char *answer; - struct strbuf msg = STRBUF_INIT; - strbuf_addf(&msg, _("Run '%s' instead [y/N]? "), assumed); - answer = git_prompt(msg.buf, PROMPT_ECHO); - strbuf_release(&msg); - if (!(starts_with(answer, "y") || - starts_with(answer, "Y"))) - exit(1); - } else { - fprintf_ln(stderr, - _("Continuing in %0.1f seconds, " - "assuming that you meant '%s'."), - (float)cfg.autocorrect/10.0, assumed); - sleep_millisec(cfg.autocorrect * 100); - } - cmdnames_release(&cfg.aliases); + autocorr_prompt_or_delay(&autocorr, assumed); + + cmdnames_release(&aliases); cmdnames_release(&main_cmds); cmdnames_release(&other_cmds); return assumed;
-- 2.53.0