home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / editor / j414src.arc / INSERT.C < prev    next >
C/C++ Source or Header  |  1989-10-10  |  16KB  |  777 lines

  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 */
  777.