mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
implement set --show
This adds a new capability to the `set` command. It is similar to running `set` with no other arguments but provides far more detail about each variable. Such as whether it is set in each of the local, global, and universal scopes. And the values in each scope. You can also ask for specific variables to be shown. Fixes #4265
This commit is contained in:
parent
b4ce749c69
commit
e825415917
5 changed files with 155 additions and 14 deletions
|
@ -8,6 +8,7 @@ set [OPTIONS] VARIABLE_NAME[INDICES]... VALUES...
|
|||
set ( -q | --query ) [SCOPE_OPTIONS] VARIABLE_NAMES...
|
||||
set ( -e | --erase ) [SCOPE_OPTIONS] VARIABLE_NAME
|
||||
set ( -e | --erase ) [SCOPE_OPTIONS] VARIABLE_NAME[INDICES]...
|
||||
set ( -S | --show ) [SCOPE_OPTIONS] [VARIABLE_NAME]...
|
||||
\endfish
|
||||
|
||||
\subsection set-description Description
|
||||
|
@ -39,6 +40,8 @@ The following options are available:
|
|||
|
||||
- `-n` or `--names` List only the names of all defined variables, not their value. The names are guaranteed to be sorted.
|
||||
|
||||
- `-S` or `--show` Shows information about the given variables. If no variable names are given then all variables are shown in sorted order. No other flags can be used with this option. The information shown includes whether or not it is set in each of the local, global, and universal scopes. If it is set in one of those scopes whether or not it is exported is reported. The individual elements are also shown along with the length of each element.
|
||||
|
||||
- `-L` or `--long` do not abbreviate long values when printing set variables
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ class parser_t;
|
|||
|
||||
struct set_cmd_opts_t {
|
||||
bool print_help = false;
|
||||
bool show = false;
|
||||
bool local = false;
|
||||
bool global = false;
|
||||
bool exportv = false;
|
||||
|
@ -45,18 +46,14 @@ struct set_cmd_opts_t {
|
|||
// Variables used for parsing the argument list. This command is atypical in using the "+"
|
||||
// (REQUIRE_ORDER) option for flag parsing. This is not typical of most fish commands. It means
|
||||
// we stop scanning for flags when the first non-flag argument is seen.
|
||||
static const wchar_t *short_options = L"+:LUeghlnqux";
|
||||
static const struct woption long_options[] = {{L"export", no_argument, NULL, 'x'},
|
||||
{L"global", no_argument, NULL, 'g'},
|
||||
{L"local", no_argument, NULL, 'l'},
|
||||
{L"erase", no_argument, NULL, 'e'},
|
||||
{L"names", no_argument, NULL, 'n'},
|
||||
{L"unexport", no_argument, NULL, 'u'},
|
||||
{L"universal", no_argument, NULL, 'U'},
|
||||
{L"long", no_argument, NULL, 'L'},
|
||||
{L"query", no_argument, NULL, 'q'},
|
||||
{L"help", no_argument, NULL, 'h'},
|
||||
{NULL, 0, NULL, 0}};
|
||||
static const wchar_t *short_options = L"+:LSUeghlnqux";
|
||||
static const struct woption long_options[] = {
|
||||
{L"export", no_argument, NULL, 'x'}, {L"global", no_argument, NULL, 'g'},
|
||||
{L"local", no_argument, NULL, 'l'}, {L"erase", no_argument, NULL, 'e'},
|
||||
{L"names", no_argument, NULL, 'n'}, {L"unexport", no_argument, NULL, 'u'},
|
||||
{L"universal", no_argument, NULL, 'U'}, {L"long", no_argument, NULL, 'L'},
|
||||
{L"query", no_argument, NULL, 'q'}, {L"show", no_argument, NULL, 'S'},
|
||||
{L"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}};
|
||||
|
||||
// Error message for invalid path operations.
|
||||
#define BUILTIN_SET_PATH_ERROR _(L"%ls: Warning: $%ls entry \"%ls\" is not valid (%s)\n")
|
||||
|
@ -117,6 +114,11 @@ static int parse_cmd_opts(set_cmd_opts_t &opts, int *optind, //!OCLINT(high ncs
|
|||
opts.preserve_failure_exit_status = false;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
opts.show = true;
|
||||
opts.preserve_failure_exit_status = false;
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
opts.print_help = true;
|
||||
break;
|
||||
|
@ -140,8 +142,8 @@ static int parse_cmd_opts(set_cmd_opts_t &opts, int *optind, //!OCLINT(high ncs
|
|||
return STATUS_CMD_OK;
|
||||
}
|
||||
|
||||
static int validate_cmd_opts(const wchar_t *cmd, set_cmd_opts_t &opts, int argc,
|
||||
parser_t &parser, io_streams_t &streams) {
|
||||
static int validate_cmd_opts(const wchar_t *cmd, set_cmd_opts_t &opts, //!OCLINT(npath complexity)
|
||||
int argc, parser_t &parser, io_streams_t &streams) {
|
||||
// Can't query and erase or list.
|
||||
if (opts.query && (opts.erase || opts.list)) {
|
||||
streams.err.append_format(BUILTIN_ERR_COMBO, cmd);
|
||||
|
@ -177,6 +179,14 @@ static int validate_cmd_opts(const wchar_t *cmd, set_cmd_opts_t &opts, int argc,
|
|||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
|
||||
// The --show flag cannot be combined with any other flag.
|
||||
if (opts.show &&
|
||||
(opts.local || opts.global || opts.erase || opts.list || opts.exportv || opts.universal)) {
|
||||
streams.err.append_format(BUILTIN_ERR_COMBO, cmd);
|
||||
builtin_print_help(parser, streams, cmd, streams.err);
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (argc == 0 && opts.erase) {
|
||||
streams.err.append_format(BUILTIN_SET_ERASE_NO_VAR, cmd);
|
||||
builtin_print_help(parser, streams, cmd, streams.err);
|
||||
|
@ -495,6 +505,93 @@ static int builtin_set_query(const wchar_t *cmd, set_cmd_opts_t &opts, int argc,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams) {
|
||||
const wchar_t *scope_name;
|
||||
switch (scope) {
|
||||
case ENV_LOCAL: {
|
||||
scope_name = L"local";
|
||||
break;
|
||||
}
|
||||
case ENV_GLOBAL: {
|
||||
scope_name = L"global";
|
||||
break;
|
||||
}
|
||||
case ENV_UNIVERSAL: {
|
||||
scope_name = L"universal";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("invalid scope");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (env_exist(var_name, scope)) {
|
||||
const env_var_t evar = env_get_string(var_name, scope | ENV_EXPORT | ENV_USER);
|
||||
const wchar_t *exportv = evar.missing() ? _(L"unexported") : _(L"exported");
|
||||
|
||||
const env_var_t var = env_get_string(var_name, scope | ENV_USER);
|
||||
wcstring_list_t result;
|
||||
if (!var.empty()) tokenize_variable_array(var, result);
|
||||
|
||||
streams.out.append_format(_(L"$%ls: set in %ls scope, %ls, with %d elements\n"), var_name,
|
||||
scope_name, exportv, result.size());
|
||||
for (size_t i = 0; i < result.size(); i++) {
|
||||
if (result.size() > 100) {
|
||||
if (i == 50) streams.out.append(L"...\n");
|
||||
if (i >= 50 && i < result.size() - 50) continue;
|
||||
}
|
||||
const wcstring value = result[i];
|
||||
const wcstring escaped_val =
|
||||
escape_string(value.c_str(), ESCAPE_NO_QUOTED, STRING_STYLE_SCRIPT);
|
||||
streams.out.append_format(_(L"$%ls[%d]: length=%d value=|%ls|\n"), var_name, i,
|
||||
value.size(), escaped_val.c_str());
|
||||
}
|
||||
} else {
|
||||
streams.out.append_format(_(L"$%ls: not set in %ls scope\n"), var_name, scope_name);
|
||||
}
|
||||
}
|
||||
|
||||
/// Show mode. Show information about the named variable(s).
|
||||
static int builtin_set_show(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, wchar_t **argv,
|
||||
parser_t &parser, io_streams_t &streams) {
|
||||
UNUSED(opts);
|
||||
|
||||
if (argc == 0) { // show all vars
|
||||
wcstring_list_t names = env_get_names(ENV_USER);
|
||||
sort(names.begin(), names.end());
|
||||
for (auto it : names) {
|
||||
show_scope(it.c_str(), ENV_LOCAL, streams);
|
||||
show_scope(it.c_str(), ENV_GLOBAL, streams);
|
||||
show_scope(it.c_str(), ENV_UNIVERSAL, streams);
|
||||
streams.out.push_back(L'\n');
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
wchar_t *arg = argv[i];
|
||||
|
||||
if (!valid_var_name(arg)) {
|
||||
streams.err.append_format(_(L"$%ls: invalid var name\n"), arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wcschr(arg, L'[')) {
|
||||
streams.err.append_format(
|
||||
_(L"%ls: `set --show` does not allow slices with the var names\n"), cmd);
|
||||
builtin_print_help(parser, streams, cmd, streams.err);
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
|
||||
show_scope(arg, ENV_LOCAL, streams);
|
||||
show_scope(arg, ENV_GLOBAL, streams);
|
||||
show_scope(arg, ENV_UNIVERSAL, streams);
|
||||
streams.out.push_back(L'\n');
|
||||
}
|
||||
}
|
||||
|
||||
return STATUS_CMD_OK;
|
||||
}
|
||||
|
||||
/// Erase a variable.
|
||||
static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc,
|
||||
wchar_t **argv, parser_t &parser, io_streams_t &streams) {
|
||||
|
@ -634,6 +731,8 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
retval = builtin_set_erase(cmd, opts, argc, argv, parser, streams);
|
||||
} else if (opts.list) { // explicit list the vars we know about
|
||||
retval = builtin_set_list(cmd, opts, argc, argv, parser, streams);
|
||||
} else if (opts.show) {
|
||||
retval = builtin_set_show(cmd, opts, argc, argv, parser, streams);
|
||||
} else if (argc == 0) { // implicit list the vars we know about
|
||||
retval = builtin_set_list(cmd, opts, argc, argv, parser, streams);
|
||||
} else {
|
||||
|
|
2
tests/set.err
Normal file
2
tests/set.err
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Verify behavior of `set --show` given an invalid var name
|
||||
$argle bargle: invalid var name
|
15
tests/set.in
Normal file
15
tests/set.in
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Test various behaviors of the `set` command.
|
||||
|
||||
logmsg Verify behavior of `set --show` given an invalid var name
|
||||
set --show 'argle bargle'
|
||||
|
||||
echo '# Verify behavior of `set --show`'
|
||||
set -U var1 hello
|
||||
set --show var1
|
||||
|
||||
set -l var1
|
||||
set -g var1 goodbye "and don't come back"
|
||||
set --show var1
|
||||
|
||||
set -g var2
|
||||
set --show _unset_var var2
|
22
tests/set.out
Normal file
22
tests/set.out
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Verify behavior of `set --show` given an invalid var name
|
||||
# Verify behavior of `set --show`
|
||||
$var1: not set in local scope
|
||||
$var1: not set in global scope
|
||||
$var1: set in universal scope, unexported, with 1 elements
|
||||
$var1[0]: length=5 value=|hello|
|
||||
|
||||
$var1: set in local scope, unexported, with 0 elements
|
||||
$var1: set in global scope, unexported, with 2 elements
|
||||
$var1[0]: length=7 value=|goodbye|
|
||||
$var1[1]: length=19 value=|and don\'t come back|
|
||||
$var1: set in universal scope, unexported, with 1 elements
|
||||
$var1[0]: length=5 value=|hello|
|
||||
|
||||
$_unset_var: not set in local scope
|
||||
$_unset_var: not set in global scope
|
||||
$_unset_var: not set in universal scope
|
||||
|
||||
$var2: not set in local scope
|
||||
$var2: set in global scope, unexported, with 0 elements
|
||||
$var2: not set in universal scope
|
||||
|
Loading…
Reference in a new issue