Merge pull request #14532 from janlazo/vim-8.1.1433

vim-patch:8.0.1394,8.1.1967,8.2.{23,1000,1059,2354,2419,2433,2473,2850}
This commit is contained in:
Jan Edmund Lazo 2021-05-14 18:50:44 -04:00 committed by GitHub
commit c57a85d534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 339 additions and 75 deletions

View File

@ -477,7 +477,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
goto end;
}
inserted_bytes += STRLEN(lines[i]) + 1;
inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
// Mark lines that haven't been passed to the buffer as they need
// to be freed later
lines[i] = NULL;
@ -497,7 +497,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
goto end;
}
inserted_bytes += STRLEN(lines[i]) + 1;
inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
// Same as with replacing, but we also need to free lines
xfree(lines[i]);

View File

@ -407,7 +407,8 @@ bool buf_valid(buf_T *buf)
/// there to be only one window with this buffer. e.g. when
/// ":quit" is supposed to close the window but autocommands
/// close all other windows.
void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
/// @returns true when we got to the end and b_nwindows was decremented.
bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
{
bool unload_buf = (action != 0);
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
@ -444,7 +445,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
// halfway a command that relies on it). Unloading is allowed.
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
EMSG(_("E937: Attempt to delete a buffer that is in use"));
return;
return false;
}
if (win != NULL // Avoid bogus clang warning.
@ -471,13 +472,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
EMSG(_(e_auabort));
return;
return false;
}
buf->b_locked--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
EMSG(_(e_auabort));
return;
return false;
}
// When the buffer becomes hidden, but is not unloaded, trigger
@ -488,17 +489,17 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
EMSG(_(e_auabort));
return;
return false;
}
buf->b_locked--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
EMSG(_(e_auabort));
return;
return false;
}
}
if (aborting()) { // autocmds may abort script processing
return;
return false;
}
}
@ -525,7 +526,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
/* Return when a window is displaying the buffer or when it's not
* unloaded. */
if (buf->b_nwindows > 0 || !unload_buf) {
return;
return false;
}
if (buf->terminal) {
@ -561,11 +562,11 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
if (!bufref_valid(&bufref)) {
// Autocommands may have deleted the buffer.
return;
return false;
}
if (aborting()) {
// Autocmds may abort script processing.
return;
return false;
}
/*
@ -576,7 +577,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
* deleted buffer.
*/
if (buf == curbuf && !is_curbuf) {
return;
return false;
}
if (win != NULL // Avoid bogus clang warning.
@ -636,6 +637,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf->b_p_bl = false;
}
}
// NOTE: at this point "curbuf" may be invalid!
return true;
}
/// Make buffer not contain a file.

View File

@ -3150,9 +3150,7 @@ static void ins_compl_clear(void)
XFREE_CLEAR(compl_orig_text);
compl_enter_selects = false;
// clear v:completed_item
dict_T *const d = tv_dict_alloc();
d->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_COMPLETED_ITEM, d);
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
}
/// Check that Insert completion is active.
@ -4497,9 +4495,7 @@ static void ins_compl_delete(void)
// causes flicker, thus we can't do that.
changed_cline_bef_curs();
// clear v:completed_item
dict_T *const d = tv_dict_alloc();
d->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_COMPLETED_ITEM, d);
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
}
// Insert the new text being completed.
@ -4520,8 +4516,7 @@ static void ins_compl_insert(int in_compl_func)
static dict_T *ins_compl_dict_alloc(compl_T *match)
{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc();
dict->dv_lock = VAR_FIXED;
dict_T *dict = tv_dict_alloc_lock(VAR_FIXED);
tv_dict_add_str(
dict, S_LEN("word"),
(const char *)EMPTY_IF_NULL(match->cp_str));

View File

@ -377,11 +377,9 @@ void eval_init(void)
msgpack_types_dict->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc());
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
dict_T *v_event = tv_dict_alloc();
v_event->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_EVENT, v_event);
set_vim_var_dict(VV_EVENT, tv_dict_alloc_lock(VAR_FIXED));
set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown));
set_vim_var_nr(VV_STDERR, CHAN_STDERR);
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
@ -7617,7 +7615,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
/// @param[out] ret_fnum Set to fnum for marks.
///
/// @return Pointer to position or NULL in case of error (e.g. invalid type).
pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum,
int *const ret_fnum)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{

View File

@ -217,7 +217,7 @@ return {
len={args=1},
libcall={args=3},
libcallnr={args=3},
line={args=1},
line={args={1, 2}},
line2byte={args=1},
lispindent={args=1},
list2str={args={1, 2}},

View File

@ -5540,18 +5540,36 @@ static void f_libcallnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
libcall_common(argvars, rettv, VAR_NUMBER);
}
/*
* "line(string)" function
*/
// "line(string, [winid])" function
static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum = 0;
pos_T *fp;
pos_T *fp = NULL;
int fnum;
fp = var2fpos(&argvars[0], TRUE, &fnum);
if (fp != NULL)
if (argvars[1].v_type != VAR_UNKNOWN) {
tabpage_T *tp;
win_T *save_curwin;
tabpage_T *save_curtab;
// use window specified in the second argument
win_T *wp = win_id2wp_tp(&argvars[1], &tp);
if (wp != NULL && tp != NULL) {
if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true)
== OK) {
check_cursor();
fp = var2fpos(&argvars[0], true, &fnum);
}
restore_win_noblock(save_curwin, save_curtab, true);
}
} else {
// use current window
fp = var2fpos(&argvars[0], true, &fnum);
}
if (fp != NULL) {
lnum = fp->lnum;
}
rettv->vval.v_number = lnum;
}

View File

@ -2106,6 +2106,13 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len)
return l;
}
dict_T *tv_dict_alloc_lock(VarLockStatus lock)
{
dict_T *const d = tv_dict_alloc();
d->dv_lock = lock;
return d;
}
/// Allocate an empty dictionary for a return value
///
/// Also sets reference count.
@ -2114,9 +2121,8 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len)
void tv_dict_alloc_ret(typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
dict_T *const d = tv_dict_alloc();
dict_T *const d = tv_dict_alloc_lock(VAR_UNLOCKED);
tv_dict_set_ret(ret_tv, d);
ret_tv->v_lock = VAR_UNLOCKED;
}
//{{{3 Clear

View File

@ -2434,21 +2434,25 @@ int do_ecmd(
* is returned by buflist_new(), nothing to do here.
*/
if (buf != curbuf) {
/*
* Be careful: The autocommands may delete any buffer and change
* the current buffer.
* - If the buffer we are going to edit is deleted, give up.
* - If the current buffer is deleted, prefer to load the new
* buffer when loading a buffer is required. This avoids
* loading another buffer which then must be closed again.
* - If we ended up in the new buffer already, need to skip a few
* things, set auto_buf.
*/
const int save_cmdwin_type = cmdwin_type;
// BufLeave applies to the old buffer.
cmdwin_type = 0;
// Be careful: The autocommands may delete any buffer and change
// the current buffer.
// - If the buffer we are going to edit is deleted, give up.
// - If the current buffer is deleted, prefer to load the new
// buffer when loading a buffer is required. This avoids
// loading another buffer which then must be closed again.
// - If we ended up in the new buffer already, need to skip a few
// things, set auto_buf.
if (buf->b_fname != NULL) {
new_name = vim_strsave(buf->b_fname);
}
set_bufref(&au_new_curbuf, buf);
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
cmdwin_type = save_cmdwin_type;
if (!bufref_valid(&au_new_curbuf)) {
// New buffer has been deleted.
delbuf_msg(new_name); // Frees new_name.
@ -2462,6 +2466,7 @@ int do_ecmd(
auto_buf = true;
} else {
win_T *the_curwin = curwin;
buf_T *was_curbuf = curbuf;
// Set w_closing to avoid that autocommands close the window.
// Set b_locked for the same reason.
@ -2475,9 +2480,10 @@ int do_ecmd(
// Close the link to the current buffer. This will set
// oldwin->w_buffer to NULL.
u_sync(false);
close_buffer(oldwin, curbuf,
(flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
false);
const bool did_decrement = close_buffer(
oldwin, curbuf,
(flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
false);
// Autocommands may have closed the window.
if (win_valid(the_curwin)) {
@ -2497,6 +2503,14 @@ int do_ecmd(
goto theend;
}
if (buf == curbuf) { // already in new buffer
// close_buffer() has decremented the window count,
// increment it again here and restore w_buffer.
if (did_decrement && buf_valid(was_curbuf)) {
was_curbuf->b_nwindows++;
}
if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL) {
oldwin->w_buffer = was_curbuf;
}
auto_buf = true;
} else {
// <VN> We could instead free the synblock

View File

@ -212,7 +212,7 @@ void do_exmode(int improved)
while (exmode_active) {
/* Check for a ":normal" command and no more characters left. */
if (ex_normal_busy > 0 && typebuf.tb_len == 0) {
exmode_active = FALSE;
exmode_active = 0;
break;
}
msg_scroll = true;
@ -6520,6 +6520,12 @@ ex_win_close(
int need_hide;
buf_T *buf = win->w_buffer;
// Never close the autocommand window.
if (win == aucmd_win) {
EMSG(_(e_autocmd_close));
return;
}
need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
if (need_hide && !buf_hide(buf) && !forceit) {
if ((p_confirm || cmdmod.confirm) && p_write) {
@ -6589,9 +6595,6 @@ static void ex_tabonly(exarg_T *eap)
// Repeat this up to a 1000 times, because autocommands may
// mess up the lists.
for (int done = 0; done < 1000; done++) {
FOR_ALL_TAB_WINDOWS(tp, wp) {
assert(wp != aucmd_win);
}
FOR_ALL_TABS(tp) {
if (tp->tp_topframe != topframe) {
tabpage_close_other(tp, eap->forceit);
@ -7304,7 +7307,8 @@ do_exedit(
*/
if (exmode_active && (eap->cmdidx == CMD_visual
|| eap->cmdidx == CMD_view)) {
exmode_active = FALSE;
exmode_active = 0;
ex_pressedreturn = false;
if (*eap->arg == NUL) {
/* Special case: ":global/pat/visual\NLvi-commands" */
if (global_busy) {

View File

@ -6635,11 +6635,13 @@ static int open_cmdwin(void)
wp = curwin;
set_bufref(&bufref, curbuf);
win_goto(old_curwin);
win_close(wp, true);
if (win_valid(wp) && wp != curwin) {
win_close(wp, true);
}
// win_close() may have already wiped the buffer when 'bh' is
// set to 'wipe'.
if (bufref_valid(&bufref)) {
// set to 'wipe', autocommands may have closed other windows
if (bufref_valid(&bufref) && bufref.br_buf != curbuf) {
close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false);
}

View File

@ -987,6 +987,8 @@ EXTERN char_u e_dirnotf[] INIT(= N_(
"E919: Directory not found in '%s': \"%s\""));
EXTERN char_u e_au_recursive[] INIT(= N_(
"E952: Autocommand caused recursive behavior"));
EXTERN char_u e_autocmd_close[] INIT(= N_(
"E813: Cannot close autocmd window"));
EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String"));

View File

@ -1143,7 +1143,6 @@ static int find_tagfunc_tags(
typval_T args[4];
typval_T rettv;
char_u flagString[4];
dict_T *d;
taggy_T *tag = &curwin->w_tagstack[curwin->w_tagstackidx];
if (*curbuf->b_p_tfu == NUL) {
@ -1156,7 +1155,7 @@ static int find_tagfunc_tags(
args[1].vval.v_string = flagString;
// create 'info' dict argument
d = tv_dict_alloc();
dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
if (tag->user_data != NULL) {
tv_dict_add_str(d, S_LEN("user_data"), (const char *)tag->user_data);
}

View File

@ -10,6 +10,7 @@ source test_cursor_func.vim
source test_ex_equal.vim
source test_ex_undo.vim
source test_ex_z.vim
source test_ex_mode.vim
source test_execute_func.vim
source test_expand_func.vim
source test_feedkeys.vim

View File

@ -190,7 +190,6 @@ func Test_autocmd_bufunload_avoiding_SEGV_02()
normal! i1
call assert_fails('edit a.txt', 'E517:')
call feedkeys("\<CR>")
autocmd! test_autocmd_bufunload
augroup! test_autocmd_bufunload
@ -452,6 +451,27 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
endfor
endfunc
" Using :blast and :ball for many events caused a crash, because b_nwindows was
" not incremented correctly.
func Test_autocmd_blast_badd()
let content =<< trim [CODE]
au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast
edit foo1
au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball
edit foo2
call writefile(['OK'], 'Xerrors')
qall
[CODE]
call writefile(content, 'XblastBall')
call system(GetVimCommand() .. ' --clean -S XblastBall')
" call assert_match('OK', readfile('Xerrors')->join())
call assert_match('OK', join(readfile('Xerrors')))
call delete('XblastBall')
call delete('Xerrors')
endfunc
" SEGV occurs in older versions.
func Test_autocmd_bufwipe_in_SessLoadPost2()
tabnew
@ -1949,6 +1969,26 @@ func Test_autocmd_window()
%bw!
endfunc
" Test for trying to close the tab that has the temporary window for exeucing
" an autocmd.
func Test_close_autocmd_tab()
edit one.txt
tabnew two.txt
augroup aucmd_win_test
au!
au BufEnter * if expand('<afile>') == 'one.txt' | tabfirst | tabonly | endif
augroup END
call assert_fails('doautoall BufEnter', 'E813:')
tabonly
augroup aucmd_win_test
au!
augroup END
augroup! aucmd_win_test
%bwipe!
endfunc
func Test_autocmd_closes_window()
au BufNew,BufWinLeave * e %e
file yyy
@ -1960,4 +2000,13 @@ func Test_autocmd_closes_window()
au! BufWinLeave
endfunc
func Test_autocmd_closing_cmdwin()
au BufWinLeave * nested q
call assert_fails("norm 7q?\n", 'E855:')
au! BufWinLeave
new
only
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -477,7 +477,7 @@ func Test_expand_star_star()
call delete('a', 'rf')
endfunc
func Test_paste_in_cmdline()
func Test_cmdline_paste()
let @a = "def"
call feedkeys(":abc \<C-R>a ghi\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc def ghi', @:)
@ -517,18 +517,38 @@ func Test_paste_in_cmdline()
bwipe!
endfunc
func Test_remove_char_in_cmdline()
call feedkeys(":abc def\<S-Left>\<Del>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc ef', @:)
func Test_cmdline_remove_char()
let encoding_save = &encoding
call feedkeys(":abc def\<S-Left>\<BS>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abcdef', @:)
" for e in ['utf8', 'latin1']
for e in ['utf8']
exe 'set encoding=' . e
call feedkeys(":abc def ghi\<S-Left>\<C-W>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc ghi', @:)
call feedkeys(":abc def\<S-Left>\<Del>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc ef', @:, e)
call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx')
call assert_equal('"def', @:)
call feedkeys(":abc def\<S-Left>\<BS>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abcdef', @:)
call feedkeys(":abc def ghi\<S-Left>\<C-W>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc ghi', @:, e)
call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx')
call assert_equal('"def', @:, e)
endfor
let &encoding = encoding_save
endfunc
func Test_cmdline_keymap_ctrl_hat()
if !has('keymap')
return
endif
set keymap=esperanto
call feedkeys(":\"Jxauxdo \<C-^>Jxauxdo \<C-^>Jxauxdo\<CR>", 'tx')
call assert_equal('"Jxauxdo Ĵaŭdo Jxauxdo', @:)
set keymap=
endfunc
func Test_illegal_address1()
@ -863,20 +883,20 @@ func Test_cmdline_overstrike()
" Test overstrike in the middle of the command line.
call feedkeys(":\"01234\<home>\<right>\<right>ab\<right>\<insert>cd\<enter>", 'xt')
call assert_equal('"0ab1cd4', @:)
call assert_equal('"0ab1cd4', @:, e)
" Test overstrike going beyond end of command line.
call feedkeys(":\"01234\<home>\<right>\<right>ab\<right>\<insert>cdefgh\<enter>", 'xt')
call assert_equal('"0ab1cdefgh', @:)
call assert_equal('"0ab1cdefgh', @:, e)
" Test toggling insert/overstrike a few times.
call feedkeys(":\"01234\<home>\<right>ab\<right>\<insert>cd\<right>\<insert>ef\<enter>", 'xt')
call assert_equal('"ab0cd3ef4', @:)
call assert_equal('"ab0cd3ef4', @:, e)
endfor
" Test overstrike with multi-byte characters.
call feedkeys(":\"テキストエディタ\<home>\<right>\<right>ab\<right>\<insert>cd\<enter>", 'xt')
call assert_equal('"テabキcdエディタ', @:)
call assert_equal('"テabキcdエディタ', @:, e)
let &encoding = encoding_save
endfunc
@ -985,6 +1005,25 @@ func Test_buffers_lastused()
bwipeout bufc
endfunc
" Test for CmdwinEnter autocmd
func Test_cmdwin_autocmd()
CheckFeature cmdwin
augroup CmdWin
au!
autocmd BufLeave * if &buftype == '' | update | endif
autocmd CmdwinEnter * startinsert
augroup END
call assert_fails('call feedkeys("q:xyz\<CR>", "xt")', 'E492:')
call assert_equal('xyz', @:)
augroup CmdWin
au!
augroup END
augroup! CmdWin
endfunc
func Test_cmdlineclear_tabenter()
" See test/functional/legacy/cmdline_spec.lua
CheckScreendump
@ -1033,4 +1072,52 @@ func Test_read_shellcmd()
endif
endfunc
" Test for recalling newer or older cmdline from history with <Up>, <Down>,
" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <C-p>, or <C-n>.
func Test_recalling_cmdline()
CheckFeature cmdline_hist
let g:cmdlines = []
cnoremap <Plug>(save-cmdline) <Cmd>let g:cmdlines += [getcmdline()]<CR>
let histories = [
\ {'name': 'cmd', 'enter': ':', 'exit': "\<Esc>"},
\ {'name': 'search', 'enter': '/', 'exit': "\<Esc>"},
\ {'name': 'expr', 'enter': ":\<C-r>=", 'exit': "\<Esc>\<Esc>"},
\ {'name': 'input', 'enter': ":call input('')\<CR>", 'exit': "\<CR>"},
"\ TODO: {'name': 'debug', ...}
\]
let keypairs = [
\ {'older': "\<Up>", 'newer': "\<Down>", 'prefixmatch': v:true},
\ {'older': "\<S-Up>", 'newer': "\<S-Down>", 'prefixmatch': v:false},
\ {'older': "\<PageUp>", 'newer': "\<PageDown>", 'prefixmatch': v:false},
\ {'older': "\<C-p>", 'newer': "\<C-n>", 'prefixmatch': v:false},
\]
let prefix = 'vi'
for h in histories
call histadd(h.name, 'vim')
call histadd(h.name, 'virtue')
call histadd(h.name, 'Virgo')
call histadd(h.name, 'vogue')
call histadd(h.name, 'emacs')
for k in keypairs
let g:cmdlines = []
let keyseqs = h.enter
\ .. prefix
\ .. repeat(k.older .. "\<Plug>(save-cmdline)", 2)
\ .. repeat(k.newer .. "\<Plug>(save-cmdline)", 2)
\ .. h.exit
call feedkeys(keyseqs, 'xt')
call histdel(h.name, -1) " delete the history added by feedkeys above
let expect = k.prefixmatch
\ ? ['virtue', 'vim', 'virtue', prefix]
\ : ['emacs', 'vogue', 'emacs', prefix]
call assert_equal(expect, g:cmdlines)
endfor
endfor
unlet g:cmdlines
cunmap <Plug>(save-cmdline)
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -0,0 +1,82 @@
" Test editing line in Ex mode (see :help Q and :help gQ).
" Helper function to test editing line in Q Ex mode
func Ex_Q(cmd)
" Is there a simpler way to test editing Ex line?
call feedkeys("Q"
\ .. "let s:test_ex =<< END\<CR>"
\ .. a:cmd .. "\<CR>"
\ .. "END\<CR>"
\ .. "visual\<CR>", 'tx')
return s:test_ex[0]
endfunc
" Helper function to test editing line in gQ Ex mode
func Ex_gQ(cmd)
call feedkeys("gQ" .. a:cmd .. "\<C-b>\"\<CR>", 'tx')
let ret = @:[1:] " Remove leading quote.
call feedkeys("visual\<CR>", 'tx')
return ret
endfunc
" Helper function to test editing line with both Q and gQ Ex mode.
func Ex(cmd)
return [Ex_Q(a:cmd), Ex_gQ(a:cmd)]
endfunc
" Test editing line in Ex mode (both Q and gQ)
func Test_ex_mode()
throw 'skipped: TODO: '
let encoding_save = &encoding
set sw=2
" for e in ['utf8', 'latin1']
for e in ['utf8']
exe 'set encoding=' . e
call assert_equal(['bar', 'bar'], Ex("foo bar\<C-u>bar"), e)
call assert_equal(["1\<C-u>2", "1\<C-u>2"], Ex("1\<C-v>\<C-u>2"), e)
call assert_equal(["1\<C-b>2\<C-e>3", '213'], Ex("1\<C-b>2\<C-e>3"), e)
call assert_equal(['0123', '2013'], Ex("01\<Home>2\<End>3"), e)
call assert_equal(['0123', '0213'], Ex("01\<Left>2\<Right>3"), e)
call assert_equal(['01234', '0342'], Ex("012\<Left>\<Left>\<Insert>3\<Insert>4"), e)
call assert_equal(["foo bar\<C-w>", 'foo '], Ex("foo bar\<C-w>"), e)
call assert_equal(['foo', 'foo'], Ex("fooba\<Del>\<Del>"), e)
call assert_equal(["foo\tbar", 'foobar'], Ex("foo\<Tab>bar"), e)
call assert_equal(["abbrev\t", 'abbreviate'], Ex("abbrev\<Tab>"), e)
call assert_equal([' 1', "1\<C-t>\<C-t>"], Ex("1\<C-t>\<C-t>"), e)
call assert_equal([' 1', "1\<C-t>\<C-t>"], Ex("1\<C-t>\<C-t>\<C-d>"), e)
call assert_equal([' foo', ' foo'], Ex(" foo\<C-d>"), e)
call assert_equal(['foo', ' foo0'], Ex(" foo0\<C-d>"), e)
call assert_equal(['foo', ' foo^'], Ex(" foo^\<C-d>"), e)
endfor
set sw&
let &encoding = encoding_save
endfunc
func Test_ex_mode_errors()
" Not allowed to enter ex mode when text is locked
au InsertCharPre <buffer> normal! gQ<CR>
let caught_e523 = 0
try
call feedkeys("ix\<esc>", 'xt')
catch /^Vim\%((\a\+)\)\=:E523/ " catch E523
let caught_e523 = 1
endtry
call assert_equal(1, caught_e523)
au! InsertCharPre
new
au CmdLineEnter * call ExEnterFunc()
func ExEnterFunc()
endfunc
call feedkeys("gQvi\r", 'xt')
au! CmdLineEnter
delfunc ExEnterFunc
quit
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -2286,7 +2286,7 @@ int win_close(win_T *win, bool free_buf)
return FAIL; // window is already being closed
}
if (win == aucmd_win) {
EMSG(_("E813: Cannot close autocmd window"));
EMSG(_(e_autocmd_close));
return FAIL;
}
if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) {

View File

@ -73,6 +73,7 @@ describe('NULL', function()
null_expr_test('does not crash col()', 'col(L)', 0, 0)
null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0)
null_expr_test('does not crash line()', 'line(L)', 0, 0)
null_expr_test('does not crash line() with window id', 'line(L, 1000)', 0, 0)
null_expr_test('does not crash count()', 'count(L, 1)', 0, 0)
null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1)
null_expr_test('does not crash map()', 'map(L, "v:val")', 0, {})

View File

@ -64,10 +64,13 @@ describe('float window', function()
it('win_execute() should work' , function()
local buf = meths.create_buf(false, false)
meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'})
meths.buf_set_lines(buf, 0, -1, true, {'the floatwin', 'abc', 'def'})
local win = meths.open_win(buf, false, {relative='win', width=16, height=1, row=0, col=10})
local line = funcs.win_execute(win, 'echo getline(1)')
eq('\nthe floatwin', line)
eq('\n1', funcs.win_execute(win, 'echo line(".",'..win.id..')'))
eq('\n3', funcs.win_execute(win, 'echo line("$",'..win.id..')'))
eq('\n0', funcs.win_execute(win, 'echo line("$", 123456)'))
funcs.win_execute(win, 'bwipe!')
end)