📄 text.c
字号:
#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 <complete.h>#include "dat.h"#include "fns.h"Image *tagcols[NCOL];Image *textcols[NCOL];enum{ TABDIR = 3 /* width of tabs in directory windows */};voidtextinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL]){ t->file = f; t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollwid; t->lastsr = nullrect; r.min.x += Scrollwid+Scrollgap; t->eq0 = ~0; t->ncache = 0; t->reffont = rf; t->tabstop = maxtab; memmove(t->Frame.cols, cols, sizeof t->Frame.cols); textredraw(t, r, rf->f, screen, -1);}voidtextredraw(Text *t, Rectangle r, Font *f, Image *b, int odx){ int maxt; Rectangle rr; frinit(t, r, f, b, t->Frame.cols); rr = t->r; rr.min.x -= Scrollwid+Scrollgap; /* back fill to scroll bar */ draw(t->b, rr, t->cols[BACK], nil, ZP); /* use no wider than 3-space tabs in a directory */ maxt = maxtab; if(t->what == Body){ if(t->w->isdir) maxt = min(TABDIR, maxtab); else maxt = t->tabstop; } t->maxtab = maxt*stringwidth(f, "0"); if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){ if(t->maxlines > 0){ textreset(t); textcolumnate(t, t->w->dlp, t->w->ndl); textshow(t, 0, 0, 1); } }else{ textfill(t); textsetselect(t, t->q0, t->q1); }}inttextresize(Text *t, Rectangle r){ int odx; if(Dy(r) > 0) r.max.y -= Dy(r)%t->font->height; else r.max.y = r.min.y; odx = Dx(t->all); t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollwid; t->lastsr = nullrect; r.min.x += Scrollwid+Scrollgap; frclear(t, 0); textredraw(t, r, t->font, t->b, odx); return r.max.y;}voidtextclose(Text *t){ free(t->cache); frclear(t, 1); filedeltext(t->file, t); t->file = nil; rfclose(t->reffont); if(argtext == t) argtext = nil; if(typetext == t) typetext = nil; if(seltext == t) seltext = nil; if(mousetext == t) mousetext = nil; if(barttext == t) barttext = nil;}intdircmp(void *a, void *b){ Dirlist *da, *db; int i, n; da = *(Dirlist**)a; db = *(Dirlist**)b; n = min(da->nr, db->nr); i = memcmp(da->r, db->r, n*sizeof(Rune)); if(i) return i; return da->nr - db->nr;}voidtextcolumnate(Text *t, Dirlist **dlp, int ndl){ int i, j, w, colw, mint, maxt, ncol, nrow; Dirlist *dl; uint q1; if(t->file->ntext > 1) return; mint = stringwidth(t->font, "0"); /* go for narrower tabs if set more than 3 wide */ t->maxtab = min(maxtab, TABDIR)*mint; maxt = t->maxtab; colw = 0; for(i=0; i<ndl; i++){ dl = dlp[i]; w = dl->wid; if(maxt-w%maxt < mint || w%maxt==0) w += mint; if(w % maxt) w += maxt-(w%maxt); if(w > colw) colw = w; } if(colw == 0) ncol = 1; else ncol = max(1, Dx(t->r)/colw); nrow = (ndl+ncol-1)/ncol; q1 = 0; for(i=0; i<nrow; i++){ for(j=i; j<ndl; j+=nrow){ dl = dlp[j]; fileinsert(t->file, q1, dl->r, dl->nr); q1 += dl->nr; if(j+nrow >= ndl) break; w = dl->wid; if(maxt-w%maxt < mint){ fileinsert(t->file, q1, L"\t", 1); q1++; w += mint; } do{ fileinsert(t->file, q1, L"\t", 1); q1++; w += maxt-(w%maxt); }while(w < colw); } fileinsert(t->file, q1, L"\n", 1); q1++; }}uinttextload(Text *t, uint q0, char *file, int setqid){ Rune *rp; Dirlist *dl, **dlp; int fd, i, j, n, ndl, nulls; uint q, q1; Dir *d, *dbuf; char *tmp; Text *u; if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body) error("text.load"); if(t->w->isdir && t->file->nname==0){ warning(nil, "empty directory name\n"); return 0; } fd = open(file, OREAD); if(fd < 0){ warning(nil, "can't open %s: %r\n", file); return 0; } d = dirfstat(fd); if(d == nil){ warning(nil, "can't fstat %s: %r\n", file); goto Rescue; } nulls = FALSE; if(d->qid.type & QTDIR){ /* this is checked in get() but it's possible the file changed underfoot */ if(t->file->ntext > 1){ warning(nil, "%s is a directory; can't read with multiple windows on it\n", file); goto Rescue; } t->w->isdir = TRUE; t->w->filemenu = FALSE; if(t->file->name[t->file->nname-1] != '/'){ rp = runemalloc(t->file->nname+1); runemove(rp, t->file->name, t->file->nname); rp[t->file->nname] = '/'; winsetname(t->w, rp, t->file->nname+1); free(rp); } dlp = nil; ndl = 0; dbuf = nil; while((n=dirread(fd, &dbuf)) > 0){ for(i=0; i<n; i++){ dl = emalloc(sizeof(Dirlist)); j = strlen(dbuf[i].name); tmp = emalloc(j+1+1); memmove(tmp, dbuf[i].name, j); if(dbuf[i].qid.type & QTDIR) tmp[j++] = '/'; tmp[j] = '\0'; dl->r = bytetorune(tmp, &dl->nr); dl->wid = stringwidth(t->font, tmp); free(tmp); ndl++; dlp = realloc(dlp, ndl*sizeof(Dirlist*)); dlp[ndl-1] = dl; } free(dbuf); } qsort(dlp, ndl, sizeof(Dirlist*), dircmp); t->w->dlp = dlp; t->w->ndl = ndl; textcolumnate(t, dlp, ndl); q1 = t->file->nc; }else{ t->w->isdir = FALSE; t->w->filemenu = TRUE; q1 = q0 + fileload(t->file, q0, fd, &nulls); } if(setqid){ t->file->dev = d->dev; t->file->mtime = d->mtime; t->file->qidpath = d->qid.path; } close(fd); rp = fbufalloc(); for(q=q0; q<q1; q+=n){ n = q1-q; if(n > RBUFSIZE) n = RBUFSIZE; bufread(t->file, q, rp, n); if(q < t->org) t->org += n; else if(q <= t->org+t->nchars) frinsert(t, rp, rp+n, q-t->org); if(t->lastlinefull) break; } fbuffree(rp); for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u != t){ if(u->org > u->file->nc) /* will be 0 because of reset(), but safety first */ u->org = 0; textresize(u, u->all); textbacknl(u, u->org, 0); /* go to beginning of line */ } textsetselect(u, q0, q0); } if(nulls) warning(nil, "%s: NUL bytes elided\n", file); free(d); return q1-q0; Rescue: close(fd); return 0;}uinttextbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp){ Rune *bp, *tp, *up; int i, initial; if(t->what == Tag){ /* can't happen but safety first: mustn't backspace over file name */ Err: textinsert(t, q0, r, n, tofile); *nrp = n; return q0; } bp = r; for(i=0; i<n; i++) if(*bp++ == '\b'){ --bp; initial = 0; tp = runemalloc(n); runemove(tp, r, i); up = tp+i; for(; i<n; i++){ *up = *bp++; if(*up == '\b') if(up == tp) initial++; else --up; else up++; } if(initial){ if(initial > q0) initial = q0; q0 -= initial; textdelete(t, q0, q0+initial, tofile); } n = up-tp; textinsert(t, q0, tp, n, tofile); free(tp); *nrp = n; return q0; } goto Err;}voidtextinsert(Text *t, uint q0, Rune *r, uint n, int tofile){ int c, i; Text *u; if(tofile && t->ncache != 0) error("text.insert"); if(n == 0) return; if(tofile){ fileinsert(t->file, q0, r, n); if(t->what == Body){ t->w->dirty = TRUE; t->w->utflastqid = -1; } if(t->file->ntext > 1) for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u != t){ u->w->dirty = TRUE; /* always a body */ textinsert(u, q0, r, n, FALSE); textsetselect(u, u->q0, u->q1); textscrdraw(u); } } } if(q0 < t->q1) t->q1 += n; if(q0 < t->q0) t->q0 += n; if(q0 < t->org) t->org += n; else if(q0 <= t->org+t->nchars) frinsert(t, r, r+n, q0-t->org); if(t->w){ c = 'i'; if(t->what == Body) c = 'I'; if(n <= EVENTSIZE) winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r); else winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n); }}voidtypecommit(Text *t){ if(t->w != nil) wincommit(t->w, t); else textcommit(t, TRUE);}voidtextfill(Text *t){ Rune *rp; int i, n, m, nl; if(t->lastlinefull || t->nofill) return; if(t->ncache > 0) typecommit(t); rp = fbufalloc(); do{ n = t->file->nc-(t->org+t->nchars); if(n == 0) break; if(n > 2000) /* educated guess at reasonable amount */ n = 2000; bufread(t->file, t->org+t->nchars, rp, n); /* * it's expensive to frinsert more than we need, so * count newlines. */ nl = t->maxlines-t->nlines; m = 0; for(i=0; i<n; ){ if(rp[i++] == '\n'){ m++; if(m >= nl) break; } } frinsert(t, rp, rp+i, t->nchars); }while(t->lastlinefull == FALSE); fbuffree(rp);}voidtextdelete(Text *t, uint q0, uint q1, int tofile){ uint n, p0, p1; int i, c; Text *u; if(tofile && t->ncache != 0) error("text.delete"); n = q1-q0; if(n == 0) return; if(tofile){ filedelete(t->file, q0, q1); if(t->what == Body){ t->w->dirty = TRUE; t->w->utflastqid = -1; } if(t->file->ntext > 1) for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u != t){ u->w->dirty = TRUE; /* always a body */ textdelete(u, q0, q1, FALSE); textsetselect(u, u->q0, u->q1); textscrdraw(u); } } } if(q0 < t->q0) t->q0 -= min(n, t->q0-q0); if(q0 < t->q1) t->q1 -= min(n, t->q1-q0); if(q1 <= t->org) t->org -= n; else if(q0 < t->org+t->nchars){ p1 = q1 - t->org; if(p1 > t->nchars) p1 = t->nchars; if(q0 < t->org){ t->org = q0; p0 = 0; }else p0 = q0 - t->org; frdelete(t, p0, p1); textfill(t); } if(t->w){ c = 'd'; if(t->what == Body) c = 'D'; winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1); }}voidtextconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1){ *p0 = min(q0, t->file->nc); *p1 = min(q1, t->file->nc);}Runetextreadc(Text *t, uint q){ Rune r; if(t->cq0<=q && q<t->cq0+t->ncache) r = t->cache[q-t->cq0]; else bufread(t->file, q, &r, 1); return r;}inttextbswidth(Text *t, Rune c){ uint q, eq; Rune r; int skipping; /* there is known to be at least one character to erase */ if(c == 0x08) /* ^H: erase character */ return 1; q = t->q0; skipping = TRUE; while(q > 0){ r = textreadc(t, q-1); if(r == '\n'){ /* eat at most one more character */ if(q == t->q0) /* eat the newline */ --q; break; } if(c == 0x17){ eq = isalnum(r); if(eq && skipping) /* found one; stop skipping */ skipping = FALSE; else if(!eq && !skipping) break; } --q; } return t->q0-q;}inttextfilewidth(Text *t, uint q0, int oneelement){ uint q; Rune r; q = q0; while(q > 0){ r = textreadc(t, q-1); if(r <= ' ') break; if(oneelement && r=='/') break; --q; } return q0-q;}Rune*textcomplete(Text *t){ int i, nstr, npath; uint q; Rune tmp[200]; Rune *str, *path; Rune *rp; Completion *c; char *s, *dirs; Runestr dir; /* control-f: filename completion; works back to white space or / */ if(t->q0<t->file->nc && textreadc(t, t->q0)>' ') /* must be at end of word */ return nil; nstr = textfilewidth(t, t->q0, TRUE); str = runemalloc(nstr); npath = textfilewidth(t, t->q0-nstr, FALSE); path = runemalloc(npath); c = nil; rp = nil; dirs = nil; q = t->q0-nstr; for(i=0; i<nstr; i++) str[i] = textreadc(t, q++); q = t->q0-nstr-npath; for(i=0; i<npath; i++) path[i] = textreadc(t, q++); /* is path rooted? if not, we need to make it relative to window path */ if(npath>0 && path[0]=='/') dir = (Runestr){path, npath}; else{ dir = dirname(t, nil, 0); if(dir.nr + 1 + npath > nelem(tmp)){ free(dir.r); goto Return; } if(dir.nr == 0){ dir.nr = 1; dir.r = runestrdup(L"."); } runemove(tmp, dir.r, dir.nr); tmp[dir.nr] = '/'; runemove(tmp+dir.nr+1, path, npath); free(dir.r); dir.r = tmp; dir.nr += 1+npath; dir = cleanrname(dir); } s = smprint("%.*S", nstr, str); dirs = smprint("%.*S", dir.nr, dir.r); c = complete(dirs, s); free(s); if(c == nil){ warning(nil, "error attempting completion: %r\n"); goto Return; } if(!c->advance){ warning(nil, "%.*S%s%.*S*%s\n", dir.nr, dir.r, dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "", nstr, str, c->nmatch? "" : ": no matches in:"); for(i=0; i<c->nfile; i++) warning(nil, " %s\n", c->filename[i]); } if(c->advance) rp = runesmprint("%s", c->string); else rp = nil; Return: freecompletion(c); free(dirs); free(str); free(path); return rp;}voidtexttype(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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -