mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-24 06:17:43 +08:00
Tab complete abbreviations
This allows abbreviations to be expanded by tab completions. Fixes #3233
This commit is contained in:
parent
161196fe53
commit
202bf0bede
|
@ -58,6 +58,9 @@
|
|||
/// Description for short variables. The value is concatenated to this description.
|
||||
#define COMPLETE_VAR_DESC_VAL _(L"Variable: %ls")
|
||||
|
||||
/// Description for abbreviations.
|
||||
#define ABBR_DESC _(L"Abbreviation: %ls")
|
||||
|
||||
/// The special cased translation macro for completions. The empty string needs to be special cased,
|
||||
/// since it can occur, and should not be translated. (Gettext returns the version information as
|
||||
/// the response).
|
||||
|
@ -337,6 +340,9 @@ class completer_t {
|
|||
void complete_cmd(const wcstring &str, bool use_function, bool use_builtin, bool use_command,
|
||||
bool use_implicit_cd);
|
||||
|
||||
/// Attempt to complete an abbreviation for the given string.
|
||||
void complete_abbr(const wcstring &str);
|
||||
|
||||
void complete_from_args(const wcstring &str, const wcstring &args, const wcstring &desc,
|
||||
complete_flags_t flags);
|
||||
|
||||
|
@ -539,12 +545,10 @@ void completer_t::complete_strings(const wcstring &wc_escaped, const description
|
|||
|
||||
const wcstring wc = parse_util_unescape_wildcards(tmp);
|
||||
|
||||
for (size_t i = 0; i < possible_comp.size(); i++) {
|
||||
wcstring temp = possible_comp.at(i).completion;
|
||||
const wchar_t *next_str = temp.empty() ? NULL : temp.c_str();
|
||||
|
||||
if (next_str) {
|
||||
wildcard_complete(next_str, wc.c_str(), desc_func, &this->completions,
|
||||
for (const auto &comp : possible_comp) {
|
||||
const wcstring &comp_str = comp.completion;
|
||||
if (!comp_str.empty()) {
|
||||
wildcard_complete(comp_str, wc.c_str(), desc_func, &this->completions,
|
||||
this->expand_flags(), flags);
|
||||
}
|
||||
}
|
||||
|
@ -694,6 +698,22 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
|||
}
|
||||
}
|
||||
|
||||
void completer_t::complete_abbr(const wcstring &cmd) {
|
||||
std::map<wcstring, wcstring> abbrs = get_abbreviations();
|
||||
std::vector<completion_t> possible_comp;
|
||||
possible_comp.reserve(abbrs.size());
|
||||
for (const auto &kv : abbrs) {
|
||||
possible_comp.emplace_back(kv.first);
|
||||
}
|
||||
|
||||
auto desc_func = [&](const wcstring &key) {
|
||||
auto iter = abbrs.find(key);
|
||||
assert(iter != abbrs.end() && "Abbreviation not found");
|
||||
return format_string(ABBR_DESC, iter->second.c_str());
|
||||
};
|
||||
this->complete_strings(cmd, desc_func, possible_comp, COMPLETE_NO_SPACE);
|
||||
}
|
||||
|
||||
/// Evaluate the argument list (as supplied by complete -a) and insert any
|
||||
/// return matching completions. Matching is done using @c
|
||||
/// copy_strings_with_prefix, meaning the completion may contain wildcards.
|
||||
|
@ -1353,11 +1373,6 @@ static maybe_t<size_t> find_argument_containing_position(const arg_list_t &args,
|
|||
void completer_t::perform() {
|
||||
wcstring current_command;
|
||||
const size_t pos = cmd.size();
|
||||
bool use_command = 1;
|
||||
bool use_function = 1;
|
||||
bool use_builtin = 1;
|
||||
bool use_implicit_cd = 1;
|
||||
|
||||
// debug( 1, L"Complete '%ls'", cmd.c_str() );
|
||||
|
||||
const wchar_t *tok_begin = nullptr;
|
||||
|
@ -1421,6 +1436,12 @@ void completer_t::perform() {
|
|||
} else {
|
||||
assert(plain_statement && plain_statement.has_source());
|
||||
|
||||
bool use_command = true;
|
||||
bool use_function = true;
|
||||
bool use_builtin = true;
|
||||
bool use_implicit_cd = true;
|
||||
bool use_abbr = true;
|
||||
|
||||
// Get the command node.
|
||||
tnode_t<grammar::tok_string> cmd_node = plain_statement.child<0>();
|
||||
assert(cmd_node && cmd_node.has_source() && "Expected command node to be valid");
|
||||
|
@ -1435,6 +1456,7 @@ void completer_t::perform() {
|
|||
use_function = true;
|
||||
use_builtin = true;
|
||||
use_implicit_cd = true;
|
||||
use_abbr = true;
|
||||
break;
|
||||
}
|
||||
case parse_statement_decoration_command:
|
||||
|
@ -1443,6 +1465,7 @@ void completer_t::perform() {
|
|||
use_function = false;
|
||||
use_builtin = false;
|
||||
use_implicit_cd = false;
|
||||
use_abbr = false;
|
||||
break;
|
||||
}
|
||||
case parse_statement_decoration_builtin: {
|
||||
|
@ -1450,6 +1473,7 @@ void completer_t::perform() {
|
|||
use_function = false;
|
||||
use_builtin = true;
|
||||
use_implicit_cd = false;
|
||||
use_abbr = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1457,6 +1481,7 @@ void completer_t::perform() {
|
|||
if (cmd_node.location_in_or_at_end_of_source_range(pos)) {
|
||||
// Complete command filename.
|
||||
complete_cmd(current_token, use_function, use_builtin, use_command, use_implicit_cd);
|
||||
if (use_abbr) complete_abbr(current_token);
|
||||
} else {
|
||||
// Get all the arguments.
|
||||
arg_list_t all_arguments = plain_statement.descendants<grammar::argument>();
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory> // IWYU pragma: keep
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
@ -1176,7 +1176,7 @@ bool fish_xdm_login_hack_hack_hack_hack(std::vector<std::string> *cmds, int argc
|
|||
return result;
|
||||
}
|
||||
|
||||
static owning_lock<std::unordered_map<wcstring, wcstring>> s_abbreviations;
|
||||
static owning_lock<std::map<wcstring, wcstring>> s_abbreviations;
|
||||
void update_abbr_cache(const wchar_t *op, const wcstring &varname) {
|
||||
wcstring abbr;
|
||||
if (!unescape_string(varname.substr(wcslen(L"_fish_abbr_")), &abbr, 0, STRING_STYLE_VAR)) {
|
||||
|
@ -1202,3 +1202,8 @@ bool expand_abbreviation(const wcstring &src, wcstring *output) {
|
|||
if (output != NULL) output->assign(abbr->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<wcstring, wcstring> get_abbreviations() {
|
||||
auto abbreviations = s_abbreviations.acquire();
|
||||
return *abbreviations;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -151,6 +152,9 @@ wcstring replace_home_directory_with_tilde(const wcstring &str);
|
|||
void update_abbr_cache(const wchar_t *op, const wcstring &varname);
|
||||
bool expand_abbreviation(const wcstring &src, wcstring *output);
|
||||
|
||||
/// \return a snapshot of all abbreviations as a map abbreviation->expansion.
|
||||
std::map<wcstring, wcstring> get_abbreviations();
|
||||
|
||||
// Terrible hacks
|
||||
bool fish_xdm_login_hack_hack_hack_hack(std::vector<std::string> *cmds, int argc,
|
||||
const char *const *argv);
|
||||
|
|
|
@ -1738,8 +1738,8 @@ static void test_abbreviations() {
|
|||
{L"foo", L"bar"},
|
||||
{L"gx", L"git checkout"},
|
||||
};
|
||||
for (auto it : abbreviations) {
|
||||
int ret = env_set_one(L"_fish_abbr_" + it.first, ENV_LOCAL, it.second);
|
||||
for (const auto &kv : abbreviations) {
|
||||
int ret = env_set_one(L"_fish_abbr_" + kv.first, ENV_LOCAL, kv.second);
|
||||
if (ret != 0) err(L"Unable to set abbreviation variable");
|
||||
}
|
||||
|
||||
|
@ -2458,6 +2458,21 @@ static void test_complete() {
|
|||
completions.clear();
|
||||
complete_set_variable_names(NULL);
|
||||
|
||||
// Test abbreviations.
|
||||
function_data_t fd;
|
||||
fd.name = L"testabbrsonetwothreefour";
|
||||
function_add(fd, parser_t::principal_parser());
|
||||
int ret = env_set_one(L"_fish_abbr_testabbrsonetwothreezero", ENV_LOCAL, L"expansion");
|
||||
complete(L"testabbrsonetwothree", &completions, COMPLETION_REQUEST_DEFAULT);
|
||||
do_test(ret == 0);
|
||||
do_test(completions.size() == 2);
|
||||
do_test(completions.at(0).completion == L"four");
|
||||
do_test((completions.at(0).flags & COMPLETE_NO_SPACE) == 0);
|
||||
// Abbreviations should not have a space after them.
|
||||
do_test(completions.at(1).completion == L"zero");
|
||||
do_test((completions.at(1).flags & COMPLETE_NO_SPACE) != 0);
|
||||
|
||||
|
||||
// Test wraps.
|
||||
do_test(comma_join(complete_get_wrap_targets(L"wrapper1")) == L"");
|
||||
complete_add_wrapper(L"wrapper1", L"wrapper2");
|
||||
|
|
Loading…
Reference in New Issue
Block a user