📄 ex_cmds.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.
*/
/*
* ex_cmds.c: functions for command line commands
*/
#include "vim.h"
#ifdef EX_EXTRA
static int linelen __ARGS((int *has_tab));
#endif
static void do_filter __ARGS((linenr_t line1, linenr_t line2,
char_u *buff, int do_in, int do_out));
#ifdef VIMINFO
static char_u *viminfo_filename __ARGS((char_u *));
static void do_viminfo __ARGS((FILE *fp_in, FILE *fp_out, int want_info,
int want_marks, int force_read));
static int read_viminfo_up_to_marks __ARGS((char_u *line, FILE *fp,
int forceit, int writing));
#endif
static int do_sub_msg __ARGS((void));
static int
#ifdef __BORLANDC__
_RTLENTRYF
#endif
help_compare __ARGS((const void *s1, const void *s2));
void
do_ascii()
{
int c;
char buf1[20];
char buf2[20];
char_u buf3[3];
#ifdef MULTI_BYTE
int c2 = NUL;
#endif
c = gchar_cursor();
if (c == NUL)
{
MSG("empty line");
return;
}
#ifdef MULTI_BYTE
/* split lead from trail */
c2 = (((unsigned)c >> 8) & 0xff);
c = (c & 0xff);
if (c2)
{
buf2[0] = c;
buf2[1] = c2;
buf2[2] = NUL;
sprintf((char *)IObuff, "%s %d %d, Hex %02x %02x, Octal %03o %03o",
buf2, c,c2, c,c2, c,c2);
}
else
#endif
{
if (c == NL) /* NUL is stored as NL */
c = NUL;
if (vim_isprintc(c) && (c < ' ' || c > '~'))
{
transchar_nonprint(buf3, c);
sprintf(buf1, " <%s>", (char *)buf3);
}
else
buf1[0] = NUL;
if (c >= 0x80)
sprintf(buf2, " <M-%s>", transchar(c & 0x7f));
else
buf2[0] = NUL;
sprintf((char *)IObuff, "<%s>%s%s %d, Hex %02x, Octal %03o",
transchar(c), buf1, buf2, c, c, c);
}
msg(IObuff);
}
#ifdef EX_EXTRA
/*
* Handle ":left", ":center" and ":right" commands: align text.
*/
void
do_align(eap)
EXARG *eap;
{
FPOS save_curpos;
int len;
int indent = 0;
int new_indent;
int has_tab;
int width;
#ifdef RIGHTLEFT
if (curwin->w_p_rl)
{
/* switch left and right aligning */
if (eap->cmdidx == CMD_right)
eap->cmdidx = CMD_left;
else if (eap->cmdidx == CMD_left)
eap->cmdidx = CMD_right;
}
#endif
width = atoi((char *)eap->arg);
save_curpos = curwin->w_cursor;
if (eap->cmdidx == CMD_left) /* width is used for new indent */
{
if (width >= 0)
indent = width;
}
else
{
/*
* if 'textwidth' set, use it
* else if 'wrapmargin' set, use it
* if invalid value, use 80
*/
if (width <= 0)
width = curbuf->b_p_tw;
if (width == 0 && curbuf->b_p_wm > 0)
width = Columns - curbuf->b_p_wm;
if (width <= 0)
width = 80;
}
if (u_save((linenr_t)(eap->line1 - 1), (linenr_t)(eap->line2 + 1)) == FAIL)
return;
#ifdef SYNTAX_HL
/* recompute syntax hl., starting with first line */
syn_changed(eap->line1);
#endif
for (curwin->w_cursor.lnum = eap->line1;
curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum)
{
if (eap->cmdidx == CMD_left) /* left align */
new_indent = indent;
else
{
len = linelen(eap->cmdidx == CMD_right ? &has_tab
: NULL) - get_indent();
if (len <= 0) /* skip blank lines */
continue;
if (eap->cmdidx == CMD_center)
new_indent = (width - len) / 2;
else
{
new_indent = width - len; /* right align */
/*
* Make sure that embedded TABs don't make the text go too far
* to the right.
*/
if (has_tab)
while (new_indent > 0)
{
set_indent(new_indent, TRUE); /* set indent */
if (linelen(NULL) <= width)
{
/*
* Now try to move the line as much as possible to
* the right. Stop when it moves too far.
*/
do
set_indent(++new_indent, TRUE); /* set indent */
while (linelen(NULL) <= width);
--new_indent;
break;
}
--new_indent;
}
}
}
if (new_indent < 0)
new_indent = 0;
set_indent(new_indent, TRUE); /* set indent */
}
curwin->w_cursor = save_curpos;
beginline(BL_WHITE | BL_FIX);
/*
* If the cursor is after the first changed line, its position needs to be
* updated.
*/
if (curwin->w_cursor.lnum > eap->line1)
{
changed_line_abv_curs();
invalidate_botline();
}
else if (curwin->w_cursor.lnum == eap->line1)
changed_cline_bef_curs();
/*
* If the start of the aligned lines is before botline, it may have become
* approximated (lines got longer or shorter).
*/
if (botline_approximated() && eap->line1 < curwin->w_botline)
approximate_botline();
update_screen(NOT_VALID);
}
/*
* Get the length of the current line, excluding trailing white space.
*/
static int
linelen(has_tab)
int *has_tab;
{
char_u *line;
char_u *first;
char_u *last;
int save;
int len;
/* find the first non-blank character */
line = ml_get_curline();
first = skipwhite(line);
/* find the character after the last non-blank character */
for (last = first + STRLEN(first);
last > first && vim_iswhite(last[-1]); --last)
;
save = *last;
*last = NUL;
len = linetabsize(line); /* get line length */
if (has_tab != NULL) /* check for embedded TAB */
*has_tab = (vim_strrchr(first, TAB) != NULL);
*last = save;
return len;
}
/*
* Handle ":retab" command.
*/
void
do_retab(eap)
EXARG *eap;
{
linenr_t lnum;
int got_tab = FALSE;
long num_spaces = 0;
long num_tabs;
long len;
long col;
long vcol;
long start_col = 0; /* For start of white-space string */
long start_vcol = 0; /* For start of white-space string */
int temp;
long old_len;
char_u *ptr;
char_u *new_line = (char_u *)1; /* init to non-NULL */
int did_something = FALSE;
int did_undo; /* called u_save for current line */
int new_ts;
int save_list;
save_list = curwin->w_p_list;
curwin->w_p_list = 0; /* don't want list mode here */
#ifdef SYNTAX_HL
/* recompute syntax hl. starting with line1 */
syn_changed(eap->line1);
#endif
new_ts = getdigits(&(eap->arg));
if (new_ts == 0)
new_ts = curbuf->b_p_ts;
for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum)
{
ptr = ml_get(lnum);
col = 0;
vcol = 0;
did_undo = FALSE;
for (;;)
{
if (vim_iswhite(ptr[col]))
{
if (!got_tab && num_spaces == 0)
{
/* First consecutive white-space */
start_vcol = vcol;
start_col = col;
}
if (ptr[col] == ' ')
num_spaces++;
else
got_tab = TRUE;
}
else
{
if (got_tab || (eap->forceit && num_spaces > 1))
{
/* Retabulate this string of white-space */
/* len is virtual length of white string */
len = num_spaces = vcol - start_vcol;
num_tabs = 0;
if (!curbuf->b_p_et)
{
temp = new_ts - (start_vcol % new_ts);
if (num_spaces >= temp)
{
num_spaces -= temp;
num_tabs++;
}
num_tabs += num_spaces / new_ts;
num_spaces -= (num_spaces / new_ts) * new_ts;
}
if (curbuf->b_p_et || got_tab ||
(num_spaces + num_tabs < len))
{
if (did_undo == FALSE)
{
did_undo = TRUE;
if (u_save((linenr_t)(lnum - 1),
(linenr_t)(lnum + 1)) == FAIL)
{
new_line = NULL; /* flag out-of-memory */
break;
}
}
/* len is actual number of white characters used */
len = num_spaces + num_tabs;
old_len = STRLEN(ptr);
new_line = lalloc(old_len - col + start_col + len + 1,
TRUE);
if (new_line == NULL)
break;
if (start_col > 0)
mch_memmove(new_line, ptr, (size_t)start_col);
mch_memmove(new_line + start_col + len,
ptr + col, (size_t)(old_len - col + 1));
ptr = new_line + start_col;
for (col = 0; col < len; col++)
ptr[col] = (col < num_tabs) ? '\t' : ' ';
ml_replace(lnum, new_line, FALSE);
did_something = TRUE;
ptr = new_line;
col = start_col + len;
}
}
got_tab = FALSE;
num_spaces = 0;
}
if (ptr[col] == NUL)
break;
vcol += chartabsize(ptr[col++], (colnr_t)vcol);
}
if (new_line == NULL) /* out of memory */
break;
line_breakcheck();
}
if (got_int)
emsg(e_interr);
if (did_something)
changed();
curwin->w_p_list = save_list; /* restore 'list' */
if (curbuf->b_p_ts != new_ts || did_something)
{
/*
* Cursor may need updating when change is before or at the cursor
* line. w_botline may be wrong a bit now.
*/
if (curbuf->b_p_ts != new_ts || eap->line1 < curwin->w_cursor.lnum)
changed_line_abv_curs(); /* recompute cursor pos compl. */
else if (eap->line1 == curwin->w_cursor.lnum)
changed_cline_bef_curs(); /* recompute curosr pos partly */
approximate_botline();
}
curbuf->b_p_ts = new_ts;
coladvance(curwin->w_curswant);
u_clearline();
update_screen(NOT_VALID);
}
#endif
/*
* :move command - move lines line1-line2 to line dest
*
* return FAIL for failure, OK otherwise
*/
int
do_move(line1, line2, dest)
linenr_t line1;
linenr_t line2;
linenr_t dest;
{
char_u *str;
linenr_t l;
linenr_t extra; /* Num lines added before line1 */
linenr_t num_lines; /* Num lines moved */
linenr_t last_line; /* Last line in file after adding new text */
if (dest >= line1 && dest < line2)
{
EMSG("Move lines into themselves");
return FAIL;
}
num_lines = line2 - line1 + 1;
/*
* First we copy the old text to its new location -- webb
* Also copy the flag that ":global" command uses.
*/
if (u_save(dest, dest + 1) == FAIL)
return FAIL;
for (extra = 0, l = line1; l <= line2; l++)
{
str = vim_strsave(ml_get(l + extra));
if (str != NULL)
{
ml_append(dest + l - line1, str, (colnr_t)0, FALSE);
vim_free(str);
if (dest < line1)
extra++;
}
}
/*
* Now we must be careful adjusting our marks so that we don't overlap our
* mark_adjust() calls.
*
* We adjust the marks within the old text so that they refer to the
* last lines of the file (temporarily), because we know no other marks
* will be set there since these line numbers did not exist until we added
* our new lines.
*
* Then we adjust the marks on lines between the old and new text positions
* (either forwards or backwards).
*
* And Finally we adjust the marks we put at the end of the file back to
* their final destination at the new text position -- webb
*/
last_line = curbuf->b_ml.ml_line_count;
mark_adjust(line1, line2, last_line - line2, 0L);
if (dest >= line2)
{
mark_adjust(line2 + 1, dest, -num_lines, 0L);
curbuf->b_op_start.lnum = dest - num_lines + 1;
curbuf->b_op_end.lnum = dest;
}
else
{
mark_adjust(dest + 1, line1 - 1, num_lines, 0L);
curbuf->b_op_start.lnum = dest + 1;
curbuf->b_op_end.lnum = dest + num_lines;
}
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
mark_adjust(last_line - num_lines + 1, last_line,
-(last_line - dest - extra), 0L);
/*
* Now we delete the original text -- webb
*/
if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
return FAIL;
for (l = line1; l <= line2; l++)
ml_delete(line1 + extra, TRUE);
changed();
if (!global_busy && num_lines > p_report)
smsg((char_u *)"%ld line%s moved", num_lines, plural(num_lines));
/*
* Leave the cursor on the last of the moved lines.
*/
if (dest >= line1)
curwin->w_cursor.lnum = dest;
else
curwin->w_cursor.lnum = dest + (line2 - line1) + 1;
changed_line_abv_curs();
/*
* TODO: should recompute w_botline for simple situations.
*/
invalidate_botline();
return OK;
}
/*
* :copy command - copy lines line1-line2 to line n
*/
void
do_copy(line1, line2, n)
linenr_t line1;
linenr_t line2;
linenr_t n;
{
linenr_t lnum;
char_u *p;
lnum = line2 - line1 + 1;
mark_adjust(n + 1, (linenr_t)MAXLNUM, lnum, 0L);
curbuf->b_op_start.lnum = n + 1;
curbuf->b_op_end.lnum = n + lnum;
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
/*
* there are three situations:
* 1. destination is above line1
* 2. destination is between line1 and line2
* 3. destination is below line2
*
* n = destination (when starting)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -