aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mpack/lmpack.c3
-rw-r--r--src/nvim/CMakeLists.txt5
-rw-r--r--src/nvim/api/vim.c14
-rw-r--r--src/nvim/auevents.lua4
-rw-r--r--src/nvim/autocmd.c6
-rw-r--r--src/nvim/edit.c9
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/eval/funcs.c101
-rw-r--r--src/nvim/ex_cmds.c3
-rw-r--r--src/nvim/ex_docmd.c43
-rw-r--r--src/nvim/ex_docmd.h1
-rw-r--r--src/nvim/file_search.c15
-rw-r--r--src/nvim/generators/gen_char_blob.lua52
-rw-r--r--src/nvim/getchar.c4
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/lua/executor.c20
-rw-r--r--src/nvim/lua/spell.c99
-rw-r--r--src/nvim/lua/spell.h12
-rw-r--r--src/nvim/lua/stdlib.c11
-rw-r--r--src/nvim/mbyte.c10
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/normal.c21
-rw-r--r--src/nvim/ops.c21
-rw-r--r--src/nvim/option.c35
-rw-r--r--src/nvim/option.h1
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/os/pty_process_unix.c120
-rw-r--r--src/nvim/popupmnu.c43
-rw-r--r--src/nvim/quickfix.c3
-rw-r--r--src/nvim/regexp_nfa.c36
-rw-r--r--src/nvim/screen.c9
-rw-r--r--src/nvim/sign.c3
-rw-r--r--src/nvim/terminal.c13
-rw-r--r--src/nvim/testdir/runtest.vim6
-rw-r--r--src/nvim/testdir/setup.vim2
-rw-r--r--src/nvim/testdir/test_autocmd.vim90
-rw-r--r--src/nvim/testdir/test_blob.vim8
-rw-r--r--src/nvim/testdir/test_cmdline.vim37
-rw-r--r--src/nvim/testdir/test_display.vim25
-rw-r--r--src/nvim/testdir/test_edit.vim3
-rw-r--r--src/nvim/testdir/test_ex_mode.vim2
-rw-r--r--src/nvim/testdir/test_filechanged.vim149
-rw-r--r--src/nvim/testdir/test_fileformat.vim246
-rw-r--r--src/nvim/testdir/test_filetype.vim47
-rw-r--r--src/nvim/testdir/test_listchars.vim16
-rw-r--r--src/nvim/testdir/test_messages.vim8
-rw-r--r--src/nvim/testdir/test_options.vim21
-rw-r--r--src/nvim/testdir/test_signs.vim25
-rw-r--r--src/nvim/testdir/test_substitute.vim2
-rw-r--r--src/nvim/testdir/test_timers.vim2
-rw-r--r--src/nvim/testdir/test_visual.vim2
-rw-r--r--src/nvim/tui/input.c6
-rw-r--r--src/nvim/tui/tui.c43
54 files changed, 1166 insertions, 307 deletions
diff --git a/src/mpack/lmpack.c b/src/mpack/lmpack.c
index 87acf46592..126f2f3824 100644
--- a/src/mpack/lmpack.c
+++ b/src/mpack/lmpack.c
@@ -595,6 +595,7 @@ static void lmpack_unparse_enter(mpack_parser_t *parser, mpack_node_t *node)
/* push the pair */
result = lua_next(L, -2);
assert(result); /* should not be here if the map was fully processed */
+ (void)result; /* ignore unused warning */
if (parent->key_visited) {
/* release the current key */
lmpack_unref(L, packer->reg, (int)parent->data[1].i);
@@ -1010,6 +1011,7 @@ static int lmpack_session_reply(lua_State *L)
"invalid request id");
result = mpack_rpc_reply(session->session, &b, &bl, (mpack_uint32_t)id);
assert(result == MPACK_OK);
+ (void)result; /* ignore unused warning */
lua_pushlstring(L, buf, sizeof(buf) - bl);
return 1;
}
@@ -1027,6 +1029,7 @@ static int lmpack_session_notify(lua_State *L)
session = lmpack_check_session(L, 1);
result = mpack_rpc_notify(session->session, &b, &bl);
assert(result == MPACK_OK);
+ (void)result; /* ignore unused warning */
lua_pushlstring(L, buf, sizeof(buf) - bl);
return 1;
}
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index bb16459a7f..9c4b778169 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -326,7 +326,9 @@ add_custom_command(
add_custom_command(
OUTPUT ${VIM_MODULE_FILE}
- COMMAND ${LUA_PRG} ${CHAR_BLOB_GENERATOR} ${VIM_MODULE_FILE}
+ COMMAND ${CMAKE_COMMAND} -E env
+ "LUAC_PRG=${LUAC_PRG}"
+ ${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE}
${LUA_VIM_MODULE_SOURCE} vim_module
${LUA_SHARED_MODULE_SOURCE} shared_module
${LUA_INSPECT_MODULE_SOURCE} inspect_module
@@ -339,6 +341,7 @@ add_custom_command(
${LUA_INSPECT_MODULE_SOURCE}
${LUA_F_MODULE_SOURCE}
${LUA_META_MODULE_SOURCE}
+ VERBATIM
)
list(APPEND NVIM_GENERATED_SOURCES
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 4f7c320129..f81cdf9deb 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -696,7 +696,17 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
rv = INTEGER_OBJ(numval);
break;
case 2:
- rv = BOOLEAN_OBJ(!!numval);
+ switch (numval) {
+ case 0:
+ case 1:
+ rv = BOOLEAN_OBJ(numval);
+ break;
+ default:
+ // Boolean options that return something other than 0 or 1 should return nil. Currently this
+ // only applies to 'autoread' which uses -1 as a local value to indicate "unset"
+ rv = NIL;
+ break;
+ }
break;
default:
api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
@@ -749,7 +759,7 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error
stringval = value.data.string.data;
break;
case kObjectTypeNil:
- // Do nothing
+ scope |= OPT_CLEAR;
break;
default:
api_set_error(err, kErrorTypeValidation, "invalid value for option");
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 6227b08b26..8fe623fc96 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -75,6 +75,8 @@ return {
'QuickFixCmdPost', -- after :make, :grep etc.
'QuickFixCmdPre', -- before :make, :grep etc.
'QuitPre', -- before :quit
+ 'RecordingEnter', -- when starting to record a macro
+ 'RecordingLeave', -- just before a macro stops recording
'RemoteReply', -- upon string reception from a remote vim
'SearchWrapped', -- after the search wrapped around
'SessionLoadPost', -- after loading a session file
@@ -131,6 +133,8 @@ return {
BufModifiedSet=true,
DiagnosticChanged=true,
DirChanged=true,
+ RecordingEnter=true,
+ RecordingLeave=true,
Signal=true,
TabClosed=true,
TabNew=true,
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 3780cad1d6..463bd5e0e6 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -932,6 +932,12 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, c
last_mode = get_mode();
}
+ // If the event is CursorMoved, update the last cursor position
+ // position to avoid immediately triggering the autocommand
+ if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) {
+ curwin->w_last_cursormoved = curwin->w_cursor;
+ }
+
ap->cmds = NULL;
*prev_ap = ap;
last_autopat[(int)event] = ap;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 2135d0bcd2..2e3eec3642 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -643,7 +643,10 @@ static int insert_check(VimState *state)
update_curswant();
s->old_topline = curwin->w_topline;
s->old_topfill = curwin->w_topfill;
- s->lastc = s->c; // remember previous char for CTRL-D
+
+ if (s->c != K_EVENT) {
+ s->lastc = s->c; // remember previous char for CTRL-D
+ }
// After using CTRL-G U the next cursor key will not break undo.
if (dont_sync_undo == kNone) {
@@ -1685,7 +1688,7 @@ static void init_prompt(int cmdchar_todo)
// Insert always starts after the prompt, allow editing text after it.
if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) {
Insstart.lnum = curwin->w_cursor.lnum;
- Insstart.col = STRLEN(prompt);
+ Insstart.col = (colnr_T)STRLEN(prompt);
Insstart_orig = Insstart;
Insstart_textlen = Insstart.col;
Insstart_blank_vcol = MAXCOL;
@@ -1696,7 +1699,7 @@ static void init_prompt(int cmdchar_todo)
coladvance(MAXCOL);
}
if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) {
- curwin->w_cursor.col = STRLEN(prompt);
+ curwin->w_cursor.col = (colnr_T)STRLEN(prompt);
}
// Make sure the cursor is in a valid position.
check_cursor();
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 9a76b67de6..e445a08227 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -133,6 +133,7 @@ return {
foldtext={},
foldtextresult={args=1, base=1},
foreground={},
+ fullcommand={args=1, base=1},
funcref={args={1, 3}, base=1},
['function']={args={1, 3}, base=1},
garbagecollect={args={0, 1}},
@@ -277,6 +278,7 @@ return {
readfile={args={1, 3}, base=1},
reg_executing={},
reg_recording={},
+ reg_recorded={},
reltime={args={0, 2}, base=1},
reltimefloat={args=1, base=1},
reltimestr={args=1, base=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 5252c940f7..32026282cf 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1082,15 +1082,13 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Return the current directory
cwd = xmalloc(MAXPATHL);
- if (cwd != NULL) {
- if (os_dirname(cwd, MAXPATHL) != FAIL) {
+ if (os_dirname(cwd, MAXPATHL) != FAIL) {
#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(cwd);
+ slash_adjust(cwd);
#endif
- rettv->vval.v_string = vim_strsave(cwd);
- }
- xfree(cwd);
+ rettv->vval.v_string = vim_strsave(cwd);
}
+ xfree(cwd);
if (curwin->w_localdir != NULL) {
scope = kCdScopeWindow;
@@ -3920,34 +3918,46 @@ static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_qf_loc_list(true, NULL, &argvars[0], rettv);
}
-/// "getreg()" function
-static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// Common between getreg(), getreginfo() and getregtype(): get the register
+/// name from the first argument.
+/// Returns zero on error.
+static int getreg_get_regname(typval_T *argvars)
{
- const char *strregname;
- int arg2 = false;
- bool return_list = false;
- bool error = false;
+ const char_u *strregname;
if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- error = strregname == NULL;
- if (argvars[1].v_type != VAR_UNKNOWN) {
- arg2 = tv_get_number_chk(&argvars[1], &error);
- if (!error && argvars[2].v_type != VAR_UNKNOWN) {
- return_list = tv_get_number_chk(&argvars[2], &error);
- }
+ strregname = (const char_u *)tv_get_string_chk(&argvars[0]);
+ if (strregname == NULL) { // type error; errmsg already given
+ return 0;
}
} else {
- strregname = _(get_vim_var_str(VV_REG));
+ // Default to v:register
+ strregname = get_vim_var_str(VV_REG);
}
- if (error) {
+ return *strregname == 0 ? '"' : *strregname;
+}
+
+/// "getreg()" function
+static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int arg2 = false;
+ bool return_list = false;
+
+ int regname = getreg_get_regname(argvars);
+ if (regname == 0) {
return;
}
- int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
- if (regname == 0) {
- regname = '"';
+ if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) {
+ bool error = false;
+ arg2 = (int)tv_get_number_chk(&argvars[1], &error);
+ if (!error && argvars[2].v_type != VAR_UNKNOWN) {
+ return_list = (bool)tv_get_number_chk(&argvars[2], &error);
+ }
+ if (error) {
+ return;
+ }
}
if (return_list) {
@@ -3964,28 +3974,16 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "getregtype()" function
- */
+/// "getregtype()" function
static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *strregname;
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- if (strregname == NULL) { // Type error; errmsg already given.
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- return;
- }
- } else {
- // Default to v:register.
- strregname = _(get_vim_var_str(VV_REG));
- }
+ // on error return an empty string
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
- int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
+ int regname = getreg_get_regname(argvars);
if (regname == 0) {
- regname = '"';
+ return;
}
colnr_T reglen = 0;
@@ -7333,18 +7331,12 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "getreginfo()" function
static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *strregname;
- if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- if (strregname == NULL) {
- return;
- }
- } else {
- strregname = (const char *)get_vim_var_str(VV_REG);
+ int regname = getreg_get_regname(argvars);
+ if (regname == 0) {
+ return;
}
- int regname = (strregname == NULL ? '"' : *strregname);
- if (regname == 0 || regname == '@') {
+ if (regname == '@') {
regname = '"';
}
@@ -7398,6 +7390,11 @@ static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return_register(reg_recording, rettv);
}
+static void f_reg_recorded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ return_register(reg_recorded, rettv);
+}
+
/// list2proftime - convert a List to proftime_T
///
/// @param arg The input list, must be of type VAR_LIST and have
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index c0cb17fa61..4965eb9c20 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -4584,6 +4584,9 @@ void ex_global(exarg_T *eap)
// a match on this line?
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL, NULL);
+ if (regmatch.regprog == NULL) {
+ break; // re-compiling regprog failed
+ }
if ((type == 'g' && match) || (type == 'v' && !match)) {
ml_setmarked(lnum);
ndone++;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 9f0f8d93a3..230cbd4b60 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2900,6 +2900,31 @@ int cmd_exists(const char *const name)
return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1);
}
+// "fullcommand" function
+void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ exarg_T ea;
+ char_u *name = argvars[0].vval.v_string;
+
+ while (name[0] != NUL && name[0] == ':') {
+ name++;
+ }
+ name = skip_range(name, NULL);
+
+ rettv->v_type = VAR_STRING;
+
+ ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
+ ea.cmdidx = (cmdidx_T)0;
+ char_u *p = find_command(&ea, NULL);
+ if (p == NULL || ea.cmdidx == CMD_SIZE) {
+ return;
+ }
+
+ rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
+ ? get_user_commands(NULL, ea.useridx)
+ : cmdnames[ea.cmdidx].cmd_name);
+}
+
/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
/// we don't need/want deleted. Maybe this could be done better if we didn't
/// repeat all this stuff. The only problem is that they may not stay
@@ -7807,17 +7832,21 @@ bool changedir_func(char_u *new_dir, CdScope scope)
prev_dir = pdir;
}
+ // For UNIX ":cd" means: go to home directory.
+ // On other systems too if 'cdhome' is set.
#if defined(UNIX)
- // On Unix ":cd" means: go to home directory.
if (*new_dir == NUL) {
+#else
+ if (*new_dir == NUL && p_cdh) {
+#endif
// Use NameBuff for home directory name.
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
new_dir = NameBuff;
}
-#endif
- if (vim_chdir(new_dir) == 0) {
- bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+ bool dir_differs = new_dir == NULL || pdir == NULL
+ || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+ if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) {
post_chdir(scope, dir_differs);
retval = true;
} else {
@@ -7833,9 +7862,9 @@ void ex_cd(exarg_T *eap)
{
char_u *new_dir;
new_dir = eap->arg;
-#if !defined(UNIX) && !defined(VMS)
- // for non-UNIX ":cd" means: print current directory
- if (*new_dir == NUL) {
+#if !defined(UNIX)
+ // for non-UNIX ":cd" means: print current directory unless 'cdhome' is set
+ if (*new_dir == NUL && !p_cdh) {
ex_pwd(NULL);
} else
#endif
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index 7ec4fad277..a302d4a3c5 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -2,6 +2,7 @@
#define NVIM_EX_DOCMD_H
#include "nvim/ex_cmds_defs.h"
+#include "nvim/eval/funcs.h"
#include "nvim/globals.h"
// flags for do_cmdline()
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index d31021b3ef..1884dd49c5 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1667,14 +1667,19 @@ int vim_chdirfile(char_u *fname, CdCause cause)
NameBuff[0] = NUL;
}
- if (os_chdir(dir) == 0) {
- if (cause != kCdCauseOther && pathcmp(dir, (char *)NameBuff, -1) != 0) {
- do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
- }
- } else {
+ if (pathcmp(dir, (char *)NameBuff, -1) == 0) {
+ // nothing to do
+ return OK;
+ }
+
+ if (os_chdir(dir) != 0) {
return FAIL;
}
+ if (cause != kCdCauseOther) {
+ do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
+ }
+
return OK;
}
diff --git a/src/nvim/generators/gen_char_blob.lua b/src/nvim/generators/gen_char_blob.lua
index a7dad50d48..3ec1ff2caf 100644
--- a/src/nvim/generators/gen_char_blob.lua
+++ b/src/nvim/generators/gen_char_blob.lua
@@ -1,12 +1,26 @@
if arg[1] == '--help' then
print('Usage:')
- print(' '..arg[0]..' target source varname [source varname]...')
+ print(' '..arg[0]..' [-c] target source varname [source varname]...')
print('')
print('Generates C file with big uint8_t blob.')
print('Blob will be stored in a static const array named varname.')
os.exit()
end
+-- Recognized options:
+-- -c compile Lua bytecode
+local options = {}
+
+while true do
+ local opt = string.match(arg[1], "^-(%w)")
+ if not opt then
+ break
+ end
+
+ options[opt] = true
+ table.remove(arg, 1)
+end
+
assert(#arg >= 3 and (#arg - 1) % 2 == 0)
local target_file = arg[1] or error('Need a target file')
@@ -14,6 +28,7 @@ local target = io.open(target_file, 'w')
target:write('#include <stdint.h>\n\n')
+local warn_on_missing_compiler = true
local varnames = {}
for argi = 2, #arg, 2 do
local source_file = arg[argi]
@@ -23,11 +38,26 @@ for argi = 2, #arg, 2 do
end
varnames[varname] = source_file
- local source = io.open(source_file, 'r')
- or error(string.format("source_file %q doesn't exist", source_file))
-
target:write(('static const uint8_t %s[] = {\n'):format(varname))
+ local output
+ if options.c then
+ local luac = os.getenv("LUAC_PRG")
+ if luac and luac ~= "" then
+ output = io.popen(luac:format(source_file), "r"):read("*a")
+ elseif warn_on_missing_compiler then
+ print("LUAC_PRG is missing, embedding raw source")
+ warn_on_missing_compiler = false
+ end
+ end
+
+ if not output then
+ local source = io.open(source_file, "r")
+ or error(string.format("source_file %q doesn't exist", source_file))
+ output = source:read("*a")
+ source:close()
+ end
+
local num_bytes = 0
local MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line
target:write(' ')
@@ -41,19 +71,13 @@ for argi = 2, #arg, 2 do
end
end
- for line in source:lines() do
- for i = 1, string.len(line) do
- local byte = line:byte(i)
- assert(byte ~= 0)
- target:write(string.format(' %3u,', byte))
- increase_num_bytes()
- end
- target:write(string.format(' %3u,', string.byte('\n', 1)))
+ for i = 1, string.len(output) do
+ local byte = output:byte(i)
+ target:write(string.format(' %3u,', byte))
increase_num_bytes()
end
- target:write(' 0};\n')
- source:close()
+ target:write(' 0};\n')
end
target:close()
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 05c38a5233..6b1150cefa 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1894,7 +1894,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// buffer right here. Otherwise, use the mapping (loop around).
if (mp == NULL) {
*keylenp = keylen;
- return map_result_get; // got character, break for loop
+ return map_result_get; // get character from typeahead
} else {
keylen = mp_match_len;
}
@@ -2166,7 +2166,7 @@ static int vgetorpeek(bool advance)
KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
del_typebuf(1, 0);
}
- break;
+ break; // got character, break the for loop
}
// not enough characters, get more
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index dfbc80066e..697d4b11a7 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -632,6 +632,7 @@ EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p.
EXTERN int reg_recording INIT(= 0); // register for recording or zero
EXTERN int reg_executing INIT(= 0); // register being executed or zero
+EXTERN int reg_recorded INIT(= 0); // last recorded register or zero
EXTERN int no_mapping INIT(= false); // currently no mapping allowed
EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index b09d133495..107ff22913 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -404,9 +404,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
const char *code = (char *)&shared_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/shared.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua")
|| nlua_pcall(lstate, 0, 0)) {
- nlua_error(lstate, _("E5106: Error while creating shared module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n"));
return 1;
}
}
@@ -416,18 +416,18 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_getfield(lstate, -1, "loaded"); // [package, loaded]
const char *code = (char *)&inspect_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/inspect.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua")
|| nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n"));
return 1;
}
// [package, loaded, inspect]
lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded]
code = (char *)&lua_F_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/F.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua")
|| nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n"));
return 1;
}
// [package, loaded, module]
@@ -438,9 +438,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
const char *code = (char *)&vim_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(vim_module) - 1, "@vim.lua")
|| nlua_pcall(lstate, 0, 0)) {
- nlua_error(lstate, _("E5106: Error while creating vim module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n"));
return 1;
}
}
@@ -450,9 +450,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_getfield(lstate, -1, "loaded"); // [package, loaded]
const char *code = (char *)&lua_meta_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/_meta.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(lua_meta_module) - 1, "@vim/_meta.lua")
|| nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s\n"));
return 1;
}
// [package, loaded, module]
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
new file mode 100644
index 0000000000..b84124bc19
--- /dev/null
+++ b/src/nvim/lua/spell.c
@@ -0,0 +1,99 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "nvim/spell.h"
+#include "nvim/vim.h"
+#include "nvim/lua/spell.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/spell.c.generated.h"
+#endif
+
+int nlua_spell_check(lua_State *lstate)
+{
+ if (lua_gettop(lstate) < 1) {
+ return luaL_error(lstate, "Expected 1 argument");
+ }
+
+ if (lua_type(lstate, 1) != LUA_TSTRING) {
+ luaL_argerror(lstate, 1, "expected string");
+ }
+
+ const char *str = lua_tolstring(lstate, 1, NULL);
+
+ // spell.c requires that 'spell' is enabled, so we need to temporarily enable
+ // it before we can call spell functions.
+ const int wo_spell_save = curwin->w_p_spell;
+
+ if (!curwin->w_p_spell) {
+ did_set_spelllang(curwin);
+ curwin->w_p_spell = true;
+ }
+
+ // Check 'spelllang'
+ if (*curwin->w_s->b_p_spl == NUL) {
+ emsg(_(e_no_spell));
+ curwin->w_p_spell = wo_spell_save;
+ return 0;
+ }
+
+ hlf_T attr = HLF_COUNT;
+ size_t len = 0;
+ size_t pos = 0;
+ int capcol = -1;
+ int no_res = 0;
+ const char * result;
+
+ lua_createtable(lstate, 0, 0);
+
+ while (*str != NUL) {
+ attr = HLF_COUNT;
+ len = spell_check(curwin, (char_u *)str, &attr, &capcol, false);
+ assert(len <= INT_MAX);
+
+ if (attr != HLF_COUNT) {
+ lua_createtable(lstate, 3, 0);
+
+ lua_pushlstring(lstate, str, len);
+ lua_rawseti(lstate, -2, 1);
+
+ result = attr == HLF_SPB ? "bad" :
+ attr == HLF_SPR ? "rare" :
+ attr == HLF_SPL ? "local" :
+ attr == HLF_SPC ? "caps" :
+ NULL;
+
+ assert(result != NULL);
+
+ lua_pushstring(lstate, result);
+ lua_rawseti(lstate, -2, 2);
+
+ // +1 for 1-indexing
+ lua_pushinteger(lstate, (long)pos + 1);
+ lua_rawseti(lstate, -2, 3);
+
+ lua_rawseti(lstate, -2, ++no_res);
+ }
+
+ str += len;
+ pos += len;
+ capcol -= (int)len;
+ }
+
+ // Restore 'spell'
+ curwin->w_p_spell = wo_spell_save;
+ return 1;
+}
+
+static const luaL_Reg spell_functions[] = {
+ { "check", nlua_spell_check },
+ { NULL , NULL }
+};
+
+int luaopen_spell(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_register(L, NULL, spell_functions);
+ return 1;
+}
diff --git a/src/nvim/lua/spell.h b/src/nvim/lua/spell.h
new file mode 100644
index 0000000000..8f798a5191
--- /dev/null
+++ b/src/nvim/lua/spell.h
@@ -0,0 +1,12 @@
+#ifndef NVIM_LUA_SPELL_H
+#define NVIM_LUA_SPELL_H
+
+#include <lauxlib.h>
+#include <lua.h>
+#include <lualib.h>
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/spell.h.generated.h"
+#endif
+
+#endif // NVIM_LUA_SPELL_H
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index b746e03625..18a579ed0f 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -30,6 +30,7 @@
#include "nvim/lua/stdlib.h"
#include "nvim/lua/treesitter.h"
#include "nvim/lua/xdiff.h"
+#include "nvim/lua/spell.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memline.h"
@@ -230,8 +231,8 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- int tail_offset = mb_head_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
- lua_pushinteger(lstate, tail_offset);
+ int head_offset = mb_head_off((char_u *)s1, (char_u *)s1 + offset - 1);
+ lua_pushinteger(lstate, head_offset);
return 1;
}
@@ -250,7 +251,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
+ int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + offset - 1);
lua_pushinteger(lstate, tail_offset);
return 1;
}
@@ -518,6 +519,10 @@ void nlua_state_add_stdlib(lua_State *const lstate)
lua_pushcfunction(lstate, &nlua_xdl_diff);
lua_setfield(lstate, -2, "diff");
+ // vim.spell
+ luaopen_spell(lstate);
+ lua_setfield(lstate, -2, "spell");
+
lua_cjson_new(lstate);
lua_setfield(lstate, -2, "json");
}
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index ce44f6c619..5eb209a6f6 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1609,7 +1609,8 @@ void show_utf8(void)
msg((char *)IObuff);
}
-/// Return offset from "p" to the first byte of the character it points into.
+/// Return offset from "p" to the start of a character, including composing characters.
+/// "base" must be the start of the string, which must be NUL terminated.
/// If "p" points to the NUL at the end of the string return 0.
/// Returns 0 when already at the first byte of a character.
int utf_head_off(const char_u *base, const char_u *p)
@@ -1850,10 +1851,9 @@ int mb_off_next(char_u *base, char_u *p)
return i;
}
-/*
- * Return the offset from "p" to the last byte of the character it points
- * into. Can start anywhere in a stream of bytes.
- */
+/// Return the offset from "p" to the last byte of the character it points
+/// into. Can start anywhere in a stream of bytes.
+/// Composing characters are not included.
int mb_tail_off(char_u *base, char_u *p)
{
int i;
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 0a672000e4..15ba6645f5 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1011,7 +1011,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
col -= wp->w_leftcol;
if (col >= 0 && col < wp->w_width) {
- coloff = col - scol + (local ? 0 : wp->w_wincol) + 1;
+ coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_border_adj[3]) + 1;
} else {
scol = ccol = ecol = 0;
// character is left or right of the window
@@ -1022,7 +1022,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
}
}
}
- *rowp = (local ? 0 : wp->w_winrow) + row + rowoff;
+ *rowp = (local ? 0 : wp->w_winrow + wp->w_border_adj[0]) + row + rowoff;
*scolp = scol + coloff;
*ccolp = ccol + coloff;
*ecolp = ecol + coloff;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 95a521c0d8..3246596f16 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -229,7 +229,7 @@ static const struct nv_cmd {
{ 'N', nv_next, 0, SEARCH_REV },
{ 'O', nv_open, 0, 0 },
{ 'P', nv_put, 0, 0 },
- { 'Q', nv_exmode, NV_NCW, 0 },
+ { 'Q', nv_regreplay, 0, 0 },
{ 'R', nv_Replace, 0, false },
{ 'S', nv_subst, NV_KEEPREG, 0 },
{ 'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD },
@@ -4028,15 +4028,18 @@ dozet:
/*
* "Q" command.
*/
-static void nv_exmode(cmdarg_T *cap)
+static void nv_regreplay(cmdarg_T *cap)
{
- /*
- * Ignore 'Q' in Visual mode, just give a beep.
- */
- if (VIsual_active) {
- vim_beep(BO_EX);
- } else if (!checkclearop(cap->oap)) {
- do_exmode();
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ while (cap->count1-- && !got_int) {
+ if (do_execreg(reg_recorded, false, false, false) == false) {
+ clearopbeep(cap->oap);
+ break;
+ }
+ line_breakcheck();
}
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 9bc63477e9..c6f9c5f04f 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -912,13 +912,14 @@ int do_record(int c)
showmode();
regname = c;
retval = OK;
- }
- } else { // stop recording
- /*
- * Get the recorded key hits. K_SPECIAL and CSI will be escaped, this
- * needs to be removed again to put it in a register. exec_reg then
- * adds the escaping back later.
- */
+ apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf);
+ }
+ } else { // stop recording
+ // Get the recorded key hits. K_SPECIAL and CSI will be escaped, this
+ // needs to be removed again to put it in a register. exec_reg then
+ // adds the escaping back later.
+ apply_autocmds(EVENT_RECORDINGLEAVE, NULL, NULL, false, curbuf);
+ reg_recorded = reg_recording;
reg_recording = 0;
if (ui_has(kUIMessages)) {
showmode();
@@ -932,10 +933,8 @@ int do_record(int c)
// Remove escaping for CSI and K_SPECIAL in multi-byte chars.
vim_unescape_csi(p);
- /*
- * We don't want to change the default register here, so save and
- * restore the current register name.
- */
+ // We don't want to change the default register here, so save and
+ // restore the current register name.
old_y_previous = y_previous;
retval = stuff_yank(regname, p);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 2ceb1bd992..65adda3c01 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2991,7 +2991,7 @@ ambw_end:
}
} else if (varp == &curwin->w_p_fdc || varp == &curwin->w_allbuf_opt.wo_fdc) {
// 'foldcolumn'
- if (check_opt_strings(*varp, p_fdc_values, false) != OK) {
+ if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
errmsg = e_invarg;
}
} else if (varp == &p_pt) {
@@ -3370,6 +3370,9 @@ static int int_cmp(const void *a, const void *b)
/// @return OK when the value is valid, FAIL otherwise
int check_signcolumn(char_u *val)
{
+ if (*val == NUL) {
+ return FAIL;
+ }
// check for basic match
if (check_opt_strings(val, p_scl_values, false) == OK) {
return OK;
@@ -3610,7 +3613,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
c2 = c3 = 0;
s = p + len + 1;
c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || utf_char2cells(c1) > 1) {
+ if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
@@ -3618,12 +3621,12 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
return e_invarg;
}
c2 = get_encoded_char_adv(&s);
- if (c2 == 0 || utf_char2cells(c2) > 1) {
+ if (c2 == 0 || char2cells(c2) > 1) {
return e_invarg;
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
- if (c3 == 0 || utf_char2cells(c3) > 1) {
+ if (c3 == 0 || char2cells(c3) > 1) {
return e_invarg;
}
}
@@ -3657,7 +3660,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
multispace_len = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || utf_char2cells(c1) > 1) {
+ if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
multispace_len++;
@@ -5059,6 +5062,9 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
/// @param[in] number New value for the number or boolean option.
/// @param[in] string New value for string option.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
+/// If OPT_CLEAR is set, the value of the option
+/// is cleared (the exact semantics of this depend
+/// on the option).
///
/// @return NULL on success, error message on error.
char *set_option_value(const char *const name, const long number, const char *const string,
@@ -5084,7 +5090,7 @@ char *set_option_value(const char *const name, const long number, const char *co
}
if (flags & P_STRING) {
const char *s = string;
- if (s == NULL) {
+ if (s == NULL || opt_flags & OPT_CLEAR) {
s = "";
}
return set_string_option(opt_idx, s, opt_flags);
@@ -5106,10 +5112,23 @@ char *set_option_value(const char *const name, const long number, const char *co
return NULL; // do nothing as we hit an error
}
}
+ long numval = number;
+ if (opt_flags & OPT_CLEAR) {
+ if ((int *)varp == &curbuf->b_p_ar) {
+ numval = -1;
+ } else if ((long *)varp == &curbuf->b_p_ul) {
+ numval = NO_LOCAL_UNDOLEVEL;
+ } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
+ numval = -1;
+ } else {
+ char *s = NULL;
+ (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
+ }
+ }
if (flags & P_NUM) {
- return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags);
+ return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
} else {
- return set_bool_option(opt_idx, varp, (int)number, opt_flags);
+ return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
}
}
}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 452494172f..f7dbaafeec 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -22,6 +22,7 @@ typedef enum {
OPT_ONECOLUMN = 64, ///< list options one per line
OPT_NO_REDRAW = 128, ///< ignore redraw flags on option
OPT_SKIPRTP = 256, ///< "skiprtp" in 'sessionoptions'
+ OPT_CLEAR = 512, ///< Clear local value of an option.
} OptionFlags;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 19cb33a354..09c3bf3800 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -743,6 +743,7 @@ EXTERN int p_write; // 'write'
EXTERN int p_wa; // 'writeany'
EXTERN int p_wb; // 'writebackup'
EXTERN long p_wd; // 'writedelay'
+EXTERN int p_cdh; // 'cdhome'
EXTERN int p_force_on; ///< options that cannot be turned off.
EXTERN int p_force_off; ///< options that cannot be turned on.
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 71208dfc68..28b4eb9fe2 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -275,6 +275,14 @@ return {
defaults={if_true="internal,keepascii"}
},
{
+ full_name='cdhome', abbreviation='cdh',
+ short_desc=N_(":cd without argument goes to the home directory"),
+ type='bool', scope={'global'},
+ secure=true,
+ varname='p_cdh',
+ defaults={if_true=false}
+ },
+ {
full_name='cdpath', abbreviation='cd',
short_desc=N_("list of directories searched with \":cd\""),
type='string', list='comma', scope={'global'},
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index 450bc75ffb..3459646bad 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -15,7 +15,13 @@
# include <libutil.h>
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
# include <util.h>
-#elif !defined(__sun)
+#elif defined(__sun)
+# include <sys/stream.h>
+# include <sys/syscall.h>
+# include <fcntl.h>
+# include <unistd.h>
+# include <signal.h>
+#else
# include <pty.h>
#endif
@@ -38,6 +44,118 @@
# include "os/pty_process_unix.c.generated.h"
#endif
+#if defined(__sun) && !defined(HAVE_FORKPTY)
+
+// this header defines STR, just as nvim.h, but it is defined as ('S'<<8),
+// to avoid #undef STR, #undef STR, #define STR ('S'<<8) just delay the
+// inclusion of the header even though it gets include out of order.
+#include <sys/stropts.h>
+
+static int openpty(int *amaster, int *aslave, char *name,
+ struct termios *termp, struct winsize *winp)
+{
+ int slave = -1;
+ int master = open("/dev/ptmx", O_RDWR);
+ if (master == -1) {
+ goto error;
+ }
+
+ // grantpt will invoke a setuid program to change permissions
+ // and might fail if SIGCHLD handler is set, temporarily reset
+ // while running
+ void(*sig_saved)(int) = signal(SIGCHLD, SIG_DFL);
+ int res = grantpt(master);
+ signal(SIGCHLD, sig_saved);
+
+ if (res == -1 || unlockpt(master) == -1) {
+ goto error;
+ }
+
+ char *slave_name = ptsname(master);
+ if (slave_name == NULL) {
+ goto error;
+ }
+
+ slave = open(slave_name, O_RDWR|O_NOCTTY);
+ if (slave == -1) {
+ goto error;
+ }
+
+ // ptem emulates a terminal when used on a pseudo terminal driver,
+ // must be pushed before ldterm
+ ioctl(slave, I_PUSH, "ptem");
+ // ldterm provides most of the termio terminal interface
+ ioctl(slave, I_PUSH, "ldterm");
+ // ttcompat compatability with older terminal ioctls
+ ioctl(slave, I_PUSH, "ttcompat");
+
+ if (termp) {
+ tcsetattr(slave, TCSAFLUSH, termp);
+ }
+ if (winp) {
+ ioctl(slave, TIOCSWINSZ, winp);
+ }
+
+ *amaster = master;
+ *aslave = slave;
+ // ignoring name, not passed and size is unknown in the API
+
+ return 0;
+
+error:
+ if (slave != -1) {
+ close(slave);
+ }
+ if (master != -1) {
+ close(master);
+ }
+ return -1;
+}
+
+static int login_tty(int fd)
+{
+ setsid();
+ if (ioctl(fd, TIOCSCTTY, NULL) == -1) {
+ return -1;
+ }
+
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO) {
+ close(fd);
+ }
+
+ return 0;
+}
+
+static pid_t forkpty(int *amaster, char *name,
+ struct termios *termp, struct winsize *winp)
+{
+ int master, slave;
+ if (openpty(&master, &slave, name, termp, winp) == -1) {
+ return -1;
+ }
+
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ close(master);
+ close(slave);
+ return -1;
+ case 0:
+ close(master);
+ login_tty(slave);
+ return 0;
+ default:
+ close(slave);
+ *amaster = master;
+ return pid;
+ }
+}
+
+#endif
+
/// termios saved at startup (for TUI) or initialized by pty_process_spawn().
static struct termios termios_default;
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 606c03f838..da2ada791f 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -386,7 +386,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
void pum_redraw(void)
{
int row = 0;
- int col;
+ int grid_col;
int attr_norm = win_hl_attr(curwin, HLF_PNI);
int attr_select = win_hl_attr(curwin, HLF_PSI);
int attr_scroll = win_hl_attr(curwin, HLF_PSB);
@@ -479,7 +479,7 @@ void pum_redraw(void)
// Display each entry, use two spaces for a Tab.
// Do this 3 times: For the main text, kind and extra info
- col = col_off;
+ grid_col = col_off;
totwidth = 0;
for (round = 1; round <= 3; ++round) {
@@ -537,24 +537,15 @@ void pum_redraw(void)
}
}
grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row,
- col - size + 1, attr);
+ grid_col - size + 1, attr);
xfree(rt_start);
xfree(st);
- col -= width;
+ grid_col -= width;
} else {
- int size = (int)STRLEN(st);
- int cells = (int)mb_string2cells(st);
-
- // only draw the text that fits
- while (size > 0 && col + cells > pum_width + pum_col) {
- size--;
- size -= utf_head_off(st, st + size);
- cells -= utf_ptr2cells(st + size);
- }
-
- grid_puts_len(&pum_grid, st, size, row, col, attr);
+ // use grid_puts_len() to truncate the text
+ grid_puts(&pum_grid, st, row, grid_col, attr);
xfree(st);
- col += width;
+ grid_col += width;
}
if (*p != TAB) {
@@ -563,12 +554,12 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (pum_rl) {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col - 1,
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col - 1,
attr);
- col -= 2;
+ grid_col -= 2;
} else {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col, attr);
- col += 2;
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col, attr);
+ grid_col += 2;
}
totwidth += 2;
// start text at next char
@@ -599,21 +590,21 @@ void pum_redraw(void)
if (pum_rl) {
grid_fill(&pum_grid, row, row + 1, col_off - pum_base_width - n + 1,
- col + 1, ' ', ' ', attr);
- col = col_off - pum_base_width - n + 1;
+ grid_col + 1, ' ', ' ', attr);
+ grid_col = col_off - pum_base_width - n + 1;
} else {
- grid_fill(&pum_grid, row, row + 1, col,
+ grid_fill(&pum_grid, row, row + 1, grid_col,
col_off + pum_base_width + n, ' ', ' ', attr);
- col = col_off + pum_base_width + n;
+ grid_col = col_off + pum_base_width + n;
}
totwidth = pum_base_width + n;
}
if (pum_rl) {
- grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, col + 1,
+ grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, grid_col + 1,
' ', ' ', attr);
} else {
- grid_fill(&pum_grid, row, row + 1, col, col_off + pum_width, ' ', ' ',
+ grid_fill(&pum_grid, row, row + 1, grid_col, col_off + pum_width, ' ', ' ',
attr);
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 2204e2a62a..32d0ebe8eb 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -657,7 +657,8 @@ static int qf_get_next_str_line(qfstate_T *state)
state->linebuf = IObuff;
state->linelen = len;
}
- STRLCPY(state->linebuf, p_str, state->linelen + 1);
+ memcpy(state->linebuf, p_str, state->linelen);
+ state->linebuf[state->linelen] = '\0';
// Increment using len in order to discard the rest of the line if it
// exceeds LINE_MAXLEN.
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 3e7306bad3..41c927eaa6 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -18,22 +18,20 @@
#include "nvim/garray.h"
#include "nvim/os/input.h"
-/*
- * Logging of NFA engine.
- *
- * The NFA engine can write four log files:
- * - Error log: Contains NFA engine's fatal errors.
- * - Dump log: Contains compiled NFA state machine's information.
- * - Run log: Contains information of matching procedure.
- * - Debug log: Contains detailed information of matching procedure. Can be
- * disabled by undefining NFA_REGEXP_DEBUG_LOG.
- * The first one can also be used without debug mode.
- * The last three are enabled when compiled as debug mode and individually
- * disabled by commenting them out.
- * The log files can get quite big!
- * Do disable all of this when compiling Vim for debugging, undefine REGEXP_DEBUG in
- * regexp.c
- */
+// Logging of NFA engine.
+//
+// The NFA engine can write four log files:
+// - Error log: Contains NFA engine's fatal errors.
+// - Dump log: Contains compiled NFA state machine's information.
+// - Run log: Contains information of matching procedure.
+// - Debug log: Contains detailed information of matching procedure. Can be
+// disabled by undefining NFA_REGEXP_DEBUG_LOG.
+// The first one can also be used without debug mode.
+// The last three are enabled when compiled as debug mode and individually
+// disabled by commenting them out.
+// The log files can get quite big!
+// To disable all of this when compiling Vim for debugging, undefine REGEXP_DEBUG in
+// regexp.c
#ifdef REGEXP_DEBUG
# define NFA_REGEXP_ERROR_LOG "nfa_regexp_error.log"
# define NFA_REGEXP_DUMP_LOG "nfa_regexp_dump.log"
@@ -2567,20 +2565,20 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
ga_concat(indent, (char_u *)"| ");
else
ga_concat(indent, (char_u *)" ");
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
nfa_print_state2(debugf, state->out, indent);
/* replace last part of indent for state->out1 */
indent->ga_len -= 3;
ga_concat(indent, (char_u *)" ");
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
nfa_print_state2(debugf, state->out1, indent);
/* shrink indent */
indent->ga_len -= 3;
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
}
/*
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index a938a3b062..e62e3ca7bc 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -5933,6 +5933,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
// Only 1 cell left, but character requires 2 cells:
// display a '>' in the last column to avoid wrapping. */
c = '>';
+ u8c = '>';
+ u8cc[0] = 0;
mbyte_cells = 1;
}
@@ -5963,6 +5965,13 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
clear_next_cell = true;
}
+ // When at the start of the text and overwriting the right half of a
+ // two-cell character in the same grid, truncate that into a '>'.
+ if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
+ grid->chars[off - 1][0] = '>';
+ grid->chars[off - 1][1] = 0;
+ }
+
schar_copy(grid->chars[off], buf);
grid->attrs[off] = attr;
if (mbyte_cells == 2) {
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 32be714184..a308df07d1 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -1740,7 +1740,7 @@ char_u *get_sign_name(expand_T *xp, int idx)
case EXP_SUBCMD:
return (char_u *)cmds[idx];
case EXP_DEFINE: {
- char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=",
+ char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=",
NULL };
return (char_u *)define_arg[idx];
}
@@ -1849,6 +1849,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
case SIGNCMD_DEFINE:
if (STRNCMP(last, "texthl", 6) == 0
|| STRNCMP(last, "linehl", 6) == 0
+ || STRNCMP(last, "culhl", 5) == 0
|| STRNCMP(last, "numhl", 5) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
} else if (STRNCMP(last, "icon", 4) == 0) {
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 04068a3cb8..d97c24dcf7 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -1466,6 +1466,17 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
int width, height;
vterm_get_size(term->vt, &height, &width);
+ // May still have pending scrollback after increase in terminal height if the
+ // scrollback wasn't refreshed in time; append these to the top of the buffer.
+ int row_offset = term->sb_pending;
+ while (term->sb_pending > 0 && buf->b_ml.ml_line_count < height) {
+ fetch_row(term, term->sb_pending - row_offset - 1, width);
+ ml_append(0, (uint8_t *)term->textbuf, 0, false);
+ appended_lines(0, 1);
+ term->sb_pending--;
+ }
+
+ row_offset -= term->sb_pending;
while (term->sb_pending > 0) {
// This means that either the window height has decreased or the screen
// became full and libvterm had to push all rows up. Convert the first
@@ -1476,7 +1487,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
ml_delete(1, false);
deleted_lines(1, 1);
}
- fetch_row(term, -term->sb_pending, width);
+ fetch_row(term, -term->sb_pending - row_offset, width);
int buf_index = (int)buf->b_ml.ml_line_count - height;
ml_append(buf_index, (uint8_t *)term->textbuf, 0, false);
appended_lines(buf_index, 1);
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index ab047fd2a8..b0d872e392 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -64,6 +64,9 @@ if has('reltime')
let s:start_time = reltime()
endif
+" Always use forward slashes.
+set shellslash
+
" Common with all tests on all systems.
source setup.vim
@@ -104,9 +107,6 @@ lang mess C
" Nvim: append runtime from build dir, which contains the generated doc/tags.
let &runtimepath .= ','.expand($BUILD_DIR).'/runtime/'
-" Always use forward slashes.
-set shellslash
-
let s:t_bold = &t_md
let s:t_normal = &t_me
if has('win32')
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index b3df8c63e6..fdae0697c3 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -13,7 +13,7 @@ set fillchars=vert:\|,fold:-
set laststatus=1
set listchars=eol:$
set joinspaces
-set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd
+set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd
set nrformats+=octal
set shortmess-=F
set sidescroll=0
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 4e1a24af61..45285b69a1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -2380,95 +2380,7 @@ func Test_autocmd_was_using_freed_memory()
pclose
endfunc
-func Test_FileChangedShell_reload()
- if !has('unix')
- return
- endif
- augroup testreload
- au FileChangedShell Xchanged let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
- augroup END
- new Xchanged
- call setline(1, 'reload this')
- write
- " Need to wait until the timestamp would change by at least a second.
- sleep 2
- silent !echo 'extra line' >>Xchanged
- checktime
- call assert_equal('changed', g:reason)
- call assert_equal(2, line('$'))
- call assert_equal('extra line', getline(2))
-
- " Only triggers once
- let g:reason = ''
- checktime
- call assert_equal('', g:reason)
-
- " When deleted buffer is not reloaded
- silent !rm Xchanged
- let g:reason = ''
- checktime
- call assert_equal('deleted', g:reason)
- call assert_equal(2, line('$'))
- call assert_equal('extra line', getline(2))
-
- " When recreated buffer is reloaded
- call setline(1, 'buffer is changed')
- silent !echo 'new line' >>Xchanged
- let g:reason = ''
- checktime
- call assert_equal('conflict', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- " Only mode changed
- silent !chmod +x Xchanged
- let g:reason = ''
- checktime
- call assert_equal('mode', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- " Only time changed
- sleep 2
- silent !touch Xchanged
- let g:reason = ''
- checktime
- call assert_equal('time', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- if has('persistent_undo')
- " With an undo file the reload can be undone and a change before the
- " reload.
- set undofile
- call setline(2, 'before write')
- write
- call setline(2, 'after write')
- sleep 2
- silent !echo 'different line' >>Xchanged
- let g:reason = ''
- checktime
- call assert_equal('conflict', g:reason)
- call assert_equal(3, line('$'))
- call assert_equal('before write', getline(2))
- call assert_equal('different line', getline(3))
- " undo the reload
- undo
- call assert_equal(2, line('$'))
- call assert_equal('after write', getline(2))
- " undo the change before reload
- undo
- call assert_equal(2, line('$'))
- call assert_equal('before write', getline(2))
-
- set noundofile
- endif
-
-
- au! testreload
- bwipe!
- call delete('Xchanged')
-endfunc
+" FileChangedShell tested in test_filechanged.vim
func LogACmd()
call add(g:logged, line('$'))
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index 20758b0c0a..af42b3857d 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -346,4 +346,12 @@ func Test_blob_sort()
endif
endfunc
+" The following used to cause an out-of-bounds memory access
+func Test_blob2string()
+ let v = '0z' .. repeat('01010101.', 444)
+ let v ..= '01'
+ exe 'let b = ' .. v
+ call assert_equal(v, string(b))
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 49a5386337..1672b0e840 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -467,6 +467,43 @@ func Test_getcompletion()
call assert_fails('call getcompletion("abc", [])', 'E475:')
endfunc
+func Test_fullcommand()
+ let tests = {
+ \ '': '',
+ \ ':': '',
+ \ ':::': '',
+ \ ':::5': '',
+ \ 'not_a_cmd': '',
+ \ 'Check': '',
+ \ 'syntax': 'syntax',
+ \ ':syntax': 'syntax',
+ \ '::::syntax': 'syntax',
+ \ 'sy': 'syntax',
+ \ 'syn': 'syntax',
+ \ 'synt': 'syntax',
+ \ ':sy': 'syntax',
+ \ '::::sy': 'syntax',
+ \ 'match': 'match',
+ \ '2match': 'match',
+ \ '3match': 'match',
+ \ 'aboveleft': 'aboveleft',
+ \ 'abo': 'aboveleft',
+ \ 's': 'substitute',
+ \ '5s': 'substitute',
+ \ ':5s': 'substitute',
+ \ "'<,'>s": 'substitute',
+ \ ":'<,'>s": 'substitute',
+ \ 'CheckUni': 'CheckUnix',
+ \ 'CheckUnix': 'CheckUnix',
+ \ }
+
+ for [in, want] in items(tests)
+ call assert_equal(want, fullcommand(in))
+ endfor
+
+ call assert_equal('syntax', 'syn'->fullcommand())
+endfunc
+
func Test_shellcmd_completion()
let save_path = $PATH
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index 12327f34d6..c2a9683f7c 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -263,6 +263,31 @@ func Test_display_scroll_at_topline()
call StopVimInTerminal(buf)
endfunc
+" Test for 'eob' (EndOfBuffer) item in 'fillchars'
+func Test_eob_fillchars()
+ " default value (skipped)
+ " call assert_match('eob:\~', &fillchars)
+ " invalid values
+ call assert_fails(':set fillchars=eob:', 'E474:')
+ call assert_fails(':set fillchars=eob:xy', 'E474:')
+ call assert_fails(':set fillchars=eob:\255', 'E474:')
+ call assert_fails(':set fillchars=eob:<ff>', 'E474:')
+ call assert_fails(":set fillchars=eob:\x01", 'E474:')
+ call assert_fails(':set fillchars=eob:\\x01', 'E474:')
+ " default is ~
+ new
+ redraw
+ call assert_equal('~', Screenline(2))
+ set fillchars=eob:+
+ redraw
+ call assert_equal('+', Screenline(2))
+ set fillchars=eob:\
+ redraw
+ call assert_equal(' ', nr2char(screenchar(2, 1)))
+ set fillchars&
+ close
+endfunc
+
func Test_display_linebreak_breakat()
new
vert resize 25
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 37786f3ca0..fc4e80f0d6 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1294,6 +1294,7 @@ func Test_edit_forbidden()
call assert_fails(':Sandbox', 'E48:')
delcom Sandbox
call assert_equal(['a'], getline(1,'$'))
+
" 2) edit with textlock set
fu! DoIt()
call feedkeys("i\<del>\<esc>", 'tnix')
@@ -1313,6 +1314,7 @@ func Test_edit_forbidden()
catch /^Vim\%((\a\+)\)\=:E117/ " catch E117: unknown function
endtry
au! InsertCharPre
+
" 3) edit when completion is shown
fun! Complete(findstart, base)
if a:findstart
@@ -1330,6 +1332,7 @@ func Test_edit_forbidden()
endtry
delfu Complete
set completefunc=
+
if has("rightleft") && exists("+fkmap")
" 4) 'R' when 'fkmap' and 'revins' is set.
set revins fkmap
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 1c645ad0f8..92e0559618 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -85,7 +85,7 @@ endfunc
func Test_ex_mode_count_overflow()
" this used to cause a crash
let lines =<< trim END
- call feedkeys("\<Esc>Q\<CR>")
+ call feedkeys("\<Esc>gQ\<CR>")
v9|9silent! vi|333333233333y32333333%O
call writefile(['done'], 'Xdidexmode')
qall!
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
new file mode 100644
index 0000000000..b95cd5faf8
--- /dev/null
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -0,0 +1,149 @@
+" Tests for when a file was changed outside of Vim.
+
+func Test_FileChangedShell_reload()
+ if !has('unix')
+ return
+ endif
+ augroup testreload
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
+ augroup END
+ new Xchanged_r
+ call setline(1, 'reload this')
+ write
+ " Need to wait until the timestamp would change by at least a second.
+ sleep 2
+ silent !echo 'extra line' >>Xchanged_r
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+
+ " Only triggers once
+ let g:reason = ''
+ checktime
+ call assert_equal('', g:reason)
+
+ " When deleted buffer is not reloaded
+ silent !rm Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('deleted', g:reason)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+
+ " When recreated buffer is reloaded
+ call setline(1, 'buffer is changed')
+ silent !echo 'new line' >>Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('conflict', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only mode changed
+ silent !chmod +x Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('mode', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only time changed
+ sleep 2
+ silent !touch Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('time', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ if has('persistent_undo')
+ " With an undo file the reload can be undone and a change before the
+ " reload.
+ set undofile
+ call setline(2, 'before write')
+ write
+ call setline(2, 'after write')
+ sleep 2
+ silent !echo 'different line' >>Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('conflict', g:reason)
+ call assert_equal(3, line('$'))
+ call assert_equal('before write', getline(2))
+ call assert_equal('different line', getline(3))
+ " undo the reload
+ undo
+ call assert_equal(2, line('$'))
+ call assert_equal('after write', getline(2))
+ " undo the change before reload
+ undo
+ call assert_equal(2, line('$'))
+ call assert_equal('before write', getline(2))
+
+ set noundofile
+ endif
+
+ au! testreload
+ bwipe!
+ call delete(undofile('Xchanged_r'))
+ call delete('Xchanged_r')
+endfunc
+
+func Test_file_changed_dialog()
+ throw 'skipped: TODO: '
+ if !has('unix') || has('gui_running')
+ return
+ endif
+ au! FileChangedShell
+
+ new Xchanged_d
+ call setline(1, 'reload this')
+ write
+ " Need to wait until the timestamp would change by at least a second.
+ sleep 2
+ silent !echo 'extra line' >>Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W11:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('reload this', getline(1))
+ call assert_equal('extra line', getline(2))
+
+ " delete buffer, only shows an error, no prompt
+ silent !rm Xchanged_d
+ checktime
+ call assert_match('E211:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+ let v:warningmsg = 'empty'
+
+ " change buffer, recreate the file and reload
+ call setline(1, 'buffer is changed')
+ silent !echo 'new line' >Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W12:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only mode changed, reload
+ silent !chmod +x Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W16:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only time changed, no prompt
+ sleep 2
+ silent !touch Xchanged_d
+ let v:warningmsg = ''
+ checktime
+ call assert_equal('', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ bwipe!
+ call delete('Xchanged_d')
+endfunc
diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
index 465613f1cf..81127ea59a 100644
--- a/src/nvim/testdir/test_fileformat.vim
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -1,5 +1,4 @@
" Test behavior of fileformat after bwipeout of last buffer
-
func Test_fileformat_after_bw()
bwipeout
set fileformat&
@@ -32,6 +31,251 @@ func Test_fileformat_autocommand()
bw!
endfunc
+" Convert the contents of a file into a literal string
+func s:file2str(fname)
+ let b = readfile(a:fname, 'B')
+ let s = ''
+ for c in b
+ let s .= nr2char(c)
+ endfor
+ return s
+endfunc
+
+" Concatenate the contents of files 'f1' and 'f2' and create 'destfile'
+func s:concat_files(f1, f2, destfile)
+ let b1 = readfile(a:f1, 'B')
+ let b2 = readfile(a:f2, 'B')
+ let b3 = b1 + b2
+ call writefile(b3, a:destfile)
+endfun
+
+" Test for a lot of variations of the 'fileformats' option
+func Test_fileformats()
+ " create three test files, one in each format
+ call writefile(['unix', 'unix'], 'XXUnix')
+ call writefile(["dos\r", "dos\r"], 'XXDos')
+ call writefile(["mac\rmac\r"], 'XXMac', 'b')
+ " create a file with no End Of Line
+ call writefile(["noeol"], 'XXEol', 'b')
+ " create mixed format files
+ call s:concat_files('XXUnix', 'XXDos', 'XXUxDs')
+ call s:concat_files('XXUnix', 'XXMac', 'XXUxMac')
+ call s:concat_files('XXDos', 'XXMac', 'XXDosMac')
+ call s:concat_files('XXMac', 'XXEol', 'XXMacEol')
+ call s:concat_files('XXUxDs', 'XXMac', 'XXUxDsMc')
+
+ new
+
+ " Test 1: try reading and writing with 'fileformats' empty
+ set fileformats=
+
+ " try with 'fileformat' set to 'unix'
+ set fileformat=unix
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r\n", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " try with 'fileformat' set to 'dos'
+ set fileformat=dos
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\n", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r\r\n", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " try with 'fileformat' set to 'mac'
+ set fileformat=mac
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n\r", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n\r", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " Test 2: try reading and writing with 'fileformats' set to one format
+
+ " try with 'fileformats' set to 'unix'
+ set fileformats=unix
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " try with 'fileformats' set to 'dos'
+ set fileformats=dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " try with 'fileformats' set to 'mac'
+ set fileformats=mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " Test 3: try reading and writing with 'fileformats' set to two formats
+
+ " try with 'fileformats' set to 'unix,dos'
+ set fileformats=unix,dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXUxMac
+ w! Xtest
+ call assert_equal("unix\nunix\nmac\rmac\r\n", s:file2str('Xtest'))
+ bwipe XXUxMac
+
+ e! XXDosMac
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\nmac\rmac\r\r\n", s:file2str('Xtest'))
+ bwipe XXDosMac
+
+ " try with 'fileformats' set to 'unix,mac'
+ set fileformats=unix,mac
+ e! XXUxDs
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\n", s:file2str('Xtest'))
+ bwipe XXUxDs
+
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXDosMac
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\nmac\rmac\r", s:file2str('Xtest'))
+ bwipe XXDosMac
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("unix,mac:unix\nnoeol\n", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ " try with 'fileformats' set to 'dos,mac'
+ set fileformats=dos,mac
+ e! XXUxDs
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\n", s:file2str('Xtest'))
+ bwipe XXUxDs
+
+ e! XXUxMac
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("dos,mac:dos\r\nunix\r\nunix\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe! XXUxMac
+
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXMacEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("dos,mac:mac\rmac\rmac\rnoeol\r", s:file2str('Xtest'))
+ bwipe! XXMacEol
+
+ " Test 4: try reading and writing with 'fileformats' set to three formats
+ set fileformats=unix,dos,mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("unix,dos,mac:unix\nnoeol\n", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ set fileformats=mac,dos,unix
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("mac,dos,unix:mac\rnoeol\r", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ " Test 5: try with 'binary' set
+ set fileformats=mac,unix,dos
+ set binary
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ set fileformats=mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ set fileformats=dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n", s:file2str('Xtest'))
+ bwipe! XXUnix
+
+ set nobinary ff& ffs&
+
+ " cleanup
+ only
+ %bwipe!
+ call delete('XXUnix')
+ call delete('XXDos')
+ call delete('XXMac')
+ call delete('XXEol')
+ call delete('XXUxDs')
+ call delete('XXUxMac')
+ call delete('XXDosMac')
+ call delete('XXMacEol')
+ call delete('XXUxDsMc')
+ call delete('Xtest')
+endfunc
+
" Test for changing the fileformat using ++read
func Test_fileformat_plusplus_read()
new
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 31052ce47d..1ee23bb646 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -117,7 +117,7 @@ let s:filename_checks = {
\ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'],
\ 'crm': ['file.crm'],
\ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'],
- \ 'cs': ['file.cs'],
+ \ 'cs': ['file.cs', 'file.csx'],
\ 'csc': ['file.csc'],
\ 'csdl': ['file.csdl'],
\ 'csp': ['file.csp', 'file.fdr'],
@@ -204,6 +204,7 @@ let s:filename_checks = {
\ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'],
\ 'gnuplot': ['file.gpi'],
\ 'go': ['file.go'],
+ \ 'gomod': ['go.mod'],
\ 'gp': ['file.gp', '.gprc'],
\ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'],
\ 'grads': ['file.gs'],
@@ -226,6 +227,7 @@ let s:filename_checks = {
\ 'hollywood': ['file.hws'],
\ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'],
\ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny', 'any/etc/hosts.allow', 'any/etc/hosts.deny'],
+ \ 'i3config': ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'],
\ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'],
\ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig'],
\ 'natural': ['file.NSA', 'file.NSC', 'file.NSG', 'file.NSL', 'file.NSM', 'file.NSN', 'file.NSP', 'file.NSS'],
@@ -261,7 +263,7 @@ let s:filename_checks = {
\ 'jgraph': ['file.jgr'],
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'],
- \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'],
\ 'jsonc': ['file.jsonc'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],
@@ -455,6 +457,7 @@ let s:filename_checks = {
\ 'skill': ['file.il', 'file.ils', 'file.cdf'],
\ 'slang': ['file.sl'],
\ 'slice': ['file.ice'],
+ \ 'solution': ['file.sln'],
\ 'slpconf': ['/etc/slp.conf', 'any/etc/slp.conf'],
\ 'slpreg': ['/etc/slp.reg', 'any/etc/slp.reg'],
\ 'slpspi': ['/etc/slp.spi', 'any/etc/slp.spi'],
@@ -1002,4 +1005,44 @@ func Test_fs_file()
filetype off
endfunc
+func Test_dep3patch_file()
+ filetype on
+
+ call assert_true(mkdir('debian/patches', 'p'))
+
+ " series files are not patches
+ call writefile(['Description: some awesome patch'], 'debian/patches/series')
+ split debian/patches/series
+ call assert_notequal('dep3patch', &filetype)
+ bwipe!
+
+ " diff/patch files without the right headers should still show up as ft=diff
+ call writefile([], 'debian/patches/foo.diff')
+ split debian/patches/foo.diff
+ call assert_equal('diff', &filetype)
+ bwipe!
+
+ " Files with the right headers are detected as dep3patch, even if they don't
+ " have a diff/patch extension
+ call writefile(['Subject: dep3patches'], 'debian/patches/bar')
+ split debian/patches/bar
+ call assert_equal('dep3patch', &filetype)
+ bwipe!
+
+ " Files in sub-directories are detected
+ call assert_true(mkdir('debian/patches/s390x', 'p'))
+ call writefile(['Subject: dep3patches'], 'debian/patches/s390x/bar')
+ split debian/patches/s390x/bar
+ call assert_equal('dep3patch', &filetype)
+ bwipe!
+
+ " The detection stops when seeing the "header end" marker
+ call writefile(['---', 'Origin: the cloud'], 'debian/patches/baz')
+ split debian/patches/baz
+ call assert_notequal('dep3patch', &filetype)
+ bwipe!
+
+ call delete('debian/patches', 'rf')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index f4ee539803..0bcbd9c4a5 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -331,7 +331,7 @@ func Test_listchars_invalid()
call assert_fails('set listchars=space:xx', 'E474:')
call assert_fails('set listchars=tab:xxxx', 'E474:')
- " Has non-single width character
+ " Has double-width character
call assert_fails('set listchars=space:·', 'E474:')
call assert_fails('set listchars=tab:·x', 'E474:')
call assert_fails('set listchars=tab:x·', 'E474:')
@@ -339,6 +339,20 @@ func Test_listchars_invalid()
call assert_fails('set listchars=multispace:·', 'E474:')
call assert_fails('set listchars=multispace:xxx·', 'E474:')
+ " Has control character
+ call assert_fails("set listchars=space:\x01", 'E474:')
+ call assert_fails("set listchars=tab:\x01x", 'E474:')
+ call assert_fails("set listchars=tab:x\x01", 'E474:')
+ call assert_fails("set listchars=tab:xx\x01", 'E474:')
+ call assert_fails("set listchars=multispace:\x01", 'E474:')
+ call assert_fails("set listchars=multispace:xxx\x01", 'E474:')
+ call assert_fails('set listchars=space:\\x01', 'E474:')
+ call assert_fails('set listchars=tab:\\x01x', 'E474:')
+ call assert_fails('set listchars=tab:x\\x01', 'E474:')
+ call assert_fails('set listchars=tab:xx\\x01', 'E474:')
+ call assert_fails('set listchars=multispace:\\x01', 'E474:')
+ call assert_fails('set listchars=multispace:xxx\\x01', 'E474:')
+
enew!
set ambiwidth& listchars& ff&
endfunction
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 2140fe21ea..e0286548d9 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -108,3 +108,11 @@ func Test_echospace()
set ruler& showcmd&
endfunc
+
+" this was missing a terminating NUL
+func Test_echo_string_partial()
+ function CountSpaces()
+ endfunction
+ call assert_equal("function('CountSpaces', [{'ccccccccccc': ['ab', 'cd'], 'aaaaaaaaaaa': v:false, 'bbbbbbbbbbbb': ''}])", string(function('CountSpaces', [#{aaaaaaaaaaa: v:false, bbbbbbbbbbbb: '', ccccccccccc: ['ab', 'cd']}])))
+endfunc
+
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 7d9cada074..5946732937 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -732,4 +732,25 @@ func Test_opt_reset_scroll()
call delete('Xscroll')
endfunc
+" Test for the 'cdhome' option
+func Test_opt_cdhome()
+ if has('unix') || has('vms')
+ throw 'Skipped: only works on non-Unix'
+ endif
+
+ set cdhome&
+ call assert_equal(0, &cdhome)
+ set cdhome
+
+ " This paragraph is copied from Test_cd_no_arg().
+ let path = getcwd()
+ cd
+ call assert_equal($HOME, getcwd())
+ call assert_notequal(path, getcwd())
+ exe 'cd ' .. fnameescape(path)
+ call assert_equal(path, getcwd())
+
+ set cdhome&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index 799e6cb57b..ff9ba3d8ed 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -218,15 +218,13 @@ func Test_sign_completion()
call assert_equal('"sign define jump list place undefine unplace', @:)
call feedkeys(":sign define Sign \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign icon= linehl= numhl= text= texthl=', @:)
+ call assert_equal('"sign define Sign culhl= icon= linehl= numhl= text= texthl=', @:)
- call feedkeys(":sign define Sign linehl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign linehl=SpellBad SpellCap ' .
- \ 'SpellLocal SpellRare', @:)
-
- call feedkeys(":sign define Sign texthl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign texthl=SpellBad SpellCap ' .
- \ 'SpellLocal SpellRare', @:)
+ for hl in ['culhl', 'linehl', 'numhl', 'texthl']
+ call feedkeys(":sign define Sign "..hl.."=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sign define Sign '..hl..'=SpellBad SpellCap ' .
+ \ 'SpellLocal SpellRare', @:)
+ endfor
call writefile(repeat(["Sun is shining"], 30), "XsignOne")
call writefile(repeat(["Sky is blue"], 30), "XsignTwo")
@@ -417,20 +415,21 @@ func Test_sign_funcs()
" Tests for sign_define()
let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error',
- \ 'culhl': 'Visual'}
+ \ 'culhl': 'Visual', 'numhl': 'Number'}
call assert_equal(0, "sign1"->sign_define(attr))
- call assert_equal([{'name' : 'sign1', 'texthl' : 'Error',
- \ 'linehl' : 'Search', 'culhl': 'Visual', 'text' : '=>'}],
+ call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl': 'Search',
+ \ 'culhl': 'Visual', 'numhl': 'Number', 'text' : '=>'}],
\ sign_getdefined())
" Define a new sign without attributes and then update it
call sign_define("sign2")
let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange',
- \ 'culhl': 'DiffDelete', 'icon' : 'sign2.ico'}
+ \ 'culhl': 'DiffDelete', 'numhl': 'Number', 'icon' : 'sign2.ico'}
call Sign_define_ignore_error("sign2", attr)
call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange',
\ 'linehl' : 'DiffAdd', 'culhl' : 'DiffDelete', 'text' : '!!',
- \ 'icon' : 'sign2.ico'}], "sign2"->sign_getdefined())
+ \ 'numhl': 'Number', 'icon' : 'sign2.ico'}],
+ \ "sign2"->sign_getdefined())
" Test for a sign name with digits
call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'}))
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 113c85acef..20b760ac15 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -137,7 +137,7 @@ func Test_substitute_repeat()
" This caused an invalid memory access.
split Xfile
s/^/x
- call feedkeys("Qsc\<CR>y", 'tx')
+ call feedkeys("gQsc\<CR>y", 'tx')
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 5cc0da2586..aae315b2c5 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -279,7 +279,7 @@ func Test_ex_mode()
endfunc
let timer = timer_start(40, function('g:Foo'), {'repeat':-1})
" This used to throw error E749.
- exe "normal Qsleep 100m\rvi\r"
+ exe "normal gQsleep 100m\rvi\r"
call timer_stop(timer)
endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index dbabdcf427..8344598486 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -144,7 +144,6 @@ endfun
" Test Virtual replace mode.
func Test_virtual_replace()
- throw 'skipped: TODO: '
if exists('&t_kD')
let save_t_kD = &t_kD
endif
@@ -166,7 +165,6 @@ func Test_virtual_replace()
\ ], getline(1, 6))
normal G
mark a
- inoremap <C-D> <Del>
exe "normal o0\<C-D>\nabcdefghi\njk\tlmn\n opq\trst\n\<C-D>uvwxyz\n"
exe "normal 'ajgR0\<C-D> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR" . repeat("\<BS>", 29)
call assert_equal([' 1',
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 5bb6059fa7..5fec41f9a5 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -114,6 +114,12 @@ static void tinput_done_event(void **argv)
static void tinput_wait_enqueue(void **argv)
{
TermInput *input = argv[0];
+ if (rbuffer_size(input->key_buffer) == 0 && input->paste == 3) {
+ const String keys = { .data = "", .size = 0 };
+ String copy = copy_string(keys);
+ multiqueue_put(main_loop.events, tinput_paste_event, 3,
+ copy.data, copy.size, (intptr_t)input->paste);
+ }
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
const String keys = { .data = buf, .size = len };
if (input->paste) {
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index bb75286369..e7a60aca49 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -308,23 +308,39 @@ static void terminfo_start(UI *ui)
// Enable bracketed paste
unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
+ int ret;
uv_loop_init(&data->write_loop);
if (data->out_isatty) {
- uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
+ ret = uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
+ if (ret) {
+ ELOG("uv_tty_init failed: %s", uv_strerror(ret));
+ }
#ifdef WIN32
- uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW);
+ ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW);
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
#else
int retry_count = 10;
// A signal may cause uv_tty_set_mode() to fail (e.g., SIGCONT). Retry a
// few times. #12322
- while (uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO) == UV_EINTR
+ while ((ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO)) == UV_EINTR
&& retry_count > 0) {
retry_count--;
}
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
#endif
} else {
- uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
- uv_pipe_open(&data->output_handle.pipe, data->out_fd);
+ ret = uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
+ if (ret) {
+ ELOG("uv_pipe_init failed: %s", uv_strerror(ret));
+ }
+ ret = uv_pipe_open(&data->output_handle.pipe, data->out_fd);
+ if (ret) {
+ ELOG("uv_pipe_open failed: %s", uv_strerror(ret));
+ }
}
flush_buf(ui);
}
@@ -1086,8 +1102,14 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx)
// after calling uv_tty_set_mode. So, set the mode of the TTY again here.
// #13073
if (data->is_starting && data->input.in_fd == STDERR_FILENO) {
- uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL);
- uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+ int ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL);
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
+ ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
}
#endif
tui_set_mode(ui, (ModeShape)mode_idx);
@@ -2081,8 +2103,11 @@ static void flush_buf(UI *ui)
fwrite(bufs[i].base, bufs[i].len, 1, data->screenshot);
}
} else {
- uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle),
- bufs, (unsigned)(bufp - bufs), NULL);
+ int ret = uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle),
+ bufs, (unsigned)(bufp - bufs), NULL);
+ if (ret) {
+ ELOG("uv_write failed: %s", uv_strerror(ret));
+ }
uv_run(&data->write_loop, UV_RUN_DEFAULT);
}
data->bufpos = 0;