changeset 16904:9138e2c60bf1 v8.1.1453

patch 8.1.1453: popup window "moved" property not implemented yet commit https://github.com/vim/vim/commit/3397f74ac2ac27f1eef48e950c3c8eeb0338fe55 Author: Bram Moolenaar <Bram@vim.org> 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.
author Bram Moolenaar <Bram@vim.org>
date Sun, 02 Jun 2019 18:45:06 +0200
parents 52ff7612af87
children 9c5fce4d3b07
files runtime/doc/popup.txt src/edit.c src/globals.h src/gui.c src/main.c src/popupwin.c src/proto/popupwin.pro src/screen.c src/structs.h src/testdir/test_popupwin.vim src/version.c
diffstat 11 files changed, 202 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- 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 |<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.
@@ -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*
 
--- 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;
--- 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
--- 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)
 	{
--- 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;
 	    }
 
--- 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
--- 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 : */
--- 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;
--- 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
--- 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\<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
--- 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,