📄 ecmd.c
字号:
voidprintposn(Text *t, int charsonly){ long l1, l2; if (t != nil && t->file != nil && t->file->name != nil) warning(nil, "%.*S:", t->file->nname, t->file->name); if(!charsonly){ l1 = 1+nlcount(t, 0, addr.r.q0); l2 = l1+nlcount(t, addr.r.q0, addr.r.q1); /* check if addr ends with '\n' */ if(addr.r.q1>0 && addr.r.q1>addr.r.q0 && textreadc(t, addr.r.q1-1)=='\n') --l2; warning(nil, "%lud", l1); if(l2 != l1) warning(nil, ",%lud", l2); warning(nil, "\n"); return; } warning(nil, "#%d", addr.r.q0); if(addr.r.q1 != addr.r.q0) warning(nil, ",#%d", addr.r.q1); warning(nil, "\n");}inteq_cmd(Text *t, Cmd *cp){ int charsonly; switch(cp->text->n){ case 0: charsonly = FALSE; break; case 1: if(cp->text->r[0] == '#'){ charsonly = TRUE; break; } default: SET(charsonly); editerror("newline expected"); } printposn(t, charsonly); return TRUE;}intnl_cmd(Text *t, Cmd *cp){ Address a; File *f; f = t->file; if(cp->addr == 0){ /* First put it on newline boundaries */ mkaddr(&a, f); addr = lineaddr(0, a, -1); a = lineaddr(0, a, 1); addr.r.q1 = a.r.q1; if(addr.r.q0==t->q0 && addr.r.q1==t->q1){ mkaddr(&a, f); addr = lineaddr(1, a, 1); } } textshow(t, addr.r.q0, addr.r.q1, 1); return TRUE;}intappend(File *f, Cmd *cp, long p){ if(cp->text->n > 0) eloginsert(f, p, cp->text->r, cp->text->n); f->curtext->q0 = p; f->curtext->q1 = p; return TRUE;}intpdisplay(File *f){ long p1, p2; int np; Rune *buf; p1 = addr.r.q0; p2 = addr.r.q1; if(p2 > f->nc) p2 = f->nc; buf = fbufalloc(); while(p1 < p2){ np = p2-p1; if(np>RBUFSIZE-1) np = RBUFSIZE-1; bufread(f, p1, buf, np); buf[np] = L'\0'; warning(nil, "%S", buf); p1 += np; } fbuffree(buf); f->curtext->q0 = addr.r.q0; f->curtext->q1 = addr.r.q1; return TRUE;}voidpfilename(File *f){ int dirty; Window *w; w = f->curtext->w; /* same check for dirty as in settag, but we know ncache==0 */ dirty = !w->isdir && !w->isscratch && f->mod; warning(nil, "%c%c%c %.*S\n", " '"[dirty], '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);}voidloopcmd(File *f, Cmd *cp, Range *rp, long nrp){ long i; for(i=0; i<nrp; i++){ f->curtext->q0 = rp[i].q0; f->curtext->q1 = rp[i].q1; cmdexec(f->curtext, cp); }}voidlooper(File *f, Cmd *cp, int xy){ long p, op, nrp; Range r, tr; Range *rp; r = addr.r; op= xy? -1 : r.q0; nest++; if(rxcompile(cp->re->r) == FALSE) editerror("bad regexp in %c command", cp->cmdc); nrp = 0; rp = nil; for(p = r.q0; p<=r.q1; ){ if(!rxexecute(f->curtext, nil, p, r.q1, &sel)){ /* no match, but y should still run */ if(xy || op>r.q1) break; tr.q0 = op, tr.q1 = r.q1; p = r.q1+1; /* exit next loop */ }else{ if(sel.r[0].q0==sel.r[0].q1){ /* empty match? */ if(sel.r[0].q0==op){ p++; continue; } p = sel.r[0].q1+1; }else p = sel.r[0].q1; if(xy) tr = sel.r[0]; else tr.q0 = op, tr.q1 = sel.r[0].q0; } op = sel.r[0].q1; nrp++; rp = erealloc(rp, nrp*sizeof(Range)); rp[nrp-1] = tr; } loopcmd(f, cp->cmd, rp, nrp); free(rp); --nest;}voidlinelooper(File *f, Cmd *cp){ long nrp, p; Range r, linesel; Address a, a3; Range *rp; nest++; nrp = 0; rp = nil; r = addr.r; a3.f = f; a3.r.q0 = a3.r.q1 = r.q0; a = lineaddr(0, a3, 1); linesel = a.r; for(p = r.q0; p<r.q1; p = a3.r.q1){ a3.r.q0 = a3.r.q1; if(p!=r.q0 || linesel.q1==p){ a = lineaddr(1, a3, 1); linesel = a.r; } if(linesel.q0 >= r.q1) break; if(linesel.q1 >= r.q1) linesel.q1 = r.q1; if(linesel.q1 > linesel.q0) if(linesel.q0>=a3.r.q1 && linesel.q1>a3.r.q1){ a3.r = linesel; nrp++; rp = erealloc(rp, nrp*sizeof(Range)); rp[nrp-1] = linesel; continue; } break; } loopcmd(f, cp->cmd, rp, nrp); free(rp); --nest;}struct Looper{ Cmd *cp; int XY; Window **w; int nw;} loopstruct; /* only one; X and Y can't nest */voidalllooper(Window *w, void *v){ Text *t; struct Looper *lp; Cmd *cp; lp = v; cp = lp->cp;// if(w->isscratch || w->isdir)// return; t = &w->body; /* only use this window if it's the current window for the file */ if(t->file->curtext != t) return;// if(w->nopen[QWevent] > 0)// return; /* no auto-execute on files without names */ if(cp->re==nil && t->file->nname==0) return; if(cp->re==nil || filematch(t->file, cp->re)==lp->XY){ lp->w = erealloc(lp->w, (lp->nw+1)*sizeof(Window*)); lp->w[lp->nw++] = w; }}voidalllocker(Window *w, void *v){ if(v) incref(w); else winclose(w);}voidfilelooper(Cmd *cp, int XY){ int i; if(Glooping++) editerror("can't nest %c command", "YX"[XY]); nest++; loopstruct.cp = cp; loopstruct.XY = XY; if(loopstruct.w) /* error'ed out last time */ free(loopstruct.w); loopstruct.w = nil; loopstruct.nw = 0; allwindows(alllooper, &loopstruct); /* * add a ref to all windows to keep safe windows accessed by X * that would not otherwise have a ref to hold them up during * the shenanigans. note this with globalincref so that any * newly created windows start with an extra reference. */ allwindows(alllocker, (void*)1); globalincref = 1; for(i=0; i<loopstruct.nw; i++) cmdexec(&loopstruct.w[i]->body, cp->cmd); allwindows(alllocker, (void*)0); globalincref = 0; free(loopstruct.w); loopstruct.w = nil; --Glooping; --nest;}voidnextmatch(File *f, String *r, long p, int sign){ if(rxcompile(r->r) == FALSE) editerror("bad regexp in command address"); if(sign >= 0){ if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel)) editerror("no match for regexp"); if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q0==p){ if(++p>f->nc) p = 0; if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel)) editerror("address"); } }else{ if(!rxbexecute(f->curtext, p, &sel)) editerror("no match for regexp"); if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q1==p){ if(--p<0) p = f->nc; if(!rxbexecute(f->curtext, p, &sel)) editerror("address"); } }}File *matchfile(String*);Address charaddr(long, Address, int);Address lineaddr(long, Address, int);Addresscmdaddress(Addr *ap, Address a, int sign){ File *f = a.f; Address a1, a2; do{ switch(ap->type){ case 'l': case '#': a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign); break; case '.': mkaddr(&a, f); break; case '$': a.r.q0 = a.r.q1 = f->nc; break; case '\'':editerror("can't handle '");// a.r = f->mark; break; case '?': sign = -sign; if(sign == 0) sign = -1; /* fall through */ case '/': nextmatch(f, ap->re, sign>=0? a.r.q1 : a.r.q0, sign); a.r = sel.r[0]; break; case '"': f = matchfile(ap->re); mkaddr(&a, f); break; case '*': a.r.q0 = 0, a.r.q1 = f->nc; return a; case ',': case ';': if(ap->left) a1 = cmdaddress(ap->left, a, 0); else a1.f = a.f, a1.r.q0 = a1.r.q1 = 0; if(ap->type == ';'){ f = a1.f; a = a1; f->curtext->q0 = a1.r.q0; f->curtext->q1 = a1.r.q1; } if(ap->next) a2 = cmdaddress(ap->next, a, 0); else a2.f = a.f, a2.r.q0 = a2.r.q1 = f->nc; if(a1.f != a2.f) editerror("addresses in different files"); a.f = a1.f, a.r.q0 = a1.r.q0, a.r.q1 = a2.r.q1; if(a.r.q1 < a.r.q0) editerror("addresses out of order"); return a; case '+': case '-': sign = 1; if(ap->type == '-') sign = -1; if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-') a = lineaddr(1L, a, sign); break; default: error("cmdaddress"); return a; } }while(ap = ap->next); /* assign = */ return a;}struct Tofile{ File *f; String *r;};voidalltofile(Window *w, void *v){ Text *t; struct Tofile *tp; tp = v; if(tp->f != nil) return; if(w->isscratch || w->isdir) return; t = &w->body; /* only use this window if it's the current window for the file */ if(t->file->curtext != t) return;// if(w->nopen[QWevent] > 0)// return; if(runeeq(tp->r->r, tp->r->n, t->file->name, t->file->nname)) tp->f = t->file;}File*tofile(String *r){ struct Tofile t; String rr; rr.r = skipbl(r->r, r->n, &rr.n); t.f = nil; t.r = &rr; allwindows(alltofile, &t); if(t.f == nil) editerror("no such file\"%S\"", rr.r); return t.f;}voidallmatchfile(Window *w, void *v){ struct Tofile *tp; Text *t; tp = v; if(w->isscratch || w->isdir) return; t = &w->body; /* only use this window if it's the current window for the file */ if(t->file->curtext != t) return;// if(w->nopen[QWevent] > 0)// return; if(filematch(w->body.file, tp->r)){ if(tp->f != nil) editerror("too many files match \"%S\"", tp->r->r); tp->f = w->body.file; }}File*matchfile(String *r){ struct Tofile tf; tf.f = nil; tf.r = r; allwindows(allmatchfile, &tf); if(tf.f == nil) editerror("no file matches \"%S\"", r->r); return tf.f;}intfilematch(File *f, String *r){ char *buf; Rune *rbuf; Window *w; int match, i, dirty; Rangeset s; /* compile expr first so if we get an error, we haven't allocated anything */ if(rxcompile(r->r) == FALSE) editerror("bad regexp in file match"); buf = fbufalloc(); w = f->curtext->w; /* same check for dirty as in settag, but we know ncache==0 */ dirty = !w->isdir && !w->isscratch && f->mod; snprint(buf, BUFSIZE, "%c%c%c %.*S\n", " '"[dirty], '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name); rbuf = bytetorune(buf, &i); fbuffree(buf); match = rxexecute(nil, rbuf, 0, i, &s); free(rbuf); return match;}Addresscharaddr(long l, Address addr, int sign){ if(sign == 0) addr.r.q0 = addr.r.q1 = l; else if(sign < 0) addr.r.q1 = addr.r.q0 -= l; else if(sign > 0) addr.r.q0 = addr.r.q1 += l; if(addr.r.q0<0 || addr.r.q1>addr.f->nc) editerror("address out of range"); return addr;}Addresslineaddr(long l, Address addr, int sign){ int n; int c; File *f = addr.f; Address a; long p; a.f = f; if(sign >= 0){ if(l == 0){ if(sign==0 || addr.r.q1==0){ a.r.q0 = a.r.q1 = 0; return a; } a.r.q0 = addr.r.q1; p = addr.r.q1-1; }else{ if(sign==0 || addr.r.q1==0){ p = 0; n = 1; }else{ p = addr.r.q1-1; n = textreadc(f->curtext, p++)=='\n'; } while(n < l){ if(p >= f->nc) editerror("address out of range"); if(textreadc(f->curtext, p++) == '\n') n++; } a.r.q0 = p; } while(p < f->nc && textreadc(f->curtext, p++)!='\n') ; a.r.q1 = p; }else{ p = addr.r.q0; if(l == 0) a.r.q1 = addr.r.q0; else{ for(n = 0; n<l; ){ /* always runs once */ if(p == 0){ if(++n != l) editerror("address out of range"); }else{ c = textreadc(f->curtext, p-1); if(c != '\n' || ++n != l) p--; } } a.r.q1 = p; if(p > 0) p--; } while(p > 0 && textreadc(f->curtext, p-1)!='\n') /* lines start after a newline */ p--; a.r.q0 = p; } return a;}struct Filecheck{ File *f; Rune *r; int nr;};voidallfilecheck(Window *w, void *v){ struct Filecheck *fp; File *f; fp = v; f = w->body.file; if(w->body.file == fp->f) return; if(runeeq(fp->r, fp->nr, f->name, f->nname)) warning(nil, "warning: duplicate file name \"%.*S\"\n", fp->nr, fp->r);}Rune*cmdname(File *f, String *str, int set){ Rune *r, *s; int n; struct Filecheck fc; Runestr newname; r = nil; n = str->n; s = str->r; if(n == 0){ /* no name; use existing */ if(f->nname == 0) return nil; r = runemalloc(f->nname+1); runemove(r, f->name, f->nname); return r; } s = skipbl(s, n, &n); if(n == 0) goto Return; if(s[0] == '/'){ r = runemalloc(n+1); runemove(r, s, n); }else{ newname = dirname(f->curtext, runestrdup(s), n); n = newname.nr; r = runemalloc(n+1); /* NUL terminate */ runemove(r, newname.r, n); free(newname.r); } fc.f = f; fc.r = r; fc.nr = n; allwindows(allfilecheck, &fc); if(f->nname == 0) set = TRUE; Return: if(set && !runeeq(r, n, f->name, f->nname)){ filemark(f); f->mod = TRUE; f->curtext->w->dirty = TRUE; winsetname(f->curtext->w, r, n); } return r;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -