|
|
1.1 ! root 1: /************************************************************************* ! 2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is * ! 3: * provided to you without charge for use only on a licensed Unix * ! 4: * system. You may copy JOVE provided that this notice is included with * ! 5: * the copy. You may not sell copies of this program or versions * ! 6: * modified for use on microcomputer systems, unless the copies are * ! 7: * included with a Unix system distribution and the source is provided. * ! 8: *************************************************************************/ ! 9: ! 10: #include "jove.h" ! 11: ! 12: /* Thanks to Brian Harvey for this paragraph boundery finding algorithm. ! 13: It's really quite hairy figuring it out. This deals with paragraphs that ! 14: are seperated by blank lines, lines beginning with a Period (assumed to ! 15: be an nroff command), lines beginning with BackSlash (assumed to be Tex ! 16: commands). Also handles paragraphs that are separated by lines of ! 17: different indent; and it deals with outdented paragraphs, too. It's ! 18: really quite nice. Here's Brian's algorithm. ! 19: ! 20: Definitions: ! 21: ! 22: THIS means the line containing the cursor. ! 23: PREV means the line above THIS. ! 24: NEXT means the line below THIS. ! 25: ! 26: BLANK means empty, empty except for spaces and tabs, starts with a period ! 27: or a backslash, or nonexistent (because the edge of the buffer is ! 28: reached). ((BH 12/24/85 A line starting with backslash is blank only if ! 29: the following line also starts with backslash. This is so that \noindent ! 30: is part of a paragraph, but long strings of TeX commands don't get ! 31: rearranged. It still isn't perfect but it's better.)) ! 32: ! 33: BSBLANK means BLANK or starts with a backslash. (BH 12/24/85) ! 34: ! 35: HEAD means the first (nonblank) line of the paragraph containing THIS. ! 36: BODY means all other (nonblank) lines of the paragraph. ! 37: TAIL means the last (nb) line of the paragraph. (TAIL is part of BODY.) ! 38: ! 39: HEAD INDENT means the indentation of HEAD. M-J should preserve this. ! 40: BODY INDENT means the indentation of BODY. Ditto. ! 41: ! 42: Subprocedures: ! 43: ! 44: TAILRULE(BODYLINE) ! 45: If BODYLINE is BLANK, the paragraph has only one line, and there is no ! 46: BODY and therefore no TAIL. Return. Otherwise, starting from BODYLINE, ! 47: move down until you find a line that either is BSBLANK or has a different ! 48: indentation from BODYLINE. The line above that different line is TAIL. ! 49: Return. ! 50: ! 51: Rules: ! 52: ! 53: 1. If THIS is BLANK, which command are you doing? If M-J or M-[, then go ! 54: up to the first non-BLANK line and start over. (If there is no non-BLANK ! 55: line before THIS, ring the bell.) If M-], then the first non-BLANK line ! 56: below THIS is HEAD, and the second consecutive non-BSBLANK line (if any) is ! 57: the beginning of BODY. (If there is no non-BLANK line after THIS, ring ! 58: the bell.) Do TAILRULE(beginning-of-BODY). Go to rule A. ! 59: ! 60: 2. If PREV is BLANK or THIS is BSBLANK, then THIS is HEAD, and NEXT (if ! 61: not BSBLANK) is in BODY. Do TAILRULE(NEXT). Go to rule A. ! 62: ! 63: 3. If NEXT is BSBLANK, then THIS is TAIL, therefore part of BODY. Go to ! 64: rule 5 to find HEAD. ! 65: ! 66: 4. If either NEXT or PREV has the same indentation as THIS, then THIS is ! 67: part of BODY. Do TAILRULE(THIS). Go to rule 5 to find HEAD. Otherwise, ! 68: go to rule 6. ! 69: ! 70: 5. Go up until you find a line that is either BSBLANK or has a different ! 71: indentation from THIS. If that line is BLANK, the line below it is HEAD. ! 72: If that line is non-BLANK, then call that new line THIS for what follows. ! 73: If (the new) PREV has the same indent as THIS, then (the new) NEXT is ! 74: HEAD. If PREV has a different indent from THIS, then THIS is HEAD. Go to ! 75: rule A. ! 76: ! 77: 6. If you got here, then both NEXT and PREV are nonblank and are ! 78: differently indented from THIS. This is a tricky case and there is no ! 79: guarantee that you're going to win. The most straightforward thing to do ! 80: is assume that we are not using hanging indentation. In that case: ! 81: whichever of PREV and THIS is indented further is HEAD. Do ! 82: TAILRULE(HEAD+1). Go to rule A. ! 83: ! 84: 6+. A more complicated variant would be this: if THIS is indented further ! 85: than PREV, we are using regular indentation and rule 6 applies. If PREV ! 86: is indented further than THIS, look at both NEXT and the line after NEXT. ! 87: If those two lines are indented equally, and more than THIS, then we are ! 88: using hanging indent, THIS is HEAD, and NEXT is the first line of BODY. ! 89: Do TAILRULE(NEXT). Otherwise, rule 6 applies. ! 90: ! 91: A. You now know where HEAD and TAIL are. The indentation of HEAD is HEAD ! 92: INDENT; the indentation of TAIL is BODY INDENT. ! 93: ! 94: B. If you are trying to M-J, you are now ready to do it. ! 95: ! 96: C. If you are trying to M-], leave point after the newline that ends ! 97: TAIL. In other words, leave the cursor at the beginning of the line ! 98: after TAIL. It is not possible for this to leave point where it started ! 99: unless it was already at the end of the buffer. ! 100: ! 101: D. If you are trying to M-[, if the line before HEAD is not BLANK, then ! 102: leave point just before HEAD. That is, leave the cursor at the beginning ! 103: of HEAD. If the line before HEAD is BLANK, then leave the cursor at the ! 104: beginning of that line. If the cursor didn't move, go up to the first ! 105: earlier non-BLANK line and start over. ! 106: ! 107: ! 108: End of Algorithm. I implemented rule 6+ because it seemed nicer. */ ! 109: ! 110: int RMargin = 78, ! 111: LMargin = 0; ! 112: Line *para_head, ! 113: *para_tail; ! 114: int head_indent, ! 115: body_indent; ! 116: static int use_lmargin; ! 117: ! 118: /* some defines for paragraph boundery checking */ ! 119: #define I_EMPTY -1 /* line "looks" empty (spaces and tabs) */ ! 120: #define I_PERIOD -2 /* line begins with "." or "\" */ ! 121: #define I_BUFEDGE -3 /* line is nonexistent (edge of buffer) */ ! 122: ! 123: static int bslash; /* Nonzero if get_indent finds line starting ! 124: with backslash */ ! 125: ! 126: i_bsblank(lp) ! 127: Line *lp; ! 128: { ! 129: if (i_blank(lp)) ! 130: return 1; ! 131: return bslash; ! 132: } ! 133: ! 134: i_blank(lp) ! 135: Line *lp; ! 136: { ! 137: return (get_indent(lp) < 0); ! 138: } ! 139: ! 140: static ! 141: get_indent(lp) ! 142: register Line *lp; ! 143: { ! 144: Bufpos save; ! 145: register int indent; ! 146: ! 147: bslash = 0; ! 148: if (lp == 0) ! 149: return I_BUFEDGE; ! 150: DOTsave(&save); ! 151: SetLine(lp); ! 152: if (blnkp(linebuf)) ! 153: indent = I_EMPTY; ! 154: else if (linebuf[0] == '.') ! 155: indent = I_PERIOD; ! 156: else if (linebuf[0] == '\\') { ! 157: /* BH 12/24/85. Backslash is BLANK only if next line ! 158: also starts with Backslash. */ ! 159: bslash++; ! 160: SetLine(lp->l_next); ! 161: if (linebuf[0] == '\\') ! 162: indent = I_PERIOD; ! 163: else ! 164: indent = 0; ! 165: } else { ! 166: ToIndent(); ! 167: indent = calc_pos(linebuf, curchar); ! 168: } ! 169: SetDot(&save); ! 170: ! 171: return indent; ! 172: } ! 173: ! 174: static Line * ! 175: tailrule(lp) ! 176: register Line *lp; ! 177: { ! 178: int i; ! 179: ! 180: i = get_indent(lp); ! 181: if (i < 0) ! 182: return lp; /* one line paragraph */ ! 183: do { ! 184: if ((get_indent(lp->l_next) != i) || bslash) ! 185: /* BH line with backslash is head of next para */ ! 186: break; ! 187: } while ((lp = lp->l_next) != 0); ! 188: if (lp == 0) ! 189: complain((char *) 0); ! 190: return lp; ! 191: } ! 192: ! 193: /* Finds the beginning, end and indent of the current paragraph, and sets ! 194: the above global variables. HOW says how to behave when we're between ! 195: paragraphs. That is, it's either FORWARD or BACKWARD depending on which ! 196: way we're favoring. */ ! 197: ! 198: find_para(how) ! 199: { ! 200: Line *this, ! 201: *prev, ! 202: *next, ! 203: *head = 0, ! 204: *body = 0, ! 205: *tail = 0; ! 206: int this_indent; ! 207: Bufpos orig; /* remember where we were when we started */ ! 208: ! 209: exp = 1; ! 210: DOTsave(&orig); ! 211: strt: ! 212: this = curline; ! 213: prev = curline->l_prev; ! 214: next = curline->l_next; ! 215: this_indent = get_indent(this); ! 216: ! 217: if (i_blank(this)) { /* rule 1 */ ! 218: if (how == BACKWARD) { ! 219: while (i_blank(curline)) ! 220: if (firstp(curline)) ! 221: complain((char *) 0); ! 222: else ! 223: line_move(BACKWARD, NO); ! 224: goto strt; ! 225: } else { ! 226: while (i_blank(curline)) ! 227: if (lastp(curline)) ! 228: complain((char *) 0); ! 229: else ! 230: line_move(FORWARD, NO); ! 231: head = curline; ! 232: next = curline->l_next; ! 233: if (!i_bsblank(next)) ! 234: body = next; ! 235: else ! 236: body = head; ! 237: } ! 238: } else if (i_bsblank(this) || i_blank(prev)) { /* rule 2 */ ! 239: head = this; ! 240: if (!i_bsblank(next)) ! 241: body = next; ! 242: } else if (i_bsblank(next)) { /* rule 3 */ ! 243: tail = this; ! 244: body = this; ! 245: } else if ((get_indent(next) == this_indent) || /* rule 4 */ ! 246: (get_indent(prev) == this_indent)) ! 247: body = this; ! 248: else { /* rule 6+ */ ! 249: if (get_indent(prev) > this_indent) { ! 250: /* hanging indent maybe? */ ! 251: if ((next != 0) && ! 252: (get_indent(next) == get_indent(next->l_next))) { ! 253: head = this; ! 254: body = next; ! 255: } ! 256: } ! 257: /* Now we handle hanging indent else and the other ! 258: case of this_indent > get_indent(prev). That is, ! 259: if we didn't resolve HEAD in the above if, then ! 260: we are not a hanging indent. */ ! 261: if (head == 0) { /* still don't know */ ! 262: if (this_indent > get_indent(prev)) ! 263: head = this; ! 264: else ! 265: head = prev; ! 266: body = head->l_next; ! 267: } ! 268: } ! 269: /* rule 5 -- find the missing parts */ ! 270: if (head == 0) { /* haven't found head of paragraph so do so now */ ! 271: Line *lp; ! 272: int i; ! 273: ! 274: lp = this; ! 275: do { ! 276: i = get_indent(lp->l_prev); ! 277: if (i < 0) /* is blank */ ! 278: head = lp; ! 279: else if (i != this_indent || bslash) { ! 280: Line *this = lp->l_prev; ! 281: ! 282: if (get_indent(this->l_prev) == i) ! 283: head = this->l_next; ! 284: else ! 285: head = this; ! 286: } ! 287: } while (head == 0 && (lp = lp->l_prev) != 0); ! 288: if (lp == 0) ! 289: complain((char *) 0); ! 290: } ! 291: if (body == 0) /* this must be a one line paragraph */ ! 292: body = head; ! 293: if (tail == 0) ! 294: tail = tailrule(body); ! 295: if (tail == 0 || head == 0 || body == 0) ! 296: complain("BUG! tail(%d),head(%d),body(%d)!", tail, head, body); ! 297: para_head = head; ! 298: para_tail = tail; ! 299: head_indent = get_indent(head); ! 300: body_indent = get_indent(body); ! 301: ! 302: SetDot(&orig); ! 303: } ! 304: ! 305: Justify() ! 306: { ! 307: use_lmargin = (exp_p != 0); ! 308: find_para(BACKWARD); ! 309: DoJustify(para_head, 0, para_tail, length(para_tail), NO, ! 310: use_lmargin ? LMargin : body_indent); ! 311: } ! 312: ! 313: Line * ! 314: max_line(l1, l2) ! 315: Line *l1, ! 316: *l2; ! 317: { ! 318: if (inorder(l1, 0, l2, 0)) ! 319: return l2; ! 320: return l1; ! 321: } ! 322: ! 323: Line * ! 324: min_line(l1, l2) ! 325: Line *l1, ! 326: *l2; ! 327: { ! 328: if (inorder(l1, 0, l2, 0)) ! 329: return l1; ! 330: return l2; ! 331: } ! 332: ! 333: RegJustify() ! 334: { ! 335: Mark *mp = CurMark(), ! 336: *tailmark; ! 337: Line *l1 = curline, ! 338: *l2 = mp->m_line; ! 339: int c1 = curchar, ! 340: c2 = mp->m_char; ! 341: Line *rl1, ! 342: *rl2; ! 343: ! 344: use_lmargin = (exp_p != 0); ! 345: (void) fixorder(&l1, &c1, &l2, &c2); ! 346: do { ! 347: DotTo(l1, c1); ! 348: find_para(FORWARD); ! 349: rl1 = max_line(l1, para_head); ! 350: rl2 = min_line(l2, para_tail); ! 351: tailmark = MakeMark(para_tail, 0, FLOATER); ! 352: DoJustify(rl1, (rl1 == l1) ? c1 : 0, rl2, ! 353: (rl2 == l2) ? c2 : length(rl2), ! 354: NO, use_lmargin ? LMargin : body_indent); ! 355: l1 = tailmark->m_line->l_next; ! 356: DelMark(tailmark); ! 357: c1 = 0; ! 358: } while (l1 != 0 && l2 != rl2); ! 359: } ! 360: ! 361: do_rfill() ! 362: { ! 363: Mark *mp = CurMark(); ! 364: Line *l1 = curline, ! 365: *l2 = mp->m_line; ! 366: int c1 = curchar, ! 367: c2 = mp->m_char; ! 368: ! 369: use_lmargin = (exp_p != 0); ! 370: (void) fixorder(&l1, &c1, &l2, &c2); ! 371: DoJustify(l1, c1, l2, c2, NO, use_lmargin ? LMargin : 0); ! 372: } ! 373: ! 374: do_space() ! 375: { ! 376: int c1 = curchar, ! 377: c2 = c1, ! 378: diff, ! 379: nspace; ! 380: char ch; ! 381: ! 382: while (c1 > 0 && ((ch = linebuf[c1 - 1]) == ' ' || ch == '\t')) ! 383: c1--; ! 384: while ((ch = linebuf[c2]) == ' ' || ch == '\t') ! 385: c2++; ! 386: diff = (c2 - c1); ! 387: curchar = c2; ! 388: ! 389: if (diff == 0) ! 390: return; ! 391: if (c1 > 0) { ! 392: int topunct = c1 - 1; ! 393: ! 394: nspace = 1; ! 395: if (diff >= 2) { ! 396: while (index("\")]", linebuf[topunct])) { ! 397: if (topunct == 0) ! 398: break; ! 399: topunct--; ! 400: } ! 401: if (index("?!.:", linebuf[topunct])) ! 402: nspace = 2; ! 403: } ! 404: } else ! 405: nspace = 0; ! 406: ! 407: if (diff > nspace) ! 408: DoTimes(DelPChar(), (diff - nspace)); ! 409: else if (diff < nspace) ! 410: DoTimes(Insert(' '), (nspace - diff)); ! 411: } ! 412: ! 413: DoJustify(l1, c1, l2, c2, scrunch, indent) ! 414: Line *l1, ! 415: *l2; ! 416: { ! 417: int okay_char = -1; ! 418: char *cp; ! 419: Mark *savedot = MakeMark(curline, curchar, FLOATER), ! 420: *endmark; ! 421: ! 422: exp = 1; ! 423: (void) fixorder(&l1, &c1, &l2, &c2); /* l1/c1 will be before l2/c2 */ ! 424: DotTo(l1, c1); ! 425: if (get_indent(l1) >= c1) { ! 426: if (use_lmargin) { ! 427: DelWtSpace(); ! 428: n_indent(indent + (head_indent - body_indent)); ! 429: use_lmargin = 0; /* turn this off now */ ! 430: } ! 431: ToIndent(); ! 432: } ! 433: endmark = MakeMark(l2, c2, FLOATER); ! 434: ! 435: for (;;) { ! 436: while (calc_pos(linebuf, curchar) < RMargin) { ! 437: if (curline == endmark->m_line && curchar >= endmark->m_char) ! 438: goto outahere; ! 439: okay_char = curchar; ! 440: if (eolp()) { ! 441: DelNChar(); /* Delete line separator. */ ! 442: ins_str(" ", NO); ! 443: } else { ! 444: cp = StrIndex(1, linebuf, curchar + 1, ' '); ! 445: if (cp == 0) ! 446: Eol(); ! 447: else ! 448: curchar = (cp - linebuf); ! 449: } ! 450: do_space(); ! 451: } ! 452: if (okay_char > 0) ! 453: curchar = okay_char; ! 454: if (curline == endmark->m_line && curchar >= endmark->m_char) ! 455: goto outahere; ! 456: ! 457: /* Can't fit in small margin, so we do the best we can. */ ! 458: if (eolp()) { ! 459: line_move(FORWARD, NO); ! 460: DelWtSpace(); ! 461: n_indent(indent); ! 462: } else { ! 463: DelWtSpace(); ! 464: LineInsert(); ! 465: if (scrunch && TwoBlank()) { ! 466: Eol(); ! 467: DelNChar(); ! 468: } ! 469: n_indent(indent); ! 470: } ! 471: } ! 472: outahere: ! 473: ToMark(savedot); /* Back to where we were */ ! 474: DelMark(endmark); /* Free up marks */ ! 475: DelMark(savedot); ! 476: this_cmd = last_cmd = 0; /* So everything is under control */ ! 477: f_mess(""); ! 478: } ! 479: ! 480: extern Line *para_head, ! 481: *para_tail; ! 482: ! 483: DoPara(dir) ! 484: { ! 485: register int num = exp, ! 486: first_time = TRUE; ! 487: ! 488: while (--num >= 0) { ! 489: tryagain: find_para(dir); /* find paragraph bounderies */ ! 490: if ((dir == BACKWARD) && ! 491: ((!first_time) || ((para_head == curline) && bolp()))) { ! 492: if (bobp()) ! 493: complain((char *) 0); ! 494: BackChar(); ! 495: first_time = !first_time; ! 496: goto tryagain; ! 497: } ! 498: SetLine((dir == BACKWARD) ? para_head : para_tail); ! 499: if (dir == BACKWARD && !firstp(curline) && ! 500: i_blank(curline->l_prev)) ! 501: line_move(BACKWARD, NO); ! 502: else if (dir == FORWARD) { ! 503: if (lastp(curline)) { ! 504: Eol(); ! 505: break; ! 506: } ! 507: /* otherwise */ ! 508: line_move(FORWARD, NO); ! 509: } ! 510: } ! 511: } ! 512: ! 513: BackPara() ! 514: { ! 515: DoPara(BACKWARD); ! 516: } ! 517: ! 518: ForPara() ! 519: { ! 520: DoPara(FORWARD); ! 521: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.