diff options
Diffstat (limited to 'lisp')
-rw-r--r-- | lisp/CMakeLists.txt | 3 | ||||
-rw-r--r-- | lisp/ledger-commodities.el | 25 | ||||
-rw-r--r-- | lisp/ledger-complete.el | 6 | ||||
-rw-r--r-- | lisp/ledger-context.el | 6 | ||||
-rw-r--r-- | lisp/ledger-exec.el | 5 | ||||
-rw-r--r-- | lisp/ledger-fontify.el | 199 | ||||
-rw-r--r-- | lisp/ledger-fonts.el | 188 | ||||
-rw-r--r-- | lisp/ledger-init.el | 8 | ||||
-rw-r--r-- | lisp/ledger-mode.el | 105 | ||||
-rw-r--r-- | lisp/ledger-navigate.el | 168 | ||||
-rw-r--r-- | lisp/ledger-occur.el | 157 | ||||
-rw-r--r-- | lisp/ledger-post.el | 130 | ||||
-rw-r--r-- | lisp/ledger-reconcile.el | 162 | ||||
-rw-r--r-- | lisp/ledger-regex.el | 36 | ||||
-rw-r--r-- | lisp/ledger-report.el | 76 | ||||
-rw-r--r-- | lisp/ledger-schedule.el | 260 | ||||
-rw-r--r-- | lisp/ledger-sort.el | 33 | ||||
-rw-r--r-- | lisp/ledger-state.el | 14 | ||||
-rw-r--r-- | lisp/ledger-test.el | 14 | ||||
-rw-r--r-- | lisp/ledger-texi.el | 2 | ||||
-rw-r--r-- | lisp/ledger-xact.el | 43 |
21 files changed, 1075 insertions, 565 deletions
diff --git a/lisp/CMakeLists.txt b/lisp/CMakeLists.txt index 76f221b4..9dee2abb 100644 --- a/lisp/CMakeLists.txt +++ b/lisp/CMakeLists.txt @@ -2,9 +2,12 @@ set(EMACS_LISP_SOURCES ledger-commodities.el ledger-complete.el ledger-exec.el + ledger-fontify.el ledger-fonts.el + ledger-fontify.el ledger-init.el ledger-mode.el + ledger-navigate.el ledger-occur.el ledger-post.el ledger-reconcile.el diff --git a/lisp/ledger-commodities.el b/lisp/ledger-commodities.el index e6f5417d..5ffebf3b 100644 --- a/lisp/ledger-commodities.el +++ b/lisp/ledger-commodities.el @@ -1,6 +1,6 @@ ;;; ledger-commodities.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -33,11 +33,6 @@ :type 'string :group 'ledger-reconcile) -(defcustom ledger-scale 10000 - "The 10 ^ maximum number of digits you would expect to appear in your reports. -This is a cheap way of getting around floating point silliness in subtraction" - :group 'ledger) - (defun ledger-split-commodity-string (str) "Split a commoditized string, STR, into two parts. Returns a list with (value commodity)." @@ -86,11 +81,7 @@ Returns a list with (value commodity)." (defun -commodity (c1 c2) "Subtract C2 from C1, ensuring their commodities match." (if (string= (cadr c1) (cadr c2)) - ; the scaling below is to get around inexact - ; subtraction results where, for example 1.23 - ; - 4.56 = -3.3299999999999996 instead of - ; -3.33 - (list (/ (- (* ledger-scale (car c1)) (* ledger-scale (car c2))) ledger-scale) (cadr c1)) + (list (-(car c1) (car c2)) (cadr c1)) (error "Can't subtract different commodities %S from %S" c2 c1))) (defun +commodity (c1 c2) @@ -100,22 +91,21 @@ Returns a list with (value commodity)." (error "Can't add different commodities, %S to %S" c1 c2))) (defun ledger-strip (str char) - (let (new-str) - (concat (dolist (ch (append str nil) new-str) - (unless (= ch char) - (setq new-str (append new-str (list ch)))))))) + "Return STR with CHAR removed." + (replace-regexp-in-string char "" str)) (defun ledger-string-to-number (str &optional decimal-comma) "improve builtin string-to-number by handling internationalization, and return nil if number can't be parsed" (let ((nstr (if (or decimal-comma (assoc "decimal-comma" ledger-environment-alist)) - (ledger-strip str ?.) - (ledger-strip str ?,)))) + (ledger-strip str ".") + (ledger-strip str ",")))) (while (string-match "," nstr) ;if there is a comma now, it is a thousands separator (setq nstr (replace-match "." nil nil nstr))) (string-to-number nstr))) (defun ledger-number-to-string (n &optional decimal-comma) + "number-to-string that handles comma as decimal." (let ((str (number-to-string n))) (when (or decimal-comma (assoc "decimal-comma" ledger-environment-alist)) @@ -134,6 +124,7 @@ longer ones are after the value." (concat commodity " " str)))) (defun ledger-read-commodity-string (prompt) + "Read an amount from mini-buffer using PROMPT." (let ((str (read-from-minibuffer (concat prompt " (" ledger-reconcile-default-commodity "): "))) comm) diff --git a/lisp/ledger-complete.el b/lisp/ledger-complete.el index bc4b1854..2fae9911 100644 --- a/lisp/ledger-complete.el +++ b/lisp/ledger-complete.el @@ -1,6 +1,6 @@ ;;; ledger-complete.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -157,9 +157,7 @@ (ledger-accounts))))) (defun ledger-trim-trailing-whitespace (str) - (let ((s str)) - (when (string-match "[ \t]*$" s) - (replace-match "" nil nil s)))) + (replace-regexp-in-string "[ \t]*$" "" str)) (defun ledger-fully-complete-xact () "Completes a transaction if there is another matching payee in the buffer. diff --git a/lisp/ledger-context.el b/lisp/ledger-context.el index 7b10c552..0dfa4645 100644 --- a/lisp/ledger-context.el +++ b/lisp/ledger-context.el @@ -1,6 +1,6 @@ ;;; ledger-context.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -44,9 +44,11 @@ (defconst ledger-payee-string "\\(.*\\)") (defun ledger-get-regex-str (name) + "Get the ledger regex of type NAME." (symbol-value (intern (concat "ledger-" (symbol-name name) "-string")))) (defun ledger-line-regex (elements) + "Get a regex to match ELEMENTS on a single line." (concat (apply 'concat (mapcar 'ledger-get-regex-str elements)) "[ \t]*$")) (defmacro ledger-single-line-config (&rest elements) @@ -195,4 +197,4 @@ specified line, returns nil." (provide 'ledger-context) -;;; ledger-report.el ends here +;;; ledger-context.el ends here diff --git a/lisp/ledger-exec.el b/lisp/ledger-exec.el index cd5c11a0..8902d839 100644 --- a/lisp/ledger-exec.el +++ b/lisp/ledger-exec.el @@ -1,6 +1,6 @@ ;;; ledger-exec.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -36,7 +36,7 @@ :group 'ledger) (defcustom ledger-mode-should-check-version t - "Should Ledger-mode verify that the executable is working" + "Should Ledger-mode verify that the executable is working?" :type 'boolean :group 'ledger-exec) @@ -53,6 +53,7 @@ (setq buffer-read-only t))) (defun ledger-exec-success-p (ledger-output-buffer) + "Return t if the ledger output in LEDGER-OUTPUT-BUFFER is successful." (with-current-buffer ledger-output-buffer (goto-char (point-min)) (if (and (> (buffer-size) 1) (looking-at (regexp-quote "While"))) diff --git a/lisp/ledger-fontify.el b/lisp/ledger-fontify.el new file mode 100644 index 00000000..d307208f --- /dev/null +++ b/lisp/ledger-fontify.el @@ -0,0 +1,199 @@ +;;; ledger-fontify.el --- Provide custom fontification for ledger-mode + + +;; Copyright (C) 2014 Craig P. Earls (enderw88 at gmail dot com) + +;; 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., 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301 USA. + +;;; Commentary: +;; Font-lock-mode doesn't handle multiline syntax very well. This +;; code provides font lock that is sensitive to overall transaction +;; states + + +;;; Code: + +(require 'ledger-navigate) +(require 'ledger-regex) +(require 'ledger-state) + +(defcustom ledger-fontify-xact-state-overrides nil + "If t the highlight entire xact with state." + :type 'boolean + :group 'ledger) + +(defun ledger-fontify-buffer-part (&optional beg end len) +"Fontify buffer from BEG to END, length LEN." + (save-excursion + (unless beg (setq beg (point-min))) + (unless end (setq end (point-max))) + (beginning-of-line) + (while (< (point) end) + (cond ((or (looking-at ledger-xact-start-regex) + (looking-at ledger-posting-regex)) + (ledger-fontify-xact-at (point))) + ((looking-at ledger-directive-start-regex) + (ledger-fontify-directive-at (point)))) + (ledger-navigate-next-xact-or-directive)))) + +(defun ledger-fontify-xact-at (position) + "Fontify the xact at POSITION." + (interactive "d") + (save-excursion + (goto-char position) + (let ((extents (ledger-navigate-find-element-extents position)) + (state (ledger-transaction-state))) + (if (and ledger-fontify-xact-state-overrides state) + (cond ((eq state 'cleared) + (ledger-fontify-set-face extents 'ledger-font-xact-cleared-face)) + ((eq state 'pending) + (ledger-fontify-set-face extents 'ledger-font-xact-pending-face))) + (ledger-fontify-xact-by-line extents))))) + +(defun ledger-fontify-xact-by-line (extents) + "Do line-by-line detailed fontification of xact in EXTENTS." + (save-excursion + (ledger-fontify-xact-start (car extents)) + (while (< (point) (cadr extents)) + (if (looking-at "[ \t]+;") + (ledger-fontify-set-face (list (point) (progn + (end-of-line) + (point))) 'ledger-font-comment-face) + (ledger-fontify-posting (point))) + (forward-line)))) + +(defun ledger-fontify-xact-start (pos) + "POS should be at the beginning of a line starting an xact. +Fontify the first line of an xact" + (goto-char pos) + (let ((line-start (line-beginning-position))) + (goto-char line-start) + (re-search-forward "[ \t]") + (ledger-fontify-set-face (list line-start (match-beginning 0)) 'ledger-font-posting-date-face) + (goto-char line-start) + (re-search-forward ledger-xact-after-date-regex) + (let ((state (save-match-data (ledger-state-from-string (match-string 1))))) + (ledger-fontify-set-face (list (match-beginning 3) (match-end 3)) + (cond ((eq state 'pending) + 'ledger-font-payee-pending-face) + ((eq state 'cleared) + 'ledger-font-payee-cleared-face) + (t + 'ledger-font-payee-uncleared-face)))) + (when (match-beginning 4) + (ledger-fontify-set-face (list (match-beginning 4) + (match-end 4)) 'ledger-font-comment-face)) + (forward-line))) + +(defun ledger-fontify-posting (pos) + "Fontify the posting at POS." + (let* ((state nil) + (end-of-line-comment nil) + (end (progn (end-of-line) + (point))) + (start (progn (beginning-of-line) + (point)))) + + ;; Look for a posting status flag + (set-match-data nil 'reseat) + (re-search-forward " \\([*!]\\) " end t) + (if (match-string 1) + (setq state (ledger-state-from-string (match-string 1)))) + (beginning-of-line) + (re-search-forward "[[:graph:]]\\([ \t][ \t]\\)" end 'end) ;; find the end of the account, or end of line + + (when (<= (point) end) ;; we are still on the line + (ledger-fontify-set-face (list start (point)) + (cond ((eq state 'cleared) + 'ledger-font-posting-account-cleared-face) + ((eq state 'pending) + 'ledger-font-posting-account-pending-face) + (t + 'ledger-font-posting-account-face))) + + + (when (< (point) end) ;; there is still more to fontify + (setq start (point)) ;; update start of next font region + (setq end-of-line-comment (re-search-forward ";" end 'end)) ;; find the end of the line, or start of a comment + (ledger-fontify-set-face (list start (point) ) + (cond ((eq state 'cleared) + 'ledger-font-posting-amount-cleared-face) + ((eq state 'pending) + 'ledger-font-posting-amount-pending-face) + (t + 'ledger-font-posting-amount-face))) + (when end-of-line-comment + (setq start (point)) + (end-of-line) + (ledger-fontify-set-face (list (- start 1) (point)) ;; subtract 1 from start because we passed the semi-colon + 'ledger-font-comment-face)))))) + +(defun ledger-fontify-directive-at (pos) + "Fontify the directive at POS." + (let ((extents (ledger-navigate-find-element-extents pos)) + (face 'ledger-font-default-face)) + (cond ((looking-at "=") + (setq face 'ledger-font-auto-xact-face)) + ((looking-at "~") + (setq face 'ledger-font-periodic-xact-face)) + ((looking-at "[;#%|\\*]") + (setq face 'ledger-font-comment-face)) + ((looking-at "\\(year\\)\\|Y") + (setq face 'ledger-font-year-directive-face)) + ((looking-at "account") + (setq face 'ledger-font-account-directive-face)) + ((looking-at "apply") + (setq face 'ledger-font-apply-directive-face)) + ((looking-at "alias") + (setq face 'ledger-font-alias-directive-face)) + ((looking-at "assert") + (setq face 'ledger-font-assert-directive-face)) + ((looking-at "\\(bucket\\)\\|A") + (setq face 'ledger-font-bucket-directive-face)) + ((looking-at "capture") + (setq face 'ledger-font-capture-directive-face)) + ((looking-at "check") + (setq face 'ledger-font-check-directive-face)) + ((looking-at "commodity") + (setq face 'ledger-font-commodity-directive-face)) + ((looking-at "define") + (setq face 'ledger-font-define-directive-face)) + ((looking-at "end") + (setq face 'ledger-font-end-directive-face)) + ((looking-at "expr") + (setq face 'ledger-font-expr-directive-face)) + ((looking-at "fixed") + (setq face 'ledger-font-fixed-directive-face)) + ((looking-at "include") + (setq face 'ledger-font-include-directive-face)) + ((looking-at "payee") + (setq face 'ledger-font-payee-directive-face)) + ((looking-at "P") + (setq face 'ledger-font-price-directive-face)) + ((looking-at "tag") + (setq face 'ledger-font-tag-directive-face))) + (ledger-fontify-set-face extents face))) + +(defun ledger-fontify-set-face (extents face) + "Set the text in EXTENTS to FACE." + (put-text-property (car extents) (cadr extents) 'face face)) + + +(provide 'ledger-fontify) + +;;; ledger-fontify.el ends here diff --git a/lisp/ledger-fonts.el b/lisp/ledger-fonts.el index f5ed6e94..8bdecdb3 100644 --- a/lisp/ledger-fonts.el +++ b/lisp/ledger-fonts.el @@ -1,6 +1,6 @@ ;;; ledger-fonts.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -29,6 +29,37 @@ (require 'ledger-regex) (defgroup ledger-faces nil "Ledger mode highlighting" :group 'ledger) + +(defface ledger-font-default-face + `((t :inherit default)) + "Default face" + :group 'ledger-faces) + +(defface ledger-font-auto-xact-face + `((t :foreground "orange" :weight normal)) + "Default face for automatic transactions" + :group 'ledger-faces) + +(defface ledger-font-periodic-xact-face + `((t :foreground "green" :weight normal)) + "Default face for automatic transactions" + :group 'ledger-faces) + +(defface ledger-font-xact-cleared-face + `((t :foreground "#AAAAAA" :weight normal)) + "Default face for cleared transaction" + :group 'ledger-faces) + +(defface ledger-font-xact-pending-face + `((t :foreground "#444444" :weight normal)) + "Default face for pending transaction" + :group 'ledger-faces) + +(defface ledger-font-xact-open-face + `((t :foreground "#000000" :weight normal)) + "Default face for transaction under point" + :group 'ledger-faces) + (defface ledger-font-payee-uncleared-face `((t :foreground "#dc322f" :weight bold )) "Default face for Ledger" @@ -36,7 +67,12 @@ (defface ledger-font-payee-cleared-face `((t :inherit ledger-font-other-face)) - "Default face for cleared (*) transactions" + "Default face for cleared (*) payees" + :group 'ledger-faces) + +(defface ledger-font-payee-pending-face + `((t :foreground "#F24B61" :weight normal)) + "Default face for pending (!) payees" :group 'ledger-faces) (defface ledger-font-xact-highlight-face @@ -54,6 +90,96 @@ "Default face for other transactions" :group 'ledger-faces) +(defface ledger-font-directive-face + `((t :inherit font-lock-preprocessor-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-account-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-price-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-apply-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-alias-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-assert-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-bucket-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-capture-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-check-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-commodity-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-define-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-end-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-expr-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-fixed-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-include-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-payee-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-tag-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + +(defface ledger-font-year-directive-face + `((t :inherit ledger-font-directive-face)) + "Default face for other transactions" + :group 'ledger-faces) + (defface ledger-font-posting-account-face `((t :foreground "#268bd2" )) "Face for Ledger accounts" @@ -64,11 +190,21 @@ "Face for Ledger accounts" :group 'ledger-faces) +(defface ledger-font-posting-amount-cleared-face + `((t :inherit ledger-font-posting-account-cleared-face)) + "Face for Ledger accounts" + :group 'ledger-faces) + (defface ledger-font-posting-account-pending-face `((t :inherit ledger-font-pending-face)) "Face for Ledger accounts" :group 'ledger-faces) +(defface ledger-font-posting-amount-pending-face + `((t :inherit ledger-font-posting-account-pending-face)) + "Face for Ledger accounts" + :group 'ledger-faces) + (defface ledger-font-posting-amount-face `((t :foreground "#cb4b16" )) "Face for Ledger amounts" @@ -80,18 +216,17 @@ :group 'ledger-faces) (defface ledger-occur-narrowed-face - `((t :foreground "grey70" :invisible t )) + `((t :inherit font-lock-comment-face :invisible t)) "Default face for Ledger occur mode hidden transactions" :group 'ledger-faces) (defface ledger-occur-xact-face - `((((background dark)) :background "#1a1a1a" ) - (t :background "#eee8d5" )) + `((t :inherit highlight)) "Default face for Ledger occur mode shown transactions" :group 'ledger-faces) (defface ledger-font-comment-face - `((t :foreground "#93a1a1" :slant italic)) + `((t :inherit font-lock-comment-face)) "Face for Ledger comments" :group 'ledger-faces) @@ -115,30 +250,25 @@ "Default face for pending (!) transactions in the reconcile window" :group 'ledger-faces) + (defvar ledger-font-lock-keywords + `(("account" . ledger-font-account-directive-face) + ("apply" . ledger-font-apply-directive-face) + ("alias" . ledger-font-alias-directive-face) + ("assert" . ledger-font-assert-directive-face) + ("bucket" . ledger-font-bucket-directive-face) + ("capture" . ledger-font-capture-directive-face) + ("check" . ledger-font-check-directive-face) + ("commodity" . ledger-font-commodity-directive-face) + ("define" . ledger-font-define-directive-face) + ("end" . ledger-font-end-directive-face) + ("expr" . ledger-font-expr-directive-face) + ("fixed" . ledger-font-fixed-directive-face) + ("include" . ledger-font-include-directive-face) + ("payee" . ledger-font-payee-directive-face) + ("tag" . ledger-font-tag-directive-face) + ("year" . ledger-font-year-directive-face)) + "Expressions to highlight in Ledger mode.") -(defvar ledger-font-lock-keywords - `( ;; (,ledger-other-entries-regex 1 - ;; ledger-font-other-face) - (,ledger-comment-regex 0 - 'ledger-font-comment-face) - (,ledger-amount-regex 0 - 'ledger-font-posting-amount-face) - (,ledger-multiline-comment-regex 0 'ledger-font-comment-face) - (,ledger-payee-pending-regex 2 - 'ledger-font-payee-pending-face) ; Works - (,ledger-payee-cleared-regex 2 - 'ledger-font-payee-cleared-face) ; Works - (,ledger-payee-uncleared-regex 2 - 'ledger-font-payee-uncleared-face) ; Works - (,ledger-account-cleared-regex 2 - 'ledger-font-posting-account-cleared-face) ; Works - (,ledger-account-pending-regex 2 - 'ledger-font-posting-account-pending-face) ; Works - (,ledger-account-any-status-regex 2 - 'ledger-font-posting-account-face) ; Works - (,ledger-other-entries-regex 1 - 'ledger-font-other-face)) - "Expressions to highlight in Ledger mode.") (provide 'ledger-fonts) diff --git a/lisp/ledger-init.el b/lisp/ledger-init.el index 491f20cf..49d74098 100644 --- a/lisp/ledger-init.el +++ b/lisp/ledger-init.el @@ -1,6 +1,6 @@ ;;; ledger-init.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -24,8 +24,10 @@ (require 'ledger-regex) +;;; Code: + (defcustom ledger-init-file-name "~/.ledgerrc" - "Location of the ledger initialization file. nil if you don't have one" + "Location of the ledger initialization file. nil if you don't have one." :group 'ledger-exec) (defvar ledger-environment-alist nil) @@ -33,6 +35,7 @@ (defvar ledger-default-date-format "%Y/%m/%d") (defun ledger-init-parse-initialization (buffer) + "Parse the .ledgerrc file in BUFFER." (with-current-buffer buffer (let (environment-alist) (goto-char (point-min)) @@ -53,6 +56,7 @@ environment-alist))) (defun ledger-init-load-init-file () + "Load and parse the .ledgerrc file." (interactive) (let ((init-base-name (file-name-nondirectory ledger-init-file-name))) (if (get-buffer init-base-name) ;; init file already loaded, parse it and leave it diff --git a/lisp/ledger-mode.el b/lisp/ledger-mode.el index 458c24b1..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-2014 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. @@ -27,6 +27,7 @@ ;;; Code: (require 'ledger-regex) +(require 'cus-edit) (require 'esh-util) (require 'esh-arg) (require 'easymenu) @@ -35,7 +36,9 @@ (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) @@ -59,11 +62,12 @@ (defconst ledger-mode-version "3.0.0") (defun ledger-mode-dump-variable (var) - (if 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) @@ -73,7 +77,7 @@ (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)) @@ -94,14 +98,15 @@ "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 (eq (ledger-context-line-type context) 'acct-transaction) - (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) - "Returns user-supplied date after `PROMPT', defaults to today." + "Return user-supplied date after `PROMPT', defaults to today." (let* ((default (ledger-year-and-month)) (date (read-string prompt default 'ledger-minibuffer-history))) @@ -146,7 +151,7 @@ And calculate the target-delta of the account being reconciled." (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)) @@ -164,14 +169,14 @@ Can indent, complete or align depending on context." ledger-default-date-format))) (defun ledger-remove-effective-date () - "Removes the effective date from a transaction or posting." + "Remove the effective date from a transaction or posting." (interactive) (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 'pmnt-transaction context) + (cond ((eq 'xact context) (re-search-forward ledger-iso-date-regexp) (when (= (char-after) ?=) (let ((eq-pos (point))) @@ -194,7 +199,7 @@ 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. " +With a prefix argument, remove the effective date." (interactive) (if (and (listp current-prefix-arg) (= 4 (prefix-numeric-value current-prefix-arg))) @@ -204,7 +209,7 @@ With a prefix argument, remove the effective date. " (save-restriction (narrow-to-region (point-at-bol) (point-at-eol)) (cond - ((eq 'pmnt-transaction context) + ((eq 'xact context) (beginning-of-line) (re-search-forward ledger-iso-date-regexp) (when (= (char-after) ?=) @@ -216,26 +221,35 @@ With a prefix argument, remove the effective date. " (insert " ; [=" date-string "]"))))))) (defun ledger-mode-remove-extra-lines () - (goto-char (point-min)) + "Get rid of multiple empty lines." + (goto-char (point-min)) (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" + "Indent, remove multiple line feeds and sort the buffer." (interactive) - (untabify (point-min) (point-max)) - (ledger-sort-buffer) - (ledger-post-align-postings (point-min) (point-max)) - (ledger-mode-remove-extra-lines)) - + (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))) - ;; Support comments via the syntax table - (modify-syntax-entry ?\; "< b" table) - (modify-syntax-entry ?\n "> b" table) + (let ((table (make-syntax-table text-mode-syntax-table))) + (modify-syntax-entry ?\; "<" table) + (modify-syntax-entry ?\n ">" table) table) - "Syntax table for `ledger-mode' buffers.") + "Syntax table in use in `ledger-mode' buffers.") (defvar ledger-mode-map (let ((map (make-sparse-keymap))) @@ -269,8 +283,8 @@ With a prefix argument, remove the effective date. " (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 [(meta ?p)] 'ledger-navigate-prev-xact-or-directive) + (define-key map [(meta ?n)] 'ledger-navigate-next-xact-or-directive) map) "Keymap for `ledger-mode'.") @@ -278,9 +292,10 @@ With a prefix argument, remove the effective date. " "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 ledger-schedule-available] + ["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] @@ -318,37 +333,25 @@ With a prefix argument, remove the effective date. " (define-derived-mode ledger-mode text-mode "Ledger" "A mode for editing ledger data files." (ledger-check-version) - (ledger-schedule-check-available) - ;;(ledger-post-setup) - - (set-syntax-table ledger-mode-syntax-table) - (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) + (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 'after-save-hook) (add-hook 'post-command-hook 'ledger-highlight-xact-under-point nil t) - (add-hook 'before-revert-hook 'ledger-occur-remove-all-overlays 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") diff --git a/lisp/ledger-navigate.el b/lisp/ledger-navigate.el new file mode 100644 index 00000000..904faf8c --- /dev/null +++ b/lisp/ledger-navigate.el @@ -0,0 +1,168 @@ +;;; ledger-navigate.el --- Provide navigation services through the ledger buffer. + +;; Copyright (C) 2014-2015 Craig Earls (enderw88 AT gmail DOT com) + +;; 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., 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301 USA. + + +;;; Commentary: +;; + +;;; Code: + +(require 'ledger-regex) +(require 'ledger-context) + +(defun ledger-navigate-next-xact () + "Move point to beginning of next xact." + ;; make sure we actually move to the next xact, even if we are the + ;; beginning of one now. + (if (looking-at ledger-payee-any-status-regex) + (forward-line)) + (if (re-search-forward ledger-payee-any-status-regex nil t) + (goto-char (match-beginning 0)) + (goto-char (point-max)))) + +(defun ledger-navigate-start-xact-or-directive-p () + "Return t if at the beginning of an empty or all-whitespace line." + (not (looking-at "[ \t]\\|\\(^$\\)"))) + +(defun ledger-navigate-next-xact-or-directive () + "Move to the beginning of the next xact or directive." + (interactive) + (beginning-of-line) + (if (ledger-navigate-start-xact-or-directive-p) ; if we are the start of an xact, move forward to the next xact + (progn + (forward-line) + (if (not (ledger-navigate-start-xact-or-directive-p)) ; we have moved forward and are not at another xact, recurse forward + (ledger-navigate-next-xact-or-directive))) + (while (not (or (eobp) ; we didn't start off at the beginning of an xact + (ledger-navigate-start-xact-or-directive-p))) + (forward-line)))) + +(defun ledger-navigate-prev-xact-or-directive () + "Move point to beginning of previous xact." + (interactive) + (let ((context (car (ledger-context-at-point)))) + (when (equal context 'acct-transaction) + (ledger-navigate-beginning-of-xact)) + (beginning-of-line) + (re-search-backward "^[[:graph:]]" nil t))) + +(defun ledger-navigate-beginning-of-xact () + "Move point to the beginning of the current xact." + (interactive) + ;; need to start at the beginning of a line incase we are in the first line of an xact already. + (beginning-of-line) + (let ((sreg (concat "^\\(=\\|~\\|" ledger-iso-date-regexp "\\)"))) + (unless (looking-at sreg) + (re-search-backward sreg nil t) + (beginning-of-line))) + (point)) + +(defun ledger-navigate-end-of-xact () + "Move point to end of xact." + (interactive) + (ledger-navigate-next-xact-or-directive) + (re-search-backward ".$") + (end-of-line) + (point)) + +(defun ledger-navigate-to-line (line-number) + "Rapidly move point to line LINE-NUMBER." + (goto-char (point-min)) + (forward-line (1- line-number))) + +(defun ledger-navigate-find-xact-extents (pos) + "Return list containing point for beginning and end of xact containing POS. +Requires empty line separating xacts." + (interactive "d") + (save-excursion + (goto-char pos) + (list (ledger-navigate-beginning-of-xact) + (ledger-navigate-end-of-xact)))) + +(defun ledger-navigate-find-directive-extents (pos) + "Return the extents of the directive at POS." + (goto-char pos) + (let ((begin (progn (beginning-of-line) + (point))) + (end (progn (end-of-line) + (+ 1 (point))))) + ;; handle block comments here + (beginning-of-line) + (if (looking-at " *;") + (progn + (while (and (looking-at " *;") + (> (point) (point-min))) + (forward-line -1)) + ;; We are either at the beginning of the buffer, or we found + ;; a line outside the comment. If we are not at the + ;; beginning of the buffer then we need to move forward a + ;; line. + (if (> (point) (point-min)) + (progn (forward-line 1) + (beginning-of-line))) + (setq begin (point)) + (goto-char pos) + (beginning-of-line) + (while (and (looking-at " *;") + (< (point) (point-max))) + (forward-line 1)) + (setq end (point)))) + (list begin end))) + +(defun ledger-navigate-block-comment (pos) + "Move past the block comment at POS, and return its extents." + (interactive "d") + (goto-char pos) + (let ((begin (progn (beginning-of-line) + (point))) + (end (progn (end-of-line) + (point)))) + ;; handle block comments here + (beginning-of-line) + (if (looking-at " *;") + (progn + (while (and (looking-at " *;") + (> (point) (point-min))) + (forward-line -1)) + (setq begin (point)) + (goto-char pos) + (beginning-of-line) + (while (and (looking-at " *;") + (< (point) (point-max))) + (forward-line 1)) + (setq end (point)))) + (list begin end))) + + +(defun ledger-navigate-find-element-extents (pos) + "Return list containing beginning and end of the entity surrounding POS." + (interactive "d") + (save-excursion + (goto-char pos) + (beginning-of-line) + (if (looking-at "[ =~0-9]") + (ledger-navigate-find-xact-extents pos) + (ledger-navigate-find-directive-extents pos)))) + + +(provide 'ledger-navigate) + +;;; ledger-navigate.el ends here diff --git a/lisp/ledger-occur.el b/lisp/ledger-occur.el index 9287ed13..a4fde2e1 100644 --- a/lisp/ledger-occur.el +++ b/lisp/ledger-occur.el @@ -1,6 +1,6 @@ -;;; ledger-mode.el --- Helper code for use with the "ledger" command-line tool +;;; ledger-occur.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -29,6 +29,9 @@ ;;; Code: +(require 'cl) +(require 'ledger-navigate) + (defconst ledger-occur-overlay-property-name 'ledger-occur-custom-buffer-grep) (defcustom ledger-occur-use-face-shown t @@ -38,78 +41,66 @@ (make-variable-buffer-local 'ledger-occur-use-face-shown) -(defvar ledger-occur-mode nil - "name of the minor mode, shown in the mode-line") +(defvar ledger-occur-history nil + "History of previously searched expressions for the prompt.") -(make-variable-buffer-local 'ledger-occur-mode) +(defvar ledger-occur-current-regex nil + "Pattern currently applied to narrow the buffer.") +(make-variable-buffer-local 'ledger-occur-current-regex) -(or (assq 'ledger-occur-mode minor-mode-alist) - (nconc minor-mode-alist - (list '(ledger-occur-mode ledger-occur-mode)))) +(defvar ledger-occur-mode-map (make-sparse-keymap)) -(defvar ledger-occur-history nil - "History of previously searched expressions for the prompt.") +(define-minor-mode ledger-occur-mode + "A minor mode which display only transactions matching `ledger-occur-current-regex'." + nil + (:eval (format " Ledger-Narrow(%s)" ledger-occur-current-regex)) + ledger-occur-mode-map + (if (and ledger-occur-current-regex ledger-occur-mode) + (ledger-occur-refresh) + (ledger-occur-remove-overlays) + (message "Showing all transactions"))) -(defvar ledger-occur-last-match nil - "Last match found.") -(make-variable-buffer-local 'ledger-occur-last-match) +(define-key ledger-occur-mode-map (kbd "C-c C-g") 'ledger-occur-refresh) +(define-key ledger-occur-mode-map (kbd "C-c C-f") 'ledger-occur-mode) -(defun ledger-occur-remove-all-overlays () - "Remove all overlays from the ledger buffer." +(defun ledger-occur-refresh () + "Re-apply the current narrowing expression." (interactive) - (remove-overlays)) - -(defun ledger-occur-mode (regex buffer) - "Highlight transactions that match REGEX in BUFFER, hiding others. - -When REGEX is nil, unhide everything, and remove higlight" - (set-buffer buffer) - (setq ledger-occur-mode - (if (or (null regex) - (zerop (length regex))) - nil - (concat " Ledger-Narrowed: " regex))) - (force-mode-line-update) - (ledger-occur-remove-overlays) - (when ledger-occur-mode - (ledger-occur-create-overlays - (ledger-occur-compress-matches - (ledger-occur-find-matches regex))) - (setq ledger-occur-last-match regex) - (if (get-buffer-window buffer) - (select-window (get-buffer-window buffer)))) - (recenter)) + (let ((matches (ledger-occur-compress-matches + (ledger-occur-find-matches ledger-occur-current-regex)))) + (if matches + (ledger-occur-create-overlays matches) + (message "No matches found for '%s'" ledger-occur-current-regex) + (ledger-occur-mode -1)))) (defun ledger-occur (regex) - "Perform a simple grep in current buffer for the regular expression REGEX. + "Show only transactions in the current buffer which match REGEX. - This command hides all xact from the current buffer except - those containing the regular expression REGEX. A second call - of the function unhides lines again" +This command hides all xact in the current buffer except those +matching REGEX. If REGEX is nil or empty, turn off any narrowing +currently active." (interactive - (if ledger-occur-mode - (list nil) - (list (read-string (concat "Regexp<" (ledger-occur-prompt) ">: ") - nil 'ledger-occur-history (ledger-occur-prompt))))) - (ledger-occur-mode regex (current-buffer))) + (list (read-regexp "Regexp" (ledger-occur-prompt) 'ledger-occur-history))) + (if (or (null regex) + (zerop (length regex))) ; empty regex, or already have narrowed, clear narrowing + (ledger-occur-mode -1) + (setq ledger-occur-current-regex regex) + (ledger-occur-mode 1))) (defun ledger-occur-prompt () "Return the default value of the prompt. Default value for prompt is a current word or active region(selection), if its size is 1 line" - (let ((prompt - (if (and transient-mark-mode - mark-active) - (let ((pos1 (region-beginning)) - (pos2 (region-end))) - ;; Check if the start and the of an active region is on - ;; the same line - (if (= (line-number-at-pos pos1) - (line-number-at-pos pos2)) - (buffer-substring-no-properties pos1 pos2))) - (current-word)))) - prompt)) + (if (use-region-p) + (let ((pos1 (region-beginning)) + (pos2 (region-end))) + ;; Check if the start and the of an active region is on + ;; the same line + (if (= (line-number-at-pos pos1) + (line-number-at-pos pos2)) + (buffer-substring-no-properties pos1 pos2))) + (current-word))) (defun ledger-occur-make-visible-overlay (beg end) @@ -127,6 +118,7 @@ When REGEX is nil, unhide everything, and remove higlight" Argument OVL-BOUNDS contains bounds for the transactions to be left visible." (let* ((beg (caar ovl-bounds)) (end (cadar ovl-bounds))) + (ledger-occur-remove-overlays) (ledger-occur-make-invisible-overlay (point-min) (1- beg)) (dolist (visible (cdr ovl-bounds)) (ledger-occur-make-visible-overlay beg end) @@ -135,15 +127,6 @@ Argument OVL-BOUNDS contains bounds for the transactions to be left visible." (setq end (cadr visible))) (ledger-occur-make-invisible-overlay (1+ end) (point-max)))) -(defun ledger-occur-quit-buffer (buffer) - "Quits hidings transaction in the given BUFFER. -Used for coordinating `ledger-occur' with other buffers, like reconcile." - (set-buffer buffer) - (setq ledger-occur-mode nil) - (force-mode-line-update) - (ledger-occur-remove-overlays) - (recenter)) - (defun ledger-occur-remove-overlays () "Remove the transaction hiding overlays." (interactive) @@ -155,36 +138,30 @@ Used for coordinating `ledger-occur' with other buffers, like reconcile." (save-excursion (goto-char (point-min)) ;; Set initial values for variables - (let (curpoint - endpoint - (lines (list))) + (let (endpoint lines bounds) ;; Search loop (while (not (eobp)) - (setq curpoint (point)) ;; if something found (when (setq endpoint (re-search-forward regex nil 'end)) - (save-excursion - (let ((bounds (ledger-find-xact-extents (match-beginning 0)))) - (push bounds lines) - (setq curpoint (cadr bounds)))) ;; move to the end of - ;; the xact, no need to - ;; search inside it more - (goto-char curpoint)) - (forward-line 1)) - (setq lines (nreverse lines))))) + (setq bounds (ledger-navigate-find-element-extents endpoint)) + (push bounds lines) + ;; move to the end of the xact, no need to search inside it more + (goto-char (cadr bounds)))) + (nreverse lines)))) (defun ledger-occur-compress-matches (buffer-matches) "identify sequential xacts to reduce number of overlays required" - (let ((points (list)) - (current-beginning (caar buffer-matches)) - (current-end (cadar buffer-matches))) - (dolist (match (cdr buffer-matches)) - (if (< (- (car match) current-end) 2) - (setq current-end (cadr match)) - (push (list current-beginning current-end) points) - (setq current-beginning (car match)) - (setq current-end (cadr match)))) - (nreverse (push (list current-beginning current-end) points)))) + (if buffer-matches + (let ((points (list)) + (current-beginning (caar buffer-matches)) + (current-end (cadar buffer-matches))) + (dolist (match (cdr buffer-matches)) + (if (< (- (car match) current-end) 2) + (setq current-end (cadr match)) + (push (list current-beginning current-end) points) + (setq current-beginning (car match)) + (setq current-end (cadr match)))) + (nreverse (push (list current-beginning current-end) points))))) (provide 'ledger-occur) diff --git a/lisp/ledger-post.el b/lisp/ledger-post.el index ac040bb2..e0c7aaee 100644 --- a/lisp/ledger-post.el +++ b/lisp/ledger-post.el @@ -1,6 +1,6 @@ ;;; ledger-post.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -42,7 +42,7 @@ :group 'ledger-post) (defcustom ledger-post-use-completion-engine :built-in - "Which completion engine to use, :iswitchb or :ido chose those engines, + "Which completion engine to use, :iswitchb or :ido chose those engines. :built-in uses built-in Ledger-mode completion" :type '(radio (const :tag "built in completion" :built-in) (const :tag "ido completion" :ido) @@ -82,9 +82,8 @@ point at beginning of the commodity." (- (or (match-end 4) (match-end 3)) (point))))) - (defun ledger-next-account (&optional end) - "Move point to the beginning of the next account, or status marker (!*), as long as it is not past END. + "Move to the beginning of the posting, or status marker, limit to END. Return the column of the beginning of the account and leave point at beginning of account" (if (> end (point)) @@ -96,13 +95,13 @@ at beginning of account" (current-column)))) (defun ledger-post-align-xact (pos) - (interactive "d") - (let ((bounds (ledger-find-xact-extents pos))) + "Align all the posting in the xact at POS." + (interactive "d") + (let ((bounds (ledger-navigate-find-xact-extents pos))) (ledger-post-align-postings (car bounds) (cadr bounds)))) (defun ledger-post-align-postings (&optional beg end) - "Align all accounts and amounts within region, if there is no -region align the posting on the current line." + "Align all accounts and amounts between BEG and END, or the current line." (interactive) (save-excursion @@ -110,62 +109,51 @@ region align the posting on the current line." (not (use-region-p))) (set-mark (point))) - (let* ((inhibit-modification-hooks t) - (mark-first (< (mark) (point))) - (begin-region (if beg - beg - (if mark-first (mark) (point)))) - (end-region (if end - end - (if mark-first (point) (mark)))) - acct-start-column acct-end-column acct-adjust amt-width amt-adjust - (lines-left 1)) - ;; Condition point and mark to the beginning and end of lines - (goto-char end-region) - (setq end-region (line-end-position)) - (goto-char begin-region) - (goto-char - (setq begin-region - (line-beginning-position))) - - (untabify begin-region end-region) - - (goto-char end-region) - (setq end-region (line-end-position)) - (goto-char begin-region) - (goto-char - (setq begin-region - (line-beginning-position))) - - ;; This is the guts of the alignment loop - (while (and (or (setq acct-start-column (ledger-next-account (line-end-position))) - lines-left) - (< (point) end-region)) - (when acct-start-column - (setq acct-end-column (save-excursion - (goto-char (match-end 2)) - (current-column))) - (when (/= (setq acct-adjust (- ledger-post-account-alignment-column acct-start-column)) 0) - (setq acct-end-column (+ acct-end-column acct-adjust)) ;;adjust the account ending column - (if (> acct-adjust 0) - (insert (make-string acct-adjust ? )) - (delete-char acct-adjust))) - (when (setq amt-width (ledger-next-amount (line-end-position))) - (if (/= 0 (setq amt-adjust (- (if (> (- ledger-post-amount-alignment-column amt-width) - (+ 2 acct-end-column)) - ledger-post-amount-alignment-column ;;we have room - (+ acct-end-column 2 amt-width)) - amt-width - (current-column)))) - (if (> amt-adjust 0) - (insert (make-string amt-adjust ? )) - (delete-char amt-adjust))))) - (forward-line) - (setq lines-left (not (eobp)))) + (let ((inhibit-modification-hooks t) + (mark-first (< (mark) (point))) + acct-start-column acct-end-column acct-adjust amt-width amt-adjust + (lines-left 1)) + + (unless beg (setq beg (if mark-first (mark) (point)))) + (unless end (setq end (if mark-first (mark) (point)))) + + ;; Extend region to whole lines + (let ((start-marker (set-marker (make-marker) (save-excursion + (goto-char beg) + (line-beginning-position)))) + (end-marker (set-marker (make-marker) (save-excursion + (goto-char end) + (line-end-position))))) + (untabify start-marker end-marker) + (goto-char start-marker) + + ;; This is the guts of the alignment loop + (while (and (or (setq acct-start-column (ledger-next-account (line-end-position))) + lines-left) + (< (point) end-marker)) + (when acct-start-column + (setq acct-end-column (save-excursion + (goto-char (match-end 2)) + (current-column))) + (when (/= (setq acct-adjust (- ledger-post-account-alignment-column acct-start-column)) 0) + (setq acct-end-column (+ acct-end-column acct-adjust)) ;;adjust the account ending column + (if (> acct-adjust 0) + (insert (make-string acct-adjust ? )) + (delete-char acct-adjust))) + (when (setq amt-width (ledger-next-amount (line-end-position))) + (if (/= 0 (setq amt-adjust (- (if (> (- ledger-post-amount-alignment-column amt-width) + (+ 2 acct-end-column)) + ledger-post-amount-alignment-column ;;we have room + (+ acct-end-column 2 amt-width)) + amt-width + (current-column)))) + (if (> amt-adjust 0) + (insert (make-string amt-adjust ? )) + (delete-char amt-adjust))))) + (forward-line) + (setq lines-left (not (eobp))))) (setq inhibit-modification-hooks nil)))) - - (defun ledger-post-edit-amount () "Call 'calc-mode' and push the amount in the posting to the top of stack." (interactive) @@ -186,24 +174,6 @@ region align the posting on the current line." (insert " ")) (calc)))))) -(defun ledger-post-prev-xact () - "Move point to the previous transaction." - (interactive) - (backward-paragraph) - (when (re-search-backward ledger-xact-line-regexp nil t) - (goto-char (match-beginning 0)) - (re-search-forward ledger-post-line-regexp) - (goto-char (match-end ledger-regex-post-line-group-account)))) - -(defun ledger-post-next-xact () - "Move point to the next transaction." - (interactive) - (when (re-search-forward ledger-xact-line-regexp nil t) - (goto-char (match-beginning 0)) - (re-search-forward ledger-post-line-regexp) - (goto-char (match-end ledger-regex-post-line-group-account)))) - - (provide 'ledger-post) diff --git a/lisp/ledger-reconcile.el b/lisp/ledger-reconcile.el index 48d54eb0..80e27ae3 100644 --- a/lisp/ledger-reconcile.el +++ b/lisp/ledger-reconcile.el @@ -1,6 +1,6 @@ ;;; ledger-reconcile.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -44,8 +44,7 @@ :group 'ledger-reconcile) (defcustom ledger-narrow-on-reconcile t - "If t, limit transactions shown in main buffer to those -matching the reconcile regex." + "If t, limit transactions shown in main buffer to those matching the reconcile regex." :type 'boolean :group 'ledger-reconcile) @@ -56,8 +55,7 @@ Then that transaction will be shown in its source buffer." :group 'ledger-reconcile) (defcustom ledger-reconcile-force-window-bottom nil - "If t make the reconcile window appear along the bottom of the -register window and resize." + "If t make the reconcile window appear along the bottom of the register window and resize." :type 'boolean :group 'ledger-reconcile) @@ -68,25 +66,26 @@ reconcile-finish will mark all pending posting cleared." :group 'ledger-reconcile) (defcustom ledger-reconcile-default-date-format ledger-default-date-format - "Default date format for the reconcile buffer" + "Default date format for the reconcile buffer." :type 'string :group 'ledger-reconcile) (defcustom ledger-reconcile-target-prompt-string "Target amount for reconciliation " - "Default prompt for recon target prompt" + "Default prompt for recon target prompt." :type 'string :group 'ledger-reconcile) (defcustom ledger-reconcile-buffer-header "Reconciling account %s\n\n" - "Default header string for the reconcile buffer. If non-nil, - the name of the account being reconciled will be substituted + "Default header string for the reconcile buffer. + +If non-nil, the name of the account being reconciled will be substituted into the '%s'. If nil, no header willbe displayed." :type 'string :group 'ledger-reconcile) (defcustom ledger-reconcile-buffer-line-format "%(date)s %-4(code)s %-50(payee)s %-30(account)s %15(amount)s\n" - "Format string for the ledger reconcile posting -format. Available fields are date, status, code, payee, account, + "Format string for the ledger reconcile posting format. +Available fields are date, status, code, payee, account, amount. The format for each field is %WIDTH(FIELD), WIDTH can be preced by a minus sign which mean to left justify and pad the field." @@ -94,8 +93,9 @@ field." :group 'ledger-reconcile) (defcustom ledger-reconcile-sort-key "(0)" - "Default key for sorting reconcile buffer. Possible values are -'(date)', '(amount)', '(payee)'. For no sorting, i.e. using + "Default key for sorting reconcile buffer. + +Possible values are '(date)', '(amount)', '(payee)'. For no sorting, i.e. using ledger file order, use '(0)'." :type 'string :group 'ledger-reconcile) @@ -106,7 +106,7 @@ ledger file order, use '(0)'." :group 'ledger-reconcile) (defun ledger-reconcile-get-cleared-or-pending-balance (buffer account) - "Calculate the cleared or pending balance of the account." + "Use BUFFER to Calculate the cleared or pending balance of the ACCOUNT." ;; these vars are buffer local, need to hold them for use in the ;; temp buffer below @@ -118,7 +118,7 @@ ledger file order, use '(0)'." ;; specify the individual fields in the command line. (if (ledger-exec-ledger buffer (current-buffer) "balance" "--limit" "cleared or pending" "--empty" "--collapse" - "--format" "%(display_total)" account) + "--format" "%(scrub(display_total))" account) (ledger-split-commodity-string (buffer-substring-no-properties (point-min) (point-max)))))) @@ -157,7 +157,7 @@ And calculate the target-delta of the account being reconciled." status) (when (ledger-reconcile-get-buffer where) (with-current-buffer (ledger-reconcile-get-buffer where) - (ledger-goto-line (cdr where)) + (ledger-navigate-to-line (cdr where)) (forward-char) (setq status (ledger-toggle-current (if ledger-reconcile-toggle-to-pending 'pending @@ -197,15 +197,16 @@ Return the number of uncleared xacts found." (defun ledger-reconcile-refresh-after-save () "Refresh the recon-window after the ledger buffer is saved." - (let ((curbuf (current-buffer)) + (let ((curbufwin (get-buffer-window (current-buffer))) (curpoint (point)) (recon-buf (get-buffer ledger-recon-buffer-name))) (when (buffer-live-p recon-buf) (with-current-buffer recon-buf (ledger-reconcile-refresh) (set-buffer-modified-p nil)) - (select-window (get-buffer-window curbuf)) - (goto-char curpoint)))) + (when curbufwin + (select-window curbufwin) + (goto-char curpoint))))) (defun ledger-reconcile-add () "Use ledger xact to add a new transaction." @@ -220,7 +221,7 @@ Return the number of uncleared xacts found." (let ((where (get-text-property (point) 'where))) (when (ledger-reconcile-get-buffer where) (with-current-buffer (ledger-reconcile-get-buffer where) - (ledger-goto-line (cdr where)) + (ledger-navigate-to-line (cdr where)) (ledger-delete-current-transaction (point))) (let ((inhibit-read-only t)) (goto-char (line-beginning-position)) @@ -231,22 +232,22 @@ Return the number of uncleared xacts found." (defun ledger-reconcile-visit (&optional come-back) "Recenter ledger buffer on transaction and COME-BACK if non-nil." (interactive) - (progn - (beginning-of-line) - (let* ((where (get-text-property (1+ (point)) 'where)) - (target-buffer (if where - (ledger-reconcile-get-buffer where) - nil)) - (cur-win (get-buffer-window (get-buffer ledger-recon-buffer-name)))) - (when target-buffer - (switch-to-buffer-other-window target-buffer) - (ledger-goto-line (cdr where)) - (forward-char) - (recenter) - (ledger-highlight-xact-under-point) - (forward-char -1) - (if (and come-back cur-win) - (select-window cur-win)))))) + (beginning-of-line) + (let* ((where (get-text-property (1+ (point)) 'where)) + (target-buffer (if where + (ledger-reconcile-get-buffer where) + nil)) + (cur-win (get-buffer-window (get-buffer ledger-recon-buffer-name)))) + (when target-buffer + (switch-to-buffer-other-window target-buffer) + (ledger-navigate-to-line (cdr where)) + (forward-char) + (recenter) + (ledger-highlight-xact-under-point) + (forward-char -1) + (when (and come-back cur-win) + (select-window cur-win) + (get-buffer ledger-recon-buffer-name))))) (defun ledger-reconcile-save () @@ -273,7 +274,7 @@ and exit reconcile mode" (face (get-text-property (point) 'face))) (if (eq face 'ledger-font-reconciler-pending-face) (with-current-buffer (ledger-reconcile-get-buffer where) - (ledger-goto-line (cdr where)) + (ledger-navigate-to-line (cdr where)) (ledger-toggle-current 'cleared)))) (forward-line 1))) (ledger-reconcile-save) @@ -303,7 +304,7 @@ and exit reconcile mode" (with-current-buffer buf (remove-hook 'after-save-hook 'ledger-reconcile-refresh-after-save t) (when ledger-narrow-on-reconcile - (ledger-occur-quit-buffer buf) + (ledger-occur-mode -1) (ledger-highlight-xact-under-point)))))) (defun ledger-marker-where-xact-is (emacs-xact posting) @@ -319,7 +320,7 @@ POSTING is used in `ledger-clear-whole-transactions' is nil." (nth 0 posting))))) ;; return line-no of posting (defun ledger-reconcile-compile-format-string (fstr) - "return a function that implements the format string in fstr" + "Return a function that implements the format string in FSTR." (let (fields (start 0)) (while (string-match "(\\(.*?\\))" fstr start) @@ -332,6 +333,7 @@ POSTING is used in `ledger-clear-whole-transactions' is nil." (defun ledger-reconcile-format-posting (beg where fmt date code status payee account amount) + "Format posting for the reconcile buffer." (insert (funcall fmt date code status payee account amount)) ; Set face depending on cleared status @@ -348,6 +350,7 @@ POSTING is used in `ledger-clear-whole-transactions' is nil." 'where where)))) (defun ledger-reconcile-format-xact (xact fmt) + "Format XACT using FMT." (let ((date-format (or (cdr (assoc "date-format" ledger-environment-alist)) ledger-default-date-format))) (dolist (posting (nthcdr 5 xact)) @@ -364,7 +367,8 @@ POSTING is used in `ledger-clear-whole-transactions' is nil." (nth 2 posting)))))) ; amount (defun ledger-do-reconcile (&optional sort) - "Return the number of uncleared transactions in the account and display them in the *Reconcile* buffer." + "SORT the uncleared transactions in the account and display them in the *Reconcile* buffer. +Return a count of the uncleared transactions." (let* ((buf ledger-buf) (account ledger-acct) (ledger-success nil) @@ -399,9 +403,8 @@ POSTING is used in `ledger-clear-whole-transactions' is nil." (length xacts))) (defun ledger-reconcile-ensure-xacts-visible () - "Ensures that the last of the visible transactions in the -ledger buffer is at the bottom of the main window. The key to -this is to ensure the window is selected when the buffer point is + "Ensure the last of the visible transactions in the ledger buffer is at the bottom of the main window. +The key to this is to ensure the window is selected when the buffer point is moved and recentered. If they aren't strange things happen." (let ((recon-window (get-buffer-window (get-buffer ledger-recon-buffer-name)))) @@ -436,6 +439,13 @@ moved and recentered. If they aren't strange things happen." (set-window-buffer (split-window (get-buffer-window buf) nil nil) rbuf) (pop-to-buffer rbuf))) +(defun ledger-reconcile-check-valid-account (account) + "Check to see if ACCOUNT exists in the ledger file" + (if (> (length account) 0) + (save-excursion + (goto-char (point-min)) + (search-forward account nil t)))) + (defun ledger-reconcile () "Start reconciling, prompt for account." (interactive) @@ -443,37 +453,38 @@ moved and recentered. If they aren't strange things happen." (buf (current-buffer)) (rbuf (get-buffer ledger-recon-buffer-name))) - (add-hook 'after-save-hook 'ledger-reconcile-refresh-after-save nil t) - - (if rbuf ;; *Reconcile* already exists - (with-current-buffer rbuf - (set 'ledger-acct account) ;; already buffer local - (when (not (eq buf rbuf)) - ;; called from some other ledger-mode buffer - (ledger-reconcile-quit-cleanup) - (setq ledger-buf buf)) ;; should already be buffer-local - - (unless (get-buffer-window rbuf) - (ledger-reconcile-open-windows buf rbuf))) - - ;; no recon-buffer, starting from scratch. - - (with-current-buffer (setq rbuf - (get-buffer-create ledger-recon-buffer-name)) - (ledger-reconcile-open-windows buf rbuf) - (ledger-reconcile-mode) - (make-local-variable 'ledger-target) - (set (make-local-variable 'ledger-buf) buf) - (set (make-local-variable 'ledger-acct) account))) - - ;; Narrow the ledger buffer - (with-current-buffer rbuf - (save-excursion - (if ledger-narrow-on-reconcile - (ledger-occur-mode account ledger-buf))) - (if (> (ledger-reconcile-refresh) 0) - (ledger-reconcile-change-target)) - (ledger-display-balance)))) + (when (ledger-reconcile-check-valid-account account) + (add-hook 'after-save-hook 'ledger-reconcile-refresh-after-save nil t) + + (if rbuf ;; *Reconcile* already exists + (with-current-buffer rbuf + (set 'ledger-acct account) ;; already buffer local + (when (not (eq buf rbuf)) + ;; called from some other ledger-mode buffer + (ledger-reconcile-quit-cleanup) + (setq ledger-buf buf)) ;; should already be buffer-local + + (unless (get-buffer-window rbuf) + (ledger-reconcile-open-windows buf rbuf))) + + ;; no recon-buffer, starting from scratch. + + (with-current-buffer (setq rbuf + (get-buffer-create ledger-recon-buffer-name)) + (ledger-reconcile-open-windows buf rbuf) + (ledger-reconcile-mode) + (make-local-variable 'ledger-target) + (set (make-local-variable 'ledger-buf) buf) + (set (make-local-variable 'ledger-acct) account))) + + ;; Narrow the ledger buffer + (with-current-buffer rbuf + (save-excursion + (if ledger-narrow-on-reconcile + (ledger-occur account))) + (if (> (ledger-reconcile-refresh) 0) + (ledger-reconcile-change-target)) + (ledger-display-balance))))) (defvar ledger-reconcile-mode-abbrev-table) @@ -483,7 +494,8 @@ moved and recentered. If they aren't strange things happen." (setq ledger-target (ledger-read-commodity-string ledger-reconcile-target-prompt-string))) (defmacro ledger-reconcile-change-sort-key-and-refresh (sort-by) - `(lambda () + "Set the sort-key to SORT-BY." + `(lambda () (interactive) (setq ledger-reconcile-sort-key ,sort-by) diff --git a/lisp/ledger-regex.el b/lisp/ledger-regex.el index bb080b94..41231845 100644 --- a/lisp/ledger-regex.el +++ b/lisp/ledger-regex.el @@ -1,6 +1,6 @@ ;;; ledger-regex.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -26,10 +26,10 @@ (defconst ledger-amount-regex (concat "\\( \\|\t\\| \t\\)[ \t]*-?" - "\\([A-Z$€£_]+ *\\)?" + "\\([A-Z$€£₹_(]+ *\\)?" "\\(-?[0-9,\\.]+?\\)" - "\\(.[0-9]+\\)?" - "\\( *[[:word:]€£_\"]+\\)?" + "\\(.[0-9)]+\\)?" + "\\( *[[:word:]€£₹_\"]+\\)?" "\\([ \t]*[@={]@?[^\n;]+?\\)?" "\\([ \t]+;.+?\\|[ \t]*\\)?$")) @@ -329,7 +329,33 @@ ledger-iso-date-regexp "\\([ *!]+\\)" ;; mark "\\((.*)\\)?" ;; code - "\\(.*\\)" ;; desc + "\\([[:word:] ]+\\)" ;; desc "\\)")) +(defconst ledger-xact-start-regex + (concat "^" ledger-iso-date-regexp ;; subexp 1 + "\\(=" ledger-iso-date-regexp "\\)?" + )) + +(defconst ledger-xact-after-date-regex + (concat "\\([ \t]+[*!]\\)?" ;; mark, subexp 1 + "\\([ \t]+(.*?)\\)?" ;; code, subexp 2 + "\\([ \t]+[^;\n]+\\)" ;; desc, subexp 3 + "\\(;[^\n]*\\)?" ;; comment, subexp 4 + )) + +(defconst ledger-posting-regex + (concat "^[ \t]+ ?" ;; initial white space + "\\([*!]\\)? ?" ;; state, subexpr 1 + "\\([[:print:]]+\\([ \t][ \t]\\)\\)" ;; account, subexpr 2 + "\\([^;\n]*\\)" ;; amount, subexpr 4 + "\\(.*\\)" ;; comment, subexpr 5 + )) + + + +(defconst ledger-directive-start-regex + "[=~;#%|\\*[A-Za-z]") + + (provide 'ledger-regex) diff --git a/lisp/ledger-report.el b/lisp/ledger-report.el index 85f75212..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-2014 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. @@ -57,7 +57,8 @@ specifier." '(("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 @@ -70,6 +71,11 @@ text that should replace the format specifier." :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) @@ -81,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) @@ -95,10 +109,11 @@ text that should replace the format specifier." (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 [(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) + (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)] @@ -117,11 +132,11 @@ text that should replace the format specifier." "Ledger report menu" '("Reports" ["Save Report" ledger-report-save] - ["Edit Report" ledger-report-edit] + ["Edit Current Report" ledger-report-edit-report] + ["Edit All Reports" ledger-report-edit-reports] ["Re-run Report" ledger-report-redo] - ["Kill Report" ledger-report-kill] "---" - ["Reverse report order" ledger-report-reverse-lines] + ["Reverse report order" ledger-report-reverse-report] "---" ["Scroll Up" scroll-up] ["Visit Source" ledger-report-visit-source] @@ -133,11 +148,17 @@ text that should replace the format specifier." (define-derived-mode ledger-report-mode text-mode "Ledger-Report" "A mode for viewing ledger reports.") -(defun ledger-report-value-format-specifier () +(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. @@ -182,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)) @@ -197,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) @@ -288,7 +310,7 @@ 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) @@ -325,7 +347,7 @@ Optional EDIT the command." (save-excursion (find-file file) (widen) - (ledger-goto-line line) + (ledger-navigate-to-line line) (point-marker)))))) (add-text-properties (line-beginning-position) (line-end-position) (list 'face 'ledger-font-report-clickable-face)) @@ -367,16 +389,20 @@ Optional EDIT the command." (interactive) (let ((cur-buf (current-buffer))) (if (and ledger-report-auto-refresh - (string= (format-mode-line 'mode-name) "Ledger") - (get-buffer ledger-report-buffer-name)) + (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 () @@ -386,21 +412,21 @@ Optional EDIT the command." (set-window-configuration ledger-original-window-cfg) (kill-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))) - -(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)) @@ -410,7 +436,7 @@ 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)) diff --git a/lisp/ledger-schedule.el b/lisp/ledger-schedule.el index 8e2ab1f6..d66fdbab 100644 --- a/lisp/ledger-schedule.el +++ b/lisp/ledger-schedule.el @@ -22,7 +22,7 @@ ;;; Commentary: ;; ;; This module provides for automatically adding transactions to a -;; ledger buffer on a periodic basis. Recurrence expressions are +;; ledger buffer on a periodic basis. Recurrence expressions are ;; inspired by Martin Fowler's "Recurring Events for Calendars", ;; martinfowler.com/apsupp/recurring.pdf @@ -31,13 +31,16 @@ ;; function without have to use funcall. (require 'ledger-init) +(require 'cl) + +;;; Code: (defgroup ledger-schedule nil "Support for automatically recommendation transactions." :group 'ledger) (defcustom ledger-schedule-buffer-name "*Ledger Schedule*" - "Name for the schedule buffer" + "Name for the schedule buffer." :type 'string :group 'ledger-schedule) @@ -47,7 +50,7 @@ :group 'ledger-schedule) (defcustom ledger-schedule-look-forward 14 - "Number of days auto look forward to recommend transactions" + "Number of days auto look forward to recommend transactions." :type 'integer :group 'ledger-schedule) @@ -56,28 +59,40 @@ :type 'file :group 'ledger-schedule) -(defvar ledger-schedule-available nil) +(defcustom ledger-schedule-week-days '(("Mo" 1) + ("Tu" 2) + ("We" 3) + ("Th" 4) + ("Fr" 5) + ("Sa" 6) + ("Su" 7)) + "List of weekday abbreviations. There must be exactly seven +entries each with a two character abbreviation for a day and the +number of that day in the week. " + :type '(alist :value-type (group integer)) + :group 'ledger-schedule) (defsubst between (val low high) - (and (>= val low) (<= val high))) - -(defun ledger-schedule-check-available () - (setq ledger-schedule-available (and ledger-schedule-file - (file-exists-p ledger-schedule-file)))) + "Return TRUE if VAL > LOW and < HIGH." + (and (>= val low) (<= val high))) (defun ledger-schedule-days-in-month (month year) "Return number of days in the MONTH, MONTH is from 1 to 12. -If year is nil, assume it is not a leap year" +If YEAR is nil, assume it is not a leap year" (if (between month 1 12) (if (and year (date-leap-year-p year) (= 2 month)) 29 (nth (1- month) '(31 28 31 30 31 30 31 31 30 31 30 31))) (error "Month out of range, MONTH=%S" month))) +(defun ledger-schedule-encode-day-of-week (day-string) + "Return the numerical day of week corresponding to DAY-STRING." + (cadr (assoc day-string ledger-schedule-week-days))) + ;; Macros to handle date expressions (defun ledger-schedule-constrain-day-in-month (count day-of-week) - "Return a form that evaluates DATE that returns true for the COUNT DAY-OF-WEEK. + "Return a form that returns TRUE for the the COUNT DAY-OF-WEEK. For example, return true if date is the 3rd Thursday of the month. Negative COUNT starts from the end of the month. (EQ COUNT 0) means EVERY day-of-week (eg. every Saturday)" @@ -109,11 +124,11 @@ COUNT 0) means EVERY day-of-week (eg. every Saturday)" day-of-week))) (defun ledger-schedule-constrain-every-count-day (day-of-week skip start-date) - "Return a form that is true for every DAY skipping SKIP, starting on START. + "Return a form that is true for every DAY-OF-WEEK skipping SKIP, starting on START-DATE. For example every second Friday, regardless of month." - (let ((start-day (nth 6 (decode-time (eval start-date))))) + (let ((start-day (nth 6 (decode-time start-date)))) (if (eq start-day day-of-week) ;; good, can proceed - `(zerop (mod (- (time-to-days date) ,(time-to-days (eval start-date))) ,(* skip 7))) + `(zerop (mod (- (time-to-days date) ,(time-to-days start-date)) ,(* skip 7))) (error "START-DATE day of week doesn't match DAY-OF-WEEK")))) (defun ledger-schedule-constrain-date-range (month1 day1 month2 day2) @@ -130,12 +145,10 @@ For example every second Friday, regardless of month." (< ,target-day ,day2)))))) -(defun ledger-schedule-is-holiday (date) - "Return true if DATE is a holiday.") (defun ledger-schedule-scan-transactions (schedule-file) - "Scans AUTO_FILE and returns a list of transactions with date predicates. -The car of each item is a fuction of date that returns true if + "Scan SCHEDULE-FILE and return a list of transactions with date predicates. +The car of each item is a function of date that returns true if the transaction should be logged for that day." (interactive "fFile name: ") (let ((xact-list (list))) @@ -146,67 +159,27 @@ the transaction should be logged for that day." (let ((date-descriptor "") (transaction nil) (xact-start (match-end 0))) - (setq date-descriptors + (setq date-descriptor (ledger-schedule-read-descriptor-tree (buffer-substring-no-properties (match-beginning 0) (match-end 0)))) (forward-paragraph) - (setq transaction (list date-descriptors + (setq transaction (list date-descriptor (buffer-substring-no-properties xact-start (point)))) (setq xact-list (cons transaction xact-list)))) xact-list))) -(defun ledger-schedule-replace-brackets () - "Replace all brackets with parens" - (goto-char (point-min)) - (while (search-forward "]" nil t) - (replace-match ")" nil t)) - (goto-char (point-min)) - (while (search-forward "[" nil t) - (replace-match "(" nil t))) - -(defvar ledger-schedule-descriptor-regex - (concat "\\(20[0-9][0-9]\\|[\*]\\)[/\\-]" ;; Year slot - "\\([\*EO]\\|[01][0-9]\\)[/\\-]" ;; Month slot - "\\([\*]\\|\\([0-3][0-9]\\)\\|" - "\\([0-5]" - "\\(\\(Su\\)\\|" - "\\(Mo\\)\\|" - "\\(Tu\\)\\|" - "\\(We\\)\\|" - "\\(Th\\)\\|" - "\\(Fr\\)\\|" - "\\(Sa\\)\\)\\)\\)")) - (defun ledger-schedule-read-descriptor-tree (descriptor-string) - "Take a date DESCRIPTOR-STRING and return a function of date that -returns true if the date meets the requirements" - (with-temp-buffer - ;; copy the descriptor string into a temp buffer for manipulation - (let (pos) - ;; Replace brackets with parens - (insert descriptor-string) - (ledger-schedule-replace-brackets) - - (goto-char (point-max)) - ;; double quote all the descriptors for string processing later - (while (re-search-backward ledger-schedule-descriptor-regex nil t) ;; Day slot - (goto-char - (match-end 0)) - (insert ?\") - (goto-char (match-beginning 0)) - (insert "\"" ))) - - ;; read the descriptor string into a lisp object the transform the - ;; string descriptor into useable things - (ledger-schedule-transform-auto-tree - (read (buffer-substring-no-properties (point-min) (point-max)))))) + "Read DESCRIPTOR-STRING and return a form that evaluates dates." + (ledger-schedule-transform-auto-tree + (split-string + (substring descriptor-string 1 (string-match "]" descriptor-string)) " "))) (defun ledger-schedule-transform-auto-tree (descriptor-string-list) - "Takes a lisp list of date descriptor strings, TREE, and returns a string with a lambda function of date." + "Take DESCRIPTOR-STRING-LIST, and return a string with a lambda function of date." ;; use funcall to use the lambda function spit out here (if (consp descriptor-string-list) (let (result) @@ -221,70 +194,92 @@ returns true if the date meets the requirements" (push (ledger-schedule-compile-constraints newcar) result)) ) (setq descriptor-string-list (cdr descriptor-string-list))) - ;; tie up all the clauses in a big or and lambda, and return + ;; tie up all the clauses in a big or lambda, and return ;; the lambda function as list to be executed by funcall `(lambda (date) ,(nconc (list 'or) (nreverse result) descriptor-string-list))))) (defun ledger-schedule-compile-constraints (descriptor-string) - "Return a list with the year, month and day fields split" - (let ((fields (split-string descriptor-string "[/\\-]" t)) - constrain-year constrain-month constrain-day) - (setq constrain-year (ledger-schedule-constrain-year (nth 0 fields))) - (setq constrain-month (ledger-schedule-constrain-month (nth 1 fields))) - (setq constrain-day (ledger-schedule-constrain-day (nth 2 fields))) - - (list 'and constrain-year constrain-month constrain-day))) - -(defun ledger-schedule-constrain-year (str) - (let ((year-match t)) - (cond ((string= str "*") - year-match) - ((/= 0 (setq year-match (string-to-number str))) - `(eq (nth 5 (decode-time date)) ,year-match)) - (t - (error "Improperly specified year constraint: %s" str))))) - -(defun ledger-schedule-constrain-month (str) - - (let ((month-match t)) - (cond ((string= str "*") - month-match) ;; always match - ((/= 0 (setq month-match (string-to-number str))) - (if (between month-match 1 12) ;; no month specified, assume 31 days. - `(eq (nth 4 (decode-time date)) ,month-match) - (error "ledger-schedule-constrain-numerical-month: month out of range %S" month-match))) - (t - (error "Improperly specified month constraint: %s" str))))) - -(defun ledger-schedule-constrain-day (str) - (let ((day-match t)) - (cond ((string= str "*") - t) - ((/= 0 (setq day-match (string-to-number str))) - `(eq (nth 3 (decode-time date)) ,day-match)) - (t - (error "Improperly specified day constraint: %s" str))))) - -(defun ledger-schedule-parse-date-descriptor (descriptor) - "Parse the date descriptor, return the evaluator" - (ledger-schedule-compile-constraints descriptor)) + "Return a list with the year, month and day fields split." + (let ((fields (split-string descriptor-string "[/\\-]" t))) + (if (string-match "[A-Za-z]" descriptor-string) + (ledger-schedule-constrain-day (nth 0 fields) (nth 1 fields) (nth 2 fields)) + (list 'and + (ledger-schedule-constrain-day (nth 0 fields) (nth 1 fields) (nth 2 fields)) + (ledger-schedule-constrain-year (nth 0 fields) (nth 1 fields) (nth 2 fields)) + (ledger-schedule-constrain-month (nth 0 fields) (nth 1 fields) (nth 2 fields)))))) + +(defun ledger-schedule-constrain-year (year-desc month-desc day-desc) + "Return a form that constrains the year. + +YEAR-DESC, MONT-DESC, and DAY-DESC are the string portions of the +date descriptor." + (cond ((string= year-desc "*") t) + ((/= 0 (string-to-number year-desc)) + `(memq (nth 5 (decode-time date)) ',(mapcar 'string-to-number (split-string year-desc ",")))) + (t + (error "Improperly specified year constraint: %s %s %s" year-desc month-desc day-desc)))) + +(defun ledger-schedule-constrain-month (year-desc month-desc day-desc) + "Return a form that constrains the month. + +YEAR-DESC, MONT-DESC, and DAY-DESC are the string portions of the +date descriptor." + (cond ((string= month-desc "*") + t) ;; always match + ((string= month-desc "E") ;; Even + `(evenp (nth 4 (decode-time date)))) + ((string= month-desc "O") ;; Odd + `(oddp (nth 4 (decode-time date)))) + ((/= 0 (string-to-number month-desc)) ;; Starts with number + `(memq (nth 4 (decode-time date)) ',(mapcar 'string-to-number (split-string month-desc ",")))) + (t + (error "Improperly specified month constraint: %s %s %s" year-desc month-desc day-desc)))) + +(defun ledger-schedule-constrain-day (year-desc month-desc day-desc) + "Return a form that constrains the day. + +YEAR-DESC, MONT-DESC, and DAY-DESC are the string portions of the +date descriptor." + (cond ((string= day-desc "*") + t) + ((string-match "[A-Za-z]" day-desc) ;; There is something other than digits and commas + (ledger-schedule-parse-complex-date year-desc month-desc day-desc)) + ((/= 0 (string-to-number day-desc)) + `(memq (nth 3 (decode-time date)) ',(mapcar 'string-to-number (split-string day-desc ",")))) + (t + (error "Improperly specified day constraint: %s %s %s" year-desc month-desc day-desc)))) + + + +(defun ledger-schedule-parse-complex-date (year-desc month-desc day-desc) + "Parse day descriptors that have repeats." + (let ((years (mapcar 'string-to-number (split-string year-desc ","))) + (months (mapcar 'string-to-number (split-string month-desc ","))) + (day-parts (split-string day-desc "+")) + (every-nth (string-match "+" day-desc))) + (if every-nth + (let ((base-day (string-to-number (car day-parts))) + (increment (string-to-number (substring (cadr day-parts) 0 + (string-match "[A-Za-z]" (cadr day-parts))))) + (day-of-week (ledger-schedule-encode-day-of-week + (substring (cadr day-parts) (string-match "[A-Za-z]" (cadr day-parts)))))) + (ledger-schedule-constrain-every-count-day day-of-week increment (encode-time 0 0 0 base-day (car months) (car years)))) + (let ((count (string-to-number (substring (car day-parts) 0 1))) + (day-of-week (ledger-schedule-encode-day-of-week + (substring (car day-parts) (string-match "[A-Za-z]" (car day-parts)))))) + (ledger-schedule-constrain-day-in-month count day-of-week))))) (defun ledger-schedule-list-upcoming-xacts (candidate-items early horizon) - "Search CANDIDATE-ITEMS for xacts that occur within the period today - EARLY to today + HORIZON" - (let ((start-date (time-subtract (current-time) (days-to-time early))) - test-date items) - (loop for day from 0 to (+ early horizon) by 1 do - (setq test-date (time-add start-date (days-to-time day))) - (dolist (candidate candidate-items items) - (if (funcall (car candidate) test-date) - (setq items (append items (list (list test-date (cadr candidate)))))))) - items)) - -(defun ledger-schedule-already-entered (candidate buffer) - (let ((target-date (format-time-string date-format (car candidate))) - (target-payee (cadr candidate))) - nil)) + "Search CANDIDATE-ITEMS for xacts that occur within the period today - EARLY to today + HORIZON." + (let ((start-date (time-subtract (current-time) (days-to-time early))) + test-date items) + (loop for day from 0 to (+ early horizon) by 1 do + (setq test-date (time-add start-date (days-to-time day))) + (dolist (candidate candidate-items items) + (if (funcall (car candidate) test-date) + (setq items (append items (list (list test-date (cadr candidate)))))))) + items)) (defun ledger-schedule-create-auto-buffer (candidate-items early horizon ledger-buf) "Format CANDIDATE-ITEMS for display." @@ -295,13 +290,12 @@ returns true if the date meets the requirements" (with-current-buffer schedule-buf (erase-buffer) (dolist (candidate candidates) - (if (not (ledger-schedule-already-entered candidate ledger-buf)) - (insert (format-time-string date-format (car candidate) ) " " (cadr candidate) "\n"))) + (insert (format-time-string date-format (car candidate) ) " " (cadr candidate) "\n")) (ledger-mode)) (length candidates))) (defun ledger-schedule-upcoming (file look-backward look-forward) - "Generate upcoming transaction + "Generate upcoming transactions. FILE is the file containing the scheduled transaction, default to `ledger-schedule-file'. @@ -316,12 +310,16 @@ Use a prefix arg to change the default value" (read-number "Look backward: " ledger-schedule-look-backward) (read-number "Look forward: " ledger-schedule-look-forward)) (list ledger-schedule-file ledger-schedule-look-backward ledger-schedule-look-forward))) - (ledger-schedule-create-auto-buffer - (ledger-schedule-scan-transactions file) - look-backward - look-forward - (current-buffer)) - (pop-to-buffer ledger-schedule-buffer-name)) + (if (and file + (file-exists-p file)) + (progn + (ledger-schedule-create-auto-buffer + (ledger-schedule-scan-transactions file) + look-backward + look-forward + (current-buffer)) + (pop-to-buffer ledger-schedule-buffer-name)) + (error "Could not find ledger schedule file at %s" file))) (provide 'ledger-schedule) diff --git a/lisp/ledger-sort.el b/lisp/ledger-sort.el index 80472a35..870e298c 100644 --- a/lisp/ledger-sort.el +++ b/lisp/ledger-sort.el @@ -1,6 +1,6 @@ ;;; ledger-xact.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -26,25 +26,19 @@ ;;; Code: -(defun ledger-next-record-function () - "Move point to next transaction." - (if (re-search-forward ledger-payee-any-status-regex nil t) - (goto-char (match-beginning 0)) - (goto-char (point-max)))) - -(defun ledger-end-record-function () - "Move point to end of transaction." - (forward-paragraph)) (defun ledger-sort-find-start () + "Find the beginning of a sort region" (if (re-search-forward ";.*Ledger-mode:.*Start sort" nil t) (match-end 0))) (defun ledger-sort-find-end () + "Find the end of a sort region" (if (re-search-forward ";.*Ledger-mode:.*End sort" nil t) (match-end 0))) (defun ledger-sort-insert-start-mark () + "Insert a marker to start a sort region" (interactive) (save-excursion (goto-char (point-min)) @@ -54,6 +48,7 @@ (insert "\n; Ledger-mode: Start sort\n\n")) (defun ledger-sort-insert-end-mark () + "Insert a marker to end a sort region" (interactive) (save-excursion (goto-char (point-min)) @@ -69,11 +64,11 @@ (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 - ;; automagically + ;; automagically (let ((new-beg beg) (new-end end) point-delta - (bounds (ledger-find-xact-extents (point))) + (bounds (ledger-navigate-find-xact-extents (point))) target-xact) (setq point-delta (- (point) (car bounds))) @@ -82,12 +77,14 @@ (save-excursion (save-restriction (goto-char beg) - (ledger-next-record-function) ;; make sure point is at the - ;; beginning of a xact + ;; make sure point is at the beginning of a xact + (ledger-navigate-next-xact) + (unless (looking-at ledger-payee-any-status-regex) + (ledger-navigate-next-xact)) (setq new-beg (point)) (goto-char end) - (ledger-next-record-function) ;; make sure end of region is at - ;; the beginning of next record + (ledger-navigate-next-xact) + ;; make sure end of region is at the beginning of next record ;; after the region (setq new-end (point)) (narrow-to-region new-beg new-end) @@ -96,8 +93,8 @@ (let ((inhibit-field-text-motion t)) (sort-subr nil - 'ledger-next-record-function - 'ledger-end-record-function + 'ledger-navigate-next-xact + 'ledger-navigate-end-of-xact 'ledger-sort-startkey)))) (goto-char (point-min)) diff --git a/lisp/ledger-state.el b/lisp/ledger-state.el index 989e6d33..47805f15 100644 --- a/lisp/ledger-state.el +++ b/lisp/ledger-state.el @@ -1,6 +1,6 @@ ;;; ledger-state.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -65,6 +65,16 @@ ((eql state-char ?\;) 'comment) (t nil))) + +(defun ledger-state-from-string (state-string) + "Get state from STATE-CHAR." + (when state-string + (cond + ((string-match "\\!" state-string) 'pending) + ((string-match "\\*" state-string) 'cleared) + ((string-match ";" state-string) 'comment) + (t nil)))) + (defun ledger-toggle-current-posting (&optional style) "Toggle the cleared status of the transaction under point. Optional argument STYLE may be `pending' or `cleared', depending @@ -77,7 +87,7 @@ achieved more certainly by passing the xact to ledger for formatting, but doing so causes inline math expressions to be dropped." (interactive) - (let ((bounds (ledger-find-xact-extents (point))) + (let ((bounds (ledger-navigate-find-xact-extents (point))) new-status cur-status) ;; Uncompact the xact, to make it easier to toggle the ;; transaction diff --git a/lisp/ledger-test.el b/lisp/ledger-test.el index 5f9f02fa..da120f63 100644 --- a/lisp/ledger-test.el +++ b/lisp/ledger-test.el @@ -1,6 +1,6 @@ ;;; ledger-test.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -19,6 +19,16 @@ ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ;; MA 02110-1301 USA. +;;; Commentary: + +;;; Code: + +(declare-function ledger-mode "ledger-mode") ; TODO: fix this cyclic dependency +(declare-function org-narrow-to-subtree "org") +(declare-function org-entry-get "org") +(declare-function outline-back-to-heading "outline") +(declare-function outline-next-heading "outline") + (defgroup ledger-test nil "Definitions for the Ledger testing framework" :group 'ledger) @@ -125,3 +135,5 @@ (cd prev-directory))))))) (provide 'ledger-test) + +;;; ledger-test.el ends here diff --git a/lisp/ledger-texi.el b/lisp/ledger-texi.el index 746051bf..afaf0df7 100644 --- a/lisp/ledger-texi.el +++ b/lisp/ledger-texi.el @@ -1,6 +1,6 @@ ;;; ledger-texi.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. diff --git a/lisp/ledger-xact.el b/lisp/ledger-xact.el index e747b6b2..0eb9386a 100644 --- a/lisp/ledger-xact.el +++ b/lisp/ledger-xact.el @@ -1,6 +1,6 @@ ;;; ledger-xact.el --- Helper code for use with the "ledger" command-line tool -;; Copyright (C) 2003-2014 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. @@ -25,6 +25,11 @@ ;;; Code: +(require 'eshell) +(require 'ledger-regex) +(require 'ledger-navigate) +;; TODO: This file depends on code in ledger-mode.el, which depends on this. + (defcustom ledger-highlight-xact-under-point t "If t highlight xact under point." :type 'boolean @@ -39,26 +44,10 @@ (defvar ledger-xact-highlight-overlay (list)) (make-variable-buffer-local 'ledger-xact-highlight-overlay) -(defun ledger-find-xact-extents (pos) - "Return point for beginning of xact and and of xact containing position. -Requires empty line separating xacts. Argument POS is a location -within the transaction." - (interactive "d") - (save-excursion - (goto-char pos) - (list (progn - (backward-paragraph) - (if (/= (point) (point-min)) - (forward-line)) - (line-beginning-position)) - (progn - (forward-paragraph) - (line-beginning-position))))) - (defun ledger-highlight-xact-under-point () "Move the highlight overlay to the current transaction." (if ledger-highlight-xact-under-point - (let ((exts (ledger-find-xact-extents (point))) + (let ((exts (ledger-navigate-find-element-extents (point))) (ovl ledger-xact-highlight-overlay)) (if (not ledger-xact-highlight-overlay) (setq ovl @@ -68,7 +57,7 @@ within the transaction." (current-buffer) t nil))) (move-overlay ovl (car exts) (cadr exts))) (overlay-put ovl 'face 'ledger-font-xact-highlight-face) - (overlay-put ovl 'priority 100)))) + (overlay-put ovl 'priority '(nil . 99))))) (defun ledger-xact-payee () "Return the payee of the transaction containing point or nil." @@ -98,7 +87,7 @@ MOMENT is an encoded date" (if (ledger-time-less-p moment date) (throw 'found t)))))) (when (and (eobp) last-xact-start) - (let ((end (cadr (ledger-find-xact-extents last-xact-start)))) + (let ((end (cadr (ledger-navigate-find-xact-extents last-xact-start)))) (goto-char end) (if (eobp) (insert "\n") @@ -129,11 +118,6 @@ MOMENT is an encoded date" mark desc))))) (forward-line)))) -(defun ledger-goto-line (line-number) - "Rapidly move point to line LINE-NUMBER." - (goto-char (point-min)) - (forward-line (1- line-number))) - (defun ledger-year-and-month () (let ((sep (if ledger-use-iso-dates "-" @@ -145,7 +129,7 @@ MOMENT is an encoded date" (interactive (list (ledger-read-date "Copy to date: "))) (let* ((here (point)) - (extents (ledger-find-xact-extents (point))) + (extents (ledger-navigate-find-xact-extents (point))) (transaction (buffer-substring-no-properties (car extents) (cadr extents))) encoded-date) (if (string-match ledger-iso-date-regexp date) @@ -155,7 +139,7 @@ MOMENT is an encoded date" (string-to-number (match-string 2 date))))) (ledger-xact-find-slot encoded-date) (insert transaction "\n") - (backward-paragraph 2) + (ledger-navigate-beginning-of-xact) (re-search-forward ledger-iso-date-regexp) (replace-match date) (ledger-next-amount) @@ -163,9 +147,9 @@ MOMENT is an encoded date" (goto-char (match-beginning 0))))) (defun ledger-delete-current-transaction (pos) - "Delete the transaction surrounging point." + "Delete the transaction surrounging POS." (interactive "d") - (let ((bounds (ledger-find-xact-extents pos))) + (let ((bounds (ledger-navigate-find-xact-extents pos))) (delete-region (car bounds) (cadr bounds)))) (defun ledger-add-transaction (transaction-text &optional insert-at-point) @@ -207,7 +191,6 @@ correct chronological place in the buffer." (insert (car args) " \n\n") (end-of-line -1))))) - (provide 'ledger-xact) ;;; ledger-xact.el ends here |