From c3ed247909501c093a1c9ccb7f47e1b4a960c06f Mon Sep 17 00:00:00 2001 From: Nathan Grigg Date: Thu, 14 May 2015 19:11:11 -0700 Subject: Follow include statements when completing accounts Uses Python script to seek through multiple files for accounts, following 'include' statements. The list of accounts is cached to 'b:beancount_accounts'. You can specify the root file using 'b:beancount_root', and if this is not set, we assume the current file is the root. Included files that are not found are silently skipped. Closes #5. --- autoload/beancount.vim | 66 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 11 deletions(-) (limited to 'autoload') diff --git a/autoload/beancount.vim b/autoload/beancount.vim index 49b06df..9cd1168 100644 --- a/autoload/beancount.vim +++ b/autoload/beancount.vim @@ -48,19 +48,63 @@ function! beancount#complete_account(findstart, base) endif endif - let l:pattern = '^\V\.\{10\}\s\+open\s\+\zs\S\*' . - \ substitute(a:base, ":", '\\S\\*:\\S\\*', "g") - let l:view = winsaveview() - let l:fe = &foldenable - set nofoldenable - call cursor(1, 1) + if !exists('b:beancount_accounts') + if exists('b:beancount_root') + let l:root = b:beancount_root + else + let l:root = expand('%') + endif + let b:beancount_accounts = beancount#find_accounts(l:root) + endif + let l:pattern = '\V' . substitute(a:base, ":", '\\S\\*:\\S\\*', "g") let l:matches = [] + let l:index = -1 while 1 - let l:cline = search(pattern, "W") - if l:cline == 0 | break | endif - call add(matches, expand("")) + let l:index = match(b:beancount_accounts, l:pattern, l:index + 1) + if l:index == -1 | break | endif + call add(l:matches, b:beancount_accounts[l:index]) endwhile - let &foldenable = l:fe - call winrestview(l:view) return l:matches endfunction + +" Get list of acounts. +function! beancount#find_accounts(root_file) + python << EOM +import collections +import os +import re +import sys +import vim + +RE_INCLUDE = re.compile(r'^include\s+"([^\n"]+)"') +RE_ACCOUNT = re.compile(r'^\d{4,}-\d{2}-\d{2}\s+open\s+(\S+)') + +def combine_paths(old, new): + return os.path.normpath( + new if os.path.isabs(new) else os.path.join(old, new)) + +def parse_file(fh, files, accounts): + regexes = ((RE_INCLUDE, files), (RE_ACCOUNT, accounts)) + for line in fh: + m = RE_INCLUDE.match(line) + if m: files.append(combine_paths(os.path.dirname(fh.name), m.group(1))) + m = RE_ACCOUNT.match(line) + if m: accounts.add(m.group(1)) + +files = collections.deque([vim.eval("a:root_file")]) +accounts = set() +seen = set() +while files: + current = files.popleft() + if current in seen: + continue + seen.add(current) + try: + with open(current, 'r') as fh: + parse_file(fh, files, accounts) + except IOError as err: + pass + +vim.command('return [{}]'.format(','.join(repr(x) for x in sorted(accounts)))) +EOM +endfunction -- cgit v1.2.3-2-gb3c3