vim-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 vim/vim#8987,
            closes 8950)
160e994d76
This commit is contained in:
Matěj Cepl 2021-10-16 18:40:57 +02:00
parent aa4f0879e3
commit cbec765915
No known key found for this signature in database
GPG Key ID: 79205802880BC9D8
10 changed files with 125 additions and 30 deletions

View File

@ -822,6 +822,12 @@ CTRL-X CTRL-T Works as CTRL-X CTRL-K, but in a special way. It uses
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.
@ -1032,7 +1038,7 @@ CTRL-X CTRL-Z Stop completion without changing the text.
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.

View File

@ -6459,6 +6459,16 @@ A jump table for the options with a short description can be found at |Q_op|.
uses another default.
Backticks cannot be used in this option for security reasons.
*'thesaurusfunc'* *'tsrfu'*
'thesaurusfunc' 'tsrfu' string (default: empty)
local to buffer
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

View File

@ -895,6 +895,7 @@ Short explanation of each option: *option-list*
'terse' shorten some messages
'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

View File

@ -1967,6 +1967,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_cpt);
clear_string_option(&buf->b_p_cfu);
clear_string_option(&buf->b_p_ofu);
clear_string_option(&buf->b_p_thsfu);
clear_string_option(&buf->b_p_gp);
clear_string_option(&buf->b_p_mp);
clear_string_option(&buf->b_p_efm);

View File

@ -698,6 +698,7 @@ struct file_buffer {
#endif
char_u *b_p_cfu; ///< 'completefunc'
char_u *b_p_ofu; ///< 'omnifunc'
char_u *b_p_thsfu; ///< 'thesaurusfunc'
char_u *b_p_tfu; ///< 'tagfunc'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'

View File

@ -2071,7 +2071,7 @@ static bool check_compl_option(bool dict_opt)
{
if (dict_opt
? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell)
: (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) {
: (*curbuf->b_p_tsr == NUL && *p_tsr == NUL && *curbuf->b_p_thsfu == NUL)) {
ctrl_x_mode = CTRL_X_NORMAL;
edit_submode = NULL;
msg_attr((dict_opt
@ -2544,7 +2544,7 @@ static void ins_compl_longest_match(compl_T *match)
* Add an array of matches to the list of matches.
* Frees matches[].
*/
static void ins_compl_add_matches(int num_matches, char_u **matches, int icase)
static void ins_compl_add_matches(int num_matches, char_u * *matches, int icase)
FUNC_ATTR_NONNULL_ALL
{
int add_r = OK;
@ -2899,7 +2899,7 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i
char_u *ptr;
char_u *buf;
regmatch_T regmatch;
char_u **files;
char_u * *files;
int count;
int save_p_scs;
Direction dir = compl_direction;
@ -2990,7 +2990,7 @@ theend:
xfree(buf);
}
static void ins_compl_files(int count, char_u **files, int thesaurus, int flags,
static void ins_compl_files(int count, char_u * *files, int thesaurus, int flags,
regmatch_T *regmatch, char_u *buf, Direction *dir)
FUNC_ATTR_NONNULL_ARG(2, 7)
{
@ -3924,6 +3924,20 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
}
/// 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".
///
@ -3940,7 +3954,7 @@ static void expand_by_function(int type, char_u *base)
const int save_State = State;
assert(curbuf != NULL);
funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu;
funcname = get_complete_funcname(type);
if (*funcname == NUL) {
return;
}
@ -4096,7 +4110,15 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
return FAIL;
}
return ins_compl_add((char_u *)word, -1, NULL,
(char_u **)cptext, true, &user_data, dir, flags, dup);
(char_u * *)cptext, true, &user_data, dir, flags, dup);
}
/// Returns TRUE when using a user-defined function for thesaurus completion.
static int thesaurus_func_complete(int type)
{
return (type == CTRL_X_THESAURUS
&& curbuf->b_p_thsfu != NULL
&& *curbuf->b_p_thsfu != NUL);
}
// Get the next expansion(s), using "compl_pattern".
@ -4116,7 +4138,7 @@ static int ins_compl_get_exp(pos_T *ini)
static buf_T *ins_buf = NULL; // buffer being scanned
pos_T *pos;
char_u **matches;
char_u * *matches;
int save_p_scs;
bool save_p_ws;
int save_p_ic;
@ -4272,17 +4294,17 @@ static int ins_compl_get_exp(pos_T *ini)
case CTRL_X_DICTIONARY:
case CTRL_X_THESAURUS:
ins_compl_dictionaries(dict != NULL ? dict
: (type == CTRL_X_THESAURUS
? (*curbuf->b_p_tsr == NUL
? p_tsr
: curbuf->b_p_tsr)
: (*curbuf->b_p_dict == NUL
? p_dict
: curbuf->b_p_dict)),
compl_pattern,
dict != NULL ? dict_f
: 0, type == CTRL_X_THESAURUS);
if (thesaurus_func_complete(type)) {
expand_by_function(type, compl_pattern);
} else {
ins_compl_dictionaries(dict != NULL ? dict
: (type == CTRL_X_THESAURUS
? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
: (*curbuf->b_p_dict ==
NUL ? p_dict : curbuf->b_p_dict)),
compl_pattern,
dict != NULL ? dict_f : 0, type == CTRL_X_THESAURUS);
}
dict = NULL;
break;
@ -5095,7 +5117,9 @@ static int ins_complete(int c, bool 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) {
if (!(compl_cont_status & CONT_ADDING)) {
@ -5206,22 +5230,18 @@ static int ins_complete(int c, bool 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) {
/*
* Call user defined function 'completefunc' with "a:findstart"
* set to 1 to obtain the length of text to use for completion.
*/
} else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
|| thesaurus_func_complete(ctrl_x_mode)) {
// Call user defined function 'completefunc' with "a:findstart"
// set to 1 to obtain the length of text to use for completion.
char_u *funcname;
pos_T pos;
win_T *curwin_save;
buf_T *curbuf_save;
const int save_State = State;
/* 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;
// Call 'completefunc' or 'omnifunc' and get pattern length as a string
funcname = get_complete_funcname(ctrl_x_mode);
if (*funcname == NUL) {
EMSG2(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION
? "completefunc" : "omnifunc");

View File

@ -139,6 +139,7 @@ static char_u *p_cpt;
static char_u *p_cfu;
static char_u *p_ofu;
static char_u *p_tfu;
static char_u *p_thsfu;
static int p_eol;
static int p_fixeol;
static int p_et;
@ -5915,6 +5916,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curbuf->b_p_sw);
case PV_TFU:
return (char_u *)&(curbuf->b_p_tfu);
case PV_THSFU:
return (char_u *)&(curbuf->b_p_thsfu);
case PV_TS:
return (char_u *)&(curbuf->b_p_ts);
case PV_TW:
@ -6184,6 +6187,7 @@ void buf_copy_options(buf_T *buf, int flags)
#endif
buf->b_p_cfu = vim_strsave(p_cfu);
buf->b_p_ofu = vim_strsave(p_ofu);
buf->b_p_thsfu = vim_strsave(p_thsfu);
buf->b_p_tfu = vim_strsave(p_tfu);
buf->b_p_sts = p_sts;
buf->b_p_sts_nopaste = p_sts_nopaste;

View File

@ -826,6 +826,7 @@ enum {
BV_SW,
BV_SWF,
BV_TFU,
BV_THSFU,
BV_TAGS,
BV_TC,
BV_TS,

View File

@ -2535,6 +2535,15 @@ return {
varname='p_tsr',
defaults={if_true=""}
},
{
full_name='thesaurusfunc', abbreviation='tsrfu',
short_desc=N_("function used for thesaurus completion"),
type='string', scope={'buffer'},
secure=true,
alloced=true,
varname='p_thsfu',
defaults={if_true=""}
},
{
full_name='tildeop', abbreviation='top',
short_desc=N_("tilde command \"~\" behaves like an operator"),

View File

@ -870,6 +870,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