diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 93 | ||||
-rw-r--r-- | src/nvim/eval_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/fileio.c | 5 | ||||
-rw-r--r-- | src/nvim/path.c | 39 | ||||
-rw-r--r-- | src/nvim/regexp.c | 101 | ||||
-rw-r--r-- | src/nvim/strings.c | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test55.in | 160 | ||||
-rw-r--r-- | src/nvim/testdir/test55.ok | 58 | ||||
-rw-r--r-- | src/nvim/version.c | 16 | ||||
-rw-r--r-- | src/nvim/window.c | 11 |
10 files changed, 396 insertions, 104 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 219bd38d82..a1c5f958d1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2890,9 +2890,12 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) else if (do_unlet(lp->ll_name, forceit) == FAIL) ret = FAIL; *name_end = cc; - } else if (tv_check_lock(lp->ll_tv->v_lock, lp->ll_name)) + } else if ((lp->ll_list != NULL + && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name)) + || (lp->ll_dict != NULL + && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name))) { return FAIL; - else if (lp->ll_range) { + } else if (lp->ll_range) { listitem_T *li; listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; @@ -2953,17 +2956,30 @@ int do_unlet(char_u *name, int forceit) hashtab_T *ht; hashitem_T *hi; char_u *varname; + dict_T *d; dictitem_T *di; dict_T *dict; ht = find_var_ht_dict(name, &varname, &dict); if (ht != NULL && *varname != NUL) { + if (ht == &globvarht) { + d = &globvardict; + } else if (current_funccal != NULL + && ht == ¤t_funccal->l_vars.dv_hashtab) { + d = ¤t_funccal->l_vars; + } else { + di = find_var_in_ht(ht, *name, (char_u *)"", false); + d = di->di_tv.vval.v_dict; + } + hi = hash_find(ht, varname); if (!HASHITEM_EMPTY(hi)) { di = HI2DI(hi); if (var_check_fixed(di->di_flags, name) - || var_check_ro(di->di_flags, name)) + || var_check_ro(di->di_flags, name) + || tv_check_lock(d->dv_lock, name)) { return FAIL; + } typval_T oldtv; bool watched = is_watched(dict); @@ -6055,7 +6071,7 @@ dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET #ifndef __clang_analyzer__ STRCPY(di->di_key, key); #endif - di->di_flags = 0; + di->di_flags = DI_FLAGS_ALLOC; return di; } @@ -6067,7 +6083,7 @@ static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key)); STRCPY(di->di_key, org->di_key); - di->di_flags = 0; + di->di_flags = DI_FLAGS_ALLOC; copy_tv(&org->di_tv, &di->di_tv); return di; @@ -6095,7 +6111,9 @@ static void dictitem_remove(dict_T *dict, dictitem_T *item) void dictitem_free(dictitem_T *item) { clear_tv(&item->di_tv); - xfree(item); + if (item->di_flags & DI_FLAGS_ALLOC) { + xfree(item); + } } /// Make a copy of dictionary @@ -9212,6 +9230,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) hashitem_T *hi2; int todo; bool watched = is_watched(d1); + char *arg_errmsg = N_("extend() argument"); todo = (int)d2->dv_hashtab.ht_used; for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { @@ -9245,6 +9264,11 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) } else if (*action == 'f' && HI2DI(hi2) != di1) { typval_T oldtv; + if (tv_check_lock(di1->di_tv.v_lock, (char_u *)_(arg_errmsg)) + || var_check_ro(di1->di_flags, (char_u *)_(arg_errmsg))) { + break; + } + if (watched) { copy_tv(&di1->di_tv, &oldtv); } @@ -9460,12 +9484,14 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) + || (!map && tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg)))) { return; + } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL - || tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) + || (!map && tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))) { return; + } } else { EMSG2(_(e_listdictarg), ermsg); return; @@ -9494,17 +9520,26 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) for (hi = ht->ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { --todo; + di = HI2DI(hi); - if (tv_check_lock(di->di_tv.v_lock, - (char_u *)_(arg_errmsg))) + if (map + && (tv_check_lock(di->di_tv.v_lock, (char_u *)_(arg_errmsg)) + || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg)))) { break; + } + vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); clear_tv(&vimvars[VV_KEY].vv_tv); if (r == FAIL || did_emsg) break; - if (!map && rem) + if (!map && rem) { + if (var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg)) + || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) { + break; + } dictitem_remove(d, di); + } } } hash_unlock(ht); @@ -9512,8 +9547,9 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_type = VAR_NUMBER; for (li = l->lv_first; li != NULL; li = nli) { - if (tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) + if (map && tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) { break; + } nli = li->li_next; vimvars[VV_KEY].vv_nr = idx; if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL @@ -10786,15 +10822,15 @@ static void f_globpath(typval_T *argvars, typval_T *rettv) } } -/* - * "glob2regpat()" function - */ +// "glob2regpat()" function static void f_glob2regpat(typval_T *argvars, typval_T *rettv) { - char_u *pat = get_tv_string_chk(&argvars[0]); + char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error - rettv->v_type = VAR_STRING; - rettv->vval.v_string = file_pat_to_reg_pat(pat, NULL, NULL, FALSE); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (pat == NULL) + ? NULL + : file_pat_to_reg_pat(pat, NULL, NULL, false); } /* @@ -13878,9 +13914,10 @@ static void f_remove(typval_T *argvars, typval_T *rettv) key = get_tv_string_chk(&argvars[1]); if (key != NULL) { di = dict_find(d, key, -1); - if (di == NULL) + if (di == NULL) { EMSG2(_(e_dictkey), key); - else { + } else if (!var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg)) + && !var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) { *rettv = di->di_tv; init_tv(&di->di_tv); dictitem_remove(d, di); @@ -18648,14 +18685,16 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) if (!HASHITEM_EMPTY(hi)) { --todo; - /* Free the variable. Don't remove it from the hashtab, - * ht_array might change then. hash_clear() takes care of it - * later. */ + // Free the variable. Don't remove it from the hashtab, + // ht_array might change then. hash_clear() takes care of it + // later. v = HI2DI(hi); - if (free_val) + if (free_val) { clear_tv(&v->di_tv); - if ((v->di_flags & DI_FLAGS_FIX) == 0) + } + if (v->di_flags & DI_FLAGS_ALLOC) { xfree(v); + } } } hash_clear(ht); @@ -18829,7 +18868,7 @@ set_var ( xfree(v); return; } - v->di_flags = 0; + v->di_flags = DI_FLAGS_ALLOC; } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { @@ -20720,7 +20759,7 @@ call_user_func ( v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); - v->di_flags = DI_FLAGS_RO; + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; } STRCPY(v->di_key, name); hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index ed419268d2..cdad1f3197 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -101,10 +101,11 @@ struct dictitem_S { typedef struct dictitem_S dictitem_T; -#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */ -#define DI_FLAGS_RO_SBX 2 /* "di_flags" value: read-only in the sandbox */ -#define DI_FLAGS_FIX 4 /* "di_flags" value: fixed variable, not allocated */ -#define DI_FLAGS_LOCK 8 /* "di_flags" value: locked variable */ +#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable +#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox +#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove() +#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable +#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated /* * Structure to hold info about a Dictionary. diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index c095a7d27f..badb5b85b0 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -7106,6 +7106,7 @@ char_u * file_pat_to_reg_pat( char *allow_dirs, // Result passed back out in here int no_bslash // Don't use a backward slash as pathsep ) + FUNC_ATTR_NONNULL_ARG(1) { const char_u *endp; char_u *reg_pat; @@ -7118,6 +7119,10 @@ char_u * file_pat_to_reg_pat( if (pat_end == NULL) pat_end = pat + STRLEN(pat); + if (pat_end == pat) { + return (char_u *)xstrdup("^$"); + } + size_t size = 2; // '^' at start, '$' at end. for (p = pat; p < pat_end; p++) { diff --git a/src/nvim/path.c b/src/nvim/path.c index 5ac3d07f67..8b9a49dfc0 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -681,11 +681,16 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, /* remove backslashes for the remaining components only */ (void)do_path_expand(gap, buf, len + 1, flags, false); } else { - /* no more wildcards, check if there is a match */ - /* remove backslashes for the remaining components only */ - if (*path_end != NUL) + FileInfo file_info; + + // no more wildcards, check if there is a match + // remove backslashes for the remaining components only + if (*path_end != NUL) { backslash_halve(buf + len + 1); - if (os_file_exists(buf)) { /* add existing file */ + } + // add existing file or symbolic link + if ((flags & EW_ALLLINKS) ? os_fileinfo_link((char *)buf, &file_info) + : os_file_exists(buf)) { addfile(gap, buf, flags); } } @@ -1294,26 +1299,28 @@ expand_backtick ( return cnt; } -/* - * Add a file to a file list. Accepted flags: - * EW_DIR add directories - * EW_FILE add files - * EW_EXEC add executable files - * EW_NOTFOUND add even when it doesn't exist - * EW_ADDSLASH add slash after directory name - */ -void -addfile ( +// Add a file to a file list. Accepted flags: +// EW_DIR add directories +// EW_FILE add files +// EW_EXEC add executable files +// EW_NOTFOUND add even when it doesn't exist +// EW_ADDSLASH add slash after directory name +// EW_ALLLINKS add symlink also when the referred file does not exist +void addfile( garray_T *gap, char_u *f, /* filename */ int flags ) { bool isdir; + FileInfo file_info; - /* if the file/dir doesn't exist, may not add it */ - if (!(flags & EW_NOTFOUND) && !os_file_exists(f)) + // if the file/dir/link doesn't exist, may not add it + if (!(flags & EW_NOTFOUND) && + ((flags & EW_ALLLINKS) ? + !os_fileinfo_link((char *)f, &file_info) : !os_file_exists(f))) { return; + } #ifdef FNAME_ILLEGAL /* if the file/dir contains illegal characters, don't add it */ diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index e01e812a70..608aa38466 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -796,13 +796,14 @@ static void reg_equi_class(int c) if (enc_utf8 || STRCMP(p_enc, "latin1") == 0 || STRCMP(p_enc, "iso-8859-15") == 0) { switch (c) { - case 'A': case '\300': case '\301': case '\302': + // Do not use '\300' style, it results in a negative number. + case 'A': case 0xc0: case 0xc1: case 0xc2: + case 0xc3: case 0xc4: case 0xc5: CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104) CASEMBC(0x1cd) CASEMBC(0x1de) CASEMBC(0x1e0) CASEMBC(0x1ea2) - case '\303': case '\304': case '\305': - regmbc('A'); regmbc('\300'); regmbc('\301'); - regmbc('\302'); regmbc('\303'); regmbc('\304'); - regmbc('\305'); + regmbc('A'); regmbc(0xc0); regmbc(0xc1); + regmbc(0xc2); regmbc(0xc3); regmbc(0xc4); + regmbc(0xc5); REGMBC(0x100) REGMBC(0x102) REGMBC(0x104) REGMBC(0x1cd) REGMBC(0x1de) REGMBC(0x1e0) REGMBC(0x1ea2) @@ -810,9 +811,9 @@ static void reg_equi_class(int c) case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06) regmbc('B'); REGMBC(0x1e02) REGMBC(0x1e06) return; - case 'C': case '\307': + case 'C': case 0xc7: CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a) CASEMBC(0x10c) - regmbc('C'); regmbc('\307'); + regmbc('C'); regmbc(0xc7); REGMBC(0x106) REGMBC(0x108) REGMBC(0x10a) REGMBC(0x10c) return; @@ -821,11 +822,11 @@ static void reg_equi_class(int c) regmbc('D'); REGMBC(0x10e) REGMBC(0x110) REGMBC(0x1e0a) REGMBC(0x1e0e) REGMBC(0x1e10) return; - case 'E': case '\310': case '\311': case '\312': case '\313': + case 'E': case 0xc8: case 0xc9: case 0xca: case 0xcb: CASEMBC(0x112) CASEMBC(0x114) CASEMBC(0x116) CASEMBC(0x118) CASEMBC(0x11a) CASEMBC(0x1eba) CASEMBC(0x1ebc) - regmbc('E'); regmbc('\310'); regmbc('\311'); - regmbc('\312'); regmbc('\313'); + regmbc('E'); regmbc(0xc8); regmbc(0xc9); + regmbc(0xca); regmbc(0xcb); REGMBC(0x112) REGMBC(0x114) REGMBC(0x116) REGMBC(0x118) REGMBC(0x11a) REGMBC(0x1eba) REGMBC(0x1ebc) @@ -845,11 +846,11 @@ static void reg_equi_class(int c) regmbc('H'); REGMBC(0x124) REGMBC(0x126) REGMBC(0x1e22) REGMBC(0x1e26) REGMBC(0x1e28) return; - case 'I': case '\314': case '\315': case '\316': case '\317': + case 'I': case 0xcc: case 0xcd: case 0xce: case 0xcf: CASEMBC(0x128) CASEMBC(0x12a) CASEMBC(0x12c) CASEMBC(0x12e) CASEMBC(0x130) CASEMBC(0x1cf) CASEMBC(0x1ec8) - regmbc('I'); regmbc('\314'); regmbc('\315'); - regmbc('\316'); regmbc('\317'); + regmbc('I'); regmbc(0xcc); regmbc(0xcd); + regmbc(0xce); regmbc(0xcf); REGMBC(0x128) REGMBC(0x12a) REGMBC(0x12c) REGMBC(0x12e) REGMBC(0x130) REGMBC(0x1cf) REGMBC(0x1ec8) @@ -871,20 +872,20 @@ static void reg_equi_class(int c) case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40) regmbc('M'); REGMBC(0x1e3e) REGMBC(0x1e40) return; - case 'N': case '\321': + case 'N': case 0xd1: CASEMBC(0x143) CASEMBC(0x145) CASEMBC(0x147) CASEMBC(0x1e44) CASEMBC(0x1e48) - regmbc('N'); regmbc('\321'); + regmbc('N'); regmbc(0xd1); REGMBC(0x143) REGMBC(0x145) REGMBC(0x147) REGMBC(0x1e44) REGMBC(0x1e48) return; - case 'O': case '\322': case '\323': case '\324': case '\325': - case '\326': case '\330': + case 'O': case 0xd2: case 0xd3: case 0xd4: case 0xd5: + case 0xd6: case 0xd8: CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150) CASEMBC(0x1a0) CASEMBC(0x1d1) CASEMBC(0x1ea) CASEMBC(0x1ec) CASEMBC(0x1ece) - regmbc('O'); regmbc('\322'); regmbc('\323'); - regmbc('\324'); regmbc('\325'); regmbc('\326'); - regmbc('\330'); + regmbc('O'); regmbc(0xd2); regmbc(0xd3); + regmbc(0xd4); regmbc(0xd5); regmbc(0xd6); + regmbc(0xd8); REGMBC(0x14c) REGMBC(0x14e) REGMBC(0x150) REGMBC(0x1a0) REGMBC(0x1d1) REGMBC(0x1ea) REGMBC(0x1ec) REGMBC(0x1ece) @@ -907,12 +908,12 @@ static void reg_equi_class(int c) regmbc('T'); REGMBC(0x162) REGMBC(0x164) REGMBC(0x166) REGMBC(0x1e6a) REGMBC(0x1e6e) return; - case 'U': case '\331': case '\332': case '\333': case '\334': + case 'U': case 0xd9: case 0xda: case 0xdb: case 0xdc: CASEMBC(0x168) CASEMBC(0x16a) CASEMBC(0x16c) CASEMBC(0x16e) CASEMBC(0x170) CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3) CASEMBC(0x1ee6) - regmbc('U'); regmbc('\331'); regmbc('\332'); - regmbc('\333'); regmbc('\334'); + regmbc('U'); regmbc(0xd9); regmbc(0xda); + regmbc(0xdb); regmbc(0xdc); REGMBC(0x168) REGMBC(0x16a) REGMBC(0x16c) REGMBC(0x16e) REGMBC(0x170) REGMBC(0x172) REGMBC(0x1af) REGMBC(0x1d3) REGMBC(0x1ee6) @@ -928,10 +929,10 @@ static void reg_equi_class(int c) case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c) regmbc('X'); REGMBC(0x1e8a) REGMBC(0x1e8c) return; - case 'Y': case '\335': + case 'Y': case 0xdd: CASEMBC(0x176) CASEMBC(0x178) CASEMBC(0x1e8e) CASEMBC(0x1ef2) CASEMBC(0x1ef6) CASEMBC(0x1ef8) - regmbc('Y'); regmbc('\335'); + regmbc('Y'); regmbc(0xdd); REGMBC(0x176) REGMBC(0x178) REGMBC(0x1e8e) REGMBC(0x1ef2) REGMBC(0x1ef6) REGMBC(0x1ef8) return; @@ -941,13 +942,13 @@ static void reg_equi_class(int c) REGMBC(0x17d) REGMBC(0x1b5) REGMBC(0x1e90) REGMBC(0x1e94) return; - case 'a': case '\340': case '\341': case '\342': - case '\343': case '\344': case '\345': + case 'a': case 0xe0: case 0xe1: case 0xe2: + case 0xe3: case 0xe4: case 0xe5: CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105) CASEMBC(0x1ce) CASEMBC(0x1df) CASEMBC(0x1e1) CASEMBC(0x1ea3) - regmbc('a'); regmbc('\340'); regmbc('\341'); - regmbc('\342'); regmbc('\343'); regmbc('\344'); - regmbc('\345'); + regmbc('a'); regmbc(0xe0); regmbc(0xe1); + regmbc(0xe2); regmbc(0xe3); regmbc(0xe4); + regmbc(0xe5); REGMBC(0x101) REGMBC(0x103) REGMBC(0x105) REGMBC(0x1ce) REGMBC(0x1df) REGMBC(0x1e1) REGMBC(0x1ea3) @@ -955,9 +956,9 @@ static void reg_equi_class(int c) case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07) regmbc('b'); REGMBC(0x1e03) REGMBC(0x1e07) return; - case 'c': case '\347': + case 'c': case 0xe7: CASEMBC(0x107) CASEMBC(0x109) CASEMBC(0x10b) CASEMBC(0x10d) - regmbc('c'); regmbc('\347'); + regmbc('c'); regmbc(0xe7); REGMBC(0x107) REGMBC(0x109) REGMBC(0x10b) REGMBC(0x10d) return; @@ -966,11 +967,11 @@ static void reg_equi_class(int c) regmbc('d'); REGMBC(0x10f) REGMBC(0x111) REGMBC(0x1e0b) REGMBC(0x1e0f) REGMBC(0x1e11) return; - case 'e': case '\350': case '\351': case '\352': case '\353': + case 'e': case 0xe8: case 0xe9: case 0xea: case 0xeb: CASEMBC(0x113) CASEMBC(0x115) CASEMBC(0x117) CASEMBC(0x119) CASEMBC(0x11b) CASEMBC(0x1ebb) CASEMBC(0x1ebd) - regmbc('e'); regmbc('\350'); regmbc('\351'); - regmbc('\352'); regmbc('\353'); + regmbc('e'); regmbc(0xe8); regmbc(0xe9); + regmbc(0xea); regmbc(0xeb); REGMBC(0x113) REGMBC(0x115) REGMBC(0x117) REGMBC(0x119) REGMBC(0x11b) REGMBC(0x1ebb) REGMBC(0x1ebd) @@ -991,11 +992,11 @@ static void reg_equi_class(int c) REGMBC(0x1e23) REGMBC(0x1e27) REGMBC(0x1e29) REGMBC(0x1e96) return; - case 'i': case '\354': case '\355': case '\356': case '\357': + case 'i': case 0xec: case 0xed: case 0xee: case 0xef: CASEMBC(0x129) CASEMBC(0x12b) CASEMBC(0x12d) CASEMBC(0x12f) CASEMBC(0x1d0) CASEMBC(0x1ec9) - regmbc('i'); regmbc('\354'); regmbc('\355'); - regmbc('\356'); regmbc('\357'); + regmbc('i'); regmbc(0xec); regmbc(0xed); + regmbc(0xee); regmbc(0xef); REGMBC(0x129) REGMBC(0x12b) REGMBC(0x12d) REGMBC(0x12f) REGMBC(0x1d0) REGMBC(0x1ec9) return; @@ -1016,20 +1017,20 @@ static void reg_equi_class(int c) case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41) regmbc('m'); REGMBC(0x1e3f) REGMBC(0x1e41) return; - case 'n': case '\361': + case 'n': case 0xf1: CASEMBC(0x144) CASEMBC(0x146) CASEMBC(0x148) CASEMBC(0x149) CASEMBC(0x1e45) CASEMBC(0x1e49) - regmbc('n'); regmbc('\361'); + regmbc('n'); regmbc(0xf1); REGMBC(0x144) REGMBC(0x146) REGMBC(0x148) REGMBC(0x149) REGMBC(0x1e45) REGMBC(0x1e49) return; - case 'o': case '\362': case '\363': case '\364': case '\365': - case '\366': case '\370': + case 'o': case 0xf2: case 0xf3: case 0xf4: case 0xf5: + case 0xf6: case 0xf8: CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151) CASEMBC(0x1a1) CASEMBC(0x1d2) CASEMBC(0x1eb) CASEMBC(0x1ed) CASEMBC(0x1ecf) - regmbc('o'); regmbc('\362'); regmbc('\363'); - regmbc('\364'); regmbc('\365'); regmbc('\366'); - regmbc('\370'); + regmbc('o'); regmbc(0xf2); regmbc(0xf3); + regmbc(0xf4); regmbc(0xf5); regmbc(0xf6); + regmbc(0xf8); REGMBC(0x14d) REGMBC(0x14f) REGMBC(0x151) REGMBC(0x1a1) REGMBC(0x1d2) REGMBC(0x1eb) REGMBC(0x1ed) REGMBC(0x1ecf) @@ -1052,12 +1053,12 @@ static void reg_equi_class(int c) regmbc('t'); REGMBC(0x163) REGMBC(0x165) REGMBC(0x167) REGMBC(0x1e6b) REGMBC(0x1e6f) REGMBC(0x1e97) return; - case 'u': case '\371': case '\372': case '\373': case '\374': + case 'u': case 0xf9: case 0xfa: case 0xfb: case 0xfc: CASEMBC(0x169) CASEMBC(0x16b) CASEMBC(0x16d) CASEMBC(0x16f) CASEMBC(0x171) CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4) CASEMBC(0x1ee7) - regmbc('u'); regmbc('\371'); regmbc('\372'); - regmbc('\373'); regmbc('\374'); + regmbc('u'); regmbc(0xf9); regmbc(0xfa); + regmbc(0xfb); regmbc(0xfc); REGMBC(0x169) REGMBC(0x16b) REGMBC(0x16d) REGMBC(0x16f) REGMBC(0x171) REGMBC(0x173) REGMBC(0x1b0) REGMBC(0x1d4) REGMBC(0x1ee7) @@ -1074,10 +1075,10 @@ static void reg_equi_class(int c) case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d) regmbc('x'); REGMBC(0x1e8b) REGMBC(0x1e8d) return; - case 'y': case '\375': case '\377': + case 'y': case 0xfd: case 0xff: CASEMBC(0x177) CASEMBC(0x1e8f) CASEMBC(0x1e99) CASEMBC(0x1ef3) CASEMBC(0x1ef7) CASEMBC(0x1ef9) - regmbc('y'); regmbc('\375'); regmbc('\377'); + regmbc('y'); regmbc(0xfd); regmbc(0xff); REGMBC(0x177) REGMBC(0x1e8f) REGMBC(0x1e99) REGMBC(0x1ef3) REGMBC(0x1ef7) REGMBC(0x1ef9) return; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index fe91141375..37a0fb82da 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -425,9 +425,13 @@ char_u *vim_strchr(const char_u *string, int c) const char_u *p = string; if (enc_utf8 && c >= 0x80) { while (*p != NUL) { - if (utf_ptr2char(p) == c) + int l = (*mb_ptr2len)(p); + + // Avoid matching an illegal byte here. + if (l > 1 && utf_ptr2char(p) == c) { return (char_u *) p; - p += (*mb_ptr2len)(p); + } + p += l; } return NULL; } diff --git a/src/nvim/testdir/test55.in b/src/nvim/testdir/test55.in index c4e82d429c..7b6f684caa 100644 --- a/src/nvim/testdir/test55.in +++ b/src/nvim/testdir/test55.in @@ -282,6 +282,166 @@ let l = [0, 1, 2, 3] : $put =ps : endfor :endfor +:" +:" Unletting locked variables +:$put ='Unletting:' +:for depth in range(5) +: $put ='depth is ' . depth +: for u in range(3) +: unlet l +: let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] +: exe "lockvar " . depth . " l" +: if u == 1 +: exe "unlockvar l" +: elseif u == 2 +: exe "unlockvar " . depth . " l" +: endif +: let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") +: $put =ps +: let ps = '' +: try +: unlet l[2]['6'][7] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[2][6] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[2] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[1][1][0] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[1][1] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l[1] +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: try +: unlet l +: let ps .= 'p' +: catch +: let ps .= 'F' +: endtry +: $put =ps +: endfor +:endfor +:" +:" Locked variables and :unlet or list / dict functions +:$put ='Locks and commands or functions:' +:" +:$put ='No :unlet after lock on dict:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar 1 d +:try +: unlet d.a +: $put ='did :unlet' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put =':unlet after lock on dict item:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar d.a +:try +: unlet d.a +: $put ='did :unlet' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put ='filter() after lock on dict item:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar d.a +:try +: call filter(d, 'v:key != "a"') +: $put ='did filter()' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put ='map() after lock on dict:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar 1 d +:try +: call map(d, 'v:val + 200') +: $put ='did map()' +:catch +: $put =v:exception[:16] +:endtry +:$put =string(d) +:" +:$put ='No extend() after lock on dict item:' +:unlet! d +:let d = {'a': 99, 'b': 100} +:lockvar d.a +:try +: $put =string(extend(d, {'a': 123})) +: $put ='did extend()' +:catch +: $put =v:exception[:14] +:endtry +:$put =string(d) +:" +:$put ='No remove() of write-protected scope-level variable:' +:fun! Tfunc(this_is_a_loooooooooong_parameter_name) +: try +: $put =string(remove(a:, 'this_is_a_loooooooooong_parameter_name')) +: $put ='did remove()' +: catch +: $put =v:exception[:14] +: endtry +:endfun +:call Tfunc('testval') +:" +:$put ='No extend() of write-protected scope-level variable:' +:fun! Tfunc(this_is_a_loooooooooong_parameter_name) +: try +: $put =string(extend(a:, {'this_is_a_loooooooooong_parameter_name': 1234})) +: $put ='did extend()' +: catch +: $put =v:exception[:14] +: endtry +:endfun +:call Tfunc('testval') +:" +:$put ='No :unlet of variable in locked scope:' +:let b:testvar = 123 +:lockvar 1 b: +:try +: unlet b:testvar +: $put ='b:testvar was :unlet: '. (!exists('b:testvar')) +:catch +: $put =v:exception[:16] +:endtry +:unlockvar 1 b: +:unlet! b:testvar +:" :unlet l :let l = [1, 2, 3, 4] :lockvar! l diff --git a/src/nvim/testdir/test55.ok b/src/nvim/testdir/test55.ok index ba029b2898..4e0303c26e 100644 --- a/src/nvim/testdir/test55.ok +++ b/src/nvim/testdir/test55.ok @@ -86,6 +86,64 @@ FFFFFFF FFpFFpp 0000-000 ppppppp +Unletting: +depth is 0 +0000-000 +ppppppp +0000-000 +ppppppp +0000-000 +ppppppp +depth is 1 +1000-000 +ppFppFp +0000-000 +ppppppp +0000-000 +ppppppp +depth is 2 +1100-100 +pFFpFFp +0000-000 +ppppppp +0000-000 +ppppppp +depth is 3 +1110-110 +FFFFFFp +0010-010 +FppFppp +0000-000 +ppppppp +depth is 4 +1111-111 +FFFFFFp +0011-011 +FppFppp +0000-000 +ppppppp +Locks and commands or functions: +No :unlet after lock on dict: +Vim(unlet):E741: +{'a': 99, 'b': 100} +:unlet after lock on dict item: +did :unlet +{'b': 100} +filter() after lock on dict item: +did filter() +{'b': 100} +map() after lock on dict: +did map() +{'a': 299, 'b': 300} +No extend() after lock on dict item: +Vim(put):E741: +{'a': 99, 'b': 100} +No remove() of write-protected scope-level variable: +Vim(put):E795: +No extend() of write-protected scope-level variable: +Vim(put):E742: +No :unlet of variable in locked scope: +Vim(unlet):E741: [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] diff --git a/src/nvim/version.c b/src/nvim/version.c index dcd1773a7a..0d62445483 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,6 +69,16 @@ static char *features[] = { // clang-format off static int included_patches[] = { + 1137, + + + + 1081, + + + + + 1055, // 1054, // 1053, @@ -420,13 +430,13 @@ static int included_patches[] = { 707, 706, // 705 NA - // 704, + 704, // 703 NA 702, // 701 NA // 700, 699, - // 698, + 698, // 697, 696, 695, @@ -468,7 +478,7 @@ static int included_patches[] = { 659, 658, // 657 NA - // 656, + 656, 655, 654, 653, diff --git a/src/nvim/window.c b/src/nvim/window.c index 853f8755c3..191cb04d75 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1913,9 +1913,16 @@ int win_close(win_T *win, int free_buf) */ if (win->w_buffer != NULL) { win->w_closing = true; - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, TRUE); - if (win_valid(win)) + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true); + if (win_valid(win)) { win->w_closing = false; + } + + // Make sure curbuf is valid. It can become invalid if 'bufhidden' is + // "wipe". + if (!buf_valid(curbuf)) { + curbuf = firstbuf; + } } if (only_one_window() && win_valid(win) && win->w_buffer == NULL |