diff options
Diffstat (limited to 'src/cmdexpand.c')
-rw-r--r-- | src/cmdexpand.c | 727 |
1 files changed, 402 insertions, 325 deletions
diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 5cad1db2d7..9d012ad4b5 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -682,6 +682,98 @@ ExpandCleanup(expand_T *xp) } /* + * Display one line of completion matches. Multiple matches are displayed in + * each line (used by wildmode=list and CTRL-D) + * files_found - list of completion match names + * num_files - number of completion matches in "files_found" + * lines - number of output lines + * linenr - line number of matches to display + * maxlen - maximum number of characters in each line + * showtail - display only the tail of the full path of a file name + * dir_attr - highlight attribute to use for directory names + */ + static void +showmatches_oneline( + expand_T *xp, + char_u **files_found, + int num_files, + int lines, + int linenr, + int maxlen, + int showtail, + int dir_attr) +{ + int i, j; + int isdir; + int lastlen; + char_u *p; + + lastlen = 999; + for (j = linenr; j < num_files; j += lines) + { + if (xp->xp_context == EXPAND_TAGS_LISTFILES) + { + msg_outtrans_attr(files_found[j], HL_ATTR(HLF_D)); + p = files_found[j] + STRLEN(files_found[j]) + 1; + msg_advance(maxlen + 1); + msg_puts((char *)p); + msg_advance(maxlen + 3); + msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D)); + break; + } + for (i = maxlen - lastlen; --i >= 0; ) + msg_putchar(' '); + if (xp->xp_context == EXPAND_FILES + || xp->xp_context == EXPAND_SHELLCMD + || xp->xp_context == EXPAND_BUFFERS) + { + // highlight directories + if (xp->xp_numfiles != -1) + { + char_u *halved_slash; + char_u *exp_path; + char_u *path; + + // Expansion was done before and special characters + // were escaped, need to halve backslashes. Also + // $HOME has been replaced with ~/. + exp_path = expand_env_save_opt(files_found[j], TRUE); + path = exp_path != NULL ? exp_path : files_found[j]; + halved_slash = backslash_halve_save(path); + isdir = mch_isdir(halved_slash != NULL ? halved_slash + : files_found[j]); + vim_free(exp_path); + if (halved_slash != path) + vim_free(halved_slash); + } + else + // Expansion was done here, file names are literal. + isdir = mch_isdir(files_found[j]); + if (showtail) + p = SHOW_FILE_TEXT(j); + else + { + home_replace(NULL, files_found[j], NameBuff, MAXPATHL, + TRUE); + p = NameBuff; + } + } + else + { + isdir = FALSE; + p = SHOW_FILE_TEXT(j); + } + lastlen = msg_outtrans_attr(p, isdir ? dir_attr : 0); + } + if (msg_col > 0) // when not wrapped around + { + msg_clr_eos(); + msg_putchar('\n'); + } + out_flush(); // show one line at a time +} + +/* * Show all matches for completion on the command line. * Returns EXPAND_NOTHING when the character that triggered expansion should * be inserted like a normal character. @@ -692,12 +784,10 @@ showmatches(expand_T *xp, int wildmenu UNUSED) cmdline_info_T *ccline = get_cmdline_info(); int num_files; char_u **files_found; - int i, j, k; + int i, j; int maxlen; int lines; int columns; - char_u *p; - int lastlen; int attr; int showtail; @@ -709,7 +799,6 @@ showmatches(expand_T *xp, int wildmenu UNUSED) showtail = expand_showtail(xp); if (i != EXPAND_OK) return i; - } else { @@ -789,69 +878,8 @@ showmatches(expand_T *xp, int wildmenu UNUSED) // list the files line by line for (i = 0; i < lines; ++i) { - lastlen = 999; - for (k = i; k < num_files; k += lines) - { - if (xp->xp_context == EXPAND_TAGS_LISTFILES) - { - msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D)); - p = files_found[k] + STRLEN(files_found[k]) + 1; - msg_advance(maxlen + 1); - msg_puts((char *)p); - msg_advance(maxlen + 3); - msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D)); - break; - } - for (j = maxlen - lastlen; --j >= 0; ) - msg_putchar(' '); - if (xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_SHELLCMD - || xp->xp_context == EXPAND_BUFFERS) - { - // highlight directories - if (xp->xp_numfiles != -1) - { - char_u *halved_slash; - char_u *exp_path; - char_u *path; - - // Expansion was done before and special characters - // were escaped, need to halve backslashes. Also - // $HOME has been replaced with ~/. - exp_path = expand_env_save_opt(files_found[k], TRUE); - path = exp_path != NULL ? exp_path : files_found[k]; - halved_slash = backslash_halve_save(path); - j = mch_isdir(halved_slash != NULL ? halved_slash - : files_found[k]); - vim_free(exp_path); - if (halved_slash != path) - vim_free(halved_slash); - } - else - // Expansion was done here, file names are literal. - j = mch_isdir(files_found[k]); - if (showtail) - p = SHOW_FILE_TEXT(k); - else - { - home_replace(NULL, files_found[k], NameBuff, MAXPATHL, - TRUE); - p = NameBuff; - } - } - else - { - j = FALSE; - p = SHOW_FILE_TEXT(k); - } - lastlen = msg_outtrans_attr(p, j ? attr : 0); - } - if (msg_col > 0) // when not wrapped around - { - msg_clr_eos(); - msg_putchar('\n'); - } - out_flush(); // show one line at a time + showmatches_oneline(xp, files_found, num_files, lines, i, + maxlen, showtail, attr); if (got_int) { got_int = FALSE; @@ -1334,6 +1362,73 @@ set_context_for_wildcard_arg( } /* + * Returns a pointer to the next command after a :substitute or a :& command. + * Returns NULL if there is no next command. + */ + static char_u * +find_cmd_after_substitute_cmd(char_u *arg) +{ + int delim; + + delim = *arg; + if (delim) + { + // skip "from" part + ++arg; + arg = skip_regexp(arg, delim, magic_isset()); + + if (arg[0] != NUL && arg[0] == delim) + { + // skip "to" part + ++arg; + while (arg[0] != NUL && arg[0] != delim) + { + if (arg[0] == '\\' && arg[1] != NUL) + ++arg; + ++arg; + } + if (arg[0] != NUL) // skip delimiter + ++arg; + } + } + while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) + ++arg; + if (arg[0] != NUL) + return arg; + + return NULL; +} + +/* + * Returns a pointer to the next command after a :isearch/:dsearch/:ilist + * :dlist/:ijump/:psearch/:djump/:isplit/:dsplit command. + * Returns NULL if there is no next command. + */ + static char_u * +find_cmd_after_isearch_cmd(char_u *arg, expand_T *xp) +{ + arg = skipwhite(skipdigits(arg)); // skip count + if (*arg == '/') // Match regexp, not just whole words + { + for (++arg; *arg && *arg != '/'; arg++) + if (*arg == '\\' && arg[1] != NUL) + arg++; + if (*arg) + { + arg = skipwhite(arg + 1); + + // Check for trailing illegal characters + if (*arg == NUL || vim_strchr((char_u *)"|\"\n", *arg) == NULL) + xp->xp_context = EXPAND_NOTHING; + else + return arg; + } + } + + return NULL; +} + +/* * Set the completion context in 'xp' for command 'cmd' with index 'cmdidx'. * The argument to the command is 'arg' and the argument flags is 'argt'. * For user-defined commands and for environment variables, 'compl' has the @@ -1467,32 +1562,7 @@ set_context_by_cmdname( break; case CMD_and: case CMD_substitute: - delim = *arg; - if (delim) - { - // skip "from" part - ++arg; - arg = skip_regexp(arg, delim, magic_isset()); - - if (arg[0] != NUL && arg[0] == delim) - { - // skip "to" part - ++arg; - while (arg[0] != NUL && arg[0] != delim) - { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; - } - if (arg[0] != NUL) // skip delimiter - ++arg; - } - } - while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) - ++arg; - if (arg[0] != NUL) - return arg; - break; + return find_cmd_after_substitute_cmd(arg); case CMD_isearch: case CMD_dsearch: case CMD_ilist: @@ -1502,26 +1572,7 @@ set_context_by_cmdname( case CMD_djump: case CMD_isplit: case CMD_dsplit: - arg = skipwhite(skipdigits(arg)); // skip count - if (*arg == '/') // Match regexp, not just whole words - { - for (++arg; *arg && *arg != '/'; arg++) - if (*arg == '\\' && arg[1] != NUL) - arg++; - if (*arg) - { - arg = skipwhite(arg + 1); - - // Check for trailing illegal characters - if (*arg == NUL || - vim_strchr((char_u *)"|\"\n", *arg) == NULL) - xp->xp_context = EXPAND_NOTHING; - else - return arg; - } - } - break; - + return find_cmd_after_isearch_cmd(arg, xp); case CMD_autocmd: return set_context_in_autocmd(xp, arg, FALSE); case CMD_doautocmd: @@ -1652,36 +1703,8 @@ set_context_by_cmdname( #endif case CMD_USER: case CMD_USER_BUF: - if (compl != EXPAND_NOTHING) - { - // EX_XFILE: file names are handled above - if (!(argt & EX_XFILE)) - { -#ifdef FEAT_MENU - if (compl == EXPAND_MENUS) - return set_context_in_menu_cmd(xp, cmd, arg, forceit); -#endif - if (compl == EXPAND_COMMANDS) - return arg; - if (compl == EXPAND_MAPPINGS) - return set_context_in_map_cmd(xp, (char_u *)"map", - arg, forceit, FALSE, FALSE, CMD_map); - // Find start of last argument. - p = arg; - while (*p) - { - if (*p == ' ') - // argument starts after a space - arg = p + 1; - else if (*p == '\\' && *(p + 1) != NUL) - ++p; // skip over escaped character - MB_PTR_ADV(p); - } - xp->xp_pattern = arg; - } - xp->xp_context = compl; - } - break; + return set_context_in_user_cmdarg(cmd, arg, argt, compl, xp, + forceit); case CMD_map: case CMD_noremap: case CMD_nmap: case CMD_nnoremap: @@ -2313,20 +2336,12 @@ ExpandOther( } /* - * Do the expansion based on xp->xp_context and "pat". + * Map wild expand options to flags for expand_wildcards() */ static int -ExpandFromContext( - expand_T *xp, - char_u *pat, - int *num_file, - char_u ***file, - int options) // WILD_ flags +map_wildopts_to_ewflags(int options) { - regmatch_T regmatch; - int ret; int flags; - char_u *tofree = NULL; flags = EW_DIR; // include directories if (options & WILD_LIST_NOTFOUND) @@ -2342,6 +2357,27 @@ ExpandFromContext( if (options & WILD_ALLLINKS) flags |= EW_ALLLINKS; + return flags; +} + +/* + * Do the expansion based on xp->xp_context and "pat". + */ + static int +ExpandFromContext( + expand_T *xp, + char_u *pat, + int *num_file, + char_u ***file, + int options) // WILD_ flags +{ + regmatch_T regmatch; + int ret; + int flags; + char_u *tofree = NULL; + + flags = map_wildopts_to_ewflags(options); + if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES || xp->xp_context == EXPAND_FILES_IN_PATH) @@ -2550,6 +2586,64 @@ ExpandGeneric( } /* + * Expand shell command matches in one directory of $PATH. + */ + static void +expand_shellcmd_onedir( + char_u *buf, + char_u *s, + size_t l, + char_u *pat, + char_u ***files, + int *num_files, + int flags, + hashtab_T *ht, + garray_T *gap) +{ + int ret; + int i; + hash_T hash; + hashitem_T *hi; + + vim_strncpy(buf, s, l); + add_pathsep(buf); + l = STRLEN(buf); + vim_strncpy(buf + l, pat, MAXPATHL - 1 - l); + + // Expand matches in one directory of $PATH. + ret = expand_wildcards(1, &buf, num_files, files, flags); + if (ret == OK) + { + if (ga_grow(gap, *num_files) == FAIL) + FreeWild(*num_files, *files); + else + { + for (i = 0; i < *num_files; ++i) + { + char_u *name = (*files)[i]; + + if (STRLEN(name) > l) + { + // Check if this name was already found. + hash = hash_hash(name + l); + hi = hash_lookup(ht, name + l, hash); + if (HASHITEM_EMPTY(hi)) + { + // Remove the path that was prepended. + STRMOVE(name, name + l); + ((char_u **)gap->ga_data)[gap->ga_len++] = name; + hash_add_item(ht, hi, name, hash); + name = NULL; + } + } + vim_free(name); + } + vim_free(*files); + } + } +} + +/* * Complete a shell command. * Returns FAIL or OK; */ @@ -2569,11 +2663,8 @@ expand_shellcmd( size_t l; char_u *s, *e; int flags = flagsarg; - int ret; int did_curdir = FALSE; hashtab_T found_ht; - hashitem_T *hi; - hash_T hash; buf = alloc(MAXPATHL); if (buf == NULL) @@ -2640,42 +2731,10 @@ expand_shellcmd( l = e - s; if (l > MAXPATHL - 5) break; - vim_strncpy(buf, s, l); - add_pathsep(buf); - l = STRLEN(buf); - vim_strncpy(buf + l, pat, MAXPATHL - 1 - l); - - // Expand matches in one directory of $PATH. - ret = expand_wildcards(1, &buf, num_file, file, flags); - if (ret == OK) - { - if (ga_grow(&ga, *num_file) == FAIL) - FreeWild(*num_file, *file); - else - { - for (i = 0; i < *num_file; ++i) - { - char_u *name = (*file)[i]; - if (STRLEN(name) > l) - { - // Check if this name was already found. - hash = hash_hash(name + l); - hi = hash_lookup(&found_ht, name + l, hash); - if (HASHITEM_EMPTY(hi)) - { - // Remove the path that was prepended. - STRMOVE(name, name + l); - ((char_u **)ga.ga_data)[ga.ga_len++] = name; - hash_add_item(&found_ht, hi, name, hash); - name = NULL; - } - } - vim_free(name); - } - vim_free(*file); - } - } + expand_shellcmd_onedir(buf, s, l, pat, file, num_file, flags, + &found_ht, &ga); + if (*e != NUL) ++e; } @@ -2924,7 +2983,7 @@ wildmenu_translate_key( } #endif - if (did_wild_list && p_wmnu) + if (did_wild_list) { if (c == K_LEFT) c = Ctrl_P; @@ -2933,7 +2992,7 @@ wildmenu_translate_key( } // Hitting CR after "emenu Name.": complete submenu - if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu + if (xp->xp_context == EXPAND_MENUNAMES && cclp->cmdpos > 1 && cclp->cmdbuff[cclp->cmdpos - 1] == '.' && cclp->cmdbuff[cclp->cmdpos - 2] != '\\' @@ -2957,170 +3016,188 @@ cmdline_del(cmdline_info_T *cclp, int from) } /* - * Handle a key pressed when wild menu is displayed + * Handle a key pressed when the wild menu for the menu names + * (EXPAND_MENUNAMES) is displayed. */ - int -wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp) + static int +wildmenu_process_key_menunames(cmdline_info_T *cclp, int key, expand_T *xp) { - int c = key; int i; int j; - if (!p_wmnu) - return c; - - // Special translations for 'wildmenu' - if (xp->xp_context == EXPAND_MENUNAMES) + // Hitting <Down> after "emenu Name.": complete submenu + if (key == K_DOWN && cclp->cmdpos > 0 + && cclp->cmdbuff[cclp->cmdpos - 1] == '.') { - // Hitting <Down> after "emenu Name.": complete submenu - if (c == K_DOWN && cclp->cmdpos > 0 - && cclp->cmdbuff[cclp->cmdpos - 1] == '.') - { - c = p_wc; - KeyTyped = TRUE; // in case the key was mapped - } - else if (c == K_UP) - { - // Hitting <Up>: Remove one submenu name in front of the - // cursor - int found = FALSE; + key = p_wc; + KeyTyped = TRUE; // in case the key was mapped + } + else if (key == K_UP) + { + // Hitting <Up>: Remove one submenu name in front of the + // cursor + int found = FALSE; - j = (int)(xp->xp_pattern - cclp->cmdbuff); - i = 0; - while (--j > 0) + j = (int)(xp->xp_pattern - cclp->cmdbuff); + i = 0; + while (--j > 0) + { + // check for start of menu name + if (cclp->cmdbuff[j] == ' ' + && cclp->cmdbuff[j - 1] != '\\') + { + i = j + 1; + break; + } + // check for start of submenu name + if (cclp->cmdbuff[j] == '.' + && cclp->cmdbuff[j - 1] != '\\') { - // check for start of menu name - if (cclp->cmdbuff[j] == ' ' - && cclp->cmdbuff[j - 1] != '\\') + if (found) { i = j + 1; break; } - // check for start of submenu name - if (cclp->cmdbuff[j] == '.' - && cclp->cmdbuff[j - 1] != '\\') - { - if (found) - { - i = j + 1; - break; - } - else - found = TRUE; - } + else + found = TRUE; } - if (i > 0) - cmdline_del(cclp, i); - c = p_wc; - KeyTyped = TRUE; // in case the key was mapped - xp->xp_context = EXPAND_NOTHING; } + if (i > 0) + cmdline_del(cclp, i); + key = p_wc; + KeyTyped = TRUE; // in case the key was mapped + xp->xp_context = EXPAND_NOTHING; } - if ((xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_DIRECTORIES - || xp->xp_context == EXPAND_SHELLCMD)) - { - char_u upseg[5]; - upseg[0] = PATHSEP; - upseg[1] = '.'; - upseg[2] = '.'; - upseg[3] = PATHSEP; - upseg[4] = NUL; + return key; +} - if (c == K_DOWN - && cclp->cmdpos > 0 - && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP - && (cclp->cmdpos < 3 - || cclp->cmdbuff[cclp->cmdpos - 2] != '.' - || cclp->cmdbuff[cclp->cmdpos - 3] != '.')) - { - // go down a directory - c = p_wc; - KeyTyped = TRUE; // in case the key was mapped - } - else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN) - { - // If in a direct ancestor, strip off one ../ to go down - int found = FALSE; +/* + * Handle a key pressed when the wild menu for file names (EXPAND_FILES) or + * directory names (EXPAND_DIRECTORIES) or shell command names + * (EXPAND_SHELLCMD) is displayed. + */ + static int +wildmenu_process_key_filenames(cmdline_info_T *cclp, int key, expand_T *xp) +{ + int i; + int j; + char_u upseg[5]; - j = cclp->cmdpos; - i = (int)(xp->xp_pattern - cclp->cmdbuff); - while (--j > i) - { - if (has_mbyte) - j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); - if (vim_ispathsep(cclp->cmdbuff[j])) - { - found = TRUE; - break; - } - } - if (found - && cclp->cmdbuff[j - 1] == '.' - && cclp->cmdbuff[j - 2] == '.' - && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) + upseg[0] = PATHSEP; + upseg[1] = '.'; + upseg[2] = '.'; + upseg[3] = PATHSEP; + upseg[4] = NUL; + + if (key == K_DOWN + && cclp->cmdpos > 0 + && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP + && (cclp->cmdpos < 3 + || cclp->cmdbuff[cclp->cmdpos - 2] != '.' + || cclp->cmdbuff[cclp->cmdpos - 3] != '.')) + { + // go down a directory + key = p_wc; + KeyTyped = TRUE; // in case the key was mapped + } + else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && key == K_DOWN) + { + // If in a direct ancestor, strip off one ../ to go down + int found = FALSE; + + j = cclp->cmdpos; + i = (int)(xp->xp_pattern - cclp->cmdbuff); + while (--j > i) + { + if (has_mbyte) + j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); + if (vim_ispathsep(cclp->cmdbuff[j])) { - cmdline_del(cclp, j - 2); - c = p_wc; - KeyTyped = TRUE; // in case the key was mapped + found = TRUE; + break; } } - else if (c == K_UP) + if (found + && cclp->cmdbuff[j - 1] == '.' + && cclp->cmdbuff[j - 2] == '.' + && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) { - // go up a directory - int found = FALSE; + cmdline_del(cclp, j - 2); + key = p_wc; + KeyTyped = TRUE; // in case the key was mapped + } + } + else if (key == K_UP) + { + // go up a directory + int found = FALSE; - j = cclp->cmdpos - 1; - i = (int)(xp->xp_pattern - cclp->cmdbuff); - while (--j > i) - { - if (has_mbyte) - j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); - if (vim_ispathsep(cclp->cmdbuff[j]) + j = cclp->cmdpos - 1; + i = (int)(xp->xp_pattern - cclp->cmdbuff); + while (--j > i) + { + if (has_mbyte) + j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j); + if (vim_ispathsep(cclp->cmdbuff[j]) # ifdef BACKSLASH_IN_FILENAME - && vim_strchr((char_u *)" *?[{`$%#", - cclp->cmdbuff[j + 1]) == NULL + && vim_strchr((char_u *)" *?[{`$%#", + cclp->cmdbuff[j + 1]) == NULL # endif - ) + ) + { + if (found) { - if (found) - { - i = j + 1; - break; - } - else - found = TRUE; + i = j + 1; + break; } + else + found = TRUE; } + } - if (!found) - j = i; - else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0) - j += 4; - else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0 - && j == i) - j += 3; - else - j = 0; - if (j > 0) - { - // TODO this is only for DOS/UNIX systems - need to put in - // machine-specific stuff here and in upseg init - cmdline_del(cclp, j); - put_on_cmdline(upseg + 1, 3, FALSE); - } - else if (cclp->cmdpos > i) - cmdline_del(cclp, i); - - // Now complete in the new directory. Set KeyTyped in case the - // Up key came from a mapping. - c = p_wc; - KeyTyped = TRUE; + if (!found) + j = i; + else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0) + j += 4; + else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0 + && j == i) + j += 3; + else + j = 0; + if (j > 0) + { + // TODO this is only for DOS/UNIX systems - need to put in + // machine-specific stuff here and in upseg init + cmdline_del(cclp, j); + put_on_cmdline(upseg + 1, 3, FALSE); } + else if (cclp->cmdpos > i) + cmdline_del(cclp, i); + + // Now complete in the new directory. Set KeyTyped in case the + // Up key came from a mapping. + key = p_wc; + KeyTyped = TRUE; } - return c; + return key; +} + +/* + * Handle a key pressed when the wild menu is displayed + */ + int +wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp) +{ + if (xp->xp_context == EXPAND_MENUNAMES) + return wildmenu_process_key_menunames(cclp, key, xp); + else if ((xp->xp_context == EXPAND_FILES + || xp->xp_context == EXPAND_DIRECTORIES + || xp->xp_context == EXPAND_SHELLCMD)) + return wildmenu_process_key_filenames(cclp, key, xp); + + return key; } /* |