Fossil SCM

In the "diff --debug" output show the regions of change.

drh 2021-08-30 13:55 diff-color-enhancements
Commit 4b8f203f5dece0e7452569869d2fa6f8eb8eba86f8747cfaaa52281fbb3a2736
1 file changed +162 -7
+162 -7
--- src/diff.c
+++ src/diff.c
@@ -744,11 +744,11 @@
744744
}
745745
return lenBest>0;
746746
}
747747
748748
/*
749
-** Find the smallest spans that different between two text strings that
749
+** Find the smallest spans that are different between two text strings that
750750
** are known to be different on both ends.
751751
*/
752752
static int textChangeSpans(
753753
const char *zLeft, int nA, /* String on the left */
754754
const char *zRight, int nB, /* String on the right */
@@ -1018,10 +1018,140 @@
10181018
p->a[0].iEnd = nRight - nSuffix;
10191019
p->a[0].zTag = zClassChng;
10201020
p->n = 1;
10211021
sbsWriteText(p, pRight, SBS_TXTB);
10221022
}
1023
+
1024
+/*
1025
+** Given two lines of text, pFrom and pTo, compute a set of changes
1026
+** between those two lines, for enhanced display purposes.
1027
+**
1028
+** The result is written into the ChangeSpan object given by the
1029
+** third parameter.
1030
+*/
1031
+static void oneLineChange(
1032
+ const DLine *pLeft, /* Left line of the change */
1033
+ const DLine *pRight, /* Right line of the change */
1034
+ ChangeSpan *p /* OUTPUT: Write the results here */
1035
+){
1036
+ int nLeft; /* Length of left line in bytes */
1037
+ int nRight; /* Length of right line in bytes */
1038
+ int nShort; /* Shortest of left and right */
1039
+ int nPrefix; /* Length of common prefix */
1040
+ int nSuffix; /* Length of common suffix */
1041
+ int nCommon; /* Total byte length of suffix and prefix */
1042
+ const char *zLeft; /* Text of the left line */
1043
+ const char *zRight; /* Text of the right line */
1044
+ int nLeftDiff; /* nLeft - nPrefix - nSuffix */
1045
+ int nRightDiff; /* nRight - nPrefix - nSuffix */
1046
+
1047
+ nLeft = pLeft->n;
1048
+ zLeft = pLeft->z;
1049
+ nRight = pRight->n;
1050
+ zRight = pRight->z;
1051
+ nShort = nLeft<nRight ? nLeft : nRight;
1052
+
1053
+ nPrefix = 0;
1054
+ while( nPrefix<nShort && zLeft[nPrefix]==zRight[nPrefix] ){
1055
+ nPrefix++;
1056
+ }
1057
+ if( nPrefix<nShort ){
1058
+ while( nPrefix>0 && (zLeft[nPrefix]&0xc0)==0x80 ) nPrefix--;
1059
+ }
1060
+ nSuffix = 0;
1061
+ if( nPrefix<nShort ){
1062
+ while( nSuffix<nShort && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){
1063
+ nSuffix++;
1064
+ }
1065
+ if( nSuffix<nShort ){
1066
+ while( nSuffix>0 && (zLeft[nLeft-nSuffix]&0xc0)==0x80 ) nSuffix--;
1067
+ }
1068
+ if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0;
1069
+ }
1070
+ nCommon = nPrefix + nSuffix;
1071
+
1072
+ /* If the prefix and suffix overlap, that means that we are dealing with
1073
+ ** a pure insertion or deletion of text that can have multiple alignments.
1074
+ ** Try to find an alignment to begins and ends on whitespace, or on
1075
+ ** punctuation, rather than in the middle of a name or number.
1076
+ */
1077
+ if( nCommon > nShort ){
1078
+ int iBest = -1;
1079
+ int iBestVal = -1;
1080
+ int i;
1081
+ int nLong = nLeft<nRight ? nRight : nLeft;
1082
+ int nGap = nLong - nShort;
1083
+ for(i=nShort-nSuffix; i<=nPrefix; i++){
1084
+ int iVal = 0;
1085
+ char c = zLeft[i];
1086
+ if( fossil_isspace(c) ){
1087
+ iVal += 5;
1088
+ }else if( !fossil_isalnum(c) ){
1089
+ iVal += 2;
1090
+ }
1091
+ c = zLeft[i+nGap-1];
1092
+ if( fossil_isspace(c) ){
1093
+ iVal += 5;
1094
+ }else if( !fossil_isalnum(c) ){
1095
+ iVal += 2;
1096
+ }
1097
+ if( iVal>iBestVal ){
1098
+ iBestVal = iVal;
1099
+ iBest = i;
1100
+ }
1101
+ }
1102
+ nPrefix = iBest;
1103
+ nSuffix = nShort - nPrefix;
1104
+ nCommon = nPrefix + nSuffix;
1105
+ }
1106
+
1107
+ /* A single chunk of text inserted */
1108
+ if( nCommon==nLeft ){
1109
+ p->n = 1;
1110
+ p->a[0].iStart1 = 0;
1111
+ p->a[0].iLen1 = 0;
1112
+ p->a[0].iStart2 = nPrefix;
1113
+ p->a[0].iLen2 = nRight - nCommon;
1114
+ return;
1115
+ }
1116
+
1117
+ /* A single chunk of text deleted */
1118
+ if( nCommon==nRight ){
1119
+ p->n = 1;
1120
+ p->a[0].iStart1 = nPrefix;
1121
+ p->a[0].iLen1 = nLeft - nCommon;
1122
+ p->a[0].iStart2 = 0;
1123
+ p->a[0].iLen2 = 0;
1124
+ return;
1125
+ }
1126
+
1127
+ /* At this point we know that there is a chunk of text that has
1128
+ ** changed between the left and the right. Check to see if there
1129
+ ** is a large unchanged section in the middle of that changed block.
1130
+ */
1131
+ nLeftDiff = nLeft - nCommon;
1132
+ nRightDiff = nRight - nCommon;
1133
+ if( nLeftDiff >= 4
1134
+ && nRightDiff >= 4
1135
+ && textChangeSpans(&zLeft[nPrefix], nLeftDiff,
1136
+ &zRight[nPrefix], nRightDiff, p)>1
1137
+ ){
1138
+ int i;
1139
+ for(i=0; i<p->n; i++){
1140
+ p->a[i].iStart1 += nPrefix;
1141
+ p->a[i].iStart2 += nPrefix;
1142
+ }
1143
+ return;
1144
+ }
1145
+
1146
+ /* If all else fails, show a single big change between left and right */
1147
+ p->n = 1;
1148
+ p->a[0].iStart1 = nPrefix;
1149
+ p->a[0].iLen1 = nLeft - nCommon;
1150
+ p->a[0].iStart2 = nPrefix;
1151
+ p->a[0].iLen2 = nRight - nCommon;
1152
+}
10231153
10241154
/*
10251155
** Minimum of two values
10261156
*/
10271157
static int minInt(int a, int b){ return a<b ? a : b; }
@@ -1500,14 +1630,13 @@
15001630
void (*xCommon)(DiffBuilder*,const DLine*);
15011631
void (*xInsert)(DiffBuilder*,const DLine*);
15021632
void (*xDelete)(DiffBuilder*,const DLine*);
15031633
void (*xEdit)(DiffBuilder*,const DLine*,const DLine*);
15041634
void (*xEnd)(DiffBuilder*);
1505
- unsigned int lnLeft; /* Lines seen on the left (delete) side */
1506
- unsigned int lnRight; /* Lines seen on the right (insert) side */
1507
- Blob *pOut; /* Output blob */
1508
- /* Subclass add additional fields */
1635
+ unsigned int lnLeft; /* Lines seen on the left (delete) side */
1636
+ unsigned int lnRight; /* Lines seen on the right (insert) side */
1637
+ Blob *pOut; /* Output blob */
15091638
};
15101639
15111640
/************************* DiffBuilderDebug ********************************/
15121641
static void dfdebugSkip(DiffBuilder *p, unsigned int n){
15131642
blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)\n",
@@ -1530,14 +1659,40 @@
15301659
p->lnLeft++;
15311660
blob_appendf(p->pOut, "LEFT %8u %.*s\n",
15321661
p->lnLeft, (int)pLine->n, pLine->z);
15331662
}
15341663
static void dfdebugEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
1664
+ int i, j;
1665
+ int x;
1666
+ ChangeSpan span;
15351667
p->lnLeft++;
15361668
p->lnRight++;
1537
- blob_appendf(p->pOut, "EDIT %8u %.*s\n %8u %.*s\n",
1538
- p->lnLeft, (int)pX->n, pX->z, p->lnRight, (int)pY->n, pY->z);
1669
+ blob_appendf(p->pOut, "EDIT %8u %.*s\n",
1670
+ p->lnLeft, (int)pX->n, pX->z);
1671
+ oneLineChange(pX, pY, &span);
1672
+ for(i=x=0; i<span.n; i++){
1673
+ int ofst = span.a[i].iStart1;
1674
+ int len = span.a[i].iLen1;
1675
+ if( len ){
1676
+ blob_appendf(p->pOut, "%*s", ofst+25 - x, "");
1677
+ for(j=0; j<len; j++) blob_append_char(p->pOut, '^');
1678
+ x = ofst+len+25;
1679
+ }
1680
+ }
1681
+ if( x ) blob_append_char(p->pOut, '\n');
1682
+ blob_appendf(p->pOut, " %8u %.*s\n",
1683
+ p->lnRight, (int)pY->n, pY->z);
1684
+ for(i=x=0; i<span.n; i++){
1685
+ int ofst = span.a[i].iStart2;
1686
+ int len = span.a[i].iLen2;
1687
+ if( len ){
1688
+ blob_appendf(p->pOut, "%*s", ofst+25 - x, "");
1689
+ for(j=0; j<len; j++) blob_append_char(p->pOut, '^');
1690
+ x = ofst+len+25;
1691
+ }
1692
+ }
1693
+ if( x ) blob_append_char(p->pOut, '\n');
15391694
}
15401695
static void dfdebugEnd(DiffBuilder *p){
15411696
blob_appendf(p->pOut, "END with %u lines left and %u lines right\n",
15421697
p->lnLeft, p->lnRight);
15431698
fossil_free(p);
15441699
--- src/diff.c
+++ src/diff.c
@@ -744,11 +744,11 @@
744 }
745 return lenBest>0;
746 }
747
748 /*
749 ** Find the smallest spans that different between two text strings that
750 ** are known to be different on both ends.
751 */
752 static int textChangeSpans(
753 const char *zLeft, int nA, /* String on the left */
754 const char *zRight, int nB, /* String on the right */
@@ -1018,10 +1018,140 @@
1018 p->a[0].iEnd = nRight - nSuffix;
1019 p->a[0].zTag = zClassChng;
1020 p->n = 1;
1021 sbsWriteText(p, pRight, SBS_TXTB);
1022 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1023
1024 /*
1025 ** Minimum of two values
1026 */
1027 static int minInt(int a, int b){ return a<b ? a : b; }
@@ -1500,14 +1630,13 @@
1500 void (*xCommon)(DiffBuilder*,const DLine*);
1501 void (*xInsert)(DiffBuilder*,const DLine*);
1502 void (*xDelete)(DiffBuilder*,const DLine*);
1503 void (*xEdit)(DiffBuilder*,const DLine*,const DLine*);
1504 void (*xEnd)(DiffBuilder*);
1505 unsigned int lnLeft; /* Lines seen on the left (delete) side */
1506 unsigned int lnRight; /* Lines seen on the right (insert) side */
1507 Blob *pOut; /* Output blob */
1508 /* Subclass add additional fields */
1509 };
1510
1511 /************************* DiffBuilderDebug ********************************/
1512 static void dfdebugSkip(DiffBuilder *p, unsigned int n){
1513 blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)\n",
@@ -1530,14 +1659,40 @@
1530 p->lnLeft++;
1531 blob_appendf(p->pOut, "LEFT %8u %.*s\n",
1532 p->lnLeft, (int)pLine->n, pLine->z);
1533 }
1534 static void dfdebugEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
 
 
 
1535 p->lnLeft++;
1536 p->lnRight++;
1537 blob_appendf(p->pOut, "EDIT %8u %.*s\n %8u %.*s\n",
1538 p->lnLeft, (int)pX->n, pX->z, p->lnRight, (int)pY->n, pY->z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1539 }
1540 static void dfdebugEnd(DiffBuilder *p){
1541 blob_appendf(p->pOut, "END with %u lines left and %u lines right\n",
1542 p->lnLeft, p->lnRight);
1543 fossil_free(p);
1544
--- src/diff.c
+++ src/diff.c
@@ -744,11 +744,11 @@
744 }
745 return lenBest>0;
746 }
747
748 /*
749 ** Find the smallest spans that are different between two text strings that
750 ** are known to be different on both ends.
751 */
752 static int textChangeSpans(
753 const char *zLeft, int nA, /* String on the left */
754 const char *zRight, int nB, /* String on the right */
@@ -1018,10 +1018,140 @@
1018 p->a[0].iEnd = nRight - nSuffix;
1019 p->a[0].zTag = zClassChng;
1020 p->n = 1;
1021 sbsWriteText(p, pRight, SBS_TXTB);
1022 }
1023
1024 /*
1025 ** Given two lines of text, pFrom and pTo, compute a set of changes
1026 ** between those two lines, for enhanced display purposes.
1027 **
1028 ** The result is written into the ChangeSpan object given by the
1029 ** third parameter.
1030 */
1031 static void oneLineChange(
1032 const DLine *pLeft, /* Left line of the change */
1033 const DLine *pRight, /* Right line of the change */
1034 ChangeSpan *p /* OUTPUT: Write the results here */
1035 ){
1036 int nLeft; /* Length of left line in bytes */
1037 int nRight; /* Length of right line in bytes */
1038 int nShort; /* Shortest of left and right */
1039 int nPrefix; /* Length of common prefix */
1040 int nSuffix; /* Length of common suffix */
1041 int nCommon; /* Total byte length of suffix and prefix */
1042 const char *zLeft; /* Text of the left line */
1043 const char *zRight; /* Text of the right line */
1044 int nLeftDiff; /* nLeft - nPrefix - nSuffix */
1045 int nRightDiff; /* nRight - nPrefix - nSuffix */
1046
1047 nLeft = pLeft->n;
1048 zLeft = pLeft->z;
1049 nRight = pRight->n;
1050 zRight = pRight->z;
1051 nShort = nLeft<nRight ? nLeft : nRight;
1052
1053 nPrefix = 0;
1054 while( nPrefix<nShort && zLeft[nPrefix]==zRight[nPrefix] ){
1055 nPrefix++;
1056 }
1057 if( nPrefix<nShort ){
1058 while( nPrefix>0 && (zLeft[nPrefix]&0xc0)==0x80 ) nPrefix--;
1059 }
1060 nSuffix = 0;
1061 if( nPrefix<nShort ){
1062 while( nSuffix<nShort && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){
1063 nSuffix++;
1064 }
1065 if( nSuffix<nShort ){
1066 while( nSuffix>0 && (zLeft[nLeft-nSuffix]&0xc0)==0x80 ) nSuffix--;
1067 }
1068 if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0;
1069 }
1070 nCommon = nPrefix + nSuffix;
1071
1072 /* If the prefix and suffix overlap, that means that we are dealing with
1073 ** a pure insertion or deletion of text that can have multiple alignments.
1074 ** Try to find an alignment to begins and ends on whitespace, or on
1075 ** punctuation, rather than in the middle of a name or number.
1076 */
1077 if( nCommon > nShort ){
1078 int iBest = -1;
1079 int iBestVal = -1;
1080 int i;
1081 int nLong = nLeft<nRight ? nRight : nLeft;
1082 int nGap = nLong - nShort;
1083 for(i=nShort-nSuffix; i<=nPrefix; i++){
1084 int iVal = 0;
1085 char c = zLeft[i];
1086 if( fossil_isspace(c) ){
1087 iVal += 5;
1088 }else if( !fossil_isalnum(c) ){
1089 iVal += 2;
1090 }
1091 c = zLeft[i+nGap-1];
1092 if( fossil_isspace(c) ){
1093 iVal += 5;
1094 }else if( !fossil_isalnum(c) ){
1095 iVal += 2;
1096 }
1097 if( iVal>iBestVal ){
1098 iBestVal = iVal;
1099 iBest = i;
1100 }
1101 }
1102 nPrefix = iBest;
1103 nSuffix = nShort - nPrefix;
1104 nCommon = nPrefix + nSuffix;
1105 }
1106
1107 /* A single chunk of text inserted */
1108 if( nCommon==nLeft ){
1109 p->n = 1;
1110 p->a[0].iStart1 = 0;
1111 p->a[0].iLen1 = 0;
1112 p->a[0].iStart2 = nPrefix;
1113 p->a[0].iLen2 = nRight - nCommon;
1114 return;
1115 }
1116
1117 /* A single chunk of text deleted */
1118 if( nCommon==nRight ){
1119 p->n = 1;
1120 p->a[0].iStart1 = nPrefix;
1121 p->a[0].iLen1 = nLeft - nCommon;
1122 p->a[0].iStart2 = 0;
1123 p->a[0].iLen2 = 0;
1124 return;
1125 }
1126
1127 /* At this point we know that there is a chunk of text that has
1128 ** changed between the left and the right. Check to see if there
1129 ** is a large unchanged section in the middle of that changed block.
1130 */
1131 nLeftDiff = nLeft - nCommon;
1132 nRightDiff = nRight - nCommon;
1133 if( nLeftDiff >= 4
1134 && nRightDiff >= 4
1135 && textChangeSpans(&zLeft[nPrefix], nLeftDiff,
1136 &zRight[nPrefix], nRightDiff, p)>1
1137 ){
1138 int i;
1139 for(i=0; i<p->n; i++){
1140 p->a[i].iStart1 += nPrefix;
1141 p->a[i].iStart2 += nPrefix;
1142 }
1143 return;
1144 }
1145
1146 /* If all else fails, show a single big change between left and right */
1147 p->n = 1;
1148 p->a[0].iStart1 = nPrefix;
1149 p->a[0].iLen1 = nLeft - nCommon;
1150 p->a[0].iStart2 = nPrefix;
1151 p->a[0].iLen2 = nRight - nCommon;
1152 }
1153
1154 /*
1155 ** Minimum of two values
1156 */
1157 static int minInt(int a, int b){ return a<b ? a : b; }
@@ -1500,14 +1630,13 @@
1630 void (*xCommon)(DiffBuilder*,const DLine*);
1631 void (*xInsert)(DiffBuilder*,const DLine*);
1632 void (*xDelete)(DiffBuilder*,const DLine*);
1633 void (*xEdit)(DiffBuilder*,const DLine*,const DLine*);
1634 void (*xEnd)(DiffBuilder*);
1635 unsigned int lnLeft; /* Lines seen on the left (delete) side */
1636 unsigned int lnRight; /* Lines seen on the right (insert) side */
1637 Blob *pOut; /* Output blob */
 
1638 };
1639
1640 /************************* DiffBuilderDebug ********************************/
1641 static void dfdebugSkip(DiffBuilder *p, unsigned int n){
1642 blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)\n",
@@ -1530,14 +1659,40 @@
1659 p->lnLeft++;
1660 blob_appendf(p->pOut, "LEFT %8u %.*s\n",
1661 p->lnLeft, (int)pLine->n, pLine->z);
1662 }
1663 static void dfdebugEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
1664 int i, j;
1665 int x;
1666 ChangeSpan span;
1667 p->lnLeft++;
1668 p->lnRight++;
1669 blob_appendf(p->pOut, "EDIT %8u %.*s\n",
1670 p->lnLeft, (int)pX->n, pX->z);
1671 oneLineChange(pX, pY, &span);
1672 for(i=x=0; i<span.n; i++){
1673 int ofst = span.a[i].iStart1;
1674 int len = span.a[i].iLen1;
1675 if( len ){
1676 blob_appendf(p->pOut, "%*s", ofst+25 - x, "");
1677 for(j=0; j<len; j++) blob_append_char(p->pOut, '^');
1678 x = ofst+len+25;
1679 }
1680 }
1681 if( x ) blob_append_char(p->pOut, '\n');
1682 blob_appendf(p->pOut, " %8u %.*s\n",
1683 p->lnRight, (int)pY->n, pY->z);
1684 for(i=x=0; i<span.n; i++){
1685 int ofst = span.a[i].iStart2;
1686 int len = span.a[i].iLen2;
1687 if( len ){
1688 blob_appendf(p->pOut, "%*s", ofst+25 - x, "");
1689 for(j=0; j<len; j++) blob_append_char(p->pOut, '^');
1690 x = ofst+len+25;
1691 }
1692 }
1693 if( x ) blob_append_char(p->pOut, '\n');
1694 }
1695 static void dfdebugEnd(DiffBuilder *p){
1696 blob_appendf(p->pOut, "END with %u lines left and %u lines right\n",
1697 p->lnLeft, p->lnRight);
1698 fossil_free(p);
1699

Keyboard Shortcuts

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