format: add flags to refine parsing and printing of the AST
This commit is contained in:
@@ -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()));
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
70
src/format.h
70
src/format.h
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user