aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim')
-rw-r--r--src/nvim/api/options.c3
-rw-r--r--src/nvim/api/ui.c6
-rw-r--r--src/nvim/buffer.c8
-rw-r--r--src/nvim/edit.c1
-rw-r--r--src/nvim/ex_cmds.c44
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/ex_session.c5
-rw-r--r--src/nvim/lua/executor.c4
-rw-r--r--src/nvim/mark.c26
-rw-r--r--src/nvim/mark.h4
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c343
-rw-r--r--src/nvim/option_defs.h5
-rw-r--r--src/nvim/optionstr.c109
-rw-r--r--src/nvim/regexp.c5
15 files changed, 309 insertions, 258 deletions
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index e33cb72e8d..eb80683365 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -465,9 +465,6 @@ OptVal get_option_value_for(const char *const name, uint32_t *flagsp, int scope,
/// @param[in] name Option name.
/// @param[in] value Option value.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// If OPT_CLEAR is set, the value of the option
-/// is cleared (the exact semantics of this depend
-/// on the option).
/// @param[in] opt_type Option type. See SREQ_* in option_defs.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 9fa5a89407..891c81d470 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -830,6 +830,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
size_t ncells = (size_t)(endcol - startcol);
int last_hl = -1;
uint32_t nelem = 0;
+ bool was_space = false;
for (size_t i = 0; i < ncells; i++) {
repeat++;
if (i == ncells - 1 || attrs[i] != attrs[i + 1]
@@ -868,9 +869,12 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
data->ncells_pending += MIN(repeat, 2);
last_hl = attrs[i];
repeat = 0;
+ was_space = strequal(chunk[i], " ");
}
}
- if (endcol < clearcol) {
+ // If the last chunk was all spaces, add a clearing chunk even if there are
+ // no more cells to clear, so there is no ambiguity about what to clear.
+ if (endcol < clearcol || was_space) {
nelem++;
data->ncells_pending += 1;
mpack_array(buf, 3);
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 9c061269f1..8eacec4d5e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -858,9 +858,9 @@ static void free_buffer(buf_T *buf)
xfree(buf->b_prompt_text);
callback_free(&buf->b_prompt_callback);
callback_free(&buf->b_prompt_interrupt);
- clear_fmark(&buf->b_last_cursor);
- clear_fmark(&buf->b_last_insert);
- clear_fmark(&buf->b_last_change);
+ clear_fmark(&buf->b_last_cursor, 0);
+ clear_fmark(&buf->b_last_insert, 0);
+ clear_fmark(&buf->b_last_change, 0);
for (size_t i = 0; i < NMARKS; i++) {
free_fmark(buf->b_namedm[i]);
}
@@ -1910,7 +1910,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
buf->b_flags |= BF_DUMMY;
}
buf_clear_file(buf);
- clrallmarks(buf); // clear marks
+ clrallmarks(buf, 0); // clear marks
fmarks_check_names(buf); // check file marks for this file
buf->b_p_bl = (flags & BLN_LISTED) ? true : false; // init 'buflisted'
kv_destroy(buf->update_channels);
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 88ed1e6cb4..06eb81be92 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -3579,6 +3579,7 @@ static void ins_insert(int replaceState)
// Pressed CTRL-O in Insert mode.
static void ins_ctrl_o(void)
{
+ restart_VIsual_select = 0;
if (State & VREPLACE_FLAG) {
restart_edit = 'V';
} else if (State & REPLACE_FLAG) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 494ebce370..a0618ce7d7 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2230,8 +2230,16 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// End Visual mode before switching to another buffer, so the text can be
// copied into the GUI selection buffer.
+ // Careful: may trigger ModeChanged() autocommand
+
+ // Should we block autocommands here?
reset_VIsual();
+ // autocommands freed window :(
+ if (oldwin != NULL && !win_valid(oldwin)) {
+ oldwin = NULL;
+ }
+
if ((command != NULL || newlnum > (linenr_T)0)
&& *get_vim_var_str(VV_SWAPCOMMAND) == NUL) {
// Set v:swapcommand for the SwapExists autocommands.
@@ -3169,21 +3177,21 @@ static bool sub_joining_lines(exarg_T *eap, char *pat, const char *sub, const ch
/// Slightly more memory that is strictly necessary is allocated to reduce the
/// frequency of memory (re)allocation.
///
-/// @param[in,out] new_start pointer to the memory for the replacement text
-/// @param[in] needed_len amount of memory needed
+/// @param[in,out] new_start pointer to the memory for the replacement text
+/// @param[in,out] new_start_len pointer to length of new_start
+/// @param[in] needed_len amount of memory needed
///
/// @returns pointer to the end of the allocated memory
-static char *sub_grow_buf(char **new_start, int needed_len)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_RET
+static char *sub_grow_buf(char **new_start, int *new_start_len, int needed_len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
- int new_start_len = 0;
char *new_end;
if (*new_start == NULL) {
// Get some space for a temporary buffer to do the
// substitution into (and some extra space to avoid
// too many calls to xmalloc()/free()).
- new_start_len = needed_len + 50;
- *new_start = xmalloc((size_t)new_start_len);
+ *new_start_len = needed_len + 50;
+ *new_start = xmalloc((size_t)(*new_start_len));
**new_start = NUL;
new_end = *new_start;
} else {
@@ -3192,9 +3200,9 @@ static char *sub_grow_buf(char **new_start, int needed_len)
// extra to avoid too many calls to xmalloc()/free()).
size_t len = strlen(*new_start);
needed_len += (int)len;
- if (needed_len > new_start_len) {
- new_start_len = needed_len + 50;
- *new_start = xrealloc(*new_start, (size_t)new_start_len);
+ if (needed_len > *new_start_len) {
+ *new_start_len = needed_len + 50;
+ *new_start = xrealloc(*new_start, (size_t)(*new_start_len));
}
new_end = *new_start + len;
}
@@ -3519,6 +3527,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
colnr_T matchcol;
colnr_T prev_matchcol = MAXCOL;
char *new_end, *new_start = NULL;
+ int new_start_len = 0;
char *p1;
bool did_sub = false;
int lastone;
@@ -3564,7 +3573,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
// accordingly.
//
// The new text is built up in new_start[]. It has some extra
- // room to avoid using xmalloc()/free() too often.
+ // room to avoid using xmalloc()/free() too often. new_start_len is
+ // the length of the allocated memory at new_start.
//
// Make a copy of the old line, so it won't be taken away when
// updating the screen or handling a multi-line match. The "old_"
@@ -3943,15 +3953,19 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
p1 = ml_get(sub_firstlnum + (linenr_T)nmatch - 1);
nmatch_tl += nmatch - 1;
}
- size_t copy_len = (size_t)(regmatch.startpos[0].col - copycol);
- new_end = sub_grow_buf(&new_start,
+ int copy_len = regmatch.startpos[0].col - copycol;
+ new_end = sub_grow_buf(&new_start, &new_start_len,
(colnr_T)strlen(p1) - regmatch.endpos[0].col
- + (colnr_T)copy_len + sublen + 1);
+ + copy_len + sublen + 1);
// copy the text up to the part that matched
- memmove(new_end, sub_firstline + copycol, copy_len);
+ memmove(new_end, sub_firstline + copycol, (size_t)copy_len);
new_end += copy_len;
+ if (new_start_len - copy_len < sublen) {
+ sublen = new_start_len - copy_len - 1;
+ }
+
// Finally, at this point we can know where the match actually will
// start in the new text
int start_col = (int)(new_end - new_start);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 3ff1442640..44610c81d8 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3136,7 +3136,7 @@ int cmd_exists(const char *const name)
/// "fullcommand" function
void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char *name = argvars[0].vval.v_string;
+ char *name = (char *)tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index d47d40cbee..e2a4f73840 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -1080,7 +1080,7 @@ void ex_mkrc(exarg_T *eap)
}
/// @return the name of the view file for the current buffer.
-static char *get_view_file(int c)
+static char *get_view_file(char c)
{
if (curbuf->b_ffname == NULL) {
emsg(_(e_noname));
@@ -1119,8 +1119,7 @@ static char *get_view_file(int c)
}
}
*s++ = '=';
- assert(c >= CHAR_MIN && c <= CHAR_MAX);
- *s++ = (char)c;
+ *s++ = c;
xstrlcpy(s, ".vim", 5);
xfree(sname);
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 4aaf4397f9..f2efd866f8 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -140,8 +140,8 @@ void nlua_error(lua_State *const lstate, const char *const msg)
}
if (in_script) {
- os_errmsg(str);
- os_errmsg("\n");
+ fprintf(stderr, msg, (int)len, str);
+ fprintf(stderr, "\n");
} else {
msg_ext_set_kind("lua_error");
semsg_multiline(msg, (int)len, str);
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 6480a47344..0dc1be526b 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -77,11 +77,12 @@ void free_xfmark(xfmark_T fm)
}
/// Free and clear fmark_T item
-void clear_fmark(fmark_T *fm)
+void clear_fmark(fmark_T *const fm, const Timestamp timestamp)
FUNC_ATTR_NONNULL_ALL
{
free_fmark(*fm);
*fm = (fmark_T)INIT_FMARK;
+ fm->timestamp = timestamp;
}
// Set named mark "c" to position "pos".
@@ -763,20 +764,20 @@ bool mark_check_line_bounds(buf_T *buf, fmark_T *fm, const char **errormsg)
/// Used mainly when trashing the entire buffer during ":e" type commands.
///
/// @param[out] buf Buffer to clear marks in.
-void clrallmarks(buf_T *const buf)
+void clrallmarks(buf_T *const buf, const Timestamp timestamp)
FUNC_ATTR_NONNULL_ALL
{
for (size_t i = 0; i < NMARKS; i++) {
- clear_fmark(&buf->b_namedm[i]);
+ clear_fmark(&buf->b_namedm[i], timestamp);
}
- clear_fmark(&buf->b_last_cursor);
+ clear_fmark(&buf->b_last_cursor, timestamp);
buf->b_last_cursor.mark.lnum = 1;
- clear_fmark(&buf->b_last_insert);
- clear_fmark(&buf->b_last_change);
+ clear_fmark(&buf->b_last_insert, timestamp);
+ clear_fmark(&buf->b_last_change, timestamp);
buf->b_op_start.lnum = 0; // start/end op mark cleared
buf->b_op_end.lnum = 0;
for (int i = 0; i < buf->b_changelistlen; i++) {
- clear_fmark(&buf->b_changelist[i]);
+ clear_fmark(&buf->b_changelist[i], timestamp);
}
buf->b_changelistlen = 0;
}
@@ -925,7 +926,7 @@ void ex_delmarks(exarg_T *eap)
if (*eap->arg == NUL && eap->forceit) {
// clear all marks
- clrallmarks(curbuf);
+ clrallmarks(curbuf, os_time());
} else if (eap->forceit) {
emsg(_(e_invarg));
} else if (*eap->arg == NUL) {
@@ -973,16 +974,13 @@ void ex_delmarks(exarg_T *eap)
} else {
switch (*p) {
case '"':
- curbuf->b_last_cursor.timestamp = timestamp;
- CLEAR_FMARK(&curbuf->b_last_cursor);
+ clear_fmark(&curbuf->b_last_cursor, timestamp);
break;
case '^':
- curbuf->b_last_insert.timestamp = timestamp;
- CLEAR_FMARK(&curbuf->b_last_insert);
+ clear_fmark(&curbuf->b_last_insert, timestamp);
break;
case '.':
- curbuf->b_last_change.timestamp = timestamp;
- CLEAR_FMARK(&curbuf->b_last_change);
+ clear_fmark(&curbuf->b_last_change, timestamp);
break;
case '[':
curbuf->b_op_start.lnum = 0; break;
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index 8c72579d0f..d84f6a8bb0 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -34,10 +34,6 @@
SET_FMARK(fmarkp___, mark_, fnum_, view_); \
} while (0)
-/// Clear given fmark
-#define CLEAR_FMARK(fmarkp_) \
- RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T)INIT_FMARKV))
-
/// Set given extended mark (regular mark + file name)
#define SET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
do { \
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 8270641256..96deae228f 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -4770,7 +4770,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
}
- while (bits > 0) {
+ while (bits > 0 && i < NUMBUFLEN - 1) {
buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0';
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 716fd3775a..cc3a9c181d 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1098,9 +1098,10 @@ static char *stropt_get_newval(int nextchar, int opt_idx, char **argp, void *var
/// Part of do_set() for string options.
static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int nextchar,
set_op_T op_arg, uint32_t flags, void *varp_arg, char *errbuf,
- size_t errbuflen, int *value_checked, const char **errmsg)
+ size_t errbuflen, bool *value_checked, const char **errmsg)
{
- char *arg = *argp;
+ vimoption_T *opt = get_option(opt_idx);
+
set_op_T op = op_arg;
void *varp = varp_arg;
char *origval_l = NULL;
@@ -1110,20 +1111,20 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
// with a local value the local value will be
// reset, use the global value here.
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- && ((int)options[opt_idx].indir & PV_BOTH)) {
- varp = options[opt_idx].var;
+ && ((int)opt->indir & PV_BOTH)) {
+ varp = opt->var;
}
// The old value is kept until we are sure that the new value is valid.
char *oldval = *(char **)varp;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- origval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
- origval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+ origval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
+ origval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
// A global-local string option might have an empty option as value to
// indicate that the global value should be used.
- if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) {
+ if (((int)opt->indir & PV_BOTH) && origval_l == empty_option) {
origval_l = origval_g;
}
}
@@ -1131,61 +1132,57 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
char *origval;
// When setting the local value of a global option, the old value may be
// the global value.
- if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
- origval = *(char **)get_varp(&options[opt_idx]);
+ if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
+ origval = *(char **)get_varp(opt);
} else {
origval = oldval;
}
// Get the new value for the option
- char *newval = stropt_get_newval(nextchar, opt_idx, &arg, varp, origval, &op, flags);
+ char *newval = stropt_get_newval(nextchar, opt_idx, argp, varp, origval, &op, flags);
// Set the new value.
- *(char **)(varp) = newval;
- if (newval == NULL) {
- *(char **)(varp) = empty_option;
- }
+ *(char **)(varp) = newval != NULL ? newval : empty_option;
// origval may be freed by did_set_string_option(), make a copy.
- char *saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
- char *saved_origval_l = (origval_l != NULL) ? xstrdup(origval_l) : NULL;
- char *saved_origval_g = (origval_g != NULL) ? xstrdup(origval_g) : NULL;
+ char *const saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
+ char *const saved_origval_l = (origval_l != NULL) ? xstrdup(origval_l) : NULL;
+ char *const saved_origval_g = (origval_g != NULL) ? xstrdup(origval_g) : NULL;
// newval (and varp) may become invalid if the buffer is closed by
// autocommands.
- char *saved_newval = (newval != NULL) ? xstrdup(newval) : NULL;
+ char *const saved_newval = (newval != NULL) ? xstrdup(newval) : NULL;
- {
- uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
- const int secure_saved = secure;
+ uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
+ const int secure_saved = secure;
- // When an option is set in the sandbox, from a modeline or in secure
- // mode, then deal with side effects in secure mode. Also when the
- // value was set with the P_INSECURE flag and is not completely
- // replaced.
- if ((opt_flags & OPT_MODELINE)
- || sandbox != 0
- || (op != OP_NONE && (*p & P_INSECURE))) {
- secure = 1;
- }
+ // When an option is set in the sandbox, from a modeline or in secure
+ // mode, then deal with side effects in secure mode. Also when the
+ // value was set with the P_INSECURE flag and is not completely
+ // replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0
+ || (op != OP_NONE && (*p & P_INSECURE))) {
+ secure = 1;
+ }
- // Handle side effects, and set the global value for ":set" on local
- // options. Note: when setting 'syntax' or 'filetype' autocommands may
- // be triggered that can cause havoc.
- *errmsg = did_set_string_option(opt_idx, (char **)varp, oldval, newval,
- errbuf, errbuflen,
- opt_flags, value_checked);
+ // Handle side effects, and set the global value for ":set" on local
+ // options. Note: when setting 'syntax' or 'filetype' autocommands may
+ // be triggered that can cause havoc.
+ *errmsg = did_set_string_option(curbuf, curwin, opt_idx, (char **)varp, oldval,
+ errbuf, errbuflen,
+ opt_flags, value_checked);
- secure = secure_saved;
- }
+ secure = secure_saved;
+ // call autocommand after handling side effects
if (*errmsg == NULL) {
if (!starting) {
trigger_optionset_string(opt_idx, opt_flags, saved_origval, saved_origval_l,
saved_origval_g, saved_newval);
}
if (options[opt_idx].flags & P_UI_OPTION) {
- ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ ui_call_option_set(cstr_as_string(opt->fullname),
CSTR_AS_OBJ(saved_newval));
}
}
@@ -1193,8 +1190,6 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
xfree(saved_origval_l);
xfree(saved_origval_g);
xfree(saved_newval);
-
- *argp = arg;
}
static set_op_T get_op(const char *arg)
@@ -1337,7 +1332,7 @@ static void do_set_option_value(int opt_idx, int opt_flags, char **argp, int pre
set_op_T op, uint32_t flags, void *varp, char *errbuf,
size_t errbuflen, const char **errmsg)
{
- int value_checked = false;
+ bool value_checked = false;
if (flags & P_BOOL) { // boolean
do_set_bool(opt_idx, opt_flags, prefix, nextchar, varp, errmsg);
} else if (flags & P_NUM) { // numeric
@@ -1598,7 +1593,7 @@ int do_set(char *arg, int opt_flags)
/// @param opt_flags possibly with OPT_MODELINE
/// @param new_value value was replaced completely
/// @param value_checked value was checked to be safe, no need to set P_INSECURE
-void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked)
+void did_set_option(int opt_idx, int opt_flags, bool new_value, bool value_checked)
{
options[opt_idx].flags |= P_WAS_SET;
@@ -2981,181 +2976,189 @@ static const char *check_num_option_bounds(long *pp, long old_value, long old_Ro
return errmsg;
}
-/// Set the value of a number option, taking care of side effects
-///
-/// @param[in] opt_idx Option index in options[] table.
-/// @param[out] varp Pointer to the option variable.
-/// @param[in] value New value.
-/// @param errbuf Buffer for error messages.
-/// @param[in] errbuflen Length of `errbuf`.
-/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE.
-///
-/// @return NULL on success, error message on error.
-static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf,
- size_t errbuflen, int opt_flags)
+/// Options that need some validation.
+static const char *validate_num_option(const long *pp, long *valuep)
{
- const char *errmsg = NULL;
- long old_value = *(long *)varp;
- long old_global_value = 0; // only used when setting a local and global option
- long old_Rows = Rows; // remember old Rows
- long *pp = (long *)varp;
-
- // Disallow changing some options from secure mode.
- if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) {
- return e_secure;
- }
-
- // Save the global value before changing anything. This is needed as for
- // a global-only option setting the "local value" in fact sets the global
- // value (since there is only one value).
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- }
+ long value = *valuep;
// Many number options assume their value is in the signed int range.
if (value < INT_MIN || value > INT_MAX) {
return e_invarg;
}
- // Options that need some validation.
if (pp == &p_wh) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (p_wmh > value) {
- errmsg = e_winheight;
+ return e_winheight;
}
} else if (pp == &p_hh) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_wmh) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > p_wh) {
- errmsg = e_winheight;
+ return e_winheight;
}
} else if (pp == &p_wiw) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (p_wmw > value) {
- errmsg = e_winwidth;
+ return e_winwidth;
}
} else if (pp == &p_wmw) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > p_wiw) {
- errmsg = e_winwidth;
+ return e_winwidth;
}
} else if (pp == &p_mco) {
- value = MAX_MCO;
+ *valuep = MAX_MCO;
} else if (pp == &p_titlelen) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_uc) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_ch) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else {
p_ch_was_zero = value == 0;
}
} else if (pp == &p_tm) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_hi) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > 10000) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &p_pyx) {
if (value == 0) {
- value = 3;
+ *valuep = 3;
} else if (value != 3) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &p_re) {
if (value < 0 || value > 2) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &p_report) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_so) {
if (value < 0 && full_screen) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_siso) {
if (value < 0 && full_screen) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_cwh) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_ut) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_ss) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &curwin->w_p_fdl || pp == &curwin->w_allbuf_opt.wo_fdl) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &curwin->w_p_cole || pp == &curwin->w_allbuf_opt.wo_cole) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > 3) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > MAX_NUMBERWIDTH) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) {
if (value < 0 || value > B_IMODE_LAST) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_imsearch || pp == &p_imsearch) {
if (value < -1 || value > B_IMODE_LAST) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_channel || pp == &p_channel) {
- errmsg = e_invarg;
+ return e_invarg;
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
if (value < -1 || value > SB_MAX) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_sw || pp == &p_sw) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &curbuf->b_p_ts || pp == &p_ts) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > TABSTOP_MAX) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_tw || pp == &p_tw) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_wd) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
}
+ return NULL;
+}
+
+/// Set the value of a number option, taking care of side effects
+///
+/// @param[in] opt_idx Option index in options[] table.
+/// @param[out] varp Pointer to the option variable.
+/// @param[in] value New value.
+/// @param errbuf Buffer for error messages.
+/// @param[in] errbuflen Length of `errbuf`.
+/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE.
+///
+/// @return NULL on success, error message on error.
+static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf,
+ size_t errbuflen, int opt_flags)
+{
+ long old_value = *(long *)varp;
+ long old_global_value = 0; // only used when setting a local and global option
+ long old_Rows = Rows; // remember old Rows
+ long *pp = (long *)varp;
+
+ // Disallow changing some options from secure mode.
+ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) {
+ return e_secure;
+ }
+
+ // Save the global value before changing anything. This is needed as for
+ // a global-only option setting the "local value" in fact sets the global
+ // value (since there is only one value).
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+ }
+
+ const char *errmsg = validate_num_option(pp, &value);
+
// Don't change the value and return early if validation failed.
if (errmsg != NULL) {
return errmsg;
@@ -3714,14 +3717,73 @@ vimoption_T *get_option(int opt_idx)
return &options[opt_idx];
}
+/// Clear an option
+///
+/// The exact semantics of this depend on the option.
+static OptVal clear_optval(const char *name, uint32_t flags, void *varp, buf_T *buf, win_T *win)
+{
+ OptVal v = NIL_OPTVAL;
+
+ // Change the type of the OptVal to the type used by the option so that it can be cleared.
+ // TODO(famiu): Clean up all of this after set_(num|bool|string)_option() is unified.
+
+ if (flags & P_BOOL) {
+ v.type = kOptValTypeBoolean;
+ if ((int *)varp == &buf->b_p_ar) {
+ // TODO(lewis6991): replace this with a more general condition that
+ // indicates we are setting the local value of a global-local option
+ v.data.boolean = kNone;
+ } else {
+ v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
+ }
+ } else if (flags & P_NUM) {
+ v.type = kOptValTypeNumber;
+ if ((long *)varp == &curbuf->b_p_ul) {
+ // The one true special case
+ v.data.number = NO_LOCAL_UNDOLEVEL;
+ } else if ((long *)varp == &win->w_p_so || (long *)varp == &win->w_p_siso) {
+ // TODO(lewis6991): replace this with a more general condition that
+ // indicates we are setting the local value of a global-local option
+ v.data.number = -1;
+ } else {
+ v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
+ }
+ } else if (flags & P_STRING) {
+ v.type = kOptValTypeString;
+ v.data.string.data = NULL;
+ }
+
+ return v;
+}
+
+static const char *set_option(int opt_idx, void *varp, OptVal *v, int opt_flags, char *errbuf,
+ size_t errbuflen)
+{
+ const char *errmsg = NULL;
+
+ bool value_checked = false;
+
+ if (v->type == kOptValTypeBoolean) {
+ errmsg = set_bool_option(opt_idx, varp, (int)v->data.boolean, opt_flags);
+ } else if (v->type == kOptValTypeNumber) {
+ errmsg = set_num_option(opt_idx, varp, (long)v->data.number, errbuf, errbuflen, opt_flags);
+ } else if (v->type == kOptValTypeString) {
+ errmsg = set_string_option(opt_idx, varp, v->data.string.data, opt_flags, &value_checked,
+ errbuf, errbuflen);
+ }
+
+ if (errmsg != NULL) {
+ did_set_option(opt_idx, opt_flags, true, value_checked);
+ }
+
+ return errmsg;
+}
+
/// Set the value of an option
///
/// @param[in] name Option name.
/// @param[in] value Option value. If NIL_OPTVAL, the option value is cleared.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// If OPT_CLEAR is set, the value of the option
-/// is cleared (the exact semantics of this depend
-/// on the option).
///
/// @return NULL on success, an untranslated error message on error.
const char *set_option_value(const char *const name, const OptVal value, int opt_flags)
@@ -3763,17 +3825,7 @@ const char *set_option_value(const char *const name, const OptVal value, int opt
OptVal v = optval_copy(value);
if (v.type == kOptValTypeNil) {
- opt_flags |= OPT_CLEAR;
-
- // Change the type of the OptVal to the type used by the option so that it can be cleared.
- // TODO(famiu): Clean up all of this after set_(num|bool|string)_option() is unified.
- if (flags & P_BOOL) {
- v.type = kOptValTypeBoolean;
- } else if (flags & P_NUM) {
- v.type = kOptValTypeNumber;
- } else if (flags & P_STRING) {
- v.type = kOptValTypeString;
- }
+ v = clear_optval(name, flags, varp, curbuf, curwin);
} else if (!optval_match_type(v, opt_idx)) {
char *rep = optval_to_cstr(v);
char *valid_types = option_get_valid_types(opt_idx);
@@ -3785,42 +3837,7 @@ const char *set_option_value(const char *const name, const OptVal value, int opt
goto end;
}
- switch (v.type) {
- case kOptValTypeNil:
- abort(); // This will never happen.
- case kOptValTypeBoolean: {
- if (opt_flags & OPT_CLEAR) {
- if ((int *)varp == &curbuf->b_p_ar) {
- v.data.boolean = kNone;
- } else {
- v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
- }
- }
- errmsg = set_bool_option(opt_idx, varp, (int)v.data.boolean, opt_flags);
- break;
- }
- case kOptValTypeNumber: {
- if (opt_flags & OPT_CLEAR) {
- if ((long *)varp == &curbuf->b_p_ul) {
- v.data.number = NO_LOCAL_UNDOLEVEL;
- } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
- v.data.number = -1;
- } else {
- v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
- }
- }
- errmsg = set_num_option(opt_idx, varp, (long)v.data.number, errbuf, sizeof(errbuf), opt_flags);
- break;
- }
- case kOptValTypeString: {
- const char *s = v.data.string.data;
- if (s == NULL || opt_flags & OPT_CLEAR) {
- s = "";
- }
- errmsg = set_string_option(opt_idx, s, opt_flags, errbuf, sizeof(errbuf));
- break;
- }
- }
+ errmsg = set_option(opt_idx, varp, &v, opt_flags, errbuf, sizeof(errbuf));
end:
optval_free(v); // Free the copied OptVal.
@@ -4477,7 +4494,7 @@ void *get_option_varp_scope_from(int opt_idx, int scope, buf_T *buf, win_T *win)
return get_varp_scope_from(&(options[opt_idx]), scope, buf, win);
}
-static void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
+void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
{
// hidden option, always return NULL
if (p->var == NULL) {
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 35687a19b7..1007925ccb 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -70,7 +70,6 @@ typedef enum {
OPT_ONECOLUMN = 0x40, ///< list options one per line
OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option
OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions'
- OPT_CLEAR = 0x200, ///< Clear local value of an option.
} OptionFlags;
// Return value from get_option_value_strict
@@ -1009,9 +1008,9 @@ typedef struct {
// Option value was checked to be safe, no need to set P_INSECURE
// Used for the 'keymap', 'filetype' and 'syntax' options.
- int os_value_checked;
+ bool os_value_checked;
// Option value changed. Used for the 'filetype' and 'syntax' options.
- int os_value_changed;
+ bool os_value_changed;
// Used by the 'isident', 'iskeyword', 'isprint' and 'isfname' options.
// Set to true if the character table is modified when processing the
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 07e2e5eed1..17afd10ee3 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -419,7 +419,9 @@ void set_string_option_direct_in_buf(buf_T *buf, const char *name, int opt_idx,
curbuf = save_curbuf;
unblock_autocmds();
}
+
/// Set a string option to a new value, handling the effects
+/// Must not be called with a hidden option!
///
/// @param[in] opt_idx Option to set.
/// @param[in] value New value.
@@ -427,56 +429,86 @@ void set_string_option_direct_in_buf(buf_T *buf, const char *name, int opt_idx,
/// #OPT_GLOBAL.
///
/// @return NULL on success, an untranslated error message on error.
-const char *set_string_option(const int opt_idx, const char *const value, const int opt_flags,
- char *const errbuf, const size_t errbuflen)
- FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_WARN_UNUSED_RESULT
+const char *set_string_option(const int opt_idx, void *varp_arg, const char *value,
+ const int opt_flags, bool *value_checked, char *const errbuf,
+ const size_t errbuflen)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
vimoption_T *opt = get_option(opt_idx);
- if (opt->var == NULL) { // don't set hidden option
- return NULL;
+ void *varp = (char **)varp_arg;
+ char *origval_l = NULL;
+ char *origval_g = NULL;
+
+ // When using ":set opt=val" for a global option
+ // with a local value the local value will be
+ // reset, use the global value here.
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
+ && ((int)opt->indir & PV_BOTH)) {
+ varp = opt->var;
}
- char *const s = xstrdup(value);
- char **const varp
- = (char **)get_varp_scope(opt, ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- ? ((opt->indir & PV_BOTH) ? OPT_GLOBAL : OPT_LOCAL)
- : opt_flags));
- char *const oldval = *varp;
- char *oldval_l = NULL;
- char *oldval_g = NULL;
+ // The old value is kept until we are sure that the new value is valid.
+ char *oldval = *(char **)varp;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- oldval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
- oldval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
+ origval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
+ origval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
+
+ // A global-local string option might have an empty option as value to
+ // indicate that the global value should be used.
+ if (((int)opt->indir & PV_BOTH) && origval_l == empty_option) {
+ origval_l = origval_g;
+ }
}
- *varp = s;
+ char *origval;
+ // When setting the local value of a global option, the old value may be
+ // the global value.
+ if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
+ origval = *(char **)get_varp_from(opt, curbuf, curwin);
+ } else {
+ origval = oldval;
+ }
- char *const saved_oldval = xstrdup(oldval);
- char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup(oldval_l) : 0;
- char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup(oldval_g) : 0;
- char *const saved_newval = xstrdup(s);
+ *(char **)varp = xstrdup(value != NULL ? value : empty_option);
- int value_checked = false;
- const char *const errmsg = did_set_string_option(opt_idx, varp, oldval, s, errbuf, errbuflen,
- opt_flags, &value_checked);
- if (errmsg == NULL) {
- did_set_option(opt_idx, opt_flags, true, value_checked);
+ char *const saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
+ char *const saved_oldval_l = (origval_l != NULL) ? xstrdup(origval_l) : 0;
+ char *const saved_oldval_g = (origval_g != NULL) ? xstrdup(origval_g) : 0;
+
+ // newval (and varp) may become invalid if the buffer is closed by
+ // autocommands.
+ char *const saved_newval = xstrdup(*(char **)varp);
+
+ const int secure_saved = secure;
+
+ // When an option is set in the sandbox, from a modeline or in secure
+ // mode, then deal with side effects in secure mode. Also when the
+ // value was set with the P_INSECURE flag and is not completely
+ // replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0) {
+ secure = 1;
}
+ const char *const errmsg = did_set_string_option(curbuf, curwin, opt_idx, varp, oldval, errbuf,
+ errbuflen, opt_flags, value_checked);
+
+ secure = secure_saved;
+
// call autocommand after handling side effects
if (errmsg == NULL) {
if (!starting) {
- trigger_optionset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g,
- saved_newval);
+ trigger_optionset_string(opt_idx, opt_flags, saved_origval, saved_oldval_l,
+ saved_oldval_g, saved_newval);
}
if (opt->flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(opt->fullname),
- STRING_OBJ(cstr_as_string(saved_newval)));
+ CSTR_AS_OBJ(saved_newval));
}
}
- xfree(saved_oldval);
+ xfree(saved_origval);
xfree(saved_oldval_l);
xfree(saved_oldval_g);
xfree(saved_newval);
@@ -2066,9 +2098,9 @@ static void do_spelllang_source(win_T *win)
/// @param value_checked value was checked to be safe, no need to set P_INSECURE
///
/// @return NULL for success, or an untranslated error message for an error
-static const char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char **varp,
- char *oldval, const char *value, char *errbuf,
- size_t errbuflen, int opt_flags, int *value_checked)
+const char *did_set_string_option(buf_T *buf, win_T *win, int opt_idx, char **varp, char *oldval,
+ char *errbuf, size_t errbuflen, int opt_flags,
+ bool *value_checked)
{
const char *errmsg = NULL;
int restore_chartab = false;
@@ -2082,14 +2114,14 @@ static const char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx
.os_idx = opt_idx,
.os_flags = opt_flags,
.os_oldval.string = oldval,
- .os_newval.string = value,
+ .os_newval.string = *varp,
.os_value_checked = false,
.os_value_changed = false,
.os_restore_chartab = false,
.os_errbuf = errbuf,
.os_errbuflen = errbuflen,
- .os_win = curwin,
- .os_buf = curbuf,
+ .os_win = win,
+ .os_buf = buf,
};
// Disallow changing some options from secure mode
@@ -2186,13 +2218,6 @@ static const char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx
return errmsg;
}
-const char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *value, char *errbuf,
- size_t errbuflen, int opt_flags, int *value_checked)
-{
- return did_set_string_option_for(curbuf, curwin, opt_idx, varp, oldval, value, errbuf,
- errbuflen, opt_flags, value_checked);
-}
-
/// Check an option that can be a range of string values.
///
/// @param list when true: accept a list of values
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index d5d0f3346f..1d0a987780 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1765,9 +1765,10 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
// "flags & REGSUB_COPY" == 0 to the call with
// "flags & REGSUB_COPY" != 0.
if (copy) {
- if (eval_result[nested] != NULL) {
+ size_t reslen = eval_result[nested] != NULL ? strlen(eval_result[nested]) : 0;
+ if (eval_result[nested] != NULL && reslen < (size_t)destlen) {
STRCPY(dest, eval_result[nested]);
- dst += strlen(eval_result[nested]);
+ dst += reslen;
XFREE_CLEAR(eval_result[nested]);
}
} else {