patch 9.1.1341: cannot define completion triggers

Commit: 
https://github.com/vim/vim/commit/bcd5995b40a1c26e735bc326feb2e3ac4b05426b
Author: glepnir <glephun...@gmail.com>
Date:   Thu Apr 24 21:48:35 2025 +0200

    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 <glephun...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index e6747adcf..9f91f47d4 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({startcol}, {matches}) none        set Insert mode 
completion
 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 @@ complete_info([{what}])                          
*complete_info()*
 <
                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 =~ '^ bc'
+           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 8073457a0..03a6a6c5c 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 @@ 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.
        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 636977084..a094e1483 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -436,6 +436,8 @@ $quote      eval.txt        /*$quote*
 '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_add()    builtin.txt     /*complete_add()*
 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 a3f2d7cfc..522368787 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 @@ Insert mode completion/expansion:
 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 0c24b8469..a22b6f798 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 @@ Insert mode completion:                           
*completion-functions*
        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 dfbe457a2..9560b1dfb 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 @@ Functions: ~
 |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 @@ Options: ~
 '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 c5e77afd7..941a63c40 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 <b...@vim.org>
 
 " 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>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("$", "     " .. s:local_to_buffer)
 call <SID>AddOption("iskeyword", gettext("specifies the characters in a 
keyword"))
 call append("$", "     " .. s:local_to_buffer)
 call <SID>OptionL("isk")
diff --git a/src/buffer.c b/src/buffer.c
index 0624f9dce..697efa30c 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2494,6 +2494,7 @@ free_buf_options(
     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 39f6aa9ab..3b13cf0d2 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -2104,6 +2104,8 @@ static funcentry_T global_functions[] =
                        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 ace4f55b5..77c98311d 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -3550,6 +3550,147 @@ f_complete_check(typval_T *argvars UNUSED, typval_T 
*rettv)
     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
  */
diff --git a/src/option.c b/src/option.c
index cfdeb44e1..ab9ee66cf 100644
--- a/src/option.c
+++ b/src/option.c
@@ -6400,6 +6400,9 @@ unset_global_local_option(char_u *name, void *from)
            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 @@ get_varp_scope(struct vimoption *p, int scope)
            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 @@ get_varp(struct vimoption *p)
 #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_copy_options(buf_T *buf, int flags)
            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 db1030d12..bb1226e44 100644
--- a/src/option.h
+++ b/src/option.h
@@ -731,6 +731,7 @@ EXTERN char_u       *p_inde;        // 'indentexpr'
 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 @@ enum
     , 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 8ed4ebe78..f03510440 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 @@ static struct vimoption options[] =
     {"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 90b8e52ff..f60957eb2 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -310,6 +310,7 @@ check_buf_options(buf_T *buf)
     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);
@@ -2864,6 +2865,48 @@ did_set_imactivatekey(optset_T *args UNUSED)
 }
 #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.
  */
diff --git a/src/proto/insexpand.pro b/src/proto/insexpand.pro
index 8529b7b6c..e9ff62661 100644
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -67,4 +67,5 @@ void ins_compl_check_keys(int frequency, int in_compl_func);
 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 dc1fb0a10..4c4ec3e47 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -101,6 +101,7 @@ char *did_set_highlight(optset_T *args);
 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 b5c898d16..45e1b189e 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -3302,6 +3302,7 @@ struct file_buffer
     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 a0876290f..3d798e9f8 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -229,6 +229,7 @@ let test_values = {
       \ '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 b03132f7e..73565a56e 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -4328,4 +4328,86 @@ func Test_nearest_cpt_option()
   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 =~ '^ bc'
+        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 417088bfa..8d881242f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1341,
 /**/
     1340,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1u82jv-005dfQ-AP%40256bit.org.

Raspunde prin e-mail lui