mirror of
https://github.com/neovim/neovim.git
synced 2025-09-18 09:18:19 +00:00
Merge pull request #16752 from gpanders/lua-user-commands
feat(api): implement nvim_{add,del}_user_command
This commit is contained in:
@@ -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".
|
||||
|
Reference in New Issue
Block a user