mirror of
https://github.com/neovim/neovim.git
synced 2025-09-27 05:28:33 +00:00
vim-patch:8.0.0630: it is not easy to work on lines without a match (#8734)
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 vim/vim#1760)
f84b122a99
This commit is contained in:

committed by
Justin M. Keyes

parent
fc45c97829
commit
392817c2da
@@ -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.
|
||||
*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
|
||||
|
@@ -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,11 +4270,17 @@ 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);
|
||||
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++) {
|
||||
// a match on this line?
|
||||
match = vim_regexec_multi(®match, curwin, curbuf, lnum,
|
||||
(colnr_T)0, NULL);
|
||||
if ((type == 'g' && match) || (type == 'v' && !match)) {
|
||||
@@ -4269,12 +4290,10 @@ void ex_global(exarg_T *eap)
|
||||
line_breakcheck();
|
||||
}
|
||||
|
||||
/*
|
||||
* pass 2: execute the command for each line that has been marked
|
||||
*/
|
||||
if (got_int)
|
||||
// pass 2: execute the command for each line that has been marked
|
||||
if (got_int) {
|
||||
MSG(_(e_interr));
|
||||
else if (ndone == 0) {
|
||||
} else if (ndone == 0) {
|
||||
if (type == 'v') {
|
||||
smsg(_("Pattern found in every line: %s"), pat);
|
||||
} else {
|
||||
@@ -4283,7 +4302,8 @@ void ex_global(exarg_T *eap)
|
||||
} 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();
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user