dirdiff.vim (30620B)
1 " -*- vim -*- 2 " FILE: "/home/wlee/.vim/plugin/DirDiff.vim" {{{ 3 " LAST MODIFICATION: "Wed, 11 Apr 2012 15:49:03 -0500 (wlee)" 4 " HEADER MAINTAINED BY: N/A 5 " VERSION: 1.1.5 6 " (C) 2001-2015 by William Lee, <wl1012@yahoo.com> 7 " }}} 8 9 " Public Interface: 10 command! -nargs=* -complete=dir DirDiff call <SID>DirDiff (<f-args>) 11 command! -nargs=0 DirDiffOpen call <SID>DirDiffOpen () 12 command! -nargs=0 DirDiffNext call <SID>DirDiffNext () 13 command! -nargs=0 DirDiffPrev call <SID>DirDiffPrev () 14 command! -nargs=0 DirDiffUpdate call <SID>DirDiffUpdate () 15 command! -nargs=0 DirDiffQuit call <SID>DirDiffQuit () 16 17 " The following comamnds can be used in the Vim diff mode: 18 " 19 " \dg - Diff get: maps to :diffget<CR> 20 " \dp - Diff put: maps to :diffput<CR> 21 " \dj - Diff next: (think j for down) 22 " \dk - Diff previous: (think k for up) 23 24 if !exists("g:DirDiffEnableMappings") 25 let g:DirDiffEnableMappings = 0 26 endif 27 28 if g:DirDiffEnableMappings 29 nnoremap <unique> <Leader>dg :diffget<CR> 30 nnoremap <unique> <Leader>dp :diffput<CR> 31 nnoremap <unique> <Leader>dj :DirDiffNext<CR> 32 nnoremap <unique> <Leader>dk :DirDiffPrev<CR> 33 endif 34 35 " Global Maps: 36 37 " Default Variables. You can override these in your global variables 38 " settings. 39 " 40 " For DirDiffExcludes and DirDiffIgnore, separate different patterns with a 41 " ',' (comma and no space!). 42 " 43 " eg. in your .vimrc file: let g:DirDiffExcludes = "CVS,*.class,*.o" 44 " let g:DirDiffIgnore = "Id:" 45 " " ignore white space in diff 46 " let g:DirDiffAddArgs = "-w" 47 " 48 " You can set the pattern that diff excludes. Defaults to the CVS directory 49 if !exists("g:DirDiffExcludes") 50 let g:DirDiffExcludes = "" 51 endif 52 " This is the -I argument of the diff, ignore the lines of differences that 53 " matches the pattern 54 if !exists("g:DirDiffIgnore") 55 let g:DirDiffIgnore = "" 56 endif 57 if !exists("g:DirDiffSort") 58 let g:DirDiffSort = 1 59 endif 60 if !exists("g:DirDiffWindowSize") 61 let g:DirDiffWindowSize = 14 62 endif 63 if !exists("g:DirDiffInteractive") 64 let g:DirDiffInteractive = 0 65 endif 66 if !exists("g:DirDiffIgnoreCase") 67 let g:DirDiffIgnoreCase = 0 68 endif 69 " Additional arguments 70 if !exists("g:DirDiffAddArgs") 71 let g:DirDiffAddArgs = "" 72 endif 73 " Support for i18n (dynamically figure out the diff text) 74 " Defaults to off 75 if !exists("g:DirDiffDynamicDiffText") 76 let g:DirDiffDynamicDiffText = 0 77 endif 78 79 " String used for the English equivalent "Files " 80 if !exists("g:DirDiffTextFiles") 81 let g:DirDiffTextFiles = "Files " 82 endif 83 84 " String used for the English equivalent " and " 85 if !exists("g:DirDiffTextAnd") 86 let g:DirDiffTextAnd = " and " 87 endif 88 89 " String used for the English equivalent " differ") 90 if !exists("g:DirDiffTextDiffer") 91 let g:DirDiffTextDiffer = " differ" 92 endif 93 94 " String used for the English equivalent "Only in ") 95 if !exists("g:DirDiffTextOnlyIn") 96 let g:DirDiffTextOnlyIn = "Only in " 97 endif 98 99 " String used for the English equivalent ": ") 100 if !exists("g:DirDiffTextOnlyInCenter") 101 let g:DirDiffTextOnlyInCenter = ": " 102 endif 103 104 " Set some script specific variables: 105 " 106 let s:DirDiffFirstDiffLine = 6 107 let s:DirDiffALine = 1 108 let s:DirDiffBLine = 2 109 110 " -- Variables used in various utilities 111 if has("unix") 112 let s:DirDiffCopyCmd = "cp" 113 let s:DirDiffCopyFlags = "" 114 let s:DirDiffCopyDirCmd = "cp" 115 let s:DirDiffCopyDirFlags = "-rf" 116 let s:DirDiffCopyInteractiveFlag = "-i" 117 118 let s:DirDiffDeleteCmd = "rm" 119 let s:DirDiffDeleteFlags = "" 120 let s:DirDiffDeleteInteractiveFlag = "-i" 121 122 let s:DirDiffDeleteDirCmd = "rm" 123 let s:DirDiffDeleteDirFlags = "-rf" 124 125 let s:sep = "/" 126 127 let s:DirDiffMakeDirCmd = "!mkdir " 128 129 elseif has("win32") 130 let s:DirDiffCopyCmd = "copy" 131 let s:DirDiffCopyFlags = "" 132 let s:DirDiffCopyDirCmd = "xcopy" 133 let s:DirDiffCopyDirFlags = "/e /i /q" 134 let s:DirDiffCopyInteractiveFlag = "/-y" 135 136 let s:DirDiffDeleteCmd = "del" 137 let s:DirDiffDeleteFlags = "/s /q" 138 let s:DirDiffDeleteInteractiveFlag = "/p" 139 " Windows is somewhat stupid since "del" can only remove the files, not 140 " the directory. The command "rd" would remove files recursively, but it 141 " doesn't really work on a file (!). where is the deltree command??? 142 143 let s:DirDiffDeleteDirCmd = "rd" 144 " rd is by default prompting, we need to handle this in a different way 145 let s:DirDiffDeleteDirFlags = "/s" 146 let s:DirDiffDeleteDirQuietFlag = "/q" 147 148 let s:sep = "\\" 149 150 let s:DirDiffMakeDirCmd = "!mkdir " 151 else 152 " Platforms not supported 153 let s:DirDiffCopyCmd = "" 154 let s:DirDiffCopyFlags = "" 155 let s:DirDiffDeleteCmd = "" 156 let s:DirDiffDeleteFlags = "" 157 let s:sep = "" 158 endif 159 160 161 function! <SID>DirDiff(srcA, srcB) 162 " Setup 163 let DirDiffAbsSrcA = fnamemodify(expand(a:srcA, ":p"), ":p") 164 let DirDiffAbsSrcB = fnamemodify(expand(a:srcB, ":p"), ":p") 165 166 " Check for an internationalized version of diff ? 167 call <SID>GetDiffStrings() 168 169 " Remove the trailing \ or / 170 let DirDiffAbsSrcA = substitute(DirDiffAbsSrcA, '\\$\|/$', '', '') 171 let DirDiffAbsSrcB = substitute(DirDiffAbsSrcB, '\\$\|/$', '', '') 172 173 let DiffBuffer = tempname() 174 " We first write to that file 175 " Constructs the command line 176 let cmd = "!diff" 177 let cmdarg = " -r --brief" 178 179 " If variable is set, we ignore the case 180 if (g:DirDiffIgnoreCase) 181 let cmdarg = cmdarg." -i" 182 endif 183 if (g:DirDiffAddArgs != "") 184 let cmdarg = cmdarg." ".g:DirDiffAddArgs." " 185 endif 186 if (g:DirDiffExcludes != "") 187 let cmdarg = cmdarg.' -x"'.substitute(g:DirDiffExcludes, ',', '" -x"', 'g').'"' 188 endif 189 if (g:DirDiffIgnore != "") 190 let cmdarg = cmdarg.' -I"'.substitute(g:DirDiffIgnore, ',', '" -I"', 'g').'"' 191 endif 192 " Prompt the user for additional arguments 193 " let addarg = input("Additional diff args (current =". cmdarg. "): ") 194 let addarg = "" 195 let cmd = cmd.cmdarg." ".addarg." \"".DirDiffAbsSrcA."\" \"".DirDiffAbsSrcB."\"" 196 let cmd = cmd." > \"".DiffBuffer."\"" 197 198 echo "Diffing directories, it may take a while..." 199 let error = <SID>DirDiffExec(cmd, 0) 200 if (error == 0) 201 redraw | echom "diff found no differences - directories match." 202 return 203 endif 204 silent exe "edit ".DiffBuffer 205 echo "Defining [A] and [B] ... " 206 " We then do a substitution on the directory path 207 " We need to do substitution of the the LONGER string first, otherwise 208 " it'll mix up the A and B directory 209 if (strlen(DirDiffAbsSrcA) > strlen(DirDiffAbsSrcB)) 210 silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcA)."/[A]/" 211 silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcB)."/[B]/" 212 else 213 silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcB)."/[B]/" 214 silent! exe "%s/".<SID>EscapeDirForRegex(DirDiffAbsSrcA)."/[A]/" 215 endif 216 " In windows, diff behaves somewhat weirdly, for the appened path it'll 217 " use "/" instead of "\". Convert this to \ 218 if (has("win32")) 219 silent! %s/\//\\/g 220 endif 221 222 echo "Sorting entries ..." 223 " We then sort the lines if the option is set 224 if (g:DirDiffSort == 1) 225 1,$call <SID>Sort("s:Strcmp") 226 endif 227 228 " Put in spacer in front of each line 229 silent! %s/^/ / 230 231 " We then put the file [A] and [B] on top of the diff lines 232 call append(0, "[A]=". DirDiffAbsSrcA) 233 call append(1, "[B]=". DirDiffAbsSrcB) 234 if g:DirDiffEnableMappings 235 call append(2, "Usage: <Enter>/'o'=open,'s'=sync,'<Leader>dg'=diffget,'<Leader>dp'=diffput,'<Leader>dj'=next,'<Leader>dk'=prev, 'q'=quit") 236 else 237 call append(2, "Usage: <Enter>/'o'=open,'s'=sync,'q'=quit") 238 endif 239 call append(3, "Options: 'u'=update,'x'=set excludes,'i'=set ignore,'a'=set args" ) 240 call append(4, "Diff Args:" . cmdarg) 241 call append(5, "") 242 " go to the beginning of the file 243 0 244 setlocal nomodified 245 setlocal nomodifiable 246 setlocal buftype=nowrite 247 "setlocal buftype=nofile 248 "setlocal bufhidden=delete 249 setlocal bufhidden=hide 250 setlocal nowrap 251 252 " Set up local key bindings 253 " 'n' actually messes with the search next pattern, I think using \dj and 254 " \dk is enough. Otherwise, use j,k, and enter. 255 " nnoremap <buffer> n :call <SID>DirDiffNext()<CR> 256 " nnoremap <buffer> p :call <SID>DirDiffPrev()<CR> 257 nnoremap <buffer> s :. call <SID>DirDiffSync()<CR> 258 vnoremap <buffer> s :call <SID>DirDiffSync()<CR> 259 nnoremap <buffer> u :call <SID>DirDiffUpdate()<CR> 260 nnoremap <buffer> x :call <SID>ChangeExcludes()<CR> 261 nnoremap <buffer> a :call <SID>ChangeArguments()<CR> 262 nnoremap <buffer> i :call <SID>ChangeIgnore()<CR> 263 nnoremap <buffer> q :call <SID>DirDiffQuit()<CR> 264 265 nnoremap <buffer> o :call <SID>DirDiffOpen()<CR> 266 nnoremap <buffer> <CR> :call <SID>DirDiffOpen()<CR> 267 nnoremap <buffer> <2-Leftmouse> :call <SID>DirDiffOpen()<CR> 268 call <SID>SetupSyntax() 269 270 " Open the first diff 271 call <SID>DirDiffNext() 272 endfunction 273 274 " Set up syntax highlighing for the diff window 275 "function! <SID>SetupSyntax() 276 function! <SID>SetupSyntax() 277 if has("syntax") && exists("g:syntax_on") 278 "&& !has("syntax_items") 279 syn match DirDiffSrcA "\[A\]" 280 syn match DirDiffSrcB "\[B\]" 281 syn match DirDiffUsage "^Usage.*" 282 syn match DirDiffOptions "^Options.*" 283 " exec 'syn match DirDiffFiles "' . s:DirDiffDifferLine .'"' 284 " exec 'syn match DirDiffOnly "' . s:DirDiffDiffOnlyLine . '"' 285 syn match DirDiffSelected "^==>.*" contains=DirDiffSrcA,DirDiffSrcB 286 287 hi def link DirDiffSrcA Directory 288 hi def link DirDiffSrcB Type 289 hi def link DirDiffUsage Special 290 hi def link DirDiffOptions Special 291 hi def link DirDiffFiles String 292 hi def link DirDiffOnly PreProc 293 hi def link DirDiffSelected DiffChange 294 endif 295 endfunction 296 297 " You should call this within the diff window 298 function! <SID>DirDiffUpdate() 299 let dirA = <SID>GetBaseDir("A") 300 let dirB = <SID>GetBaseDir("B") 301 call <SID>DirDiff(dirA, dirB) 302 endfun 303 304 " Quit the DirDiff mode 305 function! <SID>DirDiffQuit() 306 let in = confirm ("Are you sure you want to quit DirDiff?", "&Yes\n&No", 2) 307 if (in == 1) 308 call <SID>CloseDiffWindows() 309 bd! 310 endif 311 endfun 312 313 " Returns an escaped version of the path for regex uses 314 function! <SID>EscapeDirForRegex(path) 315 " This list is probably not complete, modify later 316 return escape(a:path, "/\\[]$^~") 317 endfunction 318 319 " Close the opened diff comparison windows if they exist 320 function! <SID>CloseDiffWindows() 321 if (<SID>AreDiffWinsOpened()) 322 wincmd k 323 " Ask the user to save if buffer is modified 324 call <SID>AskIfModified() 325 bd! 326 " User may just have one window opened, we may not need to close 327 " the second diff window 328 if (&diff) 329 call <SID>AskIfModified() 330 bd! 331 endif 332 endif 333 endfunction 334 335 function! <SID>EscapeFileName(path) 336 if (v:version >= 702) 337 return fnameescape(a:path) 338 else 339 " This is not a complete list of escaped character, so it's 340 " not as sophisicated as the fnameescape, but this should 341 " cover most of the cases and should work for Vim version < 342 " 7.2 343 return escape(a:path, " \t\n*?[{`$\\%#'\"|!<") 344 endif 345 endfunction 346 347 function! <SID>EchoErr(varName, varValue) 348 echoe '' . a:varName . ' : ' . a:varValue 349 endfunction 350 351 function! <SID>DirDiffOpen() 352 " First dehighlight the last marked 353 call <SID>DeHighlightLine() 354 355 let buffNumber = bufnr('%') 356 let line = getline(".") 357 358 " We first parse back the [A] and [B] directories from the top of the line 359 let dirA = <SID>GetBaseDir("A") 360 let dirB = <SID>GetBaseDir("B") 361 362 363 " Save the number of this window, to which we wish to return 364 " This is required in case there are other windows open 365 " let thisWindow = winnr() 366 "let thisWindow = winnr(buffNumber) 367 368 exec 'buffer ' . buffNumber 369 370 call <SID>CloseDiffWindows() 371 " Mark the current location of the line 372 "mark n 373 let b:currentDiff = line(".") 374 375 " Ensure we're in the right window 376 " silent! exec thisWindow.'wincmd w' 377 378 " Parse the line and see whether it's a "Only in" or "Files Differ" 379 call <SID>HighlightLine() 380 let fileA = <SID>GetFileNameFromLine("A", line) 381 let fileB = <SID>GetFileNameFromLine("B", line) 382 if <SID>IsOnly(line) 383 " We open the file 384 let fileSrc = <SID>ParseOnlySrc(line) 385 if (fileSrc == "A") 386 let fileToOpen = fileA 387 elseif (fileSrc == "B") 388 let fileToOpen = fileB 389 endif 390 split 391 wincmd k 392 silent exec "edit ". <SID>EscapeFileName(fileToOpen) 393 " Fool the window saying that this is diff 394 diffthis 395 wincmd j 396 " Resize the window 397 exe("resize " . g:DirDiffWindowSize) 398 exe (b:currentDiff) 399 elseif <SID>IsDiffer(line) 400 "Open the diff windows 401 split 402 wincmd k 403 silent exec "edit ".<SID>EscapeFileName(fileB) 404 405 " To ensure that A is on the left and B on the right, splitright must be off 406 " let saved_splitright = &splitright 407 " set nosplitright 408 " silent exec "vert diffsplit ".<SID>EscapeFileName(fileA) 409 " let &splitright = saved_splitright 410 silent exec "leftabove vert diffsplit ".<SID>EscapeFileName(fileA) 411 412 " Go back to the diff window 413 wincmd j 414 " Resize the window 415 exe("resize " . g:DirDiffWindowSize) 416 exe (b:currentDiff) 417 " Center the line 418 exe ("normal z.") 419 else 420 echo "There is no diff at the current line!" 421 endif 422 endfunction 423 424 " Ask the user to save if the buffer is modified 425 " 426 function! <SID>AskIfModified() 427 if (&modified) 428 let input = confirm("File " . expand("%:p") . " has been modified.", "&Save\nCa&ncel", 1) 429 if (input == 1) 430 w! 431 endif 432 endif 433 endfunction 434 435 436 function! <SID>HighlightLine() 437 let savedLine = line(".") 438 exe (b:currentDiff) 439 setlocal modifiable 440 let line = getline(".") 441 if (match(line, "^ ") == 0) 442 s/^ /==> / 443 endif 444 setlocal nomodifiable 445 setlocal nomodified 446 exe (savedLine) 447 " This is necessary since the modified file would make the syntax 448 " disappear. 449 call <SID>SetupSyntax() 450 redraw 451 endfunction 452 453 function! <SID>DeHighlightLine() 454 let savedLine = line(".") 455 exe (b:currentDiff) 456 let line = getline(".") 457 setlocal modifiable 458 if (match(line, "^==> ") == 0) 459 s/^==> / / 460 endif 461 setlocal nomodifiable 462 setlocal nomodified 463 exe (savedLine) 464 redraw 465 endfunction 466 467 " Returns the directory for buffer "A" or "B". You need to be in the diff 468 " buffer though. 469 function! <SID>GetBaseDir(diffName) 470 let currLine = line(".") 471 if (a:diffName == "A") 472 let baseLine = s:DirDiffALine 473 else 474 let baseLine = s:DirDiffBLine 475 endif 476 let regex = '\['.a:diffName.'\]=\(.*\)' 477 let line = getline(baseLine) 478 let rtn = substitute(line, regex , '\1', '') 479 return rtn 480 endfunction 481 482 function! <SID>DirDiffNext() 483 " If the current window is a diff, go down one 484 if (&diff == 1) 485 wincmd j 486 endif 487 " if the current line is <= 6, (within the header range), we go to the 488 " first diff line open it 489 if (line(".") < s:DirDiffFirstDiffLine) 490 exe (s:DirDiffFirstDiffLine) 491 let b:currentDiff = line(".") 492 endif 493 silent! exe (b:currentDiff + 1) 494 call <SID>DirDiffOpen() 495 endfunction 496 497 function! <SID>DirDiffPrev() 498 " If the current window is a diff, go down one 499 if (&diff == 1) 500 wincmd j 501 endif 502 silent! exe (b:currentDiff - 1) 503 call <SID>DirDiffOpen() 504 endfunction 505 506 " For each line, we can perform a recursive copy or delete to sync up the 507 " difference. Returns non-zero if the operation is NOT successful, returns 0 508 " if everything is fine. 509 " 510 function! <SID>DirDiffSyncHelper(AB, line) 511 let fileA = <SID>GetFileNameFromLine("A", a:line) 512 let fileB = <SID>GetFileNameFromLine("B", a:line) 513 " echo "Helper line is ". a:line. " fileA " . fileA . " fileB " . fileB 514 if <SID>IsOnly(a:line) 515 " If a:AB is "A" and the ParseOnlySrc returns "A", that means we need to 516 " copy 517 let fileSrc = <SID>ParseOnlySrc(a:line) 518 let operation = "" 519 if (a:AB == "A" && fileSrc == "A") 520 let operation = "Copy" 521 " Use A, and A has source, thus copy the file from A to B 522 let fileFrom = fileA 523 let fileTo = fileB 524 elseif (a:AB == "A" && fileSrc == "B") 525 let operation = "Delete" 526 " Use A, but B has source, thus delete the file from B 527 let fileFrom = fileB 528 let fileTo = fileA 529 elseif (a:AB == "B" && fileSrc == "A") 530 let operation = "Delete" 531 " Use B, but the source file is A, thus removing A 532 let fileFrom = fileA 533 let fileTo = fileB 534 elseif (a:AB == "B" && fileSrc == "B") 535 " Use B, and B has the source file, thus copy B to A 536 let operation = "Copy" 537 let fileFrom = fileB 538 let fileTo = fileA 539 endif 540 elseif <SID>IsDiffer(a:line) 541 " Copy no matter what 542 let operation = "Copy" 543 if (a:AB == "A") 544 let fileFrom = fileA 545 let fileTo = fileB 546 elseif (a:AB == "B") 547 let fileFrom = fileB 548 let fileTo = fileA 549 endif 550 else 551 echo "There is no diff here!" 552 " Error 553 return 1 554 endif 555 if (operation == "Copy") 556 let rtnCode = <SID>Copy(fileFrom, fileTo) 557 elseif (operation == "Delete") 558 let rtnCode = <SID>Delete(fileFrom) 559 endif 560 return rtnCode 561 endfunction 562 563 " Synchronize the range 564 function! <SID>DirDiffSync() range 565 let answer = 1 566 let silence = 0 567 let syncMaster = "A" 568 let currLine = a:firstline 569 let lastLine = a:lastline 570 let syncCount = 0 571 572 while ((currLine <= lastLine)) 573 " Update the highlight 574 call <SID>DeHighlightLine() 575 let b:currentDiff = currLine 576 call <SID>HighlightLine() 577 let line = getline(currLine) 578 if (!silence) 579 let answer = confirm(substitute(line, "^....", '', ''). "\nSynchronization option:" , "&A -> B\n&B -> A\nA&lways A\nAl&ways B\n&Skip\nCa&ncel", 6) 580 if (answer == 1 || answer == 3) 581 let syncMaster = "A" 582 endif 583 if (answer == 2 || answer == 4) 584 let syncMaster = "B" 585 endif 586 if (answer == 3 || answer == 4) 587 let silence = 1 588 endif 589 if (answer == 5) 590 let currLine = currLine + 1 591 continue 592 endif 593 if (answer == 6) 594 break 595 endif 596 endif 597 598 " call <SID>DeHighlightLine() 599 let rtnCode = <SID>DirDiffSyncHelper(syncMaster, line) 600 if (rtnCode == 0) 601 " Successful 602 let syncCount = syncCount + 1 603 " Assume that the line is synchronized, we delete the entry 604 setlocal modifiable 605 exe (currLine.",".currLine." delete") 606 setlocal nomodifiable 607 setlocal nomodified 608 let lastLine = lastLine - 1 609 else 610 " Failed! 611 let currLine = currLine + 1 612 endif 613 endwhile 614 echo syncCount . " diff item(s) synchronized." 615 endfunction 616 617 " Return file "A" or "B" depending on the line given. If it's a Only line, 618 " either A or B does not exist, but the according value would be returned. 619 function! <SID>GetFileNameFromLine(AB, line) 620 " Determine where the source of the copy is. 621 let dirA = <SID>GetBaseDir("A") 622 let dirB = <SID>GetBaseDir("B") 623 624 let fileToProcess = "" 625 626 if <SID>IsOnly(a:line) 627 let fileToProcess = <SID>ParseOnlyFile(a:line) 628 elseif <SID>IsDiffer(a:line) 629 let regex = '^.*' . s:DirDiffDifferLine . '\[A\]\(.*\)' . s:DirDiffDifferAndLine . '\[B\]\(.*\)' . s:DirDiffDifferEndLine . '.*$' 630 let fileToProcess = substitute(a:line, regex, '\1', '') 631 else 632 endif 633 634 "echo "line : " . a:line. "AB = " . a:AB . " File to Process " . fileToProcess 635 if (a:AB == "A") 636 return dirA . fileToProcess 637 elseif (a:AB == "B") 638 return dirB . fileToProcess 639 else 640 return "" 641 endif 642 endfunction 643 644 "Returns the source (A or B) of the "Only" line 645 function! <SID>ParseOnlySrc(line) 646 return substitute(a:line, '^.*' . s:DirDiffDiffOnlyLine . '\[\(.\)\].*' . s:DirDiffDiffOnlyLineCenter . '.*', '\1', '') 647 endfunction 648 649 function! <SID>ParseOnlyFile(line) 650 let regex = '^.*' . s:DirDiffDiffOnlyLine . '\[.\]\(.*\)' . s:DirDiffDiffOnlyLineCenter . '\(.*\)' 651 let root = substitute(a:line, regex , '\1', '') 652 let file = root . s:sep . substitute(a:line, regex , '\2', '') 653 return file 654 endfunction 655 656 function! <SID>Copy(fileFromOrig, fileToOrig) 657 let fileFrom = substitute(a:fileFromOrig, '/', s:sep, 'g') 658 let fileTo = substitute(a:fileToOrig, '/', s:sep, 'g') 659 echo "Copy from " . fileFrom . " to " . fileTo 660 if (s:DirDiffCopyCmd == "") 661 echo "Copy not supported on this platform" 662 return 1 663 endif 664 665 " Constructs the copy command 666 let copycmd = "!".s:DirDiffCopyCmd." ".s:DirDiffCopyFlags 667 " Append the interactive flag 668 if (g:DirDiffInteractive) 669 let copycmd = copycmd . " " . s:DirDiffCopyInteractiveFlag 670 endif 671 let copycmd = copycmd . " \"".fileFrom."\" \"".fileTo."\"" 672 673 " Constructs the copy directory command 674 let copydircmd = "!".s:DirDiffCopyDirCmd." ".s:DirDiffCopyDirFlags 675 " Append the interactive flag 676 if (g:DirDiffInteractive) 677 let copydircmd = copydircmd . " " . s:DirDiffCopyInteractiveFlag 678 endif 679 let copydircmd = copydircmd . " \"".fileFrom."\" \"".fileTo."\"" 680 681 let error = 0 682 if (isdirectory(fileFrom)) 683 let error = <SID>DirDiffExec(copydircmd, g:DirDiffInteractive) 684 else 685 let error = <SID>DirDiffExec(copycmd, g:DirDiffInteractive) 686 endif 687 if (error != 0) 688 echo "Can't copy from " . fileFrom . " to " . fileTo 689 return 1 690 endif 691 return 0 692 endfunction 693 694 " Would execute the command, either silent or not silent, by the 695 " interactive flag ([0|1]). Returns the v:shell_error after 696 " executing the command. 697 function! <SID>DirDiffExec(cmd, interactive) 698 let error = 0 699 if (a:interactive) 700 exe (a:cmd) 701 let error = v:shell_error 702 else 703 silent exe (a:cmd) 704 let error = v:shell_error 705 endif 706 " let d = input("DirDiffExec: " . a:cmd . " " . a:interactive . " returns " . v:shell_error) 707 return error 708 endfunction 709 710 " Delete the file or directory. Returns 0 if nothing goes wrong, error code 711 " otherwise. 712 function! <SID>Delete(fileFromOrig) 713 let fileFrom = substitute(a:fileFromOrig, '/', s:sep, 'g') 714 echo "Deleting from " . fileFrom 715 if (s:DirDiffDeleteCmd == "") 716 echo "Delete not supported on this platform" 717 return 1 718 endif 719 720 let delcmd = "" 721 722 if (isdirectory(fileFrom)) 723 let delcmd = "!".s:DirDiffDeleteDirCmd." ".s:DirDiffDeleteDirFlags 724 if (g:DirDiffInteractive) 725 " If running on Unix, and we're running in interactive mode, we 726 " append the -i tag 727 if (has("unix")) 728 let delcmd = delcmd . " " . s:DirDiffDeleteInteractiveFlag 729 endif 730 else 731 " If running on windows, and we're not running in interactive 732 " mode, we append the quite flag to the "rd" command 733 if (has("win32")) 734 let delcmd = delcmd . " " . s:DirDiffDeleteDirQuietFlag 735 endif 736 endif 737 else 738 let delcmd = "!".s:DirDiffDeleteCmd." ".s:DirDiffDeleteFlags 739 if (g:DirDiffInteractive) 740 let delcmd = delcmd . " " . s:DirDiffDeleteInteractiveFlag 741 endif 742 endif 743 744 let delcmd = delcmd ." \"".fileFrom."\"" 745 let error = <SID>DirDiffExec(delcmd, g:DirDiffInteractive) 746 if (error != 0) 747 echo "Can't delete " . fileFrom 748 endif 749 return error 750 endfunction 751 752 function! <SID>AreDiffWinsOpened() 753 let currBuff = expand("%:p") 754 let currLine = line(".") 755 wincmd k 756 let abovedBuff = expand("%:p") 757 if (&diff) 758 let abovedIsDiff = 1 759 else 760 let abovedIsDiff = 0 761 endif 762 " Go Back if the aboved buffer is not the same 763 if (currBuff != abovedBuff) 764 wincmd j 765 " Go back to the same line 766 exe (currLine) 767 if (abovedIsDiff == 1) 768 return 1 769 else 770 " Aboved is just a bogus buffer, not a diff buffer 771 return 0 772 endif 773 else 774 exe (currLine) 775 return 0 776 endif 777 endfunction 778 779 " The given line begins with the "Only in" 780 function! <SID>IsOnly(line) 781 return (match(a:line, "^ *" . s:DirDiffDiffOnlyLine . "\\|^==> " . s:DirDiffDiffOnlyLine ) == 0) 782 endfunction 783 784 " The given line begins with the "Files" 785 function! <SID>IsDiffer(line) 786 return (match(a:line, "^ *" . s:DirDiffDifferLine . "\\|^==> " . s:DirDiffDifferLine ) == 0) 787 endfunction 788 789 " Let you modify the Exclude patthern 790 function! <SID>ChangeExcludes() 791 let g:DirDiffExcludes = input ("Exclude pattern (separate multiple patterns with ','): ", g:DirDiffExcludes) 792 echo "\nPress update ('u') to refresh the diff." 793 endfunction 794 795 " Let you modify additional arguments for diff 796 function! <SID>ChangeArguments() 797 let g:DirDiffAddArgs = input ("Additional diff args: ", g:DirDiffAddArgs) 798 echo "\nPress update ('u') to refresh the diff." 799 endfunction 800 801 " Let you modify the Ignore patthern 802 function! <SID>ChangeIgnore() 803 let g:DirDiffIgnore = input ("Ignore pattern (separate multiple patterns with ','): ", g:DirDiffIgnore) 804 echo "\nPress update ('u') to refresh the diff." 805 endfunction 806 807 " Sorting functions from the Vim docs. Use this instead of the sort binary. 808 " 809 " Function for use with Sort(), to compare two strings. 810 func! <SID>Strcmp(str1, str2) 811 if (a:str1 < a:str2) 812 return -1 813 elseif (a:str1 > a:str2) 814 return 1 815 else 816 return 0 817 endif 818 endfunction 819 820 " Sort lines. SortR() is called recursively. 821 func! <SID>SortR(start, end, cmp) 822 if (a:start >= a:end) 823 return 824 endif 825 let partition = a:start - 1 826 let middle = partition 827 let partStr = getline((a:start + a:end) / 2) 828 let i = a:start 829 while (i <= a:end) 830 let str = getline(i) 831 exec "let result = " . a:cmp . "(str, partStr)" 832 if (result <= 0) 833 " Need to put it before the partition. Swap lines i and partition. 834 let partition = partition + 1 835 if (result == 0) 836 let middle = partition 837 endif 838 if (i != partition) 839 let str2 = getline(partition) 840 call setline(i, str2) 841 call setline(partition, str) 842 endif 843 endif 844 let i = i + 1 845 endwhile 846 847 " Now we have a pointer to the "middle" element, as far as partitioning 848 " goes, which could be anywhere before the partition. Make sure it is at 849 " the end of the partition. 850 if (middle != partition) 851 let str = getline(middle) 852 let str2 = getline(partition) 853 call setline(middle, str2) 854 call setline(partition, str) 855 endif 856 call <SID>SortR(a:start, partition - 1, a:cmp) 857 call <SID>SortR(partition + 1, a:end, a:cmp) 858 endfunc 859 860 " To Sort a range of lines, pass the range to Sort() along with the name of a 861 " function that will compare two lines. 862 func! <SID>Sort(cmp) range 863 call <SID>SortR(a:firstline, a:lastline, a:cmp) 864 endfunc 865 866 " Added to deal with internationalized version of diff, which returns a 867 " different string than "Files ... differ" or "Only in ... " 868 869 function! <SID>GetDiffStrings() 870 " Check if we have the dynamic text string turned on. If not, just return 871 " what's set in the global variables 872 873 if (g:DirDiffDynamicDiffText == 0) 874 let s:DirDiffDiffOnlyLineCenter = g:DirDiffTextOnlyInCenter 875 let s:DirDiffDiffOnlyLine = g:DirDiffTextOnlyIn 876 let s:DirDiffDifferLine = g:DirDiffTextFiles 877 let s:DirDiffDifferAndLine = g:DirDiffTextAnd 878 let s:DirDiffDifferEndLine = g:DirDiffTextDiffer 879 return 880 endif 881 882 let tmp1 = tempname() 883 let tmp2 = tempname() 884 let tmpdiff = tempname() 885 886 " We need to pad the backslashes in order to make it match 887 let tmp1rx = <SID>EscapeDirForRegex(tmp1) 888 let tmp2rx = <SID>EscapeDirForRegex(tmp2) 889 let tmpdiffrx = <SID>EscapeDirForRegex(tmpdiff) 890 891 silent exe s:DirDiffMakeDirCmd . "\"" . tmp1 . "\"" 892 silent exe s:DirDiffMakeDirCmd . "\"" . tmp2 . "\"" 893 silent exe "!echo test > \"" . tmp1 . s:sep . "test" . "\"" 894 silent exe "!diff -r --brief \"" . tmp1 . "\" \"" . tmp2 . "\" > \"" . tmpdiff . "\"" 895 896 " Now get the result of that diff cmd 897 silent exe "split ". tmpdiff 898 "echo "First line: " . getline(1) 899 "echo "tmp1: " . tmp1 900 "echo "tmp1rx: " . tmp1rx 901 let regex = '\(^.*\)' . tmp1rx . '\(.*\)' . "test" 902 let s:DirDiffDiffOnlyLine = substitute( getline(1), regex, '\1', '') 903 let s:DirDiffDiffOnlyLineCenter = substitute( getline(1), regex, '\2', '') 904 "echo "DirDiff Only: " . s:DirDiffDiffOnlyLine 905 906 q 907 908 " Now let's get the Differ string 909 "echo "Getting the diff in GetDiffStrings" 910 911 silent exe "!echo testdifferent > \"" . tmp2 . s:sep . "test" . "\"" 912 silent exe "!diff -r --brief \"" . tmp1 . "\" \"" . tmp2 . "\" > \"" . tmpdiff . "\"" 913 914 silent exe "split ". tmpdiff 915 let s:DirDiffDifferLine = substitute( getline(1), tmp1rx . ".*$", "", '') 916 " Note that the diff on cygwin may output '/' instead of '\' for the 917 " separator, so we need to accomodate for both cases 918 let andrx = "^.*" . tmp1rx . "[\\\/]test\\(.*\\)" . tmp2rx . "[\\\/]test.*$" 919 let endrx = "^.*" . tmp1rx . "[\\\/]test.*" . tmp2rx . "[\\\/]test\\(.*$\\)" 920 "echo "andrx : " . andrx 921 "echo "endrx : " . endrx 922 let s:DirDiffDifferAndLine = substitute( getline(1), andrx , "\\1", '') 923 let s:DirDiffDifferEndLine = substitute( getline(1), endrx, "\\1", '') 924 925 "echo "s:DirDiffDifferLine = " . s:DirDiffDifferLine 926 "echo "s:DirDiffDifferAndLine = " . s:DirDiffDifferAndLine 927 "echo "s:DirDiffDifferEndLine = " . s:DirDiffDifferEndLine 928 929 q 930 931 " Delete tmp files 932 "echo "Deleting tmp files." 933 934 call <SID>Delete(tmp1) 935 call <SID>Delete(tmp2) 936 call <SID>Delete(tmpdiff) 937 938 "avoid get diff text again 939 let g:DirDiffTextOnlyInCenter = s:DirDiffDiffOnlyLineCenter 940 let g:DirDiffTextOnlyIn = s:DirDiffDiffOnlyLine 941 let g:DirDiffTextFiles = s:DirDiffDifferLine 942 let g:DirDiffTextAnd = s:DirDiffDifferAndLine 943 let g:DirDiffTextDiffer = s:DirDiffDifferEndLine 944 let g:DirDiffDynamicDiffText = 0 945 946 endfunction