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 ", 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: -** -** -** -** And after the call, it should output: -**
Old title -** New title
-** -** 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

- return 0; - } - - collim = collim < 4 ? 0 : collim; - - /* Compute the difference */ - diff_all(&c); - - linebuf = fossil_malloc(LENGTH_MASK+1); - if( !linebuf ){ - free(c.aFrom); - free(c.aTo); - free(c.aEdit); - return 0; - } - - iFrom=iTo=0; - i=0; - while( i