Skip to content

Commit

Permalink
patch 9.0.2025: no cmdline completion for ++opt args
Browse files Browse the repository at this point in the history
Problem:  no cmdline completion for ++opt args
Solution: Add cmdline completion for :e ++opt=arg and :terminal
          [++options]

closes: #13319

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
  • Loading branch information
ychin authored and chrisbra committed Oct 14, 2023
1 parent bd734c3 commit 989426b
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 8 deletions.
1 change: 1 addition & 0 deletions runtime/doc/cmdline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ When editing the command-line, a few commands can be used to complete the
word before the cursor. This is available for:

- Command names: At the start of the command-line.
- |++opt| values.
- Tags: Only after the ":tag" command.
- File names: Only after a command that accepts a file name or a setting for
an option that can be set to a file name. This is called file name
Expand Down
76 changes: 69 additions & 7 deletions src/cmdexpand.c
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,45 @@ set_context_for_wildcard_arg(
}
}

/*
* Set the completion context for the "++opt=arg" argument. Always returns
* NULL.
*/
static char_u *
set_context_in_argopt(expand_T *xp, char_u *arg)
{
char_u *p;

p = vim_strchr(arg, '=');
if (p == NULL)
xp->xp_pattern = arg;
else
xp->xp_pattern = p + 1;

xp->xp_context = EXPAND_ARGOPT;
return NULL;
}

#ifdef FEAT_TERMINAL
/*
* Set the completion context for :terminal's [options]. Always returns NULL.
*/
static char_u *
set_context_in_terminalopt(expand_T *xp, char_u *arg)
{
char_u *p;

p = vim_strchr(arg, '=');
if (p == NULL)
xp->xp_pattern = arg;
else
xp->xp_pattern = p + 1;

xp->xp_context = EXPAND_TERMINALOPT;
return NULL;
}
#endif

/*
* Set the completion context for the :filter command. Returns a pointer to the
* next command after the :filter command.
Expand Down Expand Up @@ -2491,13 +2530,28 @@ set_one_cmd_context(

arg = skipwhite(p);

// Skip over ++argopt argument
if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
// Does command allow "++argopt" argument?
if ((ea.argt & EX_ARGOPT) || ea.cmdidx == CMD_terminal)
{
p = arg;
while (*p && !vim_isspace(*p))
MB_PTR_ADV(p);
arg = skipwhite(p);
while (*arg != NUL && STRNCMP(arg, "++", 2) == 0)
{
p = arg + 2;
while (*p && !vim_isspace(*p))
MB_PTR_ADV(p);

// Still touching the command after "++"?
if (*p == NUL)
{
if (ea.argt & EX_ARGOPT)
return set_context_in_argopt(xp, arg + 2);
#ifdef FEAT_TERMINAL
if (ea.cmdidx == CMD_terminal)
return set_context_in_terminalopt(xp, arg + 2);
#endif
}

arg = skipwhite(p);
}
}

if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
Expand Down Expand Up @@ -3120,6 +3174,12 @@ ExpandFromContext(
ret = ExpandSettingSubtract(xp, &regmatch, numMatches, matches);
else if (xp->xp_context == EXPAND_MAPPINGS)
ret = ExpandMappings(pat, &regmatch, numMatches, matches);
else if (xp->xp_context == EXPAND_ARGOPT)
ret = expand_argopt(pat, xp, &regmatch, matches, numMatches);
#if defined(FEAT_TERMINAL)
else if (xp->xp_context == EXPAND_TERMINALOPT)
ret = expand_terminal_opt(pat, xp, &regmatch, matches, numMatches);
#endif
#if defined(FEAT_EVAL)
else if (xp->xp_context == EXPAND_USER_DEFINED)
ret = ExpandUserDefined(pat, xp, &regmatch, matches, numMatches);
Expand Down Expand Up @@ -3253,7 +3313,9 @@ ExpandGeneric(
if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES
&& xp->xp_context != EXPAND_STRING_SETTING
&& xp->xp_context != EXPAND_MENUS
&& xp->xp_context != EXPAND_SCRIPTNAMES)
&& xp->xp_context != EXPAND_SCRIPTNAMES
&& xp->xp_context != EXPAND_ARGOPT
&& xp->xp_context != EXPAND_TERMINALOPT)
sort_matches = TRUE;

// <SNR> functions should be sorted to the end.
Expand Down
111 changes: 111 additions & 0 deletions src/ex_docmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -5407,6 +5407,25 @@ get_bad_opt(char_u *p, exarg_T *eap)
return OK;
}

/*
* Function given to ExpandGeneric() to obtain the list of bad= names.
*/
static char_u *
get_bad_name(expand_T *xp UNUSED, int idx)
{
// Note: Keep this in sync with getargopt.
static char *(p_bad_values[]) =
{
"?",
"keep",
"drop",
};

if (idx < (int)ARRAY_LENGTH(p_bad_values))
return (char_u*)p_bad_values[idx];
return NULL;
}

/*
* Get "++opt=arg" argument.
* Return FAIL or OK.
Expand All @@ -5419,6 +5438,8 @@ getargopt(exarg_T *eap)
int bad_char_idx;
char_u *p;

// Note: Keep this in sync with get_argopt_name.

// ":edit ++[no]bin[ary] file"
if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0)
{
Expand Down Expand Up @@ -5499,6 +5520,96 @@ getargopt(exarg_T *eap)
return OK;
}

/*
* Function given to ExpandGeneric() to obtain the list of ++opt names.
*/
static char_u *
get_argopt_name(expand_T *xp UNUSED, int idx)
{
// Note: Keep this in sync with getargopt.
static char *(p_opt_values[]) =
{
"fileformat=",
"encoding=",
"binary",
"nobinary",
"bad=",
"edit",
};

if (idx < (int)ARRAY_LENGTH(p_opt_values))
return (char_u*)p_opt_values[idx];
return NULL;
}

/*
* Command-line expansion for ++opt=name.
*/
int
expand_argopt(
char_u *pat,
expand_T *xp,
regmatch_T *rmp,
char_u ***matches,
int *numMatches)
{
if (xp->xp_pattern > xp->xp_line && *(xp->xp_pattern-1) == '=')
{
char_u *(*cb)(expand_T *, int) = NULL;

char_u *name_end = xp->xp_pattern - 1;
if (name_end - xp->xp_line >= 2
&& STRNCMP(name_end - 2, "ff", 2) == 0)
cb = get_fileformat_name;
else if (name_end - xp->xp_line >= 10
&& STRNCMP(name_end - 10, "fileformat", 10) == 0)
cb = get_fileformat_name;
else if (name_end - xp->xp_line >= 3
&& STRNCMP(name_end - 3, "enc", 3) == 0)
cb = get_encoding_name;
else if (name_end - xp->xp_line >= 8
&& STRNCMP(name_end - 8, "encoding", 8) == 0)
cb = get_encoding_name;
else if (name_end - xp->xp_line >= 3
&& STRNCMP(name_end - 3, "bad", 3) == 0)
cb = get_bad_name;

if (cb != NULL)
{
return ExpandGeneric(
pat,
xp,
rmp,
matches,
numMatches,
cb,
FALSE);
}
return FAIL;
}

// Special handling of "ff" which acts as a short form of
// "fileformat", as "ff" is not a substring of it.
if (STRCMP(xp->xp_pattern, "ff") == 0)
{
*matches = ALLOC_MULT(char_u *, 1);
if (*matches == NULL)
return FAIL;
*numMatches = 1;
(*matches)[0] = vim_strsave((char_u*)"fileformat=");
return OK;
}

return ExpandGeneric(
pat,
xp,
rmp,
matches,
numMatches,
get_argopt_name,
FALSE);
}

static void
ex_autocmd(exarg_T *eap)
{
Expand Down
13 changes: 13 additions & 0 deletions src/optionstr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,19 @@ expand_set_fileformat(optexpand_T *args, int *numMatches, char_u ***matches)
matches);
}

/*
* Function given to ExpandGeneric() to obtain the possible arguments of the
* fileformat options.
*/
char_u *
get_fileformat_name(expand_T *xp UNUSED, int idx)
{
if (idx >= (int)ARRAY_LENGTH(p_ff_values))
return NULL;

return (char_u*)p_ff_values[idx];
}

/*
* The 'fileformats' option is changed.
*/
Expand Down
1 change: 1 addition & 0 deletions src/proto/ex_docmd.pro
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp);
void separate_nextcmd(exarg_T *eap, int keep_backslash);
char_u *skip_cmd_arg(char_u *p, int rembs);
int get_bad_opt(char_u *p, exarg_T *eap);
int expand_argopt(char_u *pat, expand_T *xp, regmatch_T *rmp, char_u ***matches, int *numMatches);
int ends_excmd(int c);
int ends_excmd2(char_u *cmd_start, char_u *cmd);
char_u *find_nextcmd(char_u *p);
Expand Down
1 change: 1 addition & 0 deletions src/proto/optionstr.pro
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ int expand_set_wildoptions(optexpand_T *args, int *numMatches, char_u ***matches
int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_wincolor(optexpand_T *args, int *numMatches, char_u ***matches);
int check_ff_value(char_u *p);
char_u *get_fileformat_name(expand_T *xp, int idx);
void save_clear_shm_value(void);
void restore_shm_value(void);
/* vim: set ft=c : */
1 change: 1 addition & 0 deletions src/proto/terminal.pro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
void init_job_options(jobopt_T *opt);
buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags);
void ex_terminal(exarg_T *eap);
int expand_terminal_opt(char_u *pat, expand_T *xp, regmatch_T *rmp, char_u ***matches, int *numMatches);
int term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs);
int term_should_restore(buf_T *buf);
void free_terminal(buf_T *buf);
Expand Down
3 changes: 2 additions & 1 deletion src/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ typedef enum {
*/
typedef struct expand
{
char_u *xp_pattern; // start of item to expand
char_u *xp_pattern; // start of item to expand, guaranteed
// to be part of xp_line
int xp_context; // type of expansion
int xp_pattern_len; // bytes in xp_pattern before cursor
xp_prefix_T xp_prefix;
Expand Down
Loading

0 comments on commit 989426b

Please sign in to comment.