mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.1685: Vim9: cannot declare a constant value
Problem: Vim9: cannot declare a constant value. Solution: Introduce ":const!".
This commit is contained in:
parent
efd5d8a967
commit
0b4c66c67a
@ -1,4 +1,4 @@
|
|||||||
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 07
|
*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 13
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -192,6 +192,9 @@ To intentionally avoid a variable being available later, a block can be used:
|
|||||||
}
|
}
|
||||||
echo temp # Error!
|
echo temp # Error!
|
||||||
|
|
||||||
|
Declaring a variable with a type but without an initializer will initialize to
|
||||||
|
zero, false or empty.
|
||||||
|
|
||||||
An existing variable cannot be assigned to with `:let`, since that implies a
|
An existing variable cannot be assigned to with `:let`, since that implies a
|
||||||
declaration. Global, window, tab, buffer and Vim variables can only be used
|
declaration. Global, window, tab, buffer and Vim variables can only be used
|
||||||
without `:let`, because they are not really declared, they can also be deleted
|
without `:let`, because they are not really declared, they can also be deleted
|
||||||
@ -210,6 +213,40 @@ at the script level. >
|
|||||||
|
|
||||||
Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be
|
Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be
|
||||||
used to repeat a `:substitute` command.
|
used to repeat a `:substitute` command.
|
||||||
|
*vim9-const*
|
||||||
|
In legacy Vim script "const list = []" would make the variable "list"
|
||||||
|
immutable and also the value. Thus you cannot add items to the list. This
|
||||||
|
differs from what many languages do. Vim9 script does it like TypeScript: only
|
||||||
|
"list" is immutable, the value can be changed.
|
||||||
|
|
||||||
|
One can use `:const!` to make both the variable and the value immutable. Use
|
||||||
|
this for composite structures that you want to make sure will not be modified.
|
||||||
|
|
||||||
|
How this works: >
|
||||||
|
vim9script
|
||||||
|
const list = [1, 2]
|
||||||
|
list = [3, 4] # Error!
|
||||||
|
list[0] = 2 # OK
|
||||||
|
|
||||||
|
const! LIST = [1, 2]
|
||||||
|
LIST = [3, 4] # Error!
|
||||||
|
LIST[0] = 2 # Error!
|
||||||
|
It is common to write constants as ALL_CAPS, but you don't have to.
|
||||||
|
|
||||||
|
The constant only applies to the value itself, not what it refers to. >
|
||||||
|
cont females = ["Mary"]
|
||||||
|
const! NAMES = [["John", "Peter"], females]
|
||||||
|
NAMES[0] = ["Jack"] # Error!
|
||||||
|
NAMES[0][0] = ["Jack"] # Error!
|
||||||
|
NAMES[1] = ["Emma"] # Error!
|
||||||
|
Names[1][0] = "Emma" # OK, now females[0] == "Emma"
|
||||||
|
|
||||||
|
Rationale: TypeScript has no way to make the value immutable. One can use
|
||||||
|
immutable types, but that quickly gets complicated for nested values. And
|
||||||
|
with a type cast the value can be made mutable again, which means there is no
|
||||||
|
guarantee the value won't change. Vim supports immutable values, in legacy
|
||||||
|
script this was done with `:lockvar`. But that is an extra statement and also
|
||||||
|
applies to nested values. Therefore the solution to use `:const!`.
|
||||||
|
|
||||||
*E1092*
|
*E1092*
|
||||||
Declaring more than one variable at a time, using the unpack notation, is
|
Declaring more than one variable at a time, using the unpack notation, is
|
||||||
@ -408,7 +445,7 @@ for using a list or job. This is very much like JavaScript, but there are a
|
|||||||
few exceptions.
|
few exceptions.
|
||||||
|
|
||||||
type TRUE when ~
|
type TRUE when ~
|
||||||
bool v:true
|
bool v:true or 1
|
||||||
number non-zero
|
number non-zero
|
||||||
float non-zero
|
float non-zero
|
||||||
string non-empty
|
string non-empty
|
||||||
@ -946,26 +983,41 @@ declarations: >
|
|||||||
Expression evaluation was already close to what JavaScript and other languages
|
Expression evaluation was already close to what JavaScript and other languages
|
||||||
are doing. Some details are unexpected and can be fixed. For example how the
|
are doing. Some details are unexpected and can be fixed. For example how the
|
||||||
|| and && operators work. Legacy Vim script: >
|
|| and && operators work. Legacy Vim script: >
|
||||||
let result = 44
|
let value = 44
|
||||||
...
|
...
|
||||||
return result || 0 # returns 1
|
let result = value || 0 # result == 1
|
||||||
|
|
||||||
Vim9 script works like JavaScript/TypeScript, keep the value: >
|
Vim9 script works like JavaScript/TypeScript, keep the value: >
|
||||||
let result = 44
|
let value = 44
|
||||||
...
|
...
|
||||||
return result || 0 # returns 44
|
let result = value || 0 # result == 44
|
||||||
|
|
||||||
On the other hand, overloading "+" to use both for addition and string
|
|
||||||
concatenation goes against legacy Vim script and often leads to mistakes.
|
|
||||||
For that reason we will keep using ".." for string concatenation. Lua also
|
|
||||||
uses ".." this way.
|
|
||||||
|
|
||||||
There is no intention to completely match TypeScript syntax and semantics. We
|
There is no intention to completely match TypeScript syntax and semantics. We
|
||||||
just want to take those parts that we can use for Vim and we expect Vim users
|
just want to take those parts that we can use for Vim and we expect Vim users
|
||||||
are happy with. TypeScript is a complex language with its own advantages and
|
will be happy with. TypeScript is a complex language with its own advantages
|
||||||
disadvantages. People used to other languages (Java, Python, etc.) will also
|
and disadvantages. To get an idea of the disadvantages read the book:
|
||||||
find things in TypeScript that they do not like or do not understand. We'll
|
"JavaScript: The Good Parts". Or find the article "TypeScript: the good
|
||||||
try to avoid those things.
|
parts" and read the "Things to avoid" section.
|
||||||
|
|
||||||
|
People used to other languages (Java, Python, etc.) will also find things in
|
||||||
|
TypeScript that they do not like or do not understand. We'll try to avoid
|
||||||
|
those things.
|
||||||
|
|
||||||
|
Specific items from TypeScript we avoid:
|
||||||
|
- Overloading "+", using it both for addition and string concatenation. This
|
||||||
|
goes against legacy Vim script and often leads to mistakes. For that reason
|
||||||
|
we will keep using ".." for string concatenation. Lua also uses ".." this
|
||||||
|
way. And it allows for conversion to string for more values.
|
||||||
|
- TypeScript can use an expression like "99 || 'yes'" in a condition, but
|
||||||
|
cannot assign the value to a boolean. That is inconsistent and can be
|
||||||
|
annoying. Vim recognizes an expression with && or || and allows using the
|
||||||
|
result as a bool.
|
||||||
|
- TypeScript considers an empty string as Falsy, but an empty list or dict as
|
||||||
|
Truthy. That is inconsistent. In Vim an empty list and dict are also
|
||||||
|
Falsy.
|
||||||
|
- TypeScript has various "Readonly" types, which have limited usefulness,
|
||||||
|
since a type cast can remove the immutable nature. Vim locks the value,
|
||||||
|
which is more flexible, but is only checked at runtime.
|
||||||
|
|
||||||
|
|
||||||
Import and Export ~
|
Import and Export ~
|
||||||
|
@ -258,4 +258,12 @@ EXTERN char e_assert_fails_fifth_argument[]
|
|||||||
INIT(= N_("E1116: assert_fails() fifth argument must be a string"));
|
INIT(= N_("E1116: assert_fails() fifth argument must be a string"));
|
||||||
EXTERN char e_cannot_use_bang_with_nested_def[]
|
EXTERN char e_cannot_use_bang_with_nested_def[]
|
||||||
INIT(= N_("E1117: Cannot use ! with nested :def"));
|
INIT(= N_("E1117: Cannot use ! with nested :def"));
|
||||||
|
EXTERN char e_cannot_change_list[]
|
||||||
|
INIT(= N_("E1118: Cannot change list"));
|
||||||
|
EXTERN char e_cannot_change_list_item[]
|
||||||
|
INIT(= N_("E1119: Cannot change list item"));
|
||||||
|
EXTERN char e_cannot_change_dict[]
|
||||||
|
INIT(= N_("E1120: Cannot change dict"));
|
||||||
|
EXTERN char e_cannot_change_dict_item[]
|
||||||
|
INIT(= N_("E1121: Cannot change dict item"));
|
||||||
#endif
|
#endif
|
||||||
|
@ -1200,7 +1200,7 @@ set_var_lval(
|
|||||||
char_u *endp,
|
char_u *endp,
|
||||||
typval_T *rettv,
|
typval_T *rettv,
|
||||||
int copy,
|
int copy,
|
||||||
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
|
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||||
char_u *op)
|
char_u *op)
|
||||||
{
|
{
|
||||||
int cc;
|
int cc;
|
||||||
|
@ -173,7 +173,6 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
|
|||||||
static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
|
static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
|
||||||
static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
|
static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
|
||||||
static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
|
static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
|
||||||
static void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
|
|
||||||
static void delete_var(hashtab_T *ht, hashitem_T *hi);
|
static void delete_var(hashtab_T *ht, hashitem_T *hi);
|
||||||
static void list_one_var(dictitem_T *v, char *prefix, int *first);
|
static void list_one_var(dictitem_T *v, char *prefix, int *first);
|
||||||
static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first);
|
static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first);
|
||||||
@ -709,6 +708,8 @@ ex_let(exarg_T *eap)
|
|||||||
// detect Vim9 assignment without ":let" or ":const"
|
// detect Vim9 assignment without ":let" or ":const"
|
||||||
if (eap->arg == eap->cmd)
|
if (eap->arg == eap->cmd)
|
||||||
flags |= LET_NO_COMMAND;
|
flags |= LET_NO_COMMAND;
|
||||||
|
if (eap->forceit)
|
||||||
|
flags |= LET_FORCEIT;
|
||||||
|
|
||||||
argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE);
|
argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE);
|
||||||
if (argend == NULL)
|
if (argend == NULL)
|
||||||
@ -859,7 +860,7 @@ ex_let_vars(
|
|||||||
int copy, // copy values from "tv", don't move
|
int copy, // copy values from "tv", don't move
|
||||||
int semicolon, // from skip_var_list()
|
int semicolon, // from skip_var_list()
|
||||||
int var_count, // from skip_var_list()
|
int var_count, // from skip_var_list()
|
||||||
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
|
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||||
char_u *op)
|
char_u *op)
|
||||||
{
|
{
|
||||||
char_u *arg = arg_start;
|
char_u *arg = arg_start;
|
||||||
@ -1214,7 +1215,7 @@ ex_let_one(
|
|||||||
char_u *arg, // points to variable name
|
char_u *arg, // points to variable name
|
||||||
typval_T *tv, // value to assign to variable
|
typval_T *tv, // value to assign to variable
|
||||||
int copy, // copy value from "tv"
|
int copy, // copy value from "tv"
|
||||||
int flags, // LET_IS_CONST and/or LET_NO_COMMAND
|
int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||||
char_u *endchars, // valid chars after variable name or NULL
|
char_u *endchars, // valid chars after variable name or NULL
|
||||||
char_u *op) // "+", "-", "." or NULL
|
char_u *op) // "+", "-", "." or NULL
|
||||||
{
|
{
|
||||||
@ -1741,7 +1742,7 @@ do_lock_var(
|
|||||||
* When "check_refcount" is TRUE do not lock a list or dict with a reference
|
* When "check_refcount" is TRUE do not lock a list or dict with a reference
|
||||||
* count larger than 1.
|
* count larger than 1.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
item_lock(typval_T *tv, int deep, int lock, int check_refcount)
|
item_lock(typval_T *tv, int deep, int lock, int check_refcount)
|
||||||
{
|
{
|
||||||
static int recurse = 0;
|
static int recurse = 0;
|
||||||
@ -2937,7 +2938,7 @@ set_var_const(
|
|||||||
type_T *type,
|
type_T *type,
|
||||||
typval_T *tv_arg,
|
typval_T *tv_arg,
|
||||||
int copy, // make copy of value in "tv"
|
int copy, // make copy of value in "tv"
|
||||||
int flags) // LET_IS_CONST and/or LET_NO_COMMAND
|
int flags) // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
|
||||||
{
|
{
|
||||||
typval_T *tv = tv_arg;
|
typval_T *tv = tv_arg;
|
||||||
typval_T bool_tv;
|
typval_T bool_tv;
|
||||||
@ -3124,8 +3125,8 @@ set_var_const(
|
|||||||
init_tv(tv);
|
init_tv(tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ":const var = val" locks the value, but not in Vim9 script
|
// ":const var = val" locks the value; in Vim9 script only with ":const!"
|
||||||
if ((flags & LET_IS_CONST) && !vim9script)
|
if ((flags & LET_IS_CONST) && (!vim9script || (flags & LET_FORCEIT)))
|
||||||
// Like :lockvar! name: lock the value and what it contains, but only
|
// Like :lockvar! name: lock the value and what it contains, but only
|
||||||
// if the reference count is up to one. That locks only literal
|
// if the reference count is up to one. That locks only literal
|
||||||
// values.
|
// values.
|
||||||
|
@ -398,7 +398,7 @@ EXCMD(CMD_confirm, "confirm", ex_wrongmodifier,
|
|||||||
EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_CMDWIN|EX_LOCK_OK,
|
EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_CMDWIN|EX_LOCK_OK,
|
||||||
ADDR_NONE),
|
ADDR_NONE),
|
||||||
EXCMD(CMD_const, "const", ex_let,
|
EXCMD(CMD_const, "const", ex_let,
|
||||||
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
EX_EXTRA|EX_BANG|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
|
||||||
ADDR_NONE),
|
ADDR_NONE),
|
||||||
EXCMD(CMD_copen, "copen", ex_copen,
|
EXCMD(CMD_copen, "copen", ex_copen,
|
||||||
EX_RANGE|EX_COUNT|EX_TRLBAR,
|
EX_RANGE|EX_COUNT|EX_TRLBAR,
|
||||||
|
@ -23,6 +23,7 @@ void ex_unlet(exarg_T *eap);
|
|||||||
void ex_lockvar(exarg_T *eap);
|
void ex_lockvar(exarg_T *eap);
|
||||||
void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
|
void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
|
||||||
int do_unlet(char_u *name, int forceit);
|
int do_unlet(char_u *name, int forceit);
|
||||||
|
void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
|
||||||
void del_menutrans_vars(void);
|
void del_menutrans_vars(void);
|
||||||
char_u *get_user_var_name(expand_T *xp, int idx);
|
char_u *get_user_var_name(expand_T *xp, int idx);
|
||||||
char *get_var_special_name(int nr);
|
char *get_var_special_name(int nr);
|
||||||
@ -65,7 +66,7 @@ void unref_var_dict(dict_T *dict);
|
|||||||
void vars_clear(hashtab_T *ht);
|
void vars_clear(hashtab_T *ht);
|
||||||
void vars_clear_ext(hashtab_T *ht, int free_val);
|
void vars_clear_ext(hashtab_T *ht, int free_val);
|
||||||
void set_var(char_u *name, typval_T *tv, int copy);
|
void set_var(char_u *name, typval_T *tv, int copy);
|
||||||
void set_var_const(char_u *name, type_T *type, typval_T *tv, int copy, int flags);
|
void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags);
|
||||||
int var_check_ro(int flags, char_u *name, int use_gettext);
|
int var_check_ro(int flags, char_u *name, int use_gettext);
|
||||||
int var_check_fixed(int flags, char_u *name, int use_gettext);
|
int var_check_fixed(int flags, char_u *name, int use_gettext);
|
||||||
int var_wrong_func_name(char_u *name, int new_var);
|
int var_wrong_func_name(char_u *name, int new_var);
|
||||||
|
@ -828,10 +828,50 @@ def Test_const()
|
|||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
const list = [1, 2, 3]
|
const list = [1, 2, 3]
|
||||||
list[0] = 4
|
list[0] = 4
|
||||||
|
list->assert_equal([4, 2, 3])
|
||||||
|
const! other = [5, 6, 7]
|
||||||
|
other->assert_equal([5, 6, 7])
|
||||||
END
|
END
|
||||||
CheckDefAndScriptSuccess(lines)
|
CheckDefAndScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_const_bang()
|
||||||
|
let lines =<< trim END
|
||||||
|
const! var = 234
|
||||||
|
var = 99
|
||||||
|
END
|
||||||
|
CheckDefExecFailure(lines, 'E1018:', 2)
|
||||||
|
CheckScriptFailure(['vim9script'] + lines, 'E46:', 3)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
const! ll = [2, 3, 4]
|
||||||
|
ll[0] = 99
|
||||||
|
END
|
||||||
|
CheckDefExecFailure(lines, 'E1119:', 2)
|
||||||
|
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
const! ll = [2, 3, 4]
|
||||||
|
ll[3] = 99
|
||||||
|
END
|
||||||
|
CheckDefExecFailure(lines, 'E1118:', 2)
|
||||||
|
CheckScriptFailure(['vim9script'] + lines, 'E684:', 3)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
const! dd = #{one: 1, two: 2}
|
||||||
|
dd["one"] = 99
|
||||||
|
END
|
||||||
|
CheckDefExecFailure(lines, 'E1121:', 2)
|
||||||
|
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
const! dd = #{one: 1, two: 2}
|
||||||
|
dd["three"] = 99
|
||||||
|
END
|
||||||
|
CheckDefExecFailure(lines, 'E1120:')
|
||||||
|
CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_range_no_colon()
|
def Test_range_no_colon()
|
||||||
CheckDefFailure(['%s/a/b/'], 'E1050:')
|
CheckDefFailure(['%s/a/b/'], 'E1050:')
|
||||||
CheckDefFailure(['+ s/a/b/'], 'E1050:')
|
CheckDefFailure(['+ s/a/b/'], 'E1050:')
|
||||||
|
@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1685,
|
||||||
/**/
|
/**/
|
||||||
1684,
|
1684,
|
||||||
/**/
|
/**/
|
||||||
|
@ -2136,7 +2136,8 @@ typedef enum {
|
|||||||
|
|
||||||
// Flags for assignment functions.
|
// Flags for assignment functions.
|
||||||
#define LET_IS_CONST 1 // ":const"
|
#define LET_IS_CONST 1 // ":const"
|
||||||
#define LET_NO_COMMAND 2 // "var = expr" without ":let" or ":const"
|
#define LET_FORCEIT 2 // ":const!" (LET_IS_CONST is also set)
|
||||||
|
#define LET_NO_COMMAND 4 // "var = expr" without ":let" or ":const"
|
||||||
|
|
||||||
#include "ex_cmds.h" // Ex command defines
|
#include "ex_cmds.h" // Ex command defines
|
||||||
#include "spell.h" // spell checking stuff
|
#include "spell.h" // spell checking stuff
|
||||||
|
@ -58,6 +58,8 @@ typedef enum {
|
|||||||
ISN_UNLET, // unlet variable isn_arg.unlet.ul_name
|
ISN_UNLET, // unlet variable isn_arg.unlet.ul_name
|
||||||
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
|
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
|
||||||
|
|
||||||
|
ISN_LOCKCONST, // lock constant value
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
ISN_PUSHNR, // push number isn_arg.number
|
ISN_PUSHNR, // push number isn_arg.number
|
||||||
ISN_PUSHBOOL, // push bool value isn_arg.number
|
ISN_PUSHBOOL, // push bool value isn_arg.number
|
||||||
|
@ -1108,6 +1108,20 @@ generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate an ISN_LOCKCONST instruction.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
generate_LOCKCONST(cctx_T *cctx)
|
||||||
|
{
|
||||||
|
isn_T *isn;
|
||||||
|
|
||||||
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
|
if ((isn = generate_instr(cctx, ISN_LOCKCONST)) == NULL)
|
||||||
|
return FAIL;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_LOADS instruction.
|
* Generate an ISN_LOADS instruction.
|
||||||
*/
|
*/
|
||||||
@ -4342,7 +4356,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
|
|||||||
ufunc_T *ufunc;
|
ufunc_T *ufunc;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (*name_start == '!')
|
if (eap->forceit)
|
||||||
{
|
{
|
||||||
emsg(_(e_cannot_use_bang_with_nested_def));
|
emsg(_(e_cannot_use_bang_with_nested_def));
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -5232,6 +5246,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (is_decl && eap->forceit && cmdidx == CMD_const
|
||||||
|
&& (dest == dest_script || dest == dest_local))
|
||||||
|
// ":const! var": lock the value, but not referenced variables
|
||||||
|
generate_LOCKCONST(cctx);
|
||||||
|
|
||||||
switch (dest)
|
switch (dest)
|
||||||
{
|
{
|
||||||
case dest_option:
|
case dest_option:
|
||||||
@ -6362,13 +6381,8 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
|
|||||||
char_u *line = arg;
|
char_u *line = arg;
|
||||||
linenr_T lnum;
|
linenr_T lnum;
|
||||||
char *errormsg;
|
char *errormsg;
|
||||||
int above = FALSE;
|
int above = eap->forceit;
|
||||||
|
|
||||||
if (*arg == '!')
|
|
||||||
{
|
|
||||||
above = TRUE;
|
|
||||||
line = skipwhite(arg + 1);
|
|
||||||
}
|
|
||||||
eap->regname = *line;
|
eap->regname = *line;
|
||||||
|
|
||||||
if (eap->regname == '=')
|
if (eap->regname == '=')
|
||||||
@ -6411,7 +6425,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
|
|||||||
|
|
||||||
if (eap->cmdidx >= 0 && eap->cmdidx < CMD_SIZE)
|
if (eap->cmdidx >= 0 && eap->cmdidx < CMD_SIZE)
|
||||||
{
|
{
|
||||||
long argt = excmd_get_argt(eap->cmdidx);
|
long argt = eap->argt;
|
||||||
int usefilter = FALSE;
|
int usefilter = FALSE;
|
||||||
|
|
||||||
has_expr = argt & (EX_XFILE | EX_EXPAND);
|
has_expr = argt & (EX_XFILE | EX_EXPAND);
|
||||||
@ -6870,8 +6884,6 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p = skipwhite(p);
|
|
||||||
|
|
||||||
if (cctx.ctx_had_return
|
if (cctx.ctx_had_return
|
||||||
&& ea.cmdidx != CMD_elseif
|
&& ea.cmdidx != CMD_elseif
|
||||||
&& ea.cmdidx != CMD_else
|
&& ea.cmdidx != CMD_else
|
||||||
@ -6886,6 +6898,18 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
|||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p = skipwhite(p);
|
||||||
|
if (ea.cmdidx != CMD_SIZE
|
||||||
|
&& ea.cmdidx != CMD_write && ea.cmdidx != CMD_read)
|
||||||
|
{
|
||||||
|
ea.argt = excmd_get_argt(ea.cmdidx);
|
||||||
|
if ((ea.argt & EX_BANG) && *p == '!')
|
||||||
|
{
|
||||||
|
ea.forceit = TRUE;
|
||||||
|
p = skipwhite(p + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (ea.cmdidx)
|
switch (ea.cmdidx)
|
||||||
{
|
{
|
||||||
case CMD_def:
|
case CMD_def:
|
||||||
@ -7309,6 +7333,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_LOADTDICT:
|
case ISN_LOADTDICT:
|
||||||
case ISN_LOADV:
|
case ISN_LOADV:
|
||||||
case ISN_LOADWDICT:
|
case ISN_LOADWDICT:
|
||||||
|
case ISN_LOCKCONST:
|
||||||
case ISN_MEMBER:
|
case ISN_MEMBER:
|
||||||
case ISN_NEGATENR:
|
case ISN_NEGATENR:
|
||||||
case ISN_NEWDICT:
|
case ISN_NEWDICT:
|
||||||
|
@ -677,6 +677,21 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if "lock" is VAR_LOCKED or VAR_FIXED. If so give an error and return
|
||||||
|
* TRUE.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
error_if_locked(int lock, char *error)
|
||||||
|
{
|
||||||
|
if (lock & (VAR_LOCKED | VAR_FIXED))
|
||||||
|
{
|
||||||
|
emsg(_(error));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store "tv" in variable "name".
|
* Store "tv" in variable "name".
|
||||||
* This is for s: and g: variables.
|
* This is for s: and g: variables.
|
||||||
@ -1455,12 +1470,12 @@ call_def_function(
|
|||||||
typval_T *tv_list = STACK_TV_BOT(-1);
|
typval_T *tv_list = STACK_TV_BOT(-1);
|
||||||
list_T *list = tv_list->vval.v_list;
|
list_T *list = tv_list->vval.v_list;
|
||||||
|
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
if (lidx < 0 && list->lv_len + lidx >= 0)
|
if (lidx < 0 && list->lv_len + lidx >= 0)
|
||||||
// negative index is relative to the end
|
// negative index is relative to the end
|
||||||
lidx = list->lv_len + lidx;
|
lidx = list->lv_len + lidx;
|
||||||
if (lidx < 0 || lidx > list->lv_len)
|
if (lidx < 0 || lidx > list->lv_len)
|
||||||
{
|
{
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
|
||||||
semsg(_(e_listidx), lidx);
|
semsg(_(e_listidx), lidx);
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
@ -1469,12 +1484,18 @@ call_def_function(
|
|||||||
{
|
{
|
||||||
listitem_T *li = list_find(list, lidx);
|
listitem_T *li = list_find(list, lidx);
|
||||||
|
|
||||||
|
if (error_if_locked(li->li_tv.v_lock,
|
||||||
|
e_cannot_change_list_item))
|
||||||
|
goto failed;
|
||||||
// overwrite existing list item
|
// overwrite existing list item
|
||||||
clear_tv(&li->li_tv);
|
clear_tv(&li->li_tv);
|
||||||
li->li_tv = *tv;
|
li->li_tv = *tv;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (error_if_locked(list->lv_lock,
|
||||||
|
e_cannot_change_list))
|
||||||
|
goto failed;
|
||||||
// append to list, only fails when out of memory
|
// append to list, only fails when out of memory
|
||||||
if (list_append_tv(list, tv) == FAIL)
|
if (list_append_tv(list, tv) == FAIL)
|
||||||
goto failed;
|
goto failed;
|
||||||
@ -1495,9 +1516,9 @@ call_def_function(
|
|||||||
dict_T *dict = tv_dict->vval.v_dict;
|
dict_T *dict = tv_dict->vval.v_dict;
|
||||||
dictitem_T *di;
|
dictitem_T *di;
|
||||||
|
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
if (dict == NULL)
|
if (dict == NULL)
|
||||||
{
|
{
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
|
||||||
emsg(_(e_dictionary_not_set));
|
emsg(_(e_dictionary_not_set));
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
@ -1507,12 +1528,18 @@ call_def_function(
|
|||||||
di = dict_find(dict, key, -1);
|
di = dict_find(dict, key, -1);
|
||||||
if (di != NULL)
|
if (di != NULL)
|
||||||
{
|
{
|
||||||
|
if (error_if_locked(di->di_tv.v_lock,
|
||||||
|
e_cannot_change_dict_item))
|
||||||
|
goto failed;
|
||||||
// overwrite existing value
|
// overwrite existing value
|
||||||
clear_tv(&di->di_tv);
|
clear_tv(&di->di_tv);
|
||||||
di->di_tv = *tv;
|
di->di_tv = *tv;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (error_if_locked(dict->dv_lock,
|
||||||
|
e_cannot_change_dict))
|
||||||
|
goto failed;
|
||||||
// add to dict, only fails when out of memory
|
// add to dict, only fails when out of memory
|
||||||
if (dict_add_tv(dict, (char *)key, tv) == FAIL)
|
if (dict_add_tv(dict, (char *)key, tv) == FAIL)
|
||||||
goto failed;
|
goto failed;
|
||||||
@ -1603,6 +1630,10 @@ call_def_function(
|
|||||||
vim_unsetenv(iptr->isn_arg.unlet.ul_name);
|
vim_unsetenv(iptr->isn_arg.unlet.ul_name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ISN_LOCKCONST:
|
||||||
|
item_lock(STACK_TV_BOT(-1), 100, TRUE, TRUE);
|
||||||
|
break;
|
||||||
|
|
||||||
// create a list from items on the stack; uses a single allocation
|
// create a list from items on the stack; uses a single allocation
|
||||||
// for the list header and the items
|
// for the list header and the items
|
||||||
case ISN_NEWLIST:
|
case ISN_NEWLIST:
|
||||||
@ -3025,6 +3056,9 @@ ex_disassemble(exarg_T *eap)
|
|||||||
iptr->isn_arg.unlet.ul_forceit ? "!" : "",
|
iptr->isn_arg.unlet.ul_forceit ? "!" : "",
|
||||||
iptr->isn_arg.unlet.ul_name);
|
iptr->isn_arg.unlet.ul_name);
|
||||||
break;
|
break;
|
||||||
|
case ISN_LOCKCONST:
|
||||||
|
smsg("%4d LOCKCONST", current);
|
||||||
|
break;
|
||||||
case ISN_NEWLIST:
|
case ISN_NEWLIST:
|
||||||
smsg("%4d NEWLIST size %lld", current,
|
smsg("%4d NEWLIST size %lld", current,
|
||||||
(long long)(iptr->isn_arg.number));
|
(long long)(iptr->isn_arg.number));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user