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