Fossil SCM
Merge the diff enhancements from the diff-experimental branch into trunk.
Commit
bba7aea8ca8ddc57dcc11d4a5b5ffa41ff3ba193
Parent
98cf5c33bc1529e…
2 files changed
+282
-44
+1
-1
+282
-44
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -355,19 +355,21 @@ | ||
| 355 | 355 | typedef struct SbsLine SbsLine; |
| 356 | 356 | struct SbsLine { |
| 357 | 357 | char *zLine; /* The output line under construction */ |
| 358 | 358 | int n; /* Index of next unused slot in the zLine[] */ |
| 359 | 359 | int width; /* Maximum width of a column in the output */ |
| 360 | - unsigned char escHtml; /* True to escape html characters */ | |
| 360 | + unsigned char escHtml; /* True to escape html characters */ | |
| 361 | + int iStart; /* Write zStart prior to character iStart */ | |
| 362 | + const char *zStart; /* A <span> tag */ | |
| 363 | + int iEnd; /* Write </span> prior to character iEnd */ | |
| 361 | 364 | }; |
| 362 | 365 | |
| 363 | 366 | /* |
| 364 | 367 | ** Flags for sbsWriteText() |
| 365 | 368 | */ |
| 366 | -#define SBS_NEWLINE 0x0001 /* End with \n\000 */ | |
| 367 | -#define SBS_PAD 0x0002 /* Pad output to width spaces */ | |
| 368 | -#define SBS_ENDSPAN 0x0004 /* Write a </span> after text */ | |
| 369 | +#define SBS_NEWLINE 0x0001 /* End with \n\000 */ | |
| 370 | +#define SBS_PAD 0x0002 /* Pad output to width spaces */ | |
| 369 | 371 | |
| 370 | 372 | /* |
| 371 | 373 | ** Write up to width characters of pLine into z[]. Translate tabs into |
| 372 | 374 | ** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters |
| 373 | 375 | ** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set. |
| @@ -380,10 +382,20 @@ | ||
| 380 | 382 | const char *zIn = pLine->z; |
| 381 | 383 | char *z = &p->zLine[p->n]; |
| 382 | 384 | int w = p->width; |
| 383 | 385 | for(i=j=k=0; k<w && i<n; i++, k++){ |
| 384 | 386 | char c = zIn[i]; |
| 387 | + if( p->escHtml ){ | |
| 388 | + if( i==p->iStart ){ | |
| 389 | + int x = strlen(p->zStart); | |
| 390 | + memcpy(z+j, p->zStart, x); | |
| 391 | + j += x; | |
| 392 | + }else if( i==p->iEnd ){ | |
| 393 | + memcpy(z+j, "</span>", 7); | |
| 394 | + j += 7; | |
| 395 | + } | |
| 396 | + } | |
| 385 | 397 | if( c=='\t' ){ |
| 386 | 398 | z[j++] = ' '; |
| 387 | 399 | while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; } |
| 388 | 400 | }else if( c=='\r' || c=='\f' ){ |
| 389 | 401 | z[j++] = ' '; |
| @@ -398,11 +410,11 @@ | ||
| 398 | 410 | j += 4; |
| 399 | 411 | }else{ |
| 400 | 412 | z[j++] = c; |
| 401 | 413 | } |
| 402 | 414 | } |
| 403 | - if( (flags & SBS_ENDSPAN) && p->escHtml ){ | |
| 415 | + if( p->escHtml && i<=p->iEnd ){ | |
| 404 | 416 | memcpy(&z[j], "</span>", 7); |
| 405 | 417 | j += 7; |
| 406 | 418 | } |
| 407 | 419 | if( (flags & SBS_PAD)!=0 ){ |
| 408 | 420 | while( k<w ){ k++; z[j++] = ' '; } |
| @@ -444,10 +456,226 @@ | ||
| 444 | 456 | p->n += 6; |
| 445 | 457 | sbsWriteHtml(p, "</span>"); |
| 446 | 458 | p->zLine[p->n++] = ' '; |
| 447 | 459 | } |
| 448 | 460 | |
| 461 | +/* | |
| 462 | +** Write out lines that have been edited. Adjust the highlight to cover | |
| 463 | +** only those parts of the line that actually changed. | |
| 464 | +*/ | |
| 465 | +static void sbsWriteLineChange( | |
| 466 | + SbsLine *p, /* The SBS output line */ | |
| 467 | + DLine *pLeft, /* Left line of the change */ | |
| 468 | + int lnLeft, /* Line number for the left line */ | |
| 469 | + DLine *pRight, /* Right line of the change */ | |
| 470 | + int lnRight /* Line number of the right line */ | |
| 471 | +){ | |
| 472 | + int nLeft; /* Length of left line in bytes */ | |
| 473 | + int nRight; /* Length of right line in bytes */ | |
| 474 | + int nPrefix; /* Length of common prefix */ | |
| 475 | + int nSuffix; /* Length of common suffix */ | |
| 476 | + int width; /* Total column width */ | |
| 477 | + const char *zLeft; /* Text of the left line */ | |
| 478 | + const char *zRight; /* Text of the right line */ | |
| 479 | + | |
| 480 | + nLeft = pLeft->h & LENGTH_MASK; | |
| 481 | + zLeft = pLeft->z; | |
| 482 | + nRight = pRight->h & LENGTH_MASK; | |
| 483 | + zRight = pRight->z; | |
| 484 | + | |
| 485 | + nPrefix = 0; | |
| 486 | + while( nPrefix<nLeft && nPrefix<nRight && zLeft[nPrefix]==zRight[nPrefix] ){ | |
| 487 | + nPrefix++; | |
| 488 | + } | |
| 489 | + nSuffix = 0; | |
| 490 | + if( nPrefix<nLeft && nPrefix<nRight ){ | |
| 491 | + while( nSuffix<nLeft && nSuffix<nRight | |
| 492 | + && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){ | |
| 493 | + nSuffix++; | |
| 494 | + } | |
| 495 | + if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0; | |
| 496 | + } | |
| 497 | + if( nPrefix+nSuffix > nLeft ) nSuffix = nLeft - nPrefix; | |
| 498 | + if( nPrefix+nSuffix > nRight ) nSuffix = nRight - nPrefix; | |
| 499 | + if( nPrefix+nSuffix==nLeft ){ | |
| 500 | + /* Text inserted on the right */ | |
| 501 | + sbsWriteLineno(p, lnLeft); | |
| 502 | + p->iStart = p->iEnd = -1; | |
| 503 | + sbsWriteText(p, pLeft, SBS_PAD); | |
| 504 | + sbsWrite(p, " | ", 3); | |
| 505 | + sbsWriteLineno(p, lnRight); | |
| 506 | + p->iStart = nPrefix; | |
| 507 | + p->iEnd = nRight - nSuffix; | |
| 508 | + p->zStart = "<span class=\"diffadd\">"; | |
| 509 | + sbsWriteText(p, pRight, SBS_NEWLINE); | |
| 510 | + }else if( nPrefix+nSuffix==nRight ){ | |
| 511 | + /* Text deleted from the left */ | |
| 512 | + sbsWriteLineno(p, lnLeft); | |
| 513 | + p->iStart = nPrefix; | |
| 514 | + p->iEnd = nLeft - nSuffix; | |
| 515 | + p->zStart = "<span class=\"diffrm\">"; | |
| 516 | + sbsWriteText(p, pLeft, SBS_PAD); | |
| 517 | + sbsWrite(p, " | ", 3); | |
| 518 | + sbsWriteLineno(p, lnRight); | |
| 519 | + p->iStart = p->iEnd = -1; | |
| 520 | + sbsWriteText(p, pRight, SBS_NEWLINE); | |
| 521 | + }else{ | |
| 522 | + /* Text modified between left and right */ | |
| 523 | + sbsWriteLineno(p, lnLeft); | |
| 524 | + p->iStart = nPrefix; | |
| 525 | + p->iEnd = nLeft - nSuffix; | |
| 526 | + p->zStart = "<span class=\"diffchng\">"; | |
| 527 | + sbsWriteText(p, pLeft, SBS_PAD); | |
| 528 | + sbsWrite(p, " | ", 3); | |
| 529 | + sbsWriteLineno(p, lnRight); | |
| 530 | + p->iEnd = nRight - nSuffix; | |
| 531 | + sbsWriteText(p, pRight, SBS_NEWLINE); | |
| 532 | + } | |
| 533 | +} | |
| 534 | + | |
| 535 | +/* | |
| 536 | +** Return the number between 0 and 100 that is smaller the closer pA and | |
| 537 | +** pB match. Return 0 for a perfect match. Return 100 if pA and pB are | |
| 538 | +** completely different. | |
| 539 | +*/ | |
| 540 | +static int match_dline(DLine *pA, DLine *pB){ | |
| 541 | + const char *zA; | |
| 542 | + const char *zB; | |
| 543 | + int nA; | |
| 544 | + int nB; | |
| 545 | + int minDist; | |
| 546 | + int i; | |
| 547 | + int nMatch; | |
| 548 | + int score; | |
| 549 | + | |
| 550 | + zA = pA->z; | |
| 551 | + zB = pB->z; | |
| 552 | + nA = pA->h & LENGTH_MASK; | |
| 553 | + nB = pB->h & LENGTH_MASK; | |
| 554 | + while( nA>0 && fossil_isspace(zA[0]) ){ nA--; zA++; } | |
| 555 | + while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; } | |
| 556 | + while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; } | |
| 557 | + while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; } | |
| 558 | + minDist = nA; | |
| 559 | + if( minDist>nB ) minDist = nB; | |
| 560 | + if( minDist==0 ){ | |
| 561 | + score = 100; | |
| 562 | + }else{ | |
| 563 | + for(i=0; i<nA && i<nB && zA[i]==zB[i]; i++){} | |
| 564 | + nMatch = i; | |
| 565 | + for(i=1; i<=nA && i<=nB && zA[nA-i]==zB[nB-i]; i++){} | |
| 566 | + nMatch += i-1; | |
| 567 | + score = (nMatch >= minDist) ? 0 : ((minDist - nMatch)*100)/minDist; | |
| 568 | + } | |
| 569 | + return score; | |
| 570 | +} | |
| 571 | + | |
| 572 | +/* | |
| 573 | +** There is a change block in which nLeft lines of text on the left are | |
| 574 | +** converted into nRight lines of text on the right. This routine computes | |
| 575 | +** how the lines on the left line up with the lines on the right. | |
| 576 | +** | |
| 577 | +** The return value is a buffer of unsigned characters, obtained from | |
| 578 | +** fossil_malloc(). (The caller needs to free the return value using | |
| 579 | +** fossil_free().) Entries in the returned array have values as follows: | |
| 580 | +** | |
| 581 | +** 1. Delete the next line of pLeft. | |
| 582 | +** 2. The next line of pLeft changes into the next line of pRight. | |
| 583 | +** 3. Insert the next line of pRight. | |
| 584 | +** | |
| 585 | +** The length of the returned array will be just large enough to cause | |
| 586 | +** all elements of pLeft and pRight to be consumed. | |
| 587 | +** | |
| 588 | +** Algorithm: Wagner's minimum edit-distance algorithm, modified by | |
| 589 | +** adding a cost to each match based on how well the two rows match | |
| 590 | +** each other. Insertion and deletion costs are 50. Match costs | |
| 591 | +** are between 0 and 100 where 0 is a perfect match 100 is a complete | |
| 592 | +** mismatch. | |
| 593 | +*/ | |
| 594 | +static unsigned char *sbsAlignment( | |
| 595 | + DLine *aLeft, int nLeft, /* Text on the left */ | |
| 596 | + DLine *aRight, int nRight /* Text on the right */ | |
| 597 | +){ | |
| 598 | + int i, j, k; /* Loop counters */ | |
| 599 | + int *a; /* One row of the Wagner matrix */ | |
| 600 | + int *pToFree; /* Space that needs to be freed */ | |
| 601 | + unsigned char *aM; /* Wagner result matrix */ | |
| 602 | + int aBuf[100]; /* Stack space for a[] if nRight not to big */ | |
| 603 | + | |
| 604 | + aM = fossil_malloc( (nLeft+1)*(nRight+1) ); | |
| 605 | + if( nLeft==0 ){ | |
| 606 | + memset(aM, 3, nRight); | |
| 607 | + return aM; | |
| 608 | + } | |
| 609 | + if( nRight==0 ){ | |
| 610 | + memset(aM, 1, nLeft); | |
| 611 | + return aM; | |
| 612 | + } | |
| 613 | + if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){ | |
| 614 | + pToFree = 0; | |
| 615 | + a = aBuf; | |
| 616 | + }else{ | |
| 617 | + a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) ); | |
| 618 | + } | |
| 619 | + | |
| 620 | + /* Compute the best alignment */ | |
| 621 | + for(i=0; i<=nRight; i++){ | |
| 622 | + aM[i] = 3; | |
| 623 | + a[i] = i*50; | |
| 624 | + } | |
| 625 | + aM[0] = 0; | |
| 626 | + for(j=1; j<=nLeft; j++){ | |
| 627 | + int p = a[0]; | |
| 628 | + a[0] = p+50; | |
| 629 | + aM[j*(nRight+1)] = 1; | |
| 630 | + for(i=1; i<=nRight; i++){ | |
| 631 | + int m = a[i-1]+50; | |
| 632 | + int d = 3; | |
| 633 | + if( m>a[i]+50 ){ | |
| 634 | + m = a[i]+50; | |
| 635 | + d = 1; | |
| 636 | + } | |
| 637 | + if( m>p ){ | |
| 638 | + int score = match_dline(&aLeft[j-1], &aRight[i-1]); | |
| 639 | + if( (score<66 || (i<j+1 && i>j-1)) && m>p+score ){ | |
| 640 | + m = p+score; | |
| 641 | + d = 2; | |
| 642 | + } | |
| 643 | + } | |
| 644 | + p = a[i]; | |
| 645 | + a[i] = m; | |
| 646 | + aM[j*(nRight+1)+i] = d; | |
| 647 | + } | |
| 648 | + } | |
| 649 | + | |
| 650 | + /* Compute the lowest-cost path back through the matrix */ | |
| 651 | + i = nRight; | |
| 652 | + j = nLeft; | |
| 653 | + k = (nRight+1)*(nLeft+1)-1; | |
| 654 | + while( i+j>0 ){ | |
| 655 | + unsigned char c = aM[k--]; | |
| 656 | + if( c==2 ){ | |
| 657 | + assert( i>0 && j>0 ); | |
| 658 | + i--; | |
| 659 | + j--; | |
| 660 | + }else if( c==3 ){ | |
| 661 | + assert( i>0 ); | |
| 662 | + i--; | |
| 663 | + }else{ | |
| 664 | + assert( j>0 ); | |
| 665 | + j--; | |
| 666 | + } | |
| 667 | + aM[k] = aM[j*(nRight+1)+i]; | |
| 668 | + } | |
| 669 | + k++; | |
| 670 | + i = (nRight+1)*(nLeft+1) - k; | |
| 671 | + memmove(aM, &aM[k], i); | |
| 672 | + | |
| 673 | + /* Return the result */ | |
| 674 | + fossil_free(pToFree); | |
| 675 | + return aM; | |
| 676 | +} | |
| 449 | 677 | |
| 450 | 678 | /* |
| 451 | 679 | ** Given a diff context in which the aEdit[] array has been filled |
| 452 | 680 | ** in, compute a side-by-side diff into pOut. |
| 453 | 681 | */ |
| @@ -474,10 +702,12 @@ | ||
| 474 | 702 | |
| 475 | 703 | s.zLine = fossil_malloc( 10*width + 100 ); |
| 476 | 704 | if( s.zLine==0 ) return; |
| 477 | 705 | s.width = width; |
| 478 | 706 | s.escHtml = escHtml; |
| 707 | + s.iStart = -1; | |
| 708 | + s.iEnd = -1; | |
| 479 | 709 | A = p->aFrom; |
| 480 | 710 | B = p->aTo; |
| 481 | 711 | R = p->aEdit; |
| 482 | 712 | mxr = p->nEdit; |
| 483 | 713 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -527,10 +757,11 @@ | ||
| 527 | 757 | b += skip; |
| 528 | 758 | m = R[r] - skip; |
| 529 | 759 | for(j=0; j<m; j++){ |
| 530 | 760 | s.n = 0; |
| 531 | 761 | sbsWriteLineno(&s, a+j); |
| 762 | + s.iStart = s.iEnd = -1; | |
| 532 | 763 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 533 | 764 | sbsWrite(&s, " ", 3); |
| 534 | 765 | sbsWriteLineno(&s, b+j); |
| 535 | 766 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 536 | 767 | blob_append(pOut, s.zLine, s.n); |
| @@ -538,52 +769,58 @@ | ||
| 538 | 769 | a += m; |
| 539 | 770 | b += m; |
| 540 | 771 | |
| 541 | 772 | /* Show the differences */ |
| 542 | 773 | for(i=0; i<nr; i++){ |
| 543 | - ma = R[r+i*3+1]; | |
| 544 | - mb = R[r+i*3+2]; | |
| 545 | - m = ma<mb ? ma : mb; | |
| 546 | - for(j=0; j<m; j++){ | |
| 547 | - s.n = 0; | |
| 548 | - sbsWriteLineno(&s, a+j); | |
| 549 | - sbsWriteHtml(&s, "<span class=\"diffchng\">"); | |
| 550 | - sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN); | |
| 551 | - sbsWrite(&s, " | ", 3); | |
| 552 | - sbsWriteLineno(&s, b+j); | |
| 553 | - sbsWriteHtml(&s, "<span class=\"diffchng\">"); | |
| 554 | - sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN); | |
| 555 | - blob_append(pOut, s.zLine, s.n); | |
| 556 | - } | |
| 557 | - a += m; | |
| 558 | - b += m; | |
| 559 | - ma -= m; | |
| 560 | - mb -= m; | |
| 561 | - for(j=0; j<ma; j++){ | |
| 562 | - s.n = 0; | |
| 563 | - sbsWriteLineno(&s, a+j); | |
| 564 | - sbsWriteHtml(&s, "<span class=\"diffrm\">"); | |
| 565 | - sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN); | |
| 566 | - sbsWrite(&s, " <\n", 3); | |
| 567 | - blob_append(pOut, s.zLine, s.n); | |
| 568 | - } | |
| 569 | - a += ma; | |
| 570 | - for(j=0; j<mb; j++){ | |
| 571 | - s.n = 0; | |
| 572 | - sbsWriteSpace(&s, width + 7); | |
| 573 | - sbsWrite(&s, " > ", 3); | |
| 574 | - sbsWriteLineno(&s, b+j); | |
| 575 | - sbsWriteHtml(&s, "<span class=\"diffadd\">"); | |
| 576 | - sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN); | |
| 577 | - blob_append(pOut, s.zLine, s.n); | |
| 578 | - } | |
| 579 | - b += mb; | |
| 774 | + unsigned char *alignment; | |
| 775 | + ma = R[r+i*3+1]; /* Lines on left but not on right */ | |
| 776 | + mb = R[r+i*3+2]; /* Lines on right but not on left */ | |
| 777 | + alignment = sbsAlignment(&A[a], ma, &B[b], mb); | |
| 778 | + for(j=0; ma+mb>0; j++){ | |
| 779 | + if( alignment[j]==1 ){ | |
| 780 | + s.n = 0; | |
| 781 | + sbsWriteLineno(&s, a); | |
| 782 | + s.iStart = 0; | |
| 783 | + s.zStart = "<span class=\"diffrm\">"; | |
| 784 | + s.iEnd = s.width; | |
| 785 | + sbsWriteText(&s, &A[a], SBS_PAD); | |
| 786 | + sbsWrite(&s, " <\n", 3); | |
| 787 | + blob_append(pOut, s.zLine, s.n); | |
| 788 | + assert( ma>0 ); | |
| 789 | + ma--; | |
| 790 | + a++; | |
| 791 | + }else if( alignment[j]==2 ){ | |
| 792 | + s.n = 0; | |
| 793 | + sbsWriteLineChange(&s, &A[a], a, &B[b], b); | |
| 794 | + blob_append(pOut, s.zLine, s.n); | |
| 795 | + assert( ma>0 && mb>0 ); | |
| 796 | + ma--; | |
| 797 | + mb--; | |
| 798 | + a++; | |
| 799 | + b++; | |
| 800 | + }else{ | |
| 801 | + s.n = 0; | |
| 802 | + sbsWriteSpace(&s, width + 7); | |
| 803 | + sbsWrite(&s, " > ", 3); | |
| 804 | + sbsWriteLineno(&s, b); | |
| 805 | + s.iStart = 0; | |
| 806 | + s.zStart = "<span class=\"diffadd\">"; | |
| 807 | + s.iEnd = s.width; | |
| 808 | + sbsWriteText(&s, &B[b], SBS_NEWLINE); | |
| 809 | + blob_append(pOut, s.zLine, s.n); | |
| 810 | + assert( mb>0 ); | |
| 811 | + mb--; | |
| 812 | + b++; | |
| 813 | + } | |
| 814 | + } | |
| 815 | + fossil_free(alignment); | |
| 580 | 816 | if( i<nr-1 ){ |
| 581 | 817 | m = R[r+i*3+3]; |
| 582 | 818 | for(j=0; j<m; j++){ |
| 583 | 819 | s.n = 0; |
| 584 | 820 | sbsWriteLineno(&s, a+j); |
| 821 | + s.iStart = s.iEnd = -1; | |
| 585 | 822 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 586 | 823 | sbsWrite(&s, " ", 3); |
| 587 | 824 | sbsWriteLineno(&s, b+j); |
| 588 | 825 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 589 | 826 | blob_append(pOut, s.zLine, s.n); |
| @@ -598,10 +835,11 @@ | ||
| 598 | 835 | m = R[r+nr*3]; |
| 599 | 836 | if( m>nContext ) m = nContext; |
| 600 | 837 | for(j=0; j<m; j++){ |
| 601 | 838 | s.n = 0; |
| 602 | 839 | sbsWriteLineno(&s, a+j); |
| 840 | + s.iStart = s.iEnd = -1; | |
| 603 | 841 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 604 | 842 | sbsWrite(&s, " ", 3); |
| 605 | 843 | sbsWriteLineno(&s, b+j); |
| 606 | 844 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 607 | 845 | blob_append(pOut, s.zLine, s.n); |
| @@ -943,11 +1181,11 @@ | ||
| 943 | 1181 | p->aEdit[r+3]++; |
| 944 | 1182 | cpy--; |
| 945 | 1183 | } |
| 946 | 1184 | |
| 947 | 1185 | /* Shift insertions toward the end of the file */ |
| 948 | - while( p->aEdit[r+3]>0 && del==0 && ins>0 ){ | |
| 1186 | + while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){ | |
| 949 | 1187 | DLine *pTop = &p->aTo[lnTo]; /* First line inserted */ |
| 950 | 1188 | DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */ |
| 951 | 1189 | if( same_dline(pTop, pBtm)==0 ) break; |
| 952 | 1190 | if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break; |
| 953 | 1191 | lnFrom++; |
| @@ -969,11 +1207,11 @@ | ||
| 969 | 1207 | p->aEdit[r+3]++; |
| 970 | 1208 | cpy--; |
| 971 | 1209 | } |
| 972 | 1210 | |
| 973 | 1211 | /* Shift deletions toward the end of the file */ |
| 974 | - while( p->aEdit[r+3]>0 && del>0 && ins==0 ){ | |
| 1212 | + while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){ | |
| 975 | 1213 | DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */ |
| 976 | 1214 | DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */ |
| 977 | 1215 | if( same_dline(pTop, pBtm)==0 ) break; |
| 978 | 1216 | if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break; |
| 979 | 1217 | lnFrom++; |
| 980 | 1218 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -355,19 +355,21 @@ | |
| 355 | typedef struct SbsLine SbsLine; |
| 356 | struct SbsLine { |
| 357 | char *zLine; /* The output line under construction */ |
| 358 | int n; /* Index of next unused slot in the zLine[] */ |
| 359 | int width; /* Maximum width of a column in the output */ |
| 360 | unsigned char escHtml; /* True to escape html characters */ |
| 361 | }; |
| 362 | |
| 363 | /* |
| 364 | ** Flags for sbsWriteText() |
| 365 | */ |
| 366 | #define SBS_NEWLINE 0x0001 /* End with \n\000 */ |
| 367 | #define SBS_PAD 0x0002 /* Pad output to width spaces */ |
| 368 | #define SBS_ENDSPAN 0x0004 /* Write a </span> after text */ |
| 369 | |
| 370 | /* |
| 371 | ** Write up to width characters of pLine into z[]. Translate tabs into |
| 372 | ** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters |
| 373 | ** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set. |
| @@ -380,10 +382,20 @@ | |
| 380 | const char *zIn = pLine->z; |
| 381 | char *z = &p->zLine[p->n]; |
| 382 | int w = p->width; |
| 383 | for(i=j=k=0; k<w && i<n; i++, k++){ |
| 384 | char c = zIn[i]; |
| 385 | if( c=='\t' ){ |
| 386 | z[j++] = ' '; |
| 387 | while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; } |
| 388 | }else if( c=='\r' || c=='\f' ){ |
| 389 | z[j++] = ' '; |
| @@ -398,11 +410,11 @@ | |
| 398 | j += 4; |
| 399 | }else{ |
| 400 | z[j++] = c; |
| 401 | } |
| 402 | } |
| 403 | if( (flags & SBS_ENDSPAN) && p->escHtml ){ |
| 404 | memcpy(&z[j], "</span>", 7); |
| 405 | j += 7; |
| 406 | } |
| 407 | if( (flags & SBS_PAD)!=0 ){ |
| 408 | while( k<w ){ k++; z[j++] = ' '; } |
| @@ -444,10 +456,226 @@ | |
| 444 | p->n += 6; |
| 445 | sbsWriteHtml(p, "</span>"); |
| 446 | p->zLine[p->n++] = ' '; |
| 447 | } |
| 448 | |
| 449 | |
| 450 | /* |
| 451 | ** Given a diff context in which the aEdit[] array has been filled |
| 452 | ** in, compute a side-by-side diff into pOut. |
| 453 | */ |
| @@ -474,10 +702,12 @@ | |
| 474 | |
| 475 | s.zLine = fossil_malloc( 10*width + 100 ); |
| 476 | if( s.zLine==0 ) return; |
| 477 | s.width = width; |
| 478 | s.escHtml = escHtml; |
| 479 | A = p->aFrom; |
| 480 | B = p->aTo; |
| 481 | R = p->aEdit; |
| 482 | mxr = p->nEdit; |
| 483 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -527,10 +757,11 @@ | |
| 527 | b += skip; |
| 528 | m = R[r] - skip; |
| 529 | for(j=0; j<m; j++){ |
| 530 | s.n = 0; |
| 531 | sbsWriteLineno(&s, a+j); |
| 532 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 533 | sbsWrite(&s, " ", 3); |
| 534 | sbsWriteLineno(&s, b+j); |
| 535 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 536 | blob_append(pOut, s.zLine, s.n); |
| @@ -538,52 +769,58 @@ | |
| 538 | a += m; |
| 539 | b += m; |
| 540 | |
| 541 | /* Show the differences */ |
| 542 | for(i=0; i<nr; i++){ |
| 543 | ma = R[r+i*3+1]; |
| 544 | mb = R[r+i*3+2]; |
| 545 | m = ma<mb ? ma : mb; |
| 546 | for(j=0; j<m; j++){ |
| 547 | s.n = 0; |
| 548 | sbsWriteLineno(&s, a+j); |
| 549 | sbsWriteHtml(&s, "<span class=\"diffchng\">"); |
| 550 | sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN); |
| 551 | sbsWrite(&s, " | ", 3); |
| 552 | sbsWriteLineno(&s, b+j); |
| 553 | sbsWriteHtml(&s, "<span class=\"diffchng\">"); |
| 554 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN); |
| 555 | blob_append(pOut, s.zLine, s.n); |
| 556 | } |
| 557 | a += m; |
| 558 | b += m; |
| 559 | ma -= m; |
| 560 | mb -= m; |
| 561 | for(j=0; j<ma; j++){ |
| 562 | s.n = 0; |
| 563 | sbsWriteLineno(&s, a+j); |
| 564 | sbsWriteHtml(&s, "<span class=\"diffrm\">"); |
| 565 | sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN); |
| 566 | sbsWrite(&s, " <\n", 3); |
| 567 | blob_append(pOut, s.zLine, s.n); |
| 568 | } |
| 569 | a += ma; |
| 570 | for(j=0; j<mb; j++){ |
| 571 | s.n = 0; |
| 572 | sbsWriteSpace(&s, width + 7); |
| 573 | sbsWrite(&s, " > ", 3); |
| 574 | sbsWriteLineno(&s, b+j); |
| 575 | sbsWriteHtml(&s, "<span class=\"diffadd\">"); |
| 576 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN); |
| 577 | blob_append(pOut, s.zLine, s.n); |
| 578 | } |
| 579 | b += mb; |
| 580 | if( i<nr-1 ){ |
| 581 | m = R[r+i*3+3]; |
| 582 | for(j=0; j<m; j++){ |
| 583 | s.n = 0; |
| 584 | sbsWriteLineno(&s, a+j); |
| 585 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 586 | sbsWrite(&s, " ", 3); |
| 587 | sbsWriteLineno(&s, b+j); |
| 588 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 589 | blob_append(pOut, s.zLine, s.n); |
| @@ -598,10 +835,11 @@ | |
| 598 | m = R[r+nr*3]; |
| 599 | if( m>nContext ) m = nContext; |
| 600 | for(j=0; j<m; j++){ |
| 601 | s.n = 0; |
| 602 | sbsWriteLineno(&s, a+j); |
| 603 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 604 | sbsWrite(&s, " ", 3); |
| 605 | sbsWriteLineno(&s, b+j); |
| 606 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 607 | blob_append(pOut, s.zLine, s.n); |
| @@ -943,11 +1181,11 @@ | |
| 943 | p->aEdit[r+3]++; |
| 944 | cpy--; |
| 945 | } |
| 946 | |
| 947 | /* Shift insertions toward the end of the file */ |
| 948 | while( p->aEdit[r+3]>0 && del==0 && ins>0 ){ |
| 949 | DLine *pTop = &p->aTo[lnTo]; /* First line inserted */ |
| 950 | DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */ |
| 951 | if( same_dline(pTop, pBtm)==0 ) break; |
| 952 | if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break; |
| 953 | lnFrom++; |
| @@ -969,11 +1207,11 @@ | |
| 969 | p->aEdit[r+3]++; |
| 970 | cpy--; |
| 971 | } |
| 972 | |
| 973 | /* Shift deletions toward the end of the file */ |
| 974 | while( p->aEdit[r+3]>0 && del>0 && ins==0 ){ |
| 975 | DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */ |
| 976 | DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */ |
| 977 | if( same_dline(pTop, pBtm)==0 ) break; |
| 978 | if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break; |
| 979 | lnFrom++; |
| 980 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -355,19 +355,21 @@ | |
| 355 | typedef struct SbsLine SbsLine; |
| 356 | struct SbsLine { |
| 357 | char *zLine; /* The output line under construction */ |
| 358 | int n; /* Index of next unused slot in the zLine[] */ |
| 359 | int width; /* Maximum width of a column in the output */ |
| 360 | unsigned char escHtml; /* True to escape html characters */ |
| 361 | int iStart; /* Write zStart prior to character iStart */ |
| 362 | const char *zStart; /* A <span> tag */ |
| 363 | int iEnd; /* Write </span> prior to character iEnd */ |
| 364 | }; |
| 365 | |
| 366 | /* |
| 367 | ** Flags for sbsWriteText() |
| 368 | */ |
| 369 | #define SBS_NEWLINE 0x0001 /* End with \n\000 */ |
| 370 | #define SBS_PAD 0x0002 /* Pad output to width spaces */ |
| 371 | |
| 372 | /* |
| 373 | ** Write up to width characters of pLine into z[]. Translate tabs into |
| 374 | ** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters |
| 375 | ** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set. |
| @@ -380,10 +382,20 @@ | |
| 382 | const char *zIn = pLine->z; |
| 383 | char *z = &p->zLine[p->n]; |
| 384 | int w = p->width; |
| 385 | for(i=j=k=0; k<w && i<n; i++, k++){ |
| 386 | char c = zIn[i]; |
| 387 | if( p->escHtml ){ |
| 388 | if( i==p->iStart ){ |
| 389 | int x = strlen(p->zStart); |
| 390 | memcpy(z+j, p->zStart, x); |
| 391 | j += x; |
| 392 | }else if( i==p->iEnd ){ |
| 393 | memcpy(z+j, "</span>", 7); |
| 394 | j += 7; |
| 395 | } |
| 396 | } |
| 397 | if( c=='\t' ){ |
| 398 | z[j++] = ' '; |
| 399 | while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; } |
| 400 | }else if( c=='\r' || c=='\f' ){ |
| 401 | z[j++] = ' '; |
| @@ -398,11 +410,11 @@ | |
| 410 | j += 4; |
| 411 | }else{ |
| 412 | z[j++] = c; |
| 413 | } |
| 414 | } |
| 415 | if( p->escHtml && i<=p->iEnd ){ |
| 416 | memcpy(&z[j], "</span>", 7); |
| 417 | j += 7; |
| 418 | } |
| 419 | if( (flags & SBS_PAD)!=0 ){ |
| 420 | while( k<w ){ k++; z[j++] = ' '; } |
| @@ -444,10 +456,226 @@ | |
| 456 | p->n += 6; |
| 457 | sbsWriteHtml(p, "</span>"); |
| 458 | p->zLine[p->n++] = ' '; |
| 459 | } |
| 460 | |
| 461 | /* |
| 462 | ** Write out lines that have been edited. Adjust the highlight to cover |
| 463 | ** only those parts of the line that actually changed. |
| 464 | */ |
| 465 | static void sbsWriteLineChange( |
| 466 | SbsLine *p, /* The SBS output line */ |
| 467 | DLine *pLeft, /* Left line of the change */ |
| 468 | int lnLeft, /* Line number for the left line */ |
| 469 | DLine *pRight, /* Right line of the change */ |
| 470 | int lnRight /* Line number of the right line */ |
| 471 | ){ |
| 472 | int nLeft; /* Length of left line in bytes */ |
| 473 | int nRight; /* Length of right line in bytes */ |
| 474 | int nPrefix; /* Length of common prefix */ |
| 475 | int nSuffix; /* Length of common suffix */ |
| 476 | int width; /* Total column width */ |
| 477 | const char *zLeft; /* Text of the left line */ |
| 478 | const char *zRight; /* Text of the right line */ |
| 479 | |
| 480 | nLeft = pLeft->h & LENGTH_MASK; |
| 481 | zLeft = pLeft->z; |
| 482 | nRight = pRight->h & LENGTH_MASK; |
| 483 | zRight = pRight->z; |
| 484 | |
| 485 | nPrefix = 0; |
| 486 | while( nPrefix<nLeft && nPrefix<nRight && zLeft[nPrefix]==zRight[nPrefix] ){ |
| 487 | nPrefix++; |
| 488 | } |
| 489 | nSuffix = 0; |
| 490 | if( nPrefix<nLeft && nPrefix<nRight ){ |
| 491 | while( nSuffix<nLeft && nSuffix<nRight |
| 492 | && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){ |
| 493 | nSuffix++; |
| 494 | } |
| 495 | if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0; |
| 496 | } |
| 497 | if( nPrefix+nSuffix > nLeft ) nSuffix = nLeft - nPrefix; |
| 498 | if( nPrefix+nSuffix > nRight ) nSuffix = nRight - nPrefix; |
| 499 | if( nPrefix+nSuffix==nLeft ){ |
| 500 | /* Text inserted on the right */ |
| 501 | sbsWriteLineno(p, lnLeft); |
| 502 | p->iStart = p->iEnd = -1; |
| 503 | sbsWriteText(p, pLeft, SBS_PAD); |
| 504 | sbsWrite(p, " | ", 3); |
| 505 | sbsWriteLineno(p, lnRight); |
| 506 | p->iStart = nPrefix; |
| 507 | p->iEnd = nRight - nSuffix; |
| 508 | p->zStart = "<span class=\"diffadd\">"; |
| 509 | sbsWriteText(p, pRight, SBS_NEWLINE); |
| 510 | }else if( nPrefix+nSuffix==nRight ){ |
| 511 | /* Text deleted from the left */ |
| 512 | sbsWriteLineno(p, lnLeft); |
| 513 | p->iStart = nPrefix; |
| 514 | p->iEnd = nLeft - nSuffix; |
| 515 | p->zStart = "<span class=\"diffrm\">"; |
| 516 | sbsWriteText(p, pLeft, SBS_PAD); |
| 517 | sbsWrite(p, " | ", 3); |
| 518 | sbsWriteLineno(p, lnRight); |
| 519 | p->iStart = p->iEnd = -1; |
| 520 | sbsWriteText(p, pRight, SBS_NEWLINE); |
| 521 | }else{ |
| 522 | /* Text modified between left and right */ |
| 523 | sbsWriteLineno(p, lnLeft); |
| 524 | p->iStart = nPrefix; |
| 525 | p->iEnd = nLeft - nSuffix; |
| 526 | p->zStart = "<span class=\"diffchng\">"; |
| 527 | sbsWriteText(p, pLeft, SBS_PAD); |
| 528 | sbsWrite(p, " | ", 3); |
| 529 | sbsWriteLineno(p, lnRight); |
| 530 | p->iEnd = nRight - nSuffix; |
| 531 | sbsWriteText(p, pRight, SBS_NEWLINE); |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | /* |
| 536 | ** Return the number between 0 and 100 that is smaller the closer pA and |
| 537 | ** pB match. Return 0 for a perfect match. Return 100 if pA and pB are |
| 538 | ** completely different. |
| 539 | */ |
| 540 | static int match_dline(DLine *pA, DLine *pB){ |
| 541 | const char *zA; |
| 542 | const char *zB; |
| 543 | int nA; |
| 544 | int nB; |
| 545 | int minDist; |
| 546 | int i; |
| 547 | int nMatch; |
| 548 | int score; |
| 549 | |
| 550 | zA = pA->z; |
| 551 | zB = pB->z; |
| 552 | nA = pA->h & LENGTH_MASK; |
| 553 | nB = pB->h & LENGTH_MASK; |
| 554 | while( nA>0 && fossil_isspace(zA[0]) ){ nA--; zA++; } |
| 555 | while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; } |
| 556 | while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; } |
| 557 | while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; } |
| 558 | minDist = nA; |
| 559 | if( minDist>nB ) minDist = nB; |
| 560 | if( minDist==0 ){ |
| 561 | score = 100; |
| 562 | }else{ |
| 563 | for(i=0; i<nA && i<nB && zA[i]==zB[i]; i++){} |
| 564 | nMatch = i; |
| 565 | for(i=1; i<=nA && i<=nB && zA[nA-i]==zB[nB-i]; i++){} |
| 566 | nMatch += i-1; |
| 567 | score = (nMatch >= minDist) ? 0 : ((minDist - nMatch)*100)/minDist; |
| 568 | } |
| 569 | return score; |
| 570 | } |
| 571 | |
| 572 | /* |
| 573 | ** There is a change block in which nLeft lines of text on the left are |
| 574 | ** converted into nRight lines of text on the right. This routine computes |
| 575 | ** how the lines on the left line up with the lines on the right. |
| 576 | ** |
| 577 | ** The return value is a buffer of unsigned characters, obtained from |
| 578 | ** fossil_malloc(). (The caller needs to free the return value using |
| 579 | ** fossil_free().) Entries in the returned array have values as follows: |
| 580 | ** |
| 581 | ** 1. Delete the next line of pLeft. |
| 582 | ** 2. The next line of pLeft changes into the next line of pRight. |
| 583 | ** 3. Insert the next line of pRight. |
| 584 | ** |
| 585 | ** The length of the returned array will be just large enough to cause |
| 586 | ** all elements of pLeft and pRight to be consumed. |
| 587 | ** |
| 588 | ** Algorithm: Wagner's minimum edit-distance algorithm, modified by |
| 589 | ** adding a cost to each match based on how well the two rows match |
| 590 | ** each other. Insertion and deletion costs are 50. Match costs |
| 591 | ** are between 0 and 100 where 0 is a perfect match 100 is a complete |
| 592 | ** mismatch. |
| 593 | */ |
| 594 | static unsigned char *sbsAlignment( |
| 595 | DLine *aLeft, int nLeft, /* Text on the left */ |
| 596 | DLine *aRight, int nRight /* Text on the right */ |
| 597 | ){ |
| 598 | int i, j, k; /* Loop counters */ |
| 599 | int *a; /* One row of the Wagner matrix */ |
| 600 | int *pToFree; /* Space that needs to be freed */ |
| 601 | unsigned char *aM; /* Wagner result matrix */ |
| 602 | int aBuf[100]; /* Stack space for a[] if nRight not to big */ |
| 603 | |
| 604 | aM = fossil_malloc( (nLeft+1)*(nRight+1) ); |
| 605 | if( nLeft==0 ){ |
| 606 | memset(aM, 3, nRight); |
| 607 | return aM; |
| 608 | } |
| 609 | if( nRight==0 ){ |
| 610 | memset(aM, 1, nLeft); |
| 611 | return aM; |
| 612 | } |
| 613 | if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){ |
| 614 | pToFree = 0; |
| 615 | a = aBuf; |
| 616 | }else{ |
| 617 | a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) ); |
| 618 | } |
| 619 | |
| 620 | /* Compute the best alignment */ |
| 621 | for(i=0; i<=nRight; i++){ |
| 622 | aM[i] = 3; |
| 623 | a[i] = i*50; |
| 624 | } |
| 625 | aM[0] = 0; |
| 626 | for(j=1; j<=nLeft; j++){ |
| 627 | int p = a[0]; |
| 628 | a[0] = p+50; |
| 629 | aM[j*(nRight+1)] = 1; |
| 630 | for(i=1; i<=nRight; i++){ |
| 631 | int m = a[i-1]+50; |
| 632 | int d = 3; |
| 633 | if( m>a[i]+50 ){ |
| 634 | m = a[i]+50; |
| 635 | d = 1; |
| 636 | } |
| 637 | if( m>p ){ |
| 638 | int score = match_dline(&aLeft[j-1], &aRight[i-1]); |
| 639 | if( (score<66 || (i<j+1 && i>j-1)) && m>p+score ){ |
| 640 | m = p+score; |
| 641 | d = 2; |
| 642 | } |
| 643 | } |
| 644 | p = a[i]; |
| 645 | a[i] = m; |
| 646 | aM[j*(nRight+1)+i] = d; |
| 647 | } |
| 648 | } |
| 649 | |
| 650 | /* Compute the lowest-cost path back through the matrix */ |
| 651 | i = nRight; |
| 652 | j = nLeft; |
| 653 | k = (nRight+1)*(nLeft+1)-1; |
| 654 | while( i+j>0 ){ |
| 655 | unsigned char c = aM[k--]; |
| 656 | if( c==2 ){ |
| 657 | assert( i>0 && j>0 ); |
| 658 | i--; |
| 659 | j--; |
| 660 | }else if( c==3 ){ |
| 661 | assert( i>0 ); |
| 662 | i--; |
| 663 | }else{ |
| 664 | assert( j>0 ); |
| 665 | j--; |
| 666 | } |
| 667 | aM[k] = aM[j*(nRight+1)+i]; |
| 668 | } |
| 669 | k++; |
| 670 | i = (nRight+1)*(nLeft+1) - k; |
| 671 | memmove(aM, &aM[k], i); |
| 672 | |
| 673 | /* Return the result */ |
| 674 | fossil_free(pToFree); |
| 675 | return aM; |
| 676 | } |
| 677 | |
| 678 | /* |
| 679 | ** Given a diff context in which the aEdit[] array has been filled |
| 680 | ** in, compute a side-by-side diff into pOut. |
| 681 | */ |
| @@ -474,10 +702,12 @@ | |
| 702 | |
| 703 | s.zLine = fossil_malloc( 10*width + 100 ); |
| 704 | if( s.zLine==0 ) return; |
| 705 | s.width = width; |
| 706 | s.escHtml = escHtml; |
| 707 | s.iStart = -1; |
| 708 | s.iEnd = -1; |
| 709 | A = p->aFrom; |
| 710 | B = p->aTo; |
| 711 | R = p->aEdit; |
| 712 | mxr = p->nEdit; |
| 713 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -527,10 +757,11 @@ | |
| 757 | b += skip; |
| 758 | m = R[r] - skip; |
| 759 | for(j=0; j<m; j++){ |
| 760 | s.n = 0; |
| 761 | sbsWriteLineno(&s, a+j); |
| 762 | s.iStart = s.iEnd = -1; |
| 763 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 764 | sbsWrite(&s, " ", 3); |
| 765 | sbsWriteLineno(&s, b+j); |
| 766 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 767 | blob_append(pOut, s.zLine, s.n); |
| @@ -538,52 +769,58 @@ | |
| 769 | a += m; |
| 770 | b += m; |
| 771 | |
| 772 | /* Show the differences */ |
| 773 | for(i=0; i<nr; i++){ |
| 774 | unsigned char *alignment; |
| 775 | ma = R[r+i*3+1]; /* Lines on left but not on right */ |
| 776 | mb = R[r+i*3+2]; /* Lines on right but not on left */ |
| 777 | alignment = sbsAlignment(&A[a], ma, &B[b], mb); |
| 778 | for(j=0; ma+mb>0; j++){ |
| 779 | if( alignment[j]==1 ){ |
| 780 | s.n = 0; |
| 781 | sbsWriteLineno(&s, a); |
| 782 | s.iStart = 0; |
| 783 | s.zStart = "<span class=\"diffrm\">"; |
| 784 | s.iEnd = s.width; |
| 785 | sbsWriteText(&s, &A[a], SBS_PAD); |
| 786 | sbsWrite(&s, " <\n", 3); |
| 787 | blob_append(pOut, s.zLine, s.n); |
| 788 | assert( ma>0 ); |
| 789 | ma--; |
| 790 | a++; |
| 791 | }else if( alignment[j]==2 ){ |
| 792 | s.n = 0; |
| 793 | sbsWriteLineChange(&s, &A[a], a, &B[b], b); |
| 794 | blob_append(pOut, s.zLine, s.n); |
| 795 | assert( ma>0 && mb>0 ); |
| 796 | ma--; |
| 797 | mb--; |
| 798 | a++; |
| 799 | b++; |
| 800 | }else{ |
| 801 | s.n = 0; |
| 802 | sbsWriteSpace(&s, width + 7); |
| 803 | sbsWrite(&s, " > ", 3); |
| 804 | sbsWriteLineno(&s, b); |
| 805 | s.iStart = 0; |
| 806 | s.zStart = "<span class=\"diffadd\">"; |
| 807 | s.iEnd = s.width; |
| 808 | sbsWriteText(&s, &B[b], SBS_NEWLINE); |
| 809 | blob_append(pOut, s.zLine, s.n); |
| 810 | assert( mb>0 ); |
| 811 | mb--; |
| 812 | b++; |
| 813 | } |
| 814 | } |
| 815 | fossil_free(alignment); |
| 816 | if( i<nr-1 ){ |
| 817 | m = R[r+i*3+3]; |
| 818 | for(j=0; j<m; j++){ |
| 819 | s.n = 0; |
| 820 | sbsWriteLineno(&s, a+j); |
| 821 | s.iStart = s.iEnd = -1; |
| 822 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 823 | sbsWrite(&s, " ", 3); |
| 824 | sbsWriteLineno(&s, b+j); |
| 825 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 826 | blob_append(pOut, s.zLine, s.n); |
| @@ -598,10 +835,11 @@ | |
| 835 | m = R[r+nr*3]; |
| 836 | if( m>nContext ) m = nContext; |
| 837 | for(j=0; j<m; j++){ |
| 838 | s.n = 0; |
| 839 | sbsWriteLineno(&s, a+j); |
| 840 | s.iStart = s.iEnd = -1; |
| 841 | sbsWriteText(&s, &A[a+j], SBS_PAD); |
| 842 | sbsWrite(&s, " ", 3); |
| 843 | sbsWriteLineno(&s, b+j); |
| 844 | sbsWriteText(&s, &B[b+j], SBS_NEWLINE); |
| 845 | blob_append(pOut, s.zLine, s.n); |
| @@ -943,11 +1181,11 @@ | |
| 1181 | p->aEdit[r+3]++; |
| 1182 | cpy--; |
| 1183 | } |
| 1184 | |
| 1185 | /* Shift insertions toward the end of the file */ |
| 1186 | while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){ |
| 1187 | DLine *pTop = &p->aTo[lnTo]; /* First line inserted */ |
| 1188 | DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */ |
| 1189 | if( same_dline(pTop, pBtm)==0 ) break; |
| 1190 | if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break; |
| 1191 | lnFrom++; |
| @@ -969,11 +1207,11 @@ | |
| 1207 | p->aEdit[r+3]++; |
| 1208 | cpy--; |
| 1209 | } |
| 1210 | |
| 1211 | /* Shift deletions toward the end of the file */ |
| 1212 | while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){ |
| 1213 | DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */ |
| 1214 | DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */ |
| 1215 | if( same_dline(pTop, pBtm)==0 ) break; |
| 1216 | if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break; |
| 1217 | lnFrom++; |
| 1218 |
+1
-1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -764,11 +764,11 @@ | ||
| 764 | 764 | @ font-family: monospace; |
| 765 | 765 | @ white-space: pre; |
| 766 | 766 | }, |
| 767 | 767 | { "span.diffchng", |
| 768 | 768 | "changes in a diff", |
| 769 | - @ background-color: #ffffc8; | |
| 769 | + @ background-color: #e0e0ff; | |
| 770 | 770 | }, |
| 771 | 771 | { "span.diffadd", |
| 772 | 772 | "added code in a diff", |
| 773 | 773 | @ background-color: #e0ffe0; |
| 774 | 774 | }, |
| 775 | 775 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -764,11 +764,11 @@ | |
| 764 | @ font-family: monospace; |
| 765 | @ white-space: pre; |
| 766 | }, |
| 767 | { "span.diffchng", |
| 768 | "changes in a diff", |
| 769 | @ background-color: #ffffc8; |
| 770 | }, |
| 771 | { "span.diffadd", |
| 772 | "added code in a diff", |
| 773 | @ background-color: #e0ffe0; |
| 774 | }, |
| 775 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -764,11 +764,11 @@ | |
| 764 | @ font-family: monospace; |
| 765 | @ white-space: pre; |
| 766 | }, |
| 767 | { "span.diffchng", |
| 768 | "changes in a diff", |
| 769 | @ background-color: #e0e0ff; |
| 770 | }, |
| 771 | { "span.diffadd", |
| 772 | "added code in a diff", |
| 773 | @ background-color: #e0ffe0; |
| 774 | }, |
| 775 |