ref: a44f4f5600ae24bed7fd91ec582f6b23295bad06
parent: 722bfb73f6d5f5cbcf5b551c51db552f535e2c9c
author: Jacob Moody <moody@posixcafe.org>
date: Fri Apr 11 19:55:37 EDT 2025
vdiff: add button 2 menu Mass collapse and expand, switching between multi patch inputs.
--- a/sys/man/1/vdiff
+++ b/sys/man/1/vdiff
@@ -29,7 +29,11 @@
or
.B del
will exit
-.I vdiff.
+.IR vdiff .
+.PP
+A menu may be summoned with button 2 of the mouse.
+It provides menu items for collapsing and expanding all files,
+as well as items for each patch present in the input.
.PP
The
.B -b
--- a/sys/src/cmd/vdiff.c
+++ b/sys/src/cmd/vdiff.c
@@ -12,6 +12,7 @@
typedef struct Block Block;
typedef struct Line Line;
typedef struct Col Col;
+typedef struct Patch Patch;
struct Block {
Image *b;
@@ -34,6 +35,12 @@
Image *fg;
};
+struct Patch {
+ char *name;
+ Block **blocks;
+ int nblocks;
+};
+
enum
{
Lfile = 0,
@@ -42,10 +49,20 @@
Ldel,
Lnone,
Ncols,
-};
+ Lterm = -1,
+ Lhash = -2,
+};
+
enum
{
+ Mcollapse,
+ Mexpand,
+ Nmenu,
+};
+
+enum
+{
Scrollwidth = 12,
Scrollgap = 2,
Margin = 8,
@@ -70,8 +87,9 @@
int lineh;
int scrolling;
int oldbuttons;
-Block **blocks;
-int nblocks;
+Patch *patches;
+int npatches;
+Patch *cur;
int maxlength;
int Δpan;
int nstrip;
@@ -201,8 +219,8 @@
vmax = viewr.max.y + offset;
clipr = screen->clipr;
replclipr(screen, 0, viewr);
- for(i = 0; i < nblocks; i++){
- b = blocks[i];
+ for(i = 0; i < cur->nblocks; i++){
+ b = cur->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);
@@ -217,7 +235,7 @@
{
int max;
- max = Dx(scrollr) + Margin + Hpadding + maxlength*spacew + 2*ellipsisw + Hpadding + Margin - Dx(blocks[0]->r)/2;
+ max = Dx(scrollr) + Margin + Hpadding + maxlength*spacew + 2*ellipsisw + Hpadding + Margin - Dx(cur->blocks[0]->r)/2;
Δpan += off * spacew;
if(Δpan < 0 || max <= 0)
Δpan = 0;
@@ -261,6 +279,8 @@
b->r = Rect(0, 0, w, h);
freeimage(b->b);
b->b = allocimage(display, b->r, screen->chan, 0, DNofill);
+ if(b-> b == nil)
+ sysfatal("allocimage: %r");
}
void
@@ -283,8 +303,8 @@
lineh = Vpadding+font->height+Vpadding;
totalh = - Margin + Vpadding + 1;
p = addpt(viewr.min, Pt(0, totalh));
- for(i = 0; i < nblocks; i++){
- b = blocks[i];
+ for(i = 0; i < cur->nblocks; i++){
+ b = cur->blocks[i];
blockresize(b);
b->sr = rectaddpt(b->r, p);
p.y += Margin + Dy(b->r);
@@ -332,6 +352,24 @@
}
}
+char*
+genmenu(int i)
+{
+ switch(i){
+ case Mcollapse:
+ return "collapse";
+ case Mexpand:
+ return "expand";
+ default:
+ i -= Nmenu;
+ if(i >= npatches)
+ return nil;
+ if(patches[i].name == nil)
+ patches[i].name = smprint("%d", i);
+ return patches[i].name;
+ }
+}
+
void
blockmouse(Block *b, Mouse m)
{
@@ -379,8 +417,8 @@
}else if(m.buttons&16){
scroll(n);
}else if((oldbuttons^m.buttons) != 0 && ptinrect(m.xy, viewr)){
- for(i = 0; i < nblocks; i++){
- b = blocks[i];
+ for(i = 0; i < cur->nblocks; i++){
+ b = cur->blocks[i];
if(ptinrect(addpt(m.xy, Pt(0, offset)), b->sr)){
blockmouse(b, m);
break;
@@ -466,9 +504,9 @@
b->f = nil;
b->lines = nil;
b->nlines = 0;
- if(nblocks%Meminc == 0)
- blocks = erealloc(blocks, (nblocks+Meminc)*sizeof *blocks);
- blocks[nblocks++] = b;
+ if(cur->nblocks%Meminc == 0)
+ cur->blocks = erealloc(cur->blocks, (cur->nblocks+Meminc)*sizeof *cur->blocks);
+ cur->blocks[cur->nblocks++] = b;
return b;
}
@@ -492,7 +530,11 @@
int type;
type = Lnone;
- if(strncmp(text, "+++", 3)==0)
+ if(strncmp(text, "⑨", 1)==0)
+ type = Lterm;
+ else if(strncmp(text, "diff", 4)==0)
+ type = Lhash;
+ else if(strncmp(text, "+++", 3)==0)
type = Lfile;
else if(strncmp(text, "---", 3)==0){
if(strlen(text) > 4)
@@ -522,6 +564,17 @@
}
void
+collapse(int v)
+{
+ int i;
+
+ for(i = 0; i < cur->nblocks; i++)
+ if(cur->blocks[i]->f != nil)
+ cur->blocks[i]->v = v;
+ eresize(0);
+}
+
+void
parse(int fd)
{
Biobuf *bp;
@@ -528,15 +581,13 @@
Block *b;
char *s, *f, *tab;
int t, n, ab, len;
+ int gotterm;
- blocks = nil;
- nblocks = 0;
- ab = 0;
- n = 0;
bp = Bfdopen(fd, OREAD);
if(bp==nil)
sysfatal("Bfdopen: %r");
- b = addblock();
+ gotterm = 0;
+ goto New;
for(;;){
s = Brdstr(bp, '\n', 1);
if(s==nil)
@@ -543,6 +594,22 @@
break;
t = linetype(s);
switch(t){
+ case Lterm:
+ gotterm = 1;
+ /* remove '--' and extra newline */
+ b->nlines--;
+ free(Brdstr(bp, '\n', 1));
+ New:
+ npatches++;
+ patches = realloc(patches, sizeof *patches * npatches);
+ cur = patches+npatches-1;
+ cur->blocks = nil;
+ cur->nblocks = 0;
+ cur->name = nil;
+ b = addblock();
+ n = 0;
+ ab = 0;
+ break;
case Lfile:
if(s[0] == '-'){
b = addblock();
@@ -568,6 +635,16 @@
case Lsep:
n = lineno(s) - 1; /* -1 as the separator is not an actual line */
if(0){
+ case Lhash:
+ f = strchr(s, ' ');
+ if(f != nil && (f = strchr(f+1, ' '))){
+ f++;
+ if(strcmp(f, "uncommitted") != 0){
+ cur->name = strdup(f);
+ cur->name[9] = '\0';
+ }
+ }
+ t = Lnone;
case Ladd:
case Lnone:
++n;
@@ -580,6 +657,9 @@
break;
}
}
+ if(gotterm)
+ npatches--;
+ cur = patches;
}
void
@@ -601,7 +681,11 @@
{ nil, &k, CHANRCV },
{ nil, nil, CHANEND },
};
- int b;
+ Menu menu = {
+ nil,
+ genmenu,
+ };
+ int b, n;
b = 0;
ARGBEGIN{
@@ -625,9 +709,8 @@
case 0:
break;
}
-
parse(0);
- if(nblocks==1 && blocks[0]->nlines==0){
+ if(cur->nblocks==1 && cur->blocks[0]->nlines==0){
fprint(2, "no diff\n");
exits(nil);
}
@@ -648,7 +731,23 @@
for(;;){
switch(alt(a)){
case Emouse:
- emouse(m);
+ if((m.buttons&2) != 0){
+ n = menuhit(2, mctl, &menu, nil);
+ switch(n){
+ case -1:
+ break;
+ case Mexpand: case Mcollapse:
+ collapse(n);
+ break;
+ default:
+ n -= Nmenu;
+ if(cur == patches+n)
+ break;
+ cur = patches+n;
+ eresize(0);
+ }
+ } else
+ emouse(m);
break;
case Eresize:
eresize(1);
--
⑨