Index: src/diff.c ================================================================== --- src/diff.c +++ src/diff.c @@ -578,10 +578,194 @@ free(c.aFrom); free(c.aTo); return c.aEdit; } } + +/* +** Copy a line with a limit. Used for side-by-side diffs to enforce a maximum +** line length limit. +*/ +static char *copylimline(char *out, DLine *dl, int lim){ + int len; + len = dl->h & LENGTH_MASK; + if( lim && len > lim ){ + memcpy(out, dl->z, lim-3); + strcpy(&out[lim-3], "..."); + }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: +** <table class="sbsdiff"> +** <tr><th colspan="2" class="diffhdr">Old title</th><th/> +** <th colspan="2" class="diffhdr">New title</th></tr> +** +** And after the call, it should output: +** </table> +** +** 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. */ + @ <p class="generalError" style="white-space: nowrap">cannot compute + @ difference between binary files</p> + 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<c.nEdit ){ + int j; + /* Copied lines */ + for( j=0; j<c.aEdit[i]; j++){ + /* Hide lines which are copied and are further away from block boundaries + ** than nConext lines. For each block with hidden lines, show a row + ** notifying the user about the hidden rows. + */ + if( j<nContext || j>c.aEdit[i]-nContext-1 ){ + @ <tr> + }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){ + @ <tr> + @ <td class="meta" colspan="5" style="white-space: nowrap;"> + @ %d(c.aEdit[i]-2*nContext) hidden lines</td> + @ </tr> + if( !allowExp ) + continue; + @ <tr style="display:none;"> + }else{ + if( !allowExp ) + continue; + @ <tr style="display:none;"> + } + + copylimline(linebuf, &c.aFrom[iFrom+j], collim); + @ <td class="lineno">%d(iFrom+j+1)</td> + @ <td class="srcline">%h(linebuf)</td> + + @ <td> </td> + + copylimline(linebuf, &c.aTo[iTo+j], collim); + @ <td class="lineno">%d(iTo+j+1)</td> + @ <td class="srcline">%h(linebuf)</td> + + @ </tr> + } + iFrom+=c.aEdit[i]; + iTo+=c.aEdit[i]; + + if( c.aEdit[i+1]!=0 && c.aEdit[i+2]!=0 ){ + int lim; + lim = c.aEdit[i+1] > c.aEdit[i+2] ? c.aEdit[i+1] : c.aEdit[i+2]; + + /* Assume changed lines */ + for( j=0; j<lim; j++ ){ + @ <tr> + + if( j<c.aEdit[i+1] ){ + copylimline(linebuf, &c.aFrom[iFrom+j], collim); + @ <td class="changed lineno">%d(iFrom+j+1)</td> + @ <td class="changed srcline">%h(linebuf)</td> + }else{ + @ <td colspan="2" class="changedvoid"/> + } + + @ <td class="changed">|</td> + + if( j<c.aEdit[i+2] ){ + copylimline(linebuf, &c.aTo[iTo+j], collim); + @ <td class="changed lineno">%d(iTo+j+1)</td> + @ <td class="changed srcline">%h(linebuf)</td> + }else{ + @ <td colspan="2" class="changedvoid"/> + } + + @ </tr> + } + iFrom+=c.aEdit[i+1]; + iTo+=c.aEdit[i+2]; + }else{ + + /* Process deleted lines */ + for( j=0; j<c.aEdit[i+1]; j++ ){ + @ <tr> + + copylimline(linebuf, &c.aFrom[iFrom+j], collim); + @ <td class="removed lineno">%d(iFrom+j+1)</td> + @ <td class="removed srcline">%h(linebuf)</td> + @ <td><</td> + @ <td colspan="2" class="removedvoid"/> + @ </tr> + } + iFrom+=c.aEdit[i+1]; + + /* Process inserted lines */ + for( j=0; j<c.aEdit[i+2]; j++ ){ + @ <tr> + @ <td colspan="2" class="addedvoid"/> + @ <td>></td> + copylimline(linebuf, &c.aTo[iTo+j], collim); + @ <td class="added lineno">%d(iTo+j+1)</td> + @ <td class="added srcline">%h(linebuf)</td> + @ </tr> + } + iTo+=c.aEdit[i+2]; + } + + i+=3; + } + + free(linebuf); + free(c.aFrom); + free(c.aTo); + free(c.aEdit); + return 1; +} + /* ** COMMAND: test-rawdiff */ void test_rawdiff_cmd(void){ Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -276,10 +276,40 @@ @ %h(blob_str(&out)) blob_reset(&from); blob_reset(&to); blob_reset(&out); } + + +/* +** Write the difference between two RIDs to the output +*/ +static void generate_sbsdiff(const char *zFrom, const char *zTo){ + int fromid; + int toid; + Blob from, to; + if( zFrom ){ + fromid = uuid_to_rid(zFrom, 0); + content_get(fromid, &from); + }else{ + blob_zero(&from); + } + if( zTo ){ + toid = uuid_to_rid(zTo, 0); + content_get(toid, &to); + }else{ + blob_zero(&to); + } + @ <table class="sbsdiff"> + @ <tr><th colspan="2" class="diffhdr">Old (%S(zFrom))</th><th/> + @ <th colspan="2" class="diffhdr">New (%S(zTo))</th></tr> + html_sbsdiff(&from, &to, 5, 1); + @ </table> + blob_reset(&from); + blob_reset(&to); +} + /* ** Write a line of web-page output that shows changes that have occurred ** to a file between two check-ins. */ @@ -287,10 +317,11 @@ const char *zName, /* Name of the file that has changed */ const char *zOld, /* blob.uuid before change. NULL for added files */ const char *zNew, /* blob.uuid after change. NULL for deletes */ const char *zOldName, /* Prior name. NULL if no name change. */ int showDiff, /* Show edit diffs if true */ + int sideBySide, /* Show diffs side-by-side */ int mperm /* executable or symlink permission for zNew */ ){ if( !g.perm.History ){ if( zNew==0 ){ @ <p>Deleted %h(zName)</p> @@ -329,13 +360,17 @@ }else{ @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a> } if( showDiff ){ - @ <blockquote><pre> - append_diff(zOld, zNew); - @ </pre></blockquote> + if( sideBySide ){ + generate_sbsdiff(zOld, zNew); + }else{ + @ <blockquote><pre> + append_diff(zOld, zNew); + @ </pre></blockquote> + } }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ @ @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> } @ </p> @@ -361,10 +396,11 @@ void ci_page(void){ Stmt q; int rid; int isLeaf; int showDiff; + int sideBySide; const char *zName; /* Name of the checkin to be displayed */ const char *zUuid; /* UUID of zName */ const char *zParent; /* UUID of the parent checkin (if any) */ login_check_credentials(); @@ -390,10 +426,11 @@ " FROM blob, event" " WHERE blob.rid=%d" " AND event.objid=%d", rid, rid ); + sideBySide = atoi(PD("sbs","1")); if( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); char *zTitle = mprintf("Check-in [%.10s]", zUuid); char *zEUser, *zEComment; const char *zUser; @@ -511,18 +548,42 @@ showDiff = g.zPath[0]!='c'; if( db_get_boolean("show-version-diffs", 0)==0 ){ showDiff = !showDiff; if( showDiff ){ @ <a href="%s(g.zTop)/vinfo/%T(zName)">[hide diffs]</a> + @ + if( sideBySide ){ + @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=0"> + @ [unified diffs]</a> + }else{ + @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=1"> + @ [side-by-side diffs]</a> + } }else{ - @ <a href="%s(g.zTop)/ci/%T(zName)">[show diffs]</a> + @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=0"> + @ [show unified diffs]</a> + @ + @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=1"> + @ [show side-by-side diffs]</a> } }else{ if( showDiff ){ @ <a href="%s(g.zTop)/ci/%T(zName)">[hide diffs]</a> + @ + if( sideBySide ){ + @ <a href="%s(g.zTop)/info/%T(zName)?sbs=0"> + @ [unified diffs]</a> + }else{ + @ <a href="%s(g.zTop)/info/%T(zName)?sbs=1"> + @ [side-by-side diffs]</a> + } }else{ - @ <a href="%s(g.zTop)/vinfo/%T(zName)">[show diffs]</a> + @ <a href="%s(g.zTop)/vinfo/%T(zName)?sbs=0"> + @ [show unified diffs]</a> + @ + @ <a href="%s(g.zTop)/vinfo/%T(zName)?sbs=1"> + @ [show side-by-side diffs]</a> } } @ @ <a href="%s(g.zTop)/vpatch?from=%S(zParent)&to=%S(zUuid)">[patch]</a><br/> db_prepare(&q, @@ -540,11 +601,12 @@ const char *zName = db_column_text(&q,0); int mperm = db_column_int(&q, 1); const char *zOld = db_column_text(&q,2); const char *zNew = db_column_text(&q,3); const char *zOldName = db_column_text(&q, 4); - append_file_change_line(zName, zOld, zNew, zOldName, showDiff, mperm); + append_file_change_line(zName, zOld, zNew, zOldName, showDiff, + sideBySide, mperm); } db_finalize(&q); } style_footer(); } @@ -690,17 +752,18 @@ } /* ** WEBPAGE: vdiff -** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN +** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN;sbs=BOOLEAN ** ** Show all differences between two checkins. */ void vdiff_page(void){ int ridFrom, ridTo; int showDetail = 0; + int sideBySide = 0; Manifest *pFrom, *pTo; ManifestFile *pFileFrom, *pFileTo; login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } @@ -709,10 +772,20 @@ pFrom = vdiff_parse_manifest("from", &ridFrom); if( pFrom==0 ) return; pTo = vdiff_parse_manifest("to", &ridTo); if( pTo==0 ) return; showDetail = atoi(PD("detail","0")); + sideBySide = atoi(PD("sbs","1")); + if( !sideBySide ){ + style_submenu_element("Side-by-side Diff", "sbsdiff", + "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1", + g.zTop, P("from"), P("to"), showDetail); + }else{ + style_submenu_element("Unified Diff", "udiff", + "%s/vdiff?from=%T&to=%T&detail=%d&sbs=0", + g.zTop, P("from"), P("to"), showDetail); + } style_header("Check-in Differences"); @ <h2>Difference From:</h2><blockquote> checkin_description(ridFrom); @ </blockquote><h2>To:</h2><blockquote> checkin_description(ridTo); @@ -731,25 +804,25 @@ }else{ cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); } if( cmp<0 ){ append_file_change_line(pFileFrom->zName, - pFileFrom->zUuid, 0, 0, 0, 0); + pFileFrom->zUuid, 0, 0, 0, 0, 0); pFileFrom = manifest_file_next(pFrom, 0); }else if( cmp>0 ){ append_file_change_line(pFileTo->zName, - 0, pFileTo->zUuid, 0, 0, + 0, pFileTo->zUuid, 0, 0, 0, manifest_file_mperm(pFileTo)); pFileTo = manifest_file_next(pTo, 0); }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ /* No changes */ pFileFrom = manifest_file_next(pFrom, 0); pFileTo = manifest_file_next(pTo, 0); }else{ append_file_change_line(pFileFrom->zName, pFileFrom->zUuid, - pFileTo->zUuid, 0, showDetail, + pFileTo->zUuid, 0, showDetail, sideBySide, manifest_file_mperm(pFileTo)); pFileFrom = manifest_file_next(pFrom, 0); pFileTo = manifest_file_next(pTo, 0); } } @@ -983,28 +1056,30 @@ } /* ** WEBPAGE: fdiff -** URL: fdiff?v1=UUID&v2=UUID&patch +** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN ** -** Two arguments, v1 and v2, identify the files to be diffed. Show the -** difference between the two artifacts. Generate plaintext if "patch" -** is present. +** Two arguments, v1 and v2, identify the files to be diffed. Show the +** difference between the two artifacts. Show diff side by side unless sbs +** is 0. Generate plaintext if "patch" is present. */ void diff_page(void){ int v1, v2; int isPatch; + int sideBySide; Blob c1, c2, diff, *pOut; char *zV1; char *zV2; login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } v1 = name_to_rid_www("v1"); v2 = name_to_rid_www("v2"); if( v1==0 || v2==0 ) fossil_redirect_home(); + sideBySide = atoi(PD("sbs","1")); zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1); zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2); isPatch = P("patch")!=0; if( isPatch ){ pOut = cgi_output_blob(); @@ -1011,28 +1086,44 @@ cgi_set_content_type("text/plain"); }else{ blob_zero(&diff); pOut = &diff; } - content_get(v1, &c1); - content_get(v2, &c2); - text_diff(&c1, &c2, pOut, 4, 1); - blob_reset(&c1); - blob_reset(&c2); + if( !sideBySide || isPatch ){ + content_get(v1, &c1); + content_get(v2, &c2); + text_diff(&c1, &c2, pOut, 4, 1); + blob_reset(&c1); + blob_reset(&c2); + } if( !isPatch ){ style_header("Diff"); style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch", g.zTop, P("v1"), P("v2")); + if( !sideBySide ){ + style_submenu_element("Side-by-side Diff", "sbsdiff", + "%s/fdiff?v1=%T&v2=%T&sbs=1", + g.zTop, P("v1"), P("v2")); + }else{ + style_submenu_element("Unified Diff", "udiff", + "%s/fdiff?v1=%T&v2=%T&sbs=0", + g.zTop, P("v1"), P("v2")); + } + @ <h2>Differences From @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2> object_description(v1, 0, 0); @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2> object_description(v2, 0, 0); @ <hr /> - @ <blockquote><pre> - @ %h(blob_str(&diff)) - @ </pre></blockquote> + if( sideBySide ){ + generate_sbsdiff(zV1, zV2); + }else{ + @ <blockquote><pre> + @ %h(blob_str(&diff)) + @ </pre></blockquote> + } blob_reset(&diff); style_footer(); } } Index: src/skins.c ================================================================== --- src/skins.c +++ src/skins.c @@ -152,10 +152,55 @@ @ /* The label/value pairs on (for example) the vinfo page */ @ table.label-value th { @ vertical-align: top; @ text-align: right; @ padding: 0.2ex 2ex; +@ } +@ +@ /* Side-by-side diff */ +@ table.sbsdiff { +@ background-color: white; +@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace; +@ font-size: 10pt; +@ border-collapse:collapse; +@ white-space: pre; +@ width: 98%; +@ border: 1px #000 dashed; +@ } +@ +@ table.sbsdiff th.diffhdr { +@ border-bottom: dotted; +@ border-width: 1px; +@ } +@ +@ table.sbsdiff tr td { +@ white-space: pre; +@ padding-left: 3px; +@ padding-right: 3px; +@ margin: 0px; +@ } +@ +@ table.sbsdiff tr td.lineno { +@ text-align: right; +@ } +@ +@ table.sbsdiff tr td.meta { +@ color: white; +@ background-color: rgb(20, 20, 20); +@ text-align: center; +@ } +@ +@ table.sbsdiff tr td.added { +@ background-color: rgb(230, 230, 230); +@ } +@ +@ table.sbsdiff tr td.removed { +@ background-color: rgb(200, 200, 200); +@ } +@ +@ table.sbsdiff tr td.changed { +@ background-color: rgb(220, 220, 220); @ }'); @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> @ <head> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" @@ -356,11 +401,54 @@ @ table.label-value th { @ vertical-align: top; @ text-align: right; @ padding: 0.2ex 2ex; @ } -@ '); +@ +@ /* Side-by-side diff */ +@ table.sbsdiff { +@ background-color: #ffffc5; +@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace; +@ font-size: 10pt; +@ border-collapse:collapse; +@ white-space: pre; +@ width: 98%; +@ border: 1px #000 dashed; +@ } +@ +@ table.sbsdiff th.diffhdr { +@ border-bottom: dotted; +@ border-width: 1px; +@ } +@ +@ table.sbsdiff tr td { +@ white-space: pre; +@ padding-left: 3px; +@ padding-right: 3px; +@ margin: 0px; +@ } +@ +@ table.sbsdiff tr td.lineno { +@ text-align: right; +@ } +@ +@ table.sbsdiff tr td.meta { +@ background-color: #a09048; +@ text-align: center; +@ } +@ +@ table.sbsdiff tr td.added { +@ background-color: rgb(210, 210, 100); +@ } +@ +@ table.sbsdiff tr td.removed { +@ background-color: rgb(190, 200, 110); +@ } +@ +@ table.sbsdiff tr td.changed { +@ background-color: rgb(200, 210, 120); +@ }'); @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> @ <head> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" @ href="$home/timeline.rss"> @@ -590,10 +678,56 @@ @ /* The label/value pairs on (for example) the ci page */ @ table.label-value th { @ vertical-align: top; @ text-align: right; @ padding: 0.2ex 2ex; +@ } +@ +@ /* Side-by-side diff */ +@ table.sbsdiff { +@ background-color: white; +@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace; +@ font-size: 10pt; +@ border-collapse:collapse; +@ white-space: pre; +@ width: 98%; +@ border: 1px #000 dashed; +@ } +@ +@ table.sbsdiff th.diffhdr { +@ border-bottom: dotted; +@ border-width: 1px; +@ } +@ +@ table.sbsdiff tr td { +@ white-space: pre; +@ padding-left: 3px; +@ padding-right: 3px; +@ margin: 0px; +@ } +@ +@ table.sbsdiff tr td.lineno { +@ text-align: right; +@ } +@ +@ table.sbsdiff tr td.meta { +@ color: white; +@ background-color: black; +@ text-align: center; +@ } +@ +@ table.sbsdiff tr td.added { +@ background-color: white; +@ } +@ +@ table.sbsdiff tr td.removed { +@ background-color: white; +@ text-decoration: line-through; +@ } +@ +@ table.sbsdiff tr td.changed { +@ background-color: white; @ }'); @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> @ <head> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" @@ -885,10 +1019,77 @@ @ padding: 3px 5px; @ } @ @ textarea { @ font-size: 1em; +@ } +@ +@ /* Side-by-side diff */ +@ table.sbsdiff { +@ background-color: white; +@ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace; +@ font-size: 8pt; +@ border-collapse:collapse; +@ width: 98%; +@ border: 1px #000 dashed; +@ margin-left: auto; +@ margin-right: auto; +@ } +@ +@ table.sbsdiff th.diffhdr { +@ border-bottom: dotted; +@ border-width: 1px; +@ } +@ +@ table.sbsdiff tr td { +@ padding-left: 3px; +@ padding-right: 3px; +@ margin: 0px; +@ vertical-align: top; +@ white-space: pre-wrap; +@ } +@ +@ table.sbsdiff tr td.lineno { +@ text-align: right; +@ /* border-bottom: 1px solid rgb(220, 220, 220); */ +@ } +@ +@ table.sbsdiff tr td.srcline { +@ max-width: 400px; +@ /* Note: May partially hide long lines without whitespaces */ +@ overflow: hidden; +@ /* border-bottom: 1px solid rgb(220, 220, 220); */ +@ } +@ +@ table.sbsdiff tr td.meta { +@ background-color: rgb(170, 160, 255); +@ padding-top: 0.25em; +@ padding-bottom: 0.25em; +@ text-align: center; +@ -moz-border-radius: 5px; +@ -moz-border-radius: 5px; +@ -webkit-border-radius: 5px; +@ -webkit-border-radius: 5px; +@ -border-radius: 5px; +@ -border-radius: 5px; +@ border-radius: 5px; +@ border-radius: 5px; +@ } +@ +@ table.sbsdiff tr td.added { +@ background-color: rgb(180, 250, 180); +@ /* border-bottom: 1px solid rgb(160, 230, 160); */ +@ } +@ +@ table.sbsdiff tr td.removed { +@ background-color: rgb(250, 130, 130); +@ /* border-bottom: 1px solid rgb(230, 110, 110); */ +@ } +@ +@ table.sbsdiff tr td.changed { +@ background-color: rgb(210, 210, 200); +@ /* border-bottom: 1px solid rgb(190, 190, 180); */ @ }'); @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> @ <head> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" Index: src/style.c ================================================================== --- src/style.c +++ src/style.c @@ -396,10 +396,69 @@ @ table.label-value th { @ vertical-align: top; @ text-align: right; @ padding: 0.2ex 2ex; @ } +@ +@ /* Side-by-side diff */ +@ table.sbsdiff { +@ background-color: white; +@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace; +@ font-size: 10pt; +@ border-collapse:collapse; +@ white-space: pre; +@ width: 98%; +@ border: 1px #000 dashed; +@ margin-left: auto; +@ margin-right: auto; +@ } +@ +@ table.sbsdiff th.diffhdr { +@ border-bottom: dotted; +@ border-width: 1px; +@ } +@ +@ table.sbsdiff tr td { +@ white-space: pre; +@ padding-left: 3px; +@ padding-right: 3px; +@ margin: 0px; +@ vertical-align: top; +@ } +@ +@ table.sbsdiff tr td.lineno { +@ text-align: right; +@ } +@ +@ table.sbsdiff tr td.srcline { +@ } +@ +@ table.sbsdiff tr td.meta { +@ background-color: rgb(170, 160, 255); +@ text-align: center; +@ } +@ +@ table.sbsdiff tr td.added { +@ background-color: rgb(180, 250, 180); +@ } +@ table.sbsdiff tr td.addedvoid { +@ background-color: rgb(190, 190, 180); +@ } +@ +@ table.sbsdiff tr td.removed { +@ background-color: rgb(250, 130, 130); +@ } +@ table.sbsdiff tr td.removedvoid { +@ background-color: rgb(190, 190, 180); +@ } +@ +@ table.sbsdiff tr td.changed { +@ background-color: rgb(210, 210, 200); +@ } +@ table.sbsdiff tr td.changedvoid { +@ background-color: rgb(190, 190, 180); +@ } @ ; /* The following table contains bits of default CSS that must