Compare commits

...

14 Commits

Author SHA1 Message Date
Bram Moolenaar
1dcf55d4f1 patch 8.2.2195: failing tests for :const
Problem:    Failing tests for :const.
Solution:   Add missing check for ASSIGN_FINAL.
2020-12-22 22:07:30 +01:00
Bram Moolenaar
89b474dd4f patch 8.2.2194: Vim9: cannot use :const or :final at the script level
Problem:    Vim9: cannot use :const or :final at the script level.
Solution:   Support using :const and :final. (closes #7526)
2020-12-22 21:19:39 +01:00
Bram Moolenaar
3bdc90b7df patch 8.2.2193: Vim9: can change constant in :def function
Problem:    Vim9: can change constant in :def function.
Solution:   Check if a variable is locked. (issue #7526)
2020-12-22 20:35:40 +01:00
Bram Moolenaar
e5492609b3 patch 8.2.2192: Codecov on github actions fails
Problem:    Codecov on github actions fails.
Solution:   Revert to codecov script. (Ozaki Kiichi, closes #7529)
2020-12-22 19:05:33 +01:00
Bram Moolenaar
58a52f215a patch 8.2.2191: Vim9: using wrong name with lambda in nested function
Problem:    Vim9: using wrong name with lambda in nested function.
Solution:   Copy the lambda name earlier. (closes #7525)
2020-12-22 18:56:55 +01:00
Bram Moolenaar
077a42318c patch 8.2.2190: Vim9: crash when compiled with EXITFREE
Problem:    Vim9: crash when compiled with EXITFREE.
Solution:   Check that df_ufunc is not NULL.
2020-12-22 18:33:27 +01:00
Bram Moolenaar
032a2d050b patch 8.2.2189: cannot repeat a command that uses the small delete register
Problem:    Cannot repeat a command that uses the small delete register.
Solution:   Store the register name instead of the contents. (Christian
            Brabandt, closes #7527)
2020-12-22 17:59:35 +01:00
Bram Moolenaar
cd45ed03bf patch 8.2.2188: Vim9: crash when calling global function from :def function
Problem:    Vim9: crash when calling global function from :def function.
Solution:   Set the outer context.  Define the partial for the context on the
            original function. Use a refcount to keep track of which ufunc is
            using a dfunc. (closes #7525)
2020-12-22 17:35:54 +01:00
Bram Moolenaar
07761a3b96 patch 8.2.2187: Python 3 test fails sometimes
Problem:    Python 3 test fails sometimes. (Christian Brabandt)
Solution:   Accept two SystemError messages.
2020-12-22 12:50:10 +01:00
Bram Moolenaar
5b3d1bb0f5 patch 8.2.2186: Vim9: error when using 'opfunc'
Problem:    Vim9: error when using 'opfunc'.
Solution:   Do not expect a return value from 'opfunc'. (closes #7510)
2020-12-22 12:20:08 +01:00
Bram Moolenaar
c882e4d169 Add missing change to version.c 2020-12-22 12:18:11 +01:00
Bram Moolenaar
1cfb9bb5c0 patch 8.2.2185: BufUnload is not triggered for the quickfix dummy buffer
Problem:    BufUnload is not triggered for the quickfix dummy buffer.
Solution:   Do trigger BufUnload. (Pontus Leitzler,closes #7518, closes #7517)
            Fix white space around "=".
2020-12-22 11:40:45 +01:00
Bram Moolenaar
9a963377b4 patch 8.2.2184: Vim9: no error when using "2" for a line number
Problem:    Vim9: no error when using "2" for a line number.
Solution:   Give an error message if the line number is invalid. (closes #7492)
2020-12-21 21:58:46 +01:00
Bram Moolenaar
60f63100b9 patch 8.2.2183: Vim9: value of 'edcompatible' and 'gdefault' are used
Problem:    Vim9: value of 'edcompatible' and 'gdefault' are used.
Solution:   Ignore these deprecated options in Vim9 script. (closes #7508)
2020-12-21 20:32:43 +01:00
30 changed files with 566 additions and 248 deletions

View File

@@ -214,11 +214,9 @@ jobs:
- name: Codecov
if: matrix.coverage && success()
uses: codecov/codecov-action@v1
with:
flags: ${{ matrix.features }}-${{ matrix.compiler }}-${{ matrix.extra }}
fail_ci_if_error: true
working-directory: ${{ env.SRCDIR }}
run: |
cd "${SRCDIR}"
bash <(curl -s https://codecov.io/bash) -F "${{ matrix.features }}-${{ matrix.compiler }}-${{ matrix.extra }}"
- name: ASan logs
if: contains(matrix.extra, 'asan') && !cancelled()

View File

@@ -9,7 +9,7 @@ SRC_ALL = \
.lgtm.yml \
.travis.yml \
.cirrus.yml \
.github/workflows/ci-windows.yaml \
.github/workflows/ci.yml \
.github/workflows/codeql-analysis.yml \
.github/CODEOWNERS \
appveyor.yml \

View File

@@ -654,11 +654,28 @@ call_func_retnr(
return retval;
}
/*
* Call Vim script function like call_func_retnr() and drop the result.
* Returns FAIL when calling the function fails.
*/
int
call_func_noret(
char_u *func,
int argc,
typval_T *argv)
{
typval_T rettv;
if (call_vim_function(func, argc, argv, &rettv) == FAIL)
return FAIL;
clear_tv(&rettv);
return OK;
}
/*
* Call Vim script function "func" and return the result as a string.
* Uses "argv" and "argc" as call_func_retnr().
* Returns NULL when calling the function fails.
* Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] should
* have type VAR_UNKNOWN.
*/
void *
call_func_retstr(
@@ -679,8 +696,7 @@ call_func_retstr(
/*
* Call Vim script function "func" and return the result as a List.
* Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] should
* have type VAR_UNKNOWN.
* Uses "argv" and "argc" as call_func_retnr().
* Returns NULL when there is something wrong.
*/
void *
@@ -1318,7 +1334,7 @@ set_var_lval(
{
typval_T tv;
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
{
emsg(_(e_cannot_mod));
*endp = cc;
@@ -1356,7 +1372,7 @@ set_var_lval(
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
{
emsg(_("E996: Cannot lock a range"));
return;
@@ -1415,7 +1431,7 @@ set_var_lval(
/*
* Assign to a List or Dictionary item.
*/
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
{
emsg(_("E996: Cannot lock a list or dict"));
return;

View File

@@ -2615,6 +2615,8 @@ f_cursor(typval_T *argvars, typval_T *rettv)
else
{
line = tv_get_lnum(argvars);
if (line < 0)
semsg(_(e_invarg2), tv_get_string(&argvars[0]));
col = (long)tv_get_number_chk(&argvars[1], NULL);
if (argvars[2].v_type != VAR_UNKNOWN)
coladd = (long)tv_get_number_chk(&argvars[2], NULL);

View File

@@ -738,25 +738,27 @@ ex_let(exarg_T *eap)
int first = TRUE;
int concat;
int has_assign;
int flags = eap->cmdidx == CMD_const ? ASSIGN_CONST : 0;
int flags = 0;
int vim9script = in_vim9script();
if (eap->cmdidx == CMD_final && !vim9script)
{
// In legacy Vim script ":final" is short for ":finally".
ex_finally(eap);
return;
// In legacy Vim script ":final" is short for ":finally".
ex_finally(eap);
return;
}
if (eap->cmdidx == CMD_let && vim9script)
{
emsg(_(e_cannot_use_let_in_vim9_script));
return;
}
if (eap->cmdidx == CMD_const && !vim9script && !eap->forceit)
// In legacy Vim script ":const" works like ":final".
eap->cmdidx = CMD_final;
// detect Vim9 assignment without ":let" or ":const"
if (eap->cmdidx == CMD_const)
flags |= ASSIGN_CONST;
else if (eap->cmdidx == CMD_final)
flags |= ASSIGN_FINAL;
// Vim9 assignment without ":let", ":const" or ":final"
if (eap->arg == eap->cmd)
flags |= ASSIGN_NO_DECL;
@@ -909,7 +911,7 @@ ex_let_vars(
int copy, // copy values from "tv", don't move
int semicolon, // from skip_var_list()
int var_count, // from skip_var_list()
int flags, // ASSIGN_CONST, ASSIGN_NO_DECL
int flags, // ASSIGN_FINAL, ASSIGN_CONST, ASSIGN_NO_DECL
char_u *op)
{
char_u *arg = arg_start;
@@ -1264,7 +1266,7 @@ ex_let_one(
char_u *arg, // points to variable name
typval_T *tv, // value to assign to variable
int copy, // copy value from "tv"
int flags, // ASSIGN_CONST, ASSIGN_NO_DECL
int flags, // ASSIGN_CONST, ASSIGN_FINAL, ASSIGN_NO_DECL
char_u *endchars, // valid chars after variable name or NULL
char_u *op) // "+", "-", "." or NULL
{
@@ -1277,6 +1279,7 @@ ex_let_one(
char_u *tofree = NULL;
if (in_vim9script() && (flags & ASSIGN_NO_DECL) == 0
&& (flags & (ASSIGN_CONST | ASSIGN_FINAL)) == 0
&& vim_strchr((char_u *)"$@&", *arg) != NULL)
{
vim9_declare_error(arg);
@@ -1286,7 +1289,7 @@ ex_let_one(
// ":let $VAR = expr": Set environment variable.
if (*arg == '$')
{
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
{
emsg(_("E996: Cannot lock an environment variable"));
return NULL;
@@ -1338,7 +1341,7 @@ ex_let_one(
// ":let &g:option = expr": Set global option value.
else if (*arg == '&')
{
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
{
emsg(_(e_const_option));
return NULL;
@@ -1422,7 +1425,7 @@ ex_let_one(
// ":let @r = expr": Set register contents.
else if (*arg == '@')
{
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
{
emsg(_("E996: Cannot lock a register"));
return NULL;
@@ -3056,7 +3059,7 @@ set_var_const(
type_T *type,
typval_T *tv_arg,
int copy, // make copy of value in "tv"
int flags) // ASSIGN_CONST, ASSIGN_NO_DECL
int flags) // ASSIGN_CONST, ASSIGN_FINAL, ASSIGN_NO_DECL
{
typval_T *tv = tv_arg;
typval_T bool_tv;
@@ -3077,6 +3080,7 @@ set_var_const(
if (vim9script
&& !is_script_local
&& (flags & ASSIGN_NO_DECL) == 0
&& (flags & (ASSIGN_CONST | ASSIGN_FINAL)) == 0
&& name[1] == ':')
{
vim9_declare_error(name);
@@ -3106,7 +3110,7 @@ set_var_const(
{
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
{
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
{
emsg(_(e_cannot_mod));
goto failed;
@@ -3125,13 +3129,7 @@ set_var_const(
goto failed;
}
// Check in this order for backwards compatibility:
// - Whether the variable is read-only
// - Whether the variable value is locked
// - Whether the variable is locked
if (var_check_ro(di->di_flags, name, FALSE)
|| value_check_lock(di->di_tv.v_lock, name, FALSE)
|| var_check_lock(di->di_flags, name, FALSE))
if (var_check_permission(di, name) == FAIL)
goto failed;
}
else
@@ -3212,7 +3210,7 @@ set_var_const(
goto failed;
}
di->di_flags = DI_FLAGS_ALLOC;
if (flags & ASSIGN_CONST)
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
di->di_flags |= DI_FLAGS_LOCK;
// A Vim9 script-local variable is also added to sn_all_vars and
@@ -3230,7 +3228,8 @@ set_var_const(
init_tv(tv);
}
// ":const var = val" locks the value
// ":const var = value" locks the value
// ":final var = value" locks "var"
if (flags & ASSIGN_CONST)
// Like :lockvar! name: lock the value and what it contains, but only
// if the reference count is up to one. That locks only literal
@@ -3242,6 +3241,22 @@ failed:
clear_tv(tv_arg);
}
/*
* Check in this order for backwards compatibility:
* - Whether the variable is read-only
* - Whether the variable value is locked
* - Whether the variable is locked
*/
int
var_check_permission(dictitem_T *di, char_u *name)
{
if (var_check_ro(di->di_flags, name, FALSE)
|| value_check_lock(di->di_tv.v_lock, name, FALSE)
|| var_check_lock(di->di_flags, name, FALSE))
return FAIL;
return OK;
}
/*
* Return TRUE if di_flags "flags" indicates variable "name" is read-only.
* Also give an error message.

View File

@@ -3778,6 +3778,15 @@ ex_substitute(exarg_T *eap)
++cmd;
else
{
#ifdef FEAT_EVAL
if (in_vim9script())
{
// ignore 'gdefault' and 'edcompatible'
subflags.do_all = FALSE;
subflags.do_ask = FALSE;
}
else
#endif
if (!p_ed)
{
if (p_gd) // default is global on

View File

@@ -9,7 +9,7 @@
/*
* ops.c: implementation of various operators: op_shift, op_delete, op_tilde,
* op_change, op_yank, do_put, do_join
* op_change, op_yank, do_join
*/
#include "vim.h"
@@ -3299,7 +3299,7 @@ op_function(oparg_T *oap UNUSED)
// function.
virtual_op = MAYBE;
(void)call_func_retnr(p_opfunc, 1, argv);
(void)call_func_noret(p_opfunc, 1, argv);
virtual_op = save_virtual_op;
if (cmdmod.cmod_flags & CMOD_LOCKMARKS)

View File

@@ -18,6 +18,7 @@ varnumber_T eval_to_number(char_u *expr);
typval_T *eval_expr(char_u *arg, exarg_T *eap);
int call_vim_function(char_u *func, int argc, typval_T *argv, typval_T *rettv);
varnumber_T call_func_retnr(char_u *func, int argc, typval_T *argv);
int call_func_noret(char_u *func, int argc, typval_T *argv);
void *call_func_retstr(char_u *func, int argc, typval_T *argv);
void *call_func_retlist(char_u *func, int argc, typval_T *argv);
int eval_foldexpr(char_u *arg, int *cp);

View File

@@ -70,6 +70,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val);
void delete_var(hashtab_T *ht, hashitem_T *hi);
void set_var(char_u *name, typval_T *tv, int copy);
void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags);
int var_check_permission(dictitem_T *di, char_u *name);
int var_check_ro(int flags, char_u *name, int use_gettext);
int var_check_lock(int flags, char_u *name, int use_gettext);
int var_check_fixed(int flags, char_u *name, int use_gettext);

View File

@@ -13,7 +13,7 @@ ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx);
ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
int func_is_global(ufunc_T *ufunc);
int func_name_refcount(char_u *name);
ufunc_T *copy_func(char_u *lambda, char_u *global);
int copy_func(char_u *lambda, char_u *global, ectx_T *ectx);
int funcdepth_increment(void);
void funcdepth_decrement(void);
int funcdepth_get(void);

View File

@@ -18,7 +18,7 @@ int check_vim9_unlet(char_u *name);
int compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx);
void set_function_type(ufunc_T *ufunc);
void delete_instr(isn_T *isn);
void clear_def_function(ufunc_T *ufunc);
void unlink_def_function(ufunc_T *ufunc);
void link_def_function(ufunc_T *ufunc);
void free_def_functions(void);
/* vim: set ft=c : */

View File

@@ -1,6 +1,7 @@
/* vim9execute.c */
void to_string_error(vartype_T vartype);
void funcstack_check_refcount(funcstack_T *funcstack);
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
void ex_disassemble(exarg_T *eap);
int tv2bool(typval_T *tv);

View File

@@ -6513,7 +6513,7 @@ wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
enter_cleanup(&cs);
#endif
wipe_buffer(buf, FALSE);
wipe_buffer(buf, TRUE);
#if defined(FEAT_EVAL)
// Restore the error/interrupt/exception state if not discarded by a

View File

@@ -809,7 +809,14 @@ insert_reg(
{
for (i = 0; i < y_current->y_size; ++i)
{
stuffescaped(y_current->y_array[i], literally);
if (regname == '-')
{
AppendCharToRedobuff(Ctrl_R);
AppendCharToRedobuff(regname);
do_put(regname, NULL, BACKWARD, 1L, PUT_CURSEND);
}
else
stuffescaped(y_current->y_array[i], literally);
// Insert a newline between lines and after last line if
// y_type is MLINE.
if (y_current->y_type == MLINE || i < y_current->y_size - 1)

View File

@@ -1363,6 +1363,7 @@ typedef struct jsonq_S jsonq_T;
typedef struct cbq_S cbq_T;
typedef struct channel_S channel_T;
typedef struct cctx_S cctx_T;
typedef struct ectx_S ectx_T;
typedef enum
{

View File

@@ -132,19 +132,19 @@ func Test_bufunload()
autocmd BufWipeout * call add(s:li, "bufwipeout")
augroup END
let s:li=[]
let s:li = []
new
setlocal bufhidden=
bunload
call assert_equal(["bufunload", "bufdelete"], s:li)
let s:li=[]
let s:li = []
new
setlocal bufhidden=delete
bunload
call assert_equal(["bufunload", "bufdelete"], s:li)
let s:li=[]
let s:li = []
new
setlocal bufhidden=unload
bwipeout
@@ -221,6 +221,29 @@ func Test_autocmd_bufunload_avoiding_SEGV_02()
bwipe! a.txt
endfunc
func Test_autocmd_dummy_wipeout()
" prepare files
call writefile([''], 'Xdummywipetest1.txt')
call writefile([''], 'Xdummywipetest2.txt')
augroup test_bufunload_group
autocmd!
autocmd BufUnload * call add(s:li, "bufunload")
autocmd BufDelete * call add(s:li, "bufdelete")
autocmd BufWipeout * call add(s:li, "bufwipeout")
augroup END
let s:li = []
split Xdummywipetest1.txt
silent! vimgrep /notmatched/ Xdummywipetest*
call assert_equal(["bufunload", "bufwipeout"], s:li)
bwipeout
call delete('Xdummywipetest1.txt')
call delete('Xdummywipetest2.txt')
au! test_bufunload_group
augroup! test_bufunload_group
endfunc
func Test_win_tab_autocmd()
let g:record = []
@@ -454,7 +477,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
let content =<< trim [CODE]
call test_override('ui_delay', 10)
set nocp noswapfile
let v:swapchoice="e"
let v:swapchoice = "e"
augroup test_autocmd_sessionload
autocmd!
autocmd SessionLoadPost * exe bufnr("Xsomething") . "bw!"
@@ -542,43 +565,43 @@ func Test_OptionSet()
au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>"))
" 1: Setting number option"
let g:options=[['number', 0, 0, 0, 1, 'global', 'set']]
let g:options = [['number', 0, 0, 0, 1, 'global', 'set']]
set nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 2: Setting local number option"
let g:options=[['number', 1, 1, '', 0, 'local', 'setlocal']]
let g:options = [['number', 1, 1, '', 0, 'local', 'setlocal']]
setlocal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 3: Setting global number option"
let g:options=[['number', 1, '', 1, 0, 'global', 'setglobal']]
let g:options = [['number', 1, '', 1, 0, 'global', 'setglobal']]
setglobal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 4: Setting local autoindent option"
let g:options=[['autoindent', 0, 0, '', 1, 'local', 'setlocal']]
let g:options = [['autoindent', 0, 0, '', 1, 'local', 'setlocal']]
setlocal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 5: Setting global autoindent option"
let g:options=[['autoindent', 0, '', 0, 1, 'global', 'setglobal']]
let g:options = [['autoindent', 0, '', 0, 1, 'global', 'setglobal']]
setglobal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 6: Setting global autoindent option"
let g:options=[['autoindent', 1, 1, 1, 0, 'global', 'set']]
let g:options = [['autoindent', 1, 1, 1, 0, 'global', 'set']]
set ai!
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 6a: Setting global autoindent option"
let g:options=[['autoindent', 1, 1, 0, 0, 'global', 'set']]
let g:options = [['autoindent', 1, 1, 0, 0, 'global', 'set']]
noa setlocal ai
noa setglobal noai
set ai!
@@ -587,55 +610,55 @@ func Test_OptionSet()
" Should not print anything, use :noa
" 7: don't trigger OptionSet"
let g:options=[['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']]
let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']]
noa set nonu
call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 8: Setting several global list and number option"
let g:options=[['list', 0, 0, 0, 1, 'global', 'set'], ['number', 0, 0, 0, 1, 'global', 'set']]
let g:options = [['list', 0, 0, 0, 1, 'global', 'set'], ['number', 0, 0, 0, 1, 'global', 'set']]
set list nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 9: don't trigger OptionSet"
let g:options=[['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']]
let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']]
noa set nolist nonu
call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 10: Setting global acd"
let g:options=[['autochdir', 0, 0, '', 1, 'local', 'setlocal']]
let g:options = [['autochdir', 0, 0, '', 1, 'local', 'setlocal']]
setlocal acd
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 11: Setting global autoread (also sets local value)"
let g:options=[['autoread', 0, 0, 0, 1, 'global', 'set']]
let g:options = [['autoread', 0, 0, 0, 1, 'global', 'set']]
set ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 12: Setting local autoread"
let g:options=[['autoread', 1, 1, '', 1, 'local', 'setlocal']]
let g:options = [['autoread', 1, 1, '', 1, 'local', 'setlocal']]
setlocal ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 13: Setting global autoread"
let g:options=[['autoread', 1, '', 1, 0, 'global', 'setglobal']]
let g:options = [['autoread', 1, '', 1, 0, 'global', 'setglobal']]
setglobal invar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 14: Setting option backspace through :let"
let g:options=[['backspace', '', '', '', 'eol,indent,start', 'global', 'set']]
let &bs="eol,indent,start"
let g:options = [['backspace', '', '', '', 'eol,indent,start', 'global', 'set']]
let &bs = "eol,indent,start"
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 15: Setting option backspace through setbufvar()"
let g:options=[['backup', 0, 0, '', 1, 'local', 'setlocal']]
let g:options = [['backup', 0, 0, '', 1, 'local', 'setlocal']]
" try twice, first time, shouldn't trigger because option name is invalid,
" second time, it should trigger
let bnum = bufnr('%')
@@ -646,13 +669,13 @@ func Test_OptionSet()
call assert_equal(g:opt[0], g:opt[1])
" 16: Setting number option using setwinvar"
let g:options=[['number', 0, 0, '', 1, 'local', 'setlocal']]
let g:options = [['number', 0, 0, '', 1, 'local', 'setlocal']]
call setwinvar(0, '&number', 1)
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 17: Setting key option, shouldn't trigger"
let g:options=[['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']]
let g:options = [['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']]
setlocal key=blah
setlocal key=
call assert_equal([['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']], g:options)
@@ -661,19 +684,19 @@ func Test_OptionSet()
" 18a: Setting string global option"
let oldval = &backupext
let g:options=[['backupext', oldval, oldval, oldval, 'foo', 'global', 'set']]
let g:options = [['backupext', oldval, oldval, oldval, 'foo', 'global', 'set']]
set backupext=foo
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 18b: Resetting string global option"
let g:options=[['backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set']]
let g:options = [['backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set']]
set backupext&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 18c: Setting global string global option"
let g:options=[['backupext', oldval, '', oldval, 'bar', 'global', 'setglobal']]
let g:options = [['backupext', oldval, '', oldval, 'bar', 'global', 'setglobal']]
setglobal backupext=bar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -682,7 +705,7 @@ func Test_OptionSet()
" As this is a global option this sets the global value even though
" :setlocal is used!
noa set backupext& " Reset global and local value (without triggering autocmd)
let g:options=[['backupext', oldval, oldval, '', 'baz', 'local', 'setlocal']]
let g:options = [['backupext', oldval, oldval, '', 'baz', 'local', 'setlocal']]
setlocal backupext=baz
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -690,7 +713,7 @@ func Test_OptionSet()
" 18e: Setting again string global option"
noa setglobal backupext=ext_global " Reset global and local value (without triggering autocmd)
noa setlocal backupext=ext_local " Sets the global(!) value!
let g:options=[['backupext', 'ext_local', 'ext_local', 'ext_local', 'fuu', 'global', 'set']]
let g:options = [['backupext', 'ext_local', 'ext_local', 'ext_local', 'fuu', 'global', 'set']]
set backupext=fuu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -698,25 +721,25 @@ func Test_OptionSet()
" 19a: Setting string local-global (to buffer) option"
let oldval = &tags
let g:options=[['tags', oldval, oldval, oldval, 'tagpath', 'global', 'set']]
let g:options = [['tags', oldval, oldval, oldval, 'tagpath', 'global', 'set']]
set tags=tagpath
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 19b: Resetting string local-global (to buffer) option"
let g:options=[['tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set']]
let g:options = [['tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set']]
set tags&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 19c: Setting global string local-global (to buffer) option "
let g:options=[['tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal']]
let g:options = [['tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal']]
setglobal tags=tagpath1
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 19d: Setting local string local-global (to buffer) option"
let g:options=[['tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal']]
let g:options = [['tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal']]
setlocal tags=tagpath2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -726,7 +749,7 @@ func Test_OptionSet()
" but the old local value for all other kinds of options.
noa setglobal tags=tag_global " Reset global and local value (without triggering autocmd)
noa setlocal tags=tag_local
let g:options=[['tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set']]
let g:options = [['tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set']]
set tags=tagpath
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -736,7 +759,7 @@ func Test_OptionSet()
" but the old local value for all other kinds of options.
noa set tags=tag_global " Reset global and local value (without triggering autocmd)
noa setlocal tags= " empty string
let g:options=[['tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set']]
let g:options = [['tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set']]
set tags=tagpath
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -744,26 +767,26 @@ func Test_OptionSet()
" 20a: Setting string local (to buffer) option"
let oldval = &spelllang
let g:options=[['spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set']]
let g:options = [['spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set']]
set spelllang=elvish,klingon
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 20b: Resetting string local (to buffer) option"
let g:options=[['spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set']]
let g:options = [['spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set']]
set spelllang&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 20c: Setting global string local (to buffer) option"
let g:options=[['spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal']]
let g:options = [['spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal']]
setglobal spelllang=elvish
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 20d: Setting local string local (to buffer) option"
noa set spelllang& " Reset global and local value (without triggering autocmd)
let g:options=[['spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal']]
let g:options = [['spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal']]
setlocal spelllang=klingon
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -773,7 +796,7 @@ func Test_OptionSet()
" but the old local value for all other kinds of options.
noa setglobal spelllang=spellglobal " Reset global and local value (without triggering autocmd)
noa setlocal spelllang=spelllocal
let g:options=[['spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set']]
let g:options = [['spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set']]
set spelllang=foo
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -781,7 +804,7 @@ func Test_OptionSet()
" 21a: Setting string local-global (to window) option"
let oldval = &statusline
let g:options=[['statusline', oldval, oldval, oldval, 'foo', 'global', 'set']]
let g:options = [['statusline', oldval, oldval, oldval, 'foo', 'global', 'set']]
set statusline=foo
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -789,20 +812,20 @@ func Test_OptionSet()
" 21b: Resetting string local-global (to window) option"
" Note: v:option_old is the old global value for local-global string options
" but the old local value for all other kinds of options.
let g:options=[['statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set']]
let g:options = [['statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set']]
set statusline&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 21c: Setting global string local-global (to window) option"
let g:options=[['statusline', oldval, '', oldval, 'bar', 'global', 'setglobal']]
let g:options = [['statusline', oldval, '', oldval, 'bar', 'global', 'setglobal']]
setglobal statusline=bar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 21d: Setting local string local-global (to window) option"
noa set statusline& " Reset global and local value (without triggering autocmd)
let g:options=[['statusline', oldval, oldval, '', 'baz', 'local', 'setlocal']]
let g:options = [['statusline', oldval, oldval, '', 'baz', 'local', 'setlocal']]
setlocal statusline=baz
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -812,7 +835,7 @@ func Test_OptionSet()
" but the old local value for all other kinds of options.
noa setglobal statusline=bar " Reset global and local value (without triggering autocmd)
noa setlocal statusline=baz
let g:options=[['statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set']]
let g:options = [['statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set']]
set statusline=foo
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -820,26 +843,26 @@ func Test_OptionSet()
" 22a: Setting string local (to window) option"
let oldval = &foldignore
let g:options=[['foldignore', oldval, oldval, oldval, 'fo', 'global', 'set']]
let g:options = [['foldignore', oldval, oldval, oldval, 'fo', 'global', 'set']]
set foldignore=fo
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 22b: Resetting string local (to window) option"
let g:options=[['foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set']]
let g:options = [['foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set']]
set foldignore&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 22c: Setting global string local (to window) option"
let g:options=[['foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal']]
let g:options = [['foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal']]
setglobal foldignore=bar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 22d: Setting local string local (to window) option"
noa set foldignore& " Reset global and local value (without triggering autocmd)
let g:options=[['foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal']]
let g:options = [['foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal']]
setlocal foldignore=baz
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -847,7 +870,7 @@ func Test_OptionSet()
" 22e: Setting again string local (to window) option"
noa setglobal foldignore=glob " Reset global and local value (without triggering autocmd)
noa setlocal foldignore=loc
let g:options=[['foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set']]
let g:options = [['foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set']]
set foldignore=fo
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -856,7 +879,7 @@ func Test_OptionSet()
" 23a: Setting global number local option"
noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd)
noa setlocal cmdheight=1 " Sets the global(!) value!
let g:options=[['cmdheight', '1', '', '1', '2', 'global', 'setglobal']]
let g:options = [['cmdheight', '1', '', '1', '2', 'global', 'setglobal']]
setglobal cmdheight=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -864,7 +887,7 @@ func Test_OptionSet()
" 23b: Setting local number global option"
noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd)
noa setlocal cmdheight=1 " Sets the global(!) value!
let g:options=[['cmdheight', '1', '1', '', '2', 'local', 'setlocal']]
let g:options = [['cmdheight', '1', '1', '', '2', 'local', 'setlocal']]
setlocal cmdheight=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -872,14 +895,14 @@ func Test_OptionSet()
" 23c: Setting again number global option"
noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd)
noa setlocal cmdheight=1 " Sets the global(!) value!
let g:options=[['cmdheight', '1', '1', '1', '2', 'global', 'set']]
let g:options = [['cmdheight', '1', '1', '1', '2', 'global', 'set']]
set cmdheight=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 23d: Setting again number global option"
noa set cmdheight=8 " Reset global and local value (without triggering autocmd)
let g:options=[['cmdheight', '8', '8', '8', '2', 'global', 'set']]
let g:options = [['cmdheight', '8', '8', '8', '2', 'global', 'set']]
set cmdheight=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -888,7 +911,7 @@ func Test_OptionSet()
" 24a: Setting global number global-local (to buffer) option"
noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd)
noa setlocal undolevels=1
let g:options=[['undolevels', '8', '', '8', '2', 'global', 'setglobal']]
let g:options = [['undolevels', '8', '', '8', '2', 'global', 'setglobal']]
setglobal undolevels=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -896,7 +919,7 @@ func Test_OptionSet()
" 24b: Setting local number global-local (to buffer) option"
noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd)
noa setlocal undolevels=1
let g:options=[['undolevels', '1', '1', '', '2', 'local', 'setlocal']]
let g:options = [['undolevels', '1', '1', '', '2', 'local', 'setlocal']]
setlocal undolevels=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -904,14 +927,14 @@ func Test_OptionSet()
" 24c: Setting again number global-local (to buffer) option"
noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd)
noa setlocal undolevels=1
let g:options=[['undolevels', '1', '1', '8', '2', 'global', 'set']]
let g:options = [['undolevels', '1', '1', '8', '2', 'global', 'set']]
set undolevels=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 24d: Setting again global number global-local (to buffer) option"
noa set undolevels=8 " Reset global and local value (without triggering autocmd)
let g:options=[['undolevels', '8', '8', '8', '2', 'global', 'set']]
let g:options = [['undolevels', '8', '8', '8', '2', 'global', 'set']]
set undolevels=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -920,7 +943,7 @@ func Test_OptionSet()
" 25a: Setting global number local (to buffer) option"
noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd)
noa setlocal wrapmargin=1
let g:options=[['wrapmargin', '8', '', '8', '2', 'global', 'setglobal']]
let g:options = [['wrapmargin', '8', '', '8', '2', 'global', 'setglobal']]
setglobal wrapmargin=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -928,7 +951,7 @@ func Test_OptionSet()
" 25b: Setting local number local (to buffer) option"
noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd)
noa setlocal wrapmargin=1
let g:options=[['wrapmargin', '1', '1', '', '2', 'local', 'setlocal']]
let g:options = [['wrapmargin', '1', '1', '', '2', 'local', 'setlocal']]
setlocal wrapmargin=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -936,14 +959,14 @@ func Test_OptionSet()
" 25c: Setting again number local (to buffer) option"
noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd)
noa setlocal wrapmargin=1
let g:options=[['wrapmargin', '1', '1', '8', '2', 'global', 'set']]
let g:options = [['wrapmargin', '1', '1', '8', '2', 'global', 'set']]
set wrapmargin=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 25d: Setting again global number local (to buffer) option"
noa set wrapmargin=8 " Reset global and local value (without triggering autocmd)
let g:options=[['wrapmargin', '8', '8', '8', '2', 'global', 'set']]
let g:options = [['wrapmargin', '8', '8', '8', '2', 'global', 'set']]
set wrapmargin=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -956,7 +979,7 @@ func Test_OptionSet()
" 27a: Setting global number local (to window) option"
noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd)
noa setlocal foldcolumn=1
let g:options=[['foldcolumn', '8', '', '8', '2', 'global', 'setglobal']]
let g:options = [['foldcolumn', '8', '', '8', '2', 'global', 'setglobal']]
setglobal foldcolumn=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -964,7 +987,7 @@ func Test_OptionSet()
" 27b: Setting local number local (to window) option"
noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd)
noa setlocal foldcolumn=1
let g:options=[['foldcolumn', '1', '1', '', '2', 'local', 'setlocal']]
let g:options = [['foldcolumn', '1', '1', '', '2', 'local', 'setlocal']]
setlocal foldcolumn=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -972,14 +995,14 @@ func Test_OptionSet()
" 27c: Setting again number local (to window) option"
noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd)
noa setlocal foldcolumn=1
let g:options=[['foldcolumn', '1', '1', '8', '2', 'global', 'set']]
let g:options = [['foldcolumn', '1', '1', '8', '2', 'global', 'set']]
set foldcolumn=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 27d: Ssettin again global number local (to window) option"
noa set foldcolumn=8 " Reset global and local value (without triggering autocmd)
let g:options=[['foldcolumn', '8', '8', '8', '2', 'global', 'set']]
let g:options = [['foldcolumn', '8', '8', '8', '2', 'global', 'set']]
set foldcolumn=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -988,7 +1011,7 @@ func Test_OptionSet()
" 28a: Setting global boolean global option"
noa setglobal nowrapscan " Reset global and local value (without triggering autocmd)
noa setlocal wrapscan " Sets the global(!) value!
let g:options=[['wrapscan', '1', '', '1', '0', 'global', 'setglobal']]
let g:options = [['wrapscan', '1', '', '1', '0', 'global', 'setglobal']]
setglobal nowrapscan
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -996,7 +1019,7 @@ func Test_OptionSet()
" 28b: Setting local boolean global option"
noa setglobal nowrapscan " Reset global and local value (without triggering autocmd)
noa setlocal wrapscan " Sets the global(!) value!
let g:options=[['wrapscan', '1', '1', '', '0', 'local', 'setlocal']]
let g:options = [['wrapscan', '1', '1', '', '0', 'local', 'setlocal']]
setlocal nowrapscan
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1004,14 +1027,14 @@ func Test_OptionSet()
" 28c: Setting again boolean global option"
noa setglobal nowrapscan " Reset global and local value (without triggering autocmd)
noa setlocal wrapscan " Sets the global(!) value!
let g:options=[['wrapscan', '1', '1', '1', '0', 'global', 'set']]
let g:options = [['wrapscan', '1', '1', '1', '0', 'global', 'set']]
set nowrapscan
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 28d: Setting again global boolean global option"
noa set nowrapscan " Reset global and local value (without triggering autocmd)
let g:options=[['wrapscan', '0', '0', '0', '1', 'global', 'set']]
let g:options = [['wrapscan', '0', '0', '0', '1', 'global', 'set']]
set wrapscan
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1020,7 +1043,7 @@ func Test_OptionSet()
" 29a: Setting global boolean global-local (to buffer) option"
noa setglobal noautoread " Reset global and local value (without triggering autocmd)
noa setlocal autoread
let g:options=[['autoread', '0', '', '0', '1', 'global', 'setglobal']]
let g:options = [['autoread', '0', '', '0', '1', 'global', 'setglobal']]
setglobal autoread
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1028,7 +1051,7 @@ func Test_OptionSet()
" 29b: Setting local boolean global-local (to buffer) option"
noa setglobal noautoread " Reset global and local value (without triggering autocmd)
noa setlocal autoread
let g:options=[['autoread', '1', '1', '', '0', 'local', 'setlocal']]
let g:options = [['autoread', '1', '1', '', '0', 'local', 'setlocal']]
setlocal noautoread
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1036,14 +1059,14 @@ func Test_OptionSet()
" 29c: Setting again boolean global-local (to buffer) option"
noa setglobal noautoread " Reset global and local value (without triggering autocmd)
noa setlocal autoread
let g:options=[['autoread', '1', '1', '0', '1', 'global', 'set']]
let g:options = [['autoread', '1', '1', '0', '1', 'global', 'set']]
set autoread
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 29d: Setting again global boolean global-local (to buffer) option"
noa set noautoread " Reset global and local value (without triggering autocmd)
let g:options=[['autoread', '0', '0', '0', '1', 'global', 'set']]
let g:options = [['autoread', '0', '0', '0', '1', 'global', 'set']]
set autoread
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1052,7 +1075,7 @@ func Test_OptionSet()
" 30a: Setting global boolean local (to buffer) option"
noa setglobal nocindent " Reset global and local value (without triggering autocmd)
noa setlocal cindent
let g:options=[['cindent', '0', '', '0', '1', 'global', 'setglobal']]
let g:options = [['cindent', '0', '', '0', '1', 'global', 'setglobal']]
setglobal cindent
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1060,7 +1083,7 @@ func Test_OptionSet()
" 30b: Setting local boolean local (to buffer) option"
noa setglobal nocindent " Reset global and local value (without triggering autocmd)
noa setlocal cindent
let g:options=[['cindent', '1', '1', '', '0', 'local', 'setlocal']]
let g:options = [['cindent', '1', '1', '', '0', 'local', 'setlocal']]
setlocal nocindent
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1068,14 +1091,14 @@ func Test_OptionSet()
" 30c: Setting again boolean local (to buffer) option"
noa setglobal nocindent " Reset global and local value (without triggering autocmd)
noa setlocal cindent
let g:options=[['cindent', '1', '1', '0', '1', 'global', 'set']]
let g:options = [['cindent', '1', '1', '0', '1', 'global', 'set']]
set cindent
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 30d: Setting again global boolean local (to buffer) option"
noa set nocindent " Reset global and local value (without triggering autocmd)
let g:options=[['cindent', '0', '0', '0', '1', 'global', 'set']]
let g:options = [['cindent', '0', '0', '0', '1', 'global', 'set']]
set cindent
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1088,7 +1111,7 @@ func Test_OptionSet()
" 32a: Setting global boolean local (to window) option"
noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd)
noa setlocal cursorcolumn
let g:options=[['cursorcolumn', '0', '', '0', '1', 'global', 'setglobal']]
let g:options = [['cursorcolumn', '0', '', '0', '1', 'global', 'setglobal']]
setglobal cursorcolumn
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1096,7 +1119,7 @@ func Test_OptionSet()
" 32b: Setting local boolean local (to window) option"
noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd)
noa setlocal cursorcolumn
let g:options=[['cursorcolumn', '1', '1', '', '0', 'local', 'setlocal']]
let g:options = [['cursorcolumn', '1', '1', '', '0', 'local', 'setlocal']]
setlocal nocursorcolumn
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1104,14 +1127,14 @@ func Test_OptionSet()
" 32c: Setting again boolean local (to window) option"
noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd)
noa setlocal cursorcolumn
let g:options=[['cursorcolumn', '1', '1', '0', '1', 'global', 'set']]
let g:options = [['cursorcolumn', '1', '1', '0', '1', 'global', 'set']]
set cursorcolumn
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 32d: Setting again global boolean local (to window) option"
noa set nocursorcolumn " Reset global and local value (without triggering autocmd)
let g:options=[['cursorcolumn', '0', '0', '0', '1', 'global', 'set']]
let g:options = [['cursorcolumn', '0', '0', '0', '1', 'global', 'set']]
set cursorcolumn
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1119,7 +1142,7 @@ func Test_OptionSet()
" 33: Test autocommands when an option value is converted internally.
noa set backspace=1 " Reset global and local value (without triggering autocmd)
let g:options=[['backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set']]
let g:options = [['backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set']]
set backspace=2
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -1139,7 +1162,7 @@ func Test_OptionSet_diffmode()
call test_override('starting', 1)
" 18: Changing an option when entering diff mode
new
au OptionSet diff :let &l:cul=v:option_new
au OptionSet diff :let &l:cul = v:option_new
call setline(1, ['buffer 1', 'line2', 'line3', 'line4'])
call assert_equal(0, &l:cul)
@@ -2355,7 +2378,7 @@ func Test_autocmd_CmdWinEnter()
autocmd CmdWinEnter * quit
let winnr = winnr('$')
END
let filename='XCmdWinEnter'
let filename = 'XCmdWinEnter'
call writefile(lines, filename)
let buf = RunVimInTerminal('-S '.filename, #{rows: 6})

View File

@@ -30,7 +30,7 @@ func Test_move_cursor()
call cursor(1, 1, 1)
call assert_equal([1, 1, 1], getcurpos()[1:3])
call assert_equal(-1, cursor(-1, -1))
call assert_fails('call cursor(-1, -1)', 'E475:')
quit!
endfunc

View File

@@ -12,6 +12,7 @@ func Create_vim_dict()
return {'a': 1}
endfunction
let s:system_error_pat = 'Vim(py3):SystemError: \(<built-in function eval> returned NULL without setting an error\|error return without exception set\)'
" This function should be called first. This sets up python functions used by
" the other tests.
@@ -539,11 +540,11 @@ endfunc
func Test_python3_list()
" Try to convert a null List
call AssertException(["py3 t = vim.eval('test_null_list()')"],
\ 'Vim(py3):SystemError: <built-in function eval> returned NULL without setting an error')
\ s:system_error_pat)
" Try to convert a List with a null List item
call AssertException(["py3 t = vim.eval('[test_null_list()]')"],
\ 'Vim(py3):SystemError: <built-in function eval> returned NULL without setting an error')
\ s:system_error_pat)
" Try to bind a null List variable (works because an empty list is used)
let cmds =<< trim END
@@ -582,11 +583,11 @@ endfunc
func Test_python3_dict()
" Try to convert a null Dict
call AssertException(["py3 t = vim.eval('test_null_dict()')"],
\ 'Vim(py3):SystemError: <built-in function eval> returned NULL without setting an error')
\ s:system_error_pat)
" Try to convert a Dict with a null List value
call AssertException(["py3 t = vim.eval(\"{'a' : test_null_list()}\")"],
\ 'Vim(py3):SystemError: <built-in function eval> returned NULL without setting an error')
\ s:system_error_pat)
" Try to convert a Dict with a null string key
py3 t = vim.eval("{test_null_string() : 10}")
@@ -1119,7 +1120,7 @@ func Test_python3_list_slice()
let l = [test_null_list()]
py3 ll = vim.bindeval('l')
call AssertException(["py3 x = ll[:]"],
\ "Vim(py3):SystemError: error return without exception set")
\ s:system_error_pat)
endfunc
" Vars

View File

@@ -698,4 +698,15 @@ func Test_ve_blockpaste()
bwipe!
endfunc
func Test_insert_small_delete()
new
call setline(1, ['foo foobar bar'])
call cursor(1,1)
exe ":norm! ciw'\<C-R>-'"
call assert_equal(getline(1), "'foo' foobar bar")
exe ":norm! w.w."
call assert_equal(getline(1), "'foo' 'foobar' 'bar'")
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -1127,6 +1127,30 @@ def Test_var_declaration()
const FOO: number = 123
assert_equal(123, FOO)
const FOOS = 'foos'
assert_equal('foos', FOOS)
final FLIST = [1]
assert_equal([1], FLIST)
FLIST[0] = 11
assert_equal([11], FLIST)
const g:FOO: number = 321
assert_equal(321, g:FOO)
const g:FOOS = 'gfoos'
assert_equal('gfoos', g:FOOS)
final g:FLIST = [2]
assert_equal([2], g:FLIST)
g:FLIST[0] = 22
assert_equal([22], g:FLIST)
const w:FOO: number = 46
assert_equal(46, w:FOO)
const w:FOOS = 'wfoos'
assert_equal('wfoos', w:FOOS)
final w:FLIST = [3]
assert_equal([3], w:FLIST)
w:FLIST[0] = 33
assert_equal([33], w:FLIST)
var s:other: number
other = 1234
@@ -1150,6 +1174,12 @@ def Test_var_declaration()
unlet g:var_test
unlet g:var_prefixed
unlet g:other_var
unlet g:FOO
unlet g:FOOS
unlet g:FLIST
unlet w:FOO
unlet w:FOOS
unlet w:FLIST
enddef
def Test_var_declaration_fails()
@@ -1159,6 +1189,22 @@ def Test_var_declaration_fails()
END
CheckScriptFailure(lines, 'E1125:')
lines =<< trim END
vim9script
const g:constvar = 'string'
g:constvar = 'xx'
END
CheckScriptFailure(lines, 'E741:')
unlet g:constvar
lines =<< trim END
vim9script
final w:finalvar = [9]
w:finalvar = [8]
END
CheckScriptFailure(lines, 'E1122:')
unlet w:finalvar
lines =<< trim END
vim9script
const var: string

View File

@@ -185,6 +185,20 @@ def Test_count()
count('ABC ABC ABC', 'b', false)->assert_equal(0)
enddef
def Test_cursor()
new
setline(1, range(4))
cursor(2, 1)
assert_equal(2, getcurpos()[1])
cursor('$', 1)
assert_equal(4, getcurpos()[1])
var lines =<< trim END
cursor('2', 1)
END
CheckDefExecAndScriptFailure(lines, 'E475:')
enddef
def Test_executable()
assert_false(executable(""))
assert_false(executable(test_null_string()))

View File

@@ -740,4 +740,17 @@ def Test_magic_not_used()
bwipe!
enddef
def Test_gdefault_not_used()
new
for cmd in ['set gdefault', 'set nogdefault']
exe cmd
setline(1, 'aaa')
s/./b/
assert_equal('baa', getline(1))
endfor
set nogdefault
bwipe!
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -299,6 +299,20 @@ def Test_nested_global_function()
Outer()
END
CheckScriptFailure(lines, "E122:")
delfunc g:Inner
lines =<< trim END
vim9script
def Outer()
def g:Inner()
echo map([1, 2, 3], {_, v -> v + 1})
enddef
g:Inner()
enddef
Outer()
END
CheckScriptSuccess(lines)
delfunc g:Inner
lines =<< trim END
vim9script
@@ -1008,6 +1022,17 @@ def Test_vim9script_call_fail_const()
writefile(lines, 'Xcall_const.vim')
assert_fails('source Xcall_const.vim', 'E46:', '', 1, 'MyFunc')
delete('Xcall_const.vim')
lines =<< trim END
const g:Aconst = 77
def Change()
# comment
g:Aconst = 99
enddef
call Change()
unlet g:Aconst
END
CheckScriptFailure(lines, 'E741: Value is locked: Aconst', 2)
enddef
" Test that inside :function a Python function can be defined, :def is not
@@ -1565,6 +1590,25 @@ def Test_global_closure()
bwipe!
enddef
def Test_global_closure_called_directly()
var lines =<< trim END
vim9script
def Outer()
var x = 1
def g:Inner()
var y = x
x += 1
assert_equal(1, y)
enddef
g:Inner()
assert_equal(2, x)
enddef
Outer()
END
CheckScriptSuccess(lines)
delfunc g:Inner
enddef
def Test_failure_in_called_function()
# this was using the frame index as the return value
var lines =<< trim END
@@ -1976,5 +2020,42 @@ def Test_dict_member_with_silent()
CheckScriptSuccess(lines)
enddef
def Test_opfunc()
nnoremap <F3> <cmd>set opfunc=Opfunc<cr>g@
def g:Opfunc(_: any): string
setline(1, 'ASDF')
return ''
enddef
new
setline(1, 'asdf')
feedkeys("\<F3>$", 'x')
assert_equal('ASDF', getline(1))
bwipe!
nunmap <F3>
enddef
" this was crashing on exit
def Test_nested_lambda_in_closure()
var lines =<< trim END
vim9script
def Outer()
def g:Inner()
echo map([1, 2, 3], {_, v -> v + 1})
enddef
g:Inner()
enddef
defcompile
writefile(['Done'], 'XnestedDone')
quit
END
if !RunVim([], lines, '--clean')
return
endif
assert_equal(['Done'], readfile('XnestedDone'))
delete('XnestedDone')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -1536,11 +1536,11 @@ eval_env_var(char_u **arg, typval_T *rettv, int evaluate)
linenr_T
tv_get_lnum(typval_T *argvars)
{
linenr_T lnum = 0;
linenr_T lnum = -1;
if (argvars[0].v_type != VAR_STRING || !in_vim9script())
lnum = (linenr_T)tv_get_number_chk(&argvars[0], NULL);
if (lnum == 0) // no valid number, try using arg like line()
if (lnum <= 0) // no valid number, try using arg like line()
{
int fnum;
pos_T *fp = var2fpos(&argvars[0], TRUE, &fnum);

View File

@@ -1260,8 +1260,7 @@ func_clear(ufunc_T *fp, int force)
// clear this function
func_clear_items(fp);
funccal_unref(fp->uf_scoped, fp, force);
if ((fp->uf_flags & FC_COPY) == 0)
clear_def_function(fp);
unlink_def_function(fp);
}
/*
@@ -1307,75 +1306,98 @@ func_clear_free(ufunc_T *fp, int force)
/*
* Copy already defined function "lambda" to a new function with name "global".
* This is for when a compiled function defines a global function.
* Caller should take care of adding a partial for a closure.
*/
ufunc_T *
copy_func(char_u *lambda, char_u *global)
int
copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
{
ufunc_T *ufunc = find_func_even_dead(lambda, TRUE, NULL);
ufunc_T *fp = NULL;
if (ufunc == NULL)
semsg(_(e_lambda_function_not_found_str), lambda);
else
{
// TODO: handle ! to overwrite
fp = find_func(global, TRUE, NULL);
if (fp != NULL)
{
semsg(_(e_funcexts), global);
return NULL;
}
fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(global) + 1);
if (fp == NULL)
return NULL;
fp->uf_varargs = ufunc->uf_varargs;
fp->uf_flags = (ufunc->uf_flags & ~FC_VIM9) | FC_COPY;
fp->uf_def_status = ufunc->uf_def_status;
fp->uf_dfunc_idx = ufunc->uf_dfunc_idx;
if (ga_copy_strings(&ufunc->uf_args, &fp->uf_args) == FAIL
|| ga_copy_strings(&ufunc->uf_def_args, &fp->uf_def_args)
== FAIL
|| ga_copy_strings(&ufunc->uf_lines, &fp->uf_lines) == FAIL)
goto failed;
fp->uf_name_exp = ufunc->uf_name_exp == NULL ? NULL
: vim_strsave(ufunc->uf_name_exp);
if (ufunc->uf_arg_types != NULL)
{
fp->uf_arg_types = ALLOC_MULT(type_T *, fp->uf_args.ga_len);
if (fp->uf_arg_types == NULL)
goto failed;
mch_memmove(fp->uf_arg_types, ufunc->uf_arg_types,
sizeof(type_T *) * fp->uf_args.ga_len);
}
if (ufunc->uf_def_arg_idx != NULL)
{
fp->uf_def_arg_idx = ALLOC_MULT(int, fp->uf_def_args.ga_len + 1);
if (fp->uf_def_arg_idx == NULL)
goto failed;
mch_memmove(fp->uf_def_arg_idx, ufunc->uf_def_arg_idx,
sizeof(int) * fp->uf_def_args.ga_len + 1);
}
if (ufunc->uf_va_name != NULL)
{
fp->uf_va_name = vim_strsave(ufunc->uf_va_name);
if (fp->uf_va_name == NULL)
goto failed;
}
fp->uf_ret_type = ufunc->uf_ret_type;
fp->uf_refcount = 1;
STRCPY(fp->uf_name, global);
hash_add(&func_hashtab, UF2HIKEY(fp));
semsg(_(e_lambda_function_not_found_str), lambda);
return FAIL;
}
return fp;
// TODO: handle ! to overwrite
fp = find_func(global, TRUE, NULL);
if (fp != NULL)
{
semsg(_(e_funcexts), global);
return FAIL;
}
fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(global) + 1);
if (fp == NULL)
return FAIL;
fp->uf_varargs = ufunc->uf_varargs;
fp->uf_flags = (ufunc->uf_flags & ~FC_VIM9) | FC_COPY;
fp->uf_def_status = ufunc->uf_def_status;
fp->uf_dfunc_idx = ufunc->uf_dfunc_idx;
if (ga_copy_strings(&ufunc->uf_args, &fp->uf_args) == FAIL
|| ga_copy_strings(&ufunc->uf_def_args, &fp->uf_def_args)
== FAIL
|| ga_copy_strings(&ufunc->uf_lines, &fp->uf_lines) == FAIL)
goto failed;
fp->uf_name_exp = ufunc->uf_name_exp == NULL ? NULL
: vim_strsave(ufunc->uf_name_exp);
if (ufunc->uf_arg_types != NULL)
{
fp->uf_arg_types = ALLOC_MULT(type_T *, fp->uf_args.ga_len);
if (fp->uf_arg_types == NULL)
goto failed;
mch_memmove(fp->uf_arg_types, ufunc->uf_arg_types,
sizeof(type_T *) * fp->uf_args.ga_len);
}
if (ufunc->uf_def_arg_idx != NULL)
{
fp->uf_def_arg_idx = ALLOC_MULT(int, fp->uf_def_args.ga_len + 1);
if (fp->uf_def_arg_idx == NULL)
goto failed;
mch_memmove(fp->uf_def_arg_idx, ufunc->uf_def_arg_idx,
sizeof(int) * fp->uf_def_args.ga_len + 1);
}
if (ufunc->uf_va_name != NULL)
{
fp->uf_va_name = vim_strsave(ufunc->uf_va_name);
if (fp->uf_va_name == NULL)
goto failed;
}
fp->uf_ret_type = ufunc->uf_ret_type;
fp->uf_refcount = 1;
STRCPY(fp->uf_name, global);
hash_add(&func_hashtab, UF2HIKEY(fp));
// the referenced dfunc_T is now used one more time
link_def_function(fp);
// Create a partial to store the context of the function, if not done
// already.
if ((ufunc->uf_flags & FC_CLOSURE) && ufunc->uf_partial == NULL)
{
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
if (pt == NULL)
goto failed;
if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
goto failed;
ufunc->uf_partial = pt;
--pt->pt_refcount; // not referenced here yet
}
if (ufunc->uf_partial != NULL)
{
fp->uf_partial = ufunc->uf_partial;
++fp->uf_partial->pt_refcount;
}
return OK;
failed:
func_clear_free(fp, TRUE);
return NULL;
return FAIL;
}
static int funcdepth = 0;
@@ -3515,7 +3537,7 @@ define_function(exarg_T *eap, char_u *name_arg)
fp->uf_profiling = FALSE;
fp->uf_prof_initialized = FALSE;
#endif
clear_def_function(fp);
unlink_def_function(fp);
}
}
}

View File

@@ -750,6 +750,32 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2195,
/**/
2194,
/**/
2193,
/**/
2192,
/**/
2191,
/**/
2190,
/**/
2189,
/**/
2188,
/**/
2187,
/**/
2186,
/**/
2185,
/**/
2184,
/**/
2183,
/**/
2182,
/**/

View File

@@ -2144,7 +2144,7 @@ typedef enum {
// Flags for assignment functions.
#define ASSIGN_FINAL 1 // ":final"
#define ASSIGN_CONST 2 // ":const"
#define ASSIGN_NO_DECL 4 // "name = expr" without ":let" or ":const"
#define ASSIGN_NO_DECL 4 // "name = expr" without ":let"/":const"/":final"
#include "ex_cmds.h" // Ex command defines
#include "spell.h" // spell checking stuff

View File

@@ -341,8 +341,10 @@ struct isn_S {
*/
struct dfunc_S {
ufunc_T *df_ufunc; // struct containing most stuff
int df_refcount; // how many ufunc_T point to this dfunc_T
int df_idx; // index in def_functions
int df_deleted; // if TRUE function was deleted
char_u *df_name; // name used for error messages
garray_T df_def_args_isn; // default argument instructions
isn_T *df_instr; // function body to be executed

View File

@@ -1428,20 +1428,27 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
/*
* Generate an ISN_NEWFUNC instruction.
* "lambda_name" and "func_name" must be in allocated memory and will be
* consumed.
*/
static int
generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
{
isn_T *isn;
char_u *name;
RETURN_OK_IF_SKIP(cctx);
name = vim_strsave(lambda_name);
if (name == NULL)
return FAIL;
if (cctx->ctx_skip == SKIP_YES)
{
vim_free(lambda_name);
vim_free(func_name);
return OK;
}
if ((isn = generate_instr(cctx, ISN_NEWFUNC)) == NULL)
{
vim_free(lambda_name);
vim_free(func_name);
return FAIL;
isn->isn_arg.newfunc.nf_lambda = name;
}
isn->isn_arg.newfunc.nf_lambda = lambda_name;
isn->isn_arg.newfunc.nf_global = func_name;
return OK;
@@ -4840,7 +4847,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
char_u *name_end = to_name_end(eap->arg, TRUE);
char_u *lambda_name;
ufunc_T *ufunc;
int r;
int r = FAIL;
if (eap->forceit)
{
@@ -4883,16 +4890,21 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
eap->cookie = cctx;
eap->skip = cctx->ctx_skip == SKIP_YES;
eap->forceit = FALSE;
lambda_name = get_lambda_name();
lambda_name = vim_strsave(get_lambda_name());
if (lambda_name == NULL)
return NULL;
ufunc = define_function(eap, lambda_name);
if (ufunc == NULL)
return eap->skip ? (char_u *)"" : NULL;
{
r = eap->skip ? OK : FAIL;
goto theend;
}
if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, TRUE, cctx) == FAIL)
{
func_ptr_unref(ufunc);
return NULL;
goto theend;
}
if (is_global)
@@ -4903,7 +4915,10 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
if (func_name == NULL)
r = FAIL;
else
{
r = generate_NEWFUNC(cctx, lambda_name, func_name);
lambda_name = NULL;
}
}
else
{
@@ -4913,9 +4928,9 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
int block_depth = cctx->ctx_ufunc->uf_block_depth;
if (lvar == NULL)
return NULL;
goto theend;
if (generate_FUNCREF(cctx, ufunc) == FAIL)
return NULL;
goto theend;
r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
// copy over the block scope IDs
@@ -4930,8 +4945,11 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
}
}
}
// TODO: warning for trailing text?
r = OK;
theend:
vim_free(lambda_name);
return r == FAIL ? NULL : (char_u *)"";
}
@@ -7336,6 +7354,8 @@ add_def_function(ufunc_T *ufunc)
dfunc->df_idx = def_functions.ga_len;
ufunc->uf_dfunc_idx = dfunc->df_idx;
dfunc->df_ufunc = ufunc;
dfunc->df_name = vim_strsave(ufunc->uf_name);
++dfunc->df_refcount;
++def_functions.ga_len;
return OK;
}
@@ -7928,6 +7948,7 @@ erret:
for (idx = 0; idx < instr->ga_len; ++idx)
delete_instr(((isn_T *)instr->ga_data) + idx);
ga_clear(instr);
VIM_CLEAR(dfunc->df_name);
// If using the last entry in the table and it was added above, we
// might as well remove it.
@@ -8078,9 +8099,10 @@ delete_instr(isn_T *isn)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ isn->isn_arg.funcref.fr_func;
ufunc_T *ufunc = dfunc->df_ufunc;
if (func_name_refcount(dfunc->df_ufunc->uf_name))
func_ptr_unref(dfunc->df_ufunc);
if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
func_ptr_unref(ufunc);
}
break;
@@ -8102,9 +8124,7 @@ delete_instr(isn_T *isn)
if (ufunc != NULL)
{
// Clear uf_dfunc_idx so that the function is deleted.
clear_def_function(ufunc);
ufunc->uf_dfunc_idx = 0;
unlink_def_function(ufunc);
func_ptr_unref(ufunc);
}
@@ -8206,7 +8226,7 @@ delete_instr(isn_T *isn)
}
/*
* Free all instructions for "dfunc".
* Free all instructions for "dfunc" except df_name.
*/
static void
delete_def_function_contents(dfunc_T *dfunc)
@@ -8227,31 +8247,39 @@ delete_def_function_contents(dfunc_T *dfunc)
/*
* When a user function is deleted, clear the contents of any associated def
* function. The position in def_functions can be re-used.
* function, unless another user function still uses it.
* The position in def_functions can be re-used.
*/
void
clear_def_function(ufunc_T *ufunc)
unlink_def_function(ufunc_T *ufunc)
{
if (ufunc->uf_dfunc_idx > 0)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
delete_def_function_contents(dfunc);
if (--dfunc->df_refcount <= 0)
delete_def_function_contents(dfunc);
ufunc->uf_def_status = UF_NOT_COMPILED;
ufunc->uf_dfunc_idx = 0;
if (dfunc->df_ufunc == ufunc)
dfunc->df_ufunc = NULL;
}
}
/*
* Used when a user function is about to be deleted: remove the pointer to it.
* The entry in def_functions is then unused.
* Used when a user function refers to an existing dfunc.
*/
void
unlink_def_function(ufunc_T *ufunc)
link_def_function(ufunc_T *ufunc)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
if (ufunc->uf_dfunc_idx > 0)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
dfunc->df_ufunc = NULL;
++dfunc->df_refcount;
}
}
#if defined(EXITFREE) || defined(PROTO)
@@ -8268,6 +8296,7 @@ free_def_functions(void)
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + idx;
delete_def_function_contents(dfunc);
vim_free(dfunc->df_name);
}
ga_clear(&def_functions);

View File

@@ -54,7 +54,7 @@ typedef struct {
/*
* Execution context.
*/
typedef struct {
struct ectx_S {
garray_T ec_stack; // stack of typval_T values
int ec_frame_idx; // index in ec_stack: context of ec_dfunc_idx
@@ -69,7 +69,7 @@ typedef struct {
int ec_iidx; // index in ec_instr: instruction to execute
garray_T ec_funcrefs; // partials that might be a closure
} ectx_T;
};
// Get pointer to item relative to the bottom of the stack, -1 is the last one.
#define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + (idx))
@@ -173,7 +173,9 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
if (dfunc->df_deleted)
{
emsg_funcname(e_func_deleted, ufunc->uf_name);
// don't use ufunc->uf_name, it may have been freed
emsg_funcname(e_func_deleted,
dfunc->df_name == NULL ? (char_u *)"unknown" : dfunc->df_name);
return FAIL;
}
@@ -260,6 +262,12 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
}
ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
if (ufunc->uf_partial != NULL)
{
ectx->ec_outer_stack = ufunc->uf_partial->pt_ectx_stack;
ectx->ec_outer_frame = ufunc->uf_partial->pt_ectx_frame;
}
// Set execution state to the start of the called function.
ectx->ec_dfunc_idx = cdf_idx;
ectx->ec_instr = dfunc->df_instr;
@@ -618,6 +626,7 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
// The function has been compiled, can call it quickly. For a function
// that was defined later: we can call it directly next time.
// TODO: what if the function was deleted and then defined again?
if (iptr != NULL)
{
delete_instr(iptr);
@@ -890,7 +899,7 @@ call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
* When a function reference is used, fill a partial with the information
* needed, especially when it is used as a closure.
*/
static int
int
fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
{
pt->pt_func = ufunc;
@@ -1684,8 +1693,10 @@ call_def_function(
case ISN_STOREW:
case ISN_STORET:
{
dictitem_T *di;
hashtab_T *ht;
dictitem_T *di;
hashtab_T *ht;
char_u *name = iptr->isn_arg.string + 2;
switch (iptr->isn_type)
{
case ISN_STOREG:
@@ -1705,11 +1716,14 @@ call_def_function(
}
--ectx.ec_stack.ga_len;
di = find_var_in_ht(ht, 0, iptr->isn_arg.string + 2, TRUE);
di = find_var_in_ht(ht, 0, name, TRUE);
if (di == NULL)
store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
else
{
SOURCING_LNUM = iptr->isn_lnum;
if (var_check_permission(di, name) == FAIL)
goto on_error;
clear_tv(&di->di_tv);
di->di_tv = *STACK_TV_BOT(0);
}
@@ -2120,25 +2134,10 @@ call_def_function(
case ISN_NEWFUNC:
{
newfunc_T *newfunc = &iptr->isn_arg.newfunc;
ufunc_T *new_ufunc;
new_ufunc = copy_func(
newfunc->nf_lambda, newfunc->nf_global);
if (new_ufunc != NULL
&& (new_ufunc->uf_flags & FC_CLOSURE))
{
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
// Need to create a partial to store the context of the
// function.
if (pt == NULL)
goto failed;
if (fill_partial_and_closure(pt, new_ufunc,
if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
&ectx) == FAIL)
goto failed;
new_ufunc->uf_partial = pt;
--pt->pt_refcount; // not referenced here
}
goto failed;
}
break;