diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2017-03-12 21:01:05 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-12 21:01:04 +0100 |
commit | bf5110266ca950b7ccdd07817f61020204e1caae (patch) | |
tree | 08dfec88cb61bed3a4bf0ae2b36d7910de3801cf | |
parent | c8f0f8fea6e3170db0d68d61dd84f3c3ef9ee77c (diff) | |
parent | c5e61b41a53c4fac93f1101ece1a23168eccb3a3 (diff) | |
download | rneovim-bf5110266ca950b7ccdd07817f61020204e1caae.tar.gz rneovim-bf5110266ca950b7ccdd07817f61020204e1caae.tar.bz2 rneovim-bf5110266ca950b7ccdd07817f61020204e1caae.zip |
Merge #6262 from justinmk/dirchanged
DirChanged fixes
-rw-r--r-- | src/nvim/buffer_defs.h | 3 | ||||
-rw-r--r-- | src/nvim/eval.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 29 | ||||
-rw-r--r-- | src/nvim/file_search.c | 30 | ||||
-rw-r--r-- | src/nvim/memory.c | 7 | ||||
-rw-r--r-- | src/nvim/window.c | 41 | ||||
-rw-r--r-- | test/functional/autocmd/dirchanged_spec.lua | 78 |
7 files changed, 128 insertions, 64 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index a1b5633c32..3e9767adde 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -825,8 +825,7 @@ struct tabpage_S { frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots dictitem_T tp_winvar; ///< variable for "t:" Dictionary dict_T *tp_vars; ///< internal variables, local to tab page - char_u *localdir; ///< Absolute path of local directory or - ///< NULL + char_u *tp_localdir; ///< Absolute path of local CWD or NULL }; /* diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6dc7e5606e..78f470b10a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10901,7 +10901,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } case kCdScopeTab: // FALLTHROUGH assert(tp); - from = tp->localdir; + from = tp->tp_localdir; if (from) { break; } @@ -12015,7 +12015,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; case kCdScopeTab: assert(tp); - rettv->vval.v_number = tp->localdir ? 1 : 0; + rettv->vval.v_number = tp->tp_localdir ? 1 : 0; break; case kCdScopeGlobal: // The global scope never has a local directory diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 87b6959101..774380b2f0 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6943,24 +6943,27 @@ void free_cd_dir(void) /// @param scope Scope of the function call (global, tab or window). void post_chdir(CdScope scope) { - // The local directory of the current window is always overwritten. + // Always overwrite the window-local CWD. xfree(curwin->w_localdir); curwin->w_localdir = NULL; - // Overwrite the local directory of the current tab page for `cd` and `tcd` + // Overwrite the tab-local CWD for :cd, :tcd. if (scope >= kCdScopeTab) { - xfree(curtab->localdir); - curtab->localdir = NULL; + xfree(curtab->tp_localdir); + curtab->tp_localdir = NULL; } if (scope < kCdScopeGlobal) { - // If still in global directory, need to remember current directory as - // global directory. + // If still in global directory, set CWD as the global directory. if (globaldir == NULL && prev_dir != NULL) { globaldir = vim_strsave(prev_dir); } } + char cwd[MAXPATHL]; + if (os_dirname((char_u *)cwd, MAXPATHL) != OK) { + return; + } switch (scope) { case kCdScopeGlobal: // We are now in the global directory, no need to remember its name. @@ -6968,23 +6971,17 @@ void post_chdir(CdScope scope) globaldir = NULL; break; case kCdScopeTab: - // Remember this local directory for the tab page. - if (os_dirname(NameBuff, MAXPATHL) == OK) { - curtab->localdir = vim_strsave(NameBuff); - } + curtab->tp_localdir = (char_u *)xstrdup(cwd); break; case kCdScopeWindow: - // Remember this local directory for the window. - if (os_dirname(NameBuff, MAXPATHL) == OK) { - curwin->w_localdir = vim_strsave(NameBuff); - } + curwin->w_localdir = (char_u *)xstrdup(cwd); break; case kCdScopeInvalid: - // We should never get here assert(false); } - shorten_fnames(TRUE); + shorten_fnames(true); + do_autocmd_dirchanged(cwd, scope); } /// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`. diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 79a39c6503..b73d9944ce 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1519,7 +1519,7 @@ theend: return file_name; } -static void do_autocmd_dirchanged(char_u *new_dir, CdScope scope) +void do_autocmd_dirchanged(char *new_dir, CdScope scope) { static bool recursive = false; @@ -1550,10 +1550,11 @@ static void do_autocmd_dirchanged(char_u *new_dir, CdScope scope) } dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); - dict_add_nr_str(dict, "cwd", 0L, new_dir); + dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); dict_set_keys_readonly(dict); - apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, new_dir, false, NULL); + apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, + NULL); dict_clear(dict); @@ -1565,14 +1566,25 @@ static void do_autocmd_dirchanged(char_u *new_dir, CdScope scope) /// @return OK or FAIL int vim_chdirfile(char_u *fname) { - char_u dir[MAXPATHL]; + char dir[MAXPATHL]; STRLCPY(dir, fname, MAXPATHL); - *path_tail_with_sep(dir) = NUL; - if (os_chdir((char *)dir) != 0) { + *path_tail_with_sep((char_u *)dir) = NUL; + + if (os_dirname(NameBuff, sizeof(NameBuff)) != OK) { + NameBuff[0] = NUL; + } + + if (os_chdir(dir) != 0) { return FAIL; } - do_autocmd_dirchanged(dir, kCdScopeWindow); + +#ifdef BACKSLASH_IN_FILENAME + slash_adjust(dir); +#endif + if (!strequal(dir, (char *)NameBuff)) { + do_autocmd_dirchanged(dir, kCdScopeWindow); + } return OK; } @@ -1587,10 +1599,6 @@ int vim_chdir(char_u *new_dir, CdScope scope) } int r = os_chdir((char *)dir_name); - if (r == 0) { - do_autocmd_dirchanged(dir_name, scope); - } - xfree(dir_name); return r; } diff --git a/src/nvim/memory.c b/src/nvim/memory.c index b593936d7b..58c01fbe7a 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -475,6 +475,13 @@ void *xmemdup(const void *data, size_t len) return memcpy(xmalloc(len), data, len); } +/// Returns true if strings `a` and `b` are equal. Arguments may be NULL. +bool strequal(const char *a, const char *b) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (a == NULL && b == NULL) || (a && b && strcmp(a, b) == 0); +} + /* * Avoid repeating the error message many times (they take 1 second each). * Did_outofmem_msg is reset when a character is read. diff --git a/src/nvim/window.c b/src/nvim/window.c index 73a60b2e04..7c7d73fdfb 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2998,8 +2998,7 @@ void free_tabpage(tabpage_T *tp) hash_init(&tp->tp_vars->dv_hashtab); unref_var_dict(tp->tp_vars); - - xfree(tp->localdir); // Free tab-local working directory + xfree(tp->tp_localdir); xfree(tp); } @@ -3025,7 +3024,7 @@ int win_new_tabpage(int after, char_u *filename) return FAIL; } - newtp->localdir = tp->localdir ? vim_strsave(tp->localdir) : NULL; + newtp->tp_localdir = tp->tp_localdir ? vim_strsave(tp->tp_localdir) : NULL; curtab = newtp; @@ -3617,28 +3616,38 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, curwin->w_cursor.coladd = 0; changed_line_abv_curs(); /* assume cursor position needs updating */ - // The new directory is either the local directory of the window, of the tab - // or NULL. - char_u *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->localdir; + // New directory is either the local directory of the window, tab or NULL. + char *new_dir = (char *)(curwin->w_localdir + ? curwin->w_localdir : curtab->tp_localdir); + + char cwd[MAXPATHL]; + if (os_dirname((char_u *)cwd, MAXPATHL) != OK) { + cwd[0] = NUL; + } if (new_dir) { // Window/tab has a local directory: Save current directory as global - // directory (unless that was done already) and change to the local - // directory. + // (unless that was done already) and change to the local directory. if (globaldir == NULL) { - char_u cwd[MAXPATHL]; - - if (os_dirname(cwd, MAXPATHL) == OK) { - globaldir = vim_strsave(cwd); + if (cwd[0] != NUL) { + globaldir = (char_u *)xstrdup(cwd); } } - if (os_chdir((char *)new_dir) == 0) { + if (os_chdir(new_dir) == 0) { + if (!p_acd && !strequal(new_dir, cwd)) { + do_autocmd_dirchanged(new_dir, curwin->w_localdir + ? kCdScopeWindow : kCdScopeTab); + } shorten_fnames(true); } } else if (globaldir != NULL) { - /* Window doesn't have a local directory and we are not in the global - * directory: Change to the global directory. */ - ignored = os_chdir((char *)globaldir); + // Window doesn't have a local directory and we are not in the global + // directory: Change to the global directory. + if (os_chdir((char *)globaldir) == 0) { + if (!p_acd && !strequal((char *)globaldir, cwd)) { + do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal); + } + } xfree(globaldir); globaldir = NULL; shorten_fnames(TRUE); diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua index 15196dbd44..63cf0bc410 100644 --- a/test/functional/autocmd/dirchanged_spec.lua +++ b/test/functional/autocmd/dirchanged_spec.lua @@ -20,29 +20,44 @@ describe('autocmd DirChanged', function() before_each(function() clear() - command('autocmd DirChanged * let [g:event, g:scope, g:cdcount] = [copy(v:event), expand("<amatch>"), 1 + get(g:, "cdcount", 0)]') + command('autocmd DirChanged * let [g:getcwd, g:ev, g:amatch, g:cdcount] ' + ..' = [getcwd(), copy(v:event), expand("<amatch>"), 1 + get(g:, "cdcount", 0)]') + -- Normalize path separators. + command([[autocmd DirChanged * let g:ev['cwd'] = substitute(g:ev['cwd'], '\\', '/', 'g')]]) + command([[autocmd DirChanged * let g:getcwd = substitute(g:getcwd, '\\', '/', 'g')]]) end) it('sets v:event', function() command('lcd '..dirs[1]) - eq({cwd=dirs[1], scope='window'}, eval('g:event')) + eq({cwd=dirs[1], scope='window'}, eval('g:ev')) eq(1, eval('g:cdcount')) command('tcd '..dirs[2]) - eq({cwd=dirs[2], scope='tab'}, eval('g:event')) + eq({cwd=dirs[2], scope='tab'}, eval('g:ev')) eq(2, eval('g:cdcount')) command('cd '..dirs[3]) - eq({cwd=dirs[3], scope='global'}, eval('g:event')) + eq({cwd=dirs[3], scope='global'}, eval('g:ev')) eq(3, eval('g:cdcount')) end) + it('sets getcwd() during event #6260', function() + command('lcd '..dirs[1]) + eq(dirs[1], eval('g:getcwd')) + + command('tcd '..dirs[2]) + eq(dirs[2], eval('g:getcwd')) + + command('cd '..dirs[3]) + eq(dirs[3], eval('g:getcwd')) + end) + it('disallows recursion', function() command('set shellslash') -- Set up a _nested_ handler. command('autocmd DirChanged * nested lcd '..dirs[3]) command('lcd '..dirs[1]) - eq({cwd=dirs[1], scope='window'}, eval('g:event')) + eq({cwd=dirs[1], scope='window'}, eval('g:ev')) eq(1, eval('g:cdcount')) -- autocmd changed to dirs[3], but did NOT trigger another DirChanged. eq(dirs[3], eval('getcwd()')) @@ -50,32 +65,32 @@ describe('autocmd DirChanged', function() it('sets <amatch> to CWD "scope"', function() command('lcd '..dirs[1]) - eq('window', eval('g:scope')) + eq('window', eval('g:amatch')) command('tcd '..dirs[2]) - eq('tab', eval('g:scope')) + eq('tab', eval('g:amatch')) command('cd '..dirs[3]) - eq('global', eval('g:scope')) + eq('global', eval('g:amatch')) end) it('does not trigger if :cd fails', function() - command('let g:event = {}') + command('let g:ev = {}') local status1, err1 = pcall(function() command('lcd '..dirs[1] .. '/doesnotexist') end) - eq({}, eval('g:event')) + eq({}, eval('g:ev')) local status2, err2 = pcall(function() command('lcd '..dirs[2] .. '/doesnotexist') end) - eq({}, eval('g:event')) + eq({}, eval('g:ev')) local status3, err3 = pcall(function() command('lcd '..dirs[3] .. '/doesnotexist') end) - eq({}, eval('g:event')) + eq({}, eval('g:ev')) eq(false, status1) eq(false, status2) @@ -90,24 +105,53 @@ describe('autocmd DirChanged', function() command('set autochdir') command('split '..dirs[1]..'/foo') - eq({cwd=dirs[1], scope='window'}, eval('g:event')) + eq({cwd=dirs[1], scope='window'}, eval('g:ev')) command('split '..dirs[2]..'/bar') - eq({cwd=dirs[2], scope='window'}, eval('g:event')) + eq({cwd=dirs[2], scope='window'}, eval('g:ev')) + + eq(2, eval('g:cdcount')) + end) + + it("is triggered by switching to win/tab with different CWD #6054", function() + command('lcd '..dirs[3]) -- window 3 + command('split '..dirs[2]..'/foo') -- window 2 + command('lcd '..dirs[2]) + command('split '..dirs[1]..'/bar') -- window 1 + command('lcd '..dirs[1]) + + command('2wincmd w') -- window 2 + eq({cwd=dirs[2], scope='window'}, eval('g:ev')) + + eq(4, eval('g:cdcount')) + command('tabnew') -- tab 2 (tab-local CWD) + eq(4, eval('g:cdcount')) -- same CWD, no DirChanged event + command('tcd '..dirs[3]) + command('tabnext') -- tab 1 (no tab-local CWD) + eq({cwd=dirs[2], scope='window'}, eval('g:ev')) + command('tabnext') -- tab 2 + eq({cwd=dirs[3], scope='tab'}, eval('g:ev')) + eq(7, eval('g:cdcount')) + + command('tabnext') -- tab 1 + command('3wincmd w') -- window 3 + eq(9, eval('g:cdcount')) + command('tabnext') -- tab 2 (has the *same* CWD) + eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event end) it('is triggered by nvim_set_current_dir()', function() request('nvim_set_current_dir', dirs[1]) - eq({cwd=dirs[1], scope='global'}, eval('g:event')) + eq({cwd=dirs[1], scope='global'}, eval('g:ev')) request('nvim_set_current_dir', dirs[2]) - eq({cwd=dirs[2], scope='global'}, eval('g:event')) + eq({cwd=dirs[2], scope='global'}, eval('g:ev')) local status, err = pcall(function() request('nvim_set_current_dir', '/doesnotexist') end) eq(false, status) eq('Failed to change directory', string.match(err, ': (.*)')) - eq({cwd=dirs[2], scope='global'}, eval('g:event')) + eq({cwd=dirs[2], scope='global'}, eval('g:ev')) end) end) |