Fossil SCM

Delete a bunch of obsolete side-by-side diff code.

drh 2021-09-02 23:43 diff-color-enhancements
Commit 9f3d89612afbb42bd37587e8e34e6201eb70e48a591057822c492ad8c45a8dab
1 file changed +87 -647
+87 -647
--- src/diff.c
+++ src/diff.c
@@ -461,33 +461,12 @@
461461
appendDiffLine(pOut, ' ', &A[a+j]);
462462
}
463463
}
464464
}
465465
466
-/*
467
-** Limits for the intra-line diffing.
468
-*/
469
-#define SBS_MXN 8 /* Maximum number of change regions per line of text */
470466
#define SBS_CSN 8 /* Maximum number of change spans across a change region */
471467
472
-/*
473
-** Status of a single output line
474
-*/
475
-typedef struct SbsLine SbsLine;
476
-struct SbsLine {
477
- Blob *apCols[5]; /* Array of pointers to output columns */
478
- int width; /* Maximum width of a column in the output */
479
- unsigned char escHtml; /* True to escape html characters */
480
- struct SbsMark {
481
- int iStart; /* Write zTag prior to character iStart */
482
- int iEnd; /* Write </span> prior to character iEnd */
483
- const char *zTag; /* A <span> tag for coloration */
484
- } a[SBS_MXN]; /* Change regions */
485
- int n; /* Number of change regions used */
486
- ReCompiled *pRe; /* Only colorize matching lines, if not NULL */
487
-};
488
-
489468
/*
490469
** A description of zero or more (up to SBS_CSN) areas of commonality
491470
** between two lines of text.
492471
*/
493472
typedef struct ChangeSpan ChangeSpan;
@@ -500,158 +479,10 @@
500479
int iLen2; /* Length of right change span in bytes */
501480
int isMin; /* True if this span is known to have no useful subdivs */
502481
} a[SBS_CSN]; /* Array of change spans, sorted order */
503482
};
504483
505
-/*
506
-** Column indices for SbsLine.apCols[]
507
-*/
508
-#define SBS_LNA 0 /* Left line number */
509
-#define SBS_TXTA 1 /* Left text */
510
-#define SBS_MKR 2 /* Middle separator column */
511
-#define SBS_LNB 3 /* Right line number */
512
-#define SBS_TXTB 4 /* Right text */
513
-
514
-/*
515
-** Append newlines to all columns.
516
-*/
517
-static void sbsWriteNewlines(SbsLine *p){
518
- int i;
519
- for( i=p->escHtml ? SBS_LNA : SBS_TXTB; i<=SBS_TXTB; i++ ){
520
- blob_append(p->apCols[i], "\n", 1);
521
- }
522
-}
523
-
524
-/*
525
-** Append n spaces to the column.
526
-*/
527
-static void sbsWriteSpace(SbsLine *p, int n, int col){
528
- blob_appendf(p->apCols[col], "%*s", n, "");
529
-}
530
-
531
-/*
532
-** Write the text of pLine into column iCol of p.
533
-**
534
-** If outputting HTML, write the full line. Otherwise, only write the
535
-** width characters. Translate tabs into spaces. Add newlines if col
536
-** is SBS_TXTB. Translate HTML characters if escHtml is true. Pad the
537
-** rendering to width bytes if col is SBS_TXTA and escHtml is false.
538
-**
539
-** This comment contains multibyte unicode characters (ü, Æ, ð) in order
540
-** to test the ability of the diff code to handle such characters.
541
-*/
542
-static void sbsWriteText(SbsLine *p, DLine *pLine, int col){
543
- Blob *pCol = p->apCols[col];
544
- int n = pLine->n;
545
- int i; /* Number of input characters consumed */
546
- int k; /* Cursor position */
547
- int needEndSpan = 0;
548
- const char *zIn = pLine->z;
549
- int w = p->width;
550
- int colorize = p->escHtml && p->n>0;
551
- if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
552
- colorize = 0;
553
- }
554
- for(i=k=0; (p->escHtml || k<w) && i<n; i++, k++){
555
- char c = zIn[i];
556
- if( colorize ){
557
- if( i==p->a[0].iStart ){
558
- int x = strlen(p->a[0].zTag);
559
- blob_append(pCol, p->a[0].zTag, x);
560
- needEndSpan = 1;
561
- }else if( i==p->a[0].iEnd ){
562
- blob_append(pCol, "</span>", 7);
563
- needEndSpan = 0;
564
- if( p->n>1 ){
565
- p->n--;
566
- memmove(p->a, p->a+1, sizeof(p->a[0])*p->n);
567
- }else{
568
- colorize = 0;
569
- }
570
- }
571
- }
572
- if( c>'>' ){
573
- blob_append_char(pCol, c);
574
- if( (c&0xc0)==0x80 ) k--;
575
- }else if( c=='\t' && !p->escHtml ){
576
- blob_append(pCol, " ", 1);
577
- while( (k&7)!=7 && (p->escHtml || k<w) ){
578
- blob_append(pCol, " ", 1);
579
- k++;
580
- }
581
- }else if( c=='\r' || c=='\f' ){
582
- blob_append(pCol, " ", 1);
583
- }else if( c=='<' && p->escHtml ){
584
- blob_append(pCol, "&lt;", 4);
585
- }else if( c=='&' && p->escHtml ){
586
- blob_append(pCol, "&amp;", 5);
587
- }else if( c=='>' && p->escHtml ){
588
- blob_append(pCol, "&gt;", 4);
589
- }else if( c=='"' && p->escHtml ){
590
- blob_append(pCol, "&quot;", 6);
591
- }else{
592
- blob_append_char(pCol, c);
593
- if( (c&0xc0)==0x80 ) k--;
594
- }
595
- }
596
- if( needEndSpan ){
597
- blob_append(pCol, "</span>", 7);
598
- }
599
- if( col==SBS_TXTB ){
600
- sbsWriteNewlines(p);
601
- }else if( !p->escHtml ){
602
- sbsWriteSpace(p, w-k, SBS_TXTA);
603
- }
604
-}
605
-
606
-/*
607
-** Append a column to the final output blob.
608
-*/
609
-static void sbsWriteColumn(Blob *pOut, Blob *pCol, int col){
610
- blob_appendf(pOut,
611
- "<td><div class=\"diff%scol\">\n"
612
- "<pre>\n"
613
- "%s"
614
- "</pre>\n"
615
- "</div></td>\n",
616
- (col % 3) ? (col == SBS_MKR ? "mkr" : "txt") : "ln",
617
- blob_str(pCol)
618
- );
619
-}
620
-
621
-/*
622
-** Append a separator line to column iCol
623
-*/
624
-static void sbsWriteSep(SbsLine *p, int len, int col){
625
- char ch = '.';
626
- if( len<1 ){
627
- len = 1;
628
- ch = ' ';
629
- }
630
- blob_appendf(p->apCols[col], "<span class=\"diffhr\">%.*c</span>\n", len, ch);
631
-}
632
-
633
-/*
634
-** Append the appropriate marker into the center column of the diff.
635
-*/
636
-static void sbsWriteMarker(SbsLine *p, const char *zTxt, const char *zHtml){
637
- blob_append(p->apCols[SBS_MKR], p->escHtml ? zHtml : zTxt, -1);
638
-}
639
-
640
-/*
641
-** Append a line number to the column.
642
-*/
643
-static void sbsWriteLineno(SbsLine *p, int ln, int col){
644
- if( p->escHtml ){
645
- blob_appendf(p->apCols[col], "%d", ln+1);
646
- }else{
647
- char zLn[7];
648
- sqlite3_snprintf(7, zLn, "%5d ", ln+1);
649
- blob_appendf(p->apCols[col], "%s ", zLn);
650
- }
651
-}
652
-
653484
/*
654485
** The two text segments zLeft and zRight are known to be different on
655486
** both ends, but they might have a common segment in the middle. If
656487
** they do not have a common segment, return 0. If they do have a large
657488
** common segment, return 1 and before doing so set:
@@ -746,232 +577,10 @@
746577
b->isMin = 0;
747578
}
748579
return p->n;
749580
}
750581
751
-/*
752
-** Try to shift a[0].iStart as far as possible to the left.
753
-*/
754
-static void sbsShiftLeft(SbsLine *p, const char *z){
755
- int i, j;
756
- while( (i=p->a[0].iStart)>0 && z[i-1]==z[i] ){
757
- for(j=i+1; j<p->a[0].iEnd && z[j-1]==z[j]; j++){}
758
- if( j<p->a[0].iEnd ) break;
759
- p->a[0].iStart--;
760
- p->a[0].iEnd--;
761
- }
762
-}
763
-
764
-/*
765
-** Simplify the diff-marks in a single line.
766
-**
767
-** * Remove any null (zero-length) diff marks.
768
-** * Make sure all changes are at character boundaries for
769
-** multi-byte characters.
770
-*/
771
-static void sbsSimplifyLine(SbsLine *p, const char *z){
772
- int i, j;
773
-
774
- /* Remove zero-length spans */
775
- for(i=j=0; i<p->n; i++){
776
- if( p->a[i].iStart<p->a[i].iEnd ){
777
- if( j<i ) memcpy(p->a+j, p->a+i, sizeof(p->a[0]));
778
- j++;
779
- }
780
- }
781
- p->n = j;
782
-
783
- /* Align all spans to a character boundary */
784
- for(i=0; i<p->n; i++){
785
- struct SbsMark *a = p->a+i;
786
- while( a->iStart>0 && (z[a->iStart]&0xc0)==0x80 ) a->iStart--;
787
- while( (z[a->iEnd]&0xc0)==0x80 ) a->iEnd++;
788
- }
789
-}
790
-
791
-/*
792
-** Write out lines that have been edited. Adjust the highlight to cover
793
-** only those parts of the line that actually changed.
794
-*/
795
-static void sbsWriteLineChange(
796
- SbsLine *p, /* The SBS output line */
797
- DLine *pLeft, /* Left line of the change */
798
- int lnLeft, /* Line number for the left line */
799
- DLine *pRight, /* Right line of the change */
800
- int lnRight /* Line number of the right line */
801
-){
802
- int nLeft; /* Length of left line in bytes */
803
- int nRight; /* Length of right line in bytes */
804
- int nShort; /* Shortest of left and right */
805
- int nPrefix; /* Length of common prefix */
806
- int nSuffix; /* Length of common suffix */
807
- const char *zLeft; /* Text of the left line */
808
- const char *zRight; /* Text of the right line */
809
- int nLeftDiff; /* nLeft - nPrefix - nSuffix */
810
- int nRightDiff; /* nRight - nPrefix - nSuffix */
811
- ChangeSpan CSpan; /* Set of changes on a line */
812
-
813
- nLeft = pLeft->n;
814
- zLeft = pLeft->z;
815
- nRight = pRight->n;
816
- zRight = pRight->z;
817
- nShort = nLeft<nRight ? nLeft : nRight;
818
-
819
- nPrefix = 0;
820
- while( nPrefix<nShort && zLeft[nPrefix]==zRight[nPrefix] ){
821
- nPrefix++;
822
- }
823
- if( nPrefix<nShort ){
824
- while( nPrefix>0 && (zLeft[nPrefix]&0xc0)==0x80 ) nPrefix--;
825
- }
826
- nSuffix = 0;
827
- if( nPrefix<nShort ){
828
- while( nSuffix<nShort && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){
829
- nSuffix++;
830
- }
831
- if( nSuffix<nShort ){
832
- while( nSuffix>0 && (zLeft[nLeft-nSuffix]&0xc0)==0x80 ) nSuffix--;
833
- }
834
- if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0;
835
- }
836
-
837
- /* If the prefix and suffix overlap, that means that we are dealing with
838
- ** a pure insertion or deletion of text that can have multiple alignments.
839
- ** Try to find an alignment to begins and ends on whitespace, or on
840
- ** punctuation, rather than in the middle of a name or number.
841
- */
842
- if( nPrefix+nSuffix > nShort ){
843
- int iBest = -1;
844
- int iBestVal = -1;
845
- int i;
846
- int nLong = nLeft<nRight ? nRight : nLeft;
847
- int nGap = nLong - nShort;
848
- for(i=nShort-nSuffix; i<=nPrefix; i++){
849
- int iVal = 0;
850
- char c = zLeft[i];
851
- if( fossil_isspace(c) ){
852
- iVal += 5;
853
- }else if( !fossil_isalnum(c) ){
854
- iVal += 2;
855
- }
856
- c = zLeft[i+nGap-1];
857
- if( fossil_isspace(c) ){
858
- iVal += 5;
859
- }else if( !fossil_isalnum(c) ){
860
- iVal += 2;
861
- }
862
- if( iVal>iBestVal ){
863
- iBestVal = iVal;
864
- iBest = i;
865
- }
866
- }
867
- nPrefix = iBest;
868
- nSuffix = nShort - nPrefix;
869
- }
870
-
871
- /* A single chunk of text inserted on the right */
872
- if( nPrefix+nSuffix==nLeft ){
873
- sbsWriteLineno(p, lnLeft, SBS_LNA);
874
- p->a[0].iStart = p->a[0].iEnd = -1;
875
- p->n = 0;
876
- sbsWriteText(p, pLeft, SBS_TXTA);
877
- if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){
878
- sbsWriteMarker(p, " ", "");
879
- }else{
880
- sbsWriteMarker(p, " | ", "|");
881
- }
882
- sbsWriteLineno(p, lnRight, SBS_LNB);
883
- p->a[0].iStart = nPrefix;
884
- p->a[0].iEnd = nRight - nSuffix;
885
- p->n = 1;
886
- p->a[0].zTag = zClassAdd;
887
- sbsWriteText(p, pRight, SBS_TXTB);
888
- return;
889
- }
890
-
891
- /* A single chunk of text deleted from the left */
892
- if( nPrefix+nSuffix==nRight ){
893
- /* Text deleted from the left */
894
- sbsWriteLineno(p, lnLeft, SBS_LNA);
895
- p->a[0].iStart = nPrefix;
896
- p->a[0].iEnd = nLeft - nSuffix;
897
- p->a[0].zTag = zClassRm;
898
- p->n = 1;
899
- sbsWriteText(p, pLeft, SBS_TXTA);
900
- sbsWriteMarker(p, " | ", "|");
901
- sbsWriteLineno(p, lnRight, SBS_LNB);
902
- p->a[0].iStart = p->a[0].iEnd = -1;
903
- p->n = 0;
904
- sbsWriteText(p, pRight, SBS_TXTB);
905
- return;
906
- }
907
-
908
- /* At this point we know that there is a chunk of text that has
909
- ** changed between the left and the right. Check to see if there
910
- ** is a large unchanged section in the middle of that changed block.
911
- */
912
- nLeftDiff = nLeft - nSuffix - nPrefix;
913
- nRightDiff = nRight - nSuffix - nPrefix;
914
- if( p->escHtml
915
- && nLeftDiff >= 6
916
- && nRightDiff >= 6
917
- && textChangeSpans(&zLeft[nPrefix], nLeftDiff,
918
- &zRight[nPrefix], nRightDiff, &CSpan)>1
919
- ){
920
- int i, j;
921
- sbsWriteLineno(p, lnLeft, SBS_LNA);
922
- for(i=j=0; i<CSpan.n; i++){
923
- if( CSpan.a[i].iLen1==0 ) continue;
924
- p->a[j].iStart = nPrefix + CSpan.a[i].iStart1;
925
- p->a[j].iEnd = p->a[j].iStart + CSpan.a[i].iLen1;
926
- if( CSpan.a[i].iLen2==0 ){
927
- if( i==0 ) sbsShiftLeft(p, zLeft);
928
- p->a[j].zTag = zClassRm;
929
- }else{
930
- p->a[j].zTag = zClassChng;
931
- }
932
- j++;
933
- }
934
- p->n = j;
935
- sbsSimplifyLine(p, zLeft);
936
- sbsWriteText(p, pLeft, SBS_TXTA);
937
- sbsWriteMarker(p, " | ", "|");
938
- sbsWriteLineno(p, lnRight, SBS_LNB);
939
- for(i=j=0; i<CSpan.n; i++){
940
- if( CSpan.a[i].iLen2==0 ) continue;
941
- p->a[j].iStart = nPrefix + CSpan.a[i].iStart2;
942
- p->a[j].iEnd = p->a[j].iStart + CSpan.a[i].iLen2;
943
- if( CSpan.a[i].iLen1==0 ){
944
- if( i==0 ) sbsShiftLeft(p, zRight);
945
- p->a[j].zTag = zClassAdd;
946
- }else{
947
- p->a[j].zTag = zClassChng;
948
- }
949
- j++;
950
- }
951
- p->n = j;
952
- sbsSimplifyLine(p, zRight);
953
- sbsWriteText(p, pRight, SBS_TXTB);
954
- return;
955
- }
956
-
957
- /* If all else fails, show a single big change between left and right */
958
- sbsWriteLineno(p, lnLeft, SBS_LNA);
959
- p->a[0].iStart = nPrefix;
960
- p->a[0].iEnd = nLeft - nSuffix;
961
- p->a[0].zTag = zClassChng;
962
- p->n = 1;
963
- sbsWriteText(p, pLeft, SBS_TXTA);
964
- sbsWriteMarker(p, " | ", "|");
965
- sbsWriteLineno(p, lnRight, SBS_LNB);
966
- p->a[0].iStart = nPrefix;
967
- p->a[0].iEnd = nRight - nSuffix;
968
- p->a[0].zTag = zClassChng;
969
- p->n = 1;
970
- sbsWriteText(p, pRight, SBS_TXTB);
971
-}
972
-
973582
/*
974583
** Given two lines of text, pFrom and pTo, compute a set of changes
975584
** between those two lines, for enhanced display purposes.
976585
**
977586
** The result is written into the ChangeSpan object given by the
@@ -1199,11 +808,11 @@
1199808
int i, j, k; /* Loop counters */
1200809
int *a; /* One row of the Wagner matrix */
1201810
int *pToFree; /* Space that needs to be freed */
1202811
unsigned char *aM; /* Wagner result matrix */
1203812
int nMatch, iMatch; /* Number of matching lines and match score */
1204
- int mnLen; /* MIN(nLeft, nRight) */
813
+ int mnLen; /* minInt(nLeft, nRight) */
1205814
int mxLen; /* MAX(nLeft, nRight) */
1206815
int aBuf[100]; /* Stack space for a[] if nRight not to big */
1207816
1208817
if( nLeft==0 ){
1209818
aM = fossil_malloc( nLeft + nRight + 2 );
@@ -1314,262 +923,10 @@
1314923
/* Return the result */
1315924
fossil_free(pToFree);
1316925
return aM;
1317926
}
1318927
1319
-/*
1320
-** Given a diff context in which the aEdit[] array has been filled
1321
-** in, compute a side-by-side diff into pOut.
1322
-*/
1323
-static void sbsDiff(
1324
- DContext *p, /* The computed diff */
1325
- Blob *pOut, /* Write the results here */
1326
- ReCompiled *pRe, /* Only show changes that match this regex */
1327
- u64 diffFlags /* Flags controlling the diff */
1328
-){
1329
- DLine *A; /* Left side of the diff */
1330
- DLine *B; /* Right side of the diff */
1331
- int a = 0; /* Index of next line in A[] */
1332
- int b = 0; /* Index of next line in B[] */
1333
- int *R; /* Array of COPY/DELETE/INSERT triples */
1334
- int r; /* Index into R[] */
1335
- int nr; /* Number of COPY/DELETE/INSERT triples to process */
1336
- int mxr; /* Maximum value for r */
1337
- int na, nb; /* Number of lines shown from A and B */
1338
- int i, j; /* Loop counters */
1339
- int m, ma, mb;/* Number of lines to output */
1340
- int skip; /* Number of lines to skip */
1341
- static int nChunk = 0; /* Number of chunks of diff output seen so far */
1342
- SbsLine s; /* Output line buffer */
1343
- int nContext; /* Lines of context above and below each change */
1344
- int showDivider = 0; /* True to show the divider */
1345
- Blob aCols[5]; /* Array of column blobs */
1346
-
1347
- memset(&s, 0, sizeof(s));
1348
- s.width = diff_width(diffFlags);
1349
- nContext = diff_context_lines(diffFlags);
1350
- s.escHtml = (diffFlags & DIFF_HTML)!=0;
1351
- if( s.escHtml ){
1352
- for(i=SBS_LNA; i<=SBS_TXTB; i++){
1353
- blob_zero(&aCols[i]);
1354
- s.apCols[i] = &aCols[i];
1355
- }
1356
- }else{
1357
- for(i=SBS_LNA; i<=SBS_TXTB; i++){
1358
- s.apCols[i] = pOut;
1359
- }
1360
- }
1361
- s.pRe = pRe;
1362
- s.a[0].iStart = -1;
1363
- s.a[1].iStart = 0;
1364
- s.a[0].iEnd = -1;
1365
- s.n = 0;
1366
- A = p->aFrom;
1367
- B = p->aTo;
1368
- R = p->aEdit;
1369
- mxr = p->nEdit;
1370
- while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
1371
-
1372
- for(r=0; r<mxr; r += 3*nr){
1373
- /* Figure out how many triples to show in a single block */
1374
- for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
1375
- /* printf("r=%d nr=%d\n", r, nr); */
1376
-
1377
- /* If there is a regex, skip this block (generate no diff output)
1378
- ** if the regex matches or does not match both insert and delete.
1379
- ** Only display the block if one side matches but the other side does
1380
- ** not.
1381
- */
1382
- if( pRe ){
1383
- int hideBlock = 1;
1384
- int xa = a, xb = b;
1385
- for(i=0; hideBlock && i<nr; i++){
1386
- int c1, c2;
1387
- xa += R[r+i*3];
1388
- xb += R[r+i*3];
1389
- c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
1390
- c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
1391
- hideBlock = c1==c2;
1392
- xa += R[r+i*3+1];
1393
- xb += R[r+i*3+2];
1394
- }
1395
- if( hideBlock ){
1396
- a = xa;
1397
- b = xb;
1398
- continue;
1399
- }
1400
- }
1401
-
1402
- /* For the current block comprising nr triples, figure out
1403
- ** how many lines of A and B are to be displayed
1404
- */
1405
- if( R[r]>nContext ){
1406
- na = nb = nContext;
1407
- skip = R[r] - nContext;
1408
- }else{
1409
- na = nb = R[r];
1410
- skip = 0;
1411
- }
1412
- for(i=0; i<nr; i++){
1413
- na += R[r+i*3+1];
1414
- nb += R[r+i*3+2];
1415
- }
1416
- if( R[r+nr*3]>nContext ){
1417
- na += nContext;
1418
- nb += nContext;
1419
- }else{
1420
- na += R[r+nr*3];
1421
- nb += R[r+nr*3];
1422
- }
1423
- for(i=1; i<nr; i++){
1424
- na += R[r+i*3];
1425
- nb += R[r+i*3];
1426
- }
1427
-
1428
- /* Draw the separator between blocks */
1429
- if( showDivider ){
1430
- if( s.escHtml ){
1431
- char zLn[10];
1432
- sqlite3_snprintf(sizeof(zLn), zLn, "%d", a+skip+1);
1433
- sbsWriteSep(&s, strlen(zLn), SBS_LNA);
1434
- sbsWriteSep(&s, s.width, SBS_TXTA);
1435
- sbsWriteSep(&s, 0, SBS_MKR);
1436
- sqlite3_snprintf(sizeof(zLn), zLn, "%d", b+skip+1);
1437
- sbsWriteSep(&s, strlen(zLn), SBS_LNB);
1438
- sbsWriteSep(&s, s.width, SBS_TXTB);
1439
- }else{
1440
- blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
1441
- }
1442
- }
1443
- showDivider = 1;
1444
- nChunk++;
1445
- if( s.escHtml ){
1446
- blob_appendf(s.apCols[SBS_LNA], "<span id=\"chunk%d\"></span>", nChunk);
1447
- }
1448
-
1449
- /* Show the initial common area */
1450
- a += skip;
1451
- b += skip;
1452
- m = R[r] - skip;
1453
- for(j=0; j<m; j++){
1454
- sbsWriteLineno(&s, a+j, SBS_LNA);
1455
- s.a[0].iStart = s.a[0].iEnd = -1;
1456
- s.n = 1;
1457
- sbsWriteText(&s, &A[a+j], SBS_TXTA);
1458
- sbsWriteMarker(&s, " ", "");
1459
- sbsWriteLineno(&s, b+j, SBS_LNB);
1460
- sbsWriteText(&s, &B[b+j], SBS_TXTB);
1461
- }
1462
- a += m;
1463
- b += m;
1464
-
1465
- /* Show the differences */
1466
- for(i=0; i<nr; i++){
1467
- unsigned char *alignment;
1468
- ma = R[r+i*3+1]; /* Lines on left but not on right */
1469
- mb = R[r+i*3+2]; /* Lines on right but not on left */
1470
-
1471
- alignment = diffBlockAlignment(&A[a], ma, &B[b], mb, diffFlags);
1472
- for(j=0; ma+mb>0; j++){
1473
- if( alignment[j]==1 ){
1474
- /* Delete one line from the left */
1475
- sbsWriteLineno(&s, a, SBS_LNA);
1476
- s.a[0].iStart = 0;
1477
- s.a[0].zTag = zClassRm;
1478
- s.a[0].iEnd = LENGTH(&A[a]);
1479
- s.n = 1;
1480
- sbsWriteText(&s, &A[a], SBS_TXTA);
1481
- sbsWriteMarker(&s, " <", "&lt;");
1482
- sbsWriteNewlines(&s);
1483
- assert( ma>0 );
1484
- ma--;
1485
- a++;
1486
- }else if( alignment[j]==3 ){
1487
- /* The left line is changed into the right line */
1488
- sbsWriteLineChange(&s, &A[a], a, &B[b], b);
1489
- assert( ma>0 && mb>0 );
1490
- ma--;
1491
- mb--;
1492
- a++;
1493
- b++;
1494
- }else if( alignment[j]==2 ){
1495
- /* Insert one line on the right */
1496
- if( !s.escHtml ){
1497
- sbsWriteSpace(&s, s.width + 7, SBS_TXTA);
1498
- }
1499
- sbsWriteMarker(&s, " > ", "&gt;");
1500
- sbsWriteLineno(&s, b, SBS_LNB);
1501
- s.a[0].iStart = 0;
1502
- s.a[0].zTag = zClassAdd;
1503
- s.a[0].iEnd = LENGTH(&B[b]);
1504
- s.n = 1;
1505
- sbsWriteText(&s, &B[b], SBS_TXTB);
1506
- assert( mb>0 );
1507
- mb--;
1508
- b++;
1509
- }else{
1510
- /* Delete from the left and insert on the right */
1511
- sbsWriteLineno(&s, a, SBS_LNA);
1512
- s.a[0].iStart = 0;
1513
- s.a[0].zTag = zClassRm;
1514
- s.a[0].iEnd = LENGTH(&A[a]);
1515
- s.n = 1;
1516
- sbsWriteText(&s, &A[a], SBS_TXTA);
1517
- sbsWriteMarker(&s, " | ", "|");
1518
- sbsWriteLineno(&s, b, SBS_LNB);
1519
- s.a[0].iStart = 0;
1520
- s.a[0].zTag = zClassAdd;
1521
- s.a[0].iEnd = LENGTH(&B[b]);
1522
- s.n = 1;
1523
- sbsWriteText(&s, &B[b], SBS_TXTB);
1524
- ma--;
1525
- mb--;
1526
- a++;
1527
- b++;
1528
- }
1529
- }
1530
- fossil_free(alignment);
1531
- if( i<nr-1 ){
1532
- m = R[r+i*3+3];
1533
- for(j=0; j<m; j++){
1534
- sbsWriteLineno(&s, a+j, SBS_LNA);
1535
- s.a[0].iStart = s.a[0].iEnd = -1;
1536
- s.n = 0;
1537
- sbsWriteText(&s, &A[a+j], SBS_TXTA);
1538
- sbsWriteMarker(&s, " ", "");
1539
- sbsWriteLineno(&s, b+j, SBS_LNB);
1540
- sbsWriteText(&s, &B[b+j], SBS_TXTB);
1541
- }
1542
- b += m;
1543
- a += m;
1544
- }
1545
- }
1546
-
1547
- /* Show the final common area */
1548
- assert( nr==i );
1549
- m = R[r+nr*3];
1550
- if( m>nContext ) m = nContext;
1551
- for(j=0; j<m; j++){
1552
- sbsWriteLineno(&s, a+j, SBS_LNA);
1553
- s.a[0].iStart = s.a[0].iEnd = -1;
1554
- s.n = 0;
1555
- sbsWriteText(&s, &A[a+j], SBS_TXTA);
1556
- sbsWriteMarker(&s, " ", "");
1557
- sbsWriteLineno(&s, b+j, SBS_LNB);
1558
- sbsWriteText(&s, &B[b+j], SBS_TXTB);
1559
- }
1560
- }
1561
-
1562
- if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){
1563
- blob_append(pOut, "<table class=\"sbsdiffcols\"><tr>\n", -1);
1564
- for(i=SBS_LNA; i<=SBS_TXTB; i++){
1565
- sbsWriteColumn(pOut, s.apCols[i], i);
1566
- blob_reset(s.apCols[i]);
1567
- }
1568
- blob_append(pOut, "</tr></table>\n", -1);
1569
- }
1570
-}
1571928
1572929
/*
1573930
** This is an abstract superclass for an object that accepts difference
1574931
** lines and formats them for display. Subclasses of this object format
1575932
** the diff output in different ways.
@@ -1588,10 +945,11 @@
1588945
void (*xEnd)(DiffBuilder*);
1589946
unsigned int lnLeft; /* Lines seen on the left (delete) side */
1590947
unsigned int lnRight; /* Lines seen on the right (insert) side */
1591948
unsigned int nPending; /* Number of pending lines */
1592949
int eState; /* State of the output */
950
+ int width; /* Display width */
1593951
Blob *pOut; /* Output blob */
1594952
Blob aCol[5]; /* Holding blobs */
1595953
};
1596954
1597955
/************************* DiffBuilderDebug ********************************/
@@ -2287,10 +1645,91 @@
22871645
blob_init(&p->aCol[2], 0, 0);
22881646
blob_init(&p->aCol[3], 0, 0);
22891647
blob_init(&p->aCol[4], 0, 0);
22901648
return p;
22911649
}
1650
+
1651
+/************************* DiffBuilderSbs ******************************/
1652
+/* This formatter creates a side-by-side diff in text.
1653
+*/
1654
+static void dfsbsSkip(DiffBuilder *p, unsigned int n, int isFinal){
1655
+ if( (p->lnLeft || p->lnRight) && !isFinal ){
1656
+ blob_appendf(p->pOut, "%.*c\n", p->width*2 + 16, '.');
1657
+ }
1658
+ p->lnLeft += n;
1659
+ p->lnRight += n;
1660
+}
1661
+
1662
+/*
1663
+** Append at least iMin characters (not bytes) and at most iMax characters
1664
+** from pX onto the into of p.
1665
+**
1666
+** This comment contains multibyte unicode characters (ü, Æ, ð) in order
1667
+** to test the ability of the diff code to handle such characters.
1668
+*/
1669
+static void sbs_append_chars(Blob *p, int iMin, int iMax, const DLine *pX){
1670
+ int i;
1671
+ const char *z = pX->z;
1672
+ for(i=0; i<iMax && i<pX->n; i++){
1673
+ char c = z[i];
1674
+ blob_append_char(p, c);
1675
+ if( (c&0xc0)==0x80 ){ iMin++; iMax++; }
1676
+ }
1677
+ while( i<iMin ){
1678
+ blob_append_char(p, ' ');
1679
+ i++;
1680
+ }
1681
+}
1682
+
1683
+static void dfsbsCommon(DiffBuilder *p, const DLine *pLine){
1684
+ p->lnLeft++;
1685
+ p->lnRight++;
1686
+ blob_appendf(p->pOut,"%6u ", p->lnLeft);
1687
+ sbs_append_chars(p->pOut, p->width, p->width, pLine);
1688
+ blob_appendf(p->pOut," %6u ", p->lnRight);
1689
+ sbs_append_chars(p->pOut, 0, p->width, pLine);
1690
+ blob_append_char(p->pOut, '\n');
1691
+}
1692
+static void dfsbsInsert(DiffBuilder *p, const DLine *pLine){
1693
+ p->lnRight++;
1694
+ blob_appendf(p->pOut,"%6s %*s > %6u ",
1695
+ "", p->width, "", p->lnRight);
1696
+ sbs_append_chars(p->pOut, 0, p->width, pLine);
1697
+ blob_append_char(p->pOut, '\n');
1698
+}
1699
+static void dfsbsDelete(DiffBuilder *p, const DLine *pLine){
1700
+ p->lnLeft++;
1701
+ blob_appendf(p->pOut,"%6u ", p->lnLeft);
1702
+ sbs_append_chars(p->pOut, p->width, p->width, pLine);
1703
+ blob_append(p->pOut," <\n", 3);
1704
+}
1705
+static void dfsbsEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
1706
+ p->lnLeft++;
1707
+ p->lnRight++;
1708
+ blob_appendf(p->pOut,"%6u ", p->lnLeft);
1709
+ sbs_append_chars(p->pOut, p->width, p->width, pX);
1710
+ blob_appendf(p->pOut, " | %6u ", p->lnRight);
1711
+ sbs_append_chars(p->pOut, 0, p->width, pY);
1712
+ blob_append_char(p->pOut, '\n');
1713
+}
1714
+static void dfsbsEnd(DiffBuilder *p){
1715
+ fossil_free(p);
1716
+}
1717
+static DiffBuilder *dfsbsNew(Blob *pOut, u64 diffFlags){
1718
+ DiffBuilder *p = fossil_malloc(sizeof(*p));
1719
+ p->xSkip = dfsbsSkip;
1720
+ p->xCommon = dfsbsCommon;
1721
+ p->xInsert = dfsbsInsert;
1722
+ p->xDelete = dfsbsDelete;
1723
+ p->xReplace = dfsbsEdit;
1724
+ p->xEdit = dfsbsEdit;
1725
+ p->xEnd = dfsbsEnd;
1726
+ p->lnLeft = p->lnRight = 0;
1727
+ p->width = diff_width(diffFlags);
1728
+ p->pOut = pOut;
1729
+ return p;
1730
+}
22921731
/****************************************************************************/
22931732
22941733
/*
22951734
** Format a diff using a DiffBuilder object
22961735
*/
@@ -3015,16 +2454,17 @@
30152454
blob_append_char(pOut, '\n');
30162455
}else if( diffFlags & DIFF_TCL ){
30172456
DiffBuilder *pBuilder = dftclNew(pOut);
30182457
formatDiff(&c, pRe, diffFlags, pBuilder);
30192458
}else if( diffFlags & DIFF_SIDEBYSIDE ){
2459
+ DiffBuilder *pBuilder;
30202460
if( diffFlags & DIFF_HTML ){
3021
- DiffBuilder *pBuilder = dfsplitNew(pOut);
3022
- formatDiff(&c, pRe, diffFlags, pBuilder);
2461
+ pBuilder = dfsplitNew(pOut);
30232462
}else{
3024
- sbsDiff(&c, pOut, pRe, diffFlags);
2463
+ pBuilder = dfsbsNew(pOut, diffFlags);
30252464
}
2465
+ formatDiff(&c, pRe, diffFlags, pBuilder);
30262466
}else if( diffFlags & DIFF_DEBUG ){
30272467
DiffBuilder *pBuilder = dfdebugNew(pOut);
30282468
formatDiff(&c, pRe, diffFlags, pBuilder);
30292469
}else if( diffFlags & DIFF_HTML ){
30302470
DiffBuilder *pBuilder = dfunifiedNew(pOut);
30312471
--- src/diff.c
+++ src/diff.c
@@ -461,33 +461,12 @@
461 appendDiffLine(pOut, ' ', &A[a+j]);
462 }
463 }
464 }
465
466 /*
467 ** Limits for the intra-line diffing.
468 */
469 #define SBS_MXN 8 /* Maximum number of change regions per line of text */
470 #define SBS_CSN 8 /* Maximum number of change spans across a change region */
471
472 /*
473 ** Status of a single output line
474 */
475 typedef struct SbsLine SbsLine;
476 struct SbsLine {
477 Blob *apCols[5]; /* Array of pointers to output columns */
478 int width; /* Maximum width of a column in the output */
479 unsigned char escHtml; /* True to escape html characters */
480 struct SbsMark {
481 int iStart; /* Write zTag prior to character iStart */
482 int iEnd; /* Write </span> prior to character iEnd */
483 const char *zTag; /* A <span> tag for coloration */
484 } a[SBS_MXN]; /* Change regions */
485 int n; /* Number of change regions used */
486 ReCompiled *pRe; /* Only colorize matching lines, if not NULL */
487 };
488
489 /*
490 ** A description of zero or more (up to SBS_CSN) areas of commonality
491 ** between two lines of text.
492 */
493 typedef struct ChangeSpan ChangeSpan;
@@ -500,158 +479,10 @@
500 int iLen2; /* Length of right change span in bytes */
501 int isMin; /* True if this span is known to have no useful subdivs */
502 } a[SBS_CSN]; /* Array of change spans, sorted order */
503 };
504
505 /*
506 ** Column indices for SbsLine.apCols[]
507 */
508 #define SBS_LNA 0 /* Left line number */
509 #define SBS_TXTA 1 /* Left text */
510 #define SBS_MKR 2 /* Middle separator column */
511 #define SBS_LNB 3 /* Right line number */
512 #define SBS_TXTB 4 /* Right text */
513
514 /*
515 ** Append newlines to all columns.
516 */
517 static void sbsWriteNewlines(SbsLine *p){
518 int i;
519 for( i=p->escHtml ? SBS_LNA : SBS_TXTB; i<=SBS_TXTB; i++ ){
520 blob_append(p->apCols[i], "\n", 1);
521 }
522 }
523
524 /*
525 ** Append n spaces to the column.
526 */
527 static void sbsWriteSpace(SbsLine *p, int n, int col){
528 blob_appendf(p->apCols[col], "%*s", n, "");
529 }
530
531 /*
532 ** Write the text of pLine into column iCol of p.
533 **
534 ** If outputting HTML, write the full line. Otherwise, only write the
535 ** width characters. Translate tabs into spaces. Add newlines if col
536 ** is SBS_TXTB. Translate HTML characters if escHtml is true. Pad the
537 ** rendering to width bytes if col is SBS_TXTA and escHtml is false.
538 **
539 ** This comment contains multibyte unicode characters (ü, Æ, ð) in order
540 ** to test the ability of the diff code to handle such characters.
541 */
542 static void sbsWriteText(SbsLine *p, DLine *pLine, int col){
543 Blob *pCol = p->apCols[col];
544 int n = pLine->n;
545 int i; /* Number of input characters consumed */
546 int k; /* Cursor position */
547 int needEndSpan = 0;
548 const char *zIn = pLine->z;
549 int w = p->width;
550 int colorize = p->escHtml && p->n>0;
551 if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
552 colorize = 0;
553 }
554 for(i=k=0; (p->escHtml || k<w) && i<n; i++, k++){
555 char c = zIn[i];
556 if( colorize ){
557 if( i==p->a[0].iStart ){
558 int x = strlen(p->a[0].zTag);
559 blob_append(pCol, p->a[0].zTag, x);
560 needEndSpan = 1;
561 }else if( i==p->a[0].iEnd ){
562 blob_append(pCol, "</span>", 7);
563 needEndSpan = 0;
564 if( p->n>1 ){
565 p->n--;
566 memmove(p->a, p->a+1, sizeof(p->a[0])*p->n);
567 }else{
568 colorize = 0;
569 }
570 }
571 }
572 if( c>'>' ){
573 blob_append_char(pCol, c);
574 if( (c&0xc0)==0x80 ) k--;
575 }else if( c=='\t' && !p->escHtml ){
576 blob_append(pCol, " ", 1);
577 while( (k&7)!=7 && (p->escHtml || k<w) ){
578 blob_append(pCol, " ", 1);
579 k++;
580 }
581 }else if( c=='\r' || c=='\f' ){
582 blob_append(pCol, " ", 1);
583 }else if( c=='<' && p->escHtml ){
584 blob_append(pCol, "&lt;", 4);
585 }else if( c=='&' && p->escHtml ){
586 blob_append(pCol, "&amp;", 5);
587 }else if( c=='>' && p->escHtml ){
588 blob_append(pCol, "&gt;", 4);
589 }else if( c=='"' && p->escHtml ){
590 blob_append(pCol, "&quot;", 6);
591 }else{
592 blob_append_char(pCol, c);
593 if( (c&0xc0)==0x80 ) k--;
594 }
595 }
596 if( needEndSpan ){
597 blob_append(pCol, "</span>", 7);
598 }
599 if( col==SBS_TXTB ){
600 sbsWriteNewlines(p);
601 }else if( !p->escHtml ){
602 sbsWriteSpace(p, w-k, SBS_TXTA);
603 }
604 }
605
606 /*
607 ** Append a column to the final output blob.
608 */
609 static void sbsWriteColumn(Blob *pOut, Blob *pCol, int col){
610 blob_appendf(pOut,
611 "<td><div class=\"diff%scol\">\n"
612 "<pre>\n"
613 "%s"
614 "</pre>\n"
615 "</div></td>\n",
616 (col % 3) ? (col == SBS_MKR ? "mkr" : "txt") : "ln",
617 blob_str(pCol)
618 );
619 }
620
621 /*
622 ** Append a separator line to column iCol
623 */
624 static void sbsWriteSep(SbsLine *p, int len, int col){
625 char ch = '.';
626 if( len<1 ){
627 len = 1;
628 ch = ' ';
629 }
630 blob_appendf(p->apCols[col], "<span class=\"diffhr\">%.*c</span>\n", len, ch);
631 }
632
633 /*
634 ** Append the appropriate marker into the center column of the diff.
635 */
636 static void sbsWriteMarker(SbsLine *p, const char *zTxt, const char *zHtml){
637 blob_append(p->apCols[SBS_MKR], p->escHtml ? zHtml : zTxt, -1);
638 }
639
640 /*
641 ** Append a line number to the column.
642 */
643 static void sbsWriteLineno(SbsLine *p, int ln, int col){
644 if( p->escHtml ){
645 blob_appendf(p->apCols[col], "%d", ln+1);
646 }else{
647 char zLn[7];
648 sqlite3_snprintf(7, zLn, "%5d ", ln+1);
649 blob_appendf(p->apCols[col], "%s ", zLn);
650 }
651 }
652
653 /*
654 ** The two text segments zLeft and zRight are known to be different on
655 ** both ends, but they might have a common segment in the middle. If
656 ** they do not have a common segment, return 0. If they do have a large
657 ** common segment, return 1 and before doing so set:
@@ -746,232 +577,10 @@
746 b->isMin = 0;
747 }
748 return p->n;
749 }
750
751 /*
752 ** Try to shift a[0].iStart as far as possible to the left.
753 */
754 static void sbsShiftLeft(SbsLine *p, const char *z){
755 int i, j;
756 while( (i=p->a[0].iStart)>0 && z[i-1]==z[i] ){
757 for(j=i+1; j<p->a[0].iEnd && z[j-1]==z[j]; j++){}
758 if( j<p->a[0].iEnd ) break;
759 p->a[0].iStart--;
760 p->a[0].iEnd--;
761 }
762 }
763
764 /*
765 ** Simplify the diff-marks in a single line.
766 **
767 ** * Remove any null (zero-length) diff marks.
768 ** * Make sure all changes are at character boundaries for
769 ** multi-byte characters.
770 */
771 static void sbsSimplifyLine(SbsLine *p, const char *z){
772 int i, j;
773
774 /* Remove zero-length spans */
775 for(i=j=0; i<p->n; i++){
776 if( p->a[i].iStart<p->a[i].iEnd ){
777 if( j<i ) memcpy(p->a+j, p->a+i, sizeof(p->a[0]));
778 j++;
779 }
780 }
781 p->n = j;
782
783 /* Align all spans to a character boundary */
784 for(i=0; i<p->n; i++){
785 struct SbsMark *a = p->a+i;
786 while( a->iStart>0 && (z[a->iStart]&0xc0)==0x80 ) a->iStart--;
787 while( (z[a->iEnd]&0xc0)==0x80 ) a->iEnd++;
788 }
789 }
790
791 /*
792 ** Write out lines that have been edited. Adjust the highlight to cover
793 ** only those parts of the line that actually changed.
794 */
795 static void sbsWriteLineChange(
796 SbsLine *p, /* The SBS output line */
797 DLine *pLeft, /* Left line of the change */
798 int lnLeft, /* Line number for the left line */
799 DLine *pRight, /* Right line of the change */
800 int lnRight /* Line number of the right line */
801 ){
802 int nLeft; /* Length of left line in bytes */
803 int nRight; /* Length of right line in bytes */
804 int nShort; /* Shortest of left and right */
805 int nPrefix; /* Length of common prefix */
806 int nSuffix; /* Length of common suffix */
807 const char *zLeft; /* Text of the left line */
808 const char *zRight; /* Text of the right line */
809 int nLeftDiff; /* nLeft - nPrefix - nSuffix */
810 int nRightDiff; /* nRight - nPrefix - nSuffix */
811 ChangeSpan CSpan; /* Set of changes on a line */
812
813 nLeft = pLeft->n;
814 zLeft = pLeft->z;
815 nRight = pRight->n;
816 zRight = pRight->z;
817 nShort = nLeft<nRight ? nLeft : nRight;
818
819 nPrefix = 0;
820 while( nPrefix<nShort && zLeft[nPrefix]==zRight[nPrefix] ){
821 nPrefix++;
822 }
823 if( nPrefix<nShort ){
824 while( nPrefix>0 && (zLeft[nPrefix]&0xc0)==0x80 ) nPrefix--;
825 }
826 nSuffix = 0;
827 if( nPrefix<nShort ){
828 while( nSuffix<nShort && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){
829 nSuffix++;
830 }
831 if( nSuffix<nShort ){
832 while( nSuffix>0 && (zLeft[nLeft-nSuffix]&0xc0)==0x80 ) nSuffix--;
833 }
834 if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0;
835 }
836
837 /* If the prefix and suffix overlap, that means that we are dealing with
838 ** a pure insertion or deletion of text that can have multiple alignments.
839 ** Try to find an alignment to begins and ends on whitespace, or on
840 ** punctuation, rather than in the middle of a name or number.
841 */
842 if( nPrefix+nSuffix > nShort ){
843 int iBest = -1;
844 int iBestVal = -1;
845 int i;
846 int nLong = nLeft<nRight ? nRight : nLeft;
847 int nGap = nLong - nShort;
848 for(i=nShort-nSuffix; i<=nPrefix; i++){
849 int iVal = 0;
850 char c = zLeft[i];
851 if( fossil_isspace(c) ){
852 iVal += 5;
853 }else if( !fossil_isalnum(c) ){
854 iVal += 2;
855 }
856 c = zLeft[i+nGap-1];
857 if( fossil_isspace(c) ){
858 iVal += 5;
859 }else if( !fossil_isalnum(c) ){
860 iVal += 2;
861 }
862 if( iVal>iBestVal ){
863 iBestVal = iVal;
864 iBest = i;
865 }
866 }
867 nPrefix = iBest;
868 nSuffix = nShort - nPrefix;
869 }
870
871 /* A single chunk of text inserted on the right */
872 if( nPrefix+nSuffix==nLeft ){
873 sbsWriteLineno(p, lnLeft, SBS_LNA);
874 p->a[0].iStart = p->a[0].iEnd = -1;
875 p->n = 0;
876 sbsWriteText(p, pLeft, SBS_TXTA);
877 if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){
878 sbsWriteMarker(p, " ", "");
879 }else{
880 sbsWriteMarker(p, " | ", "|");
881 }
882 sbsWriteLineno(p, lnRight, SBS_LNB);
883 p->a[0].iStart = nPrefix;
884 p->a[0].iEnd = nRight - nSuffix;
885 p->n = 1;
886 p->a[0].zTag = zClassAdd;
887 sbsWriteText(p, pRight, SBS_TXTB);
888 return;
889 }
890
891 /* A single chunk of text deleted from the left */
892 if( nPrefix+nSuffix==nRight ){
893 /* Text deleted from the left */
894 sbsWriteLineno(p, lnLeft, SBS_LNA);
895 p->a[0].iStart = nPrefix;
896 p->a[0].iEnd = nLeft - nSuffix;
897 p->a[0].zTag = zClassRm;
898 p->n = 1;
899 sbsWriteText(p, pLeft, SBS_TXTA);
900 sbsWriteMarker(p, " | ", "|");
901 sbsWriteLineno(p, lnRight, SBS_LNB);
902 p->a[0].iStart = p->a[0].iEnd = -1;
903 p->n = 0;
904 sbsWriteText(p, pRight, SBS_TXTB);
905 return;
906 }
907
908 /* At this point we know that there is a chunk of text that has
909 ** changed between the left and the right. Check to see if there
910 ** is a large unchanged section in the middle of that changed block.
911 */
912 nLeftDiff = nLeft - nSuffix - nPrefix;
913 nRightDiff = nRight - nSuffix - nPrefix;
914 if( p->escHtml
915 && nLeftDiff >= 6
916 && nRightDiff >= 6
917 && textChangeSpans(&zLeft[nPrefix], nLeftDiff,
918 &zRight[nPrefix], nRightDiff, &CSpan)>1
919 ){
920 int i, j;
921 sbsWriteLineno(p, lnLeft, SBS_LNA);
922 for(i=j=0; i<CSpan.n; i++){
923 if( CSpan.a[i].iLen1==0 ) continue;
924 p->a[j].iStart = nPrefix + CSpan.a[i].iStart1;
925 p->a[j].iEnd = p->a[j].iStart + CSpan.a[i].iLen1;
926 if( CSpan.a[i].iLen2==0 ){
927 if( i==0 ) sbsShiftLeft(p, zLeft);
928 p->a[j].zTag = zClassRm;
929 }else{
930 p->a[j].zTag = zClassChng;
931 }
932 j++;
933 }
934 p->n = j;
935 sbsSimplifyLine(p, zLeft);
936 sbsWriteText(p, pLeft, SBS_TXTA);
937 sbsWriteMarker(p, " | ", "|");
938 sbsWriteLineno(p, lnRight, SBS_LNB);
939 for(i=j=0; i<CSpan.n; i++){
940 if( CSpan.a[i].iLen2==0 ) continue;
941 p->a[j].iStart = nPrefix + CSpan.a[i].iStart2;
942 p->a[j].iEnd = p->a[j].iStart + CSpan.a[i].iLen2;
943 if( CSpan.a[i].iLen1==0 ){
944 if( i==0 ) sbsShiftLeft(p, zRight);
945 p->a[j].zTag = zClassAdd;
946 }else{
947 p->a[j].zTag = zClassChng;
948 }
949 j++;
950 }
951 p->n = j;
952 sbsSimplifyLine(p, zRight);
953 sbsWriteText(p, pRight, SBS_TXTB);
954 return;
955 }
956
957 /* If all else fails, show a single big change between left and right */
958 sbsWriteLineno(p, lnLeft, SBS_LNA);
959 p->a[0].iStart = nPrefix;
960 p->a[0].iEnd = nLeft - nSuffix;
961 p->a[0].zTag = zClassChng;
962 p->n = 1;
963 sbsWriteText(p, pLeft, SBS_TXTA);
964 sbsWriteMarker(p, " | ", "|");
965 sbsWriteLineno(p, lnRight, SBS_LNB);
966 p->a[0].iStart = nPrefix;
967 p->a[0].iEnd = nRight - nSuffix;
968 p->a[0].zTag = zClassChng;
969 p->n = 1;
970 sbsWriteText(p, pRight, SBS_TXTB);
971 }
972
973 /*
974 ** Given two lines of text, pFrom and pTo, compute a set of changes
975 ** between those two lines, for enhanced display purposes.
976 **
977 ** The result is written into the ChangeSpan object given by the
@@ -1199,11 +808,11 @@
1199 int i, j, k; /* Loop counters */
1200 int *a; /* One row of the Wagner matrix */
1201 int *pToFree; /* Space that needs to be freed */
1202 unsigned char *aM; /* Wagner result matrix */
1203 int nMatch, iMatch; /* Number of matching lines and match score */
1204 int mnLen; /* MIN(nLeft, nRight) */
1205 int mxLen; /* MAX(nLeft, nRight) */
1206 int aBuf[100]; /* Stack space for a[] if nRight not to big */
1207
1208 if( nLeft==0 ){
1209 aM = fossil_malloc( nLeft + nRight + 2 );
@@ -1314,262 +923,10 @@
1314 /* Return the result */
1315 fossil_free(pToFree);
1316 return aM;
1317 }
1318
1319 /*
1320 ** Given a diff context in which the aEdit[] array has been filled
1321 ** in, compute a side-by-side diff into pOut.
1322 */
1323 static void sbsDiff(
1324 DContext *p, /* The computed diff */
1325 Blob *pOut, /* Write the results here */
1326 ReCompiled *pRe, /* Only show changes that match this regex */
1327 u64 diffFlags /* Flags controlling the diff */
1328 ){
1329 DLine *A; /* Left side of the diff */
1330 DLine *B; /* Right side of the diff */
1331 int a = 0; /* Index of next line in A[] */
1332 int b = 0; /* Index of next line in B[] */
1333 int *R; /* Array of COPY/DELETE/INSERT triples */
1334 int r; /* Index into R[] */
1335 int nr; /* Number of COPY/DELETE/INSERT triples to process */
1336 int mxr; /* Maximum value for r */
1337 int na, nb; /* Number of lines shown from A and B */
1338 int i, j; /* Loop counters */
1339 int m, ma, mb;/* Number of lines to output */
1340 int skip; /* Number of lines to skip */
1341 static int nChunk = 0; /* Number of chunks of diff output seen so far */
1342 SbsLine s; /* Output line buffer */
1343 int nContext; /* Lines of context above and below each change */
1344 int showDivider = 0; /* True to show the divider */
1345 Blob aCols[5]; /* Array of column blobs */
1346
1347 memset(&s, 0, sizeof(s));
1348 s.width = diff_width(diffFlags);
1349 nContext = diff_context_lines(diffFlags);
1350 s.escHtml = (diffFlags & DIFF_HTML)!=0;
1351 if( s.escHtml ){
1352 for(i=SBS_LNA; i<=SBS_TXTB; i++){
1353 blob_zero(&aCols[i]);
1354 s.apCols[i] = &aCols[i];
1355 }
1356 }else{
1357 for(i=SBS_LNA; i<=SBS_TXTB; i++){
1358 s.apCols[i] = pOut;
1359 }
1360 }
1361 s.pRe = pRe;
1362 s.a[0].iStart = -1;
1363 s.a[1].iStart = 0;
1364 s.a[0].iEnd = -1;
1365 s.n = 0;
1366 A = p->aFrom;
1367 B = p->aTo;
1368 R = p->aEdit;
1369 mxr = p->nEdit;
1370 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
1371
1372 for(r=0; r<mxr; r += 3*nr){
1373 /* Figure out how many triples to show in a single block */
1374 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
1375 /* printf("r=%d nr=%d\n", r, nr); */
1376
1377 /* If there is a regex, skip this block (generate no diff output)
1378 ** if the regex matches or does not match both insert and delete.
1379 ** Only display the block if one side matches but the other side does
1380 ** not.
1381 */
1382 if( pRe ){
1383 int hideBlock = 1;
1384 int xa = a, xb = b;
1385 for(i=0; hideBlock && i<nr; i++){
1386 int c1, c2;
1387 xa += R[r+i*3];
1388 xb += R[r+i*3];
1389 c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
1390 c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
1391 hideBlock = c1==c2;
1392 xa += R[r+i*3+1];
1393 xb += R[r+i*3+2];
1394 }
1395 if( hideBlock ){
1396 a = xa;
1397 b = xb;
1398 continue;
1399 }
1400 }
1401
1402 /* For the current block comprising nr triples, figure out
1403 ** how many lines of A and B are to be displayed
1404 */
1405 if( R[r]>nContext ){
1406 na = nb = nContext;
1407 skip = R[r] - nContext;
1408 }else{
1409 na = nb = R[r];
1410 skip = 0;
1411 }
1412 for(i=0; i<nr; i++){
1413 na += R[r+i*3+1];
1414 nb += R[r+i*3+2];
1415 }
1416 if( R[r+nr*3]>nContext ){
1417 na += nContext;
1418 nb += nContext;
1419 }else{
1420 na += R[r+nr*3];
1421 nb += R[r+nr*3];
1422 }
1423 for(i=1; i<nr; i++){
1424 na += R[r+i*3];
1425 nb += R[r+i*3];
1426 }
1427
1428 /* Draw the separator between blocks */
1429 if( showDivider ){
1430 if( s.escHtml ){
1431 char zLn[10];
1432 sqlite3_snprintf(sizeof(zLn), zLn, "%d", a+skip+1);
1433 sbsWriteSep(&s, strlen(zLn), SBS_LNA);
1434 sbsWriteSep(&s, s.width, SBS_TXTA);
1435 sbsWriteSep(&s, 0, SBS_MKR);
1436 sqlite3_snprintf(sizeof(zLn), zLn, "%d", b+skip+1);
1437 sbsWriteSep(&s, strlen(zLn), SBS_LNB);
1438 sbsWriteSep(&s, s.width, SBS_TXTB);
1439 }else{
1440 blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
1441 }
1442 }
1443 showDivider = 1;
1444 nChunk++;
1445 if( s.escHtml ){
1446 blob_appendf(s.apCols[SBS_LNA], "<span id=\"chunk%d\"></span>", nChunk);
1447 }
1448
1449 /* Show the initial common area */
1450 a += skip;
1451 b += skip;
1452 m = R[r] - skip;
1453 for(j=0; j<m; j++){
1454 sbsWriteLineno(&s, a+j, SBS_LNA);
1455 s.a[0].iStart = s.a[0].iEnd = -1;
1456 s.n = 1;
1457 sbsWriteText(&s, &A[a+j], SBS_TXTA);
1458 sbsWriteMarker(&s, " ", "");
1459 sbsWriteLineno(&s, b+j, SBS_LNB);
1460 sbsWriteText(&s, &B[b+j], SBS_TXTB);
1461 }
1462 a += m;
1463 b += m;
1464
1465 /* Show the differences */
1466 for(i=0; i<nr; i++){
1467 unsigned char *alignment;
1468 ma = R[r+i*3+1]; /* Lines on left but not on right */
1469 mb = R[r+i*3+2]; /* Lines on right but not on left */
1470
1471 alignment = diffBlockAlignment(&A[a], ma, &B[b], mb, diffFlags);
1472 for(j=0; ma+mb>0; j++){
1473 if( alignment[j]==1 ){
1474 /* Delete one line from the left */
1475 sbsWriteLineno(&s, a, SBS_LNA);
1476 s.a[0].iStart = 0;
1477 s.a[0].zTag = zClassRm;
1478 s.a[0].iEnd = LENGTH(&A[a]);
1479 s.n = 1;
1480 sbsWriteText(&s, &A[a], SBS_TXTA);
1481 sbsWriteMarker(&s, " <", "&lt;");
1482 sbsWriteNewlines(&s);
1483 assert( ma>0 );
1484 ma--;
1485 a++;
1486 }else if( alignment[j]==3 ){
1487 /* The left line is changed into the right line */
1488 sbsWriteLineChange(&s, &A[a], a, &B[b], b);
1489 assert( ma>0 && mb>0 );
1490 ma--;
1491 mb--;
1492 a++;
1493 b++;
1494 }else if( alignment[j]==2 ){
1495 /* Insert one line on the right */
1496 if( !s.escHtml ){
1497 sbsWriteSpace(&s, s.width + 7, SBS_TXTA);
1498 }
1499 sbsWriteMarker(&s, " > ", "&gt;");
1500 sbsWriteLineno(&s, b, SBS_LNB);
1501 s.a[0].iStart = 0;
1502 s.a[0].zTag = zClassAdd;
1503 s.a[0].iEnd = LENGTH(&B[b]);
1504 s.n = 1;
1505 sbsWriteText(&s, &B[b], SBS_TXTB);
1506 assert( mb>0 );
1507 mb--;
1508 b++;
1509 }else{
1510 /* Delete from the left and insert on the right */
1511 sbsWriteLineno(&s, a, SBS_LNA);
1512 s.a[0].iStart = 0;
1513 s.a[0].zTag = zClassRm;
1514 s.a[0].iEnd = LENGTH(&A[a]);
1515 s.n = 1;
1516 sbsWriteText(&s, &A[a], SBS_TXTA);
1517 sbsWriteMarker(&s, " | ", "|");
1518 sbsWriteLineno(&s, b, SBS_LNB);
1519 s.a[0].iStart = 0;
1520 s.a[0].zTag = zClassAdd;
1521 s.a[0].iEnd = LENGTH(&B[b]);
1522 s.n = 1;
1523 sbsWriteText(&s, &B[b], SBS_TXTB);
1524 ma--;
1525 mb--;
1526 a++;
1527 b++;
1528 }
1529 }
1530 fossil_free(alignment);
1531 if( i<nr-1 ){
1532 m = R[r+i*3+3];
1533 for(j=0; j<m; j++){
1534 sbsWriteLineno(&s, a+j, SBS_LNA);
1535 s.a[0].iStart = s.a[0].iEnd = -1;
1536 s.n = 0;
1537 sbsWriteText(&s, &A[a+j], SBS_TXTA);
1538 sbsWriteMarker(&s, " ", "");
1539 sbsWriteLineno(&s, b+j, SBS_LNB);
1540 sbsWriteText(&s, &B[b+j], SBS_TXTB);
1541 }
1542 b += m;
1543 a += m;
1544 }
1545 }
1546
1547 /* Show the final common area */
1548 assert( nr==i );
1549 m = R[r+nr*3];
1550 if( m>nContext ) m = nContext;
1551 for(j=0; j<m; j++){
1552 sbsWriteLineno(&s, a+j, SBS_LNA);
1553 s.a[0].iStart = s.a[0].iEnd = -1;
1554 s.n = 0;
1555 sbsWriteText(&s, &A[a+j], SBS_TXTA);
1556 sbsWriteMarker(&s, " ", "");
1557 sbsWriteLineno(&s, b+j, SBS_LNB);
1558 sbsWriteText(&s, &B[b+j], SBS_TXTB);
1559 }
1560 }
1561
1562 if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){
1563 blob_append(pOut, "<table class=\"sbsdiffcols\"><tr>\n", -1);
1564 for(i=SBS_LNA; i<=SBS_TXTB; i++){
1565 sbsWriteColumn(pOut, s.apCols[i], i);
1566 blob_reset(s.apCols[i]);
1567 }
1568 blob_append(pOut, "</tr></table>\n", -1);
1569 }
1570 }
1571
1572 /*
1573 ** This is an abstract superclass for an object that accepts difference
1574 ** lines and formats them for display. Subclasses of this object format
1575 ** the diff output in different ways.
@@ -1588,10 +945,11 @@
1588 void (*xEnd)(DiffBuilder*);
1589 unsigned int lnLeft; /* Lines seen on the left (delete) side */
1590 unsigned int lnRight; /* Lines seen on the right (insert) side */
1591 unsigned int nPending; /* Number of pending lines */
1592 int eState; /* State of the output */
 
1593 Blob *pOut; /* Output blob */
1594 Blob aCol[5]; /* Holding blobs */
1595 };
1596
1597 /************************* DiffBuilderDebug ********************************/
@@ -2287,10 +1645,91 @@
2287 blob_init(&p->aCol[2], 0, 0);
2288 blob_init(&p->aCol[3], 0, 0);
2289 blob_init(&p->aCol[4], 0, 0);
2290 return p;
2291 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2292 /****************************************************************************/
2293
2294 /*
2295 ** Format a diff using a DiffBuilder object
2296 */
@@ -3015,16 +2454,17 @@
3015 blob_append_char(pOut, '\n');
3016 }else if( diffFlags & DIFF_TCL ){
3017 DiffBuilder *pBuilder = dftclNew(pOut);
3018 formatDiff(&c, pRe, diffFlags, pBuilder);
3019 }else if( diffFlags & DIFF_SIDEBYSIDE ){
 
3020 if( diffFlags & DIFF_HTML ){
3021 DiffBuilder *pBuilder = dfsplitNew(pOut);
3022 formatDiff(&c, pRe, diffFlags, pBuilder);
3023 }else{
3024 sbsDiff(&c, pOut, pRe, diffFlags);
3025 }
 
3026 }else if( diffFlags & DIFF_DEBUG ){
3027 DiffBuilder *pBuilder = dfdebugNew(pOut);
3028 formatDiff(&c, pRe, diffFlags, pBuilder);
3029 }else if( diffFlags & DIFF_HTML ){
3030 DiffBuilder *pBuilder = dfunifiedNew(pOut);
3031
--- src/diff.c
+++ src/diff.c
@@ -461,33 +461,12 @@
461 appendDiffLine(pOut, ' ', &A[a+j]);
462 }
463 }
464 }
465
 
 
 
 
466 #define SBS_CSN 8 /* Maximum number of change spans across a change region */
467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468 /*
469 ** A description of zero or more (up to SBS_CSN) areas of commonality
470 ** between two lines of text.
471 */
472 typedef struct ChangeSpan ChangeSpan;
@@ -500,158 +479,10 @@
479 int iLen2; /* Length of right change span in bytes */
480 int isMin; /* True if this span is known to have no useful subdivs */
481 } a[SBS_CSN]; /* Array of change spans, sorted order */
482 };
483
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484 /*
485 ** The two text segments zLeft and zRight are known to be different on
486 ** both ends, but they might have a common segment in the middle. If
487 ** they do not have a common segment, return 0. If they do have a large
488 ** common segment, return 1 and before doing so set:
@@ -746,232 +577,10 @@
577 b->isMin = 0;
578 }
579 return p->n;
580 }
581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582 /*
583 ** Given two lines of text, pFrom and pTo, compute a set of changes
584 ** between those two lines, for enhanced display purposes.
585 **
586 ** The result is written into the ChangeSpan object given by the
@@ -1199,11 +808,11 @@
808 int i, j, k; /* Loop counters */
809 int *a; /* One row of the Wagner matrix */
810 int *pToFree; /* Space that needs to be freed */
811 unsigned char *aM; /* Wagner result matrix */
812 int nMatch, iMatch; /* Number of matching lines and match score */
813 int mnLen; /* minInt(nLeft, nRight) */
814 int mxLen; /* MAX(nLeft, nRight) */
815 int aBuf[100]; /* Stack space for a[] if nRight not to big */
816
817 if( nLeft==0 ){
818 aM = fossil_malloc( nLeft + nRight + 2 );
@@ -1314,262 +923,10 @@
923 /* Return the result */
924 fossil_free(pToFree);
925 return aM;
926 }
927
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
928
929 /*
930 ** This is an abstract superclass for an object that accepts difference
931 ** lines and formats them for display. Subclasses of this object format
932 ** the diff output in different ways.
@@ -1588,10 +945,11 @@
945 void (*xEnd)(DiffBuilder*);
946 unsigned int lnLeft; /* Lines seen on the left (delete) side */
947 unsigned int lnRight; /* Lines seen on the right (insert) side */
948 unsigned int nPending; /* Number of pending lines */
949 int eState; /* State of the output */
950 int width; /* Display width */
951 Blob *pOut; /* Output blob */
952 Blob aCol[5]; /* Holding blobs */
953 };
954
955 /************************* DiffBuilderDebug ********************************/
@@ -2287,10 +1645,91 @@
1645 blob_init(&p->aCol[2], 0, 0);
1646 blob_init(&p->aCol[3], 0, 0);
1647 blob_init(&p->aCol[4], 0, 0);
1648 return p;
1649 }
1650
1651 /************************* DiffBuilderSbs ******************************/
1652 /* This formatter creates a side-by-side diff in text.
1653 */
1654 static void dfsbsSkip(DiffBuilder *p, unsigned int n, int isFinal){
1655 if( (p->lnLeft || p->lnRight) && !isFinal ){
1656 blob_appendf(p->pOut, "%.*c\n", p->width*2 + 16, '.');
1657 }
1658 p->lnLeft += n;
1659 p->lnRight += n;
1660 }
1661
1662 /*
1663 ** Append at least iMin characters (not bytes) and at most iMax characters
1664 ** from pX onto the into of p.
1665 **
1666 ** This comment contains multibyte unicode characters (ü, Æ, ð) in order
1667 ** to test the ability of the diff code to handle such characters.
1668 */
1669 static void sbs_append_chars(Blob *p, int iMin, int iMax, const DLine *pX){
1670 int i;
1671 const char *z = pX->z;
1672 for(i=0; i<iMax && i<pX->n; i++){
1673 char c = z[i];
1674 blob_append_char(p, c);
1675 if( (c&0xc0)==0x80 ){ iMin++; iMax++; }
1676 }
1677 while( i<iMin ){
1678 blob_append_char(p, ' ');
1679 i++;
1680 }
1681 }
1682
1683 static void dfsbsCommon(DiffBuilder *p, const DLine *pLine){
1684 p->lnLeft++;
1685 p->lnRight++;
1686 blob_appendf(p->pOut,"%6u ", p->lnLeft);
1687 sbs_append_chars(p->pOut, p->width, p->width, pLine);
1688 blob_appendf(p->pOut," %6u ", p->lnRight);
1689 sbs_append_chars(p->pOut, 0, p->width, pLine);
1690 blob_append_char(p->pOut, '\n');
1691 }
1692 static void dfsbsInsert(DiffBuilder *p, const DLine *pLine){
1693 p->lnRight++;
1694 blob_appendf(p->pOut,"%6s %*s > %6u ",
1695 "", p->width, "", p->lnRight);
1696 sbs_append_chars(p->pOut, 0, p->width, pLine);
1697 blob_append_char(p->pOut, '\n');
1698 }
1699 static void dfsbsDelete(DiffBuilder *p, const DLine *pLine){
1700 p->lnLeft++;
1701 blob_appendf(p->pOut,"%6u ", p->lnLeft);
1702 sbs_append_chars(p->pOut, p->width, p->width, pLine);
1703 blob_append(p->pOut," <\n", 3);
1704 }
1705 static void dfsbsEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
1706 p->lnLeft++;
1707 p->lnRight++;
1708 blob_appendf(p->pOut,"%6u ", p->lnLeft);
1709 sbs_append_chars(p->pOut, p->width, p->width, pX);
1710 blob_appendf(p->pOut, " | %6u ", p->lnRight);
1711 sbs_append_chars(p->pOut, 0, p->width, pY);
1712 blob_append_char(p->pOut, '\n');
1713 }
1714 static void dfsbsEnd(DiffBuilder *p){
1715 fossil_free(p);
1716 }
1717 static DiffBuilder *dfsbsNew(Blob *pOut, u64 diffFlags){
1718 DiffBuilder *p = fossil_malloc(sizeof(*p));
1719 p->xSkip = dfsbsSkip;
1720 p->xCommon = dfsbsCommon;
1721 p->xInsert = dfsbsInsert;
1722 p->xDelete = dfsbsDelete;
1723 p->xReplace = dfsbsEdit;
1724 p->xEdit = dfsbsEdit;
1725 p->xEnd = dfsbsEnd;
1726 p->lnLeft = p->lnRight = 0;
1727 p->width = diff_width(diffFlags);
1728 p->pOut = pOut;
1729 return p;
1730 }
1731 /****************************************************************************/
1732
1733 /*
1734 ** Format a diff using a DiffBuilder object
1735 */
@@ -3015,16 +2454,17 @@
2454 blob_append_char(pOut, '\n');
2455 }else if( diffFlags & DIFF_TCL ){
2456 DiffBuilder *pBuilder = dftclNew(pOut);
2457 formatDiff(&c, pRe, diffFlags, pBuilder);
2458 }else if( diffFlags & DIFF_SIDEBYSIDE ){
2459 DiffBuilder *pBuilder;
2460 if( diffFlags & DIFF_HTML ){
2461 pBuilder = dfsplitNew(pOut);
 
2462 }else{
2463 pBuilder = dfsbsNew(pOut, diffFlags);
2464 }
2465 formatDiff(&c, pRe, diffFlags, pBuilder);
2466 }else if( diffFlags & DIFF_DEBUG ){
2467 DiffBuilder *pBuilder = dfdebugNew(pOut);
2468 formatDiff(&c, pRe, diffFlags, pBuilder);
2469 }else if( diffFlags & DIFF_HTML ){
2470 DiffBuilder *pBuilder = dfunifiedNew(pOut);
2471

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button