format: add flags to refine parsing and printing of the AST

This commit is contained in:
Andrzej Rybczak
2014-11-11 19:17:18 +01:00
parent 3aa1ba1b9d
commit 81b907a974
5 changed files with 115 additions and 79 deletions

View File

@@ -110,9 +110,11 @@ void showSongs(NC::Menu<T> &menu, const MPD::Song &s,
bool separate_albums, is_now_playing, is_selected, discard_colors; bool separate_albums, is_now_playing, is_selected, discard_colors;
setProperties(menu, s, pl, separate_albums, is_now_playing, is_selected, discard_colors); setProperties(menu, s, pl, separate_albums, is_now_playing, is_selected, discard_colors);
size_t y = menu.getY(); const size_t y = menu.getY();
NC::Buffer right_aligned; NC::Buffer right_aligned;
Format::print(ast, menu, &s, &right_aligned); Format::print(ast, menu, &s, &right_aligned,
discard_colors ? Format::Flags::Tag | Format::Flags::OutputSwitch : Format::Flags::All
);
if (!right_aligned.str().empty()) if (!right_aligned.str().empty())
{ {
size_t x_off = menu.getWidth() - wideLength(ToWString(right_aligned.str())); size_t x_off = menu.getWidth() - wideLength(ToWString(right_aligned.str()));

View File

@@ -25,6 +25,10 @@
namespace { namespace {
const unsigned properties = Format::Flags::Color
| Format::Flags::Format
| Format::Flags::OutputSwitch;
template <typename CharT> using string = std::basic_string<CharT>; template <typename CharT> using string = std::basic_string<CharT>;
template <typename CharT> using iterator = typename std::basic_string<CharT>::const_iterator; template <typename CharT> using iterator = typename std::basic_string<CharT>::const_iterator;
template <typename CharT> using expressions = std::vector<Format::Expression<CharT>>; template <typename CharT> using expressions = std::vector<Format::Expression<CharT>>;
@@ -54,7 +58,8 @@ void rangeCheck(const string<CharT> &s, iterator<CharT> current, iterator<CharT>
template <typename CharT> template <typename CharT>
expressions<CharT> parseBracket(const string<CharT> &s, expressions<CharT> parseBracket(const string<CharT> &s,
iterator<CharT> it, iterator<CharT> end) iterator<CharT> it, iterator<CharT> end,
const unsigned flags)
{ {
string<CharT> token; string<CharT> token;
expressions<CharT> tmp, result; expressions<CharT> tmp, result;
@@ -88,7 +93,7 @@ expressions<CharT> parseBracket(const string<CharT> &s,
// skip the opening bracket // skip the opening bracket
++it; ++it;
// recursively parse the bracket // recursively parse the bracket
tmp = parseBracket(s, it, jt); tmp = parseBracket(s, it, jt, flags);
// if the inner bracket contains only one expression, // if the inner bracket contains only one expression,
// put it as is. otherwise require all of them. // put it as is. otherwise require all of them.
if (tmp.size() == 1) if (tmp.size() == 1)
@@ -117,7 +122,7 @@ expressions<CharT> parseBracket(const string<CharT> &s,
any.base().push_back(string<CharT>()); any.base().push_back(string<CharT>());
result.push_back(std::move(any)); result.push_back(std::move(any));
} }
else if (*it == '%') else if (flags & Format::Flags::Tag && *it == '%')
{ {
++it; ++it;
rangeCheck(s, it, end); rangeCheck(s, it, end);
@@ -144,7 +149,7 @@ expressions<CharT> parseBracket(const string<CharT> &s,
throwError(s, it, invalidCharacter(*it)); throwError(s, it, invalidCharacter(*it));
result.push_back(Format::SongTag(f, delimiter)); result.push_back(Format::SongTag(f, delimiter));
} }
else if (*it == '$') else if (flags & properties && *it == '$')
{ {
++it; ++it;
rangeCheck(s, it, end); rangeCheck(s, it, end);
@@ -155,37 +160,14 @@ expressions<CharT> parseBracket(const string<CharT> &s,
continue; continue;
} }
push_token(); push_token();
if (isdigit(*it)) // legacy colors
if (flags & Format::Flags::Color && isdigit(*it))
{ {
auto color = charToColor(*it); auto color = charToColor(*it);
result.push_back(color); result.push_back(color);
} }
else if (*it == 'R') // new colors
result.push_back(Format::AlignRight()); else if (flags & Format::Flags::Color && *it == '(')
else if (*it == 'b')
result.push_back(NC::Format::Bold);
else if (*it == 'u')
result.push_back(NC::Format::Underline);
else if (*it == 'a')
result.push_back(NC::Format::AltCharset);
else if (*it == 'r')
result.push_back(NC::Format::Reverse);
else if (*it == '/')
{
++it;
rangeCheck(s, it, end);
if (*it == 'b')
result.push_back(NC::Format::NoBold);
else if (*it == 'u')
result.push_back(NC::Format::NoUnderline);
else if (*it == 'a')
result.push_back(NC::Format::NoAltCharset);
else if (*it == 'r')
result.push_back(NC::Format::NoReverse);
else
throwError(s, it, invalidCharacter(*it));
}
else if (*it == '(')
{ {
++it; ++it;
rangeCheck(s, it, end); rangeCheck(s, it, end);
@@ -202,6 +184,33 @@ expressions<CharT> parseBracket(const string<CharT> &s,
throwError(s, jt, "invalid color \"" + value + "\""); throwError(s, jt, "invalid color \"" + value + "\"");
} }
} }
// output switch
else if (flags & Format::Flags::OutputSwitch && *it == 'R')
result.push_back(Format::OutputSwitch());
// format
else if (flags & Format::Flags::Format && *it == 'b')
result.push_back(NC::Format::Bold);
else if (flags & Format::Flags::Format && *it == 'u')
result.push_back(NC::Format::Underline);
else if (flags & Format::Flags::Format && *it == 'a')
result.push_back(NC::Format::AltCharset);
else if (flags & Format::Flags::Format && *it == 'r')
result.push_back(NC::Format::Reverse);
else if (flags & Format::Flags::Format && *it == '/')
{
++it;
rangeCheck(s, it, end);
if (*it == 'b')
result.push_back(NC::Format::NoBold);
else if (*it == 'u')
result.push_back(NC::Format::NoUnderline);
else if (*it == 'a')
result.push_back(NC::Format::NoAltCharset);
else if (*it == 'r')
result.push_back(NC::Format::NoReverse);
else
throwError(s, it, invalidCharacter(*it));
}
else else
throwError(s, it, invalidCharacter(*it)); throwError(s, it, invalidCharacter(*it));
} }
@@ -216,14 +225,14 @@ expressions<CharT> parseBracket(const string<CharT> &s,
namespace Format { namespace Format {
AST<char> parse(const std::string &s) AST<char> parse(const std::string &s, const unsigned flags)
{ {
return AST<char>(parseBracket(s, s.begin(), s.end())); return AST<char>(parseBracket(s, s.begin(), s.end(), flags));
} }
AST<wchar_t> wparse(const std::wstring &s) AST<wchar_t> parse(const std::wstring &s, const unsigned flags)
{ {
return AST<wchar_t>(parseBracket(s, s.begin(), s.end())); return AST<wchar_t>(parseBracket(s, s.begin(), s.end(), flags));
} }
} }

View File

@@ -31,6 +31,15 @@
namespace Format { namespace Format {
namespace Flags {
const unsigned None = 0;
const unsigned Color = 1;
const unsigned Format = 2;
const unsigned OutputSwitch = 4;
const unsigned Tag = 8;
const unsigned All = Color | Format | OutputSwitch | Tag;
}
enum class ListType { All, Any, AST }; enum class ListType { All, Any, AST };
template <ListType, typename> struct List; template <ListType, typename> struct List;
@@ -38,7 +47,7 @@ template <typename CharT> using All = List<ListType::All, CharT>;
template <typename CharT> using Any = List<ListType::Any, CharT>; template <typename CharT> using Any = List<ListType::Any, CharT>;
template <typename CharT> using AST = List<ListType::AST, CharT>; template <typename CharT> using AST = List<ListType::AST, CharT>;
struct AlignRight { }; struct OutputSwitch { };
struct SongTag struct SongTag
{ {
@@ -59,7 +68,7 @@ using Expression = boost::variant<
std::basic_string<CharT>, std::basic_string<CharT>,
NC::Color, NC::Color,
NC::Format, NC::Format,
AlignRight, OutputSwitch,
SongTag, SongTag,
boost::recursive_wrapper<Any<CharT>>, boost::recursive_wrapper<Any<CharT>>,
boost::recursive_wrapper<All<CharT>> boost::recursive_wrapper<All<CharT>>
@@ -82,17 +91,18 @@ private:
Base m_base; Base m_base;
}; };
template <typename CharT, typename OutputT, typename OutputAfterAlignmentT = OutputT> template <typename CharT, typename OutputT, typename SecondOutputT = OutputT>
struct Printer: boost::static_visitor<bool> struct Printer: boost::static_visitor<bool>
{ {
typedef std::basic_string<CharT> StringT; typedef std::basic_string<CharT> StringT;
Printer(OutputT &os, const MPD::Song *song, OutputAfterAlignmentT *os_after_alignment) Printer(OutputT &os, const MPD::Song *song, SecondOutputT *second_os, const unsigned flags)
: m_output(os) : m_output(os)
, m_song(song) , m_song(song)
, m_after_alignment(false) , m_output_switched(false)
, m_output_after_alignment(os_after_alignment) , m_second_os(second_os)
, m_no_output(0) , m_no_output(0)
, m_flags(flags)
{ } { }
bool operator()(const StringT &s) bool operator()(const StringT &s)
@@ -103,27 +113,29 @@ struct Printer: boost::static_visitor<bool>
bool operator()(const NC::Color &c) bool operator()(const NC::Color &c)
{ {
output(c); if (m_flags & Flags::Color)
output(c);
return true; return true;
} }
bool operator()(NC::Format fmt) bool operator()(NC::Format fmt)
{ {
output(fmt); if (m_flags & Flags::Format)
output(fmt);
return true; return true;
} }
bool operator()(AlignRight) bool operator()(OutputSwitch)
{ {
if (!m_no_output) if (!m_no_output)
m_after_alignment = true; m_output_switched = true;
return true; return true;
} }
bool operator()(const SongTag &st) bool operator()(const SongTag &st)
{ {
StringT tags; StringT tags;
if (m_song != nullptr) if (m_flags & Flags::Tag && m_song != nullptr)
{ {
tags = convertString<CharT, char>::apply( tags = convertString<CharT, char>::apply(
m_song->getTags(st.function()) m_song->getTags(st.function())
@@ -198,11 +210,13 @@ private:
result += s; result += s;
} }
}; };
// when writing to a string, ignore all properties // when writing to a string, we should ignore all other
// properties. if this code is reached, throw an exception.
template <typename ValueT, typename SomeCharT> template <typename ValueT, typename SomeCharT>
struct output_<ValueT, std::basic_string<SomeCharT>> struct output_<ValueT, std::basic_string<SomeCharT>> {
{ static void exec(std::basic_string<CharT> &, const ValueT &) {
static void exec(std::basic_string<CharT> &, const ValueT &) { } throw std::logic_error("non-string property can't be appended to the string");
}
}; };
template <typename ValueT> template <typename ValueT>
@@ -210,8 +224,8 @@ private:
{ {
if (!m_no_output) if (!m_no_output)
{ {
if (m_after_alignment && m_output_after_alignment != nullptr) if (m_output_switched && m_second_os != nullptr)
output_<ValueT, OutputAfterAlignmentT>::exec(*m_output_after_alignment, value); output_<ValueT, SecondOutputT>::exec(*m_second_os, value);
else else
output_<ValueT, OutputT>::exec(m_output, value); output_<ValueT, OutputT>::exec(m_output, value);
} }
@@ -220,10 +234,11 @@ private:
OutputT &m_output; OutputT &m_output;
const MPD::Song *m_song; const MPD::Song *m_song;
bool m_after_alignment; bool m_output_switched;
OutputAfterAlignmentT *m_output_after_alignment; SecondOutputT *m_second_os;
unsigned m_no_output; unsigned m_no_output;
const unsigned m_flags;
}; };
template <typename CharT, typename VisitorT> template <typename CharT, typename VisitorT>
@@ -234,17 +249,18 @@ void visit(VisitorT &visitor, const AST<CharT> &ast)
} }
template <typename CharT, typename ItemT> template <typename CharT, typename ItemT>
void print(const AST<CharT> &ast, NC::Menu<ItemT> &menu, void print(const AST<CharT> &ast, NC::Menu<ItemT> &menu, const MPD::Song *song,
const MPD::Song *song, NC::BasicBuffer<CharT> *buffer) NC::BasicBuffer<CharT> *buffer, const unsigned flags = Flags::All)
{ {
Printer<CharT, NC::Menu<ItemT>, NC::Buffer> printer(menu, song, buffer); Printer<CharT, NC::Menu<ItemT>, NC::Buffer> printer(menu, song, buffer, flags);
visit(printer, ast); visit(printer, ast);
} }
template <typename CharT> template <typename CharT>
void print(const AST<CharT> &ast, NC::BasicBuffer<CharT> &buffer, const MPD::Song *song) void print(const AST<CharT> &ast, NC::BasicBuffer<CharT> &buffer,
const MPD::Song *song, const unsigned flags = Flags::All)
{ {
Printer<CharT, NC::BasicBuffer<CharT>> printer(buffer, song, &buffer); Printer<CharT, NC::BasicBuffer<CharT>> printer(buffer, song, &buffer, flags);
visit(printer, ast); visit(printer, ast);
} }
@@ -252,13 +268,13 @@ template <typename CharT>
std::basic_string<CharT> stringify(const AST<CharT> &ast, const MPD::Song *song) std::basic_string<CharT> stringify(const AST<CharT> &ast, const MPD::Song *song)
{ {
std::basic_string<CharT> result; std::basic_string<CharT> result;
Printer<CharT, std::basic_string<CharT>> printer(result, song, &result); Printer<CharT, std::basic_string<CharT>> printer(result, song, &result, Flags::Tag);
visit(printer, ast); visit(printer, ast);
return result; return result;
} }
AST<char> parse(const std::string &s); AST<char> parse(const std::string &s, const unsigned flags = Flags::All);
AST<wchar_t> wparse(const std::wstring &ws); AST<wchar_t> parse(const std::wstring &ws, const unsigned flags = Flags::All);
} }

View File

@@ -129,7 +129,7 @@ std::wstring Lyrics::title()
std::wstring result = L"Lyrics: "; std::wstring result = L"Lyrics: ";
result += Scroller( result += Scroller(
Format::stringify<wchar_t>(Format::wparse(L"%a - %t"), &itsSong), Format::stringify<wchar_t>(Format::parse(L"%a - %t"), &itsSong),
itsScrollBegin, itsScrollBegin,
COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length()) COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length())
); );

View File

@@ -165,7 +165,8 @@ option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map)
{ {
return option_parser::worker(assign<std::string>(arg, [&arg, map](std::string s) { return option_parser::worker(assign<std::string>(arg, [&arg, map](std::string s) {
NC::Buffer result; NC::Buffer result;
Format::print(Format::parse(s), result, nullptr); auto ast = Format::parse(s, Format::Flags::Color | Format::Flags::Format);
Format::print(ast, result, nullptr);
return map(std::move(result)); return map(std::move(result));
}), defaults_to(arg, map(std::forward<ValueT>(value)))); }), defaults_to(arg, map(std::forward<ValueT>(value))));
} }
@@ -271,30 +272,37 @@ bool Configuration::read(const std::string &config_path)
message_delay_time, 5 message_delay_time, 5
)); ));
p.add("song_list_format", assign_default<std::string>( p.add("song_list_format", assign_default<std::string>(
song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", Format::parse song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) {
)); return Format::parse(v);
}));
p.add("song_status_format", assign_default<std::string>( p.add("song_status_format", assign_default<std::string>(
song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) { song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
// precompute wide format for status display // precompute wide format for status display
song_status_wformat = Format::wparse(ToWString(v)); song_status_wformat = Format::parse(ToWString(v));
return Format::parse(v); return Format::parse(v);
})); }));
p.add("song_library_format", assign_default<std::string>( p.add("song_library_format", assign_default<std::string>(
song_library_format, "{%n - }{%t}|{%f}", Format::parse song_library_format, "{%n - }{%t}|{%f}", [](std::string v) {
)); return Format::parse(v);
}));
p.add("browser_sort_mode", assign_default( p.add("browser_sort_mode", assign_default(
browser_sort_mode, SortMode::Name browser_sort_mode, SortMode::Name
)); ));
p.add("browser_sort_format", assign_default<std::string>( p.add("browser_sort_format", assign_default<std::string>(
browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", Format::parse browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", [](std::string v) {
)); return Format::parse(v, Format::Flags::Tag);
}));
p.add("alternative_header_first_line_format", assign_default<std::string>( p.add("alternative_header_first_line_format", assign_default<std::string>(
new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [this](std::string v) { new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) {
return Format::wparse(ToWString(std::move(v))); return Format::parse(ToWString(std::move(v)),
Format::Flags::All ^ Format::Flags::OutputSwitch
);
})); }));
p.add("alternative_header_second_line_format", assign_default<std::string>( p.add("alternative_header_second_line_format", assign_default<std::string>(
new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [this](std::string v) { new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
return Format::wparse(ToWString(std::move(v))); return Format::parse(ToWString(std::move(v)),
Format::Flags::All ^ Format::Flags::OutputSwitch
);
})); }));
p.add("now_playing_prefix", buffer( p.add("now_playing_prefix", buffer(
now_playing_prefix, NC::Buffer::init(NC::Format::Bold), [this](NC::Buffer buf) { now_playing_prefix, NC::Buffer::init(NC::Format::Bold), [this](NC::Buffer buf) {
@@ -323,8 +331,9 @@ bool Configuration::read(const std::string &config_path)
modified_item_prefix, NC::Buffer::init(NC::Color::Green, "> ", NC::Color::End), id_() modified_item_prefix, NC::Buffer::init(NC::Color::Green, "> ", NC::Color::End), id_()
)); ));
p.add("song_window_title_format", assign_default<std::string>( p.add("song_window_title_format", assign_default<std::string>(
song_window_title_format, "{%a - }{%t}|{%f}", Format::parse song_window_title_format, "{%a - }{%t}|{%f}", [](std::string v) {
)); return Format::parse(v, Format::Flags::Tag);
}));
p.add("song_columns_list_format", assign_default<std::string>( p.add("song_columns_list_format", assign_default<std::string>(
columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}", columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
[this](std::string v) { [this](std::string v) {