I finally got around to merging the patches I used to use with the old
(minor-mode) version of beancount.el into the new version.
Most of the tweaks I was using aren't needed any more so all that's left
is the patch below:
- Remove the redundant `:group`s (since they default to the last
`defgroup` anyway).
- New var `beancount-electricity` to control behavior of RET.
When non-nil, RET at the end of a leg adds the currency if missing (or
tries to anyway: I think it's done in a too naive way which doesn't
account for the many ways to specify the amount).
- Comment out a `defvar` of `beancount-directive-names` that doesn't do
anything since it's preceded by a `defconst` of the same var.
- Add a M-RET binding that inserts the date part of a new transaction.
- Fix `beancount-collect`: the old code collects all matches and then
its callers removed the entry found at point (because that entry is
the one we're in the process of completing so it's presumably not
(yet) valid) but that misbehaves when the entry at point is actually
already valid, in which case that entry is incorrectly removed from
the possible completions. So instead I changed `beancount-collect` so
it ignores matches around point which usually gives the same result
except when the match around point is *also* found elsewhere (in which
case the new code keeps that completion whereas the old one
incorrectly discarded it).
Stefan
diff --git a/editors/emacs/beancount.el b/editors/emacs/beancount.el
index 37e7eed0..d1a8cf22 100644
--- a/editors/emacs/beancount.el
+++ b/editors/emacs/beancount.el
@@ -40,82 +40,72 @@
(defcustom beancount-transaction-indent 2
"Transaction indent."
- :type 'integer
- :group 'beancount)
+ :type 'integer)
(defcustom beancount-number-alignment-column 52
"Column to which align numbers in postinng definitions. Set to
0 to automatically determine the minimum column that will allow
to align all amounts."
- :type 'integer
- :group 'beancount)
+ :type 'integer)
(defcustom beancount-highlight-transaction-at-point nil
"If t highlight transaction under point."
- :type 'boolean
- :group 'beancount)
+ :type 'boolean)
(defcustom beancount-use-ido t
"If non-nil, use ido-style completion rather than the standard."
- :type 'boolean
- :group 'beancount)
+ :type 'boolean)
+
+(defcustom beancount-electricity t
+ "If non-nil, make some self-inserting keys electric.
+Currently, only `newline' is electric, to add missing currency."
+ :type 'boolean)
(defgroup beancount-faces nil "Beancount mode highlighting" :group 'beancount)
(defface beancount-directive
`((t :inherit font-lock-keyword-face))
- "Face for Beancount directives."
- :group 'beancount-faces)
+ "Face for Beancount directives.")
(defface beancount-tag
`((t :inherit font-lock-type-face))
- "Face for Beancount tags."
- :group 'beancount-faces)
+ "Face for Beancount tags.")
(defface beancount-link
`((t :inherit font-lock-type-face))
- "Face for Beancount links."
- :group 'beancount-faces)
+ "Face for Beancount links.")
(defface beancount-date
`((t :inherit font-lock-constant-face))
- "Face for Beancount dates."
- :group 'beancount-faces)
+ "Face for Beancount dates.")
(defface beancount-account
`((t :inherit font-lock-builtin-face))
- "Face for Beancount account names."
- :group 'beancount-faces)
+ "Face for Beancount account names.")
(defface beancount-amount
`((t :inherit font-lock-default-face))
- "Face for Beancount amounts."
- :group 'beancount-faces)
+ "Face for Beancount amounts.")
(defface beancount-narrative
`((t :inherit font-lock-builtin-face))
- "Face for Beancount transactions narrative."
- :group 'beancount-faces)
+ "Face for Beancount transactions narrative.")
(defface beancount-narrative-cleared
`((t :inherit font-lock-string-face))
- "Face for Beancount cleared transactions narrative."
- :group 'beancount-faces)
+ "Face for Beancount cleared transactions narrative.")
(defface beancount-narrative-pending
`((t :inherit font-lock-keyword-face))
- "Face for Beancount pending transactions narrative."
- :group 'beancount-faces)
+ "Face for Beancount pending transactions narrative.")
(defface beancount-metadata
`((t :inherit font-lock-type-face))
- "Face for Beancount metadata."
- :group 'beancount-faces)
+ "Face for Beancount metadata.")
(defface beancount-highlight
`((t :inherit highlight))
- "Face to highlight Beancount transaction at point."
- :group 'beancount-faces)
+ "Face to highlight Beancount transaction at point.")
(defconst beancount-account-directive-names
'("balance"
@@ -147,10 +137,10 @@ to align all amounts."
"pushtag")
"Directive names that can appear at the beginning of a line.")
-(defvar beancount-directive-names
- (append beancount-directive-names
- beancount-timestamped-directive-names)
- "A list of the directive names.")
+;; (defvar beancount-directive-names
+;; (append beancount-directive-names
+;; beancount-timestamped-directive-names)
+;; "A list of the directive names.")
(defconst beancount-account-categories
'("Assets" "Liabilities" "Equity" "Income" "Expenses"))
@@ -289,6 +279,7 @@ to align all amounts."
(let ((map (make-sparse-keymap))
(p beancount-mode-map-prefix))
(define-key map (kbd "TAB") #'beancount-tab-dwim)
+ (define-key map [?\M-\C-m] #'beancount-insert-entry)
(define-key map (vconcat p [(\')]) #'beancount-insert-account)
(define-key map (vconcat p [(control g)]) #'beancount-transaction-clear)
(define-key map (vconcat p [(l)]) #'beancount-check)
@@ -330,8 +321,10 @@ to align all amounts."
(setq-local completion-ignore-case t)
(add-hook 'completion-at-point-functions #'beancount-completion-at-point nil
t)
(add-hook 'post-command-hook #'beancount-highlight-transaction-at-point nil
t)
-
+
+ (add-hook 'post-self-insert-hook #'beancount--electric nil t)
+
(setq-local font-lock-defaults '(beancount-font-lock-keywords))
(setq-local font-lock-syntax-table t)
@@ -438,7 +432,8 @@ With an argument move to the next non cleared transaction."
((beancount-looking-at
(concat "^" beancount-date-regexp
"\\s-+" (regexp-opt beancount-account-directive-names)
- "\\s-+\\([" beancount-account-chars "]*\\)") 1 pos)
+ "\\s-+\\([" beancount-account-chars "]*\\)")
+ 1 pos)
(setq beancount-accounts nil)
(list (match-beginning 1) (match-end 1)
#'beancount-account-completion-table))
@@ -462,7 +457,7 @@ With an argument move to the next non cleared transaction."
(lambda (string pred action)
(if (null candidates)
(setq candidates
- (sort (delete string (beancount-collect regexp
1)) #'string<)))
+ (sort (beancount-collect regexp 1) #'string<)))
(complete-with-action action candidates string pred))))
(list (match-beginning 1) (match-end 1) completion-table)))
@@ -475,25 +470,29 @@ With an argument move to the next non cleared
transaction."
(lambda (string pred action)
(if (null candidates)
(setq candidates
- (sort (delete string (beancount-collect regexp
1)) #'string<)))
+ (sort (beancount-collect regexp 1) #'string<)))
(complete-with-action action candidates string pred))))
(list (match-beginning 1) (match-end 1) completion-table))))))))
(defun beancount-collect (regexp n)
"Return an unique list of REGEXP group N in the current buffer."
- (save-excursion
- (save-match-data
- (let ((hash (make-hash-table :test 'equal)))
- (goto-char (point-min))
- (while (re-search-forward regexp nil t)
- (puthash (match-string-no-properties n) nil hash))
- (hash-table-keys hash)))))
+ (let ((pos (point)))
+ (save-excursion
+ (save-match-data
+ (let ((hash (make-hash-table :test 'equal)))
+ (goto-char (point-min))
+ (while (re-search-forward regexp nil t)
+ ;; Ignore matches around `pos' since that's presumably
+ ;; what we're currently trying to complete!
+ (unless (<= (match-beginning 0) pos (match-end 0))
+ (puthash (match-string-no-properties n) nil hash)))
+ (hash-table-keys hash))))))
(defun beancount-account-completion-table (string pred action)
(if (eq action 'metadata) '(metadata (category . beancount-account))
(if (null beancount-accounts)
(setq beancount-accounts
- (sort (delete string (beancount-collect beancount-account-regexp
0)) #'string<)))
+ (sort (beancount-collect beancount-account-regexp 0) #'string<)))
(complete-with-action action beancount-accounts string pred)))
;; Default to substring completion for beancount accounts.
@@ -750,6 +749,46 @@ what that column is and returns it (an integer)."
))
column))
+(defun beancount--account-currency (account)
+ (save-excursion
+ (goto-char (point-min))
+ (when (re-search-forward (concat "^[0-9-]+[ \t]+open[ \t]+"
+ (regexp-quote account)
+ "[ \t]+\\([[:upper:]]+\\)[ \t]*"
+ "\\(?:$\\|;\\)")
+ nil t)
+ ;; The account has declared a single currency, so we can fill it in.
+ (match-string 1))))
+
+(defun beancount--electric ()
+ ;; TODO: When hitting RET after the first leg of a txn, look back
+ ;; for similar transactions and insert the matching account!
+ (when (and beancount-electricity (eq last-command-event ?\n))
+ (cond
+ ;; ((save-excursion (forward-line -1) (looking-at beancount--txn-re))
+ ;; ;; FIXME: Provide an indent-line-function instead!
+ ;; ;; TODO: Auto-align the amount (probably use a
beancount-amount-column).
+ ;; (indent-line-to 2))
+ ((save-excursion (forward-line -1)
+ (and (beancount-inside-transaction-p)
+ (looking-at (concat "[ \t]+\\(["
+ beancount-account-chars
+ "]+\\)[ \t]+-?[0-9.]+[
\t]*$"))))
+ ;; Last line is a leg without currency.
+ (let* ((account (match-string 1))
+ (pos (match-end 0))
+ (currency (beancount--account-currency account)))
+ (when currency
+ (save-excursion
+ (goto-char pos)
+ (insert " " currency))))))))
+
+(defun beancount-insert-entry ()
+ "Start a new entry."
+ (interactive)
+ (unless (bolp) (newline))
+ (insert (format-time-string "%Y-%m-%d") " "))
+
(defvar beancount-install-dir nil
"Directory in which Beancount's source is located.
Only useful if you have not installed Beancount properly in your PATH.")
--
You received this message because you are subscribed to the Google Groups
"Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/beancount/jwva77qh8da.fsf-monnier%2Bgmane.comp.finance.beancount%40gnu.org.