mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-25 18:03:37 +08:00
Adopt completion_receiver_t more widely
This switches certain uses from just appending to a list to using completion_receiver_t, in preparation for limiting how many completions may be produced. Perhaps in time this could also be used for "streaming" completions.
This commit is contained in:
parent
245f264c04
commit
48567c37de
|
@ -337,7 +337,7 @@ class completer_t {
|
|||
const completion_request_flags_t flags;
|
||||
|
||||
/// The output completions.
|
||||
completion_list_t completions;
|
||||
completion_receiver_t completions;
|
||||
|
||||
/// Table of completions conditions that have already been tested and the corresponding test
|
||||
/// results.
|
||||
|
@ -440,7 +440,7 @@ class completer_t {
|
|||
|
||||
void perform_for_commandline(wcstring cmdline);
|
||||
|
||||
completion_list_t acquire_completions() { return std::move(completions); }
|
||||
completion_list_t acquire_completions() { return completions.take(); }
|
||||
};
|
||||
|
||||
// Autoloader for completions.
|
||||
|
@ -636,8 +636,8 @@ void completer_t::complete_cmd_desc(const wcstring &str) {
|
|||
}
|
||||
|
||||
bool skip = true;
|
||||
for (const auto &c : completions) {
|
||||
if (c.completion.empty() || (c.completion[c.completion.size() - 1] != L'/')) {
|
||||
for (const auto &c : completions.get_list()) {
|
||||
if (c.completion.empty() || (c.completion.back() != L'/')) {
|
||||
skip = false;
|
||||
break;
|
||||
}
|
||||
|
@ -688,7 +688,7 @@ void completer_t::complete_cmd_desc(const wcstring &str) {
|
|||
|
||||
// Then do a lookup on every completion and if a match is found, change to the new
|
||||
// description.
|
||||
for (auto &completion : completions) {
|
||||
for (auto &completion : completions.get_list()) {
|
||||
const wcstring &el = completion.completion;
|
||||
auto new_desc_iter = lookup.find(el);
|
||||
if (new_desc_iter != lookup.end()) completion.description = new_desc_iter->second;
|
||||
|
@ -1073,7 +1073,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt,
|
|||
// It's a match.
|
||||
wcstring desc = o.localized_desc();
|
||||
// Append a short-style option
|
||||
append_completion(&this->completions, o.option, std::move(desc), 0);
|
||||
this->completions.add(wcstring{o.option}, std::move(desc), 0);
|
||||
}
|
||||
|
||||
// Check if the long style option matches.
|
||||
|
@ -1116,12 +1116,11 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt,
|
|||
// functions.
|
||||
wcstring completion = format_string(L"%ls=", whole_opt.c_str() + offset);
|
||||
// Append a long-style option with a mandatory trailing equal sign
|
||||
append_completion(&this->completions, std::move(completion), C_(o.desc),
|
||||
flags | COMPLETE_NO_SPACE);
|
||||
this->completions.add(std::move(completion), C_(o.desc), flags | COMPLETE_NO_SPACE);
|
||||
}
|
||||
|
||||
// Append a long-style option
|
||||
append_completion(&this->completions, whole_opt.substr(offset), C_(o.desc), flags);
|
||||
this->completions.add(whole_opt.substr(offset), C_(o.desc), flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1175,8 +1174,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
|
|||
for (completion_t &comp : local_completions) {
|
||||
comp.prepend_token_prefix(prefix_with_sep);
|
||||
}
|
||||
this->completions.insert(this->completions.end(), local_completions.begin(),
|
||||
local_completions.end());
|
||||
this->completions.add_list(std::move(local_completions));
|
||||
}
|
||||
|
||||
if (complete_from_start) {
|
||||
|
@ -1239,7 +1237,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) {
|
|||
}
|
||||
|
||||
// Append matching environment variables
|
||||
append_completion(&this->completions, std::move(comp), std::move(desc), flags, *match);
|
||||
this->completions.add(std::move(comp), std::move(desc), flags, *match);
|
||||
|
||||
res = true;
|
||||
}
|
||||
|
@ -1343,14 +1341,14 @@ bool completer_t::try_complete_user(const wcstring &str) {
|
|||
if (std::wcsncmp(user_name, pw_name, name_len) == 0) {
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
// Append a user name
|
||||
append_completion(&this->completions, &pw_name[name_len], std::move(desc),
|
||||
COMPLETE_NO_SPACE);
|
||||
this->completions.add(&pw_name[name_len], std::move(desc), COMPLETE_NO_SPACE);
|
||||
result = true;
|
||||
} else if (wcsncasecmp(user_name, pw_name, name_len) == 0) {
|
||||
wcstring name = format_string(L"~%ls", pw_name);
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
// Append a user name
|
||||
append_completion(&this->completions, std::move(name), std::move(desc),
|
||||
this->completions.add(
|
||||
std::move(name), std::move(desc),
|
||||
COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE);
|
||||
result = true;
|
||||
}
|
||||
|
@ -1517,7 +1515,7 @@ void completer_t::escape_opening_brackets(const wcstring &argument) {
|
|||
// unescaped version.
|
||||
wcstring unescaped_argument;
|
||||
if (!unescape_string(argument, &unescaped_argument, UNESCAPE_INCOMPLETE)) return;
|
||||
for (completion_t &comp : completions) {
|
||||
for (completion_t &comp : completions.get_list()) {
|
||||
if (comp.flags & COMPLETE_REPLACES_TOKEN) continue;
|
||||
comp.flags |= COMPLETE_REPLACES_TOKEN;
|
||||
if (comp.flags & COMPLETE_DONT_ESCAPE) {
|
||||
|
@ -1546,7 +1544,7 @@ void completer_t::mark_completions_duplicating_arguments(const wcstring &cmd,
|
|||
std::sort(arg_strs.begin(), arg_strs.end());
|
||||
|
||||
wcstring comp_str;
|
||||
for (completion_t &comp : completions) {
|
||||
for (completion_t &comp : completions.get_list()) {
|
||||
comp_str = comp.completion;
|
||||
if (!(comp.flags & COMPLETE_REPLACES_TOKEN)) {
|
||||
comp_str.insert(0, prefix);
|
||||
|
|
|
@ -124,6 +124,10 @@ using completion_list_t = std::vector<completion_t>;
|
|||
/// some conveniences.
|
||||
class completion_receiver_t {
|
||||
public:
|
||||
/// Construct, perhaps acquiring a list if necessary.
|
||||
completion_receiver_t() = default;
|
||||
explicit completion_receiver_t(completion_list_t &&v) : completions_(std::move(v)) {}
|
||||
|
||||
/// Add a completion.
|
||||
void add(completion_t &&comp);
|
||||
|
||||
|
@ -144,6 +148,21 @@ class completion_receiver_t {
|
|||
/// useful to prevent allocations.
|
||||
void clear() { completions_.clear(); }
|
||||
|
||||
/// \return whether our completion list is empty.
|
||||
bool empty() const { return completions_.empty(); }
|
||||
|
||||
/// \return how many completions we have stored.
|
||||
size_t size() const { return completions_.size(); }
|
||||
|
||||
/// \return a completion at an index.
|
||||
completion_t &at(size_t idx) { return completions_.at(idx); }
|
||||
const completion_t &at(size_t idx) const { return completions_.at(idx); }
|
||||
|
||||
/// \return the list of completions. Do not modify the size of the list via this function, as it
|
||||
/// may exceed our completion limit.
|
||||
const completion_list_t &get_list() const { return completions_; }
|
||||
completion_list_t &get_list() { return completions_; }
|
||||
|
||||
/// \return the list of completions, clearing them.
|
||||
completion_list_t take();
|
||||
|
||||
|
|
|
@ -883,7 +883,7 @@ class expander_t {
|
|||
: ctx(ctx), flags(flags), errors(errors) {}
|
||||
|
||||
public:
|
||||
static expand_result_t expand_string(wcstring input, completion_list_t *out_completions,
|
||||
static expand_result_t expand_string(wcstring input, completion_receiver_t *out_completions,
|
||||
expand_flags_t flags, const operation_context_t &ctx,
|
||||
parse_error_list_t *errors);
|
||||
};
|
||||
|
@ -1007,10 +1007,10 @@ expand_result_t expander_t::stage_wildcards(wcstring path_to_expand, completion_
|
|||
}
|
||||
|
||||
result = expand_result_t::wildcard_no_match;
|
||||
completion_list_t expanded;
|
||||
completion_receiver_t expanded_recv;
|
||||
for (const auto &effective_working_dir : effective_working_dirs) {
|
||||
wildcard_expand_result_t expand_res = wildcard_expand_string(
|
||||
path_to_expand, effective_working_dir, flags, ctx.cancel_checker, &expanded);
|
||||
path_to_expand, effective_working_dir, flags, ctx.cancel_checker, &expanded_recv);
|
||||
switch (expand_res) {
|
||||
case wildcard_expand_result_t::match:
|
||||
result = expand_result_t::ok;
|
||||
|
@ -1023,6 +1023,7 @@ expand_result_t expander_t::stage_wildcards(wcstring path_to_expand, completion_
|
|||
}
|
||||
}
|
||||
|
||||
completion_list_t expanded = expanded_recv.take();
|
||||
std::sort(expanded.begin(), expanded.end(),
|
||||
[&](const completion_t &a, const completion_t &b) {
|
||||
return wcsfilecmp_glob(a.completion.c_str(), b.completion.c_str()) < 0;
|
||||
|
@ -1039,14 +1040,14 @@ expand_result_t expander_t::stage_wildcards(wcstring path_to_expand, completion_
|
|||
return result;
|
||||
}
|
||||
|
||||
expand_result_t expander_t::expand_string(wcstring input, completion_list_t *out_completions,
|
||||
expand_result_t expander_t::expand_string(wcstring input, completion_receiver_t *out_completions,
|
||||
expand_flags_t flags, const operation_context_t &ctx,
|
||||
parse_error_list_t *errors) {
|
||||
assert(((flags & expand_flag::skip_cmdsubst) || ctx.parser) &&
|
||||
"Must have a parser if not skipping command substitutions");
|
||||
// Early out. If we're not completing, and there's no magic in the input, we're done.
|
||||
if (!(flags & expand_flag::for_completions) && expand_is_clean(input)) {
|
||||
append_completion(out_completions, std::move(input));
|
||||
out_completions->add(std::move(input));
|
||||
return expand_result_t::ok;
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1101,7 @@ expand_result_t expander_t::expand_string(wcstring input, completion_list_t *out
|
|||
if (!(flags & expand_flag::skip_home_directories)) {
|
||||
unexpand_tildes(input, ctx.vars, &completions);
|
||||
}
|
||||
vec_append(*out_completions, std::move(completions));
|
||||
out_completions->add_list(std::move(completions));
|
||||
}
|
||||
return total_result;
|
||||
}
|
||||
|
@ -1109,6 +1110,15 @@ expand_result_t expander_t::expand_string(wcstring input, completion_list_t *out
|
|||
expand_result_t expand_string(wcstring input, completion_list_t *out_completions,
|
||||
expand_flags_t flags, const operation_context_t &ctx,
|
||||
parse_error_list_t *errors) {
|
||||
completion_receiver_t recv(std::move(*out_completions));
|
||||
auto res = expand_string(std::move(input), &recv, flags, ctx, errors);
|
||||
*out_completions = recv.take();
|
||||
return res;
|
||||
}
|
||||
|
||||
expand_result_t expand_string(wcstring input, completion_receiver_t *out_completions,
|
||||
expand_flags_t flags, const operation_context_t &ctx,
|
||||
parse_error_list_t *errors) {
|
||||
return expander_t::expand_string(std::move(input), out_completions, flags, ctx, errors);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ using expand_flags_t = enum_set_t<expand_flag>;
|
|||
|
||||
class completion_t;
|
||||
using completion_list_t = std::vector<completion_t>;
|
||||
class completion_receiver_t;
|
||||
|
||||
enum : wchar_t {
|
||||
/// Character representing a home directory.
|
||||
|
@ -158,6 +159,11 @@ __warn_unused expand_result_t expand_string(wcstring input, completion_list_t *o
|
|||
expand_flags_t flags, const operation_context_t &ctx,
|
||||
parse_error_list_t *errors = nullptr);
|
||||
|
||||
/// Variant of string that inserts its results into a completion_receiver_t.
|
||||
__warn_unused expand_result_t expand_string(wcstring input, completion_receiver_t *output,
|
||||
expand_flags_t flags, const operation_context_t &ctx,
|
||||
parse_error_list_t *errors = nullptr);
|
||||
|
||||
/// expand_one is identical to expand_string, except it will fail if in expands to more than one
|
||||
/// string. This is used for expanding command names.
|
||||
///
|
||||
|
|
|
@ -187,7 +187,7 @@ struct wc_complete_pack_t {
|
|||
};
|
||||
|
||||
// Weirdly specific and non-reusable helper function that makes its one call site much clearer.
|
||||
static bool has_prefix_match(const completion_list_t *comps, size_t first) {
|
||||
static bool has_prefix_match(const completion_receiver_t *comps, size_t first) {
|
||||
if (comps != nullptr) {
|
||||
const size_t after_count = comps->size();
|
||||
for (size_t j = first; j < after_count; j++) {
|
||||
|
@ -209,7 +209,7 @@ static bool has_prefix_match(const completion_list_t *comps, size_t first) {
|
|||
static bool wildcard_complete_internal(const wchar_t *const str, size_t str_len,
|
||||
const wchar_t *const wc, size_t wc_len,
|
||||
const wc_complete_pack_t ¶ms, complete_flags_t flags,
|
||||
completion_list_t *out, bool is_first_call = false) {
|
||||
completion_receiver_t *out, bool is_first_call = false) {
|
||||
assert(str != nullptr);
|
||||
assert(wc != nullptr);
|
||||
|
||||
|
@ -253,7 +253,7 @@ static bool wildcard_complete_internal(const wchar_t *const str, size_t str_len,
|
|||
// Note: out_completion may be empty if the completion really is empty, e.g. tab-completing
|
||||
// 'foo' when a file 'foo' exists.
|
||||
complete_flags_t local_flags = flags | (full_replacement ? COMPLETE_REPLACES_TOKEN : 0);
|
||||
append_completion(out, out_completion, out_desc, local_flags, *match);
|
||||
out->add(std::move(out_completion), std::move(out_desc), local_flags, *match);
|
||||
return true;
|
||||
} else if (next_wc_char_pos > 0) {
|
||||
// The literal portion of a wildcard cannot be longer than the string itself,
|
||||
|
@ -331,7 +331,7 @@ static bool wildcard_complete_internal(const wchar_t *const str, size_t str_len,
|
|||
|
||||
bool wildcard_complete(const wcstring &str, const wchar_t *wc,
|
||||
const std::function<wcstring(const wcstring &)> &desc_func,
|
||||
completion_list_t *out, expand_flags_t expand_flags,
|
||||
completion_receiver_t *out, expand_flags_t expand_flags,
|
||||
complete_flags_t flags) {
|
||||
// Note out may be NULL.
|
||||
assert(wc != nullptr);
|
||||
|
@ -449,7 +449,7 @@ static const wchar_t *file_get_desc(int lstat_res, const struct stat &lbuf, int
|
|||
/// up. Note that the filename came from a readdir() call, so we know it exists.
|
||||
static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wcstring &filename,
|
||||
const wchar_t *wc, expand_flags_t expand_flags,
|
||||
completion_list_t *out) {
|
||||
completion_receiver_t *out) {
|
||||
// Check if it will match before stat().
|
||||
if (!wildcard_complete(filename, wc, {}, nullptr, expand_flags, 0)) {
|
||||
return false;
|
||||
|
@ -526,7 +526,7 @@ class wildcard_expander_t {
|
|||
// Flags controlling expansion.
|
||||
const expand_flags_t flags;
|
||||
// Resolved items get inserted into here. This is transient of course.
|
||||
completion_list_t *resolved_completions;
|
||||
completion_receiver_t *resolved_completions;
|
||||
// Whether we have been interrupted.
|
||||
bool did_interrupt{false};
|
||||
// Whether we have successfully added any completions.
|
||||
|
@ -567,11 +567,11 @@ class wildcard_expander_t {
|
|||
return did_interrupt;
|
||||
}
|
||||
|
||||
void add_expansion_result(const wcstring &result) {
|
||||
void add_expansion_result(wcstring &&result) {
|
||||
// This function is only for the non-completions case.
|
||||
assert(!(this->flags & expand_flag::for_completions));
|
||||
if (this->completion_set.insert(result).second) {
|
||||
append_completion(this->resolved_completions, result);
|
||||
this->resolved_completions->add(std::move(result));
|
||||
this->did_add = true;
|
||||
}
|
||||
}
|
||||
|
@ -691,7 +691,7 @@ class wildcard_expander_t {
|
|||
|
||||
public:
|
||||
wildcard_expander_t(wcstring wd, expand_flags_t f, cancel_checker_t cancel_checker,
|
||||
completion_list_t *r)
|
||||
completion_receiver_t *r)
|
||||
: cancel_checker(std::move(cancel_checker)),
|
||||
working_directory(std::move(wd)),
|
||||
flags(f),
|
||||
|
@ -699,7 +699,7 @@ class wildcard_expander_t {
|
|||
assert(resolved_completions != nullptr);
|
||||
|
||||
// Insert initial completions into our set to avoid duplicates.
|
||||
for (const auto &resolved_completion : *resolved_completions) {
|
||||
for (const auto &resolved_completion : resolved_completions->get_list()) {
|
||||
this->completion_set.insert(resolved_completion.completion);
|
||||
}
|
||||
}
|
||||
|
@ -724,7 +724,7 @@ void wildcard_expander_t::expand_trailing_slash(const wcstring &base_dir, const
|
|||
// Trailing slash and not accepting incomplete, e.g. `echo /xyz/`. Insert this file if it
|
||||
// exists.
|
||||
if (waccess(base_dir, F_OK) == 0) {
|
||||
this->add_expansion_result(base_dir);
|
||||
this->add_expansion_result(wcstring{base_dir});
|
||||
}
|
||||
} else {
|
||||
// Trailing slashes and accepting incomplete, e.g. `echo /xyz/<tab>`. Everything is added.
|
||||
|
@ -961,7 +961,7 @@ wildcard_expand_result_t wildcard_expand_string(const wcstring &wc,
|
|||
const wcstring &working_directory,
|
||||
expand_flags_t flags,
|
||||
const cancel_checker_t &cancel_checker,
|
||||
completion_list_t *output) {
|
||||
completion_receiver_t *output) {
|
||||
assert(output != nullptr);
|
||||
// Fuzzy matching only if we're doing completions.
|
||||
assert(flags.get(expand_flag::for_completions) || !flags.get(expand_flag::fuzzy_match));
|
||||
|
|
|
@ -39,7 +39,7 @@ enum {
|
|||
/// \param working_directory The working directory
|
||||
/// \param flags flags for the search. Can be any combination of for_completions and
|
||||
/// executables_only
|
||||
/// \param out The list in which to put the output
|
||||
/// \param output The list in which to put the output
|
||||
///
|
||||
enum class wildcard_expand_result_t {
|
||||
no_match, /// The wildcard did not match.
|
||||
|
@ -50,7 +50,7 @@ wildcard_expand_result_t wildcard_expand_string(const wcstring &wc,
|
|||
const wcstring &working_directory,
|
||||
expand_flags_t flags,
|
||||
const cancel_checker_t &cancel_checker,
|
||||
completion_list_t *out);
|
||||
completion_receiver_t *output);
|
||||
|
||||
/// Test whether the given wildcard matches the string. Does not perform any I/O.
|
||||
///
|
||||
|
@ -69,6 +69,7 @@ bool wildcard_has(const wchar_t *, bool internal);
|
|||
|
||||
/// Test wildcard completion.
|
||||
bool wildcard_complete(const wcstring &str, const wchar_t *wc, const description_func_t &desc_func,
|
||||
completion_list_t *out, expand_flags_t expand_flags, complete_flags_t flags);
|
||||
completion_receiver_t *out, expand_flags_t expand_flags,
|
||||
complete_flags_t flags);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue
Block a user