rename --metadata to --details

Discussion in issue #3295 resulted in a decisions to rename the
functions --metadata flag to --details.

This also fixes a bug in the definition of the short flags for the
`functions` command. The `-e` flag does not take an argument and
therefore should not be defined as `e:`. Notice that the long form,
`--erase`, specifies `no_argument`. This discrepency happened to work
due to a quirk of how the flag parsing loop was written.
This commit is contained in:
Kurtis Rader 2017-04-30 19:53:13 -07:00
parent 65c762f1e1
commit 6b1c939b67
4 changed files with 50 additions and 73 deletions

View file

@ -16,7 +16,7 @@
- Fish is now more forgiving of missing or invalid $TERM values (#3850). - Fish is now more forgiving of missing or invalid $TERM values (#3850).
- The `string` command now supports a `repeat` subcommand with the obvious behavior (#3864). - The `string` command now supports a `repeat` subcommand with the obvious behavior (#3864).
- The `string match` command now supports a `--filter` flag to emit the entire string partially matched by a pattern (#3957). - The `string match` command now supports a `--filter` flag to emit the entire string partially matched by a pattern (#3957).
- The `functions --metadata --verbose` output now includes the function description (#597). - The `functions --details --verbose` output now includes the function description (#597).
- Completions for `helm` added (#3829). - Completions for `helm` added (#3829).
- Empty components in $CDPATH, $MANPATH and $PATH are now converted to "." (#2106, #3914). - Empty components in $CDPATH, $MANPATH and $PATH are now converted to "." (#2106, #3914).
- `type` now no longer requires `which`, which means it is no longer a dependency (#3912, #3945). - `type` now no longer requires `which`, which means it is no longer a dependency (#3912, #3945).

View file

@ -3,7 +3,7 @@
\subsection functions-synopsis Synopsis \subsection functions-synopsis Synopsis
\fish{synopsis} \fish{synopsis}
functions [ -a | --all ] [ -n | --names ] functions [ -a | --all ] [ -n | --names ]
functions [ -m | --metadata ] [ -v ] FUNCTION functions [ -D | --details ] [ -v ] FUNCTION
functions -c OLDNAME NEWNAME functions -c OLDNAME NEWNAME
functions -d DESCRIPTION FUNCTION functions -d DESCRIPTION FUNCTION
functions [ -e | -q ] FUNCTIONS... functions [ -e | -q ] FUNCTIONS...
@ -23,7 +23,7 @@ The following options are available:
- `-e` or `--erase` causes the specified functions to be erased. - `-e` or `--erase` causes the specified functions to be erased.
- `-m` or `--metadata` reports the path name where each function is defined or could be autoloaded, `stdin` if the function was defined interactively or on the command line or by reading stdin, and `n/a` if the function isn't available. If the `--verbose` option is also specified then five lines are written: - `-D` or `--details` reports the path name where each function is defined or could be autoloaded, `stdin` if the function was defined interactively or on the command line or by reading stdin, and `n/a` if the function isn't available. If the `--verbose` option is also specified then five lines are written:
-# the pathname as already described, -# the pathname as already described,
-# `autoloaded`, `not-autoloaded` or `n/a`, -# `autoloaded`, `not-autoloaded` or `n/a`,

View file

@ -1084,50 +1084,38 @@ static int report_function_metadata(const wchar_t *funcname, bool verbose, io_st
/// The functions builtin, used for listing and erasing functions. /// The functions builtin, used for listing and erasing functions.
static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) { static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
wgetopter_t w; wchar_t *desc = NULL;
int i;
int erase = 0;
wchar_t *desc = 0;
int argc = builtin_count_args(argv); int argc = builtin_count_args(argv);
int list = 0; bool erase = false;
int show_hidden = 0; bool list = false;
int res = STATUS_BUILTIN_OK; bool show_hidden = false;
int query = 0; bool query = false;
int copy = 0; bool copy = false;
bool report_metadata = false; bool report_metadata = false;
bool verbose = false; bool verbose = false;
int res = STATUS_BUILTIN_OK;
static const wchar_t *short_options = L"Dacehnqv";
static const struct woption long_options[] = { static const struct woption long_options[] = {
{L"erase", no_argument, NULL, 'e'}, {L"description", required_argument, NULL, 'd'}, {L"erase", no_argument, NULL, 'e'}, {L"description", required_argument, NULL, 'd'},
{L"names", no_argument, NULL, 'n'}, {L"all", no_argument, NULL, 'a'}, {L"names", no_argument, NULL, 'n'}, {L"all", no_argument, NULL, 'a'},
{L"help", no_argument, NULL, 'h'}, {L"query", no_argument, NULL, 'q'}, {L"help", no_argument, NULL, 'h'}, {L"query", no_argument, NULL, 'q'},
{L"copy", no_argument, NULL, 'c'}, {L"metadata", no_argument, NULL, 'm'}, {L"copy", no_argument, NULL, 'c'}, {L"details", no_argument, NULL, 'D'},
{L"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0}}; {L"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0}};
while (1) { int opt;
int opt_index = 0; wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
int opt = w.wgetopt_long(argc, argv, L"ed:mnahqcv", long_options, &opt_index);
if (opt == -1) break;
switch (opt) { switch (opt) {
case 0: {
if (long_options[opt_index].flag != 0) break;
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
long_options[opt_index].name);
builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
case 'v': { case 'v': {
verbose = true; verbose = true;
break; break;
} }
case 'e': { case 'e': {
erase = 1; erase = true;
break; break;
} }
case 'm': { case 'D': {
report_metadata = true; report_metadata = true;
break; break;
} }
@ -1136,11 +1124,11 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
break; break;
} }
case 'n': { case 'n': {
list = 1; list = true;
break; break;
} }
case 'a': { case 'a': {
show_hidden = 1; show_hidden = true;
break; break;
} }
case 'h': { case 'h': {
@ -1148,11 +1136,11 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
} }
case 'q': { case 'q': {
query = 1; query = true;
break; break;
} }
case 'c': { case 'c': {
copy = 1; copy = true;
break; break;
} }
case '?': { case '?': {
@ -1170,15 +1158,12 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
int describe = desc ? 1 : 0; int describe = desc ? 1 : 0;
if (erase + describe + list + query + copy > 1) { if (erase + describe + list + query + copy > 1) {
streams.err.append_format(_(L"%ls: Invalid combination of options\n"), argv[0]); streams.err.append_format(_(L"%ls: Invalid combination of options\n"), argv[0]);
builtin_print_help(parser, streams, argv[0], streams.err); builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
if (erase) { if (erase) {
int i; for (int i = w.woptind; i < argc; i++) function_remove(argv[i]);
for (i = w.woptind; i < argc; i++) function_remove(argv[i]);
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
} else if (desc) { } else if (desc) {
wchar_t *func; wchar_t *func;
@ -1186,15 +1171,13 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
if (argc - w.woptind != 1) { if (argc - w.woptind != 1) {
streams.err.append_format(_(L"%ls: Expected exactly one function name\n"), argv[0]); streams.err.append_format(_(L"%ls: Expected exactly one function name\n"), argv[0]);
builtin_print_help(parser, streams, argv[0], streams.err); builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
func = argv[w.woptind]; func = argv[w.woptind];
if (!function_exists(func)) { if (!function_exists(func)) {
streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), argv[0], func); streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), argv[0], func);
builtin_print_help(parser, streams, argv[0], streams.err); builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
@ -1202,29 +1185,26 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
} else if (report_metadata) { } else if (report_metadata) {
if (argc - w.woptind != 1) { if (argc - w.woptind != 1) {
streams.err.append_format( streams.err.append_format(_(L"%ls: Expected exactly one function name for --details\n"),
_(L"%ls: Expected exactly one function name for --metadata\n"), argv[0]); argv[0]);
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
const wchar_t *funcname = argv[w.woptind]; const wchar_t *funcname = argv[w.woptind];
return report_function_metadata(funcname, verbose, streams, false); return report_function_metadata(funcname, verbose, streams, false);
} else if (list || (argc == w.woptind)) { } else if (list || (argc == w.woptind)) {
int is_screen = !streams.out_is_redirected && isatty(STDOUT_FILENO);
size_t i;
wcstring_list_t names = function_get_names(show_hidden); wcstring_list_t names = function_get_names(show_hidden);
std::sort(names.begin(), names.end()); std::sort(names.begin(), names.end());
bool is_screen = !streams.out_is_redirected && isatty(STDOUT_FILENO);
if (is_screen) { if (is_screen) {
wcstring buff; wcstring buff;
for (size_t i = 0; i < names.size(); i++) {
for (i = 0; i < names.size(); i++) {
buff.append(names.at(i)); buff.append(names.at(i));
buff.append(L", "); buff.append(L", ");
} }
streams.out.append(reformat_for_screen(buff)); streams.out.append(reformat_for_screen(buff));
} else { } else {
for (i = 0; i < names.size(); i++) { for (size_t i = 0; i < names.size(); i++) {
streams.out.append(names.at(i).c_str()); streams.out.append(names.at(i).c_str());
streams.out.append(L"\n"); streams.out.append(L"\n");
} }
@ -1240,7 +1220,6 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
L"and new function name)\n"), L"and new function name)\n"),
argv[0]); argv[0]);
builtin_print_help(parser, streams, argv[0], streams.err); builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
current_func = argv[w.woptind]; current_func = argv[w.woptind];
@ -1250,7 +1229,6 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), argv[0], streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), argv[0],
current_func.c_str()); current_func.c_str());
builtin_print_help(parser, streams, argv[0], streams.err); builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
@ -1267,7 +1245,6 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
_(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"), argv[0], _(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"), argv[0],
new_func.c_str(), current_func.c_str()); new_func.c_str(), current_func.c_str());
builtin_print_help(parser, streams, argv[0], streams.err); builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
@ -1275,10 +1252,10 @@ static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
for (i = w.woptind; i < argc; i++) { for (int i = w.woptind; i < argc; i++) {
if (!function_exists(argv[i])) if (!function_exists(argv[i])) {
res++; res++;
else { } else {
if (!query) { if (!query) {
if (i != w.woptind) streams.out.append(L"\n"); if (i != w.woptind) streams.out.append(L"\n");
const wchar_t *funcname = argv[w.woptind]; const wchar_t *funcname = argv[w.woptind];

View file

@ -6,57 +6,57 @@ function f1
end end
# ========== # ==========
# Verify that `functions --metadata` works as expected when given too many args. # Verify that `functions --details` works as expected when given too many args.
set x (functions --metadata f1 f2 2>&1) set x (functions --details f1 f2 2>&1)
if test "$x" != "functions: Expected exactly one function name for --metadata" if test "$x" != "functions: Expected exactly one function name for --details"
echo "Unexpected output for 'functions --metadata f1 f2': $x" >&2 echo "Unexpected output for 'functions --details f1 f2': $x" >&2
end end
# ========== # ==========
# Verify that `functions --metadata` works as expected when given the name of a # Verify that `functions --details` works as expected when given the name of a
# known function. # known function.
set x (functions --metadata f1) set x (functions --details f1)
if test "$x" != "stdin" if test "$x" != "stdin"
echo "Unexpected output for 'functions --metadata f1': $x" >&2 echo "Unexpected output for 'functions --details f1': $x" >&2
end end
# ========== # ==========
# Verify that `functions --metadata` works as expected when given the name of an # Verify that `functions --details` works as expected when given the name of an
# unknown function. # unknown function.
set x (functions -m f2) set x (functions -D f2)
if test "$x" != "n/a" if test "$x" != "n/a"
echo "Unexpected output for 'functions --metadata f2': $x" >&2 echo "Unexpected output for 'functions --details f2': $x" >&2
end end
# ========== # ==========
# Verify that `functions --metadata` works as expected when given the name of a # Verify that `functions --details` works as expected when given the name of a
# function that could be autoloaded but isn't currently loaded. # function that could be autoloaded but isn't currently loaded.
set x (functions -m abbr) set x (functions -D abbr)
if test (count $x) -ne 1 if test (count $x) -ne 1
or not string match -q '*/share/functions/abbr.fish' "$x" or not string match -q '*/share/functions/abbr.fish' "$x"
echo "Unexpected output for 'functions -m abbr': $x" >&2 echo "Unexpected output for 'functions -D abbr': $x" >&2
end end
# ========== # ==========
# Verify that `functions --verbose --metadata` works as expected when given the name of a # Verify that `functions --verbose --details` works as expected when given the name of a
# function that was autoloaded. # function that was autoloaded.
set x (functions -v -m abbr) set x (functions -v -D abbr)
if test (count $x) -ne 5 if test (count $x) -ne 5
or not string match -q '*/share/functions/abbr.fish' $x[1] or not string match -q '*/share/functions/abbr.fish' $x[1]
or test $x[2] != autoloaded or test $x[2] != autoloaded
or test $x[3] != 1 or test $x[3] != 1
or test $x[4] != scope-shadowing or test $x[4] != scope-shadowing
or test $x[5] != 'Manage abbreviations' or test $x[5] != 'Manage abbreviations'
echo "Unexpected output for 'functions -v -m abbr': $x" >&2 echo "Unexpected output for 'functions -v -D abbr': $x" >&2
end end
# ========== # ==========
# Verify that `functions --verbose --metadata` properly escapes a function # Verify that `functions --verbose --details` properly escapes a function
# with a multiline description. # with a multiline description.
function multiline_descr -d 'line 1\n function multiline_descr -d 'line 1\n
line 2 & more; way more' line 2 & more; way more'
end end
set x (functions -v -m multiline_descr) set x (functions -v -D multiline_descr)
if test $x[5] != 'line 1\\\\n\\nline 2 & more; way more' if test $x[5] != 'line 1\\\\n\\nline 2 & more; way more'
echo "Unexpected output for 'functions -v -m multiline_descr': $x" >&2 echo "Unexpected output for 'functions -v -D multiline_descr': $x" >&2
end end