Fix missing slashes in short dir.
[profile.git] / .vim / autoload / recover.vim
1 " Vim plugin for diffing when swap file was found
2 " ---------------------------------------------------------------
3 " Author: Christian Brabandt <cb@256bit.org>
4 " Version: 0.18
5 " Last Change: Wed, 14 Aug 2013 22:39:13 +0200
6 " Script:  http://www.vim.org/scripts/script.php?script_id=3068
7 " License: VIM License
8 " GetLatestVimScripts: 3068 18 :AutoInstall: recover.vim
9 "
10 fu! recover#Recover(on) "{{{1
11     if a:on
12         call s:ModifySTL(1)
13         if !exists("s:old_vsc")
14             let s:old_vsc = v:swapchoice
15         endif
16         augroup Swap
17             au!
18             au SwapExists * nested :call recover#ConfirmSwapDiff()
19             au BufWinEnter,InsertEnter,InsertLeave,FocusGained * 
20                         \ call <sid>CheckSwapFileExists()
21         augroup END
22     else
23         augroup Swap
24             au!
25         augroup end
26         if exists("s:old_vsc")
27             let v:swapchoice=s:old_vsc
28         endif
29     endif
30 endfu
31
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'
37         return ''
38     else
39         return a[1:]
40     endif
41 endfu
42
43 fu! s:CheckSwapFileExists() "{{{1
44     if !&swapfile 
45         return
46     endif
47
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
52         call s:SetSwapfile()
53     endif
54 endfu
55
56 fu! s:CheckRecover() "{{{1
57     if exists("b:swapname") && !exists("b:did_recovery")
58         let t = tempname()
59         " Doing manual recovery, otherwise, BufRead autocmd seems to
60         " get into the way of the recovery
61         try
62             exe 'recover' fnameescape(expand('%:p'))
63         catch /^Vim\%((\a\+)\)\=:E/
64             " Prevent any recovery error from disrupting the diff-split.
65         endtry
66         exe ':sil w' t
67         call system('diff '. shellescape(expand('%:p'),1).
68                     \ ' '. shellescape(t,1))
69         call delete(t)
70         if !v:shell_error
71             call inputsave()
72             redraw! " prevent overwriting of 'Select File to use for recovery dialog'
73             let p = confirm("No differences: Delete old swap file '".b:swapname."'?",
74                     \ "&No\n&Yes", 2)
75             call inputrestore()
76             if p == 2
77                 " Workaround for E305 error
78                 let v:swapchoice=''
79                 call delete(b:swapname)
80                 " can trigger SwapExists autocommands again!
81                 call s:SetSwapfile()
82             endif
83             call recover#AutoCmdBRP(0)
84         else
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
93             " feedkeys
94             if bufnr('') == 1 && winnr('$') < 3
95                 call feedkeys(":wincmd l\<cr>", 't')
96             endif
97             if !(v:version > 703 || (v:version == 703 && has("patch708")))
98                 call feedkeys(":0\<cr>", 't')
99             endif
100         endif
101         let b:did_recovery = 1
102         " Don't delete the auto command yet.
103         "call recover#AutoCmdBRP(0)
104     endif
105 endfun
106
107 fu! recover#ConfirmSwapDiff() "{{{1
108     if exists("b:swapchoice")
109         let v:swapchoice = b:swapchoice
110         return
111     endif
112     let delete = 0
113     let do_modification_check = exists("g:RecoverPlugin_Edit_Unmodified") ? g:RecoverPlugin_Edit_Unmodified : 0
114     let not_modified = 0
115     let msg = ""
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.
123         if s:isWin()
124           let wincmd = printf('-c "redir > %s|1d|:q!" ', tfile)
125           let wincmd = printf('-c "call feedkeys(\"o\n\e:q!\n\")"')
126         endif
127         let cmd = printf("%svim -u NONE -es -V %s %s",
128             \ (s:isWin() ? '' : 'TERM=vt100 LC_ALL=C '),
129             \ (s:isWin() ? wincmd : ''),
130             \ bufname)
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)
136         endif
137     endif
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.*')
149             endif
150             let msg = substitute(msg, pid_pat, '& ['.pname."]\n", '')
151             if not_modified && pname !~? 'vim'
152                 let not_modified = 0
153             endif
154         endif
155     endif
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)
159         if s:isWin()
160             let tfile = substitute(tfile, '/', '\\', 'g')
161         endif
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() ? '&' : '&&'),
166                     \ bufname, tfile)
167         call system(cmd)
168         " if return code of diff is zero, files are identical
169         let delete = !v:shell_error
170         if !do_modification_check
171             echo msg
172         endif
173     endif
174     call delete(tfile)
175     if delete && !do_modification_check
176         echomsg "Swap and on-disk file seem to be identical"
177     endif
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" : ""))
180     if !empty(msg)
181         let info = 'Please choose: '
182     else
183         let info = "Swap File '". v:swapname. "' found: "
184     endif
185 "    if has("gui_running") && &go !~ 'c'
186 "       call inputsave()
187 "       let p = confirm(info, cmd, (modified ? 3 : delete ? 7 : 1), 'I')
188 "    else
189 "       echo info
190 "       call s:Output(cmd)
191     if not_modified
192         let p = 3
193     else
194         call inputsave()
195         let p = confirm(info, cmd, (delete ? 7 : 1), 'I')
196     "    endif
197         call inputrestore()
198     endif
199     let b:swapname=v:swapname
200     if p == 1 || p == 3
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
206         if (p == 1)
207             call recover#AutoCmdBRP(1)
208         endif
209     elseif p == 2
210         " Open Read-Only
211         " Don't show the Recovery dialog
212         let v:swapchoice='o'
213         call <sid>EchoMsg("Found SwapFile, opening file readonly!")
214         sleep 2
215     elseif p == 4
216         " Recover
217         let v:swapchoice='r'
218     elseif p == 5
219         " Quit
220         let v:swapchoice='q'
221     elseif p == 6
222         " Abort
223         let v:swapchoice='a'
224     elseif p == 7
225         " Delete Swap file, if not different
226         call s:SwapChoice('d')
227         call <sid>EchoMsg("Found SwapFile, deleting...")
228         " might trigger SwapExists again!
229         call s:SetSwapfile()
230     else
231         " Show default menu from vim
232         return
233     endif
234 endfun
235
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, '&')
240         echohl WarningMsg
241         echon item[0]
242         echohl Normal
243         echon item[1:]
244     endfor
245 endfun
246
247 fu! s:SwapChoice(char) "{{{1
248     let v:swapchoice = a:char
249     let b:swapchoice = a:char
250 endfu
251
252 fu! recover#DiffRecoveredFile() "{{{1
253     " recovered version
254     diffthis
255     let b:mod='recovered version'
256     let l:filetype = &ft
257     if has("balloon_eval")
258         set ballooneval
259         setl bexpr=recover#BalloonExprRecover()
260     endif
261     " saved version
262     let curspr = &spr
263     set nospr
264     noa vert new
265     let &l:spr = curspr
266     if !empty(glob(fnameescape(expand('#'))))
267         0r #
268         $d _
269     endif
270     if l:filetype != ""
271         exe "setl filetype=".l:filetype
272     endif
273     exe "f! " . escape(expand("<afile>")," ") .
274             \ escape(' (on-disk version)', ' ')
275     diffthis
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")
280         set ballooneval
281         setl bexpr=recover#BalloonExprRecover()
282     endif
283     noa wincmd l
284     let b:swapbufnr = swapbufnr
285     command! -buffer RecoverPluginFinish :FinishRecovery
286     command! -buffer FinishRecovery :call recover#RecoverFinish()
287     setl modified
288 endfu
289
290 fu! recover#Help() "{{{1
291     echohl Title
292     echo "Diff key mappings\n".
293     \ "-----------------\n"
294     echo "Normal mode commands:\n"
295     echohl Normal
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"
300     echohl Title
301     echo "Ex-commands:\n"
302     echohl Normal
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"
310     endif
311 endfun
312
313
314
315 fu! s:EchoMsg(msg) "{{{1
316     echohl WarningMsg
317     uns echomsg a:msg
318     echohl Normal
319 endfu
320
321 fu! s:ModifySTL(enable) "{{{1
322     if a:enable
323         " Inject some info into the statusline
324         let s:ostl = &stl
325         let s:nstl = substitute(&stl, '%f',
326                 \ "\\0 %{exists('b:mod')?('['.b:mod.']') : ''}", 'g')
327         let &l:stl = s:nstl
328     else
329         " Restore old statusline setting
330         if exists("s:ostl") && s:nstl == &stl
331             let &stl=s:ostl
332         endif
333     endif
334 endfu
335
336 fu! s:SetSwapfile() "{{{1
337     if &l:swf
338         " Reset swapfile to use .swp extension
339         sil setl noswapfile swapfile
340     endif
341 endfu
342
343 fu! s:isWin() "{{{1
344     return has("win32") || has("win16") || has("win64")
345 endfu
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"
350     else
351         return "This buffer shows the unmodified version of your file as it is stored on disk"
352     endif
353 endfun
354
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"
360     diffoff
361     bd!
362     call delete(swapname)
363     diffoff
364     call s:ModifySTL(0)
365     exe bufwinnr(curbufnr) " wincmd w"
366     call s:SetSwapfile()
367     unlet! b:swapname b:did_recovery b:swapbufnr b:swapchoice
368 endfun
369
370 fu! recover#AutoCmdBRP(on) "{{{1
371     if a:on && !exists("#SwapBRP")
372         augroup SwapBRP
373             au!
374             au BufNewFile,BufReadPost <buffer> :call s:CheckRecover()
375         augroup END
376     elseif !a:on && exists('#SwapBRP')
377         augroup SwapBRP
378             au!
379         augroup END
380         augroup! SwapBRP
381     endif
382 endfu
383 " Old functions, not used anymore "{{{1
384 finish
385
386 fu! recover#DiffRecoveredFileOld() "{{{2
387         " For some reason, this only works with feedkeys.
388         " I am not sure  why.
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')
394         let l:filetype = &ft
395         call feedkeys(":vert new\n", 't')
396         call feedkeys(":0r #\n", 't')
397         call feedkeys(":$delete _\n", 't')
398         if l:filetype != ""
399             call feedkeys(":setl filetype=".l:filetype."\n", 't')
400         endif
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')
415         endif
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')
419         " Delete Autocommand
420         call recover#AutoCmdBRP(0)
421     "endif
422 endfu
423
424
425 " Modeline "{{{1
426 " vim:fdl=0