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
6 " (C) 2001-2006 by William Lee, <wl1012@yahoo.com>
10 " http://www.vim.org/scripts/script.php?script_id=102
13 " - Diffing a directory recursively and enable easy merging, copying and
14 " deleting of files and directories.
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
23 " - On Unix, you need to have "rm" and "cp" in your path.
26 " Put this file in your ~/.vim/plugin
28 " Doing the following will generate a diff window.
30 " :DirDiff <A:Src Directory> <B:Src Directory>
32 " :DirDiff ../something/dir1 /usr/bin/somethingelse/dir2
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
40 " - There are 6 Options you can choose when you hit 's':
42 " Copy A to overwrite B
43 " If A's file actually points to a directory, it'll copy it to B
46 " Copy B to overwrite A
47 " If B's file actually points to a directory, it'll copy it to A
50 " For the rest of the items that you've selected,
51 " synchronize like (1).
53 " For the rest of the items that you've selected,
54 " synchronize like (2).
56 " Skip this diff entry.
58 " Quit the loop and exit.
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,
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)
73 " You can set the following DirDiff variables. You can add the following
74 " "let" lines in your .vimrc file.
76 " Sets default exclude pattern:
77 " let g:DirDiffExcludes = "CVS,*.class,*.exe,.*.swp"
79 " Sets default ignore pattern:
80 " let g:DirDiffIgnore = "Id:,Revision:,Date:"
82 " If DirDiffSort is set to 1, sorts the diff lines.
83 " let g:DirDiffSort = 1
85 " Sets the diff window (bottom window) height (rows)
86 " let g:DirDiffWindowSize = 14
88 " Ignore case during diff
89 " let g:DirDiffIgnoreCase = 0
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
96 " let g:DirDiffDynamicDiffText = 0
98 " String used for the English equivalent "Files "
99 " let g:DirDiffTextFiles = "Files "
101 " String used for the English equivalent " and "
102 " let g:DirDiffTextAnd = " and "
104 " String used for the English equivalent " differ")
105 " let g:DirDiffTextDiffer = " differ"
107 " String used for the English equivalent "Only in ")
108 " let g:DirDiffTextOnlyIn = "Only in "
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!
117 " Please mail any comment/suggestion/patch to
118 " William Lee <wl1012@yahoo.com>
121 " Copyright (c) 2001-2006 William Lee
122 " All rights reserved.
124 " Redistribution and use in source and binary forms, with or without
125 " modification, are permitted provided that the following conditions are
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.
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.
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
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
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.
174 " 0.9 - Reorganization of the interface. Much simplier dialog for
175 " synchronization. Support for range synchronization option (REALLY
177 " - Removed unnecessary key bindings. All key bindings are local to
178 " the diff window. (except for the \dg and \dp)
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)
189 " 0.7 Initial Release
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 ()
201 if !hasmapto('<Plug>DirDiffGet')
202 map <unique> <Leader>dg <Plug>DirDiffGet
204 if !hasmapto('<Plug>DirDiffPut')
205 map <unique> <Leader>dp <Plug>DirDiffPut
207 if !hasmapto('<Plug>DirDiffNext')
208 map <unique> <Leader>dj <Plug>DirDiffNext
210 if !hasmapto('<Plug>DirDiffPrev')
211 map <unique> <Leader>dk <Plug>DirDiffPrev
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>
221 " Default Variables. You can override these in your global variables
224 " For DirDiffExcludes and DirDiffIgnore, separate different patterns with a
225 " ',' (comma and no space!).
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"
232 " You can set the pattern that diff excludes. Defaults to the CVS directory
233 if !exists("g:DirDiffExcludes")
234 let g:DirDiffExcludes = ""
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 = ""
241 if !exists("g:DirDiffSort")
242 let g:DirDiffSort = 1
244 if !exists("g:DirDiffWindowSize")
245 let g:DirDiffWindowSize = 14
247 if !exists("g:DirDiffInteractive")
248 let g:DirDiffInteractive = 0
250 if !exists("g:DirDiffIgnoreCase")
251 let g:DirDiffIgnoreCase = 0
253 " Additional arguments
254 if !exists("g:DirDiffAddArgs")
255 let g:DirDiffAddArgs = ""
257 " Support for i18n (dynamically figure out the diff text)
259 if !exists("g:DirDiffDynamicDiffText")
260 let g:DirDiffDynamicDiffText = 0
263 " String used for the English equivalent "Files "
264 if !exists("g:DirDiffTextFiles")
265 let g:DirDiffTextFiles = "Files "
268 " String used for the English equivalent " and "
269 if !exists("g:DirDiffTextAnd")
270 let g:DirDiffTextAnd = " and "
273 " String used for the English equivalent " differ")
274 if !exists("g:DirDiffTextDiffer")
275 let g:DirDiffTextDiffer = " differ"
278 " String used for the English equivalent "Only in ")
279 if !exists("g:DirDiffTextOnlyIn")
280 let g:DirDiffTextOnlyIn = "Only in "
283 " Set some script specific variables:
285 let s:DirDiffFirstDiffLine = 6
286 let s:DirDiffALine = 1
287 let s:DirDiffBLine = 2
289 " -- Variables used in various utilities
291 let s:DirDiffCopyCmd = "cp"
292 let s:DirDiffCopyFlags = ""
293 let s:DirDiffCopyDirCmd = "cp"
294 let s:DirDiffCopyDirFlags = "-rf"
295 let s:DirDiffCopyInteractiveFlag = "-i"
297 let s:DirDiffDeleteCmd = "rm"
298 let s:DirDiffDeleteFlags = ""
299 let s:DirDiffDeleteInteractiveFlag = "-i"
301 let s:DirDiffDeleteDirCmd = "rm"
302 let s:DirDiffDeleteDirFlags = "-rf"
306 let s:DirDiffMakeDirCmd = "!mkdir "
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"
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???
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"
329 let s:DirDiffMakeDirCmd = "!mkdir "
331 " Platforms not supported
332 let s:DirDiffCopyCmd = ""
333 let s:DirDiffCopyFlags = ""
334 let s:DirDiffDeleteCmd = ""
335 let s:DirDiffDeleteFlags = ""
340 function! <SID>DirDiff(srcA, srcB)
342 let DirDiffAbsSrcA = fnamemodify(expand(a:srcA, ":p"), ":p")
343 let DirDiffAbsSrcB = fnamemodify(expand(a:srcB, ":p"), ":p")
345 " Check for an internationalized version of diff ?
346 call <SID>GetDiffStrings()
348 " Remove the trailing \ or /
349 let DirDiffAbsSrcA = substitute(DirDiffAbsSrcA, '\\$\|/$', '', '')
350 let DirDiffAbsSrcB = substitute(DirDiffAbsSrcB, '\\$\|/$', '', '')
352 let DiffBuffer = tempname()
353 " We first write to that file
354 " Constructs the command line
356 let cmdarg = " -r --brief"
358 " If variable is set, we ignore the case
359 if (g:DirDiffIgnoreCase)
360 let cmdarg = cmdarg." -i"
362 if (g:DirDiffAddArgs != "")
363 let cmdarg = cmdarg." ".g:DirDiffAddArgs." "
365 if (g:DirDiffExcludes != "")
366 let cmdarg = cmdarg.' -x"'.substitute(g:DirDiffExcludes, ',', '" -x"', 'g').'"'
368 if (g:DirDiffIgnore != "")
369 let cmdarg = cmdarg.' -I"'.substitute(g:DirDiffIgnore, ',', '" -I"', 'g').'"'
371 " Prompt the user for additional arguments
372 " let addarg = input("Additional diff args (current =". cmdarg. "): ")
374 let cmd = cmd.cmdarg." ".addarg." \"".DirDiffAbsSrcA."\" \"".DirDiffAbsSrcB."\""
375 let cmd = cmd." > \"".DiffBuffer."\""
377 echo "Diffing directories, it may take a while..."
378 let error = <SID>DirDiffExec(cmd, 0)
380 echo "There is no diff here."
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]/"
392 silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcB)."/[B]/"
393 silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcA)."/[A]/"
395 " In windows, diff behaves somewhat weirdly, for the appened path it'll
396 " use "/" instead of "\". Convert this to \
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")
407 " Put in spacer in front of each line
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)
417 " go to the beginning of the file
420 setlocal nomodifiable
421 setlocal buftype=nowrite
422 setlocal bufhidden=delete
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>
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()
443 " Open the first diff
444 call <SID>DirDiffNext()
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
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
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)
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)
480 call <SID>CloseDiffWindows()
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, "/\\[]$^~")
491 " Close the opened diff comparison windows if they exist
492 function! <SID>CloseDiffWindows()
493 if (<SID>AreDiffWinsOpened())
495 " Ask the user to save if buffer is modified
496 call <SID>AskIfModified()
498 " User may just have one window opened, we may not need to close
499 " the second diff window
501 call <SID>AskIfModified()
508 function! <SID>DirDiffOpen()
509 " First dehighlight the last marked
510 call <SID>DeHighlightLine()
512 " Mark the current location of the line
514 let b:currentDiff = line(".")
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")
520 call <SID>CloseDiffWindows()
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)
529 let fileSrc = <SID>ParseOnlySrc(line)
531 let fileToOpen = fileA
532 elseif (fileSrc == "B")
533 let fileToOpen = fileB
537 silent exec "edit ".fileToOpen
538 " Fool the window saying that this is diff
542 exe("resize " . g:DirDiffWindowSize)
544 elseif <SID>IsDiffer(line)
545 "Open the diff windows
548 silent exec "edit ".fileB
549 silent exec "vert diffsplit ".fileA
550 " Go back to the diff window
553 exe("resize " . g:DirDiffWindowSize)
558 echo "There is no diff at the current line!"
562 " Ask the user to save if the buffer is modified
564 function! <SID>AskIfModified()
566 let input = confirm("File " . expand("%:p") . " has been modified.", "&Save\nCa&ncel", 1)
573 function! <SID>HighlightLine()
574 let savedLine = line(".")
577 let line = getline(".")
578 if (match(line, "^ ") == 0)
581 setlocal nomodifiable
587 function! <SID>DeHighlightLine()
588 let savedLine = line(".")
590 let line = getline(".")
592 if (match(line, "^==> ") == 0)
595 setlocal nomodifiable
601 " Returns the directory for buffer "A" or "B". You need to be in the diff
603 function! <SID>GetBaseDir(diffName)
604 let currLine = line(".")
605 if (a:diffName == "A")
606 let baseLine = s:DirDiffALine
608 let baseLine = s:DirDiffBLine
610 let regex = '\['.a:diffName.'\]=\(.*\)'
611 let line = getline(baseLine)
612 let rtn = substitute(line, regex , '\1', '')
616 function! <SID>DirDiffNext()
617 " If the current window is a diff, go down one
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(".")
627 silent! exe (b:currentDiff + 1)
628 call <SID>DirDiffOpen()
631 function! <SID>DirDiffPrev()
632 " If the current window is a diff, go down one
636 silent! exe (b:currentDiff - 1)
637 call <SID>DirDiffOpen()
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.
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
651 let fileSrc = <SID>ParseOnlySrc(a:line)
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
658 elseif (a:AB == "A" && fileSrc == "B")
659 let operation = "Delete"
660 " Use A, but B has source, thus delete the file from B
663 elseif (a:AB == "B" && fileSrc == "A")
664 let operation = "Delete"
665 " Use B, but the source file is A, thus removing A
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"
674 elseif <SID>IsDiffer(a:line)
675 " Copy no matter what
676 let operation = "Copy"
685 echo "There is no diff here!"
689 if (operation == "Copy")
690 let rtnCode = <SID>Copy(fileFrom, fileTo)
691 elseif (operation == "Delete")
692 let rtnCode = <SID>Delete(fileFrom)
697 " Synchronize the range
698 function! <SID>DirDiffSync() range
702 let currLine = a:firstline
703 let lastLine = a:lastline
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)
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)
717 if (answer == 2 || answer == 4)
720 if (answer == 3 || answer == 4)
724 let currLine = currLine + 1
732 " call <SID>DeHighlightLine()
733 let rtnCode = <SID>DirDiffSyncHelper(syncMaster, line)
736 let syncCount = syncCount + 1
737 " Assume that the line is synchronized, we delete the entry
739 exe (currLine.",".currLine." delete")
740 setlocal nomodifiable
742 let lastLine = lastLine - 1
745 let currLine = currLine + 1
748 echo syncCount . " diff item(s) synchronized."
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")
758 let fileToProcess = ""
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', '')
768 "echo "line : " . a:line. "AB = " . a:AB . " File to Process " . fileToProcess
770 return dirA . fileToProcess
772 return dirB . fileToProcess
778 "Returns the source (A or B) of the "Only" line
779 function! <SID>ParseOnlySrc(line)
780 return substitute(a:line, '^.*' . s:DirDiffDiffOnlyLine . '\[\(.\)\].*:.*', '\1', '')
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', '')
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"
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
805 let copycmd = copycmd . " \"".fileFrom."\" \"".fileTo."\""
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
813 let copydircmd = copydircmd . " \"".fileFrom."\" \"".fileTo."\""
816 if (isdirectory(fileFrom))
817 let error = <SID>DirDiffExec(copydircmd, g:DirDiffInteractive)
819 let error = <SID>DirDiffExec(copycmd, g:DirDiffInteractive)
822 echo "Can't copy from " . fileFrom . " to " . fileTo
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)
835 let error = v:shell_error
838 let error = v:shell_error
840 " let d = input("DirDiffExec: " . a:cmd . " " . a:interactive . " returns " . v:shell_error)
844 " Delete the file or directory. Returns 0 if nothing goes wrong, error code
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"
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
862 let delcmd = delcmd . " " . s:DirDiffDeleteInteractiveFlag
865 " If running on windows, and we're not running in interactive
866 " mode, we append the quite flag to the "rd" command
868 let delcmd = delcmd . " " . s:DirDiffDeleteDirQuietFlag
872 let delcmd = "!".s:DirDiffDeleteCmd." ".s:DirDiffDeleteFlags
873 if (g:DirDiffInteractive)
874 let delcmd = delcmd . " " . s:DirDiffDeleteInteractiveFlag
878 let delcmd = delcmd ." \"".fileFrom."\""
879 let error = <SID>DirDiffExec(delcmd, g:DirDiffInteractive)
881 echo "Can't delete " . fileFrom
886 function! <SID>AreDiffWinsOpened()
887 let currBuff = expand("%:p")
888 let currLine = line(".")
890 let abovedBuff = expand("%:p")
896 " Go Back if the aboved buffer is not the same
897 if (currBuff != abovedBuff)
899 " Go back to the same line
901 if (abovedIsDiff == 1)
904 " Aboved is just a bogus buffer, not a diff buffer
913 " The given line begins with the "Only in"
914 function! <SID>IsOnly(line)
915 return (match(a:line, "^ *" . s:DirDiffDiffOnlyLine . "\\|^==> " . s:DirDiffDiffOnlyLine ) == 0)
918 " The given line begins with the "Files"
919 function! <SID>IsDiffer(line)
920 return (match(a:line, "^ *" . s:DirDiffDifferLine . "\\|^==> " . s:DirDiffDifferLine ) == 0)
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."
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."
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."
941 " Sorting functions from the Vim docs. Use this instead of the sort binary.
943 " Function for use with Sort(), to compare two strings.
944 func! <SID>Strcmp(str1, str2)
947 elseif (a:str1 > a:str2)
954 " Sort lines. SortR() is called recursively.
955 func! <SID>SortR(start, end, cmp)
956 if (a:start >= a:end)
959 let partition = a:start - 1
960 let middle = partition
961 let partStr = getline((a:start + a:end) / 2)
965 exec "let result = " . a:cmp . "(str, partStr)"
967 " Need to put it before the partition. Swap lines i and partition.
968 let partition = partition + 1
970 let middle = partition
973 let str2 = getline(partition)
974 call setline(i, str2)
975 call setline(partition, str)
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)
990 call <SID>SortR(a:start, partition - 1, a:cmp)
991 call <SID>SortR(partition + 1, a:end, a:cmp)
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)
1000 " Added to deal with internationalized version of diff, which returns a
1001 " different string than "Files ... differ" or "Only in ... "
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
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
1015 let tmp1 = tempname()
1016 let tmp2 = tempname()
1017 let tmpdiff = tempname()
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)
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 . "\""
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
1039 " Now let's get the Differ string
1040 "echo "Getting the diff in GetDiffStrings"
1042 silent exe "!echo testdifferent > \"" . tmp2 . s:sep . "test" . "\""
1043 silent exe "!diff -r --brief \"" . tmp1 . "\" \"" . tmp2 . "\" > \"" . tmpdiff . "\""
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", '')
1056 "echo "s:DirDiffDifferLine = " . s:DirDiffDifferLine
1057 "echo "s:DirDiffDifferAndLine = " . s:DirDiffDifferAndLine
1058 "echo "s:DirDiffDifferEndLine = " . s:DirDiffDifferEndLine
1063 "echo "Deleting tmp files."
1065 call <SID>Delete(tmp1)
1066 call <SID>Delete(tmp2)
1067 call <SID>Delete(tmpdiff)