Add status subcomannds: features and test-feature

This teaches the status command to work with features.
'status features' will show a table listing all known features and whether
they are currently on or off.
`status test-feature` will test an individual feature, setting the exit status to
0 if the feature is on, 1 if off, 2 if unknown.
This commit is contained in:
ridiculousfish 2018-04-24 14:02:15 -07:00
parent 14f766b66d
commit 782cae2d21
4 changed files with 51 additions and 1 deletions

View file

@ -10,6 +10,7 @@
#include "builtin_status.h" #include "builtin_status.h"
#include "common.h" #include "common.h"
#include "fallback.h" // IWYU pragma: keep #include "fallback.h" // IWYU pragma: keep
#include "future_feature_flags.h"
#include "io.h" #include "io.h"
#include "parser.h" #include "parser.h"
#include "proc.h" #include "proc.h"
@ -31,6 +32,8 @@ enum status_cmd_t {
STATUS_LINE_NUMBER, STATUS_LINE_NUMBER,
STATUS_SET_JOB_CONTROL, STATUS_SET_JOB_CONTROL,
STATUS_STACK_TRACE, STATUS_STACK_TRACE,
STATUS_FEATURES,
STATUS_TEST_FEATURE,
STATUS_UNDEF STATUS_UNDEF
}; };
@ -40,6 +43,7 @@ const enum_map<status_cmd_t> status_enum_map[] = {
{STATUS_FILENAME, L"current-filename"}, {STATUS_FILENAME, L"current-filename"},
{STATUS_FUNCTION, L"current-function"}, {STATUS_FUNCTION, L"current-function"},
{STATUS_LINE_NUMBER, L"current-line-number"}, {STATUS_LINE_NUMBER, L"current-line-number"},
{STATUS_FEATURES, L"features"},
{STATUS_FILENAME, L"filename"}, {STATUS_FILENAME, L"filename"},
{STATUS_FUNCTION, L"function"}, {STATUS_FUNCTION, L"function"},
{STATUS_IS_BLOCK, L"is-block"}, {STATUS_IS_BLOCK, L"is-block"},
@ -54,6 +58,7 @@ const enum_map<status_cmd_t> status_enum_map[] = {
{STATUS_LINE_NUMBER, L"line-number"}, {STATUS_LINE_NUMBER, L"line-number"},
{STATUS_STACK_TRACE, L"print-stack-trace"}, {STATUS_STACK_TRACE, L"print-stack-trace"},
{STATUS_STACK_TRACE, L"stack-trace"}, {STATUS_STACK_TRACE, L"stack-trace"},
{STATUS_TEST_FEATURE, L"test-feature"},
{STATUS_UNDEF, NULL}}; {STATUS_UNDEF, NULL}};
#define status_enum_map_len (sizeof status_enum_map / sizeof *status_enum_map) #define status_enum_map_len (sizeof status_enum_map / sizeof *status_enum_map)
@ -66,6 +71,9 @@ const enum_map<status_cmd_t> status_enum_map[] = {
break; \ break; \
} }
/// Values that may be returned from the test-feature option to status.
enum { TEST_FEATURE_ON, TEST_FEATURE_OFF, TEST_FEATURE_NOT_RECOGNIZED };
int job_control_str_to_mode(const wchar_t *mode, wchar_t *cmd, io_streams_t &streams) { int job_control_str_to_mode(const wchar_t *mode, wchar_t *cmd, io_streams_t &streams) {
if (wcscmp(mode, L"full") == 0) { if (wcscmp(mode, L"full") == 0) {
return JOB_CONTROL_ALL; return JOB_CONTROL_ALL;
@ -82,6 +90,7 @@ struct status_cmd_opts_t {
bool print_help = false; bool print_help = false;
int level = 1; int level = 1;
int new_job_control_mode = -1; int new_job_control_mode = -1;
const wchar_t *feature_name;
status_cmd_t status_cmd = STATUS_UNDEF; status_cmd_t status_cmd = STATUS_UNDEF;
}; };
@ -126,6 +135,15 @@ static bool set_status_cmd(wchar_t *const cmd, status_cmd_opts_t &opts, status_c
return true; return true;
} }
/// Print the features and their values.
static void print_features(io_streams_t &streams) {
for (const auto &md : features_t::metadata) {
int set = fish_features().test(md.flag);
streams.out.append_format(L"%ls\t%s\t%ls\t%ls\n", md.name, set ? "on" : "off", md.groups,
md.description);
}
}
static int parse_cmd_opts(status_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss method) static int parse_cmd_opts(status_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss method)
int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
wchar_t *cmd = argv[0]; wchar_t *cmd = argv[0];
@ -307,6 +325,24 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
job_control_mode = opts.new_job_control_mode; job_control_mode = opts.new_job_control_mode;
break; break;
} }
case STATUS_FEATURES: {
print_features(streams);
break;
}
case STATUS_TEST_FEATURE: {
if (args.size() != 1) {
const wchar_t *subcmd_str = enum_to_str(opts.status_cmd, status_enum_map);
streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, subcmd_str, 1, args.size());
return STATUS_INVALID_ARGS;
}
const auto *metadata = features_t::metadata_for(args.front().c_str());
if (!metadata) {
retval = TEST_FEATURE_NOT_RECOGNIZED;
} else {
retval = fish_features().test(metadata->flag) ? TEST_FEATURE_ON : TEST_FEATURE_OFF;
}
break;
}
case STATUS_FILENAME: { case STATUS_FILENAME: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
const wchar_t *fn = parser.current_filename(); const wchar_t *fn = parser.current_filename();

View file

@ -6,3 +6,6 @@ status: Invalid combination of options,
you cannot do both 'is-block' and 'is-interactive' in the same invocation you cannot do both 'is-block' and 'is-interactive' in the same invocation
status: Invalid job control mode 'full1' status: Invalid job control mode 'full1'
status: Invalid job control mode '1none' status: Invalid job control mode '1none'
####################
# Future Feature Flags

View file

@ -49,3 +49,8 @@ end
test_function test_function
eval test_function eval test_function
logmsg Future Feature Flags
status features
status test-feature stderr-nocaret ; echo $status
status test-feature not-a-feature ; echo $status

View file

@ -1,3 +1,9 @@
Not a function Not a function
test_function test_function
test_function test_function
####################
# Future Feature Flags
stderr-nocaret off 3.0 ^ no longer redirects stderr
1
2