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/src/buffer.c b/src/buffer.c
index ee68bc9..74e0ea6 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -122,6 +122,23 @@
 }
 
 /*
+ * 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
index 01afa3d..f87fd1f 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1963,15 +1963,8 @@
 {
     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 @@
 		    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
index 2015fd8..5c9f929 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -4525,7 +4525,7 @@
 	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_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
index a10d4e0..3af35b6 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -997,14 +997,26 @@
     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 @@
 	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_set_buffer_text(buf, argvars[0]);
+    if (new_buffer)
+	popup_set_buffer_text(buf, argvars[0]);
 
     if (type == TYPE_ATCURSOR)
     {
@@ -1456,7 +1484,7 @@
 {
     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 @@
 
     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 @@
     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
index 45fcf2b..010be9c 100644
--- 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
index 4c3ded2..7541345 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1005,7 +1005,7 @@
     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 @@
 	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 @@
 		    && 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 @@
 	}
 #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 @@
 
 	// 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 @@
 	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
index a3e6acc..32ce96b 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -559,7 +559,7 @@
 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 @@
   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
index 1732fe8..1e6c146 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -1078,7 +1078,7 @@
 	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 @@
 #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
index a1c5f88..7bd2327 100644
--- a/src/version.c
+++ b/src/version.c
@@ -778,6 +778,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1612,
+/**/
     1611,
 /**/
     1610,
diff --git a/src/vim.h b/src/vim.h
index cb1d389..410d9d7 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -614,8 +614,14 @@
 #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
index d40b060..b40aae6 100644
--- a/src/window.c
+++ b/src/window.c
@@ -4887,7 +4887,7 @@
     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 @@
     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 @@
 	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);