mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
Break out COMPLETE_NO_CASE and COMPLETE_REPLACES_TOKEN into separate flags, in preparation for upcoming fuzzy completion work
This commit is contained in:
parent
b2012467b3
commit
4d19bb17a9
7 changed files with 131 additions and 73 deletions
|
@ -545,7 +545,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||||
recursion_level++;
|
recursion_level++;
|
||||||
|
|
||||||
std::vector<completion_t> comp;
|
std::vector<completion_t> comp;
|
||||||
complete(do_complete_param, comp, COMPLETE_DEFAULT);
|
complete(do_complete_param, comp, COMPLETION_REQUEST_DEFAULT);
|
||||||
|
|
||||||
for (size_t i=0; i< comp.size() ; i++)
|
for (size_t i=0; i< comp.size() ; i++)
|
||||||
{
|
{
|
||||||
|
@ -553,7 +553,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||||
|
|
||||||
const wchar_t *prepend;
|
const wchar_t *prepend;
|
||||||
|
|
||||||
if (next.flags & COMPLETE_NO_CASE)
|
if (next.flags & COMPLETE_REPLACES_TOKEN)
|
||||||
{
|
{
|
||||||
prepend = L"";
|
prepend = L"";
|
||||||
}
|
}
|
||||||
|
|
98
complete.cpp
98
complete.cpp
|
@ -104,14 +104,25 @@
|
||||||
#define C_(string) (string)
|
#define C_(string) (string)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Testing apparatus */
|
||||||
|
const wcstring_list_t *s_override_variable_names = NULL;
|
||||||
|
|
||||||
/**
|
void complete_set_variable_names(const wcstring_list_t *names)
|
||||||
The maximum amount of time that we're willing to spend doing
|
{
|
||||||
username tilde completion. This special limit has been coded in
|
s_override_variable_names = names;
|
||||||
because user lookup can be extremely slow in cases of a humongous
|
}
|
||||||
LDAP database. (Google, I'm looking at you)
|
|
||||||
*/
|
static inline wcstring_list_t complete_get_variable_names(void)
|
||||||
#define MAX_USER_LOOKUP_TIME 0.2
|
{
|
||||||
|
if (s_override_variable_names != NULL)
|
||||||
|
{
|
||||||
|
return *s_override_variable_names;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return env_get_names(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Struct describing a completion option entry.
|
Struct describing a completion option entry.
|
||||||
|
@ -326,7 +337,7 @@ void sort_completions(std::vector<completion_t> &completions)
|
||||||
/** Class representing an attempt to compute completions */
|
/** Class representing an attempt to compute completions */
|
||||||
class completer_t
|
class completer_t
|
||||||
{
|
{
|
||||||
const complete_type_t type;
|
const completion_request_flags_t flags;
|
||||||
const wcstring initial_cmd;
|
const wcstring initial_cmd;
|
||||||
std::vector<completion_t> completions;
|
std::vector<completion_t> completions;
|
||||||
wcstring_list_t commands_to_load;
|
wcstring_list_t commands_to_load;
|
||||||
|
@ -335,9 +346,31 @@ class completer_t
|
||||||
typedef std::map<wcstring, bool> condition_cache_t;
|
typedef std::map<wcstring, bool> condition_cache_t;
|
||||||
condition_cache_t condition_cache;
|
condition_cache_t condition_cache;
|
||||||
|
|
||||||
|
enum complete_type_t
|
||||||
|
{
|
||||||
|
COMPLETE_DEFAULT,
|
||||||
|
COMPLETE_AUTOSUGGEST
|
||||||
|
};
|
||||||
|
|
||||||
|
complete_type_t type() const
|
||||||
|
{
|
||||||
|
return (flags & COMPLETION_REQUEST_AUTOSUGGESTION) ? COMPLETE_AUTOSUGGEST : COMPLETE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wants_descriptions() const
|
||||||
|
{
|
||||||
|
return !! (flags & COMPLETION_REQUEST_DESCRIPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fuzzy() const
|
||||||
|
{
|
||||||
|
return !! (flags & COMPLETION_REQUEST_FUZZY_MATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
completer_t(const wcstring &c, complete_type_t t) :
|
completer_t(const wcstring &c, completion_request_flags_t f) :
|
||||||
type(t),
|
flags(f),
|
||||||
initial_cmd(c)
|
initial_cmd(c)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -389,7 +422,7 @@ public:
|
||||||
{
|
{
|
||||||
/* Never do command substitution in autosuggestions. Sadly, we also can't yet do job expansion because it's not thread safe. */
|
/* Never do command substitution in autosuggestions. Sadly, we also can't yet do job expansion because it's not thread safe. */
|
||||||
expand_flags_t result = 0;
|
expand_flags_t result = 0;
|
||||||
if (type == COMPLETE_AUTOSUGGEST)
|
if (this->type() == COMPLETE_AUTOSUGGEST)
|
||||||
result |= EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_JOBS;
|
result |= EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_JOBS;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -449,7 +482,7 @@ bool completer_t::condition_test(const wcstring &condition)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->type == COMPLETE_AUTOSUGGEST)
|
if (this->type() == COMPLETE_AUTOSUGGEST)
|
||||||
{
|
{
|
||||||
/* Autosuggestion can't support conditions */
|
/* Autosuggestion can't support conditions */
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1096,8 +1129,6 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
||||||
if (cdpath.missing_or_empty())
|
if (cdpath.missing_or_empty())
|
||||||
cdpath = L".";
|
cdpath = L".";
|
||||||
|
|
||||||
const bool wants_description = (type == COMPLETE_DEFAULT);
|
|
||||||
|
|
||||||
if (str_cmd.find(L'/') != wcstring::npos || str_cmd.at(0) == L'~')
|
if (str_cmd.find(L'/') != wcstring::npos || str_cmd.at(0) == L'~')
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1106,7 +1137,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
||||||
|
|
||||||
if (expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags()) != EXPAND_ERROR)
|
if (expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags()) != EXPAND_ERROR)
|
||||||
{
|
{
|
||||||
if (wants_description)
|
if (this->wants_descriptions())
|
||||||
{
|
{
|
||||||
this->complete_cmd_desc(str_cmd);
|
this->complete_cmd_desc(str_cmd);
|
||||||
}
|
}
|
||||||
|
@ -1144,7 +1175,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
||||||
for (size_t i=prev_count; i< this->completions.size(); i++)
|
for (size_t i=prev_count; i< this->completions.size(); i++)
|
||||||
{
|
{
|
||||||
completion_t &c = this->completions.at(i);
|
completion_t &c = this->completions.at(i);
|
||||||
if (c.flags & COMPLETE_NO_CASE)
|
if (c.flags & COMPLETE_REPLACES_TOKEN)
|
||||||
{
|
{
|
||||||
|
|
||||||
c.completion.erase(0, base_path.size());
|
c.completion.erase(0, base_path.size());
|
||||||
|
@ -1152,7 +1183,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wants_description)
|
if (this->wants_descriptions())
|
||||||
this->complete_cmd_desc(str_cmd);
|
this->complete_cmd_desc(str_cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1205,7 +1236,7 @@ void completer_t::complete_from_args(const wcstring &str,
|
||||||
|
|
||||||
std::vector<completion_t> possible_comp;
|
std::vector<completion_t> possible_comp;
|
||||||
|
|
||||||
bool is_autosuggest = (this->type == COMPLETE_AUTOSUGGEST);
|
bool is_autosuggest = (this->type() == COMPLETE_AUTOSUGGEST);
|
||||||
parser_t parser(is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL, false);
|
parser_t parser(is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL, false);
|
||||||
|
|
||||||
/* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */
|
/* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */
|
||||||
|
@ -1335,11 +1366,11 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop
|
||||||
wcstring cmd, path;
|
wcstring cmd, path;
|
||||||
parse_cmd_string(cmd_orig, path, cmd);
|
parse_cmd_string(cmd_orig, path, cmd);
|
||||||
|
|
||||||
if (this->type == COMPLETE_DEFAULT)
|
if (this->type() == COMPLETE_DEFAULT)
|
||||||
{
|
{
|
||||||
complete_load(cmd, true);
|
complete_load(cmd, true);
|
||||||
}
|
}
|
||||||
else if (this->type == COMPLETE_AUTOSUGGEST)
|
else if (this->type() == COMPLETE_AUTOSUGGEST)
|
||||||
{
|
{
|
||||||
/* Maybe indicate we should try loading this on the main thread */
|
/* Maybe indicate we should try loading this on the main thread */
|
||||||
if (! list_contains_string(this->commands_to_load, cmd) && ! completion_autoloader.has_tried_loading(cmd))
|
if (! list_contains_string(this->commands_to_load, cmd) && ! completion_autoloader.has_tried_loading(cmd))
|
||||||
|
@ -1514,11 +1545,14 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
complete_flags_t flags = 0;
|
complete_flags_t flags = 0;
|
||||||
|
|
||||||
|
|
||||||
if (match)
|
if (match)
|
||||||
|
{
|
||||||
offset = wcslen(str);
|
offset = wcslen(str);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
flags = COMPLETE_NO_CASE;
|
{
|
||||||
|
flags = COMPLETE_REPLACES_TOKEN | COMPLETE_CASE_INSENSITIVE;
|
||||||
|
}
|
||||||
|
|
||||||
has_arg = ! o->comp.empty();
|
has_arg = ! o->comp.empty();
|
||||||
req_arg = (o->result_mode & NO_COMMON);
|
req_arg = (o->result_mode & NO_COMMON);
|
||||||
|
@ -1580,7 +1614,7 @@ void completer_t::complete_param_expand(const wcstring &sstr, bool do_file)
|
||||||
flags |= EXPAND_SKIP_WILDCARDS;
|
flags |= EXPAND_SKIP_WILDCARDS;
|
||||||
|
|
||||||
/* Squelch file descriptions per issue 254 */
|
/* Squelch file descriptions per issue 254 */
|
||||||
if (type == COMPLETE_AUTOSUGGEST || do_file)
|
if (this->type() == COMPLETE_AUTOSUGGEST || do_file)
|
||||||
flags |= EXPAND_NO_DESCRIPTIONS;
|
flags |= EXPAND_NO_DESCRIPTIONS;
|
||||||
|
|
||||||
if (expand_string(comp_str,
|
if (expand_string(comp_str,
|
||||||
|
@ -1608,9 +1642,8 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset)
|
||||||
const wchar_t *var = &whole_var[start_offset];
|
const wchar_t *var = &whole_var[start_offset];
|
||||||
size_t varlen = wcslen(var);
|
size_t varlen = wcslen(var);
|
||||||
int res = 0;
|
int res = 0;
|
||||||
bool wants_description = (type != COMPLETE_AUTOSUGGEST);
|
|
||||||
|
|
||||||
const wcstring_list_t names = env_get_names(0);
|
const wcstring_list_t names = complete_get_variable_names();
|
||||||
for (size_t i=0; i<names.size(); i++)
|
for (size_t i=0; i<names.size(); i++)
|
||||||
{
|
{
|
||||||
const wcstring & env_name = names.at(i);
|
const wcstring & env_name = names.at(i);
|
||||||
|
@ -1640,18 +1673,18 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset)
|
||||||
{
|
{
|
||||||
comp.append(whole_var, start_offset);
|
comp.append(whole_var, start_offset);
|
||||||
comp.append(env_name);
|
comp.append(env_name);
|
||||||
flags = COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE;
|
flags = COMPLETE_CASE_INSENSITIVE | COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
wcstring desc;
|
wcstring desc;
|
||||||
if (wants_description)
|
if (this->wants_descriptions())
|
||||||
{
|
{
|
||||||
env_var_t value_unescaped = env_get_string(env_name);
|
env_var_t value_unescaped = env_get_string(env_name);
|
||||||
if (value_unescaped.missing())
|
if (value_unescaped.missing())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
wcstring value = expand_escape_variable(value_unescaped);
|
wcstring value = expand_escape_variable(value_unescaped);
|
||||||
if (type != COMPLETE_AUTOSUGGEST)
|
if (this->type() != COMPLETE_AUTOSUGGEST)
|
||||||
desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str());
|
desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1744,7 +1777,7 @@ bool completer_t::try_complete_user(const wcstring &str)
|
||||||
append_completion(this->completions,
|
append_completion(this->completions,
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE);
|
COMPLETE_CASE_INSENSITIVE | COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE);
|
||||||
res=1;
|
res=1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1756,11 +1789,12 @@ bool completer_t::try_complete_user(const wcstring &str)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void complete(const wcstring &cmd, std::vector<completion_t> &comps, complete_type_t type, wcstring_list_t *commands_to_load)
|
void complete(const wcstring &cmd, std::vector<completion_t> &comps, completion_request_flags_t flags, wcstring_list_t *commands_to_load)
|
||||||
{
|
{
|
||||||
/* Make our completer */
|
/* Make our completer */
|
||||||
completer_t completer(cmd, type);
|
completer_t completer(cmd, flags);
|
||||||
|
|
||||||
|
const bool fuzzy = !! (flags & COMPLETION_REQUEST_FUZZY_MATCH);
|
||||||
const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end;
|
const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end;
|
||||||
wcstring current_token, prev_token;
|
wcstring current_token, prev_token;
|
||||||
wcstring current_command;
|
wcstring current_command;
|
||||||
|
@ -1969,7 +2003,7 @@ void complete(const wcstring &cmd, std::vector<completion_t> &comps, complete_ty
|
||||||
do_file = false;
|
do_file = false;
|
||||||
|
|
||||||
/* And if we're autosuggesting, and the token is empty, don't do file suggestions */
|
/* And if we're autosuggesting, and the token is empty, don't do file suggestions */
|
||||||
if (type == COMPLETE_AUTOSUGGEST && current_token_unescape.empty())
|
if ((flags & COMPLETION_REQUEST_AUTOSUGGESTION) && current_token_unescape.empty())
|
||||||
do_file = false;
|
do_file = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
35
complete.h
35
complete.h
|
@ -75,25 +75,20 @@ enum
|
||||||
*/
|
*/
|
||||||
COMPLETE_NO_SPACE = 1 << 0,
|
COMPLETE_NO_SPACE = 1 << 0,
|
||||||
|
|
||||||
/**
|
/** This completion is case insensitive. */
|
||||||
This completion is case insensitive.
|
COMPLETE_CASE_INSENSITIVE = 1 << 1,
|
||||||
|
|
||||||
Warning: The contents of the completion_t structure is actually
|
/** This is not the suffix of a token, but replaces it entirely */
|
||||||
different if this flag is set! Specifically, the completion string
|
COMPLETE_REPLACES_TOKEN = 1 << 2,
|
||||||
contains the _entire_ completion token, not merely its suffix.
|
|
||||||
*/
|
|
||||||
COMPLETE_NO_CASE = 1 << 1,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This completion may or may not want a space at the end - guess by
|
This completion may or may not want a space at the end - guess by
|
||||||
checking the last character of the completion.
|
checking the last character of the completion.
|
||||||
*/
|
*/
|
||||||
COMPLETE_AUTO_SPACE = 1 << 2,
|
COMPLETE_AUTO_SPACE = 1 << 3,
|
||||||
|
|
||||||
/**
|
/** This completion should be inserted as-is, without escaping. */
|
||||||
This completion should be inserted as-is, without escaping.
|
COMPLETE_DONT_ESCAPE = 1 << 4
|
||||||
*/
|
|
||||||
COMPLETE_DONT_ESCAPE = 1 << 3
|
|
||||||
};
|
};
|
||||||
typedef int complete_flags_t;
|
typedef int complete_flags_t;
|
||||||
|
|
||||||
|
@ -130,7 +125,7 @@ public:
|
||||||
|
|
||||||
bool is_case_insensitive() const
|
bool is_case_insensitive() const
|
||||||
{
|
{
|
||||||
return !!(flags & COMPLETE_NO_CASE);
|
return !!(flags & COMPLETE_CASE_INSENSITIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Construction. Note: defining these so that they are not inlined reduces the executable size. */
|
/* Construction. Note: defining these so that they are not inlined reduces the executable size. */
|
||||||
|
@ -144,11 +139,13 @@ public:
|
||||||
bool operator != (const completion_t& rhs) const;
|
bool operator != (const completion_t& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum complete_type_t
|
enum {
|
||||||
{
|
COMPLETION_REQUEST_DEFAULT = 0,
|
||||||
COMPLETE_DEFAULT,
|
COMPLETION_REQUEST_AUTOSUGGESTION = 1 << 0, // indicates the completion is for an autosuggestion
|
||||||
COMPLETE_AUTOSUGGEST
|
COMPLETION_REQUEST_DESCRIPTIONS = 1 << 1, // indicates that we want descriptions
|
||||||
|
COMPLETION_REQUEST_FUZZY_MATCH = 1 << 2 // indicates that we don't require a prefix match
|
||||||
};
|
};
|
||||||
|
typedef uint32_t completion_request_flags_t;
|
||||||
|
|
||||||
/** Given a list of completions, returns a list of their completion fields */
|
/** Given a list of completions, returns a list of their completion fields */
|
||||||
wcstring_list_t completions_to_wcstring_list(const std::vector<completion_t> &completions);
|
wcstring_list_t completions_to_wcstring_list(const std::vector<completion_t> &completions);
|
||||||
|
@ -233,7 +230,7 @@ void complete_remove(const wchar_t *cmd,
|
||||||
*/
|
*/
|
||||||
void complete(const wcstring &cmd,
|
void complete(const wcstring &cmd,
|
||||||
std::vector<completion_t> &comp,
|
std::vector<completion_t> &comp,
|
||||||
complete_type_t type,
|
completion_request_flags_t flags,
|
||||||
wcstring_list_t *to_load = NULL);
|
wcstring_list_t *to_load = NULL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -284,5 +281,7 @@ void complete_load(const wcstring &cmd, bool reload);
|
||||||
*/
|
*/
|
||||||
void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc = L"", int flags = 0);
|
void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc = L"", int flags = 0);
|
||||||
|
|
||||||
|
/* Function used for testing */
|
||||||
|
void complete_set_variable_names(const wcstring_list_t *names);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -959,6 +959,25 @@ static void test_colors()
|
||||||
assert(rgb_color_t(L"mooganta").is_none());
|
assert(rgb_color_t(L"mooganta").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_complete(void)
|
||||||
|
{
|
||||||
|
say(L"Testing complete");
|
||||||
|
const wchar_t *name_strs[] = {L"Foo1", L"Foo2", L"Foo3", L"Bar1", L"Bar2", L"Bar3"};
|
||||||
|
size_t count = sizeof name_strs / sizeof *name_strs;
|
||||||
|
const wcstring_list_t names(name_strs, name_strs + count);
|
||||||
|
|
||||||
|
complete_set_variable_names(&names);
|
||||||
|
|
||||||
|
std::vector<completion_t> completions;
|
||||||
|
complete(L"$F", completions, COMPLETION_REQUEST_DEFAULT);
|
||||||
|
assert(completions.size() == 3);
|
||||||
|
assert(completions.at(0).completion == L"oo1");
|
||||||
|
assert(completions.at(1).completion == L"oo2");
|
||||||
|
assert(completions.at(2).completion == L"oo3");
|
||||||
|
|
||||||
|
complete_set_variable_names(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_1_completion(wcstring line, const wcstring &completion, complete_flags_t flags, bool append_only, wcstring expected, long source_line)
|
static void test_1_completion(wcstring line, const wcstring &completion, complete_flags_t flags, bool append_only, wcstring expected, long source_line)
|
||||||
{
|
{
|
||||||
// str is given with a caret, which we use to represent the cursor position
|
// str is given with a caret, which we use to represent the cursor position
|
||||||
|
@ -981,10 +1000,10 @@ static void test_1_completion(wcstring line, const wcstring &completion, complet
|
||||||
assert(cursor_pos == out_cursor_pos);
|
assert(cursor_pos == out_cursor_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_completions()
|
static void test_completion_insertions()
|
||||||
{
|
{
|
||||||
#define TEST_1_COMPLETION(a, b, c, d, e) test_1_completion(a, b, c, d, e, __LINE__)
|
#define TEST_1_COMPLETION(a, b, c, d, e) test_1_completion(a, b, c, d, e, __LINE__)
|
||||||
say(L"Testing completions");
|
say(L"Testing completion insertions");
|
||||||
TEST_1_COMPLETION(L"foo^", L"bar", 0, false, L"foobar ^");
|
TEST_1_COMPLETION(L"foo^", L"bar", 0, false, L"foobar ^");
|
||||||
TEST_1_COMPLETION(L"foo^ baz", L"bar", 0, false, L"foobar ^ baz"); //we really do want to insert two spaces here - otherwise it's hidden by the cursor
|
TEST_1_COMPLETION(L"foo^ baz", L"bar", 0, false, L"foobar ^ baz"); //we really do want to insert two spaces here - otherwise it's hidden by the cursor
|
||||||
TEST_1_COMPLETION(L"'foo^", L"bar", 0, false, L"'foobar' ^");
|
TEST_1_COMPLETION(L"'foo^", L"bar", 0, false, L"'foobar' ^");
|
||||||
|
@ -1006,8 +1025,8 @@ static void test_completions()
|
||||||
TEST_1_COMPLETION(L"'foo\\'^", L"bar", COMPLETE_NO_SPACE, false, L"'foo\\'bar^");
|
TEST_1_COMPLETION(L"'foo\\'^", L"bar", COMPLETE_NO_SPACE, false, L"'foo\\'bar^");
|
||||||
TEST_1_COMPLETION(L"foo\\'^", L"bar", COMPLETE_NO_SPACE, false, L"foo\\'bar^");
|
TEST_1_COMPLETION(L"foo\\'^", L"bar", COMPLETE_NO_SPACE, false, L"foo\\'bar^");
|
||||||
|
|
||||||
TEST_1_COMPLETION(L"foo^", L"bar", COMPLETE_NO_CASE, false, L"bar ^");
|
TEST_1_COMPLETION(L"foo^", L"bar", COMPLETE_CASE_INSENSITIVE | COMPLETE_REPLACES_TOKEN, false, L"bar ^");
|
||||||
TEST_1_COMPLETION(L"'foo^", L"bar", COMPLETE_NO_CASE, false, L"bar ^");
|
TEST_1_COMPLETION(L"'foo^", L"bar", COMPLETE_CASE_INSENSITIVE | COMPLETE_REPLACES_TOKEN, false, L"bar ^");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void perform_one_autosuggestion_test(const wcstring &command, const wcstring &wd, const wcstring &expected, long line)
|
static void perform_one_autosuggestion_test(const wcstring &command, const wcstring &wd, const wcstring &expected, long line)
|
||||||
|
@ -1139,7 +1158,7 @@ void perf_complete()
|
||||||
str[0]=c;
|
str[0]=c;
|
||||||
reader_set_buffer(str, 0);
|
reader_set_buffer(str, 0);
|
||||||
|
|
||||||
complete(str, out, COMPLETE_DEFAULT, NULL);
|
complete(str, out, COMPLETION_REQUEST_DEFAULT, NULL);
|
||||||
|
|
||||||
matches += out.size();
|
matches += out.size();
|
||||||
out.clear();
|
out.clear();
|
||||||
|
@ -1159,7 +1178,7 @@ void perf_complete()
|
||||||
|
|
||||||
reader_set_buffer(str, 0);
|
reader_set_buffer(str, 0);
|
||||||
|
|
||||||
complete(str, out, COMPLETE_DEFAULT, NULL);
|
complete(str, out, COMPLETION_REQUEST_DEFAULT, NULL);
|
||||||
|
|
||||||
matches += out.size();
|
matches += out.size();
|
||||||
out.clear();
|
out.clear();
|
||||||
|
@ -1710,7 +1729,8 @@ int main(int argc, char **argv)
|
||||||
test_word_motion();
|
test_word_motion();
|
||||||
test_is_potential_path();
|
test_is_potential_path();
|
||||||
test_colors();
|
test_colors();
|
||||||
test_completions();
|
test_complete();
|
||||||
|
test_completion_insertions();
|
||||||
test_autosuggestion_combining();
|
test_autosuggestion_combining();
|
||||||
test_autosuggest_suggest_special();
|
test_autosuggest_suggest_special();
|
||||||
history_tests_t::test_history();
|
history_tests_t::test_history();
|
||||||
|
|
19
reader.cpp
19
reader.cpp
|
@ -979,7 +979,7 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag
|
||||||
{
|
{
|
||||||
const wchar_t *val = val_str.c_str();
|
const wchar_t *val = val_str.c_str();
|
||||||
bool add_space = !(flags & COMPLETE_NO_SPACE);
|
bool add_space = !(flags & COMPLETE_NO_SPACE);
|
||||||
bool do_replace = !!(flags & COMPLETE_NO_CASE);
|
bool do_replace = !!(flags & COMPLETE_REPLACES_TOKEN);
|
||||||
bool do_escape = !(flags & COMPLETE_DONT_ESCAPE);
|
bool do_escape = !(flags & COMPLETE_DONT_ESCAPE);
|
||||||
|
|
||||||
const size_t cursor_pos = *inout_cursor_pos;
|
const size_t cursor_pos = *inout_cursor_pos;
|
||||||
|
@ -1134,7 +1134,6 @@ static void run_pager(const wcstring &prefix, int is_quoted, const std::vector<c
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wchar_t *escaped_separator;
|
wchar_t *escaped_separator;
|
||||||
int has_case_sensitive=0;
|
|
||||||
|
|
||||||
if (prefix.empty())
|
if (prefix.empty())
|
||||||
{
|
{
|
||||||
|
@ -1155,10 +1154,15 @@ static void run_pager(const wcstring &prefix, int is_quoted, const std::vector<c
|
||||||
|
|
||||||
escaped_separator = escape(COMPLETE_SEP_STR, 1);
|
escaped_separator = escape(COMPLETE_SEP_STR, 1);
|
||||||
|
|
||||||
|
bool has_case_sensitive = false;
|
||||||
for (size_t i=0; i< comp.size(); i++)
|
for (size_t i=0; i< comp.size(); i++)
|
||||||
{
|
{
|
||||||
const completion_t &el = comp.at(i);
|
const completion_t &el = comp.at(i);
|
||||||
has_case_sensitive |= !(el.flags & COMPLETE_NO_CASE);
|
if (! (el.flags & COMPLETE_CASE_INSENSITIVE))
|
||||||
|
{
|
||||||
|
has_case_sensitive = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i=0; i< comp.size(); i++)
|
for (size_t i=0; i< comp.size(); i++)
|
||||||
|
@ -1170,13 +1174,13 @@ static void run_pager(const wcstring &prefix, int is_quoted, const std::vector<c
|
||||||
wcstring completion_text;
|
wcstring completion_text;
|
||||||
wcstring description_text;
|
wcstring description_text;
|
||||||
|
|
||||||
if (has_case_sensitive && (el.flags & COMPLETE_NO_CASE))
|
if (has_case_sensitive && (el.flags & COMPLETE_CASE_INSENSITIVE))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that an empty completion is perfectly sensible here, e.g. tab-completing 'foo' with a file called 'foo' and another called 'foobar'
|
// Note that an empty completion is perfectly sensible here, e.g. tab-completing 'foo' with a file called 'foo' and another called 'foobar'
|
||||||
if (el.flags & COMPLETE_NO_CASE)
|
if (el.flags & COMPLETE_REPLACES_TOKEN)
|
||||||
{
|
{
|
||||||
if (base_len == -1)
|
if (base_len == -1)
|
||||||
{
|
{
|
||||||
|
@ -1328,7 +1332,7 @@ struct autosuggestion_context_t
|
||||||
|
|
||||||
/* Try normal completions */
|
/* Try normal completions */
|
||||||
std::vector<completion_t> completions;
|
std::vector<completion_t> completions;
|
||||||
complete(search_string, completions, COMPLETE_AUTOSUGGEST, &this->commands_to_load);
|
complete(search_string, completions, COMPLETION_REQUEST_AUTOSUGGESTION, &this->commands_to_load);
|
||||||
if (! completions.empty())
|
if (! completions.empty())
|
||||||
{
|
{
|
||||||
const completion_t &comp = completions.at(0);
|
const completion_t &comp = completions.at(0);
|
||||||
|
@ -3058,7 +3062,8 @@ const wchar_t *reader_readline()
|
||||||
/* Construct a copy of the string from the beginning of the command substitution up to the end of the token we're completing */
|
/* Construct a copy of the string from the beginning of the command substitution up to the end of the token we're completing */
|
||||||
const wcstring buffcpy = wcstring(cmdsub_begin, token_end);
|
const wcstring buffcpy = wcstring(cmdsub_begin, token_end);
|
||||||
|
|
||||||
data->complete_func(buffcpy, comp, COMPLETE_DEFAULT, NULL);
|
//fprintf(stderr, "Complete (%ls)\n", buffcpy.c_str());
|
||||||
|
data->complete_func(buffcpy, comp, COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_DESCRIPTIONS, NULL);
|
||||||
|
|
||||||
/* Munge our completions */
|
/* Munge our completions */
|
||||||
sort_and_make_unique(comp);
|
sort_and_make_unique(comp);
|
||||||
|
|
2
reader.h
2
reader.h
|
@ -163,7 +163,7 @@ void reader_pop();
|
||||||
- The command to be completed as a null terminated array of wchar_t
|
- The command to be completed as a null terminated array of wchar_t
|
||||||
- An array_list_t in which completions will be inserted.
|
- An array_list_t in which completions will be inserted.
|
||||||
*/
|
*/
|
||||||
typedef void (*complete_function_t)(const wcstring &, std::vector<completion_t> &, complete_type_t, wcstring_list_t * lst);
|
typedef void (*complete_function_t)(const wcstring &, std::vector<completion_t> &, completion_request_flags_t, wcstring_list_t * lst);
|
||||||
void reader_set_complete_function(complete_function_t);
|
void reader_set_complete_function(complete_function_t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -225,7 +225,7 @@ static bool wildcard_complete_internal(const wcstring &orig,
|
||||||
wcstring out_completion;
|
wcstring out_completion;
|
||||||
wcstring out_desc = (desc ? desc : L"");
|
wcstring out_desc = (desc ? desc : L"");
|
||||||
|
|
||||||
if (flags & COMPLETE_NO_CASE)
|
if (flags & COMPLETE_REPLACES_TOKEN)
|
||||||
{
|
{
|
||||||
out_completion = orig;
|
out_completion = orig;
|
||||||
}
|
}
|
||||||
|
@ -292,7 +292,7 @@ static bool wildcard_complete_internal(const wcstring &orig,
|
||||||
}
|
}
|
||||||
else if (towlower(*wc) == towlower(*str))
|
else if (towlower(*wc) == towlower(*str))
|
||||||
{
|
{
|
||||||
return wildcard_complete_internal(orig, str+1, wc+1, 0, desc, desc_func, out, flags | COMPLETE_NO_CASE);
|
return wildcard_complete_internal(orig, str+1, wc+1, 0, desc, desc_func, out, flags | COMPLETE_CASE_INSENSITIVE | COMPLETE_REPLACES_TOKEN);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1095,7 +1095,7 @@ int wildcard_expand(const wchar_t *wc,
|
||||||
{
|
{
|
||||||
completion_t &c = out.at(i);
|
completion_t &c = out.at(i);
|
||||||
|
|
||||||
if (c.flags & COMPLETE_NO_CASE)
|
if (c.flags & COMPLETE_REPLACES_TOKEN)
|
||||||
{
|
{
|
||||||
c.completion = format_string(L"%ls%ls%ls", base_dir, wc_base.c_str(), c.completion.c_str());
|
c.completion = format_string(L"%ls%ls%ls", base_dir, wc_base.c_str(), c.completion.c_str());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue