branch: elpa/beancount
commit ba6bada870eb33f9444a9e39a1eedd420cd870d4
Author: Daniele Nicolodi <[email protected]>
Commit: Daniele Nicolodi <[email protected]>
beancount.el: Rework imenu support and add tests
In org-mode the imemu entries are organized in a tree, however this
only allows to jump to leaf nodes, with no possibility to select
intermediate nodes. To avoid this problem, follow what outline-mode
does and organize the imenu entries in a flat structure. To make it
easier to select the entries in the minibuffer, the outline indicators
are removed from the node labels.
---
beancount-tests.el | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
beancount.el | 32 ++++----------------------------
2 files changed, 58 insertions(+), 28 deletions(-)
diff --git a/beancount-tests.el b/beancount-tests.el
index 0776df2d2e..7dc15e0c31 100644
--- a/beancount-tests.el
+++ b/beancount-tests.el
@@ -249,3 +249,57 @@ known option nmaes."
(should (equal (beancount--account-currency "Assets:Test:Three") "USD"))
(should (equal (beancount--account-currency "Assets:Test:Four") nil))
(should (equal (beancount--account-currency "Assets:Test:Five") nil))))
+
+(ert-deftest beancount/imenu-001 ()
+ :tags '(regress imenu)
+ (with-temp-buffer
+ (insert "
+;;; 2019
+;;;; 2019 January
+;;;; 2019 February
+;;; 2020
+;;;; 2020 January
+
+2020-01-01 * \"Example\"
+ Expenses:Test 1.00 USD
+ Assets:Checking
+
+;;;; 2020 February
+")
+ (beancount-mode)
+ (outline-minor-mode)
+ (let* ((imenu-use-markers nil) ; makes testing easier
+ (index (funcall imenu-create-index-function)))
+ (should (equal index '(("2019" . 2)
+ ("2019 January" . 11)
+ ("2019 February" . 29)
+ ("2020" . 48)
+ ("2020 January" . 57)
+ ("2020 February" . 146)))))))
+
+(ert-deftest beancount/imenu-002 ()
+ :tags '(regress imenu)
+ (with-temp-buffer
+ (insert "
+* 2019
+** 2019 January
+
+2019-01-01 * \"Example\"
+ Expenses:Test 1.00 USD
+ Assets:Checking
+
+** 2019 February
+* 2020
+** 2020 January
+** 2020 February
+")
+ (beancount-mode)
+ (outline-minor-mode)
+ (let* ((imenu-use-markers nil) ; makes testing easier
+ (index (funcall imenu-create-index-function)))
+ (should (equal index '(("2019" . 2)
+ ("2019 January" . 9)
+ ("2019 February" . 96)
+ ("2020" . 113)
+ ("2020 January" . 120)
+ ("2020 February" . 136)))))))
diff --git a/beancount.el b/beancount.el
index 08fbdde4ea..9ac5c1484d 100644
--- a/beancount.el
+++ b/beancount.el
@@ -214,6 +214,8 @@ from the open directive for the relevant account."
(defconst beancount-metadata-regexp
"^\\s-+\\([a-z][A-Za-z0-9_-]+:\\)\\s-+\\(.+\\)")
+;; This is a grouping regular expression because the subexpression is
+;; used in determining the outline level in `beancount-outline-level'.
(defvar beancount-outline-regexp "\\(;;;+\\|\\*+\\)")
(defun beancount-outline-level ()
@@ -326,7 +328,8 @@ from the open directive for the relevant account."
(setq-local outline-regexp beancount-outline-regexp)
(setq-local outline-level #'beancount-outline-level)
- (setq-local imenu-create-index-function
#'beancount-imenu-create-index-function))
+ (setq imenu-generic-expression
+ (list (list nil (concat "^" beancount-outline-regexp "\\s-+\\(.*\\)$")
2))))
(defun beancount-collect-pushed-tags (begin end)
"Return list of all pushed (and not popped) tags in the region."
@@ -1011,32 +1014,5 @@ Essentially a much simplified version of `next-line'."
(get-char-property (1- (point)) 'invisible))
(beginning-of-line 2)))
-;;; imenu support
-(defun beancount-imenu-create-index-function ()
- "The `imenu-create-index-function' for beancount-mode that returns an
-`imenu--index-alist' that stores the headings in the buffer."
- (let ((index-alist '()))
- (goto-char (point-max))
- (while (re-search-backward "^\\(\\*+\\|;;;+\\)[ \t]+\\(.*?\\)[ \t]*$" nil t)
- (let ((level (beancount-outline-level))
- (name (match-string-no-properties 2))
- (pos (point)))
- (cond ((not index-alist)
- (push (cons level (cons name pos)) index-alist))
- ((< level (caar index-alist))
- (let ((sub-index-alist index-alist))
- (while (and (cdr index-alist)
- (< level (caadr index-alist)))
- (setcar index-alist (cdar index-alist))
- (pop index-alist))
- (let ((sub-index-tail index-alist))
- (setcar index-alist (cdar index-alist))
- (pop index-alist)
- (setcdr sub-index-tail nil))
- (push (cons level (cons name sub-index-alist)) index-alist)
- ))
- (t (push (cons level (cons name pos)) index-alist)))))
- (mapcar 'cdr index-alist)))
-
(provide 'beancount)
;;; beancount.el ends here