summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt19
-rw-r--r--runtime/doc/popup.txt64
-rw-r--r--runtime/doc/usr_41.txt3
-rw-r--r--src/beval.c88
-rw-r--r--src/evalfunc.c2
-rw-r--r--src/move.c90
-rw-r--r--src/normal.c3
-rw-r--r--src/popupmnu.c7
-rw-r--r--src/popupwin.c190
-rw-r--r--src/proto/beval.pro1
-rw-r--r--src/proto/move.pro1
-rw-r--r--src/proto/popupwin.pro2
-rw-r--r--src/testdir/dumps/Test_popupwin_beval_1.dump10
-rw-r--r--src/testdir/dumps/Test_popupwin_beval_2.dump10
-rw-r--r--src/testdir/dumps/Test_popupwin_beval_3.dump10
-rw-r--r--src/testdir/test_cursor_func.vim28
-rw-r--r--src/testdir/test_popupwin.vim55
-rw-r--r--src/version.c2
18 files changed, 522 insertions, 63 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 24253d15dd..aa68fc7fdb 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2535,6 +2535,7 @@ or({expr}, {expr}) Number bitwise OR
pathshorten({expr}) String shorten directory names in a path
perleval({expr}) any evaluate |Perl| expression
popup_atcursor({what}, {options}) Number create popup window near the cursor
+popup_beval({what}, {options}) Number create popup window for 'ballooneval'
popup_clear() none close all popup windows
popup_close({id} [, {result}]) none close popup window {id}
popup_create({what}, {options}) Number create a popup window
@@ -2613,6 +2614,7 @@ screenattr({row}, {col}) Number attribute at screen position
screenchar({row}, {col}) Number character at screen position
screenchars({row}, {col}) List List of characters at screen position
screencol() Number current cursor column
+screenpos({winid}, {lnum}, {col}) Dict screen row and col of a text character
screenrow() Number current cursor row
screenstring({row}, {col}) String characters at screen position
search({pattern} [, {flags} [, {stopline} [, {timeout}]]])
@@ -7907,6 +7909,23 @@ screencol() *screencol()*
nnoremap <expr> GG ":echom ".screencol()."\n"
nnoremap <silent> GG :echom screencol()<CR>
<
+screenpos({winid}, {lnum}, {col}) *screenpos()*
+ The result is a Dict with the screen position of the text
+ character in window {winid} at buffer line {lnum} and column
+ {col}. {col} is a one-based byte index.
+ The Dict has these members:
+ row screen row
+ col first screen column
+ endcol last screen column
+ curscol cursor screen column
+ If the specified position is not visible, all values are zero.
+ The "endcol" value differs from "col" when the character
+ occupies more than one screen cell. E.g. for a Tab "col" can
+ be 1 and "endcol" can be 8.
+ The "curscol" value is where the cursor would be placed. For
+ a Tab it would be the same as "endcol", while for a double
+ width character it would be the same as "col".
+
screenrow() *screenrow()*
The result is a Number, which is the current screen row of the
cursor. The top line has number one.
diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt
index 3fb6f6bf8e..13674109ee 100644
--- a/runtime/doc/popup.txt
+++ b/runtime/doc/popup.txt
@@ -146,6 +146,8 @@ Creating a popup window:
|popup_create()| centered in the screen
|popup_atcursor()| just above the cursor position, closes when
the cursor moves away
+ |popup_beval()| at the position indicated by v:beval_
+ variables, closes when the mouse moves away
|popup_notification()| show a notification for three seconds
|popup_dialog()| centered with padding and border
|popup_menu()| prompt for selecting an item from a list
@@ -184,6 +186,20 @@ popup_atcursor({what}, {options}) *popup_atcursor()*
< Use {options} to change the properties.
+popup_beval({what}, {options}) *popup_beval()*
+ Show the {what} above the position from 'ballooneval' and
+ close it when the mouse moves. This works like: >
+ let pos = screenpos(v:beval_winnr, v:beval_lnum, v:beval_col)
+ call popup_create({what}, {
+ \ 'pos': 'botleft',
+ \ 'line': pos.lnum - 1,
+ \ 'col': pos.col,
+ \ 'mousemoved': 'WORD',
+ \ })
+< Use {options} to change the properties.
+ See |popup_beval_example| for an example use.
+
+
*popup_clear()*
popup_clear() Emergency solution to a misbehaving plugin: close all popup
windows for the current tab and global popups.
@@ -276,8 +292,11 @@ popup_getoptions({id}) *popup_getoptions()*
A zero value means the option was not set. For "zindex" the
default value is returned, not zero.
- The "moved" entry is a list with minimum and maximum column,
- [0, 0] when not set.
+ The "moved" entry is a list with line number, minimum and
+ maximum column, [0, 0, 0] when not set.
+
+ The "mousemoved" entry is a list with screen row, minimum and
+ maximum screen column, [0, 0, 0] when not set.
"border" and "padding" are not included when all values are
zero. When all values are one then an empty list is included.
@@ -566,6 +585,7 @@ The second argument of |popup_create()| is a dictionary with options:
- "any": if the cursor moved at all
- "word": if the cursor moved outside |<cword>|
- "WORD": if the cursor moved outside |<cWORD>|
+ - "expr": if the cursor moved outside |<cexpr>|
- [{start}, {end}]: if the cursor moved before column
{start} or after {end}
The popup also closes if the cursor moves to another
@@ -736,5 +756,45 @@ Extend popup_filter_menu() with shortcut keys: >
return popup_filter_menu(a:id, a:key)
endfunc
<
+ *popup_beval_example*
+Example for using a popup window for 'ballooneval': >
+
+ set ballooneval balloonevalterm
+ set balloonexpr=BalloonExpr()
+ let s:winid = 0
+
+ func BalloonExpr()
+ if s:winid
+ call popup_close(s:winid)
+ let s:winid = 0
+ endif
+ let s:winid = popup_beval([bufname(v:beval_bufnr), v:beval_text], {})
+ return ''
+ endfunc
+<
+If the text has to be obtained asynchronously return an empty string from the
+expression function and call popup_beval() once the text is available. In
+this example similated with a timer callback: >
+
+ set ballooneval balloonevalterm
+ set balloonexpr=BalloonExpr()
+ let s:winid = 0
+
+ func BalloonExpr()
+ if s:winid
+ call popup_close(s:winid)
+ let s:winid = 0
+ endif
+ " simulate an asynchronous loopup for the text to display
+ let s:balloonFile = bufname(v:beval_bufnr)
+ let s:balloonWord = v:beval_text
+ call timer_start(100, 'ShowPopup')
+ return ''
+ endfunc
+
+ func ShowPopup(id)
+ let s:winid = popup_beval([s:balloonFile, s:balloonWord], {})
+ endfunc
+<
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 6a4e4a66fd..34d60f101b 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -720,6 +720,7 @@ Cursor and mark position: *cursor-functions* *mark-functions*
cursor() position the cursor at a line/column
screencol() get screen column of the cursor
screenrow() get screen row of the cursor
+ screenpos() screen row and col of a text character
getcurpos() get position of the cursor
getpos() get position of cursor, mark, etc.
setpos() set position of cursor, mark, etc.
@@ -1046,6 +1047,8 @@ Popup window: *popup-window-functions*
popup_create() create popup centered in the screen
popup_atcursor() create popup just above the cursor position,
closes when the cursor moves away
+ popup_beval() at the position indicated by v:beval_
+ variables, closes when the mouse moves away
popup_notification() show a notification for three seconds
popup_dialog() create popup centered with padding and border
popup_menu() prompt for selecting an item from a list
diff --git a/src/beval.c b/src/beval.c
index e89b1fe50b..2cbe7ea1f0 100644
--- a/src/beval.c
+++ b/src/beval.c
@@ -14,7 +14,7 @@
/*
* Get the text and position to be evaluated for "beval".
- * If "getword" is true the returned text is not the whole line but the
+ * If "getword" is TRUE the returned text is not the whole line but the
* relevant word in allocated memory.
* Returns OK or FAIL.
*/
@@ -27,12 +27,8 @@ get_beval_info(
char_u **textp,
int *colp)
{
- win_T *wp;
int row, col;
- char_u *lbuf;
- linenr_T lnum;
- *textp = NULL;
# ifdef FEAT_BEVAL_TERM
# ifdef FEAT_GUI
if (!gui.in_use)
@@ -49,22 +45,68 @@ get_beval_info(
col = X_2_COL(beval->x);
}
#endif
+ if (find_word_under_cursor(row, col, getword,
+ FIND_IDENT + FIND_STRING + FIND_EVAL,
+ winp, lnump, textp, colp) == OK)
+ {
+#ifdef FEAT_VARTABS
+ vim_free(beval->vts);
+ beval->vts = tabstop_copy((*winp)->w_buffer->b_p_vts_array);
+ if ((*winp)->w_buffer->b_p_vts_array != NULL && beval->vts == NULL)
+ {
+ if (getword)
+ vim_free(*textp);
+ return FAIL;
+ }
+#endif
+ beval->ts = (*winp)->w_buffer->b_p_ts;
+ return OK;
+ }
+
+ return FAIL;
+}
+
+/*
+ * Find text under the mouse position "row" / "col".
+ * If "getword" is TRUE the returned text in "*textp" is not the whole line but
+ * the relevant word in allocated memory.
+ * Return OK if found.
+ * Return FAIL if not found, no text at the mouse position.
+ */
+ int
+find_word_under_cursor(
+ int mouserow,
+ int mousecol,
+ int getword,
+ int flags, // flags for find_ident_at_pos()
+ win_T **winp, // can be NULL
+ linenr_T *lnump, // can be NULL
+ char_u **textp,
+ int *colp)
+{
+ int row = mouserow;
+ int col = mousecol;
+ win_T *wp;
+ char_u *lbuf;
+ linenr_T lnum;
+
+ *textp = NULL;
wp = mouse_find_win(&row, &col, FAIL_POPUP);
if (wp != NULL && row >= 0 && row < wp->w_height && col < wp->w_width)
{
- /* Found a window and the cursor is in the text. Now find the line
- * number. */
+ // Found a window and the cursor is in the text. Now find the line
+ // number.
if (!mouse_comp_pos(wp, &row, &col, &lnum))
{
- /* Not past end of the file. */
+ // Not past end of the file.
lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE);
if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL))
{
- /* Not past end of line. */
+ // Not past end of line.
if (getword)
{
- /* For Netbeans we get the relevant part of the line
- * instead of the whole line. */
+ // For Netbeans we get the relevant part of the line
+ // instead of the whole line.
int len;
pos_T *spos = NULL, *epos = NULL;
@@ -93,9 +135,9 @@ get_beval_info(
? col <= (int)epos->col
: lnum < epos->lnum))
{
- /* Visual mode and pointing to the line with the
- * Visual selection: return selected text, with a
- * maximum of one line. */
+ // Visual mode and pointing to the line with the
+ // Visual selection: return selected text, with a
+ // maximum of one line.
if (spos->lnum != epos->lnum || spos->col == epos->col)
return FAIL;
@@ -109,10 +151,10 @@ get_beval_info(
}
else
{
- /* Find the word under the cursor. */
+ // Find the word under the cursor.
++emsg_off;
len = find_ident_at_pos(wp, lnum, (colnr_T)col, &lbuf,
- FIND_IDENT + FIND_STRING + FIND_EVAL);
+ flags);
--emsg_off;
if (len == 0)
return FAIL;
@@ -120,22 +162,16 @@ get_beval_info(
}
}
- *winp = wp;
- *lnump = lnum;
+ if (winp != NULL)
+ *winp = wp;
+ if (lnump != NULL)
+ *lnump = lnum;
*textp = lbuf;
*colp = col;
-#ifdef FEAT_VARTABS
- vim_free(beval->vts);
- beval->vts = tabstop_copy(wp->w_buffer->b_p_vts_array);
- if (wp->w_buffer->b_p_vts_array != NULL && beval->vts == NULL)
- return FAIL;
-#endif
- beval->ts = wp->w_buffer->b_p_ts;
return OK;
}
}
}
-
return FAIL;
}
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 925040d2b0..b6998993f6 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -771,6 +771,7 @@ static struct fst
#endif
#ifdef FEAT_TEXT_PROP
{"popup_atcursor", 2, 2, f_popup_atcursor},
+ {"popup_beval", 2, 2, f_popup_beval},
{"popup_clear", 0, 0, f_popup_clear},
{"popup_close", 1, 2, f_popup_close},
{"popup_create", 2, 2, f_popup_create},
@@ -849,6 +850,7 @@ static struct fst
{"screenchar", 2, 2, f_screenchar},
{"screenchars", 2, 2, f_screenchars},
{"screencol", 0, 0, f_screencol},
+ {"screenpos", 3, 3, f_screenpos},
{"screenrow", 0, 0, f_screenrow},
{"screenstring", 2, 2, f_screenstring},
{"search", 1, 4, f_search},
diff --git a/src/move.c b/src/move.c
index 232a87506d..0df4fe5ba4 100644
--- a/src/move.c
+++ b/src/move.c
@@ -1189,6 +1189,96 @@ curs_columns(
curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
}
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Compute the screen position of text character at "pos" in window "wp"
+ * The resulting values are one-based, zero when character is not visible.
+ */
+ static void
+textpos2screenpos(
+ win_T *wp,
+ pos_T *pos,
+ int *rowp, // screen row
+ int *scolp, // start screen column
+ int *ccolp, // cursor screen column
+ int *ecolp) // end screen column
+{
+ colnr_T scol = 0, ccol = 0, ecol = 0;
+ int row = 0;
+ int rowoff = 0;
+ colnr_T coloff = 0;
+
+ if (pos->lnum >= wp->w_topline && pos->lnum < wp->w_botline)
+ {
+ colnr_T off;
+ colnr_T col;
+ int width;
+
+ row = plines_m_win(wp, wp->w_topline, pos->lnum - 1) + 1;
+ getvcol(wp, pos, &scol, &ccol, &ecol);
+
+ // similar to what is done in validate_cursor_col()
+ col = scol;
+ off = win_col_off(wp);
+ col += off;
+ width = wp->w_width - off + win_col_off2(wp);
+
+ /* long line wrapping, adjust row */
+ if (wp->w_p_wrap
+ && col >= (colnr_T)wp->w_width
+ && width > 0)
+ {
+ /* use same formula as what is used in curs_columns() */
+ rowoff = ((col - wp->w_width) / width + 1);
+ col -= rowoff * width;
+ }
+ col -= wp->w_leftcol;
+ if (col >= width)
+ col = -1;
+ if (col >= 0)
+ coloff = col - scol + wp->w_wincol + 1;
+ else
+ // character is left or right of the window
+ row = scol = ccol = ecol = 0;
+ }
+ *rowp = wp->w_winrow + row + rowoff;
+ *scolp = scol + coloff;
+ *ccolp = ccol + coloff;
+ *ecolp = ecol + coloff;
+}
+
+/*
+ * "screenpos({winid}, {lnum}, {col})" function
+ */
+ void
+f_screenpos(typval_T *argvars UNUSED, typval_T *rettv)
+{
+ dict_T *dict;
+ win_T *wp;
+ pos_T pos;
+ int row = 0;
+ int scol = 0, ccol = 0, ecol = 0;
+
+ if (rettv_dict_alloc(rettv) != OK)
+ return;
+ dict = rettv->vval.v_dict;
+
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL)
+ return;
+
+ pos.lnum = tv_get_number(&argvars[1]);
+ pos.col = tv_get_number(&argvars[2]) - 1;
+ pos.coladd = 0;
+ textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol);
+
+ dict_add_number(dict, "row", row);
+ dict_add_number(dict, "col", scol);
+ dict_add_number(dict, "curscol", ccol);
+ dict_add_number(dict, "endcol", ecol);
+}
+#endif
+
/*
* Scroll the current window down by "line_count" logical lines. "CTRL-Y"
*/
diff --git a/src/normal.c b/src/normal.c
index 5c9f929d55..f626f45dc1 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -2329,6 +2329,9 @@ do_mouse(
bevalexpr_due_set = TRUE;
}
#endif
+#ifdef FEAT_TEXT_PROP
+ popup_handle_mouse_moved();
+#endif
return FALSE;
}
diff --git a/src/popupmnu.c b/src/popupmnu.c
index 5fbb3bbd28..9569ffdfc4 100644
--- a/src/popupmnu.c
+++ b/src/popupmnu.c
@@ -992,8 +992,6 @@ pum_position_at_mouse(int min_width)
# if defined(FEAT_BEVAL_TERM) || defined(PROTO)
static pumitem_T *balloon_array = NULL;
static int balloon_arraysize;
-static int balloon_mouse_row = 0;
-static int balloon_mouse_col = 0;
#define BALLOON_MIN_WIDTH 50
#define BALLOON_MIN_HEIGHT 10
@@ -1209,8 +1207,9 @@ ui_post_balloon(char_u *mesg, list_T *list)
void
ui_may_remove_balloon(void)
{
- if (mouse_row != balloon_mouse_row || mouse_col != balloon_mouse_col)
- ui_remove_balloon();
+ // For now: remove the balloon whenever the mouse moves to another screen
+ // cell.
+ ui_remove_balloon();
}
# endif
diff --git a/src/popupwin.c b/src/popupwin.c
index 02aa83edad..401f1fbea9 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -168,6 +168,35 @@ set_moved_columns(win_T *wp, int flags)
}
/*
+ * Used when popup options contain "mousemoved": set default moved values.
+ */
+ static void
+set_mousemoved_values(win_T *wp)
+{
+ wp->w_popup_mouse_row = mouse_row;
+ wp->w_popup_mouse_mincol = mouse_col;
+ wp->w_popup_mouse_maxcol = mouse_col;
+}
+
+/*
+ * Used when popup options contain "moved" with "word" or "WORD".
+ */
+ static void
+set_mousemoved_columns(win_T *wp, int flags)
+{
+ char_u *text;
+ int col;
+
+ if (find_word_under_cursor(mouse_row, mouse_col, TRUE, flags,
+ NULL, NULL, &text, &col) == OK)
+ {
+ wp->w_popup_mouse_mincol = col;
+ wp->w_popup_mouse_maxcol = col + STRLEN(text) - 1;
+ vim_free(text);
+ }
+}
+
+/*
* Return TRUE if "row"/"col" is on the border of the popup.
* The values are relative to the top-left corner.
*/
@@ -336,6 +365,53 @@ apply_move_options(win_T *wp, dict_T *d)
}
static void
+handle_moved_argument(win_T *wp, dictitem_T *di, int mousemoved)
+{
+ 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, "expr") == 0)
+ flags = FIND_IDENT | FIND_STRING | FIND_EVAL;
+ else if (STRCMP(s, "any") != 0)
+ semsg(_(e_invarg2), s);
+ if (flags != 0)
+ {
+ if (mousemoved)
+ set_mousemoved_columns(wp, flags);
+ else
+ set_moved_columns(wp, flags);
+ }
+ }
+ 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;
+ int mincol = tv_get_number(&l->lv_first->li_tv);
+ int maxcol = tv_get_number(&l->lv_first->li_next->li_tv);
+
+ if (mousemoved)
+ {
+ wp->w_popup_mouse_mincol = mincol;
+ wp->w_popup_mouse_maxcol = maxcol;
+ }
+ else
+ {
+ wp->w_popup_mincol = mincol;
+ wp->w_popup_maxcol = maxcol;
+ }
+ }
+ else
+ semsg(_(e_invarg2), tv_get_string(&di->di_tv));
+}
+
+ static void
check_highlight(dict_T *dict, char *name, char_u **pval)
{
dictitem_T *di;
@@ -541,31 +617,14 @@ apply_general_options(win_T *wp, dict_T *dict)
if (di != NULL)
{
set_moved_values(wp);
- 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)
- set_moved_columns(wp, flags);
- }
- 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;
+ handle_moved_argument(wp, di, FALSE);
+ }
- 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));
+ di = dict_find(dict, (char_u *)"mousemoved", -1);
+ if (di != NULL)
+ {
+ set_mousemoved_values(wp);
+ handle_moved_argument(wp, di, TRUE);
}
di = dict_find(dict, (char_u *)"filter", -1);
@@ -956,6 +1015,7 @@ typedef enum
{
TYPE_NORMAL,
TYPE_ATCURSOR,
+ TYPE_BEVAL,
TYPE_NOTIFICATION,
TYPE_DIALOG,
TYPE_MENU
@@ -1137,17 +1197,33 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
{
wp->w_popup_pos = POPPOS_BOTLEFT;
setcursor_mayforce(TRUE);
- wp->w_wantline = screen_screenrow();
+ wp->w_wantline = curwin->w_winrow + curwin->w_wrow;
if (wp->w_wantline == 0) // cursor in first line
{
wp->w_wantline = 2;
wp->w_popup_pos = POPPOS_TOPLEFT;
}
- wp->w_wantcol = screen_screencol() + 1;
+ wp->w_wantcol = curwin->w_wincol + curwin->w_wcol + 1;
set_moved_values(wp);
set_moved_columns(wp, FIND_STRING);
}
+ if (type == TYPE_BEVAL)
+ {
+ wp->w_popup_pos = POPPOS_BOTLEFT;
+
+ // by default use the mouse position
+ wp->w_wantline = mouse_row;
+ if (wp->w_wantline <= 0) // mouse on first line
+ {
+ wp->w_wantline = 2;
+ wp->w_popup_pos = POPPOS_TOPLEFT;
+ }
+ wp->w_wantcol = mouse_col + 1;
+ set_mousemoved_values(wp);
+ set_mousemoved_columns(wp, FIND_IDENT + FIND_STRING + FIND_EVAL);
+ }
+
// set default values
wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
wp->w_popup_close = POPCLOSE_NONE;
@@ -1276,6 +1352,15 @@ f_popup_atcursor(typval_T *argvars, typval_T *rettv)
}
/*
+ * popup_beval({text}, {options})
+ */
+ void
+f_popup_beval(typval_T *argvars, typval_T *rettv)
+{
+ popup_create(argvars, rettv, TYPE_BEVAL);
+}
+
+/*
* Invoke the close callback for window "wp" with value "result".
* Careful: The callback may make "wp" invalid!
*/
@@ -1334,6 +1419,48 @@ popup_close_for_mouse_click(win_T *wp)
popup_close_and_callback(wp, &res);
}
+ static void
+check_mouse_moved(win_T *wp, win_T *mouse_wp)
+{
+ // Close the popup when all if these are true:
+ // - the mouse is not on this popup
+ // - "mousemoved" was used
+ // - the mouse is no longer on the same screen row or the mouse column is
+ // outside of the relevant text
+ if (wp != mouse_wp
+ && wp->w_popup_mouse_row != 0
+ && (wp->w_popup_mouse_row != mouse_row
+ || mouse_col < wp->w_popup_mouse_mincol
+ || mouse_col > wp->w_popup_mouse_maxcol))
+ {
+ typval_T res;
+
+ res.v_type = VAR_NUMBER;
+ res.vval.v_number = -2;
+ popup_close_and_callback(wp, &res);
+ }
+}
+
+/*
+ * Called when the mouse moved: may close a popup with "mousemoved".
+ */
+ void
+popup_handle_mouse_moved(void)
+{
+ win_T *wp;
+ win_T *mouse_wp;
+ int row = mouse_row;
+ int col = mouse_col;
+
+ // find the window where the mouse is in
+ mouse_wp = mouse_find_win(&row, &col, FIND_POPUP);
+
+ for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+ check_mouse_moved(wp, mouse_wp);
+ for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
+ check_mouse_moved(wp, mouse_wp);
+}
+
/*
* In a filter: check if the typed key is a mouse event that is used for
* dragging the popup.
@@ -1821,7 +1948,7 @@ get_borderchars(dict_T *dict, win_T *wp)
}
/*
- * For popup_getoptions(): add a "moved" entry to "dict".
+ * For popup_getoptions(): add a "moved" and "mousemoved" entry to "dict".
*/
static void
get_moved_list(dict_T *dict, win_T *wp)
@@ -1832,9 +1959,18 @@ get_moved_list(dict_T *dict, win_T *wp)
if (list != NULL)
{
dict_add_list(dict, "moved", list);
+ list_append_number(list, wp->w_popup_lnum);
list_append_number(list, wp->w_popup_mincol);
list_append_number(list, wp->w_popup_maxcol);
}
+ list = list_alloc();
+ if (list != NULL)
+ {
+ dict_add_list(dict, "mousemoved", list);
+ list_append_number(list, wp->w_popup_mouse_row);
+ list_append_number(list, wp->w_popup_mouse_mincol);
+ list_append_number(list, wp->w_popup_mouse_maxcol);
+ }
}
/*
diff --git a/src/proto/beval.pro b/src/proto/beval.pro
index 2be64a0da1..0238907ea2 100644
--- a/src/proto/beval.pro
+++ b/src/proto/beval.pro
@@ -1,5 +1,6 @@
/* beval.c */
int get_beval_info(BalloonEval *beval, int getword, win_T **winp, linenr_T *lnump, char_u **textp, int *colp);
+int find_word_under_cursor(int mouserow, int mousecol, int getword, int flags, win_T **winp, linenr_T *lnump, char_u **textp, int *colp);
void post_balloon(BalloonEval *beval, char_u *mesg, list_T *list);
int can_use_beval(void);
void general_beval_cb(BalloonEval *beval, int state);
diff --git a/src/proto/move.pro b/src/proto/move.pro
index ed45c4dc1e..c2ec8d5953 100644
--- a/src/proto/move.pro
+++ b/src/proto/move.pro
@@ -27,6 +27,7 @@ int curwin_col_off(void);
int win_col_off2(win_T *wp);
int curwin_col_off2(void);
void curs_columns(int may_scroll);
+void f_screenpos(typval_T *argvars, typval_T *rettv);
void scrolldown(long line_count, int byfold);
void scrollup(long line_count, int byfold);
void check_topfill(win_T *wp, int down);
diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro
index a251f9ad56..7733368578 100644
--- a/src/proto/popupwin.pro
+++ b/src/proto/popupwin.pro
@@ -11,7 +11,9 @@ void popup_adjust_position(win_T *wp);
void f_popup_clear(typval_T *argvars, typval_T *rettv);
void f_popup_create(typval_T *argvars, typval_T *rettv);
void f_popup_atcursor(typval_T *argvars, typval_T *rettv);
+void f_popup_beval(typval_T *argvars, typval_T *rettv);
void popup_close_for_mouse_click(win_T *wp);
+void popup_handle_mouse_moved(void);
void f_popup_filter_menu(typval_T *argvars, typval_T *rettv);
void f_popup_filter_yesno(typval_T *argvars, typval_T *rettv);
void f_popup_dialog(typval_T *argvars, typval_T *rettv);
diff --git a/src/testdir/dumps/Test_popupwin_beval_1.dump b/src/testdir/dumps/Test_popupwin_beval_1.dump
new file mode 100644
index 0000000000..410ac5c434
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_beval_1.dump
@@ -0,0 +1,10 @@
+|1+0&#ffffff0| @73
+>2| @73
+|3| @73
+|4| @12|t+0#0000001#ffd7ff255|e|x|t| +0#0000000#ffffff0@56
+|h|e|r|e| |i|s| |s|o|m|e| |t|e|x|t| |t|o| |h|o|v|e|r| |o|v|e|r| @43
+|6| @73
+|7| @73
+|8| @73
+|9| @73
+|:|c|a|l@1| |H|o|v|e|r|(|)| @43|2|,|1| @10|T|o|p|
diff --git a/src/testdir/dumps/Test_popupwin_beval_2.dump b/src/testdir/dumps/Test_popupwin_beval_2.dump
new file mode 100644
index 0000000000..34b222d181
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_beval_2.dump
@@ -0,0 +1,10 @@
+|1+0&#ffffff0| @73
+>2| @73
+|3| @73
+|4| @12|t+0#0000001#ffd7ff255|e|x|t| +0#0000000#ffffff0@56
+|h|e|r|e| |i|s| |s|o|m|e| |t|e|x|t| |t|o| |h|o|v|e|r| |o|v|e|r| @43
+|6| @73
+|7| @73
+|8| @73
+|9| @73
+|:|c|a|l@1| |M|o|v|e|O|n|t|o|P|o|p|u|p|(|)| @35|2|,|1| @10|T|o|p|
diff --git a/src/testdir/dumps/Test_popupwin_beval_3.dump b/src/testdir/dumps/Test_popupwin_beval_3.dump
new file mode 100644
index 0000000000..2e8e419354
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_beval_3.dump
@@ -0,0 +1,10 @@
+|1+0&#ffffff0| @73
+>2| @73
+|3| @73
+|4| @73
+|h|e|r|e| |i|s| |s|o|m|e| |t|e|x|t| |t|o| |h|o|v|e|r| |o|v|e|r| @43
+|6| @73
+|7| @73
+|8| @73
+|9| @73
+|:|c|a|l@1| |M|o|v|e|A|w|a|y|(|)| @40|2|,|1| @10|T|o|p|
diff --git a/src/testdir/test_cursor_func.vim b/src/testdir/test_cursor_func.vim
index 12319577c6..0f638b3575 100644
--- a/src/testdir/test_cursor_func.vim
+++ b/src/testdir/test_cursor_func.vim
@@ -72,3 +72,31 @@ func Test_curswant_with_cursorline()
call assert_equal(6, winsaveview().curswant)
quit!
endfunc
+
+func Test_screenpos()
+ rightbelow new
+ rightbelow 20vsplit
+ call setline(1, ["\tsome text", "long wrapping line here", "next line"])
+ redraw
+ let winid = win_getid()
+ let [winrow, wincol] = win_screenpos(winid)
+ call assert_equal({'row': winrow,
+ \ 'col': wincol + 0,
+ \ 'curscol': wincol + 7,
+ \ 'endcol': wincol + 7}, screenpos(winid, 1, 1))
+ call assert_equal({'row': winrow,
+ \ 'col': wincol + 13,
+ \ 'curscol': wincol + 13,
+ \ 'endcol': wincol + 13}, screenpos(winid, 1, 7))
+ call assert_equal({'row': winrow + 2,
+ \ 'col': wincol + 1,
+ \ 'curscol': wincol + 1,
+ \ 'endcol': wincol + 1}, screenpos(winid, 2, 22))
+ setlocal number
+ call assert_equal({'row': winrow + 3,
+ \ 'col': wincol + 9,
+ \ 'curscol': wincol + 9,
+ \ 'endcol': wincol + 9}, screenpos(winid, 2, 22))
+ close
+ bwipe!
+endfunc
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 00cc2878e4..f952a42fe7 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -1005,6 +1005,53 @@ func Test_popup_atcursor()
bwipe!
endfunc
+func Test_popup_beval()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot make screendumps'
+ endif
+
+ let lines =<< trim END
+ call setline(1, range(1, 20))
+ call setline(5, 'here is some text to hover over')
+ set balloonevalterm
+ set balloonexpr=BalloonExpr()
+ set balloondelay=100
+ func BalloonExpr()
+ let s:winid = popup_beval([v:beval_text], {})
+ return ''
+ endfunc
+ func Hover()
+ call test_setmouse(5, 15)
+ call feedkeys("\<MouseMove>\<Ignore>", "xt")
+ sleep 100m
+ endfunc
+ func MoveOntoPopup()
+ call test_setmouse(4, 17)
+ call feedkeys("\<F4>\<MouseMove>\<Ignore>", "xt")
+ endfunc
+ func MoveAway()
+ call test_setmouse(5, 13)
+ call feedkeys("\<F5>\<MouseMove>\<Ignore>", "xt")
+ endfunc
+ END
+ call writefile(lines, 'XtestPopupBeval')
+ let buf = RunVimInTerminal('-S XtestPopupBeval', {'rows': 10})
+ call term_wait(buf, 100)
+ call term_sendkeys(buf, 'j')
+ call term_sendkeys(buf, ":call Hover()\<CR>")
+ call VerifyScreenDump(buf, 'Test_popupwin_beval_1', {})
+
+ call term_sendkeys(buf, ":call MoveOntoPopup()\<CR>")
+ call VerifyScreenDump(buf, 'Test_popupwin_beval_2', {})
+
+ call term_sendkeys(buf, ":call MoveAway()\<CR>")
+ call VerifyScreenDump(buf, 'Test_popupwin_beval_3', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XtestPopupBeval')
+endfunc
+
func Test_popup_filter()
new
call setline(1, 'some text')
@@ -1413,7 +1460,7 @@ func Test_popup_moved()
let winid = popup_atcursor('text', {'moved': 'any'})
redraw
call assert_equal(1, popup_getpos(winid).visible)
- call assert_equal([4, 4], popup_getoptions(winid).moved)
+ call assert_equal([1, 4, 4], popup_getoptions(winid).moved)
" trigger the check for last_cursormoved by going into insert mode
call feedkeys("li\<Esc>", 'xt')
call assert_equal({}, popup_getpos(winid))
@@ -1423,7 +1470,7 @@ func Test_popup_moved()
let winid = popup_atcursor('text', {'moved': 'word'})
redraw
call assert_equal(1, popup_getpos(winid).visible)
- call assert_equal([4, 7], popup_getoptions(winid).moved)
+ call assert_equal([1, 4, 7], popup_getoptions(winid).moved)
call feedkeys("hi\<Esc>", 'xt')
call assert_equal({}, popup_getpos(winid))
call popup_clear()
@@ -1432,7 +1479,7 @@ func Test_popup_moved()
let winid = popup_atcursor('text', {'moved': 'word'})
redraw
call assert_equal(1, popup_getpos(winid).visible)<