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

Defined functions

BackPara defined in line 513; used 2 times
DoJustify defined in line 413; used 4 times
DoPara defined in line 483; used 2 times
ForPara defined in line 518; used 2 times
Justify defined in line 305; used 2 times
RegJustify defined in line 333; used 2 times
do_rfill defined in line 361; used 1 times
do_space defined in line 374; used 1 times
find_para defined in line 198; used 3 times
get_indent defined in line 140; used 15 times
i_blank defined in line 134; used 6 times
i_bsblank defined in line 126; used 4 times
max_line defined in line 313; used 1 times
min_line defined in line 323; used 1 times
tailrule defined in line 174; used 1 times

Defined variables

RMargin defined in line 110; used 4 times
body_indent defined in line 115; used 4 times
bslash defined in line 123; used 5 times
head_indent defined in line 114; used 2 times
para_head declared in line 480; defined in line 112; used 5 times
para_tail declared in line 481; defined in line 113; used 6 times
use_lmargin defined in line 116; used 8 times

Defined macros

I_BUFEDGE defined in line 121; used 1 times
I_EMPTY defined in line 119; used 1 times
I_PERIOD defined in line 120; used 2 times
Last modified: 1986-03-28
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1892
Valid CSS Valid XHTML 1.0 Strict