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;
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;
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())
{
size_t x_off = menu.getWidth() - wideLength(ToWString(right_aligned.str()));

View File

@@ -25,6 +25,10 @@
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 iterator = typename std::basic_string<CharT>::const_iterator;
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>
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;
expressions<CharT> tmp, result;
@@ -88,7 +93,7 @@ expressions<CharT> parseBracket(const string<CharT> &s,
// skip the opening bracket
++it;
// recursively parse the bracket
tmp = parseBracket(s, it, jt);
tmp = parseBracket(s, it, jt, flags);
// if the inner bracket contains only one expression,
// put it as is. otherwise require all of them.
if (tmp.size() == 1)
@@ -117,7 +122,7 @@ expressions<CharT> parseBracket(const string<CharT> &s,
any.base().push_back(string<CharT>());
result.push_back(std::move(any));
}
else if (*it == '%')
else if (flags & Format::Flags::Tag && *it == '%')
{
++it;
rangeCheck(s, it, end);
@@ -144,7 +149,7 @@ expressions<CharT> parseBracket(const string<CharT> &s,
throwError(s, it, invalidCharacter(*it));
result.push_back(Format::SongTag(f, delimiter));
}
else if (*it == '$')
else if (flags & properties && *it == '$')
{
++it;
rangeCheck(s, it, end);
@@ -155,37 +160,14 @@ expressions<CharT> parseBracket(const string<CharT> &s,
continue;
}
push_token();
if (isdigit(*it))
// legacy colors
if (flags & Format::Flags::Color && isdigit(*it))
{
auto color = charToColor(*it);
result.push_back(color);
}
else if (*it == 'R')
result.push_back(Format::AlignRight());
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 == '(')
// new colors
else if (flags & Format::Flags::Color && *it == '(')
{
++it;
rangeCheck(s, it, end);
@@ -202,6 +184,33 @@ expressions<CharT> parseBracket(const string<CharT> &s,
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
throwError(s, it, invalidCharacter(*it));
}
@@ -216,14 +225,14 @@ expressions<CharT> parseBracket(const string<CharT> &s,
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 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 };
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 AST = List<ListType::AST, CharT>;
struct AlignRight { };
struct OutputSwitch { };
struct SongTag
{
@@ -59,7 +68,7 @@ using Expression = boost::variant<
std::basic_string<CharT>,
NC::Color,
NC::Format,
AlignRight,
OutputSwitch,
SongTag,
boost::recursive_wrapper<Any<CharT>>,
boost::recursive_wrapper<All<CharT>>
@@ -82,17 +91,18 @@ private:
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>
{
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_song(song)
, m_after_alignment(false)
, m_output_after_alignment(os_after_alignment)
, m_output_switched(false)
, m_second_os(second_os)
, m_no_output(0)
, m_flags(flags)
{ }
bool operator()(const StringT &s)
@@ -103,27 +113,29 @@ struct Printer: boost::static_visitor<bool>
bool operator()(const NC::Color &c)
{
output(c);
if (m_flags & Flags::Color)
output(c);
return true;
}
bool operator()(NC::Format fmt)
{
output(fmt);
if (m_flags & Flags::Format)
output(fmt);
return true;
}
bool operator()(AlignRight)
bool operator()(OutputSwitch)
{
if (!m_no_output)
m_after_alignment = true;
m_output_switched = true;
return true;
}
bool operator()(const SongTag &st)
{
StringT tags;
if (m_song != nullptr)
if (m_flags & Flags::Tag && m_song != nullptr)
{
tags = convertString<CharT, char>::apply(
m_song->getTags(st.function())
@@ -198,11 +210,13 @@ private:
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>
struct output_<ValueT, std::basic_string<SomeCharT>>
{
static void exec(std::basic_string<CharT> &, const ValueT &) { }
struct output_<ValueT, std::basic_string<SomeCharT>> {
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>
@@ -210,8 +224,8 @@ private:
{
if (!m_no_output)
{
if (m_after_alignment && m_output_after_alignment != nullptr)
output_<ValueT, OutputAfterAlignmentT>::exec(*m_output_after_alignment, value);
if (m_output_switched && m_second_os != nullptr)
output_<ValueT, SecondOutputT>::exec(*m_second_os, value);
else
output_<ValueT, OutputT>::exec(m_output, value);
}
@@ -220,10 +234,11 @@ private:
OutputT &m_output;
const MPD::Song *m_song;
bool m_after_alignment;
OutputAfterAlignmentT *m_output_after_alignment;
bool m_output_switched;
SecondOutputT *m_second_os;
unsigned m_no_output;
const unsigned m_flags;
};
template <typename CharT, typename VisitorT>
@@ -234,17 +249,18 @@ void visit(VisitorT &visitor, const AST<CharT> &ast)
}
template <typename CharT, typename ItemT>
void print(const AST<CharT> &ast, NC::Menu<ItemT> &menu,
const MPD::Song *song, NC::BasicBuffer<CharT> *buffer)
void print(const AST<CharT> &ast, NC::Menu<ItemT> &menu, const MPD::Song *song,
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);
}
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);
}
@@ -252,13 +268,13 @@ template <typename CharT>
std::basic_string<CharT> stringify(const AST<CharT> &ast, const MPD::Song *song)
{
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);
return result;
}
AST<char> parse(const std::string &s);
AST<wchar_t> wparse(const std::wstring &ws);
AST<char> parse(const std::string &s, const unsigned flags = Flags::All);
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: ";
result += Scroller(
Format::stringify<wchar_t>(Format::wparse(L"%a - %t"), &itsSong),
Format::stringify<wchar_t>(Format::parse(L"%a - %t"), &itsSong),
itsScrollBegin,
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) {
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));
}), defaults_to(arg, map(std::forward<ValueT>(value))));
}
@@ -271,30 +272,37 @@ bool Configuration::read(const std::string &config_path)
message_delay_time, 5
));
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>(
song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
// precompute wide format for status display
song_status_wformat = Format::wparse(ToWString(v));
song_status_wformat = Format::parse(ToWString(v));
return Format::parse(v);
}));
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(
browser_sort_mode, SortMode::Name
));
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>(
new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [this](std::string v) {
return Format::wparse(ToWString(std::move(v)));
new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string 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>(
new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [this](std::string v) {
return Format::wparse(ToWString(std::move(v)));
new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
return Format::parse(ToWString(std::move(v)),
Format::Flags::All ^ Format::Flags::OutputSwitch
);
}));
p.add("now_playing_prefix", buffer(
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_()
));
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>(
columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
[this](std::string v) {