|
|
1.1 root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.