|
|
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:
10: #ifdef MAC
11: # undef private
12: # define private
13: #endif
14:
15: #ifdef LINT_ARGS
16: private int get_indent(Line *);
17: private Line * tailrule(Line *);
18: #else
19: private int get_indent();
20: private Line * tailrule();
21: #endif
22:
23: #ifdef MAC
24: # undef private
25: # define private static
26: #endif
27:
28: /* Thanks to Brian Harvey for this paragraph boundery finding algorithm.
29: It's really quite hairy figuring it out. This deals with paragraphs that
30: are seperated by blank lines, lines beginning with a Period (assumed to
31: be an nroff command), lines beginning with BackSlash (assumed to be Tex
32: commands). Also handles paragraphs that are separated by lines of
33: different indent; and it deals with outdented paragraphs, too. It's
34: really quite nice. Here's Brian's algorithm.
35:
36: Definitions:
37:
38: THIS means the line containing the cursor.
39: PREV means the line above THIS.
40: NEXT means the line below THIS.
41:
42: BLANK means empty, empty except for spaces and tabs, starts with a period
43: or a backslash, or nonexistent (because the edge of the buffer is
44: reached). ((BH 12/24/85 A line starting with backslash is blank only if
45: the following line also starts with backslash. This is so that \noindent
46: is part of a paragraph, but long strings of TeX commands don't get
47: rearranged. It still isn't perfect but it's better.))
48:
49: BSBLANK means BLANK or starts with a backslash. (BH 12/24/85)
50:
51: HEAD means the first (nonblank) line of the paragraph containing THIS.
52: BODY means all other (nonblank) lines of the paragraph.
53: TAIL means the last (nb) line of the paragraph. (TAIL is part of BODY.)
54:
55: HEAD INDENT means the indentation of HEAD. M-J should preserve this.
56: BODY INDENT means the indentation of BODY. Ditto.
57:
58: Subprocedures:
59:
60: TAILRULE(BODYLINE)
61: If BODYLINE is BLANK, the paragraph has only one line, and there is no
62: BODY and therefore no TAIL. Return. Otherwise, starting from BODYLINE,
63: move down until you find a line that either is BSBLANK or has a different
64: indentation from BODYLINE. The line above that different line is TAIL.
65: Return.
66:
67: Rules:
68:
69: 1. If THIS is BLANK, which command are you doing? If M-J or M-[, then go
70: up to the first non-BLANK line and start over. (If there is no non-BLANK
71: line before THIS, ring the bell.) If M-], then the first non-BLANK line
72: below THIS is HEAD, and the second consecutive non-BSBLANK line (if any) is
73: the beginning of BODY. (If there is no non-BLANK line after THIS, ring
74: the bell.) Do TAILRULE(beginning-of-BODY). Go to rule A.
75:
76: 2. If PREV is BLANK or THIS is BSBLANK, then THIS is HEAD, and NEXT (if
77: not BSBLANK) is in BODY. Do TAILRULE(NEXT). Go to rule A.
78:
79: 3. If NEXT is BSBLANK, then THIS is TAIL, therefore part of BODY. Go to
80: rule 5 to find HEAD.
81:
82: 4. If either NEXT or PREV has the same indentation as THIS, then THIS is
83: part of BODY. Do TAILRULE(THIS). Go to rule 5 to find HEAD. Otherwise,
84: go to rule 6.
85:
86: 5. Go up until you find a line that is either BSBLANK or has a different
87: indentation from THIS. If that line is BLANK, the line below it is HEAD.
88: If that line is non-BLANK, then call that new line THIS for what follows.
89: If (the new) PREV has the same indent as THIS, then (the new) NEXT is
90: HEAD. If PREV has a different indent from THIS, then THIS is HEAD. Go to
91: rule A.
92:
93: 6. If you got here, then both NEXT and PREV are nonblank and are
94: differently indented from THIS. This is a tricky case and there is no
95: guarantee that you're going to win. The most straightforward thing to do
96: is assume that we are not using hanging indentation. In that case:
97: whichever of PREV and THIS is indented further is HEAD. Do
98: TAILRULE(HEAD+1). Go to rule A.
99:
100: 6+. A more complicated variant would be this: if THIS is indented further
101: than PREV, we are using regular indentation and rule 6 applies. If PREV
102: is indented further than THIS, look at both NEXT and the line after NEXT.
103: If those two lines are indented equally, and more than THIS, then we are
104: using hanging indent, THIS is HEAD, and NEXT is the first line of BODY.
105: Do TAILRULE(NEXT). Otherwise, rule 6 applies.
106:
107: A. You now know where HEAD and TAIL are. The indentation of HEAD is HEAD
108: INDENT; the indentation of TAIL is BODY INDENT.
109:
110: B. If you are trying to M-J, you are now ready to do it.
111:
112: C. If you are trying to M-], leave point after the newline that ends
113: TAIL. In other words, leave the cursor at the beginning of the line
114: after TAIL. It is not possible for this to leave point where it started
115: unless it was already at the end of the buffer.
116:
117: D. If you are trying to M-[, if the line before HEAD is not BLANK, then
118: leave point just before HEAD. That is, leave the cursor at the beginning
119: of HEAD. If the line before HEAD is BLANK, then leave the cursor at the
120: beginning of that line. If the cursor didn't move, go up to the first
121: earlier non-BLANK line and start over.
122:
123:
124: End of Algorithm. I implemented rule 6+ because it seemed nicer. */
125:
126: int RMargin = 78,
127: LMargin = 0;
128: Line *para_head,
129: *para_tail;
130: int head_indent,
131: body_indent;
132: static int use_lmargin;
133:
134: /* some defines for paragraph boundery checking */
135: #define I_EMPTY -1 /* line "looks" empty (spaces and tabs) */
136: #define I_PERIOD -2 /* line begins with "." or "\" */
137: #define I_BUFEDGE -3 /* line is nonexistent (edge of buffer) */
138:
139: static int bslash; /* Nonzero if get_indent finds line starting
140: with backslash */
141:
142: int
143: i_bsblank(lp)
144: Line *lp;
145: {
146: if (i_blank(lp))
147: return 1;
148: return bslash;
149: }
150:
151: int
152: i_blank(lp)
153: Line *lp;
154: {
155: return (get_indent(lp) < 0);
156: }
157:
158: private int
159: get_indent(lp)
160: register Line *lp;
161: {
162: Bufpos save;
163: register int indent;
164:
165: bslash = 0;
166: if (lp == 0)
167: return I_BUFEDGE;
168: DOTsave(&save);
169: SetLine(lp);
170: if (blnkp(linebuf))
171: indent = I_EMPTY;
172: else if (linebuf[0] == '.')
173: indent = I_PERIOD;
174: else if (linebuf[0] == '\\') {
175: /* BH 12/24/85. Backslash is BLANK only if next line
176: also starts with Backslash. */
177: bslash += 1;
178: SetLine(lp->l_next);
179: if (linebuf[0] == '\\')
180: indent = I_PERIOD;
181: else
182: indent = 0;
183: } else {
184: ToIndent();
185: indent = calc_pos(linebuf, curchar);
186: }
187: SetDot(&save);
188:
189: return indent;
190: }
191:
192: private Line *
193: tailrule(lp)
194: register Line *lp;
195: {
196: int i;
197:
198: i = get_indent(lp);
199: if (i < 0)
200: return lp; /* one line paragraph */
201: do {
202: if ((get_indent(lp->l_next) != i) || bslash)
203: /* BH line with backslash is head of next para */
204: break;
205: } while ((lp = lp->l_next) != 0);
206: if (lp == 0)
207: complain((char *) 0);
208: return lp;
209: }
210:
211: /* Finds the beginning, end and indent of the current paragraph, and sets
212: the above global variables. HOW says how to behave when we're between
213: paragraphs. That is, it's either FORWARD or BACKWARD depending on which
214: way we're favoring. */
215:
216: void
217: find_para(how)
218: {
219: Line *this,
220: *prev,
221: *next,
222: *head = 0,
223: *body = 0,
224: *tail = 0;
225: int this_indent;
226: Bufpos orig; /* remember where we were when we started */
227:
228: DOTsave(&orig);
229: strt:
230: this = curline;
231: prev = curline->l_prev;
232: next = curline->l_next;
233: this_indent = get_indent(this);
234:
235: if (i_blank(this)) { /* rule 1 */
236: if (how == BACKWARD) {
237: while (i_blank(curline))
238: if (firstp(curline))
239: complain((char *) 0);
240: else
241: line_move(BACKWARD, 1, NO);
242: goto strt;
243: } else {
244: while (i_blank(curline))
245: if (lastp(curline))
246: complain((char *) 0);
247: else
248: line_move(FORWARD, 1, NO);
249: head = curline;
250: next = curline->l_next;
251: if (!i_bsblank(next))
252: body = next;
253: else
254: body = head;
255: }
256: } else if (i_bsblank(this) || i_blank(prev)) { /* rule 2 */
257: head = this;
258: if (!i_bsblank(next))
259: body = next;
260: } else if (i_bsblank(next)) { /* rule 3 */
261: tail = this;
262: body = this;
263: } else if ((get_indent(next) == this_indent) || /* rule 4 */
264: (get_indent(prev) == this_indent))
265: body = this;
266: else { /* rule 6+ */
267: if (get_indent(prev) > this_indent) {
268: /* hanging indent maybe? */
269: if ((next != 0) &&
270: (get_indent(next) == get_indent(next->l_next))) {
271: head = this;
272: body = next;
273: }
274: }
275: /* Now we handle hanging indent else and the other
276: case of this_indent > get_indent(prev). That is,
277: if we didn't resolve HEAD in the above if, then
278: we are not a hanging indent. */
279: if (head == 0) { /* still don't know */
280: if (this_indent > get_indent(prev))
281: head = this;
282: else
283: head = prev;
284: body = head->l_next;
285: }
286: }
287: /* rule 5 -- find the missing parts */
288: if (head == 0) { /* haven't found head of paragraph so do so now */
289: Line *lp;
290: int i;
291:
292: lp = this;
293: do {
294: i = get_indent(lp->l_prev);
295: if (i < 0) /* is blank */
296: head = lp;
297: else if (i != this_indent || bslash) {
298: Line *this = lp->l_prev;
299:
300: if (get_indent(this->l_prev) == i)
301: head = this->l_next;
302: else
303: head = this;
304: }
305: } while (head == 0 && (lp = lp->l_prev) != 0);
306: if (lp == 0)
307: complain((char *) 0);
308: }
309: if (body == 0) /* this must be a one line paragraph */
310: body = head;
311: if (tail == 0)
312: tail = tailrule(body);
313: if (tail == 0 || head == 0 || body == 0)
314: complain("BUG! tail(%d),head(%d),body(%d)!", tail, head, body);
315: para_head = head;
316: para_tail = tail;
317: head_indent = get_indent(head);
318: body_indent = get_indent(body);
319:
320: SetDot(&orig);
321: }
322:
323: void
324: Justify()
325: {
326: use_lmargin = is_an_arg();
327: find_para(BACKWARD);
328: DoJustify(para_head, 0, para_tail, length(para_tail), NO,
329: use_lmargin ? LMargin : body_indent);
330: }
331:
332: Line *
333: max_line(l1, l2)
334: Line *l1,
335: *l2;
336: {
337: if (inorder(l1, 0, l2, 0))
338: return l2;
339: return l1;
340: }
341:
342: Line *
343: min_line(l1, l2)
344: Line *l1,
345: *l2;
346: {
347: if (inorder(l1, 0, l2, 0))
348: return l1;
349: return l2;
350: }
351:
352: void
353: RegJustify()
354: {
355: Mark *mp = CurMark(),
356: *tailmark;
357: Line *l1 = curline,
358: *l2 = mp->m_line;
359: int c1 = curchar,
360: c2 = mp->m_char;
361: Line *rl1,
362: *rl2;
363:
364: use_lmargin = is_an_arg();
365: (void) fixorder(&l1, &c1, &l2, &c2);
366: do {
367: DotTo(l1, c1);
368: find_para(FORWARD);
369: rl1 = max_line(l1, para_head);
370: rl2 = min_line(l2, para_tail);
371: tailmark = MakeMark(para_tail, 0, M_FLOATER);
372: DoJustify(rl1, (rl1 == l1) ? c1 : 0, rl2,
373: (rl2 == l2) ? c2 : length(rl2),
374: NO, use_lmargin ? LMargin : body_indent);
375: l1 = tailmark->m_line->l_next;
376: DelMark(tailmark);
377: c1 = 0;
378: } while (l1 != 0 && l2 != rl2);
379: }
380:
381: void
382: do_rfill(ulm)
383: {
384: Mark *mp = CurMark();
385: Line *l1 = curline,
386: *l2 = mp->m_line;
387: int c1 = curchar,
388: c2 = mp->m_char;
389:
390: use_lmargin = ulm;
391: (void) fixorder(&l1, &c1, &l2, &c2);
392: DoJustify(l1, c1, l2, c2, NO, use_lmargin ? LMargin : 0);
393: }
394:
395: void
396: do_space()
397: {
398: int c1 = curchar,
399: c2 = c1,
400: diff,
401: nspace;
402: char ch;
403:
404: while (c1 > 0 && ((ch = linebuf[c1 - 1]) == ' ' || ch == '\t'))
405: c1 -= 1;
406: while ((ch = linebuf[c2]) == ' ' || ch == '\t')
407: c2 += 1;
408: diff = (c2 - c1);
409: curchar = c2;
410:
411: if (diff == 0)
412: return;
413: if (c1 > 0) {
414: int topunct = c1 - 1;
415:
416: nspace = 1;
417: if (diff >= 2) {
418: while (index("\")]", linebuf[topunct])) {
419: if (topunct == 0)
420: break;
421: topunct -= 1;
422: }
423: if (index("?!.:", linebuf[topunct]))
424: nspace = 2;
425: }
426: } else
427: nspace = 0;
428:
429: if (diff > nspace)
430: del_char(BACKWARD, (diff - nspace));
431: else if (diff < nspace)
432: insert_c(' ', (nspace - diff));
433: }
434:
435: #ifdef MSDOS
436: /*#pragma loop_opt(off) */
437: #endif
438:
439: void
440: DoJustify(l1, c1, l2, c2, scrunch, indent)
441: Line *l1,
442: *l2;
443: {
444: int okay_char = -1;
445: char *cp;
446: Mark *savedot = MakeMark(curline, curchar, M_FLOATER),
447: *endmark;
448:
449: (void) fixorder(&l1, &c1, &l2, &c2); /* l1/c1 will be before l2/c2 */
450: DotTo(l1, c1);
451: if (get_indent(l1) >= c1) {
452: if (use_lmargin) {
453: n_indent(indent + (head_indent - body_indent));
454: use_lmargin = 0; /* turn this off now */
455: }
456: ToIndent();
457: }
458: endmark = MakeMark(l2, c2, M_FLOATER);
459:
460: for (;;) {
461: while (calc_pos(linebuf, curchar) < RMargin) {
462: if (curline == endmark->m_line && curchar >= endmark->m_char)
463: goto outahere;
464: okay_char = curchar;
465: if (eolp()) {
466: del_char(FORWARD, 1); /* Delete line separator. */
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 > 0)
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 we do the best we can. */
483: if (eolp()) {
484: line_move(FORWARD, 1, NO);
485: n_indent(indent);
486: } else {
487: DelWtSpace();
488: LineInsert(1);
489: if (scrunch && TwoBlank()) {
490: Eol();
491: del_char(FORWARD, 1);
492: }
493: n_indent(indent);
494: }
495: }
496: outahere:
497: ToMark(savedot); /* Back to where we were */
498: DelMark(endmark); /* Free up marks */
499: DelMark(savedot);
500: this_cmd = last_cmd = 0; /* So everything is under control */
501: f_mess("");
502: }
503:
504: #ifdef MSDOS
505: /*#pragma loop_opt() */
506: #endif
507:
508: extern Line *para_head,
509: *para_tail;
510:
511: void
512: DoPara(dir)
513: {
514: register int num = arg_value(),
515: first_time = TRUE;
516:
517: while (--num >= 0) {
518: tryagain: find_para(dir); /* find paragraph bounderies */
519: if ((dir == BACKWARD) &&
520: ((!first_time) || ((para_head == curline) && bolp()))) {
521: if (bobp())
522: complain((char *) 0);
523: b_char(1);
524: first_time = !first_time;
525: goto tryagain;
526: }
527: SetLine((dir == BACKWARD) ? para_head : para_tail);
528: if (dir == BACKWARD && !firstp(curline) &&
529: i_blank(curline->l_prev))
530: line_move(BACKWARD, 1, NO);
531: else if (dir == FORWARD) {
532: if (lastp(curline)) {
533: Eol();
534: break;
535: }
536: /* otherwise */
537: line_move(FORWARD, 1, NO);
538: }
539: }
540: }
541:
542: void
543: BackPara()
544: {
545: DoPara(BACKWARD);
546: }
547:
548: void
549: ForPara()
550: {
551: DoPara(FORWARD);
552: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.