input complete: reimplement using GPtrArray.

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
Andrew Borodin 2024-04-20 19:53:04 +03:00
parent 4eacf0f99b
commit e496af7a1c
3 changed files with 86 additions and 96 deletions

View File

@ -58,7 +58,7 @@ typedef struct
gboolean init_from_history; /* init text will be get from history */ gboolean init_from_history; /* init text will be get from history */
gboolean need_push; /* need to push the current Input on hist? */ gboolean need_push; /* need to push the current Input on hist? */
gboolean strip_password; /* need to strip password before placing string to history */ gboolean strip_password; /* need to strip password before placing string to history */
char **completions; /* possible completions array */ GPtrArray *completions; /* possible completions array */
input_complete_t completion_flags; input_complete_t completion_flags;
char charbuf[MB_LEN_MAX]; /* buffer for multibytes characters */ char charbuf[MB_LEN_MAX]; /* buffer for multibytes characters */
size_t charpoint; /* point to end of mulibyte sequence in charbuf */ size_t charpoint; /* point to end of mulibyte sequence in charbuf */

View File

@ -88,7 +88,7 @@ typedef struct
/*** forward declarations (file scope functions) *************************************************/ /*** forward declarations (file scope functions) *************************************************/
char **try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags); GPtrArray *try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags);
void complete_engine_fill_completions (WInput * in); void complete_engine_fill_completions (WInput * in);
/*** file scope variables ************************************************************************/ /*** file scope variables ************************************************************************/
@ -681,7 +681,7 @@ command_completion_function (const char *text, int state, input_complete_t flags
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
static int static int
match_compare (const void *a, const void *b) match_compare (gconstpointer a, gconstpointer b)
{ {
return strcmp (*(char *const *) a, *(char *const *) b); return strcmp (*(char *const *) a, *(char *const *) b);
} }
@ -695,72 +695,54 @@ match_compare (const void *a, const void *b)
as the second. as the second.
In case no matches were found we return NULL. */ In case no matches were found we return NULL. */
static char ** static GPtrArray *
completion_matches (const char *text, CompletionFunction entry_function, input_complete_t flags) completion_matches (const char *text, CompletionFunction entry_function, input_complete_t flags)
{ {
/* Number of slots in match_list. */ GPtrArray *match_list;
size_t match_list_size = 30;
/* The list of matches. */
char **match_list;
/* Number of matches actually found. */
size_t matches = 0;
/* Temporary string binder. */
char *string; char *string;
match_list = g_new (char *, match_list_size + 1); match_list = g_ptr_array_new_with_free_func (g_free);
match_list[1] = NULL;
while ((string = (*entry_function) (text, matches, flags)) != NULL) while ((string = entry_function (text, match_list->len, flags)) != NULL)
{ g_ptr_array_add (match_list, string);
if (matches + 1 == match_list_size)
{
match_list_size += 30;
match_list = (char **) g_renew (char *, match_list, match_list_size + 1);
}
match_list[++matches] = string;
match_list[matches + 1] = NULL;
}
/* If there were any matches, then look through them finding out the /* If there were any matches, then look through them finding out the
lowest common denominator. That then becomes match_list[0]. */ lowest common denominator. That then becomes match_list[0]. */
if (matches == 0) if (match_list->len == 0)
MC_PTR_FREE (match_list); /* There were no matches. */
else
{ {
/* If only one match, just use that. */ /* There were no matches. */
if (matches == 1) g_ptr_array_free (match_list, TRUE);
{ return NULL;
match_list[0] = match_list[1];
match_list[1] = NULL;
} }
else
{
size_t i = 1;
int low = 4096; /* Count of max-matched characters. */
size_t j;
qsort (match_list + 1, matches, sizeof (char *), match_compare); /* If only one match, just use that. */
if (match_list->len > 1)
{
size_t i, j;
size_t low = 4096; /* Count of max-matched characters. */
g_ptr_array_sort (match_list, match_compare);
/* And compare each member of the list with /* And compare each member of the list with
the next, finding out where they stop matching. the next, finding out where they stop matching.
If we find two equal strings, we have to put one away... */ If we find two equal strings, we have to put one away... */
for (i = 0, j = 1; j < match_list->len;)
j = i + 1; {
while (j < matches + 1) char *si, *sj, *mi;
si = g_ptr_array_index (match_list, i);
sj = g_ptr_array_index (match_list, j);
mi = si;
while (si[0] != '\0' && sj[0] != '\0')
{ {
char *si, *sj;
char *ni, *nj; char *ni, *nj;
for (si = match_list[i], sj = match_list[j]; si[0] != '\0' && sj[0] != '\0';)
{
ni = str_get_next_char (si); ni = str_get_next_char (si);
nj = str_get_next_char (sj); nj = str_get_next_char (sj);
if (ni - si != nj - sj) if (ni - si != nj - sj || strncmp (si, sj, ni - si) != 0)
break;
if (strncmp (si, sj, ni - si) != 0)
break; break;
si = ni; si = ni;
@ -768,24 +750,21 @@ completion_matches (const char *text, CompletionFunction entry_function, input_c
} }
if (si[0] == '\0' && sj[0] == '\0') if (si[0] == '\0' && sj[0] == '\0')
{ /* Two equal strings */ {
g_free (match_list[j]); /* Two equal strings */
j++; g_ptr_array_remove_index (match_list, j);
if (j > matches)
break;
continue; /* Look for a run of equal strings */
} }
else if (low > si - match_list[i]) else
low = si - match_list[i]; {
if (i + 1 != j) /* So there's some gap */ low = MIN (low, (size_t) (si - mi));
match_list[i + 1] = match_list[j];
i++; i++;
j++; j++;
} }
matches = i;
match_list[matches + 1] = NULL;
match_list[0] = g_strndup (match_list[1], low);
} }
string = g_ptr_array_index (match_list, 0);
g_ptr_array_insert (match_list, 0, g_strndup (string, low));
} }
return match_list; return match_list;
@ -886,10 +865,10 @@ try_complete_find_start_sign (try_complete_automation_state_t * state)
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
static char ** static GPtrArray *
try_complete_all_possible (try_complete_automation_state_t * state, char *text, int *lc_start) try_complete_all_possible (try_complete_automation_state_t * state, char *text, int *lc_start)
{ {
char **matches = NULL; GPtrArray *matches = NULL;
if (state->in_command_position != 0) if (state->in_command_position != 0)
{ {
@ -1201,32 +1180,37 @@ complete_engine (WInput * in, int what_to_do)
else else
{ {
if ((what_to_do & DO_INSERTION) != 0 if ((what_to_do & DO_INSERTION) != 0
|| ((what_to_do & DO_QUERY) != 0 && in->completions[1] == NULL)) || ((what_to_do & DO_QUERY) != 0 && in->completions->len == 1))
{ {
char *lc_complete = in->completions[0]; const char *lc_complete;
if (!insert_text (in, lc_complete, -1) || in->completions[1] != NULL) lc_complete = g_ptr_array_index (in->completions, 0);
if (!insert_text (in, lc_complete, -1) || in->completions->len > 1)
tty_beep (); tty_beep ();
else else
input_complete_free (in); input_complete_free (in);
} }
if ((what_to_do & DO_QUERY) != 0 && in->completions != NULL && in->completions[1] != NULL) if ((what_to_do & DO_QUERY) != 0 && in->completions != NULL && in->completions->len > 1)
{ {
int maxlen = 0, count = 0, i; int maxlen = 0;
int i;
size_t k;
int count;
int x, y, w, h; int x, y, w, h;
int start_x, start_y; int start_x, start_y;
char **p, *q; char *q;
WDialog *complete_dlg; WDialog *complete_dlg;
WListbox *complete_list; WListbox *complete_list;
for (p = in->completions + 1; *p != NULL; count++, p++) for (k = 1; k < in->completions->len; k++)
{ {
i = str_term_width1 (*p); q = g_ptr_array_index (in->completions, k);
if (i > maxlen) i = str_term_width1 (q);
maxlen = i; maxlen = MAX (maxlen, i);
} }
count = in->completions->len - 1;
start_x = WIDGET (in)->rect.x; start_x = WIDGET (in)->rect.x;
start_y = WIDGET (in)->rect.y; start_y = WIDGET (in)->rect.y;
if (start_y - 2 >= count) if (start_y - 2 >= count)
@ -1262,8 +1246,11 @@ complete_engine (WInput * in, int what_to_do)
complete_list = listbox_new (1, 1, h - 2, w - 2, FALSE, NULL); complete_list = listbox_new (1, 1, h - 2, w - 2, FALSE, NULL);
group_add_widget (GROUP (complete_dlg), complete_list); group_add_widget (GROUP (complete_dlg), complete_list);
for (p = in->completions + 1; *p != NULL; p++) for (k = 1; k < in->completions->len; k++)
listbox_add_item (complete_list, LISTBOX_APPEND_AT_END, 0, *p, NULL, FALSE); {
q = g_ptr_array_index (in->completions, k);
listbox_add_item (complete_list, LISTBOX_APPEND_AT_END, 0, q, NULL, FALSE);
}
i = dlg_run (complete_dlg); i = dlg_run (complete_dlg);
q = NULL; q = NULL;
@ -1290,11 +1277,11 @@ complete_engine (WInput * in, int what_to_do)
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
/** Returns an array of matches, or NULL if none. */ /** Returns an array of matches, or NULL if none. */
char ** GPtrArray *
try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags) try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
{ {
try_complete_automation_state_t state; try_complete_automation_state_t state;
char **matches = NULL; GPtrArray *matches = NULL;
memset (&state, 0, sizeof (state)); memset (&state, 0, sizeof (state));
state.flags = flags; state.flags = flags;
@ -1371,16 +1358,16 @@ try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
(flags & INPUT_COMPLETE_SHELL_ESC) == 0) (flags & INPUT_COMPLETE_SHELL_ESC) == 0)
{ {
/* FIXME: HACK? INPUT_COMPLETE_SHELL_ESC is used only in command line. */ /* FIXME: HACK? INPUT_COMPLETE_SHELL_ESC is used only in command line. */
char **m; size_t i;
for (m = matches; *m != NULL; m++) for (i = 0; i < matches->len; i++)
{ {
char *p; char *p;
p = *m; p = g_ptr_array_index (matches, i);
/* Escape only '?', '*', and '&' symbols as described in the /* Escape only '?', '*', and '&' symbols as described in the
manual page (see a11995e12b88285e044f644904c306ed6c342ad0). */ manual page (see a11995e12b88285e044f644904c306ed6c342ad0). */
*m = str_escape (*m, -1, "?*&", TRUE); g_ptr_array_index (matches, i) = str_escape (p, -1, "?*&", TRUE);
g_free (p); g_free (p);
} }
} }
@ -1456,8 +1443,11 @@ input_complete (WInput * in)
void void
input_complete_free (WInput * in) input_complete_free (WInput * in)
{ {
g_strfreev (in->completions); if (in->completions != NULL)
{
g_ptr_array_free (in->completions, TRUE);
in->completions = NULL; in->completions = NULL;
}
} }
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */

View File

@ -33,7 +33,7 @@
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
void complete_engine_fill_completions (WInput * in); void complete_engine_fill_completions (WInput * in);
char **try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags); GPtrArray *try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags);
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
@ -47,10 +47,10 @@ static int try_complete__lc_end__captured;
static input_complete_t try_complete__flags__captured; static input_complete_t try_complete__flags__captured;
/* @ThenReturnValue */ /* @ThenReturnValue */
static char **try_complete__return_value; static GPtrArray *try_complete__return_value;
/* @Mock */ /* @Mock */
char ** GPtrArray *
try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags) try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
{ {
try_complete__text__captured = g_strdup (text); try_complete__text__captured = g_strdup (text);