To: vim_dev@googlegroups.com Subject: Patch 8.1.1210 Fcc: outbox From: Bram Moolenaar 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 . + */ + 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 () + // 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 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 ! # 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 ! # 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 - */ - 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 () */ - /* 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 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 ///