diff options
author | zeertzjq <zeertzjq@outlook.com> | 2021-10-17 22:04:53 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2021-10-17 22:04:53 +0800 |
commit | 8727d38012f3c4c54380d554e2a8c53e8c50ad0d (patch) | |
tree | d060f78a874381faf3ddd6979586809cb9ead73e | |
parent | 57651df9c11740a24a2f71801d9b7b81b894d601 (diff) | |
download | rneovim-8727d38012f3c4c54380d554e2a8c53e8c50ad0d.tar.gz rneovim-8727d38012f3c4c54380d554e2a8c53e8c50ad0d.tar.bz2 rneovim-8727d38012f3c4c54380d554e2a8c53e8c50ad0d.zip |
vim-patch:8.1.1291: not easy to change directory and restore
Problem: Not easy to change directory and restore.
Solution: Add the chdir() function. (Yegappan Lakshmanan, closes vim/vim#4358)
https://github.com/vim/vim/commit/1063f3d2008f22d02ccfa9dab83a23db52febbdc
Also includes some documentation changes from patch 8.1.1218.
-rw-r--r-- | runtime/doc/editing.txt | 14 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 25 | ||||
-rw-r--r-- | runtime/doc/usr_22.txt | 24 | ||||
-rw-r--r-- | runtime/doc/usr_41.txt | 3 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 4 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 38 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 96 | ||||
-rw-r--r-- | src/nvim/testdir/test_cd.vim | 41 |
9 files changed, 191 insertions, 55 deletions
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index f91e4f0627..1eba702a06 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1275,10 +1275,12 @@ exist, the next-higher scope in the hierarchy applies. *:chd* *:chdir* :chd[ir][!] [path] Same as |:cd|. - *:tc* *:tcd* *E5000* *E5001* *E5002* -:tc[d][!] {path} Like |:cd|, but set the current directory for the - current tab and window. The current directory for - other tabs and windows is not changed. + *:tc* *:tcd* +:tc[d][!] {path} Like |:cd|, but only set the directory for the current + tab. The current window will also use this directory. + The current directory is not changed for windows in + other tabs and for windows in the current tab that + have their own window-local directory. *:tcd-* :tc[d][!] - Change to the previous current directory (before the @@ -1303,8 +1305,8 @@ exist, the next-higher scope in the hierarchy applies. :pw[d] Print the current directory name. Also see |getcwd()|. -So long as no |:tcd| or |:lcd| command has been used, all windows share the -same "current directory". Using a command to jump to another window doesn't +So long as no |:lcd| or |:tcd| command has been used, all windows share the +same current directory. Using a command to jump to another window doesn't change anything for the current directory. When |:lcd| has been used for a window, the specified directory becomes the diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e956ccaa77..4467afaa16 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2316,6 +2316,7 @@ chansend({id}, {data}) Number Writes {data} to channel char2nr({expr}[, {utf8}]) Number ASCII/UTF-8 value of first char in {expr} charidx({string}, {idx} [, {countcc}]) Number char index of byte {idx} in {string} +chdir({dir}) String change current working directory cindent({lnum}) Number C indent for line {lnum} clearmatches([{win}]) none clear all matches col({expr}) Number column nr of cursor or mark @@ -2461,6 +2462,7 @@ has({feature}) Number |TRUE| if feature {feature} supported has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key} haslocaldir([{winnr} [, {tabnr}]]) Number |TRUE| if current window executed |:lcd| + or |:tcd| hasmapto({what} [, {mode} [, {abbr}]]) Number |TRUE| if mapping to {what} exists histadd({history}, {item}) String add an item to a history @@ -3280,6 +3282,27 @@ charidx({string}, {idx} [, {countcc}]) echo charidx('áb́ć', 6, 1) returns 4 echo charidx('áb́ć', 16) returns -1 +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. + If successful, returns the previous working directory. Pass + this to another chdir() to restore the directory. + On failure, returns an empty string. + + Example: > + let save_dir = chdir(newdir) + if save_dir + " ... do some work + call chdir(save_dir) + endif +< cindent({lnum}) *cindent()* Get the amount of indent for line {lnum} according the C indenting rules, as with 'cindent'. @@ -4987,6 +5010,8 @@ getcwd([{winnr}[, {tabnr}]]) *getcwd()* getcwd(0, 0) < If {winnr} is -1 it is ignored, only the tab is resolved. {winnr} can be the window number or the |window-ID|. + If both {winnr} and {tabnr} are -1 the global working + directory is returned. Can also be used as a |method|: > GetWinnr()->getcwd() diff --git a/runtime/doc/usr_22.txt b/runtime/doc/usr_22.txt index 56fe5ada2b..f53d578456 100644 --- a/runtime/doc/usr_22.txt +++ b/runtime/doc/usr_22.txt @@ -202,14 +202,28 @@ the other window. This is called a local directory. > :pwd /home/Bram/VeryLongFileName -So long as no ":lcd" command has been used, all windows share the same current -directory. Doing a ":cd" command in one window will also change the current +So long as no `:lcd` command has been used, all windows share the same current +directory. Doing a `:cd` command in one window will also change the current directory of the other window. - For a window where ":lcd" has been used a different current directory is -remembered. Using ":cd" or ":lcd" in other windows will not change it. - When using a ":cd" command in a window that uses a different current + For a window where `:lcd` has been used a different current directory is +remembered. Using `:cd` or `:lcd` in other windows will not change it. + When using a `:cd` command in a window that uses a different current directory, it will go back to using the shared directory. + +TAB LOCAL DIRECTORY + +When you open a new tab page, it uses the directory of the window in the +previous tab page from which the new tab page was opened. You can change the +directory of the current tab page using the `:tcd` command. All the windows in +a tab page share this directory except for windows with a window-local +directory. Any new windows opened in this tab page will use this directory as +the current working directory. Using a `:cd` command in a tab page will not +change the working directory of tab pages which have a tab local directory. +When the global working directory is changed using the ":cd" command in a tab +page, it will also change the current tab page working directory. + + ============================================================================== *22.3* Finding a file diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index c575dd9fd8..6a9284dac9 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -785,9 +785,10 @@ System functions and manipulation of files: isdirectory() check if a directory exists getfsize() get the size of a file getcwd() get the current working directory - haslocaldir() check if current window used |:lcd| + haslocaldir() check if current window used |:lcd| or |:tcd| tempname() get the name of a temporary file mkdir() create a new directory + chdir() change current working directory delete() delete a file rename() rename a file system() get the result of a shell command as a string diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index c85cae4004..7e7bb7d62f 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -197,6 +197,10 @@ Functions: |stdpath()| |system()|, |systemlist()| can run {cmd} directly (without 'shell') |matchadd()| can be called before highlight group is defined + |getcwd()| and |haslocaldir()| may throw errors. *E5000* *E5001* *E5002* + |haslocaldir()|'s only possible return values are 0 and 1, it never returns 2. + `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global + working directory. Use `getcwd(-1, -1)` to get the global working directory. Highlight groups: |highlight-blend| controls blend level for a highlight group diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 70aa2bb1f8..dfc51d80af 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -72,6 +72,7 @@ return { chansend={args=2}, char2nr={args={1, 2}, base=1}, charidx={args={2, 3}}, + chdir={args=1, base=1}, cindent={args=1, base=1}, clearmatches={args={0, 1}, base=1}, col={args=1, base=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index da129c1170..fd3353e18b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -28,6 +28,7 @@ #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/globals.h" #include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/indent_c.h" @@ -1062,6 +1063,43 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = len > 0 ? len - 1 : 0; } +// "chdir(dir)" function +static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char_u *cwd; + CdScope scope = kCdScopeGlobal; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (argvars[0].v_type != VAR_STRING) { + return; + } + + // Return the current directory + cwd = xmalloc(MAXPATHL); + if (cwd != NULL) { + if (os_dirname(cwd, MAXPATHL) != FAIL) { +#ifdef BACKSLASH_IN_FILENAME + slash_adjust(cwd); +#endif + rettv->vval.v_string = vim_strsave(cwd); + } + xfree(cwd); + } + + if (curwin->w_localdir != NULL) { + scope = kCdScopeWindow; + } else if (curtab->tp_localdir != NULL) { + scope = kCdScopeTab; + } + + if (!changedir_func(argvars[0].vval.v_string, scope)) { + // Directory change failed + XFREE_CLEAR(rettv->vval.v_string); + } +} + /* * "cindent(lnum)" function */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index cc30557ead..9191c2590c 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7753,51 +7753,68 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) } } -/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`. -void ex_cd(exarg_T *eap) +/// Change directory function used by :cd/:tcd/:lcd Ex commands and the chdir() function. +/// @return true if the directory is successfully changed. +bool changedir_func(char_u *new_dir, CdScope scope) { - char_u *new_dir; char_u *tofree; + bool retval = false; - new_dir = eap->arg; -#if !defined(UNIX) - // for non-UNIX ":cd" means: print current directory - if (*new_dir == NUL) { - ex_pwd(NULL); - } else -#endif - { - if (allbuf_locked()) { - return; - } + if (allbuf_locked()) { + return false; + } - // ":cd -": Change to previous directory - if (STRCMP(new_dir, "-") == 0) { - if (prev_dir == NULL) { - EMSG(_("E186: No previous directory")); - return; - } - new_dir = prev_dir; + // ":cd -": Change to previous directory + if (STRCMP(new_dir, "-") == 0) { + if (prev_dir == NULL) { + EMSG(_("E186: No previous directory")); + return false; } + new_dir = prev_dir; + } - // Save current directory for next ":cd -" - tofree = prev_dir; - if (os_dirname(NameBuff, MAXPATHL) == OK) { - prev_dir = vim_strsave(NameBuff); - } else { - prev_dir = NULL; - } + // Save current directory for next ":cd -" + tofree = prev_dir; + if (os_dirname(NameBuff, MAXPATHL) == OK) { + prev_dir = vim_strsave(NameBuff); + } else { + prev_dir = NULL; + } #if defined(UNIX) - // On Unix ":cd" means: go to home directory. - if (*new_dir == NUL) { - // Use NameBuff for home directory name. - expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); - new_dir = NameBuff; - } + // On Unix ":cd" means: go to home directory. + if (*new_dir == NUL) { + // Use NameBuff for home directory name. + expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); + new_dir = NameBuff; + } #endif - CdScope scope = kCdScopeGlobal; // Depends on command invoked + bool dir_differs = prev_dir == NULL || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0; + if (dir_differs && vim_chdir(new_dir)) { + EMSG(_(e_failed)); + } else { + post_chdir(scope, dir_differs); + retval = true; + } + xfree(tofree); + + return retval; +} + +/// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir". +void ex_cd(exarg_T *eap) +{ + char_u *new_dir; + new_dir = eap->arg; +#if !defined(UNIX) && !defined(VMS) + // for non-UNIX ":cd" means: print current directory + if (*new_dir == NUL) { + ex_pwd(NULL); + } else +#endif + { + CdScope scope = kCdScopeGlobal; switch (eap->cmdidx) { case CMD_tcd: case CMD_tchdir: @@ -7810,19 +7827,12 @@ void ex_cd(exarg_T *eap) default: break; } - - bool dir_differs = prev_dir == NULL || pathcmp((char *)prev_dir, (char *)new_dir, -1) != 0; - if (dir_differs && vim_chdir(new_dir)) { - EMSG(_(e_failed)); - } else { - post_chdir(scope, dir_differs); + if (changedir_func(new_dir, scope)) { // Echo the new current directory if the command was typed. if (KeyTyped || p_verbose >= 5) { ex_pwd(eap); } } - - xfree(tofree); } } diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index 02a23bf82f..7500c35563 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -69,6 +69,47 @@ func Test_cd_with_cpo_chdir() bw! endfunc +" Test for chdir() +func Test_chdir_func() + let topdir = getcwd() + call mkdir('Xdir/y/z', 'p') + + " Create a few tabpages and windows with different directories + new + cd Xdir + tabnew + tcd y + below new + below new + lcd z + + tabfirst + call chdir('..') + call assert_equal('y', fnamemodify(getcwd(1, 2), ':t')) + call assert_equal('z', fnamemodify(getcwd(3, 2), ':t')) + tabnext | wincmd t + call chdir('..') + call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t')) + call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t')) + call assert_equal('z', fnamemodify(getcwd(3, 2), ':t')) + call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t')) + 3wincmd w + call chdir('..') + call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t')) + call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t')) + call assert_equal('y', fnamemodify(getcwd(3, 2), ':t')) + call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t')) + + " Error case + call assert_fails("call chdir('dir-abcd')", 'E472:') + silent! let d = chdir("dir_abcd") + call assert_equal("", d) + + only | tabonly + exe 'cd ' . topdir + call delete('Xdir', 'rf') +endfunc + func Test_cd_from_non_existing_dir() CheckNotMSWindows |