From 798cb0f19a3b1bbd9003418f8d5645b7cb3141ec Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 8 Aug 2025 21:50:41 +0800 Subject: [PATCH] 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 --- runtime/doc/news.txt | 1 + runtime/doc/vimfn.txt | 28 ++++++++++++++++++---------- runtime/lua/vim/_meta/vimfn.lua | 28 ++++++++++++++++++---------- src/nvim/eval.lua | 31 +++++++++++++++++++------------ src/nvim/eval/fs.c | 14 +++++++++++++- test/old/testdir/test_cd.vim | 10 ++++++++++ 6 files changed, 79 insertions(+), 33 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 7393f8ef04..9270726a0b 100644 --- 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 index d9c04e0de3..a2aab9f210 100644 --- 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 index 6c57179ca4..1009b67001 100644 --- 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 index e00bafc92d..455bdd25ed 100644 --- 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 index f5b33c804e..4cd3216f59 100644 --- 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 index 8a7baad45e..25e95ac4f6 100644 --- 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([]))