aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-03-05 20:54:16 -0800
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-03-05 20:54:16 -0800
commit4d19bb17a9161fc085e7b308d1ac30ec74186c33 (patch)
treecac5e5e7514ae08c66a43518b531fb86a421d276
parentb2012467b327db8d518f2b01eed3d529609f7349 (diff)
Break out COMPLETE_NO_CASE and COMPLETE_REPLACES_TOKEN into separate flags, in preparation for upcoming fuzzy completion work
-rw-r--r--builtin_complete.cpp4
-rw-r--r--complete.cpp98
-rw-r--r--complete.h37
-rw-r--r--fish_tests.cpp36
-rw-r--r--reader.cpp21
-rw-r--r--reader.h2
-rw-r--r--wildcard.cpp6
7 files changed, 131 insertions, 73 deletions
diff --git a/builtin_complete.cpp b/builtin_complete.cpp
index b4ac2a38..186fb9bc 100644
--- a/builtin_complete.cpp
+++ b/builtin_complete.cpp
@@ -545,7 +545,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
recursion_level++;
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++)
{
@@ -553,7 +553,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
const wchar_t *prepend;
- if (next.flags & COMPLETE_NO_CASE)
+ if (next.flags & COMPLETE_REPLACES_TOKEN)
{
prepend = L"";
}
diff --git a/complete.cpp b/complete.cpp
index a99277c8..3cc82a2d 100644
--- a/complete.cpp
+++ b/complete.cpp
@@ -104,14 +104,25 @@
#define C_(string) (string)
#endif
+/* Testing apparatus */
+const wcstring_list_t *s_override_variable_names = NULL;
-/**
- The maximum amount of time that we're willing to spend doing
- username tilde completion. This special limit has been coded in
- because user lookup can be extremely slow in cases of a humongous
- LDAP database. (Google, I'm looking at you)
- */
-#define MAX_USER_LOOKUP_TIME 0.2
+void complete_set_variable_names(const wcstring_list_t *names)
+{
+ s_override_variable_names = names;
+}
+
+static inline wcstring_list_t complete_get_variable_names(void)
+{
+ if (s_override_variable_names != NULL)
+ {
+ return *s_override_variable_names;
+ }
+ else
+ {
+ return env_get_names(0);
+ }
+}
/**
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 completer_t
{
- const complete_type_t type;
+ const completion_request_flags_t flags;
const wcstring initial_cmd;
std::vector<completion_t> completions;
wcstring_list_t commands_to_load;
@@ -334,10 +345,32 @@ class completer_t
/** Table of completions conditions that have already been tested and the corresponding test results */
typedef std::map<wcstring, bool> condition_cache_t;
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:
- completer_t(const wcstring &c, complete_type_t t) :
- type(t),
+ completer_t(const wcstring &c, completion_request_flags_t f) :
+ flags(f),
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. */
expand_flags_t result = 0;
- if (type == COMPLETE_AUTOSUGGEST)
+ if (this->type() == COMPLETE_AUTOSUGGEST)
result |= EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_JOBS;
return result;
}
@@ -449,7 +482,7 @@ bool completer_t::condition_test(const wcstring &condition)
return 1;
}
- if (this->type == COMPLETE_AUTOSUGGEST)
+ if (this->type() == COMPLETE_AUTOSUGGEST)
{
/* Autosuggestion can't support conditions */
return 0;
@@ -1096,8 +1129,6 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
if (cdpath.missing_or_empty())
cdpath = L".";
- const bool wants_description = (type == COMPLETE_DEFAULT);
-
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 (wants_description)
+ if (this->wants_descriptions())
{
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++)
{
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());
@@ -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);
}
}
@@ -1205,7 +1236,7 @@ void completer_t::complete_from_args(const wcstring &str,
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);
/* 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;
parse_cmd_string(cmd_orig, path, cmd);
- if (this->type == COMPLETE_DEFAULT)
+ if (this->type() == COMPLETE_DEFAULT)
{
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 */
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;
complete_flags_t flags = 0;
-
if (match)
+ {
offset = wcslen(str);
+ }
else
- flags = COMPLETE_NO_CASE;
+ {
+ flags = COMPLETE_REPLACES_TOKEN | COMPLETE_CASE_INSENSITIVE;
+ }
has_arg = ! o->comp.empty();
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;
/* Squelch file descriptions per issue 254 */
- if (type == COMPLETE_AUTOSUGGEST || do_file)
+ if (this->type() == COMPLETE_AUTOSUGGEST || do_file)
flags |= EXPAND_NO_DESCRIPTIONS;
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];
size_t varlen = wcslen(var);
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++)
{
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(env_name);
- flags = COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE;
+ flags = COMPLETE_CASE_INSENSITIVE | COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE;
}
wcstring desc;
- if (wants_description)
+ if (this->wants_descriptions())
{
env_var_t value_unescaped = env_get_string(env_name);
if (value_unescaped.missing())
continue;
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());
}
@@ -1744,7 +1777,7 @@ bool completer_t::try_complete_user(const wcstring &str)
append_completion(this->completions,
name,
desc,
- COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE);
+ COMPLETE_CASE_INSENSITIVE | COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE);
res=1;
}
}
@@ -1756,11 +1789,12 @@ bool completer_t::try_complete_user(const wcstring &str)
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 */
- 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;
wcstring current_token, prev_token;
wcstring current_command;
@@ -1969,7 +2003,7 @@ void complete(const wcstring &cmd, std::vector<completion_t> &comps, complete_ty
do_file = false;
/* 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;
/*
diff --git a/complete.h b/complete.h
index 555f2a9a..0da1fa46 100644
--- a/complete.h
+++ b/complete.h
@@ -75,25 +75,20 @@ enum
*/
COMPLETE_NO_SPACE = 1 << 0,
- /**
- This completion is case insensitive.
-
- Warning: The contents of the completion_t structure is actually
- different if this flag is set! Specifically, the completion string
- contains the _entire_ completion token, not merely its suffix.
- */
- COMPLETE_NO_CASE = 1 << 1,
+ /** This completion is case insensitive. */
+ COMPLETE_CASE_INSENSITIVE = 1 << 1,
+
+ /** This is not the suffix of a token, but replaces it entirely */
+ COMPLETE_REPLACES_TOKEN = 1 << 2,
/**
This completion may or may not want a space at the end - guess by
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.
- */
- COMPLETE_DONT_ESCAPE = 1 << 3
+ /** This completion should be inserted as-is, without escaping. */
+ COMPLETE_DONT_ESCAPE = 1 << 4
};
typedef int complete_flags_t;
@@ -130,7 +125,7 @@ public:
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. */
@@ -144,11 +139,13 @@ public:
bool operator != (const completion_t& rhs) const;
};
-enum complete_type_t
-{
- COMPLETE_DEFAULT,
- COMPLETE_AUTOSUGGEST
+enum {
+ COMPLETION_REQUEST_DEFAULT = 0,
+ COMPLETION_REQUEST_AUTOSUGGESTION = 1 << 0, // indicates the completion is for an autosuggestion
+ 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 */
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,
std::vector<completion_t> &comp,
- complete_type_t type,
+ completion_request_flags_t flags,
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);
+/* Function used for testing */
+void complete_set_variable_names(const wcstring_list_t *names);
#endif
diff --git a/fish_tests.cpp b/fish_tests.cpp
index 33b2fdb7..27a70fcf 100644
--- a/fish_tests.cpp
+++ b/fish_tests.cpp
@@ -959,6 +959,25 @@ static void test_colors()
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)
{
// 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);
}
-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__)
- 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^ 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' ^");
@@ -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_CASE, 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 ^");
+ 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)
@@ -1139,7 +1158,7 @@ void perf_complete()
str[0]=c;
reader_set_buffer(str, 0);
- complete(str, out, COMPLETE_DEFAULT, NULL);
+ complete(str, out, COMPLETION_REQUEST_DEFAULT, NULL);
matches += out.size();
out.clear();
@@ -1159,7 +1178,7 @@ void perf_complete()
reader_set_buffer(str, 0);
- complete(str, out, COMPLETE_DEFAULT, NULL);
+ complete(str, out, COMPLETION_REQUEST_DEFAULT, NULL);
matches += out.size();
out.clear();
@@ -1695,7 +1714,7 @@ int main(int argc, char **argv)
builtin_init();
reader_init();
env_init();
-
+
test_format();
test_escape();
test_convert();
@@ -1710,7 +1729,8 @@ int main(int argc, char **argv)
test_word_motion();
test_is_potential_path();
test_colors();
- test_completions();
+ test_complete();
+ test_completion_insertions();
test_autosuggestion_combining();
test_autosuggest_suggest_special();
history_tests_t::test_history();
diff --git a/reader.cpp b/reader.cpp
index 2c7d5706..f10e3ad0 100644
--- a/reader.cpp
+++ b/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();
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);
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;
wchar_t *escaped_separator;
- int has_case_sensitive=0;
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);
+ bool has_case_sensitive = false;
for (size_t i=0; i< comp.size(); 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++)
@@ -1170,13 +1174,13 @@ static void run_pager(const wcstring &prefix, int is_quoted, const std::vector<c
wcstring completion_text;
wcstring description_text;
- if (has_case_sensitive && (el.flags & COMPLETE_NO_CASE))
+ if (has_case_sensitive && (el.flags & COMPLETE_CASE_INSENSITIVE))
{
continue;
}
// 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)
{
@@ -1328,7 +1332,7 @@ struct autosuggestion_context_t
/* Try normal 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())
{
const completion_t &comp = completions.at(0);
@@ -3057,8 +3061,9 @@ 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 */
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 */
sort_and_make_unique(comp);
diff --git a/reader.h b/reader.h
index 174be574..80dbe258 100644
--- a/reader.h
+++ b/reader.h
@@ -163,7 +163,7 @@ void reader_pop();
- The command to be completed as a null terminated array of wchar_t
- 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);
/**
diff --git a/wildcard.cpp b/wildcard.cpp
index f8e1bed0..3177cf35 100644
--- a/wildcard.cpp
+++ b/wildcard.cpp
@@ -225,7 +225,7 @@ static bool wildcard_complete_internal(const wcstring &orig,
wcstring out_completion;
wcstring out_desc = (desc ? desc : L"");
- if (flags & COMPLETE_NO_CASE)
+ if (flags & COMPLETE_REPLACES_TOKEN)
{
out_completion = orig;
}
@@ -292,7 +292,7 @@ static bool wildcard_complete_internal(const wcstring &orig,
}
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;
}
@@ -1095,7 +1095,7 @@ int wildcard_expand(const wchar_t *wc,
{
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());
}