aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/CMakeLists.txt5
-rw-r--r--src/nvim/api/ui.c15
-rw-r--r--src/nvim/api/ui_events.in.h2
-rw-r--r--src/nvim/api/vim.c29
-rw-r--r--src/nvim/auevents.lua2
-rw-r--r--src/nvim/channel.c40
-rw-r--r--src/nvim/edit.c27
-rw-r--r--src/nvim/eval.c55
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/event/libuv_process.c7
-rw-r--r--src/nvim/event/loop.c1
-rw-r--r--src/nvim/event/loop.h1
-rw-r--r--src/nvim/event/process.c56
-rw-r--r--src/nvim/event/process.h3
-rw-r--r--src/nvim/ex_docmd.c24
-rw-r--r--src/nvim/fileio.c153
-rw-r--r--src/nvim/getchar.c127
-rw-r--r--src/nvim/getchar.h24
-rw-r--r--src/nvim/globals.h12
-rw-r--r--src/nvim/keymap.h2
-rw-r--r--src/nvim/macros.h18
-rw-r--r--src/nvim/main.c157
-rw-r--r--src/nvim/memline.c30
-rw-r--r--src/nvim/message.c10
-rw-r--r--src/nvim/misc1.c2
-rw-r--r--src/nvim/msgpack_rpc/server.c43
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/os/fileio.c45
-rw-r--r--src/nvim/os/fileio.h3
-rw-r--r--src/nvim/os/fs.c51
-rw-r--r--src/nvim/os/process.c2
-rw-r--r--src/nvim/os/shell.c41
-rw-r--r--src/nvim/os/signal.c2
-rw-r--r--src/nvim/os/stdpaths.c2
-rw-r--r--src/nvim/po/CMakeLists.txt8
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/screen.c38
-rw-r--r--src/nvim/shada.c255
-rw-r--r--src/nvim/terminal.c11
-rw-r--r--src/nvim/testdir/Makefile2
-rw-r--r--src/nvim/testdir/runtest.vim2
-rw-r--r--src/nvim/testdir/setup.vim8
-rw-r--r--src/nvim/testdir/test_cmdline.vim6
-rw-r--r--src/nvim/testdir/test_find_complete.vim3
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim2
-rw-r--r--src/nvim/testdir/test_makeencoding.vim9
-rw-r--r--src/nvim/testdir/test_options.vim3
-rw-r--r--src/nvim/testdir/test_quickfix.vim16
-rw-r--r--src/nvim/testdir/test_recover.vim5
-rw-r--r--src/nvim/testdir/test_stat.vim2
-rw-r--r--src/nvim/testdir/test_system.vim10
-rw-r--r--src/nvim/tui/input.c15
-rw-r--r--src/nvim/tui/tui.c11
-rw-r--r--src/nvim/viml/parser/expressions.c3
55 files changed, 923 insertions, 483 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 606baff619..2d803792c8 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -359,6 +359,10 @@ endforeach()
# Our dependencies come first.
+if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
+ list(APPEND NVIM_LINK_LIBRARIES pthread c++abi)
+endif()
+
if (LibIntl_FOUND)
list(APPEND NVIM_LINK_LIBRARIES ${LibIntl_LIBRARY})
endif()
@@ -437,6 +441,7 @@ if(WIN32)
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tidy.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/win32yank.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty-agent.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
+ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/xxd.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/D3Dcompiler_47.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/libEGL.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 4870c3fb8a..4cd2657561 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -56,7 +56,8 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
if (pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(err, kErrorTypeException, "UI already attached for channel");
+ api_set_error(err, kErrorTypeException,
+ "UI already attached to channel: %" PRId64, channel_id);
return;
}
@@ -130,7 +131,8 @@ void nvim_ui_detach(uint64_t channel_id, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(err, kErrorTypeException, "UI is not attached for channel");
+ api_set_error(err, kErrorTypeException,
+ "UI not attached to channel: %" PRId64, channel_id);
return;
}
remote_ui_disconnect(channel_id);
@@ -142,7 +144,8 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width,
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(err, kErrorTypeException, "UI is not attached for channel");
+ api_set_error(err, kErrorTypeException,
+ "UI not attached to channel: %" PRId64, channel_id);
return;
}
@@ -163,7 +166,8 @@ void nvim_ui_set_option(uint64_t channel_id, String name,
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(error, kErrorTypeException, "UI is not attached for channel");
+ api_set_error(error, kErrorTypeException,
+ "UI not attached to channel: %" PRId64, channel_id);
return;
}
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
@@ -209,7 +213,8 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error)
return;
}
- api_set_error(error, kErrorTypeValidation, "No such ui option");
+ api_set_error(error, kErrorTypeValidation, "No such UI option: %s",
+ name.data);
#undef UI_EXT_OPTION
}
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index c599b0ce72..96d494460b 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -10,7 +10,7 @@
#include "nvim/func_attr.h"
#include "nvim/ui.h"
-void resize(Integer rows, Integer columns)
+void resize(Integer width, Integer height)
FUNC_API_SINCE(3);
void clear(void)
FUNC_API_SINCE(3);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 86827224b7..af3d379870 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -239,15 +239,17 @@ String nvim_command_output(String command, Error *err)
}
if (capture_local.ga_len > 1) {
- // redir always(?) prepends a newline; remove it.
- char *s = capture_local.ga_data;
- assert(s[0] == '\n');
- memmove(s, s + 1, (size_t)capture_local.ga_len);
- s[capture_local.ga_len - 1] = '\0';
- return (String) { // Caller will free the memory.
- .data = s,
- .size = (size_t)(capture_local.ga_len - 1),
+ String s = (String){
+ .data = capture_local.ga_data,
+ .size = (size_t)capture_local.ga_len,
};
+ // redir usually (except :echon) prepends a newline.
+ if (s.data[0] == '\n') {
+ memmove(s.data, s.data + 1, s.size);
+ s.data[s.size - 1] = '\0';
+ s.size = s.size - 1;
+ }
+ return s; // Caller will free the memory.
}
theend:
@@ -1471,6 +1473,17 @@ Float nvim__id_float(Float flt)
return flt;
}
+/// Gets internal stats.
+///
+/// @return Map of various internal stats.
+Dictionary nvim__stats(void)
+{
+ Dictionary rv = ARRAY_DICT_INIT;
+ PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync));
+ PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
+ return rv;
+}
+
/// Gets a list of dictionaries representing attached UIs.
///
/// @return Array of UI dictionaries
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 7dfaf54ff0..1153314e76 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -92,6 +92,8 @@ return {
'VimLeave', -- before exiting Vim
'VimLeavePre', -- before exiting Vim and writing ShaDa file
'VimResized', -- after Vim window was resized
+ 'VimResume', -- after Nvim is resumed
+ 'VimSuspend', -- before Nvim is suspended
'WinNew', -- when entering a new window
'WinEnter', -- after entering a window
'WinLeave', -- before leaving a window
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 2e32af2e9a..4e6ca8d278 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -237,15 +237,16 @@ void channel_create_event(Channel *chan, const char *ext_source)
#endif
}
-void channel_incref(Channel *channel)
+void channel_incref(Channel *chan)
{
- channel->refcount++;
+ chan->refcount++;
}
-void channel_decref(Channel *channel)
+void channel_decref(Channel *chan)
{
- if (!(--channel->refcount)) {
- multiqueue_put(main_loop.fast_events, free_channel_event, 1, channel);
+ if (!(--chan->refcount)) {
+ // delay free, so that libuv is done with the handles
+ multiqueue_put(main_loop.events, free_channel_event, 1, chan);
}
}
@@ -267,18 +268,18 @@ void callback_reader_start(CallbackReader *reader)
static void free_channel_event(void **argv)
{
- Channel *channel = argv[0];
- if (channel->is_rpc) {
- rpc_free(channel);
+ Channel *chan = argv[0];
+ if (chan->is_rpc) {
+ rpc_free(chan);
}
- callback_reader_free(&channel->on_stdout);
- callback_reader_free(&channel->on_stderr);
- callback_free(&channel->on_exit);
+ callback_reader_free(&chan->on_stdout);
+ callback_reader_free(&chan->on_stderr);
+ callback_free(&chan->on_exit);
- pmap_del(uint64_t)(channels, channel->id);
- multiqueue_free(channel->events);
- xfree(channel);
+ pmap_del(uint64_t)(channels, chan->id);
+ multiqueue_free(chan->events);
+ xfree(chan);
}
static void channel_destroy_early(Channel *chan)
@@ -286,12 +287,15 @@ static void channel_destroy_early(Channel *chan)
if ((chan->id != --next_chan_id)) {
abort();
}
+ pmap_del(uint64_t)(channels, chan->id);
+ chan->id = 0;
if ((--chan->refcount != 0)) {
abort();
}
- free_channel_event((void **)&chan);
+ // uv will keep a reference to handles until next loop tick, so delay free
+ multiqueue_put(main_loop.events, free_channel_event, 1, chan);
}
@@ -654,9 +658,9 @@ static void channel_process_exit_cb(Process *proc, int status, void *data)
terminal_close(chan->term, msg);
}
- // if status is -1 the process did not really exit,
- // we just closed the handle onto a detached process
- if (status >= 0) {
+ // If process did not exit, we only closed the handle of a detached process.
+ bool exited = (status >= 0);
+ if (exited) {
process_channel_event(chan, &chan->on_exit, "exit", NULL, 0, status);
}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index f9c3e5f0d5..a1987cf2d5 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -847,7 +847,7 @@ static int insert_handle_key(InsertState *s)
case ' ':
- if (mod_mask != 4) {
+ if (mod_mask != MOD_MASK_CTRL) {
goto normalchar;
}
// FALLTHROUGH
@@ -1180,6 +1180,14 @@ static int insert_handle_key(InsertState *s)
normalchar:
// Insert a normal character.
+
+ if (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META) {
+ // Unmapped ALT/META chord behaves like ESC+c. #8213
+ stuffcharReadbuff(ESC);
+ stuffcharReadbuff(s->c);
+ break;
+ }
+
if (!p_paste) {
// Trigger InsertCharPre.
char_u *str = do_insert_char_pre(s->c);
@@ -1432,7 +1440,7 @@ static void ins_ctrl_v(void)
* line and will not removed by the redraw */
edit_unputchar();
clear_showcmd();
- insert_special(c, FALSE, TRUE);
+ insert_special(c, true, true);
revins_chars++;
revins_legal++;
}
@@ -3615,6 +3623,9 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
memset(cptext, 0, sizeof(cptext));
}
if (word == NULL || (!aempty && *word == NUL)) {
+ for (size_t i = 0; i < CPT_COUNT; i++) {
+ xfree(cptext[i]);
+ }
return FAIL;
}
return ins_compl_add((char_u *)word, -1, icase, NULL,
@@ -5054,13 +5065,11 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
char_u *p;
int len;
- /*
- * Special function key, translate into "<Key>". Up to the last '>' is
- * inserted with ins_str(), so as not to replace characters in replace
- * mode.
- * Only use mod_mask for special keys, to avoid things like <S-Space>,
- * unless 'allow_modmask' is TRUE.
- */
+ // Special function key, translate into "<Key>". Up to the last '>' is
+ // inserted with ins_str(), so as not to replace characters in replace
+ // mode.
+ // Only use mod_mask for special keys, to avoid things like <S-Space>,
+ // unless 'allow_modmask' is TRUE.
if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key.
allow_modmask = true;
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index e38f7e7dda..cc29496968 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -14394,8 +14394,11 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
if (argvars[0].vval.v_string) {
- server_stop((char *) argvars[0].vval.v_string);
+ bool rv = server_stop((char *)argvars[0].vval.v_string);
+ rettv->vval.v_number = (rv ? 1 : 0);
}
}
@@ -15694,6 +15697,56 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
p_cpo = save_cpo;
}
+/// "stdpath()" helper for list results
+static void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const void *iter = NULL;
+ list_T *const list = tv_list_alloc(kListLenShouldKnow);
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = list;
+ tv_list_ref(list);
+ char *const dirs = stdpaths_get_xdg_var(xdg);
+ do {
+ size_t dir_len;
+ const char *dir;
+ iter = vim_env_iter(':', dirs, iter, &dir, &dir_len);
+ if (dir != NULL && dir_len > 0) {
+ char *dir_with_nvim = xmemdupz(dir, dir_len);
+ dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true);
+ tv_list_append_string(list, dir_with_nvim, strlen(dir_with_nvim));
+ xfree(dir_with_nvim);
+ }
+ } while (iter != NULL);
+ xfree(dirs);
+}
+
+/// "stdpath(type)" function
+static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ const char *const p = tv_get_string_chk(&argvars[0]);
+ if (p == NULL) {
+ return; // Type error; errmsg already given.
+ }
+
+ if (strcmp(p, "config") == 0) {
+ rettv->vval.v_string = (char_u *)get_xdg_home(kXDGConfigHome);
+ } else if (strcmp(p, "data") == 0) {
+ rettv->vval.v_string = (char_u *)get_xdg_home(kXDGDataHome);
+ } else if (strcmp(p, "cache") == 0) {
+ rettv->vval.v_string = (char_u *)get_xdg_home(kXDGCacheHome);
+ } else if (strcmp(p, "config_dirs") == 0) {
+ get_xdg_var_list(kXDGConfigDirs, rettv);
+ } else if (strcmp(p, "data_dirs") == 0) {
+ get_xdg_var_list(kXDGDataDirs, rettv);
+ } else {
+ EMSG2(_("E6100: \"%s\" is not a valid stdpath"), p);
+ }
+}
+
/*
* "str2float()" function
*/
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index b798eae187..149dae688e 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -10,6 +10,7 @@
#include "nvim/event/rstream.h"
#include "nvim/event/wstream.h"
#include "nvim/channel.h"
+#include "nvim/os/stdpaths_defs.h"
#define COPYID_INC 2
#define COPYID_MASK (~0x1)
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index daa3b637a3..801d2cc468 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -280,6 +280,7 @@ return {
spellsuggest={args={1, 3}},
split={args={1, 3}},
sqrt={args=1, func="float_op_wrapper", data="&sqrt"},
+ stdpath={args=1},
str2float={args=1},
str2nr={args={1, 2}},
strcharpart={args={2, 3}},
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c
index 0c2bf397e5..ffe2db9b76 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -51,12 +51,19 @@ int libuv_process_spawn(LibuvProcess *uvproc)
if (!proc->in.closed) {
uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+#ifdef WIN32
+ uvproc->uvstdio[0].flags |= UV_OVERLAPPED_PIPE;
+#endif
uvproc->uvstdio[0].data.stream = STRUCT_CAST(uv_stream_t,
&proc->in.uv.pipe);
}
if (!proc->out.closed) {
uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+#ifdef WIN32
+ // pipe must be readable for IOCP to work.
+ uvproc->uvstdio[1].flags |= UV_READABLE_PIPE | UV_OVERLAPPED_PIPE;
+#endif
uvproc->uvstdio[1].data.stream = STRUCT_CAST(uv_stream_t,
&proc->out.uv.pipe);
}
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index d92464f17b..7998e0b8d0 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -21,7 +21,6 @@ void loop_init(Loop *loop, void *data)
loop->recursive = 0;
loop->uv.data = loop;
loop->children = kl_init(WatcherPtr);
- loop->children_stop_requests = 0;
loop->events = multiqueue_new_parent(loop_on_put, loop);
loop->fast_events = multiqueue_new_child(loop->events);
loop->thread_events = multiqueue_new_parent(NULL, NULL);
diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h
index 1b288fc4cb..f5dd23ac8b 100644
--- a/src/nvim/event/loop.h
+++ b/src/nvim/event/loop.h
@@ -37,7 +37,6 @@ typedef struct loop {
// generic timer, used by loop_poll_events()
uv_timer_t poll_timer;
- size_t children_stop_requests;
uv_async_t async;
uv_mutex_t mutex;
int recursive;
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index fa31024a64..7a8a39dbcf 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -23,7 +23,7 @@
#endif
// Time for a process to exit cleanly before we send KILL.
-// For pty processes SIGTERM is sent first (in case SIGHUP was not enough).
+// For PTY processes SIGTERM is sent first (in case SIGHUP was not enough).
#define KILL_TIMEOUT_MS 2000
static bool process_is_tearing_down = false;
@@ -111,6 +111,7 @@ int process_spawn(Process *proc, bool in, bool out, bool err)
proc->internal_close_cb = decref;
proc->refcount++;
kl_push(WatcherPtr, proc->loop->children, proc);
+ DLOG("new: pid=%d argv=[%s]", proc->pid, *proc->argv);
return 0;
}
@@ -188,8 +189,7 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
}
if (proc->refcount == 1) {
- // Job exited, collect status and manually invoke close_cb to free the job
- // resources
+ // Job exited, free its resources.
decref(proc);
if (events) {
// the decref call created an exit event, process it now
@@ -205,11 +205,12 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
/// Ask a process to terminate and eventually kill if it doesn't respond
void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
{
- if (proc->stopped_time) {
+ bool exited = (proc->status >= 0);
+ if (exited || proc->stopped_time) {
return;
}
-
proc->stopped_time = os_hrtime();
+
switch (proc->type) {
case kProcessTypeUv:
// Close the process's stdin. If the process doesn't close its own
@@ -227,35 +228,32 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
abort();
}
- Loop *loop = proc->loop;
- if (!loop->children_stop_requests++) {
- // When there's at least one stop request pending, start a timer that
- // will periodically check if a signal should be send to the job.
- ILOG("starting job kill timer");
- uv_timer_start(&loop->children_kill_timer, children_kill_cb,
- KILL_TIMEOUT_MS, KILL_TIMEOUT_MS);
- }
+ // (Re)start timer to verify that stopped process(es) died.
+ uv_timer_start(&proc->loop->children_kill_timer, children_kill_cb,
+ KILL_TIMEOUT_MS, 0);
}
-/// Iterates the process list sending SIGTERM to stopped processes and SIGKILL
-/// to those that didn't die from SIGTERM after a while(exit_timeout is 0).
+/// Sends SIGKILL (or SIGTERM..SIGKILL for PTY jobs) to processes that did
+/// not terminate after process_stop().
static void children_kill_cb(uv_timer_t *handle)
{
Loop *loop = handle->loop->data;
- uint64_t now = os_hrtime();
kl_iter(WatcherPtr, loop->children, current) {
Process *proc = (*current)->data;
- if (!proc->stopped_time) {
+ bool exited = (proc->status >= 0);
+ if (exited || !proc->stopped_time) {
continue;
}
- uint64_t elapsed = (now - proc->stopped_time) / 1000000 + 1;
-
- if (elapsed >= KILL_TIMEOUT_MS) {
- int sig = proc->type == kProcessTypePty && elapsed < KILL_TIMEOUT_MS * 2
- ? SIGTERM
- : SIGKILL;
- os_proc_tree_kill(proc->pid, sig);
+ uint64_t term_sent = UINT64_MAX == proc->stopped_time;
+ if (kProcessTypePty != proc->type || term_sent) {
+ os_proc_tree_kill(proc->pid, SIGKILL);
+ } else {
+ os_proc_tree_kill(proc->pid, SIGTERM);
+ proc->stopped_time = UINT64_MAX; // Flag: SIGTERM was sent.
+ // Restart timer.
+ uv_timer_start(&proc->loop->children_kill_timer, children_kill_cb,
+ KILL_TIMEOUT_MS, 0);
}
}
}
@@ -267,7 +265,7 @@ static void process_close_event(void **argv)
if (proc->type == kProcessTypePty) {
xfree(((PtyProcess *)proc)->term_name);
}
- if (proc->cb) {
+ if (proc->cb) { // "on_exit" for jobstart(). See channel_job_start().
proc->cb(proc, proc->status, proc->data);
}
}
@@ -383,12 +381,8 @@ static void process_close_handles(void **argv)
static void on_process_exit(Process *proc)
{
Loop *loop = proc->loop;
- if (proc->stopped_time && loop->children_stop_requests
- && !--loop->children_stop_requests) {
- // Stop the timer if no more stop requests are pending
- DLOG("Stopping process kill timer");
- uv_timer_stop(&loop->children_kill_timer);
- }
+ ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status,
+ proc->stopped_time);
// Process has terminated, but there could still be data to be read from the
// OS. We are still in the libuv loop, so we cannot call code that polls for
diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h
index 033ce3604b..ba2c2a6a11 100644
--- a/src/nvim/event/process.h
+++ b/src/nvim/event/process.h
@@ -19,8 +19,7 @@ struct process {
Loop *loop;
void *data;
int pid, status, refcount;
- // set to the hrtime of when process_stop was called for the process.
- uint64_t stopped_time;
+ uint64_t stopped_time; // process_stop() timestamp
const char *cwd;
char **argv;
Stream in, out, err;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 33e2805538..3dbeccaaa5 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -603,6 +603,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
cmd_getline, cmd_cookie);
recursive--;
+ // Ignore trailing '|'-separated commands in preview-mode ('inccommand').
+ if (State & CMDPREVIEW) {
+ next_cmdline = NULL;
+ }
+
if (cmd_cookie == (void *)&cmd_loop_cookie)
/* Use "current_line" from "cmd_loop_cookie", it may have been
* incremented when defining a function. */
@@ -6300,15 +6305,18 @@ static void ex_stop(exarg_T *eap)
if (!eap->forceit) {
autowrite_all();
}
+ apply_autocmds(EVENT_VIMSUSPEND, NULL, NULL, false, NULL);
ui_cursor_goto((int)Rows - 1, 0);
ui_linefeed();
ui_flush();
ui_call_suspend(); // call machine specific function
+
ui_flush();
maketitle();
resettitle(); // force updating the title
redraw_later_clear();
ui_refresh(); // may have resized window
+ apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL);
}
}
@@ -6544,18 +6552,14 @@ void alist_slash_adjust(void)
#endif
-/*
- * ":preserve".
- */
+/// ":preserve".
static void ex_preserve(exarg_T *eap)
{
curbuf->b_flags |= BF_PRESERVED;
- ml_preserve(curbuf, TRUE);
+ ml_preserve(curbuf, true, true);
}
-/*
- * ":recover".
- */
+/// ":recover".
static void ex_recover(exarg_T *eap)
{
/* Set recoverymode right away to avoid the ATTENTION prompt. */
@@ -6975,12 +6979,10 @@ do_exedit (
ex_no_reprint = TRUE;
}
-/*
- * ":gui" and ":gvim" when there is no GUI.
- */
+/// ":gui" and ":gvim" when there is no GUI.
static void ex_nogui(exarg_T *eap)
{
- eap->errmsg = e_nogvim;
+ eap->errmsg = (char_u *)N_("E25: Nvim does not have a built-in GUI");
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index c34b52cf27..efeee1ba2b 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1,9 +1,7 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * fileio.c: read from and write to a file
- */
+// fileio.c: read from and write to a file
#include <assert.h>
#include <errno.h>
@@ -65,57 +63,62 @@
#define BUFSIZE 8192 /* size of normal write buffer */
#define SMBUFSIZE 256 /* size of emergency write buffer */
-/*
- * The autocommands are stored in a list for each event.
- * Autocommands for the same pattern, that are consecutive, are joined
- * together, to avoid having to match the pattern too often.
- * The result is an array of Autopat lists, which point to AutoCmd lists:
- *
- * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
- * Autopat.cmds Autopat.cmds
- * | |
- * V V
- * AutoCmd.next AutoCmd.next
- * | |
- * V V
- * AutoCmd.next NULL
- * |
- * V
- * NULL
- *
- * first_autopat[1] --> Autopat.next --> NULL
- * Autopat.cmds
- * |
- * V
- * AutoCmd.next
- * |
- * V
- * NULL
- * etc.
- *
- * The order of AutoCmds is important, this is the order in which they were
- * defined and will have to be executed.
- */
+//
+// The autocommands are stored in a list for each event.
+// Autocommands for the same pattern, that are consecutive, are joined
+// together, to avoid having to match the pattern too often.
+// The result is an array of Autopat lists, which point to AutoCmd lists:
+//
+// last_autopat[0] -----------------------------+
+// V
+// first_autopat[0] --> Autopat.next --> Autopat.next --> NULL
+// Autopat.cmds Autopat.cmds
+// | |
+// V V
+// AutoCmd.next AutoCmd.next
+// | |
+// V V
+// AutoCmd.next NULL
+// |
+// V
+// NULL
+//
+// last_autopat[1] --------+
+// V
+// first_autopat[1] --> Autopat.next --> NULL
+// Autopat.cmds
+// |
+// V
+// AutoCmd.next
+// |
+// V
+// NULL
+// etc.
+//
+// The order of AutoCmds is important, this is the order in which they were
+// defined and will have to be executed.
+//
typedef struct AutoCmd {
- char_u *cmd; /* The command to be executed (NULL
- when command has been removed) */
- char nested; /* If autocommands nest here */
- char last; /* last command in list */
- scid_T scriptID; /* script ID where defined */
- struct AutoCmd *next; /* Next AutoCmd in list */
+ char_u *cmd; // The command to be executed (NULL
+ // when command has been removed)
+ char nested; // If autocommands nest here
+ char last; // last command in list
+ scid_T scriptID; // script ID where defined
+ struct AutoCmd *next; // Next AutoCmd in list
} AutoCmd;
typedef struct AutoPat {
- char_u *pat; /* pattern as typed (NULL when pattern
- has been removed) */
- regprog_T *reg_prog; /* compiled regprog for pattern */
- AutoCmd *cmds; /* list of commands to do */
- struct AutoPat *next; /* next AutoPat in AutoPat list */
- int group; /* group ID */
- int patlen; /* strlen() of pat */
- int buflocal_nr; /* !=0 for buffer-local AutoPat */
- char allow_dirs; /* Pattern may match whole path */
- char last; /* last pattern for apply_autocmds() */
+ struct AutoPat *next; // next AutoPat in AutoPat list; MUST
+ // be the first entry
+ char_u *pat; // pattern as typed (NULL when pattern
+ // has been removed)
+ regprog_T *reg_prog; // compiled regprog for pattern
+ AutoCmd *cmds; // list of commands to do
+ int group; // group ID
+ int patlen; // strlen() of pat
+ int buflocal_nr; // !=0 for buffer-local AutoPat
+ char allow_dirs; // Pattern may match whole path
+ char last; // last pattern for apply_autocmds()
} AutoPat;
/*
@@ -226,6 +229,15 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
msg_scrolled_ign = FALSE;
}
+static AutoPat *last_autopat[NUM_EVENTS] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
/*
* Read lines from file "fname" into the buffer after line "from".
*
@@ -1724,9 +1736,17 @@ failed:
xfree(buffer);
if (read_stdin) {
- /* Use stderr for stdin, makes shell commands work. */
close(0);
+#ifndef WIN32
+ // On Unix, use stderr for stdin, makes shell commands work.
ignored = dup(2);
+#else
+ // On Windows, use the console input handle for stdin.
+ HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
+ OPEN_EXISTING, 0, (HANDLE)NULL);
+ ignored = _open_osfhandle(conin, _O_RDONLY);
+#endif
}
if (tmpname != NULL) {
@@ -3043,7 +3063,7 @@ nobackup:
*/
if (reset_changed && !newfile && overwriting
&& !(exiting && backup != NULL)) {
- ml_preserve(buf, FALSE);
+ ml_preserve(buf, false, !!p_fs);
if (got_int) {
SET_ERRMSG(_(e_interr));
goto restore_backup;
@@ -4435,7 +4455,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
/// @param size size of the buffer
/// @param fp file to read from
///
-/// @return true for end-of-file.
+/// @return true for EOF or error
bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
{
char *retval;
@@ -4446,7 +4466,7 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
do {
errno = 0;
retval = fgets((char *)buf, size, fp);
- } while (retval == NULL && errno == EINTR);
+ } while (retval == NULL && errno == EINTR && ferror(fp));
if (buf[size - 2] != NUL && buf[size - 2] != '\n') {
char tbuf[200];
@@ -4458,12 +4478,12 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
tbuf[sizeof(tbuf) - 2] = NUL;
errno = 0;
retval = fgets((char *)tbuf, sizeof(tbuf), fp);
- if (retval == NULL && errno != EINTR) {
+ if (retval == NULL && (feof(fp) || errno != EINTR)) {
break;
}
} while (tbuf[sizeof(tbuf) - 2] != NUL && tbuf[sizeof(tbuf) - 2] != '\n');
}
- return retval ? false : feof(fp);
+ return retval == NULL;
}
/// Read 2 bytes from "fd" and turn them into an int, MSB first.
@@ -5523,6 +5543,15 @@ static void au_cleanup(void)
/* remove the pattern if it has been marked for deletion */
if (ap->pat == NULL) {
+ if (ap->next == NULL) {
+ if (prev_ap == &(first_autopat[(int)event])) {
+ last_autopat[(int)event] = NULL;
+ } else {
+ // this depends on the "next" field being the first in
+ // the struct
+ last_autopat[(int)event] = (AutoPat *)prev_ap;
+ }
+ }
*prev_ap = ap->next;
vim_regfree(ap->reg_prog);
xfree(ap);
@@ -6115,10 +6144,13 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd,
patlen = (int)STRLEN(buflocal_pat); /* but not endpat */
}
- /*
- * Find AutoPat entries with this pattern.
- */
- prev_ap = &first_autopat[(int)event];
+ // Find AutoPat entries with this pattern. When adding a command it
+ // always goes at or after the last one, so start at the end.
+ if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL) {
+ prev_ap = &last_autopat[(int)event];
+ } else {
+ prev_ap = &first_autopat[(int)event];
+ }
while ((ap = *prev_ap) != NULL) {
if (ap->pat != NULL) {
/* Accept a pattern when:
@@ -6204,6 +6236,7 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd,
}
ap->cmds = NULL;
*prev_ap = ap;
+ last_autopat[(int)event] = ap;
ap->next = NULL;
if (group == AUGROUP_ALL)
ap->group = current_augroup;
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 5d4e61d56a..e2566c8c66 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -48,8 +48,14 @@
#include "nvim/event/loop.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
+#include "nvim/os/fileio.h"
#include "nvim/api/private/handle.h"
+
+/// Index in scriptin
+static int curscript = 0;
+FileDescriptor *scriptin[NSCRIPT] = { NULL };
+
/*
* These buffers are used for storing:
* - stuffed characters: A command that is translated into another command.
@@ -1243,10 +1249,13 @@ openscript (
++curscript;
/* use NameBuff for expanded name */
expand_env(name, NameBuff, MAXPATHL);
- if ((scriptin[curscript] = mch_fopen((char *)NameBuff, READBIN)) == NULL) {
- EMSG2(_(e_notopen), name);
- if (curscript)
- --curscript;
+ int error;
+ if ((scriptin[curscript] = file_open_new(&error, (char *)NameBuff,
+ kFileReadOnly, 0)) == NULL) {
+ emsgf(_(e_notopen_2), name, os_strerror(error));
+ if (curscript) {
+ curscript--;
+ }
return;
}
save_typebuf();
@@ -1296,7 +1305,7 @@ static void closescript(void)
free_typebuf();
typebuf = saved_typebuf[curscript];
- fclose(scriptin[curscript]);
+ file_free(scriptin[curscript], false);
scriptin[curscript] = NULL;
if (curscript > 0)
--curscript;
@@ -1319,10 +1328,8 @@ int using_script(void)
return scriptin[curscript] != NULL;
}
-/*
- * This function is called just before doing a blocking wait. Thus after
- * waiting 'updatetime' for a character to arrive.
- */
+/// This function is called just before doing a blocking wait. Thus after
+/// waiting 'updatetime' for a character to arrive.
void before_blocking(void)
{
updatescript(0);
@@ -1331,21 +1338,22 @@ void before_blocking(void)
}
}
-/*
- * updatescipt() is called when a character can be written into the script file
- * or when we have waited some time for a character (c == 0)
- *
- * All the changed memfiles are synced if c == 0 or when the number of typed
- * characters reaches 'updatecount' and 'updatecount' is non-zero.
- */
-void updatescript(int c)
+/// updatescript() is called when a character can be written to the script file
+/// or when we have waited some time for a character (c == 0).
+///
+/// All the changed memfiles are synced if c == 0 or when the number of typed
+/// characters reaches 'updatecount' and 'updatecount' is non-zero.
+static void updatescript(int c)
{
static int count = 0;
- if (c && scriptout)
+ if (c && scriptout) {
putc(c, scriptout);
- if (c == 0 || (p_uc > 0 && ++count >= p_uc)) {
- ml_sync_all(c == 0, TRUE);
+ }
+ bool idle = (c == 0);
+ if (idle || (p_uc > 0 && ++count >= p_uc)) {
+ ml_sync_all(idle, true,
+ (!!p_fs || idle)); // Always fsync at idle (CursorHold).
count = 0;
}
}
@@ -1577,7 +1585,7 @@ vungetc ( /* unget one character (can only be done once!) */
old_mouse_col = mouse_col;
}
-/// get a character:
+/// Gets a character:
/// 1. from the stuffbuffer
/// This is used for abbreviated commands like "D" -> "d$".
/// Also used to redo a command for ".".
@@ -1595,7 +1603,7 @@ vungetc ( /* unget one character (can only be done once!) */
/// if "advance" is FALSE (vpeekc()):
/// just look whether there is a character available.
///
-/// When "no_mapping" is zero, checks for mappings in the current mode.
+/// When `no_mapping` (global) is zero, checks for mappings in the current mode.
/// Only returns one byte (of a multi-byte character).
/// K_SPECIAL and CSI may be escaped, need to get two more bytes then.
static int vgetorpeek(int advance)
@@ -2336,9 +2344,8 @@ inchar (
int tb_change_cnt
)
{
- int len = 0; /* init for GCC */
- int retesc = FALSE; /* return ESC with gotint */
- int script_char;
+ int len = 0; // Init for GCC.
+ int retesc = false; // Return ESC with gotint.
if (wait_time == -1L || wait_time > 100L) {
// flush output before waiting
@@ -2356,45 +2363,38 @@ inchar (
}
undo_off = FALSE; /* restart undo now */
- /*
- * Get a character from a script file if there is one.
- * If interrupted: Stop reading script files, close them all.
- */
- script_char = -1;
- while (scriptin[curscript] != NULL && script_char < 0
- && !ignore_script
- ) {
-
-
- if (got_int || (script_char = getc(scriptin[curscript])) < 0) {
- /* Reached EOF.
- * Careful: closescript() frees typebuf.tb_buf[] and buf[] may
- * point inside typebuf.tb_buf[]. Don't use buf[] after this! */
+ // Get a character from a script file if there is one.
+ // If interrupted: Stop reading script files, close them all.
+ ptrdiff_t read_size = -1;
+ while (scriptin[curscript] != NULL && read_size <= 0 && !ignore_script) {
+ char script_char;
+ if (got_int
+ || (read_size = file_read(scriptin[curscript], &script_char, 1)) != 1) {
+ // Reached EOF or some error occurred.
+ // Careful: closescript() frees typebuf.tb_buf[] and buf[] may
+ // point inside typebuf.tb_buf[]. Don't use buf[] after this!
closescript();
- /*
- * When reading script file is interrupted, return an ESC to get
- * back to normal mode.
- * Otherwise return -1, because typebuf.tb_buf[] has changed.
- */
- if (got_int)
- retesc = TRUE;
- else
+ // When reading script file is interrupted, return an ESC to get
+ // back to normal mode.
+ // Otherwise return -1, because typebuf.tb_buf[] has changed.
+ if (got_int) {
+ retesc = true;
+ } else {
return -1;
+ }
} else {
buf[0] = (char_u)script_char;
len = 1;
}
}
- if (script_char < 0) { /* did not get a character from script */
- /*
- * If we got an interrupt, skip all previously typed characters and
- * return TRUE if quit reading script file.
- * Stop reading typeahead when a single CTRL-C was read,
- * fill_input_buf() returns this when not able to read from stdin.
- * Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
- * and buf may be pointing inside typebuf.tb_buf[].
- */
+ if (read_size <= 0) { // Did not get a character from script.
+ // If we got an interrupt, skip all previously typed characters and
+ // return TRUE if quit reading script file.
+ // Stop reading typeahead when a single CTRL-C was read,
+ // fill_input_buf() returns this when not able to read from stdin.
+ // Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
+ // and buf may be pointing inside typebuf.tb_buf[].
if (got_int) {
#define DUM_LEN MAXMAPLEN * 3 + 3
char_u dum[DUM_LEN + 1];
@@ -2407,21 +2407,18 @@ inchar (
return retesc;
}
- /*
- * Always flush the output characters when getting input characters
- * from the user.
- */
+ // Always flush the output characters when getting input characters
+ // from the user.
ui_flush();
- /*
- * Fill up to a third of the buffer, because each character may be
- * tripled below.
- */
+ // Fill up to a third of the buffer, because each character may be
+ // tripled below.
len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt);
}
- if (typebuf_changed(tb_change_cnt))
+ if (typebuf_changed(tb_change_cnt)) {
return 0;
+ }
return fix_input_buffer(buf, len);
}
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index e634273e0d..38a2e75663 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -1,24 +1,30 @@
#ifndef NVIM_GETCHAR_H
#define NVIM_GETCHAR_H
+#include "nvim/os/fileio.h"
#include "nvim/types.h"
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
-/// Values for "noremap" argument of ins_typebuf(). Also used for
-/// map->m_noremap and menu->noremap[].
-/// @addtogroup REMAP_VALUES
-/// @{
-#define REMAP_YES 0 ///< allow remapping
-#define REMAP_NONE -1 ///< no remapping
-#define REMAP_SCRIPT -2 ///< remap script-local mappings only
-#define REMAP_SKIP -3 ///< no remapping for first char
-/// @}
+/// Values for "noremap" argument of ins_typebuf()
+///
+/// Also used for map->m_noremap and menu->noremap[].
+enum {
+ REMAP_YES = 0, ///< Allow remapping.
+ REMAP_NONE = -1, ///< No remapping.
+ REMAP_SCRIPT = -2, ///< Remap script-local mappings only.
+ REMAP_SKIP = -3, ///< No remapping for first char.
+} RemapValues;
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
#define KEYLEN_REMOVED 9999 /* keylen value for removed sequence */
+/// Maximum number of streams to read script from
+enum { NSCRIPT = 15 };
+
+/// Streams to read script from
+extern FileDescriptor *scriptin[NSCRIPT];
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "getchar.h.generated.h"
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 4542b7f133..e02100e933 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -78,6 +78,11 @@ typedef enum {
kTrue = 1,
} TriState;
+EXTERN struct nvim_stats_s {
+ int64_t fsync;
+ int64_t redraw;
+} g_stats INIT(= { 0, 0 });
+
/* Values for "starting" */
#define NO_SCREEN 2 /* no screen updating yet */
#define NO_BUFFERS 1 /* not all buffers loaded yet */
@@ -828,10 +833,7 @@ EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */
EXTERN int need_highlight_changed INIT(= true);
EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use
-#define NSCRIPT 15
-EXTERN FILE *scriptin[NSCRIPT]; /* streams to read script from */
-EXTERN int curscript INIT(= 0); /* index in scriptin[] */
-EXTERN FILE *scriptout INIT(= NULL); /* stream to write script to */
+EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to.
// volatile because it is used in a signal handler.
EXTERN volatile int got_int INIT(= false); // set to true when interrupt
@@ -1064,7 +1066,6 @@ EXTERN char_u e_nesting[] INIT(= N_("E22: Scripts nested too deep"));
EXTERN char_u e_noalt[] INIT(= N_("E23: No alternate file"));
EXTERN char_u e_noabbr[] INIT(= N_("E24: No such abbreviation"));
EXTERN char_u e_nobang[] INIT(= N_("E477: No ! allowed"));
-EXTERN char_u e_nogvim[] INIT(= N_("E25: Nvim does not have a built-in GUI"));
EXTERN char_u e_nogroup[] INIT(= N_("E28: No such highlight group name: %s"));
EXTERN char_u e_noinstext[] INIT(= N_("E29: No inserted text yet"));
EXTERN char_u e_nolastcmd[] INIT(= N_("E30: No previous command line"));
@@ -1080,6 +1081,7 @@ EXTERN char_u e_norange[] INIT(= N_("E481: No range allowed"));
EXTERN char_u e_noroom[] INIT(= N_("E36: Not enough room"));
EXTERN char_u e_notmp[] INIT(= N_("E483: Can't get temp file name"));
EXTERN char_u e_notopen[] INIT(= N_("E484: Can't open file %s"));
+EXTERN char_u e_notopen_2[] INIT(= N_("E484: Can't open file %s: %s"));
EXTERN char_u e_notread[] INIT(= N_("E485: Can't read file %s"));
EXTERN char_u e_nowrtmsg[] INIT(= N_(
"E37: No write since last change (add ! to override)"));
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index 00e9cf6ed3..c64691e8ea 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -443,7 +443,7 @@ enum key_extra {
#define MOD_MASK_2CLICK 0x20 // use MOD_MASK_MULTI_CLICK
#define MOD_MASK_3CLICK 0x40 // use MOD_MASK_MULTI_CLICK
#define MOD_MASK_4CLICK 0x60 // use MOD_MASK_MULTI_CLICK
-#define MOD_MASK_CMD 0x80 // "super" key (OSX/Mac: command-key)
+#define MOD_MASK_CMD 0x80 // "super" key (macOS: command-key)
#define MOD_MASK_MULTI_CLICK (MOD_MASK_2CLICK|MOD_MASK_3CLICK| \
MOD_MASK_4CLICK)
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 4e01265498..348df2d9b6 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -136,13 +136,21 @@
# define RESET_BINDING(wp) (wp)->w_p_scb = FALSE; (wp)->w_p_crb = FALSE
-/// Calculate the length of a C array.
+/// Calculate the length of a C array
///
/// This should be called with a real array. Calling this with a pointer is an
-/// error. A mechanism to detect many (though not all) of those errors at compile
-/// time is implemented. It works by the second division producing a division by
-/// zero in those cases (-Wdiv-by-zero in GCC).
-#define ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0])))))
+/// error. A mechanism to detect many (though not all) of those errors at
+/// compile time is implemented. It works by the second division producing
+/// a division by zero in those cases (-Wdiv-by-zero in GCC).
+#define ARRAY_SIZE(arr) \
+ ((sizeof(arr)/sizeof((arr)[0])) \
+ / ((size_t)(!(sizeof(arr) % sizeof((arr)[0])))))
+
+/// Get last array entry
+///
+/// This should be called with a real array. Calling this with a pointer is an
+/// error.
+#define ARRAY_LAST_ENTRY(arr) (arr)[ARRAY_SIZE(arr) - 1]
// Duplicated in os/win_defs.h to avoid include-order sensitivity.
#define RGB_(r, g, b) ((r << 16) | (g << 8) | b)
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 4288d7f9d7..a4ed868af1 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -72,30 +72,30 @@
# include "nvim/os/pty_process_unix.h"
#endif
-/* Maximum number of commands from + or -c arguments. */
+// Maximum number of commands from + or -c arguments.
#define MAX_ARG_CMDS 10
-/* values for "window_layout" */
-#define WIN_HOR 1 /* "-o" horizontally split windows */
-#define WIN_VER 2 /* "-O" vertically split windows */
-#define WIN_TABS 3 /* "-p" windows on tab pages */
+// values for "window_layout"
+#define WIN_HOR 1 // "-o" horizontally split windows
+#define WIN_VER 2 // "-O" vertically split windows
+#define WIN_TABS 3 // "-p" windows on tab pages
-/* Struct for various parameters passed between main() and other functions. */
+// Struct for various parameters passed between main() and other functions.
typedef struct {
int argc;
char **argv;
char *use_vimrc; // vimrc from -u argument
- int n_commands; /* no. of commands from + or -c */
+ int n_commands; // no. of commands from + or -c
char *commands[MAX_ARG_CMDS]; // commands from + or -c arg
- char_u cmds_tofree[MAX_ARG_CMDS]; /* commands that need free() */
- int n_pre_commands; /* no. of commands from --cmd */
+ char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free()
+ int n_pre_commands; // no. of commands from --cmd
char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument
- int edit_type; /* type of editing to do */
- char_u *tagname; /* tag from -t argument */
- char_u *use_ef; /* 'errorfile' from -q argument */
+ int edit_type; // type of editing to do
+ char_u *tagname; // tag from -t argument
+ char_u *use_ef; // 'errorfile' from -q argument
int want_full_screen;
bool input_isatty; // stdin is a terminal
@@ -103,13 +103,15 @@ typedef struct {
bool err_isatty; // stderr is a terminal
int no_swap_file; // "-n" argument used
int use_debug_break_level;
- int window_count; /* number of windows to use */
- int window_layout; /* 0, WIN_HOR, WIN_VER or WIN_TABS */
+ int window_count; // number of windows to use
+ int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS
#if !defined(UNIX)
- int literal; /* don't expand file names */
+ int literal; // don't expand file names
#endif
- int diff_mode; /* start with 'diff' set */
+ int diff_mode; // start with 'diff' set
+
+ char *listen_addr; // --listen {address}
} mparm_T;
/* Values for edit_type. */
@@ -150,7 +152,6 @@ void event_init(void)
signal_init();
// finish mspgack-rpc initialization
channel_init();
- server_init();
terminal_init();
}
@@ -241,9 +242,8 @@ int main(int argc, char **argv)
char_u *cwd = NULL; // current workding dir on startup
time_init();
- /* Many variables are in "params" so that we can pass them to invoked
- * functions without a lot of arguments. "argc" and "argv" are also
- * copied, so that they can be changed. */
+ // Many variables are in `params` so that we can pass them around easily.
+ // `argc` and `argv` are also copied, so that they can be changed.
init_params(&params, argc, argv);
init_startuptime(&params);
@@ -254,11 +254,10 @@ int main(int argc, char **argv)
check_and_set_isatty(&params);
event_init();
- /*
- * Process the command line arguments. File names are put in the global
- * argument list "global_alist".
- */
+ // Process the command line arguments. File names are put in the global
+ // argument list "global_alist".
command_line_scan(&params);
+ server_init(params.listen_addr);
if (GARGCOUNT > 0) {
fname = get_fname(&params, cwd);
@@ -726,9 +725,7 @@ static void init_locale(void)
#endif
-/*
- * Scan the command line arguments.
- */
+/// Scan the command line arguments.
static void command_line_scan(mparm_T *parmp)
{
int argc = parmp->argc;
@@ -790,7 +787,8 @@ static void command_line_scan(mparm_T *parmp)
mch_exit(0);
} else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
FileDescriptor fp;
- const int fof_ret = file_open_fd(&fp, STDOUT_FILENO, true);
+ const int fof_ret = file_open_fd(&fp, STDOUT_FILENO,
+ kFileWriteOnly);
msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write);
if (fof_ret != 0) {
@@ -819,6 +817,9 @@ static void command_line_scan(mparm_T *parmp)
if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
abort();
}
+ } else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) {
+ want_argument = true;
+ argv_idx += 6;
} else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
#if !defined(UNIX)
parmp->literal = TRUE;
@@ -864,10 +865,6 @@ static void command_line_scan(mparm_T *parmp)
case 'f': /* "-f" GUI: run in foreground. */
break;
- case 'g': /* "-g" start GUI */
- main_start_gui();
- break;
-
case 'F': { // "-F" start in Farsi mode: rl + fkmap set.
p_fkmap = true;
set_option_value("rl", 1L, NULL, 0);
@@ -898,26 +895,17 @@ static void command_line_scan(mparm_T *parmp)
p_write = FALSE;
break;
- case 'N': /* "-N" Nocompatible */
- /* No-op */
+ case 'N': // "-N" Nocompatible
+ case 'X': // "-X" Do not connect to X server
+ // No-op
break;
case 'n': /* "-n" no swap file */
parmp->no_swap_file = TRUE;
break;
- case 'p': /* "-p[N]" open N tab pages */
-#ifdef TARGET_API_MAC_OSX
- /* For some reason on MacOS X, an argument like:
- -psn_0_10223617 is passed in when invoke from Finder
- or with the 'open' command */
- if (argv[0][argv_idx] == 's') {
- argv_idx = -1; /* bypass full -psn */
- main_start_gui();
- break;
- }
-#endif
- /* default is 0: open window for each file */
+ case 'p': // "-p[N]" open N tab pages
+ // default is 0: open window for each file
parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
parmp->window_layout = WIN_TABS;
break;
@@ -1030,15 +1018,12 @@ static void command_line_scan(mparm_T *parmp)
mainerr(err_opt_unknown, argv[0]);
}
- /*
- * Handle option arguments with argument.
- */
+ // Handle option arguments with argument.
if (want_argument) {
- /*
- * Check for garbage immediately after the option letter.
- */
- if (argv[0][argv_idx] != NUL)
+ // Check for garbage immediately after the option letter.
+ if (argv[0][argv_idx] != NUL) {
mainerr(err_opt_garbage, argv[0]);
+ }
--argc;
if (argc < 1 && c != 'S') /* -S has an optional argument */
@@ -1077,13 +1062,17 @@ static void command_line_scan(mparm_T *parmp)
break;
case '-':
- if (argv[-1][2] == 'c') {
- /* "--cmd {command}" execute command */
- if (parmp->n_pre_commands >= MAX_ARG_CMDS)
+ if (strequal(argv[-1], "--cmd")) {
+ // "--cmd {command}" execute command
+ if (parmp->n_pre_commands >= MAX_ARG_CMDS) {
mainerr(err_extra_cmd, NULL);
+ }
parmp->pre_commands[parmp->n_pre_commands++] = argv[0];
+ } else if (strequal(argv[-1], "--listen")) {
+ // "--listen {address}"
+ parmp->listen_addr = argv[0];
}
- /* "--startuptime <file>" already handled */
+ // "--startuptime <file>" already handled
break;
case 'q': /* "-q {errorfile}" QuickFix mode */
@@ -1097,17 +1086,36 @@ static void command_line_scan(mparm_T *parmp)
case 's': /* "-s {scriptin}" read from script file */
if (scriptin[0] != NULL) {
scripterror:
- mch_errmsg(_("Attempt to open script file again: \""));
- mch_errmsg(argv[-1]);
- mch_errmsg(" ");
- mch_errmsg(argv[0]);
- mch_errmsg("\"\n");
+ vim_snprintf((char *)IObuff, IOSIZE,
+ _("Attempt to open script file again: \"%s %s\"\n"),
+ argv[-1], argv[0]);
+ mch_errmsg((const char *)IObuff);
mch_exit(2);
}
- if ((scriptin[0] = mch_fopen(argv[0], READBIN)) == NULL) {
- mch_errmsg(_("Cannot open for reading: \""));
- mch_errmsg(argv[0]);
- mch_errmsg("\"\n");
+ int error;
+ if (STRCMP(argv[0], "-") == 0) {
+ const int stdin_dup_fd = os_dup(STDIN_FILENO);
+#ifdef WIN32
+ // On Windows, replace the original stdin with the
+ // console input handle.
+ close(STDIN_FILENO);
+ const HANDLE conin_handle =
+ CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
+ OPEN_EXISTING, 0, (HANDLE)NULL);
+ const int conin_fd = _open_osfhandle(conin_handle, _O_RDONLY);
+ assert(conin_fd == STDIN_FILENO);
+#endif
+ FileDescriptor *const stdin_dup = file_open_fd_new(
+ &error, stdin_dup_fd, kFileReadOnly|kFileNonBlocking);
+ assert(stdin_dup != NULL);
+ scriptin[0] = stdin_dup;
+ } else if ((scriptin[0] = file_open_new(
+ &error, argv[0], kFileReadOnly|kFileNonBlocking, 0)) == NULL) {
+ vim_snprintf((char *)IObuff, IOSIZE,
+ _("Cannot open for reading: \"%s\": %s\n"),
+ argv[0], os_strerror(error));
+ mch_errmsg((const char *)IObuff);
mch_exit(2);
}
save_typebuf();
@@ -1224,11 +1232,10 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
paramp->want_full_screen = true;
paramp->use_debug_break_level = -1;
paramp->window_count = -1;
+ paramp->listen_addr = NULL;
}
-/*
- * Initialize global startuptime file if "--startuptime" passed as an argument.
- */
+/// Initialize global startuptime file if "--startuptime" passed as an argument.
static void init_startuptime(mparm_T *paramp)
{
for (int i = 1; i < paramp->argc; i++) {
@@ -1834,17 +1841,6 @@ static void source_startup_scripts(const mparm_T *const parmp)
TIME_MSG("sourcing vimrc file(s)");
}
-/*
- * Setup to start using the GUI. Exit with an error when not available.
- */
-static void main_start_gui(void)
-{
- mch_errmsg(_(e_nogvim));
- mch_errmsg("\n");
- mch_exit(2);
-}
-
-
/// Get an environment variable, and execute it as Ex commands.
///
/// @param env environment variable to execute
@@ -1968,6 +1964,7 @@ static void usage(void)
mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
+ mch_msg(_(" --listen <address> Start RPC server at this address\n"));
#if !defined(UNIX)
mch_msg(_(" --literal Don't expand wildcards\n"));
#endif
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index d4708f61ff..1f388dd34c 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1588,7 +1588,7 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot)
* If 'check_char' is TRUE, stop syncing when character becomes available, but
* always sync at least one block.
*/
-void ml_sync_all(int check_file, int check_char)
+void ml_sync_all(int check_file, int check_char, bool do_fsync)
{
FOR_ALL_BUFFERS(buf) {
if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
@@ -1607,16 +1607,17 @@ void ml_sync_all(int check_file, int check_char)
if (!os_fileinfo((char *)buf->b_ffname, &file_info)
|| file_info.stat.st_mtim.tv_sec != buf->b_mtime_read
|| os_fileinfo_size(&file_info) != buf->b_orig_size) {
- ml_preserve(buf, FALSE);
- did_check_timestamps = FALSE;
- need_check_timestamps = TRUE; /* give message later */
+ ml_preserve(buf, false, do_fsync);
+ did_check_timestamps = false;
+ need_check_timestamps = true; // give message later
}
}
if (buf->b_ml.ml_mfp->mf_dirty) {
(void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
- | (bufIsChanged(buf) ? MFS_FLUSH : 0));
- if (check_char && os_char_avail()) /* character available now */
+ | (do_fsync && bufIsChanged(buf) ? MFS_FLUSH : 0));
+ if (check_char && os_char_avail()) { // character available now
break;
+ }
}
}
}
@@ -1631,7 +1632,7 @@ void ml_sync_all(int check_file, int check_char)
*
* when message is TRUE the success of preserving is reported
*/
-void ml_preserve(buf_T *buf, int message)
+void ml_preserve(buf_T *buf, int message, bool do_fsync)
{
bhdr_T *hp;
linenr_T lnum;
@@ -1649,9 +1650,9 @@ void ml_preserve(buf_T *buf, int message)
* before. */
got_int = FALSE;
- ml_flush_line(buf); /* flush buffered line */
- (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */
- status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
+ ml_flush_line(buf); // flush buffered line
+ (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
+ status = mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0));
/* stack is invalid after mf_sync(.., MFS_ALL) */
buf->b_ml.ml_stack_top = 0;
@@ -1679,11 +1680,12 @@ void ml_preserve(buf_T *buf, int message)
CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
lnum = buf->b_ml.ml_locked_high + 1;
}
- (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */
- /* sync the updated pointer blocks */
- if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
+ (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
+ // sync the updated pointer blocks
+ if (mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0)) == FAIL) {
status = FAIL;
- buf->b_ml.ml_stack_top = 0; /* stack is invalid now */
+ }
+ buf->b_ml.ml_stack_top = 0; // stack is invalid now
}
theend:
got_int |= got_int_save;
diff --git a/src/nvim/message.c b/src/nvim/message.c
index abe21193d6..7ca82c2878 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2348,10 +2348,9 @@ static int do_more_prompt(int typed_char)
* yet. When stderr can't be used, collect error messages until the GUI has
* started and they can be displayed in a message box.
*/
-void mch_errmsg(char *str)
+void mch_errmsg(const char *const str)
+ FUNC_ATTR_NONNULL_ALL
{
- int len;
-
#ifdef UNIX
/* On Unix use stderr if it's a tty.
* When not going to start the GUI also use stderr.
@@ -2365,14 +2364,13 @@ void mch_errmsg(char *str)
/* avoid a delay for a message that isn't there */
emsg_on_display = FALSE;
- len = (int)STRLEN(str) + 1;
+ const size_t len = strlen(str) + 1;
if (error_ga.ga_data == NULL) {
ga_set_growsize(&error_ga, 80);
error_ga.ga_itemsize = 1;
}
ga_grow(&error_ga, len);
- memmove((char_u *)error_ga.ga_data + error_ga.ga_len,
- (char_u *)str, len);
+ memmove(error_ga.ga_data + error_ga.ga_len, str, len);
#ifdef UNIX
/* remove CR characters, they are displayed */
{
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index b0232b6516..28455f0ba9 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -2643,7 +2643,7 @@ void preserve_exit(void)
if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) {
mch_errmsg((uint8_t *)"Vim: preserving files...\n");
ui_flush();
- ml_sync_all(false, false); // preserve all swap files
+ ml_sync_all(false, false, true); // preserve all swap files
break;
}
}
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 9bf122f4db..e5d80aea1d 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -32,26 +32,27 @@ static garray_T watchers = GA_EMPTY_INIT_VALUE;
#endif
/// Initializes the module
-bool server_init(void)
+bool server_init(const char *listen_addr)
{
ga_init(&watchers, sizeof(SocketWatcher *), 1);
- bool must_free = false;
- const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR);
- if (listen_address == NULL) {
- must_free = true;
- listen_address = server_address_new();
- }
+ // $NVIM_LISTEN_ADDRESS
+ const char *env_addr = os_getenv(LISTEN_ADDRESS_ENV_VAR);
+ int rv = listen_addr == NULL ? 1 : server_start(listen_addr);
- if (!listen_address) {
- return false;
+ if (0 != rv) {
+ rv = env_addr == NULL ? 1 : server_start(env_addr);
+ if (0 != rv) {
+ listen_addr = server_address_new();
+ if (listen_addr == NULL) {
+ return false;
+ }
+ rv = server_start(listen_addr);
+ xfree((char *)listen_addr);
+ }
}
- bool ok = (server_start(listen_address) == 0);
- if (must_free) {
- xfree((char *) listen_address);
- }
- return ok;
+ return rv == 0;
}
/// Teardown a single server
@@ -120,8 +121,8 @@ bool server_owns_pipe_address(const char *path)
/// @param endpoint Address of the server. Either a 'ip:[port]' string or an
/// arbitrary identifier (trimmed to 256 bytes) for the Unix
/// socket or named pipe.
-/// @returns 0 on success, 1 on a regular error, and negative errno
-/// on failure to bind or listen.
+/// @returns 0: success, 1: validation error, 2: already listening,
+/// -errno: failed to bind or listen.
int server_start(const char *endpoint)
{
if (endpoint == NULL || endpoint[0] == '\0') {
@@ -145,7 +146,7 @@ int server_start(const char *endpoint)
uv_freeaddrinfo(watcher->uv.tcp.addrinfo);
}
socket_watcher_close(watcher, free_server);
- return 1;
+ return 2;
}
}
@@ -177,7 +178,7 @@ int server_start(const char *endpoint)
/// Stops listening on the address specified by `endpoint`.
///
/// @param endpoint Address of the server.
-void server_stop(char *endpoint)
+bool server_stop(char *endpoint)
{
SocketWatcher *watcher;
bool watcher_found = false;
@@ -196,8 +197,8 @@ void server_stop(char *endpoint)
}
if (!watcher_found) {
- ELOG("Not listening on %s", addr);
- return;
+ WLOG("Not listening on %s", addr);
+ return false;
}
// Unset $NVIM_LISTEN_ADDRESS if it is the stopped address.
@@ -219,6 +220,8 @@ void server_stop(char *endpoint)
if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) {
set_vservername(&watchers);
}
+
+ return true;
}
/// Returns an allocated array of server addresses.
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 80484d0ad2..66018b2475 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -976,7 +976,7 @@ return {
secure=true,
vi_def=true,
varname='p_fs',
- defaults={if_true={vi=true}}
+ defaults={if_true={vi=false}}
},
{
full_name='gdefault', abbreviation='gd',
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index a95adc86b6..ccf35fd57c 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -83,7 +83,7 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
if (fd < 0) {
return fd;
}
- return file_open_fd(ret_fp, fd, (wr == kTrue));
+ return file_open_fd(ret_fp, fd, flags);
}
/// Wrap file descriptor with FileDescriptor structure
@@ -94,14 +94,23 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
/// @param[out] ret_fp Address where information needed for reading from or
/// writing to a file is saved
/// @param[in] fd File descriptor to wrap.
-/// @param[in] wr True if fd is opened for writing only, false if it is read
-/// only.
+/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
+/// writing to the file at once is not supported, so either
+/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
///
/// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
-int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr)
+int file_open_fd(FileDescriptor *const ret_fp, const int fd,
+ const int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- ret_fp->wr = wr;
+ ret_fp->wr = !!(flags & (kFileCreate
+ |kFileCreateOnly
+ |kFileTruncate
+ |kFileAppend
+ |kFileWriteOnly));
+ ret_fp->non_blocking = !!(flags & kFileNonBlocking);
+ // Non-blocking writes not supported currently.
+ assert(!ret_fp->wr || !ret_fp->non_blocking);
ret_fp->fd = fd;
ret_fp->eof = false;
ret_fp->rv = rbuffer_new(kRWBufferSize);
@@ -138,15 +147,17 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
///
/// @param[out] error Error code, or 0 on success. @see os_strerror()
/// @param[in] fd File descriptor to wrap.
-/// @param[in] wr True if fd is opened for writing only, false if it is read
-/// only.
+/// @param[in] flags Flags, @see FileOpenFlags.
+/// @param[in] mode Permissions for the newly created file (ignored if flags
+/// does not have FILE_CREATE\*).
///
/// @return [allocated] Opened file or NULL in case of error.
-FileDescriptor *file_open_fd_new(int *const error, const int fd, const bool wr)
+FileDescriptor *file_open_fd_new(int *const error, const int fd,
+ const int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
FileDescriptor *const fp = xmalloc(sizeof(*fp));
- if ((*error = file_open_fd(fp, fd, wr)) != 0) {
+ if ((*error = file_open_fd(fp, fd, flags)) != 0) {
xfree(fp);
return NULL;
}
@@ -244,7 +255,8 @@ static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp)
return;
}
const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize);
- const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes);
+ const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes,
+ fp->non_blocking);
if (wres != (ptrdiff_t)read_bytes) {
if (wres >= 0) {
fp->_error = UV_EIO;
@@ -270,6 +282,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
char *buf = ret_buf;
size_t read_remaining = size;
RBuffer *const rv = fp->rv;
+ bool called_read = false;
while (read_remaining) {
const size_t rv_size = rbuffer_size(rv);
if (rv_size > 0) {
@@ -277,7 +290,9 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
buf += rsize;
read_remaining -= rsize;
}
- if (fp->eof) {
+ if (fp->eof
+ // Allow only at most one os_read[v] call.
+ || (called_read && fp->non_blocking)) {
break;
}
if (read_remaining) {
@@ -294,7 +309,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
};
assert(write_count == kRWBufferSize);
const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov,
- ARRAY_SIZE(iov));
+ ARRAY_SIZE(iov), fp->non_blocking);
if (r_ret > 0) {
if (r_ret > (ptrdiff_t)read_remaining) {
rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining));
@@ -310,7 +325,8 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
if (read_remaining >= kRWBufferSize) {
// …otherwise leave RBuffer empty and populate only target buffer,
// because filtering information through rbuffer will be more syscalls.
- const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining);
+ const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining,
+ fp->non_blocking);
if (r_ret >= 0) {
read_remaining -= (size_t)r_ret;
return (ptrdiff_t)(size - read_remaining);
@@ -321,7 +337,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
size_t write_count;
const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof,
rbuffer_write_ptr(rv, &write_count),
- kRWBufferSize);
+ kRWBufferSize, fp->non_blocking);
assert(write_count == kRWBufferSize);
if (r_ret > 0) {
rbuffer_produced(rv, (size_t)r_ret);
@@ -330,6 +346,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
}
}
#endif
+ called_read = true;
}
}
return (ptrdiff_t)(size - read_remaining);
diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h
index 0b55cc695f..7c53cd4f07 100644
--- a/src/nvim/os/fileio.h
+++ b/src/nvim/os/fileio.h
@@ -14,6 +14,7 @@ typedef struct {
RBuffer *rv; ///< Read or write buffer.
bool wr; ///< True if file is in write mode.
bool eof; ///< True if end of file was encountered.
+ bool non_blocking; ///< True if EAGAIN should not restart syscalls.
} FileDescriptor;
/// file_open() flags
@@ -32,6 +33,8 @@ typedef enum {
///< kFileCreateOnly.
kFileAppend = 64, ///< Append to the file. Implies kFileWriteOnly. Cannot
///< be used with kFileCreateOnly.
+ kFileNonBlocking = 128, ///< Do not restart read() or write() syscall if
+ ///< EAGAIN was encountered.
} FileOpenFlags;
static inline bool file_eof(const FileDescriptor *const fp)
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 924b3d2359..5412c5daae 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -436,6 +436,29 @@ int os_close(const int fd)
return r;
}
+/// Duplicate file descriptor
+///
+/// @param[in] fd File descriptor to duplicate.
+///
+/// @return New file descriptor or libuv error code (< 0).
+int os_dup(const int fd)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ int ret;
+os_dup_dup:
+ ret = dup(fd);
+ if (ret < 0) {
+ const int error = os_translate_sys_error(errno);
+ errno = 0;
+ if (error == UV_EINTR) {
+ goto os_dup_dup;
+ } else {
+ return error;
+ }
+ }
+ return ret;
+}
+
/// Read from a file
///
/// Handles EINTR and ENOMEM, but not other errors.
@@ -445,10 +468,11 @@ int os_close(const int fd)
/// to false. Initial value is ignored.
/// @param[out] ret_buf Buffer to write to. May be NULL if size is zero.
/// @param[in] size Amount of bytes to read.
+/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered.
///
/// @return Number of bytes read or libuv error code (< 0).
-ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
- const size_t size)
+ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf,
+ const size_t size, const bool non_blocking)
FUNC_ATTR_WARN_UNUSED_RESULT
{
*ret_eof = false;
@@ -468,7 +492,9 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
if (cur_read_bytes < 0) {
const int error = os_translate_sys_error(errno);
errno = 0;
- if (error == UV_EINTR || error == UV_EAGAIN) {
+ if (non_blocking && error == UV_EAGAIN) {
+ break;
+ } else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
} else if (error == UV_ENOMEM && !did_try_to_free) {
try_to_free_memory();
@@ -498,7 +524,11 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
/// may change, it is incorrect to use data it points to after
/// os_readv().
/// @param[in] iov_size Number of buffers in iov.
-ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size)
+/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered.
+///
+/// @return Number of bytes read or libuv error code (< 0).
+ptrdiff_t os_readv(const int fd, bool *const ret_eof, struct iovec *iov,
+ size_t iov_size, const bool non_blocking)
FUNC_ATTR_NONNULL_ALL
{
*ret_eof = false;
@@ -531,7 +561,9 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size)
} else if (cur_read_bytes < 0) {
const int error = os_translate_sys_error(errno);
errno = 0;
- if (error == UV_EINTR || error == UV_EAGAIN) {
+ if (non_blocking && error == UV_EAGAIN) {
+ break;
+ } else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
} else if (error == UV_ENOMEM && !did_try_to_free) {
try_to_free_memory();
@@ -551,9 +583,11 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size)
/// @param[in] fd File descriptor to write to.
/// @param[in] buf Data to write. May be NULL if size is zero.
/// @param[in] size Amount of bytes to write.
+/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered.
///
/// @return Number of bytes written or libuv error code (< 0).
-ptrdiff_t os_write(const int fd, const char *const buf, const size_t size)
+ptrdiff_t os_write(const int fd, const char *const buf, const size_t size,
+ const bool non_blocking)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (buf == NULL) {
@@ -571,7 +605,9 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size)
if (cur_written_bytes < 0) {
const int error = os_translate_sys_error(errno);
errno = 0;
- if (error == UV_EINTR || error == UV_EAGAIN) {
+ if (non_blocking && error == UV_EAGAIN) {
+ break;
+ } else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
} else {
return error;
@@ -593,6 +629,7 @@ int os_fsync(int fd)
{
int r;
RUN_UV_FS_FUNC(r, uv_fs_fsync, fd, NULL);
+ g_stats.fsync++;
return r;
}
diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c
index 63eea9022b..a67e7487eb 100644
--- a/src/nvim/os/process.c
+++ b/src/nvim/os/process.c
@@ -18,7 +18,7 @@
# include <sys/user.h>
#endif
-#if defined(__NetBSD__)
+#if defined(__NetBSD__) || defined(__OpenBSD__)
# include <sys/param.h>
#endif
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index f650a51fe7..04f59d7522 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -136,7 +136,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
xfree(input.data);
if (output) {
- (void)write_output(output, nread, true, true);
+ (void)write_output(output, nread, true);
xfree(output);
}
@@ -388,10 +388,10 @@ static bool out_data_decide_throttle(size_t size)
pulse_msg[1] = (tick == 0 || 1 == tick) ? ' ' : '.';
pulse_msg[2] = (tick == 0 || 1 == tick || 2 == tick) ? ' ' : '.';
if (visit == 1) {
- screen_del_lines(0, 0, 1, (int)Rows, NULL);
+ msg_putchar('\n');
}
- int lastrow = (int)Rows - 1;
- screen_puts_len((char_u *)pulse_msg, ARRAY_SIZE(pulse_msg), lastrow, 0, 0);
+ msg_putchar('\r'); // put cursor at start of line
+ msg_puts(pulse_msg);
ui_flush();
return true;
}
@@ -609,28 +609,20 @@ static void read_input(DynamicBuffer *buf)
}
}
-static size_t write_output(char *output, size_t remaining, bool to_buffer,
- bool eof)
+static size_t write_output(char *output, size_t remaining, bool eof)
{
if (!output) {
return 0;
}
- char replacement_NUL = to_buffer ? NL : 1;
char *start = output;
size_t off = 0;
- int lastrow = (int)Rows - 1;
while (off < remaining) {
if (output[off] == NL) {
// Insert the line
- if (to_buffer) {
- output[off] = NUL;
- ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1,
- false);
- } else {
- screen_del_lines(0, 0, 1, (int)Rows, NULL);
- screen_puts_len((char_u *)output, (int)off, lastrow, 0, 0);
- }
+ output[off] = NUL;
+ ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1,
+ false);
size_t skip = off + 1;
output += skip;
remaining -= skip;
@@ -640,24 +632,19 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer,
if (output[off] == NUL) {
// Translate NUL to NL
- output[off] = replacement_NUL;
+ output[off] = NL;
}
off++;
}
if (eof) {
if (remaining) {
- if (to_buffer) {
- // append unfinished line
- ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false);
- // remember that the NL was missing
- curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
- } else {
- screen_del_lines(0, 0, 1, (int)Rows, NULL);
- screen_puts_len((char_u *)output, (int)remaining, lastrow, 0, 0);
- }
+ // append unfinished line
+ ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false);
+ // remember that the NL was missing
+ curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
output += remaining;
- } else if (to_buffer) {
+ } else {
curbuf->b_no_eol_lnum = 0;
}
}
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index 732be072e1..fc7f9cefd1 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -145,7 +145,7 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
case SIGPWR:
// Signal of a power failure(eg batteries low), flush the swap files to
// be safe
- ml_sync_all(false, false);
+ ml_sync_all(false, false, true);
break;
#endif
#ifdef SIGPIPE
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index a41fb7c621..866a005228 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -88,7 +88,7 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
///
/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to
/// avoid storing configuration and data files in the same path.
-static char *get_xdg_home(const XDGVarType idx)
+char *get_xdg_home(const XDGVarType idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
char *dir = stdpaths_get_xdg_var(idx);
diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt
index d01eff6384..94cc63baea 100644
--- a/src/nvim/po/CMakeLists.txt
+++ b/src/nvim/po/CMakeLists.txt
@@ -139,17 +139,17 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
endmacro()
# Create some translations from others.
- if("ja" IN_LIST LANGUAGES)
+ if(";${LANGUAGES};" MATCHES ";ja;")
BuildPoIconv(ja utf-8 euc-jp)
BuildMo(ja.euc-jp)
endif()
- if("cs" IN_LIST LANGUAGES)
+ if(";${LANGUAGES};" MATCHES ";cs;")
BuildPoIconv(cs ISO-8859-2 cp1250)
BuildMo(cs.cp1250)
endif()
- if("sk" IN_LIST LANGUAGES)
+ if(";${LANGUAGES};" MATCHES ";sk;")
BuildPoIconv(sk ISO-8859-2 cp1250)
BuildMo(sk.cp1250)
endif()
@@ -159,7 +159,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
${CMAKE_CURRENT_SOURCE_DIR}/no.po ${CMAKE_CURRENT_SOURCE_DIR}/nb.po
DEPENDS no.po)
list(APPEND UPDATE_PO_TARGETS update-po-nb)
- if("nb" IN_LIST LANGUAGES)
+ if(";${LANGUAGES};" MATCHES ";no;")
CheckPo(nb)
BuildMo(nb)
endif()
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index fb7a9ecf5f..1c3cb5d6b2 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3029,7 +3029,7 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
/*
* Return TRUE if "buf" is the quickfix buffer.
*/
-int bt_quickfix(buf_T *buf)
+int bt_quickfix(const buf_T *const buf)
{
return buf != NULL && buf->b_p_bt[0] == 'q';
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 6dbba30cf2..2e64eb864f 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2040,7 +2040,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
}
screen_line(row + wp->w_winrow, wp->w_wincol, wp->w_width,
- wp->w_width, false, wp);
+ wp->w_width, false, wp, 0);
/*
* Update w_cline_height and w_cline_folded if the cursor line was
@@ -2460,10 +2460,6 @@ win_line (
line_attr = win_hl_attr(wp, HLF_QFL);
}
- if (wp->w_hl_attr_normal != 0) {
- line_attr = hl_combine_attr(wp->w_hl_attr_normal, line_attr);
- }
-
if (line_attr != 0) {
area_highlighting = true;
}
@@ -2920,7 +2916,8 @@ win_line (
&& lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol
&& filler_todo <= 0
) {
- screen_line(screen_row, wp->w_wincol, col, -wp->w_width, wp->w_p_rl, wp);
+ screen_line(screen_row, wp->w_wincol, col, -wp->w_width, wp->w_p_rl, wp,
+ wp->w_hl_attr_normal);
// Pretend we have finished updating the window. Except when
// 'cursorcolumn' is set.
if (wp->w_p_cuc) {
@@ -4012,7 +4009,8 @@ win_line (
col++;
}
}
- screen_line(screen_row, wp->w_wincol, col, wp->w_width, wp->w_p_rl, wp);
+ screen_line(screen_row, wp->w_wincol, col, wp->w_width, wp->w_p_rl, wp,
+ wp->w_hl_attr_normal);
row++;
/*
@@ -4230,7 +4228,7 @@ win_line (
|| (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
) {
screen_line(screen_row, wp->w_wincol, col - boguscols,
- wp->w_width, wp->w_p_rl, wp);
+ wp->w_width, wp->w_p_rl, wp, wp->w_hl_attr_normal);
boguscols = 0;
++row;
++screen_row;
@@ -4391,7 +4389,7 @@ static int char_needs_redraw(int off_from, int off_to, int cols)
* When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
*/
static void screen_line(int row, int coloff, int endcol,
- int clear_width, int rlflag, win_T *wp)
+ int clear_width, int rlflag, win_T *wp, int bg_attr)
{
unsigned off_from;
unsigned off_to;
@@ -4424,15 +4422,16 @@ static void screen_line(int row, int coloff, int endcol,
/* Clear rest first, because it's left of the text. */
if (clear_width > 0) {
while (col <= endcol && ScreenLines[off_to] == ' '
- && ScreenAttrs[off_to] == 0
+ && ScreenAttrs[off_to] == bg_attr
&& (!enc_utf8 || ScreenLinesUC[off_to] == 0)
) {
++off_to;
++col;
}
- if (col <= endcol)
- screen_fill(row, row + 1, col + coloff,
- endcol + coloff + 1, ' ', ' ', 0);
+ if (col <= endcol) {
+ screen_fill(row, row + 1, col + coloff, endcol + coloff + 1, ' ', ' ',
+ bg_attr);
+ }
}
col = endcol + 1;
off_to = LineOffset[row] + col + coloff;
@@ -4440,6 +4439,13 @@ static void screen_line(int row, int coloff, int endcol,
endcol = (clear_width > 0 ? clear_width : -clear_width);
}
+ if (bg_attr) {
+ for (int c = col; c < endcol; c++) {
+ ScreenAttrs[off_from+c] = hl_combine_attr(bg_attr,
+ ScreenAttrs[off_from+c]);
+ }
+ }
+
redraw_next = char_needs_redraw(off_from, off_to, endcol - col);
while (col < endcol) {
@@ -4535,15 +4541,15 @@ static void screen_line(int row, int coloff, int endcol,
/* blank out the rest of the line */
while (col < clear_width && ScreenLines[off_to] == ' '
- && ScreenAttrs[off_to] == 0
+ && ScreenAttrs[off_to] == bg_attr
&& (!enc_utf8 || ScreenLinesUC[off_to] == 0)
) {
++off_to;
++col;
}
if (col < clear_width) {
- screen_fill(row, row + 1, col + coloff, clear_width + coloff,
- ' ', ' ', 0);
+ screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ',
+ bg_attr);
off_to += clear_width - col;
col = clear_width;
}
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 605d9c30a6..f4454504a4 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -16,12 +16,14 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/vim.h"
+#include "nvim/pos.h"
#include "nvim/ascii.h"
#include "nvim/shada.h"
#include "nvim/message.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/mark.h"
+#include "nvim/macros.h"
#include "nvim/ops.h"
#include "nvim/garray.h"
#include "nvim/option.h"
@@ -375,7 +377,8 @@ KHASH_MAP_INIT_STR(file_marks, FileMarks)
/// Before actually writing most of the data is read to this structure.
typedef struct {
HistoryMergerState hms[HIST_COUNT]; ///< Structures for history merging.
- PossiblyFreedShadaEntry global_marks[NGLOBALMARKS]; ///< All global marks.
+ PossiblyFreedShadaEntry global_marks[NMARKS]; ///< Named global marks.
+ PossiblyFreedShadaEntry numbered_marks[EXTRA_MARKS]; ///< Numbered marks.
PossiblyFreedShadaEntry registers[NUM_SAVED_REGISTERS]; ///< All registers.
PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; ///< All dumped jumps.
size_t jumps_size; ///< Number of jumps occupied.
@@ -808,7 +811,7 @@ static int open_shada_file_for_reading(const char *const fname,
/// Wrapper for closing file descriptors
static void close_file(void *cookie)
{
- const int error = file_free(cookie, true);
+ const int error = file_free(cookie, !!p_fs);
if (error != 0) {
emsgf(_(SERR "System error while closing ShaDa file: %s"),
os_strerror(error));
@@ -2020,6 +2023,113 @@ shada_parse_msgpack_extra_bytes:
return ret;
}
+/// Format shada entry for debugging purposes
+///
+/// @param[in] entry ShaDa entry to format.
+///
+/// @return string representing ShaDa entry in a static buffer.
+static const char *shada_format_entry(const ShadaEntry entry)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_UNUSED FUNC_ATTR_NONNULL_RET
+{
+ static char ret[1024];
+ ret[0] = 0;
+ vim_snprintf(S_LEN(ret), "[ ] ts=%" PRIu64 " ");
+ // ^ Space for `can_free_entry`
+ switch (entry.type) {
+ case kSDItemMissing: {
+ vim_snprintf_add(S_LEN(ret), "Missing");
+ break;
+ }
+ case kSDItemHeader: {
+ vim_snprintf_add(S_LEN(ret), "Header { TODO }");
+ break;
+ }
+ case kSDItemBufferList: {
+ vim_snprintf_add(S_LEN(ret), "BufferList { TODO }");
+ break;
+ }
+ case kSDItemUnknown: {
+ vim_snprintf_add(S_LEN(ret), "Unknown { TODO }");
+ break;
+ }
+ case kSDItemSearchPattern: {
+ vim_snprintf_add(S_LEN(ret), "SearchPattern { TODO }");
+ break;
+ }
+ case kSDItemSubString: {
+ vim_snprintf_add(S_LEN(ret), "SubString { TODO }");
+ break;
+ }
+ case kSDItemHistoryEntry: {
+ vim_snprintf_add(S_LEN(ret), "HistoryEntry { TODO }");
+ break;
+ }
+ case kSDItemRegister: {
+ vim_snprintf_add(S_LEN(ret), "Register { TODO }");
+ break;
+ }
+ case kSDItemVariable: {
+ vim_snprintf_add(S_LEN(ret), "Variable { TODO }");
+ break;
+ }
+#define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \
+ do { \
+ typval_T ad_tv = { \
+ .v_type = VAR_DICT, \
+ .vval.v_dict = entry.data.filemark.additional_data \
+ }; \
+ size_t ad_len; \
+ char *const ad = encode_tv2string(&ad_tv, &ad_len); \
+ vim_snprintf_add( \
+ S_LEN(ret), \
+ entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \
+ "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \
+ "ad={%p:[%zu]%.64s} }", \
+ name_fmt_arg, \
+ strlen(entry.data.filemark.fname), \
+ entry.data.filemark.fname, \
+ entry.data.filemark.mark.lnum, \
+ entry.data.filemark.mark.col, \
+ entry.data.filemark.mark.coladd, \
+ entry.data.filemark.additional_data, \
+ ad_len, \
+ ad); \
+ } while (0)
+ case kSDItemGlobalMark: {
+ FORMAT_MARK_ENTRY("GlobalMark", " name='%c',", entry.data.filemark.name);
+ break;
+ }
+ case kSDItemChange: {
+ FORMAT_MARK_ENTRY("Change", "%s", "");
+ break;
+ }
+ case kSDItemLocalMark: {
+ FORMAT_MARK_ENTRY("LocalMark", " name='%c',", entry.data.filemark.name);
+ break;
+ }
+ case kSDItemJump: {
+ FORMAT_MARK_ENTRY("Jump", "%s", "");
+ break;
+ }
+#undef FORMAT_MARK_ENTRY
+ }
+ return ret;
+}
+
+/// Format possibly freed shada entry for debugging purposes
+///
+/// @param[in] entry ShaDa entry to format.
+///
+/// @return string representing ShaDa entry in a static buffer.
+static const char *shada_format_pfreed_entry(
+ const PossiblyFreedShadaEntry pfs_entry)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_UNUSED FUNC_ATTR_NONNULL_RET
+{
+ char *ret = (char *)shada_format_entry(pfs_entry.data);
+ ret[1] = (pfs_entry.can_free_entry ? 'T' : 'F');
+ return ret;
+}
+
/// Read and merge in ShaDa file, used when writing
///
/// @param[in] sd_reader Structure containing file reader definition.
@@ -2071,9 +2181,12 @@ static inline ShaDaWriteResult shada_read_when_writing(
shada_free_shada_entry(&wms_entry->data); \
} \
} \
- wms_entry->can_free_entry = true; \
- wms_entry->data = (entry); \
+ *wms_entry = pfs_entry; \
} while (0)
+ const PossiblyFreedShadaEntry pfs_entry = {
+ .can_free_entry = true,
+ .data = entry,
+ };
switch (entry.type) {
case kSDItemMissing: {
break;
@@ -2125,13 +2238,49 @@ static inline ShaDaWriteResult shada_read_when_writing(
break;
}
case kSDItemGlobalMark: {
- const int idx = mark_global_index(entry.data.filemark.name);
- if (idx < 0) {
- ret = shada_pack_entry(packer, entry, 0);
- shada_free_shada_entry(&entry);
- break;
+ if (ascii_isdigit(entry.data.filemark.name)) {
+ bool processed_mark = false;
+ // Completely ignore numbered mark names, make a list sorted by
+ // timestamp.
+ for (size_t i = ARRAY_SIZE(wms->numbered_marks); i > 0; i--) {
+ ShadaEntry wms_entry = wms->numbered_marks[i - 1].data;
+ if (wms_entry.type != kSDItemGlobalMark) {
+ continue;
+ }
+ // Ignore duplicates.
+ if (wms_entry.timestamp == entry.timestamp
+ && (wms_entry.data.filemark.additional_data == NULL
+ && entry.data.filemark.additional_data == NULL)
+ && marks_equal(wms_entry.data.filemark.mark,
+ entry.data.filemark.mark)
+ && strcmp(wms_entry.data.filemark.fname,
+ entry.data.filemark.fname) == 0) {
+ shada_free_shada_entry(&entry);
+ processed_mark = true;
+ break;
+ }
+ if (wms_entry.timestamp >= entry.timestamp) {
+ processed_mark = true;
+ if (i < ARRAY_SIZE(wms->numbered_marks)) {
+ replace_numbered_mark(wms, i, pfs_entry);
+ } else {
+ shada_free_shada_entry(&entry);
+ }
+ break;
+ }
+ }
+ if (!processed_mark) {
+ replace_numbered_mark(wms, 0, pfs_entry);
+ }
+ } else {
+ const int idx = mark_global_index(entry.data.filemark.name);
+ if (idx < 0) {
+ ret = shada_pack_entry(packer, entry, 0);
+ shada_free_shada_entry(&entry);
+ break;
+ }
+ COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry);
}
- COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry);
break;
}
case kSDItemChange:
@@ -2175,8 +2324,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
shada_free_shada_entry(&wms_entry->data);
}
}
- wms_entry->can_free_entry = true;
- wms_entry->data = entry;
+ *wms_entry = pfs_entry;
}
} else {
#define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \
@@ -2216,6 +2364,20 @@ static inline ShaDaWriteResult shada_read_when_writing(
return ret;
}
+/// Check whether buffer should be ignored
+///
+/// @param[in] buf buf_T* to check.
+/// @param[in] removable_bufs Cache of buffers ignored due to their location.
+///
+/// @return true or false.
+static inline bool ignore_buf(const buf_T *const buf,
+ khash_t(bufset) *const removable_bufs)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
+{
+ return (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \
+ || in_bufset(removable_bufs, buf));
+}
+
/// Get list of buffers to write to the shada file
///
/// @param[in] removable_bufs Buffers which are ignored
@@ -2227,11 +2389,9 @@ static inline ShadaEntry shada_get_buflist(
{
int max_bufs = get_shada_parameter('%');
size_t buf_count = 0;
-#define IGNORE_BUF(buf)\
- (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \
- || in_bufset(removable_bufs, buf)) // NOLINT(whitespace/indent)
FOR_ALL_BUFFERS(buf) {
- if (!IGNORE_BUF(buf) && (max_bufs < 0 || buf_count < (size_t)max_bufs)) {
+ if (!ignore_buf(buf, removable_bufs)
+ && (max_bufs < 0 || buf_count < (size_t)max_bufs)) {
buf_count++;
}
}
@@ -2249,7 +2409,7 @@ static inline ShadaEntry shada_get_buflist(
};
size_t i = 0;
FOR_ALL_BUFFERS(buf) {
- if (IGNORE_BUF(buf)) {
+ if (ignore_buf(buf, removable_bufs)) {
continue;
}
if (i >= buf_count) {
@@ -2263,7 +2423,6 @@ static inline ShadaEntry shada_get_buflist(
i++;
}
-#undef IGNORE_BUF
return buflist_entry;
}
@@ -2365,6 +2524,34 @@ static inline void shada_initialize_registers(WriteMergerState *const wms,
} while (reg_iter != NULL);
}
+/// Replace numbered mark in WriteMergerState
+///
+/// Frees the last mark, moves (including adjusting mark names) marks from idx
+/// to the last-but-one one and saves the new mark at given index.
+///
+/// @param[out] wms Merger state to adjust.
+/// @param[in] idx Index at which new mark should be placed.
+/// @param[in] entry New mark.
+static inline void replace_numbered_mark(WriteMergerState *const wms,
+ const size_t idx,
+ const PossiblyFreedShadaEntry entry)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
+{
+ if (ARRAY_LAST_ENTRY(wms->numbered_marks).can_free_entry) {
+ shada_free_shada_entry(&ARRAY_LAST_ENTRY(wms->numbered_marks).data);
+ }
+ for (size_t i = idx; i < ARRAY_SIZE(wms->numbered_marks) - 1; i++) {
+ if (wms->numbered_marks[i].data.type == kSDItemGlobalMark) {
+ wms->numbered_marks[i].data.data.filemark.name = (char)('0' + (int)i + 1);
+ }
+ }
+ memmove(wms->numbered_marks + idx + 1, wms->numbered_marks + idx,
+ sizeof(wms->numbered_marks[0])
+ * (ARRAY_SIZE(wms->numbered_marks) - 1 - idx));
+ wms->numbered_marks[idx] = entry;
+ wms->numbered_marks[idx].data.data.filemark.name = (char)('0' + (int)idx);
+}
+
/// Write ShaDa file
///
/// @param[in] sd_writer Structure containing file writer definition.
@@ -2597,6 +2784,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
// Initialize global marks
if (dump_global_marks) {
const void *global_mark_iter = NULL;
+ size_t digit_mark_idx = 0;
do {
char name = NUL;
xfmark_T fm;
@@ -2619,7 +2807,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}
fname = (const char *) buf->b_ffname;
}
- wms->global_marks[mark_global_index(name)] = (PossiblyFreedShadaEntry) {
+ const PossiblyFreedShadaEntry pf_entry = {
.can_free_entry = false,
.data = {
.type = kSDItemGlobalMark,
@@ -2629,11 +2817,16 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
.mark = fm.fmark.mark,
.name = name,
.additional_data = fm.fmark.additional_data,
- .fname = (char *) fname,
+ .fname = (char *)fname,
}
}
},
};
+ if (ascii_isdigit(name)) {
+ replace_numbered_mark(wms, digit_mark_idx++, pf_entry);
+ } else {
+ wms->global_marks[mark_global_index(name)] = pf_entry;
+ }
} while (global_mark_iter != NULL);
}
@@ -2715,6 +2908,26 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}
}
+ // Update numbered marks: '0' should be replaced with the current position,
+ // '9' should be removed and all other marks shifted.
+ if (!ignore_buf(curbuf, &removable_bufs) && curwin->w_cursor.lnum != 0) {
+ replace_numbered_mark(wms, 0, (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
+ .data = {
+ .type = kSDItemGlobalMark,
+ .timestamp = os_time(),
+ .data = {
+ .filemark = {
+ .mark = curwin->w_cursor,
+ .name = '0',
+ .additional_data = NULL,
+ .fname = (char *)curbuf->b_ffname,
+ }
+ }
+ },
+ });
+ }
+
// Write the rest
#define PACK_WMS_ARRAY(wms_array) \
do { \
@@ -2729,6 +2942,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
} \
} while (0)
PACK_WMS_ARRAY(wms->global_marks);
+ PACK_WMS_ARRAY(wms->numbered_marks);
PACK_WMS_ARRAY(wms->registers);
for (size_t i = 0; i < wms->jumps_size; i++) {
if (shada_pack_pfreed_entry(packer, wms->jumps[i], max_kbyte)
@@ -2823,6 +3037,7 @@ shada_write_exit:
return ret;
}
+#undef IGNORE_BUF
#undef PACK_STATIC_STR
/// Write ShaDa file to a given location
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 276b47536f..d006477c80 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -359,6 +359,15 @@ void terminal_resize(Terminal *term, uint16_t width, uint16_t height)
return;
}
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer && wp->w_buffer->terminal == term) {
+ const uint16_t win_width =
+ (uint16_t)(MAX(0, wp->w_width - win_col_off(wp)));
+ width = MAX(width, win_width);
+ height = (uint16_t)MAX(height, wp->w_height);
+ }
+ }
+
vterm_set_size(term->vt, height, width);
vterm_screen_flush_damage(term->vts);
term->pending_resize = true;
@@ -1088,7 +1097,6 @@ static void refresh_terminal(Terminal *term)
refresh_size(term, buf);
refresh_scrollback(term, buf);
refresh_screen(term, buf);
- redraw_buf_later(buf, NOT_VALID);
});
long ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
@@ -1098,6 +1106,7 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
refresh_pending = false;
if (exiting // Cannot redraw (requires event loop) during teardown/exit.
+ || (State & CMDPREVIEW)
// WM_LIST (^D) is not redrawn, unlike the normal wildmenu. So we must
// skip redraws to keep it visible.
|| wild_menu_showing == WM_LIST) {
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index a31e1843fc..4bfcbf8e79 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -20,13 +20,13 @@ SCRIPTS_DEFAULT = \
test40.out \
test42.out \
test48.out \
- test49.out \
test52.out \
test64.out \
ifneq ($(OS),Windows_NT)
SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \
test17.out \
+ test49.out \
endif
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 7090be7726..5c98455909 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -61,7 +61,7 @@ set nomore
lang mess C
" Always use forward slashes.
-set shellslash
+" set shellslash
" Prepare for calling test_garbagecollect_now().
let v:testing = 1
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index 7d6dd0c7ce..aac9fefef4 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -17,3 +17,11 @@ let &packpath = &rtp
" Make sure $HOME does not get read or written.
let $HOME = '/does/not/exist'
+
+" Use default shell on Windows to avoid segfault, caused by TUI
+if has('win32')
+ let $SHELL = ''
+ let $TERM = ''
+ let &shell = empty($COMSPEC) ? exepath('cmd.exe') : $COMSPEC
+ set shellcmdflag=/s/c shellxquote=\" shellredir=>%s\ 2>&1
+endif
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 673246e1fb..be68e9ff9d 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -154,7 +154,7 @@ func Test_getcompletion()
call assert_equal([], l)
let l = getcompletion('', 'dir')
- call assert_true(index(l, 'sautest/') >= 0)
+ call assert_true(index(l, expand('sautest/')) >= 0)
let l = getcompletion('NoMatch', 'dir')
call assert_equal([], l)
@@ -246,7 +246,7 @@ func Test_getcompletion()
" Command line completion tests
let l = getcompletion('cd ', 'cmdline')
- call assert_true(index(l, 'sautest/') >= 0)
+ call assert_true(index(l, expand('sautest/')) >= 0)
let l = getcompletion('cd NoMatch', 'cmdline')
call assert_equal([], l)
let l = getcompletion('let v:n', 'cmdline')
@@ -288,7 +288,7 @@ func Test_expand_star_star()
call mkdir('a/b', 'p')
call writefile(['asdfasdf'], 'a/b/fileXname')
call feedkeys(":find **/fileXname\<Tab>\<CR>", 'xt')
- call assert_equal('find a/b/fileXname', getreg(':'))
+ call assert_equal('find '.expand('a/b/fileXname'), getreg(':'))
bwipe!
call delete('a', 'rf')
endfunc
diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim
index 4732109ed0..1019246404 100644
--- a/src/nvim/testdir/test_find_complete.vim
+++ b/src/nvim/testdir/test_find_complete.vim
@@ -3,6 +3,8 @@
" Do all the tests in a separate window to avoid E211 when we recursively
" delete the Xfind directory during cleanup
func Test_find_complete()
+ let shellslash = &shellslash
+ set shellslash
set belloff=all
" On windows a stale "Xfind" directory may exist, remove it so that
@@ -154,4 +156,5 @@ func Test_find_complete()
exe 'cd ' . cwd
call delete('Xfind', 'rf')
set path&
+ let &shellslash = shellslash
endfunc
diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
index 06c48d8e76..4d4a902031 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -30,7 +30,7 @@ func Test_help_tagjump()
help sp?it
call assert_equal("help", &filetype)
- call assert_true(getline('.') =~ '\*:split\*')
+ call assert_true(getline('.') =~ '\*'.(has('win32') ? 'split()' : ':split').'\*')
helpclose
help :?
diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim
index a3d5538a47..6e4c7af821 100644
--- a/src/nvim/testdir/test_makeencoding.vim
+++ b/src/nvim/testdir/test_makeencoding.vim
@@ -13,12 +13,19 @@ endif
let s:script = 'test_makeencoding.py'
-let s:message_tbl = {
+if has('iconv')
+ let s:message_tbl = {
\ 'utf-8': 'ÀÈÌÒÙ こんにちは 你好',
\ 'latin1': 'ÀÈÌÒÙ',
\ 'cp932': 'こんにちは',
\ 'cp936': '你好',
\}
+else
+ let s:message_tbl = {
+ \ 'utf-8': 'ÀÈÌÒÙ こんにちは 你好',
+ \ 'latin1': 'ÀÈÌÒÙ',
+ \}
+endif
" Tests for :cgetfile and :lgetfile.
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index a15d15213a..f8c3161b40 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -158,6 +158,8 @@ func Test_set_completion()
call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:)
" Expand directories.
+ let shellslash = &shellslash
+ set shellslash
call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_match('./samples/ ', @:)
call assert_notmatch('./small.vim ', @:)
@@ -168,6 +170,7 @@ func Test_set_completion()
call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:)
+ let &shellslash = shellslash
endfunc
func Test_set_errors()
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index dd177fd633..85f93cf3da 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -850,17 +850,17 @@ func s:dir_stack_tests(cchar)
let qf = g:Xgetlist()
- call assert_equal('dir1/a/habits2.txt', bufname(qf[1].bufnr))
+ call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[1].bufnr))
call assert_equal(1, qf[1].lnum)
- call assert_equal('dir1/a/b/habits3.txt', bufname(qf[3].bufnr))
+ call assert_equal(expand('dir1/a/b/habits3.txt'), bufname(qf[3].bufnr))
call assert_equal(2, qf[3].lnum)
- call assert_equal('dir1/a/habits2.txt', bufname(qf[4].bufnr))
+ call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[4].bufnr))
call assert_equal(7, qf[4].lnum)
- call assert_equal('dir1/c/habits4.txt', bufname(qf[6].bufnr))
+ call assert_equal(expand('dir1/c/habits4.txt'), bufname(qf[6].bufnr))
call assert_equal(3, qf[6].lnum)
call assert_equal('habits1.txt', bufname(qf[9].bufnr))
call assert_equal(4, qf[9].lnum)
- call assert_equal('dir2/habits5.txt', bufname(qf[11].bufnr))
+ call assert_equal(expand('dir2/habits5.txt'), bufname(qf[11].bufnr))
call assert_equal(5, qf[11].lnum)
let &efm=save_efm
@@ -1065,7 +1065,7 @@ func Test_efm2()
call assert_equal(8, len(l))
call assert_equal(89, l[4].lnum)
call assert_equal(1, l[4].valid)
- call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr))
+ call assert_equal(expand('unittests/dbfacadeTest.py'), bufname(l[4].bufnr))
" The following sequence of commands used to crash Vim
set efm=%W%m
@@ -1609,11 +1609,11 @@ func Test_two_windows()
laddexpr 'one.txt:3:one one one'
let loc_one = getloclist(one_id)
- call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr))
+ call assert_equal(expand('Xone/a/one.txt'), bufname(loc_one[1].bufnr))
call assert_equal(3, loc_one[1].lnum)
let loc_two = getloclist(two_id)
- call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr))
+ call assert_equal(expand('Xtwo/a/two.txt'), bufname(loc_two[1].bufnr))
call assert_equal(5, loc_two[1].lnum)
call win_gotoid(one_id)
diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim
index 46d884a97c..beecb4cd0d 100644
--- a/src/nvim/testdir/test_recover.vim
+++ b/src/nvim/testdir/test_recover.vim
@@ -6,11 +6,6 @@ func Test_recover_root_dir()
set dir=/
call assert_fails('recover', 'E305:')
close!
-
- if has('win32') || filewritable('/') == 2
- " can write in / directory on MS-Windows
- set dir=/notexist/
- endif
call assert_fails('split Xtest', 'E303:')
set dir&
endfunc
diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim
index 1239fe9427..0a09130b0c 100644
--- a/src/nvim/testdir/test_stat.vim
+++ b/src/nvim/testdir/test_stat.vim
@@ -86,7 +86,7 @@ func Test_win32_symlink_dir()
let res = system('dir C:\Users /a')
if match(res, '\C<SYMLINKD> *All Users') >= 0
" Get the filetype of the symlink.
- call assert_equal('dir', getftype('C:\Users\All Users'))
+ call assert_equal('link', getftype('C:\Users\All Users'))
endif
endif
endfunc
diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim
index ce9d110d82..d3c0594c03 100644
--- a/src/nvim/testdir/test_system.vim
+++ b/src/nvim/testdir/test_system.vim
@@ -5,14 +5,12 @@ function! Test_System()
return
endif
let out = system('echo 123')
- " On Windows we may get a trailing space.
- if out != "123 \n"
- call assert_equal("123\n", out)
- endif
+ call assert_equal("123\n", out)
let out = systemlist('echo 123')
- " On Windows we may get a trailing space and CR.
- if out != ["123 \r"]
+ if &shell =~# 'cmd.exe$'
+ call assert_equal(["123\r"], out)
+ else
call assert_equal(['123'], out)
endif
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index b04a6ce4f9..0362820687 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -170,11 +170,21 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
char buf[64];
size_t len = 0;
int button, row, col;
+ static int last_pressed_button = 0;
TermKeyMouseEvent ev;
termkey_interpret_mouse(input->tk, key, &ev, &button, &row, &col);
- if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG
- && ev != TERMKEY_MOUSE_RELEASE) {
+ if ((ev == TERMKEY_MOUSE_RELEASE || ev == TERMKEY_MOUSE_DRAG)
+ && button == 0) {
+ // Some terminals (like urxvt) don't report which button was released.
+ // libtermkey reports button 0 in this case.
+ // For drag and release, we can reasonably infer the button to be the last
+ // pressed one.
+ button = last_pressed_button;
+ }
+
+ if (button == 0 || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG
+ && ev != TERMKEY_MOUSE_RELEASE)) {
return;
}
@@ -210,6 +220,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
"ScrollWheelDown");
} else {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse");
+ last_pressed_button = button;
}
break;
case TERMKEY_MOUSE_DRAG:
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 87f3cc2354..65957626cb 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -829,7 +829,7 @@ static void tui_cursor_goto(UI *ui, Integer row, Integer col)
CursorShape tui_cursor_decode_shape(const char *shape_str)
{
- CursorShape shape = 0;
+ CursorShape shape;
if (strequal(shape_str, "block")) {
shape = SHAPE_BLOCK;
} else if (strequal(shape_str, "vertical")) {
@@ -837,7 +837,8 @@ CursorShape tui_cursor_decode_shape(const char *shape_str)
} else if (strequal(shape_str, "horizontal")) {
shape = SHAPE_HOR;
} else {
- EMSG2(_(e_invarg2), shape_str);
+ WLOG("Unknown shape value '%s'", shape_str);
+ shape = SHAPE_BLOCK;
}
return shape;
}
@@ -923,7 +924,6 @@ static void tui_set_mode(UI *ui, ModeShape mode)
}
TUIData *data = ui->data;
cursorentry_T c = data->cursor_shapes[mode];
- int shape = c.shape;
if (c.id != 0 && ui->rgb) {
int attr = syn_id2attr(c.id);
@@ -934,11 +934,12 @@ static void tui_set_mode(UI *ui, ModeShape mode)
}
}
- switch (shape) {
+ int shape;
+ switch (c.shape) {
+ default: abort(); break;
case SHAPE_BLOCK: shape = 1; break;
case SHAPE_HOR: shape = 3; break;
case SHAPE_VER: shape = 5; break;
- default: WLOG("Unknown shape value %d", shape); break;
}
UNIBI_SET_NUM_VAR(data->params[0], shape + (int)(c.blinkon == 0));
unibi_out_ext(ui, data->unibi_ext.set_cursor_style);
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index 90fee146ab..1a7e55c11e 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -779,7 +779,8 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate,
eltkn_opt_scope_tab[token.data.opt.scope],
(int)token.data.opt.len, token.data.opt.name)
TKNARGS(kExprLexPlainIdentifier, "(scope=%s,autoload=%i)",
- intchar2str(token.data.var.scope), (int)token.data.var.autoload)
+ intchar2str((int)token.data.var.scope),
+ (int)token.data.var.autoload)
TKNARGS(kExprLexNumber, "(is_float=%i,base=%i,val=%lg)",
(int)token.data.num.is_float,
(int)token.data.num.base,