mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-22 12:32:40 +03:00
Ticket #55: savannah: tab completion vs. spaces and escaping
* Split big functions. * Add unit tests Signed-off-by: Slava Zanko <slavazanko@gmail.com>
This commit is contained in:
parent
74d71e7523
commit
9935acea33
@ -644,6 +644,7 @@ tests/lib/Makefile
|
|||||||
tests/lib/mcconfig/Makefile
|
tests/lib/mcconfig/Makefile
|
||||||
tests/lib/search/Makefile
|
tests/lib/search/Makefile
|
||||||
tests/lib/vfs/Makefile
|
tests/lib/vfs/Makefile
|
||||||
|
tests/lib/widget/Makefile
|
||||||
tests/src/Makefile
|
tests/src/Makefile
|
||||||
tests/src/filemanager/Makefile
|
tests/src/filemanager/Makefile
|
||||||
])
|
])
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
(Let mc type for you...)
|
(Let mc type for you...)
|
||||||
|
|
||||||
Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
||||||
2007, 2011
|
2007, 2011, 2013
|
||||||
the Free Software Foundation, Inc.
|
the Free Software Foundation, Inc.
|
||||||
|
|
||||||
Written by:
|
Written by:
|
||||||
Jakub Jelinek, 1995
|
Jakub Jelinek, 1995
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2013
|
||||||
|
|
||||||
This file is part of the Midnight Commander.
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
@ -79,6 +80,17 @@ extern char **environ;
|
|||||||
|
|
||||||
typedef char *CompletionFunction (const char *text, int state, input_complete_t flags);
|
typedef char *CompletionFunction (const char *text, int state, input_complete_t flags);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
size_t in_command_position;
|
||||||
|
char *word;
|
||||||
|
char *p;
|
||||||
|
char *q;
|
||||||
|
char *r;
|
||||||
|
gboolean is_cd;
|
||||||
|
input_complete_t flags;
|
||||||
|
} try_complete_automation_state_t;
|
||||||
|
|
||||||
/*** file scope variables ************************************************************************/
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
static char **hosts = NULL;
|
static char **hosts = NULL;
|
||||||
@ -94,6 +106,9 @@ static int end = 0;
|
|||||||
/*** file scope functions ************************************************************************/
|
/*** file scope functions ************************************************************************/
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
char **try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags);
|
||||||
|
void complete_engine_fill_completions (WInput * in);
|
||||||
|
|
||||||
#ifdef DO_COMPLETION_DEBUG
|
#ifdef DO_COMPLETION_DEBUG
|
||||||
/**
|
/**
|
||||||
* Useful to print/debug completion flags
|
* Useful to print/debug completion flags
|
||||||
@ -819,155 +834,114 @@ check_is_cd (const char *text, int lc_start, input_complete_t flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
/** Returns an array of matches, or NULL if none. */
|
|
||||||
static char **
|
static void
|
||||||
try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
|
try_complete_commands_prepare (try_complete_automation_state_t * state, char *text, int *lc_start)
|
||||||
{
|
{
|
||||||
size_t in_command_position = 0;
|
const char *command_separator_chars = ";|&{(`";
|
||||||
char *word;
|
char *ti;
|
||||||
char **matches = NULL;
|
|
||||||
char *p = NULL, *q = NULL, *r = NULL;
|
|
||||||
gboolean is_cd;
|
|
||||||
|
|
||||||
SHOW_C_CTX ("try_complete");
|
if (*lc_start == 0)
|
||||||
word = g_strndup (text + *lc_start, *lc_end - *lc_start);
|
ti = text;
|
||||||
|
else
|
||||||
is_cd = check_is_cd (text, *lc_start, flags);
|
|
||||||
|
|
||||||
/* Determine if this could be a command word. It is if it appears at
|
|
||||||
the start of the line (ignoring preceding whitespace), or if it
|
|
||||||
appears after a character that separates commands. And we have to
|
|
||||||
be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
|
|
||||||
if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS))
|
|
||||||
{
|
{
|
||||||
const char *command_separator_chars = ";|&{(`";
|
ti = str_get_prev_char (&text[*lc_start]);
|
||||||
char *ti;
|
while (ti > text && (ti[0] == ' ' || ti[0] == '\t'))
|
||||||
|
str_prev_char (&ti);
|
||||||
if (*lc_start == 0)
|
|
||||||
ti = text;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ti = str_get_prev_char (&text[*lc_start]);
|
|
||||||
while (ti > text && (ti[0] == ' ' || ti[0] == '\t'))
|
|
||||||
str_prev_char (&ti);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ti == text)
|
|
||||||
in_command_position++;
|
|
||||||
else if (strchr (command_separator_chars, ti[0]) != NULL)
|
|
||||||
{
|
|
||||||
int this_char, prev_char;
|
|
||||||
|
|
||||||
in_command_position++;
|
|
||||||
|
|
||||||
if (ti != text)
|
|
||||||
{
|
|
||||||
/* Handle the two character tokens `>&', `<&', and `>|'.
|
|
||||||
We are not in a command position after one of these. */
|
|
||||||
this_char = ti[0];
|
|
||||||
prev_char = str_get_prev_char (ti)[0];
|
|
||||||
|
|
||||||
/* Quoted */
|
|
||||||
if ((this_char == '&' && (prev_char == '<' || prev_char == '>'))
|
|
||||||
|| (this_char == '|' && prev_char == '>') || (ti != text
|
|
||||||
&& str_get_prev_char (ti)[0] ==
|
|
||||||
'\\'))
|
|
||||||
in_command_position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & INPUT_COMPLETE_COMMANDS)
|
if (ti == text)
|
||||||
p = strrchr (word, '`');
|
state->in_command_position++;
|
||||||
if (flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES))
|
else if (strchr (command_separator_chars, ti[0]) != NULL)
|
||||||
{
|
{
|
||||||
q = strrchr (word, '$');
|
int this_char, prev_char;
|
||||||
|
|
||||||
|
state->in_command_position++;
|
||||||
|
|
||||||
|
if (ti != text)
|
||||||
|
{
|
||||||
|
/* Handle the two character tokens `>&', `<&', and `>|'.
|
||||||
|
We are not in a command position after one of these. */
|
||||||
|
this_char = ti[0];
|
||||||
|
prev_char = str_get_prev_char (ti)[0];
|
||||||
|
|
||||||
|
/* Quoted */
|
||||||
|
if ((this_char == '&' && (prev_char == '<' || prev_char == '>'))
|
||||||
|
|| (this_char == '|' && prev_char == '>') || (ti != text
|
||||||
|
&& str_get_prev_char (ti)[0] == '\\'))
|
||||||
|
state->in_command_position = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static void
|
||||||
|
try_complete_find_start_sign (try_complete_automation_state_t * state)
|
||||||
|
{
|
||||||
|
if (state->flags & INPUT_COMPLETE_COMMANDS)
|
||||||
|
state->p = strrchr (state->word, '`');
|
||||||
|
if (state->flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES))
|
||||||
|
{
|
||||||
|
state->q = strrchr (state->word, '$');
|
||||||
|
|
||||||
/* don't substitute variable in \$ case */
|
/* don't substitute variable in \$ case */
|
||||||
if (q != NULL && q != word && q[-1] == '\\')
|
if (strutils_is_char_escaped (state->word, state->q))
|
||||||
{
|
{
|
||||||
size_t qlen;
|
size_t qlen;
|
||||||
|
|
||||||
qlen = strlen (q);
|
qlen = strlen (state->q);
|
||||||
/* drop '\\' */
|
/* drop '\\' */
|
||||||
memmove (q - 1, q, qlen + 1);
|
memmove (state->q - 1, state->q, qlen + 1);
|
||||||
/* adjust flags */
|
/* adjust flags */
|
||||||
flags &= ~INPUT_COMPLETE_VARIABLES;
|
state->flags &= ~INPUT_COMPLETE_VARIABLES;
|
||||||
q = NULL;
|
state->q = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flags & INPUT_COMPLETE_HOSTNAMES)
|
if (state->flags & INPUT_COMPLETE_HOSTNAMES)
|
||||||
r = strrchr (word, '@');
|
state->r = strrchr (state->word, '@');
|
||||||
if (q && q[1] == '(' && (flags & INPUT_COMPLETE_COMMANDS))
|
if (state->q && state->q[1] == '(' && (state->flags & INPUT_COMPLETE_COMMANDS))
|
||||||
{
|
{
|
||||||
if (q > p)
|
if (state->q > state->p)
|
||||||
p = str_get_next_char (q);
|
state->p = str_get_next_char (state->q);
|
||||||
q = NULL;
|
state->q = NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Command substitution? */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
if (p > q && p > r)
|
|
||||||
{
|
|
||||||
SHOW_C_CTX ("try_complete:cmd_backq_subst");
|
|
||||||
matches = completion_matches (str_cget_next_char (p),
|
|
||||||
command_completion_function,
|
|
||||||
flags & (~INPUT_COMPLETE_FILENAMES));
|
|
||||||
if (matches)
|
|
||||||
*lc_start += str_get_next_char (p) - word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Variable name? */
|
static char **
|
||||||
else if (q > p && q > r)
|
try_complete_all_possible (try_complete_automation_state_t * state, char *text, int *lc_start)
|
||||||
{
|
{
|
||||||
SHOW_C_CTX ("try_complete:var_subst");
|
char **matches = NULL;
|
||||||
matches = completion_matches (q, variable_completion_function, flags);
|
|
||||||
if (matches)
|
|
||||||
*lc_start += q - word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Starts with '@', then look through the known hostnames for
|
if (state->in_command_position != 0)
|
||||||
completion first. */
|
|
||||||
else if (r > p && r > q)
|
|
||||||
{
|
|
||||||
SHOW_C_CTX ("try_complete:host_subst");
|
|
||||||
matches = completion_matches (r, hostname_completion_function, flags);
|
|
||||||
if (matches)
|
|
||||||
*lc_start += r - word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Starts with `~' and there is no slash in the word, then
|
|
||||||
try completing this word as a username. */
|
|
||||||
if (!matches && *word == '~' && (flags & INPUT_COMPLETE_USERNAMES) && !strchr (word, PATH_SEP))
|
|
||||||
{
|
|
||||||
SHOW_C_CTX ("try_complete:user_subst");
|
|
||||||
matches = completion_matches (word, username_completion_function, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* And finally if this word is in a command position, then
|
|
||||||
complete over possible command names, including aliases, functions,
|
|
||||||
and command names. */
|
|
||||||
if (!matches && in_command_position != 0)
|
|
||||||
{
|
{
|
||||||
SHOW_C_CTX ("try_complete:cmd_subst");
|
SHOW_C_CTX ("try_complete:cmd_subst");
|
||||||
matches =
|
matches =
|
||||||
completion_matches (word, command_completion_function,
|
completion_matches (state->word, command_completion_function,
|
||||||
flags & (~INPUT_COMPLETE_FILENAMES));
|
state->flags & (~INPUT_COMPLETE_FILENAMES));
|
||||||
}
|
}
|
||||||
|
else if ((state->flags & INPUT_COMPLETE_FILENAMES) != 0)
|
||||||
else if (!matches && (flags & INPUT_COMPLETE_FILENAMES))
|
|
||||||
{
|
{
|
||||||
if (is_cd)
|
if (state->is_cd)
|
||||||
flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
|
state->flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
|
||||||
SHOW_C_CTX ("try_complete:filename_subst_1");
|
SHOW_C_CTX ("try_complete:filename_subst_1");
|
||||||
matches = completion_matches (word, filename_completion_function, flags);
|
matches = completion_matches (state->word, filename_completion_function, state->flags);
|
||||||
if (!matches && is_cd && *word != PATH_SEP && *word != '~')
|
|
||||||
|
if (matches == NULL && state->is_cd && *state->word != PATH_SEP && *state->word != '~')
|
||||||
{
|
{
|
||||||
q = text + *lc_start;
|
state->q = text + *lc_start;
|
||||||
for (p = text; *p && p < q && (*p == ' ' || *p == '\t'); str_next_char (&p));
|
for (state->p = text;
|
||||||
if (!strncmp (p, "cd", 2))
|
*state->p && state->p < state->q && (*state->p == ' ' || *state->p == '\t');
|
||||||
for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); str_next_char (&p));
|
str_next_char (&state->p))
|
||||||
if (p == q)
|
;
|
||||||
|
if (!strncmp (state->p, "cd", 2))
|
||||||
|
for (state->p += 2;
|
||||||
|
*state->p && state->p < state->q && (*state->p == ' ' || *state->p == '\t');
|
||||||
|
str_next_char (&state->p))
|
||||||
|
;
|
||||||
|
if (state->p == state->q)
|
||||||
{
|
{
|
||||||
char *const cdpath_ref = g_strdup (getenv ("CDPATH"));
|
char *const cdpath_ref = g_strdup (getenv ("CDPATH"));
|
||||||
char *cdpath = cdpath_ref;
|
char *cdpath = cdpath_ref;
|
||||||
@ -986,10 +960,12 @@ try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
|
|||||||
*s = 0;
|
*s = 0;
|
||||||
if (*cdpath)
|
if (*cdpath)
|
||||||
{
|
{
|
||||||
r = mc_build_filename (cdpath, word, NULL);
|
state->r = mc_build_filename (cdpath, state->word, NULL);
|
||||||
SHOW_C_CTX ("try_complete:filename_subst_2");
|
SHOW_C_CTX ("try_complete:filename_subst_2");
|
||||||
matches = completion_matches (r, filename_completion_function, flags);
|
matches =
|
||||||
g_free (r);
|
completion_matches (state->r, filename_completion_function,
|
||||||
|
state->flags);
|
||||||
|
g_free (state->r);
|
||||||
}
|
}
|
||||||
*s = c;
|
*s = c;
|
||||||
cdpath = str_get_next_char (s);
|
cdpath = str_get_next_char (s);
|
||||||
@ -998,9 +974,6 @@ try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free (word);
|
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1224,43 +1197,16 @@ query_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
/** Returns 1 if the user would like to see us again */
|
|
||||||
|
|
||||||
|
/** Returns 1 if the user would like to see us again */
|
||||||
static int
|
static int
|
||||||
complete_engine (WInput * in, int what_to_do)
|
complete_engine (WInput * in, int what_to_do)
|
||||||
{
|
{
|
||||||
if (in->completions != NULL && str_offset_to_pos (in->buffer, in->point) != end)
|
if (in->completions != NULL && str_offset_to_pos (in->buffer, in->point) != end)
|
||||||
input_free_completions (in);
|
input_free_completions (in);
|
||||||
|
|
||||||
if (in->completions == NULL)
|
if (in->completions == NULL)
|
||||||
{
|
complete_engine_fill_completions (in);
|
||||||
char *s;
|
|
||||||
|
|
||||||
end = str_offset_to_pos (in->buffer, in->point);
|
|
||||||
|
|
||||||
s = in->buffer;
|
|
||||||
if (in->point != 0)
|
|
||||||
{
|
|
||||||
/* get symbol before in->point */
|
|
||||||
size_t i;
|
|
||||||
for (i = in->point - 1; i > 0; i--)
|
|
||||||
str_next_char (&s);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; s >= in->buffer; str_prev_char (&s))
|
|
||||||
{
|
|
||||||
start = s - in->buffer;
|
|
||||||
if (strchr (" \t;|<>", *s) != NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start < end)
|
|
||||||
{
|
|
||||||
str_next_char (&s);
|
|
||||||
start = s - in->buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in->completions != NULL)
|
if (in->completions != NULL)
|
||||||
{
|
{
|
||||||
@ -1356,6 +1302,117 @@ complete_engine (WInput * in, int what_to_do)
|
|||||||
/*** public functions ****************************************************************************/
|
/*** public functions ****************************************************************************/
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/** Returns an array of matches, or NULL if none. */
|
||||||
|
char **
|
||||||
|
try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
|
||||||
|
{
|
||||||
|
try_complete_automation_state_t state;
|
||||||
|
char **matches = NULL;
|
||||||
|
|
||||||
|
memset (&state, 0, sizeof (try_complete_automation_state_t));
|
||||||
|
state.flags = flags;
|
||||||
|
|
||||||
|
SHOW_C_CTX ("try_complete");
|
||||||
|
state.word = g_strndup (text + *lc_start, *lc_end - *lc_start);
|
||||||
|
|
||||||
|
state.is_cd = check_is_cd (text, *lc_start, state.flags);
|
||||||
|
|
||||||
|
/* Determine if this could be a command word. It is if it appears at
|
||||||
|
the start of the line (ignoring preceding whitespace), or if it
|
||||||
|
appears after a character that separates commands. And we have to
|
||||||
|
be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
|
||||||
|
if (!state.is_cd && (flags & INPUT_COMPLETE_COMMANDS))
|
||||||
|
try_complete_commands_prepare (&state, text, lc_start);
|
||||||
|
|
||||||
|
try_complete_find_start_sign (&state);
|
||||||
|
|
||||||
|
/* Command substitution? */
|
||||||
|
if (state.p > state.q && state.p > state.r)
|
||||||
|
{
|
||||||
|
SHOW_C_CTX ("try_complete:cmd_backq_subst");
|
||||||
|
matches = completion_matches (str_cget_next_char (state.p),
|
||||||
|
command_completion_function,
|
||||||
|
state.flags & (~INPUT_COMPLETE_FILENAMES));
|
||||||
|
if (matches)
|
||||||
|
*lc_start += str_get_next_char (state.p) - state.word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variable name? */
|
||||||
|
else if (state.q > state.p && state.q > state.r)
|
||||||
|
{
|
||||||
|
SHOW_C_CTX ("try_complete:var_subst");
|
||||||
|
matches = completion_matches (state.q, variable_completion_function, state.flags);
|
||||||
|
if (matches)
|
||||||
|
*lc_start += state.q - state.word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Starts with '@', then look through the known hostnames for
|
||||||
|
completion first. */
|
||||||
|
else if (state.r > state.p && state.r > state.q)
|
||||||
|
{
|
||||||
|
SHOW_C_CTX ("try_complete:host_subst");
|
||||||
|
matches = completion_matches (state.r, hostname_completion_function, state.flags);
|
||||||
|
if (matches)
|
||||||
|
*lc_start += state.r - state.word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Starts with `~' and there is no slash in the word, then
|
||||||
|
try completing this word as a username. */
|
||||||
|
if (!matches && *state.word == '~' && (state.flags & INPUT_COMPLETE_USERNAMES)
|
||||||
|
&& !strchr (state.word, PATH_SEP))
|
||||||
|
{
|
||||||
|
SHOW_C_CTX ("try_complete:user_subst");
|
||||||
|
matches = completion_matches (state.word, username_completion_function, state.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And finally if this word is in a command position, then
|
||||||
|
complete over possible command names, including aliases, functions,
|
||||||
|
and command names. */
|
||||||
|
if (matches == NULL)
|
||||||
|
matches = try_complete_all_possible (&state, text, lc_start);
|
||||||
|
|
||||||
|
g_free (state.word);
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
complete_engine_fill_completions (WInput * in)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
end = str_offset_to_pos (in->buffer, in->point);
|
||||||
|
|
||||||
|
s = in->buffer;
|
||||||
|
if (in->point != 0)
|
||||||
|
{
|
||||||
|
/* get symbol before in->point */
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = in->point - 1; i > 0; i--)
|
||||||
|
str_next_char (&s);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; s >= in->buffer; str_prev_char (&s))
|
||||||
|
{
|
||||||
|
start = s - in->buffer;
|
||||||
|
if (strchr (" \t;|<>", *s) != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < end)
|
||||||
|
{
|
||||||
|
str_next_char (&s);
|
||||||
|
start = s - in->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* declared in lib/widget/input.h */
|
/* declared in lib/widget/input.h */
|
||||||
void
|
void
|
||||||
complete (WInput * in)
|
complete (WInput * in)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
SUBDIRS = . mcconfig search vfs
|
SUBDIRS = . mcconfig search vfs widget
|
||||||
|
|
||||||
AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) @CHECK_CFLAGS@
|
AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) @CHECK_CFLAGS@
|
||||||
|
|
||||||
|
19
tests/lib/widget/Makefile.am
Normal file
19
tests/lib/widget/Makefile.am
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
AM_CPPFLAGS = \
|
||||||
|
$(GLIB_CFLAGS) \
|
||||||
|
-I$(top_srcdir) \
|
||||||
|
-I$(top_srcdir)/lib/vfs \
|
||||||
|
@CHECK_CFLAGS@
|
||||||
|
|
||||||
|
AM_LDFLAGS = @TESTS_LDFLAGS@
|
||||||
|
|
||||||
|
LIBS=@CHECK_LIBS@ \
|
||||||
|
$(top_builddir)/lib/libmc.la
|
||||||
|
|
||||||
|
TESTS = \
|
||||||
|
complete_engine
|
||||||
|
|
||||||
|
check_PROGRAMS = $(TESTS)
|
||||||
|
|
||||||
|
complete_engine_SOURCES = \
|
||||||
|
complete_engine.c
|
233
tests/lib/widget/complete_engine.c
Normal file
233
tests/lib/widget/complete_engine.c
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
lib/widget - tests for autocomplete feature
|
||||||
|
|
||||||
|
Copyright (C) 2013
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2013
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TEST_SUITE_NAME "/lib/widget"
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
#include "lib/strutil.h"
|
||||||
|
#include "lib/widget.h"
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void complete_engine_fill_completions (WInput * in);
|
||||||
|
char **try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags);
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* @CapturedValue */
|
||||||
|
static char *try_complete__text__captured;
|
||||||
|
/* @CapturedValue */
|
||||||
|
static int try_complete__lc_start__captured;
|
||||||
|
/* @CapturedValue */
|
||||||
|
static int try_complete__lc_end__captured;
|
||||||
|
/* @CapturedValue */
|
||||||
|
static input_complete_t try_complete__flags__captured;
|
||||||
|
|
||||||
|
/* @ThenReturnValue */
|
||||||
|
static char **try_complete__return_value;
|
||||||
|
|
||||||
|
/* @Mock */
|
||||||
|
char **
|
||||||
|
try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
|
||||||
|
{
|
||||||
|
try_complete__text__captured = g_strdup (text);
|
||||||
|
try_complete__lc_start__captured = *lc_start;
|
||||||
|
try_complete__lc_end__captured = *lc_end;
|
||||||
|
try_complete__flags__captured = flags;
|
||||||
|
|
||||||
|
return try_complete__return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
try_complete__init (void)
|
||||||
|
{
|
||||||
|
try_complete__text__captured = NULL;
|
||||||
|
try_complete__lc_start__captured = 0;
|
||||||
|
try_complete__lc_end__captured = 0;
|
||||||
|
try_complete__flags__captured = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
try_complete__deinit (void)
|
||||||
|
{
|
||||||
|
g_free (try_complete__text__captured);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* @Before */
|
||||||
|
static void
|
||||||
|
setup (void)
|
||||||
|
{
|
||||||
|
str_init_strings (NULL);
|
||||||
|
try_complete__init ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* @After */
|
||||||
|
static void
|
||||||
|
teardown (void)
|
||||||
|
{
|
||||||
|
try_complete__deinit ();
|
||||||
|
str_uninit_strings ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* @DataSource("test_complete_engine_fill_completions_ds") */
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
static const struct test_complete_engine_fill_completions_ds
|
||||||
|
{
|
||||||
|
const char *input_buffer;
|
||||||
|
const int input_point;
|
||||||
|
const input_complete_t input_completion_flags;
|
||||||
|
int expected_start;
|
||||||
|
int expected_end;
|
||||||
|
} test_complete_engine_fill_completions_ds[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"string",
|
||||||
|
3,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some string",
|
||||||
|
7,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some\tstring",
|
||||||
|
7,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some;string",
|
||||||
|
7,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some|string",
|
||||||
|
7,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some<string",
|
||||||
|
7,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some>string",
|
||||||
|
7,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"some!@#$%^&*()_\\+~`\"',./?:string",
|
||||||
|
30,
|
||||||
|
INPUT_COMPLETE_DEFAULT,
|
||||||
|
0,
|
||||||
|
30
|
||||||
|
},
|
||||||
|
};
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
// " \t;|<>"
|
||||||
|
|
||||||
|
/* @Test(dataSource = "test_complete_engine_fill_completions_ds") */
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
START_TEST (test_complete_engine_fill_completions)
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
{
|
||||||
|
/* given */
|
||||||
|
|
||||||
|
WInput *w_input;
|
||||||
|
const struct test_complete_engine_fill_completions_ds *data =
|
||||||
|
&test_complete_engine_fill_completions_ds[_i];
|
||||||
|
|
||||||
|
w_input = g_new (WInput, 1);
|
||||||
|
w_input->buffer = g_strdup (data->input_buffer);
|
||||||
|
w_input->point = data->input_point;
|
||||||
|
w_input->completion_flags = data->input_completion_flags;
|
||||||
|
|
||||||
|
/* when */
|
||||||
|
complete_engine_fill_completions (w_input);
|
||||||
|
|
||||||
|
/* then */
|
||||||
|
g_assert_cmpstr (try_complete__text__captured, ==, data->input_buffer);
|
||||||
|
ck_assert_int_eq (try_complete__lc_start__captured, data->expected_start);
|
||||||
|
ck_assert_int_eq (try_complete__lc_end__captured, data->expected_end);
|
||||||
|
ck_assert_int_eq (try_complete__flags__captured, data->input_completion_flags);
|
||||||
|
|
||||||
|
}
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
END_TEST
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
int number_failed;
|
||||||
|
|
||||||
|
Suite *s = suite_create (TEST_SUITE_NAME);
|
||||||
|
TCase *tc_core = tcase_create ("Core");
|
||||||
|
SRunner *sr;
|
||||||
|
|
||||||
|
tcase_add_checked_fixture (tc_core, setup, teardown);
|
||||||
|
|
||||||
|
/* Add new tests here: *************** */
|
||||||
|
tcase_add_loop_test (tc_core, test_complete_engine_fill_completions, 0,
|
||||||
|
sizeof (test_complete_engine_fill_completions_ds) /
|
||||||
|
sizeof (test_complete_engine_fill_completions_ds[0]));
|
||||||
|
/* *********************************** */
|
||||||
|
|
||||||
|
suite_add_tcase (s, tc_core);
|
||||||
|
sr = srunner_create (s);
|
||||||
|
srunner_set_log (sr, "complete_engine.log");
|
||||||
|
srunner_run_all (sr, CK_NORMAL);
|
||||||
|
number_failed = srunner_ntests_failed (sr);
|
||||||
|
srunner_free (sr);
|
||||||
|
return (number_failed == 0) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
Loading…
Reference in New Issue
Block a user