blob: ca2a48c089c72b60135d707271cd394aec4b58b0 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9/*
10 * normal.c: Contains the main routine for processing characters in command
11 * mode. Communicates closely with the code in ops.c to handle
12 * the operators.
13 */
14
15#include "vim.h"
16
Bram Moolenaar792cf5e2019-09-30 23:12:16 +020017static int VIsual_mode_orig = NUL; // saved Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +000018static int restart_VIsual_select = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000019
Bram Moolenaarf82a2d22010-12-17 18:53:01 +010020#ifdef FEAT_EVAL
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010021static void set_vcount_ca(cmdarg_T *cap, int *set_prevcount);
Bram Moolenaarf82a2d22010-12-17 18:53:01 +010022#endif
Bram Moolenaareae1b912019-05-09 15:12:55 +020023static int nv_compare(const void *s1, const void *s2);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010024static void unshift_special(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000025#ifdef FEAT_CMDL_INFO
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010026static void del_from_showcmd(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +000027#endif
28
29/*
30 * nv_*(): functions called to handle Normal and Visual mode commands.
31 * n_*(): functions called to handle Normal mode commands.
32 * v_*(): functions called to handle Visual mode commands.
33 */
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010034static void nv_ignore(cmdarg_T *cap);
35static void nv_nop(cmdarg_T *cap);
36static void nv_error(cmdarg_T *cap);
37static void nv_help(cmdarg_T *cap);
38static void nv_addsub(cmdarg_T *cap);
39static void nv_page(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010040static void nv_zet(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000041#ifdef FEAT_GUI
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010042static void nv_ver_scrollbar(cmdarg_T *cap);
43static void nv_hor_scrollbar(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000044#endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +000045#ifdef FEAT_GUI_TABLINE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010046static void nv_tabline(cmdarg_T *cap);
47static void nv_tabmenu(cmdarg_T *cap);
Bram Moolenaar32466aa2006-02-24 23:53:04 +000048#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010049static void nv_exmode(cmdarg_T *cap);
50static void nv_colon(cmdarg_T *cap);
51static void nv_ctrlg(cmdarg_T *cap);
52static void nv_ctrlh(cmdarg_T *cap);
53static void nv_clear(cmdarg_T *cap);
54static void nv_ctrlo(cmdarg_T *cap);
55static void nv_hat(cmdarg_T *cap);
56static void nv_Zet(cmdarg_T *cap);
57static void nv_ident(cmdarg_T *cap);
58static void nv_tagpop(cmdarg_T *cap);
59static void nv_scroll(cmdarg_T *cap);
60static void nv_right(cmdarg_T *cap);
61static void nv_left(cmdarg_T *cap);
62static void nv_up(cmdarg_T *cap);
63static void nv_down(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010064static void nv_end(cmdarg_T *cap);
65static void nv_dollar(cmdarg_T *cap);
66static void nv_search(cmdarg_T *cap);
67static void nv_next(cmdarg_T *cap);
Bram Moolenaar92ea26b2019-10-18 20:53:34 +020068static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrapped);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010069static void nv_csearch(cmdarg_T *cap);
70static void nv_brackets(cmdarg_T *cap);
71static void nv_percent(cmdarg_T *cap);
72static void nv_brace(cmdarg_T *cap);
73static void nv_mark(cmdarg_T *cap);
74static void nv_findpar(cmdarg_T *cap);
75static void nv_undo(cmdarg_T *cap);
76static void nv_kundo(cmdarg_T *cap);
77static void nv_Replace(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010078static void nv_replace(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010079static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos);
80static void v_visop(cmdarg_T *cap);
81static void nv_subst(cmdarg_T *cap);
82static void nv_abbrev(cmdarg_T *cap);
83static void nv_optrans(cmdarg_T *cap);
84static void nv_gomark(cmdarg_T *cap);
85static void nv_pcmark(cmdarg_T *cap);
86static void nv_regname(cmdarg_T *cap);
87static void nv_visual(cmdarg_T *cap);
88static void n_start_visual_mode(int c);
89static void nv_window(cmdarg_T *cap);
90static void nv_suspend(cmdarg_T *cap);
91static void nv_g_cmd(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010092static void nv_dot(cmdarg_T *cap);
93static void nv_redo(cmdarg_T *cap);
94static void nv_Undo(cmdarg_T *cap);
95static void nv_tilde(cmdarg_T *cap);
96static void nv_operator(cmdarg_T *cap);
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +000097#ifdef FEAT_EVAL
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010098static void set_op_var(int optype);
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +000099#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100100static void nv_lineop(cmdarg_T *cap);
101static void nv_home(cmdarg_T *cap);
102static void nv_pipe(cmdarg_T *cap);
103static void nv_bck_word(cmdarg_T *cap);
104static void nv_wordcmd(cmdarg_T *cap);
105static void nv_beginline(cmdarg_T *cap);
106static void adjust_cursor(oparg_T *oap);
107static void adjust_for_sel(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100108static void nv_select(cmdarg_T *cap);
109static void nv_goto(cmdarg_T *cap);
110static void nv_normal(cmdarg_T *cap);
111static void nv_esc(cmdarg_T *oap);
112static void nv_edit(cmdarg_T *cap);
113static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#ifdef FEAT_TEXTOBJ
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100115static void nv_object(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100117static void nv_record(cmdarg_T *cap);
118static void nv_at(cmdarg_T *cap);
119static void nv_halfpage(cmdarg_T *cap);
120static void nv_join(cmdarg_T *cap);
121static void nv_put(cmdarg_T *cap);
Bram Moolenaar0ab190c2019-05-23 23:27:36 +0200122static void nv_put_opt(cmdarg_T *cap, int fix_indent);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100123static void nv_open(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000124#ifdef FEAT_NETBEANS_INTG
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100125static void nv_nbcmd(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000126#endif
127#ifdef FEAT_DND
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100128static void nv_drop(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000129#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100130static void nv_cursorhold(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000131
Bram Moolenaar9fd01c62008-11-01 12:52:38 +0000132static char *e_noident = N_("E349: No identifier under cursor");
133
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134/*
135 * Function to be called for a Normal or Visual mode command.
136 * The argument is a cmdarg_T.
137 */
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100138typedef void (*nv_func_T)(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000139
140/* Values for cmd_flags. */
141#define NV_NCH 0x01 /* may need to get a second char */
142#define NV_NCH_NOP (0x02|NV_NCH) /* get second char when no operator pending */
143#define NV_NCH_ALW (0x04|NV_NCH) /* always get a second char */
144#define NV_LANG 0x08 /* second char needs language adjustment */
145
146#define NV_SS 0x10 /* may start selection */
147#define NV_SSS 0x20 /* may start selection with shift modifier */
148#define NV_STS 0x40 /* may stop selection without shift modif. */
149#define NV_RL 0x80 /* 'rightleft' modifies command */
150#define NV_KEEPREG 0x100 /* don't clear regname */
151#define NV_NCW 0x200 /* not allowed in command-line window */
152
153/*
154 * Generally speaking, every Normal mode command should either clear any
155 * pending operator (with *clearop*()), or set the motion type variable
156 * oap->motion_type.
157 *
158 * When a cursor motion command is made, it is marked as being a character or
159 * line oriented motion. Then, if an operator is in effect, the operation
160 * becomes character or line oriented accordingly.
161 */
162
163/*
164 * This table contains one entry for every Normal or Visual mode command.
165 * The order doesn't matter, init_normal_cmds() will create a sorted index.
166 * It is faster when all keys from zero to '~' are present.
167 */
168static const struct nv_cmd
169{
170 int cmd_char; /* (first) command character */
171 nv_func_T cmd_func; /* function for this command */
172 short_u cmd_flags; /* NV_ flags */
173 short cmd_arg; /* value for ca.arg */
174} nv_cmds[] =
175{
176 {NUL, nv_error, 0, 0},
177 {Ctrl_A, nv_addsub, 0, 0},
178 {Ctrl_B, nv_page, NV_STS, BACKWARD},
179 {Ctrl_C, nv_esc, 0, TRUE},
180 {Ctrl_D, nv_halfpage, 0, 0},
181 {Ctrl_E, nv_scroll_line, 0, TRUE},
182 {Ctrl_F, nv_page, NV_STS, FORWARD},
183 {Ctrl_G, nv_ctrlg, 0, 0},
184 {Ctrl_H, nv_ctrlh, 0, 0},
185 {Ctrl_I, nv_pcmark, 0, 0},
186 {NL, nv_down, 0, FALSE},
187 {Ctrl_K, nv_error, 0, 0},
188 {Ctrl_L, nv_clear, 0, 0},
Bram Moolenaar2c519cf2019-03-21 21:45:34 +0100189 {CAR, nv_down, 0, TRUE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190 {Ctrl_N, nv_down, NV_STS, FALSE},
191 {Ctrl_O, nv_ctrlo, 0, 0},
192 {Ctrl_P, nv_up, NV_STS, FALSE},
Bram Moolenaardf177f62005-02-22 08:39:57 +0000193 {Ctrl_Q, nv_visual, 0, FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194 {Ctrl_R, nv_redo, 0, 0},
195 {Ctrl_S, nv_ignore, 0, 0},
196 {Ctrl_T, nv_tagpop, NV_NCW, 0},
197 {Ctrl_U, nv_halfpage, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198 {Ctrl_V, nv_visual, 0, FALSE},
199 {'V', nv_visual, 0, FALSE},
200 {'v', nv_visual, 0, FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000201 {Ctrl_W, nv_window, 0, 0},
202 {Ctrl_X, nv_addsub, 0, 0},
203 {Ctrl_Y, nv_scroll_line, 0, FALSE},
204 {Ctrl_Z, nv_suspend, 0, 0},
205 {ESC, nv_esc, 0, FALSE},
206 {Ctrl_BSL, nv_normal, NV_NCH_ALW, 0},
207 {Ctrl_RSB, nv_ident, NV_NCW, 0},
208 {Ctrl_HAT, nv_hat, NV_NCW, 0},
209 {Ctrl__, nv_error, 0, 0},
210 {' ', nv_right, 0, 0},
211 {'!', nv_operator, 0, 0},
212 {'"', nv_regname, NV_NCH_NOP|NV_KEEPREG, 0},
213 {'#', nv_ident, 0, 0},
214 {'$', nv_dollar, 0, 0},
215 {'%', nv_percent, 0, 0},
216 {'&', nv_optrans, 0, 0},
217 {'\'', nv_gomark, NV_NCH_ALW, TRUE},
218 {'(', nv_brace, 0, BACKWARD},
219 {')', nv_brace, 0, FORWARD},
220 {'*', nv_ident, 0, 0},
221 {'+', nv_down, 0, TRUE},
222 {',', nv_csearch, 0, TRUE},
223 {'-', nv_up, 0, TRUE},
224 {'.', nv_dot, NV_KEEPREG, 0},
225 {'/', nv_search, 0, FALSE},
226 {'0', nv_beginline, 0, 0},
227 {'1', nv_ignore, 0, 0},
228 {'2', nv_ignore, 0, 0},
229 {'3', nv_ignore, 0, 0},
230 {'4', nv_ignore, 0, 0},
231 {'5', nv_ignore, 0, 0},
232 {'6', nv_ignore, 0, 0},
233 {'7', nv_ignore, 0, 0},
234 {'8', nv_ignore, 0, 0},
235 {'9', nv_ignore, 0, 0},
236 {':', nv_colon, 0, 0},
237 {';', nv_csearch, 0, FALSE},
238 {'<', nv_operator, NV_RL, 0},
239 {'=', nv_operator, 0, 0},
240 {'>', nv_operator, NV_RL, 0},
241 {'?', nv_search, 0, FALSE},
242 {'@', nv_at, NV_NCH_NOP, FALSE},
243 {'A', nv_edit, 0, 0},
244 {'B', nv_bck_word, 0, 1},
245 {'C', nv_abbrev, NV_KEEPREG, 0},
246 {'D', nv_abbrev, NV_KEEPREG, 0},
247 {'E', nv_wordcmd, 0, TRUE},
248 {'F', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD},
249 {'G', nv_goto, 0, TRUE},
250 {'H', nv_scroll, 0, 0},
251 {'I', nv_edit, 0, 0},
252 {'J', nv_join, 0, 0},
253 {'K', nv_ident, 0, 0},
254 {'L', nv_scroll, 0, 0},
255 {'M', nv_scroll, 0, 0},
256 {'N', nv_next, 0, SEARCH_REV},
257 {'O', nv_open, 0, 0},
258 {'P', nv_put, 0, 0},
259 {'Q', nv_exmode, NV_NCW, 0},
260 {'R', nv_Replace, 0, FALSE},
261 {'S', nv_subst, NV_KEEPREG, 0},
262 {'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD},
263 {'U', nv_Undo, 0, 0},
264 {'W', nv_wordcmd, 0, TRUE},
265 {'X', nv_abbrev, NV_KEEPREG, 0},
266 {'Y', nv_abbrev, NV_KEEPREG, 0},
267 {'Z', nv_Zet, NV_NCH_NOP|NV_NCW, 0},
268 {'[', nv_brackets, NV_NCH_ALW, BACKWARD},
269 {'\\', nv_error, 0, 0},
270 {']', nv_brackets, NV_NCH_ALW, FORWARD},
271 {'^', nv_beginline, 0, BL_WHITE | BL_FIX},
272 {'_', nv_lineop, 0, 0},
273 {'`', nv_gomark, NV_NCH_ALW, FALSE},
274 {'a', nv_edit, NV_NCH, 0},
275 {'b', nv_bck_word, 0, 0},
276 {'c', nv_operator, 0, 0},
277 {'d', nv_operator, 0, 0},
278 {'e', nv_wordcmd, 0, FALSE},
279 {'f', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD},
280 {'g', nv_g_cmd, NV_NCH_ALW, FALSE},
281 {'h', nv_left, NV_RL, 0},
282 {'i', nv_edit, NV_NCH, 0},
283 {'j', nv_down, 0, FALSE},
284 {'k', nv_up, 0, FALSE},
285 {'l', nv_right, NV_RL, 0},
286 {'m', nv_mark, NV_NCH_NOP, 0},
287 {'n', nv_next, 0, 0},
288 {'o', nv_open, 0, 0},
289 {'p', nv_put, 0, 0},
290 {'q', nv_record, NV_NCH, 0},
291 {'r', nv_replace, NV_NCH_NOP|NV_LANG, 0},
292 {'s', nv_subst, NV_KEEPREG, 0},
293 {'t', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD},
294 {'u', nv_undo, 0, 0},
295 {'w', nv_wordcmd, 0, FALSE},
296 {'x', nv_abbrev, NV_KEEPREG, 0},
297 {'y', nv_operator, 0, 0},
298 {'z', nv_zet, NV_NCH_ALW, 0},
299 {'{', nv_findpar, 0, BACKWARD},
300 {'|', nv_pipe, 0, 0},
301 {'}', nv_findpar, 0, FORWARD},
302 {'~', nv_tilde, 0, 0},
303
304 /* pound sign */
305 {POUND, nv_ident, 0, 0},
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +0200306 {K_MOUSEUP, nv_mousescroll, 0, MSCR_UP},
307 {K_MOUSEDOWN, nv_mousescroll, 0, MSCR_DOWN},
308 {K_MOUSELEFT, nv_mousescroll, 0, MSCR_LEFT},
309 {K_MOUSERIGHT, nv_mousescroll, 0, MSCR_RIGHT},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000310 {K_LEFTMOUSE, nv_mouse, 0, 0},
311 {K_LEFTMOUSE_NM, nv_mouse, 0, 0},
312 {K_LEFTDRAG, nv_mouse, 0, 0},
313 {K_LEFTRELEASE, nv_mouse, 0, 0},
314 {K_LEFTRELEASE_NM, nv_mouse, 0, 0},
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100315 {K_MOUSEMOVE, nv_mouse, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316 {K_MIDDLEMOUSE, nv_mouse, 0, 0},
317 {K_MIDDLEDRAG, nv_mouse, 0, 0},
318 {K_MIDDLERELEASE, nv_mouse, 0, 0},
319 {K_RIGHTMOUSE, nv_mouse, 0, 0},
320 {K_RIGHTDRAG, nv_mouse, 0, 0},
321 {K_RIGHTRELEASE, nv_mouse, 0, 0},
322 {K_X1MOUSE, nv_mouse, 0, 0},
323 {K_X1DRAG, nv_mouse, 0, 0},
324 {K_X1RELEASE, nv_mouse, 0, 0},
325 {K_X2MOUSE, nv_mouse, 0, 0},
326 {K_X2DRAG, nv_mouse, 0, 0},
327 {K_X2RELEASE, nv_mouse, 0, 0},
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000328 {K_IGNORE, nv_ignore, NV_KEEPREG, 0},
Bram Moolenaarebefac62005-12-28 22:39:57 +0000329 {K_NOP, nv_nop, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000330 {K_INS, nv_edit, 0, 0},
331 {K_KINS, nv_edit, 0, 0},
332 {K_BS, nv_ctrlh, 0, 0},
333 {K_UP, nv_up, NV_SSS|NV_STS, FALSE},
334 {K_S_UP, nv_page, NV_SS, BACKWARD},
335 {K_DOWN, nv_down, NV_SSS|NV_STS, FALSE},
336 {K_S_DOWN, nv_page, NV_SS, FORWARD},
337 {K_LEFT, nv_left, NV_SSS|NV_STS|NV_RL, 0},
338 {K_S_LEFT, nv_bck_word, NV_SS|NV_RL, 0},
339 {K_C_LEFT, nv_bck_word, NV_SSS|NV_RL|NV_STS, 1},
340 {K_RIGHT, nv_right, NV_SSS|NV_STS|NV_RL, 0},
341 {K_S_RIGHT, nv_wordcmd, NV_SS|NV_RL, FALSE},
342 {K_C_RIGHT, nv_wordcmd, NV_SSS|NV_RL|NV_STS, TRUE},
343 {K_PAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD},
344 {K_KPAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD},
345 {K_PAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD},
346 {K_KPAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD},
347 {K_END, nv_end, NV_SSS|NV_STS, FALSE},
348 {K_KEND, nv_end, NV_SSS|NV_STS, FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349 {K_S_END, nv_end, NV_SS, FALSE},
350 {K_C_END, nv_end, NV_SSS|NV_STS, TRUE},
351 {K_HOME, nv_home, NV_SSS|NV_STS, 0},
352 {K_KHOME, nv_home, NV_SSS|NV_STS, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000353 {K_S_HOME, nv_home, NV_SS, 0},
354 {K_C_HOME, nv_goto, NV_SSS|NV_STS, FALSE},
355 {K_DEL, nv_abbrev, 0, 0},
356 {K_KDEL, nv_abbrev, 0, 0},
357 {K_UNDO, nv_kundo, 0, 0},
358 {K_HELP, nv_help, NV_NCW, 0},
359 {K_F1, nv_help, NV_NCW, 0},
360 {K_XF1, nv_help, NV_NCW, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000361 {K_SELECT, nv_select, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000362#ifdef FEAT_GUI
363 {K_VER_SCROLLBAR, nv_ver_scrollbar, 0, 0},
364 {K_HOR_SCROLLBAR, nv_hor_scrollbar, 0, 0},
365#endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +0000366#ifdef FEAT_GUI_TABLINE
367 {K_TABLINE, nv_tabline, 0, 0},
Bram Moolenaarba6c0522006-02-25 21:45:02 +0000368 {K_TABMENU, nv_tabmenu, 0, 0},
Bram Moolenaar32466aa2006-02-24 23:53:04 +0000369#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370#ifdef FEAT_NETBEANS_INTG
371 {K_F21, nv_nbcmd, NV_NCH_ALW, 0},
372#endif
373#ifdef FEAT_DND
374 {K_DROP, nv_drop, NV_STS, 0},
375#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000376 {K_CURSORHOLD, nv_cursorhold, NV_KEEPREG, 0},
Bram Moolenaarec2da362017-01-21 20:04:22 +0100377 {K_PS, nv_edit, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000378};
379
380/* Number of commands in nv_cmds[]. */
381#define NV_CMDS_SIZE (sizeof(nv_cmds) / sizeof(struct nv_cmd))
382
383/* Sorted index of commands in nv_cmds[]. */
384static short nv_cmd_idx[NV_CMDS_SIZE];
385
386/* The highest index for which
387 * nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char] */
388static int nv_max_linear;
389
390/*
391 * Compare functions for qsort() below, that checks the command character
392 * through the index in nv_cmd_idx[].
393 */
394 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100395nv_compare(const void *s1, const void *s2)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396{
397 int c1, c2;
398
399 /* The commands are sorted on absolute value. */
400 c1 = nv_cmds[*(const short *)s1].cmd_char;
401 c2 = nv_cmds[*(const short *)s2].cmd_char;
402 if (c1 < 0)
403 c1 = -c1;
404 if (c2 < 0)
405 c2 = -c2;
406 return c1 - c2;
407}
408
409/*
410 * Initialize the nv_cmd_idx[] table.
411 */
412 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100413init_normal_cmds(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414{
415 int i;
416
417 /* Fill the index table with a one to one relation. */
Bram Moolenaar78a15312009-05-15 19:33:18 +0000418 for (i = 0; i < (int)NV_CMDS_SIZE; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419 nv_cmd_idx[i] = i;
420
421 /* Sort the commands by the command character. */
422 qsort((void *)&nv_cmd_idx, (size_t)NV_CMDS_SIZE, sizeof(short), nv_compare);
423
424 /* Find the first entry that can't be indexed by the command character. */
Bram Moolenaar78a15312009-05-15 19:33:18 +0000425 for (i = 0; i < (int)NV_CMDS_SIZE; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426 if (i != nv_cmds[nv_cmd_idx[i]].cmd_char)
427 break;
428 nv_max_linear = i - 1;
429}
430
431/*
432 * Search for a command in the commands table.
433 * Returns -1 for invalid command.
434 */
435 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100436find_command(int cmdchar)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000437{
438 int i;
439 int idx;
440 int top, bot;
441 int c;
442
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443 /* A multi-byte character is never a command. */
444 if (cmdchar >= 0x100)
445 return -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000446
447 /* We use the absolute value of the character. Special keys have a
448 * negative value, but are sorted on their absolute value. */
449 if (cmdchar < 0)
450 cmdchar = -cmdchar;
451
452 /* If the character is in the first part: The character is the index into
453 * nv_cmd_idx[]. */
454 if (cmdchar <= nv_max_linear)
455 return nv_cmd_idx[cmdchar];
456
457 /* Perform a binary search. */
458 bot = nv_max_linear + 1;
459 top = NV_CMDS_SIZE - 1;
460 idx = -1;
461 while (bot <= top)
462 {
463 i = (top + bot) / 2;
464 c = nv_cmds[nv_cmd_idx[i]].cmd_char;
465 if (c < 0)
466 c = -c;
467 if (cmdchar == c)
468 {
469 idx = nv_cmd_idx[i];
470 break;
471 }
472 if (cmdchar > c)
473 bot = i + 1;
474 else
475 top = i - 1;
476 }
477 return idx;
478}
479
480/*
481 * Execute a command in Normal mode.
482 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100484normal_cmd(
485 oparg_T *oap,
486 int toplevel UNUSED) /* TRUE when called from main() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000488 cmdarg_T ca; /* command arguments */
489 int c;
490 int ctrl_w = FALSE; /* got CTRL-W command */
491 int old_col = curwin->w_curswant;
492#ifdef FEAT_CMDL_INFO
493 int need_flushbuf; /* need to call out_flush() */
494#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000495 pos_T old_pos; /* cursor position before command */
496 int mapped_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 static int old_mapped_len = 0;
498 int idx;
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000499#ifdef FEAT_EVAL
500 int set_prevcount = FALSE;
501#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502
503 vim_memset(&ca, 0, sizeof(ca)); /* also resets ca.retval */
504 ca.oap = oap;
Bram Moolenaara983fe92008-07-31 20:04:27 +0000505
506 /* Use a count remembered from before entering an operator. After typing
507 * "3d" we return from normal_cmd() and come back here, the "3" is
508 * remembered in "opcount". */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000509 ca.opcount = opcount;
510
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511 /*
512 * If there is an operator pending, then the command we take this time
513 * will terminate it. Finish_op tells us to finish the operation before
514 * returning this time (unless the operation was cancelled).
515 */
516#ifdef CURSOR_SHAPE
517 c = finish_op;
518#endif
519 finish_op = (oap->op_type != OP_NOP);
520#ifdef CURSOR_SHAPE
521 if (finish_op != c)
522 {
523 ui_cursor_shape(); /* may show different cursor shape */
524# ifdef FEAT_MOUSESHAPE
525 update_mouseshape(-1);
526# endif
527 }
528#endif
529
Bram Moolenaara983fe92008-07-31 20:04:27 +0000530 /* When not finishing an operator and no register name typed, reset the
531 * count. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000532 if (!finish_op && !oap->regname)
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000533 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534 ca.opcount = 0;
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000535#ifdef FEAT_EVAL
536 set_prevcount = TRUE;
537#endif
538 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539
Bram Moolenaara983fe92008-07-31 20:04:27 +0000540 /* Restore counts from before receiving K_CURSORHOLD. This means after
541 * typing "3", handling K_CURSORHOLD and then typing "2" we get "32", not
542 * "3 * 2". */
543 if (oap->prev_opcount > 0 || oap->prev_count0 > 0)
544 {
545 ca.opcount = oap->prev_opcount;
546 ca.count0 = oap->prev_count0;
547 oap->prev_opcount = 0;
548 oap->prev_count0 = 0;
549 }
Bram Moolenaara983fe92008-07-31 20:04:27 +0000550
Bram Moolenaar071d4272004-06-13 20:20:40 +0000551 mapped_len = typebuf_maplen();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000552
553 State = NORMAL_BUSY;
554#ifdef USE_ON_FLY_SCROLL
555 dont_scroll = FALSE; /* allow scrolling here */
556#endif
557
Bram Moolenaarf82a2d22010-12-17 18:53:01 +0100558#ifdef FEAT_EVAL
559 /* Set v:count here, when called from main() and not a stuffed
560 * command, so that v:count can be used in an expression mapping
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100561 * when there is no count. Do set it for redo. */
562 if (toplevel && readbuf1_empty())
Bram Moolenaarf82a2d22010-12-17 18:53:01 +0100563 set_vcount_ca(&ca, &set_prevcount);
564#endif
565
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 /*
567 * Get the command character from the user.
568 */
569 c = safe_vgetc();
Bram Moolenaar25281632016-01-21 23:32:32 +0100570 LANGMAP_ADJUST(c, get_real_state() != SELECTMODE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000571
572 /*
573 * If a mapping was started in Visual or Select mode, remember the length
574 * of the mapping. This is used below to not return to Insert mode for as
575 * long as the mapping is being executed.
576 */
577 if (restart_edit == 0)
578 old_mapped_len = 0;
579 else if (old_mapped_len
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000580 || (VIsual_active && mapped_len == 0 && typebuf_maplen() > 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 old_mapped_len = typebuf_maplen();
582
583 if (c == NUL)
584 c = K_ZERO;
585
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586 /*
587 * In Select mode, typed text replaces the selection.
588 */
589 if (VIsual_active
590 && VIsual_select
591 && (vim_isprintc(c) || c == NL || c == CAR || c == K_KENTER))
592 {
Bram Moolenaar686f51e2005-05-20 21:19:57 +0000593 /* Fake a "c"hange command. When "restart_edit" is set (e.g., because
594 * 'insertmode' is set) fake a "d"elete command, Insert mode will
595 * restart automatically.
Bram Moolenaarcf8e7d12006-12-05 20:43:17 +0000596 * Insert the typed character in the typeahead buffer, so that it can
597 * be mapped in Insert mode. Required for ":lmap" to work. */
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000598 ins_char_typebuf(c);
Bram Moolenaar686f51e2005-05-20 21:19:57 +0000599 if (restart_edit != 0)
600 c = 'd';
601 else
602 c = 'c';
Bram Moolenaarb388adb2006-02-28 23:50:17 +0000603 msg_nowait = TRUE; /* don't delay going to insert mode */
Bram Moolenaar9bad29d2013-05-21 12:46:02 +0200604 old_mapped_len = 0; /* do go to Insert mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000605 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000606
607#ifdef FEAT_CMDL_INFO
608 need_flushbuf = add_to_showcmd(c);
609#endif
610
611getcount:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612 if (!(VIsual_active && VIsual_select))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613 {
614 /*
615 * Handle a count before a command and compute ca.count0.
616 * Note that '0' is a command and not the start of a count, but it's
617 * part of a count after other digits.
618 */
619 while ( (c >= '1' && c <= '9')
620 || (ca.count0 != 0 && (c == K_DEL || c == K_KDEL || c == '0')))
621 {
622 if (c == K_DEL || c == K_KDEL)
623 {
624 ca.count0 /= 10;
625#ifdef FEAT_CMDL_INFO
626 del_from_showcmd(4); /* delete the digit and ~@% */
627#endif
628 }
629 else
630 ca.count0 = ca.count0 * 10 + (c - '0');
631 if (ca.count0 < 0) /* got too large! */
632 ca.count0 = 999999999L;
Bram Moolenaarf13249a2007-10-14 15:16:27 +0000633#ifdef FEAT_EVAL
634 /* Set v:count here, when called from main() and not a stuffed
635 * command, so that v:count can be used in an expression mapping
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100636 * right after the count. Do set it for redo. */
637 if (toplevel && readbuf1_empty())
Bram Moolenaarf82a2d22010-12-17 18:53:01 +0100638 set_vcount_ca(&ca, &set_prevcount);
Bram Moolenaarf13249a2007-10-14 15:16:27 +0000639#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 if (ctrl_w)
641 {
642 ++no_mapping;
643 ++allow_keys; /* no mapping for nchar, but keys */
644 }
645 ++no_zero_mapping; /* don't map zero here */
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000646 c = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 LANGMAP_ADJUST(c, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 --no_zero_mapping;
649 if (ctrl_w)
650 {
651 --no_mapping;
652 --allow_keys;
653 }
654#ifdef FEAT_CMDL_INFO
655 need_flushbuf |= add_to_showcmd(c);
656#endif
657 }
658
659 /*
660 * If we got CTRL-W there may be a/another count
661 */
662 if (c == Ctrl_W && !ctrl_w && oap->op_type == OP_NOP)
663 {
664 ctrl_w = TRUE;
665 ca.opcount = ca.count0; /* remember first count */
666 ca.count0 = 0;
667 ++no_mapping;
668 ++allow_keys; /* no mapping for nchar, but keys */
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000669 c = plain_vgetc(); /* get next character */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670 LANGMAP_ADJUST(c, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 --no_mapping;
672 --allow_keys;
673#ifdef FEAT_CMDL_INFO
674 need_flushbuf |= add_to_showcmd(c);
675#endif
676 goto getcount; /* jump back */
677 }
678 }
679
Bram Moolenaara983fe92008-07-31 20:04:27 +0000680 if (c == K_CURSORHOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000681 {
Bram Moolenaara983fe92008-07-31 20:04:27 +0000682 /* Save the count values so that ca.opcount and ca.count0 are exactly
683 * the same when coming back here after handling K_CURSORHOLD. */
684 oap->prev_opcount = ca.opcount;
685 oap->prev_count0 = ca.count0;
686 }
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100687 else if (ca.opcount != 0)
Bram Moolenaara983fe92008-07-31 20:04:27 +0000688 {
689 /*
690 * If we're in the middle of an operator (including after entering a
691 * yank buffer with '"') AND we had a count before the operator, then
692 * that count overrides the current value of ca.count0.
693 * What this means effectively, is that commands like "3dw" get turned
694 * into "d3w" which makes things fall into place pretty neatly.
695 * If you give a count before AND after the operator, they are
696 * multiplied.
697 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 if (ca.count0)
699 ca.count0 *= ca.opcount;
700 else
701 ca.count0 = ca.opcount;
702 }
703
704 /*
705 * Always remember the count. It will be set to zero (on the next call,
706 * above) when there is no pending operator.
707 * When called from main(), save the count for use by the "count" built-in
708 * variable.
709 */
710 ca.opcount = ca.count0;
711 ca.count1 = (ca.count0 == 0 ? 1 : ca.count0);
712
713#ifdef FEAT_EVAL
714 /*
715 * Only set v:count when called from main() and not a stuffed command.
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100716 * Do set it for redo.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 */
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100718 if (toplevel && readbuf1_empty())
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000719 set_vcount(ca.count0, ca.count1, set_prevcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000720#endif
721
722 /*
723 * Find the command character in the table of commands.
724 * For CTRL-W we already got nchar when looking for a count.
725 */
726 if (ctrl_w)
727 {
728 ca.nchar = c;
729 ca.cmdchar = Ctrl_W;
730 }
731 else
732 ca.cmdchar = c;
733 idx = find_command(ca.cmdchar);
734 if (idx < 0)
735 {
736 /* Not a known command: beep. */
737 clearopbeep(oap);
738 goto normal_end;
739 }
Bram Moolenaar05a7bb32006-01-19 22:09:32 +0000740
Bram Moolenaar2d3f4892006-01-20 23:02:51 +0000741 if (text_locked() && (nv_cmds[idx].cmd_flags & NV_NCW))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
Bram Moolenaard69bd9a2014-04-29 12:15:40 +0200743 /* This command is not allowed while editing a cmdline: beep. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000744 clearopbeep(oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +0000745 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000746 goto normal_end;
747 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000748 if ((nv_cmds[idx].cmd_flags & NV_NCW) && curbuf_locked())
749 goto normal_end;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750
Bram Moolenaar071d4272004-06-13 20:20:40 +0000751 /*
752 * In Visual/Select mode, a few keys are handled in a special way.
753 */
754 if (VIsual_active)
755 {
756 /* when 'keymodel' contains "stopsel" may stop Select/Visual mode */
757 if (km_stopsel
758 && (nv_cmds[idx].cmd_flags & NV_STS)
759 && !(mod_mask & MOD_MASK_SHIFT))
760 {
761 end_visual_mode();
762 redraw_curbuf_later(INVERTED);
763 }
764
765 /* Keys that work different when 'keymodel' contains "startsel" */
766 if (km_startsel)
767 {
768 if (nv_cmds[idx].cmd_flags & NV_SS)
769 {
770 unshift_special(&ca);
771 idx = find_command(ca.cmdchar);
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000772 if (idx < 0)
773 {
774 /* Just in case */
775 clearopbeep(oap);
776 goto normal_end;
777 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 }
779 else if ((nv_cmds[idx].cmd_flags & NV_SSS)
780 && (mod_mask & MOD_MASK_SHIFT))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 mod_mask &= ~MOD_MASK_SHIFT;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 }
783 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000784
785#ifdef FEAT_RIGHTLEFT
786 if (curwin->w_p_rl && KeyTyped && !KeyStuffed
787 && (nv_cmds[idx].cmd_flags & NV_RL))
788 {
789 /* Invert horizontal movements and operations. Only when typed by the
790 * user directly, not when the result of a mapping or "x" translated
791 * to "dl". */
792 switch (ca.cmdchar)
793 {
794 case 'l': ca.cmdchar = 'h'; break;
795 case K_RIGHT: ca.cmdchar = K_LEFT; break;
796 case K_S_RIGHT: ca.cmdchar = K_S_LEFT; break;
797 case K_C_RIGHT: ca.cmdchar = K_C_LEFT; break;
798 case 'h': ca.cmdchar = 'l'; break;
799 case K_LEFT: ca.cmdchar = K_RIGHT; break;
800 case K_S_LEFT: ca.cmdchar = K_S_RIGHT; break;
801 case K_C_LEFT: ca.cmdchar = K_C_RIGHT; break;
802 case '>': ca.cmdchar = '<'; break;
803 case '<': ca.cmdchar = '>'; break;
804 }
805 idx = find_command(ca.cmdchar);
806 }
807#endif
808
809 /*
810 * Get an additional character if we need one.
811 */
812 if ((nv_cmds[idx].cmd_flags & NV_NCH)
813 && (((nv_cmds[idx].cmd_flags & NV_NCH_NOP) == NV_NCH_NOP
814 && oap->op_type == OP_NOP)
815 || (nv_cmds[idx].cmd_flags & NV_NCH_ALW) == NV_NCH_ALW
816 || (ca.cmdchar == 'q'
817 && oap->op_type == OP_NOP
Bram Moolenaar0b6d9112018-05-22 20:35:17 +0200818 && reg_recording == 0
819 && reg_executing == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000820 || ((ca.cmdchar == 'a' || ca.cmdchar == 'i')
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +0100821 && (oap->op_type != OP_NOP || VIsual_active))))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000822 {
823 int *cp;
824 int repl = FALSE; /* get character for replace mode */
825 int lit = FALSE; /* get extra character literally */
826 int langmap_active = FALSE; /* using :lmap mappings */
827 int lang; /* getting a text character */
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100828#ifdef HAVE_INPUT_METHOD
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829 int save_smd; /* saved value of p_smd */
830#endif
831
832 ++no_mapping;
833 ++allow_keys; /* no mapping for nchar, but allow key codes */
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +0000834 /* Don't generate a CursorHold event here, most commands can't handle
835 * it, e.g., nv_replace(), nv_csearch(). */
836 did_cursorhold = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000837 if (ca.cmdchar == 'g')
838 {
839 /*
840 * For 'g' get the next character now, so that we can check for
841 * "gr", "g'" and "g`".
842 */
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000843 ca.nchar = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844 LANGMAP_ADJUST(ca.nchar, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000845#ifdef FEAT_CMDL_INFO
846 need_flushbuf |= add_to_showcmd(ca.nchar);
847#endif
848 if (ca.nchar == 'r' || ca.nchar == '\'' || ca.nchar == '`'
Bram Moolenaarba2d44f2013-11-28 19:27:30 +0100849 || ca.nchar == Ctrl_BSL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000850 {
851 cp = &ca.extra_char; /* need to get a third character */
852 if (ca.nchar != 'r')
853 lit = TRUE; /* get it literally */
854 else
855 repl = TRUE; /* get it in replace mode */
856 }
857 else
858 cp = NULL; /* no third character needed */
859 }
860 else
861 {
862 if (ca.cmdchar == 'r') /* get it in replace mode */
863 repl = TRUE;
864 cp = &ca.nchar;
865 }
866 lang = (repl || (nv_cmds[idx].cmd_flags & NV_LANG));
867
868 /*
869 * Get a second or third character.
870 */
871 if (cp != NULL)
872 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000873 if (repl)
874 {
875 State = REPLACE; /* pretend Replace mode */
Bram Moolenaar7a641ca2019-10-31 19:55:55 +0100876#ifdef CURSOR_SHAPE
Bram Moolenaar071d4272004-06-13 20:20:40 +0000877 ui_cursor_shape(); /* show different cursor shape */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878#endif
Bram Moolenaar7a641ca2019-10-31 19:55:55 +0100879 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880 if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP)
881 {
882 /* Allow mappings defined with ":lmap". */
883 --no_mapping;
884 --allow_keys;
885 if (repl)
886 State = LREPLACE;
887 else
888 State = LANGMAP;
889 langmap_active = TRUE;
890 }
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100891#ifdef HAVE_INPUT_METHOD
Bram Moolenaar071d4272004-06-13 20:20:40 +0000892 save_smd = p_smd;
893 p_smd = FALSE; /* Don't let the IM code show the mode here */
894 if (lang && curbuf->b_p_iminsert == B_IMODE_IM)
895 im_set_active(TRUE);
896#endif
897
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000898 *cp = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000899
900 if (langmap_active)
901 {
902 /* Undo the decrement done above */
903 ++no_mapping;
904 ++allow_keys;
905 State = NORMAL_BUSY;
906 }
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100907#ifdef HAVE_INPUT_METHOD
Bram Moolenaar071d4272004-06-13 20:20:40 +0000908 if (lang)
909 {
910 if (curbuf->b_p_iminsert != B_IMODE_LMAP)
911 im_save_status(&curbuf->b_p_iminsert);
912 im_set_active(FALSE);
913 }
914 p_smd = save_smd;
915#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000916 State = NORMAL_BUSY;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000917#ifdef FEAT_CMDL_INFO
918 need_flushbuf |= add_to_showcmd(*cp);
919#endif
920
921 if (!lit)
922 {
923#ifdef FEAT_DIGRAPHS
924 /* Typing CTRL-K gets a digraph. */
925 if (*cp == Ctrl_K
926 && ((nv_cmds[idx].cmd_flags & NV_LANG)
927 || cp == &ca.extra_char)
928 && vim_strchr(p_cpo, CPO_DIGRAPH) == NULL)
929 {
930 c = get_digraph(FALSE);
931 if (c > 0)
932 {
933 *cp = c;
934# ifdef FEAT_CMDL_INFO
935 /* Guessing how to update showcmd here... */
936 del_from_showcmd(3);
937 need_flushbuf |= add_to_showcmd(*cp);
938# endif
939 }
940 }
941#endif
942
Bram Moolenaar071d4272004-06-13 20:20:40 +0000943 /* adjust chars > 127, except after "tTfFr" commands */
944 LANGMAP_ADJUST(*cp, !lang);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000945#ifdef FEAT_RIGHTLEFT
946 /* adjust Hebrew mapped char */
947 if (p_hkmap && lang && KeyTyped)
948 *cp = hkmap(*cp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000949#endif
950 }
951
952 /*
953 * When the next character is CTRL-\ a following CTRL-N means the
954 * command is aborted and we go to Normal mode.
955 */
956 if (cp == &ca.extra_char
957 && ca.nchar == Ctrl_BSL
958 && (ca.extra_char == Ctrl_N || ca.extra_char == Ctrl_G))
959 {
960 ca.cmdchar = Ctrl_BSL;
961 ca.nchar = ca.extra_char;
962 idx = find_command(ca.cmdchar);
963 }
Bram Moolenaar12a753a2012-10-23 05:08:53 +0200964 else if ((ca.nchar == 'n' || ca.nchar == 'N') && ca.cmdchar == 'g')
Bram Moolenaarf00dc262012-10-21 03:54:33 +0200965 ca.oap->op_type = get_op_type(*cp, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 else if (*cp == Ctrl_BSL)
967 {
968 long towait = (p_ttm >= 0 ? p_ttm : p_tm);
969
970 /* There is a busy wait here when typing "f<C-\>" and then
971 * something different from CTRL-N. Can't be avoided. */
972 while ((c = vpeekc()) <= 0 && towait > 0L)
973 {
974 do_sleep(towait > 50L ? 50L : towait);
975 towait -= 50L;
976 }
977 if (c > 0)
978 {
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000979 c = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000980 if (c != Ctrl_N && c != Ctrl_G)
981 vungetc(c);
982 else
983 {
984 ca.cmdchar = Ctrl_BSL;
985 ca.nchar = c;
986 idx = find_command(ca.cmdchar);
987 }
988 }
989 }
990
Bram Moolenaar071d4272004-06-13 20:20:40 +0000991 /* When getting a text character and the next character is a
992 * multi-byte character, it could be a composing character.
Bram Moolenaar4f880622014-07-23 12:31:20 +0200993 * However, don't wait for it to arrive. Also, do enable mapping,
994 * because if it's put back with vungetc() it's too late to apply
995 * mapping. */
996 --no_mapping;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000997 while (enc_utf8 && lang && (c = vpeekc()) > 0
998 && (c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1))
999 {
Bram Moolenaar61abfd12007-09-13 16:26:47 +00001000 c = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001001 if (!utf_iscomposing(c))
1002 {
1003 vungetc(c); /* it wasn't, put it back */
1004 break;
1005 }
1006 else if (ca.ncharC1 == 0)
1007 ca.ncharC1 = c;
1008 else
1009 ca.ncharC2 = c;
1010 }
Bram Moolenaar4f880622014-07-23 12:31:20 +02001011 ++no_mapping;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001012 }
1013 --no_mapping;
1014 --allow_keys;
1015 }
1016
1017#ifdef FEAT_CMDL_INFO
1018 /*
1019 * Flush the showcmd characters onto the screen so we can see them while
1020 * the command is being executed. Only do this when the shown command was
1021 * actually displayed, otherwise this will slow down a lot when executing
1022 * mappings.
1023 */
1024 if (need_flushbuf)
1025 out_flush();
1026#endif
Bram Moolenaard9205ca2008-10-02 20:55:54 +00001027 if (ca.cmdchar != K_IGNORE)
1028 did_cursorhold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029
1030 State = NORMAL;
1031
1032 if (ca.nchar == ESC)
1033 {
1034 clearop(oap);
1035 if (restart_edit == 0 && goto_im())
1036 restart_edit = 'a';
1037 goto normal_end;
1038 }
1039
Bram Moolenaarc0197e22004-09-13 20:26:32 +00001040 if (ca.cmdchar != K_IGNORE)
1041 {
1042 msg_didout = FALSE; /* don't scroll screen up for normal command */
1043 msg_col = 0;
1044 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046 old_pos = curwin->w_cursor; /* remember where cursor was */
1047
1048 /* When 'keymodel' contains "startsel" some keys start Select/Visual
1049 * mode. */
1050 if (!VIsual_active && km_startsel)
1051 {
1052 if (nv_cmds[idx].cmd_flags & NV_SS)
1053 {
1054 start_selection();
1055 unshift_special(&ca);
1056 idx = find_command(ca.cmdchar);
1057 }
1058 else if ((nv_cmds[idx].cmd_flags & NV_SSS)
1059 && (mod_mask & MOD_MASK_SHIFT))
1060 {
1061 start_selection();
1062 mod_mask &= ~MOD_MASK_SHIFT;
1063 }
1064 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001065
1066 /*
1067 * Execute the command!
1068 * Call the command function found in the commands table.
1069 */
1070 ca.arg = nv_cmds[idx].cmd_arg;
1071 (nv_cmds[idx].cmd_func)(&ca);
1072
1073 /*
1074 * If we didn't start or finish an operator, reset oap->regname, unless we
1075 * need it later.
1076 */
1077 if (!finish_op
1078 && !oap->op_type
1079 && (idx < 0 || !(nv_cmds[idx].cmd_flags & NV_KEEPREG)))
1080 {
1081 clearop(oap);
1082#ifdef FEAT_EVAL
Bram Moolenaar536681b2011-05-10 16:12:45 +02001083 {
1084 int regname = 0;
Bram Moolenaare2bdce32011-05-10 17:29:33 +02001085
Bram Moolenaar536681b2011-05-10 16:12:45 +02001086 /* Adjust the register according to 'clipboard', so that when
1087 * "unnamed" is present it becomes '*' or '+' instead of '"'. */
Bram Moolenaare2bdce32011-05-10 17:29:33 +02001088# ifdef FEAT_CLIPBOARD
Bram Moolenaar536681b2011-05-10 16:12:45 +02001089 adjust_clip_reg(&regname);
Bram Moolenaare2bdce32011-05-10 17:29:33 +02001090# endif
Bram Moolenaar536681b2011-05-10 16:12:45 +02001091 set_reg_var(regname);
1092 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001093#endif
1094 }
1095
Bram Moolenaarc6039d82005-12-02 00:44:04 +00001096 /* Get the length of mapped chars again after typing a count, second
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001097 * character or "z333<cr>". */
1098 if (old_mapped_len > 0)
1099 old_mapped_len = typebuf_maplen();
1100
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 /*
Bram Moolenaarfa5612c2019-12-01 19:37:07 +01001102 * If an operation is pending, handle it. But not for K_IGNORE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001103 */
Bram Moolenaarfa5612c2019-12-01 19:37:07 +01001104 if (ca.cmdchar != K_IGNORE)
1105 do_pending_operator(&ca, old_col, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106
1107 /*
1108 * Wait for a moment when a message is displayed that will be overwritten
1109 * by the mode message.
1110 * In Visual mode and with "^O" in Insert mode, a short message will be
1111 * overwritten by the mode message. Wait a bit, until a key is hit.
1112 * In Visual mode, it's more important to keep the Visual area updated
1113 * than keeping a message (e.g. from a /pat search).
1114 * Only do this if the command was typed, not from a mapping.
1115 * Don't wait when emsg_silent is non-zero.
1116 * Also wait a bit after an error message, e.g. for "^O:".
1117 * Don't redraw the screen, it would remove the message.
1118 */
1119 if ( ((p_smd
Bram Moolenaar09df3122006-01-23 22:23:09 +00001120 && msg_silent == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121 && (restart_edit != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001122 || (VIsual_active
1123 && old_pos.lnum == curwin->w_cursor.lnum
1124 && old_pos.col == curwin->w_cursor.col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125 )
1126 && (clear_cmdline
1127 || redraw_cmdline)
1128 && (msg_didout || (msg_didany && msg_scroll))
1129 && !msg_nowait
1130 && KeyTyped)
1131 || (restart_edit != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001132 && !VIsual_active
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133 && (msg_scroll
1134 || emsg_on_display)))
1135 && oap->regname == 0
1136 && !(ca.retval & CA_COMMAND_BUSY)
1137 && stuff_empty()
1138 && typebuf_typed()
1139 && emsg_silent == 0
1140 && !did_wait_return
1141 && oap->op_type == OP_NOP)
1142 {
1143 int save_State = State;
1144
1145 /* Draw the cursor with the right shape here */
1146 if (restart_edit != 0)
1147 State = INSERT;
1148
1149 /* If need to redraw, and there is a "keep_msg", redraw before the
1150 * delay */
1151 if (must_redraw && keep_msg != NULL && !emsg_on_display)
1152 {
1153 char_u *kmsg;
1154
1155 kmsg = keep_msg;
1156 keep_msg = NULL;
Bram Moolenaare5fbd732019-09-09 20:04:13 +02001157 // showmode() will clear keep_msg, but we want to use it anyway
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 update_screen(0);
Bram Moolenaare5fbd732019-09-09 20:04:13 +02001159 // now reset it, otherwise it's put in the history again
Bram Moolenaar071d4272004-06-13 20:20:40 +00001160 keep_msg = kmsg;
Bram Moolenaare5fbd732019-09-09 20:04:13 +02001161
1162 kmsg = vim_strsave(keep_msg);
1163 if (kmsg != NULL)
1164 {
1165 msg_attr((char *)kmsg, keep_msg_attr);
1166 vim_free(kmsg);
1167 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 }
1169 setcursor();
1170 cursor_on();
1171 out_flush();
1172 if (msg_scroll || emsg_on_display)
Bram Moolenaareda1da02019-11-17 17:06:33 +01001173 ui_delay(1003L, TRUE); /* wait at least one second */
1174 ui_delay(3003L, FALSE); /* wait up to three seconds */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001175 State = save_State;
1176
1177 msg_scroll = FALSE;
1178 emsg_on_display = FALSE;
1179 }
1180
1181 /*
1182 * Finish up after executing a Normal mode command.
1183 */
1184normal_end:
1185
1186 msg_nowait = FALSE;
1187
1188 /* Reset finish_op, in case it was set */
1189#ifdef CURSOR_SHAPE
1190 c = finish_op;
1191#endif
1192 finish_op = FALSE;
1193#ifdef CURSOR_SHAPE
1194 /* Redraw the cursor with another shape, if we were in Operator-pending
1195 * mode or did a replace command. */
1196 if (c || ca.cmdchar == 'r')
1197 {
1198 ui_cursor_shape(); /* may show different cursor shape */
1199# ifdef FEAT_MOUSESHAPE
1200 update_mouseshape(-1);
1201# endif
1202 }
1203#endif
1204
1205#ifdef FEAT_CMDL_INFO
Bram Moolenaara983fe92008-07-31 20:04:27 +00001206 if (oap->op_type == OP_NOP && oap->regname == 0
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001207 && ca.cmdchar != K_CURSORHOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208 clear_showcmd();
1209#endif
1210
1211 checkpcmark(); /* check if we moved since setting pcmark */
1212 vim_free(ca.searchbuf);
1213
Bram Moolenaar071d4272004-06-13 20:20:40 +00001214 if (has_mbyte)
1215 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 if (curwin->w_p_scb && toplevel)
1218 {
1219 validate_cursor(); /* may need to update w_leftcol */
1220 do_check_scrollbind(TRUE);
1221 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222
Bram Moolenaar860cae12010-06-05 23:22:07 +02001223 if (curwin->w_p_crb && toplevel)
1224 {
1225 validate_cursor(); /* may need to update w_leftcol */
1226 do_check_cursorbind();
1227 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228
Bram Moolenaar6fe15bb2017-08-16 21:09:18 +02001229#ifdef FEAT_TERMINAL
Bram Moolenaareef9add2017-09-16 15:38:04 +02001230 /* don't go to Insert mode if a terminal has a running job */
1231 if (term_job_running(curbuf->b_term))
Bram Moolenaar6fe15bb2017-08-16 21:09:18 +02001232 restart_edit = 0;
1233#endif
1234
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235 /*
1236 * May restart edit(), if we got here with CTRL-O in Insert mode (but not
1237 * if still inside a mapping that started in Visual mode).
1238 * May switch from Visual to Select mode after CTRL-O command.
1239 */
1240 if ( oap->op_type == OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 && ((restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
1242 || restart_VIsual_select == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243 && !(ca.retval & CA_COMMAND_BUSY)
1244 && stuff_empty()
1245 && oap->regname == 0)
1246 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247 if (restart_VIsual_select == 1)
1248 {
1249 VIsual_select = TRUE;
1250 showmode();
1251 restart_VIsual_select = 0;
1252 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001253 if (restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 (void)edit(restart_edit, FALSE, 1L);
1255 }
1256
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 if (restart_VIsual_select == 2)
1258 restart_VIsual_select = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259
1260 /* Save count before an operator for next time. */
1261 opcount = ca.opcount;
1262}
1263
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001264#ifdef FEAT_EVAL
1265/*
1266 * Set v:count and v:count1 according to "cap".
1267 * Set v:prevcount only when "set_prevcount" is TRUE.
1268 */
1269 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001270set_vcount_ca(cmdarg_T *cap, int *set_prevcount)
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001271{
1272 long count = cap->count0;
1273
1274 /* multiply with cap->opcount the same way as above */
1275 if (cap->opcount != 0)
1276 count = cap->opcount * (count == 0 ? 1 : count);
1277 set_vcount(count, count == 0 ? 1 : count, *set_prevcount);
1278 *set_prevcount = FALSE; /* only set v:prevcount once */
1279}
1280#endif
1281
Bram Moolenaar071d4272004-06-13 20:20:40 +00001282/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001283 * Check if highlighting for visual mode is possible, give a warning message
1284 * if not.
1285 */
1286 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001287check_visual_highlight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288{
1289 static int did_check = FALSE;
1290
1291 if (full_screen)
1292 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01001293 if (!did_check && HL_ATTR(HLF_V) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001294 msg(_("Warning: terminal cannot highlight"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 did_check = TRUE;
1296 }
1297}
1298
1299/*
Bram Moolenaar66fa2712006-01-22 23:22:22 +00001300 * End Visual mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001301 * This function should ALWAYS be called to end Visual mode, except from
1302 * do_pending_operator().
1303 */
1304 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001305end_visual_mode(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306{
1307#ifdef FEAT_CLIPBOARD
1308 /*
1309 * If we are using the clipboard, then remember what was selected in case
1310 * we need to paste it somewhere while we still own the selection.
1311 * Only do this when the clipboard is already owned. Don't want to grab
1312 * the selection when hitting ESC.
1313 */
1314 if (clip_star.available && clip_star.owned)
1315 clip_auto_select();
1316#endif
1317
1318 VIsual_active = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001319 setmouse();
1320 mouse_dragging = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321
1322 /* Save the current VIsual area for '< and '> marks, and "gv" */
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001323 curbuf->b_visual.vi_mode = VIsual_mode;
1324 curbuf->b_visual.vi_start = VIsual;
1325 curbuf->b_visual.vi_end = curwin->w_cursor;
1326 curbuf->b_visual.vi_curswant = curwin->w_curswant;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327#ifdef FEAT_EVAL
1328 curbuf->b_visual_mode_eval = VIsual_mode;
1329#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 if (!virtual_active())
1331 curwin->w_cursor.coladd = 0;
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001332 may_clear_cmdline();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333
Bram Moolenaarf193fff2006-04-27 00:02:13 +00001334 adjust_cursor_eol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001335}
1336
1337/*
1338 * Reset VIsual_active and VIsual_reselect.
1339 */
1340 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001341reset_VIsual_and_resel(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001342{
1343 if (VIsual_active)
1344 {
1345 end_visual_mode();
1346 redraw_curbuf_later(INVERTED); /* delete the inversion later */
1347 }
1348 VIsual_reselect = FALSE;
1349}
1350
1351/*
1352 * Reset VIsual_active and VIsual_reselect if it's set.
1353 */
1354 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001355reset_VIsual(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356{
1357 if (VIsual_active)
1358 {
1359 end_visual_mode();
1360 redraw_curbuf_later(INVERTED); /* delete the inversion later */
1361 VIsual_reselect = FALSE;
1362 }
1363}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02001365 void
1366restore_visual_mode(void)
1367{
1368 if (VIsual_mode_orig != NUL)
1369 {
1370 curbuf->b_visual.vi_mode = VIsual_mode_orig;
1371 VIsual_mode_orig = NUL;
1372 }
1373}
1374
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375/*
1376 * Check for a balloon-eval special item to include when searching for an
1377 * identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
1378 * Returns TRUE if the character at "*ptr" should be included.
1379 * "dir" is FORWARD or BACKWARD, the direction of searching.
1380 * "*colp" is in/decremented if "ptr[-dir]" should also be included.
1381 * "bnp" points to a counter for square brackets.
1382 */
1383 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001384find_is_eval_item(
1385 char_u *ptr,
1386 int *colp,
1387 int *bnp,
1388 int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389{
1390 /* Accept everything inside []. */
1391 if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD))
1392 ++*bnp;
1393 if (*bnp > 0)
1394 {
1395 if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD))
1396 --*bnp;
1397 return TRUE;
1398 }
1399
1400 /* skip over "s.var" */
1401 if (*ptr == '.')
1402 return TRUE;
1403
1404 /* two-character item: s->var */
1405 if (ptr[dir == BACKWARD ? 0 : 1] == '>'
1406 && ptr[dir == BACKWARD ? -1 : 0] == '-')
1407 {
1408 *colp += dir;
1409 return TRUE;
1410 }
1411 return FALSE;
1412}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413
1414/*
1415 * Find the identifier under or to the right of the cursor.
1416 * "find_type" can have one of three values:
1417 * FIND_IDENT: find an identifier (keyword)
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001418 * FIND_STRING: find any non-white text
1419 * FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
Bram Moolenaar52b4b552005-03-07 23:00:57 +00001420 * FIND_EVAL: find text useful for C program debugging
Bram Moolenaar071d4272004-06-13 20:20:40 +00001421 *
1422 * There are three steps:
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001423 * 1. Search forward for the start of an identifier/text. Doesn't move if
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 * already on one.
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001425 * 2. Search backward for the start of this identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001426 * This doesn't match the real Vi but I like it a little better and it
1427 * shouldn't bother anyone.
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001428 * 3. Search forward to the end of this identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001429 * When FIND_IDENT isn't defined, we backup until a blank.
1430 *
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001431 * Returns the length of the text, or zero if no text is found.
1432 * If text is found, a pointer to the text is put in "*text". This
1433 * points into the current buffer line and is not always NUL terminated.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 */
1435 int
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001436find_ident_under_cursor(char_u **text, int find_type)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437{
1438 return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001439 curwin->w_cursor.col, text, NULL, find_type);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001440}
1441
1442/*
1443 * Like find_ident_under_cursor(), but for any window and any position.
1444 * However: Uses 'iskeyword' from the current window!.
1445 */
1446 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001447find_ident_at_pos(
1448 win_T *wp,
1449 linenr_T lnum,
1450 colnr_T startcol,
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001451 char_u **text,
1452 int *textcol, // column where "text" starts, can be NULL
Bram Moolenaar9b578142016-01-30 19:39:49 +01001453 int find_type)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454{
1455 char_u *ptr;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001456 int col = 0; // init to shut up GCC
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 int this_class = 0;
1459 int prev_class;
1460 int prevcol;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001461 int bn = 0; // bracket nesting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001462
1463 /*
1464 * if i == 0: try to find an identifier
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001465 * if i == 1: try to find any non-white text
Bram Moolenaar071d4272004-06-13 20:20:40 +00001466 */
1467 ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
1468 for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
1469 {
1470 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001471 * 1. skip to start of identifier/text
Bram Moolenaar071d4272004-06-13 20:20:40 +00001472 */
1473 col = startcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001474 if (has_mbyte)
1475 {
1476 while (ptr[col] != NUL)
1477 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001478 // Stop at a ']' to evaluate "a[x]".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 if ((find_type & FIND_EVAL) && ptr[col] == ']')
1480 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 this_class = mb_get_class(ptr + col);
1482 if (this_class != 0 && (i == 1 || this_class != 1))
1483 break;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001484 col += (*mb_ptr2len)(ptr + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 }
1486 }
1487 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 while (ptr[col] != NUL
Bram Moolenaar1c465442017-03-12 20:10:05 +01001489 && (i == 0 ? !vim_iswordc(ptr[col]) : VIM_ISWHITE(ptr[col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 && (!(find_type & FIND_EVAL) || ptr[col] != ']')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 )
1492 ++col;
1493
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001494 // When starting on a ']' count it, so that we include the '['.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 bn = ptr[col] == ']';
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496
1497 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001498 * 2. Back up to start of identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 if (has_mbyte)
1501 {
1502 /* Remember class of character under cursor. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 if ((find_type & FIND_EVAL) && ptr[col] == ']')
1504 this_class = mb_get_class((char_u *)"a");
1505 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 this_class = mb_get_class(ptr + col);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001507 while (col > 0 && this_class != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 {
1509 prevcol = col - 1 - (*mb_head_off)(ptr, ptr + col - 1);
1510 prev_class = mb_get_class(ptr + prevcol);
1511 if (this_class != prev_class
1512 && (i == 0
1513 || prev_class == 0
1514 || (find_type & FIND_IDENT))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515 && (!(find_type & FIND_EVAL)
1516 || prevcol == 0
1517 || !find_is_eval_item(ptr + prevcol, &prevcol,
1518 &bn, BACKWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519 )
1520 break;
1521 col = prevcol;
1522 }
1523
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001524 // If we don't want just any old text, or we've found an
1525 // identifier, stop searching.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 if (this_class > 2)
1527 this_class = 2;
1528 if (!(find_type & FIND_STRING) || this_class == 2)
1529 break;
1530 }
1531 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532 {
1533 while (col > 0
1534 && ((i == 0
1535 ? vim_iswordc(ptr[col - 1])
Bram Moolenaar1c465442017-03-12 20:10:05 +01001536 : (!VIM_ISWHITE(ptr[col - 1])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537 && (!(find_type & FIND_IDENT)
1538 || !vim_iswordc(ptr[col - 1]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 || ((find_type & FIND_EVAL)
1540 && col > 1
1541 && find_is_eval_item(ptr + col - 1, &col,
1542 &bn, BACKWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001543 ))
1544 --col;
1545
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001546 // If we don't want just any old text, or we've found an
1547 // identifier, stop searching.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 if (!(find_type & FIND_STRING) || vim_iswordc(ptr[col]))
1549 break;
1550 }
1551 }
1552
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001553 if (ptr[col] == NUL || (i == 0
1554 && (has_mbyte ? this_class != 2 : !vim_iswordc(ptr[col]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001555 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001556 // didn't find an identifier or text
Bram Moolenaar17627312019-06-02 19:53:44 +02001557 if ((find_type & FIND_NOERROR) == 0)
1558 {
1559 if (find_type & FIND_STRING)
1560 emsg(_("E348: No string under cursor"));
1561 else
1562 emsg(_(e_noident));
1563 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 return 0;
1565 }
1566 ptr += col;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001567 *text = ptr;
1568 if (textcol != NULL)
1569 *textcol = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001570
1571 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001572 * 3. Find the end if the identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001574 bn = 0;
1575 startcol -= col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001577 if (has_mbyte)
1578 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001579 // Search for point of changing multibyte character class.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 this_class = mb_get_class(ptr);
1581 while (ptr[col] != NUL
1582 && ((i == 0 ? mb_get_class(ptr + col) == this_class
1583 : mb_get_class(ptr + col) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584 || ((find_type & FIND_EVAL)
1585 && col <= (int)startcol
1586 && find_is_eval_item(ptr + col, &col, &bn, FORWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001587 ))
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001588 col += (*mb_ptr2len)(ptr + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001589 }
1590 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 while ((i == 0 ? vim_iswordc(ptr[col])
Bram Moolenaar1c465442017-03-12 20:10:05 +01001592 : (ptr[col] != NUL && !VIM_ISWHITE(ptr[col])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 || ((find_type & FIND_EVAL)
1594 && col <= (int)startcol
1595 && find_is_eval_item(ptr + col, &col, &bn, FORWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597 ++col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001598
1599 return col;
1600}
1601
1602/*
1603 * Prepare for redo of a normal command.
1604 */
1605 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001606prep_redo_cmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001607{
1608 prep_redo(cap->oap->regname, cap->count0,
1609 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
1610}
1611
1612/*
1613 * Prepare for redo of any command.
1614 * Note that only the last argument can be a multi-byte char.
1615 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001616 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001617prep_redo(
1618 int regname,
1619 long num,
1620 int cmd1,
1621 int cmd2,
1622 int cmd3,
1623 int cmd4,
1624 int cmd5)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001625{
1626 ResetRedobuff();
1627 if (regname != 0) /* yank from specified buffer */
1628 {
1629 AppendCharToRedobuff('"');
1630 AppendCharToRedobuff(regname);
1631 }
1632 if (num)
1633 AppendNumberToRedobuff(num);
1634
1635 if (cmd1 != NUL)
1636 AppendCharToRedobuff(cmd1);
1637 if (cmd2 != NUL)
1638 AppendCharToRedobuff(cmd2);
1639 if (cmd3 != NUL)
1640 AppendCharToRedobuff(cmd3);
1641 if (cmd4 != NUL)
1642 AppendCharToRedobuff(cmd4);
1643 if (cmd5 != NUL)
1644 AppendCharToRedobuff(cmd5);
1645}
1646
1647/*
1648 * check for operator active and clear it
1649 *
1650 * return TRUE if operator was active
1651 */
1652 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001653checkclearop(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001654{
1655 if (oap->op_type == OP_NOP)
1656 return FALSE;
1657 clearopbeep(oap);
1658 return TRUE;
1659}
1660
1661/*
Bram Moolenaarc980de32007-05-06 11:59:04 +00001662 * Check for operator or Visual active. Clear active operator.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001663 *
Bram Moolenaarc980de32007-05-06 11:59:04 +00001664 * Return TRUE if operator or Visual was active.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665 */
1666 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001667checkclearopq(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001669 if (oap->op_type == OP_NOP && !VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670 return FALSE;
1671 clearopbeep(oap);
1672 return TRUE;
1673}
1674
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001675 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001676clearop(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677{
1678 oap->op_type = OP_NOP;
1679 oap->regname = 0;
1680 oap->motion_force = NUL;
1681 oap->use_reg_one = FALSE;
1682}
1683
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001684 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001685clearopbeep(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001686{
1687 clearop(oap);
1688 beep_flush();
1689}
1690
Bram Moolenaar071d4272004-06-13 20:20:40 +00001691/*
1692 * Remove the shift modifier from a special key.
1693 */
1694 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001695unshift_special(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001696{
1697 switch (cap->cmdchar)
1698 {
1699 case K_S_RIGHT: cap->cmdchar = K_RIGHT; break;
1700 case K_S_LEFT: cap->cmdchar = K_LEFT; break;
1701 case K_S_UP: cap->cmdchar = K_UP; break;
1702 case K_S_DOWN: cap->cmdchar = K_DOWN; break;
1703 case K_S_HOME: cap->cmdchar = K_HOME; break;
1704 case K_S_END: cap->cmdchar = K_END; break;
1705 }
1706 cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
1707}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001708
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001709/*
1710 * If the mode is currently displayed clear the command line or update the
1711 * command displayed.
1712 */
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02001713 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001714may_clear_cmdline(void)
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001715{
1716 if (mode_displayed)
1717 clear_cmdline = TRUE; /* unshow visual mode later */
1718#ifdef FEAT_CMDL_INFO
1719 else
1720 clear_showcmd();
1721#endif
1722}
1723
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724#if defined(FEAT_CMDL_INFO) || defined(PROTO)
1725/*
1726 * Routines for displaying a partly typed command
1727 */
1728
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001729#define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730static char_u showcmd_buf[SHOWCMD_BUFLEN];
1731static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; /* For push_showcmd() */
1732static int showcmd_is_clear = TRUE;
1733static int showcmd_visual = FALSE;
1734
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01001735static void display_showcmd(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736
1737 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001738clear_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739{
1740 if (!p_sc)
1741 return;
1742
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743 if (VIsual_active && !char_avail())
1744 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001745 int cursor_bot = LT_POS(VIsual, curwin->w_cursor);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001746 long lines;
1747 colnr_T leftcol, rightcol;
1748 linenr_T top, bot;
1749
1750 /* Show the size of the Visual area. */
Bram Moolenaar81d00072009-04-29 15:41:40 +00001751 if (cursor_bot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752 {
1753 top = VIsual.lnum;
1754 bot = curwin->w_cursor.lnum;
1755 }
1756 else
1757 {
1758 top = curwin->w_cursor.lnum;
1759 bot = VIsual.lnum;
1760 }
1761# ifdef FEAT_FOLDING
1762 /* Include closed folds as a whole. */
Bram Moolenaarcde88542015-08-11 19:14:00 +02001763 (void)hasFolding(top, &top, NULL);
1764 (void)hasFolding(bot, NULL, &bot);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765# endif
1766 lines = bot - top + 1;
1767
1768 if (VIsual_mode == Ctrl_V)
1769 {
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001770# ifdef FEAT_LINEBREAK
Bram Moolenaar81d00072009-04-29 15:41:40 +00001771 char_u *saved_sbr = p_sbr;
Bram Moolenaaree857022019-11-09 23:26:40 +01001772 char_u *saved_w_sbr = curwin->w_p_sbr;
Bram Moolenaar81d00072009-04-29 15:41:40 +00001773
1774 /* Make 'sbr' empty for a moment to get the correct size. */
1775 p_sbr = empty_option;
Bram Moolenaaree857022019-11-09 23:26:40 +01001776 curwin->w_p_sbr = empty_option;
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001777# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001779# ifdef FEAT_LINEBREAK
Bram Moolenaar81d00072009-04-29 15:41:40 +00001780 p_sbr = saved_sbr;
Bram Moolenaaree857022019-11-09 23:26:40 +01001781 curwin->w_p_sbr = saved_w_sbr;
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001782# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783 sprintf((char *)showcmd_buf, "%ldx%ld", lines,
1784 (long)(rightcol - leftcol + 1));
1785 }
1786 else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum)
1787 sprintf((char *)showcmd_buf, "%ld", lines);
1788 else
Bram Moolenaarf91787c2010-07-17 12:47:16 +02001789 {
1790 char_u *s, *e;
1791 int l;
1792 int bytes = 0;
1793 int chars = 0;
1794
1795 if (cursor_bot)
1796 {
1797 s = ml_get_pos(&VIsual);
1798 e = ml_get_cursor();
1799 }
1800 else
1801 {
1802 s = ml_get_cursor();
1803 e = ml_get_pos(&VIsual);
1804 }
1805 while ((*p_sel != 'e') ? s <= e : s < e)
1806 {
1807 l = (*mb_ptr2len)(s);
1808 if (l == 0)
1809 {
1810 ++bytes;
1811 ++chars;
1812 break; /* end of line */
1813 }
1814 bytes += l;
1815 ++chars;
1816 s += l;
1817 }
1818 if (bytes == chars)
1819 sprintf((char *)showcmd_buf, "%d", chars);
1820 else
1821 sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
1822 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001823 showcmd_buf[SHOWCMD_COLS] = NUL; /* truncate */
1824 showcmd_visual = TRUE;
1825 }
1826 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 {
1828 showcmd_buf[0] = NUL;
1829 showcmd_visual = FALSE;
1830
1831 /* Don't actually display something if there is nothing to clear. */
1832 if (showcmd_is_clear)
1833 return;
1834 }
1835
1836 display_showcmd();
1837}
1838
1839/*
1840 * Add 'c' to string of shown command chars.
1841 * Return TRUE if output has been written (and setcursor() has been called).
1842 */
1843 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001844add_to_showcmd(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001845{
1846 char_u *p;
1847 int old_len;
1848 int extra_len;
1849 int overflow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 int i;
1851 static int ignore[] =
1852 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001853#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 K_VER_SCROLLBAR, K_HOR_SCROLLBAR,
1855 K_LEFTMOUSE_NM, K_LEFTRELEASE_NM,
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001856#endif
Bram Moolenaarec2da362017-01-21 20:04:22 +01001857 K_IGNORE, K_PS,
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001858 K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
1860 K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +02001861 K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001862 K_X1MOUSE, K_X1DRAG, K_X1RELEASE, K_X2MOUSE, K_X2DRAG, K_X2RELEASE,
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00001863 K_CURSORHOLD,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001864 0
1865 };
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866
Bram Moolenaar09df3122006-01-23 22:23:09 +00001867 if (!p_sc || msg_silent != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868 return FALSE;
1869
1870 if (showcmd_visual)
1871 {
1872 showcmd_buf[0] = NUL;
1873 showcmd_visual = FALSE;
1874 }
1875
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876 /* Ignore keys that are scrollbar updates and mouse clicks */
1877 if (IS_SPECIAL(c))
1878 for (i = 0; ignore[i] != 0; ++i)
1879 if (ignore[i] == c)
1880 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001881
1882 p = transchar(c);
Bram Moolenaar7ba07412013-12-11 14:55:01 +01001883 if (*p == ' ')
1884 STRCPY(p, "<20>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885 old_len = (int)STRLEN(showcmd_buf);
1886 extra_len = (int)STRLEN(p);
1887 overflow = old_len + extra_len - SHOWCMD_COLS;
1888 if (overflow > 0)
Bram Moolenaarb0db5692007-08-14 20:54:49 +00001889 mch_memmove(showcmd_buf, showcmd_buf + overflow,
1890 old_len - overflow + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891 STRCAT(showcmd_buf, p);
1892
1893 if (char_avail())
1894 return FALSE;
1895
1896 display_showcmd();
1897
1898 return TRUE;
1899}
1900
1901 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001902add_to_showcmd_c(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903{
1904 if (!add_to_showcmd(c))
1905 setcursor();
1906}
1907
1908/*
1909 * Delete 'len' characters from the end of the shown command.
1910 */
1911 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001912del_from_showcmd(int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913{
1914 int old_len;
1915
1916 if (!p_sc)
1917 return;
1918
1919 old_len = (int)STRLEN(showcmd_buf);
1920 if (len > old_len)
1921 len = old_len;
1922 showcmd_buf[old_len - len] = NUL;
1923
1924 if (!char_avail())
1925 display_showcmd();
1926}
1927
1928/*
1929 * push_showcmd() and pop_showcmd() are used when waiting for the user to type
1930 * something and there is a partial mapping.
1931 */
1932 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001933push_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001934{
1935 if (p_sc)
1936 STRCPY(old_showcmd_buf, showcmd_buf);
1937}
1938
1939 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001940pop_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001941{
1942 if (!p_sc)
1943 return;
1944
1945 STRCPY(showcmd_buf, old_showcmd_buf);
1946
1947 display_showcmd();
1948}
1949
1950 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001951display_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952{
1953 int len;
1954
1955 cursor_off();
1956
1957 len = (int)STRLEN(showcmd_buf);
1958 if (len == 0)
1959 showcmd_is_clear = TRUE;
1960 else
1961 {
1962 screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0);
1963 showcmd_is_clear = FALSE;
1964 }
1965
1966 /*
Bram Moolenaarf711faf2007-05-10 16:48:19 +00001967 * clear the rest of an old message by outputting up to SHOWCMD_COLS
1968 * spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969 */
1970 screen_puts((char_u *)" " + len, (int)Rows - 1, sc_col + len, 0);
1971
1972 setcursor(); /* put cursor back where it belongs */
1973}
1974#endif
1975
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976/*
1977 * When "check" is FALSE, prepare for commands that scroll the window.
1978 * When "check" is TRUE, take care of scroll-binding after the window has
1979 * scrolled. Called from normal_cmd() and edit().
1980 */
1981 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001982do_check_scrollbind(int check)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001983{
1984 static win_T *old_curwin = NULL;
1985 static linenr_T old_topline = 0;
1986#ifdef FEAT_DIFF
1987 static int old_topfill = 0;
1988#endif
1989 static buf_T *old_buf = NULL;
1990 static colnr_T old_leftcol = 0;
1991
1992 if (check && curwin->w_p_scb)
1993 {
1994 /* If a ":syncbind" command was just used, don't scroll, only reset
1995 * the values. */
1996 if (did_syncbind)
1997 did_syncbind = FALSE;
1998 else if (curwin == old_curwin)
1999 {
2000 /*
2001 * Synchronize other windows, as necessary according to
2002 * 'scrollbind'. Don't do this after an ":edit" command, except
2003 * when 'diff' is set.
2004 */
2005 if ((curwin->w_buffer == old_buf
2006#ifdef FEAT_DIFF
2007 || curwin->w_p_diff
2008#endif
2009 )
2010 && (curwin->w_topline != old_topline
2011#ifdef FEAT_DIFF
2012 || curwin->w_topfill != old_topfill
2013#endif
2014 || curwin->w_leftcol != old_leftcol))
2015 {
2016 check_scrollbind(curwin->w_topline - old_topline,
2017 (long)(curwin->w_leftcol - old_leftcol));
2018 }
2019 }
2020 else if (vim_strchr(p_sbo, 'j')) /* jump flag set in 'scrollopt' */
2021 {
2022 /*
2023 * When switching between windows, make sure that the relative
2024 * vertical offset is valid for the new window. The relative
2025 * offset is invalid whenever another 'scrollbind' window has
2026 * scrolled to a point that would force the current window to
2027 * scroll past the beginning or end of its buffer. When the
2028 * resync is performed, some of the other 'scrollbind' windows may
2029 * need to jump so that the current window's relative position is
2030 * visible on-screen.
2031 */
2032 check_scrollbind(curwin->w_topline - curwin->w_scbind_pos, 0L);
2033 }
2034 curwin->w_scbind_pos = curwin->w_topline;
2035 }
2036
2037 old_curwin = curwin;
2038 old_topline = curwin->w_topline;
2039#ifdef FEAT_DIFF
2040 old_topfill = curwin->w_topfill;
2041#endif
2042 old_buf = curwin->w_buffer;
2043 old_leftcol = curwin->w_leftcol;
2044}
2045
2046/*
2047 * Synchronize any windows that have "scrollbind" set, based on the
2048 * number of rows by which the current window has changed
2049 * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
2050 */
2051 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002052check_scrollbind(linenr_T topline_diff, long leftcol_diff)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053{
2054 int want_ver;
2055 int want_hor;
2056 win_T *old_curwin = curwin;
2057 buf_T *old_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 int old_VIsual_select = VIsual_select;
2059 int old_VIsual_active = VIsual_active;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 colnr_T tgt_leftcol = curwin->w_leftcol;
2061 long topline;
2062 long y;
2063
2064 /*
2065 * check 'scrollopt' string for vertical and horizontal scroll options
2066 */
2067 want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0);
2068#ifdef FEAT_DIFF
2069 want_ver |= old_curwin->w_p_diff;
2070#endif
2071 want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0));
2072
2073 /*
2074 * loop through the scrollbound windows and scroll accordingly
2075 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 VIsual_select = VIsual_active = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02002077 FOR_ALL_WINDOWS(curwin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 {
2079 curbuf = curwin->w_buffer;
2080 /* skip original window and windows with 'noscrollbind' */
2081 if (curwin != old_curwin && curwin->w_p_scb)
2082 {
2083 /*
2084 * do the vertical scroll
2085 */
2086 if (want_ver)
2087 {
2088#ifdef FEAT_DIFF
2089 if (old_curwin->w_p_diff && curwin->w_p_diff)
2090 {
2091 diff_set_topline(old_curwin, curwin);
2092 }
2093 else
2094#endif
2095 {
2096 curwin->w_scbind_pos += topline_diff;
2097 topline = curwin->w_scbind_pos;
2098 if (topline > curbuf->b_ml.ml_line_count)
2099 topline = curbuf->b_ml.ml_line_count;
2100 if (topline < 1)
2101 topline = 1;
2102
2103 y = topline - curwin->w_topline;
2104 if (y > 0)
2105 scrollup(y, FALSE);
2106 else
2107 scrolldown(-y, FALSE);
2108 }
2109
2110 redraw_later(VALID);
2111 cursor_correct();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 curwin->w_redr_status = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002113 }
2114
2115 /*
2116 * do the horizontal scroll
2117 */
2118 if (want_hor && curwin->w_leftcol != tgt_leftcol)
2119 {
2120 curwin->w_leftcol = tgt_leftcol;
2121 leftcol_changed();
2122 }
2123 }
2124 }
2125
2126 /*
2127 * reset current-window
2128 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002129 VIsual_select = old_VIsual_select;
2130 VIsual_active = old_VIsual_active;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 curwin = old_curwin;
2132 curbuf = old_curbuf;
2133}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134
2135/*
2136 * Command character that's ignored.
2137 * Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
Bram Moolenaar5ea0ac72010-05-07 15:52:08 +02002138 * xon/xoff.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002140 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002141nv_ignore(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002142{
Bram Moolenaarfc735152005-03-22 22:54:12 +00002143 cap->retval |= CA_COMMAND_BUSY; /* don't call edit() now */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144}
2145
2146/*
Bram Moolenaarebefac62005-12-28 22:39:57 +00002147 * Command character that doesn't do anything, but unlike nv_ignore() does
2148 * start edit(). Used for "startinsert" executed while starting up.
2149 */
Bram Moolenaarebefac62005-12-28 22:39:57 +00002150 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002151nv_nop(cmdarg_T *cap UNUSED)
Bram Moolenaarebefac62005-12-28 22:39:57 +00002152{
2153}
2154
2155/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002156 * Command character doesn't exist.
2157 */
2158 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002159nv_error(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002160{
2161 clearopbeep(cap->oap);
2162}
2163
2164/*
2165 * <Help> and <F1> commands.
2166 */
2167 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002168nv_help(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169{
2170 if (!checkclearopq(cap->oap))
2171 ex_help(NULL);
2172}
2173
2174/*
2175 * CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
2176 */
2177 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002178nv_addsub(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002179{
Bram Moolenaarf2732452018-06-03 14:47:35 +02002180#ifdef FEAT_JOB_CHANNEL
2181 if (bt_prompt(curbuf) && !prompt_curpos_editable())
2182 clearopbeep(cap->oap);
2183 else
2184#endif
Bram Moolenaard79e5502016-01-10 22:13:02 +01002185 if (!VIsual_active && cap->oap->op_type == OP_NOP)
Bram Moolenaar9bb19302015-07-03 12:44:07 +02002186 {
Bram Moolenaaref2b5032016-01-12 22:20:58 +01002187 prep_redo_cmd(cap);
Bram Moolenaard79e5502016-01-10 22:13:02 +01002188 cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
2189 op_addsub(cap->oap, cap->count1, cap->arg);
2190 cap->oap->op_type = OP_NOP;
Bram Moolenaar9bb19302015-07-03 12:44:07 +02002191 }
Bram Moolenaard79e5502016-01-10 22:13:02 +01002192 else if (VIsual_active)
2193 nv_operator(cap);
Bram Moolenaar3a304b22015-06-25 13:57:36 +02002194 else
Bram Moolenaard79e5502016-01-10 22:13:02 +01002195 clearop(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196}
2197
2198/*
2199 * CTRL-F, CTRL-B, etc: Scroll page up or down.
2200 */
2201 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002202nv_page(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203{
2204 if (!checkclearop(cap->oap))
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002205 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002206 if (mod_mask & MOD_MASK_CTRL)
2207 {
2208 /* <C-PageUp>: tab page back; <C-PageDown>: tab page forward */
2209 if (cap->arg == BACKWARD)
2210 goto_tabpage(-(int)cap->count1);
2211 else
2212 goto_tabpage((int)cap->count0);
2213 }
2214 else
Bram Moolenaar4033c552017-09-16 20:54:51 +02002215 (void)onepage(cap->arg, cap->count1);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002216 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217}
2218
2219/*
2220 * Implementation of "gd" and "gD" command.
2221 */
2222 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002223nv_gd(
2224 oparg_T *oap,
2225 int nchar,
2226 int thisblock) /* 1 for "1gd" and "1gD" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002227{
2228 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002229 char_u *ptr;
2230
Bram Moolenaard9d30582005-05-18 22:10:28 +00002231 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
Bram Moolenaar1538fc32016-04-16 09:13:34 +02002232 || find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)
2233 == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002234 clearopbeep(oap);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002235#ifdef FEAT_FOLDING
2236 else if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP)
2237 foldOpenCursor();
2238#endif
2239}
2240
2241/*
Bram Moolenaar226630a2016-10-08 19:21:31 +02002242 * Return TRUE if line[offset] is not inside a C-style comment or string, FALSE
2243 * otherwise.
2244 */
2245 static int
2246is_ident(char_u *line, int offset)
2247{
2248 int i;
2249 int incomment = FALSE;
2250 int instring = 0;
2251 int prev = 0;
2252
2253 for (i = 0; i < offset && line[i] != NUL; i++)
2254 {
2255 if (instring != 0)
2256 {
2257 if (prev != '\\' && line[i] == instring)
2258 instring = 0;
2259 }
2260 else if ((line[i] == '"' || line[i] == '\'') && !incomment)
2261 {
2262 instring = line[i];
2263 }
2264 else
2265 {
2266 if (incomment)
2267 {
2268 if (prev == '*' && line[i] == '/')
2269 incomment = FALSE;
2270 }
2271 else if (prev == '/' && line[i] == '*')
2272 {
2273 incomment = TRUE;
2274 }
2275 else if (prev == '/' && line[i] == '/')
2276 {
2277 return FALSE;
2278 }
2279 }
2280
2281 prev = line[i];
2282 }
2283
2284 return incomment == FALSE && instring == 0;
2285}
2286
2287/*
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002288 * Search for variable declaration of "ptr[len]".
2289 * When "locally" is TRUE in the current function ("gd"), otherwise in the
2290 * current file ("gD").
2291 * When "thisblock" is TRUE check the {} block scope.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002292 * Return FAIL when not found.
2293 */
2294 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002295find_decl(
2296 char_u *ptr,
2297 int len,
2298 int locally,
2299 int thisblock,
Bram Moolenaar23c60f22016-06-15 22:03:48 +02002300 int flags_arg) /* flags passed to searchit() */
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002301{
2302 char_u *pat;
2303 pos_T old_pos;
2304 pos_T par_pos;
2305 pos_T found_pos;
2306 int t;
2307 int save_p_ws;
2308 int save_p_scs;
2309 int retval = OK;
Bram Moolenaar89d40322006-08-29 15:30:07 +00002310 int incll;
Bram Moolenaar23c60f22016-06-15 22:03:48 +02002311 int searchflags = flags_arg;
Bram Moolenaar226630a2016-10-08 19:21:31 +02002312 int valid;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002313
2314 if ((pat = alloc(len + 7)) == NULL)
2315 return FAIL;
Bram Moolenaard9d30582005-05-18 22:10:28 +00002316
2317 /* Put "\V" before the pattern to avoid that the special meaning of "."
2318 * and "~" causes trouble. */
2319 sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
2320 len, ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321 old_pos = curwin->w_cursor;
2322 save_p_ws = p_ws;
2323 save_p_scs = p_scs;
2324 p_ws = FALSE; /* don't wrap around end of file now */
2325 p_scs = FALSE; /* don't switch ignorecase off now */
2326
2327 /*
2328 * With "gD" go to line 1.
2329 * With "gd" Search back for the start of the current function, then go
2330 * back until a blank line. If this fails go to line 1.
2331 */
Bram Moolenaar89d40322006-08-29 15:30:07 +00002332 if (!locally || !findpar(&incll, BACKWARD, 1L, '{', FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333 {
2334 setpcmark(); /* Set in findpar() otherwise */
2335 curwin->w_cursor.lnum = 1;
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002336 par_pos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337 }
2338 else
2339 {
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002340 par_pos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002341 while (curwin->w_cursor.lnum > 1 && *skipwhite(ml_get_curline()) != NUL)
2342 --curwin->w_cursor.lnum;
2343 }
2344 curwin->w_cursor.col = 0;
2345
2346 /* Search forward for the identifier, ignore comment lines. */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002347 CLEAR_POS(&found_pos);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002348 for (;;)
2349 {
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002350 t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02002351 pat, 1L, searchflags, RE_LAST, NULL);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002352 if (curwin->w_cursor.lnum >= old_pos.lnum)
2353 t = FAIL; /* match after start is failure too */
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002354
Bram Moolenaar0fd92892006-03-09 22:27:48 +00002355 if (thisblock && t != FAIL)
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002356 {
2357 pos_T *pos;
2358
2359 /* Check that the block the match is in doesn't end before the
2360 * position where we started the search from. */
2361 if ((pos = findmatchlimit(NULL, '}', FM_FORWARD,
2362 (int)(old_pos.lnum - curwin->w_cursor.lnum + 1))) != NULL
2363 && pos->lnum < old_pos.lnum)
Bram Moolenaar60402d62017-04-20 18:54:50 +02002364 {
2365 /* There can't be a useful match before the end of this block.
2366 * Skip to the end. */
2367 curwin->w_cursor = *pos;
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002368 continue;
Bram Moolenaar60402d62017-04-20 18:54:50 +02002369 }
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002370 }
2371
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002372 if (t == FAIL)
2373 {
2374 /* If we previously found a valid position, use it. */
2375 if (found_pos.lnum != 0)
2376 {
2377 curwin->w_cursor = found_pos;
2378 t = OK;
2379 }
2380 break;
2381 }
Bram Moolenaar81340392012-06-06 16:12:59 +02002382 if (get_leader_len(ml_get_curline(), NULL, FALSE, TRUE) > 0)
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002383 {
2384 /* Ignore this line, continue at start of next line. */
2385 ++curwin->w_cursor.lnum;
2386 curwin->w_cursor.col = 0;
2387 continue;
2388 }
Bram Moolenaar226630a2016-10-08 19:21:31 +02002389 valid = is_ident(ml_get_curline(), curwin->w_cursor.col);
2390
2391 /* If the current position is not a valid identifier and a previous
2392 * match is present, favor that one instead. */
2393 if (!valid && found_pos.lnum != 0)
2394 {
2395 curwin->w_cursor = found_pos;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002396 break;
Bram Moolenaar226630a2016-10-08 19:21:31 +02002397 }
2398
2399 /* Global search: use first valid match found */
2400 if (valid && !locally)
2401 break;
2402 if (valid && curwin->w_cursor.lnum >= par_pos.lnum)
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002403 {
2404 /* If we previously found a valid position, use it. */
2405 if (found_pos.lnum != 0)
2406 curwin->w_cursor = found_pos;
2407 break;
2408 }
2409
Bram Moolenaar226630a2016-10-08 19:21:31 +02002410 /* For finding a local variable and the match is before the "{" or
2411 * inside a comment, continue searching. For K&R style function
2412 * declarations this skips the function header without types. */
2413 if (!valid)
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002414 CLEAR_POS(&found_pos);
Bram Moolenaar226630a2016-10-08 19:21:31 +02002415 else
Bram Moolenaar226630a2016-10-08 19:21:31 +02002416 found_pos = curwin->w_cursor;
Bram Moolenaar226630a2016-10-08 19:21:31 +02002417 /* Remove SEARCH_START from flags to avoid getting stuck at one
2418 * position. */
Bram Moolenaar23c60f22016-06-15 22:03:48 +02002419 searchflags &= ~SEARCH_START;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002420 }
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002421
2422 if (t == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423 {
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002424 retval = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 curwin->w_cursor = old_pos;
2426 }
2427 else
2428 {
2429 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002430 /* "n" searches forward now */
2431 reset_search_dir();
2432 }
2433
2434 vim_free(pat);
2435 p_ws = save_p_ws;
2436 p_scs = save_p_scs;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002437
2438 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002439}
2440
2441/*
2442 * Move 'dist' lines in direction 'dir', counting lines by *screen*
2443 * lines rather than lines in the file.
2444 * 'dist' must be positive.
2445 *
2446 * Return OK if able to move cursor, FAIL otherwise.
2447 */
2448 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002449nv_screengo(oparg_T *oap, int dir, long dist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450{
2451 int linelen = linetabsize(ml_get_curline());
2452 int retval = OK;
2453 int atend = FALSE;
2454 int n;
2455 int col_off1; /* margin offset for first screen line */
2456 int col_off2; /* margin offset for wrapped screen line */
2457 int width1; /* text width for first screen line */
2458 int width2; /* test width for wrapped screen line */
2459
2460 oap->motion_type = MCHAR;
Bram Moolenaar91b2bdb2013-07-14 13:32:15 +02002461 oap->inclusive = (curwin->w_curswant == MAXCOL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002462
2463 col_off1 = curwin_col_off();
2464 col_off2 = col_off1 - curwin_col_off2();
Bram Moolenaar02631462017-09-22 15:20:32 +02002465 width1 = curwin->w_width - col_off1;
2466 width2 = curwin->w_width - col_off2;
Bram Moolenaar7cc8ec42015-01-27 20:59:31 +01002467 if (width2 == 0)
2468 width2 = 1; /* avoid divide by zero */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 if (curwin->w_width != 0)
Bram Moolenaar44a2f922016-03-19 22:11:51 +01002471 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 /*
2473 * Instead of sticking at the last character of the buffer line we
2474 * try to stick in the last column of the screen.
2475 */
2476 if (curwin->w_curswant == MAXCOL)
2477 {
2478 atend = TRUE;
2479 validate_virtcol();
2480 if (width1 <= 0)
2481 curwin->w_curswant = 0;
2482 else
2483 {
2484 curwin->w_curswant = width1 - 1;
2485 if (curwin->w_virtcol > curwin->w_curswant)
2486 curwin->w_curswant += ((curwin->w_virtcol
2487 - curwin->w_curswant - 1) / width2 + 1) * width2;
2488 }
2489 }
2490 else
2491 {
2492 if (linelen > width1)
2493 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
2494 else
2495 n = width1;
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002496 if (curwin->w_curswant >= (colnr_T)n)
2497 curwin->w_curswant = n - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498 }
2499
2500 while (dist--)
2501 {
2502 if (dir == BACKWARD)
2503 {
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002504 if ((long)curwin->w_curswant >= width1)
2505 // Move back within the line. This can give a negative value
2506 // for w_curswant if width1 < width2 (with cpoptions+=n),
2507 // which will get clipped to column 0.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002508 curwin->w_curswant -= width2;
2509 else
2510 {
2511 /* to previous line */
2512 if (curwin->w_cursor.lnum == 1)
2513 {
2514 retval = FAIL;
2515 break;
2516 }
2517 --curwin->w_cursor.lnum;
2518#ifdef FEAT_FOLDING
2519 /* Move to the start of a closed fold. Don't do that when
2520 * 'foldopen' contains "all": it will open in a moment. */
2521 if (!(fdo_flags & FDO_ALL))
2522 (void)hasFolding(curwin->w_cursor.lnum,
2523 &curwin->w_cursor.lnum, NULL);
2524#endif
2525 linelen = linetabsize(ml_get_curline());
2526 if (linelen > width1)
2527 curwin->w_curswant += (((linelen - width1 - 1) / width2)
2528 + 1) * width2;
2529 }
2530 }
2531 else /* dir == FORWARD */
2532 {
2533 if (linelen > width1)
2534 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
2535 else
2536 n = width1;
2537 if (curwin->w_curswant + width2 < (colnr_T)n)
2538 /* move forward within line */
2539 curwin->w_curswant += width2;
2540 else
2541 {
2542 /* to next line */
2543#ifdef FEAT_FOLDING
2544 /* Move to the end of a closed fold. */
2545 (void)hasFolding(curwin->w_cursor.lnum, NULL,
2546 &curwin->w_cursor.lnum);
2547#endif
2548 if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
2549 {
2550 retval = FAIL;
2551 break;
2552 }
2553 curwin->w_cursor.lnum++;
2554 curwin->w_curswant %= width2;
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002555 // Check if the cursor has moved below the number display
2556 // when width1 < width2 (with cpoptions+=n). Subtract width2
2557 // to get a negative value for w_curswant, which will get
2558 // clipped to column 0.
2559 if (curwin->w_curswant >= width1)
2560 curwin->w_curswant -= width2;
Bram Moolenaar914968e2011-06-20 00:45:58 +02002561 linelen = linetabsize(ml_get_curline());
Bram Moolenaar071d4272004-06-13 20:20:40 +00002562 }
2563 }
2564 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002565 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566
Bram Moolenaar6cd3aee2014-01-14 13:18:58 +01002567 if (virtual_active() && atend)
2568 coladvance(MAXCOL);
2569 else
2570 coladvance(curwin->w_curswant);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002571
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
2573 {
Bram Moolenaar773b1582014-08-29 14:20:51 +02002574 colnr_T virtcol;
2575
Bram Moolenaar071d4272004-06-13 20:20:40 +00002576 /*
2577 * Check for landing on a character that got split at the end of the
2578 * last line. We want to advance a screenline, not end up in the same
2579 * screenline or move two screenlines.
2580 */
2581 validate_virtcol();
Bram Moolenaar773b1582014-08-29 14:20:51 +02002582 virtcol = curwin->w_virtcol;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002583#if defined(FEAT_LINEBREAK)
Bram Moolenaaree857022019-11-09 23:26:40 +01002584 if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL)
2585 virtcol -= vim_strsize(get_showbreak_value(curwin));
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002586#endif
Bram Moolenaar773b1582014-08-29 14:20:51 +02002587
2588 if (virtcol > curwin->w_curswant
Bram Moolenaar071d4272004-06-13 20:20:40 +00002589 && (curwin->w_curswant < (colnr_T)width1
2590 ? (curwin->w_curswant > (colnr_T)width1 / 2)
2591 : ((curwin->w_curswant - width1) % width2
2592 > (colnr_T)width2 / 2)))
2593 --curwin->w_cursor.col;
2594 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595
2596 if (atend)
2597 curwin->w_curswant = MAXCOL; /* stick in the last column */
2598
2599 return retval;
2600}
2601
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602/*
2603 * Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
2604 * cap->arg must be TRUE for CTRL-E.
2605 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002606 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002607nv_scroll_line(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002608{
2609 if (!checkclearop(cap->oap))
2610 scroll_redraw(cap->arg, cap->count1);
2611}
2612
2613/*
2614 * Scroll "count" lines up or down, and redraw.
2615 */
2616 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002617scroll_redraw(int up, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618{
2619 linenr_T prev_topline = curwin->w_topline;
2620#ifdef FEAT_DIFF
2621 int prev_topfill = curwin->w_topfill;
2622#endif
2623 linenr_T prev_lnum = curwin->w_cursor.lnum;
2624
2625 if (up)
2626 scrollup(count, TRUE);
2627 else
2628 scrolldown(count, TRUE);
Bram Moolenaar375e3392019-01-31 18:26:10 +01002629 if (get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 {
2631 /* Adjust the cursor position for 'scrolloff'. Mark w_topline as
2632 * valid, otherwise the screen jumps back at the end of the file. */
2633 cursor_correct();
2634 check_cursor_moved(curwin);
2635 curwin->w_valid |= VALID_TOPLINE;
2636
2637 /* If moved back to where we were, at least move the cursor, otherwise
2638 * we get stuck at one position. Don't move the cursor up if the
2639 * first line of the buffer is already on the screen */
2640 while (curwin->w_topline == prev_topline
2641#ifdef FEAT_DIFF
2642 && curwin->w_topfill == prev_topfill
2643#endif
2644 )
2645 {
2646 if (up)
2647 {
2648 if (curwin->w_cursor.lnum > prev_lnum
2649 || cursor_down(1L, FALSE) == FAIL)
2650 break;
2651 }
2652 else
2653 {
2654 if (curwin->w_cursor.lnum < prev_lnum
2655 || prev_topline == 1L
2656 || cursor_up(1L, FALSE) == FAIL)
2657 break;
2658 }
2659 /* Mark w_topline as valid, otherwise the screen jumps back at the
2660 * end of the file. */
2661 check_cursor_moved(curwin);
2662 curwin->w_valid |= VALID_TOPLINE;
2663 }
2664 }
2665 if (curwin->w_cursor.lnum != prev_lnum)
2666 coladvance(curwin->w_curswant);
2667 redraw_later(VALID);
2668}
2669
2670/*
2671 * Commands that start with "z".
2672 */
2673 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002674nv_zet(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002675{
2676 long n;
2677 colnr_T col;
2678 int nchar = cap->nchar;
2679#ifdef FEAT_FOLDING
2680 long old_fdl = curwin->w_p_fdl;
2681 int old_fen = curwin->w_p_fen;
2682#endif
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00002683#ifdef FEAT_SPELL
Bram Moolenaard0131a82006-03-04 21:46:13 +00002684 int undo = FALSE;
2685#endif
Bram Moolenaar375e3392019-01-31 18:26:10 +01002686 long siso = get_sidescrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002687
2688 if (VIM_ISDIGIT(nchar))
2689 {
2690 /*
2691 * "z123{nchar}": edit the count before obtaining {nchar}
2692 */
2693 if (checkclearop(cap->oap))
2694 return;
2695 n = nchar - '0';
2696 for (;;)
2697 {
2698#ifdef USE_ON_FLY_SCROLL
2699 dont_scroll = TRUE; /* disallow scrolling here */
2700#endif
2701 ++no_mapping;
2702 ++allow_keys; /* no mapping for nchar, but allow key codes */
Bram Moolenaar61abfd12007-09-13 16:26:47 +00002703 nchar = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 LANGMAP_ADJUST(nchar, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002705 --no_mapping;
2706 --allow_keys;
2707#ifdef FEAT_CMDL_INFO
2708 (void)add_to_showcmd(nchar);
2709#endif
2710 if (nchar == K_DEL || nchar == K_KDEL)
2711 n /= 10;
2712 else if (VIM_ISDIGIT(nchar))
2713 n = n * 10 + (nchar - '0');
2714 else if (nchar == CAR)
2715 {
2716#ifdef FEAT_GUI
2717 need_mouse_correct = TRUE;
2718#endif
2719 win_setheight((int)n);
2720 break;
2721 }
2722 else if (nchar == 'l'
2723 || nchar == 'h'
2724 || nchar == K_LEFT
Bram Moolenaara88d9682005-03-25 21:45:43 +00002725 || nchar == K_RIGHT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726 {
2727 cap->count1 = n ? n * cap->count1 : cap->count1;
2728 goto dozet;
2729 }
2730 else
2731 {
2732 clearopbeep(cap->oap);
2733 break;
2734 }
2735 }
2736 cap->oap->op_type = OP_NOP;
2737 return;
2738 }
2739
2740dozet:
2741 if (
2742#ifdef FEAT_FOLDING
2743 /* "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
2744 * and "zC" only in Visual mode. "zj" and "zk" are motion
2745 * commands. */
2746 cap->nchar != 'f' && cap->nchar != 'F'
2747 && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
2748 && cap->nchar != 'j' && cap->nchar != 'k'
2749 &&
2750#endif
2751 checkclearop(cap->oap))
2752 return;
2753
2754 /*
2755 * For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
2756 * If line number given, set cursor.
2757 */
2758 if ((vim_strchr((char_u *)"+\r\nt.z^-b", nchar) != NULL)
2759 && cap->count0
2760 && cap->count0 != curwin->w_cursor.lnum)
2761 {
2762 setpcmark();
2763 if (cap->count0 > curbuf->b_ml.ml_line_count)
2764 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2765 else
2766 curwin->w_cursor.lnum = cap->count0;
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002767 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768 }
2769
2770 switch (nchar)
2771 {
2772 /* "z+", "z<CR>" and "zt": put cursor at top of screen */
2773 case '+':
2774 if (cap->count0 == 0)
2775 {
2776 /* No count given: put cursor at the line below screen */
2777 validate_botline(); /* make sure w_botline is valid */
2778 if (curwin->w_botline > curbuf->b_ml.ml_line_count)
2779 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2780 else
2781 curwin->w_cursor.lnum = curwin->w_botline;
2782 }
2783 /* FALLTHROUGH */
2784 case NL:
2785 case CAR:
2786 case K_KENTER:
2787 beginline(BL_WHITE | BL_FIX);
2788 /* FALLTHROUGH */
2789
2790 case 't': scroll_cursor_top(0, TRUE);
2791 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01002792 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 break;
2794
2795 /* "z." and "zz": put cursor in middle of screen */
2796 case '.': beginline(BL_WHITE | BL_FIX);
2797 /* FALLTHROUGH */
2798
2799 case 'z': scroll_cursor_halfway(TRUE);
2800 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01002801 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002802 break;
2803
2804 /* "z^", "z-" and "zb": put cursor at bottom of screen */
2805 case '^': /* Strange Vi behavior: <count>z^ finds line at top of window
2806 * when <count> is at bottom of window, and puts that one at
2807 * bottom of window. */
2808 if (cap->count0 != 0)
2809 {
2810 scroll_cursor_bot(0, TRUE);
2811 curwin->w_cursor.lnum = curwin->w_topline;
2812 }
2813 else if (curwin->w_topline == 1)
2814 curwin->w_cursor.lnum = 1;
2815 else
2816 curwin->w_cursor.lnum = curwin->w_topline - 1;
2817 /* FALLTHROUGH */
2818 case '-':
2819 beginline(BL_WHITE | BL_FIX);
2820 /* FALLTHROUGH */
2821
2822 case 'b': scroll_cursor_bot(0, TRUE);
2823 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01002824 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 break;
2826
2827 /* "zH" - scroll screen right half-page */
2828 case 'H':
Bram Moolenaar02631462017-09-22 15:20:32 +02002829 cap->count1 *= curwin->w_width / 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830 /* FALLTHROUGH */
2831
2832 /* "zh" - scroll screen to the right */
2833 case 'h':
2834 case K_LEFT:
2835 if (!curwin->w_p_wrap)
2836 {
2837 if ((colnr_T)cap->count1 > curwin->w_leftcol)
2838 curwin->w_leftcol = 0;
2839 else
2840 curwin->w_leftcol -= (colnr_T)cap->count1;
2841 leftcol_changed();
2842 }
2843 break;
2844
2845 /* "zL" - scroll screen left half-page */
Bram Moolenaar02631462017-09-22 15:20:32 +02002846 case 'L': cap->count1 *= curwin->w_width / 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002847 /* FALLTHROUGH */
2848
2849 /* "zl" - scroll screen to the left */
2850 case 'l':
2851 case K_RIGHT:
2852 if (!curwin->w_p_wrap)
2853 {
2854 /* scroll the window left */
2855 curwin->w_leftcol += (colnr_T)cap->count1;
2856 leftcol_changed();
2857 }
2858 break;
2859
2860 /* "zs" - scroll screen, cursor at the start */
2861 case 's': if (!curwin->w_p_wrap)
2862 {
2863#ifdef FEAT_FOLDING
2864 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
2865 col = 0; /* like the cursor is in col 0 */
2866 else
2867#endif
2868 getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
Bram Moolenaar375e3392019-01-31 18:26:10 +01002869 if ((long)col > siso)
2870 col -= siso;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 else
2872 col = 0;
2873 if (curwin->w_leftcol != col)
2874 {
2875 curwin->w_leftcol = col;
2876 redraw_later(NOT_VALID);
2877 }
2878 }
2879 break;
2880
2881 /* "ze" - scroll screen, cursor at the end */
2882 case 'e': if (!curwin->w_p_wrap)
2883 {
2884#ifdef FEAT_FOLDING
2885 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
2886 col = 0; /* like the cursor is in col 0 */
2887 else
2888#endif
2889 getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
Bram Moolenaar02631462017-09-22 15:20:32 +02002890 n = curwin->w_width - curwin_col_off();
Bram Moolenaar375e3392019-01-31 18:26:10 +01002891 if ((long)col + siso < n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 col = 0;
2893 else
Bram Moolenaar375e3392019-01-31 18:26:10 +01002894 col = col + siso - n + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 if (curwin->w_leftcol != col)
2896 {
2897 curwin->w_leftcol = col;
2898 redraw_later(NOT_VALID);
2899 }
2900 }
2901 break;
2902
2903#ifdef FEAT_FOLDING
2904 /* "zF": create fold command */
2905 /* "zf": create fold operator */
2906 case 'F':
2907 case 'f': if (foldManualAllowed(TRUE))
2908 {
2909 cap->nchar = 'f';
2910 nv_operator(cap);
2911 curwin->w_p_fen = TRUE;
2912
2913 /* "zF" is like "zfzf" */
2914 if (nchar == 'F' && cap->oap->op_type == OP_FOLD)
2915 {
2916 nv_operator(cap);
2917 finish_op = TRUE;
2918 }
2919 }
2920 else
2921 clearopbeep(cap->oap);
2922 break;
2923
2924 /* "zd": delete fold at cursor */
2925 /* "zD": delete fold at cursor recursively */
2926 case 'd':
2927 case 'D': if (foldManualAllowed(FALSE))
2928 {
2929 if (VIsual_active)
2930 nv_operator(cap);
2931 else
2932 deleteFold(curwin->w_cursor.lnum,
2933 curwin->w_cursor.lnum, nchar == 'D', FALSE);
2934 }
2935 break;
2936
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02002937 /* "zE": erase all folds */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938 case 'E': if (foldmethodIsManual(curwin))
2939 {
2940 clearFolding(curwin);
2941 changed_window_setting();
2942 }
2943 else if (foldmethodIsMarker(curwin))
2944 deleteFold((linenr_T)1, curbuf->b_ml.ml_line_count,
2945 TRUE, FALSE);
2946 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002947 emsg(_("E352: Cannot erase folds with current 'foldmethod'"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002948 break;
2949
2950 /* "zn": fold none: reset 'foldenable' */
2951 case 'n': curwin->w_p_fen = FALSE;
2952 break;
2953
2954 /* "zN": fold Normal: set 'foldenable' */
2955 case 'N': curwin->w_p_fen = TRUE;
2956 break;
2957
2958 /* "zi": invert folding: toggle 'foldenable' */
2959 case 'i': curwin->w_p_fen = !curwin->w_p_fen;
2960 break;
2961
2962 /* "za": open closed fold or close open fold at cursor */
2963 case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
2964 openFold(curwin->w_cursor.lnum, cap->count1);
2965 else
2966 {
2967 closeFold(curwin->w_cursor.lnum, cap->count1);
2968 curwin->w_p_fen = TRUE;
2969 }
2970 break;
2971
2972 /* "zA": open fold at cursor recursively */
2973 case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
2974 openFoldRecurse(curwin->w_cursor.lnum);
2975 else
2976 {
2977 closeFoldRecurse(curwin->w_cursor.lnum);
2978 curwin->w_p_fen = TRUE;
2979 }
2980 break;
2981
2982 /* "zo": open fold at cursor or Visual area */
2983 case 'o': if (VIsual_active)
2984 nv_operator(cap);
2985 else
2986 openFold(curwin->w_cursor.lnum, cap->count1);
2987 break;
2988
2989 /* "zO": open fold recursively */
2990 case 'O': if (VIsual_active)
2991 nv_operator(cap);
2992 else
2993 openFoldRecurse(curwin->w_cursor.lnum);
2994 break;
2995
2996 /* "zc": close fold at cursor or Visual area */
2997 case 'c': if (VIsual_active)
2998 nv_operator(cap);
2999 else
3000 closeFold(curwin->w_cursor.lnum, cap->count1);
3001 curwin->w_p_fen = TRUE;
3002 break;
3003
3004 /* "zC": close fold recursively */
3005 case 'C': if (VIsual_active)
3006 nv_operator(cap);
3007 else
3008 closeFoldRecurse(curwin->w_cursor.lnum);
3009 curwin->w_p_fen = TRUE;
3010 break;
3011
3012 /* "zv": open folds at the cursor */
3013 case 'v': foldOpenCursor();
3014 break;
3015
3016 /* "zx": re-apply 'foldlevel' and open folds at the cursor */
3017 case 'x': curwin->w_p_fen = TRUE;
Bram Moolenaar38ab0e22010-05-13 17:35:59 +02003018 curwin->w_foldinvalid = TRUE; /* recompute folds */
3019 newFoldLevel(); /* update right now */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003020 foldOpenCursor();
3021 break;
3022
3023 /* "zX": undo manual opens/closes, re-apply 'foldlevel' */
3024 case 'X': curwin->w_p_fen = TRUE;
Bram Moolenaar38ab0e22010-05-13 17:35:59 +02003025 curwin->w_foldinvalid = TRUE; /* recompute folds */
3026 old_fdl = -1; /* force an update */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003027 break;
3028
3029 /* "zm": fold more */
3030 case 'm': if (curwin->w_p_fdl > 0)
Bram Moolenaar7d2757a2015-03-31 17:46:22 +02003031 {
3032 curwin->w_p_fdl -= cap->count1;
3033 if (curwin->w_p_fdl < 0)
3034 curwin->w_p_fdl = 0;
3035 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003036 old_fdl = -1; /* force an update */
3037 curwin->w_p_fen = TRUE;
3038 break;
3039
3040 /* "zM": close all folds */
3041 case 'M': curwin->w_p_fdl = 0;
3042 old_fdl = -1; /* force an update */
3043 curwin->w_p_fen = TRUE;
3044 break;
3045
3046 /* "zr": reduce folding */
Bram Moolenaar7d2757a2015-03-31 17:46:22 +02003047 case 'r': curwin->w_p_fdl += cap->count1;
3048 {
3049 int d = getDeepestNesting();
3050
3051 if (curwin->w_p_fdl >= d)
3052 curwin->w_p_fdl = d;
3053 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054 break;
3055
3056 /* "zR": open all folds */
3057 case 'R': curwin->w_p_fdl = getDeepestNesting();
3058 old_fdl = -1; /* force an update */
3059 break;
3060
3061 case 'j': /* "zj" move to next fold downwards */
3062 case 'k': /* "zk" move to next fold upwards */
3063 if (foldMoveTo(TRUE, nchar == 'j' ? FORWARD : BACKWARD,
3064 cap->count1) == FAIL)
3065 clearopbeep(cap->oap);
3066 break;
3067
3068#endif /* FEAT_FOLDING */
3069
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00003070#ifdef FEAT_SPELL
Bram Moolenaard0131a82006-03-04 21:46:13 +00003071 case 'u': /* "zug" and "zuw": undo "zg" and "zw" */
3072 ++no_mapping;
3073 ++allow_keys; /* no mapping for nchar, but allow key codes */
Bram Moolenaar61abfd12007-09-13 16:26:47 +00003074 nchar = plain_vgetc();
Bram Moolenaard0131a82006-03-04 21:46:13 +00003075 LANGMAP_ADJUST(nchar, TRUE);
Bram Moolenaard0131a82006-03-04 21:46:13 +00003076 --no_mapping;
3077 --allow_keys;
3078#ifdef FEAT_CMDL_INFO
3079 (void)add_to_showcmd(nchar);
3080#endif
3081 if (vim_strchr((char_u *)"gGwW", nchar) == NULL)
3082 {
3083 clearopbeep(cap->oap);
3084 break;
3085 }
3086 undo = TRUE;
Bram Moolenaar2f40d122017-10-24 21:49:36 +02003087 /* FALLTHROUGH */
Bram Moolenaard0131a82006-03-04 21:46:13 +00003088
Bram Moolenaarb765d632005-06-07 21:00:02 +00003089 case 'g': /* "zg": add good word to word list */
3090 case 'w': /* "zw": add wrong word to word list */
Bram Moolenaar7887d882005-07-01 22:33:52 +00003091 case 'G': /* "zG": add good word to temp word list */
3092 case 'W': /* "zW": add wrong word to temp word list */
Bram Moolenaarb765d632005-06-07 21:00:02 +00003093 {
3094 char_u *ptr = NULL;
3095 int len;
3096
3097 if (checkclearop(cap->oap))
3098 break;
Bram Moolenaarb765d632005-06-07 21:00:02 +00003099 if (VIsual_active && get_visual_text(cap, &ptr, &len)
3100 == FAIL)
3101 return;
Bram Moolenaarda2303d2005-08-30 21:55:26 +00003102 if (ptr == NULL)
3103 {
3104 pos_T pos = curwin->w_cursor;
Bram Moolenaarda2303d2005-08-30 21:55:26 +00003105
Bram Moolenaar134bf072013-09-25 18:54:24 +02003106 /* Find bad word under the cursor. When 'spell' is
3107 * off this fails and find_ident_under_cursor() is
3108 * used below. */
3109 emsg_off++;
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003110 len = spell_move_to(curwin, FORWARD, TRUE, TRUE, NULL);
Bram Moolenaar134bf072013-09-25 18:54:24 +02003111 emsg_off--;
Bram Moolenaarda2303d2005-08-30 21:55:26 +00003112 if (len != 0 && curwin->w_cursor.col <= pos.col)
3113 ptr = ml_get_pos(&curwin->w_cursor);
3114 curwin->w_cursor = pos;
3115 }
3116
Bram Moolenaarb765d632005-06-07 21:00:02 +00003117 if (ptr == NULL && (len = find_ident_under_cursor(&ptr,
3118 FIND_IDENT)) == 0)
3119 return;
Bram Moolenaar08cc3742019-08-11 22:51:14 +02003120 spell_add_word(ptr, len, nchar == 'w' || nchar == 'W'
3121 ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
Bram Moolenaard0131a82006-03-04 21:46:13 +00003122 (nchar == 'G' || nchar == 'W')
3123 ? 0 : (int)cap->count1,
3124 undo);
Bram Moolenaarb765d632005-06-07 21:00:02 +00003125 }
Bram Moolenaar3982c542005-06-08 21:56:31 +00003126 break;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00003127
Bram Moolenaar43abc522005-12-10 20:15:02 +00003128 case '=': /* "z=": suggestions for a badly spelled word */
Bram Moolenaar66fa2712006-01-22 23:22:22 +00003129 if (!checkclearop(cap->oap))
Bram Moolenaard12a1322005-08-21 22:08:24 +00003130 spell_suggest((int)cap->count0);
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00003131 break;
Bram Moolenaarb765d632005-06-07 21:00:02 +00003132#endif
3133
Bram Moolenaar071d4272004-06-13 20:20:40 +00003134 default: clearopbeep(cap->oap);
3135 }
3136
3137#ifdef FEAT_FOLDING
3138 /* Redraw when 'foldenable' changed */
3139 if (old_fen != curwin->w_p_fen)
3140 {
3141# ifdef FEAT_DIFF
3142 win_T *wp;
3143
3144 if (foldmethodIsDiff(curwin) && curwin->w_p_scb)
3145 {
3146 /* Adjust 'foldenable' in diff-synced windows. */
3147 FOR_ALL_WINDOWS(wp)
3148 {
3149 if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb)
3150 {
3151 wp->w_p_fen = curwin->w_p_fen;
3152 changed_window_setting_win(wp);
3153 }
3154 }
3155 }
3156# endif
3157 changed_window_setting();
3158 }
3159
3160 /* Redraw when 'foldlevel' changed. */
3161 if (old_fdl != curwin->w_p_fdl)
3162 newFoldLevel();
3163#endif
3164}
3165
3166#ifdef FEAT_GUI
3167/*
3168 * Vertical scrollbar movement.
3169 */
3170 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003171nv_ver_scrollbar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003172{
3173 if (cap->oap->op_type != OP_NOP)
3174 clearopbeep(cap->oap);
3175
3176 /* Even if an operator was pending, we still want to scroll */
3177 gui_do_scroll();
3178}
3179
3180/*
3181 * Horizontal scrollbar movement.
3182 */
3183 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003184nv_hor_scrollbar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185{
3186 if (cap->oap->op_type != OP_NOP)
3187 clearopbeep(cap->oap);
3188
3189 /* Even if an operator was pending, we still want to scroll */
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +02003190 gui_do_horiz_scroll(scrollbar_value, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003191}
3192#endif
3193
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003194#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003195/*
3196 * Click in GUI tab.
3197 */
3198 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003199nv_tabline(cmdarg_T *cap)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003200{
3201 if (cap->oap->op_type != OP_NOP)
3202 clearopbeep(cap->oap);
3203
3204 /* Even if an operator was pending, we still want to jump tabs. */
3205 goto_tabpage(current_tab);
3206}
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003207
3208/*
3209 * Selected item in tab line menu.
3210 */
3211 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003212nv_tabmenu(cmdarg_T *cap)
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003213{
3214 if (cap->oap->op_type != OP_NOP)
3215 clearopbeep(cap->oap);
3216
3217 /* Even if an operator was pending, we still want to jump tabs. */
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003218 handle_tabmenu();
3219}
3220
3221/*
3222 * Handle selecting an item of the GUI tab line menu.
3223 * Used in Normal and Insert mode.
3224 */
3225 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003226handle_tabmenu(void)
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003227{
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003228 switch (current_tabmenu)
3229 {
3230 case TABLINE_MENU_CLOSE:
3231 if (current_tab == 0)
3232 do_cmdline_cmd((char_u *)"tabclose");
3233 else
3234 {
3235 vim_snprintf((char *)IObuff, IOSIZE, "tabclose %d",
3236 current_tab);
3237 do_cmdline_cmd(IObuff);
3238 }
3239 break;
3240
3241 case TABLINE_MENU_NEW:
Bram Moolenaardfd76912015-02-27 15:03:58 +01003242 if (current_tab == 0)
3243 do_cmdline_cmd((char_u *)"$tabnew");
3244 else
3245 {
3246 vim_snprintf((char *)IObuff, IOSIZE, "%dtabnew",
3247 current_tab - 1);
3248 do_cmdline_cmd(IObuff);
3249 }
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003250 break;
3251
3252 case TABLINE_MENU_OPEN:
Bram Moolenaardfd76912015-02-27 15:03:58 +01003253 if (current_tab == 0)
3254 do_cmdline_cmd((char_u *)"browse $tabnew");
3255 else
3256 {
3257 vim_snprintf((char *)IObuff, IOSIZE, "browse %dtabnew",
3258 current_tab - 1);
3259 do_cmdline_cmd(IObuff);
3260 }
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003261 break;
3262 }
3263}
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003264#endif
3265
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266/*
3267 * "Q" command.
3268 */
3269 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003270nv_exmode(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271{
3272 /*
3273 * Ignore 'Q' in Visual mode, just give a beep.
3274 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275 if (VIsual_active)
Bram Moolenaar165bc692015-07-21 17:53:25 +02003276 vim_beep(BO_EX);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003277 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278 do_exmode(FALSE);
3279}
3280
3281/*
3282 * Handle a ":" command.
3283 */
3284 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003285nv_colon(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003286{
3287 int old_p_im;
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003288 int cmd_result;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003289
Bram Moolenaar071d4272004-06-13 20:20:40 +00003290 if (VIsual_active)
3291 nv_operator(cap);
3292 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293 {
3294 if (cap->oap->op_type != OP_NOP)
3295 {
3296 /* Using ":" as a movement is characterwise exclusive. */
3297 cap->oap->motion_type = MCHAR;
3298 cap->oap->inclusive = FALSE;
3299 }
3300 else if (cap->count0)
3301 {
3302 /* translate "count:" into ":.,.+(count - 1)" */
3303 stuffcharReadbuff('.');
3304 if (cap->count0 > 1)
3305 {
3306 stuffReadbuff((char_u *)",.+");
3307 stuffnumReadbuff((long)cap->count0 - 1L);
3308 }
3309 }
3310
3311 /* When typing, don't type below an old message */
3312 if (KeyTyped)
3313 compute_cmdrow();
3314
3315 old_p_im = p_im;
3316
3317 /* get a command line and execute it */
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003318 cmd_result = do_cmdline(NULL, getexline, NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319 cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
3320
3321 /* If 'insertmode' changed, enter or exit Insert mode */
3322 if (p_im != old_p_im)
3323 {
3324 if (p_im)
3325 restart_edit = 'i';
3326 else
3327 restart_edit = 0;
3328 }
3329
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003330 if (cmd_result == FAIL)
3331 /* The Ex command failed, do not execute the operator. */
3332 clearop(cap->oap);
3333 else if (cap->oap->op_type != OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003334 && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
3335 || cap->oap->start.col >
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003336 (colnr_T)STRLEN(ml_get(cap->oap->start.lnum))
3337 || did_emsg
3338 ))
3339 /* The start of the operator has become invalid by the Ex command.
3340 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341 clearopbeep(cap->oap);
3342 }
3343}
3344
3345/*
3346 * Handle CTRL-G command.
3347 */
3348 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003349nv_ctrlg(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351 if (VIsual_active) /* toggle Selection/Visual mode */
3352 {
3353 VIsual_select = !VIsual_select;
3354 showmode();
3355 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003356 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 /* print full name if count given or :cd used */
3358 fileinfo((int)cap->count0, FALSE, TRUE);
3359}
3360
3361/*
3362 * Handle CTRL-H <Backspace> command.
3363 */
3364 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003365nv_ctrlh(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 if (VIsual_active && VIsual_select)
3368 {
3369 cap->cmdchar = 'x'; /* BS key behaves like 'x' in Select mode */
3370 v_visop(cap);
3371 }
3372 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 nv_left(cap);
3374}
3375
3376/*
3377 * CTRL-L: clear screen and redraw.
3378 */
3379 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003380nv_clear(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381{
3382 if (!checkclearop(cap->oap))
3383 {
3384#if defined(__BEOS__) && !USE_THREAD_FOR_INPUT_WITH_TIMEOUT
3385 /*
3386 * Right now, the BeBox doesn't seem to have an easy way to detect
3387 * window resizing, so we cheat and make the user detect it
3388 * manually with CTRL-L instead
3389 */
3390 ui_get_shellsize();
3391#endif
3392#ifdef FEAT_SYN_HL
3393 /* Clear all syntax states to force resyncing. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003394 syn_stack_free_all(curwin->w_s);
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003395# ifdef FEAT_RELTIME
3396 {
3397 win_T *wp;
3398
3399 FOR_ALL_WINDOWS(wp)
3400 wp->w_s->b_syn_slow = FALSE;
3401 }
3402# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003403#endif
3404 redraw_later(CLEAR);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003405#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3406# ifdef VIMDLL
3407 if (!gui.in_use)
3408# endif
3409 resize_console_buf();
Bram Moolenaar78d21da2019-02-17 15:00:52 +01003410#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411 }
3412}
3413
3414/*
3415 * CTRL-O: In Select mode: switch to Visual mode for one command.
3416 * Otherwise: Go to older pcmark.
3417 */
3418 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003419nv_ctrlo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421 if (VIsual_active && VIsual_select)
3422 {
3423 VIsual_select = FALSE;
3424 showmode();
3425 restart_VIsual_select = 2; /* restart Select mode later */
3426 }
3427 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428 {
3429 cap->count1 = -cap->count1;
3430 nv_pcmark(cap);
3431 }
3432}
3433
3434/*
Bram Moolenaar1bbb6192018-11-10 16:02:01 +01003435 * CTRL-^ command, short for ":e #". Works even when the alternate buffer is
3436 * not named.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003437 */
3438 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003439nv_hat(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440{
3441 if (!checkclearopq(cap->oap))
3442 (void)buflist_getfile((int)cap->count0, (linenr_T)0,
3443 GETF_SETMARK|GETF_ALT, FALSE);
3444}
3445
3446/*
3447 * "Z" commands.
3448 */
3449 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003450nv_Zet(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451{
3452 if (!checkclearopq(cap->oap))
3453 {
3454 switch (cap->nchar)
3455 {
3456 /* "ZZ": equivalent to ":x". */
3457 case 'Z': do_cmdline_cmd((char_u *)"x");
3458 break;
3459
3460 /* "ZQ": equivalent to ":q!" (Elvis compatible). */
3461 case 'Q': do_cmdline_cmd((char_u *)"q!");
3462 break;
3463
3464 default: clearopbeep(cap->oap);
3465 }
3466 }
3467}
3468
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469/*
3470 * Call nv_ident() as if "c1" was used, with "c2" as next character.
3471 */
3472 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003473do_nv_ident(int c1, int c2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474{
3475 oparg_T oa;
3476 cmdarg_T ca;
3477
3478 clear_oparg(&oa);
3479 vim_memset(&ca, 0, sizeof(ca));
3480 ca.oap = &oa;
3481 ca.cmdchar = c1;
3482 ca.nchar = c2;
3483 nv_ident(&ca);
3484}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485
3486/*
3487 * Handle the commands that use the word under the cursor.
3488 * [g] CTRL-] :ta to current identifier
3489 * [g] 'K' run program for current identifier
3490 * [g] '*' / to current identifier or string
3491 * [g] '#' ? to current identifier or string
3492 * g ']' :tselect for current identifier
3493 */
3494 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003495nv_ident(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496{
3497 char_u *ptr = NULL;
3498 char_u *buf;
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003499 unsigned buflen;
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003500 char_u *newbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 char_u *p;
3502 char_u *kp; /* value of 'keywordprg' */
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003503 int kp_help; /* 'keywordprg' is ":he" */
3504 int kp_ex; /* 'keywordprg' starts with ":" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505 int n = 0; /* init for GCC */
3506 int cmdchar;
3507 int g_cmd; /* "g" command */
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003508 int tag_cmd = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 char_u *aux_ptr;
3510 int isman;
3511 int isman_s;
3512
3513 if (cap->cmdchar == 'g') /* "g*", "g#", "g]" and "gCTRL-]" */
3514 {
3515 cmdchar = cap->nchar;
3516 g_cmd = TRUE;
3517 }
3518 else
3519 {
3520 cmdchar = cap->cmdchar;
3521 g_cmd = FALSE;
3522 }
3523
3524 if (cmdchar == POUND) /* the pound sign, '#' for English keyboards */
3525 cmdchar = '#';
3526
3527 /*
3528 * The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
3529 */
3530 if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K')
3531 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532 if (VIsual_active && get_visual_text(cap, &ptr, &n) == FAIL)
3533 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003534 if (checkclearopq(cap->oap))
3535 return;
3536 }
3537
3538 if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
3539 (cmdchar == '*' || cmdchar == '#')
3540 ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
3541 {
3542 clearop(cap->oap);
3543 return;
3544 }
3545
3546 /* Allocate buffer to put the command in. Inserting backslashes can
3547 * double the length of the word. p_kp / curbuf->b_p_kp could be added
3548 * and some numbers. */
3549 kp = (*curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp);
3550 kp_help = (*kp == NUL || STRCMP(kp, ":he") == 0
3551 || STRCMP(kp, ":help") == 0);
Bram Moolenaara4f99f52017-08-26 16:25:32 +02003552 if (kp_help && *skipwhite(ptr) == NUL)
3553 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003554 emsg(_(e_noident)); /* found white space only */
Bram Moolenaara4f99f52017-08-26 16:25:32 +02003555 return;
3556 }
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003557 kp_ex = (*kp == ':');
3558 buflen = (unsigned)(n * 2 + 30 + STRLEN(kp));
3559 buf = alloc(buflen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003560 if (buf == NULL)
3561 return;
3562 buf[0] = NUL;
3563
3564 switch (cmdchar)
3565 {
3566 case '*':
3567 case '#':
3568 /*
3569 * Put cursor at start of word, makes search skip the word
3570 * under the cursor.
3571 * Call setpcmark() first, so "*``" puts the cursor back where
3572 * it was.
3573 */
3574 setpcmark();
3575 curwin->w_cursor.col = (colnr_T) (ptr - ml_get_curline());
3576
3577 if (!g_cmd && vim_iswordp(ptr))
3578 STRCPY(buf, "\\<");
3579 no_smartcase = TRUE; /* don't use 'smartcase' now */
3580 break;
3581
3582 case 'K':
3583 if (kp_help)
3584 STRCPY(buf, "he! ");
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003585 else if (kp_ex)
3586 {
3587 if (cap->count0 != 0)
3588 vim_snprintf((char *)buf, buflen, "%s %ld",
3589 kp, cap->count0);
3590 else
3591 STRCPY(buf, kp);
3592 STRCAT(buf, " ");
3593 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003594 else
3595 {
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003596 /* An external command will probably use an argument starting
3597 * with "-" as an option. To avoid trouble we skip the "-". */
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003598 while (*ptr == '-' && n > 0)
3599 {
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003600 ++ptr;
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003601 --n;
3602 }
3603 if (n == 0)
3604 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003605 emsg(_(e_noident)); /* found dashes only */
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003606 vim_free(buf);
3607 return;
3608 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003609
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 /* When a count is given, turn it into a range. Is this
3611 * really what we want? */
3612 isman = (STRCMP(kp, "man") == 0);
3613 isman_s = (STRCMP(kp, "man -s") == 0);
3614 if (cap->count0 != 0 && !(isman || isman_s))
3615 sprintf((char *)buf, ".,.+%ld", cap->count0 - 1);
3616
3617 STRCAT(buf, "! ");
3618 if (cap->count0 == 0 && isman_s)
3619 STRCAT(buf, "man");
3620 else
3621 STRCAT(buf, kp);
3622 STRCAT(buf, " ");
3623 if (cap->count0 != 0 && (isman || isman_s))
3624 {
3625 sprintf((char *)buf + STRLEN(buf), "%ld", cap->count0);
3626 STRCAT(buf, " ");
3627 }
3628 }
3629 break;
3630
3631 case ']':
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003632 tag_cmd = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633#ifdef FEAT_CSCOPE
3634 if (p_cst)
3635 STRCPY(buf, "cstag ");
3636 else
3637#endif
3638 STRCPY(buf, "ts ");
3639 break;
3640
3641 default:
Bram Moolenaar97e7a842010-03-17 13:07:08 +01003642 tag_cmd = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643 if (curbuf->b_help)
3644 STRCPY(buf, "he! ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645 else
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003646 {
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003647 if (g_cmd)
3648 STRCPY(buf, "tj ");
3649 else
3650 sprintf((char *)buf, "%ldta ", cap->count0);
3651 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652 }
3653
3654 /*
3655 * Now grab the chars in the identifier
3656 */
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003657 if (cmdchar == 'K' && !kp_help)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658 {
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003659 ptr = vim_strnsave(ptr, n);
Bram Moolenaar426f3752016-11-04 21:22:37 +01003660 if (kp_ex)
3661 /* Escape the argument properly for an Ex command */
3662 p = vim_strsave_fnameescape(ptr, FALSE);
3663 else
3664 /* Escape the argument properly for a shell command */
3665 p = vim_strsave_shellescape(ptr, TRUE, TRUE);
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003666 vim_free(ptr);
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003667 if (p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668 {
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003669 vim_free(buf);
3670 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003672 newbuf = vim_realloc(buf, STRLEN(buf) + STRLEN(p) + 1);
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003673 if (newbuf == NULL)
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003674 {
3675 vim_free(buf);
3676 vim_free(p);
3677 return;
3678 }
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003679 buf = newbuf;
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003680 STRCAT(buf, p);
3681 vim_free(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003683 else
3684 {
3685 if (cmdchar == '*')
3686 aux_ptr = (char_u *)(p_magic ? "/.*~[^$\\" : "/^$\\");
3687 else if (cmdchar == '#')
3688 aux_ptr = (char_u *)(p_magic ? "/?.*~[^$\\" : "/?^$\\");
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003689 else if (tag_cmd)
Bram Moolenaar77a0aa42010-10-13 18:06:47 +02003690 {
3691 if (curbuf->b_help)
3692 /* ":help" handles unescaped argument */
3693 aux_ptr = (char_u *)"";
3694 else
3695 aux_ptr = (char_u *)"\\|\"\n[";
3696 }
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003697 else
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003698 aux_ptr = (char_u *)"\\|\"\n*?[";
3699
3700 p = buf + STRLEN(buf);
3701 while (n-- > 0)
3702 {
3703 /* put a backslash before \ and some others */
3704 if (vim_strchr(aux_ptr, *ptr) != NULL)
3705 *p++ = '\\';
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003706 /* When current byte is a part of multibyte character, copy all
3707 * bytes of that character. */
3708 if (has_mbyte)
3709 {
3710 int i;
3711 int len = (*mb_ptr2len)(ptr) - 1;
3712
3713 for (i = 0; i < len && n >= 1; ++i, --n)
3714 *p++ = *ptr++;
3715 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003716 *p++ = *ptr++;
3717 }
3718 *p = NUL;
3719 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720
3721 /*
3722 * Execute the command.
3723 */
3724 if (cmdchar == '*' || cmdchar == '#')
3725 {
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003726 if (!g_cmd && (has_mbyte
3727 ? vim_iswordp(mb_prevptr(ml_get_curline(), ptr))
3728 : vim_iswordc(ptr[-1])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 STRCAT(buf, "\\>");
Bram Moolenaard7663c22019-08-06 21:59:57 +02003730
3731 // put pattern in search history
Bram Moolenaarc7be3f32009-12-24 14:01:12 +00003732 init_history();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 add_to_history(HIST_SEARCH, buf, TRUE, NUL);
Bram Moolenaard7663c22019-08-06 21:59:57 +02003734
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02003735 (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 }
3737 else
Bram Moolenaar45e18cb2019-04-28 18:05:35 +02003738 {
3739 g_tag_at_cursor = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003740 do_cmdline_cmd(buf);
Bram Moolenaar45e18cb2019-04-28 18:05:35 +02003741 g_tag_at_cursor = FALSE;
3742 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743
3744 vim_free(buf);
3745}
3746
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747/*
3748 * Get visually selected text, within one line only.
3749 * Returns FAIL if more than one line selected.
3750 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00003751 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01003752get_visual_text(
3753 cmdarg_T *cap,
3754 char_u **pp, /* return: start of selected text */
3755 int *lenp) /* return: length of selected text */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756{
3757 if (VIsual_mode != 'V')
3758 unadjust_for_sel();
3759 if (VIsual.lnum != curwin->w_cursor.lnum)
3760 {
Bram Moolenaard857f0e2005-06-21 22:37:39 +00003761 if (cap != NULL)
3762 clearopbeep(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 return FAIL;
3764 }
3765 if (VIsual_mode == 'V')
3766 {
3767 *pp = ml_get_curline();
3768 *lenp = (int)STRLEN(*pp);
3769 }
3770 else
3771 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003772 if (LT_POS(curwin->w_cursor, VIsual))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003773 {
3774 *pp = ml_get_pos(&curwin->w_cursor);
3775 *lenp = VIsual.col - curwin->w_cursor.col + 1;
3776 }
3777 else
3778 {
3779 *pp = ml_get_pos(&VIsual);
3780 *lenp = curwin->w_cursor.col - VIsual.col + 1;
3781 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 if (has_mbyte)
3783 /* Correct the length to include the whole last character. */
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003784 *lenp += (*mb_ptr2len)(*pp + (*lenp - 1)) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003785 }
3786 reset_VIsual_and_resel();
3787 return OK;
3788}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003789
3790/*
3791 * CTRL-T: backwards in tag stack
3792 */
3793 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003794nv_tagpop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003795{
3796 if (!checkclearopq(cap->oap))
3797 do_tag((char_u *)"", DT_POP, (int)cap->count1, FALSE, TRUE);
3798}
3799
3800/*
3801 * Handle scrolling command 'H', 'L' and 'M'.
3802 */
3803 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003804nv_scroll(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003805{
3806 int used = 0;
3807 long n;
3808#ifdef FEAT_FOLDING
3809 linenr_T lnum;
3810#endif
3811 int half;
3812
3813 cap->oap->motion_type = MLINE;
3814 setpcmark();
3815
3816 if (cap->cmdchar == 'L')
3817 {
3818 validate_botline(); /* make sure curwin->w_botline is valid */
3819 curwin->w_cursor.lnum = curwin->w_botline - 1;
3820 if (cap->count1 - 1 >= curwin->w_cursor.lnum)
3821 curwin->w_cursor.lnum = 1;
3822 else
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003823 {
3824#ifdef FEAT_FOLDING
3825 if (hasAnyFolding(curwin))
3826 {
3827 /* Count a fold for one screen line. */
3828 for (n = cap->count1 - 1; n > 0
3829 && curwin->w_cursor.lnum > curwin->w_topline; --n)
3830 {
3831 (void)hasFolding(curwin->w_cursor.lnum,
3832 &curwin->w_cursor.lnum, NULL);
3833 --curwin->w_cursor.lnum;
3834 }
3835 }
3836 else
3837#endif
3838 curwin->w_cursor.lnum -= cap->count1 - 1;
3839 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 }
3841 else
3842 {
3843 if (cap->cmdchar == 'M')
3844 {
3845#ifdef FEAT_DIFF
3846 /* Don't count filler lines above the window. */
3847 used -= diff_check_fill(curwin, curwin->w_topline)
3848 - curwin->w_topfill;
3849#endif
3850 validate_botline(); /* make sure w_empty_rows is valid */
3851 half = (curwin->w_height - curwin->w_empty_rows + 1) / 2;
3852 for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
3853 {
3854#ifdef FEAT_DIFF
3855 /* Count half he number of filler lines to be "below this
3856 * line" and half to be "above the next line". */
3857 if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline
3858 + n) / 2 >= half)
3859 {
3860 --n;
3861 break;
3862 }
3863#endif
3864 used += plines(curwin->w_topline + n);
3865 if (used >= half)
3866 break;
3867#ifdef FEAT_FOLDING
3868 if (hasFolding(curwin->w_topline + n, NULL, &lnum))
3869 n = lnum - curwin->w_topline;
3870#endif
3871 }
3872 if (n > 0 && used > curwin->w_height)
3873 --n;
3874 }
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003875 else /* (cap->cmdchar == 'H') */
3876 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877 n = cap->count1 - 1;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003878#ifdef FEAT_FOLDING
3879 if (hasAnyFolding(curwin))
3880 {
3881 /* Count a fold for one screen line. */
3882 lnum = curwin->w_topline;
3883 while (n-- > 0 && lnum < curwin->w_botline - 1)
3884 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02003885 (void)hasFolding(lnum, NULL, &lnum);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003886 ++lnum;
3887 }
3888 n = lnum - curwin->w_topline;
3889 }
3890#endif
3891 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003892 curwin->w_cursor.lnum = curwin->w_topline + n;
3893 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
3894 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
3895 }
3896
Bram Moolenaar44cc4cf2017-10-15 22:13:37 +02003897 /* Correct for 'so', except when an operator is pending. */
3898 if (cap->oap->op_type == OP_NOP)
3899 cursor_correct();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 beginline(BL_SOL | BL_FIX);
3901}
3902
3903/*
3904 * Cursor right commands.
3905 */
3906 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003907nv_right(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908{
3909 long n;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003910 int past_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00003912 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
3913 {
3914 /* <C-Right> and <S-Right> move a word or WORD right */
3915 if (mod_mask & MOD_MASK_CTRL)
3916 cap->arg = TRUE;
3917 nv_wordcmd(cap);
3918 return;
3919 }
3920
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 cap->oap->motion_type = MCHAR;
3922 cap->oap->inclusive = FALSE;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003923 past_line = (VIsual_active && *p_sel != 'o');
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 /*
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003926 * In virtual edit mode, there's no such thing as "past_line", as lines
3927 * are (theoretically) infinitely long.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 */
3929 if (virtual_active())
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003930 past_line = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003931
3932 for (n = cap->count1; n > 0; --n)
3933 {
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003934 if ((!past_line && oneright() == FAIL)
3935 || (past_line && *ml_get_cursor() == NUL)
Bram Moolenaar78a15312009-05-15 19:33:18 +00003936 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 {
3938 /*
Bram Moolenaar362e1a32006-03-06 23:29:24 +00003939 * <Space> wraps to next line if 'whichwrap' has 's'.
3940 * 'l' wraps to next line if 'whichwrap' has 'l'.
3941 * CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003942 */
3943 if ( ((cap->cmdchar == ' '
3944 && vim_strchr(p_ww, 's') != NULL)
3945 || (cap->cmdchar == 'l'
3946 && vim_strchr(p_ww, 'l') != NULL)
Bram Moolenaara88d9682005-03-25 21:45:43 +00003947 || (cap->cmdchar == K_RIGHT
Bram Moolenaar071d4272004-06-13 20:20:40 +00003948 && vim_strchr(p_ww, '>') != NULL))
3949 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
3950 {
3951 /* When deleting we also count the NL as a character.
3952 * Set cap->oap->inclusive when last char in the line is
3953 * included, move to next line after that */
Bram Moolenaar362e1a32006-03-06 23:29:24 +00003954 if ( cap->oap->op_type != OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955 && !cap->oap->inclusive
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003956 && !LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003957 cap->oap->inclusive = TRUE;
3958 else
3959 {
3960 ++curwin->w_cursor.lnum;
3961 curwin->w_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 curwin->w_set_curswant = TRUE;
3964 cap->oap->inclusive = FALSE;
3965 }
3966 continue;
3967 }
3968 if (cap->oap->op_type == OP_NOP)
3969 {
3970 /* Only beep and flush if not moved at all */
3971 if (n == cap->count1)
3972 beep_flush();
3973 }
3974 else
3975 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003976 if (!LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003977 cap->oap->inclusive = TRUE;
3978 }
3979 break;
3980 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003981 else if (past_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982 {
3983 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 if (virtual_active())
3985 oneright();
3986 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988 if (has_mbyte)
Bram Moolenaar56ebbab2019-09-20 13:40:14 +02003989 curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor());
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 ++curwin->w_cursor.col;
3992 }
3993 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994 }
3995#ifdef FEAT_FOLDING
3996 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
3997 && cap->oap->op_type == OP_NOP)
3998 foldOpenCursor();
3999#endif
4000}
4001
4002/*
4003 * Cursor left commands.
4004 *
4005 * Returns TRUE when operator end should not be adjusted.
4006 */
4007 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004008nv_left(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004009{
4010 long n;
4011
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004012 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
4013 {
4014 /* <C-Left> and <S-Left> move a word or WORD left */
4015 if (mod_mask & MOD_MASK_CTRL)
4016 cap->arg = 1;
4017 nv_bck_word(cap);
4018 return;
4019 }
4020
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021 cap->oap->motion_type = MCHAR;
4022 cap->oap->inclusive = FALSE;
4023 for (n = cap->count1; n > 0; --n)
4024 {
4025 if (oneleft() == FAIL)
4026 {
4027 /* <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
4028 * 'h' wraps to previous line if 'whichwrap' has 'h'.
4029 * CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
4030 */
4031 if ( (((cap->cmdchar == K_BS
4032 || cap->cmdchar == Ctrl_H)
4033 && vim_strchr(p_ww, 'b') != NULL)
4034 || (cap->cmdchar == 'h'
4035 && vim_strchr(p_ww, 'h') != NULL)
Bram Moolenaara88d9682005-03-25 21:45:43 +00004036 || (cap->cmdchar == K_LEFT
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037 && vim_strchr(p_ww, '<') != NULL))
4038 && curwin->w_cursor.lnum > 1)
4039 {
4040 --(curwin->w_cursor.lnum);
4041 coladvance((colnr_T)MAXCOL);
4042 curwin->w_set_curswant = TRUE;
4043
4044 /* When the NL before the first char has to be deleted we
4045 * put the cursor on the NUL after the previous line.
4046 * This is a very special case, be careful!
Bram Moolenaarad8958b2008-01-02 15:26:04 +00004047 * Don't adjust op_end now, otherwise it won't work. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048 if ( (cap->oap->op_type == OP_DELETE
4049 || cap->oap->op_type == OP_CHANGE)
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004050 && !LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 {
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004052 char_u *cp = ml_get_cursor();
4053
4054 if (*cp != NUL)
4055 {
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004056 if (has_mbyte)
4057 curwin->w_cursor.col += (*mb_ptr2len)(cp);
4058 else
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004059 ++curwin->w_cursor.col;
4060 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061 cap->retval |= CA_NO_ADJ_OP_END;
4062 }
4063 continue;
4064 }
4065 /* Only beep and flush if not moved at all */
4066 else if (cap->oap->op_type == OP_NOP && n == cap->count1)
4067 beep_flush();
4068 break;
4069 }
4070 }
4071#ifdef FEAT_FOLDING
4072 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
4073 && cap->oap->op_type == OP_NOP)
4074 foldOpenCursor();
4075#endif
4076}
4077
4078/*
4079 * Cursor up commands.
4080 * cap->arg is TRUE for "-": Move cursor to first non-blank.
4081 */
4082 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004083nv_up(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004085 if (mod_mask & MOD_MASK_SHIFT)
4086 {
4087 /* <S-Up> is page up */
4088 cap->arg = BACKWARD;
4089 nv_page(cap);
4090 }
4091 else
4092 {
4093 cap->oap->motion_type = MLINE;
4094 if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
4095 clearopbeep(cap->oap);
4096 else if (cap->arg)
4097 beginline(BL_WHITE | BL_FIX);
4098 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099}
4100
4101/*
4102 * Cursor down commands.
4103 * cap->arg is TRUE for CR and "+": Move cursor to first non-blank.
4104 */
4105 static void
Bram Moolenaar1b010052016-09-12 12:24:11 +02004106nv_down(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004108 if (mod_mask & MOD_MASK_SHIFT)
4109 {
4110 /* <S-Down> is page down */
4111 cap->arg = FORWARD;
4112 nv_page(cap);
4113 }
Bram Moolenaar4033c552017-09-16 20:54:51 +02004114#if defined(FEAT_QUICKFIX)
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004115 /* Quickfix window only: view the result under the cursor. */
4116 else if (bt_quickfix(curbuf) && cap->cmdchar == CAR)
4117 qf_view_result(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118#endif
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004119 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120 {
4121#ifdef FEAT_CMDWIN
4122 /* In the cmdline window a <CR> executes the command. */
Bram Moolenaar05159a02005-02-26 23:04:13 +00004123 if (cmdwin_type != 0 && cap->cmdchar == CAR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124 cmdwin_result = CAR;
4125 else
4126#endif
Bram Moolenaarf2732452018-06-03 14:47:35 +02004127#ifdef FEAT_JOB_CHANNEL
4128 /* In a prompt buffer a <CR> in the last line invokes the callback. */
4129 if (bt_prompt(curbuf) && cap->cmdchar == CAR
4130 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
4131 {
4132 invoke_prompt_callback();
4133 if (restart_edit == 0)
4134 restart_edit = 'a';
4135 }
4136 else
4137#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 {
4139 cap->oap->motion_type = MLINE;
4140 if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
4141 clearopbeep(cap->oap);
4142 else if (cap->arg)
4143 beginline(BL_WHITE | BL_FIX);
4144 }
4145 }
4146}
4147
4148#ifdef FEAT_SEARCHPATH
4149/*
4150 * Grab the file name under the cursor and edit it.
4151 */
4152 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004153nv_gotofile(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154{
4155 char_u *ptr;
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004156 linenr_T lnum = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00004158 if (text_locked())
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 {
4160 clearopbeep(cap->oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00004161 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 return;
4163 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004164 if (curbuf_locked())
4165 {
4166 clearop(cap->oap);
4167 return;
4168 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004169
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004170 ptr = grab_file_name(cap->count1, &lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171
4172 if (ptr != NULL)
4173 {
4174 /* do autowrite if necessary */
Bram Moolenaareb44a682017-08-03 22:44:55 +02004175 if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf))
Bram Moolenaarcde88542015-08-11 19:14:00 +02004176 (void)autowrite(curbuf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 setpcmark();
Bram Moolenaar2a79ed22017-05-24 09:51:39 +02004178 if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
Bram Moolenaareb44a682017-08-03 22:44:55 +02004179 buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
Bram Moolenaar2a79ed22017-05-24 09:51:39 +02004180 && cap->nchar == 'F' && lnum >= 0)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004181 {
4182 curwin->w_cursor.lnum = lnum;
4183 check_cursor_lnum();
4184 beginline(BL_SOL | BL_FIX);
4185 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186 vim_free(ptr);
4187 }
4188 else
4189 clearop(cap->oap);
4190}
4191#endif
4192
4193/*
4194 * <End> command: to end of current line or last line.
4195 */
4196 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004197nv_end(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004199 if (cap->arg || (mod_mask & MOD_MASK_CTRL)) /* CTRL-END = goto last line */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200 {
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004201 cap->arg = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004202 nv_goto(cap);
4203 cap->count1 = 1; /* to end of current line */
4204 }
4205 nv_dollar(cap);
4206}
4207
4208/*
4209 * Handle the "$" command.
4210 */
4211 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004212nv_dollar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213{
4214 cap->oap->motion_type = MCHAR;
4215 cap->oap->inclusive = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 /* In virtual mode when off the edge of a line and an operator
4217 * is pending (whew!) keep the cursor where it is.
4218 * Otherwise, send it to the end of the line. */
4219 if (!virtual_active() || gchar_cursor() != NUL
4220 || cap->oap->op_type == OP_NOP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 curwin->w_curswant = MAXCOL; /* so we stay at the end */
4222 if (cursor_down((long)(cap->count1 - 1),
4223 cap->oap->op_type == OP_NOP) == FAIL)
4224 clearopbeep(cap->oap);
4225#ifdef FEAT_FOLDING
4226 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
4227 foldOpenCursor();
4228#endif
4229}
4230
4231/*
4232 * Implementation of '?' and '/' commands.
4233 * If cap->arg is TRUE don't set PC mark.
4234 */
4235 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004236nv_search(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004237{
4238 oparg_T *oap = cap->oap;
Bram Moolenaardda933d2016-09-03 21:04:58 +02004239 pos_T save_cursor = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004240
4241 if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13)
4242 {
4243 /* Translate "g??" to "g?g?" */
4244 cap->cmdchar = 'g';
4245 cap->nchar = '?';
4246 nv_operator(cap);
4247 return;
4248 }
4249
Bram Moolenaardda933d2016-09-03 21:04:58 +02004250 /* When using 'incsearch' the cursor may be moved to set a different search
4251 * start position. */
Bram Moolenaare96a2492019-06-25 04:12:16 +02004252 cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253
4254 if (cap->searchbuf == NULL)
4255 {
4256 clearop(oap);
4257 return;
4258 }
4259
Bram Moolenaar46539112015-02-17 15:43:57 +01004260 (void)normal_search(cap, cap->cmdchar, cap->searchbuf,
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004261 (cap->arg || !EQUAL_POS(save_cursor, curwin->w_cursor))
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004262 ? 0 : SEARCH_MARK, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004263}
4264
4265/*
4266 * Handle "N" and "n" commands.
4267 * cap->arg is SEARCH_REV for "N", 0 for "n".
4268 */
4269 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004270nv_next(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271{
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004272 pos_T old = curwin->w_cursor;
4273 int wrapped = FALSE;
4274 int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, &wrapped);
Bram Moolenaar46539112015-02-17 15:43:57 +01004275
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004276 if (i == 1 && !wrapped && EQUAL_POS(old, curwin->w_cursor))
Bram Moolenaar46539112015-02-17 15:43:57 +01004277 {
4278 /* Avoid getting stuck on the current cursor position, which can
4279 * happen when an offset is given and the cursor is on the last char
4280 * in the buffer: Repeat with count + 1. */
4281 cap->count1 += 1;
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004282 (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL);
Bram Moolenaar46539112015-02-17 15:43:57 +01004283 cap->count1 -= 1;
4284 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004285}
4286
4287/*
4288 * Search for "pat" in direction "dir" ('/' or '?', 0 for repeat).
4289 * Uses only cap->count1 and cap->oap from "cap".
Bram Moolenaar46539112015-02-17 15:43:57 +01004290 * Return 0 for failure, 1 for found, 2 for found and line offset added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 */
Bram Moolenaar46539112015-02-17 15:43:57 +01004292 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01004293normal_search(
4294 cmdarg_T *cap,
4295 int dir,
4296 char_u *pat,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004297 int opt, // extra flags for do_search()
4298 int *wrapped)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299{
4300 int i;
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004301 searchit_arg_T sia;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302
4303 cap->oap->motion_type = MCHAR;
4304 cap->oap->inclusive = FALSE;
4305 cap->oap->use_reg_one = TRUE;
4306 curwin->w_set_curswant = TRUE;
4307
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004308 vim_memset(&sia, 0, sizeof(sia));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 i = do_search(cap->oap, dir, pat, cap->count1,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004310 opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
4311 if (wrapped != NULL)
4312 *wrapped = sia.sa_wrapped;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 if (i == 0)
4314 clearop(cap->oap);
4315 else
4316 {
4317 if (i == 2)
4318 cap->oap->motion_type = MLINE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320#ifdef FEAT_FOLDING
4321 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
4322 foldOpenCursor();
4323#endif
4324 }
4325
4326 /* "/$" will put the cursor after the end of the line, may need to
4327 * correct that here */
4328 check_cursor();
Bram Moolenaar46539112015-02-17 15:43:57 +01004329 return i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330}
4331
4332/*
4333 * Character search commands.
4334 * cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', TRUE for
4335 * ',' and FALSE for ';'.
4336 * cap->nchar is NUL for ',' and ';' (repeat the search)
4337 */
4338 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004339nv_csearch(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340{
4341 int t_cmd;
4342
4343 if (cap->cmdchar == 't' || cap->cmdchar == 'T')
4344 t_cmd = TRUE;
4345 else
4346 t_cmd = FALSE;
4347
4348 cap->oap->motion_type = MCHAR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349 if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == FAIL)
4350 clearopbeep(cap->oap);
4351 else
4352 {
4353 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354 /* Include a Tab for "tx" and for "dfx". */
4355 if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
4356 && (t_cmd || cap->oap->op_type != OP_NOP))
4357 {
4358 colnr_T scol, ecol;
4359
4360 getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
4361 curwin->w_cursor.coladd = ecol - scol;
4362 }
4363 else
4364 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366#ifdef FEAT_FOLDING
4367 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
4368 foldOpenCursor();
4369#endif
4370 }
4371}
4372
4373/*
4374 * "[" and "]" commands.
4375 * cap->arg is BACKWARD for "[" and FORWARD for "]".
4376 */
4377 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004378nv_brackets(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379{
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01004380 pos_T new_pos = {0, 0, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 pos_T prev_pos;
4382 pos_T *pos = NULL; /* init for GCC */
4383 pos_T old_pos; /* cursor position before command */
4384 int flag;
4385 long n;
4386 int findc;
4387 int c;
4388
4389 cap->oap->motion_type = MCHAR;
4390 cap->oap->inclusive = FALSE;
4391 old_pos = curwin->w_cursor;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004392 curwin->w_cursor.coladd = 0; // TODO: don't do this for an error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393
4394#ifdef FEAT_SEARCHPATH
4395 /*
4396 * "[f" or "]f" : Edit file under the cursor (same as "gf")
4397 */
4398 if (cap->nchar == 'f')
4399 nv_gotofile(cap);
4400 else
4401#endif
4402
4403#ifdef FEAT_FIND_ID
4404 /*
Bram Moolenaarf711faf2007-05-10 16:48:19 +00004405 * Find the occurrence(s) of the identifier or define under cursor
4406 * in current and included files or jump to the first occurrence.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004407 *
4408 * search list jump
4409 * fwd bwd fwd bwd fwd bwd
4410 * identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
4411 * define "]d" "[d" "]D" "[D" "]^D" "[^D"
4412 */
4413 if (vim_strchr((char_u *)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004414# ifdef EBCDIC
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 "iI\005dD\067",
Bram Moolenaar32526b32019-01-19 17:43:09 +01004416# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004417 "iI\011dD\004",
Bram Moolenaar32526b32019-01-19 17:43:09 +01004418# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004419 cap->nchar) != NULL)
4420 {
4421 char_u *ptr;
4422 int len;
4423
4424 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
4425 clearop(cap->oap);
4426 else
4427 {
4428 find_pattern_in_path(ptr, 0, len, TRUE,
4429 cap->count0 == 0 ? !isupper(cap->nchar) : FALSE,
4430 ((cap->nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
4431 cap->count1,
4432 isupper(cap->nchar) ? ACTION_SHOW_ALL :
4433 islower(cap->nchar) ? ACTION_SHOW : ACTION_GOTO,
4434 cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 : (linenr_T)1,
4435 (linenr_T)MAXLNUM);
4436 curwin->w_set_curswant = TRUE;
4437 }
4438 }
4439 else
4440#endif
4441
4442 /*
4443 * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
4444 * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
4445 * "[/", "[*", "]/", "]*": go to Nth comment start/end.
4446 * "[m" or "]m" search for prev/next start of (Java) method.
4447 * "[M" or "]M" search for prev/next end of (Java) method.
4448 */
4449 if ( (cap->cmdchar == '['
4450 && vim_strchr((char_u *)"{(*/#mM", cap->nchar) != NULL)
4451 || (cap->cmdchar == ']'
4452 && vim_strchr((char_u *)"})*/#mM", cap->nchar) != NULL))
4453 {
4454 if (cap->nchar == '*')
4455 cap->nchar = '/';
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456 prev_pos.lnum = 0;
4457 if (cap->nchar == 'm' || cap->nchar == 'M')
4458 {
4459 if (cap->cmdchar == '[')
4460 findc = '{';
4461 else
4462 findc = '}';
4463 n = 9999;
4464 }
4465 else
4466 {
4467 findc = cap->nchar;
4468 n = cap->count1;
4469 }
4470 for ( ; n > 0; --n)
4471 {
4472 if ((pos = findmatchlimit(cap->oap, findc,
4473 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
4474 {
4475 if (new_pos.lnum == 0) /* nothing found */
4476 {
4477 if (cap->nchar != 'm' && cap->nchar != 'M')
4478 clearopbeep(cap->oap);
4479 }
4480 else
4481 pos = &new_pos; /* use last one found */
4482 break;
4483 }
4484 prev_pos = new_pos;
4485 curwin->w_cursor = *pos;
4486 new_pos = *pos;
4487 }
4488 curwin->w_cursor = old_pos;
4489
4490 /*
4491 * Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
4492 * brought us to the match for "[m" and "]M" when inside a method.
4493 * Try finding the '{' or '}' we want to be at.
4494 * Also repeat for the given count.
4495 */
4496 if (cap->nchar == 'm' || cap->nchar == 'M')
4497 {
4498 /* norm is TRUE for "]M" and "[m" */
4499 int norm = ((findc == '{') == (cap->nchar == 'm'));
4500
4501 n = cap->count1;
4502 /* found a match: we were inside a method */
4503 if (prev_pos.lnum != 0)
4504 {
4505 pos = &prev_pos;
4506 curwin->w_cursor = prev_pos;
4507 if (norm)
4508 --n;
4509 }
4510 else
4511 pos = NULL;
4512 while (n > 0)
4513 {
4514 for (;;)
4515 {
4516 if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0)
4517 {
4518 /* if not found anything, that's an error */
4519 if (pos == NULL)
4520 clearopbeep(cap->oap);
4521 n = 0;
4522 break;
4523 }
4524 c = gchar_cursor();
4525 if (c == '{' || c == '}')
4526 {
4527 /* Must have found end/start of class: use it.
4528 * Or found the place to be at. */
4529 if ((c == findc && norm) || (n == 1 && !norm))
4530 {
4531 new_pos = curwin->w_cursor;
4532 pos = &new_pos;
4533 n = 0;
4534 }
4535 /* if no match found at all, we started outside of the
4536 * class and we're inside now. Just go on. */
4537 else if (new_pos.lnum == 0)
4538 {
4539 new_pos = curwin->w_cursor;
4540 pos = &new_pos;
4541 }
4542 /* found start/end of other method: go to match */
4543 else if ((pos = findmatchlimit(cap->oap, findc,
4544 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
4545 0)) == NULL)
4546 n = 0;
4547 else
4548 curwin->w_cursor = *pos;
4549 break;
4550 }
4551 }
4552 --n;
4553 }
4554 curwin->w_cursor = old_pos;
4555 if (pos == NULL && new_pos.lnum != 0)
4556 clearopbeep(cap->oap);
4557 }
4558 if (pos != NULL)
4559 {
4560 setpcmark();
4561 curwin->w_cursor = *pos;
4562 curwin->w_set_curswant = TRUE;
4563#ifdef FEAT_FOLDING
4564 if ((fdo_flags & FDO_BLOCK) && KeyTyped
4565 && cap->oap->op_type == OP_NOP)
4566 foldOpenCursor();
4567#endif
4568 }
4569 }
4570
4571 /*
4572 * "[[", "[]", "]]" and "][": move to start or end of function
4573 */
4574 else if (cap->nchar == '[' || cap->nchar == ']')
4575 {
4576 if (cap->nchar == cap->cmdchar) /* "]]" or "[[" */
4577 flag = '{';
4578 else
4579 flag = '}'; /* "][" or "[]" */
4580
4581 curwin->w_set_curswant = TRUE;
4582 /*
4583 * Imitate strange Vi behaviour: When using "]]" with an operator
4584 * we also stop at '}'.
4585 */
Bram Moolenaar8b96d642005-09-05 22:05:30 +00004586 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587 (cap->oap->op_type != OP_NOP
4588 && cap->arg == FORWARD && flag == '{')))
4589 clearopbeep(cap->oap);
4590 else
4591 {
4592 if (cap->oap->op_type == OP_NOP)
4593 beginline(BL_WHITE | BL_FIX);
4594#ifdef FEAT_FOLDING
4595 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4596 foldOpenCursor();
4597#endif
4598 }
4599 }
4600
4601 /*
4602 * "[p", "[P", "]P" and "]p": put with indent adjustment
4603 */
4604 else if (cap->nchar == 'p' || cap->nchar == 'P')
4605 {
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02004606 nv_put_opt(cap, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607 }
4608
4609 /*
4610 * "['", "[`", "]'" and "]`": jump to next mark
4611 */
4612 else if (cap->nchar == '\'' || cap->nchar == '`')
4613 {
4614 pos = &curwin->w_cursor;
4615 for (n = cap->count1; n > 0; --n)
4616 {
4617 prev_pos = *pos;
4618 pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
4619 cap->nchar == '\'');
4620 if (pos == NULL)
4621 break;
4622 }
4623 if (pos == NULL)
4624 pos = &prev_pos;
4625 nv_cursormark(cap, cap->nchar == '\'', pos);
4626 }
4627
Bram Moolenaar071d4272004-06-13 20:20:40 +00004628 /*
4629 * [ or ] followed by a middle mouse click: put selected text with
4630 * indent adjustment. Any other button just does as usual.
4631 */
Bram Moolenaar5ea0ac72010-05-07 15:52:08 +02004632 else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633 {
4634 (void)do_mouse(cap->oap, cap->nchar,
4635 (cap->cmdchar == ']') ? FORWARD : BACKWARD,
4636 cap->count1, PUT_FIXINDENT);
4637 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638
4639#ifdef FEAT_FOLDING
4640 /*
4641 * "[z" and "]z": move to start or end of open fold.
4642 */
4643 else if (cap->nchar == 'z')
4644 {
4645 if (foldMoveTo(FALSE, cap->cmdchar == ']' ? FORWARD : BACKWARD,
4646 cap->count1) == FAIL)
4647 clearopbeep(cap->oap);
4648 }
4649#endif
4650
4651#ifdef FEAT_DIFF
4652 /*
4653 * "[c" and "]c": move to next or previous diff-change.
4654 */
4655 else if (cap->nchar == 'c')
4656 {
4657 if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
4658 cap->count1) == FAIL)
4659 clearopbeep(cap->oap);
4660 }
4661#endif
4662
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00004663#ifdef FEAT_SPELL
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00004664 /*
4665 * "[s", "[S", "]s" and "]S": move to next spell error.
4666 */
4667 else if (cap->nchar == 's' || cap->nchar == 'S')
4668 {
Bram Moolenaar2cf8b302005-04-20 19:37:22 +00004669 setpcmark();
4670 for (n = 0; n < cap->count1; ++n)
Bram Moolenaar95529562005-08-25 21:21:38 +00004671 if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
4672 cap->nchar == 's' ? TRUE : FALSE, FALSE, NULL) == 0)
Bram Moolenaar2cf8b302005-04-20 19:37:22 +00004673 {
4674 clearopbeep(cap->oap);
4675 break;
4676 }
Bram Moolenaarb73fa622017-12-21 20:27:47 +01004677 else
4678 curwin->w_set_curswant = TRUE;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004679# ifdef FEAT_FOLDING
4680 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
4681 foldOpenCursor();
4682# endif
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00004683 }
4684#endif
4685
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 /* Not a valid cap->nchar. */
4687 else
4688 clearopbeep(cap->oap);
4689}
4690
4691/*
4692 * Handle Normal mode "%" command.
4693 */
4694 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004695nv_percent(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696{
4697 pos_T *pos;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02004698#if defined(FEAT_FOLDING)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699 linenr_T lnum = curwin->w_cursor.lnum;
4700#endif
4701
4702 cap->oap->inclusive = TRUE;
4703 if (cap->count0) /* {cnt}% : goto {cnt} percentage in file */
4704 {
4705 if (cap->count0 > 100)
4706 clearopbeep(cap->oap);
4707 else
4708 {
4709 cap->oap->motion_type = MLINE;
4710 setpcmark();
4711 /* Round up, so CTRL-G will give same value. Watch out for a
4712 * large line count, the line number must not go negative! */
4713 if (curbuf->b_ml.ml_line_count > 1000000)
4714 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99L)
4715 / 100L * cap->count0;
4716 else
4717 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
4718 cap->count0 + 99L) / 100L;
4719 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
4720 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
4721 beginline(BL_SOL | BL_FIX);
4722 }
4723 }
4724 else /* "%" : go to matching paren */
4725 {
4726 cap->oap->motion_type = MCHAR;
4727 cap->oap->use_reg_one = TRUE;
4728 if ((pos = findmatch(cap->oap, NUL)) == NULL)
4729 clearopbeep(cap->oap);
4730 else
4731 {
4732 setpcmark();
4733 curwin->w_cursor = *pos;
4734 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004737 }
4738 }
4739#ifdef FEAT_FOLDING
4740 if (cap->oap->op_type == OP_NOP
4741 && lnum != curwin->w_cursor.lnum
4742 && (fdo_flags & FDO_PERCENT)
4743 && KeyTyped)
4744 foldOpenCursor();
4745#endif
4746}
4747
4748/*
4749 * Handle "(" and ")" commands.
4750 * cap->arg is BACKWARD for "(" and FORWARD for ")".
4751 */
4752 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004753nv_brace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754{
4755 cap->oap->motion_type = MCHAR;
4756 cap->oap->use_reg_one = TRUE;
Bram Moolenaarebefac62005-12-28 22:39:57 +00004757 /* The motion used to be inclusive for "(", but that is not what Vi does. */
4758 cap->oap->inclusive = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004759 curwin->w_set_curswant = TRUE;
4760
4761 if (findsent(cap->arg, cap->count1) == FAIL)
4762 clearopbeep(cap->oap);
4763 else
4764 {
Bram Moolenaar1f14d572008-01-12 16:12:10 +00004765 /* Don't leave the cursor on the NUL past end of line. */
4766 adjust_cursor(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004767 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768#ifdef FEAT_FOLDING
4769 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4770 foldOpenCursor();
4771#endif
4772 }
4773}
4774
4775/*
4776 * "m" command: Mark a position.
4777 */
4778 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004779nv_mark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780{
4781 if (!checkclearop(cap->oap))
4782 {
4783 if (setmark(cap->nchar) == FAIL)
4784 clearopbeep(cap->oap);
4785 }
4786}
4787
4788/*
4789 * "{" and "}" commands.
4790 * cmd->arg is BACKWARD for "{" and FORWARD for "}".
4791 */
4792 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004793nv_findpar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794{
4795 cap->oap->motion_type = MCHAR;
4796 cap->oap->inclusive = FALSE;
4797 cap->oap->use_reg_one = TRUE;
4798 curwin->w_set_curswant = TRUE;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00004799 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800 clearopbeep(cap->oap);
4801 else
4802 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804#ifdef FEAT_FOLDING
4805 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4806 foldOpenCursor();
4807#endif
4808 }
4809}
4810
4811/*
4812 * "u" command: Undo or make lower case.
4813 */
4814 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004815nv_undo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01004817 if (cap->oap->op_type == OP_LOWER || VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818 {
4819 /* translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu" */
4820 cap->cmdchar = 'g';
4821 cap->nchar = 'u';
4822 nv_operator(cap);
4823 }
4824 else
4825 nv_kundo(cap);
4826}
4827
4828/*
4829 * <Undo> command.
4830 */
4831 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004832nv_kundo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004833{
4834 if (!checkclearopq(cap->oap))
4835 {
Bram Moolenaarf2732452018-06-03 14:47:35 +02004836#ifdef FEAT_JOB_CHANNEL
4837 if (bt_prompt(curbuf))
4838 {
4839 clearopbeep(cap->oap);
4840 return;
4841 }
4842#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004843 u_undo((int)cap->count1);
4844 curwin->w_set_curswant = TRUE;
4845 }
4846}
4847
4848/*
4849 * Handle the "r" command.
4850 */
4851 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004852nv_replace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853{
4854 char_u *ptr;
4855 int had_ctrl_v;
4856 long n;
4857
4858 if (checkclearop(cap->oap))
4859 return;
Bram Moolenaarf2732452018-06-03 14:47:35 +02004860#ifdef FEAT_JOB_CHANNEL
4861 if (bt_prompt(curbuf) && !prompt_curpos_editable())
4862 {
4863 clearopbeep(cap->oap);
4864 return;
4865 }
4866#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867
4868 /* get another character */
4869 if (cap->nchar == Ctrl_V)
4870 {
4871 had_ctrl_v = Ctrl_V;
4872 cap->nchar = get_literal();
4873 /* Don't redo a multibyte character with CTRL-V. */
4874 if (cap->nchar > DEL)
4875 had_ctrl_v = NUL;
4876 }
4877 else
4878 had_ctrl_v = NUL;
4879
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +00004880 /* Abort if the character is a special key. */
4881 if (IS_SPECIAL(cap->nchar))
4882 {
4883 clearopbeep(cap->oap);
4884 return;
4885 }
4886
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 /* Visual mode "r" */
4888 if (VIsual_active)
4889 {
Bram Moolenaar57fb0da2009-02-04 10:46:25 +00004890 if (got_int)
4891 reset_VIsual();
Bram Moolenaard9820532013-11-04 01:41:17 +01004892 if (had_ctrl_v)
4893 {
Bram Moolenaarf12519d2018-02-06 22:52:49 +01004894 /* Use a special (negative) number to make a difference between a
4895 * literal CR or NL and a line break. */
4896 if (cap->nchar == CAR)
4897 cap->nchar = REPLACE_CR_NCHAR;
4898 else if (cap->nchar == NL)
4899 cap->nchar = REPLACE_NL_NCHAR;
Bram Moolenaard9820532013-11-04 01:41:17 +01004900 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901 nv_operator(cap);
4902 return;
4903 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 /* Break tabs, etc. */
4906 if (virtual_active())
4907 {
4908 if (u_save_cursor() == FAIL)
4909 return;
4910 if (gchar_cursor() == NUL)
4911 {
4912 /* Add extra space and put the cursor on the first one. */
4913 coladvance_force((colnr_T)(getviscol() + cap->count1));
4914 curwin->w_cursor.col -= cap->count1;
4915 }
4916 else if (gchar_cursor() == TAB)
4917 coladvance_force(getviscol());
4918 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004919
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +00004920 /* Abort if not enough characters to replace. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921 ptr = ml_get_cursor();
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +00004922 if (STRLEN(ptr) < (unsigned)cap->count1
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01004923 || (has_mbyte && mb_charlen(ptr) < cap->count1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924 {
4925 clearopbeep(cap->oap);
4926 return;
4927 }
4928
4929 /*
4930 * Replacing with a TAB is done by edit() when it is complicated because
4931 * 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB.
4932 * Other characters are done below to avoid problems with things like
4933 * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
4934 */
4935 if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
4936 {
4937 stuffnumReadbuff(cap->count1);
4938 stuffcharReadbuff('R');
4939 stuffcharReadbuff('\t');
4940 stuffcharReadbuff(ESC);
4941 return;
4942 }
4943
4944 /* save line for undo */
4945 if (u_save_cursor() == FAIL)
4946 return;
4947
4948 if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n'))
4949 {
4950 /*
4951 * Replace character(s) by a single newline.
4952 * Strange vi behaviour: Only one newline is inserted.
4953 * Delete the characters here.
4954 * Insert the newline with an insert command, takes care of
4955 * autoindent. The insert command depends on being on the last
4956 * character of a line or not.
4957 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958 (void)del_chars(cap->count1, FALSE); /* delete the characters */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004959 stuffcharReadbuff('\r');
4960 stuffcharReadbuff(ESC);
4961
4962 /* Give 'r' to edit(), to get the redo command right. */
4963 invoke_edit(cap, TRUE, 'r', FALSE);
4964 }
4965 else
4966 {
4967 prep_redo(cap->oap->regname, cap->count1,
4968 NUL, 'r', NUL, had_ctrl_v, cap->nchar);
4969
4970 curbuf->b_op_start = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971 if (has_mbyte)
4972 {
4973 int old_State = State;
4974
4975 if (cap->ncharC1 != 0)
4976 AppendCharToRedobuff(cap->ncharC1);
4977 if (cap->ncharC2 != 0)
4978 AppendCharToRedobuff(cap->ncharC2);
4979
4980 /* This is slow, but it handles replacing a single-byte with a
4981 * multi-byte and the other way around. Also handles adding
4982 * composing characters for utf-8. */
4983 for (n = cap->count1; n > 0; --n)
4984 {
4985 State = REPLACE;
Bram Moolenaar8320da42012-04-30 18:18:47 +02004986 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
4987 {
4988 int c = ins_copychar(curwin->w_cursor.lnum
4989 + (cap->nchar == Ctrl_Y ? -1 : 1));
4990 if (c != NUL)
4991 ins_char(c);
4992 else
4993 /* will be decremented further down */
4994 ++curwin->w_cursor.col;
4995 }
4996 else
4997 ins_char(cap->nchar);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 State = old_State;
4999 if (cap->ncharC1 != 0)
5000 ins_char(cap->ncharC1);
5001 if (cap->ncharC2 != 0)
5002 ins_char(cap->ncharC2);
5003 }
5004 }
5005 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006 {
5007 /*
5008 * Replace the characters within one line.
5009 */
5010 for (n = cap->count1; n > 0; --n)
5011 {
5012 /*
5013 * Get ptr again, because u_save and/or showmatch() will have
5014 * released the line. At the same time we let know that the
5015 * line will be changed.
5016 */
5017 ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
Bram Moolenaar8320da42012-04-30 18:18:47 +02005018 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
5019 {
5020 int c = ins_copychar(curwin->w_cursor.lnum
5021 + (cap->nchar == Ctrl_Y ? -1 : 1));
5022 if (c != NUL)
5023 ptr[curwin->w_cursor.col] = c;
5024 }
5025 else
5026 ptr[curwin->w_cursor.col] = cap->nchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027 if (p_sm && msg_silent == 0)
5028 showmatch(cap->nchar);
5029 ++curwin->w_cursor.col;
5030 }
5031#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005032 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005033 {
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005034 colnr_T start = (colnr_T)(curwin->w_cursor.col - cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035
Bram Moolenaar009b2592004-10-24 19:18:58 +00005036 netbeans_removed(curbuf, curwin->w_cursor.lnum, start,
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005037 (long)cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038 netbeans_inserted(curbuf, curwin->w_cursor.lnum, start,
Bram Moolenaar009b2592004-10-24 19:18:58 +00005039 &ptr[start], (int)cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005040 }
5041#endif
5042
5043 /* mark the buffer as changed and prepare for displaying */
5044 changed_bytes(curwin->w_cursor.lnum,
5045 (colnr_T)(curwin->w_cursor.col - cap->count1));
5046 }
5047 --curwin->w_cursor.col; /* cursor on the last replaced char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005048 /* if the character on the left of the current cursor is a multi-byte
5049 * character, move two characters left */
5050 if (has_mbyte)
5051 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005052 curbuf->b_op_end = curwin->w_cursor;
5053 curwin->w_set_curswant = TRUE;
5054 set_last_insert(cap->nchar);
5055 }
5056}
5057
Bram Moolenaar071d4272004-06-13 20:20:40 +00005058/*
5059 * 'o': Exchange start and end of Visual area.
5060 * 'O': same, but in block mode exchange left and right corners.
5061 */
5062 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005063v_swap_corners(int cmdchar)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005064{
5065 pos_T old_cursor;
5066 colnr_T left, right;
5067
5068 if (cmdchar == 'O' && VIsual_mode == Ctrl_V)
5069 {
5070 old_cursor = curwin->w_cursor;
5071 getvcols(curwin, &old_cursor, &VIsual, &left, &right);
5072 curwin->w_cursor.lnum = VIsual.lnum;
5073 coladvance(left);
5074 VIsual = curwin->w_cursor;
5075
5076 curwin->w_cursor.lnum = old_cursor.lnum;
5077 curwin->w_curswant = right;
5078 /* 'selection "exclusive" and cursor at right-bottom corner: move it
5079 * right one column */
5080 if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e')
5081 ++curwin->w_curswant;
5082 coladvance(curwin->w_curswant);
5083 if (curwin->w_cursor.col == old_cursor.col
Bram Moolenaar071d4272004-06-13 20:20:40 +00005084 && (!virtual_active()
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01005085 || curwin->w_cursor.coladd == old_cursor.coladd))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005086 {
5087 curwin->w_cursor.lnum = VIsual.lnum;
5088 if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e')
5089 ++right;
5090 coladvance(right);
5091 VIsual = curwin->w_cursor;
5092
5093 curwin->w_cursor.lnum = old_cursor.lnum;
5094 coladvance(left);
5095 curwin->w_curswant = left;
5096 }
5097 }
5098 else
5099 {
5100 old_cursor = curwin->w_cursor;
5101 curwin->w_cursor = VIsual;
5102 VIsual = old_cursor;
5103 curwin->w_set_curswant = TRUE;
5104 }
5105}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106
5107/*
5108 * "R" (cap->arg is FALSE) and "gR" (cap->arg is TRUE).
5109 */
5110 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005111nv_Replace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005112{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113 if (VIsual_active) /* "R" is replace lines */
5114 {
5115 cap->cmdchar = 'c';
5116 cap->nchar = NUL;
Bram Moolenaara390bb62013-03-13 19:02:41 +01005117 VIsual_mode_orig = VIsual_mode; /* remember original area for gv */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118 VIsual_mode = 'V';
5119 nv_operator(cap);
5120 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01005121 else if (!checkclearopq(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005122 {
5123 if (!curbuf->b_p_ma)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005124 emsg(_(e_modifiable));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 else
5126 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127 if (virtual_active())
5128 coladvance(getviscol());
Bram Moolenaar071d4272004-06-13 20:20:40 +00005129 invoke_edit(cap, FALSE, cap->arg ? 'V' : 'R', FALSE);
5130 }
5131 }
5132}
5133
Bram Moolenaar071d4272004-06-13 20:20:40 +00005134/*
5135 * "gr".
5136 */
5137 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005138nv_vreplace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005139{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140 if (VIsual_active)
5141 {
5142 cap->cmdchar = 'r';
5143 cap->nchar = cap->extra_char;
5144 nv_replace(cap); /* Do same as "r" in Visual mode for now */
5145 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01005146 else if (!checkclearopq(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147 {
5148 if (!curbuf->b_p_ma)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005149 emsg(_(e_modifiable));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150 else
5151 {
5152 if (cap->extra_char == Ctrl_V) /* get another character */
5153 cap->extra_char = get_literal();
5154 stuffcharReadbuff(cap->extra_char);
5155 stuffcharReadbuff(ESC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005156 if (virtual_active())
5157 coladvance(getviscol());
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 invoke_edit(cap, TRUE, 'v', FALSE);
5159 }
5160 }
5161}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005162
5163/*
5164 * Swap case for "~" command, when it does not work like an operator.
5165 */
5166 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005167n_swapchar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168{
5169 long n;
5170 pos_T startpos;
5171 int did_change = 0;
5172#ifdef FEAT_NETBEANS_INTG
5173 pos_T pos;
5174 char_u *ptr;
5175 int count;
5176#endif
5177
5178 if (checkclearopq(cap->oap))
5179 return;
5180
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01005181 if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005182 {
5183 clearopbeep(cap->oap);
5184 return;
5185 }
5186
5187 prep_redo_cmd(cap);
5188
5189 if (u_save_cursor() == FAIL)
5190 return;
5191
5192 startpos = curwin->w_cursor;
5193#ifdef FEAT_NETBEANS_INTG
5194 pos = startpos;
5195#endif
5196 for (n = cap->count1; n > 0; --n)
5197 {
5198 did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
5199 inc_cursor();
5200 if (gchar_cursor() == NUL)
5201 {
5202 if (vim_strchr(p_ww, '~') != NULL
5203 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
5204 {
5205#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005206 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005207 {
5208 if (did_change)
5209 {
5210 ptr = ml_get(pos.lnum);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005211 count = (int)STRLEN(ptr) - pos.col;
Bram Moolenaar009b2592004-10-24 19:18:58 +00005212 netbeans_removed(curbuf, pos.lnum, pos.col,
5213 (long)count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 netbeans_inserted(curbuf, pos.lnum, pos.col,
Bram Moolenaar009b2592004-10-24 19:18:58 +00005215 &ptr[pos.col], count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005216 }
5217 pos.col = 0;
5218 pos.lnum++;
5219 }
5220#endif
5221 ++curwin->w_cursor.lnum;
5222 curwin->w_cursor.col = 0;
5223 if (n > 1)
5224 {
5225 if (u_savesub(curwin->w_cursor.lnum) == FAIL)
5226 break;
5227 u_clearline();
5228 }
5229 }
5230 else
5231 break;
5232 }
5233 }
5234#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005235 if (did_change && netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005236 {
5237 ptr = ml_get(pos.lnum);
5238 count = curwin->w_cursor.col - pos.col;
Bram Moolenaar009b2592004-10-24 19:18:58 +00005239 netbeans_removed(curbuf, pos.lnum, pos.col, (long)count);
5240 netbeans_inserted(curbuf, pos.lnum, pos.col, &ptr[pos.col], count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241 }
5242#endif
5243
5244
5245 check_cursor();
5246 curwin->w_set_curswant = TRUE;
5247 if (did_change)
5248 {
5249 changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1,
5250 0L);
5251 curbuf->b_op_start = startpos;
5252 curbuf->b_op_end = curwin->w_cursor;
5253 if (curbuf->b_op_end.col > 0)
5254 --curbuf->b_op_end.col;
5255 }
5256}
5257
5258/*
5259 * Move cursor to mark.
5260 */
5261 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005262nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005263{
5264 if (check_mark(pos) == FAIL)
5265 clearop(cap->oap);
5266 else
5267 {
5268 if (cap->cmdchar == '\''
5269 || cap->cmdchar == '`'
5270 || cap->cmdchar == '['
5271 || cap->cmdchar == ']')
5272 setpcmark();
5273 curwin->w_cursor = *pos;
5274 if (flag)
5275 beginline(BL_WHITE | BL_FIX);
5276 else
5277 check_cursor();
5278 }
5279 cap->oap->motion_type = flag ? MLINE : MCHAR;
5280 if (cap->cmdchar == '`')
5281 cap->oap->use_reg_one = TRUE;
5282 cap->oap->inclusive = FALSE; /* ignored if not MCHAR */
5283 curwin->w_set_curswant = TRUE;
5284}
5285
Bram Moolenaar071d4272004-06-13 20:20:40 +00005286/*
5287 * Handle commands that are operators in Visual mode.
5288 */
5289 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005290v_visop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005291{
5292 static char_u trans[] = "YyDdCcxdXdAAIIrr";
5293
5294 /* Uppercase means linewise, except in block mode, then "D" deletes till
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02005295 * the end of the line, and "C" replaces till EOL */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005296 if (isupper(cap->cmdchar))
5297 {
5298 if (VIsual_mode != Ctrl_V)
Bram Moolenaara390bb62013-03-13 19:02:41 +01005299 {
5300 VIsual_mode_orig = VIsual_mode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005301 VIsual_mode = 'V';
Bram Moolenaara390bb62013-03-13 19:02:41 +01005302 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303 else if (cap->cmdchar == 'C' || cap->cmdchar == 'D')
5304 curwin->w_curswant = MAXCOL;
5305 }
5306 cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
5307 nv_operator(cap);
5308}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005309
5310/*
5311 * "s" and "S" commands.
5312 */
5313 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005314nv_subst(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315{
Bram Moolenaard96ff162018-02-18 22:13:29 +01005316#ifdef FEAT_TERMINAL
5317 /* When showing output of term_dumpdiff() swap the top and botom. */
5318 if (term_swap_diff() == OK)
5319 return;
5320#endif
Bram Moolenaarf2732452018-06-03 14:47:35 +02005321#ifdef FEAT_JOB_CHANNEL
5322 if (bt_prompt(curbuf) && !prompt_curpos_editable())
5323 {
5324 clearopbeep(cap->oap);
5325 return;
5326 }
5327#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005328 if (VIsual_active) /* "vs" and "vS" are the same as "vc" */
5329 {
5330 if (cap->cmdchar == 'S')
Bram Moolenaara390bb62013-03-13 19:02:41 +01005331 {
5332 VIsual_mode_orig = VIsual_mode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005333 VIsual_mode = 'V';
Bram Moolenaara390bb62013-03-13 19:02:41 +01005334 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005335 cap->cmdchar = 'c';
5336 nv_operator(cap);
5337 }
5338 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005339 nv_optrans(cap);
5340}
5341
5342/*
5343 * Abbreviated commands.
5344 */
5345 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005346nv_abbrev(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347{
5348 if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL)
5349 cap->cmdchar = 'x'; /* DEL key behaves like 'x' */
5350
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 /* in Visual mode these commands are operators */
5352 if (VIsual_active)
5353 v_visop(cap);
5354 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005355 nv_optrans(cap);
5356}
5357
5358/*
5359 * Translate a command into another command.
5360 */
5361 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005362nv_optrans(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005363{
5364 static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
5365 (char_u *)"d$", (char_u *)"c$",
5366 (char_u *)"cl", (char_u *)"cc",
5367 (char_u *)"yy", (char_u *)":s\r"};
5368 static char_u *str = (char_u *)"xXDCsSY&";
5369
5370 if (!checkclearopq(cap->oap))
5371 {
Bram Moolenaar7a9bd7c2019-09-17 22:42:55 +02005372 // In Vi "2D" doesn't delete the next line. Can't translate it
5373 // either, because "2." should also not use the count.
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005374 if (cap->cmdchar == 'D' && vim_strchr(p_cpo, CPO_HASH) != NULL)
5375 {
5376 cap->oap->start = curwin->w_cursor;
5377 cap->oap->op_type = OP_DELETE;
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00005378#ifdef FEAT_EVAL
5379 set_op_var(OP_DELETE);
5380#endif
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005381 cap->count1 = 1;
5382 nv_dollar(cap);
5383 finish_op = TRUE;
5384 ResetRedobuff();
5385 AppendCharToRedobuff('D');
5386 }
5387 else
5388 {
5389 if (cap->count0)
5390 stuffnumReadbuff(cap->count0);
Bram Moolenaar56ebbab2019-09-20 13:40:14 +02005391 stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]);
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005392 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393 }
5394 cap->opcount = 0;
5395}
5396
5397/*
5398 * "'" and "`" commands. Also for "g'" and "g`".
5399 * cap->arg is TRUE for "'" and "g'".
5400 */
5401 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005402nv_gomark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005403{
5404 pos_T *pos;
5405 int c;
5406#ifdef FEAT_FOLDING
Bram Moolenaar8754deb2013-01-17 13:24:08 +01005407 pos_T old_cursor = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005408 int old_KeyTyped = KeyTyped; /* getting file may reset it */
5409#endif
5410
5411 if (cap->cmdchar == 'g')
5412 c = cap->extra_char;
5413 else
5414 c = cap->nchar;
5415 pos = getmark(c, (cap->oap->op_type == OP_NOP));
5416 if (pos == (pos_T *)-1) /* jumped to other file */
5417 {
5418 if (cap->arg)
5419 {
5420 check_cursor_lnum();
5421 beginline(BL_WHITE | BL_FIX);
5422 }
5423 else
5424 check_cursor();
5425 }
5426 else
5427 nv_cursormark(cap, cap->arg, pos);
5428
Bram Moolenaar071d4272004-06-13 20:20:40 +00005429 /* May need to clear the coladd that a mark includes. */
5430 if (!virtual_active())
5431 curwin->w_cursor.coladd = 0;
Bram Moolenaar9aa15692017-08-19 15:05:32 +02005432 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433#ifdef FEAT_FOLDING
5434 if (cap->oap->op_type == OP_NOP
Bram Moolenaar15364d72013-01-24 21:00:20 +01005435 && pos != NULL
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01005436 && (pos == (pos_T *)-1 || !EQUAL_POS(old_cursor, *pos))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005437 && (fdo_flags & FDO_MARK)
5438 && old_KeyTyped)
5439 foldOpenCursor();
5440#endif
5441}
5442
5443/*
5444 * Handle CTRL-O, CTRL-I, "g;" and "g," commands.
5445 */
5446 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005447nv_pcmark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005448{
5449#ifdef FEAT_JUMPLIST
5450 pos_T *pos;
5451# ifdef FEAT_FOLDING
5452 linenr_T lnum = curwin->w_cursor.lnum;
5453 int old_KeyTyped = KeyTyped; /* getting file may reset it */
5454# endif
5455
5456 if (!checkclearopq(cap->oap))
5457 {
5458 if (cap->cmdchar == 'g')
5459 pos = movechangelist((int)cap->count1);
5460 else
5461 pos = movemark((int)cap->count1);
5462 if (pos == (pos_T *)-1) /* jump to other file */
5463 {
5464 curwin->w_set_curswant = TRUE;
5465 check_cursor();
5466 }
5467 else if (pos != NULL) /* can jump */
5468 nv_cursormark(cap, FALSE, pos);
5469 else if (cap->cmdchar == 'g')
5470 {
5471 if (curbuf->b_changelistlen == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005472 emsg(_("E664: changelist is empty"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005473 else if (cap->count1 < 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005474 emsg(_("E662: At start of changelist"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005475 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005476 emsg(_("E663: At end of changelist"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005477 }
5478 else
5479 clearopbeep(cap->oap);
5480# ifdef FEAT_FOLDING
5481 if (cap->oap->op_type == OP_NOP
5482 && (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum)
5483 && (fdo_flags & FDO_MARK)
5484 && old_KeyTyped)
5485 foldOpenCursor();
5486# endif
5487 }
5488#else
5489 clearopbeep(cap->oap);
5490#endif
5491}
5492
5493/*
5494 * Handle '"' command.
5495 */
5496 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005497nv_regname(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005498{
5499 if (checkclearop(cap->oap))
5500 return;
5501#ifdef FEAT_EVAL
5502 if (cap->nchar == '=')
5503 cap->nchar = get_expr_register();
5504#endif
5505 if (cap->nchar != NUL && valid_yank_reg(cap->nchar, FALSE))
5506 {
5507 cap->oap->regname = cap->nchar;
5508 cap->opcount = cap->count0; /* remember count before '"' */
5509#ifdef FEAT_EVAL
5510 set_reg_var(cap->oap->regname);
5511#endif
5512 }
5513 else
5514 clearopbeep(cap->oap);
5515}
5516
Bram Moolenaar071d4272004-06-13 20:20:40 +00005517/*
5518 * Handle "v", "V" and "CTRL-V" commands.
5519 * Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
5520 * is TRUE.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005521 * Handle CTRL-Q just like CTRL-V.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522 */
5523 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005524nv_visual(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525{
Bram Moolenaardf177f62005-02-22 08:39:57 +00005526 if (cap->cmdchar == Ctrl_Q)
5527 cap->cmdchar = Ctrl_V;
5528
Bram Moolenaar071d4272004-06-13 20:20:40 +00005529 /* 'v', 'V' and CTRL-V can be used while an operator is pending to make it
5530 * characterwise, linewise, or blockwise. */
5531 if (cap->oap->op_type != OP_NOP)
5532 {
Bram Moolenaar5976f8f2018-12-27 23:44:44 +01005533 motion_force = cap->oap->motion_force = cap->cmdchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005534 finish_op = FALSE; /* operator doesn't finish now but later */
5535 return;
5536 }
5537
5538 VIsual_select = cap->arg;
5539 if (VIsual_active) /* change Visual mode */
5540 {
5541 if (VIsual_mode == cap->cmdchar) /* stop visual mode */
5542 end_visual_mode();
5543 else /* toggle char/block mode */
5544 { /* or char/line mode */
5545 VIsual_mode = cap->cmdchar;
5546 showmode();
5547 }
5548 redraw_curbuf_later(INVERTED); /* update the inversion */
5549 }
5550 else /* start Visual mode */
5551 {
5552 check_visual_highlight();
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005553 if (cap->count0 > 0 && resel_VIsual_mode != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005554 {
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005555 /* use previously selected part */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005556 VIsual = curwin->w_cursor;
5557
5558 VIsual_active = TRUE;
5559 VIsual_reselect = TRUE;
5560 if (!cap->arg)
5561 /* start Select mode when 'selectmode' contains "cmd" */
5562 may_start_select('c');
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563 setmouse();
Bram Moolenaar09df3122006-01-23 22:23:09 +00005564 if (p_smd && msg_silent == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565 redraw_cmdline = TRUE; /* show visual mode later */
5566 /*
5567 * For V and ^V, we multiply the number of lines even if there
5568 * was only one -- webb
5569 */
5570 if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
5571 {
5572 curwin->w_cursor.lnum +=
5573 resel_VIsual_line_count * cap->count0 - 1;
5574 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
5575 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5576 }
5577 VIsual_mode = resel_VIsual_mode;
5578 if (VIsual_mode == 'v')
5579 {
5580 if (resel_VIsual_line_count <= 1)
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005581 {
5582 validate_virtcol();
5583 curwin->w_curswant = curwin->w_virtcol
5584 + resel_VIsual_vcol * cap->count0 - 1;
5585 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005586 else
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005587 curwin->w_curswant = resel_VIsual_vcol;
5588 coladvance(curwin->w_curswant);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005589 }
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005590 if (resel_VIsual_vcol == MAXCOL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005591 {
5592 curwin->w_curswant = MAXCOL;
5593 coladvance((colnr_T)MAXCOL);
5594 }
5595 else if (VIsual_mode == Ctrl_V)
5596 {
5597 validate_virtcol();
5598 curwin->w_curswant = curwin->w_virtcol
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005599 + resel_VIsual_vcol * cap->count0 - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600 coladvance(curwin->w_curswant);
5601 }
5602 else
5603 curwin->w_set_curswant = TRUE;
5604 redraw_curbuf_later(INVERTED); /* show the inversion */
5605 }
5606 else
5607 {
5608 if (!cap->arg)
5609 /* start Select mode when 'selectmode' contains "cmd" */
5610 may_start_select('c');
5611 n_start_visual_mode(cap->cmdchar);
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005612 if (VIsual_mode != 'V' && *p_sel == 'e')
5613 ++cap->count1; /* include one more char */
5614 if (cap->count0 > 0 && --cap->count1 > 0)
5615 {
5616 /* With a count select that many characters or lines. */
5617 if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V)
5618 nv_right(cap);
5619 else if (VIsual_mode == 'V')
5620 nv_down(cap);
5621 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622 }
5623 }
5624}
5625
5626/*
5627 * Start selection for Shift-movement keys.
5628 */
5629 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005630start_selection(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005631{
5632 /* if 'selectmode' contains "key", start Select mode */
5633 may_start_select('k');
5634 n_start_visual_mode('v');
5635}
5636
5637/*
5638 * Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
5639 */
5640 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005641may_start_select(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642{
5643 VIsual_select = (stuff_empty() && typebuf_typed()
5644 && (vim_strchr(p_slm, c) != NULL));
5645}
5646
5647/*
5648 * Start Visual mode "c".
5649 * Should set VIsual_select before calling this.
5650 */
5651 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005652n_start_visual_mode(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005653{
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005654#ifdef FEAT_CONCEAL
5655 /* Check for redraw before changing the state. */
Bram Moolenaarb9464822018-05-10 15:09:49 +02005656 conceal_check_cursor_line();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005657#endif
5658
Bram Moolenaar071d4272004-06-13 20:20:40 +00005659 VIsual_mode = c;
5660 VIsual_active = TRUE;
5661 VIsual_reselect = TRUE;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01005662
5663 // Corner case: the 0 position in a tab may change when going into
5664 // virtualedit. Recalculate curwin->w_cursor to avoid bad hilighting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665 if (c == Ctrl_V && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB)
Bram Moolenaar2dac2132012-08-15 13:31:00 +02005666 {
5667 validate_virtcol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 coladvance(curwin->w_virtcol);
Bram Moolenaar2dac2132012-08-15 13:31:00 +02005669 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005670 VIsual = curwin->w_cursor;
5671
5672#ifdef FEAT_FOLDING
5673 foldAdjustVisual();
5674#endif
5675
Bram Moolenaar071d4272004-06-13 20:20:40 +00005676 setmouse();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005677#ifdef FEAT_CONCEAL
5678 /* Check for redraw after changing the state. */
Bram Moolenaarb9464822018-05-10 15:09:49 +02005679 conceal_check_cursor_line();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005680#endif
5681
Bram Moolenaar09df3122006-01-23 22:23:09 +00005682 if (p_smd && msg_silent == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 redraw_cmdline = TRUE; /* show visual mode later */
5684#ifdef FEAT_CLIPBOARD
5685 /* Make sure the clipboard gets updated. Needed because start and
5686 * end may still be the same, and the selection needs to be owned */
5687 clip_star.vmode = NUL;
5688#endif
5689
5690 /* Only need to redraw this line, unless still need to redraw an old
5691 * Visual area (when 'lazyredraw' is set). */
5692 if (curwin->w_redr_type < INVERTED)
5693 {
5694 curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
5695 curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
5696 }
5697}
5698
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699
5700/*
5701 * CTRL-W: Window commands
5702 */
5703 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005704nv_window(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705{
Bram Moolenaar938783d2017-07-16 20:13:26 +02005706 if (cap->nchar == ':')
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005707 {
Bram Moolenaar938783d2017-07-16 20:13:26 +02005708 /* "CTRL-W :" is the same as typing ":"; useful in a terminal window */
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005709 cap->cmdchar = ':';
5710 cap->nchar = NUL;
Bram Moolenaar938783d2017-07-16 20:13:26 +02005711 nv_colon(cap);
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005712 }
Bram Moolenaar938783d2017-07-16 20:13:26 +02005713 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 do_window(cap->nchar, cap->count0, NUL); /* everything is in window.c */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005715}
5716
5717/*
5718 * CTRL-Z: Suspend
5719 */
5720 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005721nv_suspend(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722{
5723 clearop(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005724 if (VIsual_active)
5725 end_visual_mode(); /* stop Visual mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726 do_cmdline_cmd((char_u *)"st");
5727}
5728
5729/*
5730 * Commands starting with "g".
5731 */
5732 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005733nv_g_cmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005734{
5735 oparg_T *oap = cap->oap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736 pos_T tpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005737 int i;
5738 int flag = FALSE;
5739
5740 switch (cap->nchar)
5741 {
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005742 case Ctrl_A:
5743 case Ctrl_X:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744#ifdef MEM_PROFILE
5745 /*
5746 * "g^A": dump log of used memory.
5747 */
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005748 if (!VIsual_active && cap->nchar == Ctrl_A)
5749 vim_mem_profile_dump();
5750 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005751#endif
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005752 /*
5753 * "g^A/g^X": sequentially increment visually selected region
5754 */
5755 if (VIsual_active)
5756 {
5757 cap->arg = TRUE;
5758 cap->cmdchar = cap->nchar;
Bram Moolenaard79e5502016-01-10 22:13:02 +01005759 cap->nchar = NUL;
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005760 nv_addsub(cap);
5761 }
5762 else
5763 clearopbeep(oap);
5764 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765
Bram Moolenaar071d4272004-06-13 20:20:40 +00005766 /*
5767 * "gR": Enter virtual replace mode.
5768 */
5769 case 'R':
5770 cap->arg = TRUE;
5771 nv_Replace(cap);
5772 break;
5773
5774 case 'r':
5775 nv_vreplace(cap);
5776 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005777
5778 case '&':
5779 do_cmdline_cmd((char_u *)"%s//~/&");
5780 break;
5781
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782 /*
5783 * "gv": Reselect the previous Visual area. If Visual already active,
5784 * exchange previous and current Visual area.
5785 */
5786 case 'v':
5787 if (checkclearop(oap))
5788 break;
5789
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005790 if ( curbuf->b_visual.vi_start.lnum == 0
5791 || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
5792 || curbuf->b_visual.vi_end.lnum == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 beep_flush();
5794 else
5795 {
5796 /* set w_cursor to the start of the Visual area, tpos to the end */
5797 if (VIsual_active)
5798 {
5799 i = VIsual_mode;
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005800 VIsual_mode = curbuf->b_visual.vi_mode;
5801 curbuf->b_visual.vi_mode = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005802# ifdef FEAT_EVAL
5803 curbuf->b_visual_mode_eval = i;
5804# endif
5805 i = curwin->w_curswant;
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005806 curwin->w_curswant = curbuf->b_visual.vi_curswant;
5807 curbuf->b_visual.vi_curswant = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005808
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005809 tpos = curbuf->b_visual.vi_end;
5810 curbuf->b_visual.vi_end = curwin->w_cursor;
5811 curwin->w_cursor = curbuf->b_visual.vi_start;
5812 curbuf->b_visual.vi_start = VIsual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005813 }
5814 else
5815 {
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005816 VIsual_mode = curbuf->b_visual.vi_mode;
5817 curwin->w_curswant = curbuf->b_visual.vi_curswant;
5818 tpos = curbuf->b_visual.vi_end;
5819 curwin->w_cursor = curbuf->b_visual.vi_start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005820 }
5821
5822 VIsual_active = TRUE;
5823 VIsual_reselect = TRUE;
5824
5825 /* Set Visual to the start and w_cursor to the end of the Visual
5826 * area. Make sure they are on an existing character. */
5827 check_cursor();
5828 VIsual = curwin->w_cursor;
5829 curwin->w_cursor = tpos;
5830 check_cursor();
5831 update_topline();
5832 /*
5833 * When called from normal "g" command: start Select mode when
5834 * 'selectmode' contains "cmd". When called for K_SELECT, always
5835 * start Select mode.
5836 */
5837 if (cap->arg)
5838 VIsual_select = TRUE;
5839 else
5840 may_start_select('c');
Bram Moolenaar071d4272004-06-13 20:20:40 +00005841 setmouse();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842#ifdef FEAT_CLIPBOARD
5843 /* Make sure the clipboard gets updated. Needed because start and
5844 * end are still the same, and the selection needs to be owned */
5845 clip_star.vmode = NUL;
5846#endif
5847 redraw_curbuf_later(INVERTED);
5848 showmode();
5849 }
5850 break;
5851 /*
5852 * "gV": Don't reselect the previous Visual area after a Select mode
5853 * mapping of menu.
5854 */
5855 case 'V':
5856 VIsual_reselect = FALSE;
5857 break;
5858
5859 /*
5860 * "gh": start Select mode.
5861 * "gH": start Select line mode.
5862 * "g^H": start Select block mode.
5863 */
5864 case K_BS:
5865 cap->nchar = Ctrl_H;
5866 /* FALLTHROUGH */
5867 case 'h':
5868 case 'H':
5869 case Ctrl_H:
5870# ifdef EBCDIC
5871 /* EBCDIC: 'v'-'h' != '^v'-'^h' */
5872 if (cap->nchar == Ctrl_H)
5873 cap->cmdchar = Ctrl_V;
5874 else
5875# endif
5876 cap->cmdchar = cap->nchar + ('v' - 'h');
5877 cap->arg = TRUE;
5878 nv_visual(cap);
5879 break;
Bram Moolenaar641e2862012-07-25 15:06:34 +02005880
5881 /* "gn", "gN" visually select next/previous search match
5882 * "gn" selects next match
5883 * "gN" selects previous match
5884 */
5885 case 'N':
5886 case 'n':
5887 if (!current_search(cap->count1, cap->nchar == 'n'))
Bram Moolenaarf00dc262012-10-21 03:54:33 +02005888 clearopbeep(oap);
Bram Moolenaar641e2862012-07-25 15:06:34 +02005889 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005890
5891 /*
5892 * "gj" and "gk" two new funny movement keys -- up and down
5893 * movement based on *screen* line rather than *file* line.
5894 */
5895 case 'j':
5896 case K_DOWN:
5897 /* with 'nowrap' it works just like the normal "j" command; also when
5898 * in a closed fold */
5899 if (!curwin->w_p_wrap
5900#ifdef FEAT_FOLDING
5901 || hasFolding(curwin->w_cursor.lnum, NULL, NULL)
5902#endif
5903 )
5904 {
5905 oap->motion_type = MLINE;
5906 i = cursor_down(cap->count1, oap->op_type == OP_NOP);
5907 }
5908 else
5909 i = nv_screengo(oap, FORWARD, cap->count1);
5910 if (i == FAIL)
5911 clearopbeep(oap);
5912 break;
5913
5914 case 'k':
5915 case K_UP:
5916 /* with 'nowrap' it works just like the normal "k" command; also when
5917 * in a closed fold */
5918 if (!curwin->w_p_wrap
5919#ifdef FEAT_FOLDING
5920 || hasFolding(curwin->w_cursor.lnum, NULL, NULL)
5921#endif
5922 )
5923 {
5924 oap->motion_type = MLINE;
5925 i = cursor_up(cap->count1, oap->op_type == OP_NOP);
5926 }
5927 else
5928 i = nv_screengo(oap, BACKWARD, cap->count1);
5929 if (i == FAIL)
5930 clearopbeep(oap);
5931 break;
5932
5933 /*
5934 * "gJ": join two lines without inserting a space.
5935 */
5936 case 'J':
5937 nv_join(cap);
5938 break;
5939
5940 /*
5941 * "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
5942 * "gm": middle of "g0" and "g$".
5943 */
5944 case '^':
5945 flag = TRUE;
5946 /* FALLTHROUGH */
5947
5948 case '0':
5949 case 'm':
5950 case K_HOME:
5951 case K_KHOME:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952 oap->motion_type = MCHAR;
5953 oap->inclusive = FALSE;
Bram Moolenaar4033c552017-09-16 20:54:51 +02005954 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005955 {
Bram Moolenaar02631462017-09-22 15:20:32 +02005956 int width1 = curwin->w_width - curwin_col_off();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005957 int width2 = width1 + curwin_col_off2();
5958
5959 validate_virtcol();
5960 i = 0;
5961 if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0)
5962 i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
5963 }
5964 else
5965 i = curwin->w_leftcol;
Bram Moolenaar64486672010-05-16 15:46:46 +02005966 /* Go to the middle of the screen line. When 'number' or
5967 * 'relativenumber' is on and lines are wrapping the middle can be more
5968 * to the left. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005969 if (cap->nchar == 'm')
Bram Moolenaar02631462017-09-22 15:20:32 +02005970 i += (curwin->w_width - curwin_col_off()
Bram Moolenaar071d4272004-06-13 20:20:40 +00005971 + ((curwin->w_p_wrap && i > 0)
5972 ? curwin_col_off2() : 0)) / 2;
5973 coladvance((colnr_T)i);
5974 if (flag)
5975 {
5976 do
5977 i = gchar_cursor();
Bram Moolenaar1c465442017-03-12 20:10:05 +01005978 while (VIM_ISWHITE(i) && oneright() == OK);
Bram Moolenaarf9514162018-11-22 03:08:29 +01005979 curwin->w_valid &= ~VALID_WCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005980 }
5981 curwin->w_set_curswant = TRUE;
5982 break;
5983
Bram Moolenaar8b530c12019-10-28 02:13:05 +01005984 case 'M':
5985 {
5986 char_u *ptr = ml_get_curline();
5987
5988 oap->motion_type = MCHAR;
5989 oap->inclusive = FALSE;
5990 if (has_mbyte)
Bram Moolenaar69bf6342019-10-29 04:16:57 +01005991 i = mb_string2cells(ptr, (int)STRLEN(ptr));
Bram Moolenaar8b530c12019-10-28 02:13:05 +01005992 else
5993 i = (int)STRLEN(ptr);
5994 if (cap->count0 > 0 && cap->count0 <= 100)
5995 coladvance((colnr_T)(i * cap->count0 / 100));
5996 else
5997 coladvance((colnr_T)(i / 2));
5998 curwin->w_set_curswant = TRUE;
5999 }
6000 break;
6001
Bram Moolenaar071d4272004-06-13 20:20:40 +00006002 case '_':
6003 /* "g_": to the last non-blank character in the line or <count> lines
6004 * downward. */
6005 cap->oap->motion_type = MCHAR;
6006 cap->oap->inclusive = TRUE;
6007 curwin->w_curswant = MAXCOL;
6008 if (cursor_down((long)(cap->count1 - 1),
6009 cap->oap->op_type == OP_NOP) == FAIL)
6010 clearopbeep(cap->oap);
6011 else
6012 {
6013 char_u *ptr = ml_get_curline();
6014
6015 /* In Visual mode we may end up after the line. */
6016 if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL)
6017 --curwin->w_cursor.col;
6018
6019 /* Decrease the cursor column until it's on a non-blank. */
6020 while (curwin->w_cursor.col > 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01006021 && VIM_ISWHITE(ptr[curwin->w_cursor.col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006022 --curwin->w_cursor.col;
6023 curwin->w_set_curswant = TRUE;
Bram Moolenaar5890b2c2010-01-12 15:42:37 +01006024 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006025 }
6026 break;
6027
6028 case '$':
6029 case K_END:
6030 case K_KEND:
Bram Moolenaar071d4272004-06-13 20:20:40 +00006031 {
6032 int col_off = curwin_col_off();
6033
6034 oap->motion_type = MCHAR;
6035 oap->inclusive = TRUE;
Bram Moolenaar4033c552017-09-16 20:54:51 +02006036 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006037 {
6038 curwin->w_curswant = MAXCOL; /* so we stay at the end */
6039 if (cap->count1 == 1)
6040 {
Bram Moolenaar02631462017-09-22 15:20:32 +02006041 int width1 = curwin->w_width - col_off;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042 int width2 = width1 + curwin_col_off2();
6043
6044 validate_virtcol();
6045 i = width1 - 1;
6046 if (curwin->w_virtcol >= (colnr_T)width1)
6047 i += ((curwin->w_virtcol - width1) / width2 + 1)
6048 * width2;
6049 coladvance((colnr_T)i);
Bram Moolenaarb69510e2013-07-09 17:08:29 +02006050
6051 /* Make sure we stick in this column. */
6052 validate_virtcol();
6053 curwin->w_curswant = curwin->w_virtcol;
6054 curwin->w_set_curswant = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006055 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
6056 {
6057 /*
6058 * Check for landing on a character that got split at
6059 * the end of the line. We do not want to advance to
6060 * the next screen line.
6061 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006062 if (curwin->w_virtcol > (colnr_T)i)
6063 --curwin->w_cursor.col;
6064 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006065 }
6066 else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == FAIL)
6067 clearopbeep(oap);
6068 }
6069 else
6070 {
Bram Moolenaard5c82342019-07-27 18:44:57 +02006071 if (cap->count1 > 1)
6072 // if it fails, let the cursor still move to the last char
Bram Moolenaar9c272a92019-08-16 21:54:27 +02006073 (void)cursor_down(cap->count1 - 1, FALSE);
Bram Moolenaard5c82342019-07-27 18:44:57 +02006074
Bram Moolenaar02631462017-09-22 15:20:32 +02006075 i = curwin->w_leftcol + curwin->w_width - col_off - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006076 coladvance((colnr_T)i);
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00006077
Bram Moolenaard5c82342019-07-27 18:44:57 +02006078 // Make sure we stick in this column.
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00006079 validate_virtcol();
6080 curwin->w_curswant = curwin->w_virtcol;
6081 curwin->w_set_curswant = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006082 }
6083 }
6084 break;
6085
6086 /*
6087 * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
6088 */
6089 case '*':
6090 case '#':
6091#if POUND != '#'
6092 case POUND: /* pound sign (sometimes equal to '#') */
6093#endif
6094 case Ctrl_RSB: /* :tag or :tselect for current identifier */
6095 case ']': /* :tselect for current identifier */
6096 nv_ident(cap);
6097 break;
6098
6099 /*
6100 * ge and gE: go back to end of word
6101 */
6102 case 'e':
6103 case 'E':
6104 oap->motion_type = MCHAR;
6105 curwin->w_set_curswant = TRUE;
6106 oap->inclusive = TRUE;
6107 if (bckend_word(cap->count1, cap->nchar == 'E', FALSE) == FAIL)
6108 clearopbeep(oap);
6109 break;
6110
6111 /*
6112 * "g CTRL-G": display info about cursor position
6113 */
6114 case Ctrl_G:
Bram Moolenaared767a22016-01-03 22:49:16 +01006115 cursor_pos_info(NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006116 break;
6117
6118 /*
6119 * "gi": start Insert at the last position.
6120 */
6121 case 'i':
6122 if (curbuf->b_last_insert.lnum != 0)
6123 {
6124 curwin->w_cursor = curbuf->b_last_insert;
6125 check_cursor_lnum();
6126 i = (int)STRLEN(ml_get_curline());
6127 if (curwin->w_cursor.col > (colnr_T)i)
6128 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006129 if (virtual_active())
6130 curwin->w_cursor.coladd += curwin->w_cursor.col - i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006131 curwin->w_cursor.col = i;
6132 }
6133 }
6134 cap->cmdchar = 'i';
6135 nv_edit(cap);
6136 break;
6137
6138 /*
6139 * "gI": Start insert in column 1.
6140 */
6141 case 'I':
6142 beginline(0);
6143 if (!checkclearopq(oap))
6144 invoke_edit(cap, FALSE, 'g', FALSE);
6145 break;
6146
6147#ifdef FEAT_SEARCHPATH
6148 /*
6149 * "gf": goto file, edit file under cursor
6150 * "]f" and "[f": can also be used.
6151 */
6152 case 'f':
Bram Moolenaard1f56e62006-02-22 21:25:37 +00006153 case 'F':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006154 nv_gotofile(cap);
6155 break;
6156#endif
6157
6158 /* "g'm" and "g`m": jump to mark without setting pcmark */
6159 case '\'':
6160 cap->arg = TRUE;
Bram Moolenaar2f40d122017-10-24 21:49:36 +02006161 /* FALLTHROUGH */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006162 case '`':
6163 nv_gomark(cap);
6164 break;
6165
6166 /*
6167 * "gs": Goto sleep.
6168 */
6169 case 's':
6170 do_sleep(cap->count1 * 1000L);
6171 break;
6172
6173 /*
6174 * "ga": Display the ascii value of the character under the
6175 * cursor. It is displayed in decimal, hex, and octal. -- webb
6176 */
6177 case 'a':
6178 do_ascii(NULL);
6179 break;
6180
Bram Moolenaar071d4272004-06-13 20:20:40 +00006181 /*
6182 * "g8": Display the bytes used for the UTF-8 character under the
6183 * cursor. It is displayed in hex.
Bram Moolenaara83c3e02006-03-17 23:10:44 +00006184 * "8g8" finds illegal byte sequence.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006185 */
6186 case '8':
Bram Moolenaara83c3e02006-03-17 23:10:44 +00006187 if (cap->count0 == 8)
6188 utf_find_illegal();
6189 else
6190 show_utf8();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006191 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192
Bram Moolenaar60402d62017-04-20 18:54:50 +02006193 /* "g<": show scrollback text */
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00006194 case '<':
6195 show_sb_text();
6196 break;
6197
Bram Moolenaar071d4272004-06-13 20:20:40 +00006198 /*
6199 * "gg": Goto the first line in file. With a count it goes to
6200 * that line number like for "G". -- webb
6201 */
6202 case 'g':
6203 cap->arg = FALSE;
6204 nv_goto(cap);
6205 break;
6206
6207 /*
6208 * Two-character operators:
6209 * "gq" Format text
6210 * "gw" Format text and keep cursor position
6211 * "g~" Toggle the case of the text.
6212 * "gu" Change text to lower case.
6213 * "gU" Change text to upper case.
6214 * "g?" rot13 encoding
Bram Moolenaar12033fb2005-12-16 21:49:31 +00006215 * "g@" call 'operatorfunc'
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216 */
6217 case 'q':
6218 case 'w':
6219 oap->cursor_start = curwin->w_cursor;
Bram Moolenaar2f40d122017-10-24 21:49:36 +02006220 /* FALLTHROUGH */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006221 case '~':
6222 case 'u':
6223 case 'U':
6224 case '?':
Bram Moolenaar12033fb2005-12-16 21:49:31 +00006225 case '@':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006226 nv_operator(cap);
6227 break;
6228
6229 /*
Bram Moolenaarf711faf2007-05-10 16:48:19 +00006230 * "gd": Find first occurrence of pattern under the cursor in the
Bram Moolenaar071d4272004-06-13 20:20:40 +00006231 * current function
6232 * "gD": idem, but in the current file.
6233 */
6234 case 'd':
6235 case 'D':
Bram Moolenaarf75a9632005-09-13 21:20:47 +00006236 nv_gd(oap, cap->nchar, (int)cap->count0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006237 break;
6238
Bram Moolenaar071d4272004-06-13 20:20:40 +00006239 /*
6240 * g<*Mouse> : <C-*mouse>
6241 */
6242 case K_MIDDLEMOUSE:
6243 case K_MIDDLEDRAG:
6244 case K_MIDDLERELEASE:
6245 case K_LEFTMOUSE:
6246 case K_LEFTDRAG:
6247 case K_LEFTRELEASE:
Bram Moolenaar51b0f372017-11-18 18:52:04 +01006248 case K_MOUSEMOVE:
Bram Moolenaar071d4272004-06-13 20:20:40 +00006249 case K_RIGHTMOUSE:
6250 case K_RIGHTDRAG:
6251 case K_RIGHTRELEASE:
6252 case K_X1MOUSE:
6253 case K_X1DRAG:
6254 case K_X1RELEASE:
6255 case K_X2MOUSE:
6256 case K_X2DRAG:
6257 case K_X2RELEASE:
6258 mod_mask = MOD_MASK_CTRL;
6259 (void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0);
6260 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006261
6262 case K_IGNORE:
6263 break;
6264
6265 /*
6266 * "gP" and "gp": same as "P" and "p" but leave cursor just after new text
6267 */
6268 case 'p':
6269 case 'P':
6270 nv_put(cap);
6271 break;
6272
6273#ifdef FEAT_BYTEOFF
6274 /* "go": goto byte count from start of buffer */
6275 case 'o':
6276 goto_byte(cap->count0);
6277 break;
6278#endif
6279
6280 /* "gQ": improved Ex mode */
6281 case 'Q':
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00006282 if (text_locked())
Bram Moolenaar071d4272004-06-13 20:20:40 +00006283 {
6284 clearopbeep(cap->oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00006285 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006286 break;
6287 }
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00006288
Bram Moolenaar071d4272004-06-13 20:20:40 +00006289 if (!checkclearopq(oap))
6290 do_exmode(TRUE);
6291 break;
6292
6293#ifdef FEAT_JUMPLIST
6294 case ',':
6295 nv_pcmark(cap);
6296 break;
6297
6298 case ';':
6299 cap->count1 = -cap->count1;
6300 nv_pcmark(cap);
6301 break;
6302#endif
6303
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006304 case 't':
Bram Moolenaar89f940f2012-06-29 13:56:06 +02006305 if (!checkclearop(oap))
6306 goto_tabpage((int)cap->count0);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006307 break;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00006308 case 'T':
Bram Moolenaar89f940f2012-06-29 13:56:06 +02006309 if (!checkclearop(oap))
6310 goto_tabpage(-(int)cap->count1);
Bram Moolenaar80a94a52006-02-23 21:26:58 +00006311 break;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006312
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006313 case '+':
6314 case '-': /* "g+" and "g-": undo or redo along the timeline */
6315 if (!checkclearopq(oap))
Bram Moolenaard3667a22006-03-16 21:35:52 +00006316 undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1,
Bram Moolenaar730cde92010-06-27 05:18:54 +02006317 FALSE, FALSE, FALSE);
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006318 break;
6319
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320 default:
6321 clearopbeep(oap);
6322 break;
6323 }
6324}
6325
6326/*
6327 * Handle "o" and "O" commands.
6328 */
6329 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006330n_opencmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331{
Bram Moolenaar860cae12010-06-05 23:22:07 +02006332#ifdef FEAT_CONCEAL
6333 linenr_T oldline = curwin->w_cursor.lnum;
6334#endif
6335
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 if (!checkclearopq(cap->oap))
6337 {
6338#ifdef FEAT_FOLDING
6339 if (cap->cmdchar == 'O')
6340 /* Open above the first line of a folded sequence of lines */
6341 (void)hasFolding(curwin->w_cursor.lnum,
6342 &curwin->w_cursor.lnum, NULL);
6343 else
6344 /* Open below the last line of a folded sequence of lines */
6345 (void)hasFolding(curwin->w_cursor.lnum,
6346 NULL, &curwin->w_cursor.lnum);
6347#endif
6348 if (u_save((linenr_T)(curwin->w_cursor.lnum -
6349 (cap->cmdchar == 'O' ? 1 : 0)),
6350 (linenr_T)(curwin->w_cursor.lnum +
6351 (cap->cmdchar == 'o' ? 1 : 0))
6352 ) == OK
6353 && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
Bram Moolenaar8c96af92019-09-28 19:05:57 +02006354 has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0,
6355 0) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006356 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006357#ifdef FEAT_CONCEAL
Bram Moolenaarf5963f72010-07-23 22:10:27 +02006358 if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum)
Bram Moolenaar535d5b62019-01-11 20:45:36 +01006359 redrawWinline(curwin, oldline);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006360#endif
Bram Moolenaard710e0d2015-06-10 12:16:47 +02006361#ifdef FEAT_SYN_HL
Bram Moolenaard0d0fe02015-06-09 19:23:46 +02006362 if (curwin->w_p_cul)
6363 /* force redraw of cursorline */
6364 curwin->w_valid &= ~VALID_CROW;
Bram Moolenaard710e0d2015-06-10 12:16:47 +02006365#endif
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006366 /* When '#' is in 'cpoptions' ignore the count. */
6367 if (vim_strchr(p_cpo, CPO_HASH) != NULL)
6368 cap->count1 = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006369 invoke_edit(cap, FALSE, cap->cmdchar, TRUE);
6370 }
6371 }
6372}
6373
6374/*
6375 * "." command: redo last change.
6376 */
6377 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006378nv_dot(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006379{
6380 if (!checkclearopq(cap->oap))
6381 {
6382 /*
6383 * If "restart_edit" is TRUE, the last but one command is repeated
6384 * instead of the last command (inserting text). This is used for
6385 * CTRL-O <.> in insert mode.
6386 */
6387 if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == FAIL)
6388 clearopbeep(cap->oap);
6389 }
6390}
6391
6392/*
6393 * CTRL-R: undo undo
6394 */
6395 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006396nv_redo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006397{
6398 if (!checkclearopq(cap->oap))
6399 {
6400 u_redo((int)cap->count1);
6401 curwin->w_set_curswant = TRUE;
6402 }
6403}
6404
6405/*
6406 * Handle "U" command.
6407 */
6408 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006409nv_Undo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006410{
6411 /* In Visual mode and typing "gUU" triggers an operator */
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006412 if (cap->oap->op_type == OP_UPPER || VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006413 {
6414 /* translate "gUU" to "gUgU" */
6415 cap->cmdchar = 'g';
6416 cap->nchar = 'U';
6417 nv_operator(cap);
6418 }
6419 else if (!checkclearopq(cap->oap))
6420 {
6421 u_undoline();
6422 curwin->w_set_curswant = TRUE;
6423 }
6424}
6425
6426/*
6427 * '~' command: If tilde is not an operator and Visual is off: swap case of a
6428 * single character.
6429 */
6430 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006431nv_tilde(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006432{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006433 if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE)
Bram Moolenaarf2732452018-06-03 14:47:35 +02006434 {
6435#ifdef FEAT_JOB_CHANNEL
6436 if (bt_prompt(curbuf) && !prompt_curpos_editable())
6437 {
6438 clearopbeep(cap->oap);
6439 return;
6440 }
6441#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006442 n_swapchar(cap);
Bram Moolenaarf2732452018-06-03 14:47:35 +02006443 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006444 else
6445 nv_operator(cap);
6446}
6447
6448/*
6449 * Handle an operator command.
6450 * The actual work is done by do_pending_operator().
6451 */
6452 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006453nv_operator(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006454{
6455 int op_type;
6456
6457 op_type = get_op_type(cap->cmdchar, cap->nchar);
Bram Moolenaarf2732452018-06-03 14:47:35 +02006458#ifdef FEAT_JOB_CHANNEL
6459 if (bt_prompt(curbuf) && op_is_change(op_type) && !prompt_curpos_editable())
6460 {
6461 clearopbeep(cap->oap);
6462 return;
6463 }
6464#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006465
6466 if (op_type == cap->oap->op_type) /* double operator works on lines */
6467 nv_lineop(cap);
6468 else if (!checkclearop(cap->oap))
6469 {
6470 cap->oap->start = curwin->w_cursor;
6471 cap->oap->op_type = op_type;
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006472#ifdef FEAT_EVAL
6473 set_op_var(op_type);
6474#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006475 }
6476}
6477
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006478#ifdef FEAT_EVAL
6479/*
6480 * Set v:operator to the characters for "optype".
6481 */
6482 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006483set_op_var(int optype)
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006484{
6485 char_u opchars[3];
6486
6487 if (optype == OP_NOP)
6488 set_vim_var_string(VV_OP, NULL, 0);
6489 else
6490 {
6491 opchars[0] = get_op_char(optype);
6492 opchars[1] = get_extra_op_char(optype);
6493 opchars[2] = NUL;
6494 set_vim_var_string(VV_OP, opchars, -1);
6495 }
6496}
6497#endif
6498
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499/*
6500 * Handle linewise operator "dd", "yy", etc.
6501 *
6502 * "_" is is a strange motion command that helps make operators more logical.
6503 * It is actually implemented, but not documented in the real Vi. This motion
6504 * command actually refers to "the current line". Commands like "dd" and "yy"
6505 * are really an alternate form of "d_" and "y_". It does accept a count, so
6506 * "d3_" works to delete 3 lines.
6507 */
6508 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006509nv_lineop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006510{
6511 cap->oap->motion_type = MLINE;
6512 if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == FAIL)
6513 clearopbeep(cap->oap);
Bram Moolenaar83dadaf2012-12-12 17:33:32 +01006514 else if ( (cap->oap->op_type == OP_DELETE /* only with linewise motions */
6515 && cap->oap->motion_force != 'v'
6516 && cap->oap->motion_force != Ctrl_V)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006517 || cap->oap->op_type == OP_LSHIFT
6518 || cap->oap->op_type == OP_RSHIFT)
6519 beginline(BL_SOL | BL_FIX);
6520 else if (cap->oap->op_type != OP_YANK) /* 'Y' does not move cursor */
6521 beginline(BL_WHITE | BL_FIX);
6522}
6523
6524/*
6525 * <Home> command.
6526 */
6527 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006528nv_home(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006529{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00006530 /* CTRL-HOME is like "gg" */
6531 if (mod_mask & MOD_MASK_CTRL)
6532 nv_goto(cap);
6533 else
6534 {
6535 cap->count0 = 1;
6536 nv_pipe(cap);
6537 }
Bram Moolenaara88d9682005-03-25 21:45:43 +00006538 ins_at_eol = FALSE; /* Don't move cursor past eol (only necessary in a
6539 one-character line). */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006540}
6541
6542/*
6543 * "|" command.
6544 */
6545 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006546nv_pipe(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006547{
6548 cap->oap->motion_type = MCHAR;
6549 cap->oap->inclusive = FALSE;
6550 beginline(0);
6551 if (cap->count0 > 0)
6552 {
6553 coladvance((colnr_T)(cap->count0 - 1));
6554 curwin->w_curswant = (colnr_T)(cap->count0 - 1);
6555 }
6556 else
6557 curwin->w_curswant = 0;
6558 /* keep curswant at the column where we wanted to go, not where
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01006559 * we ended; differs if line is too short */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560 curwin->w_set_curswant = FALSE;
6561}
6562
6563/*
6564 * Handle back-word command "b" and "B".
6565 * cap->arg is 1 for "B"
6566 */
6567 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006568nv_bck_word(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006569{
6570 cap->oap->motion_type = MCHAR;
6571 cap->oap->inclusive = FALSE;
6572 curwin->w_set_curswant = TRUE;
6573 if (bck_word(cap->count1, cap->arg, FALSE) == FAIL)
6574 clearopbeep(cap->oap);
6575#ifdef FEAT_FOLDING
6576 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6577 foldOpenCursor();
6578#endif
6579}
6580
6581/*
6582 * Handle word motion commands "e", "E", "w" and "W".
6583 * cap->arg is TRUE for "E" and "W".
6584 */
6585 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006586nv_wordcmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006587{
6588 int n;
6589 int word_end;
6590 int flag = FALSE;
Bram Moolenaardfefb982008-04-01 10:06:39 +00006591 pos_T startpos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006592
6593 /*
6594 * Set inclusive for the "E" and "e" command.
6595 */
6596 if (cap->cmdchar == 'e' || cap->cmdchar == 'E')
6597 word_end = TRUE;
6598 else
6599 word_end = FALSE;
6600 cap->oap->inclusive = word_end;
6601
6602 /*
6603 * "cw" and "cW" are a special case.
6604 */
6605 if (!word_end && cap->oap->op_type == OP_CHANGE)
6606 {
6607 n = gchar_cursor();
6608 if (n != NUL) /* not an empty line */
6609 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006610 if (VIM_ISWHITE(n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006611 {
6612 /*
6613 * Reproduce a funny Vi behaviour: "cw" on a blank only
6614 * changes one character, not all blanks until the start of
6615 * the next word. Only do this when the 'w' flag is included
6616 * in 'cpoptions'.
6617 */
6618 if (cap->count1 == 1 && vim_strchr(p_cpo, CPO_CW) != NULL)
6619 {
6620 cap->oap->inclusive = TRUE;
6621 cap->oap->motion_type = MCHAR;
6622 return;
6623 }
6624 }
6625 else
6626 {
6627 /*
6628 * This is a little strange. To match what the real Vi does,
6629 * we effectively map 'cw' to 'ce', and 'cW' to 'cE', provided
6630 * that we are not on a space or a TAB. This seems impolite
6631 * at first, but it's really more what we mean when we say
6632 * 'cw'.
6633 * Another strangeness: When standing on the end of a word
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006634 * "ce" will change until the end of the next word, but "cw"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006635 * will change only one character! This is done by setting
6636 * flag.
6637 */
6638 cap->oap->inclusive = TRUE;
6639 word_end = TRUE;
6640 flag = TRUE;
6641 }
6642 }
6643 }
6644
6645 cap->oap->motion_type = MCHAR;
6646 curwin->w_set_curswant = TRUE;
6647 if (word_end)
6648 n = end_word(cap->count1, cap->arg, flag, FALSE);
6649 else
6650 n = fwd_word(cap->count1, cap->arg, cap->oap->op_type != OP_NOP);
6651
Bram Moolenaardfefb982008-04-01 10:06:39 +00006652 /* Don't leave the cursor on the NUL past the end of line. Unless we
6653 * didn't move it forward. */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006654 if (LT_POS(startpos, curwin->w_cursor))
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006655 adjust_cursor(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656
6657 if (n == FAIL && cap->oap->op_type == OP_NOP)
6658 clearopbeep(cap->oap);
6659 else
6660 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006661 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006662#ifdef FEAT_FOLDING
6663 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6664 foldOpenCursor();
6665#endif
6666 }
6667}
6668
6669/*
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006670 * Used after a movement command: If the cursor ends up on the NUL after the
6671 * end of the line, may move it back to the last character and make the motion
6672 * inclusive.
6673 */
6674 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006675adjust_cursor(oparg_T *oap)
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006676{
6677 /* The cursor cannot remain on the NUL when:
6678 * - the column is > 0
6679 * - not in Visual mode or 'selection' is "o"
6680 * - 'virtualedit' is not "all" and not "onemore".
6681 */
6682 if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006683 && (!VIsual_active || *p_sel == 'o')
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01006684 && !virtual_active() && (ve_flags & VE_ONEMORE) == 0)
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006685 {
6686 --curwin->w_cursor.col;
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006687 /* prevent cursor from moving on the trail byte */
6688 if (has_mbyte)
6689 mb_adjust_cursor();
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006690 oap->inclusive = TRUE;
6691 }
6692}
6693
6694/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006695 * "0" and "^" commands.
6696 * cap->arg is the argument for beginline().
6697 */
6698 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006699nv_beginline(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006700{
6701 cap->oap->motion_type = MCHAR;
6702 cap->oap->inclusive = FALSE;
6703 beginline(cap->arg);
6704#ifdef FEAT_FOLDING
6705 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6706 foldOpenCursor();
6707#endif
Bram Moolenaara5792f52005-11-23 21:25:05 +00006708 ins_at_eol = FALSE; /* Don't move cursor past eol (only necessary in a
6709 one-character line). */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006710}
6711
Bram Moolenaar071d4272004-06-13 20:20:40 +00006712/*
6713 * In exclusive Visual mode, may include the last character.
6714 */
6715 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006716adjust_for_sel(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006717{
6718 if (VIsual_active && cap->oap->inclusive && *p_sel == 'e'
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006719 && gchar_cursor() != NUL && LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006720 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006721 if (has_mbyte)
6722 inc_cursor();
6723 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006724 ++curwin->w_cursor.col;
6725 cap->oap->inclusive = FALSE;
6726 }
6727}
6728
6729/*
6730 * Exclude last character at end of Visual area for 'selection' == "exclusive".
6731 * Should check VIsual_mode before calling this.
6732 * Returns TRUE when backed up to the previous line.
6733 */
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02006734 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01006735unadjust_for_sel(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006736{
6737 pos_T *pp;
6738
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006739 if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006740 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006741 if (LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006742 pp = &curwin->w_cursor;
6743 else
6744 pp = &VIsual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745 if (pp->coladd > 0)
6746 --pp->coladd;
6747 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006748 if (pp->col > 0)
6749 {
6750 --pp->col;
Bram Moolenaar03a807a2011-07-07 15:08:58 +02006751 mb_adjustpos(curbuf, pp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006752 }
6753 else if (pp->lnum > 1)
6754 {
6755 --pp->lnum;
6756 pp->col = (colnr_T)STRLEN(ml_get(pp->lnum));
6757 return TRUE;
6758 }
6759 }
6760 return FALSE;
6761}
6762
6763/*
6764 * SELECT key in Normal or Visual mode: end of Select mode mapping.
6765 */
6766 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006767nv_select(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006768{
6769 if (VIsual_active)
6770 VIsual_select = TRUE;
6771 else if (VIsual_reselect)
6772 {
6773 cap->nchar = 'v'; /* fake "gv" command */
6774 cap->arg = TRUE;
6775 nv_g_cmd(cap);
6776 }
6777}
6778
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779
6780/*
6781 * "G", "gg", CTRL-END, CTRL-HOME.
6782 * cap->arg is TRUE for "G".
6783 */
6784 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006785nv_goto(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006786{
6787 linenr_T lnum;
6788
6789 if (cap->arg)
6790 lnum = curbuf->b_ml.ml_line_count;
6791 else
6792 lnum = 1L;
6793 cap->oap->motion_type = MLINE;
6794 setpcmark();
6795
6796 /* When a count is given, use it instead of the default lnum */
6797 if (cap->count0 != 0)
6798 lnum = cap->count0;
6799 if (lnum < 1L)
6800 lnum = 1L;
6801 else if (lnum > curbuf->b_ml.ml_line_count)
6802 lnum = curbuf->b_ml.ml_line_count;
6803 curwin->w_cursor.lnum = lnum;
6804 beginline(BL_SOL | BL_FIX);
6805#ifdef FEAT_FOLDING
6806 if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP)
6807 foldOpenCursor();
6808#endif
6809}
6810
6811/*
6812 * CTRL-\ in Normal mode.
6813 */
6814 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006815nv_normal(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006816{
6817 if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G)
6818 {
6819 clearop(cap->oap);
Bram Moolenaar28c258f2006-01-25 22:02:51 +00006820 if (restart_edit != 0 && mode_displayed)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006821 clear_cmdline = TRUE; /* unshow mode later */
6822 restart_edit = 0;
6823#ifdef FEAT_CMDWIN
6824 if (cmdwin_type != 0)
6825 cmdwin_result = Ctrl_C;
6826#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006827 if (VIsual_active)
6828 {
6829 end_visual_mode(); /* stop Visual */
6830 redraw_curbuf_later(INVERTED);
6831 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006832 /* CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set. */
6833 if (cap->nchar == Ctrl_G && p_im)
6834 restart_edit = 'a';
6835 }
6836 else
6837 clearopbeep(cap->oap);
6838}
6839
6840/*
6841 * ESC in Normal mode: beep, but don't flush buffers.
6842 * Don't even beep if we are canceling a command.
6843 */
6844 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006845nv_esc(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006846{
6847 int no_reason;
6848
6849 no_reason = (cap->oap->op_type == OP_NOP
6850 && cap->opcount == 0
6851 && cap->count0 == 0
6852 && cap->oap->regname == 0
6853 && !p_im);
6854
6855 if (cap->arg) /* TRUE for CTRL-C */
6856 {
6857 if (restart_edit == 0
6858#ifdef FEAT_CMDWIN
6859 && cmdwin_type == 0
6860#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006861 && !VIsual_active
Bram Moolenaar071d4272004-06-13 20:20:40 +00006862 && no_reason)
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01006863 {
6864 if (anyBufIsChanged())
6865 msg(_("Type :qa! and press <Enter> to abandon all changes and exit Vim"));
6866 else
6867 msg(_("Type :qa and press <Enter> to exit Vim"));
6868 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006869
6870 /* Don't reset "restart_edit" when 'insertmode' is set, it won't be
6871 * set again below when halfway a mapping. */
6872 if (!p_im)
6873 restart_edit = 0;
6874#ifdef FEAT_CMDWIN
6875 if (cmdwin_type != 0)
6876 {
6877 cmdwin_result = K_IGNORE;
6878 got_int = FALSE; /* don't stop executing autocommands et al. */
6879 return;
6880 }
6881#endif
6882 }
6883
Bram Moolenaar071d4272004-06-13 20:20:40 +00006884 if (VIsual_active)
6885 {
6886 end_visual_mode(); /* stop Visual */
6887 check_cursor_col(); /* make sure cursor is not beyond EOL */
6888 curwin->w_set_curswant = TRUE;
6889 redraw_curbuf_later(INVERTED);
6890 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006891 else if (no_reason)
Bram Moolenaar165bc692015-07-21 17:53:25 +02006892 vim_beep(BO_ESC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006893 clearop(cap->oap);
6894
6895 /* A CTRL-C is often used at the start of a menu. When 'insertmode' is
6896 * set return to Insert mode afterwards. */
Bram Moolenaare2c38102016-01-31 14:55:40 +01006897 if (restart_edit == 0 && goto_im() && ex_normal_busy == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006898 restart_edit = 'a';
6899}
6900
6901/*
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02006902 * Move the cursor for the "A" command.
6903 */
6904 void
6905set_cursor_for_append_to_line(void)
6906{
6907 curwin->w_set_curswant = TRUE;
6908 if (ve_flags == VE_ALL)
6909 {
6910 int save_State = State;
6911
6912 /* Pretend Insert mode here to allow the cursor on the
6913 * character past the end of the line */
6914 State = INSERT;
6915 coladvance((colnr_T)MAXCOL);
6916 State = save_State;
6917 }
6918 else
6919 curwin->w_cursor.col += (colnr_T)STRLEN(ml_get_cursor());
6920}
6921
6922/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006923 * Handle "A", "a", "I", "i" and <Insert> commands.
Bram Moolenaarec2da362017-01-21 20:04:22 +01006924 * Also handle K_PS, start bracketed paste.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006925 */
6926 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006927nv_edit(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006928{
6929 /* <Insert> is equal to "i" */
6930 if (cap->cmdchar == K_INS || cap->cmdchar == K_KINS)
6931 cap->cmdchar = 'i';
6932
Bram Moolenaar071d4272004-06-13 20:20:40 +00006933 /* in Visual mode "A" and "I" are an operator */
6934 if (VIsual_active && (cap->cmdchar == 'A' || cap->cmdchar == 'I'))
Bram Moolenaareef9add2017-09-16 15:38:04 +02006935 {
6936#ifdef FEAT_TERMINAL
6937 if (term_in_normal_mode())
6938 {
6939 end_visual_mode();
6940 clearop(cap->oap);
6941 term_enter_job_mode();
6942 return;
6943 }
6944#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006945 v_visop(cap);
Bram Moolenaareef9add2017-09-16 15:38:04 +02006946 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006947
6948 /* in Visual mode and after an operator "a" and "i" are for text objects */
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006949 else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
6950 && (cap->oap->op_type != OP_NOP || VIsual_active))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006951 {
6952#ifdef FEAT_TEXTOBJ
6953 nv_object(cap);
6954#else
6955 clearopbeep(cap->oap);
6956#endif
6957 }
Bram Moolenaar662d9382017-07-31 22:56:24 +02006958#ifdef FEAT_TERMINAL
Bram Moolenaar6d819742017-08-06 14:57:49 +02006959 else if (term_in_normal_mode())
Bram Moolenaar662d9382017-07-31 22:56:24 +02006960 {
6961 clearop(cap->oap);
Bram Moolenaar6d819742017-08-06 14:57:49 +02006962 term_enter_job_mode();
Bram Moolenaar662d9382017-07-31 22:56:24 +02006963 return;
6964 }
6965#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006966 else if (!curbuf->b_p_ma && !p_im)
6967 {
6968 /* Only give this error when 'insertmode' is off. */
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006969 emsg(_(e_modifiable));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006970 clearop(cap->oap);
Bram Moolenaarec2da362017-01-21 20:04:22 +01006971 if (cap->cmdchar == K_PS)
6972 /* drop the pasted text */
6973 bracketed_paste(PASTE_INSERT, TRUE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006974 }
Bram Moolenaara1891842017-02-04 21:34:31 +01006975 else if (cap->cmdchar == K_PS && VIsual_active)
6976 {
6977 pos_T old_pos = curwin->w_cursor;
6978 pos_T old_visual = VIsual;
6979
6980 /* In Visual mode the selected text is deleted. */
6981 if (VIsual_mode == 'V' || curwin->w_cursor.lnum != VIsual.lnum)
6982 {
6983 shift_delete_registers();
6984 cap->oap->regname = '1';
6985 }
6986 else
6987 cap->oap->regname = '-';
6988 cap->cmdchar = 'd';
6989 cap->nchar = NUL;
6990 nv_operator(cap);
6991 do_pending_operator(cap, 0, FALSE);
6992 cap->cmdchar = K_PS;
6993
6994 /* When the last char in the line was deleted then append. Detect this
6995 * by checking if the cursor moved to before the Visual area. */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006996 if (*ml_get_cursor() != NUL && LT_POS(curwin->w_cursor, old_pos)
6997 && LT_POS(curwin->w_cursor, old_visual))
Bram Moolenaara1891842017-02-04 21:34:31 +01006998 inc_cursor();
6999
7000 /* Insert to replace the deleted text with the pasted text. */
7001 invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
7002 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007003 else if (!checkclearopq(cap->oap))
7004 {
7005 switch (cap->cmdchar)
7006 {
7007 case 'A': /* "A"ppend after the line */
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02007008 set_cursor_for_append_to_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007009 break;
7010
7011 case 'I': /* "I"nsert before the first non-blank */
Bram Moolenaar4399ef42005-02-12 14:29:27 +00007012 if (vim_strchr(p_cpo, CPO_INSEND) == NULL)
7013 beginline(BL_WHITE);
7014 else
7015 beginline(BL_WHITE|BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007016 break;
7017
Bram Moolenaara1891842017-02-04 21:34:31 +01007018 case K_PS:
7019 /* Bracketed paste works like "a"ppend, unless the cursor is in
7020 * the first column, then it inserts. */
Bram Moolenaarfd8983b2017-02-02 22:21:29 +01007021 if (curwin->w_cursor.col == 0)
7022 break;
Bram Moolenaar2f40d122017-10-24 21:49:36 +02007023 /* FALLTHROUGH */
Bram Moolenaarfd8983b2017-02-02 22:21:29 +01007024
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025 case 'a': /* "a"ppend is like "i"nsert on the next character. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007026 /* increment coladd when in virtual space, increment the
7027 * column otherwise, also to append after an unprintable char */
7028 if (virtual_active()
7029 && (curwin->w_cursor.coladd > 0
7030 || *ml_get_cursor() == NUL
7031 || *ml_get_cursor() == TAB))
7032 curwin->w_cursor.coladd++;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01007033 else if (*ml_get_cursor() != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007034 inc_cursor();
7035 break;
7036 }
7037
Bram Moolenaar071d4272004-06-13 20:20:40 +00007038 if (curwin->w_cursor.coladd && cap->cmdchar != 'A')
7039 {
7040 int save_State = State;
7041
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02007042 /* Pretend Insert mode here to allow the cursor on the
Bram Moolenaar071d4272004-06-13 20:20:40 +00007043 * character past the end of the line */
7044 State = INSERT;
7045 coladvance(getviscol());
7046 State = save_State;
7047 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007048
7049 invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
7050 }
Bram Moolenaarec2da362017-01-21 20:04:22 +01007051 else if (cap->cmdchar == K_PS)
7052 /* drop the pasted text */
7053 bracketed_paste(PASTE_INSERT, TRUE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007054}
7055
7056/*
7057 * Invoke edit() and take care of "restart_edit" and the return value.
7058 */
7059 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007060invoke_edit(
7061 cmdarg_T *cap,
7062 int repl, /* "r" or "gr" command */
7063 int cmd,
7064 int startln)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007065{
7066 int restart_edit_save = 0;
7067
7068 /* Complicated: When the user types "a<C-O>a" we don't want to do Insert
7069 * mode recursively. But when doing "a<C-O>." or "a<C-O>rx" we do allow
7070 * it. */
7071 if (repl || !stuff_empty())
7072 restart_edit_save = restart_edit;
7073 else
7074 restart_edit_save = 0;
7075
7076 /* Always reset "restart_edit", this is not a restarted edit. */
7077 restart_edit = 0;
7078
7079 if (edit(cmd, startln, cap->count1))
7080 cap->retval |= CA_COMMAND_BUSY;
7081
7082 if (restart_edit == 0)
7083 restart_edit = restart_edit_save;
7084}
7085
7086#ifdef FEAT_TEXTOBJ
7087/*
7088 * "a" or "i" while an operator is pending or in Visual mode: object motion.
7089 */
7090 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007091nv_object(
7092 cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007093{
7094 int flag;
7095 int include;
7096 char_u *mps_save;
7097
7098 if (cap->cmdchar == 'i')
7099 include = FALSE; /* "ix" = inner object: exclude white space */
7100 else
7101 include = TRUE; /* "ax" = an object: include white space */
7102
7103 /* Make sure (), [], {} and <> are in 'matchpairs' */
7104 mps_save = curbuf->b_p_mps;
7105 curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>";
7106
7107 switch (cap->nchar)
7108 {
7109 case 'w': /* "aw" = a word */
7110 flag = current_word(cap->oap, cap->count1, include, FALSE);
7111 break;
7112 case 'W': /* "aW" = a WORD */
7113 flag = current_word(cap->oap, cap->count1, include, TRUE);
7114 break;
7115 case 'b': /* "ab" = a braces block */
7116 case '(':
7117 case ')':
7118 flag = current_block(cap->oap, cap->count1, include, '(', ')');
7119 break;
7120 case 'B': /* "aB" = a Brackets block */
7121 case '{':
7122 case '}':
7123 flag = current_block(cap->oap, cap->count1, include, '{', '}');
7124 break;
7125 case '[': /* "a[" = a [] block */
7126 case ']':
7127 flag = current_block(cap->oap, cap->count1, include, '[', ']');
7128 break;
7129 case '<': /* "a<" = a <> block */
7130 case '>':
7131 flag = current_block(cap->oap, cap->count1, include, '<', '>');
7132 break;
Bram Moolenaarf8c07b22005-07-19 22:10:03 +00007133 case 't': /* "at" = a tag block (xml and html) */
Bram Moolenaarb6c27352015-03-05 19:57:49 +01007134 /* Do not adjust oap->end in do_pending_operator()
7135 * otherwise there are different results for 'dit'
7136 * (note leading whitespace in last line):
7137 * 1) <b> 2) <b>
7138 * foobar foobar
7139 * </b> </b>
7140 */
7141 cap->retval |= CA_NO_ADJ_OP_END;
Bram Moolenaarf8c07b22005-07-19 22:10:03 +00007142 flag = current_tagblock(cap->oap, cap->count1, include);
7143 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007144 case 'p': /* "ap" = a paragraph */
7145 flag = current_par(cap->oap, cap->count1, include, 'p');
7146 break;
7147 case 's': /* "as" = a sentence */
7148 flag = current_sent(cap->oap, cap->count1, include);
7149 break;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007150 case '"': /* "a"" = a double quoted string */
7151 case '\'': /* "a'" = a single quoted string */
7152 case '`': /* "a`" = a backtick quoted string */
7153 flag = current_quote(cap->oap, cap->count1, include,
7154 cap->nchar);
7155 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007156#if 0 /* TODO */
7157 case 'S': /* "aS" = a section */
7158 case 'f': /* "af" = a filename */
7159 case 'u': /* "au" = a URL */
7160#endif
7161 default:
7162 flag = FAIL;
7163 break;
7164 }
7165
7166 curbuf->b_p_mps = mps_save;
7167 if (flag == FAIL)
7168 clearopbeep(cap->oap);
7169 adjust_cursor_col();
7170 curwin->w_set_curswant = TRUE;
7171}
7172#endif
7173
7174/*
7175 * "q" command: Start/stop recording.
7176 * "q:", "q/", "q?": edit command-line in command-line window.
7177 */
7178 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007179nv_record(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007180{
7181 if (cap->oap->op_type == OP_FORMAT)
7182 {
7183 /* "gqq" is the same as "gqgq": format line */
7184 cap->cmdchar = 'g';
7185 cap->nchar = 'q';
7186 nv_operator(cap);
7187 }
7188 else if (!checkclearop(cap->oap))
7189 {
7190#ifdef FEAT_CMDWIN
7191 if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?')
7192 {
7193 stuffcharReadbuff(cap->nchar);
7194 stuffcharReadbuff(K_CMDWIN);
7195 }
7196 else
7197#endif
7198 /* (stop) recording into a named register, unless executing a
7199 * register */
Bram Moolenaar0b6d9112018-05-22 20:35:17 +02007200 if (reg_executing == 0 && do_record(cap->nchar) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007201 clearopbeep(cap->oap);
7202 }
7203}
7204
7205/*
7206 * Handle the "@r" command.
7207 */
7208 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007209nv_at(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007210{
7211 if (checkclearop(cap->oap))
7212 return;
7213#ifdef FEAT_EVAL
7214 if (cap->nchar == '=')
7215 {
7216 if (get_expr_register() == NUL)
7217 return;
7218 }
7219#endif
7220 while (cap->count1-- && !got_int)
7221 {
Bram Moolenaard333d1e2006-11-07 17:43:47 +00007222 if (do_execreg(cap->nchar, FALSE, FALSE, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007223 {
7224 clearopbeep(cap->oap);
7225 break;
7226 }
7227 line_breakcheck();
7228 }
7229}
7230
7231/*
7232 * Handle the CTRL-U and CTRL-D commands.
7233 */
7234 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007235nv_halfpage(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007236{
7237 if ((cap->cmdchar == Ctrl_U && curwin->w_cursor.lnum == 1)
7238 || (cap->cmdchar == Ctrl_D
7239 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count))
7240 clearopbeep(cap->oap);
7241 else if (!checkclearop(cap->oap))
7242 halfpage(cap->cmdchar == Ctrl_D, cap->count0);
7243}
7244
7245/*
7246 * Handle "J" or "gJ" command.
7247 */
7248 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007249nv_join(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007250{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007251 if (VIsual_active) /* join the visual lines */
7252 nv_operator(cap);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007253 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007254 {
7255 if (cap->count0 <= 1)
7256 cap->count0 = 2; /* default for join is two lines! */
7257 if (curwin->w_cursor.lnum + cap->count0 - 1 >
7258 curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007259 {
Bram Moolenaar41e0f2f2016-03-08 14:44:42 +01007260 /* can't join when on the last line */
7261 if (cap->count0 <= 2)
7262 {
7263 clearopbeep(cap->oap);
7264 return;
7265 }
7266 cap->count0 = curbuf->b_ml.ml_line_count
7267 - curwin->w_cursor.lnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007268 }
Bram Moolenaar41e0f2f2016-03-08 14:44:42 +01007269
7270 prep_redo(cap->oap->regname, cap->count0,
7271 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
7272 (void)do_join(cap->count0, cap->nchar == NUL, TRUE, TRUE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007273 }
7274}
7275
7276/*
7277 * "P", "gP", "p" and "gp" commands.
7278 */
7279 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007280nv_put(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007281{
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02007282 nv_put_opt(cap, FALSE);
7283}
7284
7285/*
7286 * "P", "gP", "p" and "gp" commands.
7287 * "fix_indent" is TRUE for "[p", "[P", "]p" and "]P".
7288 */
7289 static void
7290nv_put_opt(cmdarg_T *cap, int fix_indent)
7291{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007292 int regname = 0;
7293 void *reg1 = NULL, *reg2 = NULL;
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007294 int empty = FALSE;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007295 int was_visual = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007296 int dir;
7297 int flags = 0;
7298
7299 if (cap->oap->op_type != OP_NOP)
7300 {
7301#ifdef FEAT_DIFF
7302 /* "dp" is ":diffput" */
7303 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'p')
7304 {
7305 clearop(cap->oap);
Bram Moolenaar6a643652014-10-31 13:54:25 +01007306 nv_diffgetput(TRUE, cap->opcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007307 }
7308 else
7309#endif
7310 clearopbeep(cap->oap);
7311 }
Bram Moolenaarf2732452018-06-03 14:47:35 +02007312#ifdef FEAT_JOB_CHANNEL
7313 else if (bt_prompt(curbuf) && !prompt_curpos_editable())
7314 {
7315 clearopbeep(cap->oap);
7316 }
7317#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007318 else
7319 {
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02007320 if (fix_indent)
7321 {
7322 dir = (cap->cmdchar == ']' && cap->nchar == 'p')
7323 ? FORWARD : BACKWARD;
7324 flags |= PUT_FIXINDENT;
7325 }
7326 else
7327 dir = (cap->cmdchar == 'P'
7328 || (cap->cmdchar == 'g' && cap->nchar == 'P'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007329 ? BACKWARD : FORWARD;
7330 prep_redo_cmd(cap);
7331 if (cap->cmdchar == 'g')
7332 flags |= PUT_CURSEND;
7333
Bram Moolenaar071d4272004-06-13 20:20:40 +00007334 if (VIsual_active)
7335 {
7336 /* Putting in Visual mode: The put text replaces the selected
7337 * text. First delete the selected text, then put the new text.
7338 * Need to save and restore the registers that the delete
7339 * overwrites if the old contents is being put.
7340 */
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007341 was_visual = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007342 regname = cap->oap->regname;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007343#ifdef FEAT_CLIPBOARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007344 adjust_clip_reg(&regname);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007345#endif
Bram Moolenaar7d311c52014-02-22 23:49:35 +01007346 if (regname == 0 || regname == '"'
Bram Moolenaarba6e8582012-12-12 18:20:32 +01007347 || VIM_ISDIGIT(regname) || regname == '-'
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007348#ifdef FEAT_CLIPBOARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007349 || (clip_unnamed && (regname == '*' || regname == '+'))
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007350#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007351
7352 )
7353 {
Bram Moolenaarba6e8582012-12-12 18:20:32 +01007354 /* The delete is going to overwrite the register we want to
Bram Moolenaar071d4272004-06-13 20:20:40 +00007355 * put, save it first. */
7356 reg1 = get_register(regname, TRUE);
7357 }
7358
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007359 // Now delete the selected text. Avoid messages here.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007360 cap->cmdchar = 'd';
7361 cap->nchar = NUL;
7362 cap->oap->regname = NUL;
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007363 ++msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007364 nv_operator(cap);
7365 do_pending_operator(cap, 0, FALSE);
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007366 empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007367 --msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007368
7369 /* delete PUT_LINE_BACKWARD; */
7370 cap->oap->regname = regname;
7371
7372 if (reg1 != NULL)
7373 {
7374 /* Delete probably changed the register we want to put, save
7375 * it first. Then put back what was there before the delete. */
7376 reg2 = get_register(regname, FALSE);
7377 put_register(regname, reg1);
7378 }
7379
7380 /* When deleted a linewise Visual area, put the register as
7381 * lines to avoid it joined with the next line. When deletion was
7382 * characterwise, split a line when putting lines. */
7383 if (VIsual_mode == 'V')
7384 flags |= PUT_LINE;
7385 else if (VIsual_mode == 'v')
7386 flags |= PUT_LINE_SPLIT;
7387 if (VIsual_mode == Ctrl_V && dir == FORWARD)
7388 flags |= PUT_LINE_FORWARD;
7389 dir = BACKWARD;
7390 if ((VIsual_mode != 'V'
7391 && curwin->w_cursor.col < curbuf->b_op_start.col)
7392 || (VIsual_mode == 'V'
7393 && curwin->w_cursor.lnum < curbuf->b_op_start.lnum))
7394 /* cursor is at the end of the line or end of file, put
7395 * forward. */
7396 dir = FORWARD;
Bram Moolenaarec11aef2013-09-22 15:23:44 +02007397 /* May have been reset in do_put(). */
7398 VIsual_active = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007399 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007400 do_put(cap->oap->regname, dir, cap->count1, flags);
7401
Bram Moolenaar071d4272004-06-13 20:20:40 +00007402 /* If a register was saved, put it back now. */
7403 if (reg2 != NULL)
7404 put_register(regname, reg2);
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007405
7406 /* What to reselect with "gv"? Selecting the just put text seems to
7407 * be the most useful, since the original text was removed. */
7408 if (was_visual)
7409 {
Bram Moolenaara226a6d2006-02-26 23:59:20 +00007410 curbuf->b_visual.vi_start = curbuf->b_op_start;
7411 curbuf->b_visual.vi_end = curbuf->b_op_end;
Bram Moolenaard29c6fe2015-11-19 20:11:54 +01007412 /* need to adjust cursor position */
7413 if (*p_sel == 'e')
7414 inc(&curbuf->b_visual.vi_end);
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007415 }
7416
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007417 /* When all lines were selected and deleted do_put() leaves an empty
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007418 * line that needs to be deleted now. */
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007419 if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL)
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007420 {
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007421 ml_delete(curbuf->b_ml.ml_line_count, TRUE);
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007422 deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007423
7424 /* If the cursor was in that line, move it to the end of the last
7425 * line. */
7426 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
7427 {
7428 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
7429 coladvance((colnr_T)MAXCOL);
7430 }
7431 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007432 auto_format(FALSE, TRUE);
7433 }
7434}
7435
7436/*
7437 * "o" and "O" commands.
7438 */
7439 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007440nv_open(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007441{
7442#ifdef FEAT_DIFF
7443 /* "do" is ":diffget" */
7444 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'o')
7445 {
7446 clearop(cap->oap);
Bram Moolenaar6a643652014-10-31 13:54:25 +01007447 nv_diffgetput(FALSE, cap->opcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007448 }
7449 else
7450#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007451 if (VIsual_active) /* switch start and end of visual */
7452 v_swap_corners(cap->cmdchar);
Bram Moolenaarf2732452018-06-03 14:47:35 +02007453#ifdef FEAT_JOB_CHANNEL
7454 else if (bt_prompt(curbuf))
Bram Moolenaarf2732452018-06-03 14:47:35 +02007455 clearopbeep(cap->oap);
Bram Moolenaarf2732452018-06-03 14:47:35 +02007456#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007457 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007458 n_opencmd(cap);
7459}
7460
Bram Moolenaar071d4272004-06-13 20:20:40 +00007461#ifdef FEAT_NETBEANS_INTG
7462 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007463nv_nbcmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007464{
7465 netbeans_keycommand(cap->nchar);
7466}
7467#endif
7468
7469#ifdef FEAT_DND
Bram Moolenaar071d4272004-06-13 20:20:40 +00007470 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007471nv_drop(cmdarg_T *cap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007472{
7473 do_put('~', BACKWARD, 1L, PUT_CURSEND);
7474}
7475#endif
Bram Moolenaar3918c952005-03-15 22:34:55 +00007476
Bram Moolenaar3918c952005-03-15 22:34:55 +00007477/*
7478 * Trigger CursorHold event.
7479 * When waiting for a character for 'updatetime' K_CURSORHOLD is put in the
7480 * input buffer. "did_cursorhold" is set to avoid retriggering.
7481 */
Bram Moolenaar3918c952005-03-15 22:34:55 +00007482 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007483nv_cursorhold(cmdarg_T *cap)
Bram Moolenaar3918c952005-03-15 22:34:55 +00007484{
7485 apply_autocmds(EVENT_CURSORHOLD, NULL, NULL, FALSE, curbuf);
7486 did_cursorhold = TRUE;
Bram Moolenaarfc735152005-03-22 22:54:12 +00007487 cap->retval |= CA_COMMAND_BUSY; /* don't call edit() now */
Bram Moolenaar3918c952005-03-15 22:34:55 +00007488}