1 " Vim plugin for diffing when swap file was found
2 " ---------------------------------------------------------------
3 " Author: Christian Brabandt <cb@256bit.org>
5 " Last Change: Wed, 14 Aug 2013 22:39:13 +0200
6 " Script: http://www.vim.org/scripts/script.php?script_id=3068
8 " GetLatestVimScripts: 3068 18 :AutoInstall: recover.vim
10 fu! recover#Recover(on) "{{{1
13 if !exists("s:old_vsc")
14 let s:old_vsc = v:swapchoice
18 au SwapExists * nested :call recover#ConfirmSwapDiff()
19 au BufWinEnter,InsertEnter,InsertLeave,FocusGained *
20 \ call <sid>CheckSwapFileExists()
26 if exists("s:old_vsc")
27 let v:swapchoice=s:old_vsc
32 fu! s:Swapname() "{{{1
33 " Use sil! so a failing redir (e.g. recursive redir call)
34 " won't hurt. (https://github.com/chrisbra/Recover.vim/pull/8)
35 sil! redir => a |sil swapname|redir end
36 if a[1:] == 'No swap file'
43 fu! s:CheckSwapFileExists() "{{{1
48 let swap = s:Swapname()
49 if !empty(swap) && !filereadable(swap)
50 " previous SwapExists autocommand deleted our swapfile,
51 " recreate it and avoid E325 Message
56 fu! s:CheckRecover() "{{{1
57 if exists("b:swapname") && !exists("b:did_recovery")
59 " Doing manual recovery, otherwise, BufRead autocmd seems to
60 " get into the way of the recovery
62 exe 'recover' fnameescape(expand('%:p'))
63 catch /^Vim\%((\a\+)\)\=:E/
64 " Prevent any recovery error from disrupting the diff-split.
67 call system('diff '. shellescape(expand('%:p'),1).
68 \ ' '. shellescape(t,1))
72 redraw! " prevent overwriting of 'Select File to use for recovery dialog'
73 let p = confirm("No differences: Delete old swap file '".b:swapname."'?",
77 " Workaround for E305 error
79 call delete(b:swapname)
80 " can trigger SwapExists autocommands again!
83 call recover#AutoCmdBRP(0)
85 echo "Found Swapfile '". b:swapname. "', showing diff!"
86 call recover#DiffRecoveredFile()
87 " Not sure, why this needs feedkeys
88 " Sometimes cursor is wrong, I hate when this happens
89 " Cursor is wrong only when there is a single buffer open, a simple
90 " workaround for that is to check if bufnr('') is 1 and total number
91 " of windows in current tab is less than 3 (i.e. no windows were
92 " autoopen): in this case ':wincmd l\n:0\n' must be fed to
94 if bufnr('') == 1 && winnr('$') < 3
95 call feedkeys(":wincmd l\<cr>", 't')
97 if !(v:version > 703 || (v:version == 703 && has("patch708")))
98 call feedkeys(":0\<cr>", 't')
101 let b:did_recovery = 1
102 " Don't delete the auto command yet.
103 "call recover#AutoCmdBRP(0)
107 fu! recover#ConfirmSwapDiff() "{{{1
108 if exists("b:swapchoice")
109 let v:swapchoice = b:swapchoice
113 let do_modification_check = exists("g:RecoverPlugin_Edit_Unmodified") ? g:RecoverPlugin_Edit_Unmodified : 0
116 let bufname = s:isWin() ? fnamemodify(expand('%'), ':p:8') : shellescape(expand('%'))
117 let tfile = tempname()
118 if executable('vim') && !s:isWin()
119 " Doesn't work on windows (system() won't be able to fetch the output)
120 " Capture E325 Warning message
121 " Leave English output, so parsing will be easier
122 " TODO: make it work on windows.
124 let wincmd = printf('-c "redir > %s|1d|:q!" ', tfile)
125 let wincmd = printf('-c "call feedkeys(\"o\n\e:q!\n\")"')
127 let cmd = printf("%svim -u NONE -es -V %s %s",
128 \ (s:isWin() ? '' : 'TERM=vt100 LC_ALL=C '),
129 \ (s:isWin() ? wincmd : ''),
131 let msg = system(cmd)
132 let msg = substitute(msg, '.*\(E325.*process ID:.\{-}\)\%x0d.*', '\1', '')
133 let msg = substitute(msg, "\e\\[\\d\\+C", "", "g")
134 if do_modification_check
135 let not_modified = (match(msg, "modified: no") > -1)
138 if has("unix") && !empty(msg) && system("uname") =~? "linux"
139 " try to get process name from pid
140 " This is Linux specific.
141 " TODO Is there a portable way to retrive this info for at least unix?
142 let pid_pat = 'process ID:\s*\zs\d\+'
143 let pid = matchstr(msg, pid_pat)+0
144 if !empty(pid) && isdirectory('/proc')
145 let pname = 'not existing'
146 let proc = '/proc/'. pid. '/status'
147 if filereadable(proc)
148 let pname = matchstr(readfile(proc)[0], '^Name:\s*\zs.*')
150 let msg = substitute(msg, pid_pat, '& ['.pname."]\n", '')
151 if not_modified && pname !~? 'vim'
156 if executable('vim') && executable('diff') "&& s:isWin()
157 " Check, whether the files differ issue #7
158 " doesn't work on Windows? (cmd is ok, should be executable)
160 let tfile = substitute(tfile, '/', '\\', 'g')
162 let cmd = printf("vim -u NONE -N %s -r %s -c \":w %s|:q!\" %s diff %s %s",
163 \ (s:isWin() ? '' : '-es'),
164 \ (s:isWin() ? fnamemodify(v:swapname, ':p:8') : shellescape(v:swapname)),
165 \ tfile, (s:isWin() ? '&' : '&&'),
168 " if return code of diff is zero, files are identical
169 let delete = !v:shell_error
170 if !do_modification_check
175 if delete && !do_modification_check
176 echomsg "Swap and on-disk file seem to be identical"
178 let cmd = printf("D&iff\n&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort%s",
179 \ ( (delete || !empty(msg)) ? "\n&Delete" : ""))
181 let info = 'Please choose: '
183 let info = "Swap File '". v:swapname. "' found: "
185 " if has("gui_running") && &go !~ 'c'
187 " let p = confirm(info, cmd, (modified ? 3 : delete ? 7 : 1), 'I')
195 let p = confirm(info, cmd, (delete ? 7 : 1), 'I')
199 let b:swapname=v:swapname
201 " Diff or Edit Anyway
202 call s:SwapChoice('e')
203 " postpone recovering until later, for now, we are opening anyways...
204 " (this is done by s:CheckRecover()
205 " in an BufReadPost autocommand
207 call recover#AutoCmdBRP(1)
211 " Don't show the Recovery dialog
213 call <sid>EchoMsg("Found SwapFile, opening file readonly!")
225 " Delete Swap file, if not different
226 call s:SwapChoice('d')
227 call <sid>EchoMsg("Found SwapFile, deleting...")
228 " might trigger SwapExists again!
231 " Show default menu from vim
236 fu! s:Output(msg) "{{{1
237 " Display as one string, without linebreaks
238 let msg = substitute(a:msg, '\n', '/', 'g')
239 for item in split(msg, '&')
247 fu! s:SwapChoice(char) "{{{1
248 let v:swapchoice = a:char
249 let b:swapchoice = a:char
252 fu! recover#DiffRecoveredFile() "{{{1
255 let b:mod='recovered version'
257 if has("balloon_eval")
259 setl bexpr=recover#BalloonExprRecover()
266 if !empty(glob(fnameescape(expand('#'))))
271 exe "setl filetype=".l:filetype
273 exe "f! " . escape(expand("<afile>")," ") .
274 \ escape(' (on-disk version)', ' ')
276 setl noswapfile buftype=nowrite bufhidden=delete nobuflisted
277 let b:mod='unmodified version on-disk'
278 let swapbufnr=bufnr('')
279 if has("balloon_eval")
281 setl bexpr=recover#BalloonExprRecover()
284 let b:swapbufnr = swapbufnr
285 command! -buffer RecoverPluginFinish :FinishRecovery
286 command! -buffer FinishRecovery :call recover#RecoverFinish()
290 fu! recover#Help() "{{{1
292 echo "Diff key mappings\n".
293 \ "-----------------\n"
294 echo "Normal mode commands:\n"
296 echo "]c - next diff\n".
297 \ "[c - prev diff\n".
298 \ "do - diff obtain - get change from other window\n".
299 \ "dp - diff put - put change into other window\n"
301 echo "Ex-commands:\n"
303 echo ":[range]diffget - get changes from other window\n".
304 \ ":[range]diffput - put changes into other window\n".
305 \ ":RecoverPluginDisable - DisablePlugin\n".
306 \ ":RecoverPluginEnable - EnablePlugin\n".
307 \ ":RecoverPluginHelp - this help"
308 if exists(":RecoverPluginFinish")
309 echo ":RecoverPluginFinish - finish recovery"
315 fu! s:EchoMsg(msg) "{{{1
321 fu! s:ModifySTL(enable) "{{{1
323 " Inject some info into the statusline
325 let s:nstl = substitute(&stl, '%f',
326 \ "\\0 %{exists('b:mod')?('['.b:mod.']') : ''}", 'g')
329 " Restore old statusline setting
330 if exists("s:ostl") && s:nstl == &stl
336 fu! s:SetSwapfile() "{{{1
338 " Reset swapfile to use .swp extension
339 sil setl noswapfile swapfile
344 return has("win32") || has("win16") || has("win64")
346 fu! recover#BalloonExprRecover() "{{{1
347 " Set up a balloon expr.
348 if exists("b:swapbufnr") && v:beval_bufnr!=?b:swapbufnr
349 return "This buffer shows the recovered and modified version of your file"
351 return "This buffer shows the unmodified version of your file as it is stored on disk"
355 fu! recover#RecoverFinish() abort "{{{1
356 let swapname = b:swapname
357 let curbufnr = bufnr('')
358 delcommand FinishRecovery
359 exe bufwinnr(b:swapbufnr) " wincmd w"
362 call delete(swapname)
365 exe bufwinnr(curbufnr) " wincmd w"
367 unlet! b:swapname b:did_recovery b:swapbufnr b:swapchoice
370 fu! recover#AutoCmdBRP(on) "{{{1
371 if a:on && !exists("#SwapBRP")
374 au BufNewFile,BufReadPost <buffer> :call s:CheckRecover()
376 elseif !a:on && exists('#SwapBRP')
383 " Old functions, not used anymore "{{{1
386 fu! recover#DiffRecoveredFileOld() "{{{2
387 " For some reason, this only works with feedkeys.
389 let histnr = histnr('cmd')+1
390 call feedkeys(":diffthis\n", 't')
391 call feedkeys(":setl modified\n", 't')
392 call feedkeys(":let b:mod='recovered version'\n", 't')
393 call feedkeys(":let g:recover_bufnr=bufnr('%')\n", 't')
395 call feedkeys(":vert new\n", 't')
396 call feedkeys(":0r #\n", 't')
397 call feedkeys(":$delete _\n", 't')
399 call feedkeys(":setl filetype=".l:filetype."\n", 't')
401 call feedkeys(":f! " . escape(expand("<afile>")," ") . "\\ (on-disk\\ version)\n", 't')
402 call feedkeys(":let swapbufnr = bufnr('')\n", 't')
403 call feedkeys(":diffthis\n", 't')
404 call feedkeys(":setl noswapfile buftype=nowrite bufhidden=delete nobuflisted\n", 't')
405 call feedkeys(":let b:mod='unmodified version on-disk'\n", 't')
406 call feedkeys(":exe bufwinnr(g:recover_bufnr) ' wincmd w'"."\n", 't')
407 call feedkeys(":let b:swapbufnr=swapbufnr\n", 't')
408 "call feedkeys(":command! -buffer DeleteSwapFile :call delete(b:swapname)|delcommand DeleteSwapFile\n", 't')
409 call feedkeys(":command! -buffer RecoverPluginFinish :FinishRecovery\n", 't')
410 call feedkeys(":command! -buffer FinishRecovery :call recover#RecoverFinish()\n", 't')
411 call feedkeys(":0\n", 't')
412 if has("balloon_eval")
413 "call feedkeys(':if has("balloon_eval")|:set ballooneval|setl bexpr=recover#BalloonExprRecover()|endif'."\n", 't')
414 call feedkeys(":set ballooneval|setl bexpr=recover#BalloonExprRecover()\n", 't')
416 "call feedkeys(":redraw!\n", 't')
417 call feedkeys(":for i in range(".histnr.", histnr('cmd'), 1)|:call histdel('cmd',i)|:endfor\n",'t')
418 call feedkeys(":echo 'Found Swapfile '.b:swapname . ', showing diff!'\n", 'm')
420 call recover#AutoCmdBRP(0)