Mercurial > vim
comparison src/cmdexpand.c @ 27661:2062de7c0edd v8.2.4356
patch 8.2.4356: command line completion functions are very long
Commit: https://github.com/vim/vim/commit/620d8edba01bb2779485718dd1a99ca670ca894b
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Sat Feb 12 12:03:07 2022 +0000
patch 8.2.4356: command line completion functions are very long
Problem: Command line completion functions are very long.
Solution: Refactor into multiple functions. (Yegappan Lakshmanan,
closes #9753)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 12 Feb 2022 13:15:03 +0100 |
parents | 9caeb7f8b094 |
children | 38eab98ef5a9 |
comparison
equal
deleted
inserted
replaced
27660:6322a4258c29 | 27661:2062de7c0edd |
---|---|
347 int cmdline_compl_startcol(void) | 347 int cmdline_compl_startcol(void) |
348 { | 348 { |
349 return compl_startcol; | 349 return compl_startcol; |
350 } | 350 } |
351 #endif | 351 #endif |
352 | |
353 /* | |
354 * Get the next or prev cmdline completion match. The index of the match is set | |
355 * in 'p_findex' | |
356 */ | |
357 static char_u * | |
358 get_next_or_prev_match( | |
359 int mode, | |
360 expand_T *xp, | |
361 int *p_findex, | |
362 char_u *orig_save) | |
363 { | |
364 int findex = *p_findex; | |
365 | |
366 if (xp->xp_numfiles <= 0) | |
367 return NULL; | |
368 | |
369 if (mode == WILD_PREV) | |
370 { | |
371 if (findex == -1) | |
372 findex = xp->xp_numfiles; | |
373 --findex; | |
374 } | |
375 else // mode == WILD_NEXT | |
376 ++findex; | |
377 | |
378 // When wrapping around, return the original string, set findex to | |
379 // -1. | |
380 if (findex < 0) | |
381 { | |
382 if (orig_save == NULL) | |
383 findex = xp->xp_numfiles - 1; | |
384 else | |
385 findex = -1; | |
386 } | |
387 if (findex >= xp->xp_numfiles) | |
388 { | |
389 if (orig_save == NULL) | |
390 findex = 0; | |
391 else | |
392 findex = -1; | |
393 } | |
394 #ifdef FEAT_WILDMENU | |
395 if (compl_match_array) | |
396 { | |
397 compl_selected = findex; | |
398 cmdline_pum_display(); | |
399 } | |
400 else if (p_wmnu) | |
401 win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, | |
402 findex, cmd_showtail); | |
403 #endif | |
404 *p_findex = findex; | |
405 | |
406 if (findex == -1) | |
407 return vim_strsave(orig_save); | |
408 | |
409 return vim_strsave(xp->xp_files[findex]); | |
410 } | |
411 | |
412 /* | |
413 * Start the command-line expansion and get the matches. | |
414 */ | |
415 static char_u * | |
416 ExpandOne_start(int mode, expand_T *xp, char_u *str, int options) | |
417 { | |
418 int non_suf_match; // number without matching suffix | |
419 int i; | |
420 char_u *ss = NULL; | |
421 | |
422 // Do the expansion. | |
423 if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, | |
424 options) == FAIL) | |
425 { | |
426 #ifdef FNAME_ILLEGAL | |
427 // Illegal file name has been silently skipped. But when there | |
428 // are wildcards, the real problem is that there was no match, | |
429 // causing the pattern to be added, which has illegal characters. | |
430 if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND)) | |
431 semsg(_(e_no_match_str_2), str); | |
432 #endif | |
433 } | |
434 else if (xp->xp_numfiles == 0) | |
435 { | |
436 if (!(options & WILD_SILENT)) | |
437 semsg(_(e_no_match_str_2), str); | |
438 } | |
439 else | |
440 { | |
441 // Escape the matches for use on the command line. | |
442 ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options); | |
443 | |
444 // Check for matching suffixes in file names. | |
445 if (mode != WILD_ALL && mode != WILD_ALL_KEEP | |
446 && mode != WILD_LONGEST) | |
447 { | |
448 if (xp->xp_numfiles) | |
449 non_suf_match = xp->xp_numfiles; | |
450 else | |
451 non_suf_match = 1; | |
452 if ((xp->xp_context == EXPAND_FILES | |
453 || xp->xp_context == EXPAND_DIRECTORIES) | |
454 && xp->xp_numfiles > 1) | |
455 { | |
456 // More than one match; check suffix. | |
457 // The files will have been sorted on matching suffix in | |
458 // expand_wildcards, only need to check the first two. | |
459 non_suf_match = 0; | |
460 for (i = 0; i < 2; ++i) | |
461 if (match_suffix(xp->xp_files[i])) | |
462 ++non_suf_match; | |
463 } | |
464 if (non_suf_match != 1) | |
465 { | |
466 // Can we ever get here unless it's while expanding | |
467 // interactively? If not, we can get rid of this all | |
468 // together. Don't really want to wait for this message | |
469 // (and possibly have to hit return to continue!). | |
470 if (!(options & WILD_SILENT)) | |
471 emsg(_(e_too_many_file_names)); | |
472 else if (!(options & WILD_NO_BEEP)) | |
473 beep_flush(); | |
474 } | |
475 if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) | |
476 ss = vim_strsave(xp->xp_files[0]); | |
477 } | |
478 } | |
479 | |
480 return ss; | |
481 } | |
482 | |
483 /* | |
484 * Return the longest common part in the list of cmdline completion matches. | |
485 */ | |
486 static char_u * | |
487 find_longest_match(expand_T *xp, int options) | |
488 { | |
489 long_u len; | |
490 int mb_len = 1; | |
491 int c0, ci; | |
492 int i; | |
493 char_u *ss; | |
494 | |
495 for (len = 0; xp->xp_files[0][len]; len += mb_len) | |
496 { | |
497 if (has_mbyte) | |
498 { | |
499 mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]); | |
500 c0 =(* mb_ptr2char)(&xp->xp_files[0][len]); | |
501 } | |
502 else | |
503 c0 = xp->xp_files[0][len]; | |
504 for (i = 1; i < xp->xp_numfiles; ++i) | |
505 { | |
506 if (has_mbyte) | |
507 ci =(* mb_ptr2char)(&xp->xp_files[i][len]); | |
508 else | |
509 ci = xp->xp_files[i][len]; | |
510 if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES | |
511 || xp->xp_context == EXPAND_FILES | |
512 || xp->xp_context == EXPAND_SHELLCMD | |
513 || xp->xp_context == EXPAND_BUFFERS)) | |
514 { | |
515 if (MB_TOLOWER(c0) != MB_TOLOWER(ci)) | |
516 break; | |
517 } | |
518 else if (c0 != ci) | |
519 break; | |
520 } | |
521 if (i < xp->xp_numfiles) | |
522 { | |
523 if (!(options & WILD_NO_BEEP)) | |
524 vim_beep(BO_WILD); | |
525 break; | |
526 } | |
527 } | |
528 | |
529 ss = alloc(len + 1); | |
530 if (ss) | |
531 vim_strncpy(ss, xp->xp_files[0], (size_t)len); | |
532 | |
533 return ss; | |
534 } | |
352 | 535 |
353 /* | 536 /* |
354 * Do wildcard expansion on the string 'str'. | 537 * Do wildcard expansion on the string 'str'. |
355 * Chars that should not be expanded must be preceded with a backslash. | 538 * Chars that should not be expanded must be preceded with a backslash. |
356 * Return a pointer to allocated memory containing the new string. | 539 * Return a pointer to allocated memory containing the new string. |
369 * mode = WILD_NEXT: use next match in multiple match, wrap to first | 552 * mode = WILD_NEXT: use next match in multiple match, wrap to first |
370 * mode = WILD_PREV: use previous match in multiple match, wrap to first | 553 * mode = WILD_PREV: use previous match in multiple match, wrap to first |
371 * mode = WILD_ALL: return all matches concatenated | 554 * mode = WILD_ALL: return all matches concatenated |
372 * mode = WILD_LONGEST: return longest matched part | 555 * mode = WILD_LONGEST: return longest matched part |
373 * mode = WILD_ALL_KEEP: get all matches, keep matches | 556 * mode = WILD_ALL_KEEP: get all matches, keep matches |
557 * mode = WILD_APPLY: apply the item selected in the cmdline completion | |
558 * popup menu and close the menu. | |
559 * mode = WILD_CANCEL: cancel and close the cmdline completion popup and | |
560 * use the original text. | |
374 * | 561 * |
375 * options = WILD_LIST_NOTFOUND: list entries without a match | 562 * options = WILD_LIST_NOTFOUND: list entries without a match |
376 * options = WILD_HOME_REPLACE: do home_replace() for buffer names | 563 * options = WILD_HOME_REPLACE: do home_replace() for buffer names |
377 * options = WILD_USE_NL: Use '\n' for WILD_ALL | 564 * options = WILD_USE_NL: Use '\n' for WILD_ALL |
378 * options = WILD_NO_BEEP: Don't beep for multiple matches | 565 * options = WILD_NO_BEEP: Don't beep for multiple matches |
397 static int findex; | 584 static int findex; |
398 static char_u *orig_save = NULL; // kept value of orig | 585 static char_u *orig_save = NULL; // kept value of orig |
399 int orig_saved = FALSE; | 586 int orig_saved = FALSE; |
400 int i; | 587 int i; |
401 long_u len; | 588 long_u len; |
402 int non_suf_match; // number without matching suffix | |
403 | 589 |
404 // first handle the case of using an old match | 590 // first handle the case of using an old match |
405 if (mode == WILD_NEXT || mode == WILD_PREV) | 591 if (mode == WILD_NEXT || mode == WILD_PREV) |
406 { | 592 return get_next_or_prev_match(mode, xp, &findex, orig_save); |
407 if (xp->xp_numfiles > 0) | |
408 { | |
409 if (mode == WILD_PREV) | |
410 { | |
411 if (findex == -1) | |
412 findex = xp->xp_numfiles; | |
413 --findex; | |
414 } | |
415 else // mode == WILD_NEXT | |
416 ++findex; | |
417 | |
418 // When wrapping around, return the original string, set findex to | |
419 // -1. | |
420 if (findex < 0) | |
421 { | |
422 if (orig_save == NULL) | |
423 findex = xp->xp_numfiles - 1; | |
424 else | |
425 findex = -1; | |
426 } | |
427 if (findex >= xp->xp_numfiles) | |
428 { | |
429 if (orig_save == NULL) | |
430 findex = 0; | |
431 else | |
432 findex = -1; | |
433 } | |
434 #ifdef FEAT_WILDMENU | |
435 if (compl_match_array) | |
436 { | |
437 compl_selected = findex; | |
438 cmdline_pum_display(); | |
439 } | |
440 else if (p_wmnu) | |
441 win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, | |
442 findex, cmd_showtail); | |
443 #endif | |
444 if (findex == -1) | |
445 return vim_strsave(orig_save); | |
446 return vim_strsave(xp->xp_files[findex]); | |
447 } | |
448 else | |
449 return NULL; | |
450 } | |
451 | 593 |
452 if (mode == WILD_CANCEL) | 594 if (mode == WILD_CANCEL) |
453 ss = vim_strsave(orig_save ? orig_save : (char_u *)""); | 595 ss = vim_strsave(orig_save ? orig_save : (char_u *)""); |
454 else if (mode == WILD_APPLY) | 596 else if (mode == WILD_APPLY) |
455 ss = vim_strsave(findex == -1 ? (orig_save ? | 597 ss = vim_strsave(findex == -1 ? (orig_save ? |
471 { | 613 { |
472 vim_free(orig_save); | 614 vim_free(orig_save); |
473 orig_save = orig; | 615 orig_save = orig; |
474 orig_saved = TRUE; | 616 orig_saved = TRUE; |
475 | 617 |
476 // Do the expansion. | 618 ss = ExpandOne_start(mode, xp, str, options); |
477 if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, | |
478 options) == FAIL) | |
479 { | |
480 #ifdef FNAME_ILLEGAL | |
481 // Illegal file name has been silently skipped. But when there | |
482 // are wildcards, the real problem is that there was no match, | |
483 // causing the pattern to be added, which has illegal characters. | |
484 if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND)) | |
485 semsg(_(e_no_match_str_2), str); | |
486 #endif | |
487 } | |
488 else if (xp->xp_numfiles == 0) | |
489 { | |
490 if (!(options & WILD_SILENT)) | |
491 semsg(_(e_no_match_str_2), str); | |
492 } | |
493 else | |
494 { | |
495 // Escape the matches for use on the command line. | |
496 ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options); | |
497 | |
498 // Check for matching suffixes in file names. | |
499 if (mode != WILD_ALL && mode != WILD_ALL_KEEP | |
500 && mode != WILD_LONGEST) | |
501 { | |
502 if (xp->xp_numfiles) | |
503 non_suf_match = xp->xp_numfiles; | |
504 else | |
505 non_suf_match = 1; | |
506 if ((xp->xp_context == EXPAND_FILES | |
507 || xp->xp_context == EXPAND_DIRECTORIES) | |
508 && xp->xp_numfiles > 1) | |
509 { | |
510 // More than one match; check suffix. | |
511 // The files will have been sorted on matching suffix in | |
512 // expand_wildcards, only need to check the first two. | |
513 non_suf_match = 0; | |
514 for (i = 0; i < 2; ++i) | |
515 if (match_suffix(xp->xp_files[i])) | |
516 ++non_suf_match; | |
517 } | |
518 if (non_suf_match != 1) | |
519 { | |
520 // Can we ever get here unless it's while expanding | |
521 // interactively? If not, we can get rid of this all | |
522 // together. Don't really want to wait for this message | |
523 // (and possibly have to hit return to continue!). | |
524 if (!(options & WILD_SILENT)) | |
525 emsg(_(e_too_many_file_names)); | |
526 else if (!(options & WILD_NO_BEEP)) | |
527 beep_flush(); | |
528 } | |
529 if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) | |
530 ss = vim_strsave(xp->xp_files[0]); | |
531 } | |
532 } | |
533 } | 619 } |
534 | 620 |
535 // Find longest common part | 621 // Find longest common part |
536 if (mode == WILD_LONGEST && xp->xp_numfiles > 0) | 622 if (mode == WILD_LONGEST && xp->xp_numfiles > 0) |
537 { | 623 { |
538 int mb_len = 1; | 624 ss = find_longest_match(xp, options); |
539 int c0, ci; | |
540 | |
541 for (len = 0; xp->xp_files[0][len]; len += mb_len) | |
542 { | |
543 if (has_mbyte) | |
544 { | |
545 mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]); | |
546 c0 =(* mb_ptr2char)(&xp->xp_files[0][len]); | |
547 } | |
548 else | |
549 c0 = xp->xp_files[0][len]; | |
550 for (i = 1; i < xp->xp_numfiles; ++i) | |
551 { | |
552 if (has_mbyte) | |
553 ci =(* mb_ptr2char)(&xp->xp_files[i][len]); | |
554 else | |
555 ci = xp->xp_files[i][len]; | |
556 if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES | |
557 || xp->xp_context == EXPAND_FILES | |
558 || xp->xp_context == EXPAND_SHELLCMD | |
559 || xp->xp_context == EXPAND_BUFFERS)) | |
560 { | |
561 if (MB_TOLOWER(c0) != MB_TOLOWER(ci)) | |
562 break; | |
563 } | |
564 else if (c0 != ci) | |
565 break; | |
566 } | |
567 if (i < xp->xp_numfiles) | |
568 { | |
569 if (!(options & WILD_NO_BEEP)) | |
570 vim_beep(BO_WILD); | |
571 break; | |
572 } | |
573 } | |
574 | |
575 ss = alloc(len + 1); | |
576 if (ss) | |
577 vim_strncpy(ss, xp->xp_files[0], (size_t)len); | |
578 findex = -1; // next p_wc gets first one | 625 findex = -1; // next p_wc gets first one |
579 } | 626 } |
580 | 627 |
581 // Concatenate all matching names | 628 // Concatenate all matching names |
582 if (mode == WILD_ALL && xp->xp_numfiles > 0) | 629 if (mode == WILD_ALL && xp->xp_numfiles > 0) |
1075 } | 1122 } |
1076 set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, TRUE); | 1123 set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, TRUE); |
1077 } | 1124 } |
1078 | 1125 |
1079 /* | 1126 /* |
1080 * This is all pretty much copied from do_one_cmd(), with all the extra stuff | 1127 * Sets the index of a built-in or user defined command 'cmd' in eap->cmdidx. |
1081 * we don't need/want deleted. Maybe this could be done better if we didn't | 1128 * For user defined commands, the completion context is set in 'xp' and the |
1082 * repeat all this stuff. The only problem is that they may not stay | 1129 * completion flags in 'complp'. |
1083 * perfectly compatible with each other, but then the command line syntax | 1130 * |
1084 * probably won't change that much -- webb. | 1131 * Returns a pointer to the text after the command or NULL for failure. |
1085 */ | 1132 */ |
1086 static char_u * | 1133 static char_u * |
1087 set_one_cmd_context( | 1134 set_cmd_index(char_u *cmd, exarg_T *eap, expand_T *xp, int *complp) |
1088 expand_T *xp, | 1135 { |
1089 char_u *buff) // buffer for command string | 1136 char_u *p = NULL; |
1090 { | 1137 int len = 0; |
1091 char_u *p; | |
1092 char_u *cmd, *arg; | |
1093 int len = 0; | |
1094 exarg_T ea; | |
1095 int compl = EXPAND_NOTHING; | |
1096 int delim; | |
1097 int forceit = FALSE; | |
1098 int usefilter = FALSE; // filter instead of file name | |
1099 | |
1100 ExpandInit(xp); | |
1101 xp->xp_pattern = buff; | |
1102 xp->xp_line = buff; | |
1103 xp->xp_context = EXPAND_COMMANDS; // Default until we get past command | |
1104 ea.argt = 0; | |
1105 | |
1106 // 1. skip comment lines and leading space, colons or bars | |
1107 for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++) | |
1108 ; | |
1109 xp->xp_pattern = cmd; | |
1110 | |
1111 if (*cmd == NUL) | |
1112 return NULL; | |
1113 if (*cmd == '"') // ignore comment lines | |
1114 { | |
1115 xp->xp_context = EXPAND_NOTHING; | |
1116 return NULL; | |
1117 } | |
1118 | |
1119 // 3. Skip over the range to find the command. | |
1120 cmd = skip_range(cmd, TRUE, &xp->xp_context); | |
1121 xp->xp_pattern = cmd; | |
1122 if (*cmd == NUL) | |
1123 return NULL; | |
1124 if (*cmd == '"') | |
1125 { | |
1126 xp->xp_context = EXPAND_NOTHING; | |
1127 return NULL; | |
1128 } | |
1129 | |
1130 if (*cmd == '|' || *cmd == '\n') | |
1131 return cmd + 1; // There's another command | |
1132 | 1138 |
1133 // Isolate the command and search for it in the command table. | 1139 // Isolate the command and search for it in the command table. |
1134 // Exceptions: | 1140 // Exceptions: |
1135 // - the 'k' command can directly be followed by any character, but | 1141 // - the 'k' command can directly be followed by any character, but |
1136 // do accept "keepmarks", "keepalt" and "keepjumps". | 1142 // do accept "keepmarks", "keepalt" and "keepjumps". |
1137 // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' | 1143 // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' |
1138 if (*cmd == 'k' && cmd[1] != 'e') | 1144 if (*cmd == 'k' && cmd[1] != 'e') |
1139 { | 1145 { |
1140 ea.cmdidx = CMD_k; | 1146 eap->cmdidx = CMD_k; |
1141 p = cmd + 1; | 1147 p = cmd + 1; |
1142 } | 1148 } |
1143 else | 1149 else |
1144 { | 1150 { |
1145 p = cmd; | 1151 p = cmd; |
1166 { | 1172 { |
1167 xp->xp_context = EXPAND_UNSUCCESSFUL; | 1173 xp->xp_context = EXPAND_UNSUCCESSFUL; |
1168 return NULL; | 1174 return NULL; |
1169 } | 1175 } |
1170 | 1176 |
1171 ea.cmdidx = excmd_get_cmdidx(cmd, len); | 1177 eap->cmdidx = excmd_get_cmdidx(cmd, len); |
1172 | 1178 |
1173 if (cmd[0] >= 'A' && cmd[0] <= 'Z') | 1179 if (cmd[0] >= 'A' && cmd[0] <= 'Z') |
1174 while (ASCII_ISALNUM(*p) || *p == '*') // Allow * wild card | 1180 while (ASCII_ISALNUM(*p) || *p == '*') // Allow * wild card |
1175 ++p; | 1181 ++p; |
1176 } | 1182 } |
1178 // If the cursor is touching the command, and it ends in an alphanumeric | 1184 // If the cursor is touching the command, and it ends in an alphanumeric |
1179 // character, complete the command name. | 1185 // character, complete the command name. |
1180 if (*p == NUL && ASCII_ISALNUM(p[-1])) | 1186 if (*p == NUL && ASCII_ISALNUM(p[-1])) |
1181 return NULL; | 1187 return NULL; |
1182 | 1188 |
1183 if (ea.cmdidx == CMD_SIZE) | 1189 if (eap->cmdidx == CMD_SIZE) |
1184 { | 1190 { |
1185 if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL) | 1191 if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL) |
1186 { | 1192 { |
1187 ea.cmdidx = CMD_substitute; | 1193 eap->cmdidx = CMD_substitute; |
1188 p = cmd + 1; | 1194 p = cmd + 1; |
1189 } | 1195 } |
1190 else if (cmd[0] >= 'A' && cmd[0] <= 'Z') | 1196 else if (cmd[0] >= 'A' && cmd[0] <= 'Z') |
1191 { | 1197 { |
1192 ea.cmd = cmd; | 1198 eap->cmd = cmd; |
1193 p = find_ucmd(&ea, p, NULL, xp, &compl); | 1199 p = find_ucmd(eap, p, NULL, xp, complp); |
1194 if (p == NULL) | 1200 if (p == NULL) |
1195 ea.cmdidx = CMD_SIZE; // ambiguous user command | 1201 eap->cmdidx = CMD_SIZE; // ambiguous user command |
1196 } | 1202 } |
1197 } | 1203 } |
1198 if (ea.cmdidx == CMD_SIZE) | 1204 if (eap->cmdidx == CMD_SIZE) |
1199 { | 1205 { |
1200 // Not still touching the command and it was an illegal one | 1206 // Not still touching the command and it was an illegal one |
1201 xp->xp_context = EXPAND_UNSUCCESSFUL; | 1207 xp->xp_context = EXPAND_UNSUCCESSFUL; |
1202 return NULL; | 1208 return NULL; |
1203 } | 1209 } |
1204 | 1210 |
1205 xp->xp_context = EXPAND_NOTHING; // Default now that we're past command | 1211 return p; |
1206 | 1212 } |
1207 if (*p == '!') // forced commands | 1213 |
1208 { | 1214 /* |
1209 forceit = TRUE; | 1215 * Set the completion context for a command argument with wild card characters. |
1210 ++p; | 1216 */ |
1211 } | 1217 static void |
1212 | 1218 set_context_for_wildcard_arg( |
1213 // 6. parse arguments | 1219 exarg_T *eap, |
1214 if (!IS_USER_CMDIDX(ea.cmdidx)) | 1220 char_u *arg, |
1215 ea.argt = excmd_get_argt(ea.cmdidx); | 1221 int usefilter, |
1216 | 1222 expand_T *xp, |
1217 arg = skipwhite(p); | 1223 int *complp) |
1218 | 1224 { |
1219 // Skip over ++argopt argument | 1225 char_u *p; |
1220 if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0) | 1226 int c; |
1221 { | 1227 int in_quote = FALSE; |
1222 p = arg; | 1228 char_u *bow = NULL; // Beginning of word |
1223 while (*p && !vim_isspace(*p)) | 1229 int len = 0; |
1224 MB_PTR_ADV(p); | 1230 |
1225 arg = skipwhite(p); | 1231 // Allow spaces within back-quotes to count as part of the argument |
1226 } | 1232 // being expanded. |
1227 | 1233 xp->xp_pattern = skipwhite(arg); |
1228 if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) | 1234 p = xp->xp_pattern; |
1229 { | 1235 while (*p != NUL) |
1230 if (*arg == '>') // append | 1236 { |
1231 { | 1237 if (has_mbyte) |
1232 if (*++arg == '>') | 1238 c = mb_ptr2char(p); |
1233 ++arg; | |
1234 arg = skipwhite(arg); | |
1235 } | |
1236 else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter | |
1237 { | |
1238 ++arg; | |
1239 usefilter = TRUE; | |
1240 } | |
1241 } | |
1242 | |
1243 if (ea.cmdidx == CMD_read) | |
1244 { | |
1245 usefilter = forceit; // :r! filter if forced | |
1246 if (*arg == '!') // :r !filter | |
1247 { | |
1248 ++arg; | |
1249 usefilter = TRUE; | |
1250 } | |
1251 } | |
1252 | |
1253 if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) | |
1254 { | |
1255 while (*arg == *cmd) // allow any number of '>' or '<' | |
1256 ++arg; | |
1257 arg = skipwhite(arg); | |
1258 } | |
1259 | |
1260 // Does command allow "+command"? | |
1261 if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') | |
1262 { | |
1263 // Check if we're in the +command | |
1264 p = arg + 1; | |
1265 arg = skip_cmd_arg(arg, FALSE); | |
1266 | |
1267 // Still touching the command after '+'? | |
1268 if (*arg == NUL) | |
1269 return p; | |
1270 | |
1271 // Skip space(s) after +command to get to the real argument | |
1272 arg = skipwhite(arg); | |
1273 } | |
1274 | |
1275 | |
1276 // Check for '|' to separate commands and '"' to start comments. | |
1277 // Don't do this for ":read !cmd" and ":write !cmd". | |
1278 if ((ea.argt & EX_TRLBAR) && !usefilter) | |
1279 { | |
1280 p = arg; | |
1281 // ":redir @" is not the start of a comment | |
1282 if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') | |
1283 p += 2; | |
1284 while (*p) | |
1285 { | |
1286 if (*p == Ctrl_V) | |
1287 { | |
1288 if (p[1] != NUL) | |
1289 ++p; | |
1290 } | |
1291 else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM)) | |
1292 || *p == '|' || *p == '\n') | |
1293 { | |
1294 if (*(p - 1) != '\\') | |
1295 { | |
1296 if (*p == '|' || *p == '\n') | |
1297 return p + 1; | |
1298 return NULL; // It's a comment | |
1299 } | |
1300 } | |
1301 MB_PTR_ADV(p); | |
1302 } | |
1303 } | |
1304 | |
1305 if (!(ea.argt & EX_EXTRA) && *arg != NUL | |
1306 && vim_strchr((char_u *)"|\"", *arg) == NULL) | |
1307 // no arguments allowed but there is something | |
1308 return NULL; | |
1309 | |
1310 // Find start of last argument (argument just before cursor): | |
1311 p = buff; | |
1312 xp->xp_pattern = p; | |
1313 len = (int)STRLEN(buff); | |
1314 while (*p && p < buff + len) | |
1315 { | |
1316 if (*p == ' ' || *p == TAB) | |
1317 { | |
1318 // argument starts after a space | |
1319 xp->xp_pattern = ++p; | |
1320 } | |
1321 else | 1239 else |
1322 { | 1240 c = *p; |
1323 if (*p == '\\' && *(p + 1) != NUL) | 1241 if (c == '\\' && p[1] != NUL) |
1324 ++p; // skip over escaped character | 1242 ++p; |
1325 MB_PTR_ADV(p); | 1243 else if (c == '`') |
1326 } | 1244 { |
1327 } | 1245 if (!in_quote) |
1328 | 1246 { |
1329 if (ea.argt & EX_XFILE) | 1247 xp->xp_pattern = p; |
1330 { | 1248 bow = p + 1; |
1331 int c; | 1249 } |
1332 int in_quote = FALSE; | 1250 in_quote = !in_quote; |
1333 char_u *bow = NULL; // Beginning of word | 1251 } |
1334 | 1252 // An argument can contain just about everything, except |
1335 // Allow spaces within back-quotes to count as part of the argument | 1253 // characters that end the command and white space. |
1336 // being expanded. | 1254 else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c) |
1337 xp->xp_pattern = skipwhite(arg); | 1255 #ifdef SPACE_IN_FILENAME |
1338 p = xp->xp_pattern; | 1256 && (!(eap->argt & EX_NOSPC) || usefilter) |
1339 while (*p != NUL) | 1257 #endif |
1340 { | 1258 )) |
1341 if (has_mbyte) | 1259 { |
1342 c = mb_ptr2char(p); | 1260 len = 0; // avoid getting stuck when space is in 'isfname' |
1261 while (*p != NUL) | |
1262 { | |
1263 if (has_mbyte) | |
1264 c = mb_ptr2char(p); | |
1265 else | |
1266 c = *p; | |
1267 if (c == '`' || vim_isfilec_or_wc(c)) | |
1268 break; | |
1269 if (has_mbyte) | |
1270 len = (*mb_ptr2len)(p); | |
1271 else | |
1272 len = 1; | |
1273 MB_PTR_ADV(p); | |
1274 } | |
1275 if (in_quote) | |
1276 bow = p; | |
1343 else | 1277 else |
1344 c = *p; | 1278 xp->xp_pattern = p; |
1345 if (c == '\\' && p[1] != NUL) | 1279 p -= len; |
1346 ++p; | 1280 } |
1347 else if (c == '`') | 1281 MB_PTR_ADV(p); |
1348 { | 1282 } |
1349 if (!in_quote) | 1283 |
1350 { | 1284 // If we are still inside the quotes, and we passed a space, just |
1351 xp->xp_pattern = p; | 1285 // expand from there. |
1352 bow = p + 1; | 1286 if (bow != NULL && in_quote) |
1353 } | 1287 xp->xp_pattern = bow; |
1354 in_quote = !in_quote; | 1288 xp->xp_context = EXPAND_FILES; |
1355 } | 1289 |
1356 // An argument can contain just about everything, except | 1290 // For a shell command more chars need to be escaped. |
1357 // characters that end the command and white space. | 1291 if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) |
1358 else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c) | 1292 { |
1359 #ifdef SPACE_IN_FILENAME | |
1360 && (!(ea.argt & EX_NOSPC) || usefilter) | |
1361 #endif | |
1362 )) | |
1363 { | |
1364 len = 0; // avoid getting stuck when space is in 'isfname' | |
1365 while (*p != NUL) | |
1366 { | |
1367 if (has_mbyte) | |
1368 c = mb_ptr2char(p); | |
1369 else | |
1370 c = *p; | |
1371 if (c == '`' || vim_isfilec_or_wc(c)) | |
1372 break; | |
1373 if (has_mbyte) | |
1374 len = (*mb_ptr2len)(p); | |
1375 else | |
1376 len = 1; | |
1377 MB_PTR_ADV(p); | |
1378 } | |
1379 if (in_quote) | |
1380 bow = p; | |
1381 else | |
1382 xp->xp_pattern = p; | |
1383 p -= len; | |
1384 } | |
1385 MB_PTR_ADV(p); | |
1386 } | |
1387 | |
1388 // If we are still inside the quotes, and we passed a space, just | |
1389 // expand from there. | |
1390 if (bow != NULL && in_quote) | |
1391 xp->xp_pattern = bow; | |
1392 xp->xp_context = EXPAND_FILES; | |
1393 | |
1394 // For a shell command more chars need to be escaped. | |
1395 if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal) | |
1396 { | |
1397 #ifndef BACKSLASH_IN_FILENAME | 1293 #ifndef BACKSLASH_IN_FILENAME |
1398 xp->xp_shell = TRUE; | 1294 xp->xp_shell = TRUE; |
1399 #endif | 1295 #endif |
1400 // When still after the command name expand executables. | 1296 // When still after the command name expand executables. |
1401 if (xp->xp_pattern == skipwhite(arg)) | 1297 if (xp->xp_pattern == skipwhite(arg)) |
1402 xp->xp_context = EXPAND_SHELLCMD; | 1298 xp->xp_context = EXPAND_SHELLCMD; |
1403 } | 1299 } |
1404 | 1300 |
1405 // Check for environment variable. | 1301 // Check for environment variable. |
1406 if (*xp->xp_pattern == '$') | 1302 if (*xp->xp_pattern == '$') |
1407 { | 1303 { |
1408 for (p = xp->xp_pattern + 1; *p != NUL; ++p) | 1304 for (p = xp->xp_pattern + 1; *p != NUL; ++p) |
1409 if (!vim_isIDc(*p)) | 1305 if (!vim_isIDc(*p)) |
1410 break; | 1306 break; |
1411 if (*p == NUL) | 1307 if (*p == NUL) |
1412 { | 1308 { |
1413 xp->xp_context = EXPAND_ENV_VARS; | 1309 xp->xp_context = EXPAND_ENV_VARS; |
1414 ++xp->xp_pattern; | 1310 ++xp->xp_pattern; |
1415 // Avoid that the assignment uses EXPAND_FILES again. | 1311 // Avoid that the assignment uses EXPAND_FILES again. |
1416 if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) | 1312 if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST) |
1417 compl = EXPAND_ENV_VARS; | 1313 *complp = EXPAND_ENV_VARS; |
1418 } | 1314 } |
1419 } | 1315 } |
1420 // Check for user names. | 1316 // Check for user names. |
1421 if (*xp->xp_pattern == '~') | 1317 if (*xp->xp_pattern == '~') |
1422 { | 1318 { |
1423 for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p) | 1319 for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p) |
1424 ; | 1320 ; |
1425 // Complete ~user only if it partially matches a user name. | 1321 // Complete ~user only if it partially matches a user name. |
1426 // A full match ~user<Tab> will be replaced by user's home | 1322 // A full match ~user<Tab> will be replaced by user's home |
1427 // directory i.e. something like ~user<Tab> -> /home/user/ | 1323 // directory i.e. something like ~user<Tab> -> /home/user/ |
1428 if (*p == NUL && p > xp->xp_pattern + 1 | 1324 if (*p == NUL && p > xp->xp_pattern + 1 |
1429 && match_user(xp->xp_pattern + 1) >= 1) | 1325 && match_user(xp->xp_pattern + 1) >= 1) |
1430 { | 1326 { |
1431 xp->xp_context = EXPAND_USER; | 1327 xp->xp_context = EXPAND_USER; |
1432 ++xp->xp_pattern; | 1328 ++xp->xp_pattern; |
1433 } | 1329 } |
1434 } | 1330 } |
1435 } | 1331 } |
1436 | 1332 |
1437 // 6. Switch on command name. | 1333 /* |
1438 switch (ea.cmdidx) | 1334 * Set the completion context in 'xp' for command 'cmd' with index 'cmdidx'. |
1335 * The argument to the command is 'arg' and the argument flags is 'argt'. | |
1336 * For user-defined commands and for environment variables, 'compl' has the | |
1337 * completion type. | |
1338 * Returns a pointer to the next command. Returns NULL if there is no next | |
1339 * command. | |
1340 */ | |
1341 static char_u * | |
1342 set_context_by_cmdname( | |
1343 char_u *cmd, | |
1344 cmdidx_T cmdidx, | |
1345 char_u *arg, | |
1346 long argt, | |
1347 int compl, | |
1348 expand_T *xp, | |
1349 int forceit) | |
1350 { | |
1351 char_u *p; | |
1352 int delim; | |
1353 | |
1354 switch (cmdidx) | |
1439 { | 1355 { |
1440 case CMD_find: | 1356 case CMD_find: |
1441 case CMD_sfind: | 1357 case CMD_sfind: |
1442 case CMD_tabfind: | 1358 case CMD_tabfind: |
1443 if (xp->xp_context == EXPAND_FILES) | 1359 if (xp->xp_context == EXPAND_FILES) |
1656 case CMD_caddexpr: | 1572 case CMD_caddexpr: |
1657 case CMD_cgetexpr: | 1573 case CMD_cgetexpr: |
1658 case CMD_lexpr: | 1574 case CMD_lexpr: |
1659 case CMD_laddexpr: | 1575 case CMD_laddexpr: |
1660 case CMD_lgetexpr: | 1576 case CMD_lgetexpr: |
1661 set_context_for_expression(xp, arg, ea.cmdidx); | 1577 set_context_for_expression(xp, arg, cmdidx); |
1662 break; | 1578 break; |
1663 | 1579 |
1664 case CMD_unlet: | 1580 case CMD_unlet: |
1665 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) | 1581 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) |
1666 arg = xp->xp_pattern + 1; | 1582 arg = xp->xp_pattern + 1; |
1694 break; | 1610 break; |
1695 #ifdef FEAT_CSCOPE | 1611 #ifdef FEAT_CSCOPE |
1696 case CMD_cscope: | 1612 case CMD_cscope: |
1697 case CMD_lcscope: | 1613 case CMD_lcscope: |
1698 case CMD_scscope: | 1614 case CMD_scscope: |
1699 set_context_in_cscope_cmd(xp, arg, ea.cmdidx); | 1615 set_context_in_cscope_cmd(xp, arg, cmdidx); |
1700 break; | 1616 break; |
1701 #endif | 1617 #endif |
1702 #ifdef FEAT_SIGNS | 1618 #ifdef FEAT_SIGNS |
1703 case CMD_sign: | 1619 case CMD_sign: |
1704 set_context_in_sign_cmd(xp, arg); | 1620 set_context_in_sign_cmd(xp, arg); |
1728 case CMD_USER: | 1644 case CMD_USER: |
1729 case CMD_USER_BUF: | 1645 case CMD_USER_BUF: |
1730 if (compl != EXPAND_NOTHING) | 1646 if (compl != EXPAND_NOTHING) |
1731 { | 1647 { |
1732 // EX_XFILE: file names are handled above | 1648 // EX_XFILE: file names are handled above |
1733 if (!(ea.argt & EX_XFILE)) | 1649 if (!(argt & EX_XFILE)) |
1734 { | 1650 { |
1735 #ifdef FEAT_MENU | 1651 #ifdef FEAT_MENU |
1736 if (compl == EXPAND_MENUS) | 1652 if (compl == EXPAND_MENUS) |
1737 return set_context_in_menu_cmd(xp, cmd, arg, forceit); | 1653 return set_context_in_menu_cmd(xp, cmd, arg, forceit); |
1738 #endif | 1654 #endif |
1767 case CMD_lmap: case CMD_lnoremap: | 1683 case CMD_lmap: case CMD_lnoremap: |
1768 case CMD_smap: case CMD_snoremap: | 1684 case CMD_smap: case CMD_snoremap: |
1769 case CMD_tmap: case CMD_tnoremap: | 1685 case CMD_tmap: case CMD_tnoremap: |
1770 case CMD_xmap: case CMD_xnoremap: | 1686 case CMD_xmap: case CMD_xnoremap: |
1771 return set_context_in_map_cmd(xp, cmd, arg, forceit, | 1687 return set_context_in_map_cmd(xp, cmd, arg, forceit, |
1772 FALSE, FALSE, ea.cmdidx); | 1688 FALSE, FALSE, cmdidx); |
1773 case CMD_unmap: | 1689 case CMD_unmap: |
1774 case CMD_nunmap: | 1690 case CMD_nunmap: |
1775 case CMD_vunmap: | 1691 case CMD_vunmap: |
1776 case CMD_ounmap: | 1692 case CMD_ounmap: |
1777 case CMD_iunmap: | 1693 case CMD_iunmap: |
1779 case CMD_lunmap: | 1695 case CMD_lunmap: |
1780 case CMD_sunmap: | 1696 case CMD_sunmap: |
1781 case CMD_tunmap: | 1697 case CMD_tunmap: |
1782 case CMD_xunmap: | 1698 case CMD_xunmap: |
1783 return set_context_in_map_cmd(xp, cmd, arg, forceit, | 1699 return set_context_in_map_cmd(xp, cmd, arg, forceit, |
1784 FALSE, TRUE, ea.cmdidx); | 1700 FALSE, TRUE, cmdidx); |
1785 case CMD_mapclear: | 1701 case CMD_mapclear: |
1786 case CMD_nmapclear: | 1702 case CMD_nmapclear: |
1787 case CMD_vmapclear: | 1703 case CMD_vmapclear: |
1788 case CMD_omapclear: | 1704 case CMD_omapclear: |
1789 case CMD_imapclear: | 1705 case CMD_imapclear: |
1798 | 1714 |
1799 case CMD_abbreviate: case CMD_noreabbrev: | 1715 case CMD_abbreviate: case CMD_noreabbrev: |
1800 case CMD_cabbrev: case CMD_cnoreabbrev: | 1716 case CMD_cabbrev: case CMD_cnoreabbrev: |
1801 case CMD_iabbrev: case CMD_inoreabbrev: | 1717 case CMD_iabbrev: case CMD_inoreabbrev: |
1802 return set_context_in_map_cmd(xp, cmd, arg, forceit, | 1718 return set_context_in_map_cmd(xp, cmd, arg, forceit, |
1803 TRUE, FALSE, ea.cmdidx); | 1719 TRUE, FALSE, cmdidx); |
1804 case CMD_unabbreviate: | 1720 case CMD_unabbreviate: |
1805 case CMD_cunabbrev: | 1721 case CMD_cunabbrev: |
1806 case CMD_iunabbrev: | 1722 case CMD_iunabbrev: |
1807 return set_context_in_map_cmd(xp, cmd, arg, forceit, | 1723 return set_context_in_map_cmd(xp, cmd, arg, forceit, |
1808 TRUE, TRUE, ea.cmdidx); | 1724 TRUE, TRUE, cmdidx); |
1809 #ifdef FEAT_MENU | 1725 #ifdef FEAT_MENU |
1810 case CMD_menu: case CMD_noremenu: case CMD_unmenu: | 1726 case CMD_menu: case CMD_noremenu: case CMD_unmenu: |
1811 case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: | 1727 case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: |
1812 case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: | 1728 case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: |
1813 case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu: | 1729 case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu: |
1905 break; | 1821 break; |
1906 } | 1822 } |
1907 return NULL; | 1823 return NULL; |
1908 } | 1824 } |
1909 | 1825 |
1826 /* | |
1827 * This is all pretty much copied from do_one_cmd(), with all the extra stuff | |
1828 * we don't need/want deleted. Maybe this could be done better if we didn't | |
1829 * repeat all this stuff. The only problem is that they may not stay | |
1830 * perfectly compatible with each other, but then the command line syntax | |
1831 * probably won't change that much -- webb. | |
1832 */ | |
1833 static char_u * | |
1834 set_one_cmd_context( | |
1835 expand_T *xp, | |
1836 char_u *buff) // buffer for command string | |
1837 { | |
1838 char_u *p; | |
1839 char_u *cmd, *arg; | |
1840 int len = 0; | |
1841 exarg_T ea; | |
1842 int compl = EXPAND_NOTHING; | |
1843 int forceit = FALSE; | |
1844 int usefilter = FALSE; // filter instead of file name | |
1845 | |
1846 ExpandInit(xp); | |
1847 xp->xp_pattern = buff; | |
1848 xp->xp_line = buff; | |
1849 xp->xp_context = EXPAND_COMMANDS; // Default until we get past command | |
1850 ea.argt = 0; | |
1851 | |
1852 // 1. skip comment lines and leading space, colons or bars | |
1853 for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++) | |
1854 ; | |
1855 xp->xp_pattern = cmd; | |
1856 | |
1857 if (*cmd == NUL) | |
1858 return NULL; | |
1859 if (*cmd == '"') // ignore comment lines | |
1860 { | |
1861 xp->xp_context = EXPAND_NOTHING; | |
1862 return NULL; | |
1863 } | |
1864 | |
1865 // 3. Skip over the range to find the command. | |
1866 cmd = skip_range(cmd, TRUE, &xp->xp_context); | |
1867 xp->xp_pattern = cmd; | |
1868 if (*cmd == NUL) | |
1869 return NULL; | |
1870 if (*cmd == '"') | |
1871 { | |
1872 xp->xp_context = EXPAND_NOTHING; | |
1873 return NULL; | |
1874 } | |
1875 | |
1876 if (*cmd == '|' || *cmd == '\n') | |
1877 return cmd + 1; // There's another command | |
1878 | |
1879 // Get the command index. | |
1880 p = set_cmd_index(cmd, &ea, xp, &compl); | |
1881 if (p == NULL) | |
1882 return NULL; | |
1883 | |
1884 xp->xp_context = EXPAND_NOTHING; // Default now that we're past command | |
1885 | |
1886 if (*p == '!') // forced commands | |
1887 { | |
1888 forceit = TRUE; | |
1889 ++p; | |
1890 } | |
1891 | |
1892 // 6. parse arguments | |
1893 if (!IS_USER_CMDIDX(ea.cmdidx)) | |
1894 ea.argt = excmd_get_argt(ea.cmdidx); | |
1895 | |
1896 arg = skipwhite(p); | |
1897 | |
1898 // Skip over ++argopt argument | |
1899 if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0) | |
1900 { | |
1901 p = arg; | |
1902 while (*p && !vim_isspace(*p)) | |
1903 MB_PTR_ADV(p); | |
1904 arg = skipwhite(p); | |
1905 } | |
1906 | |
1907 if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) | |
1908 { | |
1909 if (*arg == '>') // append | |
1910 { | |
1911 if (*++arg == '>') | |
1912 ++arg; | |
1913 arg = skipwhite(arg); | |
1914 } | |
1915 else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter | |
1916 { | |
1917 ++arg; | |
1918 usefilter = TRUE; | |
1919 } | |
1920 } | |
1921 | |
1922 if (ea.cmdidx == CMD_read) | |
1923 { | |
1924 usefilter = forceit; // :r! filter if forced | |
1925 if (*arg == '!') // :r !filter | |
1926 { | |
1927 ++arg; | |
1928 usefilter = TRUE; | |
1929 } | |
1930 } | |
1931 | |
1932 if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) | |
1933 { | |
1934 while (*arg == *cmd) // allow any number of '>' or '<' | |
1935 ++arg; | |
1936 arg = skipwhite(arg); | |
1937 } | |
1938 | |
1939 // Does command allow "+command"? | |
1940 if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') | |
1941 { | |
1942 // Check if we're in the +command | |
1943 p = arg + 1; | |
1944 arg = skip_cmd_arg(arg, FALSE); | |
1945 | |
1946 // Still touching the command after '+'? | |
1947 if (*arg == NUL) | |
1948 return p; | |
1949 | |
1950 // Skip space(s) after +command to get to the real argument | |
1951 arg = skipwhite(arg); | |
1952 } | |
1953 | |
1954 | |
1955 // Check for '|' to separate commands and '"' to start comments. | |
1956 // Don't do this for ":read !cmd" and ":write !cmd". | |
1957 if ((ea.argt & EX_TRLBAR) && !usefilter) | |
1958 { | |
1959 p = arg; | |
1960 // ":redir @" is not the start of a comment | |
1961 if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') | |
1962 p += 2; | |
1963 while (*p) | |
1964 { | |
1965 if (*p == Ctrl_V) | |
1966 { | |
1967 if (p[1] != NUL) | |
1968 ++p; | |
1969 } | |
1970 else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM)) | |
1971 || *p == '|' || *p == '\n') | |
1972 { | |
1973 if (*(p - 1) != '\\') | |
1974 { | |
1975 if (*p == '|' || *p == '\n') | |
1976 return p + 1; | |
1977 return NULL; // It's a comment | |
1978 } | |
1979 } | |
1980 MB_PTR_ADV(p); | |
1981 } | |
1982 } | |
1983 | |
1984 if (!(ea.argt & EX_EXTRA) && *arg != NUL | |
1985 && vim_strchr((char_u *)"|\"", *arg) == NULL) | |
1986 // no arguments allowed but there is something | |
1987 return NULL; | |
1988 | |
1989 // Find start of last argument (argument just before cursor): | |
1990 p = buff; | |
1991 xp->xp_pattern = p; | |
1992 len = (int)STRLEN(buff); | |
1993 while (*p && p < buff + len) | |
1994 { | |
1995 if (*p == ' ' || *p == TAB) | |
1996 { | |
1997 // argument starts after a space | |
1998 xp->xp_pattern = ++p; | |
1999 } | |
2000 else | |
2001 { | |
2002 if (*p == '\\' && *(p + 1) != NUL) | |
2003 ++p; // skip over escaped character | |
2004 MB_PTR_ADV(p); | |
2005 } | |
2006 } | |
2007 | |
2008 if (ea.argt & EX_XFILE) | |
2009 set_context_for_wildcard_arg(&ea, arg, usefilter, xp, &compl); | |
2010 | |
2011 // 6. Switch on command name. | |
2012 return set_context_by_cmdname(cmd, ea.cmdidx, arg, ea.argt, compl, xp, | |
2013 forceit); | |
2014 } | |
2015 | |
1910 void | 2016 void |
1911 set_cmd_context( | 2017 set_cmd_context( |
1912 expand_T *xp, | 2018 expand_T *xp, |
1913 char_u *str, // start of command line | 2019 char_u *str, // start of command line |
1914 int len, // length of command line (excl. NUL) | 2020 int len, // length of command line (excl. NUL) |
2005 | 2111 |
2006 return EXPAND_OK; | 2112 return EXPAND_OK; |
2007 } | 2113 } |
2008 | 2114 |
2009 /* | 2115 /* |
2116 * Expand file or directory names. | |
2117 */ | |
2118 static int | |
2119 expand_files_and_dirs( | |
2120 expand_T *xp, | |
2121 char_u *pat, | |
2122 char_u ***file, | |
2123 int *num_file, | |
2124 int flags, | |
2125 int options) | |
2126 { | |
2127 int free_pat = FALSE; | |
2128 int i; | |
2129 int ret; | |
2130 | |
2131 // for ":set path=" and ":set tags=" halve backslashes for escaped | |
2132 // space | |
2133 if (xp->xp_backslash != XP_BS_NONE) | |
2134 { | |
2135 free_pat = TRUE; | |
2136 pat = vim_strsave(pat); | |
2137 for (i = 0; pat[i]; ++i) | |
2138 if (pat[i] == '\\') | |
2139 { | |
2140 if (xp->xp_backslash == XP_BS_THREE | |
2141 && pat[i + 1] == '\\' | |
2142 && pat[i + 2] == '\\' | |
2143 && pat[i + 3] == ' ') | |
2144 STRMOVE(pat + i, pat + i + 3); | |
2145 if (xp->xp_backslash == XP_BS_ONE | |
2146 && pat[i + 1] == ' ') | |
2147 STRMOVE(pat + i, pat + i + 1); | |
2148 } | |
2149 } | |
2150 | |
2151 if (xp->xp_context == EXPAND_FILES) | |
2152 flags |= EW_FILE; | |
2153 else if (xp->xp_context == EXPAND_FILES_IN_PATH) | |
2154 flags |= (EW_FILE | EW_PATH); | |
2155 else | |
2156 flags = (flags | EW_DIR) & ~EW_FILE; | |
2157 if (options & WILD_ICASE) | |
2158 flags |= EW_ICASE; | |
2159 | |
2160 // Expand wildcards, supporting %:h and the like. | |
2161 ret = expand_wildcards_eval(&pat, num_file, file, flags); | |
2162 if (free_pat) | |
2163 vim_free(pat); | |
2164 #ifdef BACKSLASH_IN_FILENAME | |
2165 if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) | |
2166 { | |
2167 int j; | |
2168 | |
2169 for (j = 0; j < *num_file; ++j) | |
2170 { | |
2171 char_u *ptr = (*file)[j]; | |
2172 | |
2173 while (*ptr != NUL) | |
2174 { | |
2175 if (p_csl[0] == 's' && *ptr == '\\') | |
2176 *ptr = '/'; | |
2177 else if (p_csl[0] == 'b' && *ptr == '/') | |
2178 *ptr = '\\'; | |
2179 ptr += (*mb_ptr2len)(ptr); | |
2180 } | |
2181 } | |
2182 } | |
2183 #endif | |
2184 return ret; | |
2185 } | |
2186 | |
2187 /* | |
2010 * Function given to ExpandGeneric() to obtain the possible arguments of the | 2188 * Function given to ExpandGeneric() to obtain the possible arguments of the |
2011 * ":behave {mswin,xterm}" command. | 2189 * ":behave {mswin,xterm}" command. |
2012 */ | 2190 */ |
2013 static char_u * | 2191 static char_u * |
2014 get_behave_arg(expand_T *xp UNUSED, int idx) | 2192 get_behave_arg(expand_T *xp UNUSED, int idx) |
2036 get_mapclear_arg(expand_T *xp UNUSED, int idx) | 2214 get_mapclear_arg(expand_T *xp UNUSED, int idx) |
2037 { | 2215 { |
2038 if (idx == 0) | 2216 if (idx == 0) |
2039 return (char_u *)"<buffer>"; | 2217 return (char_u *)"<buffer>"; |
2040 return NULL; | 2218 return NULL; |
2219 } | |
2220 | |
2221 /* | |
2222 * Do the expansion based on xp->xp_context and 'rmp'. | |
2223 */ | |
2224 static int | |
2225 ExpandOther( | |
2226 expand_T *xp, | |
2227 regmatch_T *rmp, | |
2228 int *num_file, | |
2229 char_u ***file) | |
2230 { | |
2231 static struct expgen | |
2232 { | |
2233 int context; | |
2234 char_u *((*func)(expand_T *, int)); | |
2235 int ic; | |
2236 int escaped; | |
2237 } tab[] = | |
2238 { | |
2239 {EXPAND_COMMANDS, get_command_name, FALSE, TRUE}, | |
2240 {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE}, | |
2241 {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE}, | |
2242 {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE}, | |
2243 {EXPAND_HISTORY, get_history_arg, TRUE, TRUE}, | |
2244 {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE}, | |
2245 {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE}, | |
2246 {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE}, | |
2247 {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE}, | |
2248 {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE}, | |
2249 # ifdef FEAT_EVAL | |
2250 {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE}, | |
2251 {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE}, | |
2252 {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE}, | |
2253 {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE}, | |
2254 {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE}, | |
2255 # endif | |
2256 # ifdef FEAT_MENU | |
2257 {EXPAND_MENUS, get_menu_name, FALSE, TRUE}, | |
2258 {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE}, | |
2259 # endif | |
2260 # ifdef FEAT_SYN_HL | |
2261 {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE}, | |
2262 # endif | |
2263 # ifdef FEAT_PROFILE | |
2264 {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE}, | |
2265 # endif | |
2266 {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE}, | |
2267 {EXPAND_EVENTS, get_event_name, TRUE, FALSE}, | |
2268 {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE}, | |
2269 # ifdef FEAT_CSCOPE | |
2270 {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE}, | |
2271 # endif | |
2272 # ifdef FEAT_SIGNS | |
2273 {EXPAND_SIGN, get_sign_name, TRUE, TRUE}, | |
2274 # endif | |
2275 # ifdef FEAT_PROFILE | |
2276 {EXPAND_PROFILE, get_profile_name, TRUE, TRUE}, | |
2277 # endif | |
2278 # if defined(HAVE_LOCALE_H) || defined(X_LOCALE) | |
2279 {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE}, | |
2280 {EXPAND_LOCALES, get_locales, TRUE, FALSE}, | |
2281 # endif | |
2282 {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE}, | |
2283 {EXPAND_USER, get_users, TRUE, FALSE}, | |
2284 {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE}, | |
2285 }; | |
2286 int i; | |
2287 int ret = FAIL; | |
2288 | |
2289 // Find a context in the table and call the ExpandGeneric() with the | |
2290 // right function to do the expansion. | |
2291 for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i) | |
2292 { | |
2293 if (xp->xp_context == tab[i].context) | |
2294 { | |
2295 if (tab[i].ic) | |
2296 rmp->rm_ic = TRUE; | |
2297 ret = ExpandGeneric(xp, rmp, num_file, file, | |
2298 tab[i].func, tab[i].escaped); | |
2299 break; | |
2300 } | |
2301 } | |
2302 | |
2303 return ret; | |
2041 } | 2304 } |
2042 | 2305 |
2043 /* | 2306 /* |
2044 * Do the expansion based on xp->xp_context and "pat". | 2307 * Do the expansion based on xp->xp_context and "pat". |
2045 */ | 2308 */ |
2071 flags |= EW_ALLLINKS; | 2334 flags |= EW_ALLLINKS; |
2072 | 2335 |
2073 if (xp->xp_context == EXPAND_FILES | 2336 if (xp->xp_context == EXPAND_FILES |
2074 || xp->xp_context == EXPAND_DIRECTORIES | 2337 || xp->xp_context == EXPAND_DIRECTORIES |
2075 || xp->xp_context == EXPAND_FILES_IN_PATH) | 2338 || xp->xp_context == EXPAND_FILES_IN_PATH) |
2076 { | 2339 return expand_files_and_dirs(xp, pat, file, num_file, flags, options); |
2077 // Expand file or directory names. | |
2078 int free_pat = FALSE; | |
2079 int i; | |
2080 | |
2081 // for ":set path=" and ":set tags=" halve backslashes for escaped | |
2082 // space | |
2083 if (xp->xp_backslash != XP_BS_NONE) | |
2084 { | |
2085 free_pat = TRUE; | |
2086 pat = vim_strsave(pat); | |
2087 for (i = 0; pat[i]; ++i) | |
2088 if (pat[i] == '\\') | |
2089 { | |
2090 if (xp->xp_backslash == XP_BS_THREE | |
2091 && pat[i + 1] == '\\' | |
2092 && pat[i + 2] == '\\' | |
2093 && pat[i + 3] == ' ') | |
2094 STRMOVE(pat + i, pat + i + 3); | |
2095 if (xp->xp_backslash == XP_BS_ONE | |
2096 && pat[i + 1] == ' ') | |
2097 STRMOVE(pat + i, pat + i + 1); | |
2098 } | |
2099 } | |
2100 | |
2101 if (xp->xp_context == EXPAND_FILES) | |
2102 flags |= EW_FILE; | |
2103 else if (xp->xp_context == EXPAND_FILES_IN_PATH) | |
2104 flags |= (EW_FILE | EW_PATH); | |
2105 else | |
2106 flags = (flags | EW_DIR) & ~EW_FILE; | |
2107 if (options & WILD_ICASE) | |
2108 flags |= EW_ICASE; | |
2109 | |
2110 // Expand wildcards, supporting %:h and the like. | |
2111 ret = expand_wildcards_eval(&pat, num_file, file, flags); | |
2112 if (free_pat) | |
2113 vim_free(pat); | |
2114 #ifdef BACKSLASH_IN_FILENAME | |
2115 if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) | |
2116 { | |
2117 int j; | |
2118 | |
2119 for (j = 0; j < *num_file; ++j) | |
2120 { | |
2121 char_u *ptr = (*file)[j]; | |
2122 | |
2123 while (*ptr != NUL) | |
2124 { | |
2125 if (p_csl[0] == 's' && *ptr == '\\') | |
2126 *ptr = '/'; | |
2127 else if (p_csl[0] == 'b' && *ptr == '/') | |
2128 *ptr = '\\'; | |
2129 ptr += (*mb_ptr2len)(ptr); | |
2130 } | |
2131 } | |
2132 } | |
2133 #endif | |
2134 return ret; | |
2135 } | |
2136 | 2340 |
2137 *file = (char_u **)""; | 2341 *file = (char_u **)""; |
2138 *num_file = 0; | 2342 *num_file = 0; |
2139 if (xp->xp_context == EXPAND_HELP) | 2343 if (xp->xp_context == EXPAND_HELP) |
2140 { | 2344 { |
2220 # if defined(FEAT_EVAL) | 2424 # if defined(FEAT_EVAL) |
2221 else if (xp->xp_context == EXPAND_USER_DEFINED) | 2425 else if (xp->xp_context == EXPAND_USER_DEFINED) |
2222 ret = ExpandUserDefined(xp, ®match, num_file, file); | 2426 ret = ExpandUserDefined(xp, ®match, num_file, file); |
2223 # endif | 2427 # endif |
2224 else | 2428 else |
2225 { | 2429 ret = ExpandOther(xp, ®match, num_file, file); |
2226 static struct expgen | |
2227 { | |
2228 int context; | |
2229 char_u *((*func)(expand_T *, int)); | |
2230 int ic; | |
2231 int escaped; | |
2232 } tab[] = | |
2233 { | |
2234 {EXPAND_COMMANDS, get_command_name, FALSE, TRUE}, | |
2235 {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE}, | |
2236 {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE}, | |
2237 {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE}, | |
2238 {EXPAND_HISTORY, get_history_arg, TRUE, TRUE}, | |
2239 {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE}, | |
2240 {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE}, | |
2241 {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE}, | |
2242 {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE}, | |
2243 {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE}, | |
2244 # ifdef FEAT_EVAL | |
2245 {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE}, | |
2246 {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE}, | |
2247 {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE}, | |
2248 {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE}, | |
2249 {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE}, | |
2250 # endif | |
2251 # ifdef FEAT_MENU | |
2252 {EXPAND_MENUS, get_menu_name, FALSE, TRUE}, | |
2253 {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE}, | |
2254 # endif | |
2255 # ifdef FEAT_SYN_HL | |
2256 {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE}, | |
2257 # endif | |
2258 # ifdef FEAT_PROFILE | |
2259 {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE}, | |
2260 # endif | |
2261 {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE}, | |
2262 {EXPAND_EVENTS, get_event_name, TRUE, FALSE}, | |
2263 {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE}, | |
2264 # ifdef FEAT_CSCOPE | |
2265 {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE}, | |
2266 # endif | |
2267 # ifdef FEAT_SIGNS | |
2268 {EXPAND_SIGN, get_sign_name, TRUE, TRUE}, | |
2269 # endif | |
2270 # ifdef FEAT_PROFILE | |
2271 {EXPAND_PROFILE, get_profile_name, TRUE, TRUE}, | |
2272 # endif | |
2273 # if defined(HAVE_LOCALE_H) || defined(X_LOCALE) | |
2274 {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE}, | |
2275 {EXPAND_LOCALES, get_locales, TRUE, FALSE}, | |
2276 # endif | |
2277 {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE}, | |
2278 {EXPAND_USER, get_users, TRUE, FALSE}, | |
2279 {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE}, | |
2280 }; | |
2281 int i; | |
2282 | |
2283 // Find a context in the table and call the ExpandGeneric() with the | |
2284 // right function to do the expansion. | |
2285 ret = FAIL; | |
2286 for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i) | |
2287 if (xp->xp_context == tab[i].context) | |
2288 { | |
2289 if (tab[i].ic) | |
2290 regmatch.rm_ic = TRUE; | |
2291 ret = ExpandGeneric(xp, ®match, num_file, file, | |
2292 tab[i].func, tab[i].escaped); | |
2293 break; | |
2294 } | |
2295 } | |
2296 | 2430 |
2297 vim_regfree(regmatch.regprog); | 2431 vim_regfree(regmatch.regprog); |
2298 vim_free(tofree); | 2432 vim_free(tofree); |
2299 | 2433 |
2300 return ret; | 2434 return ret; |