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