branch: elpa/beancount
commit ba6bada870eb33f9444a9e39a1eedd420cd870d4
Author: Daniele Nicolodi <dani...@grinta.net>
Commit: Daniele Nicolodi <dani...@grinta.net>

    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

Reply via email to