emacs beancount mode and multiple files for accounts

543 views
Skip to first unread message

jrgo...@gmail.com

unread,
Apr 28, 2021, 7:45:19 AM4/28/21
to Beancount
Hi everyone,

I have emacs beancount mode working well. I really like it when I first tried to use beancount 1 year ago. I'm restarting everything now and want to modify to not put everything in one file.

Does beancount mode only look within the current document to create an index of accounts for possible autocomplete? Is there anyway to modify this to point to another file with all the accounts I have? I have a file called "accounts.beancount" that contains all the beancount accounts I want to use to populate and I'm trying to organize my beancount files a bit to more easily rollback changes. 

thanks,
Jonathan

john.a.r...@gmail.com

unread,
Apr 28, 2021, 12:02:48 PM4/28/21
to Beancount
Hi, Jonathan --


Does beancount mode only look within the current document to create an index of accounts for possible autocomplete? Is there anyway to modify this to point to another file with all the accounts I have? I have a file called "accounts.beancount" that contains all the beancount accounts I want to use to populate and I'm trying to organize my beancount files a bit to more easily rollback changes. 

You can include the file -- put this on a separate line in your ledger --

include /path/to/accounts.beancount

--John

Jonathan Goldman

unread,
Apr 28, 2021, 5:11:32 PM4/28/21
to bean...@googlegroups.com
Isn’t this include directive you reference for the Beancount loader?

I’m asking how to have emacs Beancount mode be aware of all accounts even if the currently active file doesn’t list them all? Is there also and include method I add to the emacs “.el” file?

--
You received this message because you are subscribed to a topic in the Google Groups "Beancount" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/beancount/mNUrYeOsOqE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to beancount+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beancount/7f9b684c-775d-4d41-b655-58c689f370fdn%40googlegroups.com.

TRS-80

unread,
Apr 29, 2021, 3:31:29 PM4/29/21
to bean...@googlegroups.com
On 2021-04-28 07:45, jrgo...@gmail.com wrote:
>
> Does beancount mode only look within the current document to create an
> index of accounts for possible autocomplete?

I am pretty sure that beancount-mode only collects from current
buffer. If you take a look at the function `beancount-insert-account'
it in turn calls `beancount-collect', if you read the docstring for
that function it says:

"Return an unique list of REGEXP group N in the current buffer."

> Is there anyway to modify this to point to another file with all the
> accounts I have? I have a file called "accounts.beancount" that
> contains all the beancount accounts I want to use to populate

With a little custom Emacs Lisp, anything is possible. ;) For
example, I have made my own little custom function to insert any of my
Beancount account names anywhere in any buffer (or insert transfer
transactions involving 2 accounts, and many others).

> I'm trying to organize my beancount files a bit to more easily
> rollback changes.

The tool you may be looking for to "rollback changes" is a good text
editor and/or git. Although some of concepts may be foreign to you at
first if you never did any some sort of development.

So I suppose the answer (for /you/) depends on how comfortable you are
with Emacs, Emacs Lisp, git, and/or other tools, as there are many
different ways to skin this particular cat.

Myself (and some others here) are big proponents, users, and lovers of
Emacs, for many different reasons which go far beyond the scope of
this particular issue. Therefore our solutions involve Emacs.
However that might not be your cup of tea.

Cheers,
TRS-80

Jonathan Goldman

unread,
Apr 29, 2021, 7:05:15 PM4/29/21
to bean...@googlegroups.com
Thanks for confirming what I thought was the case @TRS-80.

Yes I want to create a modification to Emacs Lisp code to specify a specific file to load. In my case I have one file called "journal.beancount" that loads "accounts.beancount" and then all the transactions which are organized by real-world accounts. So ideally all I need to do is load "accounts.beancount" and then when I'm editing any specific transactions file I have access to type-ahead for any of the accounts found in accounts.beancount. 

I'm not yet comfortable with Emacs Lisp. I'll try to figure it out but if anyone knows how to do this modification easily that would save me a lot of time and perhaps be generally useful as I think there are others who split up their beancount data among multiple files. 

Thanks,
Jonathan

--
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 beancount+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beancount/68aebfe4c176ef7a766d87f086826f58%40isnotmyreal.name.

Ben Blount

unread,
Apr 29, 2021, 7:17:24 PM4/29/21
to Beancount
Relevant thread: https://groups.google.com/g/beancount/c/QOJInwm1TZ0/m/ggsT1AsyAQAJ

I was using https://github.com/cnsunyour/beancount.el previously to get completion with accounts defined in different files. However I've since merged into one file and am using the official plugin. Daniele mentioned there is support for this in the official plugin but I haven't tried it out myself.

jrgo...@gmail.com

unread,
Apr 30, 2021, 2:09:14 AM4/30/21
to Beancount
Thanks for the pointer. This was very helpful. I'm not familiar enough with lisp and what I need to do to get it to work for me--currently getting a read error when launching emacs.  My ".emacs" file had the following lines (now commented out) below that worked fine. I changed to what is "new" below based on following that link. Does "straight" code below mean it reads the beancount.el file from GitHub directly? Should I read in the beancount.el in a different way?

thanks,
Jonathan


OLD
====

;;(add-to-list 'load-path "/Users/jonathan/.emacs.d/lisp/beancount.el")

;;(require 'beancount)

;;(add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))


NEW

====

(use-package beancount

  :straight (beancount

             :type git

             :host github

             :repo "cnsunyour/beancount.el")

  :bind

  ("C-M-b" . (lambda ()

               (interactive)

               (find-file "/Users/jonathan/Documents/Finances/beandata/beancount/myjournal/accounts.beancount")))

  :mode

  ("\\.bean\\(?:count\\)?\\'" . beancount-mode)

  :config

  (setq beancount-accounts-files

        (directory-files "/Users/jonathan/Documents/Finances/beandata/beancount/myjournal/"

                         'full

                         (rx ".beancount" eos))))


Ben Blount

unread,
Apr 30, 2021, 9:18:13 AM4/30/21
to Beancount
I'm fairly new to emacs myself but can answer those questions.
Straight is a package manager for emacs. It automates grabbing files from github and updating them when versions change.

You can keep using your old code if you don't want to futz with it, you'd just put cnsunyour's version of beancount.el somewhere on your disk and change the
(add-to-list 'load-path "/Users/jonathan/.emacs.d/lisp/beancount.el")
line accordingly.

Then you need to set the variable `beancount-account-files`. So just add

  (setq beancount-accounts-files

        (directory-files "/Users/jonathan/Documents/Finances/beandata/beancount/myjournal/"

                         'full

                         (rx ".beancount" eos)))


to your config also.


TRS-80

unread,
May 1, 2021, 7:10:21 PM5/1/21
to bean...@googlegroups.com
On 2021-04-30 02:09, jrgo...@gmail.com wrote:
> Thanks for the pointer. This was very helpful. I'm not familiar
> enough with lisp and what I need to do to get it to work for me

Emacs (and the language it's mostly written in, Emacs Lisp) are very
powerful and versatile. Later on, when you are not in a hurry to see
some early success, I can highly recommend the built-in Tutorial (C-h
t), and then after that, the Introduction to Emacs Lisp (C-h i m intro
<RET>). Even the first few pages of that Elisp Intro can begin to
de-mystify a lot of otherwise cryptic error messages.

> "straight"

Personally, I'm not a fan of adding complexity, especially if you are
already new to Emacs in general. I would instead (strongly) recommend
reading error messages, searching in the many help facilities Emacs
provides (C-h ?), and/or the Internet, and fixing the original
problem, instead of covering that up with more layers of obfuscation.

I have been using Emacs for a few years now, even wrote some packages,
but I still use the vanilla included package manager (just as a for
instance). Straight certainly is very popular (as are all these
"starter configs" like Doom, etc.) but I am not a fan of any of them,
and all for the same reasons.

Anyway, let's try to get you some early success, so that maybe you
stick with it long enough to realize its (immense) power.

> My ".emacs" file had the following lines (now commented out) below
> that worked fine
>
> OLD
> ====
>
> ;;(add-to-list 'load-path
> "/Users/jonathan/.emacs.d/lisp/beancount.el")
>
> ;;(require 'beancount)
>
> ;;(add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))

That looks fine to me. Just add beancount.el to your load-path, and
then require it.

> currently getting a read error when launching emacs.

What error are you getting? Try starting emacs from command line with
--debug-init option, like:

$ emacs --debug-init

In the meantime, I'll look at how to gather the list of accounts from
some other file(s).

Cheers,
TRS-80

TRS-80

unread,
May 1, 2021, 8:14:54 PM5/1/21
to bean...@googlegroups.com
On 2021-04-29 19:04, Jonathan Goldman wrote:
>
> Yes I want to create a modification to Emacs Lisp code to specify a
> specific file to load. In my case I have one file called
> "journal.beancount" that loads "accounts.beancount" and then all the
> transactions which are organized by real-world accounts. So ideally
> all I need to do is load "accounts.beancount" and then when I'm
> editing any specific transactions file I have access to type-ahead
> for any of the accounts found in accounts.beancount.
>
> I'm not yet comfortable with Emacs Lisp. I'll try to figure it out
> but if anyone knows how to do this modification easily that would
> save me a lot of time and perhaps be generally useful as I think
> there are others who split up their beancount data among multiple
> files.

There are likely many different ways to do this (some advice around
one of the beancount functions, patching in a simple variable to tell
beancount where to look for accounts, etc.) but what I did (some time
ago) was just to leverage some of the existing beancount.el code to
collect a list of accounts, and then do a very simple completing-read
interface with the accounts list as candidates, in order to insert
account name at point.

Upsides of doing it this way is that it works in every buffer. Comes
in handy some times.

Note that ostrta- is just my namespace prefix for this package, you
can change that to my- or whatever you like. In fact you can change
the names of the functions to something else that suits you, if you
prefer. Just make sure that you change all of them the same (as one
function references the other one).

#+begin_src emacs-lisp

;; Define variables

(defvar ostrta-beancount-account-list nil
"List of all accounts in `ostrta-beancount-file'.

Refresh list with function
`ostrta-beancount-account-list-refresh'.")


(defvar ostrta-beancount-file nil
"Full path to beancount file.")


;; Define functions

(defun ostrta-beancount-account-insert-at-point ()
"Just like it sounds."
(interactive)
(when (null ostrta-beancount-account-list)
(ostrta-beancount-account-list-refresh))
(let ((account (completing-read
"Account: "
ostrta-beancount-account-list)))
(insert account)))


(defun ostrta-beancount-account-list-refresh ()
"Update `ostrta-beancount-account-list'.

Collect a (unique, sorted) list of account names from
`ostrta-beancount-file'."
(interactive)
(require 'beancount)
(find-file-noselect ostrta-beancount-file)
(save-current-buffer
(set-buffer (file-name-nondirectory ostrta-beancount-file))
(setq ostrta-beancount-account-list
(sort (beancount-collect beancount-account-regexp 0)
#'string<))))


;; Set variable

(setq ostrta-beancount-file "/path/to/your/file.beancount")

#+end_src

Just put that in your init file. Restart Emacs and you should be off
to the races. Oh yes, you can invoke the command by doing M-x then
start typing name of command (e.g., "beancount account insert"). But
you might want to bind it to some key. Let me know if you need some
help with that.

If you want to poke around and start learning Elisp, C-h f and C-h v
are your friends. With point on a function or variable name
(respectively) invoke those commands to see what does what. I use
them constantly.

Let us know how you are getting on, or if you need some more help.
Good luck.

Cheers,
TRS-80

TRS-80

unread,
May 1, 2021, 8:58:47 PM5/1/21
to bean...@googlegroups.com
On 2021-04-29 19:04, Jonathan Goldman wrote:
> Yes I want to create a modification to Emacs Lisp code to specify a
> specific file to load. In my case I have one file called
> "journal.beancount" that loads "accounts.beancount" and then all the
> transactions which are organized by real-world accounts. So ideally
> all I need to do is load "accounts.beancount" and then when I'm
> editing any specific transactions file I have access to type-ahead for
> any of the accounts found in accounts.beancount.
>
> I'm not yet comfortable with Emacs Lisp. I'll try to figure it out but
> if anyone knows how to do this modification easily that would save me
> a lot of time and perhaps be generally useful as I think there are
> others who split up their beancount data among multiple files.

Jonathan,

Here is a second approach.

If you are planning on doing a lot of manual entry, you might want the
autocompletion to "just work" in the mode (with <TAB> or whatever).
In that case, my first try is not going to be ideal (invoking a
function every time, even with a keybinding).

First I started thinking about modifying beancount-collect function,
until I realized that is a general function called from many different
places. So instead I think we need to try modifying the
beancount-account-completion-table function.

Note that this is completely untested code, but give this a whirl:

#+begin_src emacs-lisp

;; Define variable

(defvar my-beancount-accounts-file nil
"Full path to beancount accounts file.")


;; (Re-)Define function

;; N.B. this must go AFTER (require 'beancount) in your init
;; file, as we are re-defining the function.
(defun beancount-account-completion-table (string pred action)
(if (eq action 'metadata) '(metadata (category . beancount-account))
(if (null beancount-accounts)
(setq beancount-accounts
(save-current-buffer
(find-file-noselect my-beancount-accounts-file)
(set-buffer (file-name-nondirectory
my-beancount-accounts-file))
(sort (beancount-collect beancount-account-regexp 0) #'string<))))
(complete-with-action action beancount-accounts string pred)))


;; Set variable

(setq my-beancount-accounts-file "/path/to/your/accounts.beancount")

#+end_src

Essentially we inserted a call to open (if it's not already) your
accounts file in a background buffer, execute the search over there,
and then return to current position.

Let me know if it works.

Cheers,
TRS-80

jrgo...@gmail.com

unread,
May 3, 2021, 2:23:59 AM5/3/21
to Beancount
Hi TRS-80,

Thanks so much for exploring this. I'll give this a try shortly and let you know if doesn't work. This is great!


thanks,
Jonathan
Reply all
Reply to author
Forward
0 new messages