patch 8.2.1297: when a test fails it's often not easy to see where

Problem:    When a test fails it's often not easy to see what the call stack
            is.
Solution:   Add more entries from the call stack in the exception message.
diff --git a/src/debugger.c b/src/debugger.c
index 91ab5ee..00fb9c8 100644
--- a/src/debugger.c
+++ b/src/debugger.c
@@ -105,7 +105,7 @@
 	vim_free(debug_newval);
 	debug_newval = NULL;
     }
-    sname = estack_sfile();
+    sname = estack_sfile(FALSE);
     if (sname != NULL)
 	msg((char *)sname);
     vim_free(sname);
@@ -344,7 +344,7 @@
     }
     else
     {
-	char_u	*sname = estack_sfile();
+	char_u	*sname = estack_sfile(FALSE);
 	int	max = get_maxbacktrace_level(sname);
 
 	if (debug_backtrace_level > max)
@@ -365,7 +365,7 @@
     int	    i = 0;
     int	    max;
 
-    sname = estack_sfile();
+    sname = estack_sfile(FALSE);
     max = get_maxbacktrace_level(sname);
     if (sname != NULL)
     {
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 04c3b46..987e92d 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -8268,8 +8268,10 @@
 #define SPEC_SFILE  (SPEC_CFILE + 1)
 		    "<slnum>",		// ":so" file line number
 #define SPEC_SLNUM  (SPEC_SFILE + 1)
+		    "<stack>",		// call stack
+#define SPEC_STACK  (SPEC_SLNUM + 1)
 		    "<afile>",		// autocommand file name
-#define SPEC_AFILE  (SPEC_SLNUM + 1)
+#define SPEC_AFILE  (SPEC_STACK + 1)
 		    "<abuf>",		// autocommand buffer number
 #define SPEC_ABUF   (SPEC_AFILE + 1)
 		    "<amatch>",		// autocommand match name
@@ -8520,10 +8522,13 @@
 		break;
 
 	case SPEC_SFILE:	// file name for ":so" command
-		result = estack_sfile();
+	case SPEC_STACK:	// call stack
+		result = estack_sfile(spec_idx == SPEC_SFILE);
 		if (result == NULL)
 		{
-		    *errormsg = _("E498: no :source file name to substitute for \"<sfile>\"");
+		    *errormsg = spec_idx == SPEC_SFILE
+			? _("E498: no :source file name to substitute for \"<sfile>\"")
+			: _("E489: no call stack to substitute for \"<stack>\"");
 		    return NULL;
 		}
 		resultbuf = result;	    // remember allocated string
diff --git a/src/ex_eval.c b/src/ex_eval.c
index f0ffaff..58088a0 100644
--- a/src/ex_eval.c
+++ b/src/ex_eval.c
@@ -290,7 +290,7 @@
 
 		    // Get the source name and lnum now, it may change before
 		    // reaching do_errthrow().
-		    elem->sfile = estack_sfile();
+		    elem->sfile = estack_sfile(FALSE);
 		    elem->slnum = SOURCING_LNUM;
 		}
 	    }
@@ -549,7 +549,7 @@
     }
     else
     {
-	excp->throw_name = estack_sfile();
+	excp->throw_name = estack_sfile(FALSE);
 	if (excp->throw_name == NULL)
 	    excp->throw_name = vim_strsave((char_u *)"");
 	if (excp->throw_name == NULL)
diff --git a/src/message.c b/src/message.c
index 76ec456..91b2fe2 100644
--- a/src/message.c
+++ b/src/message.c
@@ -461,7 +461,7 @@
 
     if (SOURCING_NAME != NULL && other_sourcing_name())
     {
-	char_u	    *sname = estack_sfile();
+	char_u	    *sname = estack_sfile(FALSE);
 	char_u	    *tofree = sname;
 
 	if (sname == NULL)
diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro
index fd4c104..00f1301 100644
--- a/src/proto/scriptfile.pro
+++ b/src/proto/scriptfile.pro
@@ -4,7 +4,7 @@
 estack_T *estack_push_ufunc(ufunc_T *ufunc, long lnum);
 int estack_top_is_ufunc(ufunc_T *ufunc, long lnum);
 estack_T *estack_pop(void);
-char_u *estack_sfile(void);
+char_u *estack_sfile(int is_sfile);
 void ex_runtime(exarg_T *eap);
 int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
 int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
diff --git a/src/scriptfile.c b/src/scriptfile.c
index ce269a1..ce9c15d 100644
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -111,58 +111,68 @@
 
 /*
  * Get the current value for <sfile> in allocated memory.
+ * "is_sfile" is TRUE for <sfile> itself.
  */
     char_u *
-estack_sfile(void)
+estack_sfile(int is_sfile)
 {
     estack_T	*entry;
 #ifdef FEAT_EVAL
+    garray_T	ga;
     size_t	len;
     int		idx;
-    char	*res;
-    size_t	done;
+    etype_T	last_type = ETYPE_SCRIPT;
+    char	*type_name;
 #endif
 
     entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
-    if (entry->es_name == NULL)
-	return NULL;
 #ifdef FEAT_EVAL
-    if (entry->es_info.ufunc == NULL)
+    if (is_sfile && entry->es_type != ETYPE_UFUNC)
 #endif
+    {
+	if (entry->es_name == NULL)
+	    return NULL;
 	return vim_strsave(entry->es_name);
-
+    }
 #ifdef FEAT_EVAL
+    // Give information about each stack entry up to the root.
     // For a function we compose the call stack, as it was done in the past:
     //   "function One[123]..Two[456]..Three"
-    len = STRLEN(entry->es_name) + 10;
-    for (idx = exestack.ga_len - 2; idx >= 0; --idx)
+    ga_init2(&ga, sizeof(char), 100);
+    for (idx = 0; idx < exestack.ga_len; ++idx)
     {
 	entry = ((estack_T *)exestack.ga_data) + idx;
-	if (entry->es_name == NULL || entry->es_info.ufunc == NULL)
+	if (entry->es_name != NULL)
 	{
-	    ++idx;
-	    break;
+	    len = STRLEN(entry->es_name) + 15;
+	    type_name = "";
+	    if (entry->es_type != last_type)
+	    {
+		switch (entry->es_type)
+		{
+		    case ETYPE_SCRIPT: type_name = "script "; break;
+		    case ETYPE_UFUNC: type_name = "function "; break;
+		    default: type_name = ""; break;
+		}
+		last_type = entry->es_type;
+	    }
+	    len += STRLEN(type_name);
+	    if (ga_grow(&ga, len) == FAIL)
+		break;
+	    if (idx == exestack.ga_len - 1 || entry->es_lnum == 0)
+		// For the bottom entry: do not add the line number, it is used
+		// in <slnum>.  Also leave it out when the number is not set.
+		vim_snprintf(ga.ga_data + ga.ga_len, len, "%s%s%s",
+				type_name, entry->es_name,
+				idx == exestack.ga_len - 1 ? "" : "..");
+	    else
+		vim_snprintf(ga.ga_data + ga.ga_len, len, "%s%s[%ld]..",
+				    type_name, entry->es_name, entry->es_lnum);
+	    ga.ga_len += STRLEN(ga.ga_data + ga.ga_len);
 	}
-	len += STRLEN(entry->es_name) + 15;
     }
 
-    res = (char *)alloc((int)len);
-    if (res != NULL)
-    {
-	STRCPY(res, "function ");
-	while (idx < exestack.ga_len - 1)
-	{
-	    done = STRLEN(res);
-	    entry = ((estack_T *)exestack.ga_data) + idx;
-	    vim_snprintf(res + done, len - done, "%s[%ld]..",
-					       entry->es_name, entry->es_lnum);
-	    ++idx;
-	}
-	done = STRLEN(res);
-	entry = ((estack_T *)exestack.ga_data) + idx;
-	vim_snprintf(res + done, len - done, "%s", entry->es_name);
-    }
-    return (char_u *)res;
+    return (char_u *)ga.ga_data;
 #endif
 }
 
diff --git a/src/testdir/test_expand_func.vim b/src/testdir/test_expand_func.vim
index 18437b7..41673e5 100644
--- a/src/testdir/test_expand_func.vim
+++ b/src/testdir/test_expand_func.vim
@@ -16,17 +16,47 @@
   return str2nr(expand('<sflnum>'))  
 endfunc
 
-func Test_expand_sfile()
-  call assert_match('test_expand_func\.vim$', s:sfile)
-  call assert_match('^function .*\.\.Test_expand_sfile$', expand('<sfile>'))
+" This test depends on the location in the test file, put it first.
+func Test_expand_sflnum()
+  call assert_equal(5, s:sflnum)
+  call assert_equal(22, str2nr(expand('<sflnum>')))
+
+  " Line-continuation
+  call assert_equal(
+        \ 25,
+        \ str2nr(expand('<sflnum>')))
 
   " Call in script-local function
-  call assert_match('^function .*\.\.Test_expand_sfile\[5\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
+  call assert_equal(16, s:expand_sflnum())
+
+  " Call in command
+  command Flnum echo expand('<sflnum>')
+  call assert_equal(34, str2nr(trim(execute('Flnum'))))
+  delcommand Flnum
+endfunc
+
+func Test_expand_sfile_and_stack()
+  call assert_match('test_expand_func\.vim$', s:sfile)
+  let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$'
+  call assert_match(expected , expand('<sfile>'))
+  call assert_match(expected , expand('<stack>'))
+
+  " Call in script-local function
+  call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
 
   " Call in command
   command Sfile echo expand('<sfile>')
-  call assert_match('^function .*\.\.Test_expand_sfile$', trim(execute('Sfile')))
+  call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$', trim(execute('Sfile')))
   delcommand Sfile
+
+  " Use <stack> from sourced script.
+  let lines =<< trim END
+    let g:stack_value = expand('<stack>')
+  END
+  call writefile(lines, 'Xstack')
+  source Xstack
+  call assert_match('\<Xstack$', g:stack_value)
+  call delete('Xstack')
 endfunc
 
 func Test_expand_slnum()
@@ -47,24 +77,6 @@
   delcommand Slnum
 endfunc
 
-func Test_expand_sflnum()
-  call assert_equal(5, s:sflnum)
-  call assert_equal(52, str2nr(expand('<sflnum>')))
-
-  " Line-continuation
-  call assert_equal(
-        \ 55,
-        \ str2nr(expand('<sflnum>')))
-
-  " Call in script-local function
-  call assert_equal(16, s:expand_sflnum())
-
-  " Call in command
-  command Flnum echo expand('<sflnum>')
-  call assert_equal(64, str2nr(trim(execute('Flnum'))))
-  delcommand Flnum
-endfunc
-
 func Test_expand()
   new
   call assert_equal("",  expand('%:S'))
diff --git a/src/testing.c b/src/testing.c
index de89bd9..137d5fe 100644
--- a/src/testing.c
+++ b/src/testing.c
@@ -22,7 +22,7 @@
 prepare_assert_error(garray_T *gap)
 {
     char    buf[NUMBUFLEN];
-    char_u  *sname = estack_sfile();
+    char_u  *sname = estack_sfile(FALSE);
 
     ga_init2(gap, 1, 100);
     if (sname != NULL)
diff --git a/src/version.c b/src/version.c
index 6a5f199..c45c155 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1297,
+/**/
     1296,
 /**/
     1295,