diff options
-rw-r--r-- | runtime/doc/repeat.txt | 13 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 76 | ||||
-rw-r--r-- | src/nvim/keymap.h | 252 | ||||
-rw-r--r-- | src/nvim/testdir/test_global.vim | 9 | ||||
-rw-r--r-- | test/functional/api/command_spec.lua | 6 |
5 files changed, 195 insertions, 161 deletions
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 421ebab100..42889cca50 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -37,7 +37,7 @@ of area is used, see |visual-repeat|. ============================================================================== 2. Multiple repeats *multi-repeat* - *:g* *:global* *E147* *E148* + *:g* *:global* *E148* :[range]g[lobal]/{pattern}/[cmd] Execute the Ex command [cmd] (default ":p") on the lines within [range] where {pattern} matches. @@ -70,8 +70,15 @@ The default for [range] is the whole buffer (1,$). Use "CTRL-C" to interrupt the command. If an error message is given for a line, the command for that line is aborted and the global command continues with the next marked or unmarked line. - -To repeat a non-Ex command, you can use the ":normal" command: > + *E147* +When the command is used recursively, it only works on one line. Giving a +range is then not allowed. This is useful to find all lines that match a +pattern and do not match another pattern: > + :g/found/v/notfound/{cmd} +This first finds all lines containing "found", but only executes {cmd} when +there is no match for "notfound". + +To execute a non-Ex command, you can use the `:normal` command: > :g/pat/normal {commands} Make sure that {commands} ends with a whole command, otherwise Vim will wait for you to type the rest of the command for each match. The screen will not diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 0b30132eb8..115df815c6 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4176,6 +4176,17 @@ do_sub_msg ( return false; } +static void global_exe_one(char_u *const cmd, const linenr_T lnum) +{ + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; + if (*cmd == NUL || *cmd == '\n') { + do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT); + } else { + do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT); + } +} + /* * Execute a global command of the form: * @@ -4205,8 +4216,12 @@ void ex_global(exarg_T *eap) int match; int which_pat; - if (global_busy) { - EMSG(_("E147: Cannot do :global recursive")); /* will increment global_busy */ + // When nesting the command works on one line. This allows for + // ":g/found/v/notfound/command". + if (global_busy && (eap->line1 != 1 + || eap->line2 != curbuf->b_ml.ml_line_count)) { + // will increment global_busy to break out of the loop + EMSG(_("E147: Cannot do :global recursive with a range")); return; } @@ -4255,35 +4270,40 @@ void ex_global(exarg_T *eap) return; } - /* - * pass 1: set marks for each (not) matching line - */ - for (lnum = eap->line1; lnum <= eap->line2 && !got_int; ++lnum) { - /* a match on this line? */ + if (global_busy) { + lnum = curwin->w_cursor.lnum; match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + (colnr_T)0, NULL); if ((type == 'g' && match) || (type == 'v' && !match)) { - ml_setmarked(lnum); - ndone++; + global_exe_one(cmd, lnum); + } + } else { + // pass 1: set marks for each (not) matching line + for (lnum = eap->line1; lnum <= eap->line2 && !got_int; lnum++) { + // a match on this line? + match = vim_regexec_multi(®match, curwin, curbuf, lnum, + (colnr_T)0, NULL); + if ((type == 'g' && match) || (type == 'v' && !match)) { + ml_setmarked(lnum); + ndone++; + } + line_breakcheck(); } - line_breakcheck(); - } - /* - * pass 2: execute the command for each line that has been marked - */ - if (got_int) - MSG(_(e_interr)); - else if (ndone == 0) { - if (type == 'v') { - smsg(_("Pattern found in every line: %s"), pat); + // pass 2: execute the command for each line that has been marked + if (got_int) { + MSG(_(e_interr)); + } else if (ndone == 0) { + if (type == 'v') { + smsg(_("Pattern found in every line: %s"), pat); + } else { + smsg(_("Pattern not found: %s"), pat); + } } else { - smsg(_("Pattern not found: %s"), pat); + global_exe(cmd); } - } else { - global_exe(cmd); + ml_clearmarked(); // clear rest of the marks } - ml_clearmarked(); /* clear rest of the marks */ vim_regfree(regmatch.regprog); } @@ -4312,13 +4332,7 @@ void global_exe(char_u *cmd) old_lcount = curbuf->b_ml.ml_line_count; while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1) { - curwin->w_cursor.lnum = lnum; - curwin->w_cursor.col = 0; - if (*cmd == NUL || *cmd == '\n') { - do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT); - } else { - do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT); - } + global_exe_one(cmd, lnum); os_breakcheck(); } diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index c64691e8ea..baf8963aa8 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -115,135 +115,139 @@ KS_ZERO ? K_ZERO : TERMCAP2KEY(a, b)) // Codes for keys that do not have a termcap name. +// The numbers are fixed to make sure that recorded key sequences remain valid. +// Add new entries at the end, not halfway. // // K_SPECIAL KS_EXTRA KE_xxx // // Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). enum key_extra { - KE_NAME = 3 /* name of this terminal entry */ - - , KE_S_UP /* shift-up */ - , KE_S_DOWN /* shift-down */ - - , KE_S_F1 /* shifted function keys */ - , KE_S_F2 - , KE_S_F3 - , KE_S_F4 - , KE_S_F5 - , KE_S_F6 - , KE_S_F7 - , KE_S_F8 - , KE_S_F9 - , KE_S_F10 - - , KE_S_F11 - , KE_S_F12 - , KE_S_F13 - , KE_S_F14 - , KE_S_F15 - , KE_S_F16 - , KE_S_F17 - , KE_S_F18 - , KE_S_F19 - , KE_S_F20 - - , KE_S_F21 - , KE_S_F22 - , KE_S_F23 - , KE_S_F24 - , KE_S_F25 - , KE_S_F26 - , KE_S_F27 - , KE_S_F28 - , KE_S_F29 - , KE_S_F30 - - , KE_S_F31 - , KE_S_F32 - , KE_S_F33 - , KE_S_F34 - , KE_S_F35 - , KE_S_F36 - , KE_S_F37 - - , KE_MOUSE /* mouse event start */ - - /* - * Symbols for pseudo keys which are translated from the real key symbols - * above. - */ - , KE_LEFTMOUSE /* Left mouse button click */ - , KE_LEFTDRAG /* Drag with left mouse button down */ - , KE_LEFTRELEASE /* Left mouse button release */ - , KE_MIDDLEMOUSE /* Middle mouse button click */ - , KE_MIDDLEDRAG /* Drag with middle mouse button down */ - , KE_MIDDLERELEASE /* Middle mouse button release */ - , KE_RIGHTMOUSE /* Right mouse button click */ - , KE_RIGHTDRAG /* Drag with right mouse button down */ - , KE_RIGHTRELEASE /* Right mouse button release */ - - , KE_IGNORE /* Ignored mouse drag/release */ - - , KE_TAB /* unshifted TAB key */ - , KE_S_TAB_OLD /* shifted TAB key (no longer used) */ - - , KE_XF1 /* extra vt100 function keys for xterm */ - , KE_XF2 - , KE_XF3 - , KE_XF4 - , KE_XEND /* extra (vt100) end key for xterm */ - , KE_ZEND /* extra (vt100) end key for xterm */ - , KE_XHOME /* extra (vt100) home key for xterm */ - , KE_ZHOME /* extra (vt100) home key for xterm */ - , KE_XUP /* extra vt100 cursor keys for xterm */ - , KE_XDOWN - , KE_XLEFT - , KE_XRIGHT - - , KE_LEFTMOUSE_NM /* non-mappable Left mouse button click */ - , KE_LEFTRELEASE_NM /* non-mappable left mouse button release */ - - , KE_S_XF1 /* extra vt100 shifted function keys for xterm */ - , KE_S_XF2 - , KE_S_XF3 - , KE_S_XF4 - - /* NOTE: The scroll wheel events are inverted: i.e. UP is the same as - * moving the actual scroll wheel down, LEFT is the same as moving the - * scroll wheel right. */ - , KE_MOUSEDOWN /* scroll wheel pseudo-button Down */ - , KE_MOUSEUP /* scroll wheel pseudo-button Up */ - , KE_MOUSELEFT /* scroll wheel pseudo-button Left */ - , KE_MOUSERIGHT /* scroll wheel pseudo-button Right */ - - , KE_KINS /* keypad Insert key */ - , KE_KDEL /* keypad Delete key */ - - , KE_CSI /* CSI typed directly */ - , KE_SNR /* <SNR> */ - , KE_PLUG /* <Plug> */ - , KE_CMDWIN /* open command-line window from Command-line Mode */ - - , KE_C_LEFT /* control-left */ - , KE_C_RIGHT /* control-right */ - , KE_C_HOME /* control-home */ - , KE_C_END /* control-end */ - - , KE_X1MOUSE /* X1/X2 mouse-buttons */ - , KE_X1DRAG - , KE_X1RELEASE - , KE_X2MOUSE - , KE_X2DRAG - , KE_X2RELEASE - - , KE_DROP /* DnD data is available */ - , KE_NOP /* doesn't do something */ - , KE_FOCUSGAINED /* focus gained */ - , KE_FOCUSLOST /* focus lost */ - , KE_EVENT // event - , KE_PASTE // special key to toggle the 'paste' option. - // sent only by UIs - , KE_COMMAND // special key to execute command in any mode + KE_NAME = 3 // name of this terminal entry + + , KE_S_UP = 4 // shift-up + , KE_S_DOWN = 5 // shift-down + + , KE_S_F1 = 6 // shifted function keys + , KE_S_F2 = 7 + , KE_S_F3 = 8 + , KE_S_F4 = 9 + , KE_S_F5 = 10 + , KE_S_F6 = 11 + , KE_S_F7 = 12 + , KE_S_F8 = 13 + , KE_S_F9 = 14 + , KE_S_F10 = 15 + + , KE_S_F11 = 16 + , KE_S_F12 = 17 + , KE_S_F13 = 18 + , KE_S_F14 = 19 + , KE_S_F15 = 20 + , KE_S_F16 = 21 + , KE_S_F17 = 22 + , KE_S_F18 = 23 + , KE_S_F19 = 24 + , KE_S_F20 = 25 + + , KE_S_F21 = 26 + , KE_S_F22 = 27 + , KE_S_F23 = 28 + , KE_S_F24 = 29 + , KE_S_F25 = 30 + , KE_S_F26 = 31 + , KE_S_F27 = 32 + , KE_S_F28 = 33 + , KE_S_F29 = 34 + , KE_S_F30 = 35 + + , KE_S_F31 = 36 + , KE_S_F32 = 37 + , KE_S_F33 = 38 + , KE_S_F34 = 39 + , KE_S_F35 = 40 + , KE_S_F36 = 41 + , KE_S_F37 = 42 + + , KE_MOUSE = 43 // mouse event start + + // Symbols for pseudo keys which are translated from the real key symbols + // above. + , KE_LEFTMOUSE = 44 // Left mouse button click + , KE_LEFTDRAG = 45 // Drag with left mouse button down + , KE_LEFTRELEASE = 46 // Left mouse button release + , KE_MIDDLEMOUSE = 47 // Middle mouse button click + , KE_MIDDLEDRAG = 48 // Drag with middle mouse button down + , KE_MIDDLERELEASE = 49 // Middle mouse button release + , KE_RIGHTMOUSE = 50 // Right mouse button click + , KE_RIGHTDRAG = 51 // Drag with right mouse button down + , KE_RIGHTRELEASE = 52 // Right mouse button release + + , KE_IGNORE = 53 // Ignored mouse drag/release + + , KE_TAB = 54 // unshifted TAB key + , KE_S_TAB_OLD = 55 // shifted TAB key (no longer used) + + // , KE_SNIFF_UNUSED = 56 // obsolete + , KE_XF1 = 57 // extra vt100 function keys for xterm + , KE_XF2 = 58 + , KE_XF3 = 59 + , KE_XF4 = 60 + , KE_XEND = 61 // extra (vt100) end key for xterm + , KE_ZEND = 62 // extra (vt100) end key for xterm + , KE_XHOME = 63 // extra (vt100) home key for xterm + , KE_ZHOME = 64 // extra (vt100) home key for xterm + , KE_XUP = 65 // extra vt100 cursor keys for xterm + , KE_XDOWN = 66 + , KE_XLEFT = 67 + , KE_XRIGHT = 68 + + , KE_LEFTMOUSE_NM = 69 // non-mappable Left mouse button click + , KE_LEFTRELEASE_NM = 70 // non-mappable left mouse button release + + , KE_S_XF1 = 71 // vt100 shifted function keys for xterm + , KE_S_XF2 = 72 + , KE_S_XF3 = 73 + , KE_S_XF4 = 74 + + // NOTE: The scroll wheel events are inverted: i.e. UP is the same as + // moving the actual scroll wheel down, LEFT is the same as moving the + // scroll wheel right. + , KE_MOUSEDOWN = 75 // scroll wheel pseudo-button Down + , KE_MOUSEUP = 76 // scroll wheel pseudo-button Up + , KE_MOUSELEFT = 77 // scroll wheel pseudo-button Left + , KE_MOUSERIGHT = 78 // scroll wheel pseudo-button Right + + , KE_KINS = 79 // keypad Insert key + , KE_KDEL = 80 // keypad Delete key + + , KE_CSI = 81 // CSI typed directly + , KE_SNR = 82 // <SNR> + , KE_PLUG = 83 // <Plug> + , KE_CMDWIN = 84 // open command-line window from Command-line Mode + + , KE_C_LEFT = 85 // control-left + , KE_C_RIGHT = 86 // control-right + , KE_C_HOME = 87 // control-home + , KE_C_END = 88 // control-end + + , KE_X1MOUSE = 89 // X1/X2 mouse-buttons + , KE_X1DRAG = 90 + , KE_X1RELEASE = 91 + , KE_X2MOUSE = 92 + , KE_X2DRAG = 93 + , KE_X2RELEASE = 94 + + , KE_DROP = 95 // DnD data is available + // , KE_CURSORHOLD = 96 // CursorHold event + , KE_NOP = 97 // doesn't do something + , KE_FOCUSGAINED = 98 // focus gained + , KE_FOCUSLOST = 99 // focus lost + // , KE_MOUSEMOVE = 100 // mouse moved with no button down + // , KE_CANCEL = 101 // return from vgetc + , KE_EVENT = 102 // event + , KE_PASTE = 103 // special key to toggle the 'paste' option. + // sent only by UIs + , KE_COMMAND = 104 // <Cmd> special key }; /* diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index be8aa69623..bdeaf8e2cf 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -9,3 +9,12 @@ func Test_yank_put_clipboard() set clipboard& bwipe! endfunc + +func Test_nested_global() + new + call setline(1, ['nothing', 'found', 'found bad', 'bad']) + call assert_fails('g/found/3v/bad/s/^/++/', 'E147') + g/found/v/bad/s/^/++/ + call assert_equal(['nothing', '++found', 'found bad', 'bad'], getline(1, 4)) + bwipe! +endfunc diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index f3f9f93649..b7520235b7 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -55,9 +55,9 @@ describe('nvim_get_commands', function() it('gets various command attributes', function() local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='0', range='10', register=false, script_id=0, } local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, script_id=1, } - local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253Q2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, } - local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253Q3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, } - local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253Q4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, script_id=4, } + local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, } + local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, } + local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, script_id=4, } source([[ command -complete=custom,ListUsers -nargs=+ Finger !finger <args> ]]) |