diff options
Diffstat (limited to 'data/vim/patches/8.1.1210')
-rw-r--r-- | data/vim/patches/8.1.1210 | 4639 |
1 files changed, 4639 insertions, 0 deletions
diff --git a/data/vim/patches/8.1.1210 b/data/vim/patches/8.1.1210 new file mode 100644 index 000000000..4405c63ae --- /dev/null +++ b/data/vim/patches/8.1.1210 @@ -0,0 +1,4639 @@ +To: vim_dev@googlegroups.com +Subject: Patch 8.1.1210 +Fcc: outbox +From: Bram Moolenaar <Bram@moolenaar.net> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +------------ + +Patch 8.1.1210 +Problem: Support for user commands is spread out. No good reason to make + user commands optional. +Solution: Move user command support to usercmd.c. Always enable the + user_commands feature. +Files: src/usercmd.c, src/proto/usercmd.pro, Filelist, src/Make_bc5.mak, + src/Make_cyg_ming.mak, src/Make_dice.mak, src/Make_ivc.mak, + src/Make_manx.mak, src/Make_morph.mak, src/Make_mvc.mak, + src/Make_sas.mak, src/Make_vms.mms, src/Makefile, src/README.md, + src/buffer.c, src/eval.c, src/evalfunc.c, src/ex_cmds.h, + src/ex_docmd.c, src/proto/ex_docmd.pro, src/ex_getln.c, + src/feature.h, src/macros.h, src/misc2.c, src/proto.h, + src/structs.h, src/version.c, runtime/doc/eval.txt, + runtime/doc/various.txt + + +*** ../vim-8.1.1209/src/usercmd.c 2019-04-27 12:58:18.994395422 +0200 +--- src/usercmd.c 2019-04-27 12:54:00.423836989 +0200 +*************** +*** 0 **** +--- 1,1656 ---- ++ /* vi:set ts=8 sts=4 sw=4 noet: ++ * ++ * VIM - Vi IMproved by Bram Moolenaar ++ * ++ * Do ":help uganda" in Vim to read copying and usage conditions. ++ * Do ":help credits" in Vim to see a list of people who contributed. ++ * See README.txt for an overview of the Vim source code. ++ */ ++ ++ /* ++ * usercmd.c: User defined command support ++ */ ++ ++ #include "vim.h" ++ ++ typedef struct ucmd ++ { ++ char_u *uc_name; // The command name ++ long_u 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 ++ int uc_addr_type; // The command's address type ++ # ifdef FEAT_EVAL ++ sctx_T uc_script_ctx; // SCTX where the command was defined ++ # ifdef FEAT_CMDL_COMPL ++ char_u *uc_compl_arg; // completion argument if any ++ # endif ++ # endif ++ } ucmd_T; ++ ++ // List of all user commands. ++ 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]) ++ ++ /* ++ * List of names for completion for ":command" with the EXPAND_ flag. ++ * Must be alphabetical for completion. ++ */ ++ static struct ++ { ++ int expand; ++ char *name; ++ } command_complete[] = ++ { ++ {EXPAND_ARGLIST, "arglist"}, ++ {EXPAND_AUGROUP, "augroup"}, ++ {EXPAND_BEHAVE, "behave"}, ++ {EXPAND_BUFFERS, "buffer"}, ++ {EXPAND_COLORS, "color"}, ++ {EXPAND_COMMANDS, "command"}, ++ {EXPAND_COMPILER, "compiler"}, ++ #if defined(FEAT_CSCOPE) ++ {EXPAND_CSCOPE, "cscope"}, ++ #endif ++ #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ {EXPAND_USER_DEFINED, "custom"}, ++ {EXPAND_USER_LIST, "customlist"}, ++ #endif ++ {EXPAND_DIRECTORIES, "dir"}, ++ {EXPAND_ENV_VARS, "environment"}, ++ {EXPAND_EVENTS, "event"}, ++ {EXPAND_EXPRESSION, "expression"}, ++ {EXPAND_FILES, "file"}, ++ {EXPAND_FILES_IN_PATH, "file_in_path"}, ++ {EXPAND_FILETYPE, "filetype"}, ++ {EXPAND_FUNCTIONS, "function"}, ++ {EXPAND_HELP, "help"}, ++ {EXPAND_HIGHLIGHT, "highlight"}, ++ #if defined(FEAT_CMDHIST) ++ {EXPAND_HISTORY, "history"}, ++ #endif ++ #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) ++ {EXPAND_LOCALES, "locale"}, ++ #endif ++ {EXPAND_MAPCLEAR, "mapclear"}, ++ {EXPAND_MAPPINGS, "mapping"}, ++ {EXPAND_MENUS, "menu"}, ++ {EXPAND_MESSAGES, "messages"}, ++ {EXPAND_OWNSYNTAX, "syntax"}, ++ #if defined(FEAT_PROFILE) ++ {EXPAND_SYNTIME, "syntime"}, ++ #endif ++ {EXPAND_SETTINGS, "option"}, ++ {EXPAND_PACKADD, "packadd"}, ++ {EXPAND_SHELLCMD, "shellcmd"}, ++ #if defined(FEAT_SIGNS) ++ {EXPAND_SIGN, "sign"}, ++ #endif ++ {EXPAND_TAGS, "tag"}, ++ {EXPAND_TAGS_LISTFILES, "tag_listfiles"}, ++ {EXPAND_USER, "user"}, ++ {EXPAND_USER_VARS, "var"}, ++ {0, NULL} ++ }; ++ ++ /* ++ * List of names of address types. Must be alphabetical for completion. ++ */ ++ static struct ++ { ++ int expand; ++ char *name; ++ char *shortname; ++ } addr_type_complete[] = ++ { ++ {ADDR_ARGUMENTS, "arguments", "arg"}, ++ {ADDR_LINES, "lines", "line"}, ++ {ADDR_LOADED_BUFFERS, "loaded_buffers", "load"}, ++ {ADDR_TABS, "tabs", "tab"}, ++ {ADDR_BUFFERS, "buffers", "buf"}, ++ {ADDR_WINDOWS, "windows", "win"}, ++ {ADDR_QUICKFIX, "quickfix", "qf"}, ++ {ADDR_OTHER, "other", "?"}, ++ {-1, NULL, NULL} ++ }; ++ ++ #define UC_BUFFER 1 // -buffer: local to current buffer ++ ++ /* ++ * Search for a user command that matches "eap->cmd". ++ * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". ++ * Return a pointer to just after the command. ++ * Return NULL if there is no matching command. ++ */ ++ char_u * ++ find_ucmd( ++ exarg_T *eap, ++ char_u *p, // end of the command (possibly including count) ++ int *full, // set to TRUE for a full match ++ expand_T *xp, // used for completion, NULL otherwise ++ int *compl UNUSED) // completion flags or NULL ++ { ++ int len = (int)(p - eap->cmd); ++ int j, k, matchlen = 0; ++ ucmd_T *uc; ++ int found = FALSE; ++ int possible = FALSE; ++ char_u *cp, *np; // Point into typed cmd and test name ++ garray_T *gap; ++ int amb_local = FALSE; // Found ambiguous buffer-local command, ++ // only full match global is accepted. ++ ++ /* ++ * Look for buffer-local user commands first, then global ones. ++ */ ++ gap = &curbuf->b_ucmds; ++ for (;;) ++ { ++ for (j = 0; j < gap->ga_len; ++j) ++ { ++ uc = USER_CMD_GA(gap, j); ++ cp = eap->cmd; ++ np = uc->uc_name; ++ k = 0; ++ while (k < len && *np != NUL && *cp++ == *np++) ++ k++; ++ if (k == len || (*np == NUL && vim_isdigit(eap->cmd[k]))) ++ { ++ // If finding a second match, the command is ambiguous. But ++ // not if a buffer-local command wasn't a full match and a ++ // global command is a full match. ++ if (k == len && found && *np != NUL) ++ { ++ if (gap == &ucmds) ++ return NULL; ++ amb_local = TRUE; ++ } ++ ++ if (!found || (k == len && *np == NUL)) ++ { ++ // If we matched up to a digit, then there could ++ // be another command including the digit that we ++ // should use instead. ++ if (k == len) ++ found = TRUE; ++ else ++ possible = TRUE; ++ ++ if (gap == &ucmds) ++ eap->cmdidx = CMD_USER; ++ else ++ eap->cmdidx = CMD_USER_BUF; ++ eap->argt = (long)uc->uc_argt; ++ eap->useridx = j; ++ eap->addr_type = uc->uc_addr_type; ++ ++ # ifdef FEAT_CMDL_COMPL ++ if (compl != NULL) ++ *compl = uc->uc_compl; ++ # ifdef FEAT_EVAL ++ if (xp != NULL) ++ { ++ xp->xp_arg = uc->uc_compl_arg; ++ xp->xp_script_ctx = uc->uc_script_ctx; ++ xp->xp_script_ctx.sc_lnum += sourcing_lnum; ++ } ++ # endif ++ # endif ++ // Do not search for further abbreviations ++ // if this is an exact match. ++ matchlen = k; ++ if (k == len && *np == NUL) ++ { ++ if (full != NULL) ++ *full = TRUE; ++ amb_local = FALSE; ++ break; ++ } ++ } ++ } ++ } ++ ++ // Stop if we found a full match or searched all. ++ if (j < gap->ga_len || gap == &ucmds) ++ break; ++ gap = &ucmds; ++ } ++ ++ // Only found ambiguous matches. ++ if (amb_local) ++ { ++ if (xp != NULL) ++ xp->xp_context = EXPAND_UNSUCCESSFUL; ++ return NULL; ++ } ++ ++ // The match we found may be followed immediately by a number. Move "p" ++ // back to point to it. ++ if (found || possible) ++ return p + (matchlen - len); ++ return p; ++ } ++ ++ #if defined(FEAT_CMDL_COMPL) || defined(PROTO) ++ ++ char_u * ++ set_context_in_user_cmd(expand_T *xp, char_u *arg_in) ++ { ++ char_u *arg = arg_in; ++ char_u *p; ++ ++ // Check for attributes ++ while (*arg == '-') ++ { ++ arg++; // Skip "-" ++ p = skiptowhite(arg); ++ if (*p == NUL) ++ { ++ // Cursor is still in the attribute ++ p = vim_strchr(arg, '='); ++ if (p == NULL) ++ { ++ // No "=", so complete attribute names ++ xp->xp_context = EXPAND_USER_CMD_FLAGS; ++ xp->xp_pattern = arg; ++ return NULL; ++ } ++ ++ // For the -complete, -nargs and -addr attributes, we complete ++ // their arguments as well. ++ if (STRNICMP(arg, "complete", p - arg) == 0) ++ { ++ xp->xp_context = EXPAND_USER_COMPLETE; ++ xp->xp_pattern = p + 1; ++ return NULL; ++ } ++ else if (STRNICMP(arg, "nargs", p - arg) == 0) ++ { ++ xp->xp_context = EXPAND_USER_NARGS; ++ xp->xp_pattern = p + 1; ++ return NULL; ++ } ++ else if (STRNICMP(arg, "addr", p - arg) == 0) ++ { ++ xp->xp_context = EXPAND_USER_ADDR_TYPE; ++ xp->xp_pattern = p + 1; ++ return NULL; ++ } ++ return NULL; ++ } ++ arg = skipwhite(p); ++ } ++ ++ // After the attributes comes the new command name ++ p = skiptowhite(arg); ++ if (*p == NUL) ++ { ++ xp->xp_context = EXPAND_USER_COMMANDS; ++ xp->xp_pattern = arg; ++ return NULL; ++ } ++ ++ // And finally comes a normal command ++ return skipwhite(p); ++ } ++ ++ char_u * ++ get_user_command_name(int idx) ++ { ++ return get_user_commands(NULL, idx - (int)CMD_SIZE); ++ } ++ ++ /* ++ * Function given to ExpandGeneric() to obtain the list of user command names. ++ */ ++ char_u * ++ get_user_commands(expand_T *xp UNUSED, int idx) ++ { ++ if (idx < curbuf->b_ucmds.ga_len) ++ return USER_CMD_GA(&curbuf->b_ucmds, idx)->uc_name; ++ idx -= curbuf->b_ucmds.ga_len; ++ if (idx < ucmds.ga_len) ++ return USER_CMD(idx)->uc_name; ++ return NULL; ++ } ++ ++ /* ++ * Function given to ExpandGeneric() to obtain the list of user address type ++ * names. ++ */ ++ char_u * ++ get_user_cmd_addr_type(expand_T *xp UNUSED, int idx) ++ { ++ return (char_u *)addr_type_complete[idx].name; ++ } ++ ++ /* ++ * Function given to ExpandGeneric() to obtain the list of user command ++ * attributes. ++ */ ++ char_u * ++ get_user_cmd_flags(expand_T *xp UNUSED, int idx) ++ { ++ static char *user_cmd_flags[] = { ++ "addr", "bang", "bar", "buffer", "complete", ++ "count", "nargs", "range", "register" ++ }; ++ ++ if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0]))) ++ return NULL; ++ return (char_u *)user_cmd_flags[idx]; ++ } ++ ++ /* ++ * Function given to ExpandGeneric() to obtain the list of values for -nargs. ++ */ ++ char_u * ++ get_user_cmd_nargs(expand_T *xp UNUSED, int idx) ++ { ++ static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"}; ++ ++ if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0]))) ++ return NULL; ++ return (char_u *)user_cmd_nargs[idx]; ++ } ++ ++ /* ++ * Function given to ExpandGeneric() to obtain the list of values for ++ * -complete. ++ */ ++ char_u * ++ get_user_cmd_complete(expand_T *xp UNUSED, int idx) ++ { ++ return (char_u *)command_complete[idx].name; ++ } ++ ++ int ++ cmdcomplete_str_to_type(char_u *complete_str) ++ { ++ int i; ++ ++ for (i = 0; command_complete[i].expand != 0; ++i) ++ if (STRCMP(complete_str, command_complete[i].name) == 0) ++ return command_complete[i].expand; ++ ++ return EXPAND_NOTHING; ++ } ++ ++ #endif // FEAT_CMDL_COMPL ++ ++ /* ++ * List user commands starting with "name[name_len]". ++ */ ++ static void ++ uc_list(char_u *name, size_t name_len) ++ { ++ int i, j; ++ int found = FALSE; ++ ucmd_T *cmd; ++ int len; ++ int over; ++ long a; ++ garray_T *gap; ++ ++ gap = &curbuf->b_ucmds; ++ for (;;) ++ { ++ for (i = 0; i < gap->ga_len; ++i) ++ { ++ cmd = USER_CMD_GA(gap, i); ++ a = (long)cmd->uc_argt; ++ ++ // Skip commands which don't match the requested prefix and ++ // commands filtered out. ++ if (STRNCMP(name, cmd->uc_name, name_len) != 0 ++ || message_filtered(cmd->uc_name)) ++ continue; ++ ++ // Put out the title first time ++ if (!found) ++ msg_puts_title(_("\n Name Args Address Complete Definition")); ++ found = TRUE; ++ msg_putchar('\n'); ++ if (got_int) ++ break; ++ ++ // Special cases ++ len = 4; ++ if (a & BANG) ++ { ++ msg_putchar('!'); ++ --len; ++ } ++ if (a & REGSTR) ++ { ++ msg_putchar('"'); ++ --len; ++ } ++ if (gap != &ucmds) ++ { ++ msg_putchar('b'); ++ --len; ++ } ++ if (a & TRLBAR) ++ { ++ msg_putchar('|'); ++ --len; ++ } ++ while (len-- > 0) ++ msg_putchar(' '); ++ ++ msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D)); ++ len = (int)STRLEN(cmd->uc_name) + 4; ++ ++ do { ++ msg_putchar(' '); ++ ++len; ++ } while (len < 22); ++ ++ // "over" is how much longer the name is than the column width for ++ // the name, we'll try to align what comes after. ++ over = len - 22; ++ len = 0; ++ ++ // Arguments ++ switch ((int)(a & (EXTRA|NOSPC|NEEDARG))) ++ { ++ case 0: IObuff[len++] = '0'; break; ++ case (EXTRA): IObuff[len++] = '*'; break; ++ case (EXTRA|NOSPC): IObuff[len++] = '?'; break; ++ case (EXTRA|NEEDARG): IObuff[len++] = '+'; break; ++ case (EXTRA|NOSPC|NEEDARG): IObuff[len++] = '1'; break; ++ } ++ ++ do { ++ IObuff[len++] = ' '; ++ } while (len < 5 - over); ++ ++ // Address / Range ++ if (a & (RANGE|COUNT)) ++ { ++ if (a & COUNT) ++ { ++ // -count=N ++ sprintf((char *)IObuff + len, "%ldc", cmd->uc_def); ++ len += (int)STRLEN(IObuff + len); ++ } ++ else if (a & DFLALL) ++ IObuff[len++] = '%'; ++ else if (cmd->uc_def >= 0) ++ { ++ // -range=N ++ sprintf((char *)IObuff + len, "%ld", cmd->uc_def); ++ len += (int)STRLEN(IObuff + len); ++ } ++ else ++ IObuff[len++] = '.'; ++ } ++ ++ do { ++ IObuff[len++] = ' '; ++ } while (len < 8 - over); ++ ++ // Address Type ++ for (j = 0; addr_type_complete[j].expand != -1; ++j) ++ if (addr_type_complete[j].expand != ADDR_LINES ++ && addr_type_complete[j].expand == cmd->uc_addr_type) ++ { ++ STRCPY(IObuff + len, addr_type_complete[j].shortname); ++ len += (int)STRLEN(IObuff + len); ++ break; ++ } ++ ++ do { ++ IObuff[len++] = ' '; ++ } while (len < 13 - over); ++ ++ // Completion ++ for (j = 0; command_complete[j].expand != 0; ++j) ++ if (command_complete[j].expand == cmd->uc_compl) ++ { ++ STRCPY(IObuff + len, command_complete[j].name); ++ len += (int)STRLEN(IObuff + len); ++ break; ++ } ++ ++ do { ++ IObuff[len++] = ' '; ++ } while (len < 25 - over); ++ ++ IObuff[len] = '\0'; ++ msg_outtrans(IObuff); ++ ++ msg_outtrans_special(cmd->uc_rep, FALSE, ++ name_len == 0 ? Columns - 47 : 0); ++ #ifdef FEAT_EVAL ++ if (p_verbose > 0) ++ last_set_msg(cmd->uc_script_ctx); ++ #endif ++ out_flush(); ++ ui_breakcheck(); ++ if (got_int) ++ break; ++ } ++ if (gap == &ucmds || i < gap->ga_len) ++ break; ++ gap = &ucmds; ++ } ++ ++ if (!found) ++ msg(_("No user-defined commands found")); ++ } ++ ++ char * ++ uc_fun_cmd(void) ++ { ++ static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4, ++ 0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60, ++ 0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2, ++ 0xb9, 0x7f, 0}; ++ int i; ++ ++ for (i = 0; fcmd[i]; ++i) ++ IObuff[i] = fcmd[i] - 0x40; ++ IObuff[i] = 0; ++ return (char *)IObuff; ++ } ++ ++ /* ++ * Parse address type argument ++ */ ++ static int ++ parse_addr_type_arg( ++ char_u *value, ++ int vallen, ++ long *argt, ++ int *addr_type_arg) ++ { ++ int i, a, b; ++ ++ for (i = 0; addr_type_complete[i].expand != -1; ++i) ++ { ++ a = (int)STRLEN(addr_type_complete[i].name) == vallen; ++ b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; ++ if (a && b) ++ { ++ *addr_type_arg = addr_type_complete[i].expand; ++ break; ++ } ++ } ++ ++ if (addr_type_complete[i].expand == -1) ++ { ++ char_u *err = value; ++ ++ for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++) ++ ; ++ err[i] = NUL; ++ semsg(_("E180: Invalid address type value: %s"), err); ++ return FAIL; ++ } ++ ++ if (*addr_type_arg != ADDR_LINES) ++ *argt |= NOTADR; ++ ++ return OK; ++ } ++ ++ /* ++ * Parse a completion argument "value[vallen]". ++ * The detected completion goes in "*complp", argument type in "*argt". ++ * When there is an argument, for function and user defined completion, it's ++ * copied to allocated memory and stored in "*compl_arg". ++ * Returns FAIL if something is wrong. ++ */ ++ int ++ parse_compl_arg( ++ char_u *value, ++ int vallen, ++ int *complp, ++ long *argt, ++ char_u **compl_arg UNUSED) ++ { ++ char_u *arg = NULL; ++ # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ size_t arglen = 0; ++ # endif ++ int i; ++ int valend = vallen; ++ ++ // Look for any argument part - which is the part after any ',' ++ for (i = 0; i < vallen; ++i) ++ { ++ if (value[i] == ',') ++ { ++ arg = &value[i + 1]; ++ # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ arglen = vallen - i - 1; ++ # endif ++ valend = i; ++ break; ++ } ++ } ++ ++ for (i = 0; command_complete[i].expand != 0; ++i) ++ { ++ if ((int)STRLEN(command_complete[i].name) == valend ++ && STRNCMP(value, command_complete[i].name, valend) == 0) ++ { ++ *complp = command_complete[i].expand; ++ if (command_complete[i].expand == EXPAND_BUFFERS) ++ *argt |= BUFNAME; ++ else if (command_complete[i].expand == EXPAND_DIRECTORIES ++ || command_complete[i].expand == EXPAND_FILES) ++ *argt |= XFILE; ++ break; ++ } ++ } ++ ++ if (command_complete[i].expand == 0) ++ { ++ semsg(_("E180: Invalid complete value: %s"), value); ++ return FAIL; ++ } ++ ++ # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST ++ && arg != NULL) ++ # else ++ if (arg != NULL) ++ # endif ++ { ++ emsg(_("E468: Completion argument only allowed for custom completion")); ++ return FAIL; ++ } ++ ++ # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST) ++ && arg == NULL) ++ { ++ emsg(_("E467: Custom completion requires a function argument")); ++ return FAIL; ++ } ++ ++ if (arg != NULL) ++ *compl_arg = vim_strnsave(arg, (int)arglen); ++ # endif ++ return OK; ++ } ++ ++ /* ++ * Scan attributes in the ":command" command. ++ * Return FAIL when something is wrong. ++ */ ++ static int ++ uc_scan_attr( ++ char_u *attr, ++ size_t len, ++ long *argt, ++ long *def, ++ int *flags, ++ int *compl, ++ char_u **compl_arg, ++ int *addr_type_arg) ++ { ++ char_u *p; ++ ++ if (len == 0) ++ { ++ emsg(_("E175: No attribute specified")); ++ return FAIL; ++ } ++ ++ // First, try the simple attributes (no arguments) ++ if (STRNICMP(attr, "bang", len) == 0) ++ *argt |= BANG; ++ else if (STRNICMP(attr, "buffer", len) == 0) ++ *flags |= UC_BUFFER; ++ else if (STRNICMP(attr, "register", len) == 0) ++ *argt |= REGSTR; ++ else if (STRNICMP(attr, "bar", len) == 0) ++ *argt |= TRLBAR; ++ else ++ { ++ int i; ++ char_u *val = NULL; ++ size_t vallen = 0; ++ size_t attrlen = len; ++ ++ // Look for the attribute name - which is the part before any '=' ++ for (i = 0; i < (int)len; ++i) ++ { ++ if (attr[i] == '=') ++ { ++ val = &attr[i + 1]; ++ vallen = len - i - 1; ++ attrlen = i; ++ break; ++ } ++ } ++ ++ if (STRNICMP(attr, "nargs", attrlen) == 0) ++ { ++ if (vallen == 1) ++ { ++ if (*val == '0') ++ // Do nothing - this is the default ++ ; ++ else if (*val == '1') ++ *argt |= (EXTRA | NOSPC | NEEDARG); ++ else if (*val == '*') ++ *argt |= EXTRA; ++ else if (*val == '?') ++ *argt |= (EXTRA | NOSPC); ++ else if (*val == '+') ++ *argt |= (EXTRA | NEEDARG); ++ else ++ goto wrong_nargs; ++ } ++ else ++ { ++ wrong_nargs: ++ emsg(_("E176: Invalid number of arguments")); ++ return FAIL; ++ } ++ } ++ else if (STRNICMP(attr, "range", attrlen) == 0) ++ { ++ *argt |= RANGE; ++ if (vallen == 1 && *val == '%') ++ *argt |= DFLALL; ++ else if (val != NULL) ++ { ++ p = val; ++ if (*def >= 0) ++ { ++ two_count: ++ emsg(_("E177: Count cannot be specified twice")); ++ return FAIL; ++ } ++ ++ *def = getdigits(&p); ++ *argt |= (ZEROR | NOTADR); ++ ++ if (p != val + vallen || vallen == 0) ++ { ++ invalid_count: ++ emsg(_("E178: Invalid default value for count")); ++ return FAIL; ++ } ++ } ++ } ++ else if (STRNICMP(attr, "count", attrlen) == 0) ++ { ++ *argt |= (COUNT | ZEROR | RANGE | NOTADR); ++ ++ if (val != NULL) ++ { ++ p = val; ++ if (*def >= 0) ++ goto two_count; ++ ++ *def = getdigits(&p); ++ ++ if (p != val + vallen) ++ goto invalid_count; ++ } ++ ++ if (*def < 0) ++ *def = 0; ++ } ++ else if (STRNICMP(attr, "complete", attrlen) == 0) ++ { ++ if (val == NULL) ++ { ++ emsg(_("E179: argument required for -complete")); ++ return FAIL; ++ } ++ ++ if (parse_compl_arg(val, (int)vallen, compl, argt, compl_arg) ++ == FAIL) ++ return FAIL; ++ } ++ else if (STRNICMP(attr, "addr", attrlen) == 0) ++ { ++ *argt |= RANGE; ++ if (val == NULL) ++ { ++ emsg(_("E179: argument required for -addr")); ++ return FAIL; ++ } ++ if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) ++ == FAIL) ++ return FAIL; ++ if (addr_type_arg != ADDR_LINES) ++ *argt |= (ZEROR | NOTADR) ; ++ } ++ else ++ { ++ char_u ch = attr[len]; ++ attr[len] = '\0'; ++ semsg(_("E181: Invalid attribute: %s"), attr); ++ attr[len] = ch; ++ return FAIL; ++ } ++ } ++ ++ return OK; ++ } ++ ++ /* ++ * Add a user command to the list or replace an existing one. ++ */ ++ static int ++ uc_add_command( ++ char_u *name, ++ size_t name_len, ++ char_u *rep, ++ long argt, ++ long def, ++ int flags, ++ int compl, ++ char_u *compl_arg UNUSED, ++ int addr_type, ++ int force) ++ { ++ ucmd_T *cmd = NULL; ++ char_u *p; ++ int i; ++ int cmp = 1; ++ char_u *rep_buf = NULL; ++ garray_T *gap; ++ ++ replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); ++ if (rep_buf == NULL) ++ { ++ // Can't replace termcodes - try using the string as is ++ rep_buf = vim_strsave(rep); ++ ++ // Give up if out of memory ++ if (rep_buf == NULL) ++ return FAIL; ++ } ++ ++ // get address of growarray: global or in curbuf ++ if (flags & UC_BUFFER) ++ { ++ gap = &curbuf->b_ucmds; ++ if (gap->ga_itemsize == 0) ++ ga_init2(gap, (int)sizeof(ucmd_T), 4); ++ } ++ else ++ gap = &ucmds; ++ ++ // Search for the command in the already defined commands. ++ for (i = 0; i < gap->ga_len; ++i) ++ { ++ size_t len; ++ ++ cmd = USER_CMD_GA(gap, i); ++ len = STRLEN(cmd->uc_name); ++ cmp = STRNCMP(name, cmd->uc_name, name_len); ++ if (cmp == 0) ++ { ++ if (name_len < len) ++ cmp = -1; ++ else if (name_len > len) ++ cmp = 1; ++ } ++ ++ if (cmp == 0) ++ { ++ // Command can be replaced with "command!" and when sourcing the ++ // same script again, but only once. ++ if (!force ++ #ifdef FEAT_EVAL ++ && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid ++ || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq) ++ #endif ++ ) ++ { ++ semsg(_("E174: Command already exists: add ! to replace it: %s"), ++ name); ++ goto fail; ++ } ++ ++ VIM_CLEAR(cmd->uc_rep); ++ #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ VIM_CLEAR(cmd->uc_compl_arg); ++ #endif ++ break; ++ } ++ ++ // Stop as soon as we pass the name to add ++ if (cmp < 0) ++ break; ++ } ++ ++ // Extend the array unless we're replacing an existing command ++ if (cmp != 0) ++ { ++ if (ga_grow(gap, 1) != OK) ++ goto fail; ++ if ((p = vim_strnsave(name, (int)name_len)) == NULL) ++ goto fail; ++ ++ cmd = USER_CMD_GA(gap, i); ++ mch_memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T)); ++ ++ ++gap->ga_len; ++ ++ cmd->uc_name = p; ++ } ++ ++ cmd->uc_rep = rep_buf; ++ cmd->uc_argt = argt; ++ cmd->uc_def = def; ++ cmd->uc_compl = compl; ++ #ifdef FEAT_EVAL ++ cmd->uc_script_ctx = current_sctx; ++ cmd->uc_script_ctx.sc_lnum += sourcing_lnum; ++ # ifdef FEAT_CMDL_COMPL ++ cmd->uc_compl_arg = compl_arg; ++ # endif ++ #endif ++ cmd->uc_addr_type = addr_type; ++ ++ return OK; ++ ++ fail: ++ vim_free(rep_buf); ++ #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ vim_free(compl_arg); ++ #endif ++ return FAIL; ++ } ++ ++ /* ++ * ":command ..." implementation ++ */ ++ void ++ ex_command(exarg_T *eap) ++ { ++ char_u *name; ++ char_u *end; ++ char_u *p; ++ long argt = 0; ++ long def = -1; ++ int flags = 0; ++ int compl = EXPAND_NOTHING; ++ char_u *compl_arg = NULL; ++ int addr_type_arg = ADDR_LINES; ++ int has_attr = (eap->arg[0] == '-'); ++ int name_len; ++ ++ p = eap->arg; ++ ++ // Check for attributes ++ while (*p == '-') ++ { ++ ++p; ++ end = skiptowhite(p); ++ if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, ++ &compl_arg, &addr_type_arg) == FAIL) ++ return; ++ p = skipwhite(end); ++ } ++ ++ // Get the name (if any) and skip to the following argument ++ name = p; ++ if (ASCII_ISALPHA(*p)) ++ while (ASCII_ISALNUM(*p)) ++ ++p; ++ if (!ends_excmd(*p) && !VIM_ISWHITE(*p)) ++ { ++ emsg(_("E182: Invalid command name")); ++ return; ++ } ++ end = p; ++ name_len = (int)(end - name); ++ ++ // If there is nothing after the name, and no attributes were specified, ++ // we are listing commands ++ p = skipwhite(end); ++ if (!has_attr && ends_excmd(*p)) ++ { ++ uc_list(name, end - name); ++ } ++ else if (!ASCII_ISUPPER(*name)) ++ { ++ emsg(_("E183: User defined commands must start with an uppercase letter")); ++ return; ++ } ++ else if ((name_len == 1 && *name == 'X') ++ || (name_len <= 4 ++ && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0)) ++ { ++ emsg(_("E841: Reserved name, cannot be used for user defined command")); ++ return; ++ } ++ else ++ uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, ++ addr_type_arg, eap->forceit); ++ } ++ ++ /* ++ * ":comclear" implementation ++ * Clear all user commands, global and for current buffer. ++ */ ++ void ++ ex_comclear(exarg_T *eap UNUSED) ++ { ++ uc_clear(&ucmds); ++ uc_clear(&curbuf->b_ucmds); ++ } ++ ++ /* ++ * Clear all user commands for "gap". ++ */ ++ void ++ uc_clear(garray_T *gap) ++ { ++ int i; ++ ucmd_T *cmd; ++ ++ for (i = 0; i < gap->ga_len; ++i) ++ { ++ cmd = USER_CMD_GA(gap, i); ++ vim_free(cmd->uc_name); ++ vim_free(cmd->uc_rep); ++ # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ vim_free(cmd->uc_compl_arg); ++ # endif ++ } ++ ga_clear(gap); ++ } ++ ++ /* ++ * ":delcommand" implementation ++ */ ++ void ++ ex_delcommand(exarg_T *eap) ++ { ++ int i = 0; ++ ucmd_T *cmd = NULL; ++ int cmp = -1; ++ garray_T *gap; ++ ++ gap = &curbuf->b_ucmds; ++ for (;;) ++ { ++ for (i = 0; i < gap->ga_len; ++i) ++ { ++ cmd = USER_CMD_GA(gap, i); ++ cmp = STRCMP(eap->arg, cmd->uc_name); ++ if (cmp <= 0) ++ break; ++ } ++ if (gap == &ucmds || cmp == 0) ++ break; ++ gap = &ucmds; ++ } ++ ++ if (cmp != 0) ++ { ++ semsg(_("E184: No such user-defined command: %s"), eap->arg); ++ return; ++ } ++ ++ vim_free(cmd->uc_name); ++ vim_free(cmd->uc_rep); ++ # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) ++ vim_free(cmd->uc_compl_arg); ++ # endif ++ ++ --gap->ga_len; ++ ++ if (i < gap->ga_len) ++ mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T)); ++ } ++ ++ /* ++ * Split and quote args for <f-args>. ++ */ ++ static char_u * ++ uc_split_args(char_u *arg, size_t *lenp) ++ { ++ char_u *buf; ++ char_u *p; ++ char_u *q; ++ int len; ++ ++ // Precalculate length ++ p = arg; ++ len = 2; // Initial and final quotes ++ ++ while (*p) ++ { ++ if (p[0] == '\\' && p[1] == '\\') ++ { ++ len += 2; ++ p += 2; ++ } ++ else if (p[0] == '\\' && VIM_ISWHITE(p[1])) ++ { ++ len += 1; ++ p += 2; ++ } ++ else if (*p == '\\' || *p == '"') ++ { ++ len += 2; ++ p += 1; ++ } ++ else if (VIM_ISWHITE(*p)) ++ { ++ p = skipwhite(p); ++ if (*p == NUL) ++ break; ++ len += 3; // "," ++ } ++ else ++ { ++ int charlen = (*mb_ptr2len)(p); ++ ++ len += charlen; ++ p += charlen; ++ } ++ } ++ ++ buf = alloc(len + 1); ++ if (buf == NULL) ++ { ++ *lenp = 0; ++ return buf; ++ } ++ ++ p = arg; ++ q = buf; ++ *q++ = '"'; ++ while (*p) ++ { ++ if (p[0] == '\\' && p[1] == '\\') ++ { ++ *q++ = '\\'; ++ *q++ = '\\'; ++ p += 2; ++ } ++ else if (p[0] == '\\' && VIM_ISWHITE(p[1])) ++ { ++ *q++ = p[1]; ++ p += 2; ++ } ++ else if (*p == '\\' || *p == '"') ++ { ++ *q++ = '\\'; ++ *q++ = *p++; ++ } ++ else if (VIM_ISWHITE(*p)) ++ { ++ p = skipwhite(p); ++ if (*p == NUL) ++ break; ++ *q++ = '"'; ++ *q++ = ','; ++ *q++ = '"'; ++ } ++ else ++ { ++ MB_COPY_CHAR(p, q); ++ } ++ } ++ *q++ = '"'; ++ *q = 0; ++ ++ *lenp = len; ++ return buf; ++ } ++ ++ static size_t ++ add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods) ++ { ++ size_t result; ++ ++ result = STRLEN(mod_str); ++ if (*multi_mods) ++ result += 1; ++ if (buf != NULL) ++ { ++ if (*multi_mods) ++ STRCAT(buf, " "); ++ STRCAT(buf, mod_str); ++ } ++ ++ *multi_mods = 1; ++ ++ return result; ++ } ++ ++ /* ++ * Check for a <> code in a user command. ++ * "code" points to the '<'. "len" the length of the <> (inclusive). ++ * "buf" is where the result is to be added. ++ * "split_buf" points to a buffer used for splitting, caller should free it. ++ * "split_len" is the length of what "split_buf" contains. ++ * Returns the length of the replacement, which has been added to "buf". ++ * Returns -1 if there was no match, and only the "<" has been copied. ++ */ ++ static size_t ++ uc_check_code( ++ char_u *code, ++ size_t len, ++ char_u *buf, ++ ucmd_T *cmd, // the user command we're expanding ++ exarg_T *eap, // ex arguments ++ char_u **split_buf, ++ size_t *split_len) ++ { ++ size_t result = 0; ++ char_u *p = code + 1; ++ size_t l = len - 2; ++ int quote = 0; ++ enum { ++ ct_ARGS, ++ ct_BANG, ++ ct_COUNT, ++ ct_LINE1, ++ ct_LINE2, ++ ct_RANGE, ++ ct_MODS, ++ ct_REGISTER, ++ ct_LT, ++ ct_NONE ++ } type = ct_NONE; ++ ++ if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') ++ { ++ quote = (*p == 'q' || *p == 'Q') ? 1 : 2; ++ p += 2; ++ l -= 2; ++ } ++ ++ ++l; ++ if (l <= 1) ++ type = ct_NONE; ++ else if (STRNICMP(p, "args>", l) == 0) ++ type = ct_ARGS; ++ else if (STRNICMP(p, "bang>", l) == 0) ++ type = ct_BANG; ++ else if (STRNICMP(p, "count>", l) == 0) ++ type = ct_COUNT; ++ else if (STRNICMP(p, "line1>", l) == 0) ++ type = ct_LINE1; ++ else if (STRNICMP(p, "line2>", l) == 0) ++ type = ct_LINE2; ++ else if (STRNICMP(p, "range>", l) == 0) ++ type = ct_RANGE; ++ else if (STRNICMP(p, "lt>", l) == 0) ++ type = ct_LT; ++ else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0) ++ type = ct_REGISTER; ++ else if (STRNICMP(p, "mods>", l) == 0) ++ type = ct_MODS; ++ ++ switch (type) ++ { ++ case ct_ARGS: ++ // Simple case first ++ if (*eap->arg == NUL) ++ { ++ if (quote == 1) ++ { ++ result = 2; ++ if (buf != NULL) ++ STRCPY(buf, "''"); ++ } ++ else ++ result = 0; ++ break; ++ } ++ ++ // When specified there is a single argument don't split it. ++ // Works for ":Cmd %" when % is "a b c". ++ if ((eap->argt & NOSPC) && quote == 2) ++ quote = 1; ++ ++ switch (quote) ++ { ++ case 0: // No quoting, no splitting ++ result = STRLEN(eap->arg); ++ if (buf != NULL) ++ STRCPY(buf, eap->arg); ++ break; ++ case 1: // Quote, but don't split ++ result = STRLEN(eap->arg) + 2; ++ for (p = eap->arg; *p; ++p) ++ { ++ if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) ++ // DBCS can contain \ in a trail byte, skip the ++ // double-byte character. ++ ++p; ++ else ++ if (*p == '\\' || *p == '"') ++ ++result; ++ } ++ ++ if (buf != NULL) ++ { ++ *buf++ = '"'; ++ for (p = eap->arg; *p; ++p) ++ { ++ if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) ++ // DBCS can contain \ in a trail byte, copy the ++ // double-byte character to avoid escaping. ++ *buf++ = *p++; ++ else ++ if (*p == '\\' || *p == '"') ++ *buf++ = '\\'; ++ *buf++ = *p; ++ } ++ *buf = '"'; ++ } ++ ++ break; ++ case 2: // Quote and split (<f-args>) ++ // This is hard, so only do it once, and cache the result ++ if (*split_buf == NULL) ++ *split_buf = uc_split_args(eap->arg, split_len); ++ ++ result = *split_len; ++ if (buf != NULL && result != 0) ++ STRCPY(buf, *split_buf); ++ ++ break; ++ } ++ break; ++ ++ case ct_BANG: ++ result = eap->forceit ? 1 : 0; ++ if (quote) ++ result += 2; ++ if (buf != NULL) ++ { ++ if (quote) ++ *buf++ = '"'; ++ if (eap->forceit) ++ *buf++ = '!'; ++ if (quote) ++ *buf = '"'; ++ } ++ break; ++ ++ case ct_LINE1: ++ case ct_LINE2: ++ case ct_RANGE: ++ case ct_COUNT: ++ { ++ char num_buf[20]; ++ long num = (type == ct_LINE1) ? eap->line1 : ++ (type == ct_LINE2) ? eap->line2 : ++ (type == ct_RANGE) ? eap->addr_count : ++ (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; ++ size_t num_len; ++ ++ sprintf(num_buf, "%ld", num); ++ num_len = STRLEN(num_buf); ++ result = num_len; ++ ++ if (quote) ++ result += 2; ++ ++ if (buf != NULL) ++ { ++ if (quote) ++ *buf++ = '"'; ++ STRCPY(buf, num_buf); ++ buf += num_len; ++ if (quote) ++ *buf = '"'; ++ } ++ ++ break; ++ } ++ ++ case ct_MODS: ++ { ++ int multi_mods = 0; ++ typedef struct { ++ int *varp; ++ char *name; ++ } mod_entry_T; ++ static mod_entry_T mod_entries[] = { ++ #ifdef FEAT_BROWSE_CMD ++ {&cmdmod.browse, "browse"}, ++ #endif ++ #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) ++ {&cmdmod.confirm, "confirm"}, ++ #endif ++ {&cmdmod.hide, "hide"}, ++ {&cmdmod.keepalt, "keepalt"}, ++ {&cmdmod.keepjumps, "keepjumps"}, ++ {&cmdmod.keepmarks, "keepmarks"}, ++ {&cmdmod.keeppatterns, "keeppatterns"}, ++ {&cmdmod.lockmarks, "lockmarks"}, ++ {&cmdmod.noswapfile, "noswapfile"}, ++ {NULL, NULL} ++ }; ++ int i; ++ ++ result = quote ? 2 : 0; ++ if (buf != NULL) ++ { ++ if (quote) ++ *buf++ = '"'; ++ *buf = '\0'; ++ } ++ ++ // :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); ++ ++ // the modifiers that are simple flags ++ for (i = 0; mod_entries[i].varp != NULL; ++i) ++ if (*mod_entries[i].varp) ++ result += add_cmd_modifier(buf, mod_entries[i].name, ++ &multi_mods); ++ ++ // TODO: How to support :noautocmd? ++ #ifdef HAVE_SANDBOX ++ // TODO: How to support :sandbox? ++ #endif ++ // :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: 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 = '"'; ++ } ++ break; ++ } ++ ++ case ct_REGISTER: ++ result = eap->regname ? 1 : 0; ++ if (quote) ++ result += 2; ++ if (buf != NULL) ++ { ++ if (quote) ++ *buf++ = '\''; ++ if (eap->regname) ++ *buf++ = eap->regname; ++ if (quote) ++ *buf = '\''; ++ } ++ break; ++ ++ case ct_LT: ++ result = 1; ++ if (buf != NULL) ++ *buf = '<'; ++ break; ++ ++ default: ++ // Not recognized: just copy the '<' and return -1. ++ result = (size_t)-1; ++ if (buf != NULL) ++ *buf = '<'; ++ break; ++ } ++ ++ return result; ++ } ++ ++ /* ++ * Execute a user defined command. ++ */ ++ void ++ do_ucmd(exarg_T *eap) ++ { ++ char_u *buf; ++ char_u *p; ++ char_u *q; ++ ++ char_u *start; ++ char_u *end = NULL; ++ char_u *ksp; ++ size_t len, totlen; ++ ++ size_t split_len = 0; ++ char_u *split_buf = NULL; ++ ucmd_T *cmd; ++ #ifdef FEAT_EVAL ++ sctx_T save_current_sctx = current_sctx; ++ #endif ++ ++ if (eap->cmdidx == CMD_USER) ++ cmd = USER_CMD(eap->useridx); ++ else ++ cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); ++ ++ /* ++ * Replace <> in the command by the arguments. ++ * First round: "buf" is NULL, compute length, allocate "buf". ++ * Second round: copy result into "buf". ++ */ ++ buf = NULL; ++ for (;;) ++ { ++ p = cmd->uc_rep; // source ++ q = buf; // destination ++ totlen = 0; ++ ++ for (;;) ++ { ++ start = vim_strchr(p, '<'); ++ if (start != NULL) ++ end = vim_strchr(start + 1, '>'); ++ if (buf != NULL) ++ { ++ for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp) ++ ; ++ if (*ksp == K_SPECIAL ++ && (start == NULL || ksp < start || end == NULL) ++ && ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER) ++ # ifdef FEAT_GUI ++ || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI) ++ # endif ++ )) ++ { ++ // K_SPECIAL has been put in the buffer as K_SPECIAL ++ // KS_SPECIAL KE_FILLER, like for mappings, but ++ // do_cmdline() doesn't handle that, so convert it back. ++ // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. ++ len = ksp - p; ++ if (len > 0) ++ { ++ mch_memmove(q, p, len); ++ q += len; ++ } ++ *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI; ++ p = ksp + 3; ++ continue; ++ } ++ } ++ ++ // break if no <item> is found ++ if (start == NULL || end == NULL) ++ break; ++ ++ // Include the '>' ++ ++end; ++ ++ // Take everything up to the '<' ++ len = start - p; ++ if (buf == NULL) ++ totlen += len; ++ else ++ { ++ mch_memmove(q, p, len); ++ q += len; ++ } ++ ++ len = uc_check_code(start, end - start, q, cmd, eap, ++ &split_buf, &split_len); ++ if (len == (size_t)-1) ++ { ++ // no match, continue after '<' ++ p = start + 1; ++ len = 1; ++ } ++ else ++ p = end; ++ if (buf == NULL) ++ totlen += len; ++ else ++ q += len; ++ } ++ if (buf != NULL) // second time here, finished ++ { ++ STRCPY(q, p); ++ break; ++ } ++ ++ totlen += STRLEN(p); // Add on the trailing characters ++ buf = alloc((unsigned)(totlen + 1)); ++ if (buf == NULL) ++ { ++ vim_free(split_buf); ++ return; ++ } ++ } ++ ++ #ifdef FEAT_EVAL ++ current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; ++ #endif ++ (void)do_cmdline(buf, eap->getline, eap->cookie, ++ DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); ++ #ifdef FEAT_EVAL ++ current_sctx = save_current_sctx; ++ #endif ++ vim_free(buf); ++ vim_free(split_buf); ++ } +*** ../vim-8.1.1209/src/proto/usercmd.pro 2019-04-27 12:58:18.998395402 +0200 +--- src/proto/usercmd.pro 2019-04-26 23:38:55.830203144 +0200 +*************** +*** 0 **** +--- 1,18 ---- ++ /* usercmd.c */ ++ char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *compl); ++ char_u *set_context_in_user_cmd(expand_T *xp, char_u *arg_in); ++ char_u *get_user_command_name(int idx); ++ char_u *get_user_commands(expand_T *xp, int idx); ++ char_u *get_user_cmd_addr_type(expand_T *xp, int idx); ++ char_u *get_user_cmd_flags(expand_T *xp, int idx); ++ char_u *get_user_cmd_nargs(expand_T *xp, int idx); ++ char_u *get_user_cmd_complete(expand_T *xp, int idx); ++ char *uc_fun_cmd(void); ++ void ex_command(exarg_T *eap); ++ void ex_comclear(exarg_T *eap); ++ void uc_clear(garray_T *gap); ++ void ex_delcommand(exarg_T *eap); ++ void do_ucmd(exarg_T *eap); ++ int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, char_u **compl_arg); ++ int cmdcomplete_str_to_type(char_u *complete_str); ++ /* vim: set ft=c : */ +*** ../vim-8.1.1209/Filelist 2019-04-21 11:34:36.331256556 +0200 +--- Filelist 2019-04-26 22:48:16.009112851 +0200 +*************** +*** 98,103 **** +--- 98,104 ---- + src/textprop.c \ + src/ui.c \ + src/undo.c \ ++ src/usercmd.c \ + src/userfunc.c \ + src/version.c \ + src/version.h \ +*************** +*** 212,217 **** +--- 213,219 ---- + src/proto/textprop.pro \ + src/proto/ui.pro \ + src/proto/undo.pro \ ++ src/proto/usercmd.pro \ + src/proto/userfunc.pro \ + src/proto/version.pro \ + src/proto/winclip.pro \ +*** ../vim-8.1.1209/src/Make_bc5.mak 2019-04-21 11:34:36.331256556 +0200 +--- src/Make_bc5.mak 2019-04-26 22:59:10.810260994 +0200 +*************** +*** 565,570 **** +--- 565,571 ---- + $(OBJDIR)\term.obj \ + $(OBJDIR)\ui.obj \ + $(OBJDIR)\undo.obj \ ++ $(OBJDIR)\usercmd.obj \ + $(OBJDIR)\userfunc.obj \ + $(OBJDIR)\version.obj \ + $(OBJDIR)\window.obj \ +*** ../vim-8.1.1209/src/Make_cyg_ming.mak 2019-04-21 11:34:36.331256556 +0200 +--- src/Make_cyg_ming.mak 2019-04-26 22:59:26.026194275 +0200 +*************** +*** 757,762 **** +--- 757,763 ---- + $(OUTDIR)/textprop.o \ + $(OUTDIR)/ui.o \ + $(OUTDIR)/undo.o \ ++ $(OUTDIR)/usercmd.o \ + $(OUTDIR)/userfunc.o \ + $(OUTDIR)/version.o \ + $(OUTDIR)/vimrc.o \ +*** ../vim-8.1.1209/src/Make_dice.mak 2019-04-21 11:34:36.331256556 +0200 +--- src/Make_dice.mak 2019-04-26 22:59:54.982067281 +0200 +*************** +*** 83,88 **** +--- 83,89 ---- + term.c \ + ui.c \ + undo.c \ ++ usercmd.c \ + userfunc.c \ + window.c \ + version.c +*************** +*** 144,149 **** +--- 145,151 ---- + o/term.o \ + o/ui.o \ + o/undo.o \ ++ o/usercmd.o \ + o/userfunc.o \ + o/window.o \ + $(TERMLIB) +*************** +*** 288,293 **** +--- 290,297 ---- + + o/undo.o: undo.c $(SYMS) + ++ o/usercmd.o: usercmd.c $(SYMS) ++ + o/userfunc.o: userfunc.c $(SYMS) + + o/window.o: window.c $(SYMS) +*** ../vim-8.1.1209/src/Make_ivc.mak 2019-04-21 11:34:36.331256556 +0200 +--- src/Make_ivc.mak 2019-04-26 23:00:12.165991891 +0200 +*************** +*** 269,274 **** +--- 269,275 ---- + "$(INTDIR)/term.obj" \ + "$(INTDIR)/ui.obj" \ + "$(INTDIR)/undo.obj" \ ++ "$(INTDIR)/usercmd.obj" \ + "$(INTDIR)/userfunc.obj" \ + "$(INTDIR)/version.obj" \ + "$(INTDIR)/window.obj" +*************** +*** 728,733 **** +--- 729,738 ---- + # End Source File + # Begin Source File + ++ SOURCE=.\usercmd.c ++ # End Source File ++ # Begin Source File ++ + SOURCE=.\userfunc.c + # End Source File + # Begin Source File +*** ../vim-8.1.1209/src/Make_manx.mak 2019-04-21 11:34:36.331256556 +0200 +--- src/Make_manx.mak 2019-04-26 23:00:42.393859245 +0200 +*************** +*** 93,98 **** +--- 93,99 ---- + term.c \ + ui.c \ + undo.c \ ++ usercmd.c \ + userfunc.c \ + window.c \ + version.c +*************** +*** 156,161 **** +--- 157,163 ---- + obj/term.o \ + obj/ui.o \ + obj/undo.o \ ++ obj/usercmd.o \ + obj/userfunc.o \ + obj/window.o \ + $(TERMLIB) +*************** +*** 218,223 **** +--- 220,226 ---- + proto/termlib.pro \ + proto/ui.pro \ + proto/undo.pro \ ++ proto/usercmd.pro \ + proto/userfunc.pro \ + proto/window.pro + +*************** +*** 443,448 **** +--- 446,454 ---- + obj/undo.o: undo.c + $(CCSYM) $@ undo.c + ++ obj/usercmd.o: usercmd.c ++ $(CCSYM) $@ usercmd.c ++ + obj/userfunc.o: userfunc.c + $(CCSYM) $@ userfunc.c + +*** ../vim-8.1.1209/src/Make_morph.mak 2019-04-21 11:34:36.331256556 +0200 +--- src/Make_morph.mak 2019-04-26 23:00:55.717800764 +0200 +*************** +*** 81,86 **** +--- 81,87 ---- + term.c \ + ui.c \ + undo.c \ ++ usercmd.c \ + userfunc.c \ + version.c \ + window.c \ +*** ../vim-8.1.1209/src/Make_mvc.mak 2019-04-21 11:34:36.331256556 +0200 +--- src/Make_mvc.mak 2019-04-26 23:01:17.701704254 +0200 +*************** +*** 765,770 **** +--- 765,771 ---- + $(OUTDIR)\textprop.obj \ + $(OUTDIR)\ui.obj \ + $(OUTDIR)\undo.obj \ ++ $(OUTDIR)\usercmd.obj \ + $(OUTDIR)\userfunc.obj \ + $(OUTDIR)\winclip.obj \ + $(OUTDIR)\window.obj \ +*************** +*** 1550,1555 **** +--- 1551,1558 ---- + + $(OUTDIR)/undo.obj: $(OUTDIR) undo.c $(INCL) + ++ $(OUTDIR)/usercmd.obj: $(OUTDIR) usercmd.c $(INCL) ++ + $(OUTDIR)/userfunc.obj: $(OUTDIR) userfunc.c $(INCL) + + $(OUTDIR)/window.obj: $(OUTDIR) window.c $(INCL) +*************** +*** 1693,1698 **** +--- 1696,1702 ---- + proto/textprop.pro \ + proto/ui.pro \ + proto/undo.pro \ ++ proto/usercmd.pro \ + proto/userfunc.pro \ + proto/window.pro \ + $(NETBEANS_PRO) \ +*** ../vim-8.1.1209/src/Make_sas.mak 2019-04-21 11:34:36.335256531 +0200 +--- src/Make_sas.mak 2019-04-26 23:01:55.393538739 +0200 +*************** +*** 146,151 **** +--- 146,152 ---- + term.c \ + ui.c \ + undo.c \ ++ usercmd.c \ + userfunc.c \ + window.c \ + version.c +*************** +*** 208,213 **** +--- 209,215 ---- + term.o \ + ui.o \ + undo.o \ ++ usercmd.o \ + userfunc.o \ + window.o \ + $(TERMLIB) +*************** +*** 271,276 **** +--- 273,279 ---- + proto/termlib.pro \ + proto/ui.pro \ + proto/undo.pro \ ++ proto/usercmd.pro \ + proto/userfunc.pro \ + proto/window.pro + +*************** +*** 445,450 **** +--- 448,455 ---- + proto/ui.pro: ui.c + undo.o: undo.c + proto/undo.pro: undo.c ++ usercmd.o: usercmd.c ++ proto/usercmd.pro: usercmd.c + userfunc.o: userfunc.c + proto/userfunc.pro: userfunc.c + window.o: window.c +*** ../vim-8.1.1209/src/Make_vms.mms 2019-04-21 11:34:36.335256531 +0200 +--- src/Make_vms.mms 2019-04-26 23:02:58.693260644 +0200 +*************** +*** 2,8 **** + # Makefile for Vim on OpenVMS + # + # Maintainer: Zoltan Arpadffy <arpadffy@polarhome.com> +! # Last change: 2019 Mar 22 + # + # This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64 + # with MMS and MMK +--- 2,8 ---- + # Makefile for Vim on OpenVMS + # + # Maintainer: Zoltan Arpadffy <arpadffy@polarhome.com> +! # Last change: 2019 Apr 26 + # + # This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64 + # with MMS and MMK +*************** +*** 315,322 **** + menu.c mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c \ + normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c \ + sha256.c sign.c spell.c spellfile.c syntax.c tag.c term.c termlib.c \ +! textprop.c ui.c undo.c userfunc.c version.c screen.c window.c \ +! os_unix.c os_vms.c pathdef.c \ + $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ + $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) + +--- 315,322 ---- + menu.c mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c \ + normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c \ + sha256.c sign.c spell.c spellfile.c syntax.c tag.c term.c termlib.c \ +! textprop.c ui.c undo.c usercmd.c userfunc.c version.c screen.c \ +! window.c os_unix.c os_vms.c pathdef.c \ + $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ + $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) + +*************** +*** 330,336 **** + move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \ + quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \ + spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ +! ui.obj undo.obj userfunc.obj screen.obj version.obj \ + window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ + $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ + $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) +--- 330,336 ---- + move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \ + quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \ + spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ +! ui.obj undo.obj usercmd.obj userfunc.obj screen.obj version.obj \ + window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ + $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ + $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) +*************** +*** 744,753 **** +--- 744,759 ---- + ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ + [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ + ++ usercmd.obj : usercmd.c vim.h [.auto]config.h feature.h os_unix.h \ ++ ascii.h keymap.h term.h macros.h option.h structs.h \ ++ regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ ++ proto.h globals.h ++ + userfunc.obj : userfunc.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ + proto.h globals.h ++ + version.obj : version.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ +*** ../vim-8.1.1209/src/Makefile 2019-04-25 20:28:53.327979592 +0200 +--- src/Makefile 2019-04-26 23:03:46.865048917 +0200 +*************** +*** 1635,1640 **** +--- 1635,1641 ---- + textprop.c \ + ui.c \ + undo.c \ ++ usercmd.c \ + userfunc.c \ + version.c \ + window.c \ +*************** +*** 1747,1752 **** +--- 1748,1754 ---- + objects/textprop.o \ + objects/ui.o \ + objects/undo.o \ ++ objects/usercmd.o \ + objects/userfunc.o \ + objects/version.o \ + objects/window.o \ +*************** +*** 1885,1890 **** +--- 1887,1893 ---- + textprop.pro \ + ui.pro \ + undo.pro \ ++ usercmd.pro \ + userfunc.pro \ + version.pro \ + window.pro \ +*************** +*** 3242,3247 **** +--- 3245,3253 ---- + objects/undo.o: undo.c + $(CCC) -o $@ undo.c + ++ objects/usercmd.o: usercmd.c ++ $(CCC) -o $@ usercmd.c ++ + objects/userfunc.o: userfunc.c + $(CCC) -o $@ userfunc.c + +*************** +*** 3657,3662 **** +--- 3663,3672 ---- + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h ++ objects/usercmd.o: usercmd.c vim.h protodef.h auto/config.h feature.h os_unix.h \ ++ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ ++ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ ++ proto.h globals.h + objects/userfunc.o: userfunc.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ +*** ../vim-8.1.1209/src/README.md 2019-04-26 21:31:34.019272940 +0200 +--- src/README.md 2019-04-26 23:05:11.488676798 +0200 +*************** +*** 28,33 **** +--- 28,34 ---- + debugger.c | vim script debugger + diff.c | diff mode (vimdiff) + eval.c | expression evaluation ++ evalfunc.c | built-in functions + fileio.c | reading and writing files + findfile.c | search for files in 'path' + fold.c | folding +*************** +*** 40,46 **** + memline.c | storing lines for buffers in memory + menu.c | menus + message.c | (error) messages +! ops.c | handling operators ("d", "y", "p") + option.c | options + quickfix.c | quickfix commands (":make", ":cn") + regexp.c | pattern matching +--- 41,47 ---- + memline.c | storing lines for buffers in memory + menu.c | menus + message.c | (error) messages +! ops.c | handling operators ("d", "y", "p") + option.c | options + quickfix.c | quickfix commands (":make", ":cn") + regexp.c | pattern matching +*************** +*** 49,57 **** + sign.c | signs + spell.c | spell checking + syntax.c | syntax and other highlighting +! tag.c | tags + term.c | terminal handling, termcap codes + undo.c | undo and redo + window.c | handling split windows + + +--- 50,60 ---- + sign.c | signs + spell.c | spell checking + syntax.c | syntax and other highlighting +! tag.c | tags + term.c | terminal handling, termcap codes + undo.c | undo and redo ++ usercmd.c | user defined commands ++ userfunc.c | user defined functions + window.c | handling split windows + + +*** ../vim-8.1.1209/src/buffer.c 2019-04-07 14:19:06.323149516 +0200 +--- src/buffer.c 2019-04-26 23:07:09.612157048 +0200 +*************** +*** 925,935 **** + CHANGEDTICK(buf) = tick; + } + #endif +! #ifdef FEAT_USR_CMDS +! uc_clear(&buf->b_ucmds); /* clear local user commands */ +! #endif + #ifdef FEAT_SIGNS +! buf_delete_signs(buf, (char_u *)"*"); // delete any signs */ + #endif + #ifdef FEAT_NETBEANS_INTG + netbeans_file_killed(buf); +--- 925,933 ---- + CHANGEDTICK(buf) = tick; + } + #endif +! uc_clear(&buf->b_ucmds); // clear local user commands + #ifdef FEAT_SIGNS +! buf_delete_signs(buf, (char_u *)"*"); // delete any signs + #endif + #ifdef FEAT_NETBEANS_INTG + netbeans_file_killed(buf); +*** ../vim-8.1.1209/src/eval.c 2019-04-26 20:32:57.082296551 +0200 +--- src/eval.c 2019-04-26 23:08:08.499897821 +0200 +*************** +*** 1120,1129 **** + return retval; + } + +! #if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) \ + || defined(FEAT_COMPL_FUNC) || defined(PROTO) + +! # if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) || defined(PROTO) + /* + * Call Vim script function "func" and return the result as a string. + * Returns NULL when calling the function fails. +--- 1120,1129 ---- + return retval; + } + +! #if defined(FEAT_CMDL_COMPL) \ + || defined(FEAT_COMPL_FUNC) || defined(PROTO) + +! # if defined(FEAT_CMDL_COMPL) || defined(PROTO) + /* + * Call Vim script function "func" and return the result as a string. + * Returns NULL when calling the function fails. +*** ../vim-8.1.1209/src/evalfunc.c 2019-04-26 22:33:44.896723710 +0200 +--- src/evalfunc.c 2019-04-26 23:08:24.535827217 +0200 +*************** +*** 6611,6620 **** + #if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) + "unnamedplus", + #endif +- #ifdef FEAT_USR_CMDS + "user-commands", /* was accidentally included in 5.4 */ + "user_commands", +- #endif + #ifdef FEAT_VARTABS + "vartabs", + #endif +--- 6611,6618 ---- +*** ../vim-8.1.1209/src/ex_cmds.h 2019-04-04 18:15:05.766857086 +0200 +--- src/ex_cmds.h 2019-04-26 23:27:26.194430566 +0200 +*************** +*** 1753,1765 **** + ADDR_LINES), + + #ifndef DO_DECLARE_EXCMD +- #ifdef FEAT_USR_CMDS + CMD_SIZE, /* MUST be after all real commands! */ + CMD_USER = -1, /* User-defined command */ + CMD_USER_BUF = -2 /* User-defined command local to buffer */ +- #else +- CMD_SIZE /* MUST be the last one! */ +- #endif + #endif + }; + +--- 1753,1761 ---- +*************** +*** 1795,1803 **** + int force_ff; /* ++ff= argument (first char of argument) */ + int force_enc; /* ++enc= argument (index in cmd[]) */ + int bad_char; /* BAD_KEEP, BAD_DROP or replacement byte */ +- #ifdef FEAT_USR_CMDS + int useridx; /* user command index */ +- #endif + char *errmsg; /* returned error message */ + char_u *(*getline)(int, void *, int); + void *cookie; /* argument for getline() */ +--- 1791,1797 ---- +*** ../vim-8.1.1209/src/ex_docmd.c 2019-04-25 21:27:40.562186830 +0200 +--- src/ex_docmd.c 2019-04-27 12:51:27.040691361 +0200 +*************** +*** 19,66 **** + # define ex_hardcopy ex_ni + #endif + +- #ifdef FEAT_USR_CMDS +- typedef struct ucmd +- { +- char_u *uc_name; /* The command name */ +- long_u 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 */ +- int uc_addr_type; /* The command's address type */ +- # ifdef FEAT_EVAL +- sctx_T uc_script_ctx; /* SCTX where the command was defined */ +- # ifdef FEAT_CMDL_COMPL +- char_u *uc_compl_arg; /* completion argument if any */ +- # endif +- # endif +- } 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]) +- +- static void do_ucmd(exarg_T *eap); +- static void ex_command(exarg_T *eap); +- static void ex_delcommand(exarg_T *eap); +- # ifdef FEAT_CMDL_COMPL +- static char_u *get_user_command_name(int idx); +- # endif +- +- /* Wether a command index indicates a user command. */ +- # define IS_USER_CMDIDX(idx) ((int)(idx) < 0) +- +- #else +- # define ex_command ex_ni +- # define ex_comclear ex_ni +- # define ex_delcommand ex_ni +- /* Wether a command index indicates a user command. */ +- # define IS_USER_CMDIDX(idx) (FALSE) +- #endif +- + #ifdef FEAT_EVAL + static char_u *do_one_cmd(char_u **, int, struct condstack *, char_u *(*fgetline)(int, void *, int), void *cookie); + #else +--- 19,24 ---- +*************** +*** 300,309 **** + static void close_redir(void); + static void ex_mkrc(exarg_T *eap); + static void ex_mark(exarg_T *eap); +- #ifdef FEAT_USR_CMDS +- static char *uc_fun_cmd(void); +- static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *compl); +- #endif + static void ex_startinsert(exarg_T *eap); + static void ex_stopinsert(exarg_T *eap); + #ifdef FEAT_FIND_ID +--- 258,263 ---- +*************** +*** 1929,1949 **** + ) ? find_command(&ea, NULL) : ea.cmd; + } + +- #ifdef FEAT_USR_CMDS + if (p == NULL) + { + if (!ea.skip) + errormsg = _("E464: Ambiguous use of user-defined command"); + goto doend; + } +! /* Check for wrong commands. */ + if (*p == '!' && ea.cmd[1] == 0151 && ea.cmd[0] == 78 + && !IS_USER_CMDIDX(ea.cmdidx)) + { + errormsg = uc_fun_cmd(); + goto doend; + } +! #endif + if (ea.cmdidx == CMD_SIZE) + { + if (!ea.skip) +--- 1883,1902 ---- + ) ? find_command(&ea, NULL) : ea.cmd; + } + + if (p == NULL) + { + if (!ea.skip) + errormsg = _("E464: Ambiguous use of user-defined command"); + goto doend; + } +! // Check for wrong commands. + if (*p == '!' && ea.cmd[1] == 0151 && ea.cmd[0] == 78 + && !IS_USER_CMDIDX(ea.cmdidx)) + { + errormsg = uc_fun_cmd(); + goto doend; + } +! + if (ea.cmdidx == CMD_SIZE) + { + if (!ea.skip) +*************** +*** 2508,2514 **** + * 7. Execute the command. + */ + +- #ifdef FEAT_USR_CMDS + if (IS_USER_CMDIDX(ea.cmdidx)) + { + /* +--- 2461,2466 ---- +*************** +*** 2517,2526 **** + do_ucmd(&ea); + } + else +- #endif + { + /* +! * Call the function to execute the command. + */ + ea.errmsg = NULL; + (cmdnames[ea.cmdidx].cmd_func)(&ea); +--- 2469,2477 ---- + do_ucmd(&ea); + } + else + { + /* +! * Call the function to execute the builtin command. + */ + ea.errmsg = NULL; + (cmdnames[ea.cmdidx].cmd_func)(&ea); +*************** +*** 3235,3252 **** + break; + } + +! #ifdef FEAT_USR_CMDS +! /* Look for a user defined command as a last resort. Let ":Print" be +! * overruled by a user defined command. */ + if ((eap->cmdidx == CMD_SIZE || eap->cmdidx == CMD_Print) + && *eap->cmd >= 'A' && *eap->cmd <= 'Z') + { +! /* User defined commands may contain digits. */ + while (ASCII_ISALNUM(*p)) + ++p; + p = find_ucmd(eap, p, full, NULL, NULL); + } +- #endif + if (p == eap->cmd) + eap->cmdidx = CMD_SIZE; + } +--- 3186,3201 ---- + break; + } + +! // Look for a user defined command as a last resort. Let ":Print" be +! // overruled by a user defined command. + if ((eap->cmdidx == CMD_SIZE || eap->cmdidx == CMD_Print) + && *eap->cmd >= 'A' && *eap->cmd <= 'Z') + { +! // User defined commands may contain digits. + while (ASCII_ISALNUM(*p)) + ++p; + p = find_ucmd(eap, p, full, NULL, NULL); + } + if (p == eap->cmd) + eap->cmdidx = CMD_SIZE; + } +*************** +*** 3254,3377 **** + return p; + } + +- #ifdef FEAT_USR_CMDS +- /* +- * Search for a user command that matches "eap->cmd". +- * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". +- * Return a pointer to just after the command. +- * Return NULL if there is no matching command. +- */ +- static char_u * +- find_ucmd( +- exarg_T *eap, +- char_u *p, /* end of the command (possibly including count) */ +- int *full, /* set to TRUE for a full match */ +- expand_T *xp, /* used for completion, NULL otherwise */ +- int *compl) /* completion flags or NULL */ +- { +- int len = (int)(p - eap->cmd); +- int j, k, matchlen = 0; +- ucmd_T *uc; +- int found = FALSE; +- int possible = FALSE; +- char_u *cp, *np; /* Point into typed cmd and test name */ +- garray_T *gap; +- int amb_local = FALSE; /* Found ambiguous buffer-local command, +- only full match global is accepted. */ +- +- /* +- * Look for buffer-local user commands first, then global ones. +- */ +- gap = &curbuf->b_ucmds; +- for (;;) +- { +- for (j = 0; j < gap->ga_len; ++j) +- { +- uc = USER_CMD_GA(gap, j); +- cp = eap->cmd; +- np = uc->uc_name; +- k = 0; +- while (k < len && *np != NUL && *cp++ == *np++) +- k++; +- if (k == len || (*np == NUL && vim_isdigit(eap->cmd[k]))) +- { +- /* If finding a second match, the command is ambiguous. But +- * not if a buffer-local command wasn't a full match and a +- * global command is a full match. */ +- if (k == len && found && *np != NUL) +- { +- if (gap == &ucmds) +- return NULL; +- amb_local = TRUE; +- } +- +- if (!found || (k == len && *np == NUL)) +- { +- /* If we matched up to a digit, then there could +- * be another command including the digit that we +- * should use instead. +- */ +- if (k == len) +- found = TRUE; +- else +- possible = TRUE; +- +- if (gap == &ucmds) +- eap->cmdidx = CMD_USER; +- else +- eap->cmdidx = CMD_USER_BUF; +- eap->argt = (long)uc->uc_argt; +- eap->useridx = j; +- eap->addr_type = uc->uc_addr_type; +- +- # ifdef FEAT_CMDL_COMPL +- if (compl != NULL) +- *compl = uc->uc_compl; +- # ifdef FEAT_EVAL +- if (xp != NULL) +- { +- xp->xp_arg = uc->uc_compl_arg; +- xp->xp_script_ctx = uc->uc_script_ctx; +- xp->xp_script_ctx.sc_lnum += sourcing_lnum; +- } +- # endif +- # endif +- /* Do not search for further abbreviations +- * if this is an exact match. */ +- matchlen = k; +- if (k == len && *np == NUL) +- { +- if (full != NULL) +- *full = TRUE; +- amb_local = FALSE; +- break; +- } +- } +- } +- } +- +- /* Stop if we found a full match or searched all. */ +- if (j < gap->ga_len || gap == &ucmds) +- break; +- gap = &ucmds; +- } +- +- /* Only found ambiguous matches. */ +- if (amb_local) +- { +- if (xp != NULL) +- xp->xp_context = EXPAND_UNSUCCESSFUL; +- return NULL; +- } +- +- /* The match we found may be followed immediately by a number. Move "p" +- * back to point to it. */ +- if (found || possible) +- return p + (matchlen - len); +- return p; +- } +- #endif +- + #if defined(FEAT_EVAL) || defined(PROTO) + static struct cmdmod + { +--- 3203,3208 ---- +*************** +*** 3483,3492 **** + char_u *cmd, *arg; + int len = 0; + exarg_T ea; +- #if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) +- int compl = EXPAND_NOTHING; +- #endif + #ifdef FEAT_CMDL_COMPL + int delim; + #endif + int forceit = FALSE; +--- 3314,3321 ---- + char_u *cmd, *arg; + int len = 0; + exarg_T ea; + #ifdef FEAT_CMDL_COMPL ++ int compl = EXPAND_NOTHING; + int delim; + #endif + int forceit = FALSE; +*************** +*** 3572,3582 **** + (size_t)len) == 0) + break; + +- #ifdef FEAT_USR_CMDS + if (cmd[0] >= 'A' && cmd[0] <= 'Z') +! while (ASCII_ISALNUM(*p) || *p == '*') /* Allow * wild card */ + ++p; +- #endif + } + + /* +--- 3401,3409 ---- + (size_t)len) == 0) + break; + + if (cmd[0] >= 'A' && cmd[0] <= 'Z') +! while (ASCII_ISALNUM(*p) || *p == '*') // Allow * wild card + ++p; + } + + /* +*************** +*** 3593,3613 **** + ea.cmdidx = CMD_substitute; + p = cmd + 1; + } +- #ifdef FEAT_USR_CMDS + else if (cmd[0] >= 'A' && cmd[0] <= 'Z') + { + ea.cmd = cmd; + p = find_ucmd(&ea, p, NULL, xp, +! # if defined(FEAT_CMDL_COMPL) + &compl +! # else + NULL +! # endif + ); + if (p == NULL) +! ea.cmdidx = CMD_SIZE; /* ambiguous user command */ + } +- #endif + } + if (ea.cmdidx == CMD_SIZE) + { +--- 3420,3438 ---- + ea.cmdidx = CMD_substitute; + p = cmd + 1; + } + else if (cmd[0] >= 'A' && cmd[0] <= 'Z') + { + ea.cmd = cmd; + p = find_ucmd(&ea, p, NULL, xp, +! #if defined(FEAT_CMDL_COMPL) + &compl +! #else + NULL +! #endif + ); + if (p == NULL) +! ea.cmdidx = CMD_SIZE; // ambiguous user command + } + } + if (ea.cmdidx == CMD_SIZE) + { +*************** +*** 3828,3834 **** + { + xp->xp_context = EXPAND_ENV_VARS; + ++xp->xp_pattern; +! #if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) + /* Avoid that the assignment uses EXPAND_FILES again. */ + if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) + compl = EXPAND_ENV_VARS; +--- 3653,3659 ---- + { + xp->xp_context = EXPAND_ENV_VARS; + ++xp->xp_pattern; +! #if defined(FEAT_CMDL_COMPL) + /* Avoid that the assignment uses EXPAND_FILES again. */ + if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) + compl = EXPAND_ENV_VARS; +*************** +*** 3944,4011 **** + * All completion for the +cmdline_compl feature goes here. + */ + +- # ifdef FEAT_USR_CMDS + case CMD_command: +! /* Check for attributes */ +! while (*arg == '-') +! { +! arg++; /* Skip "-" */ +! p = skiptowhite(arg); +! if (*p == NUL) +! { +! /* Cursor is still in the attribute */ +! p = vim_strchr(arg, '='); +! if (p == NULL) +! { +! /* No "=", so complete attribute names */ +! xp->xp_context = EXPAND_USER_CMD_FLAGS; +! xp->xp_pattern = arg; +! return NULL; +! } +! +! /* For the -complete, -nargs and -addr attributes, we complete +! * their arguments as well. +! */ +! if (STRNICMP(arg, "complete", p - arg) == 0) +! { +! xp->xp_context = EXPAND_USER_COMPLETE; +! xp->xp_pattern = p + 1; +! return NULL; +! } +! else if (STRNICMP(arg, "nargs", p - arg) == 0) +! { +! xp->xp_context = EXPAND_USER_NARGS; +! xp->xp_pattern = p + 1; +! return NULL; +! } +! else if (STRNICMP(arg, "addr", p - arg) == 0) +! { +! xp->xp_context = EXPAND_USER_ADDR_TYPE; +! xp->xp_pattern = p + 1; +! return NULL; +! } +! return NULL; +! } +! arg = skipwhite(p); +! } +! +! /* After the attributes comes the new command name */ +! p = skiptowhite(arg); +! if (*p == NUL) +! { +! xp->xp_context = EXPAND_USER_COMMANDS; +! xp->xp_pattern = arg; +! break; +! } +! +! /* And finally comes a normal command */ +! return skipwhite(p); + + case CMD_delcommand: + xp->xp_context = EXPAND_USER_COMMANDS; + xp->xp_pattern = arg; + break; +- # endif + + case CMD_global: + case CMD_vglobal: +--- 3769,3781 ---- + * All completion for the +cmdline_compl feature goes here. + */ + + case CMD_command: +! return set_context_in_user_cmd(xp, arg); + + case CMD_delcommand: + xp->xp_context = EXPAND_USER_COMMANDS; + xp->xp_pattern = arg; + break; + + case CMD_global: + case CMD_vglobal: +*************** +*** 4186,4217 **** + xp->xp_context = EXPAND_BUFFERS; + xp->xp_pattern = arg; + break; +! #ifdef FEAT_USR_CMDS + case CMD_USER: + case CMD_USER_BUF: + if (compl != EXPAND_NOTHING) + { +! /* XFILE: file names are handled above */ + if (!(ea.argt & XFILE)) + { +! # ifdef FEAT_MENU + if (compl == EXPAND_MENUS) + return set_context_in_menu_cmd(xp, cmd, arg, forceit); +! # endif + if (compl == EXPAND_COMMANDS) + return arg; + if (compl == EXPAND_MAPPINGS) + return set_context_in_map_cmd(xp, (char_u *)"map", + arg, forceit, FALSE, FALSE, CMD_map); +! /* Find start of last argument. */ + p = arg; + while (*p) + { + if (*p == ' ') +! /* argument starts after a space */ + arg = p + 1; + else if (*p == '\\' && *(p + 1) != NUL) +! ++p; /* skip over escaped character */ + MB_PTR_ADV(p); + } + xp->xp_pattern = arg; +--- 3956,3987 ---- + xp->xp_context = EXPAND_BUFFERS; + xp->xp_pattern = arg; + break; +! + case CMD_USER: + case CMD_USER_BUF: + if (compl != EXPAND_NOTHING) + { +! // XFILE: file names are handled above + if (!(ea.argt & XFILE)) + { +! #ifdef FEAT_MENU + if (compl == EXPAND_MENUS) + return set_context_in_menu_cmd(xp, cmd, arg, forceit); +! #endif + if (compl == EXPAND_COMMANDS) + return arg; + if (compl == EXPAND_MAPPINGS) + return set_context_in_map_cmd(xp, (char_u *)"map", + arg, forceit, FALSE, FALSE, CMD_map); +! // Find start of last argument. + p = arg; + while (*p) + { + if (*p == ' ') +! // argument starts after a space + arg = p + 1; + else if (*p == '\\' && *(p + 1) != NUL) +! ++p; // skip over escaped character + MB_PTR_ADV(p); + } + xp->xp_pattern = arg; +*************** +*** 4219,4225 **** + xp->xp_context = compl; + } + break; +! #endif + case CMD_map: case CMD_noremap: + case CMD_nmap: case CMD_nnoremap: + case CMD_vmap: case CMD_vnoremap: +--- 3989,3995 ---- + xp->xp_context = compl; + } + break; +! + case CMD_map: case CMD_noremap: + case CMD_nmap: case CMD_nnoremap: + case CMD_vmap: case CMD_vnoremap: +*************** +*** 5771,5777 **** + return OK; + } + +! #ifdef FEAT_CMDL_COMPL + /* + * Function given to ExpandGeneric() to obtain the list of command names. + */ +--- 5541,5547 ---- + return OK; + } + +! #if defined(FEAT_CMDL_COMPL) || defined(PROTO) + /* + * Function given to ExpandGeneric() to obtain the list of command names. + */ +*************** +*** 5779,7218 **** + get_command_name(expand_T *xp UNUSED, int idx) + { + if (idx >= (int)CMD_SIZE) +- # ifdef FEAT_USR_CMDS + return get_user_command_name(idx); +- # else +- return NULL; +- # endif + return cmdnames[idx].cmd_name; + } + #endif + +- #if defined(FEAT_USR_CMDS) || defined(PROTO) +- static int +- uc_add_command( +- char_u *name, +- size_t name_len, +- char_u *rep, +- long argt, +- long def, +- int flags, +- int compl, +- char_u *compl_arg, +- int addr_type, +- int force) +- { +- ucmd_T *cmd = NULL; +- char_u *p; +- int i; +- int cmp = 1; +- char_u *rep_buf = NULL; +- garray_T *gap; +- +- replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); +- if (rep_buf == NULL) +- { +- /* Can't replace termcodes - try using the string as is */ +- rep_buf = vim_strsave(rep); +- +- /* Give up if out of memory */ +- if (rep_buf == NULL) +- return FAIL; +- } +- +- /* get address of growarray: global or in curbuf */ +- if (flags & UC_BUFFER) +- { +- gap = &curbuf->b_ucmds; +- if (gap->ga_itemsize == 0) +- ga_init2(gap, (int)sizeof(ucmd_T), 4); +- } +- else +- gap = &ucmds; +- +- /* Search for the command in the already defined commands. */ +- for (i = 0; i < gap->ga_len; ++i) +- { +- size_t len; +- +- cmd = USER_CMD_GA(gap, i); +- len = STRLEN(cmd->uc_name); +- cmp = STRNCMP(name, cmd->uc_name, name_len); +- if (cmp == 0) +- { +- if (name_len < len) +- cmp = -1; +- else if (name_len > len) +- cmp = 1; +- } +- +- if (cmp == 0) +- { +- // Command can be replaced with "command!" and when sourcing the +- // same script again, but only once. +- if (!force && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid +- || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq)) +- { +- semsg(_("E174: Command already exists: add ! to replace it: %s"), +- name); +- goto fail; +- } +- +- VIM_CLEAR(cmd->uc_rep); +- #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- VIM_CLEAR(cmd->uc_compl_arg); +- #endif +- break; +- } +- +- /* Stop as soon as we pass the name to add */ +- if (cmp < 0) +- break; +- } +- +- /* Extend the array unless we're replacing an existing command */ +- if (cmp != 0) +- { +- if (ga_grow(gap, 1) != OK) +- goto fail; +- if ((p = vim_strnsave(name, (int)name_len)) == NULL) +- goto fail; +- +- cmd = USER_CMD_GA(gap, i); +- mch_memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T)); +- +- ++gap->ga_len; +- +- cmd->uc_name = p; +- } +- +- cmd->uc_rep = rep_buf; +- cmd->uc_argt = argt; +- cmd->uc_def = def; +- cmd->uc_compl = compl; +- #ifdef FEAT_EVAL +- cmd->uc_script_ctx = current_sctx; +- cmd->uc_script_ctx.sc_lnum += sourcing_lnum; +- # ifdef FEAT_CMDL_COMPL +- cmd->uc_compl_arg = compl_arg; +- # endif +- #endif +- cmd->uc_addr_type = addr_type; +- +- return OK; +- +- fail: +- vim_free(rep_buf); +- #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- vim_free(compl_arg); +- #endif +- return FAIL; +- } +- #endif +- +- #if defined(FEAT_USR_CMDS) +- static struct +- { +- int expand; +- char *name; +- char *shortname; +- } addr_type_complete[] = +- { +- {ADDR_ARGUMENTS, "arguments", "arg"}, +- {ADDR_LINES, "lines", "line"}, +- {ADDR_LOADED_BUFFERS, "loaded_buffers", "load"}, +- {ADDR_TABS, "tabs", "tab"}, +- {ADDR_BUFFERS, "buffers", "buf"}, +- {ADDR_WINDOWS, "windows", "win"}, +- {ADDR_QUICKFIX, "quickfix", "qf"}, +- {ADDR_OTHER, "other", "?"}, +- {-1, NULL, NULL} +- }; +- #endif +- +- #if defined(FEAT_USR_CMDS) || defined(FEAT_EVAL) || defined(PROTO) +- /* +- * List of names for completion for ":command" with the EXPAND_ flag. +- * Must be alphabetical for completion. +- */ +- static struct +- { +- int expand; +- char *name; +- } command_complete[] = +- { +- {EXPAND_ARGLIST, "arglist"}, +- {EXPAND_AUGROUP, "augroup"}, +- {EXPAND_BEHAVE, "behave"}, +- {EXPAND_BUFFERS, "buffer"}, +- {EXPAND_COLORS, "color"}, +- {EXPAND_COMMANDS, "command"}, +- {EXPAND_COMPILER, "compiler"}, +- #if defined(FEAT_CSCOPE) +- {EXPAND_CSCOPE, "cscope"}, +- #endif +- #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- {EXPAND_USER_DEFINED, "custom"}, +- {EXPAND_USER_LIST, "customlist"}, +- #endif +- {EXPAND_DIRECTORIES, "dir"}, +- {EXPAND_ENV_VARS, "environment"}, +- {EXPAND_EVENTS, "event"}, +- {EXPAND_EXPRESSION, "expression"}, +- {EXPAND_FILES, "file"}, +- {EXPAND_FILES_IN_PATH, "file_in_path"}, +- {EXPAND_FILETYPE, "filetype"}, +- {EXPAND_FUNCTIONS, "function"}, +- {EXPAND_HELP, "help"}, +- {EXPAND_HIGHLIGHT, "highlight"}, +- #if defined(FEAT_CMDHIST) +- {EXPAND_HISTORY, "history"}, +- #endif +- #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) +- {EXPAND_LOCALES, "locale"}, +- #endif +- {EXPAND_MAPCLEAR, "mapclear"}, +- {EXPAND_MAPPINGS, "mapping"}, +- {EXPAND_MENUS, "menu"}, +- {EXPAND_MESSAGES, "messages"}, +- {EXPAND_OWNSYNTAX, "syntax"}, +- #if defined(FEAT_PROFILE) +- {EXPAND_SYNTIME, "syntime"}, +- #endif +- {EXPAND_SETTINGS, "option"}, +- {EXPAND_PACKADD, "packadd"}, +- {EXPAND_SHELLCMD, "shellcmd"}, +- #if defined(FEAT_SIGNS) +- {EXPAND_SIGN, "sign"}, +- #endif +- {EXPAND_TAGS, "tag"}, +- {EXPAND_TAGS_LISTFILES, "tag_listfiles"}, +- {EXPAND_USER, "user"}, +- {EXPAND_USER_VARS, "var"}, +- {0, NULL} +- }; +- #endif +- +- #if defined(FEAT_USR_CMDS) || defined(PROTO) +- static void +- uc_list(char_u *name, size_t name_len) +- { +- int i, j; +- int found = FALSE; +- ucmd_T *cmd; +- int len; +- int over; +- long a; +- garray_T *gap; +- +- gap = &curbuf->b_ucmds; +- for (;;) +- { +- for (i = 0; i < gap->ga_len; ++i) +- { +- cmd = USER_CMD_GA(gap, i); +- a = (long)cmd->uc_argt; +- +- /* Skip commands which don't match the requested prefix and +- * commands filtered out. */ +- if (STRNCMP(name, cmd->uc_name, name_len) != 0 +- || message_filtered(cmd->uc_name)) +- continue; +- +- /* Put out the title first time */ +- if (!found) +- msg_puts_title(_("\n Name Args Address Complete Definition")); +- found = TRUE; +- msg_putchar('\n'); +- if (got_int) +- break; +- +- // Special cases +- len = 4; +- if (a & BANG) +- { +- msg_putchar('!'); +- --len; +- } +- if (a & REGSTR) +- { +- msg_putchar('"'); +- --len; +- } +- if (gap != &ucmds) +- { +- msg_putchar('b'); +- --len; +- } +- if (a & TRLBAR) +- { +- msg_putchar('|'); +- --len; +- } +- while (len-- > 0) +- msg_putchar(' '); +- +- msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D)); +- len = (int)STRLEN(cmd->uc_name) + 4; +- +- do { +- msg_putchar(' '); +- ++len; +- } while (len < 22); +- +- // "over" is how much longer the name is than the column width for +- // the name, we'll try to align what comes after. +- over = len - 22; +- len = 0; +- +- // Arguments +- switch ((int)(a & (EXTRA|NOSPC|NEEDARG))) +- { +- case 0: IObuff[len++] = '0'; break; +- case (EXTRA): IObuff[len++] = '*'; break; +- case (EXTRA|NOSPC): IObuff[len++] = '?'; break; +- case (EXTRA|NEEDARG): IObuff[len++] = '+'; break; +- case (EXTRA|NOSPC|NEEDARG): IObuff[len++] = '1'; break; +- } +- +- do { +- IObuff[len++] = ' '; +- } while (len < 5 - over); +- +- // Address / Range +- if (a & (RANGE|COUNT)) +- { +- if (a & COUNT) +- { +- // -count=N +- sprintf((char *)IObuff + len, "%ldc", cmd->uc_def); +- len += (int)STRLEN(IObuff + len); +- } +- else if (a & DFLALL) +- IObuff[len++] = '%'; +- else if (cmd->uc_def >= 0) +- { +- // -range=N +- sprintf((char *)IObuff + len, "%ld", cmd->uc_def); +- len += (int)STRLEN(IObuff + len); +- } +- else +- IObuff[len++] = '.'; +- } +- +- do { +- IObuff[len++] = ' '; +- } while (len < 8 - over); +- +- // Address Type +- for (j = 0; addr_type_complete[j].expand != -1; ++j) +- if (addr_type_complete[j].expand != ADDR_LINES +- && addr_type_complete[j].expand == cmd->uc_addr_type) +- { +- STRCPY(IObuff + len, addr_type_complete[j].shortname); +- len += (int)STRLEN(IObuff + len); +- break; +- } +- +- do { +- IObuff[len++] = ' '; +- } while (len < 13 - over); +- +- // Completion +- for (j = 0; command_complete[j].expand != 0; ++j) +- if (command_complete[j].expand == cmd->uc_compl) +- { +- STRCPY(IObuff + len, command_complete[j].name); +- len += (int)STRLEN(IObuff + len); +- break; +- } +- +- do { +- IObuff[len++] = ' '; +- } while (len < 25 - over); +- +- IObuff[len] = '\0'; +- msg_outtrans(IObuff); +- +- msg_outtrans_special(cmd->uc_rep, FALSE, +- name_len == 0 ? Columns - 47 : 0); +- #ifdef FEAT_EVAL +- if (p_verbose > 0) +- last_set_msg(cmd->uc_script_ctx); +- #endif +- out_flush(); +- ui_breakcheck(); +- if (got_int) +- break; +- } +- if (gap == &ucmds || i < gap->ga_len) +- break; +- gap = &ucmds; +- } +- +- if (!found) +- msg(_("No user-defined commands found")); +- } +- +- static char * +- uc_fun_cmd(void) +- { +- static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4, +- 0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60, +- 0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2, +- 0xb9, 0x7f, 0}; +- int i; +- +- for (i = 0; fcmd[i]; ++i) +- IObuff[i] = fcmd[i] - 0x40; +- IObuff[i] = 0; +- return (char *)IObuff; +- } +- +- static int +- uc_scan_attr( +- char_u *attr, +- size_t len, +- long *argt, +- long *def, +- int *flags, +- int *compl, +- char_u **compl_arg, +- int *addr_type_arg) +- { +- char_u *p; +- +- if (len == 0) +- { +- emsg(_("E175: No attribute specified")); +- return FAIL; +- } +- +- /* First, try the simple attributes (no arguments) */ +- if (STRNICMP(attr, "bang", len) == 0) +- *argt |= BANG; +- else if (STRNICMP(attr, "buffer", len) == 0) +- *flags |= UC_BUFFER; +- else if (STRNICMP(attr, "register", len) == 0) +- *argt |= REGSTR; +- else if (STRNICMP(attr, "bar", len) == 0) +- *argt |= TRLBAR; +- else +- { +- int i; +- char_u *val = NULL; +- size_t vallen = 0; +- size_t attrlen = len; +- +- /* Look for the attribute name - which is the part before any '=' */ +- for (i = 0; i < (int)len; ++i) +- { +- if (attr[i] == '=') +- { +- val = &attr[i + 1]; +- vallen = len - i - 1; +- attrlen = i; +- break; +- } +- } +- +- if (STRNICMP(attr, "nargs", attrlen) == 0) +- { +- if (vallen == 1) +- { +- if (*val == '0') +- /* Do nothing - this is the default */; +- else if (*val == '1') +- *argt |= (EXTRA | NOSPC | NEEDARG); +- else if (*val == '*') +- *argt |= EXTRA; +- else if (*val == '?') +- *argt |= (EXTRA | NOSPC); +- else if (*val == '+') +- *argt |= (EXTRA | NEEDARG); +- else +- goto wrong_nargs; +- } +- else +- { +- wrong_nargs: +- emsg(_("E176: Invalid number of arguments")); +- return FAIL; +- } +- } +- else if (STRNICMP(attr, "range", attrlen) == 0) +- { +- *argt |= RANGE; +- if (vallen == 1 && *val == '%') +- *argt |= DFLALL; +- else if (val != NULL) +- { +- p = val; +- if (*def >= 0) +- { +- two_count: +- emsg(_("E177: Count cannot be specified twice")); +- return FAIL; +- } +- +- *def = getdigits(&p); +- *argt |= (ZEROR | NOTADR); +- +- if (p != val + vallen || vallen == 0) +- { +- invalid_count: +- emsg(_("E178: Invalid default value for count")); +- return FAIL; +- } +- } +- } +- else if (STRNICMP(attr, "count", attrlen) == 0) +- { +- *argt |= (COUNT | ZEROR | RANGE | NOTADR); +- +- if (val != NULL) +- { +- p = val; +- if (*def >= 0) +- goto two_count; +- +- *def = getdigits(&p); +- +- if (p != val + vallen) +- goto invalid_count; +- } +- +- if (*def < 0) +- *def = 0; +- } +- else if (STRNICMP(attr, "complete", attrlen) == 0) +- { +- if (val == NULL) +- { +- emsg(_("E179: argument required for -complete")); +- return FAIL; +- } +- +- if (parse_compl_arg(val, (int)vallen, compl, argt, compl_arg) +- == FAIL) +- return FAIL; +- } +- else if (STRNICMP(attr, "addr", attrlen) == 0) +- { +- *argt |= RANGE; +- if (val == NULL) +- { +- emsg(_("E179: argument required for -addr")); +- return FAIL; +- } +- if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) +- == FAIL) +- return FAIL; +- if (addr_type_arg != ADDR_LINES) +- *argt |= (ZEROR | NOTADR) ; +- } +- else +- { +- char_u ch = attr[len]; +- attr[len] = '\0'; +- semsg(_("E181: Invalid attribute: %s"), attr); +- attr[len] = ch; +- return FAIL; +- } +- } +- +- return OK; +- } +- +- /* +- * ":command ..." +- */ +- static void +- ex_command(exarg_T *eap) +- { +- char_u *name; +- char_u *end; +- char_u *p; +- long argt = 0; +- long def = -1; +- int flags = 0; +- int compl = EXPAND_NOTHING; +- char_u *compl_arg = NULL; +- int addr_type_arg = ADDR_LINES; +- int has_attr = (eap->arg[0] == '-'); +- int name_len; +- +- p = eap->arg; +- +- /* Check for attributes */ +- while (*p == '-') +- { +- ++p; +- end = skiptowhite(p); +- if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, +- &compl_arg, &addr_type_arg) +- == FAIL) +- return; +- p = skipwhite(end); +- } +- +- /* Get the name (if any) and skip to the following argument */ +- name = p; +- if (ASCII_ISALPHA(*p)) +- while (ASCII_ISALNUM(*p)) +- ++p; +- if (!ends_excmd(*p) && !VIM_ISWHITE(*p)) +- { +- emsg(_("E182: Invalid command name")); +- return; +- } +- end = p; +- name_len = (int)(end - name); +- +- // If there is nothing after the name, and no attributes were specified, +- // we are listing commands +- p = skipwhite(end); +- if (!has_attr && ends_excmd(*p)) +- { +- uc_list(name, end - name); +- } +- else if (!ASCII_ISUPPER(*name)) +- { +- emsg(_("E183: User defined commands must start with an uppercase letter")); +- return; +- } +- else if ((name_len == 1 && *name == 'X') +- || (name_len <= 4 +- && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0)) +- { +- emsg(_("E841: Reserved name, cannot be used for user defined command")); +- return; +- } +- else +- uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, +- addr_type_arg, eap->forceit); +- } +- +- /* +- * ":comclear" +- * Clear all user commands, global and for current buffer. +- */ +- void +- ex_comclear(exarg_T *eap UNUSED) +- { +- uc_clear(&ucmds); +- uc_clear(&curbuf->b_ucmds); +- } +- +- /* +- * Clear all user commands for "gap". +- */ +- void +- uc_clear(garray_T *gap) +- { +- int i; +- ucmd_T *cmd; +- +- for (i = 0; i < gap->ga_len; ++i) +- { +- cmd = USER_CMD_GA(gap, i); +- vim_free(cmd->uc_name); +- vim_free(cmd->uc_rep); +- # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- vim_free(cmd->uc_compl_arg); +- # endif +- } +- ga_clear(gap); +- } +- +- static void +- ex_delcommand(exarg_T *eap) +- { +- int i = 0; +- ucmd_T *cmd = NULL; +- int cmp = -1; +- garray_T *gap; +- +- gap = &curbuf->b_ucmds; +- for (;;) +- { +- for (i = 0; i < gap->ga_len; ++i) +- { +- cmd = USER_CMD_GA(gap, i); +- cmp = STRCMP(eap->arg, cmd->uc_name); +- if (cmp <= 0) +- break; +- } +- if (gap == &ucmds || cmp == 0) +- break; +- gap = &ucmds; +- } +- +- if (cmp != 0) +- { +- semsg(_("E184: No such user-defined command: %s"), eap->arg); +- return; +- } +- +- vim_free(cmd->uc_name); +- vim_free(cmd->uc_rep); +- # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- vim_free(cmd->uc_compl_arg); +- # endif +- +- --gap->ga_len; +- +- if (i < gap->ga_len) +- mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T)); +- } +- +- /* +- * split and quote args for <f-args> +- */ +- static char_u * +- uc_split_args(char_u *arg, size_t *lenp) +- { +- char_u *buf; +- char_u *p; +- char_u *q; +- int len; +- +- /* Precalculate length */ +- p = arg; +- len = 2; /* Initial and final quotes */ +- +- while (*p) +- { +- if (p[0] == '\\' && p[1] == '\\') +- { +- len += 2; +- p += 2; +- } +- else if (p[0] == '\\' && VIM_ISWHITE(p[1])) +- { +- len += 1; +- p += 2; +- } +- else if (*p == '\\' || *p == '"') +- { +- len += 2; +- p += 1; +- } +- else if (VIM_ISWHITE(*p)) +- { +- p = skipwhite(p); +- if (*p == NUL) +- break; +- len += 3; /* "," */ +- } +- else +- { +- int charlen = (*mb_ptr2len)(p); +- +- len += charlen; +- p += charlen; +- } +- } +- +- buf = alloc(len + 1); +- if (buf == NULL) +- { +- *lenp = 0; +- return buf; +- } +- +- p = arg; +- q = buf; +- *q++ = '"'; +- while (*p) +- { +- if (p[0] == '\\' && p[1] == '\\') +- { +- *q++ = '\\'; +- *q++ = '\\'; +- p += 2; +- } +- else if (p[0] == '\\' && VIM_ISWHITE(p[1])) +- { +- *q++ = p[1]; +- p += 2; +- } +- else if (*p == '\\' || *p == '"') +- { +- *q++ = '\\'; +- *q++ = *p++; +- } +- else if (VIM_ISWHITE(*p)) +- { +- p = skipwhite(p); +- if (*p == NUL) +- break; +- *q++ = '"'; +- *q++ = ','; +- *q++ = '"'; +- } +- else +- { +- MB_COPY_CHAR(p, q); +- } +- } +- *q++ = '"'; +- *q = 0; +- +- *lenp = len; +- return buf; +- } +- +- static size_t +- add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods) +- { +- size_t result; +- +- result = STRLEN(mod_str); +- if (*multi_mods) +- result += 1; +- if (buf != NULL) +- { +- if (*multi_mods) +- STRCAT(buf, " "); +- STRCAT(buf, mod_str); +- } +- +- *multi_mods = 1; +- +- return result; +- } +- +- /* +- * Check for a <> code in a user command. +- * "code" points to the '<'. "len" the length of the <> (inclusive). +- * "buf" is where the result is to be added. +- * "split_buf" points to a buffer used for splitting, caller should free it. +- * "split_len" is the length of what "split_buf" contains. +- * Returns the length of the replacement, which has been added to "buf". +- * Returns -1 if there was no match, and only the "<" has been copied. +- */ +- static size_t +- uc_check_code( +- char_u *code, +- size_t len, +- char_u *buf, +- ucmd_T *cmd, /* the user command we're expanding */ +- exarg_T *eap, /* ex arguments */ +- char_u **split_buf, +- size_t *split_len) +- { +- size_t result = 0; +- char_u *p = code + 1; +- size_t l = len - 2; +- int quote = 0; +- enum { +- ct_ARGS, +- ct_BANG, +- ct_COUNT, +- ct_LINE1, +- ct_LINE2, +- ct_RANGE, +- ct_MODS, +- ct_REGISTER, +- ct_LT, +- ct_NONE +- } type = ct_NONE; +- +- if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') +- { +- quote = (*p == 'q' || *p == 'Q') ? 1 : 2; +- p += 2; +- l -= 2; +- } +- +- ++l; +- if (l <= 1) +- type = ct_NONE; +- else if (STRNICMP(p, "args>", l) == 0) +- type = ct_ARGS; +- else if (STRNICMP(p, "bang>", l) == 0) +- type = ct_BANG; +- else if (STRNICMP(p, "count>", l) == 0) +- type = ct_COUNT; +- else if (STRNICMP(p, "line1>", l) == 0) +- type = ct_LINE1; +- else if (STRNICMP(p, "line2>", l) == 0) +- type = ct_LINE2; +- else if (STRNICMP(p, "range>", l) == 0) +- type = ct_RANGE; +- else if (STRNICMP(p, "lt>", l) == 0) +- type = ct_LT; +- else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0) +- type = ct_REGISTER; +- else if (STRNICMP(p, "mods>", l) == 0) +- type = ct_MODS; +- +- switch (type) +- { +- case ct_ARGS: +- /* Simple case first */ +- if (*eap->arg == NUL) +- { +- if (quote == 1) +- { +- result = 2; +- if (buf != NULL) +- STRCPY(buf, "''"); +- } +- else +- result = 0; +- break; +- } +- +- /* When specified there is a single argument don't split it. +- * Works for ":Cmd %" when % is "a b c". */ +- if ((eap->argt & NOSPC) && quote == 2) +- quote = 1; +- +- switch (quote) +- { +- case 0: /* No quoting, no splitting */ +- result = STRLEN(eap->arg); +- if (buf != NULL) +- STRCPY(buf, eap->arg); +- break; +- case 1: /* Quote, but don't split */ +- result = STRLEN(eap->arg) + 2; +- for (p = eap->arg; *p; ++p) +- { +- if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) +- /* DBCS can contain \ in a trail byte, skip the +- * double-byte character. */ +- ++p; +- else +- if (*p == '\\' || *p == '"') +- ++result; +- } +- +- if (buf != NULL) +- { +- *buf++ = '"'; +- for (p = eap->arg; *p; ++p) +- { +- if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) +- /* DBCS can contain \ in a trail byte, copy the +- * double-byte character to avoid escaping. */ +- *buf++ = *p++; +- else +- if (*p == '\\' || *p == '"') +- *buf++ = '\\'; +- *buf++ = *p; +- } +- *buf = '"'; +- } +- +- break; +- case 2: /* Quote and split (<f-args>) */ +- /* This is hard, so only do it once, and cache the result */ +- if (*split_buf == NULL) +- *split_buf = uc_split_args(eap->arg, split_len); +- +- result = *split_len; +- if (buf != NULL && result != 0) +- STRCPY(buf, *split_buf); +- +- break; +- } +- break; +- +- case ct_BANG: +- result = eap->forceit ? 1 : 0; +- if (quote) +- result += 2; +- if (buf != NULL) +- { +- if (quote) +- *buf++ = '"'; +- if (eap->forceit) +- *buf++ = '!'; +- if (quote) +- *buf = '"'; +- } +- break; +- +- case ct_LINE1: +- case ct_LINE2: +- case ct_RANGE: +- case ct_COUNT: +- { +- char num_buf[20]; +- long num = (type == ct_LINE1) ? eap->line1 : +- (type == ct_LINE2) ? eap->line2 : +- (type == ct_RANGE) ? eap->addr_count : +- (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; +- size_t num_len; +- +- sprintf(num_buf, "%ld", num); +- num_len = STRLEN(num_buf); +- result = num_len; +- +- if (quote) +- result += 2; +- +- if (buf != NULL) +- { +- if (quote) +- *buf++ = '"'; +- STRCPY(buf, num_buf); +- buf += num_len; +- if (quote) +- *buf = '"'; +- } +- +- break; +- } +- +- case ct_MODS: +- { +- int multi_mods = 0; +- typedef struct { +- int *varp; +- char *name; +- } mod_entry_T; +- static mod_entry_T mod_entries[] = { +- #ifdef FEAT_BROWSE_CMD +- {&cmdmod.browse, "browse"}, +- #endif +- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) +- {&cmdmod.confirm, "confirm"}, +- #endif +- {&cmdmod.hide, "hide"}, +- {&cmdmod.keepalt, "keepalt"}, +- {&cmdmod.keepjumps, "keepjumps"}, +- {&cmdmod.keepmarks, "keepmarks"}, +- {&cmdmod.keeppatterns, "keeppatterns"}, +- {&cmdmod.lockmarks, "lockmarks"}, +- {&cmdmod.noswapfile, "noswapfile"}, +- {NULL, NULL} +- }; +- int i; +- +- result = quote ? 2 : 0; +- if (buf != NULL) +- { +- if (quote) +- *buf++ = '"'; +- *buf = '\0'; +- } +- +- /* :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); +- +- /* the modifiers that are simple flags */ +- for (i = 0; mod_entries[i].varp != NULL; ++i) +- if (*mod_entries[i].varp) +- result += add_cmd_modifier(buf, mod_entries[i].name, +- &multi_mods); +- +- /* TODO: How to support :noautocmd? */ +- #ifdef HAVE_SANDBOX +- /* TODO: How to support :sandbox?*/ +- #endif +- /* :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: 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 = '"'; +- } +- break; +- } +- +- case ct_REGISTER: +- result = eap->regname ? 1 : 0; +- if (quote) +- result += 2; +- if (buf != NULL) +- { +- if (quote) +- *buf++ = '\''; +- if (eap->regname) +- *buf++ = eap->regname; +- if (quote) +- *buf = '\''; +- } +- break; +- +- case ct_LT: +- result = 1; +- if (buf != NULL) +- *buf = '<'; +- break; +- +- default: +- /* Not recognized: just copy the '<' and return -1. */ +- result = (size_t)-1; +- if (buf != NULL) +- *buf = '<'; +- break; +- } +- +- return result; +- } +- +- static void +- do_ucmd(exarg_T *eap) +- { +- char_u *buf; +- char_u *p; +- char_u *q; +- +- char_u *start; +- char_u *end = NULL; +- char_u *ksp; +- size_t len, totlen; +- +- size_t split_len = 0; +- char_u *split_buf = NULL; +- ucmd_T *cmd; +- #ifdef FEAT_EVAL +- sctx_T save_current_sctx = current_sctx; +- #endif +- +- if (eap->cmdidx == CMD_USER) +- cmd = USER_CMD(eap->useridx); +- else +- cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); +- +- /* +- * Replace <> in the command by the arguments. +- * First round: "buf" is NULL, compute length, allocate "buf". +- * Second round: copy result into "buf". +- */ +- buf = NULL; +- for (;;) +- { +- p = cmd->uc_rep; /* source */ +- q = buf; /* destination */ +- totlen = 0; +- +- for (;;) +- { +- start = vim_strchr(p, '<'); +- if (start != NULL) +- end = vim_strchr(start + 1, '>'); +- if (buf != NULL) +- { +- for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp) +- ; +- if (*ksp == K_SPECIAL +- && (start == NULL || ksp < start || end == NULL) +- && ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER) +- # ifdef FEAT_GUI +- || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI) +- # endif +- )) +- { +- /* K_SPECIAL has been put in the buffer as K_SPECIAL +- * KS_SPECIAL KE_FILLER, like for mappings, but +- * do_cmdline() doesn't handle that, so convert it back. +- * Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. */ +- len = ksp - p; +- if (len > 0) +- { +- mch_memmove(q, p, len); +- q += len; +- } +- *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI; +- p = ksp + 3; +- continue; +- } +- } +- +- /* break if no <item> is found */ +- if (start == NULL || end == NULL) +- break; +- +- /* Include the '>' */ +- ++end; +- +- /* Take everything up to the '<' */ +- len = start - p; +- if (buf == NULL) +- totlen += len; +- else +- { +- mch_memmove(q, p, len); +- q += len; +- } +- +- len = uc_check_code(start, end - start, q, cmd, eap, +- &split_buf, &split_len); +- if (len == (size_t)-1) +- { +- /* no match, continue after '<' */ +- p = start + 1; +- len = 1; +- } +- else +- p = end; +- if (buf == NULL) +- totlen += len; +- else +- q += len; +- } +- if (buf != NULL) /* second time here, finished */ +- { +- STRCPY(q, p); +- break; +- } +- +- totlen += STRLEN(p); /* Add on the trailing characters */ +- buf = alloc((unsigned)(totlen + 1)); +- if (buf == NULL) +- { +- vim_free(split_buf); +- return; +- } +- } +- +- #ifdef FEAT_EVAL +- current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; +- #endif +- (void)do_cmdline(buf, eap->getline, eap->cookie, +- DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); +- #ifdef FEAT_EVAL +- current_sctx = save_current_sctx; +- #endif +- vim_free(buf); +- vim_free(split_buf); +- } +- +- # if defined(FEAT_CMDL_COMPL) || defined(PROTO) +- static char_u * +- get_user_command_name(int idx) +- { +- return get_user_commands(NULL, idx - (int)CMD_SIZE); +- } +- +- /* +- * Function given to ExpandGeneric() to obtain the list of user command names. +- */ +- char_u * +- get_user_commands(expand_T *xp UNUSED, int idx) +- { +- if (idx < curbuf->b_ucmds.ga_len) +- return USER_CMD_GA(&curbuf->b_ucmds, idx)->uc_name; +- idx -= curbuf->b_ucmds.ga_len; +- if (idx < ucmds.ga_len) +- return USER_CMD(idx)->uc_name; +- return NULL; +- } +- +- /* +- * Function given to ExpandGeneric() to obtain the list of user address type names. +- */ +- char_u * +- get_user_cmd_addr_type(expand_T *xp UNUSED, int idx) +- { +- return (char_u *)addr_type_complete[idx].name; +- } +- +- /* +- * Function given to ExpandGeneric() to obtain the list of user command +- * attributes. +- */ +- char_u * +- get_user_cmd_flags(expand_T *xp UNUSED, int idx) +- { +- static char *user_cmd_flags[] = +- {"addr", "bang", "bar", "buffer", "complete", +- "count", "nargs", "range", "register"}; +- +- if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0]))) +- return NULL; +- return (char_u *)user_cmd_flags[idx]; +- } +- +- /* +- * Function given to ExpandGeneric() to obtain the list of values for -nargs. +- */ +- char_u * +- get_user_cmd_nargs(expand_T *xp UNUSED, int idx) +- { +- static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"}; +- +- if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0]))) +- return NULL; +- return (char_u *)user_cmd_nargs[idx]; +- } +- +- /* +- * Function given to ExpandGeneric() to obtain the list of values for -complete. +- */ +- char_u * +- get_user_cmd_complete(expand_T *xp UNUSED, int idx) +- { +- return (char_u *)command_complete[idx].name; +- } +- # endif /* FEAT_CMDL_COMPL */ +- +- /* +- * Parse address type argument +- */ +- int +- parse_addr_type_arg( +- char_u *value, +- int vallen, +- long *argt, +- int *addr_type_arg) +- { +- int i, a, b; +- +- for (i = 0; addr_type_complete[i].expand != -1; ++i) +- { +- a = (int)STRLEN(addr_type_complete[i].name) == vallen; +- b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; +- if (a && b) +- { +- *addr_type_arg = addr_type_complete[i].expand; +- break; +- } +- } +- +- if (addr_type_complete[i].expand == -1) +- { +- char_u *err = value; +- +- for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++) +- ; +- err[i] = NUL; +- semsg(_("E180: Invalid address type value: %s"), err); +- return FAIL; +- } +- +- if (*addr_type_arg != ADDR_LINES) +- *argt |= NOTADR; +- +- return OK; +- } +- +- #endif /* FEAT_USR_CMDS */ +- +- #if defined(FEAT_USR_CMDS) || defined(FEAT_EVAL) || defined(PROTO) +- /* +- * Parse a completion argument "value[vallen]". +- * The detected completion goes in "*complp", argument type in "*argt". +- * When there is an argument, for function and user defined completion, it's +- * copied to allocated memory and stored in "*compl_arg". +- * Returns FAIL if something is wrong. +- */ +- int +- parse_compl_arg( +- char_u *value, +- int vallen, +- int *complp, +- long *argt, +- char_u **compl_arg UNUSED) +- { +- char_u *arg = NULL; +- # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- size_t arglen = 0; +- # endif +- int i; +- int valend = vallen; +- +- /* Look for any argument part - which is the part after any ',' */ +- for (i = 0; i < vallen; ++i) +- { +- if (value[i] == ',') +- { +- arg = &value[i + 1]; +- # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- arglen = vallen - i - 1; +- # endif +- valend = i; +- break; +- } +- } +- +- for (i = 0; command_complete[i].expand != 0; ++i) +- { +- if ((int)STRLEN(command_complete[i].name) == valend +- && STRNCMP(value, command_complete[i].name, valend) == 0) +- { +- *complp = command_complete[i].expand; +- if (command_complete[i].expand == EXPAND_BUFFERS) +- *argt |= BUFNAME; +- else if (command_complete[i].expand == EXPAND_DIRECTORIES +- || command_complete[i].expand == EXPAND_FILES) +- *argt |= XFILE; +- break; +- } +- } +- +- if (command_complete[i].expand == 0) +- { +- semsg(_("E180: Invalid complete value: %s"), value); +- return FAIL; +- } +- +- # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST +- && arg != NULL) +- # else +- if (arg != NULL) +- # endif +- { +- emsg(_("E468: Completion argument only allowed for custom completion")); +- return FAIL; +- } +- +- # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) +- if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST) +- && arg == NULL) +- { +- emsg(_("E467: Custom completion requires a function argument")); +- return FAIL; +- } +- +- if (arg != NULL) +- *compl_arg = vim_strnsave(arg, (int)arglen); +- # endif +- return OK; +- } +- +- int +- cmdcomplete_str_to_type(char_u *complete_str) +- { +- int i; +- +- for (i = 0; command_complete[i].expand != 0; ++i) +- if (STRCMP(complete_str, command_complete[i].name) == 0) +- return command_complete[i].expand; +- +- return EXPAND_NOTHING; +- } +- #endif +- + static void + ex_colorscheme(exarg_T *eap) + { +--- 5549,5559 ---- +*** ../vim-8.1.1209/src/proto/ex_docmd.pro 2019-01-13 23:38:33.407773189 +0100 +--- src/proto/ex_docmd.pro 2019-04-26 23:39:53.321890880 +0200 +*************** +*** 19,34 **** + char_u *find_nextcmd(char_u *p); + char_u *check_nextcmd(char_u *p); + char_u *get_command_name(expand_T *xp, int idx); +- void ex_comclear(exarg_T *eap); +- void uc_clear(garray_T *gap); +- char_u *get_user_commands(expand_T *xp, int idx); +- char_u *get_user_cmd_addr_type(expand_T *xp, int idx); +- char_u *get_user_cmd_flags(expand_T *xp, int idx); +- char_u *get_user_cmd_nargs(expand_T *xp, int idx); +- char_u *get_user_cmd_complete(expand_T *xp, int idx); +- int parse_addr_type_arg(char_u *value, int vallen, long *argt, int *addr_type_arg); +- int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, char_u **compl_arg); +- int cmdcomplete_str_to_type(char_u *complete_str); + void not_exiting(void); + void tabpage_close(int forceit); + void tabpage_close_other(tabpage_T *tp, int forceit); +--- 19,24 ---- +*** ../vim-8.1.1209/src/ex_getln.c 2019-04-11 13:45:53.125298538 +0200 +--- src/ex_getln.c 2019-04-26 23:26:22.726894948 +0200 +*************** +*** 111,117 **** + # ifdef FEAT_CMDHIST + static char_u *get_history_arg(expand_T *xp, int idx); + # endif +! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) + static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file); + static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file); + # endif +--- 111,117 ---- + # ifdef FEAT_CMDHIST + static char_u *get_history_arg(expand_T *xp, int idx); + # endif +! # if defined(FEAT_EVAL) + static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file); + static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file); + # endif +*************** +*** 939,945 **** + { + xpc.xp_context = ccline.xp_context; + xpc.xp_pattern = ccline.cmdbuff; +! # if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) + xpc.xp_arg = ccline.xp_arg; + # endif + } +--- 939,945 ---- + { + xpc.xp_context = ccline.xp_context; + xpc.xp_pattern = ccline.cmdbuff; +! # if defined(FEAT_CMDL_COMPL) + xpc.xp_arg = ccline.xp_arg; + # endif + } +*************** +*** 4210,4216 **** + #endif + xp->xp_numfiles = -1; + xp->xp_files = NULL; +! #if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + xp->xp_arg = NULL; + #endif + xp->xp_line = NULL; +--- 4210,4216 ---- + #endif + xp->xp_numfiles = -1; + xp->xp_files = NULL; +! #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + xp->xp_arg = NULL; + #endif + xp->xp_line = NULL; +*************** +*** 4879,4885 **** + { + xp->xp_context = ccline.xp_context; + xp->xp_pattern = ccline.cmdbuff; +! # if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL) + xp->xp_arg = ccline.xp_arg; + # endif + } +--- 4879,4885 ---- + { + xp->xp_context = ccline.xp_context; + xp->xp_pattern = ccline.cmdbuff; +! # if defined(FEAT_CMDL_COMPL) + xp->xp_arg = ccline.xp_arg; + # endif + } +*************** +*** 5130,5136 **** + char *directories[] = {"syntax", "indent", "ftplugin", NULL}; + return ExpandRTDir(pat, 0, num_file, file, directories); + } +! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) + if (xp->xp_context == EXPAND_USER_LIST) + return ExpandUserList(xp, num_file, file); + # endif +--- 5130,5136 ---- + char *directories[] = {"syntax", "indent", "ftplugin", NULL}; + return ExpandRTDir(pat, 0, num_file, file, directories); + } +! # if defined(FEAT_EVAL) + if (xp->xp_context == EXPAND_USER_LIST) + return ExpandUserList(xp, num_file, file); + # endif +*************** +*** 5149,5155 **** + ret = ExpandSettings(xp, ®match, num_file, file); + else if (xp->xp_context == EXPAND_MAPPINGS) + ret = ExpandMappings(®match, num_file, file); +! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) + else if (xp->xp_context == EXPAND_USER_DEFINED) + ret = ExpandUserDefined(xp, ®match, num_file, file); + # endif +--- 5149,5155 ---- + ret = ExpandSettings(xp, ®match, num_file, file); + else if (xp->xp_context == EXPAND_MAPPINGS) + ret = ExpandMappings(®match, num_file, file); +! # if defined(FEAT_EVAL) + else if (xp->xp_context == EXPAND_USER_DEFINED) + ret = ExpandUserDefined(xp, ®match, num_file, file); + # endif +*************** +*** 5170,5182 **** + #ifdef FEAT_CMDHIST + {EXPAND_HISTORY, get_history_arg, TRUE, TRUE}, + #endif +- #ifdef FEAT_USR_CMDS + {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE}, + {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE}, + {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE}, + {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE}, + {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE}, +- #endif + #ifdef FEAT_EVAL + {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE}, + {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE}, +--- 5170,5180 ---- +*************** +*** 5473,5479 **** + } + + +! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) + /* + * Call "user_expand_func()" to invoke a user defined Vim script function and + * return the result (either a string or a List). +--- 5471,5477 ---- + } + + +! # if defined(FEAT_EVAL) + /* + * Call "user_expand_func()" to invoke a user defined Vim script function and + * return the result (either a string or a List). +*** ../vim-8.1.1209/src/feature.h 2019-03-30 21:19:16.426170240 +0100 +--- src/feature.h 2019-04-26 23:27:34.074374224 +0200 +*************** +*** 379,388 **** + + /* + * +user_commands Allow the user to define his own commands. + */ +- #ifdef FEAT_NORMAL +- # define FEAT_USR_CMDS +- #endif + + /* + * +printer ":hardcopy" command +--- 379,386 ---- + + /* + * +user_commands Allow the user to define his own commands. ++ * Now always enabled. + */ + + /* + * +printer ":hardcopy" command +*** ../vim-8.1.1209/src/macros.h 2019-03-30 18:46:57.356077354 +0100 +--- src/macros.h 2019-04-26 23:12:15.806808513 +0200 +*************** +*** 336,338 **** +--- 336,341 ---- + (p) = NULL; \ + } \ + } while (0) ++ ++ /* Wether a command index indicates a user command. */ ++ #define IS_USER_CMDIDX(idx) ((int)(idx) < 0) +*** ../vim-8.1.1209/src/misc2.c 2019-03-30 13:53:26.174425093 +0100 +--- src/misc2.c 2019-04-26 23:51:39.458250238 +0200 +*************** +*** 1082,1091 **** + ui_remove_balloon(); + # endif + +! # if defined(FEAT_USR_CMDS) +! /* Clear user commands (before deleting buffers). */ + ex_comclear(NULL); +- # endif + + # ifdef FEAT_MENU + /* Clear menus. */ +--- 1082,1089 ---- + ui_remove_balloon(); + # endif + +! // Clear user commands (before deleting buffers). + ex_comclear(NULL); + + # ifdef FEAT_MENU + /* Clear menus. */ +*************** +*** 1130,1136 **** +--- 1128,1136 ---- + free_search_patterns(); + free_old_sub(); + free_last_insert(); ++ # if defined(FEAT_INS_EXPAND) + free_insexpand_stuff(); ++ # endif + free_prev_shellcmd(); + free_regexp_stuff(); + free_tag_stuff(); +*** ../vim-8.1.1209/src/proto.h 2019-04-21 11:34:36.335256531 +0200 +--- src/proto.h 2019-04-26 23:05:42.872538740 +0200 +*************** +*** 227,232 **** +--- 227,233 ---- + # endif + # include "ui.pro" + # include "undo.pro" ++ # include "usercmd.pro" + # include "userfunc.pro" + # include "version.pro" + # include "window.pro" +*** ../vim-8.1.1209/src/structs.h 2019-04-25 22:21:56.931749183 +0200 +--- src/structs.h 2019-04-26 23:28:26.458006469 +0200 +*************** +*** 549,555 **** + int xp_context; /* type of expansion */ + char_u *xp_pattern; /* start of item to expand */ + int xp_pattern_len; /* bytes in xp_pattern before cursor */ +! #if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + char_u *xp_arg; /* completion function */ + sctx_T xp_script_ctx; /* SCTX for completion function */ + #endif +--- 549,555 ---- + int xp_context; /* type of expansion */ + char_u *xp_pattern; /* start of item to expand */ + int xp_pattern_len; /* bytes in xp_pattern before cursor */ +! #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + char_u *xp_arg; /* completion function */ + sctx_T xp_script_ctx; /* SCTX for completion function */ + #endif +*************** +*** 2143,2152 **** + /* First abbreviation local to a buffer. */ + mapblock_T *b_first_abbr; + #endif +! #ifdef FEAT_USR_CMDS +! /* User commands local to the buffer. */ + garray_T b_ucmds; +- #endif + /* + * start and end of an operator, also used for '[ and '] + */ +--- 2143,2150 ---- + /* First abbreviation local to a buffer. */ + mapblock_T *b_first_abbr; + #endif +! // User commands local to the buffer. + garray_T b_ucmds; + /* + * start and end of an operator, also used for '[ and '] + */ +*** ../vim-8.1.1209/src/version.c 2019-04-26 22:33:44.896723710 +0200 +--- src/version.c 2019-04-26 23:33:32.156048100 +0200 +*************** +*** 672,682 **** + #else + "-toolbar", + #endif +- #ifdef FEAT_USR_CMDS + "+user_commands", +- #else +- "-user_commands", +- #endif + #ifdef FEAT_VARTABS + "+vartabs", + #else +--- 672,678 ---- +*** ../vim-8.1.1209/runtime/doc/eval.txt 2019-04-20 14:39:42.796386124 +0200 +--- runtime/doc/eval.txt 2019-04-27 12:38:38.744951183 +0200 +*************** +*** 10491,10497 **** + ttyout output is a terminal (tty) + unix Unix version of Vim. *+unix* + unnamedplus Compiled with support for "unnamedplus" in 'clipboard' +! user_commands User-defined commands. + vcon Win32: Virtual console support is working, can use + 'termguicolors'. Also see |+vtp|. + vertsplit Compiled with vertically split windows |:vsplit|. +--- 10550,10556 ---- + ttyout output is a terminal (tty) + unix Unix version of Vim. *+unix* + unnamedplus Compiled with support for "unnamedplus" in 'clipboard' +! user_commands User-defined commands. (always true) + vcon Win32: Virtual console support is working, can use + 'termguicolors'. Also see |+vtp|. + vertsplit Compiled with vertically split windows |:vsplit|. +*************** +*** 10501,10506 **** +--- 10560,10566 ---- + viminfo Compiled with viminfo support. + vimscript-1 Compiled Vim script version 1 support + vimscript-2 Compiled Vim script version 2 support ++ vimscript-3 Compiled Vim script version 3 support + virtualedit Compiled with 'virtualedit' option. (always true) + visual Compiled with Visual mode. (always true) + visualextra Compiled with extra Visual mode commands. (always +*************** +*** 12700,12706 **** + + These items are not allowed in the sandbox: + - changing the buffer text +! - defining or changing mapping, autocommands, functions, user commands + - setting certain options (see |option-summary|) + - setting certain v: variables (see |v:var|) *E794* + - executing a shell command +--- 12771,12777 ---- + + These items are not allowed in the sandbox: + - changing the buffer text +! - defining or changing mapping, autocommands, user commands + - setting certain options (see |option-summary|) + - setting certain v: variables (see |v:var|) *E794* + - executing a shell command +*** ../vim-8.1.1209/runtime/doc/various.txt 2019-01-11 14:37:16.689248837 +0100 +--- runtime/doc/various.txt 2019-04-27 12:40:20.376390811 +0200 +*************** +*** 458,464 **** + N *+timers* the |timer_start()| function + N *+title* Setting the window 'title' and 'icon' + N *+toolbar* |gui-toolbar| +! N *+user_commands* User-defined commands. |user-commands| + B *+vartabs* Variable-width tabstops. |'vartabstop'| + N *+viminfo* |'viminfo'| + *+vertsplit* Vertically split windows |:vsplit|; Always enabled +--- 456,463 ---- + N *+timers* the |timer_start()| function + N *+title* Setting the window 'title' and 'icon' + N *+toolbar* |gui-toolbar| +! T *+user_commands* User-defined commands. |user-commands| +! Always enabled since 8.1.1210. + B *+vartabs* Variable-width tabstops. |'vartabstop'| + N *+viminfo* |'viminfo'| + *+vertsplit* Vertically split windows |:vsplit|; Always enabled +*** ../vim-8.1.1209/src/version.c 2019-04-26 22:33:44.896723710 +0200 +--- src/version.c 2019-04-26 23:33:32.156048100 +0200 +*************** +*** 773,774 **** +--- 769,772 ---- + { /* Add new patch number below this line */ ++ /**/ ++ 1210, + /**/ + +-- +Shit makes the flowers grow and that's beautiful + + /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ +/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ +\\\ an exciting new programming language -- http://www.Zimbu.org /// + \\\ help me help AIDS victims -- http://ICCF-Holland.org /// |