aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mpack/object.h2
-rw-r--r--src/nvim/api/buffer.c4
-rw-r--r--src/nvim/api/extmark.c2
-rw-r--r--src/nvim/api/keysets.lua3
-rw-r--r--src/nvim/api/vim.c113
-rw-r--r--src/nvim/assert.h2
-rw-r--r--src/nvim/aucmd.c6
-rw-r--r--src/nvim/auevents.lua1
-rw-r--r--src/nvim/autocmd.c20
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/change.c2
-rw-r--r--src/nvim/channel.c6
-rw-r--r--src/nvim/charset.h4
-rw-r--r--src/nvim/edit.c60
-rw-r--r--src/nvim/eval.c254
-rw-r--r--src/nvim/eval.h7
-rw-r--r--src/nvim/eval/decode.c10
-rw-r--r--src/nvim/eval/encode.c36
-rw-r--r--src/nvim/ex_cmds.c41
-rw-r--r--src/nvim/ex_docmd.c17
-rw-r--r--src/nvim/ex_eval.c43
-rw-r--r--src/nvim/ex_eval.h4
-rw-r--r--src/nvim/ex_getln.c32
-rw-r--r--src/nvim/file_search.c5
-rw-r--r--src/nvim/fileio.c6
-rw-r--r--src/nvim/fold.h8
-rw-r--r--src/nvim/getchar.c851
-rw-r--r--src/nvim/globals.h7
-rw-r--r--src/nvim/hardcopy.c59
-rw-r--r--src/nvim/if_cscope.c47
-rw-r--r--src/nvim/lib/khash.h2
-rw-r--r--src/nvim/lua/converter.c16
-rw-r--r--src/nvim/lua/stdlib.c6
-rw-r--r--src/nvim/lua/vim.lua3
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/message.c14
-rw-r--r--src/nvim/misc1.c55
-rw-r--r--src/nvim/msgpack_rpc/helpers.c16
-rw-r--r--src/nvim/normal.c851
-rw-r--r--src/nvim/ops.c838
-rw-r--r--src/nvim/option.c173
-rw-r--r--src/nvim/regexp.c32
-rw-r--r--src/nvim/search.c3
-rw-r--r--src/nvim/sign.c2
-rw-r--r--src/nvim/state.c2
-rw-r--r--src/nvim/syntax.c4
-rw-r--r--src/nvim/tag.c6
-rw-r--r--src/nvim/terminal.c6
-rw-r--r--src/nvim/testdir/test_autocmd.vim557
-rw-r--r--src/nvim/testdir/test_blockedit.vim52
-rw-r--r--src/nvim/testdir/test_edit.vim91
-rw-r--r--src/nvim/testdir/test_filetype.vim68
-rw-r--r--src/nvim/vim.h2
-rw-r--r--src/nvim/viml/parser/expressions.c222
-rw-r--r--src/nvim/window.c3
-rw-r--r--src/uncrustify.cfg110
56 files changed, 2947 insertions, 1846 deletions
diff --git a/src/mpack/object.h b/src/mpack/object.h
index 5327e56e18..e69821f9de 100644
--- a/src/mpack/object.h
+++ b/src/mpack/object.h
@@ -22,7 +22,7 @@ enum {
};
/* Storing integer in pointers in undefined behavior according to the C
- * standard. Define a union type to accomodate arbitrary user data associated
+ * standard. Define a union type to accommodate arbitrary user data associated
* with nodes(and with requests in rpc.h). */
typedef union {
void *p;
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 4076a0d220..718743ed9c 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -535,7 +535,7 @@ end:
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start_row First line index
-/// @param start_column Last column
+/// @param start_column First column
/// @param end_row Last line index
/// @param end_column Last column
/// @param replacement Array of lines to use as replacement
@@ -1246,7 +1246,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
/// If the current window already shows "buffer", the window is not switched
/// If a window inside the current tabpage (including a float) already shows the
/// buffer One of these windows will be set as current window temporarily.
-/// Otherwise a temporary scratch window (calleed the "autocmd window" for
+/// Otherwise a temporary scratch window (called the "autocmd window" for
/// historical reasons) will be used.
///
/// This is useful e.g. to call vimL functions that only work with the current
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 6f1fb15dac..6e25e627b9 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -839,7 +839,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// - on_win: called when starting to redraw a specific window.
/// ["win", winid, bufnr, topline, botline_guess]
/// - on_line: called for each buffer line being redrawn. (The
-/// interation with fold lines is subject to change)
+/// interaction with fold lines is subject to change)
/// ["win", winid, bufnr, row]
/// - on_end: called at the end of a redraw cycle
/// ["end", tick]
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 144c252687..e956a54dbc 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -58,5 +58,8 @@ return {
"highlights";
"use_tabline";
};
+ option = {
+ "scope";
+ };
}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 3103905819..36179b1fab 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -642,7 +642,7 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, err);
}
-/// Gets an option value string.
+/// Gets the global value of an option.
///
/// @param name Option name
/// @param[out] err Error details, if any
@@ -653,6 +653,115 @@ Object nvim_get_option(String name, Error *err)
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
+/// Gets the value of an option. The behavior of this function matches that of
+/// |:set|: the local value of an option is returned if it exists; otherwise,
+/// the global value is returned. Local values always correspond to the current
+/// buffer or window. To get a buffer-local or window-local option for a
+/// specific buffer or window, use |nvim_buf_get_option()| or
+/// |nvim_win_get_option()|.
+///
+/// @param name Option name
+/// @param opts Optional parameters
+/// - scope: One of 'global' or 'local'. Analagous to
+/// |:setglobal| and |:setlocal|, respectively.
+/// @param[out] err Error details, if any
+/// @return Option value
+Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ Object rv = OBJECT_INIT;
+
+ int scope = 0;
+ if (opts->scope.type == kObjectTypeString) {
+ if (!strcmp(opts->scope.data.string.data, "local")) {
+ scope = OPT_LOCAL;
+ } else if (!strcmp(opts->scope.data.string.data, "global")) {
+ scope = OPT_GLOBAL;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
+ goto end;
+ }
+ } else if (HAS_KEY(opts->scope)) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
+ goto end;
+ }
+
+ long numval = 0;
+ char *stringval = NULL;
+ switch (get_option_value(name.data, &numval, (char_u **)&stringval, scope)) {
+ case 0:
+ rv = STRING_OBJ(cstr_as_string(stringval));
+ break;
+ case 1:
+ rv = INTEGER_OBJ(numval);
+ break;
+ case 2:
+ rv = BOOLEAN_OBJ(!!numval);
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
+ goto end;
+ }
+
+end:
+ return rv;
+}
+
+/// Sets the value of an option. The behavior of this function matches that of
+/// |:set|: for global-local options, both the global and local value are set
+/// unless otherwise specified with {scope}.
+///
+/// @param name Option name
+/// @param value New option value
+/// @param opts Optional parameters
+/// - scope: One of 'global' or 'local'. Analagous to
+/// |:setglobal| and |:setlocal|, respectively.
+/// @param[out] err Error details, if any
+void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ int scope = 0;
+ if (opts->scope.type == kObjectTypeString) {
+ if (!strcmp(opts->scope.data.string.data, "local")) {
+ scope = OPT_LOCAL;
+ } else if (!strcmp(opts->scope.data.string.data, "global")) {
+ scope = OPT_GLOBAL;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
+ return;
+ }
+ } else if (HAS_KEY(opts->scope)) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
+ return;
+ }
+
+ long numval = 0;
+ char *stringval = NULL;
+
+ switch (value.type) {
+ case kObjectTypeInteger:
+ numval = value.data.integer;
+ break;
+ case kObjectTypeBoolean:
+ numval = value.data.boolean ? 1 : 0;
+ break;
+ case kObjectTypeString:
+ stringval = value.data.string.data;
+ break;
+ case kObjectTypeNil:
+ // Do nothing
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "invalid value for option");
+ return;
+ }
+
+ char *e = set_option_value(name.data, numval, stringval, scope);
+ if (e) {
+ api_set_error(err, kErrorTypeException, "%s", e);
+ }
+}
+
/// Gets the option information for all options.
///
/// The dictionary has the full option names as keys and option metadata
@@ -694,7 +803,7 @@ Dictionary nvim_get_option_info(String name, Error *err)
return get_vimoption(name, err);
}
-/// Sets an option value.
+/// Sets the global value of an option.
///
/// @param channel_id
/// @param name Option name
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index ad92d9a2af..65519a8004 100644
--- a/src/nvim/assert.h
+++ b/src/nvim/assert.h
@@ -1,3 +1,5 @@
+// uncrustify:off
+
#ifndef NVIM_ASSERT_H
#define NVIM_ASSERT_H
diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c
index af519dcba9..a236b47027 100644
--- a/src/nvim/aucmd.c
+++ b/src/nvim/aucmd.c
@@ -8,6 +8,7 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/main.h"
+#include "nvim/misc1.h"
#include "nvim/os/os.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -25,13 +26,14 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
}
recursive = true;
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
assert(chanid < VARNUMBER_MAX);
tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid);
tv_dict_set_keys_readonly(dict);
apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE,
NULL, NULL, false, curbuf);
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
recursive = false;
}
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 7e7114b291..1daae85c5e 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -70,6 +70,7 @@ return {
'InsertLeave', -- just after leaving Insert mode
'InsertLeavePre', -- just before leaving Insert mode
'MenuPopup', -- just before popup menu is displayed
+ 'ModeChanged', -- after changing the mode
'OptionSet', -- after setting any option
'QuickFixCmdPost', -- after :make, :grep etc.
'QuickFixCmdPre', -- before :make, :grep etc.
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 2d0c0f3fd5..490fe5a0ac 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -925,6 +925,13 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, c
return FAIL;
}
}
+
+ // need to initialize last_mode for the first ModeChanged autocmd
+ if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) {
+ xfree(last_mode);
+ last_mode = get_mode();
+ }
+
ap->cmds = NULL;
*prev_ap = ap;
last_autopat[(int)event] = ap;
@@ -1440,7 +1447,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
// invalid.
if (fname_io == NULL) {
if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
- || event == EVENT_OPTIONSET) {
+ || event == EVENT_OPTIONSET || event == EVENT_MODECHANGED) {
autocmd_fname = NULL;
} else if (fname != NULL && !ends_excmd(*fname)) {
autocmd_fname = fname;
@@ -1494,11 +1501,12 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|| event == EVENT_CMDWINLEAVE || event == EVENT_CMDUNDEFINED
|| event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|| event == EVENT_DIRCHANGED || event == EVENT_FILETYPE
- || event == EVENT_FUNCUNDEFINED || event == EVENT_OPTIONSET
- || event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE
- || event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING
- || event == EVENT_SYNTAX || event == EVENT_SIGNAL
- || event == EVENT_TABCLOSED || event == EVENT_WINCLOSED) {
+ || event == EVENT_FUNCUNDEFINED || event == EVENT_MODECHANGED
+ || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST
+ || event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY
+ || event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX
+ || event == EVENT_SIGNAL || event == EVENT_TABCLOSED
+ || event == EVENT_WINCLOSED) {
fname = vim_strsave(fname);
} else {
fname = (char_u *)FullName_save((char *)fname, false);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 49e527e98b..e53b2d1dfa 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1116,6 +1116,12 @@ typedef struct {
pos_T w_cursor_corr; // corrected cursor position
} pos_save_T;
+// Struct passed to get_v_event() and restore_v_event().
+typedef struct {
+ bool sve_did_save;
+ hashtab_T sve_hashtab;
+} save_v_event_T;
+
/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
/// \addtogroup MENU_INDEX
/// @{
diff --git a/src/nvim/change.c b/src/nvim/change.c
index c52d992fbe..ef771125f1 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -625,7 +625,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
}
- char_u *newp = xmalloc((size_t)(linelen + newlen - oldlen));
+ char_u *newp = xmalloc(linelen + newlen - oldlen);
// Copy bytes before the cursor.
if (col > 0) {
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 9662f6205f..a662f3a951 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -10,6 +10,7 @@
#include "nvim/event/socket.h"
#include "nvim/fileio.h"
#include "nvim/lua/executor.h"
+#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/shell.h"
@@ -821,7 +822,8 @@ static void set_info_event(void **argv)
Channel *chan = argv[0];
event_T event = (event_T)(ptrdiff_t)argv[1];
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
Dictionary info = channel_info(chan->id);
typval_T retval;
(void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL);
@@ -829,7 +831,7 @@ static void set_info_event(void **argv)
apply_autocmds(event, NULL, NULL, false, curbuf);
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
api_free_dictionary(info);
channel_decref(chan);
}
diff --git a/src/nvim/charset.h b/src/nvim/charset.h
index 47d89717e8..c4e5d9522b 100644
--- a/src/nvim/charset.h
+++ b/src/nvim/charset.h
@@ -14,8 +14,8 @@
/// @return Folded variant.
#define CH_FOLD(c) \
utf_fold((sizeof(c) == sizeof(char)) \
- ?((int)(uint8_t)(c)) \
- :((int)(c)))
+ ? ((int)(uint8_t)(c)) \
+ : ((int)(c)))
/// Flags for vim_str2nr()
typedef enum {
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index d505b40935..fe69da7fcb 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -385,6 +385,7 @@ static void insert_enter(InsertState *s)
State = INSERT;
}
+ trigger_modechanged();
stop_insert_mode = false;
// Need to recompute the cursor position, it might move when the cursor is
@@ -2048,6 +2049,8 @@ static void ins_ctrl_x(void)
// CTRL-V look like CTRL-N
ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
}
+
+ trigger_modechanged();
}
// Whether other than default completion has been selected.
@@ -2660,6 +2663,7 @@ void set_completion(colnr_T startcol, list_T *list)
show_pum(save_w_wrow, save_w_leftcol);
}
+ trigger_modechanged();
ui_flush();
}
@@ -2715,12 +2719,13 @@ static bool pum_enough_matches(void)
static void trigger_complete_changed_event(int cur)
{
static bool recursive = false;
+ save_v_event_T save_v_event;
if (recursive) {
return;
}
- dict_T *v_event = get_vim_var_dict(VV_EVENT);
+ dict_T *v_event = get_v_event(&save_v_event);
if (cur < 0) {
tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc());
} else {
@@ -2736,7 +2741,7 @@ static void trigger_complete_changed_event(int cur)
textlock--;
recursive = false;
- tv_dict_clear(v_event);
+ restore_v_event(v_event, &save_v_event);
}
/// Show the popup menu for the list of matches.
@@ -3838,6 +3843,8 @@ static bool ins_compl_prep(int c)
ins_apply_autocmds(EVENT_COMPLETEDONE);
}
+ trigger_modechanged();
+
/* reset continue_* if we left expansion-mode, if we stay they'll be
* (re)set properly in ins_complete() */
if (!vim_is_ctrl_x_key(c)) {
@@ -3919,7 +3926,8 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
/// Get the user-defined completion function name for completion 'type'
-static char_u *get_complete_funcname(int type) {
+static char_u *get_complete_funcname(int type)
+{
switch (type) {
case CTRL_X_FUNCTION:
return curbuf->b_p_cfu;
@@ -4586,6 +4594,8 @@ static int ins_compl_get_exp(pos_T *ini)
compl_curr_match = compl_old_match;
}
}
+ trigger_modechanged();
+
return i;
}
@@ -5231,7 +5241,7 @@ static int ins_complete(int c, bool enable_pum)
funcname = get_complete_funcname(ctrl_x_mode);
if (*funcname == NUL) {
semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION
- ? "completefunc" : "omnifunc");
+ ? "completefunc" : "omnifunc");
// restore did_ai, so that adding comment leader works
did_ai = save_did_ai;
return FAIL;
@@ -6564,7 +6574,7 @@ static void spell_back_to_badword(void)
int stop_arrow(void)
{
if (arrow_used) {
- Insstart = curwin->w_cursor; //new insertion starts here
+ Insstart = curwin->w_cursor; // new insertion starts here
if (Insstart.col > Insstart_orig.col && !ins_need_undo) {
// Don't update the original insert position when moved to the
// right, except when nothing was inserted yet.
@@ -7630,16 +7640,34 @@ int hkmap(int c)
KAFsofit, hKAF, LAMED, MEMsofit, MEM, NUNsofit, NUN, SAMEH, AIN,
PEIsofit, PEI, ZADIsofit, ZADI, KOF, RESH, hSHIN, TAV,
};
- static char_u map[26] =
- { (char_u)hALEF /*a*/, (char_u)BET /*b*/, (char_u)hKAF /*c*/,
- (char_u)DALET /*d*/, (char_u)-1 /*e*/, (char_u)PEIsofit /*f*/,
- (char_u)GIMEL /*g*/, (char_u)HEI /*h*/, (char_u)IUD /*i*/,
- (char_u)HET /*j*/, (char_u)KOF /*k*/, (char_u)LAMED /*l*/,
- (char_u)MEM /*m*/, (char_u)NUN /*n*/, (char_u)SAMEH /*o*/,
- (char_u)PEI /*p*/, (char_u)-1 /*q*/, (char_u)RESH /*r*/,
- (char_u)ZAIN /*s*/, (char_u)TAV /*t*/, (char_u)TET /*u*/,
- (char_u)VAV /*v*/, (char_u)hSHIN /*w*/, (char_u)-1 /*x*/,
- (char_u)AIN /*y*/, (char_u)ZADI /*z*/ };
+ static char_u map[26] = {
+ (char_u)hALEF, // a
+ (char_u)BET, // b
+ (char_u)hKAF, // c
+ (char_u)DALET, // d
+ (char_u)-1, // e
+ (char_u)PEIsofit, // f
+ (char_u)GIMEL, // g
+ (char_u)HEI, // h
+ (char_u)IUD, // i
+ (char_u)HET, // j
+ (char_u)KOF, // k
+ (char_u)LAMED, // l
+ (char_u)MEM, // m
+ (char_u)NUN, // n
+ (char_u)SAMEH, // o
+ (char_u)PEI, // p
+ (char_u)-1, // q
+ (char_u)RESH, // r
+ (char_u)ZAIN, // s
+ (char_u)TAV, // t
+ (char_u)TET, // u
+ (char_u)VAV, // v
+ (char_u)hSHIN, // w
+ (char_u)-1, // x
+ (char_u)AIN, // y
+ (char_u)ZADI, // z
+ };
if (c == 'N' || c == 'M' || c == 'P' || c == 'C' || c == 'Z') {
return (int)(map[CharOrd(c)] - 1 + p_aleph);
@@ -7965,6 +7993,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
State = NORMAL;
+ trigger_modechanged();
// need to position cursor again (e.g. when on a TAB )
changed_cline_bef_curs();
@@ -8066,6 +8095,7 @@ static void ins_insert(int replaceState)
} else {
State = replaceState | (State & LANGMAP);
}
+ trigger_modechanged();
AppendCharToRedobuff(K_INS);
showmode();
ui_cursor_shape(); // may show different cursor shape
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index d3d0121632..85e81ee975 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -134,13 +134,15 @@ typedef struct {
.vv_flags = flags, \
}
+#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables
+
// Array to hold the value of v: variables.
// The value is in a dictitem, so that it can also be used in the v: scope.
// The reason to use this table anyway is for very quick access to the
// variables with the VV_ defines.
static struct vimvar {
char *vv_name; ///< Name of the variable, without v:.
- TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars).
+ TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars).
char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
} vimvars[] =
{
@@ -150,100 +152,103 @@ static struct vimvar {
// VV_SEND_SERVER "servername"
// VV_REG "register"
// VV_OP "operator"
- VV(VV_COUNT, "count", VAR_NUMBER, VV_RO),
- VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
- VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
- VV(VV_ERRMSG, "errmsg", VAR_STRING, 0),
- VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0),
- VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
- VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO),
- VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0),
- VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO),
- VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
- VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
- VV(VV_FNAME, "fname", VAR_STRING, VV_RO),
- VV(VV_LANG, "lang", VAR_STRING, VV_RO),
- VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO),
- VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO),
- VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO),
- VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO),
- VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO),
- VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO),
- VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO),
- VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO),
- VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO),
- VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX),
- VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX),
- VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX),
- VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX),
- VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO),
- VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO),
- VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
- VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
- VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
- VV(VV_REG, "register", VAR_STRING, VV_RO),
- VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
- VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
- VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO),
- VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO),
- VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO),
- VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO),
- VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0),
- VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO),
- VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0),
- VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO),
- VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0),
- VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO),
- VV(VV_CHAR, "char", VAR_STRING, 0),
- VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0),
- VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0),
- VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0),
- VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0),
- VV(VV_OP, "operator", VAR_STRING, VV_RO),
- VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0),
- VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0),
- VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0),
- VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX),
- VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO),
- VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO),
- VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO),
- VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
- VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO),
- VV(VV_ERRORS, "errors", VAR_LIST, 0),
- VV(VV_FALSE, "false", VAR_BOOL, VV_RO),
- VV(VV_TRUE, "true", VAR_BOOL, VV_RO),
- VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
- VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO),
- VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO),
- VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO),
- VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO),
- VV(VV_TESTING, "testing", VAR_NUMBER, 0),
- VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO),
- VV(VV_EVENT, "event", VAR_DICT, VV_RO),
- VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
- VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
- VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
- VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
+ VV(VV_COUNT, "count", VAR_NUMBER, VV_RO),
+ VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
+ VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
+ VV(VV_ERRMSG, "errmsg", VAR_STRING, 0),
+ VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0),
+ VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
+ VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO),
+ VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0),
+ VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
+ VV(VV_FNAME, "fname", VAR_STRING, VV_RO),
+ VV(VV_LANG, "lang", VAR_STRING, VV_RO),
+ VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO),
+ VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO),
+ VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO),
+ VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO),
+ VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO),
+ VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO),
+ VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO),
+ VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO),
+ VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO),
+ VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX),
+ VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO),
+ VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO),
+ VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
+ VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
+ VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
+ VV(VV_REG, "register", VAR_STRING, VV_RO),
+ VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
+ VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
+ VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO),
+ VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO),
+ VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO),
+ VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO),
+ VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0),
+ VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO),
+ VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0),
+ VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO),
+ VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0),
+ VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO),
+ VV(VV_CHAR, "char", VAR_STRING, 0),
+ VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0),
+ VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0),
+ VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0),
+ VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0),
+ VV(VV_OP, "operator", VAR_STRING, VV_RO),
+ VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0),
+ VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0),
+ VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0),
+ VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO),
+ VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO),
+ VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO),
+ VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO),
+ VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO),
+ VV(VV_ERRORS, "errors", VAR_LIST, 0),
+ VV(VV_FALSE, "false", VAR_BOOL, VV_RO),
+ VV(VV_TRUE, "true", VAR_BOOL, VV_RO),
+ VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
+ VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO),
+ VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO),
+ VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO),
+ VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO),
+ VV(VV_TESTING, "testing", VAR_NUMBER, 0),
+ VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO),
+ VV(VV_EVENT, "event", VAR_DICT, VV_RO),
+ VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
+ VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
+ VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
+ VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
// Neovim
- VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
- VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
- VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO),
- VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
- VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
- VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
- VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
+ VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
+ VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
+ VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO),
+ VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
+ VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
+ VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
+ VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
};
#undef VV
@@ -344,7 +349,7 @@ void eval_init(void)
for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
p = &vimvars[i];
- assert(STRLEN(p->vv_name) <= 16);
+ assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN);
STRCPY(p->vv_di.di_key, p->vv_name);
if (p->vv_flags & VV_RO) {
p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
@@ -4523,7 +4528,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
* dict.name
*/
key = *arg + 1;
- for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) {
+ for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {
}
if (len == 0) {
return FAIL;
@@ -4828,7 +4833,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
} else if (opt_type == -1) { // hidden number option
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
- } else if (opt_type == 1) { // number option
+ } else if (opt_type == 1 || opt_type == 2) { // number or boolean option
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = numval;
} else { // string option
@@ -6603,8 +6608,9 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
: (const char *)s));
// Don't check an autoload name for existence here.
} else if (trans_name != NULL
- && (is_funcref ? find_func(trans_name) == NULL
- : !translated_function_exists((const char *)trans_name))) {
+ && (is_funcref
+ ? find_func(trans_name) == NULL
+ : !translated_function_exists((const char *)trans_name))) {
semsg(_("E700: Unknown function: %s"), s);
} else {
int dict_idx = 0;
@@ -7146,30 +7152,30 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha
typval_T tv_item = { .v_lock = VAR_UNLOCKED };
switch (what) {
- case kDictListKeys:
- tv_item.v_type = VAR_STRING;
- tv_item.vval.v_string = vim_strsave(di->di_key);
- break;
- case kDictListValues:
- tv_copy(&di->di_tv, &tv_item);
- break;
- case kDictListItems: {
- // items()
- list_T *const sub_l = tv_list_alloc(2);
- tv_item.v_type = VAR_LIST;
- tv_item.vval.v_list = sub_l;
- tv_list_ref(sub_l);
-
- tv_list_append_owned_tv(sub_l, (typval_T) {
+ case kDictListKeys:
+ tv_item.v_type = VAR_STRING;
+ tv_item.vval.v_string = vim_strsave(di->di_key);
+ break;
+ case kDictListValues:
+ tv_copy(&di->di_tv, &tv_item);
+ break;
+ case kDictListItems: {
+ // items()
+ list_T *const sub_l = tv_list_alloc(2);
+ tv_item.v_type = VAR_LIST;
+ tv_item.vval.v_list = sub_l;
+ tv_list_ref(sub_l);
+
+ tv_list_append_owned_tv(sub_l, (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
.vval.v_string = (char_u *)xstrdup((const char *)di->di_key),
});
- tv_list_append_tv(sub_l, &di->di_tv);
+ tv_list_append_tv(sub_l, &di->di_tv);
- break;
- }
+ break;
+ }
}
tv_list_append_owned_tv(rettv->vval.v_list, tv_item);
@@ -9649,8 +9655,8 @@ bool var_check_func_name(const char *const name, const bool new_var)
{
// Allow for w: b: s: and t:.
if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':')
- && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2]
- : name[0])) {
+ && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':')
+ ? name[2] : name[0])) {
semsg(_("E704: Funcref variable name must start with a capital: %s"), name);
return false;
}
@@ -10446,11 +10452,15 @@ void option_last_set_msg(LastSet last_set)
}
}
-// reset v:option_new, v:option_old and v:option_type
+// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal,
+// v:option_type, and v:option_command.
void reset_v_option_vars(void)
{
- set_vim_var_string(VV_OPTION_NEW, NULL, -1);
- set_vim_var_string(VV_OPTION_OLD, NULL, -1);
+ set_vim_var_string(VV_OPTION_NEW, NULL, -1);
+ set_vim_var_string(VV_OPTION_OLD, NULL, -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1);
+ set_vim_var_string(VV_OPTION_COMMAND, NULL, -1);
set_vim_var_string(VV_OPTION_TYPE, NULL, -1);
}
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index d34348a274..3b3a68bd29 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -138,6 +138,9 @@ typedef enum {
VV_COMPLETED_ITEM,
VV_OPTION_NEW,
VV_OPTION_OLD,
+ VV_OPTION_OLDLOCAL,
+ VV_OPTION_OLDGLOBAL,
+ VV_OPTION_COMMAND,
VV_OPTION_TYPE,
VV_ERRORS,
VV_FALSE,
@@ -209,8 +212,8 @@ typedef enum {
/// flags for find_name_end()
#define FNE_INCL_BR 1 // find_name_end(): include [] in name
-#define FNE_CHECK_START 2 /* find_name_end(): check name starts with
- valid character */
+#define FNE_CHECK_START 2 // find_name_end(): check name starts with
+ // valid character
typedef struct {
TimeWatcher tw;
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 5008945f09..797420c150 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -726,11 +726,11 @@ json_decode_string_cycle_start:
semsg(_("E474: Using comma in place of colon: %.*s"), LENP(p, e));
goto json_decode_string_fail;
} else if (last_container.special_val == NULL
- ? (last_container.container.v_type == VAR_DICT
- ? (DICT_LEN(last_container.container.vval.v_dict) == 0)
- : (tv_list_len(last_container.container.vval.v_list)
- == 0))
- : (tv_list_len(last_container.special_val) == 0)) {
+ ? (last_container.container.v_type == VAR_DICT
+ ? (DICT_LEN(last_container.container.vval.v_dict) == 0)
+ : (tv_list_len(last_container.container.vval.v_list)
+ == 0))
+ : (tv_list_len(last_container.special_val) == 0)) {
semsg(_("E474: Leading comma: %.*s"), LENP(p, e));
goto json_decode_string_fail;
}
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index d694b8a374..6f4357421b 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -352,20 +352,20 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
const float_T flt_ = (flt); \
switch (xfpclassify(flt_)) { \
case FP_NAN: { \
- ga_concat(gap, "str2float('nan')"); \
- break; \
+ ga_concat(gap, "str2float('nan')"); \
+ break; \
} \
case FP_INFINITE: { \
- if (flt_ < 0) { \
- ga_append(gap, '-'); \
- } \
- ga_concat(gap, "str2float('inf')"); \
- break; \
+ if (flt_ < 0) { \
+ ga_append(gap, '-'); \
+ } \
+ ga_concat(gap, "str2float('inf')"); \
+ break; \
} \
default: { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
- ga_concat(gap, numbuf); \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
+ ga_concat(gap, numbuf); \
} \
} \
} while (0)
@@ -556,18 +556,18 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
const float_T flt_ = (flt); \
switch (xfpclassify(flt_)) { \
case FP_NAN: { \
- emsg(_("E474: Unable to represent NaN value in JSON")); \
- return FAIL; \
+ emsg(_("E474: Unable to represent NaN value in JSON")); \
+ return FAIL; \
} \
case FP_INFINITE: { \
- emsg(_("E474: Unable to represent infinity in JSON")); \
- return FAIL; \
+ emsg(_("E474: Unable to represent infinity in JSON")); \
+ return FAIL; \
} \
default: { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
- ga_concat(gap, numbuf); \
- break; \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
+ ga_concat(gap, numbuf); \
+ break; \
} \
} \
} while (0)
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 77944851d2..f95fe84f69 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -305,9 +305,8 @@ void ex_align(exarg_T *eap)
*/
do {
(void)set_indent(++new_indent, 0);
- }
- while (linelen(NULL) <= width);
- --new_indent;
+ } while (linelen(NULL) <= width);
+ new_indent--;
break;
}
--new_indent;
@@ -1457,7 +1456,7 @@ error:
filterend:
if (curbuf != old_curbuf) {
- --no_wait_return;
+ no_wait_return--;
emsg(_("E135: *Filter* Autocommands must not change current buffer"));
}
if (itmp != NULL) {
@@ -2094,7 +2093,7 @@ void do_wqall(exarg_T *eap)
}
if (buf->b_ffname == NULL) {
semsg(_("E141: No file name for buffer %" PRId64), (int64_t)buf->b_fnum);
- ++error;
+ error++;
} else if (check_readonly(&eap->forceit, buf)
|| check_overwrite(eap, buf, buf->b_fname, buf->b_ffname,
FALSE) == FAIL) {
@@ -2120,17 +2119,15 @@ void do_wqall(exarg_T *eap)
}
}
-/*
- * Check the 'write' option.
- * Return TRUE and give a message when it's not st.
- */
-int not_writing(void)
+// Check the 'write' option.
+// Return true and give a message when it's not st.
+bool not_writing(void)
{
if (p_write) {
- return FALSE;
+ return false;
}
emsg(_("E142: File not written: Writing is disabled by 'write' option"));
- return TRUE;
+ return true;
}
/*
@@ -2945,7 +2942,7 @@ void ex_append(exarg_T *eap)
}
for (;;) {
- msg_scroll = TRUE;
+ msg_scroll = true;
need_wait_return = false;
if (curbuf->b_p_ai) {
if (append_indent >= 0) {
@@ -3131,7 +3128,7 @@ void ex_z(exarg_T *eap)
// the number of '-' and '+' multiplies the distance
if (*kind == '-' || *kind == '+') {
- for (x = kind + 1; *x == *kind; ++x) {
+ for (x = kind + 1; *x == *kind; x++) {
}
}
@@ -3214,26 +3211,24 @@ void ex_z(exarg_T *eap)
ex_no_reprint = true;
}
-/*
- * Check if the secure flag is set (.exrc or .vimrc in current directory).
- * If so, give an error message and return TRUE.
- * Otherwise, return FALSE.
- */
-int check_secure(void)
+// Check if the secure flag is set (.exrc or .vimrc in current directory).
+// If so, give an error message and return true.
+// Otherwise, return false.
+bool check_secure(void)
{
if (secure) {
secure = 2;
emsg(_(e_curdir));
- return TRUE;
+ return true;
}
// In the sandbox more things are not allowed, including the things
// disallowed in secure mode.
if (sandbox != 0) {
emsg(_(e_sandbox));
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Previous substitute replacement string
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 62919c98f7..77cd50ecb7 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -196,6 +196,7 @@ void do_exmode(void)
exmode_active = true;
State = NORMAL;
+ trigger_modechanged();
// When using ":global /pat/ visual" and then "Q" we return to continue
// the :global command.
@@ -434,7 +435,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
&& cstack.cs_idx < 0
&& !(getline_is_func
&& func_has_abort(real_cookie))) {
- did_emsg = FALSE;
+ did_emsg = false;
}
/*
@@ -818,7 +819,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// of interrupts or errors to exceptions, and ensure that no more
// commands are executed.
if (current_exception) {
- void *p = NULL;
+ char *p = NULL;
char_u *saved_sourcing_name;
int saved_sourcing_lnum;
struct msglist *messages = NULL;
@@ -835,7 +836,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
vim_snprintf((char *)IObuff, IOSIZE,
_("E605: Exception not caught: %s"),
current_exception->value);
- p = vim_strsave(IObuff);
+ p = (char *)vim_strsave(IObuff);
break;
case ET_ERROR:
messages = current_exception->messages;
@@ -2716,7 +2717,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *
*/
gap = &curbuf->b_ucmds;
for (;;) {
- for (j = 0; j < gap->ga_len; ++j) {
+ for (j = 0; j < gap->ga_len; j++) {
uc = USER_CMD_GA(gap, j);
cp = eap->cmd;
np = uc->uc_name;
@@ -5327,7 +5328,7 @@ static void uc_list(char_u *name, size_t name_len)
? &prevwin->w_buffer->b_ucmds
: &curbuf->b_ucmds;
for (;;) {
- for (i = 0; i < gap->ga_len; ++i) {
+ for (i = 0; i < gap->ga_len; i++) {
cmd = USER_CMD_GA(gap, i);
a = cmd->uc_argt;
@@ -5714,7 +5715,7 @@ static void ex_delcommand(exarg_T *eap)
gap = &curbuf->b_ucmds;
for (;;) {
- for (i = 0; i < gap->ga_len; ++i) {
+ for (i = 0; i < gap->ga_len; i++) {
cmd = USER_CMD_GA(gap, i);
cmp = STRCMP(eap->arg, cmd->uc_name);
if (cmp <= 0) {
@@ -8337,9 +8338,7 @@ static void ex_redir(exarg_T *eap)
if (var_redir_start(skipwhite(arg), append) == OK) {
redir_vname = 1;
}
- }
- // TODO: redirect to a buffer
- else {
+ } else { // TODO(vim): redirect to a buffer
semsg(_(e_invarg2), eap->arg);
}
}
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index f60f0ebe98..b1c59a607c 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -437,7 +437,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int
}
}
} else {
- *should_free = FALSE;
+ *should_free = false;
ret = value;
}
@@ -888,11 +888,10 @@ void ex_endif(exarg_T *eap)
*/
void ex_else(exarg_T *eap)
{
- int skip;
int result;
cstack_T *const cstack = eap->cstack;
- skip = CHECK_SKIP;
+ bool skip = CHECK_SKIP;
if (cstack->cs_idx < 0
|| (cstack->cs_flags[cstack->cs_idx]
@@ -902,14 +901,14 @@ void ex_else(exarg_T *eap)
return;
}
eap->errmsg = N_("E582: :elseif without :if");
- skip = TRUE;
+ skip = true;
} else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) {
if (eap->cmdidx == CMD_else) {
eap->errmsg = N_("E583: multiple :else");
return;
}
eap->errmsg = N_("E584: :elseif after :else");
- skip = TRUE;
+ skip = true;
}
// if skipping or the ":if" was TRUE, reset ACTIVE, otherwise set it
@@ -917,7 +916,7 @@ void ex_else(exarg_T *eap)
if (eap->errmsg == NULL) {
cstack->cs_flags[cstack->cs_idx] = CSF_TRUE;
}
- skip = TRUE; // don't evaluate an ":elseif"
+ skip = true; // don't evaluate an ":elseif"
} else {
cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE;
}
@@ -932,7 +931,7 @@ void ex_else(exarg_T *eap)
// later on.
if (!skip && dbg_check_skipped(eap) && got_int) {
(void)do_intthrow(cstack);
- skip = TRUE;
+ skip = true;
}
if (eap->cmdidx == CMD_elseif) {
@@ -1322,9 +1321,9 @@ void ex_try(exarg_T *eap)
void ex_catch(exarg_T *eap)
{
int idx = 0;
- int give_up = FALSE;
- int skip = FALSE;
- int caught = FALSE;
+ bool give_up = false;
+ bool skip = false;
+ bool caught = false;
char_u *end;
char_u save_char = 0;
char_u *save_cpo;
@@ -1335,13 +1334,13 @@ void ex_catch(exarg_T *eap)
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) {
eap->errmsg = N_("E603: :catch without :try");
- give_up = TRUE;
+ give_up = true;
} else {
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
// Report what's missing if the matching ":try" is not in its
// finally clause.
eap->errmsg = get_end_emsg(cstack);
- skip = TRUE;
+ skip = true;
}
for (idx = cstack->cs_idx; idx > 0; --idx) {
if (cstack->cs_flags[idx] & CSF_TRY) {
@@ -1352,7 +1351,7 @@ void ex_catch(exarg_T *eap)
// Give up for a ":catch" after ":finally" and ignore it.
// Just parse.
eap->errmsg = N_("E604: :catch after :finally");
- give_up = TRUE;
+ give_up = true;
} else {
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
&cstack->cs_looplevel);
@@ -1425,7 +1424,7 @@ void ex_catch(exarg_T *eap)
// CTRL-C while matching should abort it.
//
prev_got_int = got_int;
- got_int = FALSE;
+ got_int = false;
caught = vim_regexec_nl(&regmatch, (char_u *)current_exception->value,
(colnr_T)0);
got_int |= prev_got_int;
@@ -1602,8 +1601,7 @@ void ex_finally(exarg_T *eap)
void ex_endtry(exarg_T *eap)
{
int idx;
- int skip;
- int rethrow = FALSE;
+ bool rethrow = false;
int pending = CSTP_NONE;
void *rettv = NULL;
cstack_T *const cstack = eap->cstack;
@@ -1621,20 +1619,19 @@ void ex_endtry(exarg_T *eap)
// made inactive by a ":continue", ":break", ":return", or ":finish" in
// the finally clause. The latter case need not be tested since then
// anything pending has already been discarded.
- skip = (did_emsg || got_int || current_exception
- || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE));
+ bool skip = did_emsg || got_int || current_exception
+ || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
eap->errmsg = get_end_emsg(cstack);
// Find the matching ":try" and report what's missing.
idx = cstack->cs_idx;
do {
- --idx;
- }
- while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY));
+ idx--;
+ } while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY));
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
&cstack->cs_looplevel);
- skip = TRUE;
+ skip = true;
/*
* If an exception is being thrown, discard it to prevent it from
@@ -1677,7 +1674,7 @@ void ex_endtry(exarg_T *eap)
// before the ":endtry". That is, throw an interrupt exception and
// set "skip" and "rethrow".
if (got_int) {
- skip = TRUE;
+ skip = true;
(void)do_intthrow(cstack);
// The do_intthrow() call may have reset current_exception or
// cstack->cs_pending[idx].
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index 25b30b2805..15da4b2d60 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -17,8 +17,8 @@
#define CSF_THROWN 0x0400 // exception thrown to this try conditional
#define CSF_CAUGHT 0x0800 // exception caught by this try conditional
#define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try"
-/* Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset
- * (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. */
+// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset
+// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set.
/*
* What's pending for being reactivated at the ":endtry" of this try
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 3b5953ae69..475f22d061 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -880,7 +880,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
TryState tstate;
Error err = ERROR_INIT;
bool tl_ret = true;
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
char firstcbuf[2];
firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
firstcbuf[1] = 0;
@@ -894,7 +895,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
false, curbuf);
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
tl_ret = try_leave(&tstate, &err);
@@ -906,6 +907,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
}
tl_ret = true;
}
+ trigger_modechanged();
state_enter(&s->state);
@@ -924,7 +926,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
if (tv_dict_get_number(dict, "abort") != 0) {
s->gotesc = 1;
}
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
}
cmdmsg_rl = false;
@@ -2289,7 +2291,8 @@ static int command_line_changed(CommandLineState *s)
if (has_event(EVENT_CMDLINECHANGED)) {
TryState tstate;
Error err = ERROR_INIT;
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
char firstcbuf[2];
firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-');
@@ -2303,7 +2306,7 @@ static int command_line_changed(CommandLineState *s)
apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf,
(char_u *)firstcbuf, false, curbuf);
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
bool tl_ret = try_leave(&tstate, &err);
if (!tl_ret && ERROR_SET(&err)) {
@@ -2488,27 +2491,25 @@ char *get_text_locked_msg(void)
}
/// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and
-/// return TRUE when it is and give an error message.
-int curbuf_locked(void)
+/// return true when it is and give an error message.
+bool curbuf_locked(void)
{
if (curbuf->b_ro_locked > 0) {
emsg(_("E788: Not allowed to edit another buffer now"));
- return TRUE;
+ return true;
}
return allbuf_locked();
}
-/*
- * Check if "allbuf_lock" is set and return TRUE when it is and give an error
- * message.
- */
-int allbuf_locked(void)
+// Check if "allbuf_lock" is set and return true when it is and give an error
+// message.
+bool allbuf_locked(void)
{
if (allbuf_lock > 0) {
emsg(_("E811: Not allowed to change buffer information now"));
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
static int cmdline_charsize(int idx)
@@ -6547,6 +6548,7 @@ static int open_cmdwin(void)
cmdmsg_rl = save_cmdmsg_rl;
State = save_State;
+ trigger_modechanged();
setmouse();
return cmdwin_result;
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 3a9acbd61c..5953a574f3 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1603,7 +1603,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
recursive = true;
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
char buf[8];
switch (scope) {
@@ -1648,7 +1649,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false,
curbuf);
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
recursive = false;
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 1b39a7410a..24428c2d9a 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -100,8 +100,8 @@ struct bw_info {
char_u bw_rest[CONV_RESTLEN]; // not converted bytes
int bw_restlen; // nr of bytes in bw_rest[]
int bw_first; // first write call
- char_u *bw_conv_buf; // buffer for writing converted chars
- int bw_conv_buflen; // size of bw_conv_buf
+ char_u *bw_conv_buf; // buffer for writing converted chars
+ size_t bw_conv_buflen; // size of bw_conv_buf
int bw_conv_error; // set for conversion error
linenr_T bw_conv_error_lnum; // first line with error or zero
linenr_T bw_start_lnum; // line number at start of buffer
@@ -1851,7 +1851,7 @@ failed:
msg_scrolled_ign = true;
if (!read_stdin && !read_buffer) {
- p = (char_u *)msg_trunc_attr((char *)IObuff, FALSE, 0);
+ p = (char_u *)msg_trunc_attr((char *)IObuff, false, 0);
}
if (read_stdin || read_buffer || restart_edit != 0
diff --git a/src/nvim/fold.h b/src/nvim/fold.h
index a96ea8a039..6b29214760 100644
--- a/src/nvim/fold.h
+++ b/src/nvim/fold.h
@@ -14,10 +14,10 @@
*/
typedef struct foldinfo {
linenr_T fi_lnum; // line number where fold starts
- int fi_level; /* level of the fold; when this is zero the
- other fields are invalid */
- int fi_low_level; /* lowest fold level that starts in the same
- line */
+ int fi_level; // level of the fold; when this is zero the
+ // other fields are invalid
+ int fi_low_level; // lowest fold level that starts in the same
+ // line
long fi_lines;
} foldinfo_T;
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 2f4b59837a..27e8cb36af 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1680,6 +1680,325 @@ int char_avail(void)
return retval != NUL;
}
+typedef enum {
+ map_result_fail, // failed, break loop
+ map_result_get, // get a character from typeahead
+ map_result_retry, // try to map again
+ map_result_nomatch // no matching mapping, get char
+} map_result_T;
+
+/// Handle mappings in the typeahead buffer.
+/// - When something was mapped, return map_result_retry for recursive mappings.
+/// - When nothing mapped and typeahead has a character: return map_result_get.
+/// - When there is no match yet, return map_result_nomatch, need to get more
+/// typeahead.
+static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
+{
+ mapblock_T *mp = NULL;
+ mapblock_T *mp2;
+ mapblock_T *mp_match;
+ int mp_match_len = 0;
+ int max_mlen = 0;
+ int tb_c1;
+ int mlen;
+ int nolmaplen;
+ int keylen = *keylenp;
+ int i;
+ int local_State = get_real_state();
+
+ // Check for a mappable key sequence.
+ // Walk through one maphash[] list until we find an entry that matches.
+ //
+ // Don't look for mappings if:
+ // - no_mapping set: mapping disabled (e.g. for CTRL-V)
+ // - maphash_valid not set: no mappings present.
+ // - typebuf.tb_buf[typebuf.tb_off] should not be remapped
+ // - in insert or cmdline mode and 'paste' option set
+ // - waiting for "hit return to continue" and CR or SPACE typed
+ // - waiting for a char with --more--
+ // - in Ctrl-X mode, and we get a valid char for that mode
+ tb_c1 = typebuf.tb_buf[typebuf.tb_off];
+ if (no_mapping == 0 && maphash_valid
+ && (no_zero_mapping == 0 || tb_c1 != '0')
+ && (typebuf.tb_maplen == 0
+ || (p_remap
+ && !(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR))))
+ && !(p_paste && (State & (INSERT + CMDLINE)))
+ && !(State == HITRETURN && (tb_c1 == CAR || tb_c1 == ' '))
+ && State != ASKMORE
+ && State != CONFIRM
+ && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(tb_c1))
+ || ((compl_cont_status & CONT_LOCAL)
+ && (tb_c1 == Ctrl_N || tb_c1 == Ctrl_P)))) {
+ if (tb_c1 == K_SPECIAL) {
+ nolmaplen = 2;
+ } else {
+ LANGMAP_ADJUST(tb_c1, !(State & (CMDLINE | INSERT)) && get_real_state() != SELECTMODE);
+ nolmaplen = 0;
+ }
+ // First try buffer-local mappings.
+ mp = curbuf->b_maphash[MAP_HASH(local_State, tb_c1)];
+ mp2 = maphash[MAP_HASH(local_State, tb_c1)];
+ if (mp == NULL) {
+ // There are no buffer-local mappings.
+ mp = mp2;
+ mp2 = NULL;
+ }
+ // Loop until a partly matching mapping is found or all (local)
+ // mappings have been checked.
+ // The longest full match is remembered in "mp_match".
+ // A full match is only accepted if there is no partly match, so "aa"
+ // and "aaa" can both be mapped.
+ mp_match = NULL;
+ mp_match_len = 0;
+ for (; mp != NULL; mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next)) {
+ // Only consider an entry if the first character matches and it is
+ // for the current state.
+ // Skip ":lmap" mappings if keys were mapped.
+ if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State)
+ && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) {
+ int nomap = nolmaplen;
+ int c2;
+ // find the match length of this mapping
+ for (mlen = 1; mlen < typebuf.tb_len; mlen++) {
+ c2 = typebuf.tb_buf[typebuf.tb_off + mlen];
+ if (nomap > 0) {
+ nomap--;
+ } else if (c2 == K_SPECIAL) {
+ nomap = 2;
+ } else {
+ LANGMAP_ADJUST(c2, true);
+ }
+ if (mp->m_keys[mlen] != c2) {
+ break;
+ }
+ }
+
+ // Don't allow mapping the first byte(s) of a multi-byte char.
+ // Happens when mapping <M-a> and then changing 'encoding'.
+ // Beware that 0x80 is escaped.
+ char_u *p1 = mp->m_keys;
+ char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
+
+ if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len(p2)) {
+ mlen = 0;
+ }
+
+ // Check an entry whether it matches.
+ // - Full match: mlen == keylen
+ // - Partly match: mlen == typebuf.tb_len
+ keylen = mp->m_keylen;
+ if (mlen == keylen || (mlen == typebuf.tb_len && typebuf.tb_len < keylen)) {
+ char_u *s;
+ int n;
+
+ // If only script-local mappings are allowed, check if the
+ // mapping starts with K_SNR.
+ s = typebuf.tb_noremap + typebuf.tb_off;
+ if (*s == RM_SCRIPT
+ && (mp->m_keys[0] != K_SPECIAL
+ || mp->m_keys[1] != KS_EXTRA
+ || mp->m_keys[2] != KE_SNR)) {
+ continue;
+ }
+
+ // If one of the typed keys cannot be remapped, skip the entry.
+ for (n = mlen; --n >= 0;) {
+ if (*s++ & (RM_NONE|RM_ABBR)) {
+ break;
+ }
+ }
+ if (n >= 0) {
+ continue;
+ }
+
+ if (keylen > typebuf.tb_len) {
+ if (!*timedout && !(mp_match != NULL && mp_match->m_nowait)) {
+ // break at a partly match
+ keylen = KEYLEN_PART_MAP;
+ break;
+ }
+ } else if (keylen > mp_match_len
+ || (keylen == mp_match_len
+ && mp_match != NULL
+ && (mp_match->m_mode & LANGMAP) == 0
+ && (mp->m_mode & LANGMAP) != 0)) {
+ // found a longer match
+ mp_match = mp;
+ mp_match_len = keylen;
+ }
+ } else {
+ // No match; may have to check for termcode at next character.
+ if (max_mlen < mlen) {
+ max_mlen = mlen;
+ }
+ }
+ }
+ }
+
+ // If no partly match found, use the longest full match.
+ if (keylen != KEYLEN_PART_MAP) {
+ mp = mp_match;
+ keylen = mp_match_len;
+ }
+ }
+
+ // Check for match with 'pastetoggle'
+ if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) {
+ bool match = typebuf_match_len(p_pt, &mlen);
+ if (match) {
+ // write chars to script file(s)
+ if (mlen > typebuf.tb_maplen) {
+ gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
+ (size_t)(mlen - typebuf.tb_maplen));
+ }
+
+ del_typebuf(mlen, 0); // remove the chars
+ set_option_value("paste", !p_paste, NULL, 0);
+ if (!(State & INSERT)) {
+ msg_col = 0;
+ msg_row = Rows - 1;
+ msg_clr_eos(); // clear ruler
+ }
+ status_redraw_all();
+ redraw_statuslines();
+ showmode();
+ setcursor();
+ *keylenp = keylen;
+ return map_result_retry;
+ }
+ // Need more chars for partly match.
+ if (mlen == typebuf.tb_len) {
+ keylen = KEYLEN_PART_KEY;
+ } else if (max_mlen < mlen) {
+ // no match, may have to check for termcode at next character
+ max_mlen = mlen + 1;
+ }
+ }
+
+ if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) {
+ // No matching mapping found or found a non-matching mapping that
+ // matches at least what the matching mapping matched
+ keylen = 0;
+ (void)keylen; // suppress clang/dead assignment
+ // If there was no mapping, use the character from the typeahead
+ // buffer right here. Otherwise, use the mapping (loop around).
+ if (mp == NULL) {
+ *keylenp = keylen;
+ return map_result_get; // got character, break for loop
+ } else {
+ keylen = mp_match_len;
+ }
+ }
+
+ // complete match
+ if (keylen >= 0 && keylen <= typebuf.tb_len) {
+ char_u *map_str;
+ int save_m_expr;
+ int save_m_noremap;
+ int save_m_silent;
+
+ // Write chars to script file(s).
+ // Note: :lmap mappings are written *after* being applied. #5658
+ if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) {
+ gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
+ (size_t)(keylen - typebuf.tb_maplen));
+ }
+
+ cmd_silent = (typebuf.tb_silent > 0);
+ del_typebuf(keylen, 0); // remove the mapped keys
+
+ // Put the replacement string in front of mapstr.
+ // The depth check catches ":map x y" and ":map y x".
+ if (++*mapdepth >= p_mmd) {
+ emsg(_("E223: recursive mapping"));
+ if (State & CMDLINE) {
+ redrawcmdline();
+ } else {
+ setcursor();
+ }
+ flush_buffers(FLUSH_MINIMAL);
+ *mapdepth = 0; // for next one
+ *keylenp = keylen;
+ return map_result_fail;
+ }
+
+ // In Select mode and a Visual mode mapping is used: Switch to Visual
+ // mode temporarily. Append K_SELECT to switch back to Select mode.
+ if (VIsual_active && VIsual_select && (mp->m_mode & VISUAL)) {
+ VIsual_select = false;
+ (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false);
+ }
+
+ // Copy the values from *mp that are used, because evaluating the
+ // expression may invoke a function that redefines the mapping, thereby
+ // making *mp invalid.
+ save_m_expr = mp->m_expr;
+ save_m_noremap = mp->m_noremap;
+ save_m_silent = mp->m_silent;
+ char_u *save_m_keys = NULL; // only saved when needed
+ char_u *save_m_str = NULL; // only saved when needed
+
+ // Handle ":map <expr>": evaluate the {rhs} as an
+ // expression. Also save and restore the command line
+ // for "normal :".
+ if (mp->m_expr) {
+ int save_vgetc_busy = vgetc_busy;
+ const bool save_may_garbage_collect = may_garbage_collect;
+
+ vgetc_busy = 0;
+ may_garbage_collect = false;
+
+ save_m_keys = vim_strsave(mp->m_keys);
+ save_m_str = vim_strsave(mp->m_str);
+ map_str = eval_map_expr(save_m_str, NUL);
+ vgetc_busy = save_vgetc_busy;
+ may_garbage_collect = save_may_garbage_collect;
+ } else {
+ map_str = mp->m_str;
+ }
+
+ // Insert the 'to' part in the typebuf.tb_buf.
+ // If 'from' field is the same as the start of the 'to' field, don't
+ // remap the first character (but do allow abbreviations).
+ // If m_noremap is set, don't remap the whole 'to' part.
+ if (map_str == NULL) {
+ i = FAIL;
+ } else {
+ int noremap;
+
+ // If this is a LANGMAP mapping, then we didn't record the keys
+ // at the start of the function and have to record them now.
+ if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) {
+ gotchars(map_str, STRLEN(map_str));
+ }
+
+ if (save_m_noremap != REMAP_YES) {
+ noremap = save_m_noremap;
+ } else if (STRNCMP(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys,
+ (size_t)keylen) != 0) {
+ noremap = REMAP_YES;
+ } else {
+ noremap = REMAP_SKIP;
+ }
+ i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent);
+ if (save_m_expr) {
+ xfree(map_str);
+ }
+ }
+ xfree(save_m_keys);
+ xfree(save_m_str);
+ *keylenp = keylen;
+ if (i == FAIL) {
+ return map_result_fail;
+ }
+ return map_result_retry;
+ }
+
+ *keylenp = keylen;
+ return map_result_nomatch;
+}
+
// unget one character (can only be done once!)
void vungetc(int c)
{
@@ -1715,44 +2034,27 @@ void vungetc(int c)
static int vgetorpeek(bool advance)
{
int c, c1;
- int keylen;
- char_u *s;
- mapblock_T *mp;
- mapblock_T *mp2;
- mapblock_T *mp_match;
- int mp_match_len = 0;
- bool timedout = false; // waited for more than 1 second
- // for mapping to complete
+ bool timedout = false; // waited for more than 1 second for mapping to complete
int mapdepth = 0; // check for recursive mapping
bool mode_deleted = false; // set when mode has been deleted
- int local_State;
- int mlen;
- int max_mlen;
- int i;
int new_wcol, new_wrow;
int n;
- int nolmaplen;
int old_wcol, old_wrow;
int wait_tb_len;
- /*
- * This function doesn't work very well when called recursively. This may
- * happen though, because of:
- * 1. The call to add_to_showcmd(). char_avail() is then used to check if
- * there is a character available, which calls this function. In that
- * case we must return NUL, to indicate no character is available.
- * 2. A GUI callback function writes to the screen, causing a
- * wait_return().
- * Using ":normal" can also do this, but it saves the typeahead buffer,
- * thus it should be OK. But don't get a key from the user then.
- */
- if (vgetc_busy > 0
- && ex_normal_busy == 0) {
+ // This function doesn't work very well when called recursively. This may
+ // happen though, because of:
+ // 1. The call to add_to_showcmd(). char_avail() is then used to check if
+ // there is a character available, which calls this function. In that
+ // case we must return NUL, to indicate no character is available.
+ // 2. A GUI callback function writes to the screen, causing a
+ // wait_return().
+ // Using ":normal" can also do this, but it saves the typeahead buffer,
+ // thus it should be OK. But don't get a key from the user then.
+ if (vgetc_busy > 0 && ex_normal_busy == 0) {
return NUL;
}
- local_State = get_real_state();
-
++vgetc_busy;
if (advance) {
@@ -1765,9 +2067,7 @@ static int vgetorpeek(bool advance)
reg_executing = 0;
}
do {
- /*
- * get a character: 1. from the stuffbuffer
- */
+ // get a character: 1. from the stuffbuffer
if (typeahead_char != 0) {
c = typeahead_char;
if (advance) {
@@ -1784,30 +2084,27 @@ static int vgetorpeek(bool advance)
KeyStuffed = true;
}
if (typebuf.tb_no_abbr_cnt == 0) {
- typebuf.tb_no_abbr_cnt = 1; // no abbreviations now
+ typebuf.tb_no_abbr_cnt = 1; // no abbreviations now
}
} else {
- /*
- * Loop until we either find a matching mapped key, or we
- * are sure that it is not a mapped key.
- * If a mapped key sequence is found we go back to the start to
- * try re-mapping.
- */
+ // Loop until we either find a matching mapped key, or we
+ // are sure that it is not a mapped key.
+ // If a mapped key sequence is found we go back to the start to
+ // try re-mapping.
for (;;) {
- /*
- * os_breakcheck() is slow, don't use it too often when
- * inside a mapping. But call it each time for typed
- * characters.
- */
+ // os_breakcheck() is slow, don't use it too often when
+ // inside a mapping. But call it each time for typed
+ // characters.
if (typebuf.tb_maplen) {
line_breakcheck();
} else {
- os_breakcheck(); // check for CTRL-C
+ os_breakcheck(); // check for CTRL-C
}
- keylen = 0;
+ int keylen = 0;
if (got_int) {
// flush all input
c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L);
+
// If inchar() returns TRUE (script file was active) or we
// are inside a mapping, get out of Insert mode.
// Otherwise we behave like having gotten a CTRL-C.
@@ -1831,361 +2128,49 @@ static int vgetorpeek(bool advance)
break;
} else if (typebuf.tb_len > 0) {
- /*
- * Check for a mappable key sequence.
- * Walk through one maphash[] list until we find an
- * entry that matches.
- *
- * Don't look for mappings if:
- * - no_mapping set: mapping disabled (e.g. for CTRL-V)
- * - maphash_valid not set: no mappings present.
- * - typebuf.tb_buf[typebuf.tb_off] should not be remapped
- * - in insert or cmdline mode and 'paste' option set
- * - waiting for "hit return to continue" and CR or SPACE
- * typed
- * - waiting for a char with --more--
- * - in Ctrl-X mode, and we get a valid char for that mode
- */
- mp = NULL;
- max_mlen = 0;
- c1 = typebuf.tb_buf[typebuf.tb_off];
- if (no_mapping == 0 && maphash_valid
- && (no_zero_mapping == 0 || c1 != '0')
- && (typebuf.tb_maplen == 0
- || (p_remap
- && (typebuf.tb_noremap[typebuf.tb_off]
- & (RM_NONE|RM_ABBR)) == 0))
- && !(p_paste && (State & (INSERT + CMDLINE)))
- && !(State == HITRETURN && (c1 == CAR || c1 == ' '))
- && State != ASKMORE
- && State != CONFIRM
- && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c1))
- || ((compl_cont_status & CONT_LOCAL)
- && (c1 == Ctrl_N
- || c1 == Ctrl_P)))) {
- if (c1 == K_SPECIAL) {
- nolmaplen = 2;
- } else {
- LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0
- && get_real_state() != SELECTMODE);
- nolmaplen = 0;
- }
- // First try buffer-local mappings.
- mp = curbuf->b_maphash[MAP_HASH(local_State, c1)];
- mp2 = maphash[MAP_HASH(local_State, c1)];
- if (mp == NULL) {
- // There are no buffer-local mappings.
- mp = mp2;
- mp2 = NULL;
- }
- /*
- * Loop until a partly matching mapping is found or
- * all (local) mappings have been checked.
- * The longest full match is remembered in "mp_match".
- * A full match is only accepted if there is no partly
- * match, so "aa" and "aaa" can both be mapped.
- */
- mp_match = NULL;
- mp_match_len = 0;
- for (; mp != NULL;
- mp->m_next == NULL ? (mp = mp2, mp2 = NULL) :
- (mp = mp->m_next)) {
- /*
- * Only consider an entry if the first character
- * matches and it is for the current state.
- * Skip ":lmap" mappings if keys were mapped.
- */
- if (mp->m_keys[0] == c1
- && (mp->m_mode & local_State)
- && ((mp->m_mode & LANGMAP) == 0
- || typebuf.tb_maplen == 0)) {
- int nomap = nolmaplen;
- int c2;
- // find the match length of this mapping
- for (mlen = 1; mlen < typebuf.tb_len; mlen++) {
- c2 = typebuf.tb_buf[typebuf.tb_off + mlen];
- if (nomap > 0) {
- --nomap;
- } else if (c2 == K_SPECIAL) {
- nomap = 2;
- } else {
- LANGMAP_ADJUST(c2, TRUE);
- }
- if (mp->m_keys[mlen] != c2) {
- break;
- }
- }
+ // Check for a mapping in "typebuf".
+ map_result_T result = (map_result_T)handle_mapping(&keylen, &timedout, &mapdepth);
- /* Don't allow mapping the first byte(s) of a
- * multi-byte char. Happens when mapping
- * <M-a> and then changing 'encoding'. Beware
- * that 0x80 is escaped. */
- char_u *p1 = mp->m_keys;
- char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
-
- if (p2 != NULL && MB_BYTE2LEN(c1) > utfc_ptr2len(p2)) {
- mlen = 0;
- }
-
- // Check an entry whether it matches.
- // - Full match: mlen == keylen
- // - Partly match: mlen == typebuf.tb_len
- keylen = mp->m_keylen;
- if (mlen == keylen
- || (mlen == typebuf.tb_len
- && typebuf.tb_len < keylen)) {
- /*
- * If only script-local mappings are
- * allowed, check if the mapping starts
- * with K_SNR.
- */
- s = typebuf.tb_noremap + typebuf.tb_off;
- if (*s == RM_SCRIPT
- && (mp->m_keys[0] != K_SPECIAL
- || mp->m_keys[1] != KS_EXTRA
- || mp->m_keys[2]
- != KE_SNR)) {
- continue;
- }
- /*
- * If one of the typed keys cannot be
- * remapped, skip the entry.
- */
- for (n = mlen; --n >= 0;) {
- if (*s++ & (RM_NONE|RM_ABBR)) {
- break;
- }
- }
- if (n >= 0) {
- continue;
- }
-
- if (keylen > typebuf.tb_len) {
- if (!timedout && !(mp_match != NULL
- && mp_match->m_nowait)) {
- // break at a partly match
- keylen = KEYLEN_PART_MAP;
- break;
- }
- } else if (keylen > mp_match_len
- || (keylen == mp_match_len
- && mp_match != NULL
- && (mp_match->m_mode & LANGMAP) == 0
- && (mp->m_mode & LANGMAP) != 0)) {
- // found a longer match
- mp_match = mp;
- mp_match_len = keylen;
- }
- } else {
- // No match; may have to check for termcode at next character.
- if (max_mlen < mlen) {
- max_mlen = mlen;
- }
- }
- }
- }
-
- /* If no partly match found, use the longest full
- * match. */
- if (keylen != KEYLEN_PART_MAP) {
- mp = mp_match;
- keylen = mp_match_len;
- }
- }
-
- if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) {
- bool match = typebuf_match_len(p_pt, &mlen);
- if (match) {
- // write chars to script file(s)
- if (mlen > typebuf.tb_maplen) {
- gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
- (size_t)(mlen - typebuf.tb_maplen));
- }
-
- del_typebuf(mlen, 0); // Remove the chars.
- set_option_value("paste", !p_paste, NULL, 0);
- if (!(State & INSERT)) {
- msg_col = 0;
- msg_row = Rows - 1;
- msg_clr_eos(); // clear ruler
- }
- status_redraw_all();
- redraw_statuslines();
- showmode();
- setcursor();
- continue;
- }
- // Need more chars for partly match.
- if (mlen == typebuf.tb_len) {
- keylen = KEYLEN_PART_KEY;
- } else if (max_mlen < mlen) {
- // no match, may have to check for termcode at
- // next character
- max_mlen = mlen + 1;
- }
+ if (result == map_result_retry) {
+ // try mapping again
+ continue;
}
- if ((mp == NULL || max_mlen >= mp_match_len)
- && keylen != KEYLEN_PART_MAP) {
- // No matching mapping found or found a non-matching mapping that
- // matches at least what the matching mapping matched
- keylen = 0;
- (void)keylen; // suppress clang/dead assignment
- // If there was no mapping, use the character from the typeahead
- // buffer right here. Otherwise, use the mapping (loop around).
- if (mp == NULL) {
- // get a character: 2. from the typeahead buffer
- c = typebuf.tb_buf[typebuf.tb_off] & 255;
- if (advance) { // remove chars from tb_buf
- cmd_silent = (typebuf.tb_silent > 0);
- if (typebuf.tb_maplen > 0) {
- KeyTyped = false;
- } else {
- KeyTyped = true;
- // write char to script file(s)
- gotchars(typebuf.tb_buf + typebuf.tb_off, 1);
- }
- KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
- del_typebuf(1, 0);
- }
- break; // got character, break for loop
- } else {
- keylen = mp_match_len;
- }
+ if (result == map_result_fail) {
+ // failed, use the outer loop
+ c = -1;
+ break;
}
- // complete match
- if (keylen >= 0 && keylen <= typebuf.tb_len) {
- int save_m_expr;
- int save_m_noremap;
- int save_m_silent;
-
- // Write chars to script file(s)
- // Note: :lmap mappings are written *after* being applied. #5658
- if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) {
- gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
- (size_t)(keylen - typebuf.tb_maplen));
- }
-
- cmd_silent = (typebuf.tb_silent > 0);
- del_typebuf(keylen, 0); // remove the mapped keys
-
- /*
- * Put the replacement string in front of mapstr.
- * The depth check catches ":map x y" and ":map y x".
- */
- if (++mapdepth >= p_mmd) {
- emsg(_("E223: recursive mapping"));
- if (State & CMDLINE) {
- redrawcmdline();
+ if (result == map_result_get) {
+ // get a character: 2. from the typeahead buffer
+ c = typebuf.tb_buf[typebuf.tb_off] & 255;
+ if (advance) { // remove chars from tb_buf
+ cmd_silent = (typebuf.tb_silent > 0);
+ if (typebuf.tb_maplen > 0) {
+ KeyTyped = false;
} else {
- setcursor();
+ KeyTyped = true;
+ // write char to script file(s)
+ gotchars(typebuf.tb_buf + typebuf.tb_off, 1);
}
- flush_buffers(FLUSH_MINIMAL);
- mapdepth = 0; // for next one
- c = -1;
- break;
- }
-
- /*
- * In Select mode and a Visual mode mapping is used:
- * Switch to Visual mode temporarily. Append K_SELECT
- * to switch back to Select mode.
- */
- if (VIsual_active && VIsual_select
- && (mp->m_mode & VISUAL)) {
- VIsual_select = false;
- (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false);
- }
-
- /* Copy the values from *mp that are used, because
- * evaluating the expression may invoke a function
- * that redefines the mapping, thereby making *mp
- * invalid. */
- save_m_expr = mp->m_expr;
- save_m_noremap = mp->m_noremap;
- save_m_silent = mp->m_silent;
- char_u *save_m_keys = NULL; // only saved when needed
- char_u *save_m_str = NULL; // only saved when needed
-
- /*
- * Handle ":map <expr>": evaluate the {rhs} as an
- * expression. Also save and restore the command line
- * for "normal :".
- */
- if (mp->m_expr) {
- int save_vgetc_busy = vgetc_busy;
- const bool save_may_garbage_collect = may_garbage_collect;
-
- vgetc_busy = 0;
- may_garbage_collect = false;
-
- save_m_keys = vim_strsave(mp->m_keys);
- save_m_str = vim_strsave(mp->m_str);
- s = eval_map_expr(save_m_str, NUL);
- vgetc_busy = save_vgetc_busy;
- may_garbage_collect = save_may_garbage_collect;
- } else {
- s = mp->m_str;
+ KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
+ del_typebuf(1, 0);
}
-
- /*
- * Insert the 'to' part in the typebuf.tb_buf.
- * If 'from' field is the same as the start of the
- * 'to' field, don't remap the first character (but do
- * allow abbreviations).
- * If m_noremap is set, don't remap the whole 'to'
- * part.
- */
- if (s == NULL) {
- i = FAIL;
- } else {
- int noremap;
-
- // If this is a LANGMAP mapping, then we didn't record the keys
- // at the start of the function and have to record them now.
- if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) {
- gotchars(s, STRLEN(s));
- }
-
- if (save_m_noremap != REMAP_YES) {
- noremap = save_m_noremap;
- } else if (
- STRNCMP(s, save_m_keys != NULL
- ? save_m_keys : mp->m_keys,
- (size_t)keylen)
- != 0) {
- noremap = REMAP_YES;
- } else {
- noremap = REMAP_SKIP;
- }
- i = ins_typebuf(s, noremap,
- 0, TRUE, cmd_silent || save_m_silent);
- if (save_m_expr) {
- xfree(s);
- }
- }
- xfree(save_m_keys);
- xfree(save_m_str);
- if (i == FAIL) {
- c = -1;
- break;
- }
- continue;
+ break;
}
+
+ // not enough characters, get more
}
- /*
- * get a character: 3. from the user - handle <Esc> in Insert mode
- */
- /*
- * special case: if we get an <ESC> in insert mode and there
- * are no more characters at once, we pretend to go out of
- * insert mode. This prevents the one second delay after
- * typing an <ESC>. If we get something after all, we may
- * have to redisplay the mode. That the cursor is in the wrong
- * place does not matter.
- */
+ // get a character: 3. from the user - handle <Esc> in Insert mode
+
+ // special case: if we get an <ESC> in insert mode and there
+ // are no more characters at once, we pretend to go out of
+ // insert mode. This prevents the one second delay after
+ // typing an <ESC>. If we get something after all, we may
+ // have to redisplay the mode. That the cursor is in the wrong
+ // place does not matter.
c = 0;
new_wcol = curwin->w_wcol;
new_wrow = curwin->w_wrow;
@@ -2196,10 +2181,8 @@ static int vgetorpeek(bool advance)
&& ex_normal_busy == 0
&& typebuf.tb_maplen == 0
&& (State & INSERT)
- && (p_timeout
- || (keylen == KEYLEN_PART_KEY && p_ttimeout))
- && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len,
- 3, 25L)) == 0) {
+ && (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout))
+ && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) {
colnr_T col = 0, vcol;
char_u *ptr;
@@ -2215,11 +2198,9 @@ static int vgetorpeek(bool advance)
if (curwin->w_cursor.col != 0) {
if (curwin->w_wcol > 0) {
if (did_ai) {
- /*
- * We are expecting to truncate the trailing
- * white-space, so find the last non-white
- * character -- webb
- */
+ // We are expecting to truncate the trailing
+ // white-space, so find the last non-white
+ // character -- webb
col = vcol = curwin->w_wcol = 0;
ptr = get_cursor_line_ptr();
while (col < curwin->w_cursor.col) {
@@ -2233,7 +2214,7 @@ static int vgetorpeek(bool advance)
+ curwin->w_wcol / curwin->w_width_inner;
curwin->w_wcol %= curwin->w_width_inner;
curwin->w_wcol += curwin_col_off();
- col = 0; // no correction needed
+ col = 0; // no correction needed
} else {
--curwin->w_wcol;
col = curwin->w_cursor.col - 1;
@@ -2261,7 +2242,7 @@ static int vgetorpeek(bool advance)
curwin->w_wrow = old_wrow;
}
if (c < 0) {
- continue; // end of input script reached
+ continue; // end of input script reached
}
// Allow mapping for just typed characters. When we get here c
@@ -2307,9 +2288,8 @@ static int vgetorpeek(bool advance)
break;
}
- /*
- * get a character: 3. from the user - update display
- */
+ // get a character: 3. from the user - update display
+
// In insert mode a screen update is skipped when characters
// are still available. But when those available characters
// are part of a mapping, and we are going to do a blocking
@@ -2320,26 +2300,21 @@ static int vgetorpeek(bool advance)
if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0
&& advance && must_redraw != 0 && !need_wait_return) {
update_screen(0);
- setcursor(); // put cursor back where it belongs
+ setcursor(); // put cursor back where it belongs
}
- /*
- * If we have a partial match (and are going to wait for more
- * input from the user), show the partially matched characters
- * to the user with showcmd.
- */
- i = 0;
+ // If we have a partial match (and are going to wait for more
+ // input from the user), show the partially matched characters
+ // to the user with showcmd.
+ int showcmd_idx = 0;
c1 = 0;
if (typebuf.tb_len > 0 && advance && !exmode_active) {
- if (((State & (NORMAL | INSERT)) || State == LANGMAP)
- && State != HITRETURN) {
+ if (((State & (NORMAL | INSERT)) || State == LANGMAP) && State != HITRETURN) {
// this looks nice when typing a dead character map
if (State & INSERT
- && ptr2cells(typebuf.tb_buf + typebuf.tb_off
- + typebuf.tb_len - 1) == 1) {
- edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1],
- false);
- setcursor(); // put cursor back where it belongs
+ && ptr2cells(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) {
+ edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], false);
+ setcursor(); // put cursor back where it belongs
c1 = 1;
}
// need to use the col and row from above here
@@ -2349,11 +2324,10 @@ static int vgetorpeek(bool advance)
curwin->w_wrow = new_wrow;
push_showcmd();
if (typebuf.tb_len > SHOWCMD_COLS) {
- i = typebuf.tb_len - SHOWCMD_COLS;
+ showcmd_idx = typebuf.tb_len - SHOWCMD_COLS;
}
- while (i < typebuf.tb_len) {
- (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off
- + i++]);
+ while (showcmd_idx < typebuf.tb_len) {
+ (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]);
}
curwin->w_wcol = old_wcol;
curwin->w_wrow = old_wrow;
@@ -2369,9 +2343,7 @@ static int vgetorpeek(bool advance)
}
}
- /*
- * get a character: 3. from the user - get it
- */
+ // get a character: 3. from the user - get it
if (typebuf.tb_len == 0) {
// timedout may have been set while waiting for a mapping
// that has a <Nop> RHS.
@@ -2381,8 +2353,7 @@ static int vgetorpeek(bool advance)
long wait_time = 0;
if (advance) {
- if (typebuf.tb_len == 0
- || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) {
+ if (typebuf.tb_len == 0 || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) {
// blocking wait
wait_time = -1L;
} else if (keylen == KEYLEN_PART_KEY && p_ttm >= 0) {
@@ -2397,7 +2368,7 @@ static int vgetorpeek(bool advance)
typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1,
wait_time);
- if (i != 0) {
+ if (showcmd_idx != 0) {
pop_showcmd();
}
if (c1 == 1) {
@@ -2407,47 +2378,45 @@ static int vgetorpeek(bool advance)
if (State & CMDLINE) {
unputcmdline();
} else {
- setcursor(); // put cursor back where it belongs
+ setcursor(); // put cursor back where it belongs
}
}
if (c < 0) {
- continue; // end of input script reached
+ continue; // end of input script reached
}
- if (c == NUL) { // no character available
+ if (c == NUL) { // no character available
if (!advance) {
break;
}
- if (wait_tb_len > 0) { // timed out
+ if (wait_tb_len > 0) { // timed out
timedout = true;
continue;
}
- } else { // allow mapping for just typed characters
+ } else { // allow mapping for just typed characters
while (typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] != NUL) {
typebuf.tb_noremap[typebuf.tb_off + typebuf.tb_len++] = RM_YES;
}
}
- } // for (;;)
- } // if (!character from stuffbuf)
+ } // for (;;)
+ } // if (!character from stuffbuf)
// if advance is false don't loop on NULs
} while (c < 0 || (advance && c == NUL));
- /*
- * The "INSERT" message is taken care of here:
- * if we return an ESC to exit insert mode, the message is deleted
- * if we don't return an ESC but deleted the message before, redisplay it
- */
+ // The "INSERT" message is taken care of here:
+ // if we return an ESC to exit insert mode, the message is deleted
+ // if we don't return an ESC but deleted the message before, redisplay it
if (advance && p_smd && msg_silent == 0 && (State & INSERT)) {
if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) {
if (typebuf.tb_len && !KeyTyped) {
- redraw_cmdline = true; // delete mode later
+ redraw_cmdline = true; // delete mode later
} else {
unshowmode(false);
}
} else if (c != ESC && mode_deleted) {
if (typebuf.tb_len && !KeyTyped) {
- redraw_cmdline = true; // show mode later
+ redraw_cmdline = true; // show mode later
} else {
showmode();
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index b5e1fda9f1..b2422fd531 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -4,6 +4,7 @@
#include <inttypes.h>
#include <stdbool.h>
+#include "nvim/ascii.h"
#include "nvim/event/loop.h"
#include "nvim/ex_eval.h"
#include "nvim/iconv.h"
@@ -533,6 +534,11 @@ EXTERN int VIsual_mode INIT(= 'v');
/// true when redoing Visual.
EXTERN int redo_VIsual_busy INIT(= false);
+// The Visual area is remembered for reselection.
+EXTERN int resel_VIsual_mode INIT(= NUL); // 'v', 'V', or Ctrl-V
+EXTERN linenr_T resel_VIsual_line_count; // number of lines
+EXTERN colnr_T resel_VIsual_vcol; // nr of cols or end col
+
/// When pasting text with the middle mouse button in visual mode with
/// restart_edit set, remember where it started so we can set Insstart.
EXTERN pos_T where_paste_started;
@@ -727,6 +733,7 @@ EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or
// :bufdo is executing
EXTERN bool need_start_insertmode INIT(= false);
// start insert mode soon
+EXTERN char *last_mode INIT(= NULL);
EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 93fcdc55a6..6fc70144ac 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -1253,7 +1253,7 @@ static struct prt_dsc_comment_S prt_dsc_table[] =
* Variables for the output PostScript file.
*/
static FILE *prt_ps_fd;
-static int prt_file_error;
+static bool prt_file_error;
static char_u *prt_ps_file_name = NULL;
/*
@@ -1329,7 +1329,7 @@ static void prt_write_file_raw_len(char_u *buffer, size_t bytes)
if (!prt_file_error
&& fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd) != bytes) {
emsg(_("E455: Error writing to PostScript output file"));
- prt_file_error = TRUE;
+ prt_file_error = true;
}
}
@@ -1981,7 +1981,7 @@ void mch_print_cleanup(void)
if (prt_ps_fd != NULL) {
fclose(prt_ps_fd);
prt_ps_fd = NULL;
- prt_file_error = FALSE;
+ prt_file_error = false;
}
if (prt_ps_file_name != NULL) {
XFREE_CLEAR(prt_ps_file_name);
@@ -2203,7 +2203,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
// Check encoding and character set are compatible
if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0) {
emsg(_("E673: Incompatible multi-byte encoding and character set."));
- return FALSE;
+ return false;
}
// Add charset name if not empty
@@ -2215,7 +2215,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
// Add custom CMap character set name
if (*p_pmcs == NUL) {
emsg(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
- return FALSE;
+ return false;
}
STRLCPY(prt_cmap, p_pmcs, sizeof(prt_cmap) - 2);
STRCAT(prt_cmap, "-");
@@ -2231,7 +2231,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
if (!mbfont_opts[OPT_MBFONT_REGULAR].present) {
emsg(_("E675: No default font specified for multi-byte printing."));
- return FALSE;
+ return false;
}
// Derive CID font names with fallbacks if not defined
@@ -2425,12 +2425,12 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
prt_need_bgcol = false;
prt_need_underline = false;
- prt_file_error = FALSE;
+ prt_file_error = false;
return OK;
}
-static int prt_add_resource(struct prt_ps_resource_S *resource)
+static bool prt_add_resource(struct prt_ps_resource_S *resource)
{
FILE *fd_resource;
char_u resource_buffer[512];
@@ -2439,7 +2439,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource)
fd_resource = os_fopen((char *)resource->filename, READBIN);
if (fd_resource == NULL) {
semsg(_("E456: Can't open file \"%s\""), resource->filename);
- return FALSE;
+ return false;
}
switch (resource->type) {
case PRT_RESOURCE_TYPE_PROCSET:
@@ -2449,7 +2449,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource)
(char *)resource->title);
break;
default:
- return FALSE;
+ return false;
}
prt_dsc_textline("BeginDocument", (char *)resource->filename);
@@ -2461,7 +2461,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource)
semsg(_("E457: Can't read PostScript resource file \"%s\""),
resource->filename);
fclose(fd_resource);
- return FALSE;
+ return false;
}
if (bytes_read == 0) {
break;
@@ -2469,7 +2469,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource)
prt_write_file_raw_len(resource_buffer, bytes_read);
if (prt_file_error) {
fclose(fd_resource);
- return FALSE;
+ return false;
}
}
fclose(fd_resource);
@@ -2478,10 +2478,10 @@ static int prt_add_resource(struct prt_ps_resource_S *resource)
prt_dsc_noarg("EndResource");
- return TRUE;
+ return true;
}
-int mch_print_begin(prt_settings_T *psettings)
+bool mch_print_begin(prt_settings_T *psettings)
{
int bbox[4];
double left;
@@ -2495,7 +2495,6 @@ int mch_print_begin(prt_settings_T *psettings)
char_u *p;
struct prt_ps_resource_S res_cidfont;
struct prt_ps_resource_S res_cmap;
- int retval = FALSE;
/*
* PS DSC Header comments - no PS code!
@@ -2567,25 +2566,25 @@ int mch_print_begin(prt_settings_T *psettings)
// Search for external resources VIM supplies
if (!prt_find_resource("prolog", &res_prolog)) {
emsg(_("E456: Can't find PostScript resource file \"prolog.ps\""));
- return FALSE;
+ return false;
}
if (!prt_open_resource(&res_prolog)) {
- return FALSE;
+ return false;
}
if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION)) {
- return FALSE;
+ return false;
}
if (prt_out_mbyte) {
// Look for required version of multi-byte printing procset
if (!prt_find_resource("cidfont", &res_cidfont)) {
emsg(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
- return FALSE;
+ return false;
}
if (!prt_open_resource(&res_cidfont)) {
- return FALSE;
+ return false;
}
if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION)) {
- return FALSE;
+ return false;
}
}
@@ -2610,12 +2609,12 @@ int mch_print_begin(prt_settings_T *psettings)
if (!prt_find_resource(p_encoding, &res_encoding)) {
semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
p_encoding);
- return FALSE;
+ return false;
}
}
}
if (!prt_open_resource(&res_encoding)) {
- return FALSE;
+ return false;
}
// For the moment there are no checks on encoding resource files to
// perform
@@ -2629,10 +2628,10 @@ int mch_print_begin(prt_settings_T *psettings)
if (!prt_find_resource(prt_ascii_encoding, &res_encoding)) {
semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
prt_ascii_encoding);
- return FALSE;
+ return false;
}
if (!prt_open_resource(&res_encoding)) {
- return FALSE;
+ return false;
}
// For the moment there are no checks on encoding resource files to
// perform
@@ -2655,10 +2654,10 @@ int mch_print_begin(prt_settings_T *psettings)
if (!prt_find_resource(prt_cmap, &res_cmap)) {
semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
prt_cmap);
- return FALSE;
+ return false;
}
if (!prt_open_resource(&res_cmap)) {
- return FALSE;
+ return false;
}
}
@@ -2736,7 +2735,7 @@ int mch_print_begin(prt_settings_T *psettings)
// There will be only one Roman font encoding to be included in the PS
// file.
if (!prt_add_resource(&res_encoding)) {
- return FALSE;
+ return false;
}
}
@@ -2846,9 +2845,7 @@ int mch_print_begin(prt_settings_T *psettings)
prt_dsc_noarg("EndSetup");
// Fail if any problems writing out to the PS file
- retval = !prt_file_error;
-
- return retval;
+ return !prt_file_error;
}
void mch_print_end(prt_settings_T *psettings)
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 6f02ebfb48..daef8db267 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -223,9 +223,9 @@ void ex_cstag(exarg_T *eap)
switch (p_csto) {
case 0:
if (cs_check_for_connections()) {
- ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE,
- FALSE, *eap->cmdlinep);
- if (ret == FALSE) {
+ ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false,
+ false, *eap->cmdlinep);
+ if (ret == false) {
cs_free_tags();
if (msg_col) {
msg_putchar('\n');
@@ -249,16 +249,16 @@ void ex_cstag(exarg_T *eap)
if (cs_check_for_connections()) {
ret = cs_find_common("g", (char *)(eap->arg), eap->forceit,
- FALSE, FALSE, *eap->cmdlinep);
- if (ret == FALSE) {
+ false, false, *eap->cmdlinep);
+ if (ret == false) {
cs_free_tags();
}
}
}
} else if (cs_check_for_connections()) {
- ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE,
- FALSE, *eap->cmdlinep);
- if (ret == FALSE) {
+ ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false,
+ false, *eap->cmdlinep);
+ if (ret == false) {
cs_free_tags();
}
}
@@ -520,7 +520,7 @@ add_err:
}
-static int cs_check_for_connections(void)
+static bool cs_check_for_connections(void)
{
return cs_cnt_connections() > 0;
}
@@ -887,20 +887,20 @@ static int cs_find(exarg_T *eap)
{
char *opt, *pat;
- if (cs_check_for_connections() == FALSE) {
+ if (cs_check_for_connections() == false) {
(void)emsg(_("E567: no cscope connections"));
- return FALSE;
+ return false;
}
if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL) {
cs_usage_msg(Find);
- return FALSE;
+ return false;
}
pat = opt + strlen(opt) + 1;
if (pat >= (char *)eap->arg + eap_arg_len) {
cs_usage_msg(Find);
- return FALSE;
+ return false;
}
/*
@@ -919,8 +919,8 @@ static int cs_find(exarg_T *eap)
/// Common code for cscope find, shared by cs_find() and ex_cstag().
-static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int use_ll,
- char_u *cmdline)
+static bool cs_find_common(char *opt, char *pat, int forceit, int verbose,
+ bool use_ll, char_u *cmdline)
{
char *cmd;
int *nummatches;
@@ -967,7 +967,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us
// next symbol must be + or -
if (strchr(CSQF_FLAGS, *qfpos) == NULL) {
(void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1));
- return FALSE;
+ return false;
}
if (*qfpos != '0'
@@ -982,7 +982,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us
// create the actual command to send to cscope
cmd = cs_create_cmd(opt, pat);
if (cmd == NULL) {
- return FALSE;
+ return false;
}
nummatches = xmalloc(sizeof(int) * csinfo_size);
@@ -1019,7 +1019,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us
(void)semsg(_("E259: no matches found for cscope query %s of %s"), opt, pat);
}
xfree(nummatches);
- return FALSE;
+ return false;
}
if (qfpos != NULL && *qfpos != '0') {
@@ -1064,7 +1064,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us
os_remove((char *)tmp);
xfree(tmp);
xfree(nummatches);
- return TRUE;
+ return true;
} else {
char **matches = NULL, **contexts = NULL;
size_t matched = 0;
@@ -1073,7 +1073,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us
cs_fill_results(pat, totmatches, nummatches, &matches, &contexts, &matched);
xfree(nummatches);
if (matches == NULL) {
- return FALSE;
+ return false;
}
(void)cs_manage_matches(matches, contexts, matched, Store);
@@ -1499,12 +1499,13 @@ static void cs_file_results(FILE *f, int *nummatches_a)
continue;
}
- context = xmalloc(strlen(cntx) + 5);
+ size_t context_len = strlen(cntx) + 5;
+ context = xmalloc(context_len);
if (strcmp(cntx, "<global>") == 0) {
- strcpy(context, "<<global>>");
+ xstrlcpy(context, "<<global>>", context_len);
} else {
- sprintf(context, "<<%s>>", cntx);
+ snprintf(context, context_len, "<<%s>>", cntx);
}
if (search == NULL) {
diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h
index 8cfeb03cc4..e81db43038 100644
--- a/src/nvim/lib/khash.h
+++ b/src/nvim/lib/khash.h
@@ -666,7 +666,7 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key)
} \
}
-// More conenient interfaces
+// More convenient interfaces
/*! @function
@abstract Instantiate a hash set containing integer keys
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 9f2372f831..b6792a5a97 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -782,10 +782,10 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special)
break;
}
#define ADD_TYPE(type, data_key) \
-case kObjectType##type: { \
- nlua_push_##type(lstate, obj.data.data_key, special); \
- break; \
-}
+ case kObjectType##type: { \
+ nlua_push_##type(lstate, obj.data.data_key, special); \
+ break; \
+ }
ADD_TYPE(Boolean, boolean)
ADD_TYPE(Integer, integer)
ADD_TYPE(Float, floating)
@@ -794,10 +794,10 @@ case kObjectType##type: { \
ADD_TYPE(Dictionary, dictionary)
#undef ADD_TYPE
#define ADD_REMOTE_TYPE(type) \
-case kObjectType##type: { \
- nlua_push_##type(lstate, (type)obj.data.integer, special); \
- break; \
-}
+ case kObjectType##type: { \
+ nlua_push_##type(lstate, (type)obj.data.integer, special); \
+ break; \
+ }
ADD_REMOTE_TYPE(Buffer)
ADD_REMOTE_TYPE(Window)
ADD_REMOTE_TYPE(Tabpage)
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index db79e9e7e9..b5553060a1 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -175,13 +175,13 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
size_t s1_len;
const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
intptr_t idx;
- if (lua_gettop(lstate) >= 2) {
+ if (lua_isnoneornil(lstate, 2)) {
+ idx = (intptr_t)s1_len;
+ } else {
idx = luaL_checkinteger(lstate, 2);
if (idx < 0 || idx > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- } else {
- idx = (intptr_t)s1_len;
}
size_t codepoints = 0, codeunits = 0;
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index ad886b40dd..c1a1e7f162 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -454,7 +454,7 @@ local on_key_cbs = {}
--- On each key press, Nvim passes the key char to fn(). |i_CTRL-V|
--- If {fn} is nil, it removes the callback for the associated {ns_id}
---@param ns_id number? Namespace ID. If nil or 0, generates and returns a new
---- |nvim_create_namesapce()| id.
+--- |nvim_create_namespace()| id.
---
---@return number Namespace id associated with {fn}. Or count of all callbacks
---if on_key() is called without arguments.
@@ -580,6 +580,7 @@ function vim._expand_pat(pat, env)
end
local keys = {}
+ ---@private
local function insert_keys(obj)
for k,_ in pairs(obj) do
if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 559a3cc435..3d621ebbb7 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -631,6 +631,7 @@ void free_all_mem(void)
clear_sb_text(true); // free any scrollback text
// Free some global vars.
+ xfree(last_mode);
xfree(last_cmdline);
xfree(new_last_cmdline);
set_keep_msg(NULL, 0);
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 6fcd4cef8a..8a6ac2decc 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -839,13 +839,11 @@ void msg_schedule_semsg(const char *const fmt, ...)
multiqueue_put(main_loop.events, msg_semsg_event, 1, s);
}
-/*
- * Like msg(), but truncate to a single line if p_shm contains 't', or when
- * "force" is TRUE. This truncates in another way as for normal messages.
- * Careful: The string may be changed by msg_may_trunc()!
- * Returns a pointer to the printed message, if wait_return() not called.
- */
-char *msg_trunc_attr(char *s, int force, int attr)
+// Like msg(), but truncate to a single line if p_shm contains 't', or when
+// "force" is true. This truncates in another way as for normal messages.
+// Careful: The string may be changed by msg_may_trunc()!
+// Returns a pointer to the printed message, if wait_return() not called.
+char *msg_trunc_attr(char *s, bool force, int attr)
{
int n;
@@ -869,7 +867,7 @@ char *msg_trunc_attr(char *s, int force, int attr)
* Return a pointer to where the truncated message starts.
* Note: May change the message by replacing a character with '<'.
*/
-char_u *msg_may_trunc(int force, char_u *s)
+char_u *msg_may_trunc(bool force, char_u *s)
{
int room;
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index fd5d154cea..872a2c58e3 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -1059,3 +1059,58 @@ void add_time(char_u *buf, size_t buflen, time_t tt)
seconds);
}
}
+
+dict_T *get_v_event(save_v_event_T *sve)
+{
+ dict_T *v_event = get_vim_var_dict(VV_EVENT);
+
+ if (v_event->dv_hashtab.ht_used > 0) {
+ // recursive use of v:event, save, make empty and restore later
+ sve->sve_did_save = true;
+ sve->sve_hashtab = v_event->dv_hashtab;
+ hash_init(&v_event->dv_hashtab);
+ } else {
+ sve->sve_did_save = false;
+ }
+ return v_event;
+}
+
+void restore_v_event(dict_T *v_event, save_v_event_T *sve)
+{
+ tv_dict_free_contents(v_event);
+ if (sve->sve_did_save) {
+ v_event->dv_hashtab = sve->sve_hashtab;
+ } else {
+ hash_init(&v_event->dv_hashtab);
+ }
+}
+
+/// Fires a ModeChanged autocmd.
+void trigger_modechanged(void)
+{
+ if (!has_event(EVENT_MODECHANGED)) {
+ return;
+ }
+
+ char *mode = get_mode();
+ if (STRCMP(mode, last_mode) == 0) {
+ xfree(mode);
+ return;
+ }
+
+ save_v_event_T save_v_event;
+ dict_T *v_event = get_v_event(&save_v_event);
+ tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
+ tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
+
+ char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":");
+ char_u *pat = concat_str(pat_pre, (char_u *)mode);
+ xfree(pat_pre);
+
+ apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf);
+ xfree(last_mode);
+ last_mode = mode;
+
+ xfree(pat);
+ restore_v_event(v_event, &save_v_event);
+}
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index f805858904..32014fcf2b 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -92,15 +92,15 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
break;
}
#define STR_CASE(type, attr, obj, dest, conv) \
-case type: { \
- dest = conv(((String) { \
+ case type: { \
+ dest = conv(((String) { \
.size = obj->via.attr.size, \
.data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \
? xmemdupz("", 0) \
: xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), \
})); \
- break; \
-}
+ break; \
+ }
STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ)
STR_CASE(MSGPACK_OBJECT_BIN, bin, cur.mobj, *cur.aobj, STRING_OBJ)
case MSGPACK_OBJECT_ARRAY: {
@@ -143,10 +143,10 @@ case type: { \
const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key;
switch (key->type) {
#define ID(x) x
- STR_CASE(MSGPACK_OBJECT_STR, str, key,
- cur.aobj->data.dictionary.items[idx].key, ID)
- STR_CASE(MSGPACK_OBJECT_BIN, bin, key,
- cur.aobj->data.dictionary.items[idx].key, ID)
+ STR_CASE(MSGPACK_OBJECT_STR, str, key,
+ cur.aobj->data.dictionary.items[idx].key, ID)
+ STR_CASE(MSGPACK_OBJECT_BIN, bin, key,
+ cur.aobj->data.dictionary.items[idx].key, ID)
#undef ID
case MSGPACK_OBJECT_NIL:
case MSGPACK_OBJECT_BOOLEAN:
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 03312e5df4..9332c55b5f 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -31,8 +31,8 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/indent.h"
-#include "nvim/indent_c.h"
#include "nvim/keymap.h"
#include "nvim/log.h"
#include "nvim/main.h"
@@ -84,12 +84,6 @@ typedef struct normal_state {
pos_T old_pos;
} NormalState;
-/*
- * The Visual area is remembered for reselection.
- */
-static int resel_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
-static linenr_T resel_VIsual_line_count; // number of lines
-static colnr_T resel_VIsual_vcol; // nr of cols or end col
static int VIsual_mode_orig = NUL; // saved Visual mode
@@ -487,6 +481,7 @@ static void normal_prepare(NormalState *s)
if (finish_op != c) {
ui_cursor_shape(); // may show different cursor shape
}
+ trigger_modechanged();
// When not finishing an operator and no register name typed, reset the count.
if (!finish_op && !s->oa.regname) {
@@ -928,6 +923,7 @@ normal_end:
// Reset finish_op, in case it was set
s->c = finish_op;
finish_op = false;
+ trigger_modechanged();
// Redraw the cursor with another shape, if we were in Operator-pending
// mode or did a replace command.
if (s->c || s->ca.cmdchar == 'r') {
@@ -965,6 +961,7 @@ normal_end:
&& s->oa.regname == 0) {
if (restart_VIsual_select == 1) {
VIsual_select = true;
+ trigger_modechanged();
showmode();
restart_VIsual_select = 0;
}
@@ -1452,758 +1449,6 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
*set_prevcount = false; // only set v:prevcount once
}
-// Handle an operator after Visual mode or when the movement is finished.
-// "gui_yank" is true when yanking text for the clipboard.
-void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
-{
- oparg_T *oap = cap->oap;
- pos_T old_cursor;
- bool empty_region_error;
- int restart_edit_save;
- int lbr_saved = curwin->w_p_lbr;
-
-
- // The visual area is remembered for redo
- static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
- static linenr_T redo_VIsual_line_count; // number of lines
- static colnr_T redo_VIsual_vcol; // number of cols or end column
- static long redo_VIsual_count; // count for Visual operator
- static int redo_VIsual_arg; // extra argument
- bool include_line_break = false;
-
- old_cursor = curwin->w_cursor;
-
- /*
- * If an operation is pending, handle it...
- */
- if ((finish_op
- || VIsual_active)
- && oap->op_type != OP_NOP) {
- // Yank can be redone when 'y' is in 'cpoptions', but not when yanking
- // for the clipboard.
- const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank;
-
- // Avoid a problem with unwanted linebreaks in block mode
- if (curwin->w_p_lbr) {
- curwin->w_valid &= ~VALID_VIRTCOL;
- }
- curwin->w_p_lbr = false;
- oap->is_VIsual = VIsual_active;
- if (oap->motion_force == 'V') {
- oap->motion_type = kMTLineWise;
- } else if (oap->motion_force == 'v') {
- // If the motion was linewise, "inclusive" will not have been set.
- // Use "exclusive" to be consistent. Makes "dvj" work nice.
- if (oap->motion_type == kMTLineWise) {
- oap->inclusive = false;
- } else if (oap->motion_type == kMTCharWise) {
- // If the motion already was charwise, toggle "inclusive"
- oap->inclusive = !oap->inclusive;
- }
- oap->motion_type = kMTCharWise;
- } else if (oap->motion_force == Ctrl_V) {
- // Change line- or charwise motion into Visual block mode.
- if (!VIsual_active) {
- VIsual_active = true;
- VIsual = oap->start;
- }
- VIsual_mode = Ctrl_V;
- VIsual_select = false;
- VIsual_reselect = false;
- }
-
- // Only redo yank when 'y' flag is in 'cpoptions'.
- // Never redo "zf" (define fold).
- if ((redo_yank || oap->op_type != OP_YANK)
- && ((!VIsual_active || oap->motion_force)
- // Also redo Operator-pending Visual mode mappings.
- || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND)
- && oap->op_type != OP_COLON))
- && cap->cmdchar != 'D'
- && oap->op_type != OP_FOLD
- && oap->op_type != OP_FOLDOPEN
- && oap->op_type != OP_FOLDOPENREC
- && oap->op_type != OP_FOLDCLOSE
- && oap->op_type != OP_FOLDCLOSEREC
- && oap->op_type != OP_FOLDDEL
- && oap->op_type != OP_FOLDDELREC) {
- prep_redo(oap->regname, cap->count0,
- get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
- oap->motion_force, cap->cmdchar, cap->nchar);
- if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search
- /*
- * If 'cpoptions' does not contain 'r', insert the search
- * pattern to really repeat the same command.
- */
- if (vim_strchr(p_cpo, CPO_REDO) == NULL) {
- AppendToRedobuffLit(cap->searchbuf, -1);
- }
- AppendToRedobuff(NL_STR);
- } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) {
- // do_cmdline() has stored the first typed line in
- // "repeat_cmdline". When several lines are typed repeating
- // won't be possible.
- if (repeat_cmdline == NULL) {
- ResetRedobuff();
- } else {
- AppendToRedobuffLit(repeat_cmdline, -1);
- AppendToRedobuff(NL_STR);
- XFREE_CLEAR(repeat_cmdline);
- }
- }
- }
-
- if (redo_VIsual_busy) {
- /* Redo of an operation on a Visual area. Use the same size from
- * redo_VIsual_line_count and redo_VIsual_vcol. */
- oap->start = curwin->w_cursor;
- curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- }
- VIsual_mode = redo_VIsual_mode;
- if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') {
- if (VIsual_mode == 'v') {
- if (redo_VIsual_line_count <= 1) {
- validate_virtcol();
- curwin->w_curswant =
- curwin->w_virtcol + redo_VIsual_vcol - 1;
- } else {
- curwin->w_curswant = redo_VIsual_vcol;
- }
- } else {
- curwin->w_curswant = MAXCOL;
- }
- coladvance(curwin->w_curswant);
- }
- cap->count0 = redo_VIsual_count;
- cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
- } else if (VIsual_active) {
- if (!gui_yank) {
- // Save the current VIsual area for '< and '> marks, and "gv"
- curbuf->b_visual.vi_start = VIsual;
- curbuf->b_visual.vi_end = curwin->w_cursor;
- curbuf->b_visual.vi_mode = VIsual_mode;
- if (VIsual_mode_orig != NUL) {
- curbuf->b_visual.vi_mode = VIsual_mode_orig;
- VIsual_mode_orig = NUL;
- }
- curbuf->b_visual.vi_curswant = curwin->w_curswant;
- curbuf->b_visual_mode_eval = VIsual_mode;
- }
-
- // In Select mode, a linewise selection is operated upon like a
- // charwise selection.
- // Special case: gH<Del> deletes the last line.
- if (VIsual_select && VIsual_mode == 'V'
- && cap->oap->op_type != OP_DELETE) {
- if (lt(VIsual, curwin->w_cursor)) {
- VIsual.col = 0;
- curwin->w_cursor.col =
- (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum));
- } else {
- curwin->w_cursor.col = 0;
- VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum));
- }
- VIsual_mode = 'v';
- }
- /* If 'selection' is "exclusive", backup one character for
- * charwise selections. */
- else if (VIsual_mode == 'v') {
- include_line_break =
- unadjust_for_sel();
- }
-
- oap->start = VIsual;
- if (VIsual_mode == 'V') {
- oap->start.col = 0;
- oap->start.coladd = 0;
- }
- }
-
- /*
- * Set oap->start to the first position of the operated text, oap->end
- * to the end of the operated text. w_cursor is equal to oap->start.
- */
- if (lt(oap->start, curwin->w_cursor)) {
- // Include folded lines completely.
- if (!VIsual_active) {
- if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) {
- oap->start.col = 0;
- }
- if ((curwin->w_cursor.col > 0
- || oap->inclusive
- || oap->motion_type == kMTLineWise)
- && hasFolding(curwin->w_cursor.lnum, NULL,
- &curwin->w_cursor.lnum)) {
- curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr());
- }
- }
- oap->end = curwin->w_cursor;
- curwin->w_cursor = oap->start;
-
- /* w_virtcol may have been updated; if the cursor goes back to its
- * previous position w_virtcol becomes invalid and isn't updated
- * automatically. */
- curwin->w_valid &= ~VALID_VIRTCOL;
- } else {
- // Include folded lines completely.
- if (!VIsual_active && oap->motion_type == kMTLineWise) {
- if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum,
- NULL)) {
- curwin->w_cursor.col = 0;
- }
- if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) {
- oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum));
- }
- }
- oap->end = oap->start;
- oap->start = curwin->w_cursor;
- }
-
- // Just in case lines were deleted that make the position invalid.
- check_pos(curwin->w_buffer, &oap->end);
- oap->line_count = oap->end.lnum - oap->start.lnum + 1;
-
- // Set "virtual_op" before resetting VIsual_active.
- virtual_op = virtual_active();
-
- if (VIsual_active || redo_VIsual_busy) {
- get_op_vcol(oap, redo_VIsual_vcol, true);
-
- if (!redo_VIsual_busy && !gui_yank) {
- /*
- * Prepare to reselect and redo Visual: this is based on the
- * size of the Visual text
- */
- resel_VIsual_mode = VIsual_mode;
- if (curwin->w_curswant == MAXCOL) {
- resel_VIsual_vcol = MAXCOL;
- } else {
- if (VIsual_mode != Ctrl_V) {
- getvvcol(curwin, &(oap->end),
- NULL, NULL, &oap->end_vcol);
- }
- if (VIsual_mode == Ctrl_V || oap->line_count <= 1) {
- if (VIsual_mode != Ctrl_V) {
- getvvcol(curwin, &(oap->start),
- &oap->start_vcol, NULL, NULL);
- }
- resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1;
- } else {
- resel_VIsual_vcol = oap->end_vcol;
- }
- }
- resel_VIsual_line_count = oap->line_count;
- }
-
- // can't redo yank (unless 'y' is in 'cpoptions') and ":"
- if ((redo_yank || oap->op_type != OP_YANK)
- && oap->op_type != OP_COLON
- && oap->op_type != OP_FOLD
- && oap->op_type != OP_FOLDOPEN
- && oap->op_type != OP_FOLDOPENREC
- && oap->op_type != OP_FOLDCLOSE
- && oap->op_type != OP_FOLDCLOSEREC
- && oap->op_type != OP_FOLDDEL
- && oap->op_type != OP_FOLDDELREC
- && oap->motion_force == NUL) {
- /* Prepare for redoing. Only use the nchar field for "r",
- * otherwise it might be the second char of the operator. */
- if (cap->cmdchar == 'g' && (cap->nchar == 'n'
- || cap->nchar == 'N')) {
- prep_redo(oap->regname, cap->count0,
- get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
- oap->motion_force, cap->cmdchar, cap->nchar);
- } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) {
- int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
-
- // reverse what nv_replace() did
- if (nchar == REPLACE_CR_NCHAR) {
- nchar = CAR;
- } else if (nchar == REPLACE_NL_NCHAR) {
- nchar = NL;
- }
- prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type),
- get_extra_op_char(oap->op_type), nchar);
- }
- if (!redo_VIsual_busy) {
- redo_VIsual_mode = resel_VIsual_mode;
- redo_VIsual_vcol = resel_VIsual_vcol;
- redo_VIsual_line_count = resel_VIsual_line_count;
- redo_VIsual_count = cap->count0;
- redo_VIsual_arg = cap->arg;
- }
- }
-
- // oap->inclusive defaults to true.
- // If oap->end is on a NUL (empty line) oap->inclusive becomes
- // false. This makes "d}P" and "v}dP" work the same.
- if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) {
- oap->inclusive = true;
- }
- if (VIsual_mode == 'V') {
- oap->motion_type = kMTLineWise;
- } else if (VIsual_mode == 'v') {
- oap->motion_type = kMTCharWise;
- if (*ml_get_pos(&(oap->end)) == NUL
- && (include_line_break || !virtual_op)) {
- oap->inclusive = false;
- // Try to include the newline, unless it's an operator
- // that works on lines only.
- if (*p_sel != 'o'
- && !op_on_lines(oap->op_type)
- && oap->end.lnum < curbuf->b_ml.ml_line_count) {
- oap->end.lnum++;
- oap->end.col = 0;
- oap->end.coladd = 0;
- oap->line_count++;
- }
- }
- }
-
- redo_VIsual_busy = false;
-
- /*
- * Switch Visual off now, so screen updating does
- * not show inverted text when the screen is redrawn.
- * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is
- * no screen redraw, so it is done here to remove the inverted
- * part.
- */
- if (!gui_yank) {
- VIsual_active = false;
- setmouse();
- mouse_dragging = 0;
- may_clear_cmdline();
- if ((oap->op_type == OP_YANK
- || oap->op_type == OP_COLON
- || oap->op_type == OP_FUNCTION
- || oap->op_type == OP_FILTER)
- && oap->motion_force == NUL) {
- // Make sure redrawing is correct.
- curwin->w_p_lbr = lbr_saved;
- redraw_curbuf_later(INVERTED);
- }
- }
- }
-
- // Include the trailing byte of a multi-byte char.
- if (oap->inclusive) {
- const int l = utfc_ptr2len(ml_get_pos(&oap->end));
- if (l > 1) {
- oap->end.col += l - 1;
- }
- }
- curwin->w_set_curswant = true;
-
- /*
- * oap->empty is set when start and end are the same. The inclusive
- * flag affects this too, unless yanking and the end is on a NUL.
- */
- oap->empty = (oap->motion_type != kMTLineWise
- && (!oap->inclusive
- || (oap->op_type == OP_YANK
- && gchar_pos(&oap->end) == NUL))
- && equalpos(oap->start, oap->end)
- && !(virtual_op && oap->start.coladd != oap->end.coladd)
- );
- /*
- * For delete, change and yank, it's an error to operate on an
- * empty region, when 'E' included in 'cpoptions' (Vi compatible).
- */
- empty_region_error = (oap->empty
- && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL);
-
- /* Force a redraw when operating on an empty Visual region, when
- * 'modifiable is off or creating a fold. */
- if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf)
- || oap->op_type == OP_FOLD
- )) {
- curwin->w_p_lbr = lbr_saved;
- redraw_curbuf_later(INVERTED);
- }
-
- /*
- * If the end of an operator is in column one while oap->motion_type
- * is kMTCharWise and oap->inclusive is false, we put op_end after the last
- * character in the previous line. If op_start is on or before the
- * first non-blank in the line, the operator becomes linewise
- * (strange, but that's the way vi does it).
- */
- if (oap->motion_type == kMTCharWise
- && oap->inclusive == false
- && !(cap->retval & CA_NO_ADJ_OP_END)
- && oap->end.col == 0
- && (!oap->is_VIsual || *p_sel == 'o')
- && oap->line_count > 1) {
- oap->end_adjusted = true; // remember that we did this
- oap->line_count--;
- oap->end.lnum--;
- if (inindent(0)) {
- oap->motion_type = kMTLineWise;
- } else {
- oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
- if (oap->end.col) {
- --oap->end.col;
- oap->inclusive = true;
- }
- }
- } else {
- oap->end_adjusted = false;
- }
-
- switch (oap->op_type) {
- case OP_LSHIFT:
- case OP_RSHIFT:
- op_shift(oap, true,
- oap->is_VIsual ? (int)cap->count1 :
- 1);
- auto_format(false, true);
- break;
-
- case OP_JOIN_NS:
- case OP_JOIN:
- if (oap->line_count < 2) {
- oap->line_count = 2;
- }
- if (curwin->w_cursor.lnum + oap->line_count - 1 >
- curbuf->b_ml.ml_line_count) {
- beep_flush();
- } else {
- do_join((size_t)oap->line_count, oap->op_type == OP_JOIN,
- true, true, true);
- auto_format(false, true);
- }
- break;
-
- case OP_DELETE:
- VIsual_reselect = false; // don't reselect now
- if (empty_region_error) {
- vim_beep(BO_OPER);
- CancelRedo();
- } else {
- (void)op_delete(oap);
- // save cursor line for undo if it wasn't saved yet
- if (oap->motion_type == kMTLineWise
- && has_format_option(FO_AUTO)
- && u_save_cursor() == OK) {
- auto_format(false, true);
- }
- }
- break;
-
- case OP_YANK:
- if (empty_region_error) {
- if (!gui_yank) {
- vim_beep(BO_OPER);
- CancelRedo();
- }
- } else {
- curwin->w_p_lbr = lbr_saved;
- oap->excl_tr_ws = cap->cmdchar == 'z';
- (void)op_yank(oap, !gui_yank, false);
- }
- check_cursor_col();
- break;
-
- case OP_CHANGE:
- VIsual_reselect = false; // don't reselect now
- if (empty_region_error) {
- vim_beep(BO_OPER);
- CancelRedo();
- } else {
- /* This is a new edit command, not a restart. Need to
- * remember it to make 'insertmode' work with mappings for
- * Visual mode. But do this only once and not when typed and
- * 'insertmode' isn't set. */
- if (p_im || !KeyTyped) {
- restart_edit_save = restart_edit;
- } else {
- restart_edit_save = 0;
- }
- restart_edit = 0;
-
- // Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
-
- // Reset finish_op now, don't want it set inside edit().
- finish_op = false;
- if (op_change(oap)) { // will call edit()
- cap->retval |= CA_COMMAND_BUSY;
- }
- if (restart_edit == 0) {
- restart_edit = restart_edit_save;
- }
- }
- break;
-
- case OP_FILTER:
- if (vim_strchr(p_cpo, CPO_FILTER) != NULL) {
- AppendToRedobuff("!\r"); // Use any last used !cmd.
- } else {
- bangredo = true; // do_bang() will put cmd in redo buffer.
- }
- FALLTHROUGH;
-
- case OP_INDENT:
- case OP_COLON:
-
- /*
- * If 'equalprg' is empty, do the indenting internally.
- */
- if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) {
- if (curbuf->b_p_lisp) {
- op_reindent(oap, get_lisp_indent);
- break;
- }
- op_reindent(oap,
- *curbuf->b_p_inde != NUL ? get_expr_indent :
- get_c_indent);
- break;
- }
-
- op_colon(oap);
- break;
-
- case OP_TILDE:
- case OP_UPPER:
- case OP_LOWER:
- case OP_ROT13:
- if (empty_region_error) {
- vim_beep(BO_OPER);
- CancelRedo();
- } else {
- op_tilde(oap);
- }
- check_cursor_col();
- break;
-
- case OP_FORMAT:
- if (*curbuf->b_p_fex != NUL) {
- op_formatexpr(oap); // use expression
- } else {
- if (*p_fp != NUL || *curbuf->b_p_fp != NUL) {
- op_colon(oap); // use external command
- } else {
- op_format(oap, false); // use internal function
- }
- }
- break;
-
- case OP_FORMAT2:
- op_format(oap, true); // use internal function
- break;
-
- case OP_FUNCTION:
- // Restore linebreak, so that when the user edits it looks as
- // before.
- curwin->w_p_lbr = lbr_saved;
- op_function(oap); // call 'operatorfunc'
- break;
-
- case OP_INSERT:
- case OP_APPEND:
- VIsual_reselect = false; // don't reselect now
- if (empty_region_error) {
- vim_beep(BO_OPER);
- CancelRedo();
- } else {
- /* This is a new edit command, not a restart. Need to
- * remember it to make 'insertmode' work with mappings for
- * Visual mode. But do this only once. */
- restart_edit_save = restart_edit;
- restart_edit = 0;
-
- // Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
-
- op_insert(oap, cap->count1);
-
- // Reset linebreak, so that formatting works correctly.
- curwin->w_p_lbr = false;
-
- /* TODO: when inserting in several lines, should format all
- * the lines. */
- auto_format(false, true);
-
- if (restart_edit == 0) {
- restart_edit = restart_edit_save;
- } else {
- cap->retval |= CA_COMMAND_BUSY;
- }
- }
- break;
-
- case OP_REPLACE:
- VIsual_reselect = false; // don't reselect now
- if (empty_region_error) {
- vim_beep(BO_OPER);
- CancelRedo();
- } else {
- // Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
-
- op_replace(oap, cap->nchar);
- }
- break;
-
- case OP_FOLD:
- VIsual_reselect = false; // don't reselect now
- foldCreate(curwin, oap->start, oap->end);
- break;
-
- case OP_FOLDOPEN:
- case OP_FOLDOPENREC:
- case OP_FOLDCLOSE:
- case OP_FOLDCLOSEREC:
- VIsual_reselect = false; // don't reselect now
- opFoldRange(oap->start, oap->end,
- oap->op_type == OP_FOLDOPEN
- || oap->op_type == OP_FOLDOPENREC,
- oap->op_type == OP_FOLDOPENREC
- || oap->op_type == OP_FOLDCLOSEREC,
- oap->is_VIsual);
- break;
-
- case OP_FOLDDEL:
- case OP_FOLDDELREC:
- VIsual_reselect = false; // don't reselect now
- deleteFold(curwin, oap->start.lnum, oap->end.lnum,
- oap->op_type == OP_FOLDDELREC, oap->is_VIsual);
- break;
-
- case OP_NR_ADD:
- case OP_NR_SUB:
- if (empty_region_error) {
- vim_beep(BO_OPER);
- CancelRedo();
- } else {
- VIsual_active = true;
- curwin->w_p_lbr = lbr_saved;
- op_addsub(oap, cap->count1, redo_VIsual_arg);
- VIsual_active = false;
- }
- check_cursor_col();
- break;
- default:
- clearopbeep(oap);
- }
- virtual_op = kNone;
- if (!gui_yank) {
- /*
- * if 'sol' not set, go back to old column for some commands
- */
- if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted
- && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT
- || oap->op_type == OP_DELETE)) {
- curwin->w_p_lbr = false;
- coladvance(curwin->w_curswant = old_col);
- }
- } else {
- curwin->w_cursor = old_cursor;
- }
- clearop(oap);
- motion_force = NUL;
- }
- curwin->w_p_lbr = lbr_saved;
-}
-
-/*
- * Handle indent and format operators and visual mode ":".
- */
-static void op_colon(oparg_T *oap)
-{
- stuffcharReadbuff(':');
- if (oap->is_VIsual) {
- stuffReadbuff("'<,'>");
- } else {
- // Make the range look nice, so it can be repeated.
- if (oap->start.lnum == curwin->w_cursor.lnum) {
- stuffcharReadbuff('.');
- } else {
- stuffnumReadbuff((long)oap->start.lnum);
- }
- if (oap->end.lnum != oap->start.lnum) {
- stuffcharReadbuff(',');
- if (oap->end.lnum == curwin->w_cursor.lnum) {
- stuffcharReadbuff('.');
- } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) {
- stuffcharReadbuff('$');
- } else if (oap->start.lnum == curwin->w_cursor.lnum) {
- stuffReadbuff(".+");
- stuffnumReadbuff(oap->line_count - 1);
- } else {
- stuffnumReadbuff((long)oap->end.lnum);
- }
- }
- }
- if (oap->op_type != OP_COLON) {
- stuffReadbuff("!");
- }
- if (oap->op_type == OP_INDENT) {
- stuffReadbuff((const char *)get_equalprg());
- stuffReadbuff("\n");
- } else if (oap->op_type == OP_FORMAT) {
- if (*curbuf->b_p_fp != NUL) {
- stuffReadbuff((const char *)curbuf->b_p_fp);
- } else if (*p_fp != NUL) {
- stuffReadbuff((const char *)p_fp);
- } else {
- stuffReadbuff("fmt");
- }
- stuffReadbuff("\n']");
- }
-
- /*
- * do_cmdline() does the rest
- */
-}
-
-/*
- * Handle the "g@" operator: call 'operatorfunc'.
- */
-static void op_function(const oparg_T *oap)
- FUNC_ATTR_NONNULL_ALL
-{
- const TriState save_virtual_op = virtual_op;
- const bool save_finish_op = finish_op;
-
- if (*p_opfunc == NUL) {
- emsg(_("E774: 'operatorfunc' is empty"));
- } else {
- // Set '[ and '] marks to text to be operated on.
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
- if (oap->motion_type != kMTLineWise && !oap->inclusive) {
- // Exclude the end position.
- decl(&curbuf->b_op_end);
- }
-
- typval_T argv[2];
- argv[0].v_type = VAR_STRING;
- argv[1].v_type = VAR_UNKNOWN;
- argv[0].vval.v_string =
- (char_u *)(((const char *const[]) {
- [kMTBlockWise] = "block",
- [kMTLineWise] = "line",
- [kMTCharWise] = "char",
- })[oap->motion_type]);
-
- // Reset virtual_op so that 'virtualedit' can be changed in the
- // function.
- virtual_op = kNone;
-
- // Reset finish_op so that mode() returns the right value.
- finish_op = false;
-
- (void)call_func_retnr(p_opfunc, 1, argv);
-
- virtual_op = save_virtual_op;
- finish_op = save_finish_op;
- }
-}
-
// Move the current tab to tab in same column as mouse or to end of the
// tabline if there is no tab there.
static void move_tab_to_mouse(void)
@@ -3066,6 +2311,7 @@ void end_visual_mode(void)
may_clear_cmdline();
adjust_cursor_eol();
+ trigger_modechanged();
}
/*
@@ -3092,6 +2338,14 @@ void reset_VIsual(void)
}
}
+void restore_visual_mode(void)
+{
+ if (VIsual_mode_orig != NUL) {
+ curbuf->b_visual.vi_mode = VIsual_mode_orig;
+ VIsual_mode_orig = NUL;
+ }
+}
+
// Check for a balloon-eval special item to include when searching for an
// identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
// Returns true if the character at "*ptr" should be included.
@@ -3270,7 +2524,7 @@ static void prep_redo_cmd(cmdarg_T *cap)
* Prepare for redo of any command.
* Note that only the last argument can be a multi-byte char.
*/
-static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
+void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
{
ResetRedobuff();
if (regname != 0) { // yank from specified buffer
@@ -3327,7 +2581,7 @@ static bool checkclearopq(oparg_T *oap)
return true;
}
-static void clearop(oparg_T *oap)
+void clearop(oparg_T *oap)
{
oap->op_type = OP_NOP;
oap->regname = 0;
@@ -3336,7 +2590,7 @@ static void clearop(oparg_T *oap)
motion_force = NUL;
}
-static void clearopbeep(oparg_T *oap)
+void clearopbeep(oparg_T *oap)
{
clearop(oap);
beep_flush();
@@ -3366,7 +2620,7 @@ static void unshift_special(cmdarg_T *cap)
/// If the mode is currently displayed clear the command line or update the
/// command displayed.
-static void may_clear_cmdline(void)
+void may_clear_cmdline(void)
{
if (mode_displayed) {
// unshow visual mode later
@@ -4851,6 +4105,7 @@ static void nv_ctrlg(cmdarg_T *cap)
{
if (VIsual_active) { // toggle Selection/Visual mode
VIsual_select = !VIsual_select;
+ trigger_modechanged();
showmode();
} else if (!checkclearop(cap->oap)) {
// print full name if count given or :cd used
@@ -4894,6 +4149,7 @@ static void nv_ctrlo(cmdarg_T *cap)
{
if (VIsual_active && VIsual_select) {
VIsual_select = false;
+ trigger_modechanged();
showmode();
restart_VIsual_select = 2; // restart Select mode later
} else {
@@ -6680,6 +5936,7 @@ static void nv_visual(cmdarg_T *cap)
// or char/line mode
VIsual_mode = cap->cmdchar;
showmode();
+ trigger_modechanged();
}
redraw_curbuf_later(INVERTED); // update the inversion
} else { // start Visual mode
@@ -6793,6 +6050,7 @@ static void n_start_visual_mode(int c)
foldAdjustVisual();
+ trigger_modechanged();
setmouse();
// Check for redraw after changing the state.
conceal_check_cursor_line();
@@ -7730,7 +6988,7 @@ static void adjust_for_sel(cmdarg_T *cap)
* Should check VIsual_mode before calling this.
* Returns true when backed up to the previous line.
*/
-static bool unadjust_for_sel(void)
+bool unadjust_for_sel(void)
{
pos_T *pp;
@@ -8320,71 +7578,6 @@ static void nv_open(cmdarg_T *cap)
}
}
-/// Calculate start/end virtual columns for operating in block mode.
-///
-/// @param initial when true: adjust position for 'selectmode'
-static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial)
-{
- colnr_T start;
- colnr_T end;
-
- if (VIsual_mode != Ctrl_V
- || (!initial && oap->end.col < curwin->w_width_inner)) {
- return;
- }
-
- oap->motion_type = kMTBlockWise;
-
- // prevent from moving onto a trail byte
- mark_mb_adjustpos(curwin->w_buffer, &oap->end);
-
- getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol);
- if (!redo_VIsual_busy) {
- getvvcol(curwin, &(oap->end), &start, NULL, &end);
-
- if (start < oap->start_vcol) {
- oap->start_vcol = start;
- }
- if (end > oap->end_vcol) {
- if (initial && *p_sel == 'e'
- && start >= 1
- && start - 1 >= oap->end_vcol) {
- oap->end_vcol = start - 1;
- } else {
- oap->end_vcol = end;
- }
- }
- }
-
- // if '$' was used, get oap->end_vcol from longest line
- if (curwin->w_curswant == MAXCOL) {
- curwin->w_cursor.col = MAXCOL;
- oap->end_vcol = 0;
- for (curwin->w_cursor.lnum = oap->start.lnum;
- curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) {
- getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
- if (end > oap->end_vcol) {
- oap->end_vcol = end;
- }
- }
- } else if (redo_VIsual_busy) {
- oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1;
- }
-
- // Correct oap->end.col and oap->start.col to be the
- // upper-left and lower-right corner of the block area.
- //
- // (Actually, this does convert column positions into character
- // positions)
- curwin->w_cursor.lnum = oap->end.lnum;
- coladvance(oap->end_vcol);
- oap->end = curwin->w_cursor;
-
- curwin->w_cursor = oap->start;
- coladvance(oap->start_vcol);
- oap->start = curwin->w_cursor;
-}
-
// Handle an arbitrary event in normal mode
static void nv_event(cmdarg_T *cap)
{
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index cbfed5daa5..52c382028e 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -27,7 +27,9 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/indent.h"
+#include "nvim/indent_c.h"
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/macros.h"
@@ -37,6 +39,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
+#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/ops.h"
@@ -1768,7 +1771,7 @@ static void replace_character(int c)
/*
* Replace a whole area with one character.
*/
-int op_replace(oparg_T *oap, int c)
+static int op_replace(oparg_T *oap, int c)
{
int n, numc;
int num_chars;
@@ -2162,7 +2165,8 @@ void op_insert(oparg_T *oap, long count1)
{
long ins_len, pre_textlen = 0;
char_u *firstline, *ins_text;
- colnr_T ind_pre = 0;
+ colnr_T ind_pre_col = 0, ind_post_col;
+ int ind_pre_vcol = 0, ind_post_vcol = 0;
struct block_def bd;
int i;
pos_T t1;
@@ -2196,7 +2200,8 @@ void op_insert(oparg_T *oap, long count1)
// Get the info about the block before entering the text
block_prep(oap, &bd, oap->start.lnum, true);
// Get indent information
- ind_pre = (colnr_T)getwhitecols_curline();
+ ind_pre_col = (colnr_T)getwhitecols_curline();
+ ind_pre_vcol = get_indent();
firstline = ml_get(oap->start.lnum) + bd.textcol;
if (oap->op_type == OP_APPEND) {
@@ -2261,10 +2266,11 @@ void op_insert(oparg_T *oap, long count1)
// if indent kicked in, the firstline might have changed
// but only do that, if the indent actually increased
- const colnr_T ind_post = (colnr_T)getwhitecols_curline();
- if (curbuf->b_op_start.col > ind_pre && ind_post > ind_pre) {
- bd.textcol += ind_post - ind_pre;
- bd.start_vcol += ind_post - ind_pre;
+ ind_post_col = (colnr_T)getwhitecols_curline();
+ if (curbuf->b_op_start.col > ind_pre_col && ind_post_col > ind_pre_col) {
+ bd.textcol += ind_post_col - ind_pre_col;
+ ind_post_vcol = get_indent();
+ bd.start_vcol += ind_post_vcol - ind_pre_vcol;
did_indent = true;
}
@@ -2297,12 +2303,26 @@ void op_insert(oparg_T *oap, long count1)
}
}
- /*
- * Spaces and tabs in the indent may have changed to other spaces and
- * tabs. Get the starting column again and correct the length.
- * Don't do this when "$" used, end-of-line will have changed.
- */
+ // Spaces and tabs in the indent may have changed to other spaces and
+ // tabs. Get the starting column again and correct the length.
+ // Don't do this when "$" used, end-of-line will have changed.
+ //
+ // if indent was added and the inserted text was after the indent,
+ // correct the selection for the new indent.
+ if (did_indent && bd.textcol - ind_post_col > 0) {
+ oap->start.col += ind_post_col - ind_pre_col;
+ oap->start_vcol += ind_post_vcol - ind_pre_vcol;
+ oap->end.col += ind_post_col - ind_pre_col;
+ oap->end_vcol += ind_post_vcol - ind_pre_vcol;
+ }
block_prep(oap, &bd2, oap->start.lnum, true);
+ if (did_indent && bd.textcol - ind_post_col > 0) {
+ // undo for where "oap" is used below
+ oap->start.col -= ind_post_col - ind_pre_col;
+ oap->start_vcol -= ind_post_vcol - ind_pre_vcol;
+ oap->end.col -= ind_post_col - ind_pre_col;
+ oap->end_vcol -= ind_post_vcol - ind_pre_vcol;
+ }
if (!bd.is_MAX || bd2.textlen < bd.textlen) {
if (oap->op_type == OP_APPEND) {
pre_textlen += bd2.textlen - bd.textlen;
@@ -2792,8 +2812,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
recursive = true;
+ save_v_event_T save_v_event;
// Set the v:event dictionary with information about the yank.
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ dict_T *dict = get_v_event(&save_v_event);
// The yanked text contents.
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
@@ -2830,7 +2851,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
textlock++;
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
textlock--;
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
recursive = false;
}
@@ -4159,7 +4180,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in
/// Implementation of the format operator 'gq'.
///
/// @param keep_cursor keep cursor on same text char
-void op_format(oparg_T *oap, int keep_cursor)
+static void op_format(oparg_T *oap, int keep_cursor)
{
long old_line_count = curbuf->b_ml.ml_line_count;
@@ -4227,7 +4248,7 @@ void op_format(oparg_T *oap, int keep_cursor)
/*
* Implementation of the format operator 'gq' for when using 'formatexpr'.
*/
-void op_formatexpr(oparg_T *oap)
+static void op_formatexpr(oparg_T *oap)
{
if (oap->is_VIsual) {
// When there is no change: need to remove the Visual selection
@@ -5918,6 +5939,791 @@ void cursor_pos_info(dict_T *dict)
}
}
+// Handle indent and format operators and visual mode ":".
+static void op_colon(oparg_T *oap)
+{
+ stuffcharReadbuff(':');
+ if (oap->is_VIsual) {
+ stuffReadbuff("'<,'>");
+ } else {
+ // Make the range look nice, so it can be repeated.
+ if (oap->start.lnum == curwin->w_cursor.lnum) {
+ stuffcharReadbuff('.');
+ } else {
+ stuffnumReadbuff((long)oap->start.lnum);
+ }
+ if (oap->end.lnum != oap->start.lnum) {
+ stuffcharReadbuff(',');
+ if (oap->end.lnum == curwin->w_cursor.lnum) {
+ stuffcharReadbuff('.');
+ } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) {
+ stuffcharReadbuff('$');
+ } else if (oap->start.lnum == curwin->w_cursor.lnum) {
+ stuffReadbuff(".+");
+ stuffnumReadbuff(oap->line_count - 1);
+ } else {
+ stuffnumReadbuff((long)oap->end.lnum);
+ }
+ }
+ }
+ if (oap->op_type != OP_COLON) {
+ stuffReadbuff("!");
+ }
+ if (oap->op_type == OP_INDENT) {
+ stuffReadbuff((const char *)get_equalprg());
+ stuffReadbuff("\n");
+ } else if (oap->op_type == OP_FORMAT) {
+ if (*curbuf->b_p_fp != NUL) {
+ stuffReadbuff((const char *)curbuf->b_p_fp);
+ } else if (*p_fp != NUL) {
+ stuffReadbuff((const char *)p_fp);
+ } else {
+ stuffReadbuff("fmt");
+ }
+ stuffReadbuff("\n']");
+ }
+
+ // do_cmdline() does the rest
+}
+
+// Handle the "g@" operator: call 'operatorfunc'.
+static void op_function(const oparg_T *oap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const TriState save_virtual_op = virtual_op;
+ const bool save_finish_op = finish_op;
+
+ if (*p_opfunc == NUL) {
+ emsg(_("E774: 'operatorfunc' is empty"));
+ } else {
+ // Set '[ and '] marks to text to be operated on.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ if (oap->motion_type != kMTLineWise && !oap->inclusive) {
+ // Exclude the end position.
+ decl(&curbuf->b_op_end);
+ }
+
+ typval_T argv[2];
+ argv[0].v_type = VAR_STRING;
+ argv[1].v_type = VAR_UNKNOWN;
+ argv[0].vval.v_string =
+ (char_u *)(((const char *const[]) {
+ [kMTBlockWise] = "block",
+ [kMTLineWise] = "line",
+ [kMTCharWise] = "char",
+ })[oap->motion_type]);
+
+ // Reset virtual_op so that 'virtualedit' can be changed in the
+ // function.
+ virtual_op = kNone;
+
+ // Reset finish_op so that mode() returns the right value.
+ finish_op = false;
+
+ (void)call_func_retnr(p_opfunc, 1, argv);
+
+ virtual_op = save_virtual_op;
+ finish_op = save_finish_op;
+ }
+}
+
+/// Calculate start/end virtual columns for operating in block mode.
+///
+/// @param initial when true: adjust position for 'selectmode'
+static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial)
+{
+ colnr_T start;
+ colnr_T end;
+
+ if (VIsual_mode != Ctrl_V
+ || (!initial && oap->end.col < curwin->w_width_inner)) {
+ return;
+ }
+
+ oap->motion_type = kMTBlockWise;
+
+ // prevent from moving onto a trail byte
+ mark_mb_adjustpos(curwin->w_buffer, &oap->end);
+
+ getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol);
+ if (!redo_VIsual_busy) {
+ getvvcol(curwin, &(oap->end), &start, NULL, &end);
+
+ if (start < oap->start_vcol) {
+ oap->start_vcol = start;
+ }
+ if (end > oap->end_vcol) {
+ if (initial && *p_sel == 'e'
+ && start >= 1
+ && start - 1 >= oap->end_vcol) {
+ oap->end_vcol = start - 1;
+ } else {
+ oap->end_vcol = end;
+ }
+ }
+ }
+
+ // if '$' was used, get oap->end_vcol from longest line
+ if (curwin->w_curswant == MAXCOL) {
+ curwin->w_cursor.col = MAXCOL;
+ oap->end_vcol = 0;
+ for (curwin->w_cursor.lnum = oap->start.lnum;
+ curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) {
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
+ if (end > oap->end_vcol) {
+ oap->end_vcol = end;
+ }
+ }
+ } else if (redo_VIsual_busy) {
+ oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1;
+ }
+
+ // Correct oap->end.col and oap->start.col to be the
+ // upper-left and lower-right corner of the block area.
+ //
+ // (Actually, this does convert column positions into character
+ // positions)
+ curwin->w_cursor.lnum = oap->end.lnum;
+ coladvance(oap->end_vcol);
+ oap->end = curwin->w_cursor;
+
+ curwin->w_cursor = oap->start;
+ coladvance(oap->start_vcol);
+ oap->start = curwin->w_cursor;
+}
+
+// Handle an operator after Visual mode or when the movement is finished.
+// "gui_yank" is true when yanking text for the clipboard.
+void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
+{
+ oparg_T *oap = cap->oap;
+ pos_T old_cursor;
+ bool empty_region_error;
+ int restart_edit_save;
+ int lbr_saved = curwin->w_p_lbr;
+
+
+ // The visual area is remembered for redo
+ static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
+ static linenr_T redo_VIsual_line_count; // number of lines
+ static colnr_T redo_VIsual_vcol; // number of cols or end column
+ static long redo_VIsual_count; // count for Visual operator
+ static int redo_VIsual_arg; // extra argument
+ bool include_line_break = false;
+
+ old_cursor = curwin->w_cursor;
+
+ // If an operation is pending, handle it...
+ if ((finish_op
+ || VIsual_active)
+ && oap->op_type != OP_NOP) {
+ // Yank can be redone when 'y' is in 'cpoptions', but not when yanking
+ // for the clipboard.
+ const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank;
+
+ // Avoid a problem with unwanted linebreaks in block mode
+ if (curwin->w_p_lbr) {
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ }
+ curwin->w_p_lbr = false;
+ oap->is_VIsual = VIsual_active;
+ if (oap->motion_force == 'V') {
+ oap->motion_type = kMTLineWise;
+ } else if (oap->motion_force == 'v') {
+ // If the motion was linewise, "inclusive" will not have been set.
+ // Use "exclusive" to be consistent. Makes "dvj" work nice.
+ if (oap->motion_type == kMTLineWise) {
+ oap->inclusive = false;
+ } else if (oap->motion_type == kMTCharWise) {
+ // If the motion already was charwise, toggle "inclusive"
+ oap->inclusive = !oap->inclusive;
+ }
+ oap->motion_type = kMTCharWise;
+ } else if (oap->motion_force == Ctrl_V) {
+ // Change line- or charwise motion into Visual block mode.
+ if (!VIsual_active) {
+ VIsual_active = true;
+ VIsual = oap->start;
+ }
+ VIsual_mode = Ctrl_V;
+ VIsual_select = false;
+ VIsual_reselect = false;
+ }
+
+ // Only redo yank when 'y' flag is in 'cpoptions'.
+ // Never redo "zf" (define fold).
+ if ((redo_yank || oap->op_type != OP_YANK)
+ && ((!VIsual_active || oap->motion_force)
+ // Also redo Operator-pending Visual mode mappings.
+ || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND)
+ && oap->op_type != OP_COLON))
+ && cap->cmdchar != 'D'
+ && oap->op_type != OP_FOLD
+ && oap->op_type != OP_FOLDOPEN
+ && oap->op_type != OP_FOLDOPENREC
+ && oap->op_type != OP_FOLDCLOSE
+ && oap->op_type != OP_FOLDCLOSEREC
+ && oap->op_type != OP_FOLDDEL
+ && oap->op_type != OP_FOLDDELREC) {
+ prep_redo(oap->regname, cap->count0,
+ get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
+ oap->motion_force, cap->cmdchar, cap->nchar);
+ if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search
+ // If 'cpoptions' does not contain 'r', insert the search
+ // pattern to really repeat the same command.
+ if (vim_strchr(p_cpo, CPO_REDO) == NULL) {
+ AppendToRedobuffLit(cap->searchbuf, -1);
+ }
+ AppendToRedobuff(NL_STR);
+ } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) {
+ // do_cmdline() has stored the first typed line in
+ // "repeat_cmdline". When several lines are typed repeating
+ // won't be possible.
+ if (repeat_cmdline == NULL) {
+ ResetRedobuff();
+ } else {
+ AppendToRedobuffLit(repeat_cmdline, -1);
+ AppendToRedobuff(NL_STR);
+ XFREE_CLEAR(repeat_cmdline);
+ }
+ }
+ }
+
+ if (redo_VIsual_busy) {
+ // Redo of an operation on a Visual area. Use the same size from
+ // redo_VIsual_line_count and redo_VIsual_vcol.
+ oap->start = curwin->w_cursor;
+ curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ }
+ VIsual_mode = redo_VIsual_mode;
+ if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') {
+ if (VIsual_mode == 'v') {
+ if (redo_VIsual_line_count <= 1) {
+ validate_virtcol();
+ curwin->w_curswant =
+ curwin->w_virtcol + redo_VIsual_vcol - 1;
+ } else {
+ curwin->w_curswant = redo_VIsual_vcol;
+ }
+ } else {
+ curwin->w_curswant = MAXCOL;
+ }
+ coladvance(curwin->w_curswant);
+ }
+ cap->count0 = redo_VIsual_count;
+ cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
+ } else if (VIsual_active) {
+ if (!gui_yank) {
+ // Save the current VIsual area for '< and '> marks, and "gv"
+ curbuf->b_visual.vi_start = VIsual;
+ curbuf->b_visual.vi_end = curwin->w_cursor;
+ curbuf->b_visual.vi_mode = VIsual_mode;
+ restore_visual_mode();
+ curbuf->b_visual.vi_curswant = curwin->w_curswant;
+ curbuf->b_visual_mode_eval = VIsual_mode;
+ }
+
+ // In Select mode, a linewise selection is operated upon like a
+ // charwise selection.
+ // Special case: gH<Del> deletes the last line.
+ if (VIsual_select && VIsual_mode == 'V'
+ && cap->oap->op_type != OP_DELETE) {
+ if (lt(VIsual, curwin->w_cursor)) {
+ VIsual.col = 0;
+ curwin->w_cursor.col =
+ (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum));
+ } else {
+ curwin->w_cursor.col = 0;
+ VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum));
+ }
+ VIsual_mode = 'v';
+ } else if (VIsual_mode == 'v') {
+ // If 'selection' is "exclusive", backup one character for
+ // charwise selections.
+ include_line_break =
+ unadjust_for_sel();
+ }
+
+ oap->start = VIsual;
+ if (VIsual_mode == 'V') {
+ oap->start.col = 0;
+ oap->start.coladd = 0;
+ }
+ }
+
+ // Set oap->start to the first position of the operated text, oap->end
+ // to the end of the operated text. w_cursor is equal to oap->start.
+ if (lt(oap->start, curwin->w_cursor)) {
+ // Include folded lines completely.
+ if (!VIsual_active) {
+ if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) {
+ oap->start.col = 0;
+ }
+ if ((curwin->w_cursor.col > 0
+ || oap->inclusive
+ || oap->motion_type == kMTLineWise)
+ && hasFolding(curwin->w_cursor.lnum, NULL,
+ &curwin->w_cursor.lnum)) {
+ curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr());
+ }
+ }
+ oap->end = curwin->w_cursor;
+ curwin->w_cursor = oap->start;
+
+ // w_virtcol may have been updated; if the cursor goes back to its
+ // previous position w_virtcol becomes invalid and isn't updated
+ // automatically.
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ } else {
+ // Include folded lines completely.
+ if (!VIsual_active && oap->motion_type == kMTLineWise) {
+ if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum,
+ NULL)) {
+ curwin->w_cursor.col = 0;
+ }
+ if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) {
+ oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum));
+ }
+ }
+ oap->end = oap->start;
+ oap->start = curwin->w_cursor;
+ }
+
+ // Just in case lines were deleted that make the position invalid.
+ check_pos(curwin->w_buffer, &oap->end);
+ oap->line_count = oap->end.lnum - oap->start.lnum + 1;
+
+ // Set "virtual_op" before resetting VIsual_active.
+ virtual_op = virtual_active();
+
+ if (VIsual_active || redo_VIsual_busy) {
+ get_op_vcol(oap, redo_VIsual_vcol, true);
+
+ if (!redo_VIsual_busy && !gui_yank) {
+ // Prepare to reselect and redo Visual: this is based on the
+ // size of the Visual text
+ resel_VIsual_mode = VIsual_mode;
+ if (curwin->w_curswant == MAXCOL) {
+ resel_VIsual_vcol = MAXCOL;
+ } else {
+ if (VIsual_mode != Ctrl_V) {
+ getvvcol(curwin, &(oap->end),
+ NULL, NULL, &oap->end_vcol);
+ }
+ if (VIsual_mode == Ctrl_V || oap->line_count <= 1) {
+ if (VIsual_mode != Ctrl_V) {
+ getvvcol(curwin, &(oap->start),
+ &oap->start_vcol, NULL, NULL);
+ }
+ resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1;
+ } else {
+ resel_VIsual_vcol = oap->end_vcol;
+ }
+ }
+ resel_VIsual_line_count = oap->line_count;
+ }
+
+ // can't redo yank (unless 'y' is in 'cpoptions') and ":"
+ if ((redo_yank || oap->op_type != OP_YANK)
+ && oap->op_type != OP_COLON
+ && oap->op_type != OP_FOLD
+ && oap->op_type != OP_FOLDOPEN
+ && oap->op_type != OP_FOLDOPENREC
+ && oap->op_type != OP_FOLDCLOSE
+ && oap->op_type != OP_FOLDCLOSEREC
+ && oap->op_type != OP_FOLDDEL
+ && oap->op_type != OP_FOLDDELREC
+ && oap->motion_force == NUL) {
+ // Prepare for redoing. Only use the nchar field for "r",
+ // otherwise it might be the second char of the operator.
+ if (cap->cmdchar == 'g' && (cap->nchar == 'n'
+ || cap->nchar == 'N')) {
+ prep_redo(oap->regname, cap->count0,
+ get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
+ oap->motion_force, cap->cmdchar, cap->nchar);
+ } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) {
+ int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
+
+ // reverse what nv_replace() did
+ if (nchar == REPLACE_CR_NCHAR) {
+ nchar = CAR;
+ } else if (nchar == REPLACE_NL_NCHAR) {
+ nchar = NL;
+ }
+ prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type),
+ get_extra_op_char(oap->op_type), nchar);
+ }
+ if (!redo_VIsual_busy) {
+ redo_VIsual_mode = resel_VIsual_mode;
+ redo_VIsual_vcol = resel_VIsual_vcol;
+ redo_VIsual_line_count = resel_VIsual_line_count;
+ redo_VIsual_count = cap->count0;
+ redo_VIsual_arg = cap->arg;
+ }
+ }
+
+ // oap->inclusive defaults to true.
+ // If oap->end is on a NUL (empty line) oap->inclusive becomes
+ // false. This makes "d}P" and "v}dP" work the same.
+ if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) {
+ oap->inclusive = true;
+ }
+ if (VIsual_mode == 'V') {
+ oap->motion_type = kMTLineWise;
+ } else if (VIsual_mode == 'v') {
+ oap->motion_type = kMTCharWise;
+ if (*ml_get_pos(&(oap->end)) == NUL
+ && (include_line_break || !virtual_op)) {
+ oap->inclusive = false;
+ // Try to include the newline, unless it's an operator
+ // that works on lines only.
+ if (*p_sel != 'o'
+ && !op_on_lines(oap->op_type)
+ && oap->end.lnum < curbuf->b_ml.ml_line_count) {
+ oap->end.lnum++;
+ oap->end.col = 0;
+ oap->end.coladd = 0;
+ oap->line_count++;
+ }
+ }
+ }
+
+ redo_VIsual_busy = false;
+
+ // Switch Visual off now, so screen updating does
+ // not show inverted text when the screen is redrawn.
+ // With OP_YANK and sometimes with OP_COLON and OP_FILTER there is
+ // no screen redraw, so it is done here to remove the inverted
+ // part.
+ if (!gui_yank) {
+ VIsual_active = false;
+ setmouse();
+ mouse_dragging = 0;
+ may_clear_cmdline();
+ if ((oap->op_type == OP_YANK
+ || oap->op_type == OP_COLON
+ || oap->op_type == OP_FUNCTION
+ || oap->op_type == OP_FILTER)
+ && oap->motion_force == NUL) {
+ // Make sure redrawing is correct.
+ curwin->w_p_lbr = lbr_saved;
+ redraw_curbuf_later(INVERTED);
+ }
+ }
+ }
+
+ // Include the trailing byte of a multi-byte char.
+ if (oap->inclusive) {
+ const int l = utfc_ptr2len(ml_get_pos(&oap->end));
+ if (l > 1) {
+ oap->end.col += l - 1;
+ }
+ }
+ curwin->w_set_curswant = true;
+
+ // oap->empty is set when start and end are the same. The inclusive
+ // flag affects this too, unless yanking and the end is on a NUL.
+ oap->empty = (oap->motion_type != kMTLineWise
+ && (!oap->inclusive
+ || (oap->op_type == OP_YANK
+ && gchar_pos(&oap->end) == NUL))
+ && equalpos(oap->start, oap->end)
+ && !(virtual_op && oap->start.coladd != oap->end.coladd));
+ // For delete, change and yank, it's an error to operate on an
+ // empty region, when 'E' included in 'cpoptions' (Vi compatible).
+ empty_region_error = (oap->empty
+ && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL);
+
+ // Force a redraw when operating on an empty Visual region, when
+ // 'modifiable is off or creating a fold.
+ if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf)
+ || oap->op_type == OP_FOLD)) {
+ curwin->w_p_lbr = lbr_saved;
+ redraw_curbuf_later(INVERTED);
+ }
+
+ // If the end of an operator is in column one while oap->motion_type
+ // is kMTCharWise and oap->inclusive is false, we put op_end after the last
+ // character in the previous line. If op_start is on or before the
+ // first non-blank in the line, the operator becomes linewise
+ // (strange, but that's the way vi does it).
+ if (oap->motion_type == kMTCharWise
+ && oap->inclusive == false
+ && !(cap->retval & CA_NO_ADJ_OP_END)
+ && oap->end.col == 0
+ && (!oap->is_VIsual || *p_sel == 'o')
+ && oap->line_count > 1) {
+ oap->end_adjusted = true; // remember that we did this
+ oap->line_count--;
+ oap->end.lnum--;
+ if (inindent(0)) {
+ oap->motion_type = kMTLineWise;
+ } else {
+ oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
+ if (oap->end.col) {
+ oap->end.col--;
+ oap->inclusive = true;
+ }
+ }
+ } else {
+ oap->end_adjusted = false;
+ }
+
+ switch (oap->op_type) {
+ case OP_LSHIFT:
+ case OP_RSHIFT:
+ op_shift(oap, true,
+ oap->is_VIsual ? (int)cap->count1 :
+ 1);
+ auto_format(false, true);
+ break;
+
+ case OP_JOIN_NS:
+ case OP_JOIN:
+ if (oap->line_count < 2) {
+ oap->line_count = 2;
+ }
+ if (curwin->w_cursor.lnum + oap->line_count - 1 >
+ curbuf->b_ml.ml_line_count) {
+ beep_flush();
+ } else {
+ do_join((size_t)oap->line_count, oap->op_type == OP_JOIN,
+ true, true, true);
+ auto_format(false, true);
+ }
+ break;
+
+ case OP_DELETE:
+ VIsual_reselect = false; // don't reselect now
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ (void)op_delete(oap);
+ // save cursor line for undo if it wasn't saved yet
+ if (oap->motion_type == kMTLineWise
+ && has_format_option(FO_AUTO)
+ && u_save_cursor() == OK) {
+ auto_format(false, true);
+ }
+ }
+ break;
+
+ case OP_YANK:
+ if (empty_region_error) {
+ if (!gui_yank) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ }
+ } else {
+ curwin->w_p_lbr = lbr_saved;
+ oap->excl_tr_ws = cap->cmdchar == 'z';
+ (void)op_yank(oap, !gui_yank, false);
+ }
+ check_cursor_col();
+ break;
+
+ case OP_CHANGE:
+ VIsual_reselect = false; // don't reselect now
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ // This is a new edit command, not a restart. Need to
+ // remember it to make 'insertmode' work with mappings for
+ // Visual mode. But do this only once and not when typed and
+ // 'insertmode' isn't set.
+ if (p_im || !KeyTyped) {
+ restart_edit_save = restart_edit;
+ } else {
+ restart_edit_save = 0;
+ }
+ restart_edit = 0;
+
+ // Restore linebreak, so that when the user edits it looks as before.
+ curwin->w_p_lbr = lbr_saved;
+
+ // Reset finish_op now, don't want it set inside edit().
+ finish_op = false;
+ if (op_change(oap)) { // will call edit()
+ cap->retval |= CA_COMMAND_BUSY;
+ }
+ if (restart_edit == 0) {
+ restart_edit = restart_edit_save;
+ }
+ }
+ break;
+
+ case OP_FILTER:
+ if (vim_strchr(p_cpo, CPO_FILTER) != NULL) {
+ AppendToRedobuff("!\r"); // Use any last used !cmd.
+ } else {
+ bangredo = true; // do_bang() will put cmd in redo buffer.
+ }
+ FALLTHROUGH;
+
+ case OP_INDENT:
+ case OP_COLON:
+
+ // If 'equalprg' is empty, do the indenting internally.
+ if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) {
+ if (curbuf->b_p_lisp) {
+ op_reindent(oap, get_lisp_indent);
+ break;
+ }
+ op_reindent(oap,
+ *curbuf->b_p_inde != NUL ? get_expr_indent :
+ get_c_indent);
+ break;
+ }
+
+ op_colon(oap);
+ break;
+
+ case OP_TILDE:
+ case OP_UPPER:
+ case OP_LOWER:
+ case OP_ROT13:
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ op_tilde(oap);
+ }
+ check_cursor_col();
+ break;
+
+ case OP_FORMAT:
+ if (*curbuf->b_p_fex != NUL) {
+ op_formatexpr(oap); // use expression
+ } else {
+ if (*p_fp != NUL || *curbuf->b_p_fp != NUL) {
+ op_colon(oap); // use external command
+ } else {
+ op_format(oap, false); // use internal function
+ }
+ }
+ break;
+
+ case OP_FORMAT2:
+ op_format(oap, true); // use internal function
+ break;
+
+ case OP_FUNCTION:
+ // Restore linebreak, so that when the user edits it looks as
+ // before.
+ curwin->w_p_lbr = lbr_saved;
+ op_function(oap); // call 'operatorfunc'
+ break;
+
+ case OP_INSERT:
+ case OP_APPEND:
+ VIsual_reselect = false; // don't reselect now
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ // This is a new edit command, not a restart. Need to
+ // remember it to make 'insertmode' work with mappings for
+ // Visual mode. But do this only once.
+ restart_edit_save = restart_edit;
+ restart_edit = 0;
+
+ // Restore linebreak, so that when the user edits it looks as before.
+ curwin->w_p_lbr = lbr_saved;
+
+ op_insert(oap, cap->count1);
+
+ // Reset linebreak, so that formatting works correctly.
+ curwin->w_p_lbr = false;
+
+ // TODO(brammool): when inserting in several lines, should format all
+ // the lines.
+ auto_format(false, true);
+
+ if (restart_edit == 0) {
+ restart_edit = restart_edit_save;
+ } else {
+ cap->retval |= CA_COMMAND_BUSY;
+ }
+ }
+ break;
+
+ case OP_REPLACE:
+ VIsual_reselect = false; // don't reselect now
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ // Restore linebreak, so that when the user edits it looks as before.
+ curwin->w_p_lbr = lbr_saved;
+
+ op_replace(oap, cap->nchar);
+ }
+ break;
+
+ case OP_FOLD:
+ VIsual_reselect = false; // don't reselect now
+ foldCreate(curwin, oap->start, oap->end);
+ break;
+
+ case OP_FOLDOPEN:
+ case OP_FOLDOPENREC:
+ case OP_FOLDCLOSE:
+ case OP_FOLDCLOSEREC:
+ VIsual_reselect = false; // don't reselect now
+ opFoldRange(oap->start, oap->end,
+ oap->op_type == OP_FOLDOPEN
+ || oap->op_type == OP_FOLDOPENREC,
+ oap->op_type == OP_FOLDOPENREC
+ || oap->op_type == OP_FOLDCLOSEREC,
+ oap->is_VIsual);
+ break;
+
+ case OP_FOLDDEL:
+ case OP_FOLDDELREC:
+ VIsual_reselect = false; // don't reselect now
+ deleteFold(curwin, oap->start.lnum, oap->end.lnum,
+ oap->op_type == OP_FOLDDELREC, oap->is_VIsual);
+ break;
+
+ case OP_NR_ADD:
+ case OP_NR_SUB:
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ VIsual_active = true;
+ curwin->w_p_lbr = lbr_saved;
+ op_addsub(oap, cap->count1, redo_VIsual_arg);
+ VIsual_active = false;
+ }
+ check_cursor_col();
+ break;
+ default:
+ clearopbeep(oap);
+ }
+ virtual_op = kNone;
+ if (!gui_yank) {
+ // if 'sol' not set, go back to old column for some commands
+ if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted
+ && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT
+ || oap->op_type == OP_DELETE)) {
+ curwin->w_p_lbr = false;
+ coladvance(curwin->w_curswant = old_col);
+ }
+ } else {
+ curwin->w_cursor = old_cursor;
+ }
+ clearop(oap);
+ motion_force = NUL;
+ }
+ curwin->w_p_lbr = lbr_saved;
+}
+
/// Check if the default register (used in an unnamed paste) should be a
/// clipboard register. This happens when `clipboard=unnamed[plus]` is set
/// and a provider is available.
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 45e2032b35..1fe2e1d04c 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1301,7 +1301,11 @@ int do_set(char_u *arg, int opt_flags)
char_u *oldval = NULL; // previous value if *varp
char_u *newval;
char_u *origval = NULL;
+ char_u *origval_l = NULL;
+ char_u *origval_g = NULL;
char *saved_origval = NULL;
+ char *saved_origval_l = NULL;
+ char *saved_origval_g = NULL;
char *saved_newval = NULL;
unsigned newlen;
int comma;
@@ -1319,10 +1323,21 @@ int do_set(char_u *arg, int opt_flags)
// new value is valid.
oldval = *(char_u **)varp;
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
+ origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), 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) {
+ origval_l = origval_g;
+ }
+ }
+
// 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)) {
+ if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
origval = *(char_u **)get_varp(&options[opt_idx]);
} else {
origval = oldval;
@@ -1388,6 +1403,12 @@ int do_set(char_u *arg, int opt_flags)
if (origval == oldval) {
origval = *(char_u **)varp;
}
+ if (origval_l == oldval) {
+ origval_l = *(char_u **)varp;
+ }
+ if (origval_g == oldval) {
+ origval_g = *(char_u **)varp;
+ }
oldval = *(char_u **)varp;
}
/*
@@ -1596,6 +1617,8 @@ int do_set(char_u *arg, int opt_flags)
// origval may be freed by
// did_set_string_option(), make a copy.
saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0;
+ saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : 0;
+ saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : 0;
// newval (and varp) may become invalid if the
// buffer is closed by autocommands.
@@ -1630,8 +1653,8 @@ int do_set(char_u *arg, int opt_flags)
if (errmsg == NULL) {
if (!starting) {
- trigger_optionsset_string(opt_idx, opt_flags, saved_origval,
- saved_newval);
+ trigger_optionsset_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),
@@ -1639,6 +1662,8 @@ int do_set(char_u *arg, int opt_flags)
}
}
xfree(saved_origval);
+ xfree(saved_origval_l);
+ xfree(saved_origval_g);
xfree(saved_newval);
// If error detected, print the error message.
@@ -2233,9 +2258,19 @@ static char *set_string_option(const int opt_idx, const char *const value, const
? OPT_GLOBAL : OPT_LOCAL)
: opt_flags));
char *const oldval = *varp;
+ char *oldval_l = NULL;
+ char *oldval_g = NULL;
+
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ oldval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
+ oldval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+ }
+
*varp = s;
char *const saved_oldval = xstrdup(oldval);
+ char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup((char *)oldval_l) : 0;
+ char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup((char *)oldval_g) : 0;
char *const saved_newval = xstrdup(s);
int value_checked = false;
@@ -2249,7 +2284,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const
// call autocommand after handling side effects
if (r == NULL) {
if (!starting) {
- trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval);
+ trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g,
+ saved_newval);
}
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
@@ -2257,6 +2293,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const
}
}
xfree(saved_oldval);
+ xfree(saved_oldval_l);
+ xfree(saved_oldval_g);
xfree(saved_newval);
return r;
@@ -3851,6 +3889,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
const int opt_flags)
{
int old_value = *(int *)varp;
+ int old_global_value = 0;
// Disallow changing some options from secure mode
if ((secure || sandbox != 0)
@@ -3858,6 +3897,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
return (char *)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 = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+ }
+
*(int *)varp = value; // set the new value
// Remember where the option was set.
set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
@@ -4134,20 +4180,35 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
// Don't do this while starting up or recursively.
if (!starting && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
char buf_old[2];
+ char buf_old_global[2];
char buf_new[2];
char buf_type[7];
- vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d",
- old_value ? true: false);
- vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d",
- value ? true: false);
+ vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true : false);
+ vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%d", old_global_value ? true : false);
+ vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true : false);
vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- apply_autocmds(EVENT_OPTIONSET,
- (char_u *)options[opt_idx].fullname,
- NULL, false, NULL);
+ if (opt_flags & OPT_LOCAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ }
+ if (opt_flags & OPT_GLOBAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1);
+ }
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1);
+ }
+ if (opt_flags & OPT_MODELINE) {
+ set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ }
+ apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL);
reset_v_option_vars();
}
@@ -4181,7 +4242,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
{
char *errmsg = NULL;
long old_value = *(long *)varp;
- long old_Rows = Rows; // remember old Rows
+ 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.
@@ -4190,6 +4252,13 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
return e_secure;
}
+ // Save the global value before changing anything. This is needed as for
+ // a global-only option setting the "local value" infact 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);
+ }
+
// Many number options assume their value is in the signed int range.
if (value < INT_MIN || value > INT_MAX) {
return e_invarg;
@@ -4534,19 +4603,36 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
// Don't do this while starting up, failure or recursively.
if (!starting && errmsg == NULL && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
char buf_old[NUMBUFLEN];
+ char buf_old_global[NUMBUFLEN];
char buf_new[NUMBUFLEN];
char buf_type[7];
vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
+ vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%ld", old_global_value);
vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value);
vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- apply_autocmds(EVENT_OPTIONSET,
- (char_u *)options[opt_idx].fullname,
- NULL, false, NULL);
+ if (opt_flags & OPT_LOCAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ }
+ if (opt_flags & OPT_GLOBAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1);
+ }
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1);
+ }
+ if (opt_flags & OPT_MODELINE) {
+ set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
+ }
+ apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL);
reset_v_option_vars();
}
@@ -4565,7 +4651,15 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
return (char *)errmsg;
}
-static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *newval)
+/// Trigger the OptionSet autocommand.
+/// "opt_idx" is the index of the option being set.
+/// "opt_flags" can be OPT_LOCAL etc.
+/// "oldval" the old value
+/// "oldval_l" the old local value (only non-NULL if global and local value are set)
+/// "oldval_g" the old global value (only non-NULL if global and local value are set)
+/// "newval" the new value
+static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l,
+ char *oldval_g, char *newval)
{
// Don't do this recursively.
if (oldval != NULL
@@ -4578,8 +4672,24 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval,
set_vim_var_string(VV_OPTION_OLD, oldval, -1);
set_vim_var_string(VV_OPTION_NEW, newval, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- apply_autocmds(EVENT_OPTIONSET,
- (char_u *)options[opt_idx].fullname, NULL, false, NULL);
+ if (opt_flags & OPT_LOCAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
+ }
+ if (opt_flags & OPT_GLOBAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1);
+ }
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1);
+ }
+ if (opt_flags & OPT_MODELINE) {
+ set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
+ }
+ apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL);
reset_v_option_vars();
}
}
@@ -4771,7 +4881,8 @@ static int findoption(const char *const arg)
/// @param stringval NULL when only checking existence
///
/// @returns:
-/// Number or Toggle option: 1, *numval gets value.
+/// Toggle option: 2, *numval gets value.
+/// Number option: 1, *numval gets value.
/// String option: 0, *stringval gets allocated string.
/// Hidden Number or Toggle option: -1.
/// hidden String option: -2.
@@ -4804,16 +4915,18 @@ int get_option_value(const char *name, long *numval, char_u **stringval, int opt
}
if (options[opt_idx].flags & P_NUM) {
*numval = *(long *)varp;
+ return 1;
+ }
+
+ // Special case: 'modified' is b_changed, but we also want to consider
+ // it set when 'ff' or 'fenc' changed.
+ if ((int *)varp == &curbuf->b_changed) {
+ *numval = curbufIsChanged();
} else {
- // Special case: 'modified' is b_changed, but we also want to consider
- // it set when 'ff' or 'fenc' changed.
- if ((int *)varp == &curbuf->b_changed) {
- *numval = curbufIsChanged();
- } else {
- *numval = (long)*(int *)varp; // NOLINT(whitespace/cast)
- }
+ *numval = (long)*(int *)varp; // NOLINT(whitespace/cast)
}
- return 1;
+
+ return 2;
}
// Returns the option attributes and its value. Unlike the above function it
@@ -4909,7 +5022,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
// only getting a pointer, no need to use aucmd_prepbuf()
curbuf = (buf_T *)from;
curwin->w_buffer = curbuf;
- varp = get_varp(p);
+ varp = get_varp_scope(p, OPT_LOCAL);
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
}
@@ -4917,7 +5030,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
win_T *save_curwin = curwin;
curwin = (win_T *)from;
curbuf = curwin->w_buffer;
- varp = get_varp(p);
+ varp = get_varp_scope(p, OPT_LOCAL);
curwin = save_curwin;
curbuf = curwin->w_buffer;
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index bec3bc9648..e67efb8ea0 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1760,13 +1760,14 @@ static char_u *regpiece(int *flagp)
break;
}
if (re_multi_type(peekchr()) != NOT_MULTI) {
- /* Can't have a multi follow a multi. */
- if (peekchr() == Magic('*'))
- sprintf((char *)IObuff, _("E61: Nested %s*"),
- reg_magic >= MAGIC_ON ? "" : "\\");
- else
- sprintf((char *)IObuff, _("E62: Nested %s%c"),
- reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr()));
+ // Can't have a multi follow a multi.
+ if (peekchr() == Magic('*')) {
+ snprintf((char *)IObuff, IOSIZE, _("E61: Nested %s*"),
+ reg_magic >= MAGIC_ON ? "" : "\\");
+ } else {
+ snprintf((char *)IObuff, IOSIZE, _("E62: Nested %s%c"),
+ reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr()));
+ }
EMSG_RET_NULL((char *)IObuff);
}
@@ -1926,11 +1927,11 @@ static char_u *regatom(int *flagp)
case Magic('{'):
case Magic('*'):
c = no_Magic(c);
- sprintf((char *)IObuff, _("E64: %s%c follows nothing"),
- (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL)
- ? "" : "\\", c);
+ snprintf((char *)IObuff, IOSIZE, _("E64: %s%c follows nothing"),
+ (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL)
+ ? "" : "\\", c);
EMSG_RET_NULL((char *)IObuff);
- /* NOTREACHED */
+ // NOTREACHED
case Magic('~'): /* previous substitute pattern */
if (reg_prev_sub != NULL) {
@@ -3152,8 +3153,8 @@ static int read_limits(long *minval, long *maxval)
regparse++; // Allow either \{...} or \{...\}
}
if (*regparse != '}') {
- sprintf((char *)IObuff, _("E554: Syntax error in %s{...}"),
- reg_magic == MAGIC_ALL ? "" : "\\");
+ snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"),
+ reg_magic == MAGIC_ALL ? "" : "\\");
EMSG_RET_FAIL((char *)IObuff);
}
@@ -7263,9 +7264,10 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags)
if (f) {
fprintf(f, "Syntax error in \"%s\"\n", expr);
fclose(f);
- } else
+ } else {
semsg("(NFA) Could not open \"%s\" to write !!!",
- BT_REGEXP_DEBUG_LOG_NAME);
+ BT_REGEXP_DEBUG_LOG_NAME);
+ }
}
#endif
// If the NFA engine failed, try the backtracking engine. The NFA engine
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 2e45a8f509..f47315705c 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -5248,6 +5248,9 @@ search_line:
if (depth == -1) {
// match in current file
if (l_g_do_tagpreview != 0) {
+ if (!win_valid(curwin_save)) {
+ break;
+ }
if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL,
NULL, true, lnum, false))) {
break; // failed to jump to file
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index d8eea7f942..fca73dc9f2 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -80,7 +80,7 @@ static signgroup_T *sign_group_ref(const char_u *groupname)
hi = hash_lookup(&sg_table, (char *)groupname, STRLEN(groupname), hash);
if (HASHITEM_EMPTY(hi)) {
// new group
- group = xmalloc((unsigned)(sizeof(signgroup_T) + STRLEN(groupname)));
+ group = xmalloc(sizeof(signgroup_T) + STRLEN(groupname));
STRCPY(group->sg_name, groupname);
group->sg_refcount = 1;
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 4eb0073873..71db25664f 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -136,7 +136,7 @@ int get_real_state(void)
/// @returns[allocated] mode string
char *get_mode(void)
{
- char *buf = xcalloc(4, sizeof(char));
+ char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char));
if (VIsual_active) {
if (VIsual_select) {
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index dd3f1b4dc9..cb243668ce 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -5401,7 +5401,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
do {
for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {
}
- char_u *const name = xmalloc((int)(end - p + 3)); // leave room for "^$"
+ char_u *const name = xmalloc(end - p + 3); // leave room for "^$"
STRLCPY(name + 1, p, end - p + 1);
if (STRCMP(name + 1, "ALLBUT") == 0
|| STRCMP(name + 1, "ALL") == 0
@@ -7573,7 +7573,7 @@ static bool syn_list_header(const bool did_header, const int outlen, const int i
// Show "xxx" with the attributes.
if (!did_header) {
if (endcol == Columns - 1 && endcol <= name_col) {
- msg_putchar(' ');
+ msg_putchar(' ');
}
msg_puts_attr("xxx", syn_id2attr(id));
msg_putchar(' ');
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 1f4d3adc92..483d2df778 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -141,12 +141,12 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc
/// type == DT_LTAG: use location list for displaying tag matches
/// type == DT_FREE: free cached matches
///
-/// for cscope, returns TRUE if we jumped to tag or aborted, FALSE otherwise
+/// for cscope, returns true if we jumped to tag or aborted, false otherwise
///
/// @param tag tag (pattern) to jump to
/// @param forceit :ta with !
/// @param verbose print "tag not found" message
-int do_tag(char_u *tag, int type, int count, int forceit, int verbose)
+bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
{
taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
@@ -163,7 +163,7 @@ int do_tag(char_u *tag, int type, int count, int forceit, int verbose)
int error_cur_match = 0;
int save_pos = false;
fmark_T saved_fmark;
- int jumped_to_tag = false;
+ bool jumped_to_tag = false;
int new_num_matches;
char_u **new_matches;
int use_tagstack;
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 35c68fa1f6..83ade74db1 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -324,10 +324,11 @@ void terminal_close(Terminal *term, int status)
}
if (buf && !is_autocmd_blocked()) {
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
tv_dict_add_nr(dict, S_LEN("status"), status);
apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf);
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
}
}
@@ -412,6 +413,7 @@ void terminal_enter(void)
curwin->w_redr_status = true; // For mode() in statusline. #8323
ui_busy_start();
apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf);
+ trigger_modechanged();
s->state.execute = terminal_execute;
s->state.check = terminal_check;
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 27b3eedfa1..4e1a24af61 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -33,7 +33,7 @@ if has('timers')
let g:triggered = 0
au CursorHoldI * let g:triggered += 1
set updatetime=20
- call timer_start(LoadAdjust(100), 'ExitInsertMode')
+ call timer_start(LoadAdjust(200), 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
unlet g:triggered
@@ -579,9 +579,10 @@ func Test_empty_doau()
endfunc
func s:AutoCommandOptionSet(match)
+ let template = "Option: <%s>, OldVal: <%s>, OldValLocal: <%s>, OldValGlobal: <%s>, NewVal: <%s>, Scope: <%s>, Command: <%s>\n"
let item = remove(g:options, 0)
- let expected = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", item[0], item[1], item[2], item[3])
- let actual = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", a:match, v:option_old, v:option_new, v:option_type)
+ let expected = printf(template, item[0], item[1], item[2], item[3], item[4], item[5], item[6])
+ let actual = printf(template, a:match, v:option_old, v:option_oldlocal, v:option_oldglobal, v:option_new, v:option_type, v:option_command)
let g:opt = [expected, actual]
"call assert_equal(expected, actual)
endfunc
@@ -595,130 +596,593 @@ func Test_OptionSet()
au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>"))
" 1: Setting number option"
- let g:options = [['number', 0, 1, 'global']]
+ let g:options = [['number', 0, 0, 0, 1, 'global', 'set']]
set nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 2: Setting local number option"
- let g:options = [['number', 1, 0, 'local']]
+ let g:options = [['number', 1, 1, '', 0, 'local', 'setlocal']]
setlocal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 3: Setting global number option"
- let g:options = [['number', 1, 0, 'global']]
+ let g:options = [['number', 1, '', 1, 0, 'global', 'setglobal']]
setglobal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 4: Setting local autoindent option"
- let g:options = [['autoindent', 0, 1, 'local']]
+ let g:options = [['autoindent', 0, 0, '', 1, 'local', 'setlocal']]
setlocal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 5: Setting global autoindent option"
- let g:options = [['autoindent', 0, 1, 'global']]
+ let g:options = [['autoindent', 0, '', 0, 1, 'global', 'setglobal']]
setglobal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 6: Setting global autoindent option"
- let g:options = [['autoindent', 1, 0, 'global']]
+ let g:options = [['autoindent', 1, 1, 1, 0, 'global', 'set']]
+ set ai!
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 6a: Setting global autoindent option"
+ let g:options = [['autoindent', 1, 1, 0, 0, 'global', 'set']]
+ noa setlocal ai
+ noa setglobal noai
set ai!
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" Should not print anything, use :noa
" 7: don't trigger OptionSet"
- let g:options = [['invalid', 1, 1, 'invalid']]
+ let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']]
noa set nonu
- call assert_equal([['invalid', 1, 1, 'invalid']], g:options)
+ call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 8: Setting several global list and number option"
- let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']]
+ let g:options = [['list', 0, 0, 0, 1, 'global', 'set'], ['number', 0, 0, 0, 1, 'global', 'set']]
set list nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 9: don't trigger OptionSet"
- let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']]
+ let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']]
noa set nolist nonu
- call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options)
+ call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 10: Setting global acd"
- let g:options = [['autochdir', 0, 1, 'local']]
+ let g:options = [['autochdir', 0, 0, '', 1, 'local', 'setlocal']]
setlocal acd
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 11: Setting global autoread (also sets local value)"
- let g:options = [['autoread', 0, 1, 'global']]
+ let g:options = [['autoread', 0, 0, 0, 1, 'global', 'set']]
set ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 12: Setting local autoread"
- let g:options = [['autoread', 1, 1, 'local']]
+ let g:options = [['autoread', 1, 1, '', 1, 'local', 'setlocal']]
setlocal ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 13: Setting global autoread"
- let g:options = [['autoread', 1, 0, 'global']]
+ let g:options = [['autoread', 1, '', 1, 0, 'global', 'setglobal']]
setglobal invar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 14: Setting option backspace through :let"
- let g:options = [['backspace', '', 'eol,indent,start', 'global']]
+ let g:options = [['backspace', '', '', '', 'eol,indent,start', 'global', 'set']]
let &bs = "eol,indent,start"
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 15: Setting option backspace through setbufvar()"
- let g:options = [['backup', 0, 1, 'local']]
+ let g:options = [['backup', 0, 0, '', 1, 'local', 'setlocal']]
" try twice, first time, shouldn't trigger because option name is invalid,
" second time, it should trigger
- call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355")
+ let bnum = bufnr('%')
+ call assert_fails("call setbufvar(bnum, '&l:bk', 1)", 'E355:')
" should trigger, use correct option name
- call setbufvar(1, '&backup', 1)
+ call setbufvar(bnum, '&backup', 1)
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 16: Setting number option using setwinvar"
- let g:options = [['number', 0, 1, 'local']]
+ let g:options = [['number', 0, 0, '', 1, 'local', 'setlocal']]
call setwinvar(0, '&number', 1)
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 17: Setting key option, shouldn't trigger"
- let g:options = [['key', 'invalid', 'invalid1', 'invalid']]
+ let g:options = [['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']]
setlocal key=blah
setlocal key=
- call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options)
+ call assert_equal([['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 18a: Setting string global option"
+ let oldval = &backupext
+ let g:options = [['backupext', oldval, oldval, oldval, 'foo', 'global', 'set']]
+ set backupext=foo
+ call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
- " 18: Setting string option"
+ " 18b: Resetting string global option"
+ let g:options = [['backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set']]
+ set backupext&
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 18c: Setting global string global option"
+ let g:options = [['backupext', oldval, '', oldval, 'bar', 'global', 'setglobal']]
+ setglobal backupext=bar
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 18d: Setting local string global option"
+ " As this is a global option this sets the global value even though
+ " :setlocal is used!
+ noa set backupext& " Reset global and local value (without triggering autocmd)
+ let g:options = [['backupext', oldval, oldval, '', 'baz', 'local', 'setlocal']]
+ setlocal backupext=baz
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 18e: Setting again string global option"
+ noa setglobal backupext=ext_global " Reset global and local value (without triggering autocmd)
+ noa setlocal backupext=ext_local " Sets the global(!) value!
+ let g:options = [['backupext', 'ext_local', 'ext_local', 'ext_local', 'fuu', 'global', 'set']]
+ set backupext=fuu
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 19a: Setting string global-local (to buffer) option"
let oldval = &tags
- let g:options = [['tags', oldval, 'tagpath', 'global']]
+ let g:options = [['tags', oldval, oldval, oldval, 'tagpath', 'global', 'set']]
set tags=tagpath
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
- " 1l: Resetting string option"
- let g:options = [['tags', 'tagpath', oldval, 'global']]
+ " 19b: Resetting string global-local (to buffer) option"
+ let g:options = [['tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set']]
set tags&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
+ " 19c: Setting global string global-local (to buffer) option "
+ let g:options = [['tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal']]
+ setglobal tags=tagpath1
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 19d: Setting local string global-local (to buffer) option"
+ let g:options = [['tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal']]
+ setlocal tags=tagpath2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 19e: Setting again string global-local (to buffer) option"
+ " Note: v:option_old is the old global value for global-local string options
+ " but the old local value for all other kinds of options.
+ noa setglobal tags=tag_global " Reset global and local value (without triggering autocmd)
+ noa setlocal tags=tag_local
+ let g:options = [['tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set']]
+ set tags=tagpath
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 19f: Setting string global-local (to buffer) option to an empty string"
+ " Note: v:option_old is the old global value for global-local string options
+ " but the old local value for all other kinds of options.
+ noa set tags=tag_global " Reset global and local value (without triggering autocmd)
+ noa setlocal tags= " empty string
+ let g:options = [['tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set']]
+ set tags=tagpath
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 20a: Setting string local (to buffer) option"
+ let oldval = &spelllang
+ let g:options = [['spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set']]
+ set spelllang=elvish,klingon
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 20b: Resetting string local (to buffer) option"
+ let g:options = [['spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set']]
+ set spelllang&
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 20c: Setting global string local (to buffer) option"
+ let g:options = [['spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal']]
+ setglobal spelllang=elvish
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 20d: Setting local string local (to buffer) option"
+ noa set spelllang& " Reset global and local value (without triggering autocmd)
+ let g:options = [['spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal']]
+ setlocal spelllang=klingon
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 20e: Setting again string local (to buffer) option"
+ " Note: v:option_old is the old global value for global-local string options
+ " but the old local value for all other kinds of options.
+ noa setglobal spelllang=spellglobal " Reset global and local value (without triggering autocmd)
+ noa setlocal spelllang=spelllocal
+ let g:options = [['spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set']]
+ set spelllang=foo
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 21a: Setting string global-local (to window) option"
+ let oldval = &statusline
+ let g:options = [['statusline', oldval, oldval, oldval, 'foo', 'global', 'set']]
+ set statusline=foo
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 21b: Resetting string global-local (to window) option"
+ " Note: v:option_old is the old global value for global-local string options
+ " but the old local value for all other kinds of options.
+ let g:options = [['statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set']]
+ set statusline&
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 21c: Setting global string global-local (to window) option"
+ let g:options = [['statusline', oldval, '', oldval, 'bar', 'global', 'setglobal']]
+ setglobal statusline=bar
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 21d: Setting local string global-local (to window) option"
+ noa set statusline& " Reset global and local value (without triggering autocmd)
+ let g:options = [['statusline', oldval, oldval, '', 'baz', 'local', 'setlocal']]
+ setlocal statusline=baz
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 21e: Setting again string global-local (to window) option"
+ " Note: v:option_old is the old global value for global-local string options
+ " but the old local value for all other kinds of options.
+ noa setglobal statusline=bar " Reset global and local value (without triggering autocmd)
+ noa setlocal statusline=baz
+ let g:options = [['statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set']]
+ set statusline=foo
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 22a: Setting string local (to window) option"
+ let oldval = &foldignore
+ let g:options = [['foldignore', oldval, oldval, oldval, 'fo', 'global', 'set']]
+ set foldignore=fo
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 22b: Resetting string local (to window) option"
+ let g:options = [['foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set']]
+ set foldignore&
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 22c: Setting global string local (to window) option"
+ let g:options = [['foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal']]
+ setglobal foldignore=bar
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 22d: Setting local string local (to window) option"
+ noa set foldignore& " Reset global and local value (without triggering autocmd)
+ let g:options = [['foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal']]
+ setlocal foldignore=baz
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 22e: Setting again string local (to window) option"
+ noa setglobal foldignore=glob " Reset global and local value (without triggering autocmd)
+ noa setlocal foldignore=loc
+ let g:options = [['foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set']]
+ set foldignore=fo
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 23a: Setting global number global option"
+ noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal cmdheight=1 " Sets the global(!) value!
+ let g:options = [['cmdheight', '1', '', '1', '2', 'global', 'setglobal']]
+ setglobal cmdheight=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 23b: Setting local number global option"
+ noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal cmdheight=1 " Sets the global(!) value!
+ let g:options = [['cmdheight', '1', '1', '', '2', 'local', 'setlocal']]
+ setlocal cmdheight=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 23c: Setting again number global option"
+ noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal cmdheight=1 " Sets the global(!) value!
+ let g:options = [['cmdheight', '1', '1', '1', '2', 'global', 'set']]
+ set cmdheight=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 23d: Setting again number global option"
+ noa set cmdheight=8 " Reset global and local value (without triggering autocmd)
+ let g:options = [['cmdheight', '8', '8', '8', '2', 'global', 'set']]
+ set cmdheight=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 24a: Setting global number global-local (to buffer) option"
+ noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal undolevels=1
+ let g:options = [['undolevels', '8', '', '8', '2', 'global', 'setglobal']]
+ setglobal undolevels=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 24b: Setting local number global-local (to buffer) option"
+ noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal undolevels=1
+ let g:options = [['undolevels', '1', '1', '', '2', 'local', 'setlocal']]
+ setlocal undolevels=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 24c: Setting again number global-local (to buffer) option"
+ noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal undolevels=1
+ let g:options = [['undolevels', '1', '1', '8', '2', 'global', 'set']]
+ set undolevels=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 24d: Setting again global number global-local (to buffer) option"
+ noa set undolevels=8 " Reset global and local value (without triggering autocmd)
+ let g:options = [['undolevels', '8', '8', '8', '2', 'global', 'set']]
+ set undolevels=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 25a: Setting global number local (to buffer) option"
+ noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal wrapmargin=1
+ let g:options = [['wrapmargin', '8', '', '8', '2', 'global', 'setglobal']]
+ setglobal wrapmargin=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 25b: Setting local number local (to buffer) option"
+ noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal wrapmargin=1
+ let g:options = [['wrapmargin', '1', '1', '', '2', 'local', 'setlocal']]
+ setlocal wrapmargin=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 25c: Setting again number local (to buffer) option"
+ noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal wrapmargin=1
+ let g:options = [['wrapmargin', '1', '1', '8', '2', 'global', 'set']]
+ set wrapmargin=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 25d: Setting again global number local (to buffer) option"
+ noa set wrapmargin=8 " Reset global and local value (without triggering autocmd)
+ let g:options = [['wrapmargin', '8', '8', '8', '2', 'global', 'set']]
+ set wrapmargin=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 26: Setting number global-local (to window) option.
+ " Such option does currently not exist.
+
+
+ " 27a: Setting global number local (to window) option"
+ noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal foldcolumn=1
+ let g:options = [['foldcolumn', '8', '', '8', '2', 'global', 'setglobal']]
+ setglobal foldcolumn=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 27b: Setting local number local (to window) option"
+ noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal foldcolumn=1
+ let g:options = [['foldcolumn', '1', '1', '', '2', 'local', 'setlocal']]
+ setlocal foldcolumn=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 27c: Setting again number local (to window) option"
+ noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd)
+ noa setlocal foldcolumn=1
+ let g:options = [['foldcolumn', '1', '1', '8', '2', 'global', 'set']]
+ set foldcolumn=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 27d: Setting again global number local (to window) option"
+ noa set foldcolumn=8 " Reset global and local value (without triggering autocmd)
+ let g:options = [['foldcolumn', '8', '8', '8', '2', 'global', 'set']]
+ set foldcolumn=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 28a: Setting global boolean global option"
+ noa setglobal nowrapscan " Reset global and local value (without triggering autocmd)
+ noa setlocal wrapscan " Sets the global(!) value!
+ let g:options = [['wrapscan', '1', '', '1', '0', 'global', 'setglobal']]
+ setglobal nowrapscan
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 28b: Setting local boolean global option"
+ noa setglobal nowrapscan " Reset global and local value (without triggering autocmd)
+ noa setlocal wrapscan " Sets the global(!) value!
+ let g:options = [['wrapscan', '1', '1', '', '0', 'local', 'setlocal']]
+ setlocal nowrapscan
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 28c: Setting again boolean global option"
+ noa setglobal nowrapscan " Reset global and local value (without triggering autocmd)
+ noa setlocal wrapscan " Sets the global(!) value!
+ let g:options = [['wrapscan', '1', '1', '1', '0', 'global', 'set']]
+ set nowrapscan
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 28d: Setting again global boolean global option"
+ noa set nowrapscan " Reset global and local value (without triggering autocmd)
+ let g:options = [['wrapscan', '0', '0', '0', '1', 'global', 'set']]
+ set wrapscan
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 29a: Setting global boolean global-local (to buffer) option"
+ noa setglobal noautoread " Reset global and local value (without triggering autocmd)
+ noa setlocal autoread
+ let g:options = [['autoread', '0', '', '0', '1', 'global', 'setglobal']]
+ setglobal autoread
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 29b: Setting local boolean global-local (to buffer) option"
+ noa setglobal noautoread " Reset global and local value (without triggering autocmd)
+ noa setlocal autoread
+ let g:options = [['autoread', '1', '1', '', '0', 'local', 'setlocal']]
+ setlocal noautoread
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 29c: Setting again boolean global-local (to buffer) option"
+ noa setglobal noautoread " Reset global and local value (without triggering autocmd)
+ noa setlocal autoread
+ let g:options = [['autoread', '1', '1', '0', '1', 'global', 'set']]
+ set autoread
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 29d: Setting again global boolean global-local (to buffer) option"
+ noa set noautoread " Reset global and local value (without triggering autocmd)
+ let g:options = [['autoread', '0', '0', '0', '1', 'global', 'set']]
+ set autoread
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 30a: Setting global boolean local (to buffer) option"
+ noa setglobal nocindent " Reset global and local value (without triggering autocmd)
+ noa setlocal cindent
+ let g:options = [['cindent', '0', '', '0', '1', 'global', 'setglobal']]
+ setglobal cindent
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 30b: Setting local boolean local (to buffer) option"
+ noa setglobal nocindent " Reset global and local value (without triggering autocmd)
+ noa setlocal cindent
+ let g:options = [['cindent', '1', '1', '', '0', 'local', 'setlocal']]
+ setlocal nocindent
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 30c: Setting again boolean local (to buffer) option"
+ noa setglobal nocindent " Reset global and local value (without triggering autocmd)
+ noa setlocal cindent
+ let g:options = [['cindent', '1', '1', '0', '1', 'global', 'set']]
+ set cindent
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 30d: Setting again global boolean local (to buffer) option"
+ noa set nocindent " Reset global and local value (without triggering autocmd)
+ let g:options = [['cindent', '0', '0', '0', '1', 'global', 'set']]
+ set cindent
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 31: Setting boolean global-local (to window) option
+ " Currently no such option exists.
+
+
+ " 32a: Setting global boolean local (to window) option"
+ noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd)
+ noa setlocal cursorcolumn
+ let g:options = [['cursorcolumn', '0', '', '0', '1', 'global', 'setglobal']]
+ setglobal cursorcolumn
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 32b: Setting local boolean local (to window) option"
+ noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd)
+ noa setlocal cursorcolumn
+ let g:options = [['cursorcolumn', '1', '1', '', '0', 'local', 'setlocal']]
+ setlocal nocursorcolumn
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 32c: Setting again boolean local (to window) option"
+ noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd)
+ noa setlocal cursorcolumn
+ let g:options = [['cursorcolumn', '1', '1', '0', '1', 'global', 'set']]
+ set cursorcolumn
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+ " 32d: Setting again global boolean local (to window) option"
+ noa set nocursorcolumn " Reset global and local value (without triggering autocmd)
+ let g:options = [['cursorcolumn', '0', '0', '0', '1', 'global', 'set']]
+ set cursorcolumn
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
+ " 33: Test autocommands when an option value is converted internally.
+ noa set backspace=1 " Reset global and local value (without triggering autocmd)
+ let g:options = [['backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set']]
+ set backspace=2
+ call assert_equal([], g:options)
+ call assert_equal(g:opt[0], g:opt[1])
+
+
" Cleanup
au! OptionSet
" set tags&
- for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'tags']
+ for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'backupext', 'tags', 'spelllang', 'statusline', 'foldignore', 'cmdheight', 'undolevels', 'wrapmargin', 'foldcolumn', 'wrapscan', 'autoread', 'cindent', 'cursorcolumn']
exe printf(":set %s&vim", opt)
endfor
call test_override('starting', 0)
@@ -1896,6 +2360,26 @@ func Test_autocmd_CmdWinEnter()
call delete(filename)
endfunc
+func Test_autocmd_was_using_freed_memory()
+ pedit xx
+ n x
+ augroup winenter
+ au WinEnter * if winnr('$') > 2 | quit | endif
+ augroup END
+ " Nvim needs large 'winwidth' and 'nowinfixwidth' to crash
+ set winwidth=99999 nowinfixwidth
+ split
+
+ augroup winenter
+ au! WinEnter
+ augroup END
+
+ set winwidth& winfixwidth&
+ bwipe xx
+ bwipe x
+ pclose
+endfunc
+
func Test_FileChangedShell_reload()
if !has('unix')
return
@@ -2124,6 +2608,19 @@ func Test_autocmd_closes_window()
au! BufWinLeave
endfunc
+func Test_autocmd_quit_psearch()
+ sn aa bb
+ augroup aucmd_win_test
+ au!
+ au BufEnter,BufLeave,BufNew,WinEnter,WinLeave,WinNew * if winnr('$') > 1 | q | endif
+ augroup END
+ ps /
+
+ augroup aucmd_win_test
+ au!
+ augroup END
+endfunc
+
func Test_autocmd_closing_cmdwin()
au BufWinLeave * nested q
call assert_fails("norm 7q?\n", 'E855:')
diff --git a/src/nvim/testdir/test_blockedit.vim b/src/nvim/testdir/test_blockedit.vim
index 180524cd73..38978ef689 100644
--- a/src/nvim/testdir/test_blockedit.vim
+++ b/src/nvim/testdir/test_blockedit.vim
@@ -15,6 +15,58 @@ func Test_blockinsert_indent()
bwipe!
endfunc
+func Test_blockinsert_autoindent()
+ new
+ let lines =<< trim END
+ var d = {
+ a: () => 0,
+ b: () => 0,
+ c: () => 0,
+ }
+ END
+ call setline(1, lines)
+ filetype plugin indent on
+ setlocal sw=2 et ft=vim
+ setlocal indentkeys+=:
+ exe "norm! 2Gf)\<c-v>2jA: asdf\<esc>"
+ let expected =<< trim END
+ var d = {
+ a: (): asdf => 0,
+ b: (): asdf => 0,
+ c: (): asdf => 0,
+ }
+ END
+ call assert_equal(expected, getline(1, 5))
+
+ " insert on the next column should do exactly the same
+ :%dele
+ call setline(1, lines)
+ exe "norm! 2Gf)l\<c-v>2jI: asdf\<esc>"
+ call assert_equal(expected, getline(1, 5))
+
+ :%dele
+ call setline(1, lines)
+ setlocal sw=8 noet
+ exe "norm! 2Gf)\<c-v>2jA: asdf\<esc>"
+ let expected =<< trim END
+ var d = {
+ a: (): asdf => 0,
+ b: (): asdf => 0,
+ c: (): asdf => 0,
+ }
+ END
+ call assert_equal(expected, getline(1, 5))
+
+ " insert on the next column should do exactly the same
+ :%dele
+ call setline(1, lines)
+ exe "norm! 2Gf)l\<c-v>2jI: asdf\<esc>"
+ call assert_equal(expected, getline(1, 5))
+
+ filetype off
+ bwipe!
+endfunc
+
func Test_blockinsert_delete()
new
let _bs = &bs
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 23ad8dbfc5..37786f3ca0 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1644,4 +1644,95 @@ func Test_read_invalid()
set encoding=utf-8
endfunc
+" Test for ModeChanged pattern
+func Test_mode_changes()
+ let g:index = 0
+ let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n']
+ func! TestMode()
+ call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode"))
+ call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode"))
+ call assert_equal(mode(1), get(v:event, "new_mode"))
+ let g:index += 1
+ endfunc
+
+ au ModeChanged * :call TestMode()
+ let g:n_to_any = 0
+ au ModeChanged n:* let g:n_to_any += 1
+ call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix')
+
+ let g:V_to_v = 0
+ au ModeChanged V:v let g:V_to_v += 1
+ call feedkeys("Vv\<C-G>\<esc>", 'tnix')
+ call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any)
+ call assert_equal(1, g:V_to_v)
+ call assert_equal(len(g:mode_seq) - 1, g:index)
+
+ let g:n_to_i = 0
+ au ModeChanged n:i let g:n_to_i += 1
+ let g:n_to_niI = 0
+ au ModeChanged i:niI let g:n_to_niI += 1
+ let g:niI_to_i = 0
+ au ModeChanged niI:i let g:niI_to_i += 1
+ let g:nany_to_i = 0
+ au ModeChanged n*:i let g:nany_to_i += 1
+ let g:i_to_n = 0
+ au ModeChanged i:n let g:i_to_n += 1
+ let g:nori_to_any = 0
+ au ModeChanged [ni]:* let g:nori_to_any += 1
+ let g:i_to_any = 0
+ au ModeChanged i:* let g:i_to_any += 1
+ let g:index = 0
+ let g:mode_seq = ['n', 'i', 'niI', 'i', 'n']
+ call feedkeys("a\<C-O>l\<esc>", 'tnix')
+ call assert_equal(len(g:mode_seq) - 1, g:index)
+ call assert_equal(1, g:n_to_i)
+ call assert_equal(1, g:n_to_niI)
+ call assert_equal(1, g:niI_to_i)
+ call assert_equal(2, g:nany_to_i)
+ call assert_equal(1, g:i_to_n)
+ call assert_equal(2, g:i_to_any)
+ call assert_equal(3, g:nori_to_any)
+
+ if has('terminal')
+ let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n']
+ call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix')
+ call assert_equal(len(g:mode_seq) - 1, g:index)
+ call assert_equal(1, g:n_to_i)
+ call assert_equal(1, g:n_to_niI)
+ call assert_equal(1, g:niI_to_i)
+ call assert_equal(2, g:nany_to_i)
+ call assert_equal(1, g:i_to_n)
+ call assert_equal(2, g:i_to_any)
+ call assert_equal(5, g:nori_to_any)
+ endif
+
+ au! ModeChanged
+ delfunc TestMode
+ unlet! g:mode_seq
+ unlet! g:index
+ unlet! g:n_to_any
+ unlet! g:V_to_v
+ unlet! g:n_to_i
+ unlet! g:n_to_niI
+ unlet! g:niI_to_i
+ unlet! g:nany_to_i
+ unlet! g:i_to_n
+ unlet! g:nori_to_any
+ unlet! g:i_to_any
+endfunc
+
+func Test_recursive_ModeChanged()
+ au! ModeChanged * norm 0u
+ sil! norm 
+ au!
+endfunc
+
+func Test_ModeChanged_starts_visual()
+ " This was triggering ModeChanged before setting VIsual, causing a crash.
+ au! ModeChanged * norm 0u
+ sil! norm 
+
+ au! ModeChanged
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 7560303e22..69edbc227d 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -59,7 +59,7 @@ let s:filename_checks = {
\ 'aml': ['file.aml'],
\ 'ampl': ['file.run'],
\ 'ant': ['build.xml'],
- \ 'apache': ['.htaccess', '/etc/httpd/file.conf', '/etc/apache2/sites-2/file.com', '/etc/apache2/some.config', '/etc/apache2/conf.file/conf', '/etc/apache2/mods-some/file', '/etc/apache2/sites-some/file', '/etc/httpd/conf.d/file.config', '/etc/apache2/conf.file/file', '/etc/apache2/file.conf', '/etc/apache2/file.conf-file', '/etc/apache2/mods-file/file', '/etc/apache2/sites-file/file', '/etc/apache2/sites-file/file.com', '/etc/httpd/conf.d/file.conf', '/etc/httpd/conf.d/file.conf-file', 'access.conf', 'access.conf-file', 'any/etc/apache2/conf.file/file', 'any/etc/apache2/file.conf', 'any/etc/apache2/file.conf-file', 'any/etc/apache2/mods-file/file', 'any/etc/apache2/sites-file/file', 'any/etc/apache2/sites-file/file.com', 'any/etc/httpd/conf.d/file.conf', 'any/etc/httpd/conf.d/file.conf-file', 'any/etc/httpd/file.conf', 'apache.conf', 'apache.conf-file', 'apache2.conf', 'apache2.conf-file', 'httpd.conf', 'httpd.conf-file', 'srm.conf', 'srm.conf-file'],
+ \ 'apache': ['.htaccess', '/etc/httpd/file.conf', '/etc/apache2/sites-2/file.com', '/etc/apache2/some.config', '/etc/apache2/conf.file/conf', '/etc/apache2/mods-some/file', '/etc/apache2/sites-some/file', '/etc/httpd/conf.d/file.config', '/etc/apache2/conf.file/file', '/etc/apache2/file.conf', '/etc/apache2/file.conf-file', '/etc/apache2/mods-file/file', '/etc/apache2/sites-file/file', '/etc/apache2/sites-file/file.com', '/etc/httpd/conf.d/file.conf', '/etc/httpd/conf.d/file.conf-file', 'access.conf', 'access.conf-file', 'any/etc/apache2/conf.file/file', 'any/etc/apache2/file.conf', 'any/etc/apache2/file.conf-file', 'any/etc/apache2/mods-file/file', 'any/etc/apache2/sites-file/file', 'any/etc/apache2/sites-file/file.com', 'any/etc/httpd/conf.d/file.conf', 'any/etc/httpd/conf.d/file.conf-file', 'any/etc/httpd/file.conf', 'apache.conf', 'apache.conf-file', 'apache2.conf', 'apache2.conf-file', 'httpd.conf', 'httpd.conf-file', 'srm.conf', 'srm.conf-file', '/etc/httpd/mods-some/file', '/etc/httpd/sites-some/file', '/etc/httpd/conf.file/conf'],
\ 'apachestyle': ['/etc/proftpd/file.config,/etc/proftpd/conf.file/file', '/etc/proftpd/conf.file/file', '/etc/proftpd/file.conf', '/etc/proftpd/file.conf-file', 'any/etc/proftpd/conf.file/file', 'any/etc/proftpd/file.conf', 'any/etc/proftpd/file.conf-file', 'proftpd.conf', 'proftpd.conf-file'],
\ 'applescript': ['file.scpt'],
\ 'aptconf': ['apt.conf', '/.aptitude/config', 'any/.aptitude/config'],
@@ -142,7 +142,7 @@ let s:filename_checks = {
\ 'desc': ['file.desc'],
\ 'desktop': ['file.desktop', '.directory', 'file.directory'],
\ 'dictconf': ['dict.conf', '.dictrc'],
- \ 'dictdconf': ['dictd.conf'],
+ \ 'dictdconf': ['dictd.conf', 'dictdfile.conf', 'dictd-file.conf'],
\ 'diff': ['file.diff', 'file.rej'],
\ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS', 'any/etc/DIR_COLORS'],
\ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'],
@@ -182,11 +182,12 @@ let s:filename_checks = {
\ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'],
\ 'fish': ['file.fish'],
\ 'focexec': ['file.fex', 'file.focexec'],
- \ 'forth': ['file.fs', 'file.ft', 'file.fth'],
+ \ 'forth': ['file.ft', 'file.fth'],
\ 'fortran': ['file.f', 'file.for', 'file.fortran', 'file.fpp', 'file.ftn', 'file.f77', 'file.f90', 'file.f95', 'file.f03', 'file.f08'],
\ 'fpcmake': ['file.fpc'],
\ 'framescript': ['file.fsl'],
\ 'freebasic': ['file.fb', 'file.bi'],
+ \ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'],
\ 'fstab': ['fstab', 'mtab'],
\ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'],
\ 'gdb': ['.gdbinit', 'gdbinit'],
@@ -338,7 +339,7 @@ let s:filename_checks = {
\ 'msql': ['file.msql'],
\ 'mupad': ['file.mu'],
\ 'mush': ['file.mush'],
- \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'],
+ \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', '/etc/Muttrc.d/file.rc', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'],
\ 'mysql': ['file.mysql'],
\ 'n1ql': ['file.n1ql', 'file.nql'],
\ 'named': ['namedfile.conf', 'rndcfile.conf', 'named-file.conf', 'named.conf', 'rndc-file.conf', 'rndc-file.key', 'rndc.conf', 'rndc.key'],
@@ -475,6 +476,7 @@ let s:filename_checks = {
\ 'sqlj': ['file.sqlj'],
\ 'sqr': ['file.sqr', 'file.sqi'],
\ 'squid': ['squid.conf'],
+ \ 'squirrel': ['file.nut'],
\ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'],
\ 'sshconfig': ['ssh_config', '/.ssh/config', '/etc/ssh/ssh_config.d/file.conf', 'any/etc/ssh/ssh_config.d/file.conf', 'any/.ssh/config'],
\ 'sshdconfig': ['sshd_config', '/etc/ssh/sshd_config.d/file.conf', 'any/etc/ssh/sshd_config.d/file.conf'],
@@ -500,7 +502,7 @@ let s:filename_checks = {
\ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'],
\ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'],
\ 'texmf': ['texmf.cnf'],
- \ 'text': ['file.text', 'README', '/usr/share/doc/bash-completion/AUTHORS'],
+ \ 'text': ['file.text', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'],
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
\ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
@@ -553,7 +555,7 @@ let s:filename_checks = {
\ 'xhtml': ['file.xhtml', 'file.xht'],
\ 'xinetd': ['/etc/xinetd.conf', '/etc/xinetd.d/file', 'any/etc/xinetd.conf', 'any/etc/xinetd.d/file'],
\ 'xmath': ['file.msc', 'file.msf'],
- \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu'],
+ \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.fsproj', 'file.fsproj.user', 'file.vbproj', 'file.vbproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1', 'file.mpd'],
\ 'xmodmap': ['anyXmodmap', 'Xmodmap', 'some-Xmodmap', 'some-xmodmap', 'some-xmodmap-file', 'xmodmap', 'xmodmap-file'],
\ 'xf86conf': ['xorg.conf', 'xorg.conf-4'],
\ 'xpm': ['file.xpm'],
@@ -663,6 +665,7 @@ let s:script_checks = {
\ 'fennel': [['#!/path/fennel']],
\ 'routeros': [['#!/path/rsc']],
\ 'fish': [['#!/path/fish']],
+ \ 'forth': [['#!/path/gforth']],
\ }
" Various forms of "env" optional arguments.
@@ -945,4 +948,57 @@ func Test_xpm_file()
filetype off
endfunc
+func Test_fs_file()
+ filetype on
+
+ call writefile(['looks like F#'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('fsharp', &filetype)
+ bwipe!
+
+ let g:filetype_fs = 'forth'
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+ unlet g:filetype_fs
+
+ " Test dist#ft#FTfs()
+
+ " Forth (Gforth)
+
+ call writefile(['( Forth inline comment )'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile(['.( Forth displayed inline comment )'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile(['\ Forth line comment'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ " empty line comment - no space required
+ call writefile(['\'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile(['\G Forth documentation comment '], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile([': squared ( n -- n^2 )', 'dup * ;'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call delete('Xfile.fs')
+ filetype off
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 726670f082..e3539c1a57 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -72,6 +72,8 @@ enum { NUMBUFLEN = 65, };
#define TERM_FOCUS 0x2000 // Terminal focus mode
#define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview.
+#define MODE_MAX_LENGTH 4 // max mode length returned in mode()
+
// all mode bits used for mapping
#define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS)
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index 800ecf10db..ba6cfab98b 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -204,40 +204,40 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
} \
} while (0)
switch (schar) {
- // Paired brackets.
+ // Paired brackets.
#define BRACKET(typ, opning, clsing) \
-case opning: \
-case clsing: { \
- ret.type = typ; \
- ret.data.brc.closing = (schar == clsing); \
- break; \
-}
- BRACKET(kExprLexParenthesis, '(', ')')
- BRACKET(kExprLexBracket, '[', ']')
- BRACKET(kExprLexFigureBrace, '{', '}')
+ case opning: \
+ case clsing: { \
+ ret.type = typ; \
+ ret.data.brc.closing = (schar == clsing); \
+ break; \
+ }
+ BRACKET(kExprLexParenthesis, '(', ')')
+ BRACKET(kExprLexBracket, '[', ']')
+ BRACKET(kExprLexFigureBrace, '{', '}')
#undef BRACKET
- // Single character tokens without data.
+ // Single character tokens without data.
#define CHAR(typ, ch) \
-case ch: { \
- ret.type = typ; \
- break; \
-}
- CHAR(kExprLexQuestion, '?')
- CHAR(kExprLexColon, ':')
- CHAR(kExprLexComma, ',')
+ case ch: { \
+ ret.type = typ; \
+ break; \
+ }
+ CHAR(kExprLexQuestion, '?')
+ CHAR(kExprLexColon, ':')
+ CHAR(kExprLexComma, ',')
#undef CHAR
- // Multiplication/division/modulo.
+ // Multiplication/division/modulo.
#define MUL(mul_type, ch) \
-case ch: { \
- ret.type = kExprLexMultiplication; \
- ret.data.mul.type = mul_type; \
- break; \
-}
- MUL(kExprLexMulMul, '*')
- MUL(kExprLexMulDiv, '/')
- MUL(kExprLexMulMod, '%')
+ case ch: { \
+ ret.type = kExprLexMultiplication; \
+ ret.data.mul.type = mul_type; \
+ break; \
+ }
+ MUL(kExprLexMulMul, '*')
+ MUL(kExprLexMulDiv, '/')
+ MUL(kExprLexMulMod, '%')
#undef MUL
#define CHARREG(typ, cond) \
@@ -653,16 +653,16 @@ case ch: { \
// Sign or augmented assignment.
#define CHAR_OR_ASSIGN(ch, ch_type, ass_type) \
-case ch: { \
- if (pline.size > 1 && pline.data[1] == '=') { \
- ret.len++; \
- ret.type = kExprLexAssignment; \
- ret.data.ass.type = ass_type; \
- } else { \
- ret.type = ch_type; \
- } \
- break; \
-}
+ case ch: { \
+ if (pline.size > 1 && pline.data[1] == '=') { \
+ ret.len++; \
+ ret.type = kExprLexAssignment; \
+ ret.data.ass.type = ass_type; \
+ } else { \
+ ret.type = ch_type; \
+ } \
+ break; \
+ }
CHAR_OR_ASSIGN('+', kExprLexPlus, kExprAsgnAdd)
CHAR_OR_ASSIGN('.', kExprLexDot, kExprAsgnConcat)
#undef CHAR_OR_ASSIGN
@@ -811,19 +811,19 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, const LexExpr
eltkn_type_tab[token.type]);
switch (token.type) {
#define TKNARGS(tkn_type, ...) \
-case tkn_type: { \
- ADDSTR(__VA_ARGS__); \
- break; \
-}
- TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)",
- eltkn_cmp_type_tab[token.data.cmp.type],
- ccs_tab[token.data.cmp.ccs],
- (int)token.data.cmp.inv)
- TKNARGS(kExprLexMultiplication, "(type=%s)",
- eltkn_mul_type_tab[token.data.mul.type])
- TKNARGS(kExprLexAssignment, "(type=%s)",
- expr_asgn_type_tab[token.data.ass.type])
- TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name))
+ case tkn_type: { \
+ ADDSTR(__VA_ARGS__); \
+ break; \
+ }
+ TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)",
+ eltkn_cmp_type_tab[token.data.cmp.type],
+ ccs_tab[token.data.cmp.ccs],
+ (int)token.data.cmp.inv)
+ TKNARGS(kExprLexMultiplication, "(type=%s)",
+ eltkn_mul_type_tab[token.data.mul.type])
+ TKNARGS(kExprLexAssignment, "(type=%s)",
+ expr_asgn_type_tab[token.data.ass.type])
+ TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name))
case kExprLexDoubleQuotedString:
TKNARGS(kExprLexSingleQuotedString, "(closed=%i)",
(int)token.data.str.closed)
@@ -1540,21 +1540,21 @@ static inline void east_set_error(const ParserState *const pstate, ExprASTError
case kExprNodeComplexIdentifier: \
case kExprNodePlainIdentifier: \
case kExprNodeCurlyBracesIdentifier: { \
- NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \
- cur_node->len = 0; \
- cur_node->children = *top_node_p; \
- *top_node_p = cur_node; \
- kvi_push(ast_stack, &cur_node->children->next); \
- ExprASTNode **const new_top_node_p = kv_last(ast_stack); \
- assert(*new_top_node_p == NULL); \
- new_ident_node_code; \
- *new_top_node_p = cur_node; \
- HL_CUR_TOKEN(hl); \
- break; \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \
+ cur_node->len = 0; \
+ cur_node->children = *top_node_p; \
+ *top_node_p = cur_node; \
+ kvi_push(ast_stack, &cur_node->children->next); \
+ ExprASTNode **const new_top_node_p = kv_last(ast_stack); \
+ assert(*new_top_node_p == NULL); \
+ new_ident_node_code; \
+ *new_top_node_p = cur_node; \
+ HL_CUR_TOKEN(hl); \
+ break; \
} \
default: { \
- OP_MISSING; \
- break; \
+ OP_MISSING; \
+ break; \
} \
} \
} while (0)
@@ -1747,19 +1747,19 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
const char *const v_p_start = v_p;
switch (*p) {
#define SINGLE_CHAR_ESC(ch, real_ch) \
-case ch: { \
- *v_p++ = real_ch; \
- p++; \
- break; \
-}
- SINGLE_CHAR_ESC('b', BS)
- SINGLE_CHAR_ESC('e', ESC)
- SINGLE_CHAR_ESC('f', FF)
- SINGLE_CHAR_ESC('n', NL)
- SINGLE_CHAR_ESC('r', CAR)
- SINGLE_CHAR_ESC('t', TAB)
- SINGLE_CHAR_ESC('"', '"')
- SINGLE_CHAR_ESC('\\', '\\')
+ case ch: { \
+ *v_p++ = real_ch; \
+ p++; \
+ break; \
+ }
+ SINGLE_CHAR_ESC('b', BS)
+ SINGLE_CHAR_ESC('e', ESC)
+ SINGLE_CHAR_ESC('f', FF)
+ SINGLE_CHAR_ESC('n', NL)
+ SINGLE_CHAR_ESC('r', CAR)
+ SINGLE_CHAR_ESC('t', TAB)
+ SINGLE_CHAR_ESC('"', '"')
+ SINGLE_CHAR_ESC('\\', '\\')
#undef SINGLE_CHAR_ESC
// Hexadecimal or unicode.
@@ -2141,32 +2141,32 @@ viml_pexpr_parse_process_token:
break;
}
#define SIMPLE_UB_OP(op) \
-case kExprLex##op: { \
- if (want_node == kENodeValue) { \
- /* Value level: assume unary operator. */ \
- NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \
- *top_node_p = cur_node; \
- kvi_push(ast_stack, &cur_node->children); \
- HL_CUR_TOKEN(Unary##op); \
- } else { \
- NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \
- ADD_OP_NODE(cur_node); \
- HL_CUR_TOKEN(Binary##op); \
- } \
- want_node = kENodeValue; \
- break; \
-}
+ case kExprLex##op: { \
+ if (want_node == kENodeValue) { \
+ /* Value level: assume unary operator. */ \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \
+ *top_node_p = cur_node; \
+ kvi_push(ast_stack, &cur_node->children); \
+ HL_CUR_TOKEN(Unary##op); \
+ } else { \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \
+ ADD_OP_NODE(cur_node); \
+ HL_CUR_TOKEN(Binary##op); \
+ } \
+ want_node = kENodeValue; \
+ break; \
+ }
SIMPLE_UB_OP(Plus)
SIMPLE_UB_OP(Minus)
#undef SIMPLE_UB_OP
#define SIMPLE_B_OP(op, msg) \
-case kExprLex##op: { \
- ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \
- NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \
- HL_CUR_TOKEN(op); \
- ADD_OP_NODE(cur_node); \
- break; \
-}
+ case kExprLex##op: { \
+ ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \
+ HL_CUR_TOKEN(op); \
+ ADD_OP_NODE(cur_node); \
+ break; \
+ }
SIMPLE_B_OP(Or, "or operator")
SIMPLE_B_OP(And, "and operator")
#undef SIMPLE_B_OP
@@ -2174,14 +2174,14 @@ case kExprLex##op: { \
ADD_VALUE_IF_MISSING(_("E15: Unexpected multiplication-like operator: %.*s"));
switch (cur_token.data.mul.type) {
#define MUL_OP(lex_op_tail, node_op_tail) \
-case kExprLexMul##lex_op_tail: { \
- NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \
- HL_CUR_TOKEN(node_op_tail); \
- break; \
-}
- MUL_OP(Mul, Multiplication)
- MUL_OP(Div, Division)
- MUL_OP(Mod, Mod)
+ case kExprLexMul##lex_op_tail: { \
+ NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \
+ HL_CUR_TOKEN(node_op_tail); \
+ break; \
+ }
+ MUL_OP(Mul, Multiplication)
+ MUL_OP(Div, Division)
+ MUL_OP(Mod, Mod)
#undef MUL_OP
}
ADD_OP_NODE(cur_node);
@@ -2929,11 +2929,11 @@ viml_pexpr_parse_no_paren_closing_error: {}
cur_node->data.ass.type = cur_token.data.ass.type;
switch (cur_token.data.ass.type) {
#define HL_ASGN(asgn, hl) \
-case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; }
- HL_ASGN(Plain, PlainAssignment)
- HL_ASGN(Add, AssignmentWithAddition)
- HL_ASGN(Subtract, AssignmentWithSubtraction)
- HL_ASGN(Concat, AssignmentWithConcatenation)
+ case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; }
+ HL_ASGN(Plain, PlainAssignment)
+ HL_ASGN(Add, AssignmentWithAddition)
+ HL_ASGN(Subtract, AssignmentWithSubtraction)
+ HL_ASGN(Concat, AssignmentWithConcatenation)
#undef HL_ASGN
}
ADD_OP_NODE(cur_node);
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e328ff5467..3e6e42dec2 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4525,6 +4525,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
fix_current_dir();
+ // Careful: autocommands may close the window and make "wp" invalid
if (flags & WEE_TRIGGER_NEW_AUTOCMDS) {
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
}
@@ -4558,7 +4559,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
}
// set window width to desired minimal value
- if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !wp->w_floating) {
+ if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !curwin->w_floating) {
win_setwidth((int)p_wiw);
}
diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg
index 558fa1759f..49ce394dc9 100644
--- a/src/uncrustify.cfg
+++ b/src/uncrustify.cfg
@@ -1,4 +1,4 @@
-# Uncrustify-0.73.0-199-0dfafb27
+# Uncrustify-0.74.0
#
# General options
@@ -214,6 +214,10 @@ sp_after_ptr_star_func = remove # ignore/add/remove/force/not_defined
# function prototype or function definition.
sp_after_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined
+# Add or remove space between the pointer star '*' and the name of the variable
+# in a function pointer definition.
+sp_ptr_star_func_var = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space after a pointer star '*', if followed by an open
# parenthesis, as in 'void* (*)()'.
sp_ptr_star_paren = ignore # ignore/add/remove/force/not_defined
@@ -311,19 +315,33 @@ sp_permit_cpp11_shift = false # true/false
# 'while', etc.).
sp_before_sparen = force # ignore/add/remove/force/not_defined
-# Add or remove space inside '(' and ')' of control statements.
+# Add or remove space inside '(' and ')' of control statements other than
+# 'for'.
sp_inside_sparen = remove # ignore/add/remove/force/not_defined
-# Add or remove space after '(' of control statements.
+# Add or remove space after '(' of control statements other than 'for'.
#
# Overrides sp_inside_sparen.
sp_inside_sparen_open = remove # ignore/add/remove/force/not_defined
-# Add or remove space before ')' of control statements.
+# Add or remove space before ')' of control statements other than 'for'.
#
# Overrides sp_inside_sparen.
sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined
+# Add or remove space inside '(' and ')' of 'for' statements.
+sp_inside_for = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space after '(' of 'for' statements.
+#
+# Overrides sp_inside_for.
+sp_inside_for_open = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space before ')' of 'for' statements.
+#
+# Overrides sp_inside_for.
+sp_inside_for_close = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space between '((' or '))' of control statements.
sp_sparen_paren = ignore # ignore/add/remove/force/not_defined
@@ -648,6 +666,11 @@ sp_func_class_paren = ignore # ignore/add/remove/force/not_defined
# and '()'.
sp_func_class_paren_empty = ignore # ignore/add/remove/force/not_defined
+# Add or remove space after 'return'.
+#
+# Default: force
+sp_return = force # ignore/add/remove/force/not_defined
+
# Add or remove space between 'return' and '('.
sp_return_paren = force # ignore/add/remove/force/not_defined
@@ -971,11 +994,31 @@ sp_inside_newop_paren_open = ignore # ignore/add/remove/force/not_defined
# Overrides sp_inside_newop_paren.
sp_inside_newop_paren_close = ignore # ignore/add/remove/force/not_defined
-# Add or remove space before a trailing or embedded comment.
-sp_before_tr_emb_cmt = add # ignore/add/remove/force/not_defined
+# Add or remove space before a trailing comment.
+sp_before_tr_cmt = add # ignore/add/remove/force/not_defined
+
+# Number of spaces before a trailing comment.
+sp_num_before_tr_cmt = 2 # unsigned number
+
+# Add or remove space before an embedded comment.
+#
+# Default: force
+sp_before_emb_cmt = force # ignore/add/remove/force/not_defined
+
+# Number of spaces before an embedded comment.
+#
+# Default: 1
+sp_num_before_emb_cmt = 1 # unsigned number
+
+# Add or remove space after an embedded comment.
+#
+# Default: force
+sp_after_emb_cmt = force # ignore/add/remove/force/not_defined
-# Number of spaces before a trailing or embedded comment.
-sp_num_before_tr_emb_cmt = 2 # unsigned number
+# Number of spaces after an embedded comment.
+#
+# Default: 1
+sp_num_after_emb_cmt = 1 # unsigned number
# (Java) Add or remove space between an annotation and the open parenthesis.
sp_annotation_paren = ignore # ignore/add/remove/force/not_defined
@@ -1216,12 +1259,16 @@ indent_sparen_extra = 0 # number
indent_relative_single_line_comments = true # true/false
# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns.
-# It might wise to choose the same value for the option indent_case_brace.
+# It might be wise to choose the same value for the option indent_case_brace.
indent_switch_case = 0 # unsigned number
+# Spaces to indent the body of a 'switch' before any 'case'.
+# Usually the same as indent_columns or indent_switch_case.
+indent_switch_body = 0 # unsigned number
+
# Spaces to indent '{' from 'case'. By default, the brace will appear under
# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK.
-# It might wise to choose the same value for the option indent_switch_case.
+# It might be wise to choose the same value for the option indent_switch_case.
indent_case_brace = 0 # number
# indent 'break' with 'case' from 'switch'.
@@ -1236,13 +1283,31 @@ indent_switch_pp = true # true/false
# Usually 0.
indent_case_shift = 0 # unsigned number
+# Whether to align comments before 'case' with the 'case'.
+#
+# Default: true
+indent_case_comment = true # true/false
+
+# Whether to indent comments not found in first column.
+#
+# Default: true
+indent_comment = true # true/false
+
# Whether to indent comments found in first column.
indent_col1_comment = false # true/false
# Whether to indent multi string literal in first column.
indent_col1_multi_string_literal = false # true/false
-# How to indent goto labels.
+# Align comments on adjacent lines that are this many columns apart or less.
+#
+# Default: 3
+indent_comment_align_thresh = 3 # unsigned number
+
+# Whether to ignore indent for goto labels.
+indent_ignore_label = false # true/false
+
+# How to indent goto labels. Requires indent_ignore_label=false.
#
# >0: Absolute column where 1 is the leftmost column
# <=0: Subtract from brace indent
@@ -1414,7 +1479,7 @@ indent_using_block = true # true/false
# 0: Off (default)
# 1: When the `if_false` is a continuation, indent it under `if_false`
# 2: When the `:` is a continuation, indent it under `?`
-indent_ternary_operator = 2 # unsigned number
+indent_ternary_operator = 0 # unsigned number
# Whether to indent the statements inside ternary operator.
indent_inside_ternary_operator = false # true/false
@@ -2622,6 +2687,22 @@ align_right_cmt_at_col = 0 # unsigned number
# 0: Don't align (default).
align_func_proto_span = 0 # unsigned number
+# How to consider (or treat) the '*' in the alignment of function prototypes.
+#
+# 0: Part of the type 'void * foo();' (default)
+# 1: Part of the function 'void *foo();'
+# 2: Dangling 'void *foo();'
+# Dangling: the '*' will not be taken into account when aligning.
+align_func_proto_star_style = 0 # unsigned number
+
+# How to consider (or treat) the '&' in the alignment of function prototypes.
+#
+# 0: Part of the type 'long & foo();' (default)
+# 1: Part of the function 'long &foo();'
+# 2: Dangling 'long &foo();'
+# Dangling: the '&' will not be taken into account when aligning.
+align_func_proto_amp_style = 0 # unsigned number
+
# The threshold for aligning function prototypes.
# Use a negative number for absolute thresholds.
#
@@ -3101,6 +3182,9 @@ pp_indent_in_guard = false # true/false
# indented from column 1.
pp_define_at_level = false # true/false
+# Whether to indent '#include' at the brace level.
+pp_include_at_level = false # true/false
+
# Whether to ignore the '#define' body while formatting.
pp_ignore_define_body = false # true/false
@@ -3307,5 +3391,5 @@ set QUESTION REAL_FATTR_CONST
set QUESTION REAL_FATTR_NONNULL_ALL
set QUESTION REAL_FATTR_PURE
set QUESTION REAL_FATTR_WARN_UNUSED_RESULT
-# option(s) with 'not default' value: 87
+# option(s) with 'not default' value: 86
#