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>
Erik Faye-Lund committed
Nov 26, 2010 at 17:00 UTC
6612b9e471a3e25079dac5257ead8cf6abe68575
2 files changed
+41
-8
Makefile
+2
@@ -1620,6 +1620,8 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
1620
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
1621
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
1622
1623
+help.o: common-cmds.h
1624
+
1625
builtin/help.o: common-cmds.h
1626
builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
1627
'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
help.c
+39
-8
@@ -3,6 +3,7 @@
3
#include "exec_cmd.h"
4
#include "levenshtein.h"
5
#include "help.h"
6
+#include "common-cmds.h"
7
8
/* most GUI terminals set COLUMNS (although some don't export it) */
9
static int term_columns(void)
@@ -298,7 +299,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
299
}
300
301
/* 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)
304
305
const char *help_unknown_cmd(const char *cmd)
306
{
@@ -319,10 +321,28 @@ const char *help_unknown_cmd(const char *cmd)
321
sizeof(main_cmds.names), cmdname_compare);
322
uniq(&main_cmds);
323
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
+
343
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
+ }
346
347
qsort(main_cmds.names, main_cmds.cnt,
348
sizeof(*main_cmds.names), levenshtein_compare);
@@ -330,10 +350,21 @@ const char *help_unknown_cmd(const char *cmd)
350
if (!main_cmds.cnt)
351
die ("Uh oh. Your system reports no Git commands at all.");
352
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
+ }
368
if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
369
const char *assumed = main_cmds.names[0]->name;
370
main_cmds.names[0] = NULL;