aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--autoload/beancount.vim38
-rw-r--r--doc/beancount.txt17
-rw-r--r--ftplugin/beancount.vim28
4 files changed, 84 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a56e3f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/doc/tags
diff --git a/autoload/beancount.vim b/autoload/beancount.vim
new file mode 100644
index 0000000..85adc3e
--- /dev/null
+++ b/autoload/beancount.vim
@@ -0,0 +1,38 @@
+" Align currency on decimal point.
+function! beancount#align_commodity(line1, line2)
+ " Saving cursor position to adjust it if necessary.
+ let cursor_col = col('.')
+ let cursor_line = line('.')
+ " This matches the line up to the first dot (or other separator),
+ " excluding comments.
+ " Note very nomagic so that the separator is not interpreted as regex.
+ let separator_regex = '^\V\[^;]\{-}' . g:beancount_decimal_separator
+ " This lets me increment at start of loop, because of continue statements.
+ let i = a:line1 - 1
+ while i < a:line2
+ let i += 1
+ let s = getline(i)
+ " This matches an account name followed by a space. There may be
+ " some conflicts with non-transaction syntax that I don't know about.
+ " It won't match a comment or any non-indented line.
+ let end_acc = matchend(s, '^\v([-\d]+\s+(balance|price))? +\S+ ')
+ if end_acc < 0 | continue | endif
+ " Where does commodity amount begin?
+ let end_space = matchend(s, '^ *', end_acc)
+ " Find the first decimal point, not counting comments.
+ let separator = matchend(s, separator_regex, end_space)
+ if separator < 0
+ " If there is no separator, pretend there's one after the last digit.
+ let separator = matchend(s, '^\v[^;]*\d+') + 1
+ endif
+ if separator < 0 | continue | endif
+ let has_spaces = end_space - end_acc
+ let need_spaces = g:beancount_separator_col - separator + has_spaces
+ if need_spaces < 0 | continue | endif
+ call setline(i, s[0 : end_acc - 1] . repeat(" ", need_spaces) . s[ end_space : -1])
+ if i == cursor_line && cursor_col >= end_acc
+ " Adjust cursor position for continuity.
+ call cursor(0, cursor_col + need_spaces - has_spaces)
+ endif
+ endwhile
+endfunction
diff --git a/doc/beancount.txt b/doc/beancount.txt
new file mode 100644
index 0000000..6b070c9
--- /dev/null
+++ b/doc/beancount.txt
@@ -0,0 +1,17 @@
+* `:AlignCommodity`
+
+ adds spaces between an account and commodity so that the decimal points of
+ the commodities all occur in the column given by `g:decimal_separator_col`.
+ If an amount has no decimal point, the imaginary decimal point to the right
+ of the least significant digit will align.
+
+ The command acts on a range, with the default being the current line. If the
+ cursor happens to be inside that range and to the right of the account name,
+ the cursor will be pushed to the right the appropriate amount, so that it
+ remains on the same character.
+
+ The alignment character can be set using `g:beancount_decimal_separator`.
+ The script assumes the use of spaces for alignment. It does not understand
+ tabs. You might like the following remap:
+
+ inoremap . .<C-O>:AlignCommodity<CR>
diff --git a/ftplugin/beancount.vim b/ftplugin/beancount.vim
new file mode 100644
index 0000000..f073545
--- /dev/null
+++ b/ftplugin/beancount.vim
@@ -0,0 +1,28 @@
+" These two variables customize the behavior of the AlignCommodity command.
+
+if !exists("g:beancount_separator_col")
+ let g:beancount_separator_col = 50
+endif
+if !exists("g:beancount_decimal_separator")
+ let g:beancount_decimal_separator = "."
+endif
+
+command! -range AlignCommodity :call beancount#align_commodity(<line1>, <line2>)
+
+" Align commodity to proper column
+inoremap <buffer> . .<C-O>:AlignCommodity<CR>
+nnoremap <buffer> <leader>= :AlignCommodity<CR>
+vnoremap <buffer> <leader>= :AlignCommodity<CR>
+
+" Insert incoming transactions at the bottom
+function! s:InsertIncoming()
+ let incoming = expand('%:h') . '/incoming.bean'
+ try
+ execute '$read' incoming
+ call system('mv ' . incoming . ' ' . incoming . '~')
+ catch /:E484/
+ echom "Not found: incoming.bean"
+ endtry
+endfunction
+
+command! -buffer Incoming call <SID>InsertIncoming()