" Vim plugin for diffing when swap file was found " --------------------------------------------------------------- " Author: Christian Brabandt " Version: 0.18 " Last Change: Wed, 14 Aug 2013 22:39:13 +0200 " Script: http://www.vim.org/scripts/script.php?script_id=3068 " License: VIM License " GetLatestVimScripts: 3068 18 :AutoInstall: recover.vim " fu! recover#Recover(on) "{{{1 if a:on call s:ModifySTL(1) if !exists("s:old_vsc") let s:old_vsc = v:swapchoice endif augroup Swap au! au SwapExists * nested :call recover#ConfirmSwapDiff() au BufWinEnter,InsertEnter,InsertLeave,FocusGained * \ call CheckSwapFileExists() augroup END else augroup Swap au! augroup end if exists("s:old_vsc") let v:swapchoice=s:old_vsc endif endif endfu fu! s:Swapname() "{{{1 " Use sil! so a failing redir (e.g. recursive redir call) " won't hurt. (https://github.com/chrisbra/Recover.vim/pull/8) sil! redir => a |sil swapname|redir end if a[1:] == 'No swap file' return '' else return a[1:] endif endfu fu! s:CheckSwapFileExists() "{{{1 if !&swapfile return endif let swap = s:Swapname() if !empty(swap) && !filereadable(swap) " previous SwapExists autocommand deleted our swapfile, " recreate it and avoid E325 Message call s:SetSwapfile() endif endfu fu! s:CheckRecover() "{{{1 if exists("b:swapname") && !exists("b:did_recovery") let t = tempname() " Doing manual recovery, otherwise, BufRead autocmd seems to " get into the way of the recovery try exe 'recover' fnameescape(expand('%:p')) catch /^Vim\%((\a\+)\)\=:E/ " Prevent any recovery error from disrupting the diff-split. endtry exe ':sil w' t call system('diff '. shellescape(expand('%:p'),1). \ ' '. shellescape(t,1)) call delete(t) if !v:shell_error call inputsave() redraw! " prevent overwriting of 'Select File to use for recovery dialog' let p = confirm("No differences: Delete old swap file '".b:swapname."'?", \ "&No\n&Yes", 2) call inputrestore() if p == 2 " Workaround for E305 error let v:swapchoice='' call delete(b:swapname) " can trigger SwapExists autocommands again! call s:SetSwapfile() endif call recover#AutoCmdBRP(0) else echo "Found Swapfile '". b:swapname. "', showing diff!" call recover#DiffRecoveredFile() " Not sure, why this needs feedkeys " Sometimes cursor is wrong, I hate when this happens " Cursor is wrong only when there is a single buffer open, a simple " workaround for that is to check if bufnr('') is 1 and total number " of windows in current tab is less than 3 (i.e. no windows were " autoopen): in this case ':wincmd l\n:0\n' must be fed to " feedkeys if bufnr('') == 1 && winnr('$') < 3 call feedkeys(":wincmd l\", 't') endif if !(v:version > 703 || (v:version == 703 && has("patch708"))) call feedkeys(":0\", 't') endif endif let b:did_recovery = 1 " Don't delete the auto command yet. "call recover#AutoCmdBRP(0) endif endfun fu! recover#ConfirmSwapDiff() "{{{1 if exists("b:swapchoice") let v:swapchoice = b:swapchoice return endif let delete = 0 let do_modification_check = exists("g:RecoverPlugin_Edit_Unmodified") ? g:RecoverPlugin_Edit_Unmodified : 0 let not_modified = 0 let msg = "" let bufname = s:isWin() ? fnamemodify(expand('%'), ':p:8') : shellescape(expand('%')) let tfile = tempname() if executable('vim') && !s:isWin() " Doesn't work on windows (system() won't be able to fetch the output) " Capture E325 Warning message " Leave English output, so parsing will be easier " TODO: make it work on windows. if s:isWin() let wincmd = printf('-c "redir > %s|1d|:q!" ', tfile) let wincmd = printf('-c "call feedkeys(\"o\n\e:q!\n\")"') endif let cmd = printf("%svim -u NONE -es -V %s %s", \ (s:isWin() ? '' : 'TERM=vt100 LC_ALL=C '), \ (s:isWin() ? wincmd : ''), \ bufname) let msg = system(cmd) let msg = substitute(msg, '.*\(E325.*process ID:.\{-}\)\%x0d.*', '\1', '') let msg = substitute(msg, "\e\\[\\d\\+C", "", "g") if do_modification_check let not_modified = (match(msg, "modified: no") > -1) endif endif if has("unix") && !empty(msg) && system("uname") =~? "linux" " try to get process name from pid " This is Linux specific. " TODO Is there a portable way to retrive this info for at least unix? let pid_pat = 'process ID:\s*\zs\d\+' let pid = matchstr(msg, pid_pat)+0 if !empty(pid) && isdirectory('/proc') let pname = 'not existing' let proc = '/proc/'. pid. '/status' if filereadable(proc) let pname = matchstr(readfile(proc)[0], '^Name:\s*\zs.*') endif let msg = substitute(msg, pid_pat, '& ['.pname."]\n", '') if not_modified && pname !~? 'vim' let not_modified = 0 endif endif endif if executable('vim') && executable('diff') "&& s:isWin() " Check, whether the files differ issue #7 " doesn't work on Windows? (cmd is ok, should be executable) if s:isWin() let tfile = substitute(tfile, '/', '\\', 'g') endif let cmd = printf("vim -u NONE -N %s -r %s -c \":w %s|:q!\" %s diff %s %s", \ (s:isWin() ? '' : '-es'), \ (s:isWin() ? fnamemodify(v:swapname, ':p:8') : shellescape(v:swapname)), \ tfile, (s:isWin() ? '&' : '&&'), \ bufname, tfile) call system(cmd) " if return code of diff is zero, files are identical let delete = !v:shell_error if !do_modification_check echo msg endif endif call delete(tfile) if delete && !do_modification_check echomsg "Swap and on-disk file seem to be identical" endif let cmd = printf("D&iff\n&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort%s", \ ( (delete || !empty(msg)) ? "\n&Delete" : "")) if !empty(msg) let info = 'Please choose: ' else let info = "Swap File '". v:swapname. "' found: " endif " if has("gui_running") && &go !~ 'c' " call inputsave() " let p = confirm(info, cmd, (modified ? 3 : delete ? 7 : 1), 'I') " else " echo info " call s:Output(cmd) if not_modified let p = 3 else call inputsave() let p = confirm(info, cmd, (delete ? 7 : 1), 'I') " endif call inputrestore() endif let b:swapname=v:swapname if p == 1 || p == 3 " Diff or Edit Anyway call s:SwapChoice('e') " postpone recovering until later, for now, we are opening anyways... " (this is done by s:CheckRecover() " in an BufReadPost autocommand if (p == 1) call recover#AutoCmdBRP(1) endif elseif p == 2 " Open Read-Only " Don't show the Recovery dialog let v:swapchoice='o' call EchoMsg("Found SwapFile, opening file readonly!") sleep 2 elseif p == 4 " Recover let v:swapchoice='r' elseif p == 5 " Quit let v:swapchoice='q' elseif p == 6 " Abort let v:swapchoice='a' elseif p == 7 " Delete Swap file, if not different call s:SwapChoice('d') call EchoMsg("Found SwapFile, deleting...") " might trigger SwapExists again! call s:SetSwapfile() else " Show default menu from vim return endif endfun fu! s:Output(msg) "{{{1 " Display as one string, without linebreaks let msg = substitute(a:msg, '\n', '/', 'g') for item in split(msg, '&') echohl WarningMsg echon item[0] echohl Normal echon item[1:] endfor endfun fu! s:SwapChoice(char) "{{{1 let v:swapchoice = a:char let b:swapchoice = a:char endfu fu! recover#DiffRecoveredFile() "{{{1 " recovered version diffthis let b:mod='recovered version' let l:filetype = &ft if has("balloon_eval") set ballooneval setl bexpr=recover#BalloonExprRecover() endif " saved version let curspr = &spr set nospr noa vert new let &l:spr = curspr if !empty(glob(fnameescape(expand('#')))) 0r # $d _ endif if l:filetype != "" exe "setl filetype=".l:filetype endif exe "f! " . escape(expand("")," ") . \ escape(' (on-disk version)', ' ') diffthis setl noswapfile buftype=nowrite bufhidden=delete nobuflisted let b:mod='unmodified version on-disk' let swapbufnr=bufnr('') if has("balloon_eval") set ballooneval setl bexpr=recover#BalloonExprRecover() endif noa wincmd l let b:swapbufnr = swapbufnr command! -buffer RecoverPluginFinish :FinishRecovery command! -buffer FinishRecovery :call recover#RecoverFinish() setl modified endfu fu! recover#Help() "{{{1 echohl Title echo "Diff key mappings\n". \ "-----------------\n" echo "Normal mode commands:\n" echohl Normal echo "]c - next diff\n". \ "[c - prev diff\n". \ "do - diff obtain - get change from other window\n". \ "dp - diff put - put change into other window\n" echohl Title echo "Ex-commands:\n" echohl Normal echo ":[range]diffget - get changes from other window\n". \ ":[range]diffput - put changes into other window\n". \ ":RecoverPluginDisable - DisablePlugin\n". \ ":RecoverPluginEnable - EnablePlugin\n". \ ":RecoverPluginHelp - this help" if exists(":RecoverPluginFinish") echo ":RecoverPluginFinish - finish recovery" endif endfun fu! s:EchoMsg(msg) "{{{1 echohl WarningMsg uns echomsg a:msg echohl Normal endfu fu! s:ModifySTL(enable) "{{{1 if a:enable " Inject some info into the statusline let s:ostl = &stl let s:nstl = substitute(&stl, '%f', \ "\\0 %{exists('b:mod')?('['.b:mod.']') : ''}", 'g') let &l:stl = s:nstl else " Restore old statusline setting if exists("s:ostl") && s:nstl == &stl let &stl=s:ostl endif endif endfu fu! s:SetSwapfile() "{{{1 if &l:swf " Reset swapfile to use .swp extension sil setl noswapfile swapfile endif endfu fu! s:isWin() "{{{1 return has("win32") || has("win16") || has("win64") endfu fu! recover#BalloonExprRecover() "{{{1 " Set up a balloon expr. if exists("b:swapbufnr") && v:beval_bufnr!=?b:swapbufnr return "This buffer shows the recovered and modified version of your file" else return "This buffer shows the unmodified version of your file as it is stored on disk" endif endfun fu! recover#RecoverFinish() abort "{{{1 let swapname = b:swapname let curbufnr = bufnr('') delcommand FinishRecovery exe bufwinnr(b:swapbufnr) " wincmd w" diffoff bd! call delete(swapname) diffoff call s:ModifySTL(0) exe bufwinnr(curbufnr) " wincmd w" call s:SetSwapfile() unlet! b:swapname b:did_recovery b:swapbufnr b:swapchoice endfun fu! recover#AutoCmdBRP(on) "{{{1 if a:on && !exists("#SwapBRP") augroup SwapBRP au! au BufNewFile,BufReadPost :call s:CheckRecover() augroup END elseif !a:on && exists('#SwapBRP') augroup SwapBRP au! augroup END augroup! SwapBRP endif endfu " Old functions, not used anymore "{{{1 finish fu! recover#DiffRecoveredFileOld() "{{{2 " For some reason, this only works with feedkeys. " I am not sure why. let histnr = histnr('cmd')+1 call feedkeys(":diffthis\n", 't') call feedkeys(":setl modified\n", 't') call feedkeys(":let b:mod='recovered version'\n", 't') call feedkeys(":let g:recover_bufnr=bufnr('%')\n", 't') let l:filetype = &ft call feedkeys(":vert new\n", 't') call feedkeys(":0r #\n", 't') call feedkeys(":$delete _\n", 't') if l:filetype != "" call feedkeys(":setl filetype=".l:filetype."\n", 't') endif call feedkeys(":f! " . escape(expand("")," ") . "\\ (on-disk\\ version)\n", 't') call feedkeys(":let swapbufnr = bufnr('')\n", 't') call feedkeys(":diffthis\n", 't') call feedkeys(":setl noswapfile buftype=nowrite bufhidden=delete nobuflisted\n", 't') call feedkeys(":let b:mod='unmodified version on-disk'\n", 't') call feedkeys(":exe bufwinnr(g:recover_bufnr) ' wincmd w'"."\n", 't') call feedkeys(":let b:swapbufnr=swapbufnr\n", 't') "call feedkeys(":command! -buffer DeleteSwapFile :call delete(b:swapname)|delcommand DeleteSwapFile\n", 't') call feedkeys(":command! -buffer RecoverPluginFinish :FinishRecovery\n", 't') call feedkeys(":command! -buffer FinishRecovery :call recover#RecoverFinish()\n", 't') call feedkeys(":0\n", 't') if has("balloon_eval") "call feedkeys(':if has("balloon_eval")|:set ballooneval|setl bexpr=recover#BalloonExprRecover()|endif'."\n", 't') call feedkeys(":set ballooneval|setl bexpr=recover#BalloonExprRecover()\n", 't') endif "call feedkeys(":redraw!\n", 't') call feedkeys(":for i in range(".histnr.", histnr('cmd'), 1)|:call histdel('cmd',i)|:endfor\n",'t') call feedkeys(":echo 'Found Swapfile '.b:swapname . ', showing diff!'\n", 'm') " Delete Autocommand call recover#AutoCmdBRP(0) "endif endfu " Modeline "{{{1 " vim:fdl=0