From 2993ac5fce5450428322ce43aaa5e643e6994443 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Feb 2018 14:12:43 +0100 Subject: [PATCH 1/4] patch 8.0.1490: number of spell regions is spread out through the code Problem: Number of spell regions is spread out through the code. Solution: Define MAXREGIONS. --- src/spell.h | 5 ++++- src/spellfile.c | 19 ++++++++++--------- src/version.c | 2 ++ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/spell.h b/src/spell.h index 4c44b546a8..49ec0601e9 100644 --- a/src/spell.h +++ b/src/spell.h @@ -34,6 +34,8 @@ byte, thus it can't be above 255. Must be >= PFD_NOTSPECIAL. */ +#define MAXREGIONS 8 /* Number of regions supported. */ + /* Type used for indexes in the word tree need to be at least 4 bytes. If int * is 8 bytes we could use something smaller, but what? */ #if VIM_SIZEOF_INT > 3 @@ -80,7 +82,8 @@ struct slang_S char_u *sl_info; /* infotext string or NULL */ - char_u sl_regions[17]; /* table with up to 8 region names plus NUL */ + char_u sl_regions[MAXREGIONS * 2 + 1]; + /* table with up to 8 region names plus NUL */ char_u *sl_midword; /* MIDWORD string or NULL */ diff --git a/src/spellfile.c b/src/spellfile.c index f6d7a6432f..e5cc84164b 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -54,8 +54,8 @@ * website, etc) * * sectionID == SN_REGION: ... - * 2 bytes Up to 8 region names: ca, au, etc. Lower case. - * First is region 1. + * 2 bytes Up to MAXREGIONS region names: ca, au, etc. Lower + * case. First is region 1. * * sectionID == SN_CHARFLAGS: * @@ -832,7 +832,7 @@ read_region_section(FILE *fd, slang_T *lp, int len) { int i; - if (len > 16) + if (len > MAXREGIONS * 2) return SP_FORMERROR; for (i = 0; i < len; ++i) lp->sl_regions[i] = getc(fd); /* */ @@ -1952,8 +1952,9 @@ typedef struct spellinfo_S char_u *si_info; /* info text chars or NULL */ int si_region_count; /* number of regions supported (1 when there are no regions) */ - char_u si_region_name[17]; /* region names; used only if - * si_region_count > 1) */ + char_u si_region_name[MAXREGIONS * 2 + 1]; + /* region names; used only if + * si_region_count > 1) */ garray_T si_rep; /* list of fromto_T entries from REP lines */ garray_T si_repsal; /* list of fromto_T entries from REPSAL lines */ @@ -4234,7 +4235,7 @@ spell_read_wordfile(spellinfo_T *spin, char_u *fname) else { line += 8; - if (STRLEN(line) > 16) + if (STRLEN(line) > MAXREGIONS * 2) smsg((char_u *)_("Too many regions in %s line %d: %s"), fname, lnum, line); else @@ -5954,7 +5955,7 @@ mkspell( char_u *wfname; char_u **innames; int incount; - afffile_T *(afile[8]); + afffile_T *(afile[MAXREGIONS]); int i; int len; stat_T st; @@ -6025,8 +6026,8 @@ mkspell( EMSG(_(e_invarg)); /* need at least output and input names */ else if (vim_strchr(gettail(wfname), '_') != NULL) EMSG(_("E751: Output file name must not have region name")); - else if (incount > 8) - EMSG(_("E754: Only up to 8 regions supported")); + else if (incount > MAXREGIONS) + EMSGN(_("E754: Only up to %ld regions supported"), MAXREGIONS); else { /* Check for overwriting before doing things that may take a lot of diff --git a/src/version.c b/src/version.c index ccc10668ae..0a54751895 100644 --- a/src/version.c +++ b/src/version.c @@ -771,6 +771,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1490, /**/ 1489, /**/ From a8f04aa275984183bab5bb583b128f38c64abb69 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Feb 2018 15:36:55 +0100 Subject: [PATCH 2/4] patch 8.0.1491: the minimum width of the popup menu is hard coded Problem: The minimum width of the popup menu is hard coded. Solution: Add the 'pumwidth' option. (Christian Brabandt, James McCoy, closes #2314) --- runtime/doc/options.txt | 10 +++++ src/option.c | 7 ++++ src/option.h | 1 + src/popupmnu.c | 86 +++++++++++++++++++++++++++++++++++++---- src/version.c | 2 + 5 files changed, 98 insertions(+), 8 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 87b7bbc500..b260d7e11d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5953,6 +5953,16 @@ A jump table for the options with a short description can be found at |Q_op|. {not in Vi} Determines the maximum number of items to show in the popup menu for Insert mode completion. When zero as much space as available is used. + |ins-completion-menu|. + + *'pumwidth'* *'pw'* +'pumwidth' 'pw' number (default 0) + global + {not available when compiled without the + |+insert_expand| feature} + {not in Vi} + Determines the minium width to use for the popup menu for Insert mode + completion. When zero the default of 15 screen cells is used. |ins-completion-menu|. *'pythondll'* diff --git a/src/option.c b/src/option.c index 4898e694c5..5e215d52d5 100644 --- a/src/option.c +++ b/src/option.c @@ -2237,6 +2237,13 @@ static struct vimoption options[] = (char_u *)&p_ph, PV_NONE, #else (char_u *)NULL, PV_NONE, +#endif + {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, + {"pumwidth", "pw", P_NUM|P_VI_DEF, +#ifdef FEAT_INS_EXPAND + (char_u *)&p_pw, PV_NONE, +#else + (char_u *)NULL, PV_NONE, #endif {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, {"pythonthreedll", NULL, P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, diff --git a/src/option.h b/src/option.h index 21be3a39f1..5ced8fecd3 100644 --- a/src/option.h +++ b/src/option.h @@ -424,6 +424,7 @@ EXTERN int p_cp; /* 'compatible' */ #ifdef FEAT_INS_EXPAND EXTERN char_u *p_cot; /* 'completeopt' */ EXTERN long p_ph; /* 'pumheight' */ +EXTERN long p_pw; /* 'pumwidth' */ #endif EXTERN char_u *p_cpo; /* 'cpoptions' */ #ifdef FEAT_CSCOPE diff --git a/src/popupmnu.c b/src/popupmnu.c index 447f789e59..f53649f123 100644 --- a/src/popupmnu.c +++ b/src/popupmnu.c @@ -66,6 +66,15 @@ pum_compute_size(void) } } +/* + * Return the minimum width of the popup menu. + */ + static int +pum_get_width(void) +{ + return p_pw == 0 ? PUM_DEF_WIDTH : p_pw; +} + /* * Show the popup menu with items "array[size]". * "array" must remain valid until pum_undisplay() is called! @@ -93,7 +102,7 @@ pum_display( do { - def_width = PUM_DEF_WIDTH; + def_width = pum_get_width(); above_row = 0; below_row = cmdline_row; @@ -216,16 +225,17 @@ pum_display( if (def_width < max_width) def_width = max_width; - if (((col < Columns - PUM_DEF_WIDTH || col < Columns - max_width) + if (((col < Columns - pum_get_width() || col < Columns - max_width) #ifdef FEAT_RIGHTLEFT && !curwin->w_p_rl) - || (curwin->w_p_rl && (col > PUM_DEF_WIDTH || col > max_width) + || (curwin->w_p_rl && (col > pum_get_width() || col > max_width) #endif )) { /* align pum column with "col" */ pum_col = col; + /* start with the maximum space available */ #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) pum_width = pum_col - pum_scrollbar + 1; @@ -234,12 +244,71 @@ pum_display( pum_width = Columns - pum_col - pum_scrollbar; if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 - && pum_width > PUM_DEF_WIDTH) + && pum_width > pum_get_width()) { + /* the width is too much, make it narrower */ pum_width = max_width + pum_kind_width + pum_extra_width + 1; - if (pum_width < PUM_DEF_WIDTH) - pum_width = PUM_DEF_WIDTH; + if (pum_width < pum_get_width()) + pum_width = pum_get_width(); } + else if (((col > pum_get_width() || col > max_width) +#ifdef FEAT_RIGHTLEFT + && !curwin->w_p_rl) + || (curwin->w_p_rl && (col < Columns - pum_get_width() + || col < Columns - max_width) +#endif + )) + { + /* align right pum edge with "col" */ +#ifdef FEAT_RIGHTLEFT + if (curwin->w_p_rl) + { + pum_col = col + max_width + pum_scrollbar + 1; + if (pum_col >= Columns) + pum_col = Columns - 1; + } + else +#endif + { + pum_col = col - max_width - pum_scrollbar; + if (pum_col < 0) + pum_col = 0; + } + +#ifdef FEAT_RIGHTLEFT + if (curwin->w_p_rl) + pum_width = W_ENDCOL(curwin) - pum_col - pum_scrollbar + 1; + else +#endif + pum_width = pum_col - pum_scrollbar; + + if (pum_width < pum_get_width()) + { + pum_width = pum_get_width(); +#ifdef FEAT_RIGHTLEFT + if (curwin->w_p_rl) + { + if (pum_width > pum_col) + pum_width = pum_col; + } + else +#endif + { + if (pum_width >= Columns - pum_col) + pum_width = Columns - pum_col - 1; + } + } + else if (pum_width > max_width + pum_kind_width + + pum_extra_width + 1 + && pum_width > pum_get_width()) + { + pum_width = max_width + pum_kind_width + + pum_extra_width + 1; + if (pum_width < pum_get_width()) + pum_width = pum_get_width(); + } + } + } else if (Columns < def_width) { @@ -254,8 +323,8 @@ pum_display( } else { - if (max_width > PUM_DEF_WIDTH) - max_width = PUM_DEF_WIDTH; /* truncate */ + if (max_width > pum_get_width()) + max_width = pum_get_width(); /* truncate */ #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) pum_col = max_width - 1; @@ -1005,4 +1074,5 @@ ui_may_remove_balloon(void) ui_remove_balloon(); } # endif + #endif diff --git a/src/version.c b/src/version.c index 0a54751895..1bd029555a 100644 --- a/src/version.c +++ b/src/version.c @@ -771,6 +771,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1491, /**/ 1490, /**/ From b301f6b950975b9d7ae87a4f551b32bba63ccdcf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Feb 2018 15:38:35 +0100 Subject: [PATCH 3/4] patch 8.0.1492: memory leak in balloon_split() Problem: Memory leak in balloon_split(). Solution: Free the balloon lines. Free the balloon when exiting. --- src/evalfunc.c | 2 ++ src/misc2.c | 4 ++++ src/version.c | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/evalfunc.c b/src/evalfunc.c index 632b982de0..4b34538b7b 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1447,6 +1447,8 @@ f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED) /* Skip the first and last item, they are always empty. */ for (i = 1; i < size - 1; ++i) list_append_string(rettv->vval.v_list, array[i].pum_text, -1); + while (size > 0) + vim_free(array[--size].pum_text); vim_free(array); } } diff --git a/src/misc2.c b/src/misc2.c index 368303ed9e..0deccdf509 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -1116,6 +1116,10 @@ free_all_mem(void) spell_free_all(); # endif +#if defined(FEAT_INS_EXPAND) && defined(FEAT_BEVAL_TERM) + ui_remove_balloon(); +# endif + # if defined(FEAT_USR_CMDS) /* Clear user commands (before deleting buffers). */ ex_comclear(NULL); diff --git a/src/version.c b/src/version.c index 1bd029555a..be5f864ecf 100644 --- a/src/version.c +++ b/src/version.c @@ -771,6 +771,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1492, /**/ 1491, /**/ From 9b56a57cdae31f7a2c85d440392bf63d3253a158 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Feb 2018 16:19:32 +0100 Subject: [PATCH 4/4] patch 8.0.1493: completion items cannot be annotated Problem: Completion items cannot be annotated. Solution: Add a "user_data" entry to the completion item. (Ben Jackson, coses #2608, closes #2508) --- runtime/doc/insert.txt | 6 +- src/edit.c | 4 + src/structs.h | 11 +-- src/testdir/test_ins_complete.vim | 120 ++++++++++++++++++++++++++++++ src/version.c | 2 + 5 files changed, 137 insertions(+), 6 deletions(-) diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 73719113ff..2f485102bb 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1,4 +1,4 @@ -*insert.txt* For Vim version 8.0. Last change: 2018 Jan 26 +*insert.txt* For Vim version 8.0. Last change: 2018 Feb 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1103,6 +1103,8 @@ items: item with the same word is already present. empty when non-zero this match will be added even when it is an empty string + user_data custom data which is associated with the item and + available in |v:completed_item| All of these except "icase", "dup" and "empty" must be a string. If an item does not meet these requirements then an error message is given and further @@ -1196,6 +1198,8 @@ The menu is used when: The 'pumheight' option can be used to set a maximum height. The default is to use all space available. +The 'pumwidth' option can be used to set a minimum width. The default is 15 +characters. There are three states: 1. A complete match has been inserted, e.g., after using CTRL-N or CTRL-P. diff --git a/src/edit.c b/src/edit.c index fa1d84bbc7..a9e6343a80 100644 --- a/src/edit.c +++ b/src/edit.c @@ -4236,6 +4236,8 @@ ins_compl_add_tv(typval_T *tv, int dir) (char_u *)"kind", FALSE); cptext[CPT_INFO] = get_dict_string(tv->vval.v_dict, (char_u *)"info", FALSE); + cptext[CPT_USER_DATA] = get_dict_string(tv->vval.v_dict, + (char_u *)"user_data", FALSE); if (get_dict_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL) icase = get_dict_number(tv->vval.v_dict, (char_u *)"icase"); if (get_dict_string(tv->vval.v_dict, (char_u *)"dup", FALSE) != NULL) @@ -4758,6 +4760,8 @@ ins_compl_insert(int in_compl_func) EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); dict_add_nr_str(dict, "info", 0L, EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); + dict_add_nr_str(dict, "user_data", 0L, + EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_USER_DATA])); } set_vim_var_dict(VV_COMPLETED_ITEM, dict); if (!in_compl_func) diff --git a/src/structs.h b/src/structs.h index 6aaccc5119..abfb73176f 100644 --- a/src/structs.h +++ b/src/structs.h @@ -3240,11 +3240,12 @@ typedef struct /* * Array indexes used for cptext argument of ins_compl_add(). */ -#define CPT_ABBR 0 /* "abbr" */ -#define CPT_MENU 1 /* "menu" */ -#define CPT_KIND 2 /* "kind" */ -#define CPT_INFO 3 /* "info" */ -#define CPT_COUNT 4 /* Number of entries */ +#define CPT_ABBR 0 /* "abbr" */ +#define CPT_MENU 1 /* "menu" */ +#define CPT_KIND 2 /* "kind" */ +#define CPT_INFO 3 /* "info" */ +#define CPT_USER_DATA 4 /* "user data" */ +#define CPT_COUNT 5 /* Number of entries */ typedef struct { UINT32_T total[2]; diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index 652b1d9e42..020e228820 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -117,6 +117,126 @@ func Test_omni_dash() set omnifunc= endfunc +function! s:CompleteDone_CompleteFuncDict( findstart, base ) + if a:findstart + return 0 + endif + + return { + \ 'words': [ + \ { + \ 'word': 'aword', + \ 'abbr': 'wrd', + \ 'menu': 'extra text', + \ 'info': 'words are cool', + \ 'kind': 'W', + \ 'user_data': 'test' + \ } + \ ] + \ } +endfunction + +function! s:CompleteDone_CheckCompletedItemDict() + call assert_equal( 'aword', v:completed_item[ 'word' ] ) + call assert_equal( 'wrd', v:completed_item[ 'abbr' ] ) + call assert_equal( 'extra text', v:completed_item[ 'menu' ] ) + call assert_equal( 'words are cool', v:completed_item[ 'info' ] ) + call assert_equal( 'W', v:completed_item[ 'kind' ] ) + call assert_equal( 'test', v:completed_item[ 'user_data' ] ) + + let s:called_completedone = 1 +endfunction + +function Test_CompleteDoneDict() + au CompleteDone * :call CompleteDone_CheckCompletedItemDict() + + set completefunc=CompleteDone_CompleteFuncDict + execute "normal a\\\" + set completefunc& + + call assert_equal( 'test', v:completed_item[ 'user_data' ] ) + call assert_true( s:called_completedone ) + + let s:called_completedone = 0 + au! CompleteDone +endfunc + +function! s:CompleteDone_CompleteFuncDictNoUserData( findstart, base ) + if a:findstart + return 0 + endif + + return { + \ 'words': [ + \ { + \ 'word': 'aword', + \ 'abbr': 'wrd', + \ 'menu': 'extra text', + \ 'info': 'words are cool', + \ 'kind': 'W' + \ } + \ ] + \ } +endfunction + +function! s:CompleteDone_CheckCompletedItemDictNoUserData() + call assert_equal( 'aword', v:completed_item[ 'word' ] ) + call assert_equal( 'wrd', v:completed_item[ 'abbr' ] ) + call assert_equal( 'extra text', v:completed_item[ 'menu' ] ) + call assert_equal( 'words are cool', v:completed_item[ 'info' ] ) + call assert_equal( 'W', v:completed_item[ 'kind' ] ) + call assert_equal( '', v:completed_item[ 'user_data' ] ) + + let s:called_completedone = 1 +endfunction + +function Test_CompleteDoneDictNoUserData() + au CompleteDone * :call CompleteDone_CheckCompletedItemDictNoUserData() + + set completefunc=CompleteDone_CompleteFuncDictNoUserData + execute "normal a\\\" + set completefunc& + + call assert_equal( '', v:completed_item[ 'user_data' ] ) + call assert_true( s:called_completedone ) + + let s:called_completedone = 0 + au! CompleteDone +endfunc + +function! s:CompleteDone_CompleteFuncList( findstart, base ) + if a:findstart + return 0 + endif + + return [ 'aword' ] +endfunction + +function! s:CompleteDone_CheckCompletedItemList() + call assert_equal( 'aword', v:completed_item[ 'word' ] ) + call assert_equal( '', v:completed_item[ 'abbr' ] ) + call assert_equal( '', v:completed_item[ 'menu' ] ) + call assert_equal( '', v:completed_item[ 'info' ] ) + call assert_equal( '', v:completed_item[ 'kind' ] ) + call assert_equal( '', v:completed_item[ 'user_data' ] ) + + let s:called_completedone = 1 +endfunction + +function Test_CompleteDoneList() + au CompleteDone * :call CompleteDone_CheckCompletedItemList() + + set completefunc=CompleteDone_CompleteFuncList + execute "normal a\\\" + set completefunc& + + call assert_equal( '', v:completed_item[ 'user_data' ] ) + call assert_true( s:called_completedone ) + + let s:called_completedone = 0 + au! CompleteDone +endfunc + " Check that when using feedkeys() typeahead does not interrupt searching for " completions. func Test_compl_feedkeys() diff --git a/src/version.c b/src/version.c index be5f864ecf..261e3d7c69 100644 --- a/src/version.c +++ b/src/version.c @@ -771,6 +771,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1493, /**/ 1492, /**/