|
|
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.