Merge remote branch 'kljohann/vim' into next
This commit is contained in:
commit
460ad6ee76
3 changed files with 338 additions and 110 deletions
|
|
@ -12,20 +12,17 @@ if exists(":CompilerSet") != 2
|
|||
command -nargs=* CompilerSet setlocal <args>
|
||||
endif
|
||||
|
||||
if ! exists("g:ledger_bin") || ! executable(g:ledger_bin)
|
||||
if executable('ledger')
|
||||
let g:ledger_bin = 'ledger'
|
||||
else
|
||||
echoerr "ledger command not found. Set g:ledger_bin or extend $PATH."
|
||||
finish
|
||||
endif
|
||||
" default value will be set in ftplugin
|
||||
if ! exists("g:ledger_bin") || empty(g:ledger_bin) || ! executable(split(g:ledger_bin, '\s')[0])
|
||||
finish
|
||||
endif
|
||||
|
||||
" %-G throws away blank lines, everything else is assumed to be part of a
|
||||
" multi-line error message.
|
||||
CompilerSet errorformat=%-G,%EWhile\ parsing\ file\ \"%f\"\\,\ line\ %l:%.%#,%ZError:\ %m,%C%.%#
|
||||
CompilerSet errorformat+=%tarning:\ \"%f\"\\,\ line\ %l:\ %m
|
||||
|
||||
" unfortunately there is no 'check file' command,
|
||||
" so we will just use a query that returns no results. ever.
|
||||
exe 'CompilerSet makeprg='.g:ledger_bin.'\ -f\ %\ reg\ not\ ''.*''\ \>\ /dev/null'
|
||||
exe 'CompilerSet makeprg='.substitute(g:ledger_bin, ' ', '\\ ', 'g').'\ -f\ %\ reg\ not\ ''.*''\ \>\ /dev/null'
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ let b:did_ftplugin = 1
|
|||
|
||||
let b:undo_ftplugin = "setlocal ".
|
||||
\ "foldmethod< foldtext< ".
|
||||
\ "include< comments< omnifunc< "
|
||||
\ "include< comments< omnifunc< formatprg<"
|
||||
|
||||
" don't fill fold lines --> cleaner look
|
||||
setl fillchars="fold: "
|
||||
|
|
@ -22,6 +22,21 @@ setl include=^!include
|
|||
setl comments=b:;
|
||||
setl omnifunc=LedgerComplete
|
||||
|
||||
" set location of ledger binary for checking and auto-formatting
|
||||
if ! exists("g:ledger_bin") || empty(g:ledger_bin) || ! executable(split(g:ledger_bin, '\s')[0])
|
||||
if executable('ledger')
|
||||
let g:ledger_bin = 'ledger'
|
||||
else
|
||||
unlet g:ledger_bin
|
||||
echoerr "ledger command not found. Set g:ledger_bin or extend $PATH ".
|
||||
\ "to enable error checking and auto-formatting."
|
||||
endif
|
||||
endif
|
||||
|
||||
if exists("g:ledger_bin")
|
||||
exe 'setl formatprg='.substitute(g:ledger_bin, ' ', '\\ ', 'g').'\ -f\ -\ print'
|
||||
endif
|
||||
|
||||
" You can set a maximal number of columns the fold text (excluding amount)
|
||||
" will use by overriding g:ledger_maxwidth in your .vimrc.
|
||||
" When maxwidth is zero, the amount will be displayed at the far right side
|
||||
|
|
@ -92,7 +107,7 @@ function! LedgerFoldText() "{{{1
|
|||
\ '\(^\s\+\|\s\+$\)', '', 'g')
|
||||
|
||||
" number of columns foldtext can use
|
||||
let columns = s:get_columns(0)
|
||||
let columns = s:get_columns()
|
||||
if g:ledger_maxwidth
|
||||
let columns = min([columns, g:ledger_maxwidth])
|
||||
endif
|
||||
|
|
@ -107,9 +122,9 @@ function! LedgerFoldText() "{{{1
|
|||
let foldtext .= repeat(' ', filen - (folen%filen))
|
||||
|
||||
let foldtext .= repeat(g:ledger_fillstring,
|
||||
\ s:get_columns(0)/filen)
|
||||
\ s:get_columns()/filen)
|
||||
else
|
||||
let foldtext .= repeat(' ', s:get_columns(0))
|
||||
let foldtext .= repeat(' ', s:get_columns())
|
||||
endif
|
||||
|
||||
" we don't use slices[:5], because that messes up multibyte characters
|
||||
|
|
@ -123,37 +138,7 @@ function! LedgerComplete(findstart, base) "{{{1
|
|||
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)
|
||||
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
|
||||
|
|
@ -176,16 +161,12 @@ function! LedgerComplete(findstart, base) "{{{1
|
|||
|
||||
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')
|
||||
let results = reverse(sort(results, 's:sort_accounts_by_depth'))
|
||||
else
|
||||
let results = sort(results)
|
||||
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)))
|
||||
call insert(results, a:base)
|
||||
return results
|
||||
else "}}}
|
||||
unlet! b:compl_context
|
||||
|
|
@ -228,52 +209,306 @@ function! LedgerGetAccountHierarchy() "{{{1
|
|||
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
|
||||
function! LedgerToggleTransactionState(lnum, ...)
|
||||
if a:0 == 1
|
||||
let chars = a:1
|
||||
else
|
||||
let chars = ' *'
|
||||
endif
|
||||
let trans = s:transaction.from_lnum(a:lnum)
|
||||
if empty(trans)
|
||||
return
|
||||
endif
|
||||
|
||||
let old = has_key(trans, 'state') ? trans['state'] : ' '
|
||||
let i = stridx(chars, old) + 1
|
||||
let new = chars[i >= len(chars) ? 0 : i]
|
||||
|
||||
call trans.set_state(new)
|
||||
|
||||
call setline(trans['head'], trans.format_head())
|
||||
endf
|
||||
|
||||
function! LedgerSetTransactionState(lnum, char) "{{{1
|
||||
" modifies or sets the state of the transaction at the cursor,
|
||||
" removing the state alltogether if a:char is empty
|
||||
let trans = s:transaction.from_lnum(a:lnum)
|
||||
if empty(trans)
|
||||
return
|
||||
endif
|
||||
|
||||
call trans.set_state(a:char)
|
||||
|
||||
call setline(trans['head'], trans.format_head())
|
||||
endf "}}}
|
||||
|
||||
function! LedgerSetDate(lnum, type, ...) "{{{1
|
||||
let time = a:0 == 1 ? a:1 : localtime()
|
||||
let trans = s:transaction.from_lnum(a:lnum)
|
||||
if empty(trans)
|
||||
return
|
||||
endif
|
||||
|
||||
let formatted = strftime('%Y/%m/%d', time)
|
||||
if has_key(trans, 'date') && ! empty(trans['date'])
|
||||
let date = split(trans['date'], '=')
|
||||
else
|
||||
let date = [formatted]
|
||||
endif
|
||||
|
||||
if a:type ==? 'actual'
|
||||
let date[0] = formatted
|
||||
elseif a:type ==? 'effective'
|
||||
if time < 0
|
||||
" remove effective date
|
||||
let date = [date[0]]
|
||||
else
|
||||
" set effective date
|
||||
if len(date) >= 2
|
||||
let date[1] = formatted
|
||||
else
|
||||
call add(date, formatted)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
let trans['date'] = join(date, '=')
|
||||
|
||||
call setline(trans['head'], trans.format_head())
|
||||
endf "}}}
|
||||
|
||||
let s:transaction = {} "{{{1
|
||||
function! s:transaction.new() dict
|
||||
return copy(s:transaction)
|
||||
endf
|
||||
|
||||
function! s:transaction.from_lnum(lnum) dict "{{{2
|
||||
let [head, tail] = s:get_transaction_extents(a:lnum)
|
||||
if ! head
|
||||
return {}
|
||||
endif
|
||||
|
||||
let trans = copy(s:transaction)
|
||||
let trans['head'] = head
|
||||
let trans['tail'] = tail
|
||||
|
||||
let parts = split(getline(head), '\s\+')
|
||||
if parts[0] ==# '~'
|
||||
let trans['expr'] = join(parts[1:])
|
||||
return trans
|
||||
elseif parts[0] !~ '^\d'
|
||||
" this case is avoided in s:get_transaction_extents(),
|
||||
" but we'll check anyway.
|
||||
return {}
|
||||
endif
|
||||
|
||||
for part in parts
|
||||
if ! has_key(trans, 'date') && part =~ '^\d'
|
||||
let trans['date'] = part
|
||||
elseif ! has_key(trans, 'code') && part =~ '^([^)]*)$'
|
||||
let trans['code'] = part[1:-2]
|
||||
elseif ! has_key(trans, 'state') && part =~ '^[[:punct:]]$'
|
||||
" the first character by itself is assumed to be the state of the transaction.
|
||||
let trans['state'] = part
|
||||
else
|
||||
" everything after date/code or state belongs to the description
|
||||
break
|
||||
endif
|
||||
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 "}}}
|
||||
|
||||
function! s:transaction.set_state(char) dict "{{{2
|
||||
if has_key(self, 'state') && a:char =~ '^\s*$'
|
||||
call remove(self, 'state')
|
||||
else
|
||||
let self['state'] = a:char
|
||||
endif
|
||||
endf "}}}
|
||||
|
||||
function! s:transaction.parse_body(...) dict "{{{2
|
||||
if a:0 == 2
|
||||
let head = a:1
|
||||
let tail = a:2
|
||||
elseif a:0 == 0
|
||||
let head = self['head']
|
||||
let tail = self['tail']
|
||||
else
|
||||
throw "wrong number of arguments for parse_body()"
|
||||
return []
|
||||
endif
|
||||
|
||||
if ! head || tail <= head
|
||||
return []
|
||||
endif
|
||||
|
||||
let lnum = head
|
||||
let tags = {}
|
||||
let postings = []
|
||||
while lnum <= tail
|
||||
let line = split(getline(lnum), '\s*\%(\t\| \);', 1)
|
||||
|
||||
if line[0] =~ '^\s\+[^[:blank:];]'
|
||||
" posting
|
||||
" FIXME: replaces original spacing in amount with single spaces
|
||||
let parts = split(line[0], '\%(\t\| \)\s*')
|
||||
call add(postings, {'account': parts[0], 'amount': join(parts[1:], ' ')})
|
||||
end
|
||||
|
||||
" where are tags to be stored?
|
||||
if empty(postings)
|
||||
" they belong to the transaction
|
||||
let tag_container = tags
|
||||
else
|
||||
" they belong to last posting
|
||||
if ! has_key(postings[-1], 'tags')
|
||||
let postings[-1]['tags'] = {}
|
||||
endif
|
||||
let tag_container = postings[-1]['tags']
|
||||
endif
|
||||
|
||||
let comment = join(line[1:], ' ;')
|
||||
if comment =~ '^\s*:'
|
||||
" tags without values
|
||||
for t in s:findall(comment, ':\zs[^:[:blank:]]\([^:]*[^:[:blank:]]\)\?\ze:')
|
||||
let tag_container[t] = ''
|
||||
endfor
|
||||
elseif comment =~ '^\s*[^:[:blank:]][^:]\+:'
|
||||
" tag with value
|
||||
let key = matchstr(comment, '^\s*\zs[^:]\+\ze:')
|
||||
if ! empty(key)
|
||||
let val = matchstr(comment, ':\s*\zs.*\ze\s*$')
|
||||
let tag_container[key] = val
|
||||
endif
|
||||
endif
|
||||
let lnum += 1
|
||||
endw
|
||||
return [tags, postings]
|
||||
endf "}}}
|
||||
|
||||
function! s:transaction.format_head() dict "{{{2
|
||||
if has_key(self, 'expr')
|
||||
return '~ '.self['expr']
|
||||
endif
|
||||
|
||||
let parts = []
|
||||
if has_key(self, 'date') | call add(parts, self['date']) | 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, 'description') | call add(parts, self['description']) | endif
|
||||
return join(parts)
|
||||
endf "}}}
|
||||
"}}}
|
||||
|
||||
" Helper functions {{{1
|
||||
|
||||
function! s:get_transactions(...) "{{{2
|
||||
if a:0 == 2
|
||||
let lnum = a:1
|
||||
let end = a:2
|
||||
elseif a:0 == 0
|
||||
let lnum = 1
|
||||
let end = line('$')
|
||||
else
|
||||
throw "wrong number of arguments for get_transactions()"
|
||||
return []
|
||||
endif
|
||||
|
||||
" safe view / position
|
||||
let view = winsaveview()
|
||||
let fe = &foldenable
|
||||
set nofoldenable
|
||||
|
||||
let transactions = []
|
||||
call cursor(lnum, 0)
|
||||
while lnum && lnum <= end
|
||||
let trans = s:transaction.from_lnum(lnum)
|
||||
if ! empty(trans)
|
||||
call add(transactions, trans)
|
||||
call cursor(trans['tail'], 0)
|
||||
endif
|
||||
let lnum = search('^[~[:digit:]]\S\+', 'cW')
|
||||
endw
|
||||
|
||||
" restore view / position
|
||||
let &foldenable = fe
|
||||
call winrestview(view)
|
||||
|
||||
return transactions
|
||||
endf "}}}
|
||||
|
||||
function! s:get_transaction_extents(lnum) "{{{2
|
||||
if ! (indent(a:lnum) || getline(a:lnum) =~ '^[~[:digit:]]\S\+')
|
||||
" only do something if lnum is in a transaction
|
||||
return [0, 0]
|
||||
endif
|
||||
|
||||
" safe view / position
|
||||
let view = winsaveview()
|
||||
let fe = &foldenable
|
||||
set nofoldenable
|
||||
|
||||
call cursor(a:lnum, 0)
|
||||
let head = search('^[~[:digit:]]\S\+', 'bcnW')
|
||||
let tail = search('^[^;[:blank:]]\S\+', 'nW')
|
||||
let tail = tail > head ? tail - 1 : line('$')
|
||||
|
||||
" restore view / position
|
||||
let &foldenable = fe
|
||||
call winrestview(view)
|
||||
|
||||
return head ? [head, tail] : [0, 0]
|
||||
endf "}}}
|
||||
|
||||
function! s:findall(text, rx) " {{{2
|
||||
" returns all the matches in a string,
|
||||
" there will be overlapping matches according to :help match()
|
||||
let matches = []
|
||||
|
||||
while 1
|
||||
let m = matchstr(a:text, a:rx, 0, len(matches)+1)
|
||||
if empty(m)
|
||||
break
|
||||
endif
|
||||
|
||||
call add(matches, m)
|
||||
endw
|
||||
|
||||
return matches
|
||||
endf "}}}
|
||||
|
||||
" 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
|
||||
function! s:get_columns() " {{{2
|
||||
" As long as vim doesn't provide a command natively,
|
||||
" we have to compute the available columns.
|
||||
" see :help todo.txt -> /Add argument to winwidth()/
|
||||
" FIXME: Although this will propably never be used with debug mode enabled
|
||||
" this should take the signs column into account (:help sign.txt)
|
||||
let columns = (winwidth(a:win) == 0 ? 80 : winwidth(a:win)) - &foldcolumn
|
||||
|
||||
let columns = (winwidth(0) == 0 ? 80 : winwidth(0)) - &foldcolumn
|
||||
if &number
|
||||
" line('w$') is the line number of the last line
|
||||
let columns -= max([len(line('w$'))+1, &numberwidth])
|
||||
endif
|
||||
|
||||
" are there any signs/is the sign column displayed?
|
||||
redir => signs
|
||||
silent execute 'sign place buffer='.string(bufnr("%"))
|
||||
redir END
|
||||
if signs =~# 'id='
|
||||
let columns -= 2
|
||||
endif
|
||||
|
||||
return columns
|
||||
endfunction "}}}
|
||||
endf "}}}
|
||||
|
||||
" remove spaces at start and end of string
|
||||
function! s:strip_spaces(text) "{{{2
|
||||
|
|
|
|||
|
|
@ -28,43 +28,39 @@ syntax clear
|
|||
" DATE[=EDATE] [*|!] [(CODE)] DESC <-- first line of transaction
|
||||
" ACCOUNT AMOUNT [; NOTE] <-- posting
|
||||
|
||||
" region: a transaction containing postings
|
||||
syn region transNorm start=/^[[:digit:]~]/ skip=/^\s/ end=/^/
|
||||
\ fold keepend transparent contains=transDate,Metadata,Posting
|
||||
syn match transDate /^\d\S\+/ contained
|
||||
syn match Metadata /^\s\+;.*/ contained contains=MetadataTag
|
||||
syn match Comment /^;.*$/
|
||||
syn region ledgerTransaction start=/^[[:digit:]~]/ skip=/^\s/ end=/^/
|
||||
\ fold keepend transparent contains=ledgerTransactionDate,ledgerMetadata,ledgerPosting
|
||||
syn match ledgerTransactionDate /^\d\S\+/ contained
|
||||
syn match ledgerPosting /^\s\+[^[:blank:];][^;]*\ze\%($\|;\)/
|
||||
\ contained transparent contains=ledgerAccount,ledgerMetadata
|
||||
" every space in an account name shall be surrounded by two non-spaces
|
||||
" every account name ends with a tab, two spaces or the end of the line
|
||||
syn match Account /^\s\+\zs\%(\S \S\|\S\)\+\ze\%([ ]\{2,}\|\t\s*\|\s*$\)/ contained
|
||||
syn match Posting /^\s\+[^[:blank:];].*$/ contained transparent contains=Account,Amount
|
||||
" FIXME: add other symbols?
|
||||
let s:currency = '\([$€£¢]\|\w\+\)'
|
||||
let s:figures = '\d\+\([.,]\d\+\)*'
|
||||
let s:amount = '-\?\('.s:figures.'\s*'.s:currency.'\|'.s:currency.'\s*'.s:figures.'\)'
|
||||
exe 'syn match Amount /'.s:amount.'/ contained'
|
||||
syn match MetadataTag /:[^:]\+:/hs=s+1,he=e-1 contained
|
||||
syn match MetadataTag /;\s*\zs[^:]\+\ze:[^:]\+$/ contained
|
||||
syn match ledgerAccount /^\s\+\zs\%(\S \S\|\S\)\+\ze\%( \|\t\|\s*$\)/ contained
|
||||
|
||||
syn region TagStack
|
||||
\ matchgroup=TagPush start=/^tag\>/
|
||||
\ matchgroup=TagPop end=/^pop\>/
|
||||
\ contains=TagHead,TagStack,transNorm
|
||||
syn match TagHead /\%(^tag\s\+\)\@<=\S.*$/ contained contains=tagKey transparent
|
||||
syn match TagKey /:[^:]\+:/hs=s+1,he=e-1 contained
|
||||
syn match TagKey /\%(^tag\s\+\)\@<=[^:]\+\ze:[^:]\+$/ contained
|
||||
syn match ledgerComment /^;.*$/
|
||||
" comments at eol must be preceeded by at least 2 spaces / 1 tab
|
||||
syn region ledgerMetadata start=/\%( \|\t\|^\s\+\);/ skip=/^\s\+;/ end=/^/
|
||||
\ keepend contained contains=ledgerTag,ledgerTypedTag
|
||||
syn match ledgerTag /:[^:]\+:/hs=s+1,he=e-1 contained
|
||||
syn match ledgerTag /\%(\%(;\|^tag\)[^:]\+\)\@<=[^:]\+:\ze[^:]\+$/ contained
|
||||
syn match ledgerTypedTag /\%(\%(;\|^tag\)[^:]\+\)\@<=[^:]\+::\ze[^:]\+$/ contained
|
||||
|
||||
highlight default link transDate Constant
|
||||
highlight default link Metadata Tag
|
||||
highlight default link MetadataTag Type
|
||||
highlight default link TagPop Tag
|
||||
highlight default link TagPush Tag
|
||||
highlight default link TagKey Type
|
||||
highlight default link Amount Number
|
||||
highlight default link Account Identifier
|
||||
syn region ledgerTagStack
|
||||
\ matchgroup=ledgerTagPush start=/^tag\>/
|
||||
\ matchgroup=ledgerTagPop end=/^pop\>/
|
||||
\ contains=ledgerTagHead,ledgerTagStack,ledgerTransaction,ledgerComment
|
||||
syn match ledgerTagHead /\%(^tag\s\+\)\@<=\S.*$/ contained contains=ledgerTag transparent
|
||||
|
||||
highlight default link ledgerTransactionDate Constant
|
||||
highlight default link ledgerMetadata Tag
|
||||
highlight default link ledgerTypedTag Keyword
|
||||
highlight default link ledgerTag Type
|
||||
highlight default link ledgerTagPop Tag
|
||||
highlight default link ledgerTagPush Tag
|
||||
highlight default link ledgerAccount Identifier
|
||||
|
||||
" syncinc is easy: search for the first transaction.
|
||||
syn sync clear
|
||||
syn sync match ledgerSync grouphere transNorm "^[[:digit:]~]"
|
||||
syn sync match ledgerSync grouphere ledgerTransaction "^[[:digit:]~]"
|
||||
|
||||
let b:current_syntax = "ledger"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue