diff options
author | Russ Cox <rsc@swtch.com> | 2014-04-30 12:14:29 -0400 |
---|---|---|
committer | Russ Cox <rsc@swtch.com> | 2014-04-30 12:14:29 -0400 |
commit | 4a3fb87264f8bc03fc62f00ef335056f30d18023 (patch) | |
tree | 77d52013a1fbb6fd967c1e146d5ddbf5ca0c065d | |
parent | 833216fef8b946895956737d205bcad7031bf06f (diff) | |
download | plan9port-4a3fb87264f8bc03fc62f00ef335056f30d18023.tar.gz plan9port-4a3fb87264f8bc03fc62f00ef335056f30d18023.tar.bz2 plan9port-4a3fb87264f8bc03fc62f00ef335056f30d18023.zip |
acme: add log file in acme root directory
Reading /mnt/acme/log reports a log of window create,
put, and delete events, as they happen. It blocks until the
next event is available.
Example log output:
8 new /Users/rsc/foo.go
8 put /Users/rsc/foo.go
8 del /Users/rsc/foo.go
This lets acme-aware programs react to file writes, for example
compiling code, running a test, or updating an import block.
TBR=r
R=r
https://codereview.appspot.com/89560044
-rw-r--r-- | man/man4/acme.4 | 28 | ||||
-rw-r--r-- | src/cmd/acme/acme.c | 2 | ||||
-rw-r--r-- | src/cmd/acme/dat.h | 7 | ||||
-rw-r--r-- | src/cmd/acme/exec.c | 11 | ||||
-rw-r--r-- | src/cmd/acme/fsys.c | 1 | ||||
-rw-r--r-- | src/cmd/acme/logf.c | 197 | ||||
-rw-r--r-- | src/cmd/acme/look.c | 10 | ||||
-rw-r--r-- | src/cmd/acme/mkfile | 1 | ||||
-rw-r--r-- | src/cmd/acme/rows.c | 1 | ||||
-rw-r--r-- | src/cmd/acme/util.c | 1 | ||||
-rw-r--r-- | src/cmd/acme/wind.c | 3 | ||||
-rw-r--r-- | src/cmd/acme/xfid.c | 8 |
12 files changed, 262 insertions, 8 deletions
diff --git a/man/man4/acme.4 b/man/man4/acme.4 index 78214cad..7449dbd2 100644 --- a/man/man4/acme.4 +++ b/man/man4/acme.4 @@ -73,7 +73,7 @@ was run. The window is created if necessary, but not until text is actually written. .TP .B consctl -Is an empty unwritable file present only for compatibility; there is no way +is an empty unwritable file present only for compatibility; there is no way to turn off `echo', for example, under .IR acme . .TP @@ -95,8 +95,32 @@ file. is an empty file, writable without effect, present only for compatibility with .BR rio . .TP +.B log +reports a log of window operations since the opening of the +.B log +file. +Each line describes a single operation using three fields separated by single spaces: +the decimal window ID, the operation, and the window name. +Reading from +.B log +blocks until there is an operation to report, so reading the file +can be used to monitor editor activity and react to changes. +The reported operations are +.L new +(window creation), +.L zerox +(window creation via zerox), +.LR get , +.LR put , +and +.LR del +(window deletion). +The window name can be the empty string; in particular it is empty in +.L new +log entries corresponding to windows created by external programs. +.TP .B new -A directory analogous to the numbered directories +is a directory analogous to the numbered directories .RI ( q.v. ). Accessing any file in diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c index 1005bae9..c45bc28d 100644 --- a/src/cmd/acme/acme.c +++ b/src/cmd/acme/acme.c @@ -303,6 +303,7 @@ readfile(Column *c, char *s) winresize(w, w->r, FALSE, TRUE); textscrdraw(&w->body); textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc); + xfidlog(w, "new"); } char *ignotes[] = { @@ -866,6 +867,7 @@ newwindowthread(void *v) recvp(cnewwindow); w = makenewwindow(nil); winsettag(w); + xfidlog(w, "new"); sendp(cnewwindow, w); } } diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h index 5c156eb6..df1a6422 100644 --- a/src/cmd/acme/dat.h +++ b/src/cmd/acme/dat.h @@ -8,6 +8,7 @@ enum Qeditout, Qindex, Qlabel, + Qlog, Qnew, QWaddr, @@ -391,6 +392,7 @@ struct Fid Mntdir *mntdir; int nrpart; uchar rpart[UTFmax]; + vlong logoff; // for putlog }; @@ -403,7 +405,6 @@ struct Xfid Fid *f; uchar *buf; int flushed; - }; void xfidctl(void *); @@ -418,6 +419,10 @@ void xfideventwrite(Xfid*, Window*); void xfidindexread(Xfid*); void xfidutfread(Xfid*, Text*, uint, int); int xfidruneread(Xfid*, Text*, uint, uint); +void xfidlogopen(Xfid*); +void xfidlogread(Xfid*); +void xfidlogflush(Xfid*); +void xfidlog(Window*, char*); struct Reffont { diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c index 6f8bf3f6..6fe423a6 100644 --- a/src/cmd/acme/exec.c +++ b/src/cmd/acme/exec.c @@ -347,6 +347,7 @@ void newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) { Column *c; + Window *w; USED(_0); USED(_1); @@ -356,8 +357,11 @@ newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) USED(_5); c = rowadd(et->row, nil, -1); - if(c) - winsettag(coladd(c, nil, nil, -1)); + if(c) { + w = coladd(c, nil, nil, -1); + winsettag(w); + xfidlog(w, "new"); + } } void @@ -562,6 +566,7 @@ zeroxx(Text *et, Text *t, Text *_1, int _2, int _3, Rune *_4, int _5) nw = coladd(t->w->col, nil, t->w, -1); /* ugly: fix locks so w->unlock works */ winlock1(nw, t->w->owner); + xfidlog(nw, "zerox"); } if(locked) winunlock(t->w); @@ -627,6 +632,7 @@ get(Text *et, Text *t, Text *argt, int flag1, int _0, Rune *arg, int narg) textsetselect(&u->w->tag, u->w->tag.file->b.nc, u->w->tag.file->b.nc); textscrdraw(u); } + xfidlog(w, "get"); } void @@ -782,6 +788,7 @@ put(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) } namer = bytetorune(name, &nname); putfile(f, 0, f->b.nc, namer, nname); + xfidlog(w, "put"); free(name); } diff --git a/src/cmd/acme/fsys.c b/src/cmd/acme/fsys.c index e285c908..549213d7 100644 --- a/src/cmd/acme/fsys.c +++ b/src/cmd/acme/fsys.c @@ -71,6 +71,7 @@ Dirtab dirtab[]= { "editout", QTFILE, Qeditout, 0200 }, { "index", QTFILE, Qindex, 0400 }, { "label", QTFILE, Qlabel, 0600 }, + { "log", QTFILE, Qlog, 0400 }, { "new", QTDIR, Qnew, 0500|DMDIR }, { nil, } }; diff --git a/src/cmd/acme/logf.c b/src/cmd/acme/logf.c new file mode 100644 index 00000000..ebcd4bfd --- /dev/null +++ b/src/cmd/acme/logf.c @@ -0,0 +1,197 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include <plumb.h> +#include "dat.h" +#include "fns.h" + +// State for global log file. +typedef struct Log Log; +struct Log +{ + QLock lk; + Rendez r; + + vlong start; // msg[0] corresponds to 'start' in the global sequence of events + + // queued events (nev=entries in ev, mev=capacity of p) + char **ev; + int nev; + int mev; + + // open acme/put files that need to read events + Fid **f; + int nf; + int mf; + + // active (blocked) reads waiting for events + Xfid **read; + int nread; + int mread; +}; + +static Log eventlog; + +void +xfidlogopen(Xfid *x) +{ + qlock(&eventlog.lk); + if(eventlog.nf >= eventlog.mf) { + eventlog.mf = eventlog.mf*2; + if(eventlog.mf == 0) + eventlog.mf = 8; + eventlog.f = erealloc(eventlog.f, eventlog.mf*sizeof eventlog.f[0]); + } + eventlog.f[eventlog.nf++] = x->f; + x->f->logoff = eventlog.start + eventlog.nev; + + qunlock(&eventlog.lk); +} + +void +xfidlogclose(Xfid *x) +{ + int i; + + qlock(&eventlog.lk); + for(i=0; i<eventlog.nf; i++) { + if(eventlog.f[i] == x->f) { + eventlog.f[i] = eventlog.f[--eventlog.nf]; + break; + } + } + qunlock(&eventlog.lk); +} + +void +xfidlogread(Xfid *x) +{ + char *p; + int i; + Fcall fc; + + qlock(&eventlog.lk); + if(eventlog.nread >= eventlog.mread) { + eventlog.mread = eventlog.mread*2; + if(eventlog.mread == 0) + eventlog.mread = 8; + eventlog.read = erealloc(eventlog.read, eventlog.mread*sizeof eventlog.read[0]); + } + eventlog.read[eventlog.nread++] = x; + + if(eventlog.r.l == nil) + eventlog.r.l = &eventlog.lk; + x->flushed = FALSE; + while(x->f->logoff >= eventlog.start+eventlog.nev && !x->flushed) + rsleep(&eventlog.r); + + for(i=0; i<eventlog.nread; i++) { + if(eventlog.read[i] == x) { + eventlog.read[i] = eventlog.read[--eventlog.nread]; + break; + } + } + + if(x->flushed) { + qunlock(&eventlog.lk); + respond(x, &fc, "read cancelled"); + return; + } + + i = x->f->logoff - eventlog.start; + p = estrdup(eventlog.ev[i]); + x->f->logoff++; + qunlock(&eventlog.lk); + + fc.data = p; + fc.count = strlen(p); + respond(x, &fc, nil); + free(p); +} + +void +xfidlogflush(Xfid *x) +{ + int i; + Xfid *rx; + + qlock(&eventlog.lk); + for(i=0; i<eventlog.nread; i++) { + rx = eventlog.read[i]; + if(rx->fcall.tag == x->fcall.oldtag) + rx->flushed = TRUE; + } + qunlock(&eventlog.lk); +} + +/* + * add a log entry for op on w. + * expected calls: + * + * op == "new" for each new window + * - caller of coladd or makenewwindow responsible for calling + * xfidlog after setting window name + * - exception: zerox + * + * op == "zerox" for new window created via zerox + * - called from zeroxx + * + * op == "get" for Get executed on window + * - called from get + * + * op == "put" for Put executed on window + * - called from put + * + * op == "del" for deleted window + * - called from winclose + */ +void +xfidlog(Window *w, char *op) +{ + int i, n; + vlong min; + File *f; + char *name; + + qlock(&eventlog.lk); + if(eventlog.nev >= eventlog.mev) { + // Remove and free any entries that all readers have read. + min = eventlog.start + eventlog.nev; + for(i=0; i<eventlog.nf; i++) { + if(min > eventlog.f[i]->logoff) + min = eventlog.f[i]->logoff; + } + if(min > eventlog.start) { + n = min - eventlog.start; + for(i=0; i<n; i++) + free(eventlog.ev[i]); + eventlog.nev -= n; + eventlog.start += n; + memmove(eventlog.ev, eventlog.ev+n, eventlog.nev*sizeof eventlog.ev[0]); + } + + // Otherwise grow. + if(eventlog.nev >= eventlog.mev) { + eventlog.mev = eventlog.mev*2; + if(eventlog.mev == 0) + eventlog.mev = 8; + eventlog.ev = erealloc(eventlog.ev, eventlog.mev*sizeof eventlog.ev[0]); + } + } + f = w->body.file; + name = runetobyte(f->name, f->nname); + if(name == nil) + name = estrdup(""); + eventlog.ev[eventlog.nev++] = smprint("%d %s %s\n", w->id, op, name); + free(name); + if(eventlog.r.l == nil) + eventlog.r.l = &eventlog.lk; + rwakeupall(&eventlog.r); + qunlock(&eventlog.lk); +} diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c index 8d136110..3c5765a8 100644 --- a/src/cmd/acme/look.c +++ b/src/cmd/acme/look.c @@ -298,6 +298,7 @@ plumbshow(Plumbmsg *m) winsettag(w); textscrdraw(&w->body); textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc); + xfidlog(w, "new"); } int @@ -770,6 +771,7 @@ openfile(Text *t, Expand *e) w->autoindent = ow->autoindent; }else w->autoindent = globalautoindent; + xfidlog(w, "new"); } if(e->a1 == e->a0) eval = FALSE; @@ -803,6 +805,7 @@ new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg) int na, nf; Expand e; Runestr rs; + Window *w; getarg(argt, FALSE, TRUE, &a, &na); if(a){ @@ -814,8 +817,11 @@ new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg) for(ndone=0; ; ndone++){ a = findbl(arg, narg, &na); if(a == arg){ - if(ndone==0 && et->col!=nil) - winsettag(coladd(et->col, nil, nil, -1)); + if(ndone==0 && et->col!=nil) { + w = coladd(et->col, nil, nil, -1); + winsettag(w); + xfidlog(w, "new"); + } break; } nf = narg-na; diff --git a/src/cmd/acme/mkfile b/src/cmd/acme/mkfile index a8696a15..18bea9e0 100644 --- a/src/cmd/acme/mkfile +++ b/src/cmd/acme/mkfile @@ -15,6 +15,7 @@ OFILES=\ exec.$O\ file.$O\ fsys.$O\ + logf.$O\ look.$O\ regx.$O\ rows.$O\ diff --git a/src/cmd/acme/rows.c b/src/cmd/acme/rows.c index 4f111ec3..965088e1 100644 --- a/src/cmd/acme/rows.c +++ b/src/cmd/acme/rows.c @@ -776,6 +776,7 @@ rowload(Row *row, char *file, int initing) q0 = q1 = 0; textshow(&w->body, q0, q1, 1); w->maxlines = min(w->body.fr.nlines, max(w->maxlines, w->body.fr.maxlines)); + xfidlog(w, "new"); Nextline: l = rdline(b, &line); } diff --git a/src/cmd/acme/util.c b/src/cmd/acme/util.c index de6cdf8c..28c99ad3 100644 --- a/src/cmd/acme/util.c +++ b/src/cmd/acme/util.c @@ -97,6 +97,7 @@ errorwin1(Rune *dir, int ndir, Rune **incl, int nincl) w = coladd(row.col[row.ncol-1], nil, nil, -1); w->filemenu = FALSE; winsetname(w, r, n); + xfidlog(w, "new"); } free(r); for(i=nincl; --i>=0; ){ diff --git a/src/cmd/acme/wind.c b/src/cmd/acme/wind.c index 1022154d..712eb1dc 100644 --- a/src/cmd/acme/wind.c +++ b/src/cmd/acme/wind.c @@ -320,6 +320,7 @@ winclose(Window *w) int i; if(decref(&w->ref) == 0){ + xfidlog(w, "del"); windirfree(w); textclose(&w->tag); textclose(&w->body); @@ -644,7 +645,7 @@ Rescue: } int -winclean(Window *w, int conservative) /* as it stands, conservative is always TRUE */ +winclean(Window *w, int conservative) { if(w->isscratch || w->isdir) /* don't whine if it's a guide file, error window, etc. */ return TRUE; diff --git a/src/cmd/acme/xfid.c b/src/cmd/acme/xfid.c index 671b324f..33732def 100644 --- a/src/cmd/acme/xfid.c +++ b/src/cmd/acme/xfid.c @@ -63,6 +63,8 @@ xfidflush(Xfid *x) Column *c; Xfid *wx; + xfidlogflush(x); + /* search windows for matching tag */ qlock(&row.lk); for(j=0; j<row.ncol; j++){ @@ -186,6 +188,9 @@ xfidopen(Xfid *x) } else{ switch(q){ + case Qlog: + xfidlogopen(x); + break; case Qeditout: if(!canqlock(&editoutlk)){ respond(x, &fc, Einuse); @@ -300,6 +305,9 @@ xfidread(Xfid *x) case Qindex: xfidindexread(x); return; + case Qlog: + xfidlogread(x); + return; default: warning(nil, "unknown qid %d\n", q); break; |