📄 ops.c
字号:
/* 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.
*/
/*
* 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 = number registers, for deletes
* 10..35 = named registers
* 36 = delete register (-)
* 37 = Clipboard register (*). Only if USE_CLIPBOARD defined
*/
#ifdef USE_CLIPBOARD
# define NUM_REGISTERS 38
#else
# define NUM_REGISTERS 37
#endif
/*
* Symbolic names for some registers.
*/
#define DELETION_REGISTER 36
#ifdef USE_CLIPBOARD
# define CLIPBOARD_REGISTER 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 */
} 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
*/
struct block_def
{
int startspaces;
int endspaces;
int textlen;
char_u *textstart;
colnr_t textcol;
};
#ifdef WANT_EVAL
static char_u *get_expr_line __ARGS((void));
#endif
static void get_yank_register __ARGS((int regname, int writing));
static int stuff_yank __ARGS((int, char_u *));
static int put_in_typebuf __ARGS((char_u *s, int colon));
static void stuffescaped __ARGS((char_u *arg));
static int get_spec_reg __ARGS((int regname, char_u **argp, int *allocated, int errmsg));
static void free_yank __ARGS((long));
static void free_yank_all __ARGS((void));
static void block_prep __ARGS((OPARG *oap, struct block_def *, linenr_t, int));
#if defined(USE_CLIPBOARD) || defined(WANT_EVAL)
static void str_to_reg __ARGS((struct yankreg *y_ptr, int type, char_u *str, long len));
#endif
static int same_leader __ARGS((int, char_u *, int, char_u *));
static int fmt_check_par __ARGS((linenr_t, int *, char_u **));
/*
* op_shift - handle a shift operation
*/
void
op_shift(oap, curs_top, amount)
OPARG *oap;
int curs_top;
int amount;
{
long i;
int first_char;
if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
(linenr_t)(curwin->w_cursor.lnum + oap->line_count)) == FAIL)
return;
#ifdef SYNTAX_HL
/* recompute syntax hl., starting with current line */
syn_changed(curwin->w_cursor.lnum);
#endif
for (i = oap->line_count; --i >= 0; )
{
first_char = *ml_get_curline();
if (first_char == NUL) /* empty line */
curwin->w_cursor.col = 0;
/*
* Don't move the line right if it starts with # and p_si is set.
*/
else
#if defined(SMARTINDENT) || defined(CINDENT)
if (first_char != '#' || (
# ifdef SMARTINDENT
!curbuf->b_p_si
# endif
# if defined(SMARTINDENT) && defined(CINDENT)
&&
# endif
# ifdef CINDENT
(!curbuf->b_p_cin || !in_cinkeys('#', ' ', TRUE))
# endif
))
#endif
{
/* if (oap->block_mode)
shift the block, not the whole line
else */
shift_line(oap->op_type == OP_LSHIFT, p_sr, amount);
}
++curwin->w_cursor.lnum;
}
if (curs_top) /* put cursor on first line, for ">>" */
{
curwin->w_cursor.lnum -= oap->line_count;
beginline(BL_SOL | BL_FIX); /* shift_line() may have set cursor.col */
}
else
--curwin->w_cursor.lnum; /* put cursor on last line, for ":>" */
update_topline();
update_screen(NOT_VALID);
if (oap->line_count > p_report)
smsg((char_u *)"%ld line%s %ced %d time%s", oap->line_count,
plural(oap->line_count), (oap->op_type == OP_RSHIFT) ? '>' : '<',
amount, plural((long)amount));
/*
* Set "'[" and "']" marks.
*/
curbuf->b_op_start = oap->start;
curbuf->b_op_end = oap->end;
}
/*
* 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_indent(count, TRUE); /* set new indent */
}
#if defined(LISPINDENT) || defined(CINDENT)
/*
* op_reindent - handle reindenting a block of lines for C or lisp.
*
* mechanism copied from op_shift, above
*/
void
op_reindent(oap, how)
OPARG *oap;
int (*how) __ARGS((void));
{
long i;
char_u *l;
int count;
if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
(linenr_t)(curwin->w_cursor.lnum + oap->line_count)) == FAIL)
return;
#ifdef SYNTAX_HL
/* recompute syntax hl., starting with current line */
syn_changed(curwin->w_cursor.lnum);
#endif
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 % 50 == 0
|| i == oap->line_count - 1)
&& oap->line_count > p_report)
smsg((char_u *)"%ld line%s to indent... ", i, plural(i));
/*
* Be vi-compatible: For lisp indenting the first line is not
* indented, unless there is only one line.
*/
#ifdef LISPINDENT
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 */
set_indent(count, TRUE);
}
++curwin->w_cursor.lnum;
}
/* put cursor on first non-blank of indented line */
curwin->w_cursor.lnum -= oap->line_count;
beginline(BL_SOL | BL_FIX);
update_topline();
update_screen(NOT_VALID);
if (oap->line_count > p_report)
{
i = oap->line_count - (i + 1);
smsg((char_u *)"%ld line%s indented ", i, plural(i));
}
/* set '[ and '] marks */
curbuf->b_op_start = oap->start;
curbuf->b_op_end = oap->end;
}
#endif /* defined(LISPINDENT) || defined(CINDENT) */
#ifdef WANT_EVAL
/*
* 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.
*/
static char_u *
get_expr_line()
{
if (expr_line == NULL)
return NULL;
return eval_to_string(expr_line, NULL);
}
#endif /* WANT_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 > '~')
return FALSE;
if ( isalnum(regname)
|| (!writing && vim_strchr((char_u *)
#ifdef WANT_EVAL
".%#:="
#else
".%#:"
#endif
, regname) != NULL)
|| regname == '"'
|| regname == '-'
|| regname == '_'
#ifdef USE_CLIPBOARD
|| (clipboard.available && 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 && !writing) || regname == '"') && y_previous != NULL)
{
y_current = y_previous;
return;
}
i = regname;
if (isdigit(i))
i -= '0';
else if (islower(i))
i -= 'a' - 10;
else if (isupper(i))
{
i -= 'A' - 10;
y_append = TRUE;
}
else if (regname == '-')
i = DELETION_REGISTER;
#ifdef USE_CLIPBOARD
else if (clipboard.available && regname == '*')
i = CLIPBOARD_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;
}
/*
* 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);
}
/*
* 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 > '~' || (!isalnum(c) && c != '"'))
retval = FAIL;
else
{
Recording = TRUE;
showmode();
regname = c;
retval = OK;
}
}
else /* stop recording */
{
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -