summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS63
-rw-r--r--datetime.cc14
-rw-r--r--format.cc82
-rw-r--r--format.h13
-rw-r--r--option.cc28
-rw-r--r--option.h2
-rw-r--r--report.cc2
-rw-r--r--valexpr.cc31
-rw-r--r--valexpr.h1
9 files changed, 179 insertions, 57 deletions
diff --git a/NEWS b/NEWS
index 571fd88a..e34a58b3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,43 @@
-
Ledger NEWS
* 3.0
+- The style for eliding long account names (for example, in the
+ register report) has been changed. Previously Ledger would elide
+ the end of long names, replacing the excess length with "..".
+ However, in some cases this caused the base account name to be
+ missing from the report!
+
+ What Ledger now does is that if an account name is too long, it will
+ start abbreviating the first parts of the account name down to two
+ letters in length. If this results in a string that is still too
+ long, the front will be elided -- not the end. For example:
+
+ Expenses:Cash ; OK, not too long
+ Ex:Wednesday:Cash ; "Expenses" was abbreviated to fit
+ Ex:We:Afternoon:Cash ; "Expenses" and "Wednesday" abbreviated
+ ; Expenses:Wednesday:Afternoon:Lunch:Snack:Candy:Chocolate:Cash
+ ..:Af:Lu:Sn:Ca:Ch:Cash ; Abbreviated and elided!
+
+ As you can see, it now takes a very deep account name before any
+ elision will occur, whereas in 2.x elisions were fairly common.
+
+- In addition to the new elision change mentioned above, the style is
+ also configurable:
+
+ --truncate leading ; elide at the beginning
+ --truncate middle ; elide in the middle
+ --truncate trailing ; elide at end (Ledger 2.x's behavior)
+ --truncate abbrev ; the new behavior
+
+ --abbrev-len 2 ; set length of abbreviations
+
+ These elision styles affect all format strings which have a maximum
+ width, so they will also affect the payee in a register report, for
+ example. In the case of non-account names, "abbrev" is equivalent
+ to "trailing", even though it elides at the beginning for long
+ account names.
+
- Error reporting has been greatly improving, now showing full
contextual information for most error messages.
@@ -59,7 +94,7 @@
monthly costs report, for example, because it makes the
following command possible:
- ledger -M --only "a>100" reg ^Expenses:Food
+ ledger -M --only "a>100" reg ^Expenses:Food
This shows only *months* whose amount is greater than 100. If
--limit had been used, it would have been a monthly summary of
@@ -71,7 +106,7 @@
This predicate does not constrain calculation, but only display.
Consider the same command as above:
- ledger -M --display "a>100" reg ^Expenses:Food
+ ledger -M --display "a>100" reg ^Expenses:Food
This displays only lines whose amount is greater than 100, *yet
the running total still includes amounts from all transactions*.
@@ -79,7 +114,7 @@
the current month's checking register while still giving a
correct ending balance:
- ledger --display "d>[this month]" reg Checking
+ ledger --display "d>[this month]" reg Checking
Note that these predicates can be combined. Here is a report that
considers only food bills whose individual cost is greater than
@@ -88,8 +123,8 @@
retain an accurate running total with respect to the entire ledger
file:
- ledger -M --limit "a>20" --only "a>200" \
- --display "year == yearof([last year])" reg ^Expenses:Food
+ ledger -M --limit "a>20" --only "a>200" \
+ --display "year == yearof([last year])" reg ^Expenses:Food
- Added new "--descend AMOUNT" and "--descend-if VALEXPR" reporting
options. For any reports that display valued transactions (i.e.,
@@ -166,12 +201,12 @@
G gain_total
U(x) abs(x)
S(x) quant(x), quantity(x)
- comm(x), commodity(x)
- setcomm(x,y), set_commodity(x,y)
+ comm(x), commodity(x)
+ setcomm(x,y), set_commodity(x,y)
A(x) mean(x), avg(x), average(x)
P(x,y) val(x,y), value(x,y)
- min(x,y)
- max(x,y)
+ min(x,y)
+ max(x,y)
- There are new "parse" and "expr" commands, whose argument is a
single value expression. Ledger will simply print out the result of
@@ -318,10 +353,10 @@
the following is now supported, which wasn't previously:
2004/06/21 Adjustment
- Retirement 100 FUNDA
- Retirement 200 FUNDB
- Retirement 300 FUNDC
- Equity:Adjustments
+ Retirement 100 FUNDA
+ Retirement 200 FUNDB
+ Retirement 300 FUNDC
+ Equity:Adjustments
- Fixed several bugs relating to QIF parsing, budgeting and
forecasting.
diff --git a/datetime.cc b/datetime.cc
index d8668cb8..2e47c554 100644
--- a/datetime.cc
+++ b/datetime.cc
@@ -253,6 +253,12 @@ void interval_t::parse(std::istream& in)
months = 3 * quantity;
else if (word == "years")
years = quantity;
+ else if (word == "hours")
+ hours = quantity;
+ else if (word == "minutes")
+ minutes = quantity;
+ else if (word == "seconds")
+ seconds = quantity;
}
else if (word == "day")
days = 1;
@@ -264,6 +270,12 @@ void interval_t::parse(std::istream& in)
months = 3;
else if (word == "year")
years = 1;
+ else if (word == "hour")
+ hours = 1;
+ else if (word == "minute")
+ minutes = 1;
+ else if (word == "second")
+ seconds = 1;
}
else if (word == "daily")
days = 1;
@@ -279,6 +291,8 @@ void interval_t::parse(std::istream& in)
months = 3;
else if (word == "yearly")
years = 1;
+ else if (word == "hourly")
+ hours = 1;
else if (word == "this" || word == "last" || word == "next") {
parse_date_words(in, word, &begin, &end);
}
diff --git a/format.cc b/format.cc
index 378e13f6..2872e794 100644
--- a/format.cc
+++ b/format.cc
@@ -6,29 +6,32 @@
namespace ledger {
+format_t::elision_style_t format_t::elision_style = ABBREVIATE;
+int format_t::abbrev_length = 2;
+
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)
+std::string format_t::truncate(const std::string& str, unsigned int width,
+ const bool is_account)
{
const int len = str.length();
if (len <= width)
return str;
- assert(width < 254);
+ assert(width < 4095);
- char buf[256];
+ char buf[4096];
- switch (style) {
- case 0:
+ switch (elision_style) {
+ case TRUNCATE_LEADING:
// This method truncates at the beginning.
std::strncpy(buf, str.c_str() + (len - width), width);
buf[0] = '.';
buf[1] = '.';
break;
- case 1:
+ case TRUNCATE_MIDDLE:
// This method truncates in the middle.
std::strncpy(buf, str.c_str(), width / 2);
std::strncpy(buf + width / 2,
@@ -38,7 +41,52 @@ std::string truncated(const std::string& str, unsigned int width,
buf[width / 2] = '.';
break;
- case 2:
+ case ABBREVIATE:
+ if (is_account) {
+ std::list<std::string> parts;
+ std::string::size_type beg = 0;
+ for (std::string::size_type pos = str.find(':');
+ pos != std::string::npos;
+ beg = pos + 1, pos = str.find(':', beg))
+ parts.push_back(std::string(str, beg, pos - beg));
+ parts.push_back(std::string(str, beg));
+
+ std::string result;
+ int newlen = len;
+ for (std::list<std::string>::iterator i = parts.begin();
+ i != parts.end();
+ i++) {
+ // Don't contract the last element
+ std::list<std::string>::iterator x = i;
+ if (++x == parts.end()) {
+ result += *i;
+ break;
+ }
+
+ if (newlen > width) {
+ result += std::string(*i, 0, abbrev_length);
+ result += ":";
+ newlen -= (*i).length() - abbrev_length;
+ } else {
+ result += *i;
+ result += ":";
+ }
+ }
+
+ if (newlen > width) {
+ // Even abbreviated its too big to show the last account, so
+ // abbreviate all but the last and truncate at the beginning.
+ std::strncpy(buf, result.c_str() + (result.length() - width), width);
+ buf[0] = '.';
+ buf[1] = '.';
+ } else {
+ std::strcpy(buf, result.c_str());
+ }
+ break;
+ }
+ // fall through...
+
+ case TRUNCATE_TRAILING:
// This method truncates at the end (the default).
std::strncpy(buf, str.c_str(), width - 2);
buf[width - 2] = '.';
@@ -543,7 +591,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
char buf[256];
std::strftime(buf, 255, elem->chars.c_str(), date.localtime());
- out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width));
+ out << (elem->max_width == 0 ? buf : truncate(buf, elem->max_width));
break;
}
@@ -572,9 +620,9 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
std::strcat(buf, "=");
std::strcat(buf, ebuf);
- out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width));
+ out << (elem->max_width == 0 ? buf : truncate(buf, elem->max_width));
} else {
- out << (elem->max_width == 0 ? abuf : truncated(abuf, elem->max_width));
+ out << (elem->max_width == 0 ? abuf : truncate(abuf, elem->max_width));
}
break;
}
@@ -621,8 +669,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
case element_t::PAYEE:
if (details.entry)
out << (elem->max_width == 0 ?
- details.entry->payee : truncated(details.entry->payee,
- elem->max_width));
+ details.entry->payee : truncate(details.entry->payee,
+ elem->max_width));
break;
case element_t::OPT_NOTE:
@@ -633,8 +681,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
case element_t::NOTE:
if (details.xact)
out << (elem->max_width == 0 ?
- details.xact->note : truncated(details.xact->note,
- elem->max_width));
+ details.xact->note : truncate(details.xact->note,
+ elem->max_width));
break;
case element_t::OPT_ACCOUNT:
@@ -661,7 +709,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
if (elem->max_width > 2)
- name = truncated(name, elem->max_width - 2);
+ name = truncate(name, elem->max_width - 2, true);
if (details.xact->flags & TRANSACTION_BALANCE)
name = "[" + name + "]";
@@ -669,7 +717,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
name = "(" + name + ")";
}
else if (elem->max_width > 0)
- name = truncated(name, elem->max_width);
+ name = truncate(name, elem->max_width, true);
out << name;
} else {
diff --git a/format.h b/format.h
index ccbb0f42..778ec53f 100644
--- a/format.h
+++ b/format.h
@@ -73,6 +73,16 @@ struct format_t
std::string format_string;
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;
@@ -97,6 +107,9 @@ struct format_t
static element_t * parse_elements(const std::string& fmt);
+ static std::string truncate(const std::string& str, unsigned int width,
+ const bool is_account = false);
+
void format(std::ostream& out, const details_t& details) const;
};
diff --git a/option.cc b/option.cc
index adbbdcc7..13ac4a4c 100644
--- a/option.cc
+++ b/option.cc
@@ -38,9 +38,9 @@ namespace {
if ((result = (int)name[0] - (int)array[mid].long_opt[0]) == 0)
result = std::strcmp(name, array[mid].long_opt);
- if (result > 0)
+ if (result > 0)
first = mid + 1; // repeat search in top half.
- else if (result < 0)
+ else if (result < 0)
last = mid - 1; // repeat search in bottom half.
else
return &array[mid];
@@ -102,7 +102,7 @@ void process_arguments(option_t * options, int argc, char ** argv,
opt = search_options(options, name);
if (! opt)
throw new option_error(std::string("illegal option --") + name);
-
+
if (opt->wants_arg && ! value) {
value = *++i;
if (! value)
@@ -610,6 +610,22 @@ OPT_BEGIN(pager, ":") {
config->pager = optarg;
} OPT_END(pager);
+OPT_BEGIN(truncate, ":") {
+ std::string style(optarg);
+ if (style == "leading")
+ format_t::elision_style = format_t::TRUNCATE_LEADING;
+ else if (style == "middle")
+ format_t::elision_style = format_t::TRUNCATE_MIDDLE;
+ else if (style == "trailing")
+ format_t::elision_style = format_t::TRUNCATE_TRAILING;
+ else if (style == "abbrev")
+ format_t::elision_style = format_t::ABBREVIATE;
+} OPT_END(truncate);
+
+OPT_BEGIN(abbrev_len, ":") {
+ format_t::abbrev_length = std::atoi(optarg);
+} OPT_END(abbrev_len);
+
OPT_BEGIN(empty, "E") {
report->show_empty = true;
} OPT_END(empty);
@@ -658,7 +674,7 @@ OPT_BEGIN(descend, "") {
pos != std::string::npos;
beg = pos + 1, pos = arg.find(';', beg))
report->descend_expr += (std::string("t=={") +
- std::string(arg, beg, pos) + "};");
+ std::string(arg, beg, pos - beg) + "};");
report->descend_expr += (std::string("t=={") +
std::string(arg, beg) + "}");
} OPT_END(descend);
@@ -895,7 +911,7 @@ OPT_BEGIN(set_price, ":") {
for (std::string::size_type pos = arg.find(';');
pos != std::string::npos;
beg = pos + 1, pos = arg.find(';', beg))
- parse_price_setting(std::string(arg, beg, pos).c_str());
+ parse_price_setting(std::string(arg, beg, pos - beg).c_str());
parse_price_setting(std::string(arg, beg).c_str());
} OPT_END(set_price);
@@ -940,6 +956,7 @@ OPT_BEGIN(percentage, "%") {
//////////////////////////////////////////////////////////////////////
option_t config_options[CONFIG_OPTIONS_SIZE] = {
+ { "abbrev-len", '\0', true, opt_abbrev_len, false },
{ "account", 'a', true, opt_account, false },
{ "actual", 'L', false, opt_actual, false },
{ "add-budget", '\0', false, opt_add_budget, false },
@@ -1025,6 +1042,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "total-data", 'J', false, opt_total_data, false },
{ "totals", '\0', false, opt_totals, false },
{ "trace", '\0', false, opt_trace, false },
+ { "truncate", '\0', true, opt_truncate, false },
{ "unbudgeted", '\0', false, opt_unbudgeted, false },
{ "uncleared", 'U', false, opt_uncleared, false },
{ "verbose", '\0', false, opt_verbose, false },
diff --git a/option.h b/option.h
index 4bbafdfc..91838b99 100644
--- a/option.h
+++ b/option.h
@@ -38,7 +38,7 @@ class report_t;
extern config_t * config;
extern report_t * report;
-#define CONFIG_OPTIONS_SIZE 95
+#define CONFIG_OPTIONS_SIZE 97
extern option_t config_options[CONFIG_OPTIONS_SIZE];
void option_help(std::ostream& out);
diff --git a/report.cc b/report.cc
index b83547c3..90259440 100644
--- a/report.cc
+++ b/report.cc
@@ -260,7 +260,7 @@ report_t::chain_xact_handlers(const std::string& command,
for (std::string::size_type pos = descend_expr.find(';');
pos != std::string::npos;
beg = pos + 1, pos = descend_expr.find(';', beg))
- descend_exprs.push_back(std::string(descend_expr, beg, pos));
+ descend_exprs.push_back(std::string(descend_expr, beg, pos - beg));
descend_exprs.push_back(std::string(descend_expr, beg));
for (std::list<std::string>::reverse_iterator i =
diff --git a/valexpr.cc b/valexpr.cc
index 2be3204f..64da351c 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -137,10 +137,6 @@ void value_expr_t::compute(value_t& result, const details_t& details,
{
try {
switch (kind) {
- case ZERO:
- result = 0L;
- break;
-
case ARG_INDEX:
throw new compute_error("Cannot directly compute an arg_index");
@@ -589,7 +585,8 @@ void value_expr_t::compute(value_t& result, const details_t& details,
break;
case O_DEF:
- throw new compute_error("Cannot compute function definition");
+ result = 0L;
+ break;
case O_REF: {
assert(left);
@@ -869,7 +866,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
node->left->arg_index = arg_index++;
params->define(ident, node.release());
}
-
+
if (peek_next_nonws(in) != '=') {
in.get(c);
unexpected(c, '=');
@@ -886,11 +883,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
node->set_left(new value_expr_t(value_expr_t::ARG_INDEX));
node->left->arg_index = arg_index;
node->set_right(def.release());
-
- scope->define(buf, node.release());
- // Returning a dummy value in place of the definition
- node.reset(new value_expr_t(value_expr_t::ZERO));
+ scope->define(buf, node.get());
} else {
assert(scope);
value_expr_t * def = scope->lookup(buf);
@@ -1573,10 +1567,8 @@ bool write_value_expr(std::ostream& out,
std::string symbol;
switch (node->kind) {
- case value_expr_t::ZERO:
- out << '0';
- break;
case value_expr_t::ARG_INDEX:
+ out << node->arg_index;
break;
case value_expr_t::CONSTANT:
@@ -1712,7 +1704,13 @@ bool write_value_expr(std::ostream& out,
out << "@arg" << node->arg_index;
break;
case value_expr_t::O_DEF:
- out << "O_DEF";
+ out << "<def args=\"";
+ if (write_value_expr(out, node->left, relaxed, node_to_find, start_pos, end_pos))
+ found = true;
+ out << "\" value=\"";
+ if (write_value_expr(out, node->right, relaxed, node_to_find, start_pos, end_pos))
+ found = true;
+ out << "\">";
break;
case value_expr_t::O_REF:
@@ -1875,7 +1873,7 @@ bool write_value_expr(std::ostream& out,
if (end_pos && node == node_to_find)
*end_pos = (long)out.tellp() - 1;
-
+
return found;
}
@@ -1890,9 +1888,6 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
out << " ";
switch (node->kind) {
- case value_expr_t::ZERO:
- out << "ZERO";
- break;
case value_expr_t::ARG_INDEX:
out << "ARG_INDEX - " << node->arg_index;
break;
diff --git a/valexpr.h b/valexpr.h
index ff3d4d3c..02e564d2 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -42,7 +42,6 @@ struct value_expr_t
// Constants
CONSTANT,
ARG_INDEX,
- ZERO,
CONSTANTS,