blob: 4a86e7560f04acf6182d808ae474033fd42b4e21 [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
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100140// 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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100146#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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152
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{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100170 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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174} 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
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100304 // pound sign
Bram Moolenaar071d4272004-06-13 20:20:40 +0000305 {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
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100380// Number of commands in nv_cmds[].
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381#define NV_CMDS_SIZE (sizeof(nv_cmds) / sizeof(struct nv_cmd))
382
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100383// Sorted index of commands in nv_cmds[].
Bram Moolenaar071d4272004-06-13 20:20:40 +0000384static short nv_cmd_idx[NV_CMDS_SIZE];
385
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100386// The highest index for which
387// nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]
Bram Moolenaar071d4272004-06-13 20:20:40 +0000388static 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
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100399 // The commands are sorted on absolute value.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400 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
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100417 // 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
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100421 // Sort the commands by the command character.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422 qsort((void *)&nv_cmd_idx, (size_t)NV_CMDS_SIZE, sizeof(short), nv_compare);
423
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100424 // 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 Moolenaar6e0ce172019-12-05 20:12:41 +0100443 // A multi-byte character is never a command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000444 if (cmdchar >= 0x100)
445 return -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000446
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100447 // We use the absolute value of the character. Special keys have a
448 // negative value, but are sorted on their absolute value.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 if (cmdchar < 0)
450 cmdchar = -cmdchar;
451
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100452 // If the character is in the first part: The character is the index into
453 // nv_cmd_idx[].
Bram Moolenaar071d4272004-06-13 20:20:40 +0000454 if (cmdchar <= nv_max_linear)
455 return nv_cmd_idx[cmdchar];
456
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100457 // Perform a binary search.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000458 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,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100486 int toplevel UNUSED) // TRUE when called from main()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100488 cmdarg_T ca; // command arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489 int c;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100490 int ctrl_w = FALSE; // got CTRL-W command
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 int old_col = curwin->w_curswant;
492#ifdef FEAT_CMDL_INFO
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100493 int need_flushbuf; // need to call out_flush()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100495 pos_T old_pos; // cursor position before command
Bram Moolenaar071d4272004-06-13 20:20:40 +0000496 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 Moolenaarb146e012020-07-19 23:06:05 +0200502 int save_did_cursorhold = did_cursorhold;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503
Bram Moolenaara80faa82020-04-12 19:37:17 +0200504 CLEAR_FIELD(ca); // also resets ca.retval
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505 ca.oap = oap;
Bram Moolenaara983fe92008-07-31 20:04:27 +0000506
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100507 // Use a count remembered from before entering an operator. After typing
508 // "3d" we return from normal_cmd() and come back here, the "3" is
509 // remembered in "opcount".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510 ca.opcount = opcount;
511
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 /*
513 * If there is an operator pending, then the command we take this time
514 * will terminate it. Finish_op tells us to finish the operation before
515 * returning this time (unless the operation was cancelled).
516 */
517#ifdef CURSOR_SHAPE
518 c = finish_op;
519#endif
520 finish_op = (oap->op_type != OP_NOP);
521#ifdef CURSOR_SHAPE
522 if (finish_op != c)
523 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100524 ui_cursor_shape(); // may show different cursor shape
Bram Moolenaar071d4272004-06-13 20:20:40 +0000525# ifdef FEAT_MOUSESHAPE
526 update_mouseshape(-1);
527# endif
528 }
529#endif
530
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100531 // When not finishing an operator and no register name typed, reset the
532 // count.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 if (!finish_op && !oap->regname)
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000534 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000535 ca.opcount = 0;
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000536#ifdef FEAT_EVAL
537 set_prevcount = TRUE;
538#endif
539 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000540
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100541 // Restore counts from before receiving K_CURSORHOLD. This means after
542 // typing "3", handling K_CURSORHOLD and then typing "2" we get "32", not
543 // "3 * 2".
Bram Moolenaara983fe92008-07-31 20:04:27 +0000544 if (oap->prev_opcount > 0 || oap->prev_count0 > 0)
545 {
546 ca.opcount = oap->prev_opcount;
547 ca.count0 = oap->prev_count0;
548 oap->prev_opcount = 0;
549 oap->prev_count0 = 0;
550 }
Bram Moolenaara983fe92008-07-31 20:04:27 +0000551
Bram Moolenaar071d4272004-06-13 20:20:40 +0000552 mapped_len = typebuf_maplen();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000553
554 State = NORMAL_BUSY;
555#ifdef USE_ON_FLY_SCROLL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100556 dont_scroll = FALSE; // allow scrolling here
Bram Moolenaar071d4272004-06-13 20:20:40 +0000557#endif
558
Bram Moolenaarf82a2d22010-12-17 18:53:01 +0100559#ifdef FEAT_EVAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100560 // Set v:count here, when called from main() and not a stuffed
561 // command, so that v:count can be used in an expression mapping
562 // when there is no count. Do set it for redo.
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100563 if (toplevel && readbuf1_empty())
Bram Moolenaarf82a2d22010-12-17 18:53:01 +0100564 set_vcount_ca(&ca, &set_prevcount);
565#endif
566
Bram Moolenaar071d4272004-06-13 20:20:40 +0000567 /*
568 * Get the command character from the user.
569 */
570 c = safe_vgetc();
Bram Moolenaar25281632016-01-21 23:32:32 +0100571 LANGMAP_ADJUST(c, get_real_state() != SELECTMODE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572
573 /*
574 * If a mapping was started in Visual or Select mode, remember the length
575 * of the mapping. This is used below to not return to Insert mode for as
576 * long as the mapping is being executed.
577 */
578 if (restart_edit == 0)
579 old_mapped_len = 0;
580 else if (old_mapped_len
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000581 || (VIsual_active && mapped_len == 0 && typebuf_maplen() > 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000582 old_mapped_len = typebuf_maplen();
583
584 if (c == NUL)
585 c = K_ZERO;
586
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587 /*
588 * In Select mode, typed text replaces the selection.
589 */
590 if (VIsual_active
591 && VIsual_select
592 && (vim_isprintc(c) || c == NL || c == CAR || c == K_KENTER))
593 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100594 // Fake a "c"hange command. When "restart_edit" is set (e.g., because
595 // 'insertmode' is set) fake a "d"elete command, Insert mode will
596 // restart automatically.
597 // Insert the typed character in the typeahead buffer, so that it can
598 // be mapped in Insert mode. Required for ":lmap" to work.
Bram Moolenaarb42c0d52020-05-29 22:41:41 +0200599 ins_char_typebuf(vgetc_char, vgetc_mod_mask);
Bram Moolenaar686f51e2005-05-20 21:19:57 +0000600 if (restart_edit != 0)
601 c = 'd';
602 else
603 c = 'c';
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100604 msg_nowait = TRUE; // don't delay going to insert mode
605 old_mapped_len = 0; // do go to Insert mode
Bram Moolenaar071d4272004-06-13 20:20:40 +0000606 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607
608#ifdef FEAT_CMDL_INFO
609 need_flushbuf = add_to_showcmd(c);
610#endif
611
612getcount:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613 if (!(VIsual_active && VIsual_select))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614 {
615 /*
616 * Handle a count before a command and compute ca.count0.
617 * Note that '0' is a command and not the start of a count, but it's
618 * part of a count after other digits.
619 */
620 while ( (c >= '1' && c <= '9')
621 || (ca.count0 != 0 && (c == K_DEL || c == K_KDEL || c == '0')))
622 {
623 if (c == K_DEL || c == K_KDEL)
624 {
625 ca.count0 /= 10;
626#ifdef FEAT_CMDL_INFO
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100627 del_from_showcmd(4); // delete the digit and ~@%
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628#endif
629 }
630 else
631 ca.count0 = ca.count0 * 10 + (c - '0');
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100632 if (ca.count0 < 0) // got too large!
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 ca.count0 = 999999999L;
Bram Moolenaarf13249a2007-10-14 15:16:27 +0000634#ifdef FEAT_EVAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100635 // Set v:count here, when called from main() and not a stuffed
636 // command, so that v:count can be used in an expression mapping
637 // right after the count. Do set it for redo.
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100638 if (toplevel && readbuf1_empty())
Bram Moolenaarf82a2d22010-12-17 18:53:01 +0100639 set_vcount_ca(&ca, &set_prevcount);
Bram Moolenaarf13249a2007-10-14 15:16:27 +0000640#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 if (ctrl_w)
642 {
643 ++no_mapping;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100644 ++allow_keys; // no mapping for nchar, but keys
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100646 ++no_zero_mapping; // don't map zero here
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000647 c = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 LANGMAP_ADJUST(c, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649 --no_zero_mapping;
650 if (ctrl_w)
651 {
652 --no_mapping;
653 --allow_keys;
654 }
655#ifdef FEAT_CMDL_INFO
656 need_flushbuf |= add_to_showcmd(c);
657#endif
658 }
659
660 /*
661 * If we got CTRL-W there may be a/another count
662 */
663 if (c == Ctrl_W && !ctrl_w && oap->op_type == OP_NOP)
664 {
665 ctrl_w = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100666 ca.opcount = ca.count0; // remember first count
Bram Moolenaar071d4272004-06-13 20:20:40 +0000667 ca.count0 = 0;
668 ++no_mapping;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100669 ++allow_keys; // no mapping for nchar, but keys
670 c = plain_vgetc(); // get next character
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 LANGMAP_ADJUST(c, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672 --no_mapping;
673 --allow_keys;
674#ifdef FEAT_CMDL_INFO
675 need_flushbuf |= add_to_showcmd(c);
676#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100677 goto getcount; // jump back
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678 }
679 }
680
Bram Moolenaara983fe92008-07-31 20:04:27 +0000681 if (c == K_CURSORHOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000682 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100683 // Save the count values so that ca.opcount and ca.count0 are exactly
684 // the same when coming back here after handling K_CURSORHOLD.
Bram Moolenaara983fe92008-07-31 20:04:27 +0000685 oap->prev_opcount = ca.opcount;
686 oap->prev_count0 = ca.count0;
687 }
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100688 else if (ca.opcount != 0)
Bram Moolenaara983fe92008-07-31 20:04:27 +0000689 {
690 /*
691 * If we're in the middle of an operator (including after entering a
692 * yank buffer with '"') AND we had a count before the operator, then
693 * that count overrides the current value of ca.count0.
694 * What this means effectively, is that commands like "3dw" get turned
695 * into "d3w" which makes things fall into place pretty neatly.
696 * If you give a count before AND after the operator, they are
697 * multiplied.
698 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000699 if (ca.count0)
700 ca.count0 *= ca.opcount;
701 else
702 ca.count0 = ca.opcount;
703 }
704
705 /*
706 * Always remember the count. It will be set to zero (on the next call,
707 * above) when there is no pending operator.
708 * When called from main(), save the count for use by the "count" built-in
709 * variable.
710 */
711 ca.opcount = ca.count0;
712 ca.count1 = (ca.count0 == 0 ? 1 : ca.count0);
713
714#ifdef FEAT_EVAL
715 /*
716 * Only set v:count when called from main() and not a stuffed command.
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100717 * Do set it for redo.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718 */
Bram Moolenaar0a36fec2014-02-11 15:10:43 +0100719 if (toplevel && readbuf1_empty())
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000720 set_vcount(ca.count0, ca.count1, set_prevcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721#endif
722
723 /*
724 * Find the command character in the table of commands.
725 * For CTRL-W we already got nchar when looking for a count.
726 */
727 if (ctrl_w)
728 {
729 ca.nchar = c;
730 ca.cmdchar = Ctrl_W;
731 }
732 else
733 ca.cmdchar = c;
734 idx = find_command(ca.cmdchar);
735 if (idx < 0)
736 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100737 // Not a known command: beep.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000738 clearopbeep(oap);
739 goto normal_end;
740 }
Bram Moolenaar05a7bb32006-01-19 22:09:32 +0000741
Bram Moolenaar2d3f4892006-01-20 23:02:51 +0000742 if (text_locked() && (nv_cmds[idx].cmd_flags & NV_NCW))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000743 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100744 // This command is not allowed while editing a cmdline: beep.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000745 clearopbeep(oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +0000746 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000747 goto normal_end;
748 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000749 if ((nv_cmds[idx].cmd_flags & NV_NCW) && curbuf_locked())
750 goto normal_end;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000751
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752 /*
753 * In Visual/Select mode, a few keys are handled in a special way.
754 */
755 if (VIsual_active)
756 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100757 // when 'keymodel' contains "stopsel" may stop Select/Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +0000758 if (km_stopsel
759 && (nv_cmds[idx].cmd_flags & NV_STS)
760 && !(mod_mask & MOD_MASK_SHIFT))
761 {
762 end_visual_mode();
763 redraw_curbuf_later(INVERTED);
764 }
765
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100766 // Keys that work different when 'keymodel' contains "startsel"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 if (km_startsel)
768 {
769 if (nv_cmds[idx].cmd_flags & NV_SS)
770 {
771 unshift_special(&ca);
772 idx = find_command(ca.cmdchar);
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000773 if (idx < 0)
774 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100775 // Just in case
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000776 clearopbeep(oap);
777 goto normal_end;
778 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 }
780 else if ((nv_cmds[idx].cmd_flags & NV_SSS)
781 && (mod_mask & MOD_MASK_SHIFT))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 mod_mask &= ~MOD_MASK_SHIFT;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000783 }
784 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785
786#ifdef FEAT_RIGHTLEFT
787 if (curwin->w_p_rl && KeyTyped && !KeyStuffed
788 && (nv_cmds[idx].cmd_flags & NV_RL))
789 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100790 // Invert horizontal movements and operations. Only when typed by the
791 // user directly, not when the result of a mapping or "x" translated
792 // to "dl".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000793 switch (ca.cmdchar)
794 {
795 case 'l': ca.cmdchar = 'h'; break;
796 case K_RIGHT: ca.cmdchar = K_LEFT; break;
797 case K_S_RIGHT: ca.cmdchar = K_S_LEFT; break;
798 case K_C_RIGHT: ca.cmdchar = K_C_LEFT; break;
799 case 'h': ca.cmdchar = 'l'; break;
800 case K_LEFT: ca.cmdchar = K_RIGHT; break;
801 case K_S_LEFT: ca.cmdchar = K_S_RIGHT; break;
802 case K_C_LEFT: ca.cmdchar = K_C_RIGHT; break;
803 case '>': ca.cmdchar = '<'; break;
804 case '<': ca.cmdchar = '>'; break;
805 }
806 idx = find_command(ca.cmdchar);
807 }
808#endif
809
810 /*
811 * Get an additional character if we need one.
812 */
813 if ((nv_cmds[idx].cmd_flags & NV_NCH)
814 && (((nv_cmds[idx].cmd_flags & NV_NCH_NOP) == NV_NCH_NOP
815 && oap->op_type == OP_NOP)
816 || (nv_cmds[idx].cmd_flags & NV_NCH_ALW) == NV_NCH_ALW
817 || (ca.cmdchar == 'q'
818 && oap->op_type == OP_NOP
Bram Moolenaar0b6d9112018-05-22 20:35:17 +0200819 && reg_recording == 0
820 && reg_executing == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000821 || ((ca.cmdchar == 'a' || ca.cmdchar == 'i')
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +0100822 && (oap->op_type != OP_NOP || VIsual_active))))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000823 {
824 int *cp;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100825 int repl = FALSE; // get character for replace mode
826 int lit = FALSE; // get extra character literally
827 int langmap_active = FALSE; // using :lmap mappings
828 int lang; // getting a text character
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100829#ifdef HAVE_INPUT_METHOD
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100830 int save_smd; // saved value of p_smd
Bram Moolenaar071d4272004-06-13 20:20:40 +0000831#endif
832
833 ++no_mapping;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100834 ++allow_keys; // no mapping for nchar, but allow key codes
835 // Don't generate a CursorHold event here, most commands can't handle
836 // it, e.g., nv_replace(), nv_csearch().
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +0000837 did_cursorhold = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000838 if (ca.cmdchar == 'g')
839 {
840 /*
841 * For 'g' get the next character now, so that we can check for
842 * "gr", "g'" and "g`".
843 */
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000844 ca.nchar = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000845 LANGMAP_ADJUST(ca.nchar, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000846#ifdef FEAT_CMDL_INFO
847 need_flushbuf |= add_to_showcmd(ca.nchar);
848#endif
849 if (ca.nchar == 'r' || ca.nchar == '\'' || ca.nchar == '`'
Bram Moolenaarba2d44f2013-11-28 19:27:30 +0100850 || ca.nchar == Ctrl_BSL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000851 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100852 cp = &ca.extra_char; // need to get a third character
Bram Moolenaar071d4272004-06-13 20:20:40 +0000853 if (ca.nchar != 'r')
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100854 lit = TRUE; // get it literally
Bram Moolenaar071d4272004-06-13 20:20:40 +0000855 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100856 repl = TRUE; // get it in replace mode
Bram Moolenaar071d4272004-06-13 20:20:40 +0000857 }
858 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100859 cp = NULL; // no third character needed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000860 }
861 else
862 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100863 if (ca.cmdchar == 'r') // get it in replace mode
Bram Moolenaar071d4272004-06-13 20:20:40 +0000864 repl = TRUE;
865 cp = &ca.nchar;
866 }
867 lang = (repl || (nv_cmds[idx].cmd_flags & NV_LANG));
868
869 /*
870 * Get a second or third character.
871 */
872 if (cp != NULL)
873 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874 if (repl)
875 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100876 State = REPLACE; // pretend Replace mode
Bram Moolenaar7a641ca2019-10-31 19:55:55 +0100877#ifdef CURSOR_SHAPE
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100878 ui_cursor_shape(); // show different cursor shape
Bram Moolenaar071d4272004-06-13 20:20:40 +0000879#endif
Bram Moolenaar7a641ca2019-10-31 19:55:55 +0100880 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000881 if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP)
882 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100883 // Allow mappings defined with ":lmap".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884 --no_mapping;
885 --allow_keys;
886 if (repl)
887 State = LREPLACE;
888 else
889 State = LANGMAP;
890 langmap_active = TRUE;
891 }
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100892#ifdef HAVE_INPUT_METHOD
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893 save_smd = p_smd;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100894 p_smd = FALSE; // Don't let the IM code show the mode here
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895 if (lang && curbuf->b_p_iminsert == B_IMODE_IM)
896 im_set_active(TRUE);
897#endif
898
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000899 *cp = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900
901 if (langmap_active)
902 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100903 // Undo the decrement done above
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 ++no_mapping;
905 ++allow_keys;
906 State = NORMAL_BUSY;
907 }
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100908#ifdef HAVE_INPUT_METHOD
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909 if (lang)
910 {
911 if (curbuf->b_p_iminsert != B_IMODE_LMAP)
912 im_save_status(&curbuf->b_p_iminsert);
913 im_set_active(FALSE);
914 }
915 p_smd = save_smd;
916#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000917 State = NORMAL_BUSY;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918#ifdef FEAT_CMDL_INFO
919 need_flushbuf |= add_to_showcmd(*cp);
920#endif
921
922 if (!lit)
923 {
924#ifdef FEAT_DIGRAPHS
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100925 // Typing CTRL-K gets a digraph.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000926 if (*cp == Ctrl_K
927 && ((nv_cmds[idx].cmd_flags & NV_LANG)
928 || cp == &ca.extra_char)
929 && vim_strchr(p_cpo, CPO_DIGRAPH) == NULL)
930 {
931 c = get_digraph(FALSE);
932 if (c > 0)
933 {
934 *cp = c;
935# ifdef FEAT_CMDL_INFO
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100936 // Guessing how to update showcmd here...
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 del_from_showcmd(3);
938 need_flushbuf |= add_to_showcmd(*cp);
939# endif
940 }
941 }
942#endif
943
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100944 // adjust chars > 127, except after "tTfFr" commands
Bram Moolenaar071d4272004-06-13 20:20:40 +0000945 LANGMAP_ADJUST(*cp, !lang);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946#ifdef FEAT_RIGHTLEFT
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100947 // adjust Hebrew mapped char
Bram Moolenaar071d4272004-06-13 20:20:40 +0000948 if (p_hkmap && lang && KeyTyped)
949 *cp = hkmap(*cp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000950#endif
951 }
952
953 /*
954 * When the next character is CTRL-\ a following CTRL-N means the
955 * command is aborted and we go to Normal mode.
956 */
957 if (cp == &ca.extra_char
958 && ca.nchar == Ctrl_BSL
959 && (ca.extra_char == Ctrl_N || ca.extra_char == Ctrl_G))
960 {
961 ca.cmdchar = Ctrl_BSL;
962 ca.nchar = ca.extra_char;
963 idx = find_command(ca.cmdchar);
964 }
Bram Moolenaar12a753a2012-10-23 05:08:53 +0200965 else if ((ca.nchar == 'n' || ca.nchar == 'N') && ca.cmdchar == 'g')
Bram Moolenaarf00dc262012-10-21 03:54:33 +0200966 ca.oap->op_type = get_op_type(*cp, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967 else if (*cp == Ctrl_BSL)
968 {
969 long towait = (p_ttm >= 0 ? p_ttm : p_tm);
970
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100971 // There is a busy wait here when typing "f<C-\>" and then
972 // something different from CTRL-N. Can't be avoided.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973 while ((c = vpeekc()) <= 0 && towait > 0L)
974 {
975 do_sleep(towait > 50L ? 50L : towait);
976 towait -= 50L;
977 }
978 if (c > 0)
979 {
Bram Moolenaar61abfd12007-09-13 16:26:47 +0000980 c = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000981 if (c != Ctrl_N && c != Ctrl_G)
982 vungetc(c);
983 else
984 {
985 ca.cmdchar = Ctrl_BSL;
986 ca.nchar = c;
987 idx = find_command(ca.cmdchar);
988 }
989 }
990 }
991
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100992 // When getting a text character and the next character is a
993 // multi-byte character, it could be a composing character.
994 // However, don't wait for it to arrive. Also, do enable mapping,
995 // because if it's put back with vungetc() it's too late to apply
996 // mapping.
Bram Moolenaar4f880622014-07-23 12:31:20 +0200997 --no_mapping;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000998 while (enc_utf8 && lang && (c = vpeekc()) > 0
999 && (c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1))
1000 {
Bram Moolenaar61abfd12007-09-13 16:26:47 +00001001 c = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002 if (!utf_iscomposing(c))
1003 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001004 vungetc(c); // it wasn't, put it back
Bram Moolenaar071d4272004-06-13 20:20:40 +00001005 break;
1006 }
1007 else if (ca.ncharC1 == 0)
1008 ca.ncharC1 = c;
1009 else
1010 ca.ncharC2 = c;
1011 }
Bram Moolenaar4f880622014-07-23 12:31:20 +02001012 ++no_mapping;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001013 }
1014 --no_mapping;
1015 --allow_keys;
1016 }
1017
1018#ifdef FEAT_CMDL_INFO
1019 /*
1020 * Flush the showcmd characters onto the screen so we can see them while
1021 * the command is being executed. Only do this when the shown command was
1022 * actually displayed, otherwise this will slow down a lot when executing
1023 * mappings.
1024 */
1025 if (need_flushbuf)
1026 out_flush();
1027#endif
Bram Moolenaard9205ca2008-10-02 20:55:54 +00001028 if (ca.cmdchar != K_IGNORE)
Bram Moolenaarb146e012020-07-19 23:06:05 +02001029 did_cursorhold = save_did_cursorhold;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001030
1031 State = NORMAL;
1032
1033 if (ca.nchar == ESC)
1034 {
1035 clearop(oap);
1036 if (restart_edit == 0 && goto_im())
1037 restart_edit = 'a';
1038 goto normal_end;
1039 }
1040
Bram Moolenaarc0197e22004-09-13 20:26:32 +00001041 if (ca.cmdchar != K_IGNORE)
1042 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001043 msg_didout = FALSE; // don't scroll screen up for normal command
Bram Moolenaarc0197e22004-09-13 20:26:32 +00001044 msg_col = 0;
1045 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001047 old_pos = curwin->w_cursor; // remember where cursor was
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001049 // When 'keymodel' contains "startsel" some keys start Select/Visual
1050 // mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001051 if (!VIsual_active && km_startsel)
1052 {
1053 if (nv_cmds[idx].cmd_flags & NV_SS)
1054 {
1055 start_selection();
1056 unshift_special(&ca);
1057 idx = find_command(ca.cmdchar);
1058 }
1059 else if ((nv_cmds[idx].cmd_flags & NV_SSS)
1060 && (mod_mask & MOD_MASK_SHIFT))
1061 {
1062 start_selection();
1063 mod_mask &= ~MOD_MASK_SHIFT;
1064 }
1065 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066
1067 /*
1068 * Execute the command!
1069 * Call the command function found in the commands table.
1070 */
1071 ca.arg = nv_cmds[idx].cmd_arg;
1072 (nv_cmds[idx].cmd_func)(&ca);
1073
1074 /*
1075 * If we didn't start or finish an operator, reset oap->regname, unless we
1076 * need it later.
1077 */
1078 if (!finish_op
1079 && !oap->op_type
1080 && (idx < 0 || !(nv_cmds[idx].cmd_flags & NV_KEEPREG)))
1081 {
1082 clearop(oap);
1083#ifdef FEAT_EVAL
Bram Moolenaar439c0362020-06-06 15:58:03 +02001084 reset_reg_var();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085#endif
1086 }
1087
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001088 // Get the length of mapped chars again after typing a count, second
1089 // character or "z333<cr>".
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001090 if (old_mapped_len > 0)
1091 old_mapped_len = typebuf_maplen();
1092
Bram Moolenaar071d4272004-06-13 20:20:40 +00001093 /*
Bram Moolenaarfa5612c2019-12-01 19:37:07 +01001094 * If an operation is pending, handle it. But not for K_IGNORE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001095 */
Bram Moolenaarfa5612c2019-12-01 19:37:07 +01001096 if (ca.cmdchar != K_IGNORE)
1097 do_pending_operator(&ca, old_col, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001098
1099 /*
1100 * Wait for a moment when a message is displayed that will be overwritten
1101 * by the mode message.
1102 * In Visual mode and with "^O" in Insert mode, a short message will be
1103 * overwritten by the mode message. Wait a bit, until a key is hit.
1104 * In Visual mode, it's more important to keep the Visual area updated
1105 * than keeping a message (e.g. from a /pat search).
1106 * Only do this if the command was typed, not from a mapping.
1107 * Don't wait when emsg_silent is non-zero.
1108 * Also wait a bit after an error message, e.g. for "^O:".
1109 * Don't redraw the screen, it would remove the message.
1110 */
1111 if ( ((p_smd
Bram Moolenaar09df3122006-01-23 22:23:09 +00001112 && msg_silent == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 && (restart_edit != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 || (VIsual_active
1115 && old_pos.lnum == curwin->w_cursor.lnum
1116 && old_pos.col == curwin->w_cursor.col)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001117 )
1118 && (clear_cmdline
1119 || redraw_cmdline)
1120 && (msg_didout || (msg_didany && msg_scroll))
1121 && !msg_nowait
1122 && KeyTyped)
1123 || (restart_edit != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 && !VIsual_active
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125 && (msg_scroll
1126 || emsg_on_display)))
1127 && oap->regname == 0
1128 && !(ca.retval & CA_COMMAND_BUSY)
1129 && stuff_empty()
1130 && typebuf_typed()
1131 && emsg_silent == 0
1132 && !did_wait_return
1133 && oap->op_type == OP_NOP)
1134 {
1135 int save_State = State;
1136
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001137 // Draw the cursor with the right shape here
Bram Moolenaar071d4272004-06-13 20:20:40 +00001138 if (restart_edit != 0)
1139 State = INSERT;
1140
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001141 // If need to redraw, and there is a "keep_msg", redraw before the
1142 // delay
Bram Moolenaar071d4272004-06-13 20:20:40 +00001143 if (must_redraw && keep_msg != NULL && !emsg_on_display)
1144 {
1145 char_u *kmsg;
1146
1147 kmsg = keep_msg;
1148 keep_msg = NULL;
Bram Moolenaar5715b312020-03-16 22:08:45 +01001149 // Showmode() will clear keep_msg, but we want to use it anyway.
1150 // First update w_topline.
1151 setcursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152 update_screen(0);
Bram Moolenaare5fbd732019-09-09 20:04:13 +02001153 // now reset it, otherwise it's put in the history again
Bram Moolenaar071d4272004-06-13 20:20:40 +00001154 keep_msg = kmsg;
Bram Moolenaare5fbd732019-09-09 20:04:13 +02001155
1156 kmsg = vim_strsave(keep_msg);
1157 if (kmsg != NULL)
1158 {
1159 msg_attr((char *)kmsg, keep_msg_attr);
1160 vim_free(kmsg);
1161 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 }
1163 setcursor();
Bram Moolenaar5715b312020-03-16 22:08:45 +01001164#ifdef CURSOR_SHAPE
1165 ui_cursor_shape(); // may show different cursor shape
1166#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167 cursor_on();
1168 out_flush();
1169 if (msg_scroll || emsg_on_display)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001170 ui_delay(1003L, TRUE); // wait at least one second
1171 ui_delay(3003L, FALSE); // wait up to three seconds
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 State = save_State;
1173
1174 msg_scroll = FALSE;
1175 emsg_on_display = FALSE;
1176 }
1177
1178 /*
1179 * Finish up after executing a Normal mode command.
1180 */
1181normal_end:
1182
1183 msg_nowait = FALSE;
1184
Bram Moolenaarcc613032020-06-07 21:31:18 +02001185#ifdef FEAT_EVAL
1186 if (finish_op)
1187 reset_reg_var();
1188#endif
1189
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001190 // Reset finish_op, in case it was set
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191#ifdef CURSOR_SHAPE
1192 c = finish_op;
1193#endif
1194 finish_op = FALSE;
1195#ifdef CURSOR_SHAPE
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001196 // Redraw the cursor with another shape, if we were in Operator-pending
1197 // mode or did a replace command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 if (c || ca.cmdchar == 'r')
1199 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001200 ui_cursor_shape(); // may show different cursor shape
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201# ifdef FEAT_MOUSESHAPE
1202 update_mouseshape(-1);
1203# endif
1204 }
1205#endif
1206
1207#ifdef FEAT_CMDL_INFO
Bram Moolenaara983fe92008-07-31 20:04:27 +00001208 if (oap->op_type == OP_NOP && oap->regname == 0
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001209 && ca.cmdchar != K_CURSORHOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210 clear_showcmd();
1211#endif
1212
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001213 checkpcmark(); // check if we moved since setting pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001214 vim_free(ca.searchbuf);
1215
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216 if (has_mbyte)
1217 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001218
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219 if (curwin->w_p_scb && toplevel)
1220 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001221 validate_cursor(); // may need to update w_leftcol
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222 do_check_scrollbind(TRUE);
1223 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001224
Bram Moolenaar860cae12010-06-05 23:22:07 +02001225 if (curwin->w_p_crb && toplevel)
1226 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001227 validate_cursor(); // may need to update w_leftcol
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228 do_check_cursorbind();
1229 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001230
Bram Moolenaar6fe15bb2017-08-16 21:09:18 +02001231#ifdef FEAT_TERMINAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001232 // don't go to Insert mode if a terminal has a running job
Bram Moolenaareef9add2017-09-16 15:38:04 +02001233 if (term_job_running(curbuf->b_term))
Bram Moolenaar6fe15bb2017-08-16 21:09:18 +02001234 restart_edit = 0;
1235#endif
1236
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 /*
1238 * May restart edit(), if we got here with CTRL-O in Insert mode (but not
1239 * if still inside a mapping that started in Visual mode).
1240 * May switch from Visual to Select mode after CTRL-O command.
1241 */
1242 if ( oap->op_type == OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243 && ((restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
1244 || restart_VIsual_select == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 && !(ca.retval & CA_COMMAND_BUSY)
1246 && stuff_empty()
1247 && oap->regname == 0)
1248 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 if (restart_VIsual_select == 1)
1250 {
1251 VIsual_select = TRUE;
1252 showmode();
1253 restart_VIsual_select = 0;
1254 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001255 if (restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 (void)edit(restart_edit, FALSE, 1L);
1257 }
1258
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 if (restart_VIsual_select == 2)
1260 restart_VIsual_select = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001262 // Save count before an operator for next time.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001263 opcount = ca.opcount;
1264}
1265
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001266#ifdef FEAT_EVAL
1267/*
1268 * Set v:count and v:count1 according to "cap".
1269 * Set v:prevcount only when "set_prevcount" is TRUE.
1270 */
1271 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001272set_vcount_ca(cmdarg_T *cap, int *set_prevcount)
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001273{
1274 long count = cap->count0;
1275
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001276 // multiply with cap->opcount the same way as above
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001277 if (cap->opcount != 0)
1278 count = cap->opcount * (count == 0 ? 1 : count);
1279 set_vcount(count, count == 0 ? 1 : count, *set_prevcount);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001280 *set_prevcount = FALSE; // only set v:prevcount once
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001281}
1282#endif
1283
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284/*
Bram Moolenaar5715b312020-03-16 22:08:45 +01001285 * Check if highlighting for Visual mode is possible, give a warning message
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286 * if not.
1287 */
1288 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001289check_visual_highlight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290{
1291 static int did_check = FALSE;
1292
1293 if (full_screen)
1294 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01001295 if (!did_check && HL_ATTR(HLF_V) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001296 msg(_("Warning: terminal cannot highlight"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 did_check = TRUE;
1298 }
1299}
1300
1301/*
Bram Moolenaar66fa2712006-01-22 23:22:22 +00001302 * End Visual mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 * This function should ALWAYS be called to end Visual mode, except from
1304 * do_pending_operator().
1305 */
1306 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001307end_visual_mode(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308{
1309#ifdef FEAT_CLIPBOARD
1310 /*
1311 * If we are using the clipboard, then remember what was selected in case
1312 * we need to paste it somewhere while we still own the selection.
1313 * Only do this when the clipboard is already owned. Don't want to grab
1314 * the selection when hitting ESC.
1315 */
1316 if (clip_star.available && clip_star.owned)
1317 clip_auto_select();
1318#endif
1319
1320 VIsual_active = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 setmouse();
1322 mouse_dragging = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001324 // Save the current VIsual area for '< and '> marks, and "gv"
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001325 curbuf->b_visual.vi_mode = VIsual_mode;
1326 curbuf->b_visual.vi_start = VIsual;
1327 curbuf->b_visual.vi_end = curwin->w_cursor;
1328 curbuf->b_visual.vi_curswant = curwin->w_curswant;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329#ifdef FEAT_EVAL
1330 curbuf->b_visual_mode_eval = VIsual_mode;
1331#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 if (!virtual_active())
1333 curwin->w_cursor.coladd = 0;
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001334 may_clear_cmdline();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001335
Bram Moolenaarf193fff2006-04-27 00:02:13 +00001336 adjust_cursor_eol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001337}
1338
1339/*
1340 * Reset VIsual_active and VIsual_reselect.
1341 */
1342 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001343reset_VIsual_and_resel(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344{
1345 if (VIsual_active)
1346 {
1347 end_visual_mode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001348 redraw_curbuf_later(INVERTED); // delete the inversion later
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349 }
1350 VIsual_reselect = FALSE;
1351}
1352
1353/*
1354 * Reset VIsual_active and VIsual_reselect if it's set.
1355 */
1356 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001357reset_VIsual(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001358{
1359 if (VIsual_active)
1360 {
1361 end_visual_mode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001362 redraw_curbuf_later(INVERTED); // delete the inversion later
Bram Moolenaar071d4272004-06-13 20:20:40 +00001363 VIsual_reselect = FALSE;
1364 }
1365}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02001367 void
1368restore_visual_mode(void)
1369{
1370 if (VIsual_mode_orig != NUL)
1371 {
1372 curbuf->b_visual.vi_mode = VIsual_mode_orig;
1373 VIsual_mode_orig = NUL;
1374 }
1375}
1376
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377/*
1378 * Check for a balloon-eval special item to include when searching for an
1379 * identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
1380 * Returns TRUE if the character at "*ptr" should be included.
1381 * "dir" is FORWARD or BACKWARD, the direction of searching.
1382 * "*colp" is in/decremented if "ptr[-dir]" should also be included.
1383 * "bnp" points to a counter for square brackets.
1384 */
1385 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001386find_is_eval_item(
1387 char_u *ptr,
1388 int *colp,
1389 int *bnp,
1390 int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001392 // Accept everything inside [].
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD))
1394 ++*bnp;
1395 if (*bnp > 0)
1396 {
1397 if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD))
1398 --*bnp;
1399 return TRUE;
1400 }
1401
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001402 // skip over "s.var"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 if (*ptr == '.')
1404 return TRUE;
1405
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001406 // two-character item: s->var
Bram Moolenaar071d4272004-06-13 20:20:40 +00001407 if (ptr[dir == BACKWARD ? 0 : 1] == '>'
1408 && ptr[dir == BACKWARD ? -1 : 0] == '-')
1409 {
1410 *colp += dir;
1411 return TRUE;
1412 }
1413 return FALSE;
1414}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001415
1416/*
1417 * Find the identifier under or to the right of the cursor.
1418 * "find_type" can have one of three values:
1419 * FIND_IDENT: find an identifier (keyword)
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001420 * FIND_STRING: find any non-white text
1421 * FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
Bram Moolenaar52b4b552005-03-07 23:00:57 +00001422 * FIND_EVAL: find text useful for C program debugging
Bram Moolenaar071d4272004-06-13 20:20:40 +00001423 *
1424 * There are three steps:
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001425 * 1. Search forward for the start of an identifier/text. Doesn't move if
Bram Moolenaar071d4272004-06-13 20:20:40 +00001426 * already on one.
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001427 * 2. Search backward for the start of this identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 * This doesn't match the real Vi but I like it a little better and it
1429 * shouldn't bother anyone.
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001430 * 3. Search forward to the end of this identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431 * When FIND_IDENT isn't defined, we backup until a blank.
1432 *
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001433 * Returns the length of the text, or zero if no text is found.
1434 * If text is found, a pointer to the text is put in "*text". This
1435 * points into the current buffer line and is not always NUL terminated.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001436 */
1437 int
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001438find_ident_under_cursor(char_u **text, int find_type)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001439{
1440 return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001441 curwin->w_cursor.col, text, NULL, find_type);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442}
1443
1444/*
1445 * Like find_ident_under_cursor(), but for any window and any position.
1446 * However: Uses 'iskeyword' from the current window!.
1447 */
1448 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001449find_ident_at_pos(
1450 win_T *wp,
1451 linenr_T lnum,
1452 colnr_T startcol,
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001453 char_u **text,
1454 int *textcol, // column where "text" starts, can be NULL
Bram Moolenaar9b578142016-01-30 19:39:49 +01001455 int find_type)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456{
1457 char_u *ptr;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001458 int col = 0; // init to shut up GCC
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001460 int this_class = 0;
1461 int prev_class;
1462 int prevcol;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001463 int bn = 0; // bracket nesting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464
1465 /*
1466 * if i == 0: try to find an identifier
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001467 * if i == 1: try to find any non-white text
Bram Moolenaar071d4272004-06-13 20:20:40 +00001468 */
1469 ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
1470 for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
1471 {
1472 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001473 * 1. skip to start of identifier/text
Bram Moolenaar071d4272004-06-13 20:20:40 +00001474 */
1475 col = startcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001476 if (has_mbyte)
1477 {
1478 while (ptr[col] != NUL)
1479 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001480 // Stop at a ']' to evaluate "a[x]".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 if ((find_type & FIND_EVAL) && ptr[col] == ']')
1482 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 this_class = mb_get_class(ptr + col);
1484 if (this_class != 0 && (i == 1 || this_class != 1))
1485 break;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001486 col += (*mb_ptr2len)(ptr + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 }
1488 }
1489 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 while (ptr[col] != NUL
Bram Moolenaar1c465442017-03-12 20:10:05 +01001491 && (i == 0 ? !vim_iswordc(ptr[col]) : VIM_ISWHITE(ptr[col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001492 && (!(find_type & FIND_EVAL) || ptr[col] != ']')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 )
1494 ++col;
1495
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001496 // When starting on a ']' count it, so that we include the '['.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497 bn = ptr[col] == ']';
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498
1499 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001500 * 2. Back up to start of identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 if (has_mbyte)
1503 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001504 // Remember class of character under cursor.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001505 if ((find_type & FIND_EVAL) && ptr[col] == ']')
1506 this_class = mb_get_class((char_u *)"a");
1507 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 this_class = mb_get_class(ptr + col);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001509 while (col > 0 && this_class != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 {
1511 prevcol = col - 1 - (*mb_head_off)(ptr, ptr + col - 1);
1512 prev_class = mb_get_class(ptr + prevcol);
1513 if (this_class != prev_class
1514 && (i == 0
1515 || prev_class == 0
1516 || (find_type & FIND_IDENT))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 && (!(find_type & FIND_EVAL)
1518 || prevcol == 0
1519 || !find_is_eval_item(ptr + prevcol, &prevcol,
1520 &bn, BACKWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 )
1522 break;
1523 col = prevcol;
1524 }
1525
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001526 // If we don't want just any old text, or we've found an
1527 // identifier, stop searching.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001528 if (this_class > 2)
1529 this_class = 2;
1530 if (!(find_type & FIND_STRING) || this_class == 2)
1531 break;
1532 }
1533 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001534 {
1535 while (col > 0
1536 && ((i == 0
1537 ? vim_iswordc(ptr[col - 1])
Bram Moolenaar1c465442017-03-12 20:10:05 +01001538 : (!VIM_ISWHITE(ptr[col - 1])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 && (!(find_type & FIND_IDENT)
1540 || !vim_iswordc(ptr[col - 1]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 || ((find_type & FIND_EVAL)
1542 && col > 1
1543 && find_is_eval_item(ptr + col - 1, &col,
1544 &bn, BACKWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 ))
1546 --col;
1547
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001548 // If we don't want just any old text, or we've found an
1549 // identifier, stop searching.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550 if (!(find_type & FIND_STRING) || vim_iswordc(ptr[col]))
1551 break;
1552 }
1553 }
1554
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001555 if (ptr[col] == NUL || (i == 0
1556 && (has_mbyte ? this_class != 2 : !vim_iswordc(ptr[col]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001557 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001558 // didn't find an identifier or text
Bram Moolenaar17627312019-06-02 19:53:44 +02001559 if ((find_type & FIND_NOERROR) == 0)
1560 {
1561 if (find_type & FIND_STRING)
1562 emsg(_("E348: No string under cursor"));
1563 else
1564 emsg(_(e_noident));
1565 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 return 0;
1567 }
1568 ptr += col;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001569 *text = ptr;
1570 if (textcol != NULL)
1571 *textcol = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001572
1573 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001574 * 3. Find the end if the identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 bn = 0;
1577 startcol -= col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578 col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001579 if (has_mbyte)
1580 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001581 // Search for point of changing multibyte character class.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582 this_class = mb_get_class(ptr);
1583 while (ptr[col] != NUL
1584 && ((i == 0 ? mb_get_class(ptr + col) == this_class
1585 : mb_get_class(ptr + col) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586 || ((find_type & FIND_EVAL)
1587 && col <= (int)startcol
1588 && find_is_eval_item(ptr + col, &col, &bn, FORWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001589 ))
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001590 col += (*mb_ptr2len)(ptr + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 }
1592 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 while ((i == 0 ? vim_iswordc(ptr[col])
Bram Moolenaar1c465442017-03-12 20:10:05 +01001594 : (ptr[col] != NUL && !VIM_ISWHITE(ptr[col])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001595 || ((find_type & FIND_EVAL)
1596 && col <= (int)startcol
1597 && find_is_eval_item(ptr + col, &col, &bn, FORWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001598 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 ++col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001600
1601 return col;
1602}
1603
1604/*
1605 * Prepare for redo of a normal command.
1606 */
1607 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001608prep_redo_cmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001609{
1610 prep_redo(cap->oap->regname, cap->count0,
1611 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
1612}
1613
1614/*
1615 * Prepare for redo of any command.
1616 * Note that only the last argument can be a multi-byte char.
1617 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001618 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001619prep_redo(
1620 int regname,
1621 long num,
1622 int cmd1,
1623 int cmd2,
1624 int cmd3,
1625 int cmd4,
1626 int cmd5)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001627{
1628 ResetRedobuff();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001629 if (regname != 0) // yank from specified buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00001630 {
1631 AppendCharToRedobuff('"');
1632 AppendCharToRedobuff(regname);
1633 }
1634 if (num)
1635 AppendNumberToRedobuff(num);
1636
1637 if (cmd1 != NUL)
1638 AppendCharToRedobuff(cmd1);
1639 if (cmd2 != NUL)
1640 AppendCharToRedobuff(cmd2);
1641 if (cmd3 != NUL)
1642 AppendCharToRedobuff(cmd3);
1643 if (cmd4 != NUL)
1644 AppendCharToRedobuff(cmd4);
1645 if (cmd5 != NUL)
1646 AppendCharToRedobuff(cmd5);
1647}
1648
1649/*
1650 * check for operator active and clear it
1651 *
1652 * return TRUE if operator was active
1653 */
1654 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001655checkclearop(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001656{
1657 if (oap->op_type == OP_NOP)
1658 return FALSE;
1659 clearopbeep(oap);
1660 return TRUE;
1661}
1662
1663/*
Bram Moolenaarc980de32007-05-06 11:59:04 +00001664 * Check for operator or Visual active. Clear active operator.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665 *
Bram Moolenaarc980de32007-05-06 11:59:04 +00001666 * Return TRUE if operator or Visual was active.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667 */
1668 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001669checkclearopq(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001671 if (oap->op_type == OP_NOP && !VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001672 return FALSE;
1673 clearopbeep(oap);
1674 return TRUE;
1675}
1676
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001677 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001678clearop(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001679{
1680 oap->op_type = OP_NOP;
1681 oap->regname = 0;
1682 oap->motion_force = NUL;
1683 oap->use_reg_one = FALSE;
1684}
1685
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001686 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001687clearopbeep(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688{
1689 clearop(oap);
1690 beep_flush();
1691}
1692
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693/*
1694 * Remove the shift modifier from a special key.
1695 */
1696 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001697unshift_special(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698{
1699 switch (cap->cmdchar)
1700 {
1701 case K_S_RIGHT: cap->cmdchar = K_RIGHT; break;
1702 case K_S_LEFT: cap->cmdchar = K_LEFT; break;
1703 case K_S_UP: cap->cmdchar = K_UP; break;
1704 case K_S_DOWN: cap->cmdchar = K_DOWN; break;
1705 case K_S_HOME: cap->cmdchar = K_HOME; break;
1706 case K_S_END: cap->cmdchar = K_END; break;
1707 }
1708 cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
1709}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001711/*
1712 * If the mode is currently displayed clear the command line or update the
1713 * command displayed.
1714 */
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02001715 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001716may_clear_cmdline(void)
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001717{
1718 if (mode_displayed)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001719 clear_cmdline = TRUE; // unshow visual mode later
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001720#ifdef FEAT_CMDL_INFO
1721 else
1722 clear_showcmd();
1723#endif
1724}
1725
Bram Moolenaar071d4272004-06-13 20:20:40 +00001726#if defined(FEAT_CMDL_INFO) || defined(PROTO)
1727/*
1728 * Routines for displaying a partly typed command
1729 */
1730
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001731#define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732static char_u showcmd_buf[SHOWCMD_BUFLEN];
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001733static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001734static int showcmd_is_clear = TRUE;
1735static int showcmd_visual = FALSE;
1736
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01001737static void display_showcmd(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738
1739 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001740clear_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741{
1742 if (!p_sc)
1743 return;
1744
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 if (VIsual_active && !char_avail())
1746 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001747 int cursor_bot = LT_POS(VIsual, curwin->w_cursor);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 long lines;
1749 colnr_T leftcol, rightcol;
1750 linenr_T top, bot;
1751
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001752 // Show the size of the Visual area.
Bram Moolenaar81d00072009-04-29 15:41:40 +00001753 if (cursor_bot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 {
1755 top = VIsual.lnum;
1756 bot = curwin->w_cursor.lnum;
1757 }
1758 else
1759 {
1760 top = curwin->w_cursor.lnum;
1761 bot = VIsual.lnum;
1762 }
1763# ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001764 // Include closed folds as a whole.
Bram Moolenaarcde88542015-08-11 19:14:00 +02001765 (void)hasFolding(top, &top, NULL);
1766 (void)hasFolding(bot, NULL, &bot);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767# endif
1768 lines = bot - top + 1;
1769
1770 if (VIsual_mode == Ctrl_V)
1771 {
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001772# ifdef FEAT_LINEBREAK
Bram Moolenaar81d00072009-04-29 15:41:40 +00001773 char_u *saved_sbr = p_sbr;
Bram Moolenaaree857022019-11-09 23:26:40 +01001774 char_u *saved_w_sbr = curwin->w_p_sbr;
Bram Moolenaar81d00072009-04-29 15:41:40 +00001775
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001776 // Make 'sbr' empty for a moment to get the correct size.
Bram Moolenaar81d00072009-04-29 15:41:40 +00001777 p_sbr = empty_option;
Bram Moolenaaree857022019-11-09 23:26:40 +01001778 curwin->w_p_sbr = empty_option;
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001779# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780 getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001781# ifdef FEAT_LINEBREAK
Bram Moolenaar81d00072009-04-29 15:41:40 +00001782 p_sbr = saved_sbr;
Bram Moolenaaree857022019-11-09 23:26:40 +01001783 curwin->w_p_sbr = saved_w_sbr;
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001784# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 sprintf((char *)showcmd_buf, "%ldx%ld", lines,
1786 (long)(rightcol - leftcol + 1));
1787 }
1788 else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum)
1789 sprintf((char *)showcmd_buf, "%ld", lines);
1790 else
Bram Moolenaarf91787c2010-07-17 12:47:16 +02001791 {
1792 char_u *s, *e;
1793 int l;
1794 int bytes = 0;
1795 int chars = 0;
1796
1797 if (cursor_bot)
1798 {
1799 s = ml_get_pos(&VIsual);
1800 e = ml_get_cursor();
1801 }
1802 else
1803 {
1804 s = ml_get_cursor();
1805 e = ml_get_pos(&VIsual);
1806 }
1807 while ((*p_sel != 'e') ? s <= e : s < e)
1808 {
1809 l = (*mb_ptr2len)(s);
1810 if (l == 0)
1811 {
1812 ++bytes;
1813 ++chars;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001814 break; // end of line
Bram Moolenaarf91787c2010-07-17 12:47:16 +02001815 }
1816 bytes += l;
1817 ++chars;
1818 s += l;
1819 }
1820 if (bytes == chars)
1821 sprintf((char *)showcmd_buf, "%d", chars);
1822 else
1823 sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
1824 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001825 showcmd_buf[SHOWCMD_COLS] = NUL; // truncate
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826 showcmd_visual = TRUE;
1827 }
1828 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 {
1830 showcmd_buf[0] = NUL;
1831 showcmd_visual = FALSE;
1832
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001833 // Don't actually display something if there is nothing to clear.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834 if (showcmd_is_clear)
1835 return;
1836 }
1837
1838 display_showcmd();
1839}
1840
1841/*
1842 * Add 'c' to string of shown command chars.
1843 * Return TRUE if output has been written (and setcursor() has been called).
1844 */
1845 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001846add_to_showcmd(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001847{
1848 char_u *p;
1849 int old_len;
1850 int extra_len;
1851 int overflow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 int i;
1853 static int ignore[] =
1854 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001855#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 K_VER_SCROLLBAR, K_HOR_SCROLLBAR,
1857 K_LEFTMOUSE_NM, K_LEFTRELEASE_NM,
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001858#endif
Bram Moolenaarec2da362017-01-21 20:04:22 +01001859 K_IGNORE, K_PS,
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001860 K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
1862 K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +02001863 K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001864 K_X1MOUSE, K_X1DRAG, K_X1RELEASE, K_X2MOUSE, K_X2DRAG, K_X2RELEASE,
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00001865 K_CURSORHOLD,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866 0
1867 };
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868
Bram Moolenaar09df3122006-01-23 22:23:09 +00001869 if (!p_sc || msg_silent != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870 return FALSE;
1871
1872 if (showcmd_visual)
1873 {
1874 showcmd_buf[0] = NUL;
1875 showcmd_visual = FALSE;
1876 }
1877
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001878 // Ignore keys that are scrollbar updates and mouse clicks
Bram Moolenaar071d4272004-06-13 20:20:40 +00001879 if (IS_SPECIAL(c))
1880 for (i = 0; ignore[i] != 0; ++i)
1881 if (ignore[i] == c)
1882 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001883
1884 p = transchar(c);
Bram Moolenaar7ba07412013-12-11 14:55:01 +01001885 if (*p == ' ')
1886 STRCPY(p, "<20>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887 old_len = (int)STRLEN(showcmd_buf);
1888 extra_len = (int)STRLEN(p);
1889 overflow = old_len + extra_len - SHOWCMD_COLS;
1890 if (overflow > 0)
Bram Moolenaarb0db5692007-08-14 20:54:49 +00001891 mch_memmove(showcmd_buf, showcmd_buf + overflow,
1892 old_len - overflow + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001893 STRCAT(showcmd_buf, p);
1894
1895 if (char_avail())
1896 return FALSE;
1897
1898 display_showcmd();
1899
1900 return TRUE;
1901}
1902
1903 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001904add_to_showcmd_c(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001905{
1906 if (!add_to_showcmd(c))
1907 setcursor();
1908}
1909
1910/*
1911 * Delete 'len' characters from the end of the shown command.
1912 */
1913 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001914del_from_showcmd(int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915{
1916 int old_len;
1917
1918 if (!p_sc)
1919 return;
1920
1921 old_len = (int)STRLEN(showcmd_buf);
1922 if (len > old_len)
1923 len = old_len;
1924 showcmd_buf[old_len - len] = NUL;
1925
1926 if (!char_avail())
1927 display_showcmd();
1928}
1929
1930/*
1931 * push_showcmd() and pop_showcmd() are used when waiting for the user to type
1932 * something and there is a partial mapping.
1933 */
1934 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001935push_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936{
1937 if (p_sc)
1938 STRCPY(old_showcmd_buf, showcmd_buf);
1939}
1940
1941 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001942pop_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943{
1944 if (!p_sc)
1945 return;
1946
1947 STRCPY(showcmd_buf, old_showcmd_buf);
1948
1949 display_showcmd();
1950}
1951
1952 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001953display_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001954{
1955 int len;
1956
1957 cursor_off();
1958
1959 len = (int)STRLEN(showcmd_buf);
1960 if (len == 0)
1961 showcmd_is_clear = TRUE;
1962 else
1963 {
1964 screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0);
1965 showcmd_is_clear = FALSE;
1966 }
1967
1968 /*
Bram Moolenaarf711faf2007-05-10 16:48:19 +00001969 * clear the rest of an old message by outputting up to SHOWCMD_COLS
1970 * spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 */
1972 screen_puts((char_u *)" " + len, (int)Rows - 1, sc_col + len, 0);
1973
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001974 setcursor(); // put cursor back where it belongs
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975}
1976#endif
1977
Bram Moolenaar071d4272004-06-13 20:20:40 +00001978/*
1979 * When "check" is FALSE, prepare for commands that scroll the window.
1980 * When "check" is TRUE, take care of scroll-binding after the window has
1981 * scrolled. Called from normal_cmd() and edit().
1982 */
1983 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001984do_check_scrollbind(int check)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985{
1986 static win_T *old_curwin = NULL;
1987 static linenr_T old_topline = 0;
1988#ifdef FEAT_DIFF
1989 static int old_topfill = 0;
1990#endif
1991 static buf_T *old_buf = NULL;
1992 static colnr_T old_leftcol = 0;
1993
1994 if (check && curwin->w_p_scb)
1995 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001996 // If a ":syncbind" command was just used, don't scroll, only reset
1997 // the values.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998 if (did_syncbind)
1999 did_syncbind = FALSE;
2000 else if (curwin == old_curwin)
2001 {
2002 /*
2003 * Synchronize other windows, as necessary according to
2004 * 'scrollbind'. Don't do this after an ":edit" command, except
2005 * when 'diff' is set.
2006 */
2007 if ((curwin->w_buffer == old_buf
2008#ifdef FEAT_DIFF
2009 || curwin->w_p_diff
2010#endif
2011 )
2012 && (curwin->w_topline != old_topline
2013#ifdef FEAT_DIFF
2014 || curwin->w_topfill != old_topfill
2015#endif
2016 || curwin->w_leftcol != old_leftcol))
2017 {
2018 check_scrollbind(curwin->w_topline - old_topline,
2019 (long)(curwin->w_leftcol - old_leftcol));
2020 }
2021 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002022 else if (vim_strchr(p_sbo, 'j')) // jump flag set in 'scrollopt'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 {
2024 /*
2025 * When switching between windows, make sure that the relative
2026 * vertical offset is valid for the new window. The relative
2027 * offset is invalid whenever another 'scrollbind' window has
2028 * scrolled to a point that would force the current window to
2029 * scroll past the beginning or end of its buffer. When the
2030 * resync is performed, some of the other 'scrollbind' windows may
2031 * need to jump so that the current window's relative position is
2032 * visible on-screen.
2033 */
2034 check_scrollbind(curwin->w_topline - curwin->w_scbind_pos, 0L);
2035 }
2036 curwin->w_scbind_pos = curwin->w_topline;
2037 }
2038
2039 old_curwin = curwin;
2040 old_topline = curwin->w_topline;
2041#ifdef FEAT_DIFF
2042 old_topfill = curwin->w_topfill;
2043#endif
2044 old_buf = curwin->w_buffer;
2045 old_leftcol = curwin->w_leftcol;
2046}
2047
2048/*
2049 * Synchronize any windows that have "scrollbind" set, based on the
2050 * number of rows by which the current window has changed
2051 * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
2052 */
2053 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002054check_scrollbind(linenr_T topline_diff, long leftcol_diff)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055{
2056 int want_ver;
2057 int want_hor;
2058 win_T *old_curwin = curwin;
2059 buf_T *old_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 int old_VIsual_select = VIsual_select;
2061 int old_VIsual_active = VIsual_active;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002062 colnr_T tgt_leftcol = curwin->w_leftcol;
2063 long topline;
2064 long y;
2065
2066 /*
2067 * check 'scrollopt' string for vertical and horizontal scroll options
2068 */
2069 want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0);
2070#ifdef FEAT_DIFF
2071 want_ver |= old_curwin->w_p_diff;
2072#endif
2073 want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0));
2074
2075 /*
2076 * loop through the scrollbound windows and scroll accordingly
2077 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 VIsual_select = VIsual_active = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02002079 FOR_ALL_WINDOWS(curwin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080 {
2081 curbuf = curwin->w_buffer;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002082 // skip original window and windows with 'noscrollbind'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083 if (curwin != old_curwin && curwin->w_p_scb)
2084 {
2085 /*
2086 * do the vertical scroll
2087 */
2088 if (want_ver)
2089 {
2090#ifdef FEAT_DIFF
2091 if (old_curwin->w_p_diff && curwin->w_p_diff)
2092 {
2093 diff_set_topline(old_curwin, curwin);
2094 }
2095 else
2096#endif
2097 {
2098 curwin->w_scbind_pos += topline_diff;
2099 topline = curwin->w_scbind_pos;
2100 if (topline > curbuf->b_ml.ml_line_count)
2101 topline = curbuf->b_ml.ml_line_count;
2102 if (topline < 1)
2103 topline = 1;
2104
2105 y = topline - curwin->w_topline;
2106 if (y > 0)
2107 scrollup(y, FALSE);
2108 else
2109 scrolldown(-y, FALSE);
2110 }
2111
2112 redraw_later(VALID);
2113 cursor_correct();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002114 curwin->w_redr_status = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002115 }
2116
2117 /*
2118 * do the horizontal scroll
2119 */
2120 if (want_hor && curwin->w_leftcol != tgt_leftcol)
2121 {
2122 curwin->w_leftcol = tgt_leftcol;
2123 leftcol_changed();
2124 }
2125 }
2126 }
2127
2128 /*
2129 * reset current-window
2130 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 VIsual_select = old_VIsual_select;
2132 VIsual_active = old_VIsual_active;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002133 curwin = old_curwin;
2134 curbuf = old_curbuf;
2135}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002136
2137/*
2138 * Command character that's ignored.
2139 * Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
Bram Moolenaar5ea0ac72010-05-07 15:52:08 +02002140 * xon/xoff.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002142 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002143nv_ignore(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002145 cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146}
2147
2148/*
Bram Moolenaarebefac62005-12-28 22:39:57 +00002149 * Command character that doesn't do anything, but unlike nv_ignore() does
2150 * start edit(). Used for "startinsert" executed while starting up.
2151 */
Bram Moolenaarebefac62005-12-28 22:39:57 +00002152 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002153nv_nop(cmdarg_T *cap UNUSED)
Bram Moolenaarebefac62005-12-28 22:39:57 +00002154{
2155}
2156
2157/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158 * Command character doesn't exist.
2159 */
2160 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002161nv_error(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002162{
2163 clearopbeep(cap->oap);
2164}
2165
2166/*
2167 * <Help> and <F1> commands.
2168 */
2169 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002170nv_help(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002171{
2172 if (!checkclearopq(cap->oap))
2173 ex_help(NULL);
2174}
2175
2176/*
2177 * CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
2178 */
2179 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002180nv_addsub(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002181{
Bram Moolenaarf2732452018-06-03 14:47:35 +02002182#ifdef FEAT_JOB_CHANNEL
2183 if (bt_prompt(curbuf) && !prompt_curpos_editable())
2184 clearopbeep(cap->oap);
2185 else
2186#endif
Bram Moolenaard79e5502016-01-10 22:13:02 +01002187 if (!VIsual_active && cap->oap->op_type == OP_NOP)
Bram Moolenaar9bb19302015-07-03 12:44:07 +02002188 {
Bram Moolenaaref2b5032016-01-12 22:20:58 +01002189 prep_redo_cmd(cap);
Bram Moolenaard79e5502016-01-10 22:13:02 +01002190 cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
2191 op_addsub(cap->oap, cap->count1, cap->arg);
2192 cap->oap->op_type = OP_NOP;
Bram Moolenaar9bb19302015-07-03 12:44:07 +02002193 }
Bram Moolenaard79e5502016-01-10 22:13:02 +01002194 else if (VIsual_active)
2195 nv_operator(cap);
Bram Moolenaar3a304b22015-06-25 13:57:36 +02002196 else
Bram Moolenaard79e5502016-01-10 22:13:02 +01002197 clearop(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002198}
2199
2200/*
2201 * CTRL-F, CTRL-B, etc: Scroll page up or down.
2202 */
2203 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002204nv_page(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002205{
2206 if (!checkclearop(cap->oap))
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002207 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002208 if (mod_mask & MOD_MASK_CTRL)
2209 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002210 // <C-PageUp>: tab page back; <C-PageDown>: tab page forward
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002211 if (cap->arg == BACKWARD)
2212 goto_tabpage(-(int)cap->count1);
2213 else
2214 goto_tabpage((int)cap->count0);
2215 }
2216 else
Bram Moolenaar4033c552017-09-16 20:54:51 +02002217 (void)onepage(cap->arg, cap->count1);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002218 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002219}
2220
2221/*
2222 * Implementation of "gd" and "gD" command.
2223 */
2224 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002225nv_gd(
2226 oparg_T *oap,
2227 int nchar,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002228 int thisblock) // 1 for "1gd" and "1gD"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002229{
2230 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002231 char_u *ptr;
2232
Bram Moolenaard9d30582005-05-18 22:10:28 +00002233 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
Bram Moolenaar1538fc32016-04-16 09:13:34 +02002234 || find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)
2235 == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 clearopbeep(oap);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002237#ifdef FEAT_FOLDING
2238 else if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP)
2239 foldOpenCursor();
2240#endif
2241}
2242
2243/*
Bram Moolenaar226630a2016-10-08 19:21:31 +02002244 * Return TRUE if line[offset] is not inside a C-style comment or string, FALSE
2245 * otherwise.
2246 */
2247 static int
2248is_ident(char_u *line, int offset)
2249{
2250 int i;
2251 int incomment = FALSE;
2252 int instring = 0;
2253 int prev = 0;
2254
2255 for (i = 0; i < offset && line[i] != NUL; i++)
2256 {
2257 if (instring != 0)
2258 {
2259 if (prev != '\\' && line[i] == instring)
2260 instring = 0;
2261 }
2262 else if ((line[i] == '"' || line[i] == '\'') && !incomment)
2263 {
2264 instring = line[i];
2265 }
2266 else
2267 {
2268 if (incomment)
2269 {
2270 if (prev == '*' && line[i] == '/')
2271 incomment = FALSE;
2272 }
2273 else if (prev == '/' && line[i] == '*')
2274 {
2275 incomment = TRUE;
2276 }
2277 else if (prev == '/' && line[i] == '/')
2278 {
2279 return FALSE;
2280 }
2281 }
2282
2283 prev = line[i];
2284 }
2285
2286 return incomment == FALSE && instring == 0;
2287}
2288
2289/*
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002290 * Search for variable declaration of "ptr[len]".
2291 * When "locally" is TRUE in the current function ("gd"), otherwise in the
2292 * current file ("gD").
2293 * When "thisblock" is TRUE check the {} block scope.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002294 * Return FAIL when not found.
2295 */
2296 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002297find_decl(
2298 char_u *ptr,
2299 int len,
2300 int locally,
2301 int thisblock,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002302 int flags_arg) // flags passed to searchit()
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002303{
2304 char_u *pat;
2305 pos_T old_pos;
2306 pos_T par_pos;
2307 pos_T found_pos;
2308 int t;
2309 int save_p_ws;
2310 int save_p_scs;
2311 int retval = OK;
Bram Moolenaar89d40322006-08-29 15:30:07 +00002312 int incll;
Bram Moolenaar23c60f22016-06-15 22:03:48 +02002313 int searchflags = flags_arg;
Bram Moolenaar226630a2016-10-08 19:21:31 +02002314 int valid;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002315
2316 if ((pat = alloc(len + 7)) == NULL)
2317 return FAIL;
Bram Moolenaard9d30582005-05-18 22:10:28 +00002318
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002319 // Put "\V" before the pattern to avoid that the special meaning of "."
2320 // and "~" causes trouble.
Bram Moolenaard9d30582005-05-18 22:10:28 +00002321 sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
2322 len, ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002323 old_pos = curwin->w_cursor;
2324 save_p_ws = p_ws;
2325 save_p_scs = p_scs;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002326 p_ws = FALSE; // don't wrap around end of file now
2327 p_scs = FALSE; // don't switch ignorecase off now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002328
2329 /*
2330 * With "gD" go to line 1.
2331 * With "gd" Search back for the start of the current function, then go
2332 * back until a blank line. If this fails go to line 1.
2333 */
Bram Moolenaar89d40322006-08-29 15:30:07 +00002334 if (!locally || !findpar(&incll, BACKWARD, 1L, '{', FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002336 setpcmark(); // Set in findpar() otherwise
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337 curwin->w_cursor.lnum = 1;
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002338 par_pos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 }
2340 else
2341 {
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002342 par_pos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 while (curwin->w_cursor.lnum > 1 && *skipwhite(ml_get_curline()) != NUL)
2344 --curwin->w_cursor.lnum;
2345 }
2346 curwin->w_cursor.col = 0;
2347
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002348 // Search forward for the identifier, ignore comment lines.
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002349 CLEAR_POS(&found_pos);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002350 for (;;)
2351 {
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002352 t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02002353 pat, 1L, searchflags, RE_LAST, NULL);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002354 if (curwin->w_cursor.lnum >= old_pos.lnum)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002355 t = FAIL; // match after start is failure too
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002356
Bram Moolenaar0fd92892006-03-09 22:27:48 +00002357 if (thisblock && t != FAIL)
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002358 {
2359 pos_T *pos;
2360
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002361 // Check that the block the match is in doesn't end before the
2362 // position where we started the search from.
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002363 if ((pos = findmatchlimit(NULL, '}', FM_FORWARD,
2364 (int)(old_pos.lnum - curwin->w_cursor.lnum + 1))) != NULL
2365 && pos->lnum < old_pos.lnum)
Bram Moolenaar60402d62017-04-20 18:54:50 +02002366 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002367 // There can't be a useful match before the end of this block.
2368 // Skip to the end.
Bram Moolenaar60402d62017-04-20 18:54:50 +02002369 curwin->w_cursor = *pos;
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002370 continue;
Bram Moolenaar60402d62017-04-20 18:54:50 +02002371 }
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002372 }
2373
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002374 if (t == FAIL)
2375 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002376 // If we previously found a valid position, use it.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002377 if (found_pos.lnum != 0)
2378 {
2379 curwin->w_cursor = found_pos;
2380 t = OK;
2381 }
2382 break;
2383 }
Bram Moolenaar81340392012-06-06 16:12:59 +02002384 if (get_leader_len(ml_get_curline(), NULL, FALSE, TRUE) > 0)
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002385 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002386 // Ignore this line, continue at start of next line.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002387 ++curwin->w_cursor.lnum;
2388 curwin->w_cursor.col = 0;
2389 continue;
2390 }
Bram Moolenaar226630a2016-10-08 19:21:31 +02002391 valid = is_ident(ml_get_curline(), curwin->w_cursor.col);
2392
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002393 // If the current position is not a valid identifier and a previous
2394 // match is present, favor that one instead.
Bram Moolenaar226630a2016-10-08 19:21:31 +02002395 if (!valid && found_pos.lnum != 0)
2396 {
2397 curwin->w_cursor = found_pos;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002398 break;
Bram Moolenaar226630a2016-10-08 19:21:31 +02002399 }
2400
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002401 // Global search: use first valid match found
Bram Moolenaar226630a2016-10-08 19:21:31 +02002402 if (valid && !locally)
2403 break;
2404 if (valid && curwin->w_cursor.lnum >= par_pos.lnum)
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002405 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002406 // If we previously found a valid position, use it.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002407 if (found_pos.lnum != 0)
2408 curwin->w_cursor = found_pos;
2409 break;
2410 }
2411
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002412 // For finding a local variable and the match is before the "{" or
2413 // inside a comment, continue searching. For K&R style function
2414 // declarations this skips the function header without types.
Bram Moolenaar226630a2016-10-08 19:21:31 +02002415 if (!valid)
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002416 CLEAR_POS(&found_pos);
Bram Moolenaar226630a2016-10-08 19:21:31 +02002417 else
Bram Moolenaar226630a2016-10-08 19:21:31 +02002418 found_pos = curwin->w_cursor;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002419 // Remove SEARCH_START from flags to avoid getting stuck at one
2420 // position.
Bram Moolenaar23c60f22016-06-15 22:03:48 +02002421 searchflags &= ~SEARCH_START;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422 }
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002423
2424 if (t == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 {
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002426 retval = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 curwin->w_cursor = old_pos;
2428 }
2429 else
2430 {
2431 curwin->w_set_curswant = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002432 // "n" searches forward now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 reset_search_dir();
2434 }
2435
2436 vim_free(pat);
2437 p_ws = save_p_ws;
2438 p_scs = save_p_scs;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002439
2440 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441}
2442
2443/*
2444 * Move 'dist' lines in direction 'dir', counting lines by *screen*
2445 * lines rather than lines in the file.
2446 * 'dist' must be positive.
2447 *
2448 * Return OK if able to move cursor, FAIL otherwise.
2449 */
2450 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002451nv_screengo(oparg_T *oap, int dir, long dist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452{
2453 int linelen = linetabsize(ml_get_curline());
2454 int retval = OK;
2455 int atend = FALSE;
2456 int n;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002457 int col_off1; // margin offset for first screen line
2458 int col_off2; // margin offset for wrapped screen line
2459 int width1; // text width for first screen line
2460 int width2; // test width for wrapped screen line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461
2462 oap->motion_type = MCHAR;
Bram Moolenaar91b2bdb2013-07-14 13:32:15 +02002463 oap->inclusive = (curwin->w_curswant == MAXCOL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002464
2465 col_off1 = curwin_col_off();
2466 col_off2 = col_off1 - curwin_col_off2();
Bram Moolenaar02631462017-09-22 15:20:32 +02002467 width1 = curwin->w_width - col_off1;
2468 width2 = curwin->w_width - col_off2;
Bram Moolenaar7cc8ec42015-01-27 20:59:31 +01002469 if (width2 == 0)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002470 width2 = 1; // avoid divide by zero
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 if (curwin->w_width != 0)
Bram Moolenaar44a2f922016-03-19 22:11:51 +01002473 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002474 /*
2475 * Instead of sticking at the last character of the buffer line we
2476 * try to stick in the last column of the screen.
2477 */
2478 if (curwin->w_curswant == MAXCOL)
2479 {
2480 atend = TRUE;
2481 validate_virtcol();
2482 if (width1 <= 0)
2483 curwin->w_curswant = 0;
2484 else
2485 {
2486 curwin->w_curswant = width1 - 1;
2487 if (curwin->w_virtcol > curwin->w_curswant)
2488 curwin->w_curswant += ((curwin->w_virtcol
2489 - curwin->w_curswant - 1) / width2 + 1) * width2;
2490 }
2491 }
2492 else
2493 {
2494 if (linelen > width1)
2495 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
2496 else
2497 n = width1;
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002498 if (curwin->w_curswant >= (colnr_T)n)
2499 curwin->w_curswant = n - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500 }
2501
2502 while (dist--)
2503 {
2504 if (dir == BACKWARD)
2505 {
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002506 if ((long)curwin->w_curswant >= width1)
2507 // Move back within the line. This can give a negative value
2508 // for w_curswant if width1 < width2 (with cpoptions+=n),
2509 // which will get clipped to column 0.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510 curwin->w_curswant -= width2;
2511 else
2512 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002513 // to previous line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002514 if (curwin->w_cursor.lnum == 1)
2515 {
2516 retval = FAIL;
2517 break;
2518 }
2519 --curwin->w_cursor.lnum;
2520#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002521 // Move to the start of a closed fold. Don't do that when
2522 // 'foldopen' contains "all": it will open in a moment.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523 if (!(fdo_flags & FDO_ALL))
2524 (void)hasFolding(curwin->w_cursor.lnum,
2525 &curwin->w_cursor.lnum, NULL);
2526#endif
2527 linelen = linetabsize(ml_get_curline());
2528 if (linelen > width1)
2529 curwin->w_curswant += (((linelen - width1 - 1) / width2)
2530 + 1) * width2;
2531 }
2532 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002533 else // dir == FORWARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534 {
2535 if (linelen > width1)
2536 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
2537 else
2538 n = width1;
2539 if (curwin->w_curswant + width2 < (colnr_T)n)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002540 // move forward within line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541 curwin->w_curswant += width2;
2542 else
2543 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002544 // to next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002546 // Move to the end of a closed fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 (void)hasFolding(curwin->w_cursor.lnum, NULL,
2548 &curwin->w_cursor.lnum);
2549#endif
2550 if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
2551 {
2552 retval = FAIL;
2553 break;
2554 }
2555 curwin->w_cursor.lnum++;
2556 curwin->w_curswant %= width2;
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002557 // Check if the cursor has moved below the number display
2558 // when width1 < width2 (with cpoptions+=n). Subtract width2
2559 // to get a negative value for w_curswant, which will get
2560 // clipped to column 0.
2561 if (curwin->w_curswant >= width1)
2562 curwin->w_curswant -= width2;
Bram Moolenaar914968e2011-06-20 00:45:58 +02002563 linelen = linetabsize(ml_get_curline());
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564 }
2565 }
2566 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002567 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002568
Bram Moolenaar6cd3aee2014-01-14 13:18:58 +01002569 if (virtual_active() && atend)
2570 coladvance(MAXCOL);
2571 else
2572 coladvance(curwin->w_curswant);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573
Bram Moolenaar071d4272004-06-13 20:20:40 +00002574 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
2575 {
Bram Moolenaar773b1582014-08-29 14:20:51 +02002576 colnr_T virtcol;
2577
Bram Moolenaar071d4272004-06-13 20:20:40 +00002578 /*
2579 * Check for landing on a character that got split at the end of the
2580 * last line. We want to advance a screenline, not end up in the same
2581 * screenline or move two screenlines.
2582 */
2583 validate_virtcol();
Bram Moolenaar773b1582014-08-29 14:20:51 +02002584 virtcol = curwin->w_virtcol;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002585#if defined(FEAT_LINEBREAK)
Bram Moolenaaree857022019-11-09 23:26:40 +01002586 if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL)
2587 virtcol -= vim_strsize(get_showbreak_value(curwin));
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002588#endif
Bram Moolenaar773b1582014-08-29 14:20:51 +02002589
2590 if (virtcol > curwin->w_curswant
Bram Moolenaar071d4272004-06-13 20:20:40 +00002591 && (curwin->w_curswant < (colnr_T)width1
2592 ? (curwin->w_curswant > (colnr_T)width1 / 2)
2593 : ((curwin->w_curswant - width1) % width2
2594 > (colnr_T)width2 / 2)))
2595 --curwin->w_cursor.col;
2596 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002597
2598 if (atend)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002599 curwin->w_curswant = MAXCOL; // stick in the last column
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600
2601 return retval;
2602}
2603
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604/*
2605 * Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
2606 * cap->arg must be TRUE for CTRL-E.
2607 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002608 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002609nv_scroll_line(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002610{
2611 if (!checkclearop(cap->oap))
2612 scroll_redraw(cap->arg, cap->count1);
2613}
2614
2615/*
2616 * Scroll "count" lines up or down, and redraw.
2617 */
2618 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002619scroll_redraw(int up, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002620{
2621 linenr_T prev_topline = curwin->w_topline;
2622#ifdef FEAT_DIFF
2623 int prev_topfill = curwin->w_topfill;
2624#endif
2625 linenr_T prev_lnum = curwin->w_cursor.lnum;
2626
2627 if (up)
2628 scrollup(count, TRUE);
2629 else
2630 scrolldown(count, TRUE);
Bram Moolenaar375e3392019-01-31 18:26:10 +01002631 if (get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002633 // Adjust the cursor position for 'scrolloff'. Mark w_topline as
2634 // valid, otherwise the screen jumps back at the end of the file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635 cursor_correct();
2636 check_cursor_moved(curwin);
2637 curwin->w_valid |= VALID_TOPLINE;
2638
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002639 // If moved back to where we were, at least move the cursor, otherwise
2640 // we get stuck at one position. Don't move the cursor up if the
2641 // first line of the buffer is already on the screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 while (curwin->w_topline == prev_topline
2643#ifdef FEAT_DIFF
2644 && curwin->w_topfill == prev_topfill
2645#endif
2646 )
2647 {
2648 if (up)
2649 {
2650 if (curwin->w_cursor.lnum > prev_lnum
2651 || cursor_down(1L, FALSE) == FAIL)
2652 break;
2653 }
2654 else
2655 {
2656 if (curwin->w_cursor.lnum < prev_lnum
2657 || prev_topline == 1L
2658 || cursor_up(1L, FALSE) == FAIL)
2659 break;
2660 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002661 // Mark w_topline as valid, otherwise the screen jumps back at the
2662 // end of the file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 check_cursor_moved(curwin);
2664 curwin->w_valid |= VALID_TOPLINE;
2665 }
2666 }
2667 if (curwin->w_cursor.lnum != prev_lnum)
2668 coladvance(curwin->w_curswant);
2669 redraw_later(VALID);
2670}
2671
2672/*
2673 * Commands that start with "z".
2674 */
2675 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002676nv_zet(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677{
2678 long n;
2679 colnr_T col;
2680 int nchar = cap->nchar;
2681#ifdef FEAT_FOLDING
2682 long old_fdl = curwin->w_p_fdl;
2683 int old_fen = curwin->w_p_fen;
2684#endif
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00002685#ifdef FEAT_SPELL
Bram Moolenaard0131a82006-03-04 21:46:13 +00002686 int undo = FALSE;
2687#endif
Bram Moolenaar375e3392019-01-31 18:26:10 +01002688 long siso = get_sidescrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002689
2690 if (VIM_ISDIGIT(nchar))
2691 {
2692 /*
2693 * "z123{nchar}": edit the count before obtaining {nchar}
2694 */
2695 if (checkclearop(cap->oap))
2696 return;
2697 n = nchar - '0';
2698 for (;;)
2699 {
2700#ifdef USE_ON_FLY_SCROLL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002701 dont_scroll = TRUE; // disallow scrolling here
Bram Moolenaar071d4272004-06-13 20:20:40 +00002702#endif
2703 ++no_mapping;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002704 ++allow_keys; // no mapping for nchar, but allow key codes
Bram Moolenaar61abfd12007-09-13 16:26:47 +00002705 nchar = plain_vgetc();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 LANGMAP_ADJUST(nchar, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 --no_mapping;
2708 --allow_keys;
2709#ifdef FEAT_CMDL_INFO
2710 (void)add_to_showcmd(nchar);
2711#endif
2712 if (nchar == K_DEL || nchar == K_KDEL)
2713 n /= 10;
2714 else if (VIM_ISDIGIT(nchar))
2715 n = n * 10 + (nchar - '0');
2716 else if (nchar == CAR)
2717 {
2718#ifdef FEAT_GUI
2719 need_mouse_correct = TRUE;
2720#endif
2721 win_setheight((int)n);
2722 break;
2723 }
2724 else if (nchar == 'l'
2725 || nchar == 'h'
2726 || nchar == K_LEFT
Bram Moolenaara88d9682005-03-25 21:45:43 +00002727 || nchar == K_RIGHT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728 {
2729 cap->count1 = n ? n * cap->count1 : cap->count1;
2730 goto dozet;
2731 }
2732 else
2733 {
2734 clearopbeep(cap->oap);
2735 break;
2736 }
2737 }
2738 cap->oap->op_type = OP_NOP;
2739 return;
2740 }
2741
2742dozet:
2743 if (
2744#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002745 // "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
2746 // and "zC" only in Visual mode. "zj" and "zk" are motion
2747 // commands.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748 cap->nchar != 'f' && cap->nchar != 'F'
2749 && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
2750 && cap->nchar != 'j' && cap->nchar != 'k'
2751 &&
2752#endif
2753 checkclearop(cap->oap))
2754 return;
2755
2756 /*
2757 * For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
2758 * If line number given, set cursor.
2759 */
2760 if ((vim_strchr((char_u *)"+\r\nt.z^-b", nchar) != NULL)
2761 && cap->count0
2762 && cap->count0 != curwin->w_cursor.lnum)
2763 {
2764 setpcmark();
2765 if (cap->count0 > curbuf->b_ml.ml_line_count)
2766 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2767 else
2768 curwin->w_cursor.lnum = cap->count0;
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002769 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 }
2771
2772 switch (nchar)
2773 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002774 // "z+", "z<CR>" and "zt": put cursor at top of screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 case '+':
2776 if (cap->count0 == 0)
2777 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002778 // No count given: put cursor at the line below screen
2779 validate_botline(); // make sure w_botline is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00002780 if (curwin->w_botline > curbuf->b_ml.ml_line_count)
2781 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2782 else
2783 curwin->w_cursor.lnum = curwin->w_botline;
2784 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002785 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 case NL:
2787 case CAR:
2788 case K_KENTER:
2789 beginline(BL_WHITE | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002790 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791
2792 case 't': scroll_cursor_top(0, TRUE);
2793 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01002794 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 break;
2796
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002797 // "z." and "zz": put cursor in middle of screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00002798 case '.': beginline(BL_WHITE | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002799 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800
2801 case 'z': scroll_cursor_halfway(TRUE);
2802 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01002803 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804 break;
2805
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002806 // "z^", "z-" and "zb": put cursor at bottom of screen
2807 case '^': // Strange Vi behavior: <count>z^ finds line at top of window
2808 // when <count> is at bottom of window, and puts that one at
2809 // bottom of window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810 if (cap->count0 != 0)
2811 {
2812 scroll_cursor_bot(0, TRUE);
2813 curwin->w_cursor.lnum = curwin->w_topline;
2814 }
2815 else if (curwin->w_topline == 1)
2816 curwin->w_cursor.lnum = 1;
2817 else
2818 curwin->w_cursor.lnum = curwin->w_topline - 1;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002819 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 case '-':
2821 beginline(BL_WHITE | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002822 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823
2824 case 'b': scroll_cursor_bot(0, TRUE);
2825 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01002826 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827 break;
2828
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002829 // "zH" - scroll screen right half-page
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830 case 'H':
Bram Moolenaar02631462017-09-22 15:20:32 +02002831 cap->count1 *= curwin->w_width / 2;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002832 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002833
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002834 // "zh" - scroll screen to the right
Bram Moolenaar071d4272004-06-13 20:20:40 +00002835 case 'h':
2836 case K_LEFT:
2837 if (!curwin->w_p_wrap)
2838 {
2839 if ((colnr_T)cap->count1 > curwin->w_leftcol)
2840 curwin->w_leftcol = 0;
2841 else
2842 curwin->w_leftcol -= (colnr_T)cap->count1;
2843 leftcol_changed();
2844 }
2845 break;
2846
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002847 // "zL" - scroll screen left half-page
Bram Moolenaar02631462017-09-22 15:20:32 +02002848 case 'L': cap->count1 *= curwin->w_width / 2;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002849 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002850
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002851 // "zl" - scroll screen to the left
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852 case 'l':
2853 case K_RIGHT:
2854 if (!curwin->w_p_wrap)
2855 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002856 // scroll the window left
Bram Moolenaar071d4272004-06-13 20:20:40 +00002857 curwin->w_leftcol += (colnr_T)cap->count1;
2858 leftcol_changed();
2859 }
2860 break;
2861
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002862 // "zs" - scroll screen, cursor at the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863 case 's': if (!curwin->w_p_wrap)
2864 {
2865#ifdef FEAT_FOLDING
2866 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002867 col = 0; // like the cursor is in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868 else
2869#endif
2870 getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
Bram Moolenaar375e3392019-01-31 18:26:10 +01002871 if ((long)col > siso)
2872 col -= siso;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873 else
2874 col = 0;
2875 if (curwin->w_leftcol != col)
2876 {
2877 curwin->w_leftcol = col;
2878 redraw_later(NOT_VALID);
2879 }
2880 }
2881 break;
2882
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002883 // "ze" - scroll screen, cursor at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 case 'e': if (!curwin->w_p_wrap)
2885 {
2886#ifdef FEAT_FOLDING
2887 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002888 col = 0; // like the cursor is in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889 else
2890#endif
2891 getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
Bram Moolenaar02631462017-09-22 15:20:32 +02002892 n = curwin->w_width - curwin_col_off();
Bram Moolenaar375e3392019-01-31 18:26:10 +01002893 if ((long)col + siso < n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002894 col = 0;
2895 else
Bram Moolenaar375e3392019-01-31 18:26:10 +01002896 col = col + siso - n + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897 if (curwin->w_leftcol != col)
2898 {
2899 curwin->w_leftcol = col;
2900 redraw_later(NOT_VALID);
2901 }
2902 }
2903 break;
2904
2905#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002906 // "zF": create fold command
2907 // "zf": create fold operator
Bram Moolenaar071d4272004-06-13 20:20:40 +00002908 case 'F':
2909 case 'f': if (foldManualAllowed(TRUE))
2910 {
2911 cap->nchar = 'f';
2912 nv_operator(cap);
2913 curwin->w_p_fen = TRUE;
2914
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002915 // "zF" is like "zfzf"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 if (nchar == 'F' && cap->oap->op_type == OP_FOLD)
2917 {
2918 nv_operator(cap);
2919 finish_op = TRUE;
2920 }
2921 }
2922 else
2923 clearopbeep(cap->oap);
2924 break;
2925
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002926 // "zd": delete fold at cursor
2927 // "zD": delete fold at cursor recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928 case 'd':
2929 case 'D': if (foldManualAllowed(FALSE))
2930 {
2931 if (VIsual_active)
2932 nv_operator(cap);
2933 else
2934 deleteFold(curwin->w_cursor.lnum,
2935 curwin->w_cursor.lnum, nchar == 'D', FALSE);
2936 }
2937 break;
2938
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002939 // "zE": erase all folds
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940 case 'E': if (foldmethodIsManual(curwin))
2941 {
2942 clearFolding(curwin);
2943 changed_window_setting();
2944 }
2945 else if (foldmethodIsMarker(curwin))
2946 deleteFold((linenr_T)1, curbuf->b_ml.ml_line_count,
2947 TRUE, FALSE);
2948 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002949 emsg(_("E352: Cannot erase folds with current 'foldmethod'"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 break;
2951
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002952 // "zn": fold none: reset 'foldenable'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953 case 'n': curwin->w_p_fen = FALSE;
2954 break;
2955
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002956 // "zN": fold Normal: set 'foldenable'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 case 'N': curwin->w_p_fen = TRUE;
2958 break;
2959
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002960 // "zi": invert folding: toggle 'foldenable'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002961 case 'i': curwin->w_p_fen = !curwin->w_p_fen;
2962 break;
2963
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002964 // "za": open closed fold or close open fold at cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00002965 case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
2966 openFold(curwin->w_cursor.lnum, cap->count1);
2967 else
2968 {
2969 closeFold(curwin->w_cursor.lnum, cap->count1);
2970 curwin->w_p_fen = TRUE;
2971 }
2972 break;
2973
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002974 // "zA": open fold at cursor recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00002975 case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
2976 openFoldRecurse(curwin->w_cursor.lnum);
2977 else
2978 {
2979 closeFoldRecurse(curwin->w_cursor.lnum);
2980 curwin->w_p_fen = TRUE;
2981 }
2982 break;
2983
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002984 // "zo": open fold at cursor or Visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985 case 'o': if (VIsual_active)
2986 nv_operator(cap);
2987 else
2988 openFold(curwin->w_cursor.lnum, cap->count1);
2989 break;
2990
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002991 // "zO": open fold recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 case 'O': if (VIsual_active)
2993 nv_operator(cap);
2994 else
2995 openFoldRecurse(curwin->w_cursor.lnum);
2996 break;
2997
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002998 // "zc": close fold at cursor or Visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +00002999 case 'c': if (VIsual_active)
3000 nv_operator(cap);
3001 else
3002 closeFold(curwin->w_cursor.lnum, cap->count1);
3003 curwin->w_p_fen = TRUE;
3004 break;
3005
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003006 // "zC": close fold recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00003007 case 'C': if (VIsual_active)
3008 nv_operator(cap);
3009 else
3010 closeFoldRecurse(curwin->w_cursor.lnum);
3011 curwin->w_p_fen = TRUE;
3012 break;
3013
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003014 // "zv": open folds at the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015 case 'v': foldOpenCursor();
3016 break;
3017
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003018 // "zx": re-apply 'foldlevel' and open folds at the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00003019 case 'x': curwin->w_p_fen = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003020 curwin->w_foldinvalid = TRUE; // recompute folds
3021 newFoldLevel(); // update right now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022 foldOpenCursor();
3023 break;
3024
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003025 // "zX": undo manual opens/closes, re-apply 'foldlevel'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026 case 'X': curwin->w_p_fen = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003027 curwin->w_foldinvalid = TRUE; // recompute folds
3028 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003029 break;
3030
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003031 // "zm": fold more
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032 case 'm': if (curwin->w_p_fdl > 0)
Bram Moolenaar7d2757a2015-03-31 17:46:22 +02003033 {
3034 curwin->w_p_fdl -= cap->count1;
3035 if (curwin->w_p_fdl < 0)
3036 curwin->w_p_fdl = 0;
3037 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003038 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039 curwin->w_p_fen = TRUE;
3040 break;
3041
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003042 // "zM": close all folds
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043 case 'M': curwin->w_p_fdl = 0;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003044 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003045 curwin->w_p_fen = TRUE;
3046 break;
3047
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003048 // "zr": reduce folding
Bram Moolenaar7d2757a2015-03-31 17:46:22 +02003049 case 'r': curwin->w_p_fdl += cap->count1;
3050 {
3051 int d = getDeepestNesting();
3052
3053 if (curwin->w_p_fdl >= d)
3054 curwin->w_p_fdl = d;
3055 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056 break;
3057
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003058 // "zR": open all folds
Bram Moolenaar071d4272004-06-13 20:20:40 +00003059 case 'R': curwin->w_p_fdl = getDeepestNesting();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003060 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061 break;
3062
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003063 case 'j': // "zj" move to next fold downwards
3064 case 'k': // "zk" move to next fold upwards
Bram Moolenaar071d4272004-06-13 20:20:40 +00003065 if (foldMoveTo(TRUE, nchar == 'j' ? FORWARD : BACKWARD,
3066 cap->count1) == FAIL)
3067 clearopbeep(cap->oap);
3068 break;
3069
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003070#endif // FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00003072#ifdef FEAT_SPELL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003073 case 'u': // "zug" and "zuw": undo "zg" and "zw"
Bram Moolenaard0131a82006-03-04 21:46:13 +00003074 ++no_mapping;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003075 ++allow_keys; // no mapping for nchar, but allow key codes
Bram Moolenaar61abfd12007-09-13 16:26:47 +00003076 nchar = plain_vgetc();
Bram Moolenaard0131a82006-03-04 21:46:13 +00003077 LANGMAP_ADJUST(nchar, TRUE);
Bram Moolenaard0131a82006-03-04 21:46:13 +00003078 --no_mapping;
3079 --allow_keys;
3080#ifdef FEAT_CMDL_INFO
3081 (void)add_to_showcmd(nchar);
3082#endif
3083 if (vim_strchr((char_u *)"gGwW", nchar) == NULL)
3084 {
3085 clearopbeep(cap->oap);
3086 break;
3087 }
3088 undo = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003089 // FALLTHROUGH
Bram Moolenaard0131a82006-03-04 21:46:13 +00003090
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003091 case 'g': // "zg": add good word to word list
3092 case 'w': // "zw": add wrong word to word list
3093 case 'G': // "zG": add good word to temp word list
3094 case 'W': // "zW": add wrong word to temp word list
Bram Moolenaarb765d632005-06-07 21:00:02 +00003095 {
3096 char_u *ptr = NULL;
3097 int len;
3098
3099 if (checkclearop(cap->oap))
3100 break;
Bram Moolenaarb765d632005-06-07 21:00:02 +00003101 if (VIsual_active && get_visual_text(cap, &ptr, &len)
3102 == FAIL)
3103 return;
Bram Moolenaarda2303d2005-08-30 21:55:26 +00003104 if (ptr == NULL)
3105 {
3106 pos_T pos = curwin->w_cursor;
Bram Moolenaarda2303d2005-08-30 21:55:26 +00003107
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003108 // Find bad word under the cursor. When 'spell' is
3109 // off this fails and find_ident_under_cursor() is
3110 // used below.
Bram Moolenaar134bf072013-09-25 18:54:24 +02003111 emsg_off++;
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003112 len = spell_move_to(curwin, FORWARD, TRUE, TRUE, NULL);
Bram Moolenaar134bf072013-09-25 18:54:24 +02003113 emsg_off--;
Bram Moolenaarda2303d2005-08-30 21:55:26 +00003114 if (len != 0 && curwin->w_cursor.col <= pos.col)
3115 ptr = ml_get_pos(&curwin->w_cursor);
3116 curwin->w_cursor = pos;
3117 }
3118
Bram Moolenaarb765d632005-06-07 21:00:02 +00003119 if (ptr == NULL && (len = find_ident_under_cursor(&ptr,
3120 FIND_IDENT)) == 0)
3121 return;
Bram Moolenaar08cc3742019-08-11 22:51:14 +02003122 spell_add_word(ptr, len, nchar == 'w' || nchar == 'W'
3123 ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
Bram Moolenaard0131a82006-03-04 21:46:13 +00003124 (nchar == 'G' || nchar == 'W')
3125 ? 0 : (int)cap->count1,
3126 undo);
Bram Moolenaarb765d632005-06-07 21:00:02 +00003127 }
Bram Moolenaar3982c542005-06-08 21:56:31 +00003128 break;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00003129
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003130 case '=': // "z=": suggestions for a badly spelled word
Bram Moolenaar66fa2712006-01-22 23:22:22 +00003131 if (!checkclearop(cap->oap))
Bram Moolenaard12a1322005-08-21 22:08:24 +00003132 spell_suggest((int)cap->count0);
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00003133 break;
Bram Moolenaarb765d632005-06-07 21:00:02 +00003134#endif
3135
Bram Moolenaar071d4272004-06-13 20:20:40 +00003136 default: clearopbeep(cap->oap);
3137 }
3138
3139#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003140 // Redraw when 'foldenable' changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00003141 if (old_fen != curwin->w_p_fen)
3142 {
3143# ifdef FEAT_DIFF
3144 win_T *wp;
3145
3146 if (foldmethodIsDiff(curwin) && curwin->w_p_scb)
3147 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003148 // Adjust 'foldenable' in diff-synced windows.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003149 FOR_ALL_WINDOWS(wp)
3150 {
3151 if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb)
3152 {
3153 wp->w_p_fen = curwin->w_p_fen;
3154 changed_window_setting_win(wp);
3155 }
3156 }
3157 }
3158# endif
3159 changed_window_setting();
3160 }
3161
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003162 // Redraw when 'foldlevel' changed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163 if (old_fdl != curwin->w_p_fdl)
3164 newFoldLevel();
3165#endif
3166}
3167
3168#ifdef FEAT_GUI
3169/*
3170 * Vertical scrollbar movement.
3171 */
3172 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003173nv_ver_scrollbar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003174{
3175 if (cap->oap->op_type != OP_NOP)
3176 clearopbeep(cap->oap);
3177
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003178 // Even if an operator was pending, we still want to scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00003179 gui_do_scroll();
3180}
3181
3182/*
3183 * Horizontal scrollbar movement.
3184 */
3185 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003186nv_hor_scrollbar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003187{
3188 if (cap->oap->op_type != OP_NOP)
3189 clearopbeep(cap->oap);
3190
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003191 // Even if an operator was pending, we still want to scroll
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +02003192 gui_do_horiz_scroll(scrollbar_value, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193}
3194#endif
3195
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003196#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003197/*
3198 * Click in GUI tab.
3199 */
3200 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003201nv_tabline(cmdarg_T *cap)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003202{
3203 if (cap->oap->op_type != OP_NOP)
3204 clearopbeep(cap->oap);
3205
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003206 // Even if an operator was pending, we still want to jump tabs.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003207 goto_tabpage(current_tab);
3208}
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003209
3210/*
3211 * Selected item in tab line menu.
3212 */
3213 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003214nv_tabmenu(cmdarg_T *cap)
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003215{
3216 if (cap->oap->op_type != OP_NOP)
3217 clearopbeep(cap->oap);
3218
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003219 // Even if an operator was pending, we still want to jump tabs.
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003220 handle_tabmenu();
3221}
3222
3223/*
3224 * Handle selecting an item of the GUI tab line menu.
3225 * Used in Normal and Insert mode.
3226 */
3227 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003228handle_tabmenu(void)
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003229{
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003230 switch (current_tabmenu)
3231 {
3232 case TABLINE_MENU_CLOSE:
3233 if (current_tab == 0)
3234 do_cmdline_cmd((char_u *)"tabclose");
3235 else
3236 {
3237 vim_snprintf((char *)IObuff, IOSIZE, "tabclose %d",
3238 current_tab);
3239 do_cmdline_cmd(IObuff);
3240 }
3241 break;
3242
3243 case TABLINE_MENU_NEW:
Bram Moolenaardfd76912015-02-27 15:03:58 +01003244 if (current_tab == 0)
3245 do_cmdline_cmd((char_u *)"$tabnew");
3246 else
3247 {
3248 vim_snprintf((char *)IObuff, IOSIZE, "%dtabnew",
3249 current_tab - 1);
3250 do_cmdline_cmd(IObuff);
3251 }
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003252 break;
3253
3254 case TABLINE_MENU_OPEN:
Bram Moolenaardfd76912015-02-27 15:03:58 +01003255 if (current_tab == 0)
3256 do_cmdline_cmd((char_u *)"browse $tabnew");
3257 else
3258 {
3259 vim_snprintf((char *)IObuff, IOSIZE, "browse %dtabnew",
3260 current_tab - 1);
3261 do_cmdline_cmd(IObuff);
3262 }
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003263 break;
3264 }
3265}
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003266#endif
3267
Bram Moolenaar071d4272004-06-13 20:20:40 +00003268/*
3269 * "Q" command.
3270 */
3271 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003272nv_exmode(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273{
3274 /*
3275 * Ignore 'Q' in Visual mode, just give a beep.
3276 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277 if (VIsual_active)
Bram Moolenaar165bc692015-07-21 17:53:25 +02003278 vim_beep(BO_EX);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003279 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 do_exmode(FALSE);
3281}
3282
3283/*
3284 * Handle a ":" command.
3285 */
3286 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003287nv_colon(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003288{
3289 int old_p_im;
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003290 int cmd_result;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292 if (VIsual_active)
3293 nv_operator(cap);
3294 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295 {
3296 if (cap->oap->op_type != OP_NOP)
3297 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003298 // Using ":" as a movement is characterwise exclusive.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299 cap->oap->motion_type = MCHAR;
3300 cap->oap->inclusive = FALSE;
3301 }
3302 else if (cap->count0)
3303 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003304 // translate "count:" into ":.,.+(count - 1)"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003305 stuffcharReadbuff('.');
3306 if (cap->count0 > 1)
3307 {
3308 stuffReadbuff((char_u *)",.+");
3309 stuffnumReadbuff((long)cap->count0 - 1L);
3310 }
3311 }
3312
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003313 // When typing, don't type below an old message
Bram Moolenaar071d4272004-06-13 20:20:40 +00003314 if (KeyTyped)
3315 compute_cmdrow();
3316
3317 old_p_im = p_im;
3318
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003319 // get a command line and execute it
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003320 cmd_result = do_cmdline(NULL, getexline, NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003321 cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
3322
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003323 // If 'insertmode' changed, enter or exit Insert mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00003324 if (p_im != old_p_im)
3325 {
3326 if (p_im)
3327 restart_edit = 'i';
3328 else
3329 restart_edit = 0;
3330 }
3331
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003332 if (cmd_result == FAIL)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003333 // The Ex command failed, do not execute the operator.
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003334 clearop(cap->oap);
3335 else if (cap->oap->op_type != OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003336 && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
3337 || cap->oap->start.col >
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003338 (colnr_T)STRLEN(ml_get(cap->oap->start.lnum))
3339 || did_emsg
3340 ))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003341 // The start of the operator has become invalid by the Ex command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003342 clearopbeep(cap->oap);
3343 }
3344}
3345
3346/*
3347 * Handle CTRL-G command.
3348 */
3349 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003350nv_ctrlg(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003352 if (VIsual_active) // toggle Selection/Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353 {
3354 VIsual_select = !VIsual_select;
3355 showmode();
3356 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003357 else if (!checkclearop(cap->oap))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003358 // print full name if count given or :cd used
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 fileinfo((int)cap->count0, FALSE, TRUE);
3360}
3361
3362/*
3363 * Handle CTRL-H <Backspace> command.
3364 */
3365 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003366nv_ctrlh(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 if (VIsual_active && VIsual_select)
3369 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003370 cap->cmdchar = 'x'; // BS key behaves like 'x' in Select mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 v_visop(cap);
3372 }
3373 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374 nv_left(cap);
3375}
3376
3377/*
3378 * CTRL-L: clear screen and redraw.
3379 */
3380 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003381nv_clear(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382{
3383 if (!checkclearop(cap->oap))
3384 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385#ifdef FEAT_SYN_HL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003386 // Clear all syntax states to force resyncing.
Bram Moolenaar860cae12010-06-05 23:22:07 +02003387 syn_stack_free_all(curwin->w_s);
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003388# ifdef FEAT_RELTIME
3389 {
3390 win_T *wp;
3391
3392 FOR_ALL_WINDOWS(wp)
3393 wp->w_s->b_syn_slow = FALSE;
3394 }
3395# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396#endif
3397 redraw_later(CLEAR);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003398#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3399# ifdef VIMDLL
3400 if (!gui.in_use)
3401# endif
3402 resize_console_buf();
Bram Moolenaar78d21da2019-02-17 15:00:52 +01003403#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404 }
3405}
3406
3407/*
3408 * CTRL-O: In Select mode: switch to Visual mode for one command.
3409 * Otherwise: Go to older pcmark.
3410 */
3411 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003412nv_ctrlo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003414 if (VIsual_active && VIsual_select)
3415 {
3416 VIsual_select = FALSE;
3417 showmode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003418 restart_VIsual_select = 2; // restart Select mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00003419 }
3420 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421 {
3422 cap->count1 = -cap->count1;
3423 nv_pcmark(cap);
3424 }
3425}
3426
3427/*
Bram Moolenaar1bbb6192018-11-10 16:02:01 +01003428 * CTRL-^ command, short for ":e #". Works even when the alternate buffer is
3429 * not named.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430 */
3431 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003432nv_hat(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433{
3434 if (!checkclearopq(cap->oap))
3435 (void)buflist_getfile((int)cap->count0, (linenr_T)0,
3436 GETF_SETMARK|GETF_ALT, FALSE);
3437}
3438
3439/*
3440 * "Z" commands.
3441 */
3442 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003443nv_Zet(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003444{
3445 if (!checkclearopq(cap->oap))
3446 {
3447 switch (cap->nchar)
3448 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003449 // "ZZ": equivalent to ":x".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003450 case 'Z': do_cmdline_cmd((char_u *)"x");
3451 break;
3452
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003453 // "ZQ": equivalent to ":q!" (Elvis compatible).
Bram Moolenaar071d4272004-06-13 20:20:40 +00003454 case 'Q': do_cmdline_cmd((char_u *)"q!");
3455 break;
3456
3457 default: clearopbeep(cap->oap);
3458 }
3459 }
3460}
3461
Bram Moolenaar071d4272004-06-13 20:20:40 +00003462/*
3463 * Call nv_ident() as if "c1" was used, with "c2" as next character.
3464 */
3465 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003466do_nv_ident(int c1, int c2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003467{
3468 oparg_T oa;
3469 cmdarg_T ca;
3470
3471 clear_oparg(&oa);
Bram Moolenaara80faa82020-04-12 19:37:17 +02003472 CLEAR_FIELD(ca);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 ca.oap = &oa;
3474 ca.cmdchar = c1;
3475 ca.nchar = c2;
3476 nv_ident(&ca);
3477}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478
3479/*
3480 * Handle the commands that use the word under the cursor.
3481 * [g] CTRL-] :ta to current identifier
3482 * [g] 'K' run program for current identifier
3483 * [g] '*' / to current identifier or string
3484 * [g] '#' ? to current identifier or string
3485 * g ']' :tselect for current identifier
3486 */
3487 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003488nv_ident(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489{
3490 char_u *ptr = NULL;
3491 char_u *buf;
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003492 unsigned buflen;
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003493 char_u *newbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494 char_u *p;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003495 char_u *kp; // value of 'keywordprg'
3496 int kp_help; // 'keywordprg' is ":he"
3497 int kp_ex; // 'keywordprg' starts with ":"
3498 int n = 0; // init for GCC
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 int cmdchar;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003500 int g_cmd; // "g" command
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003501 int tag_cmd = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502 char_u *aux_ptr;
3503 int isman;
3504 int isman_s;
3505
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003506 if (cap->cmdchar == 'g') // "g*", "g#", "g]" and "gCTRL-]"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507 {
3508 cmdchar = cap->nchar;
3509 g_cmd = TRUE;
3510 }
3511 else
3512 {
3513 cmdchar = cap->cmdchar;
3514 g_cmd = FALSE;
3515 }
3516
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003517 if (cmdchar == POUND) // the pound sign, '#' for English keyboards
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518 cmdchar = '#';
3519
3520 /*
3521 * The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
3522 */
3523 if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K')
3524 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525 if (VIsual_active && get_visual_text(cap, &ptr, &n) == FAIL)
3526 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527 if (checkclearopq(cap->oap))
3528 return;
3529 }
3530
3531 if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
3532 (cmdchar == '*' || cmdchar == '#')
3533 ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
3534 {
3535 clearop(cap->oap);
3536 return;
3537 }
3538
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003539 // Allocate buffer to put the command in. Inserting backslashes can
3540 // double the length of the word. p_kp / curbuf->b_p_kp could be added
3541 // and some numbers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003542 kp = (*curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp);
3543 kp_help = (*kp == NUL || STRCMP(kp, ":he") == 0
3544 || STRCMP(kp, ":help") == 0);
Bram Moolenaara4f99f52017-08-26 16:25:32 +02003545 if (kp_help && *skipwhite(ptr) == NUL)
3546 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003547 emsg(_(e_noident)); // found white space only
Bram Moolenaara4f99f52017-08-26 16:25:32 +02003548 return;
3549 }
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003550 kp_ex = (*kp == ':');
3551 buflen = (unsigned)(n * 2 + 30 + STRLEN(kp));
3552 buf = alloc(buflen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003553 if (buf == NULL)
3554 return;
3555 buf[0] = NUL;
3556
3557 switch (cmdchar)
3558 {
3559 case '*':
3560 case '#':
3561 /*
3562 * Put cursor at start of word, makes search skip the word
3563 * under the cursor.
3564 * Call setpcmark() first, so "*``" puts the cursor back where
3565 * it was.
3566 */
3567 setpcmark();
3568 curwin->w_cursor.col = (colnr_T) (ptr - ml_get_curline());
3569
3570 if (!g_cmd && vim_iswordp(ptr))
3571 STRCPY(buf, "\\<");
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003572 no_smartcase = TRUE; // don't use 'smartcase' now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003573 break;
3574
3575 case 'K':
3576 if (kp_help)
3577 STRCPY(buf, "he! ");
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003578 else if (kp_ex)
3579 {
3580 if (cap->count0 != 0)
3581 vim_snprintf((char *)buf, buflen, "%s %ld",
3582 kp, cap->count0);
3583 else
3584 STRCPY(buf, kp);
3585 STRCAT(buf, " ");
3586 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587 else
3588 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003589 // An external command will probably use an argument starting
3590 // with "-" as an option. To avoid trouble we skip the "-".
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003591 while (*ptr == '-' && n > 0)
3592 {
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003593 ++ptr;
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003594 --n;
3595 }
3596 if (n == 0)
3597 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003598 emsg(_(e_noident)); // found dashes only
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003599 vim_free(buf);
3600 return;
3601 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003602
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003603 // When a count is given, turn it into a range. Is this
3604 // really what we want?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003605 isman = (STRCMP(kp, "man") == 0);
3606 isman_s = (STRCMP(kp, "man -s") == 0);
3607 if (cap->count0 != 0 && !(isman || isman_s))
3608 sprintf((char *)buf, ".,.+%ld", cap->count0 - 1);
3609
3610 STRCAT(buf, "! ");
3611 if (cap->count0 == 0 && isman_s)
3612 STRCAT(buf, "man");
3613 else
3614 STRCAT(buf, kp);
3615 STRCAT(buf, " ");
3616 if (cap->count0 != 0 && (isman || isman_s))
3617 {
3618 sprintf((char *)buf + STRLEN(buf), "%ld", cap->count0);
3619 STRCAT(buf, " ");
3620 }
3621 }
3622 break;
3623
3624 case ']':
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003625 tag_cmd = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626#ifdef FEAT_CSCOPE
3627 if (p_cst)
3628 STRCPY(buf, "cstag ");
3629 else
3630#endif
3631 STRCPY(buf, "ts ");
3632 break;
3633
3634 default:
Bram Moolenaar97e7a842010-03-17 13:07:08 +01003635 tag_cmd = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636 if (curbuf->b_help)
3637 STRCPY(buf, "he! ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638 else
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003639 {
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003640 if (g_cmd)
3641 STRCPY(buf, "tj ");
3642 else
3643 sprintf((char *)buf, "%ldta ", cap->count0);
3644 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645 }
3646
3647 /*
3648 * Now grab the chars in the identifier
3649 */
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003650 if (cmdchar == 'K' && !kp_help)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651 {
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003652 ptr = vim_strnsave(ptr, n);
Bram Moolenaar426f3752016-11-04 21:22:37 +01003653 if (kp_ex)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003654 // Escape the argument properly for an Ex command
Bram Moolenaar426f3752016-11-04 21:22:37 +01003655 p = vim_strsave_fnameescape(ptr, FALSE);
3656 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003657 // Escape the argument properly for a shell command
Bram Moolenaar426f3752016-11-04 21:22:37 +01003658 p = vim_strsave_shellescape(ptr, TRUE, TRUE);
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003659 vim_free(ptr);
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003660 if (p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661 {
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003662 vim_free(buf);
3663 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003665 newbuf = vim_realloc(buf, STRLEN(buf) + STRLEN(p) + 1);
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003666 if (newbuf == NULL)
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003667 {
3668 vim_free(buf);
3669 vim_free(p);
3670 return;
3671 }
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003672 buf = newbuf;
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003673 STRCAT(buf, p);
3674 vim_free(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003676 else
3677 {
3678 if (cmdchar == '*')
3679 aux_ptr = (char_u *)(p_magic ? "/.*~[^$\\" : "/^$\\");
3680 else if (cmdchar == '#')
3681 aux_ptr = (char_u *)(p_magic ? "/?.*~[^$\\" : "/?^$\\");
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003682 else if (tag_cmd)
Bram Moolenaar77a0aa42010-10-13 18:06:47 +02003683 {
3684 if (curbuf->b_help)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003685 // ":help" handles unescaped argument
Bram Moolenaar77a0aa42010-10-13 18:06:47 +02003686 aux_ptr = (char_u *)"";
3687 else
3688 aux_ptr = (char_u *)"\\|\"\n[";
3689 }
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003690 else
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003691 aux_ptr = (char_u *)"\\|\"\n*?[";
3692
3693 p = buf + STRLEN(buf);
3694 while (n-- > 0)
3695 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003696 // put a backslash before \ and some others
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003697 if (vim_strchr(aux_ptr, *ptr) != NULL)
3698 *p++ = '\\';
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003699 // When current byte is a part of multibyte character, copy all
3700 // bytes of that character.
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003701 if (has_mbyte)
3702 {
3703 int i;
3704 int len = (*mb_ptr2len)(ptr) - 1;
3705
3706 for (i = 0; i < len && n >= 1; ++i, --n)
3707 *p++ = *ptr++;
3708 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003709 *p++ = *ptr++;
3710 }
3711 *p = NUL;
3712 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713
3714 /*
3715 * Execute the command.
3716 */
3717 if (cmdchar == '*' || cmdchar == '#')
3718 {
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003719 if (!g_cmd && (has_mbyte
3720 ? vim_iswordp(mb_prevptr(ml_get_curline(), ptr))
3721 : vim_iswordc(ptr[-1])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722 STRCAT(buf, "\\>");
Bram Moolenaard7663c22019-08-06 21:59:57 +02003723
3724 // put pattern in search history
Bram Moolenaarc7be3f32009-12-24 14:01:12 +00003725 init_history();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003726 add_to_history(HIST_SEARCH, buf, TRUE, NUL);
Bram Moolenaard7663c22019-08-06 21:59:57 +02003727
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02003728 (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 }
3730 else
Bram Moolenaar45e18cb2019-04-28 18:05:35 +02003731 {
3732 g_tag_at_cursor = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 do_cmdline_cmd(buf);
Bram Moolenaar45e18cb2019-04-28 18:05:35 +02003734 g_tag_at_cursor = FALSE;
3735 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736
3737 vim_free(buf);
3738}
3739
Bram Moolenaar071d4272004-06-13 20:20:40 +00003740/*
3741 * Get visually selected text, within one line only.
3742 * Returns FAIL if more than one line selected.
3743 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00003744 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01003745get_visual_text(
3746 cmdarg_T *cap,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003747 char_u **pp, // return: start of selected text
3748 int *lenp) // return: length of selected text
Bram Moolenaar071d4272004-06-13 20:20:40 +00003749{
3750 if (VIsual_mode != 'V')
3751 unadjust_for_sel();
3752 if (VIsual.lnum != curwin->w_cursor.lnum)
3753 {
Bram Moolenaard857f0e2005-06-21 22:37:39 +00003754 if (cap != NULL)
3755 clearopbeep(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756 return FAIL;
3757 }
3758 if (VIsual_mode == 'V')
3759 {
3760 *pp = ml_get_curline();
3761 *lenp = (int)STRLEN(*pp);
3762 }
3763 else
3764 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003765 if (LT_POS(curwin->w_cursor, VIsual))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003766 {
3767 *pp = ml_get_pos(&curwin->w_cursor);
3768 *lenp = VIsual.col - curwin->w_cursor.col + 1;
3769 }
3770 else
3771 {
3772 *pp = ml_get_pos(&VIsual);
3773 *lenp = curwin->w_cursor.col - VIsual.col + 1;
3774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775 if (has_mbyte)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003776 // Correct the length to include the whole last character.
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003777 *lenp += (*mb_ptr2len)(*pp + (*lenp - 1)) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778 }
3779 reset_VIsual_and_resel();
3780 return OK;
3781}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782
3783/*
3784 * CTRL-T: backwards in tag stack
3785 */
3786 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003787nv_tagpop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788{
3789 if (!checkclearopq(cap->oap))
3790 do_tag((char_u *)"", DT_POP, (int)cap->count1, FALSE, TRUE);
3791}
3792
3793/*
3794 * Handle scrolling command 'H', 'L' and 'M'.
3795 */
3796 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003797nv_scroll(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003798{
3799 int used = 0;
3800 long n;
3801#ifdef FEAT_FOLDING
3802 linenr_T lnum;
3803#endif
3804 int half;
3805
3806 cap->oap->motion_type = MLINE;
3807 setpcmark();
3808
3809 if (cap->cmdchar == 'L')
3810 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003811 validate_botline(); // make sure curwin->w_botline is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00003812 curwin->w_cursor.lnum = curwin->w_botline - 1;
3813 if (cap->count1 - 1 >= curwin->w_cursor.lnum)
3814 curwin->w_cursor.lnum = 1;
3815 else
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003816 {
3817#ifdef FEAT_FOLDING
3818 if (hasAnyFolding(curwin))
3819 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003820 // Count a fold for one screen line.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003821 for (n = cap->count1 - 1; n > 0
3822 && curwin->w_cursor.lnum > curwin->w_topline; --n)
3823 {
3824 (void)hasFolding(curwin->w_cursor.lnum,
3825 &curwin->w_cursor.lnum, NULL);
3826 --curwin->w_cursor.lnum;
3827 }
3828 }
3829 else
3830#endif
3831 curwin->w_cursor.lnum -= cap->count1 - 1;
3832 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 }
3834 else
3835 {
3836 if (cap->cmdchar == 'M')
3837 {
3838#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003839 // Don't count filler lines above the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 used -= diff_check_fill(curwin, curwin->w_topline)
3841 - curwin->w_topfill;
3842#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003843 validate_botline(); // make sure w_empty_rows is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 half = (curwin->w_height - curwin->w_empty_rows + 1) / 2;
3845 for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
3846 {
3847#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003848 // Count half he number of filler lines to be "below this
3849 // line" and half to be "above the next line".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline
3851 + n) / 2 >= half)
3852 {
3853 --n;
3854 break;
3855 }
3856#endif
3857 used += plines(curwin->w_topline + n);
3858 if (used >= half)
3859 break;
3860#ifdef FEAT_FOLDING
3861 if (hasFolding(curwin->w_topline + n, NULL, &lnum))
3862 n = lnum - curwin->w_topline;
3863#endif
3864 }
3865 if (n > 0 && used > curwin->w_height)
3866 --n;
3867 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003868 else // (cap->cmdchar == 'H')
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003869 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 n = cap->count1 - 1;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003871#ifdef FEAT_FOLDING
3872 if (hasAnyFolding(curwin))
3873 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003874 // Count a fold for one screen line.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003875 lnum = curwin->w_topline;
3876 while (n-- > 0 && lnum < curwin->w_botline - 1)
3877 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02003878 (void)hasFolding(lnum, NULL, &lnum);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003879 ++lnum;
3880 }
3881 n = lnum - curwin->w_topline;
3882 }
3883#endif
3884 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 curwin->w_cursor.lnum = curwin->w_topline + n;
3886 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
3887 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
3888 }
3889
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003890 // Correct for 'so', except when an operator is pending.
Bram Moolenaar44cc4cf2017-10-15 22:13:37 +02003891 if (cap->oap->op_type == OP_NOP)
3892 cursor_correct();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 beginline(BL_SOL | BL_FIX);
3894}
3895
3896/*
3897 * Cursor right commands.
3898 */
3899 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003900nv_right(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901{
3902 long n;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003903 int past_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003904
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00003905 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
3906 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003907 // <C-Right> and <S-Right> move a word or WORD right
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00003908 if (mod_mask & MOD_MASK_CTRL)
3909 cap->arg = TRUE;
3910 nv_wordcmd(cap);
3911 return;
3912 }
3913
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 cap->oap->motion_type = MCHAR;
3915 cap->oap->inclusive = FALSE;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003916 past_line = (VIsual_active && *p_sel != 'o');
Bram Moolenaar071d4272004-06-13 20:20:40 +00003917
Bram Moolenaar071d4272004-06-13 20:20:40 +00003918 /*
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003919 * In virtual edit mode, there's no such thing as "past_line", as lines
3920 * are (theoretically) infinitely long.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 */
3922 if (virtual_active())
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003923 past_line = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924
3925 for (n = cap->count1; n > 0; --n)
3926 {
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003927 if ((!past_line && oneright() == FAIL)
3928 || (past_line && *ml_get_cursor() == NUL)
Bram Moolenaar78a15312009-05-15 19:33:18 +00003929 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 {
3931 /*
Bram Moolenaar362e1a32006-03-06 23:29:24 +00003932 * <Space> wraps to next line if 'whichwrap' has 's'.
3933 * 'l' wraps to next line if 'whichwrap' has 'l'.
3934 * CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 */
3936 if ( ((cap->cmdchar == ' '
3937 && vim_strchr(p_ww, 's') != NULL)
3938 || (cap->cmdchar == 'l'
3939 && vim_strchr(p_ww, 'l') != NULL)
Bram Moolenaara88d9682005-03-25 21:45:43 +00003940 || (cap->cmdchar == K_RIGHT
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941 && vim_strchr(p_ww, '>') != NULL))
3942 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
3943 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003944 // When deleting we also count the NL as a character.
3945 // Set cap->oap->inclusive when last char in the line is
3946 // included, move to next line after that
Bram Moolenaar362e1a32006-03-06 23:29:24 +00003947 if ( cap->oap->op_type != OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003948 && !cap->oap->inclusive
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003949 && !LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003950 cap->oap->inclusive = TRUE;
3951 else
3952 {
3953 ++curwin->w_cursor.lnum;
3954 curwin->w_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 curwin->w_set_curswant = TRUE;
3957 cap->oap->inclusive = FALSE;
3958 }
3959 continue;
3960 }
3961 if (cap->oap->op_type == OP_NOP)
3962 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003963 // Only beep and flush if not moved at all
Bram Moolenaar071d4272004-06-13 20:20:40 +00003964 if (n == cap->count1)
3965 beep_flush();
3966 }
3967 else
3968 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003969 if (!LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 cap->oap->inclusive = TRUE;
3971 }
3972 break;
3973 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003974 else if (past_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975 {
3976 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003977 if (virtual_active())
3978 oneright();
3979 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003980 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 if (has_mbyte)
Bram Moolenaar56ebbab2019-09-20 13:40:14 +02003982 curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor());
Bram Moolenaar071d4272004-06-13 20:20:40 +00003983 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 ++curwin->w_cursor.col;
3985 }
3986 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 }
3988#ifdef FEAT_FOLDING
3989 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
3990 && cap->oap->op_type == OP_NOP)
3991 foldOpenCursor();
3992#endif
3993}
3994
3995/*
3996 * Cursor left commands.
3997 *
3998 * Returns TRUE when operator end should not be adjusted.
3999 */
4000 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004001nv_left(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002{
4003 long n;
4004
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004005 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
4006 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004007 // <C-Left> and <S-Left> move a word or WORD left
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004008 if (mod_mask & MOD_MASK_CTRL)
4009 cap->arg = 1;
4010 nv_bck_word(cap);
4011 return;
4012 }
4013
Bram Moolenaar071d4272004-06-13 20:20:40 +00004014 cap->oap->motion_type = MCHAR;
4015 cap->oap->inclusive = FALSE;
4016 for (n = cap->count1; n > 0; --n)
4017 {
4018 if (oneleft() == FAIL)
4019 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004020 // <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
4021 // 'h' wraps to previous line if 'whichwrap' has 'h'.
4022 // CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023 if ( (((cap->cmdchar == K_BS
4024 || cap->cmdchar == Ctrl_H)
4025 && vim_strchr(p_ww, 'b') != NULL)
4026 || (cap->cmdchar == 'h'
4027 && vim_strchr(p_ww, 'h') != NULL)
Bram Moolenaara88d9682005-03-25 21:45:43 +00004028 || (cap->cmdchar == K_LEFT
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 && vim_strchr(p_ww, '<') != NULL))
4030 && curwin->w_cursor.lnum > 1)
4031 {
4032 --(curwin->w_cursor.lnum);
4033 coladvance((colnr_T)MAXCOL);
4034 curwin->w_set_curswant = TRUE;
4035
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004036 // When the NL before the first char has to be deleted we
4037 // put the cursor on the NUL after the previous line.
4038 // This is a very special case, be careful!
4039 // Don't adjust op_end now, otherwise it won't work.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 if ( (cap->oap->op_type == OP_DELETE
4041 || cap->oap->op_type == OP_CHANGE)
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004042 && !LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043 {
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004044 char_u *cp = ml_get_cursor();
4045
4046 if (*cp != NUL)
4047 {
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004048 if (has_mbyte)
4049 curwin->w_cursor.col += (*mb_ptr2len)(cp);
4050 else
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004051 ++curwin->w_cursor.col;
4052 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053 cap->retval |= CA_NO_ADJ_OP_END;
4054 }
4055 continue;
4056 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004057 // Only beep and flush if not moved at all
Bram Moolenaar071d4272004-06-13 20:20:40 +00004058 else if (cap->oap->op_type == OP_NOP && n == cap->count1)
4059 beep_flush();
4060 break;
4061 }
4062 }
4063#ifdef FEAT_FOLDING
4064 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
4065 && cap->oap->op_type == OP_NOP)
4066 foldOpenCursor();
4067#endif
4068}
4069
4070/*
4071 * Cursor up commands.
4072 * cap->arg is TRUE for "-": Move cursor to first non-blank.
4073 */
4074 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004075nv_up(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004076{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004077 if (mod_mask & MOD_MASK_SHIFT)
4078 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004079 // <S-Up> is page up
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004080 cap->arg = BACKWARD;
4081 nv_page(cap);
4082 }
4083 else
4084 {
4085 cap->oap->motion_type = MLINE;
4086 if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
4087 clearopbeep(cap->oap);
4088 else if (cap->arg)
4089 beginline(BL_WHITE | BL_FIX);
4090 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091}
4092
4093/*
4094 * Cursor down commands.
4095 * cap->arg is TRUE for CR and "+": Move cursor to first non-blank.
4096 */
4097 static void
Bram Moolenaar1b010052016-09-12 12:24:11 +02004098nv_down(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004100 if (mod_mask & MOD_MASK_SHIFT)
4101 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004102 // <S-Down> is page down
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004103 cap->arg = FORWARD;
4104 nv_page(cap);
4105 }
Bram Moolenaar4033c552017-09-16 20:54:51 +02004106#if defined(FEAT_QUICKFIX)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004107 // Quickfix window only: view the result under the cursor.
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004108 else if (bt_quickfix(curbuf) && cap->cmdchar == CAR)
4109 qf_view_result(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110#endif
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004111 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 {
4113#ifdef FEAT_CMDWIN
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004114 // In the cmdline window a <CR> executes the command.
Bram Moolenaar05159a02005-02-26 23:04:13 +00004115 if (cmdwin_type != 0 && cap->cmdchar == CAR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 cmdwin_result = CAR;
4117 else
4118#endif
Bram Moolenaarf2732452018-06-03 14:47:35 +02004119#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004120 // In a prompt buffer a <CR> in the last line invokes the callback.
Bram Moolenaarf2732452018-06-03 14:47:35 +02004121 if (bt_prompt(curbuf) && cap->cmdchar == CAR
4122 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
4123 {
4124 invoke_prompt_callback();
4125 if (restart_edit == 0)
4126 restart_edit = 'a';
4127 }
4128 else
4129#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004130 {
4131 cap->oap->motion_type = MLINE;
4132 if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
4133 clearopbeep(cap->oap);
4134 else if (cap->arg)
4135 beginline(BL_WHITE | BL_FIX);
4136 }
4137 }
4138}
4139
4140#ifdef FEAT_SEARCHPATH
4141/*
4142 * Grab the file name under the cursor and edit it.
4143 */
4144 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004145nv_gotofile(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146{
4147 char_u *ptr;
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004148 linenr_T lnum = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00004150 if (text_locked())
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151 {
4152 clearopbeep(cap->oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00004153 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 return;
4155 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004156 if (curbuf_locked())
4157 {
4158 clearop(cap->oap);
4159 return;
4160 }
Bram Moolenaar5aed0cc2020-05-12 22:02:21 +02004161#ifdef FEAT_PROP_POPUP
4162 if (ERROR_IF_TERM_POPUP_WINDOW)
4163 return;
4164#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004166 ptr = grab_file_name(cap->count1, &lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167
4168 if (ptr != NULL)
4169 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004170 // do autowrite if necessary
Bram Moolenaareb44a682017-08-03 22:44:55 +02004171 if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf))
Bram Moolenaarcde88542015-08-11 19:14:00 +02004172 (void)autowrite(curbuf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 setpcmark();
Bram Moolenaar2a79ed22017-05-24 09:51:39 +02004174 if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
Bram Moolenaareb44a682017-08-03 22:44:55 +02004175 buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
Bram Moolenaar2a79ed22017-05-24 09:51:39 +02004176 && cap->nchar == 'F' && lnum >= 0)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004177 {
4178 curwin->w_cursor.lnum = lnum;
4179 check_cursor_lnum();
4180 beginline(BL_SOL | BL_FIX);
4181 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 vim_free(ptr);
4183 }
4184 else
4185 clearop(cap->oap);
4186}
4187#endif
4188
4189/*
4190 * <End> command: to end of current line or last line.
4191 */
4192 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004193nv_end(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004194{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004195 if (cap->arg || (mod_mask & MOD_MASK_CTRL)) // CTRL-END = goto last line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004196 {
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004197 cap->arg = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198 nv_goto(cap);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004199 cap->count1 = 1; // to end of current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200 }
4201 nv_dollar(cap);
4202}
4203
4204/*
4205 * Handle the "$" command.
4206 */
4207 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004208nv_dollar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209{
4210 cap->oap->motion_type = MCHAR;
4211 cap->oap->inclusive = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004212 // In virtual mode when off the edge of a line and an operator
4213 // is pending (whew!) keep the cursor where it is.
4214 // Otherwise, send it to the end of the line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 if (!virtual_active() || gchar_cursor() != NUL
4216 || cap->oap->op_type == OP_NOP)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004217 curwin->w_curswant = MAXCOL; // so we stay at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218 if (cursor_down((long)(cap->count1 - 1),
4219 cap->oap->op_type == OP_NOP) == FAIL)
4220 clearopbeep(cap->oap);
4221#ifdef FEAT_FOLDING
4222 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
4223 foldOpenCursor();
4224#endif
4225}
4226
4227/*
4228 * Implementation of '?' and '/' commands.
4229 * If cap->arg is TRUE don't set PC mark.
4230 */
4231 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004232nv_search(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004233{
4234 oparg_T *oap = cap->oap;
Bram Moolenaardda933d2016-09-03 21:04:58 +02004235 pos_T save_cursor = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004236
4237 if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13)
4238 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004239 // Translate "g??" to "g?g?"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004240 cap->cmdchar = 'g';
4241 cap->nchar = '?';
4242 nv_operator(cap);
4243 return;
4244 }
4245
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004246 // When using 'incsearch' the cursor may be moved to set a different search
4247 // start position.
Bram Moolenaare96a2492019-06-25 04:12:16 +02004248 cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249
4250 if (cap->searchbuf == NULL)
4251 {
4252 clearop(oap);
4253 return;
4254 }
4255
Bram Moolenaar46539112015-02-17 15:43:57 +01004256 (void)normal_search(cap, cap->cmdchar, cap->searchbuf,
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004257 (cap->arg || !EQUAL_POS(save_cursor, curwin->w_cursor))
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004258 ? 0 : SEARCH_MARK, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259}
4260
4261/*
4262 * Handle "N" and "n" commands.
4263 * cap->arg is SEARCH_REV for "N", 0 for "n".
4264 */
4265 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004266nv_next(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267{
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004268 pos_T old = curwin->w_cursor;
4269 int wrapped = FALSE;
4270 int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, &wrapped);
Bram Moolenaar46539112015-02-17 15:43:57 +01004271
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004272 if (i == 1 && !wrapped && EQUAL_POS(old, curwin->w_cursor))
Bram Moolenaar46539112015-02-17 15:43:57 +01004273 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004274 // Avoid getting stuck on the current cursor position, which can
4275 // happen when an offset is given and the cursor is on the last char
4276 // in the buffer: Repeat with count + 1.
Bram Moolenaar46539112015-02-17 15:43:57 +01004277 cap->count1 += 1;
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004278 (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL);
Bram Moolenaar46539112015-02-17 15:43:57 +01004279 cap->count1 -= 1;
4280 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281}
4282
4283/*
4284 * Search for "pat" in direction "dir" ('/' or '?', 0 for repeat).
4285 * Uses only cap->count1 and cap->oap from "cap".
Bram Moolenaar46539112015-02-17 15:43:57 +01004286 * Return 0 for failure, 1 for found, 2 for found and line offset added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004287 */
Bram Moolenaar46539112015-02-17 15:43:57 +01004288 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01004289normal_search(
4290 cmdarg_T *cap,
4291 int dir,
4292 char_u *pat,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004293 int opt, // extra flags for do_search()
4294 int *wrapped)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295{
4296 int i;
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004297 searchit_arg_T sia;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298
4299 cap->oap->motion_type = MCHAR;
4300 cap->oap->inclusive = FALSE;
4301 cap->oap->use_reg_one = TRUE;
4302 curwin->w_set_curswant = TRUE;
4303
Bram Moolenaara80faa82020-04-12 19:37:17 +02004304 CLEAR_FIELD(sia);
Bram Moolenaarc036e872020-02-21 21:30:52 +01004305 i = do_search(cap->oap, dir, dir, pat, cap->count1,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004306 opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
4307 if (wrapped != NULL)
4308 *wrapped = sia.sa_wrapped;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 if (i == 0)
4310 clearop(cap->oap);
4311 else
4312 {
4313 if (i == 2)
4314 cap->oap->motion_type = MLINE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316#ifdef FEAT_FOLDING
4317 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
4318 foldOpenCursor();
4319#endif
4320 }
4321
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004322 // "/$" will put the cursor after the end of the line, may need to
4323 // correct that here
Bram Moolenaar071d4272004-06-13 20:20:40 +00004324 check_cursor();
Bram Moolenaar46539112015-02-17 15:43:57 +01004325 return i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326}
4327
4328/*
4329 * Character search commands.
4330 * cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', TRUE for
4331 * ',' and FALSE for ';'.
4332 * cap->nchar is NUL for ',' and ';' (repeat the search)
4333 */
4334 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004335nv_csearch(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336{
4337 int t_cmd;
4338
4339 if (cap->cmdchar == 't' || cap->cmdchar == 'T')
4340 t_cmd = TRUE;
4341 else
4342 t_cmd = FALSE;
4343
4344 cap->oap->motion_type = MCHAR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == FAIL)
4346 clearopbeep(cap->oap);
4347 else
4348 {
4349 curwin->w_set_curswant = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004350 // Include a Tab for "tx" and for "dfx".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
4352 && (t_cmd || cap->oap->op_type != OP_NOP))
4353 {
4354 colnr_T scol, ecol;
4355
4356 getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
4357 curwin->w_cursor.coladd = ecol - scol;
4358 }
4359 else
4360 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362#ifdef FEAT_FOLDING
4363 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
4364 foldOpenCursor();
4365#endif
4366 }
4367}
4368
4369/*
4370 * "[" and "]" commands.
4371 * cap->arg is BACKWARD for "[" and FORWARD for "]".
4372 */
4373 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004374nv_brackets(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375{
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01004376 pos_T new_pos = {0, 0, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 pos_T prev_pos;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004378 pos_T *pos = NULL; // init for GCC
4379 pos_T old_pos; // cursor position before command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380 int flag;
4381 long n;
4382 int findc;
4383 int c;
4384
4385 cap->oap->motion_type = MCHAR;
4386 cap->oap->inclusive = FALSE;
4387 old_pos = curwin->w_cursor;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004388 curwin->w_cursor.coladd = 0; // TODO: don't do this for an error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389
4390#ifdef FEAT_SEARCHPATH
4391 /*
4392 * "[f" or "]f" : Edit file under the cursor (same as "gf")
4393 */
4394 if (cap->nchar == 'f')
4395 nv_gotofile(cap);
4396 else
4397#endif
4398
4399#ifdef FEAT_FIND_ID
4400 /*
Bram Moolenaarf711faf2007-05-10 16:48:19 +00004401 * Find the occurrence(s) of the identifier or define under cursor
4402 * in current and included files or jump to the first occurrence.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403 *
4404 * search list jump
4405 * fwd bwd fwd bwd fwd bwd
4406 * identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
4407 * define "]d" "[d" "]D" "[D" "]^D" "[^D"
4408 */
4409 if (vim_strchr((char_u *)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004410# ifdef EBCDIC
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411 "iI\005dD\067",
Bram Moolenaar32526b32019-01-19 17:43:09 +01004412# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413 "iI\011dD\004",
Bram Moolenaar32526b32019-01-19 17:43:09 +01004414# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 cap->nchar) != NULL)
4416 {
4417 char_u *ptr;
4418 int len;
4419
4420 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
4421 clearop(cap->oap);
4422 else
4423 {
4424 find_pattern_in_path(ptr, 0, len, TRUE,
4425 cap->count0 == 0 ? !isupper(cap->nchar) : FALSE,
4426 ((cap->nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
4427 cap->count1,
4428 isupper(cap->nchar) ? ACTION_SHOW_ALL :
4429 islower(cap->nchar) ? ACTION_SHOW : ACTION_GOTO,
4430 cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 : (linenr_T)1,
4431 (linenr_T)MAXLNUM);
4432 curwin->w_set_curswant = TRUE;
4433 }
4434 }
4435 else
4436#endif
4437
4438 /*
4439 * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
4440 * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
4441 * "[/", "[*", "]/", "]*": go to Nth comment start/end.
4442 * "[m" or "]m" search for prev/next start of (Java) method.
4443 * "[M" or "]M" search for prev/next end of (Java) method.
4444 */
4445 if ( (cap->cmdchar == '['
4446 && vim_strchr((char_u *)"{(*/#mM", cap->nchar) != NULL)
4447 || (cap->cmdchar == ']'
4448 && vim_strchr((char_u *)"})*/#mM", cap->nchar) != NULL))
4449 {
4450 if (cap->nchar == '*')
4451 cap->nchar = '/';
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452 prev_pos.lnum = 0;
4453 if (cap->nchar == 'm' || cap->nchar == 'M')
4454 {
4455 if (cap->cmdchar == '[')
4456 findc = '{';
4457 else
4458 findc = '}';
4459 n = 9999;
4460 }
4461 else
4462 {
4463 findc = cap->nchar;
4464 n = cap->count1;
4465 }
4466 for ( ; n > 0; --n)
4467 {
4468 if ((pos = findmatchlimit(cap->oap, findc,
4469 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
4470 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004471 if (new_pos.lnum == 0) // nothing found
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472 {
4473 if (cap->nchar != 'm' && cap->nchar != 'M')
4474 clearopbeep(cap->oap);
4475 }
4476 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004477 pos = &new_pos; // use last one found
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478 break;
4479 }
4480 prev_pos = new_pos;
4481 curwin->w_cursor = *pos;
4482 new_pos = *pos;
4483 }
4484 curwin->w_cursor = old_pos;
4485
4486 /*
4487 * Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
4488 * brought us to the match for "[m" and "]M" when inside a method.
4489 * Try finding the '{' or '}' we want to be at.
4490 * Also repeat for the given count.
4491 */
4492 if (cap->nchar == 'm' || cap->nchar == 'M')
4493 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004494 // norm is TRUE for "]M" and "[m"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 int norm = ((findc == '{') == (cap->nchar == 'm'));
4496
4497 n = cap->count1;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004498 // found a match: we were inside a method
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 if (prev_pos.lnum != 0)
4500 {
4501 pos = &prev_pos;
4502 curwin->w_cursor = prev_pos;
4503 if (norm)
4504 --n;
4505 }
4506 else
4507 pos = NULL;
4508 while (n > 0)
4509 {
4510 for (;;)
4511 {
4512 if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0)
4513 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004514 // if not found anything, that's an error
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515 if (pos == NULL)
4516 clearopbeep(cap->oap);
4517 n = 0;
4518 break;
4519 }
4520 c = gchar_cursor();
4521 if (c == '{' || c == '}')
4522 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004523 // Must have found end/start of class: use it.
4524 // Or found the place to be at.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 if ((c == findc && norm) || (n == 1 && !norm))
4526 {
4527 new_pos = curwin->w_cursor;
4528 pos = &new_pos;
4529 n = 0;
4530 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004531 // if no match found at all, we started outside of the
4532 // class and we're inside now. Just go on.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004533 else if (new_pos.lnum == 0)
4534 {
4535 new_pos = curwin->w_cursor;
4536 pos = &new_pos;
4537 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004538 // found start/end of other method: go to match
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539 else if ((pos = findmatchlimit(cap->oap, findc,
4540 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
4541 0)) == NULL)
4542 n = 0;
4543 else
4544 curwin->w_cursor = *pos;
4545 break;
4546 }
4547 }
4548 --n;
4549 }
4550 curwin->w_cursor = old_pos;
4551 if (pos == NULL && new_pos.lnum != 0)
4552 clearopbeep(cap->oap);
4553 }
4554 if (pos != NULL)
4555 {
4556 setpcmark();
4557 curwin->w_cursor = *pos;
4558 curwin->w_set_curswant = TRUE;
4559#ifdef FEAT_FOLDING
4560 if ((fdo_flags & FDO_BLOCK) && KeyTyped
4561 && cap->oap->op_type == OP_NOP)
4562 foldOpenCursor();
4563#endif
4564 }
4565 }
4566
4567 /*
4568 * "[[", "[]", "]]" and "][": move to start or end of function
4569 */
4570 else if (cap->nchar == '[' || cap->nchar == ']')
4571 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004572 if (cap->nchar == cap->cmdchar) // "]]" or "[["
Bram Moolenaar071d4272004-06-13 20:20:40 +00004573 flag = '{';
4574 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004575 flag = '}'; // "][" or "[]"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576
4577 curwin->w_set_curswant = TRUE;
4578 /*
4579 * Imitate strange Vi behaviour: When using "]]" with an operator
4580 * we also stop at '}'.
4581 */
Bram Moolenaar8b96d642005-09-05 22:05:30 +00004582 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004583 (cap->oap->op_type != OP_NOP
4584 && cap->arg == FORWARD && flag == '{')))
4585 clearopbeep(cap->oap);
4586 else
4587 {
4588 if (cap->oap->op_type == OP_NOP)
4589 beginline(BL_WHITE | BL_FIX);
4590#ifdef FEAT_FOLDING
4591 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4592 foldOpenCursor();
4593#endif
4594 }
4595 }
4596
4597 /*
4598 * "[p", "[P", "]P" and "]p": put with indent adjustment
4599 */
4600 else if (cap->nchar == 'p' || cap->nchar == 'P')
4601 {
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02004602 nv_put_opt(cap, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004603 }
4604
4605 /*
4606 * "['", "[`", "]'" and "]`": jump to next mark
4607 */
4608 else if (cap->nchar == '\'' || cap->nchar == '`')
4609 {
4610 pos = &curwin->w_cursor;
4611 for (n = cap->count1; n > 0; --n)
4612 {
4613 prev_pos = *pos;
4614 pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
4615 cap->nchar == '\'');
4616 if (pos == NULL)
4617 break;
4618 }
4619 if (pos == NULL)
4620 pos = &prev_pos;
4621 nv_cursormark(cap, cap->nchar == '\'', pos);
4622 }
4623
Bram Moolenaar071d4272004-06-13 20:20:40 +00004624 /*
4625 * [ or ] followed by a middle mouse click: put selected text with
4626 * indent adjustment. Any other button just does as usual.
4627 */
Bram Moolenaar5ea0ac72010-05-07 15:52:08 +02004628 else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629 {
4630 (void)do_mouse(cap->oap, cap->nchar,
4631 (cap->cmdchar == ']') ? FORWARD : BACKWARD,
4632 cap->count1, PUT_FIXINDENT);
4633 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634
4635#ifdef FEAT_FOLDING
4636 /*
4637 * "[z" and "]z": move to start or end of open fold.
4638 */
4639 else if (cap->nchar == 'z')
4640 {
4641 if (foldMoveTo(FALSE, cap->cmdchar == ']' ? FORWARD : BACKWARD,
4642 cap->count1) == FAIL)
4643 clearopbeep(cap->oap);
4644 }
4645#endif
4646
4647#ifdef FEAT_DIFF
4648 /*
4649 * "[c" and "]c": move to next or previous diff-change.
4650 */
4651 else if (cap->nchar == 'c')
4652 {
4653 if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
4654 cap->count1) == FAIL)
4655 clearopbeep(cap->oap);
4656 }
4657#endif
4658
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00004659#ifdef FEAT_SPELL
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00004660 /*
4661 * "[s", "[S", "]s" and "]S": move to next spell error.
4662 */
4663 else if (cap->nchar == 's' || cap->nchar == 'S')
4664 {
Bram Moolenaar2cf8b302005-04-20 19:37:22 +00004665 setpcmark();
4666 for (n = 0; n < cap->count1; ++n)
Bram Moolenaar95529562005-08-25 21:21:38 +00004667 if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
4668 cap->nchar == 's' ? TRUE : FALSE, FALSE, NULL) == 0)
Bram Moolenaar2cf8b302005-04-20 19:37:22 +00004669 {
4670 clearopbeep(cap->oap);
4671 break;
4672 }
Bram Moolenaarb73fa622017-12-21 20:27:47 +01004673 else
4674 curwin->w_set_curswant = TRUE;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004675# ifdef FEAT_FOLDING
4676 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
4677 foldOpenCursor();
4678# endif
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00004679 }
4680#endif
4681
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004682 // Not a valid cap->nchar.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 else
4684 clearopbeep(cap->oap);
4685}
4686
4687/*
4688 * Handle Normal mode "%" command.
4689 */
4690 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004691nv_percent(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692{
4693 pos_T *pos;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02004694#if defined(FEAT_FOLDING)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 linenr_T lnum = curwin->w_cursor.lnum;
4696#endif
4697
4698 cap->oap->inclusive = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004699 if (cap->count0) // {cnt}% : goto {cnt} percentage in file
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 {
4701 if (cap->count0 > 100)
4702 clearopbeep(cap->oap);
4703 else
4704 {
4705 cap->oap->motion_type = MLINE;
4706 setpcmark();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004707 // Round up, so CTRL-G will give same value. Watch out for a
4708 // large line count, the line number must not go negative!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004709 if (curbuf->b_ml.ml_line_count > 1000000)
4710 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99L)
4711 / 100L * cap->count0;
4712 else
4713 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
4714 cap->count0 + 99L) / 100L;
4715 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
4716 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
4717 beginline(BL_SOL | BL_FIX);
4718 }
4719 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004720 else // "%" : go to matching paren
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721 {
4722 cap->oap->motion_type = MCHAR;
4723 cap->oap->use_reg_one = TRUE;
4724 if ((pos = findmatch(cap->oap, NUL)) == NULL)
4725 clearopbeep(cap->oap);
4726 else
4727 {
4728 setpcmark();
4729 curwin->w_cursor = *pos;
4730 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 }
4734 }
4735#ifdef FEAT_FOLDING
4736 if (cap->oap->op_type == OP_NOP
4737 && lnum != curwin->w_cursor.lnum
4738 && (fdo_flags & FDO_PERCENT)
4739 && KeyTyped)
4740 foldOpenCursor();
4741#endif
4742}
4743
4744/*
4745 * Handle "(" and ")" commands.
4746 * cap->arg is BACKWARD for "(" and FORWARD for ")".
4747 */
4748 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004749nv_brace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750{
4751 cap->oap->motion_type = MCHAR;
4752 cap->oap->use_reg_one = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004753 // The motion used to be inclusive for "(", but that is not what Vi does.
Bram Moolenaarebefac62005-12-28 22:39:57 +00004754 cap->oap->inclusive = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004755 curwin->w_set_curswant = TRUE;
4756
4757 if (findsent(cap->arg, cap->count1) == FAIL)
4758 clearopbeep(cap->oap);
4759 else
4760 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004761 // Don't leave the cursor on the NUL past end of line.
Bram Moolenaar1f14d572008-01-12 16:12:10 +00004762 adjust_cursor(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004763 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764#ifdef FEAT_FOLDING
4765 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4766 foldOpenCursor();
4767#endif
4768 }
4769}
4770
4771/*
4772 * "m" command: Mark a position.
4773 */
4774 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004775nv_mark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776{
4777 if (!checkclearop(cap->oap))
4778 {
4779 if (setmark(cap->nchar) == FAIL)
4780 clearopbeep(cap->oap);
4781 }
4782}
4783
4784/*
4785 * "{" and "}" commands.
4786 * cmd->arg is BACKWARD for "{" and FORWARD for "}".
4787 */
4788 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004789nv_findpar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004790{
4791 cap->oap->motion_type = MCHAR;
4792 cap->oap->inclusive = FALSE;
4793 cap->oap->use_reg_one = TRUE;
4794 curwin->w_set_curswant = TRUE;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00004795 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 clearopbeep(cap->oap);
4797 else
4798 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800#ifdef FEAT_FOLDING
4801 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4802 foldOpenCursor();
4803#endif
4804 }
4805}
4806
4807/*
4808 * "u" command: Undo or make lower case.
4809 */
4810 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004811nv_undo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004812{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01004813 if (cap->oap->op_type == OP_LOWER || VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004814 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004815 // translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816 cap->cmdchar = 'g';
4817 cap->nchar = 'u';
4818 nv_operator(cap);
4819 }
4820 else
4821 nv_kundo(cap);
4822}
4823
4824/*
4825 * <Undo> command.
4826 */
4827 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004828nv_kundo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004829{
4830 if (!checkclearopq(cap->oap))
4831 {
Bram Moolenaarf2732452018-06-03 14:47:35 +02004832#ifdef FEAT_JOB_CHANNEL
4833 if (bt_prompt(curbuf))
4834 {
4835 clearopbeep(cap->oap);
4836 return;
4837 }
4838#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004839 u_undo((int)cap->count1);
4840 curwin->w_set_curswant = TRUE;
4841 }
4842}
4843
4844/*
4845 * Handle the "r" command.
4846 */
4847 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004848nv_replace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004849{
4850 char_u *ptr;
4851 int had_ctrl_v;
4852 long n;
4853
4854 if (checkclearop(cap->oap))
4855 return;
Bram Moolenaarf2732452018-06-03 14:47:35 +02004856#ifdef FEAT_JOB_CHANNEL
4857 if (bt_prompt(curbuf) && !prompt_curpos_editable())
4858 {
4859 clearopbeep(cap->oap);
4860 return;
4861 }
4862#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004863
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004864 // get another character
Bram Moolenaar071d4272004-06-13 20:20:40 +00004865 if (cap->nchar == Ctrl_V)
4866 {
4867 had_ctrl_v = Ctrl_V;
4868 cap->nchar = get_literal();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004869 // Don't redo a multibyte character with CTRL-V.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870 if (cap->nchar > DEL)
4871 had_ctrl_v = NUL;
4872 }
4873 else
4874 had_ctrl_v = NUL;
4875
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004876 // Abort if the character is a special key.
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +00004877 if (IS_SPECIAL(cap->nchar))
4878 {
4879 clearopbeep(cap->oap);
4880 return;
4881 }
4882
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004883 // Visual mode "r"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 if (VIsual_active)
4885 {
Bram Moolenaar57fb0da2009-02-04 10:46:25 +00004886 if (got_int)
4887 reset_VIsual();
Bram Moolenaard9820532013-11-04 01:41:17 +01004888 if (had_ctrl_v)
4889 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004890 // Use a special (negative) number to make a difference between a
4891 // literal CR or NL and a line break.
Bram Moolenaarf12519d2018-02-06 22:52:49 +01004892 if (cap->nchar == CAR)
4893 cap->nchar = REPLACE_CR_NCHAR;
4894 else if (cap->nchar == NL)
4895 cap->nchar = REPLACE_NL_NCHAR;
Bram Moolenaard9820532013-11-04 01:41:17 +01004896 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897 nv_operator(cap);
4898 return;
4899 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004901 // Break tabs, etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902 if (virtual_active())
4903 {
4904 if (u_save_cursor() == FAIL)
4905 return;
4906 if (gchar_cursor() == NUL)
4907 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004908 // Add extra space and put the cursor on the first one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 coladvance_force((colnr_T)(getviscol() + cap->count1));
4910 curwin->w_cursor.col -= cap->count1;
4911 }
4912 else if (gchar_cursor() == TAB)
4913 coladvance_force(getviscol());
4914 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004916 // Abort if not enough characters to replace.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917 ptr = ml_get_cursor();
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +00004918 if (STRLEN(ptr) < (unsigned)cap->count1
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01004919 || (has_mbyte && mb_charlen(ptr) < cap->count1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004920 {
4921 clearopbeep(cap->oap);
4922 return;
4923 }
4924
4925 /*
4926 * Replacing with a TAB is done by edit() when it is complicated because
4927 * 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB.
4928 * Other characters are done below to avoid problems with things like
4929 * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
4930 */
4931 if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
4932 {
4933 stuffnumReadbuff(cap->count1);
4934 stuffcharReadbuff('R');
4935 stuffcharReadbuff('\t');
4936 stuffcharReadbuff(ESC);
4937 return;
4938 }
4939
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004940 // save line for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 if (u_save_cursor() == FAIL)
4942 return;
4943
4944 if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n'))
4945 {
4946 /*
4947 * Replace character(s) by a single newline.
4948 * Strange vi behaviour: Only one newline is inserted.
4949 * Delete the characters here.
4950 * Insert the newline with an insert command, takes care of
4951 * autoindent. The insert command depends on being on the last
4952 * character of a line or not.
4953 */
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004954 (void)del_chars(cap->count1, FALSE); // delete the characters
Bram Moolenaar071d4272004-06-13 20:20:40 +00004955 stuffcharReadbuff('\r');
4956 stuffcharReadbuff(ESC);
4957
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004958 // Give 'r' to edit(), to get the redo command right.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004959 invoke_edit(cap, TRUE, 'r', FALSE);
4960 }
4961 else
4962 {
4963 prep_redo(cap->oap->regname, cap->count1,
4964 NUL, 'r', NUL, had_ctrl_v, cap->nchar);
4965
4966 curbuf->b_op_start = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004967 if (has_mbyte)
4968 {
4969 int old_State = State;
4970
4971 if (cap->ncharC1 != 0)
4972 AppendCharToRedobuff(cap->ncharC1);
4973 if (cap->ncharC2 != 0)
4974 AppendCharToRedobuff(cap->ncharC2);
4975
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004976 // This is slow, but it handles replacing a single-byte with a
4977 // multi-byte and the other way around. Also handles adding
4978 // composing characters for utf-8.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004979 for (n = cap->count1; n > 0; --n)
4980 {
4981 State = REPLACE;
Bram Moolenaar8320da42012-04-30 18:18:47 +02004982 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
4983 {
4984 int c = ins_copychar(curwin->w_cursor.lnum
4985 + (cap->nchar == Ctrl_Y ? -1 : 1));
4986 if (c != NUL)
4987 ins_char(c);
4988 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004989 // will be decremented further down
Bram Moolenaar8320da42012-04-30 18:18:47 +02004990 ++curwin->w_cursor.col;
4991 }
4992 else
4993 ins_char(cap->nchar);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994 State = old_State;
4995 if (cap->ncharC1 != 0)
4996 ins_char(cap->ncharC1);
4997 if (cap->ncharC2 != 0)
4998 ins_char(cap->ncharC2);
4999 }
5000 }
5001 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002 {
5003 /*
5004 * Replace the characters within one line.
5005 */
5006 for (n = cap->count1; n > 0; --n)
5007 {
5008 /*
5009 * Get ptr again, because u_save and/or showmatch() will have
5010 * released the line. At the same time we let know that the
5011 * line will be changed.
5012 */
5013 ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
Bram Moolenaar8320da42012-04-30 18:18:47 +02005014 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
5015 {
5016 int c = ins_copychar(curwin->w_cursor.lnum
5017 + (cap->nchar == Ctrl_Y ? -1 : 1));
5018 if (c != NUL)
5019 ptr[curwin->w_cursor.col] = c;
5020 }
5021 else
5022 ptr[curwin->w_cursor.col] = cap->nchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005023 if (p_sm && msg_silent == 0)
5024 showmatch(cap->nchar);
5025 ++curwin->w_cursor.col;
5026 }
5027#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005028 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005029 {
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005030 colnr_T start = (colnr_T)(curwin->w_cursor.col - cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005031
Bram Moolenaar009b2592004-10-24 19:18:58 +00005032 netbeans_removed(curbuf, curwin->w_cursor.lnum, start,
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005033 (long)cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005034 netbeans_inserted(curbuf, curwin->w_cursor.lnum, start,
Bram Moolenaar009b2592004-10-24 19:18:58 +00005035 &ptr[start], (int)cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036 }
5037#endif
5038
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005039 // mark the buffer as changed and prepare for displaying
Bram Moolenaar071d4272004-06-13 20:20:40 +00005040 changed_bytes(curwin->w_cursor.lnum,
5041 (colnr_T)(curwin->w_cursor.col - cap->count1));
5042 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005043 --curwin->w_cursor.col; // cursor on the last replaced char
5044 // if the character on the left of the current cursor is a multi-byte
5045 // character, move two characters left
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046 if (has_mbyte)
5047 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005048 curbuf->b_op_end = curwin->w_cursor;
5049 curwin->w_set_curswant = TRUE;
5050 set_last_insert(cap->nchar);
5051 }
5052}
5053
Bram Moolenaar071d4272004-06-13 20:20:40 +00005054/*
5055 * 'o': Exchange start and end of Visual area.
5056 * 'O': same, but in block mode exchange left and right corners.
5057 */
5058 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005059v_swap_corners(int cmdchar)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060{
5061 pos_T old_cursor;
5062 colnr_T left, right;
5063
5064 if (cmdchar == 'O' && VIsual_mode == Ctrl_V)
5065 {
5066 old_cursor = curwin->w_cursor;
5067 getvcols(curwin, &old_cursor, &VIsual, &left, &right);
5068 curwin->w_cursor.lnum = VIsual.lnum;
5069 coladvance(left);
5070 VIsual = curwin->w_cursor;
5071
5072 curwin->w_cursor.lnum = old_cursor.lnum;
5073 curwin->w_curswant = right;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005074 // 'selection "exclusive" and cursor at right-bottom corner: move it
5075 // right one column
Bram Moolenaar071d4272004-06-13 20:20:40 +00005076 if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e')
5077 ++curwin->w_curswant;
5078 coladvance(curwin->w_curswant);
5079 if (curwin->w_cursor.col == old_cursor.col
Bram Moolenaar071d4272004-06-13 20:20:40 +00005080 && (!virtual_active()
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01005081 || curwin->w_cursor.coladd == old_cursor.coladd))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005082 {
5083 curwin->w_cursor.lnum = VIsual.lnum;
5084 if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e')
5085 ++right;
5086 coladvance(right);
5087 VIsual = curwin->w_cursor;
5088
5089 curwin->w_cursor.lnum = old_cursor.lnum;
5090 coladvance(left);
5091 curwin->w_curswant = left;
5092 }
5093 }
5094 else
5095 {
5096 old_cursor = curwin->w_cursor;
5097 curwin->w_cursor = VIsual;
5098 VIsual = old_cursor;
5099 curwin->w_set_curswant = TRUE;
5100 }
5101}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005102
5103/*
5104 * "R" (cap->arg is FALSE) and "gR" (cap->arg is TRUE).
5105 */
5106 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005107nv_Replace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005109 if (VIsual_active) // "R" is replace lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 {
5111 cap->cmdchar = 'c';
5112 cap->nchar = NUL;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005113 VIsual_mode_orig = VIsual_mode; // remember original area for gv
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 VIsual_mode = 'V';
5115 nv_operator(cap);
5116 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01005117 else if (!checkclearopq(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118 {
5119 if (!curbuf->b_p_ma)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005120 emsg(_(e_modifiable));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121 else
5122 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005123 if (virtual_active())
5124 coladvance(getviscol());
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 invoke_edit(cap, FALSE, cap->arg ? 'V' : 'R', FALSE);
5126 }
5127 }
5128}
5129
Bram Moolenaar071d4272004-06-13 20:20:40 +00005130/*
5131 * "gr".
5132 */
5133 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005134nv_vreplace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005136 if (VIsual_active)
5137 {
5138 cap->cmdchar = 'r';
5139 cap->nchar = cap->extra_char;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005140 nv_replace(cap); // Do same as "r" in Visual mode for now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005141 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01005142 else if (!checkclearopq(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005143 {
5144 if (!curbuf->b_p_ma)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005145 emsg(_(e_modifiable));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146 else
5147 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005148 if (cap->extra_char == Ctrl_V) // get another character
Bram Moolenaar071d4272004-06-13 20:20:40 +00005149 cap->extra_char = get_literal();
5150 stuffcharReadbuff(cap->extra_char);
5151 stuffcharReadbuff(ESC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152 if (virtual_active())
5153 coladvance(getviscol());
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154 invoke_edit(cap, TRUE, 'v', FALSE);
5155 }
5156 }
5157}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158
5159/*
5160 * Swap case for "~" command, when it does not work like an operator.
5161 */
5162 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005163n_swapchar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005164{
5165 long n;
5166 pos_T startpos;
5167 int did_change = 0;
5168#ifdef FEAT_NETBEANS_INTG
5169 pos_T pos;
5170 char_u *ptr;
5171 int count;
5172#endif
5173
5174 if (checkclearopq(cap->oap))
5175 return;
5176
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01005177 if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178 {
5179 clearopbeep(cap->oap);
5180 return;
5181 }
5182
5183 prep_redo_cmd(cap);
5184
5185 if (u_save_cursor() == FAIL)
5186 return;
5187
5188 startpos = curwin->w_cursor;
5189#ifdef FEAT_NETBEANS_INTG
5190 pos = startpos;
5191#endif
5192 for (n = cap->count1; n > 0; --n)
5193 {
5194 did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
5195 inc_cursor();
5196 if (gchar_cursor() == NUL)
5197 {
5198 if (vim_strchr(p_ww, '~') != NULL
5199 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
5200 {
5201#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005202 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005203 {
5204 if (did_change)
5205 {
5206 ptr = ml_get(pos.lnum);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005207 count = (int)STRLEN(ptr) - pos.col;
Bram Moolenaar009b2592004-10-24 19:18:58 +00005208 netbeans_removed(curbuf, pos.lnum, pos.col,
5209 (long)count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005210 netbeans_inserted(curbuf, pos.lnum, pos.col,
Bram Moolenaar009b2592004-10-24 19:18:58 +00005211 &ptr[pos.col], count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212 }
5213 pos.col = 0;
5214 pos.lnum++;
5215 }
5216#endif
5217 ++curwin->w_cursor.lnum;
5218 curwin->w_cursor.col = 0;
5219 if (n > 1)
5220 {
5221 if (u_savesub(curwin->w_cursor.lnum) == FAIL)
5222 break;
5223 u_clearline();
5224 }
5225 }
5226 else
5227 break;
5228 }
5229 }
5230#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005231 if (did_change && netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005232 {
5233 ptr = ml_get(pos.lnum);
5234 count = curwin->w_cursor.col - pos.col;
Bram Moolenaar009b2592004-10-24 19:18:58 +00005235 netbeans_removed(curbuf, pos.lnum, pos.col, (long)count);
5236 netbeans_inserted(curbuf, pos.lnum, pos.col, &ptr[pos.col], count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005237 }
5238#endif
5239
5240
5241 check_cursor();
5242 curwin->w_set_curswant = TRUE;
5243 if (did_change)
5244 {
5245 changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1,
5246 0L);
5247 curbuf->b_op_start = startpos;
5248 curbuf->b_op_end = curwin->w_cursor;
5249 if (curbuf->b_op_end.col > 0)
5250 --curbuf->b_op_end.col;
5251 }
5252}
5253
5254/*
5255 * Move cursor to mark.
5256 */
5257 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005258nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005259{
5260 if (check_mark(pos) == FAIL)
5261 clearop(cap->oap);
5262 else
5263 {
5264 if (cap->cmdchar == '\''
5265 || cap->cmdchar == '`'
5266 || cap->cmdchar == '['
5267 || cap->cmdchar == ']')
5268 setpcmark();
5269 curwin->w_cursor = *pos;
5270 if (flag)
5271 beginline(BL_WHITE | BL_FIX);
5272 else
5273 check_cursor();
5274 }
5275 cap->oap->motion_type = flag ? MLINE : MCHAR;
5276 if (cap->cmdchar == '`')
5277 cap->oap->use_reg_one = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005278 cap->oap->inclusive = FALSE; // ignored if not MCHAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00005279 curwin->w_set_curswant = TRUE;
5280}
5281
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282/*
5283 * Handle commands that are operators in Visual mode.
5284 */
5285 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005286v_visop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005287{
5288 static char_u trans[] = "YyDdCcxdXdAAIIrr";
5289
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005290 // Uppercase means linewise, except in block mode, then "D" deletes till
5291 // the end of the line, and "C" replaces till EOL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005292 if (isupper(cap->cmdchar))
5293 {
5294 if (VIsual_mode != Ctrl_V)
Bram Moolenaara390bb62013-03-13 19:02:41 +01005295 {
5296 VIsual_mode_orig = VIsual_mode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005297 VIsual_mode = 'V';
Bram Moolenaara390bb62013-03-13 19:02:41 +01005298 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005299 else if (cap->cmdchar == 'C' || cap->cmdchar == 'D')
5300 curwin->w_curswant = MAXCOL;
5301 }
5302 cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
5303 nv_operator(cap);
5304}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005305
5306/*
5307 * "s" and "S" commands.
5308 */
5309 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005310nv_subst(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005311{
Bram Moolenaard96ff162018-02-18 22:13:29 +01005312#ifdef FEAT_TERMINAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005313 // When showing output of term_dumpdiff() swap the top and botom.
Bram Moolenaard96ff162018-02-18 22:13:29 +01005314 if (term_swap_diff() == OK)
5315 return;
5316#endif
Bram Moolenaarf2732452018-06-03 14:47:35 +02005317#ifdef FEAT_JOB_CHANNEL
5318 if (bt_prompt(curbuf) && !prompt_curpos_editable())
5319 {
5320 clearopbeep(cap->oap);
5321 return;
5322 }
5323#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005324 if (VIsual_active) // "vs" and "vS" are the same as "vc"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005325 {
5326 if (cap->cmdchar == 'S')
Bram Moolenaara390bb62013-03-13 19:02:41 +01005327 {
5328 VIsual_mode_orig = VIsual_mode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 VIsual_mode = 'V';
Bram Moolenaara390bb62013-03-13 19:02:41 +01005330 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005331 cap->cmdchar = 'c';
5332 nv_operator(cap);
5333 }
5334 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005335 nv_optrans(cap);
5336}
5337
5338/*
5339 * Abbreviated commands.
5340 */
5341 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005342nv_abbrev(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005343{
5344 if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005345 cap->cmdchar = 'x'; // DEL key behaves like 'x'
Bram Moolenaar071d4272004-06-13 20:20:40 +00005346
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005347 // in Visual mode these commands are operators
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348 if (VIsual_active)
5349 v_visop(cap);
5350 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 nv_optrans(cap);
5352}
5353
5354/*
5355 * Translate a command into another command.
5356 */
5357 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005358nv_optrans(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005359{
5360 static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
5361 (char_u *)"d$", (char_u *)"c$",
5362 (char_u *)"cl", (char_u *)"cc",
5363 (char_u *)"yy", (char_u *)":s\r"};
5364 static char_u *str = (char_u *)"xXDCsSY&";
5365
5366 if (!checkclearopq(cap->oap))
5367 {
Bram Moolenaar7a9bd7c2019-09-17 22:42:55 +02005368 // In Vi "2D" doesn't delete the next line. Can't translate it
5369 // either, because "2." should also not use the count.
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005370 if (cap->cmdchar == 'D' && vim_strchr(p_cpo, CPO_HASH) != NULL)
5371 {
5372 cap->oap->start = curwin->w_cursor;
5373 cap->oap->op_type = OP_DELETE;
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00005374#ifdef FEAT_EVAL
5375 set_op_var(OP_DELETE);
5376#endif
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005377 cap->count1 = 1;
5378 nv_dollar(cap);
5379 finish_op = TRUE;
5380 ResetRedobuff();
5381 AppendCharToRedobuff('D');
5382 }
5383 else
5384 {
5385 if (cap->count0)
5386 stuffnumReadbuff(cap->count0);
Bram Moolenaar56ebbab2019-09-20 13:40:14 +02005387 stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]);
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005388 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005389 }
5390 cap->opcount = 0;
5391}
5392
5393/*
5394 * "'" and "`" commands. Also for "g'" and "g`".
5395 * cap->arg is TRUE for "'" and "g'".
5396 */
5397 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005398nv_gomark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005399{
5400 pos_T *pos;
5401 int c;
5402#ifdef FEAT_FOLDING
Bram Moolenaar8754deb2013-01-17 13:24:08 +01005403 pos_T old_cursor = curwin->w_cursor;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005404 int old_KeyTyped = KeyTyped; // getting file may reset it
Bram Moolenaar071d4272004-06-13 20:20:40 +00005405#endif
5406
5407 if (cap->cmdchar == 'g')
5408 c = cap->extra_char;
5409 else
5410 c = cap->nchar;
5411 pos = getmark(c, (cap->oap->op_type == OP_NOP));
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005412 if (pos == (pos_T *)-1) // jumped to other file
Bram Moolenaar071d4272004-06-13 20:20:40 +00005413 {
5414 if (cap->arg)
5415 {
5416 check_cursor_lnum();
5417 beginline(BL_WHITE | BL_FIX);
5418 }
5419 else
5420 check_cursor();
5421 }
5422 else
5423 nv_cursormark(cap, cap->arg, pos);
5424
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005425 // May need to clear the coladd that a mark includes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005426 if (!virtual_active())
5427 curwin->w_cursor.coladd = 0;
Bram Moolenaar9aa15692017-08-19 15:05:32 +02005428 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005429#ifdef FEAT_FOLDING
5430 if (cap->oap->op_type == OP_NOP
Bram Moolenaar15364d72013-01-24 21:00:20 +01005431 && pos != NULL
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01005432 && (pos == (pos_T *)-1 || !EQUAL_POS(old_cursor, *pos))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 && (fdo_flags & FDO_MARK)
5434 && old_KeyTyped)
5435 foldOpenCursor();
5436#endif
5437}
5438
5439/*
5440 * Handle CTRL-O, CTRL-I, "g;" and "g," commands.
5441 */
5442 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005443nv_pcmark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005444{
5445#ifdef FEAT_JUMPLIST
5446 pos_T *pos;
5447# ifdef FEAT_FOLDING
5448 linenr_T lnum = curwin->w_cursor.lnum;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005449 int old_KeyTyped = KeyTyped; // getting file may reset it
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450# endif
5451
5452 if (!checkclearopq(cap->oap))
5453 {
5454 if (cap->cmdchar == 'g')
5455 pos = movechangelist((int)cap->count1);
5456 else
5457 pos = movemark((int)cap->count1);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005458 if (pos == (pos_T *)-1) // jump to other file
Bram Moolenaar071d4272004-06-13 20:20:40 +00005459 {
5460 curwin->w_set_curswant = TRUE;
5461 check_cursor();
5462 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005463 else if (pos != NULL) // can jump
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464 nv_cursormark(cap, FALSE, pos);
5465 else if (cap->cmdchar == 'g')
5466 {
5467 if (curbuf->b_changelistlen == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005468 emsg(_("E664: changelist is empty"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005469 else if (cap->count1 < 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005470 emsg(_("E662: At start of changelist"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005471 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005472 emsg(_("E663: At end of changelist"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005473 }
5474 else
5475 clearopbeep(cap->oap);
5476# ifdef FEAT_FOLDING
5477 if (cap->oap->op_type == OP_NOP
5478 && (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum)
5479 && (fdo_flags & FDO_MARK)
5480 && old_KeyTyped)
5481 foldOpenCursor();
5482# endif
5483 }
5484#else
5485 clearopbeep(cap->oap);
5486#endif
5487}
5488
5489/*
5490 * Handle '"' command.
5491 */
5492 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005493nv_regname(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494{
5495 if (checkclearop(cap->oap))
5496 return;
5497#ifdef FEAT_EVAL
5498 if (cap->nchar == '=')
5499 cap->nchar = get_expr_register();
5500#endif
5501 if (cap->nchar != NUL && valid_yank_reg(cap->nchar, FALSE))
5502 {
5503 cap->oap->regname = cap->nchar;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005504 cap->opcount = cap->count0; // remember count before '"'
Bram Moolenaar071d4272004-06-13 20:20:40 +00005505#ifdef FEAT_EVAL
5506 set_reg_var(cap->oap->regname);
5507#endif
5508 }
5509 else
5510 clearopbeep(cap->oap);
5511}
5512
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513/*
5514 * Handle "v", "V" and "CTRL-V" commands.
5515 * Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
5516 * is TRUE.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005517 * Handle CTRL-Q just like CTRL-V.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005518 */
5519 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005520nv_visual(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005521{
Bram Moolenaardf177f62005-02-22 08:39:57 +00005522 if (cap->cmdchar == Ctrl_Q)
5523 cap->cmdchar = Ctrl_V;
5524
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005525 // 'v', 'V' and CTRL-V can be used while an operator is pending to make it
5526 // characterwise, linewise, or blockwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527 if (cap->oap->op_type != OP_NOP)
5528 {
Bram Moolenaar5976f8f2018-12-27 23:44:44 +01005529 motion_force = cap->oap->motion_force = cap->cmdchar;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005530 finish_op = FALSE; // operator doesn't finish now but later
Bram Moolenaar071d4272004-06-13 20:20:40 +00005531 return;
5532 }
5533
5534 VIsual_select = cap->arg;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005535 if (VIsual_active) // change Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005537 if (VIsual_mode == cap->cmdchar) // stop visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538 end_visual_mode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005539 else // toggle char/block mode
5540 { // or char/line mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541 VIsual_mode = cap->cmdchar;
5542 showmode();
5543 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005544 redraw_curbuf_later(INVERTED); // update the inversion
Bram Moolenaar071d4272004-06-13 20:20:40 +00005545 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005546 else // start Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547 {
5548 check_visual_highlight();
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005549 if (cap->count0 > 0 && resel_VIsual_mode != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005550 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005551 // use previously selected part
Bram Moolenaar071d4272004-06-13 20:20:40 +00005552 VIsual = curwin->w_cursor;
5553
5554 VIsual_active = TRUE;
5555 VIsual_reselect = TRUE;
5556 if (!cap->arg)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005557 // start Select mode when 'selectmode' contains "cmd"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005558 may_start_select('c');
Bram Moolenaar071d4272004-06-13 20:20:40 +00005559 setmouse();
Bram Moolenaar09df3122006-01-23 22:23:09 +00005560 if (p_smd && msg_silent == 0)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005561 redraw_cmdline = TRUE; // show visual mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00005562 /*
5563 * For V and ^V, we multiply the number of lines even if there
5564 * was only one -- webb
5565 */
5566 if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
5567 {
5568 curwin->w_cursor.lnum +=
5569 resel_VIsual_line_count * cap->count0 - 1;
5570 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
5571 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5572 }
5573 VIsual_mode = resel_VIsual_mode;
5574 if (VIsual_mode == 'v')
5575 {
5576 if (resel_VIsual_line_count <= 1)
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005577 {
5578 validate_virtcol();
5579 curwin->w_curswant = curwin->w_virtcol
5580 + resel_VIsual_vcol * cap->count0 - 1;
5581 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 else
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005583 curwin->w_curswant = resel_VIsual_vcol;
5584 coladvance(curwin->w_curswant);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585 }
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005586 if (resel_VIsual_vcol == MAXCOL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005587 {
5588 curwin->w_curswant = MAXCOL;
5589 coladvance((colnr_T)MAXCOL);
5590 }
5591 else if (VIsual_mode == Ctrl_V)
5592 {
5593 validate_virtcol();
5594 curwin->w_curswant = curwin->w_virtcol
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005595 + resel_VIsual_vcol * cap->count0 - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005596 coladvance(curwin->w_curswant);
5597 }
5598 else
5599 curwin->w_set_curswant = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005600 redraw_curbuf_later(INVERTED); // show the inversion
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601 }
5602 else
5603 {
5604 if (!cap->arg)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005605 // start Select mode when 'selectmode' contains "cmd"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 may_start_select('c');
5607 n_start_visual_mode(cap->cmdchar);
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005608 if (VIsual_mode != 'V' && *p_sel == 'e')
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005609 ++cap->count1; // include one more char
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005610 if (cap->count0 > 0 && --cap->count1 > 0)
5611 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005612 // With a count select that many characters or lines.
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005613 if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V)
5614 nv_right(cap);
5615 else if (VIsual_mode == 'V')
5616 nv_down(cap);
5617 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005618 }
5619 }
5620}
5621
5622/*
5623 * Start selection for Shift-movement keys.
5624 */
5625 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005626start_selection(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005627{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005628 // if 'selectmode' contains "key", start Select mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005629 may_start_select('k');
5630 n_start_visual_mode('v');
5631}
5632
5633/*
5634 * Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
5635 */
5636 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005637may_start_select(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005638{
5639 VIsual_select = (stuff_empty() && typebuf_typed()
5640 && (vim_strchr(p_slm, c) != NULL));
5641}
5642
5643/*
5644 * Start Visual mode "c".
5645 * Should set VIsual_select before calling this.
5646 */
5647 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005648n_start_visual_mode(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005649{
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005650#ifdef FEAT_CONCEAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005651 // Check for redraw before changing the state.
Bram Moolenaarb9464822018-05-10 15:09:49 +02005652 conceal_check_cursor_line();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005653#endif
5654
Bram Moolenaar071d4272004-06-13 20:20:40 +00005655 VIsual_mode = c;
5656 VIsual_active = TRUE;
5657 VIsual_reselect = TRUE;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01005658
5659 // Corner case: the 0 position in a tab may change when going into
Bram Moolenaar4b96df52020-01-26 22:00:26 +01005660 // virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005661 if (c == Ctrl_V && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB)
Bram Moolenaar2dac2132012-08-15 13:31:00 +02005662 {
5663 validate_virtcol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005664 coladvance(curwin->w_virtcol);
Bram Moolenaar2dac2132012-08-15 13:31:00 +02005665 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005666 VIsual = curwin->w_cursor;
5667
5668#ifdef FEAT_FOLDING
5669 foldAdjustVisual();
5670#endif
5671
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672 setmouse();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005673#ifdef FEAT_CONCEAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005674 // Check for redraw after changing the state.
Bram Moolenaarb9464822018-05-10 15:09:49 +02005675 conceal_check_cursor_line();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005676#endif
5677
Bram Moolenaar09df3122006-01-23 22:23:09 +00005678 if (p_smd && msg_silent == 0)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005679 redraw_cmdline = TRUE; // show visual mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680#ifdef FEAT_CLIPBOARD
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005681 // Make sure the clipboard gets updated. Needed because start and
5682 // end may still be the same, and the selection needs to be owned
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 clip_star.vmode = NUL;
5684#endif
5685
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005686 // Only need to redraw this line, unless still need to redraw an old
5687 // Visual area (when 'lazyredraw' is set).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005688 if (curwin->w_redr_type < INVERTED)
5689 {
5690 curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
5691 curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
5692 }
5693}
5694
Bram Moolenaar071d4272004-06-13 20:20:40 +00005695
5696/*
5697 * CTRL-W: Window commands
5698 */
5699 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005700nv_window(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005701{
Bram Moolenaar938783d2017-07-16 20:13:26 +02005702 if (cap->nchar == ':')
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005703 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005704 // "CTRL-W :" is the same as typing ":"; useful in a terminal window
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005705 cap->cmdchar = ':';
5706 cap->nchar = NUL;
Bram Moolenaar938783d2017-07-16 20:13:26 +02005707 nv_colon(cap);
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005708 }
Bram Moolenaar938783d2017-07-16 20:13:26 +02005709 else if (!checkclearop(cap->oap))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005710 do_window(cap->nchar, cap->count0, NUL); // everything is in window.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00005711}
5712
5713/*
5714 * CTRL-Z: Suspend
5715 */
5716 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005717nv_suspend(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005718{
5719 clearop(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720 if (VIsual_active)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005721 end_visual_mode(); // stop Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722 do_cmdline_cmd((char_u *)"st");
5723}
5724
5725/*
5726 * Commands starting with "g".
5727 */
5728 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005729nv_g_cmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005730{
5731 oparg_T *oap = cap->oap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732 pos_T tpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005733 int i;
5734 int flag = FALSE;
5735
5736 switch (cap->nchar)
5737 {
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005738 case Ctrl_A:
5739 case Ctrl_X:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740#ifdef MEM_PROFILE
5741 /*
5742 * "g^A": dump log of used memory.
5743 */
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005744 if (!VIsual_active && cap->nchar == Ctrl_A)
5745 vim_mem_profile_dump();
5746 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747#endif
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005748 /*
5749 * "g^A/g^X": sequentially increment visually selected region
5750 */
5751 if (VIsual_active)
5752 {
5753 cap->arg = TRUE;
5754 cap->cmdchar = cap->nchar;
Bram Moolenaard79e5502016-01-10 22:13:02 +01005755 cap->nchar = NUL;
Bram Moolenaar3a304b22015-06-25 13:57:36 +02005756 nv_addsub(cap);
5757 }
5758 else
5759 clearopbeep(oap);
5760 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005761
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762 /*
5763 * "gR": Enter virtual replace mode.
5764 */
5765 case 'R':
5766 cap->arg = TRUE;
5767 nv_Replace(cap);
5768 break;
5769
5770 case 'r':
5771 nv_vreplace(cap);
5772 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005773
5774 case '&':
5775 do_cmdline_cmd((char_u *)"%s//~/&");
5776 break;
5777
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 /*
5779 * "gv": Reselect the previous Visual area. If Visual already active,
5780 * exchange previous and current Visual area.
5781 */
5782 case 'v':
5783 if (checkclearop(oap))
5784 break;
5785
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005786 if ( curbuf->b_visual.vi_start.lnum == 0
5787 || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
5788 || curbuf->b_visual.vi_end.lnum == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005789 beep_flush();
5790 else
5791 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005792 // set w_cursor to the start of the Visual area, tpos to the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 if (VIsual_active)
5794 {
5795 i = VIsual_mode;
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005796 VIsual_mode = curbuf->b_visual.vi_mode;
5797 curbuf->b_visual.vi_mode = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798# ifdef FEAT_EVAL
5799 curbuf->b_visual_mode_eval = i;
5800# endif
5801 i = curwin->w_curswant;
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005802 curwin->w_curswant = curbuf->b_visual.vi_curswant;
5803 curbuf->b_visual.vi_curswant = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005804
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005805 tpos = curbuf->b_visual.vi_end;
5806 curbuf->b_visual.vi_end = curwin->w_cursor;
5807 curwin->w_cursor = curbuf->b_visual.vi_start;
5808 curbuf->b_visual.vi_start = VIsual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005809 }
5810 else
5811 {
Bram Moolenaara226a6d2006-02-26 23:59:20 +00005812 VIsual_mode = curbuf->b_visual.vi_mode;
5813 curwin->w_curswant = curbuf->b_visual.vi_curswant;
5814 tpos = curbuf->b_visual.vi_end;
5815 curwin->w_cursor = curbuf->b_visual.vi_start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005816 }
5817
5818 VIsual_active = TRUE;
5819 VIsual_reselect = TRUE;
5820
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005821 // Set Visual to the start and w_cursor to the end of the Visual
5822 // area. Make sure they are on an existing character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005823 check_cursor();
5824 VIsual = curwin->w_cursor;
5825 curwin->w_cursor = tpos;
5826 check_cursor();
5827 update_topline();
5828 /*
5829 * When called from normal "g" command: start Select mode when
5830 * 'selectmode' contains "cmd". When called for K_SELECT, always
5831 * start Select mode.
5832 */
5833 if (cap->arg)
5834 VIsual_select = TRUE;
5835 else
5836 may_start_select('c');
Bram Moolenaar071d4272004-06-13 20:20:40 +00005837 setmouse();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838#ifdef FEAT_CLIPBOARD
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005839 // Make sure the clipboard gets updated. Needed because start and
5840 // end are still the same, and the selection needs to be owned
Bram Moolenaar071d4272004-06-13 20:20:40 +00005841 clip_star.vmode = NUL;
5842#endif
5843 redraw_curbuf_later(INVERTED);
5844 showmode();
5845 }
5846 break;
5847 /*
5848 * "gV": Don't reselect the previous Visual area after a Select mode
5849 * mapping of menu.
5850 */
5851 case 'V':
5852 VIsual_reselect = FALSE;
5853 break;
5854
5855 /*
5856 * "gh": start Select mode.
5857 * "gH": start Select line mode.
5858 * "g^H": start Select block mode.
5859 */
5860 case K_BS:
5861 cap->nchar = Ctrl_H;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005862 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00005863 case 'h':
5864 case 'H':
5865 case Ctrl_H:
5866# ifdef EBCDIC
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005867 // EBCDIC: 'v'-'h' != '^v'-'^h'
Bram Moolenaar071d4272004-06-13 20:20:40 +00005868 if (cap->nchar == Ctrl_H)
5869 cap->cmdchar = Ctrl_V;
5870 else
5871# endif
5872 cap->cmdchar = cap->nchar + ('v' - 'h');
5873 cap->arg = TRUE;
5874 nv_visual(cap);
5875 break;
Bram Moolenaar641e2862012-07-25 15:06:34 +02005876
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005877 // "gn", "gN" visually select next/previous search match
5878 // "gn" selects next match
5879 // "gN" selects previous match
Bram Moolenaar641e2862012-07-25 15:06:34 +02005880 case 'N':
5881 case 'n':
5882 if (!current_search(cap->count1, cap->nchar == 'n'))
Bram Moolenaarf00dc262012-10-21 03:54:33 +02005883 clearopbeep(oap);
Bram Moolenaar641e2862012-07-25 15:06:34 +02005884 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005885
5886 /*
5887 * "gj" and "gk" two new funny movement keys -- up and down
5888 * movement based on *screen* line rather than *file* line.
5889 */
5890 case 'j':
5891 case K_DOWN:
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005892 // with 'nowrap' it works just like the normal "j" command; also when
5893 // in a closed fold
Bram Moolenaar071d4272004-06-13 20:20:40 +00005894 if (!curwin->w_p_wrap
5895#ifdef FEAT_FOLDING
5896 || hasFolding(curwin->w_cursor.lnum, NULL, NULL)
5897#endif
5898 )
5899 {
5900 oap->motion_type = MLINE;
5901 i = cursor_down(cap->count1, oap->op_type == OP_NOP);
5902 }
5903 else
5904 i = nv_screengo(oap, FORWARD, cap->count1);
5905 if (i == FAIL)
5906 clearopbeep(oap);
5907 break;
5908
5909 case 'k':
5910 case K_UP:
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005911 // with 'nowrap' it works just like the normal "k" command; also when
5912 // in a closed fold
Bram Moolenaar071d4272004-06-13 20:20:40 +00005913 if (!curwin->w_p_wrap
5914#ifdef FEAT_FOLDING
5915 || hasFolding(curwin->w_cursor.lnum, NULL, NULL)
5916#endif
5917 )
5918 {
5919 oap->motion_type = MLINE;
5920 i = cursor_up(cap->count1, oap->op_type == OP_NOP);
5921 }
5922 else
5923 i = nv_screengo(oap, BACKWARD, cap->count1);
5924 if (i == FAIL)
5925 clearopbeep(oap);
5926 break;
5927
5928 /*
5929 * "gJ": join two lines without inserting a space.
5930 */
5931 case 'J':
5932 nv_join(cap);
5933 break;
5934
5935 /*
5936 * "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
5937 * "gm": middle of "g0" and "g$".
5938 */
5939 case '^':
5940 flag = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005941 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00005942
5943 case '0':
5944 case 'm':
5945 case K_HOME:
5946 case K_KHOME:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005947 oap->motion_type = MCHAR;
5948 oap->inclusive = FALSE;
Bram Moolenaar4033c552017-09-16 20:54:51 +02005949 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950 {
Bram Moolenaar02631462017-09-22 15:20:32 +02005951 int width1 = curwin->w_width - curwin_col_off();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952 int width2 = width1 + curwin_col_off2();
5953
5954 validate_virtcol();
5955 i = 0;
5956 if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0)
5957 i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
5958 }
5959 else
5960 i = curwin->w_leftcol;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005961 // Go to the middle of the screen line. When 'number' or
5962 // 'relativenumber' is on and lines are wrapping the middle can be more
5963 // to the left.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005964 if (cap->nchar == 'm')
Bram Moolenaar02631462017-09-22 15:20:32 +02005965 i += (curwin->w_width - curwin_col_off()
Bram Moolenaar071d4272004-06-13 20:20:40 +00005966 + ((curwin->w_p_wrap && i > 0)
5967 ? curwin_col_off2() : 0)) / 2;
5968 coladvance((colnr_T)i);
5969 if (flag)
5970 {
5971 do
5972 i = gchar_cursor();
Bram Moolenaar1c465442017-03-12 20:10:05 +01005973 while (VIM_ISWHITE(i) && oneright() == OK);
Bram Moolenaarf9514162018-11-22 03:08:29 +01005974 curwin->w_valid &= ~VALID_WCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005975 }
5976 curwin->w_set_curswant = TRUE;
5977 break;
5978
Bram Moolenaar8b530c12019-10-28 02:13:05 +01005979 case 'M':
5980 {
5981 char_u *ptr = ml_get_curline();
5982
5983 oap->motion_type = MCHAR;
5984 oap->inclusive = FALSE;
5985 if (has_mbyte)
Bram Moolenaar69bf6342019-10-29 04:16:57 +01005986 i = mb_string2cells(ptr, (int)STRLEN(ptr));
Bram Moolenaar8b530c12019-10-28 02:13:05 +01005987 else
5988 i = (int)STRLEN(ptr);
5989 if (cap->count0 > 0 && cap->count0 <= 100)
5990 coladvance((colnr_T)(i * cap->count0 / 100));
5991 else
5992 coladvance((colnr_T)(i / 2));
5993 curwin->w_set_curswant = TRUE;
5994 }
5995 break;
5996
Bram Moolenaar071d4272004-06-13 20:20:40 +00005997 case '_':
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005998 // "g_": to the last non-blank character in the line or <count> lines
5999 // downward.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006000 cap->oap->motion_type = MCHAR;
6001 cap->oap->inclusive = TRUE;
6002 curwin->w_curswant = MAXCOL;
6003 if (cursor_down((long)(cap->count1 - 1),
6004 cap->oap->op_type == OP_NOP) == FAIL)
6005 clearopbeep(cap->oap);
6006 else
6007 {
6008 char_u *ptr = ml_get_curline();
6009
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006010 // In Visual mode we may end up after the line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011 if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL)
6012 --curwin->w_cursor.col;
6013
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006014 // Decrease the cursor column until it's on a non-blank.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006015 while (curwin->w_cursor.col > 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01006016 && VIM_ISWHITE(ptr[curwin->w_cursor.col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017 --curwin->w_cursor.col;
6018 curwin->w_set_curswant = TRUE;
Bram Moolenaar5890b2c2010-01-12 15:42:37 +01006019 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006020 }
6021 break;
6022
6023 case '$':
6024 case K_END:
6025 case K_KEND:
Bram Moolenaar071d4272004-06-13 20:20:40 +00006026 {
6027 int col_off = curwin_col_off();
6028
6029 oap->motion_type = MCHAR;
6030 oap->inclusive = TRUE;
Bram Moolenaar4033c552017-09-16 20:54:51 +02006031 if (curwin->w_p_wrap && curwin->w_width != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006032 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006033 curwin->w_curswant = MAXCOL; // so we stay at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00006034 if (cap->count1 == 1)
6035 {
Bram Moolenaar02631462017-09-22 15:20:32 +02006036 int width1 = curwin->w_width - col_off;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006037 int width2 = width1 + curwin_col_off2();
6038
6039 validate_virtcol();
6040 i = width1 - 1;
6041 if (curwin->w_virtcol >= (colnr_T)width1)
6042 i += ((curwin->w_virtcol - width1) / width2 + 1)
6043 * width2;
6044 coladvance((colnr_T)i);
Bram Moolenaarb69510e2013-07-09 17:08:29 +02006045
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006046 // Make sure we stick in this column.
Bram Moolenaarb69510e2013-07-09 17:08:29 +02006047 validate_virtcol();
6048 curwin->w_curswant = curwin->w_virtcol;
6049 curwin->w_set_curswant = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006050 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
6051 {
6052 /*
6053 * Check for landing on a character that got split at
6054 * the end of the line. We do not want to advance to
6055 * the next screen line.
6056 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006057 if (curwin->w_virtcol > (colnr_T)i)
6058 --curwin->w_cursor.col;
6059 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006060 }
6061 else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == FAIL)
6062 clearopbeep(oap);
6063 }
6064 else
6065 {
Bram Moolenaard5c82342019-07-27 18:44:57 +02006066 if (cap->count1 > 1)
6067 // if it fails, let the cursor still move to the last char
Bram Moolenaar9c272a92019-08-16 21:54:27 +02006068 (void)cursor_down(cap->count1 - 1, FALSE);
Bram Moolenaard5c82342019-07-27 18:44:57 +02006069
Bram Moolenaar02631462017-09-22 15:20:32 +02006070 i = curwin->w_leftcol + curwin->w_width - col_off - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006071 coladvance((colnr_T)i);
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00006072
Bram Moolenaard5c82342019-07-27 18:44:57 +02006073 // Make sure we stick in this column.
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00006074 validate_virtcol();
6075 curwin->w_curswant = curwin->w_virtcol;
6076 curwin->w_set_curswant = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006077 }
6078 }
6079 break;
6080
6081 /*
6082 * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
6083 */
6084 case '*':
6085 case '#':
6086#if POUND != '#'
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006087 case POUND: // pound sign (sometimes equal to '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006088#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006089 case Ctrl_RSB: // :tag or :tselect for current identifier
6090 case ']': // :tselect for current identifier
Bram Moolenaar071d4272004-06-13 20:20:40 +00006091 nv_ident(cap);
6092 break;
6093
6094 /*
6095 * ge and gE: go back to end of word
6096 */
6097 case 'e':
6098 case 'E':
6099 oap->motion_type = MCHAR;
6100 curwin->w_set_curswant = TRUE;
6101 oap->inclusive = TRUE;
6102 if (bckend_word(cap->count1, cap->nchar == 'E', FALSE) == FAIL)
6103 clearopbeep(oap);
6104 break;
6105
6106 /*
6107 * "g CTRL-G": display info about cursor position
6108 */
6109 case Ctrl_G:
Bram Moolenaared767a22016-01-03 22:49:16 +01006110 cursor_pos_info(NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006111 break;
6112
6113 /*
6114 * "gi": start Insert at the last position.
6115 */
6116 case 'i':
6117 if (curbuf->b_last_insert.lnum != 0)
6118 {
6119 curwin->w_cursor = curbuf->b_last_insert;
6120 check_cursor_lnum();
6121 i = (int)STRLEN(ml_get_curline());
6122 if (curwin->w_cursor.col > (colnr_T)i)
6123 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006124 if (virtual_active())
6125 curwin->w_cursor.coladd += curwin->w_cursor.col - i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006126 curwin->w_cursor.col = i;
6127 }
6128 }
6129 cap->cmdchar = 'i';
6130 nv_edit(cap);
6131 break;
6132
6133 /*
6134 * "gI": Start insert in column 1.
6135 */
6136 case 'I':
6137 beginline(0);
6138 if (!checkclearopq(oap))
6139 invoke_edit(cap, FALSE, 'g', FALSE);
6140 break;
6141
6142#ifdef FEAT_SEARCHPATH
6143 /*
6144 * "gf": goto file, edit file under cursor
6145 * "]f" and "[f": can also be used.
6146 */
6147 case 'f':
Bram Moolenaard1f56e62006-02-22 21:25:37 +00006148 case 'F':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006149 nv_gotofile(cap);
6150 break;
6151#endif
6152
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006153 // "g'm" and "g`m": jump to mark without setting pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00006154 case '\'':
6155 cap->arg = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006156 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00006157 case '`':
6158 nv_gomark(cap);
6159 break;
6160
6161 /*
6162 * "gs": Goto sleep.
6163 */
6164 case 's':
6165 do_sleep(cap->count1 * 1000L);
6166 break;
6167
6168 /*
6169 * "ga": Display the ascii value of the character under the
6170 * cursor. It is displayed in decimal, hex, and octal. -- webb
6171 */
6172 case 'a':
6173 do_ascii(NULL);
6174 break;
6175
Bram Moolenaar071d4272004-06-13 20:20:40 +00006176 /*
6177 * "g8": Display the bytes used for the UTF-8 character under the
6178 * cursor. It is displayed in hex.
Bram Moolenaara83c3e02006-03-17 23:10:44 +00006179 * "8g8" finds illegal byte sequence.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006180 */
6181 case '8':
Bram Moolenaara83c3e02006-03-17 23:10:44 +00006182 if (cap->count0 == 8)
6183 utf_find_illegal();
6184 else
6185 show_utf8();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006186 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006187
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006188 // "g<": show scrollback text
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00006189 case '<':
6190 show_sb_text();
6191 break;
6192
Bram Moolenaar071d4272004-06-13 20:20:40 +00006193 /*
6194 * "gg": Goto the first line in file. With a count it goes to
6195 * that line number like for "G". -- webb
6196 */
6197 case 'g':
6198 cap->arg = FALSE;
6199 nv_goto(cap);
6200 break;
6201
6202 /*
6203 * Two-character operators:
6204 * "gq" Format text
6205 * "gw" Format text and keep cursor position
6206 * "g~" Toggle the case of the text.
6207 * "gu" Change text to lower case.
6208 * "gU" Change text to upper case.
6209 * "g?" rot13 encoding
Bram Moolenaar12033fb2005-12-16 21:49:31 +00006210 * "g@" call 'operatorfunc'
Bram Moolenaar071d4272004-06-13 20:20:40 +00006211 */
6212 case 'q':
6213 case 'w':
6214 oap->cursor_start = curwin->w_cursor;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006215 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216 case '~':
6217 case 'u':
6218 case 'U':
6219 case '?':
Bram Moolenaar12033fb2005-12-16 21:49:31 +00006220 case '@':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006221 nv_operator(cap);
6222 break;
6223
6224 /*
Bram Moolenaarf711faf2007-05-10 16:48:19 +00006225 * "gd": Find first occurrence of pattern under the cursor in the
Bram Moolenaar071d4272004-06-13 20:20:40 +00006226 * current function
6227 * "gD": idem, but in the current file.
6228 */
6229 case 'd':
6230 case 'D':
Bram Moolenaarf75a9632005-09-13 21:20:47 +00006231 nv_gd(oap, cap->nchar, (int)cap->count0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006232 break;
6233
Bram Moolenaar071d4272004-06-13 20:20:40 +00006234 /*
6235 * g<*Mouse> : <C-*mouse>
6236 */
6237 case K_MIDDLEMOUSE:
6238 case K_MIDDLEDRAG:
6239 case K_MIDDLERELEASE:
6240 case K_LEFTMOUSE:
6241 case K_LEFTDRAG:
6242 case K_LEFTRELEASE:
Bram Moolenaar51b0f372017-11-18 18:52:04 +01006243 case K_MOUSEMOVE:
Bram Moolenaar071d4272004-06-13 20:20:40 +00006244 case K_RIGHTMOUSE:
6245 case K_RIGHTDRAG:
6246 case K_RIGHTRELEASE:
6247 case K_X1MOUSE:
6248 case K_X1DRAG:
6249 case K_X1RELEASE:
6250 case K_X2MOUSE:
6251 case K_X2DRAG:
6252 case K_X2RELEASE:
6253 mod_mask = MOD_MASK_CTRL;
6254 (void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0);
6255 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006256
6257 case K_IGNORE:
6258 break;
6259
6260 /*
6261 * "gP" and "gp": same as "P" and "p" but leave cursor just after new text
6262 */
6263 case 'p':
6264 case 'P':
6265 nv_put(cap);
6266 break;
6267
6268#ifdef FEAT_BYTEOFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006269 // "go": goto byte count from start of buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00006270 case 'o':
6271 goto_byte(cap->count0);
6272 break;
6273#endif
6274
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006275 // "gQ": improved Ex mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00006276 case 'Q':
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00006277 if (text_locked())
Bram Moolenaar071d4272004-06-13 20:20:40 +00006278 {
6279 clearopbeep(cap->oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00006280 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006281 break;
6282 }
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00006283
Bram Moolenaar071d4272004-06-13 20:20:40 +00006284 if (!checkclearopq(oap))
6285 do_exmode(TRUE);
6286 break;
6287
6288#ifdef FEAT_JUMPLIST
6289 case ',':
6290 nv_pcmark(cap);
6291 break;
6292
6293 case ';':
6294 cap->count1 = -cap->count1;
6295 nv_pcmark(cap);
6296 break;
6297#endif
6298
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006299 case 't':
Bram Moolenaar89f940f2012-06-29 13:56:06 +02006300 if (!checkclearop(oap))
6301 goto_tabpage((int)cap->count0);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006302 break;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00006303 case 'T':
Bram Moolenaar89f940f2012-06-29 13:56:06 +02006304 if (!checkclearop(oap))
6305 goto_tabpage(-(int)cap->count1);
Bram Moolenaar80a94a52006-02-23 21:26:58 +00006306 break;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006307
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006308 case '+':
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006309 case '-': // "g+" and "g-": undo or redo along the timeline
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006310 if (!checkclearopq(oap))
Bram Moolenaard3667a22006-03-16 21:35:52 +00006311 undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1,
Bram Moolenaar730cde92010-06-27 05:18:54 +02006312 FALSE, FALSE, FALSE);
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006313 break;
6314
Bram Moolenaar071d4272004-06-13 20:20:40 +00006315 default:
6316 clearopbeep(oap);
6317 break;
6318 }
6319}
6320
6321/*
6322 * Handle "o" and "O" commands.
6323 */
6324 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006325n_opencmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326{
Bram Moolenaar860cae12010-06-05 23:22:07 +02006327#ifdef FEAT_CONCEAL
6328 linenr_T oldline = curwin->w_cursor.lnum;
6329#endif
6330
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331 if (!checkclearopq(cap->oap))
6332 {
6333#ifdef FEAT_FOLDING
6334 if (cap->cmdchar == 'O')
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006335 // Open above the first line of a folded sequence of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 (void)hasFolding(curwin->w_cursor.lnum,
6337 &curwin->w_cursor.lnum, NULL);
6338 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006339 // Open below the last line of a folded sequence of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00006340 (void)hasFolding(curwin->w_cursor.lnum,
6341 NULL, &curwin->w_cursor.lnum);
6342#endif
6343 if (u_save((linenr_T)(curwin->w_cursor.lnum -
6344 (cap->cmdchar == 'O' ? 1 : 0)),
6345 (linenr_T)(curwin->w_cursor.lnum +
6346 (cap->cmdchar == 'o' ? 1 : 0))
6347 ) == OK
6348 && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
Bram Moolenaar8c96af92019-09-28 19:05:57 +02006349 has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0,
6350 0) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006351 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006352#ifdef FEAT_CONCEAL
Bram Moolenaarf5963f72010-07-23 22:10:27 +02006353 if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum)
Bram Moolenaar535d5b62019-01-11 20:45:36 +01006354 redrawWinline(curwin, oldline);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006355#endif
Bram Moolenaard710e0d2015-06-10 12:16:47 +02006356#ifdef FEAT_SYN_HL
Bram Moolenaard0d0fe02015-06-09 19:23:46 +02006357 if (curwin->w_p_cul)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006358 // force redraw of cursorline
Bram Moolenaard0d0fe02015-06-09 19:23:46 +02006359 curwin->w_valid &= ~VALID_CROW;
Bram Moolenaard710e0d2015-06-10 12:16:47 +02006360#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006361 // When '#' is in 'cpoptions' ignore the count.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006362 if (vim_strchr(p_cpo, CPO_HASH) != NULL)
6363 cap->count1 = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006364 invoke_edit(cap, FALSE, cap->cmdchar, TRUE);
6365 }
6366 }
6367}
6368
6369/*
6370 * "." command: redo last change.
6371 */
6372 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006373nv_dot(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006374{
6375 if (!checkclearopq(cap->oap))
6376 {
6377 /*
6378 * If "restart_edit" is TRUE, the last but one command is repeated
6379 * instead of the last command (inserting text). This is used for
6380 * CTRL-O <.> in insert mode.
6381 */
6382 if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == FAIL)
6383 clearopbeep(cap->oap);
6384 }
6385}
6386
6387/*
6388 * CTRL-R: undo undo
6389 */
6390 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006391nv_redo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392{
6393 if (!checkclearopq(cap->oap))
6394 {
6395 u_redo((int)cap->count1);
6396 curwin->w_set_curswant = TRUE;
6397 }
6398}
6399
6400/*
6401 * Handle "U" command.
6402 */
6403 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006404nv_Undo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006405{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006406 // In Visual mode and typing "gUU" triggers an operator
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006407 if (cap->oap->op_type == OP_UPPER || VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006408 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006409 // translate "gUU" to "gUgU"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006410 cap->cmdchar = 'g';
6411 cap->nchar = 'U';
6412 nv_operator(cap);
6413 }
6414 else if (!checkclearopq(cap->oap))
6415 {
6416 u_undoline();
6417 curwin->w_set_curswant = TRUE;
6418 }
6419}
6420
6421/*
6422 * '~' command: If tilde is not an operator and Visual is off: swap case of a
6423 * single character.
6424 */
6425 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006426nv_tilde(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006427{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006428 if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE)
Bram Moolenaarf2732452018-06-03 14:47:35 +02006429 {
6430#ifdef FEAT_JOB_CHANNEL
6431 if (bt_prompt(curbuf) && !prompt_curpos_editable())
6432 {
6433 clearopbeep(cap->oap);
6434 return;
6435 }
6436#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006437 n_swapchar(cap);
Bram Moolenaarf2732452018-06-03 14:47:35 +02006438 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006439 else
6440 nv_operator(cap);
6441}
6442
6443/*
6444 * Handle an operator command.
6445 * The actual work is done by do_pending_operator().
6446 */
6447 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006448nv_operator(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006449{
6450 int op_type;
6451
6452 op_type = get_op_type(cap->cmdchar, cap->nchar);
Bram Moolenaarf2732452018-06-03 14:47:35 +02006453#ifdef FEAT_JOB_CHANNEL
6454 if (bt_prompt(curbuf) && op_is_change(op_type) && !prompt_curpos_editable())
6455 {
6456 clearopbeep(cap->oap);
6457 return;
6458 }
6459#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006461 if (op_type == cap->oap->op_type) // double operator works on lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462 nv_lineop(cap);
6463 else if (!checkclearop(cap->oap))
6464 {
6465 cap->oap->start = curwin->w_cursor;
6466 cap->oap->op_type = op_type;
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006467#ifdef FEAT_EVAL
6468 set_op_var(op_type);
6469#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470 }
6471}
6472
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006473#ifdef FEAT_EVAL
6474/*
6475 * Set v:operator to the characters for "optype".
6476 */
6477 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006478set_op_var(int optype)
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006479{
6480 char_u opchars[3];
6481
6482 if (optype == OP_NOP)
6483 set_vim_var_string(VV_OP, NULL, 0);
6484 else
6485 {
6486 opchars[0] = get_op_char(optype);
6487 opchars[1] = get_extra_op_char(optype);
6488 opchars[2] = NUL;
6489 set_vim_var_string(VV_OP, opchars, -1);
6490 }
6491}
6492#endif
6493
Bram Moolenaar071d4272004-06-13 20:20:40 +00006494/*
6495 * Handle linewise operator "dd", "yy", etc.
6496 *
6497 * "_" is is a strange motion command that helps make operators more logical.
6498 * It is actually implemented, but not documented in the real Vi. This motion
6499 * command actually refers to "the current line". Commands like "dd" and "yy"
6500 * are really an alternate form of "d_" and "y_". It does accept a count, so
6501 * "d3_" works to delete 3 lines.
6502 */
6503 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006504nv_lineop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006505{
6506 cap->oap->motion_type = MLINE;
6507 if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == FAIL)
6508 clearopbeep(cap->oap);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006509 else if ( (cap->oap->op_type == OP_DELETE // only with linewise motions
Bram Moolenaar83dadaf2012-12-12 17:33:32 +01006510 && cap->oap->motion_force != 'v'
6511 && cap->oap->motion_force != Ctrl_V)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512 || cap->oap->op_type == OP_LSHIFT
6513 || cap->oap->op_type == OP_RSHIFT)
6514 beginline(BL_SOL | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006515 else if (cap->oap->op_type != OP_YANK) // 'Y' does not move cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00006516 beginline(BL_WHITE | BL_FIX);
6517}
6518
6519/*
6520 * <Home> command.
6521 */
6522 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006523nv_home(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006525 // CTRL-HOME is like "gg"
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00006526 if (mod_mask & MOD_MASK_CTRL)
6527 nv_goto(cap);
6528 else
6529 {
6530 cap->count0 = 1;
6531 nv_pipe(cap);
6532 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006533 ins_at_eol = FALSE; // Don't move cursor past eol (only necessary in a
6534 // one-character line).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535}
6536
6537/*
6538 * "|" command.
6539 */
6540 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006541nv_pipe(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542{
6543 cap->oap->motion_type = MCHAR;
6544 cap->oap->inclusive = FALSE;
6545 beginline(0);
6546 if (cap->count0 > 0)
6547 {
6548 coladvance((colnr_T)(cap->count0 - 1));
6549 curwin->w_curswant = (colnr_T)(cap->count0 - 1);
6550 }
6551 else
6552 curwin->w_curswant = 0;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006553 // keep curswant at the column where we wanted to go, not where
6554 // we ended; differs if line is too short
Bram Moolenaar071d4272004-06-13 20:20:40 +00006555 curwin->w_set_curswant = FALSE;
6556}
6557
6558/*
6559 * Handle back-word command "b" and "B".
6560 * cap->arg is 1 for "B"
6561 */
6562 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006563nv_bck_word(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006564{
6565 cap->oap->motion_type = MCHAR;
6566 cap->oap->inclusive = FALSE;
6567 curwin->w_set_curswant = TRUE;
6568 if (bck_word(cap->count1, cap->arg, FALSE) == FAIL)
6569 clearopbeep(cap->oap);
6570#ifdef FEAT_FOLDING
6571 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6572 foldOpenCursor();
6573#endif
6574}
6575
6576/*
6577 * Handle word motion commands "e", "E", "w" and "W".
6578 * cap->arg is TRUE for "E" and "W".
6579 */
6580 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006581nv_wordcmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006582{
6583 int n;
6584 int word_end;
6585 int flag = FALSE;
Bram Moolenaardfefb982008-04-01 10:06:39 +00006586 pos_T startpos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006587
6588 /*
6589 * Set inclusive for the "E" and "e" command.
6590 */
6591 if (cap->cmdchar == 'e' || cap->cmdchar == 'E')
6592 word_end = TRUE;
6593 else
6594 word_end = FALSE;
6595 cap->oap->inclusive = word_end;
6596
6597 /*
6598 * "cw" and "cW" are a special case.
6599 */
6600 if (!word_end && cap->oap->op_type == OP_CHANGE)
6601 {
6602 n = gchar_cursor();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006603 if (n != NUL) // not an empty line
Bram Moolenaar071d4272004-06-13 20:20:40 +00006604 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006605 if (VIM_ISWHITE(n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006606 {
6607 /*
6608 * Reproduce a funny Vi behaviour: "cw" on a blank only
6609 * changes one character, not all blanks until the start of
6610 * the next word. Only do this when the 'w' flag is included
6611 * in 'cpoptions'.
6612 */
6613 if (cap->count1 == 1 && vim_strchr(p_cpo, CPO_CW) != NULL)
6614 {
6615 cap->oap->inclusive = TRUE;
6616 cap->oap->motion_type = MCHAR;
6617 return;
6618 }
6619 }
6620 else
6621 {
6622 /*
6623 * This is a little strange. To match what the real Vi does,
6624 * we effectively map 'cw' to 'ce', and 'cW' to 'cE', provided
6625 * that we are not on a space or a TAB. This seems impolite
6626 * at first, but it's really more what we mean when we say
6627 * 'cw'.
6628 * Another strangeness: When standing on the end of a word
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006629 * "ce" will change until the end of the next word, but "cw"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006630 * will change only one character! This is done by setting
6631 * flag.
6632 */
6633 cap->oap->inclusive = TRUE;
6634 word_end = TRUE;
6635 flag = TRUE;
6636 }
6637 }
6638 }
6639
6640 cap->oap->motion_type = MCHAR;
6641 curwin->w_set_curswant = TRUE;
6642 if (word_end)
6643 n = end_word(cap->count1, cap->arg, flag, FALSE);
6644 else
6645 n = fwd_word(cap->count1, cap->arg, cap->oap->op_type != OP_NOP);
6646
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006647 // Don't leave the cursor on the NUL past the end of line. Unless we
6648 // didn't move it forward.
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006649 if (LT_POS(startpos, curwin->w_cursor))
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006650 adjust_cursor(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651
6652 if (n == FAIL && cap->oap->op_type == OP_NOP)
6653 clearopbeep(cap->oap);
6654 else
6655 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006657#ifdef FEAT_FOLDING
6658 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6659 foldOpenCursor();
6660#endif
6661 }
6662}
6663
6664/*
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006665 * Used after a movement command: If the cursor ends up on the NUL after the
6666 * end of the line, may move it back to the last character and make the motion
6667 * inclusive.
6668 */
6669 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006670adjust_cursor(oparg_T *oap)
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006671{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006672 // The cursor cannot remain on the NUL when:
6673 // - the column is > 0
6674 // - not in Visual mode or 'selection' is "o"
6675 // - 'virtualedit' is not "all" and not "onemore".
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006676 if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006677 && (!VIsual_active || *p_sel == 'o')
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01006678 && !virtual_active() && (ve_flags & VE_ONEMORE) == 0)
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006679 {
6680 --curwin->w_cursor.col;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006681 // prevent cursor from moving on the trail byte
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006682 if (has_mbyte)
6683 mb_adjust_cursor();
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006684 oap->inclusive = TRUE;
6685 }
6686}
6687
6688/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006689 * "0" and "^" commands.
6690 * cap->arg is the argument for beginline().
6691 */
6692 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006693nv_beginline(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006694{
6695 cap->oap->motion_type = MCHAR;
6696 cap->oap->inclusive = FALSE;
6697 beginline(cap->arg);
6698#ifdef FEAT_FOLDING
6699 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6700 foldOpenCursor();
6701#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006702 ins_at_eol = FALSE; // Don't move cursor past eol (only necessary in a
6703 // one-character line).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006704}
6705
Bram Moolenaar071d4272004-06-13 20:20:40 +00006706/*
6707 * In exclusive Visual mode, may include the last character.
6708 */
6709 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006710adjust_for_sel(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006711{
6712 if (VIsual_active && cap->oap->inclusive && *p_sel == 'e'
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006713 && gchar_cursor() != NUL && LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006714 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006715 if (has_mbyte)
6716 inc_cursor();
6717 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006718 ++curwin->w_cursor.col;
6719 cap->oap->inclusive = FALSE;
6720 }
6721}
6722
6723/*
6724 * Exclude last character at end of Visual area for 'selection' == "exclusive".
6725 * Should check VIsual_mode before calling this.
6726 * Returns TRUE when backed up to the previous line.
6727 */
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02006728 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01006729unadjust_for_sel(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006730{
6731 pos_T *pp;
6732
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006733 if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006734 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006735 if (LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006736 pp = &curwin->w_cursor;
6737 else
6738 pp = &VIsual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006739 if (pp->coladd > 0)
6740 --pp->coladd;
6741 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006742 if (pp->col > 0)
6743 {
6744 --pp->col;
Bram Moolenaar03a807a2011-07-07 15:08:58 +02006745 mb_adjustpos(curbuf, pp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006746 }
6747 else if (pp->lnum > 1)
6748 {
6749 --pp->lnum;
6750 pp->col = (colnr_T)STRLEN(ml_get(pp->lnum));
6751 return TRUE;
6752 }
6753 }
6754 return FALSE;
6755}
6756
6757/*
6758 * SELECT key in Normal or Visual mode: end of Select mode mapping.
6759 */
6760 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006761nv_select(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006762{
6763 if (VIsual_active)
6764 VIsual_select = TRUE;
6765 else if (VIsual_reselect)
6766 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006767 cap->nchar = 'v'; // fake "gv" command
Bram Moolenaar071d4272004-06-13 20:20:40 +00006768 cap->arg = TRUE;
6769 nv_g_cmd(cap);
6770 }
6771}
6772
Bram Moolenaar071d4272004-06-13 20:20:40 +00006773
6774/*
6775 * "G", "gg", CTRL-END, CTRL-HOME.
6776 * cap->arg is TRUE for "G".
6777 */
6778 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006779nv_goto(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006780{
6781 linenr_T lnum;
6782
6783 if (cap->arg)
6784 lnum = curbuf->b_ml.ml_line_count;
6785 else
6786 lnum = 1L;
6787 cap->oap->motion_type = MLINE;
6788 setpcmark();
6789
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006790 // When a count is given, use it instead of the default lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00006791 if (cap->count0 != 0)
6792 lnum = cap->count0;
6793 if (lnum < 1L)
6794 lnum = 1L;
6795 else if (lnum > curbuf->b_ml.ml_line_count)
6796 lnum = curbuf->b_ml.ml_line_count;
6797 curwin->w_cursor.lnum = lnum;
6798 beginline(BL_SOL | BL_FIX);
6799#ifdef FEAT_FOLDING
6800 if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP)
6801 foldOpenCursor();
6802#endif
6803}
6804
6805/*
6806 * CTRL-\ in Normal mode.
6807 */
6808 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006809nv_normal(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006810{
6811 if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G)
6812 {
6813 clearop(cap->oap);
Bram Moolenaar28c258f2006-01-25 22:02:51 +00006814 if (restart_edit != 0 && mode_displayed)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006815 clear_cmdline = TRUE; // unshow mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00006816 restart_edit = 0;
6817#ifdef FEAT_CMDWIN
6818 if (cmdwin_type != 0)
6819 cmdwin_result = Ctrl_C;
6820#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006821 if (VIsual_active)
6822 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006823 end_visual_mode(); // stop Visual
Bram Moolenaar071d4272004-06-13 20:20:40 +00006824 redraw_curbuf_later(INVERTED);
6825 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006826 // CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006827 if (cap->nchar == Ctrl_G && p_im)
6828 restart_edit = 'a';
6829 }
6830 else
6831 clearopbeep(cap->oap);
6832}
6833
6834/*
6835 * ESC in Normal mode: beep, but don't flush buffers.
6836 * Don't even beep if we are canceling a command.
6837 */
6838 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006839nv_esc(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006840{
6841 int no_reason;
6842
6843 no_reason = (cap->oap->op_type == OP_NOP
6844 && cap->opcount == 0
6845 && cap->count0 == 0
6846 && cap->oap->regname == 0
6847 && !p_im);
6848
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006849 if (cap->arg) // TRUE for CTRL-C
Bram Moolenaar071d4272004-06-13 20:20:40 +00006850 {
6851 if (restart_edit == 0
6852#ifdef FEAT_CMDWIN
6853 && cmdwin_type == 0
6854#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006855 && !VIsual_active
Bram Moolenaar071d4272004-06-13 20:20:40 +00006856 && no_reason)
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01006857 {
6858 if (anyBufIsChanged())
6859 msg(_("Type :qa! and press <Enter> to abandon all changes and exit Vim"));
6860 else
6861 msg(_("Type :qa and press <Enter> to exit Vim"));
6862 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006863
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006864 // Don't reset "restart_edit" when 'insertmode' is set, it won't be
6865 // set again below when halfway a mapping.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006866 if (!p_im)
6867 restart_edit = 0;
6868#ifdef FEAT_CMDWIN
6869 if (cmdwin_type != 0)
6870 {
6871 cmdwin_result = K_IGNORE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006872 got_int = FALSE; // don't stop executing autocommands et al.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873 return;
6874 }
6875#endif
6876 }
6877
Bram Moolenaar071d4272004-06-13 20:20:40 +00006878 if (VIsual_active)
6879 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006880 end_visual_mode(); // stop Visual
6881 check_cursor_col(); // make sure cursor is not beyond EOL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006882 curwin->w_set_curswant = TRUE;
6883 redraw_curbuf_later(INVERTED);
6884 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006885 else if (no_reason)
Bram Moolenaar165bc692015-07-21 17:53:25 +02006886 vim_beep(BO_ESC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006887 clearop(cap->oap);
6888
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006889 // A CTRL-C is often used at the start of a menu. When 'insertmode' is
6890 // set return to Insert mode afterwards.
Bram Moolenaare2c38102016-01-31 14:55:40 +01006891 if (restart_edit == 0 && goto_im() && ex_normal_busy == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006892 restart_edit = 'a';
6893}
6894
6895/*
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02006896 * Move the cursor for the "A" command.
6897 */
6898 void
6899set_cursor_for_append_to_line(void)
6900{
6901 curwin->w_set_curswant = TRUE;
6902 if (ve_flags == VE_ALL)
6903 {
6904 int save_State = State;
6905
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006906 // Pretend Insert mode here to allow the cursor on the
6907 // character past the end of the line
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02006908 State = INSERT;
6909 coladvance((colnr_T)MAXCOL);
6910 State = save_State;
6911 }
6912 else
6913 curwin->w_cursor.col += (colnr_T)STRLEN(ml_get_cursor());
6914}
6915
6916/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006917 * Handle "A", "a", "I", "i" and <Insert> commands.
Bram Moolenaarec2da362017-01-21 20:04:22 +01006918 * Also handle K_PS, start bracketed paste.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006919 */
6920 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006921nv_edit(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006922{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006923 // <Insert> is equal to "i"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006924 if (cap->cmdchar == K_INS || cap->cmdchar == K_KINS)
6925 cap->cmdchar = 'i';
6926
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006927 // in Visual mode "A" and "I" are an operator
Bram Moolenaar071d4272004-06-13 20:20:40 +00006928 if (VIsual_active && (cap->cmdchar == 'A' || cap->cmdchar == 'I'))
Bram Moolenaareef9add2017-09-16 15:38:04 +02006929 {
6930#ifdef FEAT_TERMINAL
6931 if (term_in_normal_mode())
6932 {
6933 end_visual_mode();
6934 clearop(cap->oap);
6935 term_enter_job_mode();
6936 return;
6937 }
6938#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006939 v_visop(cap);
Bram Moolenaareef9add2017-09-16 15:38:04 +02006940 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006941
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006942 // in Visual mode and after an operator "a" and "i" are for text objects
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006943 else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
6944 && (cap->oap->op_type != OP_NOP || VIsual_active))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006945 {
6946#ifdef FEAT_TEXTOBJ
6947 nv_object(cap);
6948#else
6949 clearopbeep(cap->oap);
6950#endif
6951 }
Bram Moolenaar662d9382017-07-31 22:56:24 +02006952#ifdef FEAT_TERMINAL
Bram Moolenaar6d819742017-08-06 14:57:49 +02006953 else if (term_in_normal_mode())
Bram Moolenaar662d9382017-07-31 22:56:24 +02006954 {
6955 clearop(cap->oap);
Bram Moolenaar6d819742017-08-06 14:57:49 +02006956 term_enter_job_mode();
Bram Moolenaar662d9382017-07-31 22:56:24 +02006957 return;
6958 }
6959#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960 else if (!curbuf->b_p_ma && !p_im)
6961 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006962 // Only give this error when 'insertmode' is off.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006963 emsg(_(e_modifiable));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006964 clearop(cap->oap);
Bram Moolenaarec2da362017-01-21 20:04:22 +01006965 if (cap->cmdchar == K_PS)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006966 // drop the pasted text
Bram Moolenaarec2da362017-01-21 20:04:22 +01006967 bracketed_paste(PASTE_INSERT, TRUE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006968 }
Bram Moolenaara1891842017-02-04 21:34:31 +01006969 else if (cap->cmdchar == K_PS && VIsual_active)
6970 {
6971 pos_T old_pos = curwin->w_cursor;
6972 pos_T old_visual = VIsual;
6973
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006974 // In Visual mode the selected text is deleted.
Bram Moolenaara1891842017-02-04 21:34:31 +01006975 if (VIsual_mode == 'V' || curwin->w_cursor.lnum != VIsual.lnum)
6976 {
6977 shift_delete_registers();
6978 cap->oap->regname = '1';
6979 }
6980 else
6981 cap->oap->regname = '-';
6982 cap->cmdchar = 'd';
6983 cap->nchar = NUL;
6984 nv_operator(cap);
6985 do_pending_operator(cap, 0, FALSE);
6986 cap->cmdchar = K_PS;
6987
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006988 // When the last char in the line was deleted then append. Detect this
6989 // by checking if the cursor moved to before the Visual area.
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006990 if (*ml_get_cursor() != NUL && LT_POS(curwin->w_cursor, old_pos)
6991 && LT_POS(curwin->w_cursor, old_visual))
Bram Moolenaara1891842017-02-04 21:34:31 +01006992 inc_cursor();
6993
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006994 // Insert to replace the deleted text with the pasted text.
Bram Moolenaara1891842017-02-04 21:34:31 +01006995 invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
6996 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006997 else if (!checkclearopq(cap->oap))
6998 {
6999 switch (cap->cmdchar)
7000 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007001 case 'A': // "A"ppend after the line
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02007002 set_cursor_for_append_to_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007003 break;
7004
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007005 case 'I': // "I"nsert before the first non-blank
Bram Moolenaar4399ef42005-02-12 14:29:27 +00007006 if (vim_strchr(p_cpo, CPO_INSEND) == NULL)
7007 beginline(BL_WHITE);
7008 else
7009 beginline(BL_WHITE|BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007010 break;
7011
Bram Moolenaara1891842017-02-04 21:34:31 +01007012 case K_PS:
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007013 // Bracketed paste works like "a"ppend, unless the cursor is in
7014 // the first column, then it inserts.
Bram Moolenaarfd8983b2017-02-02 22:21:29 +01007015 if (curwin->w_cursor.col == 0)
7016 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007017 // FALLTHROUGH
Bram Moolenaarfd8983b2017-02-02 22:21:29 +01007018
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007019 case 'a': // "a"ppend is like "i"nsert on the next character.
7020 // increment coladd when in virtual space, increment the
7021 // column otherwise, also to append after an unprintable char
Bram Moolenaar071d4272004-06-13 20:20:40 +00007022 if (virtual_active()
7023 && (curwin->w_cursor.coladd > 0
7024 || *ml_get_cursor() == NUL
7025 || *ml_get_cursor() == TAB))
7026 curwin->w_cursor.coladd++;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01007027 else if (*ml_get_cursor() != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007028 inc_cursor();
7029 break;
7030 }
7031
Bram Moolenaar071d4272004-06-13 20:20:40 +00007032 if (curwin->w_cursor.coladd && cap->cmdchar != 'A')
7033 {
7034 int save_State = State;
7035
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007036 // Pretend Insert mode here to allow the cursor on the
7037 // character past the end of the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00007038 State = INSERT;
7039 coladvance(getviscol());
7040 State = save_State;
7041 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007042
7043 invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
7044 }
Bram Moolenaarec2da362017-01-21 20:04:22 +01007045 else if (cap->cmdchar == K_PS)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007046 // drop the pasted text
Bram Moolenaarec2da362017-01-21 20:04:22 +01007047 bracketed_paste(PASTE_INSERT, TRUE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007048}
7049
7050/*
7051 * Invoke edit() and take care of "restart_edit" and the return value.
7052 */
7053 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007054invoke_edit(
7055 cmdarg_T *cap,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007056 int repl, // "r" or "gr" command
Bram Moolenaar9b578142016-01-30 19:39:49 +01007057 int cmd,
7058 int startln)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007059{
7060 int restart_edit_save = 0;
7061
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007062 // Complicated: When the user types "a<C-O>a" we don't want to do Insert
7063 // mode recursively. But when doing "a<C-O>." or "a<C-O>rx" we do allow
7064 // it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007065 if (repl || !stuff_empty())
7066 restart_edit_save = restart_edit;
7067 else
7068 restart_edit_save = 0;
7069
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007070 // Always reset "restart_edit", this is not a restarted edit.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007071 restart_edit = 0;
7072
7073 if (edit(cmd, startln, cap->count1))
7074 cap->retval |= CA_COMMAND_BUSY;
7075
7076 if (restart_edit == 0)
7077 restart_edit = restart_edit_save;
7078}
7079
7080#ifdef FEAT_TEXTOBJ
7081/*
7082 * "a" or "i" while an operator is pending or in Visual mode: object motion.
7083 */
7084 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007085nv_object(
7086 cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007087{
7088 int flag;
7089 int include;
7090 char_u *mps_save;
7091
7092 if (cap->cmdchar == 'i')
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007093 include = FALSE; // "ix" = inner object: exclude white space
Bram Moolenaar071d4272004-06-13 20:20:40 +00007094 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007095 include = TRUE; // "ax" = an object: include white space
Bram Moolenaar071d4272004-06-13 20:20:40 +00007096
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007097 // Make sure (), [], {} and <> are in 'matchpairs'
Bram Moolenaar071d4272004-06-13 20:20:40 +00007098 mps_save = curbuf->b_p_mps;
7099 curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>";
7100
7101 switch (cap->nchar)
7102 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007103 case 'w': // "aw" = a word
Bram Moolenaar071d4272004-06-13 20:20:40 +00007104 flag = current_word(cap->oap, cap->count1, include, FALSE);
7105 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007106 case 'W': // "aW" = a WORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007107 flag = current_word(cap->oap, cap->count1, include, TRUE);
7108 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007109 case 'b': // "ab" = a braces block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110 case '(':
7111 case ')':
7112 flag = current_block(cap->oap, cap->count1, include, '(', ')');
7113 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007114 case 'B': // "aB" = a Brackets block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007115 case '{':
7116 case '}':
7117 flag = current_block(cap->oap, cap->count1, include, '{', '}');
7118 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007119 case '[': // "a[" = a [] block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007120 case ']':
7121 flag = current_block(cap->oap, cap->count1, include, '[', ']');
7122 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007123 case '<': // "a<" = a <> block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007124 case '>':
7125 flag = current_block(cap->oap, cap->count1, include, '<', '>');
7126 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007127 case 't': // "at" = a tag block (xml and html)
7128 // Do not adjust oap->end in do_pending_operator()
7129 // otherwise there are different results for 'dit'
7130 // (note leading whitespace in last line):
7131 // 1) <b> 2) <b>
7132 // foobar foobar
7133 // </b> </b>
Bram Moolenaarb6c27352015-03-05 19:57:49 +01007134 cap->retval |= CA_NO_ADJ_OP_END;
Bram Moolenaarf8c07b22005-07-19 22:10:03 +00007135 flag = current_tagblock(cap->oap, cap->count1, include);
7136 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007137 case 'p': // "ap" = a paragraph
Bram Moolenaar071d4272004-06-13 20:20:40 +00007138 flag = current_par(cap->oap, cap->count1, include, 'p');
7139 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007140 case 's': // "as" = a sentence
Bram Moolenaar071d4272004-06-13 20:20:40 +00007141 flag = current_sent(cap->oap, cap->count1, include);
7142 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007143 case '"': // "a"" = a double quoted string
7144 case '\'': // "a'" = a single quoted string
7145 case '`': // "a`" = a backtick quoted string
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007146 flag = current_quote(cap->oap, cap->count1, include,
7147 cap->nchar);
7148 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007149#if 0 // TODO
7150 case 'S': // "aS" = a section
7151 case 'f': // "af" = a filename
7152 case 'u': // "au" = a URL
Bram Moolenaar071d4272004-06-13 20:20:40 +00007153#endif
7154 default:
7155 flag = FAIL;
7156 break;
7157 }
7158
7159 curbuf->b_p_mps = mps_save;
7160 if (flag == FAIL)
7161 clearopbeep(cap->oap);
7162 adjust_cursor_col();
7163 curwin->w_set_curswant = TRUE;
7164}
7165#endif
7166
7167/*
7168 * "q" command: Start/stop recording.
7169 * "q:", "q/", "q?": edit command-line in command-line window.
7170 */
7171 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007172nv_record(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007173{
7174 if (cap->oap->op_type == OP_FORMAT)
7175 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007176 // "gqq" is the same as "gqgq": format line
Bram Moolenaar071d4272004-06-13 20:20:40 +00007177 cap->cmdchar = 'g';
7178 cap->nchar = 'q';
7179 nv_operator(cap);
7180 }
7181 else if (!checkclearop(cap->oap))
7182 {
7183#ifdef FEAT_CMDWIN
7184 if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?')
7185 {
7186 stuffcharReadbuff(cap->nchar);
7187 stuffcharReadbuff(K_CMDWIN);
7188 }
7189 else
7190#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007191 // (stop) recording into a named register, unless executing a
7192 // register
Bram Moolenaar0b6d9112018-05-22 20:35:17 +02007193 if (reg_executing == 0 && do_record(cap->nchar) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007194 clearopbeep(cap->oap);
7195 }
7196}
7197
7198/*
7199 * Handle the "@r" command.
7200 */
7201 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007202nv_at(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007203{
7204 if (checkclearop(cap->oap))
7205 return;
7206#ifdef FEAT_EVAL
7207 if (cap->nchar == '=')
7208 {
7209 if (get_expr_register() == NUL)
7210 return;
7211 }
7212#endif
7213 while (cap->count1-- && !got_int)
7214 {
Bram Moolenaard333d1e2006-11-07 17:43:47 +00007215 if (do_execreg(cap->nchar, FALSE, FALSE, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007216 {
7217 clearopbeep(cap->oap);
7218 break;
7219 }
7220 line_breakcheck();
7221 }
7222}
7223
7224/*
7225 * Handle the CTRL-U and CTRL-D commands.
7226 */
7227 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007228nv_halfpage(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007229{
7230 if ((cap->cmdchar == Ctrl_U && curwin->w_cursor.lnum == 1)
7231 || (cap->cmdchar == Ctrl_D
7232 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count))
7233 clearopbeep(cap->oap);
7234 else if (!checkclearop(cap->oap))
7235 halfpage(cap->cmdchar == Ctrl_D, cap->count0);
7236}
7237
7238/*
7239 * Handle "J" or "gJ" command.
7240 */
7241 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007242nv_join(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007243{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007244 if (VIsual_active) // join the visual lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00007245 nv_operator(cap);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007246 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007247 {
7248 if (cap->count0 <= 1)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007249 cap->count0 = 2; // default for join is two lines!
Bram Moolenaar071d4272004-06-13 20:20:40 +00007250 if (curwin->w_cursor.lnum + cap->count0 - 1 >
7251 curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007252 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007253 // can't join when on the last line
Bram Moolenaar41e0f2f2016-03-08 14:44:42 +01007254 if (cap->count0 <= 2)
7255 {
7256 clearopbeep(cap->oap);
7257 return;
7258 }
7259 cap->count0 = curbuf->b_ml.ml_line_count
7260 - curwin->w_cursor.lnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007261 }
Bram Moolenaar41e0f2f2016-03-08 14:44:42 +01007262
7263 prep_redo(cap->oap->regname, cap->count0,
7264 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
7265 (void)do_join(cap->count0, cap->nchar == NUL, TRUE, TRUE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007266 }
7267}
7268
7269/*
7270 * "P", "gP", "p" and "gp" commands.
7271 */
7272 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007273nv_put(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007274{
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02007275 nv_put_opt(cap, FALSE);
7276}
7277
7278/*
7279 * "P", "gP", "p" and "gp" commands.
7280 * "fix_indent" is TRUE for "[p", "[P", "]p" and "]P".
7281 */
7282 static void
7283nv_put_opt(cmdarg_T *cap, int fix_indent)
7284{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007285 int regname = 0;
7286 void *reg1 = NULL, *reg2 = NULL;
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007287 int empty = FALSE;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007288 int was_visual = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007289 int dir;
7290 int flags = 0;
7291
7292 if (cap->oap->op_type != OP_NOP)
7293 {
7294#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007295 // "dp" is ":diffput"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007296 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'p')
7297 {
7298 clearop(cap->oap);
Bram Moolenaar6a643652014-10-31 13:54:25 +01007299 nv_diffgetput(TRUE, cap->opcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007300 }
7301 else
7302#endif
7303 clearopbeep(cap->oap);
7304 }
Bram Moolenaarf2732452018-06-03 14:47:35 +02007305#ifdef FEAT_JOB_CHANNEL
7306 else if (bt_prompt(curbuf) && !prompt_curpos_editable())
7307 {
7308 clearopbeep(cap->oap);
7309 }
7310#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007311 else
7312 {
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02007313 if (fix_indent)
7314 {
7315 dir = (cap->cmdchar == ']' && cap->nchar == 'p')
7316 ? FORWARD : BACKWARD;
7317 flags |= PUT_FIXINDENT;
7318 }
7319 else
7320 dir = (cap->cmdchar == 'P'
7321 || (cap->cmdchar == 'g' && cap->nchar == 'P'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007322 ? BACKWARD : FORWARD;
7323 prep_redo_cmd(cap);
7324 if (cap->cmdchar == 'g')
7325 flags |= PUT_CURSEND;
7326
Bram Moolenaar071d4272004-06-13 20:20:40 +00007327 if (VIsual_active)
7328 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007329 // Putting in Visual mode: The put text replaces the selected
7330 // text. First delete the selected text, then put the new text.
7331 // Need to save and restore the registers that the delete
7332 // overwrites if the old contents is being put.
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007333 was_visual = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007334 regname = cap->oap->regname;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007335#ifdef FEAT_CLIPBOARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007336 adjust_clip_reg(&regname);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007337#endif
Bram Moolenaar7d311c52014-02-22 23:49:35 +01007338 if (regname == 0 || regname == '"'
Bram Moolenaarba6e8582012-12-12 18:20:32 +01007339 || VIM_ISDIGIT(regname) || regname == '-'
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007340#ifdef FEAT_CLIPBOARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007341 || (clip_unnamed && (regname == '*' || regname == '+'))
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007342#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007343
7344 )
7345 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007346 // The delete is going to overwrite the register we want to
7347 // put, save it first.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007348 reg1 = get_register(regname, TRUE);
7349 }
7350
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007351 // Now delete the selected text. Avoid messages here.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007352 cap->cmdchar = 'd';
7353 cap->nchar = NUL;
7354 cap->oap->regname = NUL;
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007355 ++msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007356 nv_operator(cap);
7357 do_pending_operator(cap, 0, FALSE);
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007358 empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007359 --msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007360
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007361 // delete PUT_LINE_BACKWARD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007362 cap->oap->regname = regname;
7363
7364 if (reg1 != NULL)
7365 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007366 // Delete probably changed the register we want to put, save
7367 // it first. Then put back what was there before the delete.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007368 reg2 = get_register(regname, FALSE);
7369 put_register(regname, reg1);
7370 }
7371
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007372 // When deleted a linewise Visual area, put the register as
7373 // lines to avoid it joined with the next line. When deletion was
7374 // characterwise, split a line when putting lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007375 if (VIsual_mode == 'V')
7376 flags |= PUT_LINE;
7377 else if (VIsual_mode == 'v')
7378 flags |= PUT_LINE_SPLIT;
7379 if (VIsual_mode == Ctrl_V && dir == FORWARD)
7380 flags |= PUT_LINE_FORWARD;
7381 dir = BACKWARD;
7382 if ((VIsual_mode != 'V'
7383 && curwin->w_cursor.col < curbuf->b_op_start.col)
7384 || (VIsual_mode == 'V'
7385 && curwin->w_cursor.lnum < curbuf->b_op_start.lnum))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007386 // cursor is at the end of the line or end of file, put
7387 // forward.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007388 dir = FORWARD;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007389 // May have been reset in do_put().
Bram Moolenaarec11aef2013-09-22 15:23:44 +02007390 VIsual_active = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007391 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007392 do_put(cap->oap->regname, dir, cap->count1, flags);
7393
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007394 // If a register was saved, put it back now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007395 if (reg2 != NULL)
7396 put_register(regname, reg2);
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007397
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007398 // What to reselect with "gv"? Selecting the just put text seems to
7399 // be the most useful, since the original text was removed.
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007400 if (was_visual)
7401 {
Bram Moolenaara226a6d2006-02-26 23:59:20 +00007402 curbuf->b_visual.vi_start = curbuf->b_op_start;
7403 curbuf->b_visual.vi_end = curbuf->b_op_end;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007404 // need to adjust cursor position
Bram Moolenaard29c6fe2015-11-19 20:11:54 +01007405 if (*p_sel == 'e')
7406 inc(&curbuf->b_visual.vi_end);
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007407 }
7408
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007409 // When all lines were selected and deleted do_put() leaves an empty
7410 // line that needs to be deleted now.
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007411 if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL)
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007412 {
Bram Moolenaarca70c072020-05-30 20:30:46 +02007413 ml_delete_flags(curbuf->b_ml.ml_line_count, ML_DEL_MESSAGE);
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007414 deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007415
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007416 // If the cursor was in that line, move it to the end of the last
7417 // line.
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007418 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
7419 {
7420 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
7421 coladvance((colnr_T)MAXCOL);
7422 }
7423 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007424 auto_format(FALSE, TRUE);
7425 }
7426}
7427
7428/*
7429 * "o" and "O" commands.
7430 */
7431 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007432nv_open(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007433{
7434#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007435 // "do" is ":diffget"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007436 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'o')
7437 {
7438 clearop(cap->oap);
Bram Moolenaar6a643652014-10-31 13:54:25 +01007439 nv_diffgetput(FALSE, cap->opcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007440 }
7441 else
7442#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007443 if (VIsual_active) // switch start and end of visual
Bram Moolenaar071d4272004-06-13 20:20:40 +00007444 v_swap_corners(cap->cmdchar);
Bram Moolenaarf2732452018-06-03 14:47:35 +02007445#ifdef FEAT_JOB_CHANNEL
7446 else if (bt_prompt(curbuf))
Bram Moolenaarf2732452018-06-03 14:47:35 +02007447 clearopbeep(cap->oap);
Bram Moolenaarf2732452018-06-03 14:47:35 +02007448#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007449 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007450 n_opencmd(cap);
7451}
7452
Bram Moolenaar071d4272004-06-13 20:20:40 +00007453#ifdef FEAT_NETBEANS_INTG
7454 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007455nv_nbcmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007456{
7457 netbeans_keycommand(cap->nchar);
7458}
7459#endif
7460
7461#ifdef FEAT_DND
Bram Moolenaar071d4272004-06-13 20:20:40 +00007462 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007463nv_drop(cmdarg_T *cap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007464{
7465 do_put('~', BACKWARD, 1L, PUT_CURSEND);
7466}
7467#endif
Bram Moolenaar3918c952005-03-15 22:34:55 +00007468
Bram Moolenaar3918c952005-03-15 22:34:55 +00007469/*
7470 * Trigger CursorHold event.
7471 * When waiting for a character for 'updatetime' K_CURSORHOLD is put in the
7472 * input buffer. "did_cursorhold" is set to avoid retriggering.
7473 */
Bram Moolenaar3918c952005-03-15 22:34:55 +00007474 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007475nv_cursorhold(cmdarg_T *cap)
Bram Moolenaar3918c952005-03-15 22:34:55 +00007476{
7477 apply_autocmds(EVENT_CURSORHOLD, NULL, NULL, FALSE, curbuf);
7478 did_cursorhold = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007479 cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
Bram Moolenaar3918c952005-03-15 22:34:55 +00007480}