From da51dc9cf202772f60bd2da975dbef257bd9237c Mon Sep 17 00:00:00 2001 From: Eliseo Martínez Date: Mon, 12 May 2014 02:25:17 +0200 Subject: Introduce nvim namespace: Move files. Move files from src/ to src/nvim/. - src/nvim/ becomes the new root dir for nvim executable sources. - src/libnvim/ is planned to become root dir of the neovim library. --- src/ops.c | 5174 ------------------------------------------------------------- 1 file changed, 5174 deletions(-) delete mode 100644 src/ops.c (limited to 'src/ops.c') diff --git a/src/ops.c b/src/ops.c deleted file mode 100644 index 30606b077e..0000000000 --- a/src/ops.c +++ /dev/null @@ -1,5174 +0,0 @@ -/* - * 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 - -#include "vim.h" -#include "ops.h" -#include "buffer.h" -#include "charset.h" -#include "edit.h" -#include "eval.h" -#include "ex_cmds.h" -#include "ex_cmds2.h" -#include "ex_getln.h" -#include "fold.h" -#include "getchar.h" -#include "indent.h" -#include "mark.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "move.h" -#include "normal.h" -#include "option.h" -#include "path.h" -#include "screen.h" -#include "search.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "window.h" - -/* - * 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 '-' - */ -#define NUM_REGISTERS 37 -#define DELETION_REGISTER 36 - -/* - * 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 */ - colnr_T y_width; /* only set if y_type == MBLOCK */ -} 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 before first char */ - int endspaces; /* 'extra' cols after last char */ - int textlen; /* chars in block */ - char_u *textstart; /* pointer to 1st char (partially) in block */ - colnr_T textcol; /* index of chars (partially) 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 */ - 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 */ - colnr_T start_char_vcols; /* number of vcols of pre-block char */ -}; - -static void shift_block(oparg_T *oap, int amount); -static void block_insert(oparg_T *oap, char_u *s, int b_insert, - struct block_def*bdp); -static int stuff_yank(int, char_u *); -static void put_reedit_in_typebuf(int silent); -static int put_in_typebuf(char_u *s, int esc, int colon, - int silent); -static void stuffescaped(char_u *arg, int literally); -static void mb_adjust_opend(oparg_T *oap); -static void free_yank(long); -static void free_yank_all(void); -static void yank_copy_line(struct block_def *bd, long y_idx); -static void dis_msg(char_u *p, int skip_esc); -static char_u *skip_comment(char_u *line, int process, - int include_space, - int *is_comment); -static void block_prep(oparg_T *oap, struct block_def *, linenr_T, int); -static void str_to_reg(struct yankreg *y_ptr, int type, char_u *str, - long len, - long blocklen); -static int ends_in_white(linenr_T lnum); -static int same_leader(linenr_T lnum, int, char_u *, int, char_u *); -static int fmt_check_par(linenr_T, int *, char_u **, int do_comments); - -/* - * 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(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; -} - -/* - * Return TRUE if operator "op" always works on whole lines. - */ -int op_on_lines(int op) -{ - return opchars[op][2]; -} - -/* - * Get first operator command character. - * Returns 'g' or 'z' if there is another command character. - */ -int get_op_char(int optype) -{ - return opchars[optype][0]; -} - -/* - * Get second operator command character. - */ -int get_extra_op_char(int optype) -{ - return opchars[optype][1]; -} - -/* - * op_shift - handle a shift operation - */ -void op_shift(oparg_T *oap, int curs_top, int amount) -{ - long i; - int first_char; - char_u *s; - int block_col = 0; - - if (u_save((linenr_T)(oap->start.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) - return; - - if (oap->block_mode) - block_col = curwin->w_cursor.col; - - for (i = oap->line_count; --i >= 0; ) { - first_char = *ml_get_curline(); - if (first_char == NUL) /* empty line */ - curwin->w_cursor.col = 0; - else if (oap->block_mode) - shift_block(oap, amount); - 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 (first_char != '#' || !preprocs_left()) { - shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, FALSE); - } - ++curwin->w_cursor.lnum; - } - - changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); - /* The cursor line is not in a closed fold */ - foldOpenCursor(); - - if (oap->block_mode) { - curwin->w_cursor.lnum = oap->start.lnum; - curwin->w_cursor.col = block_col; - } else 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, _("%" PRId64 " lines %sed 1 time"), - (int64_t)oap->line_count, s); - else - sprintf((char *)IObuff, _("%" PRId64 " lines %sed %d times"), - (int64_t)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 ( - int left, - int round, - int amount, - int call_changed_bytes /* call changed_bytes() */ -) -{ - int count; - int i, j; - int p_sw = (int)get_sw_value(curbuf); - - 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 */ - if (State & VREPLACE_FLAG) - change_indent(INDENT_SET, count, FALSE, NUL, call_changed_bytes); - else - (void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0); -} - -/* - * Shift one line of the current block one shiftwidth right or left. - * Leaves cursor on first character in block. - */ -static void shift_block(oparg_T *oap, int amount) -{ - int left = (oap->op_type == OP_LSHIFT); - int oldstate = State; - int total; - char_u *newp, *oldp; - int oldcol = curwin->w_cursor.col; - int p_sw = (int)get_sw_value(curbuf); - int p_ts = (int)curbuf->b_p_ts; - struct block_def bd; - int incr; - colnr_T ws_vcol; - int i = 0, j = 0; - int len; - int old_p_ri = p_ri; - - p_ri = 0; /* don't want revins in indent */ - - 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) { - if (has_mbyte) - bd.textstart += (*mb_ptr2len)(bd.textstart); - else - ++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 = (char_u *) xmalloc((size_t)(bd.textcol + i + j + len)); - memset(newp, NUL, (size_t)(bd.textcol + i + j + len)); - 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 */ - memmove(newp + bd.textcol + i + j, bd.textstart, (size_t)len); - } else { /* left */ - colnr_T destination_col; /* column to which text in block will - be shifted */ - char_u *verbatim_copy_end; /* end of the part of the line which is - copied verbatim */ - colnr_T verbatim_copy_width; /* the (displayed) width of this part - of line */ - unsigned fill; /* nr of spaces that replace a TAB */ - unsigned new_line_len; /* the length of the line after the - block shift */ - size_t block_space_width; - size_t shift_amount; - char_u *non_white = bd.textstart; - colnr_T non_white_col; - - /* - * Firstly, let's find the first non-whitespace character that is - * displayed after the block's start column and the character's column - * number. Also, let's calculate the width of all the whitespace - * characters that are displayed in the block and precede the searched - * non-whitespace character. - */ - - /* If "bd.startspaces" is set, "bd.textstart" points to the character, - * the part of which is displayed at the block's beginning. Let's start - * searching from the next character. */ - if (bd.startspaces) - mb_ptr_adv(non_white); - - /* The character's column is in "bd.start_vcol". */ - non_white_col = bd.start_vcol; - - while (vim_iswhite(*non_white)) { - incr = lbr_chartabsize_adv(&non_white, non_white_col); - non_white_col += incr; - } - - block_space_width = non_white_col - oap->start_vcol; - /* We will shift by "total" or "block_space_width", whichever is less. - */ - shift_amount = (block_space_width < (size_t)total - ? block_space_width : (size_t)total); - - /* The column to which we will shift the text. */ - destination_col = (colnr_T)(non_white_col - shift_amount); - - /* Now let's find out how much of the beginning of the line we can - * reuse without modification. */ - verbatim_copy_end = bd.textstart; - verbatim_copy_width = bd.start_vcol; - - /* If "bd.startspaces" is set, "bd.textstart" points to the character - * preceding the block. We have to subtract its width to obtain its - * column number. */ - if (bd.startspaces) - verbatim_copy_width -= bd.start_char_vcols; - while (verbatim_copy_width < destination_col) { - incr = lbr_chartabsize(verbatim_copy_end, verbatim_copy_width); - if (verbatim_copy_width + incr > destination_col) - break; - verbatim_copy_width += incr; - mb_ptr_adv(verbatim_copy_end); - } - - /* If "destination_col" is different from the width of the initial - * part of the line that will be copied, it means we encountered a tab - * character, which we will have to partly replace with spaces. */ - fill = destination_col - verbatim_copy_width; - - /* The replacement line will consist of: - * - the beginning of the original line up to "verbatim_copy_end", - * - "fill" number of spaces, - * - the rest of the line, pointed to by non_white. */ - new_line_len = (unsigned)(verbatim_copy_end - oldp) - + fill - + (unsigned)STRLEN(non_white) + 1; - - newp = (char_u *) xmalloc((size_t)(new_line_len)); - memmove(newp, oldp, (size_t)(verbatim_copy_end - oldp)); - copy_spaces(newp + (verbatim_copy_end - oldp), (size_t)fill); - STRMOVE(newp + (verbatim_copy_end - oldp) + fill, non_white); - } - /* 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; - p_ri = old_p_ri; -} - -/* - * Insert string "s" (b_insert ? before : after) block :AKelly - * Caller must prepare for undo. - */ -static void block_insert(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 = (char_u *) xmalloc((size_t)(STRLEN(oldp) + s_len + count + 1)); - - /* copy up to shifted part */ - memmove(newp, oldp, (size_t)(offset)); - oldp += offset; - - /* insert pre-padding */ - copy_spaces(newp + offset, (size_t)spaces); - - /* copy the new text */ - 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; - STRMOVE(newp + offset, oldp); - - 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; -} - -/* - * op_reindent - handle reindenting a block of lines. - */ -void op_reindent(oap, how) -oparg_T *oap; -int (*how)(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 *)_("%" PRId64 " lines to indent... "), (int64_t)i); - - /* - * Be vi-compatible: For lisp indenting the first line is not - * indented, unless there is only one line. - */ - if (i != oap->line_count - 1 || oap->line_count == 1 - || how != get_lisp_indent) { - 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; - curwin->w_cursor.col = 0; /* make sure it's valid */ - } - - /* 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, - oap->is_VIsual ? start_lnum + oap->line_count : - last_changed + 1, 0L); - else if (oap->is_VIsual) - redraw_curbuf_later(INVERTED); - - if (oap->line_count > p_report) { - i = oap->line_count - (i + 1); - if (i == 1) - MSG(_("1 line indented ")); - else - smsg((char_u *)_("%" PRId64 " lines indented "), (int64_t)i); - } - /* set '[ and '] marks */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; -} - -/* - * 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(void) -{ - char_u *new_line; - - new_line = getcmdline('=', 0L, 0); - if (new_line == NULL) - return NUL; - if (*new_line == NUL) /* use previous line */ - 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(char_u *new_line) -{ - 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(void) -{ - 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; - free(expr_copy); - return rv; -} - -/* - * Get the '=' register expression itself, without evaluating it. - */ -char_u *get_expr_line_src(void) -{ - if (expr_line == NULL) - return NULL; - return vim_strsave(expr_line); -} - -/* - * 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 ( - int regname, - int writing /* if TRUE check for writable registers */ -) -{ - if ( (regname > 0 && ASCII_ISALNUM(regname)) - || (!writing && vim_strchr((char_u *) - "/.%#:=" - , regname) != NULL) - || regname == '"' - || regname == '-' - || regname == '_' - ) - 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(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; - 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; -} - - -/* - * 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 ( - int name, - int copy /* make a copy, if FALSE make register empty. */ -) -{ - get_yank_register(name, 0); - - struct yankreg *reg = xmalloc(sizeof(struct yankreg)); - *reg = *y_current; - if (copy) { - if (reg->y_size == 0) { - reg->y_array = NULL; - } else { - reg->y_array = xmalloc(reg->y_size * sizeof(char_u *)); - for (linenr_T 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 and "reg". - */ -void put_register(int name, void *reg) -{ - get_yank_register(name, 0); - free_yank_all(); - *y_current = *(struct yankreg *)reg; - free(reg); - -} - -/* - * return TRUE if the current yank register has type MLINE - */ -int yank_register_mline(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; -} - -/* - * Start or stop recording into a yank register. - * - * Return FAIL for failure, OK otherwise. - */ -int do_record(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, this - * needs to be removed again to put it in a register. exec_reg then - * adds the escaping back later. - */ - 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(int regname, char_u *p) -{ - /* check for read-only register */ - if (regname != 0 && !valid_yank_reg(regname, TRUE)) { - free(p); - return FAIL; - } - if (regname == '_') { /* black hole: don't do anything */ - free(p); - return OK; - } - get_yank_register(regname, TRUE); - if (y_append && y_current->y_array != NULL) { - char_u **pp = &(y_current->y_array[y_current->y_size - 1]); - char_u *lp = xmalloc(STRLEN(*pp) + STRLEN(p) + 1); - STRCPY(lp, *pp); - // TODO(philix): use xstpcpy() in stuff_yank() - STRCAT(lp, p); - free(p); - free(*pp); - *pp = lp; - } else { - free_yank_all(); - y_current->y_array = (char_u **)alloc((unsigned)sizeof(char_u *)); - y_current->y_array[0] = p; - y_current->y_size = 1; - y_current->y_type = MCHAR; /* used to be MLINE, why? */ - } - return OK; -} - -static int execreg_lastc = NUL; - -/* - * execute a yank register: copy it into the stuff buffer - * - * return FAIL for failure, OK otherwise - */ -int -do_execreg ( - 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 */ -) -{ - long i; - char_u *p; - int retval = OK; - int remap; - - if (regname == '@') { /* repeat previous one */ - if (execreg_lastc == NUL) { - EMSG(_("E748: No previously used register")); - return FAIL; - } - regname = execreg_lastc; - } - /* check for valid regname */ - if (regname == '%' || regname == '#' || !valid_yank_reg(regname, FALSE)) { - emsg_invreg(regname); - return FAIL; - } - execreg_lastc = regname; - - - if (regname == '_') /* black hole: don't stuff anything */ - return OK; - - if (regname == ':') { /* use last command line */ - if (last_cmdline == NULL) { - EMSG(_(e_nolastcmd)); - return FAIL; - } - 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); - } - free(p); - } else if (regname == '=') { - p = get_expr_line(); - if (p == NULL) - return FAIL; - retval = put_in_typebuf(p, TRUE, colon, silent); - free(p); - } 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); - 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); - 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(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; - } -} - -/* - * Insert register contents "s" into the typeahead buffer, so that it will be - * executed again. - * When "esc" is TRUE it is to be taken literally: Escape CSI characters and - * no remapping. - */ -static int -put_in_typebuf ( - char_u *s, - int esc, - 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_NONE, 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, esc ? REMAP_NONE : REMAP_YES, - 0, TRUE, silent); - if (esc) - free(p); - } - if (colon && retval == OK) - retval = ins_typebuf((char_u *)":", REMAP_NONE, 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 ( - 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; - - - 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) - 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(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 >= ' ' - && *arg < DEL /* EBCDIC: chars above space are normal */ - ) - || (*arg == K_SPECIAL && !literally)) - ++arg; - if (arg > start) - stuffReadbuffLen(start, (long)(arg - start)); - - /* stuff a single special character */ - if (*arg != NUL) { - if (has_mbyte) - c = mb_cptr2char_adv(&arg); - else - c = *arg++; - if (literally && ((c < ' ' && c != TAB) || c == DEL)) - stuffcharReadbuff(Ctrl_V); - stuffcharReadbuff(c); - } - } -} - -/* - * If "regname" is a special register, return TRUE and store a pointer to its - * value in "argp". - */ -int -get_spec_reg ( - int regname, - char_u **argp, - int *allocated, /* return: TRUE when value was 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; - - case '=': /* result of expression */ - *argp = get_expr_line(); - *allocated = TRUE; - return TRUE; - - 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; - - 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; - - 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 ( - 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; -} - - -/* - * Handle a delete operation. - * - * Return FAIL if undo failed, OK otherwise. - */ -int op_delete(oparg_T *oap) -{ - int n; - linenr_T lnum; - char_u *ptr; - char_u *newp, *oldp; - struct block_def bd; - linenr_T old_lcount = curbuf->b_ml.ml_line_count; - int did_yank = FALSE; - int orig_regname = oap->regname; - - 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; - } - - - if (has_mbyte) - mb_adjust_opend(oap); - - /* - * 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 - && !oap->is_VIsual - && !oap->block_mode - && oap->line_count > 1 - && oap->motion_force == NUL - && oap->op_type == OP_DELETE) { - ptr = ml_get(oap->end.lnum) + oap->end.col; - if (*ptr != NUL) - ptr += 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). - */ - if (virtual_op) - /* Virtual editing: Nothing gets deleted, but we set the '[ and '] - * marks as if it happened. */ - goto setmarks; - 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. - * Use the register name from before adjust_clip_reg() may have - * changed it. - */ - if (orig_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 named 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; - } - } - } - - /* - * 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; - curwin->w_cursor.coladd = 0; - } - - /* 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 = (char_u *) xmalloc((size_t)(STRLEN(oldp) + 1 - n)); - /* copy up to deleted part */ - 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; - STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp); - /* 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 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 { - 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; - } - } - - 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 - && !oap->is_VIsual - ) - display_dollar(oap->end.col - !oap->inclusive); - - n = oap->end.col - oap->start.col + 1 - !oap->inclusive; - - 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; - } - if (oap->op_type == OP_DELETE - && oap->inclusive - && oap->end.lnum == curbuf->b_ml.ml_line_count - && n > (int)STRLEN(ml_get(oap->end.lnum))) { - /* Special case: gH deletes the last line. */ - del_lines(1L, FALSE); - } else { - (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE - && !oap->is_VIsual - ); - } - } else { /* delete characters between lines */ - pos_T curpos; - int delete_last_line; - - /* 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; - - delete_last_line = (oap->end.lnum == curbuf->b_ml.ml_line_count); - 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); - - if (delete_last_line) - oap->end.lnum = curbuf->b_ml.ml_line_count; - - n = (oap->end.col + 1 - !oap->inclusive); - if (oap->inclusive && delete_last_line - && n > (int)STRLEN(ml_get(oap->end.lnum))) { - /* Special case: gH deletes the last line. */ - del_lines(1L, FALSE); - curwin->w_cursor = curpos; /* restore curwin->w_cursor */ - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } else { - /* delete from start of line until op_end */ - curwin->w_cursor.col = 0; - (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE - && !oap->is_VIsual - ); - curwin->w_cursor = curpos; /* restore curwin->w_cursor */ - } - if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) - (void)do_join(2, FALSE, FALSE, FALSE); - } - } - - msgmore(curbuf->b_ml.ml_line_count - old_lcount); - -setmarks: - if (oap->block_mode) { - curbuf->b_op_end.lnum = oap->end.lnum; - curbuf->b_op_end.col = oap->start.col; - } else - curbuf->b_op_end = oap->start; - curbuf->b_op_start = oap->start; - - return OK; -} - -/* - * Adjust end of operating area for ending on a multi-byte character. - * Used for deletion. - */ -static void mb_adjust_opend(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); - } -} - -/* - * Put character 'c' at position 'lp' - */ -static inline void pchar(pos_T lp, int c) -{ - *(ml_get_buf(curbuf, lp.lnum, TRUE) + lp.col) = c;; -} - -/* - * Replace a whole area with one character. - */ -int op_replace(oparg_T *oap, int c) -{ - int n, numc; - int num_chars; - char_u *newp, *oldp; - size_t oldlen; - struct block_def bd; - char_u *after_p = NULL; - int had_ctrl_v_cr = (c == -1 || c == -2); - - if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty) - return OK; /* nothing to do */ - - if (had_ctrl_v_cr) - c = (c == -1 ? '\r' : '\n'); - - if (has_mbyte) - mb_adjust_opend(oap); - - 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 == MAXCOL); - for (; curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { - curwin->w_cursor.col = 0; /* make sure cursor position is valid */ - block_prep(oap, &bd, curwin->w_cursor.lnum, TRUE); - if (bd.textlen == 0 && (!virtual_op || bd.is_MAX)) - continue; /* nothing to replace */ - - /* n == number of extra chars required - * If we split a TAB, it may be replaced by several characters. - * Thus the number of characters may increase! - */ - /* If the range starts in virtual space, count the initial - * coladd offset as part of "startspaces" */ - if (virtual_op && bd.is_short && *bd.textstart == NUL) { - pos_T vpos; - - vpos.lnum = curwin->w_cursor.lnum; - getvpos(&vpos, oap->start_vcol); - bd.startspaces += vpos.coladd; - n = bd.startspaces; - } else - /* allow for pre spaces */ - n = (bd.startspaces ? bd.start_char_vcols - 1 : 0); - - /* allow for post spp */ - n += (bd.endspaces - && !bd.is_oneChar - && bd.end_char_vcols > 0) ? bd.end_char_vcols - 1 : 0; - /* Figure out how many characters to replace. */ - numc = oap->end_vcol - oap->start_vcol + 1; - if (bd.is_short && (!virtual_op || bd.is_MAX)) - numc -= (oap->end_vcol - bd.end_vcol) + 1; - - /* A double-wide character can be replaced only up to half the - * times. */ - if ((*mb_char2cells)(c) > 1) { - if ((numc & 1) && !bd.is_short) { - ++bd.endspaces; - ++n; - } - numc = numc / 2; - } - - /* Compute bytes needed, move character count to num_chars. */ - num_chars = numc; - numc *= (*mb_char2len)(c); - /* oldlen includes textlen, so don't double count */ - n += numc - bd.textlen; - - oldp = ml_get_curline(); - oldlen = STRLEN(oldp); - newp = (char_u *) xmalloc((size_t)(oldlen + 1 + n)); - memset(newp, NUL, (size_t)(oldlen + 1 + n)); - /* copy up to deleted part */ - memmove(newp, oldp, (size_t)bd.textcol); - oldp += bd.textcol + bd.textlen; - /* insert pre-spaces */ - copy_spaces(newp + bd.textcol, (size_t)bd.startspaces); - /* insert replacement chars CHECK FOR ALLOCATED SPACE */ - /* -1/-2 is used for entering CR literally. */ - if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { - if (has_mbyte) { - n = (int)STRLEN(newp); - while (--num_chars >= 0) - n += (*mb_char2bytes)(c, newp + n); - } else - copy_chars(newp + STRLEN(newp), (size_t)numc, c); - if (!bd.is_short) { - /* insert post-spaces */ - copy_spaces(newp + STRLEN(newp), (size_t)bd.endspaces); - /* copy the part after the changed part */ - STRMOVE(newp + STRLEN(newp), oldp); - } - } else { - /* Replacing with \r or \n means splitting the line. */ - after_p = (char_u *) xmalloc((size_t)(oldlen + 1 + n - STRLEN(newp))); - STRMOVE(after_p, oldp); - } - /* replace the line */ - ml_replace(curwin->w_cursor.lnum, newp, FALSE); - if (after_p != NULL) { - ml_append(curwin->w_cursor.lnum++, after_p, 0, FALSE); - appended_lines_mark(curwin->w_cursor.lnum, 1L); - oap->end.lnum++; - free(after_p); - } - } - } else { - /* - * MCHAR and MLINE motion replace. - */ - if (oap->motion_type == MLINE) { - oap->start.col = 0; - curwin->w_cursor.col = 0; - oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) - --oap->end.col; - } else if (!oap->inclusive) - dec(&(oap->end)); - - while (ltoreq(curwin->w_cursor, oap->end)) { - n = gchar_cursor(); - if (n != NUL) { - if ((*mb_char2len)(c) > 1 || (*mb_char2len)(n) > 1) { - /* This is slow, but it handles replacing a single-byte - * with a multi-byte and the other way around. */ - if (curwin->w_cursor.lnum == oap->end.lnum) - oap->end.col += (*mb_char2len)(c) - (*mb_char2len)(n); - n = State; - State = REPLACE; - ins_char(c); - State = n; - /* Backup to the replaced character. */ - dec_cursor(); - } else { - if (n == TAB) { - int end_vcol = 0; - - if (curwin->w_cursor.lnum == oap->end.lnum) { - /* oap->end has to be recalculated when - * the tab breaks */ - end_vcol = getviscol2(oap->end.col, - oap->end.coladd); - } - coladvance_force(getviscol()); - if (curwin->w_cursor.lnum == oap->end.lnum) - getvpos(&oap->end, end_vcol); - } - pchar(curwin->w_cursor, c); - } - } else if (virtual_op && curwin->w_cursor.lnum == oap->end.lnum) { - int virtcols = oap->end.coladd; - - if (curwin->w_cursor.lnum == oap->start.lnum - && oap->start.col == oap->end.col && oap->start.coladd) - virtcols -= oap->start.coladd; - - /* oap->end has been trimmed so it's effectively inclusive; - * as a result an extra +1 must be counted so we don't - * trample the NUL byte. */ - coladvance_force(getviscol2(oap->end.col, oap->end.coladd) + 1); - curwin->w_cursor.col -= (virtcols + 1); - for (; virtcols >= 0; virtcols--) { - pchar(curwin->w_cursor, c); - if (inc(&curwin->w_cursor) == -1) - break; - } - } - - /* Advance to next character, stop at the end of the file. */ - if (inc_cursor() == -1) - break; - } - } - - curwin->w_cursor = oap->start; - check_cursor(); - changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L); - - /* Set "'[" and "']" marks. */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - - return OK; -} - -static int swapchars(int op_type, pos_T *pos, int length); - -/* - * Handle the (non-standard vi) tilde operator. Also for "gu", "gU" and "g?". - */ -void op_tilde(oparg_T *oap) -{ - pos_T pos; - struct block_def bd; - int did_change = FALSE; - - if (u_save((linenr_T)(oap->start.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) - return; - - pos = oap->start; - if (oap->block_mode) { /* Visual block mode */ - for (; pos.lnum <= oap->end.lnum; ++pos.lnum) { - int one_change; - - block_prep(oap, &bd, pos.lnum, FALSE); - pos.col = bd.textcol; - one_change = swapchars(oap->op_type, &pos, bd.textlen); - did_change |= one_change; - - } - if (did_change) - changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); - } else { /* not block mode */ - if (oap->motion_type == MLINE) { - oap->start.col = 0; - pos.col = 0; - oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) - --oap->end.col; - } else if (!oap->inclusive) - dec(&(oap->end)); - - if (pos.lnum == oap->end.lnum) - did_change = swapchars(oap->op_type, &pos, - oap->end.col - pos.col + 1); - else - for (;; ) { - did_change |= swapchars(oap->op_type, &pos, - pos.lnum == oap->end.lnum ? oap->end.col + 1 : - (int)STRLEN(ml_get_pos(&pos))); - if (ltoreq(oap->end, pos) || inc(&pos) == -1) - break; - } - if (did_change) { - changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, - 0L); - } - } - - if (!did_change && oap->is_VIsual) - /* No change: need to remove the Visual selection */ - redraw_curbuf_later(INVERTED); - - /* - * Set '[ and '] marks. - */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - - if (oap->line_count > p_report) { - if (oap->line_count == 1) - MSG(_("1 line changed")); - else - smsg((char_u *)_("%" PRId64 " lines changed"), (int64_t)oap->line_count); - } -} - -/* - * Invoke swapchar() on "length" bytes at position "pos". - * "pos" is advanced to just after the changed characters. - * "length" is rounded up to include the whole last multi-byte character. - * Also works correctly when the number of bytes changes. - * Returns TRUE if some character was changed. - */ -static int swapchars(int op_type, pos_T *pos, int length) -{ - int todo; - int did_change = 0; - - for (todo = length; todo > 0; --todo) { - if (has_mbyte) { - int len = (*mb_ptr2len)(ml_get_pos(pos)); - - /* we're counting bytes, not characters */ - if (len > 0) - todo -= len - 1; - } - did_change |= swapchar(op_type, pos); - if (inc(pos) == -1) /* at end of file */ - break; - } - return did_change; -} - -/* - * If op_type == OP_UPPER: make uppercase, - * if op_type == OP_LOWER: make lowercase, - * if op_type == OP_ROT13: do rot13 encoding, - * else swap case of character at 'pos' - * returns TRUE when something actually changed. - */ -int swapchar(int op_type, pos_T *pos) -{ - int c; - int nc; - - c = gchar_pos(pos); - - /* Only do rot13 encoding for ASCII characters. */ - if (c >= 0x80 && op_type == OP_ROT13) - return FALSE; - - if (op_type == OP_UPPER && c == 0xdf - && (enc_latin1like || STRCMP(p_enc, "iso-8859-2") == 0)) { - pos_T sp = curwin->w_cursor; - - /* Special handling of German sharp s: change to "SS". */ - curwin->w_cursor = *pos; - del_char(FALSE); - ins_char('S'); - ins_char('S'); - curwin->w_cursor = sp; - inc(pos); - } - - if (enc_dbcs != 0 && c >= 0x100) /* No lower/uppercase letter */ - return FALSE; - nc = c; - if (vim_islower(c)) { - if (op_type == OP_ROT13) - nc = ROT13(c, 'a'); - else if (op_type != OP_LOWER) - nc = vim_toupper(c); - } else if (vim_isupper(c)) { - if (op_type == OP_ROT13) - nc = ROT13(c, 'A'); - else if (op_type != OP_UPPER) - nc = vim_tolower(c); - } - if (nc != c) { - if (enc_utf8 && (c >= 0x80 || nc >= 0x80)) { - pos_T sp = curwin->w_cursor; - - curwin->w_cursor = *pos; - /* don't use del_char(), it also removes composing chars */ - del_bytes(utf_ptr2len(ml_get_cursor()), FALSE, FALSE); - ins_char(nc); - curwin->w_cursor = sp; - } else - pchar(*pos, nc); - return TRUE; - } - return FALSE; -} - -/* - * op_insert - Insert and append operators for Visual mode. - */ -void op_insert(oparg_T *oap, long count1) -{ - long ins_len, pre_textlen = 0; - char_u *firstline, *ins_text; - struct block_def bd; - int i; - - /* edit() changes this - record it for OP_APPEND */ - bd.is_MAX = (curwin->w_curswant == MAXCOL); - - /* vis block is still marked. Get rid of it now. */ - curwin->w_cursor.lnum = oap->start.lnum; - update_screen(INVERTED); - - if (oap->block_mode) { - /* When 'virtualedit' is used, need to insert the extra spaces before - * doing block_prep(). When only "block" is used, virtual edit is - * already disabled, but still need it when calling - * coladvance_force(). */ - if (curwin->w_cursor.coladd > 0) { - int old_ve_flags = ve_flags; - - ve_flags = VE_ALL; - if (u_save_cursor() == FAIL) - return; - coladvance_force(oap->op_type == OP_APPEND - ? oap->end_vcol + 1 : getviscol()); - if (oap->op_type == OP_APPEND) - --curwin->w_cursor.col; - ve_flags = old_ve_flags; - } - /*