patch 8.0.1459: cannot handle change of directory

Problem:    Cannot handle change of directory.
Solution:   Add the DirChanged autocommand event. (Andy Massimino,
            closes #888)  Avoid changing directory for 'autochdir' too often.
diff --git a/src/buffer.c b/src/buffer.c
index ecd8f4e..c0d3d3d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -595,7 +595,7 @@
 
 #ifdef FEAT_DIFF
     if (diffopt_hiddenoff() && !unload_buf && buf->b_nwindows == 0)
-    	diff_buf_delete(buf);	/* Clear 'diff' for hidden buffer. */
+	diff_buf_delete(buf);	/* Clear 'diff' for hidden buffer. */
 #endif
 
     /* Return when a window is displaying the buffer or when it's not
@@ -657,9 +657,6 @@
 	--buf->b_nwindows;
 #endif
 
-    /* Change directories when the 'acd' option is set. */
-    DO_AUTOCHDIR
-
     /*
      * Remove the buffer from the list.
      */
@@ -1862,7 +1859,7 @@
 {
     if ((starting == 0 || test_autochdir)
 	    && curbuf->b_ffname != NULL
-	    && vim_chdirfile(curbuf->b_ffname) == OK)
+	    && vim_chdirfile(curbuf->b_ffname, "auto") == OK)
 	shorten_fnames(TRUE);
 }
 #endif
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 539e39f..cd915c9 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -9048,11 +9048,19 @@
 	    EMSG(_(e_failed));
 	else
 	{
-	    post_chdir(eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir);
+	    int is_local_chdir = eap->cmdidx == CMD_lcd
+						  || eap->cmdidx == CMD_lchdir;
+
+	    post_chdir(is_local_chdir);
 
 	    /* Echo the new current directory if the command was typed. */
 	    if (KeyTyped || p_verbose >= 5)
 		ex_pwd(eap);
+#ifdef FEAT_AUTOCMD
+	    apply_autocmds(EVENT_DIRCHANGED,
+		    is_local_chdir ? (char_u *)"window" : (char_u *)"global",
+		    new_dir, FALSE, curbuf);
+#endif
 	}
 	vim_free(tofree);
     }
@@ -9932,7 +9940,7 @@
 			*dirnow = NUL;
 		    if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR))
 		    {
-			if (vim_chdirfile(fname) == OK)
+			if (vim_chdirfile(fname, NULL) == OK)
 			    shorten_fnames(TRUE);
 		    }
 		    else if (*dirnow != NUL
diff --git a/src/fileio.c b/src/fileio.c
index fb5af64..ab216ab 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -7798,6 +7798,7 @@
     {"CursorHoldI",	EVENT_CURSORHOLDI},
     {"CursorMoved",	EVENT_CURSORMOVED},
     {"CursorMovedI",	EVENT_CURSORMOVEDI},
+    {"DirChanged",	EVENT_DIRCHANGED},
     {"EncodingChanged",	EVENT_ENCODINGCHANGED},
     {"FileEncoding",	EVENT_ENCODINGCHANGED},
     {"FileAppendPost",	EVENT_FILEAPPENDPOST},
@@ -9588,7 +9589,7 @@
     {
 	sfname = vim_strsave(fname);
 	/* Don't try expanding FileType, Syntax, FuncUndefined, WindowID,
-	 * ColorScheme or QuickFixCmd* */
+	 * ColorScheme, QuickFixCmd* or DirChanged */
 	if (event == EVENT_FILETYPE
 		|| event == EVENT_SYNTAX
 		|| event == EVENT_FUNCUNDEFINED
@@ -9597,7 +9598,8 @@
 		|| event == EVENT_QUICKFIXCMDPRE
 		|| event == EVENT_COLORSCHEME
 		|| event == EVENT_OPTIONSET
-		|| event == EVENT_QUICKFIXCMDPOST)
+		|| event == EVENT_QUICKFIXCMDPOST
+		|| event == EVENT_DIRCHANGED)
 	    fname = vim_strsave(fname);
 	else
 	    fname = FullName_save(fname, FALSE);
diff --git a/src/gui_mac.c b/src/gui_mac.c
index db9f959..6386b63 100644
--- a/src/gui_mac.c
+++ b/src/gui_mac.c
@@ -1105,7 +1105,8 @@
 	}
 
 	/* Change directory to the location of the first file. */
-	if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK)
+	if (GARGCOUNT > 0
+		      && vim_chdirfile(alist_name(&GARGLIST[0]), "drop") == OK)
 	    shorten_fnames(TRUE);
 
 	goto finished;
diff --git a/src/main.c b/src/main.c
index aea652b..9f443ae 100644
--- a/src/main.c
+++ b/src/main.c
@@ -264,7 +264,7 @@
 	 * Hint: to avoid this when typing a command use a forward slash.
 	 * If the cd fails, it doesn't matter.
 	 */
-	(void)vim_chdirfile(params.fname);
+	(void)vim_chdirfile(params.fname, "drop");
 	if (start_dir != NULL)
 	    mch_dirname(start_dir, MAXPATHL);
     }
@@ -314,7 +314,7 @@
 						&& STRCMP(NameBuff, "/") == 0)
 	{
 	    if (params.fname != NULL)
-		(void)vim_chdirfile(params.fname);
+		(void)vim_chdirfile(params.fname, "drop");
 	    else
 	    {
 		expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
diff --git a/src/netbeans.c b/src/netbeans.c
index c906d28..199cfd6 100644
--- a/src/netbeans.c
+++ b/src/netbeans.c
@@ -2663,7 +2663,7 @@
     nbdebug(("EVT: %s", buffer));
 
     nb_send(buffer, "netbeans_file_opened");
-    if (p_acd && vim_chdirfile(bufp->b_ffname) == OK)
+    if (p_acd && vim_chdirfile(bufp->b_ffname, "auto") == OK)
 	shorten_fnames(TRUE);
 }
 
diff --git a/src/os_win32.c b/src/os_win32.c
index d356c7e..a42df6f 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -7193,7 +7193,7 @@
     {
 	do_cmdline_cmd((char_u *)":rewind");
 	if (GARGCOUNT == 1 && used_file_full_path)
-	    (void)vim_chdirfile(alist_name(&GARGLIST[0]));
+	    (void)vim_chdirfile(alist_name(&GARGLIST[0]), "drop");
     }
 
     set_alist_count();
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index 68c40da..6999683 100644
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -82,7 +82,7 @@
 int get_real_state(void);
 int after_pathsep(char_u *b, char_u *p);
 int same_directory(char_u *f1, char_u *f2);
-int vim_chdirfile(char_u *fname);
+int vim_chdirfile(char_u *fname, char *trigger_autocmd);
 int vim_stat(const char *name, stat_T *stp);
 char_u *parse_shape_opt(int what);
 int get_shape_idx(int mouse);
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index b7d43b5..c504f70 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -1190,3 +1190,59 @@
   call assert_fails('lv½ /x', 'E480')
   au!
 endfunc
+
+function s:Before_test_dirchanged()
+  augroup test_dirchanged
+    autocmd!
+  augroup END
+  let s:li = []
+  let s:dir_this = getcwd()
+  let s:dir_other = s:dir_this . '/foo'
+  call mkdir(s:dir_other)
+endfunc
+
+function s:After_test_dirchanged()
+  exe 'cd' s:dir_this
+  call delete(s:dir_other, 'd')
+  augroup test_dirchanged
+    autocmd!
+  augroup END
+endfunc
+
+function Test_dirchanged_global()
+  call s:Before_test_dirchanged()
+  autocmd test_dirchanged DirChanged global call add(s:li, "cd:")
+  autocmd test_dirchanged DirChanged global call add(s:li, expand("<afile>"))
+  exe 'cd' s:dir_other
+  call assert_equal(["cd:", s:dir_other], s:li)
+  exe 'lcd' s:dir_other
+  call assert_equal(["cd:", s:dir_other], s:li)
+  call s:After_test_dirchanged()
+endfunc
+
+function Test_dirchanged_local()
+  call s:Before_test_dirchanged()
+  autocmd test_dirchanged DirChanged window call add(s:li, "lcd:")
+  autocmd test_dirchanged DirChanged window call add(s:li, expand("<afile>"))
+  exe 'cd' s:dir_other
+  call assert_equal([], s:li)
+  exe 'lcd' s:dir_other
+  call assert_equal(["lcd:", s:dir_other], s:li)
+  call s:After_test_dirchanged()
+endfunc
+
+function Test_dirchanged_auto()
+  call s:Before_test_dirchanged()
+  call test_autochdir()
+  autocmd test_dirchanged DirChanged auto call add(s:li, "auto:")
+  autocmd test_dirchanged DirChanged auto call add(s:li, expand("<afile>"))
+  set acd
+  exe 'cd ..'
+  call assert_equal([], s:li)
+  exe 'edit ' . s:dir_other . '/Xfile'
+  call assert_equal(s:dir_other, getcwd())
+  call assert_equal(["auto:", s:dir_other], s:li)
+  set noacd
+  bwipe!
+  call s:After_test_dirchanged()
+endfunc
diff --git a/src/version.c b/src/version.c
index c836d3f..4706262 100644
--- a/src/version.c
+++ b/src/version.c
@@ -772,6 +772,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1459,
+/**/
     1458,
 /**/
     1457,
diff --git a/src/vim.h b/src/vim.h
index 1f9671a..ad37235 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1276,6 +1276,7 @@
     EVENT_CMDWINLEAVE,		/* before leaving the cmdline window */
     EVENT_COLORSCHEME,		/* after loading a colorscheme */
     EVENT_COMPLETEDONE,		/* after finishing insert complete */
+    EVENT_DIRCHANGED,		/* after changing directory as a result of user cmd */
     EVENT_FILEAPPENDPOST,	/* after appending to a file */
     EVENT_FILEAPPENDPRE,	/* before appending to a file */
     EVENT_FILEAPPENDCMD,	/* append to a file using command */