diff options
Diffstat (limited to 'lisp/ledger-report.el')
-rw-r--r-- | lisp/ledger-report.el | 297 |
1 files changed, 170 insertions, 127 deletions
diff --git a/lisp/ledger-report.el b/lisp/ledger-report.el index e785bc1b..c477707f 100644 --- a/lisp/ledger-report.el +++ b/lisp/ledger-report.el @@ -1,6 +1,6 @@ ;;; ledger-report.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2013 John Wiegley (johnw AT gnu DOT org) +;; Copyright (C) 2003-2015 John Wiegley (johnw AT gnu DOT org) ;; This file is not part of GNU Emacs. @@ -16,8 +16,8 @@ ;; ;; 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. +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301 USA. ;;; Commentary: @@ -25,6 +25,7 @@ ;;; Code: +(require 'easymenu) (eval-when-compile (require 'cl)) @@ -49,14 +50,15 @@ the substitution. See the documentation of the individual functions in that variable for more information on the behavior of each specifier." :type '(repeat (list (string :tag "Report Name") - (string :tag "Command Line"))) + (string :tag "Command Line"))) :group 'ledger-report) (defcustom ledger-report-format-specifiers '(("ledger-file" . ledger-report-ledger-file-format-specifier) ("payee" . ledger-report-payee-format-specifier) ("account" . ledger-report-account-format-specifier) - ("value" . ledger-report-value-format-specifier)) + ("tagname" . ledger-report-tagname-format-specifier) + ("tagvalue" . ledger-report-tagvalue-format-specifier)) "An alist mapping ledger report format specifiers to implementing functions. The function is called with no parameters and expected to return the @@ -64,6 +66,16 @@ text that should replace the format specifier." :type 'alist :group 'ledger-report) +(defcustom ledger-report-auto-refresh t + "If t then automatically rerun the report when the ledger buffer is saved." + :type 'boolean + :group 'ledger-report) + +(defcustom ledger-report-auto-refresh-sticky-cursor nil + "If t then try to place cursor at same relative position as it was before auto-refresh." + :type 'boolean + :group 'ledger-report) + (defvar ledger-report-buffer-name "*Ledger Report*") (defvar ledger-report-name nil) @@ -75,8 +87,16 @@ text that should replace the format specifier." (defvar ledger-minibuffer-history nil) (defvar ledger-report-mode-abbrev-table) +(defvar ledger-report-is-reversed nil) +(defvar ledger-report-cursor-line-number nil) + +(defun ledger-report-reverse-report () + "Reverse the order of the report." + (interactive) + (ledger-report-reverse-lines) + (setq ledger-report-is-reversed (not ledger-report-is-reversed))) + (defun ledger-report-reverse-lines () - (interactive) (goto-char (point-min)) (forward-paragraph) (forward-line) @@ -84,51 +104,61 @@ text that should replace the format specifier." (setq inhibit-read-only t) (reverse-region (point) (point-max)))) +(defvar ledger-report-mode-map + (let ((map (make-sparse-keymap))) + (define-key map [? ] 'scroll-up) + (define-key map [backspace] 'scroll-down) + (define-key map [?r] 'ledger-report-redo) + (define-key map [(shift ?r)] 'ledger-report-reverse-report) + (define-key map [?s] 'ledger-report-save) + (define-key map [?k] 'ledger-report-kill) + (define-key map [?e] 'ledger-report-edit-report) + (define-key map [( shift ?e)] 'ledger-report-edit-reports) + (define-key map [?q] 'ledger-report-quit) + (define-key map [?g] 'ledger-report-redo) + (define-key map [(control ?c) (control ?l) (control ?r)] + 'ledger-report-redo) + (define-key map [(control ?c) (control ?l) (control ?S)] + 'ledger-report-save) + (define-key map [(control ?c) (control ?l) (control ?k)] + 'ledger-report-kill) + (define-key map [(control ?c) (control ?l) (control ?e)] + 'ledger-report-edit) + (define-key map [return] 'ledger-report-visit-source) + map) + "Keymap for `ledger-report-mode'.") + +(easy-menu-define ledger-report-mode-menu ledger-report-mode-map + "Ledger report menu" + '("Reports" + ["Save Report" ledger-report-save] + ["Edit Current Report" ledger-report-edit-report] + ["Edit All Reports" ledger-report-edit-reports] + ["Re-run Report" ledger-report-redo] + "---" + ["Reverse report order" ledger-report-reverse-report] + "---" + ["Scroll Up" scroll-up] + ["Visit Source" ledger-report-visit-source] + ["Scroll Down" scroll-down] + "---" + ["Quit" ledger-report-quit] + )) + (define-derived-mode ledger-report-mode text-mode "Ledger-Report" - "A mode for viewing ledger reports." - (let ((map (make-sparse-keymap))) - (define-key map [? ] 'scroll-up) - (define-key map [backspace] 'scroll-down) - (define-key map [?r] 'ledger-report-redo) - (define-key map [(shift ?r)] 'ledger-report-reverse-lines) - (define-key map [?s] 'ledger-report-save) - (define-key map [?k] 'ledger-report-kill) - (define-key map [?e] 'ledger-report-edit) - (define-key map [?q] 'ledger-report-quit) - (define-key map [(control ?c) (control ?l) (control ?r)] - 'ledger-report-redo) - (define-key map [(control ?c) (control ?l) (control ?S)] - 'ledger-report-save) - (define-key map [(control ?c) (control ?l) (control ?k)] - 'ledger-report-kill) - (define-key map [(control ?c) (control ?l) (control ?e)] - 'ledger-report-edit) - (define-key map [return] 'ledger-report-visit-source) - - - (define-key map [menu-bar] (make-sparse-keymap "ledger-rep")) - (define-key map [menu-bar ledger-rep] (cons "Reports" map)) - - (define-key map [menu-bar ledger-rep lrq] '("Quit" . ledger-report-quit)) - (define-key map [menu-bar ledger-rep s2] '("--")) - (define-key map [menu-bar ledger-rep lrd] '("Scroll Down" . scroll-down)) - (define-key map [menu-bar ledger-rep vis] '("Visit Source" . ledger-report-visit-source)) - (define-key map [menu-bar ledger-rep lru] '("Scroll Up" . scroll-up)) - (define-key map [menu-bar ledger-rep s1] '("--")) - (define-key map [menu-bar ledger-rep rev] '("Reverse report order" . ledger-report-reverse-lines)) - (define-key map [menu-bar ledger-rep s0] '("--")) - (define-key map [menu-bar ledger-rep lrk] '("Kill Report" . ledger-report-kill)) - (define-key map [menu-bar ledger-rep lrr] '("Re-run Report" . ledger-report-redo)) - (define-key map [menu-bar ledger-rep lre] '("Edit Report" . ledger-report-edit)) - (define-key map [menu-bar ledger-rep lrs] '("Save Report" . ledger-report-save)) - - (use-local-map map))) - -(defun ledger-report-value-format-specifier () + "A mode for viewing ledger reports.") + +(defun ledger-report-tagname-format-specifier () "Return a valid meta-data tag name" ;; It is intended completion should be available on existing account ;; names, but it remains to be implemented. - (ledger-read-string-with-default "Value: " nil)) + (ledger-read-string-with-default "Tag Name: " nil)) + +(defun ledger-report-tagvalue-format-specifier () + "Return a valid meta-data tag name" + ;; It is intended completion should be available on existing account + ;; names, but it remains to be implemented. + (ledger-read-string-with-default "Tag Value: " nil)) (defun ledger-report-read-name () "Read the name of a ledger report to use, with completion. @@ -173,13 +203,14 @@ used to generate the buffer, navigating the buffer, etc." (set (make-local-variable 'ledger-buf) buf) (set (make-local-variable 'ledger-report-name) report-name) (set (make-local-variable 'ledger-original-window-cfg) wcfg) + (set (make-local-variable 'ledger-report-is-reversed) nil) (ledger-do-report (ledger-report-cmd report-name edit)) (shrink-window-if-larger-than-buffer) (set-buffer-modified-p nil) (setq buffer-read-only t) (message "q to quit; r to redo; e to edit; k to kill; s to save; SPC and DEL to scroll")))) -(defun string-empty-p (s) +(defun ledger-report-string-empty-p (s) "Check S for the empty string." (string-equal "" s)) @@ -188,7 +219,7 @@ used to generate the buffer, navigating the buffer, etc." If name exists, returns the object naming the report, otherwise returns nil." - (unless (string-empty-p name) + (unless (ledger-report-string-empty-p name) (car (assoc name ledger-reports)))) (defun ledger-reports-add (name cmd) @@ -227,7 +258,7 @@ used to generate the buffer, navigating the buffer, etc." end of a ledger file which is included in some other file." (if ledger-master-file (expand-file-name ledger-master-file) - (buffer-file-name))) + (buffer-file-name))) (defun ledger-report-payee-format-specifier () "Substitute a payee name. @@ -257,16 +288,16 @@ used to generate the buffer, navigating the buffer, etc." (let ((expanded-cmd report-cmd)) (set-match-data (list 0 0)) (while (string-match "%(\\([^)]*\\))" expanded-cmd (if (> (length expanded-cmd) (match-end 0)) - (match-end 0) - (1- (length expanded-cmd)))) - (let* ((specifier (match-string 1 expanded-cmd)) - (f (cdr (assoc specifier ledger-report-format-specifiers)))) - (if f - (setq expanded-cmd (replace-match - (save-match-data - (with-current-buffer ledger-buf - (shell-quote-argument (funcall f)))) - t t expanded-cmd))))) + (match-end 0) + (1- (length expanded-cmd)))) + (let* ((specifier (match-string 1 expanded-cmd)) + (f (cdr (assoc specifier ledger-report-format-specifiers)))) + (if f + (setq expanded-cmd (replace-match + (save-match-data + (with-current-buffer ledger-buf + (shell-quote-argument (funcall f)))) + t t expanded-cmd))))) expanded-cmd))) (defun ledger-report-cmd (report-name edit) @@ -279,11 +310,11 @@ Optional EDIT the command." (setq ledger-report-saved nil)) ;; this is a new report, or edited report (setq report-cmd (ledger-report-expand-format-specifiers report-cmd)) (set (make-local-variable 'ledger-report-cmd) report-cmd) - (or (string-empty-p report-name) + (or (ledger-report-string-empty-p report-name) (ledger-report-name-exists report-name) (progn - (ledger-reports-add report-name report-cmd) - (ledger-reports-custom-save))) + (ledger-reports-add report-name report-cmd) + (ledger-reports-custom-save))) report-cmd)) (defun ledger-do-report (cmd) @@ -295,32 +326,32 @@ Optional EDIT the command." "\n\n") (let ((data-pos (point)) (register-report (string-match " reg\\(ister\\)? " cmd)) - files-in-report) + files-in-report) (shell-command ;; --subtotal does not produce identifiable transactions, so don't ;; prepend location information for them (if (and register-report - (not (string-match "--subtotal" cmd))) - (concat cmd " --prepend-format='%(filename):%(beg_line):'") - cmd) + (not (string-match "--subtotal" cmd))) + (concat cmd " --prepend-format='%(filename):%(beg_line):'") + cmd) t nil) (when register-report (goto-char data-pos) (while (re-search-forward "^\\(/[^:]+\\)?:\\([0-9]+\\)?:" nil t) - (let ((file (match-string 1)) - (line (string-to-number (match-string 2)))) - (delete-region (match-beginning 0) (match-end 0)) - (when file - (set-text-properties (line-beginning-position) (line-end-position) - (list 'ledger-source (cons file (save-window-excursion - (save-excursion - (find-file file) - (widen) - (ledger-goto-line line) - (point-marker)))))) - (add-text-properties (line-beginning-position) (line-end-position) - (list 'face 'ledger-font-report-clickable-face)) - (end-of-line))))) + (let ((file (match-string 1)) + (line (string-to-number (match-string 2)))) + (delete-region (match-beginning 0) (match-end 0)) + (when file + (set-text-properties (line-beginning-position) (line-end-position) + (list 'ledger-source (cons file (save-window-excursion + (save-excursion + (find-file file) + (widen) + (ledger-navigate-to-line line) + (point-marker)))))) + (add-text-properties (line-beginning-position) (line-end-position) + (list 'face 'ledger-font-report-clickable-face)) + (end-of-line))))) (goto-char data-pos))) @@ -328,21 +359,21 @@ Optional EDIT the command." "Visit the transaction under point in the report window." (interactive) (let* ((prop (get-text-property (point) 'ledger-source)) - (file (if prop (car prop))) - (line-or-marker (if prop (cdr prop)))) + (file (if prop (car prop))) + (line-or-marker (if prop (cdr prop)))) (when (and file line-or-marker) (find-file-other-window file) (widen) (if (markerp line-or-marker) - (goto-char line-or-marker) - (goto-char (point-min)) - (forward-line (1- line-or-marker)) - (re-search-backward "^[0-9]+") - (beginning-of-line) - (let ((start-of-txn (point))) - (forward-paragraph) - (narrow-to-region start-of-txn (point)) - (backward-paragraph)))))) + (goto-char line-or-marker) + (goto-char (point-min)) + (forward-line (1- line-or-marker)) + (re-search-backward "^[0-9]+") + (beginning-of-line) + (let ((start-of-txn (point))) + (forward-paragraph) + (narrow-to-region start-of-txn (point)) + (backward-paragraph)))))) (defun ledger-report-goto () "Goto the ledger report buffer." @@ -356,34 +387,46 @@ Optional EDIT the command." (defun ledger-report-redo () "Redo the report in the current ledger report buffer." (interactive) - (ledger-report-goto) - (setq buffer-read-only nil) - (erase-buffer) - (ledger-do-report ledger-report-cmd) - (setq buffer-read-only nil)) + (let ((cur-buf (current-buffer))) + (if (and ledger-report-auto-refresh + (or (string= (format-mode-line 'mode-name) "Ledger") + (string= (format-mode-line 'mode-name) "Ledger-Report")) + (get-buffer ledger-report-buffer-name)) + (progn + + (pop-to-buffer (get-buffer ledger-report-buffer-name)) + (shrink-window-if-larger-than-buffer) + (setq buffer-read-only nil) + (setq ledger-report-cursor-line-number (line-number-at-pos)) + (erase-buffer) + (ledger-do-report ledger-report-cmd) + (setq buffer-read-only nil) + (if ledger-report-is-reversed (ledger-report-reverse-lines)) + (if ledger-report-auto-refresh-sticky-cursor (forward-line (- ledger-report-cursor-line-number 5))) + (pop-to-buffer cur-buf))))) (defun ledger-report-quit () - "Quit the ledger report buffer by burying it." - (interactive) - (ledger-report-goto) - (set-window-configuration ledger-original-window-cfg) - (bury-buffer (get-buffer ledger-report-buffer-name))) - -(defun ledger-report-kill () - "Kill the ledger report buffer." - (interactive) - (ledger-report-quit) - (kill-buffer (get-buffer ledger-report-buffer-name))) + "Quit the ledger report buffer." + (interactive) + (ledger-report-goto) + (set-window-configuration ledger-original-window-cfg) + (kill-buffer (get-buffer ledger-report-buffer-name))) -(defun ledger-report-edit () +(defun ledger-report-edit-reports () "Edit the defined ledger reports." (interactive) (customize-variable 'ledger-reports)) +(defun ledger-report-edit-report () + (interactive) + "Edit the current report command in the mini buffer and re-run the report" + (setq ledger-report-cmd (ledger-report-read-command ledger-report-cmd)) + (ledger-report-redo)) + (defun ledger-report-read-new-name () "Read the name for a new report from the minibuffer." (let ((name "")) - (while (string-empty-p name) + (while (ledger-report-string-empty-p name) (setq name (read-from-minibuffer "Report name: " nil nil nil 'ledger-report-name-prompt-history))) name)) @@ -393,26 +436,26 @@ Optional EDIT the command." (interactive) (ledger-report-goto) (let (existing-name) - (when (string-empty-p ledger-report-name) + (when (ledger-report-string-empty-p ledger-report-name) (setq ledger-report-name (ledger-report-read-new-name))) (if (setq existing-name (ledger-report-name-exists ledger-report-name)) - (cond ((y-or-n-p (format "Overwrite existing report named '%s'? " - ledger-report-name)) - (if (string-equal - ledger-report-cmd - (car (cdr (assq existing-name ledger-reports)))) - (message "Nothing to save. Current command is identical to existing saved one") - (progn - (setq ledger-reports - (assq-delete-all existing-name ledger-reports)) - (ledger-reports-add ledger-report-name ledger-report-cmd) - (ledger-reports-custom-save)))) - (t - (progn - (setq ledger-report-name (ledger-report-read-new-name)) - (ledger-reports-add ledger-report-name ledger-report-cmd) - (ledger-reports-custom-save))))))) + (cond ((y-or-n-p (format "Overwrite existing report named '%s'? " + ledger-report-name)) + (if (string-equal + ledger-report-cmd + (car (cdr (assq existing-name ledger-reports)))) + (message "Nothing to save. Current command is identical to existing saved one") + (progn + (setq ledger-reports + (assq-delete-all existing-name ledger-reports)) + (ledger-reports-add ledger-report-name ledger-report-cmd) + (ledger-reports-custom-save)))) + (t + (progn + (setq ledger-report-name (ledger-report-read-new-name)) + (ledger-reports-add ledger-report-name ledger-report-cmd) + (ledger-reports-custom-save))))))) (provide 'ledger-report) |