xref: /plan9-contrib/sys/src/cmd/acme/text.c (revision 2c2299ce0e1270fbef669a6f1c8a8e80dfa5d69c)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include <complete.h>
12 #include "dat.h"
13 #include "fns.h"
14 
15 Image	*tagcols[NCOL];
16 Image	*textcols[NCOL];
17 
18 enum{
19 	TABDIR = 3	/* width of tabs in directory windows */
20 };
21 
22 void
textinit(Text * t,File * f,Rectangle r,Reffont * rf,Image * cols[NCOL])23 textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
24 {
25 	t->file = f;
26 	t->all = r;
27 	t->scrollr = r;
28 	t->scrollr.max.x = r.min.x+Scrollwid;
29 	t->lastsr = nullrect;
30 	r.min.x += Scrollwid+Scrollgap;
31 	t->eq0 = ~0;
32 	t->ncache = 0;
33 	t->reffont = rf;
34 	t->tabstop = maxtab;
35 	memmove(t->Frame.cols, cols, sizeof t->Frame.cols);
36 	textredraw(t, r, rf->f, screen, -1);
37 }
38 
39 void
textredraw(Text * t,Rectangle r,Font * f,Image * b,int odx)40 textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx)
41 {
42 	int maxt;
43 	Rectangle rr;
44 
45 	frinit(t, r, f, b, t->Frame.cols);
46 	rr = t->r;
47 	rr.min.x -= Scrollwid+Scrollgap;	/* back fill to scroll bar */
48 	if(!t->noredraw)
49 		draw(t->b, rr, t->cols[BACK], nil, ZP);
50 	/* use no wider than 3-space tabs in a directory */
51 	maxt = maxtab;
52 	if(t->what == Body){
53 		if(t->w->isdir)
54 			maxt = min(TABDIR, maxtab);
55 		else
56 			maxt = t->tabstop;
57 	}
58 	t->maxtab = maxt*stringwidth(f, "0");
59 	if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){
60 		if(t->maxlines > 0){
61 			textreset(t);
62 			textcolumnate(t, t->w->dlp,  t->w->ndl);
63 			textshow(t, 0, 0, 1);
64 		}
65 	}else{
66 		textfill(t);
67 		textsetselect(t, t->q0, t->q1);
68 	}
69 }
70 
71 int
textresize(Text * t,Rectangle r,int keepextra)72 textresize(Text *t, Rectangle r, int keepextra)
73 {
74 	int odx;
75 
76 	if(Dy(r) <= 0)
77 		r.max.y = r.min.y;
78 	else if(!keepextra)
79 		r.max.y -= Dy(r)%t->font->height;
80 	odx = Dx(t->all);
81 	t->all = r;
82 	t->scrollr = r;
83 	t->scrollr.max.x = r.min.x+Scrollwid;
84 	t->lastsr = nullrect;
85 	r.min.x += Scrollwid+Scrollgap;
86 	frclear(t, 0);
87 	textredraw(t, r, t->font, t->b, odx);
88 	if(keepextra && t->r.max.y < t->all.max.y && !t->noredraw){
89 		/* draw background in bottom fringe of window */
90 		r.min.x -= Scrollgap;
91 		r.min.y = t->r.max.y;
92 		r.max.y = t->all.max.y;
93 		draw(screen, r, t->cols[BACK], nil, ZP);
94 	}
95 	return t->all.max.y;
96 }
97 
98 void
textclose(Text * t)99 textclose(Text *t)
100 {
101 	free(t->cache);
102 	frclear(t, 1);
103 	filedeltext(t->file, t);
104 	t->file = nil;
105 	rfclose(t->reffont);
106 	if(argtext == t)
107 		argtext = nil;
108 	if(typetext == t)
109 		typetext = nil;
110 	if(seltext == t)
111 		seltext = nil;
112 	if(mousetext == t)
113 		mousetext = nil;
114 	if(barttext == t)
115 		barttext = nil;
116 }
117 
118 int
dircmp(void * a,void * b)119 dircmp(void *a, void *b)
120 {
121 	Dirlist *da, *db;
122 	int i, n;
123 
124 	da = *(Dirlist**)a;
125 	db = *(Dirlist**)b;
126 	n = min(da->nr, db->nr);
127 	i = memcmp(da->r, db->r, n*sizeof(Rune));
128 	if(i)
129 		return i;
130 	return da->nr - db->nr;
131 }
132 
133 void
textcolumnate(Text * t,Dirlist ** dlp,int ndl)134 textcolumnate(Text *t, Dirlist **dlp, int ndl)
135 {
136 	int i, j, w, colw, mint, maxt, ncol, nrow;
137 	Dirlist *dl;
138 	uint q1;
139 
140 	if(t->file->ntext > 1)
141 		return;
142 	mint = stringwidth(t->font, "0");
143 	/* go for narrower tabs if set more than 3 wide */
144 	t->maxtab = min(maxtab, TABDIR)*mint;
145 	maxt = t->maxtab;
146 	colw = 0;
147 	for(i=0; i<ndl; i++){
148 		dl = dlp[i];
149 		w = dl->wid;
150 		if(maxt-w%maxt < mint || w%maxt==0)
151 			w += mint;
152 		if(w % maxt)
153 			w += maxt-(w%maxt);
154 		if(w > colw)
155 			colw = w;
156 	}
157 	if(colw == 0)
158 		ncol = 1;
159 	else
160 		ncol = max(1, Dx(t->r)/colw);
161 	nrow = (ndl+ncol-1)/ncol;
162 
163 	q1 = 0;
164 	for(i=0; i<nrow; i++){
165 		for(j=i; j<ndl; j+=nrow){
166 			dl = dlp[j];
167 			fileinsert(t->file, q1, dl->r, dl->nr);
168 			q1 += dl->nr;
169 			if(j+nrow >= ndl)
170 				break;
171 			w = dl->wid;
172 			if(maxt-w%maxt < mint){
173 				fileinsert(t->file, q1, L"\t", 1);
174 				q1++;
175 				w += mint;
176 			}
177 			do{
178 				fileinsert(t->file, q1, L"\t", 1);
179 				q1++;
180 				w += maxt-(w%maxt);
181 			}while(w < colw);
182 		}
183 		fileinsert(t->file, q1, L"\n", 1);
184 		q1++;
185 	}
186 }
187 
188 uint
textload(Text * t,uint q0,char * file,int setqid)189 textload(Text *t, uint q0, char *file, int setqid)
190 {
191 	Rune *rp;
192 	Dirlist *dl, **dlp;
193 	int fd, i, j, n, ndl, nulls;
194 	uint q, q1;
195 	Dir *d, *dbuf;
196 	char *tmp;
197 	Text *u;
198 
199 	if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body)
200 		error("text.load");
201 	if(t->w->isdir && t->file->nname==0){
202 		warning(nil, "empty directory name\n");
203 		return 0;
204 	}
205 	fd = open(file, OREAD);
206 	if(fd < 0){
207 		warning(nil, "can't open %s: %r\n", file);
208 		return 0;
209 	}
210 	d = dirfstat(fd);
211 	if(d == nil){
212 		warning(nil, "can't fstat %s: %r\n", file);
213 		goto Rescue;
214 	}
215 	nulls = FALSE;
216 	if(d->qid.type & QTDIR){
217 		/* this is checked in get() but it's possible the file changed underfoot */
218 		if(t->file->ntext > 1){
219 			warning(nil, "%s is a directory; can't read with multiple windows on it\n", file);
220 			goto Rescue;
221 		}
222 		t->w->isdir = TRUE;
223 		t->w->filemenu = FALSE;
224 		if(t->file->name[t->file->nname-1] != '/'){
225 			rp = runemalloc(t->file->nname+1);
226 			runemove(rp, t->file->name, t->file->nname);
227 			rp[t->file->nname] = '/';
228 			winsetname(t->w, rp, t->file->nname+1);
229 			free(rp);
230 		}
231 		dlp = nil;
232 		ndl = 0;
233 		dbuf = nil;
234 		while((n=dirread(fd, &dbuf)) > 0){
235 			for(i=0; i<n; i++){
236 				dl = emalloc(sizeof(Dirlist));
237 				j = strlen(dbuf[i].name);
238 				tmp = emalloc(j+1+1);
239 				memmove(tmp, dbuf[i].name, j);
240 				if(dbuf[i].qid.type & QTDIR)
241 					tmp[j++] = '/';
242 				tmp[j] = '\0';
243 				dl->r = bytetorune(tmp, &dl->nr);
244 				dl->wid = stringwidth(t->font, tmp);
245 				free(tmp);
246 				ndl++;
247 				dlp = realloc(dlp, ndl*sizeof(Dirlist*));
248 				dlp[ndl-1] = dl;
249 			}
250 			free(dbuf);
251 		}
252 		qsort(dlp, ndl, sizeof(Dirlist*), dircmp);
253 		t->w->dlp = dlp;
254 		t->w->ndl = ndl;
255 		textcolumnate(t, dlp, ndl);
256 		q1 = t->file->nc;
257 	}else{
258 		t->w->isdir = FALSE;
259 		t->w->filemenu = TRUE;
260 		q1 = q0 + fileload(t->file, q0, fd, &nulls);
261 	}
262 	if(setqid){
263 		t->file->dev = d->dev;
264 		t->file->mtime = d->mtime;
265 		t->file->qidpath = d->qid.path;
266 	}
267 	close(fd);
268 	rp = fbufalloc();
269 	for(q=q0; q<q1; q+=n){
270 		n = q1-q;
271 		if(n > RBUFSIZE)
272 			n = RBUFSIZE;
273 		bufread(t->file, q, rp, n);
274 		if(q < t->org)
275 			t->org += n;
276 		else if(q <= t->org+t->nchars)
277 			frinsert(t, rp, rp+n, q-t->org);
278 		if(t->lastlinefull)
279 			break;
280 	}
281 	fbuffree(rp);
282 	for(i=0; i<t->file->ntext; i++){
283 		u = t->file->text[i];
284 		if(u != t){
285 			if(u->org > u->file->nc)	/* will be 0 because of reset(), but safety first */
286 				u->org = 0;
287 			textresize(u, u->all, TRUE);
288 			textbacknl(u, u->org, 0);	/* go to beginning of line */
289 		}
290 		textsetselect(u, q0, q0);
291 	}
292 	if(nulls)
293 		warning(nil, "%s: NUL bytes elided\n", file);
294 	free(d);
295 	return q1-q0;
296 
297     Rescue:
298 	close(fd);
299 	return 0;
300 }
301 
302 uint
textbsinsert(Text * t,uint q0,Rune * r,uint n,int tofile,int * nrp)303 textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp)
304 {
305 	Rune *bp, *tp, *up;
306 	int i, initial;
307 
308 	if(t->what == Tag){	/* can't happen but safety first: mustn't backspace over file name */
309     Err:
310 		textinsert(t, q0, r, n, tofile);
311 		*nrp = n;
312 		return q0;
313 	}
314 	bp = r;
315 	for(i=0; i<n; i++)
316 		if(*bp++ == '\b'){
317 			--bp;
318 			initial = 0;
319 			tp = runemalloc(n);
320 			runemove(tp, r, i);
321 			up = tp+i;
322 			for(; i<n; i++){
323 				*up = *bp++;
324 				if(*up == '\b')
325 					if(up == tp)
326 						initial++;
327 					else
328 						--up;
329 				else
330 					up++;
331 			}
332 			if(initial){
333 				if(initial > q0)
334 					initial = q0;
335 				q0 -= initial;
336 				textdelete(t, q0, q0+initial, tofile);
337 			}
338 			n = up-tp;
339 			textinsert(t, q0, tp, n, tofile);
340 			free(tp);
341 			*nrp = n;
342 			return q0;
343 		}
344 	goto Err;
345 }
346 
347 void
textinsert(Text * t,uint q0,Rune * r,uint n,int tofile)348 textinsert(Text *t, uint q0, Rune *r, uint n, int tofile)
349 {
350 	int c, i;
351 	Text *u;
352 
353 	if(tofile && t->ncache != 0)
354 		error("text.insert");
355 	if(n == 0)
356 		return;
357 	if(tofile){
358 		fileinsert(t->file, q0, r, n);
359 		if(t->what == Body){
360 			t->w->dirty = TRUE;
361 			t->w->utflastqid = -1;
362 		}
363 		if(t->file->ntext > 1)
364 			for(i=0; i<t->file->ntext; i++){
365 				u = t->file->text[i];
366 				if(u != t){
367 					u->w->dirty = TRUE;	/* always a body */
368 					textinsert(u, q0, r, n, FALSE);
369 					textsetselect(u, u->q0, u->q1);
370 					textscrdraw(u);
371 				}
372 			}
373 
374 	}
375 	if(q0 < t->q1)
376 		t->q1 += n;
377 	if(q0 < t->q0)
378 		t->q0 += n;
379 	if(q0 < t->org)
380 		t->org += n;
381 	else if(q0 <= t->org+t->nchars)
382 		frinsert(t, r, r+n, q0-t->org);
383 	if(t->w){
384 		c = 'i';
385 		if(t->what == Body)
386 			c = 'I';
387 		if(n <= EVENTSIZE)
388 			winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r);
389 		else
390 			winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n);
391 	}
392 }
393 
394 void
typecommit(Text * t)395 typecommit(Text *t)
396 {
397 	if(t->w != nil)
398 		wincommit(t->w, t);
399 	else
400 		textcommit(t, TRUE);
401 }
402 
403 void
textfill(Text * t)404 textfill(Text *t)
405 {
406 	Rune *rp;
407 	int i, n, m, nl;
408 
409 	if(t->lastlinefull || t->nofill)
410 		return;
411 	if(t->ncache > 0)
412 		typecommit(t);
413 	rp = fbufalloc();
414 	do{
415 		n = t->file->nc-(t->org+t->nchars);
416 		if(n == 0)
417 			break;
418 		if(n > 2000)	/* educated guess at reasonable amount */
419 			n = 2000;
420 		bufread(t->file, t->org+t->nchars, rp, n);
421 		/*
422 		 * it's expensive to frinsert more than we need, so
423 		 * count newlines.
424 		 */
425 		nl = t->maxlines-t->nlines;
426 		m = 0;
427 		for(i=0; i<n; ){
428 			if(rp[i++] == '\n'){
429 				m++;
430 				if(m >= nl)
431 					break;
432 			}
433 		}
434 		frinsert(t, rp, rp+i, t->nchars);
435 	}while(t->lastlinefull == FALSE);
436 	fbuffree(rp);
437 }
438 
439 void
textdelete(Text * t,uint q0,uint q1,int tofile)440 textdelete(Text *t, uint q0, uint q1, int tofile)
441 {
442 	uint n, p0, p1;
443 	int i, c;
444 	Text *u;
445 
446 	if(tofile && t->ncache != 0)
447 		error("text.delete");
448 	n = q1-q0;
449 	if(n == 0)
450 		return;
451 	if(tofile){
452 		filedelete(t->file, q0, q1);
453 		if(t->what == Body){
454 			t->w->dirty = TRUE;
455 			t->w->utflastqid = -1;
456 		}
457 		if(t->file->ntext > 1)
458 			for(i=0; i<t->file->ntext; i++){
459 				u = t->file->text[i];
460 				if(u != t){
461 					u->w->dirty = TRUE;	/* always a body */
462 					textdelete(u, q0, q1, FALSE);
463 					textsetselect(u, u->q0, u->q1);
464 					textscrdraw(u);
465 				}
466 			}
467 	}
468 	if(q0 < t->q0)
469 		t->q0 -= min(n, t->q0-q0);
470 	if(q0 < t->q1)
471 		t->q1 -= min(n, t->q1-q0);
472 	if(q1 <= t->org)
473 		t->org -= n;
474 	else if(q0 < t->org+t->nchars){
475 		p1 = q1 - t->org;
476 		if(p1 > t->nchars)
477 			p1 = t->nchars;
478 		if(q0 < t->org){
479 			t->org = q0;
480 			p0 = 0;
481 		}else
482 			p0 = q0 - t->org;
483 		frdelete(t, p0, p1);
484 		textfill(t);
485 	}
486 	if(t->w){
487 		c = 'd';
488 		if(t->what == Body)
489 			c = 'D';
490 		winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);
491 	}
492 }
493 
494 void
textconstrain(Text * t,uint q0,uint q1,uint * p0,uint * p1)495 textconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1)
496 {
497 	*p0 = min(q0, t->file->nc);
498 	*p1 = min(q1, t->file->nc);
499 }
500 
501 Rune
textreadc(Text * t,uint q)502 textreadc(Text *t, uint q)
503 {
504 	Rune r;
505 
506 	if(t->cq0<=q && q<t->cq0+t->ncache)
507 		r = t->cache[q-t->cq0];
508 	else
509 		bufread(t->file, q, &r, 1);
510 	return r;
511 }
512 
513 int
textbswidth(Text * t,Rune c)514 textbswidth(Text *t, Rune c)
515 {
516 	uint q, eq;
517 	Rune r;
518 	int skipping;
519 
520 	/* there is known to be at least one character to erase */
521 	if(c == 0x08)	/* ^H: erase character */
522 		return 1;
523 	q = t->q0;
524 	skipping = TRUE;
525 	while(q > 0){
526 		r = textreadc(t, q-1);
527 		if(r == '\n'){		/* eat at most one more character */
528 			if(q == t->q0)	/* eat the newline */
529 				--q;
530 			break;
531 		}
532 		if(c == 0x17){
533 			eq = isalnum(r);
534 			if(eq && skipping)	/* found one; stop skipping */
535 				skipping = FALSE;
536 			else if(!eq && !skipping)
537 				break;
538 		}
539 		--q;
540 	}
541 	return t->q0-q;
542 }
543 
544 int
textfilewidth(Text * t,uint q0,int oneelement)545 textfilewidth(Text *t, uint q0, int oneelement)
546 {
547 	uint q;
548 	Rune r;
549 
550 	q = q0;
551 	while(q > 0){
552 		r = textreadc(t, q-1);
553 		if(r <= ' ')
554 			break;
555 		if(oneelement && r=='/')
556 			break;
557 		--q;
558 	}
559 	return q0-q;
560 }
561 
562 Rune*
textcomplete(Text * t)563 textcomplete(Text *t)
564 {
565 	int i, nstr, npath;
566 	uint q;
567 	Rune tmp[200];
568 	Rune *str, *path;
569 	Rune *rp;
570 	Completion *c;
571 	char *s, *dirs;
572 	Runestr dir;
573 
574 	/* control-f: filename completion; works back to white space or / */
575 	if(t->q0<t->file->nc && textreadc(t, t->q0)>' ')	/* must be at end of word */
576 		return nil;
577 	nstr = textfilewidth(t, t->q0, TRUE);
578 	str = runemalloc(nstr);
579 	npath = textfilewidth(t, t->q0-nstr, FALSE);
580 	path = runemalloc(npath);
581 
582 	c = nil;
583 	rp = nil;
584 	dirs = nil;
585 
586 	q = t->q0-nstr;
587 	for(i=0; i<nstr; i++)
588 		str[i] = textreadc(t, q++);
589 	q = t->q0-nstr-npath;
590 	for(i=0; i<npath; i++)
591 		path[i] = textreadc(t, q++);
592 	/* is path rooted? if not, we need to make it relative to window path */
593 	if(npath>0 && path[0]=='/')
594 		dir = (Runestr){path, npath};
595 	else{
596 		dir = dirname(t, nil, 0);
597 		if(dir.nr + 1 + npath > nelem(tmp)){
598 			free(dir.r);
599 			goto Return;
600 		}
601 		if(dir.nr == 0){
602 			dir.nr = 1;
603 			dir.r = runestrdup(L".");
604 		}
605 		runemove(tmp, dir.r, dir.nr);
606 		tmp[dir.nr] = '/';
607 		runemove(tmp+dir.nr+1, path, npath);
608 		free(dir.r);
609 		dir.r = tmp;
610 		dir.nr += 1+npath;
611 		dir = cleanrname(dir);
612 	}
613 
614 	s = smprint("%.*S", nstr, str);
615 	dirs = smprint("%.*S", dir.nr, dir.r);
616 	c = complete(dirs, s);
617 	free(s);
618 	if(c == nil){
619 		warning(nil, "error attempting completion: %r\n");
620 		goto Return;
621 	}
622 
623 	if(!c->advance){
624 		warning(nil, "%.*S%s%.*S*%s\n",
625 			dir.nr, dir.r,
626 			dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "",
627 			nstr, str,
628 			c->nmatch? "" : ": no matches in:");
629 		for(i=0; i<c->nfile; i++)
630 			warning(nil, " %s\n", c->filename[i]);
631 	}
632 
633 	if(c->advance)
634 		rp = runesmprint("%s", c->string);
635 	else
636 		rp = nil;
637   Return:
638 	freecompletion(c);
639 	free(dirs);
640 	free(str);
641 	free(path);
642 	return rp;
643 }
644 
645 void
texttype(Text * t,Rune r)646 texttype(Text *t, Rune r)
647 {
648 	uint q0, q1;
649 	int nnb, nb, n, i;
650 	int nr;
651 	Rune *rp;
652 	Text *u;
653 
654 	if(t->what!=Body && t->what!=Tag && r=='\n')
655 		return;
656 	if(t->what == Tag)
657 		t->w->tagsafe = FALSE;
658 
659 	nr = 1;
660 	rp = &r;
661 	switch(r){
662 	case Kleft:
663 		if(t->q0 > 0){
664 			typecommit(t);
665 			textshow(t, t->q0-1, t->q0-1, TRUE);
666 		}
667 		return;
668 	case Kright:
669 		if(t->q1 < t->file->nc){
670 			typecommit(t);
671 			textshow(t, t->q1+1, t->q1+1, TRUE);
672 		}
673 		return;
674 	case Kdown:
675 		if(t->what == Tag)
676 			goto Tagdown;
677 		n = t->maxlines/3;
678 		goto case_Down;
679 	case Kscrollonedown:
680 		if(t->what == Tag)
681 			goto Tagdown;
682 		n = mousescrollsize(t->maxlines);
683 		if(n <= 0)
684 			n = 1;
685 		goto case_Down;
686 	case Kpgdown:
687 		n = 2*t->maxlines/3;
688 	case_Down:
689 		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));
690 		textsetorigin(t, q0, TRUE);
691 		return;
692 	case Kup:
693 		if(t->what == Tag)
694 			goto Tagup;
695 		n = t->maxlines/3;
696 		goto case_Up;
697 	case Kscrolloneup:
698 		if(t->what == Tag)
699 			goto Tagup;
700 		n = mousescrollsize(t->maxlines);
701 		goto case_Up;
702 	case Kpgup:
703 		n = 2*t->maxlines/3;
704 	case_Up:
705 		q0 = textbacknl(t, t->org, n);
706 		textsetorigin(t, q0, TRUE);
707 		return;
708 	case Khome:
709 		typecommit(t);
710 		textshow(t, 0, 0, FALSE);
711 		return;
712 	case Kend:
713 		typecommit(t);
714 		textshow(t, t->file->nc, t->file->nc, FALSE);
715 		return;
716 	case 0x01:	/* ^A: beginning of line */
717 		typecommit(t);
718 		/* go to where ^U would erase, if not already at BOL */
719 		nnb = 0;
720 		if(t->q0>0 && textreadc(t, t->q0-1)!='\n')
721 			nnb = textbswidth(t, 0x15);
722 		textshow(t, t->q0-nnb, t->q0-nnb, TRUE);
723 		return;
724 	case 0x05:	/* ^E: end of line */
725 		typecommit(t);
726 		q0 = t->q0;
727 		while(q0<t->file->nc && textreadc(t, q0)!='\n')
728 			q0++;
729 		textshow(t, q0, q0, TRUE);
730 		return;
731 
732 	Tagdown:
733 		/* expand tag to show all text */
734 		if(!t->w->tagexpand){
735 			t->w->tagexpand = TRUE;
736 			winresize(t->w, t->w->r, FALSE, TRUE);
737 		}
738 		return;
739 
740 	Tagup:
741 		/* shrink tag to single line */
742 		if(t->w->tagexpand){
743 			t->w->tagexpand = FALSE;
744 			t->w->taglines = 1;
745 			winresize(t->w, t->w->r, FALSE, TRUE);
746 		}
747 		return;
748 	}
749 	if(t->what == Body){
750 		seq++;
751 		filemark(t->file);
752 	}
753 	if(t->q1 > t->q0){
754 		if(t->ncache != 0)
755 			error("text.type");
756 		cut(t, t, nil, TRUE, TRUE, nil, 0);
757 		t->eq0 = ~0;
758 	}
759 	textshow(t, t->q0, t->q0, 1);
760 	switch(r){
761 	case 0x06:
762 	case Kins:
763 		rp = textcomplete(t);
764 		if(rp == nil)
765 			return;
766 		nr = runestrlen(rp);
767 		break;	/* fall through to normal insertion case */
768 	case 0x1B:
769 		if(t->eq0 != ~0)
770 			textsetselect(t, t->eq0, t->q0);
771 		if(t->ncache > 0)
772 			typecommit(t);
773 		return;
774 	case 0x08:	/* ^H: erase character */
775 	case 0x15:	/* ^U: erase line */
776 	case 0x17:	/* ^W: erase word */
777 		if(t->q0 == 0)	/* nothing to erase */
778 			return;
779 		nnb = textbswidth(t, r);
780 		q1 = t->q0;
781 		q0 = q1-nnb;
782 		/* if selection is at beginning of window, avoid deleting invisible text */
783 		if(q0 < t->org){
784 			q0 = t->org;
785 			nnb = q1-q0;
786 		}
787 		if(nnb <= 0)
788 			return;
789 		for(i=0; i<t->file->ntext; i++){
790 			u = t->file->text[i];
791 			u->nofill = TRUE;
792 			nb = nnb;
793 			n = u->ncache;
794 			if(n > 0){
795 				if(q1 != u->cq0+n)
796 					error("text.type backspace");
797 				if(n > nb)
798 					n = nb;
799 				u->ncache -= n;
800 				textdelete(u, q1-n, q1, FALSE);
801 				nb -= n;
802 			}
803 			if(u->eq0==q1 || u->eq0==~0)
804 				u->eq0 = q0;
805 			if(nb && u==t)
806 				textdelete(u, q0, q0+nb, TRUE);
807 			if(u != t)
808 				textsetselect(u, u->q0, u->q1);
809 			else
810 				textsetselect(t, q0, q0);
811 			u->nofill = FALSE;
812 		}
813 		for(i=0; i<t->file->ntext; i++)
814 			textfill(t->file->text[i]);
815 		return;
816 	case '\n':
817 		if(t->w->autoindent){
818 			/* find beginning of previous line using backspace code */
819 			nnb = textbswidth(t, 0x15); /* ^U case */
820 			rp = runemalloc(nnb + 1);
821 			nr = 0;
822 			rp[nr++] = r;
823 			for(i=0; i<nnb; i++){
824 				r = textreadc(t, t->q0-nnb+i);
825 				if(r != ' ' && r != '\t')
826 					break;
827 				rp[nr++] = r;
828 			}
829 		}
830 		break; /* fall through to normal code */
831 	}
832 	/* otherwise ordinary character; just insert, typically in caches of all texts */
833 	for(i=0; i<t->file->ntext; i++){
834 		u = t->file->text[i];
835 		if(u->eq0 == ~0)
836 			u->eq0 = t->q0;
837 		if(u->ncache == 0)
838 			u->cq0 = t->q0;
839 		else if(t->q0 != u->cq0+u->ncache)
840 			error("text.type cq1");
841 		textinsert(u, t->q0, rp, nr, FALSE);
842 		if(u != t)
843 			textsetselect(u, u->q0, u->q1);
844 		if(u->ncache+nr > u->ncachealloc){
845 			u->ncachealloc += 10 + nr;
846 			u->cache = runerealloc(u->cache, u->ncachealloc);
847 		}
848 		runemove(u->cache+u->ncache, rp, nr);
849 		u->ncache += nr;
850 	}
851 	if(rp != &r)
852 		free(rp);
853 	textsetselect(t, t->q0+nr, t->q0+nr);
854 	if(r=='\n' && t->w!=nil)
855 		wincommit(t->w, t);
856 }
857 
858 void
textcommit(Text * t,int tofile)859 textcommit(Text *t, int tofile)
860 {
861 	if(t->ncache == 0)
862 		return;
863 	if(tofile)
864 		fileinsert(t->file, t->cq0, t->cache, t->ncache);
865 	if(t->what == Body){
866 		t->w->dirty = TRUE;
867 		t->w->utflastqid = -1;
868 	}
869 	t->ncache = 0;
870 }
871 
872 static	Text	*clicktext;
873 static	uint	clickmsec;
874 static	Text	*selecttext;
875 static	uint	selectq;
876 
877 /*
878  * called from frame library
879  */
880 void
framescroll(Frame * f,int dl)881 framescroll(Frame *f, int dl)
882 {
883 	if(f != &selecttext->Frame)
884 		error("frameselect not right frame");
885 	textframescroll(selecttext, dl);
886 }
887 
888 void
textframescroll(Text * t,int dl)889 textframescroll(Text *t, int dl)
890 {
891 	uint q0;
892 
893 	if(dl == 0){
894 		scrsleep(100);
895 		return;
896 	}
897 	if(dl < 0){
898 		q0 = textbacknl(t, t->org, -dl);
899 		if(selectq > t->org+t->p0)
900 			textsetselect(t, t->org+t->p0, selectq);
901 		else
902 			textsetselect(t, selectq, t->org+t->p0);
903 	}else{
904 		if(t->org+t->nchars == t->file->nc)
905 			return;
906 		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height));
907 		if(selectq > t->org+t->p1)
908 			textsetselect(t, t->org+t->p1, selectq);
909 		else
910 			textsetselect(t, selectq, t->org+t->p1);
911 	}
912 	textsetorigin(t, q0, TRUE);
913 }
914 
915 
916 void
textselect(Text * t)917 textselect(Text *t)
918 {
919 	uint q0, q1;
920 	int b, x, y;
921 	int state;
922 
923 	selecttext = t;
924 	/*
925 	 * To have double-clicking and chording, we double-click
926 	 * immediately if it might make sense.
927 	 */
928 	b = mouse->buttons;
929 	q0 = t->q0;
930 	q1 = t->q1;
931 	selectq = t->org+frcharofpt(t, mouse->xy);
932 	if(clicktext==t && mouse->msec-clickmsec<500)
933 	if(q0==q1 && selectq==q0){
934 		textdoubleclick(t, &q0, &q1);
935 		textsetselect(t, q0, q1);
936 		flushimage(display, 1);
937 		x = mouse->xy.x;
938 		y = mouse->xy.y;
939 		/* stay here until something interesting happens */
940 		do
941 			readmouse(mousectl);
942 		while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
943 		mouse->xy.x = x;	/* in case we're calling frselect */
944 		mouse->xy.y = y;
945 		q0 = t->q0;	/* may have changed */
946 		q1 = t->q1;
947 		selectq = q0;
948 	}
949 	if(mouse->buttons == b){
950 		t->Frame.scroll = framescroll;
951 		frselect(t, mousectl);
952 		/* horrible botch: while asleep, may have lost selection altogether */
953 		if(selectq > t->file->nc)
954 			selectq = t->org + t->p0;
955 		t->Frame.scroll = nil;
956 		if(selectq < t->org)
957 			q0 = selectq;
958 		else
959 			q0 = t->org + t->p0;
960 		if(selectq > t->org+t->nchars)
961 			q1 = selectq;
962 		else
963 			q1 = t->org+t->p1;
964 	}
965 	if(q0 == q1){
966 		if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){
967 			textdoubleclick(t, &q0, &q1);
968 			clicktext = nil;
969 		}else{
970 			clicktext = t;
971 			clickmsec = mouse->msec;
972 		}
973 	}else
974 		clicktext = nil;
975 	textsetselect(t, q0, q1);
976 	flushimage(display, 1);
977 	state = 0;	/* undo when possible; +1 for cut, -1 for paste */
978 	while(mouse->buttons){
979 		mouse->msec = 0;
980 		b = mouse->buttons;
981 		if((b&1) && (b&6)){
982 			if(state==0 && t->what==Body){
983 				seq++;
984 				filemark(t->w->body.file);
985 			}
986 			if(b & 2){
987 				if(state==-1 && t->what==Body){
988 					winundo(t->w, TRUE);
989 					textsetselect(t, q0, t->q0);
990 					state = 0;
991 				}else if(state != 1){
992 					cut(t, t, nil, TRUE, TRUE, nil, 0);
993 					state = 1;
994 				}
995 			}else{
996 				if(state==1 && t->what==Body){
997 					winundo(t->w, TRUE);
998 					textsetselect(t, q0, t->q1);
999 					state = 0;
1000 				}else if(state != -1){
1001 					paste(t, t, nil, TRUE, FALSE, nil, 0);
1002 					state = -1;
1003 				}
1004 			}
1005 			textscrdraw(t);
1006 			clearmouse();
1007 		}
1008 		flushimage(display, 1);
1009 		while(mouse->buttons == b)
1010 			readmouse(mousectl);
1011 		clicktext = nil;
1012 	}
1013 }
1014 
1015 void
textshow(Text * t,uint q0,uint q1,int doselect)1016 textshow(Text *t, uint q0, uint q1, int doselect)
1017 {
1018 	int qe;
1019 	int nl;
1020 	uint q;
1021 
1022 	if(t->what != Body){
1023 		if(doselect)
1024 			textsetselect(t, q0, q1);
1025 		return;
1026 	}
1027 	if(t->w!=nil && t->maxlines==0)
1028 		colgrow(t->col, t->w, 1);
1029 	if(doselect)
1030 		textsetselect(t, q0, q1);
1031 	qe = t->org+t->nchars;
1032 	if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->file->nc+t->ncache)))
1033 		textscrdraw(t);
1034 	else{
1035 		if(t->w->nopen[QWevent] > 0)
1036 			nl = 3*t->maxlines/4;
1037 		else
1038 			nl = t->maxlines/4;
1039 		q = textbacknl(t, q0, nl);
1040 		/* avoid going backwards if trying to go forwards - long lines! */
1041 		if(!(q0>t->org && q<t->org))
1042 			textsetorigin(t, q, TRUE);
1043 		while(q0 > t->org+t->nchars)
1044 			textsetorigin(t, t->org+1, FALSE);
1045 	}
1046 }
1047 
1048 static
1049 int
region(int a,int b)1050 region(int a, int b)
1051 {
1052 	if(a < b)
1053 		return -1;
1054 	if(a == b)
1055 		return 0;
1056 	return 1;
1057 }
1058 
1059 void
selrestore(Frame * f,Point pt0,uint p0,uint p1)1060 selrestore(Frame *f, Point pt0, uint p0, uint p1)
1061 {
1062 	if(p1<=f->p0 || p0>=f->p1){
1063 		/* no overlap */
1064 		frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);
1065 		return;
1066 	}
1067 	if(p0>=f->p0 && p1<=f->p1){
1068 		/* entirely inside */
1069 		frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
1070 		return;
1071 	}
1072 
1073 	/* they now are known to overlap */
1074 
1075 	/* before selection */
1076 	if(p0 < f->p0){
1077 		frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);
1078 		p0 = f->p0;
1079 		pt0 = frptofchar(f, p0);
1080 	}
1081 	/* after selection */
1082 	if(p1 > f->p1){
1083 		frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]);
1084 		p1 = f->p1;
1085 	}
1086 	/* inside selection */
1087 	frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
1088 }
1089 
1090 void
textsetselect(Text * t,uint q0,uint q1)1091 textsetselect(Text *t, uint q0, uint q1)
1092 {
1093 	int p0, p1;
1094 
1095 	/* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */
1096 	t->q0 = q0;
1097 	t->q1 = q1;
1098 	/* compute desired p0,p1 from q0,q1 */
1099 	p0 = q0-t->org;
1100 	p1 = q1-t->org;
1101 	if(p0 < 0)
1102 		p0 = 0;
1103 	if(p1 < 0)
1104 		p1 = 0;
1105 	if(p0 > t->nchars)
1106 		p0 = t->nchars;
1107 	if(p1 > t->nchars)
1108 		p1 = t->nchars;
1109 	if(p0==t->p0 && p1==t->p1)
1110 		return;
1111 	/* screen disagrees with desired selection */
1112 	if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){
1113 		/* no overlap or too easy to bother trying */
1114 		frdrawsel(t, frptofchar(t, t->p0), t->p0, t->p1, 0);
1115 		frdrawsel(t, frptofchar(t, p0), p0, p1, 1);
1116 		goto Return;
1117 	}
1118 	/* overlap; avoid unnecessary painting */
1119 	if(p0 < t->p0){
1120 		/* extend selection backwards */
1121 		frdrawsel(t, frptofchar(t, p0), p0, t->p0, 1);
1122 	}else if(p0 > t->p0){
1123 		/* trim first part of selection */
1124 		frdrawsel(t, frptofchar(t, t->p0), t->p0, p0, 0);
1125 	}
1126 	if(p1 > t->p1){
1127 		/* extend selection forwards */
1128 		frdrawsel(t, frptofchar(t, t->p1), t->p1, p1, 1);
1129 	}else if(p1 < t->p1){
1130 		/* trim last part of selection */
1131 		frdrawsel(t, frptofchar(t, p1), p1, t->p1, 0);
1132 	}
1133 
1134     Return:
1135 	t->p0 = p0;
1136 	t->p1 = p1;
1137 }
1138 
1139 /*
1140  * Release the button in less than DELAY ms and it's considered a null selection
1141  * if the mouse hardly moved, regardless of whether it crossed a char boundary.
1142  */
1143 enum {
1144 	DELAY = 2,
1145 	MINMOVE = 4,
1146 };
1147 
1148 uint
xselect(Frame * f,Mousectl * mc,Image * col,uint * p1p)1149 xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p)	/* when called, button is down */
1150 {
1151 	uint p0, p1, q, tmp;
1152 	ulong msec;
1153 	Point mp, pt0, pt1, qt;
1154 	int reg, b;
1155 
1156 	mp = mc->xy;
1157 	b = mc->buttons;
1158 	msec = mc->msec;
1159 
1160 	/* remove tick */
1161 	if(f->p0 == f->p1)
1162 		frtick(f, frptofchar(f, f->p0), 0);
1163 	p0 = p1 = frcharofpt(f, mp);
1164 	pt0 = frptofchar(f, p0);
1165 	pt1 = frptofchar(f, p1);
1166 	reg = 0;
1167 	frtick(f, pt0, 1);
1168 	do{
1169 		q = frcharofpt(f, mc->xy);
1170 		if(p1 != q){
1171 			if(p0 == p1)
1172 				frtick(f, pt0, 0);
1173 			if(reg != region(q, p0)){	/* crossed starting point; reset */
1174 				if(reg > 0)
1175 					selrestore(f, pt0, p0, p1);
1176 				else if(reg < 0)
1177 					selrestore(f, pt1, p1, p0);
1178 				p1 = p0;
1179 				pt1 = pt0;
1180 				reg = region(q, p0);
1181 				if(reg == 0)
1182 					frdrawsel0(f, pt0, p0, p1, col, display->white);
1183 			}
1184 			qt = frptofchar(f, q);
1185 			if(reg > 0){
1186 				if(q > p1)
1187 					frdrawsel0(f, pt1, p1, q, col, display->white);
1188 
1189 				else if(q < p1)
1190 					selrestore(f, qt, q, p1);
1191 			}else if(reg < 0){
1192 				if(q > p1)
1193 					selrestore(f, pt1, p1, q);
1194 				else
1195 					frdrawsel0(f, qt, q, p1, col, display->white);
1196 			}
1197 			p1 = q;
1198 			pt1 = qt;
1199 		}
1200 		if(p0 == p1)
1201 			frtick(f, pt0, 1);
1202 		flushimage(f->display, 1);
1203 		readmouse(mc);
1204 	}while(mc->buttons == b);
1205 	if(mc->msec-msec < DELAY && p0!=p1
1206 	&& abs(mp.x-mc->xy.x)<MINMOVE
1207 	&& abs(mp.y-mc->xy.y)<MINMOVE) {
1208 		if(reg > 0)
1209 			selrestore(f, pt0, p0, p1);
1210 		else if(reg < 0)
1211 			selrestore(f, pt1, p1, p0);
1212 		p1 = p0;
1213 	}
1214 	if(p1 < p0){
1215 		tmp = p0;
1216 		p0 = p1;
1217 		p1 = tmp;
1218 	}
1219 	pt0 = frptofchar(f, p0);
1220 	if(p0 == p1)
1221 		frtick(f, pt0, 0);
1222 	selrestore(f, pt0, p0, p1);
1223 	/* restore tick */
1224 	if(f->p0 == f->p1)
1225 		frtick(f, frptofchar(f, f->p0), 1);
1226 	flushimage(f->display, 1);
1227 	*p1p = p1;
1228 	return p0;
1229 }
1230 
1231 int
textselect23(Text * t,uint * q0,uint * q1,Image * high,int mask)1232 textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask)
1233 {
1234 	uint p0, p1;
1235 	int buts;
1236 
1237 	p0 = xselect(t, mousectl, high, &p1);
1238 	buts = mousectl->buttons;
1239 	if((buts & mask) == 0){
1240 		*q0 = p0+t->org;
1241 		*q1 = p1+t->org;
1242 	}
1243 
1244 	while(mousectl->buttons)
1245 		readmouse(mousectl);
1246 	return buts;
1247 }
1248 
1249 int
textselect2(Text * t,uint * q0,uint * q1,Text ** tp)1250 textselect2(Text *t, uint *q0, uint *q1, Text **tp)
1251 {
1252 	int buts;
1253 
1254 	*tp = nil;
1255 	buts = textselect23(t, q0, q1, but2col, 4);
1256 	if(buts & 4)
1257 		return 0;
1258 	if(buts & 1){	/* pick up argument */
1259 		*tp = argtext;
1260 		return 1;
1261 	}
1262 	return 1;
1263 }
1264 
1265 int
textselect3(Text * t,uint * q0,uint * q1)1266 textselect3(Text *t, uint *q0, uint *q1)
1267 {
1268 	int h;
1269 
1270 	h = (textselect23(t, q0, q1, but3col, 1|2) == 0);
1271 	return h;
1272 }
1273 
1274 static Rune left1[] =  { L'{', L'[', L'(', L'<', L'«', 0 };
1275 static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
1276 static Rune left2[] =  { L'\n', 0 };
1277 static Rune left3[] =  { L'\'', L'"', L'`', 0 };
1278 
1279 static
1280 Rune *left[] = {
1281 	left1,
1282 	left2,
1283 	left3,
1284 	nil
1285 };
1286 static
1287 Rune *right[] = {
1288 	right1,
1289 	left2,
1290 	left3,
1291 	nil
1292 };
1293 
1294 void
textdoubleclick(Text * t,uint * q0,uint * q1)1295 textdoubleclick(Text *t, uint *q0, uint *q1)
1296 {
1297 	int c, i;
1298 	Rune *r, *l, *p;
1299 	uint q;
1300 
1301 	for(i=0; left[i]!=nil; i++){
1302 		q = *q0;
1303 		l = left[i];
1304 		r = right[i];
1305 		/* try matching character to left, looking right */
1306 		if(q == 0)
1307 			c = '\n';
1308 		else
1309 			c = textreadc(t, q-1);
1310 		p = runestrchr(l, c);
1311 		if(p != nil){
1312 			if(textclickmatch(t, c, r[p-l], 1, &q))
1313 				*q1 = q-(c!='\n');
1314 			return;
1315 		}
1316 		/* try matching character to right, looking left */
1317 		if(q == t->file->nc)
1318 			c = '\n';
1319 		else
1320 			c = textreadc(t, q);
1321 		p = runestrchr(r, c);
1322 		if(p != nil){
1323 			if(textclickmatch(t, c, l[p-r], -1, &q)){
1324 				*q1 = *q0+(*q0<t->file->nc && c=='\n');
1325 				*q0 = q;
1326 				if(c!='\n' || q!=0 || textreadc(t, 0)=='\n')
1327 					(*q0)++;
1328 			}
1329 			return;
1330 		}
1331 	}
1332 	/* try filling out word to right */
1333 	while(*q1<t->file->nc && isalnum(textreadc(t, *q1)))
1334 		(*q1)++;
1335 	/* try filling out word to left */
1336 	while(*q0>0 && isalnum(textreadc(t, *q0-1)))
1337 		(*q0)--;
1338 }
1339 
1340 int
textclickmatch(Text * t,int cl,int cr,int dir,uint * q)1341 textclickmatch(Text *t, int cl, int cr, int dir, uint *q)
1342 {
1343 	Rune c;
1344 	int nest;
1345 
1346 	nest = 1;
1347 	for(;;){
1348 		if(dir > 0){
1349 			if(*q == t->file->nc)
1350 				break;
1351 			c = textreadc(t, *q);
1352 			(*q)++;
1353 		}else{
1354 			if(*q == 0)
1355 				break;
1356 			(*q)--;
1357 			c = textreadc(t, *q);
1358 		}
1359 		if(c == cr){
1360 			if(--nest==0)
1361 				return 1;
1362 		}else if(c == cl)
1363 			nest++;
1364 	}
1365 	return cl=='\n' && nest==1;
1366 }
1367 
1368 uint
textbacknl(Text * t,uint p,uint n)1369 textbacknl(Text *t, uint p, uint n)
1370 {
1371 	int i, j;
1372 
1373 	/* look for start of this line if n==0 */
1374 	if(n==0 && p>0 && textreadc(t, p-1)!='\n')
1375 		n = 1;
1376 	i = n;
1377 	while(i-->0 && p>0){
1378 		--p;	/* it's at a newline now; back over it */
1379 		if(p == 0)
1380 			break;
1381 		/* at 128 chars, call it a line anyway */
1382 		for(j=128; --j>0 && p>0; p--)
1383 			if(textreadc(t, p-1)=='\n')
1384 				break;
1385 	}
1386 	return p;
1387 }
1388 
1389 void
textsetorigin(Text * t,uint org,int exact)1390 textsetorigin(Text *t, uint org, int exact)
1391 {
1392 	int i, a, fixup;
1393 	Rune *r;
1394 	uint n;
1395 
1396 	if(org>0 && !exact){
1397 		/* org is an estimate of the char posn; find a newline */
1398 		/* don't try harder than 256 chars */
1399 		for(i=0; i<256 && org<t->file->nc; i++){
1400 			if(textreadc(t, org) == '\n'){
1401 				org++;
1402 				break;
1403 			}
1404 			org++;
1405 		}
1406 	}
1407 	a = org-t->org;
1408 	fixup = 0;
1409 	if(a>=0 && a<t->nchars){
1410 		frdelete(t, 0, a);
1411 		fixup = 1;	/* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1412 	}
1413 	else if(a<0 && -a<t->nchars){
1414 		n = t->org - org;
1415 		r = runemalloc(n);
1416 		bufread(t->file, org, r, n);
1417 		frinsert(t, r, r+n, 0);
1418 		free(r);
1419 	}else
1420 		frdelete(t, 0, t->nchars);
1421 	t->org = org;
1422 	textfill(t);
1423 	textscrdraw(t);
1424 	textsetselect(t, t->q0, t->q1);
1425 	if(fixup && t->p1 > t->p0)
1426 		frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1);
1427 }
1428 
1429 void
textreset(Text * t)1430 textreset(Text *t)
1431 {
1432 	t->file->seq = 0;
1433 	t->eq0 = ~0;
1434 	/* do t->delete(0, t->nc, TRUE) without building backup stuff */
1435 	textsetselect(t, t->org, t->org);
1436 	frdelete(t, 0, t->nchars);
1437 	t->org = 0;
1438 	t->q0 = 0;
1439 	t->q1 = 0;
1440 	filereset(t->file);
1441 	bufreset(t->file);
1442 }
1443