Fossil SCM
Rework the side-by-side diff so that it handles tab characters. Fix an off-by-one error in the line numbers of side-by-side diffs.
Commit
8670373321604573901fad634ef7fb2cef2ca46d
Parent
ab47cc73d7d95c7…
1 file changed
+70
-51
+70
-51
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -299,35 +299,40 @@ | ||
| 299 | 299 | } |
| 300 | 300 | } |
| 301 | 301 | } |
| 302 | 302 | |
| 303 | 303 | /* |
| 304 | -** Append spaces to a blob | |
| 304 | +** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to | |
| 305 | +** have space for at least 7 characters. | |
| 305 | 306 | */ |
| 306 | -static void appendSpace(Blob *pOut, int n){ | |
| 307 | - const char z100[101] = | |
| 308 | - " " | |
| 309 | - " "; | |
| 310 | - while( n>100 ){ | |
| 311 | - blob_append(pOut, z100, 100); n -= 100; | |
| 312 | - } | |
| 313 | - if( n>0 ){ | |
| 314 | - blob_append(pOut, z100, n); | |
| 315 | - } | |
| 307 | +static void sbsWriteLineno(char *z, int ln){ | |
| 308 | + sqlite3_snprintf(7, z, "%6d", ln+1); | |
| 309 | + z[6] = ' '; | |
| 316 | 310 | } |
| 317 | 311 | |
| 318 | 312 | /* |
| 319 | -** Append text to a sbs diff output | |
| 313 | +** Write up to width characters of pLine into z[]. Translate tabs into | |
| 314 | +** spaces. If trunc is true, then append \n\000 after the last character | |
| 315 | +** written. | |
| 320 | 316 | */ |
| 321 | -static void appendSbsLine(Blob *pOut, DLine *pLine, int width, int pad){ | |
| 322 | - int sz = pLine->h & LENGTH_MASK; | |
| 323 | - if( sz<width ){ | |
| 324 | - blob_append(pOut, pLine->z, sz); | |
| 325 | - if( pad ) appendSpace(pOut, width-sz); | |
| 326 | - }else{ | |
| 327 | - blob_append(pOut, pLine->z, width); | |
| 328 | - } | |
| 317 | +static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){ | |
| 318 | + int n = pLine->h & LENGTH_MASK; | |
| 319 | + int i, j; | |
| 320 | + const char *zIn = pLine->z; | |
| 321 | + for(i=j=0; i<n && j<width; i++){ | |
| 322 | + if( zIn[i]=='\t' ){ | |
| 323 | + z[j++] = ' '; | |
| 324 | + while( (j&7)!=0 && j<width ) z[j++] = ' '; | |
| 325 | + }else{ | |
| 326 | + z[j++] = zIn[i]; | |
| 327 | + } | |
| 328 | + } | |
| 329 | + if( trunc ){ | |
| 330 | + z[j++] = '\n'; | |
| 331 | + z[j] = 0; | |
| 332 | + } | |
| 333 | + return j; | |
| 329 | 334 | } |
| 330 | 335 | |
| 331 | 336 | |
| 332 | 337 | /* |
| 333 | 338 | ** Given a diff context in which the aEdit[] array has been filled |
| @@ -344,15 +349,18 @@ | ||
| 344 | 349 | int mxr; /* Maximum value for r */ |
| 345 | 350 | int na, nb; /* Number of lines shown from A and B */ |
| 346 | 351 | int i, j; /* Loop counters */ |
| 347 | 352 | int m, ma, mb;/* Number of lines to output */ |
| 348 | 353 | int skip; /* Number of lines to skip */ |
| 349 | - char zFormat[50]; /* Output format */ | |
| 354 | + int mxLine; /* Length of a line of text */ | |
| 355 | + char *zLine; /* A line of text being formatted */ | |
| 356 | + int len; /* Length of an output line */ | |
| 350 | 357 | |
| 351 | - sqlite3_snprintf(sizeof(zFormat), zFormat, | |
| 352 | - "%%4d %%%d.%ds %%c %%4d %%.%ds\n", | |
| 353 | - width, width, width); | |
| 358 | + mxLine = width*2 + 2*7 + 3 + 1; | |
| 359 | + zLine = fossil_malloc( mxLine + 1 ); | |
| 360 | + if( zLine==0 ) return; | |
| 361 | + zLine[mxLine] = 0; | |
| 354 | 362 | A = p->aFrom; |
| 355 | 363 | B = p->aTo; |
| 356 | 364 | R = p->aEdit; |
| 357 | 365 | mxr = p->nEdit; |
| 358 | 366 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -396,15 +404,16 @@ | ||
| 396 | 404 | /* Show the initial common area */ |
| 397 | 405 | a += skip; |
| 398 | 406 | b += skip; |
| 399 | 407 | m = R[r] - skip; |
| 400 | 408 | for(j=0; j<m; j++){ |
| 401 | - blob_appendf(pOut, "%6d ", a+j); | |
| 402 | - appendSbsLine(pOut, &A[a+j], width, 1); | |
| 403 | - blob_appendf(pOut, " %6d ", b+j); | |
| 404 | - appendSbsLine(pOut, &B[b+j], width, 0); | |
| 405 | - blob_append(pOut, "\n", 1); | |
| 409 | + memset(zLine, ' ', mxLine); | |
| 410 | + sbsWriteLineno(zLine, a+j); | |
| 411 | + sbsWriteText(&zLine[7], &A[a+j], width, 0); | |
| 412 | + sbsWriteLineno(&zLine[width+10], b+j); | |
| 413 | + len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | |
| 414 | + blob_append(pOut, zLine, len+width+17); | |
| 406 | 415 | } |
| 407 | 416 | a += m; |
| 408 | 417 | b += m; |
| 409 | 418 | |
| 410 | 419 | /* Show the differences */ |
| @@ -411,41 +420,49 @@ | ||
| 411 | 420 | for(i=0; i<nr; i++){ |
| 412 | 421 | ma = R[r+i*3+1]; |
| 413 | 422 | mb = R[r+i*3+2]; |
| 414 | 423 | m = ma<mb ? ma : mb; |
| 415 | 424 | for(j=0; j<m; j++){ |
| 416 | - blob_appendf(pOut, "%6d ", a+j); | |
| 417 | - appendSbsLine(pOut, &A[a+j], width, 1); | |
| 418 | - blob_appendf(pOut, " | %6d ", b+j); | |
| 419 | - appendSbsLine(pOut, &B[b+j], width, 0); | |
| 420 | - blob_append(pOut, "\n", 1); | |
| 425 | + memset(zLine, ' ', mxLine); | |
| 426 | + sbsWriteLineno(zLine, a+j); | |
| 427 | + sbsWriteText(&zLine[7], &A[a+j], width, 0); | |
| 428 | + zLine[width+8] = '|'; | |
| 429 | + sbsWriteLineno(&zLine[width+10], b+j); | |
| 430 | + len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | |
| 431 | + blob_append(pOut, zLine, len+width+17); | |
| 421 | 432 | } |
| 422 | 433 | a += m; |
| 423 | 434 | b += m; |
| 424 | 435 | ma -= m; |
| 425 | 436 | mb -= m; |
| 426 | 437 | for(j=0; j<ma; j++){ |
| 427 | - blob_appendf(pOut, "%6d ", a+j); | |
| 428 | - appendSbsLine(pOut, &A[a+j], width, 1); | |
| 429 | - blob_append(pOut, " <\n", 3); | |
| 438 | + memset(zLine, ' ', width+7); | |
| 439 | + sbsWriteLineno(zLine, a+j); | |
| 440 | + sbsWriteText(&zLine[7], &A[a+j], width, 0); | |
| 441 | + zLine[width+8] = '<'; | |
| 442 | + zLine[width+9] = '\n'; | |
| 443 | + zLine[width+10] = 0; | |
| 444 | + blob_append(pOut, zLine, width+10); | |
| 430 | 445 | } |
| 431 | 446 | a += ma; |
| 432 | 447 | for(j=0; j<mb; j++){ |
| 433 | - appendSpace(pOut, width+7); | |
| 434 | - blob_appendf(pOut, " > %6d ", b+j); | |
| 435 | - appendSbsLine(pOut, &B[b+j], width, 0); | |
| 436 | - blob_append(pOut, "\n", 1); | |
| 448 | + memset(zLine, ' ', mxLine); | |
| 449 | + zLine[width+8] = '>'; | |
| 450 | + sbsWriteLineno(&zLine[width+10], b+j); | |
| 451 | + len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | |
| 452 | + blob_append(pOut, zLine, len+width+17); | |
| 437 | 453 | } |
| 438 | 454 | b += mb; |
| 439 | 455 | if( i<nr-1 ){ |
| 440 | 456 | m = R[r+i*3+3]; |
| 441 | 457 | for(j=0; j<m; j++){ |
| 442 | - blob_appendf(pOut, "%6d ", a+j); | |
| 443 | - appendSbsLine(pOut, &A[a+j], width, 1); | |
| 444 | - blob_appendf(pOut, " %6d ", b+j); | |
| 445 | - appendSbsLine(pOut, &B[b+j], width, 0); | |
| 446 | - blob_append(pOut, "\n", 1); | |
| 458 | + memset(zLine, ' ', mxLine); | |
| 459 | + sbsWriteLineno(zLine, a+j); | |
| 460 | + sbsWriteText(&zLine[7], &A[a+j], width, 0); | |
| 461 | + sbsWriteLineno(&zLine[width+10], b+j); | |
| 462 | + len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | |
| 463 | + blob_append(pOut, zLine, len+width+17); | |
| 447 | 464 | } |
| 448 | 465 | b += m; |
| 449 | 466 | a += m; |
| 450 | 467 | } |
| 451 | 468 | } |
| @@ -453,17 +470,19 @@ | ||
| 453 | 470 | /* Show the final common area */ |
| 454 | 471 | assert( nr==i ); |
| 455 | 472 | m = R[r+nr*3]; |
| 456 | 473 | if( m>nContext ) m = nContext; |
| 457 | 474 | for(j=0; j<m; j++){ |
| 458 | - blob_appendf(pOut, "%6d ", a+j); | |
| 459 | - appendSbsLine(pOut, &A[a+j], width, 1); | |
| 460 | - blob_appendf(pOut, " %6d ", b+j); | |
| 461 | - appendSbsLine(pOut, &B[b+j], width, 0); | |
| 462 | - blob_append(pOut, "\n", 1); | |
| 475 | + memset(zLine, ' ', mxLine); | |
| 476 | + sbsWriteLineno(zLine, a+j); | |
| 477 | + sbsWriteText(&zLine[7], &A[a+j], width, 0); | |
| 478 | + sbsWriteLineno(&zLine[width+10], b+j); | |
| 479 | + len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | |
| 480 | + blob_append(pOut, zLine, len+width+17); | |
| 463 | 481 | } |
| 464 | 482 | } |
| 483 | + free(zLine); | |
| 465 | 484 | } |
| 466 | 485 | |
| 467 | 486 | /* |
| 468 | 487 | ** Compute the optimal longest common subsequence (LCS) using an |
| 469 | 488 | ** exhaustive search. This version of the LCS is only used for |
| 470 | 489 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -299,35 +299,40 @@ | |
| 299 | } |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | /* |
| 304 | ** Append spaces to a blob |
| 305 | */ |
| 306 | static void appendSpace(Blob *pOut, int n){ |
| 307 | const char z100[101] = |
| 308 | " " |
| 309 | " "; |
| 310 | while( n>100 ){ |
| 311 | blob_append(pOut, z100, 100); n -= 100; |
| 312 | } |
| 313 | if( n>0 ){ |
| 314 | blob_append(pOut, z100, n); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | /* |
| 319 | ** Append text to a sbs diff output |
| 320 | */ |
| 321 | static void appendSbsLine(Blob *pOut, DLine *pLine, int width, int pad){ |
| 322 | int sz = pLine->h & LENGTH_MASK; |
| 323 | if( sz<width ){ |
| 324 | blob_append(pOut, pLine->z, sz); |
| 325 | if( pad ) appendSpace(pOut, width-sz); |
| 326 | }else{ |
| 327 | blob_append(pOut, pLine->z, width); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | |
| 332 | /* |
| 333 | ** Given a diff context in which the aEdit[] array has been filled |
| @@ -344,15 +349,18 @@ | |
| 344 | int mxr; /* Maximum value for r */ |
| 345 | int na, nb; /* Number of lines shown from A and B */ |
| 346 | int i, j; /* Loop counters */ |
| 347 | int m, ma, mb;/* Number of lines to output */ |
| 348 | int skip; /* Number of lines to skip */ |
| 349 | char zFormat[50]; /* Output format */ |
| 350 | |
| 351 | sqlite3_snprintf(sizeof(zFormat), zFormat, |
| 352 | "%%4d %%%d.%ds %%c %%4d %%.%ds\n", |
| 353 | width, width, width); |
| 354 | A = p->aFrom; |
| 355 | B = p->aTo; |
| 356 | R = p->aEdit; |
| 357 | mxr = p->nEdit; |
| 358 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -396,15 +404,16 @@ | |
| 396 | /* Show the initial common area */ |
| 397 | a += skip; |
| 398 | b += skip; |
| 399 | m = R[r] - skip; |
| 400 | for(j=0; j<m; j++){ |
| 401 | blob_appendf(pOut, "%6d ", a+j); |
| 402 | appendSbsLine(pOut, &A[a+j], width, 1); |
| 403 | blob_appendf(pOut, " %6d ", b+j); |
| 404 | appendSbsLine(pOut, &B[b+j], width, 0); |
| 405 | blob_append(pOut, "\n", 1); |
| 406 | } |
| 407 | a += m; |
| 408 | b += m; |
| 409 | |
| 410 | /* Show the differences */ |
| @@ -411,41 +420,49 @@ | |
| 411 | for(i=0; i<nr; i++){ |
| 412 | ma = R[r+i*3+1]; |
| 413 | mb = R[r+i*3+2]; |
| 414 | m = ma<mb ? ma : mb; |
| 415 | for(j=0; j<m; j++){ |
| 416 | blob_appendf(pOut, "%6d ", a+j); |
| 417 | appendSbsLine(pOut, &A[a+j], width, 1); |
| 418 | blob_appendf(pOut, " | %6d ", b+j); |
| 419 | appendSbsLine(pOut, &B[b+j], width, 0); |
| 420 | blob_append(pOut, "\n", 1); |
| 421 | } |
| 422 | a += m; |
| 423 | b += m; |
| 424 | ma -= m; |
| 425 | mb -= m; |
| 426 | for(j=0; j<ma; j++){ |
| 427 | blob_appendf(pOut, "%6d ", a+j); |
| 428 | appendSbsLine(pOut, &A[a+j], width, 1); |
| 429 | blob_append(pOut, " <\n", 3); |
| 430 | } |
| 431 | a += ma; |
| 432 | for(j=0; j<mb; j++){ |
| 433 | appendSpace(pOut, width+7); |
| 434 | blob_appendf(pOut, " > %6d ", b+j); |
| 435 | appendSbsLine(pOut, &B[b+j], width, 0); |
| 436 | blob_append(pOut, "\n", 1); |
| 437 | } |
| 438 | b += mb; |
| 439 | if( i<nr-1 ){ |
| 440 | m = R[r+i*3+3]; |
| 441 | for(j=0; j<m; j++){ |
| 442 | blob_appendf(pOut, "%6d ", a+j); |
| 443 | appendSbsLine(pOut, &A[a+j], width, 1); |
| 444 | blob_appendf(pOut, " %6d ", b+j); |
| 445 | appendSbsLine(pOut, &B[b+j], width, 0); |
| 446 | blob_append(pOut, "\n", 1); |
| 447 | } |
| 448 | b += m; |
| 449 | a += m; |
| 450 | } |
| 451 | } |
| @@ -453,17 +470,19 @@ | |
| 453 | /* Show the final common area */ |
| 454 | assert( nr==i ); |
| 455 | m = R[r+nr*3]; |
| 456 | if( m>nContext ) m = nContext; |
| 457 | for(j=0; j<m; j++){ |
| 458 | blob_appendf(pOut, "%6d ", a+j); |
| 459 | appendSbsLine(pOut, &A[a+j], width, 1); |
| 460 | blob_appendf(pOut, " %6d ", b+j); |
| 461 | appendSbsLine(pOut, &B[b+j], width, 0); |
| 462 | blob_append(pOut, "\n", 1); |
| 463 | } |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | /* |
| 468 | ** Compute the optimal longest common subsequence (LCS) using an |
| 469 | ** exhaustive search. This version of the LCS is only used for |
| 470 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -299,35 +299,40 @@ | |
| 299 | } |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | /* |
| 304 | ** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to |
| 305 | ** have space for at least 7 characters. |
| 306 | */ |
| 307 | static void sbsWriteLineno(char *z, int ln){ |
| 308 | sqlite3_snprintf(7, z, "%6d", ln+1); |
| 309 | z[6] = ' '; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | ** Write up to width characters of pLine into z[]. Translate tabs into |
| 314 | ** spaces. If trunc is true, then append \n\000 after the last character |
| 315 | ** written. |
| 316 | */ |
| 317 | static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){ |
| 318 | int n = pLine->h & LENGTH_MASK; |
| 319 | int i, j; |
| 320 | const char *zIn = pLine->z; |
| 321 | for(i=j=0; i<n && j<width; i++){ |
| 322 | if( zIn[i]=='\t' ){ |
| 323 | z[j++] = ' '; |
| 324 | while( (j&7)!=0 && j<width ) z[j++] = ' '; |
| 325 | }else{ |
| 326 | z[j++] = zIn[i]; |
| 327 | } |
| 328 | } |
| 329 | if( trunc ){ |
| 330 | z[j++] = '\n'; |
| 331 | z[j] = 0; |
| 332 | } |
| 333 | return j; |
| 334 | } |
| 335 | |
| 336 | |
| 337 | /* |
| 338 | ** Given a diff context in which the aEdit[] array has been filled |
| @@ -344,15 +349,18 @@ | |
| 349 | int mxr; /* Maximum value for r */ |
| 350 | int na, nb; /* Number of lines shown from A and B */ |
| 351 | int i, j; /* Loop counters */ |
| 352 | int m, ma, mb;/* Number of lines to output */ |
| 353 | int skip; /* Number of lines to skip */ |
| 354 | int mxLine; /* Length of a line of text */ |
| 355 | char *zLine; /* A line of text being formatted */ |
| 356 | int len; /* Length of an output line */ |
| 357 | |
| 358 | mxLine = width*2 + 2*7 + 3 + 1; |
| 359 | zLine = fossil_malloc( mxLine + 1 ); |
| 360 | if( zLine==0 ) return; |
| 361 | zLine[mxLine] = 0; |
| 362 | A = p->aFrom; |
| 363 | B = p->aTo; |
| 364 | R = p->aEdit; |
| 365 | mxr = p->nEdit; |
| 366 | while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } |
| @@ -396,15 +404,16 @@ | |
| 404 | /* Show the initial common area */ |
| 405 | a += skip; |
| 406 | b += skip; |
| 407 | m = R[r] - skip; |
| 408 | for(j=0; j<m; j++){ |
| 409 | memset(zLine, ' ', mxLine); |
| 410 | sbsWriteLineno(zLine, a+j); |
| 411 | sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 412 | sbsWriteLineno(&zLine[width+10], b+j); |
| 413 | len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 414 | blob_append(pOut, zLine, len+width+17); |
| 415 | } |
| 416 | a += m; |
| 417 | b += m; |
| 418 | |
| 419 | /* Show the differences */ |
| @@ -411,41 +420,49 @@ | |
| 420 | for(i=0; i<nr; i++){ |
| 421 | ma = R[r+i*3+1]; |
| 422 | mb = R[r+i*3+2]; |
| 423 | m = ma<mb ? ma : mb; |
| 424 | for(j=0; j<m; j++){ |
| 425 | memset(zLine, ' ', mxLine); |
| 426 | sbsWriteLineno(zLine, a+j); |
| 427 | sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 428 | zLine[width+8] = '|'; |
| 429 | sbsWriteLineno(&zLine[width+10], b+j); |
| 430 | len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 431 | blob_append(pOut, zLine, len+width+17); |
| 432 | } |
| 433 | a += m; |
| 434 | b += m; |
| 435 | ma -= m; |
| 436 | mb -= m; |
| 437 | for(j=0; j<ma; j++){ |
| 438 | memset(zLine, ' ', width+7); |
| 439 | sbsWriteLineno(zLine, a+j); |
| 440 | sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 441 | zLine[width+8] = '<'; |
| 442 | zLine[width+9] = '\n'; |
| 443 | zLine[width+10] = 0; |
| 444 | blob_append(pOut, zLine, width+10); |
| 445 | } |
| 446 | a += ma; |
| 447 | for(j=0; j<mb; j++){ |
| 448 | memset(zLine, ' ', mxLine); |
| 449 | zLine[width+8] = '>'; |
| 450 | sbsWriteLineno(&zLine[width+10], b+j); |
| 451 | len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 452 | blob_append(pOut, zLine, len+width+17); |
| 453 | } |
| 454 | b += mb; |
| 455 | if( i<nr-1 ){ |
| 456 | m = R[r+i*3+3]; |
| 457 | for(j=0; j<m; j++){ |
| 458 | memset(zLine, ' ', mxLine); |
| 459 | sbsWriteLineno(zLine, a+j); |
| 460 | sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 461 | sbsWriteLineno(&zLine[width+10], b+j); |
| 462 | len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 463 | blob_append(pOut, zLine, len+width+17); |
| 464 | } |
| 465 | b += m; |
| 466 | a += m; |
| 467 | } |
| 468 | } |
| @@ -453,17 +470,19 @@ | |
| 470 | /* Show the final common area */ |
| 471 | assert( nr==i ); |
| 472 | m = R[r+nr*3]; |
| 473 | if( m>nContext ) m = nContext; |
| 474 | for(j=0; j<m; j++){ |
| 475 | memset(zLine, ' ', mxLine); |
| 476 | sbsWriteLineno(zLine, a+j); |
| 477 | sbsWriteText(&zLine[7], &A[a+j], width, 0); |
| 478 | sbsWriteLineno(&zLine[width+10], b+j); |
| 479 | len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); |
| 480 | blob_append(pOut, zLine, len+width+17); |
| 481 | } |
| 482 | } |
| 483 | free(zLine); |
| 484 | } |
| 485 | |
| 486 | /* |
| 487 | ** Compute the optimal longest common subsequence (LCS) using an |
| 488 | ** exhaustive search. This version of the LCS is only used for |
| 489 |