|
|
1.1 root 1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
2: static char rcsid[] = "$Header: deco.c,v 2.3 84/07/19 11:45:12 guido Exp $";
3:
4: /*
5: * B editor -- Delete and copy commands.
6: */
7:
8: #include <ctype.h>
9:
10: #include "b.h"
11: #include "erro.h"
12: #include "bobj.h"
13: #include "node.h"
14: #include "gram.h"
15: #include "supr.h"
16: #include "queu.h"
17:
18:
19: value copyout(); /* Forward */
20:
21: /*
22: * DELETE and COPY currently share a buffer, called the copy buffer.
23: * (Physically, there is one such a buffer in each environment.)
24: * In ordinary use, the copy buffer receives the text deleted by the
25: * last DELETE command (unless it just removed a hole); the COPY command
26: * can then be used (with the focus on a hole) to copy it back.
27: * When some portion of text must be held while other text is deleted,
28: * the COPY command again, but now with the focus on the text to be held,
29: * copies it to the buffer and deleted text won't overwrite the buffer
30: * until it is copied back at least once.
31: * If the buffer holds text that was explicitly copied out but not yet
32: * copied back in, it is saved on a file when the editor exits, so it can
33: * be used in the next session; but this is not true for text implicitly
34: * placed in the buffer through DELETE.
35: */
36:
37: /*
38: * Delete command -- delete the text in the focus, or delete the hole
39: * if it is only a hole.
40: */
41:
42: Visible bool
43: delete(ep)
44: register environ *ep;
45: {
46: higher(ep);
47: shrink(ep);
48: if (ishole(ep))
49: return delhole(ep);
50: if (!ep->copyflag) {
51: release(ep->copybuffer);
52: ep->copybuffer = copyout(ep);
53: }
54: return delbody(ep);
55: }
56:
57:
58: /*
59: * Delete the focus under the assumption that it contains some text.
60: */
61:
62: Visible bool
63: delbody(ep)
64: register environ *ep;
65: {
66: ep->changed = Yes;
67:
68: subgrow(ep, No); /* Don't ignore spaces */
69: switch (ep->mode) {
70:
71: case SUBRANGE:
72: if (ep->s1&1)
73: return delfixed(ep);
74: return delvarying(ep);
75:
76: case SUBSET:
77: return delsubset(ep, Yes);
78:
79: case SUBLIST:
80: return delsublist(ep);
81:
82: case WHOLE:
83: return delwhole(ep);
84:
85: default:
86: Abort();
87: /* NOTREACHED */
88: }
89: }
90:
91:
92: /*
93: * Delete portion (ep->mode == SUBRANGE) of varying text ((ep->s1&1) == 0).
94: */
95:
96: Hidden bool
97: delvarying(ep)
98: register environ *ep;
99: {
100: auto queue q = Qnil;
101: register node n = tree(ep->focus);
102: auto value v = (value) child(n, ep->s1/2);
103: register len = Length(v);
104:
105: Assert(ep->mode == SUBRANGE && !(ep->s1&1)); /* Wrong call */
106: Assert(Type(v) == Tex); /* Inconsistent parse tree */
107: if (ep->s2 == 0
108: && !mayinsert(tree(ep->focus), ep->s1/2, 0, Str(v)[ep->s3 + 1])) {
109: /* Cannot do simple substring deletion. */
110: stringtoqueue(Str(v) + ep->s3 + 1, &q);
111: delfocus(&ep->focus);
112: ep->mode = WHOLE;
113: return app_queue(ep, &q);
114: }
115: v = copy(v);
116: putintrim(&v, ep->s2, len - ep->s3 - 1, "");
117: s_downi(ep, ep->s1/2);
118: replace(&ep->focus, (node) v);
119: s_up(ep);
120: ep->mode = VHOLE;
121: return Yes;
122: }
123:
124:
125: /*
126: * Delete portion (ep->mode == SUBRANGE) of fixed text ((ep->s1&1) == 1).
127: */
128:
129: Hidden bool
130: delfixed(ep)
131: register environ *ep;
132: {
133: register node n = tree(ep->focus);
134: char buf[15]; /* Long enough for all fixed texts */
135: register string repr = noderepr(n)[ep->s1/2];
136: register int len;
137: queue q = Qnil;
138: bool ok;
139:
140: Assert(ep->mode == SUBRANGE && (ep->s1&1));
141: if (ep->s1 > 1) {
142: ep->mode = FHOLE;
143: return Yes;
144: }
145: Assert(fwidth(repr) < sizeof buf - 1);
146: len = ep->s2;
147: ep->s2 = ep->s3 + 1;
148: ep->mode = FHOLE;
149: nosuggtoqueue(ep, &q);
150: strcpy(buf, repr);
151: if (nchildren(tree(ep->focus)) > 0)
152: buf[len] = 0;
153: else
154: strcpy(buf+len, buf+ep->s2);
155: delfocus(&ep->focus);
156: ep->mode = WHOLE;
157: markpath(&ep->focus, 1);
158: ok = ins_string(ep, buf, &q, 0);
159: if (!ok) {
160: qrelease(q);
161: return No;
162: }
163: firstmarked(&ep->focus, 1) || Abort();
164: unmkpath(&ep->focus, 1);
165: fixfocus(ep, len);
166: return app_queue(ep, &q);
167: }
168:
169:
170: /*
171: * Delete focus if ep->mode == SUBSET.
172: */
173:
174: Hidden bool
175: delsubset(ep, hack)
176: register environ *ep;
177: bool hack;
178: {
179: auto queue q = Qnil;
180: auto queue q2 = Qnil;
181: register node n = tree(ep->focus);
182: register node nn;
183: register string *rp = noderepr(n);
184: register int nch = nchildren(n);
185: register int i;
186:
187: if (hack) {
188: shrsubset(ep);
189: if (ep->s1 == ep->s2 && !(ep->s1&1)) {
190: nn = child(tree(ep->focus), ep->s1/2);
191: if (fwidth(noderepr(nn)[0]) < 0) {
192: /* It starts with a newline, leave the newline */
193: s_downi(ep, ep->s1/2);
194: ep->mode = SUBSET;
195: ep->s1 = 2;
196: ep->s2 = 2*nchildren(nn) + 1;
197: return delsubset(ep, hack);
198: }
199: }
200: subgrsubset(ep, No); /* Undo shrsubset */
201: if (ep->s2 == 3 && rp[1] && Strequ(rp[1], "\t"))
202: --ep->s2; /* Hack for deletion of unit-head or if/for/wh. head */
203: }
204: if (ep->s1 == 1 && Fw_negative(rp[0]))
205: ++ep->s1; /* Hack for deletion of test-suite or refinement head */
206:
207: if (Fw_zero(rp[0]) ? (ep->s2 < 3 || ep->s1 > 3) : ep->s1 > 1) {
208: /* No deep structural change */
209: for (i = (ep->s1+1)/2; i <= ep->s2/2; ++i) {
210: s_downi(ep, i);
211: delfocus(&ep->focus);
212: s_up(ep);
213: }
214: if (ep->s1&1) {
215: ep->mode = FHOLE;
216: ep->s2 = 0;
217: }
218: else if (Type(child(tree(ep->focus), ep->s1/2)) == Tex) {
219: ep->mode = VHOLE;
220: ep->s2 = 0;
221: }
222: else {
223: s_downi(ep, ep->s1/2);
224: ep->mode = ATBEGIN;
225: }
226: return Yes;
227: }
228:
229: balance(ep); /* Make balanced \t - \b pairs */
230: subsettoqueue(n, 1, ep->s1-1, &q);
231: subsettoqueue(n, ep->s2+1, 2*nch+1, &q2);
232: nonewline(&q2); /* Wonder what will happen...? */
233: delfocus(&ep->focus);
234: ep->mode = ATBEGIN;
235: leftvhole(ep);
236: if (!ins_queue(ep, &q, &q2)) {
237: qrelease(q2);
238: return No;
239: }
240: return app_queue(ep, &q2);
241: }
242:
243:
244: /*
245: * Delete the focus if ep->mode == SUBLIST.
246: */
247:
248: delsublist(ep)
249: register environ *ep;
250: {
251: register node n;
252: register int i;
253: register int sym;
254: queue q = Qnil;
255: bool flag;
256:
257: Assert(ep->mode == SUBLIST);
258: n = tree(ep->focus);
259: flag = fwidth(noderepr(n)[0]) < 0;
260: for (i = ep->s3; i > 0; --i) {
261: n = lastchild(n);
262: Assert(n);
263: }
264: if (flag) {
265: n = nodecopy(n);
266: s_down(ep);
267: do {
268: delfocus(&ep->focus);
269: } while (rite(&ep->focus));
270: if (!allowed(ep->focus, symbol(n))) {
271: error(DEL_REM); /* The remains wouldn't fit */
272: noderelease(n);
273: return No;
274: }
275: replace(&ep->focus, n);
276: s_up(ep);
277: s_down(ep); /* I.e., to leftmost sibling */
278: ep->mode = WHOLE;
279: return Yes;
280: }
281: sym = symbol(n);
282: if (sym == Optional || sym == Hole) {
283: delfocus(&ep->focus);
284: ep->mode = WHOLE;
285: }
286: else if (!allowed(ep->focus, sym)) {
287: preptoqueue(n, &q);
288: delfocus(&ep->focus);
289: ep->mode = WHOLE;
290: return app_queue(ep, &q);
291: }
292: else {
293: replace(&ep->focus, nodecopy(n));
294: ep->mode = ATBEGIN;
295: }
296: return Yes;
297: }
298:
299:
300: /*
301: * Delete the focus if ep->mode == WHOLE.
302: */
303:
304: Hidden bool
305: delwhole(ep)
306: register environ *ep;
307: {
308: register int sym = symbol(tree(ep->focus));
309:
310: Assert(ep->mode == WHOLE);
311: if (sym == Optional || sym == Hole)
312: return No;
313: delfocus(&ep->focus);
314: return Yes;
315: }
316:
317:
318: /*
319: * Delete the focus if it is only a hole.
320: * Assume shrink() has been called before!
321: */
322:
323: Hidden bool
324: delhole(ep)
325: register environ *ep;
326: {
327: node n;
328: int sym;
329: bool flag = No;
330:
331: switch (ep->mode) {
332:
333: case ATBEGIN:
334: case VHOLE:
335: case FHOLE:
336: case ATEND:
337: return widen(ep);
338:
339: case WHOLE:
340: Assert((sym = symbol(tree(ep->focus))) == Optional || sym == Hole);
341: if (ichild(ep->focus) != 1)
342: break;
343: if (!up(&ep->focus))
344: return No;
345: higher(ep);
346: ep->mode = SUBSET;
347: ep->s1 = 2;
348: ep->s2 = 2;
349: if (fwidth(noderepr(tree(ep->focus))[0]) < 0) {
350: flag = Yes;
351: ep->s2 = 3; /* Extend to rest of line */
352: }
353: }
354:
355: ep->changed = Yes;
356: grow(ep);
357:
358: switch (ep->mode) {
359:
360: case SUBSET:
361: if (!delsubset(ep, No))
362: return No;
363: if (!flag)
364: return widen(ep);
365: leftvhole(ep);
366: oneline(ep);
367: return Yes;
368:
369: case SUBLIST:
370: n = tree(ep->focus);
371: n = lastchild(n);
372: sym = symbol(n);
373: if (!allowed(ep->focus, sym)) {
374: error(DEL_REM); /* The remains wouldn't fit */
375: return No;
376: }
377: flag = samelevel(sym, symbol(tree(ep->focus)));
378: replace(&ep->focus, nodecopy(n));
379: if (flag) {
380: ep->mode = SUBLIST;
381: ep->s3 = 1;
382: }
383: else
384: ep->mode = WHOLE;
385: return Yes;
386:
387: case WHOLE:
388: Assert(!parent(ep->focus)); /* Must be at root! */
389: return No;
390:
391: default:
392: Abort();
393: /* NOTREACHED */
394:
395: }
396: }
397:
398:
399: /*
400: * Subroutine to delete the focus.
401: */
402:
403: Visible Procedure
404: delfocus(pp)
405: register path *pp;
406: {
407: register path pa = parent(*pp);
408: register int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
409:
410: replace(pp, child(gram(sympa), ichild(*pp)));
411: }
412:
413:
414: /*
415: * Copy command -- copy the focus to the copy buffer if it contains
416: * some text, copy the copy buffer into the focus if the focus is
417: * empty (just a hole).
418: */
419:
420: Visible bool
421: copyinout(ep)
422: register environ *ep;
423: {
424: shrink(ep);
425: if (!ishole(ep)) {
426: release(ep->copybuffer);
427: ep->copybuffer = copyout(ep);
428: ep->copyflag = !!ep->copybuffer;
429: return ep->copyflag;
430: }
431: else {
432: fixit(ep); /* Make sure it looks like a hole now */
433: if (!copyin(ep, (queue) ep->copybuffer))
434: return No;
435: ep->copyflag = No;
436: return Yes;
437: }
438: }
439:
440:
441: /*
442: * Copy the focus to the copy buffer.
443: */
444:
445: Visible value
446: copyout(ep)
447: register environ *ep;
448: {
449: auto queue q = Qnil;
450: auto path p;
451: register node n;
452: register value v;
453: char buf[15];
454: register string *rp;
455: register int i;
456:
457: switch (ep->mode) {
458: case WHOLE:
459: preptoqueue(tree(ep->focus), &q);
460: break;
461: case SUBLIST:
462: p = pathcopy(ep->focus);
463: for (i = ep->s3; i > 0; --i)
464: downrite(&p) || Abort();
465: for (i = ep->s3; i > 0; --i) {
466: up(&p) || Abort();
467: n = tree(p);
468: subsettoqueue(n, 1, 2*nchildren(n) - 1, &q);
469: }
470: pathrelease(p);
471: break;
472: case SUBSET:
473: balance(ep);
474: subsettoqueue(tree(ep->focus), ep->s1, ep->s2, &q);
475: break;
476: case SUBRANGE:
477: Assert(ep->s3 >= ep->s2);
478: if (ep->s1&1) { /* Fixed text */
479: Assert(ep->s3 - ep->s2 + 1 < sizeof buf);
480: rp = noderepr(tree(ep->focus));
481: Assert(ep->s2 < Fwidth(rp[ep->s1/2]));
482: strncpy(buf, rp[ep->s1/2] + ep->s2, ep->s3 - ep->s2 + 1);
483: buf[ep->s3 - ep->s2 + 1] = 0;
484: stringtoqueue(buf, &q);
485: }
486: else { /* Varying text */
487: v = (value) child(tree(ep->focus), ep->s1/2);
488: Assert(Type(v) == Tex);
489: v = trim(v, ep->s2, Length(v) - ep->s3 - 1);
490: preptoqueue((node)v, &q);
491: release(v);
492: }
493: break;
494: default:
495: Abort();
496: }
497: nonewline(&q);
498: return (value)q;
499: }
500:
501:
502: /*
503: * Subroutine to ensure the copy buffer doesn't start with a newline.
504: */
505:
506: Hidden Procedure
507: nonewline(pq)
508: register queue *pq;
509: {
510: register node n;
511: register int c;
512:
513: if (!emptyqueue(*pq)) {
514: for (;;) {
515: n = queuebehead(pq);
516: if (Type(n) == Tex) {
517: if (Str((value) n)[0] != '\n')
518: preptoqueue(n, pq);
519: noderelease(n);
520: break;
521: }
522: else {
523: c = nodechar(n);
524: if (c != '\n')
525: preptoqueue(n, pq);
526: else
527: splitnode(n, pq);
528: noderelease(n);
529: if (c != '\n')
530: break;
531: }
532: }
533: }
534: }
535:
536:
537: /*
538: * Refinement for copyout, case SUBSET: make sure that \t is balanced with \b.
539: * Actually it can only handle the case where a \t is in the subset and the
540: * matching \b is immediately following.
541: */
542:
543: Hidden Procedure
544: balance(ep)
545: environ *ep;
546: {
547: string *rp = noderepr(tree(ep->focus));
548: int i;
549: int level = 0;
550:
551: Assert(ep->mode == SUBSET);
552: for (i = ep->s1/2; i*2 < ep->s2; ++i) {
553: if (rp[i]) {
554: if (index(rp[i], '\t'))
555: ++level;
556: else if (index(rp[i], '\b'))
557: --level;
558: }
559: }
560: if (level > 0 && i*2 == ep->s2 && rp[i] && index(rp[i], '\b'))
561: ep->s2 = 2*i + 1;
562: }
563:
564:
565: /*
566: * Copy the copy buffer to the focus.
567: */
568:
569: Hidden bool
570: copyin(ep, q)
571: register environ *ep;
572: /*auto*/ queue q;
573: {
574: auto queue q2 = Qnil;
575:
576: if (!q) {
577: error(COPY_EMPTY); /* Empty copy buffer */
578: return No;
579: }
580: ep->changed = Yes;
581: q = qcopy(q);
582: if (!ins_queue(ep, &q, &q2)) {
583: qrelease(q2);
584: return No;
585: }
586: return app_queue(ep, &q2);
587: }
588:
589:
590: /*
591: * Find out whether the focus looks like a hole or if it has some real
592: * text in it.
593: * Assumes shrink(ep) has already been performed.
594: */
595:
596: Visible bool
597: ishole(ep)
598: register environ *ep;
599: {
600: register int sym;
601:
602: switch (ep->mode) {
603:
604: case ATBEGIN:
605: case ATEND:
606: case VHOLE:
607: case FHOLE:
608: return Yes;
609:
610: case SUBLIST:
611: case SUBRANGE:
612: return No;
613:
614: case SUBSET:
615: return colonhack(ep); /* (Side-effect!) */
616:
617: case WHOLE:
618: sym = symbol(tree(ep->focus));
619: return sym == Optional || sym == Hole;
620:
621: default:
622: Abort();
623: /* NOTREACHED */
624: }
625: }
626:
627:
628: /*
629: * Amendment to ishole so that it categorizes '?: ?' as a hole.
630: * This makes deletion of empty refinements / alternative-suites
631: * easier (Steven).
632: */
633:
634: Hidden bool
635: colonhack(ep)
636: environ *ep;
637: {
638: node n = tree(ep->focus);
639: node n1;
640: string *rp = noderepr(n);
641: int i;
642: int sym;
643:
644: for (i = ep->s1; i <= ep->s2; ++i) {
645: if (i&1) {
646: if (!allright(rp[i/2]))
647: return No;
648: }
649: else {
650: n1 = child(n, i/2);
651: if (Type(n1) == Tex)
652: return No;
653: sym = symbol(n1);
654: if (sym != Hole && sym != Optional)
655: return No;
656: }
657: }
658: return Yes;
659: }
660:
661:
662: /*
663: * Refinement for colonhack. Recognize strings that are almost blank
664: * (i.e. containing only spaces, colons and the allowed control characters).
665: */
666:
667: Hidden bool
668: allright(repr)
669: string repr;
670: {
671: if (repr) {
672: for (; *repr; ++repr) {
673: if (!index(": \t\b\n\r", *repr))
674: return No;
675: }
676: }
677: return Yes;
678: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.