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 */