Fossil SCM
Delete a bunch of obsolete side-by-side diff code.
Commit
9f3d89612afbb42bd37587e8e34e6201eb70e48a591057822c492ad8c45a8dab
Parent
1fb022ff98d56e6…
1 file changed
+87
-647
+87
-647
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -461,33 +461,12 @@ | ||
| 461 | 461 | appendDiffLine(pOut, ' ', &A[a+j]); |
| 462 | 462 | } |
| 463 | 463 | } |
| 464 | 464 | } |
| 465 | 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 | 466 | #define SBS_CSN 8 /* Maximum number of change spans across a change region */ |
| 471 | 467 | |
| 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 | 468 | /* |
| 490 | 469 | ** A description of zero or more (up to SBS_CSN) areas of commonality |
| 491 | 470 | ** between two lines of text. |
| 492 | 471 | */ |
| 493 | 472 | typedef struct ChangeSpan ChangeSpan; |
| @@ -500,158 +479,10 @@ | ||
| 500 | 479 | int iLen2; /* Length of right change span in bytes */ |
| 501 | 480 | int isMin; /* True if this span is known to have no useful subdivs */ |
| 502 | 481 | } a[SBS_CSN]; /* Array of change spans, sorted order */ |
| 503 | 482 | }; |
| 504 | 483 | |
| 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, "<", 4); | |
| 585 | - }else if( c=='&' && p->escHtml ){ | |
| 586 | - blob_append(pCol, "&", 5); | |
| 587 | - }else if( c=='>' && p->escHtml ){ | |
| 588 | - blob_append(pCol, ">", 4); | |
| 589 | - }else if( c=='"' && p->escHtml ){ | |
| 590 | - blob_append(pCol, """, 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 | 484 | /* |
| 654 | 485 | ** The two text segments zLeft and zRight are known to be different on |
| 655 | 486 | ** both ends, but they might have a common segment in the middle. If |
| 656 | 487 | ** they do not have a common segment, return 0. If they do have a large |
| 657 | 488 | ** common segment, return 1 and before doing so set: |
| @@ -746,232 +577,10 @@ | ||
| 746 | 577 | b->isMin = 0; |
| 747 | 578 | } |
| 748 | 579 | return p->n; |
| 749 | 580 | } |
| 750 | 581 | |
| 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 | 582 | /* |
| 974 | 583 | ** Given two lines of text, pFrom and pTo, compute a set of changes |
| 975 | 584 | ** between those two lines, for enhanced display purposes. |
| 976 | 585 | ** |
| 977 | 586 | ** The result is written into the ChangeSpan object given by the |
| @@ -1199,11 +808,11 @@ | ||
| 1199 | 808 | int i, j, k; /* Loop counters */ |
| 1200 | 809 | int *a; /* One row of the Wagner matrix */ |
| 1201 | 810 | int *pToFree; /* Space that needs to be freed */ |
| 1202 | 811 | unsigned char *aM; /* Wagner result matrix */ |
| 1203 | 812 | int nMatch, iMatch; /* Number of matching lines and match score */ |
| 1204 | - int mnLen; /* MIN(nLeft, nRight) */ | |
| 813 | + int mnLen; /* minInt(nLeft, nRight) */ | |
| 1205 | 814 | int mxLen; /* MAX(nLeft, nRight) */ |
| 1206 | 815 | int aBuf[100]; /* Stack space for a[] if nRight not to big */ |
| 1207 | 816 | |
| 1208 | 817 | if( nLeft==0 ){ |
| 1209 | 818 | aM = fossil_malloc( nLeft + nRight + 2 ); |
| @@ -1314,262 +923,10 @@ | ||
| 1314 | 923 | /* Return the result */ |
| 1315 | 924 | fossil_free(pToFree); |
| 1316 | 925 | return aM; |
| 1317 | 926 | } |
| 1318 | 927 | |
| 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, " <", "<"); | |
| 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, " > ", ">"); | |
| 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 | 928 | |
| 1572 | 929 | /* |
| 1573 | 930 | ** This is an abstract superclass for an object that accepts difference |
| 1574 | 931 | ** lines and formats them for display. Subclasses of this object format |
| 1575 | 932 | ** the diff output in different ways. |
| @@ -1588,10 +945,11 @@ | ||
| 1588 | 945 | void (*xEnd)(DiffBuilder*); |
| 1589 | 946 | unsigned int lnLeft; /* Lines seen on the left (delete) side */ |
| 1590 | 947 | unsigned int lnRight; /* Lines seen on the right (insert) side */ |
| 1591 | 948 | unsigned int nPending; /* Number of pending lines */ |
| 1592 | 949 | int eState; /* State of the output */ |
| 950 | + int width; /* Display width */ | |
| 1593 | 951 | Blob *pOut; /* Output blob */ |
| 1594 | 952 | Blob aCol[5]; /* Holding blobs */ |
| 1595 | 953 | }; |
| 1596 | 954 | |
| 1597 | 955 | /************************* DiffBuilderDebug ********************************/ |
| @@ -2287,10 +1645,91 @@ | ||
| 2287 | 1645 | blob_init(&p->aCol[2], 0, 0); |
| 2288 | 1646 | blob_init(&p->aCol[3], 0, 0); |
| 2289 | 1647 | blob_init(&p->aCol[4], 0, 0); |
| 2290 | 1648 | return p; |
| 2291 | 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 | +} | |
| 2292 | 1731 | /****************************************************************************/ |
| 2293 | 1732 | |
| 2294 | 1733 | /* |
| 2295 | 1734 | ** Format a diff using a DiffBuilder object |
| 2296 | 1735 | */ |
| @@ -3015,16 +2454,17 @@ | ||
| 3015 | 2454 | blob_append_char(pOut, '\n'); |
| 3016 | 2455 | }else if( diffFlags & DIFF_TCL ){ |
| 3017 | 2456 | DiffBuilder *pBuilder = dftclNew(pOut); |
| 3018 | 2457 | formatDiff(&c, pRe, diffFlags, pBuilder); |
| 3019 | 2458 | }else if( diffFlags & DIFF_SIDEBYSIDE ){ |
| 2459 | + DiffBuilder *pBuilder; | |
| 3020 | 2460 | if( diffFlags & DIFF_HTML ){ |
| 3021 | - DiffBuilder *pBuilder = dfsplitNew(pOut); | |
| 3022 | - formatDiff(&c, pRe, diffFlags, pBuilder); | |
| 2461 | + pBuilder = dfsplitNew(pOut); | |
| 3023 | 2462 | }else{ |
| 3024 | - sbsDiff(&c, pOut, pRe, diffFlags); | |
| 2463 | + pBuilder = dfsbsNew(pOut, diffFlags); | |
| 3025 | 2464 | } |
| 2465 | + formatDiff(&c, pRe, diffFlags, pBuilder); | |
| 3026 | 2466 | }else if( diffFlags & DIFF_DEBUG ){ |
| 3027 | 2467 | DiffBuilder *pBuilder = dfdebugNew(pOut); |
| 3028 | 2468 | formatDiff(&c, pRe, diffFlags, pBuilder); |
| 3029 | 2469 | }else if( diffFlags & DIFF_HTML ){ |
| 3030 | 2470 | DiffBuilder *pBuilder = dfunifiedNew(pOut); |
| 3031 | 2471 |
| --- 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, "<", 4); |
| 585 | }else if( c=='&' && p->escHtml ){ |
| 586 | blob_append(pCol, "&", 5); |
| 587 | }else if( c=='>' && p->escHtml ){ |
| 588 | blob_append(pCol, ">", 4); |
| 589 | }else if( c=='"' && p->escHtml ){ |
| 590 | blob_append(pCol, """, 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, " <", "<"); |
| 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, " > ", ">"); |
| 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 |