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>
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index e6747ad..9f91f47 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -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
@@ -136,6 +136,7 @@
complete_add({expr}) Number add completion match
complete_check() Number check for key typed during completion
complete_info([{what}]) Dict get current completion information
+complete_match([{lnum}, {col}]) List get completion column and trigger text
confirm({msg} [, {choices} [, {default} [, {type}]]])
Number number of choice picked by user
copy({expr}) any make a shallow copy of {expr}
@@ -2032,6 +2033,50 @@
<
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({msg} [, {choices} [, {default} [, {type}]]])
confirm() offers the user a dialog, from which a choice can be
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 8073457..03a6a6c 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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
@@ -4983,6 +4983,19 @@
and there is a letter before it, the completed part is made uppercase.
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' boolean (default off)
global
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 6369770..a094e14 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -436,6 +436,8 @@
'infercase' options.txt /*'infercase'*
'insertmode' options.txt /*'insertmode'*
'is' options.txt /*'is'*
+'ise' options.txt /*'ise'*
+'isexpand' options.txt /*'isexpand'*
'isf' options.txt /*'isf'*
'isfname' options.txt /*'isfname'*
'isi' options.txt /*'isi'*
@@ -6663,6 +6665,7 @@
complete_check() builtin.txt /*complete_check()*
complete_info() builtin.txt /*complete_info()*
complete_info_mode builtin.txt /*complete_info_mode*
+complete_match() builtin.txt /*complete_match()*
completed_item-variable eval.txt /*completed_item-variable*
completion-functions usr_41.txt /*completion-functions*
complex-change change.txt /*complex-change*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index a3f2d7c..5223687 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -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
@@ -4749,20 +4749,10 @@
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
"$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
the last added line, instead of continuing where the last match ended.
(Webb)
-8 Add option to set different behavior for Insert mode completion:
- - 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 option 'istagword': characters used for CTRL-]. like 'isexpand'
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
text does: www.textware.com
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 0c24b84..a22b6f7 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -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
@@ -1124,6 +1124,8 @@
complete_add() add to found matches
complete_check() check if completion should be aborted
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
pum_getpos() position and size of popup menu if visible
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index dfbe457..9560b1d 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -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
@@ -41688,6 +41688,7 @@
|blob2str()| convert a blob into a List of strings
|bindtextdomain()| set message lookup translation base path
|cmdcomplete_info()| get current cmdline completion info
+|complete_match()| get completion and trigger info
|diff()| diff two Lists of strings
|filecopy()| copy a file {from} to {to}
|foreach()| apply function to List items
@@ -41750,6 +41751,7 @@
'eventignorewin' autocommand events that are ignored in a window
'findfunc' Vim function to obtain the results for a |:find|
command
+'isexpand' defines triggers for completion
'lhistory' Size of the location list stack |quickfix-stack|.
'messagesopt' configure |:messages| and |hit-enter| prompt
'pummaxwidth' maximum width for the completion popup menu
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index c5e77af..941a63c 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1,7 +1,7 @@
" These commands create the option window.
"
" 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>
" If there already is an option window, jump to that one.
@@ -1254,6 +1254,8 @@
call <SID>OptionG("isf", &isf)
call <SID>AddOption("isident", gettext("specifies the characters in an identifier"))
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 append("$", "\t" .. s:local_to_buffer)
call <SID>OptionL("isk")
diff --git a/src/buffer.c b/src/buffer.c
index 0624f9d..697efa3 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2494,6 +2494,7 @@
clear_string_option(&buf->b_p_cinw);
clear_string_option(&buf->b_p_cot);
clear_string_option(&buf->b_p_cpt);
+ clear_string_option(&buf->b_p_ise);
#ifdef FEAT_COMPL_FUNC
clear_string_option(&buf->b_p_cfu);
free_callback(&buf->b_cfu_cb);
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 39f6aa9..3b13cf0 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -2104,6 +2104,8 @@
ret_number_bool, f_complete_check},
{"complete_info", 0, 1, FEARG_1, arg1_list_string,
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,
ret_number, f_confirm},
{"copy", 1, 1, FEARG_1, NULL,
diff --git a/src/insexpand.c b/src/insexpand.c
index ace4f55..77c9831 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -3551,6 +3551,147 @@
}
/*
+ * 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(®match, 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
*/
static char_u *
diff --git a/src/option.c b/src/option.c
index cfdeb44..ab9ee66 100644
--- a/src/option.c
+++ b/src/option.c
@@ -6400,6 +6400,9 @@
clear_string_option(&buf->b_p_cot);
buf->b_cot_flags = 0;
break;
+ case PV_ISE:
+ clear_string_option(&buf->b_p_ise);
+ break;
case PV_DICT:
clear_string_option(&buf->b_p_dict);
break;
@@ -6518,6 +6521,7 @@
case PV_INC: return (char_u *)&(curbuf->b_p_inc);
#endif
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_TSR: return (char_u *)&(curbuf->b_p_tsr);
#ifdef FEAT_COMPL_FUNC
@@ -6600,6 +6604,8 @@
#endif
case PV_COT: return *curbuf->b_p_cot != NUL
? (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
? (char_u *)&(curbuf->b_p_dict) : p->var;
case PV_TSR: return *curbuf->b_p_tsr != NUL
@@ -7431,6 +7437,7 @@
buf->b_cot_flags = 0;
buf->b_p_dict = empty_option;
buf->b_p_tsr = empty_option;
+ buf->b_p_ise = empty_option;
#ifdef FEAT_COMPL_FUNC
buf->b_p_tsrfu = empty_option;
#endif
diff --git a/src/option.h b/src/option.h
index db1030d..bb1226e 100644
--- a/src/option.h
+++ b/src/option.h
@@ -731,6 +731,7 @@
EXTERN char_u *p_indk; // 'indentkeys'
#endif
EXTERN int p_im; // 'insertmode'
+EXTERN char_u *p_ise; // 'isexpand'
EXTERN char_u *p_isf; // 'isfname'
EXTERN char_u *p_isi; // 'isident'
EXTERN char_u *p_isk; // 'iskeyword'
@@ -1205,6 +1206,7 @@
, BV_INEX
#endif
, BV_INF
+ , BV_ISE
, BV_ISK
#ifdef FEAT_CRYPT
, BV_KEY
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 8ed4ebe..f035104 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -90,6 +90,7 @@
# define PV_INEX OPT_BUF(BV_INEX)
#endif
#define PV_INF OPT_BUF(BV_INF)
+#define PV_ISE OPT_BOTH(OPT_BUF(BV_ISE))
#define PV_ISK OPT_BUF(BV_ISK)
#ifdef FEAT_CRYPT
# define PV_KEY OPT_BUF(BV_KEY)
@@ -1458,6 +1459,10 @@
{"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM,
(char_u *)&p_im, PV_NONE, did_set_insertmode, NULL,
{(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,
(char_u *)&p_isf, PV_NONE, did_set_isopt, NULL,
{
diff --git a/src/optionstr.c b/src/optionstr.c
index 90b8e52..f60957e 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -310,6 +310,7 @@
check_string_option(&buf->b_p_cinw);
check_string_option(&buf->b_p_cot);
check_string_option(&buf->b_p_cpt);
+ check_string_option(&buf->b_p_ise);
#ifdef FEAT_COMPL_FUNC
check_string_option(&buf->b_p_cfu);
check_string_option(&buf->b_p_ofu);
@@ -2865,6 +2866,48 @@
#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.
*/
char *
diff --git a/src/proto/insexpand.pro b/src/proto/insexpand.pro
index 8529b7b..e9ff626 100644
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -67,4 +67,5 @@
int ins_complete(int c, int enable_pum);
void free_insexpand_stuff(void);
int ins_compl_cancel(void);
+void f_complete_match(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index dc1fb0a..4c4ec3e 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -101,6 +101,7 @@
int expand_set_highlight(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_iconstring(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_isopt(optset_T *args);
char *did_set_jumpoptions(optset_T *args);
diff --git a/src/structs.h b/src/structs.h
index b5c898d..45e1b18 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -3302,6 +3302,7 @@
char_u *b_p_fo; // 'formatoptions'
char_u *b_p_flp; // 'formatlistpat'
int b_p_inf; // 'infercase'
+ char_u *b_p_ise; // 'isexpand' local value
char_u *b_p_isk; // 'iskeyword'
#ifdef FEAT_FIND_ID
char_u *b_p_def; // 'define' local value
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index a087629..3d798e9 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -229,6 +229,7 @@
\ 'imactivatekey': [['', 'S-space'], ['xxx']],
\ 'isfname': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'isident': [['', '@', '@,48-52'], ['xxx', '@48']],
+ \ 'isexpand': [['', '.,->', '/,/*,\\,'], [',,', '\\,,']],
\ 'iskeyword': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'isprint': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'jumpoptions': [['', 'stack'], ['xxx']],
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index b03132f..73565a5 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -4328,4 +4328,86 @@
delfunc PrintMenuWords
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
diff --git a/src/version.c b/src/version.c
index 417088b..8d88124 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1341,
+/**/
1340,
/**/
1339,