aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.builds/freebsd.yml1
-rw-r--r--.builds/openbsd.yml1
-rw-r--r--runtime/doc/api.txt6
-rw-r--r--src/nvim/api/autocmd.c66
-rw-r--r--src/nvim/api/keysets.lua1
-rw-r--r--src/nvim/buffer.c92
-rw-r--r--src/nvim/buffer_defs.h9
-rw-r--r--src/nvim/decoration.c9
-rw-r--r--src/nvim/extmark.c4
-rw-r--r--src/nvim/sign.c16
-rw-r--r--src/nvim/testdir/test_autochdir.vim61
-rw-r--r--src/nvim/window.c3
-rw-r--r--src/nvim/window.h25
-rw-r--r--test/functional/api/autocmd_spec.lua56
-rw-r--r--test/functional/legacy/autochdir_spec.lua94
-rw-r--r--test/functional/lua/vim_spec.lua4
-rw-r--r--test/functional/options/autochdir_spec.lua31
17 files changed, 423 insertions, 56 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index 8596eb2a7f..508dcacd3c 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -12,6 +12,7 @@ packages:
- gettext
- python
- libffi
+- gdb
sources:
- https://github.com/neovim/neovim
diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml
index 422fa366b6..2f9ccdc1be 100644
--- a/.builds/openbsd.yml
+++ b/.builds/openbsd.yml
@@ -12,6 +12,7 @@ packages:
- libtool
- ninja-1.10.2p0
- unzip-6.0p14
+- gdb
sources:
- https://github.com/neovim/neovim
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index fe5f9eaf35..73536d174a 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -3226,7 +3226,11 @@ nvim_get_autocmds({*opts}) *nvim_get_autocmds()*
against
• group (string): Name of group to match against
• pattern: Pattern or list of patterns to match
- against
+ against. Cannot be used with {buffer}
+ • buffer: Buffer number or list of buffer numbers
+ for buffer local autocommands
+ |autocmd-buflocal|. Cannot be used with
+ {pattern}
Return: ~
A list of autocmds that match
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 0ad7b320d0..685667ac64 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -41,7 +41,9 @@ static int64_t next_autocmd_id = 1;
/// @param opts Optional Parameters:
/// - event : Name or list of name of events to match against
/// - group (string): Name of group to match against
-/// - pattern: Pattern or list of patterns to match against
+/// - pattern: Pattern or list of patterns to match against. Cannot be used with {buffer}
+/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+/// |autocmd-buflocal|. Cannot be used with {pattern}
///
/// @return A list of autocmds that match
Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
@@ -53,6 +55,8 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
char_u *pattern_filters[AUCMD_MAX_PATTERNS];
char_u pattern_buflocal[BUFLOCAL_PAT_LEN];
+ Array buffers = ARRAY_DICT_INIT;
+
bool event_set[NUM_EVENTS] = { false };
bool check_event = false;
@@ -100,6 +104,12 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
}
}
+ if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "Cannot use both 'pattern' and 'buffer'");
+ goto cleanup;
+ }
+
int pattern_filter_count = 0;
if (opts->pattern.type != kObjectTypeNil) {
Object v = opts->pattern;
@@ -107,25 +117,70 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
pattern_filters[pattern_filter_count] = (char_u *)v.data.string.data;
pattern_filter_count += 1;
} else if (v.type == kObjectTypeArray) {
+ if (v.data.array.size > AUCMD_MAX_PATTERNS) {
+ api_set_error(err, kErrorTypeValidation,
+ "Too many patterns. Please limit yourself to %d or fewer",
+ AUCMD_MAX_PATTERNS);
+ goto cleanup;
+ }
+
FOREACH_ITEM(v.data.array, item, {
+ if (item.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'pattern': must be a string");
+ goto cleanup;
+ }
+
pattern_filters[pattern_filter_count] = (char_u *)item.data.string.data;
pattern_filter_count += 1;
});
} else {
- api_set_error(err,
- kErrorTypeValidation,
+ api_set_error(err, kErrorTypeValidation,
"Not a valid 'pattern' value. Must be a string or an array");
goto cleanup;
}
+ }
+
+ if (opts->buffer.type == kObjectTypeInteger || opts->buffer.type == kObjectTypeBuffer) {
+ buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err);
+ if (ERROR_SET(err)) {
+ goto cleanup;
+ }
- if (pattern_filter_count >= AUCMD_MAX_PATTERNS) {
+ snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
+ ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal));
+ } else if (opts->buffer.type == kObjectTypeArray) {
+ if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) {
api_set_error(err,
kErrorTypeValidation,
- "Too many patterns. Please limit yourself to less");
+ "Too many buffers. Please limit yourself to %d or fewer", AUCMD_MAX_PATTERNS);
goto cleanup;
}
+
+ FOREACH_ITEM(opts->buffer.data.array, bufnr, {
+ if (bufnr.type != kObjectTypeInteger && bufnr.type != kObjectTypeBuffer) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'buffer': must be an integer");
+ goto cleanup;
+ }
+
+ buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err);
+ if (ERROR_SET(err)) {
+ goto cleanup;
+ }
+
+ snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
+ ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal));
+ });
+ } else if (opts->buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value for 'buffer': must be an integer or array of integers");
+ goto cleanup;
}
+ FOREACH_ITEM(buffers, bufnr, {
+ pattern_filters[pattern_filter_count] = (char_u *)bufnr.data.string.data;
+ pattern_filter_count += 1;
+ });
+
FOR_ALL_AUEVENTS(event) {
if (check_event && !event_set[event]) {
continue;
@@ -234,6 +289,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
}
cleanup:
+ api_free_array(buffers);
return autocmd_list;
}
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 32d4e98822..435e8195dd 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -142,6 +142,7 @@ return {
"event";
"group";
"pattern";
+ "buffer";
};
create_augroup = {
"clear";
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 084e18c6cb..2bd14e2103 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1737,7 +1737,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
buf = xcalloc(1, sizeof(buf_T));
// init b: variables
buf->b_vars = tv_dict_alloc();
- buf->b_signcols_valid = false;
+ buf->b_signcols.valid = false;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
@@ -5468,6 +5468,8 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
int linesum = 0;
linenr_T curline = 0;
+ buf->b_signcols.sentinel = 0;
+
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->se_lnum > curline) {
// Counted all signs, now add extmark signs
@@ -5475,13 +5477,14 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1,
maximum-linesum);
}
+ curline = sign->se_lnum;
if (linesum > signcols) {
signcols = linesum;
+ buf->b_signcols.sentinel = curline;
if (signcols >= maximum) {
return maximum;
}
}
- curline = sign->se_lnum;
linesum = 0;
}
if (sign->se_has_text_or_icon) {
@@ -5504,6 +5507,7 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
if (linesum > signcols) {
signcols = linesum;
+ buf->b_signcols.sentinel = curline;
if (signcols >= maximum) {
return maximum;
}
@@ -5512,28 +5516,96 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
return signcols;
}
+/// Invalidate the signcolumn if needed after deleting
+/// signs between line1 and line2 (inclusive).
+///
+/// @param buf buffer to check
+/// @param line1 start of region being deleted
+/// @param line2 end of region being deleted
+void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2)
+{
+ if (!buf->b_signcols.valid) {
+ return;
+ }
+
+ if (!buf->b_signcols.sentinel) {
+ buf->b_signcols.valid = false;
+ return;
+ }
+
+ linenr_T sent = buf->b_signcols.sentinel;
+
+ if (sent >= line1 && sent <= line2) {
+ // Only invalidate when removing signs at the sentinel line.
+ buf->b_signcols.valid = false;
+ }
+}
+
+/// Re-calculate the signcolumn after adding a sign.
+///
+/// @param buf buffer to check
+/// @param added sign being added
+void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
+{
+ if (!buf->b_signcols.valid) {
+ return;
+ }
+
+ if (!added || !buf->b_signcols.sentinel) {
+ buf->b_signcols.valid = false;
+ return;
+ }
+
+ if (added->se_lnum == buf->b_signcols.sentinel) {
+ if (buf->b_signcols.size == buf->b_signcols.max) {
+ buf->b_signcols.max++;
+ }
+ buf->b_signcols.size++;
+ return;
+ }
+
+ sign_entry_T *s;
+
+ // Get first sign for added lnum
+ for (s = added; s->se_prev && s->se_lnum == s->se_prev->se_lnum; s = s->se_prev) {}
+
+ // Count signs for lnum
+ int linesum = 1;
+ for (; s->se_next && s->se_lnum == s->se_next->se_lnum; s = s->se_next) {
+ linesum++;
+ }
+ linesum += decor_signcols(buf, &decor_state, (int)s->se_lnum-1, (int)s->se_lnum-1,
+ SIGN_SHOW_MAX-linesum);
+
+ if (linesum > buf->b_signcols.size) {
+ buf->b_signcols.size = linesum;
+ buf->b_signcols.max = linesum;
+ buf->b_signcols.sentinel = added->se_lnum;
+ }
+}
+
int buf_signcols(buf_T *buf, int maximum)
{
// The maximum can be determined from 'signcolumn' which is window scoped so
// need to invalidate signcols if the maximum is greater than the previous
// maximum.
- if (maximum > buf->b_signcols_max) {
- buf->b_signcols_valid = false;
+ if (maximum > buf->b_signcols.max) {
+ buf->b_signcols.valid = false;
}
- if (!buf->b_signcols_valid) {
+ if (!buf->b_signcols.valid) {
int signcols = buf_signcols_inner(buf, maximum);
// Check if we need to redraw
- if (signcols != buf->b_signcols) {
- buf->b_signcols = signcols;
- buf->b_signcols_max = maximum;
+ if (signcols != buf->b_signcols.size) {
+ buf->b_signcols.size = signcols;
+ buf->b_signcols.max = maximum;
redraw_buf_later(buf, NOT_VALID);
}
- buf->b_signcols_valid = true;
+ buf->b_signcols.valid = true;
}
- return buf->b_signcols;
+ return buf->b_signcols.size;
}
// Get "buf->b_fname", use "[No Name]" if it is NULL.
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index c7afa5f9d0..7acb646980 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -862,9 +862,12 @@ struct file_buffer {
// may use a different synblock_T.
sign_entry_T *b_signlist; // list of placed signs
- int b_signcols; // last calculated number of sign columns
- bool b_signcols_valid; // calculated sign columns is valid
- int b_signcols_max; // Maximum value b_signcols is valid for.
+ struct {
+ int size; // last calculated number of sign columns
+ bool valid; // calculated sign columns is valid
+ linenr_T sentinel; // a line number which is holding up the signcolumn
+ int max; // Maximum value size is valid for.
+ } b_signcols;
Terminal *terminal; // Terminal instance associated with the buffer
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 6c006b7fe0..619e8fdadc 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -1,6 +1,7 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+#include "nvim/buffer.h"
#include "nvim/decoration.h"
#include "nvim/extmark.h"
#include "nvim/highlight.h"
@@ -67,11 +68,6 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
{
if (row2 >= row1) {
- if (decor && decor->sign_text) {
- buf->b_signcols_valid = false;
- changed_line_abv_curs();
- }
-
if (!decor || decor->hl_id || decor_has_sign(decor)) {
redraw_buf_range_later(buf, row1+1, row2+1);
}
@@ -99,6 +95,9 @@ void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
assert(buf->b_signs > 0);
buf->b_signs--;
}
+ if (row2 >= row && decor->sign_text) {
+ buf_signcols_del_check(buf, row+1, row2+1);
+ }
}
decor_free(decor);
}
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index e1f1ed7d13..2916e3e978 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -147,6 +147,10 @@ revised:
if (decor_has_sign(decor)) {
buf->b_signs++;
}
+ if (decor->sign_text) {
+ // TODO(lewis6991): smarter invalidation
+ buf_signcols_add_check(buf, NULL);
+ }
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
}
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index ad48003f3b..6d0ac30003 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -196,7 +196,8 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int
if (next != NULL) {
next->se_prev = newsign;
}
- buf->b_signcols_valid = false;
+
+ buf_signcols_add_check(buf, newsign);
if (prev == NULL) {
// When adding first sign need to redraw the windows to create the
@@ -541,7 +542,6 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
sign_entry_T *next; // the next sign in a b_signlist
linenr_T lnum; // line number whose sign was deleted
- buf->b_signcols_valid = false;
lastp = &buf->b_signlist;
lnum = 0;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
@@ -554,6 +554,7 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
next->se_prev = sign->se_prev;
}
lnum = sign->se_lnum;
+ buf_signcols_del_check(buf, lnum, lnum);
if (sign->se_group != NULL) {
sign_group_unref(sign->se_group->sg_name);
}
@@ -675,7 +676,7 @@ void buf_delete_signs(buf_T *buf, char_u *group)
lastp = &sign->se_next;
}
}
- buf->b_signcols_valid = false;
+ buf_signcols_del_check(buf, 1, MAXLNUM);
}
/// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
@@ -737,14 +738,19 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
int is_fixed = 0;
int signcol = win_signcol_configured(curwin, &is_fixed);
- curbuf->b_signcols_valid = false;
+ bool delete = amount == MAXLNUM;
+
+ if (delete) {
+ buf_signcols_del_check(curbuf, line1, line2);
+ }
+
lastp = &curbuf->b_signlist;
for (sign = curbuf->b_signlist; sign != NULL; sign = next) {
next = sign->se_next;
new_lnum = sign->se_lnum;
if (sign->se_lnum >= line1 && sign->se_lnum <= line2) {
- if (amount != MAXLNUM) {
+ if (!delete) {
new_lnum += amount;
} else if (!is_fixed || signcol >= 2) {
*lastp = next;
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
index 53ed4617f7..4229095f9f 100644
--- a/src/nvim/testdir/test_autochdir.vim
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -26,6 +26,54 @@ func Test_set_filename()
call delete('samples/Xtest')
endfunc
+func Test_set_filename_other_window()
+ CheckFunction test_autochdir
+ let cwd = getcwd()
+ call test_autochdir()
+ call mkdir('Xa')
+ call mkdir('Xb')
+ call mkdir('Xc')
+ try
+ args Xa/aaa.txt Xb/bbb.txt
+ set acd
+ let winid = win_getid()
+ snext
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt')
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ finally
+ set noacd
+ call chdir(cwd)
+ call delete('Xa', 'rf')
+ call delete('Xb', 'rf')
+ call delete('Xc', 'rf')
+ bwipe! aaa.txt
+ bwipe! bbb.txt
+ bwipe! ccc.txt
+ endtry
+endfunc
+
+func Test_acd_win_execute()
+ CheckFunction test_autochdir
+ let cwd = getcwd()
+ set acd
+ call test_autochdir()
+
+ call mkdir('Xfile')
+ let winid = win_getid()
+ new Xfile/file
+ call assert_match('testdir.Xfile$', getcwd())
+ cd ..
+ call assert_match('testdir$', getcwd())
+ call win_execute(winid, 'echo')
+ call assert_match('testdir$', getcwd())
+
+ bwipe!
+ set noacd
+ call chdir(cwd)
+ call delete('Xfile', 'rf')
+endfunc
+
func Test_verbose_pwd()
CheckFunction test_autochdir
let cwd = getcwd()
@@ -42,20 +90,27 @@ func Test_verbose_pwd()
set acd
wincmd w
call assert_match('\[autochdir\].*testdir$', execute('verbose pwd'))
- execute 'lcd' cwd
- call assert_match('\[window\].*testdir$', execute('verbose pwd'))
execute 'tcd' cwd
call assert_match('\[tabpage\].*testdir$', execute('verbose pwd'))
execute 'cd' cwd
call assert_match('\[global\].*testdir$', execute('verbose pwd'))
+ execute 'lcd' cwd
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
edit
call assert_match('\[autochdir\].*testdir$', execute('verbose pwd'))
+ enew
+ wincmd w
+ call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
+ wincmd w
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
wincmd w
call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
set noacd
call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
wincmd w
- call assert_match('\[global\].*testdir', execute('verbose pwd'))
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
+ execute 'cd' cwd
+ call assert_match('\[global\].*testdir$', execute('verbose pwd'))
wincmd w
call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd'))
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 83048d911f..d5299202b0 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -6763,9 +6763,6 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display)
curwin = switchwin->sw_curwin;
curbuf = curwin->w_buffer;
}
- // If called by win_execute() and executing the command changed the
- // directory, it now has to be restored.
- fix_current_dir();
}
/// Make "buf" the current buffer.
diff --git a/src/nvim/window.h b/src/nvim/window.h
index e2fd2c515d..42701f72b4 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -5,6 +5,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/mark.h"
+#include "nvim/os/os.h"
// Values for file_name_in_line()
#define FNAME_MESS 1 // give error message
@@ -47,12 +48,36 @@ typedef struct {
do { \
win_T *const wp_ = (wp); \
const pos_T curpos_ = wp_->w_cursor; \
+ char_u cwd_[MAXPATHL]; \
+ char_u autocwd_[MAXPATHL]; \
+ bool apply_acd_ = false; \
+ int cwd_status_ = FAIL; \
+ /* Getting and setting directory can be slow on some systems, only do */ \
+ /* this when the current or target window/tab have a local directory or */ \
+ /* 'acd' is set. */ \
+ if (curwin != wp \
+ && (curwin->w_localdir != NULL || wp->w_localdir != NULL \
+ || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \
+ || p_acd)) { \
+ cwd_status_ = os_dirname(cwd_, MAXPATHL); \
+ } \
+ /* If 'acd' is set, check we are using that directory. If yes, then */ \
+ /* apply 'acd' afterwards, otherwise restore the current directory. */ \
+ if (cwd_status_ == OK && p_acd) { \
+ do_autochdir(); \
+ apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && STRCMP(cwd_, autocwd_) == 0; \
+ } \
switchwin_T switchwin_; \
if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \
check_cursor(); \
block; \
} \
restore_win_noblock(&switchwin_, true); \
+ if (apply_acd_) { \
+ do_autochdir(); \
+ } else if (cwd_status_ == OK) { \
+ os_chdir((char *)cwd_); \
+ } \
/* Update the status line if the cursor moved. */ \
if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \
wp_->w_redr_status = true; \
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua
index e50e812b4b..e8dc284925 100644
--- a/test/functional/api/autocmd_spec.lua
+++ b/test/functional/api/autocmd_spec.lua
@@ -8,6 +8,7 @@ local exec_lua = helpers.exec_lua
local matches = helpers.matches
local meths = helpers.meths
local source = helpers.source
+local pcall_err = helpers.pcall_err
before_each(clear)
@@ -225,6 +226,61 @@ describe('autocmd api', function()
local aus = meths.get_autocmds { event = "InsertEnter" }
eq({ { buflocal = false, command = ':echo "1"', event = "InsertEnter", once = false, pattern = "*" } }, aus)
end)
+
+ it('should work with buffer numbers', function()
+ command [[new]]
+ command [[au! InsertEnter]]
+ command [[au InsertEnter <buffer=1> :echo "1"]]
+ command [[au InsertEnter <buffer=2> :echo "2"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter", buffer = 0 }
+ eq({{
+ buffer = 2,
+ buflocal = true,
+ command = ':echo "2"',
+ event = 'InsertEnter',
+ once = false,
+ pattern = '<buffer=2>',
+ }}, aus)
+
+ aus = meths.get_autocmds { event = "InsertEnter", buffer = 1 }
+ eq({{
+ buffer = 1,
+ buflocal = true,
+ command = ':echo "1"',
+ event = "InsertEnter",
+ once = false,
+ pattern = "<buffer=1>",
+ }}, aus)
+
+ aus = meths.get_autocmds { event = "InsertEnter", buffer = { 1, 2 } }
+ eq({{
+ buffer = 1,
+ buflocal = true,
+ command = ':echo "1"',
+ event = "InsertEnter",
+ once = false,
+ pattern = "<buffer=1>",
+ }, {
+ buffer = 2,
+ buflocal = true,
+ command = ':echo "2"',
+ event = "InsertEnter",
+ once = false,
+ pattern = "<buffer=2>",
+ }}, aus)
+
+ eq("Invalid value for 'buffer': must be an integer or array of integers", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" }))
+ eq("Invalid value for 'buffer': must be an integer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } }))
+ eq("Invalid buffer id: 42", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { 42 } }))
+
+ local bufs = {}
+ for _ = 1, 257 do
+ table.insert(bufs, meths.create_buf(true, false))
+ end
+
+ eq("Too many buffers. Please limit yourself to 256 or fewer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs }))
+ end)
end)
describe('groups', function()
diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua
index 37a94476a0..13cb6cd287 100644
--- a/test/functional/legacy/autochdir_spec.lua
+++ b/test/functional/legacy/autochdir_spec.lua
@@ -1,8 +1,12 @@
local lfs = require('lfs')
local helpers = require('test.functional.helpers')(after_each)
local clear, eq, matches = helpers.clear, helpers.eq, helpers.matches
-local eval, command, call = helpers.eval, helpers.command, helpers.call
-local exec_capture = helpers.exec_capture
+local eval, command, call, meths = helpers.eval, helpers.command, helpers.call, helpers.meths
+local source, exec_capture = helpers.source, helpers.exec_capture
+
+local function expected_empty()
+ eq({}, meths.get_vvar('errors'))
+end
describe('autochdir behavior', function()
local dir = 'Xtest_functional_legacy_autochdir'
@@ -10,19 +14,66 @@ describe('autochdir behavior', function()
before_each(function()
lfs.mkdir(dir)
clear()
+ command('set shellslash')
end)
after_each(function()
helpers.rmdir(dir)
end)
- -- Tests vim/vim/777 without test_autochdir().
+ -- Tests vim/vim#777 without test_autochdir().
it('sets filename', function()
command('set acd')
command('new')
command('w '..dir..'/Xtest')
eq('Xtest', eval("expand('%')"))
- eq(dir, eval([[substitute(getcwd(), '.*[/\\]\(\k*\)', '\1', '')]]))
+ eq(dir, eval([[substitute(getcwd(), '.*/\(\k*\)', '\1', '')]]))
+ end)
+
+ it(':file in win_execute() does not cause wrong directory', function()
+ command('cd '..dir)
+ source([[
+ func Test_set_filename_other_window()
+ let cwd = getcwd()
+ call mkdir('Xa')
+ call mkdir('Xb')
+ call mkdir('Xc')
+ try
+ args Xa/aaa.txt Xb/bbb.txt
+ set acd
+ let winid = win_getid()
+ snext
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt')
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ finally
+ set noacd
+ call chdir(cwd)
+ call delete('Xa', 'rf')
+ call delete('Xb', 'rf')
+ call delete('Xc', 'rf')
+ bwipe! aaa.txt
+ bwipe! bbb.txt
+ bwipe! ccc.txt
+ endtry
+ endfunc
+ ]])
+ call('Test_set_filename_other_window')
+ expected_empty()
+ end)
+
+ it('win_execute() does not change directory', function()
+ local subdir = 'Xfile'
+ command('cd '..dir)
+ command('set acd')
+ call('mkdir', subdir)
+ local winid = eval('win_getid()')
+ command('new '..subdir..'/file')
+ matches(dir..'/'..subdir..'$', eval('getcwd()'))
+ command('cd ..')
+ matches(dir..'$', eval('getcwd()'))
+ call('win_execute', winid, 'echo')
+ matches(dir..'$', eval('getcwd()'))
end)
it(':verbose pwd shows whether autochdir is used', function()
@@ -30,29 +81,36 @@ describe('autochdir behavior', function()
command('cd '..dir)
local cwd = eval('getcwd()')
command('edit global.txt')
- matches('%[global%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[global%].*'..dir..'$', exec_capture('verbose pwd'))
call('mkdir', subdir)
command('split '..subdir..'/local.txt')
command('lcd '..subdir)
- matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
- command('set autochdir')
+ matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
+ command('set acd')
command('wincmd w')
- matches('%[autochdir%].*'..dir, exec_capture('verbose pwd'))
- command('lcd '..cwd)
- matches('%[window%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd'))
command('tcd '..cwd)
- matches('%[tabpage%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[tabpage%].*'..dir..'$', exec_capture('verbose pwd'))
command('cd '..cwd)
- matches('%[global%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[global%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('lcd '..cwd)
+ matches('%[window%].*'..dir..'$', exec_capture('verbose pwd'))
command('edit')
- matches('%[autochdir%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('enew')
command('wincmd w')
- matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
- command('set noautochdir')
- matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
+ matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
command('wincmd w')
- matches('%[global%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[window%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('wincmd w')
+ matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
+ command('set noacd')
+ matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
+ command('wincmd w')
+ matches('%[window%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('cd '..cwd)
+ matches('%[global%].*'..dir..'$', exec_capture('verbose pwd'))
command('wincmd w')
- matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
+ matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
end)
end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index e66e08d9d0..3dacb7a114 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -1,5 +1,6 @@
-- Test suite for testing interactions with API bindings
local helpers = require('test.functional.helpers')(after_each)
+local isCI = require('test.helpers').isCI
local Screen = require('test.functional.ui.screen')
local funcs = helpers.funcs
@@ -2557,6 +2558,9 @@ describe('lua: builtin modules', function()
it('does not work when disabled without runtime', function()
+ if isCI('sourcehut') then
+ pending('causes a core dump')
+ end
clear{args={'--luamod-dev'}, env={VIMRUNTIME='fixtures/a'}}
-- error checking could be better here. just check that --luamod-dev
-- does anything at all by breaking with missing runtime..
diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua
index 2fce0a5ed9..74959a8e76 100644
--- a/test/functional/options/autochdir_spec.lua
+++ b/test/functional/options/autochdir_spec.lua
@@ -1,7 +1,9 @@
+local lfs = require('lfs')
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
-local getcwd = helpers.funcs.getcwd
+local funcs = helpers.funcs
+local command = helpers.command
describe("'autochdir'", function()
it('given on the shell gets processed properly', function()
@@ -9,11 +11,34 @@ describe("'autochdir'", function()
-- By default 'autochdir' is off, thus getcwd() returns the repo root.
clear(targetdir..'/tty-test.c')
- local rootdir = getcwd()
+ local rootdir = funcs.getcwd()
local expected = rootdir .. '/' .. targetdir
-- With 'autochdir' on, we should get the directory of tty-test.c.
clear('--cmd', 'set autochdir', targetdir..'/tty-test.c')
- eq(helpers.iswin() and expected:gsub('/', '\\') or expected, getcwd())
+ eq(helpers.iswin() and expected:gsub('/', '\\') or expected, funcs.getcwd())
+ end)
+
+ it('is not overwritten by getwinvar() call #17609',function()
+ local curdir = string.gsub(lfs.currentdir(), '\\', '/')
+ local dir_a = curdir..'/Xtest-functional-options-autochdir.dir_a'
+ local dir_b = curdir..'/Xtest-functional-options-autochdir.dir_b'
+ lfs.mkdir(dir_a)
+ lfs.mkdir(dir_b)
+ clear()
+ command('set shellslash')
+ command('set autochdir')
+ command('edit '..dir_a..'/file1')
+ eq(dir_a, funcs.getcwd())
+ command('lcd '..dir_b)
+ eq(dir_b, funcs.getcwd())
+ command('botright vnew ../file2')
+ eq(curdir, funcs.getcwd())
+ command('wincmd w')
+ eq(dir_a, funcs.getcwd())
+ funcs.getwinvar(2, 'foo')
+ eq(dir_a, funcs.getcwd())
+ helpers.rmdir(dir_a)
+ helpers.rmdir(dir_b)
end)
end)