From 14f766b66d23f422795239e083247f6deb2b8605 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 24 Apr 2018 12:09:13 -0700 Subject: [PATCH] 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. --- CMakeLists.txt | 1 + Makefile.in | 3 +- src/fish_tests.cpp | 25 ++++++++++++++ src/future_feature_flags.cpp | 23 +++++++++++++ src/future_feature_flags.h | 63 ++++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/future_feature_flags.cpp create mode 100644 src/future_feature_flags.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a01397a17..b6514c4c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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. diff --git a/Makefile.in b/Makefile.in index 21e018dad..620d224b1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 8795fab54..e022fa166 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -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(); diff --git a/src/future_feature_flags.cpp b/src/future_feature_flags.cpp new file mode 100644 index 000000000..26b854975 --- /dev/null +++ b/src/future_feature_flags.cpp @@ -0,0 +1,23 @@ +#include "config.h" // IWYU pragma: keep + +#include +#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; +} diff --git a/src/future_feature_flags.h b/src/future_feature_flags.h new file mode 100644 index 000000000..d8373e064 --- /dev/null +++ b/src/future_feature_flags.h @@ -0,0 +1,63 @@ +// Flags to enable upcoming features +#ifndef FISH_FUTURE_FEATURE_FLAGS_H +#define FISH_FUTURE_FEATURE_FLAGS_H + +#include + +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