* /etc/skel/.zshrc: add functions for mercurial
[grml-etc-core.git] / etc / skel / .vim / plugin / DirDiff.vim
1 " -*- vim -*-
2 " FILE: "C:\Documents and Settings\William Lee\vimfiles\plugin\DirDiff.vim" {{{
3 " LAST MODIFICATION: "Wed, 22 Feb 2006 22:31:59 Central Standard Time"
4 " HEADER MAINTAINED BY: N/A
5 " VERSION: 1.1.0
6 " (C) 2001-2006 by William Lee, <wl1012@yahoo.com>
7 " }}}
8
9 " Source taken from:
10 " http://www.vim.org/scripts/script.php?script_id=102
11 "
12 " PURPOSE: {{{
13 "   - Diffing a directory recursively and enable easy merging, copying and
14 "   deleting of files and directories.
15 "
16 " REQUIREMENTS:
17 "   - Make sure you have GNU diff in your path on Unix and Windows. I only
18 "     tested this on cygwin's version on Windows.  If you have a diff that
19 "     doesn't support -x or -I flag, do not set variable g:DirDiffExcludes and
20 "     g:DirDiffIgnore to "".  It should still work.
21 "   - On Windows, you need to have "xcopy", "copy", "del", and "rd" in your
22 "     path.
23 "   - On Unix, you need to have "rm" and "cp" in your path.
24 "
25 " USAGE:
26 "   Put this file in your ~/.vim/plugin
27
28 "   Doing the following will generate a diff window.
29 "
30 "       :DirDiff <A:Src Directory> <B:Src Directory>
31 "   e.g.
32 "       :DirDiff ../something/dir1 /usr/bin/somethingelse/dir2
33 "
34 "   The following commands can be used inside the diff window:
35 "   'Enter','o' - Diff open: open the diff file(s) where your cursor is at
36 "   's' - Synchronize the current diff.  You can also select
37 "         a range (through visual) and press 's' to synchronize differences
38 "         across a range.
39 "
40 "         - There are 6 Options you can choose when you hit 's':
41 "           1. A -> B
42 "              Copy A to overwrite B
43 "              If A's file actually points to a directory, it'll copy it to B
44 "              recursively.
45 "           2. B -> A
46 "              Copy B to overwrite A
47 "              If B's file actually points to a directory, it'll copy it to A
48 "              recursively.
49 "           3. Always A
50 "              For the rest of the items that you've selected,
51 "              synchronize like (1).
52 "           4. Always B
53 "              For the rest of the items that you've selected,
54 "              synchronize like (2).
55 "           5. Skip
56 "              Skip this diff entry.
57 "           6. Cancel
58 "              Quit the loop and exit.
59 "
60 "   'u' - Diff update: update the diff window
61 "   'x' - Sets the exclude pattern, separated by ','
62 "   'i' - Sets the ignore pattern, separated by ','
63 "   'a' - Sets additional arguments for diff, eg. -w to ignore white space,
64 "         etc.
65 "   'q' - Quit DirDiff
66 "    
67 "   The following comamnds can be used in the Vim diff mode
68 "   \dg - Diff get: maps to :diffget<CR>
69 "   \dp - Diff put: maps to :diffput<CR>
70 "   \dj - Diff next: (think j for down) 
71 "   \dk - Diff previous: (think k for up)
72 "
73 "   You can set the following DirDiff variables.  You can add the following
74 "   "let" lines in your .vimrc file.
75 "
76 "   Sets default exclude pattern:
77 "       let g:DirDiffExcludes = "CVS,*.class,*.exe,.*.swp"
78 "
79 "   Sets default ignore pattern:
80 "       let g:DirDiffIgnore = "Id:,Revision:,Date:"
81 "
82 "   If DirDiffSort is set to 1, sorts the diff lines.
83 "       let g:DirDiffSort = 1
84 "
85 "   Sets the diff window (bottom window) height (rows)
86 "       let g:DirDiffWindowSize = 14
87 "
88 "   Ignore case during diff
89 "       let g:DirDiffIgnoreCase = 0
90 "
91 "   Dynamically figure out the diff text.  If you are using and i18n version
92 "   of diff, this will try to get the specific diff text during runtime.  It's
93 "   turned off by default.  If you are always targetting a specific version of
94 "   diff, you can turn this off and set the DirDiffText* variables
95 "   accordingly.
96 "       let g:DirDiffDynamicDiffText = 0
97 "
98 "   String used for the English equivalent "Files "
99 "       let g:DirDiffTextFiles = "Files "
100
101 "   String used for the English equivalent " and "
102 "       let g:DirDiffTextAnd = " and "
103 "
104 "   String used for the English equivalent " differ")
105 "       let g:DirDiffTextDiffer = " differ"
106 "
107 "   String used for the English equivalent "Only in ")
108 "       let g:DirDiffTextOnlyIn = "Only in "
109 "
110 " NOTES:
111 "   This script can copy and remove your files.  This can be powerful (or too
112 "   powerful) at times.  Please do not blame me if you use this and
113 "   disintegrate your hard work.  Be warned!
114 "
115 " CREDITS:
116 "
117 "   Please mail any comment/suggestion/patch to 
118 "   William Lee <wl1012@yahoo.com>
119 "
120 " LICENSE:
121 "   Copyright (c) 2001-2006 William Lee
122 "   All rights reserved.
123 "
124 "   Redistribution and use in source and binary forms, with or without
125 "   modification, are permitted provided that the following conditions are
126 "   met:
127 "
128 "     * Redistributions of source code must retain the above copyright
129 "       notice, this list of conditions and the following disclaimer.
130 "     * Redistributions in binary form must reproduce the above copyright
131 "       notice, this list of conditions and the following disclaimer in the
132 "       documentation and/or other materials provided with the distribution.
133 "     * Neither the name William Lee nor the names of its contributors may be
134 "       used to endorse or promote products derived from this software without
135 "       specific prior written permission.
136 "
137 "   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
138 "   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
139 "   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
140 "   WILLIAM LEE AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
141 "   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
142 "   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
143 "   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
144 "   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
145 "   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
146 "   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
147 "
148 " THANKS:
149 "
150 "   Florian Delizy for the i18n diff patch
151 "   Robert Webb for his sorting function
152 "   Salman Halim, Yosuke Kimura, and others for their suggestions
153 "
154 " HISTORY:
155 "  1.1.0  - Added support for i18n (tested on a French version for now only).
156 "           Can dynamically figure out the diff strings output by diff.
157 "  1.0.2  - Fixed a small typo bug in the quit function.
158 "  1.0.1  - Ensure the path separator is correct when running in W2K
159 "  1.0  - Fixed a bug that flags errors if the user use the nowrapscan option.
160 "         Implements a quit function that exit the diff windows.
161 "  0.94 - Fixed a bug where the diff will give incorrect A and B file due to
162 "         similarity of directory names.  Allow you to modify the diff
163 "         argument.
164 "  0.93 - Opps, messed up the key mapping usage.
165 "  0.92 - Doesn't use n and p mappings since it confuses the search next key
166 "         mapping and causes some bugs.  Minor modification to change the
167 "         exclude and ignore pattern.
168 "  0.91 - Clean up delete routine.
169 "       - Added interactive mode.
170 "       - Added multiple entries of exclude and ignore pattern.  
171 "       - Custom configuration through global variables.
172 "       - Change exclude and ignore patterns on the fly.
173 "        
174 "  0.9  - Reorganization of the interface.  Much simplier dialog for
175 "         synchronization.  Support for range synchronization option (REALLY
176 "         powerful)
177 "       - Removed unnecessary key bindings.  All key bindings are local to
178 "         the diff window. (except for the \dg and \dp)
179 "
180 "  0.8  - Added syntax highlighting.
181 "       - Enter and double-click in buffer opens diff.
182 "       - Removed dependency on "sort"
183 "       - Removed usage of registry and marker
184 "       - Code cleanup and some bug fixes
185 "       - On Windows the diff command will use the -i flag instead
186 "       - Changed mappings for diff next (\dj) and diff previous (\dk)
187 "       - Added mappings for vim diff mode (\dg, \dp)
188 "
189 "  0.7  Initial Release
190 "
191 " }}}
192
193 " Public Interface:
194 command! -nargs=* -complete=dir DirDiff call <SID>DirDiff (<f-args>)
195 command! -nargs=0 DirDiffOpen call <SID>DirDiffOpen ()
196 command! -nargs=0 DirDiffNext call <SID>DirDiffNext ()
197 command! -nargs=0 DirDiffPrev call <SID>DirDiffPrev ()
198 command! -nargs=0 DirDiffUpdate call <SID>DirDiffUpdate ()
199 command! -nargs=0 DirDiffQuit call <SID>DirDiffQuit ()
200
201 if !hasmapto('<Plug>DirDiffGet')
202   map <unique> <Leader>dg <Plug>DirDiffGet
203 endif
204 if !hasmapto('<Plug>DirDiffPut')
205   map <unique> <Leader>dp <Plug>DirDiffPut
206 endif
207 if !hasmapto('<Plug>DirDiffNext')
208   map <unique> <Leader>dj <Plug>DirDiffNext
209 endif
210 if !hasmapto('<Plug>DirDiffPrev')
211   map <unique> <Leader>dk <Plug>DirDiffPrev
212 endif
213
214 " Global Maps:
215 map <unique> <script> <Plug>DirDiffGet    :diffget<CR>
216 map <unique> <script> <Plug>DirDiffPut    :diffput<CR>
217 map <unique> <script> <Plug>DirDiffNext    :call <SID>DirDiffNext()<CR>
218 map <unique> <script> <Plug>DirDiffPrev    :call <SID>DirDiffPrev()<CR>
219 map <unique> <script> <Plug>DirDiffQuit    :call <SID>DirDiffQuit()<CR>
220
221 " Default Variables.  You can override these in your global variables
222 " settings.
223 "
224 " For DirDiffExcludes and DirDiffIgnore, separate different patterns with a
225 " ',' (comma and no space!).
226 "
227 " eg. in your .vimrc file: let g:DirDiffExcludes = "CVS,*.class,*.o"
228 "                          let g:DirDiffIgnore = "Id:"
229 "                          " ignore white space in diff
230 "                          let g:DirDiffAddArgs = "-w" 
231 "
232 " You can set the pattern that diff excludes.  Defaults to the CVS directory
233 if !exists("g:DirDiffExcludes")
234     let g:DirDiffExcludes = ""
235 endif
236 " This is the -I argument of the diff, ignore the lines of differences that
237 " matches the pattern
238 if !exists("g:DirDiffIgnore")
239     let g:DirDiffIgnore = ""
240 endif
241 if !exists("g:DirDiffSort")
242     let g:DirDiffSort = 1
243 endif
244 if !exists("g:DirDiffWindowSize")
245     let g:DirDiffWindowSize = 14
246 endif
247 if !exists("g:DirDiffInteractive")
248     let g:DirDiffInteractive = 0
249 endif
250 if !exists("g:DirDiffIgnoreCase")
251     let g:DirDiffIgnoreCase = 0
252 endif
253 " Additional arguments
254 if !exists("g:DirDiffAddArgs")
255     let g:DirDiffAddArgs = ""
256 endif
257 " Support for i18n (dynamically figure out the diff text)
258 " Defaults to off
259 if !exists("g:DirDiffDynamicDiffText")
260     let g:DirDiffDynamicDiffText = 0
261 endif
262
263 " String used for the English equivalent "Files "
264 if !exists("g:DirDiffTextFiles")
265     let g:DirDiffTextFiles = "Files "
266 endif
267
268 " String used for the English equivalent " and "
269 if !exists("g:DirDiffTextAnd")
270     let g:DirDiffTextAnd = " and "
271 endif
272
273 " String used for the English equivalent " differ")
274 if !exists("g:DirDiffTextDiffer")
275     let g:DirDiffTextDiffer = " differ"
276 endif
277
278 " String used for the English equivalent "Only in ")
279 if !exists("g:DirDiffTextOnlyIn")
280     let g:DirDiffTextOnlyIn = "Only in "
281 endif
282
283 " Set some script specific variables:
284 "
285 let s:DirDiffFirstDiffLine = 6
286 let s:DirDiffALine = 1
287 let s:DirDiffBLine = 2
288
289 " -- Variables used in various utilities
290 if has("unix")
291     let s:DirDiffCopyCmd = "cp"
292     let s:DirDiffCopyFlags = ""
293     let s:DirDiffCopyDirCmd = "cp"
294     let s:DirDiffCopyDirFlags = "-rf"
295     let s:DirDiffCopyInteractiveFlag = "-i"
296
297     let s:DirDiffDeleteCmd = "rm"
298     let s:DirDiffDeleteFlags = ""
299     let s:DirDiffDeleteInteractiveFlag = "-i"
300
301     let s:DirDiffDeleteDirCmd = "rm"
302     let s:DirDiffDeleteDirFlags = "-rf"
303
304     let s:sep = "/"
305
306     let s:DirDiffMakeDirCmd  = "!mkdir "
307
308 elseif has("win32")
309     let s:DirDiffCopyCmd = "copy"
310     let s:DirDiffCopyFlags = ""
311     let s:DirDiffCopyDirCmd = "xcopy"
312     let s:DirDiffCopyDirFlags = "/e /i /q"
313     let s:DirDiffCopyInteractiveFlag = "/-y"
314
315     let s:DirDiffDeleteCmd = "del"
316     let s:DirDiffDeleteFlags = "/s /q"
317     let s:DirDiffDeleteInteractiveFlag = "/p"
318     " Windows is somewhat stupid since "del" can only remove the files, not
319     " the directory.  The command "rd" would remove files recursively, but it
320     " doesn't really work on a file (!).  where is the deltree command???
321      
322     let s:DirDiffDeleteDirCmd = "rd"
323     " rd is by default prompting, we need to handle this in a different way
324     let s:DirDiffDeleteDirFlags = "/s"
325     let s:DirDiffDeleteDirQuietFlag = "/q"
326
327     let s:sep = "\\"
328
329     let s:DirDiffMakeDirCmd  = "!mkdir "
330 else
331     " Platforms not supported
332     let s:DirDiffCopyCmd = ""
333     let s:DirDiffCopyFlags = ""
334     let s:DirDiffDeleteCmd = ""
335     let s:DirDiffDeleteFlags = ""
336     let s:sep = ""
337 endif
338
339
340 function! <SID>DirDiff(srcA, srcB)
341     " Setup
342     let DirDiffAbsSrcA = fnamemodify(expand(a:srcA, ":p"), ":p")
343     let DirDiffAbsSrcB = fnamemodify(expand(a:srcB, ":p"), ":p")
344
345     " Check for an internationalized version of diff ?
346     call <SID>GetDiffStrings()
347
348     " Remove the trailing \ or /
349     let DirDiffAbsSrcA = substitute(DirDiffAbsSrcA, '\\$\|/$', '', '')
350     let DirDiffAbsSrcB = substitute(DirDiffAbsSrcB, '\\$\|/$', '', '')
351
352     let DiffBuffer = tempname()
353     " We first write to that file
354     " Constructs the command line
355     let cmd = "!diff"
356     let cmdarg = " -r --brief"
357
358     " If variable is set, we ignore the case
359     if (g:DirDiffIgnoreCase)
360         let cmdarg = cmdarg." -i"
361     endif
362     if (g:DirDiffAddArgs != "")
363         let cmdarg = cmdarg." ".g:DirDiffAddArgs." "
364     endif
365     if (g:DirDiffExcludes != "")
366         let cmdarg = cmdarg.' -x"'.substitute(g:DirDiffExcludes, ',', '" -x"', 'g').'"'
367     endif
368     if (g:DirDiffIgnore != "")
369         let cmdarg = cmdarg.' -I"'.substitute(g:DirDiffIgnore, ',', '" -I"', 'g').'"'
370     endif
371     " Prompt the user for additional arguments
372 "    let addarg = input("Additional diff args (current =". cmdarg. "): ")
373     let addarg = ""
374     let cmd = cmd.cmdarg." ".addarg." \"".DirDiffAbsSrcA."\" \"".DirDiffAbsSrcB."\""
375     let cmd = cmd." > \"".DiffBuffer."\""
376
377     echo "Diffing directories, it may take a while..."
378     let error = <SID>DirDiffExec(cmd, 0)
379     if (error == 0)
380         echo "There is no diff here."
381         return
382     endif
383     silent exe "edit ".DiffBuffer
384     echo "Defining [A] and [B] ... "
385     " We then do a substitution on the directory path
386     " We need to do substitution of the the LONGER string first, otherwise
387     " it'll mix up the A and B directory
388     if (strlen(DirDiffAbsSrcA) > strlen(DirDiffAbsSrcB))
389             silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcA)."/[A]/"
390             silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcB)."/[B]/"
391     else
392             silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcB)."/[B]/"
393             silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcA)."/[A]/"
394     endif
395     " In windows, diff behaves somewhat weirdly, for the appened path it'll
396     " use "/" instead of "\".  Convert this to \
397     if (has("win32"))
398         silent! %s/\//\\/g
399     endif
400
401     echo "Sorting entries ..."
402     " We then sort the lines if the option is set
403     if (g:DirDiffSort == 1)
404         1,$call <SID>Sort("s:Strcmp")
405     endif
406
407     " Put in spacer in front of each line
408     silent! %s/^/    /
409
410     " We then put the file [A] and [B] on top of the diff lines
411     call append(0, "[A]=". DirDiffAbsSrcA)
412     call append(1, "[B]=". DirDiffAbsSrcB)
413     call append(2, "Usage:   <Enter>/'o'=open,'s'=sync,'\\dj'=next,'\\dk'=prev, 'q'=quit")
414     call append(3, "Options: 'u'=update,'x'=set excludes,'i'=set ignore,'a'=set args" )
415     call append(4, "Diff Args:" . cmdarg)
416     call append(5, "")
417     " go to the beginning of the file
418     0
419     setlocal nomodified
420     setlocal nomodifiable
421     setlocal buftype=nowrite
422     setlocal bufhidden=delete
423     setlocal nowrap
424
425     " Set up local key bindings
426     " 'n' actually messes with the search next pattern, I think using \dj and
427     " \dk is enough.  Otherwise, use j,k, and enter.
428 "    nnoremap <buffer> n :call <SID>DirDiffNext()<CR>
429 "    nnoremap <buffer> p :call <SID>DirDiffPrev()<CR>
430     nnoremap <buffer> s :. call <SID>DirDiffSync()<CR>
431     vnoremap <buffer> s :call <SID>DirDiffSync()<CR>
432     nnoremap <buffer> u :call <SID>DirDiffUpdate()<CR>
433     nnoremap <buffer> x :call <SID>ChangeExcludes()<CR>
434     nnoremap <buffer> a :call <SID>ChangeArguments()<CR>
435     nnoremap <buffer> i :call <SID>ChangeIgnore()<CR>
436     nnoremap <buffer> q :call <SID>DirDiffQuit()<CR>
437
438     nnoremap <buffer> o    :call <SID>DirDiffOpen()<CR>
439     nnoremap <buffer> <CR>  :call <SID>DirDiffOpen()<CR>  
440     nnoremap <buffer> <2-Leftmouse> :call <SID>DirDiffOpen()<CR>
441     call <SID>SetupSyntax()
442
443     " Open the first diff
444     call <SID>DirDiffNext()
445 endfunction
446
447 " Set up syntax highlighing for the diff window
448 function! <SID>SetupSyntax()
449   if has("syntax") && exists("g:syntax_on") 
450       "&& !has("syntax_items")
451     syn match DirDiffSrcA               "\[A\]"
452     syn match DirDiffSrcB               "\[B\]"
453     syn match DirDiffUsage              "^Usage.*"
454     syn match DirDiffOptions            "^Options.*"
455     exec 'syn match DirDiffFiles              "' . s:DirDiffDifferLine .'"'
456     exec 'syn match DirDiffOnly               "' . s:DirDiffDiffOnlyLine . '"'
457     syn match DirDiffSelected           "^==>.*" contains=DirDiffSrcA,DirDiffSrcB
458
459     hi def link DirDiffSrcA               Directory
460     hi def link DirDiffSrcB               Type
461     hi def link DirDiffUsage              Special
462     hi def link DirDiffOptions            Special
463     hi def link DirDiffFiles              String
464     hi def link DirDiffOnly               PreProc
465     hi def link DirDiffSelected           DiffChange
466   endif
467 endfunction
468
469 " You should call this within the diff window
470 function! <SID>DirDiffUpdate()
471     let dirA = <SID>GetBaseDir("A")
472     let dirB = <SID>GetBaseDir("B")
473     call <SID>DirDiff(dirA, dirB)
474 endfun
475
476 " Quit the DirDiff mode
477 function! <SID>DirDiffQuit()
478     let in = confirm ("Are you sure you want to quit DirDiff?", "&Yes\n&No", 2)
479     if (in == 1)
480         call <SID>CloseDiffWindows()
481         bd!
482     endif
483 endfun
484
485 " Returns an escaped version of the path for regex uses
486 function! <SID>EscapeDirForRegex(path)
487     " This list is probably not complete, modify later
488     return escape(a:path, "/\\[]$^~")
489 endfunction
490
491 " Close the opened diff comparison windows if they exist
492 function! <SID>CloseDiffWindows()
493     if (<SID>AreDiffWinsOpened())
494         wincmd k
495         " Ask the user to save if buffer is modified
496         call <SID>AskIfModified()
497         bd!
498         " User may just have one window opened, we may not need to close
499         " the second diff window
500         if (&diff)
501             call <SID>AskIfModified()
502             bd!
503         endif
504     endif
505 endfunction
506
507
508 function! <SID>DirDiffOpen()
509     " First dehighlight the last marked
510     call <SID>DeHighlightLine()
511
512     " Mark the current location of the line
513     "mark n
514     let b:currentDiff = line(".")
515
516     " We first parse back the [A] and [B] directories from the top of the line
517     let dirA = <SID>GetBaseDir("A")
518     let dirB = <SID>GetBaseDir("B")
519
520     call <SID>CloseDiffWindows()
521
522     let line = getline(".")
523     " Parse the line and see whether it's a "Only in" or "Files Differ"
524     call <SID>HighlightLine()
525     let fileA = <SID>GetFileNameFromLine("A", line)
526     let fileB = <SID>GetFileNameFromLine("B", line)
527     if <SID>IsOnly(line)
528         " We open the file
529         let fileSrc = <SID>ParseOnlySrc(line)
530         if (fileSrc == "A")
531             let fileToOpen = fileA
532         elseif (fileSrc == "B")
533             let fileToOpen = fileB
534         endif
535         split
536         wincmd k
537         silent exec "edit ".fileToOpen
538         " Fool the window saying that this is diff
539         diffthis
540         wincmd j
541         " Resize the window
542         exe("resize " . g:DirDiffWindowSize)
543         exe (b:currentDiff)
544     elseif <SID>IsDiffer(line)
545         "Open the diff windows
546         split
547         wincmd k
548         silent exec "edit ".fileB
549         silent exec "vert diffsplit ".fileA
550         " Go back to the diff window
551         wincmd j
552         " Resize the window
553         exe("resize " . g:DirDiffWindowSize)
554         exe (b:currentDiff)
555         " Center the line
556         exe ("normal z.")
557     else
558         echo "There is no diff at the current line!"
559     endif
560 endfunction
561
562 " Ask the user to save if the buffer is modified
563 "
564 function! <SID>AskIfModified()
565     if (&modified)
566         let input = confirm("File " . expand("%:p") . " has been modified.", "&Save\nCa&ncel", 1)
567         if (input == 1)
568             w!
569         endif
570     endif
571 endfunction
572
573 function! <SID>HighlightLine()
574     let savedLine = line(".")
575     exe (b:currentDiff)
576     setlocal modifiable
577     let line = getline(".")
578     if (match(line, "^    ") == 0)
579         s/^    /==> /
580     endif
581     setlocal nomodifiable
582     setlocal nomodified
583     exe (savedLine)
584     redraw
585 endfunction
586
587 function! <SID>DeHighlightLine()
588     let savedLine = line(".")
589     exe (b:currentDiff)
590     let line = getline(".")
591     setlocal modifiable
592     if (match(line, "^==> ") == 0)
593         s/^==> /    /
594     endif
595     setlocal nomodifiable
596     setlocal nomodified
597     exe (savedLine)
598     redraw
599 endfunction
600
601 " Returns the directory for buffer "A" or "B".  You need to be in the diff
602 " buffer though.
603 function! <SID>GetBaseDir(diffName)
604     let currLine = line(".")
605     if (a:diffName == "A")
606         let baseLine = s:DirDiffALine
607     else
608         let baseLine = s:DirDiffBLine
609     endif
610     let regex = '\['.a:diffName.'\]=\(.*\)'
611     let line = getline(baseLine)
612     let rtn = substitute(line, regex , '\1', '')
613     return rtn
614 endfunction
615
616 function! <SID>DirDiffNext()
617     " If the current window is a diff, go down one
618     if (&diff == 1)
619         wincmd j
620     endif
621     " if the current line is <= 6, (within the header range), we go to the
622     " first diff line open it
623     if (line(".") < s:DirDiffFirstDiffLine)
624         exe (s:DirDiffFirstDiffLine)
625         let b:currentDiff = line(".")
626     endif
627     silent! exe (b:currentDiff + 1)
628     call <SID>DirDiffOpen()
629 endfunction
630
631 function! <SID>DirDiffPrev()
632     " If the current window is a diff, go down one
633     if (&diff == 1)
634         wincmd j
635     endif
636     silent! exe (b:currentDiff - 1)
637     call <SID>DirDiffOpen()
638 endfunction
639
640 " For each line, we can perform a recursive copy or delete to sync up the
641 " difference. Returns non-zero if the operation is NOT successful, returns 0
642 " if everything is fine.
643 "
644 function! <SID>DirDiffSyncHelper(AB, line)
645     let fileA = <SID>GetFileNameFromLine("A", a:line)
646     let fileB = <SID>GetFileNameFromLine("B", a:line)
647 "    echo "Helper line is ". a:line. " fileA " . fileA . " fileB " . fileB
648     if <SID>IsOnly(a:line)
649         " If a:AB is "A" and the ParseOnlySrc returns "A", that means we need to
650         " copy
651         let fileSrc = <SID>ParseOnlySrc(a:line)
652         let operation = ""
653         if (a:AB == "A" && fileSrc == "A")
654             let operation = "Copy"
655             " Use A, and A has source, thus copy the file from A to B
656             let fileFrom = fileA
657             let fileTo = fileB
658         elseif (a:AB == "A" && fileSrc == "B")
659             let operation = "Delete"
660             " Use A, but B has source, thus delete the file from B
661             let fileFrom = fileB
662             let fileTo = fileA
663         elseif (a:AB == "B" && fileSrc == "A")
664             let operation = "Delete"
665             " Use B, but the source file is A, thus removing A
666             let fileFrom = fileA
667             let fileTo = fileB
668         elseif (a:AB == "B" && fileSrc == "B")
669             " Use B, and B has the source file, thus copy B to A
670             let operation = "Copy"
671             let fileFrom = fileB
672             let fileTo = fileA
673         endif
674     elseif <SID>IsDiffer(a:line)
675         " Copy no matter what
676         let operation = "Copy"
677         if (a:AB == "A")
678             let fileFrom = fileA
679             let fileTo = fileB
680         elseif (a:AB == "B")
681             let fileFrom = fileB
682             let fileTo = fileA
683         endif
684     else 
685         echo "There is no diff here!"
686         " Error
687         return 1
688     endif
689     if (operation == "Copy")
690         let rtnCode = <SID>Copy(fileFrom, fileTo)
691     elseif (operation == "Delete")
692         let rtnCode = <SID>Delete(fileFrom)
693     endif
694     return rtnCode
695 endfunction
696
697 " Synchronize the range
698 function! <SID>DirDiffSync() range
699     let answer = 1
700     let silence = 0
701     let syncMaster = "A"
702     let currLine = a:firstline
703     let lastLine = a:lastline
704     let syncCount = 0
705
706     while ((currLine <= lastLine))
707         " Update the highlight
708         call <SID>DeHighlightLine()
709         let b:currentDiff = currLine
710         call <SID>HighlightLine()
711         let line = getline(currLine)
712         if (!silence)
713             let answer = confirm(substitute(line, "^....", '', ''). "\nSynchronization option:" , "&A -> B\n&B -> A\nA&lways A\nAl&ways B\n&Skip\nCa&ncel", 6)
714             if (answer == 1 || answer == 3)
715                 let syncMaster = "A"
716             endif
717             if (answer == 2 || answer == 4)
718                 let syncMaster = "B"
719             endif
720             if (answer == 3 || answer == 4)
721                 let silence = 1
722             endif
723             if (answer == 5)
724                 let currLine = currLine + 1
725                 continue
726             endif
727             if (answer == 6)
728                 break
729             endif
730         endif
731
732 "        call <SID>DeHighlightLine()
733         let rtnCode = <SID>DirDiffSyncHelper(syncMaster, line)
734         if (rtnCode == 0)
735             " Successful
736             let syncCount = syncCount + 1
737             " Assume that the line is synchronized, we delete the entry
738             setlocal modifiable
739             exe (currLine.",".currLine." delete")
740             setlocal nomodifiable
741             setlocal nomodified
742             let lastLine = lastLine - 1
743         else
744             " Failed!
745             let currLine = currLine + 1
746         endif
747     endwhile
748     echo syncCount . " diff item(s) synchronized."
749 endfunction
750
751 " Return file "A" or "B" depending on the line given.  If it's a Only line,
752 " either A or B does not exist, but the according value would be returned.
753 function! <SID>GetFileNameFromLine(AB, line)
754     " Determine where the source of the copy is.
755     let dirA = <SID>GetBaseDir("A")
756     let dirB = <SID>GetBaseDir("B")
757
758     let fileToProcess = ""
759
760     if <SID>IsOnly(a:line)
761         let fileToProcess = <SID>ParseOnlyFile(a:line)
762     elseif <SID>IsDiffer(a:line)
763         let regex = '^.*' . s:DirDiffDifferLine . '\[A\]\(.*\)' . s:DirDiffDifferAndLine . '\[B\]\(.*\)' . s:DirDiffDifferEndLine . '.*$'
764         let fileToProcess = substitute(a:line, regex, '\1', '')
765     else
766     endif
767
768     "echo "line : " . a:line. "AB = " . a:AB . " File to Process " . fileToProcess
769     if (a:AB == "A")
770         return dirA . fileToProcess
771     elseif (a:AB == "B")
772         return dirB . fileToProcess
773     else
774         return ""
775     endif
776 endfunction
777
778 "Returns the source (A or B) of the "Only" line
779 function! <SID>ParseOnlySrc(line)
780     return substitute(a:line, '^.*' . s:DirDiffDiffOnlyLine . '\[\(.\)\].*:.*', '\1', '')
781 endfunction
782
783 function! <SID>ParseOnlyFile(line)
784     let regex = '^.*' . s:DirDiffDiffOnlyLine . '\[.\]\(.*\): \(.*\)'
785     let root = substitute(a:line, regex , '\1', '')
786     let file = root . s:sep . substitute(a:line, regex , '\2', '')
787     return file
788 endfunction
789
790 function! <SID>Copy(fileFromOrig, fileToOrig)
791     let fileFrom = substitute(a:fileFromOrig, '/', s:sep, 'g')
792     let fileTo = substitute(a:fileToOrig, '/', s:sep, 'g')
793     echo "Copy from " . fileFrom . " to " . fileTo
794     if (s:DirDiffCopyCmd == "")
795         echo "Copy not supported on this platform"
796         return 1
797     endif
798
799     " Constructs the copy command
800     let copycmd = "!".s:DirDiffCopyCmd." ".s:DirDiffCopyFlags
801     " Append the interactive flag
802     if (g:DirDiffInteractive)
803         let copycmd = copycmd . " " . s:DirDiffCopyInteractiveFlag
804     endif
805     let copycmd = copycmd . " \"".fileFrom."\" \"".fileTo."\""
806
807     " Constructs the copy directory command
808     let copydircmd = "!".s:DirDiffCopyDirCmd." ".s:DirDiffCopyDirFlags
809     " Append the interactive flag
810     if (g:DirDiffInteractive)
811         let copydircmd = copydircmd . " " . s:DirDiffCopyInteractiveFlag
812     endif
813     let copydircmd = copydircmd . " \"".fileFrom."\" \"".fileTo."\""
814
815     let error = 0
816     if (isdirectory(fileFrom))
817         let error = <SID>DirDiffExec(copydircmd, g:DirDiffInteractive)
818     else
819         let error = <SID>DirDiffExec(copycmd, g:DirDiffInteractive)
820     endif
821     if (error != 0)
822         echo "Can't copy from " . fileFrom . " to " . fileTo
823         return 1
824     endif
825     return 0
826 endfunction
827
828 " Would execute the command, either silent or not silent, by the
829 " interactive flag ([0|1]).  Returns the v:shell_error after
830 " executing the command.
831 function! <SID>DirDiffExec(cmd, interactive)
832     let error = 0
833     if (a:interactive)
834         exe (a:cmd)
835         let error = v:shell_error
836     else
837         silent exe (a:cmd)
838         let error = v:shell_error
839     endif
840 "    let d = input("DirDiffExec: " . a:cmd . " " . a:interactive . " returns " . v:shell_error)
841     return error
842 endfunction
843
844 " Delete the file or directory.  Returns 0 if nothing goes wrong, error code
845 " otherwise.
846 function! <SID>Delete(fileFromOrig)
847     let fileFrom = substitute(a:fileFromOrig, '/', s:sep, 'g')
848     echo "Deleting from " . fileFrom
849     if (s:DirDiffDeleteCmd == "")
850         echo "Delete not supported on this platform"
851         return 1
852     endif
853
854     let delcmd = ""
855
856     if (isdirectory(fileFrom))
857         let delcmd = "!".s:DirDiffDeleteDirCmd." ".s:DirDiffDeleteDirFlags
858         if (g:DirDiffInteractive)
859             " If running on Unix, and we're running in interactive mode, we
860             " append the -i tag
861             if (has("unix"))
862                 let delcmd = delcmd . " " . s:DirDiffDeleteInteractiveFlag
863             endif
864         else
865             " If running on windows, and we're not running in interactive
866             " mode, we append the quite flag to the "rd" command
867             if (has("win32"))
868                 let delcmd = delcmd . " " . s:DirDiffDeleteDirQuietFlag
869             endif
870         endif
871     else
872         let delcmd = "!".s:DirDiffDeleteCmd." ".s:DirDiffDeleteFlags
873         if (g:DirDiffInteractive)
874             let delcmd = delcmd . " " . s:DirDiffDeleteInteractiveFlag
875         endif
876     endif
877
878     let delcmd = delcmd ." \"".fileFrom."\""
879     let error = <SID>DirDiffExec(delcmd, g:DirDiffInteractive)
880     if (error != 0)
881         echo "Can't delete " . fileFrom
882     endif
883     return error
884 endfunction
885
886 function! <SID>AreDiffWinsOpened()
887     let currBuff = expand("%:p")
888     let currLine = line(".")
889     wincmd k
890     let abovedBuff = expand("%:p")
891     if (&diff)
892         let abovedIsDiff = 1
893     else
894         let abovedIsDiff = 0
895     endif
896     " Go Back if the aboved buffer is not the same
897     if (currBuff != abovedBuff)
898         wincmd j
899         " Go back to the same line
900         exe (currLine)
901         if (abovedIsDiff == 1)
902             return 1
903         else
904             " Aboved is just a bogus buffer, not a diff buffer
905             return 0
906         endif
907     else
908         exe (currLine)
909         return 0
910     endif
911 endfunction
912
913 " The given line begins with the "Only in"
914 function! <SID>IsOnly(line)     
915     return (match(a:line, "^ *" . s:DirDiffDiffOnlyLine . "\\|^==> " . s:DirDiffDiffOnlyLine ) == 0)
916 endfunction
917
918 " The given line begins with the "Files"
919 function! <SID>IsDiffer(line)
920     return (match(a:line, "^ *" . s:DirDiffDifferLine . "\\|^==> " . s:DirDiffDifferLine  ) == 0)
921 endfunction
922
923 " Let you modify the Exclude patthern
924 function! <SID>ChangeExcludes()
925     let g:DirDiffExcludes = input ("Exclude pattern (separate multiple patterns with ','): ", g:DirDiffExcludes)
926     echo "\nPress update ('u') to refresh the diff."
927 endfunction
928
929 " Let you modify additional arguments for diff
930 function! <SID>ChangeArguments()
931     let g:DirDiffAddArgs = input ("Additional diff args: ", g:DirDiffAddArgs)
932     echo "\nPress update ('u') to refresh the diff."
933 endfunction
934
935 " Let you modify the Ignore patthern
936 function! <SID>ChangeIgnore()
937     let g:DirDiffIgnore = input ("Ignore pattern (separate multiple patterns with ','): ", g:DirDiffIgnore)
938     echo "\nPress update ('u') to refresh the diff."
939 endfunction
940
941 " Sorting functions from the Vim docs.  Use this instead of the sort binary.
942 "
943 " Function for use with Sort(), to compare two strings.
944 func! <SID>Strcmp(str1, str2)
945   if (a:str1 < a:str2)
946         return -1
947   elseif (a:str1 > a:str2)
948         return 1
949   else
950         return 0
951   endif
952 endfunction
953
954 " Sort lines.  SortR() is called recursively.
955 func! <SID>SortR(start, end, cmp)
956   if (a:start >= a:end)
957         return
958   endif
959   let partition = a:start - 1
960   let middle = partition
961   let partStr = getline((a:start + a:end) / 2)
962   let i = a:start
963   while (i <= a:end)
964         let str = getline(i)
965         exec "let result = " . a:cmp . "(str, partStr)"
966         if (result <= 0)
967             " Need to put it before the partition.  Swap lines i and partition.
968             let partition = partition + 1
969             if (result == 0)
970                 let middle = partition
971             endif
972             if (i != partition)
973                 let str2 = getline(partition)
974                 call setline(i, str2)
975                 call setline(partition, str)
976             endif
977         endif
978         let i = i + 1
979   endwhile
980
981   " Now we have a pointer to the "middle" element, as far as partitioning
982   " goes, which could be anywhere before the partition.  Make sure it is at
983   " the end of the partition.
984   if (middle != partition)
985         let str = getline(middle)
986         let str2 = getline(partition)
987         call setline(middle, str2)
988         call setline(partition, str)
989   endif
990   call <SID>SortR(a:start, partition - 1, a:cmp)
991   call <SID>SortR(partition + 1, a:end, a:cmp)
992 endfunc
993
994 " To Sort a range of lines, pass the range to Sort() along with the name of a
995 " function that will compare two lines.
996 func! <SID>Sort(cmp) range
997   call <SID>SortR(a:firstline, a:lastline, a:cmp)
998 endfunc
999
1000 " Added to deal with internationalized version of diff, which returns a
1001 " different string than "Files ... differ" or "Only in ... "
1002
1003 function! <SID>GetDiffStrings()
1004     " Check if we have the dynamic text string turned on.  If not, just return
1005     " what's set in the global variables
1006
1007     if (g:DirDiffDynamicDiffText == 0)
1008         let s:DirDiffDiffOnlyLine = g:DirDiffTextOnlyIn
1009         let s:DirDiffDifferLine = g:DirDiffTextFiles
1010         let s:DirDiffDifferAndLine = g:DirDiffTextAnd
1011         let s:DirDiffDifferEndLine = g:DirDiffTextDiffer
1012         return
1013     endif
1014
1015         let tmp1 = tempname()
1016         let tmp2 = tempname()
1017         let tmpdiff = tempname()
1018
1019     " We need to pad the backslashes in order to make it match
1020     let tmp1rx = <SID>EscapeDirForRegex(tmp1)
1021     let tmp2rx = <SID>EscapeDirForRegex(tmp2)
1022     let tmpdiffrx = <SID>EscapeDirForRegex(tmpdiff)
1023
1024         silent exe s:DirDiffMakeDirCmd . "\"" . tmp1 . "\""
1025         silent exe s:DirDiffMakeDirCmd . "\"" . tmp2 . "\""
1026         silent exe "!echo test > \"" . tmp1 . s:sep . "test" . "\""
1027         silent exe "!diff -r --brief \"" . tmp1 . "\" \"" . tmp2 . "\" > \"" . tmpdiff . "\""
1028
1029         " Now get the result of that diff cmd
1030         silent exe "split ". tmpdiff
1031     "echo "First line: " . getline(1)
1032     "echo "tmp1: " . tmp1
1033     "echo "tmp1rx: " . tmp1rx
1034         let s:DirDiffDiffOnlyLine = substitute( getline(1), tmp1rx . ".*$", "", '') 
1035     "echo "DirDiff Only: " . s:DirDiffDiffOnlyLine
1036         
1037         q
1038
1039         " Now let's get the Differ string
1040     "echo "Getting the diff in GetDiffStrings"
1041         
1042         silent exe "!echo testdifferent > \"" . tmp2 . s:sep . "test" . "\""
1043         silent exe "!diff -r --brief \"" . tmp1 . "\" \"" . tmp2 . "\" > \"" . tmpdiff . "\""
1044         
1045         silent exe "split ". tmpdiff
1046         let s:DirDiffDifferLine = substitute( getline(1), tmp1rx . ".*$", "", '') 
1047     " Note that the diff on cygwin may output '/' instead of '\' for the
1048     " separator, so we need to accomodate for both cases
1049     let andrx = "^.*" . tmp1rx . "[\\\/]test\\(.*\\)" . tmp2rx . "[\\\/]test.*$"
1050     let endrx = "^.*" . tmp1rx . "[\\\/]test.*" . tmp2rx . "[\\\/]test\\(.*$\\)"
1051     "echo "andrx : " . andrx
1052     "echo "endrx : " . endrx
1053         let s:DirDiffDifferAndLine = substitute( getline(1), andrx , "\\1", '') 
1054     let s:DirDiffDifferEndLine = substitute( getline(1), endrx, "\\1", '') 
1055
1056         "echo "s:DirDiffDifferLine = " . s:DirDiffDifferLine
1057         "echo "s:DirDiffDifferAndLine = " . s:DirDiffDifferAndLine
1058         "echo "s:DirDiffDifferEndLine = " . s:DirDiffDifferEndLine
1059
1060         q
1061
1062         " Delete tmp files
1063     "echo "Deleting tmp files."
1064
1065         call <SID>Delete(tmp1)
1066         call <SID>Delete(tmp2)
1067         call <SID>Delete(tmpdiff)
1068
1069 endfunction