Index: src/diff.c
==================================================================
--- src/diff.c
+++ src/diff.c
@@ -25,15 +25,18 @@
#if INTERFACE
/*
** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
*/
-#define DIFF_CONTEXT_MASK 0x0000fff /* Lines of context. Default if 0 */
-#define DIFF_WIDTH_MASK 0x00ff000 /* side-by-side column width */
-#define DIFF_IGNORE_EOLWS 0x0100000 /* Ignore end-of-line whitespace */
-#define DIFF_SIDEBYSIDE 0x0200000 /* Generate a side-by-side diff */
-#define DIFF_NEWFILE 0x0400000 /* Missing files are as empty files */
+#define DIFF_CONTEXT_MASK 0x0000ffff /* Lines of context. Default if 0 */
+#define DIFF_WIDTH_MASK 0x00ff0000 /* side-by-side column width */
+#define DIFF_IGNORE_EOLWS 0x01000000 /* Ignore end-of-line whitespace */
+#define DIFF_SIDEBYSIDE 0x02000000 /* Generate a side-by-side diff */
+#define DIFF_NEWFILE 0x04000000 /* Missing files are as empty files */
+#define DIFF_INLINE 0x08000000 /* Inline (not side-by-side) diff */
+#define DIFF_HTML 0x10000000 /* Render for HTML */
+#define DIFF_LINENO 0x20000000 /* Show line numbers in context diff */
#endif /* INTERFACE */
/*
** Maximum length of a line in a text file. (8192)
@@ -146,15 +149,49 @@
}
/*
** Append a single line of "diff" output to pOut.
*/
-static void appendDiffLine(Blob *pOut, char *zPrefix, DLine *pLine){
- blob_append(pOut, zPrefix, 1);
- blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
+static void appendDiffLine(Blob *pOut, char cPrefix, DLine *pLine, int html){
+ blob_append(pOut, &cPrefix, 1);
+ if( html ){
+ char *zHtml;
+ if( cPrefix=='+' ){
+ blob_append(pOut, "", -1);
+ }else if( cPrefix=='-' ){
+ blob_append(pOut, "", -1);
+ }
+ zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
+ blob_append(pOut, zHtml, -1);
+ fossil_free(zHtml);
+ if( cPrefix!=' ' ){
+ blob_append(pOut, "", -1);
+ }
+ }else{
+ blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
+ }
blob_append(pOut, "\n", 1);
}
+
+/*
+** Append line numbers to the context diff output. Zero or negative numbers
+** are blanks.
+*/
+static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
+ if( html ) blob_append(pOut, "", -1);
+ if( lnA>0 ){
+ blob_appendf(pOut, "%6d ", lnA);
+ }else{
+ blob_append(pOut, " ", 7);
+ }
+ if( lnB>0 ){
+ blob_appendf(pOut, "%6d ", lnB);
+ }else{
+ blob_append(pOut, " ", 8);
+ }
+ if( html ) blob_append(pOut, "", -1);
+}
/*
** Expand the size of aEdit[] array to hold nEdit elements.
*/
static void expandEdit(DContext *p, int nEdit){
@@ -198,11 +235,17 @@
/*
** Given a diff context in which the aEdit[] array has been filled
** in, compute a context diff into pOut.
*/
-static void contextDiff(DContext *p, Blob *pOut, int nContext){
+static void contextDiff(
+ DContext *p, /* The difference */
+ Blob *pOut, /* Output a context diff to here */
+ int nContext, /* Number of lines of context */
+ int showLn, /* Show line numbers */
+ int html /* Render as HTML */
+){
DLine *A; /* Left side of the diff */
DLine *B; /* Right side of the diff */
int a = 0; /* Index of next line in A[] */
int b = 0; /* Index of next line in B[] */
int *R; /* Array of COPY/DELETE/INSERT triples */
@@ -252,40 +295,57 @@
/*
* If the patch changes an empty file or results in an empty file,
* the block header must use 0,0 as position indicator and not 1,0.
* Otherwise, patch would be confused and may reject the diff.
*/
- blob_appendf(pOut,"@@ -%d,%d +%d,%d @@\n",
- na ? a+skip+1 : 0, na,
- nb ? b+skip+1 : 0, nb);
+ if( showLn ){
+ if( r==0 ){
+ /* Do not show a top divider */
+ }else if( html ){
+ blob_appendf(pOut, "%.80c\n", '.');
+ }else{
+ blob_appendf(pOut, "%.80c\n", '.');
+ }
+ }else{
+ if( html ) blob_appendf(pOut, "");
+ blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
+ na ? a+skip+1 : 0, na,
+ nb ? b+skip+1 : 0, nb);
+ if( html ) blob_appendf(pOut, "");
+ blob_append(pOut, "\n", 1);
+ }
/* Show the initial common area */
a += skip;
b += skip;
m = R[r] - skip;
for(j=0; jnContext ) m = nContext;
for(j=0; j after text */
/*
** Write up to width characters of pLine into z[]. Translate tabs into
-** spaces. If trunc is true, then append \n\000 after the last character
-** written.
+** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters
+** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set.
*/
-static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){
+static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){
int n = pLine->h & LENGTH_MASK;
int i, j;
const char *zIn = pLine->z;
- for(i=j=0; izLine[p->n];
+ int w = p->width;
+ if( n>w ) n = w;
+ for(i=j=0; iescHtml ){
+ memcpy(&z[j], "<", 4);
+ j += 4;
+ }else if( c=='&' && p->escHtml ){
+ memcpy(&z[j], "&", 5);
+ j += 5;
+ }else if( c=='>' && p->escHtml ){
+ memcpy(&z[j], ">", 4);
+ j += 4;
}else{
z[j++] = c;
}
}
- if( trunc ){
+ if( (flags & SBS_ENDSPAN) && p->escHtml ){
+ memcpy(&z[j], "", 7);
+ j += 7;
+ }
+ if( (flags & SBS_PAD)!=0 ){
+ while( in += j;
+}
+
+/*
+** Append a string to an SbSLine with coding, interpretation, or padding.
+*/
+static void sbsWrite(SbsLine *p, const char *zIn, int nIn){
+ memcpy(p->zLine+p->n, zIn, nIn);
+ p->n += nIn;
+}
+
+/*
+** Append n spaces to the string.
+*/
+static void sbsWriteSpace(SbsLine *p, int n){
+ while( n-- ) p->zLine[p->n++] = ' ';
+}
+
+/*
+** Append a string to the output only if we are rendering HTML.
+*/
+static void sbsWriteHtml(SbsLine *p, const char *zIn){
+ if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn));
+}
+
+/*
+** Write a 6-digit line number followed by a single space onto the line.
+*/
+static void sbsWriteLineno(SbsLine *p, int ln){
+ sbsWriteHtml(p, "");
+ sqlite3_snprintf(7, &p->zLine[p->n], "%5d ", ln+1);
+ p->n += 6;
+ sbsWriteHtml(p, "");
+ p->zLine[p->n++] = ' ';
}
/*
** Given a diff context in which the aEdit[] array has been filled
** in, compute a side-by-side diff into pOut.
*/
-static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
+static void sbsDiff(
+ DContext *p, /* The computed diff */
+ Blob *pOut, /* Write the results here */
+ int nContext, /* Number of lines of context around each change */
+ int width, /* Width of each column of output */
+ int escHtml /* True to generate HTML output */
+){
DLine *A; /* Left side of the diff */
DLine *B; /* Right side of the diff */
int a = 0; /* Index of next line in A[] */
int b = 0; /* Index of next line in B[] */
int *R; /* Array of COPY/DELETE/INSERT triples */
@@ -352,18 +479,16 @@
int mxr; /* Maximum value for r */
int na, nb; /* Number of lines shown from A and B */
int i, j; /* Loop counters */
int m, ma, mb;/* Number of lines to output */
int skip; /* Number of lines to skip */
- int mxLine; /* Length of a line of text */
- char *zLine; /* A line of text being formatted */
- int len; /* Length of an output line */
-
- mxLine = width*2 + 2*7 + 3 + 1;
- zLine = fossil_malloc( mxLine + 1 );
- if( zLine==0 ) return;
- zLine[mxLine] = 0;
+ SbsLine s; /* Output line buffer */
+
+ s.zLine = fossil_malloc( 10*width + 100 );
+ if( s.zLine==0 ) return;
+ s.width = width;
+ s.escHtml = escHtml;
A = p->aFrom;
B = p->aTo;
R = p->aEdit;
mxr = p->nEdit;
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -400,23 +525,31 @@
/*
* If the patch changes an empty file or results in an empty file,
* the block header must use 0,0 as position indicator and not 1,0.
* Otherwise, patch would be confused and may reject the diff.
*/
- if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.');
+ if( r>0 ){
+ if( escHtml ){
+ blob_appendf(pOut, "%.*c\n",
+ width*2+16, '.');
+ }else{
+ blob_appendf(pOut, "%.*c\n", width*2+16, '.');
+ }
+ }
/* Show the initial common area */
a += skip;
b += skip;
m = R[r] - skip;
for(j=0; j");
+ sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
+ sbsWrite(&s, " | ", 3);
+ sbsWriteLineno(&s, b+j);
+ sbsWriteHtml(&s, "");
+ sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
+ blob_append(pOut, s.zLine, s.n);
}
a += m;
b += m;
ma -= m;
mb -= m;
for(j=0; j");
+ sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
+ sbsWrite(&s, " <\n", 3);
+ blob_append(pOut, s.zLine, s.n);
}
a += ma;
for(j=0; j';
- sbsWriteLineno(&zLine[width+10], b+j);
- len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
- blob_append(pOut, zLine, len+width+17);
+ s.n = 0;
+ sbsWriteSpace(&s, width + 7);
+ sbsWrite(&s, " > ", 3);
+ sbsWriteLineno(&s, b+j);
+ sbsWriteHtml(&s, "");
+ sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
+ blob_append(pOut, s.zLine, s.n);
}
b += mb;
if( inContext ) m = nContext;
for(j=0; jh & LENGTH_MASK;
- if( lim && len > lim ){
- memcpy(out, dl->z, lim-3);
- memcpy(&out[lim-3], "...", 4);
- }else{
- memcpy(out, dl->z, len);
- out[len] = '\0';
- }
- return out;
-}
-
-/*
-** Output table body of a side-by-side diff. Prior to the call, the caller
-** should have output:
-**
-**
Old title
-**
New title
-**
-** And after the call, it should output:
-**
-**
-** Some good reference diffs in the fossil repository for testing:
-** /vdiff?from=080d27a&to=4b0f813&detail=1
-** /vdiff?from=636804745b&to=c1d78e0556&detail=1
-** /vdiff?from=c0b6c28d29&to=25169506b7&detail=1
-** /vdiff?from=e3d022dffa&to=48bcfbd47b&detail=1
-*/
-int html_sbsdiff(
- Blob *pA_Blob, /* FROM file */
- Blob *pB_Blob, /* TO file */
- int nContext, /* Amount of context to unified diff */
- int ignoreEolWs /* Ignore whitespace at the end of lines */
-){
- DContext c;
- int i;
- int iFrom, iTo;
- char *linebuf;
- int collim=0; /* Currently not settable; allows a column limit for diffs */
- int allowExp=0; /* Currently not settable; (dis)allow expansion of rows */
-
- /* Prepare the input files */
- memset(&c, 0, sizeof(c));
- c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
- &c.nFrom, ignoreEolWs);
- c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
- &c.nTo, ignoreEolWs);
- if( c.aFrom==0 || c.aTo==0 ){
- free(c.aFrom);
- free(c.aTo);
- /* Note: This would be generated within a table. */
- @
cannot compute
- @ difference between binary files
- blob_reset(&from);
- blob_reset(&to);
-}
-
/*
** Write a line of web-page output that shows changes that have occurred
** to a file between two check-ins.
*/
@@ -314,12 +294,11 @@
static void append_file_change_line(
const char *zName, /* Name of the file that has changed */
const char *zOld, /* blob.uuid before change. NULL for added files */
const char *zNew, /* blob.uuid after change. NULL for deletes */
const char *zOldName, /* Prior name. NULL if no name change. */
- int showDiff, /* Show edit diffs if true */
- int sideBySide, /* Show diffs side-by-side */
+ int diffFlags, /* Flags for text_diff(). Zero to omit diffs */
int mperm /* executable or symlink permission for zNew */
){
if( !g.perm.History ){
if( zNew==0 ){
@
Deleted %h(zName)
@@ -331,18 +310,14 @@
@
Execute permission %s(( mperm==PERM_EXE )?"set":"cleared")
@ for %h(zName)