1. ZyX_I
  2. aurum

Commits

ZyX_I  committed 0209388

Initial :AuRecord implementation

  • Participants
  • Parent commits daf70bf
  • Branches default

Comments (0)

Files changed (3)

File ftplugin/aurumstatus.vim

View file
  • Ignore whitespace
     let status=bvar.repo.functions.status(bvar.repo, get(bvar.opts, 'rev1', 0),
                 \                                    get(bvar.opts, 'rev2', 0))
     let bvar.types=[]
+    let bvar.chars=[]
+    let bvar.files=[]
     let show=get(bvar.opts, 'show', s:defshow)
+    let isrecord=get(bvar.opts, 'record', 0)
     for [type, files] in filter(items(status), 'index(show, v:val[0])!=-1')
         let char=has_key(s:statchars, type)? s:statchars[type]: toupper(type[0])
         for file in files
             if ignore
                 continue
             endif
-            call append('$', char.' '.file)
+            call append('$', ((isrecord)?('-'):('')).char.' '.file)
             let bvar.types+=[type]
+            let bvar.chars+=[char]
+            let bvar.files+=[file]
         endfor
     endfor
     if empty(bvar.types)
     if empty(rev1) && empty(rev2)
         let rev1='.'
     endif
-    let file=getline('.')[2:]
+    let file=bvar.files[line('.')-1]
     let epath=escape(bvar.repo.path, ':\')
     if index(s:noacttypes[a:action], bvar.types[line('.')-1])!=-1
         return ''

File plugin/aurum.vim

View file
  • Ignore whitespace
     "▶2 Объявление переменных
     execute frawor#Setup('0.0', {'@/commands': '0.0',
                 \               '@/functions': '0.0',
+                \                '@/mappings': '0.0',
                 \                      '@/os': '0.1',
                 \                  '@/python': '0.0',
                 \            '@/autocommands': '0.0',
                 \               '@/resources': '0.0',
                 \                     '@/fwc': '0.0',
                 \                 '@/options': '0.0',}, 0)
-    call map(['hg', 'comm', 'graph', 'glog'], 'extend(s:F, {v:val : {}})')
+    call map(['hg', 'comm', 'rec'], 'extend(s:F, {v:val : {}})')
     lockvar 1 s:F
     "▶2 Команды
     call FraworLoad('@/commands')
     let s:statcomp=[]
     call s:_f.command.add('AuStatus', s:statfunc, {'nargs': '*',
                 \                                  'complete': s:statcomp})
+    let s:recfunc={}
+    let s:reccomp=[]
+    call s:_f.command.add('AuRecord', s:recfunc, {'nargs': '*',
+                \                              'complete': s:reccomp})
     "▶2 Autocommands
     call FraworLoad('@/autocommands')
     let s:auefunc={}
 "▶1 Вторая загрузка
 let s:repos={}
 let s:_options={
-            \'diffopts': {'default': {},
-            \             'checker': 'dict {numlines         range 0 inf '.
-            \                              '?key hgdiffopts  bool}'},
+            \'diffopts':  {'default': {},
+            \              'checker': 'dict {numlines         range 0 inf '.
+            \                               '?key hgdiffopts  bool}'},
+            \'recheight': {'default': 0,
+            \               'filter': '(if type "" earg _  range 0 inf)'},
+        \}
+let s:repoarg=':*F.comm.getrepo(".") (either(path d, '.
+            \                               'match @\v^\w+%(\+\w+)*\V://@) '.
+            \                        '|*F.comm.getrepo '.
+            \                        'type {})'
+let s:_messages={
+            \'uknundo': 'Cannot undo changes made manually. Resetting buffer '.
+            \           'instead.',
+            \ 'uchngs': 'Found changes done manually. Resetting buffer, '.
+            \           'please retry.',
+            \'renfail': 'Failed to move file %s to %s',
+            \'delfail': 'Failed to remove file %s',
         \}
 "▶1 Вторая загрузка — функции
 "▶2 hg
     execute s:_r.py.cmd 'aurum.get_status(vim.eval("a:repo.path"), '.revargs.')'
     return r
 endfunction
+"▶2 rec
+"▶3 recfunc
+function s:recfunc.function(repo, opts, ...)
+    tabnew
+    let t:aurecid='AuRecordTab'
+    let w:aurecid='AuRecordLeft'
+    rightbelow vsplit
+    let w:aurecid='AuRecordRight'
+    let epath=escape(a:repo.path, ':\')
+    let files=copy(a:000)
+    call map(files, 'join('.
+                \   's:_r.os.path.split('.
+                \   's:_r.os.path.relpath(v:val, a:repo.path))[1:], "/")')
+    let opts='record:1'
+    if !empty(files)
+        let opts.=',files:'.join(map(copy(files), 'escape(v:val,"\\;:,")'), ';')
+    endif
+    let height=s:_f.getoption('recheight')
+    if height<=0
+        let height=winheight(0)/5
+    endif
+    execute 'botright '.height.' split '.
+                \fnameescape('aurum://status:'.epath.':'.opts)
+    let w:aurecid='AuRecordStatus'
+    setlocal nomodifiable
+    call s:_f.mapgroup.map('AuRecord', bufnr('%'))
+    let bvar=s:bufvars[bufnr('%')]
+    " 0: not included, unmodified
+    " 1: not included,   modified
+    " 2:     included, unmodified
+    " 3:     included,   modified
+    let bvar.statuses=repeat([0], len(bvar.types))
+    let bvar.prevct=b:changedtick
+    let bvar.reset=0
+    let bvar.backupfiles={}
+    let bvar.lines=map(copy(bvar.chars), 'v:val." ".bvar.files[v:key]')
+    let bvar.swheight=height
+    let bvar.startundo=undotree().seq_cur
+    augroup AuRecordStatus
+        autocmd BufUnload <buffer> :call s:F.rec.unload(
+                    \                               s:bufvars[expand('<abuf>')])
+    augroup END
+endfunction
+let s:_augroups+=['AuRecordStatus']
+let s:recfunc['@FWC']=['['.s:repoarg.'] '.
+            \          '{!addremove   :=(0)'.
+            \          ' ?message           type ""'.
+            \          ' ?date              type ""'.
+            \          ' ?user              type ""'.
+            \          ' !closebranch :=(0)'.
+            \          '} '.
+            \          '+ type ""', 'filter']
+call add(s:reccomp,
+            \substitute(substitute(s:recfunc['@FWC'][0],
+            \'\V|*F.comm.getrepo',  '',           ''),
+            \'\V+ type ""', '+ (path)', ''))
+"▶3 rec.reset
+function s:F.rec.reset(bvar, msgid)
+    call s:_f.warn(a:msgid)
+    for idx in range(0, len(a:bvar.lines)-1)
+        call setline(idx+1, s:statchars[a:bvar.statuses[idx]].bvar.lines[idx])
+    endfor
+    let a:bvar.prevct=b:changedtick
+    let a:bvar.reset=1
+    let a:bvar.undolevels=&undolevels
+    let a:bvar.startundo=undotree().seq_cur
+    setlocal undolevels=-1
+endfunction
+"▶3 rec.unload
+function s:F.rec.unload(bvar)
+    for [backupfile, file] in items(a:bvar.backupfiles)
+        if delete(file)
+            call s:_f.warn('delfail', file)
+        endif
+        if rename(backupfile, file)
+            call s:_f.warn('renfail', backupfile, file)
+        endif
+    endfor
+    if exists('t:aurecid') && t:aurecid is# 'AuRecordTab'
+        tabclose!
+    endif
+endfunction
+"▶3 rec.getwnrs
+function s:F.rec.getwnrs()
+    let lwnr=0
+    let rwnr=0
+    let swnr=0
+    for wnr in range(1, winnr('$'))
+        let wid=getwinvar(wnr, 'aurecid')
+        if wid is# 'AuRecordLeft'
+            let lwnr=wnr
+        elseif wid is# 'AuRecordRight'
+            let rwnr=wnr
+        elseif wid is# 'AuRecordStatus'
+            let swnr=wnr
+        endif
+    endfor
+    if lwnr is 0 || rwnr is 0
+        execute swnr.'wincmd w'
+        wincmd o
+        topleft split
+        let w:aurecid='AuRecordLeft'
+        vsplit
+        let w:aurecid='AuRecordRight'
+        wincmd j
+        execute 'resize' bvar.swheight
+        let lwnr=1
+        let rwnr=2
+    endif
+    return [lwnr, rwnr, swnr]
+endfunction
+"▶3 rec.runstatmap
+let s:statchars='-^+*'
+function s:F.rec.runstatmap(action)
+    let buf=bufnr('%')
+    let bvar=s:bufvars[buf]
+    setlocal modifiable noreadonly
+    if b:changedtick!=bvar.prevct
+        call s:F.rec.reset(bvar, 'uchngs')
+        setlocal readonly nomodifiable
+        return
+    endif
+    if a:action[-3:] is# 'add' || a:action[-6:] is# 'ignore'
+        if a:action[0] is# 'v'
+            let sline=line("'<")
+            let eline=line("'>")
+            if sline>eline
+                let [sline, eline]=[eline, sline]
+            endif
+        else
+            let sline=line('.')
+            let eline=line('.')+v:count1-1
+        endif
+        let add=(a:action[-3:] is# 'add')
+        for line in range(sline, eline)
+            let status=bvar.statuses[line-1]
+            if add && status<2
+                let status+=2
+            elseif status>1
+                let status-=2
+            endif
+            call setline(line, s:statchars[status].bvar.lines[line-1])
+        endfor
+    elseif a:action is# 'discard'
+        call s:F.rec.unload(bvar)
+        return
+    elseif a:action is# 'undo'
+        if bvar.reset || undotree().seq_cur<=bvar.startundo
+            setlocal readonly nomodifiable
+            return
+        endif
+        silent undo
+        for line in range(1, line('$'))
+            let bvar.statuses[line-1]=stridx(s:statchars, getline(line)[0])
+        endfor
+        if undotree().seq_cur<bvar.startundo
+            silent redo
+        endif
+        " TODO Undo modified files
+    elseif a:action is# 'redo'
+        if bvar.reset || undotree().seq_cur<=bvar.startundo
+            setlocal readonly nomodifiable
+            return
+        endif
+        silent redo
+        for line in range(1, line('$'))
+            let bvar.statuses[line-1]=stridx(s:statchars, getline(line)[0])
+        endfor
+    elseif a:action is# 'edit'
+        let [lwnr, rwnr, swnr]=s:F.rec.getwnrs()
+        let file=bvar.lines[line('.')-1][2:]
+        let type=bvar.types[line('.')-1]
+        let epath=escape(bvar.repo.path, ':\')
+        let status=bvar.statuses[line('.')-1]
+        let modified=status%2
+        execute lwnr.'wincmd w'
+        let fullpath=s:_r.os.path.join(bvar.repo.path, file)
+        if !modified
+            let backupfile=fullpath.'.orig'
+            let i=0
+            while s:_r.os.path.exists(backupfile)
+                let backupfile=fullpath.'.'.i.'.orig'
+                let i+=1
+            endwhile
+        endif
+        if type is# 'modified' || type is# 'added' || type is# 'unknown'
+            if !modified
+                let status=3
+                let bvar.statuses[line('.')-1]=status
+                call setline('.', s:statchars[status].bvar.lines[line('.')-1])
+                let bvar.prevct=b:changedtick
+            else
+                execute 'edit' fnameescape(fullpath)
+                setlocal bufhidden=wipe
+            endif
+            if type is# 'modified'
+                if !modified
+                    execute 'edit' fnameescape('aurum://file:'.epath.':.:'.file)
+                    setlocal noreadonly modifiable bufhidden=wipe
+                endif
+                diffthis
+                execute rwnr.'wincmd w'
+                execute 'edit' fnameescape('aurum://copy:'.fullpath)
+                setlocal readonly nomodifiable bufhidden=wipe
+                diffthis
+                wincmd p
+            elseif !modified
+                execute 'edit' fnameescape('aurum://copy:'.fullpath)
+                setlocal noreadonly modifiable bufhidden=wipe
+            endif
+            if !modified
+                setlocal buftype=acwrite
+                let isexe=executable(fullpath)
+                if rename(fullpath, backupfile)
+                    call s:_f.warn('renfail', fullpath, backupfile)
+                    setlocal readonly nomodifiable
+                    return
+                endif
+                augroup AuRecordLeft
+                    execute 'autocmd BufWriteCmd <buffer> w! '.
+                                \fnameescape(fullpath)
+                augroup END
+                let bvar.backupfiles[backupfile]=fullpath
+                write
+                if isexe && s:_r.os.name is# 'posix'
+                    call s:_r.os.run(['chmod', '+x', fullpath])
+                endif
+            endif
+        endif
+    elseif a:action is# 'commit'
+        " TODO
+        call s:F.rec.unload(bvar)
+        return
+    endif
+    if bufnr('%')==buf && b:changedtick!=bvar.prevct
+        let bvar.prevct=b:changedtick
+        if bvar.reset
+            let &l:undolevels=bvar.undolevels
+            unlet bvar.undolevels
+            let bvar.reset=0
+        endif
+    endif
+    setlocal readonly nomodifiable
+endfunction
+let s:_augroups+=['AuRecordLeft']
+"▶3 rec.runleftmap
+function s:F.rec.runleftmap(action)
+    let [lwnr, rwnr, swnr]=s:F.rec.getwnrs()
+    execute swnr.'wincmd w'
+endfunction
+"▶3 rec mappings
+function s:F.rec.gm(...)
+    return ':<C-u>call call(<SID>Eval("s:F.rec.runstatmap"),'.string(a:000).','.
+                \          '{})<CR>'
+endfunction
+function s:F.rec.gml(...)
+    return ':<C-u>call call(<SID>Eval("s:F.rec.runleftmap"),'.string(a:000).','.
+                \          '{})<CR>'
+endfunction
+call s:_f.mapgroup.add('AuRecord', {
+            \    'add': {'lhs': 'A', 'rhs': s:F.rec.gm('add')    },
+            \ 'ignore': {'lhs': 'I', 'rhs': s:F.rec.gm('ignore') },
+            \   'edit': {'lhs': 'O', 'rhs': s:F.rec.gm('edit')   },
+            \'discard': {'lhs': 'X', 'rhs': s:F.rec.gm('discard')},
+            \ 'commit': {'lhs': 'C', 'rhs': s:F.rec.gm('commit') },
+            \   'undo': {'lhs': 'u', 'rhs': s:F.rec.gm('undo')   },
+            \   'vadd': {'lhs': 'A', 'rhs': s:F.rec.gm('vadd'),    'mode': 'v'},
+            \'vignore': {'lhs': 'I', 'rhs': s:F.rec.gm('vignore'), 'mode': 'v'},
+        \}, {'mode': 'n', 'silent': 1, 'dontmap': 1})
+call s:_f.mapgroup.add('AuRecordLeft', {
+            \   'save': {'lhs': ',A', 'rhs': s:F.rec.gml('save')},
+        \}, {'mode': 'n', 'silent': 1, 'dontmap': 1, 'leader': ','})
 "▶2 comm
 "▶3 comm.difftobuffer
 function s:F.comm.difftobuffer(repo, buf, ...)
     return [repo]+strings+[opts]
 endfunction
 "▲4
-function s:auefunc.function()
+function s:auefunc.function() abort
     "▶4 Split <amatch> into command and arguments
     let tail=substitute(expand('<amatch>'), '\V\^aurum://', '', '')
     let command=tolower(matchstr(tail, '\v^\w+'))
     let tail=tail[len(command)+1:]
     "▲4
-    setlocal buftype=nofile
+    setlocal buftype=nofile readonly
+    let buf=bufnr('%')
     "▶4 glog command (repo:opts)
     if command is# 'glog'
         let [repo, opts]=s:F.comm.repotupleoptssplit(tail, 0)
                     \     'has_key(opts, v:val)')
             let opts[key]=+opts[key]
         endfor
-        let s:bufvars[bufnr('%')]={'repo': repo, 'opts': opts}
+        let s:bufvars[buf]={'repo': repo, 'opts': opts}
         setlocal filetype=aurumgraphlog bufhidden=wipe
         runtime ftplugin/aurumgraphlog.vim
     "▶4 file command (repo:rev:file)
             setlocal binary noendofline
         endif
         call setline('.', fcontents)
+        let s:bufvars[buf]={'repo': repo, 'rev': rev, 'file': file}
     "▶4 annotate command (repo:rev:file)
     elseif command is# 'annotate'
         let [repo, rev, file]=s:F.comm.repotuplesplit(tail, 2)
-        let s:bufvars[bufnr('%')]={'repo': repo, 'rev': rev, 'file': file}
+        let s:bufvars[buf]={'repo': repo, 'rev': rev, 'file': file}
         setlocal filetype=aurumannotate bufhidden=wipe
         runtime ftplugin/aurumannotate.vim
     "▶4 diff command (repo:rev1:rev2:files:opts)
                         \                 'index(filelist, v:val)==-1')
         endfor
         "▲5
-        let buf=bufnr('%')
         let s:bufvars[buf]={'repo': repo, 'rev1': rev1, 'rev2': rev2,
                     \       'files': filelist,}
         setlocal filetype=diff
     "▶4 status command (repo:opts)
     elseif command is# 'status'
         let [repo, opts]=s:F.comm.repotupleoptssplit(tail, 0)
-        let buf=bufnr('%')
         if has_key(opts, 'files')
             let opts.filepats=map(split(opts.files, '\v%(\\@<!\\%(\\\\)*)@<!;'),
                         \         's:F.comm.globtopattern(v:val)')
         let s:bufvars[buf]={'repo': repo, 'opts': opts,}
         setlocal filetype=aurumstatus bufhidden=wipe
         runtime ftplugin/aurumstatus.vim
+    "▶4 copy command (file)
+    elseif command is# 'copy'
+        let fcontents=readfile(tail)
+        if empty(fcontents[-1])
+            call remove(fcontents, -1)
+        else
+            setlocal binary noendofline
+        endif
+        call setline('.', fcontents)
+        let s:bufvars[buf]={'file': tail}
     "▶4 noop command (do nothing)
     elseif command is# 'noop'
+        if !has_key(s:bufvars, buf)
+            let s:bufvars[buf]={}
+        endif
     endif
 endfunction
 "▶3 glogfunc
     setlocal filetype=aurumgraphlog bufhidden=wipe
     runtime ftplugin/aurumgraphlog.vim
 endfunction
-let s:repoarg=':*F.comm.getrepo(".") (either(path d, '.
-            \                               'match @\v^\w+%(\+\w+)*\V://@) '.
-            \                        '|*F.comm.getrepo)'
 let s:glogfunc['@FWC']=['['.s:repoarg.']'.
             \           '{ *?files    (type "")'.
             \           '  *?ignfiles in [patch renames diff] ~start'.
 "▶3 statfunc
 function s:statfunc.function(repo, opts)
     let opts=[]
+    if has_key(a:opts, 'files')
+        call map(a:opts.files, 'join('.
+                    \          's:_r.os.path.split('.
+                    \          's:_r.os.path.relpath(v:val, a:repo.path))[1:],'.
+                    \               '"/")')
+    endif
     for [key, value] in items(a:opts)
         let opt=key.':'
         if key is# 'files'

File syntax/aurumstatus.vim

View file
  • Ignore whitespace
     finish
 endif
 
-syn region auStatusModified matchgroup=auStatusModifiedStart start=/\m^M / end=/\v$/ contains=auStatusFile
-syn region auStatusAdded    matchgroup=auStatusAddedStart    start=/\m^A / end=/\v$/ contains=auStatusFile
-syn region auStatusRemoved  matchgroup=auStatusRemovedStart  start=/\m^R / end=/\v$/ contains=auStatusFile
-syn region auStatusDeleted  matchgroup=auStatusDeletedStart  start=/\m^! / end=/\v$/ contains=auStatusFile
-syn region auStatusUnknown  matchgroup=auStatusUnknownStart  start=/\m^? / end=/\v$/ contains=auStatusFile
-syn region auStatusIgnored  matchgroup=auStatusIgnoredStart  start=/\m^I / end=/\v$/ contains=auStatusFile
-syn region auStatusClean    matchgroup=auStatusCleanStart    start=/\m^C / end=/\v$/ contains=auStatusFile
+syn region auStatusModified matchgroup=auStatusModifiedStart start=/\m^[\-\^+*]\?M / end=/\v$/ contains=auStatusFile
+syn region auStatusAdded    matchgroup=auStatusAddedStart    start=/\m^[\-\^+*]\?A / end=/\v$/ contains=auStatusFile
+syn region auStatusRemoved  matchgroup=auStatusRemovedStart  start=/\m^[\-\^+*]\?R / end=/\v$/ contains=auStatusFile
+syn region auStatusDeleted  matchgroup=auStatusDeletedStart  start=/\m^[\-\^+*]\?! / end=/\v$/ contains=auStatusFile
+syn region auStatusUnknown  matchgroup=auStatusUnknownStart  start=/\m^[\-\^+*]\?? / end=/\v$/ contains=auStatusFile
+syn region auStatusIgnored  matchgroup=auStatusIgnoredStart  start=/\m^[\-\^+*]\?I / end=/\v$/ contains=auStatusFile
+syn region auStatusClean    matchgroup=auStatusCleanStart    start=/\m^[\-\^+*]\?C / end=/\v$/ contains=auStatusFile
 
 hi def link auStatusModifiedStart  auStatusModified
 hi def link auStatusAddedStart     auStatusAdded