patch 8.1.1522: poup_notification() not implemented yet

Problem:    Popup_notification() not implemented yet.
Solution:   Implement it.
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 134280f..0ef7609 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -819,6 +819,7 @@
     {"popup_getpos",	1, 1, f_popup_getpos},
     {"popup_hide",	1, 1, f_popup_hide},
     {"popup_move",	2, 2, f_popup_move},
+    {"popup_notification", 2, 2, f_popup_notification},
     {"popup_show",	1, 1, f_popup_show},
 #endif
 #ifdef FEAT_FLOAT
diff --git a/src/popupwin.c b/src/popupwin.c
index adfa462..d0f106f 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -110,7 +110,6 @@
 {
     dictitem_T	*di;
 
-    vim_memset(array, 0, sizeof(int) * 4);
     di = dict_find(dict, (char_u *)name, -1);
     if (di != NULL)
     {
@@ -165,6 +164,26 @@
     }
 }
 
+
+#if defined(FEAT_TIMERS)
+    static void
+popup_add_timeout(win_T *wp, int time)
+{
+    char_u	    cbbuf[50];
+    char_u	    *ptr = cbbuf;
+    typval_T	    tv;
+
+    vim_snprintf((char *)cbbuf, sizeof(cbbuf),
+				       "{_ -> popup_close(%d)}", wp->w_id);
+    if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
+    {
+	wp->w_popup_timer = create_timer(time, 0);
+	wp->w_popup_timer->tr_callback = get_callback(&tv);
+	clear_tv(&tv);
+    }
+}
+#endif
+
 /*
  * Go through the options in "dict" and apply them to buffer "buf" displayed in
  * popup window "wp".
@@ -184,31 +203,22 @@
 
     get_pos_options(wp, dict);
 
-    wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
-    if (wp->w_zindex < 1)
-	wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
-    if (wp->w_zindex > 32000)
-	wp->w_zindex = 32000;
+    di = dict_find(dict, (char_u *)"zindex", -1);
+    if (di != NULL)
+    {
+	wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
+	if (wp->w_zindex < 1)
+	    wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
+	if (wp->w_zindex > 32000)
+	    wp->w_zindex = 32000;
+    }
 
-# if defined(FEAT_TIMERS)
+#if defined(FEAT_TIMERS)
     // Add timer to close the popup after some time.
     nr = dict_get_number(dict, (char_u *)"time");
     if (nr > 0)
-    {
-	char_u	    cbbuf[50];
-	char_u	    *ptr = cbbuf;
-	typval_T    tv;
-
-	vim_snprintf((char *)cbbuf, sizeof(cbbuf),
-					   "{_ -> popup_close(%d)}", wp->w_id);
-	if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
-	{
-	    wp->w_popup_timer = create_timer(nr, 0);
-	    wp->w_popup_timer->tr_callback = get_callback(&tv);
-	    clear_tv(&tv);
-	}
-    }
-# endif
+	popup_add_timeout(wp, nr);
+#endif
 
     // Option values resulting in setting an option.
     str = dict_get_string(dict, (char_u *)"highlight", FALSE);
@@ -601,7 +611,8 @@
 typedef enum
 {
     TYPE_NORMAL,
-    TYPE_ATCURSOR
+    TYPE_ATCURSOR,
+    TYPE_NOTIFICATION
 } create_type_T;
 
 /*
@@ -659,7 +670,13 @@
     // Avoid that 'buftype' is reset when this buffer is entered.
     buf->b_p_initialized = TRUE;
 
-    nr = (int)dict_get_number(d, (char_u *)"tab");
+    if (dict_find(d, (char_u *)"tab", -1) != NULL)
+	nr = (int)dict_get_number(d, (char_u *)"tab");
+    else if (type == TYPE_NOTIFICATION)
+	nr = -1;  // notifications are global by default
+    else
+	nr = 0;
+
     if (nr == 0)
     {
 	// popup on current tab
@@ -668,9 +685,18 @@
     }
     else if (nr < 0)
     {
-	// global popup
-	wp->w_next = first_popupwin;
-	first_popupwin = wp;
+	win_T *prev = first_popupwin;
+
+	// Global popup: add at the end, so that it gets displayed on top of
+	// older ones with the same zindex. Matters for notifications.
+	if (first_popupwin == NULL)
+	    first_popupwin = wp;
+	else
+	{
+	    while (prev->w_next != NULL)
+		prev = prev->w_next;
+	    prev->w_next = wp;
+	}
     }
     else
 	// TODO: find tab page "nr"
@@ -720,9 +746,52 @@
     // set default values
     wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
 
+    if (type == TYPE_NOTIFICATION)
+    {
+	win_T  *twp, *nextwin;
+	int	height = buf->b_ml.ml_line_count + 3;
+	int	i;
+
+	// Try to not overlap with another global popup.  Guess we need 3
+	// more screen lines than buffer lines.
+	wp->w_wantline = 1;
+	for (twp = first_popupwin; twp != NULL; twp = nextwin)
+	{
+	    nextwin = twp->w_next;
+	    if (twp != wp
+		    && twp->w_zindex == POPUPWIN_NOTIFICATION_ZINDEX
+		    && twp->w_winrow <= wp->w_wantline - 1 + height
+		    && twp->w_winrow + popup_height(twp) > wp->w_wantline - 1)
+	    {
+		// move to below this popup and restart the loop to check for
+		// overlap with other popups
+		wp->w_wantline = twp->w_winrow + popup_height(twp) + 1;
+		nextwin = first_popupwin;
+	    }
+	}
+	if (wp->w_wantline + height > Rows)
+	{
+	    // can't avoid overlap, put on top in the hope that message goes
+	    // away soon.
+	    wp->w_wantline = 1;
+	}
+
+	wp->w_wantcol = 10;
+	wp->w_zindex = POPUPWIN_NOTIFICATION_ZINDEX;
+	for (i = 0; i < 4; ++i)
+	    wp->w_popup_border[i] = 1;
+	wp->w_popup_padding[1] = 1;
+	wp->w_popup_padding[3] = 1;
+	set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
+				(char_u *)"WarningMsg", OPT_FREE|OPT_LOCAL, 0);
+    }
+
     // Deal with options.
     apply_options(wp, buf, argvars[1].vval.v_dict);
 
+    if (type == TYPE_NOTIFICATION && wp->w_popup_timer == NULL)
+	popup_add_timeout(wp, 3000);
+
     popup_adjust_position(wp);
 
     wp->w_vsep_width = 0;
@@ -759,6 +828,15 @@
 }
 
 /*
+ * popup_notification({text}, {options})
+ */
+    void
+f_popup_notification(typval_T *argvars, typval_T *rettv)
+{
+    popup_create(argvars, rettv, TYPE_NOTIFICATION);
+}
+
+/*
  * 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.
@@ -1017,8 +1095,10 @@
 
 	dict_add_number(dict, "line", wp->w_winrow + 1);
 	dict_add_number(dict, "col", wp->w_wincol + 1);
-	dict_add_number(dict, "width", wp->w_width + left_extra + wp->w_popup_border[1] + wp->w_popup_padding[1]);
-	dict_add_number(dict, "height", wp->w_height + top_extra + wp->w_popup_border[2] + wp->w_popup_padding[2]);
+	dict_add_number(dict, "width", wp->w_width + left_extra
+			     + wp->w_popup_border[1] + wp->w_popup_padding[1]);
+	dict_add_number(dict, "height", wp->w_height + top_extra
+			     + wp->w_popup_border[2] + wp->w_popup_padding[2]);
 
 	dict_add_number(dict, "core_line", wp->w_winrow + 1 + top_extra);
 	dict_add_number(dict, "core_col", wp->w_wincol + 1 + left_extra);
diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro
index a274446..03d3729 100644
--- a/src/proto/popupwin.pro
+++ b/src/proto/popupwin.pro
@@ -5,6 +5,7 @@
 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_notification(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);
diff --git a/src/structs.h b/src/structs.h
index 8e6d93f..1fd4309 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1992,8 +1992,9 @@
     POPPOS_CENTER
 } poppos_T;
 
-# define POPUPWIN_DEFAULT_ZINDEX    50
-# define POPUPMENU_ZINDEX	    100
+# define POPUPWIN_DEFAULT_ZINDEX	 50
+# define POPUPMENU_ZINDEX		100
+# define POPUPWIN_NOTIFICATION_ZINDEX   200
 #endif
 
 /*
diff --git a/src/testdir/dumps/Test_popupwin_notify_01.dump b/src/testdir/dumps/Test_popupwin_notify_01.dump
new file mode 100644
index 0000000..2ecd5ee
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_notify_01.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @7|╔+0#e000002&|═@19|╗| +0#0000000&@43
+|2| @7|║+0#e000002&| |f|i|r|s|t| |n|o|t|i|f|i|c|a|t|i|o|n| |║| +0#0000000&@43
+|3| @7|╚+0#e000002&|═@19|╝| +0#0000000&@43
+|4| @73
+|5| @73
+|6| @73
+|7| @73
+|8| @73
+|9| @73
+@57|1|,|1| @10|T|o|p| 
diff --git a/src/testdir/dumps/Test_popupwin_notify_02.dump b/src/testdir/dumps/Test_popupwin_notify_02.dump
new file mode 100644
index 0000000..1017061
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_notify_02.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @7|╔+0#e000002&|═@19|╗| +0#0000000&@43
+|2| @7|║+0#e000002&| |f|i|r|s|t| |n|o|t|i|f|i|c|a|t|i|o|n| |║| +0#0000000&@43
+|3| @7|╚+0#e000002&|═@19|╝| +0#0000000&@43
+|4| @7|╔+0&#5fd7ff255|═@31|╗| +0&#ffffff0@31
+|5| @7|║+0&#5fd7ff255| |a|n|o|t|h|e|r| |i|m|p|o|r|t|a|n|t| |n|o|t|i|f|i|c|a|t|i|o|n| |║| +0&#ffffff0@31
+|6| @7|╚+0&#5fd7ff255|═@31|╝| +0&#ffffff0@31
+|7| @73
+|8| @73
+|9| @73
+@57|1|,|1| @10|T|o|p| 
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index d100dc9..9afd840 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -428,7 +428,7 @@
 
 func Test_popup_time()
   if !has('timers')
-    return
+    throw 'Skipped, timer feature not supported'
   endif
   topleft vnew
   call setline(1, 'hello')
@@ -1086,3 +1086,29 @@
   bwipe!
   call test_override('ALL', 0)
 endfunc
+
+func Test_notifications()
+  if !has('timers')
+    throw 'Skipped, timer feature not supported'
+  endif
+  if !CanRunVimInTerminal()
+    throw 'Skipped: cannot make screendumps'
+  endif
+
+  call writefile([
+	\ "call setline(1, range(1, 20))",
+	\ "hi Notification ctermbg=lightblue",
+	\ "call popup_notification('first notification', {})",
+	\], 'XtestNotifications')
+  let buf = RunVimInTerminal('-S XtestNotifications', {'rows': 10})
+  call VerifyScreenDump(buf, 'Test_popupwin_notify_01', {})
+
+  " second one goes below the first one
+  call term_sendkeys(buf, ":call popup_notification('another important notification', {'highlight': 'Notification'})\<CR>")
+  call VerifyScreenDump(buf, 'Test_popupwin_notify_02', {})
+
+
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('XtestNotifications')
+endfunc
diff --git a/src/version.c b/src/version.c
index 28877cb..9c4d068 100644
--- a/src/version.c
+++ b/src/version.c
@@ -778,6 +778,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1522,
+/**/
     1521,
 /**/
     1520,