changeset 11494:8e5ec22db3d8 v8.0.0630

patch 8.0.0630: it is not easy to work on lines without a match commit https://github.com/vim/vim/commit/f84b122a99da75741ae686fabb6f81b8b4755998 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jun 10 14:29:52 2017 +0200 patch 8.0.0630: it is not easy to work on lines without a match 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)
author Christian Brabandt <cb@256bit.org>
date Sat, 10 Jun 2017 14:30:03 +0200
parents bc5afe585f0d
children 2d448ff0efa2
files runtime/doc/repeat.txt src/ex_cmds.c src/testdir/test_global.vim src/version.c
diffstat 4 files changed, 86 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- 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 buf
 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.
+								*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 repeat a non-Ex command, you can use the ":normal" command: >
+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
--- 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)
-    {
-	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;
     }
 
@@ -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)
-    {
-	/* a match on this line? */
+    if (global_busy)
+    {
+	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))
-	{
-	    ml_setmarked(lnum);
-	    ndone++;
-	}
-	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);
-	else
-	    smsg((char_u *)_("Pattern not found: %s"), pat);
+	    global_exe_one(cmd, lnum);
     }
     else
     {
-#ifdef FEAT_CLIPBOARD
-	start_global_changes();
-#endif
-	global_exe(cmd);
+	/*
+	 * 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(&regmatch, curwin, curbuf, lnum,
+							     (colnr_T)0, NULL);
+	    if ((type == 'g' && match) || (type == 'v' && !match))
+	    {
+		ml_setmarked(lnum);
+		ndone++;
+	    }
+	    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);
+	    else
+		smsg((char_u *)_("Pattern not found: %s"), pat);
+	}
+	else
+	{
 #ifdef FEAT_CLIPBOARD
-	end_global_changes();
-#endif
-    }
-
-    ml_clearmarked();	   /* clear rest of the marks */
+	    start_global_changes();
+#endif
+	    global_exe(cmd);
+#ifdef FEAT_CLIPBOARD
+	    end_global_changes();
+#endif
+	}
+
+	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
-
--- 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
--- 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,