patch 8.1.1406: popup_hide() and popup_show() not implemented yet

Problem:    popup_hide() and popup_show() not implemented yet.
Solution:   Implement the functions.
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 3256b22..b15e74d 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -810,6 +810,8 @@
 #ifdef FEAT_TEXT_PROP
     {"popup_close",	1, 1, f_popup_close},
     {"popup_create",	2, 2, f_popup_create},
+    {"popup_hide",	1, 1, f_popup_hide},
+    {"popup_show",	1, 1, f_popup_show},
 #endif
 #ifdef FEAT_FLOAT
     {"pow",		2, 2, f_pow},
diff --git a/src/popupwin.c b/src/popupwin.c
index e39ee67..0857f6b 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -195,18 +195,85 @@
 }
 
 /*
+ * Find the popup window with window-ID "id".
+ * If the popup window does not exist NULL is returned.
+ * If the window is not a popup window, and error message is given.
+ */
+    static win_T *
+find_popup_win(int id)
+{
+    win_T *wp = win_id2wp(id);
+
+    if (wp != NULL && !bt_popup(wp->w_buffer))
+    {
+	semsg(_("E993: window %d is not a popup window"), id);
+	return NULL;
+    }
+    return wp;
+}
+
+/*
+ * Return TRUE if there any popups that are not hidden.
+ */
+    int
+popup_any_visible(void)
+{
+    win_T *wp;
+
+    for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+	if ((wp->w_popup_flags & PFL_HIDDEN) == 0)
+	    return TRUE;
+    for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
+	if ((wp->w_popup_flags & PFL_HIDDEN) == 0)
+	    return TRUE;
+    return FALSE;
+}
+
+/*
  * popup_close({id})
  */
     void
 f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
 {
-    int		nr = (int)tv_get_number(argvars);
+    int		id = (int)tv_get_number(argvars);
 
-    popup_close(nr);
+    popup_close(id);
+}
+
+/*
+ * popup_hide({id})
+ */
+    void
+f_popup_hide(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    int		id = (int)tv_get_number(argvars);
+    win_T	*wp = find_popup_win(id);
+
+    if (wp != NULL && (wp->w_popup_flags & PFL_HIDDEN) == 0)
+    {
+	wp->w_popup_flags |= PFL_HIDDEN;
+	redraw_all_later(NOT_VALID);
+    }
+}
+
+/*
+ * popup_show({id})
+ */
+    void
+f_popup_show(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    int		id = (int)tv_get_number(argvars);
+    win_T	*wp = find_popup_win(id);
+
+    if (wp != NULL && (wp->w_popup_flags & PFL_HIDDEN) != 0)
+    {
+	wp->w_popup_flags &= ~PFL_HIDDEN;
+	redraw_all_later(NOT_VALID);
+    }
 }
 
     static void
-popup_undisplay(win_T *wp)
+popup_free(win_T *wp)
 {
     if (wp->w_winrow + wp->w_height >= cmdline_row)
 	clear_cmdline = TRUE;
@@ -232,7 +299,7 @@
 		first_popupwin = wp->w_next;
 	    else
 		prev->w_next = wp->w_next;
-	    popup_undisplay(wp);
+	    popup_free(wp);
 	    return;
 	}
 
@@ -258,7 +325,7 @@
 		*root = wp->w_next;
 	    else
 		prev->w_next = wp->w_next;
-	    popup_undisplay(wp);
+	    popup_free(wp);
 	    return;
 	}
 }
diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro
index 172c079..64fca5f 100644
--- a/src/proto/popupwin.pro
+++ b/src/proto/popupwin.pro
@@ -1,6 +1,9 @@
 /* popupwin.c */
 void f_popup_create(typval_T *argvars, typval_T *rettv);
+int popup_any_visible(void);
 void f_popup_close(typval_T *argvars, typval_T *rettv);
+void f_popup_hide(typval_T *argvars, typval_T *rettv);
+void f_popup_show(typval_T *argvars, typval_T *rettv);
 void popup_close(int id);
 void popup_close_tabpage(tabpage_T *tp, int id);
 void close_all_popups(void);
diff --git a/src/screen.c b/src/screen.c
index 30e1bb2..40cff9a 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -610,7 +610,7 @@
     }
 #ifdef FEAT_TEXT_PROP
     // TODO: avoid redrawing everything when there is a popup window.
-    if (first_popupwin != NULL || curtab->tp_first_popupwin != NULL)
+    if (popup_any_visible())
 	type = NOT_VALID;
 #endif
 
@@ -999,9 +999,9 @@
 
     // Reset all the VALID_POPUP flags.
     for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
-	wp->w_valid &= ~VALID_POPUP;
+	wp->w_popup_flags &= ~PFL_REDRAWN;
     for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
-	wp->w_valid &= ~VALID_POPUP;
+	wp->w_popup_flags &= ~PFL_REDRAWN;
 
     // TODO: don't redraw every popup every time.
     for (;;)
@@ -1012,14 +1012,14 @@
 	lowest_zindex = INT_MAX;
 	lowest_wp = NULL;
 	for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
-	    if ((wp->w_valid & VALID_POPUP) == 0
+	    if ((wp->w_popup_flags & (PFL_REDRAWN|PFL_HIDDEN)) == 0
 					       && wp->w_zindex < lowest_zindex)
 	    {
 		lowest_zindex = wp->w_zindex;
 		lowest_wp = wp;
 	    }
 	for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
-	    if ((wp->w_valid & VALID_POPUP) == 0
+	    if ((wp->w_popup_flags & (PFL_REDRAWN|PFL_HIDDEN)) == 0
 					       && wp->w_zindex < lowest_zindex)
 	    {
 		lowest_zindex = wp->w_zindex;
@@ -1029,7 +1029,7 @@
 	if (lowest_wp == NULL)
 	    break;
 	win_update(lowest_wp);
-	lowest_wp->w_valid |= VALID_POPUP;
+	lowest_wp->w_popup_flags |= PFL_REDRAWN;
     }
 }
 #endif
diff --git a/src/structs.h b/src/structs.h
index ca7c540..925bf03 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2871,6 +2871,7 @@
     int		w_vsep_width;	    /* Number of separator columns (0 or 1). */
     pos_save_T	w_save_cursor;	    /* backup of cursor pos and topline */
 #ifdef FEAT_TEXT_PROP
+    int		w_popup_flags;	    // PFL_ values
     int		w_zindex;
     int		w_maxheight;	    // "maxheight" for popup window
     int		w_maxwidth;	    // "maxwidth" for popup window
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 04c67a2..73efe0a 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -76,3 +76,41 @@
 
   bwipe!
 endfunc
+
+func Test_popup_hide()
+  topleft vnew
+  call setline(1, 'hello')
+
+  let winid = popup_create('world', {
+	\ 'line': 1,
+	\ 'col': 1,
+	\})
+  redraw
+  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
+  call assert_equal('world', line)
+
+  call popup_hide(winid)
+  redraw
+  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
+  call assert_equal('hello', line)
+
+  call popup_show(winid)
+  redraw
+  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
+  call assert_equal('world', line)
+
+
+  call popup_close(winid)
+  redraw
+  let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
+  call assert_equal('hello', line)
+
+  " error is given for existing non-popup window
+  call assert_fails('call popup_hide(win_getid())', 'E993:')
+
+  " no error non-existing window
+  call popup_hide(1234234)
+  call popup_show(41234234)
+
+  bwipe!
+endfunc
diff --git a/src/version.c b/src/version.c
index 04de1af..5df9639 100644
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1406,
+/**/
     1405,
 /**/
     1404,
diff --git a/src/vim.h b/src/vim.h
index 3389ef5..a52ba5e 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -612,7 +612,10 @@
 #define VALID_BOTLINE	0x20	// w_botine and w_empty_rows are valid
 #define VALID_BOTLINE_AP 0x40	// w_botine is approximated
 #define VALID_TOPLINE	0x80	// w_topline is valid (for cursor position)
-#define VALID_POPUP	0x100	// popup has been redrawn
+
+// Values for w_popup_flags.
+#define PFL_HIDDEN	1	// popup is not displayed
+#define PFL_REDRAWN	2	// popup was just redrawn
 
 /*
  * Terminal highlighting attribute bits.