Merge pull request #13655 from janlazo/vim-8.2.2255

vim-patch:8.1.{1241,1261,1275,1281,1549,1590,1881,2079,2322,2360,2411},8.2.{113,388,934,2255,2258,2260,2269}
This commit is contained in:
Jan Edmund Lazo 2021-01-01 17:40:26 -05:00 committed by GitHub
commit 5eccfd2b2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1403 additions and 884 deletions

View File

@ -1173,9 +1173,11 @@ tag command action ~
|:caddbuffer| :cad[dbuffer] add errors from buffer |:caddbuffer| :cad[dbuffer] add errors from buffer
|:caddexpr| :cadde[xpr] add errors from expr |:caddexpr| :cadde[xpr] add errors from expr
|:caddfile| :caddf[ile] add error message to current quickfix list |:caddfile| :caddf[ile] add error message to current quickfix list
|:cafter| :caf[ter] go to error after current cursor
|:call| :cal[l] call a function |:call| :cal[l] call a function
|:catch| :cat[ch] part of a :try command |:catch| :cat[ch] part of a :try command
|:cbelow| :cbe[low] go to error below current line |:cbefore| :cbef[ore] go to error before current cursor
|:cbelow| :cbel[ow] go to error below current line
|:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window
|:cbuffer| :cb[uffer] parse error messages and jump to first error |:cbuffer| :cb[uffer] parse error messages and jump to first error
|:cc| :cc go to specific error |:cc| :cc go to specific error
@ -1336,10 +1338,12 @@ tag command action ~
|:laddexpr| :lad[dexpr] add locations from expr |:laddexpr| :lad[dexpr] add locations from expr
|:laddbuffer| :laddb[uffer] add locations from buffer |:laddbuffer| :laddb[uffer] add locations from buffer
|:laddfile| :laddf[ile] add locations to current location list |:laddfile| :laddf[ile] add locations to current location list
|:lafter| :laf[ter] go to location after current cursor
|:last| :la[st] go to the last file in the argument list |:last| :la[st] go to the last file in the argument list
|:language| :lan[guage] set the language (locale) |:language| :lan[guage] set the language (locale)
|:later| :lat[er] go to newer change, redo |:later| :lat[er] go to newer change, redo
|:lbelow| :lbe[low] go to location below current line |:lbefore| :lbef[ore] go to location before current cursor
|:lbelow| :lbel[ow] go to location below current line
|:lbottom| :lbo[ttom] scroll to the bottom of the location window |:lbottom| :lbo[ttom] scroll to the bottom of the location window
|:lbuffer| :lb[uffer] parse locations and jump to first location |:lbuffer| :lb[uffer] parse locations and jump to first location
|:lcd| :lc[d] change directory locally |:lcd| :lc[d] change directory locally

View File

@ -74,7 +74,7 @@ processing a quickfix or location list command, it will be aborted.
*:cc* *:cc*
:cc[!] [nr] Display error [nr]. If [nr] is omitted, the same :cc[!] [nr] Display error [nr]. If [nr] is omitted, the same
error is displayed again. Without [!] this doesn't :[nr]cc[!] error is displayed again. Without [!] this doesn't
work when jumping to another buffer, the current buffer work when jumping to another buffer, the current buffer
has been changed, there is the only window for the has been changed, there is the only window for the
buffer and both 'hidden' and 'autowrite' are off. buffer and both 'hidden' and 'autowrite' are off.
@ -83,10 +83,13 @@ processing a quickfix or location list command, it will be aborted.
there is another window for this buffer. there is another window for this buffer.
The 'switchbuf' settings are respected when jumping The 'switchbuf' settings are respected when jumping
to a buffer. to a buffer.
When used in the quickfix window the line number can
be used, including "." for the current line and "$"
for the last line.
*:ll* *:ll*
:ll[!] [nr] Same as ":cc", except the location list for the :ll[!] [nr] Same as ":cc", except the location list for the
current window is used instead of the quickfix list. :[nr]ll[!] current window is used instead of the quickfix list.
*:cn* *:cne* *:cnext* *E553* *:cn* *:cne* *:cnext* *E553*
:[count]cn[ext][!] Display the [count] next error in the list that :[count]cn[ext][!] Display the [count] next error in the list that
@ -125,8 +128,8 @@ processing a quickfix or location list command, it will be aborted.
:[count]lab[ove] Same as ":cabove", except the location list for the :[count]lab[ove] Same as ":cabove", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
*:cbe* *:cbelow* *:cbel* *:cbelow*
:[count]cbe[low] Go to the [count] error below the current line in the :[count]cbel[ow] Go to the [count] error below the current line in the
current buffer. If [count] is omitted, then 1 is current buffer. If [count] is omitted, then 1 is
used. If there are no errors, then an error message used. If there are no errors, then an error message
is displayed. Assumes that the entries in a quickfix is displayed. Assumes that the entries in a quickfix
@ -136,8 +139,36 @@ processing a quickfix or location list command, it will be aborted.
exceeds the number of entries below the current line, exceeds the number of entries below the current line,
then the last error in the file is selected. then the last error in the file is selected.
*:lbe* *:lbelow* *:lbel* *:lbelow*
:[count]lbe[low] Same as ":cbelow", except the location list for the :[count]lbel[ow] Same as ":cbelow", except the location list for the
current window is used instead of the quickfix list.
*:cbe* *:cbefore*
:[count]cbe[fore] Go to the [count] error before the current cursor
position in the current buffer. If [count] is
omitted, then 1 is used. If there are no errors, then
an error message is displayed. Assumes that the
entries in a quickfix list are sorted by their buffer,
line and column numbers. If [count] exceeds the
number of entries before the current position, then
the first error in the file is selected.
*:lbe* *:lbefore*
:[count]lbe[fore] Same as ":cbefore", except the location list for the
current window is used instead of the quickfix list.
*:caf* *:cafter*
:[count]caf[ter] Go to the [count] error after the current cursor
position in the current buffer. If [count] is
omitted, then 1 is used. If there are no errors, then
an error message is displayed. Assumes that the
entries in a quickfix list are sorted by their buffer,
line and column numbers. If [count] exceeds the
number of entries after the current position, then
the last error in the file is selected.
*:laf* *:lafter*
:[count]laf[ter] Same as ":cafter", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
*:cnf* *:cnfile* *:cnf* *:cnfile*
@ -805,14 +836,19 @@ lists. They set one of the existing error lists as the current one.
the current window instead of the quickfix list. the current window instead of the quickfix list.
*:chistory* *:chi* *:chistory* *:chi*
:chi[story] Show the list of error lists. The current list is :[count]chi[story] Show the list of error lists. The current list is
marked with ">". The output looks like: marked with ">". The output looks like:
error list 1 of 3; 43 errors ~ error list 1 of 3; 43 errors ~
> error list 2 of 3; 0 errors ~ > error list 2 of 3; 0 errors ~
error list 3 of 3; 15 errors ~ error list 3 of 3; 15 errors ~
When [count] is given, then the count'th quickfix
list is made the current list. Example: >
" Make the 4th quickfix list current
:4chistory
<
*:lhistory* *:lhi* *:lhistory* *:lhi*
:lhi[story] Show the list of location lists, otherwise like :[count]lhi[story] Show the list of location lists, otherwise like
`:chistory`. `:chistory`.
When adding a new error list, it becomes the current list. When adding a new error list, it becomes the current list.

View File

@ -495,6 +495,9 @@ au BufNewFile,BufRead *.com call dist#ft#BindzoneCheck('dcl')
" DOT " DOT
au BufNewFile,BufRead *.dot,*.gv setf dot au BufNewFile,BufRead *.dot,*.gv setf dot
" Dune
au BufNewFile,BufRead jbuild,dune,dune-project,dune-workspace setf dune
" Dylan - lid files " Dylan - lid files
au BufNewFile,BufRead *.lid setf dylanlid au BufNewFile,BufRead *.lid setf dylanlid
@ -1121,8 +1124,8 @@ au BufNewFile,BufRead *.nse setf lua
" NSIS " NSIS
au BufNewFile,BufRead *.nsi,*.nsh setf nsis au BufNewFile,BufRead *.nsi,*.nsh setf nsis
" OCAML " OCaml
au BufNewFile,BufRead *.ml,*.mli,*.mll,*.mly,.ocamlinit setf ocaml au BufNewFile,BufRead *.ml,*.mli,*.mll,*.mly,.ocamlinit,*.mlt,*.mlp,*.mlip,*.mli.cppo,*.ml.cppo setf ocaml
" Occam " Occam
au BufNewFile,BufRead *.occ setf occam au BufNewFile,BufRead *.occ setf occam
@ -1130,6 +1133,9 @@ au BufNewFile,BufRead *.occ setf occam
" Omnimark " Omnimark
au BufNewFile,BufRead *.xom,*.xin setf omnimark au BufNewFile,BufRead *.xom,*.xin setf omnimark
" OPAM
au BufNewFile,BufRead opam,*.opam,*.opam.template setf opam
" OpenROAD " OpenROAD
au BufNewFile,BufRead *.or setf openroad au BufNewFile,BufRead *.or setf openroad
@ -1549,6 +1555,9 @@ au BufNewFile,BufRead *.scm,*.ss,*.rkt setf scheme
" Screen RC " Screen RC
au BufNewFile,BufRead .screenrc,screenrc setf screen au BufNewFile,BufRead .screenrc,screenrc setf screen
" Sexplib
au BufNewFile,BufRead *.sexp setf sexplib
" Simula " Simula
au BufNewFile,BufRead *.sim setf simula au BufNewFile,BufRead *.sim setf simula

View File

@ -55,7 +55,7 @@ end
vimcmd_start = 'syn keyword vimCommand contained ' vimcmd_start = 'syn keyword vimCommand contained '
w(vimcmd_start) w(vimcmd_start)
local prev_cmd = nil local prev_cmd = nil
for _, cmd_desc in ipairs(ex_cmds) do for _, cmd_desc in ipairs(ex_cmds.cmds) do
if lld.line_length > 850 then if lld.line_length > 850 then
w('\n' .. vimcmd_start) w('\n' .. vimcmd_start)
end end

View File

@ -5948,6 +5948,19 @@ int assert_exception(typval_T *argvars)
return 0; return 0;
} }
static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars,
const char *cmd)
FUNC_ATTR_NONNULL_ALL
{
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
char *const tofree = encode_tv2echo(&argvars[2], NULL);
ga_concat(gap, (char_u *)tofree);
xfree(tofree);
} else {
ga_concat(gap, (char_u *)cmd);
}
}
int assert_fails(typval_T *argvars) int assert_fails(typval_T *argvars)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
@ -5966,14 +5979,7 @@ int assert_fails(typval_T *argvars)
if (!called_emsg) { if (!called_emsg) {
prepare_assert_error(&ga); prepare_assert_error(&ga);
ga_concat(&ga, (const char_u *)"command did not fail: "); ga_concat(&ga, (const char_u *)"command did not fail: ");
if (argvars[1].v_type != VAR_UNKNOWN assert_append_cmd_or_arg(&ga, argvars, cmd);
&& argvars[2].v_type != VAR_UNKNOWN) {
char *const tofree = encode_tv2echo(&argvars[2], NULL);
ga_concat(&ga, (char_u *)tofree);
xfree(tofree);
} else {
ga_concat(&ga, (const char_u *)cmd);
}
assert_error(&ga); assert_error(&ga);
ga_clear(&ga); ga_clear(&ga);
ret = 1; ret = 1;
@ -5986,6 +5992,8 @@ int assert_fails(typval_T *argvars)
prepare_assert_error(&ga); prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
&vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER); &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER);
ga_concat(&ga, (char_u *)": ");
assert_append_cmd_or_arg(&ga, argvars, cmd);
assert_error(&ga); assert_error(&ga);
ga_clear(&ga); ga_clear(&ga);
ret = 1; ret = 1;

View File

@ -1032,14 +1032,15 @@ void free_prev_shellcmd(void)
* Bangs in the argument are replaced with the previously entered command. * Bangs in the argument are replaced with the previously entered command.
* Remember the argument. * Remember the argument.
*/ */
void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) void do_bang(int addr_count, exarg_T *eap, bool forceit,
bool do_in, bool do_out)
FUNC_ATTR_NONNULL_ALL
{ {
char_u *arg = eap->arg; /* command */ char_u *arg = eap->arg; // command
linenr_T line1 = eap->line1; /* start of range */ linenr_T line1 = eap->line1; // start of range
linenr_T line2 = eap->line2; /* end of range */ linenr_T line2 = eap->line2; // end of range
char_u *newcmd = NULL; /* the new command */ char_u *newcmd = NULL; // the new command
int free_newcmd = FALSE; /* need to free() newcmd */ bool free_newcmd = false; // need to free() newcmd
int ins_prevcmd;
char_u *t; char_u *t;
char_u *p; char_u *p;
char_u *trailarg; char_u *trailarg;
@ -1064,7 +1065,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out)
* Try to find an embedded bang, like in :!<cmd> ! [args] * Try to find an embedded bang, like in :!<cmd> ! [args]
* (:!! is indicated by the 'forceit' variable) * (:!! is indicated by the 'forceit' variable)
*/ */
ins_prevcmd = forceit; bool ins_prevcmd = forceit;
trailarg = arg; trailarg = arg;
do { do {
len = (int)STRLEN(trailarg) + 1; len = (int)STRLEN(trailarg) + 1;
@ -1101,7 +1102,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out)
else { else {
trailarg = p; trailarg = p;
*trailarg++ = NUL; *trailarg++ = NUL;
ins_prevcmd = TRUE; ins_prevcmd = true;
break; break;
} }
} }
@ -1131,7 +1132,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out)
STRCPY(newcmd, p_shq); STRCPY(newcmd, p_shq);
STRCAT(newcmd, prevcmd); STRCAT(newcmd, prevcmd);
STRCAT(newcmd, p_shq); STRCAT(newcmd, p_shq);
free_newcmd = TRUE; free_newcmd = true;
} }
if (addr_count == 0) { /* :! */ if (addr_count == 0) { /* :! */
/* echo the command */ /* echo the command */
@ -1164,15 +1165,15 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out)
// do this. // do this.
// Alternatively, if on Unix and redirecting input or output, but not both, // Alternatively, if on Unix and redirecting input or output, but not both,
// and the 'shelltemp' option isn't set, use pipes. // and the 'shelltemp' option isn't set, use pipes.
// We use input redirection if do_in is TRUE. // We use input redirection if do_in is true.
// We use output redirection if do_out is TRUE. // We use output redirection if do_out is true.
static void do_filter( static void do_filter(
linenr_T line1, linenr_T line1,
linenr_T line2, linenr_T line2,
exarg_T *eap, /* for forced 'ff' and 'fenc' */ exarg_T *eap, /* for forced 'ff' and 'fenc' */
char_u *cmd, char_u *cmd,
int do_in, bool do_in,
int do_out) bool do_out)
{ {
char_u *itmp = NULL; char_u *itmp = NULL;
char_u *otmp = NULL; char_u *otmp = NULL;
@ -1669,10 +1670,17 @@ void ex_update(exarg_T *eap)
*/ */
void ex_write(exarg_T *eap) void ex_write(exarg_T *eap)
{ {
if (eap->usefilter) /* input lines to shell command */ if (eap->cmdidx == CMD_saveas) {
do_bang(1, eap, FALSE, TRUE, FALSE); // :saveas does not take a range, uses all lines.
else eap->line1 = 1;
eap->line2 = curbuf->b_ml.ml_line_count;
}
if (eap->usefilter) { // input lines to shell command
do_bang(1, eap, false, true, false);
} else {
(void)do_write(eap); (void)do_write(eap);
}
} }
/* /*

File diff suppressed because it is too large Load Diff

View File

@ -2113,7 +2113,7 @@ void ex_listdo(exarg_T *eap)
} }
} else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
|| eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
qf_size = qf_get_size(eap); qf_size = qf_get_valid_size(eap);
assert(eap->line1 >= 0); assert(eap->line1 >= 0);
if (qf_size == 0 || (size_t)eap->line1 > qf_size) { if (qf_size == 0 || (size_t)eap->line1 > qf_size) {
buf = NULL; buf = NULL;

View File

@ -51,17 +51,16 @@
#define NOTRLCOM 0x800 // no trailing comment allowed #define NOTRLCOM 0x800 // no trailing comment allowed
#define ZEROR 0x1000 // zero line number allowed #define ZEROR 0x1000 // zero line number allowed
#define USECTRLV 0x2000 // do not remove CTRL-V from argument #define USECTRLV 0x2000 // do not remove CTRL-V from argument
#define NOTADR 0x4000 // number before command is not an address #define EDITCMD 0x4000 // allow "+command" argument
#define EDITCMD 0x8000 // allow "+command" argument #define BUFNAME 0x8000 // accepts buffer name
#define BUFNAME 0x10000 // accepts buffer name #define BUFUNL 0x10000L // accepts unlisted buffer too
#define BUFUNL 0x20000 // accepts unlisted buffer too #define ARGOPT 0x20000L // allow "++opt=val" argument
#define ARGOPT 0x40000 // allow "++opt=val" argument #define SBOXOK 0x40000L // allowed in the sandbox
#define SBOXOK 0x80000 // allowed in the sandbox #define CMDWIN 0x80000L // allowed in cmdline window; when missing
#define CMDWIN 0x100000 // allowed in cmdline window; when missing
// disallows editing another buffer when // disallows editing another buffer when
// curbuf_lock is set // curbuf_lock is set
#define MODIFY 0x200000 // forbidden in non-'modifiable' buffer #define MODIFY 0x100000L // forbidden in non-'modifiable' buffer
#define EXFLAGS 0x400000 // allow flags after count in argument #define EXFLAGS 0x200000L // allow flags after count in argument
#define FILES (XFILE | EXTRA) // multiple extra files allowed #define FILES (XFILE | EXTRA) // multiple extra files allowed
#define WORD1 (EXTRA | NOSPC) // one extra word allowed #define WORD1 (EXTRA | NOSPC) // one extra word allowed
#define FILE1 (FILES | NOSPC) // 1 file allowed, defaults to current file #define FILE1 (FILES | NOSPC) // 1 file allowed, defaults to current file
@ -75,8 +74,10 @@ typedef enum {
ADDR_BUFFERS, // buffer number ADDR_BUFFERS, // buffer number
ADDR_TABS, // tab page number ADDR_TABS, // tab page number
ADDR_TABS_RELATIVE, // Tab page that only relative ADDR_TABS_RELATIVE, // Tab page that only relative
ADDR_QUICKFIX_VALID, // quickfix list valid entry number
ADDR_QUICKFIX, // quickfix list entry number ADDR_QUICKFIX, // quickfix list entry number
ADDR_OTHER, // something else ADDR_UNSIGNED, // positive count or zero, defaults to 1
ADDR_OTHER, // something else, use line number for '$', '%', etc.
ADDR_NONE // no range used ADDR_NONE // no range used
} cmd_addr_T; } cmd_addr_T;
@ -153,7 +154,7 @@ struct exarg {
int addr_count; ///< the number of addresses given int addr_count; ///< the number of addresses given
linenr_T line1; ///< the first line number linenr_T line1; ///< the first line number
linenr_T line2; ///< the second line number or count linenr_T line2; ///< the second line number or count
int addr_type; ///< type of the count/range cmd_addr_T addr_type; ///< type of the count/range
int flags; ///< extra flags after count: EXFLAG_ int flags; ///< extra flags after count: EXFLAG_
char_u *do_ecmd_cmd; ///< +command arg to be used in edited file char_u *do_ecmd_cmd; ///< +command arg to be used in edited file
linenr_T do_ecmd_lnum; ///< the line number in an edited file linenr_T do_ecmd_lnum; ///< the line number in an edited file

View File

@ -1086,7 +1086,10 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset)
return buf->b_fnum; return buf->b_fnum;
} }
static int current_win_nr(win_T *win) // Return the window number of "win".
// When "win" is NULL return the number of windows.
static int current_win_nr(const win_T *win)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{ {
int nr = 0; int nr = 0;
@ -1163,7 +1166,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap)
case 'd': case 'd':
case Ctrl_D: case Ctrl_D:
// window size or any count // window size or any count
eap->addr_type = ADDR_LINES; // -V1037 eap->addr_type = ADDR_OTHER; // -V1037
break; break;
case Ctrl_HAT: case Ctrl_HAT:
@ -1199,7 +1202,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap)
case '=': case '=':
case CAR: case CAR:
// no count // no count
eap->addr_type = 0; eap->addr_type = ADDR_NONE;
break; break;
} }
} }
@ -1380,6 +1383,10 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (ea.cmdidx == CMD_wincmd && p != NULL) { if (ea.cmdidx == CMD_wincmd && p != NULL) {
get_wincmd_addr_type(skipwhite(p), &ea); get_wincmd_addr_type(skipwhite(p), &ea);
} }
// :.cc in quickfix window uses line number
if ((ea.cmdidx == CMD_cc || ea.cmdidx == CMD_ll) && bt_quickfix(curbuf)) {
ea.addr_type = ADDR_OTHER;
}
} }
ea.cmd = cmd; ea.cmd = cmd;
@ -1490,9 +1497,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.forceit = false; ea.forceit = false;
} }
/* // 6. Parse arguments. Then check for errors.
* 6. Parse arguments.
*/
if (!IS_USER_CMDIDX(ea.cmdidx)) { if (!IS_USER_CMDIDX(ea.cmdidx)) {
ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt;
} }
@ -1547,12 +1552,10 @@ static char_u * do_one_cmd(char_u **cmdlinep,
* Don't complain about the range if it is not used * Don't complain about the range if it is not used
* (could happen if line_count is accidentally set to 0). * (could happen if line_count is accidentally set to 0).
*/ */
if (!ea.skip && !ni) { if (!ea.skip && !ni && (ea.argt & RANGE)) {
/* // If the range is backwards, ask for confirmation and, if given, swap
* If the range is backwards, ask for confirmation and, if given, swap // ea.line1 & ea.line2 so it's forwards again.
* ea.line1 & ea.line2 so it's forwards again. // When global command is busy, don't ask, will fail below.
* When global command is busy, don't ask, will fail below.
*/
if (!global_busy && ea.line1 > ea.line2) { if (!global_busy && ea.line1 > ea.line2) {
if (msg_silent == 0) { if (msg_silent == 0) {
if ((flags & DOCMD_VERBOSE) || exmode_active) { if ((flags & DOCMD_VERBOSE) || exmode_active) {
@ -1571,8 +1574,10 @@ static char_u * do_one_cmd(char_u **cmdlinep,
goto doend; goto doend;
} }
if ((ea.argt & NOTADR) && ea.addr_count == 0) /* default is 1, not cursor */ if ((ea.addr_type == ADDR_OTHER) && ea.addr_count == 0) {
// default is 1, not cursor
ea.line2 = 1; ea.line2 = 1;
}
correct_range(&ea); correct_range(&ea);
@ -1694,6 +1699,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.line1 = 1; ea.line1 = 1;
switch (ea.addr_type) { switch (ea.addr_type) {
case ADDR_LINES: case ADDR_LINES:
case ADDR_OTHER:
ea.line2 = curbuf->b_ml.ml_line_count; ea.line2 = curbuf->b_ml.ml_line_count;
break; break;
case ADDR_LOADED_BUFFERS: case ADDR_LOADED_BUFFERS:
@ -1728,14 +1734,17 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.line2 = ARGCOUNT; ea.line2 = ARGCOUNT;
} }
break; break;
case ADDR_QUICKFIX: case ADDR_QUICKFIX_VALID:
ea.line2 = qf_get_size(&ea); ea.line2 = qf_get_valid_size(&ea);
if (ea.line2 == 0) { if (ea.line2 == 0) {
ea.line2 = 1; ea.line2 = 1;
} }
break; break;
case ADDR_NONE: case ADDR_NONE:
IEMSG(_("INTERNAL: Cannot use DFLALL with ADDR_NONE")); case ADDR_UNSIGNED:
case ADDR_QUICKFIX:
IEMSG(_("INTERNAL: Cannot use DFLALL "
"with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
break; break;
} }
} }
@ -1771,7 +1780,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
errormsg = (char_u *)_(e_zerocount); errormsg = (char_u *)_(e_zerocount);
goto doend; goto doend;
} }
if (ea.argt & NOTADR) { /* e.g. :buffer 2, :sleep 3 */ if (ea.addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3
ea.line2 = n; ea.line2 = n;
if (ea.addr_count == 0) if (ea.addr_count == 0)
ea.addr_count = 1; ea.addr_count = 1;
@ -1780,8 +1789,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.line2 += n - 1; ea.line2 += n - 1;
++ea.addr_count; ++ea.addr_count;
// Be vi compatible: no error message for out of range. // Be vi compatible: no error message for out of range.
if (ea.addr_type == ADDR_LINES if (ea.line2 > curbuf->b_ml.ml_line_count) {
&& ea.line2 > curbuf->b_ml.ml_line_count) {
ea.line2 = curbuf->b_ml.ml_line_count; ea.line2 = curbuf->b_ml.ml_line_count;
} }
} }
@ -2325,6 +2333,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent)
eap->line1 = eap->line2; eap->line1 = eap->line2;
switch (eap->addr_type) { switch (eap->addr_type) {
case ADDR_LINES: case ADDR_LINES:
case ADDR_OTHER:
// default is current line number // default is current line number
eap->line2 = curwin->w_cursor.lnum; eap->line2 = curwin->w_cursor.lnum;
break; break;
@ -2345,9 +2354,13 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent)
eap->line2 = CURRENT_TAB_NR; eap->line2 = CURRENT_TAB_NR;
break; break;
case ADDR_TABS_RELATIVE: case ADDR_TABS_RELATIVE:
case ADDR_UNSIGNED:
eap->line2 = 1; eap->line2 = 1;
break; break;
case ADDR_QUICKFIX: case ADDR_QUICKFIX:
eap->line2 = qf_get_cur_idx(eap);
break;
case ADDR_QUICKFIX_VALID:
eap->line2 = qf_get_cur_valid_idx(eap); eap->line2 = qf_get_cur_valid_idx(eap);
break; break;
case ADDR_NONE: case ADDR_NONE:
@ -2365,6 +2378,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent)
eap->cmd++; eap->cmd++;
switch (eap->addr_type) { switch (eap->addr_type) {
case ADDR_LINES: case ADDR_LINES:
case ADDR_OTHER:
eap->line1 = 1; eap->line1 = 1;
eap->line2 = curbuf->b_ml.ml_line_count; eap->line2 = curbuf->b_ml.ml_line_count;
break; break;
@ -2400,7 +2414,8 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent)
} }
break; break;
case ADDR_TABS_RELATIVE: case ADDR_TABS_RELATIVE:
case ADDR_OTHER: case ADDR_UNSIGNED:
case ADDR_QUICKFIX:
*errormsg = (char_u *)_(e_invrange); *errormsg = (char_u *)_(e_invrange);
return FAIL; return FAIL;
case ADDR_ARGUMENTS: case ADDR_ARGUMENTS:
@ -2411,9 +2426,9 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent)
eap->line2 = ARGCOUNT; eap->line2 = ARGCOUNT;
} }
break; break;
case ADDR_QUICKFIX: case ADDR_QUICKFIX_VALID:
eap->line1 = 1; eap->line1 = 1;
eap->line2 = qf_get_size(eap); eap->line2 = qf_get_valid_size(eap);
if (eap->line2 == 0) { if (eap->line2 == 0) {
eap->line2 = 1; eap->line2 = 1;
} }
@ -2522,14 +2537,13 @@ static void append_command(char_u *cmd)
*d = NUL; *d = NUL;
} }
/* // Find an Ex command by its name, either built-in or user.
* Find an Ex command by its name, either built-in or user. // Start of the name can be found at eap->cmd.
* Start of the name can be found at eap->cmd. // Sets eap->cmdidx and returns a pointer to char after the command name.
* Returns pointer to char after the command name. // "full" is set to TRUE if the whole command name matched.
* "full" is set to TRUE if the whole command name matched. // Returns NULL for an ambiguous user command.
* Returns NULL for an ambiguous user command.
*/
static char_u *find_command(exarg_T *eap, int *full) static char_u *find_command(exarg_T *eap, int *full)
FUNC_ATTR_NONNULL_ARG(1)
{ {
int len; int len;
char_u *p; char_u *p;
@ -3716,14 +3730,13 @@ char_u *skip_range(
// Return MAXLNUM when no Ex address was found. // Return MAXLNUM when no Ex address was found.
static linenr_T get_address(exarg_T *eap, static linenr_T get_address(exarg_T *eap,
char_u **ptr, char_u **ptr,
cmd_addr_T addr_type_arg, cmd_addr_T addr_type,
int skip, // only skip the address, don't use it int skip, // only skip the address, don't use it
bool silent, // no errors or side effects bool silent, // no errors or side effects
int to_other_file, // flag: may jump to other file int to_other_file, // flag: may jump to other file
int address_count) // 1 for first, >1 after comma int address_count) // 1 for first, >1 after comma
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
const int addr_type = addr_type_arg;
int c; int c;
int i; int i;
long n; long n;
@ -3741,6 +3754,7 @@ static linenr_T get_address(exarg_T *eap,
++cmd; ++cmd;
switch (addr_type) { switch (addr_type) {
case ADDR_LINES: case ADDR_LINES:
case ADDR_OTHER:
lnum = curwin->w_cursor.lnum; lnum = curwin->w_cursor.lnum;
break; break;
case ADDR_WINDOWS: case ADDR_WINDOWS:
@ -3758,11 +3772,15 @@ static linenr_T get_address(exarg_T *eap,
break; break;
case ADDR_TABS_RELATIVE: case ADDR_TABS_RELATIVE:
case ADDR_NONE: case ADDR_NONE:
case ADDR_UNSIGNED:
EMSG(_(e_invrange)); EMSG(_(e_invrange));
cmd = NULL; cmd = NULL;
goto error; goto error;
break; break;
case ADDR_QUICKFIX: case ADDR_QUICKFIX:
lnum = qf_get_cur_idx(eap);
break;
case ADDR_QUICKFIX_VALID:
lnum = qf_get_cur_valid_idx(eap); lnum = qf_get_cur_valid_idx(eap);
break; break;
} }
@ -3772,6 +3790,7 @@ static linenr_T get_address(exarg_T *eap,
++cmd; ++cmd;
switch (addr_type) { switch (addr_type) {
case ADDR_LINES: case ADDR_LINES:
case ADDR_OTHER:
lnum = curbuf->b_ml.ml_line_count; lnum = curbuf->b_ml.ml_line_count;
break; break;
case ADDR_WINDOWS: case ADDR_WINDOWS:
@ -3798,6 +3817,7 @@ static linenr_T get_address(exarg_T *eap,
break; break;
case ADDR_TABS_RELATIVE: case ADDR_TABS_RELATIVE:
case ADDR_NONE: case ADDR_NONE:
case ADDR_UNSIGNED:
EMSG(_(e_invrange)); EMSG(_(e_invrange));
cmd = NULL; cmd = NULL;
goto error; goto error;
@ -3808,6 +3828,12 @@ static linenr_T get_address(exarg_T *eap,
lnum = 1; lnum = 1;
} }
break; break;
case ADDR_QUICKFIX_VALID:
lnum = qf_get_valid_size(eap);
if (lnum == 0) {
lnum = 1;
}
break;
} }
break; break;
@ -3939,7 +3965,9 @@ static linenr_T get_address(exarg_T *eap,
if (lnum == MAXLNUM) { if (lnum == MAXLNUM) {
switch (addr_type) { switch (addr_type) {
case ADDR_LINES: case ADDR_LINES:
lnum = curwin->w_cursor.lnum; /* "+1" is same as ".+1" */ case ADDR_OTHER:
// "+1" is same as ".+1"
lnum = curwin->w_cursor.lnum;
break; break;
case ADDR_WINDOWS: case ADDR_WINDOWS:
lnum = CURRENT_WIN_NR; lnum = CURRENT_WIN_NR;
@ -3958,9 +3986,14 @@ static linenr_T get_address(exarg_T *eap,
lnum = 1; lnum = 1;
break; break;
case ADDR_QUICKFIX: case ADDR_QUICKFIX:
lnum = qf_get_cur_idx(eap);
break;
case ADDR_QUICKFIX_VALID:
lnum = qf_get_cur_valid_idx(eap); lnum = qf_get_cur_valid_idx(eap);
break; break;
case ADDR_NONE: case ADDR_NONE:
case ADDR_UNSIGNED:
lnum = 0;
break; break;
} }
} }
@ -4052,11 +4085,10 @@ static char_u *invalid_range(exarg_T *eap)
} }
if (eap->argt & RANGE) { if (eap->argt & RANGE) {
switch(eap->addr_type) { switch (eap->addr_type) {
case ADDR_LINES: case ADDR_LINES:
if (!(eap->argt & NOTADR) if (eap->line2 > (curbuf->b_ml.ml_line_count
&& eap->line2 > curbuf->b_ml.ml_line_count + (eap->cmdidx == CMD_diffget))) {
+ (eap->cmdidx == CMD_diffget)) {
return (char_u *)_(e_invrange); return (char_u *)_(e_invrange);
} }
break; break;
@ -4105,11 +4137,24 @@ static char_u *invalid_range(exarg_T *eap)
} }
break; break;
case ADDR_TABS_RELATIVE: case ADDR_TABS_RELATIVE:
// Do nothing case ADDR_OTHER:
// Any range is OK.
break; break;
case ADDR_QUICKFIX: case ADDR_QUICKFIX:
assert(eap->line2 >= 0); assert(eap->line2 >= 0);
if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) { // No error for value that is too big, will use the last entry.
if (eap->line2 <= 0) {
return (char_u *)_(e_invrange);
}
break;
case ADDR_QUICKFIX_VALID:
if ((eap->line2 != 1 && (size_t)eap->line2 > qf_get_valid_size(eap))
|| eap->line2 < 0) {
return (char_u *)_(e_invrange);
}
break;
case ADDR_UNSIGNED:
if (eap->line2 < 0) {
return (char_u *)_(e_invrange); return (char_u *)_(e_invrange);
} }
break; break;
@ -5376,7 +5421,7 @@ two_count:
} }
*def = getdigits_long(&p, true, 0); *def = getdigits_long(&p, true, 0);
*argt |= (ZEROR | NOTADR); *argt |= ZEROR;
if (p != val + vallen || vallen == 0) { if (p != val + vallen || vallen == 0) {
invalid_count: invalid_count:
@ -5384,8 +5429,16 @@ invalid_count:
return FAIL; return FAIL;
} }
} }
// default for -range is using buffer lines
if (*addr_type_arg == ADDR_NONE) {
*addr_type_arg = ADDR_LINES;
}
} else if (STRNICMP(attr, "count", attrlen) == 0) { } else if (STRNICMP(attr, "count", attrlen) == 0) {
*argt |= (COUNT | ZEROR | RANGE | NOTADR); *argt |= (COUNT | ZEROR | RANGE);
// default for -count is using any number
if (*addr_type_arg == ADDR_NONE) {
*addr_type_arg = ADDR_OTHER;
}
if (val != NULL) { if (val != NULL) {
p = val; p = val;
@ -5416,11 +5469,11 @@ invalid_count:
EMSG(_("E179: argument required for -addr")); EMSG(_("E179: argument required for -addr"));
return FAIL; return FAIL;
} }
if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) == FAIL) { if (parse_addr_type_arg(val, (int)vallen, addr_type_arg) == FAIL) {
return FAIL; return FAIL;
} }
if (*addr_type_arg != ADDR_LINES) { if (*addr_type_arg != ADDR_LINES) {
*argt |= (ZEROR | NOTADR); *argt |= ZEROR;
} }
} else { } else {
char_u ch = attr[len]; char_u ch = attr[len];
@ -5447,7 +5500,7 @@ static void ex_command(exarg_T *eap)
int flags = 0; int flags = 0;
int compl = EXPAND_NOTHING; int compl = EXPAND_NOTHING;
char_u *compl_arg = NULL; char_u *compl_arg = NULL;
cmd_addr_T addr_type_arg = ADDR_LINES; cmd_addr_T addr_type_arg = ADDR_NONE;
int has_attr = (eap->arg[0] == '-'); int has_attr = (eap->arg[0] == '-');
int name_len; int name_len;
@ -6105,8 +6158,7 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx)
/* /*
* Parse address type argument * Parse address type argument
*/ */
int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg)
cmd_addr_T *addr_type_arg)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
int i, a, b; int i, a, b;
@ -6129,9 +6181,6 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt,
return FAIL; return FAIL;
} }
if (*addr_type_arg != ADDR_LINES)
*argt |= NOTADR;
return OK; return OK;
} }
@ -7423,11 +7472,12 @@ static void ex_read(exarg_T *eap)
int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
linenr_T lnum; linenr_T lnum;
if (eap->usefilter) /* :r!cmd */ if (eap->usefilter) { // :r!cmd
do_bang(1, eap, FALSE, FALSE, TRUE); do_bang(1, eap, false, false, true);
else { } else {
if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) {
return; return;
}
if (*eap->arg == NUL) { if (*eap->arg == NUL) {
if (check_fname() == FAIL) /* check for no file name */ if (check_fname() == FAIL) /* check for no file name */
@ -7897,7 +7947,7 @@ static void ex_at(exarg_T *eap)
*/ */
static void ex_bang(exarg_T *eap) static void ex_bang(exarg_T *eap)
{ {
do_bang(eap->addr_count, eap, eap->forceit, TRUE, TRUE); do_bang(eap->addr_count, eap, eap->forceit, true, true);
} }
/* /*

View File

@ -22,7 +22,10 @@ local defsfname = autodir .. '/ex_cmds_defs.generated.h'
local enumfile = io.open(enumfname, 'w') local enumfile = io.open(enumfname, 'w')
local defsfile = io.open(defsfname, 'w') local defsfile = io.open(defsfname, 'w')
local defs = require('ex_cmds') local bit = require 'bit'
local ex_cmds = require('ex_cmds')
local defs = ex_cmds.cmds
local flags = ex_cmds.flags
local byte_a = string.byte('a') local byte_a = string.byte('a')
local byte_z = string.byte('z') local byte_z = string.byte('z')
@ -51,6 +54,17 @@ static CommandDefinition cmdnames[%u] = {
]], #defs, #defs)) ]], #defs, #defs))
local cmds, cmdidxs1, cmdidxs2 = {}, {}, {} local cmds, cmdidxs1, cmdidxs2 = {}, {}, {}
for _, cmd in ipairs(defs) do for _, cmd in ipairs(defs) do
if bit.band(cmd.flags, flags.RANGE) == flags.RANGE then
assert(cmd.addr_type ~= 'ADDR_NONE',
string.format('ex_cmds.lua:%s: Using RANGE with ADDR_NONE\n', cmd.command))
else
assert(cmd.addr_type == 'ADDR_NONE',
string.format('ex_cmds.lua:%s: Missing ADDR_NONE\n', cmd.command))
end
if bit.band(cmd.flags, flags.DFLALL) == flags.DFLALL then
assert(cmd.addr_type ~= 'ADDR_OTHER' and cmd.addr_type ~= 'ADDR_NONE',
string.format('ex_cmds.lua:%s: Missing misplaced DFLALL\n', cmd.command))
end
local enumname = cmd.enum or ('CMD_' .. cmd.command) local enumname = cmd.enum or ('CMD_' .. cmd.command)
local byte_cmd = cmd.command:sub(1, 1):byte() local byte_cmd = cmd.command:sub(1, 1):byte()
if byte_a <= byte_cmd and byte_cmd <= byte_z then if byte_a <= byte_cmd and byte_cmd <= byte_z then

View File

@ -3300,6 +3300,24 @@ void qf_history(exarg_T *eap)
qf_info_T *qi = qf_cmd_get_stack(eap, false); qf_info_T *qi = qf_cmd_get_stack(eap, false);
int i; int i;
if (eap->addr_count > 0) {
if (qi == NULL) {
EMSG(_(e_loclist));
return;
}
// Jump to the specified quickfix list
if (eap->line2 > 0 && eap->line2 <= qi->qf_listcount) {
qi->qf_curlist = (int)(eap->line2 - 1);
qf_msg(qi, qi->qf_curlist, "");
qf_update_buffer(qi, NULL);
} else {
EMSG(_(e_invrange));
}
return;
}
if (qf_stack_empty(qi)) { if (qf_stack_empty(qi)) {
MSG(_("No entries")); MSG(_("No entries"));
} else { } else {
@ -3622,7 +3640,7 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height)
// Set the options for the quickfix buffer/window (if not already done) // Set the options for the quickfix buffer/window (if not already done)
// Do this even if the quickfix buffer was already present, as an autocmd // Do this even if the quickfix buffer was already present, as an autocmd
// might have previously deleted (:bdelete) the quickfix buffer. // might have previously deleted (:bdelete) the quickfix buffer.
if (curbuf->b_p_bt[0] != 'q') { if (!bt_quickfix(curbuf)) {
qf_set_cwindow_options(); qf_set_cwindow_options();
} }
@ -4269,9 +4287,20 @@ static char_u *get_mef_name(void)
return name; return name;
} }
/// Returns the number of valid entries in the current quickfix/location list. /// Returns the number of entries in the current quickfix/location list.
size_t qf_get_size(exarg_T *eap) size_t qf_get_size(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{
qf_info_T *qi;
if ((qi = qf_cmd_get_stack(eap, false)) == NULL) {
return 0;
}
return (size_t)qf_get_curlist(qi)->qf_count;
}
/// Returns the number of valid entries in the current quickfix/location list.
size_t qf_get_valid_size(exarg_T *eap)
{ {
qf_info_T *qi; qf_info_T *qi;
qf_list_T *qfl; qf_list_T *qfl;
@ -4562,74 +4591,150 @@ static qfline_T * qf_find_last_entry_on_line(qfline_T *entry, int *errornr)
return entry; return entry;
} }
/// Find the first quickfix entry below line 'lnum' in buffer 'bnr'. // Returns true if the specified quickfix entry is
// after the given line (linewise is true)
// or after the line and column.
static bool qf_entry_after_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum > pos->lnum;
}
return qfp->qf_lnum > pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col > pos->col);
}
// Returns true if the specified quickfix entry is
// before the given line (linewise is true)
// or before the line and column.
static bool qf_entry_before_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum < pos->lnum;
}
return qfp->qf_lnum < pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col < pos->col);
}
// Returns true if the specified quickfix entry is
// on or after the given line (linewise is true)
// or on or after the line and column.
static bool qf_entry_on_or_after_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum >= pos->lnum;
}
return qfp->qf_lnum > pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col >= pos->col);
}
// Returns true if the specified quickfix entry is
// on or before the given line (linewise is true)
// or on or before the line and column.
static bool qf_entry_on_or_before_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum <= pos->lnum;
}
return qfp->qf_lnum < pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col <= pos->col);
}
/// Find the first quickfix entry after position 'pos' in buffer 'bnr'.
/// If 'linewise' is true, returns the entry after the specified line and treats
/// multiple entries on a single line as one. Otherwise returns the entry after
/// the specified line and column.
/// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// 'qfp' points to the very first entry in the buffer and 'errornr' is the
/// index of the very first entry in the quickfix list. /// index of the very first entry in the quickfix list.
/// Returns NULL if an entry is not found after 'lnum'. /// Returns NULL if an entry is not found after 'pos'.
static qfline_T *qf_find_entry_on_next_line(int bnr, static qfline_T *qf_find_entry_after_pos(
linenr_T lnum, int bnr,
const pos_T *pos,
bool linewise,
qfline_T *qfp, qfline_T *qfp,
int *errornr) int *errornr
)
FUNC_ATTR_NONNULL_ALL
{ {
if (qfp->qf_lnum > lnum) { if (qf_entry_after_pos(qfp, pos, linewise)) {
// First entry is after line 'lnum' // First entry is after postion 'pos'
return qfp; return qfp;
} }
// Find the entry just before or at the line 'lnum' // Find the entry just before or at the position 'pos'
while (qfp->qf_next != NULL while (qfp->qf_next != NULL
&& qfp->qf_next->qf_fnum == bnr && qfp->qf_next->qf_fnum == bnr
&& qfp->qf_next->qf_lnum <= lnum) { && qf_entry_on_or_before_pos(qfp->qf_next, pos, linewise)) {
qfp = qfp->qf_next; qfp = qfp->qf_next;
(*errornr)++; (*errornr)++;
} }
if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) { if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) {
// No entries found after 'lnum' // No entries found after position 'pos'
return NULL; return NULL;
} }
// Use the entry just after line 'lnum' // Use the entry just after position 'pos'
qfp = qfp->qf_next; qfp = qfp->qf_next;
(*errornr)++; (*errornr)++;
return qfp; return qfp;
} }
/// Find the first quickfix entry before line 'lnum' in buffer 'bnr'. /// Find the first quickfix entry before position 'pos' in buffer 'bnr'.
/// If 'linewise' is true, returns the entry before the specified line and
/// treats multiple entries on a single line as one. Otherwise returns the entry
/// before the specified line and column.
/// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// 'qfp' points to the very first entry in the buffer and 'errornr' is the
/// index of the very first entry in the quickfix list. /// index of the very first entry in the quickfix list.
/// Returns NULL if an entry is not found before 'lnum'. /// Returns NULL if an entry is not found before 'pos'.
static qfline_T *qf_find_entry_on_prev_line(int bnr, static qfline_T *qf_find_entry_before_pos(
linenr_T lnum, int bnr,
const pos_T *pos,
bool linewise,
qfline_T *qfp, qfline_T *qfp,
int *errornr) int *errornr
)
FUNC_ATTR_NONNULL_ALL
{ {
// Find the entry just before the line 'lnum' // Find the entry just before the position 'pos'
while (qfp->qf_next != NULL while (qfp->qf_next != NULL
&& qfp->qf_next->qf_fnum == bnr && qfp->qf_next->qf_fnum == bnr
&& qfp->qf_next->qf_lnum < lnum) { && qf_entry_before_pos(qfp->qf_next, pos, linewise)) {
qfp = qfp->qf_next; qfp = qfp->qf_next;
(*errornr)++; (*errornr)++;
} }
if (qfp->qf_lnum >= lnum) { // entry is after 'lnum' if (qf_entry_on_or_after_pos(qfp, pos, linewise)) {
return NULL; return NULL;
} }
if (linewise) {
// If multiple entries are on the same line, then use the first entry // If multiple entries are on the same line, then use the first entry
qfp = qf_find_first_entry_on_line(qfp, errornr); qfp = qf_find_first_entry_on_line(qfp, errornr);
}
return qfp; return qfp;
} }
/// Find a quickfix entry in 'qfl' closest to line 'lnum' in buffer 'bnr' in /// Find a quickfix entry in 'qfl' closest to position 'pos' in buffer 'bnr' in
/// the direction 'dir'. /// the direction 'dir'.
static qfline_T *qf_find_closest_entry(qf_list_T *qfl, static qfline_T *qf_find_closest_entry(
qf_list_T *qfl,
int bnr, int bnr,
linenr_T lnum, const pos_T *pos,
int dir, Direction dir,
int *errornr) bool linewise,
int *errornr
)
FUNC_ATTR_NONNULL_ALL
{ {
qfline_T *qfp; qfline_T *qfp;
@ -4642,33 +4747,38 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl,
} }
if (dir == FORWARD) { if (dir == FORWARD) {
qfp = qf_find_entry_on_next_line(bnr, lnum, qfp, errornr); qfp = qf_find_entry_after_pos(bnr, pos, linewise, qfp, errornr);
} else { } else {
qfp = qf_find_entry_on_prev_line(bnr, lnum, qfp, errornr); qfp = qf_find_entry_before_pos(bnr, pos, linewise, qfp, errornr);
} }
return qfp; return qfp;
} }
/// Get the nth quickfix entry below the specified entry treating multiple /// Get the nth quickfix entry below the specified entry. Searches forward in
/// entries on a single line as one. Searches forward in the list. /// the list. If linewise is true, then treat multiple entries on a single line
static void qf_get_nth_below_entry(qfline_T *entry, /// as one.
int *errornr, static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n,
linenr_T n) bool linewise, int *errornr)
FUNC_ATTR_NONNULL_ALL
{ {
while (n-- > 0 && !got_int) { while (n-- > 0 && !got_int) {
// qfline_T *first_entry = entry; // qfline_T *first_entry = entry;
int first_errornr = *errornr; int first_errornr = *errornr;
if (linewise) {
// Treat all the entries on the same line in this file as one // Treat all the entries on the same line in this file as one
entry = qf_find_last_entry_on_line(entry, errornr); entry = qf_find_last_entry_on_line(entry, errornr);
}
if (entry->qf_next == NULL if (entry->qf_next == NULL
|| entry->qf_next->qf_fnum != entry->qf_fnum) { || entry->qf_next->qf_fnum != entry->qf_fnum) {
if (linewise) {
// If multiple entries are on the same line, then use the first // If multiple entries are on the same line, then use the first
// entry // entry
// entry = first_entry; // entry = first_entry;
*errornr = first_errornr; *errornr = first_errornr;
}
break; break;
} }
@ -4677,11 +4787,12 @@ static void qf_get_nth_below_entry(qfline_T *entry,
} }
} }
/// Get the nth quickfix entry above the specified entry treating multiple /// Get the nth quickfix entry above the specified entry. Searches backwards in
/// entries on a single line as one. Searches backwards in the list. /// the list. If linewise is TRUE, then treat multiple entries on a single line
static void qf_get_nth_above_entry(qfline_T *entry, /// as one.
int *errornr, static void qf_get_nth_above_entry(qfline_T *entry, linenr_T n,
linenr_T n) bool linewise, int *errornr)
FUNC_ATTR_NONNULL_ALL
{ {
while (n-- > 0 && !got_int) { while (n-- > 0 && !got_int) {
if (entry->qf_prev == NULL if (entry->qf_prev == NULL
@ -4692,25 +4803,30 @@ static void qf_get_nth_above_entry(qfline_T *entry,
entry = entry->qf_prev; entry = entry->qf_prev;
(*errornr)--; (*errornr)--;
// If multiple entries are on the same line, then use the first entry if (linewise) {
entry = qf_find_first_entry_on_line(entry, errornr); entry = qf_find_first_entry_on_line(entry, errornr);
} }
}
} }
/// Find the n'th quickfix entry adjacent to line 'lnum' in buffer 'bnr' in the /// Find the n'th quickfix entry adjacent to position 'pos' in buffer 'bnr' in
/// specified direction. /// the specified direction. Returns the error number in the quickfix list or 0
/// Returns the error number in the quickfix list or 0 if an entry is not found. /// if an entry is not found.
static int qf_find_nth_adj_entry(qf_list_T *qfl, static int qf_find_nth_adj_entry(
qf_list_T *qfl,
int bnr, int bnr,
linenr_T lnum, pos_T *pos,
linenr_T n, linenr_T n,
int dir) Direction dir,
bool linewise
)
FUNC_ATTR_NONNULL_ALL
{ {
qfline_T *adj_entry;
int errornr; int errornr;
// Find an entry closest to the specified line // Find an entry closest to the specified position
adj_entry = qf_find_closest_entry(qfl, bnr, lnum, dir, &errornr); qfline_T *const adj_entry = qf_find_closest_entry(qfl, bnr, pos, dir,
linewise, &errornr);
if (adj_entry == NULL) { if (adj_entry == NULL) {
return 0; return 0;
} }
@ -4718,24 +4834,25 @@ static int qf_find_nth_adj_entry(qf_list_T *qfl,
if (--n > 0) { if (--n > 0) {
// Go to the n'th entry in the current buffer // Go to the n'th entry in the current buffer
if (dir == FORWARD) { if (dir == FORWARD) {
qf_get_nth_below_entry(adj_entry, &errornr, n); qf_get_nth_below_entry(adj_entry, n, linewise, &errornr);
} else { } else {
qf_get_nth_above_entry(adj_entry, &errornr, n); qf_get_nth_above_entry(adj_entry, n, linewise, &errornr);
} }
} }
return errornr; return errornr;
} }
/// Jump to a quickfix entry in the current file nearest to the current line. /// Jump to a quickfix entry in the current file nearest to the current line or
/// ":cabove", ":cbelow", ":labove" and ":lbelow" commands /// current line/col.
/// ":cabove", ":cbelow", ":labove", ":lbelow", ":cafter", ":cbefore",
/// ":lafter" and ":lbefore" commands
void ex_cbelow(exarg_T *eap) void ex_cbelow(exarg_T *eap)
{ {
qf_info_T *qi; qf_info_T *qi;
qf_list_T *qfl; qf_list_T *qfl;
int dir; int dir;
int buf_has_flag; int buf_has_flag;
int errornr = 0;
if (eap->addr_count > 0 && eap->line2 <= 0) { if (eap->addr_count > 0 && eap->line2 <= 0) {
EMSG(_(e_invrange)); EMSG(_(e_invrange));
@ -4743,7 +4860,8 @@ void ex_cbelow(exarg_T *eap)
} }
// Check whether the current buffer has any quickfix entries // Check whether the current buffer has any quickfix entries
if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow) { if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow
|| eap->cmdidx == CMD_cbefore || eap->cmdidx == CMD_cafter) {
buf_has_flag = BUF_HAS_QF_ENTRY; buf_has_flag = BUF_HAS_QF_ENTRY;
} else { } else {
buf_has_flag = BUF_HAS_LL_ENTRY; buf_has_flag = BUF_HAS_LL_ENTRY;
@ -4764,14 +4882,30 @@ void ex_cbelow(exarg_T *eap)
return; return;
} }
if (eap->cmdidx == CMD_cbelow || eap->cmdidx == CMD_lbelow) { if (eap->cmdidx == CMD_cbelow
|| eap->cmdidx == CMD_lbelow
|| eap->cmdidx == CMD_cafter
|| eap->cmdidx == CMD_lafter) {
// Forward motion commands
dir = FORWARD; dir = FORWARD;
} else { } else {
dir = BACKWARD; dir = BACKWARD;
} }
errornr = qf_find_nth_adj_entry(qfl, curbuf->b_fnum, curwin->w_cursor.lnum, pos_T pos = curwin->w_cursor;
eap->addr_count > 0 ? eap->line2 : 0, dir); // A quickfix entry column number is 1 based whereas cursor column
// number is 0 based. Adjust the column number.
pos.col++;
const int errornr = qf_find_nth_adj_entry(
qfl,
curbuf->b_fnum,
&pos,
eap->addr_count > 0 ? eap->line2 : 0,
dir,
eap->cmdidx == CMD_cbelow
|| eap->cmdidx == CMD_lbelow
|| eap->cmdidx == CMD_cabove
|| eap->cmdidx == CMD_labove);
if (errornr > 0) { if (errornr > 0) {
qf_jump(qi, 0, errornr, false); qf_jump(qi, 0, errornr, false);
@ -6818,8 +6952,9 @@ void ex_helpgrep(exarg_T *eap)
if (au_name != NULL) { if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, true, curbuf); curbuf->b_fname, true, curbuf);
if (!new_qi && IS_LL_STACK(qi) && qf_find_buf(qi) == NULL) { // When adding a location list to an existing location list stack,
// autocommands made "qi" invalid // if the autocmd made the stack invalid, then just return.
if (!new_qi && IS_LL_STACK(qi) && qf_find_win_with_loclist(qi) == NULL) {
decr_quickfix_busy(); decr_quickfix_busy();
return; return;
} }

View File

@ -150,6 +150,7 @@ let s:filename_checks = {
\ 'dsl': ['file.dsl'], \ 'dsl': ['file.dsl'],
\ 'dtd': ['file.dtd'], \ 'dtd': ['file.dtd'],
\ 'dts': ['file.dts', 'file.dtsi'], \ 'dts': ['file.dts', 'file.dtsi'],
\ 'dune': ['jbuild', 'dune', 'dune-project', 'dune-workspace'],
\ 'dylan': ['file.dylan'], \ 'dylan': ['file.dylan'],
\ 'dylanintr': ['file.intr'], \ 'dylanintr': ['file.intr'],
\ 'dylanlid': ['file.lid'], \ 'dylanlid': ['file.lid'],
@ -322,9 +323,10 @@ let s:filename_checks = {
\ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom'], \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom'],
\ 'nsis': ['file.nsi', 'file.nsh'], \ 'nsis': ['file.nsi', 'file.nsh'],
\ 'obj': ['file.obj'], \ 'obj': ['file.obj'],
\ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit'], \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit', 'file.mlt', 'file.mlp', 'file.mlip', 'file.mli.cppo', 'file.ml.cppo'],
\ 'occam': ['file.occ'], \ 'occam': ['file.occ'],
\ 'omnimark': ['file.xom', 'file.xin'], \ 'omnimark': ['file.xom', 'file.xin'],
\ 'opam': ['opam', 'file.opam', 'file.opam.template'],
\ 'openroad': ['file.or'], \ 'openroad': ['file.or'],
\ 'ora': ['file.ora'], \ 'ora': ['file.ora'],
\ 'pamconf': ['/etc/pam.conf'], \ 'pamconf': ['/etc/pam.conf'],
@ -398,6 +400,7 @@ let s:filename_checks = {
\ 'scheme': ['file.scm', 'file.ss', 'file.rkt'], \ 'scheme': ['file.scm', 'file.ss', 'file.rkt'],
\ 'scilab': ['file.sci', 'file.sce'], \ 'scilab': ['file.sci', 'file.sce'],
\ 'screen': ['.screenrc', 'screenrc'], \ 'screen': ['.screenrc', 'screenrc'],
\ 'sexplib': ['file.sexp'],
\ 'scss': ['file.scss'], \ 'scss': ['file.scss'],
\ 'sd': ['file.sd'], \ 'sd': ['file.sd'],
\ 'sdc': ['file.sdc'], \ 'sdc': ['file.sdc'],

View File

@ -1,5 +1,7 @@
" Test :hardcopy " Test :hardcopy
source check.vim
func Test_printoptions() func Test_printoptions()
edit test_hardcopy.vim edit test_hardcopy.vim
syn on syn on
@ -8,8 +10,10 @@ func Test_printoptions()
\ 'left:2in,top:30pt,right:16mm,bottom:3pc', \ 'left:2in,top:30pt,right:16mm,bottom:3pc',
\ 'header:3,syntax:y,number:y,wrap:n', \ 'header:3,syntax:y,number:y,wrap:n',
\ 'header:3,syntax:n,number:y,wrap:y', \ 'header:3,syntax:n,number:y,wrap:y',
\ 'header:0,syntax:a,number:y,wrap:y',
\ 'duplex:short,collate:n,jobsplit:y,portrait:n', \ 'duplex:short,collate:n,jobsplit:y,portrait:n',
\ 'duplex:long,collate:y,jobsplit:n,portrait:y', \ 'duplex:long,collate:y,jobsplit:n,portrait:y',
\ 'duplex:off,collate:y,jobsplit:y,portrait:y',
\ 'paper:10x14', \ 'paper:10x14',
\ 'paper:A3', \ 'paper:A3',
\ 'paper:A4', \ 'paper:A4',
@ -28,7 +32,7 @@ func Test_printoptions()
\ ''] \ '']
exe 'set printoptions=' .. opt exe 'set printoptions=' .. opt
if has('postscript') if has('postscript')
hardcopy > Xhardcopy_printoptions 1,50hardcopy > Xhardcopy_printoptions
let lines = readfile('Xhardcopy_printoptions') let lines = readfile('Xhardcopy_printoptions')
call assert_true(len(lines) > 20, opt) call assert_true(len(lines) > 20, opt)
call assert_true(lines[0] =~ 'PS-Adobe', opt) call assert_true(lines[0] =~ 'PS-Adobe', opt)
@ -44,8 +48,8 @@ func Test_printoptions()
endfunc endfunc
func Test_printmbfont() func Test_printmbfont()
" Print a small help page which contains tabs to cover code that expands tabs to spaces. " Print a help page which contains tabs, underlines (etc) to recover more code.
help help help syntax.txt
syn on syn on
for opt in [':WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-Bold-Italic,c:yes,a:no', for opt in [':WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-Bold-Italic,c:yes,a:no',
@ -63,10 +67,39 @@ func Test_printmbfont()
bwipe bwipe
endfunc endfunc
func Test_printmbcharset()
CheckFeature postscript
" digraph.txt has plenty of non-latin1 characters.
help digraph.txt
set printmbcharset=ISO10646 printencoding=utf-8
for courier in ['yes', 'no']
for ascii in ['yes', 'no']
exe 'set printmbfont=r:WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-BoldItalic'
\ .. ',c:' .. courier .. ',a:' .. ascii
hardcopy > Xhardcopy_printmbcharset
let lines = readfile('Xhardcopy_printmbcharset')
call assert_true(len(lines) > 20)
call assert_true(lines[0] =~ 'PS-Adobe')
endfor
endfor
set printmbcharset=does-not-exist printencoding=utf-8 printmbfont=r:WadaMin-Regular
call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E456:')
set printmbcharset=GB_2312-80 printencoding=utf-8 printmbfont=r:WadaMin-Regular
call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E673:')
set printmbcharset=ISO10646 printencoding=utf-8 printmbfont=
call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E675:')
call delete('Xhardcopy_printmbcharset')
set printmbcharset& printencoding& printmbfont&
bwipe
endfunc
func Test_printexpr() func Test_printexpr()
if !has('unix') CheckFeature postscript
return
endif
" Not a very useful printexpr value, but enough to test " Not a very useful printexpr value, but enough to test
" hardcopy with 'printexpr'. " hardcopy with 'printexpr'.
@ -84,7 +117,7 @@ func Test_printexpr()
\ readfile('Xhardcopy_printexpr')) \ readfile('Xhardcopy_printexpr'))
call delete('Xhardcopy_printexpr') call delete('Xhardcopy_printexpr')
" Function return 1 to test print failure. " Function returns 1 to test print failure.
function PrintFails(fname) function PrintFails(fname)
call delete(a:fname) call delete(a:fname)
return 1 return 1
@ -97,12 +130,11 @@ func Test_printexpr()
endfunc endfunc
func Test_errors() func Test_errors()
" FIXME: Windows fails differently than Unix. CheckFeature postscript
if has('unix')
edit test_hardcopy.vim edit test_hardcopy.vim
call assert_fails('hardcopy >', 'E324:') call assert_fails('hardcopy >', 'E324:')
bwipe bwipe
endif
endfunc endfunc
func Test_dark_background() func Test_dark_background()
@ -126,12 +158,11 @@ func Test_dark_background()
endfun endfun
func Test_empty_buffer() func Test_empty_buffer()
" FIXME: Unclear why this fails on Windows. CheckFeature postscript
if has('unix')
new new
call assert_equal("\nNo text to be printed", execute('hardcopy')) call assert_equal("\nNo text to be printed", execute('hardcopy'))
bwipe bwipe
endif
endfunc endfunc
func Test_printheader_parsing() func Test_printheader_parsing()
@ -145,9 +176,8 @@ func Test_printheader_parsing()
endfunc endfunc
func Test_fname_with_spaces() func Test_fname_with_spaces()
if !has('postscript') CheckFeature postscript
return
endif
split t\ e\ s\ t.txt split t\ e\ s\ t.txt
call setline(1, ['just', 'some', 'text']) call setline(1, ['just', 'some', 'text'])
hardcopy > %.ps hardcopy > %.ps
@ -157,9 +187,11 @@ func Test_fname_with_spaces()
endfunc endfunc
func Test_illegal_byte() func Test_illegal_byte()
if !has('postscript') || &enc != 'utf-8' CheckFeature postscript
if &enc != 'utf-8'
return return
endif endif
new new
" conversion of 0xff will fail, this used to cause a crash " conversion of 0xff will fail, this used to cause a crash
call setline(1, "\xff") call setline(1, "\xff")
@ -168,3 +200,5 @@ func Test_illegal_byte()
bwipe! bwipe!
call delete('Xpstest') call delete('Xpstest')
endfunc endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -29,7 +29,7 @@ func s:setup_commands(cchar)
command! -count -nargs=* -bang Xprev <mods><count>cprev<bang> <args> command! -count -nargs=* -bang Xprev <mods><count>cprev<bang> <args>
command! -nargs=* -bang Xfirst <mods>cfirst<bang> <args> command! -nargs=* -bang Xfirst <mods>cfirst<bang> <args>
command! -nargs=* -bang Xlast <mods>clast<bang> <args> command! -nargs=* -bang Xlast <mods>clast<bang> <args>
command! -nargs=* -bang -range Xnfile <mods><count>cnfile<bang> <args> command! -count -nargs=* -bang Xnfile <mods><count>cnfile<bang> <args>
command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args> command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args>
command! -nargs=* Xexpr <mods>cexpr <args> command! -nargs=* Xexpr <mods>cexpr <args>
command! -range -nargs=* Xvimgrep <mods><count>vimgrep <args> command! -range -nargs=* Xvimgrep <mods><count>vimgrep <args>
@ -40,6 +40,8 @@ func s:setup_commands(cchar)
command! -nargs=0 -count Xcc <count>cc command! -nargs=0 -count Xcc <count>cc
command! -count=1 -nargs=0 Xbelow <mods><count>cbelow command! -count=1 -nargs=0 Xbelow <mods><count>cbelow
command! -count=1 -nargs=0 Xabove <mods><count>cabove command! -count=1 -nargs=0 Xabove <mods><count>cabove
command! -count=1 -nargs=0 Xbefore <mods><count>cbefore
command! -count=1 -nargs=0 Xafter <mods><count>cafter
let g:Xgetlist = function('getqflist') let g:Xgetlist = function('getqflist')
let g:Xsetlist = function('setqflist') let g:Xsetlist = function('setqflist')
call setqflist([], 'f') call setqflist([], 'f')
@ -64,7 +66,7 @@ func s:setup_commands(cchar)
command! -count -nargs=* -bang Xprev <mods><count>lprev<bang> <args> command! -count -nargs=* -bang Xprev <mods><count>lprev<bang> <args>
command! -nargs=* -bang Xfirst <mods>lfirst<bang> <args> command! -nargs=* -bang Xfirst <mods>lfirst<bang> <args>
command! -nargs=* -bang Xlast <mods>llast<bang> <args> command! -nargs=* -bang Xlast <mods>llast<bang> <args>
command! -nargs=* -bang -range Xnfile <mods><count>lnfile<bang> <args> command! -count -nargs=* -bang Xnfile <mods><count>lnfile<bang> <args>
command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args> command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args>
command! -nargs=* Xexpr <mods>lexpr <args> command! -nargs=* Xexpr <mods>lexpr <args>
command! -range -nargs=* Xvimgrep <mods><count>lvimgrep <args> command! -range -nargs=* Xvimgrep <mods><count>lvimgrep <args>
@ -75,6 +77,8 @@ func s:setup_commands(cchar)
command! -nargs=0 -count Xcc <count>ll command! -nargs=0 -count Xcc <count>ll
command! -count=1 -nargs=0 Xbelow <mods><count>lbelow command! -count=1 -nargs=0 Xbelow <mods><count>lbelow
command! -count=1 -nargs=0 Xabove <mods><count>labove command! -count=1 -nargs=0 Xabove <mods><count>labove
command! -count=1 -nargs=0 Xbefore <mods><count>lbefore
command! -count=1 -nargs=0 Xafter <mods><count>lafter
let g:Xgetlist = function('getloclist', [0]) let g:Xgetlist = function('getloclist', [0])
let g:Xsetlist = function('setloclist', [0]) let g:Xsetlist = function('setloclist', [0])
call setloclist(0, [], 'f') call setloclist(0, [], 'f')
@ -260,6 +264,9 @@ func XwindowTests(cchar)
\ winheight('.') == 7 && \ winheight('.') == 7 &&
\ getline('.') ==# '|| non-error 1') \ getline('.') ==# '|| non-error 1')
" :cnext in quickfix window should move to the next entry
Xnext
call assert_equal(2, g:Xgetlist({'idx' : 0}).idx)
" Calling cwindow should close the quickfix window with no valid errors " Calling cwindow should close the quickfix window with no valid errors
Xwindow Xwindow
@ -431,13 +438,19 @@ func Xtest_browse(cchar)
" result in failure " result in failure
if a:cchar == 'c' if a:cchar == 'c'
let err = 'E42:' let err = 'E42:'
let cmd = '$cc'
else else
let err = 'E776:' let err = 'E776:'
let cmd = '$ll'
endif endif
call assert_fails('Xnext', err) call assert_fails('Xnext', err)
call assert_fails('Xprev', err) call assert_fails('Xprev', err)
call assert_fails('Xnfile', err) call assert_fails('Xnfile', err)
call assert_fails('Xpfile', err) call assert_fails('Xpfile', err)
call assert_fails(cmd, err)
Xexpr ''
call assert_fails(cmd, 'E42:')
call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile1')
call s:create_test_file('Xqftestfile2') call s:create_test_file('Xqftestfile2')
@ -1810,14 +1823,27 @@ func s:test_xgrep(cchar)
enew! | only enew! | only
set makeef&vim set makeef&vim
silent Xgrep Grep_Test_Text: test_quickfix.vim silent Xgrep Grep_Test_Text: test_quickfix.vim
call assert_true(len(g:Xgetlist()) == 3) call assert_true(len(g:Xgetlist()) == 5)
Xopen Xopen
call assert_true(w:quickfix_title =~ '^:grep') call assert_true(w:quickfix_title =~ '^:grep')
Xclose Xclose
enew enew
set makeef=Temp_File_## set makeef=Temp_File_##
silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim
call assert_true(len(g:Xgetlist()) == 6)
" Try with 'grepprg' set to 'internal'
set grepprg=internal
silent Xgrep Grep_Test_Text: test_quickfix.vim
silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim
call assert_true(len(g:Xgetlist()) == 9)
set grepprg&vim
call writefile(['Vim'], 'XtestTempFile')
set makeef=XtestTempFile
silent Xgrep Grep_Test_Text: test_quickfix.vim
call assert_equal(5, len(g:Xgetlist()))
call assert_false(filereadable('XtestTempFile'))
set makeef&vim
endfunc endfunc
func Test_grep() func Test_grep()
@ -1914,9 +1940,23 @@ func HistoryTest(cchar)
call assert_equal(' error list 2 of 3; 2 ' . common, res[1]) call assert_equal(' error list 2 of 3; 2 ' . common, res[1])
call assert_equal('> error list 3 of 3; 3 ' . common, res[2]) call assert_equal('> error list 3 of 3; 3 ' . common, res[2])
" Test for changing the quickfix lists
call assert_equal(3, g:Xgetlist({'nr' : 0}).nr)
exe '1' . a:cchar . 'hist'
call assert_equal(1, g:Xgetlist({'nr' : 0}).nr)
exe '3' . a:cchar . 'hist'
call assert_equal(3, g:Xgetlist({'nr' : 0}).nr)
call assert_fails('-2' . a:cchar . 'hist', 'E16:')
call assert_fails('4' . a:cchar . 'hist', 'E16:')
call g:Xsetlist([], 'f') call g:Xsetlist([], 'f')
let l = split(execute(a:cchar . 'hist'), "\n") let l = split(execute(a:cchar . 'hist'), "\n")
call assert_equal('No entries', l[0]) call assert_equal('No entries', l[0])
if a:cchar == 'c'
call assert_fails('4chist', 'E16:')
else
call assert_fails('4lhist', 'E776:')
endif
" An empty list should still show the stack history " An empty list should still show the stack history
call g:Xsetlist([]) call g:Xsetlist([])
@ -2365,6 +2405,12 @@ func Test_Autocmd()
silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim
silent grep abc123def Xtest silent grep abc123def Xtest
silent grepadd abc123def Xtest silent grepadd abc123def Xtest
set grepprg=internal
silent grep Grep_Autocmd_Text test_quickfix.vim
silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim
silent lgrep Grep_Autocmd_Text test_quickfix.vim
silent lgrepadd GrepAdd_Autocmd_Text test_quickfix.vim
set grepprg&vim
let l = ['pregrep', let l = ['pregrep',
\ 'postgrep', \ 'postgrep',
\ 'pregrepadd', \ 'pregrepadd',
@ -2372,7 +2418,15 @@ func Test_Autocmd()
\ 'pregrep', \ 'pregrep',
\ 'postgrep', \ 'postgrep',
\ 'pregrepadd', \ 'pregrepadd',
\ 'postgrepadd'] \ 'postgrepadd',
\ 'pregrep',
\ 'postgrep',
\ 'pregrepadd',
\ 'postgrepadd',
\ 'prelgrep',
\ 'postlgrep',
\ 'prelgrepadd',
\ 'postlgrepadd']
call assert_equal(l, g:acmds) call assert_equal(l, g:acmds)
endif endif
@ -2491,6 +2545,19 @@ func Test_cwindow_jump()
call assert_true(winnr('$') == 2) call assert_true(winnr('$') == 2)
call assert_true(winnr() == 1) call assert_true(winnr() == 1)
" Jumping to a file from the location list window should find a usuable
" window by wrapping around the window list.
enew | only
call setloclist(0, [], 'f')
new | new
lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"]
lopen
1close
call assert_equal(0, getloclist(3, {'id' : 0}).id)
lnext
call assert_equal(3, winnr())
call assert_equal(getloclist(1, {'id' : 0}).id, getloclist(3, {'id' : 0}).id)
enew | only enew | only
set efm&vim set efm&vim
endfunc endfunc
@ -4201,17 +4268,22 @@ func Test_empty_qfbuf()
endfunc endfunc
" Test for the :cbelow, :cabove, :lbelow and :labove commands. " Test for the :cbelow, :cabove, :lbelow and :labove commands.
" And for the :cafter, :cbefore, :lafter and :lbefore commands.
func Xtest_below(cchar) func Xtest_below(cchar)
call s:setup_commands(a:cchar) call s:setup_commands(a:cchar)
" No quickfix/location list " No quickfix/location list
call assert_fails('Xbelow', 'E42:') call assert_fails('Xbelow', 'E42:')
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
" Empty quickfix/location list " Empty quickfix/location list
call g:Xsetlist([]) call g:Xsetlist([])
call assert_fails('Xbelow', 'E42:') call assert_fails('Xbelow', 'E42:')
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
call s:create_test_file('X1') call s:create_test_file('X1')
call s:create_test_file('X2') call s:create_test_file('X2')
@ -4225,39 +4297,74 @@ func Xtest_below(cchar)
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('3Xbelow', 'E42:') call assert_fails('3Xbelow', 'E42:')
call assert_fails('4Xabove', 'E42:') call assert_fails('4Xabove', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
call assert_fails('3Xbefore', 'E42:')
call assert_fails('4Xafter', 'E42:')
" Test the commands with various arguments " Test the commands with various arguments
Xexpr ["X1:5:L5", "X2:5:L5", "X2:10:L10", "X2:15:L15", "X3:3:L3"] Xexpr ["X1:5:3:L5", "X2:5:2:L5", "X2:10:3:L10", "X2:15:4:L15", "X3:3:5:L3"]
edit +7 X2 edit +7 X2
Xabove Xabove
call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_equal(['X2', 5], [bufname(''), line('.')])
call assert_fails('Xabove', 'E553:') call assert_fails('Xabove', 'E553:')
normal 7G
Xbefore
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal 2j normal 2j
Xbelow Xbelow
call assert_equal(['X2', 10], [bufname(''), line('.')]) call assert_equal(['X2', 10], [bufname(''), line('.')])
normal 7G
Xafter
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
" Last error in this file " Last error in this file
Xbelow 99 Xbelow 99
call assert_equal(['X2', 15], [bufname(''), line('.')]) call assert_equal(['X2', 15], [bufname(''), line('.')])
call assert_fails('Xbelow', 'E553:') call assert_fails('Xbelow', 'E553:')
normal gg
Xafter 99
call assert_equal(['X2', 15, 4], [bufname(''), line('.'), col('.')])
call assert_fails('Xafter', 'E553:')
" First error in this file " First error in this file
Xabove 99 Xabove 99
call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_equal(['X2', 5], [bufname(''), line('.')])
call assert_fails('Xabove', 'E553:') call assert_fails('Xabove', 'E553:')
normal G
Xbefore 99
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal gg normal gg
Xbelow 2 Xbelow 2
call assert_equal(['X2', 10], [bufname(''), line('.')]) call assert_equal(['X2', 10], [bufname(''), line('.')])
normal gg
Xafter 2
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
normal G normal G
Xabove 2 Xabove 2
call assert_equal(['X2', 10], [bufname(''), line('.')]) call assert_equal(['X2', 10], [bufname(''), line('.')])
normal G
Xbefore 2
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
edit X4 edit X4
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('Xbelow', 'E42:') call assert_fails('Xbelow', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
if a:cchar == 'l' if a:cchar == 'l'
" If a buffer has location list entries from some other window but not " If a buffer has location list entries from some other window but not
" from the current window, then the commands should fail. " from the current window, then the commands should fail.
edit X1 | split | call setloclist(0, [], 'f') edit X1 | split | call setloclist(0, [], 'f')
call assert_fails('Xabove', 'E776:') call assert_fails('Xabove', 'E776:')
call assert_fails('Xbelow', 'E776:') call assert_fails('Xbelow', 'E776:')
call assert_fails('Xbefore', 'E776:')
call assert_fails('Xafter', 'E776:')
close close
endif endif
@ -4268,31 +4375,52 @@ func Xtest_below(cchar)
edit +1 X2 edit +1 X2
Xbelow 2 Xbelow 2
call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
normal 1G
Xafter 2
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
normal gg normal gg
Xbelow 99 Xbelow 99
call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
normal gg
Xafter 99
call assert_equal(['X2', 15, 3], [bufname(''), line('.'), col('.')])
normal G normal G
Xabove 2 Xabove 2
call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
normal G
Xbefore 2
call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
normal G normal G
Xabove 99 Xabove 99
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
normal G
Xbefore 99
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
normal 10G normal 10G
Xabove Xabove
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
normal 10G$
2Xbefore
call assert_equal(['X2', 10, 2], [bufname(''), line('.'), col('.')])
normal 10G normal 10G
Xbelow Xbelow
call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
normal 9G
5Xafter
call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
" Invalid range " Invalid range
if a:cchar == 'c' if a:cchar == 'c'
call assert_fails('-2cbelow', 'E553:') call assert_fails('-2cbelow', 'E16:')
" TODO: should go to first error in the current line? call assert_fails('-2cafter', 'E16:')
0cabove
else else
call assert_fails('-2lbelow', 'E553:') call assert_fails('-2lbelow', 'E16:')
" TODO: should go to first error in the current line? call assert_fails('-2lafter', 'E16:')
0labove
endif endif
call delete('X1') call delete('X1')
@ -4306,6 +4434,42 @@ func Test_cbelow()
call Xtest_below('l') call Xtest_below('l')
endfunc endfunc
func Test_quickfix_count()
let commands = [
\ 'cNext',
\ 'cNfile',
\ 'cabove',
\ 'cbelow',
\ 'cfirst',
\ 'clast',
\ 'cnewer',
\ 'cnext',
\ 'cnfile',
\ 'colder',
\ 'cprevious',
\ 'crewind',
\
\ 'lNext',
\ 'lNfile',
\ 'labove',
\ 'lbelow',
\ 'lfirst',
\ 'llast',
\ 'lnewer',
\ 'lnext',
\ 'lnfile',
\ 'lolder',
\ 'lprevious',
\ 'lrewind',
\ ]
for cmd in commands
call assert_fails('-1' .. cmd, 'E16:')
call assert_fails('.' .. cmd, 'E16:')
call assert_fails('%' .. cmd, 'E16:')
call assert_fails('$' .. cmd, 'E16:')
endfor
endfunc
" Test for aborting quickfix commands using QuickFixCmdPre " Test for aborting quickfix commands using QuickFixCmdPre
func Xtest_qfcmd_abort(cchar) func Xtest_qfcmd_abort(cchar)
call s:setup_commands(a:cchar) call s:setup_commands(a:cchar)
@ -4461,6 +4625,24 @@ func Test_cquit()
call assert_fails('-3cquit', 'E16:') call assert_fails('-3cquit', 'E16:')
endfunc endfunc
" Running :lhelpgrep command more than once in a help window, doesn't jump to
" the help topic
func Test_lhelpgrep_from_help_window()
call mkdir('Xtestdir/doc', 'p')
call writefile(['window'], 'Xtestdir/doc/a.txt')
call writefile(['buffer'], 'Xtestdir/doc/b.txt')
let save_rtp = &rtp
let &rtp = 'Xtestdir'
lhelpgrep window
lhelpgrep buffer
call assert_equal('b.txt', fnamemodify(@%, ":p:t"))
lhelpgrep window
call assert_equal('a.txt', fnamemodify(@%, ":p:t"))
let &rtp = save_rtp
call delete('Xtestdir', 'rf')
new | only!
endfunc
" Test for adding an invalid entry with the quickfix window open and making " Test for adding an invalid entry with the quickfix window open and making
" sure that the window contents are not changed " sure that the window contents are not changed
func Test_add_invalid_entry_with_qf_window() func Test_add_invalid_entry_with_qf_window()

View File

@ -393,9 +393,13 @@ func Test_addr_all()
call assert_equal(len(gettabinfo()), g:a2) call assert_equal(len(gettabinfo()), g:a2)
bwipe bwipe
command! -addr=other DoSomething echo 'nothing' command! -addr=other DoSomething let g:a1 = <line1> | let g:a2 = <line2>
DoSomething DoSomething
call assert_fails('%DoSomething') call assert_equal(line('.'), g:a1)
call assert_equal(line('.'), g:a2)
%DoSomething
call assert_equal(1, g:a1)
call assert_equal(line('$'), g:a2)
delcommand DoSomething delcommand DoSomething
endfunc endfunc
@ -421,7 +425,7 @@ func Test_command_list()
\ execute('command DoCmd')) \ execute('command DoCmd'))
command! -count=2 DoCmd : command! -count=2 DoCmd :
call assert_equal("\n Name Args Address Complete Definition" call assert_equal("\n Name Args Address Complete Definition"
\ .. "\n DoCmd 0 2c :", \ .. "\n DoCmd 0 2c ? :",
\ execute('command DoCmd')) \ execute('command DoCmd'))
" Test with various -addr= argument values. " Test with various -addr= argument values.

View File

@ -856,7 +856,7 @@ func Test_window_resize()
wincmd l wincmd l
let other_winnr = winnr('h') let other_winnr = winnr('h')
call assert_notequal(winnr(), other_winnr) call assert_notequal(winnr(), other_winnr)
exe 'vert ' .. other_winnr .. 'resize -100' exe 'vert ' .. other_winnr .. 'resize -' .. &columns
call assert_equal(0, winwidth(other_winnr)) call assert_equal(0, winwidth(other_winnr))
%bwipe! %bwipe!

View File

@ -242,9 +242,9 @@ describe('assert function:', function()
-- assert_fails({cmd}, [, {error}]) -- assert_fails({cmd}, [, {error}])
describe('assert_fails', function() describe('assert_fails', function()
it('should change v:errors when error does not match v:errmsg', function() it('should change v:errors when error does not match v:errmsg', function()
eq(1, eval([[assert_fails('xxx', {})]])) eq(1, eval([[assert_fails('xxx', 'E12345')]]))
command([[call assert_match("Expected {} but got 'E731:", v:errors[0])]]) command([[call assert_match("Expected 'E12345' but got 'E492:", v:errors[0])]])
expected_errors({"Expected {} but got 'E731: using Dictionary as a String'"}) expected_errors({"Expected 'E12345' but got 'E492: Not an editor command: xxx': xxx"})
end) end)
it('should not change v:errors when cmd errors', function() it('should not change v:errors when cmd errors', function()
@ -258,9 +258,9 @@ describe('assert function:', function()
end) end)
it('can specify and get a message about what failed', function() it('can specify and get a message about what failed', function()
eq(1, eval([[assert_fails('xxx', {}, 'stupid')]])) eq(1, eval([[assert_fails('xxx', 'E9876', 'stupid')]]))
command([[call assert_match("stupid: Expected {} but got 'E731:", v:errors[0])]]) command([[call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0])]])
expected_errors({"stupid: Expected {} but got 'E731: using Dictionary as a String'"}) expected_errors({"stupid: Expected 'E9876' but got 'E492: Not an editor command: xxx': stupid"})
end) end)
it('can specify and get a message even when cmd succeeds', function() it('can specify and get a message even when cmd succeeds', function()

View File

@ -10,6 +10,29 @@ local source = helpers.source
local poke_eventloop = helpers.poke_eventloop local poke_eventloop = helpers.poke_eventloop
local uname = helpers.uname local uname = helpers.uname
local load_adjust = helpers.load_adjust local load_adjust = helpers.load_adjust
local isCI = helpers.isCI
local function isasan()
local version = eval('execute("version")')
return version:match('-fsanitize=[a-z,]*address')
end
clear()
if isasan() then
pending('ASAN build is difficult to estimate memory usage', function() end)
return
elseif iswin() then
if isCI('github') then
pending('Windows runners in Github Actions do not have a stable environment to estimate memory usage', function() end)
return
elseif eval("executable('wmic')") == 0 then
pending('missing "wmic" command', function() end)
return
end
elseif eval("executable('ps')") == 0 then
pending('missing "ps" command', function() end)
return
end
local monitor_memory_usage = { local monitor_memory_usage = {
memory_usage = function(self) memory_usage = function(self)
@ -71,11 +94,6 @@ describe('memory usage', function()
end end
end end
local function isasan()
local version = eval('execute("version")')
return version:match('-fsanitize=[a-z,]*address')
end
before_each(clear) before_each(clear)
--[[ --[[
@ -83,15 +101,6 @@ describe('memory usage', function()
just after it finishes. just after it finishes.
]]-- ]]--
it('function capture vargs', function() it('function capture vargs', function()
if isasan() then
pending('ASAN build is difficult to estimate memory usage')
end
if iswin() and eval("executable('wmic')") == 0 then
pending('missing "wmic" command')
elseif eval("executable('ps')") == 0 then
pending('missing "ps" command')
end
local pid = eval('getpid()') local pid = eval('getpid()')
local before = monitor_memory_usage(pid) local before = monitor_memory_usage(pid)
source([[ source([[
@ -125,15 +134,6 @@ describe('memory usage', function()
increase so much even when rerun Xtest.vim since system memory caches. increase so much even when rerun Xtest.vim since system memory caches.
]]-- ]]--
it('function capture lvars', function() it('function capture lvars', function()
if isasan() then
pending('ASAN build is difficult to estimate memory usage')
end
if iswin() and eval("executable('wmic')") == 0 then
pending('missing "wmic" command')
elseif eval("executable('ps')") == 0 then
pending('missing "ps" command')
end
local pid = eval('getpid()') local pid = eval('getpid()')
local before = monitor_memory_usage(pid) local before = monitor_memory_usage(pid)
local fname = source([[ local fname = source([[

View File

@ -136,7 +136,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init)
end) end)
it('with nargs/count', function() it('with nargs/count', function()
call(fn, args..', {"nargs": "1", "range": "5"}') call(fn, args..', {"nargs": "1", "count": "5"}')
local function on_setup() local function on_setup()
command('5RpcCommand arg') command('5RpcCommand arg')
end end
@ -152,7 +152,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init)
end) end)
it('with nargs/count/bang', function() it('with nargs/count/bang', function()
call(fn, args..', {"nargs": "1", "range": "5", "bang": ""}') call(fn, args..', {"nargs": "1", "count": "5", "bang": ""}')
local function on_setup() local function on_setup()
command('5RpcCommand! arg') command('5RpcCommand! arg')
end end
@ -169,7 +169,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init)
end) end)
it('with nargs/count/bang/register', function() it('with nargs/count/bang/register', function()
call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. call(fn, args..', {"nargs": "1", "count": "5", "bang": "",'..
' "register": ""}') ' "register": ""}')
local function on_setup() local function on_setup()
command('5RpcCommand! b arg') command('5RpcCommand! b arg')
@ -188,7 +188,7 @@ local function command_specs_for(fn, sync, first_arg_factory, init)
end) end)
it('with nargs/count/bang/register/eval', function() it('with nargs/count/bang/register/eval', function()
call(fn, args..', {"nargs": "1", "range": "5", "bang": "",'.. call(fn, args..', {"nargs": "1", "count": "5", "bang": "",'..
' "register": "", "eval": "@<reg>"}') ' "register": "", "eval": "@<reg>"}')
local function on_setup() local function on_setup()
command('let @b = "regb"') command('let @b = "regb"')