mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #16752 from gpanders/lua-user-commands
feat(api): implement nvim_{add,del}_user_command
This commit is contained in:
commit
7bb593169e
@ -626,6 +626,56 @@ nvim__stats() *nvim__stats()*
|
||||
Return: ~
|
||||
Map of various internal stats.
|
||||
|
||||
*nvim_add_user_command()*
|
||||
nvim_add_user_command({name}, {command}, {*opts})
|
||||
Create a new user command |user-commands|
|
||||
|
||||
{name} is the name of the new command. The name must begin
|
||||
with an uppercase letter.
|
||||
|
||||
{command} is the replacement text or Lua function to execute.
|
||||
|
||||
Example: >
|
||||
:call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
|
||||
:SayHello
|
||||
Hello world!
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
{name} Name of the new user command. Must begin with
|
||||
an uppercase letter.
|
||||
{command} Replacement command to execute when this user
|
||||
command is executed. When called from Lua, the
|
||||
command can also be a Lua function. The
|
||||
function is called with a single table argument
|
||||
that contains the following keys:
|
||||
• args: (string) The args passed to the
|
||||
command, if any |<args>|
|
||||
• bang: (boolean) "true" if the command was
|
||||
executed with a ! modifier |<bang>|
|
||||
• line1: (number) The starting line of the
|
||||
command range |<line1>|
|
||||
• line2: (number) The final line of the command
|
||||
range |<line2>|
|
||||
• range: (number) The number of items in the
|
||||
command range: 0, 1, or 2 |<range>|
|
||||
• count: (number) Any count supplied |<count>|
|
||||
• reg: (string) The optional register, if
|
||||
specified |<reg>|
|
||||
• mods: (string) Command modifiers, if any
|
||||
|<mods>|
|
||||
{opts} Optional command attributes. See
|
||||
|command-attributes| for more details. To use
|
||||
boolean attributes (such as |:command-bang| or
|
||||
|:command-bar|) set the value to "true". When
|
||||
using a Lua function for {command} you can also
|
||||
provide a "desc" key that will be displayed
|
||||
when listing commands. In addition to the
|
||||
string options listed in |:command-complete|,
|
||||
the "complete" key also accepts a Lua function
|
||||
which works like the "customlist" completion
|
||||
mode |:command-complete-customlist|.
|
||||
|
||||
nvim_call_atomic({calls}) *nvim_call_atomic()*
|
||||
Calls many API methods atomically.
|
||||
|
||||
@ -714,6 +764,12 @@ nvim_del_mark({name}) *nvim_del_mark()*
|
||||
|nvim_buf_del_mark()|
|
||||
|nvim_get_mark()|
|
||||
|
||||
nvim_del_user_command({name}) *nvim_del_user_command()*
|
||||
Delete a user-defined command.
|
||||
|
||||
Parameters: ~
|
||||
{name} Name of the command to delete.
|
||||
|
||||
nvim_del_var({name}) *nvim_del_var()*
|
||||
Removes a global (g:) variable.
|
||||
|
||||
@ -1790,6 +1846,16 @@ nvim__buf_redraw_range({buffer}, {first}, {last})
|
||||
nvim__buf_stats({buffer}) *nvim__buf_stats()*
|
||||
TODO: Documentation
|
||||
|
||||
*nvim_buf_add_user_command()*
|
||||
nvim_buf_add_user_command({buffer}, {name}, {command}, {*opts})
|
||||
Create a new user command |user-commands| in the given buffer.
|
||||
|
||||
Parameters: ~
|
||||
{buffer} Buffer handle, or 0 for current buffer.
|
||||
|
||||
See also: ~
|
||||
nvim_add_user_command
|
||||
|
||||
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
|
||||
Activates buffer-update events on a channel, or as Lua
|
||||
callbacks.
|
||||
@ -1925,6 +1991,18 @@ nvim_buf_del_mark({buffer}, {name}) *nvim_buf_del_mark()*
|
||||
|nvim_buf_set_mark()|
|
||||
|nvim_del_mark()|
|
||||
|
||||
*nvim_buf_del_user_command()*
|
||||
nvim_buf_del_user_command({buffer}, {name})
|
||||
Delete a buffer-local user-defined command.
|
||||
|
||||
Only commands created with |:command-buffer| or
|
||||
|nvim_buf_add_user_command()| can be deleted with this
|
||||
function.
|
||||
|
||||
Parameters: ~
|
||||
{buffer} Buffer handle, or 0 for current buffer.
|
||||
{name} Name of the command to delete.
|
||||
|
||||
nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
|
||||
Removes a buffer-scoped (b:) variable
|
||||
|
||||
|
@ -1247,8 +1247,8 @@ See |:verbose-cmd| for more information.
|
||||
|
||||
|
||||
Command attributes ~
|
||||
|
||||
User-defined commands are treated by Vim just like any other Ex commands. They
|
||||
*command-attributes*
|
||||
User-defined commands are treated by Nvim just like any other Ex commands. They
|
||||
can have arguments, or have a range specified. Arguments are subject to
|
||||
completion as filenames, buffers, etc. Exactly how this works depends upon the
|
||||
command's attributes, which are specified when the command is defined.
|
||||
|
@ -1273,6 +1273,63 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Create a new user command |user-commands| in the given buffer.
|
||||
///
|
||||
/// @param buffer Buffer handle, or 0 for current buffer.
|
||||
/// @param[out] err Error details, if any.
|
||||
/// @see nvim_add_user_command
|
||||
void nvim_buf_add_user_command(Buffer buffer, String name, Object command,
|
||||
Dict(user_command) *opts, Error *err)
|
||||
FUNC_API_SINCE(9)
|
||||
{
|
||||
buf_T *target_buf = find_buffer_by_handle(buffer, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
buf_T *save_curbuf = curbuf;
|
||||
curbuf = target_buf;
|
||||
add_user_command(name, command, opts, UC_BUFFER, err);
|
||||
curbuf = save_curbuf;
|
||||
}
|
||||
|
||||
/// Delete a buffer-local user-defined command.
|
||||
///
|
||||
/// Only commands created with |:command-buffer| or
|
||||
/// |nvim_buf_add_user_command()| can be deleted with this function.
|
||||
///
|
||||
/// @param buffer Buffer handle, or 0 for current buffer.
|
||||
/// @param name Name of the command to delete.
|
||||
/// @param[out] err Error details, if any.
|
||||
void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
|
||||
FUNC_API_SINCE(9)
|
||||
{
|
||||
garray_T *gap;
|
||||
if (buffer == -1) {
|
||||
gap = &ucmds;
|
||||
} else {
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
gap = &buf->b_ucmds;
|
||||
}
|
||||
|
||||
for (int i = 0; i < gap->ga_len; i++) {
|
||||
ucmd_T *cmd = USER_CMD_GA(gap, i);
|
||||
if (!STRCMP(name.data, cmd->uc_name)) {
|
||||
free_ucmd(cmd);
|
||||
|
||||
gap->ga_len -= 1;
|
||||
|
||||
if (i < gap->ga_len) {
|
||||
memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
|
||||
}
|
||||
|
||||
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
|
||||
{
|
||||
Dictionary rv = ARRAY_DICT_INIT;
|
||||
|
@ -33,6 +33,18 @@ return {
|
||||
get_commands = {
|
||||
"builtin";
|
||||
};
|
||||
user_command = {
|
||||
"addr";
|
||||
"bang";
|
||||
"bar";
|
||||
"complete";
|
||||
"count";
|
||||
"desc";
|
||||
"force";
|
||||
"nargs";
|
||||
"range";
|
||||
"register";
|
||||
};
|
||||
float_config = {
|
||||
"row";
|
||||
"col";
|
||||
|
@ -961,6 +961,10 @@ Object copy_object(Object obj)
|
||||
|
||||
case kObjectTypeDictionary:
|
||||
return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
|
||||
|
||||
case kObjectTypeLuaRef:
|
||||
return LUAREF_OBJ(api_new_luaref(obj.data.luaref));
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
@ -1342,3 +1346,184 @@ const char *get_default_stl_hl(win_T *wp)
|
||||
return "StatusLineNC";
|
||||
}
|
||||
}
|
||||
|
||||
void add_user_command(String name, Object command, Dict(user_command) *opts, int flags, Error *err)
|
||||
{
|
||||
uint32_t argt = 0;
|
||||
long def = -1;
|
||||
cmd_addr_T addr_type_arg = ADDR_NONE;
|
||||
int compl = EXPAND_NOTHING;
|
||||
char *compl_arg = NULL;
|
||||
char *rep = NULL;
|
||||
LuaRef luaref = LUA_NOREF;
|
||||
LuaRef compl_luaref = LUA_NOREF;
|
||||
|
||||
if (HAS_KEY(opts->range) && HAS_KEY(opts->count)) {
|
||||
api_set_error(err, kErrorTypeValidation, "'range' and 'count' are mutually exclusive");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (opts->nargs.type == kObjectTypeInteger) {
|
||||
switch (opts->nargs.data.integer) {
|
||||
case 0:
|
||||
// Default value, nothing to do
|
||||
break;
|
||||
case 1:
|
||||
argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
|
||||
break;
|
||||
default:
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||
goto err;
|
||||
}
|
||||
} else if (opts->nargs.type == kObjectTypeString) {
|
||||
if (opts->nargs.data.string.size > 1) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (opts->nargs.data.string.data[0]) {
|
||||
case '*':
|
||||
argt |= EX_EXTRA;
|
||||
break;
|
||||
case '?':
|
||||
argt |= EX_EXTRA | EX_NOSPC;
|
||||
break;
|
||||
case '+':
|
||||
argt |= EX_EXTRA | EX_NEEDARG;
|
||||
break;
|
||||
default:
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||
goto err;
|
||||
}
|
||||
} else if (HAS_KEY(opts->nargs)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (HAS_KEY(opts->complete) && !argt) {
|
||||
api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (opts->range.type == kObjectTypeBoolean) {
|
||||
if (opts->range.data.boolean) {
|
||||
argt |= EX_RANGE;
|
||||
addr_type_arg = ADDR_LINES;
|
||||
}
|
||||
} else if (opts->range.type == kObjectTypeString) {
|
||||
if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) {
|
||||
argt |= EX_RANGE | EX_DFLALL;
|
||||
addr_type_arg = ADDR_LINES;
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
|
||||
goto err;
|
||||
}
|
||||
} else if (opts->range.type == kObjectTypeInteger) {
|
||||
argt |= EX_RANGE | EX_ZEROR;
|
||||
def = opts->range.data.integer;
|
||||
addr_type_arg = ADDR_LINES;
|
||||
} else if (HAS_KEY(opts->range)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (opts->count.type == kObjectTypeBoolean) {
|
||||
if (opts->count.data.boolean) {
|
||||
argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
|
||||
addr_type_arg = ADDR_OTHER;
|
||||
def = 0;
|
||||
}
|
||||
} else if (opts->count.type == kObjectTypeInteger) {
|
||||
argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
|
||||
addr_type_arg = ADDR_OTHER;
|
||||
def = opts->count.data.integer;
|
||||
} else if (HAS_KEY(opts->count)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (opts->addr.type == kObjectTypeString) {
|
||||
if (parse_addr_type_arg((char_u *)opts->addr.data.string.data, (int)opts->addr.data.string.size,
|
||||
&addr_type_arg) != OK) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (addr_type_arg != ADDR_LINES) {
|
||||
argt |= EX_ZEROR;
|
||||
}
|
||||
} else if (HAS_KEY(opts->addr)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (api_object_to_bool(opts->bang, "bang", false, err)) {
|
||||
argt |= EX_BANG;
|
||||
} else if (ERROR_SET(err)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (api_object_to_bool(opts->bar, "bar", false, err)) {
|
||||
argt |= EX_TRLBAR;
|
||||
} else if (ERROR_SET(err)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
if (api_object_to_bool(opts->register_, "register", false, err)) {
|
||||
argt |= EX_REGSTR;
|
||||
} else if (ERROR_SET(err)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
bool force = api_object_to_bool(opts->force, "force", false, err);
|
||||
if (ERROR_SET(err)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (opts->complete.type == kObjectTypeLuaRef) {
|
||||
compl = EXPAND_USER_LUA;
|
||||
compl_luaref = api_new_luaref(opts->complete.data.luaref);
|
||||
} else if (opts->complete.type == kObjectTypeString) {
|
||||
if (parse_compl_arg((char_u *)opts->complete.data.string.data,
|
||||
(int)opts->complete.data.string.size, &compl, &argt,
|
||||
(char_u **)&compl_arg) != OK) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
|
||||
goto err;
|
||||
}
|
||||
} else if (HAS_KEY(opts->complete)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (command.type) {
|
||||
case kObjectTypeLuaRef:
|
||||
luaref = api_new_luaref(command.data.luaref);
|
||||
if (opts->desc.type == kObjectTypeString) {
|
||||
rep = opts->desc.data.string.data;
|
||||
} else {
|
||||
snprintf((char *)IObuff, IOSIZE, "<Lua function %d>", luaref);
|
||||
rep = (char *)IObuff;
|
||||
}
|
||||
break;
|
||||
case kObjectTypeString:
|
||||
rep = command.data.string.data;
|
||||
break;
|
||||
default:
|
||||
api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (uc_add_command((char_u *)name.data, name.size, (char_u *)rep, argt, def, flags,
|
||||
compl, (char_u *)compl_arg, compl_luaref, addr_type_arg, luaref,
|
||||
force) != OK) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to create user command");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
NLUA_CLEAR_REF(luaref);
|
||||
NLUA_CLEAR_REF(compl_luaref);
|
||||
}
|
||||
|
@ -2363,3 +2363,52 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Create a new user command |user-commands|
|
||||
///
|
||||
/// {name} is the name of the new command. The name must begin with an uppercase letter.
|
||||
///
|
||||
/// {command} is the replacement text or Lua function to execute.
|
||||
///
|
||||
/// Example:
|
||||
/// <pre>
|
||||
/// :call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
|
||||
/// :SayHello
|
||||
/// Hello world!
|
||||
/// </pre>
|
||||
///
|
||||
/// @param name Name of the new user command. Must begin with an uppercase letter.
|
||||
/// @param command Replacement command to execute when this user command is executed. When called
|
||||
/// from Lua, the command can also be a Lua function. The function is called with a
|
||||
/// single table argument that contains the following keys:
|
||||
/// - args: (string) The args passed to the command, if any |<args>|
|
||||
/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>|
|
||||
/// - line1: (number) The starting line of the command range |<line1>|
|
||||
/// - line2: (number) The final line of the command range |<line2>|
|
||||
/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>|
|
||||
/// - count: (number) Any count supplied |<count>|
|
||||
/// - reg: (string) The optional register, if specified |<reg>|
|
||||
/// - mods: (string) Command modifiers, if any |<mods>|
|
||||
/// @param opts Optional command attributes. See |command-attributes| for more details. To use
|
||||
/// boolean attributes (such as |:command-bang| or |:command-bar|) set the value to
|
||||
/// "true". When using a Lua function for {command} you can also provide a "desc"
|
||||
/// key that will be displayed when listing commands. In addition to the string
|
||||
/// options listed in |:command-complete|, the "complete" key also accepts a Lua
|
||||
/// function which works like the "customlist" completion mode
|
||||
/// |:command-complete-customlist|.
|
||||
/// @param[out] err Error details, if any.
|
||||
void nvim_add_user_command(String name, Object command, Dict(user_command) *opts, Error *err)
|
||||
FUNC_API_SINCE(9)
|
||||
{
|
||||
add_user_command(name, command, opts, 0, err);
|
||||
}
|
||||
|
||||
/// Delete a user-defined command.
|
||||
///
|
||||
/// @param name Name of the command to delete.
|
||||
/// @param[out] err Error details, if any.
|
||||
void nvim_del_user_command(String name, Error *err)
|
||||
FUNC_API_SINCE(9)
|
||||
{
|
||||
nvim_buf_del_user_command(-1, name, err);
|
||||
}
|
||||
|
@ -192,6 +192,7 @@ struct expand {
|
||||
int xp_context; // type of expansion
|
||||
size_t xp_pattern_len; // bytes in xp_pattern before cursor
|
||||
char_u *xp_arg; // completion function
|
||||
LuaRef xp_luaref; // Ref to Lua completion function
|
||||
sctx_T xp_script_ctx; // SCTX for completion function
|
||||
int xp_backslash; // one of the XP_BS_ values
|
||||
#ifndef BACKSLASH_IN_FILENAME
|
||||
|
@ -81,23 +81,7 @@
|
||||
static int quitmore = 0;
|
||||
static bool ex_pressedreturn = false;
|
||||
|
||||
typedef struct ucmd {
|
||||
char_u *uc_name; // The command name
|
||||
uint32_t uc_argt; // The argument type
|
||||
char_u *uc_rep; // The command's replacement string
|
||||
long uc_def; // The default value for a range/count
|
||||
int uc_compl; // completion type
|
||||
cmd_addr_T uc_addr_type; // The command's address type
|
||||
sctx_T uc_script_ctx; // SCTX where the command was defined
|
||||
char_u *uc_compl_arg; // completion argument if any
|
||||
} ucmd_T;
|
||||
|
||||
#define UC_BUFFER 1 // -buffer: local to current buffer
|
||||
|
||||
static garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
|
||||
|
||||
#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
|
||||
#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
|
||||
garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
|
||||
|
||||
// Whether a command index indicates a user command.
|
||||
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
|
||||
@ -2761,6 +2745,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *
|
||||
*complp = uc->uc_compl;
|
||||
}
|
||||
if (xp != NULL) {
|
||||
xp->xp_luaref = uc->uc_compl_luaref;
|
||||
xp->xp_arg = uc->uc_compl_arg;
|
||||
xp->xp_script_ctx = uc->uc_script_ctx;
|
||||
xp->xp_script_ctx.sc_lnum += sourcing_lnum;
|
||||
@ -5171,8 +5156,9 @@ char_u *get_command_name(expand_T *xp, int idx)
|
||||
return cmdnames[idx].cmd_name;
|
||||
}
|
||||
|
||||
static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def,
|
||||
int flags, int compl, char_u *compl_arg, cmd_addr_T addr_type, bool force)
|
||||
int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags,
|
||||
int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type,
|
||||
LuaRef luaref, bool force)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 3)
|
||||
{
|
||||
ucmd_T *cmd = NULL;
|
||||
@ -5226,6 +5212,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a
|
||||
|
||||
XFREE_CLEAR(cmd->uc_rep);
|
||||
XFREE_CLEAR(cmd->uc_compl_arg);
|
||||
NLUA_CLEAR_REF(cmd->uc_luaref);
|
||||
NLUA_CLEAR_REF(cmd->uc_compl_luaref);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -5256,13 +5244,17 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a
|
||||
cmd->uc_script_ctx = current_sctx;
|
||||
cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
|
||||
cmd->uc_compl_arg = compl_arg;
|
||||
cmd->uc_compl_luaref = compl_luaref;
|
||||
cmd->uc_addr_type = addr_type;
|
||||
cmd->uc_luaref = luaref;
|
||||
|
||||
return OK;
|
||||
|
||||
fail:
|
||||
xfree(rep_buf);
|
||||
xfree(compl_arg);
|
||||
NLUA_CLEAR_REF(luaref);
|
||||
NLUA_CLEAR_REF(compl_luaref);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@ -5301,6 +5293,7 @@ static const char *command_complete[] =
|
||||
[EXPAND_CSCOPE] = "cscope",
|
||||
[EXPAND_USER_DEFINED] = "custom",
|
||||
[EXPAND_USER_LIST] = "customlist",
|
||||
[EXPAND_USER_LUA] = "<Lua function>",
|
||||
[EXPAND_DIFF_BUFFERS] = "diff_buffer",
|
||||
[EXPAND_DIRECTORIES] = "dir",
|
||||
[EXPAND_ENV_VARS] = "environment",
|
||||
@ -5702,8 +5695,8 @@ static void ex_command(exarg_T *eap)
|
||||
} else if (compl > 0 && (argt & EX_EXTRA) == 0) {
|
||||
emsg(_(e_complete_used_without_nargs));
|
||||
} else {
|
||||
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
|
||||
addr_type_arg, eap->forceit);
|
||||
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF,
|
||||
addr_type_arg, LUA_NOREF, eap->forceit);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5717,11 +5710,13 @@ void ex_comclear(exarg_T *eap)
|
||||
uc_clear(&curbuf->b_ucmds);
|
||||
}
|
||||
|
||||
static void free_ucmd(ucmd_T *cmd)
|
||||
void free_ucmd(ucmd_T *cmd)
|
||||
{
|
||||
xfree(cmd->uc_name);
|
||||
xfree(cmd->uc_rep);
|
||||
xfree(cmd->uc_compl_arg);
|
||||
NLUA_CLEAR_REF(cmd->uc_compl_luaref);
|
||||
NLUA_CLEAR_REF(cmd->uc_luaref);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5759,9 +5754,7 @@ static void ex_delcommand(exarg_T *eap)
|
||||
return;
|
||||
}
|
||||
|
||||
xfree(cmd->uc_name);
|
||||
xfree(cmd->uc_rep);
|
||||
xfree(cmd->uc_compl_arg);
|
||||
free_ucmd(cmd);
|
||||
|
||||
--gap->ga_len;
|
||||
|
||||
@ -5843,7 +5836,7 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static size_t add_cmd_modifier(char_u *buf, char *mod_str, bool *multi_mods)
|
||||
static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
|
||||
{
|
||||
size_t result = STRLEN(mod_str);
|
||||
if (*multi_mods) {
|
||||
@ -6044,70 +6037,8 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
|
||||
*buf = '\0';
|
||||
}
|
||||
|
||||
bool multi_mods = false;
|
||||
result += uc_mods((char *)buf);
|
||||
|
||||
// :aboveleft and :leftabove
|
||||
if (cmdmod.split & WSP_ABOVE) {
|
||||
result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
|
||||
}
|
||||
// :belowright and :rightbelow
|
||||
if (cmdmod.split & WSP_BELOW) {
|
||||
result += add_cmd_modifier(buf, "belowright", &multi_mods);
|
||||
}
|
||||
// :botright
|
||||
if (cmdmod.split & WSP_BOT) {
|
||||
result += add_cmd_modifier(buf, "botright", &multi_mods);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool *set;
|
||||
char *name;
|
||||
} mod_entry_T;
|
||||
static mod_entry_T mod_entries[] = {
|
||||
{ &cmdmod.browse, "browse" },
|
||||
{ &cmdmod.confirm, "confirm" },
|
||||
{ &cmdmod.hide, "hide" },
|
||||
{ &cmdmod.keepalt, "keepalt" },
|
||||
{ &cmdmod.keepjumps, "keepjumps" },
|
||||
{ &cmdmod.keepmarks, "keepmarks" },
|
||||
{ &cmdmod.keeppatterns, "keeppatterns" },
|
||||
{ &cmdmod.lockmarks, "lockmarks" },
|
||||
{ &cmdmod.noswapfile, "noswapfile" }
|
||||
};
|
||||
// the modifiers that are simple flags
|
||||
for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
|
||||
if (*mod_entries[i].set) {
|
||||
result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(vim): How to support :noautocmd?
|
||||
// TODO(vim): How to support :sandbox?
|
||||
|
||||
// :silent
|
||||
if (msg_silent > 0) {
|
||||
result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent",
|
||||
&multi_mods);
|
||||
}
|
||||
// :tab
|
||||
if (cmdmod.tab > 0) {
|
||||
result += add_cmd_modifier(buf, "tab", &multi_mods);
|
||||
}
|
||||
// :topleft
|
||||
if (cmdmod.split & WSP_TOP) {
|
||||
result += add_cmd_modifier(buf, "topleft", &multi_mods);
|
||||
}
|
||||
|
||||
// TODO(vim): How to support :unsilent?
|
||||
|
||||
// :verbose
|
||||
if (p_verbose > 0) {
|
||||
result += add_cmd_modifier(buf, "verbose", &multi_mods);
|
||||
}
|
||||
// :vertical
|
||||
if (cmdmod.split & WSP_VERT) {
|
||||
result += add_cmd_modifier(buf, "vertical", &multi_mods);
|
||||
}
|
||||
if (quote && buf != NULL) {
|
||||
buf += result - 2;
|
||||
*buf = '"';
|
||||
@ -6152,6 +6083,76 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t uc_mods(char *buf)
|
||||
{
|
||||
size_t result = 0;
|
||||
bool multi_mods = false;
|
||||
|
||||
// :aboveleft and :leftabove
|
||||
if (cmdmod.split & WSP_ABOVE) {
|
||||
result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
|
||||
}
|
||||
// :belowright and :rightbelow
|
||||
if (cmdmod.split & WSP_BELOW) {
|
||||
result += add_cmd_modifier(buf, "belowright", &multi_mods);
|
||||
}
|
||||
// :botright
|
||||
if (cmdmod.split & WSP_BOT) {
|
||||
result += add_cmd_modifier(buf, "botright", &multi_mods);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool *set;
|
||||
char *name;
|
||||
} mod_entry_T;
|
||||
static mod_entry_T mod_entries[] = {
|
||||
{ &cmdmod.browse, "browse" },
|
||||
{ &cmdmod.confirm, "confirm" },
|
||||
{ &cmdmod.hide, "hide" },
|
||||
{ &cmdmod.keepalt, "keepalt" },
|
||||
{ &cmdmod.keepjumps, "keepjumps" },
|
||||
{ &cmdmod.keepmarks, "keepmarks" },
|
||||
{ &cmdmod.keeppatterns, "keeppatterns" },
|
||||
{ &cmdmod.lockmarks, "lockmarks" },
|
||||
{ &cmdmod.noswapfile, "noswapfile" }
|
||||
};
|
||||
// the modifiers that are simple flags
|
||||
for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
|
||||
if (*mod_entries[i].set) {
|
||||
result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(vim): How to support :noautocmd?
|
||||
// TODO(vim): How to support :sandbox?
|
||||
|
||||
// :silent
|
||||
if (msg_silent > 0) {
|
||||
result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
|
||||
}
|
||||
// :tab
|
||||
if (cmdmod.tab > 0) {
|
||||
result += add_cmd_modifier(buf, "tab", &multi_mods);
|
||||
}
|
||||
// :topleft
|
||||
if (cmdmod.split & WSP_TOP) {
|
||||
result += add_cmd_modifier(buf, "topleft", &multi_mods);
|
||||
}
|
||||
|
||||
// TODO(vim): How to support :unsilent?
|
||||
|
||||
// :verbose
|
||||
if (p_verbose > 0) {
|
||||
result += add_cmd_modifier(buf, "verbose", &multi_mods);
|
||||
}
|
||||
// :vertical
|
||||
if (cmdmod.split & WSP_VERT) {
|
||||
result += add_cmd_modifier(buf, "vertical", &multi_mods);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void do_ucmd(exarg_T *eap)
|
||||
{
|
||||
char_u *buf;
|
||||
@ -6174,6 +6175,11 @@ static void do_ucmd(exarg_T *eap)
|
||||
cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
|
||||
}
|
||||
|
||||
if (cmd->uc_luaref > 0) {
|
||||
nlua_do_ucmd(cmd, eap);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace <> in the command by the arguments.
|
||||
* First round: "buf" is NULL, compute length, allocate "buf".
|
||||
|
@ -32,6 +32,26 @@ typedef struct {
|
||||
tasave_T tabuf;
|
||||
} save_state_T;
|
||||
|
||||
typedef struct ucmd {
|
||||
char_u *uc_name; // The command name
|
||||
uint32_t uc_argt; // The argument type
|
||||
char_u *uc_rep; // The command's replacement string
|
||||
long uc_def; // The default value for a range/count
|
||||
int uc_compl; // completion type
|
||||
cmd_addr_T uc_addr_type; // The command's address type
|
||||
sctx_T uc_script_ctx; // SCTX where the command was defined
|
||||
char_u *uc_compl_arg; // completion argument if any
|
||||
LuaRef uc_compl_luaref; // Reference to Lua completion function
|
||||
LuaRef uc_luaref; // Reference to Lua function
|
||||
} ucmd_T;
|
||||
|
||||
#define UC_BUFFER 1 // -buffer: local to current buffer
|
||||
|
||||
extern garray_T ucmds;
|
||||
|
||||
#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
|
||||
#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "ex_docmd.h.generated.h"
|
||||
#endif
|
||||
|
@ -4983,6 +4983,9 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
|
||||
if (xp->xp_context == EXPAND_USER_LIST) {
|
||||
return ExpandUserList(xp, num_file, file);
|
||||
}
|
||||
if (xp->xp_context == EXPAND_USER_LUA) {
|
||||
return ExpandUserLua(xp, num_file, file);
|
||||
}
|
||||
if (xp->xp_context == EXPAND_PACKADD) {
|
||||
return ExpandPackAddDir(pat, num_file, file);
|
||||
}
|
||||
@ -5411,6 +5414,35 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int ExpandUserLua(expand_T *xp, int *num_file, char_u ***file)
|
||||
{
|
||||
typval_T rettv;
|
||||
nlua_call_user_expand_func(xp, &rettv);
|
||||
if (rettv.v_type != VAR_LIST) {
|
||||
tv_clear(&rettv);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
list_T *const retlist = rettv.vval.v_list;
|
||||
|
||||
garray_T ga;
|
||||
ga_init(&ga, (int)sizeof(char *), 3);
|
||||
// Loop over the items in the list.
|
||||
TV_LIST_ITER_CONST(retlist, li, {
|
||||
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
|
||||
|| TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
|
||||
continue; // Skip non-string items and empty strings.
|
||||
}
|
||||
|
||||
GA_APPEND(char *, &ga, xstrdup((const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
|
||||
});
|
||||
tv_list_unref(retlist);
|
||||
|
||||
*file = ga.ga_data;
|
||||
*num_file = ga.ga_len;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Expand color scheme, compiler or filetype names.
|
||||
/// Search from 'runtimepath':
|
||||
/// 'runtimepath'/{dirnames}/{pat}.vim
|
||||
|
@ -441,8 +441,8 @@ local function process_function(fn)
|
||||
local cparam = string.format('arg%u', j)
|
||||
local param_type = real_type(param[1])
|
||||
local lc_param_type = real_type(param[1]):lower()
|
||||
local extra = ((param_type == "Object" or param_type == "Dictionary") and "false, ") or ""
|
||||
if param[1] == "DictionaryOf(LuaRef)" then
|
||||
local extra = param_type == "Dictionary" and "false, " or ""
|
||||
if param[1] == "Object" or param[1] == "DictionaryOf(LuaRef)" then
|
||||
extra = "true, "
|
||||
end
|
||||
local errshift = 0
|
||||
|
@ -26,6 +26,17 @@ local defspipe = io.open(defs_file, 'wb')
|
||||
|
||||
local keysets = require'api.keysets'
|
||||
|
||||
local keywords = {
|
||||
register = true,
|
||||
}
|
||||
|
||||
local function sanitize(key)
|
||||
if keywords[key] then
|
||||
return key .. "_"
|
||||
end
|
||||
return key
|
||||
end
|
||||
|
||||
for name, keys in pairs(keysets) do
|
||||
local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx)
|
||||
return name.."_table["..idx.."].str"
|
||||
@ -33,7 +44,7 @@ for name, keys in pairs(keysets) do
|
||||
|
||||
defspipe:write("typedef struct {\n")
|
||||
for _, key in ipairs(neworder) do
|
||||
defspipe:write(" Object "..key..";\n")
|
||||
defspipe:write(" Object "..sanitize(key)..";\n")
|
||||
end
|
||||
defspipe:write("} KeyDict_"..name..";\n\n")
|
||||
|
||||
@ -41,7 +52,7 @@ for name, keys in pairs(keysets) do
|
||||
|
||||
funcspipe:write("KeySetLink "..name.."_table[] = {\n")
|
||||
for _, key in ipairs(neworder) do
|
||||
funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..key..")},\n")
|
||||
funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..sanitize(key)..")},\n")
|
||||
end
|
||||
funcspipe:write(' {NULL, 0},\n')
|
||||
funcspipe:write("};\n\n")
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/time.h"
|
||||
#include "nvim/ex_cmds2.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/extmark.h"
|
||||
#include "nvim/func_attr.h"
|
||||
@ -914,6 +915,24 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg
|
||||
}
|
||||
}
|
||||
|
||||
void nlua_call_user_expand_func(expand_T *xp, typval_T *ret_tv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
lua_State *const lstate = global_lstate;
|
||||
|
||||
nlua_pushref(lstate, xp->xp_luaref);
|
||||
lua_pushstring(lstate, (char *)xp->xp_pattern);
|
||||
lua_pushstring(lstate, (char *)xp->xp_line);
|
||||
lua_pushinteger(lstate, xp->xp_col);
|
||||
|
||||
if (nlua_pcall(lstate, 3, 1)) {
|
||||
nlua_error(lstate, _("E5108: Error executing Lua function: %.*s"));
|
||||
return;
|
||||
}
|
||||
|
||||
nlua_pop_typval(lstate, ret_tv);
|
||||
}
|
||||
|
||||
static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name,
|
||||
typval_T *const args, int argcount, bool special, typval_T *ret_tv)
|
||||
{
|
||||
@ -1432,3 +1451,48 @@ void nlua_execute_on_key(int c)
|
||||
#endif
|
||||
}
|
||||
|
||||
void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
|
||||
{
|
||||
lua_State *const lstate = global_lstate;
|
||||
|
||||
nlua_pushref(lstate, cmd->uc_luaref);
|
||||
|
||||
lua_newtable(lstate);
|
||||
lua_pushboolean(lstate, eap->forceit == 1);
|
||||
lua_setfield(lstate, -2, "bang");
|
||||
|
||||
lua_pushinteger(lstate, eap->line1);
|
||||
lua_setfield(lstate, -2, "line1");
|
||||
|
||||
lua_pushinteger(lstate, eap->line2);
|
||||
lua_setfield(lstate, -2, "line2");
|
||||
|
||||
lua_pushstring(lstate, (const char *)eap->arg);
|
||||
lua_setfield(lstate, -2, "args");
|
||||
|
||||
lua_pushstring(lstate, (const char *)&eap->regname);
|
||||
lua_setfield(lstate, -2, "reg");
|
||||
|
||||
lua_pushinteger(lstate, eap->addr_count);
|
||||
lua_setfield(lstate, -2, "range");
|
||||
|
||||
if (eap->addr_count > 0) {
|
||||
lua_pushinteger(lstate, eap->line2);
|
||||
} else {
|
||||
lua_pushinteger(lstate, cmd->uc_def);
|
||||
}
|
||||
lua_setfield(lstate, -2, "count");
|
||||
|
||||
// The size of this buffer is chosen empirically to be large enough to hold
|
||||
// every possible modifier (with room to spare). If the list of possible
|
||||
// modifiers grows this may need to be updated.
|
||||
char buf[200] = { 0 };
|
||||
(void)uc_mods(buf);
|
||||
lua_pushstring(lstate, buf);
|
||||
lua_setfield(lstate, -2, "mods");
|
||||
|
||||
if (nlua_pcall(lstate, 1, 0)) {
|
||||
nlua_error(lstate, _("Error executing Lua callback: %.*s"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/lua/converter.h"
|
||||
|
||||
|
@ -143,6 +143,7 @@ enum {
|
||||
EXPAND_COMPILER,
|
||||
EXPAND_USER_DEFINED,
|
||||
EXPAND_USER_LIST,
|
||||
EXPAND_USER_LUA,
|
||||
EXPAND_SHELLCMD,
|
||||
EXPAND_CSCOPE,
|
||||
EXPAND_SIGN,
|
||||
|
@ -6,8 +6,14 @@ local command = helpers.command
|
||||
local curbufmeths = helpers.curbufmeths
|
||||
local eq = helpers.eq
|
||||
local meths = helpers.meths
|
||||
local bufmeths = helpers.bufmeths
|
||||
local matches = helpers.matches
|
||||
local source = helpers.source
|
||||
local pcall_err = helpers.pcall_err
|
||||
local exec_lua = helpers.exec_lua
|
||||
local assert_alive = helpers.assert_alive
|
||||
local feed = helpers.feed
|
||||
local funcs = helpers.funcs
|
||||
|
||||
describe('nvim_get_commands', function()
|
||||
local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, }
|
||||
@ -78,3 +84,118 @@ describe('nvim_get_commands', function()
|
||||
eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('nvim_add_user_command', function()
|
||||
before_each(clear)
|
||||
|
||||
it('works with strings', function()
|
||||
meths.add_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1})
|
||||
meths.command('SomeCommand 42')
|
||||
eq(42, meths.eval('g:command_fired'))
|
||||
end)
|
||||
|
||||
it('works with Lua functions', function()
|
||||
exec_lua [[
|
||||
result = {}
|
||||
vim.api.nvim_add_user_command('CommandWithLuaCallback', function(opts)
|
||||
result = opts
|
||||
end, {
|
||||
nargs = "*",
|
||||
bang = true,
|
||||
count = 2,
|
||||
})
|
||||
]]
|
||||
|
||||
eq({
|
||||
args = "hello",
|
||||
bang = false,
|
||||
line1 = 1,
|
||||
line2 = 1,
|
||||
mods = "",
|
||||
range = 0,
|
||||
count = 2,
|
||||
reg = "",
|
||||
}, exec_lua [[
|
||||
vim.api.nvim_command('CommandWithLuaCallback hello')
|
||||
return result
|
||||
]])
|
||||
|
||||
eq({
|
||||
args = "",
|
||||
bang = true,
|
||||
line1 = 10,
|
||||
line2 = 10,
|
||||
mods = "botright",
|
||||
range = 1,
|
||||
count = 10,
|
||||
reg = "",
|
||||
}, exec_lua [[
|
||||
vim.api.nvim_command('botright 10CommandWithLuaCallback!')
|
||||
return result
|
||||
]])
|
||||
|
||||
eq({
|
||||
args = "",
|
||||
bang = false,
|
||||
line1 = 1,
|
||||
line2 = 42,
|
||||
mods = "",
|
||||
range = 1,
|
||||
count = 42,
|
||||
reg = "",
|
||||
}, exec_lua [[
|
||||
vim.api.nvim_command('CommandWithLuaCallback 42')
|
||||
return result
|
||||
]])
|
||||
end)
|
||||
|
||||
it('can define buffer-local commands', function()
|
||||
local bufnr = meths.create_buf(false, false)
|
||||
bufmeths.add_user_command(bufnr, "Hello", "", {})
|
||||
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
|
||||
meths.set_current_buf(bufnr)
|
||||
meths.command("Hello")
|
||||
assert_alive()
|
||||
end)
|
||||
|
||||
it('can use a Lua complete function', function()
|
||||
exec_lua [[
|
||||
vim.api.nvim_add_user_command('Test', '', {
|
||||
nargs = "*",
|
||||
complete = function(arg, cmdline, pos)
|
||||
local options = {"aaa", "bbb", "ccc"}
|
||||
local t = {}
|
||||
for _, v in ipairs(options) do
|
||||
if string.find(v, "^" .. arg) then
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
return t
|
||||
end,
|
||||
})
|
||||
]]
|
||||
|
||||
feed(':Test a<Tab>')
|
||||
eq('Test aaa', funcs.getcmdline())
|
||||
feed('<C-U>Test b<Tab>')
|
||||
eq('Test bbb', funcs.getcmdline())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('nvim_del_user_command', function()
|
||||
before_each(clear)
|
||||
|
||||
it('can delete global commands', function()
|
||||
meths.add_user_command('Hello', 'echo "Hi"', {})
|
||||
meths.command('Hello')
|
||||
meths.del_user_command('Hello')
|
||||
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
|
||||
end)
|
||||
|
||||
it('can delete buffer-local commands', function()
|
||||
bufmeths.add_user_command(0, 'Hello', 'echo "Hi"', {})
|
||||
meths.command('Hello')
|
||||
bufmeths.del_user_command(0, 'Hello')
|
||||
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
|
||||
end)
|
||||
end)
|
||||
|
@ -8,6 +8,7 @@ local clear = helpers.clear
|
||||
local eval = helpers.eval
|
||||
local NIL = helpers.NIL
|
||||
local eq = helpers.eq
|
||||
local exec_lua = helpers.exec_lua
|
||||
|
||||
before_each(clear)
|
||||
|
||||
@ -111,6 +112,12 @@ describe('luaeval(vim.api.…)', function()
|
||||
eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]]))
|
||||
|
||||
eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})'))
|
||||
|
||||
eq(true, funcs.luaeval('vim.api.nvim__id(vim.api.nvim__id)(true)'))
|
||||
eq(42, exec_lua [[
|
||||
local f = vim.api.nvim__id({42, vim.api.nvim__id})
|
||||
return f[2](f[1])
|
||||
]])
|
||||
end)
|
||||
|
||||
it('correctly converts container objects with type_idx to API objects', function()
|
||||
@ -159,12 +166,8 @@ describe('luaeval(vim.api.…)', function()
|
||||
|
||||
it('errors out correctly when working with API', function()
|
||||
-- Conversion errors
|
||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
|
||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id(vim.api.nvim__id)")]])))
|
||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table',
|
||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])))
|
||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
|
||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({42, vim.api.nvim__id})")]])))
|
||||
-- Errors in number of arguments
|
||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
|
||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id()")]])))
|
||||
|
Loading…
Reference in New Issue
Block a user