summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2017-06-10 14:29:52 +0200
committerBram Moolenaar <Bram@vim.org>2017-06-10 14:29:52 +0200
commitf84b122a99da75741ae686fabb6f81b8b4755998 (patch)
tree617a26feddb25a746ec6aa89e6865ebb3f28138c
parent6b1da3312e15c065b373c9ec2732f31a77cee61f (diff)
downloadvim-git-f84b122a99da75741ae686fabb6f81b8b4755998.tar.gz
patch 8.0.0630: it is not easy to work on lines without a matchv8.0.0630
Problem: The :global command does not work recursively, which makes it difficult to execute a command on a line where one pattern matches and another does not match. (Miles Cranmer) Solution: Allow for recursion if it is for only one line. (closes #1760)
-rw-r--r--runtime/doc/repeat.txt15
-rw-r--r--src/ex_cmds.c93
-rw-r--r--src/testdir/test_global.vim9
-rw-r--r--src/version.c2
4 files changed, 79 insertions, 40 deletions
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index 8f810536b..2d5195371 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -1,4 +1,4 @@
-*repeat.txt* For Vim version 8.0. Last change: 2017 Feb 06
+*repeat.txt* For Vim version 8.0. Last change: 2017 Jun 10
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -46,7 +46,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.
@@ -79,8 +79,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/ex_cmds.c b/src/ex_cmds.c
index 70f01453b..859b8c65b 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -5903,6 +5903,17 @@ do_sub_msg(
return FALSE;
}
+ static void
+global_exe_one(char_u *cmd, 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:
*
@@ -5933,9 +5944,13 @@ ex_global(exarg_T *eap)
int match;
int which_pat;
- if (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))
{
- EMSG(_("E147: Cannot do :global recursive")); /* will increment global_busy */
+ /* will increment global_busy to break out of the loop */
+ EMSG(_("E147: Cannot do :global recursive with a range"));
return;
}
@@ -5993,46 +6008,58 @@ 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)
+ if (global_busy)
{
- /* a match on this line? */
+ lnum = curwin->w_cursor.lnum;
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL);
+ (colnr_T)0, NULL);
if ((type == 'g' && match) || (type == 'v' && !match))
+ 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)
{
- ml_setmarked(lnum);
- ndone++;
+ /* a match on this line? */
+ match = vim_regexec_multi(&regmatch, 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((char_u *)_("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((char_u *)_("Pattern found in every line: %s"), pat);
+ else
+ smsg((char_u *)_("Pattern not found: %s"), pat);
+ }
else
- smsg((char_u *)_("Pattern not found: %s"), pat);
- }
- else
- {
+ {
#ifdef FEAT_CLIPBOARD
- start_global_changes();
+ start_global_changes();
#endif
- global_exe(cmd);
+ global_exe(cmd);
#ifdef FEAT_CLIPBOARD
- end_global_changes();
+ end_global_changes();
#endif
+ }
+
+ ml_clearmarked(); /* clear rest of the marks */
}
- ml_clearmarked(); /* clear rest of the marks */
vim_regfree(regmatch.regprog);
}
@@ -6063,12 +6090,7 @@ 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);
ui_breakcheck();
}
@@ -8514,4 +8536,3 @@ ex_oldfiles(exarg_T *eap UNUSED)
}
}
#endif
-
diff --git a/src/testdir/test_global.vim b/src/testdir/test_global.vim
index be8aa6962..bdeaf8e2c 100644
--- a/src/testdir/test_global.vim
+++ b/src/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/src/version.c b/src/version.c
index bc6a12daa..5bebc54ac 100644
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 630,
+/**/
629,
/**/
628,