|
|
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 "ctype.h"
10: #include "table.h"
11:
12: #ifdef MAC
13: # undef private
14: # define private
15: #endif
16:
17: #ifdef LINT_ARGS
18: private int
19: newchunk(void);
20: private void
21: init_specials(void),
22: remfreelines(struct chunk *);
23: #else
24: private int
25: newchunk();
26: private void
27: init_specials(),
28: remfreelines();
29: #endif /* LINT_ARGS */
30:
31: #ifdef MAC
32: # undef private
33: # define private static
34: #endif
35:
36: /* Make a newline after AFTER in buffer BUF, UNLESS after is 0,
37: in which case we insert the newline before after. */
38:
39: Line *
40: listput(buf, after)
41: register Buffer *buf;
42: register Line *after;
43: {
44: register Line *newline = nbufline();
45:
46: if (after == 0) { /* Before the first line */
47: newline->l_next = buf->b_first;
48: newline->l_prev = 0;
49: buf->b_first = newline;
50: } else {
51: newline->l_prev = after;
52: newline->l_next = after->l_next;
53: after->l_next = newline;
54: }
55: if (newline->l_next)
56: newline->l_next->l_prev = newline;
57: else
58: if (buf)
59: buf->b_last = newline;
60: if (buf && buf->b_dot == 0)
61: buf->b_dot = newline;
62: return newline;
63: }
64:
65: /* Divide the current line and move the current line to the next one */
66:
67: void
68: LineInsert(num)
69: register int num;
70: {
71: char newline[LBSIZE];
72: register Line *newdot,
73: *olddot;
74: int oldchar;
75:
76: olddot = curline;
77: oldchar = curchar;
78:
79: newdot = curline;
80: while (--num >= 0) {
81: newdot = listput(curbuf, newdot);
82: SavLine(newdot, NullStr);
83: }
84:
85: modify();
86: if (curchar != 0) {
87: strcpy(newline, &linebuf[curchar]);
88: linebuf[curchar] = '\0'; /* Shorten this line */
89: SavLine(curline, linebuf);
90: strcpy(linebuf, newline);
91: } else { /* Redisplay optimization */
92: newdot->l_dline = curline->l_dline;
93: SavLine(curline, NullStr);
94: }
95:
96: makedirty(curline);
97: curline = newdot;
98: curchar = 0;
99: makedirty(curline);
100: IFixMarks(olddot, oldchar, curline, curchar);
101: }
102:
103: /* Makes the indent of the current line == goal. If the current indent
104: is greater than GOAL it deletes. If more indent is needed, it uses
105: tabs and spaces to get to where it's going. */
106:
107: void
108: n_indent(goal)
109: register int goal;
110: {
111: int dotcol,
112: incrmt;
113:
114: ToIndent();
115: dotcol = calc_pos(linebuf, curchar);
116: if (goal < dotcol) {
117: DelWtSpace();
118: dotcol = 0;
119: }
120:
121: for (;;) {
122: incrmt = (tabstop - (dotcol % tabstop));
123: if (dotcol + incrmt > goal)
124: break;
125: insert_c('\t', 1);
126: dotcol += incrmt;
127: }
128: if (dotcol != goal)
129: insert_c(' ', (goal - dotcol));
130: }
131:
132: #ifdef ABBREV
133: void
134: MaybeAbbrevExpand()
135: {
136: if (MinorMode(Abbrev) && !ismword(LastKeyStruck) &&
137: !bolp() && ismword(linebuf[curchar - 1]))
138: AbbrevExpand();
139: }
140: #endif
141:
142: void
143: SelfInsert()
144: {
145: #ifdef ABBREV
146: MaybeAbbrevExpand();
147: #endif
148: if (LastKeyStruck != CTL('J') && MinorMode(OverWrite)) {
149: register int num,
150: i;
151:
152: for (i = 0, num = arg_value(); i < num; i++) {
153: int pos = calc_pos(linebuf, curchar);
154:
155: if (!eolp()) {
156: if (linebuf[curchar] == '\t') {
157: if ((pos + 1) == ((pos + tabstop) - (pos % tabstop)))
158: del_char(FORWARD, 1);
159: } else
160: del_char(FORWARD, 1);
161: }
162: insert_c(LastKeyStruck, 1);
163: }
164: } else
165: Insert(LastKeyStruck);
166:
167: if (MinorMode(Fill) && (curchar >= RMargin ||
168: (calc_pos(linebuf, curchar) >= RMargin))) {
169: int margin;
170: Bufpos save;
171:
172: if (MinorMode(Indent)) {
173: DOTsave(&save);
174: ToIndent();
175: margin = calc_pos(linebuf, curchar);
176: SetDot(&save);
177: } else
178: margin = LMargin;
179: DoJustify(curline, 0, curline,
180: curchar + strlen(&linebuf[curchar]), 1, margin);
181: }
182: }
183:
184: void
185: Insert(c)
186: {
187: if (c == CTL('J'))
188: LineInsert(arg_value());
189: else
190: insert_c(c, arg_value());
191: }
192:
193: /* insert character C N times at point */
194: void
195: insert_c(c, n)
196: {
197: if (n <= 0)
198: return;
199: modify();
200: makedirty(curline);
201: ins_c(c, linebuf, curchar, n, LBSIZE);
202: IFixMarks(curline, curchar, curline, curchar + n);
203: curchar += n;
204: }
205:
206: /* Tab in to the right place for C mode */
207:
208: void
209: Tab()
210: {
211: #ifdef LISP
212: if (MajorMode(LISPMODE) && (bolp() || !eolp())) {
213: int dotchar = curchar;
214: Mark *m = 0;
215:
216: ToIndent();
217: if (dotchar > curchar)
218: m = MakeMark(curline, dotchar, M_FLOATER);
219: (void) lisp_indent();
220: if (m) {
221: ToMark(m);
222: DelMark(m);
223: } else
224: ToIndent();
225: return;
226: }
227: #endif
228: if (MajorMode(CMODE) && strlen(linebuf) == 0)
229: (void) c_indent(CIndIncrmt);
230: else
231: SelfInsert();
232: }
233:
234: void
235: QuotChar()
236: {
237: int c,
238: slow;
239:
240: c = waitchar(&slow);
241: if (slow)
242: message(key_strokes);
243: if (c != CTL('@'))
244: Insert(c);
245: }
246:
247: /* Insert the paren. If in C mode and c is a '}' then insert the
248: '}' in the "right" place for C indentation; that is indented
249: the same amount as the matching '{' is indented. */
250:
251: int PDelay = 5, /* 1/2 a second */
252: CIndIncrmt = 8;
253:
254: void
255: DoParen()
256: {
257: Bufpos *bp = (Bufpos *) -1;
258: int nx,
259: c = LastKeyStruck;
260:
261: if (!isclosep(c)) {
262: SelfInsert();
263: return;
264: }
265:
266: if (MajorMode(CMODE) && c == '}' && blnkp(linebuf))
267: bp = c_indent(0);
268: #ifdef LISP
269: if (MajorMode(LISPMODE) && c == ')' && blnkp(linebuf))
270: bp = lisp_indent();
271: #endif
272: SelfInsert();
273: #ifdef MAC
274: if (MinorMode(ShowMatch) && !in_macro()) {
275: #else
276: if (MinorMode(ShowMatch) && !charp() && !in_macro()) {
277: #endif
278: b_char(1); /* Back onto the ')' */
279: if ((int) bp == -1)
280: bp = m_paren(c, BACKWARD, NO, YES);
281: f_char(1);
282: if (bp != 0) {
283: nx = in_window(curwind, bp->p_line);
284: if (nx != -1) { /* is visible */
285: Bufpos b;
286:
287: DOTsave(&b);
288: SetDot(bp);
289: SitFor(PDelay);
290: SetDot(&b);
291: } else
292: s_mess("%s", lcontents(bp->p_line));
293: }
294: mp_error(); /* display error message */
295: }
296: }
297:
298: void
299: LineAI()
300: {
301: DoNewline(TRUE);
302: }
303:
304: void
305: Newline()
306: {
307: DoNewline(MinorMode(Indent));
308: }
309:
310: void
311: DoNewline(indentp)
312: {
313: Bufpos save;
314: int indent;
315:
316: /* first we calculate the indent of the current line */
317: DOTsave(&save);
318: ToIndent();
319: indent = calc_pos(linebuf, curchar);
320: SetDot(&save);
321:
322: #ifdef ABBREV
323: MaybeAbbrevExpand();
324: #endif
325: #ifdef LISP
326: if (MajorMode(LISPMODE))
327: DelWtSpace();
328: else
329: #endif
330: if (indentp || blnkp(linebuf))
331: DelWtSpace();
332:
333: /* If there is more than 2 blank lines in a row then don't make
334: a newline, just move down one. */
335: if (arg_value() == 1 && eolp() && TwoBlank())
336: SetLine(curline->l_next);
337: else
338: LineInsert(arg_value());
339:
340: if (indentp)
341: #ifdef LISP
342: if (MajorMode(LISPMODE))
343: (void) lisp_indent();
344: else
345: #endif
346: n_indent((LMargin == 0) ? indent : LMargin);
347: }
348:
349: void
350: ins_str(str, ok_nl)
351: register char *str;
352: {
353: register char c;
354: Bufpos save;
355: int llen;
356:
357: if (*str == 0)
358: return; /* ain't nothing to insert! */
359: DOTsave(&save);
360: llen = strlen(linebuf);
361: while (c = *str++) {
362: if (c == '\n' || (ok_nl && llen >= LBSIZE - 2)) {
363: IFixMarks(save.p_line, save.p_char, curline, curchar);
364: modify();
365: makedirty(curline);
366: LineInsert(1);
367: DOTsave(&save);
368: llen = strlen(linebuf);
369: }
370: if (c != '\n') {
371: ins_c(c, linebuf, curchar++, 1, LBSIZE);
372: llen += 1;
373: }
374: }
375: IFixMarks(save.p_line, save.p_char, curline, curchar);
376: modify();
377: makedirty(curline);
378: }
379:
380: void
381: open_lines(n)
382: {
383: Bufpos dot;
384:
385: DOTsave(&dot);
386: LineInsert(n); /* Open the lines... */
387: SetDot(&dot);
388: }
389:
390: void
391: OpenLine()
392: {
393: open_lines(arg_value());
394: }
395:
396: /* Take the region FLINE/FCHAR to TLINE/TCHAR and insert it at
397: ATLINE/ATCHAR in WHATBUF. */
398:
399: Bufpos *
400: DoYank(fline, fchar, tline, tchar, atline, atchar, whatbuf)
401: Line *fline,
402: *tline,
403: *atline;
404: Buffer *whatbuf;
405: {
406: register Line *newline;
407: static Bufpos bp;
408: char save[LBSIZE],
409: buf[LBSIZE];
410: Line *startline = atline;
411: int startchar = atchar;
412:
413: lsave();
414: if (whatbuf)
415: modify();
416: (void) ltobuf(atline, genbuf);
417: strcpy(save, &genbuf[atchar]);
418:
419: (void) ltobuf(fline, buf);
420: if (fline == tline)
421: buf[tchar] = '\0';
422:
423: linecopy(genbuf, atchar, &buf[fchar]);
424: atline->l_dline = putline(genbuf);
425: makedirty(atline);
426:
427: fline = fline->l_next;
428: while (fline != tline->l_next) {
429: newline = listput(whatbuf, atline);
430: newline->l_dline = fline->l_dline;
431: makedirty(newline);
432: fline = fline->l_next;
433: atline = newline;
434: atchar = 0;
435: }
436:
437: getline(atline->l_dline, genbuf);
438: atchar += tchar;
439: linecopy(genbuf, atchar, save);
440: atline->l_dline = putline(genbuf);
441: makedirty(atline);
442: IFixMarks(startline, startchar, atline, atchar);
443: bp.p_line = atline;
444: bp.p_char = atchar;
445: this_cmd = YANKCMD;
446: getDOT(); /* Whatever used to be in linebuf */
447: return &bp;
448: }
449:
450: void
451: YankPop()
452: {
453: Line *line,
454: *last;
455: Mark *mp = CurMark();
456: Bufpos *dot;
457: int dir = -1; /* Direction to rotate the ring */
458:
459: if (last_cmd != YANKCMD)
460: complain("Yank something first!");
461:
462: lfreelist(reg_delete(mp->m_line, mp->m_char, curline, curchar));
463:
464: /* Now must find a recently killed region. */
465:
466: if (arg_value() < 0)
467: dir = 1;
468:
469: killptr += dir;
470: for (;;) {
471: if (killptr < 0)
472: killptr = NUMKILLS - 1;
473: else if (killptr >= NUMKILLS)
474: killptr = 0;
475: if (killbuf[killptr])
476: break;
477: killptr += dir;
478: }
479:
480: this_cmd = YANKCMD;
481:
482: line = killbuf[killptr];
483: last = lastline(line);
484: dot = DoYank(line, 0, last, length(last), curline, curchar, curbuf);
485: MarkSet(CurMark(), curline, curchar);
486: SetDot(dot);
487: }
488:
489: /* This is an attempt to reduce the amount of memory taken up by each line.
490: Without this each malloc of a line uses sizeof (line) + sizeof(HEADER)
491: where line is 3 words and HEADER is 1 word.
492: This is going to allocate memory in chucks of CHUNKSIZE * sizeof (line)
493: and divide each chuck into lineS. A line is free in a chunk when its
494: line->l_dline == 0, so freeline sets dline to 0. */
495:
496: #define CHUNKSIZE 300
497:
498: struct chunk {
499: int c_nlines; /* Number of lines in this chunk (so they
500: don't all have to be CHUNKSIZE long). */
501: Line *c_block; /* Chunk of memory */
502: struct chunk *c_nextfree; /* Next chunk of lines */
503: };
504:
505: private struct chunk *fchunk = 0;
506: private Line *ffline = 0; /* First free line */
507:
508: void
509: freeline(line)
510: register Line *line;
511: {
512: line->l_dline = 0;
513: line->l_next = ffline;
514: if (ffline)
515: ffline->l_prev = line;
516: line->l_prev = 0;
517: ffline = line;
518: }
519:
520: void
521: lfreelist(first)
522: register Line *first;
523: {
524: if (first)
525: lfreereg(first, lastline(first));
526: }
527:
528: /* Append region from line1 to line2 onto the free list of lines */
529:
530: void
531: lfreereg(line1, line2)
532: register Line *line1,
533: *line2;
534: {
535: register Line *next,
536: *last = line2->l_next;
537:
538: while (line1 != last) {
539: next = line1->l_next;
540: freeline(line1);
541: line1 = next;
542: }
543: }
544:
545: private int
546: newchunk()
547: {
548: register Line *newline;
549: register int i;
550: struct chunk *f;
551: int nlines = CHUNKSIZE;
552:
553: f = (struct chunk *) emalloc(sizeof (struct chunk));
554: if (f == 0)
555: return 0;
556:
557: if ((f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines))) == 0) {
558: while (nlines > 0) {
559: f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines));
560: if (f->c_block != 0)
561: break;
562: nlines /= 2;
563: }
564: }
565:
566: if (nlines <= 0)
567: return 0;
568:
569: f->c_nlines = nlines;
570: for (i = 0, newline = f->c_block; i < nlines; newline++, i++)
571: freeline(newline);
572: f->c_nextfree = fchunk;
573: fchunk = f;
574: return 1;
575: }
576:
577: /* New BUFfer LINE */
578:
579: Line *
580: nbufline()
581: {
582: register Line *newline;
583:
584: if (ffline == 0) /* No free list */
585: if (newchunk() == 0)
586: complain("[Out of lines] ");
587: newline = ffline;
588: ffline = ffline->l_next;
589: if (ffline)
590: ffline->l_prev = 0;
591: return newline;
592: }
593:
594: /* Remove the free lines, in chunk c, from the free list because they are
595: no longer free. */
596:
597: private void
598: remfreelines(c)
599: register struct chunk *c;
600: {
601: register Line *lp;
602: register int i;
603:
604: for (lp = c->c_block, i = 0; i < c->c_nlines; i++, lp++) {
605: if (lp->l_prev)
606: lp->l_prev->l_next = lp->l_next;
607: else
608: ffline = lp->l_next;
609: if (lp->l_next)
610: lp->l_next->l_prev = lp->l_prev;
611: }
612: }
613:
614: /* This is used to garbage collect the chunks of lines when malloc fails
615: and we are NOT looking for a new buffer line. This goes through each
616: chunk, and if every line in a given chunk is not allocated, the entire
617: chunk is `free'd by "free()". */
618:
619: void
620: GCchunks()
621: {
622: register struct chunk *cp;
623: struct chunk *prev = 0,
624: *next = 0;
625: register int i;
626: register Line *newline;
627:
628: for (cp = fchunk; cp != 0; cp = next) {
629: for (i = 0, newline = cp->c_block; i < cp->c_nlines; newline++, i++)
630: if (newline->l_dline != 0)
631: break;
632:
633: next = cp->c_nextfree;
634:
635: if (i == cp->c_nlines) { /* Unlink it!!! */
636: if (prev)
637: prev->c_nextfree = cp->c_nextfree;
638: else
639: fchunk = cp->c_nextfree;
640: remfreelines(cp);
641: free((char *) cp->c_block);
642: free((char *) cp);
643: } else
644: prev = cp;
645: }
646: }
647:
648: #ifdef LISP
649:
650: /* Grind S-Expr */
651:
652: void
653: GSexpr()
654: {
655: Bufpos dot,
656: end;
657:
658: if (linebuf[curchar] != '(')
659: complain((char *) 0);
660: DOTsave(&dot);
661: FSexpr();
662: DOTsave(&end);
663: SetDot(&dot);
664: for (;;) {
665: if (curline == end.p_line)
666: break;
667: line_move(FORWARD, 1, NO);
668: if (!blnkp(linebuf))
669: (void) lisp_indent();
670: }
671: SetDot(&dot);
672: }
673:
674: /* lisp_indent() indents a new line in Lisp Mode, according to where
675: the matching close-paren would go if we typed that (sort of). */
676:
677: private Table *specials = NIL;
678:
679: private void
680: init_specials()
681: {
682: static char *words[] = {
683: "case",
684: "def",
685: "dolist",
686: "fluid-let",
687: "lambda",
688: "let",
689: "lexpr",
690: "macro",
691: "named-l", /* named-let and named-lambda */
692: "nlambda",
693: "prog",
694: "selectq",
695: 0
696: };
697: char **wordp = words;
698:
699: specials = make_table();
700: while (*wordp)
701: add_word(*wordp++, specials);
702: }
703:
704: void
705: AddSpecial()
706: {
707: char *word;
708:
709: word = ask((char *) 0, ProcFmt);
710: if (specials == NIL)
711: init_specials();
712: add_word(copystr(word), specials);
713: }
714:
715: Bufpos *
716: lisp_indent()
717: {
718: Bufpos *bp,
719: savedot;
720: int goal;
721:
722: bp = m_paren(')', BACKWARD, NO, YES);
723:
724: if (bp == 0)
725: return 0;
726:
727: /* We want to end up
728:
729: (atom atom atom ...
730: ^ here.
731: */
732:
733: DOTsave(&savedot);
734: SetDot(bp);
735: f_char(1);
736: if (linebuf[curchar] != '(') {
737: register Word *wp;
738:
739: if (specials == NIL)
740: init_specials();
741: for (wp = table_top(specials); wp != NIL; wp = next_word(wp))
742: if (casencmp(word_text(wp), &linebuf[curchar], word_length(wp)) == 0)
743: break;
744: if (wp == NIL) { /* not special */
745: int c_char = curchar;
746:
747: WITH_TABLE(curbuf->b_major)
748: f_word(1);
749: END_TABLE();
750: if (LookingAt("[ \t]*;\\|[ \t]*$", linebuf, curchar))
751: curchar = c_char;
752: else while (linebuf[curchar] == ' ')
753: curchar += 1;
754: } else
755: curchar += 1;
756: }
757: goal = calc_pos(linebuf, curchar);
758: SetDot(&savedot);
759: n_indent(goal);
760:
761: return bp;
762: }
763: #endif /* LISP */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.