Fossil SCM
Clean up the data structures associated with adding color to diffs in preparation for attempts to improve the coloration.
Commit
8c619bf2782e955b19ea2feb6c8b9293ada57d1bac41206e12e056e0bfc67eda
Parent
c717d2803e19a12…
1 file changed
+110
-94
+110
-94
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -120,10 +120,16 @@ | ||
| 120 | 120 | DLine *aTo; /* File on right side of the diff */ |
| 121 | 121 | int nTo; /* Number of lines in aTo[] */ |
| 122 | 122 | int (*xDiffer)(const DLine*,const DLine*); /* comparison function */ |
| 123 | 123 | }; |
| 124 | 124 | |
| 125 | +/* <span> text for change coloration | |
| 126 | +*/ | |
| 127 | +static const char zClassRm[] = "<span class=\"diffrm\">"; | |
| 128 | +static const char zClassAdd[] = "<span class=\"diffadd\">"; | |
| 129 | +static const char zClassChng[] = "<span class=\"diffchng\">"; | |
| 130 | + | |
| 125 | 131 | /* |
| 126 | 132 | ** Count the number of lines in the input string. Include the last line |
| 127 | 133 | ** in the count even if it lacks the \n terminator. If an empty string |
| 128 | 134 | ** is specified, the number of lines is zero. For the purposes of this |
| 129 | 135 | ** function, a string is considered empty if it contains no characters |
| @@ -301,13 +307,13 @@ | ||
| 301 | 307 | blob_append(pOut, &cPrefix, 1); |
| 302 | 308 | if( html ){ |
| 303 | 309 | if( pRe && re_dline_match(pRe, pLine, 1)==0 ){ |
| 304 | 310 | cPrefix = ' '; |
| 305 | 311 | }else if( cPrefix=='+' ){ |
| 306 | - blob_append(pOut, "<span class=\"diffadd\">", -1); | |
| 312 | + blob_append(pOut, zClassAdd, -1); | |
| 307 | 313 | }else if( cPrefix=='-' ){ |
| 308 | - blob_append(pOut, "<span class=\"diffrm\">", -1); | |
| 314 | + blob_append(pOut, zClassRm, -1); | |
| 309 | 315 | } |
| 310 | 316 | htmlize_to_blob(pOut, pLine->z, pLine->n); |
| 311 | 317 | if( cPrefix!=' ' ){ |
| 312 | 318 | blob_append(pOut, "</span>", -1); |
| 313 | 319 | } |
| @@ -506,26 +512,32 @@ | ||
| 506 | 512 | } |
| 507 | 513 | } |
| 508 | 514 | if( html ) blob_append(pOut, "</pre>\n", -1); |
| 509 | 515 | } |
| 510 | 516 | |
| 517 | +/* | |
| 518 | +** Maximum number of change regions per line | |
| 519 | +*/ | |
| 520 | +#define SBS_MXN 4 | |
| 521 | + | |
| 511 | 522 | /* |
| 512 | 523 | ** Status of a single output line |
| 513 | 524 | */ |
| 514 | 525 | typedef struct SbsLine SbsLine; |
| 515 | 526 | struct SbsLine { |
| 516 | 527 | Blob *apCols[5]; /* Array of pointers to output columns */ |
| 517 | 528 | int width; /* Maximum width of a column in the output */ |
| 518 | 529 | unsigned char escHtml; /* True to escape html characters */ |
| 519 | - int iStart; /* Write zStart prior to character iStart */ | |
| 520 | - const char *zStart; /* A <span> tag */ | |
| 521 | - int iEnd; /* Write </span> prior to character iEnd */ | |
| 522 | - int iStart2; /* Write zStart2 prior to character iStart2 */ | |
| 523 | - const char *zStart2; /* A <span> tag */ | |
| 524 | - int iEnd2; /* Write </span> prior to character iEnd2 */ | |
| 530 | + struct SbsMark { | |
| 531 | + int iStart; /* Write zTag prior to character iStart */ | |
| 532 | + const char *zTag; /* A <span> tag for coloration */ | |
| 533 | + int iEnd; /* Write </span> prior to character iEnd */ | |
| 534 | + } a[SBS_MXN]; /* Change regions */ | |
| 535 | + int n; /* Number of change regions used */ | |
| 525 | 536 | ReCompiled *pRe; /* Only colorize matching lines, if not NULL */ |
| 526 | 537 | }; |
| 538 | + | |
| 527 | 539 | |
| 528 | 540 | /* |
| 529 | 541 | ** Column indices for SbsLine.apCols[] |
| 530 | 542 | */ |
| 531 | 543 | #define SBS_LNA 0 /* Left line number */ |
| @@ -575,25 +587,23 @@ | ||
| 575 | 587 | colorize = 0; |
| 576 | 588 | } |
| 577 | 589 | for(i=k=0; (p->escHtml || k<w) && i<n; i++, k++){ |
| 578 | 590 | char c = zIn[i]; |
| 579 | 591 | if( colorize ){ |
| 580 | - if( i==p->iStart ){ | |
| 581 | - int x = strlen(p->zStart); | |
| 582 | - blob_append(pCol, p->zStart, x); | |
| 592 | + if( i==p->a[0].iStart ){ | |
| 593 | + int x = strlen(p->a[0].zTag); | |
| 594 | + blob_append(pCol, p->a[0].zTag, x); | |
| 583 | 595 | needEndSpan = 1; |
| 584 | - if( p->iStart2 ){ | |
| 585 | - p->iStart = p->iStart2; | |
| 586 | - p->zStart = p->zStart2; | |
| 587 | - p->iStart2 = 0; | |
| 588 | - } | |
| 589 | - }else if( i==p->iEnd ){ | |
| 596 | + }else if( i==p->a[0].iEnd ){ | |
| 590 | 597 | blob_append(pCol, "</span>", 7); |
| 591 | 598 | needEndSpan = 0; |
| 592 | - if( p->iEnd2 ){ | |
| 593 | - p->iEnd = p->iEnd2; | |
| 594 | - p->iEnd2 = 0; | |
| 599 | + if( p->n>1 ){ | |
| 600 | + p->n--; | |
| 601 | + memmove(p->a, p->a+1, sizeof(p->a[0])*p->n); | |
| 602 | + }else{ | |
| 603 | + p->a[0].iStart = -1; | |
| 604 | + p->a[0].iEnd = -1; | |
| 595 | 605 | } |
| 596 | 606 | } |
| 597 | 607 | } |
| 598 | 608 | if( c=='\t' && !p->escHtml ){ |
| 599 | 609 | blob_append(pCol, " ", 1); |
| @@ -739,19 +749,19 @@ | ||
| 739 | 749 | } |
| 740 | 750 | return rc; |
| 741 | 751 | } |
| 742 | 752 | |
| 743 | 753 | /* |
| 744 | -** Try to shift iStart as far as possible to the left. | |
| 754 | +** Try to shift a[0].iStart as far as possible to the left. | |
| 745 | 755 | */ |
| 746 | 756 | static void sbsShiftLeft(SbsLine *p, const char *z){ |
| 747 | 757 | int i, j; |
| 748 | - while( (i=p->iStart)>0 && z[i-1]==z[i] ){ | |
| 749 | - for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){} | |
| 750 | - if( j<p->iEnd ) break; | |
| 751 | - p->iStart--; | |
| 752 | - p->iEnd--; | |
| 758 | + while( (i=p->a[0].iStart)>0 && z[i-1]==z[i] ){ | |
| 759 | + for(j=i+1; j<p->a[0].iEnd && z[j-1]==z[j]; j++){} | |
| 760 | + if( j<p->a[0].iEnd ) break; | |
| 761 | + p->a[0].iStart--; | |
| 762 | + p->a[0].iEnd--; | |
| 753 | 763 | } |
| 754 | 764 | } |
| 755 | 765 | |
| 756 | 766 | /* |
| 757 | 767 | ** Simplify iStart and iStart2: |
| @@ -760,28 +770,26 @@ | ||
| 760 | 770 | ** * Make sure any null-changes are in canonoical form. |
| 761 | 771 | ** * Make sure all changes are at character boundaries for |
| 762 | 772 | ** multi-byte characters. |
| 763 | 773 | */ |
| 764 | 774 | static void sbsSimplifyLine(SbsLine *p, const char *z){ |
| 765 | - if( p->iStart2==p->iEnd2 ){ | |
| 766 | - p->iStart2 = p->iEnd2 = 0; | |
| 767 | - }else if( p->iStart2 ){ | |
| 768 | - while( p->iStart2>0 && (z[p->iStart2]&0xc0)==0x80 ) p->iStart2--; | |
| 769 | - while( (z[p->iEnd2]&0xc0)==0x80 ) p->iEnd2++; | |
| 770 | - } | |
| 771 | - if( p->iStart==p->iEnd ){ | |
| 772 | - p->iStart = p->iStart2; | |
| 773 | - p->iEnd = p->iEnd2; | |
| 774 | - p->zStart = p->zStart2; | |
| 775 | - p->iStart2 = 0; | |
| 776 | - p->iEnd2 = 0; | |
| 777 | - } | |
| 778 | - if( p->iStart==p->iEnd ){ | |
| 779 | - p->iStart = p->iEnd = -1; | |
| 780 | - }else if( p->iStart>0 ){ | |
| 781 | - while( p->iStart>0 && (z[p->iStart]&0xc0)==0x80 ) p->iStart--; | |
| 782 | - while( (z[p->iEnd]&0xc0)==0x80 ) p->iEnd++; | |
| 775 | + int i, j; | |
| 776 | + | |
| 777 | + /* Remove zero-length spans */ | |
| 778 | + for(i=j=0; i<p->n; i++){ | |
| 779 | + if( p->a[i].iStart<p->a[i].iEnd ){ | |
| 780 | + if( j<i ) memcpy(p->a+j, p->a+i, sizeof(p->a[0])); | |
| 781 | + j++; | |
| 782 | + } | |
| 783 | + } | |
| 784 | + p->n = j; | |
| 785 | + | |
| 786 | + /* Align all spans to a character boundary */ | |
| 787 | + for(i=0; i<p->n; i++){ | |
| 788 | + struct SbsMark *a = p->a+i; | |
| 789 | + while( a->iStart>0 && (z[a->iStart]&0xc0)==0x80 ) a->iStart--; | |
| 790 | + while( (z[a->iEnd]&0xc0)==0x80 ) a->iEnd++; | |
| 783 | 791 | } |
| 784 | 792 | } |
| 785 | 793 | |
| 786 | 794 | /* |
| 787 | 795 | ** Write out lines that have been edited. Adjust the highlight to cover |
| @@ -802,13 +810,10 @@ | ||
| 802 | 810 | const char *zLeft; /* Text of the left line */ |
| 803 | 811 | const char *zRight; /* Text of the right line */ |
| 804 | 812 | int nLeftDiff; /* nLeft - nPrefix - nSuffix */ |
| 805 | 813 | int nRightDiff; /* nRight - nPrefix - nSuffix */ |
| 806 | 814 | int aLCS[4]; /* Bounds of common middle segment */ |
| 807 | - static const char zClassRm[] = "<span class=\"diffrm\">"; | |
| 808 | - static const char zClassAdd[] = "<span class=\"diffadd\">"; | |
| 809 | - static const char zClassChng[] = "<span class=\"diffchng\">"; | |
| 810 | 815 | |
| 811 | 816 | nLeft = pLeft->n; |
| 812 | 817 | zLeft = pLeft->z; |
| 813 | 818 | nRight = pRight->n; |
| 814 | 819 | zRight = pRight->z; |
| @@ -867,38 +872,40 @@ | ||
| 867 | 872 | } |
| 868 | 873 | |
| 869 | 874 | /* A single chunk of text inserted on the right */ |
| 870 | 875 | if( nPrefix+nSuffix==nLeft ){ |
| 871 | 876 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 872 | - p->iStart2 = p->iEnd2 = 0; | |
| 873 | - p->iStart = p->iEnd = -1; | |
| 877 | + p->a[0].iStart = p->a[0].iEnd = -1; | |
| 878 | + p->n = 0; | |
| 874 | 879 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 875 | 880 | if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){ |
| 876 | 881 | sbsWriteMarker(p, " ", ""); |
| 877 | 882 | }else{ |
| 878 | 883 | sbsWriteMarker(p, " | ", "|"); |
| 879 | 884 | } |
| 880 | 885 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 881 | - p->iStart = nPrefix; | |
| 882 | - p->iEnd = nRight - nSuffix; | |
| 883 | - p->zStart = zClassAdd; | |
| 886 | + p->a[0].iStart = nPrefix; | |
| 887 | + p->a[0].iEnd = nRight - nSuffix; | |
| 888 | + p->n = 1; | |
| 889 | + p->a[0].zTag = zClassAdd; | |
| 884 | 890 | sbsWriteText(p, pRight, SBS_TXTB); |
| 885 | 891 | return; |
| 886 | 892 | } |
| 887 | 893 | |
| 888 | 894 | /* A single chunk of text deleted from the left */ |
| 889 | 895 | if( nPrefix+nSuffix==nRight ){ |
| 890 | 896 | /* Text deleted from the left */ |
| 891 | 897 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 892 | - p->iStart2 = p->iEnd2 = 0; | |
| 893 | - p->iStart = nPrefix; | |
| 894 | - p->iEnd = nLeft - nSuffix; | |
| 895 | - p->zStart = zClassRm; | |
| 898 | + p->a[0].iStart = nPrefix; | |
| 899 | + p->a[0].iEnd = nLeft - nSuffix; | |
| 900 | + p->a[0].zTag = zClassRm; | |
| 901 | + p->n = 1; | |
| 896 | 902 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 897 | 903 | sbsWriteMarker(p, " | ", "|"); |
| 898 | 904 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 899 | - p->iStart = p->iEnd = -1; | |
| 905 | + p->a[0].iStart = p->a[0].iEnd = -1; | |
| 906 | + p->n = 0; | |
| 900 | 907 | sbsWriteText(p, pRight, SBS_TXTB); |
| 901 | 908 | return; |
| 902 | 909 | } |
| 903 | 910 | |
| 904 | 911 | /* At this point we know that there is a chunk of text that has |
| @@ -911,51 +918,53 @@ | ||
| 911 | 918 | && nLeftDiff >= 6 |
| 912 | 919 | && nRightDiff >= 6 |
| 913 | 920 | && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS) |
| 914 | 921 | ){ |
| 915 | 922 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 916 | - p->iStart = nPrefix; | |
| 917 | - p->iEnd = nPrefix + aLCS[0]; | |
| 923 | + p->a[0].iStart = nPrefix; | |
| 924 | + p->a[0].iEnd = nPrefix + aLCS[0]; | |
| 918 | 925 | if( aLCS[2]==0 ){ |
| 919 | 926 | sbsShiftLeft(p, pLeft->z); |
| 920 | - p->zStart = zClassRm; | |
| 927 | + p->a[0].zTag = zClassRm; | |
| 921 | 928 | }else{ |
| 922 | - p->zStart = zClassChng; | |
| 929 | + p->a[0].zTag = zClassChng; | |
| 923 | 930 | } |
| 924 | - p->iStart2 = nPrefix + aLCS[1]; | |
| 925 | - p->iEnd2 = nLeft - nSuffix; | |
| 926 | - p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng; | |
| 931 | + p->a[1].iStart = nPrefix + aLCS[1]; | |
| 932 | + p->a[1].iEnd = nLeft - nSuffix; | |
| 933 | + p->a[1].zTag = aLCS[3]==nRightDiff ? zClassRm : zClassChng; | |
| 934 | + p->n = 2; | |
| 927 | 935 | sbsSimplifyLine(p, zLeft); |
| 928 | 936 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 929 | 937 | sbsWriteMarker(p, " | ", "|"); |
| 930 | 938 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 931 | - p->iStart = nPrefix; | |
| 932 | - p->iEnd = nPrefix + aLCS[2]; | |
| 939 | + p->a[0].iStart = nPrefix; | |
| 940 | + p->a[0].iEnd = nPrefix + aLCS[2]; | |
| 933 | 941 | if( aLCS[0]==0 ){ |
| 934 | 942 | sbsShiftLeft(p, pRight->z); |
| 935 | - p->zStart = zClassAdd; | |
| 943 | + p->a[0].zTag = zClassAdd; | |
| 936 | 944 | }else{ |
| 937 | - p->zStart = zClassChng; | |
| 945 | + p->a[0].zTag = zClassChng; | |
| 938 | 946 | } |
| 939 | - p->iStart2 = nPrefix + aLCS[3]; | |
| 940 | - p->iEnd2 = nRight - nSuffix; | |
| 941 | - p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng; | |
| 947 | + p->a[1].iStart = nPrefix + aLCS[3]; | |
| 948 | + p->a[1].iEnd = nRight - nSuffix; | |
| 949 | + p->a[1].zTag = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng; | |
| 950 | + p->n = 2; | |
| 942 | 951 | sbsSimplifyLine(p, zRight); |
| 943 | 952 | sbsWriteText(p, pRight, SBS_TXTB); |
| 944 | 953 | return; |
| 945 | 954 | } |
| 946 | 955 | |
| 947 | 956 | /* If all else fails, show a single big change between left and right */ |
| 948 | 957 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 949 | - p->iStart2 = p->iEnd2 = 0; | |
| 950 | - p->iStart = nPrefix; | |
| 951 | - p->iEnd = nLeft - nSuffix; | |
| 952 | - p->zStart = zClassChng; | |
| 958 | + p->a[0].iStart = nPrefix; | |
| 959 | + p->a[0].iEnd = nLeft - nSuffix; | |
| 960 | + p->a[0].zTag = zClassChng; | |
| 961 | + p->n = 1; | |
| 953 | 962 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 954 | 963 | sbsWriteMarker(p, " | ", "|"); |
| 955 | 964 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 956 | - p->iEnd = nRight - nSuffix; | |
| 965 | + p->a[0].iEnd = nRight - nSuffix; | |
| 957 | 966 | sbsWriteText(p, pRight, SBS_TXTB); |
| 958 | 967 | } |
| 959 | 968 | |
| 960 | 969 | /* |
| 961 | 970 | ** Minimum of two values |
| @@ -1224,13 +1233,13 @@ | ||
| 1224 | 1233 | for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1225 | 1234 | s.apCols[i] = pOut; |
| 1226 | 1235 | } |
| 1227 | 1236 | } |
| 1228 | 1237 | s.pRe = pRe; |
| 1229 | - s.iStart = -1; | |
| 1230 | - s.iStart2 = 0; | |
| 1231 | - s.iEnd = -1; | |
| 1238 | + s.a[0].iStart = -1; | |
| 1239 | + s.a[1].iStart = 0; | |
| 1240 | + s.a[0].iEnd = -1; | |
| 1232 | 1241 | A = p->aFrom; |
| 1233 | 1242 | B = p->aTo; |
| 1234 | 1243 | R = p->aEdit; |
| 1235 | 1244 | mxr = p->nEdit; |
| 1236 | 1245 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -1316,11 +1325,12 @@ | ||
| 1316 | 1325 | a += skip; |
| 1317 | 1326 | b += skip; |
| 1318 | 1327 | m = R[r] - skip; |
| 1319 | 1328 | for(j=0; j<m; j++){ |
| 1320 | 1329 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1321 | - s.iStart = s.iEnd = -1; | |
| 1330 | + s.a[0].iStart = s.a[0].iEnd = -1; | |
| 1331 | + s.n = 1; | |
| 1322 | 1332 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1323 | 1333 | sbsWriteMarker(&s, " ", ""); |
| 1324 | 1334 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1325 | 1335 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1326 | 1336 | } |
| @@ -1346,13 +1356,14 @@ | ||
| 1346 | 1356 | alignment = sbsAlignment(&A[a], ma, &B[b], mb, diffFlags); |
| 1347 | 1357 | for(j=0; ma+mb>0; j++){ |
| 1348 | 1358 | if( alignment[j]==1 ){ |
| 1349 | 1359 | /* Delete one line from the left */ |
| 1350 | 1360 | sbsWriteLineno(&s, a, SBS_LNA); |
| 1351 | - s.iStart = 0; | |
| 1352 | - s.zStart = "<span class=\"diffrm\">"; | |
| 1353 | - s.iEnd = LENGTH(&A[a]); | |
| 1361 | + s.a[0].iStart = 0; | |
| 1362 | + s.a[0].zTag = zClassRm; | |
| 1363 | + s.a[0].iEnd = LENGTH(&A[a]); | |
| 1364 | + s.n = 1; | |
| 1354 | 1365 | sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1355 | 1366 | sbsWriteMarker(&s, " <", "<"); |
| 1356 | 1367 | sbsWriteNewlines(&s); |
| 1357 | 1368 | assert( ma>0 ); |
| 1358 | 1369 | ma--; |
| @@ -1370,29 +1381,32 @@ | ||
| 1370 | 1381 | if( !s.escHtml ){ |
| 1371 | 1382 | sbsWriteSpace(&s, s.width + 7, SBS_TXTA); |
| 1372 | 1383 | } |
| 1373 | 1384 | sbsWriteMarker(&s, " > ", ">"); |
| 1374 | 1385 | sbsWriteLineno(&s, b, SBS_LNB); |
| 1375 | - s.iStart = 0; | |
| 1376 | - s.zStart = "<span class=\"diffadd\">"; | |
| 1377 | - s.iEnd = LENGTH(&B[b]); | |
| 1386 | + s.a[0].iStart = 0; | |
| 1387 | + s.a[0].zTag = zClassAdd; | |
| 1388 | + s.a[0].iEnd = LENGTH(&B[b]); | |
| 1389 | + s.n = 1; | |
| 1378 | 1390 | sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1379 | 1391 | assert( mb>0 ); |
| 1380 | 1392 | mb--; |
| 1381 | 1393 | b++; |
| 1382 | 1394 | }else{ |
| 1383 | 1395 | /* Delete from the left and insert on the right */ |
| 1384 | 1396 | sbsWriteLineno(&s, a, SBS_LNA); |
| 1385 | - s.iStart = 0; | |
| 1386 | - s.zStart = "<span class=\"diffrm\">"; | |
| 1387 | - s.iEnd = LENGTH(&A[a]); | |
| 1397 | + s.a[0].iStart = 0; | |
| 1398 | + s.a[0].zTag = zClassRm; | |
| 1399 | + s.a[0].iEnd = LENGTH(&A[a]); | |
| 1400 | + s.n = 1; | |
| 1388 | 1401 | sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1389 | 1402 | sbsWriteMarker(&s, " | ", "|"); |
| 1390 | 1403 | sbsWriteLineno(&s, b, SBS_LNB); |
| 1391 | - s.iStart = 0; | |
| 1392 | - s.zStart = "<span class=\"diffadd\">"; | |
| 1393 | - s.iEnd = LENGTH(&B[b]); | |
| 1404 | + s.a[0].iStart = 0; | |
| 1405 | + s.a[0].zTag = zClassAdd; | |
| 1406 | + s.a[0].iEnd = LENGTH(&B[b]); | |
| 1407 | + s.n = 1; | |
| 1394 | 1408 | sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1395 | 1409 | ma--; |
| 1396 | 1410 | mb--; |
| 1397 | 1411 | a++; |
| 1398 | 1412 | b++; |
| @@ -1401,11 +1415,12 @@ | ||
| 1401 | 1415 | fossil_free(alignment); |
| 1402 | 1416 | if( i<nr-1 ){ |
| 1403 | 1417 | m = R[r+i*3+3]; |
| 1404 | 1418 | for(j=0; j<m; j++){ |
| 1405 | 1419 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1406 | - s.iStart = s.iEnd = -1; | |
| 1420 | + s.a[0].iStart = s.a[0].iEnd = -1; | |
| 1421 | + s.n = 0; | |
| 1407 | 1422 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1408 | 1423 | sbsWriteMarker(&s, " ", ""); |
| 1409 | 1424 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1410 | 1425 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1411 | 1426 | } |
| @@ -1418,11 +1433,12 @@ | ||
| 1418 | 1433 | assert( nr==i ); |
| 1419 | 1434 | m = R[r+nr*3]; |
| 1420 | 1435 | if( m>nContext ) m = nContext; |
| 1421 | 1436 | for(j=0; j<m; j++){ |
| 1422 | 1437 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1423 | - s.iStart = s.iEnd = -1; | |
| 1438 | + s.a[0].iStart = s.a[0].iEnd = -1; | |
| 1439 | + s.n = 0; | |
| 1424 | 1440 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1425 | 1441 | sbsWriteMarker(&s, " ", ""); |
| 1426 | 1442 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1427 | 1443 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1428 | 1444 | } |
| 1429 | 1445 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -120,10 +120,16 @@ | |
| 120 | DLine *aTo; /* File on right side of the diff */ |
| 121 | int nTo; /* Number of lines in aTo[] */ |
| 122 | int (*xDiffer)(const DLine*,const DLine*); /* comparison function */ |
| 123 | }; |
| 124 | |
| 125 | /* |
| 126 | ** Count the number of lines in the input string. Include the last line |
| 127 | ** in the count even if it lacks the \n terminator. If an empty string |
| 128 | ** is specified, the number of lines is zero. For the purposes of this |
| 129 | ** function, a string is considered empty if it contains no characters |
| @@ -301,13 +307,13 @@ | |
| 301 | blob_append(pOut, &cPrefix, 1); |
| 302 | if( html ){ |
| 303 | if( pRe && re_dline_match(pRe, pLine, 1)==0 ){ |
| 304 | cPrefix = ' '; |
| 305 | }else if( cPrefix=='+' ){ |
| 306 | blob_append(pOut, "<span class=\"diffadd\">", -1); |
| 307 | }else if( cPrefix=='-' ){ |
| 308 | blob_append(pOut, "<span class=\"diffrm\">", -1); |
| 309 | } |
| 310 | htmlize_to_blob(pOut, pLine->z, pLine->n); |
| 311 | if( cPrefix!=' ' ){ |
| 312 | blob_append(pOut, "</span>", -1); |
| 313 | } |
| @@ -506,26 +512,32 @@ | |
| 506 | } |
| 507 | } |
| 508 | if( html ) blob_append(pOut, "</pre>\n", -1); |
| 509 | } |
| 510 | |
| 511 | /* |
| 512 | ** Status of a single output line |
| 513 | */ |
| 514 | typedef struct SbsLine SbsLine; |
| 515 | struct SbsLine { |
| 516 | Blob *apCols[5]; /* Array of pointers to output columns */ |
| 517 | int width; /* Maximum width of a column in the output */ |
| 518 | unsigned char escHtml; /* True to escape html characters */ |
| 519 | int iStart; /* Write zStart prior to character iStart */ |
| 520 | const char *zStart; /* A <span> tag */ |
| 521 | int iEnd; /* Write </span> prior to character iEnd */ |
| 522 | int iStart2; /* Write zStart2 prior to character iStart2 */ |
| 523 | const char *zStart2; /* A <span> tag */ |
| 524 | int iEnd2; /* Write </span> prior to character iEnd2 */ |
| 525 | ReCompiled *pRe; /* Only colorize matching lines, if not NULL */ |
| 526 | }; |
| 527 | |
| 528 | /* |
| 529 | ** Column indices for SbsLine.apCols[] |
| 530 | */ |
| 531 | #define SBS_LNA 0 /* Left line number */ |
| @@ -575,25 +587,23 @@ | |
| 575 | colorize = 0; |
| 576 | } |
| 577 | for(i=k=0; (p->escHtml || k<w) && i<n; i++, k++){ |
| 578 | char c = zIn[i]; |
| 579 | if( colorize ){ |
| 580 | if( i==p->iStart ){ |
| 581 | int x = strlen(p->zStart); |
| 582 | blob_append(pCol, p->zStart, x); |
| 583 | needEndSpan = 1; |
| 584 | if( p->iStart2 ){ |
| 585 | p->iStart = p->iStart2; |
| 586 | p->zStart = p->zStart2; |
| 587 | p->iStart2 = 0; |
| 588 | } |
| 589 | }else if( i==p->iEnd ){ |
| 590 | blob_append(pCol, "</span>", 7); |
| 591 | needEndSpan = 0; |
| 592 | if( p->iEnd2 ){ |
| 593 | p->iEnd = p->iEnd2; |
| 594 | p->iEnd2 = 0; |
| 595 | } |
| 596 | } |
| 597 | } |
| 598 | if( c=='\t' && !p->escHtml ){ |
| 599 | blob_append(pCol, " ", 1); |
| @@ -739,19 +749,19 @@ | |
| 739 | } |
| 740 | return rc; |
| 741 | } |
| 742 | |
| 743 | /* |
| 744 | ** Try to shift iStart as far as possible to the left. |
| 745 | */ |
| 746 | static void sbsShiftLeft(SbsLine *p, const char *z){ |
| 747 | int i, j; |
| 748 | while( (i=p->iStart)>0 && z[i-1]==z[i] ){ |
| 749 | for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){} |
| 750 | if( j<p->iEnd ) break; |
| 751 | p->iStart--; |
| 752 | p->iEnd--; |
| 753 | } |
| 754 | } |
| 755 | |
| 756 | /* |
| 757 | ** Simplify iStart and iStart2: |
| @@ -760,28 +770,26 @@ | |
| 760 | ** * Make sure any null-changes are in canonoical form. |
| 761 | ** * Make sure all changes are at character boundaries for |
| 762 | ** multi-byte characters. |
| 763 | */ |
| 764 | static void sbsSimplifyLine(SbsLine *p, const char *z){ |
| 765 | if( p->iStart2==p->iEnd2 ){ |
| 766 | p->iStart2 = p->iEnd2 = 0; |
| 767 | }else if( p->iStart2 ){ |
| 768 | while( p->iStart2>0 && (z[p->iStart2]&0xc0)==0x80 ) p->iStart2--; |
| 769 | while( (z[p->iEnd2]&0xc0)==0x80 ) p->iEnd2++; |
| 770 | } |
| 771 | if( p->iStart==p->iEnd ){ |
| 772 | p->iStart = p->iStart2; |
| 773 | p->iEnd = p->iEnd2; |
| 774 | p->zStart = p->zStart2; |
| 775 | p->iStart2 = 0; |
| 776 | p->iEnd2 = 0; |
| 777 | } |
| 778 | if( p->iStart==p->iEnd ){ |
| 779 | p->iStart = p->iEnd = -1; |
| 780 | }else if( p->iStart>0 ){ |
| 781 | while( p->iStart>0 && (z[p->iStart]&0xc0)==0x80 ) p->iStart--; |
| 782 | while( (z[p->iEnd]&0xc0)==0x80 ) p->iEnd++; |
| 783 | } |
| 784 | } |
| 785 | |
| 786 | /* |
| 787 | ** Write out lines that have been edited. Adjust the highlight to cover |
| @@ -802,13 +810,10 @@ | |
| 802 | const char *zLeft; /* Text of the left line */ |
| 803 | const char *zRight; /* Text of the right line */ |
| 804 | int nLeftDiff; /* nLeft - nPrefix - nSuffix */ |
| 805 | int nRightDiff; /* nRight - nPrefix - nSuffix */ |
| 806 | int aLCS[4]; /* Bounds of common middle segment */ |
| 807 | static const char zClassRm[] = "<span class=\"diffrm\">"; |
| 808 | static const char zClassAdd[] = "<span class=\"diffadd\">"; |
| 809 | static const char zClassChng[] = "<span class=\"diffchng\">"; |
| 810 | |
| 811 | nLeft = pLeft->n; |
| 812 | zLeft = pLeft->z; |
| 813 | nRight = pRight->n; |
| 814 | zRight = pRight->z; |
| @@ -867,38 +872,40 @@ | |
| 867 | } |
| 868 | |
| 869 | /* A single chunk of text inserted on the right */ |
| 870 | if( nPrefix+nSuffix==nLeft ){ |
| 871 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 872 | p->iStart2 = p->iEnd2 = 0; |
| 873 | p->iStart = p->iEnd = -1; |
| 874 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 875 | if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){ |
| 876 | sbsWriteMarker(p, " ", ""); |
| 877 | }else{ |
| 878 | sbsWriteMarker(p, " | ", "|"); |
| 879 | } |
| 880 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 881 | p->iStart = nPrefix; |
| 882 | p->iEnd = nRight - nSuffix; |
| 883 | p->zStart = zClassAdd; |
| 884 | sbsWriteText(p, pRight, SBS_TXTB); |
| 885 | return; |
| 886 | } |
| 887 | |
| 888 | /* A single chunk of text deleted from the left */ |
| 889 | if( nPrefix+nSuffix==nRight ){ |
| 890 | /* Text deleted from the left */ |
| 891 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 892 | p->iStart2 = p->iEnd2 = 0; |
| 893 | p->iStart = nPrefix; |
| 894 | p->iEnd = nLeft - nSuffix; |
| 895 | p->zStart = zClassRm; |
| 896 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 897 | sbsWriteMarker(p, " | ", "|"); |
| 898 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 899 | p->iStart = p->iEnd = -1; |
| 900 | sbsWriteText(p, pRight, SBS_TXTB); |
| 901 | return; |
| 902 | } |
| 903 | |
| 904 | /* At this point we know that there is a chunk of text that has |
| @@ -911,51 +918,53 @@ | |
| 911 | && nLeftDiff >= 6 |
| 912 | && nRightDiff >= 6 |
| 913 | && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS) |
| 914 | ){ |
| 915 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 916 | p->iStart = nPrefix; |
| 917 | p->iEnd = nPrefix + aLCS[0]; |
| 918 | if( aLCS[2]==0 ){ |
| 919 | sbsShiftLeft(p, pLeft->z); |
| 920 | p->zStart = zClassRm; |
| 921 | }else{ |
| 922 | p->zStart = zClassChng; |
| 923 | } |
| 924 | p->iStart2 = nPrefix + aLCS[1]; |
| 925 | p->iEnd2 = nLeft - nSuffix; |
| 926 | p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng; |
| 927 | sbsSimplifyLine(p, zLeft); |
| 928 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 929 | sbsWriteMarker(p, " | ", "|"); |
| 930 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 931 | p->iStart = nPrefix; |
| 932 | p->iEnd = nPrefix + aLCS[2]; |
| 933 | if( aLCS[0]==0 ){ |
| 934 | sbsShiftLeft(p, pRight->z); |
| 935 | p->zStart = zClassAdd; |
| 936 | }else{ |
| 937 | p->zStart = zClassChng; |
| 938 | } |
| 939 | p->iStart2 = nPrefix + aLCS[3]; |
| 940 | p->iEnd2 = nRight - nSuffix; |
| 941 | p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng; |
| 942 | sbsSimplifyLine(p, zRight); |
| 943 | sbsWriteText(p, pRight, SBS_TXTB); |
| 944 | return; |
| 945 | } |
| 946 | |
| 947 | /* If all else fails, show a single big change between left and right */ |
| 948 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 949 | p->iStart2 = p->iEnd2 = 0; |
| 950 | p->iStart = nPrefix; |
| 951 | p->iEnd = nLeft - nSuffix; |
| 952 | p->zStart = zClassChng; |
| 953 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 954 | sbsWriteMarker(p, " | ", "|"); |
| 955 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 956 | p->iEnd = nRight - nSuffix; |
| 957 | sbsWriteText(p, pRight, SBS_TXTB); |
| 958 | } |
| 959 | |
| 960 | /* |
| 961 | ** Minimum of two values |
| @@ -1224,13 +1233,13 @@ | |
| 1224 | for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1225 | s.apCols[i] = pOut; |
| 1226 | } |
| 1227 | } |
| 1228 | s.pRe = pRe; |
| 1229 | s.iStart = -1; |
| 1230 | s.iStart2 = 0; |
| 1231 | s.iEnd = -1; |
| 1232 | A = p->aFrom; |
| 1233 | B = p->aTo; |
| 1234 | R = p->aEdit; |
| 1235 | mxr = p->nEdit; |
| 1236 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -1316,11 +1325,12 @@ | |
| 1316 | a += skip; |
| 1317 | b += skip; |
| 1318 | m = R[r] - skip; |
| 1319 | for(j=0; j<m; j++){ |
| 1320 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1321 | s.iStart = s.iEnd = -1; |
| 1322 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1323 | sbsWriteMarker(&s, " ", ""); |
| 1324 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1325 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1326 | } |
| @@ -1346,13 +1356,14 @@ | |
| 1346 | alignment = sbsAlignment(&A[a], ma, &B[b], mb, diffFlags); |
| 1347 | for(j=0; ma+mb>0; j++){ |
| 1348 | if( alignment[j]==1 ){ |
| 1349 | /* Delete one line from the left */ |
| 1350 | sbsWriteLineno(&s, a, SBS_LNA); |
| 1351 | s.iStart = 0; |
| 1352 | s.zStart = "<span class=\"diffrm\">"; |
| 1353 | s.iEnd = LENGTH(&A[a]); |
| 1354 | sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1355 | sbsWriteMarker(&s, " <", "<"); |
| 1356 | sbsWriteNewlines(&s); |
| 1357 | assert( ma>0 ); |
| 1358 | ma--; |
| @@ -1370,29 +1381,32 @@ | |
| 1370 | if( !s.escHtml ){ |
| 1371 | sbsWriteSpace(&s, s.width + 7, SBS_TXTA); |
| 1372 | } |
| 1373 | sbsWriteMarker(&s, " > ", ">"); |
| 1374 | sbsWriteLineno(&s, b, SBS_LNB); |
| 1375 | s.iStart = 0; |
| 1376 | s.zStart = "<span class=\"diffadd\">"; |
| 1377 | s.iEnd = LENGTH(&B[b]); |
| 1378 | sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1379 | assert( mb>0 ); |
| 1380 | mb--; |
| 1381 | b++; |
| 1382 | }else{ |
| 1383 | /* Delete from the left and insert on the right */ |
| 1384 | sbsWriteLineno(&s, a, SBS_LNA); |
| 1385 | s.iStart = 0; |
| 1386 | s.zStart = "<span class=\"diffrm\">"; |
| 1387 | s.iEnd = LENGTH(&A[a]); |
| 1388 | sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1389 | sbsWriteMarker(&s, " | ", "|"); |
| 1390 | sbsWriteLineno(&s, b, SBS_LNB); |
| 1391 | s.iStart = 0; |
| 1392 | s.zStart = "<span class=\"diffadd\">"; |
| 1393 | s.iEnd = LENGTH(&B[b]); |
| 1394 | sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1395 | ma--; |
| 1396 | mb--; |
| 1397 | a++; |
| 1398 | b++; |
| @@ -1401,11 +1415,12 @@ | |
| 1401 | fossil_free(alignment); |
| 1402 | if( i<nr-1 ){ |
| 1403 | m = R[r+i*3+3]; |
| 1404 | for(j=0; j<m; j++){ |
| 1405 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1406 | s.iStart = s.iEnd = -1; |
| 1407 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1408 | sbsWriteMarker(&s, " ", ""); |
| 1409 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1410 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1411 | } |
| @@ -1418,11 +1433,12 @@ | |
| 1418 | assert( nr==i ); |
| 1419 | m = R[r+nr*3]; |
| 1420 | if( m>nContext ) m = nContext; |
| 1421 | for(j=0; j<m; j++){ |
| 1422 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1423 | s.iStart = s.iEnd = -1; |
| 1424 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1425 | sbsWriteMarker(&s, " ", ""); |
| 1426 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1427 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1428 | } |
| 1429 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -120,10 +120,16 @@ | |
| 120 | DLine *aTo; /* File on right side of the diff */ |
| 121 | int nTo; /* Number of lines in aTo[] */ |
| 122 | int (*xDiffer)(const DLine*,const DLine*); /* comparison function */ |
| 123 | }; |
| 124 | |
| 125 | /* <span> text for change coloration |
| 126 | */ |
| 127 | static const char zClassRm[] = "<span class=\"diffrm\">"; |
| 128 | static const char zClassAdd[] = "<span class=\"diffadd\">"; |
| 129 | static const char zClassChng[] = "<span class=\"diffchng\">"; |
| 130 | |
| 131 | /* |
| 132 | ** Count the number of lines in the input string. Include the last line |
| 133 | ** in the count even if it lacks the \n terminator. If an empty string |
| 134 | ** is specified, the number of lines is zero. For the purposes of this |
| 135 | ** function, a string is considered empty if it contains no characters |
| @@ -301,13 +307,13 @@ | |
| 307 | blob_append(pOut, &cPrefix, 1); |
| 308 | if( html ){ |
| 309 | if( pRe && re_dline_match(pRe, pLine, 1)==0 ){ |
| 310 | cPrefix = ' '; |
| 311 | }else if( cPrefix=='+' ){ |
| 312 | blob_append(pOut, zClassAdd, -1); |
| 313 | }else if( cPrefix=='-' ){ |
| 314 | blob_append(pOut, zClassRm, -1); |
| 315 | } |
| 316 | htmlize_to_blob(pOut, pLine->z, pLine->n); |
| 317 | if( cPrefix!=' ' ){ |
| 318 | blob_append(pOut, "</span>", -1); |
| 319 | } |
| @@ -506,26 +512,32 @@ | |
| 512 | } |
| 513 | } |
| 514 | if( html ) blob_append(pOut, "</pre>\n", -1); |
| 515 | } |
| 516 | |
| 517 | /* |
| 518 | ** Maximum number of change regions per line |
| 519 | */ |
| 520 | #define SBS_MXN 4 |
| 521 | |
| 522 | /* |
| 523 | ** Status of a single output line |
| 524 | */ |
| 525 | typedef struct SbsLine SbsLine; |
| 526 | struct SbsLine { |
| 527 | Blob *apCols[5]; /* Array of pointers to output columns */ |
| 528 | int width; /* Maximum width of a column in the output */ |
| 529 | unsigned char escHtml; /* True to escape html characters */ |
| 530 | struct SbsMark { |
| 531 | int iStart; /* Write zTag prior to character iStart */ |
| 532 | const char *zTag; /* A <span> tag for coloration */ |
| 533 | int iEnd; /* Write </span> prior to character iEnd */ |
| 534 | } a[SBS_MXN]; /* Change regions */ |
| 535 | int n; /* Number of change regions used */ |
| 536 | ReCompiled *pRe; /* Only colorize matching lines, if not NULL */ |
| 537 | }; |
| 538 | |
| 539 | |
| 540 | /* |
| 541 | ** Column indices for SbsLine.apCols[] |
| 542 | */ |
| 543 | #define SBS_LNA 0 /* Left line number */ |
| @@ -575,25 +587,23 @@ | |
| 587 | colorize = 0; |
| 588 | } |
| 589 | for(i=k=0; (p->escHtml || k<w) && i<n; i++, k++){ |
| 590 | char c = zIn[i]; |
| 591 | if( colorize ){ |
| 592 | if( i==p->a[0].iStart ){ |
| 593 | int x = strlen(p->a[0].zTag); |
| 594 | blob_append(pCol, p->a[0].zTag, x); |
| 595 | needEndSpan = 1; |
| 596 | }else if( i==p->a[0].iEnd ){ |
| 597 | blob_append(pCol, "</span>", 7); |
| 598 | needEndSpan = 0; |
| 599 | if( p->n>1 ){ |
| 600 | p->n--; |
| 601 | memmove(p->a, p->a+1, sizeof(p->a[0])*p->n); |
| 602 | }else{ |
| 603 | p->a[0].iStart = -1; |
| 604 | p->a[0].iEnd = -1; |
| 605 | } |
| 606 | } |
| 607 | } |
| 608 | if( c=='\t' && !p->escHtml ){ |
| 609 | blob_append(pCol, " ", 1); |
| @@ -739,19 +749,19 @@ | |
| 749 | } |
| 750 | return rc; |
| 751 | } |
| 752 | |
| 753 | /* |
| 754 | ** Try to shift a[0].iStart as far as possible to the left. |
| 755 | */ |
| 756 | static void sbsShiftLeft(SbsLine *p, const char *z){ |
| 757 | int i, j; |
| 758 | while( (i=p->a[0].iStart)>0 && z[i-1]==z[i] ){ |
| 759 | for(j=i+1; j<p->a[0].iEnd && z[j-1]==z[j]; j++){} |
| 760 | if( j<p->a[0].iEnd ) break; |
| 761 | p->a[0].iStart--; |
| 762 | p->a[0].iEnd--; |
| 763 | } |
| 764 | } |
| 765 | |
| 766 | /* |
| 767 | ** Simplify iStart and iStart2: |
| @@ -760,28 +770,26 @@ | |
| 770 | ** * Make sure any null-changes are in canonoical form. |
| 771 | ** * Make sure all changes are at character boundaries for |
| 772 | ** multi-byte characters. |
| 773 | */ |
| 774 | static void sbsSimplifyLine(SbsLine *p, const char *z){ |
| 775 | int i, j; |
| 776 | |
| 777 | /* Remove zero-length spans */ |
| 778 | for(i=j=0; i<p->n; i++){ |
| 779 | if( p->a[i].iStart<p->a[i].iEnd ){ |
| 780 | if( j<i ) memcpy(p->a+j, p->a+i, sizeof(p->a[0])); |
| 781 | j++; |
| 782 | } |
| 783 | } |
| 784 | p->n = j; |
| 785 | |
| 786 | /* Align all spans to a character boundary */ |
| 787 | for(i=0; i<p->n; i++){ |
| 788 | struct SbsMark *a = p->a+i; |
| 789 | while( a->iStart>0 && (z[a->iStart]&0xc0)==0x80 ) a->iStart--; |
| 790 | while( (z[a->iEnd]&0xc0)==0x80 ) a->iEnd++; |
| 791 | } |
| 792 | } |
| 793 | |
| 794 | /* |
| 795 | ** Write out lines that have been edited. Adjust the highlight to cover |
| @@ -802,13 +810,10 @@ | |
| 810 | const char *zLeft; /* Text of the left line */ |
| 811 | const char *zRight; /* Text of the right line */ |
| 812 | int nLeftDiff; /* nLeft - nPrefix - nSuffix */ |
| 813 | int nRightDiff; /* nRight - nPrefix - nSuffix */ |
| 814 | int aLCS[4]; /* Bounds of common middle segment */ |
| 815 | |
| 816 | nLeft = pLeft->n; |
| 817 | zLeft = pLeft->z; |
| 818 | nRight = pRight->n; |
| 819 | zRight = pRight->z; |
| @@ -867,38 +872,40 @@ | |
| 872 | } |
| 873 | |
| 874 | /* A single chunk of text inserted on the right */ |
| 875 | if( nPrefix+nSuffix==nLeft ){ |
| 876 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 877 | p->a[0].iStart = p->a[0].iEnd = -1; |
| 878 | p->n = 0; |
| 879 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 880 | if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){ |
| 881 | sbsWriteMarker(p, " ", ""); |
| 882 | }else{ |
| 883 | sbsWriteMarker(p, " | ", "|"); |
| 884 | } |
| 885 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 886 | p->a[0].iStart = nPrefix; |
| 887 | p->a[0].iEnd = nRight - nSuffix; |
| 888 | p->n = 1; |
| 889 | p->a[0].zTag = zClassAdd; |
| 890 | sbsWriteText(p, pRight, SBS_TXTB); |
| 891 | return; |
| 892 | } |
| 893 | |
| 894 | /* A single chunk of text deleted from the left */ |
| 895 | if( nPrefix+nSuffix==nRight ){ |
| 896 | /* Text deleted from the left */ |
| 897 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 898 | p->a[0].iStart = nPrefix; |
| 899 | p->a[0].iEnd = nLeft - nSuffix; |
| 900 | p->a[0].zTag = zClassRm; |
| 901 | p->n = 1; |
| 902 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 903 | sbsWriteMarker(p, " | ", "|"); |
| 904 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 905 | p->a[0].iStart = p->a[0].iEnd = -1; |
| 906 | p->n = 0; |
| 907 | sbsWriteText(p, pRight, SBS_TXTB); |
| 908 | return; |
| 909 | } |
| 910 | |
| 911 | /* At this point we know that there is a chunk of text that has |
| @@ -911,51 +918,53 @@ | |
| 918 | && nLeftDiff >= 6 |
| 919 | && nRightDiff >= 6 |
| 920 | && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS) |
| 921 | ){ |
| 922 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 923 | p->a[0].iStart = nPrefix; |
| 924 | p->a[0].iEnd = nPrefix + aLCS[0]; |
| 925 | if( aLCS[2]==0 ){ |
| 926 | sbsShiftLeft(p, pLeft->z); |
| 927 | p->a[0].zTag = zClassRm; |
| 928 | }else{ |
| 929 | p->a[0].zTag = zClassChng; |
| 930 | } |
| 931 | p->a[1].iStart = nPrefix + aLCS[1]; |
| 932 | p->a[1].iEnd = nLeft - nSuffix; |
| 933 | p->a[1].zTag = aLCS[3]==nRightDiff ? zClassRm : zClassChng; |
| 934 | p->n = 2; |
| 935 | sbsSimplifyLine(p, zLeft); |
| 936 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 937 | sbsWriteMarker(p, " | ", "|"); |
| 938 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 939 | p->a[0].iStart = nPrefix; |
| 940 | p->a[0].iEnd = nPrefix + aLCS[2]; |
| 941 | if( aLCS[0]==0 ){ |
| 942 | sbsShiftLeft(p, pRight->z); |
| 943 | p->a[0].zTag = zClassAdd; |
| 944 | }else{ |
| 945 | p->a[0].zTag = zClassChng; |
| 946 | } |
| 947 | p->a[1].iStart = nPrefix + aLCS[3]; |
| 948 | p->a[1].iEnd = nRight - nSuffix; |
| 949 | p->a[1].zTag = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng; |
| 950 | p->n = 2; |
| 951 | sbsSimplifyLine(p, zRight); |
| 952 | sbsWriteText(p, pRight, SBS_TXTB); |
| 953 | return; |
| 954 | } |
| 955 | |
| 956 | /* If all else fails, show a single big change between left and right */ |
| 957 | sbsWriteLineno(p, lnLeft, SBS_LNA); |
| 958 | p->a[0].iStart = nPrefix; |
| 959 | p->a[0].iEnd = nLeft - nSuffix; |
| 960 | p->a[0].zTag = zClassChng; |
| 961 | p->n = 1; |
| 962 | sbsWriteText(p, pLeft, SBS_TXTA); |
| 963 | sbsWriteMarker(p, " | ", "|"); |
| 964 | sbsWriteLineno(p, lnRight, SBS_LNB); |
| 965 | p->a[0].iEnd = nRight - nSuffix; |
| 966 | sbsWriteText(p, pRight, SBS_TXTB); |
| 967 | } |
| 968 | |
| 969 | /* |
| 970 | ** Minimum of two values |
| @@ -1224,13 +1233,13 @@ | |
| 1233 | for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1234 | s.apCols[i] = pOut; |
| 1235 | } |
| 1236 | } |
| 1237 | s.pRe = pRe; |
| 1238 | s.a[0].iStart = -1; |
| 1239 | s.a[1].iStart = 0; |
| 1240 | s.a[0].iEnd = -1; |
| 1241 | A = p->aFrom; |
| 1242 | B = p->aTo; |
| 1243 | R = p->aEdit; |
| 1244 | mxr = p->nEdit; |
| 1245 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -1316,11 +1325,12 @@ | |
| 1325 | a += skip; |
| 1326 | b += skip; |
| 1327 | m = R[r] - skip; |
| 1328 | for(j=0; j<m; j++){ |
| 1329 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1330 | s.a[0].iStart = s.a[0].iEnd = -1; |
| 1331 | s.n = 1; |
| 1332 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1333 | sbsWriteMarker(&s, " ", ""); |
| 1334 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1335 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1336 | } |
| @@ -1346,13 +1356,14 @@ | |
| 1356 | alignment = sbsAlignment(&A[a], ma, &B[b], mb, diffFlags); |
| 1357 | for(j=0; ma+mb>0; j++){ |
| 1358 | if( alignment[j]==1 ){ |
| 1359 | /* Delete one line from the left */ |
| 1360 | sbsWriteLineno(&s, a, SBS_LNA); |
| 1361 | s.a[0].iStart = 0; |
| 1362 | s.a[0].zTag = zClassRm; |
| 1363 | s.a[0].iEnd = LENGTH(&A[a]); |
| 1364 | s.n = 1; |
| 1365 | sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1366 | sbsWriteMarker(&s, " <", "<"); |
| 1367 | sbsWriteNewlines(&s); |
| 1368 | assert( ma>0 ); |
| 1369 | ma--; |
| @@ -1370,29 +1381,32 @@ | |
| 1381 | if( !s.escHtml ){ |
| 1382 | sbsWriteSpace(&s, s.width + 7, SBS_TXTA); |
| 1383 | } |
| 1384 | sbsWriteMarker(&s, " > ", ">"); |
| 1385 | sbsWriteLineno(&s, b, SBS_LNB); |
| 1386 | s.a[0].iStart = 0; |
| 1387 | s.a[0].zTag = zClassAdd; |
| 1388 | s.a[0].iEnd = LENGTH(&B[b]); |
| 1389 | s.n = 1; |
| 1390 | sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1391 | assert( mb>0 ); |
| 1392 | mb--; |
| 1393 | b++; |
| 1394 | }else{ |
| 1395 | /* Delete from the left and insert on the right */ |
| 1396 | sbsWriteLineno(&s, a, SBS_LNA); |
| 1397 | s.a[0].iStart = 0; |
| 1398 | s.a[0].zTag = zClassRm; |
| 1399 | s.a[0].iEnd = LENGTH(&A[a]); |
| 1400 | s.n = 1; |
| 1401 | sbsWriteText(&s, &A[a], SBS_TXTA); |
| 1402 | sbsWriteMarker(&s, " | ", "|"); |
| 1403 | sbsWriteLineno(&s, b, SBS_LNB); |
| 1404 | s.a[0].iStart = 0; |
| 1405 | s.a[0].zTag = zClassAdd; |
| 1406 | s.a[0].iEnd = LENGTH(&B[b]); |
| 1407 | s.n = 1; |
| 1408 | sbsWriteText(&s, &B[b], SBS_TXTB); |
| 1409 | ma--; |
| 1410 | mb--; |
| 1411 | a++; |
| 1412 | b++; |
| @@ -1401,11 +1415,12 @@ | |
| 1415 | fossil_free(alignment); |
| 1416 | if( i<nr-1 ){ |
| 1417 | m = R[r+i*3+3]; |
| 1418 | for(j=0; j<m; j++){ |
| 1419 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1420 | s.a[0].iStart = s.a[0].iEnd = -1; |
| 1421 | s.n = 0; |
| 1422 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1423 | sbsWriteMarker(&s, " ", ""); |
| 1424 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1425 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1426 | } |
| @@ -1418,11 +1433,12 @@ | |
| 1433 | assert( nr==i ); |
| 1434 | m = R[r+nr*3]; |
| 1435 | if( m>nContext ) m = nContext; |
| 1436 | for(j=0; j<m; j++){ |
| 1437 | sbsWriteLineno(&s, a+j, SBS_LNA); |
| 1438 | s.a[0].iStart = s.a[0].iEnd = -1; |
| 1439 | s.n = 0; |
| 1440 | sbsWriteText(&s, &A[a+j], SBS_TXTA); |
| 1441 | sbsWriteMarker(&s, " ", ""); |
| 1442 | sbsWriteLineno(&s, b+j, SBS_LNB); |
| 1443 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1444 | } |
| 1445 |