]> granicus.if.org Git - vim/commitdiff
patch 8.0.0630: it is not easy to work on lines without a match v8.0.0630
authorBram Moolenaar <Bram@vim.org>
Sat, 10 Jun 2017 12:29:52 +0000 (14:29 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 10 Jun 2017 12:29:52 +0000 (14:29 +0200)
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)

runtime/doc/repeat.txt
src/ex_cmds.c
src/testdir/test_global.vim
src/version.c

index 8f810536b1d5d1af9ca5c763463e6ab8c37cecd3..2d51953714cba8554a01180a5a1a9273ddbab4d1 100644 (file)
@@ -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
index 70f01453bdb1e064635cfd24cc830199c05bcc55..859b8c65bfe25db0f65ad5a0f4082fc590ecd65f 100644 (file)
@@ -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
-
index be8aa69623a8a52eda4ae31adef9e95ac9d61ef3..bdeaf8e2cf8dbfcd45b193917e710979648bc625 100644 (file)
@@ -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
index bc6a12daa390af43db69b475151d66ff5191e968..5bebc54ac02ad60eb46985a046b9d548ae3fe762 100644 (file)
@@ -764,6 +764,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    630,
 /**/
     629,
 /**/