Mercurial > vim
comparison src/cmdexpand.c @ 27744:515ce8e07bf2 v8.2.4398
patch 8.2.4398: some command completion functions are too long
Commit: https://github.com/vim/vim/commit/b31aec3b9387ed12677dca09069c3ae98c6c7447
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Wed Feb 16 12:44:29 2022 +0000
patch 8.2.4398: some command completion functions are too long
Problem: Some command completion functions are too long.
Solution: Refactor code into separate functions. Add a few more tests.
(Yegappan Lakshmanan, closes #9785)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Wed, 16 Feb 2022 13:45:04 +0100 |
parents | 637ccebaf328 |
children | 010fa62d6fe2 |
comparison
equal
deleted
inserted
replaced
27743:629769fd1ff1 | 27744:515ce8e07bf2 |
---|---|
680 xp->xp_numfiles = -1; | 680 xp->xp_numfiles = -1; |
681 } | 681 } |
682 } | 682 } |
683 | 683 |
684 /* | 684 /* |
685 * Display one line of completion matches. Multiple matches are displayed in | |
686 * each line (used by wildmode=list and CTRL-D) | |
687 * files_found - list of completion match names | |
688 * num_files - number of completion matches in "files_found" | |
689 * lines - number of output lines | |
690 * linenr - line number of matches to display | |
691 * maxlen - maximum number of characters in each line | |
692 * showtail - display only the tail of the full path of a file name | |
693 * dir_attr - highlight attribute to use for directory names | |
694 */ | |
695 static void | |
696 showmatches_oneline( | |
697 expand_T *xp, | |
698 char_u **files_found, | |
699 int num_files, | |
700 int lines, | |
701 int linenr, | |
702 int maxlen, | |
703 int showtail, | |
704 int dir_attr) | |
705 { | |
706 int i, j; | |
707 int isdir; | |
708 int lastlen; | |
709 char_u *p; | |
710 | |
711 lastlen = 999; | |
712 for (j = linenr; j < num_files; j += lines) | |
713 { | |
714 if (xp->xp_context == EXPAND_TAGS_LISTFILES) | |
715 { | |
716 msg_outtrans_attr(files_found[j], HL_ATTR(HLF_D)); | |
717 p = files_found[j] + STRLEN(files_found[j]) + 1; | |
718 msg_advance(maxlen + 1); | |
719 msg_puts((char *)p); | |
720 msg_advance(maxlen + 3); | |
721 msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D)); | |
722 break; | |
723 } | |
724 for (i = maxlen - lastlen; --i >= 0; ) | |
725 msg_putchar(' '); | |
726 if (xp->xp_context == EXPAND_FILES | |
727 || xp->xp_context == EXPAND_SHELLCMD | |
728 || xp->xp_context == EXPAND_BUFFERS) | |
729 { | |
730 // highlight directories | |
731 if (xp->xp_numfiles != -1) | |
732 { | |
733 char_u *halved_slash; | |
734 char_u *exp_path; | |
735 char_u *path; | |
736 | |
737 // Expansion was done before and special characters | |
738 // were escaped, need to halve backslashes. Also | |
739 // $HOME has been replaced with ~/. | |
740 exp_path = expand_env_save_opt(files_found[j], TRUE); | |
741 path = exp_path != NULL ? exp_path : files_found[j]; | |
742 halved_slash = backslash_halve_save(path); | |
743 isdir = mch_isdir(halved_slash != NULL ? halved_slash | |
744 : files_found[j]); | |
745 vim_free(exp_path); | |
746 if (halved_slash != path) | |
747 vim_free(halved_slash); | |
748 } | |
749 else | |
750 // Expansion was done here, file names are literal. | |
751 isdir = mch_isdir(files_found[j]); | |
752 if (showtail) | |
753 p = SHOW_FILE_TEXT(j); | |
754 else | |
755 { | |
756 home_replace(NULL, files_found[j], NameBuff, MAXPATHL, | |
757 TRUE); | |
758 p = NameBuff; | |
759 } | |
760 } | |
761 else | |
762 { | |
763 isdir = FALSE; | |
764 p = SHOW_FILE_TEXT(j); | |
765 } | |
766 lastlen = msg_outtrans_attr(p, isdir ? dir_attr : 0); | |
767 } | |
768 if (msg_col > 0) // when not wrapped around | |
769 { | |
770 msg_clr_eos(); | |
771 msg_putchar('\n'); | |
772 } | |
773 out_flush(); // show one line at a time | |
774 } | |
775 | |
776 /* | |
685 * Show all matches for completion on the command line. | 777 * Show all matches for completion on the command line. |
686 * Returns EXPAND_NOTHING when the character that triggered expansion should | 778 * Returns EXPAND_NOTHING when the character that triggered expansion should |
687 * be inserted like a normal character. | 779 * be inserted like a normal character. |
688 */ | 780 */ |
689 int | 781 int |
690 showmatches(expand_T *xp, int wildmenu UNUSED) | 782 showmatches(expand_T *xp, int wildmenu UNUSED) |
691 { | 783 { |
692 cmdline_info_T *ccline = get_cmdline_info(); | 784 cmdline_info_T *ccline = get_cmdline_info(); |
693 int num_files; | 785 int num_files; |
694 char_u **files_found; | 786 char_u **files_found; |
695 int i, j, k; | 787 int i, j; |
696 int maxlen; | 788 int maxlen; |
697 int lines; | 789 int lines; |
698 int columns; | 790 int columns; |
699 char_u *p; | |
700 int lastlen; | |
701 int attr; | 791 int attr; |
702 int showtail; | 792 int showtail; |
703 | 793 |
704 if (xp->xp_numfiles == -1) | 794 if (xp->xp_numfiles == -1) |
705 { | 795 { |
707 i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos, | 797 i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos, |
708 &num_files, &files_found); | 798 &num_files, &files_found); |
709 showtail = expand_showtail(xp); | 799 showtail = expand_showtail(xp); |
710 if (i != EXPAND_OK) | 800 if (i != EXPAND_OK) |
711 return i; | 801 return i; |
712 | |
713 } | 802 } |
714 else | 803 else |
715 { | 804 { |
716 num_files = xp->xp_numfiles; | 805 num_files = xp->xp_numfiles; |
717 files_found = xp->xp_files; | 806 files_found = xp->xp_files; |
787 } | 876 } |
788 | 877 |
789 // list the files line by line | 878 // list the files line by line |
790 for (i = 0; i < lines; ++i) | 879 for (i = 0; i < lines; ++i) |
791 { | 880 { |
792 lastlen = 999; | 881 showmatches_oneline(xp, files_found, num_files, lines, i, |
793 for (k = i; k < num_files; k += lines) | 882 maxlen, showtail, attr); |
794 { | |
795 if (xp->xp_context == EXPAND_TAGS_LISTFILES) | |
796 { | |
797 msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D)); | |
798 p = files_found[k] + STRLEN(files_found[k]) + 1; | |
799 msg_advance(maxlen + 1); | |
800 msg_puts((char *)p); | |
801 msg_advance(maxlen + 3); | |
802 msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D)); | |
803 break; | |
804 } | |
805 for (j = maxlen - lastlen; --j >= 0; ) | |
806 msg_putchar(' '); | |
807 if (xp->xp_context == EXPAND_FILES | |
808 || xp->xp_context == EXPAND_SHELLCMD | |
809 || xp->xp_context == EXPAND_BUFFERS) | |
810 { | |
811 // highlight directories | |
812 if (xp->xp_numfiles != -1) | |
813 { | |
814 char_u *halved_slash; | |
815 char_u *exp_path; | |
816 char_u *path; | |
817 | |
818 // Expansion was done before and special characters | |
819 // were escaped, need to halve backslashes. Also | |
820 // $HOME has been replaced with ~/. | |
821 exp_path = expand_env_save_opt(files_found[k], TRUE); | |
822 path = exp_path != NULL ? exp_path : files_found[k]; | |
823 halved_slash = backslash_halve_save(path); | |
824 j = mch_isdir(halved_slash != NULL ? halved_slash | |
825 : files_found[k]); | |
826 vim_free(exp_path); | |
827 if (halved_slash != path) | |
828 vim_free(halved_slash); | |
829 } | |
830 else | |
831 // Expansion was done here, file names are literal. | |
832 j = mch_isdir(files_found[k]); | |
833 if (showtail) | |
834 p = SHOW_FILE_TEXT(k); | |
835 else | |
836 { | |
837 home_replace(NULL, files_found[k], NameBuff, MAXPATHL, | |
838 TRUE); | |
839 p = NameBuff; | |
840 } | |
841 } | |
842 else | |
843 { | |
844 j = FALSE; | |
845 p = SHOW_FILE_TEXT(k); | |
846 } | |
847 lastlen = msg_outtrans_attr(p, j ? attr : 0); | |
848 } | |
849 if (msg_col > 0) // when not wrapped around | |
850 { | |
851 msg_clr_eos(); | |
852 msg_putchar('\n'); | |
853 } | |
854 out_flush(); // show one line at a time | |
855 if (got_int) | 883 if (got_int) |
856 { | 884 { |
857 got_int = FALSE; | 885 got_int = FALSE; |
858 break; | 886 break; |
859 } | 887 } |
1332 } | 1360 } |
1333 } | 1361 } |
1334 } | 1362 } |
1335 | 1363 |
1336 /* | 1364 /* |
1365 * Returns a pointer to the next command after a :substitute or a :& command. | |
1366 * Returns NULL if there is no next command. | |
1367 */ | |
1368 static char_u * | |
1369 find_cmd_after_substitute_cmd(char_u *arg) | |
1370 { | |
1371 int delim; | |
1372 | |
1373 delim = *arg; | |
1374 if (delim) | |
1375 { | |
1376 // skip "from" part | |
1377 ++arg; | |
1378 arg = skip_regexp(arg, delim, magic_isset()); | |
1379 | |
1380 if (arg[0] != NUL && arg[0] == delim) | |
1381 { | |
1382 // skip "to" part | |
1383 ++arg; | |
1384 while (arg[0] != NUL && arg[0] != delim) | |
1385 { | |
1386 if (arg[0] == '\\' && arg[1] != NUL) | |
1387 ++arg; | |
1388 ++arg; | |
1389 } | |
1390 if (arg[0] != NUL) // skip delimiter | |
1391 ++arg; | |
1392 } | |
1393 } | |
1394 while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) | |
1395 ++arg; | |
1396 if (arg[0] != NUL) | |
1397 return arg; | |
1398 | |
1399 return NULL; | |
1400 } | |
1401 | |
1402 /* | |
1403 * Returns a pointer to the next command after a :isearch/:dsearch/:ilist | |
1404 * :dlist/:ijump/:psearch/:djump/:isplit/:dsplit command. | |
1405 * Returns NULL if there is no next command. | |
1406 */ | |
1407 static char_u * | |
1408 find_cmd_after_isearch_cmd(char_u *arg, expand_T *xp) | |
1409 { | |
1410 arg = skipwhite(skipdigits(arg)); // skip count | |
1411 if (*arg == '/') // Match regexp, not just whole words | |
1412 { | |
1413 for (++arg; *arg && *arg != '/'; arg++) | |
1414 if (*arg == '\\' && arg[1] != NUL) | |
1415 arg++; | |
1416 if (*arg) | |
1417 { | |
1418 arg = skipwhite(arg + 1); | |
1419 | |
1420 // Check for trailing illegal characters | |
1421 if (*arg == NUL || vim_strchr((char_u *)"|\"\n", *arg) == NULL) | |
1422 xp->xp_context = EXPAND_NOTHING; | |
1423 else | |
1424 return arg; | |
1425 } | |
1426 } | |
1427 | |
1428 return NULL; | |
1429 } | |
1430 | |
1431 /* | |
1337 * Set the completion context in 'xp' for command 'cmd' with index 'cmdidx'. | 1432 * Set the completion context in 'xp' for command 'cmd' with index 'cmdidx'. |
1338 * The argument to the command is 'arg' and the argument flags is 'argt'. | 1433 * The argument to the command is 'arg' and the argument flags is 'argt'. |
1339 * For user-defined commands and for environment variables, 'compl' has the | 1434 * For user-defined commands and for environment variables, 'compl' has the |
1340 * completion type. | 1435 * completion type. |
1341 * Returns a pointer to the next command. Returns NULL if there is no next | 1436 * Returns a pointer to the next command. Returns NULL if there is no next |
1465 if (arg[0] != NUL) | 1560 if (arg[0] != NUL) |
1466 return arg + 1; | 1561 return arg + 1; |
1467 break; | 1562 break; |
1468 case CMD_and: | 1563 case CMD_and: |
1469 case CMD_substitute: | 1564 case CMD_substitute: |
1470 delim = *arg; | 1565 return find_cmd_after_substitute_cmd(arg); |
1471 if (delim) | |
1472 { | |
1473 // skip "from" part | |
1474 ++arg; | |
1475 arg = skip_regexp(arg, delim, magic_isset()); | |
1476 | |
1477 if (arg[0] != NUL && arg[0] == delim) | |
1478 { | |
1479 // skip "to" part | |
1480 ++arg; | |
1481 while (arg[0] != NUL && arg[0] != delim) | |
1482 { | |
1483 if (arg[0] == '\\' && arg[1] != NUL) | |
1484 ++arg; | |
1485 ++arg; | |
1486 } | |
1487 if (arg[0] != NUL) // skip delimiter | |
1488 ++arg; | |
1489 } | |
1490 } | |
1491 while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) | |
1492 ++arg; | |
1493 if (arg[0] != NUL) | |
1494 return arg; | |
1495 break; | |
1496 case CMD_isearch: | 1566 case CMD_isearch: |
1497 case CMD_dsearch: | 1567 case CMD_dsearch: |
1498 case CMD_ilist: | 1568 case CMD_ilist: |
1499 case CMD_dlist: | 1569 case CMD_dlist: |
1500 case CMD_ijump: | 1570 case CMD_ijump: |
1501 case CMD_psearch: | 1571 case CMD_psearch: |
1502 case CMD_djump: | 1572 case CMD_djump: |
1503 case CMD_isplit: | 1573 case CMD_isplit: |
1504 case CMD_dsplit: | 1574 case CMD_dsplit: |
1505 arg = skipwhite(skipdigits(arg)); // skip count | 1575 return find_cmd_after_isearch_cmd(arg, xp); |
1506 if (*arg == '/') // Match regexp, not just whole words | |
1507 { | |
1508 for (++arg; *arg && *arg != '/'; arg++) | |
1509 if (*arg == '\\' && arg[1] != NUL) | |
1510 arg++; | |
1511 if (*arg) | |
1512 { | |
1513 arg = skipwhite(arg + 1); | |
1514 | |
1515 // Check for trailing illegal characters | |
1516 if (*arg == NUL || | |
1517 vim_strchr((char_u *)"|\"\n", *arg) == NULL) | |
1518 xp->xp_context = EXPAND_NOTHING; | |
1519 else | |
1520 return arg; | |
1521 } | |
1522 } | |
1523 break; | |
1524 | |
1525 case CMD_autocmd: | 1576 case CMD_autocmd: |
1526 return set_context_in_autocmd(xp, arg, FALSE); | 1577 return set_context_in_autocmd(xp, arg, FALSE); |
1527 case CMD_doautocmd: | 1578 case CMD_doautocmd: |
1528 case CMD_doautoall: | 1579 case CMD_doautoall: |
1529 return set_context_in_autocmd(xp, arg, TRUE); | 1580 return set_context_in_autocmd(xp, arg, TRUE); |
1650 xp->xp_pattern = arg; | 1701 xp->xp_pattern = arg; |
1651 break; | 1702 break; |
1652 #endif | 1703 #endif |
1653 case CMD_USER: | 1704 case CMD_USER: |
1654 case CMD_USER_BUF: | 1705 case CMD_USER_BUF: |
1655 if (compl != EXPAND_NOTHING) | 1706 return set_context_in_user_cmdarg(cmd, arg, argt, compl, xp, |
1656 { | 1707 forceit); |
1657 // EX_XFILE: file names are handled above | |
1658 if (!(argt & EX_XFILE)) | |
1659 { | |
1660 #ifdef FEAT_MENU | |
1661 if (compl == EXPAND_MENUS) | |
1662 return set_context_in_menu_cmd(xp, cmd, arg, forceit); | |
1663 #endif | |
1664 if (compl == EXPAND_COMMANDS) | |
1665 return arg; | |
1666 if (compl == EXPAND_MAPPINGS) | |
1667 return set_context_in_map_cmd(xp, (char_u *)"map", | |
1668 arg, forceit, FALSE, FALSE, CMD_map); | |
1669 // Find start of last argument. | |
1670 p = arg; | |
1671 while (*p) | |
1672 { | |
1673 if (*p == ' ') | |
1674 // argument starts after a space | |
1675 arg = p + 1; | |
1676 else if (*p == '\\' && *(p + 1) != NUL) | |
1677 ++p; // skip over escaped character | |
1678 MB_PTR_ADV(p); | |
1679 } | |
1680 xp->xp_pattern = arg; | |
1681 } | |
1682 xp->xp_context = compl; | |
1683 } | |
1684 break; | |
1685 | 1708 |
1686 case CMD_map: case CMD_noremap: | 1709 case CMD_map: case CMD_noremap: |
1687 case CMD_nmap: case CMD_nnoremap: | 1710 case CMD_nmap: case CMD_nnoremap: |
1688 case CMD_vmap: case CMD_vnoremap: | 1711 case CMD_vmap: case CMD_vnoremap: |
1689 case CMD_omap: case CMD_onoremap: | 1712 case CMD_omap: case CMD_onoremap: |
2311 | 2334 |
2312 return ret; | 2335 return ret; |
2313 } | 2336 } |
2314 | 2337 |
2315 /* | 2338 /* |
2316 * Do the expansion based on xp->xp_context and "pat". | 2339 * Map wild expand options to flags for expand_wildcards() |
2317 */ | 2340 */ |
2318 static int | 2341 static int |
2319 ExpandFromContext( | 2342 map_wildopts_to_ewflags(int options) |
2320 expand_T *xp, | 2343 { |
2321 char_u *pat, | |
2322 int *num_file, | |
2323 char_u ***file, | |
2324 int options) // WILD_ flags | |
2325 { | |
2326 regmatch_T regmatch; | |
2327 int ret; | |
2328 int flags; | 2344 int flags; |
2329 char_u *tofree = NULL; | |
2330 | 2345 |
2331 flags = EW_DIR; // include directories | 2346 flags = EW_DIR; // include directories |
2332 if (options & WILD_LIST_NOTFOUND) | 2347 if (options & WILD_LIST_NOTFOUND) |
2333 flags |= EW_NOTFOUND; | 2348 flags |= EW_NOTFOUND; |
2334 if (options & WILD_ADD_SLASH) | 2349 if (options & WILD_ADD_SLASH) |
2339 flags |= EW_SILENT; | 2354 flags |= EW_SILENT; |
2340 if (options & WILD_NOERROR) | 2355 if (options & WILD_NOERROR) |
2341 flags |= EW_NOERROR; | 2356 flags |= EW_NOERROR; |
2342 if (options & WILD_ALLLINKS) | 2357 if (options & WILD_ALLLINKS) |
2343 flags |= EW_ALLLINKS; | 2358 flags |= EW_ALLLINKS; |
2359 | |
2360 return flags; | |
2361 } | |
2362 | |
2363 /* | |
2364 * Do the expansion based on xp->xp_context and "pat". | |
2365 */ | |
2366 static int | |
2367 ExpandFromContext( | |
2368 expand_T *xp, | |
2369 char_u *pat, | |
2370 int *num_file, | |
2371 char_u ***file, | |
2372 int options) // WILD_ flags | |
2373 { | |
2374 regmatch_T regmatch; | |
2375 int ret; | |
2376 int flags; | |
2377 char_u *tofree = NULL; | |
2378 | |
2379 flags = map_wildopts_to_ewflags(options); | |
2344 | 2380 |
2345 if (xp->xp_context == EXPAND_FILES | 2381 if (xp->xp_context == EXPAND_FILES |
2346 || xp->xp_context == EXPAND_DIRECTORIES | 2382 || xp->xp_context == EXPAND_DIRECTORIES |
2347 || xp->xp_context == EXPAND_FILES_IN_PATH) | 2383 || xp->xp_context == EXPAND_FILES_IN_PATH) |
2348 return expand_files_and_dirs(xp, pat, file, num_file, flags, options); | 2384 return expand_files_and_dirs(xp, pat, file, num_file, flags, options); |
2548 #endif | 2584 #endif |
2549 return OK; | 2585 return OK; |
2550 } | 2586 } |
2551 | 2587 |
2552 /* | 2588 /* |
2589 * Expand shell command matches in one directory of $PATH. | |
2590 */ | |
2591 static void | |
2592 expand_shellcmd_onedir( | |
2593 char_u *buf, | |
2594 char_u *s, | |
2595 size_t l, | |
2596 char_u *pat, | |
2597 char_u ***files, | |
2598 int *num_files, | |
2599 int flags, | |
2600 hashtab_T *ht, | |
2601 garray_T *gap) | |
2602 { | |
2603 int ret; | |
2604 int i; | |
2605 hash_T hash; | |
2606 hashitem_T *hi; | |
2607 | |
2608 vim_strncpy(buf, s, l); | |
2609 add_pathsep(buf); | |
2610 l = STRLEN(buf); | |
2611 vim_strncpy(buf + l, pat, MAXPATHL - 1 - l); | |
2612 | |
2613 // Expand matches in one directory of $PATH. | |
2614 ret = expand_wildcards(1, &buf, num_files, files, flags); | |
2615 if (ret == OK) | |
2616 { | |
2617 if (ga_grow(gap, *num_files) == FAIL) | |
2618 FreeWild(*num_files, *files); | |
2619 else | |
2620 { | |
2621 for (i = 0; i < *num_files; ++i) | |
2622 { | |
2623 char_u *name = (*files)[i]; | |
2624 | |
2625 if (STRLEN(name) > l) | |
2626 { | |
2627 // Check if this name was already found. | |
2628 hash = hash_hash(name + l); | |
2629 hi = hash_lookup(ht, name + l, hash); | |
2630 if (HASHITEM_EMPTY(hi)) | |
2631 { | |
2632 // Remove the path that was prepended. | |
2633 STRMOVE(name, name + l); | |
2634 ((char_u **)gap->ga_data)[gap->ga_len++] = name; | |
2635 hash_add_item(ht, hi, name, hash); | |
2636 name = NULL; | |
2637 } | |
2638 } | |
2639 vim_free(name); | |
2640 } | |
2641 vim_free(*files); | |
2642 } | |
2643 } | |
2644 } | |
2645 | |
2646 /* | |
2553 * Complete a shell command. | 2647 * Complete a shell command. |
2554 * Returns FAIL or OK; | 2648 * Returns FAIL or OK; |
2555 */ | 2649 */ |
2556 static int | 2650 static int |
2557 expand_shellcmd( | 2651 expand_shellcmd( |
2567 garray_T ga; | 2661 garray_T ga; |
2568 char_u *buf; | 2662 char_u *buf; |
2569 size_t l; | 2663 size_t l; |
2570 char_u *s, *e; | 2664 char_u *s, *e; |
2571 int flags = flagsarg; | 2665 int flags = flagsarg; |
2572 int ret; | |
2573 int did_curdir = FALSE; | 2666 int did_curdir = FALSE; |
2574 hashtab_T found_ht; | 2667 hashtab_T found_ht; |
2575 hashitem_T *hi; | |
2576 hash_T hash; | |
2577 | 2668 |
2578 buf = alloc(MAXPATHL); | 2669 buf = alloc(MAXPATHL); |
2579 if (buf == NULL) | 2670 if (buf == NULL) |
2580 return FAIL; | 2671 return FAIL; |
2581 | 2672 |
2638 flags &= ~EW_DIR; | 2729 flags &= ~EW_DIR; |
2639 | 2730 |
2640 l = e - s; | 2731 l = e - s; |
2641 if (l > MAXPATHL - 5) | 2732 if (l > MAXPATHL - 5) |
2642 break; | 2733 break; |
2643 vim_strncpy(buf, s, l); | 2734 |
2644 add_pathsep(buf); | 2735 expand_shellcmd_onedir(buf, s, l, pat, file, num_file, flags, |
2645 l = STRLEN(buf); | 2736 &found_ht, &ga); |
2646 vim_strncpy(buf + l, pat, MAXPATHL - 1 - l); | 2737 |
2647 | |
2648 // Expand matches in one directory of $PATH. | |
2649 ret = expand_wildcards(1, &buf, num_file, file, flags); | |
2650 if (ret == OK) | |
2651 { | |
2652 if (ga_grow(&ga, *num_file) == FAIL) | |
2653 FreeWild(*num_file, *file); | |
2654 else | |
2655 { | |
2656 for (i = 0; i < *num_file; ++i) | |
2657 { | |
2658 char_u *name = (*file)[i]; | |
2659 | |
2660 if (STRLEN(name) > l) | |
2661 { | |
2662 // Check if this name was already found. | |
2663 hash = hash_hash(name + l); | |
2664 hi = hash_lookup(&found_ht, name + l, hash); | |
2665 if (HASHITEM_EMPTY(hi)) | |
2666 { | |
2667 // Remove the path that was prepended. | |
2668 STRMOVE(name, name + l); | |
2669 ((char_u **)ga.ga_data)[ga.ga_len++] = name; | |
2670 hash_add_item(&found_ht, hi, name, hash); | |
2671 name = NULL; | |
2672 } | |
2673 } | |
2674 vim_free(name); | |
2675 } | |
2676 vim_free(*file); | |
2677 } | |
2678 } | |
2679 if (*e != NUL) | 2738 if (*e != NUL) |
2680 ++e; | 2739 ++e; |
2681 } | 2740 } |
2682 *file = ga.ga_data; | 2741 *file = ga.ga_data; |
2683 *num_file = ga.ga_len; | 2742 *num_file = ga.ga_len; |
2922 default: break; | 2981 default: break; |
2923 } | 2982 } |
2924 } | 2983 } |
2925 #endif | 2984 #endif |
2926 | 2985 |
2927 if (did_wild_list && p_wmnu) | 2986 if (did_wild_list) |
2928 { | 2987 { |
2929 if (c == K_LEFT) | 2988 if (c == K_LEFT) |
2930 c = Ctrl_P; | 2989 c = Ctrl_P; |
2931 else if (c == K_RIGHT) | 2990 else if (c == K_RIGHT) |
2932 c = Ctrl_N; | 2991 c = Ctrl_N; |
2933 } | 2992 } |
2934 | 2993 |
2935 // Hitting CR after "emenu Name.": complete submenu | 2994 // Hitting CR after "emenu Name.": complete submenu |
2936 if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu | 2995 if (xp->xp_context == EXPAND_MENUNAMES |
2937 && cclp->cmdpos > 1 | 2996 && cclp->cmdpos > 1 |
2938 && cclp->cmdbuff[cclp->cmdpos - 1] == '.' | 2997 && cclp->cmdbuff[cclp->cmdpos - 1] == '.' |
2939 && cclp->cmdbuff[cclp->cmdpos - 2] != '\\' | 2998 && cclp->cmdbuff[cclp->cmdpos - 2] != '\\' |
2940 && (c == '\n' || c == '\r' || c == K_KENTER)) | 2999 && (c == '\n' || c == '\r' || c == K_KENTER)) |
2941 c = K_DOWN; | 3000 c = K_DOWN; |
2955 cclp->cmdlen -= cclp->cmdpos - from; | 3014 cclp->cmdlen -= cclp->cmdpos - from; |
2956 cclp->cmdpos = from; | 3015 cclp->cmdpos = from; |
2957 } | 3016 } |
2958 | 3017 |
2959 /* | 3018 /* |
2960 * Handle a key pressed when wild menu is displayed | 3019 * Handle a key pressed when the wild menu for the menu names |
2961 */ | 3020 * (EXPAND_MENUNAMES) is displayed. |
2962 int | 3021 */ |
2963 wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp) | 3022 static int |
2964 { | 3023 wildmenu_process_key_menunames(cmdline_info_T *cclp, int key, expand_T *xp) |
2965 int c = key; | 3024 { |
2966 int i; | 3025 int i; |
2967 int j; | 3026 int j; |
2968 | 3027 |
2969 if (!p_wmnu) | 3028 // Hitting <Down> after "emenu Name.": complete submenu |
2970 return c; | 3029 if (key == K_DOWN && cclp->cmdpos > 0 |
2971 | 3030 && cclp->cmdbuff[cclp->cmdpos - 1] == '.') |
2972 // Special translations for 'wildmenu' | 3031 { |
2973 if (xp->xp_context == EXPAND_MENUNAMES) | 3032 key = p_wc; |
2974 { | 3033 KeyTyped = TRUE; // in case the key was mapped |
2975 // Hitting <Down> after "emenu Name.": complete submenu | 3034 } |
2976 if (c == K_DOWN && cclp->cmdpos > 0 | 3035 else if (key == K_UP) |
2977 && cclp->cmdbuff[cclp->cmdpos - 1] == '.') | 3036 { |
2978 { | 3037 // Hitting <Up>: Remove one submenu name in front of the |
2979 c = p_wc; | 3038 // cursor |
2980 KeyTyped = TRUE; // in case the key was mapped | 3039 int found = FALSE; |
2981 } | 3040 |
2982 else if (c == K_UP) | 3041 j = (int)(xp->xp_pattern - cclp->cmdbuff); |
2983 { | 3042 i = 0; |
2984 // Hitting <Up>: Remove one submenu name in front of the | 3043 while (--j > 0) |
2985 // cursor | 3044 { |
2986 int found = FALSE; | 3045 // check for start of menu name |
2987 | 3046 if (cclp->cmdbuff[j] == ' ' |
2988 j = (int)(xp->xp_pattern - cclp->cmdbuff); | 3047 && cclp->cmdbuff[j - 1] != '\\') |
2989 i = 0; | 3048 { |
2990 while (--j > 0) | 3049 i = j + 1; |
2991 { | 3050 break; |
2992 // check for start of menu name | 3051 } |
2993 if (cclp->cmdbuff[j] == ' ' | 3052 // check for start of submenu name |
2994 && cclp->cmdbuff[j - 1] != '\\') | 3053 if (cclp->cmdbuff[j] == '.' |
3054 && cclp->cmdbuff[j - 1] != '\\') | |
3055 { | |
3056 if (found) | |
2995 { | 3057 { |
2996 i = j + 1; | 3058 i = j + 1; |
2997 break; | 3059 break; |
2998 } | 3060 } |
2999 // check for start of submenu name | 3061 else |
3000 if (cclp->cmdbuff[j] == '.' | 3062 found = TRUE; |
3001 && cclp->cmdbuff[j - 1] != '\\') | 3063 } |
3064 } | |
3065 if (i > 0) | |
3066 cmdline_del(cclp, i); | |
3067 key = p_wc; | |
3068 KeyTyped = TRUE; // in case the key was mapped | |
3069 xp->xp_context = EXPAND_NOTHING; | |
3070 } | |
3071 | |
3072 return key; | |
3073 } | |
3074 | |
3075 /* | |
3076 * Handle a key pressed when the wild menu for file names (EXPAND_FILES) or | |
3077 * directory names (EXPAND_DIRECTORIES) or shell command names | |
3078 * (EXPAND_SHELLCMD) is displayed. | |
3079 */ | |
3080 static int | |
3081 wildmenu_process_key_filenames(cmdline_info_T *cclp, int key, expand_T *xp) | |
3082 { | |
3083 int i; | |
3084 int j; | |
3085 char_u upseg[5]; | |
3086 | |
3087 upseg[0] = PATHSEP; | |
3088 upseg[1] = '.'; | |
3089 upseg[2] = '.'; | |
3090 upseg[3] = PATHSEP; | |
3091 upseg[4] = NUL; | |
3092 | |
3093 if (key == K_DOWN | |
3094 && cclp->cmdpos > 0 | |
3095 && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP | |
3096 && (cclp->cmdpos < 3 | |
3097 || cclp->cmdbuff[cclp->cmdpos - 2] != '.' | |
3098 || cclp->cmdbuff[cclp->cmdpos - 3] != '.')) | |
3099 { | |
3100 // go down a directory | |
3101 key = p_wc; | |
3102 KeyTyped = TRUE; // in case the key was mapped | |
3103 } | |
3104 else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && key == K_DOWN) | |
3105 { | |
3106 // If in a direct ancestor, strip off one ../ to go down | |
3107 int found = FALSE; | |
3108 | |
3109 j = cclp->cmdpos; | |
3110 i = (int)(xp->xp_pattern - cclp->cmdbuff); | |
3111 while (--j > i) | |
3112 { | |
3113 if (has_mbyte) | |
3114 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); | |
3115 if (vim_ispathsep(cclp->cmdbuff[j])) | |
3116 { | |
3117 found = TRUE; | |
3118 break; | |
3119 } | |
3120 } | |
3121 if (found | |
3122 && cclp->cmdbuff[j - 1] == '.' | |
3123 && cclp->cmdbuff[j - 2] == '.' | |
3124 && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) | |
3125 { | |
3126 cmdline_del(cclp, j - 2); | |
3127 key = p_wc; | |
3128 KeyTyped = TRUE; // in case the key was mapped | |
3129 } | |
3130 } | |
3131 else if (key == K_UP) | |
3132 { | |
3133 // go up a directory | |
3134 int found = FALSE; | |
3135 | |
3136 j = cclp->cmdpos - 1; | |
3137 i = (int)(xp->xp_pattern - cclp->cmdbuff); | |
3138 while (--j > i) | |
3139 { | |
3140 if (has_mbyte) | |
3141 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); | |
3142 if (vim_ispathsep(cclp->cmdbuff[j]) | |
3143 # ifdef BACKSLASH_IN_FILENAME | |
3144 && vim_strchr((char_u *)" *?[{`$%#", | |
3145 cclp->cmdbuff[j + 1]) == NULL | |
3146 # endif | |
3147 ) | |
3148 { | |
3149 if (found) | |
3002 { | 3150 { |
3003 if (found) | 3151 i = j + 1; |
3004 { | 3152 break; |
3005 i = j + 1; | |
3006 break; | |
3007 } | |
3008 else | |
3009 found = TRUE; | |
3010 } | 3153 } |
3011 } | 3154 else |
3012 if (i > 0) | 3155 found = TRUE; |
3013 cmdline_del(cclp, i); | 3156 } |
3014 c = p_wc; | 3157 } |
3015 KeyTyped = TRUE; // in case the key was mapped | 3158 |
3016 xp->xp_context = EXPAND_NOTHING; | 3159 if (!found) |
3017 } | 3160 j = i; |
3018 } | 3161 else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0) |
3019 if ((xp->xp_context == EXPAND_FILES | 3162 j += 4; |
3163 else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0 | |
3164 && j == i) | |
3165 j += 3; | |
3166 else | |
3167 j = 0; | |
3168 if (j > 0) | |
3169 { | |
3170 // TODO this is only for DOS/UNIX systems - need to put in | |
3171 // machine-specific stuff here and in upseg init | |
3172 cmdline_del(cclp, j); | |
3173 put_on_cmdline(upseg + 1, 3, FALSE); | |
3174 } | |
3175 else if (cclp->cmdpos > i) | |
3176 cmdline_del(cclp, i); | |
3177 | |
3178 // Now complete in the new directory. Set KeyTyped in case the | |
3179 // Up key came from a mapping. | |
3180 key = p_wc; | |
3181 KeyTyped = TRUE; | |
3182 } | |
3183 | |
3184 return key; | |
3185 } | |
3186 | |
3187 /* | |
3188 * Handle a key pressed when the wild menu is displayed | |
3189 */ | |
3190 int | |
3191 wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp) | |
3192 { | |
3193 if (xp->xp_context == EXPAND_MENUNAMES) | |
3194 return wildmenu_process_key_menunames(cclp, key, xp); | |
3195 else if ((xp->xp_context == EXPAND_FILES | |
3020 || xp->xp_context == EXPAND_DIRECTORIES | 3196 || xp->xp_context == EXPAND_DIRECTORIES |
3021 || xp->xp_context == EXPAND_SHELLCMD)) | 3197 || xp->xp_context == EXPAND_SHELLCMD)) |
3022 { | 3198 return wildmenu_process_key_filenames(cclp, key, xp); |
3023 char_u upseg[5]; | 3199 |
3024 | 3200 return key; |
3025 upseg[0] = PATHSEP; | |
3026 upseg[1] = '.'; | |
3027 upseg[2] = '.'; | |
3028 upseg[3] = PATHSEP; | |
3029 upseg[4] = NUL; | |
3030 | |
3031 if (c == K_DOWN | |
3032 && cclp->cmdpos > 0 | |
3033 && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP | |
3034 && (cclp->cmdpos < 3 | |
3035 || cclp->cmdbuff[cclp->cmdpos - 2] != '.' | |
3036 || cclp->cmdbuff[cclp->cmdpos - 3] != '.')) | |
3037 { | |
3038 // go down a directory | |
3039 c = p_wc; | |
3040 KeyTyped = TRUE; // in case the key was mapped | |
3041 } | |
3042 else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN) | |
3043 { | |
3044 // If in a direct ancestor, strip off one ../ to go down | |
3045 int found = FALSE; | |
3046 | |
3047 j = cclp->cmdpos; | |
3048 i = (int)(xp->xp_pattern - cclp->cmdbuff); | |
3049 while (--j > i) | |
3050 { | |
3051 if (has_mbyte) | |
3052 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); | |
3053 if (vim_ispathsep(cclp->cmdbuff[j])) | |
3054 { | |
3055 found = TRUE; | |
3056 break; | |
3057 } | |
3058 } | |
3059 if (found | |
3060 && cclp->cmdbuff[j - 1] == '.' | |
3061 && cclp->cmdbuff[j - 2] == '.' | |
3062 && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) | |
3063 { | |
3064 cmdline_del(cclp, j - 2); | |
3065 c = p_wc; | |
3066 KeyTyped = TRUE; // in case the key was mapped | |
3067 } | |
3068 } | |
3069 else if (c == K_UP) | |
3070 { | |
3071 // go up a directory | |
3072 int found = FALSE; | |
3073 | |
3074 j = cclp->cmdpos - 1; | |
3075 i = (int)(xp->xp_pattern - cclp->cmdbuff); | |
3076 while (--j > i) | |
3077 { | |
3078 if (has_mbyte) | |
3079 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); | |
3080 if (vim_ispathsep(cclp->cmdbuff[j]) | |
3081 # ifdef BACKSLASH_IN_FILENAME | |
3082 && vim_strchr((char_u *)" *?[{`$%#", | |
3083 cclp->cmdbuff[j + 1]) == NULL | |
3084 # endif | |
3085 ) | |
3086 { | |
3087 if (found) | |
3088 { | |
3089 i = j + 1; | |
3090 break; | |
3091 } | |
3092 else | |
3093 found = TRUE; | |
3094 } | |
3095 } | |
3096 | |
3097 if (!found) | |
3098 j = i; | |
3099 else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0) | |
3100 j += 4; | |
3101 else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0 | |
3102 && j == i) | |
3103 j += 3; | |
3104 else | |
3105 j = 0; | |
3106 if (j > 0) | |
3107 { | |
3108 // TODO this is only for DOS/UNIX systems - need to put in | |
3109 // machine-specific stuff here and in upseg init | |
3110 cmdline_del(cclp, j); | |
3111 put_on_cmdline(upseg + 1, 3, FALSE); | |
3112 } | |
3113 else if (cclp->cmdpos > i) | |
3114 cmdline_del(cclp, i); | |
3115 | |
3116 // Now complete in the new directory. Set KeyTyped in case the | |
3117 // Up key came from a mapping. | |
3118 c = p_wc; | |
3119 KeyTyped = TRUE; | |
3120 } | |
3121 } | |
3122 | |
3123 return c; | |
3124 } | 3201 } |
3125 | 3202 |
3126 /* | 3203 /* |
3127 * Free expanded names when finished walking through the matches | 3204 * Free expanded names when finished walking through the matches |
3128 */ | 3205 */ |