# HG changeset patch # User Bram Moolenaar # Date 1561926605 -7200 # Node ID 09fa437d33d887b734f837a01fa0a11a2c6c0711 # Parent e92df9dfef0fc1d558193fb55e83fa0cc609c55a patch 8.1.1612: cannot show an existing buffer in a popup window commit https://github.com/vim/vim/commit/5b8cfedfbd19a71a30c73cf44b0aec3da7fc1a24 Author: Bram Moolenaar Date: Sun Jun 30 22:16:10 2019 +0200 patch 8.1.1612: cannot show an existing buffer in a popup window Problem: Cannot show an existing buffer in a popup window. Solution: Support buffer number argument in popup_create(). diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -98,9 +98,6 @@ or by clicking anywhere inside the popup TODO: -- Currently 'buftype' is set to "popup", but all the specifics are on the - window. Can we use a "normal" buffer and put the type on the window? (#4595) - What if it's modified and the window closes? - Add test for when popup with mask is off the left and off the right of the screen. - check padding/border when popup is off the left and right of the screen. @@ -164,10 +161,10 @@ Other: [functions help to be moved to eval.txt later] -popup_atcursor({text}, {options}) *popup_atcursor()* - Show the {text} above the cursor, and close it when the cursor +popup_atcursor({what}, {options}) *popup_atcursor()* + Show the {what} above the cursor, and close it when the cursor moves. This works like: > - call popup_create({text}, { + call popup_create({what}, { \ 'pos': 'botleft', \ 'line': 'cursor-1', \ 'col': 'cursor', @@ -191,11 +188,15 @@ popup_close({id} [, {result}]) *popup Otherwise zero is passed to the callback. -popup_create({text}, {options}) *popup_create()* - Open a popup window showing {text}, which is either: +popup_create({what}, {options}) *popup_create()* + Open a popup window showing {what}, which is either: + - a buffer number - a string - a list of strings - a list of text lines with text properties + When {what} is not a buffer number, a buffer is created with + 'buftype' set to "popup". That buffer will be wiped out once + the popup closes. {options} is a dictionary with many possible entries. See |popup_create-usage| for details. @@ -209,9 +210,9 @@ popup_create({text}, {options}) *popu < In case of failure zero is returned. -popup_dialog({text}, {options}) *popup_dialog()* +popup_dialog({what}, {options}) *popup_dialog()* Just like |popup_create()| but with these default options: > - call popup_create({text}, { + call popup_create({what}, { \ 'pos': 'center', \ 'zindex': 200, \ 'drag': 1, @@ -312,12 +313,12 @@ popup_hide({id}) *popup_hide()* exists but is not a popup window an error is given. *E993* -popup_menu({text}, {options}) *popup_menu()* - Show the {text} near the cursor, handle selecting one of the +popup_menu({what}, {options}) *popup_menu()* + Show the {what} near the cursor, handle selecting one of the items with cursorkeys, and close it an item is selected with - Space or Enter. {text} should have multiple lines to make this + Space or Enter. {what} should have multiple lines to make this useful. This works like: > - call popup_create({text}, { + call popup_create({what}, { \ 'pos': 'center', \ 'zindex': 200, \ 'drag': 1, @@ -349,10 +350,10 @@ popup_move({id}, {options}) *popup_m For other options see |popup_setoptions()|. -popup_notification({text}, {options}) *popup_notification()* - Show the {text} for 3 seconds at the top of the Vim window. +popup_notification({what}, {options}) *popup_notification()* + Show the {what} for 3 seconds at the top of the Vim window. This works like: > - call popup_create({text}, { + call popup_create({what}, { \ 'line': 1, \ 'col': 10, \ 'minwidth': 20, @@ -410,7 +411,8 @@ popup_setoptions({id}, {options}) *pop popup_settext({id}, {text}) *popup_settext()* Set the text of the buffer in poup win {id}. {text} is the - same as supplied to |popup_create()|. + same as supplied to |popup_create()|, except that a buffer + number is not allowed. Does not change the window size or position, other than caused by the different text. @@ -450,7 +452,8 @@ POPUP_CREATE() ARGUMENTS *popup_creat The first argument of |popup_create()| (and the second argument to |popup_settext()|) specifies the text to be displayed, and optionally text -properties. It is in one of three forms: +properties. It is in one of four forms: +- a buffer number - a string - a list of strings - a list of dictionaries, where each dictionary has these entries: diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -122,6 +122,23 @@ read_buffer( } /* + * Ensure buffer "buf" is loaded. Does not trigger the swap-exists action. + */ + void +buffer_ensure_loaded(buf_T *buf) +{ + if (buf->b_ml.ml_mfp == NULL) + { + aco_save_T aco; + + aucmd_prepbuf(&aco, buf); + swap_exists_action = SEA_NONE; + open_buffer(FALSE, NULL, 0); + aucmd_restbuf(&aco); + } +} + +/* * Open current buffer, that is: open the memfile and read the file into * memory. * Return FAIL for failure, OK otherwise. diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1963,15 +1963,8 @@ f_bufload(typval_T *argvars, typval_T *r { buf_T *buf = get_buf_arg(&argvars[0]); - if (buf != NULL && buf->b_ml.ml_mfp == NULL) - { - aco_save_T aco; - - aucmd_prepbuf(&aco, buf); - swap_exists_action = SEA_NONE; - open_buffer(FALSE, NULL, 0); - aucmd_restbuf(&aco); - } + if (buf != NULL) + buffer_ensure_loaded(buf); } /* @@ -4905,7 +4898,7 @@ f_getchar(typval_T *argvars, typval_T *r return; (void)mouse_comp_pos(win, &row, &col, &lnum); # ifdef FEAT_TEXT_PROP - if (bt_popup(win->w_buffer)) + if (WIN_IS_POPUP(win)) winnr = 0; else # endif diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -4525,7 +4525,7 @@ nv_mousescroll(cmdarg_T *cap) if (wp == NULL) return; #ifdef FEAT_TEXT_PROP - if (bt_popup(wp->w_buffer) && !wp->w_has_scrollbar) + if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar) return; #endif curwin = wp; @@ -4560,7 +4560,7 @@ nv_mousescroll(cmdarg_T *cap) nv_scroll_line(cap); } #ifdef FEAT_TEXT_PROP - if (bt_popup(curwin->w_buffer)) + if (WIN_IS_POPUP(curwin)) popup_set_firstline(curwin); #endif } diff --git a/src/popupwin.c b/src/popupwin.c --- a/src/popupwin.c +++ b/src/popupwin.c @@ -997,14 +997,26 @@ popup_create(typval_T *argvars, typval_T win_T *wp; tabpage_T *tp = NULL; int tabnr; - buf_T *buf; + int new_buffer; + buf_T *buf = NULL; dict_T *d; int nr; int i; // Check arguments look OK. - if (!(argvars[0].v_type == VAR_STRING && argvars[0].vval.v_string != NULL) - && !(argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list != NULL)) + if (argvars[0].v_type == VAR_NUMBER) + { + buf = buflist_findnr( argvars[0].vval.v_number); + if (buf == NULL) + { + semsg(_(e_nobufnr), argvars[0].vval.v_number); + return NULL; + } + } + else if (!(argvars[0].v_type == VAR_STRING + && argvars[0].vval.v_string != NULL) + && !(argvars[0].v_type == VAR_LIST + && argvars[0].vval.v_list != NULL)) { emsg(_(e_listreq)); return NULL; @@ -1038,27 +1050,42 @@ popup_create(typval_T *argvars, typval_T return NULL; rettv->vval.v_number = wp->w_id; wp->w_popup_pos = POPPOS_TOPLEFT; + wp->w_popup_flags = POPF_IS_POPUP; - buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY); - if (buf == NULL) - return NULL; - ml_open(buf); + if (buf != NULL) + { + // use existing buffer + new_buffer = FALSE; + wp->w_buffer = buf; + ++buf->b_nwindows; + buffer_ensure_loaded(buf); + } + else + { + // create a new buffer associated with the popup + new_buffer = TRUE; + buf = buflist_new(NULL, NULL, (linenr_T)0, + BLN_NEW|BLN_LISTED|BLN_DUMMY); + if (buf == NULL) + return NULL; + ml_open(buf); - win_init_popup_win(wp, buf); + win_init_popup_win(wp, buf); - set_local_options_default(wp); - set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1, + set_local_options_default(wp); + set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1, (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0); - set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1, - (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0); - buf->b_p_ul = -1; // no undo - buf->b_p_swf = FALSE; // no swap file - buf->b_p_bl = FALSE; // unlisted buffer - buf->b_locked = TRUE; - wp->w_p_wrap = TRUE; // 'wrap' is default on + set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1, + (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0); + buf->b_p_ul = -1; // no undo + buf->b_p_swf = FALSE; // no swap file + buf->b_p_bl = FALSE; // unlisted buffer + buf->b_locked = TRUE; + wp->w_p_wrap = TRUE; // 'wrap' is default on - // Avoid that 'buftype' is reset when this buffer is entered. - buf->b_p_initialized = TRUE; + // Avoid that 'buftype' is reset when this buffer is entered. + buf->b_p_initialized = TRUE; + } if (tp != NULL) { @@ -1088,7 +1115,8 @@ popup_create(typval_T *argvars, typval_T } } - popup_set_buffer_text(buf, argvars[0]); + if (new_buffer) + popup_set_buffer_text(buf, argvars[0]); if (type == TYPE_ATCURSOR) { @@ -1456,7 +1484,7 @@ find_popup_win(int id) { win_T *wp = win_id2wp(id); - if (wp != NULL && !bt_popup(wp->w_buffer)) + if (wp != NULL && !WIN_IS_POPUP(wp)) { semsg(_("E993: window %d is not a popup window"), id); return NULL; @@ -1524,8 +1552,13 @@ f_popup_settext(typval_T *argvars, typva if (wp != NULL) { - popup_set_buffer_text(wp->w_buffer, argvars[1]); - popup_adjust_position(wp); + if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_LIST) + semsg(_(e_invarg2), tv_get_string(&argvars[1])); + else + { + popup_set_buffer_text(wp->w_buffer, argvars[1]); + popup_adjust_position(wp); + } } } @@ -1880,7 +1913,7 @@ f_popup_getoptions(typval_T *argvars, ty int error_if_popup_window() { - if (bt_popup(curwin->w_buffer)) + if (WIN_IS_POPUP(curwin)) { emsg(_("E994: Not allowed in a popup window")); return TRUE; diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro --- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -1,4 +1,5 @@ /* buffer.c */ +void buffer_ensure_loaded(buf_T *buf); int open_buffer(int read_stdin, exarg_T *eap, int flags); void set_bufref(bufref_T *bufref, buf_T *buf); int bufref_valid(bufref_T *bufref); diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -1005,7 +1005,7 @@ get_wcr_attr(win_T *wp) if (*wp->w_p_wcr != NUL) wcr_attr = syn_name2attr(wp->w_p_wcr); #ifdef FEAT_TEXT_PROP - if (bt_popup(wp->w_buffer) && wcr_attr == 0) + if (WIN_IS_POPUP(wp) && wcr_attr == 0) wcr_attr = HL_ATTR(HLF_PNI); #endif return wcr_attr; @@ -1555,11 +1555,7 @@ win_update(win_T *wp) if (mid_start == 0) { mid_end = wp->w_height; - if (ONE_WINDOW -#ifdef FEAT_TEXT_PROP - && !bt_popup(wp->w_buffer) -#endif - ) + if (ONE_WINDOW && !WIN_IS_POPUP(wp)) { /* Clear the screen when it was not done by win_del_lines() or * win_ins_lines() above, "screen_cleared" is FALSE or MAYBE @@ -2085,9 +2081,7 @@ win_update(win_T *wp) && wp->w_lines[idx].wl_lnum == lnum && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) -#ifdef FEAT_TEXT_PROP - && !bt_popup(wp->w_buffer) -#endif + && !WIN_IS_POPUP(wp) && srow + wp->w_lines[idx].wl_size > wp->w_height #ifdef FEAT_DIFF && diff_check_fill(wp, lnum) == 0 @@ -2244,7 +2238,7 @@ win_update(win_T *wp) } #endif #ifdef FEAT_TEXT_PROP - else if (bt_popup(wp->w_buffer)) + else if (WIN_IS_POPUP(wp)) { // popup line that doesn't fit is left as-is wp->w_botline = lnum; @@ -2310,11 +2304,8 @@ win_update(win_T *wp) // Make sure the rest of the screen is blank // put '~'s on rows that aren't part of the file. - win_draw_end(wp, -#ifdef FEAT_TEXT_PROP - bt_popup(wp->w_buffer) ? ' ' : -#endif - '~', ' ', FALSE, row, wp->w_height, HLF_EOB); + win_draw_end(wp, WIN_IS_POPUP(wp) ? ' ' : '~', + ' ', FALSE, row, wp->w_height, HLF_EOB); } #ifdef SYN_TIME_LIMIT @@ -3673,7 +3664,7 @@ win_line( area_highlighting = TRUE; } #ifdef FEAT_TEXT_PROP - if (bt_popup(wp->w_buffer)) + if (WIN_IS_POPUP(wp)) screen_line_flags |= SLF_POPUP; #endif 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 @@ -559,7 +559,7 @@ func Test_popup_valid_arguments() endfunc func Test_popup_invalid_arguments() - call assert_fails('call popup_create(666, {})', 'E714:') + call assert_fails('call popup_create(666, {})', 'E86:') call popup_clear() call assert_fails('call popup_create("text", "none")', 'E715:') call popup_clear() @@ -1654,3 +1654,18 @@ func Test_popupwin_garbage_collect() call popup_close(winid) delfunc MyPopupFilter endfunc + +func Test_popupwin_with_buffer() + call writefile(['some text', 'in a buffer'], 'XsomeFile') + let buf = bufadd('XsomeFile') + call assert_equal(0, bufloaded(buf)) + let winid = popup_create(buf, {}) + call assert_notequal(0, winid) + let pos = popup_getpos(winid) + call assert_equal(2, pos.height) + call assert_equal(1, bufloaded(buf)) + call popup_close(winid) + call assert_equal({}, popup_getpos(winid)) + call assert_equal(1, bufloaded(buf)) + exe 'bwipe! ' .. buf +endfunc diff --git a/src/ui.c b/src/ui.c --- a/src/ui.c +++ b/src/ui.c @@ -1078,7 +1078,7 @@ clip_start_selection(int col, int row, i int col_cp = col; wp = mouse_find_win(&row_cp, &col_cp, FIND_POPUP); - if (wp != NULL && bt_popup(wp->w_buffer)) + if (wp != NULL && WIN_IS_POPUP(wp)) { // Click in a popup window restricts selection to that window, // excluding the border. @@ -3052,7 +3052,7 @@ retnomove: #ifdef FEAT_TEXT_PROP // Click in a popup window may start dragging or modeless selection, // but not much else. - if (bt_popup(wp->w_buffer)) + if (WIN_IS_POPUP(wp)) { on_sep_line = 0; in_popup_win = TRUE; 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 */ /**/ + 1612, +/**/ 1611, /**/ 1610, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -614,8 +614,14 @@ extern int (*dyn_libintl_wputenv)(const #define VALID_TOPLINE 0x80 // w_topline is valid (for cursor position) // Values for w_popup_flags. -#define POPF_HIDDEN 1 // popup is not displayed -#define POPF_HANDLED 2 // popup was just redrawn or filtered +#define POPF_IS_POPUP 1 // this is a popup window +#define POPF_HIDDEN 2 // popup is not displayed +#define POPF_HANDLED 4 // popup was just redrawn or filtered +#ifdef FEAT_TEXT_PROP +# define WIN_IS_POPUP(wp) ((wp)->w_popup_flags != 0) +#else +# define WIN_IS_POPUP(wp) 0 +#endif /* * Terminal highlighting attribute bits. diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -4887,7 +4887,7 @@ win_free( int win_unlisted(win_T *wp) { - return wp == aucmd_win || bt_popup(wp->w_buffer); + return wp == aucmd_win || WIN_IS_POPUP(wp); } #if defined(FEAT_TEXT_PROP) || defined(PROTO) @@ -4898,7 +4898,10 @@ win_unlisted(win_T *wp) void win_free_popup(win_T *win) { - win_close_buffer(win, DOBUF_WIPE, FALSE); + if (bt_popup(win->w_buffer)) + win_close_buffer(win, DOBUF_WIPE, FALSE); + else + close_buffer(win, win->w_buffer, 0, FALSE); # if defined(FEAT_TIMERS) if (win->w_popup_timer != NULL) stop_timer(win->w_popup_timer); @@ -6605,7 +6608,7 @@ restore_win_noblock( curbuf = curwin->w_buffer; } #ifdef FEAT_TEXT_PROP - else if (bt_popup(curwin->w_buffer)) + else if (WIN_IS_POPUP(curwin)) // original window was closed and now we're in a popup window: Go // to the first valid window. win_goto(firstwin);