changeset 18623:3089b422b9dc v8.1.2304

patch 8.1.2304: cannot get the mouse position when getting a mouse click Commit: https://github.com/vim/vim/commit/db3a205147ce2c335d5c2181c1f789277f8775b0 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Nov 16 18:22:41 2019 +0100 patch 8.1.2304: cannot get the mouse position when getting a mouse click Problem: Cannot get the mouse position when getting a mouse click. Solution: Add getmousepos().
author Bram Moolenaar <Bram@vim.org>
date Sat, 16 Nov 2019 18:30:04 +0100
parents 5beb893fc3db
children f2b29b42312b
files runtime/doc/eval.txt runtime/doc/popup.txt src/evalfunc.c src/mouse.c src/popupwin.c src/proto/mouse.pro src/testdir/test_functions.vim src/testdir/test_popupwin.vim src/version.c
diffstat 9 files changed, 205 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1956,15 +1956,11 @@ v:mouse_winid	Window ID for a mouse clic
 
 					*v:mouse_lnum* *mouse_lnum-variable*
 v:mouse_lnum	Line number for a mouse click obtained with |getchar()|.
-		Also used for a click in a popup window when the filter is
-		invoked.
 		This is the text line number, not the screen line number.  The
 		value is zero when there was no mouse button click.
 
 					*v:mouse_col* *mouse_col-variable*
 v:mouse_col	Column number for a mouse click obtained with |getchar()|.
-		Also used for a click in a popup window when the filter is
-		invoked.
 		This is the screen column number, like with |virtcol()|.  The
 		value is zero when there was no mouse button click.
 
@@ -2484,6 +2480,7 @@ getline({lnum})			String	line {lnum} of 
 getline({lnum}, {end})		List	lines {lnum} to {end} of current buffer
 getloclist({nr} [, {what}])	List	list of location list items
 getmatches([{win}])		List	list of current matches
+getmousepos()			Dict	last known mouse position
 getpid()			Number	process ID of Vim
 getpos({expr})			List	position of cursor, mark, etc.
 getqflist([{what}])		List	list of quickfix items
@@ -4922,8 +4919,9 @@ getchar([expr])						*getchar()*
 
 		When the user clicks a mouse button, the mouse event will be
 		returned.  The position can then be found in |v:mouse_col|,
-		|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.  This
-		example positions the mouse as it would normally happen: >
+		|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
+		|getmousepos()| can also be used.  This example positions the
+		mouse as it would normally happen: >
 			let c = getchar()
 			if c == "\<LeftMouse>" && v:mouse_win > 0
 			  exe v:mouse_win . "wincmd w"
@@ -5333,6 +5331,35 @@ getmatches([{win}])					*getmatches()*
 			'pattern': 'FIXME', 'priority': 10, 'id': 2}] >
 			:unlet m
 <
+getmousepos()						*getmousepos()*
+		Returns a Dictionary with the last known position of the
+		mouse.  This can be used in a mapping for a mouse click or in
+		a filter of a popup window.  The items are:
+			screenrow	screen row
+			screencol	screen column
+			winid		Window ID of the click
+			winrow		row inside "winid"
+			wincol		column inside "winid"
+			line		text line inside "winid"
+			column		text column inside "winid"
+		All numbers are 1-based.
+
+		If not over a window, e.g. when in the command line, then only
+		"screenrow" and "screencol" are valid, the others are zero.
+
+		When on the status line below a window or the vertical
+		separater right of a window, the "line" and "column" values
+		are zero.
+
+		When the position is after the text then "column" is the
+		length of the text in bytes.
+
+		If the mouse is over a popup window then that window is used.
+
+
+		When using |getchar()| the Vim variables |v:mouse_lnum|,
+		|v:mouse_col| and |v:mouse_winid| also provide these values.
+
 							*getpid()*
 getpid()	Return a Number which is the process ID of the Vim process.
 		On Unix and MS-Windows this is a unique number, until Vim
--- a/runtime/doc/popup.txt
+++ b/runtime/doc/popup.txt
@@ -862,10 +862,8 @@ Some recommended key actions:
 	cursor keys	select another entry
 	Tab		accept current suggestion
 
-A mouse click arrives as <LeftMouse>.  The coordinates are in |v:mouse_col|
-and |v:mouse_lnum|.  |v:mouse_winid| holds the window ID, |v:mouse_win| is
-always zero.  The top-left screen cell of the popup is col 1, row 1 (not
-counting the border).
+A mouse click arrives as <LeftMouse>.  The coordinates can be obtained with
+|mousegetpos()|.
 
 Vim provides standard filters |popup_filter_menu()| and
 |popup_filter_yesno()|.
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -465,6 +465,7 @@ static funcentry_T global_functions[] =
     {"getline",		1, 2, FEARG_1,	  f_getline},
     {"getloclist",	1, 2, 0,	  f_getloclist},
     {"getmatches",	0, 1, 0,	  f_getmatches},
+    {"getmousepos",	0, 0, 0,	  f_getmousepos},
     {"getpid",		0, 0, 0,	  f_getpid},
     {"getpos",		1, 1, FEARG_1,	  f_getpos},
     {"getqflist",	0, 1, 0,	  f_getqflist},
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -2822,6 +2822,7 @@ mouse_comp_pos(
     int		retval = FALSE;
     int		off;
     int		count;
+    char_u	*p;
 
 #ifdef FEAT_RIGHTLEFT
     if (win->w_p_rl)
@@ -2881,6 +2882,11 @@ mouse_comp_pos(
 	col += row * (win->w_width - off);
 	// add skip column (for long wrapping line)
 	col += win->w_skipcol;
+	// limit to text length plus one
+	p = ml_get_buf(win->w_buffer, lnum, FALSE);
+	count = STRLEN(p);
+	if (col > count)
+	    col = count;
     }
 
     if (!win->w_p_wrap)
@@ -3001,3 +3007,61 @@ vcol2col(win_T *wp, linenr_T lnum, int v
     return (int)(ptr - line);
 }
 #endif
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+    void
+f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    dict_T	*d;
+    win_T	*wp;
+    int		row = mouse_row;
+    int		col = mouse_col;
+    varnumber_T winid = 0;
+    varnumber_T winrow = 0;
+    varnumber_T wincol = 0;
+    varnumber_T line = 0;
+    varnumber_T column = 0;
+
+    if (rettv_dict_alloc(rettv) != OK)
+	return;
+    d = rettv->vval.v_dict;
+
+    dict_add_number(d, "screenrow", (varnumber_T)mouse_row + 1);
+    dict_add_number(d, "screencol", (varnumber_T)mouse_col + 1);
+
+    wp = mouse_find_win(&row, &col, FIND_POPUP);
+    if (wp != NULL)
+    {
+	int	top_off = 0;
+	int	left_off = 0;
+	int	height = wp->w_height + wp->w_status_height;
+
+#ifdef FEAT_TEXT_PROP
+	if (WIN_IS_POPUP(wp))
+	{
+	    top_off = popup_top_extra(wp);
+	    left_off = popup_left_extra(wp);
+	    height = popup_height(wp);
+	}
+#endif
+	if (row < height)
+	{
+	    winid = wp->w_id;
+	    winrow = row + 1;
+	    wincol = col + 1;
+	    row -= top_off;
+	    col -= left_off;
+	    if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width)
+	    {
+		mouse_comp_pos(wp, &row, &col, &line, NULL);
+		column = col + 1;
+	    }
+	}
+    }
+    dict_add_number(d, "winid", winid);
+    dict_add_number(d, "winrow", winrow);
+    dict_add_number(d, "wincol", wincol);
+    dict_add_number(d, "line", line);
+    dict_add_number(d, "column", column);
+}
+#endif
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -1047,6 +1047,15 @@ popup_top_extra(win_T *wp)
 }
 
 /*
+ * Get the padding plus border at the left.
+ */
+    int
+popup_left_extra(win_T *wp)
+{
+    return wp->w_popup_border[3] + wp->w_popup_padding[3];
+}
+
+/*
  * Return the height of popup window "wp", including border and padding.
  */
     int
@@ -2908,33 +2917,12 @@ invoke_popup_filter(win_T *wp, int c)
 
     argv[2].v_type = VAR_UNKNOWN;
 
-    if (is_mouse_key(c))
-    {
-	int		row = mouse_row - wp->w_winrow;
-	int		col = mouse_col - wp->w_wincol;
-	linenr_T	lnum;
-
-	if (row >= 0 && col >= 0)
-	{
-	    (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL);
-	    set_vim_var_nr(VV_MOUSE_LNUM, lnum);
-	    set_vim_var_nr(VV_MOUSE_COL, col + 1);
-	    set_vim_var_nr(VV_MOUSE_WINID, wp->w_id);
-	}
-    }
-
     // NOTE: The callback might close the popup and make "wp" invalid.
     call_callback(&wp->w_filter_cb, -1, &rettv, 2, argv);
     if (win_valid_popup(wp) && old_lnum != wp->w_cursor.lnum)
 	popup_highlight_curline(wp);
     res = tv_get_number(&rettv);
 
-    if (is_mouse_key(c))
-    {
-	set_vim_var_nr(VV_MOUSE_LNUM, 0);
-	set_vim_var_nr(VV_MOUSE_COL, 0);
-	set_vim_var_nr(VV_MOUSE_WINID, wp->w_id);
-    }
     vim_free(argv[1].vval.v_string);
     clear_tv(&rettv);
     return res;
--- a/src/proto/mouse.pro
+++ b/src/proto/mouse.pro
@@ -17,4 +17,5 @@ int check_termcode_mouse(char_u *tp, int
 int mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump, int *plines_cache);
 win_T *mouse_find_win(int *rowp, int *colp, mouse_find_T popup);
 int vcol2col(win_T *wp, linenr_T lnum, int vcol);
+void f_getmousepos(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -1331,6 +1331,7 @@ func Test_getchar()
   call feedkeys('a', '')
   call assert_equal(char2nr('a'), getchar())
 
+  call setline(1, 'xxxx')
   call test_setmouse(1, 3)
   let v:mouse_win = 9
   let v:mouse_winid = 9
@@ -1342,6 +1343,7 @@ func Test_getchar()
   call assert_equal(win_getid(1), v:mouse_winid)
   call assert_equal(1, v:mouse_lnum)
   call assert_equal(3, v:mouse_col)
+  enew!
 endfunc
 
 func Test_libcall_libcallnr()
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -2205,42 +2205,106 @@ endfunc
 
 func Test_popupwin_filter_mouse()
   func MyPopupFilter(winid, c)
-    let g:got_mouse_col = v:mouse_col
-    let g:got_mouse_lnum = v:mouse_lnum
-    let g:got_mouse_winid = v:mouse_winid
+    let g:got_mousepos = getmousepos()
     return 0
   endfunc
 
-  let winid = popup_create(['short', 'long line that will wrap', 'short'], #{
-	\ line: 4,
-	\ col: 8,
+  call setline(1, ['.'->repeat(25)]->repeat(10))
+  let winid = popup_create(['short', 'long line that will wrap', 'other'], #{
+	\ line: 2,
+	\ col: 4,
 	\ maxwidth: 12,
+	\ padding: [],
+	\ border: [],
 	\ filter: 'MyPopupFilter',
 	\ })
   redraw
-  call test_setmouse(4, 8)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(1, g:got_mouse_col)
-  call assert_equal(1, g:got_mouse_lnum)
-  call assert_equal(winid, g:got_mouse_winid)
-
-  call test_setmouse(5, 8)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(1, g:got_mouse_col)
-  call assert_equal(2, g:got_mouse_lnum)
-
-  call test_setmouse(6, 8)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(13, g:got_mouse_col)
-  call assert_equal(2, g:got_mouse_lnum)
-
-  call test_setmouse(7, 20)
-  call feedkeys("\<LeftMouse>", 'xt')
-  call assert_equal(13, g:got_mouse_col)
-  call assert_equal(3, g:got_mouse_lnum)
-  call assert_equal(winid, g:got_mouse_winid)
+  "    123456789012345678901
+  "  1 .....................
+  "  2 ...+--------------+..
+  "  3 ...|              |..
+  "  4 ...| short        |..
+  "  5 ...| long line th |..
+  "  6 ...| at will wrap |..
+  "  7 ...| other        |..
+  "  8 ...|              |..
+  "  9 ...+--------------+..
+  " 10 .....................
+  let tests = []
+
+  func AddItemOutsidePopup(tests, row, col)
+    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
+	  \ screenrow: a:row, screencol: a:col,
+	  \ winid: win_getid(), winrow: a:row, wincol: a:col,
+	  \ line: a:row, column: a:col,
+	  \ }})
+  endfunc
+  func AddItemInPopupBorder(tests, winid, row, col)
+    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
+	  \ screenrow: a:row, screencol: a:col,
+	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
+	  \ line: 0, column: 0,
+	  \ }})
+  endfunc
+  func AddItemInPopupText(tests, winid, row, col, textline, textcol)
+    eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{
+	  \ screenrow: a:row, screencol: a:col,
+	  \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3,
+	  \ line: a:textline, column: a:textcol,
+	  \ }})
+  endfunc
+
+  " above and below popup
+  for c in range(1, 21)
+    call AddItemOutsidePopup(tests, 1, c)
+    call AddItemOutsidePopup(tests, 10, c)
+  endfor
+  " left and right of popup
+  for r in range(1, 10)
+    call AddItemOutsidePopup(tests, r, 3)
+    call AddItemOutsidePopup(tests, r, 20)
+  endfor
+  " top and bottom in popup
+  for c in range(4, 19)
+    call AddItemInPopupBorder(tests, winid, 2, c)
+    call AddItemInPopupBorder(tests, winid, 3, c)
+    call AddItemInPopupBorder(tests, winid, 8, c)
+    call AddItemInPopupBorder(tests, winid, 9, c)
+  endfor
+  " left and right margin in popup
+  for r in range(2, 9)
+    call AddItemInPopupBorder(tests, winid, r, 4)
+    call AddItemInPopupBorder(tests, winid, r, 5)
+    call AddItemInPopupBorder(tests, winid, r, 18)
+    call AddItemInPopupBorder(tests, winid, r, 19)
+  endfor
+  " text "short"
+  call AddItemInPopupText(tests, winid, 4, 6, 1, 1)
+  call AddItemInPopupText(tests, winid, 4, 10, 1, 5)
+  call AddItemInPopupText(tests, winid, 4, 11, 1, 6)
+  call AddItemInPopupText(tests, winid, 4, 17, 1, 6)
+  " text "long line th"
+  call AddItemInPopupText(tests, winid, 5, 6, 2, 1)
+  call AddItemInPopupText(tests, winid, 5, 10, 2, 5)
+  call AddItemInPopupText(tests, winid, 5, 17, 2, 12)
+  " text "at will wrap"
+  call AddItemInPopupText(tests, winid, 6, 6, 2, 13)
+  call AddItemInPopupText(tests, winid, 6, 10, 2, 17)
+  call AddItemInPopupText(tests, winid, 6, 17, 2, 24)
+  " text "other"
+  call AddItemInPopupText(tests, winid, 7, 6, 3, 1)
+  call AddItemInPopupText(tests, winid, 7, 10, 3, 5)
+  call AddItemInPopupText(tests, winid, 7, 11, 3, 6)
+  call AddItemInPopupText(tests, winid, 7, 17, 3, 6)
+
+  for item in tests
+    call test_setmouse(item.clickrow, item.clickcol)
+    call feedkeys("\<LeftMouse>", 'xt')
+    call assert_equal(item.result, g:got_mousepos)
+  endfor
 
   call popup_close(winid)
+  enew!
   delfunc MyPopupFilter
 endfunc
 
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2304,
+/**/
     2303,
 /**/
     2302,