Added --ansi and --ansi-invert options.
This commit is contained in:
parent
fd525ec382
commit
82d0ee869b
10 changed files with 126 additions and 47 deletions
|
|
@ -47,7 +47,7 @@ endif
|
|||
if DEBUG
|
||||
libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
|
||||
endif
|
||||
libledger_la_LDFLAGS = -release 2.6
|
||||
libledger_la_LDFLAGS = -release 3.0
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
acconf.h \
|
||||
|
|
|
|||
7
NEWS
7
NEWS
|
|
@ -1,11 +1,16 @@
|
|||
|
||||
Ledger NEWS
|
||||
|
||||
* 2.6
|
||||
* 3.0
|
||||
|
||||
- Error reporting has been greatly improving, now showing full
|
||||
contextual information for most error messages.
|
||||
|
||||
- Added new --ansi reporting option, which shows negative values as
|
||||
red using ANSI terminal codes; --ansi-invert makes non-negative
|
||||
values red (which makes more sense for income and budget reports,
|
||||
for example).
|
||||
|
||||
- Added a new --only predicate, which occurs during transaction
|
||||
processing between --limit and --display. Here is a summary of how
|
||||
the three supported predicates are used:
|
||||
|
|
|
|||
12
amount.cc
12
amount.cc
|
|
@ -1344,7 +1344,7 @@ void amount_t::annotate_commodity(const amount_t& price,
|
|||
|
||||
if (commodity().annotated) {
|
||||
this_ann = &static_cast<annotated_commodity_t&>(commodity());
|
||||
this_base = this_ann->base;
|
||||
this_base = this_ann->ptr;
|
||||
} else {
|
||||
this_base = &commodity();
|
||||
}
|
||||
|
|
@ -1392,7 +1392,7 @@ amount_t amount_t::strip_annotations(const bool _keep_price,
|
|||
(_keep_tag && ! ann_comm.tag.empty()))
|
||||
{
|
||||
new_comm = annotated_commodity_t::find_or_create
|
||||
(*ann_comm.base, _keep_price ? ann_comm.price : amount_t(),
|
||||
(*ann_comm.ptr, _keep_price ? ann_comm.price : amount_t(),
|
||||
_keep_date ? ann_comm.date : 0, _keep_tag ? ann_comm.tag : "");
|
||||
} else {
|
||||
new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
|
||||
|
|
@ -1465,7 +1465,7 @@ commodity_t * commodity_t::create(const std::string& symbol)
|
|||
{
|
||||
std::auto_ptr<commodity_t> commodity(new commodity_t);
|
||||
|
||||
commodity->ptr = commodity_base_t::create(symbol);
|
||||
commodity->base = commodity_base_t::create(symbol);
|
||||
|
||||
if (needs_quotes(symbol)) {
|
||||
commodity->qualified_symbol = "\"";
|
||||
|
|
@ -1607,10 +1607,10 @@ annotated_commodity_t::create(const commodity_t& comm,
|
|||
commodity->date = date;
|
||||
commodity->tag = tag;
|
||||
|
||||
commodity->base = &comm;
|
||||
assert(commodity->base);
|
||||
commodity->ptr = comm.ptr;
|
||||
commodity->ptr = &comm;
|
||||
assert(commodity->ptr);
|
||||
commodity->base = comm.base;
|
||||
assert(commodity->base);
|
||||
|
||||
commodity->qualified_symbol = comm.symbol();
|
||||
|
||||
|
|
|
|||
56
amount.h
56
amount.h
|
|
@ -444,25 +444,25 @@ class commodity_t
|
|||
typedef unsigned long ident_t;
|
||||
|
||||
ident_t ident;
|
||||
commodity_base_t * ptr;
|
||||
commodity_base_t * base;
|
||||
std::string qualified_symbol;
|
||||
bool annotated;
|
||||
|
||||
public:
|
||||
explicit commodity_t() : ptr(NULL), annotated(false) {}
|
||||
explicit commodity_t() : base(NULL), annotated(false) {}
|
||||
|
||||
operator bool() const {
|
||||
return this != null_commodity;
|
||||
}
|
||||
bool operator==(const commodity_t& comm) const {
|
||||
return ptr == comm.ptr;
|
||||
return base == comm.base;
|
||||
}
|
||||
bool operator!=(const commodity_t& comm) const {
|
||||
return ptr != comm.ptr;
|
||||
return base != comm.base;
|
||||
}
|
||||
|
||||
std::string base_symbol() const {
|
||||
return ptr->symbol;
|
||||
return base->symbol;
|
||||
}
|
||||
std::string symbol() const {
|
||||
return qualified_symbol;
|
||||
|
|
@ -473,69 +473,69 @@ class commodity_t
|
|||
}
|
||||
|
||||
std::string name() const {
|
||||
return ptr->name;
|
||||
return base->name;
|
||||
}
|
||||
void set_name(const std::string& arg) {
|
||||
ptr->name = arg;
|
||||
base->name = arg;
|
||||
}
|
||||
|
||||
std::string note() const {
|
||||
return ptr->note;
|
||||
return base->note;
|
||||
}
|
||||
void set_note(const std::string& arg) {
|
||||
ptr->note = arg;
|
||||
base->note = arg;
|
||||
}
|
||||
|
||||
unsigned char precision() const {
|
||||
return ptr->precision;
|
||||
return base->precision;
|
||||
}
|
||||
void set_precision(unsigned char arg) {
|
||||
ptr->precision = arg;
|
||||
base->precision = arg;
|
||||
}
|
||||
|
||||
unsigned char flags() const {
|
||||
return ptr->flags;
|
||||
return base->flags;
|
||||
}
|
||||
void set_flags(unsigned char arg) {
|
||||
ptr->flags = arg;
|
||||
base->flags = arg;
|
||||
}
|
||||
void add_flags(unsigned char arg) {
|
||||
ptr->flags |= arg;
|
||||
base->flags |= arg;
|
||||
}
|
||||
void drop_flags(unsigned char arg) {
|
||||
ptr->flags &= ~arg;
|
||||
base->flags &= ~arg;
|
||||
}
|
||||
|
||||
amount_t * smaller() const {
|
||||
return ptr->smaller;
|
||||
return base->smaller;
|
||||
}
|
||||
void set_smaller(const amount_t& arg) {
|
||||
if (ptr->smaller)
|
||||
delete ptr->smaller;
|
||||
ptr->smaller = new amount_t(arg);
|
||||
if (base->smaller)
|
||||
delete base->smaller;
|
||||
base->smaller = new amount_t(arg);
|
||||
}
|
||||
|
||||
amount_t * larger() const {
|
||||
return ptr->larger;
|
||||
return base->larger;
|
||||
}
|
||||
void set_larger(const amount_t& arg) {
|
||||
if (ptr->larger)
|
||||
delete ptr->larger;
|
||||
ptr->larger = new amount_t(arg);
|
||||
if (base->larger)
|
||||
delete base->larger;
|
||||
base->larger = new amount_t(arg);
|
||||
}
|
||||
|
||||
commodity_base_t::history_t * history() const {
|
||||
return ptr->history;
|
||||
return base->history;
|
||||
}
|
||||
|
||||
void add_price(const std::time_t date, const amount_t& price) {
|
||||
return ptr->add_price(date, price);
|
||||
return base->add_price(date, price);
|
||||
}
|
||||
bool remove_price(const std::time_t date) {
|
||||
return ptr->remove_price(date);
|
||||
return base->remove_price(date);
|
||||
}
|
||||
amount_t value(const std::time_t moment = std::time(NULL)) const {
|
||||
return ptr->value(moment);
|
||||
return base->value(moment);
|
||||
}
|
||||
|
||||
bool valid() const;
|
||||
|
|
@ -544,7 +544,7 @@ class commodity_t
|
|||
class annotated_commodity_t : public commodity_t
|
||||
{
|
||||
public:
|
||||
const commodity_t * base;
|
||||
const commodity_t * ptr;
|
||||
|
||||
amount_t price;
|
||||
std::time_t date;
|
||||
|
|
|
|||
14
binary.cc
14
binary.cc
|
|
@ -455,7 +455,7 @@ inline commodity_t * read_binary_commodity(char *& data)
|
|||
commodity_t * commodity = new commodity_t;
|
||||
*commodities_next++ = commodity;
|
||||
|
||||
commodity->ptr =
|
||||
commodity->base =
|
||||
base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
|
||||
|
||||
read_binary_string(data, commodity->qualified_symbol);
|
||||
|
|
@ -469,13 +469,13 @@ inline commodity_t * read_binary_commodity_annotated(char *& data)
|
|||
annotated_commodity_t * commodity = new annotated_commodity_t;
|
||||
*commodities_next++ = commodity;
|
||||
|
||||
commodity->ptr =
|
||||
commodity->base =
|
||||
base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
|
||||
|
||||
read_binary_string(data, commodity->qualified_symbol);
|
||||
commodity->annotated = true;
|
||||
|
||||
commodity->base =
|
||||
commodity->ptr =
|
||||
commodities[read_binary_long<commodity_t::ident_t>(data) - 1];
|
||||
read_binary_amount(data, commodity->price);
|
||||
read_binary_long(data, commodity->date);
|
||||
|
|
@ -658,7 +658,7 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
|
||||
if (read_binary_number<char>(data) == 0) {
|
||||
commodity = read_binary_commodity(data);
|
||||
mapping_key = commodity->ptr->symbol;
|
||||
mapping_key = commodity->base->symbol;
|
||||
} else {
|
||||
read_binary_string(data, mapping_key);
|
||||
commodity = read_binary_commodity_annotated(data);
|
||||
|
|
@ -677,7 +677,7 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
commodity));
|
||||
if (! result.second && commodity->annotated)
|
||||
throw new error(std::string("Failed to read commodity from cache: ") +
|
||||
commodity->ptr->symbol);
|
||||
commodity->base->symbol);
|
||||
}
|
||||
|
||||
commodity_t::ident_t ident;
|
||||
|
|
@ -1005,7 +1005,7 @@ void write_binary_commodity(std::ostream& out, commodity_t * commodity)
|
|||
{
|
||||
commodity->ident = ++commodity_index;
|
||||
|
||||
write_binary_long(out, commodity->ptr->ident);
|
||||
write_binary_long(out, commodity->base->ident);
|
||||
write_binary_string(out, commodity->qualified_symbol);
|
||||
}
|
||||
|
||||
|
|
@ -1014,7 +1014,7 @@ void write_binary_commodity_annotated(std::ostream& out,
|
|||
{
|
||||
commodity->ident = ++commodity_index;
|
||||
|
||||
write_binary_long(out, commodity->ptr->ident);
|
||||
write_binary_long(out, commodity->base->ident);
|
||||
write_binary_string(out, commodity->qualified_symbol);
|
||||
|
||||
annotated_commodity_t * ann_comm =
|
||||
|
|
|
|||
12
config.cc
12
config.cc
|
|
@ -1115,6 +1115,16 @@ OPT_BEGIN(total_data, "J") {
|
|||
config->format_string = config->plot_total_format;
|
||||
} OPT_END(total_data);
|
||||
|
||||
OPT_BEGIN(ansi, "") {
|
||||
format_t::ansi_codes = true;
|
||||
format_t::ansi_invert = false;
|
||||
} OPT_END(ansi);
|
||||
|
||||
OPT_BEGIN(ansi_invert, "") {
|
||||
format_t::ansi_codes =
|
||||
format_t::ansi_invert = true;
|
||||
} OPT_END(ansi);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Commodity reporting
|
||||
|
|
@ -1192,6 +1202,8 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
|
|||
{ "add-budget", '\0', false, opt_add_budget, false },
|
||||
{ "amount", 't', true, opt_amount, false },
|
||||
{ "amount-data", 'j', false, opt_amount_data, false },
|
||||
{ "ansi", '\0', false, opt_ansi, false },
|
||||
{ "ansi-invert", '\0', false, opt_ansi_invert, false },
|
||||
{ "average", 'A', false, opt_average, false },
|
||||
{ "balance-format", '\0', true, opt_balance_format, false },
|
||||
{ "basis", 'B', false, opt_basis, false },
|
||||
|
|
|
|||
2
config.h
2
config.h
|
|
@ -107,7 +107,7 @@ class config_t
|
|||
std::list<item_handler<transaction_t> *>& ptrs);
|
||||
};
|
||||
|
||||
#define CONFIG_OPTIONS_SIZE 86
|
||||
#define CONFIG_OPTIONS_SIZE 88
|
||||
extern option_t config_options[CONFIG_OPTIONS_SIZE];
|
||||
|
||||
void option_help(std::ostream& out);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(ledger, 2.6, johnw@newartisans.com)
|
||||
AM_INIT_AUTOMAKE(ledger, 2.6)
|
||||
AC_INIT(ledger, 3.0, johnw@newartisans.com)
|
||||
AM_INIT_AUTOMAKE(ledger, 3.0)
|
||||
AC_CONFIG_SRCDIR([main.cc])
|
||||
AC_CONFIG_HEADER([acconf.h])
|
||||
|
||||
|
|
|
|||
61
format.cc
61
format.cc
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
namespace ledger {
|
||||
|
||||
bool format_t::ansi_codes = false;
|
||||
bool format_t::ansi_invert = false;
|
||||
|
||||
std::string truncated(const std::string& str, unsigned int width,
|
||||
const int style)
|
||||
{
|
||||
|
|
@ -264,6 +267,26 @@ static bool entry_state(const entry_t * entry, transaction_t::state_t * state)
|
|||
return ! hetero;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void mark_red(std::ostream& out, const element_t * elem) {
|
||||
out.setf(std::ios::left);
|
||||
out.width(0);
|
||||
out << "\e[31m";
|
||||
|
||||
if (elem->align_left)
|
||||
out << std::left;
|
||||
else
|
||||
out << std::right;
|
||||
|
||||
if (elem->min_width > 0)
|
||||
out.width(elem->min_width);
|
||||
}
|
||||
|
||||
void mark_plain(std::ostream& out) {
|
||||
out << "\e[0m";
|
||||
}
|
||||
}
|
||||
|
||||
void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||
{
|
||||
for (const element_t * elem = elements; elem; elem = elem->next) {
|
||||
|
|
@ -322,15 +345,37 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
case value_t::BOOLEAN:
|
||||
out << (*((bool *) value.data) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case value_t::INTEGER:
|
||||
if (ansi_codes) {
|
||||
if (ansi_invert) {
|
||||
if (*((long *) value.data) > 0)
|
||||
mark_red(out, elem);
|
||||
} else {
|
||||
if (*((long *) value.data) < 0)
|
||||
mark_red(out, elem);
|
||||
}
|
||||
}
|
||||
out << *((long *) value.data);
|
||||
break;
|
||||
|
||||
case value_t::DATETIME:
|
||||
out << *((datetime_t *) value.data);
|
||||
break;
|
||||
|
||||
case value_t::AMOUNT:
|
||||
if (ansi_codes) {
|
||||
if (ansi_invert) {
|
||||
if (*((amount_t *) value.data) > 0)
|
||||
mark_red(out, elem);
|
||||
} else {
|
||||
if (*((amount_t *) value.data) < 0)
|
||||
mark_red(out, elem);
|
||||
}
|
||||
}
|
||||
out << *((amount_t *) value.data);
|
||||
break;
|
||||
|
||||
case value_t::BALANCE:
|
||||
bal = (balance_t *) value.data;
|
||||
// fall through...
|
||||
|
|
@ -339,14 +384,28 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
if (! bal)
|
||||
bal = &((balance_pair_t *) value.data)->quantity;
|
||||
|
||||
if (ansi_codes) {
|
||||
if (ansi_invert) {
|
||||
if (*bal > 0)
|
||||
mark_red(out, elem);
|
||||
} else {
|
||||
if (*bal < 0)
|
||||
mark_red(out, elem);
|
||||
}
|
||||
}
|
||||
bal->write(out, elem->min_width,
|
||||
(elem->max_width > 0 ? elem->max_width : elem->min_width));
|
||||
(elem->max_width > 0 ?
|
||||
elem->max_width : elem->min_width));
|
||||
|
||||
ignore_max_width = true;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ansi_codes)
|
||||
mark_plain(out);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
3
format.h
3
format.h
|
|
@ -72,6 +72,9 @@ struct format_t
|
|||
std::string format_string;
|
||||
element_t * elements;
|
||||
|
||||
static bool ansi_codes;
|
||||
static bool ansi_invert;
|
||||
|
||||
format_t() : elements(NULL) {
|
||||
DEBUG_PRINT("ledger.memory.ctors", "ctor format_t");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue