commit 798cb0f19a3b1bbd9003418f8d5645b7cb3141ec
parent 36361d6e4a189e0d53a26bdaa8d9aa5db791b6d7
Author: zeertzjq <zeertzjq@outlook.com>
Date: Fri, 8 Aug 2025 21:50:41 +0800
vim-patch:9.1.1605: cannot specify scope for chdir() (#35239)
Problem: Cannot specify scope for chdir()
Solution: Add optional scope argument (kuuote)
closes: vim/vim#17888
https://github.com/vim/vim/commit/8a65a49d509d5a9dd5759a4287969896e8941c10
Co-authored-by: kuuote <znmxodq1@gmail.com>
Diffstat:
6 files changed, 79 insertions(+), 33 deletions(-)
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -302,6 +302,7 @@ UI
VIMSCRIPT
+• |chdir()| allows optionally specifying a scope argument.
• |cmdcomplete_info()| gets current cmdline completion info.
• |getcompletiontype()| gets command-line completion type for any string.
• |prompt_getinput()| gets current user-input in prompt-buffer.
diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt
@@ -1008,16 +1008,23 @@ charidx({string}, {idx} [, {countcc} [, {utf16}]]) *charidx()*
Return: ~
(`integer`)
-chdir({dir}) *chdir()*
- Change the current working directory to {dir}. The scope of
- the directory change depends on the directory of the current
- window:
- - If the current window has a window-local directory
- (|:lcd|), then changes the window local directory.
- - Otherwise, if the current tabpage has a local
- directory (|:tcd|) then changes the tabpage local
- directory.
- - Otherwise, changes the global directory.
+chdir({dir} [, {scope}]) *chdir()*
+ Changes the current working directory to {dir}. The scope of
+ the change is determined as follows:
+ If {scope} is not present, the current working directory is
+ changed to the scope of the current directory:
+ - If the window local directory (|:lcd|) is set, it
+ changes the current working directory for that scope.
+ - Otherwise, if the tab page local directory (|:tcd|) is
+ set, it changes the current directory for that scope.
+ - Otherwise, changes the global directory for that scope.
+
+ If {scope} is present, changes the current working directory
+ for the specified scope:
+ "window" Changes the window local directory. |:lcd|
+ "tabpage" Changes the tab page local directory. |:tcd|
+ "global" Changes the global directory. |:cd|
+
{dir} must be a String.
If successful, returns the previous working directory. Pass
this to another chdir() to restore the directory.
@@ -1033,6 +1040,7 @@ chdir({dir}) *chdir()*
Parameters: ~
• {dir} (`string`)
+ • {scope} (`string?`)
Return: ~
(`string`)
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
@@ -873,15 +873,22 @@ function vim.fn.charcol(expr, winid) end
--- @return integer
function vim.fn.charidx(string, idx, countcc, utf16) end
---- Change the current working directory to {dir}. The scope of
---- the directory change depends on the directory of the current
---- window:
---- - If the current window has a window-local directory
---- (|:lcd|), then changes the window local directory.
---- - Otherwise, if the current tabpage has a local
---- directory (|:tcd|) then changes the tabpage local
---- directory.
---- - Otherwise, changes the global directory.
+--- Changes the current working directory to {dir}. The scope of
+--- the change is determined as follows:
+--- If {scope} is not present, the current working directory is
+--- changed to the scope of the current directory:
+--- - If the window local directory (|:lcd|) is set, it
+--- changes the current working directory for that scope.
+--- - Otherwise, if the tab page local directory (|:tcd|) is
+--- set, it changes the current directory for that scope.
+--- - Otherwise, changes the global directory for that scope.
+---
+--- If {scope} is present, changes the current working directory
+--- for the specified scope:
+--- "window" Changes the window local directory. |:lcd|
+--- "tabpage" Changes the tab page local directory. |:tcd|
+--- "global" Changes the global directory. |:cd|
+---
--- {dir} must be a String.
--- If successful, returns the previous working directory. Pass
--- this to another chdir() to restore the directory.
@@ -896,8 +903,9 @@ function vim.fn.charidx(string, idx, countcc, utf16) end
--- <
---
--- @param dir string
+--- @param scope? string
--- @return string
-function vim.fn.chdir(dir) end
+function vim.fn.chdir(dir, scope) end
--- Get the amount of indent for line {lnum} according the
--- |C-indenting| rules, as with 'cindent'.
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
@@ -1190,18 +1190,25 @@ M.funcs = {
signature = 'charidx({string}, {idx} [, {countcc} [, {utf16}]])',
},
chdir = {
- args = 1,
+ args = { 1, 2 },
base = 1,
desc = [=[
- Change the current working directory to {dir}. The scope of
- the directory change depends on the directory of the current
- window:
- - If the current window has a window-local directory
- (|:lcd|), then changes the window local directory.
- - Otherwise, if the current tabpage has a local
- directory (|:tcd|) then changes the tabpage local
- directory.
- - Otherwise, changes the global directory.
+ Changes the current working directory to {dir}. The scope of
+ the change is determined as follows:
+ If {scope} is not present, the current working directory is
+ changed to the scope of the current directory:
+ - If the window local directory (|:lcd|) is set, it
+ changes the current working directory for that scope.
+ - Otherwise, if the tab page local directory (|:tcd|) is
+ set, it changes the current directory for that scope.
+ - Otherwise, changes the global directory for that scope.
+
+ If {scope} is present, changes the current working directory
+ for the specified scope:
+ "window" Changes the window local directory. |:lcd|
+ "tabpage" Changes the tab page local directory. |:tcd|
+ "global" Changes the global directory. |:cd|
+
{dir} must be a String.
If successful, returns the previous working directory. Pass
this to another chdir() to restore the directory.
@@ -1217,9 +1224,9 @@ M.funcs = {
]=],
name = 'chdir',
- params = { { 'dir', 'string' } },
+ params = { { 'dir', 'string' }, { 'scope', 'string' } },
returns = 'string',
- signature = 'chdir({dir})',
+ signature = 'chdir({dir} [, {scope}])',
},
cindent = {
args = 1,
diff --git a/src/nvim/eval/fs.c b/src/nvim/eval/fs.c
@@ -74,7 +74,19 @@ void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(cwd);
CdScope scope = kCdScopeGlobal;
- if (curwin->w_localdir != NULL) {
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ const char *s = tv_get_string(&argvars[1]);
+ if (strcmp(s, "global") == 0) {
+ scope = kCdScopeGlobal;
+ } else if (strcmp(s, "tabpage") == 0) {
+ scope = kCdScopeTabpage;
+ } else if (strcmp(s, "window") == 0) {
+ scope = kCdScopeWindow;
+ } else {
+ semsg(_(e_invargNval), "scope", s);
+ return;
+ }
+ } else if (curwin->w_localdir != NULL) {
scope = kCdScopeWindow;
} else if (curtab->tp_localdir != NULL) {
scope = kCdScopeTabpage;
diff --git a/test/old/testdir/test_cd.vim b/test/old/testdir/test_cd.vim
@@ -99,10 +99,20 @@ func Test_chdir_func()
call assert_equal('y', fnamemodify(getcwd(3, 2), ':t'))
call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
+ " Forcing scope
+ call chdir('.', 'global')
+ call assert_match('^\[global\]', trim(execute('verbose pwd')))
+ call chdir('.', 'tabpage')
+ call assert_match('^\[tabpage\]', trim(execute('verbose pwd')))
+ call chdir('.', 'window')
+ call assert_match('^\[window\]', trim(execute('verbose pwd')))
+
" Error case
call assert_fails("call chdir('dir-abcd')", 'E344:')
silent! let d = chdir("dir_abcd")
call assert_equal("", d)
+ call assert_fails("call chdir('.', v:_null_string)", 'E475:')
+ call assert_fails("call chdir('.', [])", 'E730:')
" Should not crash
call chdir(d)
call assert_equal('', chdir([]))