diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e1354184bf..dfc75c5fb7 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5776,7 +5776,16 @@ A jump table for the options with a short description can be found at |Q_op|. {not available when compiled without the |+windows| or |+quickfix| features} Default height for a preview window. Used for |:ptag| and associated - commands. Used for |CTRL-W_}| when no count is given. + commands. Used for |CTRL-W_}| when no count is given. Not used when + 'previewpopup' is set. + + *'previewpopup'* *'pvp'* +'previewpopup' 'pvp' string (default empty) + global + {not available when compiled without the |+windows|, + |+textprop| or |+quickfix| feature} + When not empty a popup window is used for commands that would open a + preview window. See |preview-popup|. *'previewwindow'* *'nopreviewwindow'* *'pvw'* *'nopvw'* *E590* diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt index cbe83107de..1b39bcf679 100644 --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -303,7 +303,7 @@ popup_getoptions({id}) *popup_getoptions()* zero. When all values are one then an empty list is included. "borderhighlight" is not included when all values are empty. - "scrollbarhighlight" and "thumbhighlight" are onlu included + "scrollbarhighlight" and "thumbhighlight" are only included when set. "tabpage" will be -1 for a global popup, zero for a popup on @@ -345,7 +345,7 @@ popup_hide({id}) *popup_hide()* popup_locate({row}, {col}) *popup_locate()* - Return the |window-ID| of the popup at screen positoin {row} + Return the |window-ID| of the popup at screen position {row} and {col}. If there are multiple popups the one with the highest zindex is returned. If there are no popups at this position then zero is returned. @@ -362,6 +362,7 @@ popup_menu({what}, {options}) *popup_menu()* \ drag: 1, \ wrap: 0, \ border: [], + \ cursorline: 1, \ padding: [0,1,0,1], \ filter: 'popup_filter_menu', \ }) @@ -429,6 +430,7 @@ popup_setoptions({id}, {options}) *popup_setoptions()* callback close drag + cursorline filter firstline flip @@ -598,6 +600,11 @@ The second argument of |popup_create()| is a dictionary with options: {start} or after {end} The popup also closes if the cursor moves to another line or to another window. + cursorline non-zero: Highlight the cursor line. Also scrolls the + text to show this line (only works properly + when 'wrap' is off). + zero: Do not highlight the cursor line. + Default is zero, except for |popup_menu()|. filter A callback that can filter typed characters, see |popup-filter|. callback A callback that is called when the popup closes, e.g. @@ -695,8 +702,8 @@ If the text does not fit in the popup a scrollbar is displayed on the right of the window. This can be disabled by setting the "scrollbar" option to zero. When the scrollbar is displayed mouse scroll events, while the mouse pointer is on the popup, will cause the text to scroll up or down as you would expect. -A click in the upper halve of the scrollbar will scroll the text one line -down. A click in the lower halve wil scroll the text one line up. However, +A click in the upper half of the scrollbar will scroll the text one line +down. A click in the lower half wil scroll the text one line up. However, this is limited so that the popup does not get smaller. @@ -709,7 +716,7 @@ list has four numbers: leftmost, negative for counting from the right, -1 for rightmost endcol last column, like "col" - line start line, positive for conting from the top, 1 for top, + line start line, positive for counting from the top, 1 for top, negative for counting from the bottom, -1 for bottom endline end line, like "line" diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 2c0cdc3b0e..1c6702fc2a 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -864,7 +864,15 @@ height of the preview window when it's opened. The 'previewwindow' option is set in the preview window to be able to recognize it. The 'winfixheight' option is set to have it keep the same height when opening/closing other windows. - + *preview-popup* +Alternatively, a popup window can be used by setting the 'previewpopup' +option. When set, it overrules the 'previewwindow' and 'previewheight' +settings. The option is a comma separated list of values: + height maximum height of the popup + width maximu width of the popup +Example: > + :set previewpopup=height:10,width:60 +< *:pta* *:ptag* :pta[g][!] [tagname] Does ":tag[!] [tagname]" and shows the found tag in a diff --git a/src/beval.c b/src/beval.c index dc8966fe2f..3dd9b6770c 100644 --- a/src/beval.c +++ b/src/beval.c @@ -10,7 +10,7 @@ #include "vim.h" -#if defined(FEAT_BEVAL) || defined(FEAT_TEXT_PROP) || defined(PROT) +#if defined(FEAT_BEVAL) || defined(FEAT_TEXT_PROP) || defined(PROTO) /* * Find text under the mouse position "row" / "col". * If "getword" is TRUE the returned text in "*textp" is not the whole line but @@ -72,6 +72,7 @@ find_word_under_cursor( } col = vcol2col(wp, lnum, col); + scol = col; if (VIsual_active && wp->w_buffer == curwin->w_buffer @@ -95,6 +96,7 @@ find_word_under_cursor( lbuf = vim_strnsave(lbuf + spos->col, len); lnum = spos->lnum; col = spos->col; + scol = col; } else { diff --git a/src/change.c b/src/change.c index dd9180905b..2df71eab5b 100644 --- a/src/change.c +++ b/src/change.c @@ -155,19 +155,16 @@ changed_internal(void) static long next_listener_id = 0; /* - * Check if the change at "lnum" / "col" is above or overlaps with an existing - * changed. If above then flush changes and invoke listeners. - * If "merge" is TRUE do the merge. + * Check if the change at "lnum" is above or overlaps with an existing + * change. If above then flush changes and invoke listeners. * Returns TRUE if the change was merged. */ static int check_recorded_changes( buf_T *buf, linenr_T lnum, - colnr_T col, linenr_T lnume, - long xtra, - int merge) + long xtra) { if (buf->b_recorded_changes != NULL && xtra != 0) { @@ -182,42 +179,12 @@ check_recorded_changes( li->li_tv.vval.v_dict, (char_u *)"lnum"); prev_lnume = (linenr_T)dict_get_number( li->li_tv.vval.v_dict, (char_u *)"end"); - if (prev_lnum >= lnum || prev_lnum > lnume - || (prev_lnume >= lnum && xtra != 0)) + if (prev_lnum >= lnum || prev_lnum > lnume || prev_lnume >= lnum) { - if (li->li_next == NULL && lnum == prev_lnum - && xtra == 0 - && col + 1 == (colnr_T)dict_get_number( - li->li_tv.vval.v_dict, (char_u *)"col")) - { - if (merge) - { - dictitem_T *di; - - // Same start point and nothing is following, entries - // can be merged. - di = dict_find(li->li_tv.vval.v_dict, - (char_u *)"end", -1); - if (di != NULL) - { - prev_lnum = tv_get_number(&di->di_tv); - if (lnume > prev_lnum) - di->di_tv.vval.v_number = lnume; - } - di = dict_find(li->li_tv.vval.v_dict, - (char_u *)"added", -1); - if (di != NULL) - di->di_tv.vval.v_number += xtra; - return TRUE; - } - } - else - { - // the current change is going to make the line number in - // the older change invalid, flush now - invoke_listeners(curbuf); - break; - } + // the current change is going to make the line number in + // the older change invalid, flush now + invoke_listeners(curbuf); + break; } } } @@ -242,7 +209,7 @@ may_record_change( // If the new change is going to change the line numbers in already listed // changes, then flush. - if (check_recorded_changes(curbuf, lnum, col, lnume, xtra, TRUE)) + if (check_recorded_changes(curbuf, lnum, lnume, xtra)) return; if (curbuf->b_recorded_changes == NULL) @@ -362,7 +329,7 @@ f_listener_remove(typval_T *argvars, typval_T *rettv) void may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added) { - check_recorded_changes(buf, lnum, 0, lnume, added, FALSE); + check_recorded_changes(buf, lnum, lnume, added); } /* diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 4126fe09f9..e262da1d59 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -6270,9 +6270,20 @@ prepare_tagpreview( */ if (!curwin->w_p_pvw) { - FOR_ALL_WINDOWS(wp) - if (wp->w_p_pvw) - break; +# ifdef FEAT_TEXT_PROP + if (*p_pvp != NUL) + { + wp = popup_find_preview_window(); + if (wp != NULL) + popup_set_wantpos(wp); + } + else +# endif + { + FOR_ALL_WINDOWS(wp) + if (wp->w_p_pvw) + break; + } if (wp != NULL) win_enter(wp, undo_sync); else @@ -6280,18 +6291,21 @@ prepare_tagpreview( /* * There is no preview window open yet. Create one. */ - if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0) - == FAIL) +# ifdef FEAT_TEXT_PROP + if (*p_pvp != NUL) + return popup_create_preview_window(); +# endif + if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0) == FAIL) return FALSE; curwin->w_p_pvw = TRUE; curwin->w_p_wfh = TRUE; - RESET_BINDING(curwin); /* don't take over 'scrollbind' - and 'cursorbind' */ + RESET_BINDING(curwin); // don't take over 'scrollbind' + // and 'cursorbind' # ifdef FEAT_DIFF - curwin->w_p_diff = FALSE; /* no 'diff' */ + curwin->w_p_diff = FALSE; // no 'diff' # endif # ifdef FEAT_FOLDING - curwin->w_p_fdc = 0; /* no 'foldcolumn' */ + curwin->w_p_fdc = 0; // no 'foldcolumn' # endif return TRUE; } diff --git a/src/ex_docmd.c b/src/ex_docmd.c index a9e86fccf0..299862fd0a 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -5908,12 +5908,17 @@ ex_pclose(exarg_T *eap) { win_T *win; + // First close any normal window. FOR_ALL_WINDOWS(win) if (win->w_p_pvw) { ex_win_close(eap->forceit, win, NULL); - break; + return; } +# ifdef FEAT_TEXT_PROP + // Also when 'previewpopup' is empty, it might have been cleared. + popup_close_preview(); +# endif } #endif diff --git a/src/memline.c b/src/memline.c index af3e2f4434..0a947f9273 100644 --- a/src/memline.c +++ b/src/memline.c @@ -3566,6 +3566,15 @@ adjust_text_props_for_delete( ml_delete(linenr_T lnum, int message) { ml_flush_line(curbuf); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + return FAIL; + +#ifdef FEAT_EVAL + // When inserting above recorded changes: flush the changes before changing + // the text. + may_invoke_listeners(curbuf, lnum, lnum + 1, -1); +#endif + return ml_delete_int(curbuf, lnum, message); } @@ -3590,14 +3599,6 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message) int textprop_save_len; #endif - if (lnum < 1 || lnum > buf->b_ml.ml_line_count) - return FAIL; - -#ifdef FEAT_EVAL - // When inserting above recorded changes: flush the changes before changing - // the text. - may_invoke_listeners(buf, lnum, lnum + 1, -1); -#endif if (lowest_marked && lowest_marked > lnum) lowest_marked--; diff --git a/src/option.c b/src/option.c index 5ab8f2e19c..d3b3401ed4 100644 --- a/src/option.c +++ b/src/option.c @@ -2155,6 +2155,15 @@ static struct vimoption options[] = (char_u *)NULL, PV_NONE, #endif {(char_u *)12L, (char_u *)0L} SCTX_INIT}, + {"previewpopup", "pvp", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, +#ifdef FEAT_TEXT_PROP + (char_u *)&p_pvp, PV_NONE, + {(char_u *)"", (char_u *)0L} +#else + (char_u *)NULL, PV_NONE, + {(char_u *)NULL, (char_u *)0L} +#endif + SCTX_INIT}, {"previewwindow", "pvw", P_BOOL|P_VI_DEF|P_RSTAT|P_NOGLOB, #if defined(FEAT_QUICKFIX) (char_u *)VAR_WIN, PV_PVW, @@ -7954,6 +7963,15 @@ did_set_string_option( } #endif +#ifdef FEAT_TEXT_PROP + // 'previewpopup' + else if (varp == &p_pvp) + { + if (parse_previewpopup(NULL) == FAIL) + errmsg = e_invarg; + } +#endif + /* Options that are a list of flags. */ else { diff --git a/src/option.h b/src/option.h index bc6ba968bd..6ee14ebb17 100644 --- a/src/option.h +++ b/src/option.h @@ -511,6 +511,9 @@ EXTERN int fuoptions_bgcolor; argb color. */ #endif EXTERN int p_gd; /* 'gdefault' */ +#ifdef FEAT_TEXT_PROP +EXTERN char_u *p_pvp; // 'previewpopup' +#endif #ifdef FEAT_PRINTER EXTERN char_u *p_pdev; /* 'printdevice' */ # ifdef FEAT_POSTSCRIPT diff --git a/src/popupwin.c b/src/popupwin.c index e61b793cd4..47a5c789dc 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -13,7 +13,7 @@ #include "vim.h" -#ifdef FEAT_TEXT_PROP +#if defined(FEAT_TEXT_PROP) || defined(PROTO) typedef struct { char *pp_name; @@ -441,6 +441,41 @@ check_highlight(dict_T *dict, char *name, char_u **pval) } } +/* + * Scroll to show the line with the cursor. This assumes lines don't wrap. + */ + static void +popup_show_curline(win_T *wp) +{ + if (wp->w_cursor.lnum < wp->w_topline) + wp->w_topline = wp->w_cursor.lnum; + else if (wp->w_cursor.lnum >= wp->w_botline) + wp->w_topline = wp->w_cursor.lnum - wp->w_height + 1; +} + +/* + * Highlight the line with the cursor. + * Also scrolls the text to put the cursor line in view. + */ + static void +popup_highlight_curline(win_T *wp) +{ + int id; + char buf[100]; + + match_delete(wp, 1, FALSE); + + if ((wp->w_popup_flags & POPF_CURSORLINE) != 0) + { + popup_show_curline(wp); + + id = syn_name2id((char_u *)"PopupSelected"); + vim_snprintf(buf, sizeof(buf), "\\%%%dl.*", (int)wp->w_cursor.lnum); + match_add(wp, (char_u *)(id == 0 ? "PmenuSel" : "PopupSelected"), + (char_u *)buf, 10, 1, NULL, NULL); + } +} + /* * Shared between popup_create() and f_popup_setoptions(). */ @@ -516,7 +551,7 @@ apply_general_options(win_T *wp, dict_T *dict) di = dict_find(dict, (char_u *)"borderhighlight", -1); if (di != NULL) { - if (di->di_tv.v_type != VAR_LIST) + if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL) emsg(_(e_listreq)); else { @@ -524,17 +559,16 @@ apply_general_options(win_T *wp, dict_T *dict) listitem_T *li; int i; - if (list != NULL) - for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len; - ++i, li = li->li_next) - { - str = tv_get_string(&li->li_tv); - if (*str != NUL) - wp->w_border_highlight[i] = vim_strsave(str); - } + for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len; + ++i, li = li->li_next) + { + str = tv_get_string(&li->li_tv); + if (*str != NUL) + wp->w_border_highlight[i] = vim_strsave(str); + } if (list->lv_len == 1 && wp->w_border_highlight[0] != NULL) for (i = 1; i < 4; ++i) - wp->w_border_highlight[i] = + wp->w_border_highlight[i] = vim_strsave(wp->w_border_highlight[0]); } } @@ -587,14 +621,13 @@ apply_general_options(win_T *wp, dict_T *dict) di = dict_find(dict, (char_u *)"mask", -1); if (di != NULL) { - int ok = TRUE; + int ok = FALSE; - if (di->di_tv.v_type != VAR_LIST) - ok = FALSE; - else if (di->di_tv.vval.v_list != NULL) + if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { listitem_T *li; + ok = TRUE; for (li = di->di_tv.vval.v_list->lv_first; li != NULL; li = li->li_next) { @@ -637,6 +670,20 @@ apply_general_options(win_T *wp, dict_T *dict) handle_moved_argument(wp, di, TRUE); } + di = dict_find(dict, (char_u *)"cursorline", -1); + if (di != NULL) + { + if (di->di_tv.v_type == VAR_NUMBER) + { + if (di->di_tv.vval.v_number != 0) + wp->w_popup_flags |= POPF_CURSORLINE; + else + wp->w_popup_flags &= ~POPF_CURSORLINE; + } + else + semsg(_(e_invargval), "cursorline"); + } + di = dict_find(dict, (char_u *)"filter", -1); if (di != NULL) { @@ -664,6 +711,7 @@ apply_general_options(win_T *wp, dict_T *dict) /* * Go through the options in "dict" and apply them to popup window "wp". + * Only used when creating a new popup window. */ static void apply_options(win_T *wp, dict_T *dict) @@ -681,6 +729,7 @@ apply_options(win_T *wp, dict_T *dict) } popup_mask_refresh = TRUE; + popup_highlight_curline(wp); } /* @@ -881,7 +930,8 @@ popup_adjust_position(win_T *wp) } // start at the desired first line - wp->w_topline = wp->w_firstline; + if (wp->w_firstline != 0) + wp->w_topline = wp->w_firstline; if (wp->w_topline > wp->w_buffer->b_ml.ml_line_count) wp->w_topline = wp->w_buffer->b_ml.ml_line_count; @@ -1036,7 +1086,8 @@ typedef enum TYPE_BEVAL, TYPE_NOTIFICATION, TYPE_DIALOG, - TYPE_MENU + TYPE_MENU, + TYPE_PREVIEW } create_type_T; /* @@ -1081,68 +1132,144 @@ popup_set_buffer_text(buf_T *buf, typval_T text) curbuf = curwin->w_buffer; } +/* + * Parse the 'previewpopup' option and apply the values to window "wp" if it + * not NULL. + * Return FAIL if the parsing fails. + */ + int +parse_previewpopup(win_T *wp) +{ + char_u *p; + + for (p = p_pvp; *p != NUL; p += (*p == ',' ? 1 : 0)) + { + char_u *e, *dig; + char_u *s = p; + int x; + + e = vim_strchr(p, ':'); + if (e == NULL || e[1] == NUL) + return FAIL; + + p = vim_strchr(e, ','); + if (p == NULL) + p = e + STRLEN(e); + dig = e + 1; + x = getdigits(&dig); + if (dig != p) + return FAIL; + + if (STRNCMP(s, "height:", 7) == 0) + { + if (wp != NULL) + { + wp->w_minheight = x; + wp->w_maxheight = x; + } + } + else if (STRNCMP(s, "width:", 6) == 0) + { + if (wp != NULL) + { + wp->w_minwidth = x; + wp->w_maxwidth = x; + } + } + else + return FAIL; + } + return OK; +} + +/* + * Set w_wantline and w_wantcol for the cursor position in the current window. + */ + void +popup_set_wantpos(win_T *wp) +{ + setcursor_mayforce(TRUE); + wp->w_wantline = curwin->w_winrow + curwin->w_wrow; + if (wp->w_wantline == 0) // cursor in first line + { + wp->w_wantline = 2; + wp->w_popup_pos = POPPOS_TOPLEFT; + } + wp->w_wantcol = curwin->w_wincol + curwin->w_wcol + 1; + popup_adjust_position(wp); +} + /* * popup_create({text}, {options}) * popup_atcursor({text}, {options}) + * etc. + * When creating a preview window popup "argvars" and "rettv" are NULL. */ static win_T * popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) { win_T *wp; tabpage_T *tp = NULL; - int tabnr; + int tabnr = 0; int new_buffer; buf_T *buf = NULL; - dict_T *d; + dict_T *d = NULL; int nr; int i; - // Check arguments look OK. - if (argvars[0].v_type == VAR_NUMBER) + if (argvars != NULL) { - buf = buflist_findnr( argvars[0].vval.v_number); - if (buf == NULL) + // Check arguments look OK. + if (argvars[0].v_type == VAR_NUMBER) { - semsg(_(e_nobufnr), argvars[0].vval.v_number); + buf = buflist_findnr( argvars[0].vval.v_number); + if (buf == NULL) + { + semsg(_(e_nobufnr), argvars[0].vval.v_number); + return NULL; + } + } + else if (!(argvars[0].v_type == VAR_STRING + && argvars[0].vval.v_string != NULL) + && !(argvars[0].v_type == VAR_LIST + && argvars[0].vval.v_list != NULL)) + { + emsg(_(e_listreq)); return NULL; } - } - else if (!(argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL) - && !(argvars[0].v_type == VAR_LIST - && argvars[0].vval.v_list != NULL)) - { - emsg(_(e_listreq)); - return NULL; - } - if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL) - { - emsg(_(e_dictreq)); - return NULL; - } - d = argvars[1].vval.v_dict; - - if (dict_find(d, (char_u *)"tabpage", -1) != NULL) - tabnr = (int)dict_get_number(d, (char_u *)"tabpage"); - else if (type == TYPE_NOTIFICATION) - tabnr = -1; // notifications are global by default - else - tabnr = 0; - if (tabnr > 0) - { - tp = find_tabpage(tabnr); - if (tp == NULL) + if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL) { - semsg(_("E997: Tabpage not found: %d"), tabnr); + emsg(_(e_dictreq)); return NULL; } + d = argvars[1].vval.v_dict; + } + + if (d != NULL) + { + if (dict_find(d, (char_u *)"tabpage", -1) != NULL) + tabnr = (int)dict_get_number(d, (char_u *)"tabpage"); + else if (type == TYPE_NOTIFICATION) + tabnr = -1; // notifications are global by default + else + tabnr = 0; + if (tabnr > 0) + { + tp = find_tabpage(tabnr); + if (tp == NULL) + { + semsg(_("E997: Tabpage not found: %d"), tabnr); + return NULL; + } + } } // Create the window and buffer. wp = win_alloc_popup_win(); if (wp == NULL) return NULL; - rettv->vval.v_number = wp->w_id; + if (rettv != NULL) + rettv->vval.v_number = wp->w_id; wp->w_popup_pos = POPPOS_TOPLEFT; wp->w_popup_flags = POPF_IS_POPUP; @@ -1169,7 +1296,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1, (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0); set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1, - (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0); + (char_u *)"wipe", OPT_FREE|OPT_LOCAL, 0); buf->b_p_ul = -1; // no undo buf->b_p_swf = FALSE; // no swap file buf->b_p_bl = FALSE; // unlisted buffer @@ -1208,20 +1335,16 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) } } - if (new_buffer) + if (new_buffer && argvars != NULL) popup_set_buffer_text(buf, argvars[0]); - if (type == TYPE_ATCURSOR) + if (type == TYPE_ATCURSOR || type == TYPE_PREVIEW) { wp->w_popup_pos = POPPOS_BOTLEFT; - setcursor_mayforce(TRUE); - wp->w_wantline = curwin->w_winrow + curwin->w_wrow; - if (wp->w_wantline == 0) // cursor in first line - { - wp->w_wantline = 2; - wp->w_popup_pos = POPPOS_TOPLEFT; - } - wp->w_wantcol = curwin->w_wincol + curwin->w_wcol + 1; + popup_set_wantpos(wp); + } + if (type == TYPE_ATCURSOR) + { set_moved_values(wp); set_moved_columns(wp, FIND_STRING); } @@ -1315,6 +1438,16 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) set_callback(&wp->w_filter_cb, &callback); wp->w_p_wrap = 0; + wp->w_popup_flags |= POPF_CURSORLINE; + } + + if (type == TYPE_PREVIEW) + { + wp->w_popup_drag = 1; + wp->w_popup_close = POPCLOSE_BUTTON; + for (i = 0; i < 4; ++i) + wp->w_popup_border[i] = 1; + parse_previewpopup(wp); } for (i = 0; i < 4; ++i) @@ -1324,8 +1457,9 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) wp->w_want_scrollbar = 1; wp->w_popup_fixed = 0; - // Deal with options. - apply_options(wp, argvars[1].vval.v_dict); + if (d != NULL) + // Deal with options. + apply_options(wp, d); #ifdef FEAT_TIMERS if (type == TYPE_NOTIFICATION && wp->w_popup_timer == NULL) @@ -1504,26 +1638,6 @@ filter_handle_drag(win_T *wp, int c, typval_T *rettv) rettv->vval.v_number = 0; } - static void -popup_highlight_curline(win_T *wp) -{ - int id; - char buf[100]; - - match_delete(wp, 1, FALSE); - - // Scroll to show the line with the cursor. This assumes lines don't wrap. - while (wp->w_topline + wp->w_height - 1 < wp->w_cursor.lnum) - wp->w_topline++; - while (wp->w_cursor.lnum < wp->w_topline) - wp->w_topline--; - - id = syn_name2id((char_u *)"PopupSelected"); - vim_snprintf(buf, sizeof(buf), "\\%%%dl.*", (int)wp->w_cursor.lnum); - match_add(wp, (char_u *)(id == 0 ? "PmenuSel" : "PopupSelected"), - (char_u *)buf, 10, 1, NULL, NULL); -} - /* * popup_filter_menu({text}, {options}) */ @@ -1632,10 +1746,7 @@ f_popup_dialog(typval_T *argvars, typval_T *rettv) void f_popup_menu(typval_T *argvars, typval_T *rettv) { - win_T *wp = popup_create(argvars, rettv, TYPE_MENU); - - if (wp != NULL) - popup_highlight_curline(wp); + popup_create(argvars, rettv, TYPE_MENU); } /* @@ -1860,6 +1971,7 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED) if (old_firstline != wp->w_firstline) redraw_win_later(wp, NOT_VALID); popup_mask_refresh = TRUE; + popup_highlight_curline(wp); popup_adjust_position(wp); } @@ -2049,6 +2161,7 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv) dict_add_string(dict, "title", wp->w_popup_title); dict_add_number(dict, "wrap", wp->w_p_wrap); dict_add_number(dict, "drag", wp->w_popup_drag); + dict_add_number(dict, "cursorline", (wp->w_popup_flags & POPF_CURSORLINE) != 0); dict_add_string(dict, "highlight", wp->w_p_wcr); if (wp->w_scrollbar_highlight != NULL) dict_add_string(dict, "scrollbarhighlight", @@ -2183,6 +2296,7 @@ invoke_popup_filter(win_T *wp, int c) int dummy; typval_T argv[3]; char_u buf[NUMBUFLEN]; + linenr_T old_lnum = wp->w_cursor.lnum; // Emergency exit: CTRL-C closes the popup. if (c == Ctrl_C) @@ -2207,6 +2321,9 @@ invoke_popup_filter(win_T *wp, int c) // NOTE: The callback might close the popup, thus make "wp" invalid. call_callback(&wp->w_filter_cb, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); + if (old_lnum != wp->w_cursor.lnum) + popup_highlight_curline(wp); + res = tv_get_number(&rettv); vim_free(argv[1].vval.v_string); clear_tv(&rettv); @@ -2813,4 +2930,72 @@ set_ref_in_popups(int copyID) } return abort; } + +/* + * Find an existing popup used as the preview window, in the current tab page. + * Return NULL if not found. + */ + win_T * +popup_find_preview_window(void) +{ + win_T *wp; + + // Preview window popup is always local to tab page. + for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next) + if (wp->w_p_pvw) + return wp; + return wp; +} + + int +popup_is_popup(win_T *wp) +{ + return wp->w_popup_flags != 0; +} + +/* + * Create a popup to be used as the preview window. + * NOTE: this makes the popup the current window, so that the file can be + * edited. However, it must not remain to be the current window, the caller + * must make sure of that. + */ + int +popup_create_preview_window(void) +{ + win_T *wp = popup_create(NULL, NULL, TYPE_PREVIEW); + + if (wp == NULL) + return FAIL; + wp->w_p_pvw = TRUE; + + // Set the width to a reasonable value, so that w_topline can be computed. + if (wp->w_minwidth > 0) + wp->w_width = wp->w_minwidth; + else if (wp->w_maxwidth > 0) + wp->w_width = wp->w_maxwidth; + else + wp->w_width = curwin->w_width; + + // Will switch to another buffer soon, dummy one can be wiped. + wp->w_buffer->b_locked = FALSE; + + win_enter(wp, FALSE); + return OK; +} + + void +popup_close_preview() +{ + win_T *wp = popup_find_preview_window(); + + if (wp != NULL) + { + typval_T res; + + res.v_type = VAR_NUMBER; + res.vval.v_number = -1; + popup_close_and_callback(wp, &res); + } +} + #endif // FEAT_TEXT_PROP diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro index d1267e0224..605575b77b 100644 --- a/src/proto/popupwin.pro +++ b/src/proto/popupwin.pro @@ -8,6 +8,8 @@ void popup_handle_scrollbar_click(win_T *wp, int row, int col); int popup_height(win_T *wp); int popup_width(win_T *wp); void popup_adjust_position(win_T *wp); +int parse_previewpopup(win_T *wp); +void popup_set_wantpos(win_T *wp); void f_popup_clear(typval_T *argvars, typval_T *rettv); void f_popup_create(typval_T *argvars, typval_T *rettv); void f_popup_atcursor(typval_T *argvars, typval_T *rettv); @@ -39,4 +41,8 @@ void popup_check_cursor_pos(void); void may_update_popup_mask(int type); void update_popups(void (*win_update)(win_T *wp)); int set_ref_in_popups(int copyID); +win_T *popup_find_preview_window(void); +int popup_is_popup(win_T *wp); +int popup_create_preview_window(void); +void popup_close_preview(void); /* vim: set ft=c : */ diff --git a/src/screen.c b/src/screen.c index 5eea6b2aaf..877e64f704 100644 --- a/src/screen.c +++ b/src/screen.c @@ -3124,8 +3124,13 @@ get_sign_display_info( { if (nrcol) { - sprintf((char *)extra, "%*s ", number_width(wp), - *pp_extra); + int n, width = number_width(wp) - 2; + + for (n = 0; n < width; n++) + extra[n] = ' '; + extra[n] = 0; + STRCAT(extra, *pp_extra); + STRCAT(extra, " "); *pp_extra = extra; } *c_extrap = NUL; diff --git a/src/testdir/dumps/Test_popupwin_cursorline_1.dump b/src/testdir/dumps/Test_popupwin_cursorline_1.dump new file mode 100644 index 0000000000..d73b149c1b --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_cursorline_1.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @34|1+0#0000001#ffd7ff255@2| +0#4040ff13#ffffff0@35 +|~| @34|2+0#0000001#ffd7ff255@2| +0#4040ff13#ffffff0@35 +|~| @34|3+0#0000001#ffd7ff255@2| +0#4040ff13#ffffff0@35 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_cursorline_2.dump b/src/testdir/dumps/Test_popupwin_cursorline_2.dump new file mode 100644 index 0000000000..6c00f96563 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_cursorline_2.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @34|1+0#0000001#e0e0e08@2| +0#4040ff13#ffffff0@35 +|~| @34|2+0#0000001#ffd7ff255@2| +0#4040ff13#ffffff0@35 +|~| @34|3+0#0000001#ffd7ff255@2| +0#4040ff13#ffffff0@35 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_cursorline_3.dump b/src/testdir/dumps/Test_popupwin_cursorline_3.dump new file mode 100644 index 0000000000..478c59d90b --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_cursorline_3.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |1@2| | +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |2@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_cursorline_4.dump b/src/testdir/dumps/Test_popupwin_cursorline_4.dump new file mode 100644 index 0000000000..85eef41272 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_cursorline_4.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |2@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |3@2| | +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_cursorline_5.dump b/src/testdir/dumps/Test_popupwin_cursorline_5.dump new file mode 100644 index 0000000000..57d24aad88 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_cursorline_5.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |1+0&#e0e0e08@2| +0&#ffd7ff255| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |2@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_cursorline_6.dump b/src/testdir/dumps/Test_popupwin_cursorline_6.dump new file mode 100644 index 0000000000..314c2d1b0c --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_cursorline_6.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |2@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |3+0&#e0e0e08@2| +0&#ffd7ff255| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_menu_filter_1.dump b/src/testdir/dumps/Test_popupwin_menu_filter_1.dump new file mode 100644 index 0000000000..991ddec0e6 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_menu_filter_1.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |1@2| | +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |2+0&#e0e0e08@2| +0&#ffd7ff255| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |3@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_menu_filter_2.dump b/src/testdir/dumps/Test_popupwin_menu_filter_2.dump new file mode 100644 index 0000000000..3862b92d19 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_menu_filter_2.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |1+0&#e0e0e08@2| +0&#ffd7ff255| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |2@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |3@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_menu_filter_3.dump b/src/testdir/dumps/Test_popupwin_menu_filter_3.dump new file mode 100644 index 0000000000..af0735cd62 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_menu_filter_3.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |7@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |8@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |9+0&#e0e0e08@2| +0&#ffd7ff255| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_menu_filter_4.dump b/src/testdir/dumps/Test_popupwin_menu_filter_4.dump new file mode 100644 index 0000000000..3862b92d19 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_menu_filter_4.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |1+0&#e0e0e08@2| +0&#ffd7ff255| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |2@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|║+0#0000001#ffd7ff255| |3@2| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@32 +|~| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#4040ff13#ffffff0@32 +|~| @73 +|~| @73 +| +0#0000000&@56|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_1.dump b/src/testdir/dumps/Test_popupwin_previewpopup_1.dump new file mode 100644 index 0000000000..d23dd677f8 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_previewpopup_1.dump @@ -0,0 +1,14 @@ +|o+0&#ffffff0|n|e| @71 +|t|w|o| @1|╔+0#0000001#ffd7ff255|═@40|X| +0#0000000#ffffff0@26 +|t|h|r|e@1|║+0#0000001#ffd7ff255|2|0| @37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@26 +|f|o|u|r| |║+0#0000001#ffd7ff255|t|h|e|w|o|r|d| |i|s| |h|e|r|e| @24| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@26 +|f|i|v|e| |║+0#0000001#ffd7ff255|2@1| @37| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@26 +|s|i|x| @1|║+0#0000001#ffd7ff255|2|3| @37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@26 +|s|e|v|e|n|╚+0#0000001#ffd7ff255|═@40|╝| +0#0000000#ffffff0@26 +|f|i|n|d| >t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @52 +|n|i|n|e| @70 +|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @54 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|:+0#0000000&| @55|8|,|6| @10|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_2.dump b/src/testdir/dumps/Test_popupwin_previewpopup_2.dump new file mode 100644 index 0000000000..4d0e49ca0d --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_previewpopup_2.dump @@ -0,0 +1,14 @@ +|o+0&#ffffff0|n|e| @71 +|t|w|o| @71 +|t|h|r|e@1| @69 +|f|o|u|r| @3|╔+0#0000001#ffd7ff255|═@40|X| +0#0000000#ffffff0@23 +|f|i|v|e| @3|║+0#0000001#ffd7ff255|2|7| @37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@23 +|s|i|x| @4|║+0#0000001#ffd7ff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@23 +|s|e|v|e|n| @2|║+0#0000001#ffd7ff255|2|9| @37| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@23 +|f|i|n|d| |t|h|e|║+0#0000001#ffd7ff255|3|0| @37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@23 +|n|i|n|e| @3|╚+0#0000001#ffd7ff255|═@40|╝| +0#0000000#ffffff0@23 +|t|h|i|s| |i|s| >a|n|o|t|h|e|r| |w|o|r|d| @54 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|/+0#0000000&|a|n|o|t|h|e|r| @48|1|0|,|9| @9|A|l@1| diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim index e10c71ebf0..fb37c35483 100644 --- a/src/testdir/gen_opt_test.vim +++ b/src/testdir/gen_opt_test.vim @@ -116,6 +116,7 @@ let test_values = { \ 'mousemodel': [['', 'popup'], ['xxx']], \ 'mouseshape': [['', 'n:arrow'], ['xxx']], \ 'nrformats': [['', 'alpha', 'alpha,hex,bin'], ['xxx']], + \ 'previewpopup': [['', 'height:13', 'width:10,height:234'], ['height:yes', 'xxx', 'xxx:99']], \ 'printmbfont': [['', 'r:some', 'b:Bold,c:yes'], ['xxx']], \ 'printoptions': [['', 'header:0', 'left:10pc,top:5pc'], ['xxx']], \ 'scrollopt': [['', 'ver', 'ver,hor'], ['xxx']], diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 8b1ea1f6f6..0ec901cb59 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -634,6 +634,8 @@ func Test_popup_invalid_arguments() call popup_clear() call assert_fails('call popup_create("text", #{borderhighlight: "none"})', 'E714:') call popup_clear() + call assert_fails('call popup_create("text", #{borderhighlight: test_null_list()})', 'E714:') + call popup_clear() call assert_fails('call popup_create("text", #{borderchars: "none"})', 'E714:') call popup_clear() @@ -643,6 +645,10 @@ func Test_popup_invalid_arguments() call popup_clear() call assert_fails('call popup_create([#{text: "text", props: ["none"]}], {})', 'E715:') call popup_clear() + call assert_fails('call popup_create("text", #{mask: ["asdf"]})', 'E475:') + call popup_clear() + call assert_fails('call popup_create("text", #{mask: test_null_list()})', 'E475:') + call popup_clear() endfunc func Test_win_execute_closing_curwin() @@ -1915,4 +1921,176 @@ func Test_popup_menu_with_scrollbar() call delete('XtestPopupMenuScroll') endfunc +func Test_popup_menu_filter() + if !CanRunVimInTerminal() + throw 'Skipped: cannot make screendumps' + endif + + let lines =<< trim END + function! MyFilter(winid, key) abort + if a:key == "0" + call win_execute(a:winid, "call setpos('.', [0, 1, 1, 0])") + return 1 + endif + if a:key == "G" + call win_execute(a:winid, "call setpos('.', [0, line('$'), 1, 0])") + return 1 + endif + if a:key == "j" + call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0])") + return 1 + endif + if a:key == "k" + call win_execute(a:winid, "call setpos('.', [0, line('.') - 1, 1, 0])") + return 1 + endif + if a:key == 'x' + call popup_close(a:winid) + return 1 + endif + return 0 + endfunction + call popup_menu(['111', '222', '333', '444', '555', '666', '777', '888', '999'], #{ + \ maxheight : 3, + \ filter : 'MyFilter' + \ }) + END + call writefile(lines, 'XtestPopupMenuFilter') + let buf = RunVimInTerminal('-S XtestPopupMenuFilter', #{rows: 10}) + + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_1', {}) + + call term_sendkeys(buf, "k") + call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_2', {}) + + call term_sendkeys(buf, "G") + call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_3', {}) + + call term_sendkeys(buf, "0") + call VerifyScreenDump(buf, 'Test_popupwin_menu_filter_4', {}) + + call term_sendkeys(buf, "x") + + " clean up + call StopVimInTerminal(buf) + call delete('XtestPopupMenuFilter') +endfunc + +func Test_popup_cursorline() + if !CanRunVimInTerminal() + throw 'Skipped: cannot make screendumps' + endif + + let winid = popup_create('some text', {}) + call assert_equal(0, popup_getoptions(winid).cursorline) + call popup_close(winid) + + let winid = popup_create('some text', #{ cursorline: 1, }) + call assert_equal(1, popup_getoptions(winid).cursorline) + call popup_close(winid) + + let winid = popup_create('some text', #{ cursorline: 0, }) + call assert_equal(0, popup_getoptions(winid).cursorline) + call popup_close(winid) + + let winid = popup_menu('some text', {}) + call assert_equal(1, popup_getoptions(winid).cursorline) + call popup_close(winid) + + let winid = popup_menu('some text', #{ cursorline: 1, }) + call assert_equal(1, popup_getoptions(winid).cursorline) + call popup_close(winid) + + let winid = popup_menu('some text', #{ cursorline: 0, }) + call assert_equal(0, popup_getoptions(winid).cursorline) + call popup_close(winid) + + " --------- + " Pattern 1 + " --------- + let lines =<< trim END + call popup_create(['111', '222', '333'], #{ cursorline : 0 }) + END + call writefile(lines, 'XtestPopupCursorLine') + let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10}) + call VerifyScreenDump(buf, 'Test_popupwin_cursorline_1', {}) + call term_sendkeys(buf, ":call popup_clear()\") + call StopVimInTerminal(buf) + + " --------- + " Pattern 2 + " --------- + let lines =<< trim END + call popup_create(['111', '222', '333'], #{ cursorline : 1 }) + END + call writefile(lines, 'XtestPopupCursorLine') + let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10}) + call VerifyScreenDump(buf, 'Test_popupwin_cursorline_2', {}) + call term_sendkeys(buf, ":call popup_clear()\") + call StopVimInTerminal(buf) + + " --------- + " Pattern 3 + " --------- + let lines =<< trim END + function! MyFilter(winid, key) abort + if a:key == "j" + call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0]) | redraw") + return 1 + endif + if a:key == 'x' + call popup_close(a:winid) + return 1 + endif + return 0 + endfunction + call popup_menu(['111', '222', '333'], #{ + \ cursorline : 0, + \ maxheight : 2, + \ filter : 'MyFilter', + \ }) + END + call writefile(lines, 'XtestPopupCursorLine') + let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10}) + call VerifyScreenDump(buf, 'Test_popupwin_cursorline_3', {}) + call term_sendkeys(buf, "j") + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_popupwin_cursorline_4', {}) + call term_sendkeys(buf, "x") + call StopVimInTerminal(buf) + + " --------- + " Pattern 4 + " --------- + let lines =<< trim END + function! MyFilter(winid, key) abort + if a:key == "j" + call win_execute(a:winid, "call setpos('.', [0, line('.') + 1, 1, 0]) | redraw") + return 1 + endif + if a:key == 'x' + call popup_close(a:winid) + return 1 + endif + return 0 + endfunction + call popup_menu(['111', '222', '333'], #{ + \ cursorline : 1, + \ maxheight : 2, + \ filter : 'MyFilter', + \ }) + END + call writefile(lines, 'XtestPopupCursorLine') + let buf = RunVimInTerminal('-S XtestPopupCursorLine', #{rows: 10}) + call VerifyScreenDump(buf, 'Test_popupwin_cursorline_5', {}) + call term_sendkeys(buf, "j") + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_popupwin_cursorline_6', {}) + call term_sendkeys(buf, "x") + call StopVimInTerminal(buf) + + call delete('XtestPopupCursorLine') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_signs.vim b/src/testdir/test_signs.vim index 1b9a29d9fe..3d885d6428 100644 --- a/src/testdir/test_signs.vim +++ b/src/testdir/test_signs.vim @@ -1766,6 +1766,7 @@ func Test_sign_numcol() set number set signcolumn=number sign define sign1 text==> + sign define sign2 text=V sign place 10 line=1 name=sign1 redraw! call assert_equal("=> 01234", s:ScreenLine(1, 1, 8)) @@ -1846,6 +1847,12 @@ func Test_sign_numcol() redraw! call assert_equal("=> 01234", s:ScreenLine(1, 1, 8)) call assert_equal(" 2 abcde", s:ScreenLine(2, 1, 8)) + " Add sign with multi-byte text + set numberwidth=4 + sign place 40 line=2 name=sign2 + redraw! + call assert_equal(" => 01234", s:ScreenLine(1, 1, 9)) + call assert_equal(" V abcde", s:ScreenLine(2, 1, 9)) sign unplace * group=* sign undefine sign1 diff --git a/src/version.c b/src/version.c index 396efad7e7..caec4c74fe 100644 --- a/src/version.c +++ b/src/version.c @@ -792,6 +792,24 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1714, +/**/ + 1713, +/**/ + 1712, +/**/ + 1711, +/**/ + 1710, +/**/ + 1709, +/**/ + 1708, +/**/ + 1707, +/**/ + 1706, /**/ 1705, /**/ diff --git a/src/vim.h b/src/vim.h index 941723dd47..3e6c0da6fa 100644 --- a/src/vim.h +++ b/src/vim.h @@ -618,6 +618,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define POPF_IS_POPUP 1 // this is a popup window #define POPF_HIDDEN 2 // popup is not displayed #define POPF_HANDLED 4 // popup was just redrawn or filtered +#define POPF_CURSORLINE 8 // popup is highlighting at the cursorline #ifdef FEAT_TEXT_PROP # define WIN_IS_POPUP(wp) ((wp)->w_popup_flags != 0) #else