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
diff --git a/man/man4/acme.4 b/man/man4/acme.4
index 78214ca..7449dbd 100644
--- a/man/man4/acme.4
+++ b/man/man4/acme.4
@@ -73,7 +73,7 @@
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 @@
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 1005bae..c45bc28 100644
--- a/src/cmd/acme/acme.c
+++ b/src/cmd/acme/acme.c
@@ -303,6 +303,7 @@
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 @@
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 5c156eb..df1a642 100644
--- a/src/cmd/acme/dat.h
+++ b/src/cmd/acme/dat.h
@@ -8,6 +8,7 @@
Qeditout,
Qindex,
Qlabel,
+ Qlog,
Qnew,
QWaddr,
@@ -391,6 +392,7 @@
Mntdir *mntdir;
int nrpart;
uchar rpart[UTFmax];
+ vlong logoff; // for putlog
};
@@ -403,7 +405,6 @@
Fid *f;
uchar *buf;
int flushed;
-
};
void xfidctl(void *);
@@ -418,6 +419,10 @@
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 6f8bf3f..6fe423a 100644
--- a/src/cmd/acme/exec.c
+++ b/src/cmd/acme/exec.c
@@ -347,6 +347,7 @@
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 @@
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 @@
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 @@
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 @@
}
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 e285c90..549213d 100644
--- a/src/cmd/acme/fsys.c
+++ b/src/cmd/acme/fsys.c
@@ -71,6 +71,7 @@
{ "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 0000000..ebcd4bf
--- /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 8d13611..3c5765a 100644
--- a/src/cmd/acme/look.c
+++ b/src/cmd/acme/look.c
@@ -298,6 +298,7 @@
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 @@
w->autoindent = ow->autoindent;
}else
w->autoindent = globalautoindent;
+ xfidlog(w, "new");
}
if(e->a1 == e->a0)
eval = FALSE;
@@ -803,6 +805,7 @@
int na, nf;
Expand e;
Runestr rs;
+ Window *w;
getarg(argt, FALSE, TRUE, &a, &na);
if(a){
@@ -814,8 +817,11 @@
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 a8696a1..18bea9e 100644
--- a/src/cmd/acme/mkfile
+++ b/src/cmd/acme/mkfile
@@ -15,6 +15,7 @@
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 4f111ec..965088e 100644
--- a/src/cmd/acme/rows.c
+++ b/src/cmd/acme/rows.c
@@ -776,6 +776,7 @@
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 de6cdf8..28c99ad 100644
--- a/src/cmd/acme/util.c
+++ b/src/cmd/acme/util.c
@@ -97,6 +97,7 @@
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 1022154..712eb1d 100644
--- a/src/cmd/acme/wind.c
+++ b/src/cmd/acme/wind.c
@@ -320,6 +320,7 @@
int i;
if(decref(&w->ref) == 0){
+ xfidlog(w, "del");
windirfree(w);
textclose(&w->tag);
textclose(&w->body);
@@ -644,7 +645,7 @@
}
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 671b324..33732de 100644
--- a/src/cmd/acme/xfid.c
+++ b/src/cmd/acme/xfid.c
@@ -63,6 +63,8 @@
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 @@
}
else{
switch(q){
+ case Qlog:
+ xfidlogopen(x);
+ break;
case Qeditout:
if(!canqlock(&editoutlk)){
respond(x, &fc, Einuse);
@@ -300,6 +305,9 @@
case Qindex:
xfidindexread(x);
return;
+ case Qlog:
+ xfidlogread(x);
+ return;
default:
warning(nil, "unknown qid %d\n", q);
break;