From 809ffada40b5ca1ddd504688e05600a16058e352 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 11 Feb 2015 16:28:21 +0100 Subject: [PATCH] format: redesign how grouping behaves so it's more sensible --- doc/config | 14 ++++-- src/format.cpp | 20 ++++---- src/format.h | 120 ++++++++++++++++++++++++++++++++--------------- src/settings.cpp | 6 +-- 4 files changed, 103 insertions(+), 57 deletions(-) diff --git a/doc/config b/doc/config index eedacd42..ccb23a6c 100644 --- a/doc/config +++ b/doc/config @@ -133,7 +133,7 @@ # ##### song format ##### ## -## for song format you can use: +## For a song format you can use: ## ## %l - length ## %f - filename @@ -153,10 +153,14 @@ ## %P - priority ## $R - begin right alignment ## -## You can also put them in { } and then it will be displayed -## only if all requested values are available and/or define alternate -## value with { }|{ } eg. {%a - %t}|{%f}. It is worth noting that -## a single bracket {..} is equivalent to the alternative {..}|{}. +## If you want to make sure that a part of the format is displayed +## only when certain tags are present, you can archieve it by +## grouping them with brackets, e.g. '{%a - %t}' will be evaluated +## to 'ARTIST - TITLE' if both tags are present or '' otherwise. +## It is also possible to define a list of alternatives by providing +## several groups and separating them with '|', e.g. '{%t}|{%f}' +## will be evaluated to 'TITLE' or 'FILENAME' if the former is not +## present. ## ## Note: If you want to set limit on maximal length of a tag, just ## put the appropriate number between % and character that defines diff --git a/src/format.cpp b/src/format.cpp index 025cf2ae..329df462 100644 --- a/src/format.cpp +++ b/src/format.cpp @@ -64,7 +64,8 @@ expressions parseBracket(const string &s, string token; expressions tmp, result; auto push_token = [&] { - result.push_back(std::move(token)); + if (!token.empty()) + result.push_back(std::move(token)); }; for (; it != end; ++it) { @@ -72,7 +73,7 @@ expressions parseBracket(const string &s, { push_token(); bool done; - Format::Any any; + Format::FirstOf first_of; do { auto jt = it; @@ -95,11 +96,11 @@ expressions parseBracket(const string &s, // recursively parse the bracket tmp = parseBracket(s, it, jt, flags); // if the inner bracket contains only one expression, - // put it as is. otherwise require all of them. + // put it as is. otherwise make a group out of them. if (tmp.size() == 1) - any.base().push_back(std::move(tmp[0])); + first_of.base().push_back(std::move(tmp[0])); else - any.base().push_back(Format::All(std::move(tmp))); + first_of.base().push_back(Format::Group(std::move(tmp))); it = jt; // check for the alternative ++jt; @@ -114,13 +115,8 @@ expressions parseBracket(const string &s, } } while (!done); - assert(!any.base().empty()); - // if there was only one bracket, append empty branch - // so that it always evaluates to true. otherwise put - // it as is. - if (any.base().size() == 1) - any.base().push_back(string()); - result.push_back(std::move(any)); + assert(!first_of.base().empty()); + result.push_back(std::move(first_of)); } else if (flags & Format::Flags::Tag && *it == '%') { diff --git a/src/format.h b/src/format.h index a2762e69..378943e4 100644 --- a/src/format.h +++ b/src/format.h @@ -40,11 +40,11 @@ const unsigned Tag = 8; const unsigned All = Color | Format | OutputSwitch | Tag; } -enum class ListType { All, Any, AST }; +enum class ListType { Group, FirstOf, AST }; template struct List; -template using All = List; -template using Any = List; +template using Group = List; +template using FirstOf = List; template using AST = List; struct OutputSwitch { }; @@ -63,6 +63,41 @@ private: unsigned m_delimiter; }; +enum class Result { Empty, Missing, Ok }; + +// Commutative binary operation such that: +// - Empty + Empty = Empty +// - Empty + Missing = Missing +// - Empty + Ok = Ok +// - Missing + Missing = Missing +// - Missing + Ok = Missing +// - Ok + Ok = Ok +inline Result &operator+=(Result &base, Result result) +{ + if (base == Result::Missing || result == Result::Missing) + base = Result::Missing; + else if (base == Result::Ok || result == Result::Ok) + base = Result::Ok; + return base; +} + +/*inline std::ostream &operator<<(std::ostream &os, Result r) +{ + switch (r) + { + case Result::Empty: + os << "empty"; + break; + case Result::Missing: + os << "missing"; + break; + case Result::Ok: + os << "ok"; + break; + } + return os; +}*/ + template using Expression = boost::variant< std::basic_string, @@ -70,8 +105,8 @@ using Expression = boost::variant< NC::Format, OutputSwitch, SongTag, - boost::recursive_wrapper>, - boost::recursive_wrapper> + boost::recursive_wrapper>, + boost::recursive_wrapper> >; template @@ -92,7 +127,7 @@ private: }; template -struct Printer: boost::static_visitor +struct Printer: boost::static_visitor { typedef std::basic_string StringT; @@ -105,34 +140,39 @@ struct Printer: boost::static_visitor , m_flags(flags) { } - bool operator()(const StringT &s) + Result operator()(const StringT &s) { - output(s); - return true; + if (!s.empty()) + { + output(s); + return Result::Ok; + } + else + return Result::Empty; } - bool operator()(const NC::Color &c) + Result operator()(const NC::Color &c) { if (m_flags & Flags::Color) output(c); - return true; + return Result::Empty; } - bool operator()(NC::Format fmt) + Result operator()(NC::Format fmt) { if (m_flags & Flags::Format) output(fmt); - return true; + return Result::Empty; } - bool operator()(OutputSwitch) + Result operator()(OutputSwitch) { if (!m_no_output) m_output_switched = true; - return true; + return Result::Ok; } - bool operator()(const SongTag &st) + Result operator()(const SongTag &st) { StringT tags; if (m_flags & Flags::Tag && m_song != nullptr) @@ -152,40 +192,46 @@ struct Printer: boost::static_visitor tags = wideShorten(tags, st.delimiter()); } output(tags); - return true; + return Result::Ok; } else - return false; + return Result::Missing; } - bool operator()(const All &all) + // If all Empty -> Empty, if any Ok -> continue with Ok, if any Missing -> stop with Empty. + Result operator()(const Group &group) { - auto visit = [this, &all] { - return std::all_of( - all.base().begin(), - all.base().end(), - [this](const Expression &ex) { - return boost::apply_visitor(*this, ex); + auto visit = [this, &group] { + Result result = Result::Empty; + for (const auto &ex : group.base()) + { + result += boost::apply_visitor(*this, ex); + if (result == Result::Missing) + { + result = Result::Empty; + break; } - ); + } + return result; }; + ++m_no_output; - bool all_ok = visit(); + Result result = visit(); --m_no_output; - if (!m_no_output && all_ok) + if (!m_no_output && result == Result::Ok) visit(); - return all_ok; + return result; } - bool operator()(const Any &any) + // If all Empty or Missing -> Empty, if any Ok -> stop with Ok. + Result operator()(const FirstOf &first_of) { - return std::any_of( - any.base().begin(), - any.base().end(), - [this](const Expression &ex) { - return boost::apply_visitor(*this, ex); - } - ); + for (const auto &ex : first_of.base()) + { + if (boost::apply_visitor(*this, ex) == Result::Ok) + return Result::Ok; + } + return Result::Empty; } private: diff --git a/src/settings.cpp b/src/settings.cpp index d0791289..a42dbc35 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -125,14 +125,14 @@ Format::AST columns_to_format(const std::vector &columns) auto column = columns.begin(); while (true) { - Format::Any any; + Format::FirstOf first_of; for (const auto &type : column->type) { auto f = charToGetFunction(type); assert(f != nullptr); - any.base().push_back(f); + first_of.base().push_back(f); } - result.push_back(std::move(any)); + result.push_back(std::move(first_of)); if (++column != columns.end()) result.push_back(" ");