changeset 25974:416237f1de22 v8.2.3520

patch 8.2.3520: cannot define a function for thesaurus completion Commit: https://github.com/vim/vim/commit/160e994d768d03a3c826b58115cde94df8fce607 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sat Oct 16 15:41:29 2021 +0100 patch 8.2.3520: cannot define a function for thesaurus completion Problem: Cannot define a function for thesaurus completion. Solution: Add 'thesaurusfunc'. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/8987, closes 8950)
author Bram Moolenaar <Bram@vim.org>
date Sat, 16 Oct 2021 16:45:04 +0200
parents 3b34837f4538
children 11cfbeb809d3
files runtime/doc/insert.txt runtime/doc/options.txt runtime/doc/quickref.txt src/buffer.c src/insexpand.c src/option.c src/option.h src/optiondefs.h src/optionstr.c src/structs.h src/testdir/test_edit.vim src/version.c
diffstat 12 files changed, 138 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -842,6 +842,12 @@ CTRL-X CTRL-T		Works as CTRL-X CTRL-K, b
 			Other uses include translation between two languages,
 			or grouping API functions by keyword.
 
+			If the 'thesaurusfunc' option is set, then the user
+			specified function is invoked to get the list of
+			completion matches and the 'thesaurus' option is not
+			used. See |complete-functions| for an explanation of
+			how the function is invoked and what it should return.
+
 	CTRL-T	or
 	CTRL-N		Search forward for next matching keyword.  This
 			keyword replaces the previous matching keyword.
@@ -1052,7 +1058,7 @@ CTRL-X CTRL-Z		Stop completion without c
 
 FUNCTIONS FOR FINDING COMPLETIONS			*complete-functions*
 
-This applies to 'completefunc' and 'omnifunc'.
+This applies to 'completefunc', 'thesaurusfunc' and 'omnifunc'.
 
 The function is called in two different ways:
 - First the function is called to find the start of the text to be completed.
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -8028,6 +8028,18 @@ A jump table for the options with a shor
 	uses another default.
 	Backticks cannot be used in this option for security reasons.
 
+						*'thesaurusfunc'* *tsrfu'*
+'thesaurusfunc' 'tsrfu'	string	(default: empty)
+			local to buffer
+			{not available when compiled without the |+eval|
+			feature}
+	This option specifies a function to be used for thesaurus completion
+	with CTRL-X CTRL-T. |i_CTRL-X_CTRL-T|
+	See |complete-functions| for an explanation of how the function is
+	invoked and what it should return.
+	This option cannot be set from a |modeline| or in the |sandbox|, for
+	security reasons.
+
 			     *'tildeop'* *'top'* *'notildeop'* *'notop'*
 'tildeop' 'top'		boolean	(default off)
 			global
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -948,6 +948,7 @@ Short explanation of each option:		*opti
 'textmode'	  'tx'	    obsolete, use 'fileformat'
 'textwidth'	  'tw'	    maximum width of text that is being inserted
 'thesaurus'	  'tsr'     list of thesaurus files for keyword completion
+'thesaurusfunc'	  'tsrfu'   function to be used for thesaurus completion
 'tildeop'	  'top'     tilde command "~" behaves like an operator
 'timeout'	  'to'	    time out on mappings and key codes
 'timeoutlen'	  'tm'	    time out time in milliseconds
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2364,6 +2364,7 @@ free_buf_options(
 #ifdef FEAT_COMPL_FUNC
     clear_string_option(&buf->b_p_cfu);
     clear_string_option(&buf->b_p_ofu);
+    clear_string_option(&buf->b_p_thsfu);
 #endif
 #ifdef FEAT_QUICKFIX
     clear_string_option(&buf->b_p_gp);
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -299,7 +299,11 @@ has_compl_option(int dict_opt)
 							&& !curwin->w_p_spell
 #endif
 							)
-		 : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL))
+		 : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL
+#ifdef FEAT_COMPL_FUNC
+		     && *curbuf->b_p_thsfu == NUL
+#endif
+		   ))
     {
 	ctrl_x_mode = CTRL_X_NORMAL;
 	edit_submode = NULL;
@@ -2230,6 +2234,25 @@ ins_compl_next_buf(buf_T *buf, int flag)
 
 #ifdef FEAT_COMPL_FUNC
 /*
+ * Get the user-defined completion function name for completion 'type'
+ */
+    static char_u *
+get_complete_funcname(int type)
+{
+    switch (type)
+    {
+	case CTRL_X_FUNCTION:
+	    return curbuf->b_p_cfu;
+	case CTRL_X_OMNI:
+	    return curbuf->b_p_ofu;
+	case CTRL_X_THESAURUS:
+	    return curbuf->b_p_thsfu;
+	default:
+	    return (char_u *)"";
+    }
+}
+
+/*
  * Execute user defined complete function 'completefunc' or 'omnifunc', and
  * get matches in "matches".
  */
@@ -2246,7 +2269,7 @@ expand_by_function(
     typval_T	rettv;
     int		save_State = State;
 
-    funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu;
+    funcname = get_complete_funcname(type);
     if (*funcname == NUL)
 	return;
 
@@ -2721,6 +2744,21 @@ f_complete_info(typval_T *argvars, typva
 #endif
 
 /*
+ * Returns TRUE when using a user-defined function for thesaurus completion.
+ */
+    static int
+thesaurus_func_complete(int type UNUSED)
+{
+#ifdef FEAT_COMPL_FUNC
+    return (type == CTRL_X_THESAURUS
+		&& curbuf->b_p_thsfu != NULL
+		&& *curbuf->b_p_thsfu != NUL);
+#else
+    return FALSE;
+#endif
+}
+
+/*
  * Get the next expansion(s), using "compl_pattern".
  * The search starts at position "ini" in curbuf and in the direction
  * compl_direction.
@@ -2906,7 +2944,12 @@ ins_compl_get_exp(pos_T *ini)
 
 	case CTRL_X_DICTIONARY:
 	case CTRL_X_THESAURUS:
-	    ins_compl_dictionaries(
+#ifdef FEAT_COMPL_FUNC
+	    if (thesaurus_func_complete(type))
+		expand_by_function(type, compl_pattern);
+	    else
+#endif
+		ins_compl_dictionaries(
 		    dict != NULL ? dict
 			 : (type == CTRL_X_THESAURUS
 			     ? (*curbuf->b_p_tsr == NUL
@@ -3760,7 +3803,9 @@ ins_complete(int c, int enable_pum)
 	}
 
 	// Work out completion pattern and original text -- webb
-	if (ctrl_x_mode == CTRL_X_NORMAL || (ctrl_x_mode & CTRL_X_WANT_IDENT))
+	if (ctrl_x_mode == CTRL_X_NORMAL
+		|| (ctrl_x_mode & CTRL_X_WANT_IDENT
+		    && !thesaurus_func_complete(ctrl_x_mode)))
 	{
 	    if ((compl_cont_status & CONT_SOL)
 		    || ctrl_x_mode == CTRL_X_PATH_DEFINES)
@@ -3910,7 +3955,8 @@ ins_complete(int c, int enable_pum)
 		compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
 	    compl_length = curs_col - compl_col;
 	}
-	else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI)
+	else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
+		|| thesaurus_func_complete(ctrl_x_mode))
 	{
 #ifdef FEAT_COMPL_FUNC
 	    // Call user defined function 'completefunc' with "a:findstart"
@@ -3923,8 +3969,7 @@ ins_complete(int c, int enable_pum)
 
 	    // Call 'completefunc' or 'omnifunc' and get pattern length as a
 	    // string
-	    funcname = ctrl_x_mode == CTRL_X_FUNCTION
-					  ? curbuf->b_p_cfu : curbuf->b_p_ofu;
+	    funcname = get_complete_funcname(ctrl_x_mode);
 	    if (*funcname == NUL)
 	    {
 		semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION
--- a/src/option.c
+++ b/src/option.c
@@ -5433,6 +5433,7 @@ get_varp(struct vimoption *p)
 #ifdef FEAT_COMPL_FUNC
 	case PV_CFU:	return (char_u *)&(curbuf->b_p_cfu);
 	case PV_OFU:	return (char_u *)&(curbuf->b_p_ofu);
+	case PV_THSFU:	return (char_u *)&(curbuf->b_p_thsfu);
 #endif
 #ifdef FEAT_EVAL
 	case PV_TFU:	return (char_u *)&(curbuf->b_p_tfu);
@@ -5935,6 +5936,8 @@ buf_copy_options(buf_T *buf, int flags)
 	    COPY_OPT_SCTX(buf, BV_CFU);
 	    buf->b_p_ofu = vim_strsave(p_ofu);
 	    COPY_OPT_SCTX(buf, BV_OFU);
+	    buf->b_p_thsfu = vim_strsave(p_thsfu);
+	    COPY_OPT_SCTX(buf, BV_THSFU);
 #endif
 #ifdef FEAT_EVAL
 	    buf->b_p_tfu = vim_strsave(p_tfu);
--- a/src/option.h
+++ b/src/option.h
@@ -404,6 +404,7 @@ EXTERN char_u	*p_cinw;	// 'cinwords'
 #ifdef FEAT_COMPL_FUNC
 EXTERN char_u	*p_cfu;		// 'completefunc'
 EXTERN char_u	*p_ofu;		// 'omnifunc'
+EXTERN char_u	*p_thsfu;	// 'thesaurusfunc'
 #endif
 EXTERN int	p_ci;		// 'copyindent'
 #if defined(FEAT_GUI) && defined(MACOS_X)
@@ -1217,6 +1218,9 @@ enum
 #endif
     , BV_TAGS
     , BV_TC
+#ifdef FEAT_COMPL_FUNC
+    , BV_THSFU
+#endif
     , BV_TS
     , BV_TW
     , BV_TX
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -140,6 +140,9 @@
 #ifdef FEAT_EVAL
 # define PV_TFU		OPT_BUF(BV_TFU)
 #endif
+#ifdef FEAT_COMPL_FUNC
+# define PV_THSFU		OPT_BUF(BV_THSFU)
+#endif
 #define PV_TAGS		OPT_BOTH(OPT_BUF(BV_TAGS))
 #define PV_TC		OPT_BOTH(OPT_BUF(BV_TC))
 #define PV_TS		OPT_BUF(BV_TS)
@@ -2616,6 +2619,15 @@ static struct vimoption options[] =
     {"thesaurus",   "tsr",  P_STRING|P_EXPAND|P_VI_DEF|P_ONECOMMA|P_NODUP|P_NDNAME,
 			    (char_u *)&p_tsr, PV_TSR,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
+    {"thesaurusfunc", "tsrfu",  P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE,
+#ifdef FEAT_COMPL_FUNC
+			    (char_u *)&p_thsfu, PV_THSFU,
+			    {(char_u *)"", (char_u *)0L}
+#else
+			    (char_u *)NULL, PV_NONE,
+			    {(char_u *)0L, (char_u *)0L}
+#endif
+			    SCTX_INIT},
     {"tildeop",	    "top",  P_BOOL|P_VI_DEF|P_VIM,
 			    (char_u *)&p_to, PV_NONE,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -271,6 +271,7 @@ check_buf_options(buf_T *buf)
 #ifdef FEAT_COMPL_FUNC
     check_string_option(&buf->b_p_cfu);
     check_string_option(&buf->b_p_ofu);
+    check_string_option(&buf->b_p_thsfu);
 #endif
 #ifdef FEAT_EVAL
     check_string_option(&buf->b_p_tfu);
--- a/src/structs.h
+++ b/src/structs.h
@@ -2864,6 +2864,7 @@ struct file_buffer
 #ifdef FEAT_COMPL_FUNC
     char_u	*b_p_cfu;	// 'completefunc'
     char_u	*b_p_ofu;	// 'omnifunc'
+    char_u	*b_p_thsfu;	// 'thesaurusfunc'
 #endif
 #ifdef FEAT_EVAL
     char_u	*b_p_tfu;	// 'tagfunc'
--- a/src/testdir/test_edit.vim
+++ b/src/testdir/test_edit.vim
@@ -890,6 +890,48 @@ func Test_edit_CTRL_T()
   bw!
 endfunc
 
+" Test 'thesaurusfunc'
+func MyThesaurus(findstart, base)
+  let mythesaurus = [
+        \ #{word: "happy",
+        \   synonyms: "cheerful,blissful,flying high,looking good,peppy"},
+        \ #{word: "kind",
+        \   synonyms: "amiable,bleeding-heart,heart in right place"}]
+  if a:findstart
+    " locate the start of the word
+    let line = getline('.')
+    let start = col('.') - 1
+    while start > 0 && line[start - 1] =~ '\a'
+      let start -= 1
+    endwhile
+    return start
+  else
+    " find strings matching with "a:base"
+    let res = []
+    for w in mythesaurus
+      if w.word =~ '^' . a:base
+        call add(res, w.word)
+        call extend(res, split(w.synonyms, ","))
+      endif
+    endfor
+    return res
+  endif
+endfunc
+
+func Test_thesaurus_func()
+  new
+  set thesaurus=
+  set thesaurusfunc=MyThesaurus
+  call setline(1, "an ki")
+  call cursor(1, 1)
+  call feedkeys("A\<c-x>\<c-t>\<c-n>\<cr>\<esc>", 'tnix')
+  call assert_equal(['an amiable', ''], getline(1, '$'))
+  set thesaurusfunc=NonExistingFunc
+  call assert_fails("normal $a\<C-X>\<C-T>", 'E117:')
+  set thesaurusfunc&
+  %bw!
+endfunc
+
 func Test_edit_CTRL_U()
   " Test 'completefunc'
   new
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3520,
+/**/
     3519,
 /**/
     3518,