What this means is that the utility code, basic math, value expressions, string formatting and option handling are now entirely decoupled from the rest of the code. This decoupling not only greatly simplifies the more basic parts of Ledger, but makes it much easier to test and verify its completeness. For example, when the formatting code %X is seen by the format parser, it turns into a call to the expression function fmt_X, which must be defined when the format string is first compiled against an object. If that object is a transaction, the transaction's scope will be the first to have a chance at providing a definition. If an account is being reported, it will. If neither does, the next scope in sequence -- soon to be the current report -- will, and then the session object that "owns" the current Ledger session. In 2.6, the formatting code new everything about transaction and accounts, and relied on flags to communicate special details between them. Now the transaction will offer the details for its own reporting, while the formatter worries only about strings and how to output them.
112 lines
2.1 KiB
C++
112 lines
2.1 KiB
C++
#ifndef _FORMAT_H
|
|
#define _FORMAT_H
|
|
|
|
#include "journal.h"
|
|
#include "expr.h"
|
|
#include "walk.h"
|
|
|
|
namespace ledger {
|
|
|
|
DECLARE_EXCEPTION(format_error, std::runtime_error);
|
|
|
|
class format_t : public noncopyable
|
|
{
|
|
struct element_t : public noncopyable
|
|
{
|
|
#define ELEMENT_ALIGN_LEFT 0x01
|
|
#define ELEMENT_HIGHLIGHT 0x02
|
|
|
|
enum kind_t {
|
|
STRING,
|
|
EXPR,
|
|
#if 0
|
|
DEPTH_SPACER
|
|
#endif
|
|
};
|
|
|
|
kind_t type;
|
|
unsigned char flags;
|
|
unsigned char min_width;
|
|
unsigned char max_width;
|
|
string chars;
|
|
expr_t expr;
|
|
|
|
scoped_ptr<struct element_t> next;
|
|
|
|
element_t() throw()
|
|
: type(STRING), flags(false), min_width(0), max_width(0) {
|
|
TRACE_CTOR(element_t, "");
|
|
}
|
|
~element_t() throw() {
|
|
TRACE_DTOR(element_t);
|
|
}
|
|
|
|
friend inline void mark_red(std::ostream& out, const element_t * elem) {
|
|
out.setf(std::ios::left);
|
|
out.width(0);
|
|
out << "\e[31m";
|
|
|
|
if (elem->flags & ELEMENT_ALIGN_LEFT)
|
|
out << std::left;
|
|
else
|
|
out << std::right;
|
|
|
|
if (elem->min_width > 0)
|
|
out.width(elem->min_width);
|
|
}
|
|
|
|
void dump(std::ostream& out) const;
|
|
};
|
|
|
|
string format_string;
|
|
scoped_ptr<element_t> elements;
|
|
|
|
enum elision_style_t {
|
|
TRUNCATE_TRAILING,
|
|
TRUNCATE_MIDDLE,
|
|
TRUNCATE_LEADING,
|
|
ABBREVIATE
|
|
};
|
|
|
|
static elision_style_t elision_style;
|
|
static int abbrev_length;
|
|
|
|
static bool ansi_codes;
|
|
static bool ansi_invert;
|
|
|
|
public:
|
|
format_t() {
|
|
TRACE_CTOR(format_t, "");
|
|
}
|
|
format_t(const string& _format) {
|
|
TRACE_CTOR(format_t, "const string&");
|
|
parse(_format);
|
|
}
|
|
~format_t() {
|
|
TRACE_DTOR(format_t);
|
|
}
|
|
|
|
void parse(const string& _format) {
|
|
elements.reset(parse_elements(_format));
|
|
format_string = _format;
|
|
}
|
|
|
|
void format(std::ostream& out, scope_t& scope);
|
|
|
|
void dump(std::ostream& out) const {
|
|
for (const element_t * elem = elements.get();
|
|
elem;
|
|
elem = elem->next.get())
|
|
elem->dump(out);
|
|
}
|
|
|
|
private:
|
|
static element_t * parse_elements(const string& fmt);
|
|
|
|
static string truncate(const string& str, unsigned int width,
|
|
const bool is_account = false);
|
|
};
|
|
|
|
} // namespace ledger
|
|
|
|
#endif // _FORMAT_H
|