Browse Source

Merge branch 'ar/autospell'

* ar/autospell:
  Add help.autocorrect to enable/disable autocorrecting
  git wrapper: DWIM mistyped commands
maint
Junio C Hamano 17 years ago
parent
commit
277cd4c4bd
  1. 9
      Documentation/config.txt
  2. 2
      Makefile
  3. 2
      builtin.h
  4. 4
      git.c
  5. 89
      help.c
  6. 2
      help.h
  7. 47
      levenshtein.c
  8. 8
      levenshtein.h

9
Documentation/config.txt

@ -790,6 +790,15 @@ help.format:: @@ -790,6 +790,15 @@ help.format::
Values 'man', 'info', 'web' and 'html' are supported. 'man' is
the default. 'web' and 'html' are the same.

help.autocorrect::
Automatically correct and execute mistyped commands after
waiting for the given number of deciseconds (0.1 sec). If more
than one command can be deduced from the entered text, nothing
will be executed. If the value of this option is negative,
the corrected command will be executed immediately. If the
value is 0 - the command will be just shown but not executed.
This is the default.

http.proxy::
Override the HTTP proxy, normally configured using the 'http_proxy'
environment variable (see linkgit:curl[1]). This can be overridden

2
Makefile

@ -358,6 +358,7 @@ LIB_H += graph.h @@ -358,6 +358,7 @@ LIB_H += graph.h
LIB_H += grep.h
LIB_H += hash.h
LIB_H += help.h
LIB_H += levenshtein.h
LIB_H += list-objects.h
LIB_H += ll-merge.h
LIB_H += log-tree.h
@ -434,6 +435,7 @@ LIB_OBJS += hash.o @@ -434,6 +435,7 @@ LIB_OBJS += hash.o
LIB_OBJS += help.o
LIB_OBJS += ident.o
LIB_OBJS += interpolate.o
LIB_OBJS += levenshtein.o
LIB_OBJS += list-objects.o
LIB_OBJS += ll-merge.o
LIB_OBJS += lockfile.o

2
builtin.h

@ -11,7 +11,7 @@ extern const char git_usage_string[]; @@ -11,7 +11,7 @@ extern const char git_usage_string[];
extern const char git_more_info_string[];

extern void list_common_cmds_help(void);
extern void help_unknown_cmd(const char *cmd);
extern const char *help_unknown_cmd(const char *cmd);
extern void prune_packed_objects(int);
extern int read_line_with_nul(char *buf, int size, FILE *file);
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,

4
git.c

@ -499,7 +499,9 @@ int main(int argc, const char **argv) @@ -499,7 +499,9 @@ int main(int argc, const char **argv)
cmd, argv[0]);
exit(1);
}
help_unknown_cmd(cmd);
argv[0] = help_unknown_cmd(cmd);
handle_internal_command(argc, argv);
execv_dashed_external(argv);
}

fprintf(stderr, "Failed to run command '%s': %s\n",

89
help.c

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
#include "cache.h"
#include "builtin.h"
#include "exec_cmd.h"
#include "levenshtein.h"
#include "help.h"

/* most GUI terminals set COLUMNS (although some don't export it) */
@ -37,6 +38,16 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len) @@ -37,6 +38,16 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len)
cmds->names[cmds->cnt++] = ent;
}

static void clean_cmdnames(struct cmdnames *cmds)
{
int i;
for (i = 0; i < cmds->cnt; ++i)
free(cmds->names[i]);
free(cmds->names);
cmds->cnt = 0;
cmds->alloc = 0;
}

static int cmdname_compare(const void *a_, const void *b_)
{
struct cmdname *a = *(struct cmdname **)a_;
@ -250,9 +261,85 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) @@ -250,9 +261,85 @@ int is_in_cmdlist(struct cmdnames *c, const char *s)
return 0;
}

void help_unknown_cmd(const char *cmd)
static int autocorrect;

static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "help.autocorrect"))
autocorrect = git_config_int(var,value);

return git_default_config(var, value, cb);
}

static int levenshtein_compare(const void *p1, const void *p2)
{
const struct cmdname *const *c1 = p1, *const *c2 = p2;
const char *s1 = (*c1)->name, *s2 = (*c2)->name;
int l1 = (*c1)->len;
int l2 = (*c2)->len;
return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
}

const char *help_unknown_cmd(const char *cmd)
{
int i, n, best_similarity = 0;
struct cmdnames main_cmds, other_cmds;

memset(&main_cmds, 0, sizeof(main_cmds));
memset(&other_cmds, 0, sizeof(main_cmds));

git_config(git_unknown_cmd_config, NULL);

load_command_list("git-", &main_cmds, &other_cmds);

ALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt,
main_cmds.alloc);
memcpy(main_cmds.names + main_cmds.cnt, other_cmds.names,
other_cmds.cnt * sizeof(other_cmds.names[0]));
main_cmds.cnt += other_cmds.cnt;
free(other_cmds.names);

/* This reuses cmdname->len for similarity index */
for (i = 0; i < main_cmds.cnt; ++i)
main_cmds.names[i]->len =
levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);

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

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

best_similarity = main_cmds.names[0]->len;
n = 1;
while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
++n;
if (autocorrect && n == 1) {
const char *assumed = main_cmds.names[0]->name;
main_cmds.names[0] = NULL;
clean_cmdnames(&main_cmds);
fprintf(stderr, "WARNING: You called a Git program named '%s', "
"which does not exist.\n"
"Continuing under the assumption that you meant '%s'\n",
cmd, assumed);
if (autocorrect > 0) {
fprintf(stderr, "in %0.1f seconds automatically...\n",
(float)autocorrect/10.0);
poll(NULL, 0, autocorrect * 100);
}
return assumed;
}

fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);

if (best_similarity < 6) {
fprintf(stderr, "\nDid you mean %s?\n",
n < 2 ? "this": "one of these");

for (i = 0; i < n; i++)
fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
}

exit(1);
}


2
help.h

@ -5,7 +5,7 @@ struct cmdnames { @@ -5,7 +5,7 @@ struct cmdnames {
int alloc;
int cnt;
struct cmdname {
size_t len;
size_t len; /* also used for similarity index in help.c */
char name[FLEX_ARRAY];
} **names;
};

47
levenshtein.c

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
#include "cache.h"
#include "levenshtein.h"

int levenshtein(const char *string1, const char *string2,
int w, int s, int a, int d)
{
int len1 = strlen(string1), len2 = strlen(string2);
int *row0 = xmalloc(sizeof(int) * (len2 + 1));
int *row1 = xmalloc(sizeof(int) * (len2 + 1));
int *row2 = xmalloc(sizeof(int) * (len2 + 1));
int i, j;

for (j = 0; j <= len2; j++)
row1[j] = j * a;
for (i = 0; i < len1; i++) {
int *dummy;

row2[0] = (i + 1) * d;
for (j = 0; j < len2; j++) {
/* substitution */
row2[j + 1] = row1[j] + s * (string1[i] != string2[j]);
/* swap */
if (i > 0 && j > 0 && string1[i - 1] == string2[j] &&
string1[i] == string2[j - 1] &&
row2[j + 1] > row0[j - 1] + w)
row2[j + 1] = row0[j - 1] + w;
/* deletion */
if (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d)
row2[j + 1] = row1[j + 1] + d;
/* insertion */
if (row2[j + 1] > row2[j] + a)
row2[j + 1] = row2[j] + a;
}

dummy = row0;
row0 = row1;
row1 = row2;
row2 = dummy;
}

i = row1[len2];
free(row0);
free(row1);
free(row2);

return i;
}

8
levenshtein.h

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
#ifndef LEVENSHTEIN_H
#define LEVENSHTEIN_H

int levenshtein(const char *string1, const char *string2,
int swap_penalty, int substition_penalty,
int insertion_penalty, int deletion_penalty);

#endif
Loading…
Cancel
Save