Merge commit 'kljohann/master' into next
This commit is contained in:
commit
752677edf0
2 changed files with 242 additions and 15 deletions
|
|
@ -11,17 +11,60 @@ Configuration
|
||||||
Include the following let-statements somewhere in your .vimrc
|
Include the following let-statements somewhere in your .vimrc
|
||||||
to modify the behaviour of the ledger filetype.
|
to modify the behaviour of the ledger filetype.
|
||||||
|
|
||||||
Number of colums that will be used to display the foldtext.
|
* Number of colums that will be used to display the foldtext.
|
||||||
Set this when you think that the amount is too far off to the right.
|
Set this when you think that the amount is too far off to the right.
|
||||||
let g:ledger_maxwidth = 80
|
let g:ledger_maxwidth = 80
|
||||||
|
|
||||||
String that will be used to fill the space between account name
|
* String that will be used to fill the space between account name
|
||||||
and amount in the foldtext. Set this to get some kind of lines
|
and amount in the foldtext. Set this to get some kind of lines
|
||||||
or visual aid.
|
or visual aid.
|
||||||
let g:ledger_fillstring = ' -'
|
let g:ledger_fillstring = ' -'
|
||||||
|
My special tip is to use so-called digraphs:
|
||||||
|
Press <C-K> followed by the two-characters key sequence below.
|
||||||
|
(in insert-mode)
|
||||||
|
'. = ˙ or ': = ¨ --> ˙˙˙˙˙˙ or ¨¨¨¨¨¨
|
||||||
|
', = ¸ --> ¸¸¸¸¸¸
|
||||||
|
.M = · --> ······
|
||||||
|
>> = » --> »»»»»»
|
||||||
|
All those look rather unobstrusive
|
||||||
|
and provide a good visual aid to find the correct amount.
|
||||||
|
|
||||||
Revision history
|
* If you want the account completion to be sorted by level of detail/depth
|
||||||
|
instead of alphabetical, include the following line:
|
||||||
|
let g:ledger_detailed_first = 1
|
||||||
|
|
||||||
|
Completion
|
||||||
======================================================================
|
======================================================================
|
||||||
|
Omni completion is implemented for account names and tags.
|
||||||
|
|
||||||
|
Accounts
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Account names are matched by the start of every sub-level.
|
||||||
|
When you insert an account name like this:
|
||||||
|
Asse<C-X><C-O>
|
||||||
|
You will get a list of top-level accounts that start like this.
|
||||||
|
|
||||||
|
Go ahead and try something like:
|
||||||
|
As:Ban:Che<C-X><C-O>
|
||||||
|
When you have an account like this, 'Assets:Bank:Checking' should show up.
|
||||||
|
|
||||||
|
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
|
2009-06-17 J. Klähn: Highlight account text
|
||||||
Updated documentation and added fillstring option.
|
Updated documentation and added fillstring option.
|
||||||
2009-06-15 J. Klähn: Split into multiple files
|
2009-06-15 J. Klähn: Split into multiple files
|
||||||
|
|
@ -37,7 +80,7 @@ Revision history
|
||||||
2005-02-05 first version (partly copied from ledger.vim 0.0.1)
|
2005-02-05 first version (partly copied from ledger.vim 0.0.1)
|
||||||
|
|
||||||
License
|
License
|
||||||
=======
|
======================================================================
|
||||||
Copyright 2009 by Johann Klähn
|
Copyright 2009 by Johann Klähn
|
||||||
Copyright 2009 by Stefan Karrmann
|
Copyright 2009 by Stefan Karrmann
|
||||||
Copyright 2005 by Wolfgang Oertl
|
Copyright 2005 by Wolfgang Oertl
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ let b:did_ftplugin = 1
|
||||||
|
|
||||||
let b:undo_ftplugin = "setlocal ".
|
let b:undo_ftplugin = "setlocal ".
|
||||||
\ "foldmethod< foldtext< ".
|
\ "foldmethod< foldtext< ".
|
||||||
\ "include< comments< iskeyword< "
|
\ "include< comments< omnifunc< "
|
||||||
|
|
||||||
" don't fill fold lines --> cleaner look
|
" don't fill fold lines --> cleaner look
|
||||||
setl fillchars="fold: "
|
setl fillchars="fold: "
|
||||||
|
|
@ -20,10 +20,7 @@ setl foldtext=LedgerFoldText()
|
||||||
setl foldmethod=syntax
|
setl foldmethod=syntax
|
||||||
setl include=^!include
|
setl include=^!include
|
||||||
setl comments=b:;
|
setl comments=b:;
|
||||||
" so you can use C-X C-N completion on accounts
|
setl omnifunc=LedgerComplete
|
||||||
" FIXME: Does not work with something like:
|
|
||||||
" Assets:Accountname with Spaces
|
|
||||||
setl iskeyword+=:
|
|
||||||
|
|
||||||
" You can set a maximal number of columns the fold text (excluding amount)
|
" You can set a maximal number of columns the fold text (excluding amount)
|
||||||
" will use by overriding g:ledger_maxwidth in your .vimrc.
|
" will use by overriding g:ledger_maxwidth in your .vimrc.
|
||||||
|
|
@ -37,6 +34,30 @@ if !exists('g:ledger_fillstring')
|
||||||
let g:ledger_fillstring = ' '
|
let g:ledger_fillstring = ' '
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" If enabled this will list the most detailed matches at the top {{{
|
||||||
|
" of the completion list.
|
||||||
|
" For example when you have some accounts like this:
|
||||||
|
" A:Ba:Bu
|
||||||
|
" A:Bu:Bu
|
||||||
|
" and you complete on A:B:B normal behaviour may be the following
|
||||||
|
" A:B:B
|
||||||
|
" A:Bu:Bu
|
||||||
|
" A:Bu
|
||||||
|
" A:Ba:Bu
|
||||||
|
" A:Ba
|
||||||
|
" A
|
||||||
|
" with this option turned on it will be
|
||||||
|
" A:B:B
|
||||||
|
" A:Bu:Bu
|
||||||
|
" A:Ba:Bu
|
||||||
|
" A:Bu
|
||||||
|
" A:Ba
|
||||||
|
" A
|
||||||
|
" }}}
|
||||||
|
if !exists('g:ledger_detailed_first')
|
||||||
|
let g:ledger_detailed_first = 0
|
||||||
|
endif
|
||||||
|
|
||||||
let s:rx_amount = '\('.
|
let s:rx_amount = '\('.
|
||||||
\ '\%([0-9]\+\)'.
|
\ '\%([0-9]\+\)'.
|
||||||
\ '\%([,.][0-9]\+\)*'.
|
\ '\%([,.][0-9]\+\)*'.
|
||||||
|
|
@ -54,7 +75,7 @@ function! LedgerFoldText() "{{{1
|
||||||
let line = getline(lnum)
|
let line = getline(lnum)
|
||||||
|
|
||||||
" Skip metadata/leading comment
|
" Skip metadata/leading comment
|
||||||
if line !~ '^\s\+;'
|
if line !~ '^\%(\s\+;\|\d\)'
|
||||||
" No comment, look for amount...
|
" No comment, look for amount...
|
||||||
let groups = matchlist(line, s:rx_amount)
|
let groups = matchlist(line, s:rx_amount)
|
||||||
if ! empty(groups)
|
if ! empty(groups)
|
||||||
|
|
@ -97,11 +118,149 @@ function! LedgerFoldText() "{{{1
|
||||||
return printf(fmt, foldtext, amount)
|
return printf(fmt, foldtext, amount)
|
||||||
endfunction "}}}
|
endfunction "}}}
|
||||||
|
|
||||||
|
function! LedgerComplete(findstart, base) "{{{1
|
||||||
|
if a:findstart
|
||||||
|
let lnum = line('.')
|
||||||
|
let line = getline('.')
|
||||||
|
let lastcol = col('.') - 2
|
||||||
|
if line =~ '^\d' "{{{2 (date / payee / description)
|
||||||
|
let b:compl_context = 'payee'
|
||||||
|
return -1
|
||||||
|
elseif line =~ '^\s\+;' "{{{2 (metadata / tags)
|
||||||
|
let b:compl_context = 'meta-tag'
|
||||||
|
let first_possible = matchend(line, '^\s\+;')
|
||||||
|
|
||||||
|
" find first column of text to be replaced
|
||||||
|
let firstcol = lastcol
|
||||||
|
while firstcol >= 0
|
||||||
|
if firstcol <= first_possible
|
||||||
|
" Stop before the ';' don't ever include it
|
||||||
|
let firstcol = first_possible
|
||||||
|
break
|
||||||
|
elseif line[firstcol] =~ ':'
|
||||||
|
" Stop before first ':'
|
||||||
|
let firstcol += 1
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
|
||||||
|
let firstcol -= 1
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
" strip whitespace starting from firstcol
|
||||||
|
let end_of_whitespace = matchend(line, '^\s\+', firstcol)
|
||||||
|
if end_of_whitespace != -1
|
||||||
|
let firstcol = end_of_whitespace
|
||||||
|
endif
|
||||||
|
|
||||||
|
return firstcol
|
||||||
|
elseif line =~ '^\s\+' "{{{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
|
||||||
|
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
|
||||||
|
else
|
||||||
|
if b:compl_context == 'account' "{{{2 (account)
|
||||||
|
unlet! b:compl_context
|
||||||
|
let hierarchy = split(a:base, ':')
|
||||||
|
if a:base =~ ':$'
|
||||||
|
call add(hierarchy, '')
|
||||||
|
endif
|
||||||
|
|
||||||
|
let results = LedgerFindInTree(LedgerGetAccountHierarchy(), hierarchy)
|
||||||
|
" sort by alphabet and reverse because it will get reversed one more time
|
||||||
|
let results = reverse(sort(results))
|
||||||
|
if g:ledger_detailed_first
|
||||||
|
let results = sort(results, 's:sort_accounts_by_depth')
|
||||||
|
endif
|
||||||
|
call add(results, a:base)
|
||||||
|
return reverse(results)
|
||||||
|
elseif b:compl_context == 'meta-tag' "{{{2
|
||||||
|
unlet! b:compl_context
|
||||||
|
let results = [a:base]
|
||||||
|
call extend(results, sort(s:filter_items(keys(LedgerGetTags()), a:base)))
|
||||||
|
return results
|
||||||
|
else "}}}
|
||||||
|
unlet! b:compl_context
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endf "}}}
|
||||||
|
|
||||||
|
function! LedgerFindInTree(tree, levels) "{{{1
|
||||||
|
if empty(a:levels)
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
let results = []
|
||||||
|
let currentlvl = a:levels[0]
|
||||||
|
let nextlvls = a:levels[1:]
|
||||||
|
let branches = s:filter_items(keys(a:tree), currentlvl)
|
||||||
|
for branch in branches
|
||||||
|
call add(results, branch)
|
||||||
|
if !empty(nextlvls)
|
||||||
|
for result in LedgerFindInTree(a:tree[branch], nextlvls)
|
||||||
|
call add(results, branch.':'.result)
|
||||||
|
endfor
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
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! LedgerGetTags() "{{{1
|
||||||
|
let alltags = {}
|
||||||
|
let metalines = s:grep_buffer('^\s\+;\s*\zs.*$')
|
||||||
|
for line in metalines
|
||||||
|
" (spaces at beginning are stripped by matchstr!)
|
||||||
|
if line[0] == ':'
|
||||||
|
" multiple tags
|
||||||
|
for val in split(line, ':')
|
||||||
|
if val !~ '^\s*$'
|
||||||
|
let name = s:strip_spaces(val)
|
||||||
|
let alltags[name] = get(alltags, name, [])
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
elseif line =~ '^.*:.*$'
|
||||||
|
" line with tag=value
|
||||||
|
let name = s:strip_spaces(split(line, ':')[0])
|
||||||
|
let val = s:strip_spaces(join(split(line, ':')[1:], ':'))
|
||||||
|
let values = get(alltags, name, [])
|
||||||
|
call add(values, val)
|
||||||
|
let alltags[name] = values
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
return alltags
|
||||||
|
endf "}}}
|
||||||
|
|
||||||
" Helper functions {{{1
|
" Helper functions {{{1
|
||||||
|
|
||||||
|
" return length of string with fix for multibyte characters
|
||||||
function! s:multibyte_strlen(text) "{{{2
|
function! s:multibyte_strlen(text) "{{{2
|
||||||
return strlen(substitute(a:text, ".", "x", "g"))
|
return strlen(substitute(a:text, ".", "x", "g"))
|
||||||
endfunction "}}}
|
endfunction "}}}
|
||||||
|
|
||||||
|
" get # of visible/usable columns in current window
|
||||||
function! s:get_columns(win) "{{{2
|
function! s:get_columns(win) "{{{2
|
||||||
" As long as vim doesn't provide a command natively,
|
" As long as vim doesn't provide a command natively,
|
||||||
" we have to compute the available columns.
|
" we have to compute the available columns.
|
||||||
|
|
@ -116,3 +275,28 @@ function! s:get_columns(win) "{{{2
|
||||||
return columns
|
return columns
|
||||||
endfunction "}}}
|
endfunction "}}}
|
||||||
|
|
||||||
|
" remove spaces at start and end of string
|
||||||
|
function! s:strip_spaces(text) "{{{2
|
||||||
|
return matchstr(a:text, '^\s*\zs\S\%(.*\S\)\?\ze\s*$')
|
||||||
|
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').'''')
|
||||||
|
endf "}}}
|
||||||
|
|
||||||
|
" return all lines matching an expression, returning only the matched part
|
||||||
|
function! s:grep_buffer(expression) "{{{2
|
||||||
|
let lines = map(getline(1, '$'), 'matchstr(v:val, '''.a:expression.''')')
|
||||||
|
return filter(lines, 'v:val != ""')
|
||||||
|
endf "}}}
|
||||||
|
|
||||||
|
function! s:sort_accounts_by_depth(name1, name2) "{{{2
|
||||||
|
let depth1 = s:count_expression(a:name1, ':')
|
||||||
|
let depth2 = s:count_expression(a:name2, ':')
|
||||||
|
return depth1 == depth2 ? 0 : depth1 > depth2 ? 1 : -1
|
||||||
|
endf "}}}
|
||||||
|
|
||||||
|
function! s:count_expression(text, expression) "{{{2
|
||||||
|
return len(split(a:text, a:expression, 1))-1
|
||||||
|
endf "}}}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue