diff options
Diffstat (limited to 'lisp/ledger-mode.el')
-rw-r--r-- | lisp/ledger-mode.el | 407 |
1 files changed, 242 insertions, 165 deletions
diff --git a/lisp/ledger-mode.el b/lisp/ledger-mode.el index b604581c..4e2beff6 100644 --- a/lisp/ledger-mode.el +++ b/lisp/ledger-mode.el @@ -1,6 +1,6 @@ ;;; ledger-mode.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. @@ -27,14 +27,18 @@ ;;; Code: (require 'ledger-regex) +(require 'cus-edit) (require 'esh-util) (require 'esh-arg) +(require 'easymenu) (require 'ledger-commodities) (require 'ledger-complete) (require 'ledger-context) (require 'ledger-exec) (require 'ledger-fonts) +(require 'ledger-fontify) (require 'ledger-init) +(require 'ledger-navigate) (require 'ledger-occur) (require 'ledger-post) (require 'ledger-reconcile) @@ -58,29 +62,32 @@ (defconst ledger-mode-version "3.0.0") (defun ledger-mode-dump-variable (var) - (if var - (insert (format " %s: %S\n" (symbol-name var) (eval var))))) + "Format VAR for dump to buffer." + (if var + (insert (format " %s: %S\n" (symbol-name var) (eval var))))) (defun ledger-mode-dump-group (group) - "Dump GROUP customizations to current buffer" + "Dump GROUP customizations to current buffer." (let ((members (custom-group-members group nil))) (dolist (member members) (cond ((eq (cadr member) 'custom-group) - (insert (format "Group %s:\n" (symbol-name (car member)))) - (ledger-mode-dump-group (car member))) - ((eq (cadr member) 'custom-variable) - (ledger-mode-dump-variable (car member))))))) + (insert (format "Group %s:\n" (symbol-name (car member)))) + (ledger-mode-dump-group (car member))) + ((eq (cadr member) 'custom-variable) + (ledger-mode-dump-variable (car member))))))) (defun ledger-mode-dump-configuration () - "Dump all customizations" + "Dump all customizations." + (interactive) (find-file "ledger-mode-dump") (ledger-mode-dump-group 'ledger)) -(defsubst ledger-current-year () +(defun ledger-current-year () "The default current year for adding transactions." (format-time-string "%Y")) -(defsubst ledger-current-month () + +(defun ledger-current-month () "The default current month for adding transactions." (format-time-string "%m")) @@ -91,32 +98,44 @@ "Start a ledger session with the current month, but make it customizable to ease retro-entry.") (defun ledger-read-account-with-prompt (prompt) - (let* ((context (ledger-context-at-point)) - (default (if (and (eq (ledger-context-line-type context) 'acct-transaction) - (eq (ledger-context-current-field context) 'account)) - (regexp-quote (ledger-context-field-value context 'account)) - nil))) - (ledger-read-string-with-default prompt default))) + "Read an account from the minibuffer with PROMPT." + (let ((context (ledger-context-at-point))) + (ledger-read-string-with-default prompt + (if (eq (ledger-context-current-field context) 'account) + (regexp-quote (ledger-context-field-value context 'account)) + nil)))) + +(defun ledger-read-date (prompt) + "Return user-supplied date after `PROMPT', defaults to today." + (let* ((default (ledger-year-and-month)) + (date (read-string prompt default + 'ledger-minibuffer-history))) + (if (or (string= date default) + (string= "" date)) + (format-time-string + (or (cdr (assoc "date-format" ledger-environment-alist)) + ledger-default-date-format)) + date))) (defun ledger-read-string-with-default (prompt default) "Return user supplied string after PROMPT, or DEFAULT." (read-string (concat prompt - (if default - (concat " (" default "): ") - ": ")) - nil 'ledger-minibuffer-history default)) + (if default + (concat " (" default "): ") + ": ")) + nil 'ledger-minibuffer-history default)) (defun ledger-display-balance-at-point () "Display the cleared-or-pending balance. And calculate the target-delta of the account being reconciled." (interactive) (let* ((account (ledger-read-account-with-prompt "Account balance to show")) - (buffer (current-buffer)) - (balance (with-temp-buffer - (ledger-exec-ledger buffer (current-buffer) "cleared" account) - (if (> (buffer-size) 0) - (buffer-substring-no-properties (point-min) (1- (point-max))) - (concat account " is empty."))))) + (buffer (current-buffer)) + (balance (with-temp-buffer + (ledger-exec-ledger buffer (current-buffer) "cleared" account) + (if (> (buffer-size) 0) + (buffer-substring-no-properties (point-min) (1- (point-max))) + (concat account " is empty."))))) (when balance (message balance)))) @@ -125,171 +144,229 @@ And calculate the target-delta of the account being reconciled." And calculate the target-delta of the account being reconciled." (interactive) (let* ((buffer (current-buffer)) - (balance (with-temp-buffer - (ledger-exec-ledger buffer (current-buffer) "stats") - (buffer-substring-no-properties (point-min) (1- (point-max)))))) + (balance (with-temp-buffer + (ledger-exec-ledger buffer (current-buffer) "stats") + (buffer-substring-no-properties (point-min) (1- (point-max)))))) (when balance (message balance)))) (defun ledger-magic-tab (&optional interactively) - "Decide what to with with <TAB>. + "Decide what to with with <TAB>, INTERACTIVELY. Can indent, complete or align depending on context." (interactive "p") - (if (= (point) (line-beginning-position)) - (indent-to ledger-post-account-alignment-column) - (if (and (> (point) 1) - (looking-back "\\([^ \t]\\)" 1)) - (ledger-pcomplete interactively) - (ledger-post-align-postings)))) + (if (= (point) (line-beginning-position)) + (indent-to ledger-post-account-alignment-column) + (if (and (> (point) 1) + (looking-back "\\([^ \t]\\)" 1)) + (ledger-pcomplete interactively) + (ledger-post-align-postings)))) (defvar ledger-mode-abbrev-table) -(defun ledger-insert-effective-date () +(defvar ledger-date-string-today + (format-time-string (or + (cdr (assoc "date-format" ledger-environment-alist)) + ledger-default-date-format))) + +(defun ledger-remove-effective-date () + "Remove the effective date from a transaction or posting." (interactive) - (let ((context (car (ledger-context-at-point))) - (date-string (format-time-string (cdr (assoc "date-format" ledger-environment-alist))))) - (cond ((eq 'xact context) - (beginning-of-line) - (insert date-string "=")) - ((eq 'acct-transaction context) - (end-of-line) - (insert " ; [=" date-string "]"))))) + (let ((context (car (ledger-context-at-point)))) + (save-excursion + (save-restriction + (narrow-to-region (point-at-bol) (point-at-eol)) + (beginning-of-line) + (cond ((eq 'xact context) + (re-search-forward ledger-iso-date-regexp) + (when (= (char-after) ?=) + (let ((eq-pos (point))) + (delete-region + eq-pos + (re-search-forward ledger-iso-date-regexp))))) + ((eq 'acct-transaction context) + ;; Match "; [=date]" & delete string + (when (re-search-forward + (concat ledger-comment-regex + "\\[=" ledger-iso-date-regexp "\\]") + nil 'noerr) + (replace-match "")))))))) + +(defun ledger-insert-effective-date (&optional date) + "Insert effective date `DATE' to the transaction or posting. + +If `DATE' is nil, prompt the user a date. + +Replace the current effective date if there's one in the same +line. + +With a prefix argument, remove the effective date." + (interactive) + (if (and (listp current-prefix-arg) + (= 4 (prefix-numeric-value current-prefix-arg))) + (ledger-remove-effective-date) + (let* ((context (car (ledger-context-at-point))) + (date-string (or date (ledger-read-date "Effective date: ")))) + (save-restriction + (narrow-to-region (point-at-bol) (point-at-eol)) + (cond + ((eq 'xact context) + (beginning-of-line) + (re-search-forward ledger-iso-date-regexp) + (when (= (char-after) ?=) + (ledger-remove-effective-date)) + (insert "=" date-string)) + ((eq 'acct-transaction context) + (end-of-line) + (ledger-remove-effective-date) + (insert " ; [=" date-string "]"))))))) (defun ledger-mode-remove-extra-lines () + "Get rid of multiple empty lines." (goto-char (point-min)) - (while (re-search-forward "\n\n\\(\n\\)+" nil t) - (replace-match "\n\n"))) + (while (re-search-forward "\n\n\\(\n\\)+" nil t) + (replace-match "\n\n"))) (defun ledger-mode-clean-buffer () - "indent, remove multiple linfe feeds and sort the buffer" - (interactive) - (ledger-sort-buffer) - (ledger-post-align-postings (point-min) (point-max)) - (ledger-mode-remove-extra-lines)) - + "Indent, remove multiple line feeds and sort the buffer." + (interactive) + (let ((start (point-min-marker)) + (end (point-max-marker))) + (goto-char start) + (ledger-navigate-beginning-of-xact) + (beginning-of-line) + (let ((target (buffer-substring (point) (progn + (end-of-line) + (point))))) + (untabify start end) + (ledger-sort-buffer) + (ledger-post-align-postings start end) + (ledger-mode-remove-extra-lines) + (goto-char start) + (search-forward target)))) + +(defvar ledger-mode-syntax-table + (let ((table (make-syntax-table text-mode-syntax-table))) + (modify-syntax-entry ?\; "<" table) + (modify-syntax-entry ?\n ">" table) + table) + "Syntax table in use in `ledger-mode' buffers.") + +(defvar ledger-mode-map + (let ((map (make-sparse-keymap))) + (define-key map [(control ?c) (control ?a)] 'ledger-add-transaction) + (define-key map [(control ?c) (control ?b)] 'ledger-post-edit-amount) + (define-key map [(control ?c) (control ?c)] 'ledger-toggle-current) + (define-key map [(control ?c) (control ?d)] 'ledger-delete-current-transaction) + (define-key map [(control ?c) (control ?e)] 'ledger-toggle-current-transaction) + (define-key map [(control ?c) (control ?f)] 'ledger-occur) + (define-key map [(control ?c) (control ?k)] 'ledger-copy-transaction-at-point) + (define-key map [(control ?c) (control ?m)] 'ledger-set-month) + (define-key map [(control ?c) (control ?r)] 'ledger-reconcile) + (define-key map [(control ?c) (control ?s)] 'ledger-sort-region) + (define-key map [(control ?c) (control ?t)] 'ledger-insert-effective-date) + (define-key map [(control ?c) (control ?u)] 'ledger-schedule-upcoming) + (define-key map [(control ?c) (control ?y)] 'ledger-set-year) + (define-key map [(control ?c) (control ?p)] 'ledger-display-balance-at-point) + (define-key map [(control ?c) (control ?l)] 'ledger-display-ledger-stats) + (define-key map [(control ?c) (control ?q)] 'ledger-post-align-xact) + + (define-key map [tab] 'ledger-magic-tab) + (define-key map [(control tab)] 'ledger-post-align-xact) + (define-key map [(control ?i)] 'ledger-magic-tab) + (define-key map [(control ?c) tab] 'ledger-fully-complete-xact) + (define-key map [(control ?c) (control ?i)] 'ledger-fully-complete-xact) + + (define-key map [(control ?c) (control ?o) (control ?a)] 'ledger-report-redo) + (define-key map [(control ?c) (control ?o) (control ?e)] 'ledger-report-edit) + (define-key map [(control ?c) (control ?o) (control ?g)] 'ledger-report-goto) + (define-key map [(control ?c) (control ?o) (control ?k)] 'ledger-report-kill) + (define-key map [(control ?c) (control ?o) (control ?r)] 'ledger-report) + (define-key map [(control ?c) (control ?o) (control ?s)] 'ledger-report-save) + + (define-key map [(meta ?p)] 'ledger-navigate-prev-xact-or-directive) + (define-key map [(meta ?n)] 'ledger-navigate-next-xact-or-directive) + map) + "Keymap for `ledger-mode'.") + +(easy-menu-define ledger-mode-menu ledger-mode-map + "Ledger menu" + '("Ledger" + ["Narrow to REGEX" ledger-occur] + ["Show all transactions" ledger-occur-mode ledger-occur-mode] + ["Ledger Statistics" ledger-display-ledger-stats ledger-works] + "---" + ["Show upcoming transactions" ledger-schedule-upcoming] + ["Add Transaction (ledger xact)" ledger-add-transaction ledger-works] + ["Complete Transaction" ledger-fully-complete-xact] + ["Delete Transaction" ledger-delete-current-transaction] + "---" + ["Calc on Amount" ledger-post-edit-amount] + "---" + ["Check Balance" ledger-display-balance-at-point ledger-works] + ["Reconcile Account" ledger-reconcile ledger-works] + "---" + ["Toggle Current Transaction" ledger-toggle-current-transaction] + ["Toggle Current Posting" ledger-toggle-current] + ["Copy Trans at Point" ledger-copy-transaction-at-point] + "---" + ["Clean-up Buffer" ledger-mode-clean-buffer] + ["Align Region" ledger-post-align-postings mark-active] + ["Align Xact" ledger-post-align-xact] + ["Sort Region" ledger-sort-region mark-active] + ["Sort Buffer" ledger-sort-buffer] + ["Mark Sort Beginning" ledger-sort-insert-start-mark] + ["Mark Sort End" ledger-sort-insert-end-mark] + ["Set effective date" ledger-insert-effective-date] + "---" + ["Customize Ledger Mode" (lambda () (interactive) (customize-group 'ledger))] + ["Set Year" ledger-set-year ledger-works] + ["Set Month" ledger-set-month ledger-works] + "---" + ["Run Report" ledger-report ledger-works] + ["Goto Report" ledger-report-goto ledger-works] + ["Re-run Report" ledger-report-redo ledger-works] + ["Save Report" ledger-report-save ledger-works] + ["Edit Report" ledger-report-edit ledger-works] + ["Kill Report" ledger-report-kill ledger-works])) ;;;###autoload (define-derived-mode ledger-mode text-mode "Ledger" - "A mode for editing ledger data files." - (ledger-check-version) - (ledger-post-setup) - - (set (make-local-variable 'comment-start) "; ") - (set (make-local-variable 'comment-end) "") - (set (make-local-variable 'indent-tabs-mode) nil) - - (if (boundp 'font-lock-defaults) - (set (make-local-variable 'font-lock-defaults) - '(ledger-font-lock-keywords nil t))) - (setq font-lock-extend-region-functions - (list #'font-lock-extend-region-wholelines)) - (setq font-lock-multiline nil) - - (set (make-local-variable 'pcomplete-parse-arguments-function) - 'ledger-parse-arguments) - (set (make-local-variable 'pcomplete-command-completion-function) - 'ledger-complete-at-point) - (set (make-local-variable 'pcomplete-termination-string) "") - - (add-hook 'post-command-hook 'ledger-highlight-xact-under-point nil t) - (add-hook 'before-revert-hook 'ledger-occur-remove-all-overlays nil t) - (make-variable-buffer-local 'highlight-overlay) - - (ledger-init-load-init-file) - - (set (make-local-variable 'indent-region-function) 'ledger-post-align-postings) - - (let ((map (current-local-map))) - (define-key map [(control ?c) (control ?a)] 'ledger-add-transaction) - (define-key map [(control ?c) (control ?b)] 'ledger-post-edit-amount) - (define-key map [(control ?c) (control ?c)] 'ledger-toggle-current) - (define-key map [(control ?c) (control ?d)] 'ledger-delete-current-transaction) - (define-key map [(control ?c) (control ?e)] 'ledger-toggle-current-transaction) - (define-key map [(control ?c) (control ?f)] 'ledger-occur) - (define-key map [(control ?c) (control ?k)] 'ledger-copy-transaction-at-point) - (define-key map [(control ?c) (control ?m)] 'ledger-set-month) - (define-key map [(control ?c) (control ?r)] 'ledger-reconcile) - (define-key map [(control ?c) (control ?s)] 'ledger-sort-region) - (define-key map [(control ?c) (control ?t)] 'ledger-insert-effective-date) - (define-key map [(control ?c) (control ?u)] 'ledger-schedule-upcoming) - (define-key map [(control ?c) (control ?y)] 'ledger-set-year) - (define-key map [(control ?c) (control ?p)] 'ledger-display-balance-at-point) - (define-key map [(control ?c) (control ?l)] 'ledger-display-ledger-stats) - (define-key map [(control ?c) (control ?q)] 'ledger-post-align-xact) - - (define-key map [tab] 'ledger-magic-tab) - (define-key map [(control tab)] 'ledger-post-align-xact) - (define-key map [(control ?i)] 'ledger-magic-tab) - (define-key map [(control ?c) tab] 'ledger-fully-complete-xact) - (define-key map [(control ?c) (control ?i)] 'ledger-fully-complete-xact) - - (define-key map [(control ?c) (control ?o) (control ?a)] 'ledger-report-redo) - (define-key map [(control ?c) (control ?o) (control ?e)] 'ledger-report-edit) - (define-key map [(control ?c) (control ?o) (control ?g)] 'ledger-report-goto) - (define-key map [(control ?c) (control ?o) (control ?k)] 'ledger-report-kill) - (define-key map [(control ?c) (control ?o) (control ?r)] 'ledger-report) - (define-key map [(control ?c) (control ?o) (control ?s)] 'ledger-report-save) - - (define-key map [(meta ?p)] 'ledger-post-prev-xact) - (define-key map [(meta ?n)] 'ledger-post-next-xact) - - (define-key map [menu-bar] (make-sparse-keymap "ledger-menu")) - (define-key map [menu-bar ledger-menu] (cons "Ledger" map)) - - (define-key map [report-kill] '(menu-item "Kill Report" ledger-report-kill :enable ledger-works)) - (define-key map [report-edit] '(menu-item "Edit Report" ledger-report-edit :enable ledger-works)) - (define-key map [report-save] '(menu-item "Save Report" ledger-report-save :enable ledger-works)) - (define-key map [report-rrun] '(menu-item "Re-run Report" ledger-report-redo :enable ledger-works)) - (define-key map [report-goto] '(menu-item "Goto Report" ledger-report-goto :enable ledger-works)) - (define-key map [report-run] '(menu-item "Run Report" ledger-report :enable ledger-works)) - (define-key map [sep5] '(menu-item "--")) - (define-key map [set-month] '(menu-item "Set Month" ledger-set-month :enable ledger-works)) - (define-key map [set-year] '(menu-item "Set Year" ledger-set-year :enable ledger-works)) - (define-key map [cust] '(menu-item "Customize Ledger Mode" (lambda () - (interactive) - (customize-group 'ledger)))) - (define-key map [sep1] '("--")) - (define-key map [effective-date] '(menu-item "Set effective date" ledger-insert-effective-date)) - (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 [align-xact] '(menu-item "Align Xact" ledger-post-align-xact)) - (define-key map [align-reg] '(menu-item "Align Region" ledger-post-align-postings :enable mark-active)) - (define-key map [clean-buf] '(menu-item "Clean-up Buffer" ledger-mode-clean-buffer)) - (define-key map [sep2] '(menu-item "--")) - (define-key map [copy-xact] '(menu-item "Copy Trans at Point" ledger-copy-transaction-at-point)) - (define-key map [toggle-post] '(menu-item "Toggle Current Posting" ledger-toggle-current)) - (define-key map [toggle-xact] '(menu-item "Toggle Current Transaction" ledger-toggle-current-transaction)) - (define-key map [sep4] '(menu-item "--")) - (define-key map [recon-account] '(menu-item "Reconcile Account" ledger-reconcile :enable ledger-works)) - (define-key map [check-balance] '(menu-item "Check Balance" ledger-display-balance-at-point :enable ledger-works)) - (define-key map [sep6] '(menu-item "--")) - (define-key map [edit-amount] '(menu-item "Calc on Amount" ledger-post-edit-amount)) - (define-key map [sep] '(menu-item "--")) - (define-key map [delete-xact] '(menu-item "Delete Transaction" ledger-delete-current-transaction)) - (define-key map [cmp-xact] '(menu-item "Complete Transaction" ledger-fully-complete-xact)) - (define-key map [add-xact] '(menu-item "Add Transaction (ledger xact)" ledger-add-transaction :enable ledger-works)) - (define-key map [sep3] '(menu-item "--")) - (define-key map [stats] '(menu-item "Ledger Statistics" ledger-display-ledger-stats :enable ledger-works)) - (define-key map [fold-buffer] '(menu-item "Narrow to REGEX" ledger-occur)))) + "A mode for editing ledger data files." + (ledger-check-version) + (when (boundp 'font-lock-defaults) + (setq font-lock-defaults + '(ledger-font-lock-keywords t t nil nil + (font-lock-fontify-region-function . ledger-fontify-buffer-part)))) + + (set (make-local-variable 'pcomplete-parse-arguments-function) 'ledger-parse-arguments) + (set (make-local-variable 'pcomplete-command-completion-function) 'ledger-complete-at-point) + (add-hook 'completion-at-point-functions 'pcomplete-completions-at-point nil t) + (add-hook 'after-save-hook 'ledger-report-redo) + + (add-hook 'post-command-hook 'ledger-highlight-xact-under-point nil t) + + (ledger-init-load-init-file) + (setq comment-start ";") + (set (make-local-variable 'indent-region-function) 'ledger-post-align-postings)) (defun ledger-set-year (newyear) "Set ledger's idea of the current year to the prefix argument NEWYEAR." (interactive "p") - (if (= newyear 1) - (setq ledger-year (read-string "Year: " (ledger-current-year))) - (setq ledger-year (number-to-string newyear)))) + (setq ledger-year + (if (= newyear 1) + (read-string "Year: " (ledger-current-year)) + (number-to-string newyear)))) (defun ledger-set-month (newmonth) "Set ledger's idea of the current month to the prefix argument NEWMONTH." (interactive "p") - (if (= newmonth 1) - (setq ledger-month (read-string "Month: " (ledger-current-month))) - (setq ledger-month (format "%02d" newmonth)))) + (setq ledger-month + (if (= newmonth 1) + (read-string "Month: " (ledger-current-month)) + (format "%02d" newmonth)))) |