]> granicus.if.org Git - vim/commitdiff
patch 8.1.1645: cannot use a popup window for a balloon v8.1.1645
authorBram Moolenaar <Bram@vim.org>
Sun, 7 Jul 2019 16:28:14 +0000 (18:28 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 7 Jul 2019 16:28:14 +0000 (18:28 +0200)
Problem:    Cannot use a popup window for a balloon.
Solution:   Add popup_beval().  Add the "mousemoved" property.  Add the
            screenpos() function.

18 files changed:
runtime/doc/eval.txt
runtime/doc/popup.txt
runtime/doc/usr_41.txt
src/beval.c
src/evalfunc.c
src/move.c
src/normal.c
src/popupmnu.c
src/popupwin.c
src/proto/beval.pro
src/proto/move.pro
src/proto/popupwin.pro
src/testdir/dumps/Test_popupwin_beval_1.dump [new file with mode: 0644]
src/testdir/dumps/Test_popupwin_beval_2.dump [new file with mode: 0644]
src/testdir/dumps/Test_popupwin_beval_3.dump [new file with mode: 0644]
src/testdir/test_cursor_func.vim
src/testdir/test_popupwin.vim
src/version.c

index 24253d15ddafe7f884e02f9d6bbe6c699241fca5..aa68fc7fdb4a57e8a8b814b6a26aab969acb544d 100644 (file)
@@ -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.
index 3fb6f6bf8e95e10f04f2a253862dd2033f73c837..13674109eedf6180e6f9fc30e53a1577c7bf73f5 100644 (file)
@@ -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:
index 6a4e4a66fd0e9059b118ef771bbb41cf5aae645a..34d60f101ba01124235b52ddc01cc6b64fbe5f4a 100644 (file)
@@ -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
index e89b1fe50b57cc08f246968df1e5aeb6b26273ad..2cbe7ea1f0539ce337bb7a0aa8cfd73ea0c8d4e7 100644 (file)
@@ -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;
 }
 
index 925040d2b04d9d9b7446db0453e9df283d54be7c..b6998993f61a5c5788ae075196537665b280725a 100644 (file)
@@ -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},
index 232a87506d6a93281f992aed63d7b0f39add7f6c..0df4fe5ba4ded23b71a861db98ab6ee62aee54d8 100644 (file)
@@ -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"
  */
index 5c9f929d55a9a2229c5cdee3a5adb8799611288c..f626f45dc1e02dce5fa2d45f56b15c25370474d2 100644 (file)
@@ -2328,6 +2328,9 @@ do_mouse(
            profile_setlimit(p_bdlay, &bevalexpr_due);
            bevalexpr_due_set = TRUE;
        }
+#endif
+#ifdef FEAT_TEXT_PROP
+       popup_handle_mouse_moved();
 #endif
        return FALSE;
     }
index 5fbb3bbd284f8db8df96dff703c434d8f460c469..9569ffdfc41eb0003c4c462b75a717dcc373bc6e 100644 (file)
@@ -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
 
index 02aa83edad1396939d8a0c92c01f0dc7e1f96cee..401f1fbea9d62c5c031399d90a5447009bbf2ef5 100644 (file)
@@ -167,6 +167,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.
@@ -335,6 +364,53 @@ apply_move_options(win_T *wp, dict_T *d)
     get_pos_options(wp, 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)
 {
@@ -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;
@@ -1275,6 +1351,15 @@ f_popup_atcursor(typval_T *argvars, typval_T *rettv)
     popup_create(argvars, rettv, TYPE_ATCURSOR);
 }
 
+/*
+ * 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);
+    }
 }
 
 /*
index 2be64a0da195d64f3c06365068ba36214d62dcdb..0238907ea2f2ee1f85a36e0d1b9e290842c86518 100644 (file)
@@ -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);
index ed45c4dc1e97a880b8ba54b6ba907c75bb40062b..c2ec8d5953e37db6127b2673688ff9b1da3f08d7 100644 (file)
@@ -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);
index a251f9ad567b69c3125965be9933ece8faf268f0..7733368578334d789c057ef3ee158104021f9706 100644 (file)
@@ -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 (file)
index 0000000..410ac5c
--- /dev/null
@@ -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 (file)
index 0000000..34b222d
--- /dev/null
@@ -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 (file)
index 0000000..2e8e419
--- /dev/null
@@ -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| 
index 12319577c6193c8b4c214ea91d62e418fc610f03..0f638b357534f6ba77f04cd792b3712b51afe80d 100644 (file)
@@ -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
index 00cc2878e457e708d6c682cff9a8f310c9abeb27..f952a42fe7bd261e3487ed29b0f9aeed3f59e988 100644 (file)
@@ -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)
-  call assert_equal([4, 7], popup_getoptions(winid).moved)
+  call assert_equal([1, 4, 7], popup_getoptions(winid).moved)
   call feedkeys("li\<Esc>", 'xt')
   call assert_equal(1, popup_getpos(winid).visible)
   call feedkeys("ei\<Esc>", 'xt')
@@ -1446,7 +1493,7 @@ func Test_popup_moved()
   let winid = popup_atcursor('text', {})
   redraw
   call assert_equal(1, popup_getpos(winid).visible)
-  call assert_equal([2, 15], popup_getoptions(winid).moved)
+  call assert_equal([2, 2, 15], popup_getoptions(winid).moved)
   call feedkeys("eli\<Esc>", 'xt')
   call assert_equal(1, popup_getpos(winid).visible)
   call feedkeys("wi\<Esc>", 'xt')
index 754155bfd2f90cd2073df890135218c3033030c4..61651330c09f1479115a265c7c31c0fed221b8af 100644 (file)
@@ -777,6 +777,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1645,
 /**/
     1644,
 /**/