diff options
Diffstat (limited to 'src/nvim/cmdexpand.c')
-rw-r--r-- | src/nvim/cmdexpand.c | 233 |
1 files changed, 227 insertions, 6 deletions
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 28806094d6..dd5251e761 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -10,6 +10,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdhist.h" +#include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/funcs.h" #include "nvim/eval/userfunc.h" @@ -40,6 +41,7 @@ #include "nvim/ui.h" #include "nvim/usercmd.h" #include "nvim/vim.h" +#include "nvim/window.h" /// Type used by ExpandGeneric() typedef char *(*CompleteListItemGetter)(expand_T *, int); @@ -53,15 +55,13 @@ typedef void *(*user_expand_func_T)(const char_u *, int, typval_T *); static int cmd_showtail; ///< Only show path tail in lists ? -// TODO(zeertzjq): make these four variables static in cmdexpand.c - /// "compl_match_array" points the currently displayed list of entries in the /// popup menu. It is NULL when there is no popup menu. -pumitem_T *compl_match_array = NULL; -int compl_match_arraysize; +pumitem_T *compl_match_array = NULL; // TODO(zeertzjq): make this static in cmdexpand.c +static int compl_match_arraysize; /// First column in cmdline of the matched item for completion. -int compl_startcol; -int compl_selected; +static int compl_startcol; +static int compl_selected; static int sort_func_compare(const void *s1, const void *s2) { @@ -2509,6 +2509,227 @@ void globpath(char *path, char_u *file, garray_T *ga, int expand_options) xfree(buf); } +/// Translate some keys pressed when 'wildmenu' is used. +int wildmenu_translate_key(CmdlineInfo *cclp, int key, expand_T *xp, int did_wild_list) +{ + int c = key; + + if (did_wild_list && p_wmnu) { + if (c == K_LEFT) { + c = Ctrl_P; + } else if (c == K_RIGHT) { + c = Ctrl_N; + } + } + // Hitting CR after "emenu Name.": complete submenu + if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu + && cclp->cmdpos > 1 + && cclp->cmdbuff[cclp->cmdpos - 1] == '.' + && cclp->cmdbuff[cclp->cmdpos - 2] != '\\' + && (c == '\n' || c == '\r' || c == K_KENTER)) { + c = K_DOWN; + } + + return c; +} + +/// Delete characters on the command line, from "from" to the current position. +static void cmdline_del(CmdlineInfo *cclp, int from) +{ + assert(cclp->cmdpos <= cclp->cmdlen); + memmove(cclp->cmdbuff + from, cclp->cmdbuff + cclp->cmdpos, + (size_t)cclp->cmdlen - (size_t)cclp->cmdpos + 1); + cclp->cmdlen -= cclp->cmdpos - from; + cclp->cmdpos = from; +} + +/// Handle a key pressed when wild menu is displayed +int wildmenu_process_key(CmdlineInfo *cclp, int key, expand_T *xp) +{ + int c = key; + + if (!p_wmnu) { + return c; + } + + // Special translations for 'wildmenu' + if (xp->xp_context == EXPAND_MENUNAMES) { + // Hitting <Down> after "emenu Name.": complete submenu + if (c == K_DOWN && cclp->cmdpos > 0 + && cclp->cmdbuff[cclp->cmdpos - 1] == '.') { + c = (int)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; + + int j = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff); + int 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] != '\\') { + if (found) { + i = j + 1; + break; + } else { + found = true; + } + } + } + if (i > 0) { + cmdline_del(cclp, i); + } + c = (int)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; + + 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 = (int)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; + + int j = cclp->cmdpos; + int i = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff); + while (--j > i) { + j -= utf_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)) { + cmdline_del(cclp, j - 2); + c = (int)p_wc; + KeyTyped = true; // in case the key was mapped + } + } else if (c == K_UP) { + // go up a directory + int found = false; + + int j = cclp->cmdpos - 1; + int i = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff); + while (--j > i) { + j -= utf_head_off(cclp->cmdbuff, cclp->cmdbuff + j); + if (vim_ispathsep(cclp->cmdbuff[j]) +#ifdef BACKSLASH_IN_FILENAME + && vim_strchr((const char_u *)" *?[{`$%#", cclp->cmdbuff[j + 1]) + == NULL +#endif + ) { + if (found) { + 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(tarruda): 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 = (int)p_wc; + KeyTyped = true; + } + } + + return c; +} + +/// Free expanded names when finished walking through the matches +void wildmenu_cleanup(CmdlineInfo *cclp) +{ + if (!p_wmnu || wild_menu_showing == 0) { + return; + } + + const bool skt = KeyTyped; + int old_RedrawingDisabled = RedrawingDisabled; + + if (cclp->input_fn) { + RedrawingDisabled = 0; + } + + if (wild_menu_showing == WM_SCROLLED) { + // Entered command line, move it up + cmdline_row--; + redrawcmd(); + wild_menu_showing = 0; + } else if (save_p_ls != -1) { + // restore 'laststatus' and 'winminheight' + p_ls = save_p_ls; + p_wmh = save_p_wmh; + last_status(false); + update_screen(VALID); // redraw the screen NOW + redrawcmd(); + save_p_ls = -1; + wild_menu_showing = 0; + // don't redraw statusline if WM_LIST is showing + } else if (wild_menu_showing != WM_LIST) { + win_redraw_last_status(topframe); + wild_menu_showing = 0; // must be before redraw_statuslines #8385 + redraw_statuslines(); + } else { + wild_menu_showing = 0; + } + KeyTyped = skt; + if (cclp->input_fn) { + RedrawingDisabled = old_RedrawingDisabled; + } +} + /// "getcompletion()" function void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) { |