Merge branch 'master' into next

This commit is contained in:
John Wiegley 2011-06-30 11:55:03 -05:00
commit 80f81bdd96
4 changed files with 132 additions and 87 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 You can also use a modeline like this in every ledger file
vim:filetype=ledger 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 Configuration
====================================================================== ======================================================================
Include the following let-statements somewhere in your .vimrc Include the following let-statements somewhere in your .vimrc
@ -35,7 +51,7 @@ to modify the behaviour of the ledger filetype.
Completion Completion
====================================================================== ======================================================================
Omni completion is implemented for account names and tags. Omni completion is currently implemented for account names only.
Accounts 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. 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. 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 License
====================================================================== ======================================================================
Copyright 2009 by Johann Klähn Copyright 2011-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
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,5 @@
" Vim filetype plugin file " Vim filetype plugin file
" filetype: ledger " filetype: ledger
" Version: 0.1.0
" by Johann Klähn; Use according to the terms of the GPL>=2. " by Johann Klähn; Use according to the terms of the GPL>=2.
" vim:ts=2:sw=2:sts=2:foldmethod=marker " vim:ts=2:sw=2:sts=2:foldmethod=marker
@ -137,22 +136,33 @@ function! LedgerComplete(findstart, base) "{{{1
if a:findstart if a:findstart
let lnum = line('.') let lnum = line('.')
let line = getline('.') let line = getline('.')
let lastcol = col('.') - 2
let b:compl_context = '' let b:compl_context = ''
if line =~ '^\s\+[^[:blank:];]' "{{{2 (account) if line =~ '^\s\+[^[:blank:];]' "{{{2 (account)
let b:compl_context = 'account' " only allow completion when in or at end of account name
if matchend(line, '^\s\+\%(\S \S\|\S\)\+') <= lastcol if matchend(line, '^\s\+\%(\S \S\|\S\)\+') >= col('.') - 1
" only allow completion when in or at end of account name " the start of the first non-blank character
return -1 " (excluding virtual-transaction-marks)
" is the beginning of the account name
let b:compl_context = 'account'
return matchend(line, '^\s\+[\[(]\?')
endif endif
" the start of the first non-blank character elseif line =~ '^\d' "{{{2 (description)
" (excluding virtual-transaction-marks) let pre = matchend(line, '^\d\S\+\%(([^)]*)\|[*?!]\|\s\)\+')
" is the beginning of the account name if pre < col('.') - 1
return matchend(line, '^\s\+[\[(]\?') let b:compl_context = 'description'
else "}}} return pre
return -1 endif
endif elseif line =~ '^$' "{{{2 (new line)
let b:compl_context = 'new'
endif "}}}
return -1
else 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) if b:compl_context == 'account' "{{{2 (account)
unlet! b:compl_context unlet! b:compl_context
let hierarchy = split(a:base, ':') let hierarchy = split(a:base, ':')
@ -160,7 +170,7 @@ function! LedgerComplete(findstart, base) "{{{1
call add(hierarchy, '') call add(hierarchy, '')
endif 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 " sort by alphabet and reverse because it will get reversed one more time
if g:ledger_detailed_first if g:ledger_detailed_first
let results = reverse(sort(results, 's:sort_accounts_by_depth')) let results = reverse(sort(results, 's:sort_accounts_by_depth'))
@ -168,10 +178,19 @@ function! LedgerComplete(findstart, base) "{{{1
let results = sort(results) let results = sort(results)
endif endif
call insert(results, a:base) call insert(results, a:base)
return results elseif b:compl_context == 'description' "{{{2 (description)
else "}}} 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 unlet! b:compl_context
return [] return results
endif endif
endif endif
endf "}}} endf "}}}
@ -195,21 +214,6 @@ function! LedgerFindInTree(tree, levels) "{{{1
return results return results
endf "}}} 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, ...) function! LedgerToggleTransactionState(lnum, ...)
if a:0 == 1 if a:0 == 1
let chars = a:1 let chars = a:1
@ -278,6 +282,51 @@ function! LedgerSetDate(lnum, type, ...) "{{{1
call setline(trans['head'], trans.format_head()) call setline(trans['head'], trans.format_head())
endf "}}} 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 let s:transaction = {} "{{{1
function! s:transaction.new() dict function! s:transaction.new() dict
return copy(s:transaction) return copy(s:transaction)
@ -293,7 +342,15 @@ function! s:transaction.from_lnum(lnum) dict "{{{2
let trans['head'] = head let trans['head'] = head
let trans['tail'] = tail 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] ==# '~' if parts[0] ==# '~'
let trans['expr'] = join(parts[1:]) let trans['expr'] = join(parts[1:])
return trans return trans
@ -318,8 +375,6 @@ function! s:transaction.from_lnum(lnum) dict "{{{2
call remove(parts, 0) call remove(parts, 0)
endfor 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) let trans['description'] = join(parts)
return trans return trans
endf "}}} 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, 'code') | call add(parts, '('.self['code'].')') | endif
if has_key(self, 'state') | call add(parts, self['state']) | endif if has_key(self, 'state') | call add(parts, self['state']) | endif
if has_key(self, 'description') | call add(parts, self['description']) | 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 "}}} endf "}}}
"}}} "}}}
@ -518,7 +577,7 @@ endf "}}}
" return only those items that start with a specified keyword " return only those items that start with a specified keyword
function! s:filter_items(list, keyword) "{{{2 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 "}}} endf "}}}
" return all lines matching an expression, returning only the matched part " return all lines matching an expression, returning only the matched part

View file

@ -1,19 +1,8 @@
" Vim syntax file " Vim syntax file
" filetype: ledger " filetype: ledger
" Version: 0.1.0
" by Johann Klähn; Use according to the terms of the GPL>=2. " 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 Stefan Karrmann; Use according to the terms of the GPL>=2.
" by Wolfgang Oertl; 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 " vim:ts=2:sw=2:sts=2:foldmethod=marker
if version < 600 if version < 600

View file

@ -433,7 +433,9 @@ dropped."
;;;###autoload ;;;###autoload
(define-derived-mode ledger-mode text-mode "Ledger" (define-derived-mode ledger-mode text-mode "Ledger"
"A mode for editing ledger data files." "A mode for editing ledger data files.
\\{ledger-mode-map}"
(set (make-local-variable 'comment-start) " ; ") (set (make-local-variable 'comment-start) " ; ")
(set (make-local-variable 'comment-end) "") (set (make-local-variable 'comment-end) "")
(set (make-local-variable 'indent-tabs-mode) nil) (set (make-local-variable 'indent-tabs-mode) nil)
@ -632,8 +634,7 @@ dropped."
(defvar ledger-reconcile-mode-abbrev-table) (defvar ledger-reconcile-mode-abbrev-table)
(define-derived-mode ledger-reconcile-mode text-mode "Reconcile" (defvar ledger-reconcile-mode-map
"A mode for reconciling ledger entries."
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(define-key map [(control ?m)] 'ledger-reconcile-visit) (define-key map [(control ?m)] 'ledger-reconcile-visit)
(define-key map [return] 'ledger-reconcile-visit) (define-key map [return] 'ledger-reconcile-visit)
@ -647,7 +648,12 @@ dropped."
(define-key map [?p] 'previous-line) (define-key map [?p] 'previous-line)
(define-key map [?s] 'ledger-reconcile-save) (define-key map [?s] 'ledger-reconcile-save)
(define-key map [?q] 'ledger-reconcile-quit) (define-key map [?q] 'ledger-reconcile-quit)
(use-local-map map))) map))
(define-derived-mode ledger-reconcile-mode text-mode "Reconcile"
"A mode for reconciling ledger entries.
\\{ledger-reconcile-mode-map}")
;; Context sensitivity ;; Context sensitivity
@ -807,8 +813,7 @@ specified line, returns nil."
(defvar ledger-report-mode-abbrev-table) (defvar ledger-report-mode-abbrev-table)
(define-derived-mode ledger-report-mode text-mode "Ledger-Report" (defvar ledger-report-mode-map
"A mode for viewing ledger reports."
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(define-key map [? ] 'scroll-up) (define-key map [? ] 'scroll-up)
(define-key map [backspace] 'scroll-down) (define-key map [backspace] 'scroll-down)
@ -825,7 +830,10 @@ specified line, returns nil."
'ledger-report-kill) 'ledger-report-kill)
(define-key map [(control ?c) (control ?l) (control ?e)] (define-key map [(control ?c) (control ?l) (control ?e)]
'ledger-report-edit) 'ledger-report-edit)
(use-local-map map))) map))
(define-derived-mode ledger-report-mode text-mode "Ledger-Report"
"A mode for viewing ledger reports.")
(defun ledger-report-read-name () (defun ledger-report-read-name ()
"Read the name of a ledger report to use, with completion. "Read the name of a ledger report to use, with completion.
@ -1201,7 +1209,7 @@ the default."
;; A sample function for $ users ;; A sample function for $ users
(defun ledger-next-amount (&optional end) (defun ledger-next-amount (&optional end)
(when (re-search-forward "\\( \\|\t\\| \t\\)[ \t]*-?\\([A-Z$]+ *\\)?\\(-?[0-9,]+?\\)\\(.[0-9]+\\)?\\( *[A-Z$]+\\)?\\([ \t]*@@?[^\n;]+?\\)?\\([ \t]+;.+?\\)?$" (marker-position end) t) (when (re-search-forward "\\( \\|\t\\| \t\\)[ \t]*-?\\([A-Z$€£]+ *\\)?\\(-?[0-9,]+?\\)\\(.[0-9]+\\)?\\( *[A-Z$€£]+\\)?\\([ \t]*@@?[^\n;]+?\\)?\\([ \t]+;.+?\\)?$" (marker-position end) t)
(goto-char (match-beginning 0)) (goto-char (match-beginning 0))
(skip-syntax-forward " ") (skip-syntax-forward " ")
(- (or (match-end 4) (- (or (match-end 4)