vim-patch:9.0.1956: Custom completion skips orig cmdline if it invokes glob() (#25427)

Problem:  Custom cmdline completion skips original cmdline when pressing
          Ctrl-P at first match if completion function invokes glob().
Solution: Move orig_save into struct expand_T.

closes: vim/vim#13216

28a23602e8
This commit is contained in:
zeertzjq 2023-09-30 06:30:10 +08:00 committed by GitHub
parent be463e7643
commit dbfdb52ea8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 14 deletions

View File

@ -617,7 +617,7 @@ static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int m
/// Get the next or prev cmdline completion match. The index of the match is set /// Get the next or prev cmdline completion match. The index of the match is set
/// in "xp->xp_selected" /// in "xp->xp_selected"
static char *get_next_or_prev_match(int mode, expand_T *xp, char *orig_save) static char *get_next_or_prev_match(int mode, expand_T *xp)
{ {
if (xp->xp_numfiles <= 0) { if (xp->xp_numfiles <= 0) {
return NULL; return NULL;
@ -677,14 +677,14 @@ static char *get_next_or_prev_match(int mode, expand_T *xp, char *orig_save)
// When wrapping around, return the original string, set findex to -1. // When wrapping around, return the original string, set findex to -1.
if (findex < 0) { if (findex < 0) {
if (orig_save == NULL) { if (xp->xp_orig == NULL) {
findex = xp->xp_numfiles - 1; findex = xp->xp_numfiles - 1;
} else { } else {
findex = -1; findex = -1;
} }
} }
if (findex >= xp->xp_numfiles) { if (findex >= xp->xp_numfiles) {
if (orig_save == NULL) { if (xp->xp_orig == NULL) {
findex = 0; findex = 0;
} else { } else {
findex = -1; findex = -1;
@ -698,7 +698,7 @@ static char *get_next_or_prev_match(int mode, expand_T *xp, char *orig_save)
} }
xp->xp_selected = findex; xp->xp_selected = findex;
return xstrdup(findex == -1 ? orig_save : xp->xp_files[findex]); return xstrdup(findex == -1 ? xp->xp_orig : xp->xp_files[findex]);
} }
/// Start the command-line expansion and get the matches. /// Start the command-line expansion and get the matches.
@ -805,8 +805,8 @@ static char *find_longest_match(expand_T *xp, int options)
/// Return NULL for failure. /// Return NULL for failure.
/// ///
/// "orig" is the originally expanded string, copied to allocated memory. It /// "orig" is the originally expanded string, copied to allocated memory. It
/// should either be kept in orig_save or freed. When "mode" is WILD_NEXT or /// should either be kept in "xp->xp_orig" or freed. When "mode" is WILD_NEXT
/// WILD_PREV "orig" should be NULL. /// or WILD_PREV "orig" should be NULL.
/// ///
/// Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode" /// Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
/// is WILD_EXPAND_FREE or WILD_ALL. /// is WILD_EXPAND_FREE or WILD_ALL.
@ -841,21 +841,20 @@ static char *find_longest_match(expand_T *xp, int options)
char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode) char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode)
{ {
char *ss = NULL; char *ss = NULL;
static char *orig_save = NULL; // kept value of orig
int orig_saved = false; int orig_saved = false;
// first handle the case of using an old match // 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_PAGEUP || mode == WILD_PAGEDOWN
|| mode == WILD_PUM_WANT) { || mode == WILD_PUM_WANT) {
return get_next_or_prev_match(mode, xp, orig_save); return get_next_or_prev_match(mode, xp);
} }
if (mode == WILD_CANCEL) { if (mode == WILD_CANCEL) {
ss = xstrdup(orig_save ? orig_save : ""); ss = xstrdup(xp->xp_orig ? xp->xp_orig : "");
} else if (mode == WILD_APPLY) { } else if (mode == WILD_APPLY) {
ss = xstrdup(xp->xp_selected == -1 ss = xstrdup(xp->xp_selected == -1
? (orig_save ? orig_save : "") ? (xp->xp_orig ? xp->xp_orig : "")
: xp->xp_files[xp->xp_selected]); : xp->xp_files[xp->xp_selected]);
} }
@ -863,7 +862,7 @@ char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode)
if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) { if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) {
FreeWild(xp->xp_numfiles, xp->xp_files); FreeWild(xp->xp_numfiles, xp->xp_files);
xp->xp_numfiles = -1; xp->xp_numfiles = -1;
XFREE_CLEAR(orig_save); XFREE_CLEAR(xp->xp_orig);
// The entries from xp_files may be used in the PUM, remove it. // The entries from xp_files may be used in the PUM, remove it.
if (compl_match_array != NULL) { if (compl_match_array != NULL) {
@ -877,8 +876,8 @@ char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode)
} }
if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL) { if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL) {
xfree(orig_save); xfree(xp->xp_orig);
orig_save = orig; xp->xp_orig = orig;
orig_saved = true; orig_saved = true;
ss = ExpandOne_start(mode, xp, str, options); ss = ExpandOne_start(mode, xp, str, options);
@ -927,7 +926,7 @@ char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode)
ExpandCleanup(xp); ExpandCleanup(xp);
} }
// Free "orig" if it wasn't stored in "orig_save". // Free "orig" if it wasn't stored in "xp->xp_orig".
if (!orig_saved) { if (!orig_saved) {
xfree(orig); xfree(orig);
} }
@ -952,6 +951,7 @@ void ExpandCleanup(expand_T *xp)
FreeWild(xp->xp_numfiles, xp->xp_files); FreeWild(xp->xp_numfiles, xp->xp_files);
xp->xp_numfiles = -1; xp->xp_numfiles = -1;
} }
XFREE_CLEAR(xp->xp_orig);
} }
/// Display one line of completion matches. Multiple matches are displayed in /// Display one line of completion matches. Multiple matches are displayed in

View File

@ -245,6 +245,7 @@ struct expand {
int xp_numfiles; // number of files found by file name completion int xp_numfiles; // number of files found by file name completion
int xp_col; // cursor position in line int xp_col; // cursor position in line
int xp_selected; // selected index in completion int xp_selected; // selected index in completion
char *xp_orig; // originally expanded string
char **xp_files; // list of files char **xp_files; // list of files
char *xp_line; // text being completed char *xp_line; // text being completed
#define EXPAND_BUF_LEN 256 #define EXPAND_BUF_LEN 256

View File

@ -3702,4 +3702,20 @@ func Test_custom_completion()
delfunc Check_customlist_completion delfunc Check_customlist_completion
endfunc endfunc
func Test_custom_completion_with_glob()
func TestGlobComplete(A, L, P)
return split(glob('Xglob*'), "\n")
endfunc
command -nargs=* -complete=customlist,TestGlobComplete TestGlobComplete :
call writefile([], 'Xglob1', 'D')
call writefile([], 'Xglob2', 'D')
call feedkeys(":TestGlobComplete \<Tab> \<Tab>\<C-N> \<Tab>\<C-P>;\<C-B>\"\<CR>", 'xt')
call assert_equal('"TestGlobComplete Xglob1 Xglob2 ;', @:)
delcommand TestGlobComplete
delfunc TestGlobComplete
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab