|
|
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.