1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ 2: static char rcsid[] = "$Header: ins2.c,v 2.4 84/10/26 12:08:24 guido Exp $"; 3: 4: /* 5: * B editor -- Insert characters from keyboard. 6: */ 7: 8: #include "b.h" 9: #include "bobj.h" 10: #include "node.h" 11: #include "supr.h" 12: #include "queu.h" 13: #include "gram.h" 14: #include "tabl.h" 15: 16: 17: /* 18: * Insert a character. 19: */ 20: 21: Visible bool 22: ins_char(ep, c, alt_c) 23: register environ *ep; 24: int c; 25: int alt_c; 26: { 27: auto queue q = Qnil; 28: auto queue qf = Qnil; 29: auto value copyout(); 30: auto string str; 31: char buf[2]; 32: int where; 33: bool spwhere; 34: 35: higher(ep); 36: shrink(ep); 37: if (index("({[`'\"", c) && !ishole(ep)) { 38: /* Surround something. Wonder what will happen! */ 39: qf = (queue) copyout(ep); 40: if (!delbody(ep)) { 41: qrelease(qf); 42: return No; 43: } 44: } 45: fixit(ep); 46: ep->changed = Yes; 47: buf[0] = c; 48: buf[1] = 0; 49: if (!ins_string(ep, buf, &q, alt_c)) 50: return No; 51: if (!emptyqueue(q) || !emptyqueue(qf)) { 52: /* Slight variation on app_queue */ 53: if (!emptyqueue(qf) && emptyqueue(q)) 54: ritevhole(ep); /* Wizardry. Why does this work? */ 55: spwhere = ep->spflag; 56: ep->spflag = No; 57: where = focoffset(ep); 58: markpath(&ep->focus, 1); 59: ep->spflag = spwhere; 60: if (ep->mode == FHOLE && ep->s2 > 0) { 61: /* If we just caused a suggestion, insert the remains 62: after the suggested text, not after its first character. */ 63: str = ""; 64: if (!soften(ep, &str, 0)) { 65: ep->mode = ATEND; 66: leftvhole(ep); 67: if (symbol(tree(ep->focus)) == Hole) { 68: ep->mode = ATBEGIN; 69: leftvhole(ep); 70: } 71: } 72: } 73: if (!emptyqueue(q)) { /* Re-insert stuff queued by ins_string */ 74: if (!ins_queue(ep, &q, &q)) 75: return No; 76: where += spwhere; 77: spwhere = No; 78: } 79: if (!emptyqueue(qf)) { /* Re-insert deleted old focus */ 80: firstmarked(&ep->focus, 1) || Abort(); 81: fixfocus(ep, where); 82: if (!ins_queue(ep, &qf, &qf)) 83: return No; 84: } 85: firstmarked(&ep->focus, 1) || Abort(); 86: unmkpath(&ep->focus, 1); 87: ep->spflag = No; 88: fixfocus(ep, where + spwhere); 89: } 90: return Yes; 91: } 92: 93: 94: /* 95: * Insert a newline. 96: */ 97: 98: Visible bool 99: ins_newline(ep) 100: register environ *ep; 101: { 102: register node n; 103: register int sym; 104: auto bool mayindent; 105: 106: ep->changed = Yes; 107: if (!fiddle(ep, &mayindent)) 108: return No; 109: for (;;) { 110: switch (ep->mode) { 111: 112: case VHOLE: 113: ep->mode = ATEND; 114: continue; 115: 116: case FHOLE: 117: ep->s2 = lenitem(ep); 118: if (!fix_move(ep)) 119: return No; 120: continue; 121: 122: case ATEND: 123: if (!joinstring(&ep->focus, "\n", No, 0, mayindent)) { 124: if (!move_on(ep)) 125: return No; 126: continue; 127: } 128: s_downi(ep, 2); 129: s_downi(ep, 1); 130: ep->mode = WHOLE; 131: Assert((sym = symbol(tree(ep->focus))) == Hole || sym == Optional); 132: return Yes; 133: 134: case ATBEGIN: 135: n = tree(ep->focus); 136: if (Type(n) == Tex) { 137: ep->mode = ATEND; 138: continue; 139: } 140: sym = symbol(n); 141: if (sym == Hole || sym == Optional) { 142: ep->mode = WHOLE; 143: continue; 144: } 145: n = nodecopy(n); 146: if (!fitstring(&ep->focus, "\n", 0)) { 147: if (!down(&ep->focus)) 148: ep->mode = ATEND; 149: noderelease(n); 150: continue; 151: } 152: s_downrite(ep); 153: if (fitnode(&ep->focus, n)) { 154: noderelease(n); 155: s_up(ep); 156: s_down(ep); 157: ep->mode = WHOLE; 158: return Yes; 159: } 160: s_up(ep); 161: s_down(ep); 162: if (!fitnode(&ep->focus, n)) { 163: noderelease(n); 164: #ifndef NDEBUG 165: debug("[Sorry, I don't see how to insert a newline here]"); 166: #endif NDEBUG 167: return No; 168: } 169: noderelease(n); 170: ep->mode = ATBEGIN; 171: return Yes; 172: 173: case WHOLE: 174: Assert((sym = symbol(tree(ep->focus))) == Hole || sym == Optional); 175: if (!fitstring(&ep->focus, "\n", 0)) { 176: ep->mode = ATEND; 177: continue; 178: } 179: s_downi(ep, 1); 180: Assert((sym = symbol(tree(ep->focus))) == Hole || sym == Optional); 181: ep->mode = WHOLE; 182: return Yes; 183: 184: default: 185: Abort(); 186: 187: } 188: } 189: } 190: 191: 192: /* 193: * Refinement for ins_newline() to do the initial processing. 194: */ 195: 196: Hidden bool 197: fiddle(ep, pmayindent) 198: register environ *ep; 199: bool *pmayindent; 200: { 201: register int level; 202: auto string str = ""; 203: 204: higher(ep); 205: while (rnarrow(ep)) 206: ; 207: fixit(ep); 208: soften(ep, &str, 0); 209: higher(ep); 210: *pmayindent = Yes; 211: if (atdedent(ep)) { 212: *pmayindent = No; 213: s_up(ep); 214: level = Level(ep->focus); 215: delfocus(&ep->focus); 216: if (symbol(tree(ep->focus)) == Hole) { 217: if (hackhack(ep)) 218: return Yes; 219: } 220: while (Level(ep->focus) >= level) { 221: if (!nexthole(ep)) { 222: ep->mode = ATEND; 223: break; 224: } 225: } 226: if (ep->mode == ATEND) { 227: leftvhole(ep); 228: ep->mode = ATEND; 229: while (Level(ep->focus) >= level) { 230: if (!up(&ep->focus)) 231: return No; 232: } 233: } 234: return Yes; 235: } 236: return Yes; 237: } 238: 239: 240: /* 241: * "Hier komen de houthakkers." 242: * 243: * Incredibly ugly hack to delete a join whose second child begins with \n, 244: * such as a suite after an IF, FOR or WHILE or unit heading. 245: * Inspects the parent node. 246: * If this has rp[0] ands rp[1] both empty, replace it by its first child. 247: * (caller assures this makes sense). 248: * Return Yes if this happened AND rp[1] contained a \t. 249: */ 250: 251: Hidden Procedure 252: hackhack(ep) 253: environ *ep; 254: { 255: node n; 256: int ich = ichild(ep->focus); 257: string *rp; 258: 259: if (!up(&ep->focus)) 260: return No; 261: higher(ep); 262: rp = noderepr(tree(ep->focus)); 263: if (!Fw_zero(rp[0]) || !Fw_zero(rp[1])) { 264: s_downi(ep, ich); 265: return No; 266: } 267: n = nodecopy(firstchild(tree(ep->focus))); 268: delfocus(&ep->focus); 269: replace(&ep->focus, n); 270: ep->mode = ATEND; 271: return rp[1] && rp[1][0] == '\t'; 272: } 273: 274: 275: /* 276: * Refinement for fiddle() to find out whether we are at a possible 277: * decrease-indentation position. 278: */ 279: 280: Hidden bool 281: atdedent(ep) 282: register environ *ep; 283: { 284: register path pa; 285: register node npa; 286: register int i; 287: register int sym = symbol(tree(ep->focus)); 288: 289: if (sym != Hole && sym != Optional) 290: return No; 291: if (ichild(ep->focus) != 1) 292: return No; 293: switch (ep->mode) { 294: case FHOLE: 295: if (ep->s1 != 1 || ep->s2 != 0) 296: return No; 297: break; 298: case ATBEGIN: 299: case WHOLE: 300: case SUBSET: 301: break; 302: default: 303: return No; 304: } 305: pa = parent(ep->focus); 306: if (!pa) 307: return No; 308: npa = tree(pa); 309: if (fwidth(noderepr(npa)[0]) >= 0) 310: return No; 311: for (i = nchildren(npa); i > 1; --i) { 312: sym = symbol(child(npa, i)); 313: if (sym != Hole && sym != Optional) 314: return No; 315: } 316: return Yes; /* Sigh! */ 317: } 318: 319: /* 320: * Refinement for ins_node() and fiddle() to find the next hole, 321: * skipping blank space only. 322: */ 323: 324: Hidden bool 325: nexthole(ep) 326: register environ *ep; 327: { 328: register node n; 329: register int ich; 330: register string repr; 331: 332: do { 333: ich = ichild(ep->focus); 334: if (!up(&ep->focus)) 335: return No; 336: higher(ep); 337: n = tree(ep->focus); 338: repr = noderepr(n)[ich]; 339: if (!Fw_zero(repr) && !allspaces(repr)) 340: return No; 341: } while (ich >= nchildren(n)); 342: s_downi(ep, ich+1); 343: return Yes; 344: }