mirror of
https://github.com/neovim/neovim.git
synced 2026-08-14 11:29:31 +00:00
feat(editor): :bcd changes buffer-local directory
Problem: No way to set a buffer-local directory. Use-cases: - "Root dir" for LSP (and the "project" concept). - `:terminal` OSC 7 Solution: Add `:bcd` command. - Extend `getcwd()` to take a third arg; `getcwd(-1, -1, bunfr)` returns the buffer-local working directory. - Buffer-local directories have less priority than window-local ones, and more priority than tab-local ones. Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
@@ -615,10 +615,11 @@ DiffUpdated After diffs have been updated. Depending on
|
||||
*DirChanged*
|
||||
DirChanged After the |current-directory| was changed.
|
||||
The pattern can be:
|
||||
"window" to trigger on `:lcd`
|
||||
"tabpage" to trigger on `:tcd`
|
||||
"global" to trigger on `:cd`
|
||||
"auto" to trigger on 'autochdir'.
|
||||
"buffer" to trigger on `:bcd`
|
||||
"global" to trigger on `:cd`
|
||||
"tabpage" to trigger on `:tcd`
|
||||
"window" to trigger on `:lcd`
|
||||
Sets these |v:event| keys:
|
||||
cwd: current working directory
|
||||
scope: "global", "tabpage", "window"
|
||||
@@ -633,7 +634,7 @@ DirChangedPre When the |current-directory| is going to be
|
||||
The pattern is like with |DirChanged|.
|
||||
Sets these |v:event| keys:
|
||||
directory: new working directory
|
||||
scope: "global", "tabpage", "window"
|
||||
scope: "global", "tabpage", "window", "buffer"
|
||||
changed_window: v:true if we fired the event
|
||||
switching window (or tab)
|
||||
<afile> is set to the new directory name.
|
||||
|
||||
@@ -1387,10 +1387,11 @@ scope in the hierarchy applies.
|
||||
|
||||
*: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.
|
||||
tab. The current buffer and 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, or for
|
||||
buffers that have their own buffer-local directory.
|
||||
|
||||
*:tcd-*
|
||||
:tc[d][!] - Change to the previous current directory (before the
|
||||
@@ -1399,10 +1400,15 @@ scope in the hierarchy applies.
|
||||
*:tch* *:tchdir*
|
||||
:tch[dir][!] Same as |:tcd|.
|
||||
|
||||
*:bc* *:bcd*
|
||||
:bc[d][!] {path} Like |:cd|, but only set the current directory for the
|
||||
current buffer. The current directory for other
|
||||
windows or buffers or tabs is not changed.
|
||||
|
||||
*:lc* *:lcd*
|
||||
:lc[d][!] {path} Like |:cd|, but only set the current directory for the
|
||||
current window. The current directory for other
|
||||
windows or tabs is not changed.
|
||||
windows, tabs, or buffers is not changed.
|
||||
|
||||
*:lch* *:lchdir*
|
||||
:lch[dir][!] Same as |:lcd|.
|
||||
|
||||
@@ -247,6 +247,8 @@ DIAGNOSTICS
|
||||
|
||||
EDITOR
|
||||
|
||||
• |:bcd| sets a buffer-local directory. |getcwd()| and |haslocaldir()| take
|
||||
`bufnr` as a third parameter.
|
||||
• |:command-completion-customlist| can return a list of dictionaries with
|
||||
kind/menu/info/abbr for the popup menu.
|
||||
• |cmdwin| (|q:|, |q/|, |q?|, |c_CTRL-F|) is implemented as a "normal"
|
||||
|
||||
@@ -695,8 +695,8 @@ Vimscript compatibility:
|
||||
Working directory (Vim implemented some of these after Nvim):
|
||||
- |DirChanged| and |DirChangedPre| can be triggered when switching to another
|
||||
window or tab.
|
||||
- |getcwd()| and |haslocaldir()| may throw errors if the tabpage or window
|
||||
cannot be found. *E5000* *E5001* *E5002*
|
||||
- |getcwd()| and |haslocaldir()| may throw errors if the tabpage, window, or
|
||||
buffer cannot be found. *E5000* *E5001* *E5002* *E5006* *E5007*
|
||||
- |haslocaldir()| checks for tab-local directory if and only if -1 is passed as
|
||||
window number, and its only possible returns values are 0 and 1.
|
||||
- `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global
|
||||
|
||||
@@ -1117,6 +1117,8 @@ chdir({dir} [, {scope}]) *chdir()*
|
||||
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.
|
||||
- If the buffer local directory (|:bcd|) is set, it
|
||||
changes the current working directory for that scope.
|
||||
- Otherwise, if the tabpage local directory (|:tcd|) is
|
||||
set, it changes the current directory for that scope.
|
||||
- Otherwise, changes the global directory for that scope.
|
||||
@@ -1124,6 +1126,7 @@ chdir({dir} [, {scope}]) *chdir()*
|
||||
If {scope} is present, changes the current working directory
|
||||
for the specified scope:
|
||||
"window" Changes the window local directory. |:lcd|
|
||||
"buffer" Changes the buffer local directory. |:bcd|
|
||||
"tabpage" Changes the tabpage local directory. |:tcd|
|
||||
"global" Changes the global directory. |:cd|
|
||||
|
||||
@@ -3745,31 +3748,46 @@ getcursorcharpos([{winid}]) *getcursorcharpos()*
|
||||
Return: ~
|
||||
(`any`)
|
||||
|
||||
getcwd([{winnr} [, {tabnr}]]) *getcwd()*
|
||||
getcwd([{winnr} [, {tabnr} [, {bufnr}]]]) *getcwd()*
|
||||
Lua: Prefer |uv.cwd()| for the global working directory;
|
||||
tab-local and window-local scopes differ.
|
||||
|
||||
With no arguments, returns the name of the effective
|
||||
|current-directory|. With {winnr} or {tabnr} the working
|
||||
directory of that scope is returned, and 'autochdir' is
|
||||
ignored. Tabs and windows are identified by their respective
|
||||
numbers, 0 means current tab or window. Missing tab number
|
||||
|current-directory|. With {winnr} or {tabnr} or {bufnr} the
|
||||
working directory of that scope is returned, and 'autochdir'
|
||||
is ignored.
|
||||
|
||||
Tabs, windows and buffers are identified by their respective
|
||||
numbers, 0 means current tab or window or buffer. Missing tab
|
||||
number
|
||||
implies 0. Thus the following are equivalent: >vim
|
||||
getcwd(0)
|
||||
getcwd(0, 0)
|
||||
< If {winnr} is -1 it is ignored, only the tab is resolved.
|
||||
{winnr} is a |window-number| or |window-ID|.
|
||||
If both {winnr} and {tabnr} are -1 the global working
|
||||
directory is returned.
|
||||
If both {winnr} and {tabnr} are -1 and {bufnr} is missing the
|
||||
global working directory is returned.
|
||||
Note: When {tabnr} is -1 Vim returns an empty string to
|
||||
signal that it is invalid, whereas Nvim returns either the
|
||||
global working directory if {winnr} is -1 or the working
|
||||
directory of the window indicated by {winnr}.
|
||||
Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
|
||||
|
||||
If {bufnr} is provided, {winnr} and {tabnr} must be -1 and the
|
||||
working directory of that buffer is returned. If {bufnr} is
|
||||
-1, it is ignored, and the global working directory is
|
||||
returned.
|
||||
Examples of buffer usage: >vim
|
||||
getcwd(-1, -1, 0) " Get current buffer's directory
|
||||
getcwd(-1, -1, 3) " Get directory of buffer #3
|
||||
getcwd(-1, -1, -1) " Get global directory
|
||||
getcwd(-1, -1) " Get global directory
|
||||
< Throw error if the arguments are invalid.
|
||||
|E5000| |E5001| |E5002| |E5006| |E5007|
|
||||
|
||||
Parameters: ~
|
||||
• {winnr} (`integer?`)
|
||||
• {tabnr} (`integer?`)
|
||||
• {bufnr} (`integer?`)
|
||||
|
||||
Return: ~
|
||||
(`string`)
|
||||
@@ -4994,15 +5012,16 @@ has_key({dict}, {key}) *has_key()*
|
||||
Return: ~
|
||||
(`0|1`)
|
||||
|
||||
haslocaldir([{winnr} [, {tabnr}]]) *haslocaldir()*
|
||||
Checks whether the window or tabpage has set a local working
|
||||
directory. Returns 1 when the window has set a local path
|
||||
via |:lcd| or when {winnr} is -1 and the tabpage has set a
|
||||
local path via |:tcd|, otherwise 0.
|
||||
haslocaldir([{winnr} [, {tabnr} [, {bufnr} ]]]) *haslocaldir()*
|
||||
Checks whether the window, tabpage or buffer has set a local
|
||||
working directory. Returns 1 when the window has set a local
|
||||
path via |:lcd|, or when {winnr} is -1 and the tabpage has set
|
||||
a local path via |:tcd|, or when {winnr} and {tabnr} are -1
|
||||
and {bufnr} has set a local path via |:bcd|, otherwise 0.
|
||||
|
||||
Tabs and windows are identified by their respective numbers,
|
||||
0 means current tab or window. Missing argument implies 0.
|
||||
Thus the following are equivalent: >vim
|
||||
Tabs, windows and buffers are identified by their respective
|
||||
numbers, 0 means current tab or window. Missing argument
|
||||
implies 0. Thus the following are equivalent: >vim
|
||||
echo haslocaldir()
|
||||
echo haslocaldir(0)
|
||||
echo haslocaldir(0, 0)
|
||||
@@ -5010,11 +5029,18 @@ haslocaldir([{winnr} [, {tabnr}]]) *haslocaldir()*
|
||||
With {winnr} and {tabnr} use the window in that tabpage.
|
||||
{winnr} is a |window-number| or |window-ID|.
|
||||
If {winnr} is -1 it is ignored, only the tab is resolved.
|
||||
Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
|
||||
If {bufnr} is provided, {winnr} and {tabnr} must be -1 and
|
||||
only the buffer is resolved.
|
||||
Examples of buffer usage: >vim
|
||||
haslocaldir(-1, -1, 0) " Current buf has a local directory?
|
||||
haslocaldir(-1, -1, 3) " Buf #3 has a local directory?
|
||||
< Throw error if the arguments are invalid.
|
||||
|E5000| |E5001| |E5002| |E5006| |E5007|
|
||||
|
||||
Parameters: ~
|
||||
• {winnr} (`integer?`)
|
||||
• {tabnr} (`integer?`)
|
||||
• {bufnr} (`integer?`)
|
||||
|
||||
Return: ~
|
||||
(`0|1`)
|
||||
|
||||
60
runtime/lua/vim/_meta/vimfn.gen.lua
generated
60
runtime/lua/vim/_meta/vimfn.gen.lua
generated
@@ -952,6 +952,8 @@ function vim.fn.charidx(string, idx, countcc, utf16) end
|
||||
--- 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.
|
||||
--- - If the buffer local directory (|:bcd|) is set, it
|
||||
--- changes the current working directory for that scope.
|
||||
--- - Otherwise, if the tabpage local directory (|:tcd|) is
|
||||
--- set, it changes the current directory for that scope.
|
||||
--- - Otherwise, changes the global directory for that scope.
|
||||
@@ -959,6 +961,7 @@ function vim.fn.charidx(string, idx, countcc, utf16) end
|
||||
--- If {scope} is present, changes the current working directory
|
||||
--- for the specified scope:
|
||||
--- "window" Changes the window local directory. |:lcd|
|
||||
--- "buffer" Changes the buffer local directory. |:bcd|
|
||||
--- "tabpage" Changes the tabpage local directory. |:tcd|
|
||||
--- "global" Changes the global directory. |:cd|
|
||||
---
|
||||
@@ -3318,27 +3321,42 @@ function vim.fn.getcursorcharpos(winid) end
|
||||
--- Lua: Prefer |uv.cwd()| for the global working directory; tab-local and window-local scopes differ.
|
||||
---
|
||||
--- With no arguments, returns the name of the effective
|
||||
--- |current-directory|. With {winnr} or {tabnr} the working
|
||||
--- directory of that scope is returned, and 'autochdir' is
|
||||
--- ignored. Tabs and windows are identified by their respective
|
||||
--- numbers, 0 means current tab or window. Missing tab number
|
||||
--- |current-directory|. With {winnr} or {tabnr} or {bufnr} the
|
||||
--- working directory of that scope is returned, and 'autochdir'
|
||||
--- is ignored.
|
||||
---
|
||||
--- Tabs, windows and buffers are identified by their respective
|
||||
--- numbers, 0 means current tab or window or buffer. Missing tab
|
||||
--- number
|
||||
--- implies 0. Thus the following are equivalent: >vim
|
||||
--- getcwd(0)
|
||||
--- getcwd(0, 0)
|
||||
--- <If {winnr} is -1 it is ignored, only the tab is resolved.
|
||||
--- {winnr} is a |window-number| or |window-ID|.
|
||||
--- If both {winnr} and {tabnr} are -1 the global working
|
||||
--- directory is returned.
|
||||
--- If both {winnr} and {tabnr} are -1 and {bufnr} is missing the
|
||||
--- global working directory is returned.
|
||||
--- Note: When {tabnr} is -1 Vim returns an empty string to
|
||||
--- signal that it is invalid, whereas Nvim returns either the
|
||||
--- global working directory if {winnr} is -1 or the working
|
||||
--- directory of the window indicated by {winnr}.
|
||||
--- Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
|
||||
---
|
||||
--- If {bufnr} is provided, {winnr} and {tabnr} must be -1 and the
|
||||
--- working directory of that buffer is returned. If {bufnr} is
|
||||
--- -1, it is ignored, and the global working directory is
|
||||
--- returned.
|
||||
--- Examples of buffer usage: >vim
|
||||
--- getcwd(-1, -1, 0) " Get current buffer's directory
|
||||
--- getcwd(-1, -1, 3) " Get directory of buffer #3
|
||||
--- getcwd(-1, -1, -1) " Get global directory
|
||||
--- getcwd(-1, -1) " Get global directory
|
||||
--- <Throw error if the arguments are invalid.
|
||||
--- |E5000| |E5001| |E5002| |E5006| |E5007|
|
||||
---
|
||||
--- @param winnr? integer
|
||||
--- @param tabnr? integer
|
||||
--- @param bufnr? integer
|
||||
--- @return string
|
||||
function vim.fn.getcwd(winnr, tabnr) end
|
||||
function vim.fn.getcwd(winnr, tabnr, bufnr) end
|
||||
|
||||
--- Lua: Prefer |vim.env|.
|
||||
---
|
||||
@@ -4445,14 +4463,15 @@ function vim.fn.has(feature) end
|
||||
--- @return 0|1
|
||||
function vim.fn.has_key(dict, key) end
|
||||
|
||||
--- Checks whether the window or tabpage has set a local working
|
||||
--- directory. Returns 1 when the window has set a local path
|
||||
--- via |:lcd| or when {winnr} is -1 and the tabpage has set a
|
||||
--- local path via |:tcd|, otherwise 0.
|
||||
--- Checks whether the window, tabpage or buffer has set a local
|
||||
--- working directory. Returns 1 when the window has set a local
|
||||
--- path via |:lcd|, or when {winnr} is -1 and the tabpage has set
|
||||
--- a local path via |:tcd|, or when {winnr} and {tabnr} are -1
|
||||
--- and {bufnr} has set a local path via |:bcd|, otherwise 0.
|
||||
---
|
||||
--- Tabs and windows are identified by their respective numbers,
|
||||
--- 0 means current tab or window. Missing argument implies 0.
|
||||
--- Thus the following are equivalent: >vim
|
||||
--- Tabs, windows and buffers are identified by their respective
|
||||
--- numbers, 0 means current tab or window. Missing argument
|
||||
--- implies 0. Thus the following are equivalent: >vim
|
||||
--- echo haslocaldir()
|
||||
--- echo haslocaldir(0)
|
||||
--- echo haslocaldir(0, 0)
|
||||
@@ -4460,12 +4479,19 @@ function vim.fn.has_key(dict, key) end
|
||||
--- With {winnr} and {tabnr} use the window in that tabpage.
|
||||
--- {winnr} is a |window-number| or |window-ID|.
|
||||
--- If {winnr} is -1 it is ignored, only the tab is resolved.
|
||||
--- Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
|
||||
--- If {bufnr} is provided, {winnr} and {tabnr} must be -1 and
|
||||
--- only the buffer is resolved.
|
||||
--- Examples of buffer usage: >vim
|
||||
--- haslocaldir(-1, -1, 0) " Current buf has a local directory?
|
||||
--- haslocaldir(-1, -1, 3) " Buf #3 has a local directory?
|
||||
--- <Throw error if the arguments are invalid.
|
||||
--- |E5000| |E5001| |E5002| |E5006| |E5007|
|
||||
---
|
||||
--- @param winnr? integer
|
||||
--- @param tabnr? integer
|
||||
--- @param bufnr? integer
|
||||
--- @return 0|1
|
||||
function vim.fn.haslocaldir(winnr, tabnr) end
|
||||
function vim.fn.haslocaldir(winnr, tabnr, bufnr) end
|
||||
|
||||
--- Checks whether a mapping exists whose rhs contains {what}.
|
||||
--- Returns TRUE if there is such a mapping in one of the modes
|
||||
|
||||
@@ -938,6 +938,9 @@ bool buf_freeall(buf_T *buf, int flags)
|
||||
}
|
||||
syntax_clear(&buf->b_s); // reset syntax info
|
||||
buf->b_flags &= ~BF_READERR; // a read error is no longer relevant
|
||||
|
||||
XFREE_CLEAR(buf->b_localdir);
|
||||
XFREE_CLEAR(buf->b_prevdir);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1023,6 +1026,8 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
|
||||
map_clear_mode(buf, MAP_ALL_MODES, true, false); // clear local mappings
|
||||
map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
|
||||
XFREE_CLEAR(buf->b_start_fenc);
|
||||
XFREE_CLEAR(buf->b_localdir);
|
||||
XFREE_CLEAR(buf->b_prevdir);
|
||||
|
||||
buf_free_callbacks(buf);
|
||||
}
|
||||
@@ -1771,6 +1776,9 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist)
|
||||
if (bufref_valid(&prevbufref) && prevbuf->terminal != NULL) {
|
||||
terminal_check_size(prevbuf->terminal);
|
||||
}
|
||||
|
||||
// Maybe cd to buffer-local directory
|
||||
fix_current_dir(false);
|
||||
}
|
||||
|
||||
/// Enter a new current buffer.
|
||||
@@ -2023,6 +2031,8 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
|
||||
trigger_undo_ftplugin(buf, curwin);
|
||||
// It's like this buffer is deleted. Watch out for autocommands that
|
||||
// change curbuf! If that happens, allocate a new buffer anyway.
|
||||
// We also ask it to not free the buffer-local directory so we can reuse
|
||||
// it.
|
||||
buf_freeall(buf, BFA_WIPE | BFA_DEL);
|
||||
if (aborting()) { // autocmds may abort script processing
|
||||
xfree(ffname);
|
||||
@@ -2152,6 +2162,8 @@ bool curbuf_reusable(void)
|
||||
&& curbuf->b_ffname == NULL
|
||||
&& curbuf->b_nwindows <= 1
|
||||
&& !curbuf->terminal
|
||||
&& curbuf->b_localdir == NULL
|
||||
&& curbuf->b_prevdir == NULL
|
||||
&& (curbuf->b_ml.ml_mfp == NULL || buf_is_empty(curbuf))
|
||||
&& !bt_quickfix(curbuf)
|
||||
&& !curbufIsChanged());
|
||||
|
||||
@@ -767,6 +767,9 @@ struct file_buffer {
|
||||
|
||||
// The number for times the current line has been flushed in the memline.
|
||||
int flush_count;
|
||||
|
||||
char *b_localdir; ///< Absolute path of local cwd or NULL.
|
||||
char *b_prevdir; ///< Previous directory.
|
||||
};
|
||||
|
||||
// Stuff for diff mode.
|
||||
|
||||
@@ -256,6 +256,7 @@ static void ctx_cwd_save(CtxSwitch *cs, win_T *wp, tabpage_T *tp)
|
||||
char cwd[MAXPATHL];
|
||||
if (curwin != wp
|
||||
&& (curwin->w_localdir != NULL || (wp != NULL && wp->w_localdir != NULL)
|
||||
|| curbuf->b_localdir != NULL || (wp != NULL && wp->w_buffer->b_localdir != NULL)
|
||||
|| (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL))
|
||||
|| p_acd)) {
|
||||
cs->cs_cwd_status = os_dirname(cwd, MAXPATHL);
|
||||
@@ -342,9 +343,12 @@ static win_T *ctx_win_prep(CtxSwitch *cs, buf_T *buf)
|
||||
buf->b_nwindows++;
|
||||
win_init_empty(cw_win); // set cursor and topline to safe values
|
||||
|
||||
// Make sure w_localdir, tp_localdir, globaldir are NULL: the switched-to code runs in the actual
|
||||
// cwd (no chdir on switch), and a pooled tmp-window must not carry a stale w_localdir.
|
||||
// Make sure w_localdir, b_localdir, tp_localdir, globaldir are NULL: the switched-to code runs
|
||||
// in the actual cwd (no chdir on switch), and a pooled tmp-window must not carry a stale
|
||||
// w_localdir.
|
||||
XFREE_CLEAR(cw_win->w_localdir);
|
||||
cs->cs_b_localdir = buf->b_localdir;
|
||||
buf->b_localdir = NULL;
|
||||
cs->cs_tp_localdir = curtab->tp_localdir;
|
||||
curtab->tp_localdir = NULL;
|
||||
cs->cs_globaldir = globaldir;
|
||||
@@ -577,9 +581,15 @@ void ctx_restore(CtxSwitch *cs)
|
||||
hash_init(&cwp->w_vars->dv_hashtab); // re-use the hashtab
|
||||
|
||||
// If :lcd has been used in the autocommand window, correct current
|
||||
// directory before restoring tp_localdir and globaldir.
|
||||
// directory before restoring b_localdir, tp_localdir and globaldir.
|
||||
if (cwp->w_localdir != NULL) {
|
||||
win_fix_current_dir();
|
||||
fix_current_dir(true);
|
||||
}
|
||||
if (bufref_valid(&cs->cs_new_curbuf)) {
|
||||
xfree(cs->cs_new_curbuf.br_buf->b_localdir);
|
||||
cs->cs_new_curbuf.br_buf->b_localdir = cs->cs_b_localdir;
|
||||
} else {
|
||||
xfree(cs->cs_b_localdir);
|
||||
}
|
||||
xfree(curtab->tp_localdir);
|
||||
curtab->tp_localdir = cs->cs_tp_localdir;
|
||||
|
||||
@@ -82,6 +82,7 @@ typedef struct {
|
||||
handle_T cs_target_win; ///< the window switched to
|
||||
pos_T cs_target_old_pos; ///< its cursor before the switch
|
||||
// State kept across the switch:
|
||||
char *cs_b_localdir; ///< saved b_localdir of the target buffer (autocmd window)
|
||||
char *cs_tp_localdir; ///< saved tp_localdir (autocmd window)
|
||||
char *cs_globaldir; ///< saved globaldir (autocmd window)
|
||||
char *cs_cwd; ///< saved cwd (kCtxKeepCwd; allocated on demand)
|
||||
|
||||
@@ -20,6 +20,8 @@ EXTERN const char e_invalid_buffer_name_str[] INIT(= N_("E158: Invalid buffer na
|
||||
EXTERN const char e_command_too_recursive[] INIT(= N_("E169: Command too recursive"));
|
||||
EXTERN const char e_buffer_nr_invalid_buffer_number[] INIT(= N_("E680: <buffer=%d>: invalid buffer number"));
|
||||
EXTERN const char e_buffer_is_not_loaded[] INIT(= N_("E681: Buffer is not loaded"));
|
||||
EXTERN const char e_getcwd_buffer_scope_requires_minus_one[] INIT(= N_("E5006: Window and tab scope must be -1 when using buffer scope"));
|
||||
EXTERN const char e_cannot_find_buffer_number[] INIT(= N_("E5007: Cannot find buffer number."));
|
||||
EXTERN const char e_endif[] INIT(= N_("E171: Missing :endif"));
|
||||
EXTERN const char e_endtry[] INIT(= N_("E600: Missing :endtry"));
|
||||
EXTERN const char e_endwhile[] INIT(= N_("E170: Missing :endwhile"));
|
||||
|
||||
@@ -1257,6 +1257,8 @@ M.funcs = {
|
||||
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.
|
||||
- If the buffer local directory (|:bcd|) is set, it
|
||||
changes the current working directory for that scope.
|
||||
- Otherwise, if the tabpage local directory (|:tcd|) is
|
||||
set, it changes the current directory for that scope.
|
||||
- Otherwise, changes the global directory for that scope.
|
||||
@@ -1264,6 +1266,7 @@ M.funcs = {
|
||||
If {scope} is present, changes the current working directory
|
||||
for the specified scope:
|
||||
"window" Changes the window local directory. |:lcd|
|
||||
"buffer" Changes the buffer local directory. |:bcd|
|
||||
"tabpage" Changes the tabpage local directory. |:tcd|
|
||||
"global" Changes the global directory. |:cd|
|
||||
|
||||
@@ -4089,32 +4092,45 @@ M.funcs = {
|
||||
signature = 'getcursorcharpos([{winid}])',
|
||||
},
|
||||
getcwd = {
|
||||
args = { 0, 2 },
|
||||
args = { 0, 3 },
|
||||
base = 1,
|
||||
desc = [=[
|
||||
With no arguments, returns the name of the effective
|
||||
|current-directory|. With {winnr} or {tabnr} the working
|
||||
directory of that scope is returned, and 'autochdir' is
|
||||
ignored. Tabs and windows are identified by their respective
|
||||
numbers, 0 means current tab or window. Missing tab number
|
||||
|current-directory|. With {winnr} or {tabnr} or {bufnr} the
|
||||
working directory of that scope is returned, and 'autochdir'
|
||||
is ignored.
|
||||
|
||||
Tabs, windows and buffers are identified by their respective
|
||||
numbers, 0 means current tab or window or buffer. Missing tab
|
||||
number
|
||||
implies 0. Thus the following are equivalent: >vim
|
||||
getcwd(0)
|
||||
getcwd(0, 0)
|
||||
<If {winnr} is -1 it is ignored, only the tab is resolved.
|
||||
{winnr} is a |window-number| or |window-ID|.
|
||||
If both {winnr} and {tabnr} are -1 the global working
|
||||
directory is returned.
|
||||
If both {winnr} and {tabnr} are -1 and {bufnr} is missing the
|
||||
global working directory is returned.
|
||||
Note: When {tabnr} is -1 Vim returns an empty string to
|
||||
signal that it is invalid, whereas Nvim returns either the
|
||||
global working directory if {winnr} is -1 or the working
|
||||
directory of the window indicated by {winnr}.
|
||||
Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
|
||||
|
||||
If {bufnr} is provided, {winnr} and {tabnr} must be -1 and the
|
||||
working directory of that buffer is returned. If {bufnr} is
|
||||
-1, it is ignored, and the global working directory is
|
||||
returned.
|
||||
Examples of buffer usage: >vim
|
||||
getcwd(-1, -1, 0) " Get current buffer's directory
|
||||
getcwd(-1, -1, 3) " Get directory of buffer #3
|
||||
getcwd(-1, -1, -1) " Get global directory
|
||||
getcwd(-1, -1) " Get global directory
|
||||
<Throw error if the arguments are invalid.
|
||||
|E5000| |E5001| |E5002| |E5006| |E5007|
|
||||
]=],
|
||||
name = 'getcwd',
|
||||
params = { { 'winnr', 'integer' }, { 'tabnr', 'integer' } },
|
||||
params = { { 'winnr', 'integer' }, { 'tabnr', 'integer' }, { 'bufnr', 'integer' } },
|
||||
returns = 'string',
|
||||
signature = 'getcwd([{winnr} [, {tabnr}]])',
|
||||
signature = 'getcwd([{winnr} [, {tabnr} [, {bufnr}]]])',
|
||||
see_lua = {
|
||||
'|uv.cwd()| for the global working directory; tab-local and window-local scopes differ',
|
||||
},
|
||||
@@ -5410,17 +5426,18 @@ M.funcs = {
|
||||
see_lua = { '`dict[key] ~= nil`' },
|
||||
},
|
||||
haslocaldir = {
|
||||
args = { 0, 2 },
|
||||
args = { 0, 3 },
|
||||
base = 1,
|
||||
desc = [=[
|
||||
Checks whether the window or tabpage has set a local working
|
||||
directory. Returns 1 when the window has set a local path
|
||||
via |:lcd| or when {winnr} is -1 and the tabpage has set a
|
||||
local path via |:tcd|, otherwise 0.
|
||||
Checks whether the window, tabpage or buffer has set a local
|
||||
working directory. Returns 1 when the window has set a local
|
||||
path via |:lcd|, or when {winnr} is -1 and the tabpage has set
|
||||
a local path via |:tcd|, or when {winnr} and {tabnr} are -1
|
||||
and {bufnr} has set a local path via |:bcd|, otherwise 0.
|
||||
|
||||
Tabs and windows are identified by their respective numbers,
|
||||
0 means current tab or window. Missing argument implies 0.
|
||||
Thus the following are equivalent: >vim
|
||||
Tabs, windows and buffers are identified by their respective
|
||||
numbers, 0 means current tab or window. Missing argument
|
||||
implies 0. Thus the following are equivalent: >vim
|
||||
echo haslocaldir()
|
||||
echo haslocaldir(0)
|
||||
echo haslocaldir(0, 0)
|
||||
@@ -5428,13 +5445,19 @@ M.funcs = {
|
||||
With {winnr} and {tabnr} use the window in that tabpage.
|
||||
{winnr} is a |window-number| or |window-ID|.
|
||||
If {winnr} is -1 it is ignored, only the tab is resolved.
|
||||
Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
|
||||
If {bufnr} is provided, {winnr} and {tabnr} must be -1 and
|
||||
only the buffer is resolved.
|
||||
Examples of buffer usage: >vim
|
||||
haslocaldir(-1, -1, 0) " Current buf has a local directory?
|
||||
haslocaldir(-1, -1, 3) " Buf #3 has a local directory?
|
||||
<Throw error if the arguments are invalid.
|
||||
|E5000| |E5001| |E5002| |E5006| |E5007|
|
||||
|
||||
]=],
|
||||
name = 'haslocaldir',
|
||||
params = { { 'winnr', 'integer' }, { 'tabnr', 'integer' } },
|
||||
params = { { 'winnr', 'integer' }, { 'tabnr', 'integer' }, { 'bufnr', 'integer' } },
|
||||
returns = '0|1',
|
||||
signature = 'haslocaldir([{winnr} [, {tabnr}]])',
|
||||
signature = 'haslocaldir([{winnr} [, {tabnr} [, {bufnr} ]]])',
|
||||
},
|
||||
hasmapto = {
|
||||
args = { 1, 3 },
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "auto/config.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/ascii_defs.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/cmdexpand.h"
|
||||
@@ -419,6 +420,8 @@ void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
}
|
||||
} else if (curwin->w_localdir != NULL) {
|
||||
scope = kCdScopeWindow;
|
||||
} else if (curbuf->b_localdir != NULL) {
|
||||
scope = kCdScopeBuffer;
|
||||
} else if (curtab->tp_localdir != NULL) {
|
||||
scope = kCdScopeTabpage;
|
||||
}
|
||||
@@ -641,12 +644,12 @@ void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
xfree(fbuf);
|
||||
}
|
||||
|
||||
/// `getcwd([{win}[, {tab}]])` function
|
||||
/// `getcwd([{win}[, {tab}[, {buf}]]])` function
|
||||
///
|
||||
/// Every scope not specified implies the currently selected scope object.
|
||||
///
|
||||
/// @pre The arguments must be of type number.
|
||||
/// @pre There may not be more than two arguments.
|
||||
/// @pre There may not be more than three arguments.
|
||||
/// @pre An argument may not be -1 if preceding arguments are not all -1.
|
||||
///
|
||||
/// @post The return value will be a string.
|
||||
@@ -655,50 +658,103 @@ void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
// Possible scope of working directory to return.
|
||||
CdScope scope = kCdScopeInvalid;
|
||||
|
||||
// Numbers of the scope objects (window, tab) we want the working directory
|
||||
// of. A `-1` means to skip this scope, a `0` means the current object.
|
||||
int scope_number[] = {
|
||||
[kCdScopeWindow] = 0, // Number of window to look at.
|
||||
[kCdScopeTabpage] = 0, // Number of tab to look at.
|
||||
// Numbers of the scope objects (window, buffer, tab) we want the working
|
||||
// directory of. A `-1` means to skip this scope, a `0` means the current object.
|
||||
|
||||
// getcwd() takes arguments in this order: (window, tab, buffer)
|
||||
// Note that this is different from the order of CdScope
|
||||
enum {
|
||||
WINDOW_IDX = 0,
|
||||
TABPAGE_IDX = 1,
|
||||
BUFFER_IDX = 2,
|
||||
};
|
||||
|
||||
int argv[] = { // arguments passed to getcwd().
|
||||
0, // Number of window to look at.
|
||||
0, // Number of tab to look at.
|
||||
0, // Number of buffer to look at.
|
||||
};
|
||||
int argc = 0; // number of arguments passed to getcwd().
|
||||
|
||||
char *cwd = NULL; // Current working directory to print
|
||||
char *from = NULL; // The original string to copy
|
||||
|
||||
tabpage_T *tp = curtab; // The tabpage to look at.
|
||||
win_T *win = curwin; // The window to look at.
|
||||
buf_T *buf = curbuf; // The buffer to look at.
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
// Pre-conditions and scope extraction together
|
||||
for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) {
|
||||
// Pre-conditions
|
||||
for (; argc < 3; argc++) {
|
||||
// If there is no argument there are no more scopes after it, break out.
|
||||
if (argvars[i].v_type == VAR_UNKNOWN) {
|
||||
if (argvars[argc].v_type == VAR_UNKNOWN) {
|
||||
break;
|
||||
}
|
||||
if (argvars[i].v_type != VAR_NUMBER) {
|
||||
if (argvars[argc].v_type != VAR_NUMBER) {
|
||||
emsg(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
scope_number[i] = (int)argvars[i].vval.v_number;
|
||||
argv[argc] = (int)argvars[argc].vval.v_number;
|
||||
// It is an error for the scope number to be less than `-1`.
|
||||
if (scope_number[i] < -1) {
|
||||
if (argv[argc] < -1) {
|
||||
emsg(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
// Use the narrowest scope the user requested
|
||||
if (scope_number[i] >= 0 && scope == kCdScopeInvalid) {
|
||||
// The scope is the current iteration step.
|
||||
scope = i;
|
||||
} else if (scope_number[i] < 0) {
|
||||
scope = i + 1;
|
||||
}
|
||||
|
||||
// Scope extraction
|
||||
// Imagine X >= 0
|
||||
switch (argc) {
|
||||
case 0:
|
||||
scope = kCdScopeInvalid; // getcwd()
|
||||
break;
|
||||
case 1:
|
||||
if (argv[WINDOW_IDX] > -1) {
|
||||
scope = kCdScopeWindow; // getcwd(X)
|
||||
} else {
|
||||
scope = kCdScopeTabpage; // getcwd(-1)
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (argv[WINDOW_IDX] > -1) {
|
||||
scope = kCdScopeWindow; // getcwd(X, ...)
|
||||
} else if (argv[TABPAGE_IDX] > -1) {
|
||||
scope = kCdScopeTabpage; // getcwd(-1, X)
|
||||
} else {
|
||||
scope = kCdScopeGlobal; // getcwd(-1, -1)
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (argv[BUFFER_IDX] > -1) {
|
||||
scope = kCdScopeBuffer; // getcwd(..., ..., X)
|
||||
} else {
|
||||
scope = kCdScopeGlobal; // getcwd(..., ..., -1)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// getcwd(-1, -1, X)
|
||||
if (scope == kCdScopeBuffer) {
|
||||
if (argv[WINDOW_IDX] >= 0 || argv[TABPAGE_IDX] >= 0) {
|
||||
emsg(_(e_getcwd_buffer_scope_requires_minus_one));
|
||||
return;
|
||||
}
|
||||
if (argv[BUFFER_IDX] > 0) {
|
||||
Error err = ERROR_INIT;
|
||||
buf = find_buffer_by_handle(argv[BUFFER_IDX], &err);
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg(_(e_cannot_find_buffer_number));
|
||||
xfree(err.msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the tabpage by number
|
||||
if (scope_number[kCdScopeTabpage] > 0) {
|
||||
tp = find_tabpage(scope_number[kCdScopeTabpage]);
|
||||
if (argv[TABPAGE_IDX] > 0) {
|
||||
tp = find_tabpage(argv[TABPAGE_IDX]);
|
||||
if (!tp) {
|
||||
emsg(_("E5000: Cannot find tab number."));
|
||||
return;
|
||||
@@ -706,19 +762,21 @@ void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
}
|
||||
|
||||
// Find the window in `tp` by number, `NULL` if none.
|
||||
if (scope_number[kCdScopeWindow] >= 0) {
|
||||
if (scope_number[kCdScopeTabpage] < 0) {
|
||||
if (argv[WINDOW_IDX] >= 0) {
|
||||
if (argv[TABPAGE_IDX] < 0) {
|
||||
emsg(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope_number[kCdScopeWindow] > 0) {
|
||||
if (argv[WINDOW_IDX] > 0) {
|
||||
win = find_win_by_nr(&argvars[0], tp);
|
||||
if (!win) {
|
||||
emsg(_("E5002: Cannot find window number."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Window scope may fall through to the buffer shown in that window.
|
||||
buf = win->w_buffer;
|
||||
}
|
||||
|
||||
cwd = xmalloc(MAXPATHL);
|
||||
@@ -731,6 +789,13 @@ void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
break;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case kCdScopeBuffer:
|
||||
assert(buf);
|
||||
from = buf->b_localdir;
|
||||
if (from) {
|
||||
break;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case kCdScopeTabpage:
|
||||
assert(tp);
|
||||
from = tp->tp_localdir;
|
||||
@@ -959,7 +1024,7 @@ void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
rettv->vval.v_string = pat == NULL ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, false);
|
||||
}
|
||||
|
||||
/// `haslocaldir([{win}[, {tab}]])` function
|
||||
/// `haslocaldir([{win}[, {tab}[, {buf}]]])` function
|
||||
///
|
||||
/// Returns `1` if the scope object has a local directory, `0` otherwise. If a
|
||||
/// scope object is not specified the current one is implied. This function
|
||||
@@ -977,62 +1042,113 @@ void f_haslocaldir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|
||||
// Numbers of the scope objects (window, tab) we want the working directory
|
||||
// of. A `-1` means to skip this scope, a `0` means the current object.
|
||||
int scope_number[] = {
|
||||
[kCdScopeWindow] = 0, // Number of window to look at.
|
||||
[kCdScopeTabpage] = 0, // Number of tab to look at.
|
||||
|
||||
// haslocaldir() takes arguments in this order: (window, tab, buffer)
|
||||
// Note that this is different from the order of CdScope
|
||||
enum {
|
||||
WINDOW_IDX = 0,
|
||||
TABPAGE_IDX = 1,
|
||||
BUFFER_IDX = 2,
|
||||
};
|
||||
|
||||
int argv[] = { // arguments passed to haslocaldir().
|
||||
0, // Number of window to look at.
|
||||
0, // Number of tab to look at.
|
||||
0, // Number of buffer to look at.
|
||||
};
|
||||
int argc = 0; // number of arguments passed to haslocaldir.
|
||||
|
||||
tabpage_T *tp = curtab; // The tabpage to look at.
|
||||
win_T *win = curwin; // The window to look at.
|
||||
win_T *win = curwin; // The window to look at.
|
||||
buf_T *buf = curbuf; // The buffer to look at.
|
||||
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = 0;
|
||||
|
||||
// Pre-conditions and scope extraction together
|
||||
for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) {
|
||||
if (argvars[i].v_type == VAR_UNKNOWN) {
|
||||
// Pre-conditions
|
||||
for (; argc < 3; argc++) {
|
||||
if (argvars[argc].v_type == VAR_UNKNOWN) {
|
||||
break;
|
||||
}
|
||||
if (argvars[i].v_type != VAR_NUMBER) {
|
||||
if (argvars[argc].v_type != VAR_NUMBER) {
|
||||
emsg(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
scope_number[i] = (int)argvars[i].vval.v_number;
|
||||
if (scope_number[i] < -1) {
|
||||
argv[argc] = (int)argvars[argc].vval.v_number;
|
||||
if (argv[argc] < -1) {
|
||||
emsg(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
// Use the narrowest scope the user requested
|
||||
if (scope_number[i] >= 0 && scope == kCdScopeInvalid) {
|
||||
// The scope is the current iteration step.
|
||||
scope = i;
|
||||
} else if (scope_number[i] < 0) {
|
||||
scope = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If the user didn't specify anything, default to window scope
|
||||
if (scope == kCdScopeInvalid) {
|
||||
scope = MIN_CD_SCOPE;
|
||||
// Scope extraction
|
||||
// Imagine X >= 0
|
||||
switch (argc) {
|
||||
case 0:
|
||||
// If the user didn't specify anything, default to window scope
|
||||
scope = kCdScopeWindow; // haslocaldir()
|
||||
break;
|
||||
case 1:
|
||||
if (argv[0] > -1) {
|
||||
scope = kCdScopeWindow; // haslocaldir(X)
|
||||
} else {
|
||||
scope = kCdScopeTabpage; // haslocaldir(-1)
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (argv[0] > -1) {
|
||||
scope = kCdScopeWindow; // haslocaldir(X, ...)
|
||||
} else if (argv[1] > -1) {
|
||||
scope = kCdScopeTabpage; // haslocaldir(-1, X)
|
||||
} else {
|
||||
scope = kCdScopeGlobal; // haslocaldir(-1, -1)
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (argv[2] > -1) {
|
||||
scope = kCdScopeBuffer; // haslocaldir(..., ..., X)
|
||||
} else {
|
||||
scope = kCdScopeGlobal; // haslocaldir(..., ..., -1)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// haslocaldir(-1, -1, X)
|
||||
if (scope == kCdScopeBuffer) {
|
||||
if (argv[WINDOW_IDX] >= 0 || argv[TABPAGE_IDX] >= 0) {
|
||||
emsg(_(e_getcwd_buffer_scope_requires_minus_one));
|
||||
return;
|
||||
}
|
||||
if (argv[BUFFER_IDX] > 0) {
|
||||
Error err = ERROR_INIT;
|
||||
buf = find_buffer_by_handle(argv[BUFFER_IDX], &err);
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg(_(e_cannot_find_buffer_number));
|
||||
xfree(err.msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the tabpage by number
|
||||
if (scope_number[kCdScopeTabpage] > 0) {
|
||||
tp = find_tabpage(scope_number[kCdScopeTabpage]);
|
||||
if (!tp) {
|
||||
emsg(_("E5000: Cannot find tab number."));
|
||||
return;
|
||||
if (argv[TABPAGE_IDX] >= 0) {
|
||||
if (argv[TABPAGE_IDX] > 0) {
|
||||
tp = find_tabpage(argv[TABPAGE_IDX]);
|
||||
if (!tp) {
|
||||
emsg(_("E5000: Cannot find tab number."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the window in `tp` by number, `NULL` if none.
|
||||
if (scope_number[kCdScopeWindow] >= 0) {
|
||||
if (scope_number[kCdScopeTabpage] < 0) {
|
||||
if (argv[WINDOW_IDX] >= 0) {
|
||||
if (argv[TABPAGE_IDX] < 0) {
|
||||
emsg(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope_number[kCdScopeWindow] > 0) {
|
||||
if (argv[WINDOW_IDX] > 0) {
|
||||
win = find_win_by_nr(&argvars[0], tp);
|
||||
if (!win) {
|
||||
emsg(_("E5002: Cannot find window number."));
|
||||
@@ -1046,6 +1162,10 @@ void f_haslocaldir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
assert(win);
|
||||
rettv->vval.v_number = win->w_localdir ? 1 : 0;
|
||||
break;
|
||||
case kCdScopeBuffer:
|
||||
assert(buf);
|
||||
rettv->vval.v_number = buf->b_localdir ? 1 : 0;
|
||||
break;
|
||||
case kCdScopeTabpage:
|
||||
assert(tp);
|
||||
rettv->vval.v_number = tp->tp_localdir ? 1 : 0;
|
||||
|
||||
@@ -2937,6 +2937,24 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
|
||||
redraw_curbuf_later(UPD_NOT_VALID); // redraw this buffer later
|
||||
}
|
||||
|
||||
// A new buffer inherits the buffer-local directory of the buffer it was created from (unless
|
||||
// autocommands already set one).
|
||||
if (!oldbuf && curbuf->b_localdir == NULL
|
||||
&& bufref_valid(&old_curbuf)
|
||||
&& old_curbuf.br_buf != curbuf
|
||||
&& old_curbuf.br_buf->b_localdir != NULL) {
|
||||
curbuf->b_localdir = xstrdup(old_curbuf.br_buf->b_localdir);
|
||||
if (old_curbuf.br_buf->b_prevdir != NULL) {
|
||||
curbuf->b_prevdir = xstrdup(old_curbuf.br_buf->b_prevdir);
|
||||
}
|
||||
}
|
||||
|
||||
// If editing a buffer in the current window, make sure to update to the
|
||||
// buffer's working directory.
|
||||
if (oldwin == curwin) {
|
||||
fix_current_dir(false);
|
||||
}
|
||||
|
||||
// Change directories when the 'acd' option is set.
|
||||
do_autochdir();
|
||||
|
||||
|
||||
@@ -192,6 +192,18 @@ M.cmds = {
|
||||
addr_type = 'ADDR_NONE',
|
||||
func = 'ex_edit',
|
||||
},
|
||||
{
|
||||
command = 'bcd',
|
||||
flags = bit.bor(BANG, FILE1, TRLBAR, BUFLOCK_OK, LOCK_OK),
|
||||
addr_type = 'ADDR_NONE',
|
||||
func = 'ex_cd',
|
||||
},
|
||||
{
|
||||
command = 'bchdir',
|
||||
flags = bit.bor(BANG, FILE1, TRLBAR, BUFLOCK_OK, LOCK_OK),
|
||||
addr_type = 'ADDR_NONE',
|
||||
func = 'ex_cd',
|
||||
},
|
||||
{
|
||||
command = 'bdelete',
|
||||
flags = bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR),
|
||||
|
||||
@@ -6212,6 +6212,9 @@ static char *get_prevdir(CdScope scope)
|
||||
case kCdScopeTabpage:
|
||||
return curtab->tp_prevdir;
|
||||
break;
|
||||
case kCdScopeBuffer:
|
||||
return curbuf->b_prevdir;
|
||||
break;
|
||||
case kCdScopeWindow:
|
||||
return curwin->w_prevdir;
|
||||
break;
|
||||
@@ -6222,7 +6225,7 @@ static char *get_prevdir(CdScope scope)
|
||||
|
||||
/// Deal with the side effects of changing the current directory.
|
||||
///
|
||||
/// @param scope Scope of the function call (global, tab or window).
|
||||
/// @param scope Scope of the function call (global, tab, buffer or window).
|
||||
static void post_chdir(CdScope scope, bool trigger_dirchanged)
|
||||
{
|
||||
// Always overwrite the window-local CWD.
|
||||
@@ -6233,6 +6236,10 @@ static void post_chdir(CdScope scope, bool trigger_dirchanged)
|
||||
XFREE_CLEAR(curtab->tp_localdir);
|
||||
}
|
||||
|
||||
if (scope >= kCdScopeBuffer) {
|
||||
XFREE_CLEAR(curbuf->b_localdir);
|
||||
}
|
||||
|
||||
if (scope < kCdScopeGlobal) {
|
||||
char *pdir = get_prevdir(scope);
|
||||
// If still in global directory, set CWD as the global directory.
|
||||
@@ -6253,6 +6260,9 @@ static void post_chdir(CdScope scope, bool trigger_dirchanged)
|
||||
case kCdScopeTabpage:
|
||||
curtab->tp_localdir = xstrdup(cwd);
|
||||
break;
|
||||
case kCdScopeBuffer:
|
||||
curbuf->b_localdir = xstrdup(cwd);
|
||||
break;
|
||||
case kCdScopeWindow:
|
||||
curwin->w_localdir = xstrdup(cwd);
|
||||
break;
|
||||
@@ -6268,9 +6278,9 @@ static void post_chdir(CdScope scope, bool trigger_dirchanged)
|
||||
}
|
||||
}
|
||||
|
||||
/// Change directory function used by :cd/:tcd/:lcd Ex commands and the chdir() function.
|
||||
/// Change directory function used by :cd/:tcd/:bcd/:lcd Ex commands and the chdir() function.
|
||||
/// @param new_dir The directory to change to.
|
||||
/// @param scope Scope of the function call (global, tab or window).
|
||||
/// @param scope Scope of the function call (global, tab, buffer or window).
|
||||
/// @return true if the directory is successfully changed.
|
||||
bool changedir_func(char *new_dir, CdScope scope)
|
||||
{
|
||||
@@ -6325,6 +6335,9 @@ bool changedir_func(char *new_dir, CdScope scope)
|
||||
case kCdScopeWindow:
|
||||
pp = &curwin->w_prevdir;
|
||||
break;
|
||||
case kCdScopeBuffer:
|
||||
pp = &curbuf->b_prevdir;
|
||||
break;
|
||||
default:
|
||||
pp = &prev_dir;
|
||||
}
|
||||
@@ -6336,7 +6349,7 @@ bool changedir_func(char *new_dir, CdScope scope)
|
||||
return true;
|
||||
}
|
||||
|
||||
/// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir".
|
||||
/// ":cd", ":tcd", ":bcd", ":lcd", ":chdir", "tchdir", ":bchdir", and ":lchdir".
|
||||
void ex_cd(exarg_T *eap)
|
||||
{
|
||||
char *new_dir = eap->arg;
|
||||
@@ -6352,6 +6365,10 @@ void ex_cd(exarg_T *eap)
|
||||
case CMD_tchdir:
|
||||
scope = kCdScopeTabpage;
|
||||
break;
|
||||
case CMD_bcd:
|
||||
case CMD_bchdir:
|
||||
scope = kCdScopeBuffer;
|
||||
break;
|
||||
case CMD_lcd:
|
||||
case CMD_lchdir:
|
||||
scope = kCdScopeWindow;
|
||||
@@ -6380,6 +6397,8 @@ static void ex_pwd(exarg_T *eap)
|
||||
context = last_chdir_reason;
|
||||
} else if (curwin->w_localdir != NULL) {
|
||||
context = "window";
|
||||
} else if (curbuf->b_localdir != NULL) {
|
||||
context = "buffer";
|
||||
} else if (curtab->tp_localdir != NULL) {
|
||||
context = "tabpage";
|
||||
}
|
||||
|
||||
@@ -822,9 +822,8 @@ static int makeopens(FILE *fd, char *dirnow)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Restore the tab-local working directory if specified
|
||||
// Do this before the windows, so that the window-local directory can
|
||||
// override the tab-local directory.
|
||||
// Restore the tab-local working directory if specified. Do this before the buffers and windows,
|
||||
// so the buf-local / win-local directory can override the tab-local directory.
|
||||
if ((ssop_flags & kOptSsopFlagCurdir) && tp->tp_localdir != NULL) {
|
||||
if (fputs("tcd ", fd) < 0
|
||||
|| ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|
||||
@@ -834,6 +833,32 @@ static int makeopens(FILE *fd, char *dirnow)
|
||||
did_lcd = true;
|
||||
}
|
||||
|
||||
// Restore buffer-local CWD (:bcd) via a one-shot BufEnter handler: there is no reliable point
|
||||
// during session-load where all buffers are entered. Re-applied per tab, AFTER the ":tcd"
|
||||
// above: ":tcd" clears the b_localdir of the then-current buffer, which may be a buffer whose
|
||||
// autocmd already fired.
|
||||
if (ssop_flags & kOptSsopFlagCurdir) {
|
||||
FOR_ALL_BUFFERS(buf) {
|
||||
if (buf->b_localdir == NULL || buf->b_fname == NULL || !buf->b_p_bl
|
||||
|| (only_save_windows && buf->b_nwindows == 0)
|
||||
|| (buf->b_help && !(ssop_flags & kOptSsopFlagHelp))
|
||||
|| (bt_terminal(buf) && !(ssop_flags & kOptSsopFlagTerminal))) {
|
||||
continue;
|
||||
}
|
||||
// `bufadd()` finds the buffer by exact (literal) name.
|
||||
if (fputs("lua vim.api.nvim_create_autocmd('BufEnter', { once = true, "
|
||||
"buffer = vim.fn.bufadd([=[", fd) < 0
|
||||
|| fputs(ses_get_fname(buf, &ssop_flags), fd) < 0
|
||||
|| fputs("]=]), callback = function() vim.cmd.bcd({ [=[", fd) < 0
|
||||
|| fputs(buf->b_localdir, fd) < 0
|
||||
|| fputs("]=], magic = { file = false, bar = false } }) end })\n", fd) < 0) {
|
||||
return FAIL;
|
||||
}
|
||||
// Cwd may change when the autocmd fires: filenames must be written absolute from here on.
|
||||
did_lcd = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the view of the window (options, file, cursor, etc.).
|
||||
for (win_T *wp = tab_firstwin; wp != NULL; wp = wp->w_next) {
|
||||
if (!ses_do_win(wp)) {
|
||||
|
||||
@@ -1854,6 +1854,9 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre
|
||||
case kCdScopeTabpage:
|
||||
snprintf(buf, sizeof(buf), "tabpage");
|
||||
break;
|
||||
case kCdScopeBuffer:
|
||||
snprintf(buf, sizeof(buf), "buffer");
|
||||
break;
|
||||
case kCdScopeWindow:
|
||||
snprintf(buf, sizeof(buf), "window");
|
||||
break;
|
||||
@@ -1874,6 +1877,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre
|
||||
switch (cause) {
|
||||
case kCdCauseManual:
|
||||
case kCdCauseWindow:
|
||||
case kCdCauseBuffer:
|
||||
break;
|
||||
case kCdCauseAuto:
|
||||
snprintf(buf, sizeof(buf), "auto");
|
||||
|
||||
@@ -33,23 +33,21 @@ typedef enum {
|
||||
///
|
||||
/// Scopes are enumerated from lowest to highest. When adding a scope make sure
|
||||
/// to update all functions using scopes as well, such as the implementation of
|
||||
/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes
|
||||
/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead.
|
||||
/// `getcwd()`.
|
||||
typedef enum {
|
||||
kCdScopeInvalid = -1,
|
||||
kCdScopeWindow, ///< Affects one window.
|
||||
kCdScopeBuffer, ///< Affects one buffer.
|
||||
kCdScopeTabpage, ///< Affects one tab page.
|
||||
kCdScopeGlobal, ///< Affects the entire Nvim instance.
|
||||
} CdScope;
|
||||
|
||||
#define MIN_CD_SCOPE kCdScopeWindow
|
||||
#define MAX_CD_SCOPE kCdScopeGlobal
|
||||
|
||||
/// What caused the current directory to change.
|
||||
typedef enum {
|
||||
kCdCauseOther = -1,
|
||||
kCdCauseManual, ///< Using `:cd`, `:tcd`, `:lcd` or `chdir()`.
|
||||
kCdCauseManual, ///< Using `:cd`, `:bcd`, `:tcd`, `:lcd` or `chdir()`.
|
||||
kCdCauseWindow, ///< Switching to another window.
|
||||
kCdCauseBuffer, ///< Switching to another buffer.
|
||||
kCdCauseAuto, ///< On 'autochdir'.
|
||||
} CdCause;
|
||||
|
||||
|
||||
@@ -5300,7 +5300,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
|
||||
win_fix_cursor(get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL));
|
||||
}
|
||||
|
||||
win_fix_current_dir();
|
||||
fix_current_dir(true);
|
||||
|
||||
entering_window(curwin);
|
||||
// Careful: autocommands may close the window and make "wp" invalid
|
||||
@@ -5352,18 +5352,41 @@ static void win_enter_ext(win_T *const wp, const int flags)
|
||||
do_autochdir();
|
||||
}
|
||||
|
||||
/// Used after making another window the current one: change directory if needed.
|
||||
void win_fix_current_dir(void)
|
||||
/// Used after making another window or buffer the current one: change directory if needed.
|
||||
void fix_current_dir(bool caused_by_win)
|
||||
{
|
||||
// New directory is either the local directory of the window, tab or NULL.
|
||||
char *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir;
|
||||
CdCause cause;
|
||||
if (caused_by_win) {
|
||||
cause = kCdCauseWindow;
|
||||
} else {
|
||||
cause = kCdCauseBuffer;
|
||||
}
|
||||
|
||||
// New directory is either the local directory of the window, buffer, tab or NULL.
|
||||
char *new_dir;
|
||||
CdScope scope;
|
||||
|
||||
if (curwin->w_localdir) {
|
||||
new_dir = curwin->w_localdir;
|
||||
scope = kCdScopeWindow;
|
||||
} else if (curbuf->b_localdir) {
|
||||
new_dir = curbuf->b_localdir;
|
||||
scope = kCdScopeBuffer;
|
||||
} else if (curtab->tp_localdir) {
|
||||
new_dir = curtab->tp_localdir;
|
||||
scope = kCdScopeTabpage;
|
||||
} else {
|
||||
new_dir = NULL;
|
||||
scope = kCdScopeGlobal;
|
||||
}
|
||||
|
||||
char cwd[MAXPATHL];
|
||||
if (os_dirname(cwd, MAXPATHL) != OK) {
|
||||
cwd[0] = NUL;
|
||||
}
|
||||
|
||||
if (new_dir) {
|
||||
// Window/tab has a local directory: Save current directory as global
|
||||
// Window/buffer/tab has a local directory: Save current directory as global
|
||||
// (unless that was done already) and change to the local directory.
|
||||
if (globaldir == NULL) {
|
||||
if (cwd[0] != NUL) {
|
||||
@@ -5372,27 +5395,25 @@ void win_fix_current_dir(void)
|
||||
}
|
||||
bool dir_differs = pathcmp(new_dir, cwd, -1) != 0;
|
||||
if (!p_acd && dir_differs) {
|
||||
do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage,
|
||||
kCdCauseWindow, true);
|
||||
do_autocmd_dirchanged(new_dir, scope, cause, true);
|
||||
}
|
||||
if (os_chdir(new_dir) == 0) {
|
||||
if (!p_acd && dir_differs) {
|
||||
do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage,
|
||||
kCdCauseWindow, false);
|
||||
do_autocmd_dirchanged(new_dir, scope, cause, false);
|
||||
}
|
||||
}
|
||||
last_chdir_reason = NULL;
|
||||
shorten_fnames(true);
|
||||
} else if (globaldir != NULL) {
|
||||
// Window doesn't have a local directory and we are not in the global
|
||||
// Window nor buffer have a local directory and we are not in the global
|
||||
// directory: Change to the global directory.
|
||||
bool dir_differs = pathcmp(globaldir, cwd, -1) != 0;
|
||||
if (!p_acd && dir_differs) {
|
||||
do_autocmd_dirchanged(globaldir, kCdScopeGlobal, kCdCauseWindow, true);
|
||||
do_autocmd_dirchanged(globaldir, kCdScopeGlobal, cause, true);
|
||||
}
|
||||
if (os_chdir(globaldir) == 0) {
|
||||
if (!p_acd && dir_differs) {
|
||||
do_autocmd_dirchanged(globaldir, kCdScopeGlobal, kCdCauseWindow, false);
|
||||
do_autocmd_dirchanged(globaldir, kCdScopeGlobal, cause, false);
|
||||
}
|
||||
}
|
||||
XFREE_CLEAR(globaldir);
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
curdir .. '/Xtest-functional-autocmd-dirchanged.dir1',
|
||||
curdir .. '/Xtest-functional-autocmd-dirchanged.dir2',
|
||||
curdir .. '/Xtest-functional-autocmd-dirchanged.dir3',
|
||||
curdir .. '/Xtest-functional-autocmd-dirchanged.dir4',
|
||||
}
|
||||
local win_dirs = {
|
||||
curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR1',
|
||||
@@ -62,32 +63,43 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
eq(1, eval('g:cdprecount'))
|
||||
eq(1, eval('g:cdcount'))
|
||||
|
||||
command('tcd ' .. dirs[2])
|
||||
eq({ directory = dirs[2], scope = 'tabpage', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[2], scope = 'tabpage', changed_window = false }, eval('g:ev'))
|
||||
eq('tabpage', eval('g:amatchpre'))
|
||||
eq('tabpage', eval('g:amatch'))
|
||||
command('bcd ' .. dirs[2])
|
||||
eq({ directory = dirs[2], scope = 'buffer', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[2], scope = 'buffer', changed_window = false }, eval('g:ev'))
|
||||
eq('buffer', eval('g:amatchpre'))
|
||||
eq('buffer', eval('g:amatch'))
|
||||
eq(2, eval('g:cdprecount'))
|
||||
eq(2, eval('g:cdcount'))
|
||||
|
||||
command('cd ' .. dirs[3])
|
||||
eq({ directory = dirs[3], scope = 'global', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[3], scope = 'global', changed_window = false }, eval('g:ev'))
|
||||
eq('global', eval('g:amatchpre'))
|
||||
eq('global', eval('g:amatch'))
|
||||
command('tcd ' .. dirs[3])
|
||||
eq({ directory = dirs[3], scope = 'tabpage', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[3], scope = 'tabpage', changed_window = false }, eval('g:ev'))
|
||||
eq('tabpage', eval('g:amatchpre'))
|
||||
eq('tabpage', eval('g:amatch'))
|
||||
eq(3, eval('g:cdprecount'))
|
||||
eq(3, eval('g:cdcount'))
|
||||
|
||||
command('cd ' .. dirs[4])
|
||||
eq({ directory = dirs[4], scope = 'global', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[4], scope = 'global', changed_window = false }, eval('g:ev'))
|
||||
eq('global', eval('g:amatchpre'))
|
||||
eq('global', eval('g:amatch'))
|
||||
eq(4, eval('g:cdprecount'))
|
||||
eq(4, eval('g:cdcount'))
|
||||
end)
|
||||
|
||||
it('DirChanged set getcwd() during event #6260', function()
|
||||
command('lcd ' .. dirs[1])
|
||||
eq(dirs[1], eval('g:getcwd'))
|
||||
|
||||
command('tcd ' .. dirs[2])
|
||||
command('bcd ' .. dirs[2])
|
||||
eq(dirs[2], eval('g:getcwd'))
|
||||
|
||||
command('cd ' .. dirs[3])
|
||||
command('tcd ' .. dirs[3])
|
||||
eq(dirs[3], eval('g:getcwd'))
|
||||
|
||||
command('cd ' .. dirs[4])
|
||||
eq(dirs[4], eval('g:getcwd'))
|
||||
end)
|
||||
|
||||
it('disallow recursion', function()
|
||||
@@ -216,52 +228,75 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
eq(2, eval('g:cdcount'))
|
||||
end
|
||||
|
||||
command('cd ' .. dirs[3])
|
||||
eq({ directory = dirs[3], scope = 'global', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[3], scope = 'global', changed_window = false }, eval('g:ev'))
|
||||
eq('global', eval('g:amatch'))
|
||||
command('tcd ' .. dirs[3])
|
||||
eq({ directory = dirs[3], scope = 'tabpage', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[3], scope = 'tabpage', changed_window = false }, eval('g:ev'))
|
||||
eq('tabpage', eval('g:amatchpre'))
|
||||
eq('tabpage', eval('g:amatch'))
|
||||
eq(3, eval('g:cdprecount'))
|
||||
eq(3, eval('g:cdcount'))
|
||||
command('let g:evpre = {}')
|
||||
command('let g:ev = {}')
|
||||
command('cd ' .. dirs[3])
|
||||
command('tcd ' .. dirs[3])
|
||||
eq({}, eval('g:evpre'))
|
||||
eq({}, eval('g:ev'))
|
||||
eq(3, eval('g:cdprecount'))
|
||||
eq(3, eval('g:cdcount'))
|
||||
|
||||
if is_os('win') then
|
||||
command('cd ' .. win_dirs[3])
|
||||
command('tcd ' .. win_dirs[3])
|
||||
eq({}, eval('g:evpre'))
|
||||
eq({}, eval('g:ev'))
|
||||
eq(3, eval('g:cdprecount'))
|
||||
eq(3, eval('g:cdcount'))
|
||||
end
|
||||
|
||||
command('set autochdir')
|
||||
|
||||
command('split ' .. dirs[1] .. '/foo')
|
||||
eq({ directory = dirs[1], scope = 'window', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[1], scope = 'window', changed_window = false }, eval('g:ev'))
|
||||
eq('auto', eval('g:amatchpre'))
|
||||
eq('auto', eval('g:amatch'))
|
||||
command('cd ' .. dirs[4])
|
||||
eq({ directory = dirs[4], scope = 'global', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[4], scope = 'global', changed_window = false }, eval('g:ev'))
|
||||
eq('global', eval('g:amatch'))
|
||||
eq(4, eval('g:cdprecount'))
|
||||
eq(4, eval('g:cdcount'))
|
||||
command('let g:evpre = {}')
|
||||
command('let g:ev = {}')
|
||||
command('split ' .. dirs[1] .. '/bar')
|
||||
command('cd ' .. dirs[4])
|
||||
eq({}, eval('g:evpre'))
|
||||
eq({}, eval('g:ev'))
|
||||
eq(4, eval('g:cdprecount'))
|
||||
eq(4, eval('g:cdcount'))
|
||||
|
||||
if is_os('win') then
|
||||
command('split ' .. win_dirs[1] .. '/baz')
|
||||
command('cd ' .. dirs[4])
|
||||
eq({}, eval('g:evpre'))
|
||||
eq({}, eval('g:ev'))
|
||||
eq(4, eval('g:cdprecount'))
|
||||
eq(4, eval('g:cdcount'))
|
||||
end
|
||||
|
||||
command('set autochdir')
|
||||
|
||||
command(('split %s/foo'):format(dirs[2]))
|
||||
eq({ directory = dirs[2], scope = 'window', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[2], scope = 'window', changed_window = false }, eval('g:ev'))
|
||||
eq('auto', eval('g:amatchpre'))
|
||||
eq('auto', eval('g:amatch'))
|
||||
eq(5, eval('g:cdprecount'))
|
||||
eq(5, eval('g:cdcount'))
|
||||
command('let g:evpre = {}')
|
||||
command('let g:ev = {}')
|
||||
command(('split %s/bar'):format(dirs[2]))
|
||||
eq({}, eval('g:evpre'))
|
||||
eq({}, eval('g:ev'))
|
||||
eq(5, eval('g:cdprecount'))
|
||||
eq(5, eval('g:cdcount'))
|
||||
|
||||
if is_os('win') then
|
||||
command(('split %s/baz'):format(win_dirs[2]))
|
||||
eq({}, eval('g:evpre'))
|
||||
eq({}, eval('g:ev'))
|
||||
eq(5, eval('g:cdprecount'))
|
||||
eq(5, eval('g:cdcount'))
|
||||
end
|
||||
end)
|
||||
|
||||
it('are triggered by switching to win/tab with different CWD #6054', function()
|
||||
@@ -341,6 +376,141 @@ describe('autocmd DirChanged and DirChangedPre', function()
|
||||
end
|
||||
end)
|
||||
|
||||
it('are triggered by switching to buf/tab with different CWD', function()
|
||||
local files = {
|
||||
dirs[1] .. '/file',
|
||||
dirs[2] .. '/file',
|
||||
dirs[3] .. '/file',
|
||||
}
|
||||
|
||||
command('e ' .. files[3]) -- buffer 3
|
||||
command('e ' .. files[2]) -- buffer 2
|
||||
command('e ' .. files[1]) -- buffer 1
|
||||
|
||||
command('bcd ' .. dirs[1])
|
||||
command('b ' .. files[2]) -- Switch to buffer 2
|
||||
command('bcd ' .. dirs[2])
|
||||
command('b ' .. files[3]) -- Switch to buffer 3
|
||||
command('bcd ' .. dirs[3])
|
||||
|
||||
eq({ directory = dirs[3], scope = 'buffer', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[3], scope = 'buffer', changed_window = false }, eval('g:ev'))
|
||||
eq('buffer', eval('g:amatchpre'))
|
||||
eq('buffer', eval('g:amatch'))
|
||||
|
||||
eq(5, eval('g:cdprecount'))
|
||||
eq(5, eval('g:cdcount'))
|
||||
command('tabnew') -- tab 2 (tab-local CWD)
|
||||
eq(5, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(5, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tcd ' .. dirs[2])
|
||||
command('tabnext') -- tab 1 (no tab-local CWD)
|
||||
eq({ directory = dirs[3], scope = 'buffer', changed_window = true }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[3], scope = 'buffer', changed_window = true }, eval('g:ev'))
|
||||
eq('buffer', eval('g:amatchpre'))
|
||||
eq('buffer', eval('g:amatch'))
|
||||
command('tabnext') -- tab 2
|
||||
eq({ directory = dirs[2], scope = 'tabpage', changed_window = true }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[2], scope = 'tabpage', changed_window = true }, eval('g:ev'))
|
||||
eq('tabpage', eval('g:amatchpre'))
|
||||
eq('tabpage', eval('g:amatch'))
|
||||
eq(8, eval('g:cdprecount'))
|
||||
eq(8, eval('g:cdcount'))
|
||||
|
||||
command('tabnext') -- tab 1
|
||||
command('b ' .. files[2]) -- buffer 2
|
||||
eq(10, eval('g:cdprecount'))
|
||||
eq(10, eval('g:cdcount'))
|
||||
command('tabnext') -- tab 2 (has the *same* CWD)
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
|
||||
if is_os('win') then
|
||||
command('tabnew') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tcd ' .. dirs[2])
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 1
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 2
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 1
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('bcd ' .. dirs[2]) -- buffer 2
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 2
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabnext') -- tab 1
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command('tabprevious') -- tab 3
|
||||
eq(10, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(10, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
end
|
||||
end)
|
||||
|
||||
it('are triggered by switching to buf/win with different CWD', function()
|
||||
command('lcd ' .. dirs[3]) -- window 3
|
||||
command(('split %s/file'):format(dirs[2])) -- window 2
|
||||
command('lcd ' .. dirs[2])
|
||||
command(('split %s/file'):format(dirs[1])) -- window 1
|
||||
command('lcd ' .. dirs[1])
|
||||
|
||||
-- All windows have a window-local CWD
|
||||
|
||||
command('2wincmd w') -- window 2
|
||||
eq({ directory = dirs[2], scope = 'window', changed_window = true }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[2], scope = 'window', changed_window = true }, eval('g:ev'))
|
||||
eq('window', eval('g:amatchpre'))
|
||||
eq('window', eval('g:amatch'))
|
||||
|
||||
eq(4, eval('g:cdprecount'))
|
||||
eq(4, eval('g:cdcount'))
|
||||
command('bcd ' .. dirs[1]) -- window 2 now has a buffer with buffer-local CWD (and no window-local CWD)
|
||||
eq({ directory = dirs[1], scope = 'buffer', changed_window = false }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[1], scope = 'buffer', changed_window = false }, eval('g:ev'))
|
||||
eq('buffer', eval('g:amatchpre'))
|
||||
eq('buffer', eval('g:amatch'))
|
||||
eq(5, eval('g:cdprecount'))
|
||||
eq(5, eval('g:cdcount'))
|
||||
command('3wincmd w') -- window 3 (window-local CWD)
|
||||
eq(6, eval('g:cdprecount'))
|
||||
eq(6, eval('g:cdcount'))
|
||||
command('bcd ' .. dirs[2]) -- window 3 now has a buffer with buffer-local CWD (and no window-local CWD)
|
||||
command('1wincmd w') -- window 1 (window-local CWD)
|
||||
eq({ directory = dirs[1], scope = 'window', changed_window = true }, eval('g:evpre'))
|
||||
eq({ cwd = dirs[1], scope = 'window', changed_window = true }, eval('g:ev'))
|
||||
eq('window', eval('g:amatchpre'))
|
||||
eq('window', eval('g:amatch'))
|
||||
command('2wincmd w') -- window 2 (buffer-local CWD)
|
||||
eq({ directory = dirs[1], scope = 'window', changed_window = true }, eval('g:evpre')) -- buffer-local CWD
|
||||
eq({ cwd = dirs[1], scope = 'window', changed_window = true }, eval('g:ev'))
|
||||
eq('window', eval('g:amatchpre'))
|
||||
eq('window', eval('g:amatch'))
|
||||
eq(8, eval('g:cdprecount'))
|
||||
eq(8, eval('g:cdcount'))
|
||||
|
||||
command('1wincmd w') -- window 1 (window-local cwd)
|
||||
eq(8, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
|
||||
eq(8, eval('g:cdcount')) -- same CWD, no DirChanged event
|
||||
command(('b %s/file'):format(dirs[2])) -- buffer 2 (has buffer-local cwd)
|
||||
eq(8, eval('g:cdprecount')) -- no DirChangedPre event, window-local CWD has higher priority
|
||||
eq(8, eval('g:cdcount')) -- no DirChanged event, window-local CWD has higher priority
|
||||
end)
|
||||
|
||||
it('are triggered by nvim_set_current_dir()', function()
|
||||
request('nvim_set_current_dir', dirs[1])
|
||||
eq({ directory = dirs[1], scope = 'global', changed_window = false }, eval('g:evpre'))
|
||||
|
||||
@@ -18,9 +18,12 @@ local mkdir = t.mkdir
|
||||
local directories = {
|
||||
tab = 'Xtest-functional-ex_cmds-cd_spec.tab', -- Tab
|
||||
window = 'Xtest-functional-ex_cmds-cd_spec.window', -- Window
|
||||
buffer = 'Xtest-functional-ex_cmds-cd_spec.buffer', -- Buffer
|
||||
global = 'Xtest-functional-ex_cmds-cd_spec.global', -- New global
|
||||
}
|
||||
|
||||
local tmpfile = 'Xtest-functional-ex_cmds-cd_spec-tmpfile'
|
||||
|
||||
-- Shorthand writing to get the current working directory
|
||||
local cwd = function(...)
|
||||
return call('getcwd', ...)
|
||||
@@ -28,6 +31,9 @@ end -- effective working dir
|
||||
local wcwd = function()
|
||||
return cwd(0)
|
||||
end -- window dir
|
||||
local bcwd = function()
|
||||
return cwd(-1, -1, 0)
|
||||
end -- buffer dir
|
||||
local tcwd = function()
|
||||
return cwd(-1, 0)
|
||||
end -- tab dir
|
||||
@@ -39,6 +45,9 @@ end -- effective working dir
|
||||
local wlwd = function()
|
||||
return lwd(0)
|
||||
end -- window dir
|
||||
local blwd = function()
|
||||
return lwd(-1, -1, 0)
|
||||
end -- buffer dir
|
||||
local tlwd = function()
|
||||
return lwd(-1, 0)
|
||||
end -- tab dir
|
||||
@@ -134,6 +143,45 @@ for _, cmd in ipairs { 'cd', 'chdir' } do
|
||||
eq(globalDir .. pathsep .. directories.tab, cwd(-1, localtab))
|
||||
eq(1, lwd(-1, localtab))
|
||||
end)
|
||||
|
||||
it('for buffer', function()
|
||||
local globalDir = directories.start
|
||||
-- Create two buffers
|
||||
command(('e %s1'):format(tmpfile))
|
||||
command(('e %s%s%s2'):format(directories.buffer, pathsep, tmpfile))
|
||||
|
||||
-- Initially matches globalDir
|
||||
eq(globalDir, cwd())
|
||||
eq(0, lwd())
|
||||
|
||||
-- Change buffer-local directory to subdirectory
|
||||
command('bcd ' .. directories.buffer)
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
eq(1, blwd())
|
||||
|
||||
-- Verify Other buffer is unchanged
|
||||
command('b# ')
|
||||
eq(globalDir, cwd())
|
||||
eq(0, blwd())
|
||||
|
||||
-- A new buffer created with :edit inherits the buffer-local directory; editing an
|
||||
-- existing buffer keeps that buffer's own directory.
|
||||
command('b# ')
|
||||
command(('e %s3'):format(tmpfile))
|
||||
eq(1, blwd())
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
command(('e ..%s%s1'):format(pathsep, tmpfile))
|
||||
eq(0, blwd())
|
||||
eq(globalDir, cwd())
|
||||
|
||||
-- getcwd({winnr}) falls through to the buffer shown in that window, not the current
|
||||
-- buffer.
|
||||
command(('split %s%s%s2'):format(directories.buffer, pathsep, tmpfile))
|
||||
command('wincmd p')
|
||||
eq(globalDir, cwd())
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd(1))
|
||||
eq(globalDir, cwd(2))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('getcwd(-1, -1)', function()
|
||||
@@ -153,6 +201,16 @@ for _, cmd in ipairs { 'cd', 'chdir' } do
|
||||
eq(directories.start, cwd(-1, -1))
|
||||
eq(0, lwd(-1, -1))
|
||||
end)
|
||||
|
||||
it('works with buffer-local pwd', function()
|
||||
command(('silent b%s %s'):format(cmd, directories.buffer))
|
||||
eq(directories.start, cwd(-1, -1))
|
||||
eq(0, lwd(-1, -1))
|
||||
|
||||
-- Must behave the same if bufnr is -1
|
||||
eq(directories.start, cwd(-1, -1, -1))
|
||||
eq(0, lwd(-1, -1, -1))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('Local directory gets inherited', function()
|
||||
@@ -239,6 +297,137 @@ for _, cmd in ipairs { 'cd', 'chdir' } do
|
||||
eq(0, tlwd())
|
||||
eq(globalDir .. pathsep .. directories.window, wcwd())
|
||||
end)
|
||||
|
||||
it('works when mixing tab-local and buffer-local directories', function()
|
||||
local globalDir = directories.start
|
||||
|
||||
-- Create two buffers for testing. One in each tab
|
||||
command(('e %s1'):format(tmpfile))
|
||||
command('tabnew')
|
||||
command(('e %s2'):format(tmpfile))
|
||||
|
||||
-- Verify that buffer 2 has the same working directory
|
||||
eq(globalDir, cwd())
|
||||
eq(globalDir, tcwd()) -- Has no tab-local directory
|
||||
eq(0, tlwd())
|
||||
eq(globalDir, bcwd()) -- Has no buffer-local directory
|
||||
eq(0, blwd())
|
||||
|
||||
-- Change tab-local working directory and verify it is different
|
||||
command(('silent t%s %s'):format(cmd, directories.tab))
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.tab), cwd())
|
||||
eq(cwd(), tcwd()) -- Working directory matches tab directory
|
||||
eq(1, tlwd())
|
||||
eq(cwd(), bcwd()) -- Still no buffer-directory
|
||||
eq(0, blwd())
|
||||
|
||||
-- Change buffer 2's buffer-local directory
|
||||
command(('silent b%s ..%s%s'):format(cmd, pathsep, directories.buffer))
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.tab), tcwd())
|
||||
eq(cwd(), bcwd()) -- Has no buffer-directory
|
||||
eq(1, blwd())
|
||||
|
||||
-- Verify the first tab has no local-directory
|
||||
command('tabfirst')
|
||||
eq(0, tlwd())
|
||||
|
||||
-- Verify buffer 2 has buffer-local directory even in first tab
|
||||
command(('b %s2'):format(tmpfile)) -- Switch to buffer 2
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), bcwd())
|
||||
eq(0, tlwd()) -- Still no tab-local directory
|
||||
|
||||
-- Verify buffer 1 did not have its directory changed
|
||||
command('b#') -- Switch to buffer 1
|
||||
eq(globalDir, cwd())
|
||||
eq(0, blwd()) -- No window-buffer directory
|
||||
end)
|
||||
it('works when mixing window local and buffer local directories', function()
|
||||
local globalDir = directories.start
|
||||
-- Create a new window first and verify that is has the same working directory
|
||||
command('new')
|
||||
eq(globalDir, cwd())
|
||||
eq(globalDir, wcwd()) -- Has no window-local directory
|
||||
eq(0, tlwd())
|
||||
eq(globalDir, bcwd()) -- Has no buffer-local directory
|
||||
eq(0, blwd())
|
||||
|
||||
-- Create a buffer in current window
|
||||
command('e ' .. tmpfile)
|
||||
|
||||
-- Change buffer-local working directory and verify it is different
|
||||
command(('silent b%s %s'):format(cmd, directories.buffer))
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
eq(cwd(), bcwd()) -- Working directory matches buffer directory
|
||||
eq(1, blwd())
|
||||
eq(cwd(), wcwd()) -- Still no window-directory
|
||||
eq(0, wlwd())
|
||||
|
||||
-- Change window-local directory to test `:lcd`
|
||||
command(('silent l%s ../%s'):format(cmd, directories.window))
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.window), cwd())
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), bcwd())
|
||||
eq(1, blwd())
|
||||
|
||||
-- Verify buffer has buffer-local directory in original window
|
||||
command('wincmd w')
|
||||
command('b ' .. tmpfile)
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
|
||||
-- Verify going to second window uses window-local directory
|
||||
command('wincmd w')
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.window), cwd())
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
for _, cmd in ipairs { 'bcd', 'bchdir' } do
|
||||
describe(':' .. cmd, function()
|
||||
before_each(function()
|
||||
clear()
|
||||
for _, d in pairs(directories) do
|
||||
mkdir(d)
|
||||
end
|
||||
directories.start = cwd()
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
for _, d in pairs(directories) do
|
||||
vim.uv.fs_rmdir(d)
|
||||
end
|
||||
end)
|
||||
|
||||
it('works after deleting the only buffer', function()
|
||||
command(('%s %s'):format(cmd, directories.buffer))
|
||||
command('bd') -- delete buffer
|
||||
end)
|
||||
|
||||
it('makes :new use the buffer-local directory', function()
|
||||
local globalDir = directories.start
|
||||
command(('%s %s'):format(cmd, directories.buffer))
|
||||
|
||||
command(':new')
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
command('wincmd x') -- close :new window
|
||||
|
||||
command(':vnew')
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
command('wincmd x') -- close :vnew window
|
||||
|
||||
command(':enew')
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
end)
|
||||
|
||||
it('makes :enew use the buffer-local directory in a split', function()
|
||||
local globalDir = directories.start
|
||||
command(('%s %s'):format(cmd, directories.buffer))
|
||||
|
||||
command(':vsplit')
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
|
||||
command(':enew')
|
||||
eq(('%s%s%s'):format(globalDir, pathsep, directories.buffer), cwd())
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -272,14 +461,23 @@ for _, cmd in ipairs { 'getcwd', 'haslocaldir' } do
|
||||
eq(err474, pcall_err(command, 'call ' .. cmd .. '(-2)'))
|
||||
end)
|
||||
local err5001 = 'Vim(call):E5001: Higher scope cannot be -1 if lower scope is >= 0.'
|
||||
local err5006 = 'Vim(call):E5006: Window and tab scope must be -1 when using buffer scope'
|
||||
local err5007 = 'Vim(call):E5007: Cannot find buffer number.'
|
||||
it('fails on -1 if previous arg is >=0', function()
|
||||
eq(err5001, pcall_err(command, 'call ' .. cmd .. '(0, -1)'))
|
||||
end)
|
||||
it('fails when 1st and 2nd args != -1 when 3rd arg > -1', function()
|
||||
eq(err5006, pcall_err(command, ('call %s(0, 0, 0)'):format(cmd)))
|
||||
eq(err5006, pcall_err(command, ('call %s(1, 2, 3)'):format(cmd)))
|
||||
end)
|
||||
it('fails when passing a bufnr that does not exist', function()
|
||||
eq(err5007, pcall_err(command, ('call %s(-1, -1, 99999)'):format(cmd)))
|
||||
end)
|
||||
|
||||
-- Test wrong number of arguments
|
||||
local err118 = 'Vim(call):E118: Too many arguments for function: ' .. cmd
|
||||
it('fails to parse more than one argument', function()
|
||||
eq(err118, pcall_err(command, 'call ' .. cmd .. '(0, 0, 0)'))
|
||||
eq(err118, pcall_err(command, ('call %s(0, 0, 0, 0)'):format(cmd)))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -23,16 +23,19 @@ local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec'
|
||||
|
||||
describe(':mksession', function()
|
||||
local session_file = file_prefix .. '.vim'
|
||||
local tab_dir = file_prefix .. '.d'
|
||||
local tab_dir = file_prefix .. '.tab.d'
|
||||
local buf_dir = file_prefix .. '.buf.d'
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
mkdir(tab_dir)
|
||||
mkdir(buf_dir)
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
os.remove(session_file)
|
||||
rmdir(tab_dir)
|
||||
rmdir(buf_dir)
|
||||
end)
|
||||
|
||||
it('restores same :terminal buf in splits', function()
|
||||
@@ -362,4 +365,66 @@ describe(':mksession', function()
|
||||
-- contains all splits
|
||||
eq(3, #api.nvim_tabpage_list_wins(0))
|
||||
end)
|
||||
|
||||
it('restores buffer-local working directories', function()
|
||||
-- "'" and " " in the names must survive the session file's string quoting and escaping.
|
||||
local tmpfile_base = file_prefix .. "-tmp'file"
|
||||
local quote_dir = file_prefix .. ".b'uf d"
|
||||
local session_file2 = session_file .. '2'
|
||||
local cwd_dir = fn.getcwd()
|
||||
mkdir(quote_dir)
|
||||
finally(function()
|
||||
rmdir(quote_dir)
|
||||
os.remove(session_file2)
|
||||
end)
|
||||
|
||||
command(('edit %s1'):format(tmpfile_base))
|
||||
command(('edit %s2'):format(tmpfile_base))
|
||||
command('bcd ' .. fn.fnameescape(quote_dir))
|
||||
command('b# ')
|
||||
command('mksession ' .. session_file)
|
||||
command('set sessionoptions-=curdir')
|
||||
command('mksession ' .. session_file2)
|
||||
|
||||
-- Create a new test instance of Nvim.
|
||||
clear()
|
||||
|
||||
command('silent source ' .. session_file)
|
||||
command(('b %s%s2'):format(get_pathsep(), tmpfile_base))
|
||||
eq(('%s%s%s'):format(cwd_dir, get_pathsep(), quote_dir), fn.getcwd())
|
||||
|
||||
-- Without "curdir" in 'sessionoptions' no buffer-local directory is restored.
|
||||
clear()
|
||||
command('silent source ' .. session_file2)
|
||||
command(('b %s%s2'):format(get_pathsep(), tmpfile_base))
|
||||
eq(cwd_dir, fn.getcwd())
|
||||
eq(0, fn.haslocaldir(-1, -1, 0))
|
||||
end)
|
||||
|
||||
it('restores buffer-local working directories when used with tabs', function()
|
||||
local tmpfile_base = file_prefix .. '-tmpfile'
|
||||
local cwd_dir = fn.getcwd()
|
||||
|
||||
command(('edit %s1'):format(tmpfile_base))
|
||||
command('tabnew ')
|
||||
command(('edit %s2'):format(tmpfile_base))
|
||||
command('tcd ' .. tab_dir)
|
||||
command('bcd ..')
|
||||
command('bcd ' .. buf_dir)
|
||||
command('tabfirst')
|
||||
command('mksession ' .. session_file)
|
||||
|
||||
-- Create a new test instance of Nvim.
|
||||
clear()
|
||||
|
||||
command('source ' .. session_file)
|
||||
-- First tab should have the original working directory.
|
||||
command('tabnext 1')
|
||||
eq(cwd_dir, fn.getcwd())
|
||||
-- Second tab should have the tab-local working directory.
|
||||
command('tabnext 2')
|
||||
eq(('%s%s%s'):format(cwd_dir, get_pathsep(), tab_dir), fn.getcwd(-1, 0))
|
||||
-- Buffer in second tab should have buffer-local working directory
|
||||
eq(('%s%s%s'):format(cwd_dir, get_pathsep(), buf_dir), fn.getcwd(-1, -1, 0))
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user