1
0
Fork 0
mirror of https://github.com/vim/vim synced 2025-03-27 12:06:44 +01:00
vim/src/normal.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

7555 lines
178 KiB
C
Raw Normal View History

/* vi:set ts=8 sts=4 sw=4 noet:
2004-06-13 20:20:40 +00:00
*
* VIM - Vi IMproved by Bram Moolenaar et al.
2004-06-13 20:20:40 +00:00
*
* 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.
*/
/*
* normal.c: Contains the main routine for processing characters in command
* mode. Communicates closely with the code in ops.c to handle
* the operators.
*/
#include "vim.h"
static int VIsual_mode_orig = NUL; // saved Visual mode
2004-06-13 20:20:40 +00:00
#ifdef FEAT_EVAL
static void set_vcount_ca(cmdarg_T *cap, int *set_prevcount);
#endif
static void unshift_special(cmdarg_T *cap);
static void del_from_showcmd(int);
2004-06-13 20:20:40 +00:00
/*
* nv_*(): functions called to handle Normal and Visual mode commands.
* n_*(): functions called to handle Normal mode commands.
* v_*(): functions called to handle Visual mode commands.
*/
static void nv_ignore(cmdarg_T *cap);
static void nv_nop(cmdarg_T *cap);
static void nv_error(cmdarg_T *cap);
static void nv_help(cmdarg_T *cap);
static void nv_addsub(cmdarg_T *cap);
static void nv_page(cmdarg_T *cap);
static void nv_zet(cmdarg_T *cap);
2004-06-13 20:20:40 +00:00
#ifdef FEAT_GUI
static void nv_ver_scrollbar(cmdarg_T *cap);
static void nv_hor_scrollbar(cmdarg_T *cap);
2004-06-13 20:20:40 +00:00
#endif
2006-02-24 23:53:04 +00:00
#ifdef FEAT_GUI_TABLINE
static void nv_tabline(cmdarg_T *cap);
static void nv_tabmenu(cmdarg_T *cap);
#endif
static void nv_exmode(cmdarg_T *cap);
static void nv_colon(cmdarg_T *cap);
static void nv_ctrlg(cmdarg_T *cap);
static void nv_ctrlh(cmdarg_T *cap);
static void nv_clear(cmdarg_T *cap);
static void nv_ctrlo(cmdarg_T *cap);
static void nv_hat(cmdarg_T *cap);
static void nv_Zet(cmdarg_T *cap);
static void nv_ident(cmdarg_T *cap);
static void nv_tagpop(cmdarg_T *cap);
static void nv_scroll(cmdarg_T *cap);
static void nv_right(cmdarg_T *cap);
static void nv_left(cmdarg_T *cap);
static void nv_up(cmdarg_T *cap);
static void nv_down(cmdarg_T *cap);
static void nv_end(cmdarg_T *cap);
static void nv_dollar(cmdarg_T *cap);
static void nv_search(cmdarg_T *cap);
static void nv_next(cmdarg_T *cap);
static int normal_search(cmdarg_T *cap, int dir, char_u *pat, size_t patlen, int opt, int *wrapped);
static void nv_csearch(cmdarg_T *cap);
static void nv_brackets(cmdarg_T *cap);
static void nv_percent(cmdarg_T *cap);
static void nv_brace(cmdarg_T *cap);
static void nv_mark(cmdarg_T *cap);
static void nv_findpar(cmdarg_T *cap);
static void nv_undo(cmdarg_T *cap);
static void nv_kundo(cmdarg_T *cap);
static void nv_Replace(cmdarg_T *cap);
static void nv_replace(cmdarg_T *cap);
static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos);
static void v_visop(cmdarg_T *cap);
static void nv_subst(cmdarg_T *cap);
static void nv_abbrev(cmdarg_T *cap);
static void nv_optrans(cmdarg_T *cap);
static void nv_gomark(cmdarg_T *cap);
static void nv_pcmark(cmdarg_T *cap);
static void nv_regname(cmdarg_T *cap);
static void nv_visual(cmdarg_T *cap);
static void n_start_visual_mode(int c);
static void nv_window(cmdarg_T *cap);
static void nv_suspend(cmdarg_T *cap);
static void nv_g_cmd(cmdarg_T *cap);
static void nv_dot(cmdarg_T *cap);
static void nv_redo_or_register(cmdarg_T *cap);
static void nv_Undo(cmdarg_T *cap);
static void nv_tilde(cmdarg_T *cap);
static void nv_operator(cmdarg_T *cap);
2008-01-05 12:35:21 +00:00
#ifdef FEAT_EVAL
static void set_op_var(int optype);
#endif
static void nv_lineop(cmdarg_T *cap);
static void nv_home(cmdarg_T *cap);
static void nv_pipe(cmdarg_T *cap);
static void nv_bck_word(cmdarg_T *cap);
static void nv_wordcmd(cmdarg_T *cap);
static void nv_beginline(cmdarg_T *cap);
static void adjust_cursor(oparg_T *oap);
static void adjust_for_sel(cmdarg_T *cap);
static void nv_select(cmdarg_T *cap);
static void nv_goto(cmdarg_T *cap);
static void nv_normal(cmdarg_T *cap);
static void nv_esc(cmdarg_T *oap);
static void nv_edit(cmdarg_T *cap);
static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln);
static void nv_object(cmdarg_T *cap);
static void nv_record(cmdarg_T *cap);
static void nv_at(cmdarg_T *cap);
static void nv_halfpage(cmdarg_T *cap);
static void nv_join(cmdarg_T *cap);
static void nv_put(cmdarg_T *cap);
static void nv_put_opt(cmdarg_T *cap, int fix_indent);
static void nv_open(cmdarg_T *cap);
2004-06-13 20:20:40 +00:00
#ifdef FEAT_NETBEANS_INTG
static void nv_nbcmd(cmdarg_T *cap);
2004-06-13 20:20:40 +00:00
#endif
#ifdef FEAT_DND
static void nv_drop(cmdarg_T *cap);
2004-06-13 20:20:40 +00:00
#endif
static void nv_cursorhold(cmdarg_T *cap);
2004-06-13 20:20:40 +00:00
// Declare nv_cmds[].
#define DO_DECLARE_NVCMD
#include "nv_cmds.h"
2004-06-13 20:20:40 +00:00
// Include the lookuptable generated by create_nvcmdidx.vim.
#include "nv_cmdidxs.h"
2004-06-13 20:20:40 +00:00
/*
* Search for a command in the commands table.
* Returns -1 for invalid command.
*/
static int
find_command(int cmdchar)
2004-06-13 20:20:40 +00:00
{
int i;
int idx;
int top, bot;
int c;
// A multi-byte character is never a command.
2004-06-13 20:20:40 +00:00
if (cmdchar >= 0x100)
return -1;
// We use the absolute value of the character. Special keys have a
// negative value, but are sorted on their absolute value.
2004-06-13 20:20:40 +00:00
if (cmdchar < 0)
cmdchar = -cmdchar;
// If the character is in the first part: The character is the index into
// nv_cmd_idx[].
2004-06-13 20:20:40 +00:00
if (cmdchar <= nv_max_linear)
return nv_cmd_idx[cmdchar];
// Perform a binary search.
2004-06-13 20:20:40 +00:00
bot = nv_max_linear + 1;
top = NV_CMDS_SIZE - 1;
idx = -1;
while (bot <= top)
{
i = (top + bot) / 2;
c = nv_cmds[nv_cmd_idx[i]].cmd_char;
if (c < 0)
c = -c;
if (cmdchar == c)
{
idx = nv_cmd_idx[i];
break;
}
if (cmdchar > c)
bot = i + 1;
else
top = i - 1;
}
return idx;
}
/*
* If currently editing a cmdline or text is locked: beep and give an error
* message, return TRUE.
*/
static int
check_text_locked(oparg_T *oap)
{
if (!text_locked())
return FALSE;
if (oap != NULL)
clearopbeep(oap);
text_locked_msg();
return TRUE;
}
/*
* If text is locked, "curbuf_lock" or "allbuf_lock" is set:
* Give an error message, possibly beep and return TRUE.
* "oap" may be NULL.
*/
int
check_text_or_curbuf_locked(oparg_T *oap)
{
if (check_text_locked(oap))
return TRUE;
if (!curbuf_locked())
return FALSE;
if (oap != NULL)
clearop(oap);
return TRUE;
}
/*
* Handle the count before a normal command and set cap->count0.
*/
static int
normal_cmd_get_count(
cmdarg_T *cap,
int c,
int toplevel UNUSED,
int set_prevcount UNUSED,
int *ctrl_w,
int *need_flushbuf UNUSED)
{
getcount:
if (!(VIsual_active && VIsual_select))
{
// Handle a count before a command and compute ca.count0.
// Note that '0' is a command and not the start of a count, but it's
// part of a count after other digits.
while ((c >= '1' && c <= '9')
|| (cap->count0 != 0 && (c == K_DEL || c == K_KDEL
|| c == '0')))
{
if (c == K_DEL || c == K_KDEL)
{
cap->count0 /= 10;
del_from_showcmd(4); // delete the digit and ~@%
}
else if (cap->count0 > 99999999L)
{
cap->count0 = 999999999L;
}
else
{
cap->count0 = cap->count0 * 10 + (c - '0');
}
#ifdef FEAT_EVAL
// Set v:count here, when called from main() and not a stuffed
// command, so that v:count can be used in an expression mapping
// right after the count. Do set it for redo.
if (toplevel && readbuf1_empty())
set_vcount_ca(cap, &set_prevcount);
#endif
if (*ctrl_w)
{
++no_mapping;
++allow_keys; // no mapping for nchar, but keys
}
++no_zero_mapping; // don't map zero here
c = plain_vgetc();
LANGMAP_ADJUST(c, TRUE);
--no_zero_mapping;
if (*ctrl_w)
{
--no_mapping;
--allow_keys;
}
*need_flushbuf |= add_to_showcmd(c);
}
// If we got CTRL-W there may be a/another count
if (c == Ctrl_W && !*ctrl_w && cap->oap->op_type == OP_NOP)
{
*ctrl_w = TRUE;
cap->opcount = cap->count0; // remember first count
cap->count0 = 0;
++no_mapping;
++allow_keys; // no mapping for nchar, but keys
c = plain_vgetc(); // get next character
LANGMAP_ADJUST(c, TRUE);
--no_mapping;
--allow_keys;
*need_flushbuf |= add_to_showcmd(c);
goto getcount; // jump back
}
}
if (c == K_CURSORHOLD)
{
// Save the count values so that ca.opcount and ca.count0 are exactly
// the same when coming back here after handling K_CURSORHOLD.
cap->oap->prev_opcount = cap->opcount;
cap->oap->prev_count0 = cap->count0;
}
else if (cap->opcount != 0)
{
// If we're in the middle of an operator (including after entering a
// yank buffer with '"') AND we had a count before the operator, then
// that count overrides the current value of ca.count0.
// What this means effectively, is that commands like "3dw" get turned
// into "d3w" which makes things fall into place pretty neatly.
// If you give a count before AND after the operator, they are
// multiplied.
if (cap->count0)
{
if (cap->opcount >= 999999999L / cap->count0)
cap->count0 = 999999999L;
else
cap->count0 *= cap->opcount;
}
else
cap->count0 = cap->opcount;
}
// Always remember the count. It will be set to zero (on the next call,
// above) when there is no pending operator.
// When called from main(), save the count for use by the "count" built-in
// variable.
cap->opcount = cap->count0;
cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
#ifdef FEAT_EVAL
// Only set v:count when called from main() and not a stuffed command.
// Do set it for redo.
if (toplevel && readbuf1_empty())
set_vcount(cap->count0, cap->count1, set_prevcount);
#endif
return c;
}
/*
* Returns TRUE if the normal command (cap) needs a second character.
*/
static int
normal_cmd_needs_more_chars(cmdarg_T *cap, short_u cmd_flags)
{
return ((cmd_flags & NV_NCH)
&& (((cmd_flags & NV_NCH_NOP) == NV_NCH_NOP
&& cap->oap->op_type == OP_NOP)
|| (cmd_flags & NV_NCH_ALW) == NV_NCH_ALW
|| (cap->cmdchar == 'q'
&& cap->oap->op_type == OP_NOP
&& reg_recording == 0
&& reg_executing == 0)
|| ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
&& (cap->oap->op_type != OP_NOP || VIsual_active))));
}
/*
* Get one or more additional characters for a normal command.
* Return the updated command index (if changed).
*/
static int
normal_cmd_get_more_chars(
int idx_arg,
cmdarg_T *cap,
int *need_flushbuf UNUSED)
{
int idx = idx_arg;
int c;
int *cp;
int repl = FALSE; // get character for replace mode
int lit = FALSE; // get extra character literally
int langmap_active = FALSE; // using :lmap mappings
int lang; // getting a text character
#ifdef HAVE_INPUT_METHOD
int save_smd; // saved value of p_smd
#endif
++no_mapping;
++allow_keys; // no mapping for nchar, but allow key codes
// Don't generate a CursorHold event here, most commands can't handle
// it, e.g., nv_replace(), nv_csearch().
did_cursorhold = TRUE;
if (cap->cmdchar == 'g')
{
/*
* For 'g' get the next character now, so that we can check for
* "gr", "g'" and "g`".
*/
cap->nchar = plain_vgetc();
LANGMAP_ADJUST(cap->nchar, TRUE);
*need_flushbuf |= add_to_showcmd(cap->nchar);
if (cap->nchar == 'r' || cap->nchar == '\'' || cap->nchar == '`'
|| cap->nchar == Ctrl_BSL)
{
cp = &cap->extra_char; // need to get a third character
if (cap->nchar != 'r')
lit = TRUE; // get it literally
else
repl = TRUE; // get it in replace mode
}
else
cp = NULL; // no third character needed
}
else
{
if (cap->cmdchar == 'r') // get it in replace mode
repl = TRUE;
cp = &cap->nchar;
}
lang = (repl || (nv_cmds[idx].cmd_flags & NV_LANG));
/*
* Get a second or third character.
*/
if (cp != NULL)
{
if (repl)
{
State = MODE_REPLACE; // pretend Replace mode
#ifdef CURSOR_SHAPE
ui_cursor_shape(); // show different cursor shape
#endif
#ifdef FEAT_MOUSESHAPE
update_mouseshape(-1);
#endif
}
if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP)
{
// Allow mappings defined with ":lmap".
--no_mapping;
--allow_keys;
if (repl)
State = MODE_LREPLACE;
else
State = MODE_LANGMAP;
langmap_active = TRUE;
}
#ifdef HAVE_INPUT_METHOD
save_smd = p_smd;
p_smd = FALSE; // Don't let the IM code show the mode here
if (lang && curbuf->b_p_iminsert == B_IMODE_IM)
im_set_active(TRUE);
#endif
if ((State & MODE_INSERT) && !p_ek)
{
MAY_WANT_TO_LOG_THIS;
// Disable bracketed paste and modifyOtherKeys here, we won't
// recognize the escape sequences with 'esckeys' off.
out_str(T_BD);
out_str_t_TE();
}
*cp = plain_vgetc();
if ((State & MODE_INSERT) && !p_ek)
{
MAY_WANT_TO_LOG_THIS;
// Re-enable bracketed paste mode and modifyOtherKeys
out_str_t_BE();
out_str_t_TI();
}
if (langmap_active)
{
// Undo the decrement done above
++no_mapping;
++allow_keys;
State = MODE_NORMAL_BUSY;
}
#ifdef HAVE_INPUT_METHOD
if (lang)
{
if (curbuf->b_p_iminsert != B_IMODE_LMAP)
im_save_status(&curbuf->b_p_iminsert);
im_set_active(FALSE);
}
p_smd = save_smd;
#endif
State = MODE_NORMAL_BUSY;
*need_flushbuf |= add_to_showcmd(*cp);
if (!lit)
{
#ifdef FEAT_DIGRAPHS
// Typing CTRL-K gets a digraph.
if (*cp == Ctrl_K
&& ((nv_cmds[idx].cmd_flags & NV_LANG)
|| cp == &cap->extra_char)
&& vim_strchr(p_cpo, CPO_DIGRAPH) == NULL)
{
c = get_digraph(FALSE);
if (c > 0)
{
*cp = c;
// Guessing how to update showcmd here...
del_from_showcmd(3);
*need_flushbuf |= add_to_showcmd(*cp);
}
}
#endif
// adjust chars > 127, except after "tTfFr" commands
LANGMAP_ADJUST(*cp, !lang);
#ifdef FEAT_RIGHTLEFT
// adjust Hebrew mapped char
if (p_hkmap && lang && KeyTyped)
*cp = hkmap(*cp);
#endif
}
// When the next character is CTRL-\ a following CTRL-N means the
// command is aborted and we go to Normal mode.
if (cp == &cap->extra_char
&& cap->nchar == Ctrl_BSL
&& (cap->extra_char == Ctrl_N || cap->extra_char == Ctrl_G))
{
cap->cmdchar = Ctrl_BSL;
cap->nchar = cap->extra_char;
idx = find_command(cap->cmdchar);
}
else if ((cap->nchar == 'n' || cap->nchar == 'N')
&& cap->cmdchar == 'g')
cap->oap->op_type = get_op_type(*cp, NUL);
else if (*cp == Ctrl_BSL)
{
long towait = (p_ttm >= 0 ? p_ttm : p_tm);
// There is a busy wait here when typing "f<C-\>" and then
// something different from CTRL-N. Can't be avoided.
while ((c = vpeekc()) <= 0 && towait > 0L)
{
do_sleep(towait > 50L ? 50L : towait, FALSE);
towait -= 50L;
}
if (c > 0)
{
c = plain_vgetc();
if (c != Ctrl_N && c != Ctrl_G)
vungetc(c);
else
{
cap->cmdchar = Ctrl_BSL;
cap->nchar = c;
idx = find_command(cap->cmdchar);
}
}
}
if (enc_utf8 && lang)
{
// When getting a text character and the next character is a
// multi-byte character, it could be a composing character.
// However, don't wait for it to arrive. Also, do enable mapping,
// because if it's put back with vungetc() it's too late to apply
// mapping.
--no_mapping;
while ((c = vpeekc()) > 0
&& (c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1))
{
c = plain_vgetc();
if (!utf_iscomposing(c))
{
vungetc(c); // it wasn't, put it back
break;
}
else if (cap->ncharC1 == 0)
cap->ncharC1 = c;
else
cap->ncharC2 = c;
}
++no_mapping;
// Vim may be in a different mode when the user types the next key,
// but when replaying a recording the next key is already in the
// typeahead buffer, so record an <Ignore> before that to prevent
// the vpeekc() above from applying wrong mappings when replaying.
++no_u_sync;
gotchars_ignore();
--no_u_sync;
}
}
--no_mapping;
--allow_keys;
return idx;
}
/*
* Returns TRUE if after processing a normal mode command, need to wait for a
* moment when a message is displayed that will be overwritten by the mode
* message.
*/
static int
normal_cmd_need_to_wait_for_msg(cmdarg_T *cap, pos_T *old_pos)
{
// In Visual mode and with "^O" in Insert mode, a short message will be
// overwritten by the mode message. Wait a bit, until a key is hit.
// In Visual mode, it's more important to keep the Visual area updated
// than keeping a message (e.g. from a /pat search).
// Only do this if the command was typed, not from a mapping.
// Don't wait when emsg_silent is non-zero.
// Also wait a bit after an error message, e.g. for "^O:".
// Don't redraw the screen, it would remove the message.
return ( ((p_smd
&& msg_silent == 0
&& (restart_edit != 0
|| (VIsual_active
&& old_pos->lnum == curwin->w_cursor.lnum
&& old_pos->col == curwin->w_cursor.col)
)
&& (clear_cmdline
|| redraw_cmdline)
&& (msg_didout || (msg_didany && msg_scroll))
&& !msg_nowait
&& KeyTyped)
|| (restart_edit != 0
&& !VIsual_active
&& (msg_scroll
|| emsg_on_display)))
&& cap->oap->regname == 0
&& !(cap->retval & CA_COMMAND_BUSY)
&& stuff_empty()
&& typebuf_typed()
&& emsg_silent == 0
&& !in_assert_fails
&& !did_wait_return
&& cap->oap->op_type == OP_NOP);
}
/*
* After processing a normal mode command, wait for a moment when a message is
* displayed that will be overwritten by the mode message.
*/
static void
normal_cmd_wait_for_msg(void)
{
int save_State = State;
// Draw the cursor with the right shape here
if (restart_edit != 0)
State = MODE_INSERT;
// If need to redraw, and there is a "keep_msg", redraw before the
// delay
if (must_redraw && keep_msg != NULL && !emsg_on_display)
{
char_u *kmsg;
kmsg = keep_msg;
keep_msg = NULL;
// Showmode() will clear keep_msg, but we want to use it anyway.
// First update w_topline.
setcursor();
update_screen(0);
// now reset it, otherwise it's put in the history again
keep_msg = kmsg;
kmsg = vim_strsave(keep_msg);
if (kmsg != NULL)
{
msg_attr((char *)kmsg, keep_msg_attr);
vim_free(kmsg);
}
}
setcursor();
#ifdef CURSOR_SHAPE
ui_cursor_shape(); // may show different cursor shape
#endif
cursor_on();
out_flush();
if (msg_scroll || emsg_on_display)
ui_delay(1003L, TRUE); // wait at least one second
ui_delay(3003L, FALSE); // wait up to three seconds
State = save_State;
msg_scroll = FALSE;
emsg_on_display = FALSE;
}
2004-06-13 20:20:40 +00:00
/*
* Execute a command in Normal mode.
*/
void
normal_cmd(
oparg_T *oap,
int toplevel UNUSED) // TRUE when called from main()
2004-06-13 20:20:40 +00:00
{
cmdarg_T ca; // command arguments
2004-06-13 20:20:40 +00:00
int c;
int ctrl_w = FALSE; // got CTRL-W command
2004-06-13 20:20:40 +00:00
int old_col = curwin->w_curswant;
int need_flushbuf = FALSE; // need to call out_flush()
pos_T old_pos; // cursor position before command
2004-06-13 20:20:40 +00:00
int mapped_len;
static int old_mapped_len = 0;
int idx;
2008-11-20 15:12:02 +00:00
int set_prevcount = FALSE;
int save_did_cursorhold = did_cursorhold;
2004-06-13 20:20:40 +00:00
CLEAR_FIELD(ca); // also resets ca.retval
2004-06-13 20:20:40 +00:00
ca.oap = oap;
2008-07-31 20:04:27 +00:00
// Use a count remembered from before entering an operator. After typing
// "3d" we return from normal_cmd() and come back here, the "3" is
// remembered in "opcount".
2004-06-13 20:20:40 +00:00
ca.opcount = opcount;
// If there is an operator pending, then the command we take this time
// will terminate it. Finish_op tells us to finish the operation before
// returning this time (unless the operation was cancelled).
2004-06-13 20:20:40 +00:00
#ifdef CURSOR_SHAPE
c = finish_op;
#endif
finish_op = (oap->op_type != OP_NOP);
#ifdef CURSOR_SHAPE
if (finish_op != c)
{
ui_cursor_shape(); // may show different cursor shape
2004-06-13 20:20:40 +00:00
# ifdef FEAT_MOUSESHAPE
update_mouseshape(-1);
# endif
}
#endif
may_trigger_modechanged();
2004-06-13 20:20:40 +00:00
// When not finishing an operator and no register name typed, reset the
// count.
2004-06-13 20:20:40 +00:00
if (!finish_op && !oap->regname)
2008-11-20 15:12:02 +00:00
{
2004-06-13 20:20:40 +00:00
ca.opcount = 0;
2008-11-20 15:12:02 +00:00
#ifdef FEAT_EVAL
set_prevcount = TRUE;
#endif
}
2004-06-13 20:20:40 +00:00
// Restore counts from before receiving K_CURSORHOLD. This means after
// typing "3", handling K_CURSORHOLD and then typing "2" we get "32", not
// "3 * 2".
2008-07-31 20:04:27 +00:00
if (oap->prev_opcount > 0 || oap->prev_count0 > 0)
{
ca.opcount = oap->prev_opcount;
ca.count0 = oap->prev_count0;
oap->prev_opcount = 0;
oap->prev_count0 = 0;
}
2004-06-13 20:20:40 +00:00
mapped_len = typebuf_maplen();
State = MODE_NORMAL_BUSY;
2004-06-13 20:20:40 +00:00
#ifdef USE_ON_FLY_SCROLL
dont_scroll = FALSE; // allow scrolling here
2004-06-13 20:20:40 +00:00
#endif
#ifdef FEAT_EVAL
// Set v:count here, when called from main() and not a stuffed
// command, so that v:count can be used in an expression mapping
// when there is no count. Do set it for redo.
if (toplevel && readbuf1_empty())
set_vcount_ca(&ca, &set_prevcount);
#endif
2004-06-13 20:20:40 +00:00
/*
* Get the command character from the user.
*/
c = safe_vgetc();
LANGMAP_ADJUST(c, get_real_state() != MODE_SELECT);
2004-06-13 20:20:40 +00:00
// If a mapping was started in Visual or Select mode, remember the length
// of the mapping. This is used below to not return to Insert mode for as
// long as the mapping is being executed.
2004-06-13 20:20:40 +00:00
if (restart_edit == 0)
old_mapped_len = 0;
else if (old_mapped_len
2006-04-05 20:41:53 +00:00
|| (VIsual_active && mapped_len == 0 && typebuf_maplen() > 0))
2004-06-13 20:20:40 +00:00
old_mapped_len = typebuf_maplen();
if (c == NUL)
c = K_ZERO;
// In Select mode, typed text replaces the selection.
2004-06-13 20:20:40 +00:00
if (VIsual_active
&& VIsual_select
&& (vim_isprintc(c) || c == NL || c == CAR || c == K_KENTER))
{
int len;
// Fake a "c"hange command. When "restart_edit" is set (e.g., because
// 'insertmode' is set) fake a "d"elete command, Insert mode will
// restart automatically.
// Insert the typed character in the typeahead buffer, so that it can
// be mapped in Insert mode. Required for ":lmap" to work.
len = ins_char_typebuf(vgetc_char, vgetc_mod_mask);
// When recording and gotchars() was called the character will be
// recorded again, remove the previous recording.
if (KeyTyped)
ungetchars(len);
2005-05-20 21:19:57 +00:00
if (restart_edit != 0)
c = 'd';
else
c = 'c';
msg_nowait = TRUE; // don't delay going to insert mode
old_mapped_len = 0; // do go to Insert mode
2004-06-13 20:20:40 +00:00
}
// If the window was made so small that nothing shows, make it at least one
// line and one column when typing a command.
if (KeyTyped && !KeyStuffed)
win_ensure_size();
2004-06-13 20:20:40 +00:00
need_flushbuf = add_to_showcmd(c);
// Get the command count
c = normal_cmd_get_count(&ca, c, toplevel, set_prevcount, &ctrl_w,
&need_flushbuf);
2004-06-13 20:20:40 +00:00
// Find the command character in the table of commands.
// For CTRL-W we already got nchar when looking for a count.
2004-06-13 20:20:40 +00:00
if (ctrl_w)
{
ca.nchar = c;
ca.cmdchar = Ctrl_W;
}
else
ca.cmdchar = c;
idx = find_command(ca.cmdchar);
if (idx < 0)
{
// Not a known command: beep.
2004-06-13 20:20:40 +00:00
clearopbeep(oap);
goto normal_end;
}
2006-01-19 22:09:32 +00:00
if ((nv_cmds[idx].cmd_flags & NV_NCW) && check_text_or_curbuf_locked(oap))
// this command is not allowed now
2006-04-05 20:41:53 +00:00
goto normal_end;
2004-06-13 20:20:40 +00:00
// In Visual/Select mode, a few keys are handled in a special way.
2004-06-13 20:20:40 +00:00
if (VIsual_active)
{
// when 'keymodel' contains "stopsel" may stop Select/Visual mode
2004-06-13 20:20:40 +00:00
if (km_stopsel
&& (nv_cmds[idx].cmd_flags & NV_STS)
&& !(mod_mask & MOD_MASK_SHIFT))
{
end_visual_mode();
redraw_curbuf_later(UPD_INVERTED);
2004-06-13 20:20:40 +00:00
}
// Keys that work different when 'keymodel' contains "startsel"
2004-06-13 20:20:40 +00:00
if (km_startsel)
{
if (nv_cmds[idx].cmd_flags & NV_SS)
{
unshift_special(&ca);
idx = find_command(ca.cmdchar);
2006-04-22 22:33:57 +00:00
if (idx < 0)
{
// Just in case
2006-04-22 22:33:57 +00:00
clearopbeep(oap);
goto normal_end;
}
2004-06-13 20:20:40 +00:00
}
else if ((nv_cmds[idx].cmd_flags & NV_SSS)
&& (mod_mask & MOD_MASK_SHIFT))
mod_mask &= ~MOD_MASK_SHIFT;
}
}
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl && KeyTyped && !KeyStuffed
&& (nv_cmds[idx].cmd_flags & NV_RL))
{
// Invert horizontal movements and operations. Only when typed by the
// user directly, not when the result of a mapping or "x" translated
// to "dl".
2004-06-13 20:20:40 +00:00
switch (ca.cmdchar)
{
case 'l': ca.cmdchar = 'h'; break;
case K_RIGHT: ca.cmdchar = K_LEFT; break;
case K_S_RIGHT: ca.cmdchar = K_S_LEFT; break;
case K_C_RIGHT: ca.cmdchar = K_C_LEFT; break;
case 'h': ca.cmdchar = 'l'; break;
case K_LEFT: ca.cmdchar = K_RIGHT; break;
case K_S_LEFT: ca.cmdchar = K_S_RIGHT; break;
case K_C_LEFT: ca.cmdchar = K_C_RIGHT; break;
case '>': ca.cmdchar = '<'; break;
case '<': ca.cmdchar = '>'; break;
}
idx = find_command(ca.cmdchar);
}
#endif
// Get additional characters if we need them.
if (normal_cmd_needs_more_chars(&ca, nv_cmds[idx].cmd_flags))
idx = normal_cmd_get_more_chars(idx, &ca, &need_flushbuf);
2004-06-13 20:20:40 +00:00
// Flush the showcmd characters onto the screen so we can see them while
// the command is being executed. Only do this when the shown command was
// actually displayed, otherwise this will slow down a lot when executing
// mappings.
2004-06-13 20:20:40 +00:00
if (need_flushbuf)
out_flush();
2008-10-02 20:55:54 +00:00
if (ca.cmdchar != K_IGNORE)
{
if (ex_normal_busy)
did_cursorhold = save_did_cursorhold;
else
did_cursorhold = FALSE;
}
2004-06-13 20:20:40 +00:00
State = MODE_NORMAL;
2004-06-13 20:20:40 +00:00
if (ca.nchar == ESC || ca.extra_char == ESC)
2004-06-13 20:20:40 +00:00
{
clearop(oap);
if (restart_edit == 0 && goto_im())
restart_edit = 'a';
goto normal_end;
}
2004-09-13 20:26:32 +00:00
if (ca.cmdchar != K_IGNORE)
{
msg_didout = FALSE; // don't scroll screen up for normal command
2004-09-13 20:26:32 +00:00
msg_col = 0;
}
2004-06-13 20:20:40 +00:00
old_pos = curwin->w_cursor; // remember where the cursor was
2004-06-13 20:20:40 +00:00
// When 'keymodel' contains "startsel" some keys start Select/Visual
// mode.
2004-06-13 20:20:40 +00:00
if (!VIsual_active && km_startsel)
{
if (nv_cmds[idx].cmd_flags & NV_SS)
{
start_selection();
unshift_special(&ca);
idx = find_command(ca.cmdchar);
}
else if ((nv_cmds[idx].cmd_flags & NV_SSS)
&& (mod_mask & MOD_MASK_SHIFT))
{
start_selection();
mod_mask &= ~MOD_MASK_SHIFT;
}
}
// Execute the command!
// Call the command function found in the commands table.
2004-06-13 20:20:40 +00:00
ca.arg = nv_cmds[idx].cmd_arg;
(nv_cmds[idx].cmd_func)(&ca);
// If we didn't start or finish an operator, reset oap->regname, unless we
// need it later.
2004-06-13 20:20:40 +00:00
if (!finish_op
&& !oap->op_type
&& (idx < 0 || !(nv_cmds[idx].cmd_flags & NV_KEEPREG)))
{
clearop(oap);
#ifdef FEAT_EVAL
reset_reg_var();
2004-06-13 20:20:40 +00:00
#endif
}
// Get the length of mapped chars again after typing a count, second
// character or "z333<cr>".
2004-12-09 21:34:53 +00:00
if (old_mapped_len > 0)
old_mapped_len = typebuf_maplen();
// If an operation is pending, handle it. But not for K_IGNORE or
// K_MOUSEMOVE.
if (ca.cmdchar != K_IGNORE && ca.cmdchar != K_MOUSEMOVE)
do_pending_operator(&ca, old_col, FALSE);
2004-06-13 20:20:40 +00:00
// Wait for a moment when a message is displayed that will be overwritten
// by the mode message.
if (normal_cmd_need_to_wait_for_msg(&ca, &old_pos))
normal_cmd_wait_for_msg();
// Finish up after executing a Normal mode command.
2004-06-13 20:20:40 +00:00
normal_end:
msg_nowait = FALSE;
#ifdef FEAT_EVAL
if (finish_op)
reset_reg_var();
#endif
2004-06-13 20:20:40 +00:00
#ifdef CURSOR_SHAPE
int prev_finish_op = finish_op;
2004-06-13 20:20:40 +00:00
#endif
if (oap->op_type == OP_NOP)
{
// Reset finish_op, in case it was set
finish_op = FALSE;
may_trigger_modechanged();
}
2004-06-13 20:20:40 +00:00
#ifdef CURSOR_SHAPE
// Redraw the cursor with another shape, if we were in Operator-pending
// mode or did a replace command.
if (prev_finish_op || ca.cmdchar == 'r'
|| (ca.cmdchar == 'g' && ca.nchar == 'r'))
2004-06-13 20:20:40 +00:00
{
ui_cursor_shape(); // may show different cursor shape
2004-06-13 20:20:40 +00:00
# ifdef FEAT_MOUSESHAPE
update_mouseshape(-1);
# endif
}
#endif
2008-07-31 20:04:27 +00:00
if (oap->op_type == OP_NOP && oap->regname == 0
&& ca.cmdchar != K_CURSORHOLD)
2004-06-13 20:20:40 +00:00
clear_showcmd();
checkpcmark(); // check if we moved since setting pcmark
2004-06-13 20:20:40 +00:00
vim_free(ca.searchbuf);
if (has_mbyte)
mb_adjust_cursor();
if (curwin->w_p_scb && toplevel)
{
validate_cursor(); // may need to update w_leftcol
2004-06-13 20:20:40 +00:00
do_check_scrollbind(TRUE);
}
if (curwin->w_p_crb && toplevel)
{
validate_cursor(); // may need to update w_leftcol
do_check_cursorbind();
}
#ifdef FEAT_TERMINAL
// don't go to Insert mode if a terminal has a running job
if (term_job_running(curbuf->b_term))
restart_edit = 0;
#endif
// May restart edit(), if we got here with CTRL-O in Insert mode (but not
// if still inside a mapping that started in Visual mode).
// May switch from Visual to Select mode after CTRL-O command.
2004-06-13 20:20:40 +00:00
if ( oap->op_type == OP_NOP
&& ((restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
|| restart_VIsual_select == 1)
&& !(ca.retval & CA_COMMAND_BUSY)
&& stuff_empty()
&& oap->regname == 0)
{
if (restart_VIsual_select == 1)
{
VIsual_select = TRUE;
may_trigger_modechanged();
2004-06-13 20:20:40 +00:00
showmode();
restart_VIsual_select = 0;
VIsual_select_reg = 0;
2004-06-13 20:20:40 +00:00
}
if (restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
2004-06-13 20:20:40 +00:00
(void)edit(restart_edit, FALSE, 1L);
}
if (restart_VIsual_select == 2)
restart_VIsual_select = 1;
// Save count before an operator for next time.
2004-06-13 20:20:40 +00:00
opcount = ca.opcount;
}
#ifdef FEAT_EVAL
/*
* Set v:count and v:count1 according to "cap".
* Set v:prevcount only when "set_prevcount" is TRUE.
*/
static void
set_vcount_ca(cmdarg_T *cap, int *set_prevcount)
{
long count = cap->count0;
// multiply with cap->opcount the same way as above
if (cap->opcount != 0)
count = cap->opcount * (count == 0 ? 1 : count);
set_vcount(count, count == 0 ? 1 : count, *set_prevcount);
*set_prevcount = FALSE; // only set v:prevcount once
}
#endif
2004-06-13 20:20:40 +00:00
/*
* Check if highlighting for Visual mode is possible, give a warning message
2004-06-13 20:20:40 +00:00
* if not.
*/
void
check_visual_highlight(void)
2004-06-13 20:20:40 +00:00
{
static int did_check = FALSE;
if (full_screen)
{
if (!did_check && HL_ATTR(HLF_V) == 0)
msg(_("Warning: terminal cannot highlight"));
2004-06-13 20:20:40 +00:00
did_check = TRUE;
}
}
#if defined(FEAT_CLIPBOARD) && defined(FEAT_EVAL)
/*
* Call yank_do_autocmd() for "regname".
*/
static void
call_yank_do_autocmd(int regname)
{
oparg_T oa;
yankreg_T *reg;
clear_oparg(&oa);
oa.regname = regname;
oa.op_type = OP_YANK;
oa.is_VIsual = TRUE;
reg = get_register(regname, TRUE);
yank_do_autocmd(&oa, reg);
free_register(reg);
}
#endif
2004-06-13 20:20:40 +00:00
/*
2006-01-22 23:22:22 +00:00
* End Visual mode.
* This function or the next should ALWAYS be called to end Visual mode, except
* from do_pending_operator().
2004-06-13 20:20:40 +00:00
*/
void
end_visual_mode(void)
{
end_visual_mode_keep_button();
reset_held_button();
}
void
end_visual_mode_keep_button(void)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_CLIPBOARD
// If we are using the clipboard, then remember what was selected in case
// we need to paste it somewhere while we still own the selection.
// Only do this when the clipboard is already owned. Don't want to grab
// the selection when hitting ESC.
2004-06-13 20:20:40 +00:00
if (clip_star.available && clip_star.owned)
clip_auto_select();
# if defined(FEAT_EVAL)
// Emit a TextYankPost for the automatic copy of the selection into the
// star and/or plus register.
if (has_textyankpost())
{
if (clip_isautosel_star())
call_yank_do_autocmd('*');
if (clip_isautosel_plus())
call_yank_do_autocmd('+');
}
# endif
2004-06-13 20:20:40 +00:00
#endif
VIsual_active = FALSE;
setmouse();
mouse_dragging = 0;
// Save the current VIsual area for '< and '> marks, and "gv"
2006-02-26 23:59:20 +00:00
curbuf->b_visual.vi_mode = VIsual_mode;
curbuf->b_visual.vi_start = VIsual;
curbuf->b_visual.vi_end = curwin->w_cursor;
curbuf->b_visual.vi_curswant = curwin->w_curswant;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_EVAL
curbuf->b_visual_mode_eval = VIsual_mode;
#endif
if (!virtual_active())
curwin->w_cursor.coladd = 0;
may_clear_cmdline();
2004-06-13 20:20:40 +00:00
2006-04-27 00:02:13 +00:00
adjust_cursor_eol();
may_trigger_modechanged();
2004-06-13 20:20:40 +00:00
}
/*
* Reset VIsual_active and VIsual_reselect.
*/
void
reset_VIsual_and_resel(void)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active)
{
end_visual_mode();
redraw_curbuf_later(UPD_INVERTED); // delete the inversion later
2004-06-13 20:20:40 +00:00
}
VIsual_reselect = FALSE;
}
/*
* Reset VIsual_active and VIsual_reselect if it's set.
*/
void
reset_VIsual(void)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active)
{
end_visual_mode();
redraw_curbuf_later(UPD_INVERTED); // delete the inversion later
2004-06-13 20:20:40 +00:00
VIsual_reselect = FALSE;
}
}
void
restore_visual_mode(void)
{
if (VIsual_mode_orig != NUL)
{
curbuf->b_visual.vi_mode = VIsual_mode_orig;
VIsual_mode_orig = NUL;
}
}
2004-06-13 20:20:40 +00:00
/*
* Check for a balloon-eval special item to include when searching for an
* identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
* Returns TRUE if the character at "*ptr" should be included.
* "dir" is FORWARD or BACKWARD, the direction of searching.
* "*colp" is in/decremented if "ptr[-dir]" should also be included.
* "bnp" points to a counter for square brackets.
*/
static int
find_is_eval_item(
char_u *ptr,
int *colp,
int *bnp,
int dir)
2004-06-13 20:20:40 +00:00
{
// Accept everything inside [].
2004-06-13 20:20:40 +00:00
if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD))
++*bnp;
if (*bnp > 0)
{
if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD))
--*bnp;
return TRUE;
}
// skip over "s.var"
2004-06-13 20:20:40 +00:00
if (*ptr == '.')
return TRUE;
// two-character item: s->var
2004-06-13 20:20:40 +00:00
if (ptr[dir == BACKWARD ? 0 : 1] == '>'
&& ptr[dir == BACKWARD ? -1 : 0] == '-')
{
*colp += dir;
return TRUE;
}
return FALSE;
}
/*
* Find the identifier under or to the right of the cursor.
* "find_type" can have one of three values:
* FIND_IDENT: find an identifier (keyword)
* FIND_STRING: find any non-white text
* FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
2005-03-07 23:00:57 +00:00
* FIND_EVAL: find text useful for C program debugging
2004-06-13 20:20:40 +00:00
*
* There are three steps:
* 1. Search forward for the start of an identifier/text. Doesn't move if
2004-06-13 20:20:40 +00:00
* already on one.
* 2. Search backward for the start of this identifier/text.
2004-06-13 20:20:40 +00:00
* This doesn't match the real Vi but I like it a little better and it
* shouldn't bother anyone.
* 3. Search forward to the end of this identifier/text.
2004-06-13 20:20:40 +00:00
* When FIND_IDENT isn't defined, we backup until a blank.
*
* Returns the length of the text, or zero if no text is found.
* If text is found, a pointer to the text is put in "*text". This
* points into the current buffer line and is not always NUL terminated.
2004-06-13 20:20:40 +00:00
*/
int
find_ident_under_cursor(char_u **text, int find_type)
2004-06-13 20:20:40 +00:00
{
return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
curwin->w_cursor.col, text, NULL, find_type);
2004-06-13 20:20:40 +00:00
}
/*
* Like find_ident_under_cursor(), but for any window and any position.
* However: Uses 'iskeyword' from the current window!.
*/
int
find_ident_at_pos(
win_T *wp,
linenr_T lnum,
colnr_T startcol,
char_u **text,
int *textcol, // column where "text" starts, can be NULL
int find_type)
2004-06-13 20:20:40 +00:00
{
char_u *ptr;
int col = 0; // init to shut up GCC
2004-06-13 20:20:40 +00:00
int i;
int this_class = 0;
int prev_class;
int prevcol;
int bn = 0; // bracket nesting
2004-06-13 20:20:40 +00:00
// if i == 0: try to find an identifier
// if i == 1: try to find any non-white text
2004-06-13 20:20:40 +00:00
ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
{
/*
* 1. skip to start of identifier/text
2004-06-13 20:20:40 +00:00
*/
col = startcol;
if (has_mbyte)
{
while (ptr[col] != NUL)
{
// Stop at a ']' to evaluate "a[x]".
2004-06-13 20:20:40 +00:00
if ((find_type & FIND_EVAL) && ptr[col] == ']')
break;
this_class = mb_get_class(ptr + col);
if (this_class != 0 && (i == 1 || this_class != 1))
break;
2005-08-10 21:07:57 +00:00
col += (*mb_ptr2len)(ptr + col);
2004-06-13 20:20:40 +00:00
}
}
else
while (ptr[col] != NUL
&& (i == 0 ? !vim_iswordc(ptr[col]) : VIM_ISWHITE(ptr[col]))
2004-06-13 20:20:40 +00:00
&& (!(find_type & FIND_EVAL) || ptr[col] != ']')
)
++col;
// When starting on a ']' count it, so that we include the '['.
2004-06-13 20:20:40 +00:00
bn = ptr[col] == ']';
/*
* 2. Back up to start of identifier/text.
2004-06-13 20:20:40 +00:00
*/
if (has_mbyte)
{
// Remember class of character under cursor.
2004-06-13 20:20:40 +00:00
if ((find_type & FIND_EVAL) && ptr[col] == ']')
this_class = mb_get_class((char_u *)"a");
else
this_class = mb_get_class(ptr + col);
2006-04-17 22:14:47 +00:00
while (col > 0 && this_class != 0)
2004-06-13 20:20:40 +00:00
{
prevcol = col - 1 - (*mb_head_off)(ptr, ptr + col - 1);
prev_class = mb_get_class(ptr + prevcol);
if (this_class != prev_class
&& (i == 0
|| prev_class == 0
|| (find_type & FIND_IDENT))
&& (!(find_type & FIND_EVAL)
|| prevcol == 0
|| !find_is_eval_item(ptr + prevcol, &prevcol,
&bn, BACKWARD))
)
break;
col = prevcol;
}
// If we don't want just any old text, or we've found an
// identifier, stop searching.
2004-06-13 20:20:40 +00:00
if (this_class > 2)
this_class = 2;
if (!(find_type & FIND_STRING) || this_class == 2)
break;
}
else
{
while (col > 0
&& ((i == 0
? vim_iswordc(ptr[col - 1])
: (!VIM_ISWHITE(ptr[col - 1])
2004-06-13 20:20:40 +00:00
&& (!(find_type & FIND_IDENT)
|| !vim_iswordc(ptr[col - 1]))))
|| ((find_type & FIND_EVAL)
&& col > 1
&& find_is_eval_item(ptr + col - 1, &col,
&bn, BACKWARD))
))
--col;
// If we don't want just any old text, or we've found an
// identifier, stop searching.
2004-06-13 20:20:40 +00:00
if (!(find_type & FIND_STRING) || vim_iswordc(ptr[col]))
break;
}
}
if (ptr[col] == NUL || (i == 0
&& (has_mbyte ? this_class != 2 : !vim_iswordc(ptr[col]))))
2004-06-13 20:20:40 +00:00
{
// didn't find an identifier or text
if ((find_type & FIND_NOERROR) == 0)
{
if (find_type & FIND_STRING)
emsg(_(e_no_string_under_cursor));
else
emsg(_(e_no_identifier_under_cursor));
}
2004-06-13 20:20:40 +00:00
return 0;
}
ptr += col;
*text = ptr;
if (textcol != NULL)
*textcol = col;
2004-06-13 20:20:40 +00:00
/*
* 3. Find the end if the identifier/text.
2004-06-13 20:20:40 +00:00
*/
bn = 0;
startcol -= col;
col = 0;
if (has_mbyte)
{
// Search for point of changing multibyte character class.
2004-06-13 20:20:40 +00:00
this_class = mb_get_class(ptr);
while (ptr[col] != NUL
&& ((i == 0 ? mb_get_class(ptr + col) == this_class
: mb_get_class(ptr + col) != 0)
|| ((find_type & FIND_EVAL)
&& col <= (int)startcol
&& find_is_eval_item(ptr + col, &col, &bn, FORWARD))
))
2005-08-10 21:07:57 +00:00
col += (*mb_ptr2len)(ptr + col);
2004-06-13 20:20:40 +00:00
}
else
while ((i == 0 ? vim_iswordc(ptr[col])
: (ptr[col] != NUL && !VIM_ISWHITE(ptr[col])))
2004-06-13 20:20:40 +00:00
|| ((find_type & FIND_EVAL)
&& col <= (int)startcol
&& find_is_eval_item(ptr + col, &col, &bn, FORWARD))
)
++col;
return col;
}
/*
* Prepare for redo of a normal command.
*/
static void
prep_redo_cmd(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
prep_redo(cap->oap->regname, cap->count0,
NUL, cap->cmdchar, NUL, NUL, cap->nchar);
}
/*
* Prepare for redo of any command.
* Note that only the last argument can be a multi-byte char.
*/
void
prep_redo(
int regname,
long num,
int cmd1,
int cmd2,
int cmd3,
int cmd4,
int cmd5)
{
prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5);
}
/*
* Prepare for redo of any command with extra count after "cmd2".
*/
void
prep_redo_num2(
int regname,
long num1,
int cmd1,
int cmd2,
long num2,
int cmd3,
int cmd4,
int cmd5)
2004-06-13 20:20:40 +00:00
{
ResetRedobuff();
#ifdef FEAT_EVAL
// Put info about a mapping in the redo buffer, so that "." will use the
// same script context.
may_add_last_used_map_to_redobuff();
#endif
if (regname != 0) // yank from specified buffer
2004-06-13 20:20:40 +00:00
{
AppendCharToRedobuff('"');
AppendCharToRedobuff(regname);
}
if (num1 != 0)
AppendNumberToRedobuff(num1);
2004-06-13 20:20:40 +00:00
if (cmd1 != NUL)
AppendCharToRedobuff(cmd1);
if (cmd2 != NUL)
AppendCharToRedobuff(cmd2);
if (num2 != 0)
AppendNumberToRedobuff(num2);
2004-06-13 20:20:40 +00:00
if (cmd3 != NUL)
AppendCharToRedobuff(cmd3);
if (cmd4 != NUL)
AppendCharToRedobuff(cmd4);
if (cmd5 != NUL)
AppendCharToRedobuff(cmd5);
}
/*
* Check for operator active and clear it.
2004-06-13 20:20:40 +00:00
*
* Beep and return TRUE if an operator was active.
2004-06-13 20:20:40 +00:00
*/
static int
checkclearop(oparg_T *oap)
2004-06-13 20:20:40 +00:00
{
if (oap->op_type == OP_NOP)
return FALSE;
clearopbeep(oap);
return TRUE;
}
/*
2007-05-06 11:59:04 +00:00
* Check for operator or Visual active. Clear active operator.
2004-06-13 20:20:40 +00:00
*
* Beep and return TRUE if an operator or Visual was active.
2004-06-13 20:20:40 +00:00
*/
static int
checkclearopq(oparg_T *oap)
2004-06-13 20:20:40 +00:00
{
if (oap->op_type == OP_NOP && !VIsual_active)
2004-06-13 20:20:40 +00:00
return FALSE;
clearopbeep(oap);
return TRUE;
}
void
clearop(oparg_T *oap)
2004-06-13 20:20:40 +00:00
{
oap->op_type = OP_NOP;
oap->regname = 0;
oap->motion_force = NUL;
oap->use_reg_one = FALSE;
motion_force = NUL;
2004-06-13 20:20:40 +00:00
}
void
clearopbeep(oparg_T *oap)
2004-06-13 20:20:40 +00:00
{
clearop(oap);
beep_flush();
}
/*
* Remove the shift modifier from a special key.
*/
static void
unshift_special(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
switch (cap->cmdchar)
{
case K_S_RIGHT: cap->cmdchar = K_RIGHT; break;
case K_S_LEFT: cap->cmdchar = K_LEFT; break;
case K_S_UP: cap->cmdchar = K_UP; break;
case K_S_DOWN: cap->cmdchar = K_DOWN; break;
case K_S_HOME: cap->cmdchar = K_HOME; break;
case K_S_END: cap->cmdchar = K_END; break;
}
cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
}
/*
* If the mode is currently displayed clear the command line or update the
* command displayed.
*/
void
may_clear_cmdline(void)
{
if (mode_displayed)
clear_cmdline = TRUE; // unshow visual mode later
else
clear_showcmd();
}
2004-06-13 20:20:40 +00:00
/*
* Routines for displaying a partly typed command
*/
static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd()
2004-06-13 20:20:40 +00:00
static int showcmd_is_clear = TRUE;
static int showcmd_visual = FALSE;
static void display_showcmd(void);
2004-06-13 20:20:40 +00:00
void
clear_showcmd(void)
2004-06-13 20:20:40 +00:00
{
if (!p_sc)
return;
if (VIsual_active && !char_avail())
{
int cursor_bot = LT_POS(VIsual, curwin->w_cursor);
2004-06-13 20:20:40 +00:00
long lines;
colnr_T leftcol, rightcol;
linenr_T top, bot;
// Show the size of the Visual area.
2009-04-29 15:41:40 +00:00
if (cursor_bot)
2004-06-13 20:20:40 +00:00
{
top = VIsual.lnum;
bot = curwin->w_cursor.lnum;
}
else
{
top = curwin->w_cursor.lnum;
bot = VIsual.lnum;
}
# ifdef FEAT_FOLDING
// Include closed folds as a whole.
(void)hasFolding(top, &top, NULL);
(void)hasFolding(bot, NULL, &bot);
2004-06-13 20:20:40 +00:00
# endif
lines = bot - top + 1;
if (VIsual_mode == Ctrl_V)
{
# ifdef FEAT_LINEBREAK
2009-04-29 15:41:40 +00:00
char_u *saved_sbr = p_sbr;
char_u *saved_w_sbr = curwin->w_p_sbr;
2009-04-29 15:41:40 +00:00
// Make 'sbr' empty for a moment to get the correct size.
2009-04-29 15:41:40 +00:00
p_sbr = empty_option;
curwin->w_p_sbr = empty_option;
# endif
2004-06-13 20:20:40 +00:00
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
# ifdef FEAT_LINEBREAK
2009-04-29 15:41:40 +00:00
p_sbr = saved_sbr;
curwin->w_p_sbr = saved_w_sbr;
# endif
2004-06-13 20:20:40 +00:00
sprintf((char *)showcmd_buf, "%ldx%ld", lines,
(long)(rightcol - leftcol + 1));
}
else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum)
sprintf((char *)showcmd_buf, "%ld", lines);
else
{
char_u *s, *e;
int l;
int bytes = 0;
int chars = 0;
if (cursor_bot)
{
s = ml_get_pos(&VIsual);
e = ml_get_cursor();
}
else
{
s = ml_get_cursor();
e = ml_get_pos(&VIsual);
}
while ((*p_sel != 'e') ? s <= e : s < e)
{
l = (*mb_ptr2len)(s);
if (l == 0)
{
++bytes;
++chars;
break; // end of line
}
bytes += l;
++chars;
s += l;
}
if (bytes == chars)
sprintf((char *)showcmd_buf, "%d", chars);
else
sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
}
showcmd_buf[SHOWCMD_COLS] = NUL; // truncate
2004-06-13 20:20:40 +00:00
showcmd_visual = TRUE;
}
else
{
showcmd_buf[0] = NUL;
showcmd_visual = FALSE;
// Don't actually display something if there is nothing to clear.
2004-06-13 20:20:40 +00:00
if (showcmd_is_clear)
return;
}
display_showcmd();
}
/*
* Add 'c' to string of shown command chars.
* Return TRUE if output has been written (and setcursor() has been called).
*/
int
add_to_showcmd(int c)
2004-06-13 20:20:40 +00:00
{
char_u *p;
int old_len;
int extra_len;
int overflow;
int i;
char_u mbyte_buf[MB_MAXBYTES];
2004-06-13 20:20:40 +00:00
static int ignore[] =
{
#ifdef FEAT_GUI
2004-06-13 20:20:40 +00:00
K_VER_SCROLLBAR, K_HOR_SCROLLBAR,
K_LEFTMOUSE_NM, K_LEFTRELEASE_NM,
#endif
K_IGNORE, K_PS,
K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
2004-06-13 20:20:40 +00:00
K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
2004-06-13 20:20:40 +00:00
K_X1MOUSE, K_X1DRAG, K_X1RELEASE, K_X2MOUSE, K_X2DRAG, K_X2RELEASE,
2006-01-19 22:09:32 +00:00
K_CURSORHOLD,
2004-06-13 20:20:40 +00:00
0
};
2006-01-23 22:23:09 +00:00
if (!p_sc || msg_silent != 0)
2004-06-13 20:20:40 +00:00
return FALSE;
if (showcmd_visual)
{
showcmd_buf[0] = NUL;
showcmd_visual = FALSE;
}
// Ignore keys that are scrollbar updates and mouse clicks
2004-06-13 20:20:40 +00:00
if (IS_SPECIAL(c))
for (i = 0; ignore[i] != 0; ++i)
if (ignore[i] == c)
return FALSE;
if (c <= 0x7f || !vim_isprintc(c))
{
p = transchar(c);
if (*p == ' ')
STRCPY(p, "<20>");
}
else
{
mbyte_buf[(*mb_char2bytes)(c, mbyte_buf)] = NUL;
p = mbyte_buf;
}
2004-06-13 20:20:40 +00:00
old_len = (int)STRLEN(showcmd_buf);
extra_len = (int)STRLEN(p);
overflow = old_len + extra_len - SHOWCMD_COLS;
if (overflow > 0)
2007-08-14 20:54:49 +00:00
mch_memmove(showcmd_buf, showcmd_buf + overflow,
old_len - overflow + 1);
2004-06-13 20:20:40 +00:00
STRCAT(showcmd_buf, p);
if (char_avail())
return FALSE;
display_showcmd();
return TRUE;
}
void
add_to_showcmd_c(int c)
2004-06-13 20:20:40 +00:00
{
if (!add_to_showcmd(c))
setcursor();
}
/*
* Delete 'len' characters from the end of the shown command.
*/
static void
del_from_showcmd(int len)
2004-06-13 20:20:40 +00:00
{
int old_len;
if (!p_sc)
return;
old_len = (int)STRLEN(showcmd_buf);
if (len > old_len)
len = old_len;
showcmd_buf[old_len - len] = NUL;
if (!char_avail())
display_showcmd();
}
/*
* push_showcmd() and pop_showcmd() are used when waiting for the user to type
* something and there is a partial mapping.
*/
void
push_showcmd(void)
2004-06-13 20:20:40 +00:00
{
if (p_sc)
STRCPY(old_showcmd_buf, showcmd_buf);
}
void
pop_showcmd(void)
2004-06-13 20:20:40 +00:00
{
if (!p_sc)
return;
STRCPY(showcmd_buf, old_showcmd_buf);
display_showcmd();
}
static void
display_showcmd(void)
2004-06-13 20:20:40 +00:00
{
int len = vim_strsize(showcmd_buf);
2004-06-13 20:20:40 +00:00
showcmd_is_clear = (len == 0);
2004-06-13 20:20:40 +00:00
cursor_off();
if (*p_sloc == 's')
{
if (showcmd_is_clear)
curwin->w_redr_status = TRUE;
else
win_redr_status(curwin, FALSE);
}
else if (*p_sloc == 't')
{
if (showcmd_is_clear)
redraw_tabline = TRUE;
else
draw_tabline();
}
else // 'showcmdloc' is "last" or empty
2004-06-13 20:20:40 +00:00
{
if (!showcmd_is_clear)
screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0);
2004-06-13 20:20:40 +00:00
// clear the rest of an old message by outputting up to SHOWCMD_COLS
// spaces
screen_puts((char_u *)" " + len,
(int)Rows - 1, sc_col + len, 0);
}
2004-06-13 20:20:40 +00:00
setcursor(); // put cursor back where it belongs
2004-06-13 20:20:40 +00:00
}
/*
* When "check" is FALSE, prepare for commands that scroll the window.
* When "check" is TRUE, take care of scroll-binding after the window has
* scrolled. Called from normal_cmd() and edit().
*/
void
do_check_scrollbind(int check)
2004-06-13 20:20:40 +00:00
{
static win_T *old_curwin = NULL;
static linenr_T old_topline = 0;
#ifdef FEAT_DIFF
static int old_topfill = 0;
#endif
static buf_T *old_buf = NULL;
static colnr_T old_leftcol = 0;
if (check && curwin->w_p_scb)
{
// If a ":syncbind" command was just used, don't scroll, only reset
// the values.
2004-06-13 20:20:40 +00:00
if (did_syncbind)
did_syncbind = FALSE;
else if (curwin == old_curwin)
{
// Synchronize other windows, as necessary according to
// 'scrollbind'. Don't do this after an ":edit" command, except
// when 'diff' is set.
2004-06-13 20:20:40 +00:00
if ((curwin->w_buffer == old_buf
#ifdef FEAT_DIFF
|| curwin->w_p_diff
#endif
)
&& (curwin->w_topline != old_topline
#ifdef FEAT_DIFF
|| curwin->w_topfill != old_topfill
#endif
|| curwin->w_leftcol != old_leftcol))
{
check_scrollbind(curwin->w_topline - old_topline,
(long)(curwin->w_leftcol - old_leftcol));
}
}
else if (vim_strchr(p_sbo, 'j')) // jump flag set in 'scrollopt'
2004-06-13 20:20:40 +00:00
{
// When switching between windows, make sure that the relative
// vertical offset is valid for the new window. The relative
// offset is invalid whenever another 'scrollbind' window has
// scrolled to a point that would force the current window to
// scroll past the beginning or end of its buffer. When the
// resync is performed, some of the other 'scrollbind' windows may
// need to jump so that the current window's relative position is
// visible on-screen.
2004-06-13 20:20:40 +00:00
check_scrollbind(curwin->w_topline - curwin->w_scbind_pos, 0L);
}
curwin->w_scbind_pos = curwin->w_topline;
}
old_curwin = curwin;
old_topline = curwin->w_topline;
#ifdef FEAT_DIFF
old_topfill = curwin->w_topfill;
#endif
old_buf = curwin->w_buffer;
old_leftcol = curwin->w_leftcol;
}
/*
* Synchronize any windows that have "scrollbind" set, based on the
* number of rows by which the current window has changed
* (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
*/
void
check_scrollbind(linenr_T topline_diff, long leftcol_diff)
2004-06-13 20:20:40 +00:00
{
int want_ver;
int want_hor;
win_T *old_curwin = curwin;
buf_T *old_curbuf = curbuf;
int old_VIsual_select = VIsual_select;
int old_VIsual_active = VIsual_active;
colnr_T tgt_leftcol = curwin->w_leftcol;
long topline;
long y;
// check 'scrollopt' string for vertical and horizontal scroll options
2004-06-13 20:20:40 +00:00
want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0);
#ifdef FEAT_DIFF
want_ver |= old_curwin->w_p_diff;
#endif
want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0));
// loop through the scrollbound windows and scroll accordingly
2004-06-13 20:20:40 +00:00
VIsual_select = VIsual_active = 0;
FOR_ALL_WINDOWS(curwin)
2004-06-13 20:20:40 +00:00
{
curbuf = curwin->w_buffer;
// skip original window and windows with 'noscrollbind'
if (curwin == old_curwin || !curwin->w_p_scb)
continue;
// do the vertical scroll
if (want_ver)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_DIFF
if (old_curwin->w_p_diff && curwin->w_p_diff)
{
diff_set_topline(old_curwin, curwin);
2004-06-13 20:20:40 +00:00
}
else
#endif
2004-06-13 20:20:40 +00:00
{
curwin->w_scbind_pos += topline_diff;
topline = curwin->w_scbind_pos;
if (topline > curbuf->b_ml.ml_line_count)
topline = curbuf->b_ml.ml_line_count;
if (topline < 1)
topline = 1;
y = topline - curwin->w_topline;
if (y > 0)
scrollup(y, FALSE);
else
scrolldown(-y, FALSE);
2004-06-13 20:20:40 +00:00
}
redraw_later(UPD_VALID);
cursor_correct();
curwin->w_redr_status = TRUE;
}
// do the horizontal scroll
if (want_hor)
(void)set_leftcol(tgt_leftcol);
2004-06-13 20:20:40 +00:00
}
// reset current-window
2004-06-13 20:20:40 +00:00
VIsual_select = old_VIsual_select;
VIsual_active = old_VIsual_active;
curwin = old_curwin;
curbuf = old_curbuf;
}
/*
* Command character that's ignored.
* Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
* xon/xoff.
2004-06-13 20:20:40 +00:00
*/
static void
nv_ignore(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
2004-06-13 20:20:40 +00:00
}
2005-12-28 22:39:57 +00:00
/*
* Command character that doesn't do anything, but unlike nv_ignore() does
* start edit(). Used for "startinsert" executed while starting up.
*/
static void
nv_nop(cmdarg_T *cap UNUSED)
2005-12-28 22:39:57 +00:00
{
}
2004-06-13 20:20:40 +00:00
/*
* Command character doesn't exist.
*/
static void
nv_error(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
clearopbeep(cap->oap);
}
/*
* <Help> and <F1> commands.
*/
static void
nv_help(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (!checkclearopq(cap->oap))
ex_help(NULL);
}
/*
* CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
*/
static void
nv_addsub(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf) && !prompt_curpos_editable())
clearopbeep(cap->oap);
else
#endif
if (!VIsual_active && cap->oap->op_type == OP_NOP)
{
prep_redo_cmd(cap);
cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
op_addsub(cap->oap, cap->count1, cap->arg);
cap->oap->op_type = OP_NOP;
}
else if (VIsual_active)
nv_operator(cap);
else
clearop(cap->oap);
2004-06-13 20:20:40 +00:00
}
/*
* CTRL-F, CTRL-B, etc: Scroll page up or down.
*/
static void
nv_page(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearop(cap->oap))
return;
if (mod_mask & MOD_MASK_CTRL)
2006-04-05 20:41:53 +00:00
{
// <C-PageUp>: tab page back; <C-PageDown>: tab page forward
if (cap->arg == BACKWARD)
goto_tabpage(-(int)cap->count1);
2006-04-05 20:41:53 +00:00
else
goto_tabpage((int)cap->count0);
2006-04-05 20:41:53 +00:00
}
else
(void)pagescroll(cap->arg, cap->count1, FALSE);
2004-06-13 20:20:40 +00:00
}
/*
* Implementation of "gd" and "gD" command.
*/
static void
nv_gd(
oparg_T *oap,
int nchar,
int thisblock) // 1 for "1gd" and "1gD"
2004-06-13 20:20:40 +00:00
{
int len;
2005-09-05 22:05:30 +00:00
char_u *ptr;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
|| find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)
== FAIL)
{
2005-09-05 22:05:30 +00:00
clearopbeep(oap);
return;
}
2005-09-05 22:05:30 +00:00
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP)
foldOpenCursor();
2005-09-05 22:05:30 +00:00
#endif
// clear any search statistics
if (messaging() && !msg_silent && !shortmess(SHM_SEARCHCOUNT))
clear_cmdline = TRUE;
2005-09-05 22:05:30 +00:00
}
/*
* Return TRUE if line[offset] is not inside a C-style comment or string, FALSE
* otherwise.
*/
static int
is_ident(char_u *line, int offset)
{
int i;
int incomment = FALSE;
int instring = 0;
int prev = 0;
for (i = 0; i < offset && line[i] != NUL; i++)
{
if (instring != 0)
{
if (prev != '\\' && line[i] == instring)
instring = 0;
}
else if ((line[i] == '"' || line[i] == '\'') && !incomment)
{
instring = line[i];
}
else
{
if (incomment)
{
if (prev == '*' && line[i] == '/')
incomment = FALSE;
}
else if (prev == '/' && line[i] == '*')
{
incomment = TRUE;
}
else if (prev == '/' && line[i] == '/')
{
return FALSE;
}
}
prev = line[i];
}
return incomment == FALSE && instring == 0;
}
2005-09-05 22:05:30 +00:00
/*
2005-09-13 21:20:47 +00:00
* Search for variable declaration of "ptr[len]".
* When "locally" is TRUE in the current function ("gd"), otherwise in the
* current file ("gD").
* When "thisblock" is TRUE check the {} block scope.
2005-09-05 22:05:30 +00:00
* Return FAIL when not found.
*/
int
find_decl(
char_u *ptr,
int len,
int locally,
int thisblock,
int flags_arg) // flags passed to searchit()
2005-09-05 22:05:30 +00:00
{
2004-06-13 20:20:40 +00:00
char_u *pat;
size_t patlen;
2004-06-13 20:20:40 +00:00
pos_T old_pos;
2005-09-05 22:05:30 +00:00
pos_T par_pos;
pos_T found_pos;
2004-06-13 20:20:40 +00:00
int t;
int save_p_ws;
int save_p_scs;
2005-09-05 22:05:30 +00:00
int retval = OK;
2006-08-29 15:30:07 +00:00
int incll;
int searchflags = flags_arg;
int valid;
2004-06-13 20:20:40 +00:00
2005-09-05 22:05:30 +00:00
if ((pat = alloc(len + 7)) == NULL)
return FAIL;
2005-05-18 22:10:28 +00:00
// Put "\V" before the pattern to avoid that the special meaning of "."
// and "~" causes trouble.
patlen = vim_snprintf((char *)pat, len + 7,
vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", len, ptr);
2004-06-13 20:20:40 +00:00
old_pos = curwin->w_cursor;
save_p_ws = p_ws;
save_p_scs = p_scs;
p_ws = FALSE; // don't wrap around end of file now
p_scs = FALSE; // don't switch ignorecase off now
2004-06-13 20:20:40 +00:00
// With "gD" go to line 1.
// With "gd" Search back for the start of the current function, then go
// back until a blank line. If this fails go to line 1.
2006-08-29 15:30:07 +00:00
if (!locally || !findpar(&incll, BACKWARD, 1L, '{', FALSE))
2004-06-13 20:20:40 +00:00
{
setpcmark(); // Set in findpar() otherwise
2004-06-13 20:20:40 +00:00
curwin->w_cursor.lnum = 1;
2005-10-03 21:52:09 +00:00
par_pos = curwin->w_cursor;
2004-06-13 20:20:40 +00:00
}
else
{
2005-10-03 21:52:09 +00:00
par_pos = curwin->w_cursor;
2004-06-13 20:20:40 +00:00
while (curwin->w_cursor.lnum > 1 && *skipwhite(ml_get_curline()) != NUL)
--curwin->w_cursor.lnum;
}
curwin->w_cursor.col = 0;
// Search forward for the identifier, ignore comment lines.
CLEAR_POS(&found_pos);
2005-09-05 22:05:30 +00:00
for (;;)
{
t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
pat, patlen, 1L, searchflags, RE_LAST, NULL);
2005-09-05 22:05:30 +00:00
if (curwin->w_cursor.lnum >= old_pos.lnum)
t = FAIL; // match after start is failure too
2005-09-13 21:20:47 +00:00
2006-03-09 22:27:48 +00:00
if (thisblock && t != FAIL)
2005-09-13 21:20:47 +00:00
{
pos_T *pos;
// Check that the block the match is in doesn't end before the
// position where we started the search from.
2005-09-13 21:20:47 +00:00
if ((pos = findmatchlimit(NULL, '}', FM_FORWARD,
(int)(old_pos.lnum - curwin->w_cursor.lnum + 1))) != NULL
&& pos->lnum < old_pos.lnum)
{
// There can't be a useful match before the end of this block.
// Skip to the end.
curwin->w_cursor = *pos;
2005-09-13 21:20:47 +00:00
continue;
}
2005-09-13 21:20:47 +00:00
}
2005-09-05 22:05:30 +00:00
if (t == FAIL)
{
// If we previously found a valid position, use it.
2005-09-05 22:05:30 +00:00
if (found_pos.lnum != 0)
{
curwin->w_cursor = found_pos;
t = OK;
}
break;
}
if (get_leader_len(ml_get_curline(), NULL, FALSE, TRUE) > 0)
2005-09-05 22:05:30 +00:00
{
// Ignore this line, continue at start of next line.
2005-09-05 22:05:30 +00:00
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
continue;
}
valid = is_ident(ml_get_curline(), curwin->w_cursor.col);
// If the current position is not a valid identifier and a previous
// match is present, favor that one instead.
if (!valid && found_pos.lnum != 0)
{
curwin->w_cursor = found_pos;
2005-09-05 22:05:30 +00:00
break;
}
// Global search: use first valid match found
if (valid && !locally)
break;
if (valid && curwin->w_cursor.lnum >= par_pos.lnum)
2005-09-05 22:05:30 +00:00
{
// If we previously found a valid position, use it.
2005-09-05 22:05:30 +00:00
if (found_pos.lnum != 0)
curwin->w_cursor = found_pos;
break;
}
// For finding a local variable and the match is before the "{" or
// inside a comment, continue searching. For K&R style function
// declarations this skips the function header without types.
if (!valid)
CLEAR_POS(&found_pos);
else
found_pos = curwin->w_cursor;
// Remove SEARCH_START from flags to avoid getting stuck at one
// position.
searchflags &= ~SEARCH_START;
2004-06-13 20:20:40 +00:00
}
2005-09-05 22:05:30 +00:00
if (t == FAIL)
2004-06-13 20:20:40 +00:00
{
2005-09-05 22:05:30 +00:00
retval = FAIL;
2004-06-13 20:20:40 +00:00
curwin->w_cursor = old_pos;
}
else
{
curwin->w_set_curswant = TRUE;
// "n" searches forward now
2004-06-13 20:20:40 +00:00
reset_search_dir();
}
vim_free(pat);
p_ws = save_p_ws;
p_scs = save_p_scs;
2005-09-05 22:05:30 +00:00
return retval;
2004-06-13 20:20:40 +00:00
}
/*
* Move 'dist' lines in direction 'dir', counting lines by *screen*
* lines rather than lines in the file.
* 'dist' must be positive.
*
* Return OK if able to move cursor, FAIL otherwise.
*/
int
nv_screengo(oparg_T *oap, int dir, long dist)
2004-06-13 20:20:40 +00:00
{
int linelen = linetabsize_no_outer(curwin, curwin->w_cursor.lnum);
2004-06-13 20:20:40 +00:00
int retval = OK;
int atend = FALSE;
int n;
int col_off1; // margin offset for first screen line
int col_off2; // margin offset for wrapped screen line
int width1; // text width for first screen line
int width2; // text width for wrapped screen line
2004-06-13 20:20:40 +00:00
oap->motion_type = MCHAR;
oap->inclusive = (curwin->w_curswant == MAXCOL);
2004-06-13 20:20:40 +00:00
col_off1 = curwin_col_off();
col_off2 = col_off1 - curwin_col_off2();
width1 = curwin->w_width - col_off1;
width2 = curwin->w_width - col_off2;
if (width2 == 0)
width2 = 1; // avoid divide by zero
2004-06-13 20:20:40 +00:00
if (curwin->w_width != 0)
{
// Instead of sticking at the last character of the buffer line we
// try to stick in the last column of the screen.
2004-06-13 20:20:40 +00:00
if (curwin->w_curswant == MAXCOL)
{
atend = TRUE;
validate_virtcol();
if (width1 <= 0)
curwin->w_curswant = 0;
else
{
curwin->w_curswant = width1 - 1;
if (curwin->w_virtcol > curwin->w_curswant)
curwin->w_curswant += ((curwin->w_virtcol
- curwin->w_curswant - 1) / width2 + 1) * width2;
}
}
else
{
if (linelen > width1)
n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
else
n = width1;
if (curwin->w_curswant >= (colnr_T)n)
curwin->w_curswant = n - 1;
2004-06-13 20:20:40 +00:00
}
while (dist--)
{
if (dir == BACKWARD)
{
if ((long)curwin->w_curswant >= width1
#ifdef FEAT_FOLDING
&& !hasFolding(curwin->w_cursor.lnum, NULL, NULL)
#endif
)
// Move back within the line. This can give a negative value
// for w_curswant if width1 < width2 (with cpoptions+=n),
// which will get clipped to column 0.
2004-06-13 20:20:40 +00:00
curwin->w_curswant -= width2;
else
{
// to previous line
if (curwin->w_cursor.lnum <= 1)
{
retval = FAIL;
break;
}
cursor_up_inner(curwin, 1);
linelen = linetabsize_no_outer(curwin, curwin->w_cursor.lnum);
2004-06-13 20:20:40 +00:00
if (linelen > width1)
curwin->w_curswant += (((linelen - width1 - 1) / width2)
+ 1) * width2;
}
}
else // dir == FORWARD
2004-06-13 20:20:40 +00:00
{
if (linelen > width1)
n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
else
n = width1;
if (curwin->w_curswant + width2 < (colnr_T)n
#ifdef FEAT_FOLDING
&& !hasFolding(curwin->w_cursor.lnum, NULL, NULL)
#endif
)
// move forward within line
2004-06-13 20:20:40 +00:00
curwin->w_curswant += width2;
else
{
// to next line
if (curwin->w_cursor.lnum
>= curwin->w_buffer->b_ml.ml_line_count)
2004-06-13 20:20:40 +00:00
{
retval = FAIL;
break;
}
cursor_down_inner(curwin, 1);
2004-06-13 20:20:40 +00:00
curwin->w_curswant %= width2;
// Check if the cursor has moved below the number display
// when width1 < width2 (with cpoptions+=n). Subtract width2
// to get a negative value for w_curswant, which will get
// clipped to column 0.
if (curwin->w_curswant >= width1)
curwin->w_curswant -= width2;
linelen = linetabsize_no_outer(curwin, curwin->w_cursor.lnum);
2004-06-13 20:20:40 +00:00
}
}
}
}
if (virtual_active() && atend)
coladvance(MAXCOL);
else
coladvance(curwin->w_curswant);
2004-06-13 20:20:40 +00:00
if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
{
colnr_T virtcol;
int c;
// Check for landing on a character that got split at the end of the
// last line. We want to advance a screenline, not end up in the same
// screenline or move two screenlines.
2004-06-13 20:20:40 +00:00
validate_virtcol();
virtcol = curwin->w_virtcol;
#if defined(FEAT_LINEBREAK)
if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL)
virtcol -= vim_strsize(get_showbreak_value(curwin));
#endif
c = (*mb_ptr2char)(ml_get_cursor());
if (dir == FORWARD && virtcol < curwin->w_curswant
&& (curwin->w_curswant <= (colnr_T)width1)
&& !vim_isprintc(c) && c > 255)
oneright();
if (virtcol > curwin->w_curswant
2004-06-13 20:20:40 +00:00
&& (curwin->w_curswant < (colnr_T)width1
? (curwin->w_curswant > (colnr_T)width1 / 2)
: ((curwin->w_curswant - width1) % width2
> (colnr_T)width2 / 2)))
--curwin->w_cursor.col;
}
if (atend)
curwin->w_curswant = MAXCOL; // stick in the last column
adjust_skipcol();
2004-06-13 20:20:40 +00:00
return retval;
}
/*
* Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
* cap->arg must be TRUE for CTRL-E.
*/
void
nv_scroll_line(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (!checkclearop(cap->oap))
scroll_redraw(cap->arg, cap->count1);
}
/*
* Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh',
* 'z<Left>', and 'z<Right>' commands accept a count after 'z'.
* Returns TRUE to process the 'z' command and FALSE to skip it.
*/
static int
nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
{
int nchar = *nchar_arg;
long n;
// "z123{nchar}": edit the count before obtaining {nchar}
if (checkclearop(cap->oap))
return FALSE;
n = nchar - '0';
for (;;)
{
#ifdef USE_ON_FLY_SCROLL
dont_scroll = TRUE; // disallow scrolling here
#endif
++no_mapping;
++allow_keys; // no mapping for nchar, but allow key codes
nchar = plain_vgetc();
LANGMAP_ADJUST(nchar, TRUE);
--no_mapping;
--allow_keys;
(void)add_to_showcmd(nchar);
if (nchar == K_DEL || nchar == K_KDEL)
n /= 10;
else if (VIM_ISDIGIT(nchar))
{
if (vim_append_digit_long(&n, nchar - '0') == FAIL)
{
clearopbeep(cap->oap);
break;
}
}
else if (nchar == CAR)
{
#ifdef FEAT_GUI
need_mouse_correct = TRUE;
#endif
win_setheight((int)n);
break;
}
else if (nchar == 'l'
|| nchar == 'h'
|| nchar == K_LEFT
|| nchar == K_RIGHT)
{
cap->count1 = n ? n * cap->count1 : cap->count1;
*nchar_arg = nchar;
return TRUE;
}
else
{
clearopbeep(cap->oap);
break;
}
}
cap->oap->op_type = OP_NOP;
return FALSE;
}
#ifdef FEAT_SPELL
/*
* "zug" and "zuw": undo "zg" and "zw"
* "zg": add good word to word list
* "zw": add wrong word to word list
* "zG": add good word to temp word list
* "zW": add wrong word to temp word list
*/
static int
nv_zg_zw(cmdarg_T *cap, int nchar)
{
char_u *ptr = NULL;
int len;
int undo = FALSE;
if (nchar == 'u')
{
++no_mapping;
++allow_keys; // no mapping for nchar, but allow key codes
nchar = plain_vgetc();
LANGMAP_ADJUST(nchar, TRUE);
--no_mapping;
--allow_keys;
(void)add_to_showcmd(nchar);
if (vim_strchr((char_u *)"gGwW", nchar) == NULL)
{
clearopbeep(cap->oap);
return OK;
}
undo = TRUE;
}
if (checkclearop(cap->oap))
return OK;
if (VIsual_active && get_visual_text(cap, &ptr, &len) == FAIL)
return FAIL;
if (ptr == NULL)
{
pos_T pos = curwin->w_cursor;
// Find bad word under the cursor. When 'spell' is
// off this fails and find_ident_under_cursor() is
// used below.
emsg_off++;
len = spell_move_to(curwin, FORWARD, SMT_ALL, TRUE, NULL);
emsg_off--;
if (len != 0 && curwin->w_cursor.col <= pos.col)
ptr = ml_get_pos(&curwin->w_cursor);
curwin->w_cursor = pos;
}
if (ptr == NULL
&& (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
return FAIL;
spell_add_word(ptr, len, nchar == 'w' || nchar == 'W'
? SPELL_ADD_BAD : SPELL_ADD_GOOD,
(nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1, undo);
return OK;
}
#endif
2004-06-13 20:20:40 +00:00
/*
* Commands that start with "z".
*/
static void
nv_zet(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
long n;
colnr_T col;
int nchar = cap->nchar;
#ifdef FEAT_FOLDING
long old_fdl = curwin->w_p_fdl;
int old_fen = curwin->w_p_fen;
2006-03-04 21:46:13 +00:00
#endif
long siso = get_sidescrolloff_value();
2004-06-13 20:20:40 +00:00
if (VIM_ISDIGIT(nchar) && !nv_z_get_count(cap, &nchar))
2004-06-13 20:20:40 +00:00
return;
if (
#ifdef FEAT_FOLDING
// "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
// and "zC" only in Visual mode. "zj" and "zk" are motion
// commands.
2004-06-13 20:20:40 +00:00
cap->nchar != 'f' && cap->nchar != 'F'
&& !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
&& cap->nchar != 'j' && cap->nchar != 'k'
&&
#endif
checkclearop(cap->oap))
return;
// For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
// If line number given, set cursor.
2004-06-13 20:20:40 +00:00
if ((vim_strchr((char_u *)"+\r\nt.z^-b", nchar) != NULL)
&& cap->count0
&& cap->count0 != curwin->w_cursor.lnum)
{
setpcmark();
if (cap->count0 > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = cap->count0;
2004-09-02 19:12:26 +00:00
check_cursor_col();
2004-06-13 20:20:40 +00:00
}
switch (nchar)
{
// "z+", "z<CR>" and "zt": put cursor at top of screen
2004-06-13 20:20:40 +00:00
case '+':
if (cap->count0 == 0)
{
// No count given: put cursor at the line below screen
validate_botline(); // make sure w_botline is valid
2004-06-13 20:20:40 +00:00
if (curwin->w_botline > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = curwin->w_botline;
}
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case NL:
case CAR:
case K_KENTER:
beginline(BL_WHITE | BL_FIX);
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case 't': scroll_cursor_top(0, TRUE);
redraw_later(UPD_VALID);
set_fraction(curwin);
2004-06-13 20:20:40 +00:00
break;
// "z." and "zz": put cursor in middle of screen
2004-06-13 20:20:40 +00:00
case '.': beginline(BL_WHITE | BL_FIX);
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case 'z': scroll_cursor_halfway(TRUE, FALSE);
redraw_later(UPD_VALID);
set_fraction(curwin);
2004-06-13 20:20:40 +00:00
break;
// "z^", "z-" and "zb": put cursor at bottom of screen
case '^': // Strange Vi behavior: <count>z^ finds line at top of window
// when <count> is at bottom of window, and puts that one at
// bottom of window.
2004-06-13 20:20:40 +00:00
if (cap->count0 != 0)
{
scroll_cursor_bot(0, TRUE);
curwin->w_cursor.lnum = curwin->w_topline;
}
else if (curwin->w_topline == 1)
curwin->w_cursor.lnum = 1;
else
curwin->w_cursor.lnum = curwin->w_topline - 1;
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case '-':
beginline(BL_WHITE | BL_FIX);
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case 'b': scroll_cursor_bot(0, TRUE);
redraw_later(UPD_VALID);
set_fraction(curwin);
2004-06-13 20:20:40 +00:00
break;
// "zH" - scroll screen right half-page
2004-06-13 20:20:40 +00:00
case 'H':
cap->count1 *= curwin->w_width / 2;
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
// "zh" - scroll screen to the right
2004-06-13 20:20:40 +00:00
case 'h':
case K_LEFT:
if (!curwin->w_p_wrap)
(void)set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol
? 0 : curwin->w_leftcol - (colnr_T)cap->count1);
2004-06-13 20:20:40 +00:00
break;
// "zL" - scroll window left half-page
case 'L': cap->count1 *= curwin->w_width / 2;
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
// "zl" - scroll window to the left if not wrapping
2004-06-13 20:20:40 +00:00
case 'l':
case K_RIGHT:
if (!curwin->w_p_wrap)
(void)set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1);
2004-06-13 20:20:40 +00:00
break;
// "zs" - scroll screen, cursor at the start
2004-06-13 20:20:40 +00:00
case 's': if (!curwin->w_p_wrap)
{
#ifdef FEAT_FOLDING
if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
col = 0; // like the cursor is in col 0
2004-06-13 20:20:40 +00:00
else
#endif
getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
if ((long)col > siso)
col -= siso;
2004-06-13 20:20:40 +00:00
else
col = 0;
if (curwin->w_leftcol != col)
{
curwin->w_leftcol = col;
redraw_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
}
break;
// "ze" - scroll screen, cursor at the end
2004-06-13 20:20:40 +00:00
case 'e': if (!curwin->w_p_wrap)
{
#ifdef FEAT_FOLDING
if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
col = 0; // like the cursor is in col 0
2004-06-13 20:20:40 +00:00
else
#endif
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
n = curwin->w_width - curwin_col_off();
if ((long)col + siso < n)
2004-06-13 20:20:40 +00:00
col = 0;
else
col = col + siso - n + 1;
2004-06-13 20:20:40 +00:00
if (curwin->w_leftcol != col)
{
curwin->w_leftcol = col;
redraw_later(UPD_NOT_VALID);
2004-06-13 20:20:40 +00:00
}
}
break;
// "zp", "zP" in block mode put without addind trailing spaces
case 'P':
case 'p': nv_put(cap);
break;
// "zy" Yank without trailing spaces
case 'y': nv_operator(cap);
break;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_FOLDING
// "zF": create fold command
// "zf": create fold operator
2004-06-13 20:20:40 +00:00
case 'F':
case 'f': if (foldManualAllowed(TRUE))
{
cap->nchar = 'f';
nv_operator(cap);
curwin->w_p_fen = TRUE;
// "zF" is like "zfzf"
2004-06-13 20:20:40 +00:00
if (nchar == 'F' && cap->oap->op_type == OP_FOLD)
{
nv_operator(cap);
finish_op = TRUE;
}
}
else
clearopbeep(cap->oap);
break;
// "zd": delete fold at cursor
// "zD": delete fold at cursor recursively
2004-06-13 20:20:40 +00:00
case 'd':
case 'D': if (foldManualAllowed(FALSE))
{
if (VIsual_active)
nv_operator(cap);
else
deleteFold(curwin->w_cursor.lnum,
curwin->w_cursor.lnum, nchar == 'D', FALSE);
}
break;
// "zE": erase all folds
2004-06-13 20:20:40 +00:00
case 'E': if (foldmethodIsManual(curwin))
{
clearFolding(curwin);
changed_window_setting();
}
else if (foldmethodIsMarker(curwin))
deleteFold((linenr_T)1, curbuf->b_ml.ml_line_count,
TRUE, FALSE);
else
emsg(_(e_cannot_erase_folds_with_current_foldmethod));
2004-06-13 20:20:40 +00:00
break;
// "zn": fold none: reset 'foldenable'
2004-06-13 20:20:40 +00:00
case 'n': curwin->w_p_fen = FALSE;
break;
// "zN": fold Normal: set 'foldenable'
2004-06-13 20:20:40 +00:00
case 'N': curwin->w_p_fen = TRUE;
break;
// "zi": invert folding: toggle 'foldenable'
2004-06-13 20:20:40 +00:00
case 'i': curwin->w_p_fen = !curwin->w_p_fen;
break;
// "za": open closed fold or close open fold at cursor
2004-06-13 20:20:40 +00:00
case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
openFold(curwin->w_cursor.lnum, cap->count1);
else
{
closeFold(curwin->w_cursor.lnum, cap->count1);
curwin->w_p_fen = TRUE;
}
break;
// "zA": open fold at cursor recursively
2004-06-13 20:20:40 +00:00
case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
openFoldRecurse(curwin->w_cursor.lnum);
else
{
closeFoldRecurse(curwin->w_cursor.lnum);
curwin->w_p_fen = TRUE;
}
break;
// "zo": open fold at cursor or Visual area
2004-06-13 20:20:40 +00:00
case 'o': if (VIsual_active)
nv_operator(cap);
else
openFold(curwin->w_cursor.lnum, cap->count1);
break;
// "zO": open fold recursively
2004-06-13 20:20:40 +00:00
case 'O': if (VIsual_active)
nv_operator(cap);
else
openFoldRecurse(curwin->w_cursor.lnum);
break;
// "zc": close fold at cursor or Visual area
2004-06-13 20:20:40 +00:00
case 'c': if (VIsual_active)
nv_operator(cap);
else
closeFold(curwin->w_cursor.lnum, cap->count1);
curwin->w_p_fen = TRUE;
break;
// "zC": close fold recursively
2004-06-13 20:20:40 +00:00
case 'C': if (VIsual_active)
nv_operator(cap);
else
closeFoldRecurse(curwin->w_cursor.lnum);
curwin->w_p_fen = TRUE;
break;
// "zv": open folds at the cursor
2004-06-13 20:20:40 +00:00
case 'v': foldOpenCursor();
break;
// "zx": re-apply 'foldlevel' and open folds at the cursor
2004-06-13 20:20:40 +00:00
case 'x': curwin->w_p_fen = TRUE;
curwin->w_foldinvalid = TRUE; // recompute folds
newFoldLevel(); // update right now
2004-06-13 20:20:40 +00:00
foldOpenCursor();
break;
// "zX": undo manual opens/closes, re-apply 'foldlevel'
2004-06-13 20:20:40 +00:00
case 'X': curwin->w_p_fen = TRUE;
curwin->w_foldinvalid = TRUE; // recompute folds
old_fdl = -1; // force an update
2004-06-13 20:20:40 +00:00
break;
// "zm": fold more
2004-06-13 20:20:40 +00:00
case 'm': if (curwin->w_p_fdl > 0)
{
curwin->w_p_fdl -= cap->count1;
if (curwin->w_p_fdl < 0)
curwin->w_p_fdl = 0;
}
old_fdl = -1; // force an update
2004-06-13 20:20:40 +00:00
curwin->w_p_fen = TRUE;
break;
// "zM": close all folds
2004-06-13 20:20:40 +00:00
case 'M': curwin->w_p_fdl = 0;
old_fdl = -1; // force an update
2004-06-13 20:20:40 +00:00
curwin->w_p_fen = TRUE;
break;
// "zr": reduce folding
case 'r': curwin->w_p_fdl += cap->count1;
{
int d = getDeepestNesting();
if (curwin->w_p_fdl >= d)
curwin->w_p_fdl = d;
}
2004-06-13 20:20:40 +00:00
break;
// "zR": open all folds
2004-06-13 20:20:40 +00:00
case 'R': curwin->w_p_fdl = getDeepestNesting();
old_fdl = -1; // force an update
2004-06-13 20:20:40 +00:00
break;
case 'j': // "zj" move to next fold downwards
case 'k': // "zk" move to next fold upwards
2004-06-13 20:20:40 +00:00
if (foldMoveTo(TRUE, nchar == 'j' ? FORWARD : BACKWARD,
cap->count1) == FAIL)
clearopbeep(cap->oap);
break;
#endif // FEAT_FOLDING
2004-06-13 20:20:40 +00:00
2006-03-12 21:50:18 +00:00
#ifdef FEAT_SPELL
case 'u': // "zug" and "zuw": undo "zg" and "zw"
case 'g': // "zg": add good word to word list
case 'w': // "zw": add wrong word to word list
case 'G': // "zG": add good word to temp word list
case 'W': // "zW": add wrong word to temp word list
if (nv_zg_zw(cap, nchar) == FAIL)
return;
2005-06-08 21:56:31 +00:00
break;
2005-06-13 22:28:56 +00:00
case '=': // "z=": suggestions for a badly spelled word
2006-01-22 23:22:22 +00:00
if (!checkclearop(cap->oap))
2005-08-21 22:08:24 +00:00
spell_suggest((int)cap->count0);
2005-06-13 22:28:56 +00:00
break;
2005-06-07 21:00:02 +00:00
#endif
2004-06-13 20:20:40 +00:00
default: clearopbeep(cap->oap);
}
#ifdef FEAT_FOLDING
// Redraw when 'foldenable' changed
2004-06-13 20:20:40 +00:00
if (old_fen != curwin->w_p_fen)
{
# ifdef FEAT_DIFF
win_T *wp;
if (foldmethodIsDiff(curwin) && curwin->w_p_scb)
{
// Adjust 'foldenable' in diff-synced windows.
2004-06-13 20:20:40 +00:00
FOR_ALL_WINDOWS(wp)
{
if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb)
{
wp->w_p_fen = curwin->w_p_fen;
changed_window_setting_win(wp);
}
}
}
# endif
changed_window_setting();
}
// Redraw when 'foldlevel' changed.
2004-06-13 20:20:40 +00:00
if (old_fdl != curwin->w_p_fdl)
newFoldLevel();
#endif
}
#ifdef FEAT_GUI
/*
* Vertical scrollbar movement.
*/
static void
nv_ver_scrollbar(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->oap->op_type != OP_NOP)
clearopbeep(cap->oap);
// Even if an operator was pending, we still want to scroll
2004-06-13 20:20:40 +00:00
gui_do_scroll();
}
/*
* Horizontal scrollbar movement.
*/
static void
nv_hor_scrollbar(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->oap->op_type != OP_NOP)
clearopbeep(cap->oap);
// Even if an operator was pending, we still want to scroll
do_mousescroll_horiz(scrollbar_value);
2004-06-13 20:20:40 +00:00
}
#endif
2006-02-26 23:59:20 +00:00
#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2006-02-24 23:53:04 +00:00
/*
* Click in GUI tab.
*/
static void
nv_tabline(cmdarg_T *cap)
2006-02-24 23:53:04 +00:00
{
if (cap->oap->op_type != OP_NOP)
clearopbeep(cap->oap);
// Even if an operator was pending, we still want to jump tabs.
2006-02-24 23:53:04 +00:00
goto_tabpage(current_tab);
}
2006-02-25 21:45:02 +00:00
/*
* Selected item in tab line menu.
*/
static void
nv_tabmenu(cmdarg_T *cap)
2006-02-25 21:45:02 +00:00
{
if (cap->oap->op_type != OP_NOP)
clearopbeep(cap->oap);
// Even if an operator was pending, we still want to jump tabs.
2006-02-26 23:59:20 +00:00
handle_tabmenu();
}
/*
* Handle selecting an item of the GUI tab line menu.
* Used in Normal and Insert mode.
*/
void
handle_tabmenu(void)
2006-02-26 23:59:20 +00:00
{
2006-02-25 21:45:02 +00:00
switch (current_tabmenu)
{
case TABLINE_MENU_CLOSE:
if (current_tab == 0)
do_cmdline_cmd((char_u *)"tabclose");
else
{
vim_snprintf((char *)IObuff, IOSIZE, "tabclose %d",
current_tab);
do_cmdline_cmd(IObuff);
}
break;
case TABLINE_MENU_NEW:
if (current_tab == 0)
do_cmdline_cmd((char_u *)"$tabnew");
else
{
vim_snprintf((char *)IObuff, IOSIZE, "%dtabnew",
current_tab - 1);
do_cmdline_cmd(IObuff);
}
2006-02-25 21:45:02 +00:00
break;
case TABLINE_MENU_OPEN:
if (current_tab == 0)
do_cmdline_cmd((char_u *)"browse $tabnew");
else
{
vim_snprintf((char *)IObuff, IOSIZE, "browse %dtabnew",
current_tab - 1);
do_cmdline_cmd(IObuff);
}
2006-02-25 21:45:02 +00:00
break;
}
}
2006-02-24 23:53:04 +00:00
#endif
2004-06-13 20:20:40 +00:00
/*
* "Q" command.
*/
static void
nv_exmode(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
// Ignore 'Q' in Visual mode, just give a beep.
2004-06-13 20:20:40 +00:00
if (VIsual_active)
vim_beep(BO_EX);
else if (!checkclearop(cap->oap))
2004-06-13 20:20:40 +00:00
do_exmode(FALSE);
}
/*
* Handle a ":" command.
*/
static void
nv_colon(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int old_p_im;
int cmd_result;
int is_cmdkey = cap->cmdchar == K_COMMAND
|| cap->cmdchar == K_SCRIPT_COMMAND;
int flags;
2004-06-13 20:20:40 +00:00
if (VIsual_active && !is_cmdkey)
{
2004-06-13 20:20:40 +00:00
nv_operator(cap);
return;
}
if (cap->oap->op_type != OP_NOP)
2004-06-13 20:20:40 +00:00
{
// Using ":" as a movement is characterwise exclusive.
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
}
else if (cap->count0 && !is_cmdkey)
{
// translate "count:" into ":.,.+(count - 1)"
stuffcharReadbuff('.');
if (cap->count0 > 1)
2004-06-13 20:20:40 +00:00
{
stuffReadbuff((char_u *)",.+");
stuffnumReadbuff((long)cap->count0 - 1L);
2004-06-13 20:20:40 +00:00
}
}
2004-06-13 20:20:40 +00:00
// When typing, don't type below an old message
if (KeyTyped)
compute_cmdrow();
2004-06-13 20:20:40 +00:00
old_p_im = p_im;
2004-06-13 20:20:40 +00:00
// get a command line and execute it
flags = cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0;
if (is_cmdkey)
cmd_result = do_cmdkey_command(cap->cmdchar, flags);
else
cmd_result = do_cmdline(NULL, getexline, NULL, flags);
2004-06-13 20:20:40 +00:00
// If 'insertmode' changed, enter or exit Insert mode
if (p_im != old_p_im)
{
if (p_im)
restart_edit = 'i';
else
restart_edit = 0;
2004-06-13 20:20:40 +00:00
}
if (cmd_result == FAIL)
// The Ex command failed, do not execute the operator.
clearop(cap->oap);
else if (cap->oap->op_type != OP_NOP
&& (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
|| cap->oap->start.col > ml_get_len(cap->oap->start.lnum)
|| did_emsg
))
// The start of the operator has become invalid by the Ex command.
clearopbeep(cap->oap);
2004-06-13 20:20:40 +00:00
}
/*
* Handle CTRL-G command.
*/
static void
nv_ctrlg(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active) // toggle Selection/Visual mode
2004-06-13 20:20:40 +00:00
{
VIsual_select = !VIsual_select;
may_trigger_modechanged();
2004-06-13 20:20:40 +00:00
showmode();
}
else if (!checkclearop(cap->oap))
// print full name if count given or :cd used
2004-06-13 20:20:40 +00:00
fileinfo((int)cap->count0, FALSE, TRUE);
}
/*
* Handle CTRL-H <Backspace> command.
*/
static void
nv_ctrlh(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active && VIsual_select)
{
cap->cmdchar = 'x'; // BS key behaves like 'x' in Select mode
2004-06-13 20:20:40 +00:00
v_visop(cap);
}
else
nv_left(cap);
}
/*
* CTRL-L: clear screen and redraw.
*/
static void
nv_clear(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearop(cap->oap))
return;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_SYN_HL
// Clear all syntax states to force resyncing.
syn_stack_free_all(curwin->w_s);
# ifdef FEAT_RELTIME
{
win_T *wp;
FOR_ALL_WINDOWS(wp)
wp->w_s->b_syn_slow = FALSE;
}
# endif
2004-06-13 20:20:40 +00:00
#endif
redraw_later(UPD_CLEAR);
#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
# ifdef VIMDLL
if (!gui.in_use)
# endif
resize_console_buf();
#endif
2004-06-13 20:20:40 +00:00
}
/*
* CTRL-O: In Select mode: switch to Visual mode for one command.
* Otherwise: Go to older pcmark.
*/
static void
nv_ctrlo(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active && VIsual_select)
{
VIsual_select = FALSE;
may_trigger_modechanged();
2004-06-13 20:20:40 +00:00
showmode();
restart_VIsual_select = 2; // restart Select mode later
2004-06-13 20:20:40 +00:00
}
else
{
cap->count1 = -cap->count1;
nv_pcmark(cap);
}
}
/*
* CTRL-^ command, short for ":e #". Works even when the alternate buffer is
* not named.
2004-06-13 20:20:40 +00:00
*/
static void
nv_hat(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (!checkclearopq(cap->oap))
(void)buflist_getfile((int)cap->count0, (linenr_T)0,
GETF_SETMARK|GETF_ALT, FALSE);
}
/*
* "Z" commands.
*/
static void
nv_Zet(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearopq(cap->oap))
return;
switch (cap->nchar)
2004-06-13 20:20:40 +00:00
{
// "ZZ": equivalent to ":x".
case 'Z': do_cmdline_cmd((char_u *)"x");
2004-06-13 20:20:40 +00:00
break;
// "ZQ": equivalent to ":q!" (Elvis compatible).
case 'Q': do_cmdline_cmd((char_u *)"q!");
2004-06-13 20:20:40 +00:00
break;
default: clearopbeep(cap->oap);
2004-06-13 20:20:40 +00:00
}
}
/*
* Call nv_ident() as if "c1" was used, with "c2" as next character.
*/
void
do_nv_ident(int c1, int c2)
2004-06-13 20:20:40 +00:00
{
oparg_T oa;
cmdarg_T ca;
clear_oparg(&oa);
CLEAR_FIELD(ca);
2004-06-13 20:20:40 +00:00
ca.oap = &oa;
ca.cmdchar = c1;
ca.nchar = c2;
nv_ident(&ca);
}
/*
* 'K' normal-mode command. Get the command to lookup the keyword under the
* cursor.
*/
static int
nv_K_getcmd(
cmdarg_T *cap,
char_u *kp,
int kp_help,
int kp_ex,
char_u **ptr_arg,
int n,
char_u *buf,
size_t bufsize,
size_t *buflen)
{
char_u *ptr = *ptr_arg;
int isman;
int isman_s;
if (kp_help)
{
// in the help buffer
STRCPY(buf, "he! ");
*buflen = STRLEN_LITERAL("he! ");
return n;
}
if (kp_ex)
{
// 'keywordprog' is an ex command
if (cap->count0 != 0)
*buflen = vim_snprintf((char *)buf, bufsize, "%s %ld ", kp, cap->count0);
else
*buflen = vim_snprintf((char *)buf, bufsize, "%s ", kp);
return n;
}
// An external command will probably use an argument starting
// with "-" as an option. To avoid trouble we skip the "-".
while (*ptr == '-' && n > 0)
{
++ptr;
--n;
}
if (n == 0)
{
// found dashes only
emsg(_(e_no_identifier_under_cursor));
vim_free(buf);
*ptr_arg = ptr;
return 0;
}
// When a count is given, turn it into a range. Is this
// really what we want?
isman = (STRCMP(kp, "man") == 0);
isman_s = (STRCMP(kp, "man -s") == 0);
if (cap->count0 != 0 && !(isman || isman_s))
*buflen = vim_snprintf((char *)buf, bufsize, ".,.+%ld! ", cap->count0 - 1);
else
*buflen = vim_snprintf((char *)buf, bufsize, "! ");
if (cap->count0 == 0 && isman_s)
*buflen += vim_snprintf((char *)buf + *buflen, bufsize - *buflen, "man ");
else
*buflen += vim_snprintf((char *)buf + *buflen, bufsize - *buflen, "%s ", kp);
if (cap->count0 != 0 && (isman || isman_s))
*buflen += vim_snprintf((char *)buf + *buflen, bufsize - *buflen, "%ld ", cap->count0);
*ptr_arg = ptr;
return n;
}
2004-06-13 20:20:40 +00:00
/*
* Handle the commands that use the word under the cursor.
* [g] CTRL-] :ta to current identifier
* [g] 'K' run program for current identifier
* [g] '*' / to current identifier or string
* [g] '#' ? to current identifier or string
* g ']' :tselect for current identifier
*/
static void
nv_ident(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
char_u *ptr = NULL;
char_u *buf;
size_t bufsize;
size_t buflen;
2010-07-10 13:52:13 +02:00
char_u *newbuf;
2004-06-13 20:20:40 +00:00
char_u *p;
char_u *kp; // value of 'keywordprg'
int kp_help; // 'keywordprg' is ":he"
int kp_ex; // 'keywordprg' starts with ":"
int n = 0; // init for GCC
2004-06-13 20:20:40 +00:00
int cmdchar;
int g_cmd; // "g" command
int tag_cmd = FALSE;
2004-06-13 20:20:40 +00:00
char_u *aux_ptr;
if (cap->cmdchar == 'g') // "g*", "g#", "g]" and "gCTRL-]"
2004-06-13 20:20:40 +00:00
{
cmdchar = cap->nchar;
g_cmd = TRUE;
}
else
{
cmdchar = cap->cmdchar;
g_cmd = FALSE;
}
if (cmdchar == POUND) // the pound sign, '#' for English keyboards
2004-06-13 20:20:40 +00:00
cmdchar = '#';
// The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
2004-06-13 20:20:40 +00:00
if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K')
{
if (VIsual_active && get_visual_text(cap, &ptr, &n) == FAIL)
return;
if (checkclearopq(cap->oap))
return;
}
if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
(cmdchar == '*' || cmdchar == '#')
? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
{
clearop(cap->oap);
return;
}
// Allocate buffer to put the command in. Inserting backslashes can
// double the length of the word. p_kp / curbuf->b_p_kp could be added
// and some numbers.
2004-06-13 20:20:40 +00:00
kp = (*curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp);
kp_help = (*kp == NUL || STRCMP(kp, ":he") == 0
|| STRCMP(kp, ":help") == 0);
if (kp_help && *skipwhite(ptr) == NUL)
{
emsg(_(e_no_identifier_under_cursor)); // found white space only
return;
}
kp_ex = (*kp == ':');
bufsize = (size_t)(n * 2 + 30 + STRLEN(kp));
buf = alloc(bufsize);
2004-06-13 20:20:40 +00:00
if (buf == NULL)
return;
buf[0] = NUL;
buflen = 0;
2004-06-13 20:20:40 +00:00
switch (cmdchar)
{
case '*':
case '#':
// Put cursor at start of word, makes search skip the word
// under the cursor.
// Call setpcmark() first, so "*``" puts the cursor back where
// it was.
2004-06-13 20:20:40 +00:00
setpcmark();
curwin->w_cursor.col = (colnr_T) (ptr - ml_get_curline());
if (!g_cmd && vim_iswordp(ptr))
{
2004-06-13 20:20:40 +00:00
STRCPY(buf, "\\<");
buflen = STRLEN_LITERAL("\\<");
}
no_smartcase = TRUE; // don't use 'smartcase' now
2004-06-13 20:20:40 +00:00
break;
case 'K':
n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, bufsize, &buflen);
if (n == 0)
return;
2004-06-13 20:20:40 +00:00
break;
case ']':
tag_cmd = TRUE;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_CSCOPE
if (p_cst)
{
2004-06-13 20:20:40 +00:00
STRCPY(buf, "cstag ");
buflen = STRLEN_LITERAL("cstag ");
}
2004-06-13 20:20:40 +00:00
else
#endif
{
2004-06-13 20:20:40 +00:00
STRCPY(buf, "ts ");
buflen = STRLEN_LITERAL("ts ");
}
2004-06-13 20:20:40 +00:00
break;
default:
tag_cmd = TRUE;
2004-06-13 20:20:40 +00:00
if (curbuf->b_help)
{
2004-06-13 20:20:40 +00:00
STRCPY(buf, "he! ");
buflen = STRLEN_LITERAL("he! ");
}
2004-06-13 20:20:40 +00:00
else
{
if (g_cmd)
{
STRCPY(buf, "tj ");
buflen = STRLEN_LITERAL("tj ");
}
else if (cap->count0 == 0)
{
STRCPY(buf, "ta ");
buflen = STRLEN_LITERAL("ta ");
}
else
buflen = vim_snprintf((char *)buf, bufsize, ":%ldta ", cap->count0);
}
2004-06-13 20:20:40 +00:00
}
// Now grab the chars in the identifier
2008-09-06 14:44:59 +00:00
if (cmdchar == 'K' && !kp_help)
{
size_t plen;
2008-11-01 12:52:38 +00:00
ptr = vim_strnsave(ptr, n);
if (kp_ex)
// Escape the argument properly for an Ex command
p = vim_strsave_fnameescape(ptr, VSE_NONE);
else
// Escape the argument properly for a shell command
p = vim_strsave_shellescape(ptr, TRUE, TRUE);
2008-11-01 12:52:38 +00:00
vim_free(ptr);
2008-09-06 14:44:59 +00:00
if (p == NULL)
{
vim_free(buf);
return;
}
plen = STRLEN(p);
newbuf = vim_realloc(buf, buflen + plen + 1);
2010-07-10 13:52:13 +02:00
if (newbuf == NULL)
2008-09-06 14:44:59 +00:00
{
vim_free(buf);
vim_free(p);
return;
}
2010-07-10 13:52:13 +02:00
buf = newbuf;
STRCPY(buf + buflen, p);
buflen += plen;
2008-09-06 14:44:59 +00:00
vim_free(p);
}
2004-06-13 20:20:40 +00:00
else
{
2008-09-06 14:44:59 +00:00
if (cmdchar == '*')
aux_ptr = (char_u *)(magic_isset() ? "/.*~[^$\\" : "/^$\\");
2008-09-06 14:44:59 +00:00
else if (cmdchar == '#')
aux_ptr = (char_u *)(magic_isset() ? "/?.*~[^$\\" : "/?^$\\");
else if (tag_cmd)
{
if (curbuf->b_help)
// ":help" handles unescaped argument
aux_ptr = (char_u *)"";
else
aux_ptr = (char_u *)"\\|\"\n[";
}
else
2008-09-06 14:44:59 +00:00
aux_ptr = (char_u *)"\\|\"\n*?[";
p = buf + buflen;
2008-09-06 14:44:59 +00:00
while (n-- > 0)
2004-06-13 20:20:40 +00:00
{
// put a backslash before \ and some others
2008-09-06 14:44:59 +00:00
if (vim_strchr(aux_ptr, *ptr) != NULL)
*p++ = '\\';
// When current byte is a part of multibyte character, copy all
// bytes of that character.
2008-09-06 14:44:59 +00:00
if (has_mbyte)
{
int i;
int len = (*mb_ptr2len)(ptr) - 1;
2004-06-13 20:20:40 +00:00
2008-09-06 14:44:59 +00:00
for (i = 0; i < len && n >= 1; ++i, --n)
*p++ = *ptr++;
}
*p++ = *ptr++;
}
*p = NUL;
buflen = p - buf;
2004-06-13 20:20:40 +00:00
}
// Execute the command.
2004-06-13 20:20:40 +00:00
if (cmdchar == '*' || cmdchar == '#')
{
if (!g_cmd && (has_mbyte
? vim_iswordp(mb_prevptr(ml_get_curline(), ptr))
: vim_iswordc(ptr[-1])))
{
STRCPY(buf + buflen, "\\>");
buflen += STRLEN_LITERAL("\\>");
}
// put pattern in search history
2009-12-24 14:01:12 +00:00
init_history();
add_to_history(HIST_SEARCH, buf, buflen, TRUE, NUL);
(void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, buflen, 0, NULL);
2004-06-13 20:20:40 +00:00
}
else
{
g_tag_at_cursor = TRUE;
2004-06-13 20:20:40 +00:00
do_cmdline_cmd(buf);
g_tag_at_cursor = FALSE;
}
2004-06-13 20:20:40 +00:00
vim_free(buf);
}
/*
* Get visually selected text, within one line only.
* Returns FAIL if more than one line selected.
*/
2005-06-21 22:37:39 +00:00
int
get_visual_text(
cmdarg_T *cap,
char_u **pp, // return: start of selected text
int *lenp) // return: length of selected text
2004-06-13 20:20:40 +00:00
{
if (VIsual_mode != 'V')
unadjust_for_sel();
if (VIsual.lnum != curwin->w_cursor.lnum)
{
2005-06-21 22:37:39 +00:00
if (cap != NULL)
clearopbeep(cap->oap);
2004-06-13 20:20:40 +00:00
return FAIL;
}
if (VIsual_mode == 'V')
{
*pp = ml_get_curline();
*lenp = (int)ml_get_curline_len();
2004-06-13 20:20:40 +00:00
}
else
{
if (LT_POS(curwin->w_cursor, VIsual))
2004-06-13 20:20:40 +00:00
{
*pp = ml_get_pos(&curwin->w_cursor);
*lenp = VIsual.col - curwin->w_cursor.col + 1;
}
else
{
*pp = ml_get_pos(&VIsual);
*lenp = curwin->w_cursor.col - VIsual.col + 1;
}
if (**pp == NUL)
*lenp = 0;
if (*lenp > 0)
{
if (has_mbyte)
// Correct the length to include all bytes of the last
// character.
*lenp += (*mb_ptr2len)(*pp + (*lenp - 1)) - 1;
else if ((*pp)[*lenp - 1] == NUL)
// Do not include a trailing NUL.
*lenp -= 1;
}
2004-06-13 20:20:40 +00:00
}
reset_VIsual_and_resel();
return OK;
}
/*
* CTRL-T: backwards in tag stack
*/
static void
nv_tagpop(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (!checkclearopq(cap->oap))
do_tag((char_u *)"", DT_POP, (int)cap->count1, FALSE, TRUE);
}
/*
* Handle scrolling command 'H', 'L' and 'M'.
*/
static void
nv_scroll(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int used = 0;
long n;
#ifdef FEAT_FOLDING
linenr_T lnum;
#endif
int half;
cap->oap->motion_type = MLINE;
setpcmark();
if (cap->cmdchar == 'L')
{
validate_botline(); // make sure curwin->w_botline is valid
2004-06-13 20:20:40 +00:00
curwin->w_cursor.lnum = curwin->w_botline - 1;
if (cap->count1 - 1 >= curwin->w_cursor.lnum)
curwin->w_cursor.lnum = 1;
else
2004-06-20 12:51:53 +00:00
{
#ifdef FEAT_FOLDING
if (hasAnyFolding(curwin))
{
// Count a fold for one screen line.
2004-06-20 12:51:53 +00:00
for (n = cap->count1 - 1; n > 0
&& curwin->w_cursor.lnum > curwin->w_topline; --n)
{
(void)hasFolding(curwin->w_cursor.lnum,
&curwin->w_cursor.lnum, NULL);
if (curwin->w_cursor.lnum > curwin->w_topline)
--curwin->w_cursor.lnum;
2004-06-20 12:51:53 +00:00
}
}
else
#endif
curwin->w_cursor.lnum -= cap->count1 - 1;
}
2004-06-13 20:20:40 +00:00
}
else
{
if (cap->cmdchar == 'M')
{
#ifdef FEAT_DIFF
// Don't count filler lines above the window.
2004-06-13 20:20:40 +00:00
used -= diff_check_fill(curwin, curwin->w_topline)
- curwin->w_topfill;
#endif
validate_botline(); // make sure w_empty_rows is valid
2004-06-13 20:20:40 +00:00
half = (curwin->w_height - curwin->w_empty_rows + 1) / 2;
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
{
#ifdef FEAT_DIFF
// Count half he number of filler lines to be "below this
// line" and half to be "above the next line".
2004-06-13 20:20:40 +00:00
if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline
+ n) / 2 >= half)
{
--n;
break;
}
#endif
used += plines(curwin->w_topline + n);
if (used >= half)
break;
#ifdef FEAT_FOLDING
if (hasFolding(curwin->w_topline + n, NULL, &lnum))
n = lnum - curwin->w_topline;
#endif
}
if (n > 0 && used > curwin->w_height)
--n;
}
else // (cap->cmdchar == 'H')
2004-06-20 12:51:53 +00:00
{
2004-06-13 20:20:40 +00:00
n = cap->count1 - 1;
2004-06-20 12:51:53 +00:00
#ifdef FEAT_FOLDING
if (hasAnyFolding(curwin))
{
// Count a fold for one screen line.
2004-06-20 12:51:53 +00:00
lnum = curwin->w_topline;
while (n-- > 0 && lnum < curwin->w_botline - 1)
{
(void)hasFolding(lnum, NULL, &lnum);
2004-06-20 12:51:53 +00:00
++lnum;
}
n = lnum - curwin->w_topline;
}
#endif
}
2004-06-13 20:20:40 +00:00
curwin->w_cursor.lnum = curwin->w_topline + n;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
// Correct for 'so', except when an operator is pending.
if (cap->oap->op_type == OP_NOP)
cursor_correct();
2004-06-13 20:20:40 +00:00
beginline(BL_SOL | BL_FIX);
}
/*
* Cursor right commands.
*/
static void
nv_right(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
long n;
int past_line;
2004-06-13 20:20:40 +00:00
2005-03-06 23:38:09 +00:00
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
{
// <C-Right> and <S-Right> move a word or WORD right
2005-03-06 23:38:09 +00:00
if (mod_mask & MOD_MASK_CTRL)
cap->arg = TRUE;
nv_wordcmd(cap);
return;
}
2004-06-13 20:20:40 +00:00
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
past_line = (VIsual_active && *p_sel != 'o');
2004-06-13 20:20:40 +00:00
// In virtual edit mode, there's no such thing as "past_line", as lines
// are (theoretically) infinitely long.
2004-06-13 20:20:40 +00:00
if (virtual_active())
past_line = 0;
2004-06-13 20:20:40 +00:00
for (n = cap->count1; n > 0; --n)
{
if ((!past_line && oneright() == FAIL)
|| (past_line && *ml_get_cursor() == NUL)
2009-05-15 19:33:18 +00:00
)
2004-06-13 20:20:40 +00:00
{
// <Space> wraps to next line if 'whichwrap' has 's'.
// 'l' wraps to next line if 'whichwrap' has 'l'.
// CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
2004-06-13 20:20:40 +00:00
if ( ((cap->cmdchar == ' '
&& vim_strchr(p_ww, 's') != NULL)
|| (cap->cmdchar == 'l'
&& vim_strchr(p_ww, 'l') != NULL)
2005-03-25 21:45:43 +00:00
|| (cap->cmdchar == K_RIGHT
2004-06-13 20:20:40 +00:00
&& vim_strchr(p_ww, '>') != NULL))
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
{
// When deleting we also count the NL as a character.
// Set cap->oap->inclusive when last char in the line is
// included, move to next line after that
2006-03-06 23:29:24 +00:00
if ( cap->oap->op_type != OP_NOP
2004-06-13 20:20:40 +00:00
&& !cap->oap->inclusive
&& !LINEEMPTY(curwin->w_cursor.lnum))
2004-06-13 20:20:40 +00:00
cap->oap->inclusive = TRUE;
else
{
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
curwin->w_set_curswant = TRUE;
cap->oap->inclusive = FALSE;
}
continue;
}
if (cap->oap->op_type == OP_NOP)
{
// Only beep and flush if not moved at all
2004-06-13 20:20:40 +00:00
if (n == cap->count1)
beep_flush();
}
else
{
if (!LINEEMPTY(curwin->w_cursor.lnum))
2004-06-13 20:20:40 +00:00
cap->oap->inclusive = TRUE;
}
break;
}
else if (past_line)
2004-06-13 20:20:40 +00:00
{
curwin->w_set_curswant = TRUE;
if (virtual_active())
oneright();
else
{
if (has_mbyte)
curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor());
2004-06-13 20:20:40 +00:00
else
++curwin->w_cursor.col;
}
}
}
#ifdef FEAT_FOLDING
if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
&& cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
/*
* Cursor left commands.
*
* Returns TRUE when operator end should not be adjusted.
*/
static void
nv_left(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
long n;
2005-03-06 23:38:09 +00:00
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
{
// <C-Left> and <S-Left> move a word or WORD left
2005-03-06 23:38:09 +00:00
if (mod_mask & MOD_MASK_CTRL)
cap->arg = 1;
nv_bck_word(cap);
return;
}
2004-06-13 20:20:40 +00:00
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
for (n = cap->count1; n > 0; --n)
{
if (oneleft() == FAIL)
{
// <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
// 'h' wraps to previous line if 'whichwrap' has 'h'.
// CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
2004-06-13 20:20:40 +00:00
if ( (((cap->cmdchar == K_BS
|| cap->cmdchar == Ctrl_H)
&& vim_strchr(p_ww, 'b') != NULL)
|| (cap->cmdchar == 'h'
&& vim_strchr(p_ww, 'h') != NULL)
2005-03-25 21:45:43 +00:00
|| (cap->cmdchar == K_LEFT
2004-06-13 20:20:40 +00:00
&& vim_strchr(p_ww, '<') != NULL))
&& curwin->w_cursor.lnum > 1)
{
--(curwin->w_cursor.lnum);
coladvance((colnr_T)MAXCOL);
curwin->w_set_curswant = TRUE;
// When the NL before the first char has to be deleted we
// put the cursor on the NUL after the previous line.
// This is a very special case, be careful!
// Don't adjust op_end now, otherwise it won't work.
2004-06-13 20:20:40 +00:00
if ( (cap->oap->op_type == OP_DELETE
|| cap->oap->op_type == OP_CHANGE)
&& !LINEEMPTY(curwin->w_cursor.lnum))
2004-06-13 20:20:40 +00:00
{
char_u *cp = ml_get_cursor();
if (*cp != NUL)
{
if (has_mbyte)
curwin->w_cursor.col += (*mb_ptr2len)(cp);
else
++curwin->w_cursor.col;
}
2004-06-13 20:20:40 +00:00
cap->retval |= CA_NO_ADJ_OP_END;
}
continue;
}
// Only beep and flush if not moved at all
2004-06-13 20:20:40 +00:00
else if (cap->oap->op_type == OP_NOP && n == cap->count1)
beep_flush();
break;
}
}
#ifdef FEAT_FOLDING
if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
&& cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
/*
* Cursor up commands.
* cap->arg is TRUE for "-": Move cursor to first non-blank.
*/
static void
nv_up(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
2005-03-06 23:38:09 +00:00
if (mod_mask & MOD_MASK_SHIFT)
{
// <S-Up> is page up
2005-03-06 23:38:09 +00:00
cap->arg = BACKWARD;
nv_page(cap);
return;
2005-03-06 23:38:09 +00:00
}
cap->oap->motion_type = MLINE;
if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
clearopbeep(cap->oap);
else if (cap->arg)
beginline(BL_WHITE | BL_FIX);
2004-06-13 20:20:40 +00:00
}
/*
* Cursor down commands.
* cap->arg is TRUE for CR and "+": Move cursor to first non-blank.
*/
static void
nv_down(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
2005-03-06 23:38:09 +00:00
if (mod_mask & MOD_MASK_SHIFT)
{
// <S-Down> is page down
2005-03-06 23:38:09 +00:00
cap->arg = FORWARD;
nv_page(cap);
}
#if defined(FEAT_QUICKFIX)
// Quickfix window only: view the result under the cursor.
else if (bt_quickfix(curbuf) && cap->cmdchar == CAR)
qf_view_result(FALSE);
2004-06-13 20:20:40 +00:00
#endif
else
2004-06-13 20:20:40 +00:00
{
// In the cmdline window a <CR> executes the command.
2005-02-26 23:04:13 +00:00
if (cmdwin_type != 0 && cap->cmdchar == CAR)
2004-06-13 20:20:40 +00:00
cmdwin_result = CAR;
else
#ifdef FEAT_JOB_CHANNEL
// In a prompt buffer a <CR> in the last line invokes the callback.
if (bt_prompt(curbuf) && cap->cmdchar == CAR
&& curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
{
invoke_prompt_callback();
if (restart_edit == 0)
restart_edit = 'a';
}
else
2004-06-13 20:20:40 +00:00
#endif
{
cap->oap->motion_type = MLINE;
if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
clearopbeep(cap->oap);
else if (cap->arg)
beginline(BL_WHITE | BL_FIX);
}
}
}
/*
* Grab the file name under the cursor and edit it.
*/
static void
nv_gotofile(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
char_u *ptr;
2006-02-22 21:25:37 +00:00
linenr_T lnum = -1;
2004-06-13 20:20:40 +00:00
if (check_text_or_curbuf_locked(cap->oap))
2004-06-13 20:20:40 +00:00
return;
#ifdef FEAT_PROP_POPUP
if (ERROR_IF_TERM_POPUP_WINDOW)
return;
#endif
2004-06-13 20:20:40 +00:00
if (!check_can_set_curbuf_disabled())
return;
2006-02-22 21:25:37 +00:00
ptr = grab_file_name(cap->count1, &lnum);
2004-06-13 20:20:40 +00:00
if (ptr != NULL)
{
// do autowrite if necessary
if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf))
(void)autowrite(curbuf, FALSE);
2004-06-13 20:20:40 +00:00
setpcmark();
if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
&& cap->nchar == 'F' && lnum >= 0)
2006-02-22 21:25:37 +00:00
{
curwin->w_cursor.lnum = lnum;
check_cursor_lnum();
beginline(BL_SOL | BL_FIX);
}
2004-06-13 20:20:40 +00:00
vim_free(ptr);
}
else
clearop(cap->oap);
}
/*
* <End> command: to end of current line or last line.
*/
static void
nv_end(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->arg || (mod_mask & MOD_MASK_CTRL)) // CTRL-END = goto last line
2004-06-13 20:20:40 +00:00
{
2005-03-06 23:38:09 +00:00
cap->arg = TRUE;
2004-06-13 20:20:40 +00:00
nv_goto(cap);
cap->count1 = 1; // to end of current line
2004-06-13 20:20:40 +00:00
}
nv_dollar(cap);
}
/*
* Handle the "$" command.
*/
static void
nv_dollar(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = TRUE;
// In virtual mode when off the edge of a line and an operator
// is pending (whew!) keep the cursor where it is.
// Otherwise, send it to the end of the line.
2004-06-13 20:20:40 +00:00
if (!virtual_active() || gchar_cursor() != NUL
|| cap->oap->op_type == OP_NOP)
curwin->w_curswant = MAXCOL; // so we stay at the end
2004-06-13 20:20:40 +00:00
if (cursor_down((long)(cap->count1 - 1),
cap->oap->op_type == OP_NOP) == FAIL)
clearopbeep(cap->oap);
#ifdef FEAT_FOLDING
else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
/*
* Implementation of '?' and '/' commands.
* If cap->arg is TRUE don't set PC mark.
*/
static void
nv_search(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
oparg_T *oap = cap->oap;
pos_T save_cursor = curwin->w_cursor;
2004-06-13 20:20:40 +00:00
if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13)
{
// Translate "g??" to "g?g?"
2004-06-13 20:20:40 +00:00
cap->cmdchar = 'g';
cap->nchar = '?';
nv_operator(cap);
return;
}
// When using 'incsearch' the cursor may be moved to set a different search
// start position.
cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0, 0);
2004-06-13 20:20:40 +00:00
if (cap->searchbuf == NULL)
{
clearop(oap);
return;
}
(void)normal_search(cap, cap->cmdchar, cap->searchbuf, STRLEN(cap->searchbuf),
(cap->arg || !EQUAL_POS(save_cursor, curwin->w_cursor))
? 0 : SEARCH_MARK, NULL);
2004-06-13 20:20:40 +00:00
}
2004-06-13 20:20:40 +00:00
/*
* Handle "N" and "n" commands.
* cap->arg is SEARCH_REV for "N", 0 for "n".
*/
static void
nv_next(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
pos_T old = curwin->w_cursor;
int wrapped = FALSE;
int i = normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, &wrapped);
if (i == 1 && !wrapped && EQUAL_POS(old, curwin->w_cursor))
{
// Avoid getting stuck on the current cursor position, which can
// happen when an offset is given and the cursor is on the last char
// in the buffer: Repeat with count + 1.
cap->count1 += 1;
(void)normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, NULL);
cap->count1 -= 1;
}
#ifdef FEAT_SEARCH_EXTRA
// Redraw the window to refresh the highlighted matches.
if (i > 0 && p_hls && !no_hlsearch)
redraw_later(UPD_SOME_VALID);
#endif
2004-06-13 20:20:40 +00:00
}
/*
* Search for "pat" in direction "dir" ('/' or '?', 0 for repeat).
* Uses only cap->count1 and cap->oap from "cap".
* Return 0 for failure, 1 for found, 2 for found and line offset added.
2004-06-13 20:20:40 +00:00
*/
static int
normal_search(
cmdarg_T *cap,
int dir,
char_u *pat,
size_t patlen,
int opt, // extra flags for do_search()
int *wrapped)
2004-06-13 20:20:40 +00:00
{
int i;
searchit_arg_T sia;
#ifdef FEAT_SEARCH_EXTRA
pos_T prev_cursor = curwin->w_cursor;
#endif
2004-06-13 20:20:40 +00:00
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
cap->oap->use_reg_one = TRUE;
curwin->w_set_curswant = TRUE;
CLEAR_FIELD(sia);
i = do_search(cap->oap, dir, dir, pat, patlen, cap->count1,
opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
if (wrapped != NULL)
*wrapped = sia.sa_wrapped;
2004-06-13 20:20:40 +00:00
if (i == 0)
clearop(cap->oap);
else
{
if (i == 2)
cap->oap->motion_type = MLINE;
curwin->w_cursor.coladd = 0;
#ifdef FEAT_FOLDING
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
foldOpenCursor();
#endif
}
#ifdef FEAT_SEARCH_EXTRA
// Redraw the window to refresh the highlighted matches.
if (!EQUAL_POS(curwin->w_cursor, prev_cursor) && p_hls && !no_hlsearch)
redraw_later(UPD_SOME_VALID);
#endif
2004-06-13 20:20:40 +00:00
// "/$" will put the cursor after the end of the line, may need to
// correct that here
2004-06-13 20:20:40 +00:00
check_cursor();
return i;
2004-06-13 20:20:40 +00:00
}
/*
* Character search commands.
* cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', TRUE for
* ',' and FALSE for ';'.
* cap->nchar is NUL for ',' and ';' (repeat the search)
*/
static void
nv_csearch(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int t_cmd;
if (cap->cmdchar == 't' || cap->cmdchar == 'T')
t_cmd = TRUE;
else
t_cmd = FALSE;
cap->oap->motion_type = MCHAR;
if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == FAIL)
{
2004-06-13 20:20:40 +00:00
clearopbeep(cap->oap);
return;
}
curwin->w_set_curswant = TRUE;
// Include a Tab for "tx" and for "dfx".
if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
&& (t_cmd || cap->oap->op_type != OP_NOP))
2004-06-13 20:20:40 +00:00
{
colnr_T scol, ecol;
2004-06-13 20:20:40 +00:00
getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
curwin->w_cursor.coladd = ecol - scol;
}
else
curwin->w_cursor.coladd = 0;
adjust_for_sel(cap);
2004-06-13 20:20:40 +00:00
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
2004-06-13 20:20:40 +00:00
#endif
}
/*
* "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
* "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
* "[/", "[*", "]/", "]*": go to Nth comment start/end.
* "[m" or "]m" search for prev/next start of (Java) method.
* "[M" or "]M" search for prev/next end of (Java) method.
*/
static void
nv_bracket_block(cmdarg_T *cap, pos_T *old_pos)
{
pos_T new_pos = {0, 0, 0};
pos_T *pos = NULL; // init for GCC
pos_T prev_pos;
long n;
int findc;
int c;
if (cap->nchar == '*')
cap->nchar = '/';
prev_pos.lnum = 0;
if (cap->nchar == 'm' || cap->nchar == 'M')
{
if (cap->cmdchar == '[')
findc = '{';
else
findc = '}';
n = 9999;
}
else
{
findc = cap->nchar;
n = cap->count1;
}
for ( ; n > 0; --n)
{
if ((pos = findmatchlimit(cap->oap, findc,
(cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
{
if (new_pos.lnum == 0) // nothing found
{
if (cap->nchar != 'm' && cap->nchar != 'M')
clearopbeep(cap->oap);
}
else
pos = &new_pos; // use last one found
break;
}
prev_pos = new_pos;
curwin->w_cursor = *pos;
new_pos = *pos;
}
curwin->w_cursor = *old_pos;
// Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
// brought us to the match for "[m" and "]M" when inside a method.
// Try finding the '{' or '}' we want to be at.
// Also repeat for the given count.
if (cap->nchar == 'm' || cap->nchar == 'M')
{
// norm is TRUE for "]M" and "[m"
int norm = ((findc == '{') == (cap->nchar == 'm'));
n = cap->count1;
// found a match: we were inside a method
if (prev_pos.lnum != 0)
{
pos = &prev_pos;
curwin->w_cursor = prev_pos;
if (norm)
--n;
}
else
pos = NULL;
while (n > 0)
{
for (;;)
{
if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0)
{
// if not found anything, that's an error
if (pos == NULL)
clearopbeep(cap->oap);
n = 0;
break;
}
c = gchar_cursor();
if (c == '{' || c == '}')
{
// Must have found end/start of class: use it.
// Or found the place to be at.
if ((c == findc && norm) || (n == 1 && !norm))
{
new_pos = curwin->w_cursor;
pos = &new_pos;
n = 0;
}
// if no match found at all, we started outside of the
// class and we're inside now. Just go on.
else if (new_pos.lnum == 0)
{
new_pos = curwin->w_cursor;
pos = &new_pos;
}
// found start/end of other method: go to match
else if ((pos = findmatchlimit(cap->oap, findc,
(cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
0)) == NULL)
n = 0;
else
curwin->w_cursor = *pos;
break;
}
}
--n;
}
curwin->w_cursor = *old_pos;
if (pos == NULL && new_pos.lnum != 0)
clearopbeep(cap->oap);
}
if (pos != NULL)
{
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = TRUE;
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_BLOCK) && KeyTyped
&& cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
}
2004-06-13 20:20:40 +00:00
/*
* "[" and "]" commands.
* cap->arg is BACKWARD for "[" and FORWARD for "]".
*/
static void
nv_brackets(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
pos_T prev_pos;
pos_T *pos = NULL; // init for GCC
pos_T old_pos; // cursor position before command
2004-06-13 20:20:40 +00:00
int flag;
long n;
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
old_pos = curwin->w_cursor;
curwin->w_cursor.coladd = 0; // TODO: don't do this for an error.
2004-06-13 20:20:40 +00:00
// "[f" or "]f" : Edit file under the cursor (same as "gf")
2004-06-13 20:20:40 +00:00
if (cap->nchar == 'f')
nv_gotofile(cap);
else
#ifdef FEAT_FIND_ID
// Find the occurrence(s) of the identifier or define under cursor
// in current and included files or jump to the first occurrence.
//
// search list jump
// fwd bwd fwd bwd fwd bwd
// identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
// define "]d" "[d" "]D" "[D" "]^D" "[^D"
if (vim_strchr((char_u *)"iI\011dD\004", cap->nchar) != NULL)
2004-06-13 20:20:40 +00:00
{
char_u *ptr;
int len;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
clearop(cap->oap);
else
{
// Make a copy, if the line was changed it will be freed.
ptr = vim_strnsave(ptr, len);
if (ptr == NULL)
return;
2004-06-13 20:20:40 +00:00
find_pattern_in_path(ptr, 0, len, TRUE,
cap->count0 == 0 ? !SAFE_isupper(cap->nchar) : FALSE,
2004-06-13 20:20:40 +00:00
((cap->nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
cap->count1,
SAFE_isupper(cap->nchar) ? ACTION_SHOW_ALL :
SAFE_islower(cap->nchar) ? ACTION_SHOW : ACTION_GOTO,
2004-06-13 20:20:40 +00:00
cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 : (linenr_T)1,
(linenr_T)MAXLNUM,
FALSE);
vim_free(ptr);
2004-06-13 20:20:40 +00:00
curwin->w_set_curswant = TRUE;
}
}
else
#endif
// "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
// "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
// "[/", "[*", "]/", "]*": go to Nth comment start/end.
// "[m" or "]m" search for prev/next start of (Java) method.
// "[M" or "]M" search for prev/next end of (Java) method.
2004-06-13 20:20:40 +00:00
if ( (cap->cmdchar == '['
&& vim_strchr((char_u *)"{(*/#mM", cap->nchar) != NULL)
|| (cap->cmdchar == ']'
&& vim_strchr((char_u *)"})*/#mM", cap->nchar) != NULL))
nv_bracket_block(cap, &old_pos);
2004-06-13 20:20:40 +00:00
// "[[", "[]", "]]" and "][": move to start or end of function
2004-06-13 20:20:40 +00:00
else if (cap->nchar == '[' || cap->nchar == ']')
{
if (cap->nchar == cap->cmdchar) // "]]" or "[["
2004-06-13 20:20:40 +00:00
flag = '{';
else
flag = '}'; // "][" or "[]"
2004-06-13 20:20:40 +00:00
curwin->w_set_curswant = TRUE;
// Imitate strange Vi behaviour: When using "]]" with an operator
// we also stop at '}'.
2005-09-05 22:05:30 +00:00
if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag,
2004-06-13 20:20:40 +00:00
(cap->oap->op_type != OP_NOP
&& cap->arg == FORWARD && flag == '{')))
clearopbeep(cap->oap);
else
{
if (cap->oap->op_type == OP_NOP)
beginline(BL_WHITE | BL_FIX);
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
}
// "[p", "[P", "]P" and "]p": put with indent adjustment
2004-06-13 20:20:40 +00:00
else if (cap->nchar == 'p' || cap->nchar == 'P')
{
nv_put_opt(cap, TRUE);
2004-06-13 20:20:40 +00:00
}
// "['", "[`", "]'" and "]`": jump to next mark
2004-06-13 20:20:40 +00:00
else if (cap->nchar == '\'' || cap->nchar == '`')
{
pos = &curwin->w_cursor;
for (n = cap->count1; n > 0; --n)
{
prev_pos = *pos;
pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
cap->nchar == '\'');
if (pos == NULL)
break;
}
if (pos == NULL)
pos = &prev_pos;
nv_cursormark(cap, cap->nchar == '\'', pos);
}
// [ or ] followed by a middle mouse click: put selected text with
// indent adjustment. Any other button just does as usual.
else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE)
2004-06-13 20:20:40 +00:00
{
(void)do_mouse(cap->oap, cap->nchar,
(cap->cmdchar == ']') ? FORWARD : BACKWARD,
cap->count1, PUT_FIXINDENT);
}
#ifdef FEAT_FOLDING
// "[z" and "]z": move to start or end of open fold.
2004-06-13 20:20:40 +00:00
else if (cap->nchar == 'z')
{
if (foldMoveTo(FALSE, cap->cmdchar == ']' ? FORWARD : BACKWARD,
cap->count1) == FAIL)
clearopbeep(cap->oap);
}
#endif
#ifdef FEAT_DIFF
// "[c" and "]c": move to next or previous diff-change.
2004-06-13 20:20:40 +00:00
else if (cap->nchar == 'c')
{
if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
cap->count1) == FAIL)
clearopbeep(cap->oap);
}
#endif
2006-03-12 21:50:18 +00:00
#ifdef FEAT_SPELL
// "[r", "[s", "[S", "]r", "]s" and "]S": move to next spell error.
else if (cap->nchar == 'r' || cap->nchar == 's' || cap->nchar == 'S')
2005-04-15 21:00:38 +00:00
{
2005-04-20 19:37:22 +00:00
setpcmark();
for (n = 0; n < cap->count1; ++n)
2005-08-25 21:21:38 +00:00
if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
cap->nchar == 's' ? SMT_ALL :
cap->nchar == 'r' ? SMT_RARE :
SMT_BAD, FALSE, NULL) == 0)
2005-04-20 19:37:22 +00:00
{
clearopbeep(cap->oap);
break;
}
else
curwin->w_set_curswant = TRUE;
2006-04-05 20:41:53 +00:00
# ifdef FEAT_FOLDING
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
foldOpenCursor();
# endif
2005-04-15 21:00:38 +00:00
}
#endif
// Not a valid cap->nchar.
2004-06-13 20:20:40 +00:00
else
clearopbeep(cap->oap);
}
/*
* Handle Normal mode "%" command.
*/
static void
nv_percent(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
pos_T *pos;
#if defined(FEAT_FOLDING)
2004-06-13 20:20:40 +00:00
linenr_T lnum = curwin->w_cursor.lnum;
#endif
cap->oap->inclusive = TRUE;
if (cap->count0) // {cnt}% : goto {cnt} percentage in file
2004-06-13 20:20:40 +00:00
{
if (cap->count0 > 100)
clearopbeep(cap->oap);
else
{
cap->oap->motion_type = MLINE;
setpcmark();
// Round up, so 'normal 100%' always jumps at the line line.
// Beyond 21474836 lines, (ml_line_count * 100 + 99) would
// overflow on 32-bits, so use a formula with less accuracy
// to avoid overflows.
if (curbuf->b_ml.ml_line_count >= 21474836)
2004-06-13 20:20:40 +00:00
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99L)
/ 100L * cap->count0;
else
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
cap->count0 + 99L) / 100L;
if (curwin->w_cursor.lnum < 1)
curwin->w_cursor.lnum = 1;
2004-06-13 20:20:40 +00:00
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
beginline(BL_SOL | BL_FIX);
}
}
else // "%" : go to matching paren
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MCHAR;
cap->oap->use_reg_one = TRUE;
if ((pos = findmatch(cap->oap, NUL)) == NULL)
clearopbeep(cap->oap);
else
{
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = TRUE;
curwin->w_cursor.coladd = 0;
adjust_for_sel(cap);
}
}
#ifdef FEAT_FOLDING
if (cap->oap->op_type == OP_NOP
&& lnum != curwin->w_cursor.lnum
&& (fdo_flags & FDO_PERCENT)
&& KeyTyped)
foldOpenCursor();
#endif
}
/*
* Handle "(" and ")" commands.
* cap->arg is BACKWARD for "(" and FORWARD for ")".
*/
static void
nv_brace(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MCHAR;
cap->oap->use_reg_one = TRUE;
// The motion used to be inclusive for "(", but that is not what Vi does.
2005-12-28 22:39:57 +00:00
cap->oap->inclusive = FALSE;
2004-06-13 20:20:40 +00:00
curwin->w_set_curswant = TRUE;
if (findsent(cap->arg, cap->count1) == FAIL)
{
clearopbeep(cap->oap);
return;
}
// Don't leave the cursor on the NUL past end of line.
adjust_cursor(cap->oap);
curwin->w_cursor.coladd = 0;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
2004-06-13 20:20:40 +00:00
#endif
}
/*
* "m" command: Mark a position.
*/
static void
nv_mark(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearop(cap->oap))
return;
if (setmark(cap->nchar) == FAIL)
clearopbeep(cap->oap);
2004-06-13 20:20:40 +00:00
}
/*
* "{" and "}" commands.
* cmd->arg is BACKWARD for "{" and FORWARD for "}".
*/
static void
nv_findpar(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
cap->oap->use_reg_one = TRUE;
curwin->w_set_curswant = TRUE;
2005-09-05 22:05:30 +00:00
if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, FALSE))
2004-06-13 20:20:40 +00:00
{
clearopbeep(cap->oap);
return;
}
curwin->w_cursor.coladd = 0;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
2004-06-13 20:20:40 +00:00
#endif
}
/*
* "u" command: Undo or make lower case.
*/
static void
nv_undo(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->oap->op_type == OP_LOWER || VIsual_active)
2004-06-13 20:20:40 +00:00
{
// translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu"
2004-06-13 20:20:40 +00:00
cap->cmdchar = 'g';
cap->nchar = 'u';
nv_operator(cap);
}
else
nv_kundo(cap);
}
/*
* <Undo> command.
*/
static void
nv_kundo(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearopq(cap->oap))
return;
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf))
{
clearopbeep(cap->oap);
return;
2004-06-13 20:20:40 +00:00
}
#endif
u_undo((int)cap->count1);
curwin->w_set_curswant = TRUE;
2004-06-13 20:20:40 +00:00
}
/*
* Handle the "r" command.
*/
static void
nv_replace(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int had_ctrl_v;
long n;
if (checkclearop(cap->oap))
return;
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf) && !prompt_curpos_editable())
{
clearopbeep(cap->oap);
return;
}
#endif
2004-06-13 20:20:40 +00:00
// get another character
if (cap->nchar == Ctrl_V || cap->nchar == Ctrl_Q)
2004-06-13 20:20:40 +00:00
{
had_ctrl_v = Ctrl_V;
cap->nchar = get_literal(FALSE);
// Don't redo a multibyte character with CTRL-V.
2004-06-13 20:20:40 +00:00
if (cap->nchar > DEL)
had_ctrl_v = NUL;
}
else
had_ctrl_v = NUL;
// Abort if the character is a special key.
2007-08-08 19:42:05 +00:00
if (IS_SPECIAL(cap->nchar))
{
clearopbeep(cap->oap);
return;
}
// Visual mode "r"
2004-06-13 20:20:40 +00:00
if (VIsual_active)
{
2009-02-04 10:46:25 +00:00
if (got_int)
got_int = FALSE;
if (had_ctrl_v)
{
// Use a special (negative) number to make a difference between a
// literal CR or NL and a line break.
if (cap->nchar == CAR)
cap->nchar = REPLACE_CR_NCHAR;
else if (cap->nchar == NL)
cap->nchar = REPLACE_NL_NCHAR;
}
2004-06-13 20:20:40 +00:00
nv_operator(cap);
return;
}
// Break tabs, etc.
2004-06-13 20:20:40 +00:00
if (virtual_active())
{
if (u_save_cursor() == FAIL)
return;
if (gchar_cursor() == NUL)
{
// Add extra space and put the cursor on the first one.
2004-06-13 20:20:40 +00:00
coladvance_force((colnr_T)(getviscol() + cap->count1));
curwin->w_cursor.col -= cap->count1;
}
else if (gchar_cursor() == TAB)
coladvance_force(getviscol());
}
// Abort if not enough characters to replace.
if ((size_t)ml_get_cursor_len() < (unsigned)cap->count1
|| (has_mbyte && mb_charlen(ml_get_cursor()) < cap->count1))
2004-06-13 20:20:40 +00:00
{
clearopbeep(cap->oap);
return;
}
// Replacing with a TAB is done by edit() when it is complicated because
// 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB.
// Other characters are done below to avoid problems with things like
// CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
2004-06-13 20:20:40 +00:00
if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
{
stuffnumReadbuff(cap->count1);
stuffcharReadbuff('R');
stuffcharReadbuff('\t');
stuffcharReadbuff(ESC);
return;
}
// save line for undo
2004-06-13 20:20:40 +00:00
if (u_save_cursor() == FAIL)
return;
if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n'))
{
// Replace character(s) by a single newline.
// Strange vi behaviour: Only one newline is inserted.
// Delete the characters here.
// Insert the newline with an insert command, takes care of
// autoindent. The insert command depends on being on the last
// character of a line or not.
(void)del_chars(cap->count1, FALSE); // delete the characters
2004-06-13 20:20:40 +00:00
stuffcharReadbuff('\r');
stuffcharReadbuff(ESC);
// Give 'r' to edit(), to get the redo command right.
2004-06-13 20:20:40 +00:00
invoke_edit(cap, TRUE, 'r', FALSE);
}
else
{
prep_redo(cap->oap->regname, cap->count1,
NUL, 'r', NUL, had_ctrl_v, cap->nchar);
curbuf->b_op_start = curwin->w_cursor;
if (has_mbyte)
{
int old_State = State;
if (cap->ncharC1 != 0)
AppendCharToRedobuff(cap->ncharC1);
if (cap->ncharC2 != 0)
AppendCharToRedobuff(cap->ncharC2);
// This is slow, but it handles replacing a single-byte with a
// multi-byte and the other way around. Also handles adding
// composing characters for utf-8.
2004-06-13 20:20:40 +00:00
for (n = cap->count1; n > 0; --n)
{
State = MODE_REPLACE;
if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
{
int c = ins_copychar(curwin->w_cursor.lnum
+ (cap->nchar == Ctrl_Y ? -1 : 1));
if (c != NUL)
ins_char(c);
else
// will be decremented further down
++curwin->w_cursor.col;
}
else
ins_char(cap->nchar);
2004-06-13 20:20:40 +00:00
State = old_State;
if (cap->ncharC1 != 0)
ins_char(cap->ncharC1);
if (cap->ncharC2 != 0)
ins_char(cap->ncharC2);
}
}
else
{
char_u *ptr;
// Replace the characters within one line.
2004-06-13 20:20:40 +00:00
for (n = cap->count1; n > 0; --n)
{
// Get ptr again, because ins_copychar() and showmatch()
// will have released the line.
// At the same time we let know that the line will be changed.
if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
{
int c = ins_copychar(curwin->w_cursor.lnum
+ (cap->nchar == Ctrl_Y ? -1 : 1));
ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
if (c != NUL)
ptr[curwin->w_cursor.col] = c;
}
else
{
ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
ptr[curwin->w_cursor.col] = cap->nchar;
}
2004-06-13 20:20:40 +00:00
if (p_sm && msg_silent == 0)
showmatch(cap->nchar);
++curwin->w_cursor.col;
}
#ifdef FEAT_NETBEANS_INTG
2010-05-22 21:34:09 +02:00
if (netbeans_active())
2004-06-13 20:20:40 +00:00
{
2010-05-22 21:34:09 +02:00
colnr_T start = (colnr_T)(curwin->w_cursor.col - cap->count1);
ptr = ml_get_curline();
2004-06-13 20:20:40 +00:00
2004-10-24 19:18:58 +00:00
netbeans_removed(curbuf, curwin->w_cursor.lnum, start,
cap->count1);
2004-06-13 20:20:40 +00:00
netbeans_inserted(curbuf, curwin->w_cursor.lnum, start,
2004-10-24 19:18:58 +00:00
&ptr[start], (int)cap->count1);
2004-06-13 20:20:40 +00:00
}
#endif
// mark the buffer as changed and prepare for displaying
2004-06-13 20:20:40 +00:00
changed_bytes(curwin->w_cursor.lnum,
(colnr_T)(curwin->w_cursor.col - cap->count1));
}
--curwin->w_cursor.col; // cursor on the last replaced char
// if the character on the left of the current cursor is a multi-byte
// character, move two characters left
2004-06-13 20:20:40 +00:00
if (has_mbyte)
mb_adjust_cursor();
curbuf->b_op_end = curwin->w_cursor;
curwin->w_set_curswant = TRUE;
set_last_insert(cap->nchar);
}
}
/*
* 'o': Exchange start and end of Visual area.
* 'O': same, but in block mode exchange left and right corners.
*/
static void
v_swap_corners(int cmdchar)
2004-06-13 20:20:40 +00:00
{
pos_T old_cursor;
colnr_T left, right;
if (cmdchar == 'O' && VIsual_mode == Ctrl_V)
{
old_cursor = curwin->w_cursor;
getvcols(curwin, &old_cursor, &VIsual, &left, &right);
curwin->w_cursor.lnum = VIsual.lnum;
coladvance(left);
VIsual = curwin->w_cursor;
curwin->w_cursor.lnum = old_cursor.lnum;
curwin->w_curswant = right;
// 'selection "exclusive" and cursor at right-bottom corner: move it
// right one column
2004-06-13 20:20:40 +00:00
if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e')
++curwin->w_curswant;
coladvance(curwin->w_curswant);
if (curwin->w_cursor.col == old_cursor.col
&& (!virtual_active()
|| curwin->w_cursor.coladd == old_cursor.coladd))
2004-06-13 20:20:40 +00:00
{
curwin->w_cursor.lnum = VIsual.lnum;
if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e')
++right;
coladvance(right);
VIsual = curwin->w_cursor;
curwin->w_cursor.lnum = old_cursor.lnum;
coladvance(left);
curwin->w_curswant = left;
}
}
else
{
old_cursor = curwin->w_cursor;
curwin->w_cursor = VIsual;
VIsual = old_cursor;
curwin->w_set_curswant = TRUE;
}
}
/*
* "R" (cap->arg is FALSE) and "gR" (cap->arg is TRUE).
*/
static void
nv_Replace(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active) // "R" is replace lines
2004-06-13 20:20:40 +00:00
{
cap->cmdchar = 'c';
cap->nchar = NUL;
VIsual_mode_orig = VIsual_mode; // remember original area for gv
2004-06-13 20:20:40 +00:00
VIsual_mode = 'V';
nv_operator(cap);
return;
2004-06-13 20:20:40 +00:00
}
if (checkclearopq(cap->oap))
return;
if (!curbuf->b_p_ma)
emsg(_(e_cannot_make_changes_modifiable_is_off));
else
2004-06-13 20:20:40 +00:00
{
if (virtual_active())
coladvance(getviscol());
invoke_edit(cap, FALSE, cap->arg ? 'V' : 'R', FALSE);
2004-06-13 20:20:40 +00:00
}
}
/*
* "gr".
*/
static void
nv_vreplace(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active)
{
cap->cmdchar = 'r';
cap->nchar = cap->extra_char;
nv_replace(cap); // Do same as "r" in Visual mode for now
return;
2004-06-13 20:20:40 +00:00
}
if (checkclearopq(cap->oap))
return;
if (!curbuf->b_p_ma)
emsg(_(e_cannot_make_changes_modifiable_is_off));
else
2004-06-13 20:20:40 +00:00
{
if (cap->extra_char == Ctrl_V || cap->extra_char == Ctrl_Q)
// get another character
cap->extra_char = get_literal(FALSE);
if (cap->extra_char < ' ')
// Prefix a control character with CTRL-V to avoid it being used as
// a command.
stuffcharReadbuff(Ctrl_V);
stuffcharReadbuff(cap->extra_char);
stuffcharReadbuff(ESC);
if (virtual_active())
coladvance(getviscol());
invoke_edit(cap, TRUE, 'v', FALSE);
2004-06-13 20:20:40 +00:00
}
}
/*
* Swap case for "~" command, when it does not work like an operator.
*/
static void
n_swapchar(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
long n;
pos_T startpos;
int did_change = 0;
#ifdef FEAT_NETBEANS_INTG
pos_T pos;
char_u *ptr;
int count;
#endif
if (checkclearopq(cap->oap))
return;
if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL)
2004-06-13 20:20:40 +00:00
{
clearopbeep(cap->oap);
return;
}
prep_redo_cmd(cap);
if (u_save_cursor() == FAIL)
return;
startpos = curwin->w_cursor;
#ifdef FEAT_NETBEANS_INTG
pos = startpos;
#endif
for (n = cap->count1; n > 0; --n)
{
did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
inc_cursor();
if (gchar_cursor() == NUL)
{
if (vim_strchr(p_ww, '~') != NULL
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
{
#ifdef FEAT_NETBEANS_INTG
2010-05-22 21:34:09 +02:00
if (netbeans_active())
2004-06-13 20:20:40 +00:00
{
if (did_change)
{
ptr = ml_get(pos.lnum);
count = (int)ml_get_len(pos.lnum) - pos.col;
2004-10-24 19:18:58 +00:00
netbeans_removed(curbuf, pos.lnum, pos.col,
(long)count);
// line may have been flushed, get it again
ptr = ml_get(pos.lnum);
2004-06-13 20:20:40 +00:00
netbeans_inserted(curbuf, pos.lnum, pos.col,
2004-10-24 19:18:58 +00:00
&ptr[pos.col], count);
2004-06-13 20:20:40 +00:00
}
pos.col = 0;
pos.lnum++;
}
#endif
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
if (n > 1)
{
if (u_savesub(curwin->w_cursor.lnum) == FAIL)
break;
u_clearline();
}
}
else
break;
}
}
#ifdef FEAT_NETBEANS_INTG
2010-05-22 21:34:09 +02:00
if (did_change && netbeans_active())
2004-06-13 20:20:40 +00:00
{
ptr = ml_get(pos.lnum);
count = curwin->w_cursor.col - pos.col;
2004-10-24 19:18:58 +00:00
netbeans_removed(curbuf, pos.lnum, pos.col, (long)count);
netbeans_inserted(curbuf, pos.lnum, pos.col, &ptr[pos.col], count);
2004-06-13 20:20:40 +00:00
}
#endif
check_cursor();
curwin->w_set_curswant = TRUE;
if (did_change)
{
changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1,
0L);
curbuf->b_op_start = startpos;
curbuf->b_op_end = curwin->w_cursor;
if (curbuf->b_op_end.col > 0)
--curbuf->b_op_end.col;
}
}
/*
* Move cursor to mark.
*/
static void
nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
2004-06-13 20:20:40 +00:00
{
if (check_mark(pos) == FAIL)
clearop(cap->oap);
else
{
if (cap->cmdchar == '\''
|| cap->cmdchar == '`'
|| cap->cmdchar == '['
|| cap->cmdchar == ']')
setpcmark();
curwin->w_cursor = *pos;
if (flag)
beginline(BL_WHITE | BL_FIX);
else
check_cursor();
}
cap->oap->motion_type = flag ? MLINE : MCHAR;
if (cap->cmdchar == '`')
cap->oap->use_reg_one = TRUE;
cap->oap->inclusive = FALSE; // ignored if not MCHAR
2004-06-13 20:20:40 +00:00
curwin->w_set_curswant = TRUE;
}
/*
* Handle commands that are operators in Visual mode.
*/
static void
v_visop(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
static char_u trans[] = "YyDdCcxdXdAAIIrr";
// Uppercase means linewise, except in block mode, then "D" deletes till
// the end of the line, and "C" replaces till EOL
if (SAFE_isupper(cap->cmdchar))
2004-06-13 20:20:40 +00:00
{
if (VIsual_mode != Ctrl_V)
{
VIsual_mode_orig = VIsual_mode;
2004-06-13 20:20:40 +00:00
VIsual_mode = 'V';
}
2004-06-13 20:20:40 +00:00
else if (cap->cmdchar == 'C' || cap->cmdchar == 'D')
curwin->w_curswant = MAXCOL;
}
cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
nv_operator(cap);
}
/*
* "s" and "S" commands.
*/
static void
nv_subst(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_TERMINAL
// When showing output of term_dumpdiff() swap the top and bottom.
if (term_swap_diff() == OK)
return;
#endif
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf) && !prompt_curpos_editable())
{
clearopbeep(cap->oap);
return;
}
#endif
if (VIsual_active) // "vs" and "vS" are the same as "vc"
2004-06-13 20:20:40 +00:00
{
if (cap->cmdchar == 'S')
{
VIsual_mode_orig = VIsual_mode;
2004-06-13 20:20:40 +00:00
VIsual_mode = 'V';
}
2004-06-13 20:20:40 +00:00
cap->cmdchar = 'c';
nv_operator(cap);
}
else
nv_optrans(cap);
}
/*
* Abbreviated commands.
*/
static void
nv_abbrev(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL)
cap->cmdchar = 'x'; // DEL key behaves like 'x'
2004-06-13 20:20:40 +00:00
// in Visual mode these commands are operators
2004-06-13 20:20:40 +00:00
if (VIsual_active)
v_visop(cap);
else
nv_optrans(cap);
}
/*
* Translate a command into another command.
*/
static void
nv_optrans(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
(char_u *)"d$", (char_u *)"c$",
(char_u *)"cl", (char_u *)"cc",
(char_u *)"yy", (char_u *)":s\r"};
static char_u *str = (char_u *)"xXDCsSY&";
if (!checkclearopq(cap->oap))
{
// In Vi "2D" doesn't delete the next line. Can't translate it
// either, because "2." should also not use the count.
2005-02-12 14:29:27 +00:00
if (cap->cmdchar == 'D' && vim_strchr(p_cpo, CPO_HASH) != NULL)
{
cap->oap->start = curwin->w_cursor;
cap->oap->op_type = OP_DELETE;
2008-01-05 12:35:21 +00:00
#ifdef FEAT_EVAL
set_op_var(OP_DELETE);
#endif
2005-02-12 14:29:27 +00:00
cap->count1 = 1;
nv_dollar(cap);
finish_op = TRUE;
ResetRedobuff();
AppendCharToRedobuff('D');
}
else
{
if (cap->count0)
stuffnumReadbuff(cap->count0);
stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]);
2005-02-12 14:29:27 +00:00
}
2004-06-13 20:20:40 +00:00
}
cap->opcount = 0;
}
/*
* "'" and "`" commands. Also for "g'" and "g`".
* cap->arg is TRUE for "'" and "g'".
*/
static void
nv_gomark(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
pos_T *pos;
int c;
#ifdef FEAT_FOLDING
pos_T old_cursor = curwin->w_cursor;
int old_KeyTyped = KeyTyped; // getting file may reset it
2004-06-13 20:20:40 +00:00
#endif
if (cap->cmdchar == 'g')
c = cap->extra_char;
else
c = cap->nchar;
pos = getmark(c, (cap->oap->op_type == OP_NOP));
if (pos == (pos_T *)-1) // jumped to other file
2004-06-13 20:20:40 +00:00
{
if (cap->arg)
{
check_cursor_lnum();
beginline(BL_WHITE | BL_FIX);
}
else
check_cursor();
}
else
nv_cursormark(cap, cap->arg, pos);
// May need to clear the coladd that a mark includes.
2004-06-13 20:20:40 +00:00
if (!virtual_active())
curwin->w_cursor.coladd = 0;
check_cursor_col();
2004-06-13 20:20:40 +00:00
#ifdef FEAT_FOLDING
if (cap->oap->op_type == OP_NOP
&& pos != NULL
&& (pos == (pos_T *)-1 || !EQUAL_POS(old_cursor, *pos))
2004-06-13 20:20:40 +00:00
&& (fdo_flags & FDO_MARK)
&& old_KeyTyped)
foldOpenCursor();
#endif
}
/*
* Handle CTRL-O, CTRL-I, "g;", "g," and "CTRL-Tab" commands.
2004-06-13 20:20:40 +00:00
*/
static void
nv_pcmark(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
pos_T *pos;
#ifdef FEAT_FOLDING
2004-06-13 20:20:40 +00:00
linenr_T lnum = curwin->w_cursor.lnum;
int old_KeyTyped = KeyTyped; // getting file may reset it
#endif
2004-06-13 20:20:40 +00:00
if (checkclearopq(cap->oap))
return;
if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL)
2004-06-13 20:20:40 +00:00
{
if (goto_tabpage_lastused() == FAIL)
2004-06-13 20:20:40 +00:00
clearopbeep(cap->oap);
return;
}
if (cap->cmdchar == 'g')
pos = movechangelist((int)cap->count1);
else
pos = movemark((int)cap->count1);
if (pos == (pos_T *)-1) // jump to other file
{
curwin->w_set_curswant = TRUE;
check_cursor();
}
else if (pos != NULL) // can jump
nv_cursormark(cap, FALSE, pos);
else if (cap->cmdchar == 'g')
{
if (curbuf->b_changelistlen == 0)
emsg(_(e_changelist_is_empty));
else if (cap->count1 < 0)
emsg(_(e_at_start_of_changelist));
else
emsg(_(e_at_end_of_changelist));
}
else
clearopbeep(cap->oap);
2004-06-13 20:20:40 +00:00
# ifdef FEAT_FOLDING
if (cap->oap->op_type == OP_NOP
&& (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum)
&& (fdo_flags & FDO_MARK)
&& old_KeyTyped)
foldOpenCursor();
2004-06-13 20:20:40 +00:00
# endif
}
/*
* Handle '"' command.
*/
static void
nv_regname(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearop(cap->oap))
return;
#ifdef FEAT_EVAL
if (cap->nchar == '=')
cap->nchar = get_expr_register();
#endif
if (cap->nchar != NUL && valid_yank_reg(cap->nchar, FALSE))
{
cap->oap->regname = cap->nchar;
cap->opcount = cap->count0; // remember count before '"'
2004-06-13 20:20:40 +00:00
#ifdef FEAT_EVAL
set_reg_var(cap->oap->regname);
#endif
}
else
clearopbeep(cap->oap);
}
/*
* Handle "v", "V" and "CTRL-V" commands.
* Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
* is TRUE.
2005-02-22 08:39:57 +00:00
* Handle CTRL-Q just like CTRL-V.
2004-06-13 20:20:40 +00:00
*/
static void
nv_visual(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
2005-02-22 08:39:57 +00:00
if (cap->cmdchar == Ctrl_Q)
cap->cmdchar = Ctrl_V;
// 'v', 'V' and CTRL-V can be used while an operator is pending to make it
// characterwise, linewise, or blockwise.
2004-06-13 20:20:40 +00:00
if (cap->oap->op_type != OP_NOP)
{
motion_force = cap->oap->motion_force = cap->cmdchar;
finish_op = FALSE; // operator doesn't finish now but later
2004-06-13 20:20:40 +00:00
return;
}
VIsual_select = cap->arg;
if (VIsual_active) // change Visual mode
2004-06-13 20:20:40 +00:00
{
if (VIsual_mode == cap->cmdchar) // stop visual mode
2004-06-13 20:20:40 +00:00
end_visual_mode();
else // toggle char/block mode
{ // or char/line mode
2004-06-13 20:20:40 +00:00
VIsual_mode = cap->cmdchar;
showmode();
may_trigger_modechanged();
2004-06-13 20:20:40 +00:00
}
redraw_curbuf_later(UPD_INVERTED); // update the inversion
2004-06-13 20:20:40 +00:00
}
else // start Visual mode
2004-06-13 20:20:40 +00:00
{
check_visual_highlight();
if (cap->count0 > 0 && resel_VIsual_mode != NUL)
2004-06-13 20:20:40 +00:00
{
// use previously selected part
2004-06-13 20:20:40 +00:00
VIsual = curwin->w_cursor;
VIsual_active = TRUE;
VIsual_reselect = TRUE;
if (!cap->arg)
// start Select mode when 'selectmode' contains "cmd"
2004-06-13 20:20:40 +00:00
may_start_select('c');
setmouse();
2006-01-23 22:23:09 +00:00
if (p_smd && msg_silent == 0)
redraw_cmdline = TRUE; // show visual mode later
// For V and ^V, we multiply the number of lines even if there
// was only one -- webb
2004-06-13 20:20:40 +00:00
if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
{
curwin->w_cursor.lnum +=
resel_VIsual_line_count * cap->count0 - 1;
check_cursor();
2004-06-13 20:20:40 +00:00
}
VIsual_mode = resel_VIsual_mode;
if (VIsual_mode == 'v')
{
if (resel_VIsual_line_count <= 1)
{
update_curswant_force();
curwin->w_curswant += resel_VIsual_vcol * cap->count0;
if (*p_sel != 'e')
--curwin->w_curswant;
}
2004-06-13 20:20:40 +00:00
else
curwin->w_curswant = resel_VIsual_vcol;
coladvance(curwin->w_curswant);
2004-06-13 20:20:40 +00:00
}
if (resel_VIsual_vcol == MAXCOL)
2004-06-13 20:20:40 +00:00
{
curwin->w_curswant = MAXCOL;
coladvance((colnr_T)MAXCOL);
}
else if (VIsual_mode == Ctrl_V)
{
// Update curswant on the original line, that is where "col" is
// valid.
linenr_T lnum = curwin->w_cursor.lnum;
curwin->w_cursor.lnum = VIsual.lnum;
update_curswant_force();
curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1;
curwin->w_cursor.lnum = lnum;
2004-06-13 20:20:40 +00:00
coladvance(curwin->w_curswant);
}
else
curwin->w_set_curswant = TRUE;
redraw_curbuf_later(UPD_INVERTED); // show the inversion
2004-06-13 20:20:40 +00:00
}
else
{
if (!cap->arg)
// start Select mode when 'selectmode' contains "cmd"
2004-06-13 20:20:40 +00:00
may_start_select('c');
n_start_visual_mode(cap->cmdchar);
if (VIsual_mode != 'V' && *p_sel == 'e')
++cap->count1; // include one more char
if (cap->count0 > 0 && --cap->count1 > 0)
{
// With a count select that many characters or lines.
if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V)
nv_right(cap);
else if (VIsual_mode == 'V')
nv_down(cap);
}
2004-06-13 20:20:40 +00:00
}
}
}
/*
* Start selection for Shift-movement keys.
*/
void
start_selection(void)
2004-06-13 20:20:40 +00:00
{
// if 'selectmode' contains "key", start Select mode
2004-06-13 20:20:40 +00:00
may_start_select('k');
n_start_visual_mode('v');
}
/*
* Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
* When "c" is 'o' (checking for "mouse") then also when mapped.
2004-06-13 20:20:40 +00:00
*/
void
may_start_select(int c)
2004-06-13 20:20:40 +00:00
{
VIsual_select = (c == 'o' || (stuff_empty() && typebuf_typed()))
&& vim_strchr(p_slm, c) != NULL;
2004-06-13 20:20:40 +00:00
}
/*
* Start Visual mode "c".
* Should set VIsual_select before calling this.
*/
static void
n_start_visual_mode(int c)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_CONCEAL
int cursor_line_was_concealed = curwin->w_p_cole > 0
&& conceal_cursor_line(curwin);
#endif
2004-06-13 20:20:40 +00:00
VIsual_mode = c;
VIsual_active = TRUE;
VIsual_reselect = TRUE;
// Corner case: the 0 position in a tab may change when going into
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB)
{
validate_virtcol();
2004-06-13 20:20:40 +00:00
coladvance(curwin->w_virtcol);
}
2004-06-13 20:20:40 +00:00
VIsual = curwin->w_cursor;
#ifdef FEAT_FOLDING
foldAdjustVisual();
#endif
may_trigger_modechanged();
2004-06-13 20:20:40 +00:00
setmouse();
#ifdef FEAT_CONCEAL
// Check if redraw is needed after changing the state.
conceal_check_cursor_line(cursor_line_was_concealed);
#endif
2006-01-23 22:23:09 +00:00
if (p_smd && msg_silent == 0)
redraw_cmdline = TRUE; // show visual mode later
2004-06-13 20:20:40 +00:00
#ifdef FEAT_CLIPBOARD
// Make sure the clipboard gets updated. Needed because start and
// end may still be the same, and the selection needs to be owned
2004-06-13 20:20:40 +00:00
clip_star.vmode = NUL;
#endif
// Only need to redraw this line, unless still need to redraw an old
// Visual area (when 'lazyredraw' is set).
if (curwin->w_redr_type < UPD_INVERTED)
2004-06-13 20:20:40 +00:00
{
curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
}
}
/*
* CTRL-W: Window commands
*/
static void
nv_window(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->nchar == ':')
{
// "CTRL-W :" is the same as typing ":"; useful in a terminal window
cap->cmdchar = ':';
cap->nchar = NUL;
nv_colon(cap);
}
else if (!checkclearop(cap->oap))
do_window(cap->nchar, cap->count0, NUL); // everything is in window.c
2004-06-13 20:20:40 +00:00
}
/*
* CTRL-Z: Suspend
*/
static void
nv_suspend(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
clearop(cap->oap);
if (VIsual_active)
end_visual_mode(); // stop Visual mode
do_cmdline_cmd((char_u *)"stop");
2004-06-13 20:20:40 +00:00
}
/*
* "gv": Reselect the previous Visual area. If Visual already active,
* exchange previous and current Visual area.
*/
static void
nv_gv_cmd(cmdarg_T *cap)
{
pos_T tpos;
int i;
if (checkclearop(cap->oap))
return;
if (curbuf->b_visual.vi_start.lnum == 0
|| curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
|| curbuf->b_visual.vi_end.lnum == 0)
{
beep_flush();
return;
}
// set w_cursor to the start of the Visual area, tpos to the end
if (VIsual_active)
{
i = VIsual_mode;
VIsual_mode = curbuf->b_visual.vi_mode;
curbuf->b_visual.vi_mode = i;
# ifdef FEAT_EVAL
curbuf->b_visual_mode_eval = i;
# endif
i = curwin->w_curswant;
curwin->w_curswant = curbuf->b_visual.vi_curswant;
curbuf->b_visual.vi_curswant = i;
tpos = curbuf->b_visual.vi_end;
curbuf->b_visual.vi_end = curwin->w_cursor;
curwin->w_cursor = curbuf->b_visual.vi_start;
curbuf->b_visual.vi_start = VIsual;
}
else
{
VIsual_mode = curbuf->b_visual.vi_mode;
curwin->w_curswant = curbuf->b_visual.vi_curswant;
tpos = curbuf->b_visual.vi_end;
curwin->w_cursor = curbuf->b_visual.vi_start;
}
VIsual_active = TRUE;
VIsual_reselect = TRUE;
// Set Visual to the start and w_cursor to the end of the Visual
// area. Make sure they are on an existing character.
check_cursor();
VIsual = curwin->w_cursor;
curwin->w_cursor = tpos;
check_cursor();
update_topline();
// When called from normal "g" command: start Select mode when
// 'selectmode' contains "cmd". When called for K_SELECT, always
// start Select mode.
if (cap->arg)
{
VIsual_select = TRUE;
VIsual_select_reg = 0;
}
else
may_start_select('c');
setmouse();
#ifdef FEAT_CLIPBOARD
// Make sure the clipboard gets updated. Needed because start and
// end are still the same, and the selection needs to be owned
clip_star.vmode = NUL;
#endif
redraw_curbuf_later(UPD_INVERTED);
showmode();
}
/*
* "g0", "g^" : Like "0" and "^" but for screen lines.
* "gm": middle of "g0" and "g$".
*/
void
nv_g_home_m_cmd(cmdarg_T *cap)
{
int i;
int flag = FALSE;
if (cap->nchar == '^')
flag = TRUE;
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
if (curwin->w_p_wrap && curwin->w_width != 0)
{
int width1 = curwin->w_width - curwin_col_off();
int width2 = width1 + curwin_col_off2();
int virtcol;
validate_virtcol();
virtcol = curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
;
i = 0;
if (virtcol >= (colnr_T)width1 && width2 > 0)
i = (virtcol - width1) / width2 * width2 + width1;
// When ending up below 'smoothscroll' marker, move just beyond it so
// that skipcol is not adjusted later.
if (curwin->w_skipcol > 0 && curwin->w_cursor.lnum == curwin->w_topline)
{
int overlap = sms_marker_overlap(curwin, -1);
if (overlap > 0 && i == curwin->w_skipcol)
i += overlap;
}
}
else
i = curwin->w_leftcol;
// Go to the middle of the screen line. When 'number' or
// 'relativenumber' is on and lines are wrapping the middle can be more
// to the left.
if (cap->nchar == 'm')
i += (curwin->w_width - curwin_col_off()
+ ((curwin->w_p_wrap && i > 0)
? curwin_col_off2() : 0)) / 2;
coladvance((colnr_T)i);
if (flag)
{
do
i = gchar_cursor();
while (VIM_ISWHITE(i) && oneright() == OK);
curwin->w_valid &= ~VALID_WCOL;
}
curwin->w_set_curswant = TRUE;
#ifdef FEAT_FOLDING
if (hasAnyFolding(curwin))
{
validate_cheight();
if (curwin->w_cline_folded)
update_curswant_force();
}
#endif
adjust_skipcol();
}
/*
* "g_": to the last non-blank character in the line or <count> lines
* downward.
*/
static void
nv_g_underscore_cmd(cmdarg_T *cap)
{
char_u *ptr;
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = TRUE;
curwin->w_curswant = MAXCOL;
if (cursor_down((long)(cap->count1 - 1),
cap->oap->op_type == OP_NOP) == FAIL)
{
clearopbeep(cap->oap);
return;
}
ptr = ml_get_curline();
// In Visual mode we may end up after the line.
if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL)
--curwin->w_cursor.col;
// Decrease the cursor column until it's on a non-blank.
while (curwin->w_cursor.col > 0
&& VIM_ISWHITE(ptr[curwin->w_cursor.col]))
--curwin->w_cursor.col;
curwin->w_set_curswant = TRUE;
adjust_for_sel(cap);
}
/*
* "g$" : Like "$" but for screen lines.
*/
static void
nv_g_dollar_cmd(cmdarg_T *cap)
{
oparg_T *oap = cap->oap;
int i;
int col_off = curwin_col_off();
int flag = FALSE;
if (cap->nchar == K_END || cap->nchar == K_KEND)
flag = TRUE;
oap->motion_type = MCHAR;
oap->inclusive = TRUE;
if (curwin->w_p_wrap && curwin->w_width != 0)
{
curwin->w_curswant = MAXCOL; // so we stay at the end
if (cap->count1 == 1)
{
int width1 = curwin->w_width - col_off;
int width2 = width1 + curwin_col_off2();
int virtcol;
validate_virtcol();
virtcol = curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
;
i = width1 - 1;
if (virtcol >= (colnr_T)width1)
i += ((virtcol - width1) / width2 + 1)
* width2;
coladvance((colnr_T)i);
// Make sure we stick in this column.
update_curswant_force();
if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
{
// Check for landing on a character that got split at
// the end of the line. We do not want to advance to
// the next screen line.
if (curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
> (colnr_T)i)
--curwin->w_cursor.col;
}
}
else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == FAIL)
clearopbeep(oap);
}
else
{
if (cap->count1 > 1)
// if it fails, let the cursor still move to the last char
(void)cursor_down(cap->count1 - 1, FALSE);
i = curwin->w_leftcol + curwin->w_width - col_off - 1;
coladvance((colnr_T)i);
// if the character doesn't fit move one back
if (curwin->w_cursor.col > 0
&& (*mb_ptr2cells)(ml_get_cursor()) > 1)
{
colnr_T vcol;
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
if (vcol >= curwin->w_leftcol + curwin->w_width - col_off)
--curwin->w_cursor.col;
}
// Make sure we stick in this column.
update_curswant_force();
}
if (flag)
{
do
i = gchar_cursor();
while (VIM_ISWHITE(i) && oneleft() == OK);
curwin->w_valid &= ~VALID_WCOL;
}
}
/*
* "gi": start Insert at the last position.
*/
static void
nv_gi_cmd(cmdarg_T *cap)
{
int i;
if (curbuf->b_last_insert.lnum != 0)
{
curwin->w_cursor = curbuf->b_last_insert;
check_cursor_lnum();
i = (int)ml_get_curline_len();
if (curwin->w_cursor.col > (colnr_T)i)
{
if (virtual_active())
curwin->w_cursor.coladd += curwin->w_cursor.col - i;
curwin->w_cursor.col = i;
}
}
cap->cmdchar = 'i';
nv_edit(cap);
}
2004-06-13 20:20:40 +00:00
/*
* Commands starting with "g".
*/
static void
nv_g_cmd(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
oparg_T *oap = cap->oap;
int i;
switch (cap->nchar)
{
case Ctrl_A:
case Ctrl_X:
2004-06-13 20:20:40 +00:00
#ifdef MEM_PROFILE
// "g^A": dump log of used memory.
if (!VIsual_active && cap->nchar == Ctrl_A)
vim_mem_profile_dump();
else
2004-06-13 20:20:40 +00:00
#endif
// "g^A/g^X": sequentially increment visually selected region
if (VIsual_active)
{
cap->arg = TRUE;
cap->cmdchar = cap->nchar;
cap->nchar = NUL;
nv_addsub(cap);
}
else
clearopbeep(oap);
break;
2004-06-13 20:20:40 +00:00
// "gR": Enter virtual replace mode.
2004-06-13 20:20:40 +00:00
case 'R':
cap->arg = TRUE;
nv_Replace(cap);
break;
case 'r':
nv_vreplace(cap);
break;
case '&':
do_cmdline_cmd((char_u *)"%s//~/&");
break;
// "gv": Reselect the previous Visual area. If Visual already active,
// exchange previous and current Visual area.
2004-06-13 20:20:40 +00:00
case 'v':
nv_gv_cmd(cap);
2004-06-13 20:20:40 +00:00
break;
// "gV": Don't reselect the previous Visual area after a Select mode
// mapping of menu.
2004-06-13 20:20:40 +00:00
case 'V':
VIsual_reselect = FALSE;
break;
// "gh": start Select mode.
// "gH": start Select line mode.
// "g^H": start Select block mode.
2004-06-13 20:20:40 +00:00
case K_BS:
cap->nchar = Ctrl_H;
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case 'h':
case 'H':
case Ctrl_H:
cap->cmdchar = cap->nchar + ('v' - 'h');
cap->arg = TRUE;
nv_visual(cap);
break;
// "gn", "gN" visually select next/previous search match
// "gn" selects next match
// "gN" selects previous match
case 'N':
case 'n':
if (!current_search(cap->count1, cap->nchar == 'n'))
clearopbeep(oap);
break;
2004-06-13 20:20:40 +00:00
// "gj" and "gk" two new funny movement keys -- up and down
// movement based on *screen* line rather than *file* line.
2004-06-13 20:20:40 +00:00
case 'j':
case K_DOWN:
// with 'nowrap' it works just like the normal "j" command.
if (!curwin->w_p_wrap)
2004-06-13 20:20:40 +00:00
{
oap->motion_type = MLINE;
i = cursor_down(cap->count1, oap->op_type == OP_NOP);
}
else
i = nv_screengo(oap, FORWARD, cap->count1);
if (i == FAIL)
clearopbeep(oap);
break;
case 'k':
case K_UP:
// with 'nowrap' it works just like the normal "k" command.
if (!curwin->w_p_wrap)
2004-06-13 20:20:40 +00:00
{
oap->motion_type = MLINE;
i = cursor_up(cap->count1, oap->op_type == OP_NOP);
}
else
i = nv_screengo(oap, BACKWARD, cap->count1);
if (i == FAIL)
clearopbeep(oap);
break;
// "gJ": join two lines without inserting a space.
2004-06-13 20:20:40 +00:00
case 'J':
nv_join(cap);
break;
// "g0", "g^" : Like "0" and "^" but for screen lines.
// "gm": middle of "g0" and "g$".
2004-06-13 20:20:40 +00:00
case '^':
case '0':
case 'm':
case K_HOME:
case K_KHOME:
nv_g_home_m_cmd(cap);
2004-06-13 20:20:40 +00:00
break;
case 'M':
{
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
i = linetabsize_no_outer(curwin, curwin->w_cursor.lnum);
if (cap->count0 > 0 && cap->count0 <= 100)
coladvance((colnr_T)(i * cap->count0 / 100));
else
coladvance((colnr_T)(i / 2));
curwin->w_set_curswant = TRUE;
}
break;
// "g_": to the last non-blank character in the line or <count> lines
// downward.
2004-06-13 20:20:40 +00:00
case '_':
nv_g_underscore_cmd(cap);
2004-06-13 20:20:40 +00:00
break;
// "g$" : Like "$" but for screen lines.
2004-06-13 20:20:40 +00:00
case '$':
case K_END:
case K_KEND:
nv_g_dollar_cmd(cap);
2004-06-13 20:20:40 +00:00
break;
// "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
2004-06-13 20:20:40 +00:00
case '*':
case '#':
#if POUND != '#'
case POUND: // pound sign (sometimes equal to '#')
2004-06-13 20:20:40 +00:00
#endif
case Ctrl_RSB: // :tag or :tselect for current identifier
case ']': // :tselect for current identifier
2004-06-13 20:20:40 +00:00
nv_ident(cap);
break;
// ge and gE: go back to end of word
2004-06-13 20:20:40 +00:00
case 'e':
case 'E':
oap->motion_type = MCHAR;
curwin->w_set_curswant = TRUE;
oap->inclusive = TRUE;
if (bckend_word(cap->count1, cap->nchar == 'E', FALSE) == FAIL)
clearopbeep(oap);
break;
// "g CTRL-G": display info about cursor position
2004-06-13 20:20:40 +00:00
case Ctrl_G:
cursor_pos_info(NULL);
2004-06-13 20:20:40 +00:00
break;
// "gi": start Insert at the last position.
2004-06-13 20:20:40 +00:00
case 'i':
nv_gi_cmd(cap);
2004-06-13 20:20:40 +00:00
break;
// "gI": Start insert in column 1.
2004-06-13 20:20:40 +00:00
case 'I':
beginline(0);
if (!checkclearopq(oap))
invoke_edit(cap, FALSE, 'g', FALSE);
break;
// "gf": goto file, edit file under cursor
// "]f" and "[f": can also be used.
2004-06-13 20:20:40 +00:00
case 'f':
2006-02-22 21:25:37 +00:00
case 'F':
2004-06-13 20:20:40 +00:00
nv_gotofile(cap);
break;
// "g'm" and "g`m": jump to mark without setting pcmark
2004-06-13 20:20:40 +00:00
case '\'':
cap->arg = TRUE;
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case '`':
nv_gomark(cap);
break;
// "gs": Goto sleep.
2004-06-13 20:20:40 +00:00
case 's':
do_sleep(cap->count1 * 1000L, FALSE);
2004-06-13 20:20:40 +00:00
break;
// "ga": Display the ascii value of the character under the
// cursor. It is displayed in decimal, hex, and octal. -- webb
2004-06-13 20:20:40 +00:00
case 'a':
do_ascii(NULL);
break;
// "g8": Display the bytes used for the UTF-8 character under the
// cursor. It is displayed in hex.
// "8g8" finds illegal byte sequence.
2004-06-13 20:20:40 +00:00
case '8':
2006-03-17 23:10:44 +00:00
if (cap->count0 == 8)
utf_find_illegal();
else
show_utf8();
2004-06-13 20:20:40 +00:00
break;
// "g<": show scrollback text
2005-07-28 22:28:16 +00:00
case '<':
show_sb_text();
break;
// "gg": Goto the first line in file. With a count it goes to
// that line number like for "G". -- webb
2004-06-13 20:20:40 +00:00
case 'g':
cap->arg = FALSE;
nv_goto(cap);
break;
// Two-character operators:
// "gq" Format text
// "gw" Format text and keep cursor position
// "g~" Toggle the case of the text.
// "gu" Change text to lower case.
// "gU" Change text to upper case.
// "g?" rot13 encoding
// "g@" call 'operatorfunc'
2004-06-13 20:20:40 +00:00
case 'q':
case 'w':
oap->cursor_start = curwin->w_cursor;
// FALLTHROUGH
2004-06-13 20:20:40 +00:00
case '~':
case 'u':
case 'U':
case '?':
2005-12-16 21:49:31 +00:00
case '@':
2004-06-13 20:20:40 +00:00
nv_operator(cap);
break;
// "gd": Find first occurrence of pattern under the cursor in the
// current function
// "gD": idem, but in the current file.
2004-06-13 20:20:40 +00:00
case 'd':
case 'D':
2005-09-13 21:20:47 +00:00
nv_gd(oap, cap->nchar, (int)cap->count0);
2004-06-13 20:20:40 +00:00
break;
// g<*Mouse> : <C-*mouse>
2004-06-13 20:20:40 +00:00
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_MOUSEMOVE:
2004-06-13 20:20:40 +00:00
case K_RIGHTMOUSE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
case K_X1MOUSE:
case K_X1DRAG:
case K_X1RELEASE:
case K_X2MOUSE:
case K_X2DRAG:
case K_X2RELEASE:
mod_mask = MOD_MASK_CTRL;
(void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0);
break;
case K_IGNORE:
break;
// "gP" and "gp": same as "P" and "p" but leave cursor just after new text
2004-06-13 20:20:40 +00:00
case 'p':
case 'P':
nv_put(cap);
break;
#ifdef FEAT_BYTEOFF
// "go": goto byte count from start of buffer
2004-06-13 20:20:40 +00:00
case 'o':
oap->inclusive = FALSE;
2004-06-13 20:20:40 +00:00
goto_byte(cap->count0);
break;
#endif
// "gQ": improved Ex mode
2004-06-13 20:20:40 +00:00
case 'Q':
if (!check_text_locked(cap->oap) && !checkclearopq(oap))
2004-06-13 20:20:40 +00:00
do_exmode(TRUE);
break;
case ',':
nv_pcmark(cap);
break;
case ';':
cap->count1 = -cap->count1;
nv_pcmark(cap);
break;
2006-02-14 22:29:30 +00:00
case 't':
if (!checkclearop(oap))
goto_tabpage((int)cap->count0);
2006-02-14 22:29:30 +00:00
break;
2006-02-23 21:26:58 +00:00
case 'T':
if (!checkclearop(oap))
goto_tabpage(-(int)cap->count1);
2006-02-23 21:26:58 +00:00
break;
2006-02-14 22:29:30 +00:00
case TAB:
if (!checkclearop(oap) && goto_tabpage_lastused() == FAIL)
clearopbeep(oap);
break;
2006-03-13 22:07:11 +00:00
case '+':
case '-': // "g+" and "g-": undo or redo along the timeline
2006-03-13 22:07:11 +00:00
if (!checkclearopq(oap))
2006-03-16 21:35:52 +00:00
undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1,
2010-06-27 05:18:54 +02:00
FALSE, FALSE, FALSE);
2006-03-13 22:07:11 +00:00
break;
2004-06-13 20:20:40 +00:00
default:
clearopbeep(oap);
break;
}
}
/*
* Handle "o" and "O" commands.
*/
static void
n_opencmd(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_CONCEAL
linenr_T oldline = curwin->w_cursor.lnum;
#endif
if (checkclearopq(cap->oap))
return;
2004-06-13 20:20:40 +00:00
#ifdef FEAT_FOLDING
if (cap->cmdchar == 'O')
// Open above the first line of a folded sequence of lines
(void)hasFolding(curwin->w_cursor.lnum,
&curwin->w_cursor.lnum, NULL);
else
// Open below the last line of a folded sequence of lines
(void)hasFolding(curwin->w_cursor.lnum,
NULL, &curwin->w_cursor.lnum);
#endif
// trigger TextChangedI for the 'o/O' command
curbuf->b_last_changedtick_i = CHANGEDTICK(curbuf);
if (u_save((linenr_T)(curwin->w_cursor.lnum -
(cap->cmdchar == 'O' ? 1 : 0)),
(linenr_T)(curwin->w_cursor.lnum +
(cap->cmdchar == 'o' ? 1 : 0))
) == OK
&& open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0,
0, NULL) == OK)
{
#ifdef FEAT_CONCEAL
if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum)
redrawWinline(curwin, oldline);
#endif
#ifdef FEAT_SYN_HL
if (curwin->w_p_cul)
// force redraw of cursorline
curwin->w_valid &= ~VALID_CROW;
#endif
// When '#' is in 'cpoptions' ignore the count.
if (vim_strchr(p_cpo, CPO_HASH) != NULL)
cap->count1 = 1;
invoke_edit(cap, FALSE, cap->cmdchar, TRUE);
2004-06-13 20:20:40 +00:00
}
}
/*
* "." command: redo last change.
*/
static void
nv_dot(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearopq(cap->oap))
return;
// If "restart_edit" is TRUE, the last but one command is repeated
// instead of the last command (inserting text). This is used for
// CTRL-O <.> in insert mode.
if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == FAIL)
clearopbeep(cap->oap);
2004-06-13 20:20:40 +00:00
}
/*
* CTRL-R: undo undo or specify register in select mode
2004-06-13 20:20:40 +00:00
*/
static void
nv_redo_or_register(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_select && VIsual_active)
{
int reg;
// Get register name
++no_mapping;
++allow_keys;
reg = plain_vgetc();
LANGMAP_ADJUST(reg, TRUE);
--no_mapping;
--allow_keys;
if (reg == '"')
// the unnamed register is 0
reg = 0;
VIsual_select_reg = valid_yank_reg(reg, TRUE) ? reg : 0;
return;
}
if (checkclearopq(cap->oap))
return;
u_redo((int)cap->count1);
curwin->w_set_curswant = TRUE;
2004-06-13 20:20:40 +00:00
}
/*
* Handle "U" command.
*/
static void
nv_Undo(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
// In Visual mode and typing "gUU" triggers an operator
if (cap->oap->op_type == OP_UPPER || VIsual_active)
2004-06-13 20:20:40 +00:00
{
// translate "gUU" to "gUgU"
2004-06-13 20:20:40 +00:00
cap->cmdchar = 'g';
cap->nchar = 'U';
nv_operator(cap);
return;
2004-06-13 20:20:40 +00:00
}
if (checkclearopq(cap->oap))
return;
u_undoline();
curwin->w_set_curswant = TRUE;
2004-06-13 20:20:40 +00:00
}
/*
* '~' command: If tilde is not an operator and Visual is off: swap case of a
* single character.
*/
static void
nv_tilde(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE)
{
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf) && !prompt_curpos_editable())
{
clearopbeep(cap->oap);
return;
}
#endif
2004-06-13 20:20:40 +00:00
n_swapchar(cap);
}
2004-06-13 20:20:40 +00:00
else
nv_operator(cap);
}
/*
* Handle an operator command.
* The actual work is done by do_pending_operator().
*/
static void
nv_operator(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int op_type;
op_type = get_op_type(cap->cmdchar, cap->nchar);
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf) && op_is_change(op_type) && !prompt_curpos_editable())
{
clearopbeep(cap->oap);
return;
}
#endif
2004-06-13 20:20:40 +00:00
if (op_type == cap->oap->op_type) // double operator works on lines
2004-06-13 20:20:40 +00:00
nv_lineop(cap);
else if (!checkclearop(cap->oap))
{
cap->oap->start = curwin->w_cursor;
cap->oap->op_type = op_type;
2008-01-05 12:35:21 +00:00
#ifdef FEAT_EVAL
set_op_var(op_type);
#endif
}
}
#ifdef FEAT_EVAL
/*
* Set v:operator to the characters for "optype".
*/
static void
set_op_var(int optype)
2008-01-05 12:35:21 +00:00
{
char_u opchars[3];
if (optype == OP_NOP)
set_vim_var_string(VV_OP, NULL, 0);
else
{
opchars[0] = get_op_char(optype);
opchars[1] = get_extra_op_char(optype);
opchars[2] = NUL;
set_vim_var_string(VV_OP, opchars, -1);
2004-06-13 20:20:40 +00:00
}
}
2008-01-05 12:35:21 +00:00
#endif
2004-06-13 20:20:40 +00:00
/*
* Handle linewise operator "dd", "yy", etc.
*
* "_" is is a strange motion command that helps make operators more logical.
* It is actually implemented, but not documented in the real Vi. This motion
* command actually refers to "the current line". Commands like "dd" and "yy"
* are really an alternate form of "d_" and "y_". It does accept a count, so
* "d3_" works to delete 3 lines.
*/
static void
nv_lineop(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MLINE;
if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == FAIL)
clearopbeep(cap->oap);
else if ( (cap->oap->op_type == OP_DELETE // only with linewise motions
&& cap->oap->motion_force != 'v'
&& cap->oap->motion_force != Ctrl_V)
2004-06-13 20:20:40 +00:00
|| cap->oap->op_type == OP_LSHIFT
|| cap->oap->op_type == OP_RSHIFT)
beginline(BL_SOL | BL_FIX);
else if (cap->oap->op_type != OP_YANK) // 'Y' does not move cursor
2004-06-13 20:20:40 +00:00
beginline(BL_WHITE | BL_FIX);
}
/*
* <Home> command.
*/
static void
nv_home(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
// CTRL-HOME is like "gg"
2005-03-06 23:38:09 +00:00
if (mod_mask & MOD_MASK_CTRL)
nv_goto(cap);
else
{
cap->count0 = 1;
nv_pipe(cap);
}
ins_at_eol = FALSE; // Don't move cursor past eol (only necessary in a
// one-character line).
2004-06-13 20:20:40 +00:00
}
/*
* "|" command.
*/
static void
nv_pipe(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
beginline(0);
if (cap->count0 > 0)
{
coladvance((colnr_T)(cap->count0 - 1));
curwin->w_curswant = (colnr_T)(cap->count0 - 1);
}
else
curwin->w_curswant = 0;
// keep curswant at the column where we wanted to go, not where
// we ended; differs if line is too short
2004-06-13 20:20:40 +00:00
curwin->w_set_curswant = FALSE;
}
/*
* Handle back-word command "b" and "B".
* cap->arg is 1 for "B"
*/
static void
nv_bck_word(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
curwin->w_set_curswant = TRUE;
if (bck_word(cap->count1, cap->arg, FALSE) == FAIL)
clearopbeep(cap->oap);
#ifdef FEAT_FOLDING
else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
/*
* Handle word motion commands "e", "E", "w" and "W".
* cap->arg is TRUE for "E" and "W".
*/
static void
nv_wordcmd(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int n;
int word_end;
int flag = FALSE;
2008-04-01 10:06:39 +00:00
pos_T startpos = curwin->w_cursor;
2004-06-13 20:20:40 +00:00
// Set inclusive for the "E" and "e" command.
2004-06-13 20:20:40 +00:00
if (cap->cmdchar == 'e' || cap->cmdchar == 'E')
word_end = TRUE;
else
word_end = FALSE;
cap->oap->inclusive = word_end;
// "cw" and "cW" are a special case.
2004-06-13 20:20:40 +00:00
if (!word_end && cap->oap->op_type == OP_CHANGE)
{
n = gchar_cursor();
if (n != NUL) // not an empty line
2004-06-13 20:20:40 +00:00
{
if (VIM_ISWHITE(n))
2004-06-13 20:20:40 +00:00
{
// Reproduce a funny Vi behaviour: "cw" on a blank only
// changes one character, not all blanks until the start of
// the next word. Only do this when the 'w' flag is included
// in 'cpoptions'.
2004-06-13 20:20:40 +00:00
if (cap->count1 == 1 && vim_strchr(p_cpo, CPO_CW) != NULL)
{
cap->oap->inclusive = TRUE;
cap->oap->motion_type = MCHAR;
return;
}
}
else
{
// This is a little strange. To match what the real Vi does,
// we effectively map 'cw' to 'ce', and 'cW' to 'cE', provided
// that we are not on a space or a TAB. This seems impolite
// at first, but it's really more what we mean when we say
// 'cw'.
// Another strangeness: When standing on the end of a word
// "ce" will change until the end of the next word, but "cw"
// will change only one character! This is done by setting
// flag.
patch 9.1.0589: vi: d{motion} and cw work differently than expected Problem: vi: d{motion} and cw command work differently than expected Solution: add new cpo-z flag to make the behaviour configurable There are two special vi compatible behaviours (or should I say bugs?): 1): cw behaves differently than dw. That is, because cw is special cased by Vim and is effectively aliased to ce. POSIX behaviour is documented here: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html#tag_20_152_13_81 2): d{motion} may make the whole delete operation linewise, if the start and end of the motion are on different lines and there are only blanks before the start and after the end of the motion. Did not find a related POSIX link that requires this behaviour. Both behaviours can be considered inconsistent, but we cannot easily change it, because it would be a backward incompatible change and also incompatible to how classic vi behaved. So let's add the new cpo flag "z", which when not included fixes both behaviours and make them more consistent to what users would expect. This has been requested several times: https://groups.google.com/d/msg/vim_use/aaBqT6ECkA4/ALf4odKzEDgJ https://groups.google.com/d/msg/vim_dev/Dpn3xtUF16I/T6JcOPKN6usJ http://www.reddit.com/r/vim/comments/26nut8/why_does_cw_work_like_ce/ https://groups.google.com/d/msg/vim_use/vunNWLFWfQg/MmJh_ZGaAgAJ https://github.com/vim/vim/issues/4390 So in summary, if you want to have the w motion work more consistent, remove the 'z' from the cpo settings. related: #4390 closes: #15263 Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-07-15 20:51:11 +02:00
// This can be configured using :set cpo-z
if (vim_strchr(p_cpo, CPO_WORD) != NULL)
{
cap->oap->inclusive = TRUE;
word_end = TRUE;
}
2004-06-13 20:20:40 +00:00
flag = TRUE;
}
}
}
cap->oap->motion_type = MCHAR;
curwin->w_set_curswant = TRUE;
if (word_end)
n = end_word(cap->count1, cap->arg, flag, FALSE);
else
n = fwd_word(cap->count1, cap->arg, cap->oap->op_type != OP_NOP);
// Don't leave the cursor on the NUL past the end of line. Unless we
// didn't move it forward.
if (LT_POS(startpos, curwin->w_cursor))
2008-01-12 16:12:10 +00:00
adjust_cursor(cap->oap);
2004-06-13 20:20:40 +00:00
if (n == FAIL && cap->oap->op_type == OP_NOP)
clearopbeep(cap->oap);
else
{
adjust_for_sel(cap);
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
}
2008-01-12 16:12:10 +00:00
/*
* Used after a movement command: If the cursor ends up on the NUL after the
* end of the line, may move it back to the last character and make the motion
* inclusive.
*/
static void
adjust_cursor(oparg_T *oap)
2008-01-12 16:12:10 +00:00
{
// The cursor cannot remain on the NUL when:
// - the column is > 0
// - not in Visual mode or 'selection' is "o"
// - 'virtualedit' is not "all" and not "onemore".
2008-01-12 16:12:10 +00:00
if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
&& (!VIsual_active || *p_sel == 'o')
&& !virtual_active() && (get_ve_flags() & VE_ONEMORE) == 0)
2008-01-12 16:12:10 +00:00
{
--curwin->w_cursor.col;
// prevent cursor from moving on the trail byte
2008-01-12 16:12:10 +00:00
if (has_mbyte)
mb_adjust_cursor();
oap->inclusive = TRUE;
}
}
2004-06-13 20:20:40 +00:00
/*
* "0" and "^" commands.
* cap->arg is the argument for beginline().
*/
static void
nv_beginline(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
beginline(cap->arg);
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
ins_at_eol = FALSE; // Don't move cursor past eol (only necessary in a
// one-character line).
2004-06-13 20:20:40 +00:00
}
/*
* In exclusive Visual mode, may include the last character.
*/
static void
adjust_for_sel(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active && cap->oap->inclusive && *p_sel == 'e'
&& gchar_cursor() != NUL && LT_POS(VIsual, curwin->w_cursor))
2004-06-13 20:20:40 +00:00
{
if (has_mbyte)
inc_cursor();
else
++curwin->w_cursor.col;
cap->oap->inclusive = FALSE;
}
}
/*
* Exclude last character at end of Visual area for 'selection' == "exclusive".
* Should check VIsual_mode before calling this.
* Returns TRUE when backed up to the previous line.
*/
int
unadjust_for_sel(void)
2004-06-13 20:20:40 +00:00
{
if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
return unadjust_for_sel_inner(LT_POS(VIsual, curwin->w_cursor)
? &curwin->w_cursor : &VIsual);
return FALSE;
}
/*
* Move position "*pp" back one character for 'selection' == "exclusive".
* Returns TRUE when backed up to the previous line.
*/
int
unadjust_for_sel_inner(pos_T *pp)
{
colnr_T cs, ce;
if (pp->coladd > 0)
--pp->coladd;
else if (pp->col > 0)
2004-06-13 20:20:40 +00:00
{
--pp->col;
mb_adjustpos(curbuf, pp);
if (virtual_active())
2004-06-13 20:20:40 +00:00
{
getvcol(curwin, pp, &cs, NULL, &ce);
pp->coladd = ce - cs;
2004-06-13 20:20:40 +00:00
}
}
else if (pp->lnum > 1)
{
--pp->lnum;
pp->col = ml_get_len(pp->lnum);
return TRUE;
}
2004-06-13 20:20:40 +00:00
return FALSE;
}
/*
* SELECT key in Normal or Visual mode: end of Select mode mapping.
*/
static void
nv_select(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active)
{
2004-06-13 20:20:40 +00:00
VIsual_select = TRUE;
VIsual_select_reg = 0;
}
2004-06-13 20:20:40 +00:00
else if (VIsual_reselect)
{
cap->nchar = 'v'; // fake "gv" command
2004-06-13 20:20:40 +00:00
cap->arg = TRUE;
nv_g_cmd(cap);
}
}
/*
* "G", "gg", CTRL-END, CTRL-HOME.
* cap->arg is TRUE for "G".
*/
static void
nv_goto(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
linenr_T lnum;
if (cap->arg)
lnum = curbuf->b_ml.ml_line_count;
else
lnum = 1L;
cap->oap->motion_type = MLINE;
setpcmark();
// When a count is given, use it instead of the default lnum
2004-06-13 20:20:40 +00:00
if (cap->count0 != 0)
lnum = cap->count0;
if (lnum < 1L)
lnum = 1L;
else if (lnum > curbuf->b_ml.ml_line_count)
lnum = curbuf->b_ml.ml_line_count;
curwin->w_cursor.lnum = lnum;
beginline(BL_SOL | BL_FIX);
#ifdef FEAT_FOLDING
if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP)
foldOpenCursor();
#endif
}
/*
* CTRL-\ in Normal mode.
*/
static void
nv_normal(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G)
{
clearop(cap->oap);
2006-01-25 22:02:51 +00:00
if (restart_edit != 0 && mode_displayed)
clear_cmdline = TRUE; // unshow mode later
2004-06-13 20:20:40 +00:00
restart_edit = 0;
if (cmdwin_type != 0)
cmdwin_result = Ctrl_C;
if (VIsual_active)
{
end_visual_mode(); // stop Visual
redraw_curbuf_later(UPD_INVERTED);
2004-06-13 20:20:40 +00:00
}
// CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set.
2004-06-13 20:20:40 +00:00
if (cap->nchar == Ctrl_G && p_im)
restart_edit = 'a';
}
else
clearopbeep(cap->oap);
}
/*
* ESC in Normal mode: beep, but don't flush buffers.
* Don't even beep if we are canceling a command.
*/
static void
nv_esc(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int no_reason;
no_reason = (cap->oap->op_type == OP_NOP
&& cap->opcount == 0
&& cap->count0 == 0
&& cap->oap->regname == 0
&& !p_im);
if (cap->arg) // TRUE for CTRL-C
2004-06-13 20:20:40 +00:00
{
if (restart_edit == 0 && cmdwin_type == 0
&& !VIsual_active && no_reason)
{
int out_redir = !stdout_isatty && !is_not_a_term_or_gui();
// The user may accidentally do "vim file | grep word" and then
// CTRL-C doesn't show anything. With a changed buffer give the
// message on stderr. Without any changes might as well exit.
if (anyBufIsChanged())
{
char *ms = _("Type :qa! and press <Enter> to abandon all changes and exit Vim");
if (out_redir)
mch_errmsg(ms);
else
msg(ms);
}
else
{
if (out_redir)
{
got_int = FALSE;
do_cmdline_cmd((char_u *)"qa");
}
else
msg(_("Type :qa and press <Enter> to exit Vim"));
}
}
2004-06-13 20:20:40 +00:00
if (restart_edit != 0)
redraw_mode = TRUE; // remove "-- (insert) --"
// Don't reset "restart_edit" when 'insertmode' is set, it won't be
// set again below when halfway a mapping.
2004-06-13 20:20:40 +00:00
if (!p_im)
restart_edit = 0;
if (cmdwin_type != 0)
{
cmdwin_result = K_IGNORE;
got_int = FALSE; // don't stop executing autocommands et al.
2004-06-13 20:20:40 +00:00
return;
}
}
else if (cmdwin_type != 0 && ex_normal_busy && typebuf_was_empty)
{
// When :normal runs out of characters while in the command line window
// vgetorpeek() will repeatedly return ESC. Exit the cmdline window to
// break the loop.
cmdwin_result = K_IGNORE;
return;
}
2004-06-13 20:20:40 +00:00
if (VIsual_active)
{
end_visual_mode(); // stop Visual
check_cursor_col(); // make sure cursor is not beyond EOL
2004-06-13 20:20:40 +00:00
curwin->w_set_curswant = TRUE;
redraw_curbuf_later(UPD_INVERTED);
2004-06-13 20:20:40 +00:00
}
else if (no_reason)
{
#ifdef HAS_MESSAGE_WINDOW
if (!cap->arg && popup_message_win_visible())
popup_hide_message_win();
else
#endif
vim_beep(BO_ESC);
}
2004-06-13 20:20:40 +00:00
clearop(cap->oap);
// A CTRL-C is often used at the start of a menu. When 'insertmode' is
// set return to Insert mode afterwards.
if (restart_edit == 0 && goto_im() && ex_normal_busy == 0)
2004-06-13 20:20:40 +00:00
restart_edit = 'a';
}
/*
* Move the cursor for the "A" command.
*/
void
set_cursor_for_append_to_line(void)
{
curwin->w_set_curswant = TRUE;
if (get_ve_flags() == VE_ALL)
{
int save_State = State;
// Pretend Insert mode here to allow the cursor on the
// character past the end of the line
State = MODE_INSERT;
coladvance((colnr_T)MAXCOL);
State = save_State;
}
else
curwin->w_cursor.col += (colnr_T)STRLEN(ml_get_cursor());
}
2004-06-13 20:20:40 +00:00
/*
* Handle "A", "a", "I", "i" and <Insert> commands.
* Also handle K_PS, start bracketed paste.
2004-06-13 20:20:40 +00:00
*/
static void
nv_edit(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
// <Insert> is equal to "i"
2004-06-13 20:20:40 +00:00
if (cap->cmdchar == K_INS || cap->cmdchar == K_KINS)
cap->cmdchar = 'i';
// in Visual mode "A" and "I" are an operator
2004-06-13 20:20:40 +00:00
if (VIsual_active && (cap->cmdchar == 'A' || cap->cmdchar == 'I'))
{
#ifdef FEAT_TERMINAL
if (term_in_normal_mode())
{
end_visual_mode();
clearop(cap->oap);
term_enter_job_mode();
return;
}
#endif
2004-06-13 20:20:40 +00:00
v_visop(cap);
}
2004-06-13 20:20:40 +00:00
// in Visual mode and after an operator "a" and "i" are for text objects
else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
&& (cap->oap->op_type != OP_NOP || VIsual_active))
2004-06-13 20:20:40 +00:00
{
nv_object(cap);
}
#ifdef FEAT_TERMINAL
else if (term_in_normal_mode())
{
clearop(cap->oap);
term_enter_job_mode();
return;
}
#endif
2004-06-13 20:20:40 +00:00
else if (!curbuf->b_p_ma && !p_im)
{
// Only give this error when 'insertmode' is off.
emsg(_(e_cannot_make_changes_modifiable_is_off));
2004-06-13 20:20:40 +00:00
clearop(cap->oap);
if (cap->cmdchar == K_PS)
// drop the pasted text
bracketed_paste(PASTE_INSERT, TRUE, NULL);
2004-06-13 20:20:40 +00:00
}
else if (cap->cmdchar == K_PS && VIsual_active)
{
pos_T old_pos = curwin->w_cursor;
pos_T old_visual = VIsual;
int old_visual_mode = VIsual_mode;
// In Visual mode the selected text is deleted.
if (VIsual_mode == 'V' || curwin->w_cursor.lnum != VIsual.lnum)
{
shift_delete_registers();
cap->oap->regname = '1';
}
else
cap->oap->regname = '-';
cap->cmdchar = 'd';
cap->nchar = NUL;
nv_operator(cap);
do_pending_operator(cap, 0, FALSE);
cap->cmdchar = K_PS;
if (*ml_get_cursor() != NUL)
{
if (old_visual_mode == 'V')
{
// In linewise Visual mode insert before the beginning of the
// next line.
// When the last line in the buffer was deleted then create a
// new line, otherwise there is not need to move cursor.
// Detect this by checking if cursor moved above Visual area.
if (curwin->w_cursor.lnum < old_pos.lnum
&& curwin->w_cursor.lnum < old_visual.lnum)
{
if (u_save_cursor() == OK)
{
ml_append(curwin->w_cursor.lnum, (char_u *)"", 0,
FALSE);
appended_lines(curwin->w_cursor.lnum++, 1L);
}
}
}
// When the last char in the line was deleted then append.
// Detect this by checking if cursor moved before Visual area.
else if (curwin->w_cursor.col < old_pos.col
&& curwin->w_cursor.col < old_visual.col)
inc_cursor();
}
// Insert to replace the deleted text with the pasted text.
invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
}
2004-06-13 20:20:40 +00:00
else if (!checkclearopq(cap->oap))
{
switch (cap->cmdchar)
{
case 'A': // "A"ppend after the line
set_cursor_for_append_to_line();
2004-06-13 20:20:40 +00:00
break;
case 'I': // "I"nsert before the first non-blank
2005-02-12 14:29:27 +00:00
if (vim_strchr(p_cpo, CPO_INSEND) == NULL)
beginline(BL_WHITE);
else
beginline(BL_WHITE|BL_FIX);
2004-06-13 20:20:40 +00:00
break;
case K_PS:
// Bracketed paste works like "a"ppend, unless the cursor is in
// the first column, then it inserts.
if (curwin->w_cursor.col == 0)
break;
// FALLTHROUGH
case 'a': // "a"ppend is like "i"nsert on the next character.
// increment coladd when in virtual space, increment the
// column otherwise, also to append after an unprintable char
2004-06-13 20:20:40 +00:00
if (virtual_active()
&& (curwin->w_cursor.coladd > 0
|| *ml_get_cursor() == NUL
|| *ml_get_cursor() == TAB))
curwin->w_cursor.coladd++;
else if (*ml_get_cursor() != NUL)
2004-06-13 20:20:40 +00:00
inc_cursor();
break;
}
if (curwin->w_cursor.coladd && cap->cmdchar != 'A')
{
int save_State = State;
// Pretend Insert mode here to allow the cursor on the
// character past the end of the line
State = MODE_INSERT;
2004-06-13 20:20:40 +00:00
coladvance(getviscol());
State = save_State;
}
invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
}
else if (cap->cmdchar == K_PS)
// drop the pasted text
bracketed_paste(PASTE_INSERT, TRUE, NULL);
2004-06-13 20:20:40 +00:00
}
/*
* Invoke edit() and take care of "restart_edit" and the return value.
*/
static void
invoke_edit(
cmdarg_T *cap,
int repl, // "r" or "gr" command
int cmd,
int startln)
2004-06-13 20:20:40 +00:00
{
int restart_edit_save = 0;
// Complicated: When the user types "a<C-O>a" we don't want to do Insert
// mode recursively. But when doing "a<C-O>." or "a<C-O>rx" we do allow
// it.
2004-06-13 20:20:40 +00:00
if (repl || !stuff_empty())
restart_edit_save = restart_edit;
else
restart_edit_save = 0;
// Always reset "restart_edit", this is not a restarted edit.
2004-06-13 20:20:40 +00:00
restart_edit = 0;
// Reset Changedtick_i, so that TextChangedI will only be triggered for stuff
// from insert mode, for 'o/O' this has already been done in n_opencmd
if (cap->cmdchar != 'O' && cap->cmdchar != 'o')
curbuf->b_last_changedtick_i = CHANGEDTICK(curbuf);
2004-06-13 20:20:40 +00:00
if (edit(cmd, startln, cap->count1))
cap->retval |= CA_COMMAND_BUSY;
if (restart_edit == 0)
restart_edit = restart_edit_save;
}
/*
* "a" or "i" while an operator is pending or in Visual mode: object motion.
*/
static void
nv_object(
cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int flag;
int include;
char_u *mps_save;
if (cap->cmdchar == 'i')
include = FALSE; // "ix" = inner object: exclude white space
2004-06-13 20:20:40 +00:00
else
include = TRUE; // "ax" = an object: include white space
2004-06-13 20:20:40 +00:00
// Make sure (), [], {} and <> are in 'matchpairs'
2004-06-13 20:20:40 +00:00
mps_save = curbuf->b_p_mps;
curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>";
switch (cap->nchar)
{
case 'w': // "aw" = a word
2004-06-13 20:20:40 +00:00
flag = current_word(cap->oap, cap->count1, include, FALSE);
break;
case 'W': // "aW" = a WORD
2004-06-13 20:20:40 +00:00
flag = current_word(cap->oap, cap->count1, include, TRUE);
break;
case 'b': // "ab" = a braces block
2004-06-13 20:20:40 +00:00
case '(':
case ')':
flag = current_block(cap->oap, cap->count1, include, '(', ')');
break;
case 'B': // "aB" = a Brackets block
2004-06-13 20:20:40 +00:00
case '{':
case '}':
flag = current_block(cap->oap, cap->count1, include, '{', '}');
break;
case '[': // "a[" = a [] block
2004-06-13 20:20:40 +00:00
case ']':
flag = current_block(cap->oap, cap->count1, include, '[', ']');
break;
case '<': // "a<" = a <> block
2004-06-13 20:20:40 +00:00
case '>':
flag = current_block(cap->oap, cap->count1, include, '<', '>');
break;
#ifdef FEAT_EVAL
case 't': // "at" = a tag block (xml and html)
// Do not adjust oap->end in do_pending_operator()
// otherwise there are different results for 'dit'
// (note leading whitespace in last line):
// 1) <b> 2) <b>
// foobar foobar
// </b> </b>
cap->retval |= CA_NO_ADJ_OP_END;
2005-07-19 22:10:03 +00:00
flag = current_tagblock(cap->oap, cap->count1, include);
break;
#endif
case 'p': // "ap" = a paragraph
2004-06-13 20:20:40 +00:00
flag = current_par(cap->oap, cap->count1, include, 'p');
break;
case 's': // "as" = a sentence
2004-06-13 20:20:40 +00:00
flag = current_sent(cap->oap, cap->count1, include);
break;
case '"': // "a"" = a double quoted string
case '\'': // "a'" = a single quoted string
case '`': // "a`" = a backtick quoted string
2004-07-02 15:38:35 +00:00
flag = current_quote(cap->oap, cap->count1, include,
cap->nchar);
break;
#if 0 // TODO
case 'S': // "aS" = a section
case 'f': // "af" = a filename
case 'u': // "au" = a URL
2004-06-13 20:20:40 +00:00
#endif
default:
flag = FAIL;
break;
}
curbuf->b_p_mps = mps_save;
if (flag == FAIL)
clearopbeep(cap->oap);
adjust_cursor_col();
curwin->w_set_curswant = TRUE;
}
/*
* "q" command: Start/stop recording.
* "q:", "q/", "q?": edit command-line in command-line window.
*/
static void
nv_record(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (cap->oap->op_type == OP_FORMAT)
{
// "gqq" is the same as "gqgq": format line
2004-06-13 20:20:40 +00:00
cap->cmdchar = 'g';
cap->nchar = 'q';
nv_operator(cap);
return;
2004-06-13 20:20:40 +00:00
}
if (checkclearop(cap->oap))
return;
if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?')
2004-06-13 20:20:40 +00:00
{
if (cmdwin_type != 0)
2004-06-13 20:20:40 +00:00
{
emsg(_(e_cmdline_window_already_open));
return;
2004-06-13 20:20:40 +00:00
}
stuffcharReadbuff(cap->nchar);
stuffcharReadbuff(K_CMDWIN);
2004-06-13 20:20:40 +00:00
}
else
// (stop) recording into a named register, unless executing a
// register
if (reg_executing == 0 && do_record(cap->nchar) == FAIL)
clearopbeep(cap->oap);
2004-06-13 20:20:40 +00:00
}
/*
* Handle the "@r" command.
*/
static void
nv_at(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (checkclearop(cap->oap))
return;
#ifdef FEAT_EVAL
if (cap->nchar == '=')
{
if (get_expr_register() == NUL)
return;
}
#endif
while (cap->count1-- && !got_int)
{
2006-11-07 17:43:47 +00:00
if (do_execreg(cap->nchar, FALSE, FALSE, FALSE) == FAIL)
2004-06-13 20:20:40 +00:00
{
clearopbeep(cap->oap);
break;
}
line_breakcheck();
}
}
/*
* Handle the CTRL-U and CTRL-D commands.
*/
static void
nv_halfpage(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
int dir = cap->cmdchar == Ctrl_D ? FORWARD : BACKWARD;
if (!checkclearop(cap->oap))
pagescroll(dir, cap->count0, TRUE);
2004-06-13 20:20:40 +00:00
}
/*
* Handle "J" or "gJ" command.
*/
static void
nv_join(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
if (VIsual_active) // join the visual lines
{
2004-06-13 20:20:40 +00:00
nv_operator(cap);
return;
}
if (checkclearop(cap->oap))
return;
if (cap->count0 <= 1)
cap->count0 = 2; // default for join is two lines!
if (curwin->w_cursor.lnum + cap->count0 - 1 >
curbuf->b_ml.ml_line_count)
2004-06-13 20:20:40 +00:00
{
// can't join when on the last line
if (cap->count0 <= 2)
2004-06-13 20:20:40 +00:00
{
clearopbeep(cap->oap);
return;
2004-06-13 20:20:40 +00:00
}
cap->count0 = curbuf->b_ml.ml_line_count
- curwin->w_cursor.lnum + 1;
2004-06-13 20:20:40 +00:00
}
prep_redo(cap->oap->regname, cap->count0,
NUL, cap->cmdchar, NUL, NUL, cap->nchar);
(void)do_join(cap->count0, cap->nchar == NUL, TRUE, TRUE, TRUE);
2004-06-13 20:20:40 +00:00
}
/*
* "P", "gP", "p" and "gp" commands.
*/
static void
nv_put(cmdarg_T *cap)
{
nv_put_opt(cap, FALSE);
}
/*
* "P", "gP", "p" and "gp" commands.
* "fix_indent" is TRUE for "[p", "[P", "]p" and "]P".
*/
static void
nv_put_opt(cmdarg_T *cap, int fix_indent)
2004-06-13 20:20:40 +00:00
{
int regname = 0;
void *reg1 = NULL, *reg2 = NULL;
2005-01-08 16:04:29 +00:00
int empty = FALSE;
2005-04-15 21:00:38 +00:00
int was_visual = FALSE;
2004-06-13 20:20:40 +00:00
int dir;
int flags = 0;
int keep_registers = FALSE;
#ifdef FEAT_FOLDING
int save_fen = curwin->w_p_fen;
#endif
2004-06-13 20:20:40 +00:00
if (cap->oap->op_type != OP_NOP)
{
#ifdef FEAT_DIFF
// "dp" is ":diffput"
2004-06-13 20:20:40 +00:00
if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'p')
{
clearop(cap->oap);
nv_diffgetput(TRUE, cap->opcount);
2004-06-13 20:20:40 +00:00
}
else
#endif
clearopbeep(cap->oap);
return;
2004-06-13 20:20:40 +00:00
}
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf) && !prompt_curpos_editable())
{
clearopbeep(cap->oap);
return;
}
#endif
if (fix_indent)
2004-06-13 20:20:40 +00:00
{
dir = (cap->cmdchar == ']' && cap->nchar == 'p')
? FORWARD : BACKWARD;
flags |= PUT_FIXINDENT;
}
else
dir = (cap->cmdchar == 'P'
|| ((cap->cmdchar == 'g' || cap->cmdchar == 'z')
&& cap->nchar == 'P')) ? BACKWARD : FORWARD;
prep_redo_cmd(cap);
if (cap->cmdchar == 'g')
flags |= PUT_CURSEND;
else if (cap->cmdchar == 'z')
flags |= PUT_BLOCK_INNER;
2004-06-13 20:20:40 +00:00
if (VIsual_active)
{
// Putting in Visual mode: The put text replaces the selected
// text. First delete the selected text, then put the new text.
// Need to save and restore the registers that the delete
// overwrites if the old contents is being put.
was_visual = TRUE;
regname = cap->oap->regname;
keep_registers = cap->cmdchar == 'P';
#ifdef FEAT_CLIPBOARD
adjust_clip_reg(&regname);
#endif
if (regname == 0 || regname == '"'
|| VIM_ISDIGIT(regname) || regname == '-'
#ifdef FEAT_CLIPBOARD
|| (clip_unnamed && (regname == '*' || regname == '+'))
#endif
2004-06-13 20:20:40 +00:00
)
2005-04-15 21:00:38 +00:00
{
// The delete is going to overwrite the register we want to
// put, save it first.
reg1 = get_register(regname, TRUE);
2005-04-15 21:00:38 +00:00
}
#ifdef FEAT_FOLDING
// Temporarily disable folding, as deleting a fold marker may cause
// the cursor to be included in a fold.
curwin->w_p_fen = FALSE;
#endif
// Now delete the selected text. Avoid messages here.
cap->cmdchar = 'd';
cap->nchar = NUL;
cap->oap->regname = keep_registers ? '_' : NUL;
++msg_silent;
nv_operator(cap);
do_pending_operator(cap, 0, FALSE);
empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
--msg_silent;
// delete PUT_LINE_BACKWARD;
cap->oap->regname = regname;
if (reg1 != NULL)
{
// Delete probably changed the register we want to put, save
// it first. Then put back what was there before the delete.
reg2 = get_register(regname, FALSE);
put_register(regname, reg1);
}
// When deleted a linewise Visual area, put the register as
// lines to avoid it joined with the next line. When deletion was
// characterwise, split a line when putting lines.
if (VIsual_mode == 'V')
flags |= PUT_LINE;
else if (VIsual_mode == 'v')
flags |= PUT_LINE_SPLIT;
if (VIsual_mode == Ctrl_V && dir == FORWARD)
flags |= PUT_LINE_FORWARD;
dir = BACKWARD;
if ((VIsual_mode != 'V'
&& curwin->w_cursor.col < curbuf->b_op_start.col)
|| (VIsual_mode == 'V'
&& curwin->w_cursor.lnum < curbuf->b_op_start.lnum))
// cursor is at the end of the line or end of file, put
// forward.
dir = FORWARD;
// May have been reset in do_put().
VIsual_active = TRUE;
}
do_put(cap->oap->regname, NULL, dir, cap->count1, flags);
// If a register was saved, put it back now.
if (reg2 != NULL)
put_register(regname, reg2);
if (was_visual)
{
#ifdef FEAT_FOLDING
if (save_fen)
curwin->w_p_fen = TRUE;
#endif
// What to reselect with "gv"? Selecting the just put text seems to
// be the most useful, since the original text was removed.
curbuf->b_visual.vi_start = curbuf->b_op_start;
curbuf->b_visual.vi_end = curbuf->b_op_end;
// need to adjust cursor position
if (*p_sel == 'e')
inc(&curbuf->b_visual.vi_end);
}
// When all lines were selected and deleted do_put() leaves an empty
// line that needs to be deleted now.
if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL)
{
ml_delete_flags(curbuf->b_ml.ml_line_count, ML_DEL_MESSAGE);
deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
// If the cursor was in that line, move it to the end of the last
// line.
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
2006-03-29 21:06:37 +00:00
{
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
coladvance((colnr_T)MAXCOL);
2006-03-29 21:06:37 +00:00
}
2004-06-13 20:20:40 +00:00
}
auto_format(FALSE, TRUE);
2004-06-13 20:20:40 +00:00
}
/*
* "o" and "O" commands.
*/
static void
nv_open(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
#ifdef FEAT_DIFF
// "do" is ":diffget"
2004-06-13 20:20:40 +00:00
if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'o')
{
clearop(cap->oap);
nv_diffgetput(FALSE, cap->opcount);
2004-06-13 20:20:40 +00:00
}
else
#endif
if (VIsual_active) // switch start and end of visual
2004-06-13 20:20:40 +00:00
v_swap_corners(cap->cmdchar);
#ifdef FEAT_JOB_CHANNEL
else if (bt_prompt(curbuf))
clearopbeep(cap->oap);
#endif
2004-06-13 20:20:40 +00:00
else
n_opencmd(cap);
}
#ifdef FEAT_NETBEANS_INTG
static void
nv_nbcmd(cmdarg_T *cap)
2004-06-13 20:20:40 +00:00
{
netbeans_keycommand(cap->nchar);
}
#endif
#ifdef FEAT_DND
static void
nv_drop(cmdarg_T *cap UNUSED)
2004-06-13 20:20:40 +00:00
{
do_put('~', NULL, BACKWARD, 1L, PUT_CURSEND);
2004-06-13 20:20:40 +00:00
}
#endif
2005-03-15 22:34:55 +00:00
/*
* Trigger CursorHold event.
* When waiting for a character for 'updatetime' K_CURSORHOLD is put in the
* input buffer. "did_cursorhold" is set to avoid retriggering.
*/
static void
nv_cursorhold(cmdarg_T *cap)
2005-03-15 22:34:55 +00:00
{
apply_autocmds(EVENT_CURSORHOLD, NULL, NULL, FALSE, curbuf);
did_cursorhold = TRUE;
cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
2005-03-15 22:34:55 +00:00
}