patch 8.2.4335: no autocommand event triggered before changing directory

Problem:    No autocommand event triggered before changing directory. (Ronnie
            Magatti)
Solution:   Add DirChangedPre. (closes #9721)
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 37b4a3d..c410ae6 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -326,6 +326,7 @@
 |FileChangedRO|		before making the first change to a read-only file
 
 |DiffUpdated|		after diffs have been updated
+|DirChangedPre|		before the working directory will change
 |DirChanged|		after the working directory has changed
 
 |ShellCmdPost|		after executing a shell command
@@ -738,6 +739,11 @@
 				what kind of diff is being used (internal or
 				external) this can be triggered on every
 				change or when doing |:diffupdate|.
+							*DirChangedPre*
+DirChangedPre			The working directory is going to be changed,
+				as with ||DirChanged|.  The pattern is like
+				with |DirChanged|.  The new directory can be
+				found in v:event.directory.
 							*DirChanged*
 DirChanged			The working directory has changed in response
 				to the |:cd| or |:tcd| or |:lcd| commands, or
diff --git a/src/autocmd.c b/src/autocmd.c
index a10be9b..97b27ef 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -119,6 +119,7 @@
     {"CursorMovedI",	EVENT_CURSORMOVEDI},
     {"DiffUpdated",	EVENT_DIFFUPDATED},
     {"DirChanged",	EVENT_DIRCHANGED},
+    {"DirChangedPre",	EVENT_DIRCHANGEDPRE},
     {"EncodingChanged",	EVENT_ENCODINGCHANGED},
     {"ExitPre",		EVENT_EXITPRE},
     {"FileEncoding",	EVENT_ENCODINGCHANGED},
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index d8adf85..4003605 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -7343,6 +7343,26 @@
 }
 
 /*
+ * Trigger DirChangedPre for "acmd_fname" with directory "new_dir".
+ */
+    void
+trigger_DirChangedPre(char_u *acmd_fname, char_u *new_dir)
+{
+#ifdef FEAT_EVAL
+    dict_T	    *v_event;
+    save_v_event_T  save_v_event;
+
+    v_event = get_v_event(&save_v_event);
+    (void)dict_add_string(v_event, "directory", new_dir);
+    dict_set_items_ro(v_event);
+#endif
+    apply_autocmds(EVENT_DIRCHANGEDPRE, acmd_fname, new_dir, FALSE, curbuf);
+#ifdef FEAT_EVAL
+    restore_v_event(v_event, &save_v_event);
+#endif
+}
+
+/*
  * Change directory function used by :cd/:tcd/:lcd Ex commands and the
  * chdir() function.
  * scope == CDSCOPE_WINDOW: changes the window-local directory
@@ -7358,7 +7378,7 @@
 {
     char_u	*pdir = NULL;
     int		dir_differs;
-    char_u	*acmd_fname;
+    char_u	*acmd_fname = NULL;
     char_u	**pp;
 
     if (new_dir == NULL || allbuf_locked())
@@ -7411,12 +7431,23 @@
 	new_dir = NameBuff;
     }
     dir_differs = pdir == NULL
-	|| pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
-    if (dir_differs && vim_chdir(new_dir))
+			    || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+    if (dir_differs)
     {
-	emsg(_(e_command_failed));
-	vim_free(pdir);
-	return FALSE;
+	if (scope == CDSCOPE_WINDOW)
+	    acmd_fname = (char_u *)"window";
+	else if (scope == CDSCOPE_TABPAGE)
+	    acmd_fname = (char_u *)"tabpage";
+	else
+	    acmd_fname = (char_u *)"global";
+	trigger_DirChangedPre(acmd_fname, new_dir);
+
+	if (vim_chdir(new_dir))
+	{
+	    emsg(_(e_command_failed));
+	    vim_free(pdir);
+	    return FALSE;
+	}
     }
 
     if (scope == CDSCOPE_WINDOW)
@@ -7431,16 +7462,7 @@
     post_chdir(scope);
 
     if (dir_differs)
-    {
-	if (scope == CDSCOPE_WINDOW)
-	    acmd_fname = (char_u *)"window";
-	else if (scope == CDSCOPE_TABPAGE)
-	    acmd_fname = (char_u *)"tabpage";
-	else
-	    acmd_fname = (char_u *)"global";
-	apply_autocmds(EVENT_DIRCHANGED, acmd_fname, new_dir, FALSE,
-							    curbuf);
-    }
+	apply_autocmds(EVENT_DIRCHANGED, acmd_fname, new_dir, FALSE, curbuf);
     return TRUE;
 }
 
diff --git a/src/misc2.c b/src/misc2.c
index fac836f..38da7e5 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -1914,6 +1914,9 @@
 	// nothing to do
 	return OK;
 
+    if (trigger_autocmd != NULL)
+	trigger_DirChangedPre((char_u *)trigger_autocmd, new_dir);
+
     if (mch_chdir((char *)new_dir) != 0)
 	return FAIL;
 
diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro
index 0fed2f0..3be7471 100644
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -48,6 +48,7 @@
 void do_exedit(exarg_T *eap, win_T *old_curwin);
 void free_cd_dir(void);
 void post_chdir(cdscope_T scope);
+void trigger_DirChangedPre(char_u *acmd_fname, char_u *new_dir);
 int changedir_func(char_u *new_dir, int forceit, cdscope_T scope);
 void ex_cd(exarg_T *eap);
 void do_sleep(long msec, int hide_cursor);
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 1ecb883..3ff4c3c 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
Binary files differ
diff --git a/src/version.c b/src/version.c
index 9ef520e..ab53d51 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4335,
+/**/
     4334,
 /**/
     4333,
diff --git a/src/vim.h b/src/vim.h
index 67b4e33..166edf5 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1304,6 +1304,7 @@
     EVENT_CURSORMOVEDI,		// cursor was moved in Insert mode
     EVENT_DIFFUPDATED,		// after diffs were updated
     EVENT_DIRCHANGED,		// after user changed directory
+    EVENT_DIRCHANGEDPRE,	// before directory changes
     EVENT_ENCODINGCHANGED,	// after changing the 'encoding' option
     EVENT_EXITPRE,		// before exiting
     EVENT_FILEAPPENDCMD,	// append to a file using command