usercmd.c (49467B)
1 // usercmd.c: User defined command support 2 3 #include <assert.h> 4 #include <inttypes.h> 5 #include <lauxlib.h> 6 #include <stdbool.h> 7 #include <stdio.h> 8 #include <string.h> 9 10 #include "auto/config.h" 11 #include "nvim/api/private/helpers.h" 12 #include "nvim/ascii_defs.h" 13 #include "nvim/buffer_defs.h" 14 #include "nvim/charset.h" 15 #include "nvim/cmdexpand_defs.h" 16 #include "nvim/eval.h" 17 #include "nvim/ex_docmd.h" 18 #include "nvim/garray.h" 19 #include "nvim/gettext_defs.h" 20 #include "nvim/globals.h" 21 #include "nvim/highlight_defs.h" 22 #include "nvim/keycodes.h" 23 #include "nvim/lua/executor.h" 24 #include "nvim/macros_defs.h" 25 #include "nvim/mapping.h" 26 #include "nvim/mbyte.h" 27 #include "nvim/memory.h" 28 #include "nvim/memory_defs.h" 29 #include "nvim/menu.h" 30 #include "nvim/message.h" 31 #include "nvim/option_vars.h" 32 #include "nvim/os/input.h" 33 #include "nvim/runtime.h" 34 #include "nvim/strings.h" 35 #include "nvim/usercmd.h" 36 #include "nvim/vim_defs.h" 37 #include "nvim/window.h" 38 39 #include "usercmd.c.generated.h" 40 41 garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL }; 42 43 static const char e_argument_required_for_str[] 44 = N_("E179: Argument required for %s"); 45 static const char e_no_such_user_defined_command_str[] 46 = N_("E184: No such user-defined command: %s"); 47 static const char e_complete_used_without_allowing_arguments[] 48 = N_("E1208: -complete used without allowing arguments"); 49 static const char e_no_such_user_defined_command_in_current_buffer_str[] 50 = N_("E1237: No such user-defined command in current buffer: %s"); 51 52 /// List of names for completion for ":command" with the EXPAND_ flag. 53 /// Must be alphabetical for completion. 54 static const char *command_complete[] = { 55 [EXPAND_ARGLIST] = "arglist", 56 [EXPAND_AUGROUP] = "augroup", 57 [EXPAND_BUFFERS] = "buffer", 58 [EXPAND_CHECKHEALTH] = "checkhealth", 59 [EXPAND_COLORS] = "color", 60 [EXPAND_COMMANDS] = "command", 61 [EXPAND_COMPILER] = "compiler", 62 [EXPAND_USER_DEFINED] = "custom", 63 [EXPAND_USER_LIST] = "customlist", 64 [EXPAND_USER_LUA] = "<Lua function>", 65 [EXPAND_DIFF_BUFFERS] = "diff_buffer", 66 [EXPAND_DIRECTORIES] = "dir", 67 [EXPAND_ENV_VARS] = "environment", 68 [EXPAND_EVENTS] = "event", 69 [EXPAND_EXPRESSION] = "expression", 70 [EXPAND_FILES] = "file", 71 [EXPAND_FILES_IN_PATH] = "file_in_path", 72 [EXPAND_FILETYPE] = "filetype", 73 [EXPAND_FILETYPECMD] = "filetypecmd", 74 [EXPAND_FUNCTIONS] = "function", 75 [EXPAND_HELP] = "help", 76 [EXPAND_HIGHLIGHT] = "highlight", 77 [EXPAND_HISTORY] = "history", 78 [EXPAND_KEYMAP] = "keymap", 79 #ifdef HAVE_WORKING_LIBINTL 80 [EXPAND_LOCALES] = "locale", 81 #endif 82 [EXPAND_LUA] = "lua", 83 [EXPAND_MAPCLEAR] = "mapclear", 84 [EXPAND_MAPPINGS] = "mapping", 85 [EXPAND_MENUS] = "menu", 86 [EXPAND_MESSAGES] = "messages", 87 [EXPAND_OWNSYNTAX] = "syntax", 88 [EXPAND_SYNTIME] = "syntime", 89 [EXPAND_SETTINGS] = "option", 90 [EXPAND_PACKADD] = "packadd", 91 [EXPAND_RETAB] = "retab", 92 [EXPAND_RUNTIME] = "runtime", 93 [EXPAND_SHELLCMD] = "shellcmd", 94 [EXPAND_SHELLCMDLINE] = "shellcmdline", 95 [EXPAND_SIGN] = "sign", 96 [EXPAND_TAGS] = "tag", 97 [EXPAND_TAGS_LISTFILES] = "tag_listfiles", 98 [EXPAND_USER] = "user", 99 [EXPAND_USER_VARS] = "var", 100 [EXPAND_BREAKPOINT] = "breakpoint", 101 [EXPAND_SCRIPTNAMES] = "scriptnames", 102 [EXPAND_DIRS_IN_CDPATH] = "dir_in_path", 103 }; 104 105 /// List of names of address types. Must be alphabetical for completion. 106 static struct { 107 cmd_addr_T expand; 108 char *name; 109 char *shortname; 110 } addr_type_complete[] = { 111 { ADDR_ARGUMENTS, "arguments", "arg" }, 112 { ADDR_LINES, "lines", "line" }, 113 { ADDR_LOADED_BUFFERS, "loaded_buffers", "load" }, 114 { ADDR_TABS, "tabs", "tab" }, 115 { ADDR_BUFFERS, "buffers", "buf" }, 116 { ADDR_WINDOWS, "windows", "win" }, 117 { ADDR_QUICKFIX, "quickfix", "qf" }, 118 { ADDR_OTHER, "other", "?" }, 119 { ADDR_NONE, NULL, NULL } 120 }; 121 122 /// Search for a user command that matches "eap->cmd". 123 /// Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". 124 /// Return a pointer to just after the command. 125 /// Return NULL if there is no matching command. 126 /// 127 /// @param *p end of the command (possibly including count) 128 /// @param full set to true for a full match 129 /// @param xp used for completion, NULL otherwise 130 /// @param complp completion flags or NULL 131 char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp) 132 { 133 int len = (int)(p - eap->cmd); 134 int matchlen = 0; 135 bool found = false; 136 bool possible = false; 137 bool amb_local = false; // Found ambiguous buffer-local command, 138 // only full match global is accepted. 139 140 // Look for buffer-local user commands first, then global ones. 141 garray_T *gap = &prevwin_curwin()->w_buffer->b_ucmds; 142 while (true) { 143 int j; 144 for (j = 0; j < gap->ga_len; j++) { 145 ucmd_T *uc = USER_CMD_GA(gap, j); 146 char *cp = eap->cmd; 147 char *np = uc->uc_name; 148 int k = 0; 149 while (k < len && *np != NUL && *cp++ == *np++) { 150 k++; 151 } 152 if (k == len || (*np == NUL && ascii_isdigit(eap->cmd[k]))) { 153 // If finding a second match, the command is ambiguous. But 154 // not if a buffer-local command wasn't a full match and a 155 // global command is a full match. 156 if (k == len && found && *np != NUL) { 157 if (gap == &ucmds) { 158 return NULL; 159 } 160 amb_local = true; 161 } 162 163 if (!found || (k == len && *np == NUL)) { 164 // If we matched up to a digit, then there could 165 // be another command including the digit that we 166 // should use instead. 167 if (k == len) { 168 found = true; 169 } else { 170 possible = true; 171 } 172 173 if (gap == &ucmds) { 174 eap->cmdidx = CMD_USER; 175 } else { 176 eap->cmdidx = CMD_USER_BUF; 177 } 178 eap->argt = uc->uc_argt; 179 eap->useridx = j; 180 eap->addr_type = uc->uc_addr_type; 181 182 if (complp != NULL) { 183 *complp = uc->uc_compl; 184 } 185 if (xp != NULL) { 186 xp->xp_luaref = uc->uc_compl_luaref; 187 xp->xp_arg = uc->uc_compl_arg; 188 xp->xp_script_ctx = uc->uc_script_ctx; 189 xp->xp_script_ctx.sc_lnum += SOURCING_LNUM; 190 } 191 // Do not search for further abbreviations 192 // if this is an exact match. 193 matchlen = k; 194 if (k == len && *np == NUL) { 195 if (full != NULL) { 196 *full = true; 197 } 198 amb_local = false; 199 break; 200 } 201 } 202 } 203 } 204 205 // Stop if we found a full match or searched all. 206 if (j < gap->ga_len || gap == &ucmds) { 207 break; 208 } 209 gap = &ucmds; 210 } 211 212 // Only found ambiguous matches. 213 if (amb_local) { 214 if (xp != NULL) { 215 xp->xp_context = EXPAND_UNSUCCESSFUL; 216 } 217 return NULL; 218 } 219 220 // The match we found may be followed immediately by a number. Move "p" 221 // back to point to it. 222 if (found || possible) { 223 return p + (matchlen - len); 224 } 225 return p; 226 } 227 228 /// Set completion context for :command 229 const char *set_context_in_user_cmd(expand_T *xp, const char *arg_in) 230 { 231 const char *arg = arg_in; 232 const char *p; 233 234 // Check for attributes 235 while (*arg == '-') { 236 arg++; // Skip "-". 237 p = skiptowhite(arg); 238 if (*p == NUL) { 239 // Cursor is still in the attribute. 240 p = strchr(arg, '='); 241 if (p == NULL) { 242 // No "=", so complete attribute names. 243 xp->xp_context = EXPAND_USER_CMD_FLAGS; 244 xp->xp_pattern = (char *)arg; 245 return NULL; 246 } 247 248 // For the -complete, -nargs and -addr attributes, we complete 249 // their arguments as well. 250 if (STRNICMP(arg, "complete", p - arg) == 0) { 251 xp->xp_context = EXPAND_USER_COMPLETE; 252 xp->xp_pattern = (char *)p + 1; 253 return NULL; 254 } else if (STRNICMP(arg, "nargs", p - arg) == 0) { 255 xp->xp_context = EXPAND_USER_NARGS; 256 xp->xp_pattern = (char *)p + 1; 257 return NULL; 258 } else if (STRNICMP(arg, "addr", p - arg) == 0) { 259 xp->xp_context = EXPAND_USER_ADDR_TYPE; 260 xp->xp_pattern = (char *)p + 1; 261 return NULL; 262 } 263 return NULL; 264 } 265 arg = skipwhite(p); 266 } 267 268 // After the attributes comes the new command name. 269 p = skiptowhite(arg); 270 if (*p == NUL) { 271 xp->xp_context = EXPAND_USER_COMMANDS; 272 xp->xp_pattern = (char *)arg; 273 return NULL; 274 } 275 276 // And finally comes a normal command. 277 return skipwhite(p); 278 } 279 280 /// Set the completion context for the argument of a user defined command. 281 const char *set_context_in_user_cmdarg(const char *cmd FUNC_ATTR_UNUSED, const char *arg, 282 uint32_t argt, int context, expand_T *xp, bool forceit) 283 { 284 if (context == EXPAND_NOTHING) { 285 return NULL; 286 } 287 288 if (argt & EX_XFILE) { 289 // EX_XFILE: file names are handled before this call. 290 return NULL; 291 } 292 293 if (context == EXPAND_MENUS) { 294 return set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit); 295 } 296 if (context == EXPAND_COMMANDS) { 297 return arg; 298 } 299 if (context == EXPAND_MAPPINGS) { 300 return set_context_in_map_cmd(xp, "map", (char *)arg, forceit, false, false, 301 CMD_map); 302 } 303 // Find start of last argument. 304 const char *p = arg; 305 while (*p) { 306 if (*p == ' ') { 307 // argument starts after a space 308 arg = p + 1; 309 } else if (*p == '\\' && *(p + 1) != NUL) { 310 p++; // skip over escaped character 311 } 312 MB_PTR_ADV(p); 313 } 314 xp->xp_pattern = (char *)arg; 315 xp->xp_context = context; 316 317 return NULL; 318 } 319 320 char *expand_user_command_name(int idx) 321 { 322 return get_user_commands(NULL, idx - CMD_SIZE); 323 } 324 325 /// Function given to ExpandGeneric() to obtain the list of user command names. 326 char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) 327 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 328 { 329 // In cmdwin, the alternative buffer should be used. 330 const buf_T *const buf = prevwin_curwin()->w_buffer; 331 332 if (idx < buf->b_ucmds.ga_len) { 333 return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; 334 } 335 336 idx -= buf->b_ucmds.ga_len; 337 if (idx < ucmds.ga_len) { 338 char *name = USER_CMD(idx)->uc_name; 339 340 for (int i = 0; i < buf->b_ucmds.ga_len; i++) { 341 if (strcmp(name, USER_CMD_GA(&buf->b_ucmds, i)->uc_name) == 0) { 342 // global command is overruled by buffer-local one 343 return ""; 344 } 345 } 346 return name; 347 } 348 return NULL; 349 } 350 351 /// Get the name of user command "idx". "cmdidx" can be CMD_USER or 352 /// CMD_USER_BUF. 353 /// 354 /// @return NULL if the command is not found. 355 char *get_user_command_name(int idx, int cmdidx) 356 { 357 if (cmdidx == CMD_USER && idx < ucmds.ga_len) { 358 return USER_CMD(idx)->uc_name; 359 } 360 if (cmdidx == CMD_USER_BUF) { 361 // In cmdwin, the alternative buffer should be used. 362 const buf_T *const buf = prevwin_curwin()->w_buffer; 363 364 if (idx < buf->b_ucmds.ga_len) { 365 return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; 366 } 367 } 368 return NULL; 369 } 370 371 /// Function given to ExpandGeneric() to obtain the list of user address type names. 372 char *get_user_cmd_addr_type(expand_T *xp, int idx) 373 { 374 return addr_type_complete[idx].name; 375 } 376 377 /// Function given to ExpandGeneric() to obtain the list of user command 378 /// attributes. 379 char *get_user_cmd_flags(expand_T *xp, int idx) 380 { 381 static char *user_cmd_flags[] = { "addr", "bang", "bar", 382 "buffer", "complete", "count", 383 "nargs", "range", "register", 384 "keepscript" }; 385 386 if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) { 387 return NULL; 388 } 389 return user_cmd_flags[idx]; 390 } 391 392 /// Function given to ExpandGeneric() to obtain the list of values for -nargs. 393 char *get_user_cmd_nargs(expand_T *xp, int idx) 394 { 395 static char *user_cmd_nargs[] = { "0", "1", "*", "?", "+" }; 396 397 if (idx >= (int)ARRAY_SIZE(user_cmd_nargs)) { 398 return NULL; 399 } 400 return user_cmd_nargs[idx]; 401 } 402 403 static char *get_command_complete(int arg) 404 { 405 if (arg < 0 || arg >= (int)(ARRAY_SIZE(command_complete))) { 406 return NULL; 407 } 408 return (char *)command_complete[arg]; 409 } 410 411 /// Function given to ExpandGeneric() to obtain the list of values for -complete. 412 char *get_user_cmd_complete(expand_T *xp, int idx) 413 { 414 if (idx >= (int)ARRAY_SIZE(command_complete)) { 415 return NULL; 416 } 417 char *cmd_compl = get_command_complete(idx); 418 if (cmd_compl == NULL || idx == EXPAND_USER_LUA) { 419 return ""; 420 } 421 return cmd_compl; 422 } 423 424 /// Get the name of completion type "expand" as an allocated string. 425 /// "compl_arg" is the function name for "custom" and "customlist" types. 426 /// Returns NULL if no completion is available. 427 char *cmdcomplete_type_to_str(int expand, const char *compl_arg) 428 { 429 char *cmd_compl = get_command_complete(expand); 430 if (cmd_compl == NULL || expand == EXPAND_USER_LUA) { 431 return NULL; 432 } 433 434 if (expand == EXPAND_USER_LIST || expand == EXPAND_USER_DEFINED) { 435 size_t buflen = strlen(cmd_compl) + strlen(compl_arg) + 2; 436 char *buffer = xmalloc(buflen); 437 snprintf(buffer, buflen, "%s,%s", cmd_compl, compl_arg); 438 return buffer; 439 } 440 441 return xstrdup(cmd_compl); 442 } 443 444 int cmdcomplete_str_to_type(const char *complete_str) 445 { 446 if (strncmp(complete_str, "custom,", 7) == 0) { 447 return EXPAND_USER_DEFINED; 448 } 449 if (strncmp(complete_str, "customlist,", 11) == 0) { 450 return EXPAND_USER_LIST; 451 } 452 453 for (int i = 0; i < (int)(ARRAY_SIZE(command_complete)); i++) { 454 char *cmd_compl = get_command_complete(i); 455 if (cmd_compl == NULL) { 456 continue; 457 } 458 if (strcmp(complete_str, command_complete[i]) == 0) { 459 return i; 460 } 461 } 462 463 return EXPAND_NOTHING; 464 } 465 466 static void uc_list(char *name, size_t name_len) 467 { 468 bool found = false; 469 470 msg_ext_set_kind("list_cmd"); 471 // In cmdwin, the alternative buffer should be used. 472 const garray_T *gap = &prevwin_curwin()->w_buffer->b_ucmds; 473 while (true) { 474 int i; 475 for (i = 0; i < gap->ga_len; i++) { 476 ucmd_T *cmd = USER_CMD_GA(gap, i); 477 uint32_t a = cmd->uc_argt; 478 479 // Skip commands which don't match the requested prefix and 480 // commands filtered out. 481 if (strncmp(name, cmd->uc_name, name_len) != 0 482 || message_filtered(cmd->uc_name)) { 483 continue; 484 } 485 486 // Put out the title first time 487 if (!found) { 488 msg_puts_title(_("\n Name Args Address Complete Definition")); 489 } 490 found = true; 491 msg_putchar('\n'); 492 if (got_int) { 493 break; 494 } 495 496 // Special cases 497 size_t len = 4; 498 if (a & EX_BANG) { 499 msg_putchar('!'); 500 len--; 501 } 502 if (a & EX_REGSTR) { 503 msg_putchar('"'); 504 len--; 505 } 506 if (gap != &ucmds) { 507 msg_putchar('b'); 508 len--; 509 } 510 if (a & EX_TRLBAR) { 511 msg_putchar('|'); 512 len--; 513 } 514 if (len != 0) { 515 msg_puts(&" "[4 - len]); 516 } 517 518 msg_outtrans(cmd->uc_name, HLF_D, false); 519 len = strlen(cmd->uc_name) + 4; 520 521 if (len < 21) { 522 // Field padding spaces 12345678901234567 523 static char spaces[18] = " "; 524 msg_puts(&spaces[len - 4]); 525 len = 21; 526 } 527 msg_putchar(' '); 528 len++; 529 530 // "over" is how much longer the name is than the column width for 531 // the name, we'll try to align what comes after. 532 const int64_t over = (int64_t)len - 22; 533 len = 0; 534 535 // Arguments 536 switch (a & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { 537 case 0: 538 IObuff[len++] = '0'; 539 break; 540 case (EX_EXTRA): 541 IObuff[len++] = '*'; 542 break; 543 case (EX_EXTRA | EX_NOSPC): 544 IObuff[len++] = '?'; 545 break; 546 case (EX_EXTRA | EX_NEEDARG): 547 IObuff[len++] = '+'; 548 break; 549 case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): 550 IObuff[len++] = '1'; 551 break; 552 } 553 554 do { 555 IObuff[len++] = ' '; 556 } while ((int64_t)len < 5 - over); 557 558 // Address / Range 559 if (a & (EX_RANGE | EX_COUNT)) { 560 if (a & EX_COUNT) { 561 // -count=N 562 int rc = snprintf(IObuff + len, IOSIZE - len, "%" PRId64 "c", cmd->uc_def); 563 assert(rc > 0); 564 len += (size_t)rc; 565 } else if (a & EX_DFLALL) { 566 IObuff[len++] = '%'; 567 } else if (cmd->uc_def >= 0) { 568 // -range=N 569 int rc = snprintf(IObuff + len, IOSIZE - len, "%" PRId64 "", cmd->uc_def); 570 assert(rc > 0); 571 len += (size_t)rc; 572 } else { 573 IObuff[len++] = '.'; 574 } 575 } 576 577 do { 578 IObuff[len++] = ' '; 579 } while ((int64_t)len < 8 - over); 580 581 // Address Type 582 for (int j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { 583 if (addr_type_complete[j].expand != ADDR_LINES 584 && addr_type_complete[j].expand == cmd->uc_addr_type) { 585 int rc = snprintf(IObuff + len, IOSIZE - len, "%s", addr_type_complete[j].shortname); 586 assert(rc > 0); 587 len += (size_t)rc; 588 break; 589 } 590 } 591 592 do { 593 IObuff[len++] = ' '; 594 } while ((int64_t)len < 13 - over); 595 596 // Completion 597 char *cmd_compl = get_command_complete(cmd->uc_compl); 598 if (cmd_compl != NULL) { 599 int rc = snprintf(IObuff + len, IOSIZE - len, "%s", get_command_complete(cmd->uc_compl)); 600 assert(rc > 0); 601 len += (size_t)rc; 602 } 603 604 do { 605 IObuff[len++] = ' '; 606 } while ((int64_t)len < 25 - over); 607 608 IObuff[len] = NUL; 609 msg_outtrans(IObuff, 0, false); 610 611 if (cmd->uc_luaref != LUA_NOREF) { 612 char *fn = nlua_funcref_str(cmd->uc_luaref, NULL); 613 msg_puts_hl(fn, HLF_8, false); 614 xfree(fn); 615 // put the description on a new line 616 if (*cmd->uc_rep != NUL) { 617 msg_puts("\n "); 618 } 619 } 620 621 msg_outtrans_special(cmd->uc_rep, false, 622 name_len == 0 ? Columns - 47 : 0); 623 if (p_verbose > 0) { 624 last_set_msg(cmd->uc_script_ctx); 625 } 626 line_breakcheck(); 627 if (got_int) { 628 break; 629 } 630 } 631 if (gap == &ucmds || i < gap->ga_len) { 632 break; 633 } 634 gap = &ucmds; 635 } 636 637 if (!found) { 638 msg(_("No user-defined commands found"), 0); 639 } 640 } 641 642 /// Parse address type argument 643 int parse_addr_type_arg(char *value, int vallen, cmd_addr_T *addr_type_arg) 644 FUNC_ATTR_NONNULL_ALL 645 { 646 int i; 647 648 for (i = 0; addr_type_complete[i].expand != ADDR_NONE; i++) { 649 int a = (int)strlen(addr_type_complete[i].name) == vallen; 650 int b = strncmp(value, addr_type_complete[i].name, (size_t)vallen) == 0; 651 if (a && b) { 652 *addr_type_arg = addr_type_complete[i].expand; 653 break; 654 } 655 } 656 657 if (addr_type_complete[i].expand == ADDR_NONE) { 658 char *err = value; 659 660 for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {} 661 err[i] = NUL; 662 semsg(_("E180: Invalid address type value: %s"), err); 663 return FAIL; 664 } 665 666 return OK; 667 } 668 669 /// Parse a completion argument "value[vallen]". 670 /// The detected completion goes in "*complp", argument type in "*argt". 671 /// When there is an argument, for function and user defined completion, it's 672 /// copied to allocated memory and stored in "*compl_arg". 673 /// 674 /// @return FAIL if something is wrong. 675 int parse_compl_arg(const char *value, int vallen, int *complp, uint32_t *argt, char **compl_arg) 676 FUNC_ATTR_NONNULL_ALL 677 { 678 const char *arg = NULL; 679 size_t arglen = 0; 680 int valend = vallen; 681 682 // Look for any argument part - which is the part after any ',' 683 for (int i = 0; i < vallen; i++) { 684 if (value[i] == ',') { 685 arg = (char *)&value[i + 1]; 686 arglen = (size_t)(vallen - i - 1); 687 valend = i; 688 break; 689 } 690 } 691 692 int i; 693 for (i = 0; i < (int)ARRAY_SIZE(command_complete); i++) { 694 if (get_command_complete(i) == NULL) { 695 continue; 696 } 697 if ((int)strlen(command_complete[i]) == valend 698 && strncmp(value, command_complete[i], (size_t)valend) == 0) { 699 *complp = i; 700 if (i == EXPAND_BUFFERS) { 701 *argt |= EX_BUFNAME; 702 } else if (i == EXPAND_DIRECTORIES || i == EXPAND_FILES 703 || i == EXPAND_SHELLCMDLINE) { 704 *argt |= EX_XFILE; 705 } 706 break; 707 } 708 } 709 710 if (i == (int)ARRAY_SIZE(command_complete)) { 711 semsg(_("E180: Invalid complete value: %s"), value); 712 return FAIL; 713 } 714 715 if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST 716 && arg != NULL) { 717 emsg(_("E468: Completion argument only allowed for custom completion")); 718 return FAIL; 719 } 720 721 if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST) 722 && arg == NULL) { 723 emsg(_("E467: Custom completion requires a function argument")); 724 return FAIL; 725 } 726 727 if (arg != NULL) { 728 *compl_arg = xstrnsave(arg, arglen); 729 } 730 return OK; 731 } 732 733 static int uc_scan_attr(char *attr, size_t len, uint32_t *argt, int *def, int *flags, int *complp, 734 char **compl_arg, cmd_addr_T *addr_type_arg) 735 FUNC_ATTR_NONNULL_ALL 736 { 737 if (len == 0) { 738 emsg(_("E175: No attribute specified")); 739 return FAIL; 740 } 741 742 // First, try the simple attributes (no arguments) 743 if (STRNICMP(attr, "bang", len) == 0) { 744 *argt |= EX_BANG; 745 } else if (STRNICMP(attr, "buffer", len) == 0) { 746 *flags |= UC_BUFFER; 747 } else if (STRNICMP(attr, "register", len) == 0) { 748 *argt |= EX_REGSTR; 749 } else if (STRNICMP(attr, "keepscript", len) == 0) { 750 *argt |= EX_KEEPSCRIPT; 751 } else if (STRNICMP(attr, "bar", len) == 0) { 752 *argt |= EX_TRLBAR; 753 } else { 754 char *val = NULL; 755 size_t vallen = 0; 756 size_t attrlen = len; 757 758 // Look for the attribute name - which is the part before any '=' 759 for (int i = 0; i < (int)len; i++) { 760 if (attr[i] == '=') { 761 val = &attr[i + 1]; 762 vallen = len - (size_t)i - 1; 763 attrlen = (size_t)i; 764 break; 765 } 766 } 767 768 if (STRNICMP(attr, "nargs", attrlen) == 0) { 769 if (vallen == 1) { 770 if (*val == '0') { 771 // Do nothing - this is the default; 772 } else if (*val == '1') { 773 *argt |= (EX_EXTRA | EX_NOSPC | EX_NEEDARG); 774 } else if (*val == '*') { 775 *argt |= EX_EXTRA; 776 } else if (*val == '?') { 777 *argt |= (EX_EXTRA | EX_NOSPC); 778 } else if (*val == '+') { 779 *argt |= (EX_EXTRA | EX_NEEDARG); 780 } else { 781 goto wrong_nargs; 782 } 783 } else { 784 wrong_nargs: 785 emsg(_("E176: Invalid number of arguments")); 786 return FAIL; 787 } 788 } else if (STRNICMP(attr, "range", attrlen) == 0) { 789 *argt |= EX_RANGE; 790 if (vallen == 1 && *val == '%') { 791 *argt |= EX_DFLALL; 792 } else if (val != NULL) { 793 char *p = val; 794 if (*def >= 0) { 795 two_count: 796 emsg(_("E177: Count cannot be specified twice")); 797 return FAIL; 798 } 799 800 *def = getdigits_int(&p, true, 0); 801 *argt |= EX_ZEROR; 802 803 if (p != val + vallen || vallen == 0) { 804 invalid_count: 805 emsg(_("E178: Invalid default value for count")); 806 return FAIL; 807 } 808 } 809 // default for -range is using buffer lines 810 if (*addr_type_arg == ADDR_NONE) { 811 *addr_type_arg = ADDR_LINES; 812 } 813 } else if (STRNICMP(attr, "count", attrlen) == 0) { 814 *argt |= (EX_COUNT | EX_ZEROR | EX_RANGE); 815 // default for -count is using any number 816 if (*addr_type_arg == ADDR_NONE) { 817 *addr_type_arg = ADDR_OTHER; 818 } 819 820 if (val != NULL) { 821 char *p = val; 822 if (*def >= 0) { 823 goto two_count; 824 } 825 826 *def = getdigits_int(&p, true, 0); 827 828 if (p != val + vallen) { 829 goto invalid_count; 830 } 831 } 832 833 *def = MAX(*def, 0); 834 } else if (STRNICMP(attr, "complete", attrlen) == 0) { 835 if (val == NULL) { 836 semsg(_(e_argument_required_for_str), "-complete"); 837 return FAIL; 838 } 839 840 if (parse_compl_arg(val, (int)vallen, complp, argt, compl_arg) 841 == FAIL) { 842 return FAIL; 843 } 844 } else if (STRNICMP(attr, "addr", attrlen) == 0) { 845 *argt |= EX_RANGE; 846 if (val == NULL) { 847 semsg(_(e_argument_required_for_str), "-addr"); 848 return FAIL; 849 } 850 if (parse_addr_type_arg(val, (int)vallen, addr_type_arg) == FAIL) { 851 return FAIL; 852 } 853 if (*addr_type_arg != ADDR_LINES) { 854 *argt |= EX_ZEROR; 855 } 856 } else { 857 char ch = attr[len]; 858 attr[len] = NUL; 859 semsg(_("E181: Invalid attribute: %s"), attr); 860 attr[len] = ch; 861 return FAIL; 862 } 863 } 864 865 return OK; 866 } 867 868 /// Check for a valid user command name 869 /// 870 /// If the given {name} is valid, then a pointer to the end of the valid name is returned. 871 /// Otherwise, returns NULL. 872 char *uc_validate_name(char *name) 873 { 874 if (ASCII_ISALPHA(*name)) { 875 while (ASCII_ISALNUM(*name)) { 876 name++; 877 } 878 } 879 if (!ends_excmd(*name) && !ascii_iswhite(*name)) { 880 return NULL; 881 } 882 883 return name; 884 } 885 886 /// Create a new user command {name}, if one doesn't already exist. 887 /// 888 /// This function takes ownership of compl_arg, compl_luaref, and luaref. 889 /// 890 /// @return OK if the command is created, FAIL otherwise. 891 int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt, int64_t def, 892 int flags, int context, char *compl_arg, LuaRef compl_luaref, 893 LuaRef preview_luaref, cmd_addr_T addr_type, LuaRef luaref, bool force) 894 FUNC_ATTR_NONNULL_ARG(1, 3) 895 { 896 ucmd_T *cmd = NULL; 897 int cmp = 1; 898 char *rep_buf = NULL; 899 garray_T *gap; 900 901 replace_termcodes(rep, strlen(rep), &rep_buf, 0, 0, NULL, p_cpo); 902 if (rep_buf == NULL) { 903 // Can't replace termcodes - try using the string as is 904 rep_buf = xstrdup(rep); 905 } 906 907 // get address of growarray: global or in curbuf 908 if (flags & UC_BUFFER) { 909 gap = &curbuf->b_ucmds; 910 if (gap->ga_itemsize == 0) { 911 ga_init(gap, (int)sizeof(ucmd_T), 4); 912 } 913 } else { 914 gap = &ucmds; 915 } 916 917 int i; 918 919 // Search for the command in the already defined commands. 920 for (i = 0; i < gap->ga_len; i++) { 921 cmd = USER_CMD_GA(gap, i); 922 size_t len = strlen(cmd->uc_name); 923 cmp = strncmp(name, cmd->uc_name, name_len); 924 if (cmp == 0) { 925 if (name_len < len) { 926 cmp = -1; 927 } else if (name_len > len) { 928 cmp = 1; 929 } 930 } 931 932 if (cmp == 0) { 933 // Command can be replaced with "command!" and when sourcing the 934 // same script again, but only once. 935 if (!force 936 && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid 937 || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq)) { 938 semsg(_("E174: Command already exists: add ! to replace it: %s"), 939 name); 940 goto fail; 941 } 942 943 XFREE_CLEAR(cmd->uc_rep); 944 XFREE_CLEAR(cmd->uc_compl_arg); 945 NLUA_CLEAR_REF(cmd->uc_luaref); 946 NLUA_CLEAR_REF(cmd->uc_compl_luaref); 947 NLUA_CLEAR_REF(cmd->uc_preview_luaref); 948 break; 949 } 950 951 // Stop as soon as we pass the name to add 952 if (cmp < 0) { 953 break; 954 } 955 } 956 957 // Extend the array unless we're replacing an existing command 958 if (cmp != 0) { 959 ga_grow(gap, 1); 960 961 char *const p = xstrnsave(name, name_len); 962 963 cmd = USER_CMD_GA(gap, i); 964 memmove(cmd + 1, cmd, (size_t)(gap->ga_len - i) * sizeof(ucmd_T)); 965 966 gap->ga_len++; 967 968 cmd->uc_name = p; 969 } 970 971 cmd->uc_rep = rep_buf; 972 cmd->uc_argt = argt; 973 cmd->uc_def = def; 974 cmd->uc_compl = context; 975 cmd->uc_script_ctx = current_sctx; 976 cmd->uc_script_ctx.sc_lnum += SOURCING_LNUM; 977 nlua_set_sctx(&cmd->uc_script_ctx); 978 cmd->uc_compl_arg = compl_arg; 979 cmd->uc_compl_luaref = compl_luaref; 980 cmd->uc_preview_luaref = preview_luaref; 981 cmd->uc_addr_type = addr_type; 982 cmd->uc_luaref = luaref; 983 984 return OK; 985 986 fail: 987 xfree(rep_buf); 988 xfree(compl_arg); 989 NLUA_CLEAR_REF(luaref); 990 NLUA_CLEAR_REF(compl_luaref); 991 NLUA_CLEAR_REF(preview_luaref); 992 return FAIL; 993 } 994 995 /// ":command ..." 996 void ex_command(exarg_T *eap) 997 { 998 char *end; 999 uint32_t argt = 0; 1000 int def = -1; 1001 int flags = 0; 1002 int context = EXPAND_NOTHING; 1003 char *compl_arg = NULL; 1004 cmd_addr_T addr_type_arg = ADDR_NONE; 1005 int has_attr = (eap->arg[0] == '-'); 1006 1007 char *p = eap->arg; 1008 1009 // Check for attributes 1010 while (*p == '-') { 1011 p++; 1012 end = skiptowhite(p); 1013 if (uc_scan_attr(p, (size_t)(end - p), &argt, &def, &flags, &context, &compl_arg, 1014 &addr_type_arg) == FAIL) { 1015 goto theend; 1016 } 1017 p = skipwhite(end); 1018 } 1019 1020 // Get the name (if any) and skip to the following argument. 1021 char *name = p; 1022 end = uc_validate_name(name); 1023 if (!end) { 1024 emsg(_("E182: Invalid command name")); 1025 goto theend; 1026 } 1027 size_t name_len = (size_t)(end - name); 1028 1029 // If there is nothing after the name, and no attributes were specified, 1030 // we are listing commands 1031 p = skipwhite(end); 1032 if (!has_attr && ends_excmd(*p)) { 1033 uc_list(name, name_len); 1034 } else if (!ASCII_ISUPPER(*name)) { 1035 emsg(_("E183: User defined commands must start with an uppercase letter")); 1036 } else if (name_len <= 4 && strncmp(name, "Next", name_len) == 0) { 1037 emsg(_("E841: Reserved name, cannot be used for user defined command")); 1038 } else if (context > 0 && (argt & EX_EXTRA) == 0) { 1039 emsg(_(e_complete_used_without_allowing_arguments)); 1040 } else { 1041 uc_add_command(name, name_len, p, argt, def, flags, context, compl_arg, LUA_NOREF, LUA_NOREF, 1042 addr_type_arg, LUA_NOREF, eap->forceit); 1043 1044 return; // success 1045 } 1046 1047 theend: 1048 xfree(compl_arg); 1049 } 1050 1051 /// ":comclear" 1052 /// Clear all user commands, global and for current buffer. 1053 void ex_comclear(exarg_T *eap) 1054 { 1055 uc_clear(&ucmds); 1056 if (curbuf != NULL) { 1057 uc_clear(&curbuf->b_ucmds); 1058 } 1059 } 1060 1061 void free_ucmd(ucmd_T *cmd) 1062 { 1063 xfree(cmd->uc_name); 1064 xfree(cmd->uc_rep); 1065 xfree(cmd->uc_compl_arg); 1066 NLUA_CLEAR_REF(cmd->uc_compl_luaref); 1067 NLUA_CLEAR_REF(cmd->uc_luaref); 1068 NLUA_CLEAR_REF(cmd->uc_preview_luaref); 1069 } 1070 1071 /// Clear all user commands for "gap". 1072 void uc_clear(garray_T *gap) 1073 { 1074 GA_DEEP_CLEAR(gap, ucmd_T, free_ucmd); 1075 } 1076 1077 void ex_delcommand(exarg_T *eap) 1078 { 1079 int i = 0; 1080 ucmd_T *cmd = NULL; 1081 int res = -1; 1082 const char *arg = eap->arg; 1083 bool buffer_only = false; 1084 1085 if (strncmp(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) { 1086 buffer_only = true; 1087 arg = skipwhite(arg + 7); 1088 } 1089 1090 garray_T *gap = &curbuf->b_ucmds; 1091 while (true) { 1092 for (i = 0; i < gap->ga_len; i++) { 1093 cmd = USER_CMD_GA(gap, i); 1094 res = strcmp(arg, cmd->uc_name); 1095 if (res <= 0) { 1096 break; 1097 } 1098 } 1099 if (gap == &ucmds || res == 0 || buffer_only) { 1100 break; 1101 } 1102 gap = &ucmds; 1103 } 1104 1105 if (res != 0) { 1106 semsg(_(buffer_only 1107 ? e_no_such_user_defined_command_in_current_buffer_str 1108 : e_no_such_user_defined_command_str), 1109 arg); 1110 return; 1111 } 1112 1113 free_ucmd(cmd); 1114 1115 gap->ga_len--; 1116 1117 if (i < gap->ga_len) { 1118 memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T)); 1119 } 1120 } 1121 1122 /// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback. 1123 /// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator. 1124 /// 1125 /// @param[in] arg String to split 1126 /// @param[in] arglen Length of {arg} 1127 /// @param[inout] end Index of last character of previous iteration 1128 /// @param[out] buf Buffer to copy string into 1129 /// @param[out] len Length of string in {buf} 1130 /// 1131 /// @return true if iteration is complete, else false 1132 bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf, size_t *len) 1133 { 1134 if (!arglen) { 1135 return true; 1136 } 1137 1138 size_t pos = *end; 1139 while (pos < arglen && ascii_iswhite(arg[pos])) { 1140 pos++; 1141 } 1142 1143 size_t l = 0; 1144 for (; pos < arglen - 1; pos++) { 1145 if (arg[pos] == '\\' && (arg[pos + 1] == '\\' || ascii_iswhite(arg[pos + 1]))) { 1146 buf[l++] = arg[++pos]; 1147 } else { 1148 buf[l++] = arg[pos]; 1149 } 1150 if (ascii_iswhite(arg[pos + 1])) { 1151 *end = pos + 1; 1152 *len = l; 1153 return false; 1154 } 1155 } 1156 1157 if (pos < arglen && !ascii_iswhite(arg[pos])) { 1158 buf[l++] = arg[pos]; 1159 } 1160 1161 *len = l; 1162 return true; 1163 } 1164 1165 size_t uc_nargs_upper_bound(const char *arg, size_t arglen) 1166 { 1167 bool was_white = true; // space before first arg 1168 size_t nargs = 0; 1169 for (size_t i = 0; i < arglen; i++) { 1170 bool is_white = ascii_iswhite(arg[i]); 1171 if (was_white && !is_white) { 1172 nargs++; 1173 } 1174 was_white = is_white; 1175 } 1176 return nargs; 1177 } 1178 1179 /// split and quote args for <f-args> 1180 static char *uc_split_args(const char *arg, char **args, const size_t *arglens, size_t argc, 1181 size_t *lenp) 1182 { 1183 // Precalculate length 1184 int len = 2; // Initial and final quotes 1185 if (args == NULL) { 1186 const char *p = arg; 1187 1188 while (*p) { 1189 if (p[0] == '\\' && p[1] == '\\') { 1190 len += 2; 1191 p += 2; 1192 } else if (p[0] == '\\' && ascii_iswhite(p[1])) { 1193 len += 1; 1194 p += 2; 1195 } else if (*p == '\\' || *p == '"') { 1196 len += 2; 1197 p += 1; 1198 } else if (ascii_iswhite(*p)) { 1199 p = skipwhite(p); 1200 if (*p == NUL) { 1201 break; 1202 } 1203 len += 4; // ", " 1204 } else { 1205 const int charlen = utfc_ptr2len(p); 1206 1207 len += charlen; 1208 p += charlen; 1209 } 1210 } 1211 } else { 1212 for (size_t i = 0; i < argc; i++) { 1213 const char *p = args[i]; 1214 const char *arg_end = args[i] + arglens[i]; 1215 1216 while (p < arg_end) { 1217 if (*p == '\\' || *p == '"') { 1218 len += 2; 1219 p += 1; 1220 } else { 1221 const int charlen = utfc_ptr2len(p); 1222 1223 len += charlen; 1224 p += charlen; 1225 } 1226 } 1227 1228 if (i != argc - 1) { 1229 len += 4; // ", " 1230 } 1231 } 1232 } 1233 1234 char *buf = xmalloc((size_t)len + 1); 1235 1236 char *q = buf; 1237 *q++ = '"'; 1238 1239 if (args == NULL) { 1240 const char *p = arg; 1241 while (*p) { 1242 if (p[0] == '\\' && p[1] == '\\') { 1243 *q++ = '\\'; 1244 *q++ = '\\'; 1245 p += 2; 1246 } else if (p[0] == '\\' && ascii_iswhite(p[1])) { 1247 *q++ = p[1]; 1248 p += 2; 1249 } else if (*p == '\\' || *p == '"') { 1250 *q++ = '\\'; 1251 *q++ = *p++; 1252 } else if (ascii_iswhite(*p)) { 1253 p = skipwhite(p); 1254 if (*p == NUL) { 1255 break; 1256 } 1257 *q++ = '"'; 1258 *q++ = ','; 1259 *q++ = ' '; 1260 *q++ = '"'; 1261 } else { 1262 mb_copy_char(&p, &q); 1263 } 1264 } 1265 } else { 1266 for (size_t i = 0; i < argc; i++) { 1267 const char *p = args[i]; 1268 const char *arg_end = args[i] + arglens[i]; 1269 1270 while (p < arg_end) { 1271 if (*p == '\\' || *p == '"') { 1272 *q++ = '\\'; 1273 *q++ = *p++; 1274 } else { 1275 mb_copy_char(&p, &q); 1276 } 1277 } 1278 if (i != argc - 1) { 1279 *q++ = '"'; 1280 *q++ = ','; 1281 *q++ = ' '; 1282 *q++ = '"'; 1283 } 1284 } 1285 } 1286 1287 *q++ = '"'; 1288 *q = 0; 1289 1290 *lenp = (size_t)len; 1291 return buf; 1292 } 1293 1294 static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods) 1295 { 1296 size_t result = strlen(mod_str); 1297 if (*multi_mods) { 1298 result++; 1299 } 1300 1301 if (buf != NULL) { 1302 if (*multi_mods) { 1303 strcat(buf, " "); 1304 } 1305 strcat(buf, mod_str); 1306 } 1307 1308 *multi_mods = true; 1309 return result; 1310 } 1311 1312 /// Add modifiers from "cmod->cmod_split" to "buf". Set "multi_mods" when one 1313 /// was added. 1314 /// 1315 /// @return the number of bytes added 1316 size_t add_win_cmd_modifiers(char *buf, const cmdmod_T *cmod, bool *multi_mods) 1317 { 1318 size_t result = 0; 1319 1320 // :aboveleft and :leftabove 1321 if (cmod->cmod_split & WSP_ABOVE) { 1322 result += add_cmd_modifier(buf, "aboveleft", multi_mods); 1323 } 1324 // :belowright and :rightbelow 1325 if (cmod->cmod_split & WSP_BELOW) { 1326 result += add_cmd_modifier(buf, "belowright", multi_mods); 1327 } 1328 // :botright 1329 if (cmod->cmod_split & WSP_BOT) { 1330 result += add_cmd_modifier(buf, "botright", multi_mods); 1331 } 1332 1333 // :tab 1334 if (cmod->cmod_tab > 0) { 1335 int tabnr = cmod->cmod_tab - 1; 1336 if (tabnr == tabpage_index(curtab)) { 1337 // For compatibility, don't add a tabpage number if it is the same 1338 // as the default number for :tab. 1339 result += add_cmd_modifier(buf, "tab", multi_mods); 1340 } else { 1341 char tab_buf[NUMBUFLEN + 3]; 1342 snprintf(tab_buf, sizeof(tab_buf), "%dtab", tabnr); 1343 result += add_cmd_modifier(buf, tab_buf, multi_mods); 1344 } 1345 } 1346 1347 // :topleft 1348 if (cmod->cmod_split & WSP_TOP) { 1349 result += add_cmd_modifier(buf, "topleft", multi_mods); 1350 } 1351 // :vertical 1352 if (cmod->cmod_split & WSP_VERT) { 1353 result += add_cmd_modifier(buf, "vertical", multi_mods); 1354 } 1355 // :horizontal 1356 if (cmod->cmod_split & WSP_HOR) { 1357 result += add_cmd_modifier(buf, "horizontal", multi_mods); 1358 } 1359 return result; 1360 } 1361 1362 /// Generate text for the "cmod" command modifiers. 1363 /// If "buf" is NULL just return the length. 1364 size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote) 1365 { 1366 size_t result = 0; 1367 bool multi_mods = false; 1368 1369 typedef struct { 1370 int flag; 1371 char *name; 1372 } mod_entry_T; 1373 static mod_entry_T mod_entries[] = { 1374 { CMOD_BROWSE, "browse" }, 1375 { CMOD_CONFIRM, "confirm" }, 1376 { CMOD_HIDE, "hide" }, 1377 { CMOD_KEEPALT, "keepalt" }, 1378 { CMOD_KEEPJUMPS, "keepjumps" }, 1379 { CMOD_KEEPMARKS, "keepmarks" }, 1380 { CMOD_KEEPPATTERNS, "keeppatterns" }, 1381 { CMOD_LOCKMARKS, "lockmarks" }, 1382 { CMOD_NOSWAPFILE, "noswapfile" }, 1383 { CMOD_UNSILENT, "unsilent" }, 1384 { CMOD_NOAUTOCMD, "noautocmd" }, 1385 { CMOD_SANDBOX, "sandbox" }, 1386 }; 1387 1388 result = quote ? 2 : 0; 1389 if (buf != NULL) { 1390 if (quote) { 1391 *buf++ = '"'; 1392 } 1393 *buf = NUL; 1394 } 1395 1396 // the modifiers that are simple flags 1397 for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) { 1398 if (cmod->cmod_flags & mod_entries[i].flag) { 1399 result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods); 1400 } 1401 } 1402 1403 // :silent 1404 if (cmod->cmod_flags & CMOD_SILENT) { 1405 result += add_cmd_modifier(buf, 1406 (cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!" : "silent", 1407 &multi_mods); 1408 } 1409 // :verbose 1410 if (cmod->cmod_verbose > 0) { 1411 int verbose_value = cmod->cmod_verbose - 1; 1412 if (verbose_value == 1) { 1413 result += add_cmd_modifier(buf, "verbose", &multi_mods); 1414 } else { 1415 char verbose_buf[NUMBUFLEN]; 1416 snprintf(verbose_buf, sizeof(verbose_buf), "%dverbose", verbose_value); 1417 result += add_cmd_modifier(buf, verbose_buf, &multi_mods); 1418 } 1419 } 1420 // flags from cmod->cmod_split 1421 result += add_win_cmd_modifiers(buf, cmod, &multi_mods); 1422 1423 if (quote && buf != NULL) { 1424 buf += result - 2; 1425 *buf = '"'; 1426 } 1427 return result; 1428 } 1429 1430 /// Check for a <> code in a user command. 1431 /// 1432 /// @param code points to the '<'. "len" the length of the <> (inclusive). 1433 /// @param buf is where the result is to be added. 1434 /// @param cmd the user command we're expanding 1435 /// @param eap ex arguments 1436 /// @param split_buf points to a buffer used for splitting, caller should free it. 1437 /// @param split_len is the length of what "split_buf" contains. 1438 /// 1439 /// @return the length of the replacement, which has been added to "buf". 1440 /// Return -1 if there was no match, and only the "<" has been copied. 1441 static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exarg_T *eap, 1442 char **split_buf, size_t *split_len) 1443 { 1444 size_t result = 0; 1445 char *p = code + 1; 1446 size_t l = len - 2; 1447 int quote = 0; 1448 enum { 1449 ct_ARGS, 1450 ct_BANG, 1451 ct_COUNT, 1452 ct_LINE1, 1453 ct_LINE2, 1454 ct_RANGE, 1455 ct_MODS, 1456 ct_REGISTER, 1457 ct_LT, 1458 ct_NONE, 1459 } type = ct_NONE; 1460 1461 if ((vim_strchr("qQfF", (uint8_t)(*p)) != NULL) && p[1] == '-') { 1462 quote = (*p == 'q' || *p == 'Q') ? 1 : 2; 1463 p += 2; 1464 l -= 2; 1465 } 1466 1467 l++; 1468 if (l <= 1) { 1469 // type = ct_NONE; 1470 } else if (STRNICMP(p, "args>", l) == 0) { 1471 type = ct_ARGS; 1472 } else if (STRNICMP(p, "bang>", l) == 0) { 1473 type = ct_BANG; 1474 } else if (STRNICMP(p, "count>", l) == 0) { 1475 type = ct_COUNT; 1476 } else if (STRNICMP(p, "line1>", l) == 0) { 1477 type = ct_LINE1; 1478 } else if (STRNICMP(p, "line2>", l) == 0) { 1479 type = ct_LINE2; 1480 } else if (STRNICMP(p, "range>", l) == 0) { 1481 type = ct_RANGE; 1482 } else if (STRNICMP(p, "lt>", l) == 0) { 1483 type = ct_LT; 1484 } else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0) { 1485 type = ct_REGISTER; 1486 } else if (STRNICMP(p, "mods>", l) == 0) { 1487 type = ct_MODS; 1488 } 1489 1490 switch (type) { 1491 case ct_ARGS: 1492 // Simple case first 1493 if (*eap->arg == NUL) { 1494 if (quote == 1) { 1495 result = 2; 1496 if (buf != NULL) { 1497 STRCPY(buf, "''"); 1498 } 1499 } else { 1500 result = 0; 1501 } 1502 break; 1503 } 1504 1505 // When specified there is a single argument don't split it. 1506 // Works for ":Cmd %" when % is "a b c". 1507 if ((eap->argt & EX_NOSPC) && quote == 2) { 1508 quote = 1; 1509 } 1510 1511 switch (quote) { 1512 case 0: // No quoting, no splitting 1513 result = strlen(eap->arg); 1514 if (buf != NULL) { 1515 STRCPY(buf, eap->arg); 1516 } 1517 break; 1518 case 1: // Quote, but don't split 1519 result = strlen(eap->arg) + 2; 1520 for (p = eap->arg; *p; p++) { 1521 if (*p == '\\' || *p == '"') { 1522 result++; 1523 } 1524 } 1525 1526 if (buf != NULL) { 1527 *buf++ = '"'; 1528 for (p = eap->arg; *p; p++) { 1529 if (*p == '\\' || *p == '"') { 1530 *buf++ = '\\'; 1531 } 1532 *buf++ = *p; 1533 } 1534 *buf = '"'; 1535 } 1536 1537 break; 1538 case 2: // Quote and split (<f-args>) 1539 // This is hard, so only do it once, and cache the result 1540 if (*split_buf == NULL) { 1541 *split_buf = uc_split_args(eap->arg, eap->args, eap->arglens, eap->argc, split_len); 1542 } 1543 1544 result = *split_len; 1545 if (buf != NULL && result != 0) { 1546 STRCPY(buf, *split_buf); 1547 } 1548 1549 break; 1550 } 1551 break; 1552 1553 case ct_BANG: 1554 result = eap->forceit ? 1 : 0; 1555 if (quote) { 1556 result += 2; 1557 } 1558 if (buf != NULL) { 1559 if (quote) { 1560 *buf++ = '"'; 1561 } 1562 if (eap->forceit) { 1563 *buf++ = '!'; 1564 } 1565 if (quote) { 1566 *buf = '"'; 1567 } 1568 } 1569 break; 1570 1571 case ct_LINE1: 1572 case ct_LINE2: 1573 case ct_RANGE: 1574 case ct_COUNT: { 1575 char num_buf[20]; 1576 int64_t num = type == ct_LINE1 1577 ? eap->line1 1578 : (type == ct_LINE2 1579 ? eap->line2 1580 : (type == ct_RANGE 1581 ? eap->addr_count 1582 : (eap->addr_count > 0 ? eap->line2 : cmd->uc_def))); 1583 size_t num_len; 1584 1585 snprintf(num_buf, sizeof(num_buf), "%" PRId64, num); 1586 num_len = strlen(num_buf); 1587 result = num_len; 1588 1589 if (quote) { 1590 result += 2; 1591 } 1592 1593 if (buf != NULL) { 1594 if (quote) { 1595 *buf++ = '"'; 1596 } 1597 STRCPY(buf, num_buf); 1598 buf += num_len; 1599 if (quote) { 1600 *buf = '"'; 1601 } 1602 } 1603 1604 break; 1605 } 1606 1607 case ct_MODS: 1608 result = uc_mods(buf, &cmdmod, quote); 1609 break; 1610 1611 case ct_REGISTER: 1612 result = eap->regname ? 1 : 0; 1613 if (quote) { 1614 result += 2; 1615 } 1616 if (buf != NULL) { 1617 if (quote) { 1618 *buf++ = '\''; 1619 } 1620 if (eap->regname) { 1621 *buf++ = (char)eap->regname; 1622 } 1623 if (quote) { 1624 *buf = '\''; 1625 } 1626 } 1627 break; 1628 1629 case ct_LT: 1630 result = 1; 1631 if (buf != NULL) { 1632 *buf = '<'; 1633 } 1634 break; 1635 1636 default: 1637 // Not recognized: just copy the '<' and return -1. 1638 result = (size_t)-1; 1639 if (buf != NULL) { 1640 *buf = '<'; 1641 } 1642 break; 1643 } 1644 1645 return result; 1646 } 1647 1648 int do_ucmd(exarg_T *eap, bool preview) 1649 { 1650 char *end = NULL; 1651 1652 size_t split_len = 0; 1653 char *split_buf = NULL; 1654 ucmd_T *cmd; 1655 1656 if (eap->cmdidx == CMD_USER) { 1657 cmd = USER_CMD(eap->useridx); 1658 } else { 1659 cmd = USER_CMD_GA(&prevwin_curwin()->w_buffer->b_ucmds, eap->useridx); 1660 } 1661 1662 if (preview) { 1663 assert(cmd->uc_preview_luaref > 0); 1664 return nlua_do_ucmd(cmd, eap, true); 1665 } 1666 1667 if (cmd->uc_luaref > 0) { 1668 nlua_do_ucmd(cmd, eap, false); 1669 return 0; 1670 } 1671 1672 // Replace <> in the command by the arguments. 1673 // First round: "buf" is NULL, compute length, allocate "buf". 1674 // Second round: copy result into "buf". 1675 char *buf = NULL; 1676 while (true) { 1677 char *p = cmd->uc_rep; // source 1678 char *q = buf; // destination 1679 size_t totlen = 0; 1680 1681 while (true) { 1682 char *start = vim_strchr(p, '<'); 1683 if (start != NULL) { 1684 end = vim_strchr(start + 1, '>'); 1685 } 1686 if (buf != NULL) { 1687 char *ksp; 1688 for (ksp = p; *ksp != NUL && (uint8_t)(*ksp) != K_SPECIAL; ksp++) {} 1689 if ((uint8_t)(*ksp) == K_SPECIAL 1690 && (start == NULL || ksp < start || end == NULL) 1691 && ((uint8_t)ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)) { 1692 // K_SPECIAL has been put in the buffer as K_SPECIAL 1693 // KS_SPECIAL KE_FILLER, like for mappings, but 1694 // do_cmdline() doesn't handle that, so convert it back. 1695 size_t len = (size_t)(ksp - p); 1696 if (len > 0) { 1697 memmove(q, p, len); 1698 q += len; 1699 } 1700 *q++ = (char)K_SPECIAL; 1701 p = ksp + 3; 1702 continue; 1703 } 1704 } 1705 1706 // break if no <item> is found 1707 if (start == NULL || end == NULL) { 1708 break; 1709 } 1710 1711 // Include the '>' 1712 end++; 1713 1714 // Take everything up to the '<' 1715 size_t len = (size_t)(start - p); 1716 if (buf == NULL) { 1717 totlen += len; 1718 } else { 1719 memmove(q, p, len); 1720 q += len; 1721 } 1722 1723 len = uc_check_code(start, (size_t)(end - start), q, cmd, eap, &split_buf, &split_len); 1724 if (len == (size_t)-1) { 1725 // no match, continue after '<' 1726 p = start + 1; 1727 len = 1; 1728 } else { 1729 p = end; 1730 } 1731 if (buf == NULL) { 1732 totlen += len; 1733 } else { 1734 q += len; 1735 } 1736 } 1737 if (buf != NULL) { // second time here, finished 1738 STRCPY(q, p); 1739 break; 1740 } 1741 1742 totlen += strlen(p); // Add on the trailing characters 1743 buf = xmalloc(totlen + 1); 1744 } 1745 1746 sctx_T save_current_sctx; 1747 bool restore_current_sctx = false; 1748 if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { 1749 restore_current_sctx = true; 1750 save_current_sctx = current_sctx; 1751 current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; 1752 } 1753 do_cmdline(buf, eap->ea_getline, eap->cookie, 1754 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); 1755 1756 // Careful: Do not use "cmd" here, it may have become invalid if a user 1757 // command was added. 1758 if (restore_current_sctx) { 1759 current_sctx = save_current_sctx; 1760 } 1761 xfree(buf); 1762 xfree(split_buf); 1763 1764 return 0; 1765 } 1766 1767 /// Gets a map of maps describing user-commands defined for buffer `buf` or 1768 /// defined globally if `buf` is NULL. 1769 /// 1770 /// @param buf Buffer to inspect, or NULL to get global commands. 1771 /// 1772 /// @return Map of maps describing commands 1773 Dict commands_array(buf_T *buf, Arena *arena) 1774 { 1775 garray_T *gap = (buf == NULL) ? &ucmds : &buf->b_ucmds; 1776 1777 Dict rv = arena_dict(arena, (size_t)gap->ga_len); 1778 for (int i = 0; i < gap->ga_len; i++) { 1779 char arg[2] = { 0, 0 }; 1780 Dict d = arena_dict(arena, 16); 1781 ucmd_T *cmd = USER_CMD_GA(gap, i); 1782 1783 PUT_C(d, "name", CSTR_AS_OBJ(cmd->uc_name)); 1784 PUT_C(d, "definition", CSTR_AS_OBJ(cmd->uc_rep)); 1785 PUT_C(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid)); 1786 PUT_C(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); 1787 PUT_C(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); 1788 PUT_C(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); 1789 PUT_C(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT))); 1790 1791 if (cmd->uc_preview_luaref != LUA_NOREF) { 1792 PUT_C(d, "preview", LUAREF_OBJ(api_new_luaref(cmd->uc_preview_luaref))); 1793 } 1794 1795 if (cmd->uc_luaref != LUA_NOREF) { 1796 PUT_C(d, "callback", LUAREF_OBJ(api_new_luaref(cmd->uc_luaref))); 1797 } 1798 1799 switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { 1800 case 0: 1801 arg[0] = '0'; break; 1802 case (EX_EXTRA): 1803 arg[0] = '*'; break; 1804 case (EX_EXTRA | EX_NOSPC): 1805 arg[0] = '?'; break; 1806 case (EX_EXTRA | EX_NEEDARG): 1807 arg[0] = '+'; break; 1808 case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): 1809 arg[0] = '1'; break; 1810 } 1811 PUT_C(d, "nargs", CSTR_TO_ARENA_OBJ(arena, arg)); 1812 1813 if (cmd->uc_compl_luaref != LUA_NOREF) { 1814 PUT_C(d, "complete", LUAREF_OBJ(api_new_luaref(cmd->uc_compl_luaref))); 1815 } else { 1816 char *cmd_compl = get_command_complete(cmd->uc_compl); 1817 PUT_C(d, "complete", (cmd_compl == NULL ? NIL : CSTR_AS_OBJ(cmd_compl))); 1818 } 1819 PUT_C(d, "complete_arg", cmd->uc_compl_arg == NULL 1820 ? NIL : CSTR_AS_OBJ(cmd->uc_compl_arg)); 1821 1822 Object obj = NIL; 1823 if (cmd->uc_argt & EX_COUNT) { 1824 if (cmd->uc_def >= 0) { 1825 obj = STRING_OBJ(arena_printf(arena, "%" PRId64, cmd->uc_def)); // -count=N 1826 } else { 1827 obj = CSTR_AS_OBJ("0"); // -count 1828 } 1829 } 1830 PUT_C(d, "count", obj); 1831 1832 obj = NIL; 1833 if (cmd->uc_argt & EX_RANGE) { 1834 if (cmd->uc_argt & EX_DFLALL) { 1835 obj = STATIC_CSTR_AS_OBJ("%"); // -range=% 1836 } else if (cmd->uc_def >= 0) { 1837 obj = STRING_OBJ(arena_printf(arena, "%" PRId64, cmd->uc_def)); // -range=N 1838 } else { 1839 obj = STATIC_CSTR_AS_OBJ("."); // -range 1840 } 1841 } 1842 PUT_C(d, "range", obj); 1843 1844 obj = NIL; 1845 for (int j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { 1846 if (addr_type_complete[j].expand != ADDR_LINES 1847 && addr_type_complete[j].expand == cmd->uc_addr_type) { 1848 obj = CSTR_AS_OBJ(addr_type_complete[j].name); 1849 break; 1850 } 1851 } 1852 PUT_C(d, "addr", obj); 1853 1854 PUT_C(rv, cmd->uc_name, DICT_OBJ(d)); 1855 } 1856 return rv; 1857 }