From 4621f1117ee0a2cd4848531727ef249e98c80835 Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Tue, 23 Jun 2009 01:11:59 +0200 Subject: vim: First try on omni completion for account names That is a completion which is aware of what it should complete. Currently only account names are supported. When you insert an account name like this: Asse You will get a list of top-level accounts that start like this. Go ahead and try something like: As:Ban:Che When you have an account like this, 'Assets:Bank:Checking' should show up. --- contrib/vim/ftplugin/ledger.vim | 74 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index a2a4c468..d74e900f 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -12,7 +12,7 @@ let b:did_ftplugin = 1 let b:undo_ftplugin = "setlocal ". \ "foldmethod< foldtext< ". - \ "include< comments< iskeyword< " + \ "include< comments< iskeyword< omnifunc< " " don't fill fold lines --> cleaner look setl fillchars="fold: " @@ -24,6 +24,7 @@ setl comments=b:; " FIXME: Does not work with something like: " Assets:Accountname with Spaces setl iskeyword+=: +setl omnifunc=LedgerComplete " You can set a maximal number of columns the fold text (excluding amount) " will use by overriding g:ledger_maxwidth in your .vimrc. @@ -97,6 +98,77 @@ function! LedgerFoldText() "{{{1 return printf(fmt, foldtext, amount) endfunction "}}} +function! LedgerComplete(findstart, base) + if a:findstart + let lnum = line('.') + let line = getline('.') + let lastcol = col('.') - 2 + if line =~ '^\d' + let b:compl_context = 'payee' + return -1 + elseif line =~ '^\s\+;' + let b:compl_context = 'meta' + return -1 + elseif line =~ '^\s\+' + let b:compl_context = 'account' + let firstcol = lastcol + while firstcol >= 0 && (matchend(line, '^\%(\S\|\S \S\)\+', (firstcol - 1))-1) == lastcol + let firstcol -= 1 + endwhile + return firstcol + else + return -1 + endif + else + if b:compl_context == 'account' + unlet! b:compl_context + let hierarchy = split(a:base, ':') + if a:base =~ ':$' + call add(hierarchy, '') + endif + + let results = [] + return reverse(LedgerFindInTree(LedgerGetAccountHierarchy(), hierarchy)) + else + unlet! b:compl_context + return [] + endif + endif +endf + +function! LedgerFindInTree(tree, levels) + if empty(a:levels) + return [] + endif + let results = [] + let currentlvl = a:levels[0] + let nextlvls = a:levels[1:] + let branches = filter(keys(a:tree), 'v:val =~ ''^\V'.substitute(currentlvl, '\\', '\\\\', 'g').'''') + 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() + let hierarchy = {} + let accounts = map(getline(1, '$'), 'matchstr(v:val, ''^\s\+\zs[^[:blank:];]\%(\S \S\|\S\)\+\ze'')') + let accounts = filter(accounts, 'v:val != ""') + for name in accounts + let last = hierarchy + for part in split(name, ':') + let last[part] = get(last, part, {}) + let last = last[part] + endfor + endfor + return hierarchy +endf + " Helper functions {{{1 function! s:multibyte_strlen(text) "{{{2 return strlen(substitute(a:text, ".", "x", "g")) -- cgit v1.2.3 From 3b1898f3e809ec00f807da6108c130d003161e38 Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Thu, 25 Jun 2009 13:47:34 +0200 Subject: vim: add current text to completion --- contrib/vim/ftplugin/ledger.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index d74e900f..c965c153 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -127,8 +127,9 @@ function! LedgerComplete(findstart, base) call add(hierarchy, '') endif - let results = [] - return reverse(LedgerFindInTree(LedgerGetAccountHierarchy(), hierarchy)) + let results = LedgerFindInTree(LedgerGetAccountHierarchy(), hierarchy) + call add(results, a:base) + return reverse(results) else unlet! b:compl_context return [] -- cgit v1.2.3 From 4156a0488c08478298109f52bfa9c94d124397ac Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Thu, 25 Jun 2009 13:47:57 +0200 Subject: vim. fix for accounts with spaces --- contrib/vim/ftplugin/ledger.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index c965c153..f56864f5 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -55,7 +55,7 @@ function! LedgerFoldText() "{{{1 let line = getline(lnum) " Skip metadata/leading comment - if line !~ '^\s\+;' + if line !~ '^\%(\s\+;\|\d\)' " No comment, look for amount... let groups = matchlist(line, s:rx_amount) if ! empty(groups) @@ -111,11 +111,11 @@ function! LedgerComplete(findstart, base) return -1 elseif line =~ '^\s\+' let b:compl_context = 'account' - let firstcol = lastcol - while firstcol >= 0 && (matchend(line, '^\%(\S\|\S \S\)\+', (firstcol - 1))-1) == lastcol - let firstcol -= 1 - endwhile - return firstcol + if matchend(line, '^\s\+\%(\S \S\|\S\)\+') <= lastcol + " only allow completion when in or at end of account name + return -1 + endif + return matchend(line, '^\s\+') else return -1 endif -- cgit v1.2.3 From 7aabf2928571dd500668414a93b27a1d8a2e4f5e Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Thu, 25 Jun 2009 16:50:09 +0200 Subject: vim. add function to collect all tags in a file --- contrib/vim/ftplugin/ledger.vim | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index f56864f5..cb9a9fc4 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -170,6 +170,32 @@ function! LedgerGetAccountHierarchy() return hierarchy endf +function! LedgerGetTags() "{{{2 + let alltags = {} + let metalines = map(getline(1, '$'), 'matchstr(v:val, ''^\s\+;\s*\zs.*$'')') + let metalines = filter(metalines, 'v:val != ""') + 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 function! s:multibyte_strlen(text) "{{{2 return strlen(substitute(a:text, ".", "x", "g")) @@ -189,3 +215,6 @@ function! s:get_columns(win) "{{{2 return columns endfunction "}}} +function! s:strip_spaces(text) "{{{2 + return matchstr(a:text, '^\s*\zs\S\%(.*\S\)\?\ze\s*$') +endf "}}} -- cgit v1.2.3 From fbf211c95e61881f0ce2bd1b22bd876211d3e5f4 Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Thu, 25 Jun 2009 17:47:40 +0200 Subject: vim. basic support for tag completion --- contrib/vim/ftplugin/ledger.vim | 55 ++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 11 deletions(-) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index cb9a9fc4..91faa02c 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -98,29 +98,53 @@ function! LedgerFoldText() "{{{1 return printf(fmt, foldtext, amount) endfunction "}}} -function! LedgerComplete(findstart, base) +function! LedgerComplete(findstart, base) "{{{1 if a:findstart let lnum = line('.') let line = getline('.') let lastcol = col('.') - 2 - if line =~ '^\d' + if line =~ '^\d' "{{{2 (date / payee / description) let b:compl_context = 'payee' return -1 - elseif line =~ '^\s\+;' - let b:compl_context = 'meta' - return -1 - elseif line =~ '^\s\+' + 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 return matchend(line, '^\s\+') - else + else "}}} return -1 endif else - if b:compl_context == 'account' + if b:compl_context == 'account' "{{{2 (account) unlet! b:compl_context let hierarchy = split(a:base, ':') if a:base =~ ':$' @@ -130,12 +154,17 @@ function! LedgerComplete(findstart, base) let results = LedgerFindInTree(LedgerGetAccountHierarchy(), hierarchy) call add(results, a:base) return reverse(results) - else + 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 +endf "}}} function! LedgerFindInTree(tree, levels) if empty(a:levels) @@ -144,7 +173,7 @@ function! LedgerFindInTree(tree, levels) let results = [] let currentlvl = a:levels[0] let nextlvls = a:levels[1:] - let branches = filter(keys(a:tree), 'v:val =~ ''^\V'.substitute(currentlvl, '\\', '\\\\', 'g').'''') + let branches = s:filter_items(keys(a:tree), currentlvl) for branch in branches call add(results, branch) if !empty(nextlvls) @@ -218,3 +247,7 @@ endfunction "}}} function! s:strip_spaces(text) "{{{2 return matchstr(a:text, '^\s*\zs\S\%(.*\S\)\?\ze\s*$') endf "}}} + +function! s:filter_items(list, keyword) + return filter(a:list, 'v:val =~ ''^\V'.substitute(a:keyword, '\\', '\\\\', 'g').'''') +endf -- cgit v1.2.3 From a853a1c59a8806b5511b292a8a5fcf6af84515df Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Thu, 25 Jun 2009 17:52:13 +0200 Subject: vim. some refactoring --- contrib/vim/ftplugin/ledger.vim | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index 91faa02c..698a5929 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -166,7 +166,7 @@ function! LedgerComplete(findstart, base) "{{{1 endif endf "}}} -function! LedgerFindInTree(tree, levels) +function! LedgerFindInTree(tree, levels) "{{{1 if empty(a:levels) return [] endif @@ -183,12 +183,11 @@ function! LedgerFindInTree(tree, levels) endif endfor return results -endf +endf "}}} -function! LedgerGetAccountHierarchy() +function! LedgerGetAccountHierarchy() "{{{1 let hierarchy = {} - let accounts = map(getline(1, '$'), 'matchstr(v:val, ''^\s\+\zs[^[:blank:];]\%(\S \S\|\S\)\+\ze'')') - let accounts = filter(accounts, 'v:val != ""') + let accounts = s:grep_buffer('^\s\+\zs[^[:blank:];]\%(\S \S\|\S\)\+\ze') for name in accounts let last = hierarchy for part in split(name, ':') @@ -197,12 +196,11 @@ function! LedgerGetAccountHierarchy() endfor endfor return hierarchy -endf +endf "}}} -function! LedgerGetTags() "{{{2 +function! LedgerGetTags() "{{{1 let alltags = {} - let metalines = map(getline(1, '$'), 'matchstr(v:val, ''^\s\+;\s*\zs.*$'')') - let metalines = filter(metalines, 'v:val != ""') + let metalines = s:grep_buffer('^\s\+;\s*\zs.*$') for line in metalines " (spaces at beginning are stripped by matchstr!) if line[0] == ':' @@ -226,10 +224,13 @@ function! LedgerGetTags() "{{{2 endf "}}} " Helper functions {{{1 + +" return length of string with fix for multibyte characters function! s:multibyte_strlen(text) "{{{2 return strlen(substitute(a:text, ".", "x", "g")) endfunction "}}} +" get # of visible/usable columns in current window function! s:get_columns(win) "{{{2 " As long as vim doesn't provide a command natively, " we have to compute the available columns. @@ -244,10 +245,18 @@ function! s:get_columns(win) "{{{2 return columns 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 "}}} -function! s:filter_items(list, keyword) +" 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 +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 "}}} -- cgit v1.2.3 From 84e6a6e926effcb53148e8d0b0d4b5b79d8914c2 Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Thu, 25 Jun 2009 18:27:07 +0200 Subject: vim. list 'deeper' entries first (completion) --- contrib/vim/ftplugin/ledger.vim | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index 698a5929..4b1c3a5d 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -38,6 +38,30 @@ if !exists('g:ledger_fillstring') let g:ledger_fillstring = ' ' 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 = '\('. \ '\%([0-9]\+\)'. \ '\%([,.][0-9]\+\)*'. @@ -152,6 +176,11 @@ function! LedgerComplete(findstart, base) "{{{1 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 @@ -260,3 +289,13 @@ 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 "}}} -- cgit v1.2.3 From 396e1a5002c8152d0c9bb8b85fd711eece03f221 Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Mon, 29 Jun 2009 15:06:55 +0200 Subject: Fix account completion for virtual transactions --- contrib/vim/ftplugin/ledger.vim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index 4b1c3a5d..05a5d6d7 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -163,7 +163,10 @@ function! LedgerComplete(findstart, base) "{{{1 " only allow completion when in or at end of account name return -1 endif - return matchend(line, '^\s\+') + " 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 @@ -218,6 +221,8 @@ 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, {}) -- cgit v1.2.3 From 2aa9f5115cde5db8b98af45eb0fedfa4955ce6a0 Mon Sep 17 00:00:00 2001 From: Johann Klähn Date: Mon, 29 Jun 2009 16:37:05 +0200 Subject: vim. remove old completion by keyword --- contrib/vim/ftplugin/ledger.vim | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'contrib/vim/ftplugin') diff --git a/contrib/vim/ftplugin/ledger.vim b/contrib/vim/ftplugin/ledger.vim index 05a5d6d7..d75a6869 100644 --- a/contrib/vim/ftplugin/ledger.vim +++ b/contrib/vim/ftplugin/ledger.vim @@ -12,7 +12,7 @@ let b:did_ftplugin = 1 let b:undo_ftplugin = "setlocal ". \ "foldmethod< foldtext< ". - \ "include< comments< iskeyword< omnifunc< " + \ "include< comments< omnifunc< " " don't fill fold lines --> cleaner look setl fillchars="fold: " @@ -20,10 +20,6 @@ setl foldtext=LedgerFoldText() setl foldmethod=syntax setl include=^!include setl comments=b:; -" so you can use C-X C-N completion on accounts -" FIXME: Does not work with something like: -" Assets:Accountname with Spaces -setl iskeyword+=: setl omnifunc=LedgerComplete " You can set a maximal number of columns the fold text (excluding amount) -- cgit v1.2.3