Commits

ZyX_I  committed b6a96de

Moved some common aurum://{cmd} handling code into @aurum/edit, moved other code into @aurum/{cmd}

  • Participants
  • Parent commits 4bf4f79

Comments (0)

Files changed (16)

File plugin/aurum.vim

                 \                '@/mappings': '0.0',
                 \                 '@/options': '0.0',
                 \                      '@/os': '0.1',
-                \            '@/autocommands': '0.0',
                 \           '@aurum/cmdutils': '0.0',
                 \                     '@/fwc': '0.2',
                 \           '@aurum/annotate': '0.0',
         call s:_f.command.add('Au'.s:cmd, s:{s:part}func, s:args)
     endfor
     unlet s:cmd s:addargs s:args s:part
-    "▶2 Autocommands
-    call FraworLoad('@/autocommands')
-    let s:auefunc={}
-    let s:aubwfunc={}
-    call s:_f.augroup.add('Aurum',[['BufReadCmd',  'aurum://*',0,[s:auefunc,0]],
-                \                  ['FileReadCmd', 'aurum://*',0,[s:auefunc,1]],
-                \                 ])
     "▶2 Global mappings
     call FraworLoad('@/mappings')
     " TODO mapping that closes status window
 endif
 "▶1 Globals
 let s:_messages={
-            \'nocread': 'Cannot read aurum://commit',
             \ 'invurl': 'Failed to parse url %s of repository %s',
             \ 'unkurl': 'Failed to process url %s of repository %s',
             \ 'uunsup': 'Url type `%s'' is not supported for repository %s '.
     endwhile
     return r
 endfunction
-"▶1 auefunc
-"▶2 repotuplesplit :: str, UInt → (repo, String, ...)
-function s:F.repotuplesplit(str, num)
-    let tail=a:str
-    let repopath=matchstr(tail, '\v^[^:]+')
-    let tail=tail[len(repopath)+1:]
-    let repo=s:_r.repo.get(s:_r.cmdutils.unescape(repopath))
-    if repo is 0
-        call s:_f.throw('nrepo', repopath)
-    endif
-    let r=[repo]
-    let i=0
-    while i<a:num-1
-        let chunk=matchstr(tail, '\v^[^:]+')
-        let tail=tail[len(chunk)+1:]
-        call add(r, s:_r.cmdutils.unescape(chunk))
-        let i+=1
-    endwhile
-    call add(r, s:_r.cmdutils.unescape(tail))
-    return r
-endfunction
-"▶2 repotupleoptssplit :: str, UInt → (repo, ..., opts::Dictionary)
-function s:F.repotupleoptssplit(str, num)
-    let [repo; strings]=s:F.repotuplesplit(a:str, a:num+1)
-    let ostr=remove(strings, -1)
-    let opts={}
-    for o in split(ostr, ',')
-        let colonidx=stridx(o, ':')
-        if colonidx==-1
-            continue
-        endif
-        let opts[o[:(colonidx-1)]]=s:_r.cmdutils.unescape(o[(colonidx+1):])
-    endfor
-    return [repo]+strings+[opts]
-endfunction
-"▲2
-function s:auefunc.function(read) abort
-    "▶2 Split buffer name into command and arguments
-    " XXX Cannot use <amatch> because it unescapes arguments on windows
-    " XXX On windows all forward slashes are transformed to backward
-    let tail=expand('<amatch>')[len('aurum://'):]
-    let command=tolower(matchstr(tail, '\v^\w+'))
-    let tail=tail[len(command)+1:]
-    "▲2
-    setlocal buftype=nofile readonly
-    let buf=bufnr('%')
-    if has_key(s:_r.bufvars, buf) &&
-                \has_key(s:_r.bufvars[buf], 'prevbuf')
-        let prevbuf=s:_r.bufvars[buf].prevbuf
-    endif
-    setlocal modifiable
-    " FIXME This code is creating repository object for the second time
-    "▶2 log command (repo:opts)
-    if command is# 'log'
-        let [repo, opts]=s:F.repotupleoptssplit(tail, 0)
-        if has_key(opts, 'files')
-            let opts.files=map(split(opts.files, ';'),
-                        \      's:_r.cmdutils.unescape(v:val)')
-            let opts.filepats=map(copy(opts.files),
-                        \         's:_r.cmdutils.globtopat(v:val)')
-        endif
-        if has_key(opts, 'revrange')
-            let opts.revrange=split(opts.revrange, ';')
-        endif
-        if has_key(opts, 'ignfiles')
-            let opts.ignfiles=split(opts.ignfiles, ';')
-        endif
-        for key in filter(['merges', 'patch', 'limit', 'stat', 'showfiles']+
-                    \     s:_r.repo.diffoptslst,
-                    \     'has_key(opts, v:val)')
-            let opts[key]=+opts[key]
-        endfor
-        let bvar={'repo': repo, 'opts': opts, 'read': a:read}
-        call s:_r.log.setup(bvar)
-        if !a:read
-            let s:_r.bufvars[buf]=bvar
-            setlocal filetype=aurumlog
-            runtime ftplugin/aurumlog.vim
-        endif
-    "▶2 file command (repo:rev:file)
-    elseif command is# 'file'
-        let [repo, rev, file]=s:F.repotuplesplit(tail, 2)
-        let rev=repo.functions.getrevhex(repo, rev)
-        call s:_r.cmdutils.setlines(repo.functions.readfile(repo, rev, file),
-                    \               a:read)
-        if !a:read
-            let s:_r.bufvars[buf]={'repo': repo, 'rev': rev, 'file': file}
-        endif
-        if exists('#filetypedetect#BufRead')
-            execute 'doautocmd filetypedetect BufRead'
-                        \ fnameescape(s:_r.os.path.normpath(
-                        \             s:_r.os.path.join(repo.path, file)))
-        endif
-    "▶2 annotate command (repo:rev:file)
-    elseif command is# 'annotate'
-        let [repo, rev, file]=s:F.repotuplesplit(tail, 2)
-        let bvar={'repo': repo, 'rev': rev, 'file': file, 'read': a:read}
-        call s:_r.annotate.setup(bvar)
-        if !a:read
-            let s:_r.bufvars[buf]=bvar
-            setlocal filetype=aurumannotate
-            runtime ftplugin/aurumannotate.vim
-        endif
-    "▶2 diff command (repo:rev1:rev2:files:opts)
-    elseif command is# 'diff'
-        let [repo, rev1, rev2, files, opts]=s:F.repotupleoptssplit(tail, 3)
-        "▶3 Get revisions
-        if empty(rev1) && empty(rev2)
-            let rev2=repo.work_hex
-        endif
-        if !empty(rev1)
-            let rev1=repo.functions.getrevhex(repo, rev1)
-        endif
-        if !empty(rev2)
-            let rev2=repo.functions.getrevhex(repo, rev2)
-        endif
-        "▲3
-        let filelist=map(split(files, ';'), 's:_r.cmdutils.unescape(v:val)')
-        " XXX All options should be from s:_r.repo.diffoptslst list
-        call map(opts, '+v:val')
-        if a:read
-            call s:_r.cmdutils.setlines(repo.functions.diff(repo, rev1, rev2,
-                        \                                   filelist, opts), 1)
-        else
-            let s:_r.bufvars[buf]={'repo': repo, 'rev1': rev1, 'rev2': rev2,
-                        \          'files': filelist, 'opts': opts,
-                        \          'originfiles': files}
-            call repo.functions.difftobuffer(repo, buf, rev1, rev2,
-                        \                    filelist, opts)
-            setlocal filetype=diff
-        endif
-    "▶2 status command (repo:opts)
-    elseif command is# 'status'
-        let [repo, opts]=s:F.repotupleoptssplit(tail, 0)
-        if has_key(opts, 'files')
-            let opts.files=map(split(opts.files, ';'),
-                        \      's:_r.cmdutils.unescape(v:val)')
-            let opts.filepats=map(copy(opts.files),
-                        \         's:_r.cmdutils.globtopat(v:val)')
-        endif
-        if has_key(opts, 'show')
-            let opts.show=split(opts.show, ';')
-        endif
-        for key in filter(['rev1', 'rev2'], 'has_key(opts, v:val)')
-            let opts[key]=repo.functions.getrevhex(repo, opts[key])
-        endfor
-        let bvar={'repo': repo, 'opts': opts, 'read': a:read}
-        call s:_r.status.setup(bvar)
-        if !a:read
-            let s:_r.bufvars[buf]=bvar
-            setlocal filetype=aurumstatus
-            runtime ftplugin/aurumstatus.vim
-        endif
-    "▶2 commit command  (repo:user:date:files)
-    elseif command is# 'commit'
-        if a:read
-            call s:_f.throw('nocread')
-        endif
-        let [repo, user, date, cb, files]=s:F.repotuplesplit(tail, 4)
-        let bvar={'repo': repo, 'user': user, 'date': date, 'closebranch': !!cb,}
-        let bvar.files=map(split(files, ';'), 's:_r.cmdutils.unescape(v:val)')
-        let s:_r.bufvars[buf]=bvar
-        setlocal modifiable noreadonly
-        augroup AuCommitMessage
-            autocmd BufWriteCmd <buffer> 
-                        \call s:_r.commit.finish(s:_r.bufvars[expand('<abuf>')])
-                        \ | call feedkeys("\<C-\>\<C-n>:bwipeout!\n")
-        augroup END
-        setlocal buftype=acwrite
-        setlocal filetype=aurumcommit
-        runtime ftplugin/aurumcommit.vim
-    "▶2 copy command (file)
-    elseif command is# 'copy'
-        let file=tail
-        call s:_r.cmdutils.setlines(readfile(file, 'b'), a:read)
-        if !a:read
-            let s:_r.bufvars[buf]={'file': file}
-        endif
-        if exists('#filetypedetect#BufRead')
-            execute 'doautocmd filetypedetect BufRead'
-                        \ fnameescape(s:_r.os.path.normpath(file))
-        endif
-    "▶2 Unknown command
-    else
-        return
-    endif
-    "▲2
-    if !a:read
-        let s:_r.bufvars[buf].command=command
-        if exists('prevbuf')
-            let s:_r.bufvars[buf].prevbuf=prevbuf
-        endif
-    endif
-    file
-endfunction
-let s:_augroups+=['AuCommitMessage']
 "▶1 updfunc
 function s:updfunc.function(bang, rev, repopath)
     let repo=s:_r.repo.get(a:repopath)

File plugin/aurum/annotate.vim

     return cs
 endfunction
 "▲2
-function s:F.setup(bvar)
-    let bvar=a:bvar
-    let ann=copy(bvar.repo.functions.annotate(bvar.repo, bvar.rev, bvar.file))
-    let d={'repo': bvar.repo, 'getcs': s:F.getcs}
+function s:F.setup(read, repo, rev, file)
+    let rev=a:repo.functions.getrevhex(a:repo, a:rev)
+    let bvar={'rev': rev, 'file': a:file}
+    let ann=copy(a:repo.functions.annotate(a:repo, rev, a:file))
+    let d={'repo': a:repo, 'getcs': s:F.getcs}
     let css=map(copy(ann), 'd.getcs(v:val[1])')
     let d={}
     let nl=max(map(copy(css), 'len(v:val.rev)'))
     let bvar.files=map(copy(ann), 'v:val[0]')
     let bvar.linenumbers=map(copy(ann), 'v:val[2]')
     let bvar.revisions=map(copy(css), 'v:val.hex')
-    let lines=map(copy(css), 'call(s:F.formatann, [bvar.repo, v:val, v:key, '.
+    let lines=map(copy(css), 'call(s:F.formatann, [a:repo, v:val, v:key, '.
                 \                                  nl.'],d)')
-    if bvar.read
+    if a:read
         call append('.', lines)
     else
         call setline('.', lines)
             autocmd InsertEnter <buffer> :call feedkeys("\e", 'n')
         augroup END
     endif
+    return bvar
 endfunction
 let s:_augroups+=['AuAnnotateNoInsert']
 "▶1 setannbuf
             \'\vfile\s+type\s*\V""', 'file path',          ''),
             \'\vrev\s+type\s*\V""',  'rev '.s:_r.comp.rev, ''))
 "▶1 post resource
-call s:_f.postresource('annotate', {'setup': s:F.setup,
-            \                   'setannbuf': s:F.setannbuf})
+let s:annotate={'function': s:F.setup,
+            \  'arguments': 2,
+            \   'filetype': 'aurumannotate',}
+" XXX
+let s:annotate.setannbuf=s:F.setannbuf
+call s:_f.postresource('annotate', s:annotate)
 "▶1
 call frawor#Lockvar(s:, '_r,_pluginloaded')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File plugin/aurum/cmdutils.vim

 if !exists('s:_pluginloaded')
     execute frawor#Setup('0.0', {'@/resources': '0.0',
                 \                       '@/os': '0.0',
-                \                  '@/options': '0.0',
                 \                '@aurum/repo': '0.0',
                 \               '@aurum/cache': '0.0',
                 \             '@aurum/bufvars': '0.0',}, 0)

File plugin/aurum/commit.vim

                 \                  '@/options': '0.0',
                 \              '@aurum/status': '0.0',
                 \            '@aurum/cmdutils': '0.0',
+                \             '@aurum/bufvars': '0.0',
                 \                      '@/fwc': '0.3',
                 \                '@aurum/repo': '0.0',
                 \                 '@/commands': '0.0',
 let s:_messages={
             \'emptmsg': 'Message must contain at least one non-blank character',
             \'nocfile': 'Unsure what should be commited',
+            \'nocread': 'Cannot read aurum://commit',
         \}
 let s:_options={
             \'remembermsg': {'default': 1, 'filter': 'bool'},
             \'\V|*_r.repo.get',  '',         ''),
             \'\V+ type ""',      '+ (path)', ''))
 "▶1 Post resource
-call s:_f.postresource('commit', {'commit': s:F.commit,
-            \                     'finish': s:F.finish,})
+let s:commit={'arguments': 3,
+            \  'listargs': 1,
+            \'modifiable': 1,
+            \  'filetype': 'aurumcommit',
+            \}
+" XXX
+let s:commit.commit=s:F.commit
+let s:commit.finish=s:F.finish
+function s:commit.function(read, repo, user, date, cb, files)
+    if a:read
+        call s:_f.throw('nocread')
+    endif
+    augroup AuCommitMessage
+        autocmd BufWriteCmd <buffer>
+                    \  call s:F.finish(s:_r.bufvars[expand('<abuf>')])
+                    \| call feedkeys("\<C-\>\<C-n>:bwipeout!\n")
+    augroup END
+    setlocal buftype=acwrite
+    return {'user': a:user, 'date': a:date, 'files': a:files,
+                \'closebranch': !!a:cb}
+endfunction
+call s:_f.postresource('commit', s:commit)
 "▶1
 call frawor#Lockvar(s:, '_r,_pluginloaded')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File plugin/aurum/diff.vim

                 \                    '@aurum/repo': '0.0',
                 \                           '@/os': '0.0',
                 \                          '@/fwc': '0.0',
+                \                    '@/resources': '0.0',
                 \                     '@/mappings': '0.0',
                 \                     '@/commands': '0.0',
                 \                    '@/functions': '0.0',}, 0)
             let rev2=bvar.repo.functions.getnthparent(bvar.repo,bvar.rev2,c).hex
         endif
         let epath=s:_r.cmdutils.escape(bvar.repo.path)
+        let filesstr=join(map(copy(bvar.files), 's:_r.cmdutils.escape(v:val)'),
+                    \     ';')
         let cmd.=':edit '.
                     \fnameescape('aurum://diff:'.epath.':'.rev1.':'.rev2.':'.
-                    \                s:_r.cmdutils.escape(bvar.originfiles).':'.
+                    \                filesstr.':'.
                     \                s:_r.cmdutils.encodeopts(bvar.opts))."\n"
         let cmd.=s:mmgroup
         let cmd.=":bwipeout ".buf."\n"
             \  'Open': {'lhs': 'o', 'rhs': ['open'    ]},
             \ 'Vdiff': {'lhs': 'D', 'rhs': ['vimdiff' ]},
         \}, {'func': s:F.rundiffmap, 'silent': 1, 'mode': 'n', 'dontmap': 1,})
+"▶1 diff resource
+let s:diff= {'arguments': 2,
+            \ 'listargs': 1,
+            \  'options': {'num': s:_r.repo.diffoptslst},
+            \ 'filetype': 'diff',
+            \}
+function s:diff.function(read, repo, rev1, rev2, files, opts)
+    "▶2 Get revisions
+    let rev1=a:rev1
+    let rev2=a:rev2
+    if !empty(rev1)
+        let rev1=a:repo.functions.getrevhex(a:repo, rev1)
+    endif
+    if !empty(rev2)
+        let rev2=a:repo.functions.getrevhex(a:repo, rev2)
+    endif
+    if empty(rev1) && empty(rev2)
+        let rev2=a:repo.work_hex
+    endif
+    "▲2
+    if a:read
+        call s:_r.cmdutils.setlines(a:repo.functions.diff(a:repo, rev1, rev2,
+                    \                                     a:files, a:opts), 1)
+        return {}
+    else
+        call a:repo.functions.difftobuffer(a:repo, bufnr('%'), rev1, rev2,
+                    \                      a:files, a:opts)
+        return {'rev1': rev1, 'rev2': rev2, 'files': a:files}
+    endif
+endfunction
+call s:_f.postresource('diff', s:diff)
 "▶1
 call frawor#Lockvar(s:, '_r,_pluginloaded')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File plugin/aurum/drivers/mercurial.vim

 "       http://translit3.hg.sourceforge.net:8000/hgroot/translit3/translit3
 "g git://vimpluginloader.git.sourceforge.net/gitroot/vimpluginloader/vam-test-repository
 "       / git+ssh://zyxsf@vimpluginloader.git.sourceforge.net/gitroot/vimpluginloader/vam-test-repository
+"t svn+https://vimpluginloader.svn.sourceforge.net/svnroot/vimpluginloader
 "  https://vim-pyinteractive-plugin.googlecode.com/hg/
 "t svn+http://conque.googlecode.com/svn/trunk
 "? (unable to clone with hg-git) https://code.google.com/p/tortoisegit/
 \    'line': '"l".line',
 \   'clone': '"http://".domain.":8000".path',
 \    'push': '"ssh://".user."@".domain.path',}],
+\['domain =~? "\\Vsvn.sourceforge.net\\$"',
+\ {  'file': '"http://".domain."/viewvc".path[8:]."/".file."?view=log"',
+\     'raw': '"http://".domain."/viewvc".path[8:]."/".file',
+\     'log': '"http://".domain."/viewvc".path[8:]."?view=log"',
+\   'clone': 'url',}],
 \['domain =~? "\\Vgooglecode.com\\$" && path[:2] is? "/hg"',
 \ {  'file': '"http://code.google.com/p/".matchstr(domain, "\\v^[^.]+")."/source/browse/".file."?r=".cs.hex',
 \     'log': '"http://code.google.com/p/".matchstr(domain, "\\v^[^.]+")."/source/list"',
 \    'push': 'url',}],
 \['domain =~? "\\Vgooglecode.com\\$" && path[:3] is? "/svn"',
 \ {  'file': '"http://code.google.com/p/".matchstr(domain, "\\v^[^.]+")."/source/browse".path[4:]."/".file',
+\     'raw': '"http://".domain.path."/".file',
 \     'log': '"http://code.google.com/p/".matchstr(domain, "\\v^[^.]+")."/source/list"',
 \    'line': 'line',
-\   'clone': '"http://".domain."/svn/trunk"',}],
+\   'clone': 'url',}],
 \['domain is? "code.google.com"',
 \ {  'file': '"http://code.google.com/".substitute(path, "/$", "", "")."/source/browse/".file."?r=".gitbranch',}],
 \['domain is? "anonscm.debian.org" && path[:2] is? "/hg"',

File plugin/aurum/edit.vim

+"▶1
+scriptencoding utf-8
+if !exists('s:_pluginloaded')
+    execute frawor#Setup('0.0', {'@/autocommands': '0.0',
+                \                   '@/functions': '0.0',
+                \                   '@aurum/repo': '0.0',
+                \                    '@aurum/log': '0.0',
+                \                   '@aurum/file': '0.0',
+                \                   '@aurum/diff': '0.0',
+                \                 '@aurum/status': '0.0',
+                \                 '@aurum/commit': '0.0',
+                \               '@aurum/annotate': '0.0',
+                \               '@aurum/cmdutils': '0.0',
+                \                '@aurum/bufvars': '0.0',}, 0)
+    call FraworLoad('@/autocommands')
+    call FraworLoad('@/functions')
+    let s:auefunc={}
+    call s:_f.augroup.add('Aurum',[['BufReadCmd',  'aurum://*',0,[s:auefunc,0]],
+                \                  ['FileReadCmd', 'aurum://*',0,[s:auefunc,1]],
+                \                 ])
+    finish
+elseif s:_pluginloaded
+    finish
+endif
+let s:commands={}
+let s:_messages={
+            \'ucmd': 'Unknown command: %s',
+        \}
+"▶1 commands
+for s:cmd in ['log', 'file', 'annotate', 'status', 'diff', 'commit']
+    let s:commands[s:cmd]=s:_r[s:cmd]
+endfor
+unlet s:cmd
+"▶1 copy
+function s:F.copy(read, file)
+    call s:_r.cmdutils.setlines(readfile(a:file, 'b'), a:read)
+    if !a:read
+        let s:_r.bufvars[bufnr('%')]={'file': a:file, 'command': 'copy'}
+    endif
+    if exists('#filetypedetect#BufRead')
+        execute 'doautocmd filetypedetect BufRead' fnameescape(a:file)
+    endif
+endfunction
+"▶1 repotuplesplit :: str, UInt → (repo, String, ...)
+function s:F.repotuplesplit(str, num)
+    let tail=a:str
+    let repopath=matchstr(tail, '\v^[^:]+')
+    let tail=tail[len(repopath)+1:]
+    let repo=s:_r.repo.get(s:_r.cmdutils.unescape(repopath))
+    if repo is 0
+        call s:_f.throw('nrepo', repopath)
+    endif
+    let r=[repo]
+    let i=0
+    while i<a:num-1
+        let chunk=matchstr(tail, '\v^[^:]+')
+        let tail=tail[len(chunk)+1:]
+        call add(r, chunk)
+        let i+=1
+    endwhile
+    call add(r, tail)
+    return r
+endfunction
+"▶1 repotupleoptssplit :: str, UInt → (repo, ..., opts::Dictionary)
+function s:F.repotupleoptssplit(str, num)
+    let [repo; strings]=s:F.repotuplesplit(a:str, a:num+1)
+    let ostr=remove(strings, -1)
+    let opts={}
+    for o in split(ostr, ',')
+        let colonidx=stridx(o, ':')
+        if colonidx==-1
+            continue
+        endif
+        let opts[o[:(colonidx-1)]]=o[(colonidx+1):]
+    endfor
+    return [repo]+strings+[opts]
+endfunction
+"▶1 auefunc
+let s:okeys={
+            \'list': 'map(split(opts[o],";"), "s:_r.cmdutils.unescape(v:val)")',
+            \'bool': '!!(+opts[o])',
+            \ 'num': '+opts[o]',
+            \ 'str': 's:_r.cmdutils.unescape(opts[o])',
+        \}
+" FIXME This code is creating repository object for the second time
+function s:auefunc.function(read)
+    " XXX On windows all forward slashes are transformed to backward in @%,
+    "     all backward are transformed to forward in <amatch>
+    let tail=expand('<amatch>')[len('aurum://'):]
+    let command=tolower(matchstr(tail, '\v^\w+'))
+    let tail=tail[len(command)+1:]
+    if command is# 'copy'
+        return s:F.copy(a:read, tail)
+    endif
+    if !has_key(s:commands, command)
+        call s:_f.throw('ucmd', command)
+    endif
+    let cdescr=s:commands[command]
+    let arguments=get(cdescr, 'arguments', 0)
+    let argnum=arguments+get(cdescr, 'listargs', 0)
+    let hasopts=has_key(cdescr, 'options')
+    let fname='repotuple'.((hasopts)?('opts'):('')).'split'
+    let args=s:F[fname](tail, argnum)
+    for i in range(1, arguments)
+        let args[i]=s:_r.cmdutils.unescape(args[i])
+    endfor
+    if argnum>arguments
+        for i in range(arguments+1, argnum)
+            let args[i]=map(split(args[i],';'), 's:_r.cmdutils.unescape(v:val)')
+        endfor
+    endif
+    if hasopts
+        let opts=remove(args, -1)
+        let newopts={}
+        for [key, expr] in items(s:okeys)
+            for o in filter(copy(get(cdescr.options, key, [])),
+                        \   'has_key(opts, v:val)')
+                let newopts[o]=eval(expr)
+            endfor
+        endfor
+        for o in filter(copy(get(cdescr.options, 'pats', [])),
+                    \   'has_key(newopts, v:val)')
+            let newopts[o[:-2].'pats']=map(newopts[o],
+                        \                  's:_r.cmdutils.globtopat(v:val)')
+        endfor
+        call add(args, newopts)
+    endif
+    if !a:read
+        setlocal modifiable noreadonly
+    endif
+    let bvar=call(cdescr.function, [a:read]+args, {})
+    if !a:read
+        let buf=bufnr('%')
+        if has_key(s:_r.bufvars, buf) &&
+                    \has_key(s:_r.bufvars[buf], 'prevbuf')
+            let bvar.prevbuf=s:_r.bufvars[buf].prevbuf
+        endif
+        if !get(cdescr, 'modifiable', 0)
+            setlocal buftype=nofile nomodifiable readonly
+        endif
+        let bvar.command=command
+        let bvar.repo=args[0]
+        if hasopts
+            let bvar.opts=args[-1]
+        endif
+        let s:_r.bufvars[buf]=bvar
+        if has_key(cdescr, 'filetype')
+            let &l:filetype=cdescr.filetype
+            execute 'runtime! ftplugin/'.cdescr.filetype.'.vim'
+        endif
+        file
+    endif
+endfunction
+"▶1
+call frawor#Lockvar(s:, '_pluginloaded,_r')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File plugin/aurum/file.vim

                 \                    '@aurum/repo': '0.0',
                 \                           '@/os': '0.0',
                 \                          '@/fwc': '0.0',
+                \                    '@/resources': '0.0',
                 \                     '@/mappings': '0.0',
                 \                     '@/commands': '0.0',
                 \                    '@/functions': '0.0',}, 0)
             \'\V:=(0)\s\+either (\[^)]\+)', 'path',                         ''),
             \'\Vcmd\s\+type ""',            'cmd '.s:_r.comp.cmd,           ''),
             \'\V:"."\s\+type ""', 'either ((type ""), '.s:_r.comp.rev.')',  ''))
+"▶1 file resource
+let s:file= {'arguments': 2,}
+function s:file.function(read, repo, rev, file)
+    let rev=a:repo.functions.getrevhex(a:repo, a:rev)
+    call s:_r.cmdutils.setlines(a:repo.functions.readfile(a:repo, rev, a:file),
+                \               a:read)
+    if exists('#filetypedetect#BufRead')
+        execute 'doautocmd filetypedetect BufRead'
+                    \ fnameescape(s:_r.os.path.normpath(
+                    \             s:_r.os.path.join(a:repo.path, a:file)))
+    endif
+    return {'rev': rev, 'file': a:file}
+endfunction
+call s:_f.postresource('file', s:file)
 "▶1 aurum://file mappings
 let s:mmgroup=':call <SNR>'.s:_sid.'_Eval("s:_f.mapgroup.map(''AuFile'', '.
             \                                               "bufnr('%'))\")\n"

File plugin/aurum/log.vim

     endif
 endfunction
 "▲2
-function s:F.setup(bvar)
-    let bvar=a:bvar
-    if has_key(bvar, 'createdlog')
-        return
-    endif
-    let bvar.createdlog=1
-    call bvar.repo.functions.getchangesets(bvar.repo)
+function s:F.setup(read, repo, opts)
+    let opts=a:opts
+    let bvar={}
+    call a:repo.functions.getchangesets(a:repo)
     "▶2 Add `ignorefiles'
-    let ignorefiles=(has_key(bvar.opts, 'ignfiles')?
-                \               (bvar.opts.ignfiles):
+    let ignorefiles=(has_key(opts, 'ignfiles')?
+                \               (opts.ignfiles):
                 \               (s:_f.getoption('ignorefiles')))
-    let bvar.opts.ignorefiles={}
-    call map(copy(ignorefiles), 'extend(bvar.opts.ignorefiles, {v:val : 1})')
+    let opts.ignorefiles={}
+    call map(copy(ignorefiles), 'extend(opts.ignorefiles, {v:val : 1})')
     unlet ignorefiles
     "▶2 Get revision range
-    if has_key(bvar.opts, 'revrange')
-        let bvar.opts.revs=map(copy(bvar.opts.revrange),
-                    \'bvar.repo.changesets['.
-                    \   'bvar.repo.functions.getrevhex(bvar.repo, v:val)].rev')
-    elseif get(bvar.opts, 'limit', 0)>0
-        let bvar.opts.revs=[bvar.repo.csnum-bvar.opts.limit-1,
-                    \       bvar.repo.csnum-2]
+    if has_key(opts, 'revrange')
+        let opts.revs=map(copy(opts.revrange),
+                    \'a:repo.changesets['.
+                    \   'a:repo.functions.getrevhex(a:repo, v:val)].rev')
+    elseif get(opts, 'limit', 0)>0
+        let opts.revs=[a:repo.csnum-opts.limit-1,
+                    \       a:repo.csnum-2]
     else
-        let bvar.opts.revs=[0, bvar.repo.csnum-2]
+        let opts.revs=[0, a:repo.csnum-2]
     endif
     "▶2 Process `revision' option
-    if has_key(bvar.opts, 'revision')
-        let hex=bvar.repo.functions.getrevhex(bvar.repo, bvar.opts.revision)
-        let cs=bvar.repo.changesets[hex]
-        if cs.rev<bvar.opts.revs[1]
-            let bvar.opts.revs[1]=cs.rev
+    if has_key(opts, 'revision')
+        let hex=a:repo.functions.getrevhex(a:repo, opts.revision)
+        let cs=a:repo.changesets[hex]
+        if cs.rev<opts.revs[1]
+            let opts.revs[1]=cs.rev
         endif
-        let bvar.opts.revisions={}
+        let opts.revisions={}
         let addrevs=[cs]
         while !empty(addrevs)
             let cs=remove(addrevs, 0)
-            if has_key(bvar.opts.revisions, cs.hex)
+            if has_key(opts.revisions, cs.hex)
                 continue
             endif
-            let bvar.opts.revisions[cs.hex]=1
-            let addrevs+=map(copy(cs.parents), 'bvar.repo.changesets[v:val]')
+            let opts.revisions[cs.hex]=1
+            let addrevs+=map(copy(cs.parents), 'a:repo.changesets[v:val]')
         endwhile
     endif
     "▲2
-    let cslist=bvar.repo.cslist[bvar.opts.revs[0]:bvar.opts.revs[1]]
+    let cslist=a:repo.cslist[opts.revs[0]:opts.revs[1]]
     "▶2 Generate cs.{kw} for various options (`show{kw}'+`files')
     for key in ['renames', 'copies']
-        if get(bvar.opts, 'show'.key, 0) || has_key(bvar.opts, 'files')
+        if get(opts, 'show'.key, 0) || has_key(opts, 'files')
             for cs in cslist
-                call bvar.repo.functions.getcsprop(bvar.repo, cs, key)
+                call a:repo.functions.getcsprop(a:repo, cs, key)
             endfor
         endif
     endfor
     "▶2 Generate cs.files for several options
-    if has_key(bvar.opts, 'files') || get(bvar.opts, 'showrenames', 0) ||
-                \                     get(bvar.opts, 'showcopies',  0) ||
-                \                     get(bvar.opts, 'showfiles',   0) ||
-                \                     get(bvar.opts, 'stat',        0)
+    if has_key(opts, 'files') || get(opts, 'showrenames', 0) ||
+                \                     get(opts, 'showcopies',  0) ||
+                \                     get(opts, 'showfiles',   0) ||
+                \                     get(opts, 'stat',        0)
         for cs in cslist
-            call bvar.repo.functions.getcsprop(bvar.repo, cs, 'files')
+            call a:repo.functions.getcsprop(a:repo, cs, 'files')
         endfor
     endif
     "▶2 Generate cs.changes for showfiles option
-    if get(bvar.opts, 'showfiles', 0)
+    if get(opts, 'showfiles', 0)
         for cs in cslist
-            call bvar.repo.functions.getcsprop(bvar.repo, cs, 'changes')
+            call a:repo.functions.getcsprop(a:repo, cs, 'changes')
         endfor
     endif
     "▶2 Generate file lists for `files' option
-    if has_key(bvar.opts, 'files')
-        let bvar.opts.csfiles={}
+    if has_key(opts, 'files')
+        let opts.csfiles={}
         for cs in cslist
-            let changes=bvar.repo.functions.getcsprop(bvar.repo,cs, 'changes')
+            let changes=a:repo.functions.getcsprop(a:repo,cs, 'changes')
             let changes=copy(changes)
             let csfiles=[]
-            let bvar.opts.csfiles[cs.hex]=csfiles
-            for pattern in bvar.opts.filepats
+            let opts.csfiles[cs.hex]=csfiles
+            for pattern in opts.filepats
                 let newfiles=filter(copy(changes), 'v:val=~#pattern')
                 call filter(changes, 'index(newfiles, v:val)==-1')
                 let csfiles+=newfiles
                     break
                 endif
             endfor
-            call map(copy(csfiles), 's:F.trackfile(bvar.repo, cs, v:val, '.
-                        \                         'bvar.opts.csfiles)')
+            call map(copy(csfiles), 's:F.trackfile(a:repo, cs, v:val, '.
+                        \                         'opts.csfiles)')
         endfor
-        let bvar.opts.totrack={}
+        let opts.totrack={}
     endif
     "▶2 Narrow changeset range
-    let bvar.opts.skipchangesets={}
+    let opts.skipchangesets={}
     let firstnoskip=-1
     let foundfirst=0
     let lastnoskip=-1
-    let i=bvar.opts.revs[0]
+    let i=opts.revs[0]
     for cs in cslist
         let skip=0
         "▶3 `branch', `merges', `search', `user', `revision'
-        if (has_key(bvar.opts, 'branch') && cs.branch isnot# bvar.opts.branch)||
-                    \(has_key(bvar.opts, 'merges') &&
-                    \   ((bvar.opts.merges)?(len(cs.parents)<=1):
+        if (has_key(opts, 'branch') && cs.branch isnot# opts.branch)||
+                    \(has_key(opts, 'merges') &&
+                    \   ((opts.merges)?(len(cs.parents)<=1):
                     \                    (len(cs.parents)>1))) ||
-                    \(has_key(bvar.opts, 'search') &&
-                    \   cs.description!~#bvar.opts.search) ||
-                    \(has_key(bvar.opts, 'user') && cs.user!~#bvar.opts.user) ||
-                    \(has_key(bvar.opts, 'revision') &&
-                    \   !has_key(bvar.opts.revisions, cs.hex))
+                    \(has_key(opts, 'search') &&
+                    \   cs.description!~#opts.search) ||
+                    \(has_key(opts, 'user') && cs.user!~#opts.user) ||
+                    \(has_key(opts, 'revision') &&
+                    \   !has_key(opts.revisions, cs.hex))
             let skip=1
         "▶3 `date'
-        elseif has_key(bvar.opts, 'date')
-            if match(bvar.opts.date, '\V<=\?>')!=-1
-                let [date1, date2]=split(bvar.opts.date, '\V<=\?>')
-                let acceptexact=(stridx(bvar.opts.date, '<=>', len(date1))!=-1)
+        elseif has_key(opts, 'date')
+            if match(opts.date, '\V<=\?>')!=-1
+                let [date1, date2]=split(opts.date, '\V<=\?>')
+                let acceptexact=(stridx(opts.date, '<=>', len(date1))!=-1)
                 let cmp1result=s:F.comparedates(date1, cs.time)
                 let cmp2result=s:F.comparedates(date2, cs.time)
                 if !((cmp1result==1 && cmp2result==-1) ||
                     let skip=1
                 endif
             else
-                let selector=bvar.opts.date[0]
-                let acceptexact=(stridx('<>', selector)==-1 || bvar.opts.date[1] is# '=')
-                let cmpresult=s:F.comparedates(bvar.opts.date, cs.time)
+                let selector=opts.date[0]
+                let acceptexact=(stridx('<>', selector)==-1 || opts.date[1] is# '=')
+                let cmpresult=s:F.comparedates(opts.date, cs.time)
                 if !((acceptexact && cmpresult==0) ||
                             \(selector is# '<' && cmpresult==-1) ||
                             \(selector is# '>' && cmpresult==1))
             endif
         endif
         "▶3 `files'
-        if !skip && has_key(bvar.opts, 'files')
-            let files=bvar.opts.csfiles[cs.hex]
+        if !skip && has_key(opts, 'files')
+            let files=opts.csfiles[cs.hex]
             if empty(files)
                 let skip=1
             endif
         "▲3
         if skip
             if foundfirst
-                let bvar.opts.skipchangesets[cs.hex]=1
+                let opts.skipchangesets[cs.hex]=1
             endif
         else
             if foundfirst
         let i+=1
     endfor
     if firstnoskip!=-1
-        let bvar.opts.revs[0]=firstnoskip
+        let opts.revs[0]=firstnoskip
     endif
     if lastnoskip!=-1
-        let bvar.opts.revs[1]=lastnoskip
+        let opts.revs[1]=lastnoskip
     endif
     "▶2 Get template
-    if has_key(bvar.opts, 'template')
-        let template=eval(bvar.opts.template)
-    elseif has_key(bvar.opts, 'style')
-        let template=s:templates[bvar.opts.style]
+    if has_key(opts, 'template')
+        let template=eval(opts.template)
+    elseif has_key(opts, 'style')
+        let template=s:templates[opts.style]
     else
         let template=s:templates.default
     endif
     let bvar.templatelist=s:F.temp.parse(template)
-    let bvar.opts.templatefunc=s:F.temp.compile(bvar.templatelist,
-                \                               bvar.opts)
+    let opts.templatefunc=s:F.temp.compile(bvar.templatelist,
+                \                               opts)
     "▲2
-    let cslist=bvar.repo.cslist[bvar.opts.revs[0]:bvar.opts.revs[1]]
-    let text=s:F.glog.graphlog(bvar.repo, bvar.opts)
+    let cslist=a:repo.cslist[opts.revs[0]:opts.revs[1]]
+    let text=s:F.glog.graphlog(a:repo, opts)
     let bvar.specials=text.specials
     let bvar.rectangles=text.rectangles
     let bvar.csstarts=text.csstarts
     let bvar.cw=s:_f.getoption('closewindow')
-    if !bvar.read
+    if !a:read
         setlocal noreadonly modifiable
     endif
-    call setline(1, text.text)
-    if !bvar.read
+    call s:_r.cmdutils.setlines(text.text, a:read)
+    if !a:read
         setlocal readonly nomodifiable buftype=nofile
         augroup AuLogNoInsert
             autocmd InsertEnter <buffer> :call feedkeys("\e", "n")
         augroup END
     endif
+    return bvar
 endfunction
 let s:_augroups+=['AuLogNoInsert']
 "▶1 syndef
             \                         'revrange '.s:_r.comp.rev.' '.
             \                                     s:_r.comp.rev,            ''))
 "▶1 Post resource
-call s:_f.postresource('log', {'setup': s:F.setup})
+let s:log = { 'function': s:F.setup,
+            \  'options': {'list': ['files', 'revrange', 'ignfiles'],
+            \              'bool': ['merges', 'patch', 'stat', 'showfiles',
+            \                       'showrenames', 'showcopies'],
+            \               'num': ['limit']+s:_r.repo.diffoptslst,
+            \               'str': ['date', 'search', 'user', 'branch',
+            \                       'revision', 'style', 'template',
+            \                       'crrestrict'],
+            \              'pats': ['files'],
+            \             },
+            \ 'filetype': 'aurumlog',
+            \}
+call s:_f.postresource('log', s:log)
 "▶1
 call frawor#Lockvar(s:, '_r,_pluginloaded,compilecache,parsecache,syncache')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File plugin/aurum/status.vim

     return r
 endfunction
 "▶1 setup
-function s:F.setup(bvar)
-    let bvar=a:bvar
-    let status=bvar.repo.functions.status(bvar.repo, get(bvar.opts, 'rev1', 0),
-                \                                    get(bvar.opts, 'rev2', 0))
+function s:F.setup(read, repo, opts)
+    let opts=a:opts
+    for key in filter(['rev1', 'rev2'], 'has_key(opts, v:val)')
+        let opts[key]=a:repo.functions.getrevhex(a:repo, opts[key])
+    endfor
+    let bvar={}
+    let status=a:repo.functions.status(a:repo, get(opts, 'rev1', 0),
+                \                              get(opts, 'rev2', 0))
     let bvar.status=status
     let bvar.types=[]
     let bvar.chars=[]
     let bvar.files=[]
-    if has_key(bvar.opts, 'show')
-        if index(bvar.opts.show, 'all')==-1
-            let show=s:F.parseshow(bvar.opts.show)
+    if has_key(opts, 'show')
+        if index(opts.show, 'all')==-1
+            let show=s:F.parseshow(opts.show)
         else
             let show=s:allshow
         endif
     else
         let show=s:defshow
     endif
-    let isrecord=get(bvar.opts, 'record', 0)
+    let isrecord=get(opts, 'record', 0)
     let statlines=[]
     for [type, files] in filter(sort(items(status)), 'index(show,v:val[0])!=-1')
         let char=has_key(s:statchars, type)? s:statchars[type]: toupper(type[0])
         for file in files
             let ignore=0
-            if has_key(bvar.opts, 'files')
+            if has_key(opts, 'files')
                 let ignore=1
-                for pattern in bvar.opts.filepats
+                for pattern in opts.filepats
                     if file=~#pattern
                         let ignore=0
                         break
     if empty(statlines)
         let statlines=['No changes found']
     endif
-    if bvar.read
+    if a:read
         call append('.', statlines)
     else
         call setline('.', statlines)
             autocmd InsertEnter <buffer> :call feedkeys("\e", 'n')
         augroup END
     endif
+    return bvar
 endfunction
 let s:_augroups+=['AuStatusNoInsert']
-call s:_f.postresource('status', {'setup': s:F.setup,
-            \                 'parseshow': s:F.parseshow,})
 "▶1 statfunc
 function s:statfunc.function(repopath, opts)
     if has_key(a:opts, 'files') && a:repopath is# ':'
             \'\vfiles\s+\([^)]*\)',       'files path',            ''),
             \'\Vcmd\s\+(type "")',        'cmd '.  s:_r.comp.cmd,  ''),
             \'\vrev([12])\s+\V(type "")', 'rev\1 '.s:_r.comp.rev,  'g'))
+"▶1 status resource
+let s:status={'function': s:F.setup,
+            \  'options': {'list': ['files', 'show'],
+            \              'bool': ['record'],
+            \               'str': ['rev1', 'rev2'],
+            \              'pats': ['files'],},
+            \     'repo': 1,
+            \ 'filetype': 'aurumstatus',
+            \}
+" XXX
+let s:status.parseshow=s:F.parseshow
+call s:_f.postresource('status', s:status)
 "▶1
 call frawor#Lockvar(s:, '_r,_pluginloaded')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File test/diff.in

-:let g:postcmd='g/\V\^\(+++\|---\)/s/\t.*//'
 :W{{{1
 :Run AuDiff
 :call WriteFile('w$: '.winnr('$'))

File test/file.in

 :call WriteFile(bufname('%'), 'w$: '.winnr('$'))
 :AuFile 6 cmd silent\ vsplit
 :call WriteFile(bufname('%'), 'w$: '.winnr('$').'; l$: '.line('$'))
-:set noreadonly
+:set modifiable noreadonly
 :AuFile 5 testrepo/chgrepo.zsh replace
 :call WriteFile(bufname('%'), 'l$: '.line('$'))
 :source addmessages.vim

File test/log-styles.ok

 |  :+)
 |  :+tar cJf testrepo.tar.xz testrepo
 |  :
-|  
+|  

File test/log-templates.ok

 o  
 |  
 o  
-|  
+|  
 o | | |    Changeset 9:269399222040415b3928a316f5d28792cc0be4dd
 |\ \ \ \   Commited 01 Jan 2001 00:00 by C <c@example.gov>
 | | | | |  @ Merge from C
-| | | | |  
+| | | | |  
     if !a:bang
         execute 'silent cd' fnameescape(reporoot)
     endif
-    execute a:cmd
     try
+        execute a:cmd
         if exists('g:postcmd')
             execute 'silent' g:postcmd
         endif