shithub: riscv

Download patch

ref: 081b58a69e5efc2bbf49ee31099372527edb3fa7
parent: 1eadcba462d82b73eecfcc0d7aabd56cdc3d13e1
parent: 9ced712a2ad70557a8ceb89ff3b969b92c85f68c
author: phil9 <[email protected]>
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);