0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 9.1.1341: cannot define completion triggers

Problem:  Cannot define completion triggers and act upon it
Solution: add the new option 'isexpand' and add the complete_match()
          function to return the completion matches according to the
          'isexpand' setting (glepnir)

Currently, completion trigger position is determined solely by the
'iskeyword' pattern (\k\+$), which causes issues when users need
different completion behaviors - such as triggering after '/' for
comments or '.' for methods. Modifying 'iskeyword' to include these
characters has undesirable side effects on other Vim functionality that
relies on keyword definitions.

Introduce a new buffer-local option 'isexpand' that allows specifying
different completion triggers and add the complete_match() function that
finds the appropriate start column for completion based on these
triggers, scanning backwards from cursor position.

This separation of concerns allows customized completion behavior
without affecting iskeyword-dependent features. The option's
buffer-local nature enables per-filetype completion triggers.

closes: #16716

Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
glepnir 2025-04-24 21:48:35 +02:00 committed by Christian Brabandt
parent 32f49738d1
commit bcd5995b40
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
20 changed files with 363 additions and 17 deletions

View File

@ -1,4 +1,4 @@
*builtin.txt* For Vim version 9.1. Last change: 2025 Apr 23 *builtin.txt* For Vim version 9.1. Last change: 2025 Apr 24
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -136,6 +136,7 @@ complete({startcol}, {matches}) none set Insert mode completion
complete_add({expr}) Number add completion match complete_add({expr}) Number add completion match
complete_check() Number check for key typed during completion complete_check() Number check for key typed during completion
complete_info([{what}]) Dict get current completion information complete_info([{what}]) Dict get current completion information
complete_match([{lnum}, {col}]) List get completion column and trigger text
confirm({msg} [, {choices} [, {default} [, {type}]]]) confirm({msg} [, {choices} [, {default} [, {type}]]])
Number number of choice picked by user Number number of choice picked by user
copy({expr}) any make a shallow copy of {expr} copy({expr}) any make a shallow copy of {expr}
@ -2032,6 +2033,50 @@ complete_info([{what}]) *complete_info()*
< <
Return type: dict<any> Return type: dict<any>
complete_match([{lnum}, {col}]) *complete_match()*
Returns a List of matches found according to the 'isexpand'
option. Each match is represented as a List containing
[startcol, trigger_text] where:
- startcol: column position where completion should start,
or -1 if no trigger position is found. For multi-character
triggers, returns the column of the first character.
- trigger_text: the matching trigger string from 'isexpand',
or empty string if no match was found or when using the
default 'iskeyword' pattern.
When 'isexpand' is empty, uses the 'iskeyword' pattern
"\k\+$" to find the start of the current keyword.
When no arguments are provided, uses the current cursor
position.
Examples: >
set isexpand=.,->,/,/*,abc
func CustomComplete()
let res = complete_match()
if res->len() == 0 | return | endif
let [col, trigger] = res[0]
let items = []
if trigger == '/*'
let items = ['/** */']
elseif trigger == '/'
let items = ['/*! */', '// TODO:', '// fixme:']
elseif trigger == '.'
let items = ['length()']
elseif trigger =~ '^\->'
let items = ['map()', 'reduce()']
elseif trigger =~ '^\abc'
let items = ['def', 'ghk']
endif
if items->len() > 0
let startcol = trigger =~ '^/' ? col : col + len(trigger)
call complete(startcol, items)
endif
endfunc
inoremap <Tab> <Cmd>call CustomComplete()<CR>
<
Return type: list<list<any>>
*confirm()* *confirm()*
confirm({msg} [, {choices} [, {default} [, {type}]]]) confirm({msg} [, {choices} [, {default} [, {type}]]])
confirm() offers the user a dialog, from which a choice can be confirm() offers the user a dialog, from which a choice can be

View File

@ -1,4 +1,4 @@
*options.txt* For Vim version 9.1. Last change: 2025 Apr 19 *options.txt* For Vim version 9.1. Last change: 2025 Apr 24
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -4983,6 +4983,19 @@ A jump table for the options with a short description can be found at |Q_op|.
and there is a letter before it, the completed part is made uppercase. and there is a letter before it, the completed part is made uppercase.
With 'noinfercase' the match is used as-is. With 'noinfercase' the match is used as-is.
*'isexpand'* *'ise'*
'isexpand' 'ise' string (default: "")
local to buffer
Defines characters and patterns for completion in insert mode. Used by
the |complete_match()| function to determine the starting position for
completion. This is a comma-separated list of triggers. Each trigger
can be:
- A single character like "." or "/"
- A sequence of characters like "->", "/*", or "/**"
Note: Use "\\," to add a literal comma as trigger character, see
|option-backslash|.
*'insertmode'* *'im'* *'noinsertmode'* *'noim'* *'insertmode'* *'im'* *'noinsertmode'* *'noim'*
'insertmode' 'im' boolean (default off) 'insertmode' 'im' boolean (default off)
global global

View File

@ -436,6 +436,8 @@ $quote eval.txt /*$quote*
'infercase' options.txt /*'infercase'* 'infercase' options.txt /*'infercase'*
'insertmode' options.txt /*'insertmode'* 'insertmode' options.txt /*'insertmode'*
'is' options.txt /*'is'* 'is' options.txt /*'is'*
'ise' options.txt /*'ise'*
'isexpand' options.txt /*'isexpand'*
'isf' options.txt /*'isf'* 'isf' options.txt /*'isf'*
'isfname' options.txt /*'isfname'* 'isfname' options.txt /*'isfname'*
'isi' options.txt /*'isi'* 'isi' options.txt /*'isi'*
@ -6663,6 +6665,7 @@ complete_add() builtin.txt /*complete_add()*
complete_check() builtin.txt /*complete_check()* complete_check() builtin.txt /*complete_check()*
complete_info() builtin.txt /*complete_info()* complete_info() builtin.txt /*complete_info()*
complete_info_mode builtin.txt /*complete_info_mode* complete_info_mode builtin.txt /*complete_info_mode*
complete_match() builtin.txt /*complete_match()*
completed_item-variable eval.txt /*completed_item-variable* completed_item-variable eval.txt /*completed_item-variable*
completion-functions usr_41.txt /*completion-functions* completion-functions usr_41.txt /*completion-functions*
complex-change change.txt /*complex-change* complex-change change.txt /*complex-change*

View File

@ -1,4 +1,4 @@
*todo.txt* For Vim version 9.1. Last change: 2025 Mar 27 *todo.txt* For Vim version 9.1. Last change: 2025 Apr 24
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -4749,20 +4749,10 @@ Insert mode completion/expansion:
7 When expanding file names with an environment variable, add the match with 7 When expanding file names with an environment variable, add the match with
the unexpanded var. So $HOME/tm expands to "/home/guy/tmp" and the unexpanded var. So $HOME/tm expands to "/home/guy/tmp" and
"$HOME/tmp" "$HOME/tmp"
8 When there is no word before the cursor but something like "sys." complete
with "sys.". Works well for C and similar languages.
9 ^X^L completion doesn't repeat correctly. It uses the first match with 9 ^X^L completion doesn't repeat correctly. It uses the first match with
the last added line, instead of continuing where the last match ended. the last added line, instead of continuing where the last match ended.
(Webb) (Webb)
8 Add option to set different behavior for Insert mode completion: 8 Add option 'istagword': characters used for CTRL-]. like 'isexpand'
- ignore/match case
- different characters than 'iskeyword'
8 Add option 'isexpand', containing characters when doing expansion (so that
"." and "\" can be included, without changing 'iskeyword'). (Goldfarb)
Also: 'istagword': characters used for CTRL-].
When 'isexpand' or 'istagword' are empty, use 'iskeyword'.
Alternative: Use a pattern so that start and end of a keyword can be
defined, only allow dash in the middle, etc.
8 Add a command to undo the completion, go back to the original text. 8 Add a command to undo the completion, go back to the original text.
7 Completion of an abbreviation: Can leave letters out, like what Instant 7 Completion of an abbreviation: Can leave letters out, like what Instant
text does: www.textware.com text does: www.textware.com

View File

@ -1,4 +1,4 @@
*usr_41.txt* For Vim version 9.1. Last change: 2025 Apr 21 *usr_41.txt* For Vim version 9.1. Last change: 2025 Apr 24
VIM USER MANUAL - by Bram Moolenaar VIM USER MANUAL - by Bram Moolenaar
@ -1124,6 +1124,8 @@ Insert mode completion: *completion-functions*
complete_add() add to found matches complete_add() add to found matches
complete_check() check if completion should be aborted complete_check() check if completion should be aborted
complete_info() get current completion information complete_info() get current completion information
complete_match() get insert completion start match col and
trigger text
pumvisible() check if the popup menu is displayed pumvisible() check if the popup menu is displayed
pum_getpos() position and size of popup menu if visible pum_getpos() position and size of popup menu if visible

View File

@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.1. Last change: 2025 Apr 23 *version9.txt* For Vim version 9.1. Last change: 2025 Apr 24
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -41688,6 +41688,7 @@ Functions: ~
|blob2str()| convert a blob into a List of strings |blob2str()| convert a blob into a List of strings
|bindtextdomain()| set message lookup translation base path |bindtextdomain()| set message lookup translation base path
|cmdcomplete_info()| get current cmdline completion info |cmdcomplete_info()| get current cmdline completion info
|complete_match()| get completion and trigger info
|diff()| diff two Lists of strings |diff()| diff two Lists of strings
|filecopy()| copy a file {from} to {to} |filecopy()| copy a file {from} to {to}
|foreach()| apply function to List items |foreach()| apply function to List items
@ -41750,6 +41751,7 @@ Options: ~
'eventignorewin' autocommand events that are ignored in a window 'eventignorewin' autocommand events that are ignored in a window
'findfunc' Vim function to obtain the results for a |:find| 'findfunc' Vim function to obtain the results for a |:find|
command command
'isexpand' defines triggers for completion
'lhistory' Size of the location list stack |quickfix-stack|. 'lhistory' Size of the location list stack |quickfix-stack|.
'messagesopt' configure |:messages| and |hit-enter| prompt 'messagesopt' configure |:messages| and |hit-enter| prompt
'pummaxwidth' maximum width for the completion popup menu 'pummaxwidth' maximum width for the completion popup menu

View File

@ -1,7 +1,7 @@
" These commands create the option window. " These commands create the option window.
" "
" Maintainer: The Vim Project <https://github.com/vim/vim> " Maintainer: The Vim Project <https://github.com/vim/vim>
" Last Change: 2025 Apr 07 " Last Change: 2025 Apr 24
" Former Maintainer: Bram Moolenaar <Bram@vim.org> " Former Maintainer: Bram Moolenaar <Bram@vim.org>
" If there already is an option window, jump to that one. " If there already is an option window, jump to that one.
@ -1254,6 +1254,8 @@ call <SID>AddOption("isfname", gettext("specifies the characters in a file name"
call <SID>OptionG("isf", &isf) call <SID>OptionG("isf", &isf)
call <SID>AddOption("isident", gettext("specifies the characters in an identifier")) call <SID>AddOption("isident", gettext("specifies the characters in an identifier"))
call <SID>OptionG("isi", &isi) call <SID>OptionG("isi", &isi)
call <SID>AddOption("isexpand", gettext("defines trigger strings for complete_match()"))
call append("$", "\t" .. s:local_to_buffer)
call <SID>AddOption("iskeyword", gettext("specifies the characters in a keyword")) call <SID>AddOption("iskeyword", gettext("specifies the characters in a keyword"))
call append("$", "\t" .. s:local_to_buffer) call append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("isk") call <SID>OptionL("isk")

View File

@ -2494,6 +2494,7 @@ free_buf_options(
clear_string_option(&buf->b_p_cinw); clear_string_option(&buf->b_p_cinw);
clear_string_option(&buf->b_p_cot); clear_string_option(&buf->b_p_cot);
clear_string_option(&buf->b_p_cpt); clear_string_option(&buf->b_p_cpt);
clear_string_option(&buf->b_p_ise);
#ifdef FEAT_COMPL_FUNC #ifdef FEAT_COMPL_FUNC
clear_string_option(&buf->b_p_cfu); clear_string_option(&buf->b_p_cfu);
free_callback(&buf->b_cfu_cb); free_callback(&buf->b_cfu_cb);

View File

@ -2104,6 +2104,8 @@ static funcentry_T global_functions[] =
ret_number_bool, f_complete_check}, ret_number_bool, f_complete_check},
{"complete_info", 0, 1, FEARG_1, arg1_list_string, {"complete_info", 0, 1, FEARG_1, arg1_list_string,
ret_dict_any, f_complete_info}, ret_dict_any, f_complete_info},
{"complete_match", 0, 2, 0, NULL,
ret_list_any, f_complete_match},
{"confirm", 1, 4, FEARG_1, arg4_string_string_number_string, {"confirm", 1, 4, FEARG_1, arg4_string_string_number_string,
ret_number, f_confirm}, ret_number, f_confirm},
{"copy", 1, 1, FEARG_1, NULL, {"copy", 1, 1, FEARG_1, NULL,

View File

@ -3550,6 +3550,147 @@ f_complete_check(typval_T *argvars UNUSED, typval_T *rettv)
RedrawingDisabled = save_RedrawingDisabled; RedrawingDisabled = save_RedrawingDisabled;
} }
/*
* Add match item to the return list.
* Returns FAIL if out of memory, OK otherwise.
*/
static int
add_match_to_list(
typval_T *rettv,
char_u *str,
int len,
int pos)
{
list_T *match;
int ret;
match = list_alloc();
if (match == NULL)
return FAIL;
if ((ret = list_append_number(match, pos + 1)) == FAIL
|| (ret = list_append_string(match, str, len)) == FAIL
|| (ret = list_append_list(rettv->vval.v_list, match)) == FAIL)
{
vim_free(match);
return FAIL;
}
return OK;
}
/*
* "complete_match()" function
*/
void
f_complete_match(typval_T *argvars, typval_T *rettv)
{
linenr_T lnum;
colnr_T col;
char_u *line = NULL;
char_u *ise = NULL;
regmatch_T regmatch;
char_u *before_cursor = NULL;
char_u *cur_end = NULL;
char_u *trig = NULL;
int bytepos = 0;
char_u part[MAXPATHL];
int ret;
if (rettv_list_alloc(rettv) == FAIL)
return;
ise = curbuf->b_p_ise[0] != NUL ? curbuf->b_p_ise : p_ise;
if (argvars[0].v_type == VAR_UNKNOWN)
{
lnum = curwin->w_cursor.lnum;
col = curwin->w_cursor.col;
}
else if (argvars[1].v_type == VAR_UNKNOWN)
{
emsg(_(e_invalid_argument));
return;
}
else
{
lnum = (linenr_T)tv_get_number(&argvars[0]);
col = (colnr_T)tv_get_number(&argvars[1]);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
{
semsg(_(e_invalid_line_number_nr), lnum);
return;
}
if (col < 1 || col > ml_get_buf_len(curbuf, lnum))
{
semsg(_(e_invalid_column_number_nr), col + 1);
return;
}
}
line = ml_get_buf(curbuf, lnum, FALSE);
if (line == NULL)
return;
before_cursor = vim_strnsave(line, col);
if (before_cursor == NULL)
return;
if (ise == NULL || *ise == NUL)
{
regmatch.regprog = vim_regcomp((char_u *)"\\k\\+$", RE_MAGIC);
if (regmatch.regprog != NULL)
{
if (vim_regexec_nl(&regmatch, before_cursor, (colnr_T)0))
{
bytepos = (int)(regmatch.startp[0] - before_cursor);
trig = vim_strnsave(regmatch.startp[0],
regmatch.endp[0] - regmatch.startp[0]);
if (trig == NULL)
{
vim_free(before_cursor);
return;
}
ret = add_match_to_list(rettv, trig, -1, bytepos);
vim_free(trig);
if (ret == FAIL)
{
vim_free(trig);
vim_regfree(regmatch.regprog);
return;
}
}
vim_regfree(regmatch.regprog);
}
}
else
{
char_u *p = ise;
cur_end = before_cursor + (int)STRLEN(before_cursor);
while (*p != NUL)
{
int len = copy_option_part(&p, part, MAXPATHL, ",");
if (len > 0 && len <= col)
{
if (STRNCMP(cur_end - len, part, len) == 0)
{
bytepos = col - len;
if (add_match_to_list(rettv, part, len, bytepos) == FAIL)
{
vim_free(before_cursor);
return;
}
}
}
}
}
vim_free(before_cursor);
}
/* /*
* Return Insert completion mode name string * Return Insert completion mode name string
*/ */

View File

@ -6400,6 +6400,9 @@ unset_global_local_option(char_u *name, void *from)
clear_string_option(&buf->b_p_cot); clear_string_option(&buf->b_p_cot);
buf->b_cot_flags = 0; buf->b_cot_flags = 0;
break; break;
case PV_ISE:
clear_string_option(&buf->b_p_ise);
break;
case PV_DICT: case PV_DICT:
clear_string_option(&buf->b_p_dict); clear_string_option(&buf->b_p_dict);
break; break;
@ -6518,6 +6521,7 @@ get_varp_scope(struct vimoption *p, int scope)
case PV_INC: return (char_u *)&(curbuf->b_p_inc); case PV_INC: return (char_u *)&(curbuf->b_p_inc);
#endif #endif
case PV_COT: return (char_u *)&(curbuf->b_p_cot); case PV_COT: return (char_u *)&(curbuf->b_p_cot);
case PV_ISE: return (char_u *)&(curbuf->b_p_ise);
case PV_DICT: return (char_u *)&(curbuf->b_p_dict); case PV_DICT: return (char_u *)&(curbuf->b_p_dict);
case PV_TSR: return (char_u *)&(curbuf->b_p_tsr); case PV_TSR: return (char_u *)&(curbuf->b_p_tsr);
#ifdef FEAT_COMPL_FUNC #ifdef FEAT_COMPL_FUNC
@ -6600,6 +6604,8 @@ get_varp(struct vimoption *p)
#endif #endif
case PV_COT: return *curbuf->b_p_cot != NUL case PV_COT: return *curbuf->b_p_cot != NUL
? (char_u *)&(curbuf->b_p_cot) : p->var; ? (char_u *)&(curbuf->b_p_cot) : p->var;
case PV_ISE: return *curbuf->b_p_ise != NUL
? (char_u *)&(curbuf->b_p_ise) : p->var;
case PV_DICT: return *curbuf->b_p_dict != NUL case PV_DICT: return *curbuf->b_p_dict != NUL
? (char_u *)&(curbuf->b_p_dict) : p->var; ? (char_u *)&(curbuf->b_p_dict) : p->var;
case PV_TSR: return *curbuf->b_p_tsr != NUL case PV_TSR: return *curbuf->b_p_tsr != NUL
@ -7431,6 +7437,7 @@ buf_copy_options(buf_T *buf, int flags)
buf->b_cot_flags = 0; buf->b_cot_flags = 0;
buf->b_p_dict = empty_option; buf->b_p_dict = empty_option;
buf->b_p_tsr = empty_option; buf->b_p_tsr = empty_option;
buf->b_p_ise = empty_option;
#ifdef FEAT_COMPL_FUNC #ifdef FEAT_COMPL_FUNC
buf->b_p_tsrfu = empty_option; buf->b_p_tsrfu = empty_option;
#endif #endif

View File

@ -731,6 +731,7 @@ EXTERN char_u *p_inde; // 'indentexpr'
EXTERN char_u *p_indk; // 'indentkeys' EXTERN char_u *p_indk; // 'indentkeys'
#endif #endif
EXTERN int p_im; // 'insertmode' EXTERN int p_im; // 'insertmode'
EXTERN char_u *p_ise; // 'isexpand'
EXTERN char_u *p_isf; // 'isfname' EXTERN char_u *p_isf; // 'isfname'
EXTERN char_u *p_isi; // 'isident' EXTERN char_u *p_isi; // 'isident'
EXTERN char_u *p_isk; // 'iskeyword' EXTERN char_u *p_isk; // 'iskeyword'
@ -1205,6 +1206,7 @@ enum
, BV_INEX , BV_INEX
#endif #endif
, BV_INF , BV_INF
, BV_ISE
, BV_ISK , BV_ISK
#ifdef FEAT_CRYPT #ifdef FEAT_CRYPT
, BV_KEY , BV_KEY

View File

@ -90,6 +90,7 @@
# define PV_INEX OPT_BUF(BV_INEX) # define PV_INEX OPT_BUF(BV_INEX)
#endif #endif
#define PV_INF OPT_BUF(BV_INF) #define PV_INF OPT_BUF(BV_INF)
#define PV_ISE OPT_BOTH(OPT_BUF(BV_ISE))
#define PV_ISK OPT_BUF(BV_ISK) #define PV_ISK OPT_BUF(BV_ISK)
#ifdef FEAT_CRYPT #ifdef FEAT_CRYPT
# define PV_KEY OPT_BUF(BV_KEY) # define PV_KEY OPT_BUF(BV_KEY)
@ -1458,6 +1459,10 @@ static struct vimoption options[] =
{"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM, {"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM,
(char_u *)&p_im, PV_NONE, did_set_insertmode, NULL, (char_u *)&p_im, PV_NONE, did_set_insertmode, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT}, {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"isexpand", "ise", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
(char_u *)&p_ise, PV_ISE, did_set_isexpand, NULL,
{(char_u *)"", (char_u *)0L}
SCTX_INIT},
{"isfname", "isf", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, {"isfname", "isf", P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
(char_u *)&p_isf, PV_NONE, did_set_isopt, NULL, (char_u *)&p_isf, PV_NONE, did_set_isopt, NULL,
{ {

View File

@ -310,6 +310,7 @@ check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_cinw); check_string_option(&buf->b_p_cinw);
check_string_option(&buf->b_p_cot); check_string_option(&buf->b_p_cot);
check_string_option(&buf->b_p_cpt); check_string_option(&buf->b_p_cpt);
check_string_option(&buf->b_p_ise);
#ifdef FEAT_COMPL_FUNC #ifdef FEAT_COMPL_FUNC
check_string_option(&buf->b_p_cfu); check_string_option(&buf->b_p_cfu);
check_string_option(&buf->b_p_ofu); check_string_option(&buf->b_p_ofu);
@ -2864,6 +2865,48 @@ did_set_imactivatekey(optset_T *args UNUSED)
} }
#endif #endif
/*
* The 'isexpand' option is changed.
*/
char *
did_set_isexpand(optset_T *args)
{
char_u *ise = p_ise;
char_u *p;
int last_was_comma = FALSE;
if (args->os_flags & OPT_LOCAL)
ise = curbuf->b_p_ise;
for (p = ise; *p != NUL;)
{
if (*p == '\\' && p[1] == ',')
{
p += 2;
last_was_comma = FALSE;
continue;
}
if (*p == ',')
{
if (last_was_comma)
return e_invalid_argument;
last_was_comma = TRUE;
p++;
continue;
}
last_was_comma = FALSE;
MB_PTR_ADV(p);
}
if (last_was_comma)
return e_invalid_argument;
return NULL;
}
/* /*
* The 'iskeyword' option is changed. * The 'iskeyword' option is changed.
*/ */

View File

@ -67,4 +67,5 @@ void ins_compl_check_keys(int frequency, int in_compl_func);
int ins_complete(int c, int enable_pum); int ins_complete(int c, int enable_pum);
void free_insexpand_stuff(void); void free_insexpand_stuff(void);
int ins_compl_cancel(void); int ins_compl_cancel(void);
void f_complete_match(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@ -101,6 +101,7 @@ char *did_set_highlight(optset_T *args);
int expand_set_highlight(optexpand_T *args, int *numMatches, char_u ***matches); int expand_set_highlight(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_iconstring(optset_T *args); char *did_set_iconstring(optset_T *args);
char *did_set_imactivatekey(optset_T *args); char *did_set_imactivatekey(optset_T *args);
char *did_set_isexpand(optset_T *args);
char *did_set_iskeyword(optset_T *args); char *did_set_iskeyword(optset_T *args);
char *did_set_isopt(optset_T *args); char *did_set_isopt(optset_T *args);
char *did_set_jumpoptions(optset_T *args); char *did_set_jumpoptions(optset_T *args);

View File

@ -3302,6 +3302,7 @@ struct file_buffer
char_u *b_p_fo; // 'formatoptions' char_u *b_p_fo; // 'formatoptions'
char_u *b_p_flp; // 'formatlistpat' char_u *b_p_flp; // 'formatlistpat'
int b_p_inf; // 'infercase' int b_p_inf; // 'infercase'
char_u *b_p_ise; // 'isexpand' local value
char_u *b_p_isk; // 'iskeyword' char_u *b_p_isk; // 'iskeyword'
#ifdef FEAT_FIND_ID #ifdef FEAT_FIND_ID
char_u *b_p_def; // 'define' local value char_u *b_p_def; // 'define' local value

View File

@ -229,6 +229,7 @@ let test_values = {
\ 'imactivatekey': [['', 'S-space'], ['xxx']], \ 'imactivatekey': [['', 'S-space'], ['xxx']],
\ 'isfname': [['', '@', '@,48-52'], ['xxx', '@48']], \ 'isfname': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'isident': [['', '@', '@,48-52'], ['xxx', '@48']], \ 'isident': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'isexpand': [['', '.,->', '/,/*,\\,'], [',,', '\\,,']],
\ 'iskeyword': [['', '@', '@,48-52'], ['xxx', '@48']], \ 'iskeyword': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'isprint': [['', '@', '@,48-52'], ['xxx', '@48']], \ 'isprint': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'jumpoptions': [['', 'stack'], ['xxx']], \ 'jumpoptions': [['', 'stack'], ['xxx']],

View File

@ -4328,4 +4328,86 @@ func Test_nearest_cpt_option()
delfunc PrintMenuWords delfunc PrintMenuWords
endfunc endfunc
func Test_complete_match()
set isexpand=.,/,->,abc,/*,_
func TestComplete()
let res = complete_match()
if res->len() == 0
return
endif
let [startcol, expandchar] = res[0]
if startcol >= 0
let line = getline('.')
let items = []
if expandchar == '/*'
let items = ['/** */']
elseif expandchar =~ '^/'
let items = ['/*! */', '// TODO:', '// fixme:']
elseif expandchar =~ '^\.' && startcol < 4
let items = ['length()', 'push()', 'pop()', 'slice()']
elseif expandchar =~ '^\.' && startcol > 4
let items = ['map()', 'filter()', 'reduce()']
elseif expandchar =~ '^\abc'
let items = ['def', 'ghk']
elseif expandchar =~ '^\->'
let items = ['free()', 'xfree()']
else
let items = ['test1', 'test2', 'test3']
endif
call complete(expandchar =~ '^/' ? startcol : startcol + strlen(expandchar), items)
endif
endfunc
new
inoremap <buffer> <F5> <cmd>call TestComplete()<CR>
call feedkeys("S/*\<F5>\<C-Y>", 'tx')
call assert_equal('/** */', getline('.'))
call feedkeys("S/\<F5>\<C-N>\<C-Y>", 'tx')
call assert_equal('// TODO:', getline('.'))
call feedkeys("Swp.\<F5>\<C-N>\<C-Y>", 'tx')
call assert_equal('wp.push()', getline('.'))
call feedkeys("Swp.property.\<F5>\<C-N>\<C-Y>", 'tx')
call assert_equal('wp.property.filter()', getline('.'))
call feedkeys("Sp->\<F5>\<C-N>\<C-Y>", 'tx')
call assert_equal('p->xfree()', getline('.'))
call feedkeys("Swp->property.\<F5>\<C-Y>", 'tx')
call assert_equal('wp->property.map()', getline('.'))
call feedkeys("Sabc\<F5>\<C-Y>", 'tx')
call assert_equal('abcdef', getline('.'))
call feedkeys("S_\<F5>\<C-Y>", 'tx')
call assert_equal('_test1', getline('.'))
set ise&
call feedkeys("Sabc \<ESC>:let g:result=complete_match()\<CR>", 'tx')
call assert_equal([[1, 'abc']], g:result)
call assert_fails('call complete_match(99, 0)', 'E966:')
call assert_fails('call complete_match(1, 99)', 'E964:')
call assert_fails('call complete_match(1)', 'E474:')
set ise=你好,
call feedkeys("S你好 \<ESC>:let g:result=complete_match()\<CR>", 'tx')
call assert_equal([[1, '你好'], [4, '好']], g:result)
set ise=\\,,->
call feedkeys("Sabc, \<ESC>:let g:result=complete_match()\<CR>", 'tx')
call assert_equal([[4, ',']], g:result)
bw!
unlet g:result
set isexpand&
delfunc TestComplete
endfunc
" vim: shiftwidth=2 sts=2 expandtab nofoldenable " vim: shiftwidth=2 sts=2 expandtab nofoldenable

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1341,
/**/ /**/
1340, 1340,
/**/ /**/