aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/eval.c84
-rw-r--r--src/nvim/version.c2
-rw-r--r--test/functional/legacy/eval_spec.lua18
3 files changed, 72 insertions, 32 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 982c14cd08..7273acb839 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -173,6 +173,7 @@ static char *e_illvar = N_("E461: Illegal variable name: %s");
static char *e_float_as_string = N_("E806: using Float as a String");
static char_u * const empty_string = (char_u *)"";
+static char_u * const namespace_char = (char_u *)"abglstvw";
static dictitem_T globvars_var; /* variable used for g: */
#define globvarht globvardict.dv_hashtab
@@ -17801,21 +17802,28 @@ static int get_env_len(char_u **arg)
return len;
}
-/*
- * Get the length of the name of a function or internal variable.
- * "arg" is advanced to the first non-white character after the name.
- * Return 0 if something is wrong.
- */
-static int get_id_len(char_u **arg)
-{
- char_u *p;
+// Get the length of the name of a function or internal variable.
+// "arg" is advanced to the first non-white character after the name.
+// Return 0 if something is wrong.
+static int get_id_len(char_u **arg) {
+ char_u *p;
int len;
- /* Find the end of the name. */
- for (p = *arg; eval_isnamec(*p); ++p)
- ;
- if (p == *arg) /* no name found */
+ // Find the end of the name.
+ for (p = *arg; eval_isnamec(*p); p++) {
+ if (*p == ':') {
+ // "s:" is start of "s:var", but "n:" is not and can be used in
+ // slice "[n:]". Also "xx:" is not a namespace.
+ len = (int)(p - *arg);
+ if (len > 1
+ || (len == 1 && vim_strchr(namespace_char, **arg) == NULL)) {
+ break;
+ }
+ }
+ }
+ if (p == *arg) { // no name found
return 0;
+ }
len = (int)(p - *arg);
*arg = skipwhite(p);
@@ -17886,28 +17894,28 @@ static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose)
return len;
}
-/*
- * Find the end of a variable or function name, taking care of magic braces.
- * If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the
- * start and end of the first magic braces item.
- * "flags" can have FNE_INCL_BR and FNE_CHECK_START.
- * Return a pointer to just after the name. Equal to "arg" if there is no
- * valid name.
- */
+// Find the end of a variable or function name, taking care of magic braces.
+// If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the
+// start and end of the first magic braces item.
+// "flags" can have FNE_INCL_BR and FNE_CHECK_START.
+// Return a pointer to just after the name. Equal to "arg" if there is no
+// valid name.
static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags)
{
int mb_nest = 0;
int br_nest = 0;
- char_u *p;
+ char_u *p;
+ int len;
if (expr_start != NULL) {
*expr_start = NULL;
*expr_end = NULL;
}
- /* Quick check for valid starting character. */
- if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{')
+ // Quick check for valid starting character.
+ if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') {
return arg;
+ }
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
@@ -17922,30 +17930,44 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end
if (*p == NUL)
break;
} else if (*p == '"') {
- /* skip over "str\"ing" to avoid counting [ and ] inside it. */
- for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p))
- if (*p == '\\' && p[1] != NUL)
+ // skip over "str\"ing" to avoid counting [ and ] inside it.
+ for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) {
+ if (*p == '\\' && p[1] != NUL) {
++p;
- if (*p == NUL)
+ }
+ }
+ if (*p == NUL) {
+ break;
+ }
+ } else if (br_nest == 0 && mb_nest == 0 && *p == ':') {
+ // "s:" is start of "s:var", but "n:" is not and can be used in
+ // slice "[n:]". Also "xx:" is not a namespace.
+ len = (int)(p - arg);
+ if (len > 1
+ || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) {
break;
+ }
}
if (mb_nest == 0) {
- if (*p == '[')
+ if (*p == '[') {
++br_nest;
- else if (*p == ']')
+ } else if (*p == ']') {
--br_nest;
+ }
}
if (br_nest == 0) {
if (*p == '{') {
mb_nest++;
- if (expr_start != NULL && *expr_start == NULL)
+ if (expr_start != NULL && *expr_start == NULL) {
*expr_start = p;
+ }
} else if (*p == '}') {
mb_nest--;
- if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL)
+ if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) {
*expr_end = p;
+ }
}
}
}
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 80b1b236dd..efd3c230a7 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -185,7 +185,7 @@ static int included_patches[] = {
// 1108,
// 1107,
// 1106 NA
- // 1105,
+ 1105,
// 1104 NA
// 1103 NA
// 1102,
diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua
index 1c81b47ed6..9304e3b331 100644
--- a/test/functional/legacy/eval_spec.lua
+++ b/test/functional/legacy/eval_spec.lua
@@ -693,4 +693,22 @@ describe('eval', function()
start:
6]])
end)
+
+ it('substring and variable name', function()
+ execute("let str = 'abcdef'")
+ execute('let n = 3')
+ eq('def', eval('str[n:]'))
+ eq('abcd', eval('str[:n]'))
+ eq('d', eval('str[n:n]'))
+ execute('unlet n')
+ execute('let nn = 3')
+ eq('def', eval('str[nn:]'))
+ eq('abcd', eval('str[:nn]'))
+ eq('d', eval('str[nn:nn]'))
+ execute('unlet nn')
+ execute('let b:nn = 4')
+ eq('ef', eval('str[b:nn:]'))
+ eq('abcde', eval('str[:b:nn]'))
+ eq('e', eval('str[b:nn:b:nn]'))
+ end)
end)