/branches/vim7.0/src/ops.c
C | 2620 lines | 2369 code | 81 blank | 170 comment | 200 complexity | fb88e3a9d9493fafc551190e91b63e0f MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /* 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 int stuff_yank __ARGS((int, char_u *));
- static void put_reedit_in_typebuf __ARGS((int silent));
- static int put_in_typebuf __ARGS((char_u *s, int esc, int colon,
- int silent));
- static void stuffescaped __ARGS((char_u *arg, 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 */
- {'g', '@', FALSE}, /* OP_FUNCTION */
- };
- /*
- * 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)(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;
- /* Don't even try when 'modifiable' is off. */
- if (!curbuf->b_p_ma)
- {
- EMSG(_(e_modifiable));
- return;
- }
- 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;
- static int nested = 0;
- 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;
- /* When we are invoked recursively limit the evaluation to 10 levels.
- * Then return the string as-is. */
- if (nested >= 10)
- return expr_copy;
- ++nested;
- rv = eval_to_string(expr_copy, NULL, TRUE);
- --nested;
- vim_free(expr_copy);
- return rv;
- }
- /*
- * Get the '=' register expression itself, without evaluating it.
- */
- char_u *
- get_expr_line_src()
- {
- if (expr_line == NULL)
- return NULL;
- return vim_strsave(expr_line);
- }
- #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.
- * Must only be called with a valid register name!
- *
- * If regname is 0 and writing, use register 0
- * If regname is 0 and reading, use previous register
- */
- 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;
- }
- #if defined(FEAT_CLIPBOARD) || defined(PROTO)
- /*
- * When "regname" is a clipboard register, obtain the selection. If it's not
- * available return zero, otherwise return "regname".
- */
- 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
- {
- /* Remove escaping for CSI and K_SPECIAL in multi-byte chars. */
- vim_unescape_csi(p);
- /*
- * 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, silent)
- int regname;
- int colon; /* insert ':' before each line */
- int addcr; /* always add '\n' to end of line */
- int silent; /* set "silent" flag in typeahead buffer */
- {
- static int lastc = NUL;
- long i;
- char_u *p;
- int retval = OK;
- int remap;
- if (regname == '@') /* repeat previous one */
- {
- if (lastc == NUL)
- {
- EMSG(_("E748: No previously used register"));
- return FAIL;
- }
- regname = lastc;
- }
- /* check for valid regname */
- if (regname == '%' || regname == '#' || !valid_yank_reg(regname, FALSE))
- {
- emsg_invreg(regname);
- 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;
- /* Escape all control characters with a CTRL-V */
- p = vim_strsave_escaped_ext(last_cmdline,
- (char_u *)"\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037", Ctrl_V, FALSE);
- if (p != NULL)
- {
- /* When in Visual mode "'<,'>" will be prepended to the command.
- * Remove it when it's already there. */
- if (VIsual_active && STRNCMP(p, "'<,'>", 5) == 0)
- retval = put_in_typebuf(p + 5, TRUE, TRUE, silent);
- else
- retval = put_in_typebuf(p, TRUE, TRUE, silent);
- }
- vim_free(p);
- }
- #endif
- #ifdef FEAT_EVAL
- else if (regname == '=')
- {
- p = get_expr_line();
- if (p == NULL)
- return FAIL;
- retval = put_in_typebuf(p, TRUE, colon, silent);
- 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, FALSE, colon, silent);
- 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(silent);
- for (i = y_current->y_size; --i >= 0; )
- {
- char_u *escaped;
- /* 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, silent) == FAIL)
- return FAIL;
- }
- escaped = vim_strsave_escape_csi(y_current->y_array[i]);
- if (escaped == NULL)
- return FAIL;
- retval = ins_typebuf(escaped, remap, 0, TRUE, silent);
- vim_free(escaped);
- if (retval == FAIL)
- return FAIL;
- if (colon && ins_typebuf((char_u *)":", remap, 0, TRUE, silent)
- == 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(silent)
- int silent;
- {
- char_u buf[3];
- if (restart_edit != NUL)
- {
- if (restart_edit == 'V')
- {
- buf[0] = 'g';
- buf[1] = 'R';
- buf[2] = NUL;
- }
- else
- {
- buf[0] = restart_edit == 'I' ? 'i' : restart_edit;
- buf[1] = NUL;
- }
- if (ins_typebuf(buf, REMAP_NONE, 0, TRUE, silent) == OK)
- restart_edit = NUL;
- }
- }
- static int
- put_in_typebuf(s, esc, colon, silent)
- char_u *s;
- int esc; /* Escape CSI characters */
- int colon; /* add ':' before the line */
- int silent;
- {
- int retval = OK;
- put_reedit_in_typebuf(silent);
- if (colon)
- retval = ins_typebuf((char_u *)"\n", REMAP_YES, 0, TRUE, silent);
- if (retval == OK)
- {
- char_u *p;
- if (esc)
- p = vim_strsave_escape_csi(s);
- else
- p = s;
- if (p == NULL)
- retval = FAIL;
- else
- retval = ins_typebuf(p, REMAP_YES, 0, TRUE, silent);
- if (esc)
- vim_free(p);
- }
- if (colon && retval == OK)
- retval = ins_typebuf((char_u *)":", REMAP_YES, 0, TRUE, silent);
- return retval;
- }
- /*
- * Insert a yank register: copy it into the Read buffer.
- * Used by CTRL-R command and middle mouse button in insert mode.
- *
- * return FAIL for failure, OK otherwise
- */
- int
- insert_reg(regname, literally)
- int regname;
- int literally; /* insert literally, not as if typed */
- {
- long i;
- int retval = OK;
- char_u *arg;
- int allocated;
- /*
- * It is possible to get into an endless loop by having CTRL-R a in
- * register a and then, in insert mode, doing CTRL-R a.
- * If you hit CTRL-C, the loop will be broken here.
- */
- ui_breakcheck();
- if (got_int)
- return FAIL;
- /* check for valid regname */
- if (regname != NUL && !valid_yank_reg(regname, FALSE))
- return FAIL;
- #ifdef FEAT_CLIPBOARD
- regname = may_get_selection(regname);
- #endif
- if (regname == '.') /* insert last inserted text */
- retval = stuff_inserted(NUL, 1L, TRUE);
- else if (get_spec_reg(regname, &arg, &allocated, TRUE))
- {
- if (arg == NULL)
- return FAIL;
- stuffescaped(arg, literally);
- if (allocated)
- vim_free(arg);
- }
- else /* name or number register */
- {
- get_yank_register(regname, FALSE);
- if (y_current->y_array == NULL)
- retval = FAIL;
- else
- {
- for (i = 0; i < y_current->y_size; ++i)
- {
- stuffescaped(y_current->y_array[i], literally);
- /*
- * Insert a newline between lines and after last line if
- * y_type is MLINE.
- */
- if (y_current->y_type == MLINE || i < y_current->y_size - 1)
- stuffcharReadbuff('\n');
- }
- }
- }
- return retval;
- }
- /*
- * Stuff a string into the typeahead buffer, such that edit() will insert it
- * literally ("literally" TRUE) or interpret is as typed characters.
- */
- static void
- stuffescaped(arg, literally)
- char_u *arg;
- int literally;
- {
- int c;
- char_u *start;
- while (*arg != NUL)
- {
- /* Stuff a sequence of normal ASCII characters, that's fast. Also
- * stuff K_SPECIAL to get the effect of a special key when "literally"
- * is TRUE. */
- start = arg;
- while ((*arg >= ' '
- #ifndef EBCDIC
- && *arg < DEL /* EBCDIC: chars above space are normal */
- #endif
- )
- || (*arg == K_SPECIAL && !literally))
- ++arg;
- if (arg > start)
- stuffReadbuffLen(start, (long)(arg - start));
- /* stuff a single special character */
- if (*arg != NUL)
- {
- #ifdef FEAT_MBYTE
- if (has_mbyte)
- c = mb_ptr2char_adv(&arg);
- else
- #endif
- c = *arg++;
- if (literally && ((c < ' ' && c != TAB) || c == DEL))
- stuffcharReadbuff(Ctrl_V);
- stuffcharReadbuff(c);
- }
- }
- }
- /*
- * If "regname" is a special register, return a pointer to its value.
- */
- int
- get_spec_reg(regname, argp, allocated, errmsg)
- int regname;
- char_u **argp;
- int *allocated;
- int errmsg; /* give error message when failing */
- {
- int cnt;
- *argp = NULL;
- *allocated = FALSE;
- switch (regname)
- {
- case '%': /* file name */
- if (errmsg)
- check_fname(); /* will give emsg if not set */
- *argp = curbuf->b_fname;
- return TRUE;
- case '#': /* alternate file name */
- *argp = getaltfname(errmsg); /* may give emsg if not set */
- return TRUE;
- #ifdef FEAT_EVAL
- case '=': /* result of expression */
- *argp = get_expr_line();
- *allocated = TRUE;
- return TRUE;
- #endif
- case ':': /* last command line */
- if (last_cmdline == NULL && errmsg)
- EMSG(_(e_nolastcmd));
- *argp = last_cmdline;
- return TRUE;
- case '/': /* last search-pattern */
- if (last_search_pat() == NULL && errmsg)
- EMSG(_(e_noprevre));
- *argp = last_search_pat();
- return TRUE;
- case '.': /* last inserted text */
- *argp = get_last_insert_save();
- *allocated = TRUE;
- if (*argp == NULL && errmsg)
- EMSG(_(e_noinstext));
- return TRUE;
- #ifdef FEAT_SEARCHPATH
- case Ctrl_F: /* Filename under cursor */
- case Ctrl_P: /* Path under cursor, expand via "path" */
- if (!errmsg)
- return FALSE;
- *argp = file_name_at_cursor(FNAME_MESS | FNAME_HYP
- | (regname == Ctrl_P ? FNAME_EXP : 0), 1L, NULL);
- *allocated = TRUE;
- return TRUE;
- #endif
- case Ctrl_W: /* word under cursor */
- case Ctrl_A: /* WORD (mnemonic All) under cursor */
- if (!errmsg)
- return FALSE;
- cnt = find_ident_under_cursor(argp, regname == Ctrl_W
- ? (FIND_IDENT|FIND_STRING) : FIND_STRING);
- *argp = cnt ? vim_strnsave(*argp, cnt) : NULL;
- *allocated = TRUE;
- return TRUE;
- case '_': /* black hole: always empty */
- *argp = (char_u *)"";
- return TRUE;
- }
- return FALSE;
- }
- /*
- * Paste a yank register into the command line.
- * Only for non-special registers.
- * Used by CTRL-R command in command-line mode
- * insert_reg() can't be used here, because special characters from the
- * register contents will be interpreted as commands.
- *
- * return FAIL for failure, OK otherwise
- */
- int
- cmdline_paste_reg(regname, literally, remcr)
- int regname;
- int literally; /* Insert text literally instead of "as typed" */
- int remcr; /* don't add trailing CR */
- {
- long i;
- get_yank_register(regname, FALSE);
- if (y_current->y_array == NULL)
- return FAIL;
- for (i = 0; i < y_current->y_size; ++i)
- {
- cmdline_paste_str(y_current->y_array[i], literally);
- /* Insert ^M between lines and after last line if type is MLINE.
- * Don't do this when "remcr" is TRUE and the next line is empty. */
- if (y_current->y_type == MLINE
- || (i < y_current->y_size - 1
- && !(remcr
- && i == y_current->y_size - 2
- && *y_current->y_array[i + 1] == NUL)))
- cmdline_paste_str((char_u *)"\r", literally);
- /* Check for CTRL-C, in case someone tries to paste a few thousand
- * lines and gets bored. */
- ui_breakcheck();
- if (got_int)
- return FAIL;
- }
- return OK;
- }
- #if defined(FEAT_CLIPBOARD) || defined(PROTO)
- /*
- * Adjust the register name pointed to with "rp" for the clipboard being
- * used always and the clipboard being available.
- */
- void
- adjust_clip_reg(rp)
- int *rp;
- {
- /* If no reg. specified, and "unnamed" is in 'clipboard', use '*' reg. */
- if (*rp == 0 && clip_unnamed)
- *rp = '*';
- if (!clip_star.available && *rp == '*')
- *rp = 0;
- if (!clip_plus.available && *rp == '+')
- *rp = 0;
- }
- #endif
- /*
- * op_delete - handle a delete operation
- *
- * return FAIL if undo failed, OK otherwise.
- */
- int
- op_delete(oap)
- oparg_T *oap;
- {
- int n;
- linenr_T lnum;
- char_u *ptr;
- #ifdef FEAT_VISUAL
- char_u *newp, *oldp;
- struct block_def bd;
- #endif
- linenr_T old_lcount = curbuf->b_ml.ml_line_count;
- int did_yank = FALSE;
- if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to do */
- return OK;
- /* Nothing to delete, return here. Do prepare undo, for op_change(). */
- if (oap->empty)
- return u_save_cursor();
- if (!curbuf->b_p_ma)
- {
- EMSG(_(e_modifiable));
- return FAIL;
- }
- #ifdef FEAT_CLIPBOARD
- adjust_clip_reg(&oap->regname);
- #endif
- #ifdef FEAT_MBYTE
- if (has_mbyte)
- mb_adjust_opend(oap);
- #endif
- /*
- * Imitate the strange Vi behaviour: If the delete spans more than one line
- * and motion_type == MCHAR and the result is a blank line, make the delete
- * linewise. Don't do this for the change command or Visual mode.
- */
- if ( oap->motion_type == MCHAR
- #ifdef FEAT_VISUAL
- && !oap->is_VIsual
- && !oap->block_mode
- #endif
- && oap->line_count > 1
- && oap->op_type == OP_DELETE)
- {
- ptr = ml_get(oap->end.lnum) + oap->end.col + oap->inclusive;
- ptr = skipwhite(ptr);
- if (*ptr == NUL && inindent(0))
- oap->motion_type = MLINE;
- }
- /*
- * Check for trying to delete (e.g. "D") in an empty line.
- * Note: For the change operator it is ok.
- */
- if ( oap->motion_type == MCHAR
- && oap->line_count == 1
- && oap->op_type == OP_DELETE
- && *ml_get(oap->start.lnum) == NUL)
- {
- /*
- * It's an error to operate on an empty region, when 'E' included in
- * 'cpoptions' (Vi compatible).
- */
- #ifdef FEAT_VIRTUALEDIT
- if (virtual_op)
- /* Virtual editing: Nothing gets deleted, but we set the '[ and ']
- * marks as if it happened. */
- goto setmarks;
- #endif
- if (vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL)
- beep_flush();
- return OK;
- }
- /*
- * Do a yank of whatever we're about to delete.
- * If a yank register was specified, put the deleted text into that register.
- * For the black hole register '_' don't yank anything.
- */
- if (oap->regname != '_')
- {
- if (oap->regname != 0)
- {
- /* check for read-only register */
- if (!valid_yank_reg(oap->regname, TRUE))
- {
- beep_flush();
- return OK;
- }
- get_yank_register(oap->regname, TRUE); /* yank into specif'd reg. */
- if (op_yank(oap, TRUE, FALSE) == OK) /* yank without message */
- did_yank = TRUE;
- }
- /*
- * Put deleted text into register 1 and shift number registers if the
- * delete contains a line break, or when a regname has been specified.
- */
- if (oap->regname != 0 || oap->motion_type == MLINE
- || oap->line_count > 1 || oap->use_reg_one)
- {
- y_current = &y_regs[9];
- free_yank_all(); /* free register nine */
- for (n = 9; n > 1; --n)
- y_regs[n] = y_regs[n - 1];
- y_previous = y_current = &y_regs[1];
- y_regs[1].y_array = NULL; /* set register one to empty */
- if (op_yank(oap, TRUE, FALSE) == OK)
- did_yank = TRUE;
- }
- /* Yank into small delete register when no register specified and the
- * delete is within one line. */
- if (oap->regname == 0 && oap->motion_type != MLINE
- && oap->line_count == 1)
- {
- oap->regname = '-';
- get_yank_register(oap->regname, TRUE);
- if (op_yank(oap, TRUE, FALSE) == OK)
- did_yank = TRUE;
- oap->regname = 0;
- }
- /*
- * If there's too much stuff to fit in the yank register, then get a
- * confirmation before doing the delete. This is crude, but simple.
- * And it avoids doing a delete of something we can't put back if we
- * want.
- */
- if (!did_yank)
- {
- int msg_silent_save = msg_silent;
- msg_silent = 0; /* must display the prompt */
- n = ask_yesno((char_u *)_("cannot yank; delete anyway"), TRUE);
- msg_silent = msg_silent_save;
- if (n != 'y')
- {
- EMSG(_(e_abort));
- return FAIL;
- }
- }
- }
- #ifdef FEAT_VISUAL
- /*
- * block mode delete
- */
- if (oap->block_mode)
- {
- if (u_save((linenr_T)(oap->start.lnum - 1),
- (linenr_T)(oap->end.lnum + 1)) == FAIL)
- return FAIL;
- for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; ++lnum)
- {
- block_prep(oap, &bd, lnum, TRUE);
- if (bd.textlen == 0) /* nothing to delete */
- continue;
- /* Adjust cursor position for tab replaced by spaces and 'lbr'. */
- if (lnum == curwin->w_cursor.lnum)
- {
- curwin->w_cursor.col = bd.textcol + bd.startspaces;
- # ifdef FEAT_VIRTUALEDIT
- curwin->w_cursor.coladd = 0;
- # endif
- }
- /* n == number of chars deleted
- * If we delete a TAB, it may be replaced by several characters.
- * Thus the number of characters may increase!
- */
- n = bd.textlen - bd.startspaces - bd.endspaces;
- oldp = ml_get(lnum);
- newp = alloc_check((unsigned)STRLEN(oldp) + 1 - n);
- if (newp == NULL)
- continue;
- /* copy up to deleted part */
- mch_memmove(newp, oldp, (size_t)bd.textcol);
- /* insert spaces */
- copy_spaces(newp + bd.textcol,
- (size_t)(bd.startspaces + bd.endspaces));
- /* copy the part after the deleted part */
- oldp += bd.textcol + bd.textlen;
- mch_memmove(newp + bd.textcol + bd.startspaces + bd.endspaces,
- oldp, STRLEN(oldp) + 1);
- /* replace the line */
- ml_replace(lnum, newp, FALSE);
- }
- check_cursor_col();
- changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col,
- oap->end.lnum + 1, 0L);
- oap->line_count = 0; /* no lines deleted */
- }
- else
- #endif
- if (oap->motion_type == MLINE)
- {
- if (oap->op_type == OP_CHANGE)
- {
- /* Delete the lines except the first one. Temporarily move the
- * cursor to the next line. Save the current line number, if the
- * last line is deleted it may be changed.
- */
- if (oap->line_count > 1)
- {
- lnum = curwin->w_cursor.lnum;
- ++curwin->w_cursor.lnum;
- del_lines((long)(oap->line_count - 1), TRUE);
- curwin->w_cursor.lnum = lnum;
- }
- if (u_save_cursor() == FAIL)
- return FAIL;
- if (curbuf->b_p_ai) /* don't delete indent */
- {
- beginline(BL_WHITE); /* cursor on first non-white */
- did_ai = TRUE; /* delete the indent when ESC hit */
- ai_col = curwin->w_cursor.col;
- }
- else
- beginline(0); /* cursor in column 0 */
- truncate_line(FALSE); /* delete the rest of the line */
- /* leave cursor past last char in line */
- if (oap->line_count > 1)
- u_clearline(); /* "U" command not possible after "2cc" */
- }
- else
- {
- del_lines(oap->line_count, TRUE);
- beginline(BL_WHITE | BL_FIX);
- u_clearline(); /* "U" command not possible after "dd" */
- }
- }
- else
- {
- #ifdef FEAT_VIRTUALEDIT
- if (virtual_op)
- {
- int endcol = 0;
- /* For virtualedit: break the tabs that are partly included. */
- if (gchar_pos(&oap->start) == '\t')
- {
- if (u_save_cursor() == FAIL) /* save first line for undo */
- return FAIL;
- if (oap->line_count == 1)
- endcol = getviscol2(oap->end.col, oap->end.coladd);
- coladvance_force(getviscol2(oap->start.col, oap->start.coladd));
- oap->start = curwin->w_cursor;
- if (oap->line_count == 1)
- {
- coladvance(endcol);
- oap->end.col = curwin->w_cursor.col;
- oap->end.coladd = curwin->w_cursor.coladd;
- curwin->w_cursor = oap->start;
- }
- }
- /* Break a tab only when it's included in the area. */
- if (gchar_pos(&oap->end) == '\t'
- && (int)oap->end.coladd < oap->inclusive)
- {
- /* save last line for undo */
- if (u_save((linenr_T)(oap->end.lnum - 1),
- (linenr_T)(oap->end.lnum + 1)) == FAIL)
- return FAIL;
- curwin->w_cursor = oap->end;
- coladvance_force(getviscol2(oap->end.col, oap->end.coladd));
- oap->end = curwin->w_cursor;
- curwin->w_cursor = oap->start;
- }
- }
- #endif
- if (oap->line_count == 1) /* delete characters within one line */
- {
- if (u_save_cursor() == FAIL) /* save line for undo */
- return FAIL;
- /* if 'cpoptions' contains '$', display '$' at end of change */
- if ( vim_strchr(p_cpo, CPO_DOLLAR) != NULL
- && oap->op_type == OP_CHANGE
- && oap->end.lnum == curwin->w_cursor.lnum
- #ifdef FEAT_VISUAL
- && !oap->is_VIsual
- #endif
- )
- display_dollar(oap->end.col - !oap->inclusive);
- n = oap->end.col - oap->start.col + 1 - !oap->inclusive;
- #ifdef FEAT_VIRTUALEDIT
- if (virtual_op)
- {
- /* fix up things for virtualedit-delete:
- * break the tabs which are going to get in our way
- */
- char_u *curline = ml_get_curline();
- int len = (int)STRLEN(curline);
- if (oap->end.coladd != 0
- && (int)oap->end.col >= len - 1
- && !(oap->start.coladd && (int)oap->end.col >= len - 1))
- n++;
- /* Delete at least one char (e.g, when on a control char). */
- if (n == 0 && oap->start.coladd != oap->end.coladd)
- n = 1;
- /* When deleted a char in the line, reset coladd. */
- if (gchar_cursor() != NUL)
- curwin->w_cursor.coladd = 0;
- }
- #endif
- (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE
- #ifdef FEAT_VISUAL
- && !oap->is_VIsual
- #endif
- );
- }
- else /* delete characters between lines */
- {
- pos_T curpos;
- /* save deleted and changed lines for undo */
- if (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
- (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL)
- return FAIL;
- truncate_line(TRUE); /* delete from cursor to end of line */
- curpos = curwin->w_cursor; /* remember curwin->w_cursor */
- ++curwin->w_cursor.lnum;
- del_lines((long)(oap->line_count - 2), FALSE);
- /* delete from start of line until op_end */
- curwin->w_cursor.col = 0;
- (void)del_bytes((long)(oap->end.col + 1 - !oap->inclusive),
- !virtual_op, oap->op_type == OP_DELETE
- #ifdef FEAT_VISUAL
- && !oap->is_VIsual
- #endif
- );
- curwin->w_cursor = curpos; /* restore curwin->w_cursor */
- (void)do_join(FALSE);
- }
- }
- msgmore(curbuf->b_ml.ml_line_count - old_lcount);
- #ifdef FEAT_VIRTUALEDIT
- setmarks:
- #endif
- #ifdef FEAT_VISUAL
- if (oap->block_mode)
- {
- curbuf->b_op_end.lnum = oap->end.lnum;
- curbuf->b_op_end.col = oap->start.col;
- }
- else
- #endif
- curbuf->b_op_end = oap->start;
- curbuf->b_op_start = oap->start;
- return OK;
- }
- #ifdef FEAT_MBYTE
- /*
- * Adjust end of operating area for ending on a multi-byte character.
- * Used for deletion.
- */
- static void
- mb_adjust_opend(oap)
- oparg_T *oap;
- {
- char_u *p;
- if (oap->inclusive)
- {
- p = ml_get(oap->end.lnum);
- oap->end.col += mb_tail_off(p, p + oap->end.col);
- }
- }
- #endif
- #if defined(FEAT_VISUALEXTRA) || defined(PROTO)
- /*
- * Replace a whole area with one character.
- */
- int
- op_replace(oap, c)
- oparg_T *oap;
- int c;
- {
- int n, numc;
- #ifdef FEAT_MBYTE
- int num_chars;
- #endif
- char_u *newp, *oldp;
- size_t oldlen;
- struct block_def bd;
- if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty)
- return OK; /* nothing to do */
- #ifdef FEAT_MBYTE
- if (has_mbyte)
- mb_adjust_opend(oap);
- #endif
- if (u_save((linenr_T)(oap->start.lnum - 1),
- (linenr_T)(oap->end.lnum + 1)) == FAIL)
- return FAIL;
- /*
- * block mode replace
- */
- if (oap->block_mode)
- {
- bd.is_MAX = (curwin->w_curswant == MAX…
Large files files are truncated, but you can click here to view the full file