summaryrefslogtreecommitdiffstats
path: root/src/ops.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2004-06-13 20:20:40 +0000
committerBram Moolenaar <Bram@vim.org>2004-06-13 20:20:40 +0000
commit071d4279d6ab81b7187b48f3a0fc61e587b6db6c (patch)
tree221cbe3c40e043163c06f61c52a7ba2eb41e12ce /src/ops.c
parentb4210b3bc14e2918f153a7307530fbe6eba659e1 (diff)
updated for version 7.0001v7.0001
Diffstat (limited to 'src/ops.c')
-rw-r--r--src/ops.c6114
1 files changed, 6114 insertions, 0 deletions
diff --git a/src/ops.c b/src/ops.c
new file mode 100644
index 0000000000..e240c45f34
--- /dev/null
+++ b/src/ops.c
@@ -0,0 +1,6114 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * ops.c: implementation of various operators: op_shift, op_delete, op_tilde,
+ * op_change, op_yank, do_put, do_join
+ */
+
+#include "vim.h"
+
+/*
+ * Number of registers.
+ * 0 = unnamed register, for normal yanks and puts
+ * 1..9 = registers '1' to '9', for deletes
+ * 10..35 = registers 'a' to 'z'
+ * 36 = delete register '-'
+ * 37 = Selection register '*'. Only if FEAT_CLIPBOARD defined
+ * 38 = Clipboard register '+'. Only if FEAT_CLIPBOARD and FEAT_X11 defined
+ */
+/*
+ * Symbolic names for some registers.
+ */
+#define DELETION_REGISTER 36
+#ifdef FEAT_CLIPBOARD
+# define STAR_REGISTER 37
+# ifdef FEAT_X11
+# define PLUS_REGISTER 38
+# else
+# define PLUS_REGISTER STAR_REGISTER /* there is only one */
+# endif
+#endif
+#ifdef FEAT_DND
+# define TILDE_REGISTER (PLUS_REGISTER + 1)
+#endif
+
+#ifdef FEAT_CLIPBOARD
+# ifdef FEAT_DND
+# define NUM_REGISTERS (TILDE_REGISTER + 1)
+# else
+# define NUM_REGISTERS (PLUS_REGISTER + 1)
+# endif
+#else
+# define NUM_REGISTERS 37
+#endif
+
+/*
+ * Each yank register is an array of pointers to lines.
+ */
+static struct yankreg
+{
+ char_u **y_array; /* pointer to array of line pointers */
+ linenr_T y_size; /* number of lines in y_array */
+ char_u y_type; /* MLINE, MCHAR or MBLOCK */
+#ifdef FEAT_VISUAL
+ colnr_T y_width; /* only set if y_type == MBLOCK */
+#endif
+} y_regs[NUM_REGISTERS];
+
+static struct yankreg *y_current; /* ptr to current yankreg */
+static int y_append; /* TRUE when appending */
+static struct yankreg *y_previous = NULL; /* ptr to last written yankreg */
+
+/*
+ * structure used by block_prep, op_delete and op_yank for blockwise operators
+ * also op_change, op_shift, op_insert, op_replace - AKelly
+ */
+struct block_def
+{
+ int startspaces; /* 'extra' cols of first char */
+ int endspaces; /* 'extra' cols of first char */
+ int textlen; /* chars in block */
+ char_u *textstart; /* pointer to 1st char in block */
+ colnr_T textcol; /* cols of chars (at least part.) in block */
+ colnr_T start_vcol; /* start col of 1st char wholly inside block */
+ colnr_T end_vcol; /* start col of 1st char wholly after block */
+#ifdef FEAT_VISUALEXTRA
+ int is_short; /* TRUE if line is too short to fit in block */
+ int is_MAX; /* TRUE if curswant==MAXCOL when starting */
+ int is_oneChar; /* TRUE if block within one character */
+ int pre_whitesp; /* screen cols of ws before block */
+ int pre_whitesp_c; /* chars of ws before block */
+ colnr_T end_char_vcols; /* number of vcols of post-block char */
+#endif
+ colnr_T start_char_vcols; /* number of vcols of pre-block char */
+};
+
+#ifdef FEAT_VISUALEXTRA
+static void shift_block __ARGS((oparg_T *oap, int amount));
+static void block_insert __ARGS((oparg_T *oap, char_u *s, int b_insert, struct block_def*bdp));
+#endif
+static void get_yank_register __ARGS((int regname, int writing));
+static int stuff_yank __ARGS((int, char_u *));
+static void put_reedit_in_typebuf __ARGS((void));
+static int put_in_typebuf __ARGS((char_u *s, int colon));
+static void stuffescaped __ARGS((char_u *arg, int literally));
+static int get_spec_reg __ARGS((int regname, char_u **argp, int *allocated, int errmsg));
+static void cmdline_paste_str __ARGS((char_u *s, int literally));
+#ifdef FEAT_MBYTE
+static void mb_adjust_opend __ARGS((oparg_T *oap));
+#endif
+static void free_yank __ARGS((long));
+static void free_yank_all __ARGS((void));
+static int yank_copy_line __ARGS((struct block_def *bd, long y_idx));
+#ifdef FEAT_CLIPBOARD
+static void copy_yank_reg __ARGS((struct yankreg *reg));
+# if defined(FEAT_VISUAL) || defined(FEAT_EVAL)
+static void may_set_selection __ARGS((void));
+# endif
+#endif
+static void dis_msg __ARGS((char_u *p, int skip_esc));
+#ifdef FEAT_VISUAL
+static void block_prep __ARGS((oparg_T *oap, struct block_def *, linenr_T, int));
+#endif
+#if defined(FEAT_CLIPBOARD) || defined(FEAT_EVAL)
+static void str_to_reg __ARGS((struct yankreg *y_ptr, int type, char_u *str, long len, long blocklen));
+#endif
+static int ends_in_white __ARGS((linenr_T lnum));
+#ifdef FEAT_COMMENTS
+static int same_leader __ARGS((linenr_T lnum, int, char_u *, int, char_u *));
+static int fmt_check_par __ARGS((linenr_T, int *, char_u **, int do_comments));
+#else
+static int fmt_check_par __ARGS((linenr_T));
+#endif
+
+/*
+ * The names of operators.
+ * IMPORTANT: Index must correspond with defines in vim.h!!!
+ * The third field indicates whether the operator always works on lines.
+ */
+static char opchars[][3] =
+{
+ {NUL, NUL, FALSE}, /* OP_NOP */
+ {'d', NUL, FALSE}, /* OP_DELETE */
+ {'y', NUL, FALSE}, /* OP_YANK */
+ {'c', NUL, FALSE}, /* OP_CHANGE */
+ {'<', NUL, TRUE}, /* OP_LSHIFT */
+ {'>', NUL, TRUE}, /* OP_RSHIFT */
+ {'!', NUL, TRUE}, /* OP_FILTER */
+ {'g', '~', FALSE}, /* OP_TILDE */
+ {'=', NUL, TRUE}, /* OP_INDENT */
+ {'g', 'q', TRUE}, /* OP_FORMAT */
+ {':', NUL, TRUE}, /* OP_COLON */
+ {'g', 'U', FALSE}, /* OP_UPPER */
+ {'g', 'u', FALSE}, /* OP_LOWER */
+ {'J', NUL, TRUE}, /* DO_JOIN */
+ {'g', 'J', TRUE}, /* DO_JOIN_NS */
+ {'g', '?', FALSE}, /* OP_ROT13 */
+ {'r', NUL, FALSE}, /* OP_REPLACE */
+ {'I', NUL, FALSE}, /* OP_INSERT */
+ {'A', NUL, FALSE}, /* OP_APPEND */
+ {'z', 'f', TRUE}, /* OP_FOLD */
+ {'z', 'o', TRUE}, /* OP_FOLDOPEN */
+ {'z', 'O', TRUE}, /* OP_FOLDOPENREC */
+ {'z', 'c', TRUE}, /* OP_FOLDCLOSE */
+ {'z', 'C', TRUE}, /* OP_FOLDCLOSEREC */
+ {'z', 'd', TRUE}, /* OP_FOLDDEL */
+ {'z', 'D', TRUE}, /* OP_FOLDDELREC */
+ {'g', 'w', TRUE}, /* OP_FORMAT2 */
+};
+
+/*
+ * Translate a command name into an operator type.
+ * Must only be called with a valid operator name!
+ */
+ int
+get_op_type(char1, char2)
+ int char1;
+ int char2;
+{
+ int i;
+
+ if (char1 == 'r') /* ignore second character */
+ return OP_REPLACE;
+ if (char1 == '~') /* when tilde is an operator */
+ return OP_TILDE;
+ for (i = 0; ; ++i)
+ if (opchars[i][0] == char1 && opchars[i][1] == char2)
+ break;
+ return i;
+}
+
+#if defined(FEAT_VISUAL) || defined(PROTO)
+/*
+ * Return TRUE if operator "op" always works on whole lines.
+ */
+ int
+op_on_lines(op)
+ int op;
+{
+ return opchars[op][2];
+}
+#endif
+
+/*
+ * Get first operator command character.
+ * Returns 'g' or 'z' if there is another command character.
+ */
+ int
+get_op_char(optype)
+ int optype;
+{
+ return opchars[optype][0];
+}
+
+/*
+ * Get second operator command character.
+ */
+ int
+get_extra_op_char(optype)
+ int optype;
+{
+ return opchars[optype][1];
+}
+
+/*
+ * op_shift - handle a shift operation
+ */
+ void
+op_shift(oap, curs_top, amount)
+ oparg_T *oap;
+ int curs_top;
+ int amount;
+{
+ long i;
+ int first_char;
+ char_u *s;
+#ifdef FEAT_VISUAL
+ int block_col = 0;
+#endif
+
+ if (u_save((linenr_T)(oap->start.lnum - 1),
+ (linenr_T)(oap->end.lnum + 1)) == FAIL)
+ return;
+
+#ifdef FEAT_VISUAL
+ if (oap->block_mode)
+ block_col = curwin->w_cursor.col;
+#endif
+
+ for (i = oap->line_count; --i >= 0; )
+ {
+ first_char = *ml_get_curline();
+ if (first_char == NUL) /* empty line */
+ curwin->w_cursor.col = 0;
+#ifdef FEAT_VISUALEXTRA
+ else if (oap->block_mode)
+ shift_block(oap, amount);
+#endif
+ else
+ /* Move the line right if it doesn't start with '#', 'smartindent'
+ * isn't set or 'cindent' isn't set or '#' isn't in 'cino'. */
+#if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT)
+ if (first_char != '#' || !preprocs_left())
+#endif
+ {
+ shift_line(oap->op_type == OP_LSHIFT, p_sr, amount);
+ }
+ ++curwin->w_cursor.lnum;
+ }
+
+ changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
+
+#ifdef FEAT_VISUAL
+ if (oap->block_mode)
+ {
+ curwin->w_cursor.lnum = oap->start.lnum;
+ curwin->w_cursor.col = block_col;
+ }
+ else
+#endif
+ if (curs_top) /* put cursor on first line, for ">>" */
+ {
+ curwin->w_cursor.lnum = oap->start.lnum;
+ beginline(BL_SOL | BL_FIX); /* shift_line() may have set cursor.col */
+ }
+ else
+ --curwin->w_cursor.lnum; /* put cursor on last line, for ":>" */
+
+ if (oap->line_count > p_report)
+ {
+ if (oap->op_type == OP_RSHIFT)
+ s = (char_u *)">";
+ else
+ s = (char_u *)"<";
+ if (oap->line_count == 1)
+ {
+ if (amount == 1)
+ sprintf((char *)IObuff, _("1 line %sed 1 time"), s);
+ else
+ sprintf((char *)IObuff, _("1 line %sed %d times"), s, amount);
+ }
+ else
+ {
+ if (amount == 1)
+ sprintf((char *)IObuff, _("%ld lines %sed 1 time"),
+ oap->line_count, s);
+ else
+ sprintf((char *)IObuff, _("%ld lines %sed %d times"),
+ oap->line_count, s, amount);
+ }
+ msg(IObuff);
+ }
+
+ /*
+ * Set "'[" and "']" marks.
+ */
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end.lnum = oap->end.lnum;
+ curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
+ if (curbuf->b_op_end.col > 0)
+ --curbuf->b_op_end.col;
+}
+
+/*
+ * shift the current line one shiftwidth left (if left != 0) or right
+ * leaves cursor on first blank in the line
+ */
+ void
+shift_line(left, round, amount)
+ int left;
+ int round;
+ int amount;
+{
+ int count;
+ int i, j;
+ int p_sw = (int)curbuf->b_p_sw;
+
+ count = get_indent(); /* get current indent */
+
+ if (round) /* round off indent */
+ {
+ i = count / p_sw; /* number of p_sw rounded down */
+ j = count % p_sw; /* extra spaces */
+ if (j && left) /* first remove extra spaces */
+ --amount;
+ if (left)
+ {
+ i -= amount;
+ if (i < 0)
+ i = 0;
+ }
+ else
+ i += amount;
+ count = i * p_sw;
+ }
+ else /* original vi indent */
+ {
+ if (left)
+ {
+ count -= p_sw * amount;
+ if (count < 0)
+ count = 0;
+ }
+ else
+ count += p_sw * amount;
+ }
+
+ /* Set new indent */
+#ifdef FEAT_VREPLACE
+ if (State & VREPLACE_FLAG)
+ change_indent(INDENT_SET, count, FALSE, NUL);
+ else
+#endif
+ (void)set_indent(count, SIN_CHANGED);
+}
+
+#if defined(FEAT_VISUALEXTRA) || defined(PROTO)
+/*
+ * Shift one line of the current block one shiftwidth right or left.
+ * Leaves cursor on first character in block.
+ */
+ static void
+shift_block(oap, amount)
+ oparg_T *oap;
+ int amount;
+{
+ int left = (oap->op_type == OP_LSHIFT);
+ int oldstate = State;
+ int total, split;
+ char_u *newp, *oldp, *midp, *ptr;
+ int oldcol = curwin->w_cursor.col;
+ int p_sw = (int)curbuf->b_p_sw;
+ int p_ts = (int)curbuf->b_p_ts;
+ struct block_def bd;
+ int internal = 0;
+ int incr;
+ colnr_T vcol, col = 0, ws_vcol;
+ int i = 0, j = 0;
+ int len;
+
+#ifdef FEAT_RIGHTLEFT
+ int old_p_ri = p_ri;
+
+ p_ri = 0; /* don't want revins in ident */
+#endif
+
+ State = INSERT; /* don't want REPLACE for State */
+ block_prep(oap, &bd, curwin->w_cursor.lnum, TRUE);
+ if (bd.is_short)
+ return;
+
+ /* total is number of screen columns to be inserted/removed */
+ total = amount * p_sw;
+ oldp = ml_get_curline();
+
+ if (!left)
+ {
+ /*
+ * 1. Get start vcol
+ * 2. Total ws vcols
+ * 3. Divvy into TABs & spp
+ * 4. Construct new string
+ */
+ total += bd.pre_whitesp; /* all virtual WS upto & incl a split TAB */
+ ws_vcol = bd.start_vcol - bd.pre_whitesp;
+ if (bd.startspaces)
+ {
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ bd.textstart += (*mb_ptr2len_check)(bd.textstart);
+#endif
+ ++bd.textstart;
+ }
+ for ( ; vim_iswhite(*bd.textstart); )
+ {
+ incr = lbr_chartabsize_adv(&bd.textstart, (colnr_T)(bd.start_vcol));
+ total += incr;
+ bd.start_vcol += incr;
+ }
+ /* OK, now total=all the VWS reqd, and textstart points at the 1st
+ * non-ws char in the block. */
+ if (!curbuf->b_p_et)
+ i = ((ws_vcol % p_ts) + total) / p_ts; /* number of tabs */
+ if (i)
+ j = ((ws_vcol % p_ts) + total) % p_ts; /* number of spp */
+ else
+ j = total;
+ /* if we're splitting a TAB, allow for it */
+ bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0);
+ len = (int)STRLEN(bd.textstart) + 1;
+ newp = alloc_check((unsigned)(bd.textcol + i + j + len));
+ if (newp == NULL)
+ return;
+ vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + len));
+ mch_memmove(newp, oldp, (size_t)bd.textcol);
+ copy_chars(newp + bd.textcol, (size_t)i, TAB);
+ copy_spaces(newp + bd.textcol + i, (size_t)j);
+ /* the end */
+ mch_memmove(newp + bd.textcol + i + j, bd.textstart, (size_t)len);
+ }
+ else /* left */
+ {
+ vcol = oap->start_vcol;
+ /* walk vcol past ws to be removed */
+ for (midp = oldp + bd.textcol;
+ vcol < (oap->start_vcol + total) && vim_iswhite(*midp); )
+ {
+ incr = lbr_chartabsize_adv(&midp, (colnr_T)vcol);
+ vcol += incr;
+ }
+ /* internal is the block-internal ws replacing a split TAB */
+ if (vcol > (oap->start_vcol + total))
+ {
+ /* we have to split the TAB *(midp-1) */
+ internal = vcol - (oap->start_vcol + total);
+ }
+ /* if 'expandtab' is not set, use TABs */
+
+ split = bd.startspaces + internal;
+ if (split > 0)
+ {
+ if (!curbuf->b_p_et)
+ {
+ for (ptr = oldp, col = 0; ptr < oldp+bd.textcol; )
+ col += lbr_chartabsize_adv(&ptr, (colnr_T)col);
+
+ /* col+1 now equals the start col of the first char of the
+ * block (may be < oap.start_vcol if we're splitting a TAB) */
+ i = ((col % p_ts) + split) / p_ts; /* number of tabs */
+ }
+ if (i)
+ j = ((col % p_ts) + split) % p_ts; /* number of spp */
+ else
+ j = split;
+ }
+
+ newp = alloc_check(bd.textcol + i + j + (unsigned)STRLEN(midp) + 1);
+ if (newp == NULL)
+ return;
+ vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + STRLEN(midp) + 1));
+
+ /* copy first part we want to keep */
+ mch_memmove(newp, oldp, (size_t)bd.textcol);
+ /* Now copy any TABS and spp to ensure correct alignment! */
+ while (vim_iswhite(*midp))
+ {
+ if (*midp == TAB)
+ i++;
+ else /*space */
+ j++;
+ midp++;
+ }
+ /* We might have an extra TAB worth of spp now! */
+ if (j / p_ts && !curbuf->b_p_et)
+ {
+ i++;
+ j -= p_ts;
+ }
+ copy_chars(newp + bd.textcol, (size_t)i, TAB);
+ copy_spaces(newp + bd.textcol + i, (size_t)j);
+
+ /* the end */
+ mch_memmove(newp + STRLEN(newp), midp, (size_t)STRLEN(midp) + 1);
+ }
+ /* replace the line */
+ ml_replace(curwin->w_cursor.lnum, newp, FALSE);
+ changed_bytes(curwin->w_cursor.lnum, (colnr_T)bd.textcol);
+ State = oldstate;
+ curwin->w_cursor.col = oldcol;
+#ifdef FEAT_RIGHTLEFT
+ p_ri = old_p_ri;
+#endif
+}
+#endif
+
+#ifdef FEAT_VISUALEXTRA
+/*
+ * Insert string "s" (b_insert ? before : after) block :AKelly
+ * Caller must prepare for undo.
+ */
+ static void
+block_insert(oap, s, b_insert, bdp)
+ oparg_T *oap;
+ char_u *s;
+ int b_insert;
+ struct block_def *bdp;
+{
+ int p_ts;
+ int count = 0; /* extra spaces to replace a cut TAB */
+ int spaces = 0; /* non-zero if cutting a TAB */
+ colnr_T offset; /* pointer along new line */
+ unsigned s_len; /* STRLEN(s) */
+ char_u *newp, *oldp; /* new, old lines */
+ linenr_T lnum; /* loop var */
+ int oldstate = State;
+
+ State = INSERT; /* don't want REPLACE for State */
+ s_len = (unsigned)STRLEN(s);
+
+ for (lnum = oap->start.lnum + 1; lnum <= oap->end.lnum; lnum++)
+ {
+ block_prep(oap, bdp, lnum, TRUE);
+ if (bdp->is_short && b_insert)
+ continue; /* OP_INSERT, line ends before block start */
+
+ oldp = ml_get(lnum);
+
+ if (b_insert)
+ {
+ p_ts = bdp->start_char_vcols;
+ spaces = bdp->startspaces;
+ if (spaces != 0)
+ count = p_ts - 1; /* we're cutting a TAB */
+ offset = bdp->textcol;
+ }
+ else /* append */
+ {
+ p_ts = bdp->end_char_vcols;
+ if (!bdp->is_short) /* spaces = padding after block */
+ {
+ spaces = (bdp->endspaces ? p_ts - bdp->endspaces : 0);
+ if (spaces != 0)
+ count = p_ts - 1; /* we're cutting a TAB */
+ offset = bdp->textcol + bdp->textlen - (spaces != 0);
+ }
+ else /* spaces = padding to block edge */
+ {
+ /* if $ used, just append to EOL (ie spaces==0) */
+ if (!bdp->is_MAX)
+ spaces = (oap->end_vcol - bdp->end_vcol) + 1;
+ count = spaces;
+ offset = bdp->textcol + bdp->textlen;
+ }
+ }
+
+ newp = alloc_check((unsigned)(STRLEN(oldp)) + s_len + count + 1);
+ if (newp == NULL)
+ continue;
+
+ /* copy up to shifted part */
+ mch_memmove(newp, oldp, (size_t)(offset));
+ oldp += offset;
+
+ /* insert pre-padding */
+ copy_spaces(newp + offset, (size_t)spaces);
+
+ /* copy the new text */
+ mch_memmove(newp + offset + spaces, s, (size_t)s_len);
+ offset += s_len;
+
+ if (spaces && !bdp->is_short)
+ {
+ /* insert post-padding */
+ copy_spaces(newp + offset + spaces, (size_t)(p_ts - spaces));
+ /* We're splitting a TAB, don't copy it. */
+ oldp++;
+ /* We allowed for that TAB, remember this now */
+ count++;
+ }
+
+ if (spaces > 0)
+ offset += count;
+ mch_memmove(newp + offset, oldp, (size_t)(STRLEN(oldp) + 1));
+
+ ml_replace(lnum, newp, FALSE);
+
+ if (lnum == oap->end.lnum)
+ {
+ /* Set "']" mark to the end of the block instead of the end of
+ * the insert in the first line. */
+ curbuf->b_op_end.lnum = oap->end.lnum;
+ curbuf->b_op_end.col = offset;
+ }
+ } /* for all lnum */
+
+ changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L);
+
+ State = oldstate;
+}
+#endif
+
+#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO)
+/*
+ * op_reindent - handle reindenting a block of lines.
+ */
+ void
+op_reindent(oap, how)
+ oparg_T *oap;
+ int (*how) __ARGS((void));
+{
+ long i;
+ char_u *l;
+ int count;
+ linenr_T first_changed = 0;
+ linenr_T last_changed = 0;
+ linenr_T start_lnum = curwin->w_cursor.lnum;
+
+ for (i = oap->line_count; --i >= 0 && !got_int; )
+ {
+ /* it's a slow thing to do, so give feedback so there's no worry that
+ * the computer's just hung. */
+
+ if (i > 1
+ && (i % 50 == 0 || i == oap->line_count - 1)
+ && oap->line_count > p_report)
+ smsg((char_u *)_("%ld lines to indent... "), i);
+
+ /*
+ * Be vi-compatible: For lisp indenting the first line is not
+ * indented, unless there is only one line.
+ */
+#ifdef FEAT_LISP
+ if (i != oap->line_count - 1 || oap->line_count == 1
+ || how != get_lisp_indent)
+#endif
+ {
+ l = skipwhite(ml_get_curline());
+ if (*l == NUL) /* empty or blank line */
+ count = 0;
+ else
+ count = how(); /* get the indent for this line */
+
+ if (set_indent(count, SIN_UNDO))
+ {
+ /* did change the indent, call changed_lines() later */
+ if (first_changed == 0)
+ first_changed = curwin->w_cursor.lnum;
+ last_changed = curwin->w_cursor.lnum;
+ }
+ }
+ ++curwin->w_cursor.lnum;
+ }
+
+ /* put cursor on first non-blank of indented line */
+ curwin->w_cursor.lnum = start_lnum;
+ beginline(BL_SOL | BL_FIX);
+
+ /* Mark changed lines so that they will be redrawn. When Visual
+ * highlighting was present, need to continue until the last line. When
+ * there is no change still need to remove the Visual highlighting. */
+ if (last_changed != 0)
+ changed_lines(first_changed, 0,
+#ifdef FEAT_VISUAL
+ oap->is_VIsual ? start_lnum + oap->line_count :
+#endif
+ last_changed + 1, 0L);
+#ifdef FEAT_VISUAL
+ else if (oap->is_VIsual)
+ redraw_curbuf_later(INVERTED);
+#endif
+
+ if (oap->line_count > p_report)
+ {
+ i = oap->line_count - (i + 1);
+ if (i == 1)
+ MSG(_("1 line indented "));
+ else
+ smsg((char_u *)_("%ld lines indented "), i);
+ }
+ /* set '[ and '] marks */
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+}
+#endif /* defined(FEAT_LISP) || defined(FEAT_CINDENT) */
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Keep the last expression line here, for repeating.
+ */
+static char_u *expr_line = NULL;
+
+/*
+ * Get an expression for the "\"=expr1" or "CTRL-R =expr1"
+ * Returns '=' when OK, NUL otherwise.
+ */
+ int
+get_expr_register()
+{
+ char_u *new_line;
+
+ new_line = getcmdline('=', 0L, 0);
+ if (new_line == NULL)
+ return NUL;
+ if (*new_line == NUL) /* use previous line */
+ vim_free(new_line);
+ else
+ set_expr_line(new_line);
+ return '=';
+}
+
+/*
+ * Set the expression for the '=' register.
+ * Argument must be an allocated string.
+ */
+ void
+set_expr_line(new_line)
+ char_u *new_line;
+{
+ vim_free(expr_line);
+ expr_line = new_line;
+}
+
+/*
+ * Get the result of the '=' register expression.
+ * Returns a pointer to allocated memory, or NULL for failure.
+ */
+ char_u *
+get_expr_line()
+{
+ char_u *expr_copy;
+ char_u *rv;
+
+ if (expr_line == NULL)
+ return NULL;
+
+ /* Make a copy of the expression, because evaluating it may cause it to be
+ * changed. */
+ expr_copy = vim_strsave(expr_line);
+ if (expr_copy == NULL)
+ return NULL;
+
+ rv = eval_to_string(expr_copy, NULL);
+ vim_free(expr_copy);
+ return rv;
+}
+#endif /* FEAT_EVAL */
+
+/*
+ * Check if 'regname' is a valid name of a yank register.
+ * Note: There is no check for 0 (default register), caller should do this
+ */
+ int
+valid_yank_reg(regname, writing)
+ int regname;
+ int writing; /* if TRUE check for writable registers */
+{
+ if ( (regname > 0 && ASCII_ISALNUM(regname))
+ || (!writing && vim_strchr((char_u *)
+#ifdef FEAT_EVAL
+ "/.%#:="
+#else
+ "/.%#:"
+#endif
+ , regname) != NULL)
+ || regname == '"'
+ || regname == '-'
+ || regname == '_'
+#ifdef FEAT_CLIPBOARD
+ || regname == '*'
+ || regname == '+'
+#endif
+#ifdef FEAT_DND
+ || (!writing && regname == '~')
+#endif
+ )
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Set y_current and y_append, according to the value of "regname".
+ * Cannot handle the '_' register.
+ *
+ * If regname is 0 and writing, use register 0
+ * If regname is 0 and reading, use previous register
+ */
+ static void
+get_yank_register(regname, writing)
+ int regname;
+ int writing;
+{
+ int i;
+
+ y_append = FALSE;
+ if ((regname == 0 || regname == '"') && !writing && y_previous != NULL)
+ {
+ y_current = y_previous;
+ return;
+ }
+ i = regname;
+ if (VIM_ISDIGIT(i))
+ i -= '0';
+ else if (ASCII_ISLOWER(i))
+ i = CharOrdLow(i) + 10;
+ else if (ASCII_ISUPPER(i))
+ {
+ i = CharOrdUp(i) + 10;
+ y_append = TRUE;
+ }
+ else if (regname == '-')
+ i = DELETION_REGISTER;
+#ifdef FEAT_CLIPBOARD
+ /* When selection is not available, use register 0 instead of '*' */
+ else if (clip_star.available && regname == '*')
+ i = STAR_REGISTER;
+ /* When clipboard is not available, use register 0 instead of '+' */
+ else if (clip_plus.available && regname == '+')
+ i = PLUS_REGISTER;
+#endif
+#ifdef FEAT_DND
+ else if (!writing && regname == '~')
+ i = TILDE_REGISTER;
+#endif
+ else /* not 0-9, a-z, A-Z or '-': use register 0 */
+ i = 0;
+ y_current = &(y_regs[i]);
+ if (writing) /* remember the register we write into for do_put() */
+ y_previous = y_current;
+}
+
+#ifdef FEAT_CLIPBOARD
+/*
+ * When "regname" is a clipboard register, obtain the selection. If it's not
+ * available return zero, otherwise return "regname".
+ */
+static int may_get_selection __ARGS((int regname));
+
+ static int
+may_get_selection(regname)
+ int regname;
+{
+ if (regname == '*')
+ {
+ if (!clip_star.available)
+ regname = 0;
+ else
+ clip_get_selection(&clip_star);
+ }
+ else if (regname == '+')
+ {
+ if (!clip_plus.available)
+ regname = 0;
+ else
+ clip_get_selection(&clip_plus);
+ }
+ return regname;
+}
+#endif
+
+#if defined(FEAT_VISUAL) || defined(PROTO)
+/*
+ * Obtain the contents of a "normal" register. The register is made empty.
+ * The returned pointer has allocated memory, use put_register() later.
+ */
+ void *
+get_register(name, copy)
+ int name;
+ int copy; /* make a copy, if FALSE make register empty. */
+{
+ static struct yankreg *reg;
+ int i;
+
+#ifdef FEAT_CLIPBOARD
+ /* When Visual area changed, may have to update selection. Obtain the
+ * selection too. */
+ if (name == '*' && clip_star.available && clip_isautosel())
+ {
+ clip_update_selection();
+ may_get_selection(name);
+ }
+#endif
+
+ get_yank_register(name, 0);
+ reg = (struct yankreg *)alloc((unsigned)sizeof(struct yankreg));
+ if (reg != NULL)
+ {
+ *reg = *y_current;
+ if (copy)
+ {
+ /* If we run out of memory some or all of the lines are empty. */
+ if (reg->y_size == 0)
+ reg->y_array = NULL;
+ else
+ reg->y_array = (char_u **)alloc((unsigned)(sizeof(char_u *)
+ * reg->y_size));
+ if (reg->y_array != NULL)
+ {
+ for (i = 0; i < reg->y_size; ++i)
+ reg->y_array[i] = vim_strsave(y_current->y_array[i]);
+ }
+ }
+ else
+ y_current->y_array = NULL;
+ }
+ return (void *)reg;
+}
+
+/*
+ * Put "reg" into register "name". Free any previous contents.
+ */
+ void
+put_register(name, reg)
+ int name;
+ void *reg;
+{
+ get_yank_register(name, 0);
+ free_yank_all();
+ *y_current = *(struct yankreg *)reg;
+
+# ifdef FEAT_CLIPBOARD
+ /* Send text written to clipboard register to the clipboard. */
+ may_set_selection();
+# endif
+}
+#endif
+
+#if defined(FEAT_MOUSE) || defined(PROTO)
+/*
+ * return TRUE if the current yank register has type MLINE
+ */
+ int
+yank_register_mline(regname)
+ int regname;
+{
+ if (regname != 0 && !valid_yank_reg(regname, FALSE))
+ return FALSE;
+ if (regname == '_') /* black hole is always empty */
+ return FALSE;
+ get_yank_register(regname, FALSE);
+ return (y_current->y_type == MLINE);
+}
+#endif
+
+/*
+ * start or stop recording into a yank register
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_record(c)
+ int c;
+{
+ char_u *p;
+ static int regname;
+ struct yankreg *old_y_previous, *old_y_current;
+ int retval;
+
+ if (Recording == FALSE) /* start recording */
+ {
+ /* registers 0-9, a-z and " are allowed */
+ if (c < 0 || (!ASCII_ISALNUM(c) && c != '"'))
+ retval = FAIL;
+ else
+ {
+ Recording = TRUE;
+ showmode();
+ regname = c;
+ retval = OK;
+ }
+ }
+ else /* stop recording */
+ {
+ /*
+ * Get the recorded key hits. K_SPECIAL and CSI will be escaped, so
+ * that the register can be put into the typeahead buffer without
+ * translation.
+ */
+ Recording = FALSE;
+ MSG("");
+ p = get_recorded();
+ if (p == NULL)
+ retval = FAIL;
+ else
+ {
+ /*
+ * We don't want to change the default register here, so save and
+ * restore the current register name.
+ */
+ old_y_previous = y_previous;
+ old_y_current = y_current;
+
+ retval = stuff_yank(regname, p);
+
+ y_previous = old_y_previous;
+ y_current = old_y_current;
+ }
+ }
+ return retval;
+}
+
+/*
+ * Stuff string "p" into yank register "regname" as a single line (append if
+ * uppercase). "p" must have been alloced.
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ static int
+stuff_yank(regname, p)
+ int regname;
+ char_u *p;
+{
+ char_u *lp;
+ char_u **pp;
+
+ /* check for read-only register */
+ if (regname != 0 && !valid_yank_reg(regname, TRUE))
+ {
+ vim_free(p);
+ return FAIL;
+ }
+ if (regname == '_') /* black hole: don't do anything */
+ {
+ vim_free(p);
+ return OK;
+ }
+ get_yank_register(regname, TRUE);
+ if (y_append && y_current->y_array != NULL)
+ {
+ pp = &(y_current->y_array[y_current->y_size - 1]);
+ lp = lalloc((long_u)(STRLEN(*pp) + STRLEN(p) + 1), TRUE);
+ if (lp == NULL)
+ {
+ vim_free(p);
+ return FAIL;
+ }
+ STRCPY(lp, *pp);
+ STRCAT(lp, p);
+ vim_free(p);
+ vim_free(*pp);
+ *pp = lp;
+ }
+ else
+ {
+ free_yank_all();
+ if ((y_current->y_array =
+ (char_u **)alloc((unsigned)sizeof(char_u *))) == NULL)
+ {
+ vim_free(p);
+ return FAIL;
+ }
+ y_current->y_array[0] = p;
+ y_current->y_size = 1;
+ y_current->y_type = MCHAR; /* used to be MLINE, why? */
+ }
+ return OK;
+}
+
+/*
+ * execute a yank register: copy it into the stuff buffer
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+do_execreg(regname, colon, addcr)
+ int regname;
+ int colon; /* insert ':' before each line */
+ int addcr; /* always add '\n' to end of line */
+{
+ static int lastc = NUL;
+ long i;
+ char_u *p;
+ int retval = OK;
+ int remap;
+
+ if (regname == '@') /* repeat previous one */
+ regname = lastc;
+ /* check for valid regname */
+ if (regname == '%' || regname == '#' || !valid_yank_reg(regname, FALSE))
+ return FAIL;
+ lastc = regname;
+
+#ifdef FEAT_CLIPBOARD
+ regname = may_get_selection(regname);
+#endif
+
+ if (regname == '_') /* black hole: don't stuff anything */
+ return OK;
+
+#ifdef FEAT_CMDHIST
+ if (regname == ':') /* use last command line */
+ {
+ if (last_cmdline == NULL)
+ {
+ EMSG(_(e_nolastcmd));
+ return FAIL;
+ }
+ vim_free(new_last_cmdline); /* don't keep the cmdline containing @: */
+ new_last_cmdline = NULL;
+ retval = put_in_typebuf(last_cmdline, TRUE);
+ }
+#endif
+#ifdef FEAT_EVAL
+ else if (regname == '=')
+ {
+ p = get_expr_line();
+ if (p == NULL)
+ return FAIL;
+ retval = put_in_typebuf(p, colon);
+ vim_free(p);
+ }
+#endif
+ else if (regname == '.') /* use last inserted text */
+ {
+ p = get_last_insert_save();
+ if (p == NULL)
+ {
+ EMSG(_(e_noinstext));
+ return FAIL;
+ }
+ retval = put_in_typebuf(p, colon);
+ vim_free(p);
+ }
+ else
+ {
+ get_yank_register(regname, FALSE);
+ if (y_current->y_array == NULL)
+ return FAIL;
+
+ /* Disallow remaping for ":@r". */
+ remap = colon ? REMAP_NONE : REMAP_YES;
+
+ /*
+ * Insert lines into typeahead buffer, from last one to first one.
+ */
+ put_reedit_in_typebuf();
+ for (i = y_current->y_size; --i >= 0; )
+ {
+ /* insert NL between lines and after last line if type is MLINE */
+ if (y_current->y_type == MLINE || i < y_current->y_size - 1
+ || addcr)
+ {
+ if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, FALSE) == FAIL)
+ return FAIL;
+ }
+ if (ins_typebuf(y_current->y_array[i], remap, 0, TRUE, FALSE)
+ == FAIL)
+ return FAIL;
+ if (colon && ins_typebuf((char_u *)":", remap, 0, TRUE, FALSE)
+ == FAIL)
+ return FAIL;
+ }
+ Exec_reg = TRUE; /* disable the 'q' command */
+ }
+ return retval;
+}
+
+/*
+ * If "restart_edit" is not zero, put it in the typeahead buffer, so that it's
+ * used only after other typeahead has been processed.
+ */
+ static void
+put_reedit_in_typebuf()
+{
+ char_u buf[3];
+
+ if (restart_edit != NUL)
+ {
+ if (restart_edit == 'V')
+ {
+ buf[0] = 'g';
+ buf[1] = 'R';
+ buf[2] = NUL;
+ }
+ else
+ {