Fossil SCM
Begin inserting code to implement an "annotate" command.
Commit
9b68bc33bd40d979bd762fff98b3257d9cdf5ab8
Parent
e81cc91aa402489…
1 file changed
+155
+155
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -549,5 +549,160 @@ | ||
| 549 | 549 | blob_read_from_file(&b, g.argv[3]); |
| 550 | 550 | blob_zero(&out); |
| 551 | 551 | text_diff(&a, &b, &out, 3); |
| 552 | 552 | blob_write_to_file(&out, "-"); |
| 553 | 553 | } |
| 554 | + | |
| 555 | +/************************************************************************** | |
| 556 | +** The basic difference engine is above. What follows is the annotation | |
| 557 | +** engine. Both are in the same file since they share many components. | |
| 558 | +*/ | |
| 559 | + | |
| 560 | +/* | |
| 561 | +** The status of an annotation operation is recorded by an instance | |
| 562 | +** of the following structure. | |
| 563 | +*/ | |
| 564 | +typedef struct Annotator Annotator; | |
| 565 | +struct Annotator { | |
| 566 | + DContext c; /* The diff-engine context */ | |
| 567 | + Blob blobTo; /* Blob to free at next step */ | |
| 568 | + int nOrig; /* Number of lines in original file */ | |
| 569 | + int nNoSrc; /* Number of uncompleted aOrig[].zSrc entries */ | |
| 570 | + struct { /* Lines of the original files... */ | |
| 571 | + const char *z; /* The text of the line */ | |
| 572 | + int n; /* Number of bytes (omitting trailing space and \n) */ | |
| 573 | + const char *zSrc; /* Tag showing origin of this line */ | |
| 574 | + } *aOrig; | |
| 575 | + int *aMap; /* Map lines for c.aTo into aOrig */ | |
| 576 | +}; | |
| 577 | + | |
| 578 | +/* | |
| 579 | +** Initialize the annotation process by specifying the file that is | |
| 580 | +** to be annotated. The annotator takes control of the input Blob and | |
| 581 | +** will release it when it is finished with it. | |
| 582 | +*/ | |
| 583 | +static int annotation_start(Annotator *p, Blob *pInput){ | |
| 584 | + int i; | |
| 585 | + | |
| 586 | + memset(p, 0, sizeof(*p)); | |
| 587 | + p->c.aTo = break_into_lines(blob_str(pInput), &p->c.nTo); | |
| 588 | + if( p->c.aTo==0 ){ | |
| 589 | + return 1; | |
| 590 | + } | |
| 591 | + p->aMap = malloc( sizeof(int)*p->c.nTo ); | |
| 592 | + if( p->aMap==0 ) fossil_panic("out of memory"); | |
| 593 | + for(i=0; i<p->c.nTo; i++) p->aMap[i] = i; | |
| 594 | + p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo ); | |
| 595 | + if( p->aOrig==0 ) fossil_panic("out of memory"); | |
| 596 | + for(i=0; i<p->c.nTo; i++){ | |
| 597 | + p->aOrig[i].z = p->c.aTo[i].z; | |
| 598 | + p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; | |
| 599 | + p->aOrig[i].zSrc = 0; | |
| 600 | + } | |
| 601 | + p->nOrig = p->c.nTo; | |
| 602 | + p->nNoSrc = p->c.nTo; | |
| 603 | + return 0; | |
| 604 | +} | |
| 605 | + | |
| 606 | +/* | |
| 607 | +** The input pParent is the next most recent ancestor of the file | |
| 608 | +** being annotated. Do another step of the annotation. Return true | |
| 609 | +** if additional annotation is required. zPName is the tag to insert | |
| 610 | +** on each line of the file being annotated that was contributed by | |
| 611 | +** pParent. Memory to hold zPName is leaked. | |
| 612 | +*/ | |
| 613 | +static int annotation_step(Annotator *p, Blob *pParent, char *zPName){ | |
| 614 | + int i, j; | |
| 615 | + int lnTo, lnFrom; | |
| 616 | + int *aFromMap; | |
| 617 | + | |
| 618 | + /* Prepare the parent file to be diffed */ | |
| 619 | + p->c.aFrom = break_into_lines(blob_str(pParent), &p->c.nFrom); | |
| 620 | + if( p->c.aFrom==0 ){ | |
| 621 | + return 1; | |
| 622 | + } | |
| 623 | + | |
| 624 | + /* Compute the differences going from pParent to the last file | |
| 625 | + ** processed */ | |
| 626 | + diff_all(&p->c); | |
| 627 | + | |
| 628 | + /* Where new lines are inserted on this difference, record the | |
| 629 | + ** zPName as the source of the new line. | |
| 630 | + */ | |
| 631 | + for(i=lnTo=0; i<p->c.nEdit; i+=3){ | |
| 632 | + lnTo += p->c.aEdit[i]; | |
| 633 | + for(j=0; j<p->c.aEdit[i+2]; j++, lnTo++){ | |
| 634 | + int x = p->aMap[lnTo]; | |
| 635 | + if( x>=0 && p->aOrig[x].zSrc==0 ){ | |
| 636 | + p->aOrig[x].zSrc = zPName; | |
| 637 | + p->nNoSrc--; | |
| 638 | + } | |
| 639 | + } | |
| 640 | + } | |
| 641 | + | |
| 642 | + /* We will be converting aFrom into aTo for the next step. Compute | |
| 643 | + ** a map from the aFrom into the original file being annotated. | |
| 644 | + */ | |
| 645 | + aFromMap = malloc( sizeof(int)*p->c.nFrom ); | |
| 646 | + if( aFromMap==0 ){ | |
| 647 | + fossil_panic("out of memory"); | |
| 648 | + } | |
| 649 | + for(i=lnTo=lnFrom=0; i<p->c.nEdit; i+=3){ | |
| 650 | + for(j=0; j<p->c.aEdit[i]; j++){ | |
| 651 | + aFromMap[lnFrom++] = p->aMap[lnTo++]; | |
| 652 | + } | |
| 653 | + for(j=0; j<p->c.aEdit[i+1]; j++){ | |
| 654 | + aFromMap[lnFrom++] = -1; | |
| 655 | + } | |
| 656 | + lnTo += p->c.aEdit[i+2]; | |
| 657 | + } | |
| 658 | + assert( lnFrom==p->c.nFrom ); | |
| 659 | + free(p->aMap); | |
| 660 | + p->aMap = aFromMap; | |
| 661 | + | |
| 662 | + /* Clear out the diff results */ | |
| 663 | + free(p->c.aEdit); | |
| 664 | + p->c.aEdit = 0; | |
| 665 | + p->c.nEdit = 0; | |
| 666 | + p->c.nEditAlloc = 0; | |
| 667 | + | |
| 668 | + /* Move aFrom over to aTo in preparation for the next step */ | |
| 669 | + free(p->c.aTo); | |
| 670 | + if( blob_buffer(&p->blobTo) ) blob_reset(&p->blobTo); | |
| 671 | + p->blobTo = *pParent; | |
| 672 | + blob_zero(pParent); | |
| 673 | + p->c.aTo = p->c.aFrom; | |
| 674 | + p->c.nTo = p->c.nFrom; | |
| 675 | + | |
| 676 | + /* Return no errors */ | |
| 677 | + return 0; | |
| 678 | +} | |
| 679 | + | |
| 680 | + | |
| 681 | +/* | |
| 682 | +** COMMAND: test-annotate-step | |
| 683 | +*/ | |
| 684 | +void test_annotate_step_cmd(void){ | |
| 685 | + Blob orig, b; | |
| 686 | + Annotator x; | |
| 687 | + int i; | |
| 688 | + | |
| 689 | + if( g.argc<4 ) usage("RID1 RID2 ..."); | |
| 690 | + db_must_be_within_tree(); | |
| 691 | + blob_zero(&b); | |
| 692 | + content_get(name_to_rid(g.argv[2]), &orig); | |
| 693 | + if( annotation_start(&x, &orig) ){ | |
| 694 | + fossil_fatal("binary file"); | |
| 695 | + } | |
| 696 | + for(i=3; i<g.argc; i++){ | |
| 697 | + blob_zero(&b); | |
| 698 | + content_get(name_to_rid(g.argv[i]), &b); | |
| 699 | + if( annotation_step(&x, &b, g.argv[i-1]) ){ | |
| 700 | + fossil_fatal("binary file"); | |
| 701 | + } | |
| 702 | + } | |
| 703 | + for(i=0; i<x.nOrig; i++){ | |
| 704 | + const char *zSrc = x.aOrig[i].zSrc; | |
| 705 | + if( zSrc==0 ) zSrc = g.argv[g.argc-1]; | |
| 706 | + printf("%10s: %.*s\n", zSrc, x.aOrig[i].n, x.aOrig[i].z); | |
| 707 | + } | |
| 708 | +} | |
| 554 | 709 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -549,5 +549,160 @@ | |
| 549 | blob_read_from_file(&b, g.argv[3]); |
| 550 | blob_zero(&out); |
| 551 | text_diff(&a, &b, &out, 3); |
| 552 | blob_write_to_file(&out, "-"); |
| 553 | } |
| 554 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -549,5 +549,160 @@ | |
| 549 | blob_read_from_file(&b, g.argv[3]); |
| 550 | blob_zero(&out); |
| 551 | text_diff(&a, &b, &out, 3); |
| 552 | blob_write_to_file(&out, "-"); |
| 553 | } |
| 554 | |
| 555 | /************************************************************************** |
| 556 | ** The basic difference engine is above. What follows is the annotation |
| 557 | ** engine. Both are in the same file since they share many components. |
| 558 | */ |
| 559 | |
| 560 | /* |
| 561 | ** The status of an annotation operation is recorded by an instance |
| 562 | ** of the following structure. |
| 563 | */ |
| 564 | typedef struct Annotator Annotator; |
| 565 | struct Annotator { |
| 566 | DContext c; /* The diff-engine context */ |
| 567 | Blob blobTo; /* Blob to free at next step */ |
| 568 | int nOrig; /* Number of lines in original file */ |
| 569 | int nNoSrc; /* Number of uncompleted aOrig[].zSrc entries */ |
| 570 | struct { /* Lines of the original files... */ |
| 571 | const char *z; /* The text of the line */ |
| 572 | int n; /* Number of bytes (omitting trailing space and \n) */ |
| 573 | const char *zSrc; /* Tag showing origin of this line */ |
| 574 | } *aOrig; |
| 575 | int *aMap; /* Map lines for c.aTo into aOrig */ |
| 576 | }; |
| 577 | |
| 578 | /* |
| 579 | ** Initialize the annotation process by specifying the file that is |
| 580 | ** to be annotated. The annotator takes control of the input Blob and |
| 581 | ** will release it when it is finished with it. |
| 582 | */ |
| 583 | static int annotation_start(Annotator *p, Blob *pInput){ |
| 584 | int i; |
| 585 | |
| 586 | memset(p, 0, sizeof(*p)); |
| 587 | p->c.aTo = break_into_lines(blob_str(pInput), &p->c.nTo); |
| 588 | if( p->c.aTo==0 ){ |
| 589 | return 1; |
| 590 | } |
| 591 | p->aMap = malloc( sizeof(int)*p->c.nTo ); |
| 592 | if( p->aMap==0 ) fossil_panic("out of memory"); |
| 593 | for(i=0; i<p->c.nTo; i++) p->aMap[i] = i; |
| 594 | p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo ); |
| 595 | if( p->aOrig==0 ) fossil_panic("out of memory"); |
| 596 | for(i=0; i<p->c.nTo; i++){ |
| 597 | p->aOrig[i].z = p->c.aTo[i].z; |
| 598 | p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; |
| 599 | p->aOrig[i].zSrc = 0; |
| 600 | } |
| 601 | p->nOrig = p->c.nTo; |
| 602 | p->nNoSrc = p->c.nTo; |
| 603 | return 0; |
| 604 | } |
| 605 | |
| 606 | /* |
| 607 | ** The input pParent is the next most recent ancestor of the file |
| 608 | ** being annotated. Do another step of the annotation. Return true |
| 609 | ** if additional annotation is required. zPName is the tag to insert |
| 610 | ** on each line of the file being annotated that was contributed by |
| 611 | ** pParent. Memory to hold zPName is leaked. |
| 612 | */ |
| 613 | static int annotation_step(Annotator *p, Blob *pParent, char *zPName){ |
| 614 | int i, j; |
| 615 | int lnTo, lnFrom; |
| 616 | int *aFromMap; |
| 617 | |
| 618 | /* Prepare the parent file to be diffed */ |
| 619 | p->c.aFrom = break_into_lines(blob_str(pParent), &p->c.nFrom); |
| 620 | if( p->c.aFrom==0 ){ |
| 621 | return 1; |
| 622 | } |
| 623 | |
| 624 | /* Compute the differences going from pParent to the last file |
| 625 | ** processed */ |
| 626 | diff_all(&p->c); |
| 627 | |
| 628 | /* Where new lines are inserted on this difference, record the |
| 629 | ** zPName as the source of the new line. |
| 630 | */ |
| 631 | for(i=lnTo=0; i<p->c.nEdit; i+=3){ |
| 632 | lnTo += p->c.aEdit[i]; |
| 633 | for(j=0; j<p->c.aEdit[i+2]; j++, lnTo++){ |
| 634 | int x = p->aMap[lnTo]; |
| 635 | if( x>=0 && p->aOrig[x].zSrc==0 ){ |
| 636 | p->aOrig[x].zSrc = zPName; |
| 637 | p->nNoSrc--; |
| 638 | } |
| 639 | } |
| 640 | } |
| 641 | |
| 642 | /* We will be converting aFrom into aTo for the next step. Compute |
| 643 | ** a map from the aFrom into the original file being annotated. |
| 644 | */ |
| 645 | aFromMap = malloc( sizeof(int)*p->c.nFrom ); |
| 646 | if( aFromMap==0 ){ |
| 647 | fossil_panic("out of memory"); |
| 648 | } |
| 649 | for(i=lnTo=lnFrom=0; i<p->c.nEdit; i+=3){ |
| 650 | for(j=0; j<p->c.aEdit[i]; j++){ |
| 651 | aFromMap[lnFrom++] = p->aMap[lnTo++]; |
| 652 | } |
| 653 | for(j=0; j<p->c.aEdit[i+1]; j++){ |
| 654 | aFromMap[lnFrom++] = -1; |
| 655 | } |
| 656 | lnTo += p->c.aEdit[i+2]; |
| 657 | } |
| 658 | assert( lnFrom==p->c.nFrom ); |
| 659 | free(p->aMap); |
| 660 | p->aMap = aFromMap; |
| 661 | |
| 662 | /* Clear out the diff results */ |
| 663 | free(p->c.aEdit); |
| 664 | p->c.aEdit = 0; |
| 665 | p->c.nEdit = 0; |
| 666 | p->c.nEditAlloc = 0; |
| 667 | |
| 668 | /* Move aFrom over to aTo in preparation for the next step */ |
| 669 | free(p->c.aTo); |
| 670 | if( blob_buffer(&p->blobTo) ) blob_reset(&p->blobTo); |
| 671 | p->blobTo = *pParent; |
| 672 | blob_zero(pParent); |
| 673 | p->c.aTo = p->c.aFrom; |
| 674 | p->c.nTo = p->c.nFrom; |
| 675 | |
| 676 | /* Return no errors */ |
| 677 | return 0; |
| 678 | } |
| 679 | |
| 680 | |
| 681 | /* |
| 682 | ** COMMAND: test-annotate-step |
| 683 | */ |
| 684 | void test_annotate_step_cmd(void){ |
| 685 | Blob orig, b; |
| 686 | Annotator x; |
| 687 | int i; |
| 688 | |
| 689 | if( g.argc<4 ) usage("RID1 RID2 ..."); |
| 690 | db_must_be_within_tree(); |
| 691 | blob_zero(&b); |
| 692 | content_get(name_to_rid(g.argv[2]), &orig); |
| 693 | if( annotation_start(&x, &orig) ){ |
| 694 | fossil_fatal("binary file"); |
| 695 | } |
| 696 | for(i=3; i<g.argc; i++){ |
| 697 | blob_zero(&b); |
| 698 | content_get(name_to_rid(g.argv[i]), &b); |
| 699 | if( annotation_step(&x, &b, g.argv[i-1]) ){ |
| 700 | fossil_fatal("binary file"); |
| 701 | } |
| 702 | } |
| 703 | for(i=0; i<x.nOrig; i++){ |
| 704 | const char *zSrc = x.aOrig[i].zSrc; |
| 705 | if( zSrc==0 ) zSrc = g.argv[g.argc-1]; |
| 706 | printf("%10s: %.*s\n", zSrc, x.aOrig[i].n, x.aOrig[i].z); |
| 707 | } |
| 708 | } |
| 709 |