From 31e8ed7587f6c7dc54e9623dd6a4e09ad5b6b017 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 17 Jun 2010 23:42:23 -0400 Subject: Individual postings may each have their own payee If a posting has the metadata field "Payee" set to a string, that will be used as the payee name for that posting. This affects the register report, the payees report, and the --by-payee option. This is useful because sometimes I send, say, 4 checks at a time to my bank. So on my bank statement, this is all just one amount: 2010-06-17 Sample Assets:Bank $400.00 Income:Check1 $-100.00 Income:Check2 $-100.00 Income:Check3 $-100.00 Income:Check4 $-100.00 Though it's important that the Assets:Bank posting be a single posting of $400 value, I'd like for income reports to show whom each check came from. Now I can say: 2010-06-17 Sample Assets:Bank $400.00 Income:Check1 $-100.00 ; Payee: Person One Income:Check2 $-100.00 ; Payee: Person Two Income:Check3 $-100.00 ; Payee: Person Three Income:Check4 $-100.00 ; Payee: Person Four When I report this, it appears as: 10-Jun-17 Sample Assets:Bank $400.00 $400.00 Person One Income:Check1 $-100.00 $300.00 Person Two Income:Check2 $-100.00 $200.00 Person Three Income:Check3 $-100.00 $100.00 Person Four Income:Check4 $-100.00 0 This shows that they are all in the same transaction (which is why the date is not repeated), but they have different payees. --- src/report.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/report.h') diff --git a/src/report.h b/src/report.h index eff375cc..a453d351 100644 --- a/src/report.h +++ b/src/report.h @@ -816,8 +816,9 @@ public: " %(justify(scrub(display_total), total_width, " " 4 + meta_width + date_width + payee_width + account_width" " + amount_width + total_width + prepend_width, true, color))\n%/" - "%(justify(\" \", 2 + date_width + payee_width))" - "%$3 %$4 %$5\n"); + "%(justify(\" \", date_width))" + " %(justify((has_tag(\"Payee\") ? payee : \" \"), payee_width))" + " %$3 %$4 %$5\n"); }); OPTION(report_t, related); // -r -- cgit v1.2.3 From 5da1e7756d2a4eb04753b8a97bc00063b4d3f687 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 18 Jun 2010 02:26:50 -0400 Subject: Added new option --inject=KEY[,KEY...] If you have a typed metadata key which contains an amount, you can use --inject=KEY to inject a posting with that amount wherever a match occurs. There are two main forms of usage: 2010-06-18 Sample ; Key:: $100 Expenses:Food $100.00 Assets:Checking The command would be: ledger reg --inject=Key In the above, transactional form, a posting under the account "Key" will be injected before the first posting reported for this transaction. It's amount will be $100. This only happens once for the whole transaction. It is also possible to associate the key with a posting: 2010-06-18 Sample Expenses:Food $100.00 ; Key:: $100 Assets:Checking Now the injected posting is generated whenever that particular post is reported. --- doc/ledger.1 | 3 ++- src/chain.cc | 4 +++ src/filters.cc | 60 +++++++++++++++++++++++++++++++++++++++++++ src/filters.h | 20 +++++++++++++++ src/report.cc | 1 + src/report.h | 2 ++ test/baseline/opt-inject.test | 0 7 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 test/baseline/opt-inject.test (limited to 'src/report.h') diff --git a/doc/ledger.1 b/doc/ledger.1 index 4e72c792..ba4317f9 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd June 15, 2010 +.Dd June 18, 2010 .Dt ledger 1 .Sh NAME .Nm ledger @@ -336,6 +336,7 @@ See .It Fl \-help-disp .It Fl \-import Ar STR .It Fl \-init-file Ar FILE +.It Fl \-inject Ar STR .It Fl \-input-date-format Ar DATEFMT .It Fl \-invert .It Fl \-last Ar INT diff --git a/src/chain.cc b/src/chain.cc index 64550663..8010ba74 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -258,6 +258,10 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, if (report.HANDLED(related)) handler.reset(new related_posts(handler, report.HANDLED(related_all))); + if (report.HANDLED(inject_)) + handler.reset(new inject_posts(handler, report.HANDLED(inject_).str(), + report.session.journal->master)); + return handler; } diff --git a/src/filters.cc b/src/filters.cc index 6e832149..33905856 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1338,6 +1338,66 @@ void forecast_posts::flush() item_handler::flush(); } +inject_posts::inject_posts(post_handler_ptr handler, + const string& tag_list, + account_t * master) + : item_handler(handler) +{ + TRACE_CTOR(inject_posts, "post_handler_ptr, string"); + + scoped_array buf(new char[tag_list.length() + 1]); + std::strcpy(buf.get(), tag_list.c_str()); + + for (char * q = std::strtok(buf.get(), ","); + q; + q = std::strtok(NULL, ",")) { + + std::list account_names; + split_string(q, ':', account_names); + account_t * account = + create_temp_account_from_path(account_names, temps, master); + account->add_flags(ACCOUNT_GENERATED); + + tags_list.push_back + (tags_list_pair(q, tag_mapping_pair(account, tag_injected_set()))); + } +} + +void inject_posts::operator()(post_t& post) +{ + foreach (tags_list_pair& pair, tags_list) { + optional tag_value = post.get_tag(pair.first, false); + if (! tag_value && + pair.second.second.find(post.xact) == pair.second.second.end()) { + // When checking if the transaction has the tag, only inject once + // per transaction. + pair.second.second.insert(post.xact); + tag_value = post.xact->get_tag(pair.first); + } + + if (tag_value) { + if (tag_value->is_amount()) { + xact_t& xact = temps.copy_xact(*post.xact); + xact._date = post.date(); + xact.add_flags(ITEM_GENERATED); + post_t& temp = temps.copy_post(post, xact); + + temp.account = pair.second.first; + temp.amount = tag_value->as_amount(); + temp.add_flags(ITEM_GENERATED); + + item_handler::operator()(temp); + } else { + throw_(std::logic_error, + _("Attempt to inject a posting with non-amount %1 for tag %2") + << *tag_value << pair.first); + } + } + } + + item_handler::operator()(post); +} + pass_down_accounts::pass_down_accounts(acct_handler_ptr handler, accounts_iterator& iter, const optional& _pred, diff --git a/src/filters.h b/src/filters.h index 9102d4f1..180253d2 100644 --- a/src/filters.h +++ b/src/filters.h @@ -929,6 +929,26 @@ class forecast_posts : public generate_posts } }; +class inject_posts : public item_handler +{ + typedef std::set tag_injected_set; + typedef std::pair tag_mapping_pair; + typedef std::pair tags_list_pair; + + std::list tags_list; + temporaries_t temps; + + public: + inject_posts(post_handler_ptr handler, const string& tag_list, + account_t * master); + + virtual ~inject_posts() throw() { + TRACE_DTOR(inject_posts); + } + + virtual void operator()(post_t& post); +}; + ////////////////////////////////////////////////////////////////////// // // Account filters diff --git a/src/report.cc b/src/report.cc index 9626283a..da7c11f5 100644 --- a/src/report.cc +++ b/src/report.cc @@ -955,6 +955,7 @@ option_t * report_t::lookup_option(const char * p) break; case 'i': OPT(invert); + else OPT(inject_); break; case 'j': OPT_CH(amount_data); diff --git a/src/report.h b/src/report.h index a453d351..7d725b8c 100644 --- a/src/report.h +++ b/src/report.h @@ -260,6 +260,7 @@ public: HANDLER(group_by_).report(out); HANDLER(group_title_format_).report(out); HANDLER(head_).report(out); + HANDLER(inject_).report(out); HANDLER(invert).report(out); HANDLER(limit_).report(out); HANDLER(lot_dates).report(out); @@ -616,6 +617,7 @@ public: }); OPTION(report_t, head_); + OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { parent->HANDLER(amount_).set_expr(string("--invert"), "-amount"); diff --git a/test/baseline/opt-inject.test b/test/baseline/opt-inject.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 9205809d4978ba6673fd759546375c26a156c09e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 18 Jun 2010 07:27:35 -0400 Subject: Fixed register formatting of postings with payees --- src/report.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/report.h') diff --git a/src/report.h b/src/report.h index 7d725b8c..7e52933a 100644 --- a/src/report.h +++ b/src/report.h @@ -819,7 +819,8 @@ public: " 4 + meta_width + date_width + payee_width + account_width" " + amount_width + total_width + prepend_width, true, color))\n%/" "%(justify(\" \", date_width))" - " %(justify((has_tag(\"Payee\") ? payee : \" \"), payee_width))" + " %(justify(truncated(has_tag(\"Payee\") ? payee : \" \", " + " payee_width), payee_width))" " %$3 %$4 %$5\n"); }); -- cgit v1.2.3 From 963161a817c3d6b2a8171c8e3faa382dbc39ba02 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 21 Jun 2010 18:32:03 -0400 Subject: bal was sometimes reporting empty accounts --- src/output.cc | 3 ++- src/report.cc | 15 ++++++++++----- src/report.h | 2 ++ test/regress/56BBE69B.test | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 test/regress/56BBE69B.test (limited to 'src/report.h') diff --git a/src/output.cc b/src/output.cc index cc4845a3..f53e60c9 100644 --- a/src/output.cc +++ b/src/output.cc @@ -228,7 +228,8 @@ format_accounts::mark_accounts(account_t& account, const bool flat) if ((! flat && to_display > 1) || ((flat || to_display != 1 || account.has_xflags(ACCOUNT_EXT_VISITED)) && - (report.HANDLED(empty) || report.fn_display_total(call_scope)) && + (report.HANDLED(empty) || + report.display_value(report.fn_display_total(call_scope))) && disp_pred(bound_scope))) { account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY); DEBUG("account.display", "Marking account as TO_DISPLAY"); diff --git a/src/report.cc b/src/report.cc index da7c11f5..df37f9dc 100644 --- a/src/report.cc +++ b/src/report.cc @@ -428,6 +428,15 @@ void report_t::commodities_report(post_handler_ptr handler) session.journal->clear_xdata(); } +value_t report_t::display_value(const value_t& val) +{ + value_t temp(val.strip_annotations(what_to_keep())); + if (HANDLED(base)) + return temp; + else + return temp.unreduced(); +} + value_t report_t::fn_amount_expr(call_scope_t& scope) { return HANDLER(amount_).expr.calc(scope); @@ -533,11 +542,7 @@ value_t report_t::fn_print(call_scope_t& args) value_t report_t::fn_scrub(call_scope_t& args) { - value_t temp(args.value().strip_annotations(what_to_keep())); - if (HANDLED(base)) - return temp; - else - return temp.unreduced(); + return display_value(args.value()); } value_t report_t::fn_rounded(call_scope_t& args) diff --git a/src/report.h b/src/report.h index 7e52933a..e0073193 100644 --- a/src/report.h +++ b/src/report.h @@ -134,6 +134,8 @@ public: void accounts_report(acct_handler_ptr handler); void commodities_report(post_handler_ptr handler); + value_t display_value(const value_t& val); + value_t fn_amount_expr(call_scope_t& scope); value_t fn_total_expr(call_scope_t& scope); value_t fn_display_amount(call_scope_t& scope); diff --git a/test/regress/56BBE69B.test b/test/regress/56BBE69B.test new file mode 100644 index 00000000..76fedc17 --- /dev/null +++ b/test/regress/56BBE69B.test @@ -0,0 +1,17 @@ +bol +<<< +D 1000.00 USD + +2010-01-07 * Put money in + Assets:A -20.00 EUR + Equity:Opening balances + +2010-01-11 * Purchase + Assets:A 20.00 EUR @@ 25.00 USD + Expenses:B +>>> + 20.00 EUR Equity:Opening balances + -25.00 USD Expenses:B +-------------------- + 20.00 EUR + -25.00 USD -- cgit v1.2.3 From 81bf38584e43e5fc31e316246f11e210418548a9 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 22 Jun 2010 01:16:29 -0400 Subject: Added new --bold-if option --- doc/ledger.1 | 3 +- src/report.cc | 11 ++++++++ src/report.h | 64 +++++++++++++++++++++++++++++++----------- test/baseline/opt-bold-if.test | 0 4 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 test/baseline/opt-bold-if.test (limited to 'src/report.h') diff --git a/doc/ledger.1 b/doc/ledger.1 index ba4317f9..5a3bd6db 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd June 18, 2010 +.Dd June 22, 2010 .Dt ledger 1 .Sh NAME .Nm ledger @@ -275,6 +275,7 @@ transactions they are contained in. See the manual for more information. .It Fl \-base .It Fl \-basis Pq Fl B .It Fl \-begin Ar DATE Pq Fl b +.It Fl \-bold-if Ar EXPR .It Fl \-budget .It Fl \-budget-format Ar FMT .It Fl \-by-payee Pq Fl P diff --git a/src/report.cc b/src/report.cc index df37f9dc..77cfaffd 100644 --- a/src/report.cc +++ b/src/report.cc @@ -457,6 +457,14 @@ value_t report_t::fn_display_total(call_scope_t& scope) return HANDLER(display_total_).expr.calc(scope); } +value_t report_t::fn_should_bold(call_scope_t& scope) +{ + if (HANDLED(bold_if_)) + return HANDLER(bold_if_).expr.calc(scope); + else + return false; +} + value_t report_t::fn_market(call_scope_t& args) { optional moment = (args.has(1) ? @@ -905,6 +913,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(base); else OPT_ALT(basis, cost); else OPT_(begin_); + else OPT(bold_if_); else OPT(budget); else OPT(by_payee); break; @@ -1226,6 +1235,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return MAKE_FUNCTOR(report_t::fn_scrub); else if (is_eq(p, "strip")) return MAKE_FUNCTOR(report_t::fn_strip); + else if (is_eq(p, "should_bold")) + return MAKE_FUNCTOR(report_t::fn_should_bold); break; case 't': diff --git a/src/report.h b/src/report.h index e0073193..fc2fc1a2 100644 --- a/src/report.h +++ b/src/report.h @@ -140,6 +140,7 @@ public: value_t fn_total_expr(call_scope_t& scope); value_t fn_display_amount(call_scope_t& scope); value_t fn_display_total(call_scope_t& scope); + value_t fn_should_bold(call_scope_t& scope); value_t fn_market(call_scope_t& scope); value_t fn_get_at(call_scope_t& scope); value_t fn_is_seq(call_scope_t& scope); @@ -379,9 +380,13 @@ public: OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { on(none, - "%(justify(scrub(display_total), 20, 20 + prepend_width, true, color))" + "%(ansify_if(" + " justify(scrub(display_total), 20, 20 + prepend_width, true, color)," + " bold if should_bold))" " %(!options.flat ? depth_spacer : \"\")" - "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" + "%-(ansify_if(" + " ansify_if(partial_account(options.flat), blue if color)," + " bold if should_bold))\n%/" "%$1\n%/" "--------------------\n"); }); @@ -405,6 +410,18 @@ public: parent->HANDLER(limit_).on(string("--begin"), predicate); }); + OPTION__ + (report_t, bold_if_, + expr_t expr; + CTOR(report_t, bold_if_) {} + void set_expr(const optional& whence, const string& str) { + expr = str; + on(whence, str); + } + DO_(args) { + set_expr(args.get(0), args.get(1)); + }); + OPTION_(report_t, budget, DO() { parent->budget_flags |= BUDGET_BUDGETED; }); @@ -808,21 +825,36 @@ public: OPTION__(report_t, register_format_, CTOR(report_t, register_format_) { on(none, - "%(ansify_if(justify(format_date(date), date_width), green " - " if color & date > today))" - " %(ansify_if(justify(truncated(payee, payee_width), payee_width), " - " bold if color & !cleared & actual))" - " %(ansify_if(justify(truncated(display_account, account_width, " - " abbrev_len), account_width), blue if color))" - " %(justify(scrub(display_amount), amount_width, " - " 3 + meta_width + date_width + payee_width + account_width" - " + amount_width + prepend_width, true, color))" - " %(justify(scrub(display_total), total_width, " - " 4 + meta_width + date_width + payee_width + account_width" - " + amount_width + total_width + prepend_width, true, color))\n%/" + "%(ansify_if(" + " ansify_if(justify(format_date(date), date_width)," + " green if color and date > today)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(payee, payee_width), payee_width), " + " bold if color and !cleared and actual)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(display_account, account_width, " + " abbrev_len), account_width)," + " blue if color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(display_amount), amount_width, " + " 3 + meta_width + date_width + payee_width" + " + account_width + amount_width + prepend_width," + " true, color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(display_total), total_width, " + " 4 + meta_width + date_width + payee_width" + " + account_width + amount_width + total_width" + " + prepend_width, true, color)," + " bold if should_bold))\n%/" "%(justify(\" \", date_width))" - " %(justify(truncated(has_tag(\"Payee\") ? payee : \" \", " - " payee_width), payee_width))" + " %(ansify_if(" + " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " + " payee_width), payee_width)," + " bold if should_bold))" " %$3 %$4 %$5\n"); }); diff --git a/test/baseline/opt-bold-if.test b/test/baseline/opt-bold-if.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From e8e28c794bfb12fe1c9562c5bd124688ce45fc8e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 22 Jun 2010 21:56:19 -0400 Subject: Added report query modifiers: for, since, until Now instead of ledger reg expense -p "this month", you can say: ledger reg expense for this month And as a shorthand for "for until this month", you can just say "until this month" or "since this month". --- src/precmd.cc | 10 ++++---- src/query.cc | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/query.h | 24 ++++++++++++++------ src/report.cc | 70 +++++++++++++++++++++++++++++++------------------------- src/report.h | 1 + 5 files changed, 131 insertions(+), 47 deletions(-) (limited to 'src/report.h') diff --git a/src/precmd.cc b/src/precmd.cc index d9977ab6..8ca27fd8 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -202,21 +202,19 @@ value_t query_command(call_scope_t& args) query_t query(args.value(), report.what_to_keep(), ! report.HANDLED(collapse)); - if (query.has_predicate(query_t::QUERY_LIMIT)) { + if (query.has_query(query_t::QUERY_LIMIT)) { call_scope_t sub_args(static_cast(args)); - sub_args.push_back - (string_value(query.get_predicate(query_t::QUERY_LIMIT).print_to_str())); + sub_args.push_back(string_value(query.get_query(query_t::QUERY_LIMIT))); parse_command(sub_args); } - if (query.has_predicate(query_t::QUERY_SHOW)) { + if (query.has_query(query_t::QUERY_SHOW)) { out << std::endl << _("====== Display predicate ======") << std::endl << std::endl; call_scope_t disp_sub_args(static_cast(args)); - disp_sub_args.push_back - (string_value(query.get_predicate(query_t::QUERY_SHOW).print_to_str())); + disp_sub_args.push_back(string_value(query.get_query(query_t::QUERY_SHOW))); parse_command(disp_sub_args); } diff --git a/src/query.cc b/src/query.cc index 43e3ffed..bed6afae 100644 --- a/src/query.cc +++ b/src/query.cc @@ -55,6 +55,10 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() resume: switch (*arg_i) { + case '\0': + assert(false); + break; + case '\'': case '"': case '/': { @@ -85,12 +89,17 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() if (multiple_args && consume_next_arg) { consume_next_arg = false; token_t tok(token_t::TERM, string(arg_i, arg_end)); + prev_arg_i = arg_i; arg_i = arg_end; return tok; } bool consume_next = false; switch (*arg_i) { + case '\0': + assert(false); + break; + case ' ': case '\t': case '\r': @@ -121,6 +130,10 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() string::const_iterator beg = arg_i; for (; arg_i != arg_end; ++arg_i) { switch (*arg_i) { + case '\0': + assert(false); + break; + case ' ': case '\t': case '\n': @@ -130,6 +143,7 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() else ident.push_back(*arg_i); break; + case '(': case ')': case '&': @@ -174,6 +188,12 @@ test_ident: return token_t(token_t::TOK_SHOW); else if (ident == "bold") return token_t(token_t::TOK_BOLD); + else if (ident == "for") + return token_t(token_t::TOK_FOR); + else if (ident == "since") + return token_t(token_t::TOK_SINCE); + else if (ident == "until") + return token_t(token_t::TOK_UNTIL); else if (ident == "expr") { // The expr keyword takes the whole of the next string as its argument. consume_next_arg = true; @@ -230,6 +250,9 @@ query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_contex switch (tok.kind) { case lexer_t::token_t::TOK_SHOW: case lexer_t::token_t::TOK_BOLD: + case lexer_t::token_t::TOK_FOR: + case lexer_t::token_t::TOK_SINCE: + case lexer_t::token_t::TOK_UNTIL: case lexer_t::token_t::END_REACHED: lexer.push_token(tok); break; @@ -423,7 +446,8 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, if (! subexpression) { if (limiter) query_map.insert - (query_map_t::value_type(QUERY_LIMIT, predicate_t(limiter, what_to_keep))); + (query_map_t::value_type + (QUERY_LIMIT, predicate_t(limiter, what_to_keep).print_to_str())); lexer_t::token_t tok = lexer.peek_token(); while (tok.kind != lexer_t::token_t::END_REACHED) { @@ -445,7 +469,8 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, if (node) query_map.insert - (query_map_t::value_type(QUERY_SHOW, predicate_t(node, what_to_keep))); + (query_map_t::value_type + (QUERY_SHOW, predicate_t(node, what_to_keep).print_to_str())); break; } @@ -462,7 +487,49 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, if (node) query_map.insert - (query_map_t::value_type(QUERY_BOLD, predicate_t(node, what_to_keep))); + (query_map_t::value_type + (QUERY_BOLD, predicate_t(node, what_to_keep).print_to_str())); + break; + } + + case lexer_t::token_t::TOK_FOR: + case lexer_t::token_t::TOK_SINCE: + case lexer_t::token_t::TOK_UNTIL: { + tok = lexer.next_token(); + + string for_string; + + if (tok.kind == lexer_t::token_t::TOK_SINCE) + for_string = "since"; + else if (tok.kind == lexer_t::token_t::TOK_UNTIL) + for_string = "until"; + + lexer.consume_next_arg = true; + tok = lexer.peek_token(); + + while (tok.kind != lexer_t::token_t::END_REACHED) { + tok = lexer.next_token(); + assert(tok.kind == lexer_t::token_t::TERM); + + if (*tok.value == "show" || *tok.value == "bold" || + *tok.value == "for" || *tok.value == "since" || + *tok.value == "until") { + lexer.token_cache = lexer_t::token_t(); + lexer.arg_i = lexer.prev_arg_i; + lexer.consume_next_arg = false; + break; + } + + if (! for_string.empty()) + for_string += " "; + for_string += *tok.value; + + lexer.consume_next_arg = true; + tok = lexer.peek_token(); + } + + if (! for_string.empty()) + query_map.insert(query_map_t::value_type(QUERY_FOR, for_string)); break; } diff --git a/src/query.h b/src/query.h index e953206a..5a4651a0 100644 --- a/src/query.h +++ b/src/query.h @@ -60,6 +60,7 @@ public: value_t::sequence_t::const_iterator begin; value_t::sequence_t::const_iterator end; + string::const_iterator prev_arg_i; string::const_iterator arg_i; string::const_iterator arg_end; @@ -90,6 +91,9 @@ public: TOK_SHOW, TOK_BOLD, + TOK_FOR, + TOK_SINCE, + TOK_UNTIL, TERM, @@ -141,6 +145,9 @@ public: case TOK_EXPR: return "TOK_EXPR"; case TOK_SHOW: return "TOK_SHOW"; case TOK_BOLD: return "TOK_BOLD"; + case TOK_FOR: return "TOK_FOR"; + case TOK_SINCE: return "TOK_SINCE"; + case TOK_UNTIL: return "TOK_UNTIL"; case TERM: return string("TERM(") + *value + ")"; case END_REACHED: return "END_REACHED"; } @@ -164,6 +171,9 @@ public: case TOK_EXPR: return "expr"; case TOK_SHOW: return "show"; case TOK_BOLD: return "bold"; + case TOK_FOR: return "for"; + case TOK_SINCE: return "since"; + case TOK_UNTIL: return "until"; case END_REACHED: return ""; @@ -201,8 +211,7 @@ public: arg_i(lexer.arg_i), arg_end(lexer.arg_end), consume_whitespace(lexer.consume_whitespace), consume_next_arg(lexer.consume_next_arg), - multiple_args(lexer.multiple_args), - token_cache(lexer.token_cache) + multiple_args(lexer.multiple_args), token_cache(lexer.token_cache) { TRACE_CTOR(query_t::lexer_t, "copy"); } @@ -225,10 +234,11 @@ public: enum kind_t { QUERY_LIMIT, QUERY_SHOW, - QUERY_BOLD + QUERY_BOLD, + QUERY_FOR }; - typedef std::map query_map_t; + typedef std::map query_map_t; protected: class parser_t @@ -315,16 +325,16 @@ public: return parser->parse(subexpression); } - bool has_predicate(const kind_t& id) const { + bool has_query(const kind_t& id) const { return parser && parser->query_map.find(id) != parser->query_map.end(); } - predicate_t get_predicate(const kind_t& id) const { + string get_query(const kind_t& id) const { if (parser) { query_map_t::const_iterator i = parser->query_map.find(id); if (i != parser->query_map.end()) return (*i).second; } - return predicate_t(); + return empty_string; } bool tokens_remaining() { diff --git a/src/report.cc b/src/report.cc index 4a7ed2e5..9d733674 100644 --- a/src/report.cc +++ b/src/report.cc @@ -145,26 +145,8 @@ void report_t::normalize_options(const string& verb) // using -b or -e). Then, if no _duration_ was specified (such as monthly), // then ignore the period since the begin/end are the only interesting // details. - if (HANDLED(period_)) { - date_interval_t interval(HANDLER(period_).str()); - - optional begin = interval.begin(); - optional end = interval.end(); - - if (! HANDLED(begin_) && begin) { - string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; - HANDLER(limit_).on(string("?normalize"), predicate); - } - if (! HANDLED(end_) && end) { - string predicate = "date<[" + to_iso_extended_string(*end) + "]"; - HANDLER(limit_).on(string("?normalize"), predicate); - } - - if (! interval.duration) - HANDLER(period_).off(); - else if (! HANDLED(sort_all_)) - HANDLER(sort_xacts_).on_only(string("?normalize")); - } + if (HANDLED(period_)) + normalize_period(); // If -j or -J were specified, set the appropriate format string now so as // to avoid option ordering issues were we to have done it during the @@ -254,26 +236,52 @@ void report_t::normalize_options(const string& verb) } } +void report_t::normalize_period() +{ + date_interval_t interval(HANDLER(period_).str()); + + optional begin = interval.begin(); + optional end = interval.end(); + + if (! HANDLED(begin_) && begin) { + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; + HANDLER(limit_).on(string("?normalize"), predicate); + } + if (! HANDLED(end_) && end) { + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; + HANDLER(limit_).on(string("?normalize"), predicate); + } + + if (! interval.duration) + HANDLER(period_).off(); + else if (! HANDLED(sort_all_)) + HANDLER(sort_xacts_).on_only(string("?normalize")); +} + void report_t::parse_query_args(const value_t& args, const string& whence) { query_t query(args, what_to_keep()); - if (query.has_predicate(query_t::QUERY_LIMIT)) { - HANDLER(limit_) - .on(whence, query.get_predicate(query_t::QUERY_LIMIT).print_to_str()); - DEBUG("report.predicate", "Predicate = " << HANDLER(limit_).str()); + if (query.has_query(query_t::QUERY_LIMIT)) { + HANDLER(limit_).on(whence, query.get_query(query_t::QUERY_LIMIT)); + DEBUG("report.predicate", "Limit predicate = " << HANDLER(limit_).str()); } - if (query.has_predicate(query_t::QUERY_SHOW)) { - HANDLER(display_) - .on(whence, query.get_predicate(query_t::QUERY_SHOW).print_to_str()); + if (query.has_query(query_t::QUERY_SHOW)) { + HANDLER(display_).on(whence, query.get_query(query_t::QUERY_SHOW)); DEBUG("report.predicate", "Display predicate = " << HANDLER(display_).str()); } - if (query.has_predicate(query_t::QUERY_BOLD)) { - HANDLER(bold_if_) - .set_expr(whence, query.get_predicate(query_t::QUERY_BOLD).print_to_str()); - DEBUG("report.predicate", "Bolding predicate = " << HANDLER(display_).str()); + if (query.has_query(query_t::QUERY_BOLD)) { + HANDLER(bold_if_).set_expr(whence, query.get_query(query_t::QUERY_BOLD)); + DEBUG("report.predicate", "Bolding predicate = " << HANDLER(bold_if_).str()); + } + + if (query.has_query(query_t::QUERY_FOR)) { + HANDLER(period_).on(whence, query.get_query(query_t::QUERY_FOR)); + DEBUG("report.predicate", "Report period = " << HANDLER(period_).str()); + + normalize_period(); // it needs normalization } } diff --git a/src/report.h b/src/report.h index fc2fc1a2..59a632e6 100644 --- a/src/report.h +++ b/src/report.h @@ -126,6 +126,7 @@ public: } void normalize_options(const string& verb); + void normalize_period(); void parse_query_args(const value_t& args, const string& whence); void posts_report(post_handler_ptr handler); -- cgit v1.2.3