sh: improve command completion

When multiple matches are found, we keep the provided string on the
input line and print unique matches as suggestions.

But the multiple matches might be the same command found in different
directories, so we should deduplicate the matches first and then decide
whether to autocomplete the command or not, based on the number of
unique matches.
This commit is contained in:
Piotr Pawel Stefaniak 2021-09-18 13:26:51 +02:00
parent e19d93b19d
commit b8ff849cbd

View file

@ -590,7 +590,7 @@ static char
char *free_path = NULL, *path;
const char *dirname;
char **matches = NULL;
size_t i = 0, size = 16, j, k;
size_t i = 0, size = 16, uniq;
size_t curpos = end - start;
if (start > 0 || memchr("/.~", text[0], 3) != NULL)
@ -639,6 +639,21 @@ static char
}
out:
free(free_path);
if (i == 0) {
free(matches);
return (NULL);
}
uniq = 1;
if (i > 1) {
qsort_s(matches + 1, i, sizeof(matches[0]), comparator,
(void *)(intptr_t)curpos);
for (size_t k = 2; k <= i; k++)
if (strcmp(matches[uniq] + curpos, matches[k] + curpos) == 0)
free(matches[k]);
else
matches[++uniq] = matches[k];
}
matches[uniq + 1] = NULL;
/*
* matches[0] is special: it's not a real matching file name but a common
* prefix for all matching names. It can't be null, unlike any other
@ -648,30 +663,13 @@ static char
* string in matches[0] which is the reason to copy the full name of the
* only match.
*/
if (i == 0) {
free(matches);
return (NULL);
} else if (i == 1) {
matches[0] = strdup(matches[1]);
matches[2] = NULL;
if (matches[0] != NULL)
return (matches);
} else
matches[0] = strdup(text);
matches[0] = strdup(uniq == 1 ? matches[1] : text);
if (matches[0] == NULL) {
for (j = 1; j <= i; j++)
free(matches[j]);
for (size_t k = 1; k <= uniq; k++)
free(matches[k]);
free(matches);
return (NULL);
}
qsort_s(matches + 1, i, sizeof(matches[0]), comparator,
(void *)(intptr_t)curpos);
for (j = 1, k = 2; k <= i; k++)
if (strcmp(matches[j] + curpos, matches[k] + curpos) == 0)
free(matches[k]);
else
matches[++j] = matches[k];
matches[j + 1] = NULL;
return (matches);
}