# HG changeset patch # User Bram Moolenaar # Date 1562517005 -7200 # Node ID 8a095d343c59179f1e9f2bb2d99983e6a8aa58f6 # Parent 4feeb79228ab586aca623d885aee91254dd5c1dc patch 8.1.1645: cannot use a popup window for a balloon commit https://github.com/vim/vim/commit/b3d17a20d243f65bcfe23de08b7afd948c5132c2 Author: Bram Moolenaar Date: Sun Jul 7 18:28:14 2019 +0200 patch 8.1.1645: cannot use a popup window for a balloon Problem: Cannot use a popup window for a balloon. Solution: Add popup_beval(). Add the "mousemoved" property. Add the screenpos() function. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- 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 attribut 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 GG ":echom ".screencol()."\n" nnoremap GG :echom screencol() < +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 --- 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}) *pop < 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_getopt 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()| - "any": if the cursor moved at all - "word": if the cursor moved outside || - "WORD": if the cursor moved outside || + - "expr": if the cursor moved outside || - [{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 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 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -720,6 +720,7 @@ Cursor and mark position: *cursor-funct 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-function 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 --- 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 --- 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 --- 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 --- 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 --- 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 *li 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 --- 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 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; + handle_moved_argument(wp, di, FALSE); + } - 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; - - 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 { 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, typv } /* + * 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 --- 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 --- 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 --- 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 --- /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 --- /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 --- /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 --- 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 --- 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("\\", "xt") + sleep 100m + endfunc + func MoveOntoPopup() + call test_setmouse(4, 17) + call feedkeys("\\\", "xt") + endfunc + func MoveAway() + call test_setmouse(5, 13) + call feedkeys("\\\", "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()\") + call VerifyScreenDump(buf, 'Test_popupwin_beval_1', {}) + + call term_sendkeys(buf, ":call MoveOntoPopup()\") + call VerifyScreenDump(buf, 'Test_popupwin_beval_2', {}) + + call term_sendkeys(buf, ":call MoveAway()\") + 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\", '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\", '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\", 'xt') call assert_equal(1, popup_getpos(winid).visible) call feedkeys("ei\", '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\", 'xt') call assert_equal(1, popup_getpos(winid).visible) call feedkeys("wi\", 'xt') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -778,6 +778,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1645, +/**/ 1644, /**/ 1643,