Add support for feature flags

This introduces a new type features_t that exposes feature flags. The intent
is to allow a deprecation/incremental adoption path. This is not a general
purpose configuration mechanism, but instead allows for compatibility during
the transition as features are added/removed.

Each feature has a user-presentable short name and a short description. Their
values are tracked in a struct features_t.

We start with one feature stderr_nocaret, but it's not hooked up yet.
This commit is contained in:
ridiculousfish 2018-04-24 12:09:13 -07:00
parent 7cbc0c371a
commit 14f766b66d
5 changed files with 114 additions and 1 deletions

View file

@ -58,6 +58,7 @@ SET(FISH_SRCS
src/postfork.cpp src/proc.cpp src/reader.cpp src/sanity.cpp src/screen.cpp
src/signal.cpp src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp
src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp src/wutil.cpp
src/future_feature_flags.cpp
)
# Header files are just globbed.

View file

@ -118,7 +118,8 @@ FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bg.o obj/builtin_bind.o ob
obj/parse_productions.o obj/parse_tree.o obj/parse_util.o obj/parser.o \
obj/parser_keywords.o obj/path.o obj/postfork.o obj/proc.o obj/reader.o \
obj/sanity.o obj/screen.o obj/signal.o obj/tinyexpr.o obj/tokenizer.o obj/tnode.o obj/utf8.o \
obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o
obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o \
obj/future_feature_flags.o
FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS)

View file

@ -45,6 +45,7 @@
#include "expand.h"
#include "fallback.h" // IWYU pragma: keep
#include "function.h"
#include "future_feature_flags.h"
#include "highlight.h"
#include "history.h"
#include "input.h"
@ -1352,6 +1353,29 @@ static void test_utf8() {
#endif
}
static void test_feature_flags() {
say(L"Testing future feature flags");
using ft = features_t;
ft f;
do_test(!f.test(ft::stderr_nocaret));
f.set(ft::stderr_nocaret, true);
do_test(f.test(ft::stderr_nocaret));
f.set(ft::stderr_nocaret, false);
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) {
counts[md.flag]++;
}
for (size_t c : counts) {
do_test(c == 1);
}
do_test(ft::metadata[ft::stderr_nocaret].name == wcstring(L"stderr-nocaret"));
do_test(ft::metadata_for(L"stderr-nocaret") == &ft::metadata[ft::stderr_nocaret]);
do_test(ft::metadata_for(L"not-a-flag") == nullptr);
}
static void test_escape_sequences() {
say(L"Testing escape_sequences");
if (escape_code_length(L"") != 0) err(L"test_escape_sequences failed on line %d\n", __LINE__);
@ -4611,6 +4635,7 @@ int main(int argc, char **argv) {
if (should_test_function("cancellation")) test_cancellation();
if (should_test_function("indents")) test_indents();
if (should_test_function("utf8")) test_utf8();
if (should_test_function("feature_flags")) test_feature_flags();
if (should_test_function("escape_sequences")) test_escape_sequences();
if (should_test_function("lru")) test_lru();
if (should_test_function("expand")) test_expand();

View file

@ -0,0 +1,23 @@
#include "config.h" // IWYU pragma: keep
#include <wchar.h>
#include "future_feature_flags.h"
/// The set of features applying to this instance.
static features_t global_features;
const features_t &fish_features() { return global_features; }
features_t &mutable_fish_features() { return global_features; }
const features_t::metadata_t features_t::metadata[features_t::flag_count] = {
{stderr_nocaret, L"stderr-nocaret", L"3.0", L"^ no longer redirects stderr"},
};
const struct features_t::metadata_t *features_t::metadata_for(const wchar_t *name) {
assert(name && "null flag name");
for (const auto &md : metadata) {
if (!wcscmp(name, md.name)) return &md;
}
return nullptr;
}

View file

@ -0,0 +1,63 @@
// Flags to enable upcoming features
#ifndef FISH_FUTURE_FEATURE_FLAGS_H
#define FISH_FUTURE_FEATURE_FLAGS_H
#include <assert.h>
class features_t {
public:
/// The list of flags.
enum flag_t {
/// Whether ^ is supported for stderr redirection.
stderr_nocaret,
/// The number of flags.
flag_count
};
/// Return whether a flag is set.
bool test(flag_t f) const {
assert(f >= 0 && f < flag_count && "Invalid flag");
return values[f];
}
/// Set a flag.
void set(flag_t f, bool value) {
assert(f >= 0 && f < flag_count && "Invalid flag");
values[f] = value;
}
/// Metadata about feature flags.
struct metadata_t {
/// The flag itself.
features_t::flag_t flag;
/// User-presentable short name of the feature flag.
const wchar_t *name;
/// Comma-separated list of feature groups.
const wchar_t *groups;
/// User-presentable description of the feature flag.
const wchar_t *description;
};
/// The metadata, indexed by flag.
static const metadata_t metadata[flag_count];
/// Return the metadata for a particular name, or nullptr if not found.
static const struct metadata_t *metadata_for(const wchar_t *name);
private:
/// Values for the flags.
bool values[flag_count] = {};
};
/// Return the global set of features for fish. This is const to prevent accidental mutation.
const features_t &fish_features();
/// Return the global set of features for fish, but mutable. In general fish features should be set
/// at startup only.
features_t &mutable_fish_features();
#endif