|
|
1.1 root 1: /* Copyright (c) 1979 Regents of the University of California */
2: #include "ex.h"
3: #include "ex_tty.h"
4: #include "ex_vis.h"
5:
6: /*
7: * This file defines the operation sequences which interface the
8: * logical changes to the file buffer with the internal and external
9: * display representations.
10: */
11:
12: /*
13: * Undo.
14: *
15: * Undo is accomplished in two ways. We often for small changes in the
16: * current line know how (in terms of a change operator) how the change
17: * occurred. Thus on an intelligent terminal we can undo the operation
18: * by another such operation, using insert and delete character
19: * stuff. The pointers vU[AD][12] index the buffer vutmp when this
20: * is possible and provide the necessary information.
21: *
22: * The other case is that the change involved multiple lines or that
23: * we have moved away from the line or forgotten how the change was
24: * accomplished. In this case we do a redisplay and hope that the
25: * low level optimization routines (which don't look for winning
26: * via insert/delete character) will not lose too badly.
27: */
28: char *vUA1, *vUA2;
29: char *vUD1, *vUD2;
30:
31: vUndo()
32: {
33:
34: /*
35: * Avoid UU which clobbers ability to do u.
36: */
37: if (vundkind == VCAPU || vUNDdot != dot) {
38: beep();
39: return;
40: }
41: CP(vutmp, linebuf);
42: vUD1 = linebuf; vUD2 = strend(linebuf);
43: putmk1(dot, vUNDsav);
44: getDOT();
45: vUA1 = linebuf; vUA2 = strend(linebuf);
46: vundkind = VCAPU;
47: if (state == ONEOPEN || state == HARDOPEN) {
48: vjumpto(dot, vUNDcurs, 0);
49: return;
50: }
51: vdirty(vcline, 1);
52: vsyncCL();
53: vfixcurs();
54: }
55:
56: vundo()
57: {
58: register int cnt;
59: register line *addr;
60: register char *cp;
61: char temp[LBSIZE];
62: bool savenote;
63: int (*OO)();
64: short oldhold = hold;
65:
66: switch (vundkind) {
67:
68: case VMANYINS:
69: wcursor = 0;
70: addr1 = undap1;
71: addr2 = undap2 - 1;
72: vsave();
73: YANKreg('1');
74: notecnt = 0;
75: /* fall into ... */
76:
77: case VMANY:
78: case VMCHNG:
79: vsave();
80: addr = dot - vcline;
81: notecnt = 1;
82: if (undkind == UNDPUT && undap1 == undap2) {
83: beep();
84: return;
85: }
86: /*
87: * Undo() call below basically replaces undap1 to undap2-1
88: * with dol through unddol-1. Hack screen image to
89: * reflect this replacement.
90: */
91: vreplace(undap1 - addr, undap2 - undap1,
92: undkind == UNDPUT ? 0 : unddol - dol);
93: savenote = notecnt;
94: undo(1);
95: if (vundkind != VMCHNG || addr != dot)
96: killU();
97: vundkind = VMANY;
98: cnt = dot - addr;
99: if (cnt < 0 || cnt > vcnt || state != VISUAL) {
100: vjumpto(dot, NOSTR, '.');
101: return;
102: }
103: if (!savenote)
104: notecnt = 0;
105: vcline = cnt;
106: vrepaint(vmcurs);
107: vmcurs = 0;
108: return;
109:
110: case VCHNG:
111: case VCAPU:
112: vundkind = VCHNG;
113: strcpy(temp, vutmp);
114: strcpy(vutmp, linebuf);
115: doomed = column(vUA2 - 1) - column(vUA1 - 1);
116: strcLIN(temp);
117: cp = vUA1; vUA1 = vUD1; vUD1 = cp;
118: cp = vUA2; vUA2 = vUD2; vUD2 = cp;
119: cursor = vUD1;
120: if (state == HARDOPEN) {
121: doomed = 0;
122: vsave();
123: vopen(dot, WBOT);
124: vnline(cursor);
125: return;
126: }
127: /*
128: * Pseudo insert command.
129: */
130: vcursat(cursor);
131: OO = Outchar; Outchar = vinschar; hold |= HOLDQIK;
132: vprepins();
133: temp[vUA2 - linebuf] = 0;
134: for (cp = &temp[vUA1 - linebuf]; *cp;)
135: putchar(*cp++);
136: Outchar = OO; hold = oldhold;
137: endim();
138: physdc(cindent(), cindent() + doomed);
139: doomed = 0;
140: vdirty(vcline, 1);
141: vsyncCL();
142: if (cursor > linebuf && cursor >= strend(linebuf))
143: cursor--;
144: vfixcurs();
145: return;
146:
147: case VNONE:
148: beep();
149: return;
150: }
151: }
152:
153: /*
154: * Initialize undo information before an append.
155: */
156: vnoapp()
157: {
158:
159: vUD1 = vUD2 = cursor;
160: }
161:
162: /*
163: * All the rest of the motion sequences have one or more
164: * cases to deal with. In the case wdot == 0, operation
165: * is totally within current line, from cursor to wcursor.
166: * If wdot is given, but wcursor is 0, then operation affects
167: * the inclusive line range. The hardest case is when both wdot
168: * and wcursor are given, then operation affects from line dot at
169: * cursor to line wdot at wcursor.
170: */
171:
172: /*
173: * Move is simple, except for moving onto new lines in hardcopy open mode.
174: */
175: vmove()
176: {
177: register int cnt;
178:
179: if (wdot) {
180: if (wdot < one || wdot > dol) {
181: beep();
182: return;
183: }
184: cnt = wdot - dot;
185: wdot = NOLINE;
186: if (cnt)
187: killU();
188: vupdown(cnt, wcursor);
189: return;
190: }
191:
192: /*
193: * When we move onto a new line, save information for U undo.
194: */
195: if (vUNDdot != dot) {
196: vUNDsav = *dot;
197: vUNDcurs = wcursor;
198: vUNDdot = dot;
199: }
200:
201: /*
202: * In hardcopy open, type characters to left of cursor
203: * on new line, or back cursor up if its to left of where we are.
204: * In any case if the current line is ``rubbled'' i.e. has trashy
205: * looking overstrikes on it or \'s from deletes, we reprint
206: * so it is more comprehensible (and also because we can't work
207: * if we let it get more out of sync since column() won't work right.
208: */
209: if (state == HARDOPEN) {
210: register char *cp;
211: if (rubble) {
212: register int c;
213: int oldhold = hold;
214:
215: sethard();
216: cp = wcursor;
217: c = *cp;
218: *cp = 0;
219: hold |= HOLDDOL;
220: vreopen(WTOP, lineDOT(), vcline);
221: hold = oldhold;
222: *cp = c;
223: } else if (wcursor > cursor) {
224: vfixcurs();
225: for (cp = cursor; *cp && cp < wcursor;) {
226: register int c = *cp++ & TRIM;
227:
228: putchar(c ? c : ' ');
229: }
230: }
231: }
232: vsetcurs(wcursor);
233: }
234:
235: /*
236: * Delete operator.
237: *
238: * Hard case of deleting a range where both wcursor and wdot
239: * are specified is treated as a special case of change and handled
240: * by vchange (although vchange may pass it back if it degenerates
241: * to a full line range delete.)
242: */
243: vdelete(c)
244: char c;
245: {
246: register char *cp;
247: register int i;
248:
249: if (wdot) {
250: if (wcursor) {
251: vchange('d');
252: return;
253: }
254: if ((i = xdw()) < 0)
255: return;
256: if (state != VISUAL) {
257: vgoto(LINE(0), 0);
258: vputchar('@');
259: }
260: wdot = dot;
261: vremote(i, delete, 0);
262: notenam = "delete";
263: DEL[0] = 0;
264: killU();
265: vreplace(vcline, i, 0);
266: if (wdot > dol)
267: vcline--;
268: vrepaint(NOSTR);
269: return;
270: }
271: if (wcursor < linebuf)
272: wcursor = linebuf;
273: if (cursor == wcursor) {
274: beep();
275: return;
276: }
277: i = vdcMID();
278: cp = cursor;
279: setDEL();
280: CP(cp, wcursor);
281: if (cp > linebuf && (cp[0] == 0 || c == '#'))
282: cp--;
283: if (state == HARDOPEN) {
284: bleep(i, cp);
285: cursor = cp;
286: return;
287: }
288: physdc(column(cursor - 1), i);
289: DEPTH(vcline) = 0;
290: vreopen(LINE(vcline), lineDOT(), vcline);
291: vsyncCL();
292: vsetcurs(cp);
293: }
294:
295: /*
296: * Change operator.
297: *
298: * In a single line we mark the end of the changed area with '$'.
299: * On multiple whole lines, we clear the lines first.
300: * Across lines with both wcursor and wdot given, we delete
301: * and sync then append (but one operation for undo).
302: */
303: vchange(c)
304: char c;
305: {
306: register char *cp;
307: register int i, ind, cnt;
308: line *addr;
309:
310: if (wdot) {
311: /*
312: * Change/delete of lines or across line boundaries.
313: */
314: if ((cnt = xdw()) < 0)
315: return;
316: getDOT();
317: if (wcursor && cnt == 1) {
318: /*
319: * Not really.
320: */
321: wdot = 0;
322: if (c == 'd') {
323: vdelete(c);
324: return;
325: }
326: goto smallchange;
327: }
328: if (cursor && wcursor) {
329: /*
330: * Across line boundaries, but not
331: * necessarily whole lines.
332: * Construct what will be left.
333: */
334: *cursor = 0;
335: strcpy(genbuf, linebuf);
336: getline(*wdot);
337: if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) {
338: getDOT();
339: beep();
340: return;
341: }
342: strcat(genbuf, wcursor);
343: if (c == 'd' && *vpastwh(genbuf) == 0) {
344: /*
345: * Although this is a delete
346: * spanning line boundaries, what
347: * would be left is all white space,
348: * so take it all away.
349: */
350: wcursor = 0;
351: getDOT();
352: op = 0;
353: notpart(lastreg);
354: notpart('1');
355: vdelete(c);
356: return;
357: }
358: ind = -1;
359: } else if (c == 'd' && wcursor == 0) {
360: vdelete(c);
361: return;
362: } else
363: #ifdef LISPCODE
364: /*
365: * We are just substituting text for whole lines,
366: * so determine the first autoindent.
367: */
368: if (value(LISP) && value(AUTOINDENT))
369: ind = lindent(dot);
370: else
371: #endif
372: ind = whitecnt(linebuf);
373: i = vcline >= 0 ? LINE(vcline) : WTOP;
374:
375: /*
376: * Delete the lines from the buffer,
377: * and remember how the partial stuff came about in
378: * case we are told to put.
379: */
380: addr = dot;
381: vremote(cnt, delete, 0);
382: setpk();
383: notenam = "delete";
384: if (c != 'd')
385: notenam = "change";
386: /*
387: * If DEL[0] were nonzero, put would put it back
388: * rather than the deleted lines.
389: */
390: DEL[0] = 0;
391: if (cnt > 1)
392: killU();
393:
394: /*
395: * Now hack the screen image coordination.
396: */
397: vreplace(vcline, cnt, 0);
398: wdot = NOLINE;
399: noteit(0);
400: vcline--;
401: if (addr <= dol)
402: dot--;
403:
404: /*
405: * If this is a across line delete/change,
406: * cursor stays where it is; just splice together the pieces
407: * of the new line. Otherwise generate a autoindent
408: * after a S command.
409: */
410: if (ind >= 0) {
411: *genindent(ind) = 0;
412: vdoappend(genbuf);
413: } else {
414: vmcurs = cursor;
415: strcLIN(genbuf);
416: vdoappend(linebuf);
417: }
418:
419: /*
420: * Indicate a change on hardcopies by
421: * erasing the current line.
422: */
423: if (c != 'd' && state != VISUAL && state != HARDOPEN) {
424: int oldhold = hold;
425:
426: hold |= HOLDAT, vclrlin(i, dot), hold = oldhold;
427: }
428:
429: /*
430: * Open the line (logically) on the screen, and
431: * update the screen tail. Unless we are really a delete
432: * go off and gather up inserted characters.
433: */
434: vcline++;
435: if (vcline < 0)
436: vcline = 0;
437: vopen(dot, i);
438: vsyncCL();
439: noteit(1);
440: if (c != 'd') {
441: if (ind >= 0) {
442: cursor = linebuf;
443: linebuf[0] = 0;
444: vfixcurs();
445: } else {
446: ind = 0;
447: vcursat(cursor);
448: }
449: vappend('x', 1, ind);
450: return;
451: }
452: if (*cursor == 0 && cursor > linebuf)
453: cursor--;
454: vrepaint(cursor);
455: return;
456: }
457:
458: smallchange:
459: /*
460: * The rest of this is just low level hacking on changes
461: * of small numbers of characters.
462: */
463: if (wcursor < linebuf)
464: wcursor = linebuf;
465: if (cursor == wcursor) {
466: beep();
467: return;
468: }
469: i = vdcMID();
470: cp = cursor;
471: if (state != HARDOPEN)
472: vfixcurs();
473:
474: /*
475: * Put out the \\'s indicating changed text in hardcopy,
476: * or mark the end of the change with $ if not hardcopy.
477: */
478: if (state == HARDOPEN)
479: bleep(i, cp);
480: else {
481: vcursbef(wcursor);
482: putchar('$');
483: i = cindent();
484: }
485:
486: /*
487: * Remember the deleted text for possible put,
488: * and then prepare and execute the input portion of the change.
489: */
490: cursor = cp;
491: setDEL();
492: CP(cursor, wcursor);
493: if (state != HARDOPEN) {
494: vcursaft(cursor - 1);
495: doomed = i - cindent();
496: } else {
497: /*
498: sethard();
499: wcursor = cursor;
500: cursor = linebuf;
501: vgoto(outline, value(NUMBER) << 3);
502: vmove();
503: */
504: doomed = 0;
505: }
506: prepapp();
507: vappend('c', 1, 0);
508: }
509:
510: /*
511: * Open new lines.
512: *
513: * Tricky thing here is slowopen. This causes display updating
514: * to be held off so that 300 baud dumb terminals don't lose badly.
515: * This also suppressed counts, which otherwise say how many blank
516: * space to open up. Counts are also suppressed on intelligent terminals.
517: * Actually counts are obsoleted, since if your terminal is slow
518: * you are better off with slowopen.
519: */
520: voOpen(c, cnt)
521: char c;
522: register int cnt;
523: {
524: register int ind = 0, i;
525: short oldhold = hold;
526:
527: if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
528: cnt = 1;
529: vsave();
530: setLAST();
531: if (value(AUTOINDENT))
532: ind = whitecnt(linebuf);
533: if (c == 'O') {
534: vcline--;
535: dot--;
536: if (dot > zero)
537: getDOT();
538: }
539: if (value(AUTOINDENT)) {
540: #ifdef LISPCODE
541: if (value(LISP))
542: ind = lindent(dot + 1);
543: #endif
544: }
545: killU();
546: prepapp();
547: vundkind = VMANY;
548: if (state != VISUAL)
549: c = WBOT + 1;
550: else {
551: c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
552: if (c < ZERO)
553: c = ZERO;
554: i = LINE(vcline + 1) - c;
555: if (i < cnt && c <= WBOT && (!AL || !DL))
556: vinslin(c, cnt - i, vcline);
557: }
558: *genindent(ind) = 0;
559: vdoappend(genbuf);
560: vcline++;
561: oldhold = hold;
562: hold |= HOLDROL;
563: vopen(dot, c);
564: hold = oldhold;
565: if (value(SLOWOPEN))
566: /*
567: * Oh, so lazy!
568: */
569: vscrap();
570: else
571: vsync1(LINE(vcline));
572: cursor = linebuf;
573: linebuf[0] = 0;
574: vappend('o', 1, ind);
575: }
576:
577: /*
578: * > < and = shift operators.
579: *
580: * Note that =, which aligns lisp, is just a ragged sort of shift,
581: * since it never distributes text between lines.
582: */
583: char vshnam[2] = { 'x', 0 };
584:
585: vshftop()
586: {
587: register line *addr;
588: register int cnt;
589:
590: if ((cnt = xdw()) < 0)
591: return;
592: addr = dot;
593: vremote(cnt, vshift, 0);
594: vshnam[0] = op;
595: notenam = vshnam;
596: dot = addr;
597: vreplace(vcline, cnt, cnt);
598: if (state == HARDOPEN)
599: vcnt = 0;
600: vrepaint(NOSTR);
601: }
602:
603: /*
604: * !.
605: *
606: * Filter portions of the buffer through unix commands.
607: */
608: vfilter()
609: {
610: register line *addr;
611: register int cnt;
612: char *oglobp, d;
613:
614: if ((cnt = xdw()) < 0)
615: return;
616: if (vglobp)
617: vglobp = uxb;
618: if (readecho('!'))
619: return;
620: oglobp = globp; globp = genbuf + 1;
621: d = peekc; ungetchar(0);
622: CATCH
623: fixech();
624: unix0(0);
625: ONERR
626: splitw = 0;
627: ungetchar(d);
628: vrepaint(cursor);
629: globp = oglobp;
630: return;
631: ENDCATCH
632: ungetchar(d); globp = oglobp;
633: addr = dot;
634: CATCH
635: vgoto(WECHO, 0); flusho();
636: vremote(cnt, filter, 2);
637: ONERR
638: vdirty(0, LINES);
639: ENDCATCH
640: if (dot == zero && dol > zero)
641: dot = one;
642: splitw = 0;
643: notenam = "";
644: vreplace(vcline, cnt, undap2 - undap1);
645: dot = addr;
646: if (dot > dol) {
647: dot--;
648: vcline--;
649: }
650: vrepaint(NOSTR);
651: }
652:
653: /*
654: * Xdw exchanges dot and wdot if appropriate and also checks
655: * that wdot is reasonable. Its name comes from
656: * xchange dotand wdot
657: */
658: xdw()
659: {
660: register char *cp;
661: register int cnt;
662: /*
663: register int notp = 0;
664: */
665:
666: if (wdot == NOLINE || wdot < one || wdot > dol) {
667: beep();
668: return (-1);
669: }
670: vsave();
671: setLAST();
672: if (dot > wdot) {
673: register line *addr;
674:
675: vcline -= dot - wdot;
676: addr = dot; dot = wdot; wdot = addr;
677: cp = cursor; cursor = wcursor; wcursor = cp;
678: }
679: /*
680: * If a region is specified but wcursor is at the begining
681: * of the last line, then we move it to be the end of the
682: * previous line (actually off the end).
683: */
684: if (cursor && wcursor == linebuf && wdot > dot) {
685: wdot--;
686: getDOT();
687: if (vpastwh(linebuf) >= cursor)
688: wcursor = 0;
689: else {
690: getline(*wdot);
691: wcursor = strend(linebuf);
692: getDOT();
693: }
694: /*
695: * Should prepare in caller for possible dot == wdot.
696: */
697: }
698: cnt = wdot - dot + 1;
699: if (vreg) {
700: vremote(cnt, YANKreg, vreg);
701: /*
702: if (notp)
703: notpart(vreg);
704: */
705: }
706:
707: /*
708: * Kill buffer code. If delete operator is c or d, then save
709: * the region in numbered buffers.
710: *
711: * BUG: This may be somewhat inefficient due
712: * to the way named buffer are implemented,
713: * necessitating some optimization.
714: */
715: vreg = 0;
716: if (any(op, "cd")) {
717: vremote(cnt, YANKreg, '1');
718: /*
719: if (notp)
720: notpart('1');
721: */
722: }
723: return (cnt);
724: }
725:
726: /*
727: * Routine for vremote to call to implement shifts.
728: */
729: vshift()
730: {
731:
732: shift(op, 1);
733: }
734:
735: /*
736: * Replace a single character with the next input character.
737: * A funny kind of insert.
738: */
739: vrep(cnt)
740: register int cnt;
741: {
742: register int i, c;
743:
744: if (cnt > strlen(cursor)) {
745: beep();
746: return;
747: }
748: i = column(cursor + cnt - 1);
749: vcursat(cursor);
750: doomed = i - cindent();
751: if (!vglobp) {
752: c = getesc();
753: if (c == 0) {
754: vfixcurs();
755: return;
756: }
757: ungetkey(c);
758: }
759: CP(vutmp, linebuf);
760: vundkind = VCHNG;
761: wcursor = cursor + cnt;
762: vUD1 = cursor; vUD2 = wcursor;
763: CP(cursor, wcursor);
764: prepapp();
765: vappend('r', cnt, 0);
766: *lastcp++ = INS[0];
767: setLAST();
768: }
769:
770: /*
771: * Yank.
772: *
773: * Yanking to string registers occurs for free (essentially)
774: * in the routine xdw().
775: */
776: vyankit()
777: {
778: register int cnt;
779:
780: if (wdot) {
781: if ((cnt = xdw()) < 0)
782: return;
783: vremote(cnt, yank, 0);
784: setpk();
785: notenam = "yank";
786: vundkind = VNONE;
787: DEL[0] = 0;
788: wdot = NOLINE;
789: if (notecnt <= vcnt - vcline && notecnt < value(REPORT))
790: notecnt = 0;
791: vrepaint(cursor);
792: return;
793: }
794: takeout(DEL);
795: }
796:
797: /*
798: * Set pkill variables so a put can
799: * know how to put back partial text.
800: * This is necessary because undo needs the complete
801: * line images to be saved, while a put wants to trim
802: * the first and last lines. The compromise
803: * is for put to be more clever.
804: */
805: setpk()
806: {
807:
808: if (wcursor) {
809: pkill[0] = cursor;
810: pkill[1] = wcursor;
811: }
812: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.