mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 21:25:04 -07:00
Merge #6112 from ZyX-I/split-eval'/buf_get_changedtick
Better b:changedtick support
This commit is contained in:
commit
c318d8e672
@ -8606,9 +8606,12 @@ This does NOT work: >
|
|||||||
:lockvar v
|
:lockvar v
|
||||||
:let v = 'asdf' " fails!
|
:let v = 'asdf' " fails!
|
||||||
:unlet v
|
:unlet v
|
||||||
< *E741*
|
< *E741* *E940*
|
||||||
If you try to change a locked variable you get an
|
If you try to change a locked variable you get an
|
||||||
error message: "E741: Value is locked: {name}"
|
error message: "E741: Value is locked: {name}".
|
||||||
|
If you try to lock or unlock a built-in variable you
|
||||||
|
will get an error message "E940: Cannot lock or unlock
|
||||||
|
variable {name}".
|
||||||
|
|
||||||
[depth] is relevant when locking a |List| or
|
[depth] is relevant when locking a |List| or
|
||||||
|Dictionary|. It specifies how deep the locking goes:
|
|Dictionary|. It specifies how deep the locking goes:
|
||||||
|
@ -411,10 +411,10 @@ end:
|
|||||||
|
|
||||||
/// Gets a buffer-scoped (b:) variable.
|
/// Gets a buffer-scoped (b:) variable.
|
||||||
///
|
///
|
||||||
/// @param buffer Buffer handle
|
/// @param buffer The buffer handle
|
||||||
/// @param name Variable name
|
/// @param name The variable name
|
||||||
/// @param[out] err Error details, if any
|
/// @param[out] err Details of an error that may have occurred
|
||||||
/// @return Variable value
|
/// @return The variable value
|
||||||
Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
|
Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
|
||||||
{
|
{
|
||||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||||
@ -426,6 +426,22 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
|
|||||||
return dict_get_value(buf->b_vars, name, err);
|
return dict_get_value(buf->b_vars, name, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a changed tick of a buffer
|
||||||
|
///
|
||||||
|
/// @param[in] buffer The buffer handle.
|
||||||
|
///
|
||||||
|
/// @return `b:changedtick` value.
|
||||||
|
Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
|
||||||
|
{
|
||||||
|
const buf_T *const buf = find_buffer_by_handle(buffer, err);
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf->b_changedtick;
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets a buffer-scoped (b:) variable
|
/// Sets a buffer-scoped (b:) variable
|
||||||
///
|
///
|
||||||
/// @param buffer Buffer handle
|
/// @param buffer Buffer handle
|
||||||
@ -440,7 +456,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_set_value(buf->b_vars, name, value, false, false, err);
|
dict_set_var(buf->b_vars, name, value, false, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a buffer-scoped (b:) variable
|
/// Removes a buffer-scoped (b:) variable
|
||||||
@ -456,7 +472,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_set_value(buf->b_vars, name, NIL, true, false, err);
|
dict_set_var(buf->b_vars, name, NIL, true, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a buffer-scoped (b:) variable
|
/// Sets a buffer-scoped (b:) variable
|
||||||
@ -479,7 +495,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
|
|||||||
return (Object) OBJECT_INIT;
|
return (Object) OBJECT_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict_set_value(buf->b_vars, name, value, false, true, err);
|
return dict_set_var(buf->b_vars, name, value, false, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a buffer-scoped (b:) variable
|
/// Removes a buffer-scoped (b:) variable
|
||||||
@ -498,7 +514,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err)
|
|||||||
return (Object) OBJECT_INIT;
|
return (Object) OBJECT_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict_set_value(buf->b_vars, name, NIL, true, true, err);
|
return dict_set_var(buf->b_vars, name, NIL, true, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
|
|||||||
return vim_to_object(&di->di_tv);
|
return vim_to_object(&di->di_tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a value in a dict. Objects are recursively expanded into their
|
/// Set a value in a scope dict. Objects are recursively expanded into their
|
||||||
/// vimscript equivalents.
|
/// vimscript equivalents.
|
||||||
///
|
///
|
||||||
/// @param dict The vimscript dict
|
/// @param dict The vimscript dict
|
||||||
@ -109,7 +109,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
|
|||||||
/// @param retval If true the old value will be converted and returned.
|
/// @param retval If true the old value will be converted and returned.
|
||||||
/// @param[out] err Details of an error that may have occurred
|
/// @param[out] err Details of an error that may have occurred
|
||||||
/// @return The old value if `retval` is true and the key was present, else NIL
|
/// @return The old value if `retval` is true and the key was present, else NIL
|
||||||
Object dict_set_value(dict_T *dict, String key, Object value, bool del,
|
Object dict_set_var(dict_T *dict, String key, Object value, bool del,
|
||||||
bool retval, Error *err)
|
bool retval, Error *err)
|
||||||
{
|
{
|
||||||
Object rv = OBJECT_INIT;
|
Object rv = OBJECT_INIT;
|
||||||
@ -120,7 +120,7 @@ Object dict_set_value(dict_T *dict, String key, Object value, bool del,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key.size == 0) {
|
if (key.size == 0) {
|
||||||
api_set_error(err, Validation, _("Empty dictionary keys aren't allowed"));
|
api_set_error(err, Validation, _("Empty variable names aren't allowed"));
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +129,20 @@ Object dict_set_value(dict_T *dict, String key, Object value, bool del,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
dictitem_T *di = dict_find(dict, (uint8_t *)key.data, (int)key.size);
|
dictitem_T *di = dict_find(dict, (char_u *)key.data, (int)key.size);
|
||||||
|
|
||||||
|
if (di != NULL) {
|
||||||
|
if (di->di_flags & DI_FLAGS_RO) {
|
||||||
|
api_set_error(err, Exception, _("Key is read-only: %s"), key.data);
|
||||||
|
return rv;
|
||||||
|
} else if (di->di_flags & DI_FLAGS_FIX) {
|
||||||
|
api_set_error(err, Exception, _("Key is fixed: %s"), key.data);
|
||||||
|
return rv;
|
||||||
|
} else if (di->di_flags & DI_FLAGS_LOCK) {
|
||||||
|
api_set_error(err, Exception, _("Key is locked: %s"), key.data);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (del) {
|
if (del) {
|
||||||
// Delete the key
|
// Delete the key
|
||||||
|
@ -71,7 +71,7 @@ void nvim_tabpage_set_var(Tabpage tabpage,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_set_value(tab->tp_vars, name, value, false, false, err);
|
dict_set_var(tab->tp_vars, name, value, false, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a tab-scoped (t:) variable
|
/// Removes a tab-scoped (t:) variable
|
||||||
@ -87,7 +87,7 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_set_value(tab->tp_vars, name, NIL, true, false, err);
|
dict_set_var(tab->tp_vars, name, NIL, true, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a tab-scoped (t:) variable
|
/// Sets a tab-scoped (t:) variable
|
||||||
@ -110,7 +110,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
|
|||||||
return (Object) OBJECT_INIT;
|
return (Object) OBJECT_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict_set_value(tab->tp_vars, name, value, false, true, err);
|
return dict_set_var(tab->tp_vars, name, value, false, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a tab-scoped (t:) variable
|
/// Removes a tab-scoped (t:) variable
|
||||||
@ -129,7 +129,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
|
|||||||
return (Object) OBJECT_INIT;
|
return (Object) OBJECT_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict_set_value(tab->tp_vars, name, NIL, true, true, err);
|
return dict_set_var(tab->tp_vars, name, NIL, true, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current window in a tabpage
|
/// Gets the current window in a tabpage
|
||||||
|
@ -368,7 +368,7 @@ Object nvim_get_var(String name, Error *err)
|
|||||||
/// @param[out] err Error details, if any
|
/// @param[out] err Error details, if any
|
||||||
void nvim_set_var(String name, Object value, Error *err)
|
void nvim_set_var(String name, Object value, Error *err)
|
||||||
{
|
{
|
||||||
dict_set_value(&globvardict, name, value, false, false, err);
|
dict_set_var(&globvardict, name, value, false, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a global (g:) variable
|
/// Removes a global (g:) variable
|
||||||
@ -377,7 +377,7 @@ void nvim_set_var(String name, Object value, Error *err)
|
|||||||
/// @param[out] err Error details, if any
|
/// @param[out] err Error details, if any
|
||||||
void nvim_del_var(String name, Error *err)
|
void nvim_del_var(String name, Error *err)
|
||||||
{
|
{
|
||||||
dict_set_value(&globvardict, name, NIL, true, false, err);
|
dict_set_var(&globvardict, name, NIL, true, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a global variable
|
/// Sets a global variable
|
||||||
@ -393,7 +393,7 @@ void nvim_del_var(String name, Error *err)
|
|||||||
/// or if previous value was `v:null`.
|
/// or if previous value was `v:null`.
|
||||||
Object vim_set_var(String name, Object value, Error *err)
|
Object vim_set_var(String name, Object value, Error *err)
|
||||||
{
|
{
|
||||||
return dict_set_value(&globvardict, name, value, false, true, err);
|
return dict_set_var(&globvardict, name, value, false, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a global variable
|
/// Removes a global variable
|
||||||
@ -405,7 +405,7 @@ Object vim_set_var(String name, Object value, Error *err)
|
|||||||
/// @return Old value
|
/// @return Old value
|
||||||
Object vim_del_var(String name, Error *err)
|
Object vim_del_var(String name, Error *err)
|
||||||
{
|
{
|
||||||
return dict_set_value(&globvardict, name, NIL, true, true, err);
|
return dict_set_var(&globvardict, name, NIL, true, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a v: variable
|
/// Gets a v: variable
|
||||||
|
@ -210,7 +210,7 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_set_value(win->w_vars, name, value, false, false, err);
|
dict_set_var(win->w_vars, name, value, false, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a window-scoped (w:) variable
|
/// Removes a window-scoped (w:) variable
|
||||||
@ -226,7 +226,7 @@ void nvim_win_del_var(Window window, String name, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_set_value(win->w_vars, name, NIL, true, false, err);
|
dict_set_var(win->w_vars, name, NIL, true, false, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a window-scoped (w:) variable
|
/// Sets a window-scoped (w:) variable
|
||||||
@ -249,7 +249,7 @@ Object window_set_var(Window window, String name, Object value, Error *err)
|
|||||||
return (Object) OBJECT_INIT;
|
return (Object) OBJECT_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict_set_value(win->w_vars, name, value, false, true, err);
|
return dict_set_var(win->w_vars, name, value, false, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a window-scoped (w:) variable
|
/// Removes a window-scoped (w:) variable
|
||||||
@ -268,7 +268,7 @@ Object window_del_var(Window window, String name, Error *err)
|
|||||||
return (Object) OBJECT_INIT;
|
return (Object) OBJECT_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict_set_value(win->w_vars, name, NIL, true, true, err);
|
return dict_set_var(win->w_vars, name, NIL, true, true, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a window option value
|
/// Gets a window option value
|
||||||
|
@ -686,8 +686,18 @@ free_buffer_stuff (
|
|||||||
free_buf_options(buf, true);
|
free_buf_options(buf, true);
|
||||||
ga_clear(&buf->b_s.b_langp);
|
ga_clear(&buf->b_s.b_langp);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Avoid loosing b:changedtick when deleting buffer: clearing variables
|
||||||
|
// implies using clear_tv() on b:changedtick and that sets changedtick to
|
||||||
|
// zero.
|
||||||
|
hashitem_T *const changedtick_hi = hash_find(
|
||||||
|
&buf->b_vars->dv_hashtab, (const char_u *)"changedtick");
|
||||||
|
assert(changedtick_hi != NULL);
|
||||||
|
hash_remove(&buf->b_vars->dv_hashtab, changedtick_hi);
|
||||||
|
}
|
||||||
vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables
|
vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables
|
||||||
hash_init(&buf->b_vars->dv_hashtab);
|
hash_init(&buf->b_vars->dv_hashtab);
|
||||||
|
buf_init_changedtick(buf);
|
||||||
uc_clear(&buf->b_ucmds); // clear local user commands
|
uc_clear(&buf->b_ucmds); // clear local user commands
|
||||||
buf_delete_signs(buf); // delete any signs
|
buf_delete_signs(buf); // delete any signs
|
||||||
bufhl_clear_all(buf); // delete any highligts
|
bufhl_clear_all(buf); // delete any highligts
|
||||||
@ -1436,6 +1446,26 @@ void do_autochdir(void)
|
|||||||
|
|
||||||
static int top_file_num = 1; ///< highest file number
|
static int top_file_num = 1; ///< highest file number
|
||||||
|
|
||||||
|
/// Initialize b:changedtick and changedtick_val attribute
|
||||||
|
///
|
||||||
|
/// @param[out] buf Buffer to intialize for.
|
||||||
|
static inline void buf_init_changedtick(buf_T *const buf)
|
||||||
|
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
STATIC_ASSERT(sizeof("changedtick") <= sizeof(buf->changedtick_di.di_key),
|
||||||
|
"buf->changedtick_di cannot hold large enough keys");
|
||||||
|
buf->changedtick_di = (dictitem16_T) {
|
||||||
|
.di_flags = DI_FLAGS_RO|DI_FLAGS_FIX, // Must not include DI_FLAGS_ALLOC.
|
||||||
|
.di_tv = (typval_T) {
|
||||||
|
.v_type = VAR_NUMBER,
|
||||||
|
.v_lock = VAR_FIXED,
|
||||||
|
.vval.v_number = buf->b_changedtick,
|
||||||
|
},
|
||||||
|
.di_key = "changedtick",
|
||||||
|
};
|
||||||
|
dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di);
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a file name to the buffer list.
|
/// Add a file name to the buffer list.
|
||||||
/// If the same file name already exists return a pointer to that buffer.
|
/// If the same file name already exists return a pointer to that buffer.
|
||||||
/// If it does not exist, or if fname == NULL, a new entry is created.
|
/// If it does not exist, or if fname == NULL, a new entry is created.
|
||||||
@ -1530,6 +1560,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
|
|||||||
// init b: variables
|
// init b: variables
|
||||||
buf->b_vars = dict_alloc();
|
buf->b_vars = dict_alloc();
|
||||||
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
|
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
|
||||||
|
buf_init_changedtick(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ffname != NULL) {
|
if (ffname != NULL) {
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include "nvim/pos.h" // for linenr_T
|
#include "nvim/pos.h" // for linenr_T
|
||||||
#include "nvim/ex_cmds_defs.h" // for exarg_T
|
#include "nvim/ex_cmds_defs.h" // for exarg_T
|
||||||
#include "nvim/screen.h" // for StlClickRecord
|
#include "nvim/screen.h" // for StlClickRecord
|
||||||
|
#include "nvim/func_attr.h"
|
||||||
|
#include "nvim/eval.h"
|
||||||
|
|
||||||
// Values for buflist_getfile()
|
// Values for buflist_getfile()
|
||||||
enum getf_values {
|
enum getf_values {
|
||||||
@ -79,6 +81,31 @@ static inline void restore_win_for_buf(win_T *save_curwin,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void buf_set_changedtick(buf_T *const buf, const int changedtick)
|
||||||
|
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
|
||||||
|
|
||||||
|
/// Set b_changedtick and corresponding variable
|
||||||
|
///
|
||||||
|
/// @param[out] buf Buffer to set changedtick in.
|
||||||
|
/// @param[in] changedtick New value.
|
||||||
|
static inline void buf_set_changedtick(buf_T *const buf, const int changedtick)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
dictitem_T *const changedtick_di = dict_find(
|
||||||
|
buf->b_vars, (char_u *)"changedtick", sizeof("changedtick") - 1);
|
||||||
|
assert(changedtick_di != NULL);
|
||||||
|
assert(changedtick_di->di_tv.v_type == VAR_NUMBER);
|
||||||
|
assert(changedtick_di->di_tv.v_lock == VAR_FIXED);
|
||||||
|
// For some reason formatc does not like the below.
|
||||||
|
# ifndef UNIT_TESTING_LUA_PREPROCESSING
|
||||||
|
assert(changedtick_di->di_flags == (DI_FLAGS_RO|DI_FLAGS_FIX));
|
||||||
|
# endif
|
||||||
|
assert(changedtick_di == (dictitem_T *)&buf->changedtick_di);
|
||||||
|
assert(&buf->b_changedtick == &buf->changedtick_di.di_tv.vval.v_number);
|
||||||
|
#endif
|
||||||
|
buf->b_changedtick = changedtick;
|
||||||
|
}
|
||||||
|
|
||||||
#define WITH_BUFFER(b, code) \
|
#define WITH_BUFFER(b, code) \
|
||||||
do { \
|
do { \
|
||||||
win_T *save_curwin = NULL; \
|
win_T *save_curwin = NULL; \
|
||||||
|
@ -489,7 +489,9 @@ struct file_buffer {
|
|||||||
|
|
||||||
int b_changed; // 'modified': Set to true if something in the
|
int b_changed; // 'modified': Set to true if something in the
|
||||||
// file has been changed and not written out.
|
// file has been changed and not written out.
|
||||||
int b_changedtick; // incremented for each change, also for undo
|
/// Change identifier incremented for each change, including undo
|
||||||
|
#define b_changedtick changedtick_di.di_tv.vval.v_number
|
||||||
|
dictitem16_T changedtick_di; // b:changedtick dictionary item.
|
||||||
|
|
||||||
bool b_saving; /* Set to true if we are in the middle of
|
bool b_saving; /* Set to true if we are in the middle of
|
||||||
saving the buffer. */
|
saving the buffer. */
|
||||||
|
493
src/nvim/eval.c
493
src/nvim/eval.c
@ -171,6 +171,8 @@ static char *e_letwrong = N_("E734: Wrong variable type for %s=");
|
|||||||
static char *e_nofunc = N_("E130: Unknown function: %s");
|
static char *e_nofunc = N_("E130: Unknown function: %s");
|
||||||
static char *e_illvar = N_("E461: Illegal variable name: %s");
|
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 *e_float_as_string = N_("E806: using Float as a String");
|
||||||
|
static const char *e_readonlyvar = N_(
|
||||||
|
"E46: Cannot change read-only variable \"%.*s\"");
|
||||||
|
|
||||||
static char_u * const empty_string = (char_u *)"";
|
static char_u * const empty_string = (char_u *)"";
|
||||||
static char_u * const namespace_char = (char_u *)"abglstvw";
|
static char_u * const namespace_char = (char_u *)"abglstvw";
|
||||||
@ -201,15 +203,22 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL};
|
|||||||
|
|
||||||
static int echo_attr = 0; /* attributes used for ":echo" */
|
static int echo_attr = 0; /* attributes used for ":echo" */
|
||||||
|
|
||||||
// Values for trans_function_name() argument:
|
/// trans_function_name() flags
|
||||||
#define TFN_INT 1 // internal function name OK
|
typedef enum {
|
||||||
#define TFN_QUIET 2 // no error messages
|
TFN_INT = 1, ///< May use internal function name
|
||||||
#define TFN_NO_AUTOLOAD 4 // do not use script autoloading
|
TFN_QUIET = 2, ///< Do not emit error messages.
|
||||||
#define TFN_NO_DEREF 8 // do not dereference a Funcref
|
TFN_NO_AUTOLOAD = 4, ///< Do not use script autoloading.
|
||||||
|
TFN_NO_DEREF = 8, ///< Do not dereference a Funcref.
|
||||||
|
TFN_READ_ONLY = 16, ///< Will not change the variable.
|
||||||
|
} TransFunctionNameFlags;
|
||||||
|
|
||||||
// Values for get_lval() flags argument:
|
/// get_lval() flags
|
||||||
#define GLV_QUIET TFN_QUIET // no error messages
|
typedef enum {
|
||||||
#define GLV_NO_AUTOLOAD TFN_NO_AUTOLOAD // do not use script autoloading
|
GLV_QUIET = TFN_QUIET, ///< Do not emit error messages.
|
||||||
|
GLV_NO_AUTOLOAD = TFN_NO_AUTOLOAD, ///< Do not use script autoloading.
|
||||||
|
GLV_READ_ONLY = TFN_READ_ONLY, ///< Indicates that caller will not change
|
||||||
|
///< the value (prevents error message).
|
||||||
|
} GetLvalFlags;
|
||||||
|
|
||||||
// function flags
|
// function flags
|
||||||
#define FC_ABORT 0x01 // abort function on error
|
#define FC_ABORT 0x01 // abort function on error
|
||||||
@ -542,7 +551,7 @@ void eval_init(void)
|
|||||||
type_list->lv_lock = VAR_FIXED;
|
type_list->lv_lock = VAR_FIXED;
|
||||||
type_list->lv_refcount = 1;
|
type_list->lv_refcount = 1;
|
||||||
dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]);
|
dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]);
|
||||||
di->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
|
di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX;
|
||||||
di->di_tv = (typval_T) {
|
di->di_tv = (typval_T) {
|
||||||
.v_type = VAR_LIST,
|
.v_type = VAR_LIST,
|
||||||
.vval = { .v_list = type_list, },
|
.vval = { .v_list = type_list, },
|
||||||
@ -1646,13 +1655,7 @@ static void list_glob_vars(int *first)
|
|||||||
*/
|
*/
|
||||||
static void list_buf_vars(int *first)
|
static void list_buf_vars(int *first)
|
||||||
{
|
{
|
||||||
char numbuf[NUMBUFLEN];
|
|
||||||
|
|
||||||
list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first);
|
list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first);
|
||||||
|
|
||||||
snprintf(numbuf, sizeof(numbuf), "%d", curbuf->b_changedtick);
|
|
||||||
list_one_var_a("b:", "changedtick", sizeof("changedtick") - 1, VAR_NUMBER,
|
|
||||||
numbuf, first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1949,46 +1952,33 @@ ex_let_one (
|
|||||||
return arg_end;
|
return arg_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Get an lvalue
|
||||||
* If "arg" is equal to "b:changedtick" give an error and return TRUE.
|
///
|
||||||
*/
|
/// Lvalue may be
|
||||||
static int check_changedtick(char_u *arg)
|
/// - variable: "name", "na{me}"
|
||||||
{
|
/// - dictionary item: "dict.key", "dict['key']"
|
||||||
if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13])) {
|
/// - list item: "list[expr]"
|
||||||
EMSG2(_(e_readonlyvar), arg);
|
/// - list slice: "list[expr:expr]"
|
||||||
return TRUE;
|
///
|
||||||
}
|
/// Indexing only works if trying to use it with an existing List or Dictionary.
|
||||||
return FALSE;
|
///
|
||||||
}
|
/// @param[in] name Name to parse.
|
||||||
|
/// @param rettv Pointer to the value to be assigned or NULL.
|
||||||
/*
|
/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name`
|
||||||
* Get an lval: variable, Dict item or List item that can be assigned a value
|
/// is NULL.
|
||||||
* to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
|
/// @param[in] unlet True if using `:unlet`. This results in slightly
|
||||||
* "name.key", "name.key[expr]" etc.
|
/// different behaviour when something is wrong; must end in
|
||||||
* Indexing only works if "name" is an existing List or Dictionary.
|
/// space or cmd separator.
|
||||||
* "name" points to the start of the name.
|
/// @param[in] skip True when skipping.
|
||||||
* If "rettv" is not NULL it points to the value to be assigned.
|
/// @param[in] flags @see GetLvalFlags.
|
||||||
* "unlet" is TRUE for ":unlet": slightly different behavior when something is
|
/// @param[in] fne_flags Flags for find_name_end().
|
||||||
* wrong; must end in space or cmd separator.
|
///
|
||||||
*
|
/// @return A pointer to just after the name, including indexes. Returns NULL
|
||||||
* flags:
|
/// for a parsing error, but it is still needed to free items in lp.
|
||||||
* GLV_QUIET: do not give error messages
|
static char_u *get_lval(char_u *const name, typval_T *const rettv,
|
||||||
* GLV_NO_AUTOLOAD: do not use script autoloading
|
lval_T *const lp, const bool unlet, const bool skip,
|
||||||
*
|
const int flags, const int fne_flags)
|
||||||
* Returns a pointer to just after the name, including indexes.
|
FUNC_ATTR_NONNULL_ARG(1, 3)
|
||||||
* When an evaluation error occurs "lp->ll_name" is NULL;
|
|
||||||
* Returns NULL for a parsing error. Still need to free items in "lp"!
|
|
||||||
*/
|
|
||||||
static char_u *
|
|
||||||
get_lval (
|
|
||||||
char_u *name,
|
|
||||||
typval_T *rettv,
|
|
||||||
lval_T *lp,
|
|
||||||
int unlet,
|
|
||||||
int skip,
|
|
||||||
int flags, /* GLV_ values */
|
|
||||||
int fne_flags /* flags for find_name_end() */
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
char_u *expr_start, *expr_end;
|
char_u *expr_start, *expr_end;
|
||||||
@ -2207,8 +2197,13 @@ get_lval (
|
|||||||
if (len == -1)
|
if (len == -1)
|
||||||
clear_tv(&var1);
|
clear_tv(&var1);
|
||||||
break;
|
break;
|
||||||
} else if (var_check_ro(lp->ll_di->di_flags, name, false)) {
|
|
||||||
// existing variable, need to check if it can be changed
|
// existing variable, need to check if it can be changed
|
||||||
|
} else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags,
|
||||||
|
(const char *)name,
|
||||||
|
(size_t)(p - name))) {
|
||||||
|
if (len == -1) {
|
||||||
|
clear_tv(&var1);
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2299,7 +2294,6 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
|
|||||||
dictitem_T *di;
|
dictitem_T *di;
|
||||||
|
|
||||||
if (lp->ll_tv == NULL) {
|
if (lp->ll_tv == NULL) {
|
||||||
if (!check_changedtick(lp->ll_name)) {
|
|
||||||
cc = *endp;
|
cc = *endp;
|
||||||
*endp = NUL;
|
*endp = NUL;
|
||||||
if (op != NULL && *op != '=') {
|
if (op != NULL && *op != '=') {
|
||||||
@ -2310,21 +2304,23 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
|
|||||||
if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name),
|
if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name),
|
||||||
&tv, &di, true, false) == OK) {
|
&tv, &di, true, false) == OK) {
|
||||||
if ((di == NULL
|
if ((di == NULL
|
||||||
|| (!var_check_ro(di->di_flags, lp->ll_name, false)
|
|| (!var_check_ro(di->di_flags, (const char *)lp->ll_name,
|
||||||
&& !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false)))
|
STRLEN(lp->ll_name))
|
||||||
|
&& !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name,
|
||||||
|
STRLEN(lp->ll_name))))
|
||||||
&& tv_op(&tv, rettv, op) == OK) {
|
&& tv_op(&tv, rettv, op) == OK) {
|
||||||
set_var(lp->ll_name, &tv, false);
|
set_var(lp->ll_name, &tv, false);
|
||||||
}
|
}
|
||||||
clear_tv(&tv);
|
clear_tv(&tv);
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
set_var(lp->ll_name, rettv, copy);
|
set_var(lp->ll_name, rettv, copy);
|
||||||
*endp = cc;
|
|
||||||
}
|
}
|
||||||
|
*endp = cc;
|
||||||
} else if (tv_check_lock(lp->ll_newkey == NULL
|
} else if (tv_check_lock(lp->ll_newkey == NULL
|
||||||
? lp->ll_tv->v_lock
|
? lp->ll_tv->v_lock
|
||||||
: lp->ll_tv->vval.v_dict->dv_lock,
|
: lp->ll_tv->vval.v_dict->dv_lock,
|
||||||
lp->ll_name, false)) {
|
(const char *)lp->ll_name, STRLEN(lp->ll_name))) {
|
||||||
} else if (lp->ll_range) {
|
} else if (lp->ll_range) {
|
||||||
listitem_T *ll_li = lp->ll_li;
|
listitem_T *ll_li = lp->ll_li;
|
||||||
int ll_n1 = lp->ll_n1;
|
int ll_n1 = lp->ll_n1;
|
||||||
@ -2332,7 +2328,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
|
|||||||
// Check whether any of the list items is locked
|
// Check whether any of the list items is locked
|
||||||
for (listitem_T *ri = rettv->vval.v_list->lv_first;
|
for (listitem_T *ri = rettv->vval.v_list->lv_first;
|
||||||
ri != NULL && ll_li != NULL; ) {
|
ri != NULL && ll_li != NULL; ) {
|
||||||
if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) {
|
if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name,
|
||||||
|
STRLEN(lp->ll_name))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ri = ri->li_next;
|
ri = ri->li_next;
|
||||||
@ -2938,16 +2935,18 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
|
|||||||
cc = *name_end;
|
cc = *name_end;
|
||||||
*name_end = NUL;
|
*name_end = NUL;
|
||||||
|
|
||||||
/* Normal name or expanded name. */
|
// Normal name or expanded name.
|
||||||
if (check_changedtick(lp->ll_name))
|
if (do_unlet(lp->ll_name, forceit) == FAIL) {
|
||||||
ret = FAIL;
|
|
||||||
else if (do_unlet(lp->ll_name, forceit) == FAIL)
|
|
||||||
ret = FAIL;
|
ret = FAIL;
|
||||||
|
}
|
||||||
*name_end = cc;
|
*name_end = cc;
|
||||||
} else if ((lp->ll_list != NULL
|
} else if ((lp->ll_list != NULL
|
||||||
&& tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, false))
|
&& tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name,
|
||||||
|
STRLEN(lp->ll_name)))
|
||||||
|| (lp->ll_dict != NULL
|
|| (lp->ll_dict != NULL
|
||||||
&& tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, false))) {
|
&& tv_check_lock(lp->ll_dict->dv_lock,
|
||||||
|
(const char *)lp->ll_name,
|
||||||
|
STRLEN(lp->ll_name)))) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
} else if (lp->ll_range) {
|
} else if (lp->ll_range) {
|
||||||
listitem_T *li;
|
listitem_T *li;
|
||||||
@ -2956,7 +2955,8 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
|
|||||||
|
|
||||||
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) {
|
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) {
|
||||||
li = ll_li->li_next;
|
li = ll_li->li_next;
|
||||||
if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) {
|
if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name,
|
||||||
|
STRLEN(lp->ll_name))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ll_li = li;
|
ll_li = li;
|
||||||
@ -3038,13 +3038,14 @@ int do_unlet(char_u *name, int forceit)
|
|||||||
}
|
}
|
||||||
if (hi != NULL && !HASHITEM_EMPTY(hi)) {
|
if (hi != NULL && !HASHITEM_EMPTY(hi)) {
|
||||||
di = HI2DI(hi);
|
di = HI2DI(hi);
|
||||||
if (var_check_fixed(di->di_flags, name, false)
|
if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name))
|
||||||
|| var_check_ro(di->di_flags, name, false)
|
|| var_check_ro(di->di_flags, (const char *)name, STRLEN(name))
|
||||||
|| tv_check_lock(d->dv_lock, name, false)) {
|
|| tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d == NULL || tv_check_lock(d->dv_lock, name, false)) {
|
if (d == NULL
|
||||||
|
|| tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3078,23 +3079,25 @@ int do_unlet(char_u *name, int forceit)
|
|||||||
static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock)
|
static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock)
|
||||||
{
|
{
|
||||||
int ret = OK;
|
int ret = OK;
|
||||||
int cc;
|
|
||||||
dictitem_T *di;
|
|
||||||
|
|
||||||
if (deep == 0) /* nothing to do */
|
if (deep == 0) { // Nothing to do.
|
||||||
return OK;
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (lp->ll_tv == NULL) {
|
if (lp->ll_tv == NULL) {
|
||||||
cc = *name_end;
|
|
||||||
*name_end = NUL;
|
|
||||||
|
|
||||||
// Normal name or expanded name.
|
// Normal name or expanded name.
|
||||||
if (check_changedtick(lp->ll_name)) {
|
const size_t name_len = (size_t)(name_end - lp->ll_name);
|
||||||
ret = FAIL;
|
dictitem_T *const di = find_var(
|
||||||
} else {
|
(const char *)lp->ll_name, name_len, NULL,
|
||||||
di = find_var((const char *)lp->ll_name, STRLEN(lp->ll_name), NULL, true);
|
true);
|
||||||
if (di == NULL) {
|
if (di == NULL) {
|
||||||
ret = FAIL;
|
ret = FAIL;
|
||||||
|
} else if ((di->di_flags & DI_FLAGS_FIX)
|
||||||
|
&& di->di_tv.v_type != VAR_DICT
|
||||||
|
&& di->di_tv.v_type != VAR_LIST) {
|
||||||
|
// For historical reasons this error is not given for Lists and
|
||||||
|
// Dictionaries. E.g. b: dictionary may be locked/unlocked.
|
||||||
|
emsgf(_("E940: Cannot lock or unlock variable %s"), lp->ll_name);
|
||||||
} else {
|
} else {
|
||||||
if (lock) {
|
if (lock) {
|
||||||
di->di_flags |= DI_FLAGS_LOCK;
|
di->di_flags |= DI_FLAGS_LOCK;
|
||||||
@ -3103,8 +3106,6 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock)
|
|||||||
}
|
}
|
||||||
item_lock(&di->di_tv, deep, lock);
|
item_lock(&di->di_tv, deep, lock);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
*name_end = cc;
|
|
||||||
} else if (lp->ll_range) {
|
} else if (lp->ll_range) {
|
||||||
listitem_T *li = lp->ll_li;
|
listitem_T *li = lp->ll_li;
|
||||||
|
|
||||||
@ -3131,68 +3132,72 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock)
|
|||||||
static void item_lock(typval_T *tv, int deep, int lock)
|
static void item_lock(typval_T *tv, int deep, int lock)
|
||||||
{
|
{
|
||||||
static int recurse = 0;
|
static int recurse = 0;
|
||||||
list_T *l;
|
|
||||||
listitem_T *li;
|
|
||||||
dict_T *d;
|
|
||||||
hashitem_T *hi;
|
|
||||||
int todo;
|
|
||||||
|
|
||||||
if (recurse >= DICT_MAXNEST) {
|
if (recurse >= DICT_MAXNEST) {
|
||||||
EMSG(_("E743: variable nested too deep for (un)lock"));
|
EMSG(_("E743: variable nested too deep for (un)lock"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (deep == 0)
|
if (deep == 0) {
|
||||||
return;
|
return;
|
||||||
++recurse;
|
}
|
||||||
|
recurse++;
|
||||||
|
|
||||||
/* lock/unlock the item itself */
|
// lock/unlock the item itself
|
||||||
if (lock)
|
#define CHANGE_LOCK(var, lock) \
|
||||||
tv->v_lock |= VAR_LOCKED;
|
do { \
|
||||||
else
|
var = ((VarLockStatus[]) { \
|
||||||
tv->v_lock &= ~VAR_LOCKED;
|
[VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \
|
||||||
|
[VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \
|
||||||
|
[VAR_FIXED] = VAR_FIXED, \
|
||||||
|
})[var]; \
|
||||||
|
} while (0)
|
||||||
|
CHANGE_LOCK(tv->v_lock, lock);
|
||||||
|
|
||||||
switch (tv->v_type) {
|
switch (tv->v_type) {
|
||||||
case VAR_LIST:
|
case VAR_LIST: {
|
||||||
if ((l = tv->vval.v_list) != NULL) {
|
list_T *const l = tv->vval.v_list;
|
||||||
if (lock)
|
if (l != NULL) {
|
||||||
l->lv_lock |= VAR_LOCKED;
|
CHANGE_LOCK(l->lv_lock, lock);
|
||||||
else
|
if (deep < 0 || deep > 1) {
|
||||||
l->lv_lock &= ~VAR_LOCKED;
|
// Recursive: lock/unlock the items the List contains.
|
||||||
if (deep < 0 || deep > 1)
|
for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) {
|
||||||
/* recursive: lock/unlock the items the List contains */
|
|
||||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
|
||||||
item_lock(&li->li_tv, deep - 1, lock);
|
item_lock(&li->li_tv, deep - 1, lock);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case VAR_DICT:
|
}
|
||||||
if ((d = tv->vval.v_dict) != NULL) {
|
case VAR_DICT: {
|
||||||
if (lock)
|
dict_T *const d = tv->vval.v_dict;
|
||||||
d->dv_lock |= VAR_LOCKED;
|
if (d != NULL) {
|
||||||
else
|
CHANGE_LOCK(d->dv_lock, lock);
|
||||||
d->dv_lock &= ~VAR_LOCKED;
|
|
||||||
if (deep < 0 || deep > 1) {
|
if (deep < 0 || deep > 1) {
|
||||||
/* recursive: lock/unlock the items the List contains */
|
// Recursive: lock/unlock the items the List contains.
|
||||||
todo = (int)d->dv_hashtab.ht_used;
|
int todo = (int)d->dv_hashtab.ht_used;
|
||||||
for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) {
|
for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
|
||||||
if (!HASHITEM_EMPTY(hi)) {
|
if (!HASHITEM_EMPTY(hi)) {
|
||||||
--todo;
|
todo--;
|
||||||
item_lock(&HI2DI(hi)->di_tv, deep - 1, lock);
|
item_lock(&HI2DI(hi)->di_tv, deep - 1, lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
case VAR_STRING:
|
case VAR_STRING:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
case VAR_PARTIAL:
|
case VAR_PARTIAL:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL: {
|
||||||
break;
|
break;
|
||||||
case VAR_UNKNOWN:
|
}
|
||||||
|
case VAR_UNKNOWN: {
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
--recurse;
|
}
|
||||||
|
#undef CHANGE_LOCK
|
||||||
|
recurse--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3302,10 +3307,6 @@ char_u *get_user_var_name(expand_T *xp, int idx)
|
|||||||
++hi;
|
++hi;
|
||||||
return cat_prefix_varname('b', hi->hi_key);
|
return cat_prefix_varname('b', hi->hi_key);
|
||||||
}
|
}
|
||||||
if (bdone == ht->ht_used) {
|
|
||||||
++bdone;
|
|
||||||
return (char_u *)"b:changedtick";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* w: variables */
|
/* w: variables */
|
||||||
ht = &curwin->w_vars->dv_hashtab;
|
ht = &curwin->w_vars->dv_hashtab;
|
||||||
@ -6373,7 +6374,7 @@ dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET
|
|||||||
first_dict = d;
|
first_dict = d;
|
||||||
|
|
||||||
hash_init(&d->dv_hashtab);
|
hash_init(&d->dv_hashtab);
|
||||||
d->dv_lock = 0;
|
d->dv_lock = VAR_UNLOCKED;
|
||||||
d->dv_scope = 0;
|
d->dv_scope = 0;
|
||||||
d->dv_refcount = 0;
|
d->dv_refcount = 0;
|
||||||
d->dv_copyID = 0;
|
d->dv_copyID = 0;
|
||||||
@ -6446,9 +6447,8 @@ static void dict_free_contents(dict_T *d) {
|
|||||||
* something recursive causing trouble. */
|
* something recursive causing trouble. */
|
||||||
di = HI2DI(hi);
|
di = HI2DI(hi);
|
||||||
hash_remove(&d->dv_hashtab, hi);
|
hash_remove(&d->dv_hashtab, hi);
|
||||||
clear_tv(&di->di_tv);
|
dictitem_free(di);
|
||||||
xfree(di);
|
todo--;
|
||||||
--todo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7820,9 +7820,10 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
rettv->vval.v_number = 1; /* Default: Failed */
|
rettv->vval.v_number = 1; /* Default: Failed */
|
||||||
if (argvars[0].v_type == VAR_LIST) {
|
if (argvars[0].v_type == VAR_LIST) {
|
||||||
|
const char *const arg_errmsg = _("add() argument");
|
||||||
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
if ((l = argvars[0].vval.v_list) != NULL
|
if ((l = argvars[0].vval.v_list) != NULL
|
||||||
&& !tv_check_lock(l->lv_lock,
|
&& !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) {
|
||||||
(char_u *)N_("add() argument"), true)) {
|
|
||||||
list_append_tv(l, &argvars[1]);
|
list_append_tv(l, &argvars[1]);
|
||||||
copy_tv(&argvars[0], rettv);
|
copy_tv(&argvars[0], rettv);
|
||||||
}
|
}
|
||||||
@ -9399,7 +9400,8 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
|
|||||||
hashitem_T *hi2;
|
hashitem_T *hi2;
|
||||||
int todo;
|
int todo;
|
||||||
bool watched = is_watched(d1);
|
bool watched = is_watched(d1);
|
||||||
char_u *arg_errmsg = (char_u *)N_("extend() argument");
|
const char *const arg_errmsg = _("extend() argument");
|
||||||
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
|
|
||||||
todo = (int)d2->dv_hashtab.ht_used;
|
todo = (int)d2->dv_hashtab.ht_used;
|
||||||
for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) {
|
for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) {
|
||||||
@ -9433,8 +9435,8 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
|
|||||||
} else if (*action == 'f' && HI2DI(hi2) != di1) {
|
} else if (*action == 'f' && HI2DI(hi2) != di1) {
|
||||||
typval_T oldtv;
|
typval_T oldtv;
|
||||||
|
|
||||||
if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, true)
|
if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
|
||||||
|| var_check_ro(di1->di_flags, arg_errmsg, true)) {
|
|| var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9460,7 +9462,8 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
|
|||||||
*/
|
*/
|
||||||
static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
char_u *arg_errmsg = (char_u *)N_("extend() argument");
|
const char *const arg_errmsg = N_("extend() argument");
|
||||||
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
|
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
|
||||||
list_T *l1, *l2;
|
list_T *l1, *l2;
|
||||||
@ -9470,7 +9473,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
l1 = argvars[0].vval.v_list;
|
l1 = argvars[0].vval.v_list;
|
||||||
l2 = argvars[1].vval.v_list;
|
l2 = argvars[1].vval.v_list;
|
||||||
if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, true)
|
if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len)
|
||||||
&& l2 != NULL) {
|
&& l2 != NULL) {
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
before = get_tv_number_chk(&argvars[2], &error);
|
before = get_tv_number_chk(&argvars[2], &error);
|
||||||
@ -9500,7 +9503,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
d1 = argvars[0].vval.v_dict;
|
d1 = argvars[0].vval.v_dict;
|
||||||
d2 = argvars[1].vval.v_dict;
|
d2 = argvars[1].vval.v_dict;
|
||||||
if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, true)
|
if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len)
|
||||||
&& d2 != NULL) {
|
&& d2 != NULL) {
|
||||||
/* Check the third argument. */
|
/* Check the third argument. */
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
@ -9644,19 +9647,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
int rem = false;
|
int rem = false;
|
||||||
int todo;
|
int todo;
|
||||||
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
|
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
|
||||||
char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
|
const char *const arg_errmsg = (map
|
||||||
: N_("filter() argument"));
|
? _("map() argument")
|
||||||
|
: _("filter() argument"));
|
||||||
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
int save_did_emsg;
|
int save_did_emsg;
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_LIST) {
|
if (argvars[0].v_type == VAR_LIST) {
|
||||||
if ((l = argvars[0].vval.v_list) == NULL
|
if ((l = argvars[0].vval.v_list) == NULL
|
||||||
|| (!map && tv_check_lock(l->lv_lock, arg_errmsg, true))) {
|
|| (!map && tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (argvars[0].v_type == VAR_DICT) {
|
} else if (argvars[0].v_type == VAR_DICT) {
|
||||||
if ((d = argvars[0].vval.v_dict) == NULL
|
if ((d = argvars[0].vval.v_dict) == NULL
|
||||||
|| (!map && tv_check_lock(d->dv_lock, arg_errmsg, true))) {
|
|| (!map && tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -9689,8 +9694,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
|
|
||||||
di = HI2DI(hi);
|
di = HI2DI(hi);
|
||||||
if (map
|
if (map
|
||||||
&& (tv_check_lock(di->di_tv.v_lock, arg_errmsg, true)
|
&& (tv_check_lock(di->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
|
||||||
|| var_check_ro(di->di_flags, arg_errmsg, true))) {
|
|| var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9700,8 +9705,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
if (r == FAIL || did_emsg)
|
if (r == FAIL || did_emsg)
|
||||||
break;
|
break;
|
||||||
if (!map && rem) {
|
if (!map && rem) {
|
||||||
if (var_check_fixed(di->di_flags, arg_errmsg, true)
|
if (var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len)
|
||||||
|| var_check_ro(di->di_flags, arg_errmsg, true)) {
|
|| var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dictitem_remove(d, di);
|
dictitem_remove(d, di);
|
||||||
@ -9713,7 +9718,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
|||||||
vimvars[VV_KEY].vv_type = VAR_NUMBER;
|
vimvars[VV_KEY].vv_type = VAR_NUMBER;
|
||||||
|
|
||||||
for (li = l->lv_first; li != NULL; li = nli) {
|
for (li = l->lv_first; li != NULL; li = nli) {
|
||||||
if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, true)) {
|
if (map
|
||||||
|
&& tv_check_lock(li->li_tv.v_lock, arg_errmsg, arg_errmsg_len)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nli = li->li_next;
|
nli = li->li_next;
|
||||||
@ -10524,10 +10530,6 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
// buffer-local-option
|
// buffer-local-option
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
} else if (STRCMP(varname, "changedtick") == 0) {
|
|
||||||
rettv->v_type = VAR_NUMBER;
|
|
||||||
rettv->vval.v_number = curbuf->b_changedtick;
|
|
||||||
done = true;
|
|
||||||
} else {
|
} else {
|
||||||
// Look up the variable.
|
// Look up the variable.
|
||||||
// Let getbufvar({nr}, "") return the "b:" dictionary.
|
// Let getbufvar({nr}, "") return the "b:" dictionary.
|
||||||
@ -12395,13 +12397,14 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
long before = 0;
|
long before = 0;
|
||||||
listitem_T *item;
|
listitem_T *item;
|
||||||
list_T *l;
|
list_T *l;
|
||||||
int error = FALSE;
|
int error = false;
|
||||||
|
const char *const arg_errmsg = _("insert() argument");
|
||||||
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
|
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
if (argvars[0].v_type != VAR_LIST) {
|
||||||
EMSG2(_(e_listarg), "insert()");
|
EMSG2(_(e_listarg), "insert()");
|
||||||
} else if ((l = argvars[0].vval.v_list) != NULL
|
} else if ((l = argvars[0].vval.v_list) != NULL
|
||||||
&& !tv_check_lock(l->lv_lock,
|
&& !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) {
|
||||||
(char_u *)N_("insert() argument"), true)) {
|
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
before = get_tv_number_chk(&argvars[2], &error);
|
before = get_tv_number_chk(&argvars[2], &error);
|
||||||
}
|
}
|
||||||
@ -12452,40 +12455,35 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
dictitem_T *di;
|
dictitem_T *di;
|
||||||
|
|
||||||
rettv->vval.v_number = -1;
|
rettv->vval.v_number = -1;
|
||||||
end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, FALSE, FALSE,
|
end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, false, false,
|
||||||
GLV_NO_AUTOLOAD, FNE_CHECK_START);
|
GLV_NO_AUTOLOAD|GLV_READ_ONLY, FNE_CHECK_START);
|
||||||
if (end != NULL && lv.ll_name != NULL) {
|
if (end != NULL && lv.ll_name != NULL) {
|
||||||
if (*end != NUL)
|
if (*end != NUL)
|
||||||
EMSG(_(e_trailing));
|
EMSG(_(e_trailing));
|
||||||
else {
|
else {
|
||||||
if (lv.ll_tv == NULL) {
|
if (lv.ll_tv == NULL) {
|
||||||
if (check_changedtick(lv.ll_name)) {
|
di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true);
|
||||||
rettv->vval.v_number = 1; // Always locked.
|
|
||||||
} else {
|
|
||||||
di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL,
|
|
||||||
true);
|
|
||||||
if (di != NULL) {
|
if (di != NULL) {
|
||||||
/* Consider a variable locked when:
|
// Consider a variable locked when:
|
||||||
* 1. the variable itself is locked
|
// 1. the variable itself is locked
|
||||||
* 2. the value of the variable is locked.
|
// 2. the value of the variable is locked.
|
||||||
* 3. the List or Dict value is locked.
|
// 3. the List or Dict value is locked.
|
||||||
*/
|
|
||||||
rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK)
|
rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK)
|
||||||
|| tv_islocked(&di->di_tv));
|
|| tv_islocked(&di->di_tv));
|
||||||
}
|
}
|
||||||
}
|
} else if (lv.ll_range) {
|
||||||
} else if (lv.ll_range)
|
|
||||||
EMSG(_("E786: Range not allowed"));
|
EMSG(_("E786: Range not allowed"));
|
||||||
else if (lv.ll_newkey != NULL)
|
} else if (lv.ll_newkey != NULL) {
|
||||||
EMSG2(_(e_dictkey), lv.ll_newkey);
|
EMSG2(_(e_dictkey), lv.ll_newkey);
|
||||||
else if (lv.ll_list != NULL)
|
} else if (lv.ll_list != NULL) {
|
||||||
/* List item. */
|
// List item.
|
||||||
rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv);
|
rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv);
|
||||||
else
|
} else {
|
||||||
/* Dictionary item. */
|
// Dictionary item.
|
||||||
rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv);
|
rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clear_lval(&lv);
|
clear_lval(&lv);
|
||||||
}
|
}
|
||||||
@ -14430,20 +14428,21 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
char_u *key;
|
char_u *key;
|
||||||
dict_T *d;
|
dict_T *d;
|
||||||
dictitem_T *di;
|
dictitem_T *di;
|
||||||
char_u *arg_errmsg = (char_u *)N_("remove() argument");
|
const char *const arg_errmsg = _("remove() argument");
|
||||||
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_DICT) {
|
if (argvars[0].v_type == VAR_DICT) {
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
EMSG2(_(e_toomanyarg), "remove()");
|
EMSG2(_(e_toomanyarg), "remove()");
|
||||||
} else if ((d = argvars[0].vval.v_dict) != NULL
|
} else if ((d = argvars[0].vval.v_dict) != NULL
|
||||||
&& !tv_check_lock(d->dv_lock, arg_errmsg, true)) {
|
&& !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) {
|
||||||
key = get_tv_string_chk(&argvars[1]);
|
key = get_tv_string_chk(&argvars[1]);
|
||||||
if (key != NULL) {
|
if (key != NULL) {
|
||||||
di = dict_find(d, key, -1);
|
di = dict_find(d, key, -1);
|
||||||
if (di == NULL) {
|
if (di == NULL) {
|
||||||
EMSG2(_(e_dictkey), key);
|
EMSG2(_(e_dictkey), key);
|
||||||
} else if (!var_check_fixed(di->di_flags, arg_errmsg, true)
|
} else if (!var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len)
|
||||||
&& !var_check_ro(di->di_flags, arg_errmsg, true)) {
|
&& !var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) {
|
||||||
*rettv = di->di_tv;
|
*rettv = di->di_tv;
|
||||||
init_tv(&di->di_tv);
|
init_tv(&di->di_tv);
|
||||||
dictitem_remove(d, di);
|
dictitem_remove(d, di);
|
||||||
@ -14456,7 +14455,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
} else if (argvars[0].v_type != VAR_LIST) {
|
} else if (argvars[0].v_type != VAR_LIST) {
|
||||||
EMSG2(_(e_listdictarg), "remove()");
|
EMSG2(_(e_listdictarg), "remove()");
|
||||||
} else if ((l = argvars[0].vval.v_list) != NULL
|
} else if ((l = argvars[0].vval.v_list) != NULL
|
||||||
&& !tv_check_lock(l->lv_lock, arg_errmsg, true)) {
|
&& !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) {
|
||||||
int error = (int)false;
|
int error = (int)false;
|
||||||
|
|
||||||
idx = get_tv_number_chk(&argvars[1], &error);
|
idx = get_tv_number_chk(&argvars[1], &error);
|
||||||
@ -14727,19 +14726,19 @@ fail:
|
|||||||
*/
|
*/
|
||||||
static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
list_T *l;
|
const char *const arg_errmsg = _("reverse() argument");
|
||||||
listitem_T *li, *ni;
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
|
|
||||||
|
list_T *l;
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
if (argvars[0].v_type != VAR_LIST) {
|
||||||
EMSG2(_(e_listarg), "reverse()");
|
EMSG2(_(e_listarg), "reverse()");
|
||||||
} else if ((l = argvars[0].vval.v_list) != NULL
|
} else if ((l = argvars[0].vval.v_list) != NULL
|
||||||
&& !tv_check_lock(l->lv_lock,
|
&& !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) {
|
||||||
(char_u *)N_("reverse() argument"), true)) {
|
listitem_T *li = l->lv_last;
|
||||||
li = l->lv_last;
|
|
||||||
l->lv_first = l->lv_last = NULL;
|
l->lv_first = l->lv_last = NULL;
|
||||||
l->lv_len = 0;
|
l->lv_len = 0;
|
||||||
while (li != NULL) {
|
while (li != NULL) {
|
||||||
ni = li->li_prev;
|
listitem_T *const ni = li->li_prev;
|
||||||
list_append(l, li);
|
list_append(l, li);
|
||||||
li = ni;
|
li = ni;
|
||||||
}
|
}
|
||||||
@ -16384,16 +16383,17 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
|
|||||||
sortinfo_T *old_sortinfo = sortinfo;
|
sortinfo_T *old_sortinfo = sortinfo;
|
||||||
sortinfo = &info;
|
sortinfo = &info;
|
||||||
|
|
||||||
|
const char *const arg_errmsg = (sort
|
||||||
|
? _("sort() argument")
|
||||||
|
: _("uniq() argument"));
|
||||||
|
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||||
|
|
||||||
if (argvars[0].v_type != VAR_LIST) {
|
if (argvars[0].v_type != VAR_LIST) {
|
||||||
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
|
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
|
||||||
} else {
|
} else {
|
||||||
l = argvars[0].vval.v_list;
|
l = argvars[0].vval.v_list;
|
||||||
if (l == NULL
|
if (l == NULL
|
||||||
|| tv_check_lock(l->lv_lock,
|
|| tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) {
|
||||||
(char_u *)(sort
|
|
||||||
? N_("sort() argument")
|
|
||||||
: N_("uniq() argument")),
|
|
||||||
true)) {
|
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
rettv->vval.v_list = l;
|
rettv->vval.v_list = l;
|
||||||
@ -17732,9 +17732,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
(void)setfname(curbuf, (uint8_t *)buf, NULL, true);
|
(void)setfname(curbuf, (uint8_t *)buf, NULL, true);
|
||||||
// Save the job id and pid in b:terminal_job_{id,pid}
|
// Save the job id and pid in b:terminal_job_{id,pid}
|
||||||
Error err;
|
Error err;
|
||||||
dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_id"),
|
dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
|
||||||
INTEGER_OBJ(rettv->vval.v_number), false, false, &err);
|
INTEGER_OBJ(rettv->vval.v_number), false, false, &err);
|
||||||
dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
|
dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
|
||||||
INTEGER_OBJ(pid), false, false, &err);
|
INTEGER_OBJ(pid), false, false, &err);
|
||||||
|
|
||||||
Terminal *term = terminal_open(topts);
|
Terminal *term = terminal_open(topts);
|
||||||
@ -19382,17 +19382,8 @@ static int get_var_tv(
|
|||||||
{
|
{
|
||||||
int ret = OK;
|
int ret = OK;
|
||||||
typval_T *tv = NULL;
|
typval_T *tv = NULL;
|
||||||
typval_T atv;
|
|
||||||
dictitem_T *v;
|
dictitem_T *v;
|
||||||
|
|
||||||
// Check for "b:changedtick".
|
|
||||||
if (sizeof("b:changedtick") - 1 == len
|
|
||||||
&& STRNCMP(name, "b:changedtick", len) == 0) {
|
|
||||||
atv.v_type = VAR_NUMBER;
|
|
||||||
atv.vval.v_number = curbuf->b_changedtick;
|
|
||||||
tv = &atv;
|
|
||||||
} else {
|
|
||||||
// Check for user-defined variables.
|
|
||||||
v = find_var(name, (size_t)len, NULL, no_autoload);
|
v = find_var(name, (size_t)len, NULL, no_autoload);
|
||||||
if (v != NULL) {
|
if (v != NULL) {
|
||||||
tv = &v->di_tv;
|
tv = &v->di_tv;
|
||||||
@ -19400,7 +19391,6 @@ static int get_var_tv(
|
|||||||
*dip = v;
|
*dip = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (tv == NULL) {
|
if (tv == NULL) {
|
||||||
if (rettv != NULL && verbose) {
|
if (rettv != NULL && verbose) {
|
||||||
@ -20381,7 +20371,7 @@ void new_script_vars(scid_T id)
|
|||||||
void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope)
|
void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope)
|
||||||
{
|
{
|
||||||
hash_init(&dict->dv_hashtab);
|
hash_init(&dict->dv_hashtab);
|
||||||
dict->dv_lock = 0;
|
dict->dv_lock = VAR_UNLOCKED;
|
||||||
dict->dv_scope = scope;
|
dict->dv_scope = scope;
|
||||||
dict->dv_refcount = DO_NOT_FREE_CNT;
|
dict->dv_refcount = DO_NOT_FREE_CNT;
|
||||||
dict->dv_copyID = 0;
|
dict->dv_copyID = 0;
|
||||||
@ -20557,8 +20547,8 @@ set_var (
|
|||||||
|
|
||||||
if (v != NULL) {
|
if (v != NULL) {
|
||||||
// existing variable, need to clear the value
|
// existing variable, need to clear the value
|
||||||
if (var_check_ro(v->di_flags, name, false)
|
if (var_check_ro(v->di_flags, (const char *)name, name_len)
|
||||||
|| tv_check_lock(v->di_tv.v_lock, name, false)) {
|
|| tv_check_lock(v->di_tv.v_lock, (const char *)name, name_len)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20631,28 +20621,47 @@ set_var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if di_flags "flags" indicates variable "name" is read-only.
|
/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX)
|
||||||
// Also give an error message.
|
///
|
||||||
static bool var_check_ro(int flags, char_u *name, bool use_gettext)
|
/// Also gives an error message.
|
||||||
|
///
|
||||||
|
/// @param[in] flags di_flags attribute value.
|
||||||
|
/// @param[in] name Variable name, for use in error message.
|
||||||
|
/// @param[in] name_len Variable name length.
|
||||||
|
///
|
||||||
|
/// @return True if variable is read-only: either always or in sandbox when
|
||||||
|
/// sandbox is enabled, false otherwise.
|
||||||
|
static bool var_check_ro(const int flags, const char *const name,
|
||||||
|
const size_t name_len)
|
||||||
|
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
if (flags & DI_FLAGS_RO) {
|
if (flags & DI_FLAGS_RO) {
|
||||||
EMSG2(_(e_readonlyvar), use_gettext ? (char_u *)_(name) : name);
|
emsgf(_(e_readonlyvar), (int)name_len, name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((flags & DI_FLAGS_RO_SBX) && sandbox) {
|
if ((flags & DI_FLAGS_RO_SBX) && sandbox) {
|
||||||
EMSG2(_(e_readonlysbx), use_gettext ? (char_u *)_(name) : name);
|
emsgf(_("E794: Cannot set variable in the sandbox: \"%.*s\""),
|
||||||
|
(int)name_len, name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if di_flags "flags" indicates variable "name" is fixed.
|
/// Check whether variable is fixed (DI_FLAGS_FIX)
|
||||||
// Also give an error message.
|
///
|
||||||
static bool var_check_fixed(int flags, char_u *name, bool use_gettext)
|
/// Also gives an error message.
|
||||||
|
///
|
||||||
|
/// @param[in] flags di_flags attribute value.
|
||||||
|
/// @param[in] name Variable name, for use in error message.
|
||||||
|
/// @param[in] name_len Variable name length.
|
||||||
|
///
|
||||||
|
/// @return True if variable is fixed, false otherwise.
|
||||||
|
static bool var_check_fixed(const int flags, const char *const name,
|
||||||
|
const size_t name_len)
|
||||||
|
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
if (flags & DI_FLAGS_FIX) {
|
if (flags & DI_FLAGS_FIX) {
|
||||||
EMSG2(_("E795: Cannot delete variable %s"),
|
emsgf(_("E795: Cannot delete variable %.*s"), (int)name_len, name);
|
||||||
use_gettext ? (char_u *)_(name) : name);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -20703,29 +20712,43 @@ static int valid_varname(char_u *varname)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if typeval "tv" is set to be locked (immutable).
|
/// Check whether typval is locked
|
||||||
// Also give an error message, using "name" or _("name") when use_gettext is
|
///
|
||||||
// true.
|
/// Also gives an error message.
|
||||||
static bool tv_check_lock(int lock, char_u *name, bool use_gettext)
|
///
|
||||||
|
/// @param[in] lock Lock status.
|
||||||
|
/// @param[in] name Value name, for use in error message.
|
||||||
|
/// @param[in] name_len Value name length.
|
||||||
|
///
|
||||||
|
/// @return True if value is locked.
|
||||||
|
static bool tv_check_lock(const VarLockStatus lock,
|
||||||
|
const char *const name,
|
||||||
|
const size_t name_len)
|
||||||
|
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
{
|
{
|
||||||
if (lock & VAR_LOCKED) {
|
const char *error_message = NULL;
|
||||||
EMSG2(_("E741: Value is locked: %s"),
|
switch (lock) {
|
||||||
name == NULL
|
case VAR_UNLOCKED: {
|
||||||
? (char_u *)_("Unknown")
|
|
||||||
: use_gettext ? (char_u *)_(name)
|
|
||||||
: name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (lock & VAR_FIXED) {
|
|
||||||
EMSG2(_("E742: Cannot change value of %s"),
|
|
||||||
name == NULL
|
|
||||||
? (char_u *)_("Unknown")
|
|
||||||
: use_gettext ? (char_u *)_(name)
|
|
||||||
: name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
case VAR_LOCKED: {
|
||||||
|
error_message = N_("E741: Value is locked: %.*s");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VAR_FIXED: {
|
||||||
|
error_message = N_("E742: Cannot change value of %.*s");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(error_message != NULL);
|
||||||
|
|
||||||
|
const char *const unknown_name = _("Unknown");
|
||||||
|
|
||||||
|
emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)),
|
||||||
|
(name != NULL ? name : unknown_name));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the values from typval_T "from" to typval_T "to".
|
* Copy the values from typval_T "from" to typval_T "to".
|
||||||
@ -21523,11 +21546,13 @@ void ex_function(exarg_T *eap)
|
|||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
if (fudi.fd_di == NULL) {
|
if (fudi.fd_di == NULL) {
|
||||||
if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, false)) {
|
if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
|
||||||
|
STRLEN(eap->arg))) {
|
||||||
// Can't add a function to a locked dictionary
|
// Can't add a function to a locked dictionary
|
||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
} else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, false)) {
|
} else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
|
||||||
|
STRLEN(eap->arg))) {
|
||||||
// Can't change an existing function if it is locked
|
// Can't change an existing function if it is locked
|
||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ typedef enum {
|
|||||||
/// Variable lock status for typval_T.v_lock
|
/// Variable lock status for typval_T.v_lock
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VAR_UNLOCKED = 0, ///< Not locked.
|
VAR_UNLOCKED = 0, ///< Not locked.
|
||||||
VAR_LOCKED, ///< User lock, can be unlocked.
|
VAR_LOCKED = 1, ///< User lock, can be unlocked.
|
||||||
VAR_FIXED, ///< Locked forever.
|
VAR_FIXED = 2, ///< Locked forever.
|
||||||
} VarLockStatus;
|
} VarLockStatus;
|
||||||
|
|
||||||
/// VimL variable types, for use in typval_T.v_type
|
/// VimL variable types, for use in typval_T.v_type
|
||||||
@ -93,18 +93,18 @@ struct listwatch_S {
|
|||||||
* Structure to hold info about a list.
|
* Structure to hold info about a list.
|
||||||
*/
|
*/
|
||||||
struct listvar_S {
|
struct listvar_S {
|
||||||
listitem_T *lv_first; /* first item, NULL if none */
|
listitem_T *lv_first; ///< First item, NULL if none.
|
||||||
listitem_T *lv_last; /* last item, NULL if none */
|
listitem_T *lv_last; ///< Last item, NULL if none.
|
||||||
int lv_refcount; /* reference count */
|
int lv_refcount; ///< Reference count.
|
||||||
int lv_len; /* number of items */
|
int lv_len; ///< Number of items.
|
||||||
listwatch_T *lv_watch; /* first watcher, NULL if none */
|
listwatch_T *lv_watch; ///< First watcher, NULL if none.
|
||||||
int lv_idx; /* cached index of an item */
|
int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx].
|
||||||
listitem_T *lv_idx_item; /* when not NULL item at index "lv_idx" */
|
listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx".
|
||||||
int lv_copyID; /* ID used by deepcopy() */
|
int lv_copyID; ///< ID used by deepcopy().
|
||||||
list_T *lv_copylist; /* copied list used by deepcopy() */
|
list_T *lv_copylist; ///< Copied list used by deepcopy().
|
||||||
char lv_lock; /* zero, VAR_LOCKED, VAR_FIXED */
|
VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED.
|
||||||
list_T *lv_used_next; /* next list in used lists list */
|
list_T *lv_used_next; ///< next list in used lists list.
|
||||||
list_T *lv_used_prev; /* previous list in used lists list */
|
list_T *lv_used_prev; ///< Previous list in used lists list.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Static list with 10 items. Use init_static_list() to initialize.
|
// Static list with 10 items. Use init_static_list() to initialize.
|
||||||
|
@ -6139,7 +6139,7 @@ void ex_substitute(exarg_T *eap)
|
|||||||
// restore it so that undotree() is identical before/after the preview.
|
// restore it so that undotree() is identical before/after the preview.
|
||||||
curbuf->b_u_newhead = save_b_u_newhead;
|
curbuf->b_u_newhead = save_b_u_newhead;
|
||||||
curbuf->b_u_time_cur = save_b_u_time_cur;
|
curbuf->b_u_time_cur = save_b_u_time_cur;
|
||||||
curbuf->b_changedtick = save_changedtick;
|
buf_set_changedtick(curbuf, save_changedtick);
|
||||||
}
|
}
|
||||||
if (buf_valid(preview_buf)) {
|
if (buf_valid(preview_buf)) {
|
||||||
// XXX: Must do this *after* u_undo_and_forget(), why?
|
// XXX: Must do this *after* u_undo_and_forget(), why?
|
||||||
|
@ -1172,10 +1172,6 @@ EXTERN char_u e_re_damg[] INIT(= N_("E43: Damaged match string"));
|
|||||||
EXTERN char_u e_re_corr[] INIT(= N_("E44: Corrupted regexp program"));
|
EXTERN char_u e_re_corr[] INIT(= N_("E44: Corrupted regexp program"));
|
||||||
EXTERN char_u e_readonly[] INIT(= N_(
|
EXTERN char_u e_readonly[] INIT(= N_(
|
||||||
"E45: 'readonly' option is set (add ! to override)"));
|
"E45: 'readonly' option is set (add ! to override)"));
|
||||||
EXTERN char_u e_readonlyvar[] INIT(= N_(
|
|
||||||
"E46: Cannot change read-only variable \"%s\""));
|
|
||||||
EXTERN char_u e_readonlysbx[] INIT(= N_(
|
|
||||||
"E794: Cannot set variable in the sandbox: \"%s\""));
|
|
||||||
EXTERN char_u e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
|
EXTERN char_u e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
|
||||||
EXTERN char_u e_sandbox[] INIT(= N_("E48: Not allowed in sandbox"));
|
EXTERN char_u e_sandbox[] INIT(= N_("E48: Not allowed in sandbox"));
|
||||||
EXTERN char_u e_secure[] INIT(= N_("E523: Not allowed here"));
|
EXTERN char_u e_secure[] INIT(= N_("E523: Not allowed here"));
|
||||||
|
@ -576,9 +576,9 @@ void getout(int exitval)
|
|||||||
buf_T *buf = wp->w_buffer;
|
buf_T *buf = wp->w_buffer;
|
||||||
if (buf->b_changedtick != -1) {
|
if (buf->b_changedtick != -1) {
|
||||||
apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname,
|
apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname,
|
||||||
buf->b_fname, FALSE, buf);
|
buf->b_fname, false, buf);
|
||||||
buf->b_changedtick = -1; /* note that we did it already */
|
buf_set_changedtick(buf, -1); // note that we did it already
|
||||||
/* start all over, autocommands may mess up the lists */
|
// start all over, autocommands may mess up the lists
|
||||||
next_tp = first_tabpage;
|
next_tp = first_tabpage;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1180,7 +1180,7 @@ void ml_recover(void)
|
|||||||
* empty. Don't set the modified flag then. */
|
* empty. Don't set the modified flag then. */
|
||||||
if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL)) {
|
if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL)) {
|
||||||
changed_int();
|
changed_int();
|
||||||
++curbuf->b_changedtick;
|
buf_set_changedtick(curbuf, curbuf->b_changedtick + 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (idx = 1; idx <= lnum; ++idx) {
|
for (idx = 1; idx <= lnum; ++idx) {
|
||||||
@ -1190,7 +1190,7 @@ void ml_recover(void)
|
|||||||
xfree(p);
|
xfree(p);
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
changed_int();
|
changed_int();
|
||||||
++curbuf->b_changedtick;
|
buf_set_changedtick(curbuf, curbuf->b_changedtick + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -625,18 +625,6 @@ void free_all_mem(void)
|
|||||||
/* Destroy all windows. Must come before freeing buffers. */
|
/* Destroy all windows. Must come before freeing buffers. */
|
||||||
win_free_all();
|
win_free_all();
|
||||||
|
|
||||||
/* Free all buffers. Reset 'autochdir' to avoid accessing things that
|
|
||||||
* were freed already. */
|
|
||||||
p_acd = false;
|
|
||||||
for (buf = firstbuf; buf != NULL; ) {
|
|
||||||
bufref_T bufref;
|
|
||||||
set_bufref(&bufref, buf);
|
|
||||||
nextbuf = buf->b_next;
|
|
||||||
close_buffer(NULL, buf, DOBUF_WIPE, false);
|
|
||||||
// Didn't work, try next one.
|
|
||||||
buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
free_cmdline_buf();
|
free_cmdline_buf();
|
||||||
|
|
||||||
/* Clear registers. */
|
/* Clear registers. */
|
||||||
@ -660,6 +648,20 @@ void free_all_mem(void)
|
|||||||
|
|
||||||
eval_clear();
|
eval_clear();
|
||||||
|
|
||||||
|
// Free all buffers. Reset 'autochdir' to avoid accessing things that
|
||||||
|
// were freed already.
|
||||||
|
// Must be after eval_clear to avoid it trying to access b:changedtick after
|
||||||
|
// freeing it.
|
||||||
|
p_acd = false;
|
||||||
|
for (buf = firstbuf; buf != NULL; ) {
|
||||||
|
bufref_T bufref;
|
||||||
|
set_bufref(&bufref, buf);
|
||||||
|
nextbuf = buf->b_next;
|
||||||
|
close_buffer(NULL, buf, DOBUF_WIPE, false);
|
||||||
|
// Didn't work, try next one.
|
||||||
|
buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
|
||||||
|
}
|
||||||
|
|
||||||
/* screenlines (can't display anything now!) */
|
/* screenlines (can't display anything now!) */
|
||||||
free_screenlines();
|
free_screenlines();
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os/time.h"
|
#include "nvim/os/time.h"
|
||||||
#include "nvim/event/stream.h"
|
#include "nvim/event/stream.h"
|
||||||
|
#include "nvim/buffer.h"
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "misc1.c.generated.h"
|
# include "misc1.c.generated.h"
|
||||||
@ -1788,7 +1789,7 @@ void changed(void)
|
|||||||
}
|
}
|
||||||
changed_int();
|
changed_int();
|
||||||
}
|
}
|
||||||
++curbuf->b_changedtick;
|
buf_set_changedtick(curbuf, curbuf->b_changedtick + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2147,7 +2148,7 @@ unchanged (
|
|||||||
redraw_tabline = TRUE;
|
redraw_tabline = TRUE;
|
||||||
need_maketitle = TRUE; /* set window title later */
|
need_maketitle = TRUE; /* set window title later */
|
||||||
}
|
}
|
||||||
++buf->b_changedtick;
|
buf_set_changedtick(buf, buf->b_changedtick + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -611,8 +611,8 @@ static void buf_set_term_title(buf_T *buf, char *title)
|
|||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
Error err;
|
Error err;
|
||||||
dict_set_value(buf->b_vars,
|
dict_set_var(buf->b_vars,
|
||||||
cstr_as_string("term_title"),
|
STATIC_CSTR_AS_STRING("term_title"),
|
||||||
STRING_OBJ(cstr_as_string(title)),
|
STRING_OBJ(cstr_as_string(title)),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -2,8 +2,11 @@ local helpers = require('test.functional.helpers')(after_each)
|
|||||||
local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer
|
local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer
|
||||||
local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq
|
local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq
|
||||||
local curbufmeths, ok = helpers.curbufmeths, helpers.ok
|
local curbufmeths, ok = helpers.curbufmeths, helpers.ok
|
||||||
local funcs, request = helpers.funcs, helpers.request
|
local funcs = helpers.funcs
|
||||||
|
local request = helpers.request
|
||||||
local NIL = helpers.NIL
|
local NIL = helpers.NIL
|
||||||
|
local meth_pcall = helpers.meth_pcall
|
||||||
|
local command = helpers.command
|
||||||
|
|
||||||
describe('api/buf', function()
|
describe('api/buf', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
@ -249,6 +252,24 @@ describe('api/buf', function()
|
|||||||
eq(1, funcs.exists('b:lua'))
|
eq(1, funcs.exists('b:lua'))
|
||||||
curbufmeths.del_var('lua')
|
curbufmeths.del_var('lua')
|
||||||
eq(0, funcs.exists('b:lua'))
|
eq(0, funcs.exists('b:lua'))
|
||||||
|
eq({false, 'Key "lua" doesn\'t exist'}, meth_pcall(curbufmeths.del_var, 'lua'))
|
||||||
|
curbufmeths.set_var('lua', 1)
|
||||||
|
command('lockvar b:lua')
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(curbufmeths.del_var, 'lua'))
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(curbufmeths.set_var, 'lua', 1))
|
||||||
|
eq({false, 'Key is read-only: changedtick'},
|
||||||
|
meth_pcall(curbufmeths.del_var, 'changedtick'))
|
||||||
|
eq({false, 'Key is read-only: changedtick'},
|
||||||
|
meth_pcall(curbufmeths.set_var, 'changedtick', 1))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('get_changedtick', function()
|
||||||
|
it('works', function()
|
||||||
|
eq(2, curbufmeths.get_changedtick())
|
||||||
|
curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'})
|
||||||
|
eq(3, curbufmeths.get_changedtick())
|
||||||
|
eq(3, curbufmeths.get_var('changedtick'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('buffer_set_var returns the old value', function()
|
it('buffer_set_var returns the old value', function()
|
||||||
|
@ -6,6 +6,8 @@ local curtabmeths = helpers.curtabmeths
|
|||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local request = helpers.request
|
local request = helpers.request
|
||||||
local NIL = helpers.NIL
|
local NIL = helpers.NIL
|
||||||
|
local meth_pcall = helpers.meth_pcall
|
||||||
|
local command = helpers.command
|
||||||
|
|
||||||
describe('api/tabpage', function()
|
describe('api/tabpage', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
@ -32,6 +34,11 @@ describe('api/tabpage', function()
|
|||||||
eq(1, funcs.exists('t:lua'))
|
eq(1, funcs.exists('t:lua'))
|
||||||
curtabmeths.del_var('lua')
|
curtabmeths.del_var('lua')
|
||||||
eq(0, funcs.exists('t:lua'))
|
eq(0, funcs.exists('t:lua'))
|
||||||
|
eq({false, 'Key "lua" doesn\'t exist'}, meth_pcall(curtabmeths.del_var, 'lua'))
|
||||||
|
curtabmeths.set_var('lua', 1)
|
||||||
|
command('lockvar t:lua')
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(curtabmeths.del_var, 'lua'))
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(curtabmeths.set_var, 'lua', 1))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('tabpage_set_var returns the old value', function()
|
it('tabpage_set_var returns the old value', function()
|
||||||
|
@ -7,6 +7,8 @@ local os_name = helpers.os_name
|
|||||||
local meths = helpers.meths
|
local meths = helpers.meths
|
||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local request = helpers.request
|
local request = helpers.request
|
||||||
|
local meth_pcall = helpers.meth_pcall
|
||||||
|
local command = helpers.command
|
||||||
|
|
||||||
describe('api', function()
|
describe('api', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
@ -117,6 +119,11 @@ describe('api', function()
|
|||||||
eq(1, funcs.exists('g:lua'))
|
eq(1, funcs.exists('g:lua'))
|
||||||
meths.del_var('lua')
|
meths.del_var('lua')
|
||||||
eq(0, funcs.exists('g:lua'))
|
eq(0, funcs.exists('g:lua'))
|
||||||
|
eq({false, 'Key "lua" doesn\'t exist'}, meth_pcall(meths.del_var, 'lua'))
|
||||||
|
meths.set_var('lua', 1)
|
||||||
|
command('lockvar lua')
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(meths.del_var, 'lua'))
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(meths.set_var, 'lua', 1))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('vim_set_var returns the old value', function()
|
it('vim_set_var returns the old value', function()
|
||||||
|
@ -8,6 +8,8 @@ local curwinmeths = helpers.curwinmeths
|
|||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local request = helpers.request
|
local request = helpers.request
|
||||||
local NIL = helpers.NIL
|
local NIL = helpers.NIL
|
||||||
|
local meth_pcall = helpers.meth_pcall
|
||||||
|
local command = helpers.command
|
||||||
|
|
||||||
-- check if str is visible at the beginning of some line
|
-- check if str is visible at the beginning of some line
|
||||||
local function is_visible(str)
|
local function is_visible(str)
|
||||||
@ -137,6 +139,11 @@ describe('api/win', function()
|
|||||||
eq(1, funcs.exists('w:lua'))
|
eq(1, funcs.exists('w:lua'))
|
||||||
curwinmeths.del_var('lua')
|
curwinmeths.del_var('lua')
|
||||||
eq(0, funcs.exists('w:lua'))
|
eq(0, funcs.exists('w:lua'))
|
||||||
|
eq({false, 'Key "lua" doesn\'t exist'}, meth_pcall(curwinmeths.del_var, 'lua'))
|
||||||
|
curwinmeths.set_var('lua', 1)
|
||||||
|
command('lockvar w:lua')
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(curwinmeths.del_var, 'lua'))
|
||||||
|
eq({false, 'Key is locked: lua'}, meth_pcall(curwinmeths.set_var, 'lua', 1))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('window_set_var returns the old value', function()
|
it('window_set_var returns the old value', function()
|
||||||
|
142
test/functional/eval/changedtick_spec.lua
Normal file
142
test/functional/eval/changedtick_spec.lua
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
|
||||||
|
local eq = helpers.eq
|
||||||
|
local eval = helpers.eval
|
||||||
|
local feed = helpers.feed
|
||||||
|
local clear = helpers.clear
|
||||||
|
local funcs = helpers.funcs
|
||||||
|
local meths = helpers.meths
|
||||||
|
local command = helpers.command
|
||||||
|
local exc_exec = helpers.exc_exec
|
||||||
|
local redir_exec = helpers.redir_exec
|
||||||
|
local meth_pcall = helpers.meth_pcall
|
||||||
|
local curbufmeths = helpers.curbufmeths
|
||||||
|
|
||||||
|
before_each(clear)
|
||||||
|
|
||||||
|
local function changedtick()
|
||||||
|
local ct = curbufmeths.get_changedtick()
|
||||||
|
eq(ct, curbufmeths.get_var('changedtick'))
|
||||||
|
eq(ct, curbufmeths.get_var('changedtick'))
|
||||||
|
eq(ct, eval('b:changedtick'))
|
||||||
|
eq(ct, eval('b:["changedtick"]'))
|
||||||
|
eq(ct, eval('b:.changedtick'))
|
||||||
|
eq(ct, funcs.getbufvar('%', 'changedtick'))
|
||||||
|
eq(ct, funcs.getbufvar('%', '').changedtick)
|
||||||
|
eq(ct, eval('b:').changedtick)
|
||||||
|
return ct
|
||||||
|
end
|
||||||
|
|
||||||
|
describe('b:changedtick', function()
|
||||||
|
-- Ported tests from Vim-8.0.333
|
||||||
|
it('increments', function() -- Test_changedtick_increments
|
||||||
|
-- New buffer has an empty line, tick starts at 2
|
||||||
|
eq(2, changedtick())
|
||||||
|
funcs.setline(1, 'hello')
|
||||||
|
eq(3, changedtick())
|
||||||
|
eq(0, exc_exec('undo'))
|
||||||
|
-- Somehow undo counts as two changes
|
||||||
|
eq(5, changedtick())
|
||||||
|
end)
|
||||||
|
it('is present in b: dictionary', function()
|
||||||
|
eq(2, changedtick())
|
||||||
|
command('let d = b:')
|
||||||
|
eq(2, meths.get_var('d').changedtick)
|
||||||
|
end)
|
||||||
|
it('increments at bdel', function()
|
||||||
|
command('new')
|
||||||
|
eq(2, changedtick())
|
||||||
|
local bnr = curbufmeths.get_number()
|
||||||
|
eq(2, bnr)
|
||||||
|
command('bdel')
|
||||||
|
eq(3, funcs.getbufvar(bnr, 'changedtick'))
|
||||||
|
eq(1, curbufmeths.get_number())
|
||||||
|
end)
|
||||||
|
it('fails to be changed by user', function()
|
||||||
|
local ct = changedtick()
|
||||||
|
local ctn = ct + 100500
|
||||||
|
eq(0, exc_exec('let d = b:'))
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:changedtick"',
|
||||||
|
redir_exec('let b:changedtick = ' .. ctn))
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:["changedtick"]"',
|
||||||
|
redir_exec('let b:["changedtick"] = ' .. ctn))
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:.changedtick"',
|
||||||
|
redir_exec('let b:.changedtick = ' .. ctn))
|
||||||
|
eq('\nE46: Cannot change read-only variable "d.changedtick"',
|
||||||
|
redir_exec('let d.changedtick = ' .. ctn))
|
||||||
|
eq({false, 'Key is read-only: changedtick'},
|
||||||
|
meth_pcall(curbufmeths.set_var, 'changedtick', ctn))
|
||||||
|
|
||||||
|
eq('\nE795: Cannot delete variable b:changedtick',
|
||||||
|
redir_exec('unlet b:changedtick'))
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:.changedtick"',
|
||||||
|
redir_exec('unlet b:.changedtick'))
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:["changedtick"]"',
|
||||||
|
redir_exec('unlet b:["changedtick"]'))
|
||||||
|
eq('\nE46: Cannot change read-only variable "d.changedtick"',
|
||||||
|
redir_exec('unlet d.changedtick'))
|
||||||
|
eq({false, 'Key is read-only: changedtick'},
|
||||||
|
meth_pcall(curbufmeths.del_var, 'changedtick'))
|
||||||
|
eq(ct, changedtick())
|
||||||
|
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:["changedtick"]"',
|
||||||
|
redir_exec('let b:["changedtick"] += ' .. ctn))
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:["changedtick"]"',
|
||||||
|
redir_exec('let b:["changedtick"] -= ' .. ctn))
|
||||||
|
eq('\nE46: Cannot change read-only variable "b:["changedtick"]"',
|
||||||
|
redir_exec('let b:["changedtick"] .= ' .. ctn))
|
||||||
|
|
||||||
|
eq(ct, changedtick())
|
||||||
|
|
||||||
|
funcs.setline(1, 'hello')
|
||||||
|
|
||||||
|
eq(ct + 1, changedtick())
|
||||||
|
end)
|
||||||
|
it('is listed in :let output', function()
|
||||||
|
eq('\nb:changedtick #2',
|
||||||
|
redir_exec(':let b:'))
|
||||||
|
end)
|
||||||
|
it('fails to unlock b:changedtick', function()
|
||||||
|
eq(0, exc_exec('let d = b:'))
|
||||||
|
eq(0, funcs.islocked('b:changedtick'))
|
||||||
|
eq(0, funcs.islocked('d.changedtick'))
|
||||||
|
eq('\nE940: Cannot lock or unlock variable b:changedtick',
|
||||||
|
redir_exec('unlockvar b:changedtick'))
|
||||||
|
eq('\nE46: Cannot change read-only variable "d.changedtick"',
|
||||||
|
redir_exec('unlockvar d.changedtick'))
|
||||||
|
eq(0, funcs.islocked('b:changedtick'))
|
||||||
|
eq(0, funcs.islocked('d.changedtick'))
|
||||||
|
eq('\nE940: Cannot lock or unlock variable b:changedtick',
|
||||||
|
redir_exec('lockvar b:changedtick'))
|
||||||
|
eq('\nE46: Cannot change read-only variable "d.changedtick"',
|
||||||
|
redir_exec('lockvar d.changedtick'))
|
||||||
|
eq(0, funcs.islocked('b:changedtick'))
|
||||||
|
eq(0, funcs.islocked('d.changedtick'))
|
||||||
|
end)
|
||||||
|
it('is being completed', function()
|
||||||
|
feed(':echo b:<Tab><Home>let cmdline="<End>"<CR>')
|
||||||
|
eq('echo b:changedtick', meths.get_var('cmdline'))
|
||||||
|
end)
|
||||||
|
it('cannot be changed by filter() or map()', function()
|
||||||
|
eq(2, changedtick())
|
||||||
|
eq('\nE795: Cannot delete variable filter() argument',
|
||||||
|
redir_exec('call filter(b:, 0)'))
|
||||||
|
eq('\nE742: Cannot change value of map() argument',
|
||||||
|
redir_exec('call map(b:, 0)'))
|
||||||
|
eq('\nE742: Cannot change value of map() argument',
|
||||||
|
redir_exec('call map(b:, "v:val")'))
|
||||||
|
eq(2, changedtick())
|
||||||
|
end)
|
||||||
|
it('cannot be remove()d', function()
|
||||||
|
eq(2, changedtick())
|
||||||
|
eq('\nE795: Cannot delete variable remove() argument',
|
||||||
|
redir_exec('call remove(b:, "changedtick")'))
|
||||||
|
eq(2, changedtick())
|
||||||
|
end)
|
||||||
|
it('does not inherit VAR_FIXED when copying dictionary over', function()
|
||||||
|
eq(2, changedtick())
|
||||||
|
eq('', redir_exec('let d1 = copy(b:)|let d1.changedtick = 42'))
|
||||||
|
eq('', redir_exec('let d2 = copy(b:)|unlet d2.changedtick'))
|
||||||
|
eq(2, changedtick())
|
||||||
|
end)
|
||||||
|
end)
|
@ -544,6 +544,14 @@ local function skip_fragile(pending_fn, cond)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function meth_pcall(...)
|
||||||
|
local ret = {pcall(...)}
|
||||||
|
if type(ret[2]) == 'string' then
|
||||||
|
ret[2] = ret[2]:gsub('^[^:]+:%d+: ', '')
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
local funcs = create_callindex(nvim_call)
|
local funcs = create_callindex(nvim_call)
|
||||||
local meths = create_callindex(nvim)
|
local meths = create_callindex(nvim)
|
||||||
local uimeths = create_callindex(ui)
|
local uimeths = create_callindex(ui)
|
||||||
@ -615,6 +623,7 @@ local M = {
|
|||||||
skip_fragile = skip_fragile,
|
skip_fragile = skip_fragile,
|
||||||
set_shell_powershell = set_shell_powershell,
|
set_shell_powershell = set_shell_powershell,
|
||||||
tmpname = tmpname,
|
tmpname = tmpname,
|
||||||
|
meth_pcall = meth_pcall,
|
||||||
NIL = mpack.NIL,
|
NIL = mpack.NIL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,14 @@ describe('context variables', function()
|
|||||||
-- Test for getbufvar().
|
-- Test for getbufvar().
|
||||||
-- Use strings to test for memory leaks.
|
-- Use strings to test for memory leaks.
|
||||||
source([[
|
source([[
|
||||||
|
function Getbufscope(buf, ...)
|
||||||
|
let ret = call('getbufvar', [a:buf, ''] + a:000)
|
||||||
|
if type(ret) == type({})
|
||||||
|
return filter(copy(ret), 'v:key isnot# "changedtick"')
|
||||||
|
else
|
||||||
|
return ret
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
let t:testvar='abcd'
|
let t:testvar='abcd'
|
||||||
$put =string(gettabvar(1, 'testvar'))
|
$put =string(gettabvar(1, 'testvar'))
|
||||||
$put =string(gettabvar(1, 'testvar'))
|
$put =string(gettabvar(1, 'testvar'))
|
||||||
@ -20,14 +28,14 @@ describe('context variables', function()
|
|||||||
let def_num = '5678'
|
let def_num = '5678'
|
||||||
$put =string(getbufvar(1, 'var_num'))
|
$put =string(getbufvar(1, 'var_num'))
|
||||||
$put =string(getbufvar(1, 'var_num', def_num))
|
$put =string(getbufvar(1, 'var_num', def_num))
|
||||||
$put =string(getbufvar(1, ''))
|
$put =string(Getbufscope(1))
|
||||||
$put =string(getbufvar(1, '', def_num))
|
$put =string(Getbufscope(1, def_num))
|
||||||
unlet b:var_num
|
unlet b:var_num
|
||||||
$put =string(getbufvar(1, 'var_num', def_num))
|
$put =string(getbufvar(1, 'var_num', def_num))
|
||||||
$put =string(getbufvar(1, ''))
|
$put =string(Getbufscope(1))
|
||||||
$put =string(getbufvar(1, '', def_num))
|
$put =string(Getbufscope(1, def_num))
|
||||||
$put =string(getbufvar(9, ''))
|
$put =string(Getbufscope(9))
|
||||||
$put =string(getbufvar(9, '', def_num))
|
$put =string(Getbufscope(9, def_num))
|
||||||
unlet def_num
|
unlet def_num
|
||||||
$put =string(getbufvar(1, '&autoindent'))
|
$put =string(getbufvar(1, '&autoindent'))
|
||||||
$put =string(getbufvar(1, '&autoindent', 1))
|
$put =string(getbufvar(1, '&autoindent', 1))
|
||||||
|
@ -79,6 +79,13 @@ local function cimport(...)
|
|||||||
|
|
||||||
-- format it (so that the lines are "unique" statements), also filter out
|
-- format it (so that the lines are "unique" statements), also filter out
|
||||||
-- Objective-C blocks
|
-- Objective-C blocks
|
||||||
|
if os.getenv('NVIM_TEST_PRINT_I') == '1' then
|
||||||
|
local lnum = 0
|
||||||
|
for line in body:gmatch('[^\n]+') do
|
||||||
|
lnum = lnum + 1
|
||||||
|
print(lnum, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
body = formatc(body)
|
body = formatc(body)
|
||||||
body = filter_complex_blocks(body)
|
body = filter_complex_blocks(body)
|
||||||
|
|
||||||
|
@ -124,6 +124,7 @@ function Gcc:init_defines()
|
|||||||
self:define('_GNU_SOURCE')
|
self:define('_GNU_SOURCE')
|
||||||
self:define('INCLUDE_GENERATED_DECLARATIONS')
|
self:define('INCLUDE_GENERATED_DECLARATIONS')
|
||||||
self:define('UNIT_TESTING')
|
self:define('UNIT_TESTING')
|
||||||
|
self:define('UNIT_TESTING_LUA_PREPROCESSING')
|
||||||
-- Needed for FreeBSD
|
-- Needed for FreeBSD
|
||||||
self:define('_Thread_local', nil, '')
|
self:define('_Thread_local', nil, '')
|
||||||
-- Needed for macOS Sierra
|
-- Needed for macOS Sierra
|
||||||
|
Loading…
Reference in New Issue
Block a user