|
| 1 | +" vim:ts=2:sw=2:sts=2:foldmethod=marker |
1 | 2 | function! ledger#transaction_state_toggle(lnum, ...)
|
2 | 3 | if a:0 == 1
|
3 | 4 | let chars = a:1
|
@@ -398,5 +399,233 @@ func! ledger#entry()
|
398 | 399 | let l = line('.') - 1 " Insert transaction at the current line (i.e., below the line above the current one)
|
399 | 400 | let query = getline('.')
|
400 | 401 | normal "_dd
|
401 |
| - exec l . 'read !' g:ledger_bin '-f' shellescape(expand('%')) 'entry' shellescape(query) |
| 402 | + exec l . 'read !' g:ledger_bin '-f' shellescape(expand(g:ledger_main)) 'entry' shellescape(query) |
402 | 403 | endfunc
|
| 404 | + |
| 405 | +" Report generation {{{1 |
| 406 | + |
| 407 | +" Helper functions and variables {{{2 |
| 408 | +" Position of report windows |
| 409 | +let s:winpos_map = { |
| 410 | + \ "T": "to new", "t": "abo new", "B": "bo new", "b": "bel new", |
| 411 | + \ "L": "to vnew", "l": "abo vnew", "R": "bo vnew", "r": "bel vnew" |
| 412 | + \ } |
| 413 | + |
| 414 | +function! s:error_message(msg) |
| 415 | + redraw " See h:echo-redraw |
| 416 | + echohl ErrorMsg |
| 417 | + echo "\r" |
| 418 | + echomsg a:msg |
| 419 | + echohl NONE |
| 420 | +endf |
| 421 | + |
| 422 | +function! s:warning_message(msg) |
| 423 | + redraw " See h:echo-redraw |
| 424 | + echohl WarningMsg |
| 425 | + echo "\r" |
| 426 | + echomsg a:msg |
| 427 | + echohl NONE |
| 428 | +endf |
| 429 | + |
| 430 | +" Open the quickfix/location window when it is not empty, |
| 431 | +" closes it if it is empty. |
| 432 | +" |
| 433 | +" Optional parameters: |
| 434 | +" a:1 Quickfix window title. |
| 435 | +" a:2 Message to show when the window is empty. |
| 436 | +" |
| 437 | +" Returns 0 if the quickfix window is empty, 1 otherwise. |
| 438 | +function! s:quickfix_toggle(...) |
| 439 | + if g:ledger_use_location_list |
| 440 | + let l:list = 'l' |
| 441 | + let l:open = (len(getloclist(winnr())) > 0) |
| 442 | + else |
| 443 | + let l:list = 'c' |
| 444 | + let l:open = (len(getqflist()) > 0) |
| 445 | + endif |
| 446 | + |
| 447 | + if l:open |
| 448 | + execute (g:ledger_qf_vertical ? 'vert' : 'botright') l:list.'open' g:ledger_qf_size |
| 449 | + " Set local mappings to quit the quickfix window or lose focus. |
| 450 | + nnoremap <silent> <buffer> <tab> <c-w><c-w> |
| 451 | + execute 'nnoremap <silent> <buffer> q :' l:list.'close<CR>' |
| 452 | + " Note that the following settings do not persist (e.g., when you close and re-open the quickfix window). |
| 453 | + " See: http://superuser.com/questions/356912/how-do-i-change-the-quickix-title-status-bar-in-vim |
| 454 | + if g:ledger_qf_hide_file |
| 455 | + setl conceallevel=2 |
| 456 | + setl concealcursor=nc |
| 457 | + syntax match qfFile /^[^|]*/ transparent conceal |
| 458 | + endif |
| 459 | + if a:0 > 0 |
| 460 | + let w:quickfix_title = a:1 |
| 461 | + endif |
| 462 | + return 1 |
| 463 | + endif |
| 464 | + |
| 465 | + execute l:list.'close' |
| 466 | + call s:warning_message((a:0 > 1) ? a:2 : 'No results') |
| 467 | + return 0 |
| 468 | +endf |
| 469 | + |
| 470 | +" Populate a quickfix/location window with data. The argument must be a String |
| 471 | +" or a List. |
| 472 | +function! s:quickfix_populate(data) |
| 473 | + " Note that cexpr/lexpr always uses the global value of errorformat |
| 474 | + let l:efm = &errorformat " Save global errorformat |
| 475 | + set errorformat=%EWhile\ parsing\ file\ \"%f\"\\,\ line\ %l:,%ZError:\ %m,%-C%.%# |
| 476 | + set errorformat+=%tarning:\ \"%f\"\\,\ line\ %l:\ %m |
| 477 | + " Format to parse command-line errors: |
| 478 | + set errorformat+=Error:\ %m |
| 479 | + " Format to parse reports: |
| 480 | + set errorformat+=%f:%l\ %m |
| 481 | + set errorformat+=%-G%.%# |
| 482 | + execute (g:ledger_use_location_list ? 'l' : 'c').'getexpr' 'a:data' |
| 483 | + let &errorformat = l:efm " Restore global errorformat |
| 484 | + return |
| 485 | +endf |
| 486 | + |
| 487 | +" Build a ledger command to process the given file. |
| 488 | +function! s:ledger_cmd(file, args) |
| 489 | + return join([g:ledger_bin, g:ledger_extra_options, '-f', shellescape(expand(a:file)), a:args]) |
| 490 | +endf |
| 491 | +" }}} |
| 492 | + |
| 493 | +" Run an arbitrary ledger command and show the output in a new buffer. If |
| 494 | +" there are errors, no new buffer is opened: the errors are displayed in a |
| 495 | +" quickfix window instead. |
| 496 | +" |
| 497 | +" Parameters: |
| 498 | +" file The file to be processed. |
| 499 | +" args A string of Ledger command-line arguments. |
| 500 | +function! ledger#report(file, args) |
| 501 | + let l:output = systemlist(s:ledger_cmd(a:file, a:args)) |
| 502 | + if v:shell_error " If there are errors, show them in a quickfix/location list. |
| 503 | + call s:quickfix_populate(l:output) |
| 504 | + call s:quickfix_toggle('Errors', 'Unable to parse errors') |
| 505 | + return |
| 506 | + endif |
| 507 | + if empty(l:output) |
| 508 | + call s:warning_message('No results') |
| 509 | + return |
| 510 | + endif |
| 511 | + " Open a new buffer to show Ledger's output. |
| 512 | + execute get(s:winpos_map, g:ledger_winpos, "bo new") |
| 513 | + setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap |
| 514 | + call append(0, l:output) |
| 515 | + setlocal nomodifiable |
| 516 | + " Set local mappings to quit window or lose focus. |
| 517 | + nnoremap <silent> <buffer> <tab> <c-w><c-w> |
| 518 | + nnoremap <silent> <buffer> q <c-w>c |
| 519 | + " Add some coloring to the report |
| 520 | + syntax match LedgerNumber /-\@1<!\d\+\([,.]\d\+\)\+/ |
| 521 | + syntax match LedgerNegativeNumber /-\d\+\([,.]\d\+\)\+/ |
| 522 | + syntax match LedgerImproperPerc /\d\d\d\+%/ |
| 523 | +endf |
| 524 | + |
| 525 | +" Show an arbitrary register report in a quickfix list. |
| 526 | +" |
| 527 | +" Parameters: |
| 528 | +" file The file to be processed |
| 529 | +" args A string of Ledger command-line arguments. |
| 530 | +function! ledger#register(file, args) |
| 531 | + let l:cmd = s:ledger_cmd(a:file, join([ |
| 532 | + \ "register", |
| 533 | + \ "--format='" . g:ledger_qf_register_format . "'", |
| 534 | + \ "--prepend-format='%(filename):%(beg_line) '", |
| 535 | + \ a:args |
| 536 | + \ ])) |
| 537 | + call s:quickfix_populate(systemlist(l:cmd)) |
| 538 | + call s:quickfix_toggle('Register report') |
| 539 | +endf |
| 540 | + |
| 541 | +" Reconcile the given account. |
| 542 | +" This function accepts a file path as a third optional argument. |
| 543 | +" The default is to use the value of g:ledger_main. |
| 544 | +" |
| 545 | +" Parameters: |
| 546 | +" file The file to be processed |
| 547 | +" account An account name (String) |
| 548 | +" target_amount The target amount (Float) |
| 549 | +function! ledger#reconcile(file, account, target_amount) |
| 550 | + let l:cmd = s:ledger_cmd(a:file, join([ |
| 551 | + \ "register", |
| 552 | + \ "--uncleared", |
| 553 | + \ "--format='" . g:ledger_qf_reconcile_format . "'", |
| 554 | + \ "--prepend-format='%(filename):%(beg_line) %(pending ? \"P\" : \"U\") '", |
| 555 | + \ a:account |
| 556 | + \ ])) |
| 557 | + let l:file = expand(a:file) " Needed for #show_balance() later |
| 558 | + call s:quickfix_populate(systemlist(l:cmd)) |
| 559 | + if s:quickfix_toggle("Reconcile " . a:account, "Nothing to reconcile") |
| 560 | + let g:ledger_target_amount = a:target_amount |
| 561 | + " Show updated account balance upon saving, as long as the quickfix window is open |
| 562 | + augroup reconcile |
| 563 | + autocmd! |
| 564 | + execute "autocmd BufWritePost *.ldg,*.ledger call ledger#show_balance('" . l:file . "','" . a:account . "')" |
| 565 | + autocmd BufWipeout <buffer> call <sid>finish_reconciling() |
| 566 | + augroup END |
| 567 | + " Add refresh shortcut |
| 568 | + execute "nnoremap <silent> <buffer> <c-l> :<c-u>call ledger#reconcile('" |
| 569 | + \ . l:file . "','" . a:account . "'," . string(a:target_amount) . ")<cr>" |
| 570 | + call ledger#show_balance(l:file, a:account) |
| 571 | + endif |
| 572 | +endf |
| 573 | + |
| 574 | +function! s:finish_reconciling() |
| 575 | + unlet g:ledger_target_amount |
| 576 | + augroup reconcile |
| 577 | + autocmd! |
| 578 | + augroup END |
| 579 | + augroup! reconcile |
| 580 | +endf |
| 581 | + |
| 582 | +" Show the pending/cleared balance of an account. |
| 583 | +" This function has an optional parameter: |
| 584 | +" |
| 585 | +" a:1 An account name |
| 586 | +" |
| 587 | +" If no account if given, the account in the current line is used. |
| 588 | +function! ledger#show_balance(file, ...) |
| 589 | + let l:account = a:0 > 0 && !empty(a:1) ? a:1 : matchstr(getline('.'), '\m\( \|\t\)\zs\S.\{-}\ze\( \|\t\|$\)') |
| 590 | + if empty(l:account) |
| 591 | + call s:error_message('No account found') |
| 592 | + return |
| 593 | + endif |
| 594 | + let l:cmd = s:ledger_cmd(a:file, join([ |
| 595 | + \ "cleared", |
| 596 | + \ l:account, |
| 597 | + \ "--empty", |
| 598 | + \ "--collapse", |
| 599 | + \ "--format='%(scrub(get_at(display_total, 0)))|%(scrub(get_at(display_total, 1)))|%(quantity(scrub(get_at(display_total, 1))))'", |
| 600 | + \ (empty(g:ledger_default_commodity) ? '' : "-X " . shellescape(g:ledger_default_commodity)) |
| 601 | + \ ])) |
| 602 | + let l:output = systemlist(l:cmd) |
| 603 | + " Errors may occur, for example, when the account has multiple commodities |
| 604 | + " and g:ledger_default_commodity is empty. |
| 605 | + if v:shell_error |
| 606 | + call s:quickfix_populate(l:output) |
| 607 | + call s:quickfix_toggle('Errors', 'Unable to parse errors') |
| 608 | + return |
| 609 | + endif |
| 610 | + let l:amounts = split(l:output[-1], '|') |
| 611 | + redraw " Necessary in some cases to overwrite previous messages. See :h echo-redraw |
| 612 | + if len(l:amounts) < 3 |
| 613 | + call s:error_message("Could not determine balance. Did you use a valid account?") |
| 614 | + return |
| 615 | + endif |
| 616 | + echo g:ledger_pending_string |
| 617 | + echohl LedgerPending |
| 618 | + echon l:amounts[0] |
| 619 | + echohl NONE |
| 620 | + echon ' ' g:ledger_cleared_string |
| 621 | + echohl LedgerCleared |
| 622 | + echon l:amounts[1] |
| 623 | + echohl NONE |
| 624 | + if exists('g:ledger_target_amount') |
| 625 | + echon ' ' g:ledger_target_string |
| 626 | + echohl LedgerTarget |
| 627 | + echon printf('%.2f', (g:ledger_target_amount - str2float(l:amounts[2]))) |
| 628 | + echohl NONE |
| 629 | + endif |
| 630 | +endf |
| 631 | +" }}} |
0 commit comments