vim-patch:8.1.1241: Ex command info contains confusing information

Problem:    Ex command info contains confusing information.
Solution:   When using the NOTADR flag use ADDR_OTHER for the address type.
            Cleanup code using NOTADR.  Check for errors in
            create_cmdidxs.vim.  Adjust Makefile to see the errors.
b731689e85

Use Lua's "assert()" to make an invalid command definition
a compilation error.

Misc changes:

Remove 'RESTRICT' flag.
Neovim does not support "restricted" mode
since commit 7777532ceb.

TODO:
Do not generate files before Lua assertions
so that CMake always runs the generator script
if the previous build has an invalid command definition.
This commit is contained in:
Jan Edmund Lazo
2020-12-31 19:56:15 -05:00
parent cf3a861017
commit f3c242c13c
7 changed files with 732 additions and 689 deletions

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

@@ -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,11 +1670,18 @@ 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);
} }
}
/* /*
* write current buffer to file 'eap->arg' * write current buffer to file 'eap->arg'

File diff suppressed because it is too large Load Diff

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
@@ -153,7 +152,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;
} }
} }
@@ -1490,9 +1493,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 +1548,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 +1570,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 +1695,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:
@@ -1771,7 +1773,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 +1782,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 +2326,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;
@@ -2365,6 +2367,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 +2403,6 @@ 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:
*errormsg = (char_u *)_(e_invrange); *errormsg = (char_u *)_(e_invrange);
return FAIL; return FAIL;
case ADDR_ARGUMENTS: case ADDR_ARGUMENTS:
@@ -3723,7 +3725,7 @@ static linenr_T get_address(exarg_T *eap,
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; const cmd_addr_T addr_type = addr_type_arg;
int c; int c;
int i; int i;
long n; long n;
@@ -3741,6 +3743,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:
@@ -3772,6 +3775,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:
@@ -3939,7 +3943,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;
@@ -4054,9 +4060,8 @@ 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,7 +4110,8 @@ 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);
@@ -5376,7 +5382,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 +5390,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 +5430,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 +5461,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 +6119,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 +6142,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 +7433,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 +7908,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

@@ -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.