ref: 081b58a69e5efc2bbf49ee31099372527edb3fa7
parent: 1eadcba462d82b73eecfcc0d7aabd56cdc3d13e1
parent: 9ced712a2ad70557a8ceb89ff3b969b92c85f68c
author: phil9 <telephil9@gmail.com>
date: Mon Feb 5 05:54:41 EST 2024
merge
--- a/sys/man/1/vdiff
+++ b/sys/man/1/vdiff
@@ -12,7 +12,7 @@
]
.SH DESCRIPTION
.I vdiff
-reads unified diff output from standard input and displays a colored version. Right clicking on a line will send a
+reads unified diff output from standard input and displays a colored version. Each file diff is displayed in a separate block that can be collapsed by a click on the file name. Right clicking on a line will send a
.IR plumb (6)
message to the
.B edit
@@ -21,7 +21,7 @@
or
.B del
will exit
-.B vdiff
+.B vdiff.
.PP
The
.B \-b
--- a/sys/src/cmd/vdiff.c
+++ b/sys/src/cmd/vdiff.c
@@ -7,14 +7,26 @@
#include <keyboard.h>
#include <bio.h>
+enum { Meminc = 32 };
+
+typedef struct Block Block;
typedef struct Line Line;
typedef struct Col Col;
+struct Block {
+ Image *b;
+ Rectangle r;
+ Rectangle sr;
+ int v;
+ char *f;
+ Line **lines;
+ int nlines;
+};
+
struct Line {
int t;
+ int n;
char *s;
- char *f;
- int l;
};
struct Col {
@@ -46,24 +58,49 @@
Rectangle sr;
Rectangle scrollr;
Rectangle scrposr;
-Rectangle listr;
-Rectangle textr;
+Rectangle viewr;
Col cols[Ncols];
Col scrlcol;
+Image *bord;
+Image *expander[2];
+int totalh;
+int viewh;
int scrollsize;
-int lineh;
-int nlines;
int offset;
+int lineh;
int scrolling;
int oldbuttons;
-Line **lines;
-int lsize;
-int lcount;
+Block **blocks;
+int nblocks;
int maxlength;
int Δpan;
int nstrip;
const char ellipsis[] = "...";
+int ellipsisw;
+int spacew;
+void*
+emalloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("malloc: %r");
+ return p;
+}
+
+void*
+erealloc(void *p, ulong n)
+{
+ void *q;
+
+ q = realloc(p, n);
+ if(q == nil)
+ sysfatal("realloc: %r");
+ return q;
+}
+
void
plumb(char *f, int l)
{
@@ -83,7 +120,7 @@
}
void
-drawline(Rectangle r, Line *l)
+renderline(Image *b, Rectangle r, int pad, int lt, char *ls)
{
Point p;
Rune rn;
@@ -90,10 +127,10 @@
char *s;
int off, tab, nc;
- draw(screen, r, cols[l->t].bg, nil, ZP);
- p = Pt(r.min.x + Hpadding, r.min.y + (Dy(r)-font->height)/2);
- off = Δpan / stringwidth(font, " ");
- for(s = l->s, nc = -1, tab = 0; *s; nc++, tab--, off--){
+ draw(b, r, cols[lt].bg, nil, ZP);
+ p = Pt(r.min.x + pad + Hpadding, r.min.y + (Dy(r)-font->height)/2);
+ off = Δpan / spacew;
+ for(s = ls, nc = -1, tab = 0; *s; nc++, tab--, off--){
if(tab <= 0 && *s == '\t'){
tab = 4 - nc % 4;
s++;
@@ -100,37 +137,78 @@
}
if(tab > 0){
if(off <= 0)
- p = runestring(screen, p, cols[l->t].bg, ZP, font, L"█");
- }else if((p.x+Hpadding+stringwidth(font, " ")+stringwidth(font, ellipsis)>=textr.max.x)){
- string(screen, p, cols[l->t].fg, ZP, font, ellipsis);
+ p = runestring(b, p, cols[lt].bg, ZP, font, L"█");
+ }else if((p.x+Hpadding+spacew+ellipsisw>=b->r.max.x)){
+ string(b, p, cols[lt].fg, ZP, font, ellipsis);
break;
}else{
s += chartorune(&rn, s);
if(off <= 0)
- p = runestringn(screen, p, cols[l->t].fg, ZP, font, &rn, 1);
+ p = runestringn(b, p, cols[lt].fg, ZP, font, &rn, 1);
}
}
}
void
+renderblock(Block *b)
+{
+ Rectangle r, lr, br;
+ Line *l;
+ int i, pad;
+
+ pad = 0;
+ r = insetrect(b->r, 1);
+ draw(b->b, b->r, cols[Lnone].bg, nil, ZP);
+ if(b->f != nil){
+ pad = Margin;
+ lr = r;
+ lr.max.y = lineh;
+ br = rectaddpt(expander[0]->r, Pt(lr.min.x+Hpadding, lr.min.y+Vpadding));
+ border(b->b, b->r, 1, bord, ZP);
+ renderline(b->b, lr, Dx(expander[0]->r)+Hpadding, Lfile, b->f);
+ draw(b->b, br, expander[b->v], nil, ZP);
+ r.min.y += lineh;
+ }
+ if(b->v == 0)
+ return;
+ for(i = 0; i < b->nlines; i++){
+ l = b->lines[i];
+ lr = Rect(r.min.x, r.min.y+i*lineh, r.max.x, r.min.y+(i+1)*lineh);
+ renderline(b->b, lr, pad, l->t, l->s);
+ }
+}
+
+void
redraw(void)
{
- Rectangle lr;
- int i, h, y;
+ Rectangle clipr;
+ int i, h, y, ye, vmin, vmax;
+ Block *b;
draw(screen, sr, cols[Lnone].bg, nil, ZP);
draw(screen, scrollr, scrlcol.bg, nil, ZP);
- if(lcount>0){
- h = ((double)nlines/lcount)*Dy(scrollr);
- y = ((double)offset/lcount)*Dy(scrollr);
- scrposr = Rect(scrollr.min.x, scrollr.min.y+y, scrollr.max.x-1, scrollr.min.y+y+h);
+ if(viewh < totalh){
+ h = ((double)viewh/totalh)*Dy(scrollr);
+ y = ((double)offset/totalh)*Dy(scrollr);
+ ye = scrollr.min.y + y + h - 1;
+ if(ye >= scrollr.max.y)
+ ye = scrollr.max.y - 1;
+ scrposr = Rect(scrollr.min.x, scrollr.min.y+y+1, scrollr.max.x-1, ye);
}else
scrposr = Rect(scrollr.min.x, scrollr.min.y, scrollr.max.x-1, scrollr.max.y);
draw(screen, scrposr, scrlcol.fg, nil, ZP);
- for(i=0; i<nlines && offset+i<lcount; i++){
- lr = Rect(textr.min.x, textr.min.y+i*lineh, textr.max.x, textr.min.y+(i+1)*lineh);
- drawline(lr, lines[offset+i]);
+ vmin = viewr.min.y + offset;
+ vmax = viewr.max.y + offset;
+ clipr = screen->clipr;
+ replclipr(screen, 0, viewr);
+ for(i = 0; i < nblocks; i++){
+ b = blocks[i];
+ if(b->sr.min.y <= vmax && b->sr.max.y >= vmin){
+ renderblock(b);
+ draw(screen, rectaddpt(b->sr, Pt(0, -offset)), b->b, nil, ZP);
+ }
}
+ replclipr(screen, 0, clipr);
flushimage(display, 1);
}
@@ -139,8 +217,8 @@
{
int max;
- max = Hpadding + maxlength * stringwidth(font, " ") + 2 * stringwidth(font, ellipsis) - Dx(textr);
- Δpan += off * stringwidth(font, " ");
+ max = Hpadding + Margin + Hpadding + maxlength * spacew + 2 * ellipsisw - Dx(blocks[0]->r);
+ Δpan += off * spacew;
if(Δpan < 0 || max <= 0)
Δpan = 0;
else if(Δpan > max)
@@ -153,8 +231,8 @@
{
if(offset<0)
offset = 0;
- if(offset+nlines>lcount)
- offset = lcount-nlines+1;
+ if(offset+viewh>totalh)
+ offset = totalh - viewh;
}
void
@@ -162,7 +240,7 @@
{
if(off<0 && offset<=0)
return;
- if(off>0 && offset+nlines>lcount)
+ if(off>0 && offset+viewh>totalh)
return;
offset += off;
clampoffset();
@@ -169,23 +247,31 @@
redraw();
}
-int
-indexat(Point p)
+void
+blockresize(Block *b)
{
- int n;
+ int w, h;
- if (!ptinrect(p, textr))
- return -1;
- n = (p.y - textr.min.y) / lineh;
- if ((n+offset) >= lcount)
- return -1;
- return n;
+ w = Dx(viewr) - 2; /* add 2 for border */
+ h = 0 + 2;
+ if(b->f != nil)
+ h += lineh;
+ if(b->v)
+ h += b->nlines*lineh;
+ b->r = Rect(0, 0, w, h);
+ freeimage(b->b);
+ b->b = allocimage(display, b->r, screen->chan, 0, DNofill);
}
void
-eresize(void)
+eresize(int new)
{
- if(getwindow(display, Refnone)<0)
+ Rectangle listr;
+ Block *b;
+ Point p;
+ int i;
+
+ if(new && getwindow(display, Refnone)<0)
sysfatal("cannot reattach: %r");
sr = screen->r;
scrollr = sr;
@@ -192,12 +278,22 @@
scrollr.max.x = scrollr.min.x+Scrollwidth+Scrollgap;
listr = sr;
listr.min.x = scrollr.max.x;
- textr = insetrect(listr, Margin);
+ viewr = insetrect(listr, Margin);
+ viewh = Dy(viewr);
lineh = Vpadding+font->height+Vpadding;
- nlines = Dy(textr)/lineh;
- scrollsize = mousescrollsize(nlines);
- if(offset > 0 && offset+nlines>lcount)
- offset = lcount-nlines+1;
+ totalh = - Margin + Vpadding + 1;
+ p = addpt(viewr.min, Pt(0, totalh));
+ for(i = 0; i < nblocks; i++){
+ b = blocks[i];
+ blockresize(b);
+ b->sr = rectaddpt(b->r, p);
+ p.y += Margin + Dy(b->r);
+ totalh += Margin + Dy(b->r);
+ }
+ totalh = totalh - Margin + Vpadding;
+ scrollsize = viewh / 2.0;
+ if(offset > 0 && offset+viewh>totalh)
+ offset = totalh - viewh;
redraw();
}
@@ -210,22 +306,22 @@
threadexitsall(nil);
break;
case Khome:
- scroll(-1000000);
+ scroll(-totalh);
break;
case Kend:
- scroll(1000000);
+ scroll(totalh);
break;
case Kpgup:
- scroll(-nlines);
+ scroll(-viewh);
break;
case Kpgdown:
- scroll(nlines);
+ scroll(viewh);
break;
case Kup:
- scroll(-1);
+ scroll(-scrollsize);
break;
case Kdown:
- scroll(1);
+ scroll(scrollsize);
break;
case Kleft:
pan(-4);
@@ -237,56 +333,78 @@
}
void
-emouse(Mouse m)
+blockmouse(Block *b, Mouse m)
{
+ Line *l;
int n;
+ n = (m.xy.y + offset - b->sr.min.y) / lineh;
+ if(n == 0 && b->f != nil && m.buttons&1){
+ b->v = !b->v;
+ eresize(0);
+ }else if(n > 0 && m.buttons&4){
+ l = b->lines[n-1];
+ if(l->t != Lsep)
+ plumb(b->f, l->n);
+ }
+}
+
+void
+emouse(Mouse m)
+{
+ Block *b;
+ int n, i;
+
if(oldbuttons == 0 && m.buttons != 0 && ptinrect(m.xy, scrollr))
scrolling = 1;
else if(m.buttons == 0)
scrolling = 0;
+ n = (m.xy.y - scrollr.min.y);
if(scrolling){
if(m.buttons&1){
- n = (m.xy.y - scrollr.min.y) / lineh;
- if(-n<lcount-offset){
- scroll(-n);
- } else {
- scroll(-lcount+offset);
- }
+ scroll(-n);
return;
}else if(m.buttons&2){
- n = (m.xy.y - scrollr.min.y) * lcount / Dy(scrollr);
- offset = n;
+ offset = (m.xy.y - scrollr.min.y) * totalh/Dy(scrollr);
clampoffset();
redraw();
}else if(m.buttons&4){
- n = (m.xy.y - scrollr.min.y) / lineh;
- if(n<lcount-offset){
- scroll(n);
- } else {
- scroll(lcount-offset);
- }
+ scroll(n);
return;
}
- }else{
- if(m.buttons&4){
- n = indexat(m.xy);
- if(n>=0 && lines[n+offset]->f != nil)
- plumb(lines[n+offset]->f, lines[n+offset]->l);
- }else if(m.buttons&8)
- scroll(-scrollsize);
- else if(m.buttons&16)
- scroll(scrollsize);
+ }else if(m.buttons&8){
+ scroll(-n);
+ }else if(m.buttons&16){
+ scroll(n);
+ }else if(m.buttons != 0 && ptinrect(m.xy, viewr)){
+ for(i = 0; i < nblocks; i++){
+ b = blocks[i];
+ if(ptinrect(addpt(m.xy, Pt(0, offset)), b->sr)){
+ blockmouse(b, m);
+ break;
+ }
+ }
}
oldbuttons = m.buttons;
}
+Image*
+ecolor(ulong n)
+{
+ Image *i;
+
+ i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, n);
+ if(i == nil)
+ sysfatal("allocimage: %r");
+ return i;
+}
+
void
initcol(Col *c, ulong fg, ulong bg)
{
- c->fg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, fg);
- c->bg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, bg);
+ c->fg = ecolor(fg);
+ c->bg = ecolor(bg);
}
void
@@ -293,6 +411,7 @@
initcols(int black)
{
if(black){
+ bord = ecolor(0x888888FF^(~0xFF));
initcol(&scrlcol, DBlack, 0x999999FF^(~0xFF));
initcol(&cols[Lfile], DWhite, 0x333333FF);
initcol(&cols[Lsep], DBlack, DPurpleblue);
@@ -300,6 +419,7 @@
initcol(&cols[Ldel], DWhite, 0x3F0000FF);
initcol(&cols[Lnone], DWhite, DBlack);
}else{
+ bord = ecolor(0x888888FF);
initcol(&scrlcol, DWhite, 0x999999FF);
initcol(&cols[Lfile], DBlack, 0xEFEFEFFF);
initcol(&cols[Lsep], DBlack, 0xEAFFFFFF);
@@ -309,6 +429,62 @@
}
}
+void
+initicons(void)
+{
+ int w, h;
+ Point p[4];
+
+ w = font->height;
+ h = font->height;
+ expander[0] = allocimage(display, Rect(0, 0, w, h), screen->chan, 0, DNofill);
+ draw(expander[0], expander[0]->r, cols[Lfile].bg, nil, ZP);
+ p[0] = Pt(0.25*w, 0.25*h);
+ p[1] = Pt(0.25*w, 0.75*h);
+ p[2] = Pt(0.75*w, 0.5*h);
+ p[3] = p[0];
+ fillpoly(expander[0], p, 4, 0, bord, ZP);
+ expander[1] = allocimage(display, Rect(0, 0, w, h), screen->chan, 0, DNofill);
+ draw(expander[1], expander[1]->r, cols[Lfile].bg, nil, ZP);
+ p[0] = Pt(0.25*w, 0.25*h);
+ p[1] = Pt(0.75*w, 0.25*h);
+ p[2] = Pt(0.5*w, 0.75*h);
+ p[3] = p[0];
+ fillpoly(expander[1], p, 4, 0, bord, ZP);
+ flushimage(display, 0);
+}
+
+Block*
+addblock(void)
+{
+ Block *b;
+
+ b = emalloc(sizeof *b);
+ b->b = nil;
+ b->v = 1;
+ b->f = nil;
+ b->lines = nil;
+ b->nlines = 0;
+ if(nblocks%Meminc == 0)
+ blocks = erealloc(blocks, (nblocks+Meminc)*sizeof *blocks);
+ blocks[nblocks++] = b;
+ return b;
+}
+
+void
+addline(Block *b, int t, int n, char *s)
+{
+ Line *l;
+
+ l = emalloc(sizeof *l);
+ l->t = t;
+ l->n = n;
+ l->s = s;
+ if(b->nlines%Meminc == 0)
+ b->lines = erealloc(b->lines, (b->nlines+Meminc)*sizeof(Line*));
+ b->lines[b->nlines++] = l;
+}
+
int
linetype(char *text)
{
@@ -329,28 +505,6 @@
return type;
}
-Line*
-parseline(char *f, int n, char *s)
-{
- Line *l;
- int len;
-
- l = malloc(sizeof *l);
- if(l==nil)
- sysfatal("malloc: %r");
- l->t = linetype(s);
- l->s = s;
- l->l = n;
- if(l->t != Lfile && l->t != Lsep)
- l->f = f;
- else
- l->f = nil;
- len = strlen(s);
- if(len > maxlength)
- maxlength = len;
- return l;
-}
-
int
lineno(char *s)
{
@@ -370,48 +524,55 @@
parse(int fd)
{
Biobuf *bp;
- Line *l;
- char *s, *f, *t;
- int n, ab;
+ Block *b;
+ char *s, *f, *tab;
+ int t, n, ab, len;
+ blocks = nil;
+ nblocks = 0;
ab = 0;
n = 0;
- f = nil;
- lsize = 64;
- lcount = 0;
- lines = malloc(lsize * sizeof *lines);
- if(lines==nil)
- sysfatal("malloc: %r");
bp = Bfdopen(fd, OREAD);
if(bp==nil)
sysfatal("Bfdopen: %r");
+ b = addblock();
for(;;){
s = Brdstr(bp, '\n', 1);
if(s==nil)
break;
- l = parseline(f, n, s);
- if(l->t == Lfile && l->s[0] == '-' && strncmp(l->s+4, "a/", 2)==0)
- ab = 1;
- if(l->t == Lfile && l->s[0] == '+'){
- f = l->s+4;
- if(ab && strncmp(f, "b/", 2)==0){
- f += 1;
- if(access(f, AEXIST) < 0)
+ t = linetype(s);
+ switch(t){
+ case Lfile:
+ if(s[0] == '-'){
+ b = addblock();
+ if(strncmp(s+4, "a/", 2) == 0)
+ ab = 1;
+ }else if(s[0] == '+'){
+ f = s+4;
+ if(ab && strncmp(f, "b/", 2) == 0){
f += 1;
+ if(access(f, AEXIST) < 0)
+ f += 1;
+ }
+ tab = strchr(f, '\t');
+ if(tab != nil)
+ *tab = 0;
+ b->f = f;
}
- t = strchr(f, '\t');
- if(t!=nil)
- *t = 0;
- }else if(l->t == Lsep)
- n = lineno(l->s);
- else if(l->t == Ladd || l->t == Lnone)
+ break;
+ case Lsep:
+ n = lineno(s) - 1; /* -1 as the separator is not an actual line */
+ if(0){
+ case Ladd:
+ case Lnone:
++n;
- lines[lcount++] = l;
- if(lcount>=lsize){
- lsize *= 2;
- lines = realloc(lines, lsize*sizeof *lines);
- if(lines==nil)
- sysfatal("realloc: %r");
+ }
+ default:
+ addline(b, t, n, s);
+ len = strlen(s);
+ if(len > maxlength)
+ maxlength = len;
+ break;
}
}
}
@@ -453,7 +614,7 @@
}ARGEND;
parse(0);
- if(lcount==0){
+ if(nblocks==0){
fprint(2, "no diff\n");
exits(nil);
}
@@ -468,7 +629,10 @@
a[Eresize].c = mctl->resizec;
a[Ekeyboard].c = kctl->c;
initcols(b);
- eresize();
+ initicons();
+ spacew = stringwidth(font, " ");
+ ellipsisw = stringwidth(font, ellipsis);
+ eresize(0);
for(;;){
switch(alt(a)){
case Emouse:
@@ -475,7 +639,7 @@
emouse(m);
break;
case Eresize:
- eresize();
+ eresize(1);
break;
case Ekeyboard:
ekeyboard(k);