Skip to content

Commit 6612b9e

Browse files
kusmagitster
authored andcommitted
help: always suggest common-cmds if prefix of cmd
If someone runs "git st", the command "git status" is not suggested because it's not one of the closest levenshtein-neighbour. Reserve the distance of 0 for common commands where the entered command is a prefixe, as these are often more likely to be what the user meant. This way, "git status" is the first suggestion, while a list of possible typos are still suggested as well. Signed-off-by: Erik Faye-Lund <kusmabite@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 7d43de9 commit 6612b9e

2 files changed

Lines changed: 41 additions & 8 deletions

File tree

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,8 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
16201620
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
16211621
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
16221622

1623+
help.o: common-cmds.h
1624+
16231625
builtin/help.o: common-cmds.h
16241626
builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
16251627
'-DGIT_HTML_PATH="$(htmldir_SQ)"' \

help.c

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "exec_cmd.h"
44
#include "levenshtein.h"
55
#include "help.h"
6+
#include "common-cmds.h"
67

78
/* most GUI terminals set COLUMNS (although some don't export it) */
89
static int term_columns(void)
@@ -298,7 +299,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
298299
}
299300

300301
/* An empirically derived magic number */
301-
#define SIMILAR_ENOUGH(x) ((x) < 6)
302+
#define SIMILARITY_FLOOR 7
303+
#define SIMILAR_ENOUGH(x) ((x) < SIMILARITY_FLOOR)
302304

303305
const char *help_unknown_cmd(const char *cmd)
304306
{
@@ -319,21 +321,50 @@ const char *help_unknown_cmd(const char *cmd)
319321
sizeof(main_cmds.names), cmdname_compare);
320322
uniq(&main_cmds);
321323

322-
/* This reuses cmdname->len for similarity index */
323-
for (i = 0; i < main_cmds.cnt; ++i)
324+
/* This abuses cmdname->len for levenshtein distance */
325+
for (i = 0, n = 0; i < main_cmds.cnt; i++) {
326+
int cmp = 0; /* avoid compiler stupidity */
327+
const char *candidate = main_cmds.names[i]->name;
328+
329+
/* Does the candidate appear in common_cmds list? */
330+
while (n < ARRAY_SIZE(common_cmds) &&
331+
(cmp = strcmp(common_cmds[n].name, candidate)) < 0)
332+
n++;
333+
if ((n < ARRAY_SIZE(common_cmds)) && !cmp) {
334+
/* Yes, this is one of the common commands */
335+
n++; /* use the entry from common_cmds[] */
336+
if (!prefixcmp(candidate, cmd)) {
337+
/* Give prefix match a very good score */
338+
main_cmds.names[i]->len = 0;
339+
continue;
340+
}
341+
}
342+
324343
main_cmds.names[i]->len =
325-
levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
344+
levenshtein(cmd, candidate, 0, 2, 1, 4) + 1;
345+
}
326346

327347
qsort(main_cmds.names, main_cmds.cnt,
328348
sizeof(*main_cmds.names), levenshtein_compare);
329349

330350
if (!main_cmds.cnt)
331351
die ("Uh oh. Your system reports no Git commands at all.");
332352

333-
best_similarity = main_cmds.names[0]->len;
334-
n = 1;
335-
while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
336-
++n;
353+
/* skip and count prefix matches */
354+
for (n = 0; n < main_cmds.cnt && !main_cmds.names[n]->len; n++)
355+
; /* still counting */
356+
357+
if (main_cmds.cnt <= n) {
358+
/* prefix matches with everything? that is too ambiguous */
359+
best_similarity = SIMILARITY_FLOOR + 1;
360+
} else {
361+
/* count all the most similar ones */
362+
for (best_similarity = main_cmds.names[n++]->len;
363+
(n < main_cmds.cnt &&
364+
best_similarity == main_cmds.names[n]->len);
365+
n++)
366+
; /* still counting */
367+
}
337368
if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
338369
const char *assumed = main_cmds.names[0]->name;
339370
main_cmds.names[0] = NULL;

0 commit comments

Comments
 (0)