diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2b5078febf..ba5c4786c0 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 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 index 3fb6f6bf8e..ba4dc3fc15 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.row - 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 || - "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,56 @@ 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 + let s:last_text = '' + + func BalloonExpr() + if s:winid && popup_getpos(s:winid) != {} + " previous popup window still shows + if v:beval_text == s:last_text + " Still the same text, keep the existing popup + return '' + endif + call popup_close(s:winid) + endif + let s:winid = popup_beval(v:beval_text, {'mousemoved': 'word'}) + let s:last_text = 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 simulated with a timer callback: > + + set ballooneval balloonevalterm + set balloonexpr=BalloonExpr() + let s:winid = 0 + let s:balloonText = '' + + func BalloonExpr() + if s:winid && popup_getpos(s:winid) != {} + " previous popup window still shows + if v:beval_text == s:balloonText + " Still the same text, keep the existing popup + return '' + endif + call popup_close(s:winid) + let s:winid = 0 + endif + " simulate an asynchronous loopup for the text to display + let s:balloonText = v:beval_text + call timer_start(100, 'ShowPopup') + return '' + endfunc + + func ShowPopup(id) + let s:winid = popup_beval(s:balloonText, {'mousemoved': 'word'}) + 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..dc8966fe2f 100644 --- a/src/beval.c +++ b/src/beval.c @@ -10,61 +10,50 @@ #include "vim.h" -#if defined(FEAT_BEVAL) || defined(PROTO) - +#if defined(FEAT_BEVAL) || defined(FEAT_TEXT_PROP) || defined(PROT) /* - * Get the text and position to be evaluated for "beval". - * If "getword" is true the returned text is not the whole line but the - * relevant word in allocated memory. - * Returns OK or 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 -get_beval_info( - BalloonEval *beval, - int getword, - win_T **winp, - linenr_T *lnump, - char_u **textp, - int *colp) +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, // column where mouse hovers, can be NULL + int *startcolp) // column where text starts, can be NULL { + int row = mouserow; + int col = mousecol; + int scol; 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) -# endif - { - row = mouse_row; - col = mouse_col; - } -# endif -# ifdef FEAT_GUI - if (gui.in_use) - { - row = Y_2_ROW(beval->y); - col = X_2_COL(beval->x); - } -#endif 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 +82,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 +98,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); + len = find_ident_at_pos(wp, lnum, (colnr_T)col, + &lbuf, &scol, flags); --emsg_off; if (len == 0) return FAIL; @@ -120,21 +109,67 @@ 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; + if (colp != NULL) + *colp = col; + if (startcolp != NULL) + *startcolp = scol; return OK; } } } + return FAIL; +} +#endif + +#if defined(FEAT_BEVAL) || defined(PROTO) + +/* + * Get the text and position to be evaluated for "beval". + * If "getword" is TRUE the returned text is not the whole line but the + * relevant word in allocated memory. + * Returns OK or FAIL. + */ + int +get_beval_info( + BalloonEval *beval, + int getword, + win_T **winp, + linenr_T *lnump, + char_u **textp, + int *colp) +{ + int row = mouse_row; + int col = mouse_col; + +# ifdef FEAT_GUI + if (gui.in_use) + { + row = Y_2_ROW(beval->y); + 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, NULL) == 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; } @@ -264,11 +299,15 @@ general_beval_cb(BalloonEval *beval, int state UNUSED) set_vim_var_string(VV_BEVAL_TEXT, NULL, -1); if (result != NULL && result[0] != NUL) - { post_balloon(beval, result, NULL); - recursive = FALSE; - return; - } + + // The 'balloonexpr' evaluation may show something on the screen + // that requires a screen update. + if (must_redraw) + redraw_after_callback(FALSE); + + recursive = FALSE; + return; } } #endif diff --git a/src/channel.c b/src/channel.c index 5ea28fd026..4a0f9afad4 100644 --- a/src/channel.c +++ b/src/channel.c @@ -4032,7 +4032,7 @@ channel_send( writeq_T *last = wq->wq_prev; /* append to the last entry */ - if (ga_grow(&last->wq_ga, len) == OK) + if (len > 0 && ga_grow(&last->wq_ga, len) == OK) { mch_memmove((char *)last->wq_ga.ga_data + last->wq_ga.ga_len, @@ -4054,7 +4054,7 @@ channel_send( wq->wq_prev->wq_next = last; wq->wq_prev = last; ga_init2(&last->wq_ga, 1, 1000); - if (ga_grow(&last->wq_ga, len) == OK) + if (len > 0 && ga_grow(&last->wq_ga, len) == OK) { mch_memmove(last->wq_ga.ga_data, buf, len); last->wq_ga.ga_len = len; diff --git a/src/eval.c b/src/eval.c index 26fad0c5fa..d89093cb33 100644 --- a/src/eval.c +++ b/src/eval.c @@ -9910,10 +9910,14 @@ assert_fails(typval_T *argvars) char_u *cmd = tv_get_string_chk(&argvars[0]); garray_T ga; int ret = 0; + int save_trylevel = trylevel; + // trylevel must be zero for a ":throw" command to be considered failed + trylevel = 0; called_emsg = FALSE; suppress_errthrow = TRUE; emsg_silent = TRUE; + do_cmdline_cmd(cmd); if (!called_emsg) { @@ -9939,10 +9943,11 @@ assert_fails(typval_T *argvars) assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); ga_clear(&ga); - ret = 1; + ret = 1; } } + trylevel = save_trylevel; called_emsg = FALSE; suppress_errthrow = FALSE; emsg_silent = FALSE; diff --git a/src/evalfunc.c b/src/evalfunc.c index 7cc0e74b4e..687f15e363 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/globals.h b/src/globals.h index 82ee1e8645..cbb7f41b19 100644 --- a/src/globals.h +++ b/src/globals.h @@ -603,6 +603,11 @@ EXTERN int aucmd_win_used INIT(= FALSE); /* aucmd_win is being used */ #ifdef FEAT_TEXT_PROP EXTERN win_T *first_popupwin; // first global popup window EXTERN win_T *popup_dragwin INIT(= NULL); // popup window being dragged + +// Set to TRUE if there is any visible popup. +EXTERN int popup_visible INIT(= FALSE); + +EXTERN int text_prop_frozen INIT(= 0); #endif /* @@ -1685,10 +1690,3 @@ typedef int HINSTANCE; EXTERN int ctrl_break_was_pressed INIT(= FALSE); EXTERN HINSTANCE g_hinst INIT(= NULL); #endif - -#ifdef FEAT_TEXT_PROP -EXTERN int text_prop_frozen INIT(= 0); - -// Set to TRUE if there is any visible popup. -EXTERN int popup_visible INIT(= FALSE); -#endif diff --git a/src/gui.c b/src/gui.c index f7a35b44c9..5c3fcf8d51 100644 --- a/src/gui.c +++ b/src/gui.c @@ -2285,7 +2285,7 @@ gui_outstr_nowrap( int col = gui.col; #ifdef FEAT_SIGN_ICONS int draw_sign = FALSE; - int signcol = 0; + int signcol = col; char_u extra[18]; # ifdef FEAT_NETBEANS_INTG int multi_sign = FALSE; @@ -2321,7 +2321,7 @@ gui_outstr_nowrap( --col; len = (int)STRLEN(s); if (len > 2) - signcol = len - 3; // Right align sign icon in the number column + signcol = col + len - 3; // Right align sign icon in the number column draw_sign = TRUE; highlight_mask = 0; } @@ -4864,18 +4864,15 @@ gui_focus_change(int in_focus) } /* - * Called when the mouse moved (but not when dragging). + * When mouse moved: apply 'mousefocus'. + * Also updates the mouse pointer shape. */ - void -gui_mouse_moved(int x, int y) + static void +gui_mouse_focus(int x, int y) { win_T *wp; char_u st[8]; - /* Ignore this while still starting up. */ - if (!gui.in_use || gui.starting) - return; - #ifdef FEAT_MOUSESHAPE /* Get window pointer, and update mouse shape as well. */ wp = xy2win(x, y); @@ -4934,6 +4931,27 @@ gui_mouse_moved(int x, int y) } } +/* + * Called when the mouse moved (but not when dragging). + */ + void +gui_mouse_moved(int x, int y) +{ + // Ignore this while still starting up. + if (!gui.in_use || gui.starting) + return; + + // apply 'mousefocus' and pointer shape + gui_mouse_focus(x, y); + +#ifdef FEAT_TEXT_PROP + if (popup_visible) + // Generate a mouse-moved event, so that the popup can perhaps be + // closed, just like in the terminal. + gui_send_mouse_event(MOUSE_DRAG, x, y, FALSE, 0); +#endif +} + /* * Called when mouse should be moved to window with focus. */ diff --git a/src/hangulin.c b/src/hangulin.c index e08fe6317a..337ea84f4c 100644 --- a/src/hangulin.c +++ b/src/hangulin.c @@ -404,11 +404,13 @@ hangul_input_state_set(int state) hangul_input_clear(); } +#if (!defined(FEAT_XIM) && !defined(FEAT_GUI_GTK)) || defined(PROTO) int im_get_status(void) { return hangul_input_state_get(); } +#endif void hangul_input_state_toggle(void) diff --git a/src/mbyte.c b/src/mbyte.c index 2e677f9af7..af840c3b8a 100644 --- a/src/mbyte.c +++ b/src/mbyte.c @@ -4266,14 +4266,18 @@ mb_lefthalve(int row, int col) int mb_fix_col(int col, int row) { + int off; + col = check_col(col); row = check_row(row); + off = LineOffset[row] + col; if (has_mbyte && ScreenLines != NULL && col > 0 && ((enc_dbcs - && ScreenLines[LineOffset[row] + col] != NUL + && ScreenLines[off] != NUL && dbcs_screen_head_off(ScreenLines + LineOffset[row], - ScreenLines + LineOffset[row] + col)) - || (enc_utf8 && ScreenLines[LineOffset[row] + col] == 0))) + ScreenLines + off)) + || (enc_utf8 && ScreenLines[off] == 0 + && ScreenLinesUC[off] == 0))) return col - 1; return col; } @@ -5931,6 +5935,11 @@ xim_queue_key_press_event(GdkEventKey *event, int down) int im_get_status(void) { +# ifdef FEAT_HANGULIN + if (hangul_input_state_get()) + return TRUE; +# endif + # ifdef FEAT_EVAL if (USE_IMSTATUSFUNC) return call_imstatusfunc(); diff --git a/src/misc2.c b/src/misc2.c index 1c865aa1c1..16caae6861 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -4451,12 +4451,19 @@ parse_queued_messages(void) { win_T *old_curwin = curwin; int i; + int save_may_garbage_collect = may_garbage_collect; // Do not handle messages while redrawing, because it may cause buffers to // change or be wiped while they are being redrawn. if (updating_screen) return; + // may_garbage_collect is set in main_loop() to do garbage collection when + // blocking to wait on a character. We don't want that while parsing + // messages, a callback may invoke vgetc() while lists and dicts are in use + // in the call stack. + may_garbage_collect = FALSE; + // Loop when a job ended, but don't keep looping forever. for (i = 0; i < MAX_REPEAT_PARSE; ++i) { @@ -4497,6 +4504,8 @@ parse_queued_messages(void) break; } + may_garbage_collect = save_may_garbage_collect; + // If the current window changed we need to bail out of the waiting loop. // E.g. when a job exit callback closes the terminal window. if (curwin != old_curwin) diff --git a/src/move.c b/src/move.c index 003a5f7faa..05eeb8b54f 100644 --- a/src/move.c +++ b/src/move.c @@ -1218,6 +1218,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 184a97d646..13e74fad85 100644 --- a/src/normal.c +++ b/src/normal.c @@ -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; } @@ -3325,28 +3328,28 @@ find_is_eval_item( * Find the identifier under or to the right of the cursor. * "find_type" can have one of three values: * FIND_IDENT: find an identifier (keyword) - * FIND_STRING: find any non-white string - * FIND_IDENT + FIND_STRING: find any non-white string, identifier preferred. + * FIND_STRING: find any non-white text + * FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred. * FIND_EVAL: find text useful for C program debugging * * There are three steps: - * 1. Search forward for the start of an identifier/string. Doesn't move if + * 1. Search forward for the start of an identifier/text. Doesn't move if * already on one. - * 2. Search backward for the start of this identifier/string. + * 2. Search backward for the start of this identifier/text. * This doesn't match the real Vi but I like it a little better and it * shouldn't bother anyone. - * 3. Search forward to the end of this identifier/string. + * 3. Search forward to the end of this identifier/text. * When FIND_IDENT isn't defined, we backup until a blank. * - * Returns the length of the string, or zero if no string is found. - * If a string is found, a pointer to the string is put in "*string". This - * string is not always NUL terminated. + * Returns the length of the text, or zero if no text is found. + * If text is found, a pointer to the text is put in "*text". This + * points into the current buffer line and is not always NUL terminated. */ int -find_ident_under_cursor(char_u **string, int find_type) +find_ident_under_cursor(char_u **text, int find_type) { return find_ident_at_pos(curwin, curwin->w_cursor.lnum, - curwin->w_cursor.col, string, find_type); + curwin->w_cursor.col, text, NULL, find_type); } /* @@ -3358,33 +3361,34 @@ find_ident_at_pos( win_T *wp, linenr_T lnum, colnr_T startcol, - char_u **string, + char_u **text, + int *textcol, // column where "text" starts, can be NULL int find_type) { char_u *ptr; - int col = 0; /* init to shut up GCC */ + int col = 0; // init to shut up GCC int i; int this_class = 0; int prev_class; int prevcol; - int bn = 0; /* bracket nesting */ + int bn = 0; // bracket nesting /* * if i == 0: try to find an identifier - * if i == 1: try to find any non-white string + * if i == 1: try to find any non-white text */ ptr = ml_get_buf(wp->w_buffer, lnum, FALSE); for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i) { /* - * 1. skip to start of identifier/string + * 1. skip to start of identifier/text */ col = startcol; if (has_mbyte) { while (ptr[col] != NUL) { - /* Stop at a ']' to evaluate "a[x]". */ + // Stop at a ']' to evaluate "a[x]". if ((find_type & FIND_EVAL) && ptr[col] == ']') break; this_class = mb_get_class(ptr + col); @@ -3400,11 +3404,11 @@ find_ident_at_pos( ) ++col; - /* When starting on a ']' count it, so that we include the '['. */ + // When starting on a ']' count it, so that we include the '['. bn = ptr[col] == ']'; /* - * 2. Back up to start of identifier/string. + * 2. Back up to start of identifier/text. */ if (has_mbyte) { @@ -3430,8 +3434,8 @@ find_ident_at_pos( col = prevcol; } - /* If we don't want just any old string, or we've found an - * identifier, stop searching. */ + // If we don't want just any old text, or we've found an + // identifier, stop searching. if (this_class > 2) this_class = 2; if (!(find_type & FIND_STRING) || this_class == 2) @@ -3452,8 +3456,8 @@ find_ident_at_pos( )) --col; - /* If we don't want just any old string, or we've found an - * identifier, stop searching. */ + // If we don't want just any old text, or we've found an + // identifier, stop searching. if (!(find_type & FIND_STRING) || vim_iswordc(ptr[col])) break; } @@ -3462,7 +3466,7 @@ find_ident_at_pos( if (ptr[col] == NUL || (i == 0 && (has_mbyte ? this_class != 2 : !vim_iswordc(ptr[col])))) { - // didn't find an identifier or string + // didn't find an identifier or text if ((find_type & FIND_NOERROR) == 0) { if (find_type & FIND_STRING) @@ -3473,17 +3477,19 @@ find_ident_at_pos( return 0; } ptr += col; - *string = ptr; + *text = ptr; + if (textcol != NULL) + *textcol = col; /* - * 3. Find the end if the identifier/string. + * 3. Find the end if the identifier/text. */ bn = 0; startcol -= col; col = 0; if (has_mbyte) { - /* Search for point of changing multibyte character class. */ + // Search for point of changing multibyte character class. this_class = mb_get_class(ptr); while (ptr[col] != NUL && ((i == 0 ? mb_get_class(ptr + col) == this_class 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..f2b31300f0 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -167,6 +167,45 @@ 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) +{ + win_T *textwp; + char_u *text; + int col; + pos_T pos; + colnr_T mcol; + + if (find_word_under_cursor(mouse_row, mouse_col, TRUE, flags, + &textwp, &pos.lnum, &text, NULL, &col) == OK) + { + // convert text column to mouse column + pos.col = col; + pos.coladd = 0; + getvcol(textwp, &pos, &mcol, NULL, NULL); + wp->w_popup_mouse_mincol = mcol; + + pos.col = col + STRLEN(text) - 1; + getvcol(textwp, &pos, NULL, NULL, &mcol); + wp->w_popup_mouse_maxcol = mcol; + 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 +374,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 +627,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; + 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); @@ -821,7 +890,9 @@ popup_adjust_position(win_T *wp) wp->w_width = 1; for (lnum = wp->w_topline; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum) { - int len = vim_strsize(ml_get_buf(wp->w_buffer, lnum, FALSE)); + // count Tabs for what they are worth + int len = win_linetabsize(wp, ml_get_buf(wp->w_buffer, lnum, FALSE), + (colnr_T)MAXCOL); if (wp->w_p_wrap) { @@ -956,6 +1027,7 @@ typedef enum { TYPE_NORMAL, TYPE_ATCURSOR, + TYPE_BEVAL, TYPE_NOTIFICATION, TYPE_DIALOG, TYPE_MENU @@ -1137,17 +1209,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 +1363,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 +1431,55 @@ 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; + // Careful: this makes "wp" invalid. + 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, *nextwp; + 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 = nextwp) + { + nextwp = wp->w_next; + check_mouse_moved(wp, mouse_wp); + } + for (wp = curtab->tp_first_popupwin; wp != NULL; wp = nextwp) + { + nextwp = 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 +1967,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 +1978,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.h b/src/proto.h index 946e2288c4..64a5793d65 100644 --- a/src/proto.h +++ b/src/proto.h @@ -230,13 +230,14 @@ void qsort(void *base, size_t elm_count, size_t elm_size, int (*cmp)(const void # include "if_ruby.pro" # endif -/* Ugly solution for "BalloonEval" not being defined while it's used in some - * .pro files. */ -# ifdef FEAT_BEVAL -# include "beval.pro" -# else +// Ugly solution for "BalloonEval" not being defined while it's used in some +// .pro files. +# ifndef FEAT_BEVAL # define BalloonEval int # endif +# if defined(FEAT_BEVAL) || defined(FEAT_TEXT_PROP) +# include "beval.pro" +# endif # ifdef FEAT_NETBEANS_INTG # include "netbeans.pro" diff --git a/src/proto/beval.pro b/src/proto/beval.pro index 2be64a0da1..8b67e383db 100644 --- a/src/proto/beval.pro +++ b/src/proto/beval.pro @@ -1,4 +1,5 @@ /* beval.c */ +int find_word_under_cursor(int mouserow, int mousecol, int getword, int flags, win_T **winp, linenr_T *lnump, char_u **textp, int *colp, int *startcolp); int get_beval_info(BalloonEval *beval, int getword, 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); 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/normal.pro b/src/proto/normal.pro index 55d12bb292..cc4bf7dfc9 100644 --- a/src/proto/normal.pro +++ b/src/proto/normal.pro @@ -7,8 +7,8 @@ void check_visual_highlight(void); void end_visual_mode(void); void reset_VIsual_and_resel(void); void reset_VIsual(void); -int find_ident_under_cursor(char_u **string, int find_type); -int find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **string, int find_type); +int find_ident_under_cursor(char_u **text, int find_type); +int find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **text, int *textcol, int find_type); void clear_showcmd(void); int add_to_showcmd(int c); void add_to_showcmd_c(int c); 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/screen.c b/src/screen.c index 48bf9aef28..5eea6b2aaf 100644 --- a/src/screen.c +++ b/src/screen.c @@ -611,6 +611,7 @@ update_screen(int type_arg) curwin->w_lines_valid = 0; /* don't use w_lines[].wl_size now */ return FAIL; } + updating_screen = TRUE; #ifdef FEAT_TEXT_PROP // Update popup_mask if needed. This may set w_redraw_top and w_redraw_bot @@ -618,7 +619,6 @@ update_screen(int type_arg) may_update_popup_mask(type); #endif - updating_screen = TRUE; #ifdef FEAT_SYN_HL ++display_tick; /* let syntax code know we're in a next round of * display updating */ @@ -3193,9 +3193,10 @@ win_line( int n_skip = 0; /* nr of chars to skip for 'nowrap' */ - int fromcol, tocol; /* start/end of inverting */ - int fromcol_prev = -2; /* start of inverting after cursor */ - int noinvcur = FALSE; /* don't invert the cursor */ + int fromcol = -10; // start of inverting + int tocol = MAXCOL; // end of inverting + int fromcol_prev = -2; // start of inverting after cursor + int noinvcur = FALSE; // don't invert the cursor pos_T *top, *bot; int lnum_in_visual_area = FALSE; pos_T pos; @@ -3455,39 +3456,40 @@ win_line( #endif /* - * handle visual active in this window + * handle Visual active in this window */ - fromcol = -10; - tocol = MAXCOL; if (VIsual_active && wp->w_buffer == curwin->w_buffer) { - /* Visual is after curwin->w_cursor */ if (LTOREQ_POS(curwin->w_cursor, VIsual)) { + // Visual is after curwin->w_cursor top = &curwin->w_cursor; bot = &VIsual; } - else /* Visual is before curwin->w_cursor */ + else { + // Visual is before curwin->w_cursor top = &VIsual; bot = &curwin->w_cursor; } lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum); - if (VIsual_mode == Ctrl_V) /* block mode */ + if (VIsual_mode == Ctrl_V) { + // block mode if (lnum_in_visual_area) { fromcol = wp->w_old_cursor_fcol; tocol = wp->w_old_cursor_lcol; } } - else /* non-block mode */ + else { + // non-block mode if (lnum > top->lnum && lnum <= bot->lnum) fromcol = 0; else if (lnum == top->lnum) { - if (VIsual_mode == 'V') /* linewise */ + if (VIsual_mode == 'V') // linewise fromcol = 0; else { diff --git a/src/structs.h b/src/structs.h index facf5cf6da..20e4a61740 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2953,6 +2953,9 @@ struct window_S linenr_T w_popup_lnum; // close popup if cursor not on this line colnr_T w_popup_mincol; // close popup if cursor before this col colnr_T w_popup_maxcol; // close popup if cursor after this col + int w_popup_mouse_row; // close popup if mouse moves away + int w_popup_mouse_mincol; // close popup if mouse moves away + int w_popup_mouse_maxcol; // close popup if mouse moves away int w_popup_drag; // allow moving the popup with the mouse popclose_T w_popup_close; // allow closing the popup with the mouse diff --git a/src/testdir/dumps/Test_popupwin_11.dump b/src/testdir/dumps/Test_popupwin_11.dump index 21460ddb84..f67a314645 100644 --- a/src/testdir/dumps/Test_popupwin_11.dump +++ b/src/testdir/dumps/Test_popupwin_11.dump @@ -1,10 +1,10 @@ >1+0&#ffffff0| @73 |2| @73 -|3| @18|#+0#e000e06#e0e0e08|i|n|c|l|u|d|e| |<+0#e000002&|s|t|d|i|o|.|h|>| +0#0000000#ffffff0@36 -|4| @18|i+0#00e0003#e0e0e08|n|t| +0#0000000&|m|a|i|n|(|v+0#00e0003&|o|i|d|)+0#0000000&| @3| +0&#ffffff0@36 -|5| @18|{+0&#e0e0e08| @16| +0&#ffffff0@36 -|6| @18| +0&#e0e0e08@3|p|r|i|n|t|f|(|5+0#e000002&|6|7|)+0#0000000&|;| @1| +0&#ffffff0@36 -|7| @18|}+0&#e0e0e08| @16| +0&#ffffff0@36 +|3| @18|#+0#e000e06#e0e0e08|i|n|c|l|u|d|e| |<+0#e000002&|s|t|d|i|o|.|h|>| +0#0000000&@1| +0&#ffffff0@34 +|4| @18|i+0#00e0003#e0e0e08|n|t| +0#0000000&|m|a|i|n|(|v+0#00e0003&|o|i|d|)+0#0000000&| @5| +0&#ffffff0@34 +|5| @18|{+0&#e0e0e08| @18| +0&#ffffff0@34 +|6| @18| +0&#e0e0e08@7|p|r|i|n|t|f|(|5+0#e000002&|6|7|)+0#0000000&|;| +0&#ffffff0@34 +|7| @18|}+0&#e0e0e08| @18| +0&#ffffff0@34 |8| @73 |9| @73 @57|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popupwin_24.dump b/src/testdir/dumps/Test_popupwin_24.dump new file mode 100644 index 0000000000..f5386c9542 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_24.dump @@ -0,0 +1,12 @@ +>1+0&#ffffff0| @73 +|2| |╔+0fd7ff255|═@11|╗| +0&#ffffff0@5|╔+0&#dadada255|═@11|╗+0a8a8a255| +0&#ffffff0@5|x+0fd7ff255@13| +0&#ffffff0@2|#+0fd7ff255|x@11|#| +0&#ffffff0@1 +|3| |║+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|║+0#0000000#5fd7ff255| +0&#ffffff0@5|║+0&#a8a8a8255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|║+0#0000000#8a8a8a255| +0&#ffffff0@5|x+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|x+0#0000000#5fd7ff255| +0&#ffffff0@2|x+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|x+0#0000000#5fd7ff255| +0&#ffffff0@1 +|4| |╚+0fd7ff255|═@11|╝| +0&#ffffff0@5|║+0&#a8a8a8255|a+0#0000001#ffd7ff255|n|d| |m|o|r|e| @3|║+0#0000000#8a8a8a255| +0&#ffffff0@5|x+0fd7ff255|l+0#0000001#ffd7ff255|i|n|e|s| |o|n|l|y| @1|x+0#0000000#5fd7ff255| +0&#ffffff0@2|x+0fd7ff255|w+0#0000001#ffd7ff255|i|t|h| |c|o|r|n|e|r|s|x+0#0000000#5fd7ff255| +0&#ffffff0@1 +|5| @20|╚+0�|═@11|╝| +0&#ffffff0@5|x+0fd7ff255@13| +0&#ffffff0@2|#+0fd7ff255|x@11|#| +0&#ffffff0@1 +|6| |e+0fd7ff255|a@11|f| +0&#ffffff0@58 +|7| |d+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|b+0#0000000#5fd7ff255| +0&#ffffff0@5| +0fd7ff255@13| +0&#ffffff0@38 +|8| |d+0fd7ff255|w+0#0000001#ffd7ff255|i|t|h| |n|u|m|b|e|r|s|b+0#0000000#5fd7ff255| +0&#ffffff0@5| +0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r| +0#0000000#5fd7ff255| +0&#ffffff0@5|┌+0#0000001#ffd7ff255|─@4|┐| +0#0000000#ffffff0@25 +|9| |h+0fd7ff255|c@11|g| +0&#ffffff0@5| +0fd7ff255|j+0#0000001#ffd7ff255|u|s|t| |b|l|a|n|k|s| | +0#0000000#5fd7ff255| +0&#ffffff0@5|│+0#0000001#ffd7ff255|h|e|l@1|o|│| +0#0000000#ffffff0@25 +|1|0| @19| +0fd7ff255@13| +0&#ffffff0@5|└+0#0000001#ffd7ff255|─@4|┘| +0#0000000#ffffff0@25 +|1@1| @72 +|:|c|a|l@1| |M|u|l|t|i|B|y|t|e|(|)| @39|1|,|1| @10|T|o|p| 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_assert.vim b/src/testdir/test_assert.vim index 0d59a43a2d..900710b893 100644 --- a/src/testdir/test_assert.vim +++ b/src/testdir/test_assert.vim @@ -166,6 +166,12 @@ func Test_assert_fail_fails() call remove(v:errors, 0) endfunc +func Test_assert_fails_in_try_block() + try + call assert_equal(0, assert_fails('throw "error"')) + endtry +endfunc + func Test_assert_beeps() new call assert_equal(0, assert_beeps('normal h')) 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_number.vim b/src/testdir/test_number.vim index 415aefc858..81326bce14 100644 --- a/src/testdir/test_number.vim +++ b/src/testdir/test_number.vim @@ -252,3 +252,14 @@ func Test_numberwidth_adjusted() call s:compare_lines(expect, lines) call s:close_windows() endfunc + +" This was causing a memcheck error +func Test_relativenumber_uninitialised() + new + set rnu + call setline(1, ["a", "b"]) + redraw + call feedkeys("j", 'xt') + redraw + bwipe! +endfunc diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 00cc2878e4..4e3170ce3a 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -113,6 +113,9 @@ func Test_popup_with_border_and_padding() call popup_create(['hello border', 'with corners'], {'line': 2, 'col': 60, 'border': [], 'borderhighlight': ['BlueColor'], 'borderchars': ['x', '#']}) let winid = popup_create(['hello border', 'with numbers'], {'line': 6, 'col': 3, 'border': [], 'borderhighlight': ['BlueColor'], 'borderchars': ['0', '1', '2', '3', '4', '5', '6', '7']}) call popup_create(['hello border', 'just blanks'], {'line': 7, 'col': 23, 'border': [], 'borderhighlight': ['BlueColor'], 'borderchars': [' ']}) + func MultiByte() + call popup_create(['hello'], {'line': 8, 'col': 43, 'border': [], 'borderchars': ['─', '│', '─', '│', '┌', '┐', '┘', '└']}) + endfunc END call writefile(lines, 'XtestPopupBorder') let buf = RunVimInTerminal('-S XtestPopupBorder', {'rows': 12}) @@ -122,6 +125,12 @@ func Test_popup_with_border_and_padding() call term_sendkeys(buf, ":call popup_setoptions(winid, {'borderchars': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']})\") call VerifyScreenDump(buf, 'Test_popupwin_23', {}) + " check multi-byte border only with 'ambiwidth' single + if &ambiwidth == 'single' + call term_sendkeys(buf, ":call MultiByte()\") + call VerifyScreenDump(buf, 'Test_popupwin_24', {}) + endif + call StopVimInTerminal(buf) call delete('XtestPopupBorder') @@ -216,7 +225,7 @@ func Test_popup_with_syntax_setbufvar() \ '#include ', \ 'int main(void)', \ '{', - \ ' printf(567);', + \ "\tprintf(567);", \ '}', \], {'line': 3, 'col': 21, 'highlight': 'PopupColor'}) call setbufvar(winbufnr(winid), '&syntax', 'cpp') @@ -1005,6 +1014,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 +1469,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 +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("hi\", 'xt') call assert_equal({}, popup_getpos(winid)) call popup_clear() @@ -1432,7 +1488,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 +1502,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/testdir/test_suspend.vim b/src/testdir/test_suspend.vim index cb91ef1a9e..4b3bd5eadf 100644 --- a/src/testdir/test_suspend.vim +++ b/src/testdir/test_suspend.vim @@ -54,6 +54,7 @@ func Test_suspend() " Quit gracefully to dump coverage information. call term_sendkeys(buf, ":qall!\") call term_wait(buf) + " Wait until Vim actually exited and shell shows a prompt call WaitForAssert({-> assert_match('[$#] $', term_getline(buf, '.'))}) call StopShellInTerminal(buf) diff --git a/src/version.c b/src/version.c index bd8f62063d..842f41dd80 100644 --- a/src/version.c +++ b/src/version.c @@ -792,6 +792,48 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1661, +/**/ + 1660, +/**/ + 1659, +/**/ + 1658, +/**/ + 1657, +/**/ + 1656, +/**/ + 1655, +/**/ + 1654, +/**/ + 1653, +/**/ + 1652, +/**/ + 1651, +/**/ + 1650, +/**/ + 1649, +/**/ + 1648, +/**/ + 1647, +/**/ + 1646, +/**/ + 1645, +/**/ + 1644, +/**/ + 1643, +/**/ + 1642, +/**/ + 1641, /**/ 1640, /**/