aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/cmdexpand.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/cmdexpand.c')
-rw-r--r--src/nvim/cmdexpand.c1776
1 files changed, 1125 insertions, 651 deletions
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 5c54404aab..ca19d6de95 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -3,9 +3,20 @@
// cmdexpand.c: functions for command-line completion
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auto/config.h"
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/arglist.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
@@ -13,28 +24,42 @@
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/grid.h"
+#include "nvim/hashtab.h"
#include "nvim/help.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
-#include "nvim/if_cscope.h"
+#include "nvim/keycodes.h"
#include "nvim/locale.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
#include "nvim/mapping.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
#include "nvim/menu.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
+#include "nvim/path.h"
#include "nvim/popupmenu.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/sign.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
@@ -48,7 +73,7 @@
typedef char *(*CompleteListItemGetter)(expand_T *, int);
/// Type used by call_user_expand_func
-typedef void *(*user_expand_func_T)(const char_u *, int, typval_T *);
+typedef void *(*user_expand_func_T)(const char *, int, typval_T *);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "cmdexpand.c.generated.h"
@@ -64,10 +89,14 @@ static int compl_match_arraysize;
static int compl_startcol;
static int compl_selected;
+#define SHOW_MATCH(m) (showtail ? showmatches_gettail(matches[m], false) : matches[m])
+
+/// Sort function for the completion matches.
+/// <SNR> functions should be sorted to the end.
static int sort_func_compare(const void *s1, const void *s2)
{
- char_u *p1 = *(char_u **)s1;
- char_u *p2 = *(char_u **)s2;
+ char *p1 = *(char **)s1;
+ char *p2 = *(char **)s2;
if (*p1 != '<' && *p2 == '<') {
return -1;
@@ -75,71 +104,76 @@ static int sort_func_compare(const void *s1, const void *s2)
if (*p1 == '<' && *p2 != '<') {
return 1;
}
- return STRCMP(p1, p2);
+ return strcmp(p1, p2);
}
-static void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char **files, int options)
+/// Escape special characters in the cmdline completion matches.
+static void wildescape(expand_T *xp, const char *str, int numfiles, char **files)
{
- int i;
- char_u *p;
+ char *p;
const int vse_what = xp->xp_context == EXPAND_BUFFERS ? VSE_BUFFER : VSE_NONE;
- // May change home directory back to "~"
- if (options & WILD_HOME_REPLACE) {
- tilde_replace(str, numfiles, files);
- }
-
- if (options & WILD_ESCAPE) {
- if (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_FILES_IN_PATH
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS
- || xp->xp_context == EXPAND_DIRECTORIES) {
- // Insert a backslash into a file name before a space, \, %, #
- // and wildmatch characters, except '~'.
- for (i = 0; i < numfiles; i++) {
- // for ":set path=" we need to escape spaces twice
- if (xp->xp_backslash == XP_BS_THREE) {
- p = vim_strsave_escaped((char_u *)files[i], (char_u *)" ");
- xfree(files[i]);
- files[i] = (char *)p;
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_FILES_IN_PATH
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS
+ || xp->xp_context == EXPAND_DIRECTORIES) {
+ // Insert a backslash into a file name before a space, \, %, #
+ // and wildmatch characters, except '~'.
+ for (int i = 0; i < numfiles; i++) {
+ // for ":set path=" we need to escape spaces twice
+ if (xp->xp_backslash == XP_BS_THREE) {
+ p = vim_strsave_escaped(files[i], " ");
+ xfree(files[i]);
+ files[i] = p;
#if defined(BACKSLASH_IN_FILENAME)
- p = vim_strsave_escaped(files[i], (char_u *)" ");
- xfree(files[i]);
- files[i] = p;
+ p = vim_strsave_escaped(files[i], " ");
+ xfree(files[i]);
+ files[i] = p;
#endif
- }
+ }
#ifdef BACKSLASH_IN_FILENAME
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i], vse_what);
+ p = vim_strsave_fnameescape(files[i], vse_what);
#else
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i],
- xp->xp_shell ? VSE_SHELL : vse_what);
+ p = vim_strsave_fnameescape(files[i], xp->xp_shell ? VSE_SHELL : vse_what);
#endif
- xfree(files[i]);
- files[i] = (char *)p;
+ xfree(files[i]);
+ files[i] = p;
- // If 'str' starts with "\~", replace "~" at start of
- // files[i] with "\~".
- if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~') {
- escape_fname(&files[i]);
- }
+ // If 'str' starts with "\~", replace "~" at start of
+ // files[i] with "\~".
+ if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~') {
+ escape_fname(&files[i]);
}
- xp->xp_backslash = XP_BS_NONE;
+ }
+ xp->xp_backslash = XP_BS_NONE;
- // If the first file starts with a '+' escape it. Otherwise it
- // could be seen as "+cmd".
- if (*files[0] == '+') {
- escape_fname(&files[0]);
- }
- } else if (xp->xp_context == EXPAND_TAGS) {
- // Insert a backslash before characters in a tag name that
- // would terminate the ":tag" command.
- for (i = 0; i < numfiles; i++) {
- p = vim_strsave_escaped((char_u *)files[i], (char_u *)"\\|\"");
- xfree(files[i]);
- files[i] = (char *)p;
- }
+ // If the first file starts with a '+' escape it. Otherwise it
+ // could be seen as "+cmd".
+ if (*files[0] == '+') {
+ escape_fname(&files[0]);
}
+ } else if (xp->xp_context == EXPAND_TAGS) {
+ // Insert a backslash before characters in a tag name that
+ // would terminate the ":tag" command.
+ for (int i = 0; i < numfiles; i++) {
+ p = vim_strsave_escaped(files[i], "\\|\"");
+ xfree(files[i]);
+ files[i] = p;
+ }
+ }
+}
+
+/// Escape special characters in the cmdline completion matches.
+static void ExpandEscape(expand_T *xp, char *str, int numfiles, char **files, int options)
+{
+ // May change home directory back to "~"
+ if (options & WILD_HOME_REPLACE) {
+ tilde_replace(str, numfiles, files);
+ }
+
+ if (options & WILD_ESCAPE) {
+ wildescape(xp, str, numfiles, files);
}
}
@@ -154,8 +188,8 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
{
CmdlineInfo *const ccline = get_cmdline_info();
int i, j;
- char_u *p1;
- char_u *p2;
+ char *p1;
+ char *p2;
int difflen;
if (xp->xp_numfiles == -1) {
@@ -172,34 +206,38 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
return FAIL;
}
- if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
+ // If cmd_silent is set then don't show the dots, because redrawcmd() below
+ // won't remove them.
+ if (!cmd_silent && !(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
msg_puts("..."); // show that we are busy
ui_flush();
}
- i = (int)((char_u *)xp->xp_pattern - ccline->cmdbuff);
+ i = (int)(xp->xp_pattern - ccline->cmdbuff);
assert(ccline->cmdpos >= i);
xp->xp_pattern_len = (size_t)ccline->cmdpos - (size_t)i;
- if (type == WILD_NEXT || type == WILD_PREV) {
+ if (type == WILD_NEXT || type == WILD_PREV
+ || type == WILD_PAGEUP || type == WILD_PAGEDOWN
+ || type == WILD_PUM_WANT) {
// Get next/previous match for a previous expanded pattern.
p2 = ExpandOne(xp, NULL, NULL, 0, type);
} else {
// Translate string into pattern and expand it.
- p1 = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
const int use_options = (options
| WILD_HOME_REPLACE
| WILD_ADD_SLASH
| WILD_SILENT
| (escape ? WILD_ESCAPE : 0)
| (p_wic ? WILD_ICASE : 0));
- p2 = ExpandOne(xp, p1, vim_strnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
+ p2 = ExpandOne(xp, p1, xstrnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
use_options, type);
xfree(p1);
// xp->xp_pattern might have been modified by ExpandOne (for example,
// in lua completion), so recompute the pattern index and length
- i = (int)((char_u *)xp->xp_pattern - ccline->cmdbuff);
+ i = (int)(xp->xp_pattern - ccline->cmdbuff);
xp->xp_pattern_len = (size_t)ccline->cmdpos - (size_t)i;
// Longest match: make sure it is not shorter, happens with :help.
@@ -210,23 +248,23 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
break;
}
}
- if ((int)STRLEN(p2) < j) {
+ if ((int)strlen(p2) < j) {
XFREE_CLEAR(p2);
}
}
}
if (p2 != NULL && !got_int) {
- difflen = (int)STRLEN(p2) - (int)(xp->xp_pattern_len);
+ difflen = (int)strlen(p2) - (int)(xp->xp_pattern_len);
if (ccline->cmdlen + difflen + 4 > ccline->cmdbufflen) {
realloc_cmdbuff(ccline->cmdlen + difflen + 4);
- xp->xp_pattern = (char *)ccline->cmdbuff + i;
+ xp->xp_pattern = ccline->cmdbuff + i;
}
assert(ccline->cmdpos <= ccline->cmdlen);
memmove(&ccline->cmdbuff[ccline->cmdpos + difflen],
&ccline->cmdbuff[ccline->cmdpos],
(size_t)ccline->cmdlen - (size_t)ccline->cmdpos + 1);
- memmove(&ccline->cmdbuff[i], p2, STRLEN(p2));
+ memmove(&ccline->cmdbuff[i], p2, strlen(p2));
ccline->cmdlen += difflen;
ccline->cmdpos += difflen;
}
@@ -251,19 +289,54 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
return OK;
}
+/// Create and display a cmdline completion popup menu with items from
+/// "matches".
+static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **matches, int numMatches,
+ int showtail)
+{
+ assert(numMatches >= 0);
+ // Add all the completion matches
+ compl_match_arraysize = numMatches;
+ compl_match_array = xmalloc(sizeof(pumitem_T) * (size_t)compl_match_arraysize);
+ for (int i = 0; i < numMatches; i++) {
+ compl_match_array[i] = (pumitem_T){
+ .pum_text = SHOW_MATCH(i),
+ .pum_info = NULL,
+ .pum_extra = NULL,
+ .pum_kind = NULL,
+ };
+ }
+
+ // Compute the popup menu starting column
+ char *endpos = showtail ? showmatches_gettail(xp->xp_pattern, true) : xp->xp_pattern;
+ if (ui_has(kUICmdline)) {
+ compl_startcol = (int)(endpos - ccline->cmdbuff);
+ } else {
+ compl_startcol = cmd_screencol((int)(endpos - ccline->cmdbuff));
+ }
+
+ // no default selection
+ compl_selected = -1;
+
+ cmdline_pum_display(true);
+
+ return EXPAND_OK;
+}
+
void cmdline_pum_display(bool changed_array)
{
pum_display(compl_match_array, compl_match_arraysize, compl_selected,
changed_array, compl_startcol);
}
+/// Returns true if the cmdline completion popup menu is being displayed.
bool cmdline_pum_active(void)
{
// compl_match_array != NULL should already imply pum_visible() in Nvim.
return compl_match_array != NULL;
}
-/// Remove the cmdline completion popup menu
+/// Remove the cmdline completion popup menu (if present), free the list of items.
void cmdline_pum_remove(void)
{
pum_undisplay(true);
@@ -276,9 +349,238 @@ void cmdline_pum_cleanup(CmdlineInfo *cclp)
wildmenu_cleanup(cclp);
}
+/// Return the number of characters that should be skipped in the wildmenu
+/// These are backslashes used for escaping. Do show backslashes in help tags.
+static int skip_wildmenu_char(expand_T *xp, char *s)
+{
+ if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP)
+ || ((xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES)
+ && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL)))) {
+#ifndef BACKSLASH_IN_FILENAME
+ // TODO(bfredl): Why in the actual fuck are we special casing the
+ // shell variety deep in the redraw logic? Shell special snowflakiness
+ // should already be eliminated multiple layers before reaching the
+ // screen infracstructure.
+ if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
+ return 2;
+ }
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+/// Get the length of an item as it will be shown in the status line.
+static int wildmenu_match_len(expand_T *xp, char *s)
+{
+ int len = 0;
+
+ int emenu = (xp->xp_context == EXPAND_MENUS
+ || xp->xp_context == EXPAND_MENUNAMES);
+
+ // Check for menu separators - replace with '|'.
+ if (emenu && menu_is_separator(s)) {
+ return 1;
+ }
+
+ while (*s != NUL) {
+ s += skip_wildmenu_char(xp, s);
+ len += ptr2cells(s);
+ MB_PTR_ADV(s);
+ }
+
+ return len;
+}
+
+/// Show wildchar matches in the status line.
+/// Show at least the "match" item.
+/// We start at item "first_match" in the list and show all matches that fit.
+///
+/// If inversion is possible we use it. Else '=' characters are used.
+///
+/// @param matches list of matches
+static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
+{
+ int row;
+ char *buf;
+ int len;
+ int clen; // length in screen cells
+ int fillchar;
+ int attr;
+ int i;
+ bool highlight = true;
+ char *selstart = NULL;
+ int selstart_col = 0;
+ char *selend = NULL;
+ static int first_match = 0;
+ bool add_left = false;
+ char *s;
+ int emenu;
+ int l;
+
+ if (matches == NULL) { // interrupted completion?
+ return;
+ }
+
+ buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1);
+
+ if (match == -1) { // don't show match but original text
+ match = 0;
+ highlight = false;
+ }
+ // count 1 for the ending ">"
+ clen = wildmenu_match_len(xp, SHOW_MATCH(match)) + 3;
+ if (match == 0) {
+ first_match = 0;
+ } else if (match < first_match) {
+ // jumping left, as far as we can go
+ first_match = match;
+ add_left = true;
+ } else {
+ // check if match fits on the screen
+ for (i = first_match; i < match; i++) {
+ clen += wildmenu_match_len(xp, SHOW_MATCH(i)) + 2;
+ }
+ if (first_match > 0) {
+ clen += 2;
+ }
+ // jumping right, put match at the left
+ if ((long)clen > Columns) {
+ first_match = match;
+ // if showing the last match, we can add some on the left
+ clen = 2;
+ for (i = match; i < num_matches; i++) {
+ clen += wildmenu_match_len(xp, SHOW_MATCH(i)) + 2;
+ if ((long)clen >= Columns) {
+ break;
+ }
+ }
+ if (i == num_matches) {
+ add_left = true;
+ }
+ }
+ }
+ if (add_left) {
+ while (first_match > 0) {
+ clen += wildmenu_match_len(xp, SHOW_MATCH(first_match - 1)) + 2;
+ if ((long)clen >= Columns) {
+ break;
+ }
+ first_match--;
+ }
+ }
+
+ fillchar = fillchar_status(&attr, curwin);
+
+ if (first_match == 0) {
+ *buf = NUL;
+ len = 0;
+ } else {
+ STRCPY(buf, "< ");
+ len = 2;
+ }
+ clen = len;
+
+ i = first_match;
+ while (clen + wildmenu_match_len(xp, SHOW_MATCH(i)) + 2 < Columns) {
+ if (i == match) {
+ selstart = buf + len;
+ selstart_col = clen;
+ }
+
+ s = SHOW_MATCH(i);
+ // Check for menu separators - replace with '|'
+ emenu = (xp->xp_context == EXPAND_MENUS
+ || xp->xp_context == EXPAND_MENUNAMES);
+ if (emenu && menu_is_separator(s)) {
+ STRCPY(buf + len, transchar('|'));
+ l = (int)strlen(buf + len);
+ len += l;
+ clen += l;
+ } else {
+ for (; *s != NUL; s++) {
+ s += skip_wildmenu_char(xp, s);
+ clen += ptr2cells(s);
+ if ((l = utfc_ptr2len(s)) > 1) {
+ strncpy(buf + len, s, (size_t)l); // NOLINT(runtime/printf)
+ s += l - 1;
+ len += l;
+ } else {
+ STRCPY(buf + len, transchar_byte((uint8_t)(*s)));
+ len += (int)strlen(buf + len);
+ }
+ }
+ }
+ if (i == match) {
+ selend = buf + len;
+ }
+
+ *(buf + len++) = ' ';
+ *(buf + len++) = ' ';
+ clen += 2;
+ if (++i == num_matches) {
+ break;
+ }
+ }
+
+ if (i != num_matches) {
+ *(buf + len++) = '>';
+ clen++;
+ }
+
+ buf[len] = NUL;
+
+ row = cmdline_row - 1;
+ if (row >= 0) {
+ if (wild_menu_showing == 0 || wild_menu_showing == WM_LIST) {
+ if (msg_scrolled > 0) {
+ // Put the wildmenu just above the command line. If there is
+ // no room, scroll the screen one line up.
+ if (cmdline_row == Rows - 1) {
+ msg_scroll_up(false, false);
+ msg_scrolled++;
+ } else {
+ cmdline_row++;
+ row++;
+ }
+ wild_menu_showing = WM_SCROLLED;
+ } else {
+ // Create status line if needed by setting 'laststatus' to 2.
+ // Set 'winminheight' to zero to avoid that the window is
+ // resized.
+ if (lastwin->w_status_height == 0 && global_stl_height() == 0) {
+ save_p_ls = (int)p_ls;
+ save_p_wmh = (int)p_wmh;
+ p_ls = 2;
+ p_wmh = 0;
+ last_status(false);
+ }
+ wild_menu_showing = WM_SHOWN;
+ }
+ }
+
+ // Tricky: wildmenu can be drawn either over a status line, or at empty
+ // scrolled space in the message output
+ ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
+ ? &msg_grid_adj : &default_grid;
+
+ grid_puts(grid, buf, row, 0, attr);
+ if (selstart != NULL && highlight) {
+ *selend = NUL;
+ grid_puts(grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
+ }
+
+ grid_fill(grid, row, row + 1, clen, Columns,
+ fillchar, fillchar, attr);
+ }
+
+ win_redraw_last_status(topframe);
+ xfree(buf);
+}
+
/// Get the next or prev cmdline completion match. The index of the match is set
/// in "p_findex"
-static char_u *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char_u *orig_save)
+static char *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char *orig_save)
{
if (xp->xp_numfiles <= 0) {
return NULL;
@@ -291,8 +593,49 @@ static char_u *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, cha
findex = xp->xp_numfiles;
}
findex--;
- } else { // mode == WILD_NEXT
+ } else if (mode == WILD_NEXT) {
findex++;
+ } else if (mode == WILD_PAGEUP) {
+ if (findex == 0) {
+ // at the first entry, don't select any entries
+ findex = -1;
+ } else if (findex == -1) {
+ // no entry is selected. select the last entry
+ findex = xp->xp_numfiles - 1;
+ } else {
+ // go up by the pum height
+ int ht = pum_get_height();
+ if (ht > 3) {
+ ht -= 2;
+ }
+ findex -= ht;
+ if (findex < 0) {
+ // few entries left, select the first entry
+ findex = 0;
+ }
+ }
+ } else if (mode == WILD_PAGEDOWN) {
+ if (findex == xp->xp_numfiles - 1) {
+ // at the last entry, don't select any entries
+ findex = -1;
+ } else if (findex == -1) {
+ // no entry is selected. select the first entry
+ findex = 0;
+ } else {
+ // go down by the pum height
+ int ht = pum_get_height();
+ if (ht > 3) {
+ ht -= 2;
+ }
+ findex += ht;
+ if (findex >= xp->xp_numfiles) {
+ // few entries left, select the last entry
+ findex = xp->xp_numfiles - 1;
+ }
+ }
+ } else { // mode == WILD_PUM_WANT
+ assert(pum_want.active);
+ findex = pum_want.item;
}
// When wrapping around, return the original string, set findex to -1.
@@ -318,17 +661,17 @@ static char_u *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, cha
}
*p_findex = findex;
- return vim_strsave(findex == -1 ? orig_save : (char_u *)xp->xp_files[findex]);
+ return xstrdup(findex == -1 ? orig_save : xp->xp_files[findex]);
}
/// Start the command-line expansion and get the matches.
-static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
+static char *ExpandOne_start(int mode, expand_T *xp, char *str, int options)
{
int non_suf_match; // number without matching suffix
- char_u *ss = NULL;
+ char *ss = NULL;
// Do the expansion.
- if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) {
+ if (ExpandFromContext(xp, str, &xp->xp_files, &xp->xp_numfiles, options) == FAIL) {
#ifdef FNAME_ILLEGAL
// Illegal file name has been silently skipped. But when there
// are wildcards, the real problem is that there was no match,
@@ -346,8 +689,7 @@ static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
// Check for matching suffixes in file names.
- if (mode != WILD_ALL && mode != WILD_ALL_KEEP
- && mode != WILD_LONGEST) {
+ if (mode != WILD_ALL && mode != WILD_ALL_KEEP && mode != WILD_LONGEST) {
if (xp->xp_numfiles) {
non_suf_match = xp->xp_numfiles;
} else {
@@ -361,7 +703,7 @@ static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
// expand_wildcards, only need to check the first two.
non_suf_match = 0;
for (int i = 0; i < 2; i++) {
- if (match_suffix((char_u *)xp->xp_files[i])) {
+ if (match_suffix(xp->xp_files[i])) {
non_suf_match++;
}
}
@@ -378,7 +720,7 @@ static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
}
}
if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) {
- ss = vim_strsave((char_u *)xp->xp_files[0]);
+ ss = xstrdup(xp->xp_files[0]);
}
}
}
@@ -420,7 +762,7 @@ static char *find_longest_match(expand_T *xp, int options)
return xstrndup(xp->xp_files[0], len);
}
-/// Do wildcard expansion on the string 'str'.
+/// Do wildcard expansion on the string "str".
/// Chars that should not be expanded must be preceded with a backslash.
/// Return a pointer to allocated memory containing the new string.
/// Return NULL for failure.
@@ -444,6 +786,7 @@ static char *find_longest_match(expand_T *xp, int options)
/// popup menu and close the menu.
/// mode = WILD_CANCEL: cancel and close the cmdline completion popup and
/// use the original text.
+/// mode = WILD_PUM_WANT: use the match at index pum_want.item
///
/// options = WILD_LIST_NOTFOUND: list entries without a match
/// options = WILD_HOME_REPLACE: do home_replace() for buffer names
@@ -458,24 +801,27 @@ static char *find_longest_match(expand_T *xp, int options)
/// The variables xp->xp_context and xp->xp_backslash must have been set!
///
/// @param orig allocated copy of original of expanded string
-char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode)
+char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode)
{
- char_u *ss = NULL;
+ char *ss = NULL;
static int findex;
- static char_u *orig_save = NULL; // kept value of orig
+ static char *orig_save = NULL; // kept value of orig
int orig_saved = false;
int i;
// first handle the case of using an old match
- if (mode == WILD_NEXT || mode == WILD_PREV) {
+ if (mode == WILD_NEXT || mode == WILD_PREV
+ || mode == WILD_PAGEUP || mode == WILD_PAGEDOWN
+ || mode == WILD_PUM_WANT) {
return get_next_or_prev_match(mode, xp, &findex, orig_save);
}
if (mode == WILD_CANCEL) {
- ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
+ ss = xstrdup(orig_save ? orig_save : "");
} else if (mode == WILD_APPLY) {
- ss = vim_strsave(findex == -1 ? (orig_save ? orig_save : (char_u *)"")
- : (char_u *)xp->xp_files[findex]);
+ ss = xstrdup(findex == -1
+ ? (orig_save ? orig_save : "")
+ : xp->xp_files[findex]);
}
// free old names
@@ -483,6 +829,11 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
FreeWild(xp->xp_numfiles, xp->xp_files);
xp->xp_numfiles = -1;
XFREE_CLEAR(orig_save);
+
+ // The entries from xp_files may be used in the PUM, remove it.
+ if (compl_match_array != NULL) {
+ cmdline_pum_remove();
+ }
}
findex = 0;
@@ -500,7 +851,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
// Find longest common part
if (mode == WILD_LONGEST && xp->xp_numfiles > 0) {
- ss = (char_u *)find_longest_match(xp, options);
+ ss = find_longest_match(xp, options);
findex = -1; // next p_wc gets first one
}
@@ -510,7 +861,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
if (mode == WILD_ALL && xp->xp_numfiles > 0 && !got_int) {
size_t len = 0;
for (i = 0; i < xp->xp_numfiles; i++) {
- len += STRLEN(xp->xp_files[i]) + 1;
+ len += strlen(xp->xp_files[i]) + 1;
}
ss = xmalloc(len);
*ss = NUL;
@@ -552,36 +903,99 @@ void ExpandCleanup(expand_T *xp)
}
}
+/// Display one line of completion matches. Multiple matches are displayed in
+/// each line (used by wildmode=list and CTRL-D)
+///
+/// @param matches list of completion match names
+/// @param numMatches number of completion matches in "matches"
+/// @param lines number of output lines
+/// @param linenr line number of matches to display
+/// @param maxlen maximum number of characters in each line
+/// @param showtail display only the tail of the full path of a file name
+/// @param dir_attr highlight attribute to use for directory names
+static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, int lines, int linenr,
+ int maxlen, int showtail, int dir_attr)
+{
+ char *p;
+ int lastlen = 999;
+ for (int j = linenr; j < numMatches; j += lines) {
+ if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
+ msg_outtrans_attr(matches[j], HL_ATTR(HLF_D));
+ p = matches[j] + strlen(matches[j]) + 1;
+ msg_advance(maxlen + 1);
+ msg_puts((const char *)p);
+ msg_advance(maxlen + 3);
+ msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
+ break;
+ }
+ for (int i = maxlen - lastlen; --i >= 0;) {
+ msg_putchar(' ');
+ }
+ bool isdir;
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS) {
+ // highlight directories
+ if (xp->xp_numfiles != -1) {
+ // Expansion was done before and special characters
+ // were escaped, need to halve backslashes. Also
+ // $HOME has been replaced with ~/.
+ char *exp_path = (char *)expand_env_save_opt(matches[j], true);
+ char *path = exp_path != NULL ? exp_path : matches[j];
+ char *halved_slash = backslash_halve_save(path);
+ isdir = os_isdir(halved_slash);
+ xfree(exp_path);
+ if (halved_slash != path) {
+ xfree(halved_slash);
+ }
+ } else {
+ // Expansion was done here, file names are literal.
+ isdir = os_isdir(matches[j]);
+ }
+ if (showtail) {
+ p = SHOW_MATCH(j);
+ } else {
+ home_replace(NULL, matches[j], NameBuff, MAXPATHL, true);
+ p = NameBuff;
+ }
+ } else {
+ isdir = false;
+ p = SHOW_MATCH(j);
+ }
+ lastlen = msg_outtrans_attr(p, isdir ? dir_attr : 0);
+ }
+ if (msg_col > 0) { // when not wrapped around
+ msg_clr_eos();
+ msg_putchar('\n');
+ }
+}
+
/// 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.
int showmatches(expand_T *xp, int wildmenu)
{
CmdlineInfo *const ccline = get_cmdline_info();
-#define L_SHOWFILE(m) (showtail \
- ? sm_gettail(files_found[m], false) : files_found[m])
- int num_files;
- char **files_found;
- int i, j, k;
+ int numMatches;
+ char **matches;
+ int i, j;
int maxlen;
int lines;
int columns;
- char_u *p;
- int lastlen;
int attr;
int showtail;
if (xp->xp_numfiles == -1) {
set_expand_context(xp);
i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
- &num_files, &files_found);
+ &numMatches, &matches);
showtail = expand_showtail(xp);
if (i != EXPAND_OK) {
return i;
}
} else {
- num_files = xp->xp_numfiles;
- files_found = xp->xp_files;
+ numMatches = xp->xp_numfiles;
+ matches = xp->xp_files;
showtail = cmd_showtail;
}
@@ -591,26 +1005,8 @@ int showmatches(expand_T *xp, int wildmenu)
|| ui_has(kUIWildmenu);
if (compl_use_pum) {
- assert(num_files >= 0);
- compl_match_arraysize = num_files;
- compl_match_array = xmalloc(sizeof(pumitem_T) * (size_t)compl_match_arraysize);
- for (i = 0; i < num_files; i++) {
- compl_match_array[i] = (pumitem_T){
- .pum_text = (char_u *)L_SHOWFILE(i),
- .pum_info = NULL,
- .pum_extra = NULL,
- .pum_kind = NULL,
- };
- }
- char_u *endpos = (char_u *)(showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
- if (ui_has(kUICmdline)) {
- compl_startcol = (int)(endpos - ccline->cmdbuff);
- } else {
- compl_startcol = cmd_screencol((int)(endpos - ccline->cmdbuff));
- }
- compl_selected = -1;
- cmdline_pum_display(true);
- return EXPAND_OK;
+ // cmdline completion popup menu (with wildoptions=pum)
+ return cmdline_pum_create(ccline, xp, matches, numMatches, showtail);
}
if (!wildmenu) {
@@ -626,18 +1022,18 @@ int showmatches(expand_T *xp, int wildmenu)
if (got_int) {
got_int = false; // only int. the completion, not the cmd line
} else if (wildmenu) {
- redraw_wildmenu(xp, num_files, files_found, -1, showtail);
+ redraw_wildmenu(xp, numMatches, matches, -1, showtail);
} else {
// find the length of the longest file name
maxlen = 0;
- for (i = 0; i < num_files; i++) {
+ for (i = 0; i < numMatches; i++) {
if (!showtail && (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_SHELLCMD
|| xp->xp_context == EXPAND_BUFFERS)) {
- home_replace(NULL, files_found[i], (char *)NameBuff, MAXPATHL, true);
- j = vim_strsize((char *)NameBuff);
+ home_replace(NULL, matches[i], NameBuff, MAXPATHL, true);
+ j = vim_strsize(NameBuff);
} else {
- j = vim_strsize(L_SHOWFILE(i));
+ j = vim_strsize(SHOW_MATCH(i));
}
if (j > maxlen) {
maxlen = j;
@@ -645,7 +1041,7 @@ int showmatches(expand_T *xp, int wildmenu)
}
if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- lines = num_files;
+ lines = numMatches;
} else {
// compute the number of columns and lines for the listing
maxlen += 2; // two spaces between file names
@@ -653,7 +1049,7 @@ int showmatches(expand_T *xp, int wildmenu)
if (columns < 1) {
columns = 1;
}
- lines = (num_files + columns - 1) / columns;
+ lines = (numMatches + columns - 1) / columns;
}
attr = HL_ATTR(HLF_D); // find out highlighting for directories
@@ -667,57 +1063,7 @@ int showmatches(expand_T *xp, int wildmenu)
// 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 = (char_u *)files_found[k] + STRLEN(files_found[k]) + 1;
- msg_advance(maxlen + 1);
- msg_puts((const 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) {
- // Expansion was done before and special characters
- // were escaped, need to halve backslashes. Also
- // $HOME has been replaced with ~/.
- char_u *exp_path = expand_env_save_opt((char_u *)files_found[k], true);
- char_u *path = exp_path != NULL ? exp_path : (char_u *)files_found[k];
- char_u *halved_slash = (char_u *)backslash_halve_save((char *)path);
- j = os_isdir((char *)halved_slash);
- xfree(exp_path);
- if (halved_slash != path) {
- xfree(halved_slash);
- }
- } else {
- // Expansion was done here, file names are literal.
- j = os_isdir(files_found[k]);
- }
- if (showtail) {
- p = (char_u *)L_SHOWFILE(k);
- } else {
- home_replace(NULL, files_found[k], (char *)NameBuff, MAXPATHL, true);
- p = (char_u *)NameBuff;
- }
- } else {
- j = false;
- p = (char_u *)L_SHOWFILE(k);
- }
- lastlen = msg_outtrans_attr((char *)p, j ? attr : 0);
- }
- if (msg_col > 0) { // when not wrapped around
- msg_clr_eos();
- msg_putchar('\n');
- }
- ui_flush(); // show one line at a time
+ showmatches_oneline(xp, matches, numMatches, lines, i, maxlen, showtail, attr);
if (got_int) {
got_int = false;
break;
@@ -730,21 +1076,21 @@ int showmatches(expand_T *xp, int wildmenu)
}
if (xp->xp_numfiles == -1) {
- FreeWild(num_files, files_found);
+ FreeWild(numMatches, matches);
}
return EXPAND_OK;
}
-/// Private path_tail for showmatches() (and redraw_wildmenu()):
-/// Find tail of file name path, but ignore trailing "/".
-char *sm_gettail(char *s, bool eager)
+/// path_tail() version for showmatches() and redraw_wildmenu():
+/// Return the tail of file name path "s", ignoring a trailing "/".
+static char *showmatches_gettail(char *s, bool eager)
{
- char_u *p;
- char_u *t = (char_u *)s;
+ char *p;
+ char *t = s;
bool had_sep = false;
- for (p = (char_u *)s; *p != NUL;) {
+ for (p = s; *p != NUL;) {
if (vim_ispathsep(*p)
#ifdef BACKSLASH_IN_FILENAME
&& !rem_backslash(p)
@@ -761,7 +1107,7 @@ char *sm_gettail(char *s, bool eager)
}
MB_PTR_ADV(p);
}
- return (char *)t;
+ return t;
}
/// Return true if we only need to show the tail of completion matches.
@@ -769,8 +1115,8 @@ char *sm_gettail(char *s, bool eager)
/// returned.
static bool expand_showtail(expand_T *xp)
{
- char_u *s;
- char_u *end;
+ char *s;
+ char *end;
// When not completing file names a "/" may mean something different.
if (xp->xp_context != EXPAND_FILES
@@ -779,15 +1125,15 @@ static bool expand_showtail(expand_T *xp)
return false;
}
- end = (char_u *)path_tail(xp->xp_pattern);
- if (end == (char_u *)xp->xp_pattern) { // there is no path separator
+ end = path_tail(xp->xp_pattern);
+ if (end == xp->xp_pattern) { // there is no path separator
return false;
}
- for (s = (char_u *)xp->xp_pattern; s < end; s++) {
+ for (s = xp->xp_pattern; s < end; s++) {
// Skip escaped wildcards. Only when the backslash is not a path
// separator, on DOS the '*' "path\*\file" must not be skipped.
- if (rem_backslash((char *)s)) {
+ if (rem_backslash(s)) {
s++;
} else if (vim_strchr("*?[", *s) != NULL) {
return false;
@@ -804,13 +1150,13 @@ static bool expand_showtail(expand_T *xp)
/// the name into allocated memory and prepend "^".
///
/// @param context EXPAND_FILES etc.
-char_u *addstar(char_u *fname, size_t len, int context)
+char *addstar(char *fname, size_t len, int context)
FUNC_ATTR_NONNULL_RET
{
- char_u *retval;
+ char *retval;
size_t i, j;
size_t new_len;
- char_u *tail;
+ char *tail;
int ends_in_star;
if (context != EXPAND_FILES
@@ -832,7 +1178,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
|| context == EXPAND_PACKADD
|| ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS)
&& fname[0] == '/')) {
- retval = vim_strnsave(fname, len);
+ retval = xstrnsave(fname, len);
} else {
new_len = len + 2; // +2 for '^' at start, NUL at end
for (i = 0; i < len; i++) {
@@ -894,7 +1240,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
}
} else {
retval = xmalloc(len + 4);
- STRLCPY(retval, fname, len + 1);
+ xstrlcpy(retval, fname, len + 1);
// Don't add a star to *, ~, ~user, $var or `cmd`.
// * would become **, which walks the whole tree.
@@ -902,7 +1248,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
// $ could be anywhere in the tail.
// ` could be anywhere in the file name.
// When the name ends in '$' don't add a star, remove the '$'.
- tail = (char_u *)path_tail((char *)retval);
+ tail = path_tail(retval);
ends_in_star = (len > 0 && retval[len - 1] == '*');
#ifndef BACKSLASH_IN_FILENAME
for (ssize_t k = (ssize_t)len - 2; k >= 0; k--) {
@@ -914,8 +1260,8 @@ char_u *addstar(char_u *fname, size_t len, int context)
#endif
if ((*retval != '~' || tail != retval)
&& !ends_in_star
- && vim_strchr((char *)tail, '$') == NULL
- && vim_strchr((char *)retval, '`') == NULL) {
+ && vim_strchr(tail, '$') == NULL
+ && vim_strchr(retval, '`') == NULL) {
retval[len++] = '*';
} else if (len > 0 && retval[len - 1] == '$') {
len--;
@@ -1148,24 +1494,235 @@ static void set_context_for_wildcard_arg(exarg_T *eap, const char *arg, bool use
// A full match ~user<Tab> will be replaced by user's home
// directory i.e. something like ~user<Tab> -> /home/user/
if (*p == NUL && p > (const char *)xp->xp_pattern + 1
- && match_user((char_u *)xp->xp_pattern + 1) >= 1) {
+ && match_user(xp->xp_pattern + 1) >= 1) {
xp->xp_context = EXPAND_USER;
xp->xp_pattern++;
}
}
}
+/// Set the completion context for the :filter command. Returns a pointer to the
+/// next command after the :filter command.
+static const char *set_context_in_filter_cmd(expand_T *xp, const char *arg)
+{
+ if (*arg != NUL) {
+ arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL);
+ }
+ if (arg == NULL || *arg == NUL) {
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
+ }
+ return (const char *)skipwhite(arg);
+}
+
+/// Set the completion context for the :match command. Returns a pointer to the
+/// next command after the :match command.
+static const char *set_context_in_match_cmd(expand_T *xp, const char *arg)
+{
+ if (*arg == NUL || !ends_excmd(*arg)) {
+ // also complete "None"
+ set_context_in_echohl_cmd(xp, arg);
+ arg = (const char *)skipwhite(skiptowhite(arg));
+ if (*arg != NUL) {
+ xp->xp_context = EXPAND_NOTHING;
+ arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), magic_isset());
+ }
+ }
+ return (const char *)find_nextcmd(arg);
+}
+
+/// Returns a pointer to the next command after a :global or a :v command.
+/// Returns NULL if there is no next command.
+static const char *find_cmd_after_global_cmd(const char *arg)
+{
+ const int delim = (uint8_t)(*arg); // Get the delimiter.
+ if (delim) {
+ arg++; // Skip delimiter if there is one.
+ }
+
+ while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
+ if (arg[0] == '\\' && arg[1] != NUL) {
+ arg++;
+ }
+ arg++;
+ }
+ if (arg[0] != NUL) {
+ return arg + 1;
+ }
+
+ return NULL;
+}
+
+/// Returns a pointer to the next command after a :substitute or a :& command.
+/// Returns NULL if there is no next command.
+static const char *find_cmd_after_substitute_cmd(const char *arg)
+{
+ const int delim = (uint8_t)(*arg);
+ if (delim) {
+ // Skip "from" part.
+ arg++;
+ arg = (const char *)skip_regexp((char *)arg, delim, magic_isset());
+
+ if (arg[0] != NUL && arg[0] == delim) {
+ // Skip "to" part.
+ arg++;
+ while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
+ if (arg[0] == '\\' && arg[1] != NUL) {
+ arg++;
+ }
+ arg++;
+ }
+ if (arg[0] != NUL) { // Skip delimiter.
+ arg++;
+ }
+ }
+ }
+ while (arg[0] && strchr("|\"#", 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 const char *find_cmd_after_isearch_cmd(expand_T *xp, const char *arg)
+{
+ // Skip count.
+ arg = (const char *)skipwhite(skipdigits(arg));
+ if (*arg != '/') {
+ return NULL;
+ }
+
+ // Match regexp, not just whole words.
+ for (++arg; *arg && *arg != '/'; arg++) {
+ if (*arg == '\\' && arg[1] != NUL) {
+ arg++;
+ }
+ }
+ if (*arg) {
+ arg = (const char *)skipwhite(arg + 1);
+
+ // Check for trailing illegal characters.
+ if (*arg == NUL || strchr("|\"\n", *arg) == NULL) {
+ xp->xp_context = EXPAND_NOTHING;
+ } else {
+ return arg;
+ }
+ }
+
+ return NULL;
+}
+
+/// Set the completion context for the :unlet command. Always returns NULL.
+static const char *set_context_in_unlet_cmd(expand_T *xp, const char *arg)
+{
+ while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
+ arg = (const char *)xp->xp_pattern + 1;
+ }
+
+ xp->xp_context = EXPAND_USER_VARS;
+ xp->xp_pattern = (char *)arg;
+
+ if (*xp->xp_pattern == '$') {
+ xp->xp_context = EXPAND_ENV_VARS;
+ xp->xp_pattern++;
+ }
+
+ return NULL;
+}
+
+/// Set the completion context for the :language command. Always returns NULL.
+static const char *set_context_in_lang_cmd(expand_T *xp, const char *arg)
+{
+ const char *p = (const char *)skiptowhite(arg);
+ if (*p == NUL) {
+ xp->xp_context = EXPAND_LANGUAGE;
+ xp->xp_pattern = (char *)arg;
+ } else {
+ if (strncmp(arg, "messages", (size_t)(p - arg)) == 0
+ || strncmp(arg, "ctype", (size_t)(p - arg)) == 0
+ || strncmp(arg, "time", (size_t)(p - arg)) == 0
+ || strncmp(arg, "collate", (size_t)(p - arg)) == 0) {
+ xp->xp_context = EXPAND_LOCALES;
+ xp->xp_pattern = skipwhite(p);
+ } else {
+ xp->xp_context = EXPAND_NOTHING;
+ }
+ }
+
+ return NULL;
+}
+
+static enum {
+ EXP_BREAKPT_ADD, ///< expand ":breakadd" sub-commands
+ EXP_BREAKPT_DEL, ///< expand ":breakdel" sub-commands
+ EXP_PROFDEL, ///< expand ":profdel" sub-commands
+} breakpt_expand_what;
+
+/// Set the completion context for the :breakadd command. Always returns NULL.
+static const char *set_context_in_breakadd_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx)
+{
+ xp->xp_context = EXPAND_BREAKPOINT;
+ xp->xp_pattern = (char *)arg;
+
+ if (cmdidx == CMD_breakadd) {
+ breakpt_expand_what = EXP_BREAKPT_ADD;
+ } else if (cmdidx == CMD_breakdel) {
+ breakpt_expand_what = EXP_BREAKPT_DEL;
+ } else {
+ breakpt_expand_what = EXP_PROFDEL;
+ }
+
+ const char *p = skipwhite(arg);
+ if (*p == NUL) {
+ return NULL;
+ }
+ const char *subcmd_start = p;
+
+ if (strncmp("file ", p, 5) == 0 || strncmp("func ", p, 5) == 0) {
+ // :breakadd file [lnum] <filename>
+ // :breakadd func [lnum] <funcname>
+ p += 4;
+ p = skipwhite(p);
+
+ // skip line number (if specified)
+ if (ascii_isdigit(*p)) {
+ p = skipdigits(p);
+ if (*p != ' ') {
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
+ }
+ p = skipwhite(p);
+ }
+ if (strncmp("file", subcmd_start, 4) == 0) {
+ xp->xp_context = EXPAND_FILES;
+ } else {
+ xp->xp_context = EXPAND_USER_FUNC;
+ }
+ xp->xp_pattern = (char *)p;
+ } else if (strncmp("expr ", p, 5) == 0) {
+ // :breakadd expr <expression>
+ xp->xp_context = EXPAND_EXPRESSION;
+ xp->xp_pattern = skipwhite(p + 5);
+ }
+
+ 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, "context" has the
/// completion type.
///
/// @return a pointer to the next command, or NULL if there is no next command.
-static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, const char *arg,
- uint32_t argt, int context, expand_T *xp, bool forceit)
+static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expand_T *xp,
+ const char *arg, uint32_t argt, int context, bool forceit)
{
- const char *p;
-
switch (cmdidx) {
case CMD_find:
case CMD_sfind:
@@ -1204,6 +1761,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_folddoclosed:
case CMD_folddoopen:
case CMD_hide:
+ case CMD_horizontal:
case CMD_keepalt:
case CMD_keepjumps:
case CMD_keepmarks:
@@ -1226,26 +1784,10 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
return arg;
case CMD_filter:
- if (*arg != NUL) {
- arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL);
- }
- if (arg == NULL || *arg == NUL) {
- xp->xp_context = EXPAND_NOTHING;
- return NULL;
- }
- return (const char *)skipwhite(arg);
+ return set_context_in_filter_cmd(xp, arg);
case CMD_match:
- if (*arg == NUL || !ends_excmd(*arg)) {
- // also complete "None"
- set_context_in_echohl_cmd(xp, arg);
- arg = (const char *)skipwhite(skiptowhite(arg));
- if (*arg != NUL) {
- xp->xp_context = EXPAND_NOTHING;
- arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic, NULL);
- }
- }
- return (const char *)find_nextcmd(arg);
+ return set_context_in_match_cmd(xp, arg);
// All completion for the +cmdline_compl feature goes here.
@@ -1258,49 +1800,11 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
break;
case CMD_global:
- case CMD_vglobal: {
- const int delim = (uint8_t)(*arg); // Get the delimiter.
- if (delim) {
- arg++; // Skip delimiter if there is one.
- }
-
- while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
- if (arg[0] == '\\' && arg[1] != NUL) {
- arg++;
- }
- arg++;
- }
- if (arg[0] != NUL) {
- return arg + 1;
- }
- break;
- }
+ case CMD_vglobal:
+ return find_cmd_after_global_cmd(arg);
case CMD_and:
- case CMD_substitute: {
- const int delim = (uint8_t)(*arg);
- if (delim) {
- // Skip "from" part.
- arg++;
- arg = (const char *)skip_regexp((char *)arg, delim, p_magic, NULL);
- }
- // Skip "to" part.
- while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
- if (arg[0] == '\\' && arg[1] != NUL) {
- arg++;
- }
- arg++;
- }
- if (arg[0] != NUL) { // Skip delimiter.
- arg++;
- }
- while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
- arg++;
- }
- if (arg[0] != NUL) {
- return arg;
- }
- break;
- }
+ case CMD_substitute:
+ return find_cmd_after_substitute_cmd(arg);
case CMD_isearch:
case CMD_dsearch:
case CMD_ilist:
@@ -1310,26 +1814,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_djump:
case CMD_isplit:
case CMD_dsplit:
- // Skip count.
- arg = (const char *)skipwhite(skipdigits(arg));
- if (*arg == '/') { // Match regexp, not just whole words.
- for (++arg; *arg && *arg != '/'; arg++) {
- if (*arg == '\\' && arg[1] != NUL) {
- arg++;
- }
- }
- if (*arg) {
- arg = (const char *)skipwhite(arg + 1);
-
- // Check for trailing illegal characters.
- if (*arg && strchr("|\"\n", *arg) == NULL) {
- xp->xp_context = EXPAND_NOTHING;
- } else {
- return arg;
- }
- }
- }
- break;
+ return find_cmd_after_isearch_cmd(xp, arg);
case CMD_autocmd:
return (const char *)set_context_in_autocmd(xp, (char *)arg, false);
@@ -1337,13 +1822,13 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_doautoall:
return (const char *)set_context_in_autocmd(xp, (char *)arg, true);
case CMD_set:
- set_context_in_set_cmd(xp, (char_u *)arg, 0);
+ set_context_in_set_cmd(xp, (char *)arg, 0);
break;
case CMD_setglobal:
- set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL);
+ set_context_in_set_cmd(xp, (char *)arg, OPT_GLOBAL);
break;
case CMD_setlocal:
- set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL);
+ set_context_in_set_cmd(xp, (char *)arg, OPT_LOCAL);
break;
case CMD_tag:
case CMD_stag:
@@ -1392,20 +1877,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
break;
case CMD_unlet:
- while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
- arg = (const char *)xp->xp_pattern + 1;
- }
-
- xp->xp_context = EXPAND_USER_VARS;
- xp->xp_pattern = (char *)arg;
-
- if (*xp->xp_pattern == '$') {
- xp->xp_context = EXPAND_ENV_VARS;
- xp->xp_pattern++;
- }
-
- break;
-
+ return set_context_in_unlet_cmd(xp, arg);
case CMD_function:
case CMD_delfunction:
xp->xp_context = EXPAND_USER_FUNC;
@@ -1418,13 +1890,8 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_highlight:
set_context_in_highlight_cmd(xp, arg);
break;
- case CMD_cscope:
- case CMD_lcscope:
- case CMD_scscope:
- set_context_in_cscope_cmd(xp, arg, cmdidx);
- break;
case CMD_sign:
- set_context_in_sign_cmd(xp, (char_u *)arg);
+ set_context_in_sign_cmd(xp, (char *)arg);
break;
case CMD_bdelete:
case CMD_bwipeout:
@@ -1449,34 +1916,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_USER:
case CMD_USER_BUF:
- if (context != EXPAND_NOTHING) {
- // EX_XFILE: file names are handled above.
- if (!(argt & EX_XFILE)) {
- if (context == EXPAND_MENUS) {
- return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
- } else if (context == EXPAND_COMMANDS) {
- return arg;
- } else if (context == EXPAND_MAPPINGS) {
- return (const char *)set_context_in_map_cmd(xp, "map", (char_u *)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 = (char *)arg;
- }
- xp->xp_context = context;
- }
- break;
+ return set_context_in_user_cmdarg(cmd, arg, argt, context, xp, forceit);
case CMD_map:
case CMD_noremap:
@@ -1496,7 +1936,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_snoremap:
case CMD_xmap:
case CMD_xnoremap:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, false,
false, cmdidx);
case CMD_unmap:
case CMD_nunmap:
@@ -1507,7 +1947,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_lunmap:
case CMD_sunmap:
case CMD_xunmap:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, false,
true, cmdidx);
case CMD_mapclear:
case CMD_nmapclear:
@@ -1528,12 +1968,12 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_cnoreabbrev:
case CMD_iabbrev:
case CMD_inoreabbrev:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, true,
false, cmdidx);
case CMD_unabbreviate:
case CMD_cunabbrev:
case CMD_iunabbrev:
- return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, true,
true, cmdidx);
case CMD_menu:
case CMD_noremenu:
@@ -1592,22 +2032,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
#ifdef HAVE_WORKING_LIBINTL
case CMD_language:
- p = (const char *)skiptowhite(arg);
- if (*p == NUL) {
- xp->xp_context = EXPAND_LANGUAGE;
- xp->xp_pattern = (char *)arg;
- } else {
- if (strncmp(arg, "messages", (size_t)(p - arg)) == 0
- || strncmp(arg, "ctype", (size_t)(p - arg)) == 0
- || strncmp(arg, "time", (size_t)(p - arg)) == 0
- || strncmp(arg, "collate", (size_t)(p - arg)) == 0) {
- xp->xp_context = EXPAND_LOCALES;
- xp->xp_pattern = skipwhite(p);
- } else {
- xp->xp_context = EXPAND_NOTHING;
- }
- }
- break;
+ return set_context_in_lang_cmd(xp, arg);
#endif
case CMD_profile:
set_context_in_profile_cmd(xp, arg);
@@ -1643,6 +2068,11 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
xp->xp_pattern = (char *)arg;
break;
+ case CMD_breakadd:
+ case CMD_profdel:
+ case CMD_breakdel:
+ return set_context_in_breakadd_cmd(xp, arg, cmdidx);
+
case CMD_lua:
xp->xp_context = EXPAND_LUA;
break;
@@ -1826,17 +2256,19 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
// Switch on command name.
- return set_context_by_cmdname(cmd, ea.cmdidx, arg, ea.argt, context, xp, forceit);
+ return set_context_by_cmdname(cmd, ea.cmdidx, xp, arg, ea.argt, context, forceit);
}
+/// Set the completion context in "xp" for command "str"
+///
/// @param str start of command line
/// @param len length of command line (excl. NUL)
/// @param col position of cursor
/// @param use_ccline use ccline for info
-void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline)
+void set_cmd_context(expand_T *xp, char *str, int len, int col, int use_ccline)
{
CmdlineInfo *const ccline = get_cmdline_info();
- char_u old_char = NUL;
+ char old_char = NUL;
// Avoid a UMR warning from Purify, only save the character if it has been
// written before.
@@ -1848,10 +2280,10 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
if (use_ccline && ccline->cmdfirstc == '=') {
// pass CMD_SIZE because there is no real command
- set_context_for_expression(xp, (char *)str, CMD_SIZE);
+ set_context_for_expression(xp, str, CMD_SIZE);
} else if (use_ccline && ccline->input_fn) {
xp->xp_context = ccline->xp_context;
- xp->xp_pattern = (char *)ccline->cmdbuff;
+ xp->xp_pattern = ccline->cmdbuff;
xp->xp_arg = (char *)ccline->xp_arg;
} else {
while (nextcomm != NULL) {
@@ -1861,7 +2293,7 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
// Store the string here so that call_user_expand_func() can get to them
// easily.
- xp->xp_line = (char *)str;
+ xp->xp_line = str;
xp->xp_col = col;
str[col] = old_char;
@@ -1881,9 +2313,9 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
/// @param col position of cursor
/// @param matchcount return: nr of matches
/// @param matches return: array of pointers to matches
-int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***matches)
+int expand_cmdline(expand_T *xp, const char *str, int col, int *matchcount, char ***matches)
{
- char_u *file_str = NULL;
+ char *file_str = NULL;
int options = WILD_ADD_SLASH|WILD_SILENT;
if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
@@ -1896,16 +2328,16 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***
}
// add star to file name, or convert to regexp if not exp. files.
- assert((str + col) - (char_u *)xp->xp_pattern >= 0);
- xp->xp_pattern_len = (size_t)((str + col) - (char_u *)xp->xp_pattern);
- file_str = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ assert((str + col) - xp->xp_pattern >= 0);
+ xp->xp_pattern_len = (size_t)((str + col) - xp->xp_pattern);
+ file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
if (p_wic) {
options += WILD_ICASE;
}
// find all files that match the description
- if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) {
+ if (ExpandFromContext(xp, file_str, matches, matchcount, options) == FAIL) {
*matchcount = 0;
*matches = NULL;
}
@@ -1915,15 +2347,15 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***
}
/// Expand file or directory names.
-static int expand_files_and_dirs(expand_T *xp, char_u *pat, char ***file, int *num_file, int flags,
- int options)
+static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *numMatches,
+ int flags, int options)
{
bool free_pat = false;
// for ":set path=" and ":set tags=" halve backslashes for escaped space
if (xp->xp_backslash != XP_BS_NONE) {
free_pat = true;
- pat = vim_strsave(pat);
+ pat = xstrdup(pat);
for (int i = 0; pat[i]; i++) {
if (pat[i] == '\\') {
if (xp->xp_backslash == XP_BS_THREE
@@ -1952,14 +2384,14 @@ static int expand_files_and_dirs(expand_T *xp, char_u *pat, char ***file, int *n
}
// Expand wildcards, supporting %:h and the like.
- int ret = expand_wildcards_eval(&pat, num_file, file, flags);
+ int ret = expand_wildcards_eval(&pat, numMatches, matches, flags);
if (free_pat) {
xfree(pat);
}
#ifdef BACKSLASH_IN_FILENAME
if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
- for (int j = 0; j < *num_file; j++) {
- char *ptr = (*file)[j];
+ for (int j = 0; j < *numMatches; j++) {
+ char *ptr = (*matches)[j];
while (*ptr != NUL) {
if (p_csl[0] == 's' && *ptr == '\\') {
*ptr = '/';
@@ -1988,6 +2420,32 @@ static char *get_behave_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":breakadd {expr, file, func, here}" command.
+/// ":breakdel {func, file, here}" command.
+static char *get_breakadd_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ char *opts[] = { "expr", "file", "func", "here" };
+
+ if (idx >= 0 && idx <= 3) {
+ // breakadd {expr, file, func, here}
+ if (breakpt_expand_what == EXP_BREAKPT_ADD) {
+ return opts[idx];
+ } else if (breakpt_expand_what == EXP_BREAKPT_DEL) {
+ // breakdel {func, file, here}
+ if (idx <= 2) {
+ return opts[idx + 1];
+ }
+ } else {
+ // profdel {func, file}
+ if (idx <= 1) {
+ return opts[idx + 1];
+ }
+ }
+ }
+ return NULL;
+}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":messages {clear}" command.
static char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
@@ -2032,7 +2490,7 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx)
}
/// Do the expansion based on xp->xp_context and "rmp".
-static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***file)
+static int ExpandOther(expand_T *xp, regmatch_T *rmp, char ***matches, int *numMatches)
{
typedef CompleteListItemGetter ExpandFunc;
static struct expgen {
@@ -2062,7 +2520,6 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
{ EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, false },
{ EXPAND_EVENTS, expand_get_event_name, true, false },
{ EXPAND_AUGROUP, expand_get_augroup_name, true, false },
- { EXPAND_CSCOPE, get_cscope_name, true, true },
{ EXPAND_SIGN, get_sign_name, true, true },
{ EXPAND_PROFILE, get_profile_name, true, true },
#ifdef HAVE_WORKING_LIBINTL
@@ -2072,6 +2529,7 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
{ EXPAND_ENV_VARS, get_env_name, true, true },
{ EXPAND_USER, get_users, true, false },
{ EXPAND_ARGLIST, get_arglist_name, true, false },
+ { EXPAND_BREAKPOINT, get_breakadd_arg, true, true },
{ EXPAND_CHECKHEALTH, get_healthcheck_names, true, false },
};
int ret = FAIL;
@@ -2083,7 +2541,7 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
if (tab[i].ic) {
rmp->rm_ic = true;
}
- ExpandGeneric(xp, rmp, num_file, file, tab[i].func, tab[i].escaped);
+ ExpandGeneric(xp, rmp, matches, numMatches, tab[i].func, tab[i].escaped);
ret = OK;
break;
}
@@ -2092,16 +2550,10 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
return ret;
}
-/// Do the expansion based on xp->xp_context and "pat".
-///
-/// @param options WILD_ flags
-static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***file, int options)
+/// Map wild expand options to flags for expand_wildcards()
+static int map_wildopts_to_ewflags(int options)
{
- regmatch_T regmatch;
- int ret;
- int flags;
-
- flags = EW_DIR; // include directories
+ int flags = EW_DIR; // include directories
if (options & WILD_LIST_NOTFOUND) {
flags |= EW_NOTFOUND;
}
@@ -2121,87 +2573,98 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
flags |= EW_ALLLINKS;
}
+ return flags;
+}
+
+/// Do the expansion based on xp->xp_context and "pat".
+///
+/// @param options WILD_ flags
+static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numMatches, int options)
+{
+ regmatch_T regmatch;
+ int ret;
+ int flags = map_wildopts_to_ewflags(options);
+
if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES_IN_PATH) {
- return expand_files_and_dirs(xp, pat, file, num_file, flags, options);
+ return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options);
}
- *file = NULL;
- *num_file = 0;
+ *matches = NULL;
+ *numMatches = 0;
if (xp->xp_context == EXPAND_HELP) {
// With an empty argument we would get all the help tags, which is
// very slow. Get matches for "help" instead.
- if (find_help_tags(*pat == NUL ? "help" : (char *)pat,
- num_file, file, false) == OK) {
- cleanup_help_tags(*num_file, *file);
+ if (find_help_tags(*pat == NUL ? "help" : pat,
+ numMatches, matches, false) == OK) {
+ cleanup_help_tags(*numMatches, *matches);
return OK;
}
return FAIL;
}
if (xp->xp_context == EXPAND_SHELLCMD) {
- *file = NULL;
- expand_shellcmd(pat, num_file, file, flags);
+ expand_shellcmd(pat, matches, numMatches, flags);
return OK;
}
if (xp->xp_context == EXPAND_OLD_SETTING) {
- ExpandOldSetting(num_file, file);
+ ExpandOldSetting(numMatches, matches);
return OK;
}
if (xp->xp_context == EXPAND_BUFFERS) {
- return ExpandBufnames((char *)pat, num_file, file, options);
+ return ExpandBufnames(pat, numMatches, matches, options);
}
if (xp->xp_context == EXPAND_DIFF_BUFFERS) {
- return ExpandBufnames((char *)pat, num_file, file, options | BUF_DIFF_FILTER);
+ return ExpandBufnames(pat, numMatches, matches, options | BUF_DIFF_FILTER);
}
if (xp->xp_context == EXPAND_TAGS
|| xp->xp_context == EXPAND_TAGS_LISTFILES) {
- return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
+ return expand_tags(xp->xp_context == EXPAND_TAGS, pat, numMatches, matches);
}
if (xp->xp_context == EXPAND_COLORS) {
char *directories[] = { "colors", NULL };
- return ExpandRTDir((char *)pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_COMPILER) {
char *directories[] = { "compiler", NULL };
- return ExpandRTDir((char *)pat, DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, DIP_LUA, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_OWNSYNTAX) {
char *directories[] = { "syntax", NULL };
- return ExpandRTDir((char *)pat, 0, num_file, file, directories);
+ return ExpandRTDir(pat, 0, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_FILETYPE) {
char *directories[] = { "syntax", "indent", "ftplugin", NULL };
- return ExpandRTDir((char *)pat, DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, DIP_LUA, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_USER_LIST) {
- return ExpandUserList(xp, num_file, file);
+ return ExpandUserList(xp, matches, numMatches);
}
if (xp->xp_context == EXPAND_USER_LUA) {
- return ExpandUserLua(xp, num_file, file);
+ return ExpandUserLua(xp, numMatches, matches);
}
if (xp->xp_context == EXPAND_PACKADD) {
- return ExpandPackAddDir((char *)pat, num_file, file);
+ return ExpandPackAddDir(pat, numMatches, matches);
}
// When expanding a function name starting with s:, match the <SNR>nr_
// prefix.
char *tofree = NULL;
- if (xp->xp_context == EXPAND_USER_FUNC && STRNCMP(pat, "^s:", 3) == 0) {
- const size_t len = STRLEN(pat) + 20;
+ if (xp->xp_context == EXPAND_USER_FUNC && strncmp(pat, "^s:", 3) == 0) {
+ const size_t len = strlen(pat) + 20;
tofree = xmalloc(len);
snprintf(tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
- pat = (char_u *)tofree;
+ pat = tofree;
}
if (xp->xp_context == EXPAND_LUA) {
ILOG("PAT %s", pat);
- return nlua_expand_pat(xp, pat, num_file, file);
+ return nlua_expand_pat(xp, pat, numMatches, matches);
}
- regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
+ regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
if (regmatch.regprog == NULL) {
return FAIL;
}
@@ -2211,13 +2674,13 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
if (xp->xp_context == EXPAND_SETTINGS
|| xp->xp_context == EXPAND_BOOL_SETTINGS) {
- ret = ExpandSettings(xp, &regmatch, num_file, file);
+ ret = ExpandSettings(xp, &regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_MAPPINGS) {
- ret = ExpandMappings(&regmatch, num_file, file);
+ ret = ExpandMappings(&regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_USER_DEFINED) {
- ret = ExpandUserDefined(xp, &regmatch, num_file, file);
+ ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
} else {
- ret = ExpandOther(xp, &regmatch, num_file, file);
+ ret = ExpandOther(xp, &regmatch, matches, numMatches);
}
vim_regfree(regmatch.regprog);
@@ -2233,23 +2696,23 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
/// program. Matching strings are copied into an array, which is returned.
///
/// @param func returns a string from the list
-static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file,
+static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, char ***matches, int *numMatches,
CompleteListItemGetter func, int escaped)
{
int i;
size_t count = 0;
- char_u *str;
+ char *str;
// count the number of matching names
for (i = 0;; i++) {
- str = (char_u *)(*func)(xp, i);
+ str = (*func)(xp, i);
if (str == NULL) { // end of list
break;
}
if (*str == NUL) { // skip empty strings
continue;
}
- if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
+ if (vim_regexec(regmatch, str, (colnr_T)0)) {
count++;
}
}
@@ -2257,29 +2720,29 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
return;
}
assert(count < INT_MAX);
- *num_file = (int)count;
- *file = xmalloc(count * sizeof(char_u *));
+ *numMatches = (int)count;
+ *matches = xmalloc(count * sizeof(char *));
// copy the matching names into allocated memory
count = 0;
for (i = 0;; i++) {
- str = (char_u *)(*func)(xp, i);
+ str = (*func)(xp, i);
if (str == NULL) { // End of list.
break;
}
if (*str == NUL) { // Skip empty strings.
continue;
}
- if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
+ if (vim_regexec(regmatch, str, (colnr_T)0)) {
if (escaped) {
- str = vim_strsave_escaped(str, (char_u *)" \t\\.");
+ str = vim_strsave_escaped(str, " \t\\.");
} else {
- str = vim_strsave(str);
+ str = xstrdup(str);
}
- (*file)[count++] = (char *)str;
+ (*matches)[count++] = str;
if (func == get_menu_names) {
// Test for separator added by get_menu_names().
- str += STRLEN(str) - 1;
+ str += strlen(str) - 1;
if (*str == '\001') {
*str = '.';
}
@@ -2293,10 +2756,9 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
|| xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC) {
// <SNR> functions should be sorted to the end.
- qsort((void *)(*file), (size_t)(*num_file), sizeof(char_u *),
- sort_func_compare);
+ qsort((void *)(*matches), (size_t)(*numMatches), sizeof(char *), sort_func_compare);
} else {
- sort_strings(*file, *num_file);
+ sort_strings(*matches, *numMatches);
}
}
@@ -2305,30 +2767,67 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
reset_expand_highlight();
}
+/// Expand shell command matches in one directory of $PATH.
+static void expand_shellcmd_onedir(char *buf, char *s, size_t l, char *pat, char ***matches,
+ int *numMatches, int flags, hashtab_T *ht, garray_T *gap)
+{
+ xstrlcpy(buf, s, l + 1);
+ add_pathsep(buf);
+ l = strlen(buf);
+ xstrlcpy(buf + l, pat, MAXPATHL - l);
+
+ // Expand matches in one directory of $PATH.
+ int ret = expand_wildcards(1, &buf, numMatches, matches, flags);
+ if (ret != OK) {
+ return;
+ }
+
+ ga_grow(gap, *numMatches);
+
+ for (int i = 0; i < *numMatches; i++) {
+ char *name = (*matches)[i];
+
+ if (strlen(name) > l) {
+ // Check if this name was already found.
+ hash_T hash = hash_hash((char_u *)name + l);
+ hashitem_T *hi =
+ hash_lookup(ht, (const char *)(name + l), strlen(name + l), hash);
+ if (HASHITEM_EMPTY(hi)) {
+ // Remove the path that was prepended.
+ STRMOVE(name, name + l);
+ ((char **)gap->ga_data)[gap->ga_len++] = name;
+ hash_add_item(ht, hi, (char_u *)name, hash);
+ name = NULL;
+ }
+ }
+ xfree(name);
+ }
+ xfree(*matches);
+}
+
/// Complete a shell command.
///
-/// @param filepat is a pattern to match with command names.
-/// @param[out] num_file is pointer to number of matches.
-/// @param[out] file is pointer to array of pointers to matches.
-/// *file will either be set to NULL or point to
-/// allocated memory.
-/// @param flagsarg is a combination of EW_* flags.
-static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int flagsarg)
+/// @param filepat is a pattern to match with command names.
+/// @param[out] matches is pointer to array of pointers to matches.
+/// *matches will either be set to NULL or point to
+/// allocated memory.
+/// @param[out] numMatches is pointer to number of matches.
+/// @param flagsarg is a combination of EW_* flags.
+static void expand_shellcmd(char *filepat, char ***matches, int *numMatches, int flagsarg)
FUNC_ATTR_NONNULL_ALL
{
- char_u *pat;
+ char *pat;
int i;
- char_u *path = NULL;
+ char *path = NULL;
garray_T ga;
char *buf = xmalloc(MAXPATHL);
size_t l;
- char_u *s, *e;
+ char *s, *e;
int flags = flagsarg;
- int ret;
bool did_curdir = false;
// for ":set path=" and ":set tags=" halve backslashes for escaped space
- pat = vim_strsave(filepat);
+ pat = xstrdup(filepat);
for (i = 0; pat[i]; i++) {
if (pat[i] == '\\' && pat[i + 1] == ' ') {
STRMOVE(pat + i, pat + i + 1);
@@ -2340,29 +2839,29 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
bool mustfree = false; // Track memory allocation for *path.
if (pat[0] == '.' && (vim_ispathsep(pat[1])
|| (pat[1] == '.' && vim_ispathsep(pat[2])))) {
- path = (char_u *)".";
+ path = ".";
} else {
// For an absolute name we don't use $PATH.
- if (!path_is_absolute(pat)) {
- path = (char_u *)vim_getenv("PATH");
+ if (!path_is_absolute((char_u *)pat)) {
+ path = vim_getenv("PATH");
}
if (path == NULL) {
- path = (char_u *)"";
+ path = "";
} else {
mustfree = true;
}
}
// Go over all directories in $PATH. Expand matches in that directory and
- // collect them in "ga". When "." is not in $PATH also expaned for the
+ // collect them in "ga". When "." is not in $PATH also expand for the
// current directory, to find "subdir/cmd".
ga_init(&ga, (int)sizeof(char *), 10);
hashtab_T found_ht;
hash_init(&found_ht);
for (s = path;; s = e) {
- e = (char_u *)vim_strchr((char *)s, ENV_SEPCHAR);
+ e = vim_strchr(s, ENV_SEPCHAR);
if (e == NULL) {
- e = s + STRLEN(s);
+ e = s + strlen(s);
}
if (*s == NUL) {
@@ -2372,7 +2871,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
// Find directories in the current directory, path is empty.
did_curdir = true;
flags |= EW_DIR;
- } else if (STRNCMP(s, ".", e - s) == 0) {
+ } else if (strncmp(s, ".", (size_t)(e - s)) == 0) {
did_curdir = true;
flags |= EW_DIR;
} else {
@@ -2384,44 +2883,13 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
if (l > MAXPATHL - 5) {
break;
}
- STRLCPY(buf, s, l + 1);
- add_pathsep(buf);
- l = STRLEN(buf);
- STRLCPY(buf + l, pat, MAXPATHL - l);
-
- // Expand matches in one directory of $PATH.
- ret = expand_wildcards(1, &buf, num_file, file, flags);
- if (ret == OK) {
- ga_grow(&ga, *num_file);
- {
- for (i = 0; i < *num_file; i++) {
- char_u *name = (char_u *)(*file)[i];
-
- if (STRLEN(name) > l) {
- // Check if this name was already found.
- hash_T hash = hash_hash(name + l);
- hashitem_T *hi =
- hash_lookup(&found_ht, (const char *)(name + l),
- STRLEN(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;
- }
- }
- xfree(name);
- }
- xfree(*file);
- }
- }
+ expand_shellcmd_onedir(buf, s, l, pat, matches, numMatches, flags, &found_ht, &ga);
if (*e != NUL) {
e++;
}
}
- *file = ga.ga_data;
- *num_file = ga.ga_len;
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
xfree(buf);
xfree(pat);
@@ -2433,39 +2901,36 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
/// Call "user_expand_func()" to invoke a user defined Vim script function and
/// return the result (either a string, a List or NULL).
-static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file,
- char ***file)
+static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp)
FUNC_ATTR_NONNULL_ALL
{
CmdlineInfo *const ccline = get_cmdline_info();
- char_u keep = 0;
+ char keep = 0;
typval_T args[4];
- char_u *pat = NULL;
+ char *pat = NULL;
const sctx_T save_current_sctx = current_sctx;
if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) {
return NULL;
}
- *num_file = 0;
- *file = NULL;
if (ccline->cmdbuff != NULL) {
keep = ccline->cmdbuff[ccline->cmdlen];
ccline->cmdbuff[ccline->cmdlen] = 0;
}
- pat = vim_strnsave((char_u *)xp->xp_pattern, xp->xp_pattern_len);
+ pat = xstrnsave(xp->xp_pattern, xp->xp_pattern_len);
args[0].v_type = VAR_STRING;
args[1].v_type = VAR_STRING;
args[2].v_type = VAR_NUMBER;
args[3].v_type = VAR_UNKNOWN;
- args[0].vval.v_string = (char *)pat;
+ args[0].vval.v_string = pat;
args[1].vval.v_string = xp->xp_line;
args[2].vval.v_number = xp->xp_col;
current_sctx = xp->xp_script_ctx;
- void *const ret = user_expand_func((char_u *)xp->xp_arg, 3, args);
+ void *const ret = user_expand_func(xp->xp_arg, 3, args);
current_sctx = save_current_sctx;
if (ccline->cmdbuff != NULL) {
@@ -2477,32 +2942,32 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
}
/// Expand names with a function defined by the user.
-static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
+static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, char ***matches, int *numMatches)
{
- char_u *e;
+ char *e;
garray_T ga;
- char_u *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, num_file,
- file);
+ *matches = NULL;
+ *numMatches = 0;
+ char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp);
if (retstr == NULL) {
return FAIL;
}
ga_init(&ga, (int)sizeof(char *), 3);
- for (char_u *s = retstr; *s != NUL; s = e) {
- e = (char_u *)vim_strchr((char *)s, '\n');
+ for (char *s = retstr; *s != NUL; s = e) {
+ e = vim_strchr(s, '\n');
if (e == NULL) {
- e = s + STRLEN(s);
+ e = s + strlen(s);
}
- const char_u keep = *e;
+ const char keep = *e;
*e = NUL;
- const bool skip = xp->xp_pattern[0]
- && vim_regexec(regmatch, (char *)s, (colnr_T)0) == 0;
+ const bool skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0;
*e = keep;
if (!skip) {
- GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
+ GA_APPEND(char *, &ga, xstrnsave(s, (size_t)(e - s)));
}
if (*e != NUL) {
@@ -2510,16 +2975,17 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
}
}
xfree(retstr);
- *file = ga.ga_data;
- *num_file = ga.ga_len;
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
return OK;
}
/// Expand names with a list returned by a function defined by the user.
-static int ExpandUserList(expand_T *xp, int *num_file, char ***file)
+static int ExpandUserList(expand_T *xp, char ***matches, int *numMatches)
{
- list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, num_file,
- file);
+ *matches = NULL;
+ *numMatches = 0;
+ list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp);
if (retlist == NULL) {
return FAIL;
}
@@ -2537,8 +3003,8 @@ static int ExpandUserList(expand_T *xp, int *num_file, char ***file)
});
tv_list_unref(retlist);
- *file = ga.ga_data;
- *num_file = ga.ga_len;
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
return OK;
}
@@ -2579,19 +3045,19 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options)
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
- char_u *buf = xmalloc(MAXPATHL);
+ char *buf = xmalloc(MAXPATHL);
// Loop over all entries in {path}.
while (*path != NUL) {
// Copy one item of the path to buf[] and concatenate the file name.
- copy_option_part(&path, (char *)buf, MAXPATHL, ",");
- if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) {
- add_pathsep((char *)buf);
+ copy_option_part(&path, buf, MAXPATHL, ",");
+ if (strlen(buf) + strlen(file) + 2 < MAXPATHL) {
+ add_pathsep(buf);
STRCAT(buf, file); // NOLINT
char **p;
int num_p = 0;
- (void)ExpandFromContext(&xpc, buf, &num_p, &p,
+ (void)ExpandFromContext(&xpc, buf, &p, &num_p,
WILD_SILENT | expand_options);
if (num_p > 0) {
ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options);
@@ -2646,145 +3112,158 @@ static void cmdline_del(CmdlineInfo *cclp, int from)
cclp->cmdpos = from;
}
-/// Handle a key pressed when wild menu is displayed
-int wildmenu_process_key(CmdlineInfo *cclp, int key, expand_T *xp)
+/// Handle a key pressed when the wild menu for the menu names
+/// (EXPAND_MENUNAMES) is displayed.
+static int wildmenu_process_key_menunames(CmdlineInfo *cclp, int key, expand_T *xp)
{
- int c = key;
-
- // 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] != '\\') {
+ // Hitting <Down> after "emenu Name.": complete submenu
+ if (key == K_DOWN && cclp->cmdpos > 0
+ && cclp->cmdbuff[cclp->cmdpos - 1] == '.') {
+ key = (int)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;
+
+ int j = (int)(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;
}
-
- // 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 (i > 0) {
+ cmdline_del(cclp, i);
+ }
+ key = (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
+
+ return key;
+}
+
+/// 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(CmdlineInfo *cclp, int key, expand_T *xp)
+{
+ char upseg[5];
+ 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 = (int)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;
+
+ int j = cclp->cmdpos;
+ int i = (int)(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;
}
- } 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])
+ }
+ if (found
+ && cclp->cmdbuff[j - 1] == '.'
+ && cclp->cmdbuff[j - 2] == '.'
+ && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) {
+ cmdline_del(cclp, j - 2);
+ key = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ }
+ } else if (key == K_UP) {
+ // go up a directory
+ int found = false;
+
+ int j = cclp->cmdpos - 1;
+ int i = (int)(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
+ && vim_strchr(" *?[{`$%#", (uint8_t)cclp->cmdbuff[j + 1]) == NULL
#endif
- ) {
- if (found) {
- i = j + 1;
- break;
- } else {
- found = true;
- }
+ ) {
+ 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);
- }
+ 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;
+ }
- // Now complete in the new directory. Set KeyTyped in case the
- // Up key came from a mapping.
- c = (int)p_wc;
- KeyTyped = true;
+ 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.
+ key = (int)p_wc;
+ KeyTyped = true;
}
- return c;
+ return key;
+}
+
+/// Handle a key pressed when wild menu is displayed
+int wildmenu_process_key(CmdlineInfo *cclp, int key, expand_T *xp)
+{
+ // Special translations for 'wildmenu'
+ if (xp->xp_context == EXPAND_MENUNAMES) {
+ return wildmenu_process_key_menunames(cclp, key, xp);
+ }
+ 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;
}
/// Free expanded names when finished walking through the matches
@@ -2811,7 +3290,7 @@ void wildmenu_cleanup(CmdlineInfo *cclp)
p_ls = save_p_ls;
p_wmh = save_p_wmh;
last_status(false);
- update_screen(UPD_VALID); // redraw the screen NOW
+ update_screen(); // redraw the screen NOW
redrawcmd();
save_p_ls = -1;
wild_menu_showing = 0;
@@ -2832,7 +3311,7 @@ void wildmenu_cleanup(CmdlineInfo *cclp)
/// "getcompletion()" function
void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u *pat;
+ char *pat;
expand_T xpc;
bool filtered = false;
int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
@@ -2865,14 +3344,14 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (strcmp(type, "cmdline") == 0) {
set_one_cmd_context(&xpc, pattern);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- xpc.xp_col = (int)STRLEN(pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
+ xpc.xp_col = (int)strlen(pattern);
goto theend;
}
ExpandInit(&xpc);
xpc.xp_pattern = (char *)pattern;
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
xpc.xp_context = cmdcomplete_str_to_type(type);
if (xpc.xp_context == EXPAND_NOTHING) {
semsg(_(e_invarg2), type);
@@ -2881,21 +3360,16 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (xpc.xp_context == EXPAND_MENUS) {
set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- }
-
- if (xpc.xp_context == EXPAND_CSCOPE) {
- set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
}
if (xpc.xp_context == EXPAND_SIGN) {
- set_context_in_sign_cmd(&xpc, (char_u *)xpc.xp_pattern);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
}
theend:
- pat = addstar((char_u *)xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
+ pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);