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