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: }