Annotation of 43BSDReno/contrib/jove/paragraph.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.