mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
vim-patch:7.4.858
Problem: It's a bit clumsy to execute a command on a list of matches.
Solution: Add the ":ldo", ":lfdo", ":cdo" and ":cfdo" commands. (Yegappan
Lakshmanan)
aa23b37942
This commit is contained in:
parent
25eaacd10f
commit
7f99d210fd
@ -482,6 +482,8 @@ followed by another Vim command:
|
||||
:argdo
|
||||
:autocmd
|
||||
:bufdo
|
||||
:cdo
|
||||
:cfdo
|
||||
:command
|
||||
:cscope
|
||||
:debug
|
||||
@ -492,6 +494,8 @@ followed by another Vim command:
|
||||
:help
|
||||
:helpfind
|
||||
:lcscope
|
||||
:ldo
|
||||
:lfdo
|
||||
:make
|
||||
:normal
|
||||
:promptfind
|
||||
|
@ -795,7 +795,8 @@ USING THE ARGUMENT LIST
|
||||
autocommand event is disabled by adding it to
|
||||
'eventignore'. This considerably speeds up editing
|
||||
each file.
|
||||
Also see |:windo|, |:tabdo| and |:bufdo|.
|
||||
Also see |:windo|, |:tabdo|, |:bufdo|, |:cdo|, |:ldo|,
|
||||
|:cfdo| and |:lfdo|.
|
||||
|
||||
Example: >
|
||||
:args *.c
|
||||
|
@ -1128,6 +1128,8 @@ tag command action ~
|
||||
|:cc| :cc go to specific error
|
||||
|:cclose| :ccl[ose] close quickfix window
|
||||
|:cd| :cd change directory
|
||||
|:cdo| :cdo execute command in each valid error list entry
|
||||
|:cfdo| :cfdo execute command in each file in error list
|
||||
|:center| :ce[nter] format lines at the center
|
||||
|:cexpr| :cex[pr] read errors from expr and jump to first
|
||||
|:cfile| :cf[ile] read file with error messages and jump to first
|
||||
@ -1285,6 +1287,8 @@ tag command action ~
|
||||
|:lchdir| :lch[dir] change directory locally
|
||||
|:lclose| :lcl[ose] close location window
|
||||
|:lcscope| :lcs[cope] like ":cscope" but uses location list
|
||||
|:ldo| :ld[o] execute command in valid location list entries
|
||||
|:lfdo| :lfd[o] execute command in each file in location list
|
||||
|:left| :le[ft] left align lines
|
||||
|:leftabove| :lefta[bove] make split window appear left or above
|
||||
|:let| :let assign a value to a variable or option
|
||||
|
@ -291,6 +291,100 @@ use this code: >
|
||||
|
||||
au QuickfixCmdPost make call QfMakeConv()
|
||||
|
||||
EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
|
||||
*:cdo*
|
||||
:cdo[!] {cmd} Execute {cmd} in each valid entry in the quickfix list.
|
||||
It works like doing this: >
|
||||
:cfirst
|
||||
:{cmd}
|
||||
:cnext
|
||||
:{cmd}
|
||||
etc.
|
||||
< When the current file can't be |abandon|ed and the [!]
|
||||
is not present, the command fails.
|
||||
When an error is detected on one buffer, further
|
||||
buffers will not be visited.
|
||||
The last buffer (or where an error occurred) becomes
|
||||
the current buffer.
|
||||
{cmd} can contain '|' to concatenate several commands.
|
||||
Only valid entries in the quickfix list are used.
|
||||
Note: While this command is executing, the Syntax
|
||||
autocommand event is disabled by adding it to
|
||||
'eventignore'. This considerably speeds up editing
|
||||
each buffer.
|
||||
Also see |:bufdo|, |:tabdo|, |:argdo|, |:windo|,
|
||||
|:ldo|, |:cfdo| and |:lfdo|.
|
||||
|
||||
*:cfdo*
|
||||
:cfdo[!] {cmd} Execute {cmd} in each file in the quickfix list.
|
||||
It works like doing this: >
|
||||
:cfirst
|
||||
:{cmd}
|
||||
:cnfile
|
||||
:{cmd}
|
||||
etc.
|
||||
< When the current file can't be |abandon|ed and the [!]
|
||||
is not present, the command fails.
|
||||
When an error is detected on one buffer, further
|
||||
buffers will not be visited.
|
||||
The last buffer (or where an error occurred) becomes
|
||||
the current buffer.
|
||||
{cmd} can contain '|' to concatenate several commands.
|
||||
Only valid entries in the quickfix list are used.
|
||||
Note: While this command is executing, the Syntax
|
||||
autocommand event is disabled by adding it to
|
||||
'eventignore'. This considerably speeds up editing
|
||||
each buffer.
|
||||
Also see |:bufdo|, |:tabdo|, |:argdo|, |:windo|,
|
||||
|:cdo|, |:ldo| and |:lfdo|.
|
||||
|
||||
*:ldo*
|
||||
:ld[o][!] {cmd} Execute {cmd} in each valid entry in the location list
|
||||
for the current window.
|
||||
It works like doing this: >
|
||||
:lfirst
|
||||
:{cmd}
|
||||
:lnext
|
||||
:{cmd}
|
||||
etc.
|
||||
< When the current file can't be |abandon|ed and the [!]
|
||||
is not present, the command fails.
|
||||
When an error is detected on one buffer, further
|
||||
buffers will not be visited.
|
||||
The last buffer (or where an error occurred) becomes
|
||||
the current buffer.
|
||||
{cmd} can contain '|' to concatenate several commands.
|
||||
Only valid entries in the location list are used.
|
||||
Note: While this command is executing, the Syntax
|
||||
autocommand event is disabled by adding it to
|
||||
'eventignore'. This considerably speeds up editing
|
||||
each buffer.
|
||||
Also see |:bufdo|, |:tabdo|, |:argdo|, |:windo|,
|
||||
|:cdo|, |:cfdo| and |:lfdo|.
|
||||
|
||||
*:lfdo*
|
||||
:lfdo[!] {cmd} Execute {cmd} in each file in the location list for
|
||||
the current window.
|
||||
It works like doing this: >
|
||||
:lfirst
|
||||
:{cmd}
|
||||
:lnfile
|
||||
:{cmd}
|
||||
etc.
|
||||
< When the current file can't be |abandon|ed and the [!]
|
||||
is not present, the command fails.
|
||||
When an error is detected on one buffer, further
|
||||
buffers will not be visited.
|
||||
The last buffer (or where an error occurred) becomes
|
||||
the current buffer.
|
||||
{cmd} can contain '|' to concatenate several commands.
|
||||
Only valid entries in the location list are used.
|
||||
Note: While this command is executing, the Syntax
|
||||
autocommand event is disabled by adding it to
|
||||
'eventignore'. This considerably speeds up editing
|
||||
each buffer.
|
||||
Also see |:bufdo|, |:tabdo|, |:argdo|, |:windo|,
|
||||
|:cdo|, |:ldo| and |:cfdo|.
|
||||
|
||||
=============================================================================
|
||||
2. The error window *quickfix-window*
|
||||
|
@ -234,7 +234,8 @@ LOOPING OVER TAB PAGES:
|
||||
current tab page.
|
||||
{cmd} can contain '|' to concatenate several commands.
|
||||
{cmd} must not open or close tab pages or reorder them.
|
||||
Also see |:windo|, |:argdo| and |:bufdo|.
|
||||
Also see |:windo|, |:argdo|, |:bufdo|, |:cdo|, |:ldo|, |:cfdo|
|
||||
and |:lfdo|.
|
||||
|
||||
==============================================================================
|
||||
3. Other items *tab-page-other*
|
||||
|
@ -702,7 +702,8 @@ can also get to them with the buffer list commands, like ":bnext".
|
||||
the current window.
|
||||
{cmd} can contain '|' to concatenate several commands.
|
||||
{cmd} must not open or close windows or reorder them.
|
||||
Also see |:tabdo|, |:argdo| and |:bufdo|.
|
||||
Also see |:tabdo|, |:argdo|, |:bufdo|, |:cdo|, |:ldo|,
|
||||
|:cfdo| and |:lfdo|.
|
||||
|
||||
*:bufdo*
|
||||
:[range]bufdo[!] {cmd} Execute {cmd} in each buffer in the buffer list or if
|
||||
@ -728,7 +729,8 @@ can also get to them with the buffer list commands, like ":bnext".
|
||||
autocommand event is disabled by adding it to
|
||||
'eventignore'. This considerably speeds up editing
|
||||
each buffer.
|
||||
Also see |:tabdo|, |:argdo| and |:windo|.
|
||||
Also see |:tabdo|, |:argdo|, |:windo|, |:cdo|, |:ldo|,
|
||||
|:cfdo| and |:lfdo|.
|
||||
|
||||
Examples: >
|
||||
|
||||
|
@ -34,6 +34,7 @@ local ADDR_ARGUMENTS = 2
|
||||
local ADDR_LOADED_BUFFERS = 3
|
||||
local ADDR_BUFFERS = 4
|
||||
local ADDR_TABS = 5
|
||||
local ADDR_QUICKFIX = 6
|
||||
|
||||
-- The following table is described in ex_cmds_defs.h file.
|
||||
return {
|
||||
@ -373,6 +374,12 @@ return {
|
||||
addr_type=ADDR_LINES,
|
||||
func='ex_cd',
|
||||
},
|
||||
{
|
||||
command='cdo',
|
||||
flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
|
||||
addr_type=ADDR_QUICKFIX,
|
||||
func='ex_listdo',
|
||||
},
|
||||
{
|
||||
command='center',
|
||||
flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
|
||||
@ -391,6 +398,14 @@ return {
|
||||
addr_type=ADDR_LINES,
|
||||
func='ex_cfile',
|
||||
},
|
||||
-- Even though 'cfdo' is alphabetically lower than 'cfile', it is after
|
||||
-- 'cfile' in this cmd list to support the existing ":cf" abbreviation.
|
||||
{
|
||||
command='cfdo',
|
||||
flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
|
||||
addr_type=ADDR_QUICKFIX,
|
||||
func='ex_listdo',
|
||||
},
|
||||
{
|
||||
command='cfirst',
|
||||
flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG),
|
||||
@ -1285,6 +1300,12 @@ return {
|
||||
addr_type=ADDR_LINES,
|
||||
func='do_cscope',
|
||||
},
|
||||
{
|
||||
command='ldo',
|
||||
flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
|
||||
addr_type=ADDR_QUICKFIX,
|
||||
func='ex_listdo',
|
||||
},
|
||||
{
|
||||
command='left',
|
||||
flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
|
||||
@ -1315,6 +1336,14 @@ return {
|
||||
addr_type=ADDR_LINES,
|
||||
func='ex_cfile',
|
||||
},
|
||||
-- Even though 'lfdo' is alphabetically lower than 'lfile', it is after
|
||||
-- 'lfile' in this cmd list to support the existing ":lf" abbreviation.
|
||||
{
|
||||
command='lfdo',
|
||||
flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
|
||||
addr_type=ADDR_QUICKFIX,
|
||||
func='ex_listdo',
|
||||
},
|
||||
{
|
||||
command='lfirst',
|
||||
flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG),
|
||||
|
@ -1868,7 +1868,7 @@ void ex_argdelete(exarg_T *eap)
|
||||
}
|
||||
|
||||
/*
|
||||
* ":argdo", ":windo", ":bufdo", ":tabdo"
|
||||
* ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
|
||||
*/
|
||||
void ex_listdo(exarg_T *eap)
|
||||
{
|
||||
@ -1879,7 +1879,6 @@ void ex_listdo(exarg_T *eap)
|
||||
char_u *save_ei = NULL;
|
||||
char_u *p_shm_save;
|
||||
|
||||
|
||||
if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
|
||||
/* Don't do syntax HL autocommands. Skipping the syntax file is a
|
||||
* great speed improvement. */
|
||||
@ -1914,7 +1913,10 @@ void ex_listdo(exarg_T *eap)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
buf_T *buf = curbuf;
|
||||
size_t qf_size = 0;
|
||||
|
||||
/* set pcmark now */
|
||||
if (eap->cmdidx == CMD_bufdo) {
|
||||
/* Advance to the first listed buffer after "eap->line1". */
|
||||
@ -1929,6 +1931,22 @@ void ex_listdo(exarg_T *eap)
|
||||
if (buf != NULL) {
|
||||
goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
|
||||
}
|
||||
} else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
|
||||
eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
|
||||
qf_size = qf_get_size(eap);
|
||||
assert(eap->line1 >= 0);
|
||||
if (qf_size == 0 || (size_t)eap->line1 > qf_size) {
|
||||
buf = NULL;
|
||||
} else {
|
||||
ex_cc(eap);
|
||||
|
||||
buf = curbuf;
|
||||
i = eap->line1 - 1;
|
||||
if (eap->addr_count <= 0) {
|
||||
// Default to all quickfix/location list entries.
|
||||
eap->line2 = qf_size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setpcmark();
|
||||
}
|
||||
@ -2009,9 +2027,27 @@ void ex_listdo(exarg_T *eap)
|
||||
set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
|
||||
xfree(p_shm_save);
|
||||
|
||||
/* If autocommands took us elsewhere, quit here */
|
||||
if (curbuf->b_fnum != next_fnum)
|
||||
// If autocommands took us elsewhere, quit here.
|
||||
if (curbuf->b_fnum != next_fnum) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
|
||||
eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
|
||||
assert(i >= 0);
|
||||
if ((size_t)i >= qf_size || i >= eap->line2) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t qf_idx = qf_get_cur_idx(eap);
|
||||
|
||||
ex_cnext(eap);
|
||||
|
||||
// If jumping to the next quickfix entry fails, quit here.
|
||||
if (qf_get_cur_idx(eap) == qf_idx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (eap->cmdidx == CMD_windo) {
|
||||
|
@ -72,6 +72,7 @@
|
||||
#define ADDR_LOADED_BUFFERS 3
|
||||
#define ADDR_BUFFERS 4
|
||||
#define ADDR_TABS 5
|
||||
#define ADDR_QUICKFIX 6
|
||||
|
||||
typedef struct exarg exarg_T;
|
||||
|
||||
|
@ -1531,9 +1531,12 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|
||||
lnum = CURRENT_TAB_NR;
|
||||
ea.line2 = lnum;
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
ea.line2 = qf_get_cur_valid_idx(&ea);
|
||||
break;
|
||||
}
|
||||
ea.cmd = skipwhite(ea.cmd);
|
||||
lnum = get_address(&ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0);
|
||||
lnum = get_address(&ea, &ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0);
|
||||
if (ea.cmd == NULL) /* error detected */
|
||||
goto doend;
|
||||
if (lnum == MAXLNUM) {
|
||||
@ -1582,6 +1585,13 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|
||||
ea.line2 = ARGCOUNT;
|
||||
}
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
ea.line1 = 1;
|
||||
ea.line2 = qf_get_size(&ea);
|
||||
if (ea.line2 == 0) {
|
||||
ea.line2 = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++ea.addr_count;
|
||||
}
|
||||
@ -1962,6 +1972,12 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|
||||
ea.line2 = ARGCOUNT;
|
||||
}
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
ea.line2 = qf_get_size(&ea);
|
||||
if (ea.line2 == 0) {
|
||||
ea.line2 = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2945,6 +2961,8 @@ set_one_cmd_context (
|
||||
case CMD_botright:
|
||||
case CMD_browse:
|
||||
case CMD_bufdo:
|
||||
case CMD_cdo:
|
||||
case CMD_cfdo:
|
||||
case CMD_confirm:
|
||||
case CMD_debug:
|
||||
case CMD_folddoclosed:
|
||||
@ -2954,7 +2972,9 @@ set_one_cmd_context (
|
||||
case CMD_keepjumps:
|
||||
case CMD_keepmarks:
|
||||
case CMD_keeppatterns:
|
||||
case CMD_ldo:
|
||||
case CMD_leftabove:
|
||||
case CMD_lfdo:
|
||||
case CMD_lockmarks:
|
||||
case CMD_noautocmd:
|
||||
case CMD_noswapfile:
|
||||
@ -3367,7 +3387,8 @@ skip_range (
|
||||
*
|
||||
* Return MAXLNUM when no Ex address was found.
|
||||
*/
|
||||
static linenr_T get_address(char_u **ptr,
|
||||
static linenr_T get_address(exarg_T *eap,
|
||||
char_u **ptr,
|
||||
int addr_type, // flag: one of ADDR_LINES, ...
|
||||
int skip, // only skip the address, don't use it
|
||||
int to_other_file // flag: may jump to other file
|
||||
@ -3405,6 +3426,9 @@ static linenr_T get_address(char_u **ptr,
|
||||
case ADDR_TABS:
|
||||
lnum = CURRENT_TAB_NR;
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
lnum = qf_get_cur_valid_idx(eap);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -3436,6 +3460,12 @@ static linenr_T get_address(char_u **ptr,
|
||||
case ADDR_TABS:
|
||||
lnum = LAST_TAB_NR;
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
lnum = qf_get_size(eap);
|
||||
if (lnum == 0) {
|
||||
lnum = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -3578,6 +3608,9 @@ static linenr_T get_address(char_u **ptr,
|
||||
case ADDR_TABS:
|
||||
lnum = CURRENT_TAB_NR;
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
lnum = qf_get_cur_valid_idx(eap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3702,6 +3735,12 @@ static char_u *invalid_range(exarg_T *eap)
|
||||
return (char_u *)_(e_invrange);
|
||||
}
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
assert(eap->line2 >= 0);
|
||||
if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) {
|
||||
return (char_u *)_(e_invrange);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@ -4589,6 +4628,7 @@ static struct {
|
||||
{ADDR_TABS, "tabs"},
|
||||
{ADDR_BUFFERS, "buffers"},
|
||||
{ADDR_WINDOWS, "windows"},
|
||||
{ADDR_QUICKFIX, "quickfix"},
|
||||
{-1, NULL}
|
||||
};
|
||||
|
||||
@ -7013,9 +7053,7 @@ static void ex_put(exarg_T *eap)
|
||||
*/
|
||||
static void ex_copymove(exarg_T *eap)
|
||||
{
|
||||
long n;
|
||||
|
||||
n = get_address(&eap->arg, eap->addr_type, FALSE, FALSE);
|
||||
long n = get_address(eap, &eap->arg, eap->addr_type, false, false);
|
||||
if (eap->arg == NULL) { /* error detected */
|
||||
eap->nextcmd = NULL;
|
||||
return;
|
||||
|
@ -1202,7 +1202,7 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr)
|
||||
/*
|
||||
* Check in which directory of the directory stack the given file can be
|
||||
* found.
|
||||
* Returns a pointer to the directory name or NULL if not found
|
||||
* Returns a pointer to the directory name or NULL if not found.
|
||||
* Cleans up intermediate directory entries.
|
||||
*
|
||||
* TODO: How to solve the following problem?
|
||||
@ -2571,9 +2571,159 @@ static char_u *get_mef_name(void)
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Returns the number of valid entries in the current quickfix/location list.
|
||||
size_t qf_get_size(exarg_T *eap)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
qf_info_T *qi = &ql_info;
|
||||
if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
|
||||
// Location list.
|
||||
qi = GET_LOC_LIST(curwin);
|
||||
if (qi == NULL) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int prev_fnum = 0;
|
||||
size_t sz = 0;
|
||||
qfline_T *qfp;
|
||||
size_t i;
|
||||
assert(qi->qf_lists[qi->qf_curlist].qf_count >= 0);
|
||||
for (i = 0, qfp = qi->qf_lists[qi->qf_curlist].qf_start;
|
||||
i < (size_t)qi->qf_lists[qi->qf_curlist].qf_count && qfp != NULL;
|
||||
i++, qfp = qfp->qf_next) {
|
||||
if (!qfp->qf_valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo) {
|
||||
// Count all valid entries.
|
||||
sz++;
|
||||
} else if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
|
||||
// Count the number of files.
|
||||
sz++;
|
||||
prev_fnum = qfp->qf_fnum;
|
||||
}
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
/// Returns the current index of the quickfix/location list.
|
||||
/// Returns 0 if there is an error.
|
||||
size_t qf_get_cur_idx(exarg_T *eap)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
qf_info_T *qi = &ql_info;
|
||||
|
||||
if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
|
||||
// Location list.
|
||||
qi = GET_LOC_LIST(curwin);
|
||||
if (qi == NULL) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
assert(qi->qf_lists[qi->qf_curlist].qf_index >= 0);
|
||||
return (size_t)qi->qf_lists[qi->qf_curlist].qf_index;
|
||||
}
|
||||
|
||||
/// Returns the current index in the quickfix/location list,
|
||||
/// counting only valid entries.
|
||||
/// Returns 1 if there are no valid entries.
|
||||
int qf_get_cur_valid_idx(exarg_T *eap)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
qf_info_T *qi = &ql_info;
|
||||
|
||||
if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
|
||||
// Location list.
|
||||
qi = GET_LOC_LIST(curwin);
|
||||
if (qi == NULL) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
|
||||
|
||||
// Check if the list has valid errors.
|
||||
if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int prev_fnum = 0;
|
||||
int eidx = 0;
|
||||
qfline_T *qfp;
|
||||
size_t i;
|
||||
assert(qfl->qf_index >= 0);
|
||||
for (i = 1, qfp = qfl->qf_start;
|
||||
i <= (size_t)qfl->qf_index && qfp != NULL;
|
||||
i++, qfp = qfp->qf_next) {
|
||||
if (!qfp->qf_valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
|
||||
if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
|
||||
// Count the number of files.
|
||||
eidx++;
|
||||
prev_fnum = qfp->qf_fnum;
|
||||
}
|
||||
} else {
|
||||
eidx++;
|
||||
}
|
||||
}
|
||||
|
||||
return eidx != 0 ? eidx : 1;
|
||||
}
|
||||
|
||||
/// Get the 'n'th valid error entry in the quickfix or location list.
|
||||
///
|
||||
/// Used by :cdo, :ldo, :cfdo and :lfdo commands.
|
||||
/// For :cdo and :ldo, returns the 'n'th valid error entry.
|
||||
/// For :cfdo and :lfdo, returns the 'n'th valid file entry.
|
||||
static size_t qf_get_nth_valid_entry(qf_info_T *qi, size_t n, bool fdo)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
|
||||
|
||||
// Check if the list has valid errors.
|
||||
if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int prev_fnum = 0;
|
||||
size_t eidx = 0;
|
||||
size_t i;
|
||||
qfline_T *qfp;
|
||||
assert(qfl->qf_count >= 0);
|
||||
for (i = 1, qfp = qfl->qf_start;
|
||||
i <= (size_t)qfl->qf_count && qfp != NULL;
|
||||
i++, qfp = qfp->qf_next) {
|
||||
if (qfp->qf_valid) {
|
||||
if (fdo) {
|
||||
if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
|
||||
// Count the number of files.
|
||||
eidx++;
|
||||
prev_fnum = qfp->qf_fnum;
|
||||
}
|
||||
} else {
|
||||
eidx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (eidx == n) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i <= (size_t)qfl->qf_count ? i : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ":cc", ":crewind", ":cfirst" and ":clast".
|
||||
* ":ll", ":lrewind", ":lfirst" and ":llast".
|
||||
* ":cdo", ":ldo", ":cfdo" and ":lfdo".
|
||||
*/
|
||||
void ex_cc(exarg_T *eap)
|
||||
{
|
||||
@ -2582,7 +2732,10 @@ void ex_cc(exarg_T *eap)
|
||||
if (eap->cmdidx == CMD_ll
|
||||
|| eap->cmdidx == CMD_lrewind
|
||||
|| eap->cmdidx == CMD_lfirst
|
||||
|| eap->cmdidx == CMD_llast) {
|
||||
|| eap->cmdidx == CMD_llast
|
||||
|| eap->cmdidx == CMD_llast
|
||||
|| eap->cmdidx == CMD_ldo
|
||||
|| eap->cmdidx == CMD_lfdo) {
|
||||
qi = GET_LOC_LIST(curwin);
|
||||
if (qi == NULL) {
|
||||
EMSG(_(e_loclist));
|
||||
@ -2590,21 +2743,42 @@ void ex_cc(exarg_T *eap)
|
||||
}
|
||||
}
|
||||
|
||||
qf_jump(qi, 0,
|
||||
eap->addr_count > 0
|
||||
? (int)eap->line2
|
||||
: (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll)
|
||||
? 0
|
||||
: (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind
|
||||
|| eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst)
|
||||
? 1
|
||||
: 32767,
|
||||
eap->forceit);
|
||||
int errornr;
|
||||
if (eap->addr_count > 0) {
|
||||
errornr = (int)eap->line2;
|
||||
} else if (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) {
|
||||
errornr = 0;
|
||||
} else if (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind
|
||||
|| eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst) {
|
||||
errornr = 1;
|
||||
} else {
|
||||
errornr = 32767;
|
||||
}
|
||||
|
||||
// For cdo and ldo commands, jump to the nth valid error.
|
||||
// For cfdo and lfdo commands, jump to the nth valid file entry.
|
||||
if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
|
||||
eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
|
||||
size_t n;
|
||||
if (eap->addr_count > 0) {
|
||||
assert(eap->line1 >= 0);
|
||||
n = (size_t)eap->line1;
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
size_t valid_entry = qf_get_nth_valid_entry(qi, n,
|
||||
eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo);
|
||||
assert(valid_entry <= INT_MAX);
|
||||
errornr = (int)valid_entry;
|
||||
}
|
||||
|
||||
qf_jump(qi, 0, errornr, eap->forceit);
|
||||
}
|
||||
|
||||
/*
|
||||
* ":cnext", ":cnfile", ":cNext" and ":cprevious".
|
||||
* ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile".
|
||||
* ":cdo", ":ldo", ":cfdo" and ":lfdo".
|
||||
*/
|
||||
void ex_cnext(exarg_T *eap)
|
||||
{
|
||||
@ -2615,7 +2789,10 @@ void ex_cnext(exarg_T *eap)
|
||||
|| eap->cmdidx == CMD_lprevious
|
||||
|| eap->cmdidx == CMD_lnfile
|
||||
|| eap->cmdidx == CMD_lNfile
|
||||
|| eap->cmdidx == CMD_lpfile) {
|
||||
|| eap->cmdidx == CMD_lpfile
|
||||
|| eap->cmdidx == CMD_lpfile
|
||||
|| eap->cmdidx == CMD_ldo
|
||||
|| eap->cmdidx == CMD_lfdo) {
|
||||
qi = GET_LOC_LIST(curwin);
|
||||
if (qi == NULL) {
|
||||
EMSG(_(e_loclist));
|
||||
@ -2623,15 +2800,26 @@ void ex_cnext(exarg_T *eap)
|
||||
}
|
||||
}
|
||||
|
||||
qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext)
|
||||
int errornr;
|
||||
if (eap->addr_count > 0 &&
|
||||
(eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo &&
|
||||
eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) {
|
||||
errornr = (int)eap->line2;
|
||||
} else {
|
||||
errornr = 1;
|
||||
}
|
||||
|
||||
qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext
|
||||
|| eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo)
|
||||
? FORWARD
|
||||
: (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile)
|
||||
: (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile
|
||||
|| eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
|
||||
? FORWARD_FILE
|
||||
: (eap->cmdidx == CMD_cpfile || eap->cmdidx == CMD_lpfile
|
||||
|| eap->cmdidx == CMD_cNfile || eap->cmdidx == CMD_lNfile)
|
||||
? BACKWARD_FILE
|
||||
: BACKWARD,
|
||||
eap->addr_count > 0 ? (int)eap->line2 : 1, eap->forceit);
|
||||
errornr, eap->forceit);
|
||||
}
|
||||
|
||||
/*
|
||||
|
107
src/nvim/testdir/test_cdo.in
Normal file
107
src/nvim/testdir/test_cdo.in
Normal file
@ -0,0 +1,107 @@
|
||||
Tests for the :cdo, :cfdo, :ldo and :lfdo commands
|
||||
|
||||
STARTTEST
|
||||
:so small.vim
|
||||
:if !has('quickfix') | e! test.ok | wq! test.out | endif
|
||||
|
||||
:call writefile(["Line1", "Line2", "Line3"], 'Xtestfile1')
|
||||
:call writefile(["Line1", "Line2", "Line3"], 'Xtestfile2')
|
||||
:call writefile(["Line1", "Line2", "Line3"], 'Xtestfile3')
|
||||
|
||||
:function RunTests(cchar)
|
||||
: let nl="\n"
|
||||
|
||||
: enew
|
||||
: " Try with an empty list
|
||||
: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
|
||||
: " Populate the list and then try
|
||||
: exe a:cchar . "getexpr ['non-error 1', 'Xtestfile1:1:3:Line1', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:3:1:Line3']"
|
||||
: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
|
||||
: " Run command only on selected error lines
|
||||
: enew
|
||||
: exe "2,3" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: " Boundary condition tests
|
||||
: enew
|
||||
: exe "1,1" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: enew
|
||||
: exe "3" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: " Range test commands
|
||||
: enew
|
||||
: exe "%" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: enew
|
||||
: exe "1,$" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: enew
|
||||
: exe a:cchar . 'prev'
|
||||
: exe "." . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: " Invalid error lines test
|
||||
: enew
|
||||
: exe "27" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "4,5" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
|
||||
: " Run commands from an unsaved buffer
|
||||
: let v:errmsg=''
|
||||
: enew
|
||||
: setlocal modified
|
||||
: exe "2,2" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: if v:errmsg =~# 'No write since last change'
|
||||
: let g:result .= 'Unsaved file change test passed' . nl
|
||||
: else
|
||||
: let g:result .= 'Unsaved file change test failed' . nl
|
||||
: endif
|
||||
|
||||
: " If the executed command fails, then the operation should be aborted
|
||||
: enew!
|
||||
: let subst_count = 0
|
||||
: exe a:cchar . "do s/Line/xLine/ | let subst_count += 1"
|
||||
: if subst_count == 1 && getline('.') == 'xLine1'
|
||||
: let g:result .= 'Abort command on error test passed' . nl
|
||||
: else
|
||||
: let g:result .= 'Abort command on error test failed' . nl
|
||||
: endif
|
||||
|
||||
: exe "2,2" . a:cchar . "do! let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
|
||||
: " List with no valid error entries
|
||||
: edit! +2 Xtestfile1
|
||||
: exe a:cchar . "getexpr ['non-error 1', 'non-error 2', 'non-error 3']"
|
||||
: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "2" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: let v:errmsg=''
|
||||
: exe "%" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "1,$" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "." . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: let g:result .= v:errmsg
|
||||
|
||||
: " List with only one valid entry
|
||||
: exe a:cchar . "getexpr ['Xtestfile3:3:1:Line3']"
|
||||
: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
|
||||
: " Tests for :cfdo and :lfdo commands
|
||||
: exe a:cchar . "getexpr ['non-error 1', 'Xtestfile1:1:3:Line1', 'Xtestfile1:2:1:Line2', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:2:3:Line2', 'Xtestfile3:3:1:Line3']"
|
||||
: exe a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "3" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "2,3" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "%" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe "1,$" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
: exe a:cchar . 'pfile'
|
||||
: exe "." . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
|
||||
: " List with only one valid entry
|
||||
: exe a:cchar . "getexpr ['Xtestfile2:2:5:Line2']"
|
||||
: exe a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
|
||||
:endfunction
|
||||
|
||||
:let result=''
|
||||
:" Tests for the :cdo quickfix list command
|
||||
:call RunTests('c')
|
||||
:let result .= "\n"
|
||||
:" Tests for the :ldo location list command
|
||||
:call RunTests('l')
|
||||
|
||||
:edit! test.out
|
||||
:0put =result
|
||||
:wq!
|
||||
ENDTEST
|
||||
|
66
src/nvim/testdir/test_cdo.ok
Normal file
66
src/nvim/testdir/test_cdo.ok
Normal file
@ -0,0 +1,66 @@
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile2 2L 2C
|
||||
Unsaved file change test passed
|
||||
Abort command on error test passed
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile2 2L 5C
|
||||
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile2 2L 2C
|
||||
Unsaved file change test passed
|
||||
Abort command on error test passed
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 3L 1C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile1 1L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile3 2L 3C
|
||||
Xtestfile2 2L 2C
|
||||
Xtestfile2 2L 5C
|
||||
|
@ -135,7 +135,7 @@ static int included_patches[] = {
|
||||
// 861 NA
|
||||
// 860,
|
||||
// 859,
|
||||
// 858,
|
||||
858,
|
||||
// 857,
|
||||
// 856,
|
||||
// 855 NA
|
||||
|
Loading…
Reference in New Issue
Block a user