# HG changeset patch # User Bram Moolenaar # Date 1573925404 -3600 # Node ID 3089b422b9dc3450a5e6de173e34bcdf4cd02d6b # Parent 5beb893fc3db5478534a41885ef8a1f9d2b2c68f 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 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(). diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- 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 == "\" && 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 diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt --- 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 . 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 . The coordinates can be obtained with +|mousegetpos()|. Vim provides standard filters |popup_filter_menu()| and |popup_filter_yesno()|. diff --git a/src/evalfunc.c b/src/evalfunc.c --- 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}, diff --git a/src/mouse.c b/src/mouse.c --- 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 diff --git a/src/popupwin.c b/src/popupwin.c --- 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; diff --git a/src/proto/mouse.pro b/src/proto/mouse.pro --- 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 : */ diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- 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() 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 @@ -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("\", '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("\", 'xt') - call assert_equal(1, g:got_mouse_col) - call assert_equal(2, g:got_mouse_lnum) - - call test_setmouse(6, 8) - call feedkeys("\", 'xt') - call assert_equal(13, g:got_mouse_col) - call assert_equal(2, g:got_mouse_lnum) - - call test_setmouse(7, 20) - call feedkeys("\", '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("\", 'xt') + call assert_equal(item.result, g:got_mousepos) + endfor call popup_close(winid) + enew! delfunc MyPopupFilter endfunc diff --git a/src/version.c b/src/version.c --- 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,