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* *:chdir*
|
||||||
:chd[ir][!] [path] Same as |:cd|.
|
:chd[ir][!] [path] Same as |:cd|.
|
||||||
|
|
||||||
*:tc* *:tcd* *E5000* *E5001* *E5002*
|
*:tc* *:tcd*
|
||||||
:tc[d][!] {path} Like |:cd|, but set the current directory for the
|
:tc[d][!] {path} Like |:cd|, but only set the directory for the current
|
||||||
current tab and window. The current directory for
|
tab. The current window will also use this directory.
|
||||||
other tabs and windows is not changed.
|
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-*
|
*:tcd-*
|
||||||
:tc[d][!] - Change to the previous current directory (before the
|
: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.
|
:pw[d] Print the current directory name.
|
||||||
Also see |getcwd()|.
|
Also see |getcwd()|.
|
||||||
|
|
||||||
So long as no |:tcd| or |:lcd| command has been used, all windows share the
|
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
|
same current directory. Using a command to jump to another window doesn't
|
||||||
change anything for the current directory.
|
change anything for the current directory.
|
||||||
|
|
||||||
When |:lcd| has been used for a window, the specified directory becomes the
|
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}
|
char2nr({expr}[, {utf8}]) Number ASCII/UTF-8 value of first char in {expr}
|
||||||
charidx({string}, {idx} [, {countcc}])
|
charidx({string}, {idx} [, {countcc}])
|
||||||
Number char index of byte {idx} in {string}
|
Number char index of byte {idx} in {string}
|
||||||
|
chdir({dir}) String change current working directory
|
||||||
cindent({lnum}) Number C indent for line {lnum}
|
cindent({lnum}) Number C indent for line {lnum}
|
||||||
clearmatches([{win}]) none clear all matches
|
clearmatches([{win}]) none clear all matches
|
||||||
col({expr}) Number column nr of cursor or mark
|
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}
|
has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key}
|
||||||
haslocaldir([{winnr} [, {tabnr}]])
|
haslocaldir([{winnr} [, {tabnr}]])
|
||||||
Number |TRUE| if current window executed |:lcd|
|
Number |TRUE| if current window executed |:lcd|
|
||||||
|
or |:tcd|
|
||||||
hasmapto({what} [, {mode} [, {abbr}]])
|
hasmapto({what} [, {mode} [, {abbr}]])
|
||||||
Number |TRUE| if mapping to {what} exists
|
Number |TRUE| if mapping to {what} exists
|
||||||
histadd({history}, {item}) String add an item to a history
|
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́ć', 6, 1) returns 4
|
||||||
echo charidx('áb́ć', 16) returns -1
|
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()*
|
cindent({lnum}) *cindent()*
|
||||||
Get the amount of indent for line {lnum} according the C
|
Get the amount of indent for line {lnum} according the C
|
||||||
indenting rules, as with 'cindent'.
|
indenting rules, as with 'cindent'.
|
||||||
@ -4987,6 +5010,8 @@ getcwd([{winnr}[, {tabnr}]]) *getcwd()*
|
|||||||
getcwd(0, 0)
|
getcwd(0, 0)
|
||||||
< If {winnr} is -1 it is ignored, only the tab is resolved.
|
< If {winnr} is -1 it is ignored, only the tab is resolved.
|
||||||
{winnr} can be the window number or the |window-ID|.
|
{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|: >
|
Can also be used as a |method|: >
|
||||||
GetWinnr()->getcwd()
|
GetWinnr()->getcwd()
|
||||||
|
@ -202,14 +202,28 @@ the other window. This is called a local directory. >
|
|||||||
:pwd
|
:pwd
|
||||||
/home/Bram/VeryLongFileName
|
/home/Bram/VeryLongFileName
|
||||||
|
|
||||||
So long as no ":lcd" command has been used, all windows share the same 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. Doing a `:cd` command in one window will also change the current
|
||||||
directory of the other window.
|
directory of the other window.
|
||||||
For a window where ":lcd" has been used a different current directory is
|
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.
|
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
|
When using a `:cd` command in a window that uses a different current
|
||||||
directory, it will go back to using the shared directory.
|
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
|
*22.3* Finding a file
|
||||||
|
|
||||||
|
@ -785,9 +785,10 @@ System functions and manipulation of files:
|
|||||||
isdirectory() check if a directory exists
|
isdirectory() check if a directory exists
|
||||||
getfsize() get the size of a file
|
getfsize() get the size of a file
|
||||||
getcwd() get the current working directory
|
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
|
tempname() get the name of a temporary file
|
||||||
mkdir() create a new directory
|
mkdir() create a new directory
|
||||||
|
chdir() change current working directory
|
||||||
delete() delete a file
|
delete() delete a file
|
||||||
rename() rename a file
|
rename() rename a file
|
||||||
system() get the result of a shell command as a string
|
system() get the result of a shell command as a string
|
||||||
|
@ -197,6 +197,10 @@ Functions:
|
|||||||
|stdpath()|
|
|stdpath()|
|
||||||
|system()|, |systemlist()| can run {cmd} directly (without 'shell')
|
|system()|, |systemlist()| can run {cmd} directly (without 'shell')
|
||||||
|matchadd()| can be called before highlight group is defined
|
|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 groups:
|
||||||
|highlight-blend| controls blend level for a highlight group
|
|highlight-blend| controls blend level for a highlight group
|
||||||
|
@ -72,6 +72,7 @@ return {
|
|||||||
chansend={args=2},
|
chansend={args=2},
|
||||||
char2nr={args={1, 2}, base=1},
|
char2nr={args={1, 2}, base=1},
|
||||||
charidx={args={2, 3}},
|
charidx={args={2, 3}},
|
||||||
|
chdir={args=1, base=1},
|
||||||
cindent={args=1, base=1},
|
cindent={args=1, base=1},
|
||||||
clearmatches={args={0, 1}, base=1},
|
clearmatches={args={0, 1}, base=1},
|
||||||
col={args=1, base=1},
|
col={args=1, base=1},
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "nvim/file_search.h"
|
#include "nvim/file_search.h"
|
||||||
#include "nvim/fileio.h"
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/fold.h"
|
#include "nvim/fold.h"
|
||||||
|
#include "nvim/globals.h"
|
||||||
#include "nvim/if_cscope.h"
|
#include "nvim/if_cscope.h"
|
||||||
#include "nvim/indent.h"
|
#include "nvim/indent.h"
|
||||||
#include "nvim/indent_c.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;
|
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
|
* "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)
|
void ex_cd(exarg_T *eap)
|
||||||
{
|
{
|
||||||
char_u *new_dir;
|
char_u *new_dir;
|
||||||
char_u *tofree;
|
|
||||||
|
|
||||||
new_dir = eap->arg;
|
new_dir = eap->arg;
|
||||||
#if !defined(UNIX)
|
#if !defined(UNIX) && !defined(VMS)
|
||||||
// for non-UNIX ":cd" means: print current directory
|
// for non-UNIX ":cd" means: print current directory
|
||||||
if (*new_dir == NUL) {
|
if (*new_dir == NUL) {
|
||||||
ex_pwd(NULL);
|
ex_pwd(NULL);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (allbuf_locked()) {
|
CdScope scope = kCdScopeGlobal;
|
||||||
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
|
|
||||||
|
|
||||||
switch (eap->cmdidx) {
|
switch (eap->cmdidx) {
|
||||||
case CMD_tcd:
|
case CMD_tcd:
|
||||||
case CMD_tchdir:
|
case CMD_tchdir:
|
||||||
@ -7810,19 +7827,12 @@ void ex_cd(exarg_T *eap)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (changedir_func(new_dir, scope)) {
|
||||||
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);
|
|
||||||
// Echo the new current directory if the command was typed.
|
// Echo the new current directory if the command was typed.
|
||||||
if (KeyTyped || p_verbose >= 5) {
|
if (KeyTyped || p_verbose >= 5) {
|
||||||
ex_pwd(eap);
|
ex_pwd(eap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xfree(tofree);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,47 @@ func Test_cd_with_cpo_chdir()
|
|||||||
bw!
|
bw!
|
||||||
endfunc
|
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()
|
func Test_cd_from_non_existing_dir()
|
||||||
CheckNotMSWindows
|
CheckNotMSWindows
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user