Merge pull request #26 from kljohann/vim

Vim: support for payee completion and caching
This commit is contained in:
John Wiegley 2011-06-30 09:38:03 -07:00
commit e51527a187
3 changed files with 116 additions and 79 deletions

View file

@ -6,6 +6,22 @@ Then include the following line in your .vimrc or in ~/.vim/filetype.vim
You can also use a modeline like this in every ledger file
vim:filetype=ledger
Tips and useful commands
======================================================================
• Try account-completion (as explained below)
• :call LedgerSetDate(line('.'), 'effective')
will set today's date as the effective date of the current
transaction. You can use also 'actual' in place of 'effective'
or pass in a different date measured as seconds since 1st Jan 1970.
• :call LedgerSetTransactionState(line('.'), '*')
sets the state of the current transaction to '*'. You can
use this in custom mappings.
• :call LedgerToggleTransactionState(line('.'), ' *?!')
will toggle through the provided transaction states.
You can map this to double-clicking for example:
noremap <silent><buffer> <2-LeftMouse>
\ :call LedgerToggleTransactionState(line('.'), ' *?!')<CR>
Configuration
======================================================================
Include the following let-statements somewhere in your .vimrc
@ -35,7 +51,7 @@ to modify the behaviour of the ledger filetype.
Completion
======================================================================
Omni completion is implemented for account names and tags.
Omni completion is currently implemented for account names only.
Accounts
----------------------------------------------------------------------
@ -52,38 +68,11 @@ When you want to complete on a virtual transaction,
it's currently best to keep the cursor in front of the closing bracket.
Of course you can insert the closing bracket after calling the completion, too.
Tags
----------------------------------------------------------------------
The support for completing tags is pretty basic right now
but it's useful to keep the spelling of your tags consistent.
You can call the completion after the ';' to get a list of all tags.
When you have a list of tags (:like: :this:) you can call
the completion too and everything up to the last ':' (excluding whitespace)
will be considered the beginning of the tag to search for.
Revision history (major changes)
======================================================================
2009-06-23 & 2009-06-25
J. Klähn: Omni-Completion for account names and tags
2009-06-17 J. Klähn: Highlight account text
Updated documentation and added fillstring option.
2009-06-15 J. Klähn: Split into multiple files
2009-06-12 J. Klähn: Use all available columns for foldtext
Also rewrote foldtext generation.
2009-03-25 J. Klähn: Allow Metadata
in transactions and postings (Ledger 3.0)
Also fixed alignment for multi-byte-characters
2009-01-28 S.Karrmann: minor fixes
2009-01-27 third version by S.Karrmann.
better extraction of the amount of the posting
decimal separator can be one of '.' and ','.
2005-02-05 first version (partly copied from ledger.vim 0.0.1)
License
======================================================================
Copyright 2009 by Johann Klähn
Copyright 2009 by Stefan Karrmann
Copyright 2005 by Wolfgang Oertl
Copyright 2011-2009 by Johann Klähn
Copyright 2009 by Stefan Karrmann
Copyright 2005 by Wolfgang Oertl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,5 @@
" Vim filetype plugin file
" filetype: ledger
" Version: 0.1.0
" by Johann Klähn; Use according to the terms of the GPL>=2.
" vim:ts=2:sw=2:sts=2:foldmethod=marker
@ -137,22 +136,33 @@ function! LedgerComplete(findstart, base) "{{{1
if a:findstart
let lnum = line('.')
let line = getline('.')
let lastcol = col('.') - 2
let b:compl_context = ''
if line =~ '^\s\+[^[:blank:];]' "{{{2 (account)
let b:compl_context = 'account'
if matchend(line, '^\s\+\%(\S \S\|\S\)\+') <= lastcol
" only allow completion when in or at end of account name
return -1
" only allow completion when in or at end of account name
if matchend(line, '^\s\+\%(\S \S\|\S\)\+') >= col('.') - 1
" the start of the first non-blank character
" (excluding virtual-transaction-marks)
" is the beginning of the account name
let b:compl_context = 'account'
return matchend(line, '^\s\+[\[(]\?')
endif
" the start of the first non-blank character
" (excluding virtual-transaction-marks)
" is the beginning of the account name
return matchend(line, '^\s\+[\[(]\?')
else "}}}
return -1
endif
elseif line =~ '^\d' "{{{2 (description)
let pre = matchend(line, '^\d\S\+\%(([^)]*)\|[*?!]\|\s\)\+')
if pre < col('.') - 1
let b:compl_context = 'description'
return pre
endif
elseif line =~ '^$' "{{{2 (new line)
let b:compl_context = 'new'
endif "}}}
return -1
else
if ! exists('b:compl_cache')
let b:compl_cache = s:collect_completion_data()
let b:compl_cache['#'] = changenr()
endif
let results = []
if b:compl_context == 'account' "{{{2 (account)
unlet! b:compl_context
let hierarchy = split(a:base, ':')
@ -160,7 +170,7 @@ function! LedgerComplete(findstart, base) "{{{1
call add(hierarchy, '')
endif
let results = LedgerFindInTree(LedgerGetAccountHierarchy(), hierarchy)
let results = LedgerFindInTree(b:compl_cache.accounts, hierarchy)
" sort by alphabet and reverse because it will get reversed one more time
if g:ledger_detailed_first
let results = reverse(sort(results, 's:sort_accounts_by_depth'))
@ -168,10 +178,19 @@ function! LedgerComplete(findstart, base) "{{{1
let results = sort(results)
endif
call insert(results, a:base)
return results
else "}}}
elseif b:compl_context == 'description' "{{{2 (description)
let results = [a:base] + s:filter_items(b:compl_cache.descriptions, a:base)
elseif b:compl_context == 'new' "{{{2 (new line)
return [strftime('%Y/%m/%d')]
endif "}}}
" no completion (apart from a:base) found. update cache if file has changed
if len(results) <= 1 && b:compl_cache['#'] != changenr()
unlet b:compl_cache
return LedgerComplete(a:findstart, a:base)
else
unlet! b:compl_context
return []
return results
endif
endif
endf "}}}
@ -195,21 +214,6 @@ function! LedgerFindInTree(tree, levels) "{{{1
return results
endf "}}}
function! LedgerGetAccountHierarchy() "{{{1
let hierarchy = {}
let accounts = s:grep_buffer('^\s\+\zs[^[:blank:];]\%(\S \S\|\S\)\+\ze')
for name in accounts
" remove virtual-transaction-marks
let name = substitute(name, '\%(^\s*[\[(]\?\|[\])]\?\s*$\)', '', 'g')
let last = hierarchy
for part in split(name, ':')
let last[part] = get(last, part, {})
let last = last[part]
endfor
endfor
return hierarchy
endf "}}}
function! LedgerToggleTransactionState(lnum, ...)
if a:0 == 1
let chars = a:1
@ -278,6 +282,51 @@ function! LedgerSetDate(lnum, type, ...) "{{{1
call setline(trans['head'], trans.format_head())
endf "}}}
function! s:collect_completion_data() "{{{1
let transactions = s:get_transactions()
let cache = {'descriptions': [], 'tags': {}, 'accounts': {}}
let accounts = []
for xact in transactions
" collect descriptions
if index(cache.descriptions, xact['description']) < 0
call add(cache.descriptions, xact['description'])
endif
let [t, postings] = xact.parse_body()
let tagdicts = [t]
" collect account names
for posting in postings
if has_key(posting, 'tags')
call add(tagdicts, posting.tags)
endif
" remove virtual-transaction-marks
let name = substitute(posting.account, '\%(^\s*[\[(]\?\|[\])]\?\s*$\)', '', 'g')
if index(accounts, name) < 0
call add(accounts, name)
endif
endfor
" collect tags
for tags in tagdicts | for [tag, val] in items(tags)
let values = get(cache.tags, tag, [])
if index(values, val) < 0
call add(values, val)
endif
let cache.tags[tag] = values
endfor | endfor
endfor
for account in accounts
let last = cache.accounts
for part in split(account, ':')
let last[part] = get(last, part, {})
let last = last[part]
endfor
endfor
return cache
endf "}}}
let s:transaction = {} "{{{1
function! s:transaction.new() dict
return copy(s:transaction)
@ -293,7 +342,15 @@ function! s:transaction.from_lnum(lnum) dict "{{{2
let trans['head'] = head
let trans['tail'] = tail
let parts = split(getline(head), '\s\+')
" split off eventual comments at the end of line
let line = split(getline(head), '\ze\s*\%(\t\| \);', 1)
if len(line) > 1
let trans['appendix'] = join(line[1:], '')
endif
" parse rest of line
" FIXME (minor): will not preserve spacing (see 'join(parts)')
let parts = split(line[0], '\s\+')
if parts[0] ==# '~'
let trans['expr'] = join(parts[1:])
return trans
@ -318,8 +375,6 @@ function! s:transaction.from_lnum(lnum) dict "{{{2
call remove(parts, 0)
endfor
" FIXME: this will break comments at the end of this 'head' line
" they need 2 spaces in front of the semicolon
let trans['description'] = join(parts)
return trans
endf "}}}
@ -402,7 +457,11 @@ function! s:transaction.format_head() dict "{{{2
if has_key(self, 'code') | call add(parts, '('.self['code'].')') | endif
if has_key(self, 'state') | call add(parts, self['state']) | endif
if has_key(self, 'description') | call add(parts, self['description']) | endif
return join(parts)
let line = join(parts)
if has_key(self, 'appendix') | let line .= self['appendix'] | endif
return line
endf "}}}
"}}}
@ -518,7 +577,7 @@ endf "}}}
" return only those items that start with a specified keyword
function! s:filter_items(list, keyword) "{{{2
return filter(a:list, 'v:val =~ ''^\V'.substitute(a:keyword, '\\', '\\\\', 'g').'''')
return filter(copy(a:list), 'v:val =~ ''^\V'.substitute(a:keyword, '\\', '\\\\', 'g').'''')
endf "}}}
" return all lines matching an expression, returning only the matched part

View file

@ -1,19 +1,8 @@
" Vim syntax file
" filetype: ledger
" Version: 0.1.0
" by Johann Klähn; Use according to the terms of the GPL>=2.
" by Stefan Karrmann; Use according to the terms of the GPL>=2.
" by Wolfgang Oertl; Use according to the terms of the GPL>=2.
" Revision history
" 2009-06-12 J. Klähn: Use all available columns for foldtext
" 2009-03-25 J. Klähn: Allow Metadata
" in transactions and postings (Ledger 3.0)
" Also fixed alignment for multi-byte-characters
" 2009-01-28 S.Karrmann: minor fixes
" 2009-01-27 third version by S.Karrmann.
" better extraction of the amount of the posting
" decimal separator can be one of '.' and ','.
" 2005-02-05 first version (partly copied from ledger.vim 0.0.1)
" vim:ts=2:sw=2:sts=2:foldmethod=marker
if version < 600