Mercurial > vim
comparison src/usercmd.c @ 16411:5b5c5daf57de v8.1.1210
patch 8.1.1210: support for user commands is spread out
commit https://github.com/vim/vim/commit/ac9fb18020d7e8bf16d02d45fbb02cf47328aaf7
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Apr 27 13:04:13 2019 +0200
patch 8.1.1210: support for user commands is spread out
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.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 27 Apr 2019 13:15:07 +0200 |
parents | |
children | a2befc9b3729 |
comparison
equal
deleted
inserted
replaced
16410:eb356c5e6287 | 16411:5b5c5daf57de |
---|---|
1 /* vi:set ts=8 sts=4 sw=4 noet: | |
2 * | |
3 * VIM - Vi IMproved by Bram Moolenaar | |
4 * | |
5 * Do ":help uganda" in Vim to read copying and usage conditions. | |
6 * Do ":help credits" in Vim to see a list of people who contributed. | |
7 * See README.txt for an overview of the Vim source code. | |
8 */ | |
9 | |
10 /* | |
11 * usercmd.c: User defined command support | |
12 */ | |
13 | |
14 #include "vim.h" | |
15 | |
16 typedef struct ucmd | |
17 { | |
18 char_u *uc_name; // The command name | |
19 long_u uc_argt; // The argument type | |
20 char_u *uc_rep; // The command's replacement string | |
21 long uc_def; // The default value for a range/count | |
22 int uc_compl; // completion type | |
23 int uc_addr_type; // The command's address type | |
24 # ifdef FEAT_EVAL | |
25 sctx_T uc_script_ctx; // SCTX where the command was defined | |
26 # ifdef FEAT_CMDL_COMPL | |
27 char_u *uc_compl_arg; // completion argument if any | |
28 # endif | |
29 # endif | |
30 } ucmd_T; | |
31 | |
32 // List of all user commands. | |
33 static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL}; | |
34 | |
35 #define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i]) | |
36 #define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) | |
37 | |
38 /* | |
39 * List of names for completion for ":command" with the EXPAND_ flag. | |
40 * Must be alphabetical for completion. | |
41 */ | |
42 static struct | |
43 { | |
44 int expand; | |
45 char *name; | |
46 } command_complete[] = | |
47 { | |
48 {EXPAND_ARGLIST, "arglist"}, | |
49 {EXPAND_AUGROUP, "augroup"}, | |
50 {EXPAND_BEHAVE, "behave"}, | |
51 {EXPAND_BUFFERS, "buffer"}, | |
52 {EXPAND_COLORS, "color"}, | |
53 {EXPAND_COMMANDS, "command"}, | |
54 {EXPAND_COMPILER, "compiler"}, | |
55 #if defined(FEAT_CSCOPE) | |
56 {EXPAND_CSCOPE, "cscope"}, | |
57 #endif | |
58 #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
59 {EXPAND_USER_DEFINED, "custom"}, | |
60 {EXPAND_USER_LIST, "customlist"}, | |
61 #endif | |
62 {EXPAND_DIRECTORIES, "dir"}, | |
63 {EXPAND_ENV_VARS, "environment"}, | |
64 {EXPAND_EVENTS, "event"}, | |
65 {EXPAND_EXPRESSION, "expression"}, | |
66 {EXPAND_FILES, "file"}, | |
67 {EXPAND_FILES_IN_PATH, "file_in_path"}, | |
68 {EXPAND_FILETYPE, "filetype"}, | |
69 {EXPAND_FUNCTIONS, "function"}, | |
70 {EXPAND_HELP, "help"}, | |
71 {EXPAND_HIGHLIGHT, "highlight"}, | |
72 #if defined(FEAT_CMDHIST) | |
73 {EXPAND_HISTORY, "history"}, | |
74 #endif | |
75 #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) | |
76 {EXPAND_LOCALES, "locale"}, | |
77 #endif | |
78 {EXPAND_MAPCLEAR, "mapclear"}, | |
79 {EXPAND_MAPPINGS, "mapping"}, | |
80 {EXPAND_MENUS, "menu"}, | |
81 {EXPAND_MESSAGES, "messages"}, | |
82 {EXPAND_OWNSYNTAX, "syntax"}, | |
83 #if defined(FEAT_PROFILE) | |
84 {EXPAND_SYNTIME, "syntime"}, | |
85 #endif | |
86 {EXPAND_SETTINGS, "option"}, | |
87 {EXPAND_PACKADD, "packadd"}, | |
88 {EXPAND_SHELLCMD, "shellcmd"}, | |
89 #if defined(FEAT_SIGNS) | |
90 {EXPAND_SIGN, "sign"}, | |
91 #endif | |
92 {EXPAND_TAGS, "tag"}, | |
93 {EXPAND_TAGS_LISTFILES, "tag_listfiles"}, | |
94 {EXPAND_USER, "user"}, | |
95 {EXPAND_USER_VARS, "var"}, | |
96 {0, NULL} | |
97 }; | |
98 | |
99 /* | |
100 * List of names of address types. Must be alphabetical for completion. | |
101 */ | |
102 static struct | |
103 { | |
104 int expand; | |
105 char *name; | |
106 char *shortname; | |
107 } addr_type_complete[] = | |
108 { | |
109 {ADDR_ARGUMENTS, "arguments", "arg"}, | |
110 {ADDR_LINES, "lines", "line"}, | |
111 {ADDR_LOADED_BUFFERS, "loaded_buffers", "load"}, | |
112 {ADDR_TABS, "tabs", "tab"}, | |
113 {ADDR_BUFFERS, "buffers", "buf"}, | |
114 {ADDR_WINDOWS, "windows", "win"}, | |
115 {ADDR_QUICKFIX, "quickfix", "qf"}, | |
116 {ADDR_OTHER, "other", "?"}, | |
117 {-1, NULL, NULL} | |
118 }; | |
119 | |
120 #define UC_BUFFER 1 // -buffer: local to current buffer | |
121 | |
122 /* | |
123 * Search for a user command that matches "eap->cmd". | |
124 * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". | |
125 * Return a pointer to just after the command. | |
126 * Return NULL if there is no matching command. | |
127 */ | |
128 char_u * | |
129 find_ucmd( | |
130 exarg_T *eap, | |
131 char_u *p, // end of the command (possibly including count) | |
132 int *full, // set to TRUE for a full match | |
133 expand_T *xp, // used for completion, NULL otherwise | |
134 int *compl UNUSED) // completion flags or NULL | |
135 { | |
136 int len = (int)(p - eap->cmd); | |
137 int j, k, matchlen = 0; | |
138 ucmd_T *uc; | |
139 int found = FALSE; | |
140 int possible = FALSE; | |
141 char_u *cp, *np; // Point into typed cmd and test name | |
142 garray_T *gap; | |
143 int amb_local = FALSE; // Found ambiguous buffer-local command, | |
144 // only full match global is accepted. | |
145 | |
146 /* | |
147 * Look for buffer-local user commands first, then global ones. | |
148 */ | |
149 gap = &curbuf->b_ucmds; | |
150 for (;;) | |
151 { | |
152 for (j = 0; j < gap->ga_len; ++j) | |
153 { | |
154 uc = USER_CMD_GA(gap, j); | |
155 cp = eap->cmd; | |
156 np = uc->uc_name; | |
157 k = 0; | |
158 while (k < len && *np != NUL && *cp++ == *np++) | |
159 k++; | |
160 if (k == len || (*np == NUL && vim_isdigit(eap->cmd[k]))) | |
161 { | |
162 // If finding a second match, the command is ambiguous. But | |
163 // not if a buffer-local command wasn't a full match and a | |
164 // global command is a full match. | |
165 if (k == len && found && *np != NUL) | |
166 { | |
167 if (gap == &ucmds) | |
168 return NULL; | |
169 amb_local = TRUE; | |
170 } | |
171 | |
172 if (!found || (k == len && *np == NUL)) | |
173 { | |
174 // If we matched up to a digit, then there could | |
175 // be another command including the digit that we | |
176 // should use instead. | |
177 if (k == len) | |
178 found = TRUE; | |
179 else | |
180 possible = TRUE; | |
181 | |
182 if (gap == &ucmds) | |
183 eap->cmdidx = CMD_USER; | |
184 else | |
185 eap->cmdidx = CMD_USER_BUF; | |
186 eap->argt = (long)uc->uc_argt; | |
187 eap->useridx = j; | |
188 eap->addr_type = uc->uc_addr_type; | |
189 | |
190 # ifdef FEAT_CMDL_COMPL | |
191 if (compl != NULL) | |
192 *compl = uc->uc_compl; | |
193 # ifdef FEAT_EVAL | |
194 if (xp != NULL) | |
195 { | |
196 xp->xp_arg = uc->uc_compl_arg; | |
197 xp->xp_script_ctx = uc->uc_script_ctx; | |
198 xp->xp_script_ctx.sc_lnum += sourcing_lnum; | |
199 } | |
200 # endif | |
201 # endif | |
202 // Do not search for further abbreviations | |
203 // if this is an exact match. | |
204 matchlen = k; | |
205 if (k == len && *np == NUL) | |
206 { | |
207 if (full != NULL) | |
208 *full = TRUE; | |
209 amb_local = FALSE; | |
210 break; | |
211 } | |
212 } | |
213 } | |
214 } | |
215 | |
216 // Stop if we found a full match or searched all. | |
217 if (j < gap->ga_len || gap == &ucmds) | |
218 break; | |
219 gap = &ucmds; | |
220 } | |
221 | |
222 // Only found ambiguous matches. | |
223 if (amb_local) | |
224 { | |
225 if (xp != NULL) | |
226 xp->xp_context = EXPAND_UNSUCCESSFUL; | |
227 return NULL; | |
228 } | |
229 | |
230 // The match we found may be followed immediately by a number. Move "p" | |
231 // back to point to it. | |
232 if (found || possible) | |
233 return p + (matchlen - len); | |
234 return p; | |
235 } | |
236 | |
237 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) | |
238 | |
239 char_u * | |
240 set_context_in_user_cmd(expand_T *xp, char_u *arg_in) | |
241 { | |
242 char_u *arg = arg_in; | |
243 char_u *p; | |
244 | |
245 // Check for attributes | |
246 while (*arg == '-') | |
247 { | |
248 arg++; // Skip "-" | |
249 p = skiptowhite(arg); | |
250 if (*p == NUL) | |
251 { | |
252 // Cursor is still in the attribute | |
253 p = vim_strchr(arg, '='); | |
254 if (p == NULL) | |
255 { | |
256 // No "=", so complete attribute names | |
257 xp->xp_context = EXPAND_USER_CMD_FLAGS; | |
258 xp->xp_pattern = arg; | |
259 return NULL; | |
260 } | |
261 | |
262 // For the -complete, -nargs and -addr attributes, we complete | |
263 // their arguments as well. | |
264 if (STRNICMP(arg, "complete", p - arg) == 0) | |
265 { | |
266 xp->xp_context = EXPAND_USER_COMPLETE; | |
267 xp->xp_pattern = p + 1; | |
268 return NULL; | |
269 } | |
270 else if (STRNICMP(arg, "nargs", p - arg) == 0) | |
271 { | |
272 xp->xp_context = EXPAND_USER_NARGS; | |
273 xp->xp_pattern = p + 1; | |
274 return NULL; | |
275 } | |
276 else if (STRNICMP(arg, "addr", p - arg) == 0) | |
277 { | |
278 xp->xp_context = EXPAND_USER_ADDR_TYPE; | |
279 xp->xp_pattern = p + 1; | |
280 return NULL; | |
281 } | |
282 return NULL; | |
283 } | |
284 arg = skipwhite(p); | |
285 } | |
286 | |
287 // After the attributes comes the new command name | |
288 p = skiptowhite(arg); | |
289 if (*p == NUL) | |
290 { | |
291 xp->xp_context = EXPAND_USER_COMMANDS; | |
292 xp->xp_pattern = arg; | |
293 return NULL; | |
294 } | |
295 | |
296 // And finally comes a normal command | |
297 return skipwhite(p); | |
298 } | |
299 | |
300 char_u * | |
301 get_user_command_name(int idx) | |
302 { | |
303 return get_user_commands(NULL, idx - (int)CMD_SIZE); | |
304 } | |
305 | |
306 /* | |
307 * Function given to ExpandGeneric() to obtain the list of user command names. | |
308 */ | |
309 char_u * | |
310 get_user_commands(expand_T *xp UNUSED, int idx) | |
311 { | |
312 if (idx < curbuf->b_ucmds.ga_len) | |
313 return USER_CMD_GA(&curbuf->b_ucmds, idx)->uc_name; | |
314 idx -= curbuf->b_ucmds.ga_len; | |
315 if (idx < ucmds.ga_len) | |
316 return USER_CMD(idx)->uc_name; | |
317 return NULL; | |
318 } | |
319 | |
320 /* | |
321 * Function given to ExpandGeneric() to obtain the list of user address type | |
322 * names. | |
323 */ | |
324 char_u * | |
325 get_user_cmd_addr_type(expand_T *xp UNUSED, int idx) | |
326 { | |
327 return (char_u *)addr_type_complete[idx].name; | |
328 } | |
329 | |
330 /* | |
331 * Function given to ExpandGeneric() to obtain the list of user command | |
332 * attributes. | |
333 */ | |
334 char_u * | |
335 get_user_cmd_flags(expand_T *xp UNUSED, int idx) | |
336 { | |
337 static char *user_cmd_flags[] = { | |
338 "addr", "bang", "bar", "buffer", "complete", | |
339 "count", "nargs", "range", "register" | |
340 }; | |
341 | |
342 if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0]))) | |
343 return NULL; | |
344 return (char_u *)user_cmd_flags[idx]; | |
345 } | |
346 | |
347 /* | |
348 * Function given to ExpandGeneric() to obtain the list of values for -nargs. | |
349 */ | |
350 char_u * | |
351 get_user_cmd_nargs(expand_T *xp UNUSED, int idx) | |
352 { | |
353 static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"}; | |
354 | |
355 if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0]))) | |
356 return NULL; | |
357 return (char_u *)user_cmd_nargs[idx]; | |
358 } | |
359 | |
360 /* | |
361 * Function given to ExpandGeneric() to obtain the list of values for | |
362 * -complete. | |
363 */ | |
364 char_u * | |
365 get_user_cmd_complete(expand_T *xp UNUSED, int idx) | |
366 { | |
367 return (char_u *)command_complete[idx].name; | |
368 } | |
369 | |
370 int | |
371 cmdcomplete_str_to_type(char_u *complete_str) | |
372 { | |
373 int i; | |
374 | |
375 for (i = 0; command_complete[i].expand != 0; ++i) | |
376 if (STRCMP(complete_str, command_complete[i].name) == 0) | |
377 return command_complete[i].expand; | |
378 | |
379 return EXPAND_NOTHING; | |
380 } | |
381 | |
382 #endif // FEAT_CMDL_COMPL | |
383 | |
384 /* | |
385 * List user commands starting with "name[name_len]". | |
386 */ | |
387 static void | |
388 uc_list(char_u *name, size_t name_len) | |
389 { | |
390 int i, j; | |
391 int found = FALSE; | |
392 ucmd_T *cmd; | |
393 int len; | |
394 int over; | |
395 long a; | |
396 garray_T *gap; | |
397 | |
398 gap = &curbuf->b_ucmds; | |
399 for (;;) | |
400 { | |
401 for (i = 0; i < gap->ga_len; ++i) | |
402 { | |
403 cmd = USER_CMD_GA(gap, i); | |
404 a = (long)cmd->uc_argt; | |
405 | |
406 // Skip commands which don't match the requested prefix and | |
407 // commands filtered out. | |
408 if (STRNCMP(name, cmd->uc_name, name_len) != 0 | |
409 || message_filtered(cmd->uc_name)) | |
410 continue; | |
411 | |
412 // Put out the title first time | |
413 if (!found) | |
414 msg_puts_title(_("\n Name Args Address Complete Definition")); | |
415 found = TRUE; | |
416 msg_putchar('\n'); | |
417 if (got_int) | |
418 break; | |
419 | |
420 // Special cases | |
421 len = 4; | |
422 if (a & BANG) | |
423 { | |
424 msg_putchar('!'); | |
425 --len; | |
426 } | |
427 if (a & REGSTR) | |
428 { | |
429 msg_putchar('"'); | |
430 --len; | |
431 } | |
432 if (gap != &ucmds) | |
433 { | |
434 msg_putchar('b'); | |
435 --len; | |
436 } | |
437 if (a & TRLBAR) | |
438 { | |
439 msg_putchar('|'); | |
440 --len; | |
441 } | |
442 while (len-- > 0) | |
443 msg_putchar(' '); | |
444 | |
445 msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D)); | |
446 len = (int)STRLEN(cmd->uc_name) + 4; | |
447 | |
448 do { | |
449 msg_putchar(' '); | |
450 ++len; | |
451 } while (len < 22); | |
452 | |
453 // "over" is how much longer the name is than the column width for | |
454 // the name, we'll try to align what comes after. | |
455 over = len - 22; | |
456 len = 0; | |
457 | |
458 // Arguments | |
459 switch ((int)(a & (EXTRA|NOSPC|NEEDARG))) | |
460 { | |
461 case 0: IObuff[len++] = '0'; break; | |
462 case (EXTRA): IObuff[len++] = '*'; break; | |
463 case (EXTRA|NOSPC): IObuff[len++] = '?'; break; | |
464 case (EXTRA|NEEDARG): IObuff[len++] = '+'; break; | |
465 case (EXTRA|NOSPC|NEEDARG): IObuff[len++] = '1'; break; | |
466 } | |
467 | |
468 do { | |
469 IObuff[len++] = ' '; | |
470 } while (len < 5 - over); | |
471 | |
472 // Address / Range | |
473 if (a & (RANGE|COUNT)) | |
474 { | |
475 if (a & COUNT) | |
476 { | |
477 // -count=N | |
478 sprintf((char *)IObuff + len, "%ldc", cmd->uc_def); | |
479 len += (int)STRLEN(IObuff + len); | |
480 } | |
481 else if (a & DFLALL) | |
482 IObuff[len++] = '%'; | |
483 else if (cmd->uc_def >= 0) | |
484 { | |
485 // -range=N | |
486 sprintf((char *)IObuff + len, "%ld", cmd->uc_def); | |
487 len += (int)STRLEN(IObuff + len); | |
488 } | |
489 else | |
490 IObuff[len++] = '.'; | |
491 } | |
492 | |
493 do { | |
494 IObuff[len++] = ' '; | |
495 } while (len < 8 - over); | |
496 | |
497 // Address Type | |
498 for (j = 0; addr_type_complete[j].expand != -1; ++j) | |
499 if (addr_type_complete[j].expand != ADDR_LINES | |
500 && addr_type_complete[j].expand == cmd->uc_addr_type) | |
501 { | |
502 STRCPY(IObuff + len, addr_type_complete[j].shortname); | |
503 len += (int)STRLEN(IObuff + len); | |
504 break; | |
505 } | |
506 | |
507 do { | |
508 IObuff[len++] = ' '; | |
509 } while (len < 13 - over); | |
510 | |
511 // Completion | |
512 for (j = 0; command_complete[j].expand != 0; ++j) | |
513 if (command_complete[j].expand == cmd->uc_compl) | |
514 { | |
515 STRCPY(IObuff + len, command_complete[j].name); | |
516 len += (int)STRLEN(IObuff + len); | |
517 break; | |
518 } | |
519 | |
520 do { | |
521 IObuff[len++] = ' '; | |
522 } while (len < 25 - over); | |
523 | |
524 IObuff[len] = '\0'; | |
525 msg_outtrans(IObuff); | |
526 | |
527 msg_outtrans_special(cmd->uc_rep, FALSE, | |
528 name_len == 0 ? Columns - 47 : 0); | |
529 #ifdef FEAT_EVAL | |
530 if (p_verbose > 0) | |
531 last_set_msg(cmd->uc_script_ctx); | |
532 #endif | |
533 out_flush(); | |
534 ui_breakcheck(); | |
535 if (got_int) | |
536 break; | |
537 } | |
538 if (gap == &ucmds || i < gap->ga_len) | |
539 break; | |
540 gap = &ucmds; | |
541 } | |
542 | |
543 if (!found) | |
544 msg(_("No user-defined commands found")); | |
545 } | |
546 | |
547 char * | |
548 uc_fun_cmd(void) | |
549 { | |
550 static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4, | |
551 0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60, | |
552 0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2, | |
553 0xb9, 0x7f, 0}; | |
554 int i; | |
555 | |
556 for (i = 0; fcmd[i]; ++i) | |
557 IObuff[i] = fcmd[i] - 0x40; | |
558 IObuff[i] = 0; | |
559 return (char *)IObuff; | |
560 } | |
561 | |
562 /* | |
563 * Parse address type argument | |
564 */ | |
565 static int | |
566 parse_addr_type_arg( | |
567 char_u *value, | |
568 int vallen, | |
569 long *argt, | |
570 int *addr_type_arg) | |
571 { | |
572 int i, a, b; | |
573 | |
574 for (i = 0; addr_type_complete[i].expand != -1; ++i) | |
575 { | |
576 a = (int)STRLEN(addr_type_complete[i].name) == vallen; | |
577 b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; | |
578 if (a && b) | |
579 { | |
580 *addr_type_arg = addr_type_complete[i].expand; | |
581 break; | |
582 } | |
583 } | |
584 | |
585 if (addr_type_complete[i].expand == -1) | |
586 { | |
587 char_u *err = value; | |
588 | |
589 for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++) | |
590 ; | |
591 err[i] = NUL; | |
592 semsg(_("E180: Invalid address type value: %s"), err); | |
593 return FAIL; | |
594 } | |
595 | |
596 if (*addr_type_arg != ADDR_LINES) | |
597 *argt |= NOTADR; | |
598 | |
599 return OK; | |
600 } | |
601 | |
602 /* | |
603 * Parse a completion argument "value[vallen]". | |
604 * The detected completion goes in "*complp", argument type in "*argt". | |
605 * When there is an argument, for function and user defined completion, it's | |
606 * copied to allocated memory and stored in "*compl_arg". | |
607 * Returns FAIL if something is wrong. | |
608 */ | |
609 int | |
610 parse_compl_arg( | |
611 char_u *value, | |
612 int vallen, | |
613 int *complp, | |
614 long *argt, | |
615 char_u **compl_arg UNUSED) | |
616 { | |
617 char_u *arg = NULL; | |
618 # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
619 size_t arglen = 0; | |
620 # endif | |
621 int i; | |
622 int valend = vallen; | |
623 | |
624 // Look for any argument part - which is the part after any ',' | |
625 for (i = 0; i < vallen; ++i) | |
626 { | |
627 if (value[i] == ',') | |
628 { | |
629 arg = &value[i + 1]; | |
630 # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
631 arglen = vallen - i - 1; | |
632 # endif | |
633 valend = i; | |
634 break; | |
635 } | |
636 } | |
637 | |
638 for (i = 0; command_complete[i].expand != 0; ++i) | |
639 { | |
640 if ((int)STRLEN(command_complete[i].name) == valend | |
641 && STRNCMP(value, command_complete[i].name, valend) == 0) | |
642 { | |
643 *complp = command_complete[i].expand; | |
644 if (command_complete[i].expand == EXPAND_BUFFERS) | |
645 *argt |= BUFNAME; | |
646 else if (command_complete[i].expand == EXPAND_DIRECTORIES | |
647 || command_complete[i].expand == EXPAND_FILES) | |
648 *argt |= XFILE; | |
649 break; | |
650 } | |
651 } | |
652 | |
653 if (command_complete[i].expand == 0) | |
654 { | |
655 semsg(_("E180: Invalid complete value: %s"), value); | |
656 return FAIL; | |
657 } | |
658 | |
659 # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
660 if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST | |
661 && arg != NULL) | |
662 # else | |
663 if (arg != NULL) | |
664 # endif | |
665 { | |
666 emsg(_("E468: Completion argument only allowed for custom completion")); | |
667 return FAIL; | |
668 } | |
669 | |
670 # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
671 if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST) | |
672 && arg == NULL) | |
673 { | |
674 emsg(_("E467: Custom completion requires a function argument")); | |
675 return FAIL; | |
676 } | |
677 | |
678 if (arg != NULL) | |
679 *compl_arg = vim_strnsave(arg, (int)arglen); | |
680 # endif | |
681 return OK; | |
682 } | |
683 | |
684 /* | |
685 * Scan attributes in the ":command" command. | |
686 * Return FAIL when something is wrong. | |
687 */ | |
688 static int | |
689 uc_scan_attr( | |
690 char_u *attr, | |
691 size_t len, | |
692 long *argt, | |
693 long *def, | |
694 int *flags, | |
695 int *compl, | |
696 char_u **compl_arg, | |
697 int *addr_type_arg) | |
698 { | |
699 char_u *p; | |
700 | |
701 if (len == 0) | |
702 { | |
703 emsg(_("E175: No attribute specified")); | |
704 return FAIL; | |
705 } | |
706 | |
707 // First, try the simple attributes (no arguments) | |
708 if (STRNICMP(attr, "bang", len) == 0) | |
709 *argt |= BANG; | |
710 else if (STRNICMP(attr, "buffer", len) == 0) | |
711 *flags |= UC_BUFFER; | |
712 else if (STRNICMP(attr, "register", len) == 0) | |
713 *argt |= REGSTR; | |
714 else if (STRNICMP(attr, "bar", len) == 0) | |
715 *argt |= TRLBAR; | |
716 else | |
717 { | |
718 int i; | |
719 char_u *val = NULL; | |
720 size_t vallen = 0; | |
721 size_t attrlen = len; | |
722 | |
723 // Look for the attribute name - which is the part before any '=' | |
724 for (i = 0; i < (int)len; ++i) | |
725 { | |
726 if (attr[i] == '=') | |
727 { | |
728 val = &attr[i + 1]; | |
729 vallen = len - i - 1; | |
730 attrlen = i; | |
731 break; | |
732 } | |
733 } | |
734 | |
735 if (STRNICMP(attr, "nargs", attrlen) == 0) | |
736 { | |
737 if (vallen == 1) | |
738 { | |
739 if (*val == '0') | |
740 // Do nothing - this is the default | |
741 ; | |
742 else if (*val == '1') | |
743 *argt |= (EXTRA | NOSPC | NEEDARG); | |
744 else if (*val == '*') | |
745 *argt |= EXTRA; | |
746 else if (*val == '?') | |
747 *argt |= (EXTRA | NOSPC); | |
748 else if (*val == '+') | |
749 *argt |= (EXTRA | NEEDARG); | |
750 else | |
751 goto wrong_nargs; | |
752 } | |
753 else | |
754 { | |
755 wrong_nargs: | |
756 emsg(_("E176: Invalid number of arguments")); | |
757 return FAIL; | |
758 } | |
759 } | |
760 else if (STRNICMP(attr, "range", attrlen) == 0) | |
761 { | |
762 *argt |= RANGE; | |
763 if (vallen == 1 && *val == '%') | |
764 *argt |= DFLALL; | |
765 else if (val != NULL) | |
766 { | |
767 p = val; | |
768 if (*def >= 0) | |
769 { | |
770 two_count: | |
771 emsg(_("E177: Count cannot be specified twice")); | |
772 return FAIL; | |
773 } | |
774 | |
775 *def = getdigits(&p); | |
776 *argt |= (ZEROR | NOTADR); | |
777 | |
778 if (p != val + vallen || vallen == 0) | |
779 { | |
780 invalid_count: | |
781 emsg(_("E178: Invalid default value for count")); | |
782 return FAIL; | |
783 } | |
784 } | |
785 } | |
786 else if (STRNICMP(attr, "count", attrlen) == 0) | |
787 { | |
788 *argt |= (COUNT | ZEROR | RANGE | NOTADR); | |
789 | |
790 if (val != NULL) | |
791 { | |
792 p = val; | |
793 if (*def >= 0) | |
794 goto two_count; | |
795 | |
796 *def = getdigits(&p); | |
797 | |
798 if (p != val + vallen) | |
799 goto invalid_count; | |
800 } | |
801 | |
802 if (*def < 0) | |
803 *def = 0; | |
804 } | |
805 else if (STRNICMP(attr, "complete", attrlen) == 0) | |
806 { | |
807 if (val == NULL) | |
808 { | |
809 emsg(_("E179: argument required for -complete")); | |
810 return FAIL; | |
811 } | |
812 | |
813 if (parse_compl_arg(val, (int)vallen, compl, argt, compl_arg) | |
814 == FAIL) | |
815 return FAIL; | |
816 } | |
817 else if (STRNICMP(attr, "addr", attrlen) == 0) | |
818 { | |
819 *argt |= RANGE; | |
820 if (val == NULL) | |
821 { | |
822 emsg(_("E179: argument required for -addr")); | |
823 return FAIL; | |
824 } | |
825 if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) | |
826 == FAIL) | |
827 return FAIL; | |
828 if (addr_type_arg != ADDR_LINES) | |
829 *argt |= (ZEROR | NOTADR) ; | |
830 } | |
831 else | |
832 { | |
833 char_u ch = attr[len]; | |
834 attr[len] = '\0'; | |
835 semsg(_("E181: Invalid attribute: %s"), attr); | |
836 attr[len] = ch; | |
837 return FAIL; | |
838 } | |
839 } | |
840 | |
841 return OK; | |
842 } | |
843 | |
844 /* | |
845 * Add a user command to the list or replace an existing one. | |
846 */ | |
847 static int | |
848 uc_add_command( | |
849 char_u *name, | |
850 size_t name_len, | |
851 char_u *rep, | |
852 long argt, | |
853 long def, | |
854 int flags, | |
855 int compl, | |
856 char_u *compl_arg UNUSED, | |
857 int addr_type, | |
858 int force) | |
859 { | |
860 ucmd_T *cmd = NULL; | |
861 char_u *p; | |
862 int i; | |
863 int cmp = 1; | |
864 char_u *rep_buf = NULL; | |
865 garray_T *gap; | |
866 | |
867 replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); | |
868 if (rep_buf == NULL) | |
869 { | |
870 // Can't replace termcodes - try using the string as is | |
871 rep_buf = vim_strsave(rep); | |
872 | |
873 // Give up if out of memory | |
874 if (rep_buf == NULL) | |
875 return FAIL; | |
876 } | |
877 | |
878 // get address of growarray: global or in curbuf | |
879 if (flags & UC_BUFFER) | |
880 { | |
881 gap = &curbuf->b_ucmds; | |
882 if (gap->ga_itemsize == 0) | |
883 ga_init2(gap, (int)sizeof(ucmd_T), 4); | |
884 } | |
885 else | |
886 gap = &ucmds; | |
887 | |
888 // Search for the command in the already defined commands. | |
889 for (i = 0; i < gap->ga_len; ++i) | |
890 { | |
891 size_t len; | |
892 | |
893 cmd = USER_CMD_GA(gap, i); | |
894 len = STRLEN(cmd->uc_name); | |
895 cmp = STRNCMP(name, cmd->uc_name, name_len); | |
896 if (cmp == 0) | |
897 { | |
898 if (name_len < len) | |
899 cmp = -1; | |
900 else if (name_len > len) | |
901 cmp = 1; | |
902 } | |
903 | |
904 if (cmp == 0) | |
905 { | |
906 // Command can be replaced with "command!" and when sourcing the | |
907 // same script again, but only once. | |
908 if (!force | |
909 #ifdef FEAT_EVAL | |
910 && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid | |
911 || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq) | |
912 #endif | |
913 ) | |
914 { | |
915 semsg(_("E174: Command already exists: add ! to replace it: %s"), | |
916 name); | |
917 goto fail; | |
918 } | |
919 | |
920 VIM_CLEAR(cmd->uc_rep); | |
921 #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
922 VIM_CLEAR(cmd->uc_compl_arg); | |
923 #endif | |
924 break; | |
925 } | |
926 | |
927 // Stop as soon as we pass the name to add | |
928 if (cmp < 0) | |
929 break; | |
930 } | |
931 | |
932 // Extend the array unless we're replacing an existing command | |
933 if (cmp != 0) | |
934 { | |
935 if (ga_grow(gap, 1) != OK) | |
936 goto fail; | |
937 if ((p = vim_strnsave(name, (int)name_len)) == NULL) | |
938 goto fail; | |
939 | |
940 cmd = USER_CMD_GA(gap, i); | |
941 mch_memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T)); | |
942 | |
943 ++gap->ga_len; | |
944 | |
945 cmd->uc_name = p; | |
946 } | |
947 | |
948 cmd->uc_rep = rep_buf; | |
949 cmd->uc_argt = argt; | |
950 cmd->uc_def = def; | |
951 cmd->uc_compl = compl; | |
952 #ifdef FEAT_EVAL | |
953 cmd->uc_script_ctx = current_sctx; | |
954 cmd->uc_script_ctx.sc_lnum += sourcing_lnum; | |
955 # ifdef FEAT_CMDL_COMPL | |
956 cmd->uc_compl_arg = compl_arg; | |
957 # endif | |
958 #endif | |
959 cmd->uc_addr_type = addr_type; | |
960 | |
961 return OK; | |
962 | |
963 fail: | |
964 vim_free(rep_buf); | |
965 #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
966 vim_free(compl_arg); | |
967 #endif | |
968 return FAIL; | |
969 } | |
970 | |
971 /* | |
972 * ":command ..." implementation | |
973 */ | |
974 void | |
975 ex_command(exarg_T *eap) | |
976 { | |
977 char_u *name; | |
978 char_u *end; | |
979 char_u *p; | |
980 long argt = 0; | |
981 long def = -1; | |
982 int flags = 0; | |
983 int compl = EXPAND_NOTHING; | |
984 char_u *compl_arg = NULL; | |
985 int addr_type_arg = ADDR_LINES; | |
986 int has_attr = (eap->arg[0] == '-'); | |
987 int name_len; | |
988 | |
989 p = eap->arg; | |
990 | |
991 // Check for attributes | |
992 while (*p == '-') | |
993 { | |
994 ++p; | |
995 end = skiptowhite(p); | |
996 if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, | |
997 &compl_arg, &addr_type_arg) == FAIL) | |
998 return; | |
999 p = skipwhite(end); | |
1000 } | |
1001 | |
1002 // Get the name (if any) and skip to the following argument | |
1003 name = p; | |
1004 if (ASCII_ISALPHA(*p)) | |
1005 while (ASCII_ISALNUM(*p)) | |
1006 ++p; | |
1007 if (!ends_excmd(*p) && !VIM_ISWHITE(*p)) | |
1008 { | |
1009 emsg(_("E182: Invalid command name")); | |
1010 return; | |
1011 } | |
1012 end = p; | |
1013 name_len = (int)(end - name); | |
1014 | |
1015 // If there is nothing after the name, and no attributes were specified, | |
1016 // we are listing commands | |
1017 p = skipwhite(end); | |
1018 if (!has_attr && ends_excmd(*p)) | |
1019 { | |
1020 uc_list(name, end - name); | |
1021 } | |
1022 else if (!ASCII_ISUPPER(*name)) | |
1023 { | |
1024 emsg(_("E183: User defined commands must start with an uppercase letter")); | |
1025 return; | |
1026 } | |
1027 else if ((name_len == 1 && *name == 'X') | |
1028 || (name_len <= 4 | |
1029 && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0)) | |
1030 { | |
1031 emsg(_("E841: Reserved name, cannot be used for user defined command")); | |
1032 return; | |
1033 } | |
1034 else | |
1035 uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, | |
1036 addr_type_arg, eap->forceit); | |
1037 } | |
1038 | |
1039 /* | |
1040 * ":comclear" implementation | |
1041 * Clear all user commands, global and for current buffer. | |
1042 */ | |
1043 void | |
1044 ex_comclear(exarg_T *eap UNUSED) | |
1045 { | |
1046 uc_clear(&ucmds); | |
1047 uc_clear(&curbuf->b_ucmds); | |
1048 } | |
1049 | |
1050 /* | |
1051 * Clear all user commands for "gap". | |
1052 */ | |
1053 void | |
1054 uc_clear(garray_T *gap) | |
1055 { | |
1056 int i; | |
1057 ucmd_T *cmd; | |
1058 | |
1059 for (i = 0; i < gap->ga_len; ++i) | |
1060 { | |
1061 cmd = USER_CMD_GA(gap, i); | |
1062 vim_free(cmd->uc_name); | |
1063 vim_free(cmd->uc_rep); | |
1064 # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
1065 vim_free(cmd->uc_compl_arg); | |
1066 # endif | |
1067 } | |
1068 ga_clear(gap); | |
1069 } | |
1070 | |
1071 /* | |
1072 * ":delcommand" implementation | |
1073 */ | |
1074 void | |
1075 ex_delcommand(exarg_T *eap) | |
1076 { | |
1077 int i = 0; | |
1078 ucmd_T *cmd = NULL; | |
1079 int cmp = -1; | |
1080 garray_T *gap; | |
1081 | |
1082 gap = &curbuf->b_ucmds; | |
1083 for (;;) | |
1084 { | |
1085 for (i = 0; i < gap->ga_len; ++i) | |
1086 { | |
1087 cmd = USER_CMD_GA(gap, i); | |
1088 cmp = STRCMP(eap->arg, cmd->uc_name); | |
1089 if (cmp <= 0) | |
1090 break; | |
1091 } | |
1092 if (gap == &ucmds || cmp == 0) | |
1093 break; | |
1094 gap = &ucmds; | |
1095 } | |
1096 | |
1097 if (cmp != 0) | |
1098 { | |
1099 semsg(_("E184: No such user-defined command: %s"), eap->arg); | |
1100 return; | |
1101 } | |
1102 | |
1103 vim_free(cmd->uc_name); | |
1104 vim_free(cmd->uc_rep); | |
1105 # if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) | |
1106 vim_free(cmd->uc_compl_arg); | |
1107 # endif | |
1108 | |
1109 --gap->ga_len; | |
1110 | |
1111 if (i < gap->ga_len) | |
1112 mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T)); | |
1113 } | |
1114 | |
1115 /* | |
1116 * Split and quote args for <f-args>. | |
1117 */ | |
1118 static char_u * | |
1119 uc_split_args(char_u *arg, size_t *lenp) | |
1120 { | |
1121 char_u *buf; | |
1122 char_u *p; | |
1123 char_u *q; | |
1124 int len; | |
1125 | |
1126 // Precalculate length | |
1127 p = arg; | |
1128 len = 2; // Initial and final quotes | |
1129 | |
1130 while (*p) | |
1131 { | |
1132 if (p[0] == '\\' && p[1] == '\\') | |
1133 { | |
1134 len += 2; | |
1135 p += 2; | |
1136 } | |
1137 else if (p[0] == '\\' && VIM_ISWHITE(p[1])) | |
1138 { | |
1139 len += 1; | |
1140 p += 2; | |
1141 } | |
1142 else if (*p == '\\' || *p == '"') | |
1143 { | |
1144 len += 2; | |
1145 p += 1; | |
1146 } | |
1147 else if (VIM_ISWHITE(*p)) | |
1148 { | |
1149 p = skipwhite(p); | |
1150 if (*p == NUL) | |
1151 break; | |
1152 len += 3; // "," | |
1153 } | |
1154 else | |
1155 { | |
1156 int charlen = (*mb_ptr2len)(p); | |
1157 | |
1158 len += charlen; | |
1159 p += charlen; | |
1160 } | |
1161 } | |
1162 | |
1163 buf = alloc(len + 1); | |
1164 if (buf == NULL) | |
1165 { | |
1166 *lenp = 0; | |
1167 return buf; | |
1168 } | |
1169 | |
1170 p = arg; | |
1171 q = buf; | |
1172 *q++ = '"'; | |
1173 while (*p) | |
1174 { | |
1175 if (p[0] == '\\' && p[1] == '\\') | |
1176 { | |
1177 *q++ = '\\'; | |
1178 *q++ = '\\'; | |
1179 p += 2; | |
1180 } | |
1181 else if (p[0] == '\\' && VIM_ISWHITE(p[1])) | |
1182 { | |
1183 *q++ = p[1]; | |
1184 p += 2; | |
1185 } | |
1186 else if (*p == '\\' || *p == '"') | |
1187 { | |
1188 *q++ = '\\'; | |
1189 *q++ = *p++; | |
1190 } | |
1191 else if (VIM_ISWHITE(*p)) | |
1192 { | |
1193 p = skipwhite(p); | |
1194 if (*p == NUL) | |
1195 break; | |
1196 *q++ = '"'; | |
1197 *q++ = ','; | |
1198 *q++ = '"'; | |
1199 } | |
1200 else | |
1201 { | |
1202 MB_COPY_CHAR(p, q); | |
1203 } | |
1204 } | |
1205 *q++ = '"'; | |
1206 *q = 0; | |
1207 | |
1208 *lenp = len; | |
1209 return buf; | |
1210 } | |
1211 | |
1212 static size_t | |
1213 add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods) | |
1214 { | |
1215 size_t result; | |
1216 | |
1217 result = STRLEN(mod_str); | |
1218 if (*multi_mods) | |
1219 result += 1; | |
1220 if (buf != NULL) | |
1221 { | |
1222 if (*multi_mods) | |
1223 STRCAT(buf, " "); | |
1224 STRCAT(buf, mod_str); | |
1225 } | |
1226 | |
1227 *multi_mods = 1; | |
1228 | |
1229 return result; | |
1230 } | |
1231 | |
1232 /* | |
1233 * Check for a <> code in a user command. | |
1234 * "code" points to the '<'. "len" the length of the <> (inclusive). | |
1235 * "buf" is where the result is to be added. | |
1236 * "split_buf" points to a buffer used for splitting, caller should free it. | |
1237 * "split_len" is the length of what "split_buf" contains. | |
1238 * Returns the length of the replacement, which has been added to "buf". | |
1239 * Returns -1 if there was no match, and only the "<" has been copied. | |
1240 */ | |
1241 static size_t | |
1242 uc_check_code( | |
1243 char_u *code, | |
1244 size_t len, | |
1245 char_u *buf, | |
1246 ucmd_T *cmd, // the user command we're expanding | |
1247 exarg_T *eap, // ex arguments | |
1248 char_u **split_buf, | |
1249 size_t *split_len) | |
1250 { | |
1251 size_t result = 0; | |
1252 char_u *p = code + 1; | |
1253 size_t l = len - 2; | |
1254 int quote = 0; | |
1255 enum { | |
1256 ct_ARGS, | |
1257 ct_BANG, | |
1258 ct_COUNT, | |
1259 ct_LINE1, | |
1260 ct_LINE2, | |
1261 ct_RANGE, | |
1262 ct_MODS, | |
1263 ct_REGISTER, | |
1264 ct_LT, | |
1265 ct_NONE | |
1266 } type = ct_NONE; | |
1267 | |
1268 if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') | |
1269 { | |
1270 quote = (*p == 'q' || *p == 'Q') ? 1 : 2; | |
1271 p += 2; | |
1272 l -= 2; | |
1273 } | |
1274 | |
1275 ++l; | |
1276 if (l <= 1) | |
1277 type = ct_NONE; | |
1278 else if (STRNICMP(p, "args>", l) == 0) | |
1279 type = ct_ARGS; | |
1280 else if (STRNICMP(p, "bang>", l) == 0) | |
1281 type = ct_BANG; | |
1282 else if (STRNICMP(p, "count>", l) == 0) | |
1283 type = ct_COUNT; | |
1284 else if (STRNICMP(p, "line1>", l) == 0) | |
1285 type = ct_LINE1; | |
1286 else if (STRNICMP(p, "line2>", l) == 0) | |
1287 type = ct_LINE2; | |
1288 else if (STRNICMP(p, "range>", l) == 0) | |
1289 type = ct_RANGE; | |
1290 else if (STRNICMP(p, "lt>", l) == 0) | |
1291 type = ct_LT; | |
1292 else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0) | |
1293 type = ct_REGISTER; | |
1294 else if (STRNICMP(p, "mods>", l) == 0) | |
1295 type = ct_MODS; | |
1296 | |
1297 switch (type) | |
1298 { | |
1299 case ct_ARGS: | |
1300 // Simple case first | |
1301 if (*eap->arg == NUL) | |
1302 { | |
1303 if (quote == 1) | |
1304 { | |
1305 result = 2; | |
1306 if (buf != NULL) | |
1307 STRCPY(buf, "''"); | |
1308 } | |
1309 else | |
1310 result = 0; | |
1311 break; | |
1312 } | |
1313 | |
1314 // When specified there is a single argument don't split it. | |
1315 // Works for ":Cmd %" when % is "a b c". | |
1316 if ((eap->argt & NOSPC) && quote == 2) | |
1317 quote = 1; | |
1318 | |
1319 switch (quote) | |
1320 { | |
1321 case 0: // No quoting, no splitting | |
1322 result = STRLEN(eap->arg); | |
1323 if (buf != NULL) | |
1324 STRCPY(buf, eap->arg); | |
1325 break; | |
1326 case 1: // Quote, but don't split | |
1327 result = STRLEN(eap->arg) + 2; | |
1328 for (p = eap->arg; *p; ++p) | |
1329 { | |
1330 if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) | |
1331 // DBCS can contain \ in a trail byte, skip the | |
1332 // double-byte character. | |
1333 ++p; | |
1334 else | |
1335 if (*p == '\\' || *p == '"') | |
1336 ++result; | |
1337 } | |
1338 | |
1339 if (buf != NULL) | |
1340 { | |
1341 *buf++ = '"'; | |
1342 for (p = eap->arg; *p; ++p) | |
1343 { | |
1344 if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) | |
1345 // DBCS can contain \ in a trail byte, copy the | |
1346 // double-byte character to avoid escaping. | |
1347 *buf++ = *p++; | |
1348 else | |
1349 if (*p == '\\' || *p == '"') | |
1350 *buf++ = '\\'; | |
1351 *buf++ = *p; | |
1352 } | |
1353 *buf = '"'; | |
1354 } | |
1355 | |
1356 break; | |
1357 case 2: // Quote and split (<f-args>) | |
1358 // This is hard, so only do it once, and cache the result | |
1359 if (*split_buf == NULL) | |
1360 *split_buf = uc_split_args(eap->arg, split_len); | |
1361 | |
1362 result = *split_len; | |
1363 if (buf != NULL && result != 0) | |
1364 STRCPY(buf, *split_buf); | |
1365 | |
1366 break; | |
1367 } | |
1368 break; | |
1369 | |
1370 case ct_BANG: | |
1371 result = eap->forceit ? 1 : 0; | |
1372 if (quote) | |
1373 result += 2; | |
1374 if (buf != NULL) | |
1375 { | |
1376 if (quote) | |
1377 *buf++ = '"'; | |
1378 if (eap->forceit) | |
1379 *buf++ = '!'; | |
1380 if (quote) | |
1381 *buf = '"'; | |
1382 } | |
1383 break; | |
1384 | |
1385 case ct_LINE1: | |
1386 case ct_LINE2: | |
1387 case ct_RANGE: | |
1388 case ct_COUNT: | |
1389 { | |
1390 char num_buf[20]; | |
1391 long num = (type == ct_LINE1) ? eap->line1 : | |
1392 (type == ct_LINE2) ? eap->line2 : | |
1393 (type == ct_RANGE) ? eap->addr_count : | |
1394 (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; | |
1395 size_t num_len; | |
1396 | |
1397 sprintf(num_buf, "%ld", num); | |
1398 num_len = STRLEN(num_buf); | |
1399 result = num_len; | |
1400 | |
1401 if (quote) | |
1402 result += 2; | |
1403 | |
1404 if (buf != NULL) | |
1405 { | |
1406 if (quote) | |
1407 *buf++ = '"'; | |
1408 STRCPY(buf, num_buf); | |
1409 buf += num_len; | |
1410 if (quote) | |
1411 *buf = '"'; | |
1412 } | |
1413 | |
1414 break; | |
1415 } | |
1416 | |
1417 case ct_MODS: | |
1418 { | |
1419 int multi_mods = 0; | |
1420 typedef struct { | |
1421 int *varp; | |
1422 char *name; | |
1423 } mod_entry_T; | |
1424 static mod_entry_T mod_entries[] = { | |
1425 #ifdef FEAT_BROWSE_CMD | |
1426 {&cmdmod.browse, "browse"}, | |
1427 #endif | |
1428 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) | |
1429 {&cmdmod.confirm, "confirm"}, | |
1430 #endif | |
1431 {&cmdmod.hide, "hide"}, | |
1432 {&cmdmod.keepalt, "keepalt"}, | |
1433 {&cmdmod.keepjumps, "keepjumps"}, | |
1434 {&cmdmod.keepmarks, "keepmarks"}, | |
1435 {&cmdmod.keeppatterns, "keeppatterns"}, | |
1436 {&cmdmod.lockmarks, "lockmarks"}, | |
1437 {&cmdmod.noswapfile, "noswapfile"}, | |
1438 {NULL, NULL} | |
1439 }; | |
1440 int i; | |
1441 | |
1442 result = quote ? 2 : 0; | |
1443 if (buf != NULL) | |
1444 { | |
1445 if (quote) | |
1446 *buf++ = '"'; | |
1447 *buf = '\0'; | |
1448 } | |
1449 | |
1450 // :aboveleft and :leftabove | |
1451 if (cmdmod.split & WSP_ABOVE) | |
1452 result += add_cmd_modifier(buf, "aboveleft", &multi_mods); | |
1453 // :belowright and :rightbelow | |
1454 if (cmdmod.split & WSP_BELOW) | |
1455 result += add_cmd_modifier(buf, "belowright", &multi_mods); | |
1456 // :botright | |
1457 if (cmdmod.split & WSP_BOT) | |
1458 result += add_cmd_modifier(buf, "botright", &multi_mods); | |
1459 | |
1460 // the modifiers that are simple flags | |
1461 for (i = 0; mod_entries[i].varp != NULL; ++i) | |
1462 if (*mod_entries[i].varp) | |
1463 result += add_cmd_modifier(buf, mod_entries[i].name, | |
1464 &multi_mods); | |
1465 | |
1466 // TODO: How to support :noautocmd? | |
1467 #ifdef HAVE_SANDBOX | |
1468 // TODO: How to support :sandbox? | |
1469 #endif | |
1470 // :silent | |
1471 if (msg_silent > 0) | |
1472 result += add_cmd_modifier(buf, | |
1473 emsg_silent > 0 ? "silent!" : "silent", &multi_mods); | |
1474 // :tab | |
1475 if (cmdmod.tab > 0) | |
1476 result += add_cmd_modifier(buf, "tab", &multi_mods); | |
1477 // :topleft | |
1478 if (cmdmod.split & WSP_TOP) | |
1479 result += add_cmd_modifier(buf, "topleft", &multi_mods); | |
1480 // TODO: How to support :unsilent? | |
1481 // :verbose | |
1482 if (p_verbose > 0) | |
1483 result += add_cmd_modifier(buf, "verbose", &multi_mods); | |
1484 // :vertical | |
1485 if (cmdmod.split & WSP_VERT) | |
1486 result += add_cmd_modifier(buf, "vertical", &multi_mods); | |
1487 if (quote && buf != NULL) | |
1488 { | |
1489 buf += result - 2; | |
1490 *buf = '"'; | |
1491 } | |
1492 break; | |
1493 } | |
1494 | |
1495 case ct_REGISTER: | |
1496 result = eap->regname ? 1 : 0; | |
1497 if (quote) | |
1498 result += 2; | |
1499 if (buf != NULL) | |
1500 { | |
1501 if (quote) | |
1502 *buf++ = '\''; | |
1503 if (eap->regname) | |
1504 *buf++ = eap->regname; | |
1505 if (quote) | |
1506 *buf = '\''; | |
1507 } | |
1508 break; | |
1509 | |
1510 case ct_LT: | |
1511 result = 1; | |
1512 if (buf != NULL) | |
1513 *buf = '<'; | |
1514 break; | |
1515 | |
1516 default: | |
1517 // Not recognized: just copy the '<' and return -1. | |
1518 result = (size_t)-1; | |
1519 if (buf != NULL) | |
1520 *buf = '<'; | |
1521 break; | |
1522 } | |
1523 | |
1524 return result; | |
1525 } | |
1526 | |
1527 /* | |
1528 * Execute a user defined command. | |
1529 */ | |
1530 void | |
1531 do_ucmd(exarg_T *eap) | |
1532 { | |
1533 char_u *buf; | |
1534 char_u *p; | |
1535 char_u *q; | |
1536 | |
1537 char_u *start; | |
1538 char_u *end = NULL; | |
1539 char_u *ksp; | |
1540 size_t len, totlen; | |
1541 | |
1542 size_t split_len = 0; | |
1543 char_u *split_buf = NULL; | |
1544 ucmd_T *cmd; | |
1545 #ifdef FEAT_EVAL | |
1546 sctx_T save_current_sctx = current_sctx; | |
1547 #endif | |
1548 | |
1549 if (eap->cmdidx == CMD_USER) | |
1550 cmd = USER_CMD(eap->useridx); | |
1551 else | |
1552 cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); | |
1553 | |
1554 /* | |
1555 * Replace <> in the command by the arguments. | |
1556 * First round: "buf" is NULL, compute length, allocate "buf". | |
1557 * Second round: copy result into "buf". | |
1558 */ | |
1559 buf = NULL; | |
1560 for (;;) | |
1561 { | |
1562 p = cmd->uc_rep; // source | |
1563 q = buf; // destination | |
1564 totlen = 0; | |
1565 | |
1566 for (;;) | |
1567 { | |
1568 start = vim_strchr(p, '<'); | |
1569 if (start != NULL) | |
1570 end = vim_strchr(start + 1, '>'); | |
1571 if (buf != NULL) | |
1572 { | |
1573 for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp) | |
1574 ; | |
1575 if (*ksp == K_SPECIAL | |
1576 && (start == NULL || ksp < start || end == NULL) | |
1577 && ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER) | |
1578 # ifdef FEAT_GUI | |
1579 || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI) | |
1580 # endif | |
1581 )) | |
1582 { | |
1583 // K_SPECIAL has been put in the buffer as K_SPECIAL | |
1584 // KS_SPECIAL KE_FILLER, like for mappings, but | |
1585 // do_cmdline() doesn't handle that, so convert it back. | |
1586 // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. | |
1587 len = ksp - p; | |
1588 if (len > 0) | |
1589 { | |
1590 mch_memmove(q, p, len); | |
1591 q += len; | |
1592 } | |
1593 *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI; | |
1594 p = ksp + 3; | |
1595 continue; | |
1596 } | |
1597 } | |
1598 | |
1599 // break if no <item> is found | |
1600 if (start == NULL || end == NULL) | |
1601 break; | |
1602 | |
1603 // Include the '>' | |
1604 ++end; | |
1605 | |
1606 // Take everything up to the '<' | |
1607 len = start - p; | |
1608 if (buf == NULL) | |
1609 totlen += len; | |
1610 else | |
1611 { | |
1612 mch_memmove(q, p, len); | |
1613 q += len; | |
1614 } | |
1615 | |
1616 len = uc_check_code(start, end - start, q, cmd, eap, | |
1617 &split_buf, &split_len); | |
1618 if (len == (size_t)-1) | |
1619 { | |
1620 // no match, continue after '<' | |
1621 p = start + 1; | |
1622 len = 1; | |
1623 } | |
1624 else | |
1625 p = end; | |
1626 if (buf == NULL) | |
1627 totlen += len; | |
1628 else | |
1629 q += len; | |
1630 } | |
1631 if (buf != NULL) // second time here, finished | |
1632 { | |
1633 STRCPY(q, p); | |
1634 break; | |
1635 } | |
1636 | |
1637 totlen += STRLEN(p); // Add on the trailing characters | |
1638 buf = alloc((unsigned)(totlen + 1)); | |
1639 if (buf == NULL) | |
1640 { | |
1641 vim_free(split_buf); | |
1642 return; | |
1643 } | |
1644 } | |
1645 | |
1646 #ifdef FEAT_EVAL | |
1647 current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; | |
1648 #endif | |
1649 (void)do_cmdline(buf, eap->getline, eap->cookie, | |
1650 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); | |
1651 #ifdef FEAT_EVAL | |
1652 current_sctx = save_current_sctx; | |
1653 #endif | |
1654 vim_free(buf); | |
1655 vim_free(split_buf); | |
1656 } |