Index: src/blob.c ================================================================== --- src/blob.c +++ src/blob.c @@ -927,10 +927,29 @@ } void blobarray_reset(Blob *aBlob, int n){ int i; for(i=0; i ins.edit { background-color: #c0c0ff; text-decoration: none; font-weight: bold; } - +body.tkt div.content li > table.udiff { + margin-left: 1.5em; + margin-top: 0.5em; +} span.modpending { color: #b03800; font-style: italic; } Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -2828,11 +2828,11 @@ @ } @
Changes
@

- ticket_output_change_artifact(pTktChng, 0, 1); + ticket_output_change_artifact(pTktChng, 0, 1, 0); manifest_destroy(pTktChng); style_finish_page(); } Index: src/tkt.c ================================================================== --- src/tkt.c +++ src/tkt.c @@ -1204,10 +1204,11 @@ Stmt q; char *zTitle; const char *zUuid; int tagid; int nChng = 0; + Blob *aLastVal = 0; login_check_credentials(); if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(g.anon.Hyperlink && g.anon.RdTkt); return; @@ -1233,10 +1234,14 @@ } if( P("raw")!=0 ){ @

Raw Artifacts Associated With Ticket %h(zUuid)

}else{ @

Artifacts Associated With Ticket %h(zUuid)

+ getAllTicketFields(); + if( nTicketBslns ){ + aLastVal = blobarray_new(nField); + } } db_prepare(&q, "SELECT datetime(mtime,toLocal()), objid, uuid, NULL, NULL, NULL" " FROM event, blob" " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)" @@ -1289,11 +1294,11 @@ @
           @ %h(blob_str(&c))
           @ 
blob_reset(&c); }else{ - ticket_output_change_artifact(pTicket, "a", nChng); + ticket_output_change_artifact(pTicket, "a", nChng, aLastVal); } } manifest_destroy(pTicket); } @ @@ -1301,10 +1306,11 @@ db_finalize(&q); if( nChng ){ @ } style_finish_page(); + if( aLastVal ) blobarray_delete(aLastVal, nField); } /* ** Return TRUE if the given BLOB contains a newline character. */ @@ -1322,44 +1328,85 @@ ** description of this object. */ void ticket_output_change_artifact( Manifest *pTkt, /* Parsed artifact for the ticket change */ const char *zListType, /* Which type of list */ - int n /* Which ticket change is this */ + int n, /* Which ticket change is this */ + Blob *aLastVal /* Array of latest values for the diffs */ ){ int i; if( zListType==0 ) zListType = "1"; getAllTicketFields(); @
    for(i=0; inField; i++){ - Blob val; - const char *z, *zX; - int id; - z = pTkt->aField[i].zName; - blob_set(&val, pTkt->aField[i].zValue); - zX = z[0]=='+' ? z+1 : z; - id = fieldId(zX); + const char *z = pTkt->aField[i].zName; + const char *zX = z[0]=='+' ? z+1 : z; + const int id = fieldId(zX); + const char *zValue = pTkt->aField[i].zValue; + const size_t nValue = strlen(zValue); + const int bLong = nValue>50 || memchr(zValue,'\n',nValue)!=NULL; + const int bCanDiff = aLastVal && id>=0 && aField[id].zBsln; + int bAppend = 0, bRegular = 0; @
  1. \ if( id<0 ){ @ Untracked field %h(zX): }else if( aField[id].mUsed==USEDBY_TICKETCHNG ){ @ %h(zX): }else if( n==0 ){ @ %h(zX) initialized to: }else if( z[0]=='+' && (aField[id].mUsed&USEDBY_TICKET)!=0 ){ @ Appended to %h(zX): - }else{ - @ %h(zX) changed to: - } - if( blob_size(&val)>50 || contains_newline(&val) ){ - @
    -      @ %h(blob_str(&val))
    -      @ 
  2. - }else{ - @ "%h(blob_str(&val))" - } - blob_reset(&val); + bAppend = 1; + }else{ + if( !bCanDiff ){ + @ %h(zX) changed to: \ + } + bRegular = 1; + } + if( bCanDiff ){ + Blob *prev = aLastVal+id; + Blob val = BLOB_INITIALIZER; + if( nValue ){ + blob_init(&val, zValue, nValue+1); + val.nUsed--; /* makes blob_str() faster */ + } + if( bRegular && nValue && blob_buffer(prev) && blob_size(prev) ){ + Blob d = BLOB_INITIALIZER; + DiffConfig DCfg; + construct_diff_flags(1, &DCfg); + DCfg.diffFlags |= DIFF_HTML | DIFF_LINENO; + text_diff(prev, &val, &d, &DCfg); + @ %h(zX) changed as: + @ %s(blob_str(&d)) + @ + blob_reset(&d); + }else{ + if( bRegular ){ + @ %h(zX) changed to: + } + if( bLong ){ + @
    +          @ %h(zValue)
    +          @ 
    + }else{ + @ "%h(zValue)" + } + } + if( blob_buffer(prev) && blob_size(prev) && !bAppend ){ + blob_truncate(prev,0); + } + if( nValue ) blob_appendb(prev, &val); + blob_reset(&val); + }else{ + if( bLong ){ + @
    +        @ %h(zValue)
    +        @ 
    + }else{ + @ "%h(zValue)" + } + } } @
} /*