Problem: Popup window "moved" property not implemented yet.
Solution: Implement it.
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
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 |<cword>|
- "WORD" allows for moving the cursor within |<cWORD>|
- 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 |<cword>|
+ - "WORD": if the cursor moved outside |<cWORD>|
+ - [{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.
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*
* 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;
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
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)
{
# endif
last_cursormoved = curwin->w_cursor;
}
-#endif
/* Trigger TextChangedI if b_changedtick differs. */
if (ready && has_textchangedI()
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;
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() */
#ifdef FEAT_TEXT_PROP
EXTERN int text_prop_frozen INIT(= 0);
+EXTERN int popup_visible INIT(= FALSE);
#endif
/* 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
{
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)
{
/* 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
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;
}
}
}
}
+
+ 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));
+ }
}
/*
clear_tv(&rettv);
}
+/*
+ * 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})
*/
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]);
}
/*
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
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 : */
// 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)
{
// Draw the popup text.
win_update(wp);
+ popup_visible = TRUE;
wp->w_winrow -= top_off;
wp->w_wincol -= left_off;
// 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
%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'}), '')
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\<CR>"
+ 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\<Esc>", 'xt')
+ call assert_equal({}, popup_getpos(winid))
+ popupclear
+
+ exe "normal gg0/word\<CR>"
+ let winid = popup_atcursor('text', {'moved': 'word'})
+ redraw
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("hi\<Esc>", 'xt')
+ call assert_equal({}, popup_getpos(winid))
+ popupclear
+
+ exe "normal gg0/word\<CR>"
+ let winid = popup_atcursor('text', {'moved': 'word'})
+ redraw
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("li\<Esc>", 'xt')
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("ei\<Esc>", 'xt')
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("eli\<Esc>", 'xt')
+ call assert_equal({}, popup_getpos(winid))
+ popupclear
+
+ exe "normal gg0/WORD\<CR>"
+ let winid = popup_atcursor('text', {'moved': 'WORD'})
+ redraw
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("eli\<Esc>", 'xt')
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("wi\<Esc>", 'xt')
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("Eli\<Esc>", 'xt')
+ call assert_equal({}, popup_getpos(winid))
+ popupclear
+
+ exe "normal gg0/word\<CR>"
+ let winid = popup_atcursor('text', {'moved': [5, 10]})
+ redraw
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("eli\<Esc>", 'xt')
+ call feedkeys("ei\<Esc>", 'xt')
+ call assert_equal(1, popup_getpos(winid).visible)
+ call feedkeys("eli\<Esc>", 'xt')
+ call assert_equal({}, popup_getpos(winid))
+ popupclear
+
+ bwipe!
+ call test_override('ALL', 0)
+endfunc
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1453,
/**/
1452,
/**/