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
This commit is contained in:
Russ Cox 2014-04-30 12:14:29 -04:00
parent 833216fef8
commit 4a3fb87264
12 changed files with 262 additions and 8 deletions

View file

@ -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

View file

@ -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);
}
}

View file

@ -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
{

View file

@ -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);
}

View file

@ -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, }
};

197
src/cmd/acme/logf.c Normal file
View file

@ -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);
}

View file

@ -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;

View file

@ -15,6 +15,7 @@ OFILES=\
exec.$O\
file.$O\
fsys.$O\
logf.$O\
look.$O\
regx.$O\
rows.$O\

View file

@ -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);
}

View file

@ -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; ){

View file

@ -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;

View file

@ -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;