Logo Search packages:      
Sourcecode: vile version File versions

insert.c

/*
 *
 *    insert.c
 *
 * Do various types of character insertion, including most of insert mode.
 *
 * Most code probably by Dan Lawrence or Dave Conroy for MicroEMACS
 * Extensions for vile by Paul Fox
 *
 * $Header: /usr/build/vile/vile/RCS/insert.c,v 1.141 2003/06/18 21:38:27 tom Exp $
 *
 */

#include    "estruct.h"
#include    "edef.h"

#define     DOT_ARGUMENT      ((dotcmdactive == PLAY) && dotcmdarg)

#define     BackspaceLimit() (b_val(curbp,MDBACKLIMIT) && autoindented <= 0)\
                  ? DOT.o\
                  : w_left_margin(curwp)

static int backspace(void);
static int doindent(int ind);
static int indented_newline(void);
static int indented_newline_above(void);
static int ins_anytime(int playback, int cur_count, int max_count, int *splice);
static int insbrace(int n, int c);
static int inspound(void);
static int nextindent(int *bracefp);
static int openlines(int n);
static int shiftwidth(int f, int n);
static int tab(int f, int n);
#if !SMALLER
static int istring(int f, int n, int mode);
#endif

/* value of insertmode maintained through subfuncs */
static int savedmode;

static int allow_aindent = TRUE;
static int skipindent;

/*--------------------------------------------------------------------------*/

/* If the wrapmargin mode is active (i.e., nonzero), and if it's not wider than
 * the screen, return the difference from the current position to the wrap
 * margin.  Otherwise, return negative.
 */
static int
past_wrapmargin(int c)
{
    int n;

    if ((n = b_val(curbp, VAL_WRAPMARGIN)) > 0
      && (n = (term.cols - (nu_width(curwp) + n))) >= 0) {
      int list = w_val(curwp, WMDLIST);
      int used = getccol(list);
      int tabs = tabstop_val(curbp);

      /* compute the effective screen column after adding the
       * latest character
       */
      return NEXT_COLUMN(used, c, list, tabs) - n;
    }
    return -1;
}

/* Returns true iff wrap-margin or wrap-words is active and we'll wrap at the
 * current column.
 */
static int
wrap_at_col(int c)
{
    int n;

    /* if we'll split the line, there is no point in wrapping */
    if (isreturn(c)
      && (DOT.o >= llength(DOT.l)
          || !isSpace(lgetc(DOT.l, DOT.o))))
      return FALSE;

    if (past_wrapmargin(c) >= 0)
      return TRUE;

    if (b_val(curbp, MDWRAP)
      && (n = getfillcol(curbp)) > 0)
      return (getccol(FALSE) > n);

    return FALSE;
}

/* advance one character past the current position, for 'append()' */
static void
advance_one_char(void)
{
    if (!is_header_line(DOT, curbp) && !is_at_end_of_line(DOT))
      forwchar(TRUE, 1);      /* END OF LINE HACK */
}

/* common logic for i,I,a,A commands */
static int
ins_n_times(int f, int n, int advance)
{
    int status = TRUE;
    int i;
    int flag = FALSE;

    if (!f || n < 0)
      n = 1;

    for (i = 0; i < n; i++) {
      if ((status = ins_anytime((i != 0), i, n, &flag)) != TRUE)
          break;
      if (advance && !flag)
          advance_one_char();
    }
    return status;
}

/* open lines up before this one */
int
openup(int f, int n)
{
    int s;

    if (!f)
      n = 1;
    if (n < 0)
      return (FALSE);
    if (n == 0)
      return ins();

    (void) gotobol(TRUE, 1);

    /* if we are using C-indents and this is a default <NL> */
    if (allow_aindent && n == 1 &&
      (b_val(curbp, MDCINDENT) || b_val(curbp, MDAIND)) &&
      !is_header_line(DOT, curbp)) {
      s = indented_newline_above();
      if (s != TRUE)
          return (s);

      return (ins());
    }
    s = lnewline();
    if (s != TRUE)
      return s;

    (void) backline(TRUE, 1); /* back to the blank line */

    if (n > 1) {
      s = openlines(n - 1);
      if (s != TRUE)
          return s;
      s = backline(TRUE, 1);  /* backup over the first one */
      if (s != TRUE)
          return s;
    }

    return (ins());
}

/*
 * as above, but override all autoindenting and cmode-ing
 */
int
openup_no_aindent(int f, int n)
{
    int s;
    int oallow = allow_aindent;
    allow_aindent = FALSE;
    s = openup(f, n);
    allow_aindent = oallow;
    return s;
}

/* open lines up after this one */
int
opendown(int f, int n)
{
    int s;

    if (!f)
      n = 1;
    if (n < 0)
      return (FALSE);
    if (n == 0)
      return ins();

    s = openlines(n);
    if (s != TRUE)
      return (s);

    return (ins());
}

/*
 * as above, but override all autoindenting and cmode-ing
 */
int
opendown_no_aindent(int f, int n)
{
    int s;
    int oallow = allow_aindent;
    allow_aindent = FALSE;
    s = opendown(f, n);
    allow_aindent = oallow;
    return s;
}

/*
 * Open up some blank space. The basic plan is to insert a bunch of newlines,
 * and then back up over them.
 *
 * This interprets the repeat-count for the 'o' and 'O' commands.  Unlike vi
 * (which does not use the repeat-count), this specifies the number of blank
 * lines to create before proceeding with inserting the string-argument of the
 * command.
 */
static int
openlines(int n)
{
    int i = n;                /* Insert newlines. */
    int s = TRUE;
    while (i-- && s == TRUE) {
      (void) gotoeol(FALSE, 1);
      s = newline(TRUE, 1);
    }
    if (s == TRUE && n)       /* Then back up over top */
      (void) backline(TRUE, n - 1); /* of them all.          */

    curgoal = -1;

    return s;
}

/*
 * Implements the vi 'i' command.
 */
int
insert(int f, int n)
{
    return ins_n_times(f, n, TRUE);
}

/*
 * as above, but override all autoindenting and cmode-ing
 */
int
insert_no_aindent(int f, int n)
{
    int s;
    int oallow = allow_aindent;
    allow_aindent = FALSE;
    s = ins_n_times(f, n, TRUE);
    allow_aindent = oallow;
    return s;
}

/*
 * Implements the vi 'I' command.
 */
int
insertbol(int f, int n)
{
    if (!DOT_ARGUMENT || (dotcmdrep == dotcmdcnt))
      (void) firstnonwhite(FALSE, 1);
    return ins_n_times(f, n, TRUE);
}

/*
 * Implements the vi 'a' command.
 */
int
append(int f, int n)
{
    advance_one_char();

    return ins_n_times(f, n, !DOT_ARGUMENT);
}

/*
 * Implements the vi 'A' command.
 */
int
appendeol(int f, int n)
{
    if (!is_header_line(DOT, curbp))
      (void) gotoeol(FALSE, 0);

    return ins_n_times(f, n, TRUE);
}

/*
 * Unlike most flags on the mode-line, we'll only show the insertion-mode on
 * the current window.
 */
static void
set_insertmode(int mode)
{
    insertmode = mode;
    if (b_val(curbp, MDSHOWMODE))
      curwp->w_flag |= WFMODE;
}

/*
 * Function that returns the insertion mode if we're inserting into a given
 * window
 */
int
ins_mode(WINDOW *wp)
{
    return (wp == curwp) ? insertmode : FALSE;
}

/*
 * Implements the vi 'R' command.
 *
 * This takes an optional repeat-count and a string-argument.  The repeat-count
 * (default 1) specifies the number of times that the string argument is
 * inserted.  The length of the string-argument itself determines the number of
 * characters (beginning with the cursor position) to delete before beginning
 * the insertion.
 */
int
overwritechars(int f, int n)
{
    set_insertmode(INSMODE_OVR);
    return ins_n_times(f, n, TRUE);
}

/*
 * Implements the vi 'r' command.
 *
 * This takes an optional repeat-count and a single-character argument.  The
 * repeat-count (default 1) specifies the number of characters beginning with
 * the cursor position that are replaced by the argument.  Newline is treated
 * differently from the other characters (only one newline is inserted).
 *
 * Unlike vi, the number of characters replaced can be longer than a line.
 * Also, vile allows quoted characters.
 */
int
replacechar(int f, int n)
{
    int s = TRUE;
    int t = FALSE;
    int c;

    if (!f && is_empty_line(DOT))
      return FALSE;

    if (clexec || isnamedcmd) {
      int status;
      static char cbuf[NLINE];
      if ((status = mlreply("Replace with: ", cbuf, 2)) != TRUE)
          return status;
      c = cbuf[0];
    } else {
      set_insertmode(INSMODE_RPL);  /* need to fool SPEC prefix code */
      if (dotcmdactive != PLAY)
          (void) update(FALSE);
      c = keystroke();
      if (ABORTED(c)) {
          set_insertmode(FALSE);
          return ABORT;
      }

    }
    c = kcod2key(c);

    if (!f || !n)
      n = 1;
    if (n < 0)
      s = FALSE;
    else {
      int vi_fix = (!DOT_ARGUMENT || (dotcmdrep <= 1));

      (void) ldelete((B_COUNT) n, FALSE);
      if (c == quotec) {
          t = s = quote_next(f, n);
      } else {
          if (isreturn(c)) {
            if (vi_fix)
                s = lnewline();
          } else {
            if (isbackspace(c)) {   /* vi beeps here */
                s = TRUE;     /* replaced with nothing */
            } else {
                t = s = linsert(n, c);
            }
          }
      }
      if ((t == TRUE) && (DOT.o > w_left_margin(curwp)) && vi_fix)
          s = backchar(FALSE, 1);
    }
    set_insertmode(FALSE);
    return s;
}

/*
 * Check if a command is safe to execute within insert-mode.
 */
#define can_ins_exec(cfp,c) \
      ((isSpecial(c) || global_g_val(GMDINSEXEC)) \
       && (cfp = InsertKeyBinding(c)) != 0 \
       && ((cfp->c_flags & (GOAL|MOTION)) != 0 \
        || (cfp->c_flags & (UNDO|REDO)) == (UNDO|REDO))) \

/*
 * Execute a command within the insert-mode.
 */
static int
insertion_exec(const CMDFUNC * cfp)
{
    int savedexecmode = insertmode;
    int backsp_limit = w_left_margin(curwp);

    if (curgoal < 0)
      curgoal = getccol(FALSE);

    (void) execute(cfp, FALSE, 1);
    insertmode = savedexecmode;

    return backsp_limit;
}

/*
 * This routine performs the principal decoding for insert mode (i.e.., the
 * i,I,a,A,R commands).  It is invoked via 'ins_n_times()', which loops over
 * the repeat-count for direct commands.  One complicating factor is that
 * 'ins_n_times()' (actually its callers) is called once for each repetition in
 * a '.' command.  At this level we compute the effective loop counter (the '.'
 * and the direct commands), because we have to handle the vi-compatibilty case
 * of inserting a newline.
 *
 * We stop repeating insert after the first newline in the insertion-string
 * (that's what vi does).  If a user types
 *
 *    3iABC<nl>foo<esc>
 *
 * then we want to insert
 *
 *    ABCABCABC<nl>foo
 */
static int last_insert_char;

static int
ins_anytime(int playback, int cur_count, int max_count, int *splice)
{
#if OPT_MOUSE || OPT_B_LIMITS
    WINDOW *wp0 = curwp;
#endif
    int status;
    int c;              /* command character */
    int backsp_limit;
    static ITBUFF *insbuff;
    static int nested;
    int osavedmode;
    const CMDFUNC *cfp;

    /*
     * Prevent recursion of insert-chars (it's confusing).
     */
    if (nested++
      || (curbp == bminip)) {
      kbd_alarm();
      nested--;
      return FALSE;
    }

    if (DOT_ARGUMENT) {
      max_count = cur_count + dotcmdcnt;
      cur_count += dotcmdcnt - dotcmdrep;
    }

    if (playback && (insbuff != 0))
      itb_first(insbuff);
    else if (!itb_init(&insbuff, esc_c)) {
      nested--;
      return FALSE;
    }

    if (insertmode == FALSE)
      set_insertmode(INSMODE_INS);
    osavedmode = savedmode;
    savedmode = insertmode;

    backsp_limit = BackspaceLimit();

    last_insert_char = EOS;

    for_ever {

      /*
       * Read another character from the insertion-string.
       */
      c = esc_c;
      if (playback) {
          if (*splice && !itb_more(insbuff))
            playback = FALSE;
          else
            c = itb_next(insbuff);
      }
      if (!playback) {
          if (dotcmdactive != PLAY)
            (void) update(FALSE);

          c = mapped_keystroke();

          /*
           * Like kbd_seq(), but we would find it too painful to
           * also allow '#' to be used as a prefix in insert
           * mode.
           */
          if (global_g_val(GMDINSEXEC)) {
            if (c == cntl_a) {
                c = CTLA | keystroke();
            } else if (c == cntl_x) {
                c = CTLX | keystroke();
            }
          }
#if OPT_MOUSE
          /*
           * Prevent user from starting insertion into a
           * modifiable buffer, then clicking on another
           * buffer to continue inserting.  This assumes that
           * 'setcursor()' handles entry into the other
           * buffer.
           */
          if (curwp != wp0) {
            /* end insert mode for window we started in */
            wp0->w_traits.insmode = FALSE;
            if (b_val(wp0->w_bufp, MDSHOWMODE))
                wp0->w_flag |= WFMODE;
            unkeystroke(c);
            goto leave_ins;
          }
#endif
          if (!itb_append(&insbuff, c)) {
            status = FALSE;
            break;
          }
      }

      /* if we're allowed to honor SPEC bindings,
         then see if it's bound to something, and
         execute it */
      if (can_ins_exec(cfp, c)) {
          backsp_limit = insertion_exec(cfp);
          continue;
      } else if (isSpecial(c)) {
          /* ignore SPEC bindings that we cannot use */
          continue;
      }

      if (!isident(c))
          abbr_check(&backsp_limit);

      if (isreturn(c)) {
          if ((cur_count + 1) < max_count) {
            if (DOT_ARGUMENT) {
                while (itb_more(dotcmd))
                  (void) mapped_keystroke();
            }
            *splice = TRUE;
            status = TRUE;
            break;
          } else if (DOT_ARGUMENT) {
            *splice = TRUE;
          }
      }
      /*
       * Decode the character
       */
      if (ABORTED(c)) {
#if OPT_MOUSE
        leave_ins:
#endif
          /* an unfortunate Vi-ism that ensures one
             can always type "ESC a" if you're not sure
             you're in insert mode. */
          if (DOT.o > w_left_margin(wp0))
            backchar(TRUE, 1);
          if (autoindented >= 0) {
            (void) trimline((void *) 0, 0, 0);
            autoindented = -1;
          }
          if (cur_count + 1 == max_count)
            *splice = TRUE;
          status = TRUE;
          break;
      } else if ((c & HIGHBIT) && b_val(curbp, MDMETAINSBIND)) {
          /* if we're allowed to honor meta-character bindings,
             then see if it's bound to something, and
             insert it if not */
          if (can_ins_exec(cfp, c)) {
            backsp_limit = insertion_exec(cfp);
            continue;
          }
      }

      if (c == startc || c == stopc) {    /* ^Q and ^S */
          continue;
      }
#if OPT_SHELL && SYS_UNIX && defined(SIGTSTP)   /* job control, ^Z */
      else if (c == suspc) {
          status = bktoshell(FALSE, 1);
      }
#endif
      else {
          status = inschar(c, &backsp_limit);
          curgoal = -1;
      }

      if (status != TRUE)
          break;

#if OPT_CFENCE
      /* check for CMODE fence matching */
      if (b_val(curbp, MDSHOWMAT))
          fmatch(c);
#endif

      /* do we need to do an auto-save? */
      if (b_val(curbp, MDASAVE)
          && !b_val(curbp, MDREADONLY)) {
          curbp->b_acount--;
          if (curbp->b_acount <= 0) {
            (void) update(TRUE);
            filesave(FALSE, 0);
            curbp->b_acount = (short) b_val(curbp, VAL_ASAVECNT);
          }
      }
    }

    set_insertmode(FALSE);
    savedmode = osavedmode;
    nested--;
    return (status);
}

/* grunt routine for insert mode */
int
ins(void)
{
    int flag;
    return ins_anytime(FALSE, 1, 1, &flag);
}

static int
isallspace(LINEPTR ln, int lb, int ub)
{
    while (lb <= ub) {
      if (!isSpace(lgetc(ln, ub)))
          return FALSE;
      ub--;
    }
    return TRUE;
}

/*
 * This function is used when testing for wrapping, to see if there are any
 * blanks already on the line.  We explicitly exclude blanks before the
 * autoindent margin, if any, to avoid inserting unnecessary blank lines.
 */
static int
blanks_on_line(void)
{
    int code = FALSE;
    int indentwas = b_val(curbp, MDAIND) ? previndent((int *) 0) : 0;
    int save = DOT.o;
    int list = w_val(curwp, WMDLIST);

    for (DOT.o = 0; DOT.o < llength(DOT.l); DOT.o++) {
      if (isSpace(char_at(DOT))
          && getccol(list) >= indentwas) {
          code = TRUE;
          break;
      }
    }
    DOT.o = save;
    return code;
}

/*
 * Check if we're to interpret the given character ch for C-style indenting.
 */
int
is_cindent_char(BUFFER *bp, int ch)
{
    return valid_buffer(bp)
      && b_val(bp, MDCINDENT)
      && (b_val_ptr(bp, VAL_CINDENT_CHARS) != 0)
      && (strchr(b_val_ptr(bp, VAL_CINDENT_CHARS), ch) != 0);
}

int
inschar(int c, int *backsp_limit_p)
{
    CmdFunc execfunc;         /* ptr to function to execute */

    execfunc = NULL;
    if (c == quotec) {
      execfunc = quote_next;
    } else {
      /*
       * If a space was typed, fill column is defined, the argument
       * is non-negative, wrap mode is enabled, and we are now past
       * fill column, perform word wrap.
       */
      if (wrap_at_col(c)) {
          int offset = past_wrapmargin(c);
          int is_print = (!isSpecial(c) && isPrint(c));
          int is_space = (!isSpecial(c) && isSpace(c));
          int at_end = DOT.o >= llength(DOT.l);
          int wm_flag = (offset >= 0) || (is_space && !at_end);

          if (is_space
            || (is_print && (offset >= 1) && blanks_on_line())) {
            int status = wrapword(wm_flag, is_space);
            *backsp_limit_p = w_left_margin(curwp);
            if (wm_flag && is_space)
                return status;
          } else if (wm_flag
                   && !blanks_on_line()
                   && (c == '\t' || is_print)) {
            kbd_alarm();      /* vi beeps past the margin */
          }
      }

      if (c == '\t') {  /* tab */
          execfunc = tab;
          autoindented = -1;
      } else if (isreturn(c)) {
          execfunc = newline;
          if (autoindented >= 0) {
            (void) trimline((void *) 0, 0, 0);
            autoindented = -1;
          }
          *backsp_limit_p = w_left_margin(curwp);
      } else if (isbackspace(c) ||
               c == tocntrl('D') ||
               c == killc ||
               c == wkillc) { /* ^U and ^W */
          execfunc = nullproc;
          /* all this says -- "is this a regular ^D for
             backing up a shiftwidth?".  otherwise,
             we treat it as ^U, below */
          if (c == tocntrl('D')
            && !(DOT.o > *backsp_limit_p
                 && ((lgetc(DOT.l, DOT.o - 1) == '0'
                    && last_insert_char == '0')
                   || (lgetc(DOT.l, DOT.o - 1) == '^'
                       && last_insert_char == '^'))
                 && isallspace(DOT.l, w_left_margin(curwp),
                           DOT.o - 2))) {
            int goal, col, sw;

            sw = shiftwid_val(curbp);
            if (autoindented >= 0)
                *backsp_limit_p = w_left_margin(curwp);
            col = getccol(FALSE);
            if (col > 0)
                goal = ((col - 1) / sw) * sw;
            else
                goal = 0;
            while (col > goal &&
                   DOT.o > *backsp_limit_p) {
                backspace();
                col = getccol(FALSE);
            }
            if (col < goal)
                linsert(goal - col, ' ');
          } else if (isbackspace(c)
                   && !b_val(curbp, MDBACKLIMIT)
                   && (DOT.o <= *backsp_limit_p)) {
            backspace();
          } else {
            /* have we backed thru a "word" yet? */
            int saw_word = FALSE;

            /* was it '^^D'?  then set the flag
               that tells us to skip a line
               when calculating the autoindent
               on the next newline */
            if (c == tocntrl('D') &&
                last_insert_char == '^')
                skipindent = 1;

            while (DOT.o > *backsp_limit_p) {
                if (c == wkillc) {
                  if (isSpace(lgetc(DOT.l,
                                DOT.o - 1))) {
                      if (saw_word)
                        break;
                  } else {
                      saw_word = TRUE;
                  }
                }
                backspace();
                autoindented--;
                if (c != wkillc && c != killc
                  && c != tocntrl('D'))
                  break;
            }
          }
      } else if (c == tocntrl('T')) {           /* ^T */
          execfunc = shiftwidth;
      }

      last_insert_char = c;

    }

    if (execfunc != NULL)
      return (*execfunc) (FALSE, 1);

    /* make it a real character again */
    c = kcod2key(c);

    /* if we are in overwrite mode, not at eol,
       and next char is not a tab or we are at a tab stop,
       delete a char forword                        */
    if ((insertmode == INSMODE_OVR)
      && (!DOT_ARGUMENT || (dotcmdrep <= 1))
      && (DOT.o < llength(DOT.l))
      && (char_at(DOT) != '\t'
          || DOT.o % tabstop_val(curbp) == tabstop_val(curbp) - 1)) {
      autoindented = -1;
      (void) ldelete(1L, FALSE);
    }

    /* do the appropriate insertion */
    if (allow_aindent && b_val(curbp, MDCINDENT)) {
      int dir;
      if (is_cindent_char(curbp, c) && is_user_fence(c, &dir) && dir == REVERSE) {
          return insbrace(1, c);
      } else if (c == '#' && is_cindent_char(curbp, '#')) {
          return inspound();
      }
    }

    autoindented = -1;
    return linsert(1, c);
}

#if ! SMALLER
int
appstring(int f, int n)
{
    TRACE((T_CALLED "appstring(f=%d, n=%d)\n", f, n));
    advance_one_char();
    returnCode(istring(f, n, INSMODE_INS));
}

int
insstring(int f, int n)
{
    TRACE((T_CALLED "insstring(f=%d, n=%d)\n", f, n));
    returnCode(istring(f, n, INSMODE_INS));
}

int
overwstring(int f, int n)
{
    TRACE((T_CALLED "overwstring(f=%d, n=%d)\n", f, n));
    returnCode(istring(f, n, INSMODE_OVR));
}

/* ask for and insert or overwrite a string into the current */
/* buffer at the current point */
static int
istring(int f, int n, int mode)
{
    char *tp;                 /* pointer into string to add */
    int status;               /* status return code */
    int backsp_limit;
    static char tstring[NPAT + 1];  /* string to add */

    /* ask for string to insert */
    status = mlreply("String to insert: ", tstring, NPAT);
    if (status != TRUE)
      return (status);

    if (!f)
      n = 1;

    if (n < 0)
      n = -n;

    set_insertmode(mode);

    backsp_limit = BackspaceLimit();

    /* insert it */
    while (n--) {
      tp = tstring;
      while (*tp) {
          status = inschar(*tp++, &backsp_limit);
          if (status != TRUE) {
            set_insertmode(FALSE);
            return (status);
          }
      }
    }

    set_insertmode(FALSE);
    return (TRUE);
}
#endif

static int
backspace(void)
{
    int s;

    if ((s = backchar(TRUE, 1)) == TRUE && insertmode != INSMODE_OVR)
      s = ldelete(1L, FALSE);
    return (s);
}

/*
 * Insert a newline. If we are in CMODE, do automatic
 * indentation as specified.
 */
int
newline(int f, int n)
{
    int s;

    if (!f)
      n = 1;
    else if (n < 0)
      return (FALSE);

    /* if we are in C or auto-indent modes and this is a default <NL> */
    if (allow_aindent
      && (n == 1)
      && (b_val(curbp, MDCINDENT) || b_val(curbp, MDAIND))
      && !is_header_line(DOT, curbp))
      return indented_newline();

    /* insert some lines */
    while (n--) {
      if ((s = lnewline()) != TRUE)
          return (s);
      curwp->w_flag |= WFINS;
    }
    return (TRUE);
}

/* insert a newline and indentation for C */
static int
indented_newline(void)
{
    int cmode = allow_aindent && b_val(curbp, MDCINDENT);
    int indentwas;            /* indent to reproduce */
    int bracef;               /* was there a brace at the end of line? */

    if (lnewline() == FALSE)
      return FALSE;

    indentwas = previndent(&bracef);
    skipindent = 0;

    if (cmode && bracef)
      indentwas = next_sw(indentwas);

    return doindent(indentwas);
}

/* insert a newline and indentation for autoindent */
static int
indented_newline_above(void)
{
    int cmode = allow_aindent && b_val(curbp, MDCINDENT);
    int indentwas;            /* indent to reproduce */
    int bracef;               /* was there a brace at the beginning of line? */

    indentwas = nextindent(&bracef);
    if (lnewline() == FALSE)
      return FALSE;
    if (backline(TRUE, 1) == FALSE)
      return FALSE;
    if (cmode && bracef)
      indentwas = next_sw(indentwas);

    return doindent(indentwas);
}

/*
 * Get the indent of the last previous non-blank line.  Also, if arg is
 * non-null, check if line ended in a brace.
 */
int
previndent(int *bracefp)
{
    int ind;
    int cmode = allow_aindent && is_cindent_char(curbp, '#');

    if (bracefp)
      *bracefp = FALSE;

    MK = DOT;

    /* backword() will leave us either on this line, if there's something
       non-blank here, or on the nearest previous non-blank line. */
    /* (at start of buffer, may leave us on empty line) */
    do {
      if (backword(FALSE, 1) == FALSE || is_empty_line(DOT)) {
          (void) gomark(FALSE, 1);
          return 0;
      }
      DOT.o = 0;
      /* if the line starts with a #, then don't copy its indent */
    } while ((skipindent-- > 0) || (cmode && lgetc(DOT.l, 0) == '#'));

    ind = indentlen(DOT.l);
    if (bracefp) {
      int lc = lastchar(DOT.l);
      int c = lgetc(DOT.l, lc);
      int dir;
      *bracefp = (lc >= 0 && ((c == ':' && is_cindent_char(curbp, ':')) ||
                        (is_user_fence(c, &dir) && dir == FORWARD)));

    }

    (void) gomark(FALSE, 1);

    return ind;
}

/*
 * Get the indent of the next non-blank line.  Also, if arg is non-null, check
 * if line starts in a brace.
 */
static int
nextindent(int *bracefp)
{
    int ind;
    int fc;

    MK = DOT;

    /* we want the indent of this line if it's non-blank, or the indent
       of the next non-blank line otherwise */
    fc = firstchar(DOT.l);
    if (fc < 0 && (forwword(FALSE, 1) == FALSE
               || (fc = firstchar(DOT.l)) < 0)) {
      if (bracefp)
          *bracefp = FALSE;
      DOT = MK;
      return 0;
    }
    ind = indentlen(DOT.l);
    if (bracefp) {
      *bracefp = ((lgetc(DOT.l, fc) == R_CURLY) ||
                (lgetc(DOT.l, fc) == R_PAREN) ||
                (lgetc(DOT.l, fc) == R_BLOCK));
    }

    DOT = MK;

    return ind;
}

static int
doindent(int ind)
{
    int i, j;

    /* first clean up existing leading whitespace */
    if ((i = firstchar(DOT.l)) >= 0)
      j = DOT.o - i;
    else
      j = 0;
    if (j < 0)
      j = 0;
    DOT.o = w_left_margin(curwp);
    if (i > 0)
      (void) ldelete((B_COUNT) i, FALSE);

    autoindented = 0;
    /* if no indent was asked for, we're done */
    if (ind > 0) {
      int tabs = tabstop_val(curbp);

      i = ind / tabs;         /* how many tabs? */
      if (i && b_val(curbp, MDTABINSERT)) {
          autoindented += i;
          if (tab(TRUE, i) == FALSE)
            return FALSE;
          ind %= tabs;  /* how many spaces remaining */
      }
      if (ind > 0) {          /* only spaces now */
          autoindented += ind;
          if (linsert(ind, ' ') == FALSE)
            return FALSE;
      }
    }
    if (!autoindented)
      autoindented = -1;

    DOT.o += j;               /* put dot back pointing to the same text as before */
    return TRUE;
}

/* return the column indent of the specified line */
int
indentlen(LINE *lp)
{
    int ind, i, c;
    ind = 0;
    for (i = 0; i < llength(lp); ++i) {
      c = lgetc(lp, i);
      if (!isSpace(c))
          break;
      if (c == '\t')
          ind = next_tabcol(ind);
      else
          ++ind;
    }
    return ind;
}

/*
 * Insert a brace or paren into the text here... we are in CMODE
 *
 * n - repeat count
 * c - brace/paren to insert (always '}' or ')' for now)
 */
static int
insbrace(int n, int c)
{
#if ! OPT_CFENCE
    /* wouldn't want to back up from here, but fences might take us
       forward */
    /* if we are at the beginning of the line, no go */
    if (DOT.o <= w_left_margin(curwp))
      return (linsert(n, c));
#endif

    if (autoindented < 0) {
      return linsert(n, c);
    } else {
      (void) trimline((void *) 0, 0, 0);
      skipindent = 0;
#if ! OPT_CFENCE        /* no fences?  then put brace one tab in from previous line */
      doindent(((previndent(NULL) - 1) / tabstop_val(curbp)) * tabstop_val(curbp));
#else /* line up brace with the line containing its match */
      doindent(fmatchindent(c));
#endif
      autoindented = -1;

      /* and insert the required brace(s) */
      return (linsert(n, c));
    }
}

/* insert a # into the text here...we are in CMODE */
static int
inspound(void)
{
    /* if we are at the beginning of the line, no go */
    if (DOT.o <= w_left_margin(curwp))
      return (linsert(1, '#'));

    if (autoindented > 0) {   /* must all be whitespace before us */
      if (autoindented > llength(DOT.l))
          autoindented = llength(DOT.l);
      DOT.o = w_left_margin(curwp);
      (void) ldelete((B_COUNT) autoindented, FALSE);
    }
    autoindented = -1;

    /* and insert the required pound */
    return (linsert(1, '#'));
}

/* insert a tab into the file */
static int
tab(int f, int n)
{
    int ccol;
    if (!f)
      n = 1;
    if (n <= 0)
      return FALSE;

    if (b_val(curbp, MDTABINSERT))
      return linsert(n, '\t');

    ccol = getccol(FALSE);
    return linsert((next_tabcol(ccol) - ccol)
               + (n - 1) * tabstop_val(curbp), ' ');
}

/*ARGSUSED*/
static int
shiftwidth(int f GCC_UNUSED, int n GCC_UNUSED)
{
    int logical_col;
    int char_index;
    int space_count;
    int all_white;
    int add_spaces;
    int tabs = tabstop_val(curbp);
    int c;
    int s;

    char_index = DOT.o;

    /*
     * Compute the "logical" column; i.e. the column the cursor is in on the
     * screen.
     *
     * While we're at it, compute the spaces just before the insert point.
     */

    (void) gocol(0);
    logical_col = 0;
    space_count = 0;
    all_white = TRUE;
    while (DOT.o < char_index) {
      c = char_at(DOT);
      if (c == ' ') {
          space_count++;
      } else {
          space_count = 0;
      }
      if (!isSpace(c)) {
          all_white = FALSE;
      }

      if (c == '\t') {
          logical_col += tabs - (logical_col % tabs);
      } else {
          logical_col++;
      }

      DOT.o++;
    }

    DOT.o = char_index;

    /*
     * Now we can compute the destination column. If this is the same
     * as the tab column, delete the spaces before the insert point
     * & insert a tab; otherwise, insert spaces as required.
     */
    add_spaces = next_sw(logical_col) - logical_col;
    if (space_count + add_spaces > tabs) {
      space_count = tabs - add_spaces;
    }
    if (b_val(curbp, MDTABINSERT) &&
      ((add_spaces + logical_col) % tabs == 0)) {
      if (space_count > 0) {
          DOT.o -= space_count;
          s = ldelete((B_COUNT) space_count, FALSE);
      } else {
          space_count = 0;
          s = TRUE;
      }
      if (s) {
          space_count += add_spaces;
          s = linsert((space_count + tabs - 1) / tabs, '\t');
      }
    } else {
      s = linsert(add_spaces, ' ');
    }

    if (all_white && s) {
      if (autoindented >= 0) {
          int fc = firstchar(DOT.l);
          if (fc >= 0)
            autoindented = fc;
          else          /* all white */
            autoindented = llength(DOT.l);
      }
    }
    return s;
}

/*
 * Quote the next character, and insert it into the buffer. All the characters
 * are taken literally, with the exception of a) the newline, which always has
 * its line splitting meaning, and b) decimal digits, which are accumulated
 * (up to three of them) and the resulting value put in the buffer.
 *
 * A character is always read, even if it is inserted 0 times, for regularity.
 */
int
quote_next(int f, int n)
{
    int c, s;

    if (!f)
      n = 1;
    c = read_quoted(n, TRUE);

    if (c < 0) {
      return ABORT;
    } else if (c == '\n') {
      do {
          s = lnewline();
      } while ((s == TRUE) && (--n != 0));
      return s;
    } else {
      return linsert(n, c);
    }
}

#if OPT_EVAL
char *
current_modename(void)
{
    switch (savedmode) {
    default:
      return "command";
    case INSMODE_INS:
      return "insert";
    case INSMODE_OVR:
      return "overwrite";
    case INSMODE_RPL:
      return "replace";
    }
}
#endif

Generated by  Doxygen 1.6.0   Back to index