/* called while row is locked */ void flushwarnings(void) { Warning *warn, *next; Window *w; Text *t; int owner, nr, q0, n; Rune *r; for(warn=warnings; warn; warn=next) { w = errorwin(warn->md, 'E'); t = &w->body; owner = w->owner; if(owner == 0) w->owner = 'E'; wincommit(w, t); /* * Most commands don't generate much output. For instance, * Edit ,>cat goes through /dev/cons and is already in blocks * because of the i/o system, but a few can. Edit ,p will * put the entire result into a single hunk. So it's worth doing * this in blocks (and putting the text in a buffer in the first * place), to avoid a big memory footprint. */ r = fbufalloc(); q0 = t->file->nc; for(n = 0; n < warn->buf.nc; n += nr){ nr = warn->buf.nc - n; if(nr > RBUFSIZE) nr = RBUFSIZE; bufread(&warn->buf, n, r, nr); textbsinsert(t, t->file->nc, r, nr, TRUE, &nr); } textshow(t, q0, t->file->nc, 1); free(r); winsettag(t->w); textscrdraw(t); w->owner = owner; w->dirty = FALSE; winunlock(w); bufclose(&warn->buf); next = warn->next; if(warn->md) fsysdelid(warn->md); free(warn); } warnings = nil; }
void texttype(Text *t, Rune r) { uint q0, q1; int nnb, nb, n, i; int nr; Rune *rp; Text *u; if(t->what!=Body && r=='\n') return; nr = 1; rp = &r; switch(r){ case Kleft: if(t->q0 > 0){ typecommit(t); textshow(t, t->q0-1, t->q0-1, TRUE); } return; case Kright: if(t->q1 < t->file->nc){ typecommit(t); textshow(t, t->q1+1, t->q1+1, TRUE); } return; case Kdown: n = t->maxlines/3; goto case_Down; case Kscrollonedown: n = mousescrollsize(t->maxlines); if(n <= 0) n = 1; goto case_Down; case Kpgdown: n = 2*t->maxlines/3; case_Down: q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height)); textsetorigin(t, q0, TRUE); return; case Kup: n = t->maxlines/3; goto case_Up; case Kscrolloneup: n = mousescrollsize(t->maxlines); goto case_Up; case Kpgup: n = 2*t->maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, TRUE); return; case Khome: typecommit(t); textshow(t, 0, 0, FALSE); return; case Kend: typecommit(t); textshow(t, t->file->nc, t->file->nc, FALSE); return; case 0x01: /* ^A: beginning of line */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); textshow(t, t->q0-nnb, t->q0-nnb, TRUE); return; case 0x05: /* ^E: end of line */ typecommit(t); q0 = t->q0; while(q0<t->file->nc && textreadc(t, q0)!='\n') q0++; textshow(t, q0, q0, TRUE); return; } if(t->what == Body){ seq++; filemark(t->file); } if(t->q1 > t->q0){ if(t->ncache != 0) error("text.type"); cut(t, t, nil, TRUE, TRUE, nil, 0); t->eq0 = ~0; } textshow(t, t->q0, t->q0, 1); switch(r){ case 0x06: case Kins: rp = textcomplete(t); if(rp == nil) return; nr = runestrlen(rp); break; /* fall through to normal insertion case */ case 0x1B: if(t->eq0 != ~0) textsetselect(t, t->eq0, t->q0); if(t->ncache > 0) typecommit(t); return; case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nnb = textbswidth(t, r); q1 = t->q0; q0 = q1-nnb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nnb = q1-q0; } if(nnb <= 0) return; for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; u->nofill = TRUE; nb = nnb; n = u->ncache; if(n > 0){ if(q1 != u->cq0+n) error("text.type backspace"); if(n > nb) n = nb; u->ncache -= n; textdelete(u, q1-n, q1, FALSE); nb -= n; } if(u->eq0==q1 || u->eq0==~0) u->eq0 = q0; if(nb && u==t) textdelete(u, q0, q0+nb, TRUE); if(u != t) textsetselect(u, u->q0, u->q1); else textsetselect(t, q0, q0); u->nofill = FALSE; } for(i=0; i<t->file->ntext; i++) textfill(t->file->text[i]); return; case '\n': if(t->w->autoindent){ /* find beginning of previous line using backspace code */ nnb = textbswidth(t, 0x15); /* ^U case */ rp = runemalloc(nnb + 1); nr = 0; rp[nr++] = r; for(i=0; i<nnb; i++){ r = textreadc(t, t->q0-nnb+i); if(r != ' ' && r != '\t') break; rp[nr++] = r; } } break; /* fall through to normal code */ } /* otherwise ordinary character; just insert, typically in caches of all texts */ for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u->eq0 == ~0) u->eq0 = t->q0; if(u->ncache == 0) u->cq0 = t->q0; else if(t->q0 != u->cq0+u->ncache) error("text.type cq1"); textinsert(u, t->q0, rp, nr, FALSE); if(u != t) textsetselect(u, u->q0, u->q1); if(u->ncache+nr > u->ncachealloc){ u->ncachealloc += 10 + nr; u->cache = runerealloc(u->cache, u->ncachealloc); } runemove(u->cache+u->ncache, rp, nr); u->ncache += nr; } if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(r=='\n' && t->w!=nil) wincommit(t->w, t); }
void mousethread(void *v) { Text *t, *argt; int but; uint q0, q1; Window *w; Plumbmsg *pm; Mouse m; char *act; enum { MResize, MMouse, MPlumb, MWarnings, NMALT }; static Alt alts[NMALT+1]; USED(v); threadsetname("mousethread"); alts[MResize].c = mousectl->resizec; alts[MResize].v = nil; alts[MResize].op = CHANRCV; alts[MMouse].c = mousectl->c; alts[MMouse].v = &mousectl->m; alts[MMouse].op = CHANRCV; alts[MPlumb].c = cplumb; alts[MPlumb].v = ± alts[MPlumb].op = CHANRCV; alts[MWarnings].c = cwarn; alts[MWarnings].v = nil; alts[MWarnings].op = CHANRCV; if(cplumb == nil) alts[MPlumb].op = CHANNOP; alts[NMALT].op = CHANEND; for(;;){ qlock(&row.lk); flushwarnings(); qunlock(&row.lk); flushimage(display, 1); switch(alt(alts)){ case MResize: if(getwindow(display, Refnone) < 0) error("attach to window"); draw(screen, screen->r, display->white, nil, ZP); iconinit(); scrlresize(); rowresize(&row, screen->clipr); break; case MPlumb: if(strcmp(pm->type, "text") == 0){ act = plumblookup(pm->attr, "action"); if(act==nil || strcmp(act, "showfile")==0) plumblook(pm); else if(strcmp(act, "showdata")==0) plumbshow(pm); } plumbfree(pm); break; case MWarnings: break; case MMouse: /* * Make a copy so decisions are consistent; mousectl changes * underfoot. Can't just receive into m because this introduces * another race; see /sys/src/libdraw/mouse.c. */ m = mousectl->m; qlock(&row.lk); t = rowwhich(&row, m.xy); if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){ winlock(mousetext->w, 'M'); mousetext->eq0 = ~0; wincommit(mousetext->w, mousetext); winunlock(mousetext->w); } mousetext = t; if(t == nil) goto Continue; w = t->w; if(t==nil || m.buttons==0) goto Continue; but = 0; if(m.buttons == 1) but = 1; else if(m.buttons == 2) but = 2; else if(m.buttons == 4) but = 3; barttext = t; if(t->what==Body && ptinrect(m.xy, t->scrollr)){ if(but){ if(swapscrollbuttons){ if(but == 1) but = 3; else if(but == 3) but = 1; } winlock(w, 'M'); t->eq0 = ~0; textscroll(t, but); winunlock(w); } goto Continue; } /* scroll buttons, wheels, etc. */ if(w != nil && (m.buttons & (8|16))){ if(m.buttons & 8) but = Kscrolloneup; else but = Kscrollonedown; winlock(w, 'M'); t->eq0 = ~0; texttype(t, but); winunlock(w); goto Continue; } if(ptinrect(m.xy, t->scrollr)){ if(but){ if(t->what == Columntag) rowdragcol(&row, t->col, but); else if(t->what == Tag){ coldragwin(t->col, t->w, but); if(t->w) barttext = &t->w->body; } if(t->col) activecol = t->col; } goto Continue; } if(m.buttons){ if(w) winlock(w, 'M'); t->eq0 = ~0; if(w) wincommit(w, t); else textcommit(t, TRUE); if(m.buttons & 1){ textselect(t); if(w) winsettag(w); argtext = t; seltext = t; if(t->col) activecol = t->col; /* button 1 only */ if(t->w!=nil && t==&t->w->body) activewin = t->w; }else if(m.buttons & 2){ if(textselect2(t, &q0, &q1, &argt)) execute(t, q0, q1, FALSE, argt); }else if(m.buttons & 4){ if(textselect3(t, &q0, &q1)) look3(t, q0, q1, FALSE); } if(w) winunlock(w); goto Continue; } Continue: qunlock(&row.lk); break; } } }
void keyboardthread(void *v) { Rune r; Timer *timer; Text *t; enum { KTimer, KKey, NKALT }; static Alt alts[NKALT+1]; USED(v); alts[KTimer].c = nil; alts[KTimer].v = nil; alts[KTimer].op = CHANNOP; alts[KKey].c = keyboardctl->c; alts[KKey].v = &r; alts[KKey].op = CHANRCV; alts[NKALT].op = CHANEND; timer = nil; typetext = nil; threadsetname("keyboardthread"); for(;;){ switch(alt(alts)){ case KTimer: timerstop(timer); t = typetext; if(t!=nil && t->what==Tag){ winlock(t->w, 'K'); wincommit(t->w, t); winunlock(t->w); flushimage(display, 1); } alts[KTimer].c = nil; alts[KTimer].op = CHANNOP; break; case KKey: casekeyboard: typetext = rowtype(&row, r, mouse->xy); t = typetext; if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */ activecol = t->col; if(t!=nil && t->w!=nil) t->w->body.file->curtext = &t->w->body; if(timer != nil) timercancel(timer); if(t!=nil && t->what==Tag) { timer = timerstart(500); alts[KTimer].c = timer->c; alts[KTimer].op = CHANRCV; }else{ timer = nil; alts[KTimer].c = nil; alts[KTimer].op = CHANNOP; } if(nbrecv(keyboardctl->c, &r) > 0) goto casekeyboard; flushimage(display, 1); break; } } }
void xfidutfread(Xfid *x, Text *t, uint q1, int qid) { Fcall fc; Window *w; Rune *r; char *b, *b1; uint q, off, boff; int m, n, nr, nb; w = t->w; wincommit(w, t); off = x->fcall.offset; r = fbufalloc(); b = fbufalloc(); b1 = fbufalloc(); n = 0; if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){ boff = w->utflastboff; q = w->utflastq; }else{ /* BUG: stupid code: scan from beginning */ boff = 0; q = 0; } w->utflastqid = qid; while(q<q1 && n<x->fcall.count){ /* * Updating here avoids partial rune problem: we're always on a * char boundary. The cost is we will usually do one more read * than we really need, but that's better than being n^2. */ w->utflastboff = boff; w->utflastq = q; nr = q1-q; if(nr > BUFSIZE/UTFmax) nr = BUFSIZE/UTFmax; bufread(&t->file->b, q, r, nr); nb = snprint(b, BUFSIZE+1, "%.*S", nr, r); if(boff >= off){ m = nb; if(boff+m > off+x->fcall.count) m = off+x->fcall.count - boff; memmove(b1+n, b, m); n += m; }else if(boff+nb > off){ if(n != 0) error("bad count in utfrune"); m = nb - (off-boff); if(m > x->fcall.count) m = x->fcall.count; memmove(b1, b+(off-boff), m); n += m; } boff += nb; q += nr; } fbuffree(r); fbuffree(b); fc.count = n; fc.data = b1; respond(x, &fc, nil); fbuffree(b1); }
void xfidwrite(Xfid *x) { Fcall fc; int c, cnt, qid, q, nb, nr, eval; char buf[64], *err; Window *w; Rune *r; Range a; Text *t; uint q0, tq0, tq1; qid = FILE(x->f->qid); w = x->f->w; if(w){ c = 'F'; if(qid==QWtag || qid==QWbody) c = 'E'; winlock(w, c); if(w->col == nil){ winunlock(w); respond(x, &fc, Edel); return; } } x->fcall.data[x->fcall.count] = 0; switch(qid){ case Qcons: w = errorwin(x->f->mntdir, 'X'); t=&w->body; goto BodyTag; case Qlabel: fc.count = x->fcall.count; respond(x, &fc, nil); break; case QWaddr: x->fcall.data[x->fcall.count] = 0; r = bytetorune(x->fcall.data, &nr); t = &w->body; wincommit(w, t); eval = TRUE; a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb); free(r); if(nb < nr){ respond(x, &fc, Ebadaddr); break; } if(!eval){ respond(x, &fc, Eaddr); break; } w->addr = a; fc.count = x->fcall.count; respond(x, &fc, nil); break; case Qeditout: case QWeditout: r = bytetorune(x->fcall.data, &nr); if(w) err = edittext(w, w->wrselrange.q1, r, nr); else err = edittext(nil, 0, r, nr); free(r); if(err != nil){ respond(x, &fc, err); break; } fc.count = x->fcall.count; respond(x, &fc, nil); break; case QWerrors: w = errorwinforwin(w); t = &w->body; goto BodyTag; case QWbody: case QWwrsel: t = &w->body; goto BodyTag; case QWctl: xfidctlwrite(x, w); break; case QWdata: a = w->addr; t = &w->body; wincommit(w, t); if(a.q0>t->file->b.nc || a.q1>t->file->b.nc){ respond(x, &fc, Eaddr); break; } r = runemalloc(x->fcall.count); cvttorunes(x->fcall.data, x->fcall.count, r, &nb, &nr, nil); if(w->nomark == FALSE){ seq++; filemark(t->file); } q0 = a.q0; if(a.q1 > q0){ textdelete(t, q0, a.q1, TRUE); w->addr.q1 = q0; } tq0 = t->q0; tq1 = t->q1; textinsert(t, q0, r, nr, TRUE); if(tq0 >= q0) tq0 += nr; if(tq1 >= q0) tq1 += nr; textsetselect(t, tq0, tq1); if(!t->w->noscroll) textshow(t, q0, q0+nr, 0); textscrdraw(t); winsettag(w); free(r); w->addr.q0 += nr; w->addr.q1 = w->addr.q0; fc.count = x->fcall.count; respond(x, &fc, nil); break; case QWevent: xfideventwrite(x, w); break; case QWtag: t = &w->tag; goto BodyTag; BodyTag: q = x->f->nrpart; cnt = x->fcall.count; if(q > 0){ memmove(x->fcall.data+q, x->fcall.data, cnt); /* there's room; see fsysproc */ memmove(x->fcall.data, x->f->rpart, q); cnt += q; x->f->nrpart = 0; } r = runemalloc(cnt); cvttorunes(x->fcall.data, cnt-UTFmax, r, &nb, &nr, nil); /* approach end of buffer */ while(fullrune(x->fcall.data+nb, cnt-nb)){ c = nb; nb += chartorune(&r[nr], x->fcall.data+c); if(r[nr]) nr++; } if(nb < cnt){ memmove(x->f->rpart, x->fcall.data+nb, cnt-nb); x->f->nrpart = cnt-nb; } if(nr > 0){ wincommit(w, t); if(qid == QWwrsel){ q0 = w->wrselrange.q1; if(q0 > t->file->b.nc) q0 = t->file->b.nc; }else q0 = t->file->b.nc; if(qid == QWtag) textinsert(t, q0, r, nr, TRUE); else{ if(w->nomark == FALSE){ seq++; filemark(t->file); } q0 = textbsinsert(t, q0, r, nr, TRUE, &nr); textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */ if(qid!=QWwrsel && !t->w->noscroll) textshow(t, q0+nr, q0+nr, 1); textscrdraw(t); } winsettag(w); if(qid == QWwrsel) w->wrselrange.q1 += nr; free(r); } fc.count = x->fcall.count; respond(x, &fc, nil); break; default: sprint(buf, "unknown qid %d in write", qid); respond(x, &fc, buf); break; } if(w) winunlock(w); }
void rowdump(Row *row, char *file) { int i, j, fd, m, n, dumped; uint q0, q1; Biobuf *b; char *buf, *a, *fontname; Rune *r; Column *c; Window *w, *w1; Text *t; if(row->ncol == 0) return; buf = fbufalloc(); if(file == nil){ if(home == nil){ warning(nil, "can't find file for dump: $home not defined\n"); goto Rescue; } sprint(buf, "%s/acme.dump", home); file = buf; } fd = create(file, OWRITE, 0600); if(fd < 0){ warning(nil, "can't open %s: %r\n", file); goto Rescue; } b = emalloc(sizeof(Biobuf)); Binit(b, fd, OWRITE); r = fbufalloc(); Bprint(b, "%s\n", wdir); Bprint(b, "%s\n", fontnames[0]); Bprint(b, "%s\n", fontnames[1]); for(i=0; i<row->ncol; i++){ c = row->col[i]; Bprint(b, "%11d", 100*(c->r.min.x-row->r.min.x)/Dx(row->r)); if(i == row->ncol-1) Bputc(b, '\n'); else Bputc(b, ' '); } for(i=0; i<row->ncol; i++){ c = row->col[i]; for(j=0; j<c->nw; j++) c->w[j]->body.file->dumpid = 0; } for(i=0; i<row->ncol; i++){ c = row->col[i]; for(j=0; j<c->nw; j++){ w = c->w[j]; wincommit(w, &w->tag); t = &w->body; /* windows owned by others get special treatment */ if(w->nopen[QWevent] > 0) if(w->dumpstr == nil) continue; /* zeroxes of external windows are tossed */ if(t->file->ntext > 1) for(n=0; n<t->file->ntext; n++){ w1 = t->file->text[n]->w; if(w == w1) continue; if(w1->nopen[QWevent]) goto Continue2; } fontname = ""; if(t->reffont->f != font) fontname = t->reffont->f->name; if(t->file->nname) a = runetobyte(t->file->name, t->file->nname); else a = emalloc(1); if(t->file->dumpid){ dumped = FALSE; Bprint(b, "x%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid, w->body.q0, w->body.q1, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), fontname); }else if(w->dumpstr){ dumped = FALSE; Bprint(b, "e%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid, 0, 0, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), fontname); }else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){ dumped = FALSE; t->file->dumpid = w->id; Bprint(b, "f%11d %11d %11d %11d %11d %s\n", i, w->id, w->body.q0, w->body.q1, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), fontname); }else{ dumped = TRUE; t->file->dumpid = w->id; Bprint(b, "F%11d %11d %11d %11d %11d %11d %s\n", i, j, w->body.q0, w->body.q1, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), w->body.file->nc, fontname); } free(a); winctlprint(w, buf, 0); Bwrite(b, buf, strlen(buf)); m = min(RBUFSIZE, w->tag.file->nc); bufread(w->tag.file, 0, r, m); n = 0; while(n<m && r[n]!='\n') n++; r[n++] = '\n'; Bprint(b, "%.*S", n, r); if(dumped){ q0 = 0; q1 = t->file->nc; while(q0 < q1){ n = q1 - q0; if(n > BUFSIZE/UTFmax) n = BUFSIZE/UTFmax; bufread(t->file, q0, r, n); Bprint(b, "%.*S", n, r); q0 += n; } } if(w->dumpstr){ if(w->dumpdir) Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr); else Bprint(b, "\n%s\n", w->dumpstr); } Continue2:; } } Bterm(b); close(fd); free(b); fbuffree(r); Rescue: fbuffree(buf); }
void texttype(Text *t, Rune r) { uint q0, q1; int nnb, nb, n, i; int nr; Rune *rp; Text *u; if(t->what!=Body && t->what!=Tag && r=='\n') return; if(t->what == Tag) t->w->tagsafe = FALSE; nr = 1; rp = &r; switch(r){ case Kleft: typecommit(t); if(t->q0 > 0) textshow(t, t->q0-1, t->q0-1, TRUE); return; case Kright: typecommit(t); if(t->q1 < t->file->b.nc) textshow(t, t->q1+1, t->q1+1, TRUE); return; case Kdown: if(t->what == Tag) goto Tagdown; n = t->fr.maxlines/3; goto case_Down; case Kscrollonedown: if(t->what == Tag) goto Tagdown; n = mousescrollsize(t->fr.maxlines); if(n <= 0) n = 1; goto case_Down; case Kpgdown: n = 2*t->fr.maxlines/3; case_Down: q0 = t->org+frcharofpt(&t->fr, Pt(t->fr.r.min.x, t->fr.r.min.y+n*t->fr.font->height)); textsetorigin(t, q0, TRUE); return; case Kup: if(t->what == Tag) goto Tagup; n = t->fr.maxlines/3; goto case_Up; case Kscrolloneup: if(t->what == Tag) goto Tagup; n = mousescrollsize(t->fr.maxlines); goto case_Up; case Kpgup: n = 2*t->fr.maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, TRUE); return; case Khome: typecommit(t); if(t->org > t->iq1) { q0 = textbacknl(t, t->iq1, 1); textsetorigin(t, q0, TRUE); } else textshow(t, 0, 0, FALSE); return; case Kend: typecommit(t); if(t->iq1 > t->org+t->fr.nchars) { if(t->iq1 > t->file->b.nc) { // should not happen, but does. and it will crash textbacknl. t->iq1 = t->file->b.nc; } q0 = textbacknl(t, t->iq1, 1); textsetorigin(t, q0, TRUE); } else textshow(t, t->file->b.nc, t->file->b.nc, FALSE); return; case 0x01: /* ^A: beginning of line */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); textshow(t, t->q0-nnb, t->q0-nnb, TRUE); return; case 0x05: /* ^E: end of line */ typecommit(t); q0 = t->q0; while(q0<t->file->b.nc && textreadc(t, q0)!='\n') q0++; textshow(t, q0, q0, TRUE); return; case Kcmd+'c': /* %C: copy */ typecommit(t); cut(t, t, nil, TRUE, FALSE, nil, 0); return; case Kcmd+'z': /* %Z: undo */ typecommit(t); undo(t, nil, nil, TRUE, 0, nil, 0); return; Tagdown: /* expand tag to show all text */ if(!t->w->tagexpand){ t->w->tagexpand = TRUE; winresize(t->w, t->w->r, FALSE, TRUE); } return; Tagup: /* shrink tag to single line */ if(t->w->tagexpand){ t->w->tagexpand = FALSE; t->w->taglines = 1; winresize(t->w, t->w->r, FALSE, TRUE); } return; } if(t->what == Body){ seq++; filemark(t->file); } /* cut/paste must be done after the seq++/filemark */ switch(r){ case Kcmd+'x': /* %X: cut */ typecommit(t); if(t->what == Body){ seq++; filemark(t->file); } cut(t, t, nil, TRUE, TRUE, nil, 0); textshow(t, t->q0, t->q0, 1); t->iq1 = t->q0; return; case Kcmd+'v': /* %V: paste */ typecommit(t); if(t->what == Body){ seq++; filemark(t->file); } paste(t, t, nil, TRUE, FALSE, nil, 0); textshow(t, t->q0, t->q1, 1); t->iq1 = t->q1; return; } if(t->q1 > t->q0){ if(t->ncache != 0) error("text.type"); cut(t, t, nil, TRUE, TRUE, nil, 0); t->eq0 = ~0; } textshow(t, t->q0, t->q0, 1); switch(r){ case 0x06: /* ^F: complete */ case Kins: typecommit(t); rp = textcomplete(t); if(rp == nil) return; nr = runestrlen(rp); break; /* fall through to normal insertion case */ case 0x1B: if(t->eq0 != ~0) { if(t->eq0 <= t->q0) textsetselect(t, t->eq0, t->q0); else textsetselect(t, t->q0, t->eq0); } if(t->ncache > 0) typecommit(t); t->iq1 = t->q0; return; case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nnb = textbswidth(t, r); q1 = t->q0; q0 = q1-nnb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nnb = q1-q0; } if(nnb <= 0) return; for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; u->nofill = TRUE; nb = nnb; n = u->ncache; if(n > 0){ if(q1 != u->cq0+n) error("text.type backspace"); if(n > nb) n = nb; u->ncache -= n; textdelete(u, q1-n, q1, FALSE); nb -= n; } if(u->eq0==q1 || u->eq0==~0) u->eq0 = q0; if(nb && u==t) textdelete(u, q0, q0+nb, TRUE); if(u != t) textsetselect(u, u->q0, u->q1); else textsetselect(t, q0, q0); u->nofill = FALSE; } for(i=0; i<t->file->ntext; i++) textfill(t->file->text[i]); t->iq1 = t->q0; return; case '\n': if(t->w->autoindent){ /* find beginning of previous line using backspace code */ nnb = textbswidth(t, 0x15); /* ^U case */ rp = runemalloc(nnb + 1); nr = 0; rp[nr++] = r; for(i=0; i<nnb; i++){ r = textreadc(t, t->q0-nnb+i); if(r != ' ' && r != '\t') break; rp[nr++] = r; } } break; /* fall through to normal code */ } /* otherwise ordinary character; just insert, typically in caches of all texts */ for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u->eq0 == ~0) u->eq0 = t->q0; if(u->ncache == 0) u->cq0 = t->q0; else if(t->q0 != u->cq0+u->ncache) error("text.type cq1"); /* * Change the tag before we add to ncache, * so that if the window body is resized the * commit will not find anything in ncache. */ if(u->what==Body && u->ncache == 0){ u->needundo = TRUE; winsettag(t->w); u->needundo = FALSE; } textinsert(u, t->q0, rp, nr, FALSE); if(u != t) textsetselect(u, u->q0, u->q1); if(u->ncache+nr > u->ncachealloc){ u->ncachealloc += 10 + nr; u->cache = runerealloc(u->cache, u->ncachealloc); } runemove(u->cache+u->ncache, rp, nr); u->ncache += nr; } if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(r=='\n' && t->w!=nil) wincommit(t->w, t); t->iq1 = t->q0; }