Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tags and links support for xref definitions/references/apropos #54

Merged
merged 2 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 89 additions & 15 deletions beancount-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ POSITION provided by Beancount's xref-backend-definitions lookup."
(xref-buffer-location-position loc))))
(should (equal pos position)))))

(ert-deftest beancount/xref-backend-definitions ()
(ert-deftest beancount/xref-backend-definitions-accounts ()
:tags '(xref)
(with-temp-buffer
(insert "
Expand All @@ -384,23 +384,54 @@ POSITION provided by Beancount's xref-backend-definitions lookup."
(beancount-test-xref-definition-pos "Assets:Account2" 41)
(beancount-test-xref-definition-pos "Assets:Account3" 80)))

(ert-deftest beancount/xref-backend-definitions-tags ()
:tags '(xref)
(with-temp-buffer
(insert "
2019-01-10 * \"Opening Balances\" #tag1
Equity:Opening-Balances
Assets:Account1 1.00 TDB900

2019-01-10 * \"Opening Balances\" #tag2
Equity:Opening-Balances
Assets:Account1 1.00 TDB900
")
(beancount-test-xref-definition-pos "#tag1" 35)
(beancount-test-xref-definition-pos "#tag2" 138)))

(ert-deftest beancount/xref-backend-definitions-links ()
:tags '(xref)
(with-temp-buffer
(insert "
2019-01-10 * \"Opening Balances\" #link1
Equity:Opening-Balances
Assets:Account1 1.00 TDB900

2019-01-10 * \"Opening Balances\" #link2
Equity:Opening-Balances
Assets:Account1 1.00 TDB900
")
(beancount-test-xref-definition-pos "#link1" 35)
(beancount-test-xref-definition-pos "#link2" 139)))


(defmacro beancount-with-temp-file (&rest body)
"Generate a temporary file and open it as a current buffer.
Run BODY forms in the buffer's context. Remove both the buffer
and a backing file having completed the test."
(declare (indent 1))
`(let ((file (make-temp-file "beancount-test-"))
buf)
(unwind-protect
(progn (setq buf (find-file-literally file))
,@body)
(ignore-errors (delete-file file))
(ignore-errors
(with-current-buffer buf
(set-buffer-modified-p nil))
(kill-buffer buf)))))

(ert-deftest beancount/xref-backend-references ()
buf)
(unwind-protect
(progn (setq buf (find-file-literally file))
,@body)
(ignore-errors (delete-file file))
(ignore-errors
(with-current-buffer buf
(set-buffer-modified-p nil))
(kill-buffer buf)))))

(ert-deftest beancount/xref-backend-references-accounts ()
:tags '(xref)
;; Creating Xref file locations assumes a buffer backed by a file.
(beancount-with-temp-file
Expand All @@ -423,6 +454,46 @@ and a backing file having completed the test."
(should (equal (length (xref-backend-references 'beancount "Assets:Account2")) 2))
(should (equal (length (xref-backend-references 'beancount "Assets:Account3")) 1))))

(ert-deftest beancount/xref-backend-references-tags ()
:tags '(xref)
;; Creating Xref file locations assumes a buffer backed by a file.
(beancount-with-temp-file
(insert "
2019-01-10 * \"More Balances\" #tag1
Equity:Opening-Balances
Assets:Account1 1.00 TDB900

2019-01-10 * \"Opening Balances\" #tag2
Assets:Account1 1.00 TDB900
Assets:Account2 2.00 TDB900

2019-01-10 * \"More Balances\" #tag2
Equity:Opening-Balances
Assets:Account1 1.00 TDB900
")
(should (equal (length (xref-backend-references 'beancount "#tag1")) 1))
(should (equal (length (xref-backend-references 'beancount "#tag2")) 2))))

(ert-deftest beancount/xref-backend-references-links ()
:tags '(xref)
;; Creating Xref file locations assumes a buffer backed by a file.
(beancount-with-temp-file
(insert "
2019-01-10 * \"More Balances\" ^link1
Equity:Opening-Balances
Assets:Account1 1.00 TDB900

2019-01-10 * \"Opening Balances\" ^link1
Assets:Account1 1.00 TDB900
Assets:Account2 2.00 TDB900

2019-01-10 * \"More Balances\" ^link2
Equity:Opening-Balances
Assets:Account1 1.00 TDB900
")
(should (equal (length (xref-backend-references 'beancount "^link1")) 2))
(should (equal (length (xref-backend-references 'beancount "^link2")) 1))))

(ert-deftest beancount/xref-backend-apropos ()
:tags '(xref)
;; Creating Xref file locations assumes a buffer backed by a file.
Expand All @@ -432,12 +503,12 @@ and a backing file having completed the test."
2019-01-01 open Assets:Account2 TDB900
2019-01-01 open Assets:Account3 TDB900

2019-01-10 * \"Opening Balances\"
2019-01-10 * \"Opening Balances\" #tag ^link1
Equity:Opening-Balances
Assets:Account1 1.00 TDB900
Assets:Account2 2.00 TDB900

2019-01-10 * \"More Balances\"
2019-01-10 * \"More Balances\" #tag ^link2
Equity:Opening-Balances
Assets:Account1 1.00 TDB900

Expand All @@ -447,4 +518,7 @@ and a backing file having completed the test."
(should (equal (length (xref-backend-apropos 'beancount "Assets Account1")) 3))
(should (equal (length (xref-backend-apropos 'beancount "Equity")) 2))
(should (equal (length (xref-backend-apropos 'beancount "Opening")) 2))
(should (equal (length (xref-backend-apropos 'beancount "Opening Assets")) 0))))
(should (equal (length (xref-backend-apropos 'beancount "Opening Assets")) 0))
(should (equal (length (xref-backend-apropos 'beancount "tag")) 2))
(should (equal (length (xref-backend-apropos 'beancount "link1")) 1))
(should (equal (length (xref-backend-apropos 'beancount "link2")) 1))))
58 changes: 46 additions & 12 deletions beancount.el
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
(require 'cl-lib)
(require 'xref)
(require 'apropos)
(require 'rx)

;;;###autoload
(add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))
Expand Down Expand Up @@ -272,6 +273,12 @@ account.")
;; used in determining the outline level in `beancount-outline-level'.
(defvar beancount-outline-regexp "\\(;;;+\\|\\*+\\)")

;; Regular expression for all symbols recognised by the Xref backend.
(defconst beancount-xref-symbol-regexp
(rx-to-string `(or (regexp ,beancount-account-regexp)
(regexp ,(concat "#[" beancount-tag-chars "]+"))
(regexp ,(concat "\\^[" beancount-tag-chars "]+")))))

(defun beancount-outline-level ()
(let ((len (- (match-end 1) (match-beginning 1))))
(if (string-equal (substring (match-string 1) 0 1) ";")
Expand Down Expand Up @@ -1280,20 +1287,45 @@ Essentially a much simplified version of `next-line'."

(cl-defmethod xref-backend-definitions ((_ (eql beancount)) identifier)
"Find definitions of IDENTIFIER."
(let ((buf (current-buffer)))
(let ((buf (current-buffer))
re mgroup)
(cond
;; tag
((string-prefix-p "#" identifier)
(setq re (concat "#[" beancount-tag-chars "]+"))
(setq mgroup 0))
;; link
((string-prefix-p "^" identifier)
(setq re (concat "\\^[" beancount-tag-chars "]+"))
(setq mgroup 0))
;; account
(t
(setq re beancount-open-directive-regexp)
(setq mgroup 3)))
(cl-loop
for (def-id . def-pos) in
(beancount-collect-pos-alist beancount-open-directive-regexp 3)
if (equal def-id identifier)
collect
(xref-make def-id (xref-make-buffer-location buf def-pos)))))
for (def-id . def-pos) in
(beancount-collect-pos-alist re mgroup)
if (equal def-id identifier)
collect
(xref-make def-id (xref-make-buffer-location buf def-pos)))))

(cl-defmethod xref-backend-references ((_ (eql beancount)) identifier)
"Find references of IDENTIFIER."
(let ((fname (buffer-file-name)))
(let ((fname (buffer-file-name))
re)
(setq re
(cond
;; tag
((string-prefix-p "#" identifier)
(concat "#[" beancount-tag-chars "]+"))
;; link
((string-prefix-p "^" identifier)
(concat "\\^[" beancount-tag-chars "]+"))
;; account
(t beancount-account-regexp)))
(cl-loop
for (ref-id . ref-pos) in
(beancount-collect-pos-alist beancount-account-regexp 0)
(beancount-collect-pos-alist re 0)
if (equal ref-id identifier)
collect
(xref-make ref-id
Expand All @@ -1317,20 +1349,22 @@ Essentially a much simplified version of `next-line'."
(fname (buffer-file-name)))
(cl-loop
for (ref-id . ref-pos) in
(beancount-collect-pos-alist beancount-account-regexp 0)
(beancount-collect-pos-alist beancount-xref-symbol-regexp 0)
if (string-match-p pattern-re ref-id)
collect
(xref-make ref-id
(xref-make-file-location
fname (line-number-at-pos ref-pos) 0)))))

(cl-defmethod xref-backend-identifier-completion-table ((_ (eql beancount)))
(beancount-get-account-names))
(beancount-collect-unique beancount-xref-symbol-regexp 0))

(cl-defmethod xref-backend-identifier-at-point ((_ (eql beancount)))
"Extract a symbol at point, check if it is an account, return it"
(when-let ((acc (thing-at-point 'beancount-account)))
(substring-no-properties acc)))
(when-let ((thing (or (thing-at-point 'beancount-account)
(thing-at-point 'beancount-link)
(thing-at-point 'beancount-tag))))
(substring-no-properties thing)))

(provide 'beancount)
;;; beancount.el ends here