text.c 27 KB

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