diff options
Diffstat (limited to 'src/complete.cpp')
-rw-r--r-- | src/complete.cpp | 113 |
1 files changed, 73 insertions, 40 deletions
diff --git a/src/complete.cpp b/src/complete.cpp index 877ac984..58799eb2 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -453,15 +453,16 @@ void completion_autoload_t::command_removed(const wcstring &cmd) /** Create a new completion entry. */ -void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc, complete_flags_t flags, string_fuzzy_match_t match) +void append_completion(std::vector<completion_t> *completions, const wcstring &comp, const wcstring &desc, complete_flags_t flags, string_fuzzy_match_t match) { /* If we just constructed the completion and used push_back, we would get two string copies. Try to avoid that by making a stubby completion in the vector first, and then copying our string in. Note that completion_t's constructor will munge 'flags' so it's important that we pass those to the constructor. Nasty hack for #1241 - since the constructor needs the completion string to resolve AUTO_SPACE, and we aren't providing it with the completion, we have to do the resolution ourselves. We should get this resolving out of the constructor. */ + assert(completions != NULL); const wcstring empty; - completions.push_back(completion_t(empty, empty, match, resolve_auto_space(comp, flags))); - completion_t *last = &completions.back(); + completions->push_back(completion_t(empty, empty, match, resolve_auto_space(comp, flags))); + completion_t *last = &completions->back(); last->completion = comp; last->description = desc; } @@ -965,7 +966,7 @@ void completer_t::complete_strings(const wcstring &wc_escaped, if (next_str) { - wildcard_complete(next_str, wc, desc, desc_func, this->completions, this->expand_flags(), flags); + wildcard_complete(next_str, wc, desc, desc_func, &this->completions, this->expand_flags(), flags); } } @@ -1132,7 +1133,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool if (use_command) { - if (expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR) + if (expand_string(str_cmd, &this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR) { if (this->wants_descriptions()) { @@ -1142,7 +1143,10 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool } if (use_implicit_cd) { - (void)expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | DIRECTORIES_ONLY | this->expand_flags(), NULL); + if (!expand_string(str_cmd, &this->completions, ACCEPT_INCOMPLETE | DIRECTORIES_ONLY | this->expand_flags(), NULL)) + { + // Not valid as implicit cd. + } } if (str_cmd.find(L'/') == wcstring::npos && str_cmd.at(0) != L'~') { @@ -1168,7 +1172,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool size_t prev_count = this->completions.size(); if (expand_string(nxt_completion, - this->completions, + &this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR) { /* For all new completions, if COMPLETE_NO_CASE is set, then use only the last path component */ @@ -1194,7 +1198,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool wcstring_list_t names = function_get_names(str_cmd.at(0) == L'_'); for (size_t i=0; i < names.size(); i++) { - append_completion(possible_comp, names.at(i)); + append_completion(&possible_comp, names.at(i)); } this->complete_strings(str_cmd, 0, &complete_function_desc, possible_comp, 0); @@ -1204,7 +1208,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool if (use_builtin) { - builtin_get_names(possible_comp); + builtin_get_names(&possible_comp); this->complete_strings(str_cmd, 0, &builtin_get_desc, possible_comp, 0); } @@ -1229,9 +1233,6 @@ void completer_t::complete_from_args(const wcstring &str, const wcstring &desc, complete_flags_t flags) { - - std::vector<completion_t> possible_comp; - bool is_autosuggest = (this->type() == COMPLETE_AUTOSUGGEST); parser_t parser(is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL, false /* don't show errors */); @@ -1239,7 +1240,8 @@ void completer_t::complete_from_args(const wcstring &str, if (! is_autosuggest) proc_push_interactive(0); - parser.expand_argument_list(args, possible_comp); + std::vector<completion_t> possible_comp; + parser.expand_argument_list(args, &possible_comp); if (! is_autosuggest) proc_pop_interactive(); @@ -1527,7 +1529,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop completion[0] = o->short_opt; completion[1] = 0; - append_completion(this->completions, completion, desc, 0); + append_completion(&this->completions, completion, desc, 0); } @@ -1582,14 +1584,14 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop homebrew getopt-like functions. */ wcstring completion = format_string(L"%ls=", whole_opt.c_str()+offset); - append_completion(this->completions, + append_completion(&this->completions, completion, C_(o->desc), flags); } - append_completion(this->completions, + append_completion(&this->completions, whole_opt.c_str() + offset, C_(o->desc), flags); @@ -1606,20 +1608,8 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop /** Perform file completion on the specified string */ -void completer_t::complete_param_expand(const wcstring &sstr, bool do_file, bool directories_only) +void completer_t::complete_param_expand(const wcstring &str, bool do_file, bool directories_only) { - const wchar_t * const str = sstr.c_str(); - const wchar_t *comp_str; - - if (string_prefixes_string(L"--", sstr) && (comp_str = wcschr(str, L'='))) - { - comp_str++; - } - else - { - comp_str = str; - } - expand_flags_t flags = EXPAND_SKIP_CMDSUBST | ACCEPT_INCOMPLETE | this->expand_flags(); if (! do_file) @@ -1632,15 +1622,58 @@ void completer_t::complete_param_expand(const wcstring &sstr, bool do_file, bool if (this->type() == COMPLETE_AUTOSUGGEST || do_file) flags |= EXPAND_NO_DESCRIPTIONS; - /* Don't do fuzzy matching for files if the string begins with a dash (#568). We could consider relaxing this if there was a preceding double-dash argument */ - if (string_prefixes_string(L"-", sstr)) - flags &= ~EXPAND_FUZZY_MATCH; - - if (expand_string(comp_str, - this->completions, - flags, NULL) == EXPAND_ERROR) + /* We have the following cases: + + --foo=bar => expand just bar + -foo=bar => expand just bar + foo=bar => expand the whole thing, and also just bar + + We also support colon separator (#2178). If there's more than one, prefer the last one. + */ + + size_t sep_index = str.find_last_of(L"=:"); + bool complete_from_separator = (sep_index != wcstring::npos); + bool complete_from_start = !complete_from_separator || !string_prefixes_string(L"-", str); + + if (complete_from_separator) + { + const wcstring sep_string = wcstring(str, sep_index + 1); + std::vector<completion_t> local_completions; + if (expand_string(sep_string, + &local_completions, + flags, + NULL) == EXPAND_ERROR) + { + debug(3, L"Error while expanding string '%ls'", sep_string.c_str()); + } + + /* Hack hack hack. Any COMPLETE_REPLACES_TOKEN will also stomp the separator. We need to "repair" them by inserting our separator and prefix. */ + const wcstring prefix_with_sep = wcstring(str, 0, sep_index + 1); + for (size_t i=0; i < local_completions.size(); i++) + { + completion_t *c = &local_completions.at(i); + if (c->flags & COMPLETE_REPLACES_TOKEN) + { + c->completion.insert(0, prefix_with_sep); + } + } + this->completions.insert(this->completions.end(), + local_completions.begin(), + local_completions.end()); + } + + if (complete_from_start) { - debug(3, L"Error while expanding string '%ls'", comp_str); + /* Don't do fuzzy matching for files if the string begins with a dash (#568). We could consider relaxing this if there was a preceding double-dash argument */ + if (string_prefixes_string(L"-", str)) + flags &= ~EXPAND_FUZZY_MATCH; + + if (expand_string(str, + &this->completions, + flags, NULL) == EXPAND_ERROR) + { + debug(3, L"Error while expanding string '%ls'", str.c_str()); + } } } @@ -1693,7 +1726,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str()); } - append_completion(this->completions, comp, desc, flags, match); + append_completion(&this->completions, comp, desc, flags, match); res = true; } @@ -1804,7 +1837,7 @@ bool completer_t::try_complete_user(const wcstring &str) if (wcsncmp(user_name, pw_name, name_len)==0) { wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); - append_completion(this->completions, + append_completion(&this->completions, &pw_name[name_len], desc, COMPLETE_NO_SPACE); @@ -1816,7 +1849,7 @@ bool completer_t::try_complete_user(const wcstring &str) wcstring name = format_string(L"~%ls", pw_name); wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); - append_completion(this->completions, + append_completion(&this->completions, name, desc, COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE); |