Allow setting feature flags on the command line

This introduces a new command line option --features which can be used for
enabling or disabling features for a particular fish session.

Examples:
  fish --features stderr-nocaret
  fish --features 3.0,no-stderr-nocaret
  fish --features all

Note that the feature set cannot be changed in an existing session.
This commit is contained in:
ridiculousfish 2018-04-24 14:32:06 -07:00
parent 782cae2d21
commit 8a96f283ba
8 changed files with 63 additions and 1 deletions

View file

@ -44,6 +44,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "fallback.h" // IWYU pragma: keep
#include "fish_version.h"
#include "function.h"
#include "future_feature_flags.h"
#include "history.h"
#include "io.h"
#include "parser.h"
@ -61,6 +62,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
// container to hold the options specified within the command line
class fish_cmd_opts_t {
public:
// Future feature flags values string
wcstring features;
// Commands to be executed in place of interactive shell.
std::vector<std::string> batch_cmds;
// Commands to execute after the shell's config has been read.
@ -238,9 +241,10 @@ int run_command_list(std::vector<std::string> *cmds, const io_chain_t &io) {
/// Parse the argument list, return the index of the first non-flag arguments.
static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) {
static const char *short_opts = "+hilnvc:C:p:d:D:";
static const char *short_opts = "+hilnvc:C:p:d:f:D:";
static const struct option long_opts[] = {{"command", required_argument, NULL, 'c'},
{"init-command", required_argument, NULL, 'C'},
{"features", required_argument, NULL, 'f'},
{"debug-level", required_argument, NULL, 'd'},
{"debug-stack-frames", required_argument, NULL, 'D'},
{"interactive", no_argument, NULL, 'i'},
@ -277,6 +281,10 @@ static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) {
}
break;
}
case 'f': {
opts->features = str2wcstring(optarg);
break;
}
case 'h': {
opts->batch_cmds.push_back("__fish_print_help fish");
break;
@ -375,6 +383,8 @@ int main(int argc, char **argv) {
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
env_init(&paths);
// Set features early in case other initialization depends on them.
mutable_fish_features().set_from_string(opts.features);
proc_init();
builtin_init();
misc_init();

View file

@ -1363,6 +1363,11 @@ static void test_feature_flags() {
f.set(ft::stderr_nocaret, false);
do_test(!f.test(ft::stderr_nocaret));
f.set_from_string(L"stderr-nocaret,nonsense");
do_test(f.test(ft::stderr_nocaret));
f.set_from_string(L"stderr-nocaret,no-stderr-nocaret,nonsense");
do_test(!f.test(ft::stderr_nocaret));
// Ensure every metadata is represented once.
size_t counts[ft::flag_count] = {};
for (const auto &md : ft::metadata) {

View file

@ -21,3 +21,37 @@ const struct features_t::metadata_t *features_t::metadata_for(const wchar_t *nam
}
return nullptr;
}
void features_t::set_from_string(const wcstring &str) {
wcstring_list_t entries = split_string(str, L',');
const wchar_t *whitespace = L"\t\n\v\f\r ";
for (wcstring entry : entries) {
if (entry.empty()) continue;
// Trim leading and trailing whitespace
entry.erase(0, entry.find_first_not_of(whitespace));
entry.erase(entry.find_last_not_of(whitespace) + 1);
const wchar_t *name = entry.c_str();
bool value = true;
// A "no-" prefix inverts the sense.
if (string_prefixes_string(L"no-", name)) {
value = false;
name += 3; // wcslen(L"no-")
}
// Look for a feature with this name. If we don't find it, assume it's a group name and set
// all features whose group contain it. Do nothing even if the string is unrecognized; this
// is to allow uniform invocations of fish (e.g. disable a feature that is only present in
// future versions).
// The special name 'all' may be used for those who like to live on the edge.
if (const metadata_t *md = metadata_for(name)) {
this->set(md->flag, value);
} else {
for (const metadata_t &md : metadata) {
if (wcsstr(md.groups, name) || !wcscmp(name, L"all")) {
this->set(md.flag, value);
}
}
}
}
}

View file

@ -3,6 +3,9 @@
#define FISH_FUTURE_FEATURE_FLAGS_H
#include <assert.h>
#include <unordered_map>
#include "common.h"
class features_t {
public:
@ -27,6 +30,12 @@ public:
values[f] = value;
}
/// Parses a comma-separated feature-flag string, updating ourselves with the values.
/// Feature names or group names may be prefixed with "no-" to disable them.
/// The special group name "all" may be used for those who like to live on the edge.
/// Unknown features are silently ignored.
void set_from_string(const wcstring &str);
/// Metadata about feature flags.
struct metadata_t {
/// The flag itself.

View file

@ -0,0 +1 @@
--features 'no-stderr-nocaret' -c 'status test-feature stderr-nocaret; echo nocaret: $status'

View file

@ -0,0 +1 @@
nocaret: 1

View file

@ -0,0 +1 @@
--features 'stderr-nocaret' -c 'status test-feature stderr-nocaret; echo nocaret: $status'

View file

@ -0,0 +1 @@
nocaret: 0