Fossil SCM

Add color to the retro sbs diff.

drh 2012-02-04 18:54 UTC retro-sbsdiff
Commit 7372c0a5c40dec58f7490923c0a49a327b9f242a
3 files changed +147 -69 +9 -2 +26
+147 -69
--- src/diff.c
+++ src/diff.c
@@ -25,16 +25,17 @@
2525
2626
#if INTERFACE
2727
/*
2828
** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
2929
*/
30
-#define DIFF_CONTEXT_MASK 0x0000fff /* Lines of context. Default if 0 */
31
-#define DIFF_WIDTH_MASK 0x00ff000 /* side-by-side column width */
32
-#define DIFF_IGNORE_EOLWS 0x0100000 /* Ignore end-of-line whitespace */
33
-#define DIFF_SIDEBYSIDE 0x0200000 /* Generate a side-by-side diff */
34
-#define DIFF_NEWFILE 0x0400000 /* Missing files are as empty files */
35
-#define DIFF_INLINE 0x0800000 /* Inline (not side-by-side) diff */
30
+#define DIFF_CONTEXT_MASK 0x0000ffff /* Lines of context. Default if 0 */
31
+#define DIFF_WIDTH_MASK 0x00ff0000 /* side-by-side column width */
32
+#define DIFF_IGNORE_EOLWS 0x01000000 /* Ignore end-of-line whitespace */
33
+#define DIFF_SIDEBYSIDE 0x02000000 /* Generate a side-by-side diff */
34
+#define DIFF_NEWFILE 0x04000000 /* Missing files are as empty files */
35
+#define DIFF_INLINE 0x08000000 /* Inline (not side-by-side) diff */
36
+#define DIFF_HTML 0x10000000 /* Render for HTML */
3637
3738
#endif /* INTERFACE */
3839
3940
/*
4041
** Maximum length of a line in a text file. (8192)
@@ -300,51 +301,115 @@
300301
}
301302
}
302303
}
303304
304305
/*
305
-** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to
306
-** have space for at least 7 characters.
306
+** Status of a single output line
307
+*/
308
+typedef struct SbsLine SbsLine;
309
+struct SbsLine {
310
+ char *zLine; /* The output line under construction */
311
+ int n; /* Index of next unused slot in the zLine[] */
312
+ int width; /* Maximum width of a column in the output */
313
+ unsigned char escHtml; /* True to escape html characters */
314
+};
315
+
316
+/*
317
+** Write a 6-digit line number followed by a single space onto the line.
307318
*/
308
-static void sbsWriteLineno(char *z, int ln){
309
- sqlite3_snprintf(7, z, "%6d", ln+1);
310
- z[6] = ' ';
319
+static void sbsWriteLineno(SbsLine *p, int ln){
320
+ sqlite3_snprintf(7, &p->zLine[p->n], "%6d", ln+1);
321
+ p->zLine[p->n+6] = ' ';
322
+ p->n += 7;
311323
}
312324
325
+/*
326
+** Flags for sbsWriteText()
327
+*/
328
+#define SBS_NEWLINE 0x0001 /* End with \n\000 */
329
+#define SBS_PAD 0x0002 /* Pad output to width spaces */
330
+#define SBS_ENDSPAN 0x0004 /* Write a </span> after text */
331
+
313332
/*
314333
** Write up to width characters of pLine into z[]. Translate tabs into
315
-** spaces. If trunc is true, then append \n\000 after the last character
316
-** written.
334
+** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters
335
+** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set.
317336
*/
318
-static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){
337
+static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){
319338
int n = pLine->h & LENGTH_MASK;
320
- int i, j;
339
+ int i, j, k;
321340
const char *zIn = pLine->z;
322
- for(i=j=0; i<n && j<width; i++){
341
+ char *z = &p->zLine[p->n];
342
+ int w = p->width;
343
+ if( n>w ) n = w;
344
+ for(i=j=0; i<n; i++){
323345
char c = zIn[i];
324346
if( c=='\t' ){
325347
z[j++] = ' ';
326
- while( (j&7)!=0 && j<width ) z[j++] = ' ';
348
+ while( (j&7)!=0 && j<n ) z[j++] = ' ';
327349
}else if( c=='\r' || c=='\f' ){
328350
z[j++] = ' ';
351
+ }else if( c=='<' && p->escHtml ){
352
+ memcpy(&z[j], "&lt;", 4);
353
+ j += 4;
354
+ }else if( c=='&' && p->escHtml ){
355
+ memcpy(&z[j], "&amp;", 5);
356
+ j += 5;
357
+ }else if( c=='>' && p->escHtml ){
358
+ memcpy(&z[j], "&gt;", 4);
359
+ j += 4;
329360
}else{
330361
z[j++] = c;
331362
}
332363
}
333
- if( trunc ){
364
+ if( (flags & SBS_ENDSPAN) && p->escHtml ){
365
+ memcpy(&z[j], "</span>", 7);
366
+ j += 7;
367
+ }
368
+ if( (flags & SBS_PAD)!=0 ){
369
+ while( i<w ){ i++; z[j++] = ' '; }
370
+ }
371
+ if( flags & SBS_NEWLINE ){
334372
z[j++] = '\n';
335
- z[j] = 0;
336373
}
337
- return j;
374
+ p->n += j;
375
+}
376
+
377
+/*
378
+** Append a string to an SbSLine with coding, interpretation, or padding.
379
+*/
380
+static void sbsWrite(SbsLine *p, const char *zIn, int nIn){
381
+ memcpy(p->zLine+p->n, zIn, nIn);
382
+ p->n += nIn;
383
+}
384
+
385
+/*
386
+** Append n spaces to the string.
387
+*/
388
+static void sbsWriteSpace(SbsLine *p, int n){
389
+ while( n-- ) p->zLine[p->n++] = ' ';
390
+}
391
+
392
+/*
393
+** Append a string to the output only if we are rendering HTML.
394
+*/
395
+static void sbsWriteHtml(SbsLine *p, const char *zIn){
396
+ if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn));
338397
}
339398
340399
341400
/*
342401
** Given a diff context in which the aEdit[] array has been filled
343402
** in, compute a side-by-side diff into pOut.
344403
*/
345
-static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
404
+static void sbsDiff(
405
+ DContext *p, /* The computed diff */
406
+ Blob *pOut, /* Write the results here */
407
+ int nContext, /* Number of lines of context around each change */
408
+ int width, /* Width of each column of output */
409
+ int escHtml /* True to generate HTML output */
410
+){
346411
DLine *A; /* Left side of the diff */
347412
DLine *B; /* Right side of the diff */
348413
int a = 0; /* Index of next line in A[] */
349414
int b = 0; /* Index of next line in B[] */
350415
int *R; /* Array of COPY/DELETE/INSERT triples */
@@ -353,18 +418,16 @@
353418
int mxr; /* Maximum value for r */
354419
int na, nb; /* Number of lines shown from A and B */
355420
int i, j; /* Loop counters */
356421
int m, ma, mb;/* Number of lines to output */
357422
int skip; /* Number of lines to skip */
358
- int mxLine; /* Length of a line of text */
359
- char *zLine; /* A line of text being formatted */
360
- int len; /* Length of an output line */
361
-
362
- mxLine = width*2 + 2*7 + 3 + 1;
363
- zLine = fossil_malloc( mxLine + 1 );
364
- if( zLine==0 ) return;
365
- zLine[mxLine] = 0;
423
+ SbsLine s; /* Output line buffer */
424
+
425
+ s.zLine = fossil_malloc( 10*width + 100 );
426
+ if( s.zLine==0 ) return;
427
+ s.width = width;
428
+ s.escHtml = escHtml;
366429
A = p->aFrom;
367430
B = p->aTo;
368431
R = p->aEdit;
369432
mxr = p->nEdit;
370433
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -401,23 +464,31 @@
401464
/*
402465
* If the patch changes an empty file or results in an empty file,
403466
* the block header must use 0,0 as position indicator and not 1,0.
404467
* Otherwise, patch would be confused and may reject the diff.
405468
*/
406
- if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.');
469
+ if( r>0 ){
470
+ if( escHtml ){
471
+ blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
472
+ width*2+16, '.');
473
+ }else{
474
+ blob_appendf(pOut, "%.*c\n", width*2+16, '.');
475
+ }
476
+ }
407477
408478
/* Show the initial common area */
409479
a += skip;
410480
b += skip;
411481
m = R[r] - skip;
412482
for(j=0; j<m; j++){
413
- memset(zLine, ' ', mxLine);
414
- sbsWriteLineno(zLine, a+j);
415
- sbsWriteText(&zLine[7], &A[a+j], width, 0);
416
- sbsWriteLineno(&zLine[width+10], b+j);
417
- len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
418
- blob_append(pOut, zLine, len+width+17);
483
+ s.n = 0;
484
+ sbsWriteLineno(&s, a+j);
485
+ sbsWriteText(&s, &A[a+j], SBS_PAD);
486
+ sbsWrite(&s, " ", 3);
487
+ sbsWriteLineno(&s, b+j);
488
+ sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
489
+ blob_append(pOut, s.zLine, s.n);
419490
}
420491
a += m;
421492
b += m;
422493
423494
/* Show the differences */
@@ -424,49 +495,53 @@
424495
for(i=0; i<nr; i++){
425496
ma = R[r+i*3+1];
426497
mb = R[r+i*3+2];
427498
m = ma<mb ? ma : mb;
428499
for(j=0; j<m; j++){
429
- memset(zLine, ' ', mxLine);
430
- sbsWriteLineno(zLine, a+j);
431
- sbsWriteText(&zLine[7], &A[a+j], width, 0);
432
- zLine[width+8] = '|';
433
- sbsWriteLineno(&zLine[width+10], b+j);
434
- len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
435
- blob_append(pOut, zLine, len+width+17);
500
+ s.n = 0;
501
+ sbsWriteLineno(&s, a+j);
502
+ sbsWriteHtml(&s, "<span class=\"diffchng\">");
503
+ sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
504
+ sbsWrite(&s, " | ", 3);
505
+ sbsWriteLineno(&s, b+j);
506
+ sbsWriteHtml(&s, "<span class=\"diffchng\">");
507
+ sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
508
+ blob_append(pOut, s.zLine, s.n);
436509
}
437510
a += m;
438511
b += m;
439512
ma -= m;
440513
mb -= m;
441514
for(j=0; j<ma; j++){
442
- memset(zLine, ' ', width+7);
443
- sbsWriteLineno(zLine, a+j);
444
- sbsWriteText(&zLine[7], &A[a+j], width, 0);
445
- zLine[width+8] = '<';
446
- zLine[width+9] = '\n';
447
- zLine[width+10] = 0;
448
- blob_append(pOut, zLine, width+10);
515
+ s.n = 0;
516
+ sbsWriteLineno(&s, a+j);
517
+ sbsWriteHtml(&s, "<span class=\"diffrm\">");
518
+ sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
519
+ sbsWrite(&s, " <\n", 3);
520
+ blob_append(pOut, s.zLine, s.n);
449521
}
450522
a += ma;
451523
for(j=0; j<mb; j++){
452
- memset(zLine, ' ', mxLine);
453
- zLine[width+8] = '>';
454
- sbsWriteLineno(&zLine[width+10], b+j);
455
- len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
456
- blob_append(pOut, zLine, len+width+17);
524
+ s.n = 0;
525
+ sbsWriteSpace(&s, width + 7);
526
+ sbsWrite(&s, " > ", 3);
527
+ sbsWriteLineno(&s, b+j);
528
+ sbsWriteHtml(&s, "<span class=\"diffadd\">");
529
+ sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
530
+ blob_append(pOut, s.zLine, s.n);
457531
}
458532
b += mb;
459533
if( i<nr-1 ){
460534
m = R[r+i*3+3];
461535
for(j=0; j<m; j++){
462
- memset(zLine, ' ', mxLine);
463
- sbsWriteLineno(zLine, a+j);
464
- sbsWriteText(&zLine[7], &A[a+j], width, 0);
465
- sbsWriteLineno(&zLine[width+10], b+j);
466
- len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
467
- blob_append(pOut, zLine, len+width+17);
536
+ s.n = 0;
537
+ sbsWriteLineno(&s, a+j);
538
+ sbsWriteText(&s, &A[a+j], SBS_PAD);
539
+ sbsWrite(&s, " ", 3);
540
+ sbsWriteLineno(&s, b+j);
541
+ sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
542
+ blob_append(pOut, s.zLine, s.n);
468543
}
469544
b += m;
470545
a += m;
471546
}
472547
}
@@ -474,19 +549,20 @@
474549
/* Show the final common area */
475550
assert( nr==i );
476551
m = R[r+nr*3];
477552
if( m>nContext ) m = nContext;
478553
for(j=0; j<m; j++){
479
- memset(zLine, ' ', mxLine);
480
- sbsWriteLineno(zLine, a+j);
481
- sbsWriteText(&zLine[7], &A[a+j], width, 0);
482
- sbsWriteLineno(&zLine[width+10], b+j);
483
- len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
484
- blob_append(pOut, zLine, len+width+17);
554
+ s.n = 0;
555
+ sbsWriteLineno(&s, a+j);
556
+ sbsWriteText(&s, &A[a+j], SBS_PAD);
557
+ sbsWrite(&s, " ", 3);
558
+ sbsWriteLineno(&s, b+j);
559
+ sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
560
+ blob_append(pOut, s.zLine, s.n);
485561
}
486562
}
487
- free(zLine);
563
+ free(s.zLine);
488564
}
489565
490566
/*
491567
** Compute the optimal longest common subsequence (LCS) using an
492568
** exhaustive search. This version of the LCS is only used for
@@ -789,11 +865,12 @@
789865
790866
if( pOut ){
791867
/* Compute a context or side-by-side diff into pOut */
792868
if( diffFlags & DIFF_SIDEBYSIDE ){
793869
int width = diff_width(diffFlags);
794
- sbsDiff(&c, pOut, nContext, width);
870
+ int escHtml = (diffFlags & DIFF_HTML)!=0;
871
+ sbsDiff(&c, pOut, nContext, width, escHtml);
795872
}else{
796873
contextDiff(&c, pOut, nContext);
797874
}
798875
free(c.aFrom);
799876
free(c.aTo);
@@ -851,10 +928,11 @@
851928
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
852929
f *= DIFF_CONTEXT_MASK+1;
853930
if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
854931
diffFlags |= f;
855932
}
933
+ if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
856934
return diffFlags;
857935
}
858936
859937
/*
860938
** COMMAND: test-udiff
861939
--- src/diff.c
+++ src/diff.c
@@ -25,16 +25,17 @@
25
26 #if INTERFACE
27 /*
28 ** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
29 */
30 #define DIFF_CONTEXT_MASK 0x0000fff /* Lines of context. Default if 0 */
31 #define DIFF_WIDTH_MASK 0x00ff000 /* side-by-side column width */
32 #define DIFF_IGNORE_EOLWS 0x0100000 /* Ignore end-of-line whitespace */
33 #define DIFF_SIDEBYSIDE 0x0200000 /* Generate a side-by-side diff */
34 #define DIFF_NEWFILE 0x0400000 /* Missing files are as empty files */
35 #define DIFF_INLINE 0x0800000 /* Inline (not side-by-side) diff */
 
36
37 #endif /* INTERFACE */
38
39 /*
40 ** Maximum length of a line in a text file. (8192)
@@ -300,51 +301,115 @@
300 }
301 }
302 }
303
304 /*
305 ** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to
306 ** have space for at least 7 characters.
 
 
 
 
 
 
 
 
 
 
307 */
308 static void sbsWriteLineno(char *z, int ln){
309 sqlite3_snprintf(7, z, "%6d", ln+1);
310 z[6] = ' ';
 
311 }
312
 
 
 
 
 
 
 
313 /*
314 ** Write up to width characters of pLine into z[]. Translate tabs into
315 ** spaces. If trunc is true, then append \n\000 after the last character
316 ** written.
317 */
318 static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){
319 int n = pLine->h & LENGTH_MASK;
320 int i, j;
321 const char *zIn = pLine->z;
322 for(i=j=0; i<n && j<width; i++){
 
 
 
323 char c = zIn[i];
324 if( c=='\t' ){
325 z[j++] = ' ';
326 while( (j&7)!=0 && j<width ) z[j++] = ' ';
327 }else if( c=='\r' || c=='\f' ){
328 z[j++] = ' ';
 
 
 
 
 
 
 
 
 
329 }else{
330 z[j++] = c;
331 }
332 }
333 if( trunc ){
 
 
 
 
 
 
 
334 z[j++] = '\n';
335 z[j] = 0;
336 }
337 return j;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338 }
339
340
341 /*
342 ** Given a diff context in which the aEdit[] array has been filled
343 ** in, compute a side-by-side diff into pOut.
344 */
345 static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
 
 
 
 
 
 
346 DLine *A; /* Left side of the diff */
347 DLine *B; /* Right side of the diff */
348 int a = 0; /* Index of next line in A[] */
349 int b = 0; /* Index of next line in B[] */
350 int *R; /* Array of COPY/DELETE/INSERT triples */
@@ -353,18 +418,16 @@
353 int mxr; /* Maximum value for r */
354 int na, nb; /* Number of lines shown from A and B */
355 int i, j; /* Loop counters */
356 int m, ma, mb;/* Number of lines to output */
357 int skip; /* Number of lines to skip */
358 int mxLine; /* Length of a line of text */
359 char *zLine; /* A line of text being formatted */
360 int len; /* Length of an output line */
361
362 mxLine = width*2 + 2*7 + 3 + 1;
363 zLine = fossil_malloc( mxLine + 1 );
364 if( zLine==0 ) return;
365 zLine[mxLine] = 0;
366 A = p->aFrom;
367 B = p->aTo;
368 R = p->aEdit;
369 mxr = p->nEdit;
370 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -401,23 +464,31 @@
401 /*
402 * If the patch changes an empty file or results in an empty file,
403 * the block header must use 0,0 as position indicator and not 1,0.
404 * Otherwise, patch would be confused and may reject the diff.
405 */
406 if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.');
 
 
 
 
 
 
 
407
408 /* Show the initial common area */
409 a += skip;
410 b += skip;
411 m = R[r] - skip;
412 for(j=0; j<m; j++){
413 memset(zLine, ' ', mxLine);
414 sbsWriteLineno(zLine, a+j);
415 sbsWriteText(&zLine[7], &A[a+j], width, 0);
416 sbsWriteLineno(&zLine[width+10], b+j);
417 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
418 blob_append(pOut, zLine, len+width+17);
 
419 }
420 a += m;
421 b += m;
422
423 /* Show the differences */
@@ -424,49 +495,53 @@
424 for(i=0; i<nr; i++){
425 ma = R[r+i*3+1];
426 mb = R[r+i*3+2];
427 m = ma<mb ? ma : mb;
428 for(j=0; j<m; j++){
429 memset(zLine, ' ', mxLine);
430 sbsWriteLineno(zLine, a+j);
431 sbsWriteText(&zLine[7], &A[a+j], width, 0);
432 zLine[width+8] = '|';
433 sbsWriteLineno(&zLine[width+10], b+j);
434 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
435 blob_append(pOut, zLine, len+width+17);
 
 
436 }
437 a += m;
438 b += m;
439 ma -= m;
440 mb -= m;
441 for(j=0; j<ma; j++){
442 memset(zLine, ' ', width+7);
443 sbsWriteLineno(zLine, a+j);
444 sbsWriteText(&zLine[7], &A[a+j], width, 0);
445 zLine[width+8] = '<';
446 zLine[width+9] = '\n';
447 zLine[width+10] = 0;
448 blob_append(pOut, zLine, width+10);
449 }
450 a += ma;
451 for(j=0; j<mb; j++){
452 memset(zLine, ' ', mxLine);
453 zLine[width+8] = '>';
454 sbsWriteLineno(&zLine[width+10], b+j);
455 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
456 blob_append(pOut, zLine, len+width+17);
 
 
457 }
458 b += mb;
459 if( i<nr-1 ){
460 m = R[r+i*3+3];
461 for(j=0; j<m; j++){
462 memset(zLine, ' ', mxLine);
463 sbsWriteLineno(zLine, a+j);
464 sbsWriteText(&zLine[7], &A[a+j], width, 0);
465 sbsWriteLineno(&zLine[width+10], b+j);
466 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
467 blob_append(pOut, zLine, len+width+17);
 
468 }
469 b += m;
470 a += m;
471 }
472 }
@@ -474,19 +549,20 @@
474 /* Show the final common area */
475 assert( nr==i );
476 m = R[r+nr*3];
477 if( m>nContext ) m = nContext;
478 for(j=0; j<m; j++){
479 memset(zLine, ' ', mxLine);
480 sbsWriteLineno(zLine, a+j);
481 sbsWriteText(&zLine[7], &A[a+j], width, 0);
482 sbsWriteLineno(&zLine[width+10], b+j);
483 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
484 blob_append(pOut, zLine, len+width+17);
 
485 }
486 }
487 free(zLine);
488 }
489
490 /*
491 ** Compute the optimal longest common subsequence (LCS) using an
492 ** exhaustive search. This version of the LCS is only used for
@@ -789,11 +865,12 @@
789
790 if( pOut ){
791 /* Compute a context or side-by-side diff into pOut */
792 if( diffFlags & DIFF_SIDEBYSIDE ){
793 int width = diff_width(diffFlags);
794 sbsDiff(&c, pOut, nContext, width);
 
795 }else{
796 contextDiff(&c, pOut, nContext);
797 }
798 free(c.aFrom);
799 free(c.aTo);
@@ -851,10 +928,11 @@
851 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
852 f *= DIFF_CONTEXT_MASK+1;
853 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
854 diffFlags |= f;
855 }
 
856 return diffFlags;
857 }
858
859 /*
860 ** COMMAND: test-udiff
861
--- src/diff.c
+++ src/diff.c
@@ -25,16 +25,17 @@
25
26 #if INTERFACE
27 /*
28 ** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
29 */
30 #define DIFF_CONTEXT_MASK 0x0000ffff /* Lines of context. Default if 0 */
31 #define DIFF_WIDTH_MASK 0x00ff0000 /* side-by-side column width */
32 #define DIFF_IGNORE_EOLWS 0x01000000 /* Ignore end-of-line whitespace */
33 #define DIFF_SIDEBYSIDE 0x02000000 /* Generate a side-by-side diff */
34 #define DIFF_NEWFILE 0x04000000 /* Missing files are as empty files */
35 #define DIFF_INLINE 0x08000000 /* Inline (not side-by-side) diff */
36 #define DIFF_HTML 0x10000000 /* Render for HTML */
37
38 #endif /* INTERFACE */
39
40 /*
41 ** Maximum length of a line in a text file. (8192)
@@ -300,51 +301,115 @@
301 }
302 }
303 }
304
305 /*
306 ** Status of a single output line
307 */
308 typedef struct SbsLine SbsLine;
309 struct SbsLine {
310 char *zLine; /* The output line under construction */
311 int n; /* Index of next unused slot in the zLine[] */
312 int width; /* Maximum width of a column in the output */
313 unsigned char escHtml; /* True to escape html characters */
314 };
315
316 /*
317 ** Write a 6-digit line number followed by a single space onto the line.
318 */
319 static void sbsWriteLineno(SbsLine *p, int ln){
320 sqlite3_snprintf(7, &p->zLine[p->n], "%6d", ln+1);
321 p->zLine[p->n+6] = ' ';
322 p->n += 7;
323 }
324
325 /*
326 ** Flags for sbsWriteText()
327 */
328 #define SBS_NEWLINE 0x0001 /* End with \n\000 */
329 #define SBS_PAD 0x0002 /* Pad output to width spaces */
330 #define SBS_ENDSPAN 0x0004 /* Write a </span> after text */
331
332 /*
333 ** Write up to width characters of pLine into z[]. Translate tabs into
334 ** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters
335 ** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set.
336 */
337 static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){
338 int n = pLine->h & LENGTH_MASK;
339 int i, j, k;
340 const char *zIn = pLine->z;
341 char *z = &p->zLine[p->n];
342 int w = p->width;
343 if( n>w ) n = w;
344 for(i=j=0; i<n; i++){
345 char c = zIn[i];
346 if( c=='\t' ){
347 z[j++] = ' ';
348 while( (j&7)!=0 && j<n ) z[j++] = ' ';
349 }else if( c=='\r' || c=='\f' ){
350 z[j++] = ' ';
351 }else if( c=='<' && p->escHtml ){
352 memcpy(&z[j], "&lt;", 4);
353 j += 4;
354 }else if( c=='&' && p->escHtml ){
355 memcpy(&z[j], "&amp;", 5);
356 j += 5;
357 }else if( c=='>' && p->escHtml ){
358 memcpy(&z[j], "&gt;", 4);
359 j += 4;
360 }else{
361 z[j++] = c;
362 }
363 }
364 if( (flags & SBS_ENDSPAN) && p->escHtml ){
365 memcpy(&z[j], "</span>", 7);
366 j += 7;
367 }
368 if( (flags & SBS_PAD)!=0 ){
369 while( i<w ){ i++; z[j++] = ' '; }
370 }
371 if( flags & SBS_NEWLINE ){
372 z[j++] = '\n';
 
373 }
374 p->n += j;
375 }
376
377 /*
378 ** Append a string to an SbSLine with coding, interpretation, or padding.
379 */
380 static void sbsWrite(SbsLine *p, const char *zIn, int nIn){
381 memcpy(p->zLine+p->n, zIn, nIn);
382 p->n += nIn;
383 }
384
385 /*
386 ** Append n spaces to the string.
387 */
388 static void sbsWriteSpace(SbsLine *p, int n){
389 while( n-- ) p->zLine[p->n++] = ' ';
390 }
391
392 /*
393 ** Append a string to the output only if we are rendering HTML.
394 */
395 static void sbsWriteHtml(SbsLine *p, const char *zIn){
396 if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn));
397 }
398
399
400 /*
401 ** Given a diff context in which the aEdit[] array has been filled
402 ** in, compute a side-by-side diff into pOut.
403 */
404 static void sbsDiff(
405 DContext *p, /* The computed diff */
406 Blob *pOut, /* Write the results here */
407 int nContext, /* Number of lines of context around each change */
408 int width, /* Width of each column of output */
409 int escHtml /* True to generate HTML output */
410 ){
411 DLine *A; /* Left side of the diff */
412 DLine *B; /* Right side of the diff */
413 int a = 0; /* Index of next line in A[] */
414 int b = 0; /* Index of next line in B[] */
415 int *R; /* Array of COPY/DELETE/INSERT triples */
@@ -353,18 +418,16 @@
418 int mxr; /* Maximum value for r */
419 int na, nb; /* Number of lines shown from A and B */
420 int i, j; /* Loop counters */
421 int m, ma, mb;/* Number of lines to output */
422 int skip; /* Number of lines to skip */
423 SbsLine s; /* Output line buffer */
424
425 s.zLine = fossil_malloc( 10*width + 100 );
426 if( s.zLine==0 ) return;
427 s.width = width;
428 s.escHtml = escHtml;
 
 
429 A = p->aFrom;
430 B = p->aTo;
431 R = p->aEdit;
432 mxr = p->nEdit;
433 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -401,23 +464,31 @@
464 /*
465 * If the patch changes an empty file or results in an empty file,
466 * the block header must use 0,0 as position indicator and not 1,0.
467 * Otherwise, patch would be confused and may reject the diff.
468 */
469 if( r>0 ){
470 if( escHtml ){
471 blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
472 width*2+16, '.');
473 }else{
474 blob_appendf(pOut, "%.*c\n", width*2+16, '.');
475 }
476 }
477
478 /* Show the initial common area */
479 a += skip;
480 b += skip;
481 m = R[r] - skip;
482 for(j=0; j<m; j++){
483 s.n = 0;
484 sbsWriteLineno(&s, a+j);
485 sbsWriteText(&s, &A[a+j], SBS_PAD);
486 sbsWrite(&s, " ", 3);
487 sbsWriteLineno(&s, b+j);
488 sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
489 blob_append(pOut, s.zLine, s.n);
490 }
491 a += m;
492 b += m;
493
494 /* Show the differences */
@@ -424,49 +495,53 @@
495 for(i=0; i<nr; i++){
496 ma = R[r+i*3+1];
497 mb = R[r+i*3+2];
498 m = ma<mb ? ma : mb;
499 for(j=0; j<m; j++){
500 s.n = 0;
501 sbsWriteLineno(&s, a+j);
502 sbsWriteHtml(&s, "<span class=\"diffchng\">");
503 sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
504 sbsWrite(&s, " | ", 3);
505 sbsWriteLineno(&s, b+j);
506 sbsWriteHtml(&s, "<span class=\"diffchng\">");
507 sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
508 blob_append(pOut, s.zLine, s.n);
509 }
510 a += m;
511 b += m;
512 ma -= m;
513 mb -= m;
514 for(j=0; j<ma; j++){
515 s.n = 0;
516 sbsWriteLineno(&s, a+j);
517 sbsWriteHtml(&s, "<span class=\"diffrm\">");
518 sbsWriteText(&s, &A[a+j], SBS_PAD | SBS_ENDSPAN);
519 sbsWrite(&s, " <\n", 3);
520 blob_append(pOut, s.zLine, s.n);
 
521 }
522 a += ma;
523 for(j=0; j<mb; j++){
524 s.n = 0;
525 sbsWriteSpace(&s, width + 7);
526 sbsWrite(&s, " > ", 3);
527 sbsWriteLineno(&s, b+j);
528 sbsWriteHtml(&s, "<span class=\"diffadd\">");
529 sbsWriteText(&s, &B[b+j], SBS_NEWLINE | SBS_ENDSPAN);
530 blob_append(pOut, s.zLine, s.n);
531 }
532 b += mb;
533 if( i<nr-1 ){
534 m = R[r+i*3+3];
535 for(j=0; j<m; j++){
536 s.n = 0;
537 sbsWriteLineno(&s, a+j);
538 sbsWriteText(&s, &A[a+j], SBS_PAD);
539 sbsWrite(&s, " ", 3);
540 sbsWriteLineno(&s, b+j);
541 sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
542 blob_append(pOut, s.zLine, s.n);
543 }
544 b += m;
545 a += m;
546 }
547 }
@@ -474,19 +549,20 @@
549 /* Show the final common area */
550 assert( nr==i );
551 m = R[r+nr*3];
552 if( m>nContext ) m = nContext;
553 for(j=0; j<m; j++){
554 s.n = 0;
555 sbsWriteLineno(&s, a+j);
556 sbsWriteText(&s, &A[a+j], SBS_PAD);
557 sbsWrite(&s, " ", 3);
558 sbsWriteLineno(&s, b+j);
559 sbsWriteText(&s, &B[b+j], SBS_NEWLINE);
560 blob_append(pOut, s.zLine, s.n);
561 }
562 }
563 free(s.zLine);
564 }
565
566 /*
567 ** Compute the optimal longest common subsequence (LCS) using an
568 ** exhaustive search. This version of the LCS is only used for
@@ -789,11 +865,12 @@
865
866 if( pOut ){
867 /* Compute a context or side-by-side diff into pOut */
868 if( diffFlags & DIFF_SIDEBYSIDE ){
869 int width = diff_width(diffFlags);
870 int escHtml = (diffFlags & DIFF_HTML)!=0;
871 sbsDiff(&c, pOut, nContext, width, escHtml);
872 }else{
873 contextDiff(&c, pOut, nContext);
874 }
875 free(c.aFrom);
876 free(c.aTo);
@@ -851,10 +928,11 @@
928 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
929 f *= DIFF_CONTEXT_MASK+1;
930 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
931 diffFlags |= f;
932 }
933 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
934 return diffFlags;
935 }
936
937 /*
938 ** COMMAND: test-udiff
939
+9 -2
--- src/info.c
+++ src/info.c
@@ -268,12 +268,19 @@
268268
content_get(toid, &to);
269269
}else{
270270
blob_zero(&to);
271271
}
272272
blob_zero(&out);
273
- text_diff(&from, &to, &out, diffFlags);
274
- @ %h(blob_str(&out))
273
+ if( diffFlags & DIFF_SIDEBYSIDE ){
274
+ text_diff(&from, &to, &out, diffFlags | DIFF_HTML);
275
+ @ <div class="sbsdiff">
276
+ @ %s(blob_str(&out))
277
+ @ </div>
278
+ }else{
279
+ text_diff(&from, &to, &out, diffFlags);
280
+ @ %h(blob_str(&out))
281
+ }
275282
blob_reset(&from);
276283
blob_reset(&to);
277284
blob_reset(&out);
278285
}
279286
280287
--- src/info.c
+++ src/info.c
@@ -268,12 +268,19 @@
268 content_get(toid, &to);
269 }else{
270 blob_zero(&to);
271 }
272 blob_zero(&out);
273 text_diff(&from, &to, &out, diffFlags);
274 @ %h(blob_str(&out))
 
 
 
 
 
 
 
275 blob_reset(&from);
276 blob_reset(&to);
277 blob_reset(&out);
278 }
279
280
--- src/info.c
+++ src/info.c
@@ -268,12 +268,19 @@
268 content_get(toid, &to);
269 }else{
270 blob_zero(&to);
271 }
272 blob_zero(&out);
273 if( diffFlags & DIFF_SIDEBYSIDE ){
274 text_diff(&from, &to, &out, diffFlags | DIFF_HTML);
275 @ <div class="sbsdiff">
276 @ %s(blob_str(&out))
277 @ </div>
278 }else{
279 text_diff(&from, &to, &out, diffFlags);
280 @ %h(blob_str(&out))
281 }
282 blob_reset(&from);
283 blob_reset(&to);
284 blob_reset(&out);
285 }
286
287
+26
--- src/style.c
+++ src/style.c
@@ -751,10 +751,36 @@
751751
{ "ul.filelist",
752752
"List of files in a timeline",
753753
@ margin-top: 3px;
754754
@ line-height: 100%;
755755
},
756
+ { "div.sbsdiff",
757
+ "side-by-side diff display",
758
+ @ font-family: monospace;
759
+ @ white-space: pre;
760
+ },
761
+ { "div.udiff",
762
+ "unified diff display",
763
+ @ font-family: monospace;
764
+ @ white-space: pre;
765
+ },
766
+ { "span.diffchng",
767
+ "changes in a diff",
768
+ @ background-color: #d8d8d8;
769
+ },
770
+ { "span.diffadd",
771
+ "added code in a diff",
772
+ @ background-color: #d8ffd8;
773
+ },
774
+ { "span.diffrm",
775
+ "deleted in a diff",
776
+ @ background-color: #ffd8d8;
777
+ },
778
+ { "span.diffhr",
779
+ "suppressed lines in a diff",
780
+ @ color: #0000ff;
781
+ },
756782
{ 0,
757783
0,
758784
0
759785
}
760786
};
761787
--- src/style.c
+++ src/style.c
@@ -751,10 +751,36 @@
751 { "ul.filelist",
752 "List of files in a timeline",
753 @ margin-top: 3px;
754 @ line-height: 100%;
755 },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
756 { 0,
757 0,
758 0
759 }
760 };
761
--- src/style.c
+++ src/style.c
@@ -751,10 +751,36 @@
751 { "ul.filelist",
752 "List of files in a timeline",
753 @ margin-top: 3px;
754 @ line-height: 100%;
755 },
756 { "div.sbsdiff",
757 "side-by-side diff display",
758 @ font-family: monospace;
759 @ white-space: pre;
760 },
761 { "div.udiff",
762 "unified diff display",
763 @ font-family: monospace;
764 @ white-space: pre;
765 },
766 { "span.diffchng",
767 "changes in a diff",
768 @ background-color: #d8d8d8;
769 },
770 { "span.diffadd",
771 "added code in a diff",
772 @ background-color: #d8ffd8;
773 },
774 { "span.diffrm",
775 "deleted in a diff",
776 @ background-color: #ffd8d8;
777 },
778 { "span.diffhr",
779 "suppressed lines in a diff",
780 @ color: #0000ff;
781 },
782 { 0,
783 0,
784 0
785 }
786 };
787

Keyboard Shortcuts

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