Annotation of 43BSDReno/contrib/jove/paragraph.c, revision 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.