@matheusbsbs / matheus-bagliano / commits / 6612b9e471
Fork 0

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;