mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +00:00
abbr: Let --function use a mandatory argument
This now means `abbr --add` has two modes: ```fish abbr --add name --function foo --regex regex ``` ```fish abbr --add name --regex regex replacement ``` This is because `--function` was seen to be confusing as a boolean flag.
This commit is contained in:
parent
36e8117206
commit
4c39aeed87
3 changed files with 41 additions and 18 deletions
|
@ -9,8 +9,7 @@ Synopsis
|
||||||
.. synopsis::
|
.. synopsis::
|
||||||
|
|
||||||
abbr --add NAME [--position command | anywhere] [-r | --regex PATTERN]
|
abbr --add NAME [--position command | anywhere] [-r | --regex PATTERN]
|
||||||
[--set-cursor[=MARKER]]
|
[--set-cursor[=MARKER]] ([-f | --function FUNCTION] | EXPANSION)
|
||||||
[-f | --function] EXPANSION
|
|
||||||
abbr --erase NAME ...
|
abbr --erase NAME ...
|
||||||
abbr --rename OLD_WORD NEW_WORD
|
abbr --rename OLD_WORD NEW_WORD
|
||||||
abbr --show
|
abbr --show
|
||||||
|
@ -43,7 +42,7 @@ Abbreviations may be added to :ref:`config.fish <configuration>`.
|
||||||
.. synopsis::
|
.. synopsis::
|
||||||
|
|
||||||
abbr [-a | --add] NAME [--position command | anywhere] [-r | --regex PATTERN]
|
abbr [-a | --add] NAME [--position command | anywhere] [-r | --regex PATTERN]
|
||||||
[--set-cursor[=MARKER]] [-f | --function] EXPANSION
|
[--set-cursor[=MARKER]] ([-f | --function FUNCTION] | EXPANSION)
|
||||||
|
|
||||||
``abbr --add`` creates a new abbreviation. With no other options, the string **NAME** is replaced by **EXPANSION**.
|
``abbr --add`` creates a new abbreviation. With no other options, the string **NAME** is replaced by **EXPANSION**.
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ With **--regex**, the abbreviation matches using the regular expression given by
|
||||||
|
|
||||||
With **--set-cursor=MARKER**, the cursor is moved to the first occurrence of **MARKER** in the expansion. The **MARKER** value is erased. The **MARKER** may be omitted (i.e. simply ``--set-cursor``), in which case it defaults to ``%``.
|
With **--set-cursor=MARKER**, the cursor is moved to the first occurrence of **MARKER** in the expansion. The **MARKER** value is erased. The **MARKER** may be omitted (i.e. simply ``--set-cursor``), in which case it defaults to ``%``.
|
||||||
|
|
||||||
With **-f** or **--function**, **EXPANSION** is treated as the name of a fish function instead of a literal replacement. When the abbreviation matches, the function will be called with the matching token as an argument. If the function's exit status is 0 (success), the token will be replaced by the function's output; otherwise the token will be left unchanged.
|
With **-f FUNCTION** or **--function FUNCTION**, **FUNCTION** is treated as the name of a fish function instead of a literal replacement. When the abbreviation matches, the function will be called with the matching token as an argument. If the function's exit status is 0 (success), the token will be replaced by the function's output; otherwise the token will be left unchanged. No **EXPANSION** may be given separately.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
|
|
@ -38,7 +38,7 @@ struct abbr_options_t {
|
||||||
bool list{};
|
bool list{};
|
||||||
bool erase{};
|
bool erase{};
|
||||||
bool query{};
|
bool query{};
|
||||||
bool function{};
|
maybe_t<wcstring> function;
|
||||||
maybe_t<wcstring> regex_pattern;
|
maybe_t<wcstring> regex_pattern;
|
||||||
maybe_t<abbrs_position_t> position{};
|
maybe_t<abbrs_position_t> position{};
|
||||||
maybe_t<wcstring> set_cursor_marker{};
|
maybe_t<wcstring> set_cursor_marker{};
|
||||||
|
@ -74,7 +74,7 @@ struct abbr_options_t {
|
||||||
streams.err.append_format(_(L"%ls: --regex option requires --add\n"), CMD);
|
streams.err.append_format(_(L"%ls: --regex option requires --add\n"), CMD);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!add && function) {
|
if (!add && function.has_value()) {
|
||||||
streams.err.append_format(_(L"%ls: --function option requires --add\n"), CMD);
|
streams.err.append_format(_(L"%ls: --function option requires --add\n"), CMD);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -112,12 +112,15 @@ static int abbr_show(const abbr_options_t &, io_streams_t &streams) {
|
||||||
}
|
}
|
||||||
if (abbr.replacement_is_function) {
|
if (abbr.replacement_is_function) {
|
||||||
comps.push_back(L"--function");
|
comps.push_back(L"--function");
|
||||||
|
comps.push_back(escape_string(abbr.replacement));
|
||||||
}
|
}
|
||||||
comps.push_back(L"--");
|
comps.push_back(L"--");
|
||||||
// Literal abbreviations have the name and key as the same.
|
// Literal abbreviations have the name and key as the same.
|
||||||
// Regex abbreviations have a pattern separate from the name.
|
// Regex abbreviations have a pattern separate from the name.
|
||||||
comps.push_back(escape_string(abbr.name));
|
comps.push_back(escape_string(abbr.name));
|
||||||
comps.push_back(escape_string(abbr.replacement));
|
if (!abbr.replacement_is_function) {
|
||||||
|
comps.push_back(escape_string(abbr.replacement));
|
||||||
|
}
|
||||||
wcstring result = join_strings(comps, L' ');
|
wcstring result = join_strings(comps, L' ');
|
||||||
result.push_back(L'\n');
|
result.push_back(L'\n');
|
||||||
streams.out.append(result);
|
streams.out.append(result);
|
||||||
|
@ -194,7 +197,7 @@ static int abbr_query(const abbr_options_t &opts, io_streams_t &) {
|
||||||
// Add a named abbreviation.
|
// Add a named abbreviation.
|
||||||
static int abbr_add(const abbr_options_t &opts, io_streams_t &streams) {
|
static int abbr_add(const abbr_options_t &opts, io_streams_t &streams) {
|
||||||
const wchar_t *const subcmd = L"--add";
|
const wchar_t *const subcmd = L"--add";
|
||||||
if (opts.args.size() < 2) {
|
if (opts.args.size() < 2 && !opts.function.has_value()) {
|
||||||
streams.err.append_format(_(L"%ls %ls: Requires at least two arguments\n"), CMD, subcmd);
|
streams.err.append_format(_(L"%ls %ls: Requires at least two arguments\n"), CMD, subcmd);
|
||||||
return STATUS_INVALID_ARGS;
|
return STATUS_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
@ -232,14 +235,22 @@ static int abbr_add(const abbr_options_t &opts, io_streams_t &streams) {
|
||||||
assert(regex.has_value() && "Anchored compilation should have succeeded");
|
assert(regex.has_value() && "Anchored compilation should have succeeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.function.has_value() && opts.args.size() > 1) {
|
||||||
|
streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, L"abbr");
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
}
|
||||||
wcstring replacement;
|
wcstring replacement;
|
||||||
for (auto iter = opts.args.begin() + 1; iter != opts.args.end(); ++iter) {
|
if (opts.function.has_value()) {
|
||||||
if (!replacement.empty()) replacement.push_back(L' ');
|
replacement = *opts.function;
|
||||||
replacement.append(*iter);
|
} else {
|
||||||
|
for (auto iter = opts.args.begin() + 1; iter != opts.args.end(); ++iter) {
|
||||||
|
if (!replacement.empty()) replacement.push_back(L' ');
|
||||||
|
replacement.append(*iter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Abbreviation function names disallow spaces.
|
// Abbreviation function names disallow spaces.
|
||||||
// This is to prevent accidental usage of e.g. `--function 'string replace'`
|
// This is to prevent accidental usage of e.g. `--function 'string replace'`
|
||||||
if (opts.function &&
|
if (opts.function.has_value() &&
|
||||||
(!valid_func_name(replacement) || replacement.find(L' ') != wcstring::npos)) {
|
(!valid_func_name(replacement) || replacement.find(L' ') != wcstring::npos)) {
|
||||||
streams.err.append_format(_(L"%ls: Invalid function name: %ls\n"), CMD,
|
streams.err.append_format(_(L"%ls: Invalid function name: %ls\n"), CMD,
|
||||||
replacement.c_str());
|
replacement.c_str());
|
||||||
|
@ -251,7 +262,7 @@ static int abbr_add(const abbr_options_t &opts, io_streams_t &streams) {
|
||||||
// Note historically we have allowed overwriting existing abbreviations.
|
// Note historically we have allowed overwriting existing abbreviations.
|
||||||
abbreviation_t abbr{std::move(name), std::move(key), std::move(replacement), position};
|
abbreviation_t abbr{std::move(name), std::move(key), std::move(replacement), position};
|
||||||
abbr.regex = std::move(regex);
|
abbr.regex = std::move(regex);
|
||||||
abbr.replacement_is_function = opts.function;
|
abbr.replacement_is_function = opts.function.has_value();
|
||||||
abbr.set_cursor_marker = opts.set_cursor_marker;
|
abbr.set_cursor_marker = opts.set_cursor_marker;
|
||||||
abbrs_get_set()->add(std::move(abbr));
|
abbrs_get_set()->add(std::move(abbr));
|
||||||
return STATUS_CMD_OK;
|
return STATUS_CMD_OK;
|
||||||
|
@ -286,11 +297,11 @@ maybe_t<int> builtin_abbr(parser_t &parser, io_streams_t &streams, const wchar_t
|
||||||
// Note the leading '-' causes wgetopter to return arguments in order, instead of permuting
|
// Note the leading '-' causes wgetopter to return arguments in order, instead of permuting
|
||||||
// them. We need this behavior for compatibility with pre-builtin abbreviations where options
|
// them. We need this behavior for compatibility with pre-builtin abbreviations where options
|
||||||
// could be given literally, for example `abbr e emacs -nw`.
|
// could be given literally, for example `abbr e emacs -nw`.
|
||||||
static const wchar_t *const short_options = L"-afr:seqgUh";
|
static const wchar_t *const short_options = L"-:af:r:seqgUh";
|
||||||
static const struct woption long_options[] = {
|
static const struct woption long_options[] = {
|
||||||
{L"add", no_argument, 'a'}, {L"position", required_argument, 'p'},
|
{L"add", no_argument, 'a'}, {L"position", required_argument, 'p'},
|
||||||
{L"regex", required_argument, 'r'}, {L"set-cursor", optional_argument, SET_CURSOR_SHORT},
|
{L"regex", required_argument, 'r'}, {L"set-cursor", optional_argument, SET_CURSOR_SHORT},
|
||||||
{L"function", no_argument, 'f'}, {L"rename", no_argument, RENAME_SHORT},
|
{L"function", required_argument, 'f'}, {L"rename", no_argument, RENAME_SHORT},
|
||||||
{L"erase", no_argument, 'e'}, {L"query", no_argument, 'q'},
|
{L"erase", no_argument, 'e'}, {L"query", no_argument, 'q'},
|
||||||
{L"show", no_argument, 's'}, {L"list", no_argument, 'l'},
|
{L"show", no_argument, 's'}, {L"list", no_argument, 'l'},
|
||||||
{L"global", no_argument, 'g'}, {L"universal", no_argument, 'U'},
|
{L"global", no_argument, 'g'}, {L"universal", no_argument, 'U'},
|
||||||
|
@ -355,7 +366,7 @@ maybe_t<int> builtin_abbr(parser_t &parser, io_streams_t &streams, const wchar_t
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'f':
|
case 'f':
|
||||||
opts.function = true;
|
opts.function = wcstring(w.woptarg);
|
||||||
break;
|
break;
|
||||||
case RENAME_SHORT:
|
case RENAME_SHORT:
|
||||||
opts.rename = true;
|
opts.rename = true;
|
||||||
|
@ -380,6 +391,10 @@ maybe_t<int> builtin_abbr(parser_t &parser, io_streams_t &streams, const wchar_t
|
||||||
builtin_print_help(parser, streams, cmd);
|
builtin_print_help(parser, streams, cmd);
|
||||||
return STATUS_CMD_OK;
|
return STATUS_CMD_OK;
|
||||||
}
|
}
|
||||||
|
case ':': {
|
||||||
|
builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
}
|
||||||
case '?': {
|
case '?': {
|
||||||
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
|
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
|
||||||
return STATUS_INVALID_ARGS;
|
return STATUS_INVALID_ARGS;
|
||||||
|
|
|
@ -133,11 +133,20 @@ echo $status
|
||||||
# CHECKERR: abbr: --position option requires --add
|
# CHECKERR: abbr: --position option requires --add
|
||||||
# CHECK: 2
|
# CHECK: 2
|
||||||
|
|
||||||
abbr --query banana --function
|
abbr --query banana --function foo
|
||||||
echo $status
|
echo $status
|
||||||
# CHECKERR: abbr: --function option requires --add
|
# CHECKERR: abbr: --function option requires --add
|
||||||
# CHECK: 2
|
# CHECK: 2
|
||||||
|
|
||||||
|
abbr --query banana --function
|
||||||
|
echo $status
|
||||||
|
# CHECKERR: abbr: --function: option requires an argument
|
||||||
|
# CHECKERR: checks/abbr.fish (line 141):
|
||||||
|
# CHECKERR: abbr --query banana --function
|
||||||
|
# CHECKERR: ^
|
||||||
|
# CHECKERR: (Type 'help abbr' for related documentation)
|
||||||
|
# CHECK: 2
|
||||||
|
|
||||||
abbr --add peach --function invalid/function/name
|
abbr --add peach --function invalid/function/name
|
||||||
echo $status
|
echo $status
|
||||||
# CHECKERR: abbr: Invalid function name: invalid/function/name
|
# CHECKERR: abbr: Invalid function name: invalid/function/name
|
||||||
|
@ -160,7 +169,7 @@ abbr --add !! --position anywhere --function replace_history
|
||||||
abbr --show
|
abbr --show
|
||||||
# CHECK: abbr -a -- nonregex_name foo
|
# CHECK: abbr -a -- nonregex_name foo
|
||||||
# CHECK: abbr -a --regex 'A[0-9]B' -- regex_name bar
|
# CHECK: abbr -a --regex 'A[0-9]B' -- regex_name bar
|
||||||
# CHECK: abbr -a --position anywhere --function -- !! replace_history
|
# CHECK: abbr -a --position anywhere --function replace_history -- !!
|
||||||
abbr --erase (abbr --list)
|
abbr --erase (abbr --list)
|
||||||
|
|
||||||
abbr --add bogus --position never stuff
|
abbr --add bogus --position never stuff
|
||||||
|
|
Loading…
Reference in a new issue