Skip to content

Commit

Permalink
Merge neovim#10313 from rhysd/const-patches
Browse files Browse the repository at this point in the history
vim-patch:8.1.1539,8.1.1543,8.1.1554 for :const from Vim
  • Loading branch information
justinmk authored Jun 24, 2019
2 parents 433c136 + 3e8bc4a commit f2e3849
Show file tree
Hide file tree
Showing 4 changed files with 373 additions and 33 deletions.
24 changes: 23 additions & 1 deletion runtime/doc/eval.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9629,7 +9629,29 @@ This does NOT work: >
No error message is given for a non-existing
variable, also without !.
If the system does not support deleting an environment
variable, it is made emtpy.
variable, it is made empty.

*:cons* *:const*
:cons[t] {var-name} = {expr1}
:cons[t] [{name1}, {name2}, ...] = {expr1}
:cons[t] [{name}, ..., ; {lastname}] = {expr1}
Similar to |:let|, but additionally lock the variable
after setting the value. This is the same as locking
the variable with |:lockvar| just after |:let|, thus: >
:const x = 1
< is equivalent to: >
:let x = 1
:lockvar 1 x
< This is useful if you want to make sure the variable
is not modified.
*E995*
|:const| does not allow to for changing a variable. >
:let x = 1
:const x = 2 " Error!
< *E996*
Note that environment variables, option values and
register values cannot be used here, since they cannot
be locked.

:lockv[ar][!] [depth] {name} ... *:lockvar* *:lockv*
Lock the internal variable {name}. Locking means that
Expand Down
139 changes: 107 additions & 32 deletions src/nvim/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ static char *e_funcref = N_("E718: Funcref required");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
static char *e_nofunc = N_("E130: Unknown function: %s");
static char *e_illvar = N_("E461: Illegal variable name: %s");
static char *e_cannot_mod = N_("E995: Cannot modify existing variable");
static const char *e_readonlyvar = N_(
"E46: Cannot change read-only variable \"%.*s\"");

Expand Down Expand Up @@ -776,10 +777,11 @@ var_redir_start(
did_emsg = FALSE;
tv.v_type = VAR_STRING;
tv.vval.v_string = (char_u *)"";
if (append)
set_var_lval(redir_lval, redir_endp, &tv, TRUE, (char_u *)".");
else
set_var_lval(redir_lval, redir_endp, &tv, TRUE, (char_u *)"=");
if (append) {
set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)".");
} else {
set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)"=");
}
clear_lval(redir_lval);
err = did_emsg;
did_emsg |= save_emsg;
Expand Down Expand Up @@ -837,7 +839,7 @@ void var_redir_stop(void)
redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval,
false, false, 0, FNE_CHECK_START);
if (redir_endp != NULL && redir_lval->ll_name != NULL) {
set_var_lval(redir_lval, redir_endp, &tv, false, (char_u *)".");
set_var_lval(redir_lval, redir_endp, &tv, false, false, (char_u *)".");
}
clear_lval(redir_lval);
}
Expand Down Expand Up @@ -1518,20 +1520,32 @@ int eval_foldexpr(char_u *arg, int *cp)
return (int)retval;
}

/*
* ":let" list all variable values
* ":let var1 var2" list variable values
* ":let var = expr" assignment command.
* ":let var += expr" assignment command.
* ":let var -= expr" assignment command.
* ":let var *= expr" assignment command.
* ":let var /= expr" assignment command.
* ":let var %= expr" assignment command.
* ":let var .= expr" assignment command.
* ":let var ..= expr" assignment command.
* ":let [var1, var2] = expr" unpack list.
*/
// ":cons[t] var = expr1" define constant
// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list
// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list
void ex_const(exarg_T *eap)
{
ex_let_const(eap, true);
}

// ":let" list all variable values
// ":let var1 var2" list variable values
// ":let var = expr" assignment command.
// ":let var += expr" assignment command.
// ":let var -= expr" assignment command.
// ":let var *= expr" assignment command.
// ":let var /= expr" assignment command.
// ":let var %= expr" assignment command.
// ":let var .= expr" assignment command.
// ":let var ..= expr" assignment command.
// ":let [var1, var2] = expr" unpack list.
// ":let [name, ..., ; lastname] = expr" unpack list.
void ex_let(exarg_T *eap)
{
ex_let_const(eap, false);
}

static void ex_let_const(exarg_T *eap, const bool is_const)
{
char_u *arg = eap->arg;
char_u *expr = NULL;
Expand Down Expand Up @@ -1594,7 +1608,8 @@ void ex_let(exarg_T *eap)
}
emsg_skip--;
} else if (i != FAIL) {
(void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, op);
(void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count,
is_const, op);
tv_clear(&rettv);
}
}
Expand All @@ -1612,9 +1627,10 @@ static int
ex_let_vars(
char_u *arg_start,
typval_T *tv,
int copy, /* copy values from "tv", don't move */
int semicolon, /* from skip_var_list() */
int var_count, /* from skip_var_list() */
int copy, // copy values from "tv", don't move
int semicolon, // from skip_var_list()
int var_count, // from skip_var_list()
int is_const, // lock variables for :const
char_u *nextchars
)
{
Expand All @@ -1625,8 +1641,9 @@ ex_let_vars(
/*
* ":let var = expr" or ":for var in list"
*/
if (ex_let_one(arg, tv, copy, nextchars, nextchars) == NULL)
if (ex_let_one(arg, tv, copy, is_const, nextchars, nextchars) == NULL) {
return FAIL;
}
return OK;
}

Expand Down Expand Up @@ -1654,8 +1671,8 @@ ex_let_vars(
size_t rest_len = tv_list_len(l);
while (*arg != ']') {
arg = skipwhite(arg + 1);
arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]",
nextchars);
arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const,
(const char_u *)",;]", nextchars);
if (arg == NULL) {
return FAIL;
}
Expand All @@ -1677,7 +1694,7 @@ ex_let_vars(
ltv.vval.v_list = rest_list;
tv_list_ref(rest_list);

arg = ex_let_one(skipwhite(arg + 1), &ltv, false,
arg = ex_let_one(skipwhite(arg + 1), &ltv, false, is_const,
(char_u *)"]", nextchars);
tv_clear(&ltv);
if (arg == NULL) {
Expand Down Expand Up @@ -1933,8 +1950,8 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
/// @return a pointer to the char just after the var name or NULL in case of
/// error.
static char_u *ex_let_one(char_u *arg, typval_T *const tv,
const bool copy, const char_u *const endchars,
const char_u *const op)
const bool copy, const bool is_const,
const char_u *const endchars, const char_u *const op)
FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
{
char_u *arg_end = NULL;
Expand All @@ -1946,6 +1963,10 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
* ":let $VAR = expr": Set environment variable.
*/
if (*arg == '$') {
if (is_const) {
EMSG(_("E996: Cannot lock an environment variable"));
return NULL;
}
// Find the end of the name.
arg++;
char *name = (char *)arg;
Expand Down Expand Up @@ -1991,6 +2012,10 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
// ":let &l:option = expr": Set local option value.
// ":let &g:option = expr": Set global option value.
} else if (*arg == '&') {
if (is_const) {
EMSG(_("E996: Cannot lock an option"));
return NULL;
}
// Find the end of the name.
char *const p = (char *)find_option_end((const char **)&arg, &opt_flags);
if (p == NULL
Expand Down Expand Up @@ -2041,6 +2066,10 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
}
// ":let @r = expr": Set register contents.
} else if (*arg == '@') {
if (is_const) {
EMSG(_("E996: Cannot lock a register"));
return NULL;
}
arg++;
if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) {
emsgf(_(e_letwrong), op);
Expand Down Expand Up @@ -2080,7 +2109,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) {
EMSG(_(e_letunexp));
} else {
set_var_lval(&lv, p, tv, copy, op);
set_var_lval(&lv, p, tv, copy, is_const, op);
arg_end = p;
}
}
Expand Down Expand Up @@ -2445,7 +2474,7 @@ static void clear_lval(lval_T *lp)
* "%" for "%=", "." for ".=" or "=" for "=".
*/
static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
int copy, const char_u *op)
int copy, const bool is_const, const char_u *op)
{
int cc;
listitem_T *ri;
Expand All @@ -2457,6 +2486,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
if (op != NULL && *op != '=') {
typval_T tv;

if (is_const) {
EMSG(_(e_cannot_mod));
*endp = cc;
return;
}

// handle +=, -=, *=, /=, %= and .=
di = NULL;
if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name),
Expand All @@ -2472,7 +2507,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
tv_clear(&tv);
}
} else {
set_var(lp->ll_name, lp->ll_name_len, rettv, copy);
set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const);
}
*endp = cc;
} else if (tv_check_lock(lp->ll_newkey == NULL
Expand All @@ -2483,6 +2518,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;

if (is_const) {
EMSG(_("E996: Cannot lock a range"));
return;
}

// Check whether any of the list items is locked
for (ri = tv_list_first(rettv->vval.v_list);
ri != NULL && ll_li != NULL; ) {
Expand Down Expand Up @@ -2538,6 +2578,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
dict_T *dict = lp->ll_dict;
bool watched = tv_dict_is_watched(dict);

if (is_const) {
EMSG(_("E996: Cannot lock a list or dict"));
return;
}

// Assign to a List or Dictionary item.
if (lp->ll_newkey != NULL) {
if (op != NULL && *op != '=') {
Expand Down Expand Up @@ -2661,7 +2706,7 @@ bool next_for_item(void *fi_void, char_u *arg)
} else {
fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item);
return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true,
fi->fi_semicolon, fi->fi_varcount, NULL) == OK);
fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK);
}
}

Expand Down Expand Up @@ -20111,6 +20156,24 @@ static void list_one_var_a(const char *prefix, const char *name,
static void set_var(const char *name, const size_t name_len, typval_T *const tv,
const bool copy)
FUNC_ATTR_NONNULL_ALL
{
set_var_const(name, name_len, tv, copy, false);
}

/// Set variable to the given value
///
/// If the variable already exists, the value is updated. Otherwise the variable
/// is created.
///
/// @param[in] name Variable name to set.
/// @param[in] name_len Length of the variable name.
/// @param tv Variable value.
/// @param[in] copy True if value in tv is to be copied.
/// @param[in] is_const True if value in tv is to be locked.
static void set_var_const(const char *name, const size_t name_len,
typval_T *const tv, const bool copy,
const bool is_const)
FUNC_ATTR_NONNULL_ALL
{
dictitem_T *v;
hashtab_T *ht;
Expand All @@ -20137,6 +20200,11 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,

typval_T oldtv = TV_INITIAL_VALUE;
if (v != NULL) {
if (is_const) {
EMSG(_(e_cannot_mod));
return;
}

// existing variable, need to clear the value
if (var_check_ro(v->di_flags, name, name_len)
|| tv_check_lock(v->di_tv.v_lock, name, name_len)) {
Expand Down Expand Up @@ -20203,6 +20271,9 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,
return;
}
v->di_flags = DI_FLAGS_ALLOC;
if (is_const) {
v->di_flags |= DI_FLAGS_LOCK;
}
}

if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) {
Expand All @@ -20221,6 +20292,10 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,
tv_clear(&oldtv);
}
}

if (is_const) {
v->di_tv.v_lock |= VAR_LOCKED;
}
}

/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX)
Expand Down
6 changes: 6 additions & 0 deletions src/nvim/ex_cmds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,12 @@ return {
addr_type=ADDR_LINES,
func='ex_wrongmodifier',
},
{
command='const',
flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN),
addr_type=ADDR_LINES,
func='ex_const',
},
{
command='copen',
flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR),
Expand Down
Loading

0 comments on commit f2e3849

Please sign in to comment.