# HG changeset patch # User Bram Moolenaar # Date 1559493906 -7200 # Node ID 9138e2c60bf1cd36e811885f946327e965ecc9ac # Parent 52ff7612af87d39c632114f2319df06b05769e45 patch 8.1.1453: popup window "moved" property not implemented yet commit https://github.com/vim/vim/commit/3397f74ac2ac27f1eef48e950c3c8eeb0338fe55 Author: Bram Moolenaar Date: Sun Jun 2 18:40:06 2019 +0200 patch 8.1.1453: popup window "moved" property not implemented yet Problem: Popup window "moved" property not implemented yet. Solution: Implement it. diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -90,7 +90,6 @@ Probably 2. is the best choice. IMPLEMENTATION: - Code is in popupwin.c -- Fix positioning with border and padding. - Why does 'nrformats' leak from the popup window buffer??? - Make redrawing more efficient and avoid flicker. First draw popups, creating a mask, use the mask in screen_line() when @@ -410,13 +409,14 @@ The second argument of |popup_create()| zindex Priority for the popup, default 50. time Time in milliseconds after which the popup will close. When omitted |popup_close()| must be used. - moved "cell": close the popup if the cursor moved at least - one screen cell. - "word" allows for moving the cursor within || - "WORD" allows for moving the cursor within || - a list with two numbers specifies the start and end - column outside of which the popup will close - {not implemented yet} + moved Specifies to close the popup if the cursor moved: + - "any": if the cursor moved at all + - "word": if the cursor moved outside || + - "WORD": if the cursor moved outside || + - [{start}, {end}]: if the cursor moved before column + {start} or after {end} + The popup also closes if the cursor moves to another + line or to another window. filter A callback that can filter typed characters, see |popup-filter|. callback A callback that is called when the popup closes, e.g. @@ -510,6 +510,9 @@ The callback is invoked with two argumen result, which could be an index in the popup lines, or whatever was passed as the second argument of `popup_close()`. +If the popup is closed because the cursor moved, the number -1 is passed to +the callback. + ============================================================================== 3. Examples *popup-examples* diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -1456,8 +1456,7 @@ ins_need_undo_get(void) * inserting sequences of characters (e.g., for CTRL-R). */ void -ins_redraw( - int ready UNUSED) /* not busy with something */ +ins_redraw(int ready) // not busy with something { #ifdef FEAT_CONCEAL linenr_T conceal_old_cursor_line = 0; @@ -1468,10 +1467,12 @@ ins_redraw( if (char_avail()) return; -#if defined(FEAT_CONCEAL) /* Trigger CursorMoved if the cursor moved. Not when the popup menu is * visible, the command might delete it. */ if (ready && (has_cursormovedI() +# ifdef FEAT_TEXT_PROP + || popup_visible +# endif # if defined(FEAT_CONCEAL) || curwin->w_p_cole > 0 # endif @@ -1497,6 +1498,10 @@ ins_redraw( update_curswant(); ins_apply_autocmds(EVENT_CURSORMOVEDI); } +#ifdef FEAT_TEXT_PROP + if (popup_visible) + popup_check_cursor_pos(); +#endif # ifdef FEAT_CONCEAL if (curwin->w_p_cole > 0) { @@ -1507,7 +1512,6 @@ ins_redraw( # endif last_cursormoved = curwin->w_cursor; } -#endif /* Trigger TextChangedI if b_changedtick differs. */ if (ready && has_textchangedI() @@ -3859,7 +3863,7 @@ replace_push( if (replace_stack_len <= replace_stack_nr) { replace_stack_len += 50; - p = alloc(sizeof(char_u) * replace_stack_len); + p = ALLOC_MULT(char_u, replace_stack_len); if (p == NULL) /* out of memory */ { replace_stack_len -= 50; diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -558,24 +558,25 @@ EXTERN int clip_unnamed_saved INIT(= 0); EXTERN win_T *firstwin; /* first window */ EXTERN win_T *lastwin; /* last window */ EXTERN win_T *prevwin INIT(= NULL); /* previous window */ -# define ONE_WINDOW (firstwin == lastwin) -# define W_NEXT(wp) ((wp)->w_next) -# define FOR_ALL_WINDOWS(wp) for (wp = firstwin; wp != NULL; wp = wp->w_next) -# define FOR_ALL_FRAMES(frp, first_frame) \ +#define ONE_WINDOW (firstwin == lastwin) +#define W_NEXT(wp) ((wp)->w_next) +#define FOR_ALL_WINDOWS(wp) for (wp = firstwin; wp != NULL; wp = wp->w_next) +#define FOR_ALL_FRAMES(frp, first_frame) \ for (frp = first_frame; frp != NULL; frp = frp->fr_next) -# define FOR_ALL_TABPAGES(tp) for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) -# define FOR_ALL_WINDOWS_IN_TAB(tp, wp) \ +#define FOR_ALL_TABPAGES(tp) for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) +#define FOR_ALL_WINDOWS_IN_TAB(tp, wp) \ for ((wp) = ((tp) == NULL || (tp) == curtab) \ ? firstwin : (tp)->tp_firstwin; (wp); (wp) = (wp)->w_next) /* * When using this macro "break" only breaks out of the inner loop. Use "goto" * to break out of the tabpage loop. */ -# define FOR_ALL_TAB_WINDOWS(tp, wp) \ +#define FOR_ALL_TAB_WINDOWS(tp, wp) \ for ((tp) = first_tabpage; (tp) != NULL; (tp) = (tp)->tp_next) \ for ((wp) = ((tp) == curtab) \ ? firstwin : (tp)->tp_firstwin; (wp); (wp) = (wp)->w_next) + EXTERN win_T *curwin; /* currently active window */ EXTERN win_T *aucmd_win; /* window used in aucmd_prepbuf() */ @@ -1663,4 +1664,5 @@ EXTERN HINSTANCE g_hinst INIT(= NULL); #ifdef FEAT_TEXT_PROP EXTERN int text_prop_frozen INIT(= 0); +EXTERN int popup_visible INIT(= FALSE); #endif diff --git a/src/gui.c b/src/gui.c --- a/src/gui.c +++ b/src/gui.c @@ -5117,6 +5117,9 @@ gui_update_screen(void) /* Trigger CursorMoved if the cursor moved. */ if (!finish_op && (has_cursormoved() +# ifdef FEAT_TEXT_PROP + || popup_visible +# endif # ifdef FEAT_CONCEAL || curwin->w_p_cole > 0 # endif @@ -5124,6 +5127,10 @@ gui_update_screen(void) { if (has_cursormoved()) apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, FALSE, curbuf); +#ifdef FEAT_TEXT_PROP + if (popup_visible) + popup_check_cursor_pos(); +#endif # ifdef FEAT_CONCEAL if (curwin->w_p_cole > 0) { diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -1159,6 +1159,9 @@ main_loop( /* Trigger CursorMoved if the cursor moved. */ if (!finish_op && ( has_cursormoved() +#ifdef FEAT_TEXT_PROP + || popup_visible +#endif #ifdef FEAT_CONCEAL || curwin->w_p_cole > 0 #endif @@ -1168,14 +1171,18 @@ main_loop( if (has_cursormoved()) apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, FALSE, curbuf); -# ifdef FEAT_CONCEAL +#ifdef FEAT_TEXT_PROP + if (popup_visible) + popup_check_cursor_pos(); +#endif +#ifdef FEAT_CONCEAL if (curwin->w_p_cole > 0) { conceal_old_cursor_line = last_cursormoved.lnum; conceal_new_cursor_line = curwin->w_cursor.lnum; conceal_update_lines = TRUE; } -# endif +#endif last_cursormoved = curwin->w_cursor; } diff --git a/src/popupwin.c b/src/popupwin.c --- a/src/popupwin.c +++ b/src/popupwin.c @@ -285,6 +285,49 @@ apply_options(win_T *wp, buf_T *buf UNUS } } } + + di = dict_find(dict, (char_u *)"moved", -1); + if (di != NULL) + { + wp->w_popup_curwin = curwin; + wp->w_popup_lnum = curwin->w_cursor.lnum; + wp->w_popup_mincol = curwin->w_cursor.col; + wp->w_popup_maxcol = curwin->w_cursor.col; + if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL) + { + char_u *s = di->di_tv.vval.v_string; + int flags = 0; + + if (STRCMP(s, "word") == 0) + flags = FIND_IDENT | FIND_STRING; + else if (STRCMP(s, "WORD") == 0) + flags = FIND_STRING; + else if (STRCMP(s, "any") != 0) + semsg(_(e_invarg2), s); + if (flags != 0) + { + char_u *ptr; + int len = find_ident_under_cursor(&ptr, flags); + + if (len > 0) + { + wp->w_popup_mincol = (int)(ptr - ml_get_curline()); + wp->w_popup_maxcol = wp->w_popup_mincol + len - 1; + } + } + } + else if (di->di_tv.v_type == VAR_LIST + && di->di_tv.vval.v_list != NULL + && di->di_tv.vval.v_list->lv_len == 2) + { + list_T *l = di->di_tv.vval.v_list; + + wp->w_popup_mincol = tv_get_number(&l->lv_first->li_tv); + wp->w_popup_maxcol = tv_get_number(&l->lv_first->li_next->li_tv); + } + else + semsg(_(e_invarg2), tv_get_string(&di->di_tv)); + } } /* @@ -708,6 +751,21 @@ invoke_popup_callback(win_T *wp, typval_ } /* + * Close popup "wp" and invoke any close callback for it. + */ + static void +popup_close_and_callback(win_T *wp, typval_T *arg) +{ + int id = wp->w_id; + + if (wp->w_close_cb.cb_name != NULL) + // Careful: This may make "wp" invalid. + invoke_popup_callback(wp, arg); + + popup_close(id); +} + +/* * popup_close({id}) */ void @@ -717,13 +775,7 @@ f_popup_close(typval_T *argvars, typval_ win_T *wp = find_popup_win(id); if (wp != NULL) - { - if (wp->w_close_cb.cb_name != NULL) - // Careful: This may make "wp" invalid. - invoke_popup_callback(wp, &argvars[1]); - - popup_close(id); - } + popup_close_and_callback(wp, &argvars[1]); } /* @@ -1066,4 +1118,28 @@ popup_do_filter(int c) return res; } +/* + * Called when the cursor moved: check if any popup needs to be closed if the + * cursor moved far enough. + */ + void +popup_check_cursor_pos() +{ + win_T *wp; + typval_T tv; + + popup_reset_handled(); + while ((wp = find_next_popup(TRUE)) != NULL) + if (wp->w_popup_curwin != NULL + && (curwin != wp->w_popup_curwin + || curwin->w_cursor.lnum != wp->w_popup_lnum + || curwin->w_cursor.col < wp->w_popup_mincol + || curwin->w_cursor.col > wp->w_popup_maxcol)) + { + tv.v_type = VAR_NUMBER; + tv.vval.v_number = -1; + popup_close_and_callback(wp, &tv); + } +} + #endif // FEAT_TEXT_PROP diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro --- a/src/proto/popupwin.pro +++ b/src/proto/popupwin.pro @@ -17,4 +17,5 @@ int not_in_popup_window(void); void popup_reset_handled(void); win_T *find_next_popup(int lowest); int popup_do_filter(int c); +void popup_check_cursor_pos(void); /* vim: set ft=c : */ diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -1050,6 +1050,7 @@ update_popups(void) // so that the window with a higher zindex is drawn later, thus goes on // top. // TODO: don't redraw every popup every time. + popup_visible = FALSE; popup_reset_handled(); while ((wp = find_next_popup(TRUE)) != NULL) { @@ -1066,6 +1067,7 @@ update_popups(void) // Draw the popup text. win_update(wp); + popup_visible = TRUE; wp->w_winrow -= top_off; wp->w_wincol -= left_off; diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -2897,6 +2897,12 @@ struct window_S // computed callback_T w_close_cb; // popup close callback callback_T w_filter_cb; // popup filter callback + + win_T *w_popup_curwin; // close popup if curwin differs + linenr_T w_popup_lnum; // close popup if cursor not on this line + colnr_T w_popup_mincol; // close popup if cursor before this col + colnr_T w_popup_maxcol; // close popup if cursor after this col + # if defined(FEAT_TIMERS) timer_T *w_popup_timer; // timer for closing popup window # endif diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -909,7 +909,7 @@ func Test_popup_position_adjust() %bwipe! endfunc -function Test_adjust_left_past_screen_width() +func Test_adjust_left_past_screen_width() " width of screen let X = join(map(range(&columns), {->'X'}), '') @@ -973,4 +973,65 @@ function Test_adjust_left_past_screen_wi popupclear %bwipe! -endfunction +endfunc + +func Test_popup_moved() + new + call test_override('char_avail', 1) + call setline(1, ['one word to move around', 'a WORD.and->some thing']) + + exe "normal gg0/word\" + let winid = popup_atcursor('text', {'moved': 'any'}) + redraw + call assert_equal(1, popup_getpos(winid).visible) + " trigger the check for last_cursormoved by going into insert mode + call feedkeys("li\", 'xt') + call assert_equal({}, popup_getpos(winid)) + popupclear + + exe "normal gg0/word\" + let winid = popup_atcursor('text', {'moved': 'word'}) + redraw + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("hi\", 'xt') + call assert_equal({}, popup_getpos(winid)) + popupclear + + exe "normal gg0/word\" + let winid = popup_atcursor('text', {'moved': 'word'}) + redraw + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("li\", 'xt') + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("ei\", 'xt') + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("eli\", 'xt') + call assert_equal({}, popup_getpos(winid)) + popupclear + + exe "normal gg0/WORD\" + let winid = popup_atcursor('text', {'moved': 'WORD'}) + redraw + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("eli\", 'xt') + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("wi\", 'xt') + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("Eli\", 'xt') + call assert_equal({}, popup_getpos(winid)) + popupclear + + exe "normal gg0/word\" + let winid = popup_atcursor('text', {'moved': [5, 10]}) + redraw + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("eli\", 'xt') + call feedkeys("ei\", 'xt') + call assert_equal(1, popup_getpos(winid).visible) + call feedkeys("eli\", 'xt') + call assert_equal({}, popup_getpos(winid)) + popupclear + + bwipe! + call test_override('ALL', 0) +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -768,6 +768,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1453, +/**/ 1452, /**/ 1451,