Fossil SCM

Improved alignment of partial line diffs. More work to be done in this area. Also fix a problem in diff.js that was causing problems when comparing two empty files.

drh 2021-09-03 22:49 diff-color-enhancements
Commit 24d28cb7c980001899e67604a8e049867fcc00f212ed1c46484e7fdf7846089f
2 files changed +75 -3 +2 -1
+75 -3
--- src/diff.c
+++ src/diff.c
@@ -458,11 +458,11 @@
458458
}
459459
460460
#define MX_CSN 8 /* Maximum number of change spans across a change region */
461461
462462
/*
463
-** A description of zero or more (up to SBS_CSN) areas of commonality
463
+** A description of zero or more (up to MX_CSN) areas of difference
464464
** between two lines of text.
465465
*/
466466
typedef struct LineChange LineChange;
467467
struct LineChange {
468468
int n; /* Number of change spans */
@@ -532,11 +532,11 @@
532532
p->a[0].iStart1 = 0;
533533
p->a[0].iLen1 = nA;
534534
p->a[0].iStart2 = 0;
535535
p->a[0].iLen2 = nB;
536536
p->a[0].isMin = 0;
537
- while( p->n<MX_CSN ){
537
+ while( p->n<MX_CSN-1 ){
538538
int mxi = -1;
539539
int mxLen = -1;
540540
int x, i;
541541
int aLCS[4];
542542
struct Span *a, *b;
@@ -570,10 +570,78 @@
570570
a->iLen2 = aLCS[2];
571571
b->isMin = 0;
572572
}
573573
return p->n;
574574
}
575
+
576
+/*
577
+** Return true if the string starts with n spaces
578
+*/
579
+static int allSpaces(const char *z, int n){
580
+ int i;
581
+ for(i=0; i<n && fossil_isspace(z[i]); i++){}
582
+ return i==n;
583
+}
584
+
585
+/*
586
+** Try to improve the human-readability of the LineChange p.
587
+**
588
+** (1) If the first change span shows a change of indentation, try to
589
+** move that indentation change to the left margin.
590
+**
591
+** (2) Try to shift changes so that they begin or end with a space.
592
+*/
593
+static void improveReadability(
594
+ const char *zA, /* Left line of the change */
595
+ const char *zB, /* Right line of the change */
596
+ LineChange *p /* The LineChange to be adjusted */
597
+){
598
+ int j, n, len;
599
+ if( p->n<1 ) return;
600
+
601
+ /* (1) Attempt to move indentation changes to the left margin */
602
+ if( p->a[0].iLen1==0
603
+ && (len = p->a[0].iLen2)>0
604
+ && (j = p->a[0].iStart2)>0
605
+ && zB[0]==zB[j]
606
+ && allSpaces(zB, j)
607
+ ){
608
+ for(n=1; n<len && n<j && zB[j]==zB[j+n]; n++){}
609
+ if( n<len ){
610
+ memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n);
611
+ p->n++;
612
+ p->a[0] = p->a[1];
613
+ p->a[1].iStart2 += n;
614
+ p->a[1].iLen2 -= n;
615
+ p->a[0].iLen2 = n;
616
+ }
617
+ p->a[0].iStart1 = 0;
618
+ p->a[0].iStart2 = 0;
619
+ }else
620
+ if( p->a[0].iLen2==0
621
+ && (len = p->a[0].iLen1)>0
622
+ && (j = p->a[0].iStart1)>0
623
+ && zA[0]==zA[j]
624
+ && allSpaces(zA, j)
625
+ ){
626
+ for(n=1; n<len && n<j && zA[j]==zA[j+n]; n++){}
627
+ if( n<len ){
628
+ memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n);
629
+ p->n++;
630
+ p->a[0] = p->a[1];
631
+ p->a[1].iStart1 += n;
632
+ p->a[1].iLen1 -= n;
633
+ p->a[0].iLen1 = n;
634
+ }
635
+ p->a[0].iStart1 = 0;
636
+ p->a[0].iStart2 = 0;
637
+ }
638
+
639
+ /* (2) Try to shift changes so that they begin or end with a
640
+ ** space. (TBD) */
641
+}
642
+
575643
576644
/*
577645
** Given two lines of text, pFrom and pTo, compute a set of changes
578646
** between those two lines, for enhanced display purposes.
579647
**
@@ -662,10 +730,11 @@
662730
p->n = 1;
663731
p->a[0].iStart1 = nPrefix;
664732
p->a[0].iLen1 = 0;
665733
p->a[0].iStart2 = nPrefix;
666734
p->a[0].iLen2 = nRight - nCommon;
735
+ improveReadability(zLeft, zRight, p);
667736
return;
668737
}
669738
670739
/* A single chunk of text deleted */
671740
if( nCommon==nRight ){
@@ -672,10 +741,11 @@
672741
p->n = 1;
673742
p->a[0].iStart1 = nPrefix;
674743
p->a[0].iLen1 = nLeft - nCommon;
675744
p->a[0].iStart2 = nPrefix;
676745
p->a[0].iLen2 = 0;
746
+ improveReadability(zLeft, zRight, p);
677747
return;
678748
}
679749
680750
/* At this point we know that there is a chunk of text that has
681751
** changed between the left and the right. Check to see if there
@@ -691,19 +761,21 @@
691761
int i;
692762
for(i=0; i<p->n; i++){
693763
p->a[i].iStart1 += nPrefix;
694764
p->a[i].iStart2 += nPrefix;
695765
}
766
+ improveReadability(zLeft, zRight, p);
696767
return;
697768
}
698769
699770
/* If all else fails, show a single big change between left and right */
700771
p->n = 1;
701772
p->a[0].iStart1 = nPrefix;
702773
p->a[0].iLen1 = nLeft - nCommon;
703774
p->a[0].iStart2 = nPrefix;
704775
p->a[0].iLen2 = nRight - nCommon;
776
+ improveReadability(zLeft, zRight, p);
705777
}
706778
707779
/*
708780
** COMMAND: test-line-diff
709781
** Usage: %fossil% test-line-diff STRING1 STRING2
@@ -1625,11 +1697,11 @@
16251697
blob_append_char(&p->aCol[3], '\n');
16261698
}
16271699
static void dfsplitEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
16281700
int i;
16291701
int x;
1630
- LineChange chng;
1702
+ LineChange chng;
16311703
oneLineChange(pX, pY, &chng);
16321704
dfsplitStartRow(p);
16331705
dfsplitChangeState(p, 3);
16341706
p->lnLeft++;
16351707
p->lnRight++;
16361708
--- src/diff.c
+++ src/diff.c
@@ -458,11 +458,11 @@
458 }
459
460 #define MX_CSN 8 /* Maximum number of change spans across a change region */
461
462 /*
463 ** A description of zero or more (up to SBS_CSN) areas of commonality
464 ** between two lines of text.
465 */
466 typedef struct LineChange LineChange;
467 struct LineChange {
468 int n; /* Number of change spans */
@@ -532,11 +532,11 @@
532 p->a[0].iStart1 = 0;
533 p->a[0].iLen1 = nA;
534 p->a[0].iStart2 = 0;
535 p->a[0].iLen2 = nB;
536 p->a[0].isMin = 0;
537 while( p->n<MX_CSN ){
538 int mxi = -1;
539 int mxLen = -1;
540 int x, i;
541 int aLCS[4];
542 struct Span *a, *b;
@@ -570,10 +570,78 @@
570 a->iLen2 = aLCS[2];
571 b->isMin = 0;
572 }
573 return p->n;
574 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
576 /*
577 ** Given two lines of text, pFrom and pTo, compute a set of changes
578 ** between those two lines, for enhanced display purposes.
579 **
@@ -662,10 +730,11 @@
662 p->n = 1;
663 p->a[0].iStart1 = nPrefix;
664 p->a[0].iLen1 = 0;
665 p->a[0].iStart2 = nPrefix;
666 p->a[0].iLen2 = nRight - nCommon;
 
667 return;
668 }
669
670 /* A single chunk of text deleted */
671 if( nCommon==nRight ){
@@ -672,10 +741,11 @@
672 p->n = 1;
673 p->a[0].iStart1 = nPrefix;
674 p->a[0].iLen1 = nLeft - nCommon;
675 p->a[0].iStart2 = nPrefix;
676 p->a[0].iLen2 = 0;
 
677 return;
678 }
679
680 /* At this point we know that there is a chunk of text that has
681 ** changed between the left and the right. Check to see if there
@@ -691,19 +761,21 @@
691 int i;
692 for(i=0; i<p->n; i++){
693 p->a[i].iStart1 += nPrefix;
694 p->a[i].iStart2 += nPrefix;
695 }
 
696 return;
697 }
698
699 /* If all else fails, show a single big change between left and right */
700 p->n = 1;
701 p->a[0].iStart1 = nPrefix;
702 p->a[0].iLen1 = nLeft - nCommon;
703 p->a[0].iStart2 = nPrefix;
704 p->a[0].iLen2 = nRight - nCommon;
 
705 }
706
707 /*
708 ** COMMAND: test-line-diff
709 ** Usage: %fossil% test-line-diff STRING1 STRING2
@@ -1625,11 +1697,11 @@
1625 blob_append_char(&p->aCol[3], '\n');
1626 }
1627 static void dfsplitEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
1628 int i;
1629 int x;
1630 LineChange chng;
1631 oneLineChange(pX, pY, &chng);
1632 dfsplitStartRow(p);
1633 dfsplitChangeState(p, 3);
1634 p->lnLeft++;
1635 p->lnRight++;
1636
--- src/diff.c
+++ src/diff.c
@@ -458,11 +458,11 @@
458 }
459
460 #define MX_CSN 8 /* Maximum number of change spans across a change region */
461
462 /*
463 ** A description of zero or more (up to MX_CSN) areas of difference
464 ** between two lines of text.
465 */
466 typedef struct LineChange LineChange;
467 struct LineChange {
468 int n; /* Number of change spans */
@@ -532,11 +532,11 @@
532 p->a[0].iStart1 = 0;
533 p->a[0].iLen1 = nA;
534 p->a[0].iStart2 = 0;
535 p->a[0].iLen2 = nB;
536 p->a[0].isMin = 0;
537 while( p->n<MX_CSN-1 ){
538 int mxi = -1;
539 int mxLen = -1;
540 int x, i;
541 int aLCS[4];
542 struct Span *a, *b;
@@ -570,10 +570,78 @@
570 a->iLen2 = aLCS[2];
571 b->isMin = 0;
572 }
573 return p->n;
574 }
575
576 /*
577 ** Return true if the string starts with n spaces
578 */
579 static int allSpaces(const char *z, int n){
580 int i;
581 for(i=0; i<n && fossil_isspace(z[i]); i++){}
582 return i==n;
583 }
584
585 /*
586 ** Try to improve the human-readability of the LineChange p.
587 **
588 ** (1) If the first change span shows a change of indentation, try to
589 ** move that indentation change to the left margin.
590 **
591 ** (2) Try to shift changes so that they begin or end with a space.
592 */
593 static void improveReadability(
594 const char *zA, /* Left line of the change */
595 const char *zB, /* Right line of the change */
596 LineChange *p /* The LineChange to be adjusted */
597 ){
598 int j, n, len;
599 if( p->n<1 ) return;
600
601 /* (1) Attempt to move indentation changes to the left margin */
602 if( p->a[0].iLen1==0
603 && (len = p->a[0].iLen2)>0
604 && (j = p->a[0].iStart2)>0
605 && zB[0]==zB[j]
606 && allSpaces(zB, j)
607 ){
608 for(n=1; n<len && n<j && zB[j]==zB[j+n]; n++){}
609 if( n<len ){
610 memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n);
611 p->n++;
612 p->a[0] = p->a[1];
613 p->a[1].iStart2 += n;
614 p->a[1].iLen2 -= n;
615 p->a[0].iLen2 = n;
616 }
617 p->a[0].iStart1 = 0;
618 p->a[0].iStart2 = 0;
619 }else
620 if( p->a[0].iLen2==0
621 && (len = p->a[0].iLen1)>0
622 && (j = p->a[0].iStart1)>0
623 && zA[0]==zA[j]
624 && allSpaces(zA, j)
625 ){
626 for(n=1; n<len && n<j && zA[j]==zA[j+n]; n++){}
627 if( n<len ){
628 memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n);
629 p->n++;
630 p->a[0] = p->a[1];
631 p->a[1].iStart1 += n;
632 p->a[1].iLen1 -= n;
633 p->a[0].iLen1 = n;
634 }
635 p->a[0].iStart1 = 0;
636 p->a[0].iStart2 = 0;
637 }
638
639 /* (2) Try to shift changes so that they begin or end with a
640 ** space. (TBD) */
641 }
642
643
644 /*
645 ** Given two lines of text, pFrom and pTo, compute a set of changes
646 ** between those two lines, for enhanced display purposes.
647 **
@@ -662,10 +730,11 @@
730 p->n = 1;
731 p->a[0].iStart1 = nPrefix;
732 p->a[0].iLen1 = 0;
733 p->a[0].iStart2 = nPrefix;
734 p->a[0].iLen2 = nRight - nCommon;
735 improveReadability(zLeft, zRight, p);
736 return;
737 }
738
739 /* A single chunk of text deleted */
740 if( nCommon==nRight ){
@@ -672,10 +741,11 @@
741 p->n = 1;
742 p->a[0].iStart1 = nPrefix;
743 p->a[0].iLen1 = nLeft - nCommon;
744 p->a[0].iStart2 = nPrefix;
745 p->a[0].iLen2 = 0;
746 improveReadability(zLeft, zRight, p);
747 return;
748 }
749
750 /* At this point we know that there is a chunk of text that has
751 ** changed between the left and the right. Check to see if there
@@ -691,19 +761,21 @@
761 int i;
762 for(i=0; i<p->n; i++){
763 p->a[i].iStart1 += nPrefix;
764 p->a[i].iStart2 += nPrefix;
765 }
766 improveReadability(zLeft, zRight, p);
767 return;
768 }
769
770 /* If all else fails, show a single big change between left and right */
771 p->n = 1;
772 p->a[0].iStart1 = nPrefix;
773 p->a[0].iLen1 = nLeft - nCommon;
774 p->a[0].iStart2 = nPrefix;
775 p->a[0].iLen2 = nRight - nCommon;
776 improveReadability(zLeft, zRight, p);
777 }
778
779 /*
780 ** COMMAND: test-line-diff
781 ** Usage: %fossil% test-line-diff STRING1 STRING2
@@ -1625,11 +1697,11 @@
1697 blob_append_char(&p->aCol[3], '\n');
1698 }
1699 static void dfsplitEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){
1700 int i;
1701 int x;
1702 LineChange chng;
1703 oneLineChange(pX, pY, &chng);
1704 dfsplitStartRow(p);
1705 dfsplitChangeState(p, 3);
1706 p->lnLeft++;
1707 p->lnRight++;
1708
+2 -1
--- src/diff.js
+++ src/diff.js
@@ -10,11 +10,12 @@
1010
(function(){
1111
var SCROLL_LEN = 25;
1212
function initDiff(diff){
1313
var txtCols = diff.querySelectorAll('td.difftxt');
1414
var txtPres = diff.querySelectorAll('td.difftxt pre');
15
- var width = Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth);
15
+ var width = 0;
16
+ if(txtPres.length>=2)Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth);
1617
var i;
1718
for(i=0; i<txtCols.length; i++){
1819
txtCols[i].style.width = width + 'px';
1920
txtPres[i].style.maxWidth = width + 'px';
2021
txtPres[i].style.width = width + 'px';
2122
--- src/diff.js
+++ src/diff.js
@@ -10,11 +10,12 @@
10 (function(){
11 var SCROLL_LEN = 25;
12 function initDiff(diff){
13 var txtCols = diff.querySelectorAll('td.difftxt');
14 var txtPres = diff.querySelectorAll('td.difftxt pre');
15 var width = Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth);
 
16 var i;
17 for(i=0; i<txtCols.length; i++){
18 txtCols[i].style.width = width + 'px';
19 txtPres[i].style.maxWidth = width + 'px';
20 txtPres[i].style.width = width + 'px';
21
--- src/diff.js
+++ src/diff.js
@@ -10,11 +10,12 @@
10 (function(){
11 var SCROLL_LEN = 25;
12 function initDiff(diff){
13 var txtCols = diff.querySelectorAll('td.difftxt');
14 var txtPres = diff.querySelectorAll('td.difftxt pre');
15 var width = 0;
16 if(txtPres.length>=2)Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth);
17 var i;
18 for(i=0; i<txtCols.length; i++){
19 txtCols[i].style.width = width + 'px';
20 txtPres[i].style.maxWidth = width + 'px';
21 txtPres[i].style.width = width + 'px';
22

Keyboard Shortcuts

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