diff options
-rw-r--r-- | runtime/doc/repeat.txt | 13 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 76 | ||||
-rw-r--r-- | src/nvim/testdir/test_global.vim | 9 |
3 files changed, 64 insertions, 34 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/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 |