ZyX_I avatar ZyX_I committed 6a934bc Merge

Merge: moved most files to autoload/

Comments (0)

Files changed (66)

autoload/aurum.vim

 "▶1 
-execute frawor#Setup('0.1', {'@aurum/repo': '4.0',
-            \               '@aurum/cache': '1.0',})
+scriptencoding utf-8
+execute frawor#Setup('0.1', {'@%aurum/repo': '5.0',
+            \                '@aurum/cache': '1.0',
+            \            '@%aurum/cmdutils': '3.0',})
+"▶1 getcrf
+function s:F.getcrf()
+    let buf=bufnr('%')
+    if !has_key(s:_r.cache.cachebvars, buf)
+        let cbvar={}
+    endif
+    let cbvar=s:_r.cache.cachebvars[buf]
+    if has_key(cbvar, '__relfname') && has_key(cbvar, 'repo') &&
+                \localtime()-cbvar._timerepo<cbvar._maxtimerepo
+        return [cbvar, cbvar.repo, cbvar.__relfname]
+    endif
+    try
+        silent let [hasbuf, repo, rev, file]=s:_r.cmdutils.getrrf({'repo': ':'},
+                    \                                             0,"getsilent")
+    catch /^Frawor:[^:]\+:nrepo:/
+        return [cbvar, 0, 0]
+    endtry
+    if repo isnot 0 && file isnot 0
+        let cbvar.repo=repo
+        let cbvar._timerepo=localtime()
+        let cbvar._maxtimerepo=s:_f.getoption('repocachetime')
+        let cbvar.__relfname=file
+    endif
+    return [cbvar, repo, file]
+endfunction
 "▶1 aurum#repository
 function aurum#repository()
     let repo=s:_r.cache.get('repo', s:_r.repo.get, [':'], {})
 let s:_functions+=['aurum#changeset']
 "▶1 aurum#status
 function aurum#status(...)
-    let [cbvar, repo, file]=s:_r.cache.getcrf()
+    let [cbvar, repo, file]=s:F.getcrf()
     if repo is 0 || file is 0
         return ''
     endif

autoload/aurum/annotate.vim

+"▶1 
+scriptencoding utf-8
+execute frawor#Setup('1.0', {'@%aurum/cmdutils': '3.0',
+            \                 '@%aurum/bufvars': '0.0',
+            \                    '@%aurum/edit': '1.0',
+            \                          '@aurum': '1.0',
+            \                     '@/resources': '0.0',
+            \                         '@/table': '0.1',})
+"▶1 formatann :: repo, cs, lnum, numlen → String
+function s:F.formatann(repo, cs, lnum, numlen)
+    if !has_key(self, a:cs.hex)
+        let description=matchstr(a:cs.description, '\v[^\r\n]+')
+        while s:_r.strdisplaywidth(description, a:numlen+1)>30
+            let description=substitute(description, '.$', '', '')
+        endwhile
+        if len(description)<len(a:cs.description)
+            let description.='…'
+        endif
+        let descwidth=s:_r.strdisplaywidth(description, a:numlen+1)
+        if descwidth<31
+            let description.=repeat(' ', 31-descwidth)
+        endif
+        let user=substitute(a:cs.user, '\m\s*<[^>]\+>$', '', '')
+        let self[a:cs.hex]=printf('%*s %s / %s', a:numlen, a:cs.rev,
+                    \                            description, user)
+    endif
+    return self[a:cs.hex]
+endfunction
+"▶1 unload
+function s:F.unload(bvar)
+    if has_key(a:bvar, 'annbuf') && bufexists(a:bvar.annbuf)
+        let annwin=s:F.findwin(a:bvar.annbuf)
+        if annwin==-1
+            let annwin=bufwinnr(a:bvar.annbuf)
+        endif
+        if annwin!=-1
+            for [o, v] in items(a:bvar.saved)
+                call setwinvar(annwin, '&'.o, v)
+            endfor
+        endif
+    endif
+endfunction
+"▶1 setup
+"▶2 getcs :: rev + self → cs + self
+function s:F.getcs(rev)
+    if has_key(self, a:rev)
+        return self[a:rev]
+    endif
+    let cs=self.repo.functions.getcs(self.repo, a:rev)
+    let self[a:rev]=cs
+    return cs
+endfunction
+"▲2
+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 bvar.bwfunc=s:F.unload
+    let lines=map(copy(css), 'call(s:F.formatann, [a:repo, v:val, v:key, '.
+                \                                  nl.'],d)')
+    if a:read
+        call append('.', lines)
+    else
+        call setline('.', lines)
+        setlocal readonly nomodifiable
+    endif
+    return bvar
+endfunction
+"▶1 findwin :: buf → win
+function s:F.findwin(buf)
+    let win=winnr()
+    if win>1 && winbufnr(win-1)==a:buf
+        return win-1
+    elseif winbufnr(win+1)==a:buf
+        return win+1
+    else
+        return -1
+    endif
+endfunction
+"▶1 setannbuf
+function s:F.setannbuf(bvar, annbuf)
+    let a:bvar.annbuf=a:annbuf
+    let a:bvar.saved={}
+    let a:bvar.saved.scrollbind = &scrollbind
+    let a:bvar.saved.cursorbind = &cursorbind
+    let a:bvar.saved.wrap       = &wrap
+    setlocal scrollbind cursorbind nowrap
+    let buf=bufnr('%')
+    let annwin=s:F.findwin(a:annbuf)
+    if annwin==-1
+        return
+    endif
+    execute annwin.'wincmd w'
+    setlocal scrollbind cursorbind nowrap
+    augroup AuAnnotateBW
+        execute 'autocmd BufWipeOut,BufHidden <buffer='.a:annbuf.'> '.
+                    \':if bufexists('.buf.') | '.
+                    \   'call feedkeys("\<C-\>\<C-n>'.
+                    \                 ':silent! bw '.buf.'\n") | '.
+                    \ 'endif'
+    augroup END
+endfunction
+let s:_augroups+=['AuAnnotateBW']
+"▶1 foldopen
+if has('folding')
+    function s:F.foldopen()
+        if &foldenable
+            try
+                " XXX Using silent! here because I am unable to catch E490 for 
+                " unknown reason
+                silent! %foldopen!
+            catch /^Vim:(foldopen):E490:/
+                " No folds found — ignore
+            endtry
+        endif
+    endfunction
+else
+    function s:F.foldopen()
+        " Doing nothing if there is no folding support
+    endfunction
+endif
+"▶1 annfunc
+" TODO Investigate why wiping out annotate buffer causes consumption of next
+"      character under wine
+function s:cmd.function(opts)
+    let [hasannbuf, repo, rev, file]=s:_r.cmdutils.getrrf(a:opts, 'noafile',
+                \                                         'annotate')
+    if repo is 0
+        return
+    endif
+    if rev is 0
+        let rev=repo.functions.getworkhex(repo)
+    endif
+    if hasannbuf==2
+        let hasannbuf=!repo.functions.dirty(repo, file)
+    endif
+    if hasannbuf
+        let annbuf=bufnr('%')
+    else
+        " TODO Check for errors
+        let existed=s:_r.run('silent edit', 'file', repo, rev, file)
+        let annbuf=bufnr('%')
+        if !existed
+            setlocal bufhidden=wipe
+        endif
+    endif
+    let annwin=winnr()
+    call s:F.foldopen()
+    let lnr=line('.')
+    let anwidth=min([42, winwidth(0)/2-1])
+    call s:_r.run('silent leftabove '.anwidth.'vsplit', 'annotate', repo,
+                \ rev, file)
+    execute lnr
+    setlocal bufhidden=wipe
+    call s:F.setannbuf(s:_r.bufvars[bufnr('%')], annbuf)
+endfunction
+let s:_augroups+=['AuAnnotateBW']
+"▶1 aurum://annotate
+call s:_f.newcommand({'function': s:F.setup,
+            \        'arguments': 2,
+            \         'filetype': 'aurumannotate',})
+"▶1 Post resource
+call s:_f.postresource('annotate', {'setannbuf': s:F.setannbuf,
+            \                        'foldopen': s:F.foldopen,})
+"▶1
+call frawor#Lockvar(s:, '_r,_pluginloaded')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/branch.vim

+scriptencoding utf-8
+execute frawor#Setup('0.0', {'@aurum': '1.0',
+            \      '@%aurum/cmdutils': '3.0',})
+let s:_messages={
+            \ 'bexsts': 'Error while creating branch %s for repository %s: '.
+            \           'branch already exists',
+        \}
+function s:cmd.function(bang, branch, opts)
+    let repo=s:_r.cmdutils.checkedgetrepo(a:opts.repo)
+    let force=a:bang
+    if !force && index(repo.functions.getrepoprop(repo, 'brancheslist'),
+                \      a:branch)!=-1
+        call s:_f.throw('bexsts', a:branch, repo.path)
+    endif
+    call repo.functions.branch(repo, a:branch, force)
+endfunction
+"▶1
+call frawor#Lockvar(s:, '')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/bufvars.vim

+"▶1 
+scriptencoding utf-8
+execute frawor#Setup('0.0', {'@/resources': '0.0',})
+let s:bufvars={}
+"▶1 bufwipeout
+function s:F.bufwipeout()
+    let buf=+expand('<abuf>')
+    if has_key(s:bufvars, buf)
+        let bvar=s:bufvars[buf]
+        if !has_key(bvar, 'command')
+        elseif has_key(bvar, 'bwfunc')
+            let eval='<SNR>'.s:_sid.'_Eval'
+            call feedkeys("\<C-\>\<C-n>:call ".
+                    \      "call(".eval."('s:bufvars[".buf."].bwfunc'), ".
+                    \           "[".eval."('s:bufvars[".buf."]')], {}) | ".
+                    \"call ".eval."('remove(s:bufvars, ".buf.")')\n", 'n')
+            return
+        endif
+        unlet s:bufvars[buf]
+    endif
+endfunction
+"▶1 AurumBufVars augroup
+augroup AurumBufVars
+    autocmd BufWipeOut * :call s:F.bufwipeout()
+augroup END
+let s:_augroups+=['AurumBufVars']
+"▶1
+call s:_f.postresource('bufvars', s:bufvars, 1)
+"▶1
+call frawor#Lockvar(s:, 'bufvars')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/cmdutils.vim

+"▶1
+scriptencoding utf-8
+execute frawor#Setup('3.1', {'@/resources': '0.0',
+            \                       '@/os': '0.0',
+            \               '@%aurum/repo': '5.0',
+            \               '@%aurum/edit': '1.0',
+            \            '@%aurum/bufvars': '0.0',})
+let s:_messages={
+            \  'nrepo': 'Failed to find a repository',
+            \'noafile': 'Failed to deduce which file to annotate',
+            \'noffile': 'Failed to deduce which file to show',
+            \ 'nocurf': 'Failed to deduce which file was meant',
+            \'nocfile': 'Unsure what should be commited',
+        \}
+"▶1 globescape :: path → glob
+function s:F.globescape(path)
+    return escape(a:path, '\*?[]{}')
+endfunction
+"▶1 getdifffile :: bvar + cursor → file
+function s:F.getdifffile(bvar)
+    if len(a:bvar.files)==1
+        return a:bvar.files[0]
+    endif
+    let diffre=a:bvar.repo.functions.diffre(a:bvar.repo, a:bvar.opts)
+    let lnr=search(diffre, 'bcnW')
+    if !lnr
+        return 0
+    endif
+    return a:bvar.repo.functions.diffname(a:bvar.repo, getline(lnr), diffre,
+                \                         a:bvar.opts)
+endfunction
+"▶1 getfile :: [path] → path
+function s:F.getfile(files)
+    let file=0
+    if !empty(a:files)
+        if len(a:files)==1
+            let file=a:files[0]
+        else
+            let choice=inputlist(['Select file (0 to cancel):']+
+                        \               map(copy(a:files),
+                        \                   '(v:key+1).". ".v:val'))
+            if choice
+                let file=a:files[choice-1]
+            endif
+        endif
+    endif
+    return file
+endfunction
+"▶1 rrf buffer functions :: bvar, opts, act, failmsg → scope
+let s:rrf={}
+"▶2 rrf.file : bvar → (repo, rev, file)
+function s:rrf.file(bvar, opts, act, failmsg)
+    return {'hasbuf': 1,
+           \  'repo': a:bvar.repo,
+           \   'rev': a:bvar.rev,
+           \  'file': a:bvar.file,}
+endfunction
+"▶2 rrf.copy : bvar → (file), file → (repo), 0 → (rev)
+function s:rrf.copy(bvar, opts, act, failmsg)
+    let r={}
+    if a:act is# 'getfile'
+        let r.file=a:bvar.file
+    else
+        let r.repo=s:_r.repo.get(s:_r.os.path.dirname(a:bvar.file))
+        let r.file=r.repo.functions.reltorepo(r.repo, a:bvar.file)
+    endif
+    let r.rev=0
+    let r.hasbuf=1
+    return r
+endfunction
+"▶2 rrf.edit : same as copy
+let s:rrf.edit=s:rrf.copy
+"▶2 rrf.status : bvar → (repo, rev), . → (file)
+function s:rrf.status(bvar, opts, act, failmsg)
+    let r={}
+    let r.repo=a:bvar.repo
+    let  r.rev=get(a:bvar.opts, 'rev1', 0)
+    if empty(a:bvar.files)
+        if a:failmsg isnot 0
+            call s:_f.throw(a:failmsg)
+        endif
+    elseif a:act is# 'getfiles'
+        let r.files=a:bvar.files
+    else
+        let r.file=a:bvar.files[line('.')-1]
+    endif
+    if a:act is# 'annotate' || a:act is# 'open'
+        topleft new
+    endif
+    return r
+endfunction
+"▶2 rrf.diff : bvar → (repo, rev, file(s))
+function s:rrf.diff(bvar, opts, act, failmsg)
+    let r={}
+    let r.repo=a:bvar.repo
+    let  r.rev=empty(a:bvar.rev2) ? a:bvar.rev1 : a:bvar.rev2
+    " XXX Maybe it should pull in all filenames instead when act='getfiles'?
+    let r.file=s:F.getdifffile(a:bvar)
+    if r.file is 0 && a:failmsg isnot 0
+        return 0
+    endif
+    if a:act is# 'annotate' || a:act is# 'open'
+        leftabove vnew
+    endif
+    return r
+endfunction
+"▶2 rrf.commit : bvar → (repo, file(s))
+function s:rrf.commit(bvar, opts, act, failmsg)
+    let r={}
+    let r.repo=a:bvar.repo
+    if a:act is# 'getfiles'
+        let r.files=a:bvar.files
+    elseif a:act isnot# 'getsilent'
+        let r.file=s:F.getfile(a:bvar.files)
+        if r.file is 0 && a:failmsg isnot 0
+            return 0
+        endif
+        if a:act is# 'annotate' || a:act is# 'open'
+            topleft new
+        endif
+    endif
+    return r
+endfunction
+"▶2 rrf.annotate : bvar → (repo), . → (rev, file)
+function s:rrf.annotate(bvar, opts, act, failmsg)
+    let r={}
+    let r.repo=a:bvar.repo
+    if a:act is# 'getfiles'
+        let r.file=a:bvar.file
+    else
+        let r.file=a:bvar.files[line('.')-1]
+        if !has_key(a:opts, 'rev')
+            let r.rev=a:bvar.revisions[line('.')-1]
+            if r.rev is# r.repo.functions.getrevhex(r.repo, a:bvar.rev)
+                if a:act isnot# 'annotate'
+                    " Don't do the following if we are not annotating
+                elseif has_key(a:bvar, 'annbuf') &&
+                            \bufwinnr(a:bvar.annbuf)!=-1
+                    execute bufwinnr(a:bvar.annbuf).'wincmd w'
+                else
+                    setlocal scrollbind
+                    call s:_r.run('silent rightbelow vsplit',
+                                \ 'file', r.repo, r.rev, r.file)
+                    let a:bvar.annbuf=bufnr('%')
+                    setlocal scrollbind
+                endif
+                return 0
+            endif
+        endif
+        if a:act is# 'annotate' || a:act is# 'open'
+            if winnr('$')>1
+                close
+            endif
+            if has_key(a:bvar, 'annbuf') && bufwinnr(a:bvar.annbuf)!=-1
+                execute bufwinnr(a:bvar.annbuf).'wincmd w'
+            endif
+        endif
+    endif
+    return r
+endfunction
+"▶2 rrf.log : bvar → repo, . → (rev), 0 → (file)
+function s:rrf.log(bvar, opts, act, failmsg)
+    return {'repo': a:bvar.repo,
+           \ 'rev': a:bvar.getblock(a:bvar)[2],
+           \'file': 0,}
+endfunction
+"▲2
+"▶1 getrrf :: opts, failmsg, act + buf → (hasbuf, repo, rev, file)
+let s:rrffailresult=[0, 0, 0, 0]
+function s:F.getrrf(opts, failmsg, act)
+    let hasbuf=0
+    let file=0
+    "▶2 a:opts.file file → (repo?)
+    if has_key(a:opts, 'file') && a:opts.file isnot# ':'
+        if a:act isnot# 'getfile' && a:opts.repo is# ':'
+            let repo=s:_r.repo.get(s:_r.os.path.dirname(a:opts.file))
+            let file=repo.functions.reltorepo(repo, a:opts.file)
+        else
+            let file=a:opts.file
+        endif
+        if !has_key(a:opts, 'rev')
+            let rev=0
+        endif
+    "▶2 a:opts.files files → repo?
+    elseif has_key(a:opts, 'files') && !empty(a:opts.files)
+        let files=[]
+        if index(a:opts.files, ':')!=-1
+            let newopts=copy(a:opts)
+            unlet newopts.files
+            let [repo, rev, file]=s:F.getrrf(newopts, 'nocurf', 'getfile')[1:]
+            if repo is 0
+                unlet repo
+                let repo=s:_r.repo.get(file)
+            endif
+            if repo isnot 0
+                let file=repo.functions.reltorepo(repo, file)
+            endif
+            let files+=[file]
+        else
+            let repo=s:_r.repo.get(a:opts.files[0])
+        endif
+    "▶2 aurum:// buffers
+    elseif has_key(s:_r.bufvars, bufnr('%')) &&
+                \has_key(s:_r.bufvars[bufnr('%')], 'command')
+        let bvar=s:_r.bufvars[bufnr('%')]
+        if has_key(s:rrf, bvar.command)
+            let res=call(s:rrf[bvar.command], [bvar,a:opts,a:act,a:failmsg], {})
+            if res is 0
+                return s:rrffailresult
+            else
+                for [var, val] in items(res)
+                    let {var}=val
+                    unlet val
+                endfor
+            endif
+        elseif a:failmsg isnot 0
+            call s:_f.throw(a:failmsg)
+        endif
+    "▶2 buf → (repo, file), (rev=0)
+    elseif filereadable(expand('%'))
+        if a:act is# 'getfile'
+            let file=expand('%')
+        else
+            let repo=s:_r.repo.get(':')
+            call s:F.checkrepo(repo)
+            let file=repo.functions.reltorepo(repo, expand('%'))
+        endif
+        let  rev=0
+        let hasbuf=2
+    "▲2
+    elseif a:failmsg isnot 0
+        call s:_f.throw(a:failmsg)
+    endif
+    "▶2 Update repository if appropriate
+    if exists('repo') && exists('bvar.repo') && repo is bvar.repo
+        call repo.functions.updatechangesets(repo)
+    endif
+    "▲2
+    if a:act isnot# 'getfile'
+        "▶2 repo
+        if !exists('repo')
+            let repo=s:_r.repo.get(a:opts.repo)
+            call s:F.checkrepo(repo)
+            if file isnot 0
+                let file=repo.functions.reltorepo(repo, file)
+            endif
+        endif
+        "▶2 rev
+        if a:act is# 'getfiles'
+            let rev=0
+        elseif has_key(a:opts, 'rev')
+            let oldrev=0
+            if exists('rev') && rev isnot 0
+                let oldrev=repo.functions.getrevhex(repo, rev)
+            endif
+            let rev=repo.functions.getrevhex(repo, a:opts.rev)
+            if hasbuf && rev isnot# oldrev
+                let hasbuf=0
+            endif
+        elseif exists('rev')
+            if rev isnot 0
+                let rev=repo.functions.getrevhex(repo, rev)
+            endif
+        else
+            let rev=0
+        endif
+        "▲2
+    endif
+    return [hasbuf, exists('repo') ? repo : 0, rev,
+                \((a:act is# 'getfiles')?
+                \   ((exists('files'))?
+                \       (files):
+                \   ((file is 0)?
+                \       (0)
+                \   :
+                \       ([file]))):
+                \   (file))]
+endfunction
+"▶1 checkrepo
+function s:F.checkrepo(repo)
+    if type(a:repo)!=type({})
+        call s:_f.throw('nrepo')
+    endif
+    return 1
+endfunction
+"▶1 checkedgetrepo
+function s:F.checkedgetrepo(repopath)
+    let repo=s:_r.repo.get(a:repopath)
+    call s:F.checkrepo(repo)
+    return repo
+endfunction
+"▶1 closebuf :: bvar → + buf
+function s:F.closebuf(bvar)
+    let r=''
+    if has_key(a:bvar, 'prevbuf') && bufexists(a:bvar.prevbuf)
+        let r.=':buffer '.a:bvar.prevbuf."\n"
+    endif
+    let buf=bufnr('%')
+    return r.':if bufexists('.buf.')|bwipeout '.buf."|endif\n"
+endfunction
+"▶1 getexsttrckdfiles
+function s:F.getexsttrckdfiles(repo, ...)
+    let cs=a:repo.functions.getwork(a:repo)
+    let r=copy(a:repo.functions.getcsprop(a:repo, cs, 'allfiles'))
+    let status=a:repo.functions.status(a:repo)
+    call filter(r, 'index(status.removed, v:val)==-1 && '.
+                \  'index(status.deleted, v:val)==-1')
+    let r+=status.added
+    if a:0 && a:1
+        let r+=status.unknown
+    endif
+    return r
+endfunction
+"▶1 getaddedermvdfiles
+function s:F.getaddedermvdfiles(repo)
+    let status=a:repo.functions.status(a:repo)
+    return status.unknown+filter(copy(status.removed),
+                \         'filereadable(s:_r.os.path.join(a:repo.path, v:val))')
+endfunction
+"▶1 filterfiles
+function s:F.filterfiles(repo, globs, files)
+    if empty(a:globs)
+        return []
+    endif
+    let r=[]
+    let dirs={}
+    for file in a:files
+        let fsplit=split(file, '\V/')
+        if empty(fsplit)
+            continue
+        endif
+        let d=dirs
+        for component in fsplit[:-2]
+            let component.='/'
+            if !has_key(d, component)
+                let d[component]={}
+            endif
+            let d=d[component]
+        endfor
+        let d[fsplit[-1]]=1
+    endfor
+    unlet! d
+    let patterns=map(copy(a:globs), 's:_r.globtopat('.
+                \                   'a:repo.functions.reltorepo(a:repo,v:val))')
+    let tocheck=map(items(dirs), '[v:val[1], v:val[0]]')
+    while !empty(tocheck)
+        let [d, f]=remove(tocheck, 0)
+        if !empty(filter(copy(patterns), 'f=~#v:val'))
+            let r+=[f]
+        elseif f[-1:] is# '/'
+            let tocheck+=map(items(d), '[v:val[1], f.v:val[0]]')
+        endif
+        unlet d
+    endwhile
+    return r
+endfunction
+"▶1 update
+" TODO Investigate whether this function should be moved to cmdutils, or to 
+" maputils which is probably to be created
+function s:F.update(repo, rev, count)
+    let rev=a:rev
+    if a:count>1
+        let rev=a:repo.functions.getnthparent(a:repo, rev, a:count-1).hex
+    endif
+    return a:repo.functions.update(a:repo, rev, 0)
+endfunction
+"▶1 Post cmdutils resource
+call s:_f.postresource('cmdutils', {'globescape': s:F.globescape,
+            \                           'getrrf': s:F.getrrf,
+            \                      'getdifffile': s:F.getdifffile,
+            \                        'checkrepo': s:F.checkrepo,
+            \                   'checkedgetrepo': s:F.checkedgetrepo,
+            \                         'closebuf': s:F.closebuf,
+            \                'getexsttrckdfiles': s:F.getexsttrckdfiles,
+            \               'getaddedermvdfiles': s:F.getaddedermvdfiles,
+            \                      'filterfiles': s:F.filterfiles,
+            \                           'update': s:F.update,
+            \})
+"▶1
+call frawor#Lockvar(s:, '_pluginloaded,_r')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/commit.vim

+"▶1 
+scriptencoding utf-8
+execute frawor#Setup('1.0', {'@/resources': '0.0',
+            \                  '@/options': '0.0',
+            \                     '@aurum': '1.0',
+            \             '@%aurum/status': '1.0',
+            \           '@%aurum/cmdutils': '3.0',
+            \            '@%aurum/bufvars': '0.0',
+            \               '@%aurum/edit': '1.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',
+            \  'nocom': 'Nothing to commit',
+        \}
+let s:_options={
+            \'remembermsg':         {'default': 1, 'filter': 'bool'},
+            \'bufleaveremembermsg': {'default': 1, 'filter': 'bool'},
+        \}
+"▶1 parsedate string → [year, month, day, hour, minute, second]
+" Date must have one of the following formats (XXX it is not validated):
+" %Y-%m-%d %H:%M:%S
+" %Y-%m-%d %H:%M
+" %Y-%m-%d
+"    %m-%d %H:%M:%S
+"    %m-%d %H:%M
+"    %m-%d
+"          %H:%M:%S
+"          %H:%M
+function s:F.parsedate(str)
+    let parts=split(a:str)
+    if len(parts)==1
+        if stridx(parts[0], ':')==-1
+            let day=parts[0]
+            let time=0
+        else
+            let day=0
+            let time=parts[0]
+        endif
+    else
+        let [day, time]=parts
+    endif
+    let r=[]
+    if day is# 0
+        let r+=[0, 0, 0]
+    else
+        let parts=split(day, '-')
+        if len(parts)==2
+            let r+=[0]
+        else
+            let year=remove(parts, 0)
+            if len(year)<=2
+                let y=str2nr(year)
+                let cy=str2nr(strftime('%y'))
+                let c=str2nr(strftime('%Y')[:-3])
+                if y<=cy
+                    let year=''.((c*100)+y)
+                else
+                    let year=''.(((c-1)*100)+y)
+                endif
+            endif
+            let r+=[year]
+        endif
+        let r+=map(parts, 'len(v:val)==1 ? "0".v:val : v:val')
+    endif
+    if time is# 0
+        let r+=[0, 0, 0]
+    else
+        let parts=map(split(time, ':'), 'len(v:val)==1 ? "0".v:val : v:val')
+        let r+=parts
+        if len(parts)==2
+            let r+=[0]
+        endif
+    endif
+    return r
+endfunction
+"▶1 commit :: repo, opts, files, status, types → + repo
+let s:defdate=['strftime("%Y")',
+            \  'strftime("%m")',
+            \  'strftime("%d")',
+            \  '"00"',
+            \  '"00"',
+            \  '"00"']
+let s:statmsgs={
+            \'added': 'Added',
+            \'removed': 'Removed',
+            \'modified': 'Modified',
+        \}
+let s:statmsgs.unknown=s:statmsgs.added
+let s:statmsgs.deleted=s:statmsgs.removed
+" TODO Investigate why closing commit buffer on windows consumes next character
+" XXX Do not change names of options used here, see :AuRecord
+function s:F.commit(repo, opts, files, status, types)
+    let user=''
+    let date=''
+    let message=''
+    let cb=get(a:opts, 'closebranch', 0)
+    let revstatus={}
+    call map(filter(copy(a:status), 'index(a:types, v:key)!=-1'),
+                \'map(copy(v:val),"extend(revstatus,{v:val : ''".v:key."''})")')
+    if !empty(a:files)
+        call filter(revstatus, 'index(a:files, v:key)!=-1')
+    endif
+    for key in filter(['user', 'date', 'message'], 'has_key(a:opts, v:val)')
+        let l:{key}=a:opts[key]
+    endfor
+    "▶2 Normalize date
+    if has_key(a:opts, 'date')
+        let date=substitute(date, '_', ' ', '')
+        let dparts=map(s:F.parsedate(date), 'v:val is 0 ? '.
+                    \                               'eval(s:defdate[v:key]) : '.
+                    \                               'v:val')
+        let date=join(dparts[:2], '-').' '.join(dparts[3:], ':')
+    endif
+    "▲2
+    if empty(message)
+        call s:_r.run('silent new', 'commit', a:repo, user, date, cb, a:files)
+        if exists('g:AuPreviousRepoPath') &&
+                    \   g:AuPreviousRepoPath is# a:repo.path &&
+                    \exists('g:AuPreviousTip') &&
+                    \   g:AuPreviousTip is# a:repo.functions.gettiphex(a:repo)&&
+                    \exists('g:AuPreviousCommitMessage')
+            call setline('.', split(g:AuPreviousCommitMessage, "\n", 1))
+            call cursor(line('$'), col([line('$'), '$']))
+            unlet g:AuPreviousRepoPath g:AuPreviousTip g:AuPreviousCommitMessage
+        endif
+        let fmessage=[]
+        for [file, state] in items(revstatus)
+            let fmessage+=['# '.s:statmsgs[state].' '.file]
+        endfor
+        call sort(fmessage)
+        call append('.', fmessage)
+        startinsert
+        return 0
+    else
+        call a:repo.functions.commit(a:repo, message, a:files, user, date, cb)
+        return 1
+    endif
+endfunction
+"▶1 savemsg :: message, bvar → + g:
+function s:F.savemsg(message, bvar)
+    if a:message!~#"[^[:blank:]\n]"
+        return
+    endif
+    let g:AuPreviousCommitMessage=a:message
+    let g:AuPreviousTip=a:bvar.repo.functions.gettiphex(a:bvar.repo)
+    let g:AuPreviousRepoPath=a:bvar.repo.path
+endfunction
+"▶1 finish :: bvar → + bvar.repo
+function s:F.finish(bvar)
+    let message=join(filter(getline(1, '$'), 'v:val[0] isnot# "#"'), "\n")
+    if message!~#"[^[:blank:]\n]"
+        call s:_f.throw('emptmsg')
+    endif
+    if s:_f.getoption('remembermsg')
+        call s:F.savemsg(message, a:bvar)
+    endif
+    call a:bvar.repo.functions.commit(a:bvar.repo, message, a:bvar.files,
+                \                     a:bvar.user, a:bvar.date,
+                \                     a:bvar.closebranch)
+    let a:bvar.did_message=1
+    call feedkeys("\<C-\>\<C-n>:bwipeout!\n")
+endfunction
+"▶1 commfunc
+function s:cmd.function(opts, ...)
+    let rrfopts=copy(a:opts)
+    if a:0 && index(a:000, 'all')==-1
+        let rrfopts.files=a:000
+    endif
+    let [repo, rev, files]=s:_r.cmdutils.getrrf(rrfopts,
+                \                               ((a:0)?(0):('nocfile')),
+                \                               'getfiles')[1:]
+    call s:_r.cmdutils.checkrepo(repo)
+    let status=repo.functions.status(repo)
+    "▶2 Get file list
+    let types=['modified', 'added', 'removed']
+    if a:0 && index(a:000, 'all')!=-1
+        let files=[]
+    elseif a:0
+        if has_key(a:opts, 'type')
+            let types=s:_r.status.parseshow(a:opts.type)
+            call filter(types, 'v:val isnot# "clean" && v:val isnot# "ignored"')
+        endif
+        let filepats=map(filter(copy(a:000), 'v:val isnot# ":"'),
+                    \    's:_r.globtopat('.
+                    \    'repo.functions.reltorepo(repo, v:val))')
+        let statfiles={}
+        for [type, sfiles] in items(status)
+            if index(types, type)==-1
+                continue
+            endif
+            let curfiles=[]
+            for pattern in filepats
+                let curfiles+=filter(copy(sfiles), 'v:val=~#pattern')
+            endfor
+            if !empty(curfiles)
+                let statfiles[type]=curfiles
+                let files+=curfiles
+            endif
+        endfor
+    elseif files is 0
+        call s:_f.throw('nocfile')
+    endif
+    "▲2
+    return s:F.commit(repo, a:opts, files, status, types)
+endfunction
+"▶1 aurum://commit
+let s:commit={'arguments': 3,
+            \  'listargs': 1,
+            \'modifiable': 1,
+            \  'filetype': 'aurumcommit',
+            \}
+function s:F.bufleave()
+    let bvar=s:_r.bufvars[+expand('<abuf>')]
+    if !bvar.did_message && s:_f.getoption('bufleaveremembermsg')
+        let message=join(filter(getline(1,'$'),'v:val[0] isnot# "#"'), "\n")
+        call s:F.savemsg(message, bvar)
+    endif
+endfunction
+function s:commit.function(read, repo, user, date, cb, files)
+    if a:read
+        call s:_f.throw('nocread')
+    endif
+    augroup AuCommit
+        autocmd! BufLeave <buffer> :call s:F.bufleave()
+    augroup END
+    return {'user': a:user, 'date': a:date, 'files': a:files,
+                \'closebranch': !!a:cb, 'write': s:F.finish,
+                \'did_message': 0}
+endfunction
+let s:_augroups+=['AuCommit']
+function s:commit.write(lines, repo, user, date, cb, files)
+    let message=join(filter(copy(a:lines), 'v:val[0] isnot# "#"'), "\n")
+    call a:repo.functions.commit(a:repo, message, a:files, a:user, a:date, a:cb)
+endfunction
+call s:_f.newcommand(s:commit)
+"▶1 Post resource
+call s:_f.postresource('commit', {'commit': s:F.commit,
+            \                     'finish': s:F.finish,})
+"▶1
+call frawor#Lockvar(s:, '_r,_pluginloaded')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/diff.vim

+"▶1 
+scriptencoding utf-8
+execute frawor#Setup('0.0', {'@%aurum/cmdutils': '3.1',
+            \                 '@%aurum/bufvars': '0.0',
+            \               '@%aurum/lineutils': '0.0',
+            \                 '@%aurum/vimdiff': '1.0',
+            \                    '@%aurum/edit': '1.2',
+            \                          '@aurum': '1.0',
+            \                      '@/mappings': '0.0',
+            \                            '@/os': '0.0',})
+let s:_messages={
+            \'nodfile': 'Failed to get file whose section is under the cursor',
+        \}
+"▶1 difffunc
+function s:cmd.function(opts, ...)
+    if a:0 && a:opts.repo is# ':'
+        let repo=s:_r.cmdutils.checkedgetrepo(a:1)
+    else
+        let repo=s:_r.cmdutils.checkedgetrepo(a:opts.repo)
+    endif
+    let files=map(filter(copy(a:000), 'v:val isnot# ":"'),
+                \ 'repo.functions.reltorepo(repo, v:val)')
+    let hascur = (len(a:000)!=len(files))
+    "▶2 Get revisions and file list
+    if has_key(a:opts, 'changes')
+        let rev1=repo.functions.getrevhex(repo, a:opts.changes)
+        let rev2=''
+    else
+        let rev1=get(a:opts, 'rev1', '')
+        let rev2=get(a:opts, 'rev2', '')
+    endif
+    if empty(rev2)
+        if empty(rev1)
+            let rev2=repo.functions.getworkhex(repo)
+        else
+            let rev2=repo.functions.getnthparent(repo, rev1, 1).hex
+        endif
+    else
+        let rev2=repo.functions.getrevhex(repo, rev2)
+    endif
+    if !empty(rev1)
+        let rev1=repo.functions.getrevhex(repo, rev1)
+    endif
+    let status=filter(copy(repo.functions.status(repo,
+                \                                empty(rev1)?0:rev1, rev2)),
+                \     'v:key isnot# "clean" && v:key isnot# "ignored"')
+    let csfiles=[]
+    call map(values(status), 'extend(csfiles, v:val)')
+    unlet status
+    "▶2 Filter out requested files
+    call map(csfiles, 'join(s:_r.os.path.split(v:val)[1:], "/")')
+    let filelist=[]
+    for pattern in map(copy(files), 's:_r.globtopat(v:val)')
+        let filelist+=filter(copy(csfiles), 'v:val=~#pattern && '.
+                    \                       'index(filelist, v:val)==-1')
+    endfor
+    if hascur
+        let curfile=s:_r.cmdutils.getrrf({'repo': ':'}, 'nocurf', 'getfile')[3]
+        let curfile=repo.functions.reltorepo(repo, curfile)
+        if index(csfiles, curfile)!=-1 && index(filelist, curfile)==-1
+            let filelist+=[curfile]
+        endif
+    endif
+    if a:0 && empty(filelist)
+        return
+    endif
+    "▲2
+    let opts=filter(copy(a:opts), 'index(s:_r.diffopts, v:key)!=-1')
+    call s:_r.run(get(a:opts, 'cmd', 'silent edit'), 'diff', repo, rev1, rev2,
+                \ filelist, opts)
+    if !has_key(a:opts, 'cmd')
+        setlocal bufhidden=wipe
+        if has('folding')
+            setlocal foldmethod=expr
+        endif
+    endif
+    call s:_f.mapgroup.map('AuDiff', bufnr('%'))
+endfunction
+"▶1 aurum://diff mappings
+let s:mmgroup=':call <SNR>'.s:_sid.'_Eval("s:_f.mapgroup.map(''AuDiff'', '.
+            \                                               "bufnr('%'))\")\n"
+"▶2 rundiffmap
+function s:F.rundiffmap(action)
+    let buf=bufnr('%')
+    let bvar=s:_r.bufvars[buf]
+    let cmd="\<C-\>\<C-n>"
+    if a:action is# 'exit'
+        let cmd.=s:_r.cmdutils.closebuf(bvar)
+    elseif a:action is# 'update'
+        let rev=(empty(bvar.rev1)?(bvar.rev2):(bvar.rev1))
+        call s:_r.cmdutils.update(bvar.repo, rev, v:count)
+        return ''
+    elseif a:action is# 'previous' || a:action is# 'next'
+        let c=((a:action is# 'previous')?(v:count1):(-v:count1))
+        if empty(bvar.rev1)
+            let rev1=''
+        else
+            let rev1=bvar.repo.functions.getnthparent(bvar.repo,bvar.rev1,c).hex
+        endif
+        if empty(bvar.rev2)
+            let rev2=''
+        else
+            let rev2=bvar.repo.functions.getnthparent(bvar.repo,bvar.rev2,c).hex
+        endif
+        let cmd.=':edit '.
+                    \fnameescape(s:_r.fname('diff', bvar.repo, rev1, rev2,
+                    \                       bvar.files, bvar.opts))."\n"
+        let cmd.=s:mmgroup
+        let cmd.=":bwipeout ".buf."\n"
+    elseif a:action is# 'open'
+        let file=s:_r.cmdutils.getdifffile(bvar)
+        if file is 0
+            call s:_f.throw('nodfile')
+        endif
+        let fullpath=s:_r.os.path.join(bvar.repo.path, file)
+        if empty(bvar.rev1)
+            if filereadable(fullpath)
+                let cmd.=':edit'
+            else
+                let cmd.=':AuFile '.
+                            \((empty(bvar.rev2))?
+                            \       (bvar.repo.functions.getworkhex(bvar.repo)):
+                            \       (bvar.rev2))
+            endif
+        else
+            let cmd.=':AuFile '.bvar.rev1
+        endif
+        let cmd.=' '.fnameescape(fullpath)."\n"
+    elseif a:action is# 'vimdiff'
+        let cmd.=':AuVimDiff '
+        if empty(bvar.rev1)
+            let cmd.='curfile'
+        else
+            let cmd.=bvar.rev1
+        endif
+        let cmd.=' '.bvar.rev2."\n"
+        let cmd.=':if bufwinnr('.buf.')!=-1 | '.
+                    \'execute bufwinnr('.buf.')."wincmd w" | '.
+                    \'close | '.
+                    \'wincmd p | '.
+                    \"endif\n"
+    endif
+    return cmd
+endfunction
+"▶2 fvdiff
+function s:F.fvdiff()
+    let bvar=s:_r.bufvars[bufnr('%')]
+    let args=[bvar.repo]
+    if empty(bvar.rev1)
+        let args+=[[0, bvar.rev2]]
+    elseif empty(bvar.rev2)
+        let args+=[[0, bvar.rev1]]
+    else
+        let args+=[[bvar.rev1, bvar.rev2]]
+    endif
+    let args+=[1, bvar.files, 0]
+    return call(s:_r.vimdiff.full, args, {})
+endfunction
+"▲2
+call s:_f.mapgroup.add('AuDiff', {
+            \  'Next': {'lhs':  'K', 'rhs': ['next'       ]},
+            \  'Prev': {'lhs':  'J', 'rhs': ['previous'   ]},
+            \'Update': {'lhs':  'U', 'rhs': ['update'     ]},
+            \  'Exit': {'lhs':  'X', 'rhs': ['exit'       ]},
+            \  'Open': {'lhs':  'o', 'rhs': ['open'       ]},
+            \ 'Vdiff': {'lhs':  'D', 'rhs': ['vimdiff'    ]},
+            \'FVdiff': {'lhs': 'gD', 'rhs': ':call <SNR>'.s:_sid.'_Eval('.
+            \                                            '"s:F.fvdiff()")<CR>'},
+        \}, {'func': s:F.rundiffmap, 'silent': 1, 'mode': 'n', 'dontmap': 1,})
+"▶1 aurum://diff
+let s:diff= {'arguments': 2,
+            \ 'listargs': 1,
+            \  'options': {'num': s:_r.diffopts},
+            \ 'filetype': 'diff',
+            \   'mgroup': 'AuDiff',
+            \}
+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.functions.getworkhex(a:repo)
+    endif
+    "▲2
+    if a:read
+        call s:_r.lineutils.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.newcommand(s:diff)
+unlet s:diff
+"▶1
+call frawor#Lockvar(s:, '_r,_pluginloaded')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/drivers/common/hypsites.vim

+"▶1
+scriptencoding utf-8
+execute frawor#Setup('0.1', {'@/resources': '0.0',})
+"▶1 s:hypsites
+let s:dport='domain.(empty(port)?"":":".port)'
+let s:link='shellescape("http://".'.s:dport.'.path)'
+" TODO cache
+let s:dl=    '(executable("curl")?'.
+            \   '(system("curl -L ".'.s:link.')):'.
+            \'(executable("wget")?'.
+            \   '(system("wget -O- ".'.s:link.'))'.
+            \':'.
+            \   '(0)))'
+unlet s:link
+let s:bbdict={
+\       'html': '"https://".domain.path."/src/".hex."/".file',      'hline': '"cl-".line',
+\        'raw': '"https://".domain.path."/raw/".hex."/".file',
+\   'annotate': '"https://".domain.path."/annotate/".hex."/".file', 'aline': '"line-".line',
+\   'filehist': '"https://".domain.path."/history/".file',
+\     'bundle': '"https://".domain.path."/get/".hex.".tar.bz2"',
+\  'changeset': '"https://".domain.path."/changeset/".hex',
+\        'log': '"https://".domain.path."/changesets"',
+\      'clone': '"https://".domain.path',
+\       'push': '"ssh://hg@".domain.path',
+\}
+let s:hyp={}
+let s:gcproj='matchstr(domain, "\\v^[^.]+")'
+"▶1 mercurial
+"  https://bitbucket.org/ZyX_I/aurum / ssh://hg@bitbucket.org/ZyX_I/aurum
+"  ssh://zyxsf@translit3.hg.sourceforge.net/hgroot/translit3/translit3 /
+"       http://translit3.hg.sourceforge.net:8000/hgroot/translit3/translit3
+"  https://vim-pyinteractive-plugin.googlecode.com/hg/
+"  http://hg.assembla.com/CMakeLua
+"  https://zyx@zyx.codebasehq.com/test/test.hg /
+"       ssh://hg@codebasehq.com/zyx/test/test.hg
+"  https://hg01.codeplex.com/visualhg
+"  http://mercurial.intuxication.org/hg/tryton-client_ru
+"  https://mirrors.kilnhg.com/Repo/Mirrors/Hg/Mercurial
+"  http://hg.mozdev.org/maf/ / ssh://USER:PASS@hg.mozdev.org/maf
+"u https://projectkenai.com/hg/sonichg~test (rev numbers must match)
+"  https://hg.kenai.com/hg/sonichg~test / ssh://user@hg.kenai.com/sonichg~test
+"  http://hg.savannah.nongnu.org/hgweb/mechsys/
+"  https://sharesource.org/hg/alqua/
+"  http://mercurial.tuxfamily.org/mercurialroot/slitaz/tazlito/
+"t http://anonscm.debian.org/hg/minicom/
+" len("hgroot")=6
+let s:pkbase='"http://".matchstr(domain, ''\v[^.]+\.[^.]+$'')."/projects/".matchstr(path, ''\v.*\/\zs[^~]+'').'.
+            \                                                '"/sources/". matchstr(path, "\\v[^~]+$")'
+let s:cpbase='"http://".path[1:].".codeplex.com/SourceControl'
+let s:cbbase='"https://".%s.".".domain."/projects/".%s."/repositories/".%s'
+let s:cbssh=printf(s:cbbase, 'matchstr(path, "\\v^[^/]+", 1)',
+            \                'matchstr(path, ''\v[^/]+%(\/[^/]+\/?$)'')',
+            \                'matchstr(path[:-4], "\\v[^/]+$")')
+let s:cbhttps=printf(s:cbbase, 'matchstr(domain, "\\v^[^.]+")',
+            \                  'matchstr(path, "\\v^[^/]+")',
+            \                  'matchstr(path[:-4], "\\v[^/]+$")')
+unlet s:cbbase
+let s:hgwebdict={
+\       'html': '"http://".'.s:dport.'.path."/file/".hex."/".file',     'hline': '"l".line',
+\        'raw': '"http://".'.s:dport.'.path."/raw-file/".hex."/".file',
+\   'annotate': '"http://".'.s:dport.'.path."/annotate/".hex."/".file', 'aline': '"l".line',
+\   'filehist': '"http://".'.s:dport.'.path."/log/".hex."/".file',
+\  'changeset': '"http://".'.s:dport.'.path."/rev/".hex',
+\        'log': '"http://".'.s:dport.'.path."/graph"',
+\      'clone': '"http://".'.s:dport.'.path',
+\}
+let s:hyp.mercurial=[
+\['domain is? "bitbucket.org"', s:bbdict],
+\['domain =~? "\\Vhg.sourceforge.net\\$"',
+\ {     'html': '"http://".domain."/hgweb".path[7:]."/file/".hex."/".file',     'hline': '"l".line',
+\        'raw': '"http://".domain."/hgweb".path[7:]."/raw-file/".hex."/".file',
+\   'annotate': '"http://".domain."/hgweb".path[7:]."/annotate/".hex."/".file', 'aline': '"l".line',
+\   'filehist': '"http://".domain."/hgweb".path[7:]."/log/".hex."/".file',
+\  'changeset': '"http://".domain."/hgweb".path[7:]."/rev/".hex',
+\        'log': '"http://".domain."/hgweb".path[7:]."/graph"',
+\      'clone': '"http://".domain.":8000".path',
+\       'push': '"ssh://".user."@".domain.path',}],
+\['domain =~? "\\Vgooglecode.com\\$" && path[:2] is? "/hg"',
+\ {     'html': '"http://code.google.com/p/".'.s:gcproj.'."/source/browse/".file."?r=".hex', 'hline': 'line',
+\        'raw': '"http://".domain."/hg-history/".hex."/".file',
+\   'filehist': '"http://code.google.com/p/".'.s:gcproj.'."/source/list?path=/".file."&r=".hex',
+\  'changeset': '"http://code.google.com/p/".'.s:gcproj.'."/source/detail?r=".hex',
+\        'log': '"http://code.google.com/p/".'.s:gcproj.'."/source/list"',
+\      'clone': 'url',
+\       'push': 'url',}],
+\['domain is? "hg.assembla.com"',
+\ {     'html': '"http://trac-".domain.path."/browser/".file."?rev=".hex',                'hline': '"L".line',
+\   'annotate': '"http://trac-".domain.path."/browser/".file."?annotate=blame&rev=".hex', 'aline': '"L".line',
+\   'filehist': '"http://trac-".domain.path."/log/".file."?rev=".hex',
+\  'changeset': '"http://trac-".domain.path."/changeset/".hex',
+\        'log': '"http://trac-".domain.path."/log"',
+\      'clone': '"http://".domain.path',}],
+\['domain is? "codebasehq.com" && path[-3:] is? ".hg"',
+\ {     'html': s:cbssh.'."/blob/".hex."/".file', 'hline': '"L".line',
+\        'raw': s:cbssh.'."/raw/".hex."/".file',
+\   'annotate': s:cbssh.'."/blame/".hex."/".file',
+\   'filehist': s:cbssh.'."/commits/".hex."/".file',
+\     'bundle': s:cbssh.'."/archive/zip/".hex',
+\  'changeset': s:cbssh.'."/commit/".hex',
+\        'log': s:cbssh.'."/commits/tip"',
+\      'clone': '"https://".matchstr(path, "\\v^[^/]+", 1).".".domain.matchstr(path, ''\v[^/]+\/[^/]+$'')',
+\       'push': '"ssh://hg@".domain.path',}],
+\['domain =~? "\\Vcodebasehq.com\\$" && path[-3:] is? ".hg"',
+\ {     'html': s:cbhttps.'."/blob/".hex."/".file', 'hline': '"L".line',
+\        'raw': s:cbhttps.'."/raw/".hex."/".file',
+\   'annotate': s:cbhttps.'."/blame/".hex."/".file',
+\   'filehist': s:cbhttps.'."/commits/".hex."/".file',
+\     'bundle': s:cbhttps.'."/archive/zip/".hex',
+\  'changeset': s:cbhttps.'."/commit/".hex',
+\        'log': s:cbhttps.'."/commits/tip"',
+\      'clone': '"https://".domain.path',
+\       'push': '"ssh://hg@".matchstr(domain, ''\v\.@<=.*$'')."/".matchstr(domain, "\\v^[^.]+").path',}],
+\['domain =~? "\\V\\^hg\\d\\+.codeplex.com\\$"',
+\ {     'html': s:cpbase.'"/changeset/view/".hex[:11]."#".substitute(file, "/", "%2f", "g")',
+\     'bundle': '"http://download.codeplex.com/Download/SourceControlFileDownload.ashx'.
+\                       '?ProjectName=".path[1:]."&changeSetId=".hex[:11]',
+\  'changeset': s:cpbase.'"/changeset/changes/".hex[:11]',
+\        'log': s:cpbase.'"/list/changesets"',
+\      'clone': '"https://".domain.path',
+\       'push': '"https://".domain.path',}],
+\['domain =~? "\\Vkilnhg.com\\$"',
+\ {     'html': '"https://".domain.path."/File/".file."?rev=".hex',               'hline': 'line',
+\        'raw': '"https://".domain.path."/FileDownload/".file."?rev=".hex',
+\   'annotate': '"https://".domain.path."/File/".file."?rev=".hex&view=annotate', 'aline': 'line',
+\   'filehist': '"https://".domain.path."/FileHistory/".file."?rev=".hex',
+\  'changeset': '"https://".domain.path."/History/".hex',
+\        'log': '"https://".domain.path',
+\      'clone': '"https://".domain.path',}],
+\['domain =~? ''\V\%(project\)\?kenai.com\$'' && (path[:2] is? "/hg" || domain[:2] is? "hg.")',
+\ {     'html': s:pkbase.'."/content/".file."?rev=".repo.functions.getcsprop(repo, hex, "rev")',
+\        'raw': s:pkbase.'."/content/".file."?raw=true&rev=".repo.functions.getcsprop(repo, hex, "rev")',
+\   'filehist': s:pkbase.'."/history/".file',
+\  'changeset': s:pkbase.'."/revision/".repo.functions.getcsprop(repo, hex, "rev")',
+\        'log': s:pkbase.'."/history"',
+\      'clone': '"https://".domain."/hg/".matchstr(path, "\\v[^/]+$")',
+\       'push': '"ssh://".domain."/".matchstr(path, "\\v[^/]+$")',}],
+\['domain is? "sharesource.org" && path[:2] is? "/hg"',
+\ map(copy(s:hgwebdict), 'substitute(v:val, "http", "https", "")')],
+\[ 'domain =~? ''\v^%(mercurial\.%(intuxication|tuxfamily)|hg\.mozdev|hg\.savannah\.%(non)?gnu)\.org$'' || '.
+\ '(domain is? "anonscm.debian.org" && path[:2] is? "/hg") || '.
+\ '('.s:dl.'=~#''\V<link rel="icon" href="\[^"]\*static/hgicon.png" type="image/png" />'')',
+\ s:hgwebdict],
+\]
+unlet s:hgwebdict s:pkbase s:cpbase s:cbssh s:cbhttps
+"▶1 git
+"  ssh://git@github.com:MarcWeber/vim-addon-manager / git://github.com/MarcWeber/vim-addon-manager
+"  git://vimpluginloader.git.sourceforge.net/gitroot/vimpluginloader/vam-test-repository
+"       / ssh://zyxsf@vimpluginloader.git.sourceforge.net/gitroot/vimpluginloader/vam-test-repository
+"  git://repo.or.cz/test2.git / http://repo.or.cz/r/test2.git /
+"       ssh://repo.or.cz/srv/git/test2.git
+"  git://gitorious.org/test4/test.git / https://git.gitorious.org/test4/test.git
+"       / ssh://git@gitorious.org:test4/test.git
+"  git://git.kitenet.net/mr.git / http://git.kitenet.net/git/mr.git
+"       / ssh://git.kitenet.net/srv/git/mr.git
+"  (unable to clone with hg-git) https://code.google.com/p/tortoisegit/
+let s:ghpath='substitute(path, "\\v^[:/]|\\.git$", "", "g")'
+let s:roproj='matchstr(path, ''\v\/@<=[^/]{-1,}%(%(\.git)?\/*$)@='').".git"'
+let s:robase='"http://".domain."/w/".'.s:roproj
+let s:godomain='substitute(domain, "^git\\.", "", "")'
+let s:gobase='"http://".'.s:godomain.'."/".'.s:ghpath
+let s:hyp.git=[
+\['domain is? "bitbucket.org"', s:bbdict],
+\['domain is? "github.com"',
+\ {     'html': '"https://".domain."/".'.s:ghpath.'."/blob/".hex."/".file',   'hline': '"L".line',
+\                                                                             'hlines': '"L".line1."-L".line2',
+\        'raw': '"https://".domain."/".'.s:ghpath.'."/raw/". hex."/".file',
+\   'annotate': '"https://".domain."/".'.s:ghpath.'."/blame/". hex."/".file', 'aline': '"LID".line',
+\   'filehist': '"https://".domain."/".'.s:ghpath.'."/commits/".hex."/".file',
+\     'bundle': '"https://".domain."/".'.s:ghpath.'."/zipball/".hex',
+\  'changeset': '"https://".domain."/".'.s:ghpath.'."/commit/".hex',
+\        'log': '"https://".domain."/".'.s:ghpath.'."/commits"',
+\      'clone': '"git://".domain."/".'.s:ghpath,
+\       'push': '"ssh://git@".domain.":".'.s:ghpath.'.".git"',}],
+\['domain =~? "\\Vgit.sourceforge.net\\$"',
+\ {     'html': '"http://".domain."/git/gitweb.cgi?p=".path[9:].";a=blob;hb=".hex.";f=".file', 'hline': '"l".line',
+\        'raw': '"http://".domain."/git/gitweb.cgi?p=".path[9:].";a=blob_plain;hb=".hex.";f=".file',
+\   'filehist': '"http://".domain."/git/gitweb.cgi?p=".path[9:].";a=history;hb=".hex.";f=".file',
+\  'changeset': '"http://".domain."/git/gitweb.cgi?p=".path[9:].";a=commitdiff;hb=".hex',
+\        'log': '"http://".domain."/git/gitweb.cgi?p=".path[9:].";a=log"',
+\      'clone': '"http://".domain.":8000".path',
+\       'push': '"ssh://".user."@".domain.path',}],
+\['domain is? "code.google.com"',
+\ {     'html': '"http://code.google.com/".substitute(path, "/$", "", "")."/source/browse/".file."?r=".hex',}],
+\['domain =~? ''\v^%(git\.)?gitorious\.org$''',
+\ {     'html': s:gobase.'."/blobs/".hex."/".file',       'hline': '"line".line',
+\        'raw': s:gobase.'."/blobs/raw/".hex."/".file',
+\   'annotate': s:gobase.'."/blobs/blame/".hex."/".file', 'aline': '"line".line',
+\   'filehist': s:gobase.'."/blobs/history/".hex."/".file',
+\  'changeset': s:gobase.'."/commit/".hex',
+\        'log': s:gobase.'."/commits/".hex',
+\      'clone': '"git://".'.s:godomain.'."/".'.s:ghpath,
+\       'push': '"ssh://git@".'.s:godomain.'.":".'.s:ghpath.'.".git"',}],
+\['domain is? "repo.or.cz"',
+\ {     'html': s:robase.'."/blob/".hex.":/".file',       'hline': '"l".line',
+\        'raw': s:robase.'."/blob_plain/".hex.":/".file',
+\   'annotate': s:robase.'."/blame/".hex.":/".file',      'aline': '"l".line',
+\   'filehist': s:robase.'."/history/".hex.":/".file',
+\  'changeset': s:robase.'."/commit/".hex',
+\        'log': s:robase.'."/log/".hex',
+\      'clone': '"git://".domain."/".'.s:roproj,
+\       'push': '"ssh://".domain."/srv/git/".'.s:roproj,}],
+\['domain =~? "\\Vgit.kitenet.net\\$"',
+\ {     'html': '"http://".domain."/?p=".'.s:roproj.'.";a=blob;hb=".hex.";f=".file', 'hline': '"l".line',
+\        'raw': '"http://".domain."/?p=".'.s:roproj.'.";a=blob_plain;hb=".hex.";f=".file',
+\   'filehist': '"http://".domain."/?p=".'.s:roproj.'.";a=history;hb=".hex.";f=".file',
+\  'changeset': '"http://".domain."/?p=".'.s:roproj.'.";a=commitdiff;hb=".hex',
+\        'log': '"http://".domain."/?p=".'.s:roproj.'.";a=log"',
+\      'clone': '"git://".domain."/".'.s:roproj,
+\       'push': '"ssh://".domain."/srv/git/".'.s:roproj,}],
+\]
+unlet s:ghpath s:roproj s:robase s:godomain s:gobase
+"▶1 subversion
+"  https://vimpluginloader.svn.sourceforge.net/svnroot/vimpluginloader
+"  http://conque.googlecode.com/svn/trunk
+let s:svngcbase='"http://code.google.com/p/".'.s:gcproj
+let s:svngcfile='path[5:]."/".file'
+let s:hyp.svn=[
+\['domain =~? "\\Vsvn.sourceforge.net\\$"',
+\ {     'html': '"http://".domain."/viewvc".path[8:]."/".file."?view=markup&pathrev=".hex', 'hline': '"l".line',
+\        'raw': '"http://".domain."/viewvc".path[8:]."/".file."?pathrev=".hex',             'aline': '"l".line',
+\   'annotate': '"http://".domain."/viewvc".path[8:]."/".file."?annotate=".hex',
+\     'bundle': '"http://".domain."/viewvc".path[8:]."?view=tar&pathrev=".hex',
+\        'log': '"http://".domain."/viewvc".path[8:]."?view=log"',
+\      'clone': 'url',}],
+\['domain =~? "\\Vgooglecode.com\\$" && path[:3] is? "/svn"',
+\ {     'html': s:svngcbase.'."/source/browse/".'.s:svngcfile.'."?rev=".hex', 'hline': 'line',
+\        'raw': '"http://".domain."/svn-history/r".hex.'.s:svngcfile,
+\   'filehist': s:svngcbase.'."/source/list?path=/".'.s:svngcfile.'."&r=".hex',
+\        'log': s:svngcbase.'."/source/list"',
+\      'clone': 'url',}],
+\['domain is? "svn.gna.org',
+\ {     'html': '"http://".domain."/viewvcs".path[4:]."/".file."?view=markup&revision=".hex',   'hline': '"l".line',
+\        'raw': '"http://".domain."/viewvcs/*checkout*".path[4:]."/".file."?view=markup&revision=".hex',
+\   'annotate': '"http://".domain."/viewvcs".path[4:]."/".file."?annotate=".hex',               'aline': '"l".line',
+\   'filehist': '"http://".domain."/viewvcs".path[4:]."/".file."?view=log"',
+\        'log': '"http://".domain."/viewvcs".path[4:]."?view=log"',
+\      'clone': '"svn://".domain.path',
+\       'push': '"svn+ssh://".user."@".domain.path',}],
+\]
+unlet s:svngcbase s:svngcfile
+"▶1 post resource
+unlet s:gcproj s:dl s:bbdict s:dport
+call s:_f.postresource('hypsites', s:hyp)
+unlet s:hyp
+"▶1
+call frawor#Lockvar(s:, '_pluginloaded')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/drivers/common/utils.vim

+"▶1
+scriptencoding utf-8
+execute frawor#Setup('0.0', {'@/resources': '0.0',
+            \                       '@/os': '0.2'})
+let s:utils={}
+"▶1 utils.getcmd :: cmd, args, kwargs, esc → sh
+function s:utils.getcmd(cmd, args, kwargs, esc)
+    let cmd=a:cmd
+    if !empty(a:kwargs)
+        let cmd.=' '.join(map(filter(items(a:kwargs), 'v:val[1] isnot 0'),
+                \             '((v:val[1] is 1)?'.
+                \               '(repeat("-", 1+(len(v:val[0])>1)).v:val[0]):'.
+                \               '(repeat("-", 1+(len(v:val[0])>1)).v:val[0].'.
+                \                              '" ="[len(v:val[0])>1].'.
+                \                              'shellescape(v:val[1],a:esc)))'))
+    endif
+    if !empty(a:args)
+        let cmd.=' '.join(map(copy(a:args), 'shellescape(v:val, a:esc)'))
+    endif
+    return cmd
+endfunction
+"▶1 utils.run :: sh, hasnulls::0|1|2 → [String] + shell
+function s:utils.run(cmd, hasnulls, cdpath)
+    if a:hasnulls==2 && !empty(&shellredir)
+        return call(s:_r.os.readsystem, [a:cmd]+(empty(a:cdpath)?
+                    \                               ([]):
+                    \                               ([a:cdpath])), {})
+    elseif a:hasnulls
+        let savedlazyredraw=&lazyredraw
+        let savedeventignore=&eventignore
+        set eventignore=all
+        set lazyredraw
+        try
+            try
+                tabnew
+            catch /^Vim(tabnew):E523:/
+                let r=s:utils.run(a:cmd, 2, a:cdpath)
+                if empty(r[-1])
+                    call remove(r, -1)
+                endif
+                return r
+            endtry
+            setlocal buftype=nofile modifiable noreadonly
+            if !empty(a:cdpath)
+                execute 'lcd' fnameescape(a:cdpath)
+            endif
+            " XXX this is not able to distinguish between output with and 
+            " without trailing newline, and also is “smart” about lineendings
+            silent execute '%!'.a:cmd
+            let r=getline(1, '$')
+            bwipeout!
+        finally
+            let &lazyredraw=savedlazyredraw
+            let &eventignore=savedeventignore
+        endtry
+    else
+        let cmd=a:cmd
+        if !empty(a:cdpath)
+            let cmd='cd '.shellescape(a:cdpath).' && '.cmd
+        endif
+        let r=split(system(cmd), "\n", 1)
+    endif
+    return r
+endfunction
+"▶1 utils.printm :: sh, hasnulls::Bool → + :echom, shell
+function s:utils.printm(m)
+    let prevempty=0
+    for line in a:m
+        if empty(line)
+            let prevempty+=1
+        else
+            if prevempty
+                while prevempty
+                    echom ' '
+                    let prevempty-=1
+                endwhile
+            endif
+            echom line
+        endif
+    endfor
+endfunction
+"▶1 utils.diffopts :: opts, opts, difftrans → diffopts
+function s:utils.diffopts(opts, defaultdiffopts, difftrans)
+    let opts=extend(copy(a:defaultdiffopts), a:opts)
+    let r={}
+    call map(filter(copy(a:difftrans), 'has_key(opts, v:key)'),
+            \'extend(r, {v:val : opts[v:key]})')
+    if has_key(opts, 'dates') && has_key(a:difftrans, 'dates')
+        let r[a:difftrans.dates]=!opts.dates
+    endif
+    return r
+endfunction
+"▶1 utils.addfiles :: repo, files + status → + add, forget
+function s:utils.addfiles(repo, files)
+    let status=a:repo.functions.status(a:repo, 0, 0, a:files)
+    for file in status.unknown
+        call a:repo.functions.add(a:repo, file)
+    endfor
+    for file in status.deleted
+        call a:repo.functions.forget(a:repo, file)
+    endfor
+endfunction
+"▶1 utils.usefile :: repo, message, kw, kw, func, args, kwargs, emes
+function s:utils.usefile(repo, message, kwfile, kwmes, Func, args, kwargs, ...)
+    if a:message=~#'\v[\r\n]'
+        let tmpfile=tempname()
+        call writefile(split(a:message, "\n", 1), tmpfile, 'b')
+        let a:kwargs[a:kwfile]=tmpfile
+        let usingfile=1
+    else
+        let a:kwargs[a:kwmes]=a:message
+        let usingfile=0
+    endif
+    try
+        return call(a:Func, [a:repo, 'commit', a:args, a:kwargs]+a:000, {})
+    finally
+        if usingfile && filereadable(tmpfile)
+            call delete(tmpfile)
+        endif
+    endtry
+endfunction
+"▶1 post resource
+call s:_f.postresource('utils', s:utils)
+"▶1
+call frawor#Lockvar(s:, '_pluginloaded')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/drivers/common/xml.vim

+"▶1
+scriptencoding utf-8
+execute frawor#Setup('0.0', {'@/resources': '0.0',})
+let s:xml={}
+"▶1 messages
+let s:_messages={
+            \ 'allblanks': 'No non-blank characters found',
+            \'tstartnfnd': 'Expected tag start (“<tagname”), but got “%s”',
+            \  'tendnfnd': 'Expected tag end (“>”), but got “%s”',
+            \  'ctagnfnd': 'Expected “%s”, but got “%s”',
+            \     'ctend': 'Expected “%s”, but got nothing',
+            \     'ttend': 'Expected tag end, but got nothing',
+        \}
+"▶1 new :: [String] → xml
+function s:F.new(lines)
+    let xml=deepcopy(s:xml)
+    let xml.lines=copy(a:lines)
+    let xml.tags=[]
+    return xml
+endfunction
+"▶1 skipws :: &
+function s:xml.skipws()
+    try
+        while self.lines[0]!~#'\S'
+            call remove(self.lines, 0)
+        endwhile
+        let self.lines[0]=substitute(self.lines[0], '\v^\s+', '', '')
+    catch /\m^Vim(\l*):E684:/
+        call s:_f.throw('allblanks')
+    endtry
+endfunction
+"▶1 decodeentities :: String → String
+let s:entities={
+            \  'lt': '<',
+            \  'gt': '>',
+            \ 'amp': '&',
+            \'quot': '"',
+        \}
+function s:F.decodeentities(s)
+    return substitute(a:s, '\v\&([lg]t|amp)\;', '\=s:entities[submatch(1)]','g')
+endfunction
+"▶1 skip :: &(len)
+function s:xml.skip(len)
+    let self.lines[0]=self.lines[0][(a:len):]
+    return self.skipws()
+endfunction
+"▶1 parsetag :: & → (tagname, attributes)
+" Assumes that tag names have only alphabetic characters
+function s:xml.parsetag()
+    call self.skipws()
+    let m=matchlist(self.lines[0], '\v\<(\a+)')[:1]
+    if empty(m)
+        call s:_f.throw('tstartnfnd', self.lines[0])
+    endif
+    let [match, tagname]=m
+    let self.tags+=[tagname]
+    call self.skip(len(match))
+    let attributes={}
+    while 1
+        let m=matchlist(self.lines[0], '\v^([a-zA-Z0-9_\-]+)\=\"(.{-})\"')[:2]
+        if empty(m)
+            break
+        endif
+        let [match, attrname, attrval]=m
+        call self.skip(len(match))
+        let attributes[attrname]=s:F.decodeentities(attrval)
+    endwhile
+    if self.lines[0][0] isnot# '>'
+        call s:_f.throw('tendnfnd', self.lines[0])
+    endif
+    let self.lines[0]=self.lines[0][1:]
+    return [tagname, attributes]
+endfunction
+"▶1 checkctag :: & → Bool
+function s:xml.checkctag()
+    return (!empty(self.lines) && self.lines[0][:1] is# '</')
+endfunction
+"▶1 skipctag :: &
+function s:xml.skipctag()
+    let tagname=remove(self.tags, -1)
+    let e='</'.tagname.'>'
+    let le=len(e)
+    try
+        if self.lines[0][:(le)] isnot# e
+            call s:_f.throw('ctagnfnd', e, self.lines[0])
+        endif
+        let self.lines[0]=self.lines[0][(le):]
+    catch /\m^Vim(\l*):E684:/
+        call s:_f.throw('ctend', e)
+    endtry
+endfunction
+"▶1 parsetextintag :: & → text
+function s:xml.parsetextintag()
+    try
+        let text=[]
+        while 1
+            let idx=stridx(self.lines[0], '<')
+            if idx!=-1
+                break
+            endif
+            let text+=remove(self.lines, 0, 0)
+        endwhile
+        if idx
+            let text+=[self.lines[0][:(idx-1)]]
+            let self.lines[0]=self.lines[0][(idx):]
+        else
+            let text+=['']
+        endif
+        call map(text, 's:F.decodeentities(v:val)')
+        call self.skipctag()
+        return text
+    catch /\m^Vim(\l*):E684:/
+        call s:_f.throw('ttend')
+    endtry
+endfunction
+"▶1 post resource
+call s:_f.postresource('xml', {'new': s:F.new,
+            \       'decodeentities': s:F.decodeentities})
+"▶1
+call frawor#Lockvar(s:, '_pluginloaded')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲

autoload/aurum/drivers/git.vim

+"▶1
+scriptencoding utf-8
+execute frawor#Setup('0.1', {'@%aurum/drivers/common/hypsites': '0.0',
+            \                                   '@%aurum/repo': '5.0',
+            \                   '@%aurum/drivers/common/utils': '0.0',
+            \                                           '@/os': '0.1',
+            \                                      '@/options': '0.0',})
+let s:_messages={
+            \   'hexf': 'Failed to obtain hex string for revision %s '.
+            \           'in the repository %s: %s',
+            \   'logf': 'Failed to list all revisions in the repository %s: %s',
+            \  'rlogf': 'Failed to list revisions %s..%s '.
+            \           'in the repository %s: %s',
+            \    'csf': 'Failed to obtain information about revision %s '.
+            \           'in the repository %s: %s',
+            \    'cif': 'Failed to commit changes to the repository %s: %s',
+            \   'updf': 'Failed to checkout commit %s in the repository %s: %s',
+            \    'mvf': 'Failed to move file %s to %s in the repository %s: %s',
+            \    'rmf': 'Failed to remove file %s in the repository %s: %s',
+            \    'fgf': 'Failed to forget file %s in the repository %s: %s',
+            \  'filef': 'Failed to get revision %s of the file %s '.
+            \           'from the repository %s: %s',
+            \   'annf': 'Failet to annotate revision %s of the file %s '.
+            \           'in the repository %s: %s',
+            \  'difff': 'Failed to get diff between %s and %s for files %s '.
+            \           'in the repository %s: %s',
+            \ 'sdifff': 'Failed to get status information '.
+            \           'for the repository %s: %s',
+            \ 'rdifff': 'Failed to property %s for changeset %s '.
+            \           'in the repository %s: %s',
+            \    'lsf': 'Failed to list files in the changeset %s '.
+            \           'of the repository %s: %s',
+            \'statusf': 'Failed to obtain status of the repository %s: %s',
+            \    'rlf': 'Failed to list commits in repository %s: %s',
+            \    'lbf': 'Failed to create/remove %s %s for revision %s '.
+            \           'in the repository %s: %s',
+            \'branchf': 'Failed to get list of branches '.
+            \           'from the repository %s: %s',
+            \  'grepf': 'Failed to search through the repository %s: %s',
+            \   'tagf': 'Failed to get list of tags from the repository %s: %s',
+            \   'addf': 'Failed to add file %s to the repository %s: %s',
+            \ 'cbnimp': 'Git driver is not able to close branch',
+            \   'nloc': 'Git driver does not suppport local tags or branches',
+            \   'chbf': 'Failed to create branch %s in the repository %s: %s',
+            \  'nocfg': 'Failed to get property %s of repository %s',
+            \ 'invrng': 'Range %s..%s is invalid for the repository %s, '.
+            \           'as well as reverse',
+            \    'ppf': 'Failed to run “git %s” for the repository %s: %s',
+        \}
+let s:git={}
+let s:_options={
+            \'git_maxitercsnum': {'default': 1000, 'checker': 'range 0 inf'},
+        \}
+"▶1 s:hypsites
+let s:hypsites=s:_r.hypsites.git
+"▶1 refile :: gitfname → path
+function s:F.refile(fname)
+    return a:fname[0] is# '"' ? eval(a:fname) : a:fname
+endfunction
+"▶1 gitcmd :: repo, cmd, args, kwargs, esc → String
+function s:F.gitcmd(repo, ...)
+    return 'git --git-dir='.  shellescape(a:repo.path.'/.git', a:4).
+                \' --work-tree='.shellescape(a:repo.path,         a:4).
+                \' '.call(s:_r.utils.getcmd, a:000, {})
+endfunction
+"▶1 git :: repo, cmd, args, kwargs, has0[, msgid[, marg1[, …]]] → [String] + ?
+function s:F.git(repo, cmd, args, kwargs, hasnulls, ...)
+    let cmd=s:F.gitcmd(a:repo, a:cmd, a:args, a:kwargs, a:hasnulls)
+    let r=s:_r.utils.run(cmd, a:hasnulls, a:repo.path)
+    if v:shell_error && a:0
+        call call(s:_f.throw, a:000+[a:repo.path, join(r[:-1-(a:hasnulls)],
+                    \                                  "\n")], {})
+    endif
+    return r
+endfunction
+"▶1 gitm :: {git args} → + :echom
+function s:F.gitm(...)
+    return s:_r.utils.printm(call(s:F.git, a:000, {}))
+endfunction
+"▶1 parsecs :: csdata, lstart::UInt → (cs, line::UInt)
+" hash-parent hashes-timestamp
+"  (refs)
+" author name
+" author email
+" 1-indented commit message
+let s:logformat='%h-%H-%P-%at%n%an%n%ae%n%d%n%w(0,1,1)%B'
+let s:logkwargs={'format': s:logformat, 'encoding': 'utf-8', 'date-order': 1}
+function s:F.parsecs(csdata)
+    let cs={'branch': 'default'}
+    let [rev, hex, parents, time]=split(remove(a:csdata, 0), '-', 1)
+    let cs.hex=hex
+    let cs.parents=split(parents)
+    let cs.time=+time
+    let cs.rev=rev
+    let aname=remove(a:csdata, 0)
+    let aemail=remove(a:csdata, 0)
+    let cs.user=aname.' <'.aemail.'>'
+    let cs.tags=split(remove(a:csdata, 0)[2:-2], ', ')
+    let cs.bookmarks=[]
+    "▶2 get description
+    let description=[]
+    while !empty(a:csdata) && a:csdata[0][0] is# ' '
+        let description+=[remove(a:csdata, 0)[1:]]
+    endwhile
+    let cs.description=join(description, "\n")
+    if get(a:csdata, 0, 0) is# ''
+        call remove(a:csdata, 0)
+    endif
+    "▲2
+    return cs
+endfunction
+"▶1 git.getcs :: repo, rev → cs
+function s:git.getcs(repo, rev)
+    let cs=s:F.parsecs(s:F.git(a:repo, 'log', ['-n1', a:rev], s:logkwargs,
+                \              0, 'csf', a:rev))
+    " XXX This construct is used to preserve information like “allfiles” etc
+    let a:repo.changesets[cs.hex]=extend(get(a:repo.changesets, cs.hex, {}), cs)
+    return a:repo.changesets[cs.hex]
+endfunction
+"▶1 git.getwork :: repo → cs
+function s:git.getwork(repo)
+    return a:repo.functions.getcs(a:repo, 'HEAD')
+endfunction
+"▶1 prepgitargs
+function s:F.prepgitargs(repo, ...)
+    "▶2 Prepare s:F.git arguments
+    let args=[]
+    let kwargs=copy(s:logkwargs)
+    let revargs=[]
+    let revkwargs={}
+    if a:0
+        let revargs+=[((a:1 is 0)?(''):(a:1.'^..')).
+                    \ ((a:2 is 0)?(''):(a:2))]
+    else
+        let revkwargs.all=1
+        let revkwargs['full-history']=1
+    endif
+    let gitargs=[a:repo, 'log', args, kwargs, 0]
+    if a:0
+        let gitargs+=['rlogf', a:1, a:2]
+    else
+        let gitargs+=['logf']
+    endif
+    "▶2 Prepare hexslist in case git_maxitercsnum is greater then zero
+    if a:repo.maxitercsnum
+        let gitrlargs=copy(gitargs)
+        let gitrlargs[2]=copy(gitrlargs[2])+revargs
+        let gitrlargs[3]=extend(extend(copy(gitrlargs[3]),
+                    \           s:rlkwargs),
+                    \           revkwargs)
+        let hexlist=call(s:F.git, gitrlargs, {})[:-2]
+        let kwargs['no-walk']=1
+    else
+        call extend(kwargs, revkwargs)
+        call extend(  args, revargs  )
+        let hexlist=1
+    endif
+    "▲2
+    return [hexlist, gitargs]
+endfunction
+"▶1 git.getchangesets :: repo[, rangestart[, rangeend]] → [cs]
+let s:rlkwargs={'format': '%H'}
+function s:git.getchangesets(repo, ...)
+    let [hexlist, gitargs]=call(s:F.prepgitargs, [a:repo]+a:000, {})
+    let args=gitargs[2]
+    let cslist=[]
+    while !empty(hexlist)
+        if hexlist is 1
+            let hexlist=0
+        else
+            let curhexs=remove(hexlist, 0,
+                        \      min([a:repo.maxitercsnum, len(hexlist)])-1)
+            let gitargs[2]=args+curhexs
+        endif
+        let log=call(s:F.git, gitargs, {})[:-2]
+        "▶2 Parse changeset information
+        while !empty(log)
+            let cs=s:F.parsecs(log)
+            let a:repo.changesets[cs.hex]=extend(get(a:repo.changesets, cs.hex,
+                        \                            {}),
+                        \                        cs)
+            call insert(cslist, a:repo.changesets[cs.hex])
+        endwhile
+        "▲2
+    endwhile
+    return cslist
+endfunction
+"▶1 git.revrange :: repo, rev1, rev2 → [cs]
+let s:git.revrange=s:git.getchangesets
+"▶1 git.updatechangesets :: repo → _
+function s:git.updatechangesets(...)
+endfunction
+"▶1 git.getrevhex :: repo, rev → hex
+let s:prevrevhex={}
+function s:git.getrevhex(repo, rev)
+    if a:rev=~#'\v^[0-9a-f]{40}$'
+        if has_key(s:prevrevhex, a:repo.path)
+            unlet s:prevrevhex[a:repo.path]
+        endif
+        return a:rev
+    endif
+    let r=s:F.git(a:repo, 'rev-parse', [a:rev], {}, 0, 'hexf', a:rev)[0]
+    let s:prevrevhex[a:repo.path]=[a:rev, r]
+    return r
+endfunction
+"▶1 git.gettiphex
+" XXX Uses master or working directory revision instead of latest revision
+function s:git.gettiphex(repo)
+    try
+        return a:repo.functions.getrevhex(a:repo, 'master')
+    catch
+        return a:repo.functions.gettiphex(a:repo)
+    endtry
+endfunction
+"▶1 git.getworkhex :: repo → hex
+function s:git.getworkhex(repo)
+    return a:repo.functions.getrevhex(a:repo, 'HEAD')
+endfunction
+"▶1 git.setcsprop :: repo, cs, propname → propvalue
+function s:git.setcsprop(repo, cs, prop)
+    if a:prop is# 'allfiles'
+        let r=map(s:F.git(a:repo, 'ls-tree', ['--', a:cs.hex],
+                    \                        {'name-only': 1, 'r': 1}, 0,
+                    \     'lsf', a:cs.hex)[:-2], 's:F.refile(v:val)')
+    elseif a:prop is# 'children'
+        let lines=filter(map(s:F.git(a:repo, 'rev-list', [], {'all': 1,
+                    \                                'full-history': 1,
+                    \                                    'children': 1}, 0,
+                    \                'rlf')[:-2],
+                    \        'split(v:val)'),
+                    \    'has_key(a:repo.changesets, v:val[0])')
+        for [hex; children] in lines
+            let a:repo.changesets[hex].children=children
+        endfor
+        return a:cs.children
+    elseif       a:prop is# 'renames' || a:prop is# 'copies' ||
+                \a:prop is# 'changes' || a:prop is# 'files'  ||
+                \a:prop is# 'removes'
+        let lparents=len(a:cs.parents)
+        if lparents==0
+            let allfiles=a:repo.functions.getcsprop(a:repo, a:cs, 'allfiles')
+            let a:cs.renames={}
+            let a:cs.copies={}
+            let a:cs.changes=copy(allfiles)
+            let a:cs.files=copy(a:cs.changes)
+            let a:cs.removes=[]
+        elseif lparents==1
+            let args=[a:cs.parents[0].'..'.a:cs.hex]
+            let kwargs={'name-status': 1, 'M': 1, 'diff-filter': 'ADMR'}
+            if a:prop is# 'copies'
+                let kwargs.C=1
+                let kwargs['find-copies-harder']=1
+                let kwargs['diff-filter'].='C'
+            endif
+            let d=map(s:F.git(a:repo, 'diff', args, kwargs, 'rdifff', a:prop,
+                        \     a:cs.hex)[:-2], 'split(v:val, "\t")')
+            if a:prop is# 'copies'
+                let a:cs.copies={}
+            endif
+            let a:cs.renames={}
+            let a:cs.files=[]
+            let a:cs.removes=[]
+            for [status; files] in d
+                call map(files, 's:F.refile(v:val)')
+                if status[0] is# 'M' || status[0] is# 'A'
+                    let a:cs.files+=[files[0]]
+                elseif status[0] is# 'D'
+                    let a:cs.removes+=[files[0]]
+                elseif status[0] is# 'R'
+                    let a:cs.renames[files[1]]=files[0]
+                elseif status[0] is# 'C'
+                    let a:cs.copies[files[1]]=files[0]
+                endif
+            endfor
+            let a:cs.changes=a:cs.files+a:cs.removes
+        elseif lparents>=2
+            " FIXME Here must be files that had merge conflicts
+            let a:cs.renames={}
+            let a:cs.copies={}
+            let a:cs.changes=[]
+            let a:cs.files=[]
+            let a:cs.removes=[]
+        endif
+        return a:cs[a:prop]
+    endif
+    let a:cs[a:prop]=r
+    return r
+endfunction
+"▶1 nullnl :: [String] → [String]
+" Convert between lines (NL separated strings with NULLs represented as NLs) and 
+" NULL separated strings with NLs represented by NLs.
+function s:F.nullnl(text)
+    let r=['']
+    for nlsplit in map(copy(a:text), 'split(v:val, "\n", 1)')
+        let r[-1].="\n".nlsplit[0]
+        call extend(r, nlsplit[1:])
+    endfor
+    if empty(r[0])
+        call remove(r, 0)
+    else
+        let r[0]=r[0][1:]
+    endif
+    return r
+endfunction
+"▶1 git.status :: repo[, rev1[, rev2[, files[, clean]]]]
+let s:statchars={
+            \'A': 'added',
+            \'M': 'modified',
+            \'D': 'removed',
+        \}
+let s:initstatdct={
+            \'modified': [],
+            \   'added': [],
+            \ 'removed': [],
+            \ 'deleted': [],
+            \ 'unknown': [],
+            \ 'ignored': [],
+            \   'clean': [],
+        \}
+function s:git.status(repo, ...)
+    let r=deepcopy(s:initstatdct)
+    let requiresclean=(a:0>3 && a:4)
+    if a:0 && (a:1 isnot 0 || (a:0>1 && a:2 isnot 0))
+        let args=((a:0>2 && !empty(a:3))?(['--']+a:3):([]))
+        let rspec=[]
+        let reverse=0
+        if a:1 is 0
+            if a:0>1 && a:2 isnot 0
+                let reverse=1
+            endif
+        else
+            let rspec+=[a:1]
+        endif
+        if a:0>1 && a:2 isnot 0
+            let rspec+=[a:2]
+        endif
+        call insert(args, join(rspec, '..'))
+        let kwargs={'diff-filter': 'AMD', 'name-status': 1, 'no-renames': 1}
+        let d=s:F.git(a:repo, 'diff', args, kwargs, 0, 'sdifff')[:-2]
+        let files=map(copy(d), 's:F.refile(v:val[2:])')
+        call map(copy(d), 'add(r[s:statchars[v:val[0]]], files[v:key])')
+        if reverse
+            let [r.deleted, r.unknown]=[r.unknown, r.deleted]
+            let [r.added,   r.removed]=[r.removed, r.added  ]
+        endif
+        if requiresclean
+            let allfiles=a:repo.functions.getcsprop(a:repo,rspec[0],'allfiles')
+        endif
+    else
+        let args=((a:0>2 && !empty(a:3))?(['--']+a:3):([]))
+        let kwargs={'porcelain': 1, 'z': 1}
+        let s=s:F.nullnl(s:F.git(a:repo,'status',args,kwargs,2,'statusf'))[:-2]
+        let files={}
+        while !empty(s)
+            let line=remove(s, 0)
+            let status=line[:1]
+            let file=line[3:]
+            let files[file]=1
+            if status[0] is# 'R'
+                let r.added+=[file]
+                let r.removed+=[remove(s, 0)]
+            elseif status[0] is# 'C'
+                let r.added+=[file]
+                let origfile=remove(s, 0)
+                " FIXME What should be done with origfile?
+            elseif status[0] is# 'D'
+                let r.removed+=[file]
+            elseif status[1] is# 'D'
+                let r.deleted+=[file]
+            elseif status[0] is# 'A'
+                let r.added+=[file]
+            elseif stridx(status, 'M')!=-1
+                let r.modified+=[file]
+            elseif status is# '??'
+                let r.unknown+=[file]
+            endif
+        endwhile
+        if requiresclean
+            let allfiles=a:repo.functions.getcsprop(a:repo, 'HEAD', 'allfiles')
+        endif
+    endif
+    if exists('allfiles')
+        if a:0>2 && !empty(a:3)
+            let allfiles=filter(copy(allfiles), 'index(a:3, v:val)!=-1')
+        endif
+        let r.clean=filter(copy(allfiles), '!has_key(files, v:val)')
+    endif
+    return r
+endfunction
+"▶1 git.commit :: repo, message[, files[, user[, date[, _]]]]
+function s:git.commit(repo, message, ...)
+    let kwargs={'cleanup': 'verbatim'}
+    let args=[]
+    if a:0
+        if empty(a:1)
+            let kwargs.all=1
+        else
+            let args+=['--']+a:1
+            call s:_r.utils.addfiles(a:repo, a:1)
+        endif
+        if a:0>1 && !empty(a:2)
+            let kwargs.author=a:2
+        endif
+        if a:0>2 && !empty(a:3)
+            let kwargs.date=a:3
+        endif
+        if a:0>3 && !empty(a:4)
+            call s:_f.throw('cbnimp')
+        endif
+    else
+        let kwargs.all=1
+    endif
+    return s:_r.utils.usefile(a:repo, a:message, 'file', 'message',
+                \             s:F.gitm, args, kwargs, 0, 'cif')
+endfunction
+"▶1 git.branch :: repo, branchname, force → + FS
+function s:git.branch(repo, branch, force)
+    if a:force
+        return s:F.gitm(a:repo, 'checkout', [a:branch], {'B': 1}, 0,
+                    \   'chbf', a:branch)
+    else
+        call a:repo.functions.label(a:repo, 'branch', a:branch, 'HEAD', 0, 0)
+        call a:repo.functions.update(a:repo, a:branch, 0)
+    endif
+endfunction
+"▶1 git.label :: repo, type, label, rev, force, local → + FS
+function s:git.label(repo, type, label, rev, force, local)
+    if a:local
+        call s:_f.throw('nloc')
+    endif
+    let args=['--', a:label]
+    let kwargs={}
+    if a:force
+        let kwargs.force=1
+    endif
+    if a:rev is 0
+        let kwargs.d=1
+    else
+        let args+=[a:rev]
+    endif
+    return s:F.gitm(a:repo, a:type, args, kwargs, 0,
+                \   'lbf', a:type, a:label, a:rev)
+endfunction
+"▶1 git.update :: repo, rev, force → + FS