Commits

ZyX_I committed 1f92ff7

Added `showrenames' glog option
Made `files' glog selector work
Fixed getcsprop(repo, cs, "files")
Added repo.functions.renamed()
Added `gF' (show file history) mapping
Fixed `limit' option: it now limits to N last changesets, before it limited to N first changesets

  • Participants
  • Parent commits 80fa805

Comments (0)

Files changed (3)

File ftplugin/aurumgraphlog.vim

         let r.min=1
         return r
     endif
+    "▶4 Date selector
     if has_key(a:opts, 'date')
         let nums=map(split(a:opts.date, '\v[^0-9*]+'), 'v:val is# "*"? "\\d*":'.
                     \                                                 'v:val')
             return r
         endif
     endif
+    "▶4 Files selector
+    if has_key(a:opts, 'files')
+        " XXX files list will be used later in section “Add files”
+        let files=a:opts.csfiles[a:cs.hex]
+        if empty(files)
+            let r.min=1
+            return r
+        endif
+    endif
     "▶3 Add date and user
     let date=strftime("%d %b %Y %H:%M", a:cs.time)
     let text+=['Commited '.date.' by '.a:cs.user]
         let lw=s:_r.strdisplaywidth(ftext[-1])
         let curfilenum=0
         let fi=0
-        for file in a:cs.files
+        for file in exists('files') ? files : a:cs.files
             let fw=s:_r.strdisplaywidth(file, lw)
             if curfilenum && fw+lw>ww
                 let ftext+=['Files: '.file.', ']
             let fl=len(file)
             let ll=len(ftext[-1])
             let special['file'.fi.'_r']=[[cl, ll-fl-2], [cl, ll-2]]
-            let fi+=1
+            if exists('files')
+                let fi=index(a:cs.files, file)
+            else
+                let fi+=1
+            endif
             let curfilenum+=1
         endfor
         call map(ftext, 'v:val[:-3]')
         let special.files_R=[[curline, 0], [curline+len(ftext)-1, 0]]
         let text+=ftext
     endif
+    "▶3 Add renames
+    if get(a:opts, 'showrenames', 0)
+        for [cur, old] in items(a:opts.repo.functions.getcsprop(a:opts.repo,
+                    \                                           a:cs,
+                    \                                           'renames'))
+            if type(old)!=type('') || (exists('files') && index(files, cur)==-1)
+                continue
+            endif
+            let cl=len(text)
+            let fl=len(cur)
+            let ol=len(old)
+            let text+=['Renamed '.old.' to '.cur]
+            let ll=len(text[-1])
+            let fi=index(a:cs.files, cur)
+            let special['file'.fi.'_r']=[[cl, ll-fl], [cl, ll-1]]
+            let special['oldname'.fi.'_r']=[[cl, ll-fl-4-ol], [cl, ll-fl-4]]
+            let special['rename'.fi.'_l']=[cl, 0]
+        endfor
+    endif
     "▶3 Add description
     let description=map(split(a:cs.description, "\n", 1), '"  ".v:val')
     let special.description_R=[[len(text),                    0],
                 \'specials': {},
                 \'rectangles': [],
                 \'csstarts': {},}
-    let revs=get(a:opts, 'rev', [0, get(a:opts, 'limit', 0)-1])
-    for cs in a:css[revs[0]:revs[1]]
+    for cs in a:css[a:opts.rev[0]:a:opts.rev[1]]
         let char=((index(a:showparents, cs.hex)==-1)?('o'):('@'))
         let text=call(a:Dumper, [cs, a:opts], {})
         call s:F.glog.utf(state, 'C', char, text,
     let a:opts.repo=a:repo
     return s:F.glog.generate(css, [a:repo.work_hex], s:F.glog.summary, a:opts)
 endfunction
+"▶1 encodeopts :: opts → String
+function s:F.encodeopts(opts)
+    let r=''
+    let crrestrict=get(a:opts, 'crrestrict', 0)
+    for [key, value] in filter(items(a:opts), 'crrestrict isnot# v:val[0]')
+        if type(value)==type({}) || key is# 'crrestrict'
+            unlet value
+            continue
+        endif
+        let r.=key.':'
+        if type(value)==type([])
+            let r.=join(map(copy(value), 'escape(v:val, "\\:,;")'), ';')
+        else
+            let r.=escape(value, '\:,')
+        endif
+        let r.=','
+        unlet value
+    endfor
+    return r
+endfunction
 "▶1 setup
+"▶2 trackfile :: repo, cs, file, csfiles → + csfiles
+function s:F.trackfile(repo, cs, file, csfiles)
+    let tocheck=[[a:file, a:cs]]
+    while !empty(tocheck)
+        let [file, cs]=remove(tocheck, 0)
+        if !has_key(a:csfiles, cs.hex)
+            " FIXME Check whether it causes a problem with more then one parent
+            return
+        endif
+        let rename=get(cs.renames, file, 0)
+        if type(rename)!=type('')
+            let rename=file
+        endif
+        if index(a:csfiles[cs.hex], file)==-1
+            let a:csfiles[cs.hex]+=[file]
+        endif
+        let tocheck+=map(copy(cs.parents), '[rename, a:repo.changesets[v:val]]')
+    endwhile
+endfunction
+"▲2
 function s:F.setup()
     let buf=bufnr('%')
     let bvar=s:_r.aurum.bufvars[buf]
+    "▶2 Create buffer name
+    if empty(bufname('%'))
+        let epath=escape(bvar.repo.path, ':\')
+        execute 'edit' fnameescape('aurum://noop:glog:'.epath.':'.
+                    \              s:F.encodeopts(bvar.opts))
+        setlocal buftype=nofile
+    endif
+    "▶2 Get revision range
+    if has_key(bvar.opts, 'rev')
+        call map(bvar.opts.rev,
+                    \'bvar.repo.changesets['.
+                    \   'bvar.repo.functions.getrevhex(bvar.repo, v:val)].rev')
+    elseif get(bvar.opts, 'limit', 0)>0
+        let bvar.opts.rev=[len(bvar.repo.cslist)-limit, -1]
+    else
+        let bvar.opts.rev=[0, -1]
+    endif
+    "▲2
+    let cslist=bvar.repo.cslist[bvar.opts.rev[0]:bvar.opts.rev[1]]
+    "▶2 Generate cs.renames for `showrenames' option
+    if has_key(bvar.opts, 'showrenames')
+        for cs in cslist
+            call bvar.repo.functions.getcsprop(bvar.repo, cs, 'renames')
+        endfor
+    endif
+    "▶2 Generate file lists for `files' option
+    if has_key(bvar.opts, 'files')
+        call map(bvar.opts.files, 's:_r.aurum.globtopattern(v:val)')
+        let bvar.opts.csfiles={}
+        for cs in cslist
+            let rns   = bvar.repo.functions.getcsprop(bvar.repo, cs, 'renames')
+            let files = bvar.repo.functions.getcsprop(bvar.repo, cs, 'files')
+            let files = copy(files)
+            let csfiles=[]
+            let bvar.opts.csfiles[cs.hex]=csfiles
+            for pattern in bvar.opts.files
+                let newfiles=filter(copy(files), 'v:val=~#pattern')
+                call filter(files, 'index(newfiles, v:val)==-1')
+                let csfiles+=newfiles
+                if empty(files)
+                    break
+                endif
+            endfor
+            call map(copy(csfiles), 's:F.trackfile(bvar.repo, cs, v:val, '.
+                        \                         'bvar.opts.csfiles)')
+        endfor
+        let bvar.opts.totrack={}
+    endif
+    "▲2
     let text=s:F.glog.graphlog(bvar.repo, bvar.opts)
     let bvar.specials=text.specials
     let bvar.rectangles=text.rectangles
 endfunction
 "▶1 cr
 function s:F.cr(...)
-    "▶2 Get changeset and current special
+    "▶2 Get changeset, current special, encode options
     let bvar=s:_r.aurum.bufvars[bufnr('%')]
     let [blockstart, blockend, hex]=s:F.getblock(bvar)
     if a:0
     endif
     let epath=escape(bvar.repo.path, ':\')
     let cs=bvar.repo.changesets[hex]
-    "▶2 Get options
-    let opts=''
-    let crrestrict=get(bvar.opts, 'crrestrict', [])
-    for [key, value] in filter(items(bvar.opts), 'crrestrict isnot# v:val[0]')
-        if key is# 'repo' || key is# 'crrestrict'
-            unlet value
-            continue
-        endif
-        let opts.=key.':'
-        if type(value)==type([])
-            let opts.=join(map(copy(value), 'escape(v:val, "\\:,;")'), ';')
-        else
-            let opts.=escape(value, '\:,')
-        endif
-        let opts.=','
-        unlet value
-    endfor
+    let opts=s:F.encodeopts(bvar.opts)
     "▶2 Commit actions based on current special
     "▶3 branch: add `branch' filter
     if spname is# 'branch'
     endif
     return ''
 endfunction
+"▶1 filehistory
+function s:F.filehistory()
+    let [hex, file]=s:F.gethexfile()
+    let bvar=s:_r.aurum.bufvars[bufnr('%')]
+    let opts=s:F.encodeopts(bvar.opts)
+    let epath=escape(bvar.repo.path, ':\')
+    return ':edit '.fnameescape('aurum://glog:'.epath.':'.opts.
+                \               'files:'.escape(file, ':\*?[').','.
+                \               'crrestrict:files')."\n"
+endfunction
 "▶1 AuGlog mapping group
 call s:_f.mapgroup.add('AuGlog', {
-            \   'Enter': {'lhs': '<CR>', 'rhs': [],                          },
-            \    'Open': {'lhs': 'o', 'rhs': s:F.open                        },
-            \    'Diff': {'lhs': 'd', 'rhs': ['diff']                        },
-            \   'RDiff': {'lhs': 'c', 'rhs': ['revdiff']                     },
-            \   'VDiff': {'lhs': 'D', 'rhs': [1],         'func': s:F.vimdiff},
-            \  'RVDiff': {'lhs': 'C', 'rhs': s:F.vimdiff                     },
-            \    'Next': {'lhs': 'K', 'rhs': s:F.next                        },
-            \    'Prev': {'lhs': 'J', 'rhs': s:F.prev                        },
+            \   'Enter': {'lhs': "\n", 'rhs': [],                             },
+            \    'File': {'lhs': 'gF', 'rhs': s:F.filehistory                 },
+            \    'User': {'lhs': 'gu', 'rhs': ['user']                        },
+            \    'Date': {'lhs': 'gD', 'rhs': ['date']                        },
+            \  'Branch': {'lhs': 'gb', 'rhs': ['branch']                      },
+            \    'Diff': {'lhs':  'd', 'rhs': ['diff']                        },
+            \   'RDiff': {'lhs':  'c', 'rhs': ['revdiff']                     },
+            \   'VDiff': {'lhs':  'D', 'rhs': [1],         'func': s:F.vimdiff},
+            \  'RVDiff': {'lhs':  'C', 'rhs': s:F.vimdiff                     },
+            \    'Next': {'lhs':  'K', 'rhs': s:F.next                        },
+            \    'Prev': {'lhs':  'J', 'rhs': s:F.prev                        },
+            \    'Open': {'lhs':  'o', 'rhs': s:F.open                        },
             \}, {'func': s:F.cr})
 "▶1
 execute frawor#Lockvar(s:, '_r')

File plugin/aurum.vim

 endfunction
 "▶3 hg.getcsprop :: repo, cs, propname → a
 function s:F.hg.getcsprop(repo, cs, propname)
+    if a:propname is# 'renames'
+        return a:repo.functions.renamed(a:repo, a:cs)
+    endif
     if has_key(a:cs, a:propname)
         return a:cs[a:propname]
     endif
     " with given name is added to changeset dictionary
     return a:cs[a:propname]
 endfunction
+"▶3 hg.getcs :: repo, rev → cs
+function s:F.hg.getcs(repo, rev)
+    let hex=a:repo.functions.getrevhex(a:rev)
+    return a:repo.changesets[hex]
+endfunction
+"▶3 hg.renamed :: repo, cs[, [file]] → [Maybe String]
+function s:F.hg.renamed(repo, cs, ...)
+    let files=a:repo.functions.getcsprop(a:repo, a:cs, 'files')
+    if a:0
+        let filestoprocess=filter(copy(a:1), 'index(v:val, files)!=-1')
+    else
+        if has_key(a:cs, 'gotrenames')
+            return a:cs.renames
+        endif
+        let filestoprocess=files
+    endif
+    if !has_key(a:cs, 'renames')
+        let a:cs.renames={}
+        let uknfiles=filestoprocess
+    else
+        let uknfiles=filter(copy(filestoprocess),'!has_key(a:cs.renames,v:val)')
+    endif
+    if !empty(uknfiles)
+        execute s:_r.py.cmd 'aurum.get_renames(vim.eval("a:repo.path"), '.
+                    \                         'vim.eval("a:cs.hex"), '.
+                    \                         'vim.eval("uknfiles"))'
+    endif
+    if a:0
+        let r={}
+        call map(copy(a:files),
+                    \'extend(r, {v:val : get(a:cs.renames, v:val, -1)})')
+        return r
+    else
+        let a:cs.gotrenames=1
+        return a:cs.renames
+    endif
+endfunction
 "▶3 hg.diff :: repo, rev1, rev2, [path], opts → [String]
 function s:F.hg.diff(repo, rev1, rev2, files, opts)
     let r=[]
 endfunction
 "▶3 comm.globtopattern :: glob → pattern
 function s:F.comm.globtopattern(glob)
+    " XXX If more metacharacters will be supported, they must be added to 
+    " escape() call in s:F.filehistory
     return '\V\^'.substitute(substitute(substitute(substitute(substitute(
                 \substitute(substitute(a:glob,
                 \'\v\\(.)', '\="\\{".char2nr(submatch(1))."}"', 'g'),
 function s:auefunc.function()
     "▶4 Split <amatch> into command and arguments
     let tail=substitute(expand('<amatch>'), '\V\^aurum://', '', '')
-    let command=matchstr(tail, '\v^\w+')
+    let command=tolower(matchstr(tail, '\v^\w+'))
     let tail=tail[len(command)+1:]
-    if tail is# 'no-op'
-        return
-    endif
     "▶4 glog command (repo:opts)
     if command is# 'glog'
         let [repo, opts]=s:F.comm.repotupleoptssplit(tail, 0)
                     \       'files': filelist,}
         setlocal buftype=nofile filetype=diff
         call repo.functions.difftobuffer(repo, buf, rev1, rev2, filelist, opts)
+    "▶4 noop command (do nothing)
+    elseif command is# 'noop'
     endif
 endfunction
 "▶3 glogfunc
     if type(a:repo)!=type({})
         return
     endif
-    new aurum://glog:no-op
-    if has_key(a:opts, 'rev')
-        call map(a:opts.rev,
-                    \'a:repo.changesets['.
-                    \   'a:repo.functions.getrevhex(a:repo, v:val)].rev')
+    new
+    if has_key(a:opts, 'files')
+        call map(a:opts.files, 's:_r.os.path.join('.
+                    \          's:_r.os.path.split('.
+                    \          's:_r.os.path.relpath(v:val, a:repo.path))[1:],'.
+                    \               '"/")')
     endif
     let s:bufvars[bufnr('%')]={'repo': a:repo, 'opts': a:opts}
     setlocal buftype=nofile filetype=aurumgraphlog bufhidden=wipe
 let s:glogfunc['@FWC']=['[:*F.comm.getrepo(".") '.
             \            '(either(path d, match @\v^\w+%(\+\w+)*\V://@)'.
             \                            '|*F.comm.getrepo)]'.
-            \           ' { *?files   (type "" |*F.comm.globtopattern)'.
+            \           ' { *?files   (type "")'.
             \           '    ?date    match /\v%(\d\d?|\*)'.
             \                                 '%(\.%(\d\d?|\*)'.
             \                                 '%(\.%(\d\d%(\d\d)?|\*)'.
             \           '   !?patch'.
             \           '    ?limit   range 1 inf'.
             \           '   !?stat'.
-            \           '   !?showfiles}',
+            \           '   !?showfiles'.
+            \           '   !?showrenames'.
+            \           ' }',
             \'filter']
 call add(s:glogcomp,
             \substitute(substitute(s:glogfunc['@FWC'][0],
 let s:_augroups+=['AurumDiff']
 "▶3 Post aurum resource
 call s:_f.postresource('aurum', {'bufvars': s:bufvars,
-            \                  'diffsplit': s:F.comm.diffsplit}, 1)
+            \                  'diffsplit': s:F.comm.diffsplit,
+            \              'globtopattern': s:F.comm.globtopattern,}, 1)
 "▶1
 call frawor#Lockvar(s:, 'repos,_pluginloaded,bufvars')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File python/aurum.py

 
 def new_repo(path):
     repo=get_repo(path)
-    repo_vim={}
-    repo_vim['path']=path
-    repo_vim['changesets']={}
-    repo_vim['work_hex']=repo['.'].hex()
-    repo_vim['tip_hex']=repo['tip'].hex()
-    repo_vim['cslist']=get_revlist(repo)
-    vim.eval('extend(repo, '+utf_dumps(repo_vim)+')')
+    vim.eval('extend(repo, '+utf_dumps({
+                                    'path': path,
+                                    'root': repo.root,
+                              'changesets': {},
+                                'work_hex': repo['.'].hex(),
+                                 'tip_hex': repo['tip'].hex(),
+                                  'cslist': get_revlist(repo),
+                                   'local': 1 if repo.local() else 0,
+                                    })+')')
 
 def get_file(path, rev, filepath):
     fctx=get_repo(path)[rev].filectx(filepath)
     dodiff(ui, *args, **kwargs)
 
 def get_cs_prop(path, rev, prop):
+    cs=get_repo(path)[rev]
+    if prop=='files':
+        r=[f for f in cs]
+    else:
+        r=cs.__getattribute__(prop)()
     # XXX There is much code relying on the fact that after getcsprop property 
     # with given name is added to changeset dictionary
-    vim.eval('extend(a:cs, {"'+prop+'": '+
-             nonutf_dumps(get_repo(path)[rev].__getattribute__(prop)())+'})')
+    vim.eval('extend(a:cs, {"'+prop+'": '+nonutf_dumps(r)+'})')
+
+def get_renames(path, rev, files):
+    def get_renames_value(rename):
+        return rename[0] if rename else 0
+    cs=get_repo(path)[rev]
+    vim.eval('extend(a:cs.renames, '+nonutf_dumps(
+            {f: get_renames_value(cs.filectx(f).renamed()) for f in files})+')')