1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4//
5// normal.c: Contains the main routine for processing characters in command
6// mode. Communicates closely with the code in ops.c to handle
7// the operators.
8//
9
10#include <assert.h>
11#include <inttypes.h>
12#include <string.h>
13#include <stdbool.h>
14#include <stdlib.h>
15
16#include "nvim/log.h"
17#include "nvim/vim.h"
18#include "nvim/ascii.h"
19#include "nvim/normal.h"
20#include "nvim/buffer.h"
21#include "nvim/change.h"
22#include "nvim/charset.h"
23#include "nvim/cursor.h"
24#include "nvim/diff.h"
25#include "nvim/digraph.h"
26#include "nvim/edit.h"
27#include "nvim/eval.h"
28#include "nvim/ex_cmds.h"
29#include "nvim/ex_cmds2.h"
30#include "nvim/ex_docmd.h"
31#include "nvim/ex_getln.h"
32#include "nvim/fileio.h"
33#include "nvim/fold.h"
34#include "nvim/getchar.h"
35#include "nvim/indent.h"
36#include "nvim/indent_c.h"
37#include "nvim/main.h"
38#include "nvim/mark.h"
39#include "nvim/memline.h"
40#include "nvim/memory.h"
41#include "nvim/message.h"
42#include "nvim/misc1.h"
43#include "nvim/keymap.h"
44#include "nvim/move.h"
45#include "nvim/mouse.h"
46#include "nvim/ops.h"
47#include "nvim/option.h"
48#include "nvim/quickfix.h"
49#include "nvim/screen.h"
50#include "nvim/search.h"
51#include "nvim/spell.h"
52#include "nvim/spellfile.h"
53#include "nvim/strings.h"
54#include "nvim/syntax.h"
55#include "nvim/tag.h"
56#include "nvim/ui.h"
57#include "nvim/mouse.h"
58#include "nvim/undo.h"
59#include "nvim/window.h"
60#include "nvim/state.h"
61#include "nvim/event/loop.h"
62#include "nvim/os/time.h"
63#include "nvim/os/input.h"
64#include "nvim/api/private/helpers.h"
65
66typedef struct normal_state {
67 VimState state;
68 bool command_finished;
69 bool ctrl_w;
70 bool need_flushbuf;
71 bool set_prevcount;
72 bool previous_got_int; // `got_int` was true
73 bool cmdwin; // command-line window normal mode
74 bool noexmode; // true if the normal mode was pushed from
75 // ex mode(:global or :visual for example)
76 bool toplevel; // top-level normal mode
77 oparg_T oa; // operator arguments
78 cmdarg_T ca; // command arguments
79 int mapped_len;
80 int old_mapped_len;
81 int idx;
82 int c;
83 int old_col;
84 pos_T old_pos;
85} NormalState;
86
87/*
88 * The Visual area is remembered for reselection.
89 */
90static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
91static linenr_T resel_VIsual_line_count; /* number of lines */
92static colnr_T resel_VIsual_vcol; /* nr of cols or end col */
93static int VIsual_mode_orig = NUL; /* saved Visual mode */
94
95static int restart_VIsual_select = 0;
96
97
98#ifdef INCLUDE_GENERATED_DECLARATIONS
99# include "normal.c.generated.h"
100#endif
101
102static inline void normal_state_init(NormalState *s)
103{
104 memset(s, 0, sizeof(NormalState));
105 s->state.check = normal_check;
106 s->state.execute = normal_execute;
107}
108
109/*
110 * nv_*(): functions called to handle Normal and Visual mode commands.
111 * n_*(): functions called to handle Normal mode commands.
112 * v_*(): functions called to handle Visual mode commands.
113 */
114
115static char *e_noident = N_("E349: No identifier under cursor");
116
117/*
118 * Function to be called for a Normal or Visual mode command.
119 * The argument is a cmdarg_T.
120 */
121typedef void (*nv_func_T)(cmdarg_T *cap);
122
123/* Values for cmd_flags. */
124#define NV_NCH 0x01 /* may need to get a second char */
125#define NV_NCH_NOP (0x02|NV_NCH) /* get second char when no operator pending */
126#define NV_NCH_ALW (0x04|NV_NCH) /* always get a second char */
127#define NV_LANG 0x08 /* second char needs language adjustment */
128
129#define NV_SS 0x10 /* may start selection */
130#define NV_SSS 0x20 /* may start selection with shift modifier */
131#define NV_STS 0x40 /* may stop selection without shift modif. */
132#define NV_RL 0x80 /* 'rightleft' modifies command */
133#define NV_KEEPREG 0x100 /* don't clear regname */
134#define NV_NCW 0x200 /* not allowed in command-line window */
135
136/*
137 * Generally speaking, every Normal mode command should either clear any
138 * pending operator (with *clearop*()), or set the motion type variable
139 * oap->motion_type.
140 *
141 * When a cursor motion command is made, it is marked as being a character or
142 * line oriented motion. Then, if an operator is in effect, the operation
143 * becomes character or line oriented accordingly.
144 */
145
146/*
147 * This table contains one entry for every Normal or Visual mode command.
148 * The order doesn't matter, init_normal_cmds() will create a sorted index.
149 * It is faster when all keys from zero to '~' are present.
150 */
151static const struct nv_cmd {
152 int cmd_char; /* (first) command character */
153 nv_func_T cmd_func; /* function for this command */
154 uint16_t cmd_flags; /* NV_ flags */
155 short cmd_arg; /* value for ca.arg */
156} nv_cmds[] =
157{
158 { NUL, nv_error, 0, 0 },
159 { Ctrl_A, nv_addsub, 0, 0 },
160 { Ctrl_B, nv_page, NV_STS, BACKWARD },
161 { Ctrl_C, nv_esc, 0, true },
162 { Ctrl_D, nv_halfpage, 0, 0 },
163 { Ctrl_E, nv_scroll_line, 0, true },
164 { Ctrl_F, nv_page, NV_STS, FORWARD },
165 { Ctrl_G, nv_ctrlg, 0, 0 },
166 { Ctrl_H, nv_ctrlh, 0, 0 },
167 { Ctrl_I, nv_pcmark, 0, 0 },
168 { NL, nv_down, 0, false },
169 { Ctrl_K, nv_error, 0, 0 },
170 { Ctrl_L, nv_clear, 0, 0 },
171 { Ctrl_M, nv_down, 0, true },
172 { Ctrl_N, nv_down, NV_STS, false },
173 { Ctrl_O, nv_ctrlo, 0, 0 },
174 { Ctrl_P, nv_up, NV_STS, false },
175 { Ctrl_Q, nv_visual, 0, false },
176 { Ctrl_R, nv_redo, 0, 0 },
177 { Ctrl_S, nv_ignore, 0, 0 },
178 { Ctrl_T, nv_tagpop, NV_NCW, 0 },
179 { Ctrl_U, nv_halfpage, 0, 0 },
180 { Ctrl_V, nv_visual, 0, false },
181 { 'V', nv_visual, 0, false },
182 { 'v', nv_visual, 0, false },
183 { Ctrl_W, nv_window, 0, 0 },
184 { Ctrl_X, nv_addsub, 0, 0 },
185 { Ctrl_Y, nv_scroll_line, 0, false },
186 { Ctrl_Z, nv_suspend, 0, 0 },
187 { ESC, nv_esc, 0, false },
188 { Ctrl_BSL, nv_normal, NV_NCH_ALW, 0 },
189 { Ctrl_RSB, nv_ident, NV_NCW, 0 },
190 { Ctrl_HAT, nv_hat, NV_NCW, 0 },
191 { Ctrl__, nv_error, 0, 0 },
192 { ' ', nv_right, 0, 0 },
193 { '!', nv_operator, 0, 0 },
194 { '"', nv_regname, NV_NCH_NOP|NV_KEEPREG, 0 },
195 { '#', nv_ident, 0, 0 },
196 { '$', nv_dollar, 0, 0 },
197 { '%', nv_percent, 0, 0 },
198 { '&', nv_optrans, 0, 0 },
199 { '\'', nv_gomark, NV_NCH_ALW, true },
200 { '(', nv_brace, 0, BACKWARD },
201 { ')', nv_brace, 0, FORWARD },
202 { '*', nv_ident, 0, 0 },
203 { '+', nv_down, 0, true },
204 { ',', nv_csearch, 0, true },
205 { '-', nv_up, 0, true },
206 { '.', nv_dot, NV_KEEPREG, 0 },
207 { '/', nv_search, 0, false },
208 { '0', nv_beginline, 0, 0 },
209 { '1', nv_ignore, 0, 0 },
210 { '2', nv_ignore, 0, 0 },
211 { '3', nv_ignore, 0, 0 },
212 { '4', nv_ignore, 0, 0 },
213 { '5', nv_ignore, 0, 0 },
214 { '6', nv_ignore, 0, 0 },
215 { '7', nv_ignore, 0, 0 },
216 { '8', nv_ignore, 0, 0 },
217 { '9', nv_ignore, 0, 0 },
218 { ':', nv_colon, 0, 0 },
219 { ';', nv_csearch, 0, false },
220 { '<', nv_operator, NV_RL, 0 },
221 { '=', nv_operator, 0, 0 },
222 { '>', nv_operator, NV_RL, 0 },
223 { '?', nv_search, 0, false },
224 { '@', nv_at, NV_NCH_NOP, false },
225 { 'A', nv_edit, 0, 0 },
226 { 'B', nv_bck_word, 0, 1 },
227 { 'C', nv_abbrev, NV_KEEPREG, 0 },
228 { 'D', nv_abbrev, NV_KEEPREG, 0 },
229 { 'E', nv_wordcmd, 0, true },
230 { 'F', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD },
231 { 'G', nv_goto, 0, true },
232 { 'H', nv_scroll, 0, 0 },
233 { 'I', nv_edit, 0, 0 },
234 { 'J', nv_join, 0, 0 },
235 { 'K', nv_ident, 0, 0 },
236 { 'L', nv_scroll, 0, 0 },
237 { 'M', nv_scroll, 0, 0 },
238 { 'N', nv_next, 0, SEARCH_REV },
239 { 'O', nv_open, 0, 0 },
240 { 'P', nv_put, 0, 0 },
241 { 'Q', nv_exmode, NV_NCW, 0 },
242 { 'R', nv_Replace, 0, false },
243 { 'S', nv_subst, NV_KEEPREG, 0 },
244 { 'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD },
245 { 'U', nv_Undo, 0, 0 },
246 { 'W', nv_wordcmd, 0, true },
247 { 'X', nv_abbrev, NV_KEEPREG, 0 },
248 { 'Y', nv_abbrev, NV_KEEPREG, 0 },
249 { 'Z', nv_Zet, NV_NCH_NOP|NV_NCW, 0 },
250 { '[', nv_brackets, NV_NCH_ALW, BACKWARD },
251 { '\\', nv_error, 0, 0 },
252 { ']', nv_brackets, NV_NCH_ALW, FORWARD },
253 { '^', nv_beginline, 0, BL_WHITE | BL_FIX },
254 { '_', nv_lineop, 0, 0 },
255 { '`', nv_gomark, NV_NCH_ALW, false },
256 { 'a', nv_edit, NV_NCH, 0 },
257 { 'b', nv_bck_word, 0, 0 },
258 { 'c', nv_operator, 0, 0 },
259 { 'd', nv_operator, 0, 0 },
260 { 'e', nv_wordcmd, 0, false },
261 { 'f', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD },
262 { 'g', nv_g_cmd, NV_NCH_ALW, false },
263 { 'h', nv_left, NV_RL, 0 },
264 { 'i', nv_edit, NV_NCH, 0 },
265 { 'j', nv_down, 0, false },
266 { 'k', nv_up, 0, false },
267 { 'l', nv_right, NV_RL, 0 },
268 { 'm', nv_mark, NV_NCH_NOP, 0 },
269 { 'n', nv_next, 0, 0 },
270 { 'o', nv_open, 0, 0 },
271 { 'p', nv_put, 0, 0 },
272 { 'q', nv_record, NV_NCH, 0 },
273 { 'r', nv_replace, NV_NCH_NOP|NV_LANG, 0 },
274 { 's', nv_subst, NV_KEEPREG, 0 },
275 { 't', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD },
276 { 'u', nv_undo, 0, 0 },
277 { 'w', nv_wordcmd, 0, false },
278 { 'x', nv_abbrev, NV_KEEPREG, 0 },
279 { 'y', nv_operator, 0, 0 },
280 { 'z', nv_zet, NV_NCH_ALW, 0 },
281 { '{', nv_findpar, 0, BACKWARD },
282 { '|', nv_pipe, 0, 0 },
283 { '}', nv_findpar, 0, FORWARD },
284 { '~', nv_tilde, 0, 0 },
285
286 // pound sign
287 { POUND, nv_ident, 0, 0 },
288 { K_MOUSEUP, nv_mousescroll, 0, MSCR_UP },
289 { K_MOUSEDOWN, nv_mousescroll, 0, MSCR_DOWN },
290 { K_MOUSELEFT, nv_mousescroll, 0, MSCR_LEFT },
291 { K_MOUSERIGHT, nv_mousescroll, 0, MSCR_RIGHT },
292 { K_LEFTMOUSE, nv_mouse, 0, 0 },
293 { K_LEFTMOUSE_NM, nv_mouse, 0, 0 },
294 { K_LEFTDRAG, nv_mouse, 0, 0 },
295 { K_LEFTRELEASE, nv_mouse, 0, 0 },
296 { K_LEFTRELEASE_NM, nv_mouse, 0, 0 },
297 { K_MIDDLEMOUSE, nv_mouse, 0, 0 },
298 { K_MIDDLEDRAG, nv_mouse, 0, 0 },
299 { K_MIDDLERELEASE, nv_mouse, 0, 0 },
300 { K_RIGHTMOUSE, nv_mouse, 0, 0 },
301 { K_RIGHTDRAG, nv_mouse, 0, 0 },
302 { K_RIGHTRELEASE, nv_mouse, 0, 0 },
303 { K_X1MOUSE, nv_mouse, 0, 0 },
304 { K_X1DRAG, nv_mouse, 0, 0 },
305 { K_X1RELEASE, nv_mouse, 0, 0 },
306 { K_X2MOUSE, nv_mouse, 0, 0 },
307 { K_X2DRAG, nv_mouse, 0, 0 },
308 { K_X2RELEASE, nv_mouse, 0, 0 },
309 { K_IGNORE, nv_ignore, NV_KEEPREG, 0 },
310 { K_NOP, nv_nop, 0, 0 },
311 { K_INS, nv_edit, 0, 0 },
312 { K_KINS, nv_edit, 0, 0 },
313 { K_BS, nv_ctrlh, 0, 0 },
314 { K_UP, nv_up, NV_SSS|NV_STS, false },
315 { K_S_UP, nv_page, NV_SS, BACKWARD },
316 { K_DOWN, nv_down, NV_SSS|NV_STS, false },
317 { K_S_DOWN, nv_page, NV_SS, FORWARD },
318 { K_LEFT, nv_left, NV_SSS|NV_STS|NV_RL, 0 },
319 { K_S_LEFT, nv_bck_word, NV_SS|NV_RL, 0 },
320 { K_C_LEFT, nv_bck_word, NV_SSS|NV_RL|NV_STS, 1 },
321 { K_RIGHT, nv_right, NV_SSS|NV_STS|NV_RL, 0 },
322 { K_S_RIGHT, nv_wordcmd, NV_SS|NV_RL, false },
323 { K_C_RIGHT, nv_wordcmd, NV_SSS|NV_RL|NV_STS, true },
324 { K_PAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD },
325 { K_KPAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD },
326 { K_PAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD },
327 { K_KPAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD },
328 { K_END, nv_end, NV_SSS|NV_STS, false },
329 { K_KEND, nv_end, NV_SSS|NV_STS, false },
330 { K_S_END, nv_end, NV_SS, false },
331 { K_C_END, nv_end, NV_SSS|NV_STS, true },
332 { K_HOME, nv_home, NV_SSS|NV_STS, 0 },
333 { K_KHOME, nv_home, NV_SSS|NV_STS, 0 },
334 { K_S_HOME, nv_home, NV_SS, 0 },
335 { K_C_HOME, nv_goto, NV_SSS|NV_STS, false },
336 { K_DEL, nv_abbrev, 0, 0 },
337 { K_KDEL, nv_abbrev, 0, 0 },
338 { K_UNDO, nv_kundo, 0, 0 },
339 { K_HELP, nv_help, NV_NCW, 0 },
340 { K_F1, nv_help, NV_NCW, 0 },
341 { K_XF1, nv_help, NV_NCW, 0 },
342 { K_SELECT, nv_select, 0, 0 },
343 { K_EVENT, nv_event, NV_KEEPREG, 0 },
344 { K_COMMAND, nv_colon, 0, 0 },
345};
346
347/* Number of commands in nv_cmds[]. */
348#define NV_CMDS_SIZE ARRAY_SIZE(nv_cmds)
349
350/* Sorted index of commands in nv_cmds[]. */
351static short nv_cmd_idx[NV_CMDS_SIZE];
352
353/* The highest index for which
354 * nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char] */
355static int nv_max_linear;
356
357/*
358 * Compare functions for qsort() below, that checks the command character
359 * through the index in nv_cmd_idx[].
360 */
361static int nv_compare(const void *s1, const void *s2)
362{
363 int c1, c2;
364
365 /* The commands are sorted on absolute value. */
366 c1 = nv_cmds[*(const short *)s1].cmd_char;
367 c2 = nv_cmds[*(const short *)s2].cmd_char;
368 if (c1 < 0)
369 c1 = -c1;
370 if (c2 < 0)
371 c2 = -c2;
372 return c1 - c2;
373}
374
375/*
376 * Initialize the nv_cmd_idx[] table.
377 */
378void init_normal_cmds(void)
379{
380 assert(NV_CMDS_SIZE <= SHRT_MAX);
381
382 /* Fill the index table with a one to one relation. */
383 for (short int i = 0; i < (short int)NV_CMDS_SIZE; ++i) {
384 nv_cmd_idx[i] = i;
385 }
386
387 /* Sort the commands by the command character. */
388 qsort(&nv_cmd_idx, NV_CMDS_SIZE, sizeof(short), nv_compare);
389
390 /* Find the first entry that can't be indexed by the command character. */
391 short int i;
392 for (i = 0; i < (short int)NV_CMDS_SIZE; ++i) {
393 if (i != nv_cmds[nv_cmd_idx[i]].cmd_char) {
394 break;
395 }
396 }
397 nv_max_linear = i - 1;
398}
399
400/*
401 * Search for a command in the commands table.
402 * Returns -1 for invalid command.
403 */
404static int find_command(int cmdchar)
405{
406 int i;
407 int idx;
408 int top, bot;
409 int c;
410
411 /* A multi-byte character is never a command. */
412 if (cmdchar >= 0x100)
413 return -1;
414
415 /* We use the absolute value of the character. Special keys have a
416 * negative value, but are sorted on their absolute value. */
417 if (cmdchar < 0)
418 cmdchar = -cmdchar;
419
420 /* If the character is in the first part: The character is the index into
421 * nv_cmd_idx[]. */
422 assert(nv_max_linear < (int)NV_CMDS_SIZE);
423 if (cmdchar <= nv_max_linear)
424 return nv_cmd_idx[cmdchar];
425
426 /* Perform a binary search. */
427 bot = nv_max_linear + 1;
428 top = NV_CMDS_SIZE - 1;
429 idx = -1;
430 while (bot <= top) {
431 i = (top + bot) / 2;
432 c = nv_cmds[nv_cmd_idx[i]].cmd_char;
433 if (c < 0)
434 c = -c;
435 if (cmdchar == c) {
436 idx = nv_cmd_idx[i];
437 break;
438 }
439 if (cmdchar > c)
440 bot = i + 1;
441 else
442 top = i - 1;
443 }
444 return idx;
445}
446
447// Normal state entry point. This is called on:
448//
449// - Startup, In this case the function never returns.
450// - The command-line window is opened(`q:`). Returns when `cmdwin_result` != 0.
451// - The :visual command is called from :global in ex mode, `:global/PAT/visual`
452// for example. Returns when re-entering ex mode(because ex mode recursion is
453// not allowed)
454//
455// This used to be called main_loop on main.c
456void normal_enter(bool cmdwin, bool noexmode)
457{
458 NormalState state;
459 normal_state_init(&state);
460 state.cmdwin = cmdwin;
461 state.noexmode = noexmode;
462 state.toplevel = (!cmdwin || cmdwin_result == 0) && !noexmode;
463 state_enter(&state.state);
464}
465
466static void normal_prepare(NormalState *s)
467{
468 memset(&s->ca, 0, sizeof(s->ca)); // also resets ca.retval
469 s->ca.oap = &s->oa;
470
471 // Use a count remembered from before entering an operator. After typing "3d"
472 // we return from normal_cmd() and come back here, the "3" is remembered in
473 // "opcount".
474 s->ca.opcount = opcount;
475
476 // If there is an operator pending, then the command we take this time will
477 // terminate it. Finish_op tells us to finish the operation before returning
478 // this time (unless the operation was cancelled).
479 int c = finish_op;
480 finish_op = (s->oa.op_type != OP_NOP);
481 if (finish_op != c) {
482 ui_cursor_shape(); // may show different cursor shape
483 }
484
485 // When not finishing an operator and no register name typed, reset the count.
486 if (!finish_op && !s->oa.regname) {
487 s->ca.opcount = 0;
488 s->set_prevcount = true;
489 }
490
491 // Restore counts from before receiving K_EVENT. This means after
492 // typing "3", handling K_EVENT and then typing "2" we get "32", not
493 // "3 * 2".
494 if (s->oa.prev_opcount > 0 || s->oa.prev_count0 > 0) {
495 s->ca.opcount = s->oa.prev_opcount;
496 s->ca.count0 = s->oa.prev_count0;
497 s->oa.prev_opcount = 0;
498 s->oa.prev_count0 = 0;
499 }
500
501 s->mapped_len = typebuf_maplen();
502 State = NORMAL_BUSY;
503
504 // Set v:count here, when called from main() and not a stuffed command, so
505 // that v:count can be used in an expression mapping when there is no count.
506 // Do set it for redo
507 if (s->toplevel && readbuf1_empty()) {
508 set_vcount_ca(&s->ca, &s->set_prevcount);
509 }
510}
511
512static bool normal_handle_special_visual_command(NormalState *s)
513{
514 // when 'keymodel' contains "stopsel" may stop Select/Visual mode
515 if (km_stopsel
516 && (nv_cmds[s->idx].cmd_flags & NV_STS)
517 && !(mod_mask & MOD_MASK_SHIFT)) {
518 end_visual_mode();
519 redraw_curbuf_later(INVERTED);
520 }
521
522 // Keys that work different when 'keymodel' contains "startsel"
523 if (km_startsel) {
524 if (nv_cmds[s->idx].cmd_flags & NV_SS) {
525 unshift_special(&s->ca);
526 s->idx = find_command(s->ca.cmdchar);
527 if (s->idx < 0) {
528 // Just in case
529 clearopbeep(&s->oa);
530 return true;
531 }
532 } else if ((nv_cmds[s->idx].cmd_flags & NV_SSS)
533 && (mod_mask & MOD_MASK_SHIFT)) {
534 mod_mask &= ~MOD_MASK_SHIFT;
535 }
536 }
537 return false;
538}
539
540static bool normal_need_additional_char(NormalState *s)
541{
542 int flags = nv_cmds[s->idx].cmd_flags;
543 bool pending_op = s->oa.op_type != OP_NOP;
544 int cmdchar = s->ca.cmdchar;
545 // without NV_NCH we never need to check for an additional char
546 return flags & NV_NCH && (
547 // NV_NCH_NOP is set and no operator is pending, get a second char
548 ((flags & NV_NCH_NOP) == NV_NCH_NOP && !pending_op)
549 // NV_NCH_ALW is set, always get a second char
550 || (flags & NV_NCH_ALW) == NV_NCH_ALW
551 // 'q' without a pending operator, recording or executing a register,
552 // needs to be followed by a second char, examples:
553 // - qc => record using register c
554 // - q: => open command-line window
555 || (cmdchar == 'q'
556 && !pending_op
557 && reg_recording == 0
558 && reg_executing == 0)
559 // 'a' or 'i' after an operator is a text object, examples:
560 // - ciw => change inside word
561 // - da( => delete parenthesis and everything inside.
562 // Also, don't do anything when these keys are received in visual mode
563 // so just get another char.
564 //
565 // TODO(tarruda): Visual state needs to be refactored into a
566 // separate state that "inherits" from normal state.
567 || ((cmdchar == 'a' || cmdchar == 'i') && (pending_op || VIsual_active)));
568}
569
570static bool normal_need_redraw_mode_message(NormalState *s)
571{
572 return (
573 // 'showmode' is set and messages can be printed
574 ((p_smd && msg_silent == 0
575 // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual
576 // mode
577 && (restart_edit != 0 || (VIsual_active
578 && s->old_pos.lnum == curwin->w_cursor.lnum
579 && s->old_pos.col == curwin->w_cursor.col))
580 // command-line must be cleared or redrawn
581 && (clear_cmdline || redraw_cmdline)
582 // some message was printed or scrolled
583 && (msg_didout || (msg_didany && msg_scroll))
584 // it is fine to remove the current message
585 && !msg_nowait
586 // the command was the result of direct user input and not a mapping
587 && KeyTyped)
588 // must restart insert mode, not in visual mode and error message is
589 // being shown
590 || (restart_edit != 0 && !VIsual_active && msg_scroll
591 && emsg_on_display))
592 // no register was used
593 && s->oa.regname == 0
594 && !(s->ca.retval & CA_COMMAND_BUSY)
595 && stuff_empty()
596 && typebuf_typed()
597 && emsg_silent == 0
598 && !did_wait_return
599 && s->oa.op_type == OP_NOP);
600}
601
602static void normal_redraw_mode_message(NormalState *s)
603{
604 int save_State = State;
605
606 // Draw the cursor with the right shape here
607 if (restart_edit != 0) {
608 State = INSERT;
609 }
610
611 // If need to redraw, and there is a "keep_msg", redraw before the
612 // delay
613 if (must_redraw && keep_msg != NULL && !emsg_on_display) {
614 char_u *kmsg;
615
616 kmsg = keep_msg;
617 keep_msg = NULL;
618 // showmode() will clear keep_msg, but we want to use it anyway
619 update_screen(0);
620 // now reset it, otherwise it's put in the history again
621 keep_msg = kmsg;
622 msg_attr((const char *)kmsg, keep_msg_attr);
623 xfree(kmsg);
624 }
625 setcursor();
626 ui_flush();
627 if (msg_scroll || emsg_on_display) {
628 os_delay(1000L, true); // wait at least one second
629 }
630 os_delay(3000L, false); // wait up to three seconds
631 State = save_State;
632
633 msg_scroll = false;
634 emsg_on_display = false;
635}
636
637// TODO(tarruda): Split into a "normal pending" state that can handle K_EVENT
638static void normal_get_additional_char(NormalState *s)
639{
640 int *cp;
641 bool repl = false; // get character for replace mode
642 bool lit = false; // get extra character literally
643 bool langmap_active = false; // using :lmap mappings
644 int lang; // getting a text character
645
646 no_mapping++;
647 // Don't generate a CursorHold event here, most commands can't handle
648 // it, e.g., nv_replace(), nv_csearch().
649 did_cursorhold = true;
650 if (s->ca.cmdchar == 'g') {
651 // For 'g' get the next character now, so that we can check for
652 // "gr", "g'" and "g`".
653 s->ca.nchar = plain_vgetc();
654 LANGMAP_ADJUST(s->ca.nchar, true);
655 s->need_flushbuf |= add_to_showcmd(s->ca.nchar);
656 if (s->ca.nchar == 'r' || s->ca.nchar == '\'' || s->ca.nchar == '`'
657 || s->ca.nchar == Ctrl_BSL) {
658 cp = &s->ca.extra_char; // need to get a third character
659 if (s->ca.nchar != 'r') {
660 lit = true; // get it literally
661 } else {
662 repl = true; // get it in replace mode
663 }
664 } else {
665 cp = NULL; // no third character needed
666 }
667 } else {
668 if (s->ca.cmdchar == 'r') {
669 // get it in replace mode
670 repl = true;
671 }
672 cp = &s->ca.nchar;
673 }
674 lang = (repl || (nv_cmds[s->idx].cmd_flags & NV_LANG));
675
676 // Get a second or third character.
677 if (cp != NULL) {
678 if (repl) {
679 State = REPLACE; // pretend Replace mode
680 ui_cursor_shape(); // show different cursor shape
681 }
682 if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) {
683 // Allow mappings defined with ":lmap".
684 no_mapping--;
685 if (repl) {
686 State = LREPLACE;
687 } else {
688 State = LANGMAP;
689 }
690 langmap_active = true;
691 }
692
693 *cp = plain_vgetc();
694
695 if (langmap_active) {
696 // Undo the decrement done above
697 no_mapping++;
698 }
699 State = NORMAL_BUSY;
700 s->need_flushbuf |= add_to_showcmd(*cp);
701
702 if (!lit) {
703 // Typing CTRL-K gets a digraph.
704 if (*cp == Ctrl_K && ((nv_cmds[s->idx].cmd_flags & NV_LANG)
705 || cp == &s->ca.extra_char)
706 && vim_strchr(p_cpo, CPO_DIGRAPH) == NULL) {
707 s->c = get_digraph(false);
708 if (s->c > 0) {
709 *cp = s->c;
710 // Guessing how to update showcmd here...
711 del_from_showcmd(3);
712 s->need_flushbuf |= add_to_showcmd(*cp);
713 }
714 }
715
716 // adjust chars > 127, except after "tTfFr" commands
717 LANGMAP_ADJUST(*cp, !lang);
718 // adjust Hebrew mapped char
719 if (p_hkmap && lang && KeyTyped) {
720 *cp = hkmap(*cp);
721 }
722 }
723
724 // When the next character is CTRL-\ a following CTRL-N means the
725 // command is aborted and we go to Normal mode.
726 if (cp == &s->ca.extra_char
727 && s->ca.nchar == Ctrl_BSL
728 && (s->ca.extra_char == Ctrl_N || s->ca.extra_char == Ctrl_G)) {
729 s->ca.cmdchar = Ctrl_BSL;
730 s->ca.nchar = s->ca.extra_char;
731 s->idx = find_command(s->ca.cmdchar);
732 } else if ((s->ca.nchar == 'n' || s->ca.nchar == 'N')
733 && s->ca.cmdchar == 'g') {
734 s->ca.oap->op_type = get_op_type(*cp, NUL);
735 } else if (*cp == Ctrl_BSL) {
736 long towait = (p_ttm >= 0 ? p_ttm : p_tm);
737
738 // There is a busy wait here when typing "f<C-\>" and then
739 // something different from CTRL-N. Can't be avoided.
740 while ((s->c = vpeekc()) <= 0 && towait > 0L) {
741 do_sleep(towait > 50L ? 50L : towait);
742 towait -= 50L;
743 }
744 if (s->c > 0) {
745 s->c = plain_vgetc();
746 if (s->c != Ctrl_N && s->c != Ctrl_G) {
747 vungetc(s->c);
748 } else {
749 s->ca.cmdchar = Ctrl_BSL;
750 s->ca.nchar = s->c;
751 s->idx = find_command(s->ca.cmdchar);
752 assert(s->idx >= 0);
753 }
754 }
755 }
756
757 // When getting a text character and the next character is a
758 // multi-byte character, it could be a composing character.
759 // However, don't wait for it to arrive. Also, do enable mapping,
760 // because if it's put back with vungetc() it's too late to apply
761 // mapping.
762 no_mapping--;
763 while (enc_utf8 && lang && (s->c = vpeekc()) > 0
764 && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) {
765 s->c = plain_vgetc();
766 if (!utf_iscomposing(s->c)) {
767 vungetc(s->c); /* it wasn't, put it back */
768 break;
769 } else if (s->ca.ncharC1 == 0) {
770 s->ca.ncharC1 = s->c;
771 } else {
772 s->ca.ncharC2 = s->c;
773 }
774 }
775 no_mapping++;
776 }
777 no_mapping--;
778}
779
780static void normal_invert_horizontal(NormalState *s)
781{
782 switch (s->ca.cmdchar) {
783 case 'l': s->ca.cmdchar = 'h'; break;
784 case K_RIGHT: s->ca.cmdchar = K_LEFT; break;
785 case K_S_RIGHT: s->ca.cmdchar = K_S_LEFT; break;
786 case K_C_RIGHT: s->ca.cmdchar = K_C_LEFT; break;
787 case 'h': s->ca.cmdchar = 'l'; break;
788 case K_LEFT: s->ca.cmdchar = K_RIGHT; break;
789 case K_S_LEFT: s->ca.cmdchar = K_S_RIGHT; break;
790 case K_C_LEFT: s->ca.cmdchar = K_C_RIGHT; break;
791 case '>': s->ca.cmdchar = '<'; break;
792 case '<': s->ca.cmdchar = '>'; break;
793 }
794 s->idx = find_command(s->ca.cmdchar);
795}
796
797static bool normal_get_command_count(NormalState *s)
798{
799 if (VIsual_active && VIsual_select) {
800 return false;
801 }
802 // Handle a count before a command and compute ca.count0.
803 // Note that '0' is a command and not the start of a count, but it's
804 // part of a count after other digits.
805 while ((s->c >= '1' && s->c <= '9') || (s->ca.count0 != 0
806 && (s->c == K_DEL || s->c == K_KDEL || s->c == '0'))) {
807 if (s->c == K_DEL || s->c == K_KDEL) {
808 s->ca.count0 /= 10;
809 del_from_showcmd(4); // delete the digit and ~@%
810 } else {
811 s->ca.count0 = s->ca.count0 * 10 + (s->c - '0');
812 }
813
814 if (s->ca.count0 < 0) {
815 // got too large!
816 s->ca.count0 = 999999999L;
817 }
818
819 // Set v:count here, when called from main() and not a stuffed
820 // command, so that v:count can be used in an expression mapping
821 // right after the count. Do set it for redo.
822 if (s->toplevel && readbuf1_empty()) {
823 set_vcount_ca(&s->ca, &s->set_prevcount);
824 }
825
826 if (s->ctrl_w) {
827 no_mapping++;
828 }
829
830 ++no_zero_mapping; // don't map zero here
831 s->c = plain_vgetc();
832 LANGMAP_ADJUST(s->c, true);
833 --no_zero_mapping;
834 if (s->ctrl_w) {
835 no_mapping--;
836 }
837 s->need_flushbuf |= add_to_showcmd(s->c);
838 }
839
840 // If we got CTRL-W there may be a/another count
841 if (s->c == Ctrl_W && !s->ctrl_w && s->oa.op_type == OP_NOP) {
842 s->ctrl_w = true;
843 s->ca.opcount = s->ca.count0; // remember first count
844 s->ca.count0 = 0;
845 no_mapping++;
846 s->c = plain_vgetc(); // get next character
847 LANGMAP_ADJUST(s->c, true);
848 no_mapping--;
849 s->need_flushbuf |= add_to_showcmd(s->c);
850 return true;
851 }
852
853 return false;
854}
855
856static void normal_finish_command(NormalState *s)
857{
858 if (s->command_finished) {
859 goto normal_end;
860 }
861
862 // If we didn't start or finish an operator, reset oap->regname, unless we
863 // need it later.
864 if (!finish_op
865 && !s->oa.op_type
866 && (s->idx < 0 || !(nv_cmds[s->idx].cmd_flags & NV_KEEPREG))) {
867 clearop(&s->oa);
868 set_reg_var(get_default_register_name());
869 }
870
871 // Get the length of mapped chars again after typing a count, second
872 // character or "z333<cr>".
873 if (s->old_mapped_len > 0) {
874 s->old_mapped_len = typebuf_maplen();
875 }
876
877 // If an operation is pending, handle it...
878 do_pending_operator(&s->ca, s->old_col, false);
879
880 // Wait for a moment when a message is displayed that will be overwritten
881 // by the mode message.
882 // In Visual mode and with "^O" in Insert mode, a short message will be
883 // overwritten by the mode message. Wait a bit, until a key is hit.
884 // In Visual mode, it's more important to keep the Visual area updated
885 // than keeping a message (e.g. from a /pat search).
886 // Only do this if the command was typed, not from a mapping.
887 // Don't wait when emsg_silent is non-zero.
888 // Also wait a bit after an error message, e.g. for "^O:".
889 // Don't redraw the screen, it would remove the message.
890 if (normal_need_redraw_mode_message(s)) {
891 normal_redraw_mode_message(s);
892 }
893
894 // Finish up after executing a Normal mode command.
895normal_end:
896
897 msg_nowait = false;
898
899 // Reset finish_op, in case it was set
900 s->c = finish_op;
901 finish_op = false;
902 // Redraw the cursor with another shape, if we were in Operator-pending
903 // mode or did a replace command.
904 if (s->c || s->ca.cmdchar == 'r') {
905 ui_cursor_shape(); // may show different cursor shape
906 }
907
908 if (s->oa.op_type == OP_NOP && s->oa.regname == 0
909 && s->ca.cmdchar != K_EVENT) {
910 clear_showcmd();
911 }
912
913 checkpcmark(); // check if we moved since setting pcmark
914 xfree(s->ca.searchbuf);
915
916 mb_check_adjust_col(curwin); // #6203
917
918 if (curwin->w_p_scb && s->toplevel) {
919 validate_cursor(); // may need to update w_leftcol
920 do_check_scrollbind(true);
921 }
922
923 if (curwin->w_p_crb && s->toplevel) {
924 validate_cursor(); // may need to update w_leftcol
925 do_check_cursorbind();
926 }
927
928 // May restart edit(), if we got here with CTRL-O in Insert mode (but not
929 // if still inside a mapping that started in Visual mode).
930 // May switch from Visual to Select mode after CTRL-O command.
931 if (s->oa.op_type == OP_NOP
932 && ((restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0)
933 || restart_VIsual_select == 1)
934 && !(s->ca.retval & CA_COMMAND_BUSY)
935 && stuff_empty()
936 && s->oa.regname == 0) {
937 if (restart_VIsual_select == 1) {
938 VIsual_select = true;
939 showmode();
940 restart_VIsual_select = 0;
941 }
942 if (restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0) {
943 (void)edit(restart_edit, false, 1L);
944 }
945 }
946
947 if (restart_VIsual_select == 2) {
948 restart_VIsual_select = 1;
949 }
950
951 // Save count before an operator for next time
952 opcount = s->ca.opcount;
953}
954
955static int normal_execute(VimState *state, int key)
956{
957 NormalState *s = (NormalState *)state;
958 s->command_finished = false;
959 s->ctrl_w = false; /* got CTRL-W command */
960 s->old_col = curwin->w_curswant;
961 s->c = key;
962
963 LANGMAP_ADJUST(s->c, get_real_state() != SELECTMODE);
964
965 // If a mapping was started in Visual or Select mode, remember the length
966 // of the mapping. This is used below to not return to Insert mode for as
967 // long as the mapping is being executed.
968 if (restart_edit == 0) {
969 s->old_mapped_len = 0;
970 } else if (s->old_mapped_len || (VIsual_active && s->mapped_len == 0
971 && typebuf_maplen() > 0)) {
972 s->old_mapped_len = typebuf_maplen();
973 }
974
975 if (s->c == NUL) {
976 s->c = K_ZERO;
977 }
978
979 // In Select mode, typed text replaces the selection.
980 if (VIsual_active && VIsual_select && (vim_isprintc(s->c)
981 || s->c == NL || s->c == CAR || s->c == K_KENTER)) {
982 // Fake a "c"hange command. When "restart_edit" is set (e.g., because
983 // 'insertmode' is set) fake a "d"elete command, Insert mode will
984 // restart automatically.
985 // Insert the typed character in the typeahead buffer, so that it can
986 // be mapped in Insert mode. Required for ":lmap" to work.
987 ins_char_typebuf(s->c);
988 if (restart_edit != 0) {
989 s->c = 'd';
990 } else {
991 s->c = 'c';
992 }
993 msg_nowait = true; // don't delay going to insert mode
994 s->old_mapped_len = 0; // do go to Insert mode
995 }
996
997 s->need_flushbuf = add_to_showcmd(s->c);
998
999 while (normal_get_command_count(s)) continue;
1000
1001 if (s->c == K_EVENT) {
1002 // Save the count values so that ca.opcount and ca.count0 are exactly
1003 // the same when coming back here after handling K_EVENT.
1004 s->oa.prev_opcount = s->ca.opcount;
1005 s->oa.prev_count0 = s->ca.count0;
1006 } else if (s->ca.opcount != 0) {
1007 // If we're in the middle of an operator (including after entering a
1008 // yank buffer with '"') AND we had a count before the operator, then
1009 // that count overrides the current value of ca.count0.
1010 // What this means effectively, is that commands like "3dw" get turned
1011 // into "d3w" which makes things fall into place pretty neatly.
1012 // If you give a count before AND after the operator, they are
1013 // multiplied.
1014 if (s->ca.count0) {
1015 s->ca.count0 *= s->ca.opcount;
1016 } else {
1017 s->ca.count0 = s->ca.opcount;
1018 }
1019 }
1020
1021 // Always remember the count. It will be set to zero (on the next call,
1022 // above) when there is no pending operator.
1023 // When called from main(), save the count for use by the "count" built-in
1024 // variable.
1025 s->ca.opcount = s->ca.count0;
1026 s->ca.count1 = (s->ca.count0 == 0 ? 1 : s->ca.count0);
1027
1028 // Only set v:count when called from main() and not a stuffed command.
1029 // Do set it for redo.
1030 if (s->toplevel && readbuf1_empty()) {
1031 set_vcount(s->ca.count0, s->ca.count1, s->set_prevcount);
1032 }
1033
1034 // Find the command character in the table of commands.
1035 // For CTRL-W we already got nchar when looking for a count.
1036 if (s->ctrl_w) {
1037 s->ca.nchar = s->c;
1038 s->ca.cmdchar = Ctrl_W;
1039 } else {
1040 s->ca.cmdchar = s->c;
1041 }
1042
1043 s->idx = find_command(s->ca.cmdchar);
1044
1045 if (s->idx < 0) {
1046 // Not a known command: beep.
1047 clearopbeep(&s->oa);
1048 s->command_finished = true;
1049 goto finish;
1050 }
1051
1052 if (text_locked() && (nv_cmds[s->idx].cmd_flags & NV_NCW)) {
1053 // This command is not allowed while editing a cmdline: beep.
1054 clearopbeep(&s->oa);
1055 text_locked_msg();
1056 s->command_finished = true;
1057 goto finish;
1058 }
1059
1060 if ((nv_cmds[s->idx].cmd_flags & NV_NCW) && curbuf_locked()) {
1061 s->command_finished = true;
1062 goto finish;
1063 }
1064
1065 // In Visual/Select mode, a few keys are handled in a special way.
1066 if (VIsual_active && normal_handle_special_visual_command(s)) {
1067 s->command_finished = true;
1068 goto finish;
1069 }
1070
1071 if (curwin->w_p_rl && KeyTyped && !KeyStuffed
1072 && (nv_cmds[s->idx].cmd_flags & NV_RL)) {
1073 // Invert horizontal movements and operations. Only when typed by the
1074 // user directly, not when the result of a mapping or "x" translated
1075 // to "dl".
1076 normal_invert_horizontal(s);
1077 }
1078
1079 // Get an additional character if we need one.
1080 if (normal_need_additional_char(s)) {
1081 normal_get_additional_char(s);
1082 }
1083
1084 // Flush the showcmd characters onto the screen so we can see them while
1085 // the command is being executed. Only do this when the shown command was
1086 // actually displayed, otherwise this will slow down a lot when executing
1087 // mappings.
1088 if (s->need_flushbuf) {
1089 ui_flush();
1090 }
1091 if (s->ca.cmdchar != K_IGNORE && s->ca.cmdchar != K_EVENT) {
1092 did_cursorhold = false;
1093 }
1094
1095 State = NORMAL;
1096
1097 if (s->ca.nchar == ESC) {
1098 clearop(&s->oa);
1099 if (restart_edit == 0 && goto_im()) {
1100 restart_edit = 'a';
1101 }
1102 s->command_finished = true;
1103 goto finish;
1104 }
1105
1106 if (s->ca.cmdchar != K_IGNORE) {
1107 msg_didout = false; // don't scroll screen up for normal command
1108 msg_col = 0;
1109 }
1110
1111 s->old_pos = curwin->w_cursor; // remember where cursor was
1112
1113 // When 'keymodel' contains "startsel" some keys start Select/Visual
1114 // mode.
1115 if (!VIsual_active && km_startsel) {
1116 if (nv_cmds[s->idx].cmd_flags & NV_SS) {
1117 start_selection();
1118 unshift_special(&s->ca);
1119 s->idx = find_command(s->ca.cmdchar);
1120 assert(s->idx >= 0);
1121 } else if ((nv_cmds[s->idx].cmd_flags & NV_SSS)
1122 && (mod_mask & MOD_MASK_SHIFT)) {
1123 start_selection();
1124 mod_mask &= ~MOD_MASK_SHIFT;
1125 }
1126 }
1127
1128 // Execute the command!
1129 // Call the command function found in the commands table.
1130 s->ca.arg = nv_cmds[s->idx].cmd_arg;
1131 (nv_cmds[s->idx].cmd_func)(&s->ca);
1132
1133finish:
1134 normal_finish_command(s);
1135 return 1;
1136}
1137
1138static void normal_check_stuff_buffer(NormalState *s)
1139{
1140 if (stuff_empty()) {
1141 did_check_timestamps = false;
1142
1143 if (need_check_timestamps) {
1144 check_timestamps(false);
1145 }
1146
1147 if (need_wait_return) {
1148 // if wait_return still needed call it now
1149 wait_return(false);
1150 }
1151
1152 if (need_start_insertmode && goto_im() && !VIsual_active) {
1153 need_start_insertmode = false;
1154 stuffReadbuff("i"); // start insert mode next
1155 // skip the fileinfo message now, because it would be shown
1156 // after insert mode finishes!
1157 need_fileinfo = false;
1158 }
1159 }
1160}
1161
1162static void normal_check_interrupt(NormalState *s)
1163{
1164 // Reset "got_int" now that we got back to the main loop. Except when
1165 // inside a ":g/pat/cmd" command, then the "got_int" needs to abort
1166 // the ":g" command.
1167 // For ":g/pat/vi" we reset "got_int" when used once. When used
1168 // a second time we go back to Ex mode and abort the ":g" command.
1169 if (got_int) {
1170 if (s->noexmode && global_busy && !exmode_active
1171 && s->previous_got_int) {
1172 // Typed two CTRL-C in a row: go back to ex mode as if "Q" was
1173 // used and keep "got_int" set, so that it aborts ":g".
1174 exmode_active = EXMODE_NORMAL;
1175 State = NORMAL;
1176 } else if (!global_busy || !exmode_active) {
1177 if (!quit_more) {
1178 // flush all buffers
1179 (void)vgetc();
1180 }
1181 got_int = false;
1182 }
1183 s->previous_got_int = true;
1184 } else {
1185 s->previous_got_int = false;
1186 }
1187}
1188
1189static void normal_check_cursor_moved(NormalState *s)
1190{
1191 // Trigger CursorMoved if the cursor moved.
1192 if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0)
1193 && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)) {
1194 if (has_event(EVENT_CURSORMOVED)) {
1195 apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
1196 }
1197
1198 curwin->w_last_cursormoved = curwin->w_cursor;
1199 }
1200}
1201
1202static void normal_check_text_changed(NormalState *s)
1203{
1204 // Trigger TextChanged if changedtick differs.
1205 if (!finish_op && has_event(EVENT_TEXTCHANGED)
1206 && curbuf->b_last_changedtick != buf_get_changedtick(curbuf)) {
1207 apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf);
1208 curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
1209 }
1210}
1211
1212static void normal_check_folds(NormalState *s)
1213{
1214 // Include a closed fold completely in the Visual area.
1215 foldAdjustVisual();
1216
1217 // When 'foldclose' is set, apply 'foldlevel' to folds that don't
1218 // contain the cursor.
1219 // When 'foldopen' is "all", open the fold(s) under the cursor.
1220 // This may mark the window for redrawing.
1221 if (hasAnyFolding(curwin) && !char_avail()) {
1222 foldCheckClose();
1223
1224 if (fdo_flags & FDO_ALL) {
1225 foldOpenCursor();
1226 }
1227 }
1228}
1229
1230static void normal_redraw(NormalState *s)
1231{
1232 // Before redrawing, make sure w_topline is correct, and w_leftcol
1233 // if lines don't wrap, and w_skipcol if lines wrap.
1234 update_topline();
1235 validate_cursor();
1236
1237 // If the cursor moves horizontally when 'concealcursor' is active, then the
1238 // current line needs to be redrawn in order to calculate the correct
1239 // cursor position.
1240 if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) {
1241 redrawWinline(curwin, curwin->w_cursor.lnum);
1242 }
1243
1244 if (VIsual_active) {
1245 update_curbuf(INVERTED); // update inverted part
1246 } else if (must_redraw) {
1247 update_screen(0);
1248 } else if (redraw_cmdline || clear_cmdline) {
1249 showmode();
1250 }
1251
1252 redraw_statuslines();
1253
1254 if (need_maketitle) {
1255 maketitle();
1256 }
1257
1258 // Display message after redraw. If an external message is still visible,
1259 // it contains the kept message already.
1260 if (keep_msg != NULL && !msg_ext_is_visible()) {
1261 // msg_attr_keep() will set keep_msg to NULL, must free the string here.
1262 // Don't reset keep_msg, msg_attr_keep() uses it to check for duplicates.
1263 char *p = (char *)keep_msg;
1264 msg_attr(p, keep_msg_attr);
1265 xfree(p);
1266 }
1267
1268 // show fileinfo after redraw
1269 if (need_fileinfo && !shortmess(SHM_FILEINFO)) {
1270 fileinfo(false, true, false);
1271 need_fileinfo = false;
1272 }
1273
1274 emsg_on_display = false; // can delete error message now
1275 did_emsg = false;
1276 msg_didany = false; // reset lines_left in msg_start()
1277 may_clear_sb_text(); // clear scroll-back text on next msg
1278 showruler(false);
1279
1280 setcursor();
1281}
1282
1283// Function executed before each iteration of normal mode.
1284// Return:
1285// 1 if the iteration should continue normally
1286// -1 if the iteration should be skipped
1287// 0 if the main loop must exit
1288static int normal_check(VimState *state)
1289{
1290 NormalState *s = (NormalState *)state;
1291 normal_check_stuff_buffer(s);
1292 normal_check_interrupt(s);
1293
1294 if (!exmode_active) {
1295 msg_scroll = false;
1296 }
1297 quit_more = false;
1298
1299 // If skip redraw is set (for ":" in wait_return()), don't redraw now.
1300 // If there is nothing in the stuff_buffer or do_redraw is TRUE,
1301 // update cursor and redraw.
1302 if (skip_redraw || exmode_active) {
1303 skip_redraw = false;
1304 } else if (do_redraw || stuff_empty()) {
1305 normal_check_cursor_moved(s);
1306 normal_check_text_changed(s);
1307
1308 // Updating diffs from changed() does not always work properly,
1309 // esp. updating folds. Do an update just before redrawing if
1310 // needed.
1311 if (curtab->tp_diff_update || curtab->tp_diff_invalid) {
1312 ex_diffupdate(NULL);
1313 curtab->tp_diff_update = false;
1314 }
1315
1316 // Scroll-binding for diff mode may have been postponed until
1317 // here. Avoids doing it for every change.
1318 if (diff_need_scrollbind) {
1319 check_scrollbind((linenr_T)0, 0L);
1320 diff_need_scrollbind = false;
1321 }
1322
1323 normal_check_folds(s);
1324 normal_redraw(s);
1325 do_redraw = false;
1326
1327 // Now that we have drawn the first screen all the startup stuff
1328 // has been done, close any file for startup messages.
1329 if (time_fd != NULL) {
1330 TIME_MSG("first screen update");
1331 TIME_MSG("--- NVIM STARTED ---");
1332 fclose(time_fd);
1333 time_fd = NULL;
1334 }
1335 }
1336
1337 // May perform garbage collection when waiting for a character, but
1338 // only at the very toplevel. Otherwise we may be using a List or
1339 // Dict internally somewhere.
1340 // "may_garbage_collect" is reset in vgetc() which is invoked through
1341 // do_exmode() and normal_cmd().
1342 may_garbage_collect = !s->cmdwin && !s->noexmode;
1343
1344 // Update w_curswant if w_set_curswant has been set.
1345 // Postponed until here to avoid computing w_virtcol too often.
1346 update_curswant();
1347
1348 if (exmode_active) {
1349 if (s->noexmode) {
1350 return 0;
1351 }
1352 do_exmode(exmode_active == EXMODE_VIM);
1353 return -1;
1354 }
1355
1356 if (s->cmdwin && cmdwin_result != 0) {
1357 // command-line window and cmdwin_result is set
1358 return 0;
1359 }
1360
1361 normal_prepare(s);
1362 return 1;
1363}
1364
1365/*
1366 * Set v:count and v:count1 according to "cap".
1367 * Set v:prevcount only when "set_prevcount" is true.
1368 */
1369static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
1370{
1371 long count = cap->count0;
1372
1373 /* multiply with cap->opcount the same way as above */
1374 if (cap->opcount != 0)
1375 count = cap->opcount * (count == 0 ? 1 : count);
1376 set_vcount(count, count == 0 ? 1 : count, *set_prevcount);
1377 *set_prevcount = false; /* only set v:prevcount once */
1378}
1379
1380// Handle an operator after Visual mode or when the movement is finished.
1381// "gui_yank" is true when yanking text for the clipboard.
1382void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
1383{
1384 oparg_T *oap = cap->oap;
1385 pos_T old_cursor;
1386 bool empty_region_error;
1387 int restart_edit_save;
1388 int lbr_saved = curwin->w_p_lbr;
1389
1390
1391 // The visual area is remembered for redo
1392 static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
1393 static linenr_T redo_VIsual_line_count; // number of lines
1394 static colnr_T redo_VIsual_vcol; // number of cols or end column
1395 static long redo_VIsual_count; // count for Visual operator
1396 static int redo_VIsual_arg; // extra argument
1397 bool include_line_break = false;
1398
1399 old_cursor = curwin->w_cursor;
1400
1401 /*
1402 * If an operation is pending, handle it...
1403 */
1404 if ((finish_op
1405 || VIsual_active)
1406 && oap->op_type != OP_NOP) {
1407 // Yank can be redone when 'y' is in 'cpoptions', but not when yanking
1408 // for the clipboard.
1409 const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank;
1410
1411 // Avoid a problem with unwanted linebreaks in block mode
1412 if (curwin->w_p_lbr) {
1413 curwin->w_valid &= ~VALID_VIRTCOL;
1414 }
1415 curwin->w_p_lbr = false;
1416 oap->is_VIsual = VIsual_active;
1417 if (oap->motion_force == 'V') {
1418 oap->motion_type = kMTLineWise;
1419 } else if (oap->motion_force == 'v') {
1420 // If the motion was linewise, "inclusive" will not have been set.
1421 // Use "exclusive" to be consistent. Makes "dvj" work nice.
1422 if (oap->motion_type == kMTLineWise) {
1423 oap->inclusive = false;
1424 } else if (oap->motion_type == kMTCharWise) {
1425 // If the motion already was characterwise, toggle "inclusive"
1426 oap->inclusive = !oap->inclusive;
1427 }
1428 oap->motion_type = kMTCharWise;
1429 } else if (oap->motion_force == Ctrl_V) {
1430 // Change line- or characterwise motion into Visual block mode.
1431 if (!VIsual_active) {
1432 VIsual_active = true;
1433 VIsual = oap->start;
1434 }
1435 VIsual_mode = Ctrl_V;
1436 VIsual_select = false;
1437 VIsual_reselect = false;
1438 }
1439
1440 // Only redo yank when 'y' flag is in 'cpoptions'.
1441 // Never redo "zf" (define fold).
1442 if ((redo_yank || oap->op_type != OP_YANK)
1443 && ((!VIsual_active || oap->motion_force)
1444 // Also redo Operator-pending Visual mode mappings.
1445 || (cap->cmdchar == ':' && oap->op_type != OP_COLON))
1446 && cap->cmdchar != 'D'
1447 && oap->op_type != OP_FOLD
1448 && oap->op_type != OP_FOLDOPEN
1449 && oap->op_type != OP_FOLDOPENREC
1450 && oap->op_type != OP_FOLDCLOSE
1451 && oap->op_type != OP_FOLDCLOSEREC
1452 && oap->op_type != OP_FOLDDEL
1453 && oap->op_type != OP_FOLDDELREC
1454 ) {
1455 prep_redo(oap->regname, cap->count0,
1456 get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
1457 oap->motion_force, cap->cmdchar, cap->nchar);
1458 if (cap->cmdchar == '/' || cap->cmdchar == '?') { /* was a search */
1459 /*
1460 * If 'cpoptions' does not contain 'r', insert the search
1461 * pattern to really repeat the same command.
1462 */
1463 if (vim_strchr(p_cpo, CPO_REDO) == NULL) {
1464 AppendToRedobuffLit(cap->searchbuf, -1);
1465 }
1466 AppendToRedobuff(NL_STR);
1467 } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) {
1468 // do_cmdline() has stored the first typed line in
1469 // "repeat_cmdline". When several lines are typed repeating
1470 // won't be possible.
1471 if (repeat_cmdline == NULL) {
1472 ResetRedobuff();
1473 } else {
1474 AppendToRedobuffLit(repeat_cmdline, -1);
1475 AppendToRedobuff(NL_STR);
1476 XFREE_CLEAR(repeat_cmdline);
1477 }
1478 }
1479 }
1480
1481 if (redo_VIsual_busy) {
1482 /* Redo of an operation on a Visual area. Use the same size from
1483 * redo_VIsual_line_count and redo_VIsual_vcol. */
1484 oap->start = curwin->w_cursor;
1485 curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
1486 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
1487 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
1488 VIsual_mode = redo_VIsual_mode;
1489 if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') {
1490 if (VIsual_mode == 'v') {
1491 if (redo_VIsual_line_count <= 1) {
1492 validate_virtcol();
1493 curwin->w_curswant =
1494 curwin->w_virtcol + redo_VIsual_vcol - 1;
1495 } else
1496 curwin->w_curswant = redo_VIsual_vcol;
1497 } else {
1498 curwin->w_curswant = MAXCOL;
1499 }
1500 coladvance(curwin->w_curswant);
1501 }
1502 cap->count0 = redo_VIsual_count;
1503 cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
1504 } else if (VIsual_active) {
1505 if (!gui_yank) {
1506 /* Save the current VIsual area for '< and '> marks, and "gv" */
1507 curbuf->b_visual.vi_start = VIsual;
1508 curbuf->b_visual.vi_end = curwin->w_cursor;
1509 curbuf->b_visual.vi_mode = VIsual_mode;
1510 if (VIsual_mode_orig != NUL) {
1511 curbuf->b_visual.vi_mode = VIsual_mode_orig;
1512 VIsual_mode_orig = NUL;
1513 }
1514 curbuf->b_visual.vi_curswant = curwin->w_curswant;
1515 curbuf->b_visual_mode_eval = VIsual_mode;
1516 }
1517
1518 // In Select mode, a linewise selection is operated upon like a
1519 // characterwise selection.
1520 // Special case: gH<Del> deletes the last line.
1521 if (VIsual_select && VIsual_mode == 'V'
1522 && cap->oap->op_type != OP_DELETE) {
1523 if (lt(VIsual, curwin->w_cursor)) {
1524 VIsual.col = 0;
1525 curwin->w_cursor.col =
1526 (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum));
1527 } else {
1528 curwin->w_cursor.col = 0;
1529 VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum));
1530 }
1531 VIsual_mode = 'v';
1532 }
1533 /* If 'selection' is "exclusive", backup one character for
1534 * charwise selections. */
1535 else if (VIsual_mode == 'v') {
1536 include_line_break =
1537 unadjust_for_sel();
1538 }
1539
1540 oap->start = VIsual;
1541 if (VIsual_mode == 'V') {
1542 oap->start.col = 0;
1543 oap->start.coladd = 0;
1544 }
1545 }
1546
1547 /*
1548 * Set oap->start to the first position of the operated text, oap->end
1549 * to the end of the operated text. w_cursor is equal to oap->start.
1550 */
1551 if (lt(oap->start, curwin->w_cursor)) {
1552 /* Include folded lines completely. */
1553 if (!VIsual_active) {
1554 if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL))
1555 oap->start.col = 0;
1556 if (hasFolding(curwin->w_cursor.lnum, NULL,
1557 &curwin->w_cursor.lnum))
1558 curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr());
1559 }
1560 oap->end = curwin->w_cursor;
1561 curwin->w_cursor = oap->start;
1562
1563 /* w_virtcol may have been updated; if the cursor goes back to its
1564 * previous position w_virtcol becomes invalid and isn't updated
1565 * automatically. */
1566 curwin->w_valid &= ~VALID_VIRTCOL;
1567 } else {
1568 // Include folded lines completely.
1569 if (!VIsual_active && oap->motion_type == kMTLineWise) {
1570 if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum,
1571 NULL)) {
1572 curwin->w_cursor.col = 0;
1573 }
1574 if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) {
1575 oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum));
1576 }
1577 }
1578 oap->end = oap->start;
1579 oap->start = curwin->w_cursor;
1580 }
1581
1582 // Just in case lines were deleted that make the position invalid.
1583 check_pos(curwin->w_buffer, &oap->end);
1584 oap->line_count = oap->end.lnum - oap->start.lnum + 1;
1585
1586 /* Set "virtual_op" before resetting VIsual_active. */
1587 virtual_op = virtual_active();
1588
1589 if (VIsual_active || redo_VIsual_busy) {
1590 get_op_vcol(oap, redo_VIsual_vcol, true);
1591
1592 if (!redo_VIsual_busy && !gui_yank) {
1593 /*
1594 * Prepare to reselect and redo Visual: this is based on the
1595 * size of the Visual text
1596 */
1597 resel_VIsual_mode = VIsual_mode;
1598 if (curwin->w_curswant == MAXCOL)
1599 resel_VIsual_vcol = MAXCOL;
1600 else {
1601 if (VIsual_mode != Ctrl_V)
1602 getvvcol(curwin, &(oap->end),
1603 NULL, NULL, &oap->end_vcol);
1604 if (VIsual_mode == Ctrl_V || oap->line_count <= 1) {
1605 if (VIsual_mode != Ctrl_V)
1606 getvvcol(curwin, &(oap->start),
1607 &oap->start_vcol, NULL, NULL);
1608 resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1;
1609 } else
1610 resel_VIsual_vcol = oap->end_vcol;
1611 }
1612 resel_VIsual_line_count = oap->line_count;
1613 }
1614
1615 // can't redo yank (unless 'y' is in 'cpoptions') and ":"
1616 if ((redo_yank || oap->op_type != OP_YANK)
1617 && oap->op_type != OP_COLON
1618 && oap->op_type != OP_FOLD
1619 && oap->op_type != OP_FOLDOPEN
1620 && oap->op_type != OP_FOLDOPENREC
1621 && oap->op_type != OP_FOLDCLOSE
1622 && oap->op_type != OP_FOLDCLOSEREC
1623 && oap->op_type != OP_FOLDDEL
1624 && oap->op_type != OP_FOLDDELREC
1625 && oap->motion_force == NUL
1626 ) {
1627 /* Prepare for redoing. Only use the nchar field for "r",
1628 * otherwise it might be the second char of the operator. */
1629 if (cap->cmdchar == 'g' && (cap->nchar == 'n'
1630 || cap->nchar == 'N')) {
1631 prep_redo(oap->regname, cap->count0,
1632 get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
1633 oap->motion_force, cap->cmdchar, cap->nchar);
1634 } else if (cap->cmdchar != ':') {
1635 int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
1636
1637 // reverse what nv_replace() did
1638 if (nchar == REPLACE_CR_NCHAR) {
1639 nchar = CAR;
1640 } else if (nchar == REPLACE_NL_NCHAR) {
1641 nchar = NL;
1642 }
1643 prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type),
1644 get_extra_op_char(oap->op_type), nchar);
1645 }
1646 if (!redo_VIsual_busy) {
1647 redo_VIsual_mode = resel_VIsual_mode;
1648 redo_VIsual_vcol = resel_VIsual_vcol;
1649 redo_VIsual_line_count = resel_VIsual_line_count;
1650 redo_VIsual_count = cap->count0;
1651 redo_VIsual_arg = cap->arg;
1652 }
1653 }
1654
1655 // oap->inclusive defaults to true.
1656 // If oap->end is on a NUL (empty line) oap->inclusive becomes
1657 // false. This makes "d}P" and "v}dP" work the same.
1658 if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) {
1659 oap->inclusive = true;
1660 }
1661 if (VIsual_mode == 'V') {
1662 oap->motion_type = kMTLineWise;
1663 } else if (VIsual_mode == 'v') {
1664 oap->motion_type = kMTCharWise;
1665 if (*ml_get_pos(&(oap->end)) == NUL
1666 && (include_line_break || !virtual_op)
1667 ) {
1668 oap->inclusive = false;
1669 // Try to include the newline, unless it's an operator
1670 // that works on lines only.
1671 if (*p_sel != 'o'
1672 && !op_on_lines(oap->op_type)
1673 && oap->end.lnum < curbuf->b_ml.ml_line_count) {
1674 oap->end.lnum++;
1675 oap->end.col = 0;
1676 oap->end.coladd = 0;
1677 oap->line_count++;
1678 }
1679 }
1680 }
1681
1682 redo_VIsual_busy = false;
1683
1684 /*
1685 * Switch Visual off now, so screen updating does
1686 * not show inverted text when the screen is redrawn.
1687 * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is
1688 * no screen redraw, so it is done here to remove the inverted
1689 * part.
1690 */
1691 if (!gui_yank) {
1692 VIsual_active = false;
1693 setmouse();
1694 mouse_dragging = 0;
1695 may_clear_cmdline();
1696 if ((oap->op_type == OP_YANK
1697 || oap->op_type == OP_COLON
1698 || oap->op_type == OP_FUNCTION
1699 || oap->op_type == OP_FILTER)
1700 && oap->motion_force == NUL) {
1701 // Make sure redrawing is correct.
1702 curwin->w_p_lbr = lbr_saved;
1703 redraw_curbuf_later(INVERTED);
1704 }
1705 }
1706 }
1707
1708 /* Include the trailing byte of a multi-byte char. */
1709 if (has_mbyte && oap->inclusive) {
1710 int l;
1711
1712 l = (*mb_ptr2len)(ml_get_pos(&oap->end));
1713 if (l > 1)
1714 oap->end.col += l - 1;
1715 }
1716 curwin->w_set_curswant = true;
1717
1718 /*
1719 * oap->empty is set when start and end are the same. The inclusive
1720 * flag affects this too, unless yanking and the end is on a NUL.
1721 */
1722 oap->empty = (oap->motion_type != kMTLineWise
1723 && (!oap->inclusive
1724 || (oap->op_type == OP_YANK
1725 && gchar_pos(&oap->end) == NUL))
1726 && equalpos(oap->start, oap->end)
1727 && !(virtual_op && oap->start.coladd != oap->end.coladd)
1728 );
1729 /*
1730 * For delete, change and yank, it's an error to operate on an
1731 * empty region, when 'E' included in 'cpoptions' (Vi compatible).
1732 */
1733 empty_region_error = (oap->empty
1734 && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL);
1735
1736 /* Force a redraw when operating on an empty Visual region, when
1737 * 'modifiable is off or creating a fold. */
1738 if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf)
1739 || oap->op_type == OP_FOLD
1740 )) {
1741 curwin->w_p_lbr = lbr_saved;
1742 redraw_curbuf_later(INVERTED);
1743 }
1744
1745 /*
1746 * If the end of an operator is in column one while oap->motion_type
1747 * is kMTCharWise and oap->inclusive is false, we put op_end after the last
1748 * character in the previous line. If op_start is on or before the
1749 * first non-blank in the line, the operator becomes linewise
1750 * (strange, but that's the way vi does it).
1751 */
1752 if (oap->motion_type == kMTCharWise
1753 && oap->inclusive == false
1754 && !(cap->retval & CA_NO_ADJ_OP_END)
1755 && oap->end.col == 0
1756 && (!oap->is_VIsual || *p_sel == 'o')
1757 && oap->line_count > 1) {
1758 oap->end_adjusted = true; // remember that we did this
1759 oap->line_count--;
1760 oap->end.lnum--;
1761 if (inindent(0)) {
1762 oap->motion_type = kMTLineWise;
1763 } else {
1764 oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
1765 if (oap->end.col) {
1766 --oap->end.col;
1767 oap->inclusive = true;
1768 }
1769 }
1770 } else
1771 oap->end_adjusted = false;
1772
1773 switch (oap->op_type) {
1774 case OP_LSHIFT:
1775 case OP_RSHIFT:
1776 op_shift(oap, true,
1777 oap->is_VIsual ? (int)cap->count1 :
1778 1);
1779 auto_format(false, true);
1780 break;
1781
1782 case OP_JOIN_NS:
1783 case OP_JOIN:
1784 if (oap->line_count < 2)
1785 oap->line_count = 2;
1786 if (curwin->w_cursor.lnum + oap->line_count - 1 >
1787 curbuf->b_ml.ml_line_count) {
1788 beep_flush();
1789 } else {
1790 do_join((size_t)oap->line_count, oap->op_type == OP_JOIN,
1791 true, true, true);
1792 auto_format(false, true);
1793 }
1794 break;
1795
1796 case OP_DELETE:
1797 VIsual_reselect = false; /* don't reselect now */
1798 if (empty_region_error) {
1799 vim_beep(BO_OPER);
1800 CancelRedo();
1801 } else {
1802 (void)op_delete(oap);
1803 if (oap->motion_type == kMTLineWise && has_format_option(FO_AUTO)) {
1804 // cursor line wasn't saved yet
1805 if (u_save_cursor() == FAIL) {
1806 break;
1807 }
1808 }
1809 auto_format(false, true);
1810 }
1811 break;
1812
1813 case OP_YANK:
1814 if (empty_region_error) {
1815 if (!gui_yank) {
1816 vim_beep(BO_OPER);
1817 CancelRedo();
1818 }
1819 } else {
1820 curwin->w_p_lbr = lbr_saved;
1821 (void)op_yank(oap, !gui_yank, false);
1822 }
1823 check_cursor_col();
1824 break;
1825
1826 case OP_CHANGE:
1827 VIsual_reselect = false; /* don't reselect now */
1828 if (empty_region_error) {
1829 vim_beep(BO_OPER);
1830 CancelRedo();
1831 } else {
1832 /* This is a new edit command, not a restart. Need to
1833 * remember it to make 'insertmode' work with mappings for
1834 * Visual mode. But do this only once and not when typed and
1835 * 'insertmode' isn't set. */
1836 if (p_im || !KeyTyped)
1837 restart_edit_save = restart_edit;
1838 else
1839 restart_edit_save = 0;
1840 restart_edit = 0;
1841
1842 // Restore linebreak, so that when the user edits it looks as before.
1843 if (curwin->w_p_lbr != lbr_saved) {
1844 curwin->w_p_lbr = lbr_saved;
1845 get_op_vcol(oap, redo_VIsual_mode, false);
1846 }
1847
1848 // Reset finish_op now, don't want it set inside edit().
1849 finish_op = false;
1850 if (op_change(oap)) /* will call edit() */
1851 cap->retval |= CA_COMMAND_BUSY;
1852 if (restart_edit == 0)
1853 restart_edit = restart_edit_save;
1854 }
1855 break;
1856
1857 case OP_FILTER:
1858 if (vim_strchr(p_cpo, CPO_FILTER) != NULL) {
1859 AppendToRedobuff("!\r"); // Use any last used !cmd.
1860 } else {
1861 bangredo = true; // do_bang() will put cmd in redo buffer.
1862 }
1863 FALLTHROUGH;
1864
1865 case OP_INDENT:
1866 case OP_COLON:
1867
1868 /*
1869 * If 'equalprg' is empty, do the indenting internally.
1870 */
1871 if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) {
1872 if (curbuf->b_p_lisp) {
1873 op_reindent(oap, get_lisp_indent);
1874 break;
1875 }
1876 op_reindent(oap,
1877 *curbuf->b_p_inde != NUL ? get_expr_indent :
1878 get_c_indent);
1879 break;
1880 }
1881
1882 op_colon(oap);
1883 break;
1884
1885 case OP_TILDE:
1886 case OP_UPPER:
1887 case OP_LOWER:
1888 case OP_ROT13:
1889 if (empty_region_error) {
1890 vim_beep(BO_OPER);
1891 CancelRedo();
1892 } else
1893 op_tilde(oap);
1894 check_cursor_col();
1895 break;
1896
1897 case OP_FORMAT:
1898 if (*curbuf->b_p_fex != NUL) {
1899 op_formatexpr(oap); // use expression
1900 } else if (*p_fp != NUL || *curbuf->b_p_fp != NUL) {
1901 op_colon(oap); // use external command
1902 } else {
1903 op_format(oap, false); // use internal function
1904 }
1905 break;
1906
1907 case OP_FORMAT2:
1908 op_format(oap, true); /* use internal function */
1909 break;
1910
1911 case OP_FUNCTION:
1912 // Restore linebreak, so that when the user edits it looks as
1913 // before.
1914 curwin->w_p_lbr = lbr_saved;
1915 op_function(oap); // call 'operatorfunc'
1916 break;
1917
1918 case OP_INSERT:
1919 case OP_APPEND:
1920 VIsual_reselect = false; /* don't reselect now */
1921 if (empty_region_error) {
1922 vim_beep(BO_OPER);
1923 CancelRedo();
1924 } else {
1925 /* This is a new edit command, not a restart. Need to
1926 * remember it to make 'insertmode' work with mappings for
1927 * Visual mode. But do this only once. */
1928 restart_edit_save = restart_edit;
1929 restart_edit = 0;
1930
1931 // Restore linebreak, so that when the user edits it looks as before.
1932 if (curwin->w_p_lbr != lbr_saved) {
1933 curwin->w_p_lbr = lbr_saved;
1934 get_op_vcol(oap, redo_VIsual_mode, false);
1935 }
1936
1937 op_insert(oap, cap->count1);
1938
1939 // Reset linebreak, so that formatting works correctly.
1940 curwin->w_p_lbr = false;
1941
1942 /* TODO: when inserting in several lines, should format all
1943 * the lines. */
1944 auto_format(false, true);
1945
1946 if (restart_edit == 0) {
1947 restart_edit = restart_edit_save;
1948 } else {
1949 cap->retval |= CA_COMMAND_BUSY;
1950 }
1951 }
1952 break;
1953
1954 case OP_REPLACE:
1955 VIsual_reselect = false; /* don't reselect now */
1956 if (empty_region_error) {
1957 vim_beep(BO_OPER);
1958 CancelRedo();
1959 } else {
1960 // Restore linebreak, so that when the user edits it looks as before.
1961 if (curwin->w_p_lbr != lbr_saved) {
1962 curwin->w_p_lbr = lbr_saved;
1963 get_op_vcol(oap, redo_VIsual_mode, false);
1964 }
1965
1966 op_replace(oap, cap->nchar);
1967 }
1968 break;
1969
1970 case OP_FOLD:
1971 VIsual_reselect = false; /* don't reselect now */
1972 foldCreate(oap->start.lnum, oap->end.lnum);
1973 break;
1974
1975 case OP_FOLDOPEN:
1976 case OP_FOLDOPENREC:
1977 case OP_FOLDCLOSE:
1978 case OP_FOLDCLOSEREC:
1979 VIsual_reselect = false; /* don't reselect now */
1980 opFoldRange(oap->start.lnum, oap->end.lnum,
1981 oap->op_type == OP_FOLDOPEN
1982 || oap->op_type == OP_FOLDOPENREC,
1983 oap->op_type == OP_FOLDOPENREC
1984 || oap->op_type == OP_FOLDCLOSEREC,
1985 oap->is_VIsual);
1986 break;
1987
1988 case OP_FOLDDEL:
1989 case OP_FOLDDELREC:
1990 VIsual_reselect = false; /* don't reselect now */
1991 deleteFold(oap->start.lnum, oap->end.lnum,
1992 oap->op_type == OP_FOLDDELREC, oap->is_VIsual);
1993 break;
1994
1995 case OP_NR_ADD:
1996 case OP_NR_SUB:
1997 if (empty_region_error) {
1998 vim_beep(BO_OPER);
1999 CancelRedo();
2000 } else {
2001 VIsual_active = true;
2002 curwin->w_p_lbr = lbr_saved;
2003 op_addsub(oap, cap->count1, redo_VIsual_arg);
2004 VIsual_active = false;
2005 }
2006 check_cursor_col();
2007 break;
2008 default:
2009 clearopbeep(oap);
2010 }
2011 virtual_op = kNone;
2012 if (!gui_yank) {
2013 /*
2014 * if 'sol' not set, go back to old column for some commands
2015 */
2016 if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted
2017 && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT
2018 || oap->op_type == OP_DELETE)) {
2019 curwin->w_p_lbr = false;
2020 coladvance(curwin->w_curswant = old_col);
2021 }
2022 } else {
2023 curwin->w_cursor = old_cursor;
2024 }
2025 clearop(oap);
2026 motion_force = NUL;
2027 }
2028 curwin->w_p_lbr = lbr_saved;
2029}
2030
2031/*
2032 * Handle indent and format operators and visual mode ":".
2033 */
2034static void op_colon(oparg_T *oap)
2035{
2036 stuffcharReadbuff(':');
2037 if (oap->is_VIsual) {
2038 stuffReadbuff("'<,'>");
2039 } else {
2040 // Make the range look nice, so it can be repeated.
2041 if (oap->start.lnum == curwin->w_cursor.lnum) {
2042 stuffcharReadbuff('.');
2043 } else {
2044 stuffnumReadbuff((long)oap->start.lnum);
2045 }
2046 if (oap->end.lnum != oap->start.lnum) {
2047 stuffcharReadbuff(',');
2048 if (oap->end.lnum == curwin->w_cursor.lnum) {
2049 stuffcharReadbuff('.');
2050 } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) {
2051 stuffcharReadbuff('$');
2052 } else if (oap->start.lnum == curwin->w_cursor.lnum) {
2053 stuffReadbuff(".+");
2054 stuffnumReadbuff(oap->line_count - 1);
2055 } else {
2056 stuffnumReadbuff((long)oap->end.lnum);
2057 }
2058 }
2059 }
2060 if (oap->op_type != OP_COLON) {
2061 stuffReadbuff("!");
2062 }
2063 if (oap->op_type == OP_INDENT) {
2064 stuffReadbuff((const char *)get_equalprg());
2065 stuffReadbuff("\n");
2066 } else if (oap->op_type == OP_FORMAT) {
2067 if (*curbuf->b_p_fp != NUL) {
2068 stuffReadbuff((const char *)curbuf->b_p_fp);
2069 } else if (*p_fp != NUL) {
2070 stuffReadbuff((const char *)p_fp);
2071 } else {
2072 stuffReadbuff("fmt");
2073 }
2074 stuffReadbuff("\n']");
2075 }
2076
2077 /*
2078 * do_cmdline() does the rest
2079 */
2080}
2081
2082/*
2083 * Handle the "g@" operator: call 'operatorfunc'.
2084 */
2085static void op_function(const oparg_T *oap)
2086 FUNC_ATTR_NONNULL_ALL
2087{
2088 const TriState save_virtual_op = virtual_op;
2089
2090 if (*p_opfunc == NUL)
2091 EMSG(_("E774: 'operatorfunc' is empty"));
2092 else {
2093 /* Set '[ and '] marks to text to be operated on. */
2094 curbuf->b_op_start = oap->start;
2095 curbuf->b_op_end = oap->end;
2096 if (oap->motion_type != kMTLineWise && !oap->inclusive) {
2097 // Exclude the end position.
2098 decl(&curbuf->b_op_end);
2099 }
2100
2101 typval_T argv[2];
2102 argv[0].v_type = VAR_STRING;
2103 argv[1].v_type = VAR_UNKNOWN;
2104 argv[0].vval.v_string =
2105 (char_u *)(((const char *const[]) {
2106 [kMTBlockWise] = "block",
2107 [kMTLineWise] = "line",
2108 [kMTCharWise] = "char",
2109 })[oap->motion_type]);
2110
2111 // Reset virtual_op so that 'virtualedit' can be changed in the
2112 // function.
2113 virtual_op = kNone;
2114
2115 (void)call_func_retnr(p_opfunc, 1, argv);
2116
2117 virtual_op = save_virtual_op;
2118 }
2119}
2120
2121// Move the current tab to tab in same column as mouse or to end of the
2122// tabline if there is no tab there.
2123static void move_tab_to_mouse(void)
2124{
2125 int tabnr = tab_page_click_defs[mouse_col].tabnr;
2126 if (tabnr <= 0) {
2127 tabpage_move(9999);
2128 } else if (tabnr < tabpage_index(curtab)) {
2129 tabpage_move(tabnr - 1);
2130 } else {
2131 tabpage_move(tabnr);
2132 }
2133}
2134
2135/*
2136 * Do the appropriate action for the current mouse click in the current mode.
2137 * Not used for Command-line mode.
2138 *
2139 * Normal Mode:
2140 * event modi- position visual change action
2141 * fier cursor window
2142 * left press - yes end yes
2143 * left press C yes end yes "^]" (2)
2144 * left press S yes end yes "*" (2)
2145 * left drag - yes start if moved no
2146 * left relse - yes start if moved no
2147 * middle press - yes if not active no put register
2148 * middle press - yes if active no yank and put
2149 * right press - yes start or extend yes
2150 * right press S yes no change yes "#" (2)
2151 * right drag - yes extend no
2152 * right relse - yes extend no
2153 *
2154 * Insert or Replace Mode:
2155 * event modi- position visual change action
2156 * fier cursor window
2157 * left press - yes (cannot be active) yes
2158 * left press C yes (cannot be active) yes "CTRL-O^]" (2)
2159 * left press S yes (cannot be active) yes "CTRL-O*" (2)
2160 * left drag - yes start or extend (1) no CTRL-O (1)
2161 * left relse - yes start or extend (1) no CTRL-O (1)
2162 * middle press - no (cannot be active) no put register
2163 * right press - yes start or extend yes CTRL-O
2164 * right press S yes (cannot be active) yes "CTRL-O#" (2)
2165 *
2166 * (1) only if mouse pointer moved since press
2167 * (2) only if click is in same buffer
2168 *
2169 * Return true if start_arrow() should be called for edit mode.
2170 */
2171bool
2172do_mouse (
2173 oparg_T *oap, /* operator argument, can be NULL */
2174 int c, /* K_LEFTMOUSE, etc */
2175 int dir, /* Direction to 'put' if necessary */
2176 long count,
2177 bool fixindent /* PUT_FIXINDENT if fixing indent necessary */
2178)
2179{
2180 static bool got_click = false; /* got a click some time back */
2181
2182 int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */
2183 bool is_click; /* If false it's a drag or release event */
2184 bool is_drag; /* If true it's a drag event */
2185 int jump_flags = 0; /* flags for jump_to_mouse() */
2186 pos_T start_visual;
2187 bool moved; /* Has cursor moved? */
2188 bool in_status_line; /* mouse in status line */
2189 static bool in_tab_line = false; /* mouse clicked in tab line */
2190 bool in_sep_line; /* mouse in vertical separator line */
2191 int c1, c2;
2192 pos_T save_cursor;
2193 win_T *old_curwin = curwin;
2194 static pos_T orig_cursor;
2195 colnr_T leftcol, rightcol;
2196 pos_T end_visual;
2197 long diff;
2198 int old_active = VIsual_active;
2199 int old_mode = VIsual_mode;
2200 int regname;
2201
2202 save_cursor = curwin->w_cursor;
2203
2204 for (;; ) {
2205 which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
2206 if (is_drag) {
2207 /* If the next character is the same mouse event then use that
2208 * one. Speeds up dragging the status line. */
2209 if (vpeekc() != NUL) {
2210 int nc;
2211 int save_mouse_grid = mouse_grid;
2212 int save_mouse_row = mouse_row;
2213 int save_mouse_col = mouse_col;
2214
2215 /* Need to get the character, peeking doesn't get the actual
2216 * one. */
2217 nc = safe_vgetc();
2218 if (c == nc)
2219 continue;
2220 vungetc(nc);
2221 mouse_grid = save_mouse_grid;
2222 mouse_row = save_mouse_row;
2223 mouse_col = save_mouse_col;
2224 }
2225 }
2226 break;
2227 }
2228
2229
2230 /*
2231 * Ignore drag and release events if we didn't get a click.
2232 */
2233 if (is_click)
2234 got_click = true;
2235 else {
2236 if (!got_click) /* didn't get click, ignore */
2237 return false;
2238 if (!is_drag) { /* release, reset got_click */
2239 got_click = false;
2240 if (in_tab_line) {
2241 in_tab_line = false;
2242 return false;
2243 }
2244 }
2245 }
2246
2247
2248 /*
2249 * CTRL right mouse button does CTRL-T
2250 */
2251 if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
2252 if (State & INSERT)
2253 stuffcharReadbuff(Ctrl_O);
2254 if (count > 1)
2255 stuffnumReadbuff(count);
2256 stuffcharReadbuff(Ctrl_T);
2257 got_click = false; /* ignore drag&release now */
2258 return false;
2259 }
2260
2261 /*
2262 * CTRL only works with left mouse button
2263 */
2264 if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
2265 return false;
2266
2267 /*
2268 * When a modifier is down, ignore drag and release events, as well as
2269 * multiple clicks and the middle mouse button.
2270 * Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
2271 */
2272 if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
2273 | MOD_MASK_META))
2274 && (!is_click
2275 || (mod_mask & MOD_MASK_MULTI_CLICK)
2276 || which_button == MOUSE_MIDDLE)
2277 && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
2278 && mouse_model_popup()
2279 && which_button == MOUSE_LEFT)
2280 && !((mod_mask & MOD_MASK_ALT)
2281 && !mouse_model_popup()
2282 && which_button == MOUSE_RIGHT)
2283 )
2284 return false;
2285
2286 /*
2287 * If the button press was used as the movement command for an operator
2288 * (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
2289 * drag/release events.
2290 */
2291 if (!is_click && which_button == MOUSE_MIDDLE)
2292 return false;
2293
2294 if (oap != NULL)
2295 regname = oap->regname;
2296 else
2297 regname = 0;
2298
2299 /*
2300 * Middle mouse button does a 'put' of the selected text
2301 */
2302 if (which_button == MOUSE_MIDDLE) {
2303 if (State == NORMAL) {
2304 /*
2305 * If an operator was pending, we don't know what the user wanted
2306 * to do. Go back to normal mode: Clear the operator and beep().
2307 */
2308 if (oap != NULL && oap->op_type != OP_NOP) {
2309 clearopbeep(oap);
2310 return false;
2311 }
2312
2313 /*
2314 * If visual was active, yank the highlighted text and put it
2315 * before the mouse pointer position.
2316 * In Select mode replace the highlighted text with the clipboard.
2317 */
2318 if (VIsual_active) {
2319 if (VIsual_select) {
2320 stuffcharReadbuff(Ctrl_G);
2321 stuffReadbuff("\"+p");
2322 } else {
2323 stuffcharReadbuff('y');
2324 stuffcharReadbuff(K_MIDDLEMOUSE);
2325 }
2326 return false;
2327 }
2328 /*
2329 * The rest is below jump_to_mouse()
2330 */
2331 } else if ((State & INSERT) == 0)
2332 return false;
2333
2334 /*
2335 * Middle click in insert mode doesn't move the mouse, just insert the
2336 * contents of a register. '.' register is special, can't insert that
2337 * with do_put().
2338 * Also paste at the cursor if the current mode isn't in 'mouse' (only
2339 * happens for the GUI).
2340 */
2341 if ((State & INSERT) || !mouse_has(MOUSE_NORMAL)) {
2342 if (regname == '.')
2343 insert_reg(regname, true);
2344 else {
2345 if (regname == 0 && eval_has_provider("clipboard")) {
2346 regname = '*';
2347 }
2348 if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
2349 insert_reg(regname, true);
2350 } else {
2351 do_put(regname, NULL, BACKWARD, 1L,
2352 (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
2353
2354 /* Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r */
2355 AppendCharToRedobuff(Ctrl_R);
2356 AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
2357 AppendCharToRedobuff(regname == 0 ? '"' : regname);
2358 }
2359 }
2360 return false;
2361 }
2362 }
2363
2364 /* When dragging or button-up stay in the same window. */
2365 if (!is_click)
2366 jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
2367
2368 start_visual.lnum = 0;
2369
2370 /* Check for clicking in the tab page line. */
2371 if (mouse_row == 0 && firstwin->w_winrow > 0) {
2372 if (is_drag) {
2373 if (in_tab_line) {
2374 move_tab_to_mouse();
2375 }
2376 return false;
2377 }
2378
2379 /* click in a tab selects that tab page */
2380 if (is_click
2381 && cmdwin_type == 0
2382 && mouse_col < Columns) {
2383 in_tab_line = true;
2384 c1 = tab_page_click_defs[mouse_col].tabnr;
2385 switch (tab_page_click_defs[mouse_col].type) {
2386 case kStlClickDisabled: {
2387 break;
2388 }
2389 case kStlClickTabClose: {
2390 tabpage_T *tp;
2391
2392 // Close the current or specified tab page.
2393 if (c1 == 999) {
2394 tp = curtab;
2395 } else {
2396 tp = find_tabpage(c1);
2397 }
2398 if (tp == curtab) {
2399 if (first_tabpage->tp_next != NULL) {
2400 tabpage_close(false);
2401 }
2402 } else if (tp != NULL) {
2403 tabpage_close_other(tp, false);
2404 }
2405 break;
2406 }
2407 case kStlClickTabSwitch: {
2408 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
2409 // double click opens new page
2410 end_visual_mode();
2411 tabpage_new();
2412 tabpage_move(c1 == 0 ? 9999 : c1 - 1);
2413 } else {
2414 // Go to specified tab page, or next one if not clicking
2415 // on a label.
2416 goto_tabpage(c1);
2417
2418 // It's like clicking on the status line of a window.
2419 if (curwin != old_curwin) {
2420 end_visual_mode();
2421 }
2422 }
2423 break;
2424 }
2425 case kStlClickFuncRun: {
2426 typval_T argv[] = {
2427 {
2428 .v_lock = VAR_FIXED,
2429 .v_type = VAR_NUMBER,
2430 .vval = {
2431 .v_number = (varnumber_T) tab_page_click_defs[mouse_col].tabnr
2432 },
2433 },
2434 {
2435 .v_lock = VAR_FIXED,
2436 .v_type = VAR_NUMBER,
2437 .vval = {
2438 .v_number = (((mod_mask & MOD_MASK_MULTI_CLICK)
2439 == MOD_MASK_4CLICK)
2440 ? 4
2441 : ((mod_mask & MOD_MASK_MULTI_CLICK)
2442 == MOD_MASK_3CLICK)
2443 ? 3
2444 : ((mod_mask & MOD_MASK_MULTI_CLICK)
2445 == MOD_MASK_2CLICK)
2446 ? 2
2447 : 1)
2448 },
2449 },
2450 {
2451 .v_lock = VAR_FIXED,
2452 .v_type = VAR_STRING,
2453 .vval = { .v_string = (char_u *) (which_button == MOUSE_LEFT
2454 ? "l"
2455 : which_button == MOUSE_RIGHT
2456 ? "r"
2457 : which_button == MOUSE_MIDDLE
2458 ? "m"
2459 : "?") },
2460 },
2461 {
2462 .v_lock = VAR_FIXED,
2463 .v_type = VAR_STRING,
2464 .vval = {
2465 .v_string = (char_u[]) {
2466 (char_u) (mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
2467 (char_u) (mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
2468 (char_u) (mod_mask & MOD_MASK_ALT ? 'a' : ' '),
2469 (char_u) (mod_mask & MOD_MASK_META ? 'm' : ' '),
2470 NUL
2471 }
2472 },
2473 }
2474 };
2475 typval_T rettv;
2476 int doesrange;
2477 (void)call_func((char_u *)tab_page_click_defs[mouse_col].func,
2478 (int)strlen(tab_page_click_defs[mouse_col].func),
2479 &rettv, ARRAY_SIZE(argv), argv, NULL,
2480 curwin->w_cursor.lnum, curwin->w_cursor.lnum,
2481 &doesrange, true, NULL, NULL);
2482 tv_clear(&rettv);
2483 break;
2484 }
2485 }
2486 }
2487 return true;
2488 } else if (is_drag && in_tab_line) {
2489 move_tab_to_mouse();
2490 return false;
2491 }
2492
2493
2494 /*
2495 * When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
2496 * right button up -> pop-up menu
2497 * shift-left button -> right button
2498 * alt-left button -> alt-right button
2499 */
2500 if (mouse_model_popup()) {
2501 if (which_button == MOUSE_RIGHT
2502 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
2503 /*
2504 * NOTE: Ignore right button down and drag mouse events.
2505 * Windows only shows the popup menu on the button up event.
2506 */
2507 return false;
2508 }
2509 if (which_button == MOUSE_LEFT
2510 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
2511 which_button = MOUSE_RIGHT;
2512 mod_mask &= ~MOD_MASK_SHIFT;
2513 }
2514 }
2515
2516 if ((State & (NORMAL | INSERT))
2517 && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
2518 if (which_button == MOUSE_LEFT) {
2519 if (is_click) {
2520 /* stop Visual mode for a left click in a window, but not when
2521 * on a status line */
2522 if (VIsual_active)
2523 jump_flags |= MOUSE_MAY_STOP_VIS;
2524 } else if (mouse_has(MOUSE_VISUAL))
2525 jump_flags |= MOUSE_MAY_VIS;
2526 } else if (which_button == MOUSE_RIGHT) {
2527 if (is_click && VIsual_active) {
2528 /*
2529 * Remember the start and end of visual before moving the
2530 * cursor.
2531 */
2532 if (lt(curwin->w_cursor, VIsual)) {
2533 start_visual = curwin->w_cursor;
2534 end_visual = VIsual;
2535 } else {
2536 start_visual = VIsual;
2537 end_visual = curwin->w_cursor;
2538 }
2539 }
2540 jump_flags |= MOUSE_FOCUS;
2541 if (mouse_has(MOUSE_VISUAL))
2542 jump_flags |= MOUSE_MAY_VIS;
2543 }
2544 }
2545
2546 /*
2547 * If an operator is pending, ignore all drags and releases until the
2548 * next mouse click.
2549 */
2550 if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
2551 got_click = false;
2552 oap->motion_type = kMTCharWise;
2553 }
2554
2555 /* When releasing the button let jump_to_mouse() know. */
2556 if (!is_click && !is_drag)
2557 jump_flags |= MOUSE_RELEASED;
2558
2559 /*
2560 * JUMP!
2561 */
2562 jump_flags = jump_to_mouse(jump_flags,
2563 oap == NULL ? NULL : &(oap->inclusive), which_button);
2564 moved = (jump_flags & CURSOR_MOVED);
2565 in_status_line = (jump_flags & IN_STATUS_LINE);
2566 in_sep_line = (jump_flags & IN_SEP_LINE);
2567
2568
2569 /* When jumping to another window, clear a pending operator. That's a bit
2570 * friendlier than beeping and not jumping to that window. */
2571 if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP)
2572 clearop(oap);
2573
2574 if (mod_mask == 0
2575 && !is_drag
2576 && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
2577 && which_button == MOUSE_LEFT) {
2578 /* open or close a fold at this line */
2579 if (jump_flags & MOUSE_FOLD_OPEN)
2580 openFold(curwin->w_cursor.lnum, 1L);
2581 else
2582 closeFold(curwin->w_cursor.lnum, 1L);
2583 /* don't move the cursor if still in the same window */
2584 if (curwin == old_curwin)
2585 curwin->w_cursor = save_cursor;
2586 }
2587
2588
2589 /* Set global flag that we are extending the Visual area with mouse
2590 * dragging; temporarily minimize 'scrolloff'. */
2591 if (VIsual_active && is_drag && p_so) {
2592 /* In the very first line, allow scrolling one line */
2593 if (mouse_row == 0)
2594 mouse_dragging = 2;
2595 else
2596 mouse_dragging = 1;
2597 }
2598
2599 /* When dragging the mouse above the window, scroll down. */
2600 if (is_drag && mouse_row < 0 && !in_status_line) {
2601 scroll_redraw(false, 1L);
2602 mouse_row = 0;
2603 }
2604
2605 if (start_visual.lnum) { /* right click in visual mode */
2606 /* When ALT is pressed make Visual mode blockwise. */
2607 if (mod_mask & MOD_MASK_ALT)
2608 VIsual_mode = Ctrl_V;
2609
2610 /*
2611 * In Visual-block mode, divide the area in four, pick up the corner
2612 * that is in the quarter that the cursor is in.
2613 */
2614 if (VIsual_mode == Ctrl_V) {
2615 getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
2616 if (curwin->w_curswant > (leftcol + rightcol) / 2)
2617 end_visual.col = leftcol;
2618 else
2619 end_visual.col = rightcol;
2620 if (curwin->w_cursor.lnum >=
2621 (start_visual.lnum + end_visual.lnum) / 2) {
2622 end_visual.lnum = start_visual.lnum;
2623 }
2624
2625 /* move VIsual to the right column */
2626 start_visual = curwin->w_cursor; /* save the cursor pos */
2627 curwin->w_cursor = end_visual;
2628 coladvance(end_visual.col);
2629 VIsual = curwin->w_cursor;
2630 curwin->w_cursor = start_visual; /* restore the cursor */
2631 } else {
2632 /*
2633 * If the click is before the start of visual, change the start.
2634 * If the click is after the end of visual, change the end. If
2635 * the click is inside the visual, change the closest side.
2636 */
2637 if (lt(curwin->w_cursor, start_visual))
2638 VIsual = end_visual;
2639 else if (lt(end_visual, curwin->w_cursor))
2640 VIsual = start_visual;
2641 else {
2642 /* In the same line, compare column number */
2643 if (end_visual.lnum == start_visual.lnum) {
2644 if (curwin->w_cursor.col - start_visual.col >
2645 end_visual.col - curwin->w_cursor.col)
2646 VIsual = start_visual;
2647 else
2648 VIsual = end_visual;
2649 }
2650 /* In different lines, compare line number */
2651 else {
2652 diff = (curwin->w_cursor.lnum - start_visual.lnum) -
2653 (end_visual.lnum - curwin->w_cursor.lnum);
2654
2655 if (diff > 0) /* closest to end */
2656 VIsual = start_visual;
2657 else if (diff < 0) /* closest to start */
2658 VIsual = end_visual;
2659 else { /* in the middle line */
2660 if (curwin->w_cursor.col <
2661 (start_visual.col + end_visual.col) / 2)
2662 VIsual = end_visual;
2663 else
2664 VIsual = start_visual;
2665 }
2666 }
2667 }
2668 }
2669 }
2670 /*
2671 * If Visual mode started in insert mode, execute "CTRL-O"
2672 */
2673 else if ((State & INSERT) && VIsual_active)
2674 stuffcharReadbuff(Ctrl_O);
2675
2676 /*
2677 * Middle mouse click: Put text before cursor.
2678 */
2679 if (which_button == MOUSE_MIDDLE) {
2680 if (regname == 0 && eval_has_provider("clipboard")) {
2681 regname = '*';
2682 }
2683 if (yank_register_mline(regname)) {
2684 if (mouse_past_bottom)
2685 dir = FORWARD;
2686 } else if (mouse_past_eol)
2687 dir = FORWARD;
2688
2689 if (fixindent) {
2690 c1 = (dir == BACKWARD) ? '[' : ']';
2691 c2 = 'p';
2692 } else {
2693 c1 = (dir == FORWARD) ? 'p' : 'P';
2694 c2 = NUL;
2695 }
2696 prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
2697
2698 /*
2699 * Remember where the paste started, so in edit() Insstart can be set
2700 * to this position
2701 */
2702 if (restart_edit != 0)
2703 where_paste_started = curwin->w_cursor;
2704 do_put(regname, NULL, dir, count,
2705 (fixindent ? PUT_FIXINDENT : 0)| PUT_CURSEND);
2706 }
2707 /*
2708 * Ctrl-Mouse click or double click in a quickfix window jumps to the
2709 * error under the mouse pointer.
2710 */
2711 else if (((mod_mask & MOD_MASK_CTRL)
2712 || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
2713 && bt_quickfix(curbuf)) {
2714 if (curwin->w_llist_ref == NULL) { // quickfix window
2715 do_cmdline_cmd(".cc");
2716 } else { // location list window
2717 do_cmdline_cmd(".ll");
2718 }
2719 got_click = false; // ignore drag&release now
2720 }
2721 /*
2722 * Ctrl-Mouse click (or double click in a help window) jumps to the tag
2723 * under the mouse pointer.
2724 */
2725 else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
2726 && (mod_mask &
2727 MOD_MASK_MULTI_CLICK) ==
2728 MOD_MASK_2CLICK)) {
2729 if (State & INSERT)
2730 stuffcharReadbuff(Ctrl_O);
2731 stuffcharReadbuff(Ctrl_RSB);
2732 got_click = false; /* ignore drag&release now */
2733 }
2734 /*
2735 * Shift-Mouse click searches for the next occurrence of the word under
2736 * the mouse pointer
2737 */
2738 else if ((mod_mask & MOD_MASK_SHIFT)) {
2739 if (State & INSERT
2740 || (VIsual_active && VIsual_select)
2741 )
2742 stuffcharReadbuff(Ctrl_O);
2743 if (which_button == MOUSE_LEFT)
2744 stuffcharReadbuff('*');
2745 else /* MOUSE_RIGHT */
2746 stuffcharReadbuff('#');
2747 }
2748 /* Handle double clicks, unless on status line */
2749 else if (in_status_line) {
2750 } else if (in_sep_line) {
2751 } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))
2752 && mouse_has(MOUSE_VISUAL)) {
2753 if (is_click || !VIsual_active) {
2754 if (VIsual_active) {
2755 orig_cursor = VIsual;
2756 } else {
2757 VIsual = curwin->w_cursor;
2758 orig_cursor = VIsual;
2759 VIsual_active = true;
2760 VIsual_reselect = true;
2761 /* start Select mode if 'selectmode' contains "mouse" */
2762 may_start_select('o');
2763 setmouse();
2764 }
2765 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
2766 /* Double click with ALT pressed makes it blockwise. */
2767 if (mod_mask & MOD_MASK_ALT)
2768 VIsual_mode = Ctrl_V;
2769 else
2770 VIsual_mode = 'v';
2771 } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK)
2772 VIsual_mode = 'V';
2773 else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK)
2774 VIsual_mode = Ctrl_V;
2775 }
2776 /*
2777 * A double click selects a word or a block.
2778 */
2779 if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
2780 pos_T *pos = NULL;
2781 int gc;
2782
2783 if (is_click) {
2784 /* If the character under the cursor (skipping white space) is
2785 * not a word character, try finding a match and select a (),
2786 * {}, [], #if/#endif, etc. block. */
2787 end_visual = curwin->w_cursor;
2788 while (gc = gchar_pos(&end_visual), ascii_iswhite(gc))
2789 inc(&end_visual);
2790 if (oap != NULL) {
2791 oap->motion_type = kMTCharWise;
2792 }
2793 if (oap != NULL
2794 && VIsual_mode == 'v'
2795 && !vim_iswordc(gchar_pos(&end_visual))
2796 && equalpos(curwin->w_cursor, VIsual)
2797 && (pos = findmatch(oap, NUL)) != NULL) {
2798 curwin->w_cursor = *pos;
2799 if (oap->motion_type == kMTLineWise) {
2800 VIsual_mode = 'V';
2801 } else if (*p_sel == 'e') {
2802 if (lt(curwin->w_cursor, VIsual)) {
2803 VIsual.col++;
2804 } else {
2805 curwin->w_cursor.col++;
2806 }
2807 }
2808 }
2809 }
2810
2811 if (pos == NULL && (is_click || is_drag)) {
2812 /* When not found a match or when dragging: extend to include
2813 * a word. */
2814 if (lt(curwin->w_cursor, orig_cursor)) {
2815 find_start_of_word(&curwin->w_cursor);
2816 find_end_of_word(&VIsual);
2817 } else {
2818 find_start_of_word(&VIsual);
2819 if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL)
2820 curwin->w_cursor.col +=
2821 (*mb_ptr2len)(get_cursor_pos_ptr());
2822 find_end_of_word(&curwin->w_cursor);
2823 }
2824 }
2825 curwin->w_set_curswant = true;
2826 }
2827 if (is_click)
2828 redraw_curbuf_later(INVERTED); /* update the inversion */
2829 } else if (VIsual_active && !old_active) {
2830 if (mod_mask & MOD_MASK_ALT)
2831 VIsual_mode = Ctrl_V;
2832 else
2833 VIsual_mode = 'v';
2834 }
2835
2836 /* If Visual mode changed show it later. */
2837 if ((!VIsual_active && old_active && mode_displayed)
2838 || (VIsual_active && p_smd && msg_silent == 0
2839 && (!old_active || VIsual_mode != old_mode)))
2840 redraw_cmdline = true;
2841
2842 return moved;
2843}
2844
2845/*
2846 * Move "pos" back to the start of the word it's in.
2847 */
2848static void find_start_of_word(pos_T *pos)
2849{
2850 char_u *line;
2851 int cclass;
2852 int col;
2853
2854 line = ml_get(pos->lnum);
2855 cclass = get_mouse_class(line + pos->col);
2856
2857 while (pos->col > 0) {
2858 col = pos->col - 1;
2859 col -= utf_head_off(line, line + col);
2860 if (get_mouse_class(line + col) != cclass) {
2861 break;
2862 }
2863 pos->col = col;
2864 }
2865}
2866
2867/*
2868 * Move "pos" forward to the end of the word it's in.
2869 * When 'selection' is "exclusive", the position is just after the word.
2870 */
2871static void find_end_of_word(pos_T *pos)
2872{
2873 char_u *line;
2874 int cclass;
2875 int col;
2876
2877 line = ml_get(pos->lnum);
2878 if (*p_sel == 'e' && pos->col > 0) {
2879 pos->col--;
2880 pos->col -= utf_head_off(line, line + pos->col);
2881 }
2882 cclass = get_mouse_class(line + pos->col);
2883 while (line[pos->col] != NUL) {
2884 col = pos->col + (*mb_ptr2len)(line + pos->col);
2885 if (get_mouse_class(line + col) != cclass) {
2886 if (*p_sel == 'e')
2887 pos->col = col;
2888 break;
2889 }
2890 pos->col = col;
2891 }
2892}
2893
2894/*
2895 * Get class of a character for selection: same class means same word.
2896 * 0: blank
2897 * 1: punctuation groups
2898 * 2: normal word character
2899 * >2: multi-byte word character.
2900 */
2901static int get_mouse_class(char_u *p)
2902{
2903 int c;
2904
2905 if (has_mbyte && MB_BYTE2LEN(p[0]) > 1)
2906 return mb_get_class(p);
2907
2908 c = *p;
2909 if (c == ' ' || c == '\t')
2910 return 0;
2911
2912 if (vim_iswordc(c))
2913 return 2;
2914
2915 /*
2916 * There are a few special cases where we want certain combinations of
2917 * characters to be considered as a single word. These are things like
2918 * "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
2919 * character is in its own class.
2920 */
2921 if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
2922 return 1;
2923 return c;
2924}
2925
2926/*
2927 * End Visual mode.
2928 * This function should ALWAYS be called to end Visual mode, except from
2929 * do_pending_operator().
2930 */
2931void end_visual_mode(void)
2932{
2933
2934 VIsual_active = false;
2935 setmouse();
2936 mouse_dragging = 0;
2937
2938 /* Save the current VIsual area for '< and '> marks, and "gv" */
2939 curbuf->b_visual.vi_mode = VIsual_mode;
2940 curbuf->b_visual.vi_start = VIsual;
2941 curbuf->b_visual.vi_end = curwin->w_cursor;
2942 curbuf->b_visual.vi_curswant = curwin->w_curswant;
2943 curbuf->b_visual_mode_eval = VIsual_mode;
2944 if (!virtual_active())
2945 curwin->w_cursor.coladd = 0;
2946
2947 may_clear_cmdline();
2948
2949 adjust_cursor_eol();
2950}
2951
2952/*
2953 * Reset VIsual_active and VIsual_reselect.
2954 */
2955void reset_VIsual_and_resel(void)
2956{
2957 if (VIsual_active) {
2958 end_visual_mode();
2959 redraw_curbuf_later(INVERTED); /* delete the inversion later */
2960 }
2961 VIsual_reselect = false;
2962}
2963
2964/*
2965 * Reset VIsual_active and VIsual_reselect if it's set.
2966 */
2967void reset_VIsual(void)
2968{
2969 if (VIsual_active) {
2970 end_visual_mode();
2971 redraw_curbuf_later(INVERTED); /* delete the inversion later */
2972 VIsual_reselect = false;
2973 }
2974}
2975
2976// Check for a balloon-eval special item to include when searching for an
2977// identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
2978// Returns true if the character at "*ptr" should be included.
2979// "dir" is FORWARD or BACKWARD, the direction of searching.
2980// "*colp" is in/decremented if "ptr[-dir]" should also be included.
2981// "bnp" points to a counter for square brackets.
2982static bool find_is_eval_item(
2983 const char_u *const ptr,
2984 int *const colp,
2985 int *const bnp,
2986 const int dir)
2987{
2988 // Accept everything inside [].
2989 if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD)) {
2990 *bnp += 1;
2991 }
2992 if (*bnp > 0) {
2993 if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD)) {
2994 *bnp -= 1;
2995 }
2996 return true;
2997 }
2998
2999 // skip over "s.var"
3000 if (*ptr == '.') {
3001 return true;
3002 }
3003
3004 // two-character item: s->var
3005 if (ptr[dir == BACKWARD ? 0 : 1] == '>'
3006 && ptr[dir == BACKWARD ? -1 : 0] == '-') {
3007 *colp += dir;
3008 return true;
3009 }
3010 return false;
3011}
3012
3013/*
3014 * Find the identifier under or to the right of the cursor.
3015 * "find_type" can have one of three values:
3016 * FIND_IDENT: find an identifier (keyword)
3017 * FIND_STRING: find any non-white string
3018 * FIND_IDENT + FIND_STRING: find any non-white string, identifier preferred.
3019 * FIND_EVAL: find text useful for C program debugging
3020 *
3021 * There are three steps:
3022 * 1. Search forward for the start of an identifier/string. Doesn't move if
3023 * already on one.
3024 * 2. Search backward for the start of this identifier/string.
3025 * This doesn't match the real Vi but I like it a little better and it
3026 * shouldn't bother anyone.
3027 * 3. Search forward to the end of this identifier/string.
3028 * When FIND_IDENT isn't defined, we backup until a blank.
3029 *
3030 * Returns the length of the string, or zero if no string is found.
3031 * If a string is found, a pointer to the string is put in "*string". This
3032 * string is not always NUL terminated.
3033 */
3034size_t find_ident_under_cursor(char_u **string, int find_type)
3035{
3036 return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
3037 curwin->w_cursor.col, string, find_type);
3038}
3039
3040/*
3041 * Like find_ident_under_cursor(), but for any window and any position.
3042 * However: Uses 'iskeyword' from the current window!.
3043 */
3044size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
3045 char_u **string, int find_type)
3046{
3047 char_u *ptr;
3048 int col = 0; /* init to shut up GCC */
3049 int i;
3050 int this_class = 0;
3051 int prev_class;
3052 int prevcol;
3053 int bn = 0; // bracket nesting
3054
3055 /*
3056 * if i == 0: try to find an identifier
3057 * if i == 1: try to find any non-white string
3058 */
3059 ptr = ml_get_buf(wp->w_buffer, lnum, false);
3060 for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i) {
3061 /*
3062 * 1. skip to start of identifier/string
3063 */
3064 col = startcol;
3065 while (ptr[col] != NUL) {
3066 // Stop at a ']' to evaluate "a[x]".
3067 if ((find_type & FIND_EVAL) && ptr[col] == ']') {
3068 break;
3069 }
3070 this_class = mb_get_class(ptr + col);
3071 if (this_class != 0 && (i == 1 || this_class != 1)) {
3072 break;
3073 }
3074 col += utfc_ptr2len(ptr + col);
3075 }
3076
3077 // When starting on a ']' count it, so that we include the '['.
3078 bn = ptr[col] == ']';
3079
3080 //
3081 // 2. Back up to start of identifier/string.
3082 //
3083 // Remember class of character under cursor.
3084 if ((find_type & FIND_EVAL) && ptr[col] == ']') {
3085 this_class = mb_get_class((char_u *)"a");
3086 } else {
3087 this_class = mb_get_class(ptr + col);
3088 }
3089 while (col > 0 && this_class != 0) {
3090 prevcol = col - 1 - utf_head_off(ptr, ptr + col - 1);
3091 prev_class = mb_get_class(ptr + prevcol);
3092 if (this_class != prev_class
3093 && (i == 0
3094 || prev_class == 0
3095 || (find_type & FIND_IDENT))
3096 && (!(find_type & FIND_EVAL)
3097 || prevcol == 0
3098 || !find_is_eval_item(ptr + prevcol, &prevcol, &bn, BACKWARD))) {
3099 break;
3100 }
3101 col = prevcol;
3102 }
3103
3104 // If we don't want just any old string, or we've found an
3105 // identifier, stop searching.
3106 if (this_class > 2) {
3107 this_class = 2;
3108 }
3109 if (!(find_type & FIND_STRING) || this_class == 2) {
3110 break;
3111 }
3112 }
3113
3114 if (ptr[col] == NUL || (i == 0 && this_class != 2)) {
3115 // Didn't find an identifier or string.
3116 if (find_type & FIND_STRING) {
3117 EMSG(_("E348: No string under cursor"));
3118 } else {
3119 EMSG(_(e_noident));
3120 }
3121 return 0;
3122 }
3123 ptr += col;
3124 *string = ptr;
3125
3126 /*
3127 * 3. Find the end if the identifier/string.
3128 */
3129 bn = 0;
3130 startcol -= col;
3131 col = 0;
3132 // Search for point of changing multibyte character class.
3133 this_class = mb_get_class(ptr);
3134 while (ptr[col] != NUL
3135 && ((i == 0
3136 ? mb_get_class(ptr + col) == this_class
3137 : mb_get_class(ptr + col) != 0)
3138 || ((find_type & FIND_EVAL)
3139 && col <= (int)startcol
3140 && find_is_eval_item(ptr + col, &col, &bn, FORWARD)))) {
3141 col += utfc_ptr2len(ptr + col);
3142 }
3143
3144 assert(col >= 0);
3145 return (size_t)col;
3146}
3147
3148/*
3149 * Prepare for redo of a normal command.
3150 */
3151static void prep_redo_cmd(cmdarg_T *cap)
3152{
3153 prep_redo(cap->oap->regname, cap->count0,
3154 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
3155}
3156
3157/*
3158 * Prepare for redo of any command.
3159 * Note that only the last argument can be a multi-byte char.
3160 */
3161static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
3162{
3163 ResetRedobuff();
3164 if (regname != 0) { /* yank from specified buffer */
3165 AppendCharToRedobuff('"');
3166 AppendCharToRedobuff(regname);
3167 }
3168 if (num)
3169 AppendNumberToRedobuff(num);
3170
3171 if (cmd1 != NUL)
3172 AppendCharToRedobuff(cmd1);
3173 if (cmd2 != NUL)
3174 AppendCharToRedobuff(cmd2);
3175 if (cmd3 != NUL)
3176 AppendCharToRedobuff(cmd3);
3177 if (cmd4 != NUL)
3178 AppendCharToRedobuff(cmd4);
3179 if (cmd5 != NUL)
3180 AppendCharToRedobuff(cmd5);
3181}
3182
3183/*
3184 * check for operator active and clear it
3185 *
3186 * return true if operator was active
3187 */
3188static bool checkclearop(oparg_T *oap)
3189{
3190 if (oap->op_type == OP_NOP)
3191 return false;
3192 clearopbeep(oap);
3193 return true;
3194}
3195
3196/*
3197 * Check for operator or Visual active. Clear active operator.
3198 *
3199 * Return true if operator or Visual was active.
3200 */
3201static bool checkclearopq(oparg_T *oap)
3202{
3203 if (oap->op_type == OP_NOP
3204 && !VIsual_active
3205 )
3206 return false;
3207 clearopbeep(oap);
3208 return true;
3209}
3210
3211static void clearop(oparg_T *oap)
3212{
3213 oap->op_type = OP_NOP;
3214 oap->regname = 0;
3215 oap->motion_force = NUL;
3216 oap->use_reg_one = false;
3217}
3218
3219static void clearopbeep(oparg_T *oap)
3220{
3221 clearop(oap);
3222 beep_flush();
3223}
3224
3225/*
3226 * Remove the shift modifier from a special key.
3227 */
3228static void unshift_special(cmdarg_T *cap)
3229{
3230 switch (cap->cmdchar) {
3231 case K_S_RIGHT: cap->cmdchar = K_RIGHT; break;
3232 case K_S_LEFT: cap->cmdchar = K_LEFT; break;
3233 case K_S_UP: cap->cmdchar = K_UP; break;
3234 case K_S_DOWN: cap->cmdchar = K_DOWN; break;
3235 case K_S_HOME: cap->cmdchar = K_HOME; break;
3236 case K_S_END: cap->cmdchar = K_END; break;
3237 }
3238 cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
3239}
3240
3241/// If the mode is currently displayed clear the command line or update the
3242/// command displayed.
3243static void may_clear_cmdline(void)
3244{
3245 if (mode_displayed) {
3246 // unshow visual mode later
3247 clear_cmdline = true;
3248 } else {
3249 clear_showcmd();
3250 }
3251}
3252
3253// Routines for displaying a partly typed command
3254# define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
3255static char_u showcmd_buf[SHOWCMD_BUFLEN];
3256static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; /* For push_showcmd() */
3257static bool showcmd_is_clear = true;
3258static bool showcmd_visual = false;
3259
3260
3261void clear_showcmd(void)
3262{
3263 if (!p_sc)
3264 return;
3265
3266 if (VIsual_active && !char_avail()) {
3267 int cursor_bot = lt(VIsual, curwin->w_cursor);
3268 long lines;
3269 colnr_T leftcol, rightcol;
3270 linenr_T top, bot;
3271
3272 /* Show the size of the Visual area. */
3273 if (cursor_bot) {
3274 top = VIsual.lnum;
3275 bot = curwin->w_cursor.lnum;
3276 } else {
3277 top = curwin->w_cursor.lnum;
3278 bot = VIsual.lnum;
3279 }
3280 // Include closed folds as a whole.
3281 (void)hasFolding(top, &top, NULL);
3282 (void)hasFolding(bot, NULL, &bot);
3283 lines = bot - top + 1;
3284
3285 if (VIsual_mode == Ctrl_V) {
3286 char_u *saved_sbr = p_sbr;
3287
3288 /* Make 'sbr' empty for a moment to get the correct size. */
3289 p_sbr = empty_option;
3290 getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
3291 p_sbr = saved_sbr;
3292 snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64,
3293 (int64_t)lines, (int64_t)rightcol - leftcol + 1);
3294 } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum) {
3295 snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines);
3296 } else {
3297 char_u *s, *e;
3298 int l;
3299 int bytes = 0;
3300 int chars = 0;
3301
3302 if (cursor_bot) {
3303 s = ml_get_pos(&VIsual);
3304 e = get_cursor_pos_ptr();
3305 } else {
3306 s = get_cursor_pos_ptr();
3307 e = ml_get_pos(&VIsual);
3308 }
3309 while ((*p_sel != 'e') ? s <= e : s < e) {
3310 l = (*mb_ptr2len)(s);
3311 if (l == 0) {
3312 ++bytes;
3313 ++chars;
3314 break; /* end of line */
3315 }
3316 bytes += l;
3317 ++chars;
3318 s += l;
3319 }
3320 if (bytes == chars)
3321 sprintf((char *)showcmd_buf, "%d", chars);
3322 else
3323 sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
3324 }
3325 int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
3326 showcmd_buf[limit] = NUL; // truncate
3327 showcmd_visual = true;
3328 } else {
3329 showcmd_buf[0] = NUL;
3330 showcmd_visual = false;
3331
3332 /* Don't actually display something if there is nothing to clear. */
3333 if (showcmd_is_clear)
3334 return;
3335 }
3336
3337 display_showcmd();
3338}
3339
3340/*
3341 * Add 'c' to string of shown command chars.
3342 * Return true if output has been written (and setcursor() has been called).
3343 */
3344bool add_to_showcmd(int c)
3345{
3346 char_u *p;
3347 int i;
3348 static int ignore[] =
3349 {
3350 K_IGNORE,
3351 K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE,
3352 K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
3353 K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
3354 K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
3355 K_X1MOUSE, K_X1DRAG, K_X1RELEASE, K_X2MOUSE, K_X2DRAG, K_X2RELEASE,
3356 K_EVENT,
3357 0
3358 };
3359
3360 if (!p_sc || msg_silent != 0)
3361 return false;
3362
3363 if (showcmd_visual) {
3364 showcmd_buf[0] = NUL;
3365 showcmd_visual = false;
3366 }
3367
3368 /* Ignore keys that are scrollbar updates and mouse clicks */
3369 if (IS_SPECIAL(c))
3370 for (i = 0; ignore[i] != 0; ++i)
3371 if (ignore[i] == c)
3372 return false;
3373
3374 p = transchar(c);
3375 if (*p == ' ')
3376 STRCPY(p, "<20>");
3377 size_t old_len = STRLEN(showcmd_buf);
3378 size_t extra_len = STRLEN(p);
3379 size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
3380 if (old_len + extra_len > limit) {
3381 size_t overflow = old_len + extra_len - limit;
3382 memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
3383 }
3384 STRCAT(showcmd_buf, p);
3385
3386 if (char_avail())
3387 return false;
3388
3389 display_showcmd();
3390
3391 return true;
3392}
3393
3394void add_to_showcmd_c(int c)
3395{
3396 add_to_showcmd(c);
3397 setcursor();
3398}
3399
3400/*
3401 * Delete 'len' characters from the end of the shown command.
3402 */
3403static void del_from_showcmd(int len)
3404{
3405 int old_len;
3406
3407 if (!p_sc)
3408 return;
3409
3410 old_len = (int)STRLEN(showcmd_buf);
3411 if (len > old_len)
3412 len = old_len;
3413 showcmd_buf[old_len - len] = NUL;
3414
3415 if (!char_avail())
3416 display_showcmd();
3417}
3418
3419/*
3420 * push_showcmd() and pop_showcmd() are used when waiting for the user to type
3421 * something and there is a partial mapping.
3422 */
3423void push_showcmd(void)
3424{
3425 if (p_sc)
3426 STRCPY(old_showcmd_buf, showcmd_buf);
3427}
3428
3429void pop_showcmd(void)
3430{
3431 if (!p_sc)
3432 return;
3433
3434 STRCPY(showcmd_buf, old_showcmd_buf);
3435
3436 display_showcmd();
3437}
3438
3439static void display_showcmd(void)
3440{
3441 int len;
3442 len = (int)STRLEN(showcmd_buf);
3443 showcmd_is_clear = (len == 0);
3444
3445 if (ui_has(kUIMessages)) {
3446 Array content = ARRAY_DICT_INIT;
3447 if (len > 0) {
3448 Array chunk = ARRAY_DICT_INIT;
3449 // placeholder for future highlight support
3450 ADD(chunk, INTEGER_OBJ(0));
3451 ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf)));
3452 ADD(content, ARRAY_OBJ(chunk));
3453 }
3454 ui_call_msg_showcmd(content);
3455 return;
3456 }
3457
3458 msg_grid_validate();
3459 int showcmd_row = Rows - 1;
3460 grid_puts_line_start(&msg_grid_adj, showcmd_row);
3461
3462 if (!showcmd_is_clear) {
3463 grid_puts(&msg_grid_adj, showcmd_buf, showcmd_row, sc_col,
3464 HL_ATTR(HLF_MSG));
3465 }
3466
3467 // clear the rest of an old message by outputting up to SHOWCMD_COLS spaces
3468 grid_puts(&msg_grid_adj, (char_u *)" " + len, showcmd_row,
3469 sc_col + len, HL_ATTR(HLF_MSG));
3470
3471 grid_puts_line_flush(false);
3472}
3473
3474/*
3475 * When "check" is false, prepare for commands that scroll the window.
3476 * When "check" is true, take care of scroll-binding after the window has
3477 * scrolled. Called from normal_cmd() and edit().
3478 */
3479void do_check_scrollbind(bool check)
3480{
3481 static win_T *old_curwin = NULL;
3482 static linenr_T old_topline = 0;
3483 static int old_topfill = 0;
3484 static buf_T *old_buf = NULL;
3485 static colnr_T old_leftcol = 0;
3486
3487 if (check && curwin->w_p_scb) {
3488 /* If a ":syncbind" command was just used, don't scroll, only reset
3489 * the values. */
3490 if (did_syncbind)
3491 did_syncbind = false;
3492 else if (curwin == old_curwin) {
3493 /*
3494 * Synchronize other windows, as necessary according to
3495 * 'scrollbind'. Don't do this after an ":edit" command, except
3496 * when 'diff' is set.
3497 */
3498 if ((curwin->w_buffer == old_buf
3499 || curwin->w_p_diff
3500 )
3501 && (curwin->w_topline != old_topline
3502 || curwin->w_topfill != old_topfill
3503 || curwin->w_leftcol != old_leftcol)) {
3504 check_scrollbind(curwin->w_topline - old_topline,
3505 (long)(curwin->w_leftcol - old_leftcol));
3506 }
3507 } else if (vim_strchr(p_sbo, 'j')) { /* jump flag set in 'scrollopt' */
3508 /*
3509 * When switching between windows, make sure that the relative
3510 * vertical offset is valid for the new window. The relative
3511 * offset is invalid whenever another 'scrollbind' window has
3512 * scrolled to a point that would force the current window to
3513 * scroll past the beginning or end of its buffer. When the
3514 * resync is performed, some of the other 'scrollbind' windows may
3515 * need to jump so that the current window's relative position is
3516 * visible on-screen.
3517 */
3518 check_scrollbind(curwin->w_topline - curwin->w_scbind_pos, 0L);
3519 }
3520 curwin->w_scbind_pos = curwin->w_topline;
3521 }
3522
3523 old_curwin = curwin;
3524 old_topline = curwin->w_topline;
3525 old_topfill = curwin->w_topfill;
3526 old_buf = curwin->w_buffer;
3527 old_leftcol = curwin->w_leftcol;
3528}
3529
3530/*
3531 * Synchronize any windows that have "scrollbind" set, based on the
3532 * number of rows by which the current window has changed
3533 * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
3534 */
3535void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
3536{
3537 bool want_ver;
3538 bool want_hor;
3539 win_T *old_curwin = curwin;
3540 buf_T *old_curbuf = curbuf;
3541 int old_VIsual_select = VIsual_select;
3542 int old_VIsual_active = VIsual_active;
3543 colnr_T tgt_leftcol = curwin->w_leftcol;
3544 long topline;
3545 long y;
3546
3547 /*
3548 * check 'scrollopt' string for vertical and horizontal scroll options
3549 */
3550 want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0);
3551 want_ver |= old_curwin->w_p_diff;
3552 want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0));
3553
3554 /*
3555 * loop through the scrollbound windows and scroll accordingly
3556 */
3557 VIsual_select = VIsual_active = 0;
3558 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
3559 curwin = wp;
3560 curbuf = curwin->w_buffer;
3561 /* skip original window and windows with 'noscrollbind' */
3562 if (curwin == old_curwin || !curwin->w_p_scb) {
3563 continue;
3564 }
3565 /*
3566 * do the vertical scroll
3567 */
3568 if (want_ver) {
3569 if (old_curwin->w_p_diff && curwin->w_p_diff) {
3570 diff_set_topline(old_curwin, curwin);
3571 } else {
3572 curwin->w_scbind_pos += topline_diff;
3573 topline = curwin->w_scbind_pos;
3574 if (topline > curbuf->b_ml.ml_line_count)
3575 topline = curbuf->b_ml.ml_line_count;
3576 if (topline < 1)
3577 topline = 1;
3578
3579 y = topline - curwin->w_topline;
3580 if (y > 0)
3581 scrollup(y, false);
3582 else
3583 scrolldown(-y, false);
3584 }
3585
3586 redraw_later(VALID);
3587 cursor_correct();
3588 curwin->w_redr_status = true;
3589 }
3590
3591 /*
3592 * do the horizontal scroll
3593 */
3594 if (want_hor && curwin->w_leftcol != tgt_leftcol) {
3595 curwin->w_leftcol = tgt_leftcol;
3596 leftcol_changed();
3597 }
3598 }
3599
3600 /*
3601 * reset current-window
3602 */
3603 VIsual_select = old_VIsual_select;
3604 VIsual_active = old_VIsual_active;
3605 curwin = old_curwin;
3606 curbuf = old_curbuf;
3607}
3608
3609/*
3610 * Command character that's ignored.
3611 * Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
3612 * xon/xoff.
3613 */
3614static void nv_ignore(cmdarg_T *cap)
3615{
3616 cap->retval |= CA_COMMAND_BUSY; /* don't call edit() now */
3617}
3618
3619/*
3620 * Command character that doesn't do anything, but unlike nv_ignore() does
3621 * start edit(). Used for "startinsert" executed while starting up.
3622 */
3623static void nv_nop(cmdarg_T *cap)
3624{
3625}
3626
3627/*
3628 * Command character doesn't exist.
3629 */
3630static void nv_error(cmdarg_T *cap)
3631{
3632 clearopbeep(cap->oap);
3633}
3634
3635/*
3636 * <Help> and <F1> commands.
3637 */
3638static void nv_help(cmdarg_T *cap)
3639{
3640 if (!checkclearopq(cap->oap))
3641 ex_help(NULL);
3642}
3643
3644/*
3645 * CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
3646 */
3647static void nv_addsub(cmdarg_T *cap)
3648{
3649 if (!VIsual_active && cap->oap->op_type == OP_NOP) {
3650 prep_redo_cmd(cap);
3651 cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
3652 op_addsub(cap->oap, cap->count1, cap->arg);
3653 cap->oap->op_type = OP_NOP;
3654 } else if (VIsual_active) {
3655 nv_operator(cap);
3656 } else {
3657 clearop(cap->oap);
3658 }
3659}
3660
3661/*
3662 * CTRL-F, CTRL-B, etc: Scroll page up or down.
3663 */
3664static void nv_page(cmdarg_T *cap)
3665{
3666 if (!checkclearop(cap->oap)) {
3667 if (mod_mask & MOD_MASK_CTRL) {
3668 /* <C-PageUp>: tab page back; <C-PageDown>: tab page forward */
3669 if (cap->arg == BACKWARD)
3670 goto_tabpage(-(int)cap->count1);
3671 else
3672 goto_tabpage((int)cap->count0);
3673 } else
3674 (void)onepage(cap->arg, cap->count1);
3675 }
3676}
3677
3678/*
3679 * Implementation of "gd" and "gD" command.
3680 */
3681static void
3682nv_gd (
3683 oparg_T *oap,
3684 int nchar,
3685 int thisblock /* 1 for "1gd" and "1gD" */
3686)
3687{
3688 size_t len;
3689 char_u *ptr;
3690 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
3691 || !find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)) {
3692 clearopbeep(oap);
3693 } else if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) {
3694 foldOpenCursor();
3695 }
3696}
3697
3698// Return true if line[offset] is not inside a C-style comment or string, false
3699// otherwise.
3700static bool is_ident(char_u *line, int offset)
3701{
3702 bool incomment = false;
3703 int instring = 0;
3704 int prev = 0;
3705
3706 for (int i = 0; i < offset && line[i] != NUL; i++) {
3707 if (instring != 0) {
3708 if (prev != '\\' && line[i] == instring) {
3709 instring = 0;
3710 }
3711 } else if ((line[i] == '"' || line[i] == '\'') && !incomment) {
3712 instring = line[i];
3713 } else {
3714 if (incomment) {
3715 if (prev == '*' && line[i] == '/') {
3716 incomment = false;
3717 }
3718 } else if (prev == '/' && line[i] == '*') {
3719 incomment = true;
3720 } else if (prev == '/' && line[i] == '/') {
3721 return false;
3722 }
3723 }
3724
3725 prev = line[i];
3726 }
3727
3728 return incomment == false && instring == 0;
3729}
3730
3731/*
3732 * Search for variable declaration of "ptr[len]".
3733 * When "locally" is true in the current function ("gd"), otherwise in the
3734 * current file ("gD").
3735 * When "thisblock" is true check the {} block scope.
3736 * Return fail when not found.
3737 */
3738bool
3739find_decl (
3740 char_u *ptr,
3741 size_t len,
3742 bool locally,
3743 bool thisblock,
3744 int flags_arg // flags passed to searchit()
3745)
3746{
3747 char_u *pat;
3748 pos_T old_pos;
3749 pos_T par_pos;
3750 pos_T found_pos;
3751 bool t;
3752 bool save_p_ws;
3753 bool save_p_scs;
3754 bool retval = true;
3755 bool incll;
3756 int searchflags = flags_arg;
3757 bool valid;
3758
3759 pat = xmalloc(len + 7);
3760
3761 /* Put "\V" before the pattern to avoid that the special meaning of "."
3762 * and "~" causes trouble. */
3763 assert(len <= INT_MAX);
3764 sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
3765 (int)len, ptr);
3766 old_pos = curwin->w_cursor;
3767 save_p_ws = p_ws;
3768 save_p_scs = p_scs;
3769 p_ws = false; /* don't wrap around end of file now */
3770 p_scs = false; /* don't switch ignorecase off now */
3771
3772 /*
3773 * With "gD" go to line 1.
3774 * With "gd" Search back for the start of the current function, then go
3775 * back until a blank line. If this fails go to line 1.
3776 */
3777 if (!locally || !findpar(&incll, BACKWARD, 1L, '{', false)) {
3778 setpcmark(); /* Set in findpar() otherwise */
3779 curwin->w_cursor.lnum = 1;
3780 par_pos = curwin->w_cursor;
3781 } else {
3782 par_pos = curwin->w_cursor;
3783 while (curwin->w_cursor.lnum > 1
3784 && *skipwhite(get_cursor_line_ptr()) != NUL)
3785 --curwin->w_cursor.lnum;
3786 }
3787 curwin->w_cursor.col = 0;
3788
3789 /* Search forward for the identifier, ignore comment lines. */
3790 clearpos(&found_pos);
3791 for (;; ) {
3792 valid = false;
3793 (void)valid; // Avoid "dead assignment" warning.
3794 t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
3795 pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL, NULL);
3796 if (curwin->w_cursor.lnum >= old_pos.lnum) {
3797 t = false; // match after start is failure too
3798 }
3799
3800 if (thisblock && t != false) {
3801 const int64_t maxtravel = old_pos.lnum - curwin->w_cursor.lnum + 1;
3802 const pos_T *pos = findmatchlimit(NULL, '}', FM_FORWARD, maxtravel);
3803
3804 // Check that the block the match is in doesn't end before the
3805 // position where we started the search from.
3806 if (pos != NULL && pos->lnum < old_pos.lnum) {
3807 // There can't be a useful match before the end of this block.
3808 // Skip to the end
3809 curwin->w_cursor = *pos;
3810 continue;
3811 }
3812 }
3813
3814 if (t == false) {
3815 /* If we previously found a valid position, use it. */
3816 if (found_pos.lnum != 0) {
3817 curwin->w_cursor = found_pos;
3818 t = true;
3819 }
3820 break;
3821 }
3822 if (get_leader_len(get_cursor_line_ptr(), NULL, false, true) > 0) {
3823 /* Ignore this line, continue at start of next line. */
3824 ++curwin->w_cursor.lnum;
3825 curwin->w_cursor.col = 0;
3826 continue;
3827 }
3828 valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col);
3829
3830 // If the current position is not a valid identifier and a previous match is
3831 // present, favor that one instead.
3832 if (!valid && found_pos.lnum != 0) {
3833 curwin->w_cursor = found_pos;
3834 break;
3835 }
3836 // global search: use first match found
3837 if (valid && !locally) {
3838 break;
3839 }
3840 if (valid && curwin->w_cursor.lnum >= par_pos.lnum) {
3841 // If we previously found a valid position, use it.
3842 if (found_pos.lnum != 0) {
3843 curwin->w_cursor = found_pos;
3844 }
3845 break;
3846 }
3847
3848 // For finding a local variable and the match is before the "{" or
3849 // inside a comment, continue searching. For K&R style function
3850 // declarations this skips the function header without types.
3851 if (!valid) {
3852 clearpos(&found_pos);
3853 } else {
3854 found_pos = curwin->w_cursor;
3855 }
3856 // Remove SEARCH_START from flags to avoid getting stuck at one position.
3857 searchflags &= ~SEARCH_START;
3858 }
3859
3860 if (t == false) {
3861 retval = false;
3862 curwin->w_cursor = old_pos;
3863 } else {
3864 curwin->w_set_curswant = true;
3865 /* "n" searches forward now */
3866 reset_search_dir();
3867 }
3868
3869 xfree(pat);
3870 p_ws = save_p_ws;
3871 p_scs = save_p_scs;
3872
3873 return retval;
3874}
3875
3876/*
3877 * Move 'dist' lines in direction 'dir', counting lines by *screen*
3878 * lines rather than lines in the file.
3879 * 'dist' must be positive.
3880 *
3881 * Return true if able to move cursor, false otherwise.
3882 */
3883static bool nv_screengo(oparg_T *oap, int dir, long dist)
3884{
3885 int linelen = linetabsize(get_cursor_line_ptr());
3886 bool retval = true;
3887 bool atend = false;
3888 int n;
3889 int col_off1; /* margin offset for first screen line */
3890 int col_off2; /* margin offset for wrapped screen line */
3891 int width1; /* text width for first screen line */
3892 int width2; /* test width for wrapped screen line */
3893
3894 oap->motion_type = kMTCharWise;
3895 oap->inclusive = (curwin->w_curswant == MAXCOL);
3896
3897 col_off1 = curwin_col_off();
3898 col_off2 = col_off1 - curwin_col_off2();
3899 width1 = curwin->w_width_inner - col_off1;
3900 width2 = curwin->w_width_inner - col_off2;
3901
3902 if (width2 == 0) {
3903 width2 = 1; // Avoid divide by zero.
3904 }
3905
3906 if (curwin->w_width_inner != 0) {
3907 // Instead of sticking at the last character of the buffer line we
3908 // try to stick in the last column of the screen.
3909 if (curwin->w_curswant == MAXCOL) {
3910 atend = true;
3911 validate_virtcol();
3912 if (width1 <= 0)
3913 curwin->w_curswant = 0;
3914 else {
3915 curwin->w_curswant = width1 - 1;
3916 if (curwin->w_virtcol > curwin->w_curswant)
3917 curwin->w_curswant += ((curwin->w_virtcol
3918 - curwin->w_curswant -
3919 1) / width2 + 1) * width2;
3920 }
3921 } else {
3922 if (linelen > width1)
3923 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
3924 else
3925 n = width1;
3926 if (curwin->w_curswant > (colnr_T)n + 1)
3927 curwin->w_curswant -= ((curwin->w_curswant - n) / width2 + 1)
3928 * width2;
3929 }
3930
3931 while (dist--) {
3932 if (dir == BACKWARD) {
3933 if ((long)curwin->w_curswant >= width2)
3934 /* move back within line */
3935 curwin->w_curswant -= width2;
3936 else {
3937 /* to previous line */
3938 if (curwin->w_cursor.lnum == 1) {
3939 retval = false;
3940 break;
3941 }
3942 --curwin->w_cursor.lnum;
3943 /* Move to the start of a closed fold. Don't do that when
3944 * 'foldopen' contains "all": it will open in a moment. */
3945 if (!(fdo_flags & FDO_ALL))
3946 (void)hasFolding(curwin->w_cursor.lnum,
3947 &curwin->w_cursor.lnum, NULL);
3948 linelen = linetabsize(get_cursor_line_ptr());
3949 if (linelen > width1) {
3950 int w = (((linelen - width1 - 1) / width2) + 1) * width2;
3951 assert(curwin->w_curswant <= INT_MAX - w);
3952 curwin->w_curswant += w;
3953 }
3954 }
3955 } else { /* dir == FORWARD */
3956 if (linelen > width1)
3957 n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
3958 else
3959 n = width1;
3960 if (curwin->w_curswant + width2 < (colnr_T)n)
3961 /* move forward within line */
3962 curwin->w_curswant += width2;
3963 else {
3964 /* to next line */
3965 /* Move to the end of a closed fold. */
3966 (void)hasFolding(curwin->w_cursor.lnum, NULL,
3967 &curwin->w_cursor.lnum);
3968 if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
3969 retval = false;
3970 break;
3971 }
3972 curwin->w_cursor.lnum++;
3973 curwin->w_curswant %= width2;
3974 linelen = linetabsize(get_cursor_line_ptr());
3975 }
3976 }
3977 }
3978 }
3979
3980 if (virtual_active() && atend)
3981 coladvance(MAXCOL);
3982 else
3983 coladvance(curwin->w_curswant);
3984
3985 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
3986 /*
3987 * Check for landing on a character that got split at the end of the
3988 * last line. We want to advance a screenline, not end up in the same
3989 * screenline or move two screenlines.
3990 */
3991 validate_virtcol();
3992 colnr_T virtcol = curwin->w_virtcol;
3993 if (virtcol > (colnr_T)width1 && *p_sbr != NUL)
3994 virtcol -= vim_strsize(p_sbr);
3995
3996 if (virtcol > curwin->w_curswant
3997 && (curwin->w_curswant < (colnr_T)width1
3998 ? (curwin->w_curswant > (colnr_T)width1 / 2)
3999 : ((curwin->w_curswant - width1) % width2
4000 > (colnr_T)width2 / 2)))
4001 --curwin->w_cursor.col;
4002 }
4003
4004 if (atend)
4005 curwin->w_curswant = MAXCOL; /* stick in the last column */
4006
4007 return retval;
4008}
4009
4010/*
4011 * Mouse scroll wheel: Default action is to scroll three lines, or one page
4012 * when Shift or Ctrl is used.
4013 * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
4014 * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
4015 */
4016static void nv_mousescroll(cmdarg_T *cap)
4017{
4018 win_T *old_curwin = curwin;
4019
4020 if (mouse_row >= 0 && mouse_col >= 0) {
4021 int grid, row, col;
4022
4023 grid = mouse_grid;
4024 row = mouse_row;
4025 col = mouse_col;
4026
4027 // find the window at the pointer coordinates
4028 win_T *wp = mouse_find_win(&grid, &row, &col);
4029 if (wp == NULL) {
4030 return;
4031 }
4032 curwin = wp;
4033 curbuf = curwin->w_buffer;
4034 }
4035
4036 if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN) {
4037 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
4038 (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
4039 } else {
4040 cap->count1 = 3;
4041 cap->count0 = 3;
4042 nv_scroll_line(cap);
4043 }
4044 } else {
4045 mouse_scroll_horiz(cap->arg);
4046 }
4047 if (curwin != old_curwin && curwin->w_p_cul) {
4048 redraw_for_cursorline(curwin);
4049 }
4050
4051 curwin->w_redr_status = true;
4052
4053 curwin = old_curwin;
4054 curbuf = curwin->w_buffer;
4055}
4056
4057/*
4058 * Mouse clicks and drags.
4059 */
4060static void nv_mouse(cmdarg_T *cap)
4061{
4062 (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
4063}
4064
4065/*
4066 * Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
4067 * cap->arg must be true for CTRL-E.
4068 */
4069static void nv_scroll_line(cmdarg_T *cap)
4070{
4071 if (!checkclearop(cap->oap))
4072 scroll_redraw(cap->arg, cap->count1);
4073}
4074
4075/*
4076 * Scroll "count" lines up or down, and redraw.
4077 */
4078void scroll_redraw(int up, long count)
4079{
4080 linenr_T prev_topline = curwin->w_topline;
4081 int prev_topfill = curwin->w_topfill;
4082 linenr_T prev_lnum = curwin->w_cursor.lnum;
4083
4084 if (up)
4085 scrollup(count, true);
4086 else
4087 scrolldown(count, true);
4088 if (p_so) {
4089 /* Adjust the cursor position for 'scrolloff'. Mark w_topline as
4090 * valid, otherwise the screen jumps back at the end of the file. */
4091 cursor_correct();
4092 check_cursor_moved(curwin);
4093 curwin->w_valid |= VALID_TOPLINE;
4094
4095 /* If moved back to where we were, at least move the cursor, otherwise
4096 * we get stuck at one position. Don't move the cursor up if the
4097 * first line of the buffer is already on the screen */
4098 while (curwin->w_topline == prev_topline
4099 && curwin->w_topfill == prev_topfill
4100 ) {
4101 if (up) {
4102 if (curwin->w_cursor.lnum > prev_lnum
4103 || cursor_down(1L, false) == false)
4104 break;
4105 } else {
4106 if (curwin->w_cursor.lnum < prev_lnum
4107 || prev_topline == 1L
4108 || cursor_up(1L, false) == false)
4109 break;
4110 }
4111 /* Mark w_topline as valid, otherwise the screen jumps back at the
4112 * end of the file. */
4113 check_cursor_moved(curwin);
4114 curwin->w_valid |= VALID_TOPLINE;
4115 }
4116 }
4117 if (curwin->w_cursor.lnum != prev_lnum)
4118 coladvance(curwin->w_curswant);
4119 redraw_later(VALID);
4120}
4121
4122/*
4123 * Commands that start with "z".
4124 */
4125static void nv_zet(cmdarg_T *cap)
4126{
4127 int n;
4128 colnr_T col;
4129 int nchar = cap->nchar;
4130 long old_fdl = curwin->w_p_fdl;
4131 int old_fen = curwin->w_p_fen;
4132 bool undo = false;
4133
4134 assert(p_siso <= INT_MAX);
4135 int l_p_siso = (int)p_siso;
4136
4137 if (ascii_isdigit(nchar)) {
4138 /*
4139 * "z123{nchar}": edit the count before obtaining {nchar}
4140 */
4141 if (checkclearop(cap->oap))
4142 return;
4143 n = nchar - '0';
4144 for (;; ) {
4145 no_mapping++;
4146 nchar = plain_vgetc();
4147 LANGMAP_ADJUST(nchar, true);
4148 no_mapping--;
4149 (void)add_to_showcmd(nchar);
4150 if (nchar == K_DEL || nchar == K_KDEL)
4151 n /= 10;
4152 else if (ascii_isdigit(nchar))
4153 n = n * 10 + (nchar - '0');
4154 else if (nchar == CAR) {
4155 win_setheight(n);
4156 break;
4157 } else if (nchar == 'l'
4158 || nchar == 'h'
4159 || nchar == K_LEFT
4160 || nchar == K_RIGHT) {
4161 cap->count1 = n ? n * cap->count1 : cap->count1;
4162 goto dozet;
4163 } else {
4164 clearopbeep(cap->oap);
4165 break;
4166 }
4167 }
4168 cap->oap->op_type = OP_NOP;
4169 return;
4170 }
4171
4172dozet:
4173 // "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
4174 // and "zC" only in Visual mode. "zj" and "zk" are motion
4175 // commands. */
4176 if (cap->nchar != 'f' && cap->nchar != 'F'
4177 && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
4178 && cap->nchar != 'j' && cap->nchar != 'k'
4179 && checkclearop(cap->oap)) {
4180 return;
4181 }
4182
4183 /*
4184 * For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
4185 * If line number given, set cursor.
4186 */
4187 if ((vim_strchr((char_u *)"+\r\nt.z^-b", nchar) != NULL)
4188 && cap->count0
4189 && cap->count0 != curwin->w_cursor.lnum) {
4190 setpcmark();
4191 if (cap->count0 > curbuf->b_ml.ml_line_count)
4192 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
4193 else
4194 curwin->w_cursor.lnum = cap->count0;
4195 check_cursor_col();
4196 }
4197
4198 switch (nchar) {
4199 /* "z+", "z<CR>" and "zt": put cursor at top of screen */
4200 case '+':
4201 if (cap->count0 == 0) {
4202 /* No count given: put cursor at the line below screen */
4203 validate_botline(); /* make sure w_botline is valid */
4204 if (curwin->w_botline > curbuf->b_ml.ml_line_count)
4205 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
4206 else
4207 curwin->w_cursor.lnum = curwin->w_botline;
4208 }
4209 FALLTHROUGH;
4210 case NL:
4211 case CAR:
4212 case K_KENTER:
4213 beginline(BL_WHITE | BL_FIX);
4214 FALLTHROUGH;
4215
4216 case 't': scroll_cursor_top(0, true);
4217 redraw_later(VALID);
4218 set_fraction(curwin);
4219 break;
4220
4221 /* "z." and "zz": put cursor in middle of screen */
4222 case '.': beginline(BL_WHITE | BL_FIX);
4223 FALLTHROUGH;
4224
4225 case 'z': scroll_cursor_halfway(true);
4226 redraw_later(VALID);
4227 set_fraction(curwin);
4228 break;
4229
4230 // "z^", "z-" and "zb": put cursor at bottom of screen
4231 case '^': // Strange Vi behavior: <count>z^ finds line at top of window
4232 // when <count> is at bottom of window, and puts that one at
4233 // bottom of window.
4234 if (cap->count0 != 0) {
4235 scroll_cursor_bot(0, true);
4236 curwin->w_cursor.lnum = curwin->w_topline;
4237 } else if (curwin->w_topline == 1)
4238 curwin->w_cursor.lnum = 1;
4239 else
4240 curwin->w_cursor.lnum = curwin->w_topline - 1;
4241 FALLTHROUGH;
4242 case '-':
4243 beginline(BL_WHITE | BL_FIX);
4244 FALLTHROUGH;
4245
4246 case 'b': scroll_cursor_bot(0, true);
4247 redraw_later(VALID);
4248 set_fraction(curwin);
4249 break;
4250
4251 /* "zH" - scroll screen right half-page */
4252 case 'H':
4253 cap->count1 *= curwin->w_width_inner / 2;
4254 FALLTHROUGH;
4255
4256 /* "zh" - scroll screen to the right */
4257 case 'h':
4258 case K_LEFT:
4259 if (!curwin->w_p_wrap) {
4260 if ((colnr_T)cap->count1 > curwin->w_leftcol)
4261 curwin->w_leftcol = 0;
4262 else
4263 curwin->w_leftcol -= (colnr_T)cap->count1;
4264 leftcol_changed();
4265 }
4266 break;
4267
4268 // "zL" - scroll screen left half-page
4269 case 'L': cap->count1 *= curwin->w_width_inner / 2;
4270 FALLTHROUGH;
4271
4272 /* "zl" - scroll screen to the left */
4273 case 'l':
4274 case K_RIGHT:
4275 if (!curwin->w_p_wrap) {
4276 /* scroll the window left */
4277 curwin->w_leftcol += (colnr_T)cap->count1;
4278 leftcol_changed();
4279 }
4280 break;
4281
4282 /* "zs" - scroll screen, cursor at the start */
4283 case 's': if (!curwin->w_p_wrap) {
4284 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
4285 col = 0; /* like the cursor is in col 0 */
4286 else
4287 getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
4288 if (col > l_p_siso)
4289 col -= l_p_siso;
4290 else
4291 col = 0;
4292 if (curwin->w_leftcol != col) {
4293 curwin->w_leftcol = col;
4294 redraw_later(NOT_VALID);
4295 }
4296 }
4297 break;
4298
4299 /* "ze" - scroll screen, cursor at the end */
4300 case 'e': if (!curwin->w_p_wrap) {
4301 if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
4302 col = 0; /* like the cursor is in col 0 */
4303 else
4304 getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
4305 n = curwin->w_width_inner - curwin_col_off();
4306 if (col + l_p_siso < n) {
4307 col = 0;
4308 } else {
4309 col = col + l_p_siso - n + 1;
4310 }
4311 if (curwin->w_leftcol != col) {
4312 curwin->w_leftcol = col;
4313 redraw_later(NOT_VALID);
4314 }
4315 }
4316 break;
4317
4318 /* "zF": create fold command */
4319 /* "zf": create fold operator */
4320 case 'F':
4321 case 'f': if (foldManualAllowed(true)) {
4322 cap->nchar = 'f';
4323 nv_operator(cap);
4324 curwin->w_p_fen = true;
4325
4326 /* "zF" is like "zfzf" */
4327 if (nchar == 'F' && cap->oap->op_type == OP_FOLD) {
4328 nv_operator(cap);
4329 finish_op = true;
4330 }
4331 } else
4332 clearopbeep(cap->oap);
4333 break;
4334
4335 /* "zd": delete fold at cursor */
4336 /* "zD": delete fold at cursor recursively */
4337 case 'd':
4338 case 'D': if (foldManualAllowed(false)) {
4339 if (VIsual_active)
4340 nv_operator(cap);
4341 else
4342 deleteFold(curwin->w_cursor.lnum,
4343 curwin->w_cursor.lnum, nchar == 'D', false);
4344 }
4345 break;
4346
4347 /* "zE": erase all folds */
4348 case 'E': if (foldmethodIsManual(curwin)) {
4349 clearFolding(curwin);
4350 changed_window_setting();
4351 } else if (foldmethodIsMarker(curwin))
4352 deleteFold((linenr_T)1, curbuf->b_ml.ml_line_count,
4353 true, false);
4354 else
4355 EMSG(_("E352: Cannot erase folds with current 'foldmethod'"));
4356 break;
4357
4358 /* "zn": fold none: reset 'foldenable' */
4359 case 'n': curwin->w_p_fen = false;
4360 break;
4361
4362 /* "zN": fold Normal: set 'foldenable' */
4363 case 'N': curwin->w_p_fen = true;
4364 break;
4365
4366 /* "zi": invert folding: toggle 'foldenable' */
4367 case 'i': curwin->w_p_fen = !curwin->w_p_fen;
4368 break;
4369
4370 /* "za": open closed fold or close open fold at cursor */
4371 case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
4372 openFold(curwin->w_cursor.lnum, cap->count1);
4373 else {
4374 closeFold(curwin->w_cursor.lnum, cap->count1);
4375 curwin->w_p_fen = true;
4376 }
4377 break;
4378
4379 /* "zA": open fold at cursor recursively */
4380 case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL))
4381 openFoldRecurse(curwin->w_cursor.lnum);
4382 else {
4383 closeFoldRecurse(curwin->w_cursor.lnum);
4384 curwin->w_p_fen = true;
4385 }
4386 break;
4387
4388 /* "zo": open fold at cursor or Visual area */
4389 case 'o': if (VIsual_active)
4390 nv_operator(cap);
4391 else
4392 openFold(curwin->w_cursor.lnum, cap->count1);
4393 break;
4394
4395 /* "zO": open fold recursively */
4396 case 'O': if (VIsual_active)
4397 nv_operator(cap);
4398 else
4399 openFoldRecurse(curwin->w_cursor.lnum);
4400 break;
4401
4402 /* "zc": close fold at cursor or Visual area */
4403 case 'c': if (VIsual_active)
4404 nv_operator(cap);
4405 else
4406 closeFold(curwin->w_cursor.lnum, cap->count1);
4407 curwin->w_p_fen = true;
4408 break;
4409
4410 /* "zC": close fold recursively */
4411 case 'C': if (VIsual_active)
4412 nv_operator(cap);
4413 else
4414 closeFoldRecurse(curwin->w_cursor.lnum);
4415 curwin->w_p_fen = true;
4416 break;
4417
4418 /* "zv": open folds at the cursor */
4419 case 'v': foldOpenCursor();
4420 break;
4421
4422 /* "zx": re-apply 'foldlevel' and open folds at the cursor */
4423 case 'x': curwin->w_p_fen = true;
4424 curwin->w_foldinvalid = true; /* recompute folds */
4425 newFoldLevel(); /* update right now */
4426 foldOpenCursor();
4427 break;
4428
4429 /* "zX": undo manual opens/closes, re-apply 'foldlevel' */
4430 case 'X': curwin->w_p_fen = true;
4431 curwin->w_foldinvalid = true; /* recompute folds */
4432 old_fdl = -1; /* force an update */
4433 break;
4434
4435 /* "zm": fold more */
4436 case 'm':
4437 if (curwin->w_p_fdl > 0) {
4438 curwin->w_p_fdl -= cap->count1;
4439 if (curwin->w_p_fdl < 0) {
4440 curwin->w_p_fdl = 0;
4441 }
4442 }
4443 old_fdl = -1; /* force an update */
4444 curwin->w_p_fen = true;
4445 break;
4446
4447 /* "zM": close all folds */
4448 case 'M': curwin->w_p_fdl = 0;
4449 old_fdl = -1; /* force an update */
4450 curwin->w_p_fen = true;
4451 break;
4452
4453 /* "zr": reduce folding */
4454 case 'r':
4455 curwin->w_p_fdl += cap->count1;
4456 {
4457 int d = getDeepestNesting();
4458 if (curwin->w_p_fdl >= d) {
4459 curwin->w_p_fdl = d;
4460 }
4461 }
4462 break;
4463
4464 /* "zR": open all folds */
4465 case 'R': curwin->w_p_fdl = getDeepestNesting();
4466 old_fdl = -1; /* force an update */
4467 break;
4468
4469 case 'j': /* "zj" move to next fold downwards */
4470 case 'k': /* "zk" move to next fold upwards */
4471 if (foldMoveTo(true, nchar == 'j' ? FORWARD : BACKWARD,
4472 cap->count1) == false)
4473 clearopbeep(cap->oap);
4474 break;
4475
4476
4477 case 'u': // "zug" and "zuw": undo "zg" and "zw"
4478 no_mapping++;
4479 nchar = plain_vgetc();
4480 LANGMAP_ADJUST(nchar, true);
4481 no_mapping--;
4482 (void)add_to_showcmd(nchar);
4483 if (vim_strchr((char_u *)"gGwW", nchar) == NULL) {
4484 clearopbeep(cap->oap);
4485 break;
4486 }
4487 undo = true;
4488 FALLTHROUGH;
4489
4490 case 'g': /* "zg": add good word to word list */
4491 case 'w': /* "zw": add wrong word to word list */
4492 case 'G': /* "zG": add good word to temp word list */
4493 case 'W': /* "zW": add wrong word to temp word list */
4494 {
4495 char_u *ptr = NULL;
4496 size_t len;
4497
4498 if (checkclearop(cap->oap))
4499 break;
4500 if (VIsual_active && !get_visual_text(cap, &ptr, &len))
4501 return;
4502 if (ptr == NULL) {
4503 pos_T pos = curwin->w_cursor;
4504
4505 /* Find bad word under the cursor. When 'spell' is
4506 * off this fails and find_ident_under_cursor() is
4507 * used below. */
4508 emsg_off++;
4509 len = spell_move_to(curwin, FORWARD, true, true, NULL);
4510 emsg_off--;
4511 if (len != 0 && curwin->w_cursor.col <= pos.col)
4512 ptr = ml_get_pos(&curwin->w_cursor);
4513 curwin->w_cursor = pos;
4514 }
4515
4516 if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
4517 return;
4518 assert(len <= INT_MAX);
4519 spell_add_word(ptr, (int)len, nchar == 'w' || nchar == 'W',
4520 (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
4521 undo);
4522 }
4523 break;
4524
4525 case '=': /* "z=": suggestions for a badly spelled word */
4526 if (!checkclearop(cap->oap))
4527 spell_suggest((int)cap->count0);
4528 break;
4529
4530 default: clearopbeep(cap->oap);
4531 }
4532
4533 /* Redraw when 'foldenable' changed */
4534 if (old_fen != curwin->w_p_fen) {
4535 if (foldmethodIsDiff(curwin) && curwin->w_p_scb) {
4536 /* Adjust 'foldenable' in diff-synced windows. */
4537 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
4538 if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) {
4539 wp->w_p_fen = curwin->w_p_fen;
4540 changed_window_setting_win(wp);
4541 }
4542 }
4543 }
4544 changed_window_setting();
4545 }
4546
4547 /* Redraw when 'foldlevel' changed. */
4548 if (old_fdl != curwin->w_p_fdl)
4549 newFoldLevel();
4550}
4551
4552
4553
4554/*
4555 * "Q" command.
4556 */
4557static void nv_exmode(cmdarg_T *cap)
4558{
4559 /*
4560 * Ignore 'Q' in Visual mode, just give a beep.
4561 */
4562 if (VIsual_active) {
4563 vim_beep(BO_EX);
4564 } else if (!checkclearop(cap->oap)) {
4565 do_exmode(false);
4566 }
4567}
4568
4569/// Handle a ":" command and <Cmd>.
4570static void nv_colon(cmdarg_T *cap)
4571{
4572 int old_p_im;
4573 bool cmd_result;
4574 bool is_cmdkey = cap->cmdchar == K_COMMAND;
4575
4576 if (VIsual_active && !is_cmdkey) {
4577 nv_operator(cap);
4578 } else {
4579 if (cap->oap->op_type != OP_NOP) {
4580 // Using ":" as a movement is characterwise exclusive.
4581 cap->oap->motion_type = kMTCharWise;
4582 cap->oap->inclusive = false;
4583 } else if (cap->count0 && !is_cmdkey) {
4584 // translate "count:" into ":.,.+(count - 1)"
4585 stuffcharReadbuff('.');
4586 if (cap->count0 > 1) {
4587 stuffReadbuff(",.+");
4588 stuffnumReadbuff(cap->count0 - 1L);
4589 }
4590 }
4591
4592 /* When typing, don't type below an old message */
4593 if (KeyTyped)
4594 compute_cmdrow();
4595
4596 old_p_im = p_im;
4597
4598 // get a command line and execute it
4599 cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
4600 cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
4601
4602 /* If 'insertmode' changed, enter or exit Insert mode */
4603 if (p_im != old_p_im) {
4604 if (p_im)
4605 restart_edit = 'i';
4606 else
4607 restart_edit = 0;
4608 }
4609
4610 if (cmd_result == false)
4611 /* The Ex command failed, do not execute the operator. */
4612 clearop(cap->oap);
4613 else if (cap->oap->op_type != OP_NOP
4614 && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
4615 || cap->oap->start.col >
4616 (colnr_T)STRLEN(ml_get(cap->oap->start.lnum))
4617 || did_emsg
4618 ))
4619 /* The start of the operator has become invalid by the Ex command.
4620 */
4621 clearopbeep(cap->oap);
4622 }
4623}
4624
4625/*
4626 * Handle CTRL-G command.
4627 */
4628static void nv_ctrlg(cmdarg_T *cap)
4629{
4630 if (VIsual_active) { /* toggle Selection/Visual mode */
4631 VIsual_select = !VIsual_select;
4632 showmode();
4633 } else if (!checkclearop(cap->oap))
4634 /* print full name if count given or :cd used */
4635 fileinfo((int)cap->count0, false, true);
4636}
4637
4638/*
4639 * Handle CTRL-H <Backspace> command.
4640 */
4641static void nv_ctrlh(cmdarg_T *cap)
4642{
4643 if (VIsual_active && VIsual_select) {
4644 cap->cmdchar = 'x'; /* BS key behaves like 'x' in Select mode */
4645 v_visop(cap);
4646 } else
4647 nv_left(cap);
4648}
4649
4650/*
4651 * CTRL-L: clear screen and redraw.
4652 */
4653static void nv_clear(cmdarg_T *cap)
4654{
4655 if (!checkclearop(cap->oap)) {
4656 /* Clear all syntax states to force resyncing. */
4657 syn_stack_free_all(curwin->w_s);
4658 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
4659 wp->w_s->b_syn_slow = false;
4660 }
4661 redraw_later(CLEAR);
4662 }
4663}
4664
4665/*
4666 * CTRL-O: In Select mode: switch to Visual mode for one command.
4667 * Otherwise: Go to older pcmark.
4668 */
4669static void nv_ctrlo(cmdarg_T *cap)
4670{
4671 if (VIsual_active && VIsual_select) {
4672 VIsual_select = false;
4673 showmode();
4674 restart_VIsual_select = 2; /* restart Select mode later */
4675 } else {
4676 cap->count1 = -cap->count1;
4677 nv_pcmark(cap);
4678 }
4679}
4680
4681/*
4682 * CTRL-^ command, short for ":e #"
4683 */
4684static void nv_hat(cmdarg_T *cap)
4685{
4686 if (!checkclearopq(cap->oap))
4687 (void)buflist_getfile((int)cap->count0, (linenr_T)0,
4688 GETF_SETMARK|GETF_ALT, false);
4689}
4690
4691/*
4692 * "Z" commands.
4693 */
4694static void nv_Zet(cmdarg_T *cap)
4695{
4696 if (!checkclearopq(cap->oap)) {
4697 switch (cap->nchar) {
4698 /* "ZZ": equivalent to ":x". */
4699 case 'Z': do_cmdline_cmd("x");
4700 break;
4701
4702 /* "ZQ": equivalent to ":q!" (Elvis compatible). */
4703 case 'Q': do_cmdline_cmd("q!");
4704 break;
4705
4706 default: clearopbeep(cap->oap);
4707 }
4708 }
4709}
4710
4711/*
4712 * Call nv_ident() as if "c1" was used, with "c2" as next character.
4713 */
4714void do_nv_ident(int c1, int c2)
4715{
4716 oparg_T oa;
4717 cmdarg_T ca;
4718
4719 clear_oparg(&oa);
4720 memset(&ca, 0, sizeof(ca));
4721 ca.oap = &oa;
4722 ca.cmdchar = c1;
4723 ca.nchar = c2;
4724 nv_ident(&ca);
4725}
4726
4727/*
4728 * Handle the commands that use the word under the cursor.
4729 * [g] CTRL-] :ta to current identifier
4730 * [g] 'K' run program for current identifier
4731 * [g] '*' / to current identifier or string
4732 * [g] '#' ? to current identifier or string
4733 * g ']' :tselect for current identifier
4734 */
4735static void nv_ident(cmdarg_T *cap)
4736{
4737 char_u *ptr = NULL;
4738 char_u *p;
4739 size_t n = 0; /* init for GCC */
4740 int cmdchar;
4741 bool g_cmd; /* "g" command */
4742 bool tag_cmd = false;
4743 char_u *aux_ptr;
4744
4745 if (cap->cmdchar == 'g') { /* "g*", "g#", "g]" and "gCTRL-]" */
4746 cmdchar = cap->nchar;
4747 g_cmd = true;
4748 } else {
4749 cmdchar = cap->cmdchar;
4750 g_cmd = false;
4751 }
4752
4753 if (cmdchar == POUND) /* the pound sign, '#' for English keyboards */
4754 cmdchar = '#';
4755
4756 /*
4757 * The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
4758 */
4759 if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K') {
4760 if (VIsual_active && get_visual_text(cap, &ptr, &n) == false)
4761 return;
4762 if (checkclearopq(cap->oap))
4763 return;
4764 }
4765
4766 if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
4767 ((cmdchar == '*'
4768 || cmdchar == '#')
4769 ? FIND_IDENT|FIND_STRING
4770 : FIND_IDENT))) == 0) {
4771 clearop(cap->oap);
4772 return;
4773 }
4774
4775 /* Allocate buffer to put the command in. Inserting backslashes can
4776 * double the length of the word. p_kp / curbuf->b_p_kp could be added
4777 * and some numbers. */
4778 char_u *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp; // 'keywordprg'
4779 assert(*kp != NUL); // option.c:do_set() should default to ":help" if empty.
4780 bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command
4781 bool kp_help = (STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0);
4782 if (kp_help && *skipwhite(ptr) == NUL) {
4783 EMSG(_(e_noident)); // found white space only
4784 return;
4785 }
4786 size_t buf_size = n * 2 + 30 + STRLEN(kp);
4787 char *buf = xmalloc(buf_size);
4788 buf[0] = NUL;
4789
4790 switch (cmdchar) {
4791 case '*':
4792 case '#':
4793 /*
4794 * Put cursor at start of word, makes search skip the word
4795 * under the cursor.
4796 * Call setpcmark() first, so "*``" puts the cursor back where
4797 * it was.
4798 */
4799 setpcmark();
4800 curwin->w_cursor.col = (colnr_T) (ptr - get_cursor_line_ptr());
4801
4802 if (!g_cmd && vim_iswordp(ptr))
4803 STRCPY(buf, "\\<");
4804 no_smartcase = true; /* don't use 'smartcase' now */
4805 break;
4806
4807 case 'K':
4808 if (kp_help) {
4809 STRCPY(buf, "he! ");
4810 } else if (kp_ex) {
4811 if (cap->count0 != 0) { // Send the count to the ex command.
4812 snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0));
4813 }
4814 STRCAT(buf, kp);
4815 STRCAT(buf, " ");
4816 } else {
4817 /* An external command will probably use an argument starting
4818 * with "-" as an option. To avoid trouble we skip the "-". */
4819 while (*ptr == '-' && n > 0) {
4820 ++ptr;
4821 --n;
4822 }
4823 if (n == 0) {
4824 EMSG(_(e_noident)); /* found dashes only */
4825 xfree(buf);
4826 return;
4827 }
4828
4829 /* When a count is given, turn it into a range. Is this
4830 * really what we want? */
4831 bool isman = (STRCMP(kp, "man") == 0);
4832 bool isman_s = (STRCMP(kp, "man -s") == 0);
4833 if (cap->count0 != 0 && !(isman || isman_s)) {
4834 snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
4835 }
4836
4837 STRCAT(buf, "! ");
4838 if (cap->count0 == 0 && isman_s) {
4839 STRCAT(buf, "man");
4840 } else {
4841 STRCAT(buf, kp);
4842 }
4843 STRCAT(buf, " ");
4844 if (cap->count0 != 0 && (isman || isman_s)) {
4845 snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
4846 (int64_t)cap->count0);
4847 STRCAT(buf, " ");
4848 }
4849 }
4850 break;
4851
4852 case ']':
4853 tag_cmd = true;
4854 if (p_cst)
4855 STRCPY(buf, "cstag ");
4856 else
4857 STRCPY(buf, "ts ");
4858 break;
4859
4860 default:
4861 tag_cmd = true;
4862 if (curbuf->b_help)
4863 STRCPY(buf, "he! ");
4864 else {
4865 if (g_cmd)
4866 STRCPY(buf, "tj ");
4867 else
4868 snprintf(buf, buf_size, "%" PRId64 "ta ", (int64_t)cap->count0);
4869 }
4870 }
4871
4872 // Now grab the chars in the identifier
4873 if (cmdchar == 'K' && !kp_help) {
4874 ptr = vim_strnsave(ptr, n);
4875 if (kp_ex) {
4876 // Escape the argument properly for an Ex command
4877 p = (char_u *)vim_strsave_fnameescape((const char *)ptr, false);
4878 } else {
4879 // Escape the argument properly for a shell command
4880 p = vim_strsave_shellescape(ptr, true, true);
4881 }
4882 xfree(ptr);
4883 char *newbuf = xrealloc(buf, STRLEN(buf) + STRLEN(p) + 1);
4884 buf = newbuf;
4885 STRCAT(buf, p);
4886 xfree(p);
4887 } else {
4888 if (cmdchar == '*')
4889 aux_ptr = (char_u *)(p_magic ? "/.*~[^$\\" : "/^$\\");
4890 else if (cmdchar == '#')
4891 aux_ptr = (char_u *)(p_magic ? "/?.*~[^$\\" : "/?^$\\");
4892 else if (tag_cmd) {
4893 if (curbuf->b_help)
4894 /* ":help" handles unescaped argument */
4895 aux_ptr = (char_u *)"";
4896 else
4897 aux_ptr = (char_u *)"\\|\"\n[";
4898 } else
4899 aux_ptr = (char_u *)"\\|\"\n*?[";
4900
4901 p = (char_u *)buf + STRLEN(buf);
4902 while (n-- > 0) {
4903 /* put a backslash before \ and some others */
4904 if (vim_strchr(aux_ptr, *ptr) != NULL)
4905 *p++ = '\\';
4906 /* When current byte is a part of multibyte character, copy all
4907 * bytes of that character. */
4908 if (has_mbyte) {
4909 size_t len = (size_t)((*mb_ptr2len)(ptr) - 1);
4910 for (size_t i = 0; i < len && n > 0; ++i, --n)
4911 *p++ = *ptr++;
4912 }
4913 *p++ = *ptr++;
4914 }
4915 *p = NUL;
4916 }
4917
4918 /*
4919 * Execute the command.
4920 */
4921 if (cmdchar == '*' || cmdchar == '#') {
4922 if (!g_cmd && (
4923 has_mbyte ? vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr)) :
4924 vim_iswordc(ptr[-1])))
4925 STRCAT(buf, "\\>");
4926 /* put pattern in search history */
4927 init_history();
4928 add_to_history(HIST_SEARCH, (char_u *)buf, true, NUL);
4929 (void)normal_search(cap, cmdchar == '*' ? '/' : '?', (char_u *)buf, 0);
4930 } else {
4931 do_cmdline_cmd(buf);
4932 }
4933
4934 xfree(buf);
4935}
4936
4937/*
4938 * Get visually selected text, within one line only.
4939 * Returns false if more than one line selected.
4940 */
4941bool
4942get_visual_text (
4943 cmdarg_T *cap,
4944 char_u **pp, /* return: start of selected text */
4945 size_t *lenp /* return: length of selected text */
4946)
4947{
4948 if (VIsual_mode != 'V')
4949 unadjust_for_sel();
4950 if (VIsual.lnum != curwin->w_cursor.lnum) {
4951 if (cap != NULL)
4952 clearopbeep(cap->oap);
4953 return false;
4954 }
4955 if (VIsual_mode == 'V') {
4956 *pp = get_cursor_line_ptr();
4957 *lenp = STRLEN(*pp);
4958 } else {
4959 if (lt(curwin->w_cursor, VIsual)) {
4960 *pp = ml_get_pos(&curwin->w_cursor);
4961 *lenp = (size_t)VIsual.col - (size_t)curwin->w_cursor.col + 1;
4962 } else {
4963 *pp = ml_get_pos(&VIsual);
4964 *lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
4965 }
4966 if (has_mbyte)
4967 /* Correct the length to include the whole last character. */
4968 *lenp += (size_t)((*mb_ptr2len)(*pp + (*lenp - 1)) - 1);
4969 }
4970 reset_VIsual_and_resel();
4971 return true;
4972}
4973
4974/*
4975 * CTRL-T: backwards in tag stack
4976 */
4977static void nv_tagpop(cmdarg_T *cap)
4978{
4979 if (!checkclearopq(cap->oap))
4980 do_tag((char_u *)"", DT_POP, (int)cap->count1, false, true);
4981}
4982
4983/*
4984 * Handle scrolling command 'H', 'L' and 'M'.
4985 */
4986static void nv_scroll(cmdarg_T *cap)
4987{
4988 int used = 0;
4989 long n;
4990 linenr_T lnum;
4991 int half;
4992
4993 cap->oap->motion_type = kMTLineWise;
4994 setpcmark();
4995
4996 if (cap->cmdchar == 'L') {
4997 validate_botline(); /* make sure curwin->w_botline is valid */
4998 curwin->w_cursor.lnum = curwin->w_botline - 1;
4999 if (cap->count1 - 1 >= curwin->w_cursor.lnum)
5000 curwin->w_cursor.lnum = 1;
5001 else {
5002 if (hasAnyFolding(curwin)) {
5003 /* Count a fold for one screen line. */
5004 for (n = cap->count1 - 1; n > 0
5005 && curwin->w_cursor.lnum > curwin->w_topline; --n) {
5006 (void)hasFolding(curwin->w_cursor.lnum,
5007 &curwin->w_cursor.lnum, NULL);
5008 --curwin->w_cursor.lnum;
5009 }
5010 } else
5011 curwin->w_cursor.lnum -= cap->count1 - 1;
5012 }
5013 } else {
5014 if (cap->cmdchar == 'M') {
5015 /* Don't count filler lines above the window. */
5016 used -= diff_check_fill(curwin, curwin->w_topline)
5017 - curwin->w_topfill;
5018 validate_botline(); // make sure w_empty_rows is valid
5019 half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2;
5020 for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) {
5021 // Count half he number of filler lines to be "below this
5022 // line" and half to be "above the next line".
5023 if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline
5024 + n) / 2 >= half) {
5025 --n;
5026 break;
5027 }
5028 used += plines(curwin->w_topline + n);
5029 if (used >= half)
5030 break;
5031 if (hasFolding(curwin->w_topline + n, NULL, &lnum))
5032 n = lnum - curwin->w_topline;
5033 }
5034 if (n > 0 && used > curwin->w_height_inner) {
5035 n--;
5036 }
5037 } else { // (cap->cmdchar == 'H')
5038 n = cap->count1 - 1;
5039 if (hasAnyFolding(curwin)) {
5040 /* Count a fold for one screen line. */
5041 lnum = curwin->w_topline;
5042 while (n-- > 0 && lnum < curwin->w_botline - 1) {
5043 hasFolding(lnum, NULL, &lnum);
5044 ++lnum;
5045 }
5046 n = lnum - curwin->w_topline;
5047 }
5048 }
5049 curwin->w_cursor.lnum = curwin->w_topline + n;
5050 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
5051 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5052 }
5053
5054 // Correct for 'so', except when an operator is pending.
5055 if (cap->oap->op_type == OP_NOP) {
5056 cursor_correct();
5057 }
5058 beginline(BL_SOL | BL_FIX);
5059}
5060
5061/*
5062 * Cursor right commands.
5063 */
5064static void nv_right(cmdarg_T *cap)
5065{
5066 long n;
5067 int PAST_LINE;
5068
5069 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
5070 /* <C-Right> and <S-Right> move a word or WORD right */
5071 if (mod_mask & MOD_MASK_CTRL)
5072 cap->arg = true;
5073 nv_wordcmd(cap);
5074 return;
5075 }
5076
5077 cap->oap->motion_type = kMTCharWise;
5078 cap->oap->inclusive = false;
5079 PAST_LINE = (VIsual_active && *p_sel != 'o');
5080
5081 /*
5082 * In virtual mode, there's no such thing as "PAST_LINE", as lines are
5083 * (theoretically) infinitely long.
5084 */
5085 if (virtual_active())
5086 PAST_LINE = 0;
5087
5088 for (n = cap->count1; n > 0; --n) {
5089 if ((!PAST_LINE && oneright() == false)
5090 || (PAST_LINE && *get_cursor_pos_ptr() == NUL)
5091 ) {
5092 // <Space> wraps to next line if 'whichwrap' has 's'.
5093 // 'l' wraps to next line if 'whichwrap' has 'l'.
5094 // CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
5095 if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL)
5096 || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL)
5097 || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL))
5098 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
5099 // When deleting we also count the NL as a character.
5100 // Set cap->oap->inclusive when last char in the line is
5101 // included, move to next line after that
5102 if (cap->oap->op_type != OP_NOP
5103 && !cap->oap->inclusive
5104 && !LINEEMPTY(curwin->w_cursor.lnum)) {
5105 cap->oap->inclusive = true;
5106 } else {
5107 ++curwin->w_cursor.lnum;
5108 curwin->w_cursor.col = 0;
5109 curwin->w_cursor.coladd = 0;
5110 curwin->w_set_curswant = true;
5111 cap->oap->inclusive = false;
5112 }
5113 continue;
5114 }
5115 if (cap->oap->op_type == OP_NOP) {
5116 // Only beep and flush if not moved at all
5117 if (n == cap->count1) {
5118 beep_flush();
5119 }
5120 } else {
5121 if (!LINEEMPTY(curwin->w_cursor.lnum)) {
5122 cap->oap->inclusive = true;
5123 }
5124 }
5125 break;
5126 } else if (PAST_LINE) {
5127 curwin->w_set_curswant = true;
5128 if (virtual_active())
5129 oneright();
5130 else {
5131 if (has_mbyte)
5132 curwin->w_cursor.col +=
5133 (*mb_ptr2len)(get_cursor_pos_ptr());
5134 else
5135 ++curwin->w_cursor.col;
5136 }
5137 }
5138 }
5139 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
5140 && cap->oap->op_type == OP_NOP)
5141 foldOpenCursor();
5142}
5143
5144/*
5145 * Cursor left commands.
5146 *
5147 * Returns true when operator end should not be adjusted.
5148 */
5149static void nv_left(cmdarg_T *cap)
5150{
5151 long n;
5152
5153 if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
5154 /* <C-Left> and <S-Left> move a word or WORD left */
5155 if (mod_mask & MOD_MASK_CTRL)
5156 cap->arg = 1;
5157 nv_bck_word(cap);
5158 return;
5159 }
5160
5161 cap->oap->motion_type = kMTCharWise;
5162 cap->oap->inclusive = false;
5163 for (n = cap->count1; n > 0; --n) {
5164 if (oneleft() == false) {
5165 /* <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
5166 * 'h' wraps to previous line if 'whichwrap' has 'h'.
5167 * CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
5168 */
5169 if ( (((cap->cmdchar == K_BS
5170 || cap->cmdchar == Ctrl_H)
5171 && vim_strchr(p_ww, 'b') != NULL)
5172 || (cap->cmdchar == 'h'
5173 && vim_strchr(p_ww, 'h') != NULL)
5174 || (cap->cmdchar == K_LEFT
5175 && vim_strchr(p_ww, '<') != NULL))
5176 && curwin->w_cursor.lnum > 1) {
5177 --(curwin->w_cursor.lnum);
5178 coladvance((colnr_T)MAXCOL);
5179 curwin->w_set_curswant = true;
5180
5181 // When the NL before the first char has to be deleted we
5182 // put the cursor on the NUL after the previous line.
5183 // This is a very special case, be careful!
5184 // Don't adjust op_end now, otherwise it won't work.
5185 if ((cap->oap->op_type == OP_DELETE || cap->oap->op_type == OP_CHANGE)
5186 && !LINEEMPTY(curwin->w_cursor.lnum)) {
5187 char_u *cp = get_cursor_pos_ptr();
5188
5189 if (*cp != NUL) {
5190 if (has_mbyte) {
5191 curwin->w_cursor.col += (*mb_ptr2len)(cp);
5192 } else {
5193 curwin->w_cursor.col++;
5194 }
5195 }
5196 cap->retval |= CA_NO_ADJ_OP_END;
5197 }
5198 continue;
5199 }
5200 /* Only beep and flush if not moved at all */
5201 else if (cap->oap->op_type == OP_NOP && n == cap->count1)
5202 beep_flush();
5203 break;
5204 }
5205 }
5206 if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
5207 && cap->oap->op_type == OP_NOP)
5208 foldOpenCursor();
5209}
5210
5211/*
5212 * Cursor up commands.
5213 * cap->arg is true for "-": Move cursor to first non-blank.
5214 */
5215static void nv_up(cmdarg_T *cap)
5216{
5217 if (mod_mask & MOD_MASK_SHIFT) {
5218 /* <S-Up> is page up */
5219 cap->arg = BACKWARD;
5220 nv_page(cap);
5221 } else {
5222 cap->oap->motion_type = kMTLineWise;
5223 if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) {
5224 clearopbeep(cap->oap);
5225 } else if (cap->arg) {
5226 beginline(BL_WHITE | BL_FIX);
5227 }
5228 }
5229}
5230
5231/*
5232 * Cursor down commands.
5233 * cap->arg is true for CR and "+": Move cursor to first non-blank.
5234 */
5235static void nv_down(cmdarg_T *cap)
5236{
5237 if (mod_mask & MOD_MASK_SHIFT) {
5238 /* <S-Down> is page down */
5239 cap->arg = FORWARD;
5240 nv_page(cap);
5241 } else if (bt_quickfix(curbuf) && cap->cmdchar == CAR) {
5242 // Quickfix window only: view the result under the cursor.
5243 qf_view_result(false);
5244 } else {
5245 // In the cmdline window a <CR> executes the command.
5246 if (cmdwin_type != 0 && cap->cmdchar == CAR) {
5247 cmdwin_result = CAR;
5248 } else {
5249 cap->oap->motion_type = kMTLineWise;
5250 if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) {
5251 clearopbeep(cap->oap);
5252 } else if (cap->arg) {
5253 beginline(BL_WHITE | BL_FIX);
5254 }
5255 }
5256 }
5257}
5258
5259/*
5260 * Grab the file name under the cursor and edit it.
5261 */
5262static void nv_gotofile(cmdarg_T *cap)
5263{
5264 char_u *ptr;
5265 linenr_T lnum = -1;
5266
5267 if (text_locked()) {
5268 clearopbeep(cap->oap);
5269 text_locked_msg();
5270 return;
5271 }
5272 if (curbuf_locked()) {
5273 clearop(cap->oap);
5274 return;
5275 }
5276
5277 ptr = grab_file_name(cap->count1, &lnum);
5278
5279 if (ptr != NULL) {
5280 // do autowrite if necessary
5281 if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf)) {
5282 (void)autowrite(curbuf, false);
5283 }
5284 setpcmark();
5285 if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
5286 buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
5287 && cap->nchar == 'F' && lnum >= 0) {
5288 curwin->w_cursor.lnum = lnum;
5289 check_cursor_lnum();
5290 beginline(BL_SOL | BL_FIX);
5291 }
5292 xfree(ptr);
5293 } else
5294 clearop(cap->oap);
5295}
5296
5297/*
5298 * <End> command: to end of current line or last line.
5299 */
5300static void nv_end(cmdarg_T *cap)
5301{
5302 if (cap->arg || (mod_mask & MOD_MASK_CTRL)) { /* CTRL-END = goto last line */
5303 cap->arg = true;
5304 nv_goto(cap);
5305 cap->count1 = 1; /* to end of current line */
5306 }
5307 nv_dollar(cap);
5308}
5309
5310/*
5311 * Handle the "$" command.
5312 */
5313static void nv_dollar(cmdarg_T *cap)
5314{
5315 cap->oap->motion_type = kMTCharWise;
5316 cap->oap->inclusive = true;
5317 /* In virtual mode when off the edge of a line and an operator
5318 * is pending (whew!) keep the cursor where it is.
5319 * Otherwise, send it to the end of the line. */
5320 if (!virtual_active() || gchar_cursor() != NUL
5321 || cap->oap->op_type == OP_NOP)
5322 curwin->w_curswant = MAXCOL; /* so we stay at the end */
5323 if (cursor_down(cap->count1 - 1,
5324 cap->oap->op_type == OP_NOP) == false)
5325 clearopbeep(cap->oap);
5326 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
5327 foldOpenCursor();
5328}
5329
5330/*
5331 * Implementation of '?' and '/' commands.
5332 * If cap->arg is true don't set PC mark.
5333 */
5334static void nv_search(cmdarg_T *cap)
5335{
5336 oparg_T *oap = cap->oap;
5337 pos_T save_cursor = curwin->w_cursor;
5338
5339 if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13) {
5340 /* Translate "g??" to "g?g?" */
5341 cap->cmdchar = 'g';
5342 cap->nchar = '?';
5343 nv_operator(cap);
5344 return;
5345 }
5346
5347 // When using 'incsearch' the cursor may be moved to set a different search
5348 // start position.
5349 cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0);
5350
5351 if (cap->searchbuf == NULL) {
5352 clearop(oap);
5353 return;
5354 }
5355
5356 (void)normal_search(cap, cap->cmdchar, cap->searchbuf,
5357 (cap->arg || !equalpos(save_cursor, curwin->w_cursor))
5358 ? 0 : SEARCH_MARK);
5359}
5360
5361/*
5362 * Handle "N" and "n" commands.
5363 * cap->arg is SEARCH_REV for "N", 0 for "n".
5364 */
5365static void nv_next(cmdarg_T *cap)
5366{
5367 pos_T old = curwin->w_cursor;
5368 int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg);
5369
5370 if (i == 1 && equalpos(old, curwin->w_cursor)) {
5371 // Avoid getting stuck on the current cursor position, which can happen when
5372 // an offset is given and the cursor is on the last char in the buffer:
5373 // Repeat with count + 1.
5374 cap->count1 += 1;
5375 (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg);
5376 cap->count1 -= 1;
5377 }
5378}
5379
5380/*
5381 * Search for "pat" in direction "dir" ('/' or '?', 0 for repeat).
5382 * Uses only cap->count1 and cap->oap from "cap".
5383 * Return 0 for failure, 1 for found, 2 for found and line offset added.
5384 */
5385static int normal_search(
5386 cmdarg_T *cap,
5387 int dir,
5388 char_u *pat,
5389 int opt /* extra flags for do_search() */
5390)
5391{
5392 int i;
5393
5394 cap->oap->motion_type = kMTCharWise;
5395 cap->oap->inclusive = false;
5396 cap->oap->use_reg_one = true;
5397 curwin->w_set_curswant = true;
5398
5399 i = do_search(cap->oap, dir, pat, cap->count1,
5400 opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL, NULL);
5401 if (i == 0) {
5402 clearop(cap->oap);
5403 } else {
5404 if (i == 2) {
5405 cap->oap->motion_type = kMTLineWise;
5406 }
5407 curwin->w_cursor.coladd = 0;
5408 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
5409 foldOpenCursor();
5410 }
5411
5412 /* "/$" will put the cursor after the end of the line, may need to
5413 * correct that here */
5414 check_cursor();
5415 return i;
5416}
5417
5418/*
5419 * Character search commands.
5420 * cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', true for
5421 * ',' and false for ';'.
5422 * cap->nchar is NUL for ',' and ';' (repeat the search)
5423 */
5424static void nv_csearch(cmdarg_T *cap)
5425{
5426 bool t_cmd;
5427
5428 if (cap->cmdchar == 't' || cap->cmdchar == 'T')
5429 t_cmd = true;
5430 else
5431 t_cmd = false;
5432
5433 cap->oap->motion_type = kMTCharWise;
5434 if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) {
5435 clearopbeep(cap->oap);
5436 } else {
5437 curwin->w_set_curswant = true;
5438 /* Include a Tab for "tx" and for "dfx". */
5439 if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
5440 && (t_cmd || cap->oap->op_type != OP_NOP)) {
5441 colnr_T scol, ecol;
5442
5443 getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
5444 curwin->w_cursor.coladd = ecol - scol;
5445 } else
5446 curwin->w_cursor.coladd = 0;
5447 adjust_for_sel(cap);
5448 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
5449 foldOpenCursor();
5450 }
5451}
5452
5453/*
5454 * "[" and "]" commands.
5455 * cap->arg is BACKWARD for "[" and FORWARD for "]".
5456 */
5457static void nv_brackets(cmdarg_T *cap)
5458{
5459 pos_T new_pos = { 0, 0, 0 };
5460 pos_T prev_pos;
5461 pos_T *pos = NULL; /* init for GCC */
5462 pos_T old_pos; /* cursor position before command */
5463 int flag;
5464 long n;
5465 int findc;
5466 int c;
5467
5468 cap->oap->motion_type = kMTCharWise;
5469 cap->oap->inclusive = false;
5470 old_pos = curwin->w_cursor;
5471 curwin->w_cursor.coladd = 0; /* TODO: don't do this for an error. */
5472
5473 /*
5474 * "[f" or "]f" : Edit file under the cursor (same as "gf")
5475 */
5476 if (cap->nchar == 'f')
5477 nv_gotofile(cap);
5478 else
5479 /*
5480 * Find the occurrence(s) of the identifier or define under cursor
5481 * in current and included files or jump to the first occurrence.
5482 *
5483 * search list jump
5484 * fwd bwd fwd bwd fwd bwd
5485 * identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
5486 * define "]d" "[d" "]D" "[D" "]^D" "[^D"
5487 */
5488 if (vim_strchr((char_u *)
5489 "iI\011dD\004",
5490 cap->nchar) != NULL) {
5491 char_u *ptr;
5492 size_t len;
5493
5494 if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
5495 clearop(cap->oap);
5496 else {
5497 find_pattern_in_path(ptr, 0, len, true,
5498 cap->count0 == 0 ? !isupper(cap->nchar) : false,
5499 (((cap->nchar & 0xf) == ('d' & 0xf))
5500 ? FIND_DEFINE
5501 : FIND_ANY),
5502 cap->count1,
5503 (isupper(cap->nchar) ? ACTION_SHOW_ALL :
5504 islower(cap->nchar) ? ACTION_SHOW :
5505 ACTION_GOTO),
5506 (cap->cmdchar == ']'
5507 ? curwin->w_cursor.lnum + 1
5508 : (linenr_T)1),
5509 MAXLNUM);
5510 curwin->w_set_curswant = true;
5511 }
5512 } else
5513 /*
5514 * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
5515 * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
5516 * "[/", "[*", "]/", "]*": go to Nth comment start/end.
5517 * "[m" or "]m" search for prev/next start of (Java) method.
5518 * "[M" or "]M" search for prev/next end of (Java) method.
5519 */
5520 if ( (cap->cmdchar == '['
5521 && vim_strchr((char_u *)"{(*/#mM", cap->nchar) != NULL)
5522 || (cap->cmdchar == ']'
5523 && vim_strchr((char_u *)"})*/#mM", cap->nchar) != NULL)) {
5524 if (cap->nchar == '*')
5525 cap->nchar = '/';
5526 prev_pos.lnum = 0;
5527 if (cap->nchar == 'm' || cap->nchar == 'M') {
5528 if (cap->cmdchar == '[')
5529 findc = '{';
5530 else
5531 findc = '}';
5532 n = 9999;
5533 } else {
5534 findc = cap->nchar;
5535 n = cap->count1;
5536 }
5537 for (; n > 0; --n) {
5538 if ((pos = findmatchlimit(cap->oap, findc,
5539 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
5540 if (new_pos.lnum == 0) { /* nothing found */
5541 if (cap->nchar != 'm' && cap->nchar != 'M')
5542 clearopbeep(cap->oap);
5543 } else
5544 pos = &new_pos; /* use last one found */
5545 break;
5546 }
5547 prev_pos = new_pos;
5548 curwin->w_cursor = *pos;
5549 new_pos = *pos;
5550 }
5551 curwin->w_cursor = old_pos;
5552
5553 /*
5554 * Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
5555 * brought us to the match for "[m" and "]M" when inside a method.
5556 * Try finding the '{' or '}' we want to be at.
5557 * Also repeat for the given count.
5558 */
5559 if (cap->nchar == 'm' || cap->nchar == 'M') {
5560 /* norm is true for "]M" and "[m" */
5561 int norm = ((findc == '{') == (cap->nchar == 'm'));
5562
5563 n = cap->count1;
5564 /* found a match: we were inside a method */
5565 if (prev_pos.lnum != 0) {
5566 pos = &prev_pos;
5567 curwin->w_cursor = prev_pos;
5568 if (norm)
5569 --n;
5570 } else
5571 pos = NULL;
5572 while (n > 0) {
5573 for (;; ) {
5574 if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
5575 /* if not found anything, that's an error */
5576 if (pos == NULL)
5577 clearopbeep(cap->oap);
5578 n = 0;
5579 break;
5580 }
5581 c = gchar_cursor();
5582 if (c == '{' || c == '}') {
5583 /* Must have found end/start of class: use it.
5584 * Or found the place to be at. */
5585 if ((c == findc && norm) || (n == 1 && !norm)) {
5586 new_pos = curwin->w_cursor;
5587 pos = &new_pos;
5588 n = 0;
5589 }
5590 /* if no match found at all, we started outside of the
5591 * class and we're inside now. Just go on. */
5592 else if (new_pos.lnum == 0) {
5593 new_pos = curwin->w_cursor;
5594 pos = &new_pos;
5595 }
5596 /* found start/end of other method: go to match */
5597 else if ((pos = findmatchlimit(cap->oap, findc,
5598 (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
5599 0)) == NULL)
5600 n = 0;
5601 else
5602 curwin->w_cursor = *pos;
5603 break;
5604 }
5605 }
5606 --n;
5607 }
5608 curwin->w_cursor = old_pos;
5609 if (pos == NULL && new_pos.lnum != 0)
5610 clearopbeep(cap->oap);
5611 }
5612 if (pos != NULL) {
5613 setpcmark();
5614 curwin->w_cursor = *pos;
5615 curwin->w_set_curswant = true;
5616 if ((fdo_flags & FDO_BLOCK) && KeyTyped
5617 && cap->oap->op_type == OP_NOP)
5618 foldOpenCursor();
5619 }
5620 }
5621 /*
5622 * "[[", "[]", "]]" and "][": move to start or end of function
5623 */
5624 else if (cap->nchar == '[' || cap->nchar == ']') {
5625 if (cap->nchar == cap->cmdchar) /* "]]" or "[[" */
5626 flag = '{';
5627 else
5628 flag = '}'; /* "][" or "[]" */
5629
5630 curwin->w_set_curswant = true;
5631 /*
5632 * Imitate strange Vi behaviour: When using "]]" with an operator
5633 * we also stop at '}'.
5634 */
5635 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag,
5636 (cap->oap->op_type != OP_NOP
5637 && cap->arg == FORWARD && flag == '{')))
5638 clearopbeep(cap->oap);
5639 else {
5640 if (cap->oap->op_type == OP_NOP)
5641 beginline(BL_WHITE | BL_FIX);
5642 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
5643 foldOpenCursor();
5644 }
5645 } else if (cap->nchar == 'p' || cap->nchar == 'P') {
5646 // "[p", "[P", "]P" and "]p": put with indent adjustment
5647 nv_put_opt(cap, true);
5648 }
5649 /*
5650 * "['", "[`", "]'" and "]`": jump to next mark
5651 */
5652 else if (cap->nchar == '\'' || cap->nchar == '`') {
5653 pos = &curwin->w_cursor;
5654 for (n = cap->count1; n > 0; --n) {
5655 prev_pos = *pos;
5656 pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
5657 cap->nchar == '\'');
5658 if (pos == NULL)
5659 break;
5660 }
5661 if (pos == NULL)
5662 pos = &prev_pos;
5663 nv_cursormark(cap, cap->nchar == '\'', pos);
5664 }
5665 /*
5666 * [ or ] followed by a middle mouse click: put selected text with
5667 * indent adjustment. Any other button just does as usual.
5668 */
5669 else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) {
5670 (void)do_mouse(cap->oap, cap->nchar,
5671 (cap->cmdchar == ']') ? FORWARD : BACKWARD,
5672 cap->count1, PUT_FIXINDENT);
5673 }
5674 /*
5675 * "[z" and "]z": move to start or end of open fold.
5676 */
5677 else if (cap->nchar == 'z') {
5678 if (foldMoveTo(false, cap->cmdchar == ']' ? FORWARD : BACKWARD,
5679 cap->count1) == false)
5680 clearopbeep(cap->oap);
5681 }
5682 /*
5683 * "[c" and "]c": move to next or previous diff-change.
5684 */
5685 else if (cap->nchar == 'c') {
5686 if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
5687 cap->count1) == false)
5688 clearopbeep(cap->oap);
5689 }
5690 /*
5691 * "[s", "[S", "]s" and "]S": move to next spell error.
5692 */
5693 else if (cap->nchar == 's' || cap->nchar == 'S') {
5694 setpcmark();
5695 for (n = 0; n < cap->count1; ++n)
5696 if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
5697 cap->nchar == 's', false, NULL) == 0) {
5698 clearopbeep(cap->oap);
5699 break;
5700 } else {
5701 curwin->w_set_curswant = true;
5702 }
5703 if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
5704 foldOpenCursor();
5705 }
5706 /* Not a valid cap->nchar. */
5707 else
5708 clearopbeep(cap->oap);
5709}
5710
5711/*
5712 * Handle Normal mode "%" command.
5713 */
5714static void nv_percent(cmdarg_T *cap)
5715{
5716 pos_T *pos;
5717 linenr_T lnum = curwin->w_cursor.lnum;
5718
5719 cap->oap->inclusive = true;
5720 if (cap->count0) { // {cnt}% : goto {cnt} percentage in file
5721 if (cap->count0 > 100) {
5722 clearopbeep(cap->oap);
5723 } else {
5724 cap->oap->motion_type = kMTLineWise;
5725 setpcmark();
5726 /* Round up, so CTRL-G will give same value. Watch out for a
5727 * large line count, the line number must not go negative! */
5728 if (curbuf->b_ml.ml_line_count > 1000000)
5729 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99L)
5730 / 100L * cap->count0;
5731 else
5732 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
5733 cap->count0 + 99L) / 100L;
5734 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
5735 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5736 beginline(BL_SOL | BL_FIX);
5737 }
5738 } else { // "%" : go to matching paren
5739 cap->oap->motion_type = kMTCharWise;
5740 cap->oap->use_reg_one = true;
5741 if ((pos = findmatch(cap->oap, NUL)) == NULL)
5742 clearopbeep(cap->oap);
5743 else {
5744 setpcmark();
5745 curwin->w_cursor = *pos;
5746 curwin->w_set_curswant = true;
5747 curwin->w_cursor.coladd = 0;
5748 adjust_for_sel(cap);
5749 }
5750 }
5751 if (cap->oap->op_type == OP_NOP
5752 && lnum != curwin->w_cursor.lnum
5753 && (fdo_flags & FDO_PERCENT)
5754 && KeyTyped)
5755 foldOpenCursor();
5756}
5757
5758/*
5759 * Handle "(" and ")" commands.
5760 * cap->arg is BACKWARD for "(" and FORWARD for ")".
5761 */
5762static void nv_brace(cmdarg_T *cap)
5763{
5764 cap->oap->motion_type = kMTCharWise;
5765 cap->oap->use_reg_one = true;
5766 /* The motion used to be inclusive for "(", but that is not what Vi does. */
5767 cap->oap->inclusive = false;
5768 curwin->w_set_curswant = true;
5769
5770 if (findsent(cap->arg, cap->count1) == false)
5771 clearopbeep(cap->oap);
5772 else {
5773 /* Don't leave the cursor on the NUL past end of line. */
5774 adjust_cursor(cap->oap);
5775 curwin->w_cursor.coladd = 0;
5776 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
5777 foldOpenCursor();
5778 }
5779}
5780
5781/*
5782 * "m" command: Mark a position.
5783 */
5784static void nv_mark(cmdarg_T *cap)
5785{
5786 if (!checkclearop(cap->oap)) {
5787 if (setmark(cap->nchar) == false)
5788 clearopbeep(cap->oap);
5789 }
5790}
5791
5792/*
5793 * "{" and "}" commands.
5794 * cmd->arg is BACKWARD for "{" and FORWARD for "}".
5795 */
5796static void nv_findpar(cmdarg_T *cap)
5797{
5798 cap->oap->motion_type = kMTCharWise;
5799 cap->oap->inclusive = false;
5800 cap->oap->use_reg_one = true;
5801 curwin->w_set_curswant = true;
5802 if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, false))
5803 clearopbeep(cap->oap);
5804 else {
5805 curwin->w_cursor.coladd = 0;
5806 if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP)
5807 foldOpenCursor();
5808 }
5809}
5810
5811/*
5812 * "u" command: Undo or make lower case.
5813 */
5814static void nv_undo(cmdarg_T *cap)
5815{
5816 if (cap->oap->op_type == OP_LOWER
5817 || VIsual_active
5818 ) {
5819 /* translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu" */
5820 cap->cmdchar = 'g';
5821 cap->nchar = 'u';
5822 nv_operator(cap);
5823 } else
5824 nv_kundo(cap);
5825}
5826
5827/*
5828 * <Undo> command.
5829 */
5830static void nv_kundo(cmdarg_T *cap)
5831{
5832 if (!checkclearopq(cap->oap)) {
5833 u_undo((int)cap->count1);
5834 curwin->w_set_curswant = true;
5835 }
5836}
5837
5838/*
5839 * Handle the "r" command.
5840 */
5841static void nv_replace(cmdarg_T *cap)
5842{
5843 char_u *ptr;
5844 int had_ctrl_v;
5845 long n;
5846
5847 if (checkclearop(cap->oap))
5848 return;
5849
5850 /* get another character */
5851 if (cap->nchar == Ctrl_V) {
5852 had_ctrl_v = Ctrl_V;
5853 cap->nchar = get_literal();
5854 /* Don't redo a multibyte character with CTRL-V. */
5855 if (cap->nchar > DEL)
5856 had_ctrl_v = NUL;
5857 } else
5858 had_ctrl_v = NUL;
5859
5860 /* Abort if the character is a special key. */
5861 if (IS_SPECIAL(cap->nchar)) {
5862 clearopbeep(cap->oap);
5863 return;
5864 }
5865
5866 /* Visual mode "r" */
5867 if (VIsual_active) {
5868 if (got_int)
5869 reset_VIsual();
5870 if (had_ctrl_v) {
5871 // Use a special (negative) number to make a difference between a
5872 // literal CR or NL and a line break.
5873 if (cap->nchar == CAR) {
5874 cap->nchar = REPLACE_CR_NCHAR;
5875 } else if (cap->nchar == NL) {
5876 cap->nchar = REPLACE_NL_NCHAR;
5877 }
5878 }
5879 nv_operator(cap);
5880 return;
5881 }
5882
5883 /* Break tabs, etc. */
5884 if (virtual_active()) {
5885 if (u_save_cursor() == false)
5886 return;
5887 if (gchar_cursor() == NUL) {
5888 /* Add extra space and put the cursor on the first one. */
5889 coladvance_force((colnr_T)(getviscol() + cap->count1));
5890 assert(cap->count1 <= INT_MAX);
5891 curwin->w_cursor.col -= (colnr_T)cap->count1;
5892 } else if (gchar_cursor() == TAB)
5893 coladvance_force(getviscol());
5894 }
5895
5896 /* Abort if not enough characters to replace. */
5897 ptr = get_cursor_pos_ptr();
5898 if (STRLEN(ptr) < (unsigned)cap->count1
5899 || (has_mbyte && mb_charlen(ptr) < cap->count1)
5900 ) {
5901 clearopbeep(cap->oap);
5902 return;
5903 }
5904
5905 // Replacing with a TAB is done by edit() when it is complicated because
5906 // 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB.
5907 // Other characters are done below to avoid problems with things like
5908 // CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
5909 if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta)) {
5910 stuffnumReadbuff(cap->count1);
5911 stuffcharReadbuff('R');
5912 stuffcharReadbuff('\t');
5913 stuffcharReadbuff(ESC);
5914 return;
5915 }
5916
5917 /* save line for undo */
5918 if (u_save_cursor() == false)
5919 return;
5920
5921 if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n')) {
5922 /*
5923 * Replace character(s) by a single newline.
5924 * Strange vi behaviour: Only one newline is inserted.
5925 * Delete the characters here.
5926 * Insert the newline with an insert command, takes care of
5927 * autoindent. The insert command depends on being on the last
5928 * character of a line or not.
5929 */
5930 (void)del_chars(cap->count1, false); /* delete the characters */
5931 stuffcharReadbuff('\r');
5932 stuffcharReadbuff(ESC);
5933
5934 /* Give 'r' to edit(), to get the redo command right. */
5935 invoke_edit(cap, true, 'r', false);
5936 } else {
5937 prep_redo(cap->oap->regname, cap->count1,
5938 NUL, 'r', NUL, had_ctrl_v, cap->nchar);
5939
5940 curbuf->b_op_start = curwin->w_cursor;
5941 if (has_mbyte) {
5942 int old_State = State;
5943
5944 if (cap->ncharC1 != 0)
5945 AppendCharToRedobuff(cap->ncharC1);
5946 if (cap->ncharC2 != 0)
5947 AppendCharToRedobuff(cap->ncharC2);
5948
5949 /* This is slow, but it handles replacing a single-byte with a
5950 * multi-byte and the other way around. Also handles adding
5951 * composing characters for utf-8. */
5952 for (n = cap->count1; n > 0; --n) {
5953 State = REPLACE;
5954 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) {
5955 int c = ins_copychar(curwin->w_cursor.lnum
5956 + (cap->nchar == Ctrl_Y ? -1 : 1));
5957 if (c != NUL)
5958 ins_char(c);
5959 else
5960 /* will be decremented further down */
5961 ++curwin->w_cursor.col;
5962 } else
5963 ins_char(cap->nchar);
5964 State = old_State;
5965 if (cap->ncharC1 != 0)
5966 ins_char(cap->ncharC1);
5967 if (cap->ncharC2 != 0)
5968 ins_char(cap->ncharC2);
5969 }
5970 } else {
5971 /*
5972 * Replace the characters within one line.
5973 */
5974 for (n = cap->count1; n > 0; --n) {
5975 /*
5976 * Get ptr again, because u_save and/or showmatch() will have
5977 * released the line. At the same time we let know that the
5978 * line will be changed.
5979 */
5980 ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true);
5981 if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) {
5982 int c = ins_copychar(curwin->w_cursor.lnum
5983 + (cap->nchar == Ctrl_Y ? -1 : 1));
5984 if (c != NUL) {
5985 assert(c >= 0 && c <= UCHAR_MAX);
5986 ptr[curwin->w_cursor.col] = (char_u)c;
5987 }
5988 } else {
5989 assert(cap->nchar >= 0 && cap->nchar <= UCHAR_MAX);
5990 ptr[curwin->w_cursor.col] = (char_u)cap->nchar;
5991 }
5992 if (p_sm && msg_silent == 0)
5993 showmatch(cap->nchar);
5994 ++curwin->w_cursor.col;
5995 }
5996
5997 /* mark the buffer as changed and prepare for displaying */
5998 changed_bytes(curwin->w_cursor.lnum,
5999 (colnr_T)(curwin->w_cursor.col - cap->count1));
6000 }
6001 --curwin->w_cursor.col; /* cursor on the last replaced char */
6002 /* if the character on the left of the current cursor is a multi-byte
6003 * character, move two characters left */
6004 if (has_mbyte)
6005 mb_adjust_cursor();
6006 curbuf->b_op_end = curwin->w_cursor;
6007 curwin->w_set_curswant = true;
6008 set_last_insert(cap->nchar);
6009 }
6010
6011 foldUpdateAfterInsert();
6012}
6013
6014/*
6015 * 'o': Exchange start and end of Visual area.
6016 * 'O': same, but in block mode exchange left and right corners.
6017 */
6018static void v_swap_corners(int cmdchar)
6019{
6020 pos_T old_cursor;
6021 colnr_T left, right;
6022
6023 if (cmdchar == 'O' && VIsual_mode == Ctrl_V) {
6024 old_cursor = curwin->w_cursor;
6025 getvcols(curwin, &old_cursor, &VIsual, &left, &right);
6026 curwin->w_cursor.lnum = VIsual.lnum;
6027 coladvance(left);
6028 VIsual = curwin->w_cursor;
6029
6030 curwin->w_cursor.lnum = old_cursor.lnum;
6031 curwin->w_curswant = right;
6032 /* 'selection "exclusive" and cursor at right-bottom corner: move it
6033 * right one column */
6034 if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e')
6035 ++curwin->w_curswant;
6036 coladvance(curwin->w_curswant);
6037 if (curwin->w_cursor.col == old_cursor.col
6038 && (!virtual_active()
6039 || curwin->w_cursor.coladd == old_cursor.coladd)
6040 ) {
6041 curwin->w_cursor.lnum = VIsual.lnum;
6042 if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e')
6043 ++right;
6044 coladvance(right);
6045 VIsual = curwin->w_cursor;
6046
6047 curwin->w_cursor.lnum = old_cursor.lnum;
6048 coladvance(left);
6049 curwin->w_curswant = left;
6050 }
6051 } else {
6052 old_cursor = curwin->w_cursor;
6053 curwin->w_cursor = VIsual;
6054 VIsual = old_cursor;
6055 curwin->w_set_curswant = true;
6056 }
6057}
6058
6059/*
6060 * "R" (cap->arg is false) and "gR" (cap->arg is true).
6061 */
6062static void nv_Replace(cmdarg_T *cap)
6063{
6064 if (VIsual_active) { /* "R" is replace lines */
6065 cap->cmdchar = 'c';
6066 cap->nchar = NUL;
6067 VIsual_mode_orig = VIsual_mode; /* remember original area for gv */
6068 VIsual_mode = 'V';
6069 nv_operator(cap);
6070 } else if (!checkclearopq(cap->oap)) {
6071 if (!MODIFIABLE(curbuf)) {
6072 EMSG(_(e_modifiable));
6073 } else {
6074 if (virtual_active())
6075 coladvance(getviscol());
6076 invoke_edit(cap, false, cap->arg ? 'V' : 'R', false);
6077 }
6078 }
6079}
6080
6081/*
6082 * "gr".
6083 */
6084static void nv_vreplace(cmdarg_T *cap)
6085{
6086 if (VIsual_active) {
6087 cap->cmdchar = 'r';
6088 cap->nchar = cap->extra_char;
6089 nv_replace(cap); /* Do same as "r" in Visual mode for now */
6090 } else if (!checkclearopq(cap->oap)) {
6091 if (!MODIFIABLE(curbuf)) {
6092 EMSG(_(e_modifiable));
6093 } else {
6094 if (cap->extra_char == Ctrl_V) /* get another character */
6095 cap->extra_char = get_literal();
6096 stuffcharReadbuff(cap->extra_char);
6097 stuffcharReadbuff(ESC);
6098 if (virtual_active())
6099 coladvance(getviscol());
6100 invoke_edit(cap, true, 'v', false);
6101 }
6102 }
6103}
6104
6105/*
6106 * Swap case for "~" command, when it does not work like an operator.
6107 */
6108static void n_swapchar(cmdarg_T *cap)
6109{
6110 long n;
6111 pos_T startpos;
6112 int did_change = 0;
6113
6114 if (checkclearopq(cap->oap)) {
6115 return;
6116 }
6117
6118 if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
6119 clearopbeep(cap->oap);
6120 return;
6121 }
6122
6123 prep_redo_cmd(cap);
6124
6125 if (u_save_cursor() == false)
6126 return;
6127
6128 startpos = curwin->w_cursor;
6129 for (n = cap->count1; n > 0; --n) {
6130 did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
6131 inc_cursor();
6132 if (gchar_cursor() == NUL) {
6133 if (vim_strchr(p_ww, '~') != NULL
6134 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
6135 ++curwin->w_cursor.lnum;
6136 curwin->w_cursor.col = 0;
6137 if (n > 1) {
6138 if (u_savesub(curwin->w_cursor.lnum) == false)
6139 break;
6140 u_clearline();
6141 }
6142 } else
6143 break;
6144 }
6145 }
6146
6147
6148 check_cursor();
6149 curwin->w_set_curswant = true;
6150 if (did_change) {
6151 changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1,
6152 0L, true);
6153 curbuf->b_op_start = startpos;
6154 curbuf->b_op_end = curwin->w_cursor;
6155 if (curbuf->b_op_end.col > 0)
6156 --curbuf->b_op_end.col;
6157 }
6158}
6159
6160/*
6161 * Move cursor to mark.
6162 */
6163static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
6164{
6165 if (check_mark(pos) == false)
6166 clearop(cap->oap);
6167 else {
6168 if (cap->cmdchar == '\''
6169 || cap->cmdchar == '`'
6170 || cap->cmdchar == '['
6171 || cap->cmdchar == ']')
6172 setpcmark();
6173 curwin->w_cursor = *pos;
6174 if (flag)
6175 beginline(BL_WHITE | BL_FIX);
6176 else
6177 check_cursor();
6178 }
6179 cap->oap->motion_type = flag ? kMTLineWise : kMTCharWise;
6180 if (cap->cmdchar == '`') {
6181 cap->oap->use_reg_one = true;
6182 }
6183 cap->oap->inclusive = false; // ignored if not kMTCharWise
6184 curwin->w_set_curswant = true;
6185}
6186
6187/*
6188 * Handle commands that are operators in Visual mode.
6189 */
6190static void v_visop(cmdarg_T *cap)
6191{
6192 static char_u trans[] = "YyDdCcxdXdAAIIrr";
6193
6194 /* Uppercase means linewise, except in block mode, then "D" deletes till
6195 * the end of the line, and "C" replaces till EOL */
6196 if (isupper(cap->cmdchar)) {
6197 if (VIsual_mode != Ctrl_V) {
6198 VIsual_mode_orig = VIsual_mode;
6199 VIsual_mode = 'V';
6200 } else if (cap->cmdchar == 'C' || cap->cmdchar == 'D')
6201 curwin->w_curswant = MAXCOL;
6202 }
6203 cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
6204 nv_operator(cap);
6205}
6206
6207/*
6208 * "s" and "S" commands.
6209 */
6210static void nv_subst(cmdarg_T *cap)
6211{
6212 if (VIsual_active) { /* "vs" and "vS" are the same as "vc" */
6213 if (cap->cmdchar == 'S') {
6214 VIsual_mode_orig = VIsual_mode;
6215 VIsual_mode = 'V';
6216 }
6217 cap->cmdchar = 'c';
6218 nv_operator(cap);
6219 } else
6220 nv_optrans(cap);
6221}
6222
6223/*
6224 * Abbreviated commands.
6225 */
6226static void nv_abbrev(cmdarg_T *cap)
6227{
6228 if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL)
6229 cap->cmdchar = 'x'; /* DEL key behaves like 'x' */
6230
6231 /* in Visual mode these commands are operators */
6232 if (VIsual_active)
6233 v_visop(cap);
6234 else
6235 nv_optrans(cap);
6236}
6237
6238/*
6239 * Translate a command into another command.
6240 */
6241static void nv_optrans(cmdarg_T *cap)
6242{
6243 static const char *(ar[]) = { "dl", "dh", "d$", "c$", "cl", "cc", "yy",
6244 ":s\r" };
6245 static const char *str = "xXDCsSY&";
6246
6247 if (!checkclearopq(cap->oap)) {
6248 if (cap->count0) {
6249 stuffnumReadbuff(cap->count0);
6250 }
6251 stuffReadbuff(ar[strchr(str, (char)cap->cmdchar) - str]);
6252 }
6253 cap->opcount = 0;
6254}
6255
6256/*
6257 * "'" and "`" commands. Also for "g'" and "g`".
6258 * cap->arg is true for "'" and "g'".
6259 */
6260static void nv_gomark(cmdarg_T *cap)
6261{
6262 pos_T *pos;
6263 int c;
6264 pos_T old_cursor = curwin->w_cursor;
6265 const bool old_KeyTyped = KeyTyped; // getting file may reset it
6266
6267 if (cap->cmdchar == 'g')
6268 c = cap->extra_char;
6269 else
6270 c = cap->nchar;
6271 pos = getmark(c, (cap->oap->op_type == OP_NOP));
6272 if (pos == (pos_T *)-1) { /* jumped to other file */
6273 if (cap->arg) {
6274 check_cursor_lnum();
6275 beginline(BL_WHITE | BL_FIX);
6276 } else
6277 check_cursor();
6278 } else
6279 nv_cursormark(cap, cap->arg, pos);
6280
6281 // May need to clear the coladd that a mark includes.
6282 if (!virtual_active()) {
6283 curwin->w_cursor.coladd = 0;
6284 }
6285 check_cursor_col();
6286 if (cap->oap->op_type == OP_NOP
6287 && pos != NULL
6288 && (pos == (pos_T *)-1 || !equalpos(old_cursor, *pos))
6289 && (fdo_flags & FDO_MARK)
6290 && old_KeyTyped) {
6291 foldOpenCursor();
6292 }
6293}
6294
6295/*
6296 * Handle CTRL-O, CTRL-I, "g;" and "g," commands.
6297 */
6298static void nv_pcmark(cmdarg_T *cap)
6299{
6300 pos_T *pos;
6301 linenr_T lnum = curwin->w_cursor.lnum;
6302 const bool old_KeyTyped = KeyTyped; // getting file may reset it
6303
6304 if (!checkclearopq(cap->oap)) {
6305 if (cap->cmdchar == 'g')
6306 pos = movechangelist((int)cap->count1);
6307 else
6308 pos = movemark((int)cap->count1);
6309 if (pos == (pos_T *)-1) { /* jump to other file */
6310 curwin->w_set_curswant = true;
6311 check_cursor();
6312 } else if (pos != NULL) /* can jump */
6313 nv_cursormark(cap, false, pos);
6314 else if (cap->cmdchar == 'g') {
6315 if (curbuf->b_changelistlen == 0)
6316 EMSG(_("E664: changelist is empty"));
6317 else if (cap->count1 < 0)
6318 EMSG(_("E662: At start of changelist"));
6319 else
6320 EMSG(_("E663: At end of changelist"));
6321 } else
6322 clearopbeep(cap->oap);
6323 if (cap->oap->op_type == OP_NOP
6324 && (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum)
6325 && (fdo_flags & FDO_MARK)
6326 && old_KeyTyped)
6327 foldOpenCursor();
6328 }
6329}
6330
6331/*
6332 * Handle '"' command.
6333 */
6334static void nv_regname(cmdarg_T *cap)
6335{
6336 if (checkclearop(cap->oap))
6337 return;
6338 if (cap->nchar == '=')
6339 cap->nchar = get_expr_register();
6340 if (cap->nchar != NUL && valid_yank_reg(cap->nchar, false)) {
6341 cap->oap->regname = cap->nchar;
6342 cap->opcount = cap->count0; /* remember count before '"' */
6343 set_reg_var(cap->oap->regname);
6344 } else
6345 clearopbeep(cap->oap);
6346}
6347
6348/*
6349 * Handle "v", "V" and "CTRL-V" commands.
6350 * Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
6351 * is true.
6352 * Handle CTRL-Q just like CTRL-V.
6353 */
6354static void nv_visual(cmdarg_T *cap)
6355{
6356 if (cap->cmdchar == Ctrl_Q)
6357 cap->cmdchar = Ctrl_V;
6358
6359 /* 'v', 'V' and CTRL-V can be used while an operator is pending to make it
6360 * characterwise, linewise, or blockwise. */
6361 if (cap->oap->op_type != OP_NOP) {
6362 motion_force = cap->oap->motion_force = cap->cmdchar;
6363 finish_op = false; // operator doesn't finish now but later
6364 return;
6365 }
6366
6367 VIsual_select = cap->arg;
6368 if (VIsual_active) { /* change Visual mode */
6369 if (VIsual_mode == cap->cmdchar) /* stop visual mode */
6370 end_visual_mode();
6371 else { /* toggle char/block mode */
6372 /* or char/line mode */
6373 VIsual_mode = cap->cmdchar;
6374 showmode();
6375 }
6376 redraw_curbuf_later(INVERTED); // update the inversion
6377 } else { // start Visual mode
6378 if (cap->count0 > 0 && resel_VIsual_mode != NUL) {
6379 /* use previously selected part */
6380 VIsual = curwin->w_cursor;
6381
6382 VIsual_active = true;
6383 VIsual_reselect = true;
6384 if (!cap->arg)
6385 /* start Select mode when 'selectmode' contains "cmd" */
6386 may_start_select('c');
6387 setmouse();
6388 if (p_smd && msg_silent == 0)
6389 redraw_cmdline = true; /* show visual mode later */
6390 /*
6391 * For V and ^V, we multiply the number of lines even if there
6392 * was only one -- webb
6393 */
6394 if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) {
6395 curwin->w_cursor.lnum +=
6396 resel_VIsual_line_count * cap->count0 - 1;
6397 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
6398 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
6399 }
6400 VIsual_mode = resel_VIsual_mode;
6401 if (VIsual_mode == 'v') {
6402 if (resel_VIsual_line_count <= 1) {
6403 validate_virtcol();
6404 assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
6405 curwin->w_curswant = (curwin->w_virtcol
6406 + resel_VIsual_vcol * (int)cap->count0 - 1);
6407 } else
6408 curwin->w_curswant = resel_VIsual_vcol;
6409 coladvance(curwin->w_curswant);
6410 }
6411 if (resel_VIsual_vcol == MAXCOL) {
6412 curwin->w_curswant = MAXCOL;
6413 coladvance((colnr_T)MAXCOL);
6414 } else if (VIsual_mode == Ctrl_V) {
6415 validate_virtcol();
6416 assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
6417 curwin->w_curswant = (curwin->w_virtcol
6418 + resel_VIsual_vcol * (int)cap->count0 - 1);
6419 coladvance(curwin->w_curswant);
6420 } else
6421 curwin->w_set_curswant = true;
6422 redraw_curbuf_later(INVERTED); /* show the inversion */
6423 } else {
6424 if (!cap->arg)
6425 /* start Select mode when 'selectmode' contains "cmd" */
6426 may_start_select('c');
6427 n_start_visual_mode(cap->cmdchar);
6428 if (VIsual_mode != 'V' && *p_sel == 'e')
6429 ++cap->count1; /* include one more char */
6430 if (cap->count0 > 0 && --cap->count1 > 0) {
6431 /* With a count select that many characters or lines. */
6432 if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V)
6433 nv_right(cap);
6434 else if (VIsual_mode == 'V')
6435 nv_down(cap);
6436 }
6437 }
6438 }
6439}
6440
6441/*
6442 * Start selection for Shift-movement keys.
6443 */
6444void start_selection(void)
6445{
6446 /* if 'selectmode' contains "key", start Select mode */
6447 may_start_select('k');
6448 n_start_visual_mode('v');
6449}
6450
6451/*
6452 * Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
6453 */
6454void may_start_select(int c)
6455{
6456 VIsual_select = (stuff_empty() && typebuf_typed()
6457 && (vim_strchr(p_slm, c) != NULL));
6458}
6459
6460/*
6461 * Start Visual mode "c".
6462 * Should set VIsual_select before calling this.
6463 */
6464static void n_start_visual_mode(int c)
6465{
6466 VIsual_mode = c;
6467 VIsual_active = true;
6468 VIsual_reselect = true;
6469 /* Corner case: the 0 position in a tab may change when going into
6470 * virtualedit. Recalculate curwin->w_cursor to avoid bad hilighting.
6471 */
6472 if (c == Ctrl_V && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB) {
6473 validate_virtcol();
6474 coladvance(curwin->w_virtcol);
6475 }
6476 VIsual = curwin->w_cursor;
6477
6478 foldAdjustVisual();
6479
6480 setmouse();
6481 // Check for redraw after changing the state.
6482 conceal_check_cursor_line();
6483
6484 if (p_smd && msg_silent == 0)
6485 redraw_cmdline = true; /* show visual mode later */
6486
6487 /* Only need to redraw this line, unless still need to redraw an old
6488 * Visual area (when 'lazyredraw' is set). */
6489 if (curwin->w_redr_type < INVERTED) {
6490 curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
6491 curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
6492 }
6493}
6494
6495
6496/*
6497 * CTRL-W: Window commands
6498 */
6499static void nv_window(cmdarg_T *cap)
6500{
6501 if (cap->nchar == ':') {
6502 // "CTRL-W :" is the same as typing ":"; useful in a terminal window
6503 cap->cmdchar = ':';
6504 cap->nchar = NUL;
6505 nv_colon(cap);
6506 } else if (!checkclearop(cap->oap)) {
6507 do_window(cap->nchar, cap->count0, NUL); // everything is in window.c
6508 }
6509}
6510
6511/*
6512 * CTRL-Z: Suspend
6513 */
6514static void nv_suspend(cmdarg_T *cap)
6515{
6516 clearop(cap->oap);
6517 if (VIsual_active)
6518 end_visual_mode(); /* stop Visual mode */
6519 do_cmdline_cmd("st");
6520}
6521
6522/*
6523 * Commands starting with "g".
6524 */
6525static void nv_g_cmd(cmdarg_T *cap)
6526{
6527 oparg_T *oap = cap->oap;
6528 pos_T tpos;
6529 int i;
6530 bool flag = false;
6531
6532 switch (cap->nchar) {
6533 // "g^A/g^X": Sequentially increment visually selected region.
6534 case Ctrl_A:
6535 case Ctrl_X:
6536 if (VIsual_active) {
6537 cap->arg = true;
6538 cap->cmdchar = cap->nchar;
6539 cap->nchar = NUL;
6540 nv_addsub(cap);
6541 } else {
6542 clearopbeep(oap);
6543 }
6544 break;
6545
6546 // "gR": Enter virtual replace mode.
6547 case 'R':
6548 cap->arg = true;
6549 nv_Replace(cap);
6550 break;
6551
6552 case 'r':
6553 nv_vreplace(cap);
6554 break;
6555
6556 case '&':
6557 do_cmdline_cmd("%s//~/&");
6558 break;
6559
6560 /*
6561 * "gv": Reselect the previous Visual area. If Visual already active,
6562 * exchange previous and current Visual area.
6563 */
6564 case 'v':
6565 if (checkclearop(oap))
6566 break;
6567
6568 if ( curbuf->b_visual.vi_start.lnum == 0
6569 || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
6570 || curbuf->b_visual.vi_end.lnum == 0)
6571 beep_flush();
6572 else {
6573 /* set w_cursor to the start of the Visual area, tpos to the end */
6574 if (VIsual_active) {
6575 i = VIsual_mode;
6576 VIsual_mode = curbuf->b_visual.vi_mode;
6577 curbuf->b_visual.vi_mode = i;
6578 curbuf->b_visual_mode_eval = i;
6579 i = curwin->w_curswant;
6580 curwin->w_curswant = curbuf->b_visual.vi_curswant;
6581 curbuf->b_visual.vi_curswant = i;
6582
6583 tpos = curbuf->b_visual.vi_end;
6584 curbuf->b_visual.vi_end = curwin->w_cursor;
6585 curwin->w_cursor = curbuf->b_visual.vi_start;
6586 curbuf->b_visual.vi_start = VIsual;
6587 } else {
6588 VIsual_mode = curbuf->b_visual.vi_mode;
6589 curwin->w_curswant = curbuf->b_visual.vi_curswant;
6590 tpos = curbuf->b_visual.vi_end;
6591 curwin->w_cursor = curbuf->b_visual.vi_start;
6592 }
6593
6594 VIsual_active = true;
6595 VIsual_reselect = true;
6596
6597 /* Set Visual to the start and w_cursor to the end of the Visual
6598 * area. Make sure they are on an existing character. */
6599 check_cursor();
6600 VIsual = curwin->w_cursor;
6601 curwin->w_cursor = tpos;
6602 check_cursor();
6603 update_topline();
6604 /*
6605 * When called from normal "g" command: start Select mode when
6606 * 'selectmode' contains "cmd". When called for K_SELECT, always
6607 * start Select mode.
6608 */
6609 if (cap->arg)
6610 VIsual_select = true;
6611 else
6612 may_start_select('c');
6613 setmouse();
6614 redraw_curbuf_later(INVERTED);
6615 showmode();
6616 }
6617 break;
6618 /*
6619 * "gV": Don't reselect the previous Visual area after a Select mode
6620 * mapping of menu.
6621 */
6622 case 'V':
6623 VIsual_reselect = false;
6624 break;
6625
6626 /*
6627 * "gh": start Select mode.
6628 * "gH": start Select line mode.
6629 * "g^H": start Select block mode.
6630 */
6631 case K_BS:
6632 cap->nchar = Ctrl_H;
6633 FALLTHROUGH;
6634 case 'h':
6635 case 'H':
6636 case Ctrl_H:
6637 cap->cmdchar = cap->nchar + ('v' - 'h');
6638 cap->arg = true;
6639 nv_visual(cap);
6640 break;
6641
6642 /* "gn", "gN" visually select next/previous search match
6643 * "gn" selects next match
6644 * "gN" selects previous match
6645 */
6646 case 'N':
6647 case 'n':
6648 if (!current_search(cap->count1, cap->nchar == 'n'))
6649 clearopbeep(oap);
6650 break;
6651
6652 /*
6653 * "gj" and "gk" two new funny movement keys -- up and down
6654 * movement based on *screen* line rather than *file* line.
6655 */
6656 case 'j':
6657 case K_DOWN:
6658 /* with 'nowrap' it works just like the normal "j" command; also when
6659 * in a closed fold */
6660 if (!curwin->w_p_wrap
6661 || hasFolding(curwin->w_cursor.lnum, NULL, NULL)
6662 ) {
6663 oap->motion_type = kMTLineWise;
6664 i = cursor_down(cap->count1, oap->op_type == OP_NOP);
6665 } else
6666 i = nv_screengo(oap, FORWARD, cap->count1);
6667 if (!i)
6668 clearopbeep(oap);
6669 break;
6670
6671 case 'k':
6672 case K_UP:
6673 /* with 'nowrap' it works just like the normal "k" command; also when
6674 * in a closed fold */
6675 if (!curwin->w_p_wrap
6676 || hasFolding(curwin->w_cursor.lnum, NULL, NULL)
6677 ) {
6678 oap->motion_type = kMTLineWise;
6679 i = cursor_up(cap->count1, oap->op_type == OP_NOP);
6680 } else
6681 i = nv_screengo(oap, BACKWARD, cap->count1);
6682 if (!i)
6683 clearopbeep(oap);
6684 break;
6685
6686 /*
6687 * "gJ": join two lines without inserting a space.
6688 */
6689 case 'J':
6690 nv_join(cap);
6691 break;
6692
6693 /*
6694 * "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
6695 * "gm": middle of "g0" and "g$".
6696 */
6697 case '^':
6698 flag = true;
6699 FALLTHROUGH;
6700
6701 case '0':
6702 case 'm':
6703 case K_HOME:
6704 case K_KHOME:
6705 oap->motion_type = kMTCharWise;
6706 oap->inclusive = false;
6707 if (curwin->w_p_wrap
6708 && curwin->w_width_inner != 0
6709 ) {
6710 int width1 = curwin->w_width_inner - curwin_col_off();
6711 int width2 = width1 + curwin_col_off2();
6712
6713 validate_virtcol();
6714 i = 0;
6715 if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0)
6716 i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
6717 } else
6718 i = curwin->w_leftcol;
6719 /* Go to the middle of the screen line. When 'number' or
6720 * 'relativenumber' is on and lines are wrapping the middle can be more
6721 * to the left. */
6722 if (cap->nchar == 'm') {
6723 i += (curwin->w_width_inner - curwin_col_off()
6724 + ((curwin->w_p_wrap && i > 0)
6725 ? curwin_col_off2() : 0)) / 2;
6726 }
6727 coladvance((colnr_T)i);
6728 if (flag) {
6729 do
6730 i = gchar_cursor();
6731 while (ascii_iswhite(i) && oneright());
6732 }
6733 curwin->w_set_curswant = true;
6734 break;
6735
6736 case '_':
6737 /* "g_": to the last non-blank character in the line or <count> lines
6738 * downward. */
6739 cap->oap->motion_type = kMTCharWise;
6740 cap->oap->inclusive = true;
6741 curwin->w_curswant = MAXCOL;
6742 if (cursor_down(cap->count1 - 1,
6743 cap->oap->op_type == OP_NOP) == false)
6744 clearopbeep(cap->oap);
6745 else {
6746 char_u *ptr = get_cursor_line_ptr();
6747
6748 /* In Visual mode we may end up after the line. */
6749 if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL)
6750 --curwin->w_cursor.col;
6751
6752 /* Decrease the cursor column until it's on a non-blank. */
6753 while (curwin->w_cursor.col > 0
6754 && ascii_iswhite(ptr[curwin->w_cursor.col]))
6755 --curwin->w_cursor.col;
6756 curwin->w_set_curswant = true;
6757 adjust_for_sel(cap);
6758 }
6759 break;
6760
6761 case '$':
6762 case K_END:
6763 case K_KEND:
6764 {
6765 int col_off = curwin_col_off();
6766
6767 oap->motion_type = kMTCharWise;
6768 oap->inclusive = true;
6769 if (curwin->w_p_wrap
6770 && curwin->w_width_inner != 0
6771 ) {
6772 curwin->w_curswant = MAXCOL; /* so we stay at the end */
6773 if (cap->count1 == 1) {
6774 int width1 = curwin->w_width_inner - col_off;
6775 int width2 = width1 + curwin_col_off2();
6776
6777 validate_virtcol();
6778 i = width1 - 1;
6779 if (curwin->w_virtcol >= (colnr_T)width1)
6780 i += ((curwin->w_virtcol - width1) / width2 + 1)
6781 * width2;
6782 coladvance((colnr_T)i);
6783
6784 /* Make sure we stick in this column. */
6785 validate_virtcol();
6786 curwin->w_curswant = curwin->w_virtcol;
6787 curwin->w_set_curswant = false;
6788 if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
6789 /*
6790 * Check for landing on a character that got split at
6791 * the end of the line. We do not want to advance to
6792 * the next screen line.
6793 */
6794 if (curwin->w_virtcol > (colnr_T)i)
6795 --curwin->w_cursor.col;
6796 }
6797 } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false)
6798 clearopbeep(oap);
6799 } else {
6800 i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
6801 coladvance((colnr_T)i);
6802
6803 /* Make sure we stick in this column. */
6804 validate_virtcol();
6805 curwin->w_curswant = curwin->w_virtcol;
6806 curwin->w_set_curswant = false;
6807 }
6808 }
6809 break;
6810
6811 /*
6812 * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
6813 */
6814 case '*':
6815 case '#':
6816#if POUND != '#'
6817 case POUND: /* pound sign (sometimes equal to '#') */
6818#endif
6819 case Ctrl_RSB: /* :tag or :tselect for current identifier */
6820 case ']': /* :tselect for current identifier */
6821 nv_ident(cap);
6822 break;
6823
6824 /*
6825 * ge and gE: go back to end of word
6826 */
6827 case 'e':
6828 case 'E':
6829 oap->motion_type = kMTCharWise;
6830 curwin->w_set_curswant = true;
6831 oap->inclusive = true;
6832 if (bckend_word(cap->count1, cap->nchar == 'E', false) == false)
6833 clearopbeep(oap);
6834 break;
6835
6836 // "g CTRL-G": display info about cursor position
6837 case Ctrl_G:
6838 cursor_pos_info(NULL);
6839 break;
6840
6841 // "gi": start Insert at the last position.
6842 case 'i':
6843 if (curbuf->b_last_insert.mark.lnum != 0) {
6844 curwin->w_cursor = curbuf->b_last_insert.mark;
6845 check_cursor_lnum();
6846 i = (int)STRLEN(get_cursor_line_ptr());
6847 if (curwin->w_cursor.col > (colnr_T)i) {
6848 if (virtual_active())
6849 curwin->w_cursor.coladd += curwin->w_cursor.col - i;
6850 curwin->w_cursor.col = i;
6851 }
6852 }
6853 cap->cmdchar = 'i';
6854 nv_edit(cap);
6855 break;
6856
6857 /*
6858 * "gI": Start insert in column 1.
6859 */
6860 case 'I':
6861 beginline(0);
6862 if (!checkclearopq(oap))
6863 invoke_edit(cap, false, 'g', false);
6864 break;
6865
6866 /*
6867 * "gf": goto file, edit file under cursor
6868 * "]f" and "[f": can also be used.
6869 */
6870 case 'f':
6871 case 'F':
6872 nv_gotofile(cap);
6873 break;
6874
6875 /* "g'm" and "g`m": jump to mark without setting pcmark */
6876 case '\'':
6877 cap->arg = true;
6878 FALLTHROUGH;
6879 case '`':
6880 nv_gomark(cap);
6881 break;
6882
6883 /*
6884 * "gs": Goto sleep.
6885 */
6886 case 's':
6887 do_sleep(cap->count1 * 1000L);
6888 break;
6889
6890 /*
6891 * "ga": Display the ascii value of the character under the
6892 * cursor. It is displayed in decimal, hex, and octal. -- webb
6893 */
6894 case 'a':
6895 do_ascii(NULL);
6896 break;
6897
6898 /*
6899 * "g8": Display the bytes used for the UTF-8 character under the
6900 * cursor. It is displayed in hex.
6901 * "8g8" finds illegal byte sequence.
6902 */
6903 case '8':
6904 if (cap->count0 == 8)
6905 utf_find_illegal();
6906 else
6907 show_utf8();
6908 break;
6909 // "g<": show scrollback text
6910 case '<':
6911 show_sb_text();
6912 break;
6913
6914 /*
6915 * "gg": Goto the first line in file. With a count it goes to
6916 * that line number like for "G". -- webb
6917 */
6918 case 'g':
6919 cap->arg = false;
6920 nv_goto(cap);
6921 break;
6922
6923 /*
6924 * Two-character operators:
6925 * "gq" Format text
6926 * "gw" Format text and keep cursor position
6927 * "g~" Toggle the case of the text.
6928 * "gu" Change text to lower case.
6929 * "gU" Change text to upper case.
6930 * "g?" rot13 encoding
6931 * "g@" call 'operatorfunc'
6932 */
6933 case 'q':
6934 case 'w':
6935 oap->cursor_start = curwin->w_cursor;
6936 FALLTHROUGH;
6937 case '~':
6938 case 'u':
6939 case 'U':
6940 case '?':
6941 case '@':
6942 nv_operator(cap);
6943 break;
6944
6945 /*
6946 * "gd": Find first occurrence of pattern under the cursor in the
6947 * current function
6948 * "gD": idem, but in the current file.
6949 */
6950 case 'd':
6951 case 'D':
6952 nv_gd(oap, cap->nchar, (int)cap->count0);
6953 break;
6954
6955 /*
6956 * g<*Mouse> : <C-*mouse>
6957 */
6958 case K_MIDDLEMOUSE:
6959 case K_MIDDLEDRAG:
6960 case K_MIDDLERELEASE:
6961 case K_LEFTMOUSE:
6962 case K_LEFTDRAG:
6963 case K_LEFTRELEASE:
6964 case K_RIGHTMOUSE:
6965 case K_RIGHTDRAG:
6966 case K_RIGHTRELEASE:
6967 case K_X1MOUSE:
6968 case K_X1DRAG:
6969 case K_X1RELEASE:
6970 case K_X2MOUSE:
6971 case K_X2DRAG:
6972 case K_X2RELEASE:
6973 mod_mask = MOD_MASK_CTRL;
6974 (void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0);
6975 break;
6976
6977 case K_IGNORE:
6978 break;
6979
6980 /*
6981 * "gP" and "gp": same as "P" and "p" but leave cursor just after new text
6982 */
6983 case 'p':
6984 case 'P':
6985 nv_put(cap);
6986 break;
6987
6988 /* "go": goto byte count from start of buffer */
6989 case 'o':
6990 goto_byte(cap->count0);
6991 break;
6992
6993 /* "gQ": improved Ex mode */
6994 case 'Q':
6995 if (text_locked()) {
6996 clearopbeep(cap->oap);
6997 text_locked_msg();
6998 break;
6999 }
7000
7001 if (!checkclearopq(oap))
7002 do_exmode(true);
7003 break;
7004
7005 case ',':
7006 nv_pcmark(cap);
7007 break;
7008
7009 case ';':
7010 cap->count1 = -cap->count1;
7011 nv_pcmark(cap);
7012 break;
7013
7014 case 't':
7015 if (!checkclearop(oap))
7016 goto_tabpage((int)cap->count0);
7017 break;
7018 case 'T':
7019 if (!checkclearop(oap))
7020 goto_tabpage(-(int)cap->count1);
7021 break;
7022
7023 case '+':
7024 case '-': /* "g+" and "g-": undo or redo along the timeline */
7025 if (!checkclearopq(oap))
7026 undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1,
7027 false, false, false);
7028 break;
7029
7030 default:
7031 clearopbeep(oap);
7032 break;
7033 }
7034}
7035
7036/*
7037 * Handle "o" and "O" commands.
7038 */
7039static void n_opencmd(cmdarg_T *cap)
7040{
7041 if (!checkclearopq(cap->oap)) {
7042 if (cap->cmdchar == 'O')
7043 /* Open above the first line of a folded sequence of lines */
7044 (void)hasFolding(curwin->w_cursor.lnum,
7045 &curwin->w_cursor.lnum, NULL);
7046 else
7047 /* Open below the last line of a folded sequence of lines */
7048 (void)hasFolding(curwin->w_cursor.lnum,
7049 NULL, &curwin->w_cursor.lnum);
7050 if (u_save((linenr_T)(curwin->w_cursor.lnum -
7051 (cap->cmdchar == 'O' ? 1 : 0)),
7052 (linenr_T)(curwin->w_cursor.lnum +
7053 (cap->cmdchar == 'o' ? 1 : 0))
7054 )
7055 && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
7056 has_format_option(FO_OPEN_COMS)
7057 ? OPENLINE_DO_COM : 0,
7058 0)) {
7059 if (win_cursorline_standout(curwin)) {
7060 // force redraw of cursorline
7061 curwin->w_valid &= ~VALID_CROW;
7062 }
7063 invoke_edit(cap, false, cap->cmdchar, true);
7064 }
7065 }
7066}
7067
7068/*
7069 * "." command: redo last change.
7070 */
7071static void nv_dot(cmdarg_T *cap)
7072{
7073 if (!checkclearopq(cap->oap)) {
7074 /*
7075 * If "restart_edit" is true, the last but one command is repeated
7076 * instead of the last command (inserting text). This is used for
7077 * CTRL-O <.> in insert mode.
7078 */
7079 if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == false)
7080 clearopbeep(cap->oap);
7081 }
7082}
7083
7084/*
7085 * CTRL-R: undo undo
7086 */
7087static void nv_redo(cmdarg_T *cap)
7088{
7089 if (!checkclearopq(cap->oap)) {
7090 u_redo((int)cap->count1);
7091 curwin->w_set_curswant = true;
7092 }
7093}
7094
7095/*
7096 * Handle "U" command.
7097 */
7098static void nv_Undo(cmdarg_T *cap)
7099{
7100 /* In Visual mode and typing "gUU" triggers an operator */
7101 if (cap->oap->op_type == OP_UPPER
7102 || VIsual_active
7103 ) {
7104 /* translate "gUU" to "gUgU" */
7105 cap->cmdchar = 'g';
7106 cap->nchar = 'U';
7107 nv_operator(cap);
7108 } else if (!checkclearopq(cap->oap)) {
7109 u_undoline();
7110 curwin->w_set_curswant = true;
7111 }
7112}
7113
7114/*
7115 * '~' command: If tilde is not an operator and Visual is off: swap case of a
7116 * single character.
7117 */
7118static void nv_tilde(cmdarg_T *cap)
7119{
7120 if (!p_to
7121 && !VIsual_active
7122 && cap->oap->op_type != OP_TILDE)
7123 n_swapchar(cap);
7124 else
7125 nv_operator(cap);
7126}
7127
7128/*
7129 * Handle an operator command.
7130 * The actual work is done by do_pending_operator().
7131 */
7132static void nv_operator(cmdarg_T *cap)
7133{
7134 int op_type;
7135
7136 op_type = get_op_type(cap->cmdchar, cap->nchar);
7137
7138 if (op_type == cap->oap->op_type) /* double operator works on lines */
7139 nv_lineop(cap);
7140 else if (!checkclearop(cap->oap)) {
7141 cap->oap->start = curwin->w_cursor;
7142 cap->oap->op_type = op_type;
7143 set_op_var(op_type);
7144 }
7145}
7146
7147/*
7148 * Set v:operator to the characters for "optype".
7149 */
7150static void set_op_var(int optype)
7151{
7152 if (optype == OP_NOP) {
7153 set_vim_var_string(VV_OP, NULL, 0);
7154 } else {
7155 char opchars[3];
7156 int opchar0 = get_op_char(optype);
7157 assert(opchar0 >= 0 && opchar0 <= UCHAR_MAX);
7158 opchars[0] = (char) opchar0;
7159
7160 int opchar1 = get_extra_op_char(optype);
7161 assert(opchar1 >= 0 && opchar1 <= UCHAR_MAX);
7162 opchars[1] = (char) opchar1;
7163
7164 opchars[2] = NUL;
7165 set_vim_var_string(VV_OP, opchars, -1);
7166 }
7167}
7168
7169/*
7170 * Handle linewise operator "dd", "yy", etc.
7171 *
7172 * "_" is is a strange motion command that helps make operators more logical.
7173 * It is actually implemented, but not documented in the real Vi. This motion
7174 * command actually refers to "the current line". Commands like "dd" and "yy"
7175 * are really an alternate form of "d_" and "y_". It does accept a count, so
7176 * "d3_" works to delete 3 lines.
7177 */
7178static void nv_lineop(cmdarg_T *cap)
7179{
7180 cap->oap->motion_type = kMTLineWise;
7181 if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false) {
7182 clearopbeep(cap->oap);
7183 } else if ((cap->oap->op_type == OP_DELETE
7184 // only with linewise motions
7185 && cap->oap->motion_force != 'v'
7186 && cap->oap->motion_force != Ctrl_V)
7187 || cap->oap->op_type == OP_LSHIFT
7188 || cap->oap->op_type == OP_RSHIFT) {
7189 beginline(BL_SOL | BL_FIX);
7190 } else if (cap->oap->op_type != OP_YANK) { // 'Y' does not move cursor
7191 beginline(BL_WHITE | BL_FIX);
7192 }
7193}
7194
7195/*
7196 * <Home> command.
7197 */
7198static void nv_home(cmdarg_T *cap)
7199{
7200 /* CTRL-HOME is like "gg" */
7201 if (mod_mask & MOD_MASK_CTRL)
7202 nv_goto(cap);
7203 else {
7204 cap->count0 = 1;
7205 nv_pipe(cap);
7206 }
7207 ins_at_eol = false; /* Don't move cursor past eol (only necessary in a
7208 one-character line). */
7209}
7210
7211/*
7212 * "|" command.
7213 */
7214static void nv_pipe(cmdarg_T *cap)
7215{
7216 cap->oap->motion_type = kMTCharWise;
7217 cap->oap->inclusive = false;
7218 beginline(0);
7219 if (cap->count0 > 0) {
7220 coladvance((colnr_T)(cap->count0 - 1));
7221 curwin->w_curswant = (colnr_T)(cap->count0 - 1);
7222 } else
7223 curwin->w_curswant = 0;
7224 /* keep curswant at the column where we wanted to go, not where
7225 * we ended; differs if line is too short */
7226 curwin->w_set_curswant = false;
7227}
7228
7229/*
7230 * Handle back-word command "b" and "B".
7231 * cap->arg is 1 for "B"
7232 */
7233static void nv_bck_word(cmdarg_T *cap)
7234{
7235 cap->oap->motion_type = kMTCharWise;
7236 cap->oap->inclusive = false;
7237 curwin->w_set_curswant = true;
7238 if (bck_word(cap->count1, cap->arg, false) == false)
7239 clearopbeep(cap->oap);
7240 else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
7241 foldOpenCursor();
7242}
7243
7244/*
7245 * Handle word motion commands "e", "E", "w" and "W".
7246 * cap->arg is true for "E" and "W".
7247 */
7248static void nv_wordcmd(cmdarg_T *cap)
7249{
7250 int n;
7251 bool word_end;
7252 bool flag = false;
7253 pos_T startpos = curwin->w_cursor;
7254
7255 /*
7256 * Set inclusive for the "E" and "e" command.
7257 */
7258 if (cap->cmdchar == 'e' || cap->cmdchar == 'E')
7259 word_end = true;
7260 else
7261 word_end = false;
7262 cap->oap->inclusive = word_end;
7263
7264 /*
7265 * "cw" and "cW" are a special case.
7266 */
7267 if (!word_end && cap->oap->op_type == OP_CHANGE) {
7268 n = gchar_cursor();
7269 if (n != NUL && !ascii_iswhite(n)) {
7270 // This is a little strange. To match what the real Vi does, we
7271 // effectively map "cw" to "ce", and "cW" to "cE", provided that we are
7272 // not on a space or a TAB. This seems impolite at first, but it's
7273 // really more what we mean when we say "cw".
7274 //
7275 // Another strangeness: When standing on the end of a word "ce" will
7276 // change until the end of the next word, but "cw" will change only one
7277 // character! This is done by setting "flag".
7278 if (vim_strchr(p_cpo, CPO_CHANGEW) != NULL) {
7279 cap->oap->inclusive = true;
7280 word_end = true;
7281 }
7282 flag = true;
7283 }
7284 }
7285
7286 cap->oap->motion_type = kMTCharWise;
7287 curwin->w_set_curswant = true;
7288 if (word_end)
7289 n = end_word(cap->count1, cap->arg, flag, false);
7290 else
7291 n = fwd_word(cap->count1, cap->arg, cap->oap->op_type != OP_NOP);
7292
7293 /* Don't leave the cursor on the NUL past the end of line. Unless we
7294 * didn't move it forward. */
7295 if (lt(startpos, curwin->w_cursor))
7296 adjust_cursor(cap->oap);
7297
7298 if (n == false && cap->oap->op_type == OP_NOP)
7299 clearopbeep(cap->oap);
7300 else {
7301 adjust_for_sel(cap);
7302 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
7303 foldOpenCursor();
7304 }
7305}
7306
7307/*
7308 * Used after a movement command: If the cursor ends up on the NUL after the
7309 * end of the line, may move it back to the last character and make the motion
7310 * inclusive.
7311 */
7312static void adjust_cursor(oparg_T *oap)
7313{
7314 /* The cursor cannot remain on the NUL when:
7315 * - the column is > 0
7316 * - not in Visual mode or 'selection' is "o"
7317 * - 'virtualedit' is not "all" and not "onemore".
7318 */
7319 if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
7320 && (!VIsual_active || *p_sel == 'o')
7321 && !virtual_active() && (ve_flags & VE_ONEMORE) == 0
7322 ) {
7323 --curwin->w_cursor.col;
7324 /* prevent cursor from moving on the trail byte */
7325 if (has_mbyte)
7326 mb_adjust_cursor();
7327 oap->inclusive = true;
7328 }
7329}
7330
7331/*
7332 * "0" and "^" commands.
7333 * cap->arg is the argument for beginline().
7334 */
7335static void nv_beginline(cmdarg_T *cap)
7336{
7337 cap->oap->motion_type = kMTCharWise;
7338 cap->oap->inclusive = false;
7339 beginline(cap->arg);
7340 if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
7341 foldOpenCursor();
7342 ins_at_eol = false; /* Don't move cursor past eol (only necessary in a
7343 one-character line). */
7344}
7345
7346/*
7347 * In exclusive Visual mode, may include the last character.
7348 */
7349static void adjust_for_sel(cmdarg_T *cap)
7350{
7351 if (VIsual_active && cap->oap->inclusive && *p_sel == 'e'
7352 && gchar_cursor() != NUL && lt(VIsual, curwin->w_cursor)) {
7353 if (has_mbyte)
7354 inc_cursor();
7355 else
7356 ++curwin->w_cursor.col;
7357 cap->oap->inclusive = false;
7358 }
7359}
7360
7361/*
7362 * Exclude last character at end of Visual area for 'selection' == "exclusive".
7363 * Should check VIsual_mode before calling this.
7364 * Returns true when backed up to the previous line.
7365 */
7366static bool unadjust_for_sel(void)
7367{
7368 pos_T *pp;
7369
7370 if (*p_sel == 'e' && !equalpos(VIsual, curwin->w_cursor)) {
7371 if (lt(VIsual, curwin->w_cursor))
7372 pp = &curwin->w_cursor;
7373 else
7374 pp = &VIsual;
7375 if (pp->coladd > 0) {
7376 pp->coladd--;
7377 } else if (pp->col > 0) {
7378 pp->col--;
7379 mark_mb_adjustpos(curbuf, pp);
7380 } else if (pp->lnum > 1) {
7381 --pp->lnum;
7382 pp->col = (colnr_T)STRLEN(ml_get(pp->lnum));
7383 return true;
7384 }
7385 }
7386 return false;
7387}
7388
7389/*
7390 * SELECT key in Normal or Visual mode: end of Select mode mapping.
7391 */
7392static void nv_select(cmdarg_T *cap)
7393{
7394 if (VIsual_active)
7395 VIsual_select = true;
7396 else if (VIsual_reselect) {
7397 cap->nchar = 'v'; /* fake "gv" command */
7398 cap->arg = true;
7399 nv_g_cmd(cap);
7400 }
7401}
7402
7403
7404/*
7405 * "G", "gg", CTRL-END, CTRL-HOME.
7406 * cap->arg is true for "G".
7407 */
7408static void nv_goto(cmdarg_T *cap)
7409{
7410 linenr_T lnum;
7411
7412 if (cap->arg)
7413 lnum = curbuf->b_ml.ml_line_count;
7414 else
7415 lnum = 1L;
7416 cap->oap->motion_type = kMTLineWise;
7417 setpcmark();
7418
7419 /* When a count is given, use it instead of the default lnum */
7420 if (cap->count0 != 0)
7421 lnum = cap->count0;
7422 if (lnum < 1L)
7423 lnum = 1L;
7424 else if (lnum > curbuf->b_ml.ml_line_count)
7425 lnum = curbuf->b_ml.ml_line_count;
7426 curwin->w_cursor.lnum = lnum;
7427 beginline(BL_SOL | BL_FIX);
7428 if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP)
7429 foldOpenCursor();
7430}
7431
7432/*
7433 * CTRL-\ in Normal mode.
7434 */
7435static void nv_normal(cmdarg_T *cap)
7436{
7437 if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G) {
7438 clearop(cap->oap);
7439 if (restart_edit != 0 && mode_displayed)
7440 clear_cmdline = true; /* unshow mode later */
7441 restart_edit = 0;
7442 if (cmdwin_type != 0)
7443 cmdwin_result = Ctrl_C;
7444 if (VIsual_active) {
7445 end_visual_mode(); /* stop Visual */
7446 redraw_curbuf_later(INVERTED);
7447 }
7448 /* CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set. */
7449 if (cap->nchar == Ctrl_G && p_im)
7450 restart_edit = 'a';
7451 } else
7452 clearopbeep(cap->oap);
7453}
7454
7455/*
7456 * ESC in Normal mode: beep, but don't flush buffers.
7457 * Don't even beep if we are canceling a command.
7458 */
7459static void nv_esc(cmdarg_T *cap)
7460{
7461 int no_reason;
7462
7463 no_reason = (cap->oap->op_type == OP_NOP
7464 && cap->opcount == 0
7465 && cap->count0 == 0
7466 && cap->oap->regname == 0
7467 && !p_im);
7468
7469 if (cap->arg) { /* true for CTRL-C */
7470 if (restart_edit == 0
7471 && cmdwin_type == 0
7472 && !VIsual_active
7473 && no_reason) {
7474 if (anyBufIsChanged()) {
7475 MSG(_("Type :qa! and press <Enter> to abandon all changes"
7476 " and exit Nvim"));
7477 } else {
7478 MSG(_("Type :qa and press <Enter> to exit Nvim"));
7479 }
7480 }
7481
7482 /* Don't reset "restart_edit" when 'insertmode' is set, it won't be
7483 * set again below when halfway through a mapping. */
7484 if (!p_im)
7485 restart_edit = 0;
7486 if (cmdwin_type != 0) {
7487 cmdwin_result = K_IGNORE;
7488 got_int = false; /* don't stop executing autocommands et al. */
7489 return;
7490 }
7491 }
7492
7493 if (VIsual_active) {
7494 end_visual_mode(); /* stop Visual */
7495 check_cursor_col(); /* make sure cursor is not beyond EOL */
7496 curwin->w_set_curswant = true;
7497 redraw_curbuf_later(INVERTED);
7498 } else if (no_reason) {
7499 vim_beep(BO_ESC);
7500 }
7501 clearop(cap->oap);
7502
7503 /* A CTRL-C is often used at the start of a menu. When 'insertmode' is
7504 * set return to Insert mode afterwards. */
7505 if (restart_edit == 0 && goto_im()
7506 && ex_normal_busy == 0
7507 )
7508 restart_edit = 'a';
7509}
7510
7511// Move the cursor for the "A" command.
7512void set_cursor_for_append_to_line(void)
7513{
7514 curwin->w_set_curswant = true;
7515 if (ve_flags == VE_ALL) {
7516 const int save_State = State;
7517
7518 // Pretend Insert mode here to allow the cursor on the
7519 // character past the end of the line
7520 State = INSERT;
7521 coladvance((colnr_T)MAXCOL);
7522 State = save_State;
7523 } else {
7524 curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr());
7525 }
7526}
7527
7528/// Handle "A", "a", "I", "i" and <Insert> commands.
7529static void nv_edit(cmdarg_T *cap)
7530{
7531 // <Insert> is equal to "i"
7532 if (cap->cmdchar == K_INS || cap->cmdchar == K_KINS) {
7533 cap->cmdchar = 'i';
7534 }
7535
7536 // in Visual mode "A" and "I" are an operator
7537 if (VIsual_active && (cap->cmdchar == 'A' || cap->cmdchar == 'I')) {
7538 v_visop(cap);
7539 // in Visual mode and after an operator "a" and "i" are for text objects
7540 } else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
7541 && (cap->oap->op_type != OP_NOP || VIsual_active)) {
7542 nv_object(cap);
7543 } else if (!curbuf->b_p_ma && !p_im && !curbuf->terminal) {
7544 // Only give this error when 'insertmode' is off.
7545 EMSG(_(e_modifiable));
7546 clearop(cap->oap);
7547 } else if (!checkclearopq(cap->oap)) {
7548 switch (cap->cmdchar) {
7549 case 'A': // "A"ppend after the line
7550 set_cursor_for_append_to_line();
7551 break;
7552
7553 case 'I': /* "I"nsert before the first non-blank */
7554 beginline(BL_WHITE);
7555 break;
7556
7557 case 'a': /* "a"ppend is like "i"nsert on the next character. */
7558 /* increment coladd when in virtual space, increment the
7559 * column otherwise, also to append after an unprintable char */
7560 if (virtual_active()
7561 && (curwin->w_cursor.coladd > 0
7562 || *get_cursor_pos_ptr() == NUL
7563 || *get_cursor_pos_ptr() == TAB))
7564 curwin->w_cursor.coladd++;
7565 else if (*get_cursor_pos_ptr() != NUL)
7566 inc_cursor();
7567 break;
7568 }
7569
7570 if (curwin->w_cursor.coladd && cap->cmdchar != 'A') {
7571 int save_State = State;
7572
7573 /* Pretend Insert mode here to allow the cursor on the
7574 * character past the end of the line */
7575 State = INSERT;
7576 coladvance(getviscol());
7577 State = save_State;
7578 }
7579
7580 invoke_edit(cap, false, cap->cmdchar, false);
7581 }
7582}
7583
7584/*
7585 * Invoke edit() and take care of "restart_edit" and the return value.
7586 */
7587static void
7588invoke_edit (
7589 cmdarg_T *cap,
7590 int repl, /* "r" or "gr" command */
7591 int cmd,
7592 int startln
7593)
7594{
7595 int restart_edit_save = 0;
7596
7597 /* Complicated: When the user types "a<C-O>a" we don't want to do Insert
7598 * mode recursively. But when doing "a<C-O>." or "a<C-O>rx" we do allow
7599 * it. */
7600 if (repl || !stuff_empty())
7601 restart_edit_save = restart_edit;
7602 else
7603 restart_edit_save = 0;
7604
7605 /* Always reset "restart_edit", this is not a restarted edit. */
7606 restart_edit = 0;
7607
7608 if (edit(cmd, startln, cap->count1))
7609 cap->retval |= CA_COMMAND_BUSY;
7610
7611 if (restart_edit == 0)
7612 restart_edit = restart_edit_save;
7613}
7614
7615/*
7616 * "a" or "i" while an operator is pending or in Visual mode: object motion.
7617 */
7618static void nv_object(cmdarg_T *cap)
7619{
7620 bool flag;
7621 bool include;
7622 char_u *mps_save;
7623
7624 if (cap->cmdchar == 'i')
7625 include = false; /* "ix" = inner object: exclude white space */
7626 else
7627 include = true; /* "ax" = an object: include white space */
7628
7629 /* Make sure (), [], {} and <> are in 'matchpairs' */
7630 mps_save = curbuf->b_p_mps;
7631 curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>";
7632
7633 switch (cap->nchar) {
7634 case 'w': /* "aw" = a word */
7635 flag = current_word(cap->oap, cap->count1, include, false);
7636 break;
7637 case 'W': /* "aW" = a WORD */
7638 flag = current_word(cap->oap, cap->count1, include, true);
7639 break;
7640 case 'b': /* "ab" = a braces block */
7641 case '(':
7642 case ')':
7643 flag = current_block(cap->oap, cap->count1, include, '(', ')');
7644 break;
7645 case 'B': /* "aB" = a Brackets block */
7646 case '{':
7647 case '}':
7648 flag = current_block(cap->oap, cap->count1, include, '{', '}');
7649 break;
7650 case '[': /* "a[" = a [] block */
7651 case ']':
7652 flag = current_block(cap->oap, cap->count1, include, '[', ']');
7653 break;
7654 case '<': /* "a<" = a <> block */
7655 case '>':
7656 flag = current_block(cap->oap, cap->count1, include, '<', '>');
7657 break;
7658 case 't': /* "at" = a tag block (xml and html) */
7659 // Do not adjust oap->end in do_pending_operator()
7660 // otherwise there are different results for 'dit'
7661 // (note leading whitespace in last line):
7662 // 1) <b> 2) <b>
7663 // foobar foobar
7664 // </b> </b>
7665 cap->retval |= CA_NO_ADJ_OP_END;
7666 flag = current_tagblock(cap->oap, cap->count1, include);
7667 break;
7668 case 'p': /* "ap" = a paragraph */
7669 flag = current_par(cap->oap, cap->count1, include, 'p');
7670 break;
7671 case 's': /* "as" = a sentence */
7672 flag = current_sent(cap->oap, cap->count1, include);
7673 break;
7674 case '"': /* "a"" = a double quoted string */
7675 case '\'': /* "a'" = a single quoted string */
7676 case '`': /* "a`" = a backtick quoted string */
7677 flag = current_quote(cap->oap, cap->count1, include,
7678 cap->nchar);
7679 break;
7680 default:
7681 flag = false;
7682 break;
7683 }
7684
7685 curbuf->b_p_mps = mps_save;
7686 if (!flag)
7687 clearopbeep(cap->oap);
7688 adjust_cursor_col();
7689 curwin->w_set_curswant = true;
7690}
7691
7692/*
7693 * "q" command: Start/stop recording.
7694 * "q:", "q/", "q?": edit command-line in command-line window.
7695 */
7696static void nv_record(cmdarg_T *cap)
7697{
7698 if (cap->oap->op_type == OP_FORMAT) {
7699 /* "gqq" is the same as "gqgq": format line */
7700 cap->cmdchar = 'g';
7701 cap->nchar = 'q';
7702 nv_operator(cap);
7703 } else if (!checkclearop(cap->oap)) {
7704 if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?') {
7705 stuffcharReadbuff(cap->nchar);
7706 stuffcharReadbuff(K_CMDWIN);
7707 } else {
7708 // (stop) recording into a named register, unless executing a
7709 // register.
7710 if (reg_executing == 0 && do_record(cap->nchar) == FAIL) {
7711 clearopbeep(cap->oap);
7712 }
7713 }
7714 }
7715}
7716
7717/*
7718 * Handle the "@r" command.
7719 */
7720static void nv_at(cmdarg_T *cap)
7721{
7722 if (checkclearop(cap->oap))
7723 return;
7724 if (cap->nchar == '=') {
7725 if (get_expr_register() == NUL)
7726 return;
7727 }
7728 while (cap->count1-- && !got_int) {
7729 if (do_execreg(cap->nchar, false, false, false) == false) {
7730 clearopbeep(cap->oap);
7731 break;
7732 }
7733 line_breakcheck();
7734 }
7735}
7736
7737/*
7738 * Handle the CTRL-U and CTRL-D commands.
7739 */
7740static void nv_halfpage(cmdarg_T *cap)
7741{
7742 if ((cap->cmdchar == Ctrl_U && curwin->w_cursor.lnum == 1)
7743 || (cap->cmdchar == Ctrl_D
7744 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count))
7745 clearopbeep(cap->oap);
7746 else if (!checkclearop(cap->oap))
7747 halfpage(cap->cmdchar == Ctrl_D, cap->count0);
7748}
7749
7750/*
7751 * Handle "J" or "gJ" command.
7752 */
7753static void nv_join(cmdarg_T *cap)
7754{
7755 if (VIsual_active) { // join the visual lines
7756 nv_operator(cap);
7757 } else if (!checkclearop(cap->oap)) {
7758 if (cap->count0 <= 1) {
7759 cap->count0 = 2; // default for join is two lines!
7760 }
7761 if (curwin->w_cursor.lnum + cap->count0 - 1 >
7762 curbuf->b_ml.ml_line_count) {
7763 // can't join when on the last line
7764 if (cap->count0 <= 2) {
7765 clearopbeep(cap->oap);
7766 return;
7767 }
7768 cap->count0 = curbuf->b_ml.ml_line_count - curwin->w_cursor.lnum + 1;
7769 }
7770
7771 prep_redo(cap->oap->regname, cap->count0,
7772 NUL, cap->cmdchar, NUL, NUL, cap->nchar);
7773 do_join((size_t)cap->count0, cap->nchar == NUL, true, true, true);
7774 }
7775}
7776
7777/*
7778 * "P", "gP", "p" and "gp" commands.
7779 */
7780static void nv_put(cmdarg_T *cap)
7781{
7782 nv_put_opt(cap, false);
7783}
7784
7785// "P", "gP", "p" and "gp" commands.
7786// "fix_indent" is true for "[p", "[P", "]p" and "]P".
7787static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
7788{
7789 int regname = 0;
7790 yankreg_T *savereg = NULL;
7791 bool empty = false;
7792 bool was_visual = false;
7793 int dir;
7794 int flags = 0;
7795
7796 if (cap->oap->op_type != OP_NOP) {
7797 /* "dp" is ":diffput" */
7798 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'p') {
7799 clearop(cap->oap);
7800 assert(cap->opcount >= 0);
7801 nv_diffgetput(true, (size_t)cap->opcount);
7802 } else
7803 clearopbeep(cap->oap);
7804 } else {
7805 if (fix_indent) {
7806 dir = (cap->cmdchar == ']' && cap->nchar == 'p')
7807 ? FORWARD : BACKWARD;
7808 flags |= PUT_FIXINDENT;
7809 } else {
7810 dir = (cap->cmdchar == 'P'
7811 || (cap->cmdchar == 'g' && cap->nchar == 'P'))
7812 ? BACKWARD : FORWARD;
7813 }
7814 prep_redo_cmd(cap);
7815 if (cap->cmdchar == 'g')
7816 flags |= PUT_CURSEND;
7817
7818 if (VIsual_active) {
7819 /* Putting in Visual mode: The put text replaces the selected
7820 * text. First delete the selected text, then put the new text.
7821 * Need to save and restore the registers that the delete
7822 * overwrites if the old contents is being put.
7823 */
7824 was_visual = true;
7825 regname = cap->oap->regname;
7826 // '+' and '*' could be the same selection
7827 bool clipoverwrite = (regname == '+' || regname == '*')
7828 && (cb_flags & CB_UNNAMEDMASK);
7829 if (regname == 0 || regname == '"' || clipoverwrite
7830 || ascii_isdigit(regname) || regname == '-') {
7831 // The delete might overwrite the register we want to put, save it first
7832 savereg = copy_register(regname);
7833 }
7834
7835 // To place the cursor correctly after a blockwise put, and to leave the
7836 // text in the correct position when putting over a selection with
7837 // 'virtualedit' and past the end of the line, we use the 'c' operator in
7838 // do_put(), which requires the visual selection to still be active.
7839 if (!VIsual_active || VIsual_mode == 'V' || regname != '.') {
7840 // Now delete the selected text. Avoid messages here.
7841 cap->cmdchar = 'd';
7842 cap->nchar = NUL;
7843 cap->oap->regname = NUL;
7844 msg_silent++;
7845 nv_operator(cap);
7846 do_pending_operator(cap, 0, false);
7847 empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
7848 msg_silent--;
7849
7850 // delete PUT_LINE_BACKWARD;
7851 cap->oap->regname = regname;
7852 }
7853
7854 /* When deleted a linewise Visual area, put the register as
7855 * lines to avoid it joined with the next line. When deletion was
7856 * characterwise, split a line when putting lines. */
7857 if (VIsual_mode == 'V')
7858 flags |= PUT_LINE;
7859 else if (VIsual_mode == 'v')
7860 flags |= PUT_LINE_SPLIT;
7861 if (VIsual_mode == Ctrl_V && dir == FORWARD)
7862 flags |= PUT_LINE_FORWARD;
7863 dir = BACKWARD;
7864 if ((VIsual_mode != 'V'
7865 && curwin->w_cursor.col < curbuf->b_op_start.col)
7866 || (VIsual_mode == 'V'
7867 && curwin->w_cursor.lnum < curbuf->b_op_start.lnum))
7868 /* cursor is at the end of the line or end of file, put
7869 * forward. */
7870 dir = FORWARD;
7871 /* May have been reset in do_put(). */
7872 VIsual_active = true;
7873 }
7874 do_put(cap->oap->regname, savereg, dir, cap->count1, flags);
7875
7876 // If a register was saved, free it
7877 if (savereg != NULL) {
7878 free_register(savereg);
7879 xfree(savereg);
7880 }
7881
7882 /* What to reselect with "gv"? Selecting the just put text seems to
7883 * be the most useful, since the original text was removed. */
7884 if (was_visual) {
7885 curbuf->b_visual.vi_start = curbuf->b_op_start;
7886 curbuf->b_visual.vi_end = curbuf->b_op_end;
7887 // need to adjust cursor position
7888 if (*p_sel == 'e') {
7889 inc(&curbuf->b_visual.vi_end);
7890 }
7891 }
7892
7893 /* When all lines were selected and deleted do_put() leaves an empty
7894 * line that needs to be deleted now. */
7895 if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL) {
7896 ml_delete(curbuf->b_ml.ml_line_count, true);
7897 deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
7898
7899 /* If the cursor was in that line, move it to the end of the last
7900 * line. */
7901 if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
7902 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
7903 coladvance((colnr_T)MAXCOL);
7904 }
7905 }
7906 auto_format(false, true);
7907 }
7908}
7909
7910/*
7911 * "o" and "O" commands.
7912 */
7913static void nv_open(cmdarg_T *cap)
7914{
7915 /* "do" is ":diffget" */
7916 if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'o') {
7917 clearop(cap->oap);
7918 assert(cap->opcount >= 0);
7919 nv_diffgetput(false, (size_t)cap->opcount);
7920 } else if (VIsual_active) /* switch start and end of visual */
7921 v_swap_corners(cap->cmdchar);
7922 else
7923 n_opencmd(cap);
7924}
7925
7926// Calculate start/end virtual columns for operating in block mode.
7927static void get_op_vcol(
7928 oparg_T *oap,
7929 colnr_T redo_VIsual_vcol,
7930 bool initial // when true: adjust position for 'selectmode'
7931)
7932{
7933 colnr_T start;
7934 colnr_T end;
7935
7936 if (VIsual_mode != Ctrl_V
7937 || (!initial && oap->end.col < curwin->w_width_inner)) {
7938 return;
7939 }
7940
7941 oap->motion_type = kMTBlockWise;
7942
7943 // prevent from moving onto a trail byte
7944 if (has_mbyte) {
7945 mark_mb_adjustpos(curwin->w_buffer, &oap->end);
7946 }
7947
7948 getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol);
7949 if (!redo_VIsual_busy) {
7950 getvvcol(curwin, &(oap->end), &start, NULL, &end);
7951
7952 if (start < oap->start_vcol) {
7953 oap->start_vcol = start;
7954 }
7955 if (end > oap->end_vcol) {
7956 if (initial && *p_sel == 'e'
7957 && start >= 1
7958 && start - 1 >= oap->end_vcol) {
7959 oap->end_vcol = start - 1;
7960 } else {
7961 oap->end_vcol = end;
7962 }
7963 }
7964 }
7965
7966 // if '$' was used, get oap->end_vcol from longest line
7967 if (curwin->w_curswant == MAXCOL) {
7968 curwin->w_cursor.col = MAXCOL;
7969 oap->end_vcol = 0;
7970 for (curwin->w_cursor.lnum = oap->start.lnum;
7971 curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) {
7972 getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
7973 if (end > oap->end_vcol) {
7974 oap->end_vcol = end;
7975 }
7976 }
7977 } else if (redo_VIsual_busy) {
7978 oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1;
7979 }
7980
7981 // Correct oap->end.col and oap->start.col to be the
7982 // upper-left and lower-right corner of the block area.
7983 //
7984 // (Actually, this does convert column positions into character
7985 // positions)
7986 curwin->w_cursor.lnum = oap->end.lnum;
7987 coladvance(oap->end_vcol);
7988 oap->end = curwin->w_cursor;
7989
7990 curwin->w_cursor = oap->start;
7991 coladvance(oap->start_vcol);
7992 oap->start = curwin->w_cursor;
7993}
7994
7995// Handle an arbitrary event in normal mode
7996static void nv_event(cmdarg_T *cap)
7997{
7998 // Garbage collection should have been executed before blocking for events in
7999 // the `os_inchar` in `state_enter`, but we also disable it here in case the
8000 // `os_inchar` branch was not executed (!multiqueue_empty(loop.events), which
8001 // could have `may_garbage_collect` set to true in `normal_check`).
8002 //
8003 // That is because here we may run code that calls `os_inchar`
8004 // later(`f_confirm` or `get_keystroke` for example), but in these cases it is
8005 // not safe to perform garbage collection because there could be unreferenced
8006 // lists or dicts being used.
8007 may_garbage_collect = false;
8008 bool may_restart = (restart_edit != 0);
8009 multiqueue_process_events(main_loop.events);
8010 finish_op = false;
8011 if (may_restart) {
8012 // Tricky: if restart_edit was set before the handler we are in ctrl-o mode,
8013 // but if not, the event should be allowed to trigger :startinsert.
8014 cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
8015 }
8016}
8017
8018/*
8019 * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos".
8020 */
8021static int mouse_model_popup(void)
8022{
8023 return p_mousem[0] == 'p';
8024}
8025
8026void normal_cmd(oparg_T *oap, bool toplevel)
8027{
8028 NormalState s;
8029 normal_state_init(&s);
8030 s.toplevel = toplevel;
8031 s.oa = *oap;
8032 normal_prepare(&s);
8033 (void)normal_execute(&s.state, safe_vgetc());
8034 *oap = s.oa;
8035}
8036