mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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)
1063f3d200
Also includes some documentation changes from patch 8.1.1218.
This commit is contained in:
parent
57651df9c1
commit
8727d38012
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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},
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -7753,51 +7753,68 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
|
||||
}
|
||||
}
|
||||
|
||||
/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
|
||||
/// 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 *tofree;
|
||||
bool retval = false;
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
char_u *tofree;
|
||||
|
||||
new_dir = eap->arg;
|
||||
#if !defined(UNIX)
|
||||
#if !defined(UNIX) && !defined(VMS)
|
||||
// for non-UNIX ":cd" means: print current directory
|
||||
if (*new_dir == NUL) {
|
||||
ex_pwd(NULL);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (allbuf_locked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ":cd -": Change to previous directory
|
||||
if (STRCMP(new_dir, "-") == 0) {
|
||||
if (prev_dir == NULL) {
|
||||
EMSG(_("E186: No previous directory"));
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
#endif
|
||||
CdScope scope = kCdScopeGlobal; // Depends on command invoked
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user