# HG changeset patch # User Bram Moolenaar # Date 1634395504 -7200 # Node ID 416237f1de227ec65204258ac7ff263da924f5bd # Parent 3b34837f45389cfa87b6d0a0902415f70b957b77 patch 8.2.3520: cannot define a function for thesaurus completion Commit: https://github.com/vim/vim/commit/160e994d768d03a3c826b58115cde94df8fce607 Author: Yegappan Lakshmanan 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) diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt --- 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. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- 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 diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt --- 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 diff --git a/src/buffer.c b/src/buffer.c --- 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); diff --git a/src/insexpand.c b/src/insexpand.c --- 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 diff --git a/src/option.c b/src/option.c --- 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); diff --git a/src/option.h b/src/option.h --- 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 diff --git a/src/optiondefs.h b/src/optiondefs.h --- 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}, diff --git a/src/optionstr.c b/src/optionstr.c --- 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); diff --git a/src/structs.h b/src/structs.h --- 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' diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim --- 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\\\\\", 'tnix') + call assert_equal(['an amiable', ''], getline(1, '$')) + set thesaurusfunc=NonExistingFunc + call assert_fails("normal $a\\", 'E117:') + set thesaurusfunc& + %bw! +endfunc + func Test_edit_CTRL_U() " Test 'completefunc' new diff --git a/src/version.c b/src/version.c --- 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,