commit e7e665b489c5976a54ec9489c4b49a8e18ba4e9a
parent 31e31273bc03e39dd6c2173b4c51d46f1978c627
Author: glepnir <glephunter@gmail.com>
Date: Mon, 28 Apr 2025 14:03:52 +0800
vim-patch:ffc89e4 runtime(doc): clarify complete_match() and 'isexpand' option
clarify complete_match() documentation to better explain its backward
search behavior, argument handling, and return value format and add an
example of isexpand
closes: https://github.com/vim/vim/pull/17212
https://github.com/vim/vim/commit/ffc89e47d014178bcd0a681ff2c8e18470cc972b
Diffstat:
13 files changed, 115 insertions(+), 122 deletions(-)
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
@@ -1264,8 +1264,11 @@ complete_info([{what}]) *complete_info()*
(`table`)
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
+ Searches backward from the given position and returns a List
+ of matches according to the 'isexpand' option. When no
+ arguments are provided, uses the current cursor position.
+
+ 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
@@ -1277,40 +1280,35 @@ complete_match([{lnum}, {col}]) *complete_match()*
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: >
+ Examples: >vim
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
+ 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>>
-
Parameters: ~
- • {lnum} (`integer??`)
- • {col} (`integer??`)
+ • {lnum} (`integer?`)
+ • {col} (`integer?`)
Return: ~
(`table`)
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
@@ -3550,6 +3550,10 @@ A jump table for the options with a short description can be found at |Q_op|.
Note: Use "\\," to add a literal comma as trigger character, see
|option-backslash|.
+ Examples: >vim
+ set isexpand=.,->,/*,\\,
+<
+
*'isfname'* *'isf'*
'isfname' 'isf' string (default for Windows:
"@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,="
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
@@ -934,6 +934,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/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
@@ -3439,6 +3439,13 @@ vim.bo.inf = vim.bo.infercase
--- Note: Use "\\," to add a literal comma as trigger character, see
--- `option-backslash`.
---
+--- Examples:
+---
+--- ```vim
+--- set isexpand=.,->,/*,\\,
+--- ```
+---
+---
--- @type string
vim.o.isexpand = ""
vim.o.ise = vim.o.isexpand
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
@@ -1109,8 +1109,11 @@ function vim.fn.complete_check() end
--- @return table
function vim.fn.complete_info(what) end
---- Returns a List of matches found according to the 'isexpand'
---- option. Each match is represented as a List containing
+--- Searches backward from the given position and returns a List
+--- of matches according to the 'isexpand' option. When no
+--- arguments are provided, uses the current cursor position.
+---
+--- 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
@@ -1122,39 +1125,34 @@ function vim.fn.complete_info(what) end
--- 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: >
+--- Examples: >vim
--- 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
+--- 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>>
----
---- @param lnum? integer?
---- @param col? integer?
+--- @param lnum? integer
+--- @param col? integer
--- @return table
function vim.fn.complete_match(lnum, col) end
diff --git 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.
@@ -1102,6 +1102,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("$", "\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/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
@@ -558,7 +558,7 @@ struct file_buffer {
char *b_p_fo; ///< 'formatoptions'
char *b_p_flp; ///< 'formatlistpat'
int b_p_inf; ///< 'infercase'
- char *b_p_ise; ///< 'isexpand'
+ char *b_p_ise; ///< 'isexpand' local value
char *b_p_isk; ///< 'iskeyword'
char *b_p_def; ///< 'define' local value
char *b_p_inc; ///< 'include'
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
@@ -1481,8 +1481,11 @@ M.funcs = {
args = { 0, 2 },
base = 0,
desc = [=[
- Returns a List of matches found according to the 'isexpand'
- option. Each match is represented as a List containing
+ Searches backward from the given position and returns a List
+ of matches according to the 'isexpand' option. When no
+ arguments are provided, uses the current cursor position.
+
+ 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
@@ -1494,39 +1497,34 @@ M.funcs = {
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: >
+ Examples: >vim
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
+ 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>>
+ <
]=],
name = 'complete_match',
- params = { { 'lnum', 'integer?' }, { 'col', 'integer?' } },
+ params = { { 'lnum', 'integer' }, { 'col', 'integer' } },
returns = 'table',
signature = 'complete_match([{lnum}, {col}])',
},
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
@@ -3082,24 +3082,18 @@ void f_complete_check(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
/// 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 *str, int pos)
+static void add_match_to_list(typval_T *rettv, char *str, int pos)
{
- list_T *match = tv_list_alloc(kListLenMayKnow);
- if (match == NULL) {
- return FAIL;
- }
-
+ list_T *match = tv_list_alloc(2);
tv_list_append_number(match, pos + 1);
tv_list_append_string(match, str, -1);
tv_list_append_list(rettv->vval.v_list, match);
- return OK;
}
/// "complete_match()" function
void f_complete_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- tv_list_alloc_ret(rettv, kListLenUnknown);
+ tv_list_alloc_ret(rettv, kListLenUnknown);
char *ise = curbuf->b_p_ise[0] != NUL ? curbuf->b_p_ise : p_ise;
@@ -3131,9 +3125,6 @@ void f_complete_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
char *before_cursor = xstrnsave(line, (size_t)col);
- if (before_cursor == NULL) {
- return;
- }
if (ise == NULL || *ise == NUL) {
regmatch_T regmatch;
@@ -3141,19 +3132,9 @@ void f_complete_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (regmatch.regprog != NULL) {
if (vim_regexec_nl(®match, before_cursor, (colnr_T)0)) {
char *trig = xstrnsave(regmatch.startp[0], (size_t)(regmatch.endp[0] - regmatch.startp[0]));
- if (trig == NULL) {
- xfree(before_cursor);
- return;
- }
-
int bytepos = (int)(regmatch.startp[0] - before_cursor);
- int ret = add_match_to_list(rettv, trig, bytepos);
+ add_match_to_list(rettv, trig, bytepos);
xfree(trig);
- if (ret == FAIL) {
- xfree(before_cursor);
- vim_regfree(regmatch.regprog);
- return;
- }
}
vim_regfree(regmatch.regprog);
}
@@ -3166,10 +3147,7 @@ void f_complete_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (len > 0 && (int)len <= col) {
if (strncmp(cur_end - len, part, len) == 0) {
int bytepos = col - (int)len;
- if (add_match_to_list(rettv, part, bytepos) == FAIL) {
- xfree(before_cursor);
- return;
- }
+ add_match_to_list(rettv, part, bytepos);
}
}
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
@@ -4460,10 +4460,10 @@ void *get_varp_scope_from(vimoption_T *p, int opt_flags, buf_T *buf, win_T *win)
return &(buf->b_p_def);
case kOptInclude:
return &(buf->b_p_inc);
- case kOptIsexpand:
- return &(buf->b_p_ise);
case kOptCompleteopt:
return &(buf->b_p_cot);
+ case kOptIsexpand:
+ return &(buf->b_p_ise);
case kOptDictionary:
return &(buf->b_p_dict);
case kOptThesaurus:
@@ -4547,10 +4547,10 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return *buf->b_p_def != NUL ? &(buf->b_p_def) : p->var;
case kOptInclude:
return *buf->b_p_inc != NUL ? &(buf->b_p_inc) : p->var;
- case kOptIsexpand:
- return *buf->b_p_ise != NUL ? &(buf->b_p_ise) : p->var;
case kOptCompleteopt:
return *buf->b_p_cot != NUL ? &(buf->b_p_cot) : p->var;
+ case kOptIsexpand:
+ return *buf->b_p_ise != NUL ? &(buf->b_p_ise) : p->var;
case kOptDictionary:
return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var;
case kOptThesaurus:
@@ -5242,6 +5242,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_cot_flags = 0;
buf->b_p_dict = empty_string_option;
buf->b_p_tsr = empty_string_option;
+ buf->b_p_ise = empty_string_option;
buf->b_p_tsrfu = empty_string_option;
buf->b_p_qe = xstrdup(p_qe);
COPY_OPT_SCTX(buf, kBufOptQuoteescape);
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
@@ -374,9 +374,9 @@ EXTERN int p_is; ///< 'incsearch'
EXTERN char *p_inde; ///< 'indentexpr'
EXTERN char *p_indk; ///< 'indentkeys'
EXTERN char *p_icm; ///< 'inccommand'
+EXTERN char *p_ise; ///< 'isexpand'
EXTERN char *p_isf; ///< 'isfname'
EXTERN char *p_isi; ///< 'isident'
-EXTERN char *p_ise; ///< 'isexpand'
EXTERN char *p_isk; ///< 'iskeyword'
EXTERN char *p_isp; ///< 'isprint'
EXTERN int p_js; ///< 'joinspaces'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
@@ -4632,7 +4632,7 @@ local options = {
abbreviation = 'ise',
cb = 'did_set_isexpand',
defaults = '',
- deny_duplicates = false,
+ deny_duplicates = true,
desc = [=[
Defines characters and patterns for completion in insert mode. Used by
the |complete_match()| function to determine the starting position for
@@ -4643,6 +4643,10 @@ local options = {
Note: Use "\\," to add a literal comma as trigger character, see
|option-backslash|.
+
+ Examples: >vim
+ set isexpand=.,->,/*,\\,
+ <
]=],
full_name = 'isexpand',
list = 'onecomma',
diff --git a/test/old/testdir/gen_opt_test.vim b/test/old/testdir/gen_opt_test.vim
@@ -261,6 +261,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']],