aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2016-06-04 11:05:05 -0400
committerJustin M. Keyes <justinkz@gmail.com>2016-06-04 11:05:05 -0400
commit02e6914a938fcca98f9062d502accb4d68a65d6e (patch)
treeb38b5515b75dcdf768fa6f65f629830f9a222c26
parent8958863e2678fd038ae089f837fa46d19574f977 (diff)
parent4e9f7684f636874e8127f0d018b32f8bc104a29f (diff)
downloadrneovim-02e6914a938fcca98f9062d502accb4d68a65d6e.tar.gz
rneovim-02e6914a938fcca98f9062d502accb4d68a65d6e.tar.bz2
rneovim-02e6914a938fcca98f9062d502accb4d68a65d6e.zip
Merge pull request #4860 from jamessan/tab-win-precedence
tcd: Determine correct scope from user input
-rw-r--r--src/nvim/eval.c68
-rw-r--r--src/nvim/ex_docmd.c3
-rw-r--r--src/nvim/ex_docmd.h1
-rw-r--r--test/functional/ex_cmds/cd_spec.lua162
4 files changed, 169 insertions, 65 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 7edf511c18..84c96752c7 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -9888,7 +9888,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv)
static void f_getcwd(typval_T *argvars, typval_T *rettv)
{
// Possible scope of working directory to return.
- CdScope scope = MIN_CD_SCOPE;
+ CdScope scope = kCdScopeInvalid;
// Numbers of the scope objects (window, tab) we want the working directory
// of. A `-1` means to skip this scope, a `0` means the current object.
@@ -9917,26 +9917,27 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv)
return;
}
scope_number[i] = argvars[i].vval.v_number;
- // The scope is the current iteration step.
- scope = i;
// It is an error for the scope number to be less than `-1`.
if (scope_number[i] < -1) {
EMSG(_(e_invarg));
return;
}
+ // Use the narrowest scope the user requested
+ if (scope_number[i] >= 0 && scope == kCdScopeInvalid) {
+ // The scope is the current iteration step.
+ scope = i;
+ } else if (scope_number[i] < 0) {
+ scope = i + 1;
+ }
}
- // Normalize scope, the number of the new scope will be 0.
- if (scope_number[scope] < 0) {
- // Arguments to `getcwd` always end at second-highest scope, so scope will
- // always be <= `MAX_CD_SCOPE`.
- scope++;
+ // If the user didn't specify anything, default to window scope
+ if (scope == kCdScopeInvalid) {
+ scope = MIN_CD_SCOPE;
}
// Find the tabpage by number
- if (scope_number[kCdScopeTab] == -1) {
- tp = NULL;
- } else if (scope_number[kCdScopeTab] > 0) {
+ if (scope_number[kCdScopeTab] > 0) {
tp = find_tabpage(scope_number[kCdScopeTab]);
if (!tp) {
EMSG(_("E5000: Cannot find tab number."));
@@ -9945,16 +9946,14 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv)
}
// Find the window in `tp` by number, `NULL` if none.
- if (scope_number[kCdScopeWindow] == -1) {
- win = NULL;
- } else if (scope_number[kCdScopeWindow] >= 0) {
- if (!tp) {
+ if (scope_number[kCdScopeWindow] >= 0) {
+ if (scope_number[kCdScopeTab] < 0) {
EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return;
}
if (scope_number[kCdScopeWindow] > 0) {
- win = find_win_by_nr(&argvars[0], curtab);
+ win = find_win_by_nr(&argvars[0], tp);
if (!win) {
EMSG(_("E5002: Cannot find window number."));
return;
@@ -9989,6 +9988,9 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv)
}
}
break;
+ case kCdScopeInvalid:
+ // We should never get here
+ assert(false);
}
if (from) {
@@ -10836,7 +10838,7 @@ static void f_has_key(typval_T *argvars, typval_T *rettv)
static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
{
// Possible scope of working directory to return.
- CdScope scope = MIN_CD_SCOPE;
+ CdScope scope = kCdScopeInvalid;
// Numbers of the scope objects (window, tab) we want the working directory
// of. A `-1` means to skip this scope, a `0` means the current object.
@@ -10861,25 +10863,26 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
return;
}
scope_number[i] = argvars[i].vval.v_number;
- // The scope is the current iteration step.
- scope = i;
if (scope_number[i] < -1) {
EMSG(_(e_invarg));
return;
}
+ // Use the narrowest scope the user requested
+ if (scope_number[i] >= 0 && scope == kCdScopeInvalid) {
+ // The scope is the current iteration step.
+ scope = i;
+ } else if (scope_number[i] < 0) {
+ scope = i + 1;
+ }
}
- // Normalize scope, the number of the new scope will be 0.
- if (scope_number[scope] < 0) {
- // Arguments to `haslocaldir` always end at second-highest scope, so scope
- // will always be <= `MAX_CD_SCOPE`.
- scope++;
+ // If the user didn't specify anything, default to window scope
+ if (scope == kCdScopeInvalid) {
+ scope = MIN_CD_SCOPE;
}
// Find the tabpage by number
- if (scope_number[kCdScopeTab] == -1) {
- tp = NULL;
- } else if (scope_number[kCdScopeTab] > 0) {
+ if (scope_number[kCdScopeTab] > 0) {
tp = find_tabpage(scope_number[kCdScopeTab]);
if (!tp) {
EMSG(_("5000: Cannot find tab number."));
@@ -10888,16 +10891,14 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
}
// Find the window in `tp` by number, `NULL` if none.
- if (scope_number[kCdScopeWindow] == -1) {
- win = NULL;
- } else if (scope_number[kCdScopeWindow] >= 0) {
- if (!tp) {
+ if (scope_number[kCdScopeWindow] >= 0) {
+ if (scope_number[kCdScopeTab] < 0) {
EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return;
}
if (scope_number[kCdScopeWindow] > 0) {
- win = find_win_by_nr(&argvars[0], curtab);
+ win = find_win_by_nr(&argvars[0], tp);
if (!win) {
EMSG(_("E5002: Cannot find window number."));
return;
@@ -10918,6 +10919,9 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
// The global scope never has a local directory
rettv->vval.v_number = 0;
break;
+ case kCdScopeInvalid:
+ // We should never get here
+ assert(false);
}
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index dd096af6b3..dea52ee112 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -6865,6 +6865,9 @@ void post_chdir(CdScope scope)
curwin->w_localdir = vim_strsave(NameBuff);
}
break;
+ case kCdScopeInvalid:
+ // We should never get here
+ assert(false);
}
shorten_fnames(TRUE);
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index dbfc64e2f1..bafad20169 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -26,6 +26,7 @@
/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes
/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead.
typedef enum {
+ kCdScopeInvalid = -1,
kCdScopeWindow, ///< Affects one window.
kCdScopeTab, ///< Affects one tab page.
kCdScopeGlobal, ///< Affects the entire instance of Neovim.
diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua
index 69467632a4..00b3b083d1 100644
--- a/test/functional/ex_cmds/cd_spec.lua
+++ b/test/functional/ex_cmds/cd_spec.lua
@@ -1,49 +1,145 @@
-- Specs for :cd, :tcd, :lcd and getcwd()
-local helpers = require('test.functional.helpers')
-local execute, eq, clear, eval, exc_exec =
- helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.exc_exec
local lfs = require('lfs')
+local helpers = require('test.functional.helpers')
+
+local eq = helpers.eq
+local call = helpers.call
+local clear = helpers.clear
+local execute = helpers.execute
+local exc_exec = helpers.exc_exec
-- These directories will be created for testing
local directories = {
- 'Xtest-functional-ex_cmds-cd_spec.1', -- Tab
- 'Xtest-functional-ex_cmds-cd_spec.2', -- Window
- 'Xtest-functional-ex_cmds-cd_spec.3', -- New global
+ tab = 'Xtest-functional-ex_cmds-cd_spec.tab', -- Tab
+ window = 'Xtest-functional-ex_cmds-cd_spec.window', -- Window
+ global = 'Xtest-functional-ex_cmds-cd_spec.global', -- New global
}
-- Shorthand writing to get the current working directory
-local cwd = function() return eval('getcwd( )') end -- effective working dir
-local wcwd = function() return eval('getcwd( 0 )') end -- window dir
-local tcwd = function() return eval('getcwd(-1, 0)') end -- tab dir
---local gcwd = function() return eval('getcwd(-1, -1)') end -- global dir
+local cwd = function(...) return call('getcwd', ...) end -- effective working dir
+local wcwd = function() return cwd(0) end -- window dir
+local tcwd = function() return cwd(-1, 0) end -- tab dir
-- Same, except these tell us if there is a working directory at all
---local lwd = function() return eval('haslocaldir( )') end -- effective working dir
-local wlwd = function() return eval('haslocaldir( 0 )') end -- window dir
-local tlwd = function() return eval('haslocaldir(-1, 0)') end -- tab dir
+local lwd = function(...) return call('haslocaldir', ...) end -- effective working dir
+local wlwd = function() return lwd(0) end -- window dir
+local tlwd = function() return lwd(-1, 0) end -- tab dir
--local glwd = function() return eval('haslocaldir(-1, -1)') end -- global dir
-- Test both the `cd` and `chdir` variants
for _, cmd in ipairs {'cd', 'chdir'} do
- describe(':*' .. cmd, function()
+ describe(':' .. cmd, function()
before_each(function()
clear()
- for _, d in ipairs(directories) do
+ for _, d in pairs(directories) do
lfs.mkdir(d)
end
+ directories.start = cwd()
end)
after_each(function()
- for _, d in ipairs(directories) do
+ for _, d in pairs(directories) do
lfs.rmdir(d)
end
end)
- it('works', function()
- -- Store the initial working directory
- local globalDir = cwd()
+ describe('using explicit scope', function()
+ it('for window', function()
+ local globalDir = directories.start
+ local globalwin = call('winnr')
+ local tabnr = call('tabpagenr')
+
+ -- Everything matches globalDir to start
+ eq(globalDir, cwd(globalwin))
+ eq(globalDir, cwd(globalwin, tabnr))
+ eq(0, lwd(globalwin))
+ eq(0, lwd(globalwin, tabnr))
+
+ execute('bot split')
+ local localwin = call('winnr')
+ -- Initial window is still using globalDir
+ eq(globalDir, cwd(localwin))
+ eq(globalDir, cwd(localwin, tabnr))
+ eq(0, lwd(globalwin))
+ eq(0, lwd(globalwin, tabnr))
+
+ execute('silent l' .. cmd .. ' ' .. directories.window)
+ -- From window with local dir, the original window
+ -- is still reporting the global dir
+ eq(globalDir, cwd(globalwin))
+ eq(globalDir, cwd(globalwin, tabnr))
+ eq(0, lwd(globalwin))
+ eq(0, lwd(globalwin, tabnr))
+
+ -- Window with local dir reports as such
+ eq(globalDir .. '/' .. directories.window, cwd(localwin))
+ eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr))
+ eq(1, lwd(localwin))
+ eq(1, lwd(localwin, tabnr))
+
+ execute('tabnew')
+ -- From new tab page, original window reports global dir
+ eq(globalDir, cwd(globalwin, tabnr))
+ eq(0, lwd(globalwin, tabnr))
+
+ -- From new tab page, local window reports as such
+ eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr))
+ eq(1, lwd(localwin, tabnr))
+ end)
+
+ it('for tab page', function()
+ local globalDir = directories.start
+ local globaltab = call('tabpagenr')
+
+ -- Everything matches globalDir to start
+ eq(globalDir, cwd(-1, 0))
+ eq(globalDir, cwd(-1, globaltab))
+ eq(0, lwd(-1, 0))
+ eq(0, lwd(-1, globaltab))
+
+ execute('tabnew')
+ execute('silent t' .. cmd .. ' ' .. directories.tab)
+ local localtab = call('tabpagenr')
+
+ -- From local tab page, original tab reports globalDir
+ eq(globalDir, cwd(-1, globaltab))
+ eq(0, lwd(-1, globaltab))
+
+ -- new tab reports local
+ eq(globalDir .. '/' .. directories.tab, cwd(-1, 0))
+ eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab))
+ eq(1, lwd(-1, 0))
+ eq(1, lwd(-1, localtab))
+
+ execute('tabnext')
+ -- From original tab page, local reports as such
+ eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab))
+ eq(1, lwd(-1, localtab))
+ end)
+ end)
+
+ describe('getcwd(-1, -1)', function()
+ it('works', function()
+ eq(directories.start, cwd(-1, -1))
+ eq(0, lwd(-1, -1))
+ end)
+
+ it('works with tab-local pwd', function()
+ execute('silent t' .. cmd .. ' ' .. directories.tab)
+ eq(directories.start, cwd(-1, -1))
+ eq(0, lwd(-1, -1))
+ end)
+
+ it('works with window-local pwd', function()
+ execute('silent l' .. cmd .. ' ' .. directories.window)
+ eq(directories.start, cwd(-1, -1))
+ eq(0, lwd(-1, -1))
+ end)
+ end)
+ it('works', function()
+ local globalDir = directories.start
-- Create a new tab first and verify that is has the same working dir
execute('tabnew')
eq(globalDir, cwd())
@@ -53,8 +149,8 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(0, wlwd())
-- Change tab-local working directory and verify it is different
- execute('silent t' .. cmd .. ' ' .. directories[1])
- eq(globalDir .. '/' .. directories[1], cwd())
+ execute('silent t' .. cmd .. ' ' .. directories.tab)
+ eq(globalDir .. '/' .. directories.tab, cwd())
eq(cwd(), tcwd()) -- working directory maches tab directory
eq(1, tlwd())
eq(cwd(), wcwd()) -- still no window-directory
@@ -64,16 +160,16 @@ for _, cmd in ipairs {'cd', 'chdir'} do
execute('new')
eq(1, tlwd()) -- Still tab-local working directory
eq(0, wlwd()) -- Still no window-local working directory
- eq(globalDir .. '/' .. directories[1], cwd())
- execute('silent l' .. cmd .. ' ../' .. directories[2])
- eq(globalDir .. '/' .. directories[2], cwd())
- eq(globalDir .. '/' .. directories[1], tcwd())
+ eq(globalDir .. '/' .. directories.tab, cwd())
+ execute('silent l' .. cmd .. ' ../' .. directories.window)
+ eq(globalDir .. '/' .. directories.window, cwd())
+ eq(globalDir .. '/' .. directories.tab, tcwd())
eq(1, wlwd())
-- Verify the first window still has the tab local directory
execute('wincmd w')
- eq(globalDir .. '/' .. directories[1], cwd())
- eq(globalDir .. '/' .. directories[1], tcwd())
+ eq(globalDir .. '/' .. directories.tab, cwd())
+ eq(globalDir .. '/' .. directories.tab, tcwd())
eq(0, wlwd()) -- No window-local directory
-- Change back to initial tab and verify working directory has stayed
@@ -83,11 +179,11 @@ for _, cmd in ipairs {'cd', 'chdir'} do
eq(0, wlwd())
-- Verify global changes don't affect local ones
- execute('silent ' .. cmd .. ' ' .. directories[3])
- eq(globalDir .. '/' .. directories[3], cwd())
+ execute('silent ' .. cmd .. ' ' .. directories.global)
+ eq(globalDir .. '/' .. directories.global, cwd())
execute('tabnext')
- eq(globalDir .. '/' .. directories[1], cwd())
- eq(globalDir .. '/' .. directories[1], tcwd())
+ eq(globalDir .. '/' .. directories.tab, cwd())
+ eq(globalDir .. '/' .. directories.tab, tcwd())
eq(0, wlwd()) -- Still no window-local directory in this window
-- Unless the global change happened in a tab with local directory
@@ -101,9 +197,9 @@ for _, cmd in ipairs {'cd', 'chdir'} do
-- But not in a window with its own local directory
execute('tabnext | wincmd w')
- eq(globalDir .. '/' .. directories[2], cwd() )
+ eq(globalDir .. '/' .. directories.window, cwd() )
eq(0 , tlwd())
- eq(globalDir .. '/' .. directories[2], wcwd())
+ eq(globalDir .. '/' .. directories.window, wcwd())
end)
end)
end