Merge pull request #16752 from gpanders/lua-user-commands

feat(api): implement nvim_{add,del}_user_command
This commit is contained in:
Björn Linse
2021-12-28 23:18:07 +01:00
committed by GitHub
17 changed files with 740 additions and 99 deletions

View File

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