Merge branch 'next' into ledger-mode-automatic-transactions

This commit is contained in:
Craig Earls 2013-03-09 12:27:08 -05:00
commit ca99c0de03
34 changed files with 350 additions and 3110 deletions

View file

@ -316,12 +316,14 @@ markup within your ledger. For exmaple
<<< information to not sort >>>
@end smallexample
You can use menu entries to insert start and end markers. These
functions will automatically delete old markers and put new new marker
at point.
@node Hiding Transactions, , Sorting Transactions, The Ledger Buffer
@section Hiding Transactions
Often you will want to run Ledger register reports just to look at a
Often you will want to run Ledger register reports just to look at ax
specific set of transactions. If you don't need the running total
calculation handled by Ledger, Ledger-mode provides a rapid way of
narrowing what is displayed in the buffer in a way that is simpler than

View file

@ -1,19 +1,21 @@
set(EMACS_LISP_SOURCES
ldg-commodities.el
ldg-complete.el
ldg-exec.el
ldg-fonts.el
ldg-init.el
ldg-mode.el
ldg-new.el
ldg-occur.el
ldg-post.el
ldg-reconcile.el
ldg-regex.el
ldg-register.el
ldg-report.el
ldg-sort.el
ldg-state.el
ldg-test.el
ldg-texi.el
ldg-xact.el
ledger.el
timeclock.el)
ldg-xact.el)
# find emacs and complain if not found
find_program(EMACS_EXECUTABLE emacs)

View file

@ -33,28 +33,30 @@
(defun ledger-split-commodity-string (str)
"Split a commoditized amount into two parts"
(let (val
comm)
(with-temp-buffer
(insert str)
(goto-char (point-min))
(cond ((re-search-forward "-?[1-9][0-9]*[.,][0-9]*" nil t)
;; found a decimal number
(setq val
(string-to-number
(ledger-commodity-string-number-decimalize
(delete-and-extract-region (match-beginning 0) (match-end 0)) :from-user)))
(goto-char (point-min))
(re-search-forward "[^[:space:]]" nil t)
(setq comm
(delete-and-extract-region (match-beginning 0) (match-end 0)))
(list val comm))
((re-search-forward "0" nil t)
;; couldn't find a decimal number, look for a single 0,
;; indicating account with zero balance
(list 0 ledger-reconcile-default-commodity))
(t
(error "split-commodity-string: cannot parse commodity string: %S" str))))))
(if (> (length str) 0)
(let (val
comm)
(with-temp-buffer
(insert str)
(goto-char (point-min))
(cond ((re-search-forward "-?[1-9][0-9]*[.,][0-9]*" nil t)
;; found a decimal number
(setq val
(string-to-number
(ledger-commodity-string-number-decimalize
(delete-and-extract-region (match-beginning 0) (match-end 0)) :from-user)))
(goto-char (point-min))
(re-search-forward "[^[:space:]]" nil t)
(setq comm
(delete-and-extract-region (match-beginning 0) (match-end 0)))
(list val comm))
((re-search-forward "0" nil t)
;; couldn't find a decimal number, look for a single 0,
;; indicating account with zero balance
(list 0 ledger-reconcile-default-commodity))
(t
(error "split-commodity-string: cannot parse commodity string: %S" str)))))
(list 0 ledger-reconcile-default-commodity)))
(defun ledger-string-balance-to-commoditized-amount (str)

View file

@ -145,16 +145,17 @@ Return tree structure"
"Completes a transaction if there is another matching payee in the buffer.
Does not use ledger xact"
(interactive)
(let ((name (caar (ledger-parse-arguments)))
rest-of-name
(let* ((name (caar (ledger-parse-arguments)))
(rest-of-name name)
xacts)
(save-excursion
(when (eq 'transaction (ledger-thing-at-point))
(delete-region (point) (+ (length name) (point)))
;; Search backward for a matching payee
(when (re-search-backward
(concat "^[0-9/.=-]+\\(\\s-+\\*\\)?\\(\\s-+(.*?)\\)?\\s-+"
(regexp-quote name) ) nil t) ;; "\\(\t\\|\n\\| [ \t]\\)"
(setq rest-of-name (buffer-substring-no-properties (match-end 0) (line-end-position)))
(concat "^[0-9/.=-]+\\(\\s-+\\*\\)?\\(\\s-+(.*?)\\)?\\s-+\\(.*"
(regexp-quote name) ".*\\)" ) nil t) ;; "\\(\t\\|\n\\| [ \t]\\)"
(setq rest-of-name (match-string 3))
;; Start copying the postings
(forward-line)
(while (looking-at "^\\s-+")

View file

@ -40,30 +40,48 @@
:type 'file
:group 'ledger-exec)
(defun ledger-exec-handle-error (ledger-output)
"Deal with ledger errors contained in LEDGER-OUTPUT."
(with-current-buffer (get-buffer-create "*Ledger Error*")
(insert-buffer-substring ledger-output)
(make-frame)
(fit-frame)
(view-mode)
(toggle-read-only)))
(defun ledger-exec-success-p (ledger-output-buffer)
(with-current-buffer ledger-output-buffer
(goto-char (point-min))
(if (and (> (buffer-size) 1) (looking-at (regexp-quote "While")))
nil
ledger-output-buffer)))
(defun ledger-exec-ledger (input-buffer &optional output-buffer &rest args)
"Run Ledger using INPUT-BUFFER and optionally capturing output in OUTPUT-BUFFER with ARGS."
(if (null ledger-binary-path)
(error "The variable `ledger-binary-path' has not been set"))
(let ((buf (or input-buffer (current-buffer)))
(outbuf (or output-buffer
(generate-new-buffer " *ledger-tmp*"))))
(with-current-buffer buf
(let ((coding-system-for-write 'utf-8)
(coding-system-for-read 'utf-8))
(apply #'call-process-region
(append (list (point-min) (point-max)
ledger-binary-path nil outbuf nil "-f" "-")
args)))
outbuf)))
(error "The variable `ledger-binary-path' has not been set")
(let ((buf (or input-buffer (current-buffer)))
(outbuf (or output-buffer
(generate-new-buffer " *ledger-tmp*"))))
(with-current-buffer buf
(let ((coding-system-for-write 'utf-8)
(coding-system-for-read 'utf-8))
(apply #'call-process-region
(append (list (point-min) (point-max)
ledger-binary-path nil outbuf nil "-f" "-")
args)))
(if (ledger-exec-success-p outbuf)
outbuf
(ledger-exec-handle-error outbuf))))))
(defun ledger-exec-read (&optional input-buffer &rest args)
"Run ledger from option INPUT-BUFFER using ARGS, return a list structure of the ledger Emacs output."
(with-current-buffer
(apply #'ledger-exec-ledger input-buffer nil "emacs" args)
(goto-char (point-min))
(prog1
(read (current-buffer))
(kill-buffer (current-buffer)))))
;; (defun ledger-exec-read (&optional input-buffer &rest args)
;; "Run ledger from option INPUT-BUFFER using ARGS, return a list structure of the ledger Emacs output."
;; (with-current-buffer
;; (apply #'ledger-exec-ledger input-buffer nil "emacs" args)
;; (goto-char (point-min))
;; (prog1
;; (read (current-buffer))
;; (kill-buffer (current-buffer)))))
(defun ledger-version-greater-p (needed)
"Verify the ledger binary is usable for `ledger-mode' (version greater than NEEDED)."
@ -71,17 +89,18 @@
(version-strings '())
(version-number))
(with-temp-buffer
(ledger-exec-ledger buffer (current-buffer) "--version")
(goto-char (point-min))
(delete-horizontal-space)
(setq version-strings (split-string
(buffer-substring-no-properties (point)
(+ (point) 12))))
(if (and (string-match (regexp-quote "Ledger") (car version-strings))
(or (string= needed (car (cdr version-strings)))
(string< needed (car (cdr version-strings)))))
t
nil))))
(if (ledger-exec-ledger (current-buffer) (current-buffer) "--version")
(progn
(goto-char (point-min))
(delete-horizontal-space)
(setq version-strings (split-string
(buffer-substring-no-properties (point)
(point-max))))
(if (and (string-match (regexp-quote "Ledger") (car version-strings))
(or (string= needed (car (cdr version-strings)))
(string< needed (car (cdr version-strings)))))
t
nil))))))
(defun ledger-check-version ()
"Verify that ledger works and is modern enough."

View file

@ -116,6 +116,8 @@
(interactive)
(customize-group 'ledger))))
(define-key map [sep1] '("--"))
(define-key map [sort-end] '(menu-item "Mark Sort End" ledger-sort-insert-end-mark))
(define-key map [sort-start] '(menu-item "Mark Sort Beginning" ledger-sort-insert-start-mark))
(define-key map [sort-buff] '(menu-item "Sort Buffer" ledger-sort-buffer))
(define-key map [sort-reg] '(menu-item "Sort Region" ledger-sort-region :enable mark-active))
(define-key map [sep2] '(menu-item "--"))

View file

@ -32,6 +32,7 @@
;;; Commentary:
;; Load up the ledger mode
(require 'esh-util)
(require 'esh-arg)
(require 'ldg-commodities)
(require 'ldg-complete)
@ -42,7 +43,7 @@
(require 'ldg-occur)
(require 'ldg-post)
(require 'ldg-reconcile)
(require 'ldg-register)
(require 'ldg-regex)
(require 'ldg-report)
(require 'ldg-sort)
(require 'ldg-state)
@ -123,9 +124,6 @@
(ledger-dump-variable 'ledger-buffer-tracks-reconcile-buffer)
(ledger-dump-variable 'ledger-reconcile-force-window-bottom)
(ledger-dump-variable 'ledger-reconcile-toggle-to-pending)
(insert "ldg-register:\n")
(ledger-dump-variable 'ledger-register-date-format)
(ledger-dump-variable 'ledger-register-line-format)
(insert "ldg-reports:\n")
(ledger-dump-variable 'ledger-reports)
(ledger-dump-variable 'ledger-report-format-specifiers)

View file

@ -41,7 +41,9 @@
(make-variable-buffer-local 'ledger-occur-use-face-unfolded)
(defvar ledger-occur-mode nil) ;; name of the minor mode, shown in the mode-line
(defvar ledger-occur-mode nil
"name of the minor mode, shown in the mode-line")
(make-variable-buffer-local 'ledger-occur-mode)
(or (assq 'ledger-occur-mode minor-mode-alist)

View file

@ -31,16 +31,20 @@
"Default indentation for account transactions in an entry."
:type 'string
:group 'ledger-post)
(defgroup ledger-post nil
"Options for controlling how Ledger-mode deals with postings and completion"
:group 'ledger)
(defcustom ledger-post-auto-adjust-amounts nil
"If non-nil, ."
(defcustom ledger-post-auto-adjust-postings t
"If non-nil, adjust account and amount to columns set below"
:type 'boolean
:group 'ledger-post)
(defcustom ledger-post-account-alignment-column 4
"The column Ledger-mode attempts to align accounts to."
:type 'integer
:group 'ledger-post)
(defcustom ledger-post-amount-alignment-column 52
"The column Ledger-mode attempts to align amounts to."
:type 'integer
@ -123,20 +127,26 @@ PROMPT is a string to prompt with. CHOICES is a list of
(- (or (match-end 4)
(match-end 3)) (point))))
(defun ledger-align-amounts (&optional column)
(defun ledger-post-align-postings (&optional column)
"Align amounts and accounts in the current region.
This is done so that the last digit falls in COLUMN, which
defaults to 52. ledger-default-acct-transaction-indent positions
defaults to 52. ledger-post-account-column positions
the account"
(interactive "p")
(if (or (null column) (= column 1))
(setq column ledger-post-amount-alignment-column))
(save-excursion
;; Position the account
;; (beginning-of-line)
(if (not (or (looking-at "[ \t]*[1-9]")
(and (looking-at "[ \t]+\n")
(looking-back "[ \n]" (- (point) 2)))))
(save-excursion
(beginning-of-line)
(set-mark (point))
(delete-horizontal-space)
(insert (make-string ledger-post-account-alignment-column ? )))
(set-mark (point)))
(set-mark (point))
;; (delete-horizontal-space)
;; (insert ledger-default-acct-transaction-indent)
(goto-char (1+ (line-end-position)))
(let* ((mark-first (< (mark) (point)))
(begin (if mark-first (mark) (point)))
@ -148,7 +158,7 @@ the account"
(let ((col (current-column))
(target-col (- column offset))
adjust)
(setq adjust (- target-col col))
(setq adjust (- target-col col))
(if (< col target-col)
(insert (make-string (- target-col col) ? ))
(move-to-column target-col)
@ -159,23 +169,24 @@ the account"
(insert " ")))
(forward-line))))))
(defun ledger-post-align-amount ()
(defun ledger-post-align-posting ()
"Align the amounts in this posting."
(interactive)
(save-excursion
(set-mark (line-beginning-position))
(goto-char (1+ (line-end-position)))
(ledger-align-amounts)))
(ledger-post-align-postings)))
(defun ledger-post-maybe-align (beg end len)
"Align amounts only if point is in a posting.
BEG, END, and LEN control how far it can align."
(save-excursion
(goto-char beg)
(when (<= end (line-end-position))
(goto-char (line-beginning-position))
(if (looking-at ledger-post-line-regexp)
(ledger-align-amounts)))))
(if ledger-post-auto-adjust-postings
(save-excursion
(goto-char beg)
(when (<= end (line-end-position))
(goto-char (line-beginning-position))
(if (looking-at ledger-post-line-regexp)
(ledger-post-align-postings))))))
(defun ledger-post-edit-amount ()
"Call 'calc-mode' and push the amount in the posting to the top of stack."
@ -186,19 +197,10 @@ BEG, END, and LEN control how far it can align."
(let ((end-of-amount (re-search-forward "[-.,0-9]+" (line-end-position) t)))
;; determine if there is an amount to edit
(if end-of-amount
(let ((val (match-string 0)))
(let ((val (ledger-commodity-string-number-decimalize (match-string 0) :from-user)))
(goto-char (match-beginning 0))
(delete-region (match-beginning 0) (match-end 0))
(calc)
(if ledger-use-decimal-comma
(progn
(while (string-match "\\." val)
(setq val (replace-match "" nil nil val))) ;; gets rid of periods
(while (string-match "," val)
(setq val (replace-match "." nil nil val)))) ;; switch to period separator
(progn
(while (string-match "," val)
(setq val (replace-match "" nil nil val))))) ;; gets rid of commas
(calc-eval val 'push)) ;; edit the amount
(progn ;;make sure there are two spaces after the account name and go to calc
(if (search-backward " " (- (point) 3) t)
@ -225,10 +227,21 @@ BEG, END, and LEN control how far it can align."
(defun ledger-post-setup ()
"Configure `ledger-mode' to auto-align postings."
(if ledger-post-auto-adjust-amounts
(add-hook 'after-change-functions 'ledger-post-maybe-align t t))
(add-hook 'after-change-functions 'ledger-post-maybe-align t t)
(add-hook 'after-save-hook #'(lambda () (setq ledger-post-current-list nil))))
(defun ledger-post-read-account-with-prompt (prompt)
(let* ((context (ledger-context-at-point))
(default
(if (eq (ledger-context-line-type context) 'acct-transaction)
(regexp-quote (ledger-context-field-value context 'account))
nil)))
(ledger-read-string-with-default prompt default)))
(provide 'ldg-post)
;;; ldg-post.el ends here

View file

@ -70,32 +70,29 @@ reconcile-finish will mark all pending posting cleared."
(account ledger-acct)
(val nil))
(with-temp-buffer
(ledger-exec-ledger buffer (current-buffer)
; note that in the line below, the --format option is
; separated from the actual format string. emacs does not
; split arguments like the shell does, so you need to
; specify the individual fields in the command line.
"balance" "--limit" "cleared or pending" "--empty"
"--format" "%(display_total)" account)
(setq val
(ledger-split-commodity-string
(buffer-substring-no-properties (point-min) (point-max)))))))
;; note that in the line below, the --format option is
;; separated from the actual format string. emacs does not
;; split arguments like the shell does, so you need to
;; specify the individual fields in the command line.
(if (ledger-exec-ledger buffer (current-buffer)
"balance" "--limit" "cleared or pending" "--empty"
"--format" "%(display_total)" account)
(setq val
(ledger-split-commodity-string
(buffer-substring-no-properties (point-min) (point-max))))))))
(defun ledger-display-balance ()
"Display the cleared-or-pending balnce and calculate the
target-delta of the account being reconciled."
"Display the cleared-or-pending balance.
And calculate the target-delta of the account being reconciled."
(interactive)
(let* ((pending (ledger-reconcile-get-cleared-or-pending-balance))
(target-delta (if ledger-target
(-commodity ledger-target pending)
nil)))
(if target-delta
(message "Pending balance: %s, Difference from target: %s"
(ledger-commodity-to-string pending)
(ledger-commodity-to-string target-delta))
(message "Pending balance: %s"
(ledger-commodity-to-string pending)))))
(let* ((pending (ledger-reconcile-get-cleared-or-pending-balance)))
(if pending
(if ledger-target
(message "Pending balance: %s, Difference from target: %s"
(ledger-commodity-to-string pending)
(ledger-commodity-to-string (-commodity ledger-target pending)))
(message "Pending balance: %s"
(ledger-commodity-to-string pending))))))
@ -111,7 +108,7 @@ target-delta of the account being reconciled."
"Return a buffer from WHERE the transaction is."
(if (bufferp (car where))
(car where)
(error "ledger-reconcile-get-buffer: Buffer not set")))
(error "Function ledger-reconcile-get-buffer: Buffer not set")))
(defun ledger-reconcile-toggle ()
"Toggle the current transaction, and mark the recon window."
@ -276,23 +273,27 @@ POSTING is used in `ledger-clear-whole-transactions' is nil."
"Get the uncleared transactions in the account and display them in the *Reconcile* buffer."
(let* ((buf ledger-buf)
(account ledger-acct)
(ledger-success nil)
(xacts
(with-temp-buffer
(ledger-exec-ledger buf (current-buffer)
"--uncleared" "--real" "emacs" account)
(goto-char (point-min))
(unless (eobp)
(unless (looking-at "(")
(error (concat "ledger-do-reconcile: " (buffer-string))))
(read (current-buffer)))))) ;current-buffer is the *temp* created above
(if (> (length xacts) 0)
(progn
(if (ledger-exec-ledger buf (current-buffer)
"--uncleared" "--real" "emacs" account)
(progn
(setq ledger-success t)
(goto-char (point-min))
(unless (eobp)
(if (looking-at "(")
(read (current-buffer))))))))) ;current-buffer is the *temp* created above
(if (and ledger-success (> (length xacts) 0))
(let ((date-format (cdr (assoc "date-format" ledger-environment-alist))))
(dolist (xact xacts)
(dolist (posting (nthcdr 5 xact))
(let ((beg (point))
(where (ledger-marker-where-xact-is xact posting)))
(insert (format "%s %-4s %-30s %-30s %15s\n"
(format-time-string "%Y/%m/%d" (nth 2 xact))
(format-time-string (if date-format
date-format
"%Y/%m/%d") (nth 2 xact))
(if (nth 3 xact)
(nth 3 xact)
"")
@ -310,7 +311,9 @@ POSTING is used in `ledger-clear-whole-transactions' is nil."
'where where)))) ))
(goto-char (point-max))
(delete-char -1)) ;gets rid of the extra line feed at the bottom of the list
(insert (concat "There are no uncleared entries for " account)))
(if ledger-success
(insert (concat "There are no uncleared entries for " account))
(insert "Ledger has reported a problem. Check *Ledger Error* buffer.")))
(goto-char (point-min))
(set-buffer-modified-p nil)
(toggle-read-only t)
@ -351,10 +354,11 @@ POSTING is used in `ledger-clear-whole-transactions' is nil."
(set-window-buffer (split-window (get-buffer-window buf) nil nil) rbuf)
(pop-to-buffer rbuf)))
(defun ledger-reconcile (account)
"Start reconciling ACCOUNT."
(interactive "sAccount to reconcile: ")
(let ((buf (current-buffer))
(defun ledger-reconcile ()
"Start reconciling, prompt for account."
(interactive)
(let ((account (ledger-post-read-account-with-prompt "Account to reconcile"))
(buf (current-buffer))
(rbuf (get-buffer ledger-recon-buffer-name))) ;; this means
;; only one
;; *Reconcile*
@ -396,7 +400,7 @@ POSTING is used in `ledger-clear-whole-transactions' is nil."
(defvar ledger-reconcile-mode-abbrev-table)
(defun ledger-reconcile-change-target ()
"Change the traget amount for the reconciliation process."
"Change the target amount for the reconciliation process."
(interactive)
(setq ledger-target (ledger-read-commodity-string "Set reconciliation target")))
@ -442,6 +446,5 @@ POSTING is used in `ledger-clear-whole-transactions' is nil."
(use-local-map map)))
(provide 'ldg-reconcile)
(provide 'ldg-reconcile)
;;; ldg-reconcile.el ends here

View file

@ -24,7 +24,8 @@
(eval-when-compile
(require 'cl))
(defvar ledger-date-regex "\\([0-9]+\\)[/-]\\([0-9]+\\)[/-]\\([0-9]+\\)")
(defvar ledger-date-regex
"\\([0-9]+\\)[/-]\\([0-9]+\\)[/-]\\([0-9]+\\)")
(defmacro ledger-define-regexp (name regex docs &rest args)
"Simplify the creation of a Ledger regex and helper functions."

View file

@ -1,86 +0,0 @@
;;; ldg-register.el --- Helper code for use with the "ledger" command-line tool
;; Copyright (C) 2003-2013 John Wiegley (johnw AT gnu DOT org)
;; This file is not part of GNU Emacs.
;; This is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 2, or (at your option) any later
;; version.
;;
;; This is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
;; for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
;; MA 02111-1307, USA.
(require 'ldg-post)
(require 'ldg-state)
(defgroup ledger-register nil
""
:group 'ledger)
(defcustom ledger-register-date-format "%m/%d/%y"
"*The date format used for ledger register reports."
:type 'string
:group 'ledger-register)
(defcustom ledger-register-line-format "%s %-30.30s %-25.25s %15s\n"
"*The date format used for ledger register reports."
:type 'string
:group 'ledger-register)
(defface ledger-register-pending-face
'((((background light)) (:weight bold))
(((background dark)) (:weight bold)))
"Face used to highlight pending entries in a register report."
:group 'ledger-register)
(defun ledger-register-render (data-buffer posts)
(dolist (post posts)
(let ((index 1))
(dolist (xact (nthcdr 5 post))
(let ((beg (point))
(where
(with-current-buffer data-buffer
(cons
(nth 0 post)
(if ledger-clear-whole-transactions
(save-excursion
(goto-line (nth 1 post))
(point-marker))
(save-excursion
(goto-line (nth 0 xact))
(point-marker)))))))
(insert (format ledger-register-line-format
(format-time-string ledger-register-date-format
(nth 2 post))
(nth 4 post) (nth 1 xact) (nth 2 xact)))
(if (nth 3 xact)
(set-text-properties beg (1- (point))
(list 'face 'ledger-register-pending-face
'where where))
(set-text-properties beg (1- (point))
(list 'where where))))
(setq index (1+ index)))))
(goto-char (point-min)))
(defun ledger-register-generate (&optional data-buffer &rest args)
(let ((buf (or data-buffer (current-buffer))))
(with-current-buffer (get-buffer-create "*ledger-register*")
(let ((pos (point))
(inhibit-read-only t))
(erase-buffer)
(ledger-register-render buf (apply #'ledger-exec-read buf args))
(goto-char pos))
(set-buffer-modified-p nil)
(toggle-read-only t)
(display-buffer (current-buffer) t))))
(provide 'ldg-register)

View file

@ -258,12 +258,7 @@ used to generate the buffer, navigating the buffer, etc."
the default."
;; It is intended completion should be available on existing account
;; names, but it remains to be implemented.
(let* ((context (ledger-context-at-point))
(default
(if (eq (ledger-context-line-type context) 'acct-transaction)
(regexp-quote (ledger-context-field-value context 'account))
nil)))
(ledger-read-string-with-default "Account" default)))
(ledger-post-read-account-with-prompt "Account"))
(defun ledger-report-expand-format-specifiers (report-cmd)
"Expand %(account) and %(payee) appearing in REPORT-CMD with thing under point."
@ -437,11 +432,13 @@ Optional EDIT the command."
("^\\(\\([0-9][0-9][0-9][0-9]/\\)?[01]?[0-9]/[0123]?[0-9]\\)[ \t]+\\(\\([!*]\\)[ \t]\\)?[ \t]*\\((\\(.*\\))\\)?[ \t]*\\(.*\\)[ \t]*$"
(date nil status nil nil code payee))))
(acct-transaction
(("\\(^[ \t]+\\)\\(.*?\\)[ \t]+\\([$]\\)\\(-?[0-9]*\\(\\.[0-9]*\\)?\\)[ \t]*;[ \t]*\\(.*?\\)[ \t]*$"
(("^\\([ \t]+;\\|;\\)\\s-?\\(.*\\)"
(indent comment))
("\\(^[ \t]+\\)\\([:A-Za-z0-9]+?\\)\\s-\\s-+\\([$€£]\\s-?\\)\\(-?[0-9]*\\(\\.[0-9]*\\)?\\)$"
(indent account commodity amount))
("\\(^[ \t]+\\)\\(.*?\\)[ \t]+\\([$€£]\\s-?\\)\\(-?[0-9]*\\(\\.[0-9]*\\)?\\)[ \t]*;[ \t]*\\(.*?\\)[ \t]*$"
(indent account commodity amount nil comment))
("\\(^[ \t]+\\)\\(.*?\\)[ \t]+\\([$]\\)\\(-?[0-9]*\\(\\.[0-9]*\\)?\\)[ \t]*$"
(indent account commodity amount nil))
("\\(^[ \t]+\\)\\(.*?\\)[ \t]+\\(-?[0-9]+\\(\\.[0-9]*\\)?\\)[ \t]+\\(.*?\\)[ \t]*;[ \t]*\\(.*?\\)[ \t]*$"
("\\(^[ \t]+\\)\\(.*?\\)[ \t]+\\(-?[0-9]+\\(\\.[0-9]*\\)?\\)[ \t]+\\(.*?\\)[ \t]*\\(;[ \t]*\\(.*?\\)[ \t]*$\\|@+\\)"
(indent account amount nil commodity comment))
("\\(^[ \t]+\\)\\(.*?\\)[ \t]+\\(-?[0-9]+\\(\\.[0-9]*\\)?\\)[ \t]+\\(.*?\\)[ \t]*$"
(indent account amount nil commodity))
@ -452,7 +449,13 @@ Optional EDIT the command."
("\\(^[ \t]+\\)\\(.*?\\)[ \t]*;[ \t]*\\(.*?\\)[ \t]*$"
(indent account comment))
("\\(^[ \t]+\\)\\(.*?\\)[ \t]*$"
(indent account))))))
(indent account))
;; Bad regexes
("\\(^[ \t]+\\)\\(.*?\\)[ \t]+\\([$€£]\\s-?\\)\\(-?[0-9]*\\(\\.[0-9]*\\)?\\)[ \t]*$"
(indent account commodity amount nil))
))))
(defun ledger-extract-context-info (line-type pos)
"Get context info for current line with LINE-TYPE.

View file

@ -38,6 +38,36 @@
"Move point to end of transaction."
(forward-paragraph))
(defun ledger-sort-find-start ()
(if (re-search-forward ";.*Ledger-mode:.*Start sort" nil t)
(match-end 0)))
(defun ledger-sort-find-end ()
(if (re-search-forward ";.*Ledger-mode:.*End sort" nil t)
(match-end 0)))
(defun ledger-sort-insert-start-mark ()
(interactive)
(let (has-old-marker)
(save-excursion
(goto-char (point-min))
(setq has-old-marker (ledger-sort-find-start))
(if has-old-marker
(delete-region (match-beginning 0) (match-end 0))))
(beginning-of-line)
(insert "\n; Ledger-mode: Start sort\n\n")))
(defun ledger-sort-insert-end-mark ()
(interactive)
(let (has-old-marker)
(save-excursion
(goto-char (point-min))
(setq has-old-marker (ledger-sort-find-end))
(if has-old-marker
(delete-region (match-beginning 0) (match-end 0))))
(beginning-of-line)
(insert "\n; Ledger-mode: End sort\n\n")))
(defun ledger-sort-region (beg end)
"Sort the region from BEG to END in chronological order."
(interactive "r") ;; load beg and end from point and mark
@ -66,14 +96,15 @@
(defun ledger-sort-buffer ()
"Sort the entire buffer."
(interactive)
(let ((sort-start (point-min))
(sort-end (point-max)))
(goto-char (point-min))
(if (re-search-forward ";.*Ledger-mode:.*Start sort" nil t)
(set 'sort-start (match-end 0)))
(if (re-search-forward ";.*Ledger-mode:.*End sort" nil t)
(set 'sort-end (match-end 0)))
(ledger-sort-region sort-start sort-end)))
(goto-char (point-min))
(let ((sort-start (ledger-sort-find-start))
(sort-end (ledger-sort-find-end)))
(ledger-sort-region (if sort-start
sort-start
(point-min))
(if sort-end
sort-end
(point-max)))))
(provide 'ldg-sort)

View file

@ -122,42 +122,48 @@ dropped."
;;this excursion toggles the posting status
(save-excursion
(goto-char (line-beginning-position))
(when (looking-at "[ \t]")
(skip-chars-forward " \t")
(let ((here (point))
(cur-status (ledger-state-from-char (char-after))))
(skip-chars-forward "*! ")
(let ((width (- (point) here)))
(when (> width 0)
(delete-region here (point))
(save-excursion
(if (search-forward " " (line-end-position) t)
(insert (make-string width ? ))))))
(let (inserted)
(if cur-status
(if (and style (eq style 'cleared))
(progn
(insert "* ")
(setq inserted 'cleared)))
(if (and style (eq style 'pending))
(progn
(insert "! ")
(setq inserted 'pending))
(progn
(insert "* ")
(setq inserted 'cleared))))
(if (and inserted
(re-search-forward "\\(\t\\| [ \t]\\)"
(line-end-position) t))
(cond
((looking-at "\t")
(delete-char 1))
((looking-at " [ \t]")
(delete-char 2))
((looking-at " ")
(delete-char 1))))
(setq new-status inserted)))))
(let ((has-align-hook (remove-hook
'after-change-functions
'ledger-post-maybe-align t)))
(goto-char (line-beginning-position))
(when (looking-at "[ \t]")
(skip-chars-forward " \t")
(let ((here (point))
(cur-status (ledger-state-from-char (char-after))))
(skip-chars-forward "*! ")
(let ((width (- (point) here)))
(when (> width 0)
(delete-region here (point))
(save-excursion
(if (search-forward " " (line-end-position) t)
(insert (make-string width ? ))))))
(let (inserted)
(if cur-status
(if (and style (eq style 'cleared))
(progn
(insert "* ")
(setq inserted 'cleared)))
(if (and style (eq style 'pending))
(progn
(insert "! ")
(setq inserted 'pending))
(progn
(insert "* ")
(setq inserted 'cleared))))
(if (and inserted
(re-search-forward "\\(\t\\| [ \t]\\)"
(line-end-position) t))
(cond
((looking-at "\t")
(delete-char 1))
((looking-at " [ \t]")
(delete-char 2))
((looking-at " ")
(delete-char 1))))
(setq new-status inserted))))
(if has-align-hook
(add-hook 'after-change-functions 'ledger-post-maybe-align t t))))
;; This excursion cleans up the entry so that it displays
;; minimally. This means that if all posts are cleared, remove
@ -254,6 +260,4 @@ dropped."
(provide 'ldg-state)
(provide 'ldg-state)
;;; ldg-state.el ends here

View file

@ -118,9 +118,6 @@ within the transaction."
(replace-match date)
(re-search-forward "[1-9][0-9]+\.[0-9]+")))
(provide 'ldg-xact)
(provide 'ldg-xact)
;;; ldg-xact.el ends here

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -692,12 +692,10 @@ void account_t::xdata_t::details_t::update(post_t& post,
}
}
void put_account(property_tree::ptree& pt, const account_t& acct,
void put_account(property_tree::ptree& st, const account_t& acct,
function<bool(const account_t&)> pred)
{
if (pred(acct)) {
property_tree::ptree& st(pt.put("account", ""));
std::ostringstream buf;
buf.width(sizeof(unsigned long) * 2);
buf.fill('0');
@ -709,18 +707,15 @@ void put_account(property_tree::ptree& pt, const account_t& acct,
st.put("fullname", acct.fullname());
value_t total = acct.amount();
if (! total.is_null()) {
property_tree::ptree& t(st.put("account-amount", ""));
put_value(t, total);
}
if (! total.is_null())
put_value(st.put("account-amount", ""), total);
total = acct.total();
if (! total.is_null()) {
property_tree::ptree& t(st.put("account-total", ""));
put_value(t, total);
}
if (! total.is_null())
put_value(st.put("account-total", ""), total);
foreach (const accounts_map::value_type& pair, acct.accounts)
put_account(st, *pair.second, pred);
put_account(st.add("account", ""), *pair.second, pred);
}
}

View file

@ -1320,13 +1320,11 @@ bool amount_t::valid() const
return true;
}
void put_amount(property_tree::ptree& pt, const amount_t& amt,
bool wrap, bool commodity_details)
void put_amount(property_tree::ptree& st, const amount_t& amt,
bool commodity_details)
{
property_tree::ptree& st(wrap ? pt.put("amount", "") : pt);
if (amt.has_commodity())
put_commodity(st, amt.commodity(), commodity_details);
put_commodity(st.put("commodity", ""), amt.commodity(), commodity_details);
st.put("quantity", amt.quantity_string());
}

View file

@ -793,7 +793,7 @@ inline std::istream& operator>>(std::istream& in, amount_t& amt) {
}
void put_amount(property_tree::ptree& pt, const amount_t& amt,
bool wrap = true, bool commodity_details = false);
bool commodity_details = false);
} // namespace ledger

View file

@ -216,19 +216,13 @@ void annotation_t::print(std::ostream& out, bool keep_base,
out << " ((" << *value_expr << "))";
}
void put_annotation(property_tree::ptree& pt, const annotation_t& details)
void put_annotation(property_tree::ptree& st, const annotation_t& details)
{
property_tree::ptree& st(pt.put("annotation", ""));
if (details.price)
put_amount(st.put("price", ""), *details.price);
if (details.price) {
property_tree::ptree& t(st.put("price", ""));
put_amount(t, *details.price, false);
}
if (details.date) {
property_tree::ptree& t(st.put("date", ""));
put_date(t, *details.date, false);
}
if (details.date)
put_date(st.put("date", ""), *details.date);
if (details.tag)
st.put("tag", *details.tag);

View file

@ -336,12 +336,10 @@ void balance_t::print(std::ostream& out,
amount_printer.close();
}
void put_balance(property_tree::ptree& pt, const balance_t& bal)
void put_balance(property_tree::ptree& st, const balance_t& bal)
{
property_tree::ptree& st(pt.put("balance", ""));
foreach (const balance_t::amounts_map::value_type& pair, bal.amounts)
put_amount(st, pair.second);
put_amount(st.add("amount", ""), pair.second);
}
} // namespace ledger

View file

@ -497,11 +497,9 @@ bool commodity_t::compare_by_commodity::operator()(const amount_t * left,
}
}
void put_commodity(property_tree::ptree& pt, const commodity_t& comm,
void put_commodity(property_tree::ptree& st, const commodity_t& comm,
bool commodity_details)
{
property_tree::ptree& st(pt.put("commodity", ""));
std::string flags;
if (! (comm.has_flags(COMMODITY_STYLE_SUFFIXED))) flags += 'P';
if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) flags += 'S';
@ -512,7 +510,7 @@ void put_commodity(property_tree::ptree& pt, const commodity_t& comm,
st.put("symbol", comm.symbol());
if (commodity_details && comm.has_annotation())
put_annotation(st, as_annotated_commodity(comm).details);
put_annotation(st.put("annotation", ""), as_annotated_commodity(comm).details);
}
} // namespace ledger

View file

@ -600,16 +600,15 @@ string item_context(const item_t& item, const string& desc)
return out.str();
}
void put_metadata(property_tree::ptree& pt, const item_t::string_map& metadata)
void put_metadata(property_tree::ptree& st, const item_t::string_map& metadata)
{
property_tree::ptree& st(pt.put("metadata", ""));
foreach (const item_t::string_map::value_type& pair, metadata) {
if (pair.second.first) {
property_tree::ptree& vt(st.put("pair", ""));
vt.put("key", pair.first);
property_tree::ptree& vt(st.add("value", ""));
vt.put("<xmlattr>.key", pair.first);
put_value(vt, *pair.second.first);
} else {
st.put("tag", pair.first);
st.add("tag", pair.first);
}
}
}

View file

@ -151,7 +151,7 @@ inline std::ostream& operator<<(std::ostream& out, const mask_t& mask) {
}
inline void put_mask(property_tree::ptree& pt, const mask_t& mask) {
pt.put("mask", mask.str());
pt.put_value(mask.str());
}
} // namespace ledger

View file

@ -696,10 +696,8 @@ void extend_post(post_t& post, journal_t& journal)
}
}
void put_post(property_tree::ptree& pt, const post_t& post)
void put_post(property_tree::ptree& st, const post_t& post)
{
property_tree::ptree& st(pt.put("posting", ""));
if (post.state() == item_t::CLEARED)
st.put("<xmlattr>.state", "cleared");
else if (post.state() == item_t::PENDING)
@ -710,14 +708,10 @@ void put_post(property_tree::ptree& pt, const post_t& post)
if (post.has_flags(ITEM_GENERATED))
st.put("<xmlattr>.generated", "true");
if (post._date) {
property_tree::ptree& t(st.put("date", ""));
put_date(t, *post._date, false);
}
if (post._date_aux) {
property_tree::ptree& t(st.put("aux-date", ""));
put_date(t, *post._date_aux, false);
}
if (post._date)
put_date(st.put("date", ""), *post._date);
if (post._date_aux)
put_date(st.put("aux-date", ""), *post._date_aux);
if (post.account) {
property_tree::ptree& t(st.put("account", ""));
@ -736,34 +730,27 @@ void put_post(property_tree::ptree& pt, const post_t& post)
if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND))
put_value(t, post.xdata().compound_value);
else
put_amount(t, post.amount);
put_amount(t.put("amount", ""), post.amount);
}
if (post.cost) {
property_tree::ptree& t(st.put("cost", ""));
put_amount(t, *post.cost, false);
}
if (post.cost)
put_amount(st.put("cost", ""), *post.cost);
if (post.assigned_amount) {
if (post.has_flags(POST_CALCULATED)) {
property_tree::ptree& t(st.put("balance-assertion", ""));
put_amount(t, *post.assigned_amount, false);
} else {
property_tree::ptree& t(st.put("balance-assignment", ""));
put_amount(t, *post.assigned_amount, false);
}
if (post.has_flags(POST_CALCULATED))
put_amount(st.put("balance-assertion", ""), *post.assigned_amount);
else
put_amount(st.put("balance-assignment", ""), *post.assigned_amount);
}
if (post.note)
st.put("note", *post.note);
if (post.metadata)
put_metadata(st, *post.metadata);
put_metadata(st.put("metadata", ""), *post.metadata);
if (post.xdata_ && ! post.xdata_->total.is_null()) {
property_tree::ptree& t(st.put("total", ""));
put_value(t, post.xdata_->total);
}
if (post.xdata_ && ! post.xdata_->total.is_null())
put_value(st.put("total", ""), post.xdata_->total);
}
} // namespace ledger

View file

@ -61,28 +61,28 @@ void format_ptree::flush()
property_tree::ptree& ct(pt.put("ledger.commodities", ""));
foreach (const commodities_pair& pair, commodities)
put_commodity(ct, *pair.second, true);
put_commodity(ct.add("commodity", ""), *pair.second, true);
property_tree::ptree& at(pt.put("ledger.accounts", ""));
put_account(at, *report.session.journal->master, account_visited_p);
put_account(at.add("account", ""), *report.session.journal->master, account_visited_p);
property_tree::ptree& tt(pt.put("ledger.transactions", ""));
foreach (const xact_t * xact, transactions) {
put_xact(tt, *xact);
property_tree::ptree& t(tt.add("transaction", ""));
put_xact(t, *xact);
property_tree::ptree& post_tree(tt.put("postings", ""));
property_tree::ptree& post_tree(t.put("postings", ""));
foreach (const post_t * post, xact->posts)
if (post->has_xdata() &&
post->xdata().has_flags(POST_EXT_VISITED))
put_post(post_tree, *post);
put_post(post_tree.add("posting", ""), *post);
}
switch (format) {
case FORMAT_XML:
property_tree::write_xml(out, pt);
break;
case FORMAT_JSON:
property_tree::write_json(out, pt);
property_tree::xml_writer_settings<char> indented(' ', 2);
property_tree::write_xml(out, pt, indented);
out << std::endl;
break;
}
}

View file

@ -75,8 +75,7 @@ protected:
public:
enum format_t {
FORMAT_XML,
FORMAT_JSON
FORMAT_XML
} format;
format_ptree(report_t& _report, format_t _format = FORMAT_XML)

View file

@ -1628,11 +1628,6 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
}
break;
case 'j':
if (is_eq(p, "json"))
return POSTS_REPORTER(new format_ptree(*this,
format_ptree::FORMAT_JSON));
break;
case 'l':
if (is_eq(p, "lisp"))
return POSTS_REPORTER(new format_emacs_posts(output_stream));

View file

@ -204,7 +204,6 @@ typedef std::ostream::pos_type ostream_pos_type;
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>

View file

@ -111,20 +111,12 @@ std::string format_date(const date_t& when,
void set_date_format(const char * format);
void set_input_date_format(const char * format);
inline void put_datetime(property_tree::ptree& pt, const datetime_t& when,
bool wrap = true) {
if (wrap)
pt.put("datetime", format_datetime(when, FMT_WRITTEN));
else
pt.put_value(format_datetime(when, FMT_WRITTEN));
inline void put_datetime(property_tree::ptree& pt, const datetime_t& when) {
pt.put_value(format_datetime(when, FMT_WRITTEN));
}
inline void put_date(property_tree::ptree& pt, const date_t& when,
bool wrap = true) {
if (wrap)
pt.put("date", format_date(when, FMT_WRITTEN));
else
pt.put_value(format_date(when, FMT_WRITTEN));
inline void put_date(property_tree::ptree& pt, const date_t& when) {
pt.put_value(format_date(when, FMT_WRITTEN));
}
struct date_traits_t

View file

@ -2062,35 +2062,35 @@ void put_value(property_tree::ptree& pt, const value_t& value)
{
switch (value.type()) {
case value_t::VOID:
pt.put("void", "");
pt.add("void", "");
break;
case value_t::BOOLEAN:
pt.put("bool", value.as_boolean() ? "true" : "false");
pt.add("bool", value.as_boolean() ? "true" : "false");
break;
case value_t::INTEGER:
pt.put("int", value.to_string());
pt.add("int", value.to_string());
break;
case value_t::AMOUNT:
put_amount(pt, value.as_amount());
put_amount(pt.add("amount", ""), value.as_amount());
break;
case value_t::BALANCE:
put_balance(pt, value.as_balance());
put_balance(pt.add("balance", ""), value.as_balance());
break;
case value_t::DATETIME:
put_datetime(pt, value.as_datetime());
put_datetime(pt.add("datetime", ""), value.as_datetime());
break;
case value_t::DATE:
put_date(pt, value.as_date());
put_date(pt.add("date", ""), value.as_date());
break;
case value_t::STRING:
pt.put("string", value.as_string());
pt.add("string", value.as_string());
break;
case value_t::MASK:
put_mask(pt, value.as_mask());
put_mask(pt.add("mask", ""), value.as_mask());
break;
case value_t::SEQUENCE: {
property_tree::ptree& st(pt.put("sequence", ""));
property_tree::ptree& st(pt.add("sequence", ""));
foreach (const value_t& member, value.as_sequence())
put_value(st, member);
break;

View file

@ -847,10 +847,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context)
}
}
void put_xact(property_tree::ptree& pt, const xact_t& xact)
void put_xact(property_tree::ptree& st, const xact_t& xact)
{
property_tree::ptree& st(pt.put("transaction", ""));
if (xact.state() == item_t::CLEARED)
st.put("<xmlattr>.state", "cleared");
else if (xact.state() == item_t::PENDING)
@ -859,14 +857,10 @@ void put_xact(property_tree::ptree& pt, const xact_t& xact)
if (xact.has_flags(ITEM_GENERATED))
st.put("<xmlattr>.generated", "true");
if (xact._date) {
property_tree::ptree& t(st.put("date", ""));
put_date(t, *xact._date, false);
}
if (xact._date_aux) {
property_tree::ptree& t(st.put("aux-date", ""));
put_date(t, *xact._date_aux, false);
}
if (xact._date)
put_date(st.put("date", ""), *xact._date);
if (xact._date_aux)
put_date(st.put("aux-date", ""), *xact._date_aux);
if (xact.code)
st.put("code", *xact.code);
@ -877,7 +871,7 @@ void put_xact(property_tree::ptree& pt, const xact_t& xact)
st.put("note", *xact.note);
if (xact.metadata)
put_metadata(st, *xact.metadata);
put_metadata(st.put("metadata", ""), *xact.metadata);
}
} // namespace ledger