blob: 2e8521ce0026f9458b7e04cf4ee53792d56594d8 [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 +000018
Bram Moolenaarf82a2d22010-12-17 18:53:01 +010019#ifdef FEAT_EVAL
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010020static void set_vcount_ca(cmdarg_T *cap, int *set_prevcount);
Bram Moolenaarf82a2d22010-12-17 18:53:01 +010021#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010022static void unshift_special(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#ifdef FEAT_CMDL_INFO
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010024static void del_from_showcmd(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +000025#endif
26
27/*
28 * nv_*(): functions called to handle Normal and Visual mode commands.
29 * n_*(): functions called to handle Normal mode commands.
30 * v_*(): functions called to handle Visual mode commands.
31 */
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010032static void nv_ignore(cmdarg_T *cap);
33static void nv_nop(cmdarg_T *cap);
34static void nv_error(cmdarg_T *cap);
35static void nv_help(cmdarg_T *cap);
36static void nv_addsub(cmdarg_T *cap);
37static void nv_page(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010038static void nv_zet(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000039#ifdef FEAT_GUI
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010040static void nv_ver_scrollbar(cmdarg_T *cap);
41static void nv_hor_scrollbar(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000042#endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +000043#ifdef FEAT_GUI_TABLINE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010044static void nv_tabline(cmdarg_T *cap);
45static void nv_tabmenu(cmdarg_T *cap);
Bram Moolenaar32466aa2006-02-24 23:53:04 +000046#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010047static void nv_exmode(cmdarg_T *cap);
48static void nv_colon(cmdarg_T *cap);
49static void nv_ctrlg(cmdarg_T *cap);
50static void nv_ctrlh(cmdarg_T *cap);
51static void nv_clear(cmdarg_T *cap);
52static void nv_ctrlo(cmdarg_T *cap);
53static void nv_hat(cmdarg_T *cap);
54static void nv_Zet(cmdarg_T *cap);
55static void nv_ident(cmdarg_T *cap);
56static void nv_tagpop(cmdarg_T *cap);
57static void nv_scroll(cmdarg_T *cap);
58static void nv_right(cmdarg_T *cap);
59static void nv_left(cmdarg_T *cap);
60static void nv_up(cmdarg_T *cap);
61static void nv_down(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010062static void nv_end(cmdarg_T *cap);
63static void nv_dollar(cmdarg_T *cap);
64static void nv_search(cmdarg_T *cap);
65static void nv_next(cmdarg_T *cap);
Bram Moolenaar92ea26b2019-10-18 20:53:34 +020066static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrapped);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010067static void nv_csearch(cmdarg_T *cap);
68static void nv_brackets(cmdarg_T *cap);
69static void nv_percent(cmdarg_T *cap);
70static void nv_brace(cmdarg_T *cap);
71static void nv_mark(cmdarg_T *cap);
72static void nv_findpar(cmdarg_T *cap);
73static void nv_undo(cmdarg_T *cap);
74static void nv_kundo(cmdarg_T *cap);
75static void nv_Replace(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010076static void nv_replace(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010077static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos);
78static void v_visop(cmdarg_T *cap);
79static void nv_subst(cmdarg_T *cap);
80static void nv_abbrev(cmdarg_T *cap);
81static void nv_optrans(cmdarg_T *cap);
82static void nv_gomark(cmdarg_T *cap);
83static void nv_pcmark(cmdarg_T *cap);
84static void nv_regname(cmdarg_T *cap);
85static void nv_visual(cmdarg_T *cap);
86static void n_start_visual_mode(int c);
87static void nv_window(cmdarg_T *cap);
88static void nv_suspend(cmdarg_T *cap);
89static void nv_g_cmd(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010090static void nv_dot(cmdarg_T *cap);
Shougo Matsushita4ede01f2022-01-20 15:26:03 +000091static void nv_redo_or_register(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010092static void nv_Undo(cmdarg_T *cap);
93static void nv_tilde(cmdarg_T *cap);
94static void nv_operator(cmdarg_T *cap);
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +000095#ifdef FEAT_EVAL
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010096static void set_op_var(int optype);
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +000097#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010098static void nv_lineop(cmdarg_T *cap);
99static void nv_home(cmdarg_T *cap);
100static void nv_pipe(cmdarg_T *cap);
101static void nv_bck_word(cmdarg_T *cap);
102static void nv_wordcmd(cmdarg_T *cap);
103static void nv_beginline(cmdarg_T *cap);
104static void adjust_cursor(oparg_T *oap);
105static void adjust_for_sel(cmdarg_T *cap);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100106static void nv_select(cmdarg_T *cap);
107static void nv_goto(cmdarg_T *cap);
108static void nv_normal(cmdarg_T *cap);
109static void nv_esc(cmdarg_T *oap);
110static void nv_edit(cmdarg_T *cap);
111static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112#ifdef FEAT_TEXTOBJ
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100113static void nv_object(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100115static void nv_record(cmdarg_T *cap);
116static void nv_at(cmdarg_T *cap);
117static void nv_halfpage(cmdarg_T *cap);
118static void nv_join(cmdarg_T *cap);
119static void nv_put(cmdarg_T *cap);
Bram Moolenaar0ab190c2019-05-23 23:27:36 +0200120static void nv_put_opt(cmdarg_T *cap, int fix_indent);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100121static void nv_open(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000122#ifdef FEAT_NETBEANS_INTG
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100123static void nv_nbcmd(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000124#endif
125#ifdef FEAT_DND
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100126static void nv_drop(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000127#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100128static void nv_cursorhold(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000129
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000130#ifdef FEAT_GUI
131#define NV_VER_SCROLLBAR nv_ver_scrollbar
132#define NV_HOR_SCROLLBAR nv_hor_scrollbar
133#else
134#define NV_VER_SCROLLBAR nv_error
135#define NV_HOR_SCROLLBAR nv_error
136#endif
137
138#ifdef FEAT_GUI_TABLINE
139#define NV_TABLINE nv_tabline
140#define NV_TABMENU nv_tabmenu
141#else
142#define NV_TABLINE nv_error
143#define NV_TABMENU nv_error
144#endif
145
146#ifdef FEAT_NETBEANS_INTG
147#define NV_NBCMD nv_nbcmd
148#else
149#define NV_NBCMD nv_error
150#endif
151
152#ifdef FEAT_DND
153#define NV_DROP nv_drop
154#else
155#define NV_DROP nv_error
156#endif
157
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158/*
159 * Function to be called for a Normal or Visual mode command.
160 * The argument is a cmdarg_T.
161 */
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100162typedef void (*nv_func_T)(cmdarg_T *cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100164// Values for cmd_flags.
165#define NV_NCH 0x01 // may need to get a second char
166#define NV_NCH_NOP (0x02|NV_NCH) // get second char when no operator pending
167#define NV_NCH_ALW (0x04|NV_NCH) // always get a second char
168#define NV_LANG 0x08 // second char needs language adjustment
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100170#define NV_SS 0x10 // may start selection
171#define NV_SSS 0x20 // may start selection with shift modifier
172#define NV_STS 0x40 // may stop selection without shift modif.
173#define NV_RL 0x80 // 'rightleft' modifies command
174#define NV_KEEPREG 0x100 // don't clear regname
175#define NV_NCW 0x200 // not allowed in command-line window
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176
177/*
178 * Generally speaking, every Normal mode command should either clear any
179 * pending operator (with *clearop*()), or set the motion type variable
180 * oap->motion_type.
181 *
182 * When a cursor motion command is made, it is marked as being a character or
183 * line oriented motion. Then, if an operator is in effect, the operation
184 * becomes character or line oriented accordingly.
185 */
186
187/*
188 * This table contains one entry for every Normal or Visual mode command.
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000189 * The order doesn't matter, this will be sorted by the create_nvcmdidx.vim
190 * script to generate the nv_cmd_idx[] lookup table.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000191 * It is faster when all keys from zero to '~' are present.
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000192 *
193 * After changing the "nv_cmds" table:
194 * 1. Build Vim with "make"
195 * 2. Run "make nvcmdidxs" to re-generate the nv_cmdidxs.h file.
196 * 3. Build Vim with "make" to use the newly generated index table.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197 */
198static const struct nv_cmd
199{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100200 int cmd_char; // (first) command character
201 nv_func_T cmd_func; // function for this command
202 short_u cmd_flags; // NV_ flags
203 short cmd_arg; // value for ca.arg
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204} nv_cmds[] =
205{
206 {NUL, nv_error, 0, 0},
207 {Ctrl_A, nv_addsub, 0, 0},
208 {Ctrl_B, nv_page, NV_STS, BACKWARD},
209 {Ctrl_C, nv_esc, 0, TRUE},
210 {Ctrl_D, nv_halfpage, 0, 0},
211 {Ctrl_E, nv_scroll_line, 0, TRUE},
212 {Ctrl_F, nv_page, NV_STS, FORWARD},
213 {Ctrl_G, nv_ctrlg, 0, 0},
214 {Ctrl_H, nv_ctrlh, 0, 0},
215 {Ctrl_I, nv_pcmark, 0, 0},
216 {NL, nv_down, 0, FALSE},
217 {Ctrl_K, nv_error, 0, 0},
218 {Ctrl_L, nv_clear, 0, 0},
Bram Moolenaar2c519cf2019-03-21 21:45:34 +0100219 {CAR, nv_down, 0, TRUE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220 {Ctrl_N, nv_down, NV_STS, FALSE},
221 {Ctrl_O, nv_ctrlo, 0, 0},
222 {Ctrl_P, nv_up, NV_STS, FALSE},
Bram Moolenaardf177f62005-02-22 08:39:57 +0000223 {Ctrl_Q, nv_visual, 0, FALSE},
Shougo Matsushita4ede01f2022-01-20 15:26:03 +0000224 {Ctrl_R, nv_redo_or_register, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000225 {Ctrl_S, nv_ignore, 0, 0},
226 {Ctrl_T, nv_tagpop, NV_NCW, 0},
227 {Ctrl_U, nv_halfpage, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000228 {Ctrl_V, nv_visual, 0, FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 {Ctrl_W, nv_window, 0, 0},
230 {Ctrl_X, nv_addsub, 0, 0},
231 {Ctrl_Y, nv_scroll_line, 0, FALSE},
232 {Ctrl_Z, nv_suspend, 0, 0},
233 {ESC, nv_esc, 0, FALSE},
234 {Ctrl_BSL, nv_normal, NV_NCH_ALW, 0},
235 {Ctrl_RSB, nv_ident, NV_NCW, 0},
236 {Ctrl_HAT, nv_hat, NV_NCW, 0},
237 {Ctrl__, nv_error, 0, 0},
238 {' ', nv_right, 0, 0},
239 {'!', nv_operator, 0, 0},
240 {'"', nv_regname, NV_NCH_NOP|NV_KEEPREG, 0},
241 {'#', nv_ident, 0, 0},
242 {'$', nv_dollar, 0, 0},
243 {'%', nv_percent, 0, 0},
244 {'&', nv_optrans, 0, 0},
245 {'\'', nv_gomark, NV_NCH_ALW, TRUE},
246 {'(', nv_brace, 0, BACKWARD},
247 {')', nv_brace, 0, FORWARD},
248 {'*', nv_ident, 0, 0},
249 {'+', nv_down, 0, TRUE},
250 {',', nv_csearch, 0, TRUE},
251 {'-', nv_up, 0, TRUE},
252 {'.', nv_dot, NV_KEEPREG, 0},
253 {'/', nv_search, 0, FALSE},
254 {'0', nv_beginline, 0, 0},
255 {'1', nv_ignore, 0, 0},
256 {'2', nv_ignore, 0, 0},
257 {'3', nv_ignore, 0, 0},
258 {'4', nv_ignore, 0, 0},
259 {'5', nv_ignore, 0, 0},
260 {'6', nv_ignore, 0, 0},
261 {'7', nv_ignore, 0, 0},
262 {'8', nv_ignore, 0, 0},
263 {'9', nv_ignore, 0, 0},
264 {':', nv_colon, 0, 0},
265 {';', nv_csearch, 0, FALSE},
266 {'<', nv_operator, NV_RL, 0},
267 {'=', nv_operator, 0, 0},
268 {'>', nv_operator, NV_RL, 0},
269 {'?', nv_search, 0, FALSE},
270 {'@', nv_at, NV_NCH_NOP, FALSE},
271 {'A', nv_edit, 0, 0},
272 {'B', nv_bck_word, 0, 1},
273 {'C', nv_abbrev, NV_KEEPREG, 0},
274 {'D', nv_abbrev, NV_KEEPREG, 0},
275 {'E', nv_wordcmd, 0, TRUE},
276 {'F', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD},
277 {'G', nv_goto, 0, TRUE},
278 {'H', nv_scroll, 0, 0},
279 {'I', nv_edit, 0, 0},
280 {'J', nv_join, 0, 0},
281 {'K', nv_ident, 0, 0},
282 {'L', nv_scroll, 0, 0},
283 {'M', nv_scroll, 0, 0},
284 {'N', nv_next, 0, SEARCH_REV},
285 {'O', nv_open, 0, 0},
286 {'P', nv_put, 0, 0},
287 {'Q', nv_exmode, NV_NCW, 0},
288 {'R', nv_Replace, 0, FALSE},
289 {'S', nv_subst, NV_KEEPREG, 0},
290 {'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD},
291 {'U', nv_Undo, 0, 0},
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000292 {'V', nv_visual, 0, FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 {'W', nv_wordcmd, 0, TRUE},
294 {'X', nv_abbrev, NV_KEEPREG, 0},
295 {'Y', nv_abbrev, NV_KEEPREG, 0},
296 {'Z', nv_Zet, NV_NCH_NOP|NV_NCW, 0},
297 {'[', nv_brackets, NV_NCH_ALW, BACKWARD},
298 {'\\', nv_error, 0, 0},
299 {']', nv_brackets, NV_NCH_ALW, FORWARD},
300 {'^', nv_beginline, 0, BL_WHITE | BL_FIX},
301 {'_', nv_lineop, 0, 0},
302 {'`', nv_gomark, NV_NCH_ALW, FALSE},
303 {'a', nv_edit, NV_NCH, 0},
304 {'b', nv_bck_word, 0, 0},
305 {'c', nv_operator, 0, 0},
306 {'d', nv_operator, 0, 0},
307 {'e', nv_wordcmd, 0, FALSE},
308 {'f', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD},
309 {'g', nv_g_cmd, NV_NCH_ALW, FALSE},
310 {'h', nv_left, NV_RL, 0},
311 {'i', nv_edit, NV_NCH, 0},
312 {'j', nv_down, 0, FALSE},
313 {'k', nv_up, 0, FALSE},
314 {'l', nv_right, NV_RL, 0},
315 {'m', nv_mark, NV_NCH_NOP, 0},
316 {'n', nv_next, 0, 0},
317 {'o', nv_open, 0, 0},
318 {'p', nv_put, 0, 0},
319 {'q', nv_record, NV_NCH, 0},
320 {'r', nv_replace, NV_NCH_NOP|NV_LANG, 0},
321 {'s', nv_subst, NV_KEEPREG, 0},
322 {'t', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD},
323 {'u', nv_undo, 0, 0},
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000324 {'v', nv_visual, 0, FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000325 {'w', nv_wordcmd, 0, FALSE},
326 {'x', nv_abbrev, NV_KEEPREG, 0},
327 {'y', nv_operator, 0, 0},
328 {'z', nv_zet, NV_NCH_ALW, 0},
329 {'{', nv_findpar, 0, BACKWARD},
330 {'|', nv_pipe, 0, 0},
331 {'}', nv_findpar, 0, FORWARD},
332 {'~', nv_tilde, 0, 0},
333
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100334 // pound sign
Bram Moolenaar071d4272004-06-13 20:20:40 +0000335 {POUND, nv_ident, 0, 0},
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +0200336 {K_MOUSEUP, nv_mousescroll, 0, MSCR_UP},
337 {K_MOUSEDOWN, nv_mousescroll, 0, MSCR_DOWN},
338 {K_MOUSELEFT, nv_mousescroll, 0, MSCR_LEFT},
339 {K_MOUSERIGHT, nv_mousescroll, 0, MSCR_RIGHT},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340 {K_LEFTMOUSE, nv_mouse, 0, 0},
341 {K_LEFTMOUSE_NM, nv_mouse, 0, 0},
342 {K_LEFTDRAG, nv_mouse, 0, 0},
343 {K_LEFTRELEASE, nv_mouse, 0, 0},
344 {K_LEFTRELEASE_NM, nv_mouse, 0, 0},
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100345 {K_MOUSEMOVE, nv_mouse, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346 {K_MIDDLEMOUSE, nv_mouse, 0, 0},
347 {K_MIDDLEDRAG, nv_mouse, 0, 0},
348 {K_MIDDLERELEASE, nv_mouse, 0, 0},
349 {K_RIGHTMOUSE, nv_mouse, 0, 0},
350 {K_RIGHTDRAG, nv_mouse, 0, 0},
351 {K_RIGHTRELEASE, nv_mouse, 0, 0},
352 {K_X1MOUSE, nv_mouse, 0, 0},
353 {K_X1DRAG, nv_mouse, 0, 0},
354 {K_X1RELEASE, nv_mouse, 0, 0},
355 {K_X2MOUSE, nv_mouse, 0, 0},
356 {K_X2DRAG, nv_mouse, 0, 0},
357 {K_X2RELEASE, nv_mouse, 0, 0},
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000358 {K_IGNORE, nv_ignore, NV_KEEPREG, 0},
Bram Moolenaarebefac62005-12-28 22:39:57 +0000359 {K_NOP, nv_nop, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 {K_INS, nv_edit, 0, 0},
361 {K_KINS, nv_edit, 0, 0},
362 {K_BS, nv_ctrlh, 0, 0},
363 {K_UP, nv_up, NV_SSS|NV_STS, FALSE},
364 {K_S_UP, nv_page, NV_SS, BACKWARD},
365 {K_DOWN, nv_down, NV_SSS|NV_STS, FALSE},
366 {K_S_DOWN, nv_page, NV_SS, FORWARD},
367 {K_LEFT, nv_left, NV_SSS|NV_STS|NV_RL, 0},
368 {K_S_LEFT, nv_bck_word, NV_SS|NV_RL, 0},
369 {K_C_LEFT, nv_bck_word, NV_SSS|NV_RL|NV_STS, 1},
370 {K_RIGHT, nv_right, NV_SSS|NV_STS|NV_RL, 0},
371 {K_S_RIGHT, nv_wordcmd, NV_SS|NV_RL, FALSE},
372 {K_C_RIGHT, nv_wordcmd, NV_SSS|NV_RL|NV_STS, TRUE},
373 {K_PAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD},
374 {K_KPAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD},
375 {K_PAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD},
376 {K_KPAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD},
377 {K_END, nv_end, NV_SSS|NV_STS, FALSE},
378 {K_KEND, nv_end, NV_SSS|NV_STS, FALSE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000379 {K_S_END, nv_end, NV_SS, FALSE},
380 {K_C_END, nv_end, NV_SSS|NV_STS, TRUE},
381 {K_HOME, nv_home, NV_SSS|NV_STS, 0},
382 {K_KHOME, nv_home, NV_SSS|NV_STS, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383 {K_S_HOME, nv_home, NV_SS, 0},
384 {K_C_HOME, nv_goto, NV_SSS|NV_STS, FALSE},
385 {K_DEL, nv_abbrev, 0, 0},
386 {K_KDEL, nv_abbrev, 0, 0},
387 {K_UNDO, nv_kundo, 0, 0},
388 {K_HELP, nv_help, NV_NCW, 0},
389 {K_F1, nv_help, NV_NCW, 0},
390 {K_XF1, nv_help, NV_NCW, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000391 {K_SELECT, nv_select, 0, 0},
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000392 {K_VER_SCROLLBAR, NV_VER_SCROLLBAR, 0, 0},
393 {K_HOR_SCROLLBAR, NV_HOR_SCROLLBAR, 0, 0},
394 {K_TABLINE, NV_TABLINE, 0, 0},
395 {K_TABMENU, NV_TABMENU, 0, 0},
396 {K_F21, NV_NBCMD, NV_NCH_ALW, 0},
397 {K_DROP, NV_DROP, NV_STS, 0},
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000398 {K_CURSORHOLD, nv_cursorhold, NV_KEEPREG, 0},
Bram Moolenaarec2da362017-01-21 20:04:22 +0100399 {K_PS, nv_edit, 0, 0},
Bram Moolenaar957cf672020-11-12 14:21:06 +0100400 {K_COMMAND, nv_colon, 0, 0},
Bram Moolenaare32c3c42022-01-15 18:26:04 +0000401 {K_SCRIPT_COMMAND, nv_colon, 0, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402};
403
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100404// Number of commands in nv_cmds[].
K.Takataeeec2542021-06-02 13:28:16 +0200405#define NV_CMDS_SIZE ARRAY_LENGTH(nv_cmds)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000406
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000407// Include the lookuptable generated by create_nvcmdidx.vim.
408#include "nv_cmdidxs.h"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000410#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411/*
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000412 * Return the command character for the given command index. This function is
413 * used to auto-generate nv_cmd_idx[].
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414 */
415 void
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000416f_internal_get_nv_cmdchar(typval_T *argvars, typval_T *rettv)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417{
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000418 int idx;
419 int cmd_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000421 rettv->v_type = VAR_NUMBER;
422 rettv->vval.v_number = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000423
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000424 if (check_for_number_arg(argvars, 0) == FAIL)
425 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000427 idx = tv_get_number(&argvars[0]);
428 if (idx < 0 || idx >= (int)NV_CMDS_SIZE)
429 return;
430
431 cmd_char = nv_cmds[idx].cmd_char;
432
433 // We use the absolute value of the character. Special keys have a
434 // negative value, but are sorted on their absolute value.
435 if (cmd_char < 0)
436 cmd_char = -cmd_char;
437
438 rettv->vval.v_number = cmd_char;
439
440 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441}
Yegappan Lakshmanan4dc0dd82022-01-29 13:06:40 +0000442#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443
444/*
445 * Search for a command in the commands table.
446 * Returns -1 for invalid command.
447 */
448 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +0100449find_command(int cmdchar)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450{
451 int i;
452 int idx;
453 int top, bot;
454 int c;
455
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100456 // A multi-byte character is never a command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000457 if (cmdchar >= 0x100)
458 return -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000459
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100460 // We use the absolute value of the character. Special keys have a
461 // negative value, but are sorted on their absolute value.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000462 if (cmdchar < 0)
463 cmdchar = -cmdchar;
464
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100465 // If the character is in the first part: The character is the index into
466 // nv_cmd_idx[].
Bram Moolenaar071d4272004-06-13 20:20:40 +0000467 if (cmdchar <= nv_max_linear)
468 return nv_cmd_idx[cmdchar];
469
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100470 // Perform a binary search.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471 bot = nv_max_linear + 1;
472 top = NV_CMDS_SIZE - 1;
473 idx = -1;
474 while (bot <= top)
475 {
476 i = (top + bot) / 2;
477 c = nv_cmds[nv_cmd_idx[i]].cmd_char;
478 if (c < 0)
479 c = -c;
480 if (cmdchar == c)
481 {
482 idx = nv_cmd_idx[i];
483 break;
484 }
485 if (cmdchar > c)
486 bot = i + 1;
487 else
488 top = i - 1;
489 }
490 return idx;
491}
492
493/*
Yegappan Lakshmanan46634352022-01-24 16:30:30 +0000494 * Handle the count before a normal command and set cap->count0.
495 */
496 static int
497normal_cmd_get_count(
498 cmdarg_T *cap,
499 int c,
500 int toplevel UNUSED,
501 int set_prevcount UNUSED,
502 int *ctrl_w,
503 int *need_flushbuf UNUSED)
504{
505getcount:
506 if (!(VIsual_active && VIsual_select))
507 {
508 // Handle a count before a command and compute ca.count0.
509 // Note that '0' is a command and not the start of a count, but it's
510 // part of a count after other digits.
511 while ((c >= '1' && c <= '9')
512 || (cap->count0 != 0 && (c == K_DEL || c == K_KDEL
513 || c == '0')))
514 {
515 if (c == K_DEL || c == K_KDEL)
516 {
517 cap->count0 /= 10;
518#ifdef FEAT_CMDL_INFO
519 del_from_showcmd(4); // delete the digit and ~@%
520#endif
521 }
522 else if (cap->count0 > 99999999L)
523 {
524 cap->count0 = 999999999L;
525 }
526 else
527 {
528 cap->count0 = cap->count0 * 10 + (c - '0');
529 }
530#ifdef FEAT_EVAL
531 // Set v:count here, when called from main() and not a stuffed
532 // command, so that v:count can be used in an expression mapping
533 // right after the count. Do set it for redo.
534 if (toplevel && readbuf1_empty())
535 set_vcount_ca(cap, &set_prevcount);
536#endif
537 if (*ctrl_w)
538 {
539 ++no_mapping;
540 ++allow_keys; // no mapping for nchar, but keys
541 }
542 ++no_zero_mapping; // don't map zero here
543 c = plain_vgetc();
544 LANGMAP_ADJUST(c, TRUE);
545 --no_zero_mapping;
546 if (*ctrl_w)
547 {
548 --no_mapping;
549 --allow_keys;
550 }
551#ifdef FEAT_CMDL_INFO
552 *need_flushbuf |= add_to_showcmd(c);
553#endif
554 }
555
556 // If we got CTRL-W there may be a/another count
557 if (c == Ctrl_W && !*ctrl_w && cap->oap->op_type == OP_NOP)
558 {
559 *ctrl_w = TRUE;
560 cap->opcount = cap->count0; // remember first count
561 cap->count0 = 0;
562 ++no_mapping;
563 ++allow_keys; // no mapping for nchar, but keys
564 c = plain_vgetc(); // get next character
565 LANGMAP_ADJUST(c, TRUE);
566 --no_mapping;
567 --allow_keys;
568#ifdef FEAT_CMDL_INFO
569 *need_flushbuf |= add_to_showcmd(c);
570#endif
571 goto getcount; // jump back
572 }
573 }
574
575 if (c == K_CURSORHOLD)
576 {
577 // Save the count values so that ca.opcount and ca.count0 are exactly
578 // the same when coming back here after handling K_CURSORHOLD.
579 cap->oap->prev_opcount = cap->opcount;
580 cap->oap->prev_count0 = cap->count0;
581 }
582 else if (cap->opcount != 0)
583 {
584 // If we're in the middle of an operator (including after entering a
585 // yank buffer with '"') AND we had a count before the operator, then
586 // that count overrides the current value of ca.count0.
587 // What this means effectively, is that commands like "3dw" get turned
588 // into "d3w" which makes things fall into place pretty neatly.
589 // If you give a count before AND after the operator, they are
590 // multiplied.
591 if (cap->count0)
592 {
593 if (cap->opcount >= 999999999L / cap->count0)
594 cap->count0 = 999999999L;
595 else
596 cap->count0 *= cap->opcount;
597 }
598 else
599 cap->count0 = cap->opcount;
600 }
601
602 // Always remember the count. It will be set to zero (on the next call,
603 // above) when there is no pending operator.
604 // When called from main(), save the count for use by the "count" built-in
605 // variable.
606 cap->opcount = cap->count0;
607 cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
608
609#ifdef FEAT_EVAL
610 // Only set v:count when called from main() and not a stuffed command.
611 // Do set it for redo.
612 if (toplevel && readbuf1_empty())
613 set_vcount(cap->count0, cap->count1, set_prevcount);
614#endif
615
616 return c;
617}
618
619/*
620 * Returns TRUE if the normal command (cap) needs a second character.
621 */
622 static int
623normal_cmd_needs_more_chars(cmdarg_T *cap, short_u cmd_flags)
624{
625 return ((cmd_flags & NV_NCH)
626 && (((cmd_flags & NV_NCH_NOP) == NV_NCH_NOP
627 && cap->oap->op_type == OP_NOP)
628 || (cmd_flags & NV_NCH_ALW) == NV_NCH_ALW
629 || (cap->cmdchar == 'q'
630 && cap->oap->op_type == OP_NOP
631 && reg_recording == 0
632 && reg_executing == 0)
633 || ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
634 && (cap->oap->op_type != OP_NOP || VIsual_active))));
635}
636
637/*
638 * Get one or more additional characters for a normal command.
639 * Return the updated command index (if changed).
640 */
641 static int
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +0000642normal_cmd_get_more_chars(
643 int idx_arg,
644 cmdarg_T *cap,
645 int *need_flushbuf UNUSED)
Yegappan Lakshmanan46634352022-01-24 16:30:30 +0000646{
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +0000647 int idx = idx_arg;
Yegappan Lakshmanan46634352022-01-24 16:30:30 +0000648 int c;
649 int *cp;
650 int repl = FALSE; // get character for replace mode
651 int lit = FALSE; // get extra character literally
652 int langmap_active = FALSE; // using :lmap mappings
653 int lang; // getting a text character
654#ifdef HAVE_INPUT_METHOD
655 int save_smd; // saved value of p_smd
656#endif
657
658 ++no_mapping;
659 ++allow_keys; // no mapping for nchar, but allow key codes
660 // Don't generate a CursorHold event here, most commands can't handle
661 // it, e.g., nv_replace(), nv_csearch().
662 did_cursorhold = TRUE;
663 if (cap->cmdchar == 'g')
664 {
665 /*
666 * For 'g' get the next character now, so that we can check for
667 * "gr", "g'" and "g`".
668 */
669 cap->nchar = plain_vgetc();
670 LANGMAP_ADJUST(cap->nchar, TRUE);
671#ifdef FEAT_CMDL_INFO
672 *need_flushbuf |= add_to_showcmd(cap->nchar);
673#endif
674 if (cap->nchar == 'r' || cap->nchar == '\'' || cap->nchar == '`'
675 || cap->nchar == Ctrl_BSL)
676 {
677 cp = &cap->extra_char; // need to get a third character
678 if (cap->nchar != 'r')
679 lit = TRUE; // get it literally
680 else
681 repl = TRUE; // get it in replace mode
682 }
683 else
684 cp = NULL; // no third character needed
685 }
686 else
687 {
688 if (cap->cmdchar == 'r') // get it in replace mode
689 repl = TRUE;
690 cp = &cap->nchar;
691 }
692 lang = (repl || (nv_cmds[idx].cmd_flags & NV_LANG));
693
694 /*
695 * Get a second or third character.
696 */
697 if (cp != NULL)
698 {
699 if (repl)
700 {
701 State = REPLACE; // pretend Replace mode
702#ifdef CURSOR_SHAPE
703 ui_cursor_shape(); // show different cursor shape
704#endif
705 }
706 if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP)
707 {
708 // Allow mappings defined with ":lmap".
709 --no_mapping;
710 --allow_keys;
711 if (repl)
712 State = LREPLACE;
713 else
714 State = LANGMAP;
715 langmap_active = TRUE;
716 }
717#ifdef HAVE_INPUT_METHOD
718 save_smd = p_smd;
719 p_smd = FALSE; // Don't let the IM code show the mode here
720 if (lang && curbuf->b_p_iminsert == B_IMODE_IM)
721 im_set_active(TRUE);
722#endif
723 if ((State & INSERT) && !p_ek)
724 {
725#ifdef FEAT_JOB_CHANNEL
726 ch_log_output = TRUE;
727#endif
728 // Disable bracketed paste and modifyOtherKeys here, we won't
729 // recognize the escape sequences with 'esckeys' off.
730 out_str(T_BD);
731 out_str(T_CTE);
732 }
733
734 *cp = plain_vgetc();
735
736 if ((State & INSERT) && !p_ek)
737 {
738#ifdef FEAT_JOB_CHANNEL
739 ch_log_output = TRUE;
740#endif
741 // Re-enable bracketed paste mode and modifyOtherKeys
742 out_str(T_BE);
743 out_str(T_CTI);
744 }
745
746 if (langmap_active)
747 {
748 // Undo the decrement done above
749 ++no_mapping;
750 ++allow_keys;
751 State = NORMAL_BUSY;
752 }
753#ifdef HAVE_INPUT_METHOD
754 if (lang)
755 {
756 if (curbuf->b_p_iminsert != B_IMODE_LMAP)
757 im_save_status(&curbuf->b_p_iminsert);
758 im_set_active(FALSE);
759 }
760 p_smd = save_smd;
761#endif
762 State = NORMAL_BUSY;
763#ifdef FEAT_CMDL_INFO
764 *need_flushbuf |= add_to_showcmd(*cp);
765#endif
766
767 if (!lit)
768 {
769#ifdef FEAT_DIGRAPHS
770 // Typing CTRL-K gets a digraph.
771 if (*cp == Ctrl_K
772 && ((nv_cmds[idx].cmd_flags & NV_LANG)
773 || cp == &cap->extra_char)
774 && vim_strchr(p_cpo, CPO_DIGRAPH) == NULL)
775 {
776 c = get_digraph(FALSE);
777 if (c > 0)
778 {
779 *cp = c;
780# ifdef FEAT_CMDL_INFO
781 // Guessing how to update showcmd here...
782 del_from_showcmd(3);
783 *need_flushbuf |= add_to_showcmd(*cp);
784# endif
785 }
786 }
787#endif
788
789 // adjust chars > 127, except after "tTfFr" commands
790 LANGMAP_ADJUST(*cp, !lang);
791#ifdef FEAT_RIGHTLEFT
792 // adjust Hebrew mapped char
793 if (p_hkmap && lang && KeyTyped)
794 *cp = hkmap(*cp);
795#endif
796 }
797
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +0000798 // When the next character is CTRL-\ a following CTRL-N means the
799 // command is aborted and we go to Normal mode.
Yegappan Lakshmanan46634352022-01-24 16:30:30 +0000800 if (cp == &cap->extra_char
801 && cap->nchar == Ctrl_BSL
802 && (cap->extra_char == Ctrl_N || cap->extra_char == Ctrl_G))
803 {
804 cap->cmdchar = Ctrl_BSL;
805 cap->nchar = cap->extra_char;
806 idx = find_command(cap->cmdchar);
807 }
808 else if ((cap->nchar == 'n' || cap->nchar == 'N') && cap->cmdchar == 'g')
809 cap->oap->op_type = get_op_type(*cp, NUL);
810 else if (*cp == Ctrl_BSL)
811 {
812 long towait = (p_ttm >= 0 ? p_ttm : p_tm);
813
814 // There is a busy wait here when typing "f<C-\>" and then
815 // something different from CTRL-N. Can't be avoided.
816 while ((c = vpeekc()) <= 0 && towait > 0L)
817 {
818 do_sleep(towait > 50L ? 50L : towait, FALSE);
819 towait -= 50L;
820 }
821 if (c > 0)
822 {
823 c = plain_vgetc();
824 if (c != Ctrl_N && c != Ctrl_G)
825 vungetc(c);
826 else
827 {
828 cap->cmdchar = Ctrl_BSL;
829 cap->nchar = c;
830 idx = find_command(cap->cmdchar);
831 }
832 }
833 }
834
835 // When getting a text character and the next character is a
836 // multi-byte character, it could be a composing character.
837 // However, don't wait for it to arrive. Also, do enable mapping,
838 // because if it's put back with vungetc() it's too late to apply
839 // mapping.
840 --no_mapping;
841 while (enc_utf8 && lang && (c = vpeekc()) > 0
842 && (c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1))
843 {
844 c = plain_vgetc();
845 if (!utf_iscomposing(c))
846 {
847 vungetc(c); // it wasn't, put it back
848 break;
849 }
850 else if (cap->ncharC1 == 0)
851 cap->ncharC1 = c;
852 else
853 cap->ncharC2 = c;
854 }
855 ++no_mapping;
856 }
857 --no_mapping;
858 --allow_keys;
859
860 return idx;
861}
862
863/*
864 * Returns TRUE if after processing a normal mode command, need to wait for a
865 * moment when a message is displayed that will be overwritten by the mode
866 * message.
867 */
868 static int
869normal_cmd_need_to_wait_for_msg(cmdarg_T *cap, pos_T *old_pos)
870{
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +0000871 // In Visual mode and with "^O" in Insert mode, a short message will be
872 // overwritten by the mode message. Wait a bit, until a key is hit.
873 // In Visual mode, it's more important to keep the Visual area updated
874 // than keeping a message (e.g. from a /pat search).
875 // Only do this if the command was typed, not from a mapping.
876 // Don't wait when emsg_silent is non-zero.
877 // Also wait a bit after an error message, e.g. for "^O:".
878 // Don't redraw the screen, it would remove the message.
Yegappan Lakshmanan46634352022-01-24 16:30:30 +0000879 return ( ((p_smd
880 && msg_silent == 0
881 && (restart_edit != 0
882 || (VIsual_active
883 && old_pos->lnum == curwin->w_cursor.lnum
884 && old_pos->col == curwin->w_cursor.col)
885 )
886 && (clear_cmdline
887 || redraw_cmdline)
888 && (msg_didout || (msg_didany && msg_scroll))
889 && !msg_nowait
890 && KeyTyped)
891 || (restart_edit != 0
892 && !VIsual_active
893 && (msg_scroll
894 || emsg_on_display)))
895 && cap->oap->regname == 0
896 && !(cap->retval & CA_COMMAND_BUSY)
897 && stuff_empty()
898 && typebuf_typed()
899 && emsg_silent == 0
900 && !in_assert_fails
901 && !did_wait_return
902 && cap->oap->op_type == OP_NOP);
903}
904
905/*
906 * After processing a normal mode command, wait for a moment when a message is
907 * displayed that will be overwritten by the mode message.
908 */
909 static void
910normal_cmd_wait_for_msg(void)
911{
912 int save_State = State;
913
914 // Draw the cursor with the right shape here
915 if (restart_edit != 0)
916 State = INSERT;
917
918 // If need to redraw, and there is a "keep_msg", redraw before the
919 // delay
920 if (must_redraw && keep_msg != NULL && !emsg_on_display)
921 {
922 char_u *kmsg;
923
924 kmsg = keep_msg;
925 keep_msg = NULL;
926 // Showmode() will clear keep_msg, but we want to use it anyway.
927 // First update w_topline.
928 setcursor();
929 update_screen(0);
930 // now reset it, otherwise it's put in the history again
931 keep_msg = kmsg;
932
933 kmsg = vim_strsave(keep_msg);
934 if (kmsg != NULL)
935 {
936 msg_attr((char *)kmsg, keep_msg_attr);
937 vim_free(kmsg);
938 }
939 }
940 setcursor();
941#ifdef CURSOR_SHAPE
942 ui_cursor_shape(); // may show different cursor shape
943#endif
944 cursor_on();
945 out_flush();
946 if (msg_scroll || emsg_on_display)
947 ui_delay(1003L, TRUE); // wait at least one second
948 ui_delay(3003L, FALSE); // wait up to three seconds
949 State = save_State;
950
951 msg_scroll = FALSE;
952 emsg_on_display = FALSE;
953}
954
955/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956 * Execute a command in Normal mode.
957 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958 void
Bram Moolenaar9b578142016-01-30 19:39:49 +0100959normal_cmd(
960 oparg_T *oap,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100961 int toplevel UNUSED) // TRUE when called from main()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000962{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100963 cmdarg_T ca; // command arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964 int c;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100965 int ctrl_w = FALSE; // got CTRL-W command
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 int old_col = curwin->w_curswant;
Yegappan Lakshmanan46634352022-01-24 16:30:30 +0000967 int need_flushbuf = FALSE; // need to call out_flush()
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100968 pos_T old_pos; // cursor position before command
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969 int mapped_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000970 static int old_mapped_len = 0;
971 int idx;
Bram Moolenaar8df74be2008-11-20 15:12:02 +0000972 int set_prevcount = FALSE;
Bram Moolenaarb146e012020-07-19 23:06:05 +0200973 int save_did_cursorhold = did_cursorhold;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000974
Bram Moolenaara80faa82020-04-12 19:37:17 +0200975 CLEAR_FIELD(ca); // also resets ca.retval
Bram Moolenaar071d4272004-06-13 20:20:40 +0000976 ca.oap = oap;
Bram Moolenaara983fe92008-07-31 20:04:27 +0000977
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100978 // Use a count remembered from before entering an operator. After typing
979 // "3d" we return from normal_cmd() and come back here, the "3" is
980 // remembered in "opcount".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000981 ca.opcount = opcount;
982
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +0000983 // If there is an operator pending, then the command we take this time
984 // will terminate it. Finish_op tells us to finish the operation before
985 // returning this time (unless the operation was cancelled).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986#ifdef CURSOR_SHAPE
987 c = finish_op;
988#endif
989 finish_op = (oap->op_type != OP_NOP);
990#ifdef CURSOR_SHAPE
991 if (finish_op != c)
992 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +0100993 ui_cursor_shape(); // may show different cursor shape
Bram Moolenaar071d4272004-06-13 20:20:40 +0000994# ifdef FEAT_MOUSESHAPE
995 update_mouseshape(-1);
996# endif
997 }
998#endif
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +0100999 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001000
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001001 // When not finishing an operator and no register name typed, reset the
1002 // count.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003 if (!finish_op && !oap->regname)
Bram Moolenaar8df74be2008-11-20 15:12:02 +00001004 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001005 ca.opcount = 0;
Bram Moolenaar8df74be2008-11-20 15:12:02 +00001006#ifdef FEAT_EVAL
1007 set_prevcount = TRUE;
1008#endif
1009 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001010
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001011 // Restore counts from before receiving K_CURSORHOLD. This means after
1012 // typing "3", handling K_CURSORHOLD and then typing "2" we get "32", not
1013 // "3 * 2".
Bram Moolenaara983fe92008-07-31 20:04:27 +00001014 if (oap->prev_opcount > 0 || oap->prev_count0 > 0)
1015 {
1016 ca.opcount = oap->prev_opcount;
1017 ca.count0 = oap->prev_count0;
1018 oap->prev_opcount = 0;
1019 oap->prev_count0 = 0;
1020 }
Bram Moolenaara983fe92008-07-31 20:04:27 +00001021
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022 mapped_len = typebuf_maplen();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023
1024 State = NORMAL_BUSY;
1025#ifdef USE_ON_FLY_SCROLL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001026 dont_scroll = FALSE; // allow scrolling here
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027#endif
1028
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001029#ifdef FEAT_EVAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001030 // Set v:count here, when called from main() and not a stuffed
1031 // command, so that v:count can be used in an expression mapping
1032 // when there is no count. Do set it for redo.
Bram Moolenaar0a36fec2014-02-11 15:10:43 +01001033 if (toplevel && readbuf1_empty())
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001034 set_vcount_ca(&ca, &set_prevcount);
1035#endif
1036
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037 /*
1038 * Get the command character from the user.
1039 */
1040 c = safe_vgetc();
Bram Moolenaar25281632016-01-21 23:32:32 +01001041 LANGMAP_ADJUST(c, get_real_state() != SELECTMODE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001043 // If a mapping was started in Visual or Select mode, remember the length
1044 // of the mapping. This is used below to not return to Insert mode for as
1045 // long as the mapping is being executed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046 if (restart_edit == 0)
1047 old_mapped_len = 0;
1048 else if (old_mapped_len
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001049 || (VIsual_active && mapped_len == 0 && typebuf_maplen() > 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 old_mapped_len = typebuf_maplen();
1051
1052 if (c == NUL)
1053 c = K_ZERO;
1054
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001055 // In Select mode, typed text replaces the selection.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001056 if (VIsual_active
1057 && VIsual_select
1058 && (vim_isprintc(c) || c == NL || c == CAR || c == K_KENTER))
1059 {
Bram Moolenaarc88e9772022-01-03 13:47:50 +00001060 int len;
1061
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001062 // Fake a "c"hange command. When "restart_edit" is set (e.g., because
1063 // 'insertmode' is set) fake a "d"elete command, Insert mode will
1064 // restart automatically.
1065 // Insert the typed character in the typeahead buffer, so that it can
1066 // be mapped in Insert mode. Required for ":lmap" to work.
Bram Moolenaarc88e9772022-01-03 13:47:50 +00001067 len = ins_char_typebuf(vgetc_char, vgetc_mod_mask);
1068
zeertzjqfbf4f1c2022-01-28 12:50:43 +00001069 // When recording and gotchars() was called the character will be
1070 // recorded again, remove the previous recording.
1071 if (KeyTyped)
1072 ungetchars(len);
Bram Moolenaarc88e9772022-01-03 13:47:50 +00001073
Bram Moolenaar686f51e2005-05-20 21:19:57 +00001074 if (restart_edit != 0)
1075 c = 'd';
1076 else
1077 c = 'c';
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001078 msg_nowait = TRUE; // don't delay going to insert mode
1079 old_mapped_len = 0; // do go to Insert mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081
Bram Moolenaard0fb9072021-12-09 11:57:22 +00001082 // If the window was made so small that nothing shows, make it at least one
1083 // line and one column when typing a command.
1084 if (KeyTyped && !KeyStuffed)
1085 win_ensure_size();
1086
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087#ifdef FEAT_CMDL_INFO
1088 need_flushbuf = add_to_showcmd(c);
1089#endif
1090
Yegappan Lakshmanan46634352022-01-24 16:30:30 +00001091 // Get the command count
1092 c = normal_cmd_get_count(&ca, c, toplevel, set_prevcount, &ctrl_w,
1093 &need_flushbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001095 // Find the command character in the table of commands.
1096 // For CTRL-W we already got nchar when looking for a count.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097 if (ctrl_w)
1098 {
1099 ca.nchar = c;
1100 ca.cmdchar = Ctrl_W;
1101 }
1102 else
1103 ca.cmdchar = c;
1104 idx = find_command(ca.cmdchar);
1105 if (idx < 0)
1106 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001107 // Not a known command: beep.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 clearopbeep(oap);
1109 goto normal_end;
1110 }
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00001111
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00001112 if (text_locked() && (nv_cmds[idx].cmd_flags & NV_NCW))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001114 // This command is not allowed while editing a cmdline: beep.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001115 clearopbeep(oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00001116 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001117 goto normal_end;
1118 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001119 if ((nv_cmds[idx].cmd_flags & NV_NCW) && curbuf_locked())
1120 goto normal_end;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001122 // In Visual/Select mode, a few keys are handled in a special way.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123 if (VIsual_active)
1124 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001125 // when 'keymodel' contains "stopsel" may stop Select/Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00001126 if (km_stopsel
1127 && (nv_cmds[idx].cmd_flags & NV_STS)
1128 && !(mod_mask & MOD_MASK_SHIFT))
1129 {
1130 end_visual_mode();
1131 redraw_curbuf_later(INVERTED);
1132 }
1133
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001134 // Keys that work different when 'keymodel' contains "startsel"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001135 if (km_startsel)
1136 {
1137 if (nv_cmds[idx].cmd_flags & NV_SS)
1138 {
1139 unshift_special(&ca);
1140 idx = find_command(ca.cmdchar);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001141 if (idx < 0)
1142 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001143 // Just in case
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001144 clearopbeep(oap);
1145 goto normal_end;
1146 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147 }
1148 else if ((nv_cmds[idx].cmd_flags & NV_SSS)
1149 && (mod_mask & MOD_MASK_SHIFT))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150 mod_mask &= ~MOD_MASK_SHIFT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001151 }
1152 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001153
1154#ifdef FEAT_RIGHTLEFT
1155 if (curwin->w_p_rl && KeyTyped && !KeyStuffed
1156 && (nv_cmds[idx].cmd_flags & NV_RL))
1157 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001158 // Invert horizontal movements and operations. Only when typed by the
1159 // user directly, not when the result of a mapping or "x" translated
1160 // to "dl".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001161 switch (ca.cmdchar)
1162 {
1163 case 'l': ca.cmdchar = 'h'; break;
1164 case K_RIGHT: ca.cmdchar = K_LEFT; break;
1165 case K_S_RIGHT: ca.cmdchar = K_S_LEFT; break;
1166 case K_C_RIGHT: ca.cmdchar = K_C_LEFT; break;
1167 case 'h': ca.cmdchar = 'l'; break;
1168 case K_LEFT: ca.cmdchar = K_RIGHT; break;
1169 case K_S_LEFT: ca.cmdchar = K_S_RIGHT; break;
1170 case K_C_LEFT: ca.cmdchar = K_C_RIGHT; break;
1171 case '>': ca.cmdchar = '<'; break;
1172 case '<': ca.cmdchar = '>'; break;
1173 }
1174 idx = find_command(ca.cmdchar);
1175 }
1176#endif
1177
Yegappan Lakshmanan46634352022-01-24 16:30:30 +00001178 // Get additional characters if we need them.
1179 if (normal_cmd_needs_more_chars(&ca, nv_cmds[idx].cmd_flags))
1180 idx = normal_cmd_get_more_chars(idx, &ca, &need_flushbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181
1182#ifdef FEAT_CMDL_INFO
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001183 // Flush the showcmd characters onto the screen so we can see them while
1184 // the command is being executed. Only do this when the shown command was
1185 // actually displayed, otherwise this will slow down a lot when executing
1186 // mappings.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 if (need_flushbuf)
1188 out_flush();
1189#endif
Bram Moolenaard9205ca2008-10-02 20:55:54 +00001190 if (ca.cmdchar != K_IGNORE)
Bram Moolenaar08815a12020-07-20 23:10:56 +02001191 {
1192 if (ex_normal_busy)
1193 did_cursorhold = save_did_cursorhold;
1194 else
1195 did_cursorhold = FALSE;
1196 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197
1198 State = NORMAL;
1199
1200 if (ca.nchar == ESC)
1201 {
1202 clearop(oap);
1203 if (restart_edit == 0 && goto_im())
1204 restart_edit = 'a';
1205 goto normal_end;
1206 }
1207
Bram Moolenaarc0197e22004-09-13 20:26:32 +00001208 if (ca.cmdchar != K_IGNORE)
1209 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001210 msg_didout = FALSE; // don't scroll screen up for normal command
Bram Moolenaarc0197e22004-09-13 20:26:32 +00001211 msg_col = 0;
1212 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001214 old_pos = curwin->w_cursor; // remember where cursor was
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001216 // When 'keymodel' contains "startsel" some keys start Select/Visual
1217 // mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001218 if (!VIsual_active && km_startsel)
1219 {
1220 if (nv_cmds[idx].cmd_flags & NV_SS)
1221 {
1222 start_selection();
1223 unshift_special(&ca);
1224 idx = find_command(ca.cmdchar);
1225 }
1226 else if ((nv_cmds[idx].cmd_flags & NV_SSS)
1227 && (mod_mask & MOD_MASK_SHIFT))
1228 {
1229 start_selection();
1230 mod_mask &= ~MOD_MASK_SHIFT;
1231 }
1232 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001234 // Execute the command!
1235 // Call the command function found in the commands table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001236 ca.arg = nv_cmds[idx].cmd_arg;
1237 (nv_cmds[idx].cmd_func)(&ca);
1238
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001239 // If we didn't start or finish an operator, reset oap->regname, unless we
1240 // need it later.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 if (!finish_op
1242 && !oap->op_type
1243 && (idx < 0 || !(nv_cmds[idx].cmd_flags & NV_KEEPREG)))
1244 {
1245 clearop(oap);
1246#ifdef FEAT_EVAL
Bram Moolenaar439c0362020-06-06 15:58:03 +02001247 reset_reg_var();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248#endif
1249 }
1250
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001251 // Get the length of mapped chars again after typing a count, second
1252 // character or "z333<cr>".
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001253 if (old_mapped_len > 0)
1254 old_mapped_len = typebuf_maplen();
1255
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001256 // If an operation is pending, handle it. But not for K_IGNORE or
1257 // K_MOUSEMOVE.
Bram Moolenaar1ad72c82021-05-04 21:56:28 +02001258 if (ca.cmdchar != K_IGNORE && ca.cmdchar != K_MOUSEMOVE)
Bram Moolenaarfa5612c2019-12-01 19:37:07 +01001259 do_pending_operator(&ca, old_col, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260
Yegappan Lakshmanan46634352022-01-24 16:30:30 +00001261 // Wait for a moment when a message is displayed that will be overwritten
1262 // by the mode message.
1263 if (normal_cmd_need_to_wait_for_msg(&ca, &old_pos))
1264 normal_cmd_wait_for_msg();
1265
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001266 // Finish up after executing a Normal mode command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267normal_end:
1268
1269 msg_nowait = FALSE;
1270
Bram Moolenaarcc613032020-06-07 21:31:18 +02001271#ifdef FEAT_EVAL
1272 if (finish_op)
1273 reset_reg_var();
1274#endif
1275
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001276 // Reset finish_op, in case it was set
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277#ifdef CURSOR_SHAPE
1278 c = finish_op;
1279#endif
1280 finish_op = FALSE;
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001281 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001282#ifdef CURSOR_SHAPE
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001283 // Redraw the cursor with another shape, if we were in Operator-pending
1284 // mode or did a replace command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285 if (c || ca.cmdchar == 'r')
1286 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001287 ui_cursor_shape(); // may show different cursor shape
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288# ifdef FEAT_MOUSESHAPE
1289 update_mouseshape(-1);
1290# endif
1291 }
1292#endif
1293
1294#ifdef FEAT_CMDL_INFO
Bram Moolenaara983fe92008-07-31 20:04:27 +00001295 if (oap->op_type == OP_NOP && oap->regname == 0
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001296 && ca.cmdchar != K_CURSORHOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 clear_showcmd();
1298#endif
1299
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001300 checkpcmark(); // check if we moved since setting pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001301 vim_free(ca.searchbuf);
1302
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 if (has_mbyte)
1304 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001305
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 if (curwin->w_p_scb && toplevel)
1307 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001308 validate_cursor(); // may need to update w_leftcol
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 do_check_scrollbind(TRUE);
1310 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311
Bram Moolenaar860cae12010-06-05 23:22:07 +02001312 if (curwin->w_p_crb && toplevel)
1313 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001314 validate_cursor(); // may need to update w_leftcol
Bram Moolenaar860cae12010-06-05 23:22:07 +02001315 do_check_cursorbind();
1316 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001317
Bram Moolenaar6fe15bb2017-08-16 21:09:18 +02001318#ifdef FEAT_TERMINAL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001319 // don't go to Insert mode if a terminal has a running job
Bram Moolenaareef9add2017-09-16 15:38:04 +02001320 if (term_job_running(curbuf->b_term))
Bram Moolenaar6fe15bb2017-08-16 21:09:18 +02001321 restart_edit = 0;
1322#endif
1323
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001324 // May restart edit(), if we got here with CTRL-O in Insert mode (but not
1325 // if still inside a mapping that started in Visual mode).
1326 // May switch from Visual to Select mode after CTRL-O command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 if ( oap->op_type == OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00001328 && ((restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
1329 || restart_VIsual_select == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 && !(ca.retval & CA_COMMAND_BUSY)
1331 && stuff_empty()
1332 && oap->regname == 0)
1333 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001334 if (restart_VIsual_select == 1)
1335 {
1336 VIsual_select = TRUE;
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001337 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001338 showmode();
1339 restart_VIsual_select = 0;
Shougo Matsushita4ede01f2022-01-20 15:26:03 +00001340 VIsual_select_reg = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001342 if (restart_edit != 0 && !VIsual_active && old_mapped_len == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343 (void)edit(restart_edit, FALSE, 1L);
1344 }
1345
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 if (restart_VIsual_select == 2)
1347 restart_VIsual_select = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001348
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001349 // Save count before an operator for next time.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001350 opcount = ca.opcount;
1351}
1352
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001353#ifdef FEAT_EVAL
1354/*
1355 * Set v:count and v:count1 according to "cap".
1356 * Set v:prevcount only when "set_prevcount" is TRUE.
1357 */
1358 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001359set_vcount_ca(cmdarg_T *cap, int *set_prevcount)
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001360{
1361 long count = cap->count0;
1362
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001363 // multiply with cap->opcount the same way as above
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001364 if (cap->opcount != 0)
1365 count = cap->opcount * (count == 0 ? 1 : count);
1366 set_vcount(count, count == 0 ? 1 : count, *set_prevcount);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001367 *set_prevcount = FALSE; // only set v:prevcount once
Bram Moolenaarf82a2d22010-12-17 18:53:01 +01001368}
1369#endif
1370
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371/*
Bram Moolenaar5715b312020-03-16 22:08:45 +01001372 * Check if highlighting for Visual mode is possible, give a warning message
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 * if not.
1374 */
1375 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001376check_visual_highlight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377{
1378 static int did_check = FALSE;
1379
1380 if (full_screen)
1381 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01001382 if (!did_check && HL_ATTR(HLF_V) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001383 msg(_("Warning: terminal cannot highlight"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384 did_check = TRUE;
1385 }
1386}
1387
Bram Moolenaarfccbf062020-11-26 20:34:00 +01001388#if defined(FEAT_CLIPBOARD) && defined(FEAT_EVAL)
1389/*
1390 * Call yank_do_autocmd() for "regname".
1391 */
1392 static void
1393call_yank_do_autocmd(int regname)
1394{
1395 oparg_T oa;
1396 yankreg_T *reg;
1397
1398 clear_oparg(&oa);
1399 oa.regname = regname;
1400 oa.op_type = OP_YANK;
1401 oa.is_VIsual = TRUE;
1402 reg = get_register(regname, TRUE);
1403 yank_do_autocmd(&oa, reg);
1404 free_register(reg);
1405}
1406#endif
1407
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408/*
Bram Moolenaar66fa2712006-01-22 23:22:22 +00001409 * End Visual mode.
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001410 * This function or the next should ALWAYS be called to end Visual mode, except
1411 * from do_pending_operator().
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 */
1413 void
Bram Moolenaar4f3c57f2021-06-03 22:11:08 +02001414end_visual_mode()
1415{
1416 end_visual_mode_keep_button();
1417 reset_held_button();
1418}
1419
1420 void
1421end_visual_mode_keep_button()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422{
1423#ifdef FEAT_CLIPBOARD
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001424 // If we are using the clipboard, then remember what was selected in case
1425 // we need to paste it somewhere while we still own the selection.
1426 // Only do this when the clipboard is already owned. Don't want to grab
1427 // the selection when hitting ESC.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 if (clip_star.available && clip_star.owned)
1429 clip_auto_select();
Bram Moolenaarfccbf062020-11-26 20:34:00 +01001430
1431# if defined(FEAT_EVAL)
1432 // Emit a TextYankPost for the automatic copy of the selection into the
1433 // star and/or plus register.
1434 if (has_textyankpost())
1435 {
1436 if (clip_isautosel_star())
1437 call_yank_do_autocmd('*');
1438 if (clip_isautosel_plus())
1439 call_yank_do_autocmd('+');
1440 }
1441# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442#endif
1443
1444 VIsual_active = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 setmouse();
1446 mouse_dragging = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001448 // Save the current VIsual area for '< and '> marks, and "gv"
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001449 curbuf->b_visual.vi_mode = VIsual_mode;
1450 curbuf->b_visual.vi_start = VIsual;
1451 curbuf->b_visual.vi_end = curwin->w_cursor;
1452 curbuf->b_visual.vi_curswant = curwin->w_curswant;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453#ifdef FEAT_EVAL
1454 curbuf->b_visual_mode_eval = VIsual_mode;
1455#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 if (!virtual_active())
1457 curwin->w_cursor.coladd = 0;
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001458 may_clear_cmdline();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459
Bram Moolenaarf193fff2006-04-27 00:02:13 +00001460 adjust_cursor_eol();
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01001461 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001462}
1463
1464/*
1465 * Reset VIsual_active and VIsual_reselect.
1466 */
1467 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001468reset_VIsual_and_resel(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469{
1470 if (VIsual_active)
1471 {
1472 end_visual_mode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001473 redraw_curbuf_later(INVERTED); // delete the inversion later
Bram Moolenaar071d4272004-06-13 20:20:40 +00001474 }
1475 VIsual_reselect = FALSE;
1476}
1477
1478/*
1479 * Reset VIsual_active and VIsual_reselect if it's set.
1480 */
1481 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001482reset_VIsual(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483{
1484 if (VIsual_active)
1485 {
1486 end_visual_mode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001487 redraw_curbuf_later(INVERTED); // delete the inversion later
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 VIsual_reselect = FALSE;
1489 }
1490}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02001492 void
1493restore_visual_mode(void)
1494{
1495 if (VIsual_mode_orig != NUL)
1496 {
1497 curbuf->b_visual.vi_mode = VIsual_mode_orig;
1498 VIsual_mode_orig = NUL;
1499 }
1500}
1501
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502/*
1503 * Check for a balloon-eval special item to include when searching for an
1504 * identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
1505 * Returns TRUE if the character at "*ptr" should be included.
1506 * "dir" is FORWARD or BACKWARD, the direction of searching.
1507 * "*colp" is in/decremented if "ptr[-dir]" should also be included.
1508 * "bnp" points to a counter for square brackets.
1509 */
1510 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001511find_is_eval_item(
1512 char_u *ptr,
1513 int *colp,
1514 int *bnp,
1515 int dir)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001517 // Accept everything inside [].
Bram Moolenaar071d4272004-06-13 20:20:40 +00001518 if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD))
1519 ++*bnp;
1520 if (*bnp > 0)
1521 {
1522 if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD))
1523 --*bnp;
1524 return TRUE;
1525 }
1526
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001527 // skip over "s.var"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001528 if (*ptr == '.')
1529 return TRUE;
1530
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001531 // two-character item: s->var
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532 if (ptr[dir == BACKWARD ? 0 : 1] == '>'
1533 && ptr[dir == BACKWARD ? -1 : 0] == '-')
1534 {
1535 *colp += dir;
1536 return TRUE;
1537 }
1538 return FALSE;
1539}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540
1541/*
1542 * Find the identifier under or to the right of the cursor.
1543 * "find_type" can have one of three values:
1544 * FIND_IDENT: find an identifier (keyword)
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001545 * FIND_STRING: find any non-white text
1546 * FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
Bram Moolenaar52b4b552005-03-07 23:00:57 +00001547 * FIND_EVAL: find text useful for C program debugging
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 *
1549 * There are three steps:
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001550 * 1. Search forward for the start of an identifier/text. Doesn't move if
Bram Moolenaar071d4272004-06-13 20:20:40 +00001551 * already on one.
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001552 * 2. Search backward for the start of this identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 * This doesn't match the real Vi but I like it a little better and it
1554 * shouldn't bother anyone.
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001555 * 3. Search forward to the end of this identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556 * When FIND_IDENT isn't defined, we backup until a blank.
1557 *
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001558 * Returns the length of the text, or zero if no text is found.
1559 * If text is found, a pointer to the text is put in "*text". This
1560 * points into the current buffer line and is not always NUL terminated.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001561 */
1562 int
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001563find_ident_under_cursor(char_u **text, int find_type)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564{
1565 return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001566 curwin->w_cursor.col, text, NULL, find_type);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001567}
1568
1569/*
1570 * Like find_ident_under_cursor(), but for any window and any position.
1571 * However: Uses 'iskeyword' from the current window!.
1572 */
1573 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001574find_ident_at_pos(
1575 win_T *wp,
1576 linenr_T lnum,
1577 colnr_T startcol,
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001578 char_u **text,
1579 int *textcol, // column where "text" starts, can be NULL
Bram Moolenaar9b578142016-01-30 19:39:49 +01001580 int find_type)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001581{
1582 char_u *ptr;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001583 int col = 0; // init to shut up GCC
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001585 int this_class = 0;
1586 int prev_class;
1587 int prevcol;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001588 int bn = 0; // bracket nesting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001589
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00001590 // if i == 0: try to find an identifier
1591 // if i == 1: try to find any non-white text
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592 ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
1593 for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
1594 {
1595 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001596 * 1. skip to start of identifier/text
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597 */
1598 col = startcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 if (has_mbyte)
1600 {
1601 while (ptr[col] != NUL)
1602 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001603 // Stop at a ']' to evaluate "a[x]".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001604 if ((find_type & FIND_EVAL) && ptr[col] == ']')
1605 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001606 this_class = mb_get_class(ptr + col);
1607 if (this_class != 0 && (i == 1 || this_class != 1))
1608 break;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001609 col += (*mb_ptr2len)(ptr + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610 }
1611 }
1612 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001613 while (ptr[col] != NUL
Bram Moolenaar1c465442017-03-12 20:10:05 +01001614 && (i == 0 ? !vim_iswordc(ptr[col]) : VIM_ISWHITE(ptr[col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615 && (!(find_type & FIND_EVAL) || ptr[col] != ']')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 )
1617 ++col;
1618
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001619 // When starting on a ']' count it, so that we include the '['.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620 bn = ptr[col] == ']';
Bram Moolenaar071d4272004-06-13 20:20:40 +00001621
1622 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001623 * 2. Back up to start of identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001625 if (has_mbyte)
1626 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001627 // Remember class of character under cursor.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001628 if ((find_type & FIND_EVAL) && ptr[col] == ']')
1629 this_class = mb_get_class((char_u *)"a");
1630 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631 this_class = mb_get_class(ptr + col);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001632 while (col > 0 && this_class != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633 {
1634 prevcol = col - 1 - (*mb_head_off)(ptr, ptr + col - 1);
1635 prev_class = mb_get_class(ptr + prevcol);
1636 if (this_class != prev_class
1637 && (i == 0
1638 || prev_class == 0
1639 || (find_type & FIND_IDENT))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 && (!(find_type & FIND_EVAL)
1641 || prevcol == 0
1642 || !find_is_eval_item(ptr + prevcol, &prevcol,
1643 &bn, BACKWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001644 )
1645 break;
1646 col = prevcol;
1647 }
1648
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001649 // If we don't want just any old text, or we've found an
1650 // identifier, stop searching.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001651 if (this_class > 2)
1652 this_class = 2;
1653 if (!(find_type & FIND_STRING) || this_class == 2)
1654 break;
1655 }
1656 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001657 {
1658 while (col > 0
1659 && ((i == 0
1660 ? vim_iswordc(ptr[col - 1])
Bram Moolenaar1c465442017-03-12 20:10:05 +01001661 : (!VIM_ISWHITE(ptr[col - 1])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662 && (!(find_type & FIND_IDENT)
1663 || !vim_iswordc(ptr[col - 1]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 || ((find_type & FIND_EVAL)
1665 && col > 1
1666 && find_is_eval_item(ptr + col - 1, &col,
1667 &bn, BACKWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668 ))
1669 --col;
1670
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001671 // If we don't want just any old text, or we've found an
1672 // identifier, stop searching.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001673 if (!(find_type & FIND_STRING) || vim_iswordc(ptr[col]))
1674 break;
1675 }
1676 }
1677
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001678 if (ptr[col] == NUL || (i == 0
1679 && (has_mbyte ? this_class != 2 : !vim_iswordc(ptr[col]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001681 // didn't find an identifier or text
Bram Moolenaar17627312019-06-02 19:53:44 +02001682 if ((find_type & FIND_NOERROR) == 0)
1683 {
1684 if (find_type & FIND_STRING)
Bram Moolenaareaaac012022-01-02 17:00:40 +00001685 emsg(_(e_no_string_under_cursor));
Bram Moolenaar17627312019-06-02 19:53:44 +02001686 else
Bram Moolenaareaaac012022-01-02 17:00:40 +00001687 emsg(_(e_no_identifier_under_cursor));
Bram Moolenaar17627312019-06-02 19:53:44 +02001688 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001689 return 0;
1690 }
1691 ptr += col;
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001692 *text = ptr;
1693 if (textcol != NULL)
1694 *textcol = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001695
1696 /*
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001697 * 3. Find the end if the identifier/text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001699 bn = 0;
1700 startcol -= col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001702 if (has_mbyte)
1703 {
Bram Moolenaar7ba343e2019-07-09 23:22:15 +02001704 // Search for point of changing multibyte character class.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705 this_class = mb_get_class(ptr);
1706 while (ptr[col] != NUL
1707 && ((i == 0 ? mb_get_class(ptr + col) == this_class
1708 : mb_get_class(ptr + col) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001709 || ((find_type & FIND_EVAL)
1710 && col <= (int)startcol
1711 && find_is_eval_item(ptr + col, &col, &bn, FORWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712 ))
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001713 col += (*mb_ptr2len)(ptr + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 }
1715 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001716 while ((i == 0 ? vim_iswordc(ptr[col])
Bram Moolenaar1c465442017-03-12 20:10:05 +01001717 : (ptr[col] != NUL && !VIM_ISWHITE(ptr[col])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718 || ((find_type & FIND_EVAL)
1719 && col <= (int)startcol
1720 && find_is_eval_item(ptr + col, &col, &bn, FORWARD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722 ++col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723
1724 return col;
1725}
1726
1727/*
1728 * Prepare for redo of a normal command.
1729 */
1730 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001731prep_redo_cmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732{
1733 prep_redo(cap->oap->regname, cap->count0,
1734 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
1735}
1736
1737/*
1738 * Prepare for redo of any command.
1739 * Note that only the last argument can be a multi-byte char.
1740 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001741 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001742prep_redo(
1743 int regname,
1744 long num,
1745 int cmd1,
1746 int cmd2,
1747 int cmd3,
1748 int cmd4,
1749 int cmd5)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750{
Bram Moolenaar2228cd72021-11-22 14:16:08 +00001751 prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5);
1752}
1753
1754/*
1755 * Prepare for redo of any command with extra count after "cmd2".
1756 */
1757 void
1758prep_redo_num2(
1759 int regname,
1760 long num1,
1761 int cmd1,
1762 int cmd2,
1763 long num2,
1764 int cmd3,
1765 int cmd4,
1766 int cmd5)
1767{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768 ResetRedobuff();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001769 if (regname != 0) // yank from specified buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770 {
1771 AppendCharToRedobuff('"');
1772 AppendCharToRedobuff(regname);
1773 }
Bram Moolenaar2228cd72021-11-22 14:16:08 +00001774 if (num1 != 0)
1775 AppendNumberToRedobuff(num1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776 if (cmd1 != NUL)
1777 AppendCharToRedobuff(cmd1);
1778 if (cmd2 != NUL)
1779 AppendCharToRedobuff(cmd2);
Bram Moolenaar2228cd72021-11-22 14:16:08 +00001780 if (num2 != 0)
1781 AppendNumberToRedobuff(num2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 if (cmd3 != NUL)
1783 AppendCharToRedobuff(cmd3);
1784 if (cmd4 != NUL)
1785 AppendCharToRedobuff(cmd4);
1786 if (cmd5 != NUL)
1787 AppendCharToRedobuff(cmd5);
1788}
1789
1790/*
1791 * check for operator active and clear it
1792 *
1793 * return TRUE if operator was active
1794 */
1795 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001796checkclearop(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797{
1798 if (oap->op_type == OP_NOP)
1799 return FALSE;
1800 clearopbeep(oap);
1801 return TRUE;
1802}
1803
1804/*
Bram Moolenaarc980de32007-05-06 11:59:04 +00001805 * Check for operator or Visual active. Clear active operator.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 *
Bram Moolenaarc980de32007-05-06 11:59:04 +00001807 * Return TRUE if operator or Visual was active.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808 */
1809 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001810checkclearopq(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001812 if (oap->op_type == OP_NOP && !VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813 return FALSE;
1814 clearopbeep(oap);
1815 return TRUE;
1816}
1817
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001818 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001819clearop(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820{
1821 oap->op_type = OP_NOP;
1822 oap->regname = 0;
1823 oap->motion_force = NUL;
1824 oap->use_reg_one = FALSE;
Bram Moolenaar21492742021-06-04 21:57:57 +02001825 motion_force = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826}
1827
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02001828 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001829clearopbeep(oparg_T *oap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830{
1831 clearop(oap);
1832 beep_flush();
1833}
1834
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835/*
1836 * Remove the shift modifier from a special key.
1837 */
1838 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001839unshift_special(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001840{
1841 switch (cap->cmdchar)
1842 {
1843 case K_S_RIGHT: cap->cmdchar = K_RIGHT; break;
1844 case K_S_LEFT: cap->cmdchar = K_LEFT; break;
1845 case K_S_UP: cap->cmdchar = K_UP; break;
1846 case K_S_DOWN: cap->cmdchar = K_DOWN; break;
1847 case K_S_HOME: cap->cmdchar = K_HOME; break;
1848 case K_S_END: cap->cmdchar = K_END; break;
1849 }
1850 cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
1851}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001853/*
1854 * If the mode is currently displayed clear the command line or update the
1855 * command displayed.
1856 */
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02001857 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001858may_clear_cmdline(void)
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001859{
1860 if (mode_displayed)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001861 clear_cmdline = TRUE; // unshow visual mode later
Bram Moolenaar0bbcb5c2015-08-04 19:18:52 +02001862#ifdef FEAT_CMDL_INFO
1863 else
1864 clear_showcmd();
1865#endif
1866}
1867
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868#if defined(FEAT_CMDL_INFO) || defined(PROTO)
1869/*
1870 * Routines for displaying a partly typed command
1871 */
1872
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01001873#define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
Bram Moolenaar071d4272004-06-13 20:20:40 +00001874static char_u showcmd_buf[SHOWCMD_BUFLEN];
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001875static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876static int showcmd_is_clear = TRUE;
1877static int showcmd_visual = FALSE;
1878
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01001879static void display_showcmd(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880
1881 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01001882clear_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001883{
1884 if (!p_sc)
1885 return;
1886
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887 if (VIsual_active && !char_avail())
1888 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001889 int cursor_bot = LT_POS(VIsual, curwin->w_cursor);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 long lines;
1891 colnr_T leftcol, rightcol;
1892 linenr_T top, bot;
1893
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001894 // Show the size of the Visual area.
Bram Moolenaar81d00072009-04-29 15:41:40 +00001895 if (cursor_bot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896 {
1897 top = VIsual.lnum;
1898 bot = curwin->w_cursor.lnum;
1899 }
1900 else
1901 {
1902 top = curwin->w_cursor.lnum;
1903 bot = VIsual.lnum;
1904 }
1905# ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001906 // Include closed folds as a whole.
Bram Moolenaarcde88542015-08-11 19:14:00 +02001907 (void)hasFolding(top, &top, NULL);
1908 (void)hasFolding(bot, NULL, &bot);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909# endif
1910 lines = bot - top + 1;
1911
1912 if (VIsual_mode == Ctrl_V)
1913 {
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001914# ifdef FEAT_LINEBREAK
Bram Moolenaar81d00072009-04-29 15:41:40 +00001915 char_u *saved_sbr = p_sbr;
Bram Moolenaaree857022019-11-09 23:26:40 +01001916 char_u *saved_w_sbr = curwin->w_p_sbr;
Bram Moolenaar81d00072009-04-29 15:41:40 +00001917
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001918 // Make 'sbr' empty for a moment to get the correct size.
Bram Moolenaar81d00072009-04-29 15:41:40 +00001919 p_sbr = empty_option;
Bram Moolenaaree857022019-11-09 23:26:40 +01001920 curwin->w_p_sbr = empty_option;
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001921# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001922 getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001923# ifdef FEAT_LINEBREAK
Bram Moolenaar81d00072009-04-29 15:41:40 +00001924 p_sbr = saved_sbr;
Bram Moolenaaree857022019-11-09 23:26:40 +01001925 curwin->w_p_sbr = saved_w_sbr;
Bram Moolenaarfdf732e2010-07-18 14:20:35 +02001926# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001927 sprintf((char *)showcmd_buf, "%ldx%ld", lines,
1928 (long)(rightcol - leftcol + 1));
1929 }
1930 else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum)
1931 sprintf((char *)showcmd_buf, "%ld", lines);
1932 else
Bram Moolenaarf91787c2010-07-17 12:47:16 +02001933 {
1934 char_u *s, *e;
1935 int l;
1936 int bytes = 0;
1937 int chars = 0;
1938
1939 if (cursor_bot)
1940 {
1941 s = ml_get_pos(&VIsual);
1942 e = ml_get_cursor();
1943 }
1944 else
1945 {
1946 s = ml_get_cursor();
1947 e = ml_get_pos(&VIsual);
1948 }
1949 while ((*p_sel != 'e') ? s <= e : s < e)
1950 {
1951 l = (*mb_ptr2len)(s);
1952 if (l == 0)
1953 {
1954 ++bytes;
1955 ++chars;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001956 break; // end of line
Bram Moolenaarf91787c2010-07-17 12:47:16 +02001957 }
1958 bytes += l;
1959 ++chars;
1960 s += l;
1961 }
1962 if (bytes == chars)
1963 sprintf((char *)showcmd_buf, "%d", chars);
1964 else
1965 sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
1966 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001967 showcmd_buf[SHOWCMD_COLS] = NUL; // truncate
Bram Moolenaar071d4272004-06-13 20:20:40 +00001968 showcmd_visual = TRUE;
1969 }
1970 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 {
1972 showcmd_buf[0] = NUL;
1973 showcmd_visual = FALSE;
1974
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01001975 // Don't actually display something if there is nothing to clear.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976 if (showcmd_is_clear)
1977 return;
1978 }
1979
1980 display_showcmd();
1981}
1982
1983/*
1984 * Add 'c' to string of shown command chars.
1985 * Return TRUE if output has been written (and setcursor() has been called).
1986 */
1987 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01001988add_to_showcmd(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989{
1990 char_u *p;
1991 int old_len;
1992 int extra_len;
1993 int overflow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 int i;
1995 static int ignore[] =
1996 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001997#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998 K_VER_SCROLLBAR, K_HOR_SCROLLBAR,
1999 K_LEFTMOUSE_NM, K_LEFTRELEASE_NM,
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002000#endif
Bram Moolenaarec2da362017-01-21 20:04:22 +01002001 K_IGNORE, K_PS,
Bram Moolenaar51b0f372017-11-18 18:52:04 +01002002 K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
2004 K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +02002005 K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002006 K_X1MOUSE, K_X1DRAG, K_X1RELEASE, K_X2MOUSE, K_X2DRAG, K_X2RELEASE,
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00002007 K_CURSORHOLD,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008 0
2009 };
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010
Bram Moolenaar09df3122006-01-23 22:23:09 +00002011 if (!p_sc || msg_silent != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 return FALSE;
2013
2014 if (showcmd_visual)
2015 {
2016 showcmd_buf[0] = NUL;
2017 showcmd_visual = FALSE;
2018 }
2019
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002020 // Ignore keys that are scrollbar updates and mouse clicks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 if (IS_SPECIAL(c))
2022 for (i = 0; ignore[i] != 0; ++i)
2023 if (ignore[i] == c)
2024 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025
2026 p = transchar(c);
Bram Moolenaar7ba07412013-12-11 14:55:01 +01002027 if (*p == ' ')
2028 STRCPY(p, "<20>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002029 old_len = (int)STRLEN(showcmd_buf);
2030 extra_len = (int)STRLEN(p);
2031 overflow = old_len + extra_len - SHOWCMD_COLS;
2032 if (overflow > 0)
Bram Moolenaarb0db5692007-08-14 20:54:49 +00002033 mch_memmove(showcmd_buf, showcmd_buf + overflow,
2034 old_len - overflow + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 STRCAT(showcmd_buf, p);
2036
2037 if (char_avail())
2038 return FALSE;
2039
2040 display_showcmd();
2041
2042 return TRUE;
2043}
2044
2045 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002046add_to_showcmd_c(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047{
2048 if (!add_to_showcmd(c))
2049 setcursor();
2050}
2051
2052/*
2053 * Delete 'len' characters from the end of the shown command.
2054 */
2055 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002056del_from_showcmd(int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002057{
2058 int old_len;
2059
2060 if (!p_sc)
2061 return;
2062
2063 old_len = (int)STRLEN(showcmd_buf);
2064 if (len > old_len)
2065 len = old_len;
2066 showcmd_buf[old_len - len] = NUL;
2067
2068 if (!char_avail())
2069 display_showcmd();
2070}
2071
2072/*
2073 * push_showcmd() and pop_showcmd() are used when waiting for the user to type
2074 * something and there is a partial mapping.
2075 */
2076 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002077push_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078{
2079 if (p_sc)
2080 STRCPY(old_showcmd_buf, showcmd_buf);
2081}
2082
2083 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002084pop_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085{
2086 if (!p_sc)
2087 return;
2088
2089 STRCPY(showcmd_buf, old_showcmd_buf);
2090
2091 display_showcmd();
2092}
2093
2094 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002095display_showcmd(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096{
2097 int len;
2098
2099 cursor_off();
2100
2101 len = (int)STRLEN(showcmd_buf);
2102 if (len == 0)
2103 showcmd_is_clear = TRUE;
2104 else
2105 {
2106 screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0);
2107 showcmd_is_clear = FALSE;
2108 }
2109
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002110 // clear the rest of an old message by outputting up to SHOWCMD_COLS
2111 // spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 screen_puts((char_u *)" " + len, (int)Rows - 1, sc_col + len, 0);
2113
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002114 setcursor(); // put cursor back where it belongs
Bram Moolenaar071d4272004-06-13 20:20:40 +00002115}
2116#endif
2117
Bram Moolenaar071d4272004-06-13 20:20:40 +00002118/*
2119 * When "check" is FALSE, prepare for commands that scroll the window.
2120 * When "check" is TRUE, take care of scroll-binding after the window has
2121 * scrolled. Called from normal_cmd() and edit().
2122 */
2123 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002124do_check_scrollbind(int check)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125{
2126 static win_T *old_curwin = NULL;
2127 static linenr_T old_topline = 0;
2128#ifdef FEAT_DIFF
2129 static int old_topfill = 0;
2130#endif
2131 static buf_T *old_buf = NULL;
2132 static colnr_T old_leftcol = 0;
2133
2134 if (check && curwin->w_p_scb)
2135 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002136 // If a ":syncbind" command was just used, don't scroll, only reset
2137 // the values.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002138 if (did_syncbind)
2139 did_syncbind = FALSE;
2140 else if (curwin == old_curwin)
2141 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002142 // Synchronize other windows, as necessary according to
2143 // 'scrollbind'. Don't do this after an ":edit" command, except
2144 // when 'diff' is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145 if ((curwin->w_buffer == old_buf
2146#ifdef FEAT_DIFF
2147 || curwin->w_p_diff
2148#endif
2149 )
2150 && (curwin->w_topline != old_topline
2151#ifdef FEAT_DIFF
2152 || curwin->w_topfill != old_topfill
2153#endif
2154 || curwin->w_leftcol != old_leftcol))
2155 {
2156 check_scrollbind(curwin->w_topline - old_topline,
2157 (long)(curwin->w_leftcol - old_leftcol));
2158 }
2159 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002160 else if (vim_strchr(p_sbo, 'j')) // jump flag set in 'scrollopt'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002161 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002162 // When switching between windows, make sure that the relative
2163 // vertical offset is valid for the new window. The relative
2164 // offset is invalid whenever another 'scrollbind' window has
2165 // scrolled to a point that would force the current window to
2166 // scroll past the beginning or end of its buffer. When the
2167 // resync is performed, some of the other 'scrollbind' windows may
2168 // need to jump so that the current window's relative position is
2169 // visible on-screen.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002170 check_scrollbind(curwin->w_topline - curwin->w_scbind_pos, 0L);
2171 }
2172 curwin->w_scbind_pos = curwin->w_topline;
2173 }
2174
2175 old_curwin = curwin;
2176 old_topline = curwin->w_topline;
2177#ifdef FEAT_DIFF
2178 old_topfill = curwin->w_topfill;
2179#endif
2180 old_buf = curwin->w_buffer;
2181 old_leftcol = curwin->w_leftcol;
2182}
2183
2184/*
2185 * Synchronize any windows that have "scrollbind" set, based on the
2186 * number of rows by which the current window has changed
2187 * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
2188 */
2189 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002190check_scrollbind(linenr_T topline_diff, long leftcol_diff)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191{
2192 int want_ver;
2193 int want_hor;
2194 win_T *old_curwin = curwin;
2195 buf_T *old_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196 int old_VIsual_select = VIsual_select;
2197 int old_VIsual_active = VIsual_active;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002198 colnr_T tgt_leftcol = curwin->w_leftcol;
2199 long topline;
2200 long y;
2201
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002202 // check 'scrollopt' string for vertical and horizontal scroll options
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0);
2204#ifdef FEAT_DIFF
2205 want_ver |= old_curwin->w_p_diff;
2206#endif
2207 want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0));
2208
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002209 // loop through the scrollbound windows and scroll accordingly
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 VIsual_select = VIsual_active = 0;
Bram Moolenaar29323592016-07-24 22:04:11 +02002211 FOR_ALL_WINDOWS(curwin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002212 {
2213 curbuf = curwin->w_buffer;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002214 // skip original window and windows with 'noscrollbind'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 if (curwin != old_curwin && curwin->w_p_scb)
2216 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002217 // do the vertical scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00002218 if (want_ver)
2219 {
2220#ifdef FEAT_DIFF
2221 if (old_curwin->w_p_diff && curwin->w_p_diff)
2222 {
2223 diff_set_topline(old_curwin, curwin);
2224 }
2225 else
2226#endif
2227 {
2228 curwin->w_scbind_pos += topline_diff;
2229 topline = curwin->w_scbind_pos;
2230 if (topline > curbuf->b_ml.ml_line_count)
2231 topline = curbuf->b_ml.ml_line_count;
2232 if (topline < 1)
2233 topline = 1;
2234
2235 y = topline - curwin->w_topline;
2236 if (y > 0)
2237 scrollup(y, FALSE);
2238 else
2239 scrolldown(-y, FALSE);
2240 }
2241
2242 redraw_later(VALID);
2243 cursor_correct();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 curwin->w_redr_status = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002245 }
2246
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002247 // do the horizontal scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00002248 if (want_hor && curwin->w_leftcol != tgt_leftcol)
2249 {
2250 curwin->w_leftcol = tgt_leftcol;
2251 leftcol_changed();
2252 }
2253 }
2254 }
2255
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002256 // reset current-window
Bram Moolenaar071d4272004-06-13 20:20:40 +00002257 VIsual_select = old_VIsual_select;
2258 VIsual_active = old_VIsual_active;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 curwin = old_curwin;
2260 curbuf = old_curbuf;
2261}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262
2263/*
2264 * Command character that's ignored.
2265 * Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
Bram Moolenaar5ea0ac72010-05-07 15:52:08 +02002266 * xon/xoff.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002269nv_ignore(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002270{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002271 cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002272}
2273
2274/*
Bram Moolenaarebefac62005-12-28 22:39:57 +00002275 * Command character that doesn't do anything, but unlike nv_ignore() does
2276 * start edit(). Used for "startinsert" executed while starting up.
2277 */
Bram Moolenaarebefac62005-12-28 22:39:57 +00002278 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002279nv_nop(cmdarg_T *cap UNUSED)
Bram Moolenaarebefac62005-12-28 22:39:57 +00002280{
2281}
2282
2283/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284 * Command character doesn't exist.
2285 */
2286 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002287nv_error(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288{
2289 clearopbeep(cap->oap);
2290}
2291
2292/*
2293 * <Help> and <F1> commands.
2294 */
2295 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002296nv_help(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297{
2298 if (!checkclearopq(cap->oap))
2299 ex_help(NULL);
2300}
2301
2302/*
2303 * CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
2304 */
2305 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002306nv_addsub(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002307{
Bram Moolenaarf2732452018-06-03 14:47:35 +02002308#ifdef FEAT_JOB_CHANNEL
2309 if (bt_prompt(curbuf) && !prompt_curpos_editable())
2310 clearopbeep(cap->oap);
2311 else
2312#endif
Bram Moolenaard79e5502016-01-10 22:13:02 +01002313 if (!VIsual_active && cap->oap->op_type == OP_NOP)
Bram Moolenaar9bb19302015-07-03 12:44:07 +02002314 {
Bram Moolenaaref2b5032016-01-12 22:20:58 +01002315 prep_redo_cmd(cap);
Bram Moolenaard79e5502016-01-10 22:13:02 +01002316 cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
2317 op_addsub(cap->oap, cap->count1, cap->arg);
2318 cap->oap->op_type = OP_NOP;
Bram Moolenaar9bb19302015-07-03 12:44:07 +02002319 }
Bram Moolenaard79e5502016-01-10 22:13:02 +01002320 else if (VIsual_active)
2321 nv_operator(cap);
Bram Moolenaar3a304b22015-06-25 13:57:36 +02002322 else
Bram Moolenaard79e5502016-01-10 22:13:02 +01002323 clearop(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324}
2325
2326/*
2327 * CTRL-F, CTRL-B, etc: Scroll page up or down.
2328 */
2329 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002330nv_page(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002331{
2332 if (!checkclearop(cap->oap))
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002333 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002334 if (mod_mask & MOD_MASK_CTRL)
2335 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002336 // <C-PageUp>: tab page back; <C-PageDown>: tab page forward
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002337 if (cap->arg == BACKWARD)
2338 goto_tabpage(-(int)cap->count1);
2339 else
2340 goto_tabpage((int)cap->count0);
2341 }
2342 else
Bram Moolenaar4033c552017-09-16 20:54:51 +02002343 (void)onepage(cap->arg, cap->count1);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002344 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002345}
2346
2347/*
2348 * Implementation of "gd" and "gD" command.
2349 */
2350 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002351nv_gd(
2352 oparg_T *oap,
2353 int nchar,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002354 int thisblock) // 1 for "1gd" and "1gD"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355{
2356 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002357 char_u *ptr;
2358
Bram Moolenaard9d30582005-05-18 22:10:28 +00002359 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
Bram Moolenaar1538fc32016-04-16 09:13:34 +02002360 || find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)
Bram Moolenaar0c711142021-11-12 10:30:04 +00002361 == FAIL)
2362 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002363 clearopbeep(oap);
Bram Moolenaar0c711142021-11-12 10:30:04 +00002364 }
2365 else
2366 {
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002367#ifdef FEAT_FOLDING
Bram Moolenaar0c711142021-11-12 10:30:04 +00002368 if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP)
2369 foldOpenCursor();
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002370#endif
Bram Moolenaar0c711142021-11-12 10:30:04 +00002371 // clear any search statistics
2372 if (messaging() && !msg_silent && !shortmess(SHM_SEARCHCOUNT))
2373 clear_cmdline = TRUE;
2374 }
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002375}
2376
2377/*
Bram Moolenaar226630a2016-10-08 19:21:31 +02002378 * Return TRUE if line[offset] is not inside a C-style comment or string, FALSE
2379 * otherwise.
2380 */
2381 static int
2382is_ident(char_u *line, int offset)
2383{
2384 int i;
2385 int incomment = FALSE;
2386 int instring = 0;
2387 int prev = 0;
2388
2389 for (i = 0; i < offset && line[i] != NUL; i++)
2390 {
2391 if (instring != 0)
2392 {
2393 if (prev != '\\' && line[i] == instring)
2394 instring = 0;
2395 }
2396 else if ((line[i] == '"' || line[i] == '\'') && !incomment)
2397 {
2398 instring = line[i];
2399 }
2400 else
2401 {
2402 if (incomment)
2403 {
2404 if (prev == '*' && line[i] == '/')
2405 incomment = FALSE;
2406 }
2407 else if (prev == '/' && line[i] == '*')
2408 {
2409 incomment = TRUE;
2410 }
2411 else if (prev == '/' && line[i] == '/')
2412 {
2413 return FALSE;
2414 }
2415 }
2416
2417 prev = line[i];
2418 }
2419
2420 return incomment == FALSE && instring == 0;
2421}
2422
2423/*
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002424 * Search for variable declaration of "ptr[len]".
2425 * When "locally" is TRUE in the current function ("gd"), otherwise in the
2426 * current file ("gD").
2427 * When "thisblock" is TRUE check the {} block scope.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002428 * Return FAIL when not found.
2429 */
2430 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002431find_decl(
2432 char_u *ptr,
2433 int len,
2434 int locally,
2435 int thisblock,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002436 int flags_arg) // flags passed to searchit()
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002437{
2438 char_u *pat;
2439 pos_T old_pos;
2440 pos_T par_pos;
2441 pos_T found_pos;
2442 int t;
2443 int save_p_ws;
2444 int save_p_scs;
2445 int retval = OK;
Bram Moolenaar89d40322006-08-29 15:30:07 +00002446 int incll;
Bram Moolenaar23c60f22016-06-15 22:03:48 +02002447 int searchflags = flags_arg;
Bram Moolenaar226630a2016-10-08 19:21:31 +02002448 int valid;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002449
2450 if ((pat = alloc(len + 7)) == NULL)
2451 return FAIL;
Bram Moolenaard9d30582005-05-18 22:10:28 +00002452
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002453 // Put "\V" before the pattern to avoid that the special meaning of "."
2454 // and "~" causes trouble.
Bram Moolenaard9d30582005-05-18 22:10:28 +00002455 sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
2456 len, ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 old_pos = curwin->w_cursor;
2458 save_p_ws = p_ws;
2459 save_p_scs = p_scs;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002460 p_ws = FALSE; // don't wrap around end of file now
2461 p_scs = FALSE; // don't switch ignorecase off now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002462
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002463 // With "gD" go to line 1.
2464 // With "gd" Search back for the start of the current function, then go
2465 // back until a blank line. If this fails go to line 1.
Bram Moolenaar89d40322006-08-29 15:30:07 +00002466 if (!locally || !findpar(&incll, BACKWARD, 1L, '{', FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002467 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002468 setpcmark(); // Set in findpar() otherwise
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469 curwin->w_cursor.lnum = 1;
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002470 par_pos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471 }
2472 else
2473 {
Bram Moolenaarbb15b652005-10-03 21:52:09 +00002474 par_pos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475 while (curwin->w_cursor.lnum > 1 && *skipwhite(ml_get_curline()) != NUL)
2476 --curwin->w_cursor.lnum;
2477 }
2478 curwin->w_cursor.col = 0;
2479
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002480 // Search forward for the identifier, ignore comment lines.
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002481 CLEAR_POS(&found_pos);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002482 for (;;)
2483 {
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002484 t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02002485 pat, 1L, searchflags, RE_LAST, NULL);
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002486 if (curwin->w_cursor.lnum >= old_pos.lnum)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002487 t = FAIL; // match after start is failure too
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002488
Bram Moolenaar0fd92892006-03-09 22:27:48 +00002489 if (thisblock && t != FAIL)
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002490 {
2491 pos_T *pos;
2492
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002493 // Check that the block the match is in doesn't end before the
2494 // position where we started the search from.
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002495 if ((pos = findmatchlimit(NULL, '}', FM_FORWARD,
2496 (int)(old_pos.lnum - curwin->w_cursor.lnum + 1))) != NULL
2497 && pos->lnum < old_pos.lnum)
Bram Moolenaar60402d62017-04-20 18:54:50 +02002498 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002499 // There can't be a useful match before the end of this block.
2500 // Skip to the end.
Bram Moolenaar60402d62017-04-20 18:54:50 +02002501 curwin->w_cursor = *pos;
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002502 continue;
Bram Moolenaar60402d62017-04-20 18:54:50 +02002503 }
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002504 }
2505
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002506 if (t == FAIL)
2507 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002508 // If we previously found a valid position, use it.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002509 if (found_pos.lnum != 0)
2510 {
2511 curwin->w_cursor = found_pos;
2512 t = OK;
2513 }
2514 break;
2515 }
Bram Moolenaar81340392012-06-06 16:12:59 +02002516 if (get_leader_len(ml_get_curline(), NULL, FALSE, TRUE) > 0)
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002517 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002518 // Ignore this line, continue at start of next line.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002519 ++curwin->w_cursor.lnum;
2520 curwin->w_cursor.col = 0;
2521 continue;
2522 }
Bram Moolenaar226630a2016-10-08 19:21:31 +02002523 valid = is_ident(ml_get_curline(), curwin->w_cursor.col);
2524
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002525 // If the current position is not a valid identifier and a previous
2526 // match is present, favor that one instead.
Bram Moolenaar226630a2016-10-08 19:21:31 +02002527 if (!valid && found_pos.lnum != 0)
2528 {
2529 curwin->w_cursor = found_pos;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002530 break;
Bram Moolenaar226630a2016-10-08 19:21:31 +02002531 }
2532
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002533 // Global search: use first valid match found
Bram Moolenaar226630a2016-10-08 19:21:31 +02002534 if (valid && !locally)
2535 break;
2536 if (valid && curwin->w_cursor.lnum >= par_pos.lnum)
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002537 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002538 // If we previously found a valid position, use it.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002539 if (found_pos.lnum != 0)
2540 curwin->w_cursor = found_pos;
2541 break;
2542 }
2543
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002544 // For finding a local variable and the match is before the "{" or
2545 // inside a comment, continue searching. For K&R style function
2546 // declarations this skips the function header without types.
Bram Moolenaar226630a2016-10-08 19:21:31 +02002547 if (!valid)
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002548 CLEAR_POS(&found_pos);
Bram Moolenaar226630a2016-10-08 19:21:31 +02002549 else
Bram Moolenaar226630a2016-10-08 19:21:31 +02002550 found_pos = curwin->w_cursor;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002551 // Remove SEARCH_START from flags to avoid getting stuck at one
2552 // position.
Bram Moolenaar23c60f22016-06-15 22:03:48 +02002553 searchflags &= ~SEARCH_START;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002554 }
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002555
2556 if (t == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002557 {
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002558 retval = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 curwin->w_cursor = old_pos;
2560 }
2561 else
2562 {
2563 curwin->w_set_curswant = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002564 // "n" searches forward now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002565 reset_search_dir();
2566 }
2567
2568 vim_free(pat);
2569 p_ws = save_p_ws;
2570 p_scs = save_p_scs;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00002571
2572 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573}
2574
2575/*
2576 * Move 'dist' lines in direction 'dir', counting lines by *screen*
2577 * lines rather than lines in the file.
2578 * 'dist' must be positive.
2579 *
2580 * Return OK if able to move cursor, FAIL otherwise.
2581 */
2582 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01002583nv_screengo(oparg_T *oap, int dir, long dist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584{
2585 int linelen = linetabsize(ml_get_curline());
2586 int retval = OK;
2587 int atend = FALSE;
2588 int n;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002589 int col_off1; // margin offset for first screen line
2590 int col_off2; // margin offset for wrapped screen line
2591 int width1; // text width for first screen line
Christian Brabandtaaec1d42021-11-04 13:28:29 +00002592 int width2; // text width for wrapped screen line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593
2594 oap->motion_type = MCHAR;
Bram Moolenaar91b2bdb2013-07-14 13:32:15 +02002595 oap->inclusive = (curwin->w_curswant == MAXCOL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596
2597 col_off1 = curwin_col_off();
2598 col_off2 = col_off1 - curwin_col_off2();
Bram Moolenaar02631462017-09-22 15:20:32 +02002599 width1 = curwin->w_width - col_off1;
2600 width2 = curwin->w_width - col_off2;
Bram Moolenaar7cc8ec42015-01-27 20:59:31 +01002601 if (width2 == 0)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002602 width2 = 1; // avoid divide by zero
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604 if (curwin->w_width != 0)
Bram Moolenaar44a2f922016-03-19 22:11:51 +01002605 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002606 // Instead of sticking at the last character of the buffer line we
2607 // try to stick in the last column of the screen.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002608 if (curwin->w_curswant == MAXCOL)
2609 {
2610 atend = TRUE;
2611 validate_virtcol();
2612 if (width1 <= 0)
2613 curwin->w_curswant = 0;
2614 else
2615 {
2616 curwin->w_curswant = width1 - 1;
2617 if (curwin->w_virtcol > curwin->w_curswant)
2618 curwin->w_curswant += ((curwin->w_virtcol
2619 - curwin->w_curswant - 1) / width2 + 1) * width2;
2620 }
2621 }
2622 else
2623 {
2624 if (linelen > width1)
2625 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
2626 else
2627 n = width1;
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002628 if (curwin->w_curswant >= (colnr_T)n)
2629 curwin->w_curswant = n - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 }
2631
2632 while (dist--)
2633 {
2634 if (dir == BACKWARD)
2635 {
Bram Moolenaar3c49e742021-04-04 21:26:04 +02002636 if ((long)curwin->w_curswant >= width1
2637#ifdef FEAT_FOLDING
2638 && !hasFolding(curwin->w_cursor.lnum, NULL, NULL)
2639#endif
2640 )
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002641 // Move back within the line. This can give a negative value
2642 // for w_curswant if width1 < width2 (with cpoptions+=n),
2643 // which will get clipped to column 0.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002644 curwin->w_curswant -= width2;
2645 else
2646 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002647 // to previous line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002649 // Move to the start of a closed fold. Don't do that when
2650 // 'foldopen' contains "all": it will open in a moment.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651 if (!(fdo_flags & FDO_ALL))
2652 (void)hasFolding(curwin->w_cursor.lnum,
2653 &curwin->w_cursor.lnum, NULL);
2654#endif
Bram Moolenaare71996b2021-01-21 17:03:07 +01002655 if (curwin->w_cursor.lnum == 1)
2656 {
2657 retval = FAIL;
2658 break;
2659 }
2660 --curwin->w_cursor.lnum;
2661
Bram Moolenaar071d4272004-06-13 20:20:40 +00002662 linelen = linetabsize(ml_get_curline());
2663 if (linelen > width1)
2664 curwin->w_curswant += (((linelen - width1 - 1) / width2)
2665 + 1) * width2;
2666 }
2667 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002668 else // dir == FORWARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00002669 {
2670 if (linelen > width1)
2671 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
2672 else
2673 n = width1;
Bram Moolenaar3c49e742021-04-04 21:26:04 +02002674 if (curwin->w_curswant + width2 < (colnr_T)n
2675#ifdef FEAT_FOLDING
2676 && !hasFolding(curwin->w_cursor.lnum, NULL, NULL)
2677#endif
2678 )
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002679 // move forward within line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 curwin->w_curswant += width2;
2681 else
2682 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002683 // to next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002685 // Move to the end of a closed fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 (void)hasFolding(curwin->w_cursor.lnum, NULL,
2687 &curwin->w_cursor.lnum);
2688#endif
2689 if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
2690 {
2691 retval = FAIL;
2692 break;
2693 }
2694 curwin->w_cursor.lnum++;
2695 curwin->w_curswant %= width2;
Bram Moolenaarceba3dd2019-10-12 16:12:54 +02002696 // Check if the cursor has moved below the number display
2697 // when width1 < width2 (with cpoptions+=n). Subtract width2
2698 // to get a negative value for w_curswant, which will get
2699 // clipped to column 0.
2700 if (curwin->w_curswant >= width1)
2701 curwin->w_curswant -= width2;
Bram Moolenaar914968e2011-06-20 00:45:58 +02002702 linelen = linetabsize(ml_get_curline());
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703 }
2704 }
2705 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707
Bram Moolenaar6cd3aee2014-01-14 13:18:58 +01002708 if (virtual_active() && atend)
2709 coladvance(MAXCOL);
2710 else
2711 coladvance(curwin->w_curswant);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
2714 {
Bram Moolenaar773b1582014-08-29 14:20:51 +02002715 colnr_T virtcol;
Christian Brabandtaaec1d42021-11-04 13:28:29 +00002716 int c;
Bram Moolenaar773b1582014-08-29 14:20:51 +02002717
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002718 // Check for landing on a character that got split at the end of the
2719 // last line. We want to advance a screenline, not end up in the same
2720 // screenline or move two screenlines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002721 validate_virtcol();
Bram Moolenaar773b1582014-08-29 14:20:51 +02002722 virtcol = curwin->w_virtcol;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002723#if defined(FEAT_LINEBREAK)
Bram Moolenaaree857022019-11-09 23:26:40 +01002724 if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL)
2725 virtcol -= vim_strsize(get_showbreak_value(curwin));
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002726#endif
Bram Moolenaar773b1582014-08-29 14:20:51 +02002727
Christian Brabandtaaec1d42021-11-04 13:28:29 +00002728 c = (*mb_ptr2char)(ml_get_cursor());
2729 if (dir == FORWARD && virtcol < curwin->w_curswant
2730 && (curwin->w_curswant <= (colnr_T)width1)
2731 && !vim_isprintc(c) && c > 255)
2732 oneright();
2733
Bram Moolenaar773b1582014-08-29 14:20:51 +02002734 if (virtcol > curwin->w_curswant
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 && (curwin->w_curswant < (colnr_T)width1
2736 ? (curwin->w_curswant > (colnr_T)width1 / 2)
2737 : ((curwin->w_curswant - width1) % width2
2738 > (colnr_T)width2 / 2)))
2739 --curwin->w_cursor.col;
2740 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741
2742 if (atend)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002743 curwin->w_curswant = MAXCOL; // stick in the last column
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744
2745 return retval;
2746}
2747
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748/*
2749 * Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
2750 * cap->arg must be TRUE for CTRL-E.
2751 */
Bram Moolenaarb20b9e12019-09-21 20:48:04 +02002752 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002753nv_scroll_line(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754{
2755 if (!checkclearop(cap->oap))
2756 scroll_redraw(cap->arg, cap->count1);
2757}
2758
2759/*
2760 * Scroll "count" lines up or down, and redraw.
2761 */
2762 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002763scroll_redraw(int up, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002764{
2765 linenr_T prev_topline = curwin->w_topline;
2766#ifdef FEAT_DIFF
2767 int prev_topfill = curwin->w_topfill;
2768#endif
2769 linenr_T prev_lnum = curwin->w_cursor.lnum;
2770
2771 if (up)
2772 scrollup(count, TRUE);
2773 else
2774 scrolldown(count, TRUE);
Bram Moolenaar375e3392019-01-31 18:26:10 +01002775 if (get_scrolloff_value())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002776 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002777 // Adjust the cursor position for 'scrolloff'. Mark w_topline as
2778 // valid, otherwise the screen jumps back at the end of the file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002779 cursor_correct();
2780 check_cursor_moved(curwin);
2781 curwin->w_valid |= VALID_TOPLINE;
2782
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002783 // If moved back to where we were, at least move the cursor, otherwise
2784 // we get stuck at one position. Don't move the cursor up if the
2785 // first line of the buffer is already on the screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 while (curwin->w_topline == prev_topline
2787#ifdef FEAT_DIFF
2788 && curwin->w_topfill == prev_topfill
2789#endif
2790 )
2791 {
2792 if (up)
2793 {
2794 if (curwin->w_cursor.lnum > prev_lnum
2795 || cursor_down(1L, FALSE) == FAIL)
2796 break;
2797 }
2798 else
2799 {
2800 if (curwin->w_cursor.lnum < prev_lnum
2801 || prev_topline == 1L
2802 || cursor_up(1L, FALSE) == FAIL)
2803 break;
2804 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002805 // Mark w_topline as valid, otherwise the screen jumps back at the
2806 // end of the file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807 check_cursor_moved(curwin);
2808 curwin->w_valid |= VALID_TOPLINE;
2809 }
2810 }
2811 if (curwin->w_cursor.lnum != prev_lnum)
2812 coladvance(curwin->w_curswant);
2813 redraw_later(VALID);
2814}
2815
2816/*
Yegappan Lakshmananb0ad2d92022-01-27 13:16:59 +00002817 * Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh',
2818 * 'z<Left>', and 'z<Right>' commands accept a count after 'z'.
2819 * Returns TRUE to process the 'z' command and FALSE to skip it.
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002820 */
2821 static int
2822nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
2823{
2824 int nchar = *nchar_arg;
2825 long n;
2826
2827 // "z123{nchar}": edit the count before obtaining {nchar}
2828 if (checkclearop(cap->oap))
2829 return FALSE;
2830 n = nchar - '0';
2831
2832 for (;;)
2833 {
2834#ifdef USE_ON_FLY_SCROLL
2835 dont_scroll = TRUE; // disallow scrolling here
2836#endif
2837 ++no_mapping;
2838 ++allow_keys; // no mapping for nchar, but allow key codes
2839 nchar = plain_vgetc();
2840 LANGMAP_ADJUST(nchar, TRUE);
2841 --no_mapping;
2842 --allow_keys;
2843#ifdef FEAT_CMDL_INFO
2844 (void)add_to_showcmd(nchar);
2845#endif
2846 if (nchar == K_DEL || nchar == K_KDEL)
2847 n /= 10;
2848 else if (VIM_ISDIGIT(nchar))
2849 n = n * 10 + (nchar - '0');
2850 else if (nchar == CAR)
2851 {
2852#ifdef FEAT_GUI
2853 need_mouse_correct = TRUE;
2854#endif
2855 win_setheight((int)n);
2856 break;
2857 }
2858 else if (nchar == 'l'
2859 || nchar == 'h'
2860 || nchar == K_LEFT
2861 || nchar == K_RIGHT)
2862 {
2863 cap->count1 = n ? n * cap->count1 : cap->count1;
2864 *nchar_arg = nchar;
2865 return TRUE;
2866 }
2867 else
2868 {
2869 clearopbeep(cap->oap);
2870 break;
2871 }
2872 }
2873 cap->oap->op_type = OP_NOP;
2874 return FALSE;
2875}
2876
2877#ifdef FEAT_SPELL
2878/*
2879 * "zug" and "zuw": undo "zg" and "zw"
2880 * "zg": add good word to word list
2881 * "zw": add wrong word to word list
2882 * "zG": add good word to temp word list
2883 * "zW": add wrong word to temp word list
2884 */
2885 static int
2886nv_zg_zw(cmdarg_T *cap, int nchar)
2887{
2888 char_u *ptr = NULL;
2889 int len;
2890 int undo = FALSE;
2891
2892 if (nchar == 'u')
2893 {
2894 ++no_mapping;
2895 ++allow_keys; // no mapping for nchar, but allow key codes
2896 nchar = plain_vgetc();
2897 LANGMAP_ADJUST(nchar, TRUE);
2898 --no_mapping;
2899 --allow_keys;
2900#ifdef FEAT_CMDL_INFO
2901 (void)add_to_showcmd(nchar);
2902#endif
2903 if (vim_strchr((char_u *)"gGwW", nchar) == NULL)
2904 {
2905 clearopbeep(cap->oap);
2906 return OK;
2907 }
2908 undo = TRUE;
2909 }
2910
2911 if (checkclearop(cap->oap))
2912 return OK;
2913 if (VIsual_active && get_visual_text(cap, &ptr, &len) == FAIL)
2914 return FAIL;
2915 if (ptr == NULL)
2916 {
2917 pos_T pos = curwin->w_cursor;
2918
2919 // Find bad word under the cursor. When 'spell' is
2920 // off this fails and find_ident_under_cursor() is
2921 // used below.
2922 emsg_off++;
2923 len = spell_move_to(curwin, FORWARD, TRUE, TRUE, NULL);
2924 emsg_off--;
2925 if (len != 0 && curwin->w_cursor.col <= pos.col)
2926 ptr = ml_get_pos(&curwin->w_cursor);
2927 curwin->w_cursor = pos;
2928 }
2929
2930 if (ptr == NULL
2931 && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
2932 return FAIL;
2933 spell_add_word(ptr, len, nchar == 'w' || nchar == 'W'
2934 ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
2935 (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1, undo);
2936
2937 return OK;
2938}
2939#endif
2940
2941/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942 * Commands that start with "z".
2943 */
2944 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01002945nv_zet(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002946{
2947 long n;
2948 colnr_T col;
2949 int nchar = cap->nchar;
2950#ifdef FEAT_FOLDING
2951 long old_fdl = curwin->w_p_fdl;
2952 int old_fen = curwin->w_p_fen;
2953#endif
Bram Moolenaar375e3392019-01-31 18:26:10 +01002954 long siso = get_sidescrolloff_value();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002956 if (VIM_ISDIGIT(nchar) && !nv_z_get_count(cap, &nchar))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 if (
2960#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002961 // "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
2962 // and "zC" only in Visual mode. "zj" and "zk" are motion
2963 // commands.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964 cap->nchar != 'f' && cap->nchar != 'F'
2965 && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
2966 && cap->nchar != 'j' && cap->nchar != 'k'
2967 &&
2968#endif
2969 checkclearop(cap->oap))
2970 return;
2971
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00002972 // For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
2973 // If line number given, set cursor.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002974 if ((vim_strchr((char_u *)"+\r\nt.z^-b", nchar) != NULL)
2975 && cap->count0
2976 && cap->count0 != curwin->w_cursor.lnum)
2977 {
2978 setpcmark();
2979 if (cap->count0 > curbuf->b_ml.ml_line_count)
2980 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2981 else
2982 curwin->w_cursor.lnum = cap->count0;
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002983 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 }
2985
2986 switch (nchar)
2987 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002988 // "z+", "z<CR>" and "zt": put cursor at top of screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989 case '+':
2990 if (cap->count0 == 0)
2991 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002992 // No count given: put cursor at the line below screen
2993 validate_botline(); // make sure w_botline is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 if (curwin->w_botline > curbuf->b_ml.ml_line_count)
2995 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2996 else
2997 curwin->w_cursor.lnum = curwin->w_botline;
2998 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01002999 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003000 case NL:
3001 case CAR:
3002 case K_KENTER:
3003 beginline(BL_WHITE | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003004 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005
3006 case 't': scroll_cursor_top(0, TRUE);
3007 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01003008 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003009 break;
3010
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003011 // "z." and "zz": put cursor in middle of screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 case '.': beginline(BL_WHITE | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003013 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003014
3015 case 'z': scroll_cursor_halfway(TRUE);
3016 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01003017 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018 break;
3019
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003020 // "z^", "z-" and "zb": put cursor at bottom of screen
3021 case '^': // Strange Vi behavior: <count>z^ finds line at top of window
3022 // when <count> is at bottom of window, and puts that one at
3023 // bottom of window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003024 if (cap->count0 != 0)
3025 {
3026 scroll_cursor_bot(0, TRUE);
3027 curwin->w_cursor.lnum = curwin->w_topline;
3028 }
3029 else if (curwin->w_topline == 1)
3030 curwin->w_cursor.lnum = 1;
3031 else
3032 curwin->w_cursor.lnum = curwin->w_topline - 1;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003033 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003034 case '-':
3035 beginline(BL_WHITE | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003036 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037
3038 case 'b': scroll_cursor_bot(0, TRUE);
3039 redraw_later(VALID);
Bram Moolenaar9dc2ce32015-12-05 19:47:04 +01003040 set_fraction(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003041 break;
3042
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003043 // "zH" - scroll screen right half-page
Bram Moolenaar071d4272004-06-13 20:20:40 +00003044 case 'H':
Bram Moolenaar02631462017-09-22 15:20:32 +02003045 cap->count1 *= curwin->w_width / 2;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003046 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003048 // "zh" - scroll screen to the right
Bram Moolenaar071d4272004-06-13 20:20:40 +00003049 case 'h':
3050 case K_LEFT:
3051 if (!curwin->w_p_wrap)
3052 {
3053 if ((colnr_T)cap->count1 > curwin->w_leftcol)
3054 curwin->w_leftcol = 0;
3055 else
3056 curwin->w_leftcol -= (colnr_T)cap->count1;
3057 leftcol_changed();
3058 }
3059 break;
3060
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003061 // "zL" - scroll screen left half-page
Bram Moolenaar02631462017-09-22 15:20:32 +02003062 case 'L': cap->count1 *= curwin->w_width / 2;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003063 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003065 // "zl" - scroll screen to the left
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 case 'l':
3067 case K_RIGHT:
3068 if (!curwin->w_p_wrap)
3069 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003070 // scroll the window left
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071 curwin->w_leftcol += (colnr_T)cap->count1;
3072 leftcol_changed();
3073 }
3074 break;
3075
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003076 // "zs" - scroll screen, cursor at the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077 case 's': if (!curwin->w_p_wrap)
3078 {
3079#ifdef FEAT_FOLDING
3080 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003081 col = 0; // like the cursor is in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082 else
3083#endif
3084 getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
Bram Moolenaar375e3392019-01-31 18:26:10 +01003085 if ((long)col > siso)
3086 col -= siso;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087 else
3088 col = 0;
3089 if (curwin->w_leftcol != col)
3090 {
3091 curwin->w_leftcol = col;
3092 redraw_later(NOT_VALID);
3093 }
3094 }
3095 break;
3096
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003097 // "ze" - scroll screen, cursor at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003098 case 'e': if (!curwin->w_p_wrap)
3099 {
3100#ifdef FEAT_FOLDING
3101 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003102 col = 0; // like the cursor is in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003103 else
3104#endif
3105 getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
Bram Moolenaar02631462017-09-22 15:20:32 +02003106 n = curwin->w_width - curwin_col_off();
Bram Moolenaar375e3392019-01-31 18:26:10 +01003107 if ((long)col + siso < n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003108 col = 0;
3109 else
Bram Moolenaar375e3392019-01-31 18:26:10 +01003110 col = col + siso - n + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003111 if (curwin->w_leftcol != col)
3112 {
3113 curwin->w_leftcol = col;
3114 redraw_later(NOT_VALID);
3115 }
3116 }
3117 break;
3118
Christian Brabandt2fa93842021-05-30 22:17:25 +02003119 // "zp", "zP" in block mode put without addind trailing spaces
3120 case 'P':
3121 case 'p': nv_put(cap);
3122 break;
Christian Brabandt544a38e2021-06-10 19:39:11 +02003123 // "zy" Yank without trailing spaces
3124 case 'y': nv_operator(cap);
3125 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003126#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003127 // "zF": create fold command
3128 // "zf": create fold operator
Bram Moolenaar071d4272004-06-13 20:20:40 +00003129 case 'F':
3130 case 'f': if (foldManualAllowed(TRUE))
3131 {
3132 cap->nchar = 'f';
3133 nv_operator(cap);
3134 curwin->w_p_fen = TRUE;
3135
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003136 // "zF" is like "zfzf"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003137 if (nchar == 'F' && cap->oap->op_type == OP_FOLD)
3138 {
3139 nv_operator(cap);
3140 finish_op = TRUE;
3141 }
3142 }
3143 else
3144 clearopbeep(cap->oap);
3145 break;
3146
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003147 // "zd": delete fold at cursor
3148 // "zD": delete fold at cursor recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00003149 case 'd':
3150 case 'D': if (foldManualAllowed(FALSE))
3151 {
3152 if (VIsual_active)
3153 nv_operator(cap);
3154 else
3155 deleteFold(curwin->w_cursor.lnum,
3156 curwin->w_cursor.lnum, nchar == 'D', FALSE);
3157 }
3158 break;
3159
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003160 // "zE": erase all folds
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161 case 'E': if (foldmethodIsManual(curwin))
3162 {
3163 clearFolding(curwin);
3164 changed_window_setting();
3165 }
3166 else if (foldmethodIsMarker(curwin))
3167 deleteFold((linenr_T)1, curbuf->b_ml.ml_line_count,
3168 TRUE, FALSE);
3169 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003170 emsg(_(e_cannot_erase_folds_with_current_foldmethod));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171 break;
3172
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003173 // "zn": fold none: reset 'foldenable'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003174 case 'n': curwin->w_p_fen = FALSE;
3175 break;
3176
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003177 // "zN": fold Normal: set 'foldenable'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003178 case 'N': curwin->w_p_fen = TRUE;
3179 break;
3180
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003181 // "zi": invert folding: toggle 'foldenable'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003182 case 'i': curwin->w_p_fen = !curwin->w_p_fen;
3183 break;
3184
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003185 // "za": open closed fold or close open fold at cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186 case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
3187 openFold(curwin->w_cursor.lnum, cap->count1);
3188 else
3189 {
3190 closeFold(curwin->w_cursor.lnum, cap->count1);
3191 curwin->w_p_fen = TRUE;
3192 }
3193 break;
3194
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003195 // "zA": open fold at cursor recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196 case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
3197 openFoldRecurse(curwin->w_cursor.lnum);
3198 else
3199 {
3200 closeFoldRecurse(curwin->w_cursor.lnum);
3201 curwin->w_p_fen = TRUE;
3202 }
3203 break;
3204
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003205 // "zo": open fold at cursor or Visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +00003206 case 'o': if (VIsual_active)
3207 nv_operator(cap);
3208 else
3209 openFold(curwin->w_cursor.lnum, cap->count1);
3210 break;
3211
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003212 // "zO": open fold recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00003213 case 'O': if (VIsual_active)
3214 nv_operator(cap);
3215 else
3216 openFoldRecurse(curwin->w_cursor.lnum);
3217 break;
3218
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003219 // "zc": close fold at cursor or Visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220 case 'c': if (VIsual_active)
3221 nv_operator(cap);
3222 else
3223 closeFold(curwin->w_cursor.lnum, cap->count1);
3224 curwin->w_p_fen = TRUE;
3225 break;
3226
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003227 // "zC": close fold recursively
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228 case 'C': if (VIsual_active)
3229 nv_operator(cap);
3230 else
3231 closeFoldRecurse(curwin->w_cursor.lnum);
3232 curwin->w_p_fen = TRUE;
3233 break;
3234
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003235 // "zv": open folds at the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00003236 case 'v': foldOpenCursor();
3237 break;
3238
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003239 // "zx": re-apply 'foldlevel' and open folds at the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240 case 'x': curwin->w_p_fen = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003241 curwin->w_foldinvalid = TRUE; // recompute folds
3242 newFoldLevel(); // update right now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 foldOpenCursor();
3244 break;
3245
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003246 // "zX": undo manual opens/closes, re-apply 'foldlevel'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 case 'X': curwin->w_p_fen = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003248 curwin->w_foldinvalid = TRUE; // recompute folds
3249 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250 break;
3251
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003252 // "zm": fold more
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 case 'm': if (curwin->w_p_fdl > 0)
Bram Moolenaar7d2757a2015-03-31 17:46:22 +02003254 {
3255 curwin->w_p_fdl -= cap->count1;
3256 if (curwin->w_p_fdl < 0)
3257 curwin->w_p_fdl = 0;
3258 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003259 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 curwin->w_p_fen = TRUE;
3261 break;
3262
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003263 // "zM": close all folds
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 case 'M': curwin->w_p_fdl = 0;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003265 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 curwin->w_p_fen = TRUE;
3267 break;
3268
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003269 // "zr": reduce folding
Bram Moolenaar7d2757a2015-03-31 17:46:22 +02003270 case 'r': curwin->w_p_fdl += cap->count1;
3271 {
3272 int d = getDeepestNesting();
3273
3274 if (curwin->w_p_fdl >= d)
3275 curwin->w_p_fdl = d;
3276 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277 break;
3278
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003279 // "zR": open all folds
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 case 'R': curwin->w_p_fdl = getDeepestNesting();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003281 old_fdl = -1; // force an update
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282 break;
3283
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003284 case 'j': // "zj" move to next fold downwards
3285 case 'k': // "zk" move to next fold upwards
Bram Moolenaar071d4272004-06-13 20:20:40 +00003286 if (foldMoveTo(TRUE, nchar == 'j' ? FORWARD : BACKWARD,
3287 cap->count1) == FAIL)
3288 clearopbeep(cap->oap);
3289 break;
3290
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003291#endif // FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00003293#ifdef FEAT_SPELL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003294 case 'u': // "zug" and "zuw": undo "zg" and "zw"
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003295 case 'g': // "zg": add good word to word list
3296 case 'w': // "zw": add wrong word to word list
3297 case 'G': // "zG": add good word to temp word list
3298 case 'W': // "zW": add wrong word to temp word list
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003299 if (nv_zg_zw(cap, nchar) == FAIL)
3300 return;
Bram Moolenaar3982c542005-06-08 21:56:31 +00003301 break;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00003302
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003303 case '=': // "z=": suggestions for a badly spelled word
Bram Moolenaar66fa2712006-01-22 23:22:22 +00003304 if (!checkclearop(cap->oap))
Bram Moolenaard12a1322005-08-21 22:08:24 +00003305 spell_suggest((int)cap->count0);
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00003306 break;
Bram Moolenaarb765d632005-06-07 21:00:02 +00003307#endif
3308
Bram Moolenaar071d4272004-06-13 20:20:40 +00003309 default: clearopbeep(cap->oap);
3310 }
3311
3312#ifdef FEAT_FOLDING
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003313 // Redraw when 'foldenable' changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00003314 if (old_fen != curwin->w_p_fen)
3315 {
3316# ifdef FEAT_DIFF
3317 win_T *wp;
3318
3319 if (foldmethodIsDiff(curwin) && curwin->w_p_scb)
3320 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003321 // Adjust 'foldenable' in diff-synced windows.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003322 FOR_ALL_WINDOWS(wp)
3323 {
3324 if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb)
3325 {
3326 wp->w_p_fen = curwin->w_p_fen;
3327 changed_window_setting_win(wp);
3328 }
3329 }
3330 }
3331# endif
3332 changed_window_setting();
3333 }
3334
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003335 // Redraw when 'foldlevel' changed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003336 if (old_fdl != curwin->w_p_fdl)
3337 newFoldLevel();
3338#endif
3339}
3340
3341#ifdef FEAT_GUI
3342/*
3343 * Vertical scrollbar movement.
3344 */
3345 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003346nv_ver_scrollbar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347{
3348 if (cap->oap->op_type != OP_NOP)
3349 clearopbeep(cap->oap);
3350
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003351 // Even if an operator was pending, we still want to scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 gui_do_scroll();
3353}
3354
3355/*
3356 * Horizontal scrollbar movement.
3357 */
3358 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003359nv_hor_scrollbar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360{
3361 if (cap->oap->op_type != OP_NOP)
3362 clearopbeep(cap->oap);
3363
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003364 // Even if an operator was pending, we still want to scroll
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +02003365 gui_do_horiz_scroll(scrollbar_value, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366}
3367#endif
3368
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003369#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003370/*
3371 * Click in GUI tab.
3372 */
3373 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003374nv_tabline(cmdarg_T *cap)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003375{
3376 if (cap->oap->op_type != OP_NOP)
3377 clearopbeep(cap->oap);
3378
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003379 // Even if an operator was pending, we still want to jump tabs.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003380 goto_tabpage(current_tab);
3381}
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003382
3383/*
3384 * Selected item in tab line menu.
3385 */
3386 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003387nv_tabmenu(cmdarg_T *cap)
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003388{
3389 if (cap->oap->op_type != OP_NOP)
3390 clearopbeep(cap->oap);
3391
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003392 // Even if an operator was pending, we still want to jump tabs.
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003393 handle_tabmenu();
3394}
3395
3396/*
3397 * Handle selecting an item of the GUI tab line menu.
3398 * Used in Normal and Insert mode.
3399 */
3400 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003401handle_tabmenu(void)
Bram Moolenaara226a6d2006-02-26 23:59:20 +00003402{
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003403 switch (current_tabmenu)
3404 {
3405 case TABLINE_MENU_CLOSE:
3406 if (current_tab == 0)
3407 do_cmdline_cmd((char_u *)"tabclose");
3408 else
3409 {
3410 vim_snprintf((char *)IObuff, IOSIZE, "tabclose %d",
3411 current_tab);
3412 do_cmdline_cmd(IObuff);
3413 }
3414 break;
3415
3416 case TABLINE_MENU_NEW:
Bram Moolenaardfd76912015-02-27 15:03:58 +01003417 if (current_tab == 0)
3418 do_cmdline_cmd((char_u *)"$tabnew");
3419 else
3420 {
3421 vim_snprintf((char *)IObuff, IOSIZE, "%dtabnew",
3422 current_tab - 1);
3423 do_cmdline_cmd(IObuff);
3424 }
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003425 break;
3426
3427 case TABLINE_MENU_OPEN:
Bram Moolenaardfd76912015-02-27 15:03:58 +01003428 if (current_tab == 0)
3429 do_cmdline_cmd((char_u *)"browse $tabnew");
3430 else
3431 {
3432 vim_snprintf((char *)IObuff, IOSIZE, "browse %dtabnew",
3433 current_tab - 1);
3434 do_cmdline_cmd(IObuff);
3435 }
Bram Moolenaarba6c0522006-02-25 21:45:02 +00003436 break;
3437 }
3438}
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003439#endif
3440
Bram Moolenaar071d4272004-06-13 20:20:40 +00003441/*
3442 * "Q" command.
3443 */
3444 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003445nv_exmode(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003446{
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003447 // Ignore 'Q' in Visual mode, just give a beep.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448 if (VIsual_active)
Bram Moolenaar165bc692015-07-21 17:53:25 +02003449 vim_beep(BO_EX);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003450 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451 do_exmode(FALSE);
3452}
3453
3454/*
3455 * Handle a ":" command.
3456 */
3457 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003458nv_colon(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003459{
Bram Moolenaar957cf672020-11-12 14:21:06 +01003460 int old_p_im;
3461 int cmd_result;
Bram Moolenaare32c3c42022-01-15 18:26:04 +00003462 int is_cmdkey = cap->cmdchar == K_COMMAND
3463 || cap->cmdchar == K_SCRIPT_COMMAND;
3464 int flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003465
Bram Moolenaar957cf672020-11-12 14:21:06 +01003466 if (VIsual_active && !is_cmdkey)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003467 nv_operator(cap);
3468 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469 {
3470 if (cap->oap->op_type != OP_NOP)
3471 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003472 // Using ":" as a movement is characterwise exclusive.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 cap->oap->motion_type = MCHAR;
3474 cap->oap->inclusive = FALSE;
3475 }
Bram Moolenaar957cf672020-11-12 14:21:06 +01003476 else if (cap->count0 && !is_cmdkey)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003478 // translate "count:" into ":.,.+(count - 1)"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479 stuffcharReadbuff('.');
3480 if (cap->count0 > 1)
3481 {
3482 stuffReadbuff((char_u *)",.+");
3483 stuffnumReadbuff((long)cap->count0 - 1L);
3484 }
3485 }
3486
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003487 // When typing, don't type below an old message
Bram Moolenaar071d4272004-06-13 20:20:40 +00003488 if (KeyTyped)
3489 compute_cmdrow();
3490
3491 old_p_im = p_im;
3492
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003493 // get a command line and execute it
Bram Moolenaare32c3c42022-01-15 18:26:04 +00003494 flags = cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0;
3495 if (is_cmdkey)
3496 cmd_result = do_cmdkey_command(cap->cmdchar, flags);
3497 else
3498 cmd_result = do_cmdline(NULL, getexline, NULL, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003500 // If 'insertmode' changed, enter or exit Insert mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 if (p_im != old_p_im)
3502 {
3503 if (p_im)
3504 restart_edit = 'i';
3505 else
3506 restart_edit = 0;
3507 }
3508
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003509 if (cmd_result == FAIL)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003510 // The Ex command failed, do not execute the operator.
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003511 clearop(cap->oap);
3512 else if (cap->oap->op_type != OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513 && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
3514 || cap->oap->start.col >
Bram Moolenaard7fbfe12013-04-05 17:43:14 +02003515 (colnr_T)STRLEN(ml_get(cap->oap->start.lnum))
3516 || did_emsg
3517 ))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003518 // The start of the operator has become invalid by the Ex command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519 clearopbeep(cap->oap);
3520 }
3521}
3522
3523/*
3524 * Handle CTRL-G command.
3525 */
3526 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003527nv_ctrlg(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003528{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003529 if (VIsual_active) // toggle Selection/Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530 {
3531 VIsual_select = !VIsual_select;
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01003532 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 showmode();
3534 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01003535 else if (!checkclearop(cap->oap))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003536 // print full name if count given or :cd used
Bram Moolenaar071d4272004-06-13 20:20:40 +00003537 fileinfo((int)cap->count0, FALSE, TRUE);
3538}
3539
3540/*
3541 * Handle CTRL-H <Backspace> command.
3542 */
3543 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003544nv_ctrlh(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003545{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003546 if (VIsual_active && VIsual_select)
3547 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003548 cap->cmdchar = 'x'; // BS key behaves like 'x' in Select mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00003549 v_visop(cap);
3550 }
3551 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552 nv_left(cap);
3553}
3554
3555/*
3556 * CTRL-L: clear screen and redraw.
3557 */
3558 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003559nv_clear(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003560{
3561 if (!checkclearop(cap->oap))
3562 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563#ifdef FEAT_SYN_HL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003564 // Clear all syntax states to force resyncing.
Bram Moolenaar860cae12010-06-05 23:22:07 +02003565 syn_stack_free_all(curwin->w_s);
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003566# ifdef FEAT_RELTIME
3567 {
3568 win_T *wp;
3569
3570 FOR_ALL_WINDOWS(wp)
3571 wp->w_s->b_syn_slow = FALSE;
3572 }
3573# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574#endif
3575 redraw_later(CLEAR);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003576#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
3577# ifdef VIMDLL
3578 if (!gui.in_use)
3579# endif
3580 resize_console_buf();
Bram Moolenaar78d21da2019-02-17 15:00:52 +01003581#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003582 }
3583}
3584
3585/*
3586 * CTRL-O: In Select mode: switch to Visual mode for one command.
3587 * Otherwise: Go to older pcmark.
3588 */
3589 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003590nv_ctrlo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592 if (VIsual_active && VIsual_select)
3593 {
3594 VIsual_select = FALSE;
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01003595 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596 showmode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003597 restart_VIsual_select = 2; // restart Select mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00003598 }
3599 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003600 {
3601 cap->count1 = -cap->count1;
3602 nv_pcmark(cap);
3603 }
3604}
3605
3606/*
Bram Moolenaar1bbb6192018-11-10 16:02:01 +01003607 * CTRL-^ command, short for ":e #". Works even when the alternate buffer is
3608 * not named.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609 */
3610 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003611nv_hat(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612{
3613 if (!checkclearopq(cap->oap))
3614 (void)buflist_getfile((int)cap->count0, (linenr_T)0,
3615 GETF_SETMARK|GETF_ALT, FALSE);
3616}
3617
3618/*
3619 * "Z" commands.
3620 */
3621 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003622nv_Zet(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623{
3624 if (!checkclearopq(cap->oap))
3625 {
3626 switch (cap->nchar)
3627 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003628 // "ZZ": equivalent to ":x".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003629 case 'Z': do_cmdline_cmd((char_u *)"x");
3630 break;
3631
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003632 // "ZQ": equivalent to ":q!" (Elvis compatible).
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633 case 'Q': do_cmdline_cmd((char_u *)"q!");
3634 break;
3635
3636 default: clearopbeep(cap->oap);
3637 }
3638 }
3639}
3640
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641/*
3642 * Call nv_ident() as if "c1" was used, with "c2" as next character.
3643 */
3644 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003645do_nv_ident(int c1, int c2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646{
3647 oparg_T oa;
3648 cmdarg_T ca;
3649
3650 clear_oparg(&oa);
Bram Moolenaara80faa82020-04-12 19:37:17 +02003651 CLEAR_FIELD(ca);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652 ca.oap = &oa;
3653 ca.cmdchar = c1;
3654 ca.nchar = c2;
3655 nv_ident(&ca);
3656}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657
3658/*
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003659 * 'K' normal-mode command. Get the command to lookup the keyword under the
3660 * cursor.
3661 */
3662 static int
3663nv_K_getcmd(
3664 cmdarg_T *cap,
3665 char_u *kp,
3666 int kp_help,
3667 int kp_ex,
3668 char_u **ptr_arg,
3669 int n,
3670 char_u *buf,
3671 unsigned buflen)
3672{
3673 char_u *ptr = *ptr_arg;
3674 int isman;
3675 int isman_s;
3676
3677 if (kp_help)
3678 {
3679 // in the help buffer
3680 STRCPY(buf, "he! ");
3681 return n;
3682 }
3683
3684 if (kp_ex)
3685 {
3686 // 'keywordprog' is an ex command
3687 if (cap->count0 != 0)
3688 vim_snprintf((char *)buf, buflen, "%s %ld", kp, cap->count0);
3689 else
3690 STRCPY(buf, kp);
3691 STRCAT(buf, " ");
3692 return n;
3693 }
3694
3695 // An external command will probably use an argument starting
3696 // with "-" as an option. To avoid trouble we skip the "-".
3697 while (*ptr == '-' && n > 0)
3698 {
3699 ++ptr;
3700 --n;
3701 }
3702 if (n == 0)
3703 {
3704 // found dashes only
3705 emsg(_(e_no_identifier_under_cursor));
3706 vim_free(buf);
3707 *ptr_arg = ptr;
3708 return 0;
3709 }
3710
3711 // When a count is given, turn it into a range. Is this
3712 // really what we want?
3713 isman = (STRCMP(kp, "man") == 0);
3714 isman_s = (STRCMP(kp, "man -s") == 0);
3715 if (cap->count0 != 0 && !(isman || isman_s))
3716 sprintf((char *)buf, ".,.+%ld", cap->count0 - 1);
3717
3718 STRCAT(buf, "! ");
3719 if (cap->count0 == 0 && isman_s)
3720 STRCAT(buf, "man");
3721 else
3722 STRCAT(buf, kp);
3723 STRCAT(buf, " ");
3724 if (cap->count0 != 0 && (isman || isman_s))
3725 {
3726 sprintf((char *)buf + STRLEN(buf), "%ld", cap->count0);
3727 STRCAT(buf, " ");
3728 }
3729
3730 *ptr_arg = ptr;
3731 return n;
3732}
3733
3734/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735 * Handle the commands that use the word under the cursor.
3736 * [g] CTRL-] :ta to current identifier
3737 * [g] 'K' run program for current identifier
3738 * [g] '*' / to current identifier or string
3739 * [g] '#' ? to current identifier or string
3740 * g ']' :tselect for current identifier
3741 */
3742 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003743nv_ident(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744{
3745 char_u *ptr = NULL;
3746 char_u *buf;
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003747 unsigned buflen;
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003748 char_u *newbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003749 char_u *p;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003750 char_u *kp; // value of 'keywordprg'
3751 int kp_help; // 'keywordprg' is ":he"
3752 int kp_ex; // 'keywordprg' starts with ":"
3753 int n = 0; // init for GCC
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754 int cmdchar;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003755 int g_cmd; // "g" command
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003756 int tag_cmd = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003757 char_u *aux_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003759 if (cap->cmdchar == 'g') // "g*", "g#", "g]" and "gCTRL-]"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 {
3761 cmdchar = cap->nchar;
3762 g_cmd = TRUE;
3763 }
3764 else
3765 {
3766 cmdchar = cap->cmdchar;
3767 g_cmd = FALSE;
3768 }
3769
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003770 if (cmdchar == POUND) // the pound sign, '#' for English keyboards
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771 cmdchar = '#';
3772
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003773 // The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003774 if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K')
3775 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003776 if (VIsual_active && get_visual_text(cap, &ptr, &n) == FAIL)
3777 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778 if (checkclearopq(cap->oap))
3779 return;
3780 }
3781
3782 if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
3783 (cmdchar == '*' || cmdchar == '#')
3784 ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
3785 {
3786 clearop(cap->oap);
3787 return;
3788 }
3789
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003790 // Allocate buffer to put the command in. Inserting backslashes can
3791 // double the length of the word. p_kp / curbuf->b_p_kp could be added
3792 // and some numbers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793 kp = (*curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp);
3794 kp_help = (*kp == NUL || STRCMP(kp, ":he") == 0
3795 || STRCMP(kp, ":help") == 0);
Bram Moolenaara4f99f52017-08-26 16:25:32 +02003796 if (kp_help && *skipwhite(ptr) == NUL)
3797 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00003798 emsg(_(e_no_identifier_under_cursor)); // found white space only
Bram Moolenaara4f99f52017-08-26 16:25:32 +02003799 return;
3800 }
Bram Moolenaar2ff8b642016-05-24 10:46:45 +02003801 kp_ex = (*kp == ':');
3802 buflen = (unsigned)(n * 2 + 30 + STRLEN(kp));
3803 buf = alloc(buflen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003804 if (buf == NULL)
3805 return;
3806 buf[0] = NUL;
3807
3808 switch (cmdchar)
3809 {
3810 case '*':
3811 case '#':
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003812 // Put cursor at start of word, makes search skip the word
3813 // under the cursor.
3814 // Call setpcmark() first, so "*``" puts the cursor back where
3815 // it was.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816 setpcmark();
3817 curwin->w_cursor.col = (colnr_T) (ptr - ml_get_curline());
3818
3819 if (!g_cmd && vim_iswordp(ptr))
3820 STRCPY(buf, "\\<");
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003821 no_smartcase = TRUE; // don't use 'smartcase' now
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 break;
3823
3824 case 'K':
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003825 n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, buflen);
3826 if (n == 0)
3827 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003828 break;
3829
3830 case ']':
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003831 tag_cmd = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832#ifdef FEAT_CSCOPE
3833 if (p_cst)
3834 STRCPY(buf, "cstag ");
3835 else
3836#endif
3837 STRCPY(buf, "ts ");
3838 break;
3839
3840 default:
Bram Moolenaar97e7a842010-03-17 13:07:08 +01003841 tag_cmd = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 if (curbuf->b_help)
3843 STRCPY(buf, "he! ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 else
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003845 {
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003846 if (g_cmd)
3847 STRCPY(buf, "tj ");
Bram Moolenaarb3ea36c2020-08-23 21:46:32 +02003848 else if (cap->count0 == 0)
3849 STRCPY(buf, "ta ");
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003850 else
Bram Moolenaarb3ea36c2020-08-23 21:46:32 +02003851 sprintf((char *)buf, ":%ldta ", cap->count0);
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003852 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 }
3854
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003855 // Now grab the chars in the identifier
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003856 if (cmdchar == 'K' && !kp_help)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857 {
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003858 ptr = vim_strnsave(ptr, n);
Bram Moolenaar426f3752016-11-04 21:22:37 +01003859 if (kp_ex)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003860 // Escape the argument properly for an Ex command
Bram Moolenaar21c1a0c2021-10-17 17:20:23 +01003861 p = vim_strsave_fnameescape(ptr, VSE_NONE);
Bram Moolenaar426f3752016-11-04 21:22:37 +01003862 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003863 // Escape the argument properly for a shell command
Bram Moolenaar426f3752016-11-04 21:22:37 +01003864 p = vim_strsave_shellescape(ptr, TRUE, TRUE);
Bram Moolenaar9fd01c62008-11-01 12:52:38 +00003865 vim_free(ptr);
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003866 if (p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 {
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003868 vim_free(buf);
3869 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003871 newbuf = vim_realloc(buf, STRLEN(buf) + STRLEN(p) + 1);
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003872 if (newbuf == NULL)
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003873 {
3874 vim_free(buf);
3875 vim_free(p);
3876 return;
3877 }
Bram Moolenaar0bc380a2010-07-10 13:52:13 +02003878 buf = newbuf;
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003879 STRCAT(buf, p);
3880 vim_free(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003882 else
3883 {
3884 if (cmdchar == '*')
Bram Moolenaarf4e20992020-12-21 19:59:08 +01003885 aux_ptr = (char_u *)(magic_isset() ? "/.*~[^$\\" : "/^$\\");
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003886 else if (cmdchar == '#')
Bram Moolenaarf4e20992020-12-21 19:59:08 +01003887 aux_ptr = (char_u *)(magic_isset() ? "/?.*~[^$\\" : "/?^$\\");
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003888 else if (tag_cmd)
Bram Moolenaar77a0aa42010-10-13 18:06:47 +02003889 {
3890 if (curbuf->b_help)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003891 // ":help" handles unescaped argument
Bram Moolenaar77a0aa42010-10-13 18:06:47 +02003892 aux_ptr = (char_u *)"";
3893 else
3894 aux_ptr = (char_u *)"\\|\"\n[";
3895 }
Bram Moolenaar6d8027a2010-01-19 15:24:27 +01003896 else
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003897 aux_ptr = (char_u *)"\\|\"\n*?[";
3898
3899 p = buf + STRLEN(buf);
3900 while (n-- > 0)
3901 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003902 // put a backslash before \ and some others
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003903 if (vim_strchr(aux_ptr, *ptr) != NULL)
3904 *p++ = '\\';
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003905 // When current byte is a part of multibyte character, copy all
3906 // bytes of that character.
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003907 if (has_mbyte)
3908 {
3909 int i;
3910 int len = (*mb_ptr2len)(ptr) - 1;
3911
3912 for (i = 0; i < len && n >= 1; ++i, --n)
3913 *p++ = *ptr++;
3914 }
Bram Moolenaar3094a9e2008-09-06 14:44:59 +00003915 *p++ = *ptr++;
3916 }
3917 *p = NUL;
3918 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003919
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00003920 // Execute the command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 if (cmdchar == '*' || cmdchar == '#')
3922 {
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003923 if (!g_cmd && (has_mbyte
3924 ? vim_iswordp(mb_prevptr(ml_get_curline(), ptr))
3925 : vim_iswordc(ptr[-1])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 STRCAT(buf, "\\>");
Bram Moolenaard7663c22019-08-06 21:59:57 +02003927
3928 // put pattern in search history
Bram Moolenaarc7be3f32009-12-24 14:01:12 +00003929 init_history();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 add_to_history(HIST_SEARCH, buf, TRUE, NUL);
Bram Moolenaard7663c22019-08-06 21:59:57 +02003931
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02003932 (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 }
3934 else
Bram Moolenaar45e18cb2019-04-28 18:05:35 +02003935 {
3936 g_tag_at_cursor = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 do_cmdline_cmd(buf);
Bram Moolenaar45e18cb2019-04-28 18:05:35 +02003938 g_tag_at_cursor = FALSE;
3939 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003940
3941 vim_free(buf);
3942}
3943
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944/*
3945 * Get visually selected text, within one line only.
3946 * Returns FAIL if more than one line selected.
3947 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00003948 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01003949get_visual_text(
3950 cmdarg_T *cap,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01003951 char_u **pp, // return: start of selected text
3952 int *lenp) // return: length of selected text
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953{
3954 if (VIsual_mode != 'V')
3955 unadjust_for_sel();
3956 if (VIsual.lnum != curwin->w_cursor.lnum)
3957 {
Bram Moolenaard857f0e2005-06-21 22:37:39 +00003958 if (cap != NULL)
3959 clearopbeep(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960 return FAIL;
3961 }
3962 if (VIsual_mode == 'V')
3963 {
3964 *pp = ml_get_curline();
3965 *lenp = (int)STRLEN(*pp);
3966 }
3967 else
3968 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003969 if (LT_POS(curwin->w_cursor, VIsual))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 {
3971 *pp = ml_get_pos(&curwin->w_cursor);
3972 *lenp = VIsual.col - curwin->w_cursor.col + 1;
3973 }
3974 else
3975 {
3976 *pp = ml_get_pos(&VIsual);
3977 *lenp = curwin->w_cursor.col - VIsual.col + 1;
3978 }
Bram Moolenaar615ddd52021-11-17 18:00:31 +00003979 if (**pp == NUL)
3980 *lenp = 0;
3981 if (has_mbyte && *lenp > 0)
3982 // Correct the length to include all bytes of the last character.
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003983 *lenp += (*mb_ptr2len)(*pp + (*lenp - 1)) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 }
3985 reset_VIsual_and_resel();
3986 return OK;
3987}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988
3989/*
3990 * CTRL-T: backwards in tag stack
3991 */
3992 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01003993nv_tagpop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994{
3995 if (!checkclearopq(cap->oap))
3996 do_tag((char_u *)"", DT_POP, (int)cap->count1, FALSE, TRUE);
3997}
3998
3999/*
4000 * Handle scrolling command 'H', 'L' and 'M'.
4001 */
4002 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004003nv_scroll(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004004{
4005 int used = 0;
4006 long n;
4007#ifdef FEAT_FOLDING
4008 linenr_T lnum;
4009#endif
4010 int half;
4011
4012 cap->oap->motion_type = MLINE;
4013 setpcmark();
4014
4015 if (cap->cmdchar == 'L')
4016 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004017 validate_botline(); // make sure curwin->w_botline is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018 curwin->w_cursor.lnum = curwin->w_botline - 1;
4019 if (cap->count1 - 1 >= curwin->w_cursor.lnum)
4020 curwin->w_cursor.lnum = 1;
4021 else
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004022 {
4023#ifdef FEAT_FOLDING
4024 if (hasAnyFolding(curwin))
4025 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004026 // Count a fold for one screen line.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004027 for (n = cap->count1 - 1; n > 0
4028 && curwin->w_cursor.lnum > curwin->w_topline; --n)
4029 {
4030 (void)hasFolding(curwin->w_cursor.lnum,
4031 &curwin->w_cursor.lnum, NULL);
4032 --curwin->w_cursor.lnum;
4033 }
4034 }
4035 else
4036#endif
4037 curwin->w_cursor.lnum -= cap->count1 - 1;
4038 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 }
4040 else
4041 {
4042 if (cap->cmdchar == 'M')
4043 {
4044#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004045 // Don't count filler lines above the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 used -= diff_check_fill(curwin, curwin->w_topline)
4047 - curwin->w_topfill;
4048#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004049 validate_botline(); // make sure w_empty_rows is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 half = (curwin->w_height - curwin->w_empty_rows + 1) / 2;
4051 for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
4052 {
4053#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004054 // Count half he number of filler lines to be "below this
4055 // line" and half to be "above the next line".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline
4057 + n) / 2 >= half)
4058 {
4059 --n;
4060 break;
4061 }
4062#endif
4063 used += plines(curwin->w_topline + n);
4064 if (used >= half)
4065 break;
4066#ifdef FEAT_FOLDING
4067 if (hasFolding(curwin->w_topline + n, NULL, &lnum))
4068 n = lnum - curwin->w_topline;
4069#endif
4070 }
4071 if (n > 0 && used > curwin->w_height)
4072 --n;
4073 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004074 else // (cap->cmdchar == 'H')
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004075 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004076 n = cap->count1 - 1;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004077#ifdef FEAT_FOLDING
4078 if (hasAnyFolding(curwin))
4079 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004080 // Count a fold for one screen line.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004081 lnum = curwin->w_topline;
4082 while (n-- > 0 && lnum < curwin->w_botline - 1)
4083 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02004084 (void)hasFolding(lnum, NULL, &lnum);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004085 ++lnum;
4086 }
4087 n = lnum - curwin->w_topline;
4088 }
4089#endif
4090 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091 curwin->w_cursor.lnum = curwin->w_topline + n;
4092 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
4093 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
4094 }
4095
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004096 // Correct for 'so', except when an operator is pending.
Bram Moolenaar44cc4cf2017-10-15 22:13:37 +02004097 if (cap->oap->op_type == OP_NOP)
4098 cursor_correct();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099 beginline(BL_SOL | BL_FIX);
4100}
4101
4102/*
4103 * Cursor right commands.
4104 */
4105 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004106nv_right(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107{
4108 long n;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01004109 int past_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004111 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
4112 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004113 // <C-Right> and <S-Right> move a word or WORD right
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004114 if (mod_mask & MOD_MASK_CTRL)
4115 cap->arg = TRUE;
4116 nv_wordcmd(cap);
4117 return;
4118 }
4119
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120 cap->oap->motion_type = MCHAR;
4121 cap->oap->inclusive = FALSE;
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01004122 past_line = (VIsual_active && *p_sel != 'o');
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004124 // In virtual edit mode, there's no such thing as "past_line", as lines
4125 // are (theoretically) infinitely long.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 if (virtual_active())
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01004127 past_line = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128
4129 for (n = cap->count1; n > 0; --n)
4130 {
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01004131 if ((!past_line && oneright() == FAIL)
4132 || (past_line && *ml_get_cursor() == NUL)
Bram Moolenaar78a15312009-05-15 19:33:18 +00004133 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004134 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004135 // <Space> wraps to next line if 'whichwrap' has 's'.
4136 // 'l' wraps to next line if 'whichwrap' has 'l'.
4137 // CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 if ( ((cap->cmdchar == ' '
4139 && vim_strchr(p_ww, 's') != NULL)
4140 || (cap->cmdchar == 'l'
4141 && vim_strchr(p_ww, 'l') != NULL)
Bram Moolenaara88d9682005-03-25 21:45:43 +00004142 || (cap->cmdchar == K_RIGHT
Bram Moolenaar071d4272004-06-13 20:20:40 +00004143 && vim_strchr(p_ww, '>') != NULL))
4144 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
4145 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004146 // When deleting we also count the NL as a character.
4147 // Set cap->oap->inclusive when last char in the line is
4148 // included, move to next line after that
Bram Moolenaar362e1a32006-03-06 23:29:24 +00004149 if ( cap->oap->op_type != OP_NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150 && !cap->oap->inclusive
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004151 && !LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152 cap->oap->inclusive = TRUE;
4153 else
4154 {
4155 ++curwin->w_cursor.lnum;
4156 curwin->w_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004158 curwin->w_set_curswant = TRUE;
4159 cap->oap->inclusive = FALSE;
4160 }
4161 continue;
4162 }
4163 if (cap->oap->op_type == OP_NOP)
4164 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004165 // Only beep and flush if not moved at all
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 if (n == cap->count1)
4167 beep_flush();
4168 }
4169 else
4170 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004171 if (!LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172 cap->oap->inclusive = TRUE;
4173 }
4174 break;
4175 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01004176 else if (past_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 {
4178 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179 if (virtual_active())
4180 oneright();
4181 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 if (has_mbyte)
Bram Moolenaar56ebbab2019-09-20 13:40:14 +02004184 curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor());
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186 ++curwin->w_cursor.col;
4187 }
4188 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 }
4190#ifdef FEAT_FOLDING
4191 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
4192 && cap->oap->op_type == OP_NOP)
4193 foldOpenCursor();
4194#endif
4195}
4196
4197/*
4198 * Cursor left commands.
4199 *
4200 * Returns TRUE when operator end should not be adjusted.
4201 */
4202 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004203nv_left(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204{
4205 long n;
4206
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004207 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))
4208 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004209 // <C-Left> and <S-Left> move a word or WORD left
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004210 if (mod_mask & MOD_MASK_CTRL)
4211 cap->arg = 1;
4212 nv_bck_word(cap);
4213 return;
4214 }
4215
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 cap->oap->motion_type = MCHAR;
4217 cap->oap->inclusive = FALSE;
4218 for (n = cap->count1; n > 0; --n)
4219 {
4220 if (oneleft() == FAIL)
4221 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004222 // <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
4223 // 'h' wraps to previous line if 'whichwrap' has 'h'.
4224 // CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225 if ( (((cap->cmdchar == K_BS
4226 || cap->cmdchar == Ctrl_H)
4227 && vim_strchr(p_ww, 'b') != NULL)
4228 || (cap->cmdchar == 'h'
4229 && vim_strchr(p_ww, 'h') != NULL)
Bram Moolenaara88d9682005-03-25 21:45:43 +00004230 || (cap->cmdchar == K_LEFT
Bram Moolenaar071d4272004-06-13 20:20:40 +00004231 && vim_strchr(p_ww, '<') != NULL))
4232 && curwin->w_cursor.lnum > 1)
4233 {
4234 --(curwin->w_cursor.lnum);
4235 coladvance((colnr_T)MAXCOL);
4236 curwin->w_set_curswant = TRUE;
4237
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004238 // When the NL before the first char has to be deleted we
4239 // put the cursor on the NUL after the previous line.
4240 // This is a very special case, be careful!
4241 // Don't adjust op_end now, otherwise it won't work.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 if ( (cap->oap->op_type == OP_DELETE
4243 || cap->oap->op_type == OP_CHANGE)
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004244 && !LINEEMPTY(curwin->w_cursor.lnum))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 {
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004246 char_u *cp = ml_get_cursor();
4247
4248 if (*cp != NUL)
4249 {
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004250 if (has_mbyte)
4251 curwin->w_cursor.col += (*mb_ptr2len)(cp);
4252 else
Bram Moolenaar7d311c52014-02-22 23:49:35 +01004253 ++curwin->w_cursor.col;
4254 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 cap->retval |= CA_NO_ADJ_OP_END;
4256 }
4257 continue;
4258 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004259 // Only beep and flush if not moved at all
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260 else if (cap->oap->op_type == OP_NOP && n == cap->count1)
4261 beep_flush();
4262 break;
4263 }
4264 }
4265#ifdef FEAT_FOLDING
4266 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
4267 && cap->oap->op_type == OP_NOP)
4268 foldOpenCursor();
4269#endif
4270}
4271
4272/*
4273 * Cursor up commands.
4274 * cap->arg is TRUE for "-": Move cursor to first non-blank.
4275 */
4276 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004277nv_up(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004279 if (mod_mask & MOD_MASK_SHIFT)
4280 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004281 // <S-Up> is page up
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004282 cap->arg = BACKWARD;
4283 nv_page(cap);
4284 }
4285 else
4286 {
4287 cap->oap->motion_type = MLINE;
4288 if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
4289 clearopbeep(cap->oap);
4290 else if (cap->arg)
4291 beginline(BL_WHITE | BL_FIX);
4292 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293}
4294
4295/*
4296 * Cursor down commands.
4297 * cap->arg is TRUE for CR and "+": Move cursor to first non-blank.
4298 */
4299 static void
Bram Moolenaar1b010052016-09-12 12:24:11 +02004300nv_down(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301{
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004302 if (mod_mask & MOD_MASK_SHIFT)
4303 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004304 // <S-Down> is page down
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004305 cap->arg = FORWARD;
4306 nv_page(cap);
4307 }
Bram Moolenaar4033c552017-09-16 20:54:51 +02004308#if defined(FEAT_QUICKFIX)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004309 // Quickfix window only: view the result under the cursor.
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004310 else if (bt_quickfix(curbuf) && cap->cmdchar == CAR)
4311 qf_view_result(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312#endif
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004313 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004314 {
4315#ifdef FEAT_CMDWIN
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004316 // In the cmdline window a <CR> executes the command.
Bram Moolenaar05159a02005-02-26 23:04:13 +00004317 if (cmdwin_type != 0 && cap->cmdchar == CAR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004318 cmdwin_result = CAR;
4319 else
4320#endif
Bram Moolenaarf2732452018-06-03 14:47:35 +02004321#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004322 // In a prompt buffer a <CR> in the last line invokes the callback.
Bram Moolenaarf2732452018-06-03 14:47:35 +02004323 if (bt_prompt(curbuf) && cap->cmdchar == CAR
4324 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
4325 {
4326 invoke_prompt_callback();
4327 if (restart_edit == 0)
4328 restart_edit = 'a';
4329 }
4330 else
4331#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 {
4333 cap->oap->motion_type = MLINE;
4334 if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == FAIL)
4335 clearopbeep(cap->oap);
4336 else if (cap->arg)
4337 beginline(BL_WHITE | BL_FIX);
4338 }
4339 }
4340}
4341
4342#ifdef FEAT_SEARCHPATH
4343/*
4344 * Grab the file name under the cursor and edit it.
4345 */
4346 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004347nv_gotofile(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348{
4349 char_u *ptr;
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004350 linenr_T lnum = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00004352 if (text_locked())
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 {
4354 clearopbeep(cap->oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00004355 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 return;
4357 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004358 if (curbuf_locked())
4359 {
4360 clearop(cap->oap);
4361 return;
4362 }
Bram Moolenaar5aed0cc2020-05-12 22:02:21 +02004363#ifdef FEAT_PROP_POPUP
4364 if (ERROR_IF_TERM_POPUP_WINDOW)
4365 return;
4366#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004368 ptr = grab_file_name(cap->count1, &lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369
4370 if (ptr != NULL)
4371 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004372 // do autowrite if necessary
Bram Moolenaareb44a682017-08-03 22:44:55 +02004373 if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf))
Bram Moolenaarcde88542015-08-11 19:14:00 +02004374 (void)autowrite(curbuf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375 setpcmark();
Bram Moolenaar2a79ed22017-05-24 09:51:39 +02004376 if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
Bram Moolenaareb44a682017-08-03 22:44:55 +02004377 buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
Bram Moolenaar2a79ed22017-05-24 09:51:39 +02004378 && cap->nchar == 'F' && lnum >= 0)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004379 {
4380 curwin->w_cursor.lnum = lnum;
4381 check_cursor_lnum();
4382 beginline(BL_SOL | BL_FIX);
4383 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384 vim_free(ptr);
4385 }
4386 else
4387 clearop(cap->oap);
4388}
4389#endif
4390
4391/*
4392 * <End> command: to end of current line or last line.
4393 */
4394 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004395nv_end(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004397 if (cap->arg || (mod_mask & MOD_MASK_CTRL)) // CTRL-END = goto last line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398 {
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00004399 cap->arg = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004400 nv_goto(cap);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004401 cap->count1 = 1; // to end of current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004402 }
4403 nv_dollar(cap);
4404}
4405
4406/*
4407 * Handle the "$" command.
4408 */
4409 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004410nv_dollar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411{
4412 cap->oap->motion_type = MCHAR;
4413 cap->oap->inclusive = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004414 // In virtual mode when off the edge of a line and an operator
4415 // is pending (whew!) keep the cursor where it is.
4416 // Otherwise, send it to the end of the line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004417 if (!virtual_active() || gchar_cursor() != NUL
4418 || cap->oap->op_type == OP_NOP)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004419 curwin->w_curswant = MAXCOL; // so we stay at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 if (cursor_down((long)(cap->count1 - 1),
4421 cap->oap->op_type == OP_NOP) == FAIL)
4422 clearopbeep(cap->oap);
4423#ifdef FEAT_FOLDING
4424 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
4425 foldOpenCursor();
4426#endif
4427}
4428
4429/*
4430 * Implementation of '?' and '/' commands.
4431 * If cap->arg is TRUE don't set PC mark.
4432 */
4433 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004434nv_search(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435{
4436 oparg_T *oap = cap->oap;
Bram Moolenaardda933d2016-09-03 21:04:58 +02004437 pos_T save_cursor = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438
4439 if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13)
4440 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004441 // Translate "g??" to "g?g?"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 cap->cmdchar = 'g';
4443 cap->nchar = '?';
4444 nv_operator(cap);
4445 return;
4446 }
4447
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004448 // When using 'incsearch' the cursor may be moved to set a different search
4449 // start position.
Bram Moolenaarc97f9a52021-12-28 20:59:56 +00004450 cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451
4452 if (cap->searchbuf == NULL)
4453 {
4454 clearop(oap);
4455 return;
4456 }
4457
Bram Moolenaar46539112015-02-17 15:43:57 +01004458 (void)normal_search(cap, cap->cmdchar, cap->searchbuf,
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01004459 (cap->arg || !EQUAL_POS(save_cursor, curwin->w_cursor))
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004460 ? 0 : SEARCH_MARK, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004461}
4462
4463/*
4464 * Handle "N" and "n" commands.
4465 * cap->arg is SEARCH_REV for "N", 0 for "n".
4466 */
4467 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004468nv_next(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469{
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004470 pos_T old = curwin->w_cursor;
4471 int wrapped = FALSE;
4472 int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, &wrapped);
Bram Moolenaar46539112015-02-17 15:43:57 +01004473
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004474 if (i == 1 && !wrapped && EQUAL_POS(old, curwin->w_cursor))
Bram Moolenaar46539112015-02-17 15:43:57 +01004475 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004476 // Avoid getting stuck on the current cursor position, which can
4477 // happen when an offset is given and the cursor is on the last char
4478 // in the buffer: Repeat with count + 1.
Bram Moolenaar46539112015-02-17 15:43:57 +01004479 cap->count1 += 1;
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004480 (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL);
Bram Moolenaar46539112015-02-17 15:43:57 +01004481 cap->count1 -= 1;
4482 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004483}
4484
4485/*
4486 * Search for "pat" in direction "dir" ('/' or '?', 0 for repeat).
4487 * Uses only cap->count1 and cap->oap from "cap".
Bram Moolenaar46539112015-02-17 15:43:57 +01004488 * Return 0 for failure, 1 for found, 2 for found and line offset added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489 */
Bram Moolenaar46539112015-02-17 15:43:57 +01004490 static int
Bram Moolenaar9b578142016-01-30 19:39:49 +01004491normal_search(
4492 cmdarg_T *cap,
4493 int dir,
4494 char_u *pat,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004495 int opt, // extra flags for do_search()
4496 int *wrapped)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497{
4498 int i;
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004499 searchit_arg_T sia;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500
4501 cap->oap->motion_type = MCHAR;
4502 cap->oap->inclusive = FALSE;
4503 cap->oap->use_reg_one = TRUE;
4504 curwin->w_set_curswant = TRUE;
4505
Bram Moolenaara80faa82020-04-12 19:37:17 +02004506 CLEAR_FIELD(sia);
Bram Moolenaarc036e872020-02-21 21:30:52 +01004507 i = do_search(cap->oap, dir, dir, pat, cap->count1,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02004508 opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
4509 if (wrapped != NULL)
4510 *wrapped = sia.sa_wrapped;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 if (i == 0)
4512 clearop(cap->oap);
4513 else
4514 {
4515 if (i == 2)
4516 cap->oap->motion_type = MLINE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004517 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518#ifdef FEAT_FOLDING
4519 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
4520 foldOpenCursor();
4521#endif
4522 }
4523
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004524 // "/$" will put the cursor after the end of the line, may need to
4525 // correct that here
Bram Moolenaar071d4272004-06-13 20:20:40 +00004526 check_cursor();
Bram Moolenaar46539112015-02-17 15:43:57 +01004527 return i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528}
4529
4530/*
4531 * Character search commands.
4532 * cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', TRUE for
4533 * ',' and FALSE for ';'.
4534 * cap->nchar is NUL for ',' and ';' (repeat the search)
4535 */
4536 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004537nv_csearch(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538{
4539 int t_cmd;
4540
4541 if (cap->cmdchar == 't' || cap->cmdchar == 'T')
4542 t_cmd = TRUE;
4543 else
4544 t_cmd = FALSE;
4545
4546 cap->oap->motion_type = MCHAR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547 if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == FAIL)
4548 clearopbeep(cap->oap);
4549 else
4550 {
4551 curwin->w_set_curswant = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004552 // Include a Tab for "tx" and for "dfx".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
4554 && (t_cmd || cap->oap->op_type != OP_NOP))
4555 {
4556 colnr_T scol, ecol;
4557
4558 getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
4559 curwin->w_cursor.coladd = ecol - scol;
4560 }
4561 else
4562 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004563 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564#ifdef FEAT_FOLDING
4565 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
4566 foldOpenCursor();
4567#endif
4568 }
4569}
4570
4571/*
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004572 * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
4573 * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
4574 * "[/", "[*", "]/", "]*": go to Nth comment start/end.
4575 * "[m" or "]m" search for prev/next start of (Java) method.
4576 * "[M" or "]M" search for prev/next end of (Java) method.
4577 */
4578 static void
4579nv_bracket_block(cmdarg_T *cap, pos_T *old_pos)
4580{
4581 pos_T new_pos = {0, 0, 0};
4582 pos_T *pos = NULL; // init for GCC
4583 pos_T prev_pos;
4584 long n;
4585 int findc;
4586 int c;
4587
4588 if (cap->nchar == '*')
4589 cap->nchar = '/';
4590 prev_pos.lnum = 0;
4591 if (cap->nchar == 'm' || cap->nchar == 'M')
4592 {
4593 if (cap->cmdchar == '[')
4594 findc = '{';
4595 else
4596 findc = '}';
4597 n = 9999;
4598 }
4599 else
4600 {
4601 findc = cap->nchar;
4602 n = cap->count1;
4603 }
4604 for ( ; n > 0; --n)
4605 {
4606 if ((pos = findmatchlimit(cap->oap, findc,
4607 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
4608 {
4609 if (new_pos.lnum == 0) // nothing found
4610 {
4611 if (cap->nchar != 'm' && cap->nchar != 'M')
4612 clearopbeep(cap->oap);
4613 }
4614 else
4615 pos = &new_pos; // use last one found
4616 break;
4617 }
4618 prev_pos = new_pos;
4619 curwin->w_cursor = *pos;
4620 new_pos = *pos;
4621 }
4622 curwin->w_cursor = *old_pos;
4623
4624 // Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
4625 // brought us to the match for "[m" and "]M" when inside a method.
4626 // Try finding the '{' or '}' we want to be at.
4627 // Also repeat for the given count.
4628 if (cap->nchar == 'm' || cap->nchar == 'M')
4629 {
4630 // norm is TRUE for "]M" and "[m"
4631 int norm = ((findc == '{') == (cap->nchar == 'm'));
4632
4633 n = cap->count1;
4634 // found a match: we were inside a method
4635 if (prev_pos.lnum != 0)
4636 {
4637 pos = &prev_pos;
4638 curwin->w_cursor = prev_pos;
4639 if (norm)
4640 --n;
4641 }
4642 else
4643 pos = NULL;
4644 while (n > 0)
4645 {
4646 for (;;)
4647 {
4648 if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0)
4649 {
4650 // if not found anything, that's an error
4651 if (pos == NULL)
4652 clearopbeep(cap->oap);
4653 n = 0;
4654 break;
4655 }
4656 c = gchar_cursor();
4657 if (c == '{' || c == '}')
4658 {
4659 // Must have found end/start of class: use it.
4660 // Or found the place to be at.
4661 if ((c == findc && norm) || (n == 1 && !norm))
4662 {
4663 new_pos = curwin->w_cursor;
4664 pos = &new_pos;
4665 n = 0;
4666 }
4667 // if no match found at all, we started outside of the
4668 // class and we're inside now. Just go on.
4669 else if (new_pos.lnum == 0)
4670 {
4671 new_pos = curwin->w_cursor;
4672 pos = &new_pos;
4673 }
4674 // found start/end of other method: go to match
4675 else if ((pos = findmatchlimit(cap->oap, findc,
Shougo Matsushitafb552072022-01-28 16:01:13 +00004676 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
4677 0)) == NULL)
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004678 n = 0;
4679 else
4680 curwin->w_cursor = *pos;
4681 break;
4682 }
4683 }
4684 --n;
4685 }
4686 curwin->w_cursor = *old_pos;
4687 if (pos == NULL && new_pos.lnum != 0)
4688 clearopbeep(cap->oap);
4689 }
4690 if (pos != NULL)
4691 {
4692 setpcmark();
4693 curwin->w_cursor = *pos;
4694 curwin->w_set_curswant = TRUE;
4695#ifdef FEAT_FOLDING
4696 if ((fdo_flags & FDO_BLOCK) && KeyTyped
4697 && cap->oap->op_type == OP_NOP)
4698 foldOpenCursor();
4699#endif
4700 }
4701}
4702
4703/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704 * "[" and "]" commands.
4705 * cap->arg is BACKWARD for "[" and FORWARD for "]".
4706 */
4707 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004708nv_brackets(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004709{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710 pos_T prev_pos;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004711 pos_T *pos = NULL; // init for GCC
4712 pos_T old_pos; // cursor position before command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713 int flag;
4714 long n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004715
4716 cap->oap->motion_type = MCHAR;
4717 cap->oap->inclusive = FALSE;
4718 old_pos = curwin->w_cursor;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004719 curwin->w_cursor.coladd = 0; // TODO: don't do this for an error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004720
4721#ifdef FEAT_SEARCHPATH
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004722 // "[f" or "]f" : Edit file under the cursor (same as "gf")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004723 if (cap->nchar == 'f')
4724 nv_gotofile(cap);
4725 else
4726#endif
4727
4728#ifdef FEAT_FIND_ID
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004729 // Find the occurrence(s) of the identifier or define under cursor
4730 // in current and included files or jump to the first occurrence.
4731 //
4732 // search list jump
4733 // fwd bwd fwd bwd fwd bwd
4734 // identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
4735 // define "]d" "[d" "]D" "[D" "]^D" "[^D"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 if (vim_strchr((char_u *)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004737# ifdef EBCDIC
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738 "iI\005dD\067",
Bram Moolenaar32526b32019-01-19 17:43:09 +01004739# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740 "iI\011dD\004",
Bram Moolenaar32526b32019-01-19 17:43:09 +01004741# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742 cap->nchar) != NULL)
4743 {
4744 char_u *ptr;
4745 int len;
4746
4747 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
4748 clearop(cap->oap);
4749 else
4750 {
4751 find_pattern_in_path(ptr, 0, len, TRUE,
4752 cap->count0 == 0 ? !isupper(cap->nchar) : FALSE,
4753 ((cap->nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
4754 cap->count1,
4755 isupper(cap->nchar) ? ACTION_SHOW_ALL :
4756 islower(cap->nchar) ? ACTION_SHOW : ACTION_GOTO,
4757 cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 : (linenr_T)1,
4758 (linenr_T)MAXLNUM);
4759 curwin->w_set_curswant = TRUE;
4760 }
4761 }
4762 else
4763#endif
4764
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004765 // "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
4766 // "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
4767 // "[/", "[*", "]/", "]*": go to Nth comment start/end.
4768 // "[m" or "]m" search for prev/next start of (Java) method.
4769 // "[M" or "]M" search for prev/next end of (Java) method.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004770 if ( (cap->cmdchar == '['
4771 && vim_strchr((char_u *)"{(*/#mM", cap->nchar) != NULL)
4772 || (cap->cmdchar == ']'
4773 && vim_strchr((char_u *)"})*/#mM", cap->nchar) != NULL))
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004774 nv_bracket_block(cap, &old_pos);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004776 // "[[", "[]", "]]" and "][": move to start or end of function
Bram Moolenaar071d4272004-06-13 20:20:40 +00004777 else if (cap->nchar == '[' || cap->nchar == ']')
4778 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004779 if (cap->nchar == cap->cmdchar) // "]]" or "[["
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780 flag = '{';
4781 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004782 flag = '}'; // "][" or "[]"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004783
4784 curwin->w_set_curswant = TRUE;
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004785 // Imitate strange Vi behaviour: When using "]]" with an operator
4786 // we also stop at '}'.
Bram Moolenaar8b96d642005-09-05 22:05:30 +00004787 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 (cap->oap->op_type != OP_NOP
4789 && cap->arg == FORWARD && flag == '{')))
4790 clearopbeep(cap->oap);
4791 else
4792 {
4793 if (cap->oap->op_type == OP_NOP)
4794 beginline(BL_WHITE | BL_FIX);
4795#ifdef FEAT_FOLDING
4796 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4797 foldOpenCursor();
4798#endif
4799 }
4800 }
4801
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004802 // "[p", "[P", "]P" and "]p": put with indent adjustment
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803 else if (cap->nchar == 'p' || cap->nchar == 'P')
4804 {
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02004805 nv_put_opt(cap, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806 }
4807
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004808 // "['", "[`", "]'" and "]`": jump to next mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809 else if (cap->nchar == '\'' || cap->nchar == '`')
4810 {
4811 pos = &curwin->w_cursor;
4812 for (n = cap->count1; n > 0; --n)
4813 {
4814 prev_pos = *pos;
4815 pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
4816 cap->nchar == '\'');
4817 if (pos == NULL)
4818 break;
4819 }
4820 if (pos == NULL)
4821 pos = &prev_pos;
4822 nv_cursormark(cap, cap->nchar == '\'', pos);
4823 }
4824
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004825 // [ or ] followed by a middle mouse click: put selected text with
4826 // indent adjustment. Any other button just does as usual.
Bram Moolenaar5ea0ac72010-05-07 15:52:08 +02004827 else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828 {
4829 (void)do_mouse(cap->oap, cap->nchar,
4830 (cap->cmdchar == ']') ? FORWARD : BACKWARD,
4831 cap->count1, PUT_FIXINDENT);
4832 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004833
4834#ifdef FEAT_FOLDING
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004835 // "[z" and "]z": move to start or end of open fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004836 else if (cap->nchar == 'z')
4837 {
4838 if (foldMoveTo(FALSE, cap->cmdchar == ']' ? FORWARD : BACKWARD,
4839 cap->count1) == FAIL)
4840 clearopbeep(cap->oap);
4841 }
4842#endif
4843
4844#ifdef FEAT_DIFF
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004845 // "[c" and "]c": move to next or previous diff-change.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004846 else if (cap->nchar == 'c')
4847 {
4848 if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
4849 cap->count1) == FAIL)
4850 clearopbeep(cap->oap);
4851 }
4852#endif
4853
Bram Moolenaarf71a3db2006-03-12 21:50:18 +00004854#ifdef FEAT_SPELL
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00004855 // "[s", "[S", "]s" and "]S": move to next spell error.
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00004856 else if (cap->nchar == 's' || cap->nchar == 'S')
4857 {
Bram Moolenaar2cf8b302005-04-20 19:37:22 +00004858 setpcmark();
4859 for (n = 0; n < cap->count1; ++n)
Bram Moolenaar95529562005-08-25 21:21:38 +00004860 if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
4861 cap->nchar == 's' ? TRUE : FALSE, FALSE, NULL) == 0)
Bram Moolenaar2cf8b302005-04-20 19:37:22 +00004862 {
4863 clearopbeep(cap->oap);
4864 break;
4865 }
Bram Moolenaarb73fa622017-12-21 20:27:47 +01004866 else
4867 curwin->w_set_curswant = TRUE;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004868# ifdef FEAT_FOLDING
4869 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
4870 foldOpenCursor();
4871# endif
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00004872 }
4873#endif
4874
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004875 // Not a valid cap->nchar.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 else
4877 clearopbeep(cap->oap);
4878}
4879
4880/*
4881 * Handle Normal mode "%" command.
4882 */
4883 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004884nv_percent(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885{
4886 pos_T *pos;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02004887#if defined(FEAT_FOLDING)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004888 linenr_T lnum = curwin->w_cursor.lnum;
4889#endif
4890
4891 cap->oap->inclusive = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004892 if (cap->count0) // {cnt}% : goto {cnt} percentage in file
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 {
4894 if (cap->count0 > 100)
4895 clearopbeep(cap->oap);
4896 else
4897 {
4898 cap->oap->motion_type = MLINE;
4899 setpcmark();
Bram Moolenaar2c655342021-02-23 19:32:03 +01004900 // Round up, so 'normal 100%' always jumps at the line line.
4901 // Beyond 21474836 lines, (ml_line_count * 100 + 99) would
4902 // overflow on 32-bits, so use a formula with less accuracy
4903 // to avoid overflows.
4904 if (curbuf->b_ml.ml_line_count >= 21474836)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99L)
4906 / 100L * cap->count0;
4907 else
4908 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
4909 cap->count0 + 99L) / 100L;
Bram Moolenaar1d859e22021-01-28 17:24:58 +01004910 if (curwin->w_cursor.lnum < 1)
4911 curwin->w_cursor.lnum = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004912 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
4913 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
4914 beginline(BL_SOL | BL_FIX);
4915 }
4916 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004917 else // "%" : go to matching paren
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 {
4919 cap->oap->motion_type = MCHAR;
4920 cap->oap->use_reg_one = TRUE;
4921 if ((pos = findmatch(cap->oap, NUL)) == NULL)
4922 clearopbeep(cap->oap);
4923 else
4924 {
4925 setpcmark();
4926 curwin->w_cursor = *pos;
4927 curwin->w_set_curswant = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004930 }
4931 }
4932#ifdef FEAT_FOLDING
4933 if (cap->oap->op_type == OP_NOP
4934 && lnum != curwin->w_cursor.lnum
4935 && (fdo_flags & FDO_PERCENT)
4936 && KeyTyped)
4937 foldOpenCursor();
4938#endif
4939}
4940
4941/*
4942 * Handle "(" and ")" commands.
4943 * cap->arg is BACKWARD for "(" and FORWARD for ")".
4944 */
4945 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004946nv_brace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004947{
4948 cap->oap->motion_type = MCHAR;
4949 cap->oap->use_reg_one = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004950 // The motion used to be inclusive for "(", but that is not what Vi does.
Bram Moolenaarebefac62005-12-28 22:39:57 +00004951 cap->oap->inclusive = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004952 curwin->w_set_curswant = TRUE;
4953
4954 if (findsent(cap->arg, cap->count1) == FAIL)
4955 clearopbeep(cap->oap);
4956 else
4957 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01004958 // Don't leave the cursor on the NUL past end of line.
Bram Moolenaar1f14d572008-01-12 16:12:10 +00004959 adjust_cursor(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961#ifdef FEAT_FOLDING
4962 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4963 foldOpenCursor();
4964#endif
4965 }
4966}
4967
4968/*
4969 * "m" command: Mark a position.
4970 */
4971 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004972nv_mark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973{
4974 if (!checkclearop(cap->oap))
4975 {
4976 if (setmark(cap->nchar) == FAIL)
4977 clearopbeep(cap->oap);
4978 }
4979}
4980
4981/*
4982 * "{" and "}" commands.
4983 * cmd->arg is BACKWARD for "{" and FORWARD for "}".
4984 */
4985 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01004986nv_findpar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987{
4988 cap->oap->motion_type = MCHAR;
4989 cap->oap->inclusive = FALSE;
4990 cap->oap->use_reg_one = TRUE;
4991 curwin->w_set_curswant = TRUE;
Bram Moolenaar8b96d642005-09-05 22:05:30 +00004992 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 clearopbeep(cap->oap);
4994 else
4995 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004997#ifdef FEAT_FOLDING
4998 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
4999 foldOpenCursor();
5000#endif
5001 }
5002}
5003
5004/*
5005 * "u" command: Undo or make lower case.
5006 */
5007 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005008nv_undo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01005010 if (cap->oap->op_type == OP_LOWER || VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005011 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005012 // translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013 cap->cmdchar = 'g';
5014 cap->nchar = 'u';
5015 nv_operator(cap);
5016 }
5017 else
5018 nv_kundo(cap);
5019}
5020
5021/*
5022 * <Undo> command.
5023 */
5024 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005025nv_kundo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026{
5027 if (!checkclearopq(cap->oap))
5028 {
Bram Moolenaarf2732452018-06-03 14:47:35 +02005029#ifdef FEAT_JOB_CHANNEL
5030 if (bt_prompt(curbuf))
5031 {
5032 clearopbeep(cap->oap);
5033 return;
5034 }
5035#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036 u_undo((int)cap->count1);
5037 curwin->w_set_curswant = TRUE;
5038 }
5039}
5040
5041/*
5042 * Handle the "r" command.
5043 */
5044 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005045nv_replace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046{
5047 char_u *ptr;
5048 int had_ctrl_v;
5049 long n;
5050
5051 if (checkclearop(cap->oap))
5052 return;
Bram Moolenaarf2732452018-06-03 14:47:35 +02005053#ifdef FEAT_JOB_CHANNEL
5054 if (bt_prompt(curbuf) && !prompt_curpos_editable())
5055 {
5056 clearopbeep(cap->oap);
5057 return;
5058 }
5059#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005061 // get another character
Bram Moolenaar071d4272004-06-13 20:20:40 +00005062 if (cap->nchar == Ctrl_V)
5063 {
5064 had_ctrl_v = Ctrl_V;
Bram Moolenaar0684e362020-12-03 19:54:42 +01005065 cap->nchar = get_literal(FALSE);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005066 // Don't redo a multibyte character with CTRL-V.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067 if (cap->nchar > DEL)
5068 had_ctrl_v = NUL;
5069 }
5070 else
5071 had_ctrl_v = NUL;
5072
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005073 // Abort if the character is a special key.
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +00005074 if (IS_SPECIAL(cap->nchar))
5075 {
5076 clearopbeep(cap->oap);
5077 return;
5078 }
5079
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005080 // Visual mode "r"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005081 if (VIsual_active)
5082 {
Bram Moolenaar57fb0da2009-02-04 10:46:25 +00005083 if (got_int)
5084 reset_VIsual();
Bram Moolenaard9820532013-11-04 01:41:17 +01005085 if (had_ctrl_v)
5086 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005087 // Use a special (negative) number to make a difference between a
5088 // literal CR or NL and a line break.
Bram Moolenaarf12519d2018-02-06 22:52:49 +01005089 if (cap->nchar == CAR)
5090 cap->nchar = REPLACE_CR_NCHAR;
5091 else if (cap->nchar == NL)
5092 cap->nchar = REPLACE_NL_NCHAR;
Bram Moolenaard9820532013-11-04 01:41:17 +01005093 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094 nv_operator(cap);
5095 return;
5096 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005097
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005098 // Break tabs, etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099 if (virtual_active())
5100 {
5101 if (u_save_cursor() == FAIL)
5102 return;
5103 if (gchar_cursor() == NUL)
5104 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005105 // Add extra space and put the cursor on the first one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 coladvance_force((colnr_T)(getviscol() + cap->count1));
5107 curwin->w_cursor.col -= cap->count1;
5108 }
5109 else if (gchar_cursor() == TAB)
5110 coladvance_force(getviscol());
5111 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005112
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005113 // Abort if not enough characters to replace.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 ptr = ml_get_cursor();
Bram Moolenaarc2f5abc2007-08-08 19:42:05 +00005115 if (STRLEN(ptr) < (unsigned)cap->count1
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01005116 || (has_mbyte && mb_charlen(ptr) < cap->count1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 {
5118 clearopbeep(cap->oap);
5119 return;
5120 }
5121
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00005122 // Replacing with a TAB is done by edit() when it is complicated because
5123 // 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB.
5124 // Other characters are done below to avoid problems with things like
5125 // CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005126 if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
5127 {
5128 stuffnumReadbuff(cap->count1);
5129 stuffcharReadbuff('R');
5130 stuffcharReadbuff('\t');
5131 stuffcharReadbuff(ESC);
5132 return;
5133 }
5134
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005135 // save line for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00005136 if (u_save_cursor() == FAIL)
5137 return;
5138
5139 if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n'))
5140 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00005141 // Replace character(s) by a single newline.
5142 // Strange vi behaviour: Only one newline is inserted.
5143 // Delete the characters here.
5144 // Insert the newline with an insert command, takes care of
5145 // autoindent. The insert command depends on being on the last
5146 // character of a line or not.
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005147 (void)del_chars(cap->count1, FALSE); // delete the characters
Bram Moolenaar071d4272004-06-13 20:20:40 +00005148 stuffcharReadbuff('\r');
5149 stuffcharReadbuff(ESC);
5150
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005151 // Give 'r' to edit(), to get the redo command right.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152 invoke_edit(cap, TRUE, 'r', FALSE);
5153 }
5154 else
5155 {
5156 prep_redo(cap->oap->regname, cap->count1,
5157 NUL, 'r', NUL, had_ctrl_v, cap->nchar);
5158
5159 curbuf->b_op_start = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005160 if (has_mbyte)
5161 {
5162 int old_State = State;
5163
5164 if (cap->ncharC1 != 0)
5165 AppendCharToRedobuff(cap->ncharC1);
5166 if (cap->ncharC2 != 0)
5167 AppendCharToRedobuff(cap->ncharC2);
5168
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005169 // This is slow, but it handles replacing a single-byte with a
5170 // multi-byte and the other way around. Also handles adding
5171 // composing characters for utf-8.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005172 for (n = cap->count1; n > 0; --n)
5173 {
5174 State = REPLACE;
Bram Moolenaar8320da42012-04-30 18:18:47 +02005175 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
5176 {
5177 int c = ins_copychar(curwin->w_cursor.lnum
5178 + (cap->nchar == Ctrl_Y ? -1 : 1));
5179 if (c != NUL)
5180 ins_char(c);
5181 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005182 // will be decremented further down
Bram Moolenaar8320da42012-04-30 18:18:47 +02005183 ++curwin->w_cursor.col;
5184 }
5185 else
5186 ins_char(cap->nchar);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005187 State = old_State;
5188 if (cap->ncharC1 != 0)
5189 ins_char(cap->ncharC1);
5190 if (cap->ncharC2 != 0)
5191 ins_char(cap->ncharC2);
5192 }
5193 }
5194 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005195 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00005196 // Replace the characters within one line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005197 for (n = cap->count1; n > 0; --n)
5198 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00005199 // Get ptr again, because u_save and/or showmatch() will have
5200 // released the line. This may also happen in ins_copychar().
5201 // At the same time we let know that the line will be changed.
Bram Moolenaar8320da42012-04-30 18:18:47 +02005202 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
5203 {
5204 int c = ins_copychar(curwin->w_cursor.lnum
5205 + (cap->nchar == Ctrl_Y ? -1 : 1));
Bram Moolenaar35a9a002021-09-11 21:14:20 +02005206
5207 ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
Bram Moolenaar8320da42012-04-30 18:18:47 +02005208 if (c != NUL)
5209 ptr[curwin->w_cursor.col] = c;
5210 }
5211 else
Bram Moolenaar35a9a002021-09-11 21:14:20 +02005212 {
5213 ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
Bram Moolenaar8320da42012-04-30 18:18:47 +02005214 ptr[curwin->w_cursor.col] = cap->nchar;
Bram Moolenaar35a9a002021-09-11 21:14:20 +02005215 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005216 if (p_sm && msg_silent == 0)
5217 showmatch(cap->nchar);
5218 ++curwin->w_cursor.col;
5219 }
5220#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005221 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005222 {
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005223 colnr_T start = (colnr_T)(curwin->w_cursor.col - cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005224
Bram Moolenaar009b2592004-10-24 19:18:58 +00005225 netbeans_removed(curbuf, curwin->w_cursor.lnum, start,
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00005226 cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227 netbeans_inserted(curbuf, curwin->w_cursor.lnum, start,
Bram Moolenaar009b2592004-10-24 19:18:58 +00005228 &ptr[start], (int)cap->count1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005229 }
5230#endif
5231
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005232 // mark the buffer as changed and prepare for displaying
Bram Moolenaar071d4272004-06-13 20:20:40 +00005233 changed_bytes(curwin->w_cursor.lnum,
5234 (colnr_T)(curwin->w_cursor.col - cap->count1));
5235 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005236 --curwin->w_cursor.col; // cursor on the last replaced char
5237 // if the character on the left of the current cursor is a multi-byte
5238 // character, move two characters left
Bram Moolenaar071d4272004-06-13 20:20:40 +00005239 if (has_mbyte)
5240 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241 curbuf->b_op_end = curwin->w_cursor;
5242 curwin->w_set_curswant = TRUE;
5243 set_last_insert(cap->nchar);
5244 }
5245}
5246
Bram Moolenaar071d4272004-06-13 20:20:40 +00005247/*
5248 * 'o': Exchange start and end of Visual area.
5249 * 'O': same, but in block mode exchange left and right corners.
5250 */
5251 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005252v_swap_corners(int cmdchar)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005253{
5254 pos_T old_cursor;
5255 colnr_T left, right;
5256
5257 if (cmdchar == 'O' && VIsual_mode == Ctrl_V)
5258 {
5259 old_cursor = curwin->w_cursor;
5260 getvcols(curwin, &old_cursor, &VIsual, &left, &right);
5261 curwin->w_cursor.lnum = VIsual.lnum;
5262 coladvance(left);
5263 VIsual = curwin->w_cursor;
5264
5265 curwin->w_cursor.lnum = old_cursor.lnum;
5266 curwin->w_curswant = right;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005267 // 'selection "exclusive" and cursor at right-bottom corner: move it
5268 // right one column
Bram Moolenaar071d4272004-06-13 20:20:40 +00005269 if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e')
5270 ++curwin->w_curswant;
5271 coladvance(curwin->w_curswant);
5272 if (curwin->w_cursor.col == old_cursor.col
Bram Moolenaar071d4272004-06-13 20:20:40 +00005273 && (!virtual_active()
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01005274 || curwin->w_cursor.coladd == old_cursor.coladd))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275 {
5276 curwin->w_cursor.lnum = VIsual.lnum;
5277 if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e')
5278 ++right;
5279 coladvance(right);
5280 VIsual = curwin->w_cursor;
5281
5282 curwin->w_cursor.lnum = old_cursor.lnum;
5283 coladvance(left);
5284 curwin->w_curswant = left;
5285 }
5286 }
5287 else
5288 {
5289 old_cursor = curwin->w_cursor;
5290 curwin->w_cursor = VIsual;
5291 VIsual = old_cursor;
5292 curwin->w_set_curswant = TRUE;
5293 }
5294}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005295
5296/*
5297 * "R" (cap->arg is FALSE) and "gR" (cap->arg is TRUE).
5298 */
5299 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005300nv_Replace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005301{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005302 if (VIsual_active) // "R" is replace lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303 {
5304 cap->cmdchar = 'c';
5305 cap->nchar = NUL;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005306 VIsual_mode_orig = VIsual_mode; // remember original area for gv
Bram Moolenaar071d4272004-06-13 20:20:40 +00005307 VIsual_mode = 'V';
5308 nv_operator(cap);
5309 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01005310 else if (!checkclearopq(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005311 {
5312 if (!curbuf->b_p_ma)
Bram Moolenaar108010a2021-06-27 22:03:33 +02005313 emsg(_(e_cannot_make_changes_modifiable_is_off));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 else
5315 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005316 if (virtual_active())
5317 coladvance(getviscol());
Bram Moolenaar071d4272004-06-13 20:20:40 +00005318 invoke_edit(cap, FALSE, cap->arg ? 'V' : 'R', FALSE);
5319 }
5320 }
5321}
5322
Bram Moolenaar071d4272004-06-13 20:20:40 +00005323/*
5324 * "gr".
5325 */
5326 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005327nv_vreplace(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005328{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 if (VIsual_active)
5330 {
5331 cap->cmdchar = 'r';
5332 cap->nchar = cap->extra_char;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005333 nv_replace(cap); // Do same as "r" in Visual mode for now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005334 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01005335 else if (!checkclearopq(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336 {
5337 if (!curbuf->b_p_ma)
Bram Moolenaar108010a2021-06-27 22:03:33 +02005338 emsg(_(e_cannot_make_changes_modifiable_is_off));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005339 else
5340 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005341 if (cap->extra_char == Ctrl_V) // get another character
Bram Moolenaar0684e362020-12-03 19:54:42 +01005342 cap->extra_char = get_literal(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005343 stuffcharReadbuff(cap->extra_char);
5344 stuffcharReadbuff(ESC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005345 if (virtual_active())
5346 coladvance(getviscol());
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347 invoke_edit(cap, TRUE, 'v', FALSE);
5348 }
5349 }
5350}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351
5352/*
5353 * Swap case for "~" command, when it does not work like an operator.
5354 */
5355 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005356n_swapchar(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357{
5358 long n;
5359 pos_T startpos;
5360 int did_change = 0;
5361#ifdef FEAT_NETBEANS_INTG
5362 pos_T pos;
5363 char_u *ptr;
5364 int count;
5365#endif
5366
5367 if (checkclearopq(cap->oap))
5368 return;
5369
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01005370 if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005371 {
5372 clearopbeep(cap->oap);
5373 return;
5374 }
5375
5376 prep_redo_cmd(cap);
5377
5378 if (u_save_cursor() == FAIL)
5379 return;
5380
5381 startpos = curwin->w_cursor;
5382#ifdef FEAT_NETBEANS_INTG
5383 pos = startpos;
5384#endif
5385 for (n = cap->count1; n > 0; --n)
5386 {
5387 did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
5388 inc_cursor();
5389 if (gchar_cursor() == NUL)
5390 {
5391 if (vim_strchr(p_ww, '~') != NULL
5392 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
5393 {
5394#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005395 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005396 {
5397 if (did_change)
5398 {
5399 ptr = ml_get(pos.lnum);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00005400 count = (int)STRLEN(ptr) - pos.col;
Bram Moolenaar009b2592004-10-24 19:18:58 +00005401 netbeans_removed(curbuf, pos.lnum, pos.col,
5402 (long)count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005403 netbeans_inserted(curbuf, pos.lnum, pos.col,
Bram Moolenaar009b2592004-10-24 19:18:58 +00005404 &ptr[pos.col], count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005405 }
5406 pos.col = 0;
5407 pos.lnum++;
5408 }
5409#endif
5410 ++curwin->w_cursor.lnum;
5411 curwin->w_cursor.col = 0;
5412 if (n > 1)
5413 {
5414 if (u_savesub(curwin->w_cursor.lnum) == FAIL)
5415 break;
5416 u_clearline();
5417 }
5418 }
5419 else
5420 break;
5421 }
5422 }
5423#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02005424 if (did_change && netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00005425 {
5426 ptr = ml_get(pos.lnum);
5427 count = curwin->w_cursor.col - pos.col;
Bram Moolenaar009b2592004-10-24 19:18:58 +00005428 netbeans_removed(curbuf, pos.lnum, pos.col, (long)count);
5429 netbeans_inserted(curbuf, pos.lnum, pos.col, &ptr[pos.col], count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005430 }
5431#endif
5432
5433
5434 check_cursor();
5435 curwin->w_set_curswant = TRUE;
5436 if (did_change)
5437 {
5438 changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1,
5439 0L);
5440 curbuf->b_op_start = startpos;
5441 curbuf->b_op_end = curwin->w_cursor;
5442 if (curbuf->b_op_end.col > 0)
5443 --curbuf->b_op_end.col;
5444 }
5445}
5446
5447/*
5448 * Move cursor to mark.
5449 */
5450 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005451nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005452{
5453 if (check_mark(pos) == FAIL)
5454 clearop(cap->oap);
5455 else
5456 {
5457 if (cap->cmdchar == '\''
5458 || cap->cmdchar == '`'
5459 || cap->cmdchar == '['
5460 || cap->cmdchar == ']')
5461 setpcmark();
5462 curwin->w_cursor = *pos;
5463 if (flag)
5464 beginline(BL_WHITE | BL_FIX);
5465 else
5466 check_cursor();
5467 }
5468 cap->oap->motion_type = flag ? MLINE : MCHAR;
5469 if (cap->cmdchar == '`')
5470 cap->oap->use_reg_one = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005471 cap->oap->inclusive = FALSE; // ignored if not MCHAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00005472 curwin->w_set_curswant = TRUE;
5473}
5474
Bram Moolenaar071d4272004-06-13 20:20:40 +00005475/*
5476 * Handle commands that are operators in Visual mode.
5477 */
5478 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005479v_visop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005480{
5481 static char_u trans[] = "YyDdCcxdXdAAIIrr";
5482
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005483 // Uppercase means linewise, except in block mode, then "D" deletes till
5484 // the end of the line, and "C" replaces till EOL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005485 if (isupper(cap->cmdchar))
5486 {
5487 if (VIsual_mode != Ctrl_V)
Bram Moolenaara390bb62013-03-13 19:02:41 +01005488 {
5489 VIsual_mode_orig = VIsual_mode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005490 VIsual_mode = 'V';
Bram Moolenaara390bb62013-03-13 19:02:41 +01005491 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005492 else if (cap->cmdchar == 'C' || cap->cmdchar == 'D')
5493 curwin->w_curswant = MAXCOL;
5494 }
5495 cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
5496 nv_operator(cap);
5497}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005498
5499/*
5500 * "s" and "S" commands.
5501 */
5502 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005503nv_subst(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005504{
Bram Moolenaard96ff162018-02-18 22:13:29 +01005505#ifdef FEAT_TERMINAL
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01005506 // When showing output of term_dumpdiff() swap the top and bottom.
Bram Moolenaard96ff162018-02-18 22:13:29 +01005507 if (term_swap_diff() == OK)
5508 return;
5509#endif
Bram Moolenaarf2732452018-06-03 14:47:35 +02005510#ifdef FEAT_JOB_CHANNEL
5511 if (bt_prompt(curbuf) && !prompt_curpos_editable())
5512 {
5513 clearopbeep(cap->oap);
5514 return;
5515 }
5516#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005517 if (VIsual_active) // "vs" and "vS" are the same as "vc"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005518 {
5519 if (cap->cmdchar == 'S')
Bram Moolenaara390bb62013-03-13 19:02:41 +01005520 {
5521 VIsual_mode_orig = VIsual_mode;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522 VIsual_mode = 'V';
Bram Moolenaara390bb62013-03-13 19:02:41 +01005523 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524 cap->cmdchar = 'c';
5525 nv_operator(cap);
5526 }
5527 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528 nv_optrans(cap);
5529}
5530
5531/*
5532 * Abbreviated commands.
5533 */
5534 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005535nv_abbrev(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536{
5537 if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005538 cap->cmdchar = 'x'; // DEL key behaves like 'x'
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005540 // in Visual mode these commands are operators
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541 if (VIsual_active)
5542 v_visop(cap);
5543 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005544 nv_optrans(cap);
5545}
5546
5547/*
5548 * Translate a command into another command.
5549 */
5550 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005551nv_optrans(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005552{
5553 static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
5554 (char_u *)"d$", (char_u *)"c$",
5555 (char_u *)"cl", (char_u *)"cc",
5556 (char_u *)"yy", (char_u *)":s\r"};
5557 static char_u *str = (char_u *)"xXDCsSY&";
5558
5559 if (!checkclearopq(cap->oap))
5560 {
Bram Moolenaar7a9bd7c2019-09-17 22:42:55 +02005561 // In Vi "2D" doesn't delete the next line. Can't translate it
5562 // either, because "2." should also not use the count.
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005563 if (cap->cmdchar == 'D' && vim_strchr(p_cpo, CPO_HASH) != NULL)
5564 {
5565 cap->oap->start = curwin->w_cursor;
5566 cap->oap->op_type = OP_DELETE;
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00005567#ifdef FEAT_EVAL
5568 set_op_var(OP_DELETE);
5569#endif
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005570 cap->count1 = 1;
5571 nv_dollar(cap);
5572 finish_op = TRUE;
5573 ResetRedobuff();
5574 AppendCharToRedobuff('D');
5575 }
5576 else
5577 {
5578 if (cap->count0)
5579 stuffnumReadbuff(cap->count0);
Bram Moolenaar56ebbab2019-09-20 13:40:14 +02005580 stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]);
Bram Moolenaar4399ef42005-02-12 14:29:27 +00005581 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 }
5583 cap->opcount = 0;
5584}
5585
5586/*
5587 * "'" and "`" commands. Also for "g'" and "g`".
5588 * cap->arg is TRUE for "'" and "g'".
5589 */
5590 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005591nv_gomark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592{
5593 pos_T *pos;
5594 int c;
5595#ifdef FEAT_FOLDING
Bram Moolenaar8754deb2013-01-17 13:24:08 +01005596 pos_T old_cursor = curwin->w_cursor;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005597 int old_KeyTyped = KeyTyped; // getting file may reset it
Bram Moolenaar071d4272004-06-13 20:20:40 +00005598#endif
5599
5600 if (cap->cmdchar == 'g')
5601 c = cap->extra_char;
5602 else
5603 c = cap->nchar;
5604 pos = getmark(c, (cap->oap->op_type == OP_NOP));
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005605 if (pos == (pos_T *)-1) // jumped to other file
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 {
5607 if (cap->arg)
5608 {
5609 check_cursor_lnum();
5610 beginline(BL_WHITE | BL_FIX);
5611 }
5612 else
5613 check_cursor();
5614 }
5615 else
5616 nv_cursormark(cap, cap->arg, pos);
5617
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005618 // May need to clear the coladd that a mark includes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619 if (!virtual_active())
5620 curwin->w_cursor.coladd = 0;
Bram Moolenaar9aa15692017-08-19 15:05:32 +02005621 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622#ifdef FEAT_FOLDING
5623 if (cap->oap->op_type == OP_NOP
Bram Moolenaar15364d72013-01-24 21:00:20 +01005624 && pos != NULL
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01005625 && (pos == (pos_T *)-1 || !EQUAL_POS(old_cursor, *pos))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626 && (fdo_flags & FDO_MARK)
5627 && old_KeyTyped)
5628 foldOpenCursor();
5629#endif
5630}
5631
5632/*
Bram Moolenaar62a23252020-08-09 14:04:42 +02005633 * Handle CTRL-O, CTRL-I, "g;", "g," and "CTRL-Tab" commands.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005634 */
5635 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005636nv_pcmark(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005638 pos_T *pos;
Bram Moolenaar739f13a2021-12-13 13:12:53 +00005639#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005640 linenr_T lnum = curwin->w_cursor.lnum;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005641 int old_KeyTyped = KeyTyped; // getting file may reset it
Bram Moolenaar739f13a2021-12-13 13:12:53 +00005642#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005643
5644 if (!checkclearopq(cap->oap))
5645 {
Bram Moolenaar62a23252020-08-09 14:04:42 +02005646 if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL)
5647 {
5648 if (goto_tabpage_lastused() == FAIL)
5649 clearopbeep(cap->oap);
5650 return;
5651 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005652 if (cap->cmdchar == 'g')
5653 pos = movechangelist((int)cap->count1);
5654 else
5655 pos = movemark((int)cap->count1);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005656 if (pos == (pos_T *)-1) // jump to other file
Bram Moolenaar071d4272004-06-13 20:20:40 +00005657 {
5658 curwin->w_set_curswant = TRUE;
5659 check_cursor();
5660 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005661 else if (pos != NULL) // can jump
Bram Moolenaar071d4272004-06-13 20:20:40 +00005662 nv_cursormark(cap, FALSE, pos);
5663 else if (cap->cmdchar == 'g')
5664 {
5665 if (curbuf->b_changelistlen == 0)
Bram Moolenaara6f79292022-01-04 21:30:47 +00005666 emsg(_(e_changelist_is_empty));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005667 else if (cap->count1 < 0)
Bram Moolenaara6f79292022-01-04 21:30:47 +00005668 emsg(_(e_at_start_of_changelist));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005669 else
Bram Moolenaara6f79292022-01-04 21:30:47 +00005670 emsg(_(e_at_end_of_changelist));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671 }
5672 else
5673 clearopbeep(cap->oap);
5674# ifdef FEAT_FOLDING
5675 if (cap->oap->op_type == OP_NOP
5676 && (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum)
5677 && (fdo_flags & FDO_MARK)
5678 && old_KeyTyped)
5679 foldOpenCursor();
5680# endif
5681 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005682}
5683
5684/*
5685 * Handle '"' command.
5686 */
5687 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005688nv_regname(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005689{
5690 if (checkclearop(cap->oap))
5691 return;
5692#ifdef FEAT_EVAL
5693 if (cap->nchar == '=')
5694 cap->nchar = get_expr_register();
5695#endif
5696 if (cap->nchar != NUL && valid_yank_reg(cap->nchar, FALSE))
5697 {
5698 cap->oap->regname = cap->nchar;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005699 cap->opcount = cap->count0; // remember count before '"'
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700#ifdef FEAT_EVAL
5701 set_reg_var(cap->oap->regname);
5702#endif
5703 }
5704 else
5705 clearopbeep(cap->oap);
5706}
5707
Bram Moolenaar071d4272004-06-13 20:20:40 +00005708/*
5709 * Handle "v", "V" and "CTRL-V" commands.
5710 * Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
5711 * is TRUE.
Bram Moolenaardf177f62005-02-22 08:39:57 +00005712 * Handle CTRL-Q just like CTRL-V.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005713 */
5714 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005715nv_visual(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716{
Bram Moolenaardf177f62005-02-22 08:39:57 +00005717 if (cap->cmdchar == Ctrl_Q)
5718 cap->cmdchar = Ctrl_V;
5719
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005720 // 'v', 'V' and CTRL-V can be used while an operator is pending to make it
5721 // characterwise, linewise, or blockwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722 if (cap->oap->op_type != OP_NOP)
5723 {
Bram Moolenaar5976f8f2018-12-27 23:44:44 +01005724 motion_force = cap->oap->motion_force = cap->cmdchar;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005725 finish_op = FALSE; // operator doesn't finish now but later
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726 return;
5727 }
5728
5729 VIsual_select = cap->arg;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005730 if (VIsual_active) // change Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005732 if (VIsual_mode == cap->cmdchar) // stop visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005733 end_visual_mode();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005734 else // toggle char/block mode
5735 { // or char/line mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736 VIsual_mode = cap->cmdchar;
5737 showmode();
=?UTF-8?q?Magnus=20Gro=C3=9F?=f1e88762021-09-12 13:39:55 +02005738 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005740 redraw_curbuf_later(INVERTED); // update the inversion
Bram Moolenaar071d4272004-06-13 20:20:40 +00005741 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005742 else // start Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005743 {
5744 check_visual_highlight();
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005745 if (cap->count0 > 0 && resel_VIsual_mode != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005746 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005747 // use previously selected part
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748 VIsual = curwin->w_cursor;
5749
5750 VIsual_active = TRUE;
5751 VIsual_reselect = TRUE;
5752 if (!cap->arg)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005753 // start Select mode when 'selectmode' contains "cmd"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005754 may_start_select('c');
Bram Moolenaar071d4272004-06-13 20:20:40 +00005755 setmouse();
Bram Moolenaar09df3122006-01-23 22:23:09 +00005756 if (p_smd && msg_silent == 0)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005757 redraw_cmdline = TRUE; // show visual mode later
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00005758 // For V and ^V, we multiply the number of lines even if there
5759 // was only one -- webb
Bram Moolenaar071d4272004-06-13 20:20:40 +00005760 if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
5761 {
5762 curwin->w_cursor.lnum +=
5763 resel_VIsual_line_count * cap->count0 - 1;
Bram Moolenaarb07626d2021-10-11 15:40:43 +01005764 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 }
5766 VIsual_mode = resel_VIsual_mode;
5767 if (VIsual_mode == 'v')
5768 {
5769 if (resel_VIsual_line_count <= 1)
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005770 {
5771 validate_virtcol();
5772 curwin->w_curswant = curwin->w_virtcol
5773 + resel_VIsual_vcol * cap->count0 - 1;
5774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005775 else
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005776 curwin->w_curswant = resel_VIsual_vcol;
5777 coladvance(curwin->w_curswant);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 }
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005779 if (resel_VIsual_vcol == MAXCOL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 {
5781 curwin->w_curswant = MAXCOL;
5782 coladvance((colnr_T)MAXCOL);
5783 }
5784 else if (VIsual_mode == Ctrl_V)
5785 {
5786 validate_virtcol();
5787 curwin->w_curswant = curwin->w_virtcol
Bram Moolenaarca0c9fc2011-10-04 21:22:44 +02005788 + resel_VIsual_vcol * cap->count0 - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005789 coladvance(curwin->w_curswant);
5790 }
5791 else
5792 curwin->w_set_curswant = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005793 redraw_curbuf_later(INVERTED); // show the inversion
Bram Moolenaar071d4272004-06-13 20:20:40 +00005794 }
5795 else
5796 {
5797 if (!cap->arg)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005798 // start Select mode when 'selectmode' contains "cmd"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005799 may_start_select('c');
5800 n_start_visual_mode(cap->cmdchar);
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005801 if (VIsual_mode != 'V' && *p_sel == 'e')
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005802 ++cap->count1; // include one more char
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005803 if (cap->count0 > 0 && --cap->count1 > 0)
5804 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005805 // With a count select that many characters or lines.
Bram Moolenaar6057b9c2012-05-25 13:12:36 +02005806 if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V)
5807 nv_right(cap);
5808 else if (VIsual_mode == 'V')
5809 nv_down(cap);
5810 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005811 }
5812 }
5813}
5814
5815/*
5816 * Start selection for Shift-movement keys.
5817 */
5818 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005819start_selection(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005820{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005821 // if 'selectmode' contains "key", start Select mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005822 may_start_select('k');
5823 n_start_visual_mode('v');
5824}
5825
5826/*
5827 * Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
5828 */
5829 void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005830may_start_select(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831{
5832 VIsual_select = (stuff_empty() && typebuf_typed()
5833 && (vim_strchr(p_slm, c) != NULL));
5834}
5835
5836/*
5837 * Start Visual mode "c".
5838 * Should set VIsual_select before calling this.
5839 */
5840 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005841n_start_visual_mode(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842{
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005843#ifdef FEAT_CONCEAL
Bram Moolenaarea042672021-06-29 20:22:32 +02005844 int cursor_line_was_concealed = curwin->w_p_cole > 0
5845 && conceal_cursor_line(curwin);
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005846#endif
5847
Bram Moolenaar071d4272004-06-13 20:20:40 +00005848 VIsual_mode = c;
5849 VIsual_active = TRUE;
5850 VIsual_reselect = TRUE;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01005851
5852 // Corner case: the 0 position in a tab may change when going into
Bram Moolenaar4b96df52020-01-26 22:00:26 +01005853 // virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
Gary Johnson53ba05b2021-07-26 22:19:10 +02005854 if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB)
Bram Moolenaar2dac2132012-08-15 13:31:00 +02005855 {
5856 validate_virtcol();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005857 coladvance(curwin->w_virtcol);
Bram Moolenaar2dac2132012-08-15 13:31:00 +02005858 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005859 VIsual = curwin->w_cursor;
5860
5861#ifdef FEAT_FOLDING
5862 foldAdjustVisual();
5863#endif
5864
Bram Moolenaara0620062021-11-17 16:52:40 +00005865 trigger_modechanged();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866 setmouse();
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005867#ifdef FEAT_CONCEAL
Bram Moolenaarea042672021-06-29 20:22:32 +02005868 // Check if redraw is needed after changing the state.
5869 conceal_check_cursor_line(cursor_line_was_concealed);
Bram Moolenaarf5963f72010-07-23 22:10:27 +02005870#endif
5871
Bram Moolenaar09df3122006-01-23 22:23:09 +00005872 if (p_smd && msg_silent == 0)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005873 redraw_cmdline = TRUE; // show visual mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00005874#ifdef FEAT_CLIPBOARD
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005875 // Make sure the clipboard gets updated. Needed because start and
5876 // end may still be the same, and the selection needs to be owned
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 clip_star.vmode = NUL;
5878#endif
5879
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005880 // Only need to redraw this line, unless still need to redraw an old
5881 // Visual area (when 'lazyredraw' is set).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005882 if (curwin->w_redr_type < INVERTED)
5883 {
5884 curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
5885 curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
5886 }
5887}
5888
Bram Moolenaar071d4272004-06-13 20:20:40 +00005889
5890/*
5891 * CTRL-W: Window commands
5892 */
5893 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005894nv_window(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005895{
Bram Moolenaar938783d2017-07-16 20:13:26 +02005896 if (cap->nchar == ':')
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005897 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005898 // "CTRL-W :" is the same as typing ":"; useful in a terminal window
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005899 cap->cmdchar = ':';
5900 cap->nchar = NUL;
Bram Moolenaar938783d2017-07-16 20:13:26 +02005901 nv_colon(cap);
Bram Moolenaar2efb3232017-12-19 12:27:23 +01005902 }
Bram Moolenaar938783d2017-07-16 20:13:26 +02005903 else if (!checkclearop(cap->oap))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005904 do_window(cap->nchar, cap->count0, NUL); // everything is in window.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00005905}
5906
5907/*
5908 * CTRL-Z: Suspend
5909 */
5910 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01005911nv_suspend(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005912{
5913 clearop(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005914 if (VIsual_active)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01005915 end_visual_mode(); // stop Visual mode
Bram Moolenaar100118c2020-12-11 19:30:34 +01005916 do_cmdline_cmd((char_u *)"stop");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917}
5918
5919/*
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00005920 * "gv": Reselect the previous Visual area. If Visual already active,
5921 * exchange previous and current Visual area.
5922 */
5923 static void
5924nv_gv_cmd(cmdarg_T *cap)
5925{
5926 pos_T tpos;
5927 int i;
5928
5929 if (checkclearop(cap->oap))
5930 return;
5931
5932 if (curbuf->b_visual.vi_start.lnum == 0
5933 || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
5934 || curbuf->b_visual.vi_end.lnum == 0)
5935 {
5936 beep_flush();
5937 return;
5938 }
5939
5940 // set w_cursor to the start of the Visual area, tpos to the end
5941 if (VIsual_active)
5942 {
5943 i = VIsual_mode;
5944 VIsual_mode = curbuf->b_visual.vi_mode;
5945 curbuf->b_visual.vi_mode = i;
5946# ifdef FEAT_EVAL
5947 curbuf->b_visual_mode_eval = i;
5948# endif
5949 i = curwin->w_curswant;
5950 curwin->w_curswant = curbuf->b_visual.vi_curswant;
5951 curbuf->b_visual.vi_curswant = i;
5952
5953 tpos = curbuf->b_visual.vi_end;
5954 curbuf->b_visual.vi_end = curwin->w_cursor;
5955 curwin->w_cursor = curbuf->b_visual.vi_start;
5956 curbuf->b_visual.vi_start = VIsual;
5957 }
5958 else
5959 {
5960 VIsual_mode = curbuf->b_visual.vi_mode;
5961 curwin->w_curswant = curbuf->b_visual.vi_curswant;
5962 tpos = curbuf->b_visual.vi_end;
5963 curwin->w_cursor = curbuf->b_visual.vi_start;
5964 }
5965
5966 VIsual_active = TRUE;
5967 VIsual_reselect = TRUE;
5968
5969 // Set Visual to the start and w_cursor to the end of the Visual
5970 // area. Make sure they are on an existing character.
5971 check_cursor();
5972 VIsual = curwin->w_cursor;
5973 curwin->w_cursor = tpos;
5974 check_cursor();
5975 update_topline();
5976
5977 // When called from normal "g" command: start Select mode when
5978 // 'selectmode' contains "cmd". When called for K_SELECT, always
5979 // start Select mode.
5980 if (cap->arg)
5981 {
5982 VIsual_select = TRUE;
5983 VIsual_select_reg = 0;
5984 }
5985 else
5986 may_start_select('c');
5987 setmouse();
5988#ifdef FEAT_CLIPBOARD
5989 // Make sure the clipboard gets updated. Needed because start and
5990 // end are still the same, and the selection needs to be owned
5991 clip_star.vmode = NUL;
5992#endif
5993 redraw_curbuf_later(INVERTED);
5994 showmode();
5995}
5996
5997/*
5998 * "g0", "g^" : Like "0" and "^" but for screen lines.
5999 * "gm": middle of "g0" and "g$".
6000 */
6001 static void
6002nv_g_home_m_cmd(cmdarg_T *cap)
6003{
6004 int i;
6005 int flag = FALSE;
6006
6007 if (cap->nchar == '^')
6008 flag = TRUE;
6009
6010 cap->oap->motion_type = MCHAR;
6011 cap->oap->inclusive = FALSE;
6012 if (curwin->w_p_wrap && curwin->w_width != 0)
6013 {
6014 int width1 = curwin->w_width - curwin_col_off();
6015 int width2 = width1 + curwin_col_off2();
6016
6017 validate_virtcol();
6018 i = 0;
6019 if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0)
6020 i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
6021 }
6022 else
6023 i = curwin->w_leftcol;
6024 // Go to the middle of the screen line. When 'number' or
6025 // 'relativenumber' is on and lines are wrapping the middle can be more
6026 // to the left.
6027 if (cap->nchar == 'm')
6028 i += (curwin->w_width - curwin_col_off()
6029 + ((curwin->w_p_wrap && i > 0)
6030 ? curwin_col_off2() : 0)) / 2;
6031 coladvance((colnr_T)i);
6032 if (flag)
6033 {
6034 do
6035 i = gchar_cursor();
6036 while (VIM_ISWHITE(i) && oneright() == OK);
6037 curwin->w_valid &= ~VALID_WCOL;
6038 }
6039 curwin->w_set_curswant = TRUE;
6040}
6041
6042/*
6043 * "g_": to the last non-blank character in the line or <count> lines
6044 * downward.
6045 */
6046 static void
6047nv_g_underscore_cmd(cmdarg_T *cap)
6048{
6049 char_u *ptr;
6050
6051 cap->oap->motion_type = MCHAR;
6052 cap->oap->inclusive = TRUE;
6053 curwin->w_curswant = MAXCOL;
6054 if (cursor_down((long)(cap->count1 - 1),
6055 cap->oap->op_type == OP_NOP) == FAIL)
6056 {
6057 clearopbeep(cap->oap);
6058 return;
6059 }
6060
6061 ptr = ml_get_curline();
6062
6063 // In Visual mode we may end up after the line.
6064 if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL)
6065 --curwin->w_cursor.col;
6066
6067 // Decrease the cursor column until it's on a non-blank.
6068 while (curwin->w_cursor.col > 0
6069 && VIM_ISWHITE(ptr[curwin->w_cursor.col]))
6070 --curwin->w_cursor.col;
6071 curwin->w_set_curswant = TRUE;
6072 adjust_for_sel(cap);
6073}
6074
6075/*
6076 * "g$" : Like "$" but for screen lines.
6077 */
6078 static void
6079nv_g_dollar_cmd(cmdarg_T *cap)
6080{
6081 oparg_T *oap = cap->oap;
6082 int i;
6083 int col_off = curwin_col_off();
6084
6085 oap->motion_type = MCHAR;
6086 oap->inclusive = TRUE;
6087 if (curwin->w_p_wrap && curwin->w_width != 0)
6088 {
6089 curwin->w_curswant = MAXCOL; // so we stay at the end
6090 if (cap->count1 == 1)
6091 {
6092 int width1 = curwin->w_width - col_off;
6093 int width2 = width1 + curwin_col_off2();
6094
6095 validate_virtcol();
6096 i = width1 - 1;
6097 if (curwin->w_virtcol >= (colnr_T)width1)
6098 i += ((curwin->w_virtcol - width1) / width2 + 1)
6099 * width2;
6100 coladvance((colnr_T)i);
6101
6102 // Make sure we stick in this column.
6103 validate_virtcol();
6104 curwin->w_curswant = curwin->w_virtcol;
6105 curwin->w_set_curswant = FALSE;
6106 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
6107 {
6108 // Check for landing on a character that got split at
6109 // the end of the line. We do not want to advance to
6110 // the next screen line.
6111 if (curwin->w_virtcol > (colnr_T)i)
6112 --curwin->w_cursor.col;
6113 }
6114 }
6115 else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == FAIL)
6116 clearopbeep(oap);
6117 }
6118 else
6119 {
6120 if (cap->count1 > 1)
6121 // if it fails, let the cursor still move to the last char
6122 (void)cursor_down(cap->count1 - 1, FALSE);
6123
6124 i = curwin->w_leftcol + curwin->w_width - col_off - 1;
6125 coladvance((colnr_T)i);
6126
6127 // if the character doesn't fit move one back
6128 if (curwin->w_cursor.col > 0
6129 && (*mb_ptr2cells)(ml_get_cursor()) > 1)
6130 {
6131 colnr_T vcol;
6132
6133 getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
6134 if (vcol >= curwin->w_leftcol + curwin->w_width - col_off)
6135 --curwin->w_cursor.col;
6136 }
6137
6138 // Make sure we stick in this column.
6139 validate_virtcol();
6140 curwin->w_curswant = curwin->w_virtcol;
6141 curwin->w_set_curswant = FALSE;
6142 }
6143}
6144
6145/*
6146 * "gi": start Insert at the last position.
6147 */
6148 static void
6149nv_gi_cmd(cmdarg_T *cap)
6150{
6151 int i;
6152
6153 if (curbuf->b_last_insert.lnum != 0)
6154 {
6155 curwin->w_cursor = curbuf->b_last_insert;
6156 check_cursor_lnum();
6157 i = (int)STRLEN(ml_get_curline());
6158 if (curwin->w_cursor.col > (colnr_T)i)
6159 {
6160 if (virtual_active())
6161 curwin->w_cursor.coladd += curwin->w_cursor.col - i;
6162 curwin->w_cursor.col = i;
6163 }
6164 }
6165 cap->cmdchar = 'i';
6166 nv_edit(cap);
6167}
6168
6169/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006170 * Commands starting with "g".
6171 */
6172 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006173nv_g_cmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006174{
6175 oparg_T *oap = cap->oap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006176 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006177
6178 switch (cap->nchar)
6179 {
Bram Moolenaar3a304b22015-06-25 13:57:36 +02006180 case Ctrl_A:
6181 case Ctrl_X:
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182#ifdef MEM_PROFILE
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006183 // "g^A": dump log of used memory.
Bram Moolenaar3a304b22015-06-25 13:57:36 +02006184 if (!VIsual_active && cap->nchar == Ctrl_A)
6185 vim_mem_profile_dump();
6186 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006187#endif
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006188 // "g^A/g^X": sequentially increment visually selected region
Bram Moolenaar3a304b22015-06-25 13:57:36 +02006189 if (VIsual_active)
6190 {
6191 cap->arg = TRUE;
6192 cap->cmdchar = cap->nchar;
Bram Moolenaard79e5502016-01-10 22:13:02 +01006193 cap->nchar = NUL;
Bram Moolenaar3a304b22015-06-25 13:57:36 +02006194 nv_addsub(cap);
6195 }
6196 else
6197 clearopbeep(oap);
6198 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006199
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006200 // "gR": Enter virtual replace mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006201 case 'R':
6202 cap->arg = TRUE;
6203 nv_Replace(cap);
6204 break;
6205
6206 case 'r':
6207 nv_vreplace(cap);
6208 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006209
6210 case '&':
6211 do_cmdline_cmd((char_u *)"%s//~/&");
6212 break;
6213
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006214 // "gv": Reselect the previous Visual area. If Visual already active,
6215 // exchange previous and current Visual area.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216 case 'v':
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006217 nv_gv_cmd(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006218 break;
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006219
6220 // "gV": Don't reselect the previous Visual area after a Select mode
6221 // mapping of menu.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006222 case 'V':
6223 VIsual_reselect = FALSE;
6224 break;
6225
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006226 // "gh": start Select mode.
6227 // "gH": start Select line mode.
6228 // "g^H": start Select block mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006229 case K_BS:
6230 cap->nchar = Ctrl_H;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006231 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00006232 case 'h':
6233 case 'H':
6234 case Ctrl_H:
6235# ifdef EBCDIC
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006236 // EBCDIC: 'v'-'h' != '^v'-'^h'
Bram Moolenaar071d4272004-06-13 20:20:40 +00006237 if (cap->nchar == Ctrl_H)
6238 cap->cmdchar = Ctrl_V;
6239 else
6240# endif
6241 cap->cmdchar = cap->nchar + ('v' - 'h');
6242 cap->arg = TRUE;
6243 nv_visual(cap);
6244 break;
Bram Moolenaar641e2862012-07-25 15:06:34 +02006245
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006246 // "gn", "gN" visually select next/previous search match
6247 // "gn" selects next match
6248 // "gN" selects previous match
Bram Moolenaar641e2862012-07-25 15:06:34 +02006249 case 'N':
6250 case 'n':
6251 if (!current_search(cap->count1, cap->nchar == 'n'))
Bram Moolenaarf00dc262012-10-21 03:54:33 +02006252 clearopbeep(oap);
Bram Moolenaar641e2862012-07-25 15:06:34 +02006253 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006254
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006255 // "gj" and "gk" two new funny movement keys -- up and down
6256 // movement based on *screen* line rather than *file* line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006257 case 'j':
6258 case K_DOWN:
Bram Moolenaare71996b2021-01-21 17:03:07 +01006259 // with 'nowrap' it works just like the normal "j" command.
6260 if (!curwin->w_p_wrap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006261 {
6262 oap->motion_type = MLINE;
6263 i = cursor_down(cap->count1, oap->op_type == OP_NOP);
6264 }
6265 else
6266 i = nv_screengo(oap, FORWARD, cap->count1);
6267 if (i == FAIL)
6268 clearopbeep(oap);
6269 break;
6270
6271 case 'k':
6272 case K_UP:
Bram Moolenaare71996b2021-01-21 17:03:07 +01006273 // with 'nowrap' it works just like the normal "k" command.
6274 if (!curwin->w_p_wrap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006275 {
6276 oap->motion_type = MLINE;
6277 i = cursor_up(cap->count1, oap->op_type == OP_NOP);
6278 }
6279 else
6280 i = nv_screengo(oap, BACKWARD, cap->count1);
6281 if (i == FAIL)
6282 clearopbeep(oap);
6283 break;
6284
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006285 // "gJ": join two lines without inserting a space.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006286 case 'J':
6287 nv_join(cap);
6288 break;
6289
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006290 // "g0", "g^" : Like "0" and "^" but for screen lines.
6291 // "gm": middle of "g0" and "g$".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006292 case '^':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006293 case '0':
6294 case 'm':
6295 case K_HOME:
6296 case K_KHOME:
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006297 nv_g_home_m_cmd(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006298 break;
6299
Bram Moolenaar8b530c12019-10-28 02:13:05 +01006300 case 'M':
6301 {
Bram Moolenaar8b530c12019-10-28 02:13:05 +01006302 oap->motion_type = MCHAR;
6303 oap->inclusive = FALSE;
Bram Moolenaar71c41252021-12-26 15:00:07 +00006304 i = linetabsize(ml_get_curline());
Bram Moolenaar8b530c12019-10-28 02:13:05 +01006305 if (cap->count0 > 0 && cap->count0 <= 100)
6306 coladvance((colnr_T)(i * cap->count0 / 100));
6307 else
6308 coladvance((colnr_T)(i / 2));
6309 curwin->w_set_curswant = TRUE;
6310 }
6311 break;
6312
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006313 // "g_": to the last non-blank character in the line or <count> lines
6314 // downward.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006315 case '_':
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006316 nv_g_underscore_cmd(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006317 break;
6318
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006319 // "g$" : Like "$" but for screen lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320 case '$':
6321 case K_END:
6322 case K_KEND:
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006323 nv_g_dollar_cmd(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006324 break;
6325
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006326 // "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327 case '*':
6328 case '#':
6329#if POUND != '#'
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006330 case POUND: // pound sign (sometimes equal to '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006332 case Ctrl_RSB: // :tag or :tselect for current identifier
6333 case ']': // :tselect for current identifier
Bram Moolenaar071d4272004-06-13 20:20:40 +00006334 nv_ident(cap);
6335 break;
6336
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006337 // ge and gE: go back to end of word
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338 case 'e':
6339 case 'E':
6340 oap->motion_type = MCHAR;
6341 curwin->w_set_curswant = TRUE;
6342 oap->inclusive = TRUE;
6343 if (bckend_word(cap->count1, cap->nchar == 'E', FALSE) == FAIL)
6344 clearopbeep(oap);
6345 break;
6346
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006347 // "g CTRL-G": display info about cursor position
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348 case Ctrl_G:
Bram Moolenaared767a22016-01-03 22:49:16 +01006349 cursor_pos_info(NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350 break;
6351
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006352 // "gi": start Insert at the last position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353 case 'i':
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006354 nv_gi_cmd(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006355 break;
6356
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006357 // "gI": Start insert in column 1.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006358 case 'I':
6359 beginline(0);
6360 if (!checkclearopq(oap))
6361 invoke_edit(cap, FALSE, 'g', FALSE);
6362 break;
6363
6364#ifdef FEAT_SEARCHPATH
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006365 // "gf": goto file, edit file under cursor
6366 // "]f" and "[f": can also be used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367 case 'f':
Bram Moolenaard1f56e62006-02-22 21:25:37 +00006368 case 'F':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006369 nv_gotofile(cap);
6370 break;
6371#endif
6372
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006373 // "g'm" and "g`m": jump to mark without setting pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00006374 case '\'':
6375 cap->arg = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006376 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00006377 case '`':
6378 nv_gomark(cap);
6379 break;
6380
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006381 // "gs": Goto sleep.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382 case 's':
Bram Moolenaare2edc2e2021-01-16 20:21:23 +01006383 do_sleep(cap->count1 * 1000L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384 break;
6385
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006386 // "ga": Display the ascii value of the character under the
6387 // cursor. It is displayed in decimal, hex, and octal. -- webb
Bram Moolenaar071d4272004-06-13 20:20:40 +00006388 case 'a':
6389 do_ascii(NULL);
6390 break;
6391
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006392 // "g8": Display the bytes used for the UTF-8 character under the
6393 // cursor. It is displayed in hex.
6394 // "8g8" finds illegal byte sequence.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006395 case '8':
Bram Moolenaara83c3e02006-03-17 23:10:44 +00006396 if (cap->count0 == 8)
6397 utf_find_illegal();
6398 else
6399 show_utf8();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006400 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006401
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006402 // "g<": show scrollback text
Bram Moolenaarcfc7d632005-07-28 22:28:16 +00006403 case '<':
6404 show_sb_text();
6405 break;
6406
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006407 // "gg": Goto the first line in file. With a count it goes to
6408 // that line number like for "G". -- webb
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409 case 'g':
6410 cap->arg = FALSE;
6411 nv_goto(cap);
6412 break;
6413
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006414 // Two-character operators:
6415 // "gq" Format text
6416 // "gw" Format text and keep cursor position
6417 // "g~" Toggle the case of the text.
6418 // "gu" Change text to lower case.
6419 // "gU" Change text to upper case.
6420 // "g?" rot13 encoding
6421 // "g@" call 'operatorfunc'
Bram Moolenaar071d4272004-06-13 20:20:40 +00006422 case 'q':
6423 case 'w':
6424 oap->cursor_start = curwin->w_cursor;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006425 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00006426 case '~':
6427 case 'u':
6428 case 'U':
6429 case '?':
Bram Moolenaar12033fb2005-12-16 21:49:31 +00006430 case '@':
Bram Moolenaar071d4272004-06-13 20:20:40 +00006431 nv_operator(cap);
6432 break;
6433
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006434 // "gd": Find first occurrence of pattern under the cursor in the
6435 // current function
6436 // "gD": idem, but in the current file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006437 case 'd':
6438 case 'D':
Bram Moolenaarf75a9632005-09-13 21:20:47 +00006439 nv_gd(oap, cap->nchar, (int)cap->count0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006440 break;
6441
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006442 // g<*Mouse> : <C-*mouse>
Bram Moolenaar071d4272004-06-13 20:20:40 +00006443 case K_MIDDLEMOUSE:
6444 case K_MIDDLEDRAG:
6445 case K_MIDDLERELEASE:
6446 case K_LEFTMOUSE:
6447 case K_LEFTDRAG:
6448 case K_LEFTRELEASE:
Bram Moolenaar51b0f372017-11-18 18:52:04 +01006449 case K_MOUSEMOVE:
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450 case K_RIGHTMOUSE:
6451 case K_RIGHTDRAG:
6452 case K_RIGHTRELEASE:
6453 case K_X1MOUSE:
6454 case K_X1DRAG:
6455 case K_X1RELEASE:
6456 case K_X2MOUSE:
6457 case K_X2DRAG:
6458 case K_X2RELEASE:
6459 mod_mask = MOD_MASK_CTRL;
6460 (void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0);
6461 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462
6463 case K_IGNORE:
6464 break;
6465
Yegappan Lakshmanan05386ca2022-01-20 20:18:27 +00006466 // "gP" and "gp": same as "P" and "p" but leave cursor just after new text
Bram Moolenaar071d4272004-06-13 20:20:40 +00006467 case 'p':
6468 case 'P':
6469 nv_put(cap);
6470 break;
6471
6472#ifdef FEAT_BYTEOFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006473 // "go": goto byte count from start of buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00006474 case 'o':
6475 goto_byte(cap->count0);
6476 break;
6477#endif
6478
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006479 // "gQ": improved Ex mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00006480 case 'Q':
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00006481 if (text_locked())
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482 {
6483 clearopbeep(cap->oap);
Bram Moolenaar2d3f4892006-01-20 23:02:51 +00006484 text_locked_msg();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006485 break;
6486 }
Bram Moolenaar05a7bb32006-01-19 22:09:32 +00006487
Bram Moolenaar071d4272004-06-13 20:20:40 +00006488 if (!checkclearopq(oap))
6489 do_exmode(TRUE);
6490 break;
6491
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492 case ',':
6493 nv_pcmark(cap);
6494 break;
6495
6496 case ';':
6497 cap->count1 = -cap->count1;
6498 nv_pcmark(cap);
6499 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006500
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006501 case 't':
Bram Moolenaar89f940f2012-06-29 13:56:06 +02006502 if (!checkclearop(oap))
6503 goto_tabpage((int)cap->count0);
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006504 break;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00006505 case 'T':
Bram Moolenaar89f940f2012-06-29 13:56:06 +02006506 if (!checkclearop(oap))
6507 goto_tabpage(-(int)cap->count1);
Bram Moolenaar80a94a52006-02-23 21:26:58 +00006508 break;
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +00006509
Bram Moolenaar62a23252020-08-09 14:04:42 +02006510 case TAB:
6511 if (!checkclearop(oap) && goto_tabpage_lastused() == FAIL)
6512 clearopbeep(oap);
6513 break;
6514
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006515 case '+':
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006516 case '-': // "g+" and "g-": undo or redo along the timeline
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006517 if (!checkclearopq(oap))
Bram Moolenaard3667a22006-03-16 21:35:52 +00006518 undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1,
Bram Moolenaar730cde92010-06-27 05:18:54 +02006519 FALSE, FALSE, FALSE);
Bram Moolenaar35a2e192006-03-13 22:07:11 +00006520 break;
6521
Bram Moolenaar071d4272004-06-13 20:20:40 +00006522 default:
6523 clearopbeep(oap);
6524 break;
6525 }
6526}
6527
6528/*
6529 * Handle "o" and "O" commands.
6530 */
6531 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006532n_opencmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006533{
Bram Moolenaar860cae12010-06-05 23:22:07 +02006534#ifdef FEAT_CONCEAL
6535 linenr_T oldline = curwin->w_cursor.lnum;
6536#endif
6537
Bram Moolenaar071d4272004-06-13 20:20:40 +00006538 if (!checkclearopq(cap->oap))
6539 {
6540#ifdef FEAT_FOLDING
6541 if (cap->cmdchar == 'O')
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006542 // Open above the first line of a folded sequence of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543 (void)hasFolding(curwin->w_cursor.lnum,
6544 &curwin->w_cursor.lnum, NULL);
6545 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006546 // Open below the last line of a folded sequence of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00006547 (void)hasFolding(curwin->w_cursor.lnum,
6548 NULL, &curwin->w_cursor.lnum);
6549#endif
6550 if (u_save((linenr_T)(curwin->w_cursor.lnum -
6551 (cap->cmdchar == 'O' ? 1 : 0)),
6552 (linenr_T)(curwin->w_cursor.lnum +
6553 (cap->cmdchar == 'o' ? 1 : 0))
6554 ) == OK
6555 && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
Bram Moolenaar8c96af92019-09-28 19:05:57 +02006556 has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0,
Bram Moolenaar6e371ec2021-12-12 14:16:39 +00006557 0, NULL) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006558 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006559#ifdef FEAT_CONCEAL
Bram Moolenaarf5963f72010-07-23 22:10:27 +02006560 if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum)
Bram Moolenaar535d5b62019-01-11 20:45:36 +01006561 redrawWinline(curwin, oldline);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006562#endif
Bram Moolenaard710e0d2015-06-10 12:16:47 +02006563#ifdef FEAT_SYN_HL
Bram Moolenaard0d0fe02015-06-09 19:23:46 +02006564 if (curwin->w_p_cul)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006565 // force redraw of cursorline
Bram Moolenaard0d0fe02015-06-09 19:23:46 +02006566 curwin->w_valid &= ~VALID_CROW;
Bram Moolenaard710e0d2015-06-10 12:16:47 +02006567#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006568 // When '#' is in 'cpoptions' ignore the count.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006569 if (vim_strchr(p_cpo, CPO_HASH) != NULL)
6570 cap->count1 = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571 invoke_edit(cap, FALSE, cap->cmdchar, TRUE);
6572 }
6573 }
6574}
6575
6576/*
6577 * "." command: redo last change.
6578 */
6579 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006580nv_dot(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006581{
6582 if (!checkclearopq(cap->oap))
6583 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00006584 // If "restart_edit" is TRUE, the last but one command is repeated
6585 // instead of the last command (inserting text). This is used for
6586 // CTRL-O <.> in insert mode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006587 if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == FAIL)
6588 clearopbeep(cap->oap);
6589 }
6590}
6591
6592/*
Shougo Matsushita4ede01f2022-01-20 15:26:03 +00006593 * CTRL-R: undo undo or specify register in select mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00006594 */
6595 static void
Shougo Matsushita4ede01f2022-01-20 15:26:03 +00006596nv_redo_or_register(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006597{
Shougo Matsushita4ede01f2022-01-20 15:26:03 +00006598 if (VIsual_select && VIsual_active)
6599 {
6600 int reg;
6601 // Get register name
6602 ++no_mapping;
6603 ++allow_keys;
6604 reg = plain_vgetc();
6605 LANGMAP_ADJUST(reg, TRUE);
6606 --no_mapping;
6607 --allow_keys;
6608
6609 if (reg == '"')
6610 // the unnamed register is 0
6611 reg = 0;
6612
6613 VIsual_select_reg = valid_yank_reg(reg, TRUE) ? reg : 0;
6614 return;
6615 }
6616
Bram Moolenaar071d4272004-06-13 20:20:40 +00006617 if (!checkclearopq(cap->oap))
6618 {
6619 u_redo((int)cap->count1);
6620 curwin->w_set_curswant = TRUE;
6621 }
6622}
6623
6624/*
6625 * Handle "U" command.
6626 */
6627 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006628nv_Undo(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006629{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006630 // In Visual mode and typing "gUU" triggers an operator
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006631 if (cap->oap->op_type == OP_UPPER || VIsual_active)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006632 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006633 // translate "gUU" to "gUgU"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006634 cap->cmdchar = 'g';
6635 cap->nchar = 'U';
6636 nv_operator(cap);
6637 }
6638 else if (!checkclearopq(cap->oap))
6639 {
6640 u_undoline();
6641 curwin->w_set_curswant = TRUE;
6642 }
6643}
6644
6645/*
6646 * '~' command: If tilde is not an operator and Visual is off: swap case of a
6647 * single character.
6648 */
6649 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006650nv_tilde(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651{
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01006652 if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE)
Bram Moolenaarf2732452018-06-03 14:47:35 +02006653 {
6654#ifdef FEAT_JOB_CHANNEL
6655 if (bt_prompt(curbuf) && !prompt_curpos_editable())
6656 {
6657 clearopbeep(cap->oap);
6658 return;
6659 }
6660#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006661 n_swapchar(cap);
Bram Moolenaarf2732452018-06-03 14:47:35 +02006662 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006663 else
6664 nv_operator(cap);
6665}
6666
6667/*
6668 * Handle an operator command.
6669 * The actual work is done by do_pending_operator().
6670 */
6671 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006672nv_operator(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006673{
6674 int op_type;
6675
6676 op_type = get_op_type(cap->cmdchar, cap->nchar);
Bram Moolenaarf2732452018-06-03 14:47:35 +02006677#ifdef FEAT_JOB_CHANNEL
6678 if (bt_prompt(curbuf) && op_is_change(op_type) && !prompt_curpos_editable())
6679 {
6680 clearopbeep(cap->oap);
6681 return;
6682 }
6683#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006684
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006685 if (op_type == cap->oap->op_type) // double operator works on lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00006686 nv_lineop(cap);
6687 else if (!checkclearop(cap->oap))
6688 {
6689 cap->oap->start = curwin->w_cursor;
6690 cap->oap->op_type = op_type;
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006691#ifdef FEAT_EVAL
6692 set_op_var(op_type);
6693#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006694 }
6695}
6696
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006697#ifdef FEAT_EVAL
6698/*
6699 * Set v:operator to the characters for "optype".
6700 */
6701 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006702set_op_var(int optype)
Bram Moolenaar8af1fbf2008-01-05 12:35:21 +00006703{
6704 char_u opchars[3];
6705
6706 if (optype == OP_NOP)
6707 set_vim_var_string(VV_OP, NULL, 0);
6708 else
6709 {
6710 opchars[0] = get_op_char(optype);
6711 opchars[1] = get_extra_op_char(optype);
6712 opchars[2] = NUL;
6713 set_vim_var_string(VV_OP, opchars, -1);
6714 }
6715}
6716#endif
6717
Bram Moolenaar071d4272004-06-13 20:20:40 +00006718/*
6719 * Handle linewise operator "dd", "yy", etc.
6720 *
6721 * "_" is is a strange motion command that helps make operators more logical.
6722 * It is actually implemented, but not documented in the real Vi. This motion
6723 * command actually refers to "the current line". Commands like "dd" and "yy"
6724 * are really an alternate form of "d_" and "y_". It does accept a count, so
6725 * "d3_" works to delete 3 lines.
6726 */
6727 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006728nv_lineop(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006729{
6730 cap->oap->motion_type = MLINE;
6731 if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == FAIL)
6732 clearopbeep(cap->oap);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006733 else if ( (cap->oap->op_type == OP_DELETE // only with linewise motions
Bram Moolenaar83dadaf2012-12-12 17:33:32 +01006734 && cap->oap->motion_force != 'v'
6735 && cap->oap->motion_force != Ctrl_V)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006736 || cap->oap->op_type == OP_LSHIFT
6737 || cap->oap->op_type == OP_RSHIFT)
6738 beginline(BL_SOL | BL_FIX);
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006739 else if (cap->oap->op_type != OP_YANK) // 'Y' does not move cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00006740 beginline(BL_WHITE | BL_FIX);
6741}
6742
6743/*
6744 * <Home> command.
6745 */
6746 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006747nv_home(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006748{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006749 // CTRL-HOME is like "gg"
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00006750 if (mod_mask & MOD_MASK_CTRL)
6751 nv_goto(cap);
6752 else
6753 {
6754 cap->count0 = 1;
6755 nv_pipe(cap);
6756 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006757 ins_at_eol = FALSE; // Don't move cursor past eol (only necessary in a
6758 // one-character line).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006759}
6760
6761/*
6762 * "|" command.
6763 */
6764 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006765nv_pipe(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006766{
6767 cap->oap->motion_type = MCHAR;
6768 cap->oap->inclusive = FALSE;
6769 beginline(0);
6770 if (cap->count0 > 0)
6771 {
6772 coladvance((colnr_T)(cap->count0 - 1));
6773 curwin->w_curswant = (colnr_T)(cap->count0 - 1);
6774 }
6775 else
6776 curwin->w_curswant = 0;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006777 // keep curswant at the column where we wanted to go, not where
6778 // we ended; differs if line is too short
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779 curwin->w_set_curswant = FALSE;
6780}
6781
6782/*
6783 * Handle back-word command "b" and "B".
6784 * cap->arg is 1 for "B"
6785 */
6786 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006787nv_bck_word(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006788{
6789 cap->oap->motion_type = MCHAR;
6790 cap->oap->inclusive = FALSE;
6791 curwin->w_set_curswant = TRUE;
6792 if (bck_word(cap->count1, cap->arg, FALSE) == FAIL)
6793 clearopbeep(cap->oap);
6794#ifdef FEAT_FOLDING
6795 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6796 foldOpenCursor();
6797#endif
6798}
6799
6800/*
6801 * Handle word motion commands "e", "E", "w" and "W".
6802 * cap->arg is TRUE for "E" and "W".
6803 */
6804 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006805nv_wordcmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006806{
6807 int n;
6808 int word_end;
6809 int flag = FALSE;
Bram Moolenaardfefb982008-04-01 10:06:39 +00006810 pos_T startpos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006811
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00006812 // Set inclusive for the "E" and "e" command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006813 if (cap->cmdchar == 'e' || cap->cmdchar == 'E')
6814 word_end = TRUE;
6815 else
6816 word_end = FALSE;
6817 cap->oap->inclusive = word_end;
6818
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00006819 // "cw" and "cW" are a special case.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006820 if (!word_end && cap->oap->op_type == OP_CHANGE)
6821 {
6822 n = gchar_cursor();
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006823 if (n != NUL) // not an empty line
Bram Moolenaar071d4272004-06-13 20:20:40 +00006824 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006825 if (VIM_ISWHITE(n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006826 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00006827 // Reproduce a funny Vi behaviour: "cw" on a blank only
6828 // changes one character, not all blanks until the start of
6829 // the next word. Only do this when the 'w' flag is included
6830 // in 'cpoptions'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006831 if (cap->count1 == 1 && vim_strchr(p_cpo, CPO_CW) != NULL)
6832 {
6833 cap->oap->inclusive = TRUE;
6834 cap->oap->motion_type = MCHAR;
6835 return;
6836 }
6837 }
6838 else
6839 {
Yegappan Lakshmanana827bf32022-01-26 12:14:15 +00006840 // This is a little strange. To match what the real Vi does,
6841 // we effectively map 'cw' to 'ce', and 'cW' to 'cE', provided
6842 // that we are not on a space or a TAB. This seems impolite
6843 // at first, but it's really more what we mean when we say
6844 // 'cw'.
6845 // Another strangeness: When standing on the end of a word
6846 // "ce" will change until the end of the next word, but "cw"
6847 // will change only one character! This is done by setting
6848 // flag.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006849 cap->oap->inclusive = TRUE;
6850 word_end = TRUE;
6851 flag = TRUE;
6852 }
6853 }
6854 }
6855
6856 cap->oap->motion_type = MCHAR;
6857 curwin->w_set_curswant = TRUE;
6858 if (word_end)
6859 n = end_word(cap->count1, cap->arg, flag, FALSE);
6860 else
6861 n = fwd_word(cap->count1, cap->arg, cap->oap->op_type != OP_NOP);
6862
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006863 // Don't leave the cursor on the NUL past the end of line. Unless we
6864 // didn't move it forward.
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006865 if (LT_POS(startpos, curwin->w_cursor))
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006866 adjust_cursor(cap->oap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006867
6868 if (n == FAIL && cap->oap->op_type == OP_NOP)
6869 clearopbeep(cap->oap);
6870 else
6871 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006872 adjust_for_sel(cap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873#ifdef FEAT_FOLDING
6874 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6875 foldOpenCursor();
6876#endif
6877 }
6878}
6879
6880/*
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006881 * Used after a movement command: If the cursor ends up on the NUL after the
6882 * end of the line, may move it back to the last character and make the motion
6883 * inclusive.
6884 */
6885 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006886adjust_cursor(oparg_T *oap)
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006887{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006888 // The cursor cannot remain on the NUL when:
6889 // - the column is > 0
6890 // - not in Visual mode or 'selection' is "o"
6891 // - 'virtualedit' is not "all" and not "onemore".
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006892 if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006893 && (!VIsual_active || *p_sel == 'o')
Gary Johnson53ba05b2021-07-26 22:19:10 +02006894 && !virtual_active() && (get_ve_flags() & VE_ONEMORE) == 0)
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006895 {
6896 --curwin->w_cursor.col;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006897 // prevent cursor from moving on the trail byte
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006898 if (has_mbyte)
6899 mb_adjust_cursor();
Bram Moolenaar1f14d572008-01-12 16:12:10 +00006900 oap->inclusive = TRUE;
6901 }
6902}
6903
6904/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006905 * "0" and "^" commands.
6906 * cap->arg is the argument for beginline().
6907 */
6908 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006909nv_beginline(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006910{
6911 cap->oap->motion_type = MCHAR;
6912 cap->oap->inclusive = FALSE;
6913 beginline(cap->arg);
6914#ifdef FEAT_FOLDING
6915 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
6916 foldOpenCursor();
6917#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006918 ins_at_eol = FALSE; // Don't move cursor past eol (only necessary in a
6919 // one-character line).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006920}
6921
Bram Moolenaar071d4272004-06-13 20:20:40 +00006922/*
6923 * In exclusive Visual mode, may include the last character.
6924 */
6925 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006926adjust_for_sel(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006927{
6928 if (VIsual_active && cap->oap->inclusive && *p_sel == 'e'
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006929 && gchar_cursor() != NUL && LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006930 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006931 if (has_mbyte)
6932 inc_cursor();
6933 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006934 ++curwin->w_cursor.col;
6935 cap->oap->inclusive = FALSE;
6936 }
6937}
6938
6939/*
6940 * Exclude last character at end of Visual area for 'selection' == "exclusive".
6941 * Should check VIsual_mode before calling this.
6942 * Returns TRUE when backed up to the previous line.
6943 */
Bram Moolenaar792cf5e2019-09-30 23:12:16 +02006944 int
Bram Moolenaar9b578142016-01-30 19:39:49 +01006945unadjust_for_sel(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006946{
6947 pos_T *pp;
6948
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006949 if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006950 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01006951 if (LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006952 pp = &curwin->w_cursor;
6953 else
6954 pp = &VIsual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006955 if (pp->coladd > 0)
6956 --pp->coladd;
6957 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006958 if (pp->col > 0)
6959 {
6960 --pp->col;
Bram Moolenaar03a807a2011-07-07 15:08:58 +02006961 mb_adjustpos(curbuf, pp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006962 }
6963 else if (pp->lnum > 1)
6964 {
6965 --pp->lnum;
6966 pp->col = (colnr_T)STRLEN(ml_get(pp->lnum));
6967 return TRUE;
6968 }
6969 }
6970 return FALSE;
6971}
6972
6973/*
6974 * SELECT key in Normal or Visual mode: end of Select mode mapping.
6975 */
6976 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006977nv_select(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006978{
6979 if (VIsual_active)
Shougo Matsushita4ede01f2022-01-20 15:26:03 +00006980 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006981 VIsual_select = TRUE;
Shougo Matsushita4ede01f2022-01-20 15:26:03 +00006982 VIsual_select_reg = 0;
6983 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006984 else if (VIsual_reselect)
6985 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01006986 cap->nchar = 'v'; // fake "gv" command
Bram Moolenaar071d4272004-06-13 20:20:40 +00006987 cap->arg = TRUE;
6988 nv_g_cmd(cap);
6989 }
6990}
6991
Bram Moolenaar071d4272004-06-13 20:20:40 +00006992
6993/*
6994 * "G", "gg", CTRL-END, CTRL-HOME.
6995 * cap->arg is TRUE for "G".
6996 */
6997 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01006998nv_goto(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006999{
7000 linenr_T lnum;
7001
7002 if (cap->arg)
7003 lnum = curbuf->b_ml.ml_line_count;
7004 else
7005 lnum = 1L;
7006 cap->oap->motion_type = MLINE;
7007 setpcmark();
7008
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007009 // When a count is given, use it instead of the default lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00007010 if (cap->count0 != 0)
7011 lnum = cap->count0;
7012 if (lnum < 1L)
7013 lnum = 1L;
7014 else if (lnum > curbuf->b_ml.ml_line_count)
7015 lnum = curbuf->b_ml.ml_line_count;
7016 curwin->w_cursor.lnum = lnum;
7017 beginline(BL_SOL | BL_FIX);
7018#ifdef FEAT_FOLDING
7019 if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP)
7020 foldOpenCursor();
7021#endif
7022}
7023
7024/*
7025 * CTRL-\ in Normal mode.
7026 */
7027 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007028nv_normal(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007029{
7030 if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G)
7031 {
7032 clearop(cap->oap);
Bram Moolenaar28c258f2006-01-25 22:02:51 +00007033 if (restart_edit != 0 && mode_displayed)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007034 clear_cmdline = TRUE; // unshow mode later
Bram Moolenaar071d4272004-06-13 20:20:40 +00007035 restart_edit = 0;
7036#ifdef FEAT_CMDWIN
7037 if (cmdwin_type != 0)
7038 cmdwin_result = Ctrl_C;
7039#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007040 if (VIsual_active)
7041 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007042 end_visual_mode(); // stop Visual
Bram Moolenaar071d4272004-06-13 20:20:40 +00007043 redraw_curbuf_later(INVERTED);
7044 }
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007045 // CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007046 if (cap->nchar == Ctrl_G && p_im)
7047 restart_edit = 'a';
7048 }
7049 else
7050 clearopbeep(cap->oap);
7051}
7052
7053/*
7054 * ESC in Normal mode: beep, but don't flush buffers.
7055 * Don't even beep if we are canceling a command.
7056 */
7057 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007058nv_esc(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007059{
7060 int no_reason;
7061
7062 no_reason = (cap->oap->op_type == OP_NOP
7063 && cap->opcount == 0
7064 && cap->count0 == 0
7065 && cap->oap->regname == 0
7066 && !p_im);
7067
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007068 if (cap->arg) // TRUE for CTRL-C
Bram Moolenaar071d4272004-06-13 20:20:40 +00007069 {
7070 if (restart_edit == 0
7071#ifdef FEAT_CMDWIN
7072 && cmdwin_type == 0
7073#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007074 && !VIsual_active
Bram Moolenaar071d4272004-06-13 20:20:40 +00007075 && no_reason)
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01007076 {
7077 if (anyBufIsChanged())
7078 msg(_("Type :qa! and press <Enter> to abandon all changes and exit Vim"));
7079 else
7080 msg(_("Type :qa and press <Enter> to exit Vim"));
7081 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007082
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007083 // Don't reset "restart_edit" when 'insertmode' is set, it won't be
7084 // set again below when halfway a mapping.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007085 if (!p_im)
7086 restart_edit = 0;
7087#ifdef FEAT_CMDWIN
7088 if (cmdwin_type != 0)
7089 {
7090 cmdwin_result = K_IGNORE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007091 got_int = FALSE; // don't stop executing autocommands et al.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007092 return;
7093 }
7094#endif
7095 }
Bram Moolenaar7d414102021-02-23 19:39:20 +01007096#ifdef FEAT_CMDWIN
7097 else if (cmdwin_type != 0 && ex_normal_busy)
7098 {
7099 // When :normal runs out of characters while in the command line window
7100 // vgetorpeek() will return ESC. Exit the cmdline window to break the
7101 // loop.
7102 cmdwin_result = K_IGNORE;
7103 return;
7104 }
7105#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007106
Bram Moolenaar071d4272004-06-13 20:20:40 +00007107 if (VIsual_active)
7108 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007109 end_visual_mode(); // stop Visual
7110 check_cursor_col(); // make sure cursor is not beyond EOL
Bram Moolenaar071d4272004-06-13 20:20:40 +00007111 curwin->w_set_curswant = TRUE;
7112 redraw_curbuf_later(INVERTED);
7113 }
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007114 else if (no_reason)
Bram Moolenaar165bc692015-07-21 17:53:25 +02007115 vim_beep(BO_ESC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007116 clearop(cap->oap);
7117
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007118 // A CTRL-C is often used at the start of a menu. When 'insertmode' is
7119 // set return to Insert mode afterwards.
Bram Moolenaare2c38102016-01-31 14:55:40 +01007120 if (restart_edit == 0 && goto_im() && ex_normal_busy == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007121 restart_edit = 'a';
7122}
7123
7124/*
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02007125 * Move the cursor for the "A" command.
7126 */
7127 void
7128set_cursor_for_append_to_line(void)
7129{
7130 curwin->w_set_curswant = TRUE;
Gary Johnson53ba05b2021-07-26 22:19:10 +02007131 if (get_ve_flags() == VE_ALL)
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02007132 {
7133 int save_State = State;
7134
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007135 // Pretend Insert mode here to allow the cursor on the
7136 // character past the end of the line
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02007137 State = INSERT;
7138 coladvance((colnr_T)MAXCOL);
7139 State = save_State;
7140 }
7141 else
7142 curwin->w_cursor.col += (colnr_T)STRLEN(ml_get_cursor());
7143}
7144
7145/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007146 * Handle "A", "a", "I", "i" and <Insert> commands.
Bram Moolenaarec2da362017-01-21 20:04:22 +01007147 * Also handle K_PS, start bracketed paste.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007148 */
7149 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007150nv_edit(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007151{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007152 // <Insert> is equal to "i"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007153 if (cap->cmdchar == K_INS || cap->cmdchar == K_KINS)
7154 cap->cmdchar = 'i';
7155
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007156 // in Visual mode "A" and "I" are an operator
Bram Moolenaar071d4272004-06-13 20:20:40 +00007157 if (VIsual_active && (cap->cmdchar == 'A' || cap->cmdchar == 'I'))
Bram Moolenaareef9add2017-09-16 15:38:04 +02007158 {
7159#ifdef FEAT_TERMINAL
7160 if (term_in_normal_mode())
7161 {
7162 end_visual_mode();
7163 clearop(cap->oap);
7164 term_enter_job_mode();
7165 return;
7166 }
7167#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007168 v_visop(cap);
Bram Moolenaareef9add2017-09-16 15:38:04 +02007169 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007170
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007171 // in Visual mode and after an operator "a" and "i" are for text objects
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007172 else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
7173 && (cap->oap->op_type != OP_NOP || VIsual_active))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007174 {
7175#ifdef FEAT_TEXTOBJ
7176 nv_object(cap);
7177#else
7178 clearopbeep(cap->oap);
7179#endif
7180 }
Bram Moolenaar662d9382017-07-31 22:56:24 +02007181#ifdef FEAT_TERMINAL
Bram Moolenaar6d819742017-08-06 14:57:49 +02007182 else if (term_in_normal_mode())
Bram Moolenaar662d9382017-07-31 22:56:24 +02007183 {
7184 clearop(cap->oap);
Bram Moolenaar6d819742017-08-06 14:57:49 +02007185 term_enter_job_mode();
Bram Moolenaar662d9382017-07-31 22:56:24 +02007186 return;
7187 }
7188#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007189 else if (!curbuf->b_p_ma && !p_im)
7190 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007191 // Only give this error when 'insertmode' is off.
Bram Moolenaar108010a2021-06-27 22:03:33 +02007192 emsg(_(e_cannot_make_changes_modifiable_is_off));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007193 clearop(cap->oap);
Bram Moolenaarec2da362017-01-21 20:04:22 +01007194 if (cap->cmdchar == K_PS)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007195 // drop the pasted text
Bram Moolenaarec2da362017-01-21 20:04:22 +01007196 bracketed_paste(PASTE_INSERT, TRUE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007197 }
Bram Moolenaara1891842017-02-04 21:34:31 +01007198 else if (cap->cmdchar == K_PS && VIsual_active)
7199 {
7200 pos_T old_pos = curwin->w_cursor;
7201 pos_T old_visual = VIsual;
7202
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007203 // In Visual mode the selected text is deleted.
Bram Moolenaara1891842017-02-04 21:34:31 +01007204 if (VIsual_mode == 'V' || curwin->w_cursor.lnum != VIsual.lnum)
7205 {
7206 shift_delete_registers();
7207 cap->oap->regname = '1';
7208 }
7209 else
7210 cap->oap->regname = '-';
7211 cap->cmdchar = 'd';
7212 cap->nchar = NUL;
7213 nv_operator(cap);
7214 do_pending_operator(cap, 0, FALSE);
7215 cap->cmdchar = K_PS;
7216
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007217 // When the last char in the line was deleted then append. Detect this
7218 // by checking if the cursor moved to before the Visual area.
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01007219 if (*ml_get_cursor() != NUL && LT_POS(curwin->w_cursor, old_pos)
7220 && LT_POS(curwin->w_cursor, old_visual))
Bram Moolenaara1891842017-02-04 21:34:31 +01007221 inc_cursor();
7222
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007223 // Insert to replace the deleted text with the pasted text.
Bram Moolenaara1891842017-02-04 21:34:31 +01007224 invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
7225 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007226 else if (!checkclearopq(cap->oap))
7227 {
7228 switch (cap->cmdchar)
7229 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007230 case 'A': // "A"ppend after the line
Bram Moolenaar8d3b5102019-09-05 21:29:01 +02007231 set_cursor_for_append_to_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007232 break;
7233
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007234 case 'I': // "I"nsert before the first non-blank
Bram Moolenaar4399ef42005-02-12 14:29:27 +00007235 if (vim_strchr(p_cpo, CPO_INSEND) == NULL)
7236 beginline(BL_WHITE);
7237 else
7238 beginline(BL_WHITE|BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007239 break;
7240
Bram Moolenaara1891842017-02-04 21:34:31 +01007241 case K_PS:
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007242 // Bracketed paste works like "a"ppend, unless the cursor is in
7243 // the first column, then it inserts.
Bram Moolenaarfd8983b2017-02-02 22:21:29 +01007244 if (curwin->w_cursor.col == 0)
7245 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007246 // FALLTHROUGH
Bram Moolenaarfd8983b2017-02-02 22:21:29 +01007247
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007248 case 'a': // "a"ppend is like "i"nsert on the next character.
7249 // increment coladd when in virtual space, increment the
7250 // column otherwise, also to append after an unprintable char
Bram Moolenaar071d4272004-06-13 20:20:40 +00007251 if (virtual_active()
7252 && (curwin->w_cursor.coladd > 0
7253 || *ml_get_cursor() == NUL
7254 || *ml_get_cursor() == TAB))
7255 curwin->w_cursor.coladd++;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01007256 else if (*ml_get_cursor() != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007257 inc_cursor();
7258 break;
7259 }
7260
Bram Moolenaar071d4272004-06-13 20:20:40 +00007261 if (curwin->w_cursor.coladd && cap->cmdchar != 'A')
7262 {
7263 int save_State = State;
7264
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007265 // Pretend Insert mode here to allow the cursor on the
7266 // character past the end of the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00007267 State = INSERT;
7268 coladvance(getviscol());
7269 State = save_State;
7270 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007271
7272 invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
7273 }
Bram Moolenaarec2da362017-01-21 20:04:22 +01007274 else if (cap->cmdchar == K_PS)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007275 // drop the pasted text
Bram Moolenaarec2da362017-01-21 20:04:22 +01007276 bracketed_paste(PASTE_INSERT, TRUE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007277}
7278
7279/*
7280 * Invoke edit() and take care of "restart_edit" and the return value.
7281 */
7282 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007283invoke_edit(
7284 cmdarg_T *cap,
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007285 int repl, // "r" or "gr" command
Bram Moolenaar9b578142016-01-30 19:39:49 +01007286 int cmd,
7287 int startln)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007288{
7289 int restart_edit_save = 0;
7290
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007291 // Complicated: When the user types "a<C-O>a" we don't want to do Insert
7292 // mode recursively. But when doing "a<C-O>." or "a<C-O>rx" we do allow
7293 // it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007294 if (repl || !stuff_empty())
7295 restart_edit_save = restart_edit;
7296 else
7297 restart_edit_save = 0;
7298
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007299 // Always reset "restart_edit", this is not a restarted edit.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007300 restart_edit = 0;
7301
7302 if (edit(cmd, startln, cap->count1))
7303 cap->retval |= CA_COMMAND_BUSY;
7304
7305 if (restart_edit == 0)
7306 restart_edit = restart_edit_save;
7307}
7308
7309#ifdef FEAT_TEXTOBJ
7310/*
7311 * "a" or "i" while an operator is pending or in Visual mode: object motion.
7312 */
7313 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007314nv_object(
7315 cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007316{
7317 int flag;
7318 int include;
7319 char_u *mps_save;
7320
7321 if (cap->cmdchar == 'i')
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007322 include = FALSE; // "ix" = inner object: exclude white space
Bram Moolenaar071d4272004-06-13 20:20:40 +00007323 else
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007324 include = TRUE; // "ax" = an object: include white space
Bram Moolenaar071d4272004-06-13 20:20:40 +00007325
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007326 // Make sure (), [], {} and <> are in 'matchpairs'
Bram Moolenaar071d4272004-06-13 20:20:40 +00007327 mps_save = curbuf->b_p_mps;
7328 curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>";
7329
7330 switch (cap->nchar)
7331 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007332 case 'w': // "aw" = a word
Bram Moolenaar071d4272004-06-13 20:20:40 +00007333 flag = current_word(cap->oap, cap->count1, include, FALSE);
7334 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007335 case 'W': // "aW" = a WORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007336 flag = current_word(cap->oap, cap->count1, include, TRUE);
7337 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007338 case 'b': // "ab" = a braces block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007339 case '(':
7340 case ')':
7341 flag = current_block(cap->oap, cap->count1, include, '(', ')');
7342 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007343 case 'B': // "aB" = a Brackets block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007344 case '{':
7345 case '}':
7346 flag = current_block(cap->oap, cap->count1, include, '{', '}');
7347 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007348 case '[': // "a[" = a [] block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007349 case ']':
7350 flag = current_block(cap->oap, cap->count1, include, '[', ']');
7351 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007352 case '<': // "a<" = a <> block
Bram Moolenaar071d4272004-06-13 20:20:40 +00007353 case '>':
7354 flag = current_block(cap->oap, cap->count1, include, '<', '>');
7355 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007356 case 't': // "at" = a tag block (xml and html)
7357 // Do not adjust oap->end in do_pending_operator()
7358 // otherwise there are different results for 'dit'
7359 // (note leading whitespace in last line):
7360 // 1) <b> 2) <b>
7361 // foobar foobar
7362 // </b> </b>
Bram Moolenaarb6c27352015-03-05 19:57:49 +01007363 cap->retval |= CA_NO_ADJ_OP_END;
Bram Moolenaarf8c07b22005-07-19 22:10:03 +00007364 flag = current_tagblock(cap->oap, cap->count1, include);
7365 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007366 case 'p': // "ap" = a paragraph
Bram Moolenaar071d4272004-06-13 20:20:40 +00007367 flag = current_par(cap->oap, cap->count1, include, 'p');
7368 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007369 case 's': // "as" = a sentence
Bram Moolenaar071d4272004-06-13 20:20:40 +00007370 flag = current_sent(cap->oap, cap->count1, include);
7371 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007372 case '"': // "a"" = a double quoted string
7373 case '\'': // "a'" = a single quoted string
7374 case '`': // "a`" = a backtick quoted string
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007375 flag = current_quote(cap->oap, cap->count1, include,
7376 cap->nchar);
7377 break;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007378#if 0 // TODO
7379 case 'S': // "aS" = a section
7380 case 'f': // "af" = a filename
7381 case 'u': // "au" = a URL
Bram Moolenaar071d4272004-06-13 20:20:40 +00007382#endif
7383 default:
7384 flag = FAIL;
7385 break;
7386 }
7387
7388 curbuf->b_p_mps = mps_save;
7389 if (flag == FAIL)
7390 clearopbeep(cap->oap);
7391 adjust_cursor_col();
7392 curwin->w_set_curswant = TRUE;
7393}
7394#endif
7395
7396/*
7397 * "q" command: Start/stop recording.
7398 * "q:", "q/", "q?": edit command-line in command-line window.
7399 */
7400 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007401nv_record(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007402{
7403 if (cap->oap->op_type == OP_FORMAT)
7404 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007405 // "gqq" is the same as "gqgq": format line
Bram Moolenaar071d4272004-06-13 20:20:40 +00007406 cap->cmdchar = 'g';
7407 cap->nchar = 'q';
7408 nv_operator(cap);
7409 }
7410 else if (!checkclearop(cap->oap))
7411 {
7412#ifdef FEAT_CMDWIN
7413 if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?')
7414 {
7415 stuffcharReadbuff(cap->nchar);
7416 stuffcharReadbuff(K_CMDWIN);
7417 }
7418 else
7419#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007420 // (stop) recording into a named register, unless executing a
7421 // register
Bram Moolenaar0b6d9112018-05-22 20:35:17 +02007422 if (reg_executing == 0 && do_record(cap->nchar) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007423 clearopbeep(cap->oap);
7424 }
7425}
7426
7427/*
7428 * Handle the "@r" command.
7429 */
7430 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007431nv_at(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007432{
7433 if (checkclearop(cap->oap))
7434 return;
7435#ifdef FEAT_EVAL
7436 if (cap->nchar == '=')
7437 {
7438 if (get_expr_register() == NUL)
7439 return;
7440 }
7441#endif
7442 while (cap->count1-- && !got_int)
7443 {
Bram Moolenaard333d1e2006-11-07 17:43:47 +00007444 if (do_execreg(cap->nchar, FALSE, FALSE, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007445 {
7446 clearopbeep(cap->oap);
7447 break;
7448 }
7449 line_breakcheck();
7450 }
7451}
7452
7453/*
7454 * Handle the CTRL-U and CTRL-D commands.
7455 */
7456 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007457nv_halfpage(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007458{
7459 if ((cap->cmdchar == Ctrl_U && curwin->w_cursor.lnum == 1)
7460 || (cap->cmdchar == Ctrl_D
7461 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count))
7462 clearopbeep(cap->oap);
7463 else if (!checkclearop(cap->oap))
7464 halfpage(cap->cmdchar == Ctrl_D, cap->count0);
7465}
7466
7467/*
7468 * Handle "J" or "gJ" command.
7469 */
7470 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007471nv_join(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007472{
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007473 if (VIsual_active) // join the visual lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00007474 nv_operator(cap);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007475 else if (!checkclearop(cap->oap))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007476 {
7477 if (cap->count0 <= 1)
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007478 cap->count0 = 2; // default for join is two lines!
Bram Moolenaar071d4272004-06-13 20:20:40 +00007479 if (curwin->w_cursor.lnum + cap->count0 - 1 >
7480 curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007481 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007482 // can't join when on the last line
Bram Moolenaar41e0f2f2016-03-08 14:44:42 +01007483 if (cap->count0 <= 2)
7484 {
7485 clearopbeep(cap->oap);
7486 return;
7487 }
7488 cap->count0 = curbuf->b_ml.ml_line_count
7489 - curwin->w_cursor.lnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007490 }
Bram Moolenaar41e0f2f2016-03-08 14:44:42 +01007491
7492 prep_redo(cap->oap->regname, cap->count0,
7493 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
7494 (void)do_join(cap->count0, cap->nchar == NUL, TRUE, TRUE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007495 }
7496}
7497
7498/*
7499 * "P", "gP", "p" and "gp" commands.
7500 */
7501 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007502nv_put(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007503{
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02007504 nv_put_opt(cap, FALSE);
7505}
7506
7507/*
7508 * "P", "gP", "p" and "gp" commands.
7509 * "fix_indent" is TRUE for "[p", "[P", "]p" and "]P".
7510 */
7511 static void
7512nv_put_opt(cmdarg_T *cap, int fix_indent)
7513{
Bram Moolenaar071d4272004-06-13 20:20:40 +00007514 int regname = 0;
7515 void *reg1 = NULL, *reg2 = NULL;
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007516 int empty = FALSE;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007517 int was_visual = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007518 int dir;
7519 int flags = 0;
Shougo Matsushitafb552072022-01-28 16:01:13 +00007520 int save_unnamed = FALSE;
7521 yankreg_T *old_y_current, *old_y_previous;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007522
7523 if (cap->oap->op_type != OP_NOP)
7524 {
7525#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007526 // "dp" is ":diffput"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007527 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'p')
7528 {
7529 clearop(cap->oap);
Bram Moolenaar6a643652014-10-31 13:54:25 +01007530 nv_diffgetput(TRUE, cap->opcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007531 }
7532 else
7533#endif
7534 clearopbeep(cap->oap);
7535 }
Bram Moolenaarf2732452018-06-03 14:47:35 +02007536#ifdef FEAT_JOB_CHANNEL
7537 else if (bt_prompt(curbuf) && !prompt_curpos_editable())
7538 {
7539 clearopbeep(cap->oap);
7540 }
7541#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007542 else
7543 {
Bram Moolenaar0ab190c2019-05-23 23:27:36 +02007544 if (fix_indent)
7545 {
7546 dir = (cap->cmdchar == ']' && cap->nchar == 'p')
7547 ? FORWARD : BACKWARD;
7548 flags |= PUT_FIXINDENT;
7549 }
7550 else
7551 dir = (cap->cmdchar == 'P'
Christian Brabandt2fa93842021-05-30 22:17:25 +02007552 || ((cap->cmdchar == 'g' || cap->cmdchar == 'z')
7553 && cap->nchar == 'P')) ? BACKWARD : FORWARD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007554 prep_redo_cmd(cap);
7555 if (cap->cmdchar == 'g')
7556 flags |= PUT_CURSEND;
Christian Brabandt2fa93842021-05-30 22:17:25 +02007557 else if (cap->cmdchar == 'z')
7558 flags |= PUT_BLOCK_INNER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007559
Bram Moolenaar071d4272004-06-13 20:20:40 +00007560 if (VIsual_active)
7561 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007562 // Putting in Visual mode: The put text replaces the selected
7563 // text. First delete the selected text, then put the new text.
7564 // Need to save and restore the registers that the delete
7565 // overwrites if the old contents is being put.
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007566 was_visual = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007567 regname = cap->oap->regname;
Shougo Matsushitafb552072022-01-28 16:01:13 +00007568 save_unnamed = cap->cmdchar == 'P';
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007569#ifdef FEAT_CLIPBOARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007570 adjust_clip_reg(&regname);
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007571#endif
Bram Moolenaar7d311c52014-02-22 23:49:35 +01007572 if (regname == 0 || regname == '"'
Bram Moolenaarba6e8582012-12-12 18:20:32 +01007573 || VIM_ISDIGIT(regname) || regname == '-'
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007574#ifdef FEAT_CLIPBOARD
Bram Moolenaar071d4272004-06-13 20:20:40 +00007575 || (clip_unnamed && (regname == '*' || regname == '+'))
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +01007576#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007577
7578 )
7579 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007580 // The delete is going to overwrite the register we want to
7581 // put, save it first.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007582 reg1 = get_register(regname, TRUE);
7583 }
7584
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007585 // Now delete the selected text. Avoid messages here.
Shougo Matsushitafb552072022-01-28 16:01:13 +00007586 if (save_unnamed)
7587 {
7588 old_y_current = get_y_current();
7589 old_y_previous = get_y_previous();
7590 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007591 cap->cmdchar = 'd';
7592 cap->nchar = NUL;
7593 cap->oap->regname = NUL;
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007594 ++msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007595 nv_operator(cap);
7596 do_pending_operator(cap, 0, FALSE);
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007597 empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007598 --msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007599
Shougo Matsushitafb552072022-01-28 16:01:13 +00007600 if (save_unnamed)
7601 {
7602 set_y_current(old_y_current);
7603 set_y_previous(old_y_previous);
7604 }
7605
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007606 // delete PUT_LINE_BACKWARD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007607 cap->oap->regname = regname;
7608
7609 if (reg1 != NULL)
7610 {
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007611 // Delete probably changed the register we want to put, save
7612 // it first. Then put back what was there before the delete.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007613 reg2 = get_register(regname, FALSE);
7614 put_register(regname, reg1);
7615 }
7616
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007617 // When deleted a linewise Visual area, put the register as
7618 // lines to avoid it joined with the next line. When deletion was
7619 // characterwise, split a line when putting lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007620 if (VIsual_mode == 'V')
7621 flags |= PUT_LINE;
7622 else if (VIsual_mode == 'v')
7623 flags |= PUT_LINE_SPLIT;
7624 if (VIsual_mode == Ctrl_V && dir == FORWARD)
7625 flags |= PUT_LINE_FORWARD;
7626 dir = BACKWARD;
7627 if ((VIsual_mode != 'V'
7628 && curwin->w_cursor.col < curbuf->b_op_start.col)
7629 || (VIsual_mode == 'V'
7630 && curwin->w_cursor.lnum < curbuf->b_op_start.lnum))
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007631 // cursor is at the end of the line or end of file, put
7632 // forward.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007633 dir = FORWARD;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007634 // May have been reset in do_put().
Bram Moolenaarec11aef2013-09-22 15:23:44 +02007635 VIsual_active = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007636 }
Bram Moolenaarc3516f72020-09-08 22:45:35 +02007637 do_put(cap->oap->regname, NULL, dir, cap->count1, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007638
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007639 // If a register was saved, put it back now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007640 if (reg2 != NULL)
7641 put_register(regname, reg2);
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007642
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007643 // What to reselect with "gv"? Selecting the just put text seems to
7644 // be the most useful, since the original text was removed.
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007645 if (was_visual)
7646 {
Bram Moolenaara226a6d2006-02-26 23:59:20 +00007647 curbuf->b_visual.vi_start = curbuf->b_op_start;
7648 curbuf->b_visual.vi_end = curbuf->b_op_end;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007649 // need to adjust cursor position
Bram Moolenaard29c6fe2015-11-19 20:11:54 +01007650 if (*p_sel == 'e')
7651 inc(&curbuf->b_visual.vi_end);
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00007652 }
7653
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007654 // When all lines were selected and deleted do_put() leaves an empty
7655 // line that needs to be deleted now.
Bram Moolenaarcf3630f2005-01-08 16:04:29 +00007656 if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL)
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007657 {
Bram Moolenaarca70c072020-05-30 20:30:46 +02007658 ml_delete_flags(curbuf->b_ml.ml_line_count, ML_DEL_MESSAGE);
Bram Moolenaar9a4a8c42019-08-19 22:48:30 +02007659 deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007660
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007661 // If the cursor was in that line, move it to the end of the last
7662 // line.
Bram Moolenaar86ca6e32006-03-29 21:06:37 +00007663 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
7664 {
7665 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
7666 coladvance((colnr_T)MAXCOL);
7667 }
7668 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007669 auto_format(FALSE, TRUE);
7670 }
7671}
7672
7673/*
7674 * "o" and "O" commands.
7675 */
7676 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007677nv_open(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007678{
7679#ifdef FEAT_DIFF
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007680 // "do" is ":diffget"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007681 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'o')
7682 {
7683 clearop(cap->oap);
Bram Moolenaar6a643652014-10-31 13:54:25 +01007684 nv_diffgetput(FALSE, cap->opcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007685 }
7686 else
7687#endif
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007688 if (VIsual_active) // switch start and end of visual
Bram Moolenaar071d4272004-06-13 20:20:40 +00007689 v_swap_corners(cap->cmdchar);
Bram Moolenaarf2732452018-06-03 14:47:35 +02007690#ifdef FEAT_JOB_CHANNEL
7691 else if (bt_prompt(curbuf))
Bram Moolenaarf2732452018-06-03 14:47:35 +02007692 clearopbeep(cap->oap);
Bram Moolenaarf2732452018-06-03 14:47:35 +02007693#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007694 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007695 n_opencmd(cap);
7696}
7697
Bram Moolenaar071d4272004-06-13 20:20:40 +00007698#ifdef FEAT_NETBEANS_INTG
7699 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007700nv_nbcmd(cmdarg_T *cap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007701{
7702 netbeans_keycommand(cap->nchar);
7703}
7704#endif
7705
7706#ifdef FEAT_DND
Bram Moolenaar071d4272004-06-13 20:20:40 +00007707 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007708nv_drop(cmdarg_T *cap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007709{
Bram Moolenaarc3516f72020-09-08 22:45:35 +02007710 do_put('~', NULL, BACKWARD, 1L, PUT_CURSEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007711}
7712#endif
Bram Moolenaar3918c952005-03-15 22:34:55 +00007713
Bram Moolenaar3918c952005-03-15 22:34:55 +00007714/*
7715 * Trigger CursorHold event.
7716 * When waiting for a character for 'updatetime' K_CURSORHOLD is put in the
7717 * input buffer. "did_cursorhold" is set to avoid retriggering.
7718 */
Bram Moolenaar3918c952005-03-15 22:34:55 +00007719 static void
Bram Moolenaar9b578142016-01-30 19:39:49 +01007720nv_cursorhold(cmdarg_T *cap)
Bram Moolenaar3918c952005-03-15 22:34:55 +00007721{
7722 apply_autocmds(EVENT_CURSORHOLD, NULL, NULL, FALSE, curbuf);
7723 did_cursorhold = TRUE;
Bram Moolenaar6e0ce172019-12-05 20:12:41 +01007724 cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
Bram Moolenaar3918c952005-03-15 22:34:55 +00007725}