vim-jp/vim-vimlparser

seems dead loop while parse `write ++enc=utf-8`

iamcco opened this issue · 4 comments

Way to reproducte:

node vimlparser.js --neovim t.vim

https://github.com/oblitum/formatvim/blob/master/autoload/format.vim

t.vim

"▶1 Начало
scriptencoding utf-8
execute frawor#Setup('4.1', {'@/options': '0.0',
            \                     '@/os': '0.0',
            \                    '@/fwc': '0.0',
            \        '@/fwc/constructor': '4.2',
            \              '@/functions': '0.0',
            \                  '@/table': '0.1',
            \   '@/decorators/altervars': '0.0',
            \                 '@/base64': '0.0',})
let s:formats={}
let s:keylist=['begin',
            \   'sbsdstart',
            \    'sbsdsep',
            \    'foldstart',
            \     'linestart',
            \      'foldcolumn', 'sign', 'linenr',
            \      'tagstart',
            \       'linkstart',
            \        'concealedstart',
            \         'line',
            \        'concealedend',
            \       'linkend',
            \      'tagend',
            \      'fold', 'difffiller', 'collapsedfiller',
            \     'lineend',
            \    'foldend',
            \   'sbsdend',
            \  'end', 'style']
" Used solely to not let vim delete functions which were profiled
let s:profiled=[]
let s:strdisplaywidthstr=(exists('*strdisplaywidth')?'strdisplaywidth': 's:_r.strdisplaywidth')
"▶1
if !has('syntax')
    call s:_f.warn('synnsup')
endif
"▶1 Options
" Debugging options:
"   Debugging: Disabled by default, enables other Debugging_* options.
"   Debugging_FuncF: If not zero, writes the resulting compiled function to the given file
"   Debugging_MinFu: If true, minimizes compiled function code
"   Debugging_Break: Dictionary mapping function name to a list of breaks. Break is either a regex 
"                    that line must match or a line number. Regex is a subject to %var substitutions 
"                    for compiledformat function.
"   Debugging_SaveO: Save opts.* values in Debugging_FuncF.
let s:_options={
            \   'DefaultFormat': {'default': 'html',
            \                     'checker': 'key formats',},
            \    'IgnoreCursor': {'default':  1,  'filter': 'bool'},
            \     'IgnoreFolds': {'default':  0,  'filter': 'bool'},
            \      'IgnoreList': {'default':  0,  'filter': 'bool'},
            \        'AllFolds': {'default':  0,  'filter': 'bool'},
            \     'IgnoreSigns': {'default':  0,  'filter': 'bool'},
            \      'IgnoreDiff': {'default':  0,  'filter': 'bool'},
            \     'IgnoreSpell': {'default':  0,  'filter': 'bool'},
            \      'IgnoreTags': {'default':  1, 'checker': 'range  0  2' },
            \    'ShowProgress': {'default':  0, 'checker': 'range  0  2' },
            \   'CollapsFiller': {'default':  0, 'checker': 'range  0 inf'},
            \        'NoLineNR': {'default': -1, 'checker': 'range -1  1' },
            \  'RelativeNumber': {'default': -1, 'checker': 'range -1  1' },
            \      'FoldColumn': {'default': -1, 'checker': 'range -1 inf'},
            \      'MaxDupTags': {'default':  5, 'checker': 'range  0 inf'},
            \ 'FormatConcealed': {'default':  1, 'checker': 'range  0  2' },
            \      'MinColumns': {'default': 40, 'checker': 'range  0 inf'},
            \   'FormatMatches': {'default': -1,
            \                     'checker': 'in [none search matches all]'},
            \     'StartTagReg': {'default':  0, 'checker': 'isreg'},
            \       'EndTagReg': {'default':  0, 'checker': 'isreg'},
            \       'ColorFile': {'default':  0,
            \                      'scopes': 'g',
            \                     'checker': 'either (is=(0) path)'},
            \'AddTagCmdEscapes': {'default': '[]*.~',
            \                     'checker': 'type string'},
            \   'UseStyleNames': {'default': 0, 'filter': 'bool'},
            \     'FormatLinks': {'default': 1, 'filter': 'bool'},
            \     'LinkRegexes': {'default': [
            \                       ['\vhttps?\:\/\/'.
            \                          '%([[:alnum:]\-]+\.)+\a{2,3}\/'.
            \                          '%(\S{-}%([.?!]?%(\ |$))@=|\S+>)',
            \                                                                        'submatch(0)'],
            \                       ['\v%(%(git|ssh)\:\/\/%(git|hg)\@)'.
            \                          '(github\.com|bitbucket\.org)[:/]'.
            \                          '([^/ ]+/[^/ ]+>)%(\.git)?',
            \                                         '''https://''.submatch(1).''/''.submatch(2)'],
            \                     ],
            \                     'checker': 'list (tuple ((isreg) (type string)))',},
            \
            \       'Debugging': {'default': 0, 'filter': 'bool'},
            \ 'Debugging_FuncF': {'default': 0,
            \                     'checker': 'either (is=(0) path)'},
            \ 'Debugging_MinFu': {'default': 0, 'filter': 'bool'},
            \ 'Debugging_Break': {'default': 0,
            \                     'checker': 'dict {?either ((in keylist)'.
            \                                               '(in [compiledformat '.
            \                                                    'compiledspec '.
            \                                                    'strescape]))'.
            \                                      'list (either ((range 1 inf) '.
            \                                                    '(isreg)))}'},
            \ 'Debugging_SaveO': {'default': 1, 'filter': 'bool'},
            \
            \'HTMLAnchorFileNameExpr': {'default': 'a:tag._tfname.".html"',
            \                           'checker': 'type string'},
            \'HTMLUseTagNameInAnchor': {'default': 0, 'filter': 'bool'},
            \'HTMLAddLinkAtTagLine':   {'default': 1, 'filter': 'bool'},
            \'HTMLTitleExpr':          {'default': 'expand(''%:p:~'')',
            \                           'checker': 'type string',},
            \'HTMLFontFamily':         {'default': '"Bitstream Vera Sans Mono",'.
            \                                      '"DejaVu Sans Mono",'.
            \                                      'Monaco,'.
            \                                      'monospace',
            \                           'checker': 'type string'},
            \'HTMLAdditionalCSS':      {'default': '', 'checker': 'type string'},
            \
            \'VOHelpPrefix':     {'default': 'http://vimpluginloader.sourceforge.net/doc/',
            \                     'checker': 'type string',},
            \'VOHelpSuffix':     {'default': '.html', 'checker': 'type string'},
            \'VOHelpAnchorExpr': {'default': 'tag.__ename=~#'':''?'.
            \                                   '(''line''.tag._linenr.''-0 (''.tag.name.'')''):'.
            \                                   '(tag.__ename)',
            \                     'checker': 'type string',},
        \}
function s:_options.LinkRegexes.merger(old, new, oname, plid, vname)
    unsilent echomsg string(a:vname)
    return extend(copy(a:old), a:new)
endfunction
"▶1 Выводимые сообщения
let s:_messages={
            \   'misskey': 'Required key is missing: %s',
            \   'synnsup': 'I wonder, why do you need this plugin: '.
            \              'this vim is compiled without syntax support',
            \   'misscol': 'File with colors list not found, '.
            \              'see :h format-o-ColorFile',
            \    'exists': 'Format already exists',
            \   'upcspec': 'Undefined %%. sequence: %%%s',
            \'nomagicchg': 'Changing magic state is not allowed',
            \   'nofloat': 'You must either use terminal vim or vim compiled '.
            \              'with +float feature',
            \'toolongpat': 'Pattern is too long. This may be caused by '.
            \              'too long StartTagReg or EndTagReg options or by '.
            \              'too long single tag.',
            \    'atnstr': 'The result of evaluating @<@{expr}@>@ is not a string',
            \   'nelines': 'Internal error: formatted buffer does not contain '.
            \              'enough lines. If you can reproduce this error please '.
            \              'post a bug report',
            \'noatdollar': '@$@ is not allowed inside @<@expr@>@ and %!cmd!%.',
        \}
"▶1 squote
function s:F.squote(str)
    return substitute(substitute(string(a:str), "\n", '''."\\n".''', 'g'),
                \                               '%',  '%%',          'g')
endfunction
"▶1 addsubs
function s:F.addsubs(var, ...)
    return a:var.join(map(copy(a:000), '((type(v:val)=='.type(0).')?'.
                \                           '("[".v:val."]"):'.
                \                      '((v:val=~#"\\W")?'.
                \                           '("[".s:F.squote(v:val)."]")'.
                \                      ':'.
                \                           '(".".v:val)))'), '')
endfunction
"▶1 getcolors
function s:F.getcolors(file)
    if !filereadable(a:file)
        call s:_f.throw('misscol')
    endif
    return map(filter(readfile(a:file, 'b'), 'v:val[:1] is# "- "'),'v:val[3:9]')
endfunction
"▶1 getcolorfile
function s:F.getcolorfile()
    let r=s:_f.getoption('ColorFile')
    if type(r)!=type('')
        if s:whatterm isnot# 'gui'
            let r=s:_r.os.path.join(s:_frawor.runtimepath, 'config',
                        \                     'formatvim',
                        \                     'colors-default-'.&t_Co.'.yaml')
        endif
        if s:whatterm is# 'gui' || !filereadable(r)
            let r=s:_r.os.path.join(s:_frawor.runtimepath, 'config',
                        \                     'formatvim',
                        \                     'colors-default.yaml')
        endif
    elseif stridx(r, '/')==-1 && stridx(r, s:_r.os.sep)==-1
        let r=s:_r.os.path.join(s:_frawor.runtimepath, 'config', 'formatvim', r.'.yaml')
    else
        return r
    endif
    return r
endfunction
"▶1 getexpr
"▶2 s:colors
if has('gui_running') || has('termguicolors') && &termguicolors
    let s:whatterm = 'gui'
    augroup FormatRedrawProgress
        autocmd VimResized * call s:F.redrawprogress()
    augroup END
    let s:_augroups+=['FormatRedrawProgress']
else
    let s:whatterm = 'cterm'
    let s:colors=s:F.getcolors(s:F.getcolorfile())
endif
"▶2 s:fmtexpressions
let s:fmtexpressions={
            \ 'f': '@fgcolor@',
            \ 'b': '@bgcolor@',
            \ 'S': '@S@',
            \'.S': '@.S@',
            \ 'N': '@-@',
            \ 'C': '@.@',
            \ ':': '@:@',
            \ '%': "'%'",
            \ '@': "'@'",
            \ '~': '@_difffillchar@',
            \'.~': '@_foldfillchar@',
            \ '-': '@$@repeat(@_difffillchar@,@_columns@-@=@)',
            \'.-': '@$@repeat(@_foldfillchar@,@_columns@-@=@)',
            \ '|': '@_vertseparator@',
        \}
"▶2 s:fcompexpressions
let s:spacesexpr="''repeat(''.s:F.squote(@_leadingspace@).'',''.@_linenumlen@.''-len(@@@))''"
let s:fcompexpressions={
            \ '#': "'@<@(@_dosomenr@?".
            \               "((@_donr@ && @_dornr@)?".
            \                   "(''((@-@==''.@_cline@.'')?".
            \                       "(@@@.''.".s:spacesexpr.".''):".
            \                       "(''.".s:spacesexpr.".''.@@@))''):".
            \                   "(".s:spacesexpr.".''.@@@'')):".
            \               "(''\"\"''))@>@'",
            \ '+': "'@$@repeat('.a:opts.qleadingspace.','.a:opts.columns.'-@=@)'",
            \ '_': "'@<@".
            \           "(@_dosomenr@?".
            \               "(''repeat(''.s:F.squote(@_leadingspace@).'',''.@_linenumlen@.'')''):".
            \               "(''\"\"'')".
            \           ")".
            \       "@>@'",
            \ ' ': 'a:opts.qleadingspace',
            \ '^': "'@<@".
            \           "(@_dosomenr@?".
            \               "(s:F.squote(@_leadingspace@)):".
            \               "(''\"\"'')".
            \           ")".
            \       "@>@'",
            \ 's': "get(a:cf.format, 'strescape', '@@@')",
        \}
unlet s:spacesexpr
"▲2
function s:F.getexpr(cf, str, opts)
    let fkey=matchstr(a:str, '\v\.?.')
    "▶2 Простые выражения (%f, %b, %*S, %N, %C, %:, %%, %@, %*~, %*-, %|)
    if has_key(s:fmtexpressions, fkey)
        return [len(fkey), s:fmtexpressions[fkey]]
    "▶2 Сложные выражения (%#, %_, %, %^, %*s)
    elseif has_key(s:fcompexpressions, fkey)
        return [len(fkey), eval(s:fcompexpressions[fkey])]
    "▶2 %={expr}=%
    elseif fkey is# '='
        let str=matchstr(a:str, '\v^([^\\%]|\\.){-}(\=\%)@=', 1)
        let shift=3+len(str)
        let str=substitute(str, '\m\\\([\\%]\)', '\1', 'g')
        return [shift, str]
    "▶2 %'{expr}'%
    elseif fkey is# "'"
        let str=matchstr(a:str, '\v^.{-}(\''\%)@=', 1)
        let shift=3+len(str)
        return [shift, str]
    "▶2 %>{expr}
    elseif fkey is# '>'
        return [len(a:str), a:str[1:]]
    "▶2 Остальные %*
    else
        call s:_f.throw('upcspec', a:str)
    endif
endfunction
"▶1 increq
function s:F.inqreq(req, key)
    if a:req isnot 0
        let a:req[a:key]=get(a:req, a:key, 0)+1
    endif
endfunction
"▶1 getats
let s:atargs={
            \'@': ['str', "''", 'begin', 'end', 'sbsdstart', 'sbsdsep',
            \      'sbsdend'],
            \'-': ['line', 0, 'begin', 'style'],
            \'.': ['char', 0, 'linestart', 'linenr', 'fold',
            \      'collapsedfiller', 'begin', 'end', 'sbsdstart', 'style'],
        \}
let s:atexpratargs={
            \'opts': 'a:opts',
        \}
function s:F.getats(str, opts, req, key, atargs, exprempty)
    let r=''
    let str=a:str
    while !empty(str)
        let atidx=stridx(str, '@')
        if atidx==-1
            let r.=str
            break
        elseif atidx!=0
            let r.=str[:(atidx-1)]
        endif
        let str=str[(atidx+1):]
        let atv=str[0]
        if atv is# '<'
            let idx=stridx(str, '@>@')
            if idx!=-1
                let expr=str[2:(idx-1)]
                try
                    let expr=s:F.getats(expr, a:opts, 0, 0, s:atexpratargs, -1)
                    let result=eval(expr)
                endtry
                if type(result)!=type('')
                    call s:_f.throw('atnstr')
                endif
                let arg=''
                let r.=s:F.getats(result, a:opts, a:req, a:key, a:atargs, -1)
                let str=str[(idx+3):]
            endif
        elseif atv is# '_' && str[1] is# '_'
            let arg=matchstr(str, '\v^_\w+\@@=', 1)
            if empty(arg)
                unlet arg
            else
                let str=str[len(arg)+2:]
                if a:req isnot 0
                    call s:F.inqreq(a:req, 'a:opts')
                endif
                let arg=(a:atargs.opts).'.'.arg
            endif
        elseif atv is# '_'
            let arg=matchstr(str, '\v^\l+\@@=', 1)
            if empty(arg)
                unlet arg
            else
                let str=str[len(arg)+2:]
                if type(a:opts[arg])==type('') || type(a:opts[arg])==type(0)
                    let arg=string(a:opts[arg])
                else
                    if a:req isnot 0
                        call s:F.inqreq(a:req, 'a:opts')
                    endif
                    let arg=(a:atargs.opts).'.'.arg
                endif
            endif
        elseif a:key is 0
            " Do nothing
        elseif has_key(s:atargs, atv) && str[1] is# '@'
            if index(s:atargs[atv], a:key, 2)==-1
                let arg=a:atargs[s:atargs[atv][0]]
            else
                let arg=s:atargs[atv][1]
            endif
            let str=str[2:]
        elseif atv=~#'\v^\l'
            let arg=matchstr(str, '\v^\l+%(\@)@=')
            if empty(arg)
                unlet arg
            else
                let str=str[len(arg)+1:]
                call s:F.inqreq(a:req, 'a:spec')
                let arg=a:atargs.spec.'[0].'.arg
            endif
        elseif atv is# '!'
            call s:F.inqreq(a:req, 'a:cf')
            if str[1] is# '!'
                call s:F.inqreq(a:req, 'sbsdstate')
                let arg=matchstr(str, '\v^\l+%(\@)@=', 2)
                let str=str[len(arg)+3:]
                let arg=(a:atargs.cf).'.sbsdstate.'.arg
            else
                call s:F.inqreq(a:req, 'state')
                let arg=matchstr(str, '\v^\l+%(\@)@=', 1)
                let str=str[len(arg)+2:]
                let arg=(a:atargs.cf).'.state.'.arg
            endif
        elseif atv is# 'S' && str[1] is# '@'
            call s:F.inqreq(a:req, 'a:spec')
            let arg=a:atargs.spec.'[0].'.(a:opts.usestylenames?('name'):('styleid'))
            let str=str[2:]
        elseif atv is# '.' && str[1:2] is# 'S@'
            call s:F.inqreq(a:req, 'a:cspec')
            let arg=a:atargs.cspec.'[0].'.(a:opts.usestylenames?('name'):('styleid'))
            let str=str[3:]
        elseif atv is# '$' && str[1] is# '@'
            if a:exprempty is 0
                call s:F.inqreq(a:req, '=')
                let r=substitute(r, '\.$', '', '')
                let r.="\nlet str\.="
            elseif a:exprempty is -1
                call s:_f.throw('noatdollar')
            endif
            let str=str[2:]
            let arg=''
        elseif atv is# '=' && str[1] is# '@'
            call s:F.inqreq(a:req, 'a:cur')
            call s:F.inqreq(a:req, 'a:opts')
            call s:F.inqreq(a:req, 'a:opts.strlen')
            if a:req isnot 0 && has_key(a:req, '=')
                let r.=a:atargs.opts.'.strlen('.a:atargs.cur.'.str)'
            else
                let r.=a:atargs.opts.'.strlen('.a:atargs.cur.')'
            endif
            let arg=''
            let str=str[2:]
        elseif atv is# '~'
            let arg=a:atargs.spec.'[0]'
            call s:F.inqreq(a:req, 'a:spec')
            let str=str[2:]
        elseif atv is# '^'
            let arg=a:atargs.cspec.'[0]'
            call s:F.inqreq(a:req, 'a:cspec')
            let str=str[2:]
        elseif atv is# ':'
            if a:key is# 'begin' || a:key is# 'end'
                let arg=a:atargs.style
            else
                let arg=a:atargs.spec.'[1]'
                call s:F.inqreq(a:req, 'a:spec')
            endif
            let str=str[2:]
        elseif atv is# '&' && str[1] is# '@'
            let arg=a:atargs.cspec.'[1]'
            call s:F.inqreq(a:req, 'a:cspec')
            let str=str[2:]
        endif
        if exists('arg')
            if arg[0] is# 'a'
                call s:F.inqreq(a:req, arg)
            endif
            let r.=arg
            unlet arg
        else
            let r.='@'
        endif
    endwhile
    return r
endfunction
"▶1 addexprchunk
function s:F.addexprchunk(prevchar, expr, exprchunk)
    if empty(a:exprchunk) || a:exprchunk is# '""' || a:exprchunk is# "''"
        return [a:prevchar, a:expr]
    elseif (a:prevchar is# "'" || a:prevchar is# '"') && a:exprchunk[0] is# a:prevchar
        return [a:exprchunk[-1:], a:expr[:-3].a:exprchunk[1:].'.']
    else
        return [a:exprchunk[-1:], a:expr.a:exprchunk.'.']
    endif
endfunction
"▶1 procpc
function s:F.procpc(cf, str, opts, key, req, atargs, escpc)
    let str=a:str
    let prevchar=0
    let expr=''
    while !empty(str)
        let pidx=stridx(str, '%')
        if pidx==-1
            let [prevchar, expr]=s:F.addexprchunk(prevchar, expr, s:F.squote(str))
            break
        elseif pidx!=0
            let [prevchar, expr]=s:F.addexprchunk(prevchar, expr, s:F.squote(str[:(pidx-1)]))
        endif
        let str=str[(pidx+1):]
        let [shift, chunk]=s:F.getexpr(a:cf, str, a:opts)
        let str=str[(shift):]
        if a:escpc
            let chunk=substitute(chunk, '%', '%%', 'g')
        endif
        let exprchunk=s:F.getats(chunk, a:opts, a:req, a:key, a:atargs, empty(expr))
        let [prevchar, expr]=s:F.addexprchunk(prevchar, expr, exprchunk)
    endwhile
    let expr=substitute(expr, '\v\.%(\n|$)@=', '', 'g')
    return expr
endfunction
"▶1 ftmcompile
"▶2 Create list of arguments
let s:keyargslist=['str', 'spec', 'line', 'char', 'cur', 'opts', 'style', 'cf']
let s:keynoargs={'str': s:atargs['@'][2:],
            \   'line': s:atargs['-'][2:],
            \   'char': s:atargs['.'][2:],
            \  'style': filter(copy(s:keylist), 'v:val isnot# "begin" && '.
            \                                   'v:val isnot# "end"')}
let s:keyargs={}
for s:key in s:keylist
    let s:keyargs[s:key]=[]
    for s:arg in s:keyargslist
        if has_key(s:keynoargs, s:arg) && index(s:keynoargs[s:arg], s:key)!=-1
            continue
        endif
        let s:keyargs[s:key]+=[s:arg]
    endfor
endfor
let s:keyargs.strescape=['str', 'opts']
let s:defatargs={}
call map(s:keyargslist, 'extend(s:defatargs, {v:val : "a:".v:val})')
unlet s:key s:arg s:keyargslist s:keynoargs
"▲2
function s:F.fmtcompileone(cf, str, opts, key)
    let r={'req': {}, 'isexpr': 1, 'str': a:str}
    let str=a:str
    "▶2 %!
    if str[0:1] is# '%!'
        let cmd=matchstr(str, '\v.{-}%(\!\%)@=', 2)
        let str=str[(4+len(cmd)):]
        let cmd=s:F.getats(cmd, a:opts, r.req, a:key, s:defatargs, -1)
        let r.isexpr=0
    endif
    "▲2
    let args=join(s:keyargs[a:key], ', ')
    let funstr='function r.f('.args.")\n"
    if exists('cmd')
        let funstr.=cmd."\n"
    endif
    let expr=s:F.procpc(a:cf, str, a:opts, a:key, r.req, s:defatargs, 0)
    let r.isconst=empty(filter(keys(r.req), 'v:val[0:5] isnot# "a:opts"'))
    if has_key(r.req, '=')
        let funstr.='let str='.expr."\nreturn str"
        let r.isexpr=0
    else
        let funstr.='return '.expr
    endif
    let funstr.="\nendfunction"
    execute funstr
    call a:cf.savefunc(a:key, funstr, r.f)
    return r
endfunction
"▶1 fmtprepare
function s:F.fmtprepare(type, options, slnr, elnr, sbsd)
    let format=s:formats[a:type]
    "▶2 s:F.getcolor
    if !has_key(s:F, 'getcolor')
        unlockvar 1 s:F
        if s:whatterm is# 'gui'
            function s:F.getcolor(color)
                return a:color
            endfunction
        else
            function s:F.getcolor(color)
                if a:color!~#'\v^\d+$'
                    return ''
                endif
                return get(s:colors, a:color, '')
            endfunction
        endif
        lockvar s:F
    endif
    "▶2 opts
    let opts={}
    let opts.strlen=get(format, 'strlen', s:_r.strdisplaywidth)
    call map(copy(get(format, 'addopts', {})), 'extend(opts, {"_".v:key : deepcopy(v:val)})')
    let id=hlID('Normal')
    let opts.fgcolor=s:F.getcolor(synIDattr(id, 'fg#', s:whatterm))
    let opts.bgcolor=s:F.getcolor(synIDattr(id, 'bg#', s:whatterm))
    if opts.fgcolor==''
        let opts.fgcolor=((&background is# 'dark')?('#ffffff'):('#000000'))
    endif
    if opts.bgcolor==''
        let opts.bgcolor=((&background is# 'dark')?('#000000'):('#ffffff'))
    endif
    call s:F.initopts(opts, a:options, format, a:sbsd)
    lockvar! opts
    "▲2
    let cf=s:cf.new(format, opts)
    return cf
endfunction
"▶1 s:formats
"▶2 HTML
let s:htmlreplaces={
            \'&': '&',
            \'"': '"',
            \'<': '<',
            \'>': '>',
        \}
let s:escapehtml='substitute(@@@,''['.join(keys(s:htmlreplaces),'').']'','.
            \              '''\=@__replaces@[submatch(0)]'',''g'')'
let s:htmlstylestr='((@inverse@)?'.
            \             '("color: ".'.
            \              '((@bgcolor@!=#"")?'.
            \                '(@bgcolor@):'.
            \                '(@_bgcolor@))."; background-color: ".'.
            \              '((@fgcolor@!=#"")?'.
            \                '(@fgcolor@):'.
            \                '(@_fgcolor@))."; "):'.
            \             '(((@fgcolor@!=#"")?'.
            \               '("color: ".@fgcolor@."; "):'.
            \               '("color: ".@_fgcolor@."; ")).'.
            \              '((@bgcolor@!=#"")?'.
            \               '("background-color: ".@bgcolor@."; "):'.
            \               '("background-color: ".@_bgcolor@."; ")))).'.
            \           '((@bold@)?'.
            \             '("font-weight: bold; "):'.
            \             '("")).'.
            \           '((@italic@)?'.
            \             '("font-style: italic; "):'.
            \             '("")).'.
            \           '((@underline@)?'.
            \             '("text-decoration: underline; "):'.
            \             '(""))'
let s:htmlinput=''
let s:formats.html={
            \'style':        '%>((@S@!=#"")?'.
            \                   '(".s".@S@." {".'.
            \                     s:htmlstylestr.
            \                   '."} "):'.
            \                   '(""))',
            \'begin':        "".
            \                ''.
            \                ''.
            \                ''.
            \                '<style type="text/css"> '.
            \                'body { font-family: %''@___fontfamily@''%; '.
            \                        'white-space: nowrap; '.
            \                        'margin: 0; padding: 0; border: 0;  '.
            \                        'color: %''@_fgcolor@''%; '.
            \                        'background-color: %''@_bgcolor@''% } '.
            \                'p { margin: 0; padding: 0; border: 0; '.
            \                    'color: %''@_fgcolor@''%; '.
            \                    'background-color: %''@_bgcolor@''%; '.
            \                    'text-indent: 0; white-space: pre; } '.
            \                'div { margin: 0; padding: 0; border: 0; '.
            \                      'color: %''@_fgcolor@''%; '.
            \                      'background-color: %''@_bgcolor@''%; '.
            \                      'white-space: pre; } '.
            \                '.open-fold   > .fulltext { display: block; }'.
            \                '.closed-fold > .fulltext { display: none;  }'.
            \                '.open-fold   > .toggle-open   {display: none; }'.
            \                '.open-fold   > .toggle-closed {display: block;}'.
            \                '.closed-fold > .toggle-open   {display: block;}'.
            \                '.closed-fold > .toggle-closed {display: none; }'.
            \                '.closed-fold:hover > .fulltext{display: block;}'.
            \                '.closed-fold:hover > .toggle-filler '.
            \                                              '{display: none;}'.
            \                '.Sign { width: 2.5ex; } '.
            \                '.SignImage { background-size: contain; '.
            \                             'background-repeat: no-repeat; '.
            \                             'background-position: center; }'.
            \                'span.Sign { display: inline-block; } '.
            \                '.Sign > image { max-width: 100%%; }'.
            \                '.Present { display: none; }'.
            \                '.Line { position: relative; zoom: 1; '.
            \                        'white-space: pre; }'.
            \                'input { display: inline; '.
            \                        'padding: 0; margin: 0; '.
            \                        'border: 0; background: none; '.
            \                        'font-family: inherit; '.
            \                        'font-size: 100%%; '.
            \                        'pointer-events: none; '.
            \                        'height: 100%%; }'.
            \                '.Line { min-height: 1em; } '.
            \                '.Line:hover .Shown   { display: none; }'.
            \                '.Line:hover .Present { display: inline; }'.
            \                '            .TagLink { margin-left: 1em; }'.
            \                '            .TagLink { display: none; }'.
            \                '.Line:hover .TagLink { display: inline; }'.
            \                '.s%''hlID("Conceal")''% { font-weight: normal; '.
            \                                          'font-style: normal; '.
            \                                          'text-decoration: none;'.
            \                                        '}'.
            \                '%''((@_allfolds@)?'.
            \                    '(".Fold {display:none;}'.
            \                      'a {white-space:pre;'.
            \                         'font-family:inherit;'.
            \                         'color:inherit;}'.
            \                      '.fulltext{white-space:nowrap;}"):'.
            \                    '("")).'.
            \                   '((@_sbsd@)?'.
            \                    '("table, tr, td { margin: 0; '.
            \                                      'padding: 0; '.
            \                                      'border: 0; } "):'.
            \                    '(""))''%'.
            \                '%:%''@___additionalcss@''%</style>'.
            \                '<title>%'''.substitute(s:escapehtml, '\V@@@',
            \                                        'eval(@___titleexpr@)', '').
            \                '''%</title>'.
            \                '%''((@_allfolds@)?('''.
            \                   '<script type="text/javascript">'.
            \                       'function toggleFold(event, objID) {'.
            \                           'var fold;'.
            \                           'fold=document.getElementById(objID);'.
            \                           'if(fold.className=="closed-fold")'.
            \                               '{fold.className="open-fold";'.
            \                                'if (event){event._doNotClose_=true;}}'.
            \                           'else {if (!event._doNotClose_){'.
            \                                   'fold.className="closed-fold";'.
            \                                   'if (event){event.stopPropagation();}}}'.
            \                  '}</script>'''.
            \                '):(""))''%'.
            \                ''.
            \                '%''((@_sbsd@)?(""):(""))''%',
            \'end':          '%''((@_sbsd@)?("
"):(""))''%'. \ '', \'linestart': '
'. \ '%''(@@@==0?join(map(copy('. \ 'get(@_curtags@,@-@,[])),'. \ '"''''"),'. \ '""):"")''%', \'linenr': printf(s:htmlinput, 'LineNR', \ '%''(@_linenumlen@+1)''%', \ '%#% ', ''), \'line': '%>empty(@S@)'. \ '?'.s:escapehtml. \ ':''''.'. \ s:escapehtml. \ '.''''', \'concealedstart': ''. \ '%s'. \ '', \'concealedend': '', \'lineend': '%''repeat("",'. \ 'len(get(@_curtags@,@-@,[]))*(@@@==0)).'. \ '('.'@___addlinkattagline@'. \ '&&!empty(get(@_curtags@,@-@))'. \ '&&@@@==0'. \ '?"'. \ ''. \ 'LINK'. \ ''. \ '"'. \ ':"")''%
', \'tagstart': '%''get(get(@_tags@,@@@,[]),0,'. \ '[{"__anchor":""}])[-1].__anchor''%', \'linkstart': '', \'linkend': '', \'tagend': '', \'foldcolumn': printf(s:htmlinput, 'FoldColumn', \ '%''@_foldcolumn@''%', \ '%''substitute(@@@,''\\V>'',''\\>'','. \ '''g'')''%', ''), \'fold': '%s'. \ '% %.-', \'difffiller': '%-', \'collapsedfiller': '%~ Deleted lines: %s %-', \'foldstart': '
'. \ '
'. \ '%s
'. \ '
', \'foldend': '
', \'strescape': s:escapehtml, \'sbsdstart': ''. \ '', \'sbsdsep': '%|'. \ '', \'sbsdend': '', \'sign': printf(s:htmlinput, \ 'Sign %''@__signstylelist@[@.@]''%', 2, '%s', ''), \'addopts': {'stylelist': ['Line', 'Fold', 'DiffFiller', \ 'CollapsedFiller', 'TrailLine'], \ 'signstylelist': ['SignAbsent', 'SignText', 'SignIcon'], \ 'replaces': s:htmlreplaces,}, \} unlet s:htmlreplaces "▶3 strlen function s:formats.html.strlen(str) let str=a:str let str=substitute(str, '', '\1', 'g') let str=substitute(str, '\m<.\{-}>', '', 'g') let str=substitute(str, '\m&.\{-};', '.', 'g') return s:_r.strdisplaywidth(str) endfunction "▶3 addopts.anchorescape " wiki-style .XX escapes function s:formats.html.addopts.anchorescape(char) let nr=char2nr(a:char) let nrchar=nr2char(nr) let r='' while nr let r .= printf('.%02x', nr%0x100) let nr = nr/0x100 endwhile " Caught character with diacritics if nrchar isnot# a:char && len(nrchar) ID and NAME tokens must begin with a letter ([A-Za-z]) and may be followed by any number of " > letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods ("."). let a:tag.__ename=substitute(a:tag.name, '[^a-zA-Z0-9_\-]', \ '\=a:opts._anchorescape(submatch(0))', 'g') endfunction "▶3 tagproc function s:formats.html.tagproc(opts, tag) call a:opts._addename(a:opts, a:tag) if a:opts.__usetagname let a:tag.__href='#'.a:tag.__ename else let a:tag.__href='#line'.a:tag._linenr.'-0' endif if !a:tag._incurf let fname=a:opts.strescape(eval(a:opts.__anchorfnameexpr)) let a:tag.__href=fname.a:tag.__href endif let a:tag.__anchor='' return a:tag endfunction "▶3 addoptsfun function s:formats.html.addoptsfun() return { \ 'usetagname': s:_f.getoption('HTMLUseTagNameInAnchor'), \ 'anchorfnameexpr': s:_f.getoption('HTMLAnchorFileNameExpr'), \'addlinkattagline': s:_f.getoption('HTMLAddLinkAtTagLine'), \ 'titleexpr': s:_f.getoption('HTMLTitleExpr'), \ 'fontfamily': s:_f.getoption('HTMLFontFamily'), \ 'additionalcss': s:_f.getoption('HTMLAdditionalCSS'), \} endfunction "▲3 if s:whatterm is# 'gui' function s:F.readfile(fname) if !filereadable(a:fname) return '' endif let escapedfile=shellescape(fnamemodify(a:fname, ':p')) if executable('file')==1 let mime=system('file --mime-type --brief -- '.escapedfile)[:-2] else let mime='image/'.tolower(fnamemodify(a:fname, ':e')) if mime!~#'\v^image\/[a-z\-]+$' let mime='image' endif endif if executable('base64')==1 && stridx(a:fname, "\n")==-1 let b64=system('base64 -w0 -- '.escapedfile) else let b64=s:_r.base64.encodelines(readfile(a:fname, 'b')) endif return 'data:'.mime.';base64,'.b64 endfunction let s:formats.html.addopts.readfile=s:F.readfile let s:formats.html.sign="%=((@.@==2)?". \"(''=%".printf(s:htmlinput, 'Sign SignImage', 2, ' ', \ 'style="background-image:url(%'''. \ '@__readfile@(@@@)''%)"')."%=''):". \"(''=%".s:formats.html.sign."%=''))=%" endif unlet s:htmlinput let s:styleattr='%''((!empty(@styleid@))?'. \ '(" style=\"".'.s:htmlstylestr.'."\""):'. \ '(""))''%' let s:formats['html-vimwiki']={ \'style': s:styleattr, \'begin': '
', \'end': '
', \'linestart': '', \'linenr': '%#% ', \'foldcolumn': '%s', \'lineend': '', \'fold': '%s% %.-', \'difffiller': '%-', \'collapsedfiller': '%~ Deleted lines: %s %-', \'strlen': s:formats.html.strlen, \'strescape': substitute(s:escapehtml, '&', '& ', ''), \'line': '%s', \'addopts': {'replaces': extend({' ': ' '}, \ s:formats.html.addopts.replaces)}, \} unlet s:styleattr unlet s:escapehtml s:htmlstylestr "▶2 BBcode (unixforum) let s:bbufostylestart= \'((@inverse@)?'. \ '((@bgcolor@!=#"")?("[color=".@bgcolor@):("")):'. \ '("[color=".((@fgcolor@!=#"")?(@fgcolor@):(@_fgcolor@))))."]".'. \'((@bold@)?("[b]"):("")).((@italic@)?("[i]"):(""))' let s:bbufostyleend='((@italic@)?("[/i]"):("")).'. \'((@bold@)?("[/b]"):("")).'. \'(((@inverse@ && (@bgcolor@!=#"")) || (!@inverse@))?'. \ '("[/color]"):(""))' let s:bbufoescape='substitute('. \ 'substitute('. \ 'substitute(@@@, "\\V&", "\\&", "g"), '. \ '"[", "\\[", "g"), '. \ '"]", "\\]", "g")' let s:formats["bbcode-unixforum"]={ \'begin': '%>((&background is# "dark")?'. \ '("[sh=".substitute(expand("%:p:~%"), "\\V[]",'. \ '''\="&#".char2nr(submatch(0)).";"'', "g")." '. \ '(Created by format.vim)]"):'. \ '("[codebox]"))', \'end': '%>((&background is# "dark")?'. \ '("[/sh]"):'. \ '("[/codebox]"))', \'linenr': '%>substitute(@:@, "%s", ''\='. \ 'repeat(@_leadingspace@, '. \ "@_linenumlen@-len(@-@)).". \ '@-@.@_leadingspace@'', "")', \'line': '%>substitute(@:@, "%s", ''\='.s:bbufoescape.''','. \ '"")', \'strescape': s:bbufoescape, \'style': '%>' . s:bbufostylestart. \ '."%s".'.s:bbufostyleend, \'sbsdsep': '%+%|', \'difffiller': '%-', \} unlet s:bbufostylestart s:bbufostyleend s:bbufoescape "▶3 stuf.bbstrlen function s:formats['bbcode-unixforum'].strlen(str) let str=a:str let str=substitute(str, '\m\[.\{-}\]', '', 'g') let str=substitute(str, '\m&[^;]\+;\|.', '.', 'g') return len(str) endfunction "▶2 LaTeX (xcolor) let s:texescape= \'substitute('. \ 'substitute(@@@, ''\v[\\\[\]{}&$_\^%#]'', '. \ '''\=''''\char''''.char2nr(submatch(0))."{}"'', '. \ '"g"),'. \'" ", ''\\enskip{}'', "g")' let s:texstylestart= \'((@inverse@)?'. \ '(''\colorbox[HTML]{''.'. \ '((@fgcolor@!=#"")?'. \ '(toupper(@fgcolor@[1:])):'. \ '(toupper(@_fgcolor@[1:])))."}{".'. \ '''\textcolor[HTML]{''.'. \ '((@bgcolor@!=#"")?'. \ '(toupper(@bgcolor@[1:])):'. \ '(toupper(@_bgcolor@[1:])))."}{"):'. \ '(((@bgcolor@!=#"")?'. \ '(''\colorbox[HTML]{''.toupper(@bgcolor@[1:])."}{"):'. \ '("")).'. \ '''\textcolor[HTML]{''.'. \ '((@fgcolor@!=#"")?'. \ '(toupper(@fgcolor@[1:])):'. \ '(toupper(@_fgcolor@[1:])))."}{"))' let s:texstyleend= \'repeat("}", '. \ '((@inverse@)?'. \ '(2):'. \ '((@bgcolor@!=#"")+1)))' let s:formats['latex-xcolor']={ \'begin': '\documentclass[a4paper,12pt]{article}'. \ '\usepackage[utf8]{inputenc}'. \ '\usepackage[HTML]{xcolor}'. \ '\pagecolor[HTML]{%''toupper(@_bgcolor@[1:])''%}'. \ '\color[HTML]{%''toupper(@_fgcolor@[1:])''%}'. \ '\begin{document}{\ttfamily\noindent', \'line': '%>'.s:texstylestart.".". \ s:texescape.".". \ s:texstyleend, \'lineend': '\\', \'end': '}\end{document}', \'strescape': s:texescape, \} unlet s:texstylestart s:texstyleend s:texescape "▶2 CSI let s:c={} if s:whatterm is# 'gui' function s:c.rgbtolist(color) return map(split(a:color[1:], '\v(..)@<='), 'eval("0x".v:val)') endfunction function s:c.distance(point1, point2) " For all x>0, y>0 sqrt(x)>sqrt(y) <=> x>y, thus no need in using " floating-point arithmetics. 255*255*3 which is the maximum value is " far beyond 32-bit border (needs 18 bits) return eval(join(map(map(range(len(a:point1)), \ 'a:point1[v:val]-a:point2[v:val]'), \ 'v:val*v:val'), \ '+')) endfunction function s:c.rgbtoterm(color) if !exists('s:colors') let s:colors=s:F.getcolors(s:F.getcolorfile()) lockvar! s:colors endif if !exists('s:colpoints') let s:colpoints=map(copy(s:colors), 'self.rgbtolist(v:val)') lockvar! s:colpoints let s:colmap={} elseif has_key(s:colmap, a:color) return s:colmap[a:color] endif let curcpoint=self.rgbtolist(a:color) let distances=map(copy(s:colpoints), 'self.distance(curcpoint, v:val)') let mindistance=min(distances) let r=index(distances, mindistance) let s:colmap[a:color]=r return r endfunction else function s:c.rgbtoterm(color) return index(s:colors, a:color) endfunction endif function s:c.tocsi(color, isbg) let colcode=self.rgbtoterm(a:color) if colcode==0 return "\e[".(10*a:isbg+39)."m" elseif colcode==16 return "\e[".(10*a:isbg+30)."m" elseif colcode<8 return "\e[".(10*a:isbg+30+colcode)."m" elseif colcode<16 return "\e[".(10*a:isbg+90+colcode-8)."m" else return "\e[".(10*a:isbg+38).";5;".colcode."m" endif endfunction function s:c.strlen(str) return s:_r.strdisplaywidth(substitute(a:str, "\e\\[[^m]*m", '', 'g')) endfunction let s:formats.csi={ \'style': '%>(@bold@ ?"\e[1m":"").'. \ '(@inverse@ ?"\e[7m":"").'. \ '(@underline@?"\e[4m":"").'. \ '(@italic@ ?"" :"").'. \ '(empty(@fgcolor@)?@__funcs@.tocsi(@_fgcolor@, 0):'. \ '@__funcs@.tocsi(@fgcolor@, 0)).'. \ '(empty(@bgcolor@)?@__funcs@.tocsi(@_bgcolor@, 1):'. \ '@__funcs@.tocsi(@bgcolor@, 1))', \'line': "%:%s\e[0m", \'lineend': '%:', \'strlen': remove(s:c, 'strlen'), \'addopts': {'funcs': s:c}, \} unlet s:c "▶2 vimorg-tagged let s:formats['vimorg-tagged']={ \'line': '%s', \'end': '%>@!foundtagsstr@', \'tagend': '%>@!recordtag@(@_tags@,@@@)', \'state': {}, \'addopts': {'anchorescape': s:formats.html.addopts.anchorescape}, \'tagproc': s:formats.html.addopts.addename, \'haslf': 1, \} function s:formats['vimorg-tagged'].state.init(opts) let self.foundtags={} let self.foundtagsstr='' let self.lastfound=1 let self.helpprefix=s:_f.getoption('VOHelpPrefix') let self.helpsuffix=s:_f.getoption('VOHelpSuffix') let self.anchorexpr=s:_f.getoption('VOHelpAnchorExpr') endfunction function s:formats['vimorg-tagged'].state.recordtag(tags, tag) if !has_key(a:tags, a:tag) || empty(a:tags[a:tag]) return '' endif if has_key(self.foundtags, a:tag) return ' ['.self.foundtags[a:tag].']' endif let tagnr=self.lastfound let self.lastfound+=1 let self.foundtags[a:tag]=tagnr let tag=a:tags[a:tag][0][-1] let tname=a:tag let helplink='['.tagnr.'] '. self.helpprefix . fnamemodify(tag._tfname, ':t') . self.helpsuffix let helplink.='#'.eval(self.anchorexpr) let self.foundtagsstr.="\n".helplink return ' ['.tagnr.']' endfunction "▶2 tokens let s:formats.tokens={ \'begin': "%>string(['b', @~@, expand('%'), bufnr('%')])", \'sbsdstart': "['ss', %'string(@_vertseparator@)'%, ", \'foldstart': "['fs', %:, %s, %C]", \'foldend': "['fe', %:, %s, %C]", \'linestart': "['%'@__linetypes@[@@@]'%', %:, ", \'foldcolumn': "['fc', %s, %:], ", \'sign': "['sc', %s, %:, %C], ", \'linenr': "['ln', %s, %:], ", \'tagstart': "['ts', %s ], ", \'line': "['l' , %s, %:], ", \'concealedstart': "['cs', %s, %:], ", \'concealedend': "['ce', %s, %:], ", \'tagend': "['te', %s ], ", \'fold': "['f' , %s, %:], ", \'difffiller': "['df', '', %:], ", \'collapsedfiller': "['cf', %s, %:], ", \'style': '%>string(@~@)', \'lineend': "]", \'sbsdsep': ", ", \'sbsdend': "]", \'end': "%>string(['e', @~@, @_tags@])", \'strescape': "string(@@@)", \'addopts': {'linetypes': ['lr', 'lf', 'ld', 'lc']}, \} "▶1 ishlcleared let s:cleared_dict={ \ 'fgcolor': '', \ 'bgcolor': '', \ 'bold': '', \ 'italic': '', \ 'underline': '', \ 'inverse': '', \} function s:F.ishlcleared(specdict) return empty(filter(copy(s:cleared_dict), 'v:val isnot# a:specdict[v:key]')) endfunction "▶1 getspecdict function s:F.getspecdict(hlname, ...) if type(a:hlname)==type([]) let specdicts=map(copy(a:hlname), 's:F.getspecdict(v:val)') let r=specdicts[0] call map(specdicts[1:], 'extend(l:, {"r": call(s:F.mergespecdicts, [r, v:val]+a:000, {})})') if !empty(filter(copy(a:hlname), 'v:val[0] is# "!"')) let spellidx=filter(map(copy(a:hlname), '[v:key, v:val]'), 'v:val[1][0] is# "!"')[0][0] if spellidx > 0 && spellidx < len(a:hlname)-1 for attr in ['bgcolor', 'fgcolor'] if !empty(specdicts[spellidx-1][attr]) && empty(specdicts[spellidx+1][attr]) && \!s:F.ishlcleared(specdicts[spellidx+1]) let r[attr]=specdicts[spellidx-1][attr] endif endfor endif endif return r endif if a:hlname[0] is# '!' let hlname=a:hlname[1:] else let hlname=a:hlname endif let id=synIDtrans(hlID(hlname)) return { \ 'name': hlname, \ 'styleid': id, \ 'fgcolor': s:F.getcolor( \ synIDattr(id, 'fg#', s:whatterm)), \ 'bgcolor': s:F.getcolor( \ synIDattr(id, 'bg#', s:whatterm)), \ 'bold': synIDattr(id, 'bold'), \ 'italic': synIDattr(id, 'italic'), \ 'underline': synIDattr(id, 'underline'), \ 'inverse': synIDattr(id, 'inverse'), \} endfunction "▶1 mergespecdicts function s:F.mergespecdicts(oldspecdict, newspecdict, ...) return { \'name': a:oldspecdict.name. '_'.a:newspecdict.name, \'styleid': a:oldspecdict.styleid.'_'.a:newspecdict.styleid, \'fgcolor': ((empty(a:newspecdict.fgcolor) && !a:0) \ ?(a:oldspecdict.fgcolor) \ :(a:newspecdict.fgcolor)), \'bgcolor': ((empty(a:newspecdict.bgcolor)) \ ?(a:oldspecdict.bgcolor) \ :(a:newspecdict.bgcolor)), \'bold': a:oldspecdict.bold || a:newspecdict.bold, \'italic': a:oldspecdict.italic || a:newspecdict.italic, \'underline': a:oldspecdict.underline || a:newspecdict.underline, \'inverse': a:oldspecdict.inverse || a:newspecdict.inverse, \} endfunction "▶1 redrawprogress function s:F.redrawprogress() if !get(s:progress, 'showprogress', 0) return 0 endif let barlen=((winwidth(0))- \((s:progress.showprogress==2)? \ (len(s:progress.elnr)*2+10): \ (8))) let colnum=barlen*s:progress.linesprocessed/ \ s:progress.linestoprocess let s:progress.oldcolnum=0 let bar='['.repeat('=', s:progress.oldcolnum).'>'. \repeat(' ', barlen-s:progress.oldcolnum).'] '. \((s:progress.showprogress==2)? \ (repeat(' ', len(s:progress.elnr)- \ len(s:progress.clnr)). \ (s:progress.clnr). \ '/'.(s:progress.elnr).' '): \ ('')). \repeat(' ', 3-len(s:progress.progress)). \(s:progress.progress).'%%' endfunction "▶1 cmppos function s:F.cmppos(pos1, pos2) return ((a:pos1[0]a:pos2[0]) \ ?(1) \ :((a:pos1[1]a:pos2[1]) \ ?(1) \ :(0))))) endfunction "▶1 matchestoevents function s:F.matchestoevents(matches) let lineevents={} for mtch in a:matches if mtch.startpos ==# mtch.endpos continue endif let lineevents[mtch.startpos[0]]= \extend(get(lineevents, mtch.startpos[0], {}), \ { \ mtch.startpos[1]: \ add(get(get(lineevents, mtch.startpos[0], {}), \ mtch.startpos[1], []), \ ['push', mtch]), \ }) let lineevents[mtch.endpos[0]]= \extend(get(lineevents, mtch.endpos[0], {}), \ { \ mtch.endpos[1]: \ add(get(get(lineevents, mtch.endpos[0], {}), \ mtch.endpos[1], []), \ ['pop', mtch]), \ }) endfor return lineevents endfunction "▶1 ItemsNrSort function s:ItemsNrSort(i1, i2) return a:i1[0]>a:i2[0]?1:-1 endfunction let s:_functions+=[function('s:ItemsNrSort')] "▶1 nritems function s:F.nritems(dct) return map(items(a:dct), '[+v:val[0], v:val[1]]') endfunction "▶1 eventstosplcolumns function s:F.eventstosplcolumns(lineevents) let splcolumns={} let stack=[] let emptymtch={} let oldmtch=emptymtch for [lnr, columnevents] in sort(s:F.nritems(a:lineevents), function('s:ItemsNrSort')) let splcolumns[lnr]={} for [col, cevents] in sort(s:F.nritems(columnevents), function('s:ItemsNrSort')) for [type, mtch] in cevents if type is# 'pop' let i=-1 while stack[i] isnot mtch let i-=1 endwhile call remove(stack, i) elseif type is# 'push' call add(stack, mtch) endif if empty(stack) let mmtch=emptymtch else let mmtch=stack[0] for cmtch in stack[1:] if s:CmpMatches(mmtch, cmtch)==-1 let mmtch=cmtch endif endfor endif if mmtch isnot oldmtch let splcolumns[lnr][col]=[['matchborder', mmtch is emptymtch ? 0 : mmtch.group]] let oldmtch=mmtch endif endfor endfor endfor call filter(splcolumns, '!empty(v:val)') return splcolumns endfunction "▶1 CmpMatches function s:CmpMatches(m1, m2) " Note: sorting must be stable " Note2: It is impossible for two dictionaries to have equal .num keys. Thus if a:m1.num is not " greater then a:m2.num then it is definitely lesser. return a:m1.priority>a:m2.priority \?1 \:(a:m1.num>a:m2.num \ ?1 \ :-1) endfunction let s:_functions+=[function('s:CmpMatches')] "▶1 regescapeslash function s:F.regescapeslash(s) return substitute(a:s, '\v%(\\@1||(col('$')!=1&&a:scol!=col('$')))], \'empty(v:val)'. \ '?extend(self.pasteol, {'. \ a:slnr.'+v:key : matchdct.group'. \ '})'. \ ':0') if has_key(self, 'pointer') while self.pointer (FormatVimFindNextBadlySpelledWord) ]s nnoremap (FormatVimFoundBadlySpelledWord) FoundBadlySpelledWord() try call cursor(a:slnr, 1) if empty(spellbadword()[1]) normal! ]s endif let l:empty='' while line('.') <= a:elnr let [word, type]=spellbadword() let a:d.spelltype=type call a:d.recordmatch(line('.'), col('.'), word) execute 'normal '. \"\(FormatVimFindNextBadlySpelledWord)". \"\(FormatVimFoundBadlySpelledWord)" if !s:found_badly_spelled_word break endif let s:found_badly_spelled_word=0 endwhile finally let &l:wrapscan=saved_wrapscan call winrestview(winview) nunmap (FormatVimFindNextBadlySpelledWord) nunmap (FormatVimFoundBadlySpelledWord) let s:found_badly_spelled_word=0 endtry endfunction "▶1 formatmisspells function s:F.formatmisspells(slnr, elnr) let d={'recordmatch': s:F.recordspellmatch, 'splcolumns': {}} call s:F.collectmisspells(a:slnr, a:elnr, d) return d.splcolumns endfunction "▶1 Collect matches if v:version>703 || (v:version==703 && has('patch627')) "▶2 function s:F.collectmatches(slnr, elnr, mtch, d) if type(a:mtch)!=type({}) || has_key(a:mtch, 'pattern') let regex=((type(a:mtch)==type(''))?(a:mtch):(a:mtch.pattern)) let save_history=&history if save_history let &history+=1 let lasthistitem=histget('/', -1) endif let save_atslash=@/ let winview=winsaveview() try execute 'lockmarks keepmarks keepjumps silent '.a:slnr.','.a:elnr \'s/'.regex.'/\='. \ 'a:d.recordmatch(line("."), col("."), submatch(0))/gne' " Too long pattern error catch /\m^Vim(substitute):E339:/ throw 'TOO LONG REGEX' finally if save_history let &history=save_history if histget('/', -1) isnot# lasthistitem call histdel('/', -1) endif endif let @/=save_atslash call winrestview(winview) endtry else let i=1 while has_key(a:mtch, 'pos'.i) let pos=a:mtch['pos'.i] if a:slnr <= pos[0] && pos[0] <= a:elnr if len(pos) == 1 call a:d.recordmatch(pos[0], 1, getline(pos[0])) call a:d.recordmatch(pos[0], col([pos[0], '$']), '') else if pos[1] <= col([pos[0], '$']) let match=matchstr(getline(pos[0]), '\v\C.{-}%(.%>'.pos[2].'c|$)', \ pos[1]-1) call a:d.recordmatch(pos[0], pos[1], match) endif endif endif let i+=1 endwhile endif endfunction let s:F.collectonelinematches=s:F.collectmatches function s:F.multilinewrapper(lnr, col, match) dict let lines=split(a:match, "\n", 1) if len(lines)==1 return self.recordmlmatch(a:lnr, a:col, lines) endif let lnr=a:lnr let realmatch=[] let idx=a:col-1 while !empty(lines) let line=getline(lnr) let chunk=remove(lines, 0) if empty(lines) call add(realmatch, chunk) break else " If we did not hit NUL chunk represents match from starting column (first column in " case of non-starting line) to the end of line. if line[(idx):] is# chunk call add(realmatch, chunk) else " If we did match was split at NUL (as it is represented as newline). We need to " determine how many NULs were there. Alternative and, probably, better approach " would be comparing len(chunk) to len(line)-idx in the same cycle. Note that " match may be actually one line match, but contain NULs. let chunk.="\n".remove(lines, 0) while line[(idx):] isnot# chunk && !empty(lines) let chunk.="\n".remove(lines, 0) endwhile call add(realmatch, chunk) endif " In non-starting line match starts in first column let idx=0 endif let lnr+=1 endwhile call self.recordmlmatch(a:lnr, a:col, realmatch) endfunction function s:F.collectmultilinematches(slnr, elnr, mtch, d) let a:d.recordmatch=s:F.multilinewrapper call s:F.collectmatches(a:slnr, a:elnr, a:mtch, a:d) endfunction else "▶2 function s:F.collectonelinematches(slnr, elnr, regex, d) let winview=winsaveview() try let lnr=a:slnr while lnr call cursor(lnr, 1) let lnr=search(a:regex, 'nWc', a:elnr) if lnr let line=getline(lnr) let notinlinestr="\n\n" while stridx(line, notinlinestr)!=-1 let notinlinestr.="\n" endwhile let matches=[] let chunks=split(substitute(line, \ a:regex, \ '\=[notinlinestr, add(matches, submatch(0))][0]', \ 'g'), notinlinestr, 1) let col=1 for m in matches let col+=len(remove(chunks, 0)) call a:d.recordmatch(lnr, col, m) let col+=len(m) endfor let lnr+=1 endif endwhile catch /\m^Vim(let):E339:/ throw 'TOO LONG REGEX' finally call winrestview(winview) endtry endfunction endif "▶1 collecttags function s:F.collecttags(slnr, elnr, tags, tagregexpref, tagregexsuf, d) let tagregex=join(map(map(copy(a:tags), 'v:val.name'), 'escape(v:val, "\\")'), '\|') let tagregex=escape(a:tagregexpref.tagregex.a:tagregexsuf, '/') try call s:F.collectonelinematches(a:slnr, a:elnr, tagregex, a:d) catch /\m\c^TOO LONG REGEX$/ let tlen=len(a:tags) if tlen==1 call s:_f.warn('toolongpat') else call s:F.collecttags(a:slnr, a:elnr, a:tags[:(tlen/2)], a:tagregexpref, a:tagregexsuf, \ a:d) call s:F.collecttags(a:slnr, a:elnr, a:tags[(tlen/2+1):], a:tagregexpref, a:tagregexsuf, \ a:d) endif endtry endfunction "▶1 formatlinks function s:F.formatlinks(opts, slnr, elnr) let splcolumns={} for [regex, expr] in a:opts.linkregexes let d={'recordmatch': s:F.recordlinkmatch, 'splcolumns': {}, 'expr': expr} try call s:F.collectonelinematches(a:slnr, a:elnr, s:F.regescapeslash(regex), d) endtry let splcolumns=s:F.mergesplcolumns(splcolumns, d.splcolumns) endfor return d.splcolumns endfunction "▶1 formattags function s:F.formattags(opts, slnr, elnr, starttagreg, endtagreg) "▶2 Объявление переменных let fname=expand('%:.') " Имя обрабатываемого файла let tags=taglist('.') " Список тёгов if empty(tags) return [{}, {}, {}, []] endif let tag2flmap={} " Словарь: имя тёга — список местонахождений let fcontents={} " Кэш содержимого файлов " Список символов, которых надо дополнительно экранировать let addescapes=s:_f.getoption('AddTagCmdEscapes') let curfl2tagsmap={} " Словарь: номер линии (в этом файле) — список тёгов let filetags={} " Dictionary mapping file names to list of " tags contained there and commands to find them let splcolumns={} " Dictionary mapping lines to specialcolumns dictionary that maps " columns to name of tags that start or stop at given position let winview=winsaveview() "▶2 Process tags in the current file try "▶3 Init some tag keys and filter tags call map(tags, 'extend(v:val, {"_incurf":('. \ 'extend(v:val, {"_tfname": fnamemodify(v:val.filename, ":.")})'. \ '._tfname is# fname)})') if a:opts.ignoretags call filter(tags, '!v:val._incurf') endif "▶3 Generate prefix and suffix of the regular expression let tagregexpref='\C\V' if !empty(a:starttagreg) let tagregexpref.=a:starttagreg.'\zs' endif let tagregexpref.='\k\@a:elnr || linenr1 call insert(tag2flmap[tag.name], \remove(tag2flmap[tag.name], -1)) endif else call remove(tag2flmap[tag.name], -1) endif endif "▶3 Тёг находится в другом файле elseif filereadable(tag._tfname) let tfname=tag._tfname if tag.cmd[0] is# '/' let filetags[tfname]=add(get(filetags, tfname, []), \ [tag, tag2flmap[tag.name], len(tag2flmap[tag.name])-1, \ tag2flmap[tag.name][-1]]) else let linenr=(+matchstr(tag.cmd, '\v^\d+')) if linenr call insert(tag2flmap[tag.name][-1], [tfname, linenr]) let tag._linenr=linenr else call remove(tag2flmap[tag.name], -1) endif endif "▶3 Файл, в котором должен находится тёг, не существует else call remove(tag2flmap[tag.name], -1) endif endfor finally call winrestview(winview) endtry "▶2 Tags in the files that should be searched for " WARNING: tags list below is modified for [tfname, tags] in items(filetags) let fc=readfile(tfname, 'b') call map(tags, 'add(v:val, '. \'"\\m".escape(substitute(v:val[0].cmd, ''\m^/\|/$'', "", "g"), addescapes))') let linenr=1 for line in fc try if empty(filter(tags, \'line=~#v:val[-1]' \ .'? empty(insert(v:val[-2], [tfname, linenr]))' \ .': !empty(extend(v:val[0], {"_linenr": linenr}))')) break endif catch endtry let linenr+=1 endfor call map(reverse(tags), 'remove(v:val[1], v:val[2])') endfor unlet filetags tags "▶2 Удаление лишних записей call filter(usedtags, 'has_key(v:val, "_linenr")') call map(tag2flmap, 'filter(v:val, "has_key(v:val[-1], \"_linenr\")")') call filter(tag2flmap, '!empty(v:val)') let maxduptags=s:_f.getoption('MaxDupTags') if maxduptags call filter(tag2flmap, 'type(get(v:val, '.maxduptags.', 0))=='.type(0)) endif return [tag2flmap, curfl2tagsmap, splcolumns, usedtags] endfunction "▶1 getsigns function s:F.getsigns(buf) let defined={} let placed={} redir => placedstr silent execute 'sign place buffer='.a:buf redir END for [line, id, name] in map(filter(split(placedstr, "\n"), \ 'v:val[:3] is# " "'), \ 'map(split(v:val[4:]), '. \ '"v:val[stridx(v:val, ''='')+1:]")') if !has_key(defined, name) redir => defstr silent execute 'sign list' name redir END let defstr=defstr[(6+len(name)):] let defined[name]={'id': name} " TODO check icons with spaces for prop in split(defstr) let eqidx=stridx(prop, '=') let defined[name][prop[:(eqidx-1)]]=prop[(eqidx+1):] endfor if s:whatterm isnot# 'gui' && has_key(defined[name], 'icon') unlet defined[name].icon endif endif let placed[line]=[+id, defined[name]] endfor return [defined, placed] endfunction "▶1 cf let s:cf={} "▶2 cf.new function s:cf.new(format, opts) let cf=extend(copy(self), {'format': a:format, \ 'opts': a:opts, \ 'vars': {}, \ 'nextvar': 'a', \ 'cache': {}, \ 'stylestr': ''}) if !a:opts.minimizefunc call map(copy(s:cf), 'v:key[-6:] is# "_nomin" && empty(extend(cf, {v:key[:-7]: v:val}))') endif if has_key(a:format, 'sbsdstate') let cf.sbsdstate=deepcopy(a:format.sbsdstate) if exists('*cf.sbsdstate.init') call cf.sbsdstate.init(cf.opts) endif endif if cf.opts.funcfile isnot 0 && filereadable(cf.opts.funcfile) call writefile([], cf.opts.funcfile) endif return cf endfunction "▶2 cf.savefunc function s:cf.savefunc(fname, ftext, Func) if !self.opts.debugging return endif let ftext=(type(a:ftext)==type([]) ? a:ftext : split(a:ftext, "\n")) if self.opts.breaks isnot 0 if has_key(self.opts.breaks, a:fname) let breakarg=matchstr(string(a:Func), '''\@<=.*''\@=') for v in self.opts.breaks[a:fname] if type(v)==type(0) execute 'breakadd func' v breakarg else if a:fname is# 'compiledformat' let v=self.expr(v) endif let lnr=1 for line in ftext[1:] if line=~#v execute 'breakadd func' lnr breakarg endif let lnr+=1 endfor endif endfor endif endif if self.opts.funcfile isnot 0 let firstline=substitute(ftext[0], '\v(^function\ )@<=([^(]+)', 'Compiled:'.a:fname, 'g') let lastline=ftext[-1] let ftext=add(insert(map(ftext[1:-2],'repeat(" ",shiftwidth()).v:val'),firstline),lastline) let self.fcontents=extend(get(self, 'fcontents', []), ftext) endif endfunction "▶2 cf.writefunc function s:cf.writefunc() if self.opts.funcfile is 0 return endif if self.opts.saveopts let optslst=['let opts={}'] let keys=sort(keys(self.opts)) let maxklen=max(map(copy(keys), 'len(v:val)')) for k in keys call add(optslst, printf('let opts.%-*s = %s', maxklen, k, string(self.opts[k]))) endfor else let optslst=[] endif call writefile(optslst+get(self, 'fcontents', []), self.opts.funcfile) endfunction "▶2 cf.compile function s:cf.compile(slnr, elnr, options, sbsd) let cformat={} let self.cformat=cformat let cformat.strescape=s:F.fmtcompileone(self, '%>'.get(self.format, 'strescape', '@@@'), \ self.opts, 'strescape') unlockvar self.opts call s:F.initstrescapeopts(self.opts, self) call s:F.initfileopts(self.opts, a:slnr, a:elnr, a:options, self.format, a:sbsd) lockvar! self.opts for key in s:keylist if has_key(self.format, key) let cformat[key]=s:F.fmtcompileone(self, self.format[key], self.opts, key) lockvar! cformat[key] if self.opts.profiling || self.opts.debugging call add(s:profiled, cformat[key].f) endif endif endfor for key in ['haslf', 'nolf'] let cformat[key]=get(self.format, key, 0) lockvar cformat[key] endfor let cformat.compiledspec=s:F.compilespecfunc(self) lockvar! cformat if self.opts.profiling || self.opts.debugging call add(s:profiled, cformat.compiledspec) endif unlockvar self.opts call s:F.initcfopts(self.opts, self) lockvar! self.opts if has_key(self.format, 'state') let self.state=deepcopy(self.format.state) if exists('*self.state.init') call self.state.init(self.opts) endif endif endfunction "▶2 cf.has function s:cf.has(key) return has_key(self.cformat, a:key) endfunction "▶2 cf.newvar function s:F.inc(next) if empty(a:next) return add(a:next, 'a') endif let c=a:next[-1] if c is# '9' call remove(a:next, -1) return add(s:F.inc(a:next), 'a') elseif c is# 'Z' if len(a:next)>1 let a:next[-1]='0' else call remove(a:next, -1) return add(s:F.inc(a:next), 'a') endif elseif c is# 'z' let a:next[-1]='A' else let a:next[-1]=nr2char(char2nr(c)+1) endif return a:next endfunction function s:cf.newvar(var) if a:var !~# '\v^(\a\:)@!\a\w*$' throw 'Invalid variable name: '.a:var endif if !has_key(self.vars, a:var) let self.vars[a:var]=self.nextvar let i=len(self.nextvar)-1 let self.nextvar=join(s:F.inc(split(self.nextvar, '\v.@=')), '') endif return self.vars[a:var] endfunction function s:cf.newvar_nomin(var) return a:var endfunction "▶2 cf.getvar function s:cf.getvar(var) let v=matchstr(a:var, '\v^(\a\:)@!\w+') if empty(v) return a:var elseif !has_key(self.vars, v) throw 'No variable: '.v endif return self.vars[v] endfunction "▶2 cf.getvar_nomin function s:cf.getvar_nomin(var) return a:var endfunction "▶2 cf.expr function s:cf.expr(expr, ...) return substitute(a:expr, '\v\%(\%|\-?\d+|\a\w*)', \ '\=submatch(1) is# "%" ? '. \ '"%" : '. \ (a:0&&a:1 \ ? 'self.newvar(submatch(1))' \ : 'self.getvar(submatch(1))'), \ 'g') endfunction "▶2 cf.specstr function s:cf.specstr(list) if type(a:list)==type('') return 'call(%cformat.compiledspec,[%cf]+'.a:list.',{})' else return '%cformat.compiledspec(%cf,'.join(a:list,',').')' endif endfunction "▶2 cf.get function s:cf.get(key, ...) if self.cformat[a:key].isconst return s:F.squote(call(self.cformat[a:key].f, a:000+[self.opts, self], {})) elseif self.cformat[a:key].isexpr let atargs=copy(s:defatargs) call map(s:keyargs[a:key][:-3], 'extend(atargs,{v:val : a:000[v:key]})') let atargs.opts='%opts' let atargs.cf='%cf' return s:F.procpc(self, self.cformat[a:key].str, self.opts, a:key, 0, atargs, 1) endif return '%cformat.'.a:key.'.f('.join(a:000, ',').',%opts,%cf)' endfunction "▶2 cf.getnrstr function s:cf.getnrstr(nrvarstr) return ((self.opts.dornr)? \ ((self.opts.donr)? \ ('(('.a:nrvarstr.'=='.self.opts.cline.')?'. \ self.opts.cline.' : '. \ ('abs('.a:nrvarstr.'-'.self.opts.cline.')').')'): \ ('abs('.a:nrvarstr.'-'.self.opts.cline.')')): \ (a:nrvarstr)) endfunction "▶2 cf.hasreq function s:cf.hasreq(key, reqs) let reqs=self.cformat[a:key].req for req in a:reqs if has_key(reqs, req) return 1 endif endfor return 0 endfunction "▶1 gettagreg function s:F.gettagreg(options, st) let tagreg=((a:options[a:st.'tagreg'] is 0)? \ (s:_f.getoption(toupper(a:st[0]).a:st[1:].'TagReg')): \ (a:options[a:st.'tagreg'])) if tagreg is 0 let tagreg='' else let tagreg='\m\%('.tagreg.'\)\V' endif return tagreg endfunction "▶1 ff let s:ff={} let s:ffcomp={} "▶2 ffcomp.c function s:ffcomp.c(cmd, arg) return get(self._cmds, a:cmd, a:cmd). \((a:arg =~# '\v^[a-zA-Z0-9_@!]')?(' '):('')).a:arg endfunction "▶2 ff.expr function s:ff.expr(...) return call(self.__cf.expr, a:000, self.__cf) endfunction "▶2 ff.__let function s:ff.__let(var, type, expr) return self._out()._deeper('let',self.expr(a:var, 1),a:type,self.expr(a:expr)) \._out() endfunction "▶2 ff.let function s:ff.let(var, expr) return self.__let(a:var, '', a:expr) endfunction "▶2 ff.__leta function s:ff.__leta(var, type, expr) if !empty(self._l) && self._l[-1][0] is# 'let' && self._l[-1][2] is# a:type let var=self.expr(a:var, 1) let expr=self.expr(a:expr) let lblock=self._l[-1] if lblock[1][0] is# '[' let lblock[1]=lblock[1][:-2].','.var.']' let lblock[3]=lblock[3][:-2].','.expr.']' else let lblock[1]='['.lblock[1].','.var.']' let lblock[3]='['.lblock[3].','.expr.']' endif return self else return call(self.__let, [a:var, a:type, a:expr], self) endif endfunction "▶2 ff.leta " TODO: Automatically determine validity of concatenating lets by recording variables used by right " expression function s:ff.leta(var, expr) return self.__leta(a:var, '', a:expr) endfunction "▶2 ff.letabreak function s:ffcomp.letabreak(...) " Do nothing endfunction function s:ff.letabreak() return self._out()._deeper('letabreak')._out() endfunction "▶2 ff.letspec function s:ff.letspec(spname, ...) return self.leta('%'.a:spname.'spec', self.__cf.specstr(map(copy(a:000),'"''".v:val."''"'))) endfunction "▶2 ff.letcf function s:ff.letcf(var, ...) if self.__cf.has(a:1) return self.leta(a:var, call(self.__cf.get, a:000+["''"], self.__cf)) else return self.leta(a:var, "''") endif endfunction "▶2 ff.appendcf function s:ff.appendcf(var, ...) if self.__cf.has(a:1) return self.append(a:var, call(self.__cf.get, a:000+[a:var], self.__cf)) else return self endif endfunction "▶2 ff.letc function s:ff.letc(var, ...) let self.__curvar=a:var if a:0>1 return call(self.letcf, [self.__curvar]+a:000, self) else return call(self.let, [self.__curvar, a:1], self) endif endfunction "▶2 ff.appendc function s:ff.appendc(...) if a:0>1 return call(self.appendcf, [self.__curvar]+a:000, self) else return call(self.append, [self.__curvar, a:1], self) endif endfunction "▶2 ff.for function s:ff.for(var, expr) return call(self.__orig.for, [self.expr(a:var, 1), self.expr(a:expr)], self) endfunction "▶2 ff.call, .while, .if, .elseif, .addif, .strappend, .echo, .echomsg, .echon, .unlet for s:f in ['call', 'while', 'if', 'elseif', 'addif', 'strappend', 'echo', 'echomsg', 'echon', \'unlet'] execute 'function s:ff.'.s:f."(...)\n". \' return call(self.__orig.'.s:f.', '. \ 'map(copy(a:000), "self.expr(v:val)"), '. \ "self)\n". \'endfunction' endfor unlet s:f "▶2 ff.increment function s:ff.increment(var, ...) return self.__let(a:var, '+', get(a:000, 0, 1)) endfunction "▶2 ff.decrement function s:ff.decrement(var, ...) return self.__let(a:var, '-', get(a:000, 0, 1)) endfunction "▶2 newff function s:F.newff(cf) let r=s:_r.new_constructor() let r.__orig=filter(copy(r), 'type(v:val)==2') call extend(r, s:ff) call extend(r._comp, s:ffcomp) let r.append=r.strappend let r.__cf=a:cf if !a:cf.opts.minimizefunc let r.__leta=r.__let endif return r endfunction "▶1 s:NRSort :: Integer, Integer → -1|1 " We really don’t care about the order of equal integers function s:NRSort(a, b) return a:a>a:b ? 1 : -1 endfunction let s:_functions+=['s:NRSort'] "▶1 mergesplcolumns function s:F.mergesplcolumns(splcolumns1, splcolumns2) if empty(a:splcolumns1) if empty(a:splcolumns2) return {} else return deepcopy(a:splcolumns2) endif elseif empty(a:splcolumns2) return deepcopy(a:splcolumns1) endif let splcolumns=deepcopy(a:splcolumns1) call map(copy(a:splcolumns2), \'has_key(splcolumns, v:key)'. \ '?map(copy(v:val), '. \ '"has_key(splcolumns[".v:key."], v:key)'. \ '?extend(splcolumns[".v:key."][v:key], v:val)'. \ ':extend(splcolumns[".v:key."], {v:key : v:val})")'. \ ':extend(splcolumns, {v:key : v:val})') return splcolumns endfunction "▶1 mergeallsplcolumns function s:F.mergeallsplcolumns(splcolumns1, ...) let d={} let d.ret=deepcopy(a:splcolumns1) if a:0==0 return empty(d.ret)?{}:d.ret endif for splcolumns in a:000 let d.ret=s:F.mergesplcolumns(d.ret, splcolumns) unlet splcolumns endfor return d.ret endfunction "▶1 flattensplcolumns function s:F.flattensplcolumns(lowsplcolumns, highsplcolumns) let splcolumns=s:F.mergesplcolumns(a:lowsplcolumns, a:highsplcolumns) if empty(a:lowsplcolumns) || empty(a:highsplcolumns) return splcolumns endif " FIXME Make sure they do not overlap return splcolumns endfunction "▶1 initfileopts function s:F.initfileopts(opts, slnr, elnr, options, format, sbsd) let opts=a:opts " TODO Control it with a:options? let opts.sbsd=a:sbsd "▶2 Intended display width let columns=0+(((a:options.columns)+0)? \ (a:options.columns): \ (-1)) if columns==-1 let columns=max(map(range(1, line('$')), 'virtcol([v:val,''$''])-1') \ +[s:_f.getoption('MinColumns')]) endif let opts.columns=columns "▶2 Folds let foldcolumn=0 if !opts.ignorefolds && has_key(a:format, 'foldcolumn') " TODO Support for formatting foldcolumn using .line let foldcolumn=((a:options.foldcolumn==-2)? \ (s:_f.getoption('FoldColumn')): \ (a:options.foldcolumn)) if foldcolumn==-1 let foldcolumn=&foldcolumn endif endif let opts.foldminlines=&foldminlines let opts.foldcolumn = foldcolumn let opts.columns += foldcolumn "▶2 Concealed characters let formatconcealed=0 if has('conceal') && &conceallevel if a:options.concealed is -1 let formatconcealed=s:_f.getoption('FormatConcealed') elseif a:options.concealed is 'both' let formatconcealed=2 elseif a:options.concealed is 'shown' let formatconcealed=1 else let formatconcealed=a:options.concealed endif endif if formatconcealed==2 \&& !(has_key(a:format, 'concealedstart') || has_key(a:format, 'concealedend')) let formatconcealed=1 endif let opts.formatconcealed=formatconcealed let opts.conceallevel=&conceallevel "▶2 Cursor let opts.ignorecursor=(a:options.cursor==-1? \ s:_f.getoption('IgnoreCursor'): \ !a:options.cursor) let opts.cline=line('.') let hascline=(a:slnr<=opts.cline && opts.cline<=a:elnr) let opts.docline=(!opts.ignorecursor && &cursorline && hascline) let opts.hascline=(!opts.ignorecursor && hascline) if formatconcealed && !opts.ignorecursor let opts.formatconcealedcursor=stridx(&concealcursor, 'n')==-1 else let opts.formatconcealedcursor=0 endif let opts.checkcline=opts.docline || opts.formatconcealedcursor "▶2 Line numbers if has_key(a:format, 'linenr') let opts.donr=((a:options.number==-1)? \ (s:_f.getoption('NoLineNR')): \ (!a:options.number)) if opts.donr!=-1 let opts.donr=!opts.donr endif let opts.dornr=((a:options.relativenumber==-1)? \ (s:_f.getoption('RelativeNumber')): \ (a:options.relativenumber)) if opts.dornr==-1 if exists('+relativenumber') let opts.dornr=&relativenumber else let opts.dornr=0 endif endif if opts.donr==-1 let opts.donr=&number endif let opts.dosomenr=(opts.donr || opts.dornr) if opts.dosomenr if opts.donr let opts.linenumlen=max([len(a:elnr), &numberwidth-1]) elseif opts.dornr let opts.linenumlen=max([len(a:elnr-a:slnr), &numberwidth-1]) endif let opts.columns+=1+opts.linenumlen else let opts.linenumlen = 0 endif else " TODO Add support for formatting line numbers using .line let opts.donr = 0 let opts.dornr = 0 let opts.dosomenr = 0 let opts.linenumlen = 0 endif "▶2 Structures with tags if opts.ignoretags!=2 let starttagreg=s:F.gettagreg(a:options, 'start') let endtagreg=s:F.gettagreg(a:options, 'end') let [opts.tags, opts.curtags, opts.tagssplcolumns, usedtags]= \s:F.formattags(opts, a:slnr, a:elnr, starttagreg, endtagreg) if has_key(a:format, 'tagproc') call map(usedtags, 'a:format.tagproc(opts, v:val)') endif let opts.dotags=!empty(opts.tagssplcolumns) else let opts.tags={} let opts.curtags={} let usedtags=0 let opts.dotags=0 let opts.tagssplcolumns=0 endif "▶2 Structures with link if opts.formatlinks let opts.linkssplcolumns=s:F.formatlinks(opts, a:slnr, a:elnr) let opts.dolinks=!empty(opts.linkssplcolumns) else let opts.linkssplcolumns=0 let opts.dolinks=0 endif "▶2 Structures with matches if opts.formatsomematch let [opts.matchessplcolumns, opts.matchespasteol]=s:F.formatmatches(opts, a:slnr, a:elnr) let opts.domatches=!empty(opts.matchessplcolumns) else let opts.domatches=0 let opts.matchespasteol=0 let opts.matchessplcolumns=0 endif "▶2 Structures with spell if opts.formatspell let opts.spellsplcolumns=s:F.formatmisspells(a:slnr, a:elnr) let opts.dospell=!empty(opts.spellsplcolumns) else let opts.spellsplcolumns=0 let opts.dospell=0 endif "▶2 'listchars' let listchars={} let npregex='\v\t|\p@!.' if &list && !((a:options.list)? \ (s:_f.getoption('IgnoreList')): \ (!a:options.list)) let opts.list=1 call map(map(split(&listchars, \'\v\,%(%(eol|tab|trail|extends|precedes|nbsp|conceal|space)\:)@='), \'matchlist(v:val,''\v^(\w+)\:(.*)$'')[1:2]'), \'extend(listchars, {v:val[0]: substitute(v:val[-1],''\\\(.\)'',''\1'',"g")})') if has_key(listchars, 'nbsp') let npregex=substitute(npregex, '\\t', '\0| ', '') endif else let opts.list=0 let listchars.tab=' ' endif let opts.npregex=npregex let opts.listchars=listchars let opts.spsplcolumns=s:F.formatspecialchars(a:opts, a:slnr, a:elnr) if empty(opts.spsplcolumns) if has_key(opts.listchars, 'nbsp') unlet opts.listchars.nbsp endif endif if has_key(listchars, 'space') let opts.spacesplcolumns=s:F.formatspaces(a:slnr, a:elnr) if empty(opts.spacesplcolumns) unlet opts.listchars.space endif else let opts.spacesplcolumns=0 endif "▶2 special columns let opts.splcolumns=s:F.mergeallsplcolumns(opts.linkssplcolumns, \ opts.tagssplcolumns, \ opts.matchessplcolumns, \ opts.spsplcolumns, \ opts.spellsplcolumns, \ opts.spacesplcolumns) let opts.existingspecials={} call map(copy(opts.splcolumns), \'map(copy(v:val), '. \ '''map(copy(v:val), '. \ '"extend(opts.existingspecials, {v:val[0]: 1})")'')') if has_key(opts.listchars, 'trail') let opts.existingspecials.trail=1 endif "▶2 Signs let dosigns=0 if has_key(a:format, 'sign') " TODO Support for formatting signs column using .line let dosigns=((a:options.signs==-1)? \ (!s:_f.getoption('IgnoreSigns')): \ (a:options.signs)) if dosigns let gsr=s:F.getsigns(bufnr('%')) let opts.signdefinitions=gsr[0] let opts.placedsigns=gsr[1] if empty(opts.placedsigns) let dosigns=0 endif let opts.columns+=2 endif endif let opts.dosigns=dosigns "▲2 "▶2 Progress bar: bar length let opts.barlen=((winwidth(0))- \((opts.showprogress==2)? \ (len(a:elnr)*2+10): \ (8))) if opts.barlen<0 let opts.showprogress=0 endif "▲2 let opts.dolinemergehl=(opts.dodiff || opts.docline || opts.dosigns) let opts.domergehl=(opts.dolinemergehl || opts.domatches || opts.dospell) return opts endfunction "▶1 initstrescapeopts function s:F.initstrescapeopts(opts, cf) let opts=a:opts let opts.strescape_=a:cf.cformat.strescape.f function! opts.strescape(s) dict return self.strescape_(a:s, self) endfunction let opts.leadingspace = opts.strescape(' ') let opts.difffillchar = opts.strescape(opts.fillchars.diff) let opts.foldfillchar = opts.strescape(opts.fillchars.fold) let opts.vertseparator = opts.strescape(opts.fillchars.vert) let opts.qleadingspace = s:F.squote(opts.leadingspace) return opts endfunction "▶1 initcfopts function s:F.initcfopts(opts, cf) let opts=a:opts "▶2 persistentfiller let opts.persistentfiller=0 if opts.dodiff if opts.collapsafter let opts.persistentfiller=0 elseif a:cf.has('difffiller') let opts.persistentfiller=!a:cf.hasreq('difffiller', ['a:line','a:char','a:cur','a:cf']) else let opts.persistentfiller=1 endif endif "▶2 persistent fold column if a:cf.has('foldcolumn') let opts.persistentfdc=!a:cf.hasreq('foldcolumn', ['a:line', 'a:cf']) else let opts.persistentfdc=1 endif "▶2 persistent sign column if a:cf.has('sign') let opts.persistentsc=!a:cf.hasreq('sign', ['a:line', 'a:cf']) else let opts.persistentsc=1 endif "▲2 return opts endfunction "▶1 initopts function s:F.initopts(opts, options, format, sbsd) let opts=a:opts call filter(opts, 'v:key[:1] isnot# "__"') let opts.highlight=s:F.gethighlight() let opts.dodiff=&diff && (a:options.diff == -1 \ ? !s:_f.getoption('IgnoreDiff') \ : a:options.diff) let opts.usestylenames=s:_f.getoption('UseStyleNames') "▶2 Debugging/profiling let opts.profiling=v:profiling let opts.debugging=s:_f.getoption('Debugging') if opts.debugging let opts.funcfile = s:_f.getoption('Debugging_FuncF') let opts.minimizefunc = s:_f.getoption('Debugging_MinFu') let opts.breaks = s:_f.getoption('Debugging_Break') let opts.saveopts = s:_f.getoption('Debugging_SaveO') else let opts.funcfile = 0 let opts.minimizefunc = 1 let opts.breaks = 0 let opts.saveopts = 0 endif "▶2 Folds " Ignore folds if IgnoreFolds is set, there is no “fold” key or vim does not " support folding. let ignorefolds=((a:options.folds==-1)? \ (s:_f.getoption('IgnoreFolds')): \ (!a:options.folds)) || \!has('folding') let allfolds=!(ignorefolds || a:sbsd) && \((a:options.allfolds==-1)? \ (s:_f.getoption('AllFolds')): \ (a:options.allfolds)) && \(has_key(a:format, 'foldstart') || \ has_key(a:format, 'foldend')) let ignorefolds=ignorefolds || !has_key(a:format, 'fold') let opts.ignorefolds = ignorefolds let opts.allfolds = allfolds "▶2 fillchars let fillchars={} if has('windows') && has('folding') && (!ignorefolds || opts.dodiff || a:sbsd==1) let fcs=split(&fillchars, '\v\,%(%(stl%(nc)?|vert|fold|diff)\:)@=') for fc in fcs let [o, v]=matchlist(fc, '\v^(\w*)\:(.*)$')[1:2] let fillchars[o]=v endfor endif let opts.fillchars = extend({'diff': '-', 'fold': '-', 'vert': '|'}, fillchars) "▶2 Tags let opts.ignoretags=2 if has_key(a:format, 'tagstart') || has_key(a:format, 'tagend') if a:options.tags is -1 let opts.ignoretags=s:_f.getoption('IgnoreTags') elseif a:options.tags is# 'all' let opts.ignoretags=0 elseif a:options.tags is# 'local' let opts.ignoretags=1 else let opts.ignoretags=(2-a:options.tags) endif endif "▶2 Links let opts.formatlinks=0 if has_key(a:format, 'linkstart') || has_key(a:format, 'linkend') if a:options.links is -1 let opts.formatlinks=s:_f.getoption('FormatLinks') else let opts.formatlinks=a:options.links endif endif if opts.formatlinks let opts.linkregexes=s:_f.getoption('LinkRegexes') else let opts.linkregexes=[] endif "▶2 Matches let matches=((a:options.matches == -1)? \ (s:_f.getoption('FormatMatches')): \ (a:options.matches)) if matches is -1 if has('extra_search') && &hlsearch && (exists('v:hlsearch') \ ? v:hlsearch \ : 1) let matches='all' else let matches='matches' endif endif let opts.formatsearch = (matches is# 'all' || matches is# 'search') && !empty(@/) let opts.formatmatches = (matches is# 'all' || matches is# 'matches') && !empty(getmatches()) let opts.formatsomematch = opts.formatsearch || opts.formatmatches "▶2 Spelling support let opts.formatspell = &spell && (a:options.spell == -1 \ ? !s:_f.getoption('IgnoreSpell') \ : a:options.spell) if opts.formatspell try call spellbadword() catch /Vim(call):E756:/ let opts.formatspell=0 endtry endif "▶2 collapsafter if a:sbsd let opts.collapsafter=0 else let opts.collapsafter=((a:options.collapsfiller==-1)? \ (s:_f.getoption('CollapsFiller')): \ (a:options.collapsfiller)) if !has_key(a:format, 'difffiller') && has_key(a:format, 'collapsedfiller') let opts.collapsafter=1 elseif !has_key(a:format, 'collapsedfiller') let opts.collapsafter=0 endif endif "▲2 "▶2 addoptsfun if has_key(a:format, 'addoptsfun') call map(copy(a:format.addoptsfun()), 'extend(opts, {"__".v:key : deepcopy(v:val)})') endif "▶2 Progress bar let opts.showprogress=0 if has('statusline') if a:options.progress is -1 let opts.showprogress=s:_f.getoption('ShowProgress') elseif a:options.progress is 'lines' let opts.showprogress=2 elseif a:options.progress is 'percent' let opts.showprogress=1 else let opts.showprogress=a:options.progress endif endif let opts.canresize=(s:whatterm is# 'gui') "▲2 return opts endfunction "▶1 highlight function s:F.gethighlight() let highlight={} call map(split(&highlight, ','), 'extend(highlight, {v:val[0]: v:val[2:]})') " FIXME 'highlight' option does not necessary hold C:HiGroup pairs return map({ \'SpecialKey': '8', \'NonText': '@', \'Directory': 'd', \'ErrorMsg': 'e', \'IncSearch': 'i', \'Search': 'l', \'MoreMsg': 'm', \'ModeMsg': 'M', \'LineNr': 'n', \'CursorLineNr': 'N', \'Question': 'r', \'StatusLine': 's', \'StatusLineNC': 'S', \'Title': 't', \'VertSplit': 'c', \'Visual': 'v', \'VisualNOS': 'V', \'WarningMsg': 'w', \'WildMenu': 'W', \'Folded': 'f', \'FoldColumn': 'F', \'DiffAdd': 'A', \'DiffChange': 'C', \'DiffDelete': 'D', \'DiffText': 'T', \'SignColumn': '>', \'SpellBad': 'B', \'SpellCap': 'P', \'SpellRare': 'R', \'SpellLocal': 'L', \'Conceal': '-', \'Pmenu': '+', \'PmenuSel': '=', \'PmenuSbar': 'x', \'PmenuThumb': 'X', \}, 'get(highlight, v:val, v:key)') endfunction "▶1 initspecs function s:F.initspecs(ff, opts) "normalspec — Default format "specialspec — Format for special symbols, including lcs=tab и lcs=trail "ntspec — Format for lcs=eol и lcs=nbsp "foldspec — Folds format "fcspec — Fold column format (+folds) "nrspec — Line number format (&nu||&rnu) "fillspec — Deleted lines format (&diff) "clspec — Cursor line format (&cul) "cspec — Cursor format "conspec — Concealed characters format "scspec — Sign column format call a:ff.letspec('normal', 'Normal') if a:opts.formatconcealed call a:ff.letspec('con', a:opts.highlight.Conceal) endif if a:opts.dosigns call a:ff.letspec('sc', a:opts.highlight.SignColumn) endif "▶2 Folds if a:opts.foldcolumn call a:ff.letspec('fc', a:opts.highlight.FoldColumn) endif if !a:opts.ignorefolds || a:opts.allfolds call a:ff.letspec('fold', a:opts.highlight.Folded) endif "▲2 if a:opts.dodiff call a:ff.letspec('fill', a:opts.highlight.DiffDelete) endif "▶2 Spelling support if a:opts.dospell call a:ff.letspec('spb', a:opts.highlight.SpellBad) call a:ff.letspec('spc', a:opts.highlight.SpellCap) call a:ff.letspec('spr', a:opts.highlight.SpellRare) call a:ff.letspec('spl', a:opts.highlight.SpellLocal) endif "▶2 Line numbers if a:opts.dosomenr call a:ff.letspec('nr', a:opts.highlight.LineNr) if a:opts.hascline if v:version>703 || (v:version==703 && has('patch479')) call a:ff.letspec('nrcl', a:opts.highlight.CursorLineNr) else call a:ff.letspec('nrcl', a:opts.highlight.LineNr, 'CursorLine') endif endif endif "▲2 endfunction "▶1 compilespecfunc function s:F.compilespecfunc(cf) let opts=a:cf.opts let cformat=a:cf.cformat let d={} let hlnamearg='a:hlname' if opts.domergehl let hlnamearg='a:0?([a:hlname]+a:000):a:hlname' endif let specfunction=[ \'function d.compiledspec(cf, hlname'.((opts.domergehl)?(', ...'):('')).')', \ (opts.domergehl \ ?('let key=a:0?join([a:hlname]+a:000,''+''):(empty(a:hlname)?''-'':a:hlname)') \ :('let key=empty(a:hlname)?''-'':a:hlname')), \'if has_key(a:cf.cache,key)', \ 'retu a:cf.cache[key]', \'en', \((opts.docline)? \ ('let r=[call(s:F.getspecdict,'. \ '['.hlnamearg.']+'. \ '((a:hlname is#'.string(opts.highlight.LineNr).')?([0]):([])),{}),'''']'): \ ('let r=[s:F.getspecdict('.hlnamearg.'),'''']')), \] if a:cf.has('style') call add(specfunction, \'let r[1]=a:cf.cformat.style.f(key,r,'''',a:cf.opts,a:cf)') if (a:cf.has('begin') && a:cf.hasreq('begin', ['a:style'])) || \(a:cf.has('end') && a:cf.hasreq('end', ['a:style'])) call add(specfunction, 'let a:cf.stylestr.=r[1]') endif endif call extend(specfunction, [ \'let a:cf.cache[key]=r', \'retu r', \'endfunction']) execute join(specfunction, "\n") call a:cf.savefunc('compiledspec', specfunction, d.compiledspec) return d.compiledspec endfunction "▶1 gentrailinglines function s:F.gentrailinglines(ldiff, slnr, cf) let cformat=a:cf.cformat let opts=a:cf.opts let ldiff=a:ldiff let clnr=a:slnr+1 let ntspec=cformat.compiledspec(a:cf, 'NonText') if !opts.ignorefolds let fcspec=cformat.compiledspec(a:cf, 'FoldColumn') endif if opts.dosigns let scspec=cformat.compiledspec(a:cf, 'SignColumn') endif let r=[] while ldiff let curstr='' if a:cf.has('linestart') let curstr.=cformat.linestart.f(4, ntspec, clnr, curstr, opts, a:cf) endif if opts.foldcolumn let curstr.=cformat.foldcolumn.f(repeat(' ', opts.foldcolumn), fcspec, clnr, \ 0, curstr, opts, a:cf) endif if opts.dosigns let curstr.=cformat.sign.f(' ', scspec, clnr, 0, curstr, opts, a:cf) endif if opts.dosomenr " XXX " Do nothing: line numbers are not displayed in this case, neither any space " is skipped endif let curstr.=cformat.line.f('~', ntspec, clnr, 0, curstr, opts, a:cf) if a:cf.has('lineend') let curstr.=cformat.lineend.f(4, ntspec, clnr, 1, curstr, opts, a:cf) endif call add(r, curstr) let clnr+=1 let ldiff-=1 endwhile return r endfunction "▶1 Run side-by-side diff function s:F.sbsdrun(type, slnr, elnr, options, cf, sbsd) let opts=a:cf.opts let cformat=a:cf.cformat let normalspec=cformat.compiledspec(a:cf, 'Normal') let vsspec=cformat.compiledspec(a:cf, opts.highlight.VertSplit) "▶2 Используемые буфера let lastwin=winnr('$') let curwin=winnr() let i=1 let dwinnrs=[] while i<=lastwin if i!=curwin && getwinvar(i, '&diff') call add(dwinnrs, i) endif let i+=1 endwhile if dwinnrs==[] return [] endif "▶2 Получение реальных номеров линий в текущем буфере let clnr=1 let virtclnr=1 let maxline=line('$') let virtslnr=0 let virtelnr=0 while clnr<=maxline let virtclnr+=diff_filler(clnr) if !virtslnr && clnr==a:slnr let virtslnr=clnr endif if clnr==a:elnr let virtelnr=virtclnr break endif let virtclnr+=1 let clnr+=1 endwhile "▶2 call insert(dwinnrs, curwin) for dwinnr in dwinnrs if getwinvar(curwin, '&foldmethod') isnot# 'diff' || \getwinvar(dwinnr, '&foldmethod') isnot# 'diff' let a:options.ignorefolds=1 let ignorefolds=1 endif endfor let r=[] let i=2 let width=0 for dwinnr in dwinnrs "▶2 Получение номеров линий в другом буфере execute dwinnr.'wincmd w' let clnr=1 let virtclnr=1 let maxline=line('$') let dslnr=0 let dstartinfiller=0 let delnr=0 let notenoughlines=0 while clnr<=maxline+1 let filler=diff_filler(clnr) let virtclnr+=filler if !dslnr && virtclnr>=virtslnr let dstartinfiller=filler-(virtclnr-virtslnr) let dslnr=clnr endif if virtclnr>virtelnr let delnr=((clnr<=maxline)?(clnr):(maxline)) break endif let virtclnr+=1 let clnr+=1 endwhile if !delnr let delnr=line('$') let notenoughlines=1 endif "▶2 Получение отформатированных текстов if !opts.ignorefolds normal! zM endif let r2=s:F.format(a:type, dslnr, delnr, a:options, i, a:cf) "▶2 Добавление sbsdstart или sbsdsep let oldcolumns=opts.columns unlockvar opts.columns let opts.columns=width let width+=oldcolumns+1 unlockvar opts.sbsd let opts.sbsd=a:sbsd lockvar! opts if empty(r) let r=r2 if a:cf.has('sbsdstart') call map(r, 'cformat.sbsdstart.f(normalspec, v:key, "", '. \ 'opts, a:cf).v:val') endif else let r2=r2[(dstartinfiller):(len(r)-1+dstartinfiller)] if len(r2)1 let cf=a:2 else let cf=s:F.fmtprepare(a:type, a:options, slnr, elnr, sbsd) endif call cf.compile(slnr, elnr, a:options, sbsd) let cformat=cf.cformat let opts=cf.opts call s:F.compilespecfunc(cf) "▶2 side-by-side diff if sbsd==1 return s:F.sbsdrun(a:type, slnr, elnr, a:options, cf, sbsd) endif "▲2 "▶2 Precreation of deleted line if possible, here if opts.dodiff if opts.persistentfiller if cf.has('difffiller') let fillspec=cformat.compiledspec(cf, 'DiffDelete') let fillerstr=cformat.difffiller.f(opts.fillchars.fold, fillspec, 0, 0, '', opts, cf) else let fillerstr='' endif endif endif "▲2 let ff=s:F.newff(cf) "▶2 Initialize ff variables call ff.leta('%cf', 'a:cf') call ff.leta('%cformat', 'a:cformat') call ff.leta('%opts', 'a:opts') call ff.leta('%r', '[]') " List with formatted output call ff.leta('%clnr', slnr) " Line being processed if opts.domatches call ff.leta('%matchhlname', 0) call ff.leta('%oldmatchhlname', 0) endif if opts.dospell call ff.leta('%spellhlname', 0) call ff.leta('%oldspellhlname', 0) endif if !empty(opts.existingspecials) call ff.leta('%name2sptypemap', ff.expr(substitute(string(filter({ \ 'special': '%specialchar', \ 'tagstart': '%startedtag', \ 'tagend': '%endedtag', \ 'linkstart': '%startedlink', \ 'linkend': '%endedlink', \ 'spacestart': '%startedspace', \ 'spaceend': '%endedspace', \ 'spellborder': '%spellhlname', \ 'matchborder': '%matchhlname', \ 'trail': '%dummy', \ }, 'has_key(opts.existingspecials, v:key)')), ' ', '', 'g'), 1)) call ff.leta('%spcol', '[]') endif call ff.leta('%id', 0) call ff.leta('%concealinfo', '[0]') call ff.leta('%nocbreak', 0) call ff.leta('%concealedbreak', 0) call ff.letabreak() call s:F.initspecs(ff, opts) call ff.letabreak() "▶2 Progress bar support: init if opts.showprogress set laststatus=2 call ff.leta('%oldprogress', 0 ) call ff.leta('%linesprocessed', 0 ) call ff.leta('%linestoprocess', elnr-slnr+1 ) if !opts.canresize " Вторая часть прогресс бара " Старые значения % сделанного и длины строки из '='; " первая часть progress bar’а со строкой = call ff.leta('%oldcolnum', 0) call ff.leta('%barstart', string('[')) call ff.leta('%barlen', opts.barlen) call ff.leta('%barend', string(repeat(' ', opts.barlen).'] ')) else let s:progress.showprogress=opts.showprogress let s:progress.oldcolnum=0 let s:progress.clnr=slnr let s:progress.progress=0 let s:progress.elnr=elnr let s:progress.linesprocessed=0 let s:progress.linestoprocess=(elnr-slnr+1) endif endif "▶2 Precreation of all sign columns if possible, inside function if opts.dosigns if opts.persistentsc call ff.letcf('%nosignsc', 'sign', '" "', '%scspec', 0, 0) call ff.leta('%scols', '{}') call ff.for('[%sname,%sign]', 'items(%opts.signdefinitions)') call ff.if('has_key(%sign,''texthl'')') call ff.leta('%spec', cf.specstr(['%sign.texthl'])) call ff.else() call ff.leta('%spec', '%scspec') call ff.endif() if s:whatterm is# 'gui' call ff.if('has_key(%sign,''icon'')') call ff.letcf('%scols[%sname]', 'sign', '%sign.icon', '%spec', 0, 2) call ff._up() endif call ff.addif('has_key(%sign,''text'')') call ff.letcf('%scols[%sname]', 'sign','%sign.text','%spec', 0, 1) call ff.else() " XXX If sign is missing text attribute texthl attribute does not apply call ff.letcf('%scols[%sname]', 'sign', ''' ''', '%scspec', 0, 1) call ff.endif() call ff.endfor() endif endif "▶2 Folds if opts.allfolds || opts.foldcolumn call ff.leta('%fclnr', slnr) "▶3 Get folds closed at the moment if !opts.ignorefolds call ff.leta('%closedfolds', '{}') call ff.leta('%closedfoldslist', '[]') if !opts.allfolds call ff.leta('%closedfoldsends', '[]') endif call ff.while('%fclnr<='.elnr) call ff.if('foldclosed(%fclnr)!=-1') call ff.call('add(%closedfoldslist,%fclnr)') call ff.letc('%closedfolds[%fclnr]', 'linestart', 1, '%foldspec', '%fclnr') if !opts.foldcolumn if opts.dosigns call ff.appendc('sign', '" "', '%foldspec', '%fclnr', 0) endif if opts.dosomenr call ff.appendc('sign', '" "', '%foldspec', '%fclnr', 0) endif call ff.appendc('fold', 'foldtextresult(%fclnr)', '%foldspec', '%fclnr') call ff.appendc('lineend', 1, '%foldspec', '%fclnr', 0) endif if !opts.allfolds call ff.call('add(%closedfoldsends, foldclosedend(%fclnr))') if opts.showprogress call ff.decrement('%linestoprocess', '%closedfoldsends[-1]-%fclnr') endif call ff.leta('%fclnr', '%closedfoldsends[-1]') endif call ff.endif() call ff.increment('%fclnr') call ff.endwhile() endif "▲3 call setwinvar(0, '&foldminlines', 0) "▶3 Process other folds "▶4 Initializing fold column-related variables if opts.foldcolumn call ff.leta('%foldlevel', -1) call ff.leta('%fdchange', 0) call ff.leta('%foldlevels', '{}') call ff.leta('%foldcolumns', '{}') call ff.leta('%foldclosedcolumns', '{}') if opts.persistentfdc call ff.leta('%foldcolumns[-1]', 'repeat(['. \ cf.get('foldcolumn', \ 'repeat('' '','.opts.foldcolumn.')', \ '%fcspec', 0, -1, "''").'], 3)') else call ff.leta('%foldcolumnstarts', '{}') call ff.leta('%foldcolumns[-1]', repeat(' ', opts.foldcolumn)) endif endif "▶4 Initializing common variables call ff.leta('%possiblefolds', '{}') call ff.leta('&foldlevel', 0) call ff.leta('%oldfoldnumber', -1) call ff.leta('%foldnumber', 0) "▶4 Main cycle: getting all folds call ff.while('%oldfoldnumber!=%foldnumber') call ff.leta('%oldfoldnumber', '%foldnumber') call ff.leta('%fclnr', slnr) "▶5 Fold column if opts.foldcolumn if opts.foldcolumn>1 call ff.if('&foldlevel>='.(opts.foldcolumn-1)) endif call ff.leta('%rstart', '&foldlevel'.printf('%+d', -(opts.foldcolumn-3))) call ff.leta('%rend', '&foldlevel') call ff.let('%fdctext', \ '((%rstart<=%rend)?'. \ '((%rend<10)?'. \ '(join(range(%rstart,%rend),'''')):'. \ '((%rstart<10)?'. \ '(join(range(%rstart,9),'''').'. \ 'repeat(''>'',%rend-9))'. \ ':'. \ '(repeat(''>'','.(opts.foldcolumn-2).'))))'. \ ':'. \ '(''''))') call ff.let('%fdcnexttext', '%fdctext.((&foldlevel>=9)?'. \ '(''>''):'. \ '((&foldlevel)?'. \ '(&foldlevel+1):'. \ '(''|'')))') if opts.foldcolumn>1 call ff.leta('%fdcclosedtext', '((&foldlevel>='.opts.foldcolumn.')?'. \ '(((%rstart<=10)?'. \ '(%rstart-1):'. \ '(''>'')).%fdctext):'. \ '(repeat(''|'','.(opts.foldcolumn-1).'))).''+''') else call ff.leta('%fdcclosedtext', '''+''') endif call ff.leta('%fdctextend','repeat('' '','.(opts.foldcolumn-1).'-len(%fdctext))') if opts.foldcolumn>1 call ff.else() call ff.let('%fdctext', 'repeat(''|'',&foldlevel)') call ff.let('%fdcnexttext', '%fdctext.''|''') call ff.leta('%fdctextend','repeat('' '','.(opts.foldcolumn-1).'-len(%fdctext))') call ff.let('%fdcclosedtext', '%fdctext.''+''.%fdctextend') call ff.endif() endif call ff.append('%fdcnexttext', '%fdctextend') call ff.leta('%fdcopenedtext', '%fdctext.''-''.%fdctextend') if opts.persistentfdc call ff.let('%foldcolumns[&foldlevel]', '['. \ join(map(['%fdcclosedtext', '%fdcopenedtext', '%fdcnexttext'], \ 'cf.get("foldcolumn",v:val,"%fcspec",0,'. \ '"&foldlevel","''''")'), ',').']') else call ff.leta('%foldcolumns[&foldlevel]', '%fdcnexttext') endif endif "▶5 Obtaining folds positions call ff.while('%fclnr<='.elnr) call ff.if('foldclosed(%fclnr)>-1') call ff.leta('%foldend', 'foldclosedend(%fclnr)') if opts.allfolds let foldtextstr='foldtextresult(%fclnr)' if opts.dosomenr let formatstr='''%%'.opts.linenumlen.'u''' if opts.donr && opts.dornr let formatstr='%fclnr=='.opts.cline. \ '?'.substitute(formatstr, '%%', '%%-', ''). \ ':'.formatstr endif let foldtextstr='printf('.formatstr.','.cf.getnrstr('%fclnr').').'' ''.'. \ foldtextstr endif if opts.dosigns let foldtextstr=''' ''.'.foldtextstr endif if opts.foldcolumn let foldtextstr='%fdcclosedtext.'.foldtextstr endif call ff.leta('%foldtext', foldtextstr) if opts.foldminlines>0 call ff.if('%foldend-%fclnr>'.opts.foldminlines) endif if cf.has('foldstart') call ff.if('!has_key(%possiblefolds,%fclnr)') call ff.let('%possiblefolds[%fclnr]', '{}') call ff.endif() call ff.if('!has_key(%possiblefolds[%fclnr],''start'')') call ff.let('%possiblefolds[%fclnr].start', '[]') call ff.endif() call ff.call('add(%possiblefolds[%fclnr].start,'. \ cf.get('foldstart', '%foldtext', '%foldspec', '%fclnr', \ '&foldlevel', "''").')') endif if cf.has('foldend') call ff.leta('%foldinsbefore', '%foldend+1') call ff.if('!has_key(%possiblefolds,%foldinsbefore)') call ff.let('%possiblefolds[%foldinsbefore]', '{}') call ff.endif() call ff.if('!has_key(%possiblefolds[%foldinsbefore],''end'')') call ff.let('%possiblefolds[%foldinsbefore].end', '[]') call ff.endif() call ff.call('add(%possiblefolds[%foldinsbefore].end,'. \ cf.get('foldend', '%foldtext', '%foldspec', \ '%foldend', '&foldlevel', "''").')') endif if opts.foldminlines>0 call ff.endif() endif endif if opts.foldcolumn call ff.leta('%foldlevels[%fclnr]', '&foldlevel') call ff.if('!has_key(%foldlevels, %foldend+1)') call ff.let('%foldlevels[%foldend+1]', '&foldlevel-1') call ff.endif() let self.__curvar='%closedfolds[%fclnr]' if !opts.persistentfdc if !opts.ignorefolds call ff.if('has_key(%closedfolds, %fclnr)') call ff.appendc('foldcolumn', '%fdcclosedtext', '%fcspec', '%fclnr', \ '&foldlevel') if opts.dosigns call ff.appendc('sign', '" "', '%foldspec', '%fclnr', 0,) endif if opts.dosomenr call ff.appendc('linenr',cf.getnrstr('%fclnr'), '%foldspec', '%fclnr') endif call ff.appendc('fold', 'foldtextresult(%fclnr)', '%foldspec', '%fclnr') call ff.appendc('lineend', 1, '%foldspec', '%fclnr', 0) call ff.endif() endif call ff.letcf('%foldcolumnstarts[%fclnr]', 'foldcolumn', '%fdcopenedtext', \ '%fcspec', '%fclnr', '&foldlevel') elseif !opts.ignorefolds call ff.if('has_key(%closedfolds, %fclnr)') call ff.appendc('%foldcolumns[&foldlevel][0]') if opts.dosigns call ff.appendc('sign', '" "', '%foldspec', '%fclnr', 0) endif if opts.dosomenr call ff.appendc('linenr', cf.getnrstr('%fclnr'), '%foldspec', '%fclnr') endif call ff.appendc('fold', 'foldtextresult(%fclnr)', '%foldspec', '%fclnr') call ff.appendc('lineend', 1, '%foldspec', '%fclnr', 0) call ff.endif() endif endif call ff.let('%fclnr', '%foldend') call ff.increment('%foldnumber') call ff.endif() call ff.increment('%fclnr') call ff.endwhile() call ff.increment('&foldlevel') call ff.endwhile() endif "▶2 Main cycle: processing lines call ff.while('%clnr<='.(elnr+((opts.dodiff)?(1):(0)))) call ff.letc('%curstr', "''") if opts.checkcline call ff.leta('%iscline', '%clnr=='.opts.cline) endif if has_key(opts.listchars, 'space') call ff.leta('%startedspace', 0) call ff.leta('%didstartedspace', 0) call ff.leta('%endedspace', 0) endif "▶3 Fold column support if opts.foldcolumn if opts.dodiff call ff.leta('%fillfoldlevel', '%foldlevel') endif call ff.if('has_key(%foldlevels, %clnr)') call ff.let('%fdchange', 1) call ff.leta('%foldlevel', '%foldlevels[%clnr]') call ff.else() call ff.let('%fdchange', 0) call ff.endif() endif "▶3 Progress bar support if opts.showprogress if opts.canresize call ff.leta('%barlen', 'winwidth(0)-'.((opts.showprogress==2)? \ ((len(elnr)*2)+10): \ ('8'))) endif call ff.increment('%linesprocessed') call ff.leta('%progress', '100*%linesprocessed/%linestoprocess') call ff.leta('%colnum', ((opts.canresize)? \ ('%barlen'): \ (opts.barlen)). \ '*%linesprocessed/%linestoprocess') if opts.showprogress!=2 call ff.if('%progress!=%oldprogress || '. \ '%colnum!='.((opts.canresize)? \ ('s:progress.'): \ ('%')).'oldcolnum') endif if opts.canresize call ff.leta('%bar', '''[''.repeat(''='',%colnum).''>''.'. \ 'repeat('' '',%barlen-%colnum).''] ''') else call ff.if('%colnum!=%oldcolnum') call ff.append('%barstart', 'repeat("=",%colnum-%oldcolnum)') call ff.let('%barend', '%barend[%colnum-%oldcolnum :]') call ff.endif() call ff.let('%bar', '%barstart.">".%barend') endif call ff.append('%bar', ((opts.showprogress==2)? \ ('repeat('' '','.opts.linenumlen.'-len(%clnr))'. \ '.%clnr.''/'.elnr.' ''.'): \ ('')). \ 'repeat('' '',3-len(%progress)).%progress.''%%''') call ff.call('setwinvar(0,''&statusline'',%bar)') call ff.do('redrawstatus') if opts.showprogress!=2 call ff.endif() endif call ff.let('%oldprogress', '%progress') call ff.leta(((opts.canresize)?('s:progress.'):('%')).'oldcolnum', '%colnum') if opts.canresize call ff.leta('s:progress.progress', '%progress') call ff.leta('s:progress.linesprocessed', '%linesprocessed') if opts.showprogress==2 call ff.leta('s:progress.clnr', '%clnr') endif endif endif "▶3 Processing deleted lines if opts.dodiff call ff.leta('%filler', 'diff_filler(%clnr)') call ff.if('%filler>0') call ff.letcf('%curstrstart', 'linestart', \ 2.((opts.collapsafter)? \ '+(%filler>='.opts.collapsafter.')': \ ''), '%fillspec', '%clnr') "▶4 Leading columns (fold, sign, number) if opts.foldcolumn call ff.append('%curstrstart', ((opts.persistentfdc)? \ ('%foldcolumns[%fillfoldlevel][2]'): \ (cf.get('foldcolumn', \ '%foldcolumns[%fillfoldlevel]', \ '%fcspec', '%clnr', \ '%fillfoldlevel', \ '%curstrstart')))) endif if opts.dosigns call ff.append('%curstrstart', ((opts.persistentsc)? \ ('%nosignsc'): \ (cf.get('sign', '" "', '%scspec', \ '%clnr', 0, '%curstrstart')))) endif if opts.dosomenr call ff.appendcf('%curstrstart', 'linenr',"''", '%nrspec', '%clnr') endif "▶4 Filler if !opts.persistentfiller if opts.collapsafter call ff.if('%filler<'.opts.collapsafter) endif call ff.let('%curfil', '%filler') call ff.while('%curfil') call ff.let('%curstr', '%curstrstart') call ff.appendc('difffiller', s:F.squote(opts.fillchars.diff), \ '%fillspec', '%clnr', '%curfil') call ff.appendc('lineend', 2, '%fillspec', '%clnr',0) call ff.call('add(%r,%curstr)') call ff.decrement('%curfil') call ff.endwhile() if opts.collapsafter call ff.else() call ff.let('%curstr', '%curstrstart') call ff.appendc('collapsedfiller', '%filler', '%fillspec', '%clnr') call ff.appendc('lineend', 3, '%fillspec', '%clnr', 0) call ff.call('add(%r,%curstr)') call ff.endif() endif else call ff.let('%curstr', '%curstrstart') call ff.appendc(s:F.squote(fillerstr)) call ff.appendc('lineend', 2, '%fillspec', '%clnr', 0) call ff.increment('%r', 'repeat([%curstr],%filler)') endif "▲4 call ff.let('%curstr', "''") call ff.endif() call ff.if('%clnr>'.elnr) call ff.break() call ff.endif() endif "▶3 Processing folds if !opts.ignorefolds && !opts.allfolds && !opts.foldcolumn "▶4 call ff.if('foldclosed(%clnr)!=-1') call ff.letcf('%curstr', 'linestart', 1, '%foldspec', '%clnr') if opts.dosigns call ff.appendc('sign', '" "', '%foldspec', '%clnr', 0) endif if opts.dosomenr call ff.appendc('linenr', cf.getnrstr('%clnr'), '%foldspec', '%clnr') endif call ff.appendc('fold', 'foldtextresult(%clnr)', '%foldspec', '%clnr') call ff.appendc('lineend', 1, '%foldspec', '%clnr', 0) call ff.call('add(%r,%curstr)') call ff.let('%clnr', 'foldclosedend(%clnr)+1') call ff.continue() call ff.else() elseif opts.allfolds || opts.foldcolumn "▶4 if opts.allfolds call ff.if('has_key(%possiblefolds,%clnr)') call ff.let('%pf', '%possiblefolds[%clnr]') if cf.has('foldend') call ff.if('has_key(%pf,''end'')') call ff.increment('%r', '%pf.end') call ff.endif() endif if cf.has('foldstart') call ff.if('has_key(%pf,''start'')') call ff.increment('%r', '%pf.start') call ff.endif() endif call ff.endif() endif if !opts.ignorefolds call ff.if('!empty(%closedfoldslist)&&%clnr==%closedfoldslist[0]') call ff.call('remove(%closedfoldslist,0)') call ff.call('add(%r,%closedfolds[%clnr])') if !opts.allfolds call ff.let('%clnr', 'remove(%closedfoldsends,0)+1') call ff.decrement('%foldlevel') call ff.continue() endif call ff.endif() endif endif "▶3 Processing regular lines "▶4 Initializing variables call ff.let('%linestr', 'getline(%clnr)') call ff.let('%linelen', 'len(%linestr)') " Indicates that this line differs call ff.leta('%diffattr', ((opts.dodiff)?('diff_hlID(%clnr,1)'):(0))) if opts.formatconcealed call ff.leta('%concealdiff', 0) endif if !empty(opts.existingspecials) call ff.leta('%specialcolumns', 'deepcopy(get(%opts.splcolumns,%clnr,{}))') endif if opts.dodiff "▶5 " XXX diffid is taken from beyond the end of line because inside the line there may be " differences in highlighting: overall line is highlighted in one color and parts that " differ in the other call ff.if('%diffattr') call ff.let('%diffid', 'diff_hlID(%clnr,%linelen+1)') call ff.let('%diffhlname', 'synIDattr(%diffid,''name'')') call ff.let('%dspec', cf.specstr(['''Normal''','%diffhlname'])) call ff.else() call ff.let('%diffid', 0) call ff.endif() endif "▲5 if has_key(opts.listchars, 'trail') call ff.leta('%trail', 'match(%linestr,''\v\s*$'')') call ff.let('%specialcolumns[%trail+1]', \ 'add(get(%specialcolumns,%trail+1,[]),[''trail'',0])') endif "▶4 Line start "▶5 Find sign if opts.dosigns call ff.leta('%sign', 'get(get(%opts.placedsigns,%clnr,[]),1,{})') endif "▶5 Determine line spec " Full rules are listed later. Rules used here: " diff > cursorline > signs > normal " Anything but normal is cancelled by highlighting with higher priority. let linehlnamestrbase='[''Normal'']' let linehlnamestr=linehlnamestrbase if opts.dosigns let linehlnamestr='(has_key(%sign,''linehl'')'. \ '?['.linehlnamestrbase[1:-2].',%sign.linehl]'. \ ':'.linehlnamestr.')' endif if opts.docline " XXX CursorLine highlight group is NOT controlled by &highlight option let linehlnamestr='(%iscline'. \ '?['.linehlnamestrbase[1:-2].',''CursorLine'']'. \ ':'.linehlnamestr.')' endif if opts.dodiff let linehlnamestr='(%diffattr'. \ '?['.linehlnamestrbase[1:-2].',%diffhlname]'. \ ':'.linehlnamestr.')' endif let linehlnamestrorig=linehlnamestr call ff.let('%linehlname', linehlnamestr) let linehlnamestr='%linehlname' if linehlnamestrorig is# linehlnamestrbase call ff.leta('%linespec', '%normalspec') else call ff.let( '%linespec', cf.specstr(linehlnamestr)) endif "▲5 call ff.appendc('linestart', 0, '%linespec', '%clnr') if opts.foldcolumn "▶5 if opts.persistentfdc call ff.appendc('%foldcolumns[%foldlevel][2-%fdchange]') else call ff.if('has_key(%foldcolumnstarts,%clnr)') call ff.appendc('%foldcolumnstarts[%clnr]') call ff.else() call ff.appendc('foldcolumn', '%foldcolumns[%foldlevel]', '%fcspec', '%clnr', \ '%foldlevel') call ff.endif() endif endif if opts.dosigns "▶5 call ff.if('!empty(%sign)') if opts.persistentsc call ff.appendc('%scols[%opts.placedsigns[%clnr][1].id]') else call ff.let('%sign', '%opts.placedsigns[%clnr][1]') call ff.let('%sspec', 'has_key(%sign,''texthl'')?'. \ cf.specstr(['%sign.texthl']).':%scspec') if s:whatterm is# 'gui' call ff.addif('has_key(%sign,''icon'')') call ff.appendc('sign', '%sign.icon', '%sspec', '%clnr', 2) call ff._up() endif call ff.addif('has_key(%sign,''text'')') call ff.appendc('sign', '%sign.text', '%sspec', '%clnr', 1) call ff.else() call ff.appendc('sign','" "', '%scspec', '%clnr', 1) call ff.endif() endif call ff.else() if opts.persistentsc call ff.appendc('%nosignsc') else call ff.appendc('sign', '" "', '%scspec', '%clnr', 0) endif call ff.endif() endif if opts.dosomenr "▶5 call ff.appendc('linenr', cf.getnrstr('%clnr'), \ ((opts.hascline)? \ ('(%iscline?'. \ '%nrclspec :'. \ '%nrspec)'): \ ('%nrspec')), '%clnr') endif "▶4 Processing line text "▶5 Initialize variables call ff.leta('%cstartcol', 0) call ff.leta('%oldid', hlID('Normal')) if opts.dodiff call ff.leta('%olddiffid', '%diffid') endif if opts.formatconcealed call ff.leta('%oldconcealinfo', '[0]') if opts.formatconcealed==1 call ff.leta('%concealed', 0) endif endif call ff.leta('%curcol', 1) if opts.dosigns call ff.leta('%hlnamebase', 'has_key(%sign,''linehl'')?[%sign.linehl]:[]') endif call ff.leta('%startcol', '1') "▲5 "▶5 Initialize lists " XXX Concealed list MUST be initialized in one loop as otherwise synconcealed()[-1] will report " different values on each loop run if opts.formatconcealed let synconcealedstr='map(range(1,%linelen),''synconcealed(''.%clnr.'',v:val)'')' if opts.formatconcealedcursor let synconcealedstr='%iscline?repeat([[0]],%linelen):'.synconcealedstr endif call ff.leta('%synconcealed', synconcealedstr) endif "▲5 call ff.while('%curcol<=%linelen') "▶5 Initialize variables "▶6 idstr " Note: collecting all IDs and concealinfo at once with map(range()) appears to be much slower. let idstr='synID(%clnr,%curcol,1)' if has_key(opts.listchars, 'trail') let idstr='%curcol>%trail'.(opts.dolinemergehl? \ '&&len(%linehlname)==1': \ ''). \ '?'.hlID(opts.highlight.SpecialKey). \ ':'.idstr endif "▶6 Concealed characters break condition, with hack if opts.formatconcealed let concealedbreakstr='(%concealinfo[0]||%oldconcealinfo[0])'. \ '&&%concealinfo!=#%oldconcealinfo' if opts.conceallevel==2 " XXX This is a hack to make the following work as displayed: " :syntax match Group /alpha/ conceal cchar=a " :syntax match Group1 /p/ contained containedin=Group " With the above syntax code text `alpha` is matched as below: " alpha " ^^^^^ Group " ^ Group1 " It is displayed as "a" (*single* a) when cole=2 (and "a-a" when cole=1 thus no hack needed " for that level). " %nocbreak variable is used later to omit updating %oldconcealinfo let nocbreakstr='%concealinfo[0]'. \ '&&%oldconcealinfo[0]'. \ '&&!empty(%oldconcealinfo[1])'. \ '&&empty(%concealinfo[1])' let concealedbreakstr='!%nocbreak&&('.concealedbreakstr.')' endif endif "▲6 let whilecond= \ '%curcol<=%linelen&&!empty(extend(l:,{''%id'':'.idstr. \ (opts.dodiff \ ? ',''%diffid'':diff_hlID(%clnr,%curcol)' \ : ''). \ (opts.formatconcealed \ ? ',''%concealinfo'':%synconcealed[%curcol-1]' \ : ''). \ (!empty(opts.existingspecials) \ ? ',''%spcol'':get(%specialcolumns,%curcol,[])' \ : '').'}))'. \ (opts.formatconcealed && opts.conceallevel==2 \ ? '&&!empty(extend(l:,{''%nocbreak'':'.nocbreakstr.'}))' \ : ''). \ (opts.formatconcealed \ ? '&&!empty(extend(l:,{''%concealedbreak'':'.concealedbreakstr.'}))' \ : ''). \ '&&%id==%oldid'. \ (!empty(opts.existingspecials) \ ? '&&empty(%spcol)' \ : ''). \ (opts.dodiff \ ? '&&%diffid==%olddiffid' \ : ''). \ (opts.formatconcealed \ ? '&&!%concealedbreak' \ : ''). \ '&&!empty(extend(l:,{''%curcol'':%curcol+1}))' call ff.while(whilecond) call ff.endwhile() "▶5 Process breaks "▶6 Process special columns if !empty(opts.existingspecials) call ff.call('map(%spcol,'. \ '''extend(l:,{%name2sptypemap[v:val[0]]:v:val[1]})'')') endif "▲6 "▶6 Highlighting breaks in any case call ff.if('%startcol<%curcol'.(opts.formatconcealed==1?'&&!%concealed': '')) call ff.leta('%cstr', '%linestr[%startcol-1:%curcol-2]') let hlnamestr='[synIDattr(%oldid,''name'')]' let hlnamestradd='[]' if opts.dosigns let hlnamestradd='%hlnamebase' endif if opts.docline " XXX CursorLine highlight group is NOT controlled by &highlight option let hlnamestradd='(%iscline'. \ '?[''CursorLine'']'. \ ':'.hlnamestradd.')' endif if opts.dodiff let hlnamestradd='(%diffattr'. \ '?[synIDattr(%olddiffid,''name'')]'. \ ':'.hlnamestradd.')' endif if opts.domatches let hlnamestradd='(%oldmatchhlname is 0'. \ '?'.hlnamestradd. \ ' :[%oldmatchhlname])' endif if opts.dospell let hlnamestradd='(%oldspellhlname is 0'. \ '?[]'. \ ':["!".%oldspellhlname])+'.hlnamestradd endif if hlnamestradd isnot# '[]' let hlnamestr.='+'.hlnamestradd endif if has_key(opts.listchars, 'trail') call ff.if('%startcol-1>=%trail') call ff.let('%cstr', 'substitute(%cstr,'' '','. \ escape(s:F.squote(opts.listchars.trail),'\&~').',''g'')') if opts.domatches let trailhlnamestr='(%oldmatchhlname is 0'. \ '?('.hlnamestr.')'. \ ':[%oldmatchhlname])' call ff.leta('%curspec', cf.specstr(trailhlnamestr)) if has_key(opts.listchars, 'space') call ff._up() else call ff.else() endif else if has_key(opts.listchars, 'space') call ff.leta('%curspec', cf.specstr(hlnamestr)) endif call ff._up() endif endif if has_key(opts.listchars, 'space') call ff.addif('%endedspace||%didstartedspace') let sphlnamestr=s:F.getsphlnamestr(opts, hlnamestr, \ linehlnamestrorig, 1) call ff.let('%curspec', cf.specstr(sphlnamestr)) call ff.leta('%didstartedspace', '!%endedspace') call ff.leta('%endedspace', 0) call ff.leta('%cstr', 'substitute(%cstr,'' '','. \ escape(s:F.squote(opts.listchars.space),'\&~').',''g'')') call ff.elseif('%startedspace') call ff.leta('%startedspace', 0) call ff.leta('%didstartedspace', 1) call ff.leta('%curspec', cf.specstr(hlnamestr)) call ff.else() endif call ff.leta('%curspec', cf.specstr(hlnamestr)) if (has_key(opts.listchars, 'trail') && opts.domatches) || has_key(opts.listchars, 'space') call ff.endif() endif call ff.appendc('line', '%cstr', '%curspec', '%clnr', '%startcol') call ff.endif() "▲6 "▶6 Update oldid and startcol call ff.leta('%oldid', '%id') call ff.leta('%startcol', '%curcol') "▲6 "▶6 Tag/link breaks for type in (opts.dotags?['tag']:[])+(opts.dolinks?['link']:[]) for key in ['start', 'end'] if cf.has(type.key) let varname='%'.key.'ed'.type " startedtag/endedlink/… call ff.if('exists('.string(varname).')') call ff.appendc(type.key, varname, 0, '%clnr', '%curcol') call ff.unlet(varname) call ff.endif() unlet varname endif unlet key endfor unlet type endfor "▲6 "▶6 Concealed character breaks if opts.formatconcealed call ff.addif('%concealedbreak') let ccstrstr='%oldconcealinfo[1]' let ccstrlendiffstr='-!empty(%ccstr)' if opts.conceallevel==1 let ccstrstr='empty('.ccstrstr.')'. \ '?'.s:F.squote(get(opts.listchars,'conceal',' ')). \ ':('.ccstrstr.')' let ccstrlendiffstr='-1' elseif opts.conceallevel==3 let ccstrlendiffstr='' endif let concealdiffstr=s:strdisplaywidthstr.'('. \ '%linestr[%cstartcol-1:%curcol-2],'. \ '%cstartcol==1'. \ '?0'. \ ':'.s:strdisplaywidthstr.'(%linestr[:%cstartcol-2]))'. \ ccstrlendiffstr if opts.formatconcealed==1 call ff.if('%oldconcealinfo[0]') call ff.let('%concealed', 0) if opts.conceallevel!=3 call ff.leta('%ccstr', ccstrstr) if opts.conceallevel==2 call ff.if('!empty(%ccstr)') endif call ff.appendc('line', '%ccstr','%conspec','%clnr','%cstartcol') if opts.conceallevel==2 call ff.endif() endif endif call ff.increment('%concealdiff', concealdiffstr) call ff.endif() call ff.if('%concealinfo[0]') call ff.let('%concealed', 1) call ff.leta('%cstartcol', '%curcol') call ff.endif() " TODO elseif opts.formatconcealed==2 if cf.has('concealedend') call ff.if('%oldconcealinfo[0]') call ff.let('%ccstr', ccstrstr) call ff.appendc('concealedend','%ccstr','%conspec','%clnr','%curcol') call ff.increment('%concealdiff', concealdiffstr) call ff.endif() endif call ff.if('%concealinfo[0]') call ff.let('%cstartcol', '%curcol') if cf.has('concealedstart') call ff.appendc('concealedstart', \ substitute(ccstrstr, 'old', '', 'g'), \ '%conspec', '%clnr', '%curcol') endif call ff.endif() endif call ff.endif() endif "▲6 "▶6 Special characters if has_key(opts.existingspecials, 'special') call ff.if('exists(''%specialchar'')'. \ (opts.formatconcealed==1?'&&!%concealed': '')) if has_key(opts.listchars, 'tab') call ff.if("%specialchar is#'\t'") call ff.let('%virtstartcol', \ '%curcol==1?0:'. \ s:strdisplaywidthstr.'(%linestr[:(%curcol-2)])') let ival=(&tabstop-1).'-%virtstartcol%%'.&tabstop if opts.formatconcealed let cival='%concealdiff+'.ival if opts.formatconcealed==1 let ival=cival endif endif let lcstabfirst=matchstr(opts.listchars.tab, '\v^.') let lcstabnext=opts.listchars.tab[len(lcstabfirst):] call ff.let('%cstr', s:F.squote(lcstabfirst).'.'. \ 'repeat('.s:F.squote(lcstabnext).','.ival.')') if opts.list let tabhlnamestr='[''SpecialKey'']' if opts.dolinemergehl let tabhlnamestr='(len(%linehlname)>1?'. \ substitute(substitute(linehlnamestrorig, \ '''Normal''', 'synIDattr(%id,''name'')', 'g'), \ '%diffhlname', 'synIDattr(%diffid,''name'')', 'g'). \ ':'.tabhlnamestr.')' endif if opts.domatches let tabhlnamestr='(%matchhlname is 0'. \ '?'.tabhlnamestr. \ ':[%matchhlname])' endif " FIXME Still not completely correct else let tabhlnamestr=substitute(hlnamestr, 'old', '', 'g') endif call ff.let('%tabspec', cf.specstr(tabhlnamestr)) if opts.formatconcealed==2 call ff.let('%ccstr', s:F.squote(lcstabfirst).'.'. \ 'repeat('.s:F.squote(lcstabnext).','.cival.')') if cf.has('concealedstart') call ff.if('%ccstr isnot#%cstr') call ff.appendc('concealedstart', '%ccstr', '%tabspec', '%clnr', \ '%curcol') call ff.endif() endif " TODO Test narrowing down tabs when line is hovered endif call ff.appendc('line', '%cstr', '%tabspec', '%clnr', '%curcol') if opts.formatconcealed==2 if cf.has('concealedend') call ff.if('%ccstr isnot#%cstr') call ff.appendc('concealedend', '%ccstr', '%tabspec', '%clnr', \ '%curcol') call ff.endif() endif endif if opts.formatconcealed call ff.let('%concealdiff', 0) endif call ff.else() endif let sphlnamestr=s:F.getsphlnamestr(opts, hlnamestr, \ linehlnamestrorig, 0) let spcharstr='strtrans(%specialchar)' if has_key(opts.listchars, 'nbsp') let spcharstr='(%specialchar is#'' '''. \ '?'.s:F.squote(opts.listchars.nbsp). \ ':'.spcharstr.')' endif call ff.let('%spspec', cf.specstr(sphlnamestr)) call ff.appendc('line', spcharstr, '%spspec', '%clnr', '%curcol') if has_key(opts.listchars, 'tab') call ff.endif() endif call ff.leta('%oldid', '-1') call ff.leta('%startcol', '%curcol+len(%specialchar)') call ff.let('%curcol', '%startcol-1') call ff.unlet('%specialchar') if opts.formatconcealed==1 call ff.elseif('exists(''%specialchar'')') call ff.unlet('%specialchar') endif call ff.endif() endif "▲6 "▶6 Record new variable values if opts.dodiff call ff.leta('%olddiffid', '%diffid') endif if opts.formatconcealed if opts.conceallevel==2 call ff.if('!%nocbreak') endif call ff.leta('%oldconcealinfo', '%concealinfo') if opts.conceallevel==2 call ff.endif() endif endif if opts.domatches call ff.leta('%oldmatchhlname', '%matchhlname') endif if opts.dospell call ff.leta('%oldspellhlname', '%spellhlname') endif "▲6 "▲5 "▶5 Finish cycle call ff.increment('%curcol') call ff.endwhile() "▲5 "▲4 "▶4 Line end "▶5 Concealed characters: formatconcealed=1 if opts.formatconcealed==1 call ff.if('%concealed') call ff.leta('%ccstr', ccstrstr) if opts.conceallevel!=3 if opts.conceallevel==2 call ff.if('!empty(%ccstr)') endif call ff.appendc('line', '%ccstr', '%conspec', '%clnr', '%cstartcol') if opts.conceallevel==2 call ff.endif() endif endif call ff._up() endif "▲5 "▶5 Regural text call ff.addif('%startcol<=%linelen') call ff.leta('%cstr', '%linestr[%startcol-1:]') if has_key(opts.listchars, 'trail') call ff.if('%startcol-1>=%trail') call ff.let('%cstr', 'substitute(%cstr,'' '','. \ escape(s:F.squote(opts.listchars.trail),'\&~').',''g'')') if opts.domatches call ff.leta('%curspec', cf.specstr(trailhlnamestr)) if has_key(opts.listchars, 'space') call ff._up() else call ff.else() endif else if has_key(opts.listchars, 'space') call ff.leta('%curspec', cf.specstr(hlnamestr)) endif call ff._up() endif endif if has_key(opts.listchars, 'space') call ff.addif('(%startedspace||%didstartedspace)'. \ '&&!empty(filter(copy(get(%specialcolumns,%linelen+1,[])),'. \ '''v:val[0]is#"endedspace"''))') let sphlnamestr=s:F.getsphlnamestr(opts, hlnamestr, \ linehlnamestrorig, 1) call ff.let('%curspec', cf.specstr(sphlnamestr)) call ff.leta('%didstartedspace', 0) call ff.leta('%startedspace', 0) call ff.leta('%endedspace', 0) call ff.leta('%cstr', 'substitute(%cstr,'' '','. \ escape(s:F.squote(opts.listchars.space),'\&~').',''g'')') call ff.else() endif call ff.leta('%curspec', cf.specstr(hlnamestr)) if (has_key(opts.listchars, 'trail') && opts.domatches) || has_key(opts.listchars, 'space') call ff.endif() endif call ff.appendc('line', '%cstr', '%curspec', '%clnr', '%startcol') call ff.endif() "▲5 "▶5 Concealed characters: formatconcealed=2 if opts.formatconcealed==2 && cf.has('concealedend') call ff.if('%oldconcealinfo[0]') call ff.appendc('concealedend', ccstrstr, '%conspec', '%clnr', '%curcol') call ff.endif() endif "▲5 "▶5 Special columns if opts.domatches || opts.dotags || opts.dospell call ff.if('has_key(%specialcolumns,%linelen+1)') call ff.call('map(%specialcolumns[%linelen+1],'. \ '''extend(l:,{%name2sptypemap[v:val[0]]:v:val[1]})'')') if opts.domatches call ff.let('%oldmatchhlname', '%matchhlname') endif if opts.dospell call ff.let('%oldspellhlname', '%spellhlname') endif call ff.endif() endif "▲5 "▶5 Tags/links for type in (opts.dotags?['tag']:[])+(opts.dolinks?['link']:[]) if cf.has(type.'start') call ff.if('exists(''%started'.type.''')') call ff.unlet('%started'.type) call ff.endif() endif if cf.has(type.'end') call ff.if('exists(''%ended'.type.''')') call ff.appendc(type.'end', '%ended'.type, 0, '%clnr', '%curcol') call ff.unlet('%ended'.type) call ff.endif() endif unlet type endfor "▲5 "▶5 Processing EOL if has_key(opts.listchars, 'eol') || !empty(opts.matchespasteol) if !empty(opts.matchespasteol) call ff.if('has_key(%opts.matchespasteol,%clnr)') call ff.leta('%savedmatchhlname', '%matchhlname') call ff.leta('%matchhlname', '%opts.matchespasteol[%clnr]') if has_key(opts.listchars, 'eol') call ff.endif() else call ff.letabreak() endif endif let eolhlnamestr=linehlnamestrorig let eolhlnamestr=substitute(eolhlnamestr, \ '''Normal''',string(opts.highlight.NonText),'g') if opts.dolinemergehl let eolhlnamestr='(len(%linehlname)>1?%linehlname :'.eolhlnamestr.')' endif if opts.domatches let eolhlnamestr='(%matchhlname is 0'. \ '?'.eolhlnamestr. \ ':[''Normal'',%matchhlname])' endif call ff.leta('%eolspec', cf.specstr(eolhlnamestr)) call ff.appendc('line', s:F.squote(get(opts.listchars, 'eol', ' ')), \ '%eolspec', '%clnr', '%curcol+1') if !empty(opts.matchespasteol) if has_key(opts.listchars, 'eol') call ff.if('exists(''%savedmatchhlname'')') endif call ff.let('%matchhlname', '%savedmatchhlname') call ff.unlet('%savedmatchhlname') call ff.endif() endif endif call ff.appendc('lineend', 0, '%linespec', '%clnr', '%curcol') "▲4 "▶4 Rules " Order: colorcolumn > matches > diff > cursorline > signs > spell > special > syntax " Rules: " (colorcolumn = colorcolumn and cursorcolumn) " (signs = sign linehl) " (matches = matches themselves and search) " (special = highlighting of special characters defined by listchars (nbsp, tab, trail) and " non-printable characters highlighting) " (cancel = fg, bg and other attributes are ignored) " (cleared = after “hi clear …” or “hi link … Normal”) " - signs cancels special " - cursorline cancels signs and special " - matches cancel cursorline, signs and special " - colorcolumn does NOT cancel ANY highlighting " - If special is cancelled first character (applicable for tabs and non-printable characters, " otherwise read “all characters”) is highligted using syntax (if special is not cancelled " syntax is ignored) " - cleared diff cancels signs and cursorline and enables special " - cleared cursorline cancels signs and enables special " - cleared signs does not affect highlighting " - cleared matches does not affect highlighting " - cleared colorcolumn does not affect highlighting " - cleared diff makes match at the EOL extend past the EOL " - cursorline makes 'showbreak' characters underlined, but does not alter their highlighting " " Possible vim bugs: " - If special is cancelled first character (applicable for tabs and non-printable characters, " otherwise read “all characters”) is highligted using syntax (if special is not cancelled " syntax is ignored) " (bug candidate is “first character” part) " - cleared diff makes match at the EOL extend past the EOL " " FIXME: When using search beware that Search linked to Normal group or with cleared " highlighting does not affect highlighting, while Search group with defined or linked to " a group with defined highlighting for one of fg/bg (but not both) merges with " underlying group. " Search integration with other highlighting attributes needs more investigation. "▲4 "▲3 if !opts.ignorefolds && !opts.allfolds && !opts.foldcolumn call ff.endif() endif call ff.call('add(%r,%curstr)') call ff.increment('%clnr') call ff.endwhile() "▶2 Beginning and end if opts.allfolds call ff.if('has_key(%possiblefolds,%clnr)') call ff.let('%pf', '%possiblefolds[%clnr]') call ff.if('has_key(%pf,''end'')') call ff.increment('%r', '%pf.end') call ff.endif() call ff.endif() endif if !sbsd if cf.has('begin') call ff.call('insert(%r,%cformat.begin.f(%normalspec,'''',%opts,%cf.stylestr,%cf))') endif if cf.has('end') call ff.call('add(%r,%cformat.end.f(%normalspec,'.elnr.','''',%opts,%cf.stylestr,%cf))') endif endif call ff.do('return '.cf.getvar('r')) "▲2 let f = ['function d.compiledformat(cf, cformat, opts)'] \ +ff._tolist(opts.minimizefunc)+ \['endfunction'] let d={} execute join(f, "\n") call cf.savefunc('compiledformat', f, d.compiledformat) call cf.writefunc() if opts.profiling || opts.debugging call add(s:profiled, d.compiledformat) endif "▶2 r let r=d.compiledformat(cf, cformat, opts) "▶2 s:progress if opts.showprogress && opts.canresize let s:progress.opts.showprogress=0 endif "▶2 finish if sbsd is active if sbsd return r endif "▶2 cformat.nolf if cformat.nolf let r=[join(r, '')] endif "▶2 cformat.haslf if cformat.haslf let oldr=r let r=[] for item in oldr let r+=split(item, "\n", 1) endfor endif "▲2 return r endfunction "▶2 wrap function let s:F.format=s:_f.wrapfunc({'function': s:F.format, \ '@altervars': [['+window'], \ ['&l:laststatus'], \ ['&l:statusline'], \ ['&l:foldminlines'],]}) "▶1 addformat function s:F.addformat(type, format) let s:formats[a:type]=deepcopy(a:format) return 1 endfunction "▶1 delformat function s:F.delformat(type) if has_key(s:formats, a:type) unlet s:formats[a:type] return 1 endif return 0 endfunction "▶1 cmd let s:cmd={} "▶2 s:cmd.@FWC let s:filcomprefs= \ ' columns :=(-1) |earg range -1 inf '. \ '? to path W '. \ '? starttagreg :=(0) isreg'. \ '? endtagreg :=(0) isreg'. \ '! cursor :=(-1) '. \ '! number :=(-1) '. \ '! relativenumber :=(-1) '. \ '! list :=(-1) '. \ '! diff :=(-1) '. \ '!+1 tags :=(-1) in [local all] '. \ '!+1 foldcolumn :=(-2) |earg range -1 inf '. \ '! folds :=(-1) '. \ '! signs :=(-1) '. \ '! spell :=(-1) '. \ '!+1 concealed :=(-1) in [shown both] '. \ '!+1 progress :=(-1) in [percent lines] '. \ '!+1 matches :=(-1) in [search matches all] '. \ '! links :=(-1) ' let s:filformats='[:*_f.getoption("DefaultFormat") key formats~start]' let s:cmd['@FWC']=[ \'-onlystrings _ _ '. \'', 'filter'] unlet s:filformats s:filcomprefs "▲2 function s:cmd.function(slnr, elnr, action, ...) let action=a:action "▶2 Действия if action is# 'format' || action is# 'diffformat' let result=call(s:F.format, [a:1, a:slnr, a:elnr, a:2, \ (action is# 'diffformat')], {}) if has_key(a:2, 'to') if &encoding isnot# 'utf-8' && exists('iconv()') let result = map(copy(result), 'iconv(v:val, &enc, "utf-8")') endif call writefile(result, a:2.to, 1) else new ++enc=utf-8 call setline(1, result) endif return 1 elseif action is# 'list' echo join(keys(s:formats), "\n") return 1 endif "▲2 return 0 endfunction "▶1 cmd completion let s:cmpcomprefs= 'columns in ["-1" "80" =string(&co) '. \ '=string(winwidth())] '. \ '? to path W '. \ '? starttagreg _ '. \ '? endtagreg _ '. \ '! number '. \ '! relativenumber '. \ '! list '. \((has('spell'))? \ ('! spell ' ):('')). \((has('diff'))? \ ('! diff ' ):('')). \ '!+1 tags in [local all] '. \((has('folding'))? \ ('!+1 foldcolumn _ '. \ '! folds ' ):('')). \((has('signs'))? \ ('! signs' ):('')). \((has('conceal'))? \ ('!+1 concealed in [shown both] ' ):('')). \((has('statusline'))? \ ('!+1 progress in [percent lines] '):('')). \ '!+1 matches in [all matches '. \ (has('extra_search') \ ?('search') \ :('')).']'. \ '! links ' let s:cmpformats='[key formats]' let s:cmdcomplete='<'. \((has('diff'))? \ ('diffformat ('.s:cmpformats.'{'.s:cmpcomprefs.'})'): \ ('')). \ ' format ('.s:cmpformats.'{'.s:cmpcomprefs. \ ((has('diff'))?( '!+1 collapsfiller _ '):('')). \ ((has('folding'))?('! allfolds ' ):('')).'})'. \ ' list - '. \ '>' "▶1 cmd definition let s:_aufunctions.cmd=s:cmd let s:_aufunctions.comp={ \'function': s:_f.fwc.compile(s:cmdcomplete, 'complete')[0], \} unlet s:cmd unlet s:cmpformats s:cmpcomprefs unlet s:cmdcomplete "▶1 format feature let s:format={} "▶2 format.add :: {f}, name, dict → _ + fdict, s:formats function s:format.add(plugdict, fdict, name, dict) let s:formats[a:name]=extend(deepcopy(a:dict), {'id': a:name, \ 'plid': a:plugdict.id,}) let a:fdict[a:name]=s:formats[a:name] endfunction let s:format.add=s:_f.wrapfunc({'function': s:format.add, \'@FWC':['_ _ '. \ '(#exists not key formats) '. \ '(haskey line '. \ 'dict {?in keylist type string '. \ 'tagproc isfunc 1 '. \ 'strlen isfunc 1 '. \ 'addoptsfun isfunc 1 '. \ 'strescape type string '. \ 'haslf bool '. \ 'nolf bool '. \ 'addopts dict {/^[a-zA-Z0-9]\w*$/ _}'. \ 'state dict {/^[a-zA-Z0-9]\w*$/ _}'. \ 'sbsdstate dict {/^[a-zA-Z0-9]\w*$/ _}'. \ '})', 'check'], \}) "▶2 formatunload :: {f} → + fdict, s:formats function s:F.formatunload(plugdict, fdict) call map(keys(a:fdict), 's:F.delformat(v:val)') endfunction "▶2 Register feature call s:_f.newfeature('format', {'cons': s:format, \ 'unload': s:F.formatunload,}) unlet s:format "▶1 call frawor#Lockvar(s:, 'formats,progress,profiled,found_badly_spelled_word') " vim: ft=vim:ts=8:fdm=marker:fenc=utf-8:fmr=▶,▲:tw=100

heh, that's big plugin huh? It's reason why I only load it on demand.

Plug 'oblitum/frawor', {'on': []}
Plug 'oblitum/formatvim', {'on': []}
...
" :[range]Blocks                       | gist it to bl.ocks.org {{{
function! s:blocks() range abort
  call plug#load('frawor', 'formatvim')
  ...
endfunction

command! -range=% Blocks <line1>,<line2>call s:blocks()
" }}}

I think it's not due to plugin size, vim-fugitive/autoload/fugitive.vim is 5603 lines long for example, and node vimlparser.js --neovim vim-fugitive/autoload/fugitive.vim doesn't choke. I just applied the command to all vim files in my ~/.vim/plugged directory and vimlparser.js chokes on two files, the already mentioned 4k long format.vim, but also on this one:

https://github.com/echuraev/translate-shell.vim/blob/master/autoload/common/history.vim

Which is just 209LoC.

I've isolated the issue, this line alone cause the parser to choke:

write ++enc=utf-8

Just put it on a file and call the parser for it (node vimlparser.js ./foo.vim).

format.vim contains a variation:

new ++enc=utf-8

It's the only two plugins that have ++enc in their sources.

I have create pr to fix the issue #145